From fa478891704ed567817d0026627c710c9899b145 Mon Sep 17 00:00:00 2001 From: Satoshi Tanda Date: Sun, 23 Feb 2020 13:12:33 -0800 Subject: [PATCH] Emulate IA32_BIOS_SIGN_ID read --- Sources/HostMain.c | 37 +++++++++++++++++++++++++------ Sources/Ia32.h | 26 ++++++++++++++++++++++ Sources/MemoryManager.c | 4 ---- Sources/MiniVisor.c | 48 ++++++++++++++++++++++++++++------------- 4 files changed, 89 insertions(+), 26 deletions(-) diff --git a/Sources/HostMain.c b/Sources/HostMain.c index f5a0f3a..4195cf2 100644 --- a/Sources/HostMain.c +++ b/Sources/HostMain.c @@ -47,6 +47,12 @@ typedef struct _INITIAL_HYPERVISOR_STACK /*! @brief Handles VM-exit due to execution of the RDMSR and WRMSR instruction. + @details Accessing MSR can results in #GP(0) that would have been handled by + the guest. However, in this context, this results in host exception leading + to the panic (See HandleHostException). For graceful handling, the handler + can check the exception is #GP(0) caused by RDMSR or WRMSR, and if this + is the case, inject it to the guest. + @param[in,out] GuestContext - A pointer to the guest context. @param[in] OperationType - The type of the operation. @@ -64,18 +70,35 @@ HandleMsrAccess ( msr = (IA32_MSR_ADDRESS)GuestContext->StackBasedRegisters->Rcx; if (OperationType == OperationRead) { - // - // Performs the same read on behalf of the guest. - // - value = __readmsr(msr); + switch (msr) + { + case IA32_BIOS_SIGN_ID: + // + // Linux reads this MSR during boot and may attempt to update BIOS + // microcode. Returning the greater value than the value the kernel + // wishes prevent it from attempt to update microcode. APs will enter + // infinite INIT-SIPI loop if this is not done. + // + // "The VMM may wish to prevent a guest from loading a microcode + // update (...). To prevent microcode update loading, the VMM may + // return a microcode update signature value greater than the value + // of IA32_BIOS_SIGN_ID MSR. A well behaved guest will not attempt + // to load an older microcode update." + // See: 32.4.2 Late Load of Microcode Updates + // + value = MAXUINT64; + break; + + default: + value = __readmsr(msr); + break; + } + GuestContext->StackBasedRegisters->Rax = value & MAXUINT32; GuestContext->StackBasedRegisters->Rdx = (value >> 32) & MAXUINT32; } else { - // - // Performs the same write on behalf of the guest. - // value = (GuestContext->StackBasedRegisters->Rax & MAXUINT32) | ((GuestContext->StackBasedRegisters->Rdx & MAXUINT32) << 32); __writemsr(msr, value); diff --git a/Sources/Ia32.h b/Sources/Ia32.h index 66bca8c..01b5a4a 100644 --- a/Sources/Ia32.h +++ b/Sources/Ia32.h @@ -22,6 +22,10 @@ #include "ia32-doc/out/ia32.h" #pragma warning(pop) +#if !defined(CHAR_BIT) +#define CHAR_BIT (8) +#endif + // // The entry count within an EPT page table. // @@ -137,3 +141,25 @@ typedef struct _TASK_STATE_SEGMENT_64 } TASK_STATE_SEGMENT_64; C_ASSERT(sizeof(TASK_STATE_SEGMENT_64) == 104); #pragma pack(pop) + +// +// The page-aligned, 4KB size region used as a MSR bitmap. The MSR bitmap is +// used to indicate which MSR should cause VM-exit on RDMSR and WRMSR. Each +// bit in this 4KB region represents ON or OFF of VM-exit, where 0 indicates +// not to trigger, and 1 indicates to trigger VM-exit. This hypervisor does +// not intend to handle MSR accesses and so, all bits are left as 0. It is +// important that this bitmap governs VM-exit behavior only for certain sets +// of MSRs. An access to any MSR that is not governed by this bitmap still +// causes VM-exit unconditionally. For this reason, this hypervisor still +// has RDMSR and WRMSR handling logic. +// +// See: 24.6.9 MSR-Bitmap Address +// +typedef struct _MSR_BITMAPS +{ + UINT8 ReadBitmapLow[1024]; + UINT8 ReadBitmapHigh[1024]; + UINT8 WriteBitmapLow[1024]; + UINT8 WriteBitmapHigh[1024]; +} MSR_BITMAPS; +C_ASSERT(sizeof(MSR_BITMAPS) == PAGE_SIZE); diff --git a/Sources/MemoryManager.c b/Sources/MemoryManager.c index 3c7ce1d..2dee37c 100644 --- a/Sources/MemoryManager.c +++ b/Sources/MemoryManager.c @@ -14,10 +14,6 @@ #include "Platform/EFI/EfiBitmap.h" #endif -#if !defined(CHAR_BIT) -#define CHAR_BIT (8) -#endif - typedef struct _MEMORY_MANAGER_CONTEXT { // diff --git a/Sources/MiniVisor.c b/Sources/MiniVisor.c index 475fad0..3f7afbc 100644 --- a/Sources/MiniVisor.c +++ b/Sources/MiniVisor.c @@ -149,19 +149,9 @@ typedef struct _SHARED_PROCESSOR_CONTEXT UINT32 NumberOfContexts; // - // The page-aligned, 4KB size region used as a MSR bitmap. The MSR bitmap is - // used to indicate which MSR should cause VM-exit on RDMSR and WRMSR. Each - // bit in this 4KB region represents ON or OFF of VM-exit, where 0 indicates - // not to trigger, and 1 indicates to trigger VM-exit. This hypervisor does - // not intend to handle MSR accesses and so, all bits are left as 0. It is - // important that this bitmap governs VM-exit behavior only for certain sets - // of MSRs. An access to any MSR that is not governed by this bitmap still - // causes VM-exit unconditionally. For this reason, this hypervisor still - // has RDMSR and WRMSR handling logic. + // The MSR bitmap used across all processors. // - // See: 24.6.9 MSR-Bitmap Address - // - DECLSPEC_ALIGN(PAGE_SIZE) VOID* MsrBitmap; + DECLSPEC_ALIGN(PAGE_SIZE) MSR_BITMAPS MsrBitmaps; // // An array of PER_PROCESSOR_CONTEXTs. Each context is associated with and @@ -918,7 +908,7 @@ SetupVmcs ( VmxWrite(VMCS_CTRL_VIRTUAL_PROCESSOR_IDENTIFIER, 1); /* 64-Bit Control Fields */ - VmxWrite(VMCS_CTRL_MSR_BITMAP_ADDRESS, GetPhysicalAddress(&VpContexts->MsrBitmap)); + VmxWrite(VMCS_CTRL_MSR_BITMAP_ADDRESS, GetPhysicalAddress(&VpContexts->MsrBitmaps)); VmxWrite(VMCS_CTRL_EPT_POINTER, VpContext->EptContext.EptPointer.Flags); /* 32-Bit Control Fields */ @@ -1165,9 +1155,36 @@ Exit: } /*! - @brief Enables hypervisor on the all processors. + @brief Initializes the MSR bitmaps. - @return MV_STATUS_SUCCESS on success; otherwise, an appropriate error code. + @details This function clears the bitmaps to avoid VM-exits that do not require + manual handling. The MSR that requires manual handling for MiniVisor is + IA32_BIOS_SIGN_ID to prevent the guest from attempting update BIOS + microcode. See HandleMsrAccess for more details. + + @param[out] Bitmaps - The pointer to the MSR bitmaps to initialize. + */ +MV_SECTION_PAGED +static +VOID +InitializeMsrBitmaps ( + _Out_ MSR_BITMAPS* Bitmaps + ) +{ + static CONST UINT64 biosSignatureByteOffset = (IA32_BIOS_SIGN_ID / CHAR_BIT); + static CONST UINT64 biosSignatureBitMask = (1ull << (IA32_BIOS_SIGN_ID % CHAR_BIT)); + + PAGED_CODE() + + RtlZeroMemory(Bitmaps, sizeof(*Bitmaps)); + + Bitmaps->ReadBitmapLow[biosSignatureByteOffset] |= biosSignatureBitMask; +} + +/*! + @brief Enables hypervisor on the all processors. + + @return MV_STATUS_SUCCESS on success; otherwise, an appropriate error code. */ MV_SECTION_PAGED static @@ -1211,6 +1228,7 @@ EnableHypervisorOnAllProcessors ( goto Exit; } vpContexts->NumberOfContexts = numberOfProcessors; + InitializeMsrBitmaps(&vpContexts->MsrBitmaps); // // Start virtualizing processors one-by-one. This is done by changing