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
This commit is contained in:
Petr Benes 2019-07-24 23:30:41 +02:00
parent 188479c084
commit 9608bc71c4
17 changed files with 541 additions and 492 deletions

View file

@ -104,6 +104,9 @@
<ClCompile Include="hvpp\hvpp.cpp" />
<ClCompile Include="hvpp\hypervisor.cpp" />
<ClCompile Include="hvpp\ia32\memory.cpp" />
<ClCompile Include="hvpp\lib\mm\memory_mapper.cpp" />
<ClCompile Include="hvpp\lib\mm\win32\memory_mapper.cpp" />
<ClCompile Include="hvpp\lib\mm\win32\physical_memory_descriptor.cpp" />
<ClCompile Include="hvpp\vcpu.cpp" />
<ClCompile Include="hvpp\vmexit.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)/$(RelativeDir)/%(Filename)%(Extension).obj</ObjectFileName>
@ -139,6 +142,9 @@
<ClInclude Include="hvpp\config.h" />
<ClInclude Include="hvpp\ept.h" />
<ClInclude Include="hvpp\hypervisor.h" />
<ClInclude Include="hvpp\lib\mm\memory_mapper.h" />
<ClInclude Include="hvpp\lib\mm\mtrr_descriptor.h" />
<ClInclude Include="hvpp\lib\mm\physical_memory_descriptor.h" />
<ClInclude Include="hvpp\vcpu.h" />
<ClInclude Include="hvpp\vmexit.h" />
<ClInclude Include="hvpp\vmexit\vmexit_c_wrapper.h" />
@ -160,7 +166,6 @@
<ClInclude Include="hvpp\ia32\msr\arch.h" />
<ClInclude Include="hvpp\ia32\msr\mtrr.h" />
<ClInclude Include="hvpp\ia32\msr\vmx.h" />
<ClInclude Include="hvpp\ia32\mtrr.h" />
<ClInclude Include="hvpp\ia32\paging.h" />
<ClInclude Include="hvpp\ia32\vmx.h" />
<ClInclude Include="hvpp\ia32\vmx\exception_bitmap.h" />

View file

@ -67,6 +67,15 @@
<Filter Include="Header Files\hvpp\lib\vmware">
<UniqueIdentifier>{b199f4fb-0f50-4a62-b44d-f80541e9b91a}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\hvpp\lib\mm">
<UniqueIdentifier>{8e3b0296-08e1-46b7-83f6-555347ca5fe9}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\hvpp\lib\mm">
<UniqueIdentifier>{48924d45-28d4-4be4-96bc-ef93b3095d01}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\hvpp\lib\mm\win32">
<UniqueIdentifier>{c53a048f-d7da-48c6-88a5-fa1ed9bfc1ed}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="hvpp\lib\mm.cpp">
@ -138,6 +147,15 @@
<ClCompile Include="hvpp\ia32\memory.cpp">
<Filter>Source Files\hvpp\ia32</Filter>
</ClCompile>
<ClCompile Include="hvpp\lib\mm\memory_mapper.cpp">
<Filter>Source Files\hvpp\lib\mm</Filter>
</ClCompile>
<ClCompile Include="hvpp\lib\mm\win32\memory_mapper.cpp">
<Filter>Source Files\hvpp\lib\mm\win32</Filter>
</ClCompile>
<ClCompile Include="hvpp\lib\mm\win32\physical_memory_descriptor.cpp">
<Filter>Source Files\hvpp\lib\mm\win32</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="hvpp\lib\bitmap.h">
@ -158,9 +176,6 @@
<ClInclude Include="hvpp\ia32\msr.h">
<Filter>Header Files\hvpp\ia32</Filter>
</ClInclude>
<ClInclude Include="hvpp\ia32\mtrr.h">
<Filter>Header Files\hvpp\ia32</Filter>
</ClInclude>
<ClInclude Include="hvpp\ia32\vmx.h">
<Filter>Header Files\hvpp\ia32</Filter>
</ClInclude>
@ -302,6 +317,15 @@
<ClInclude Include="hvpp\interrupt.h">
<Filter>Header Files\hvpp</Filter>
</ClInclude>
<ClInclude Include="hvpp\lib\mm\memory_mapper.h">
<Filter>Header Files\hvpp\lib\mm</Filter>
</ClInclude>
<ClInclude Include="hvpp\lib\mm\mtrr_descriptor.h">
<Filter>Header Files\hvpp\lib\mm</Filter>
</ClInclude>
<ClInclude Include="hvpp\lib\mm\physical_memory_descriptor.h">
<Filter>Header Files\hvpp\lib\mm</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<MASM Include="hvpp\ia32\context.asm">

