/*!
    @file HostUtils.h

    @brief Utility functions and structures for the host.

    @author Satoshi Tanda

    @copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
 */
#pragma once
#include "Common.h"
#include "Public.h"

//
// 128bit XMM register (ie, equivalent to __m128 on MSVC).
//
typedef struct _XMM
{
    UINT8 Value[16];
} XMM;

//
// Guest General Purpose Registers (GPRs) created on VM-exit from the guest
// state and write back to the guest on VM-entry.
//
typedef struct _GUEST_REGISTERS
{
    XMM Xmm[6];
    VOID* Alignment;
    UINT64 R15;
    UINT64 R14;
    UINT64 R13;
    UINT64 R12;
    UINT64 R11;
    UINT64 R10;
    UINT64 R9;
    UINT64 R8;
    UINT64 Rdi;
    UINT64 Rsi;
    UINT64 Rbp;
    UINT64 Rbx;
    UINT64 Rdx;
    UINT64 Rcx;
    UINT64 Rax;
} GUEST_REGISTERS;

//
// The guest registers that are stored in the VMCS as opposed to stack like
// ones in the GUEST_REGISTERS structure.
//
typedef struct _VMCS_BASED_REGISTERS
{
    UINT64 Rip;
    UINT64 Rsp;
    RFLAGS Rflags;
} VMCS_BASED_REGISTERS;

//
// State of the guest.
//
typedef struct _GUEST_CONTEXT
{
    //
    // Indicates that the processor should continue virtualization. FALSE of
    // results in disablement of hypervisor with the VMXOFF instruction. See
    // x64.asm. This value is used as a return value of the HandleVmExit function.
    //
    BOOLEAN ContinueVm;

    //
    // Collection of pointers passed from the kernel via the host stack.
    //
    HYPERVISOR_CONTEXT* Contexts;

    //
    // The guest states stored in hypervisor stack.
    //
    GUEST_REGISTERS* StackBasedRegisters;

    //
    // The guest states stored in the VMCS.
    //
    VMCS_BASED_REGISTERS VmcsBasedRegisters;
} GUEST_CONTEXT;

/*!
    @brief Dumps host state VMCS fields.
 */
VOID
DumpHostState (
    );

/*!
    @brief Dumps guest state VMCS fields.
 */
VOID
DumpGuestState (
    );

/*!
    @brief Dumps control VMCS fields.
 */
VOID
DumpControl (
    );

/*!
    @brief Writes the value to the VMCS.

    @param[in] Field - A VMCS field to write the value to.

    @param[in] FieldValue - A value to write.
 */
VOID
VmxWrite (
    _In_ VMCS_FIELD Field,
    _In_ UINT64 FieldValue
    );

/*!
    @brief Read a value from the VMCS.

    @param[in] Field - A VMCS field to read a value from.

    @return A value read from the VMCS. MAXUINT64 is returned when a non-existent
        VMCS field is requested for read.
 */
UINT64
VmxRead (
    _In_ VMCS_FIELD Field
    );

/*!
    @brief Advances the guest's RIP to the address of the next instruction. This
        implies that the hypervisor completed emulation of the instruction.

    @param[in,out] GuestContext - A pointer to the guest context.
 */
VOID
AdvanceGuestInstructionPointer (
    _Inout_ GUEST_CONTEXT* GuestContext
    );

/*!
    @brief Tests whether the guest was at the CPL 0 (kernel-mode) when VM-exit
        happened.

    @return TRUE when the guest was at the CPL 0, otherwise FALSE.
 */
_Must_inspect_result_
BOOLEAN
IsGuestInKernelMode (
    VOID
    );

/*!
    @brief Queues interrupt to occur to the VMCS.

    @details Generally, this interrupt fires on VM-entry and the guests runs a
        corresponding exception handler before executing the instruction pointed
        by Rip.

    @param[in] InterruptionType - A type of interrupt to inject.

    @param[in] Vector - A vector number of interrupt to inject.

    @param[in] DeliverErrorCode - TRUE when the interrupt should have an error
        code. Whether the interrupt should have an error code is defined by the
        Intel SDM. See comments in the EXCEPTION_VECTOR definitions for a quick
        reference.

    @param[in] ErrorCode - An error code. Not used when DeliverErrorCode is FALSE.
 */
VOID
InjectInterruption (
    _In_ INTERRUPTION_TYPE InterruptionType,
    _In_ EXCEPTION_VECTOR Vector,
    _In_ BOOLEAN DeliverErrorCode,
    _In_ UINT32 ErrorCode
    );

/*!
    @brief Switches the guest paging mode between 32 and 64bit modes according
        with CR0 and EFER.

    @param[in] NewGuestCr0 - The guest CR0 value to check the mode to switch to.
 */
VOID
SwitchGuestPagingMode (
    _In_ CR0 NewGuestCr0
    );

/*!
    @brief Returns the CR0 value after the FIXED0 and FIXED1 MSR values are applied
        for the guest.

    @param[in] Cr0 - The CR0 value to apply the FIXED0 and FIXED1 MSR values.

    @return The CR0 value where the FIXED0 and FIXED1 MSR values are applied.
 */
CR0
AdjustGuestCr0 (
    _In_ CR0 Cr0
    );

/*!
    @brief Returns the CR4 value after the FIXED0 and FIXED1 MSR values are applied
        for the guest.

    @param[in] Cr4 - The CR4 value to apply the FIXED0 and FIXED1 MSR values.

    @return The CR4 value where the FIXED0 and FIXED1 MSR values are applied.
 */
CR4
AdjustGuestCr4 (
    _In_ CR4 Cr4
    );

/*!
    @brief Find the base address of the image to which the specified address belongs.

    @param[in] GuestContext - The pointer to the guest context.

    @param[in] GuestVirtualAddress - The guest virtual address to find its image
        base.

    @return The base address of the image to which GuestVirtualAddress belongs, or
        0 on error.
 */
UINT64
FindImageBase (
    _In_ GUEST_CONTEXT* GuestContext,
    _In_ UINT64 GuestVirtualAddress
    );
