Skip to content

Commit

Permalink
Add EPT Support
Browse files Browse the repository at this point in the history
Create 1:1 512GB hugepage mappings with EPT enabled.
  • Loading branch information
Alex Ionescu committed Aug 26, 2016
1 parent 5ed9552 commit fd1d7e0
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -2,4 +2,5 @@
shv.sdf
x64/Optimized
*.suo
*.user
*.user
*.db
5 changes: 5 additions & 0 deletions shv.c
Expand Up @@ -99,6 +99,11 @@ ShvInitialize (
return STATUS_HV_INSUFFICIENT_BUFFER;
}

//
// Initialize the EPT structures
//
ShvVmxEptInitialize();

//
// Attempt to enter VMX root mode on all logical processors. This will
// broadcast a DPC interrupt which will execute the callback routine in
Expand Down
78 changes: 78 additions & 0 deletions shv.h
Expand Up @@ -69,6 +69,68 @@ typedef struct DECLSPEC_ALIGN(PAGE_SIZE) _VMX_VMCS
UCHAR Data[PAGE_SIZE - 8];
} VMX_VMCS, *PVMX_VMCS;

typedef struct _VMX_EPTP
{
union
{
struct
{
ULONGLONG Type : 3;
ULONGLONG PageWalkLength : 3;
ULONGLONG EnableAccessAndDirtyFlags : 1;
ULONGLONG Reserved : 5;
ULONGLONG PageFrameNumber : 36;
ULONGLONG ReservedHigh : 16;
};
ULONGLONG AsUlonglong;
};
} VMX_EPTP, *PVMX_EPTP;

typedef struct _VMX_EPML4E
{
union
{
struct
{
ULONGLONG Read : 1;
ULONGLONG Write : 1;
ULONGLONG Execute : 1;
ULONGLONG Reserved : 5;
ULONGLONG Accessed : 1;
ULONGLONG SoftwareUse : 3;
ULONGLONG PageFrameNumber : 36;
ULONGLONG ReservedHigh : 4;
ULONGLONG SoftwareUseHigh : 12;
};
ULONGLONG AsUlonglong;
};
} VMX_EPML4E, *PVMX_EPML4E;

typedef struct _VMX_HUGE_PDPTE
{
union
{
struct
{
ULONGLONG Read : 1;
ULONGLONG Write : 1;
ULONGLONG Execute : 1;
ULONGLONG Type : 3;
ULONGLONG IgnorePat : 1;
ULONGLONG Large : 1;
ULONGLONG Accessed : 1;
ULONGLONG Dirty : 1;
ULONGLONG SoftwareUse : 2;
ULONGLONG Reserved : 18;
ULONGLONG PageFrameNumber : 18;
ULONGLONG ReservedHigh : 4;
ULONGLONG SoftwareUseHigh : 11;
ULONGLONG SupressVme : 1;
};
ULONGLONG AsUlonglong;
};
} VMX_HUGE_PDPTE, *PVMX_HUGE_PDPTE;