View file

@ -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<uint64_t>(mm::mtrr().type(empl4_pa));
eptptr_.memory_type = static_cast<uint64_t>(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;
}
}

View file

@ -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<cr3_t>()*/, pml level /*= pml::pt*/) const noexcept
pe_t* va_t::pt_entry(pml level /*= pml::pt*/) const noexcept
{
const auto pml4e = &reinterpret_cast<pe_t*>(
pa_t::from_pfn(cr3.page_frame_number).va()
pa_t::from_pfn(read<cr3_t>().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<cr3_t>()*/, 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<uint8_t*>(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<void*>(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<uint8_t*>(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();
}
}
}

View file

@ -1,10 +1,10 @@
#pragma once
#include "hvpp/lib/log.h"
#include "paging.h"
#include "arch.h"
#include <cstddef> // std::byte
#include <cstdint>
#include <cinttypes>
#include <numeric>
#include <type_traits>
@ -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<int>(result);
}
pe_t* pt_entry(cr3_t cr3 = read<cr3_t>(), 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<const physical_memory_range*>(&range_[0]); }
auto end() const noexcept { return const_cast<const physical_memory_range*>(&range_[count_]); }
auto size() const noexcept { return static_cast<size_t>(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)

View file

@ -1,209 +0,0 @@
#pragma once
#include "memory.h"
#include "msr.h"
#include "msr/mtrr.h"
#include "../lib/typelist.h"
#include <cstdint>
#include <cinttypes>
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<msr::mtrr_def_type_t>();
auto mtrr_capabilities = msr::read<msr::mtrr_capabilities_t>();
default_memory_type_ = static_cast<memory_type>(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<msr::mtrr_fix_64k_00000_t>;
using mtrr_fix_16k_list_t = type_list<msr::mtrr_fix_16k_80000_t, msr::mtrr_fix_16k_a0000_t>;
using mtrr_fix_4k_list_t = type_list<msr::mtrr_fix_4k_c0000_t, msr::mtrr_fix_4k_c8000_t,
msr::mtrr_fix_4k_d0000_t, msr::mtrr_fix_4k_d8000_t,
msr::mtrr_fix_4k_e0000_t, msr::mtrr_fix_4k_e8000_t,
msr::mtrr_fix_4k_f0000_t, msr::mtrr_fix_4k_f8000_t>;
using mtrr_fix_list_t = type_list<mtrr_fix_64k_list_t, mtrr_fix_16k_list_t, mtrr_fix_4k_list_t>;
for_each_type(mtrr_fix_list_t{}, [this](auto mtrr_fixed, int i) {
using ia32_mtrr_t = decltype(mtrr_fixed);
mtrr_fixed = msr::read<ia32_mtrr_t>();
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<memory_type>(type);
range += ia32_mtrr_t::mtrr_size;
i += 1;
}
});
}
}
void check_variable() noexcept
{
auto mtrr_capabilities = msr::read<msr::mtrr_capabilities_t>();
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::mtrr_physbase_t::msr_id + i * 2);
auto mtrr_mask = msr::read<msr::mtrr_physmask_t>(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<memory_type>(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;
};
}

View file

@ -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 <typename T>
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 <typename T>

View file

@ -2,8 +2,6 @@
#include <ntddk.h>
#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);
}
}

View file

@ -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();
//

View file

