/*!
    @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 on Windows.
    //
    // "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);
    _lgdt(&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 shared processor 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->SharedProcessorContext;
    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;
}