typedef struct _SHV_VP_DATA
{
KPROCESSOR_STATE HostState;
Expand All @@ -79,6 +141,7 @@ typedef struct _SHV_VP_DATA
ULONGLONG VmxOnPhysicalAddress;
ULONGLONG VmcsPhysicalAddress;
ULONGLONG MsrBitmapPhysicalAddress;
ULONGLONG EptPml4PhysicalAddress;

DECLSPEC_ALIGN(PAGE_SIZE) UCHAR ShvStackLimit[KERNEL_STACK_SIZE];
VMX_VMCS VmxOn;
Expand All @@ -87,12 +150,22 @@ typedef struct _SHV_VP_DATA

C_ASSERT(sizeof(SHV_VP_DATA) == (KERNEL_STACK_SIZE + 3 * PAGE_SIZE));

C_ASSERT(sizeof(VMX_EPTP) == sizeof(ULONGLONG));
C_ASSERT(sizeof(VMX_EPML4E) == sizeof(ULONGLONG));

#define PML4E_ENTRY_COUNT 512
#define PDPTE_ENTRY_COUNT 512
typedef struct _SHV_GLOBAL_DATA
{
UCHAR MsrBitmap[PAGE_SIZE];
VMX_EPML4E Epml4[PML4E_ENTRY_COUNT];
VMX_HUGE_PDPTE Epdpt[PDPTE_ENTRY_COUNT];
SHV_VP_DATA VpData[ANYSIZE_ARRAY];
} SHV_GLOBAL_DATA, *PSHV_GLOBAL_DATA;

C_ASSERT((FIELD_OFFSET(SHV_GLOBAL_DATA, Epml4) % PAGE_SIZE) == 0);
C_ASSERT((FIELD_OFFSET(SHV_GLOBAL_DATA, Epdpt) % PAGE_SIZE) == 0);

typedef struct _SHV_VP_STATE
{
PCONTEXT VpRegs;
Expand Down Expand Up @@ -148,6 +221,11 @@ ShvVmxProbe (
VOID
);

VOID
ShvVmxEptInitialize (
VOID
);

KDEFERRED_ROUTINE ShvVpCallbackDpc;

extern PSHV_GLOBAL_DATA ShvGlobalData;
70 changes: 67 additions & 3 deletions shvvmx.c
Expand Up @@ -22,6 +22,37 @@ Module Name:

#include "shv.h"

VOID
ShvVmxEptInitialize (
VOID
)
{
ULONGLONG i;
VMX_HUGE_PDPTE tempEpdpte;

//
// Fill out the EPML4E which covers the first 512GB of RAM
//
ShvGlobalData->Epml4[0].Read = 1;
ShvGlobalData->Epml4[0].Write = 1;
ShvGlobalData->Epml4[0].Execute = 1;
ShvGlobalData->Epml4[0].PageFrameNumber = MmGetPhysicalAddress(&ShvGlobalData->Epdpt).QuadPart / PAGE_SIZE;

//
// Fill out a RWX Write-back 1GB EPDPTE
//
tempEpdpte.AsUlonglong = 0;
tempEpdpte.Read = tempEpdpte.Write = tempEpdpte.Execute = 1;
tempEpdpte.Type = MTRR_TYPE_WB;
tempEpdpte.Large = 1;

//
// Construct EPT identity map for every 1GB of RAM
//
__stosq((PULONG64)ShvGlobalData->Epdpt, tempEpdpte.AsUlonglong, PDPTE_ENTRY_COUNT);
for (i = 0; i < PDPTE_ENTRY_COUNT; i++) ShvGlobalData->Epdpt[i].PageFrameNumber = i;
}

BOOLEAN
ShvVmxEnterRootModeOnVp (
_In_ PSHV_VP_DATA VpData
Expand Down Expand Up @@ -53,6 +84,16 @@ ShvVmxEnterRootModeOnVp (
return FALSE;
}

//
// Ensure that EPT is available with the needed features SimpleVisor uses
//
if (((VpData->MsrData[12].QuadPart & VMX_EPT_PAGE_WALK_4_BIT) == 0) ||
((VpData->MsrData[12].QuadPart & VMX_EPTP_WB_BIT) == 0) ||
((VpData->MsrData[12].QuadPart & VMX_EPT_1GB_PAGE_BIT) == 0))
{
return FALSE;
}

//
// Capture the revision ID for the VMXON and VMCS region
//
Expand All @@ -65,6 +106,7 @@ ShvVmxEnterRootModeOnVp (
VpData->VmxOnPhysicalAddress = MmGetPhysicalAddress(&VpData->VmxOn).QuadPart;
VpData->VmcsPhysicalAddress = MmGetPhysicalAddress(&VpData->Vmcs).QuadPart;
VpData->MsrBitmapPhysicalAddress = MmGetPhysicalAddress(ShvGlobalData->MsrBitmap).QuadPart;
VpData->EptPml4PhysicalAddress = MmGetPhysicalAddress(&ShvGlobalData->Epml4).QuadPart;

//
// Update CR0 with the must-be-zero and must-be-one requirements
Expand Down Expand Up @@ -121,12 +163,26 @@ ShvVmxSetupVmcsForVp (
{
PKPROCESSOR_STATE state = &VpData->HostState;
VMX_GDTENTRY64 vmxGdtEntry;
VMX_EPTP vmxEptp;

//
// Begin by setting the link pointer to the required value for 4KB VMCS.
//
__vmx_vmwrite(VMCS_LINK_POINTER, MAXULONG64);

//
// Configure the EPTP
//
vmxEptp.AsUlonglong = 0;
vmxEptp.PageWalkLength = 3;
vmxEptp.Type = MTRR_TYPE_WB;
vmxEptp.PageFrameNumber = VpData->EptPml4PhysicalAddress / PAGE_SIZE;

//
// Load EPT Root Pointer
//
__vmx_vmwrite(EPT_POINTER, vmxEptp.AsUlonglong);

//
// Load the MSR bitmap. Unlike other bitmaps, not having an MSR bitmap will
// trap all MSRs, so have to allocate an empty one.
Expand All @@ -139,9 +195,15 @@ ShvVmxSetupVmcsForVp (
// ShvUtilAdjustMsr, these options will be ignored if this processor does
// not actully support the instructions to begin with.
//
// Also enable EPT support, for additional performance and ability to trap
// memory access efficiently.
//
__vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL,
ShvUtilAdjustMsr(VpData->MsrData[11],
SECONDARY_EXEC_ENABLE_RDTSCP | SECONDARY_EXEC_XSAVES));
SECONDARY_EXEC_ENABLE_RDTSCP |
SECONDARY_EXEC_XSAVES
| SECONDARY_EXEC_ENABLE_EPT
));

//
// Enable no pin-based options ourselves, but there may be some required by
Expand All @@ -157,15 +219,17 @@ ShvVmxSetupVmcsForVp (
//
__vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL,
ShvUtilAdjustMsr(VpData->MsrData[14],
CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS));
CPU_BASED_ACTIVATE_MSR_BITMAP |
CPU_BASED_ACTIVATE_SECONDARY_CONTROLS));

//
// If any interrupts were pending upon entering the hypervisor, acknowledge
// them when we're done. And make sure to enter us in x64 mode at all times
//
__vmx_vmwrite(VM_EXIT_CONTROLS,
ShvUtilAdjustMsr(VpData->MsrData[15],
VM_EXIT_ACK_INTR_ON_EXIT | VM_EXIT_IA32E_MODE));
VM_EXIT_ACK_INTR_ON_EXIT |
VM_EXIT_IA32E_MODE));