@ -66,8 +66,9 @@ namespace mm
allocator_t allocator[HVPP_MAX_CPU];
object_t<ia32::physical_memory_descriptor> memory_descriptor;
object_t<ia32::mtrr> memory_type_range_registers;
object_t<physical_memory_descriptor_t> physical_memory_descriptor;
object_t<mtrr_descriptor_t> mtrr_descriptor;
object_t<spinlock> 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<uint8_t*>(ia32::page_align(address)) + ia32::page_size;
address = reinterpret_cast<uint8_t*>(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<uint8_t*>(address);
global.page_bitmap_buffer_size = static_cast<int>(ia32::round_to_pages(size / ia32::page_size / 8));
global.page_bitmap_buffer_size = static_cast<int>(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<int>(size / ia32::page_size);
int page_bitmap_size_in_bits = static_cast<int>(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<pgmap_t*>(page_bitmap_buffer + global.page_bitmap_buffer_size);
global.page_allocation_map_size = static_cast<int>(ia32::round_to_pages(size / ia32::page_size) * sizeof(pgmap_t));
global.page_allocation_map_size = static_cast<int>(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<int>(ia32::bytes_to_pages(size));
int page_count = static_cast<int>(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<int>(ia32::bytes_to_pages(reinterpret_cast<uint8_t*>(address) - global.base_address));
const auto offset = static_cast<int>(bytes_to_pages(reinterpret_cast<uint8_t*>(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;
}
}

View file

@ -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 <cstdint>
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&;
}

View file

@ -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<uint8_t*>(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<void*>(buffer), size, true);
}
void memory_mapper::read_write(pa_t pa, void* buffer, size_t size, bool write) noexcept
{
auto byte_buffer = reinterpret_cast<uint8_t*>(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();
}
}
}

View file

@ -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_;
};
}

View file

@ -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 <cstdint>
#include <cinttypes>
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<msr::mtrr_def_type_t>();
auto mtrr_capabilities = msr::read<msr::mtrr_capabilities_t>();
default_memory_type_ = static_cast<memory_type>(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<msr::mtrr_fix_64k_00000_t>;
using mtrr_fix_16k_list_t = type_list<msr::mtrr_fix_16k_80000_t, msr::mtrr_fix_16k_a0000_t>;
using mtrr_fix_4k_list_t = type_list<msr::mtrr_fix_4k_c0000_t, msr::mtrr_fix_4k_c8000_t,
msr::mtrr_fix_4k_d0000_t, msr::mtrr_fix_4k_d8000_t,
msr::mtrr_fix_4k_e0000_t, msr::mtrr_fix_4k_e8000_t,
msr::mtrr_fix_4k_f0000_t, msr::mtrr_fix_4k_f8000_t>;
using mtrr_fix_list_t = type_list<mtrr_fix_64k_list_t, mtrr_fix_16k_list_t, mtrr_fix_4k_list_t>;
for_each_type(mtrr_fix_list_t{}, [this](auto mtrr_fixed, int i) {
using ia32_mtrr_t = decltype(mtrr_fixed);
mtrr_fixed = msr::read<ia32_mtrr_t>();
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<memory_type>(type);
range += ia32_mtrr_t::mtrr_size;
i += 1;
}
});
}
}
void check_variable() noexcept
{
auto mtrr_capabilities = msr::read<msr::mtrr_capabilities_t>();
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::mtrr_physbase_t::msr_id + i * 2);
auto mtrr_mask = msr::read<msr::mtrr_physmask_t>(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<memory_type>(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;
};
}

View file

@ -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<const physical_memory_range*>(&range_[0]); }
auto end() const noexcept { return const_cast<const physical_memory_range*>(&range_[count_]); }
auto size() const noexcept { return static_cast<size_t>(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;
};
}

View file

@ -0,0 +1,16 @@
#include <ntddk.h>
#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);
}
}

View file

@ -0,0 +1,30 @@
#include "hvpp/ia32/memory.h"
#include <ntddk.h>
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);
}
}