Files
MiniVisorPkg/Sources/HostVmcall.c
2020-02-22 13:54:50 -08:00

90 lines
3.4 KiB
C

/*!
@file HostVmcall.c
@brief Implementation of hypercall functions.
@author Satoshi Tanda
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
*/
#include "HostVmcall.h"
_Use_decl_annotations_
VOID
HandleVmcallUninstall (
GUEST_CONTEXT* GuestContext
)
{
GDTR gdtr;
IDTR idtr;
//
// This hypercall is not allowed for ring 3.
//
if (IsGuestInKernelMode() == FALSE)
{
GuestContext->StackBasedRegisters->Rax = (UINT64)MV_STATUS_ACCESS_DENIED;
goto Exit;
}
//
// On VM-exit, the processor loads registers according with the Host state
// fields in the VMCS. Some registers are changed, e.g, GPRs, and some
// others are changed with hard-coded values. The limits of GDTR and IDTR
// are such example, and updated to 0xFFFF. When the VMRESUME instruction
// is executed, this is not an issue as VM-entry reloads the proper values
// from the guest state fields of the VMCS. However, it is not the case when
// the VMRESUME is not called, like here. In such a case those values must
// be restored with normal value manually, or PatchGuard will report
// integrity violation.
//
// "The GDTR and IDTR limits are each set to FFFFH."
// See: 27.5.2 Loading Host Segment and Descriptor-Table Registers
//
gdtr.BaseAddress = VmxRead(VMCS_GUEST_GDTR_BASE);
gdtr.Limit = (UINT16)VmxRead(VMCS_GUEST_GDTR_LIMIT);
_sgdt(&gdtr);
idtr.BaseAddress = VmxRead(VMCS_GUEST_IDTR_BASE);
idtr.Limit = (UINT16)VmxRead(VMCS_GUEST_IDTR_LIMIT);
__lidt(&idtr);
//
// The host may use a different CR3 than that of the guest. This is the case
// on EFI. Apply the guest one. This assumes that translation both the host
// CR3 and the guest CR3 has the same translation. Otherwise, the system will
// crash immediately after updating CR3.
//
__writecr3(VmxRead(VMCS_GUEST_CR3));
//
// Save some values needed for clean up in the volatile registers.
// RAX = The address of the all-processors context. This is used as a
// return value of the AsmVmxCall function.
// RCX = The address to continue execution after the execution of the VMXOFF
// instruction. This value is needed because we have to manually
// transfer execution instead of doing so automatically with the
// VMRESUME instruction in this pass.
// RDX = The RSP value to be restored. Same as the case of RIP, the RSP is
// not automatically restored in this pass, and so, has to be updated
// by the original value (not host's RSP).
// Param2 = The RFLAGS value to be restored. Also same as the case of RIP and
// RSP. Recall that RFLAGS is also updated automatically on VM-exit.
// "RFLAGS is cleared, except bit 1, which is always set."
// See: 27.5.3 Loading Host RIP, RSP, and RFLAGS
//
GuestContext->StackBasedRegisters->Rax = (UINT64)GuestContext->Contexts->VpContexts;
GuestContext->StackBasedRegisters->Rcx = GuestContext->VmcsBasedRegisters.Rip +
VmxRead(VMCS_VMEXIT_INSTRUCTION_LENGTH);
GuestContext->StackBasedRegisters->Rdx = GuestContext->VmcsBasedRegisters.Rsp;
GuestContext->StackBasedRegisters->R8 = GuestContext->VmcsBasedRegisters.Rflags.Flags;
//
// Finally, indicates that virtualization should be terminated.
//
GuestContext->ContinueVm = FALSE;
Exit:
return;
}