//
// As we exit back into the guest, make sure to exist in x64 mode as well.
Expand Down
11 changes: 11 additions & 0 deletions vmx.h
Expand Up @@ -97,6 +97,17 @@ Header Name:
#define VMX_BASIC_INS_OUT_INFO (1ULL << 54)
#define VMX_BASIC_DEFAULT1_ZERO (1ULL << 55)

#define VMX_EPT_EXECUTE_ONLY_BIT (1ULL)
#define VMX_EPT_PAGE_WALK_4_BIT (1ULL << 6)
#define VMX_EPTP_UC_BIT (1ULL << 8)
#define VMX_EPTP_WB_BIT (1ULL << 14)
#define VMX_EPT_2MB_PAGE_BIT (1ULL << 16)
#define VMX_EPT_1GB_PAGE_BIT (1ULL << 17)
#define VMX_EPT_INVEPT_BIT (1ULL << 20)
#define VMX_EPT_AD_BIT (1ULL << 21)
#define VMX_EPT_EXTENT_CONTEXT_BIT (1ULL << 25)
#define VMX_EPT_EXTENT_GLOBAL_BIT (1ULL << 26)

/* MSRs & bits used for VMX enabling */
#define MSR_IA32_VMX_BASIC 0x480
#define MSR_IA32_VMX_PINBASED_CTLS 0x481
Expand Down

0 comments on commit fd1d7e0

Please sign in to comment.