Emulate IA32_BIOS_SIGN_ID read

This commit is contained in:
Satoshi Tanda
2020-02-23 13:12:33 -08:00
parent b9b638d32b
commit fa47889170
4 changed files with 89 additions and 26 deletions

View File

@@ -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)
{
switch (msr)
{
case IA32_BIOS_SIGN_ID:
//
// Performs the same read on behalf of the guest.
// 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);

View File

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

View File

@@ -14,10 +14,6 @@
#include "Platform/EFI/EfiBitmap.h"
#endif
#if !defined(CHAR_BIT)
#define CHAR_BIT (8)
#endif
typedef struct _MEMORY_MANAGER_CONTEXT
{
//

View File

@@ -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 */
@@ -1164,6 +1154,33 @@ Exit:
}
}
/*!
@brief Initializes the MSR bitmaps.
@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.
@@ -1211,6 +1228,7 @@ EnableHypervisorOnAllProcessors (
goto Exit;
}
vpContexts->NumberOfContexts = numberOfProcessors;
InitializeMsrBitmaps(&vpContexts->MsrBitmaps);
//
// Start virtualizing processors one-by-one. This is done by changing