It's a bit-perfect (and maybe the most complete in some cases) bunch of architectural headers for i386/AMD64 platform written in C++17.
- Memory paging for all possible regimes and page sizes:
- Regimes:
- Legacy Non-PAE with and without CR4.PSE (4Kb and 4Mb pages).
- Legacy PAE (4Kb and 2Mb pages).
- Long-mode 4-Level (4Kb, 2Mb and 1Gb pages).
- Long-mode 5-Level (4Kb, 2Mb and 1Gb pages).
- With definitions for all possible page tables and entries:
- PML5 table with PML5E entries introduced with Intel 10th Gen (Ice Lake) and AMD Epyc 7xxx "Genoa" (Zen4).
- PML4 table with PML4E entries.
- PDP table with PDPE entries (also known as PDPT table and PDPTE entries in Intel terms) including PageSize forms.
- PD table with PDE entries including PageSize forms.
- PT table with PTE entries.
- Regimes:
- Segmentation:
- GDT - Global Descriptor Table.
- IDT - Interrupt Descriptor Table.
- LDT - Local Descriptor Table.
- TSS - Task State Segment.
- Segment selector (format of CS, DS, GS, FS, ES, SS and TR registers).
- Descriptor table register (IDTR, GDTR and LDTR registers).
- Descriptors (system- and user-segments and gates for all possible interpretations).
- System registers:
- EFlags.
- Control registers: CR0, CR, CR3, CR4, CR8.
- Debug registers: DR0..DR7.
- Interrupt vectors.
- CPUID with some leaves specific for Intel and AMD.
- MSRs (Model Specific Registers) for Intel and AMD:
- All possible MSR addresses for Intel and AMD.
- Layouts for some of them (including Memory Type Range Registers (MTRRs) for Intel).
- Architectural intrinsics for MSVC and CLang.
- Virtualization:
- Intel VT-x (also known as Intel VMX):
- VMCS - Virtual Machine Control Structure.
- Exit reasons.
- EPT tables.
- MSR permission map.
- VMENTRY configuration.
- VMEXIT qualification.
- AMD-V (also known as AMD SVM):
- VMCB - Virtual Machine Control Block.
- Exit reasons.
- NPT - Nested Page Tables (also known as AMD-RVI) which are the same as normal page tables.
- MSR permission map.
- Event injection layout.
- A bit of Hyper-V:
- Hypercall layout.
- Hypercall result values.
- Hypercall codes.
- Hypervisor status.
- Synthetic MSRs.
- Intel VT-x (also known as Intel VMX):
- MSVC or Clang (GCC has not tested).
- C++17 or above.
- Both usermode and kernelmode are supported.
Just add the folder Arch/include
to additional headers path and you're ready to go!
Let's start with CPUID example:
#include <Arch/x86/Cpuid.h>
int main()
{
const auto basicInfo = Cpuid::Cpuid::query<Cpuid::Generic::MaximumFunctionNumberAndVendorId>();
if (basicInfo->isIntel())
{
const auto features = Cpuid::Cpuid::query<Cpuid::Intel::FeatureInformation>();
printf("SSE 4.2 is supported: %u\n", features->SSE42);
}
else if (basicInfo->isAmd())
{
const auto features = Cpuid::Cpuid::query<Cpuid::Amd::FeatureInformation>();
printf("SSE 4.2 is supported: %u\n", features->SSE42);
}
else
{
/* Unknown CPU */
}
}
All right, let's go to kernel and try MSRs and control registers:
#include <Arch/x86/Registers.h>
void test()
{
const auto efer = Msr::Msr::read<Msr::Intel::Ia32Efer>();
if (efer->LME && efer->LMA) // Is long-mode enabled and active?
{
const auto cr3 = Regs::Native::Cr3::query();
const auto cr4 = Regs::Native::Cr4::query();
if (cr4->layout.LA57)
{
// 5-Level paging:
const auto pml5pfn = cr3->paging5Level.PML5;
...
}
else
{
// 4-Level paging:
const auto pml4pfn = cr3->paging4Level.PML4;
...
}
}
}
Well done! Let's try page traversal through a page table.
Suppose we're in long 4-Level paging mode and the page is presented - checks for presense of a page were omitted for the simplicity:
#include <ntifs.h>
#include <Arch/x86/Pte.h>
template <typename Type>
typename Type::Layout* phys2virt(const Type phys)
{
return MmGetVirtualForPhysical(PHYSICAL_ADDRESS{ .QuadPart = static_cast<long long>(phys); });
}
unsigned long long getPhysicalAddress(void* virtualAddress)
{
constexpr auto k_mode = Pte::Mode::longMode4Level;
using LinearAddress = Pte::LinearAddress<k_mode>;
using Tables = Pte::Tables<k_mode>;
const LinearAddress addr{ .raw = reinterpret_cast<size_t>(virtualAddress) };
const auto cr3Pfn = Regs::Native::Cr3::query()->paging4Level.PML4;
const auto* const pml4e = phys2virt(Tables::pml4e(cr3Pfn, addr));
const auto* const pdpe = phys2virt(pml4e->pdpe(addr));
switch (pdpe->pageSize())
{
case Pte::PageSize::nonPse:
{
const auto* const pde = phys2virt(pdpe->nonPse.pde(addr));
switch (pde->pageSize())
{
case Pte::PageSize::nonPse:
{
// 4Kb:
const auto* const pte = phys2virt(pde->nonPse.pte(addr));
const auto phys = pte->physicalAddress(addr);
return phys.physicalAddress;
}
case Pte::PageSize::pse:
{
// 2Mb:
const auto phys = pde->pse.physicalAddress(addr);
return phys.physicalAddress;
}
}
break;
}
case Pte::PageSize::pse:
{
// 1Gb:
const auto phys = pdpe->pse.physicalAddress(addr);
return phys.physicalAddress;
}
}
return 0; // Invalid translation
}