From 9608bc71c4e7e0ececea3193acd94d8fa72d67a2 Mon Sep 17 00:00:00 2001 From: Petr Benes Date: Wed, 24 Jul 2019 23:30:41 +0200 Subject: [PATCH] memory manager refactoring * make ia32::byte_offset return uint64_t instead of uint32_t * move ia32::mapping_t to mm::memory_mapper * move ia32::physical_memory_descriptor to mm::physical_memory_descriptor * move ia32::mtrr to mm::mtrr_descriptor * remove useless pa_from_va (with cr3) function; the reason is that physical address for each active CR3 is actually always same --- src/hvpp/hvpp.vcxproj | 7 +- src/hvpp/hvpp.vcxproj.filters | 30 ++- src/hvpp/hvpp/ept.cpp | 8 +- src/hvpp/hvpp/ia32/memory.cpp | 121 +--------- src/hvpp/hvpp/ia32/memory.h | 90 +------- src/hvpp/hvpp/ia32/mtrr.h | 209 ----------------- src/hvpp/hvpp/ia32/paging.h | 6 +- src/hvpp/hvpp/ia32/win32/memory.cpp | 34 --- src/hvpp/hvpp/lib/driver.cpp | 2 +- src/hvpp/hvpp/lib/mm.cpp | 57 ++--- src/hvpp/hvpp/lib/mm.h | 7 +- src/hvpp/hvpp/lib/mm/memory_mapper.cpp | 103 +++++++++ src/hvpp/hvpp/lib/mm/memory_mapper.h | 41 ++++ src/hvpp/hvpp/lib/mm/mtrr_descriptor.h | 210 ++++++++++++++++++ .../hvpp/lib/mm/physical_memory_descriptor.h | 62 ++++++ src/hvpp/hvpp/lib/mm/win32/memory_mapper.cpp | 16 ++ .../mm/win32/physical_memory_descriptor.cpp | 30 +++ 17 files changed, 541 insertions(+), 492 deletions(-) delete mode 100644 src/hvpp/hvpp/ia32/mtrr.h create mode 100644 src/hvpp/hvpp/lib/mm/memory_mapper.cpp create mode 100644 src/hvpp/hvpp/lib/mm/memory_mapper.h create mode 100644 src/hvpp/hvpp/lib/mm/mtrr_descriptor.h create mode 100644 src/hvpp/hvpp/lib/mm/physical_memory_descriptor.h create mode 100644 src/hvpp/hvpp/lib/mm/win32/memory_mapper.cpp create mode 100644 src/hvpp/hvpp/lib/mm/win32/physical_memory_descriptor.cpp diff --git a/src/hvpp/hvpp.vcxproj b/src/hvpp/hvpp.vcxproj index 7fe2f32..bd4ad89 100644 --- a/src/hvpp/hvpp.vcxproj +++ b/src/hvpp/hvpp.vcxproj @@ -104,6 +104,9 @@ + + + $(IntDir)/$(RelativeDir)/%(Filename)%(Extension).obj @@ -139,6 +142,9 @@ + + + @@ -160,7 +166,6 @@ - diff --git a/src/hvpp/hvpp.vcxproj.filters b/src/hvpp/hvpp.vcxproj.filters index 0301d3e..e40f8a0 100644 --- a/src/hvpp/hvpp.vcxproj.filters +++ b/src/hvpp/hvpp.vcxproj.filters @@ -67,6 +67,15 @@ {b199f4fb-0f50-4a62-b44d-f80541e9b91a} + + {8e3b0296-08e1-46b7-83f6-555347ca5fe9} + + + {48924d45-28d4-4be4-96bc-ef93b3095d01} + + + {c53a048f-d7da-48c6-88a5-fa1ed9bfc1ed} + @@ -138,6 +147,15 @@ Source Files\hvpp\ia32 + + Source Files\hvpp\lib\mm + + + Source Files\hvpp\lib\mm\win32 + + + Source Files\hvpp\lib\mm\win32 + @@ -158,9 +176,6 @@ Header Files\hvpp\ia32 - - Header Files\hvpp\ia32 - Header Files\hvpp\ia32 @@ -302,6 +317,15 @@ Header Files\hvpp + + Header Files\hvpp\lib\mm + + + Header Files\hvpp\lib\mm + + + Header Files\hvpp\lib\mm + diff --git a/src/hvpp/hvpp/ept.cpp b/src/hvpp/hvpp/ept.cpp index 1e182b4..8b0d3a8 100644 --- a/src/hvpp/hvpp/ept.cpp +++ b/src/hvpp/hvpp/ept.cpp @@ -28,7 +28,7 @@ ept_t::ept_t() noexcept // Initialize EPT pointer. // It's not really JUST pointer, but Intel Manual calls it this way. // - eptptr_.memory_type = static_cast(mm::mtrr().type(empl4_pa)); + eptptr_.memory_type = static_cast(mm::mtrr_descriptor().type(empl4_pa)); eptptr_.page_walk_length = ept_ptr_t::page_walk_length_4; eptptr_.page_frame_number = empl4_pa.pfn(); } @@ -399,7 +399,7 @@ epte_t* ept_t::map_pdpt(pa_t guest_pa, pa_t host_pa, epte_t* pdpt, if (level == pml::pdpt) { - pdpte->update(host_pa, mm::mtrr().type(guest_pa), true, access); + pdpte->update(host_pa, mm::mtrr_descriptor().type(guest_pa), true, access); return pdpte; } @@ -414,7 +414,7 @@ epte_t* ept_t::map_pd(pa_t guest_pa, pa_t host_pa, epte_t* pd, if (level == pml::pd) { - pde->update(host_pa, mm::mtrr().type(guest_pa), true, access); + pde->update(host_pa, mm::mtrr_descriptor().type(guest_pa), true, access); return pde; } @@ -430,7 +430,7 @@ epte_t* ept_t::map_pt(pa_t guest_pa, pa_t host_pa, epte_t* pt, (void)(level); hvpp_assert(level == pml::pt); { - pte->update(host_pa, mm::mtrr().type(guest_pa), access); + pte->update(host_pa, mm::mtrr_descriptor().type(guest_pa), access); return pte; } } diff --git a/src/hvpp/hvpp/ia32/memory.cpp b/src/hvpp/hvpp/ia32/memory.cpp index 544fcb9..6313ebd 100644 --- a/src/hvpp/hvpp/ia32/memory.cpp +++ b/src/hvpp/hvpp/ia32/memory.cpp @@ -2,28 +2,14 @@ namespace ia32 { -////////////////////////////////////////////////////////////////////////// -// namespace detail -////////////////////////////////////////////////////////////////////////// - -namespace detail -{ - uint64_t pa_from_va(const void* va, cr3_t cr3) noexcept - { - return pa_t::from_pfn( - va_t(va).pt_entry(cr3)->page_frame_number - ).value() + byte_offset(va); - } -} - ////////////////////////////////////////////////////////////////////////// // va_t ////////////////////////////////////////////////////////////////////////// -pe_t* va_t::pt_entry(cr3_t cr3 /*= read()*/, pml level /*= pml::pt*/) const noexcept +pe_t* va_t::pt_entry(pml level /*= pml::pt*/) const noexcept { const auto pml4e = &reinterpret_cast( - pa_t::from_pfn(cr3.page_frame_number).va() + pa_t::from_pfn(read().page_frame_number).va() )[index(pml::pml4)]; if (!pml4e->present || level == pml::pml4) @@ -56,107 +42,4 @@ pe_t* va_t::pt_entry(cr3_t cr3 /*= read()*/, pml level /*= pml::pt*/) con return pt; } -////////////////////////////////////////////////////////////////////////// -// mapping_t -////////////////////////////////////////////////////////////////////////// - -mapping_t::mapping_t() noexcept -{ - // - // Reserve 1 page of the virtual address space. - // Note that the memory is NOT allocated, just reserved. - // - va_ = detail::mapping_allocate(page_size); - - // - // Get page-table entry for the virtual address. - // - pte_ = va_t(va_).pt_entry(); -} - -mapping_t::~mapping_t() noexcept -{ - // - // Release the virtual address space. - // - detail::mapping_free(va_); -} - -void* mapping_t::map(pa_t pa) noexcept -{ - // - // Make this entry present & writable. - // - pte_->present = true; - pte_->write = true; - - // - // Do not flush this page from the TLB on CR3 switch. - // - pte_->global = true; - - // - // Set the PFN of this PTE to the PFN of the - // provided physical address. - // - pte_->page_frame_number = pa.pfn(); - - // - // Finally, invalidate the cache for the virtual address. - // - ia32_asm_inv_page(va_); - - return reinterpret_cast(va_) + byte_offset(pa.value()); -} - -void mapping_t::unmap() noexcept -{ - pte_->flags = 0; -} - -void mapping_t::read(pa_t pa, void* buffer, size_t size) noexcept -{ - read_write(pa, buffer, size, false); -} - -void mapping_t::write(pa_t pa, const void* buffer, size_t size) noexcept -{ - read_write(pa, const_cast(buffer), size, true); -} - -void mapping_t::read_write(pa_t pa, void* buffer, size_t size, bool write) noexcept -{ - uint8_t* byte_buffer = reinterpret_cast(buffer); - - // - // Map each page of the physical memory to the reserved - // virtual address and then copy. - // - while (size != 0) - { - void* va = map(pa); - size_t bytes_to_copy = page_size - byte_offset(va); - - if (bytes_to_copy > size) - { - bytes_to_copy = size; - } - - if (write) - { - memcpy(va, byte_buffer, bytes_to_copy); - } - else - { - memcpy(byte_buffer, va, bytes_to_copy); - } - - byte_buffer += bytes_to_copy; - pa += bytes_to_copy; - size -= bytes_to_copy; - - unmap(); - } -} - } diff --git a/src/hvpp/hvpp/ia32/memory.h b/src/hvpp/hvpp/ia32/memory.h index e159a96..476e807 100644 --- a/src/hvpp/hvpp/ia32/memory.h +++ b/src/hvpp/hvpp/ia32/memory.h @@ -1,10 +1,10 @@ #pragma once -#include "hvpp/lib/log.h" #include "paging.h" #include "arch.h" #include // std::byte #include +#include #include #include @@ -26,18 +26,13 @@ static constexpr auto page_mask = page_size - 1; class pa_t; class va_t; -class mapping_t; +class memory_range; class physical_memory_range; -class physical_memory_descriptor; namespace detail { uint64_t pa_from_va(const void* va) noexcept; - uint64_t pa_from_va(const void* va, cr3_t cr3) noexcept; void* va_from_pa(uint64_t pa) noexcept; - void* mapping_allocate(size_t size) noexcept; - void mapping_free(void* va) noexcept; - void check_physical_memory(physical_memory_range* range_list, int range_list_size, int& count) noexcept; } // @@ -53,8 +48,6 @@ class pa_t static pa_t from_pfn(uint64_t pfn) noexcept { return pa_t(pfn << page_shift); } static pa_t from_va(const void* va) noexcept { return pa_t(detail::pa_from_va(va)); } - static pa_t from_va(const void* va, - cr3_t cr3) noexcept { return pa_t(detail::pa_from_va(va, cr3)); } // // ctor/operators @@ -161,40 +154,12 @@ class va_t return static_cast(result); } - pe_t* pt_entry(cr3_t cr3 = read(), pml level = pml::pt) const noexcept; + pe_t* pt_entry(pml level = pml::pt) const noexcept; private: uint64_t value_; }; -// -// Mapping -// - -class mapping_t -{ - public: - mapping_t() noexcept; - ~mapping_t() noexcept; - - mapping_t(const mapping_t& other) noexcept = delete; - mapping_t(mapping_t&& other) noexcept = delete; - mapping_t& operator=(const mapping_t& other) noexcept = delete; - mapping_t& operator=(mapping_t&& other) noexcept = delete; - - void* map(pa_t pa) noexcept; - void unmap() noexcept; - - void read(pa_t pa, void* buffer, size_t size) noexcept; - void write(pa_t pa, const void* buffer, size_t size) noexcept; - - private: - void read_write(pa_t pa, void* buffer, size_t size, bool write) noexcept; - - void* va_; - pe_t* pte_; -}; - // // Memory range // @@ -303,55 +268,6 @@ class physical_memory_range pa_t end_; }; -// -// Class for receiving physical memory ranges which are backed up -// by actual physical memory. -// - -class physical_memory_descriptor -{ - public: - static constexpr int max_range_count = 32; - - physical_memory_descriptor() noexcept { check_physical_memory(); } - physical_memory_descriptor(const physical_memory_descriptor& other) noexcept = delete; - physical_memory_descriptor(physical_memory_descriptor&& other) noexcept = delete; - physical_memory_descriptor& operator=(const physical_memory_descriptor& other) noexcept = delete; - physical_memory_descriptor& operator=(physical_memory_descriptor&& other) noexcept = delete; - - auto begin() const noexcept { return const_cast(&range_[0]); } - auto end() const noexcept { return const_cast(&range_[count_]); } - auto size() const noexcept { return static_cast(count_); } - - auto total_physical_memory_size() const noexcept - { - return std::accumulate(begin(), end(), size_t(0), [](auto sum, auto next) { - return sum + next.size(); - }); - } - - void dump() const noexcept - { - hvpp_info("Physical memory ranges (%i)", count_); - - for (int i = 0; i < count_; ++i) - { - hvpp_info( - " %3i) [%p - %p] (%8u kb)", i, - range_[i].begin().value(), - range_[i].end().value(), - range_[i].size() / 1024); - } - } - - private: - void check_physical_memory() noexcept - { detail::check_physical_memory(range_, max_range_count, count_); } - - physical_memory_range range_[max_range_count]; - int count_ = 0; -}; - constexpr inline const char* memory_type_to_string(memory_type type) noexcept { switch (type) diff --git a/src/hvpp/hvpp/ia32/mtrr.h b/src/hvpp/hvpp/ia32/mtrr.h deleted file mode 100644 index 1384d8b..0000000 --- a/src/hvpp/hvpp/ia32/mtrr.h +++ /dev/null @@ -1,209 +0,0 @@ -#pragma once -#include "memory.h" -#include "msr.h" -#include "msr/mtrr.h" -#include "../lib/typelist.h" - -#include -#include - -namespace ia32 { - -struct mtrr_range -{ - physical_memory_range range; - memory_type type; -}; - -class mtrr -{ - public: - static constexpr int fixed_count = (1 + 2 + 8) * 8; - static constexpr int max_variable_count = 255; - - mtrr() noexcept { check_fixed(); check_variable(); } - mtrr(const mtrr& other) noexcept = delete; - mtrr(mtrr&& other) noexcept = delete; - mtrr& operator=(const mtrr& other) noexcept = delete; - mtrr& operator=(mtrr&& other) noexcept = delete; - - const mtrr_range* begin() const noexcept { return &mtrr_[0]; } - const mtrr_range* end() const noexcept { return &mtrr_[size()]; } - size_t size() const noexcept { return fixed_count + variable_count_; } - - memory_type type(pa_t pa) const noexcept - { - // - // If the MTRRs are not enabled (by setting the E flag in the - // IA32_MTRR_DEF_TYPE MSR), then all memory accesses are of the - // UC memory type. If the MTRRs are enabled, then the memory - // type used for a memory access is determined as follows: - // - // 1. If the physical address falls within the first 1 MByte of - // physical memory and fixed MTRRs are enabled, the processor - // uses the memory type stored for the appropriate fixed-range - // MTRR. - // - // 2. Otherwise, the processor attempts to match the physical - // address with a memory type set by the variable-range MTRRs: - // - If one variable memory range matches, the processor uses - // the memory type stored in the IA32_MTRR_PHYSBASEn register - // for that range. - // - // - If two or more variable memory ranges match and the memory - // types are identical, then that memory type is used. - // - // - If two or more variable memory ranges match and one of the - // memory types is UC, the UC memory type is used. - // - // - If two or more variable memory ranges match and the memory - // types are WT and WB, the WT memory type is used. - // - // - For overlaps not defined by the above rules, processor - // behavior is undefined. - // - // 3. If no fixed or variable memory range matches, the processor uses - // the default memory type. - // - // (ref: Vol3A[11.11.4.1(MTRR Precedences)] - // - memory_type result = memory_type::invalid; - - for (auto mtrr_item : *this) - { - if (mtrr_item.range.contains(pa)) - { - if (is_fixed(mtrr_item) || mtrr_item.type == memory_type::uncacheable) - { - result = mtrr_item.type; - break; - } - - if ( result == memory_type::write_back && - (result == memory_type::write_through || - mtrr_item.type == memory_type::write_through)) - { - result = memory_type::write_through; - } - } - } - - if (result == memory_type::invalid) - { - result = default_memory_type_; - } - - return result; - } - - void dump() const noexcept - { - auto dump_range = [](int i, const mtrr_range& mtrr) noexcept - { - hvpp_info( - " %3i) %s [%016" PRIx64 " - %016" PRIx64 "] (%8u kb)", i, - memory_type_to_string(mtrr.type), - mtrr.range.begin().value(), - mtrr.range.end().value(), - mtrr.range.size() / 1024); - }; - - hvpp_info("Fixed MTRR ranges (%i)", fixed_count); - for (int i = 0; i < fixed_count; ++i) - { - dump_range(i, fixed_[i]); - } - - hvpp_info("Variable MTRR ranges (%i)", variable_count_); - for (int i = 0; i < variable_count_; ++i) - { - dump_range(i, variable_[i]); - } - } - - - private: - void check_fixed() noexcept - { - auto mtrr_default = msr::read(); - auto mtrr_capabilities = msr::read(); - - default_memory_type_ = static_cast(mtrr_default.default_memory_type); - - if (mtrr_capabilities.fixed_range_supported && mtrr_default.fixed_range_mtrr_enable) - { - using mtrr_fix_64k_list_t = type_list; - using mtrr_fix_16k_list_t = type_list; - using mtrr_fix_4k_list_t = type_list; - using mtrr_fix_list_t = type_list; - - for_each_type(mtrr_fix_list_t{}, [this](auto mtrr_fixed, int i) { - using ia32_mtrr_t = decltype(mtrr_fixed); - mtrr_fixed = msr::read(); - - pa_t range = ia32_mtrr_t::mtrr_base; - i *= 8; - - for (auto type : mtrr_fixed.type) - { - fixed_[i].range = physical_memory_range(range, range + ia32_mtrr_t::mtrr_size); - fixed_[i].type = static_cast(type); - - range += ia32_mtrr_t::mtrr_size; - i += 1; - } - }); - } - } - - void check_variable() noexcept - { - auto mtrr_capabilities = msr::read(); - variable_count_ = mtrr_capabilities.variable_range_count; - - for (int i = 0; i < variable_count_; ++i) - { - auto mtrr_base = msr::read(msr::mtrr_physbase_t::msr_id + i * 2); - auto mtrr_mask = msr::read(msr::mtrr_physmask_t::msr_id + i * 2); - - if (mtrr_mask.valid) - { - uint64_t size = 1ull << ia32_asm_bsf(mtrr_mask.page_frame_number); - - variable_[i].range = physical_memory_range( - pa_t::from_pfn(mtrr_base.page_frame_number), - pa_t::from_pfn(mtrr_base.page_frame_number + size)); - variable_[i].type = static_cast(mtrr_base.type); - } - } - } - - bool is_fixed(const mtrr_range& range) const noexcept - { - return (const mtrr_range*)&range < (const mtrr_range*)variable_; - } - - bool is_variable(const mtrr_range& range) const noexcept - { - return (const mtrr_range*)&range >= (const mtrr_range*)variable_; - } - - union - { - struct - { - mtrr_range fixed_[fixed_count]; - mtrr_range variable_[max_variable_count]; - }; - - mtrr_range mtrr_[fixed_count + max_variable_count]; - }; - - memory_type default_memory_type_ = memory_type::uncacheable; - int variable_count_ = 0; -}; - -} diff --git a/src/hvpp/hvpp/ia32/paging.h b/src/hvpp/hvpp/ia32/paging.h index c3f55ae..71f729a 100644 --- a/src/hvpp/hvpp/ia32/paging.h +++ b/src/hvpp/hvpp/ia32/paging.h @@ -378,8 +378,8 @@ template < ) > > -constexpr inline uint32_t byte_offset(T va, PAGE_DESCRIPTOR) noexcept -{ return (uint32_t)(uintptr_t(va) & ~PAGE_DESCRIPTOR::mask); } +constexpr inline uint64_t byte_offset(T va, PAGE_DESCRIPTOR) noexcept +{ return (uint64_t)(uintptr_t(va) & ~PAGE_DESCRIPTOR::mask); } // // Return how many pages are needed to cover specified size. @@ -422,7 +422,7 @@ constexpr inline T page_align_up(T va) noexcept { return page_align_up(va, pt_t{}); } template -constexpr inline uint32_t byte_offset(T va) noexcept +constexpr inline uint64_t byte_offset(T va) noexcept { return byte_offset(va, pt_t{}); } template diff --git a/src/hvpp/hvpp/ia32/win32/memory.cpp b/src/hvpp/hvpp/ia32/win32/memory.cpp index b8e30f5..cc6837c 100644 --- a/src/hvpp/hvpp/ia32/win32/memory.cpp +++ b/src/hvpp/hvpp/ia32/win32/memory.cpp @@ -2,8 +2,6 @@ #include -#define HVPP_MAPPING_TAG 'mpvh' - namespace ia32::detail { uint64_t pa_from_va(const void* va) noexcept @@ -18,36 +16,4 @@ namespace ia32::detail return MmGetVirtualForPhysical(win_pa); } - - void* mapping_allocate(size_t size) noexcept - { - return MmAllocateMappingAddress(size, HVPP_MAPPING_TAG); - } - - void mapping_free(void* va) noexcept - { - MmFreeMappingAddress(va, HVPP_MAPPING_TAG); - } - - void check_physical_memory(physical_memory_range* range_list, int range_list_size, int& count) noexcept - { - auto physical_memory_ranges = MmGetPhysicalMemoryRanges(); - - count = 0; - - do - { - pa_t address = physical_memory_ranges[count].BaseAddress.QuadPart; - size_t size = physical_memory_ranges[count].NumberOfBytes.QuadPart; - - if (!address && !size) - { - break; - } - - range_list[count] = physical_memory_range(address, address + size); - } while (++count < range_list_size); - - ExFreePool(physical_memory_ranges); - } } diff --git a/src/hvpp/hvpp/lib/driver.cpp b/src/hvpp/hvpp/lib/driver.cpp index 508be2f..604990e 100644 --- a/src/hvpp/hvpp/lib/driver.cpp +++ b/src/hvpp/hvpp/lib/driver.cpp @@ -49,7 +49,7 @@ namespace driver::common // // Print memory information to the debugger. // - mm::mtrr().dump(); + mm::mtrr_descriptor().dump(); mm::physical_memory_descriptor().dump(); // diff --git a/src/hvpp/hvpp/lib/mm.cpp b/src/hvpp/hvpp/lib/mm.cpp index 2e01cad..de4df34 100644 --- a/src/hvpp/hvpp/lib/mm.cpp +++ b/src/hvpp/hvpp/lib/mm.cpp @@ -66,8 +66,9 @@ namespace mm allocator_t allocator[HVPP_MAX_CPU]; - object_t memory_descriptor; - object_t memory_type_range_registers; + object_t physical_memory_descriptor; + object_t mtrr_descriptor; + object_t lock; }; @@ -98,8 +99,8 @@ namespace mm // // Initialize physical memory descriptor and MTRRs. // - global.memory_descriptor.initialize(); - global.memory_type_range_registers.initialize(); + global.physical_memory_descriptor.initialize(); + global.mtrr_descriptor.initialize(); // // Initialize lock. @@ -124,8 +125,8 @@ namespace mm // Note that this method doesn't acquire the lock and // assumes all allocations has been already freed. // - global.memory_type_range_registers.destroy(); - global.memory_descriptor.destroy(); + global.mtrr_descriptor.destroy(); + global.physical_memory_descriptor.destroy(); global.lock.destroy(); // @@ -179,7 +180,7 @@ namespace mm auto assign(void* address, size_t size) noexcept -> error_code_t { - if (size < ia32::page_size * 3) + if (size < page_size * 3) { // // We need at least 3 pages (see explanation below). @@ -192,11 +193,11 @@ namespace mm // If the provided address is not page aligned, align it // to the next page. // - if (ia32::byte_offset(address) != 0) + if (byte_offset(address) != 0) { - const auto lost_bytes = ia32::byte_offset(address); + const auto lost_bytes = byte_offset(address); - address = reinterpret_cast(ia32::page_align(address)) + ia32::page_size; + address = reinterpret_cast(page_align(address)) + page_size; // // Subtract amount of "lost" bytes due to alignment. @@ -207,12 +208,12 @@ namespace mm // // Align size to the page boundary. // - size = ia32::page_align(size); + size = page_align(size); // // Check again. // - if (size < ia32::page_size * 3) + if (size < page_size * 3) { hvpp_assert(0); return make_error_code_t(std::errc::invalid_argument); @@ -245,17 +246,17 @@ namespace mm // Construct the page bitmap. // uint8_t* page_bitmap_buffer = reinterpret_cast(address); - global.page_bitmap_buffer_size = static_cast(ia32::round_to_pages(size / ia32::page_size / 8)); + global.page_bitmap_buffer_size = static_cast(round_to_pages(size / page_size / 8)); memset(page_bitmap_buffer, 0, global.page_bitmap_buffer_size); - int page_bitmap_size_in_bits = static_cast(size / ia32::page_size); + int page_bitmap_size_in_bits = static_cast(size / page_size); global.page_bitmap.initialize(page_bitmap_buffer, page_bitmap_size_in_bits); // // Construct the page allocation map. // global.page_allocation_map = reinterpret_cast(page_bitmap_buffer + global.page_bitmap_buffer_size); - global.page_allocation_map_size = static_cast(ia32::round_to_pages(size / ia32::page_size) * sizeof(pgmap_t)); + global.page_allocation_map_size = static_cast(round_to_pages(size / page_size) * sizeof(pgmap_t)); memset(global.page_allocation_map, 0, global.page_allocation_map_size); // @@ -308,7 +309,7 @@ namespace mm size = 1; } - int page_count = static_cast(ia32::bytes_to_pages(size)); + int page_count = static_cast(bytes_to_pages(size)); // // Check if the desired number of pages can fit into the @@ -348,8 +349,8 @@ namespace mm previous_page_offset = global.last_page_offset; global.last_page_offset += page_count; - global.allocated_bytes += page_count * ia32::page_size; - global.free_bytes -= page_count * ia32::page_size; + global.allocated_bytes += page_count * page_size; + global.free_bytes -= page_count * page_size; } // @@ -358,7 +359,7 @@ namespace mm // everything neccessary has been done (bitmap + page allocation map // manipulation). // - return global.base_address + previous_page_offset * ia32::page_size; + return global.base_address + previous_page_offset * page_size; } void free(void* address) noexcept @@ -366,9 +367,9 @@ namespace mm // // Our allocator always provides page-aligned memory. // - hvpp_assert(ia32::byte_offset(address) == 0); + hvpp_assert(byte_offset(address) == 0); - const auto offset = static_cast(ia32::bytes_to_pages(reinterpret_cast(address) - global.base_address)); + const auto offset = static_cast(bytes_to_pages(reinterpret_cast(address) - global.base_address)); if (address == nullptr) { @@ -379,7 +380,7 @@ namespace mm return; } - if (size_t(offset) * ia32::page_size > global.available_size) + if (size_t(offset) * page_size > global.available_size) { // // We don't own this memory. @@ -410,8 +411,8 @@ namespace mm // global.page_bitmap->clear(offset, page_count); - global.allocated_bytes -= page_count * ia32::page_size; - global.free_bytes += page_count * ia32::page_size; + global.allocated_bytes -= page_count * page_size; + global.free_bytes += page_count * page_size; } auto system_allocate(size_t size) noexcept -> void* @@ -444,14 +445,14 @@ namespace mm global.allocator[mp::cpu_index()] = new_allocator; } - auto physical_memory_descriptor() noexcept -> const ia32::physical_memory_descriptor& + auto physical_memory_descriptor() noexcept -> const physical_memory_descriptor_t& { - return *global.memory_descriptor; + return *global.physical_memory_descriptor; } - auto mtrr() noexcept -> const ia32::mtrr& + auto mtrr_descriptor() noexcept -> const mtrr_descriptor_t& { - return *global.memory_type_range_registers; + return *global.mtrr_descriptor; } } diff --git a/src/hvpp/hvpp/lib/mm.h b/src/hvpp/hvpp/lib/mm.h index 4142f24..2b27e37 100644 --- a/src/hvpp/hvpp/lib/mm.h +++ b/src/hvpp/hvpp/lib/mm.h @@ -1,9 +1,10 @@ #pragma once #include "hvpp/ia32/memory.h" -#include "hvpp/ia32/mtrr.h" #include "error.h" +#include "mm/physical_memory_descriptor.h" +#include "mm/mtrr_descriptor.h" #include namespace mm @@ -59,6 +60,6 @@ namespace mm auto allocator() noexcept -> const allocator_t&; void allocator(const allocator_t& new_allocator) noexcept; - auto physical_memory_descriptor() noexcept -> const ia32::physical_memory_descriptor&; - auto mtrr() noexcept -> const ia32::mtrr&; + auto physical_memory_descriptor() noexcept -> const physical_memory_descriptor_t&; + auto mtrr_descriptor() noexcept -> const mtrr_descriptor_t&; } diff --git a/src/hvpp/hvpp/lib/mm/memory_mapper.cpp b/src/hvpp/hvpp/lib/mm/memory_mapper.cpp new file mode 100644 index 0000000..42de093 --- /dev/null +++ b/src/hvpp/hvpp/lib/mm/memory_mapper.cpp @@ -0,0 +1,103 @@ +#include "memory_mapper.h" + +namespace mm +{ + memory_mapper::memory_mapper() noexcept + { + // + // Reserve 1 page of the virtual address space. + // Note that the memory is NOT allocated, just reserved. + // + va_ = detail::mapper_allocate(page_size); + + // + // Get page-table entry for the virtual address. + // + pte_ = va_t(va_).pt_entry(); + } + + memory_mapper::~memory_mapper() noexcept + { + // + // Release the virtual address space. + // + detail::mapper_free(va_); + } + + void* memory_mapper::map(pa_t pa) noexcept + { + // + // Make this entry present & writable. + // + pte_->present = true; + pte_->write = true; + + // + // Do not flush this page from the TLB on CR3 switch. + // + pte_->global = true; + + // + // Set the PFN of this PTE to the PFN of the + // provided physical address. + // + pte_->page_frame_number = pa.pfn(); + + // + // Finally, invalidate the cache for the virtual address. + // + ia32_asm_inv_page(va_); + + return reinterpret_cast(va_) + byte_offset(pa.value()); + } + + void memory_mapper::unmap() noexcept + { + pte_->flags = 0; + } + + void memory_mapper::read(pa_t pa, void* buffer, size_t size) noexcept + { + read_write(pa, buffer, size, false); + } + + void memory_mapper::write(pa_t pa, const void* buffer, size_t size) noexcept + { + read_write(pa, const_cast(buffer), size, true); + } + + void memory_mapper::read_write(pa_t pa, void* buffer, size_t size, bool write) noexcept + { + auto byte_buffer = reinterpret_cast(buffer); + + // + // Map each page of the physical memory to the reserved + // system virtual address and then copy. + // + while (size != 0) + { + auto va = map(pa); + auto bytes_to_copy = page_size - byte_offset(va); + + if (bytes_to_copy > size) + { + bytes_to_copy = size; + } + + if (write) + { + memcpy(va, byte_buffer, bytes_to_copy); + } + else + { + memcpy(byte_buffer, va, bytes_to_copy); + } + + byte_buffer += bytes_to_copy; + pa += bytes_to_copy; + size -= bytes_to_copy; + + unmap(); + } + } +} diff --git a/src/hvpp/hvpp/lib/mm/memory_mapper.h b/src/hvpp/hvpp/lib/mm/memory_mapper.h new file mode 100644 index 0000000..2e59af7 --- /dev/null +++ b/src/hvpp/hvpp/lib/mm/memory_mapper.h @@ -0,0 +1,41 @@ +#pragma once +#include "hvpp/ia32/memory.h" + +namespace mm +{ + using namespace ia32; + + namespace detail + { + void* mapper_allocate(size_t size) noexcept; + void mapper_free(void* va) noexcept; + } + + // + // Class for reading/writing physical memory. + // + + class memory_mapper + { + public: + memory_mapper() noexcept; + ~memory_mapper() noexcept; + + memory_mapper(const memory_mapper& other) noexcept = delete; + memory_mapper(memory_mapper&& other) noexcept = delete; + memory_mapper& operator=(const memory_mapper& other) noexcept = delete; + memory_mapper& operator=(memory_mapper&& other) noexcept = delete; + + void* map(pa_t pa) noexcept; + void unmap() noexcept; + + void read(pa_t pa, void* buffer, size_t size) noexcept; + void write(pa_t pa, const void* buffer, size_t size) noexcept; + + private: + void read_write(pa_t pa, void* buffer, size_t size, bool write) noexcept; + + void* va_; + pe_t* pte_; + }; +} diff --git a/src/hvpp/hvpp/lib/mm/mtrr_descriptor.h b/src/hvpp/hvpp/lib/mm/mtrr_descriptor.h new file mode 100644 index 0000000..485a425 --- /dev/null +++ b/src/hvpp/hvpp/lib/mm/mtrr_descriptor.h @@ -0,0 +1,210 @@ +#pragma once +#include "hvpp/ia32/memory.h" +#include "hvpp/ia32/msr.h" +#include "hvpp/ia32/msr/mtrr.h" +#include "../typelist.h" +#include "../log.h" + +#include +#include + +namespace mm +{ + using namespace ia32; + + struct mtrr_range + { + physical_memory_range range; + memory_type type; + }; + + class mtrr_descriptor_t + { + public: + static constexpr auto fixed_count = (1 + 2 + 8) * 8; + static constexpr auto max_variable_count = 255; + + mtrr_descriptor_t() noexcept { check_fixed(); check_variable(); } + mtrr_descriptor_t(const mtrr_descriptor_t& other) noexcept = delete; + mtrr_descriptor_t(mtrr_descriptor_t&& other) noexcept = delete; + mtrr_descriptor_t& operator=(const mtrr_descriptor_t& other) noexcept = delete; + mtrr_descriptor_t& operator=(mtrr_descriptor_t&& other) noexcept = delete; + + const mtrr_range* begin() const noexcept { return &mtrr_[0]; } + const mtrr_range* end() const noexcept { return &mtrr_[size()]; } + size_t size() const noexcept { return fixed_count + variable_count_; } + + memory_type type(pa_t pa) const noexcept + { + // + // If the MTRRs are not enabled (by setting the E flag in the + // IA32_MTRR_DEF_TYPE MSR), then all memory accesses are of the + // UC memory type. If the MTRRs are enabled, then the memory + // type used for a memory access is determined as follows: + // + // 1. If the physical address falls within the first 1 MByte of + // physical memory and fixed MTRRs are enabled, the processor + // uses the memory type stored for the appropriate fixed-range + // MTRR. + // + // 2. Otherwise, the processor attempts to match the physical + // address with a memory type set by the variable-range MTRRs: + // - If one variable memory range matches, the processor uses + // the memory type stored in the IA32_MTRR_PHYSBASEn register + // for that range. + // + // - If two or more variable memory ranges match and the memory + // types are identical, then that memory type is used. + // + // - If two or more variable memory ranges match and one of the + // memory types is UC, the UC memory type is used. + // + // - If two or more variable memory ranges match and the memory + // types are WT and WB, the WT memory type is used. + // + // - For overlaps not defined by the above rules, processor + // behavior is undefined. + // + // 3. If no fixed or variable memory range matches, the processor uses + // the default memory type. + // + // (ref: Vol3A[11.11.4.1(MTRR Precedences)] + // + memory_type result = memory_type::invalid; + + for (auto mtrr_item : *this) + { + if (mtrr_item.range.contains(pa)) + { + if (is_fixed(mtrr_item) || mtrr_item.type == memory_type::uncacheable) + { + result = mtrr_item.type; + break; + } + + if ( result == memory_type::write_back && + (result == memory_type::write_through || + mtrr_item.type == memory_type::write_through)) + { + result = memory_type::write_through; + } + } + } + + if (result == memory_type::invalid) + { + result = default_memory_type_; + } + + return result; + } + + void dump() const noexcept + { + auto dump_range = [](int i, const mtrr_range& mtrr) noexcept + { + hvpp_info( + " %3i) %s [%016" PRIx64 " - %016" PRIx64 "] (%8u kb)", i, + memory_type_to_string(mtrr.type), + mtrr.range.begin().value(), + mtrr.range.end().value(), + mtrr.range.size() / 1024); + }; + + hvpp_info("Fixed MTRR ranges (%i)", fixed_count); + for (int i = 0; i < fixed_count; ++i) + { + dump_range(i, fixed_[i]); + } + + hvpp_info("Variable MTRR ranges (%i)", variable_count_); + for (int i = 0; i < variable_count_; ++i) + { + dump_range(i, variable_[i]); + } + } + + private: + void check_fixed() noexcept + { + auto mtrr_default = msr::read(); + auto mtrr_capabilities = msr::read(); + + default_memory_type_ = static_cast(mtrr_default.default_memory_type); + + if (mtrr_capabilities.fixed_range_supported && mtrr_default.fixed_range_mtrr_enable) + { + using mtrr_fix_64k_list_t = type_list; + using mtrr_fix_16k_list_t = type_list; + using mtrr_fix_4k_list_t = type_list; + using mtrr_fix_list_t = type_list; + + for_each_type(mtrr_fix_list_t{}, [this](auto mtrr_fixed, int i) { + using ia32_mtrr_t = decltype(mtrr_fixed); + mtrr_fixed = msr::read(); + + pa_t range = ia32_mtrr_t::mtrr_base; + i *= 8; + + for (auto type : mtrr_fixed.type) + { + fixed_[i].range = physical_memory_range{ range, range + ia32_mtrr_t::mtrr_size }; + fixed_[i].type = static_cast(type); + + range += ia32_mtrr_t::mtrr_size; + i += 1; + } + }); + } + } + + void check_variable() noexcept + { + auto mtrr_capabilities = msr::read(); + variable_count_ = mtrr_capabilities.variable_range_count; + + for (int i = 0; i < variable_count_; ++i) + { + auto mtrr_base = msr::read(msr::mtrr_physbase_t::msr_id + i * 2); + auto mtrr_mask = msr::read(msr::mtrr_physmask_t::msr_id + i * 2); + + if (mtrr_mask.valid) + { + uint64_t size = 1ull << ia32_asm_bsf(mtrr_mask.page_frame_number); + + variable_[i].range = physical_memory_range( + pa_t::from_pfn(mtrr_base.page_frame_number), + pa_t::from_pfn(mtrr_base.page_frame_number + size)); + variable_[i].type = static_cast(mtrr_base.type); + } + } + } + + bool is_fixed(const mtrr_range& range) const noexcept + { + return (const mtrr_range*)&range < (const mtrr_range*)variable_; + } + + bool is_variable(const mtrr_range& range) const noexcept + { + return (const mtrr_range*)&range >= (const mtrr_range*)variable_; + } + + union + { + struct + { + mtrr_range fixed_[fixed_count]; + mtrr_range variable_[max_variable_count]; + }; + + mtrr_range mtrr_[fixed_count + max_variable_count]; + }; + + memory_type default_memory_type_ = memory_type::uncacheable; + int variable_count_ = 0; + }; +} diff --git a/src/hvpp/hvpp/lib/mm/physical_memory_descriptor.h b/src/hvpp/hvpp/lib/mm/physical_memory_descriptor.h new file mode 100644 index 0000000..6ec6b21 --- /dev/null +++ b/src/hvpp/hvpp/lib/mm/physical_memory_descriptor.h @@ -0,0 +1,62 @@ +#pragma once +#include "hvpp/ia32/memory.h" +#include "../log.h" + +namespace mm +{ + using namespace ia32; + + namespace detail + { + void check_physical_memory(physical_memory_range* range_list, int range_list_size, int& count) noexcept; + } + + // + // Class for receiving physical memory ranges which are backed up + // by actual physical memory. + // + + class physical_memory_descriptor_t + { + public: + static constexpr auto max_range_count = 32; + + physical_memory_descriptor_t() noexcept { check_physical_memory(); } + physical_memory_descriptor_t(const physical_memory_descriptor_t& other) noexcept = delete; + physical_memory_descriptor_t(physical_memory_descriptor_t&& other) noexcept = delete; + physical_memory_descriptor_t& operator=(const physical_memory_descriptor_t& other) noexcept = delete; + physical_memory_descriptor_t& operator=(physical_memory_descriptor_t&& other) noexcept = delete; + + auto begin() const noexcept { return const_cast(&range_[0]); } + auto end() const noexcept { return const_cast(&range_[count_]); } + auto size() const noexcept { return static_cast(count_); } + + auto total_physical_memory_size() const noexcept + { + return std::accumulate(begin(), end(), size_t{}, [](auto sum, auto next) { + return sum + next.size(); + }); + } + + void dump() const noexcept + { + hvpp_info("Physical memory ranges (%i)", count_); + + for (int i = 0; i < count_; ++i) + { + hvpp_info( + " %3i) [%016" PRIx64 " - %016" PRIx64 "] (%8u kb)", i, + range_[i].begin().value(), + range_[i].end().value(), + range_[i].size() / 1024); + } + } + + private: + void check_physical_memory() noexcept + { detail::check_physical_memory(range_, max_range_count, count_); } + + physical_memory_range range_[max_range_count]; + int count_ = 0; + }; +} diff --git a/src/hvpp/hvpp/lib/mm/win32/memory_mapper.cpp b/src/hvpp/hvpp/lib/mm/win32/memory_mapper.cpp new file mode 100644 index 0000000..1d5519f --- /dev/null +++ b/src/hvpp/hvpp/lib/mm/win32/memory_mapper.cpp @@ -0,0 +1,16 @@ +#include + +#define HVPP_MAPPING_TAG 'mpvh' + +namespace mm::detail +{ + void* mapper_allocate(size_t size) noexcept + { + return MmAllocateMappingAddress(size, HVPP_MAPPING_TAG); + } + + void mapper_free(void* va) noexcept + { + MmFreeMappingAddress(va, HVPP_MAPPING_TAG); + } +} diff --git a/src/hvpp/hvpp/lib/mm/win32/physical_memory_descriptor.cpp b/src/hvpp/hvpp/lib/mm/win32/physical_memory_descriptor.cpp new file mode 100644 index 0000000..4218b35 --- /dev/null +++ b/src/hvpp/hvpp/lib/mm/win32/physical_memory_descriptor.cpp @@ -0,0 +1,30 @@ +#include "hvpp/ia32/memory.h" + +#include + +using namespace ia32; + +namespace mm::detail +{ + void check_physical_memory(physical_memory_range* range_list, int range_list_size, int& count) noexcept + { + auto physical_memory_ranges = MmGetPhysicalMemoryRanges(); + + count = 0; + + do + { + pa_t address = physical_memory_ranges[count].BaseAddress.QuadPart; + size_t size = physical_memory_ranges[count].NumberOfBytes.QuadPart; + + if (!address && !size) + { + break; + } + + range_list[count] = physical_memory_range(address, address + size); + } while (++count < range_list_size); + + ExFreePool(physical_memory_ranges); + } +}