Update comments
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# Locates the image base from the current RIP value. This can be manually invoked
|
||||||
|
# from the IDA Pro during the GDB remote debug session to load symbols (a PDB file).
|
||||||
|
#
|
||||||
|
# Author: Satoshi Tanda
|
||||||
current_page_base = idaapi.get_reg_val('rip') & (~0xfff)
|
current_page_base = idaapi.get_reg_val('rip') & (~0xfff)
|
||||||
offset = 0
|
offset = 0
|
||||||
while idc.read_dbg_word(current_page_base - offset) != 0x5a4d:
|
while idc.read_dbg_word(current_page_base - offset) != 0x5a4d:
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# Copies the lib files created by the EDK2's build command to a single specified
|
||||||
|
# locations, so that Visual Studio can easily find and link them. Invoked as
|
||||||
|
# part of Pre-Link Event of the MiniVisor project.
|
||||||
|
#
|
||||||
|
# Author: Satoshi Tanda
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
|||||||
@@ -23,17 +23,17 @@ extern HandleVmExit : proc
|
|||||||
extern HandleVmExitFailure : proc
|
extern HandleVmExitFailure : proc
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief An entry point for the hypervisor.
|
; @brief The entry point for the hypervisor.
|
||||||
;
|
;
|
||||||
; @details The easiest way to understand this code is to see this as an entry
|
; @details The easiest way to understand this code is to see this as an entry
|
||||||
; point of "VM-exit handler".
|
; point of "VM-exit handler".
|
||||||
;
|
;
|
||||||
; Up on VM-exit, the processor starts executing this function as
|
; Up on VM-exit, the processor starts executing this function as
|
||||||
; condifured in the VmcsHostRip field of VMCS. At this time, the processor
|
; configured in the Host RIP field of VMCS. When this function is executed,
|
||||||
; is in the vmx-root mode, which allows the processor to execute any
|
; the processor is in the vmx-root mode, which allows the processor to
|
||||||
; instructions without causing VM-exit, and the processor is not governed
|
; execute any instructions without causing VM-exit, and the processor is
|
||||||
; by EPT. The code executed from here emulates the instruction caused
|
; not governed by EPT. The code executed from here most typically emulates
|
||||||
; VM-exit by, most typically, executing the same instruction on behalf of
|
; the instruction caused VM-exit by executing the same instruction on behalf of
|
||||||
; the guest (see HandleCpuid for example), or changing relevant processor
|
; the guest (see HandleCpuid for example), or changing relevant processor
|
||||||
; state and letting the guest retry, for example, handling EPT violation.
|
; state and letting the guest retry, for example, handling EPT violation.
|
||||||
;
|
;
|
||||||
@@ -41,6 +41,8 @@ extern HandleVmExitFailure : proc
|
|||||||
; context. We also refer those code as a VM-exit handler.
|
; context. We also refer those code as a VM-exit handler.
|
||||||
;
|
;
|
||||||
AsmHypervisorEntryPoint proc frame
|
AsmHypervisorEntryPoint proc frame
|
||||||
|
;
|
||||||
|
; Windows-specific:
|
||||||
;
|
;
|
||||||
; Three not-well known techniques are used in this function in oder for
|
; Three not-well known techniques are used in this function in oder for
|
||||||
; Windbg to display the stack trace of the guest while the VM-exit
|
; Windbg to display the stack trace of the guest while the VM-exit
|
||||||
@@ -177,9 +179,8 @@ ExitVm:
|
|||||||
|
|
||||||
VmxError:
|
VmxError:
|
||||||
;
|
;
|
||||||
; Any of VMX instructions failed. Unrecoverble. The most useful thing
|
; VMRESUME or VMXOFF instruction failed. Unrecoverble. The most useful
|
||||||
; to do here is probably to call a C-function that does diagnostics
|
; thing to do here is to call a C-function to diagnose the issue.
|
||||||
; like dumping VMCS.
|
|
||||||
;
|
;
|
||||||
pushf
|
pushf
|
||||||
PUSHAQ
|
PUSHAQ
|
||||||
@@ -190,11 +191,11 @@ VmxError:
|
|||||||
AsmHypervisorEntryPoint endp
|
AsmHypervisorEntryPoint endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Invalidate translations derived from EPT
|
; @brief Invalidates translations derived from EPT
|
||||||
;
|
;
|
||||||
; @param[in] RCX - A type of invalidation.
|
; @param[in] RCX - The type of invalidation.
|
||||||
;
|
;
|
||||||
; @param[in] RDX - A description of translations to invalidate.
|
; @param[in] RDX - The description of translations to invalidate.
|
||||||
;
|
;
|
||||||
; @return An appropriate VMX_RESULT value.
|
; @return An appropriate VMX_RESULT value.
|
||||||
;
|
;
|
||||||
@@ -221,11 +222,11 @@ ErrorWithoutCode:
|
|||||||
AsmInvept endp
|
AsmInvept endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Invalidate translations based on VPID
|
; @brief Invalidates translations based on VPID
|
||||||
;
|
;
|
||||||
; @param[in] RCX - A type of invalidation.
|
; @param[in] RCX - The type of invalidation.
|
||||||
;
|
;
|
||||||
; @param[in] RDX - A description of translations to invalidate.
|
; @param[in] RDX - The description of translations to invalidate.
|
||||||
;
|
;
|
||||||
; @return An appropriate VMX_RESULT value.
|
; @return An appropriate VMX_RESULT value.
|
||||||
;
|
;
|
||||||
@@ -287,9 +288,9 @@ AsmVmxCall proc
|
|||||||
AsmVmxCall endp
|
AsmVmxCall endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Returns the address of the return address from this function.
|
; @brief Returns the return address from this function.
|
||||||
;
|
;
|
||||||
; @return The address of the return address from this function.
|
; @return The return address from this function.
|
||||||
;
|
;
|
||||||
AsmGetCurrentInstructionPointer proc
|
AsmGetCurrentInstructionPointer proc
|
||||||
mov rax, [rsp]
|
mov rax, [rsp]
|
||||||
|
|||||||
@@ -10,28 +10,27 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
#if defined(NTDDI_VERSION)
|
#if defined(MV_PLATFORM_WINDOWS)
|
||||||
#include "Platform/Windows/WinAsm.h"
|
#include "Platform/Windows/WinAsm.h"
|
||||||
#else
|
#else
|
||||||
#include "Platform/EFI/EfiAsm.h"
|
#include "Platform/EFI/EfiAsm.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief An entry point for the hypervisor.
|
@brief The entry point for the hypervisor.
|
||||||
|
|
||||||
@details See x64.asm.
|
@details See Asm.asm.
|
||||||
*/
|
*/
|
||||||
VOID
|
VOID
|
||||||
AsmHypervisorEntryPoint (
|
AsmHypervisorEntryPoint (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Invalidate translations derived from EPT.
|
@brief Invalidates translations derived from EPT.
|
||||||
|
|
||||||
@param[in] InvEptType - A type of invalidation.
|
@param[in] InvEptType - The type of invalidation.
|
||||||
|
|
||||||
@param[in] InvEptDescriptor - A description of translations to invalidate.
|
@param[in] InvEptDescriptor - The description of translations to invalidate.
|
||||||
|
|
||||||
@return An appropriate VMX_RESULT value.
|
@return An appropriate VMX_RESULT value.
|
||||||
*/
|
*/
|
||||||
@@ -42,11 +41,11 @@ AsmInvept (
|
|||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Invalidate translations based on VPID.
|
@brief Invalidates translations based on VPID.
|
||||||
|
|
||||||
@param[in] InvVpidType - A type of invalidation.
|
@param[in] InvVpidType - The type of invalidation.
|
||||||
|
|
||||||
@param[in] InvVpidDescriptor - A description of translations to invalidate.
|
@param[in] InvVpidDescriptor - The description of translations to invalidate.
|
||||||
|
|
||||||
@return An appropriate VMX_RESULT value.
|
@return An appropriate VMX_RESULT value.
|
||||||
*/
|
*/
|
||||||
@@ -56,7 +55,6 @@ AsmInvvpid (
|
|||||||
_In_ CONST INVVPID_DESCRIPTOR* InvVpidDescriptor
|
_In_ CONST INVVPID_DESCRIPTOR* InvVpidDescriptor
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Reads the access rights byte of the segment.
|
@brief Reads the access rights byte of the segment.
|
||||||
|
|
||||||
@@ -93,13 +91,12 @@ AsmVmxCall (
|
|||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Returns the address of the return address from this function.
|
@brief Returns the return address from this function.
|
||||||
|
|
||||||
@return The address of the return address from this function.
|
@return The return address from this function.
|
||||||
*/
|
*/
|
||||||
UINT64
|
UINT64
|
||||||
AsmGetCurrentInstructionPointer (
|
AsmGetCurrentInstructionPointer (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -109,5 +106,4 @@ AsmGetCurrentInstructionPointer (
|
|||||||
*/
|
*/
|
||||||
UINT64
|
UINT64
|
||||||
AsmGetCurrentStackPointer (
|
AsmGetCurrentStackPointer (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,10 +9,17 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#if defined(NTDDI_VERSION)
|
#if defined(NTDDI_VERSION)
|
||||||
|
#define MV_PLATFORM_WINDOWS
|
||||||
|
#else
|
||||||
|
#define MV_PLATFORM_EFI
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(MV_PLATFORM_WINDOWS)
|
||||||
#include "Platform/Windows/WinCommon.h"
|
#include "Platform/Windows/WinCommon.h"
|
||||||
#else
|
#else
|
||||||
#include "Platform/EFI/EfiCommon.h"
|
#include "Platform/EFI/EfiCommon.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Ia32.h"
|
#include "Ia32.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ typedef struct _EPT_ENTRIES
|
|||||||
C_ASSERT(sizeof(EPT_ENTRIES) == sizeof(VOID*) * 4);
|
C_ASSERT(sizeof(EPT_ENTRIES) == sizeof(VOID*) * 4);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Cleans up all EPT entries and the table recursively.
|
@brief Cleans up all EPT entries and the tables recursively.
|
||||||
|
|
||||||
@param[in,out] EptTable - A pointer to the EPT table to clean up.
|
@param[in,out] EptTable - The pointer to the EPT table to clean up.
|
||||||
|
|
||||||
@param[in] PageMapLevel - A level of the table.
|
@param[in] PageMapLevel - The level of the table.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
|
|||||||
@@ -63,9 +63,9 @@ typedef struct _EPT_CONTEXT
|
|||||||
} EPT_CONTEXT;
|
} EPT_CONTEXT;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Initializes EPT with pass-through style configurations.
|
@brief Initializes identity-mapping EPTs.
|
||||||
|
|
||||||
@param[in,out] EptContext - A pointer to the EPT context to initialize.
|
@param[in,out] EptContext - The pointer to the EPT context to initialize.
|
||||||
|
|
||||||
@return MV_STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
@return MV_STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
||||||
*/
|
*/
|
||||||
@@ -76,9 +76,9 @@ InitializeExtendedPageTables (
|
|||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Cleans up EPT context.
|
@brief Cleans up the EPT context.
|
||||||
|
|
||||||
@param[in,out] EptContext - A pointer to the EPT context to clean up.
|
@param[in,out] EptContext - The pointer to the EPT context to clean up.
|
||||||
*/
|
*/
|
||||||
VOID
|
VOID
|
||||||
CleanupExtendedPageTables (
|
CleanupExtendedPageTables (
|
||||||
@@ -110,7 +110,7 @@ UpdateExtendPageTables (
|
|||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Invalidate guest-physical and combined caches.
|
@brief Invalidates guest-physical and combined caches.
|
||||||
|
|
||||||
@param[in] EptPointer - The EPT pointer to invalidate associated caches. If
|
@param[in] EptPointer - The EPT pointer to invalidate associated caches. If
|
||||||
0 is specified, caches associated with any EPT pointers are invalidated.
|
0 is specified, caches associated with any EPT pointers are invalidated.
|
||||||
@@ -121,10 +121,10 @@ InvalidateEptDerivedCache (
|
|||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Invalidate liner and combined caches.
|
@brief Invalidates liner and combined caches.
|
||||||
|
|
||||||
@param[in] VirtualProcessorId - The VPID to invalidate associated caches. If
|
@param[in] VirtualProcessorId - The VPID to invalidate associated caches. If
|
||||||
0 is specified, caches associated with any VPID are invalidated.
|
0 is specified, caches associated with any VPIDs are invalidated.
|
||||||
*/
|
*/
|
||||||
VOID
|
VOID
|
||||||
InvalidateVpidDerivedCache (
|
InvalidateVpidDerivedCache (
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
#include "HostVmcall.h"
|
#include "HostVmcall.h"
|
||||||
#include "HostNesting.h"
|
#include "HostNesting.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Windows-specific:
|
||||||
//
|
//
|
||||||
// The trap frame structure for x64 systems. This is structure is used to help
|
// The trap frame structure for x64 systems. This is structure is used to help
|
||||||
// Windbg to construct call stack while VM-exit handlers are being executed.
|
// Windbg to construct call stack while VM-exit handlers are being executed.
|
||||||
@@ -53,7 +55,7 @@ typedef struct _INITIAL_HYPERVISOR_STACK
|
|||||||
can check the exception is #GP(0) caused by RDMSR or WRMSR, and if this
|
can check the exception is #GP(0) caused by RDMSR or WRMSR, and if this
|
||||||
is the case, inject it to the guest.
|
is the case, inject it to the guest.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
|
|
||||||
@param[in] OperationType - The type of the operation.
|
@param[in] OperationType - The type of the operation.
|
||||||
*/
|
*/
|
||||||
@@ -110,7 +112,7 @@ HandleMsrAccess (
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to execution of the RDMSR instruction.
|
@brief Handles VM-exit due to execution of the RDMSR instruction.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -124,7 +126,7 @@ HandleMsrRead (
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to execution of the WRMSR instruction.
|
@brief Handles VM-exit due to execution of the WRMSR instruction.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -138,7 +140,7 @@ HandleMsrWrite (
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to execution of the CPUID instruction.
|
@brief Handles VM-exit due to execution of the CPUID instruction.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -208,7 +210,7 @@ HandleCpuid (
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to execution of the VMCALL instruction.
|
@brief Handles VM-exit due to execution of the VMCALL instruction.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -245,7 +247,7 @@ Exit:
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to execution of the XSETBV instruction.
|
@brief Handles VM-exit due to execution of the XSETBV instruction.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -275,7 +277,7 @@ HandleXsetbv (
|
|||||||
@brief Returns the address of where the guest general purpose register that
|
@brief Returns the address of where the guest general purpose register that
|
||||||
corresponds to the given index is stored.
|
corresponds to the given index is stored.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
|
|
||||||
@param[in] RegisterIndex - The index provided by VMCS up on VM-exit.
|
@param[in] RegisterIndex - The index provided by VMCS up on VM-exit.
|
||||||
|
|
||||||
@@ -318,7 +320,7 @@ SelectEffectiveRegister (
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to execution of access to the control register.
|
@brief Handles VM-exit due to execution of access to the control register.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -382,7 +384,7 @@ HandleCrAccess (
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to EPT violation.
|
@brief Handles VM-exit due to EPT violation.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -408,7 +410,7 @@ HandleEptViolation (
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to EPT misconfiguration.
|
@brief Handles VM-exit due to EPT misconfiguration.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -444,7 +446,12 @@ HandleEptMisconfig (
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to interrupt or exception.
|
@brief Handles VM-exit due to interrupt or exception.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@details Currently, this handler is specialized for skipping main initialization
|
||||||
|
of PatchGuard for the demo purpose. When #DE occurs with the guest state
|
||||||
|
that seems to be the trigger of PatchGuard initialization, suppress it.
|
||||||
|
Otherwise, just inject the exception (pass-through).
|
||||||
|
|
||||||
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -455,11 +462,6 @@ HandleExceptionOrNmi (
|
|||||||
static BOOLEAN isKeInitAmd64SpecificStateCalled;
|
static BOOLEAN isKeInitAmd64SpecificStateCalled;
|
||||||
VMEXIT_INTERRUPT_INFORMATION interruptInfo;
|
VMEXIT_INTERRUPT_INFORMATION interruptInfo;
|
||||||
|
|
||||||
//
|
|
||||||
// This handler is specialized for skipping main initialization of PatchGuard.
|
|
||||||
// When #DE occurs with the guest state that seems to be during initialization
|
|
||||||
// of PatchGuard, suppress it. Otherwise, just inject the exception (pass-through).
|
|
||||||
//
|
|
||||||
interruptInfo.Flags = (UINT32)VmxRead(VMCS_VMEXIT_INTERRUPTION_INFORMATION);
|
interruptInfo.Flags = (UINT32)VmxRead(VMCS_VMEXIT_INTERRUPTION_INFORMATION);
|
||||||
MV_ASSERT(interruptInfo.InterruptionType == HardwareException);
|
MV_ASSERT(interruptInfo.InterruptionType == HardwareException);
|
||||||
MV_ASSERT(interruptInfo.Vector == DivideError);
|
MV_ASSERT(interruptInfo.Vector == DivideError);
|
||||||
@@ -513,7 +515,7 @@ Exit:
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to the INIT signal.
|
@brief Handles VM-exit due to the INIT signal.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -673,7 +675,7 @@ HandleInitSignal (
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles VM-exit due to the Startup-IPI (SIPI) signal.
|
@brief Handles VM-exit due to the Startup-IPI (SIPI) signal.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
@@ -738,12 +740,12 @@ HandleStartupIpi (
|
|||||||
Any hypervisor code including this and the AsmHypervisorEntryPoint
|
Any hypervisor code including this and the AsmHypervisorEntryPoint
|
||||||
functions are executed while interrupt is disabled via RFLAGS.IF being
|
functions are executed while interrupt is disabled via RFLAGS.IF being
|
||||||
0 (See: 27.5.3 Loading Host RIP, RSP, and RFLAGS). This means IPI, if
|
0 (See: 27.5.3 Loading Host RIP, RSP, and RFLAGS). This means IPI, if
|
||||||
requested, is never delivered and causes deadlock. This condition is
|
requested, is never delivered and causes deadlock. In the Windows
|
||||||
essentially equal to IRQL being HIGH_LEVEL (i.e., at a higher IRQL than
|
terminology, this condition is essentially equal to IRQL being HIGH_LEVEL
|
||||||
IPI_LEVEL), and so, it is unsafe to call any Windows provided API that
|
(i.e., at a higher IRQL than IPI_LEVEL), and so, it is unsafe to call any
|
||||||
is not stated as callable at HIGH_LEVEL.
|
Windows provided API that is not stated as callable at HIGH_LEVEL.
|
||||||
|
|
||||||
@param[in,out] Stack - A pointer to the hypervisor stack containing the
|
@param[in,out] Stack - The pointer to the hypervisor stack containing the
|
||||||
guest register values.
|
guest register values.
|
||||||
|
|
||||||
@return TRUE when virtualization should continue and the VMRESUME instruction
|
@return TRUE when virtualization should continue and the VMRESUME instruction
|
||||||
@@ -785,6 +787,8 @@ HandleVmExit (
|
|||||||
guestContext.VmcsBasedRegisters.Rsp = VmxRead(VMCS_GUEST_RSP);
|
guestContext.VmcsBasedRegisters.Rsp = VmxRead(VMCS_GUEST_RSP);
|
||||||
guestContext.VmcsBasedRegisters.Rip = VmxRead(VMCS_GUEST_RIP);
|
guestContext.VmcsBasedRegisters.Rip = VmxRead(VMCS_GUEST_RIP);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Windows-specific:
|
||||||
//
|
//
|
||||||
// Update the _KTRAP_FRAME structure values in hypervisor stack, so that
|
// Update the _KTRAP_FRAME structure values in hypervisor stack, so that
|
||||||
// Windbg can reconstruct call stack of the guest during debug session.
|
// Windbg can reconstruct call stack of the guest during debug session.
|
||||||
@@ -903,7 +907,7 @@ typedef struct _EXCEPTION_STACK
|
|||||||
|
|
||||||
@details On Windows, this function is unused because the host uses the same
|
@details On Windows, this function is unused because the host uses the same
|
||||||
IDT as that of the guest. All interrupts and exceptions are handled by
|
IDT as that of the guest. All interrupts and exceptions are handled by
|
||||||
the NT kernel.
|
the NT kernel allowing Windbg to work as usual.
|
||||||
|
|
||||||
@param[in] Stack - The pointer to the hypervisor stack containing the
|
@param[in] Stack - The pointer to the hypervisor stack containing the
|
||||||
guest register values.
|
guest register values.
|
||||||
|
|||||||
@@ -308,7 +308,6 @@ AdvanceGuestInstructionPointer (
|
|||||||
_Use_decl_annotations_
|
_Use_decl_annotations_
|
||||||
BOOLEAN
|
BOOLEAN
|
||||||
IsGuestInKernelMode (
|
IsGuestInKernelMode (
|
||||||
VOID
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
VMX_SEGMENT_ACCESS_RIGHTS accessRight;
|
VMX_SEGMENT_ACCESS_RIGHTS accessRight;
|
||||||
|
|||||||
@@ -107,9 +107,9 @@ DumpControl (
|
|||||||
/*!
|
/*!
|
||||||
@brief Writes the value to the VMCS.
|
@brief Writes the value to the VMCS.
|
||||||
|
|
||||||
@param[in] Field - A VMCS field to write the value to.
|
@param[in] Field - The VMCS field to write the value to.
|
||||||
|
|
||||||
@param[in] FieldValue - A value to write.
|
@param[in] FieldValue - The value to write.
|
||||||
*/
|
*/
|
||||||
VOID
|
VOID
|
||||||
VmxWrite (
|
VmxWrite (
|
||||||
@@ -120,7 +120,7 @@ VmxWrite (
|
|||||||
/*!
|
/*!
|
||||||
@brief Read a value from the VMCS.
|
@brief Read a value from the VMCS.
|
||||||
|
|
||||||
@param[in] Field - A VMCS field to read a value from.
|
@param[in] Field - The VMCS field to read a value from.
|
||||||
|
|
||||||
@return A value read from the VMCS. 0 is returned when a non-existent VMCS
|
@return A value read from the VMCS. 0 is returned when a non-existent VMCS
|
||||||
field is requested for read.
|
field is requested for read.
|
||||||
@@ -134,7 +134,7 @@ VmxRead (
|
|||||||
@brief Advances the guest's RIP to the address of the next instruction. This
|
@brief Advances the guest's RIP to the address of the next instruction. This
|
||||||
implies that the hypervisor completed emulation of the instruction.
|
implies that the hypervisor completed emulation of the instruction.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
VOID
|
VOID
|
||||||
AdvanceGuestInstructionPointer (
|
AdvanceGuestInstructionPointer (
|
||||||
@@ -150,7 +150,6 @@ AdvanceGuestInstructionPointer (
|
|||||||
_Must_inspect_result_
|
_Must_inspect_result_
|
||||||
BOOLEAN
|
BOOLEAN
|
||||||
IsGuestInKernelMode (
|
IsGuestInKernelMode (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -160,16 +159,16 @@ IsGuestInKernelMode (
|
|||||||
corresponding exception handler before executing the instruction pointed
|
corresponding exception handler before executing the instruction pointed
|
||||||
by Rip.
|
by Rip.
|
||||||
|
|
||||||
@param[in] InterruptionType - A type of interrupt to inject.
|
@param[in] InterruptionType - The type of interrupt to inject.
|
||||||
|
|
||||||
@param[in] Vector - A vector number of interrupt to inject.
|
@param[in] Vector - The vector number of interrupt to inject.
|
||||||
|
|
||||||
@param[in] DeliverErrorCode - TRUE when the interrupt should have an error
|
@param[in] DeliverErrorCode - TRUE when the interrupt should have an error
|
||||||
code. Whether the interrupt should have an error code is defined by the
|
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
|
Intel SDM. See comments in the EXCEPTION_VECTOR definitions for a quick
|
||||||
reference.
|
reference.
|
||||||
|
|
||||||
@param[in] ErrorCode - An error code. Not used when DeliverErrorCode is FALSE.
|
@param[in] ErrorCode - The error code. Not used when DeliverErrorCode is FALSE.
|
||||||
*/
|
*/
|
||||||
VOID
|
VOID
|
||||||
InjectInterruption (
|
InjectInterruption (
|
||||||
@@ -217,7 +216,7 @@ AdjustGuestCr4 (
|
|||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Find the base address of the image to which the specified address belongs.
|
@brief Finds the base address of the image to which the specified address belongs.
|
||||||
|
|
||||||
@param[in] GuestContext - The pointer to the guest context.
|
@param[in] GuestContext - The pointer to the guest context.
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ HandleVmcallUninstall (
|
|||||||
// from the guest state fields of the VMCS. However, it is not the case when
|
// 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
|
// the VMRESUME is not called, like here. In such a case those values must
|
||||||
// be restored with normal value manually, or PatchGuard will report
|
// be restored with normal value manually, or PatchGuard will report
|
||||||
// integrity violation.
|
// integrity violation on Windows.
|
||||||
//
|
//
|
||||||
// "The GDTR and IDTR limits are each set to FFFFH."
|
// "The GDTR and IDTR limits are each set to FFFFH."
|
||||||
// See: 27.5.2 Loading Host Segment and Descriptor-Table Registers
|
// See: 27.5.2 Loading Host Segment and Descriptor-Table Registers
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ VMCALL_HANDLER (
|
|||||||
/*!
|
/*!
|
||||||
@brief Handles hypercall for uninstalling the hypervisor.
|
@brief Handles hypercall for uninstalling the hypervisor.
|
||||||
|
|
||||||
@param[in,out] GuestContext - A pointer to the guest context.
|
@param[in,out] GuestContext - The pointer to the guest context.
|
||||||
*/
|
*/
|
||||||
VMCALL_HANDLER HandleVmcallUninstall;
|
VMCALL_HANDLER HandleVmcallUninstall;
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
// difference of the formatting functions. Use them like the standard's PRIx macro
|
// difference of the formatting functions. Use them like the standard's PRIx macro
|
||||||
// family.
|
// family.
|
||||||
//
|
//
|
||||||
#if defined(NTDDI_VERSION)
|
#if defined(MV_PLATFORM_WINDOWS)
|
||||||
#define LOG_PRIANSI "s"
|
#define LOG_PRIANSI "s"
|
||||||
#define LOG_PRIUNICODE "S"
|
#define LOG_PRIUNICODE "S"
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Split a 2MB EPT PDE to 512 EPT PTEs.
|
@brief Splits a 2MB PDE to 512 PTEs.
|
||||||
|
|
||||||
@param[in,out] PdeLarge - The pointer to the 2MB EPT PDE to split.
|
@param[in,out] PdeLarge - The pointer to the 2MB PDE to split.
|
||||||
|
|
||||||
@return MV_STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
@return MV_STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
||||||
*/
|
*/
|
||||||
@@ -499,11 +499,14 @@ GetPhysicalAddressForGuest (
|
|||||||
|
|
||||||
Exit:
|
Exit:
|
||||||
//
|
//
|
||||||
// Return the collected permission bits on success.
|
// Return the collected permission bits if provided.
|
||||||
//
|
//
|
||||||
if ((pa != MV_INVALID_PHYSICAL_ADDRESS) &&
|
if (ARGUMENT_PRESENT(AggregatedPagePermissions))
|
||||||
ARGUMENT_PRESENT(AggregatedPagePermissions))
|
|
||||||
{
|
{
|
||||||
|
if (pa == MV_INVALID_PHYSICAL_ADDRESS)
|
||||||
|
{
|
||||||
|
permission.Flags = 0;
|
||||||
|
}
|
||||||
*AggregatedPagePermissions = permission;
|
*AggregatedPagePermissions = permission;
|
||||||
}
|
}
|
||||||
return pa;
|
return pa;
|
||||||
|
|||||||
@@ -85,9 +85,9 @@ CleanupMemoryAccess (
|
|||||||
given virtual address and retrieves the guest physical address of it.
|
given virtual address and retrieves the guest physical address of it.
|
||||||
This is equivalent to changing the current CR3 with the guest CR3 and
|
This is equivalent to changing the current CR3 with the guest CR3 and
|
||||||
calling GetPhysicalAddress(). This function, however, exists to avoid
|
calling GetPhysicalAddress(). This function, however, exists to avoid
|
||||||
problems associated with CR3 update, for example, updating the CR3 crashes
|
problems associated with CR3 update, for example, on Windows, updating
|
||||||
the system immediately if the KVA Shadow is enabled and the guest CR3
|
the CR3 crashes the system immediately if the KVA Shadow is enabled and
|
||||||
contains the USER CR3, as it does not map our driver.
|
the guest CR3 contains the USER CR3, as it does not map our code.
|
||||||
|
|
||||||
@param[in] Context - The pointer to the memory access context.
|
@param[in] Context - The pointer to the memory access context.
|
||||||
|
|
||||||
@@ -174,7 +174,8 @@ WriteGuestVirtualAddress (
|
|||||||
|
|
||||||
@param[in] Context - The pointer to the memory access context.
|
@param[in] Context - The pointer to the memory access context.
|
||||||
|
|
||||||
@param[in] GuestPageNumber - See ReadOrWriteGuestVirtualAddress().
|
@param[in] GuestPageNumber - The guest page number (ie, the guest virtual
|
||||||
|
address without lower 12bits) to map to the host address space.
|
||||||
|
|
||||||
@return The virtual address mapping the same physical page as specified as
|
@return The virtual address mapping the same physical page as specified as
|
||||||
the page number, or NULL if the specified page number does not have
|
the page number, or NULL if the specified page number does not have
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include "MemoryManager.h"
|
#include "MemoryManager.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#if !defined(NTDDI_VERSION)
|
#if defined(MV_PLATFORM_EFI)
|
||||||
#include "Platform/EFI/EfiBitmap.h"
|
#include "Platform/EFI/EfiBitmap.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ MmInitializeMemoryManager (
|
|||||||
UINT32 lengthMapPagesCount;
|
UINT32 lengthMapPagesCount;
|
||||||
MEMORY_MANAGER_CONTEXT* memoryManager;
|
MEMORY_MANAGER_CONTEXT* memoryManager;
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
memoryManager = &g_MemoryManager;
|
memoryManager = &g_MemoryManager;
|
||||||
lengthMapPagesCount = 0;
|
lengthMapPagesCount = 0;
|
||||||
@@ -174,7 +174,7 @@ MmCleanupMemoryManager (
|
|||||||
UINT32 lengthMapPagesCount;
|
UINT32 lengthMapPagesCount;
|
||||||
MEMORY_MANAGER_CONTEXT* memoryManager;
|
MEMORY_MANAGER_CONTEXT* memoryManager;
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
memoryManager = &g_MemoryManager;
|
memoryManager = &g_MemoryManager;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
@brief Functions for memory management.
|
@brief Functions for memory management.
|
||||||
|
|
||||||
|
@details All API in this file are prefixed with Mm because naive names
|
||||||
|
conflict with platform API, for example, AllocatePages and FreePages in
|
||||||
|
EDK2.
|
||||||
|
|
||||||
@author Satoshi Tanda
|
@author Satoshi Tanda
|
||||||
|
|
||||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Ia32.h"
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Initializes the MTRR context.
|
@brief Initializes the MTRR context.
|
||||||
@@ -21,12 +20,12 @@ InitializeMemoryTypeMapping (
|
|||||||
/*!
|
/*!
|
||||||
@brief Returns a memory type for the given physical address range.
|
@brief Returns a memory type for the given physical address range.
|
||||||
|
|
||||||
@param[in] PhysicalAddress - A physical address to retrieve its memory type.
|
@param[in] PhysicalAddress - The physical address to retrieve its memory type.
|
||||||
|
|
||||||
@param[in] RangeSize - The size of the range to check.
|
@param[in] RangeSize - The size of the range to check.
|
||||||
|
|
||||||
@return The memory type for the given physical address. If the range contains
|
@return The memory type for the given physical address. If the range contains
|
||||||
more than one memory type, return MEMORY_TYPE_INVALID.
|
more than one memory type, MEMORY_TYPE_INVALID.
|
||||||
*/
|
*/
|
||||||
IA32_MEMORY_TYPE
|
IA32_MEMORY_TYPE
|
||||||
GetMemoryTypeForRange (
|
GetMemoryTypeForRange (
|
||||||
|
|||||||
@@ -164,10 +164,10 @@ typedef struct _SHARED_PROCESSOR_CONTEXT
|
|||||||
@brief Returns the VM control value that is adjusted in consideration with
|
@brief Returns the VM control value that is adjusted in consideration with
|
||||||
the VMX capability MSR.
|
the VMX capability MSR.
|
||||||
|
|
||||||
@param[in] VmxCapabilityMsr - A VMX capability MSR to consult to adjust the
|
@param[in] VmxCapabilityMsr - The VMX capability MSR to consult to adjust the
|
||||||
RequestedValue,
|
RequestedValue,
|
||||||
|
|
||||||
@param[in] RequestedValue - A VM control value that needs adjustment.
|
@param[in] RequestedValue - The VM control value that needs adjustment.
|
||||||
|
|
||||||
@return The adjusted control value.
|
@return The adjusted control value.
|
||||||
*/
|
*/
|
||||||
@@ -220,7 +220,7 @@ AdjustControlValue (
|
|||||||
@brief Adjusts a pin-based control value in consideration with the VMX
|
@brief Adjusts a pin-based control value in consideration with the VMX
|
||||||
capability MSR.
|
capability MSR.
|
||||||
|
|
||||||
@param[in,out] PinBasedControls - A pointer to the pin-based control value
|
@param[in,out] PinBasedControls - The pointer to the pin-based control value
|
||||||
to adjust.
|
to adjust.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
@@ -233,9 +233,9 @@ AdjustPinBasedControls (
|
|||||||
IA32_VMX_BASIC_REGISTER vmxBasicMsr;
|
IA32_VMX_BASIC_REGISTER vmxBasicMsr;
|
||||||
|
|
||||||
//
|
//
|
||||||
// This determine the right VMX capability MSR based on the value of
|
// This determines the right VMX capability MSR based on the value of
|
||||||
// IA32_VMX_BASIC. With the right VMX capability MSR, the
|
// IA32_VMX_BASIC. With the right VMX capability MSR, the
|
||||||
// AdjustControlValue function implements the logic described under
|
// AdjustControlValue function implements the logic described as below.
|
||||||
// "It is necessary for software to consult only one of the capability MSRs
|
// "It is necessary for software to consult only one of the capability MSRs
|
||||||
// to determine the allowed settings of the pin based VM-execution controls:"
|
// to determine the allowed settings of the pin based VM-execution controls:"
|
||||||
// See: A.3.1 Pin-Based VM-Execution Controls
|
// See: A.3.1 Pin-Based VM-Execution Controls
|
||||||
@@ -252,7 +252,7 @@ AdjustPinBasedControls (
|
|||||||
@brief Adjusts a processor-based control value in consideration with the VMX
|
@brief Adjusts a processor-based control value in consideration with the VMX
|
||||||
capability MSR.
|
capability MSR.
|
||||||
|
|
||||||
@param[in,out] PrimaryProcBasedControls - A pointer to the processor-based
|
@param[in,out] PrimaryProcBasedControls - The pointer to the processor-based
|
||||||
control value to adjust.
|
control value to adjust.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
@@ -280,7 +280,7 @@ AdjustProcessorBasedControls (
|
|||||||
@brief Adjusts a VM-exit control value in consideration with the VMX
|
@brief Adjusts a VM-exit control value in consideration with the VMX
|
||||||
capability MSR.
|
capability MSR.
|
||||||
|
|
||||||
@param[in,out] VmExitControls - A pointer to the VM-exit control value to
|
@param[in,out] VmExitControls - The pointer to the VM-exit control value to
|
||||||
adjust.
|
adjust.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
@@ -307,7 +307,7 @@ AdjustVmExitControls (
|
|||||||
@brief Adjusts a VM-entry control value in consideration with the VMX
|
@brief Adjusts a VM-entry control value in consideration with the VMX
|
||||||
capability MSR.
|
capability MSR.
|
||||||
|
|
||||||
@param[in,out] VmEntryControls - A pointer to the VM-entry control value to
|
@param[in,out] VmEntryControls - The pointer to the VM-entry control value to
|
||||||
adjust.
|
adjust.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
@@ -334,7 +334,7 @@ AdjustVmEntryControls (
|
|||||||
@brief Adjusts a secondary processor-based control value in consideration
|
@brief Adjusts a secondary processor-based control value in consideration
|
||||||
with the VMX capability MSR.
|
with the VMX capability MSR.
|
||||||
|
|
||||||
@param[in,out] SecondaryProcBasedControls - A pointer to the secondary
|
@param[in,out] SecondaryProcBasedControls - The pointer to the secondary
|
||||||
processor-based control value to adjust.
|
processor-based control value to adjust.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
@@ -361,7 +361,6 @@ static
|
|||||||
_Must_inspect_result_
|
_Must_inspect_result_
|
||||||
BOOLEAN
|
BOOLEAN
|
||||||
IsMiniVisorInstalled (
|
IsMiniVisorInstalled (
|
||||||
VOID
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int registers[4]; // EAX, EBX, ECX, and EDX
|
int registers[4]; // EAX, EBX, ECX, and EDX
|
||||||
@@ -381,11 +380,12 @@ IsMiniVisorInstalled (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Disables hypervisor on the current processor.
|
@brief Disables the hypervisor on the current processor.
|
||||||
|
|
||||||
@param[in,out] Context - A pointer to the location to receive the address of
|
@param[in,out] Context - The pointer to the location to receive the address of
|
||||||
the shared processor context.
|
the shared processor context.
|
||||||
*/
|
*/
|
||||||
|
MV_SECTION_PAGED
|
||||||
static
|
static
|
||||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||||
VOID
|
VOID
|
||||||
@@ -397,6 +397,8 @@ DisableHypervisor (
|
|||||||
SHARED_PROCESSOR_CONTEXT** sharedProcessorContextsAddress;
|
SHARED_PROCESSOR_CONTEXT** sharedProcessorContextsAddress;
|
||||||
PER_PROCESSOR_CONTEXT* processorContext;
|
PER_PROCESSOR_CONTEXT* processorContext;
|
||||||
|
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
if (IsMiniVisorInstalled() == FALSE)
|
if (IsMiniVisorInstalled() == FALSE)
|
||||||
{
|
{
|
||||||
goto Exit;
|
goto Exit;
|
||||||
@@ -404,8 +406,7 @@ DisableHypervisor (
|
|||||||
|
|
||||||
//
|
//
|
||||||
// Issues the hypercall to uninstall the hypervisor. This hypercall returns
|
// Issues the hypercall to uninstall the hypervisor. This hypercall returns
|
||||||
// the address of the shared processor context on success. If the hypervisor
|
// the address of the shared processor context on success.
|
||||||
// is not installed, this (VMCALL instruction) raises #UD.
|
|
||||||
//
|
//
|
||||||
returnedAddress = (SHARED_PROCESSOR_CONTEXT*)AsmVmxCall(VmcallUninstall, 0, 0, 0);
|
returnedAddress = (SHARED_PROCESSOR_CONTEXT*)AsmVmxCall(VmcallUninstall, 0, 0, 0);
|
||||||
MV_ASSERT(returnedAddress != NULL);
|
MV_ASSERT(returnedAddress != NULL);
|
||||||
@@ -423,7 +424,7 @@ DisableHypervisor (
|
|||||||
*sharedProcessorContextsAddress = returnedAddress;
|
*sharedProcessorContextsAddress = returnedAddress;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Clean up the per-processor stuff, to be precise, EPT setup.
|
// Clean up the per-processor data structures.
|
||||||
//
|
//
|
||||||
processorContext = &(*sharedProcessorContextsAddress)->Contexts[GetCurrentProcessorNumber()];
|
processorContext = &(*sharedProcessorContextsAddress)->Contexts[GetCurrentProcessorNumber()];
|
||||||
CleanupExtendedPageTables(&processorContext->EptContext);
|
CleanupExtendedPageTables(&processorContext->EptContext);
|
||||||
@@ -435,19 +436,18 @@ Exit:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Enables hypervisor on the all processors.
|
@brief Disables the hypervisor on the all processors.
|
||||||
*/
|
*/
|
||||||
MV_SECTION_PAGED
|
MV_SECTION_PAGED
|
||||||
static
|
static
|
||||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||||
VOID
|
VOID
|
||||||
DisableHypervisorOnAllProcessors (
|
DisableHypervisorOnAllProcessors (
|
||||||
VOID
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
SHARED_PROCESSOR_CONTEXT* sharedProcessorContext;
|
SHARED_PROCESSOR_CONTEXT* sharedProcessorContext;
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
sharedProcessorContext = NULL;
|
sharedProcessorContext = NULL;
|
||||||
RunOnAllProcessors(DisableHypervisor, &sharedProcessorContext);
|
RunOnAllProcessors(DisableHypervisor, &sharedProcessorContext);
|
||||||
@@ -467,7 +467,6 @@ static
|
|||||||
_Must_inspect_result_
|
_Must_inspect_result_
|
||||||
BOOLEAN
|
BOOLEAN
|
||||||
IsVmxAvailable (
|
IsVmxAvailable (
|
||||||
VOID
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
BOOLEAN vmxAvailable;
|
BOOLEAN vmxAvailable;
|
||||||
@@ -494,7 +493,7 @@ IsVmxAvailable (
|
|||||||
//
|
//
|
||||||
// Check the processor support the write-back type for VMCS. We do not
|
// Check the processor support the write-back type for VMCS. We do not
|
||||||
// support the processor that does not support the write-back type for
|
// support the processor that does not support the write-back type for
|
||||||
// simplicity. It is not practically an issue since
|
// simplicity. It is practically not an issue since
|
||||||
// "As of this writing, all processors that support VMX operation indicate
|
// "As of this writing, all processors that support VMX operation indicate
|
||||||
// the write-back type."
|
// the write-back type."
|
||||||
//
|
//
|
||||||
@@ -529,19 +528,17 @@ IsVmxAvailable (
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Check the followings to confirm availability of EPT related capabilities:
|
// Check the followings to confirm availability of EPT related capabilities.
|
||||||
// - page walk length is 4 steps
|
|
||||||
// - extended page tables can be laid out in write-back memory
|
|
||||||
// - INVEPT instruction for global context is supported
|
|
||||||
// - INVVPID instruction for global context is supported
|
|
||||||
//
|
//
|
||||||
eptVpidCapabilityMsr.Flags = __readmsr(IA32_VMX_EPT_VPID_CAP);
|
eptVpidCapabilityMsr.Flags = __readmsr(IA32_VMX_EPT_VPID_CAP);
|
||||||
if ((eptVpidCapabilityMsr.PageWalkLength4 == FALSE) ||
|
if ((eptVpidCapabilityMsr.PageWalkLength4 == FALSE) ||
|
||||||
(eptVpidCapabilityMsr.MemoryTypeWriteBack == FALSE) ||
|
(eptVpidCapabilityMsr.MemoryTypeWriteBack == FALSE) ||
|
||||||
(eptVpidCapabilityMsr.Pde2MbPages == FALSE) ||
|
(eptVpidCapabilityMsr.Pde2MbPages == FALSE) ||
|
||||||
(eptVpidCapabilityMsr.Invept == FALSE) ||
|
(eptVpidCapabilityMsr.Invept == FALSE) ||
|
||||||
|
(eptVpidCapabilityMsr.InveptSingleContext == FALSE) ||
|
||||||
(eptVpidCapabilityMsr.InveptAllContexts == FALSE) ||
|
(eptVpidCapabilityMsr.InveptAllContexts == FALSE) ||
|
||||||
(eptVpidCapabilityMsr.Invvpid == FALSE) ||
|
(eptVpidCapabilityMsr.Invvpid == FALSE) ||
|
||||||
|
(eptVpidCapabilityMsr.InvvpidSingleContext == FALSE) ||
|
||||||
(eptVpidCapabilityMsr.InvvpidAllContexts == FALSE))
|
(eptVpidCapabilityMsr.InvvpidAllContexts == FALSE))
|
||||||
{
|
{
|
||||||
LOG_ERROR("EPT is not supported.");
|
LOG_ERROR("EPT is not supported.");
|
||||||
@@ -562,10 +559,10 @@ Exit:
|
|||||||
See: 31.5 VMM SETUP & TEAR DOWN
|
See: 31.5 VMM SETUP & TEAR DOWN
|
||||||
See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE
|
See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE
|
||||||
|
|
||||||
@param[in] SharedProcessorContext - A pointer to the shared processor context.
|
@param[in] SharedProcessorContext - The pointer to the shared processor context.
|
||||||
|
|
||||||
@param[in,out] ProcessorContext - A pointer to the per-processor context for this
|
@param[in,out] ProcessorContext - The pointer to the per-processor context
|
||||||
processor.
|
for this processor.
|
||||||
|
|
||||||
@return MV_STATUS_SUCCESS on success, or an appropriate status code on error.
|
@return MV_STATUS_SUCCESS on success, or an appropriate status code on error.
|
||||||
*/
|
*/
|
||||||
@@ -618,7 +615,7 @@ SetupVmcs (
|
|||||||
|
|
||||||
//
|
//
|
||||||
// VMX requires TR to be configured properly (ie, non zero). This requirement
|
// VMX requires TR to be configured properly (ie, non zero). This requirement
|
||||||
// is not already satisfied in case of EFI environment. Set it up by updating
|
// is not satisfied yet in case of EFI environment. Set it up by updating
|
||||||
// GDT as necessary.
|
// GDT as necessary.
|
||||||
//
|
//
|
||||||
// "The selector fields for CS and TR cannot be 0000H."
|
// "The selector fields for CS and TR cannot be 0000H."
|
||||||
@@ -769,8 +766,7 @@ SetupVmcs (
|
|||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// Initialize EPT specific data structure, which we will referred to as the
|
// Initialize EPT specific data structures.
|
||||||
// EPT-context.
|
|
||||||
//
|
//
|
||||||
status = InitializeExtendedPageTables(&ProcessorContext->EptContext);
|
status = InitializeExtendedPageTables(&ProcessorContext->EptContext);
|
||||||
if (MV_ERROR(status))
|
if (MV_ERROR(status))
|
||||||
@@ -829,20 +825,20 @@ SetupVmcs (
|
|||||||
// instructions.
|
// instructions.
|
||||||
//
|
//
|
||||||
// - MSR bitmaps are used; this is not to cause VM-exit as much as possible.
|
// - MSR bitmaps are used; this is not to cause VM-exit as much as possible.
|
||||||
// We are setting the MSR bitmaps that are all cleared below (see code
|
// We are setting the MSR bitmaps that are mostly cleared below (see
|
||||||
// around the "64-Bit Control Fields" comment). This prevents VM-exits from
|
// InitializeMsrBitmaps). This prevents VM-exits from occurring when
|
||||||
// occurring when 0x0 - 0x1fff and 0xc0000000 - 0xc0001fff are accessed.
|
// 0x0 - 0x1fff and 0xc0000000 - 0xc0001fff are accessed. VM-exit still
|
||||||
// Note that VM-exit still occurs if outside the range is accessed, and it
|
// occurs if outside the range is accessed, and it is not possible to
|
||||||
// is not possible to prevent this.
|
// prevent this.
|
||||||
//
|
//
|
||||||
// - The secondary processor-based controls are used; this is to let the
|
// - The secondary processor-based controls are used; this is to let the
|
||||||
// guest (Windows) executes RDTSCP, INVPCID and the XSAVE/XRSTORS family
|
// guest (Windows) executes RDTSCP, INVPCID and the XSAVE/XRSTORS family
|
||||||
// instructions. Those instructions are used in Windows 10. If those are
|
// instructions. Those instructions are used in Windows 10. If those are
|
||||||
// not set, attempt to execute them causes #UD, which results in a bug
|
// not set, attempt to execute them causes #UD, which results in a bug
|
||||||
// check. VPID is enabled, which could lead to better performance for free
|
// check. VPID is enabled, which could lead to better performance for free
|
||||||
// by not flushing all TLB on every VM-exit. Finally, to enable EPT and
|
// by not flushing all TLB on every VM-exit and VM-entry. Finally, enabling
|
||||||
// unrestricted guest which are required for the UEFI hypervisor to handle
|
// EPT and unrestricted guest which are required for the UEFI hypervisor to
|
||||||
// the real-mode guest.
|
// handle the real-mode guest.
|
||||||
//
|
//
|
||||||
primaryProcBasedControls.Flags = 0;
|
primaryProcBasedControls.Flags = 0;
|
||||||
primaryProcBasedControls.UseMsrBitmaps = TRUE;
|
primaryProcBasedControls.UseMsrBitmaps = TRUE;
|
||||||
@@ -870,13 +866,20 @@ SetupVmcs (
|
|||||||
// RPL (bits 1:0) and the TI flag (bit 2) must be 0"
|
// RPL (bits 1:0) and the TI flag (bit 2) must be 0"
|
||||||
// See: 26.2.3 Checks on Host Segment and Descriptor-Table Registers
|
// See: 26.2.3 Checks on Host Segment and Descriptor-Table Registers
|
||||||
//
|
//
|
||||||
VmxWrite(VMCS_HOST_ES_SELECTOR, AsmReadEs() & ~hostSegmentSelectorMask);
|
MV_ASSERT(FlagOn(AsmReadEs(), hostSegmentSelectorMask) == FALSE);
|
||||||
VmxWrite(VMCS_HOST_CS_SELECTOR, AsmReadCs() & ~hostSegmentSelectorMask);
|
MV_ASSERT(FlagOn(AsmReadCs(), hostSegmentSelectorMask) == FALSE);
|
||||||
VmxWrite(VMCS_HOST_SS_SELECTOR, AsmReadSs() & ~hostSegmentSelectorMask);
|
MV_ASSERT(FlagOn(AsmReadSs(), hostSegmentSelectorMask) == FALSE);
|
||||||
VmxWrite(VMCS_HOST_DS_SELECTOR, AsmReadDs() & ~hostSegmentSelectorMask);
|
MV_ASSERT(FlagOn(AsmReadDs(), hostSegmentSelectorMask) == FALSE);
|
||||||
VmxWrite(VMCS_HOST_FS_SELECTOR, AsmReadFs() & ~hostSegmentSelectorMask);
|
MV_ASSERT(FlagOn(AsmReadFs(), hostSegmentSelectorMask) == FALSE);
|
||||||
VmxWrite(VMCS_HOST_GS_SELECTOR, AsmReadGs() & ~hostSegmentSelectorMask);
|
MV_ASSERT(FlagOn(AsmReadGs(), hostSegmentSelectorMask) == FALSE);
|
||||||
VmxWrite(VMCS_HOST_TR_SELECTOR, AsmReadTr() & ~hostSegmentSelectorMask);
|
MV_ASSERT(FlagOn(AsmReadTr(), hostSegmentSelectorMask) == FALSE);
|
||||||
|
VmxWrite(VMCS_HOST_ES_SELECTOR, AsmReadEs());
|
||||||
|
VmxWrite(VMCS_HOST_CS_SELECTOR, AsmReadCs());
|
||||||
|
VmxWrite(VMCS_HOST_SS_SELECTOR, AsmReadSs());
|
||||||
|
VmxWrite(VMCS_HOST_DS_SELECTOR, AsmReadDs());
|
||||||
|
VmxWrite(VMCS_HOST_FS_SELECTOR, AsmReadFs());
|
||||||
|
VmxWrite(VMCS_HOST_GS_SELECTOR, AsmReadGs());
|
||||||
|
VmxWrite(VMCS_HOST_TR_SELECTOR, AsmReadTr());
|
||||||
|
|
||||||
/* 64-Bit Host-State Fields */
|
/* 64-Bit Host-State Fields */
|
||||||
VmxWrite(VMCS_HOST_EFER, __readmsr(IA32_EFER));
|
VmxWrite(VMCS_HOST_EFER, __readmsr(IA32_EFER));
|
||||||
@@ -1021,8 +1024,9 @@ Exit:
|
|||||||
/*!
|
/*!
|
||||||
@brief Enables hypervisor on the current processor.
|
@brief Enables hypervisor on the current processor.
|
||||||
|
|
||||||
@param[in,out] Context - A pointer to the shared processor context.
|
@param[in,out] Context - The pointer to the shared processor context.
|
||||||
*/
|
*/
|
||||||
|
MV_SECTION_PAGED
|
||||||
static
|
static
|
||||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||||
VOID
|
VOID
|
||||||
@@ -1037,6 +1041,8 @@ EnableHypervisor (
|
|||||||
UINT64 guestRip;
|
UINT64 guestRip;
|
||||||
BOOLEAN maCtxInitialized;
|
BOOLEAN maCtxInitialized;
|
||||||
|
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
maCtxInitialized = FALSE;
|
maCtxInitialized = FALSE;
|
||||||
|
|
||||||
sharedProcessorContext = Context;
|
sharedProcessorContext = Context;
|
||||||
@@ -1049,7 +1055,7 @@ EnableHypervisor (
|
|||||||
// host.
|
// host.
|
||||||
//
|
//
|
||||||
processorContext->Status = InitializeMemoryAccess(&processorContext->MemoryAccessContext,
|
processorContext->Status = InitializeMemoryAccess(&processorContext->MemoryAccessContext,
|
||||||
GetHostCr3());
|
GetHostCr3());
|
||||||
if (MV_ERROR(processorContext->Status))
|
if (MV_ERROR(processorContext->Status))
|
||||||
{
|
{
|
||||||
LOG_ERROR("InitializeMemoryAccess failed : %08x", processorContext->Status);
|
LOG_ERROR("InitializeMemoryAccess failed : %08x", processorContext->Status);
|
||||||
@@ -1140,6 +1146,7 @@ EnableHypervisor (
|
|||||||
vmxErrorStatus = (result == VmxResultErrorWithStatus) ?
|
vmxErrorStatus = (result == VmxResultErrorWithStatus) ?
|
||||||
(VMX_ERROR_NUMBER)VmxRead(VMCS_VM_INSTRUCTION_ERROR) : 0;
|
(VMX_ERROR_NUMBER)VmxRead(VMCS_VM_INSTRUCTION_ERROR) : 0;
|
||||||
LOG_ERROR("__vmx_vmlaunch failed : %u", vmxErrorStatus);
|
LOG_ERROR("__vmx_vmlaunch failed : %u", vmxErrorStatus);
|
||||||
|
|
||||||
processorContext->Status = MV_STATUS_HV_OPERATION_FAILED;
|
processorContext->Status = MV_STATUS_HV_OPERATION_FAILED;
|
||||||
CleanupExtendedPageTables(&processorContext->EptContext);
|
CleanupExtendedPageTables(&processorContext->EptContext);
|
||||||
__vmx_off();
|
__vmx_off();
|
||||||
@@ -1159,8 +1166,8 @@ Exit:
|
|||||||
|
|
||||||
@details This function clears the bitmaps to avoid VM-exits that do not require
|
@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
|
manual handling. The MSR that requires manual handling for MiniVisor is
|
||||||
IA32_BIOS_SIGN_ID to prevent the guest from attempting update BIOS
|
IA32_BIOS_SIGN_ID for read to prevent the guest from attempting update
|
||||||
microcode. See HandleMsrAccess for more details.
|
BIOS microcode which is not allowed. See HandleMsrAccess for more details.
|
||||||
|
|
||||||
@param[out] Bitmaps - The pointer to the MSR bitmaps to initialize.
|
@param[out] Bitmaps - The pointer to the MSR bitmaps to initialize.
|
||||||
*/
|
*/
|
||||||
@@ -1174,7 +1181,7 @@ InitializeMsrBitmaps (
|
|||||||
static CONST UINT64 biosSignatureByteOffset = (IA32_BIOS_SIGN_ID / CHAR_BIT);
|
static CONST UINT64 biosSignatureByteOffset = (IA32_BIOS_SIGN_ID / CHAR_BIT);
|
||||||
static CONST UINT64 biosSignatureBitMask = (1ull << (IA32_BIOS_SIGN_ID % CHAR_BIT));
|
static CONST UINT64 biosSignatureBitMask = (1ull << (IA32_BIOS_SIGN_ID % CHAR_BIT));
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
RtlZeroMemory(Bitmaps, sizeof(*Bitmaps));
|
RtlZeroMemory(Bitmaps, sizeof(*Bitmaps));
|
||||||
|
|
||||||
@@ -1182,7 +1189,7 @@ InitializeMsrBitmaps (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Enables hypervisor on the all processors.
|
@brief Enables the hypervisor on the all processors.
|
||||||
|
|
||||||
@return MV_STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
@return MV_STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
||||||
*/
|
*/
|
||||||
@@ -1192,7 +1199,6 @@ _IRQL_requires_max_(PASSIVE_LEVEL)
|
|||||||
_Must_inspect_result_
|
_Must_inspect_result_
|
||||||
MV_STATUS
|
MV_STATUS
|
||||||
EnableHypervisorOnAllProcessors (
|
EnableHypervisorOnAllProcessors (
|
||||||
VOID
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
MV_STATUS status;
|
MV_STATUS status;
|
||||||
@@ -1201,7 +1207,7 @@ EnableHypervisorOnAllProcessors (
|
|||||||
SHARED_PROCESSOR_CONTEXT* sharedProcessorContext;
|
SHARED_PROCESSOR_CONTEXT* sharedProcessorContext;
|
||||||
BOOLEAN virtualized;
|
BOOLEAN virtualized;
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
virtualized = FALSE;
|
virtualized = FALSE;
|
||||||
|
|
||||||
@@ -1322,7 +1328,7 @@ InitializeMiniVisor (
|
|||||||
//
|
//
|
||||||
if (IsMiniVisorInstalled() != FALSE)
|
if (IsMiniVisorInstalled() != FALSE)
|
||||||
{
|
{
|
||||||
LOG_ERROR("MiniVisor already installed");
|
LOG_INFO("MiniVisor already installed");
|
||||||
status = MV_STATUS_HV_OPERATION_FAILED;
|
status = MV_STATUS_HV_OPERATION_FAILED;
|
||||||
goto Exit;
|
goto Exit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
//
|
//
|
||||||
// Spin lock type and state names.
|
// Spin lock type and state names.
|
||||||
//
|
//
|
||||||
#if defined(NTDDI_VERSION)
|
#if defined(MV_PLATFORM_WINDOWS)
|
||||||
typedef volatile LONG64 SPIN_LOCK;
|
typedef volatile LONG64 SPIN_LOCK;
|
||||||
typedef enum _SPIN_LOCK_STATE
|
typedef enum _SPIN_LOCK_STATE
|
||||||
{
|
{
|
||||||
@@ -81,7 +81,7 @@ GetCurrentProcessorNumber (
|
|||||||
/*!
|
/*!
|
||||||
@brief Returns the physical address of the given virtual address.
|
@brief Returns the physical address of the given virtual address.
|
||||||
|
|
||||||
@param[in] VirualAddress - A virtual address to retrieve its physical
|
@param[in] VirualAddress - The virtual address to retrieve its physical
|
||||||
address for the current CR3. This must be non paged pool, otherwise the
|
address for the current CR3. This must be non paged pool, otherwise the
|
||||||
result is undefined.
|
result is undefined.
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ Index = 0
|
|||||||
; Index is incremented whenever this macro is used.
|
; Index is incremented whenever this macro is used.
|
||||||
;
|
;
|
||||||
INTERRUPT_HANDLER macro InterruptNumber
|
INTERRUPT_HANDLER macro InterruptNumber
|
||||||
push 0 ; Push dummy error code for consistent stack layout.
|
;
|
||||||
|
; Push dummy error code for consistent stack layout.
|
||||||
|
;
|
||||||
|
push 0
|
||||||
push InterruptNumber
|
push InterruptNumber
|
||||||
jmp AsmCommonExceptionHandler
|
jmp AsmCommonExceptionHandler
|
||||||
Index = Index + 1
|
Index = Index + 1
|
||||||
@@ -38,7 +41,10 @@ endm
|
|||||||
; Index is incremented whenever this macro is used.
|
; Index is incremented whenever this macro is used.
|
||||||
;
|
;
|
||||||
INTERRUPT_HANDLER_WITH_CODE macro InterruptNumber
|
INTERRUPT_HANDLER_WITH_CODE macro InterruptNumber
|
||||||
nop ; Error code is expected to be pushed by the processor.
|
;
|
||||||
|
; Error code is expected to be pushed by the processor.
|
||||||
|
;
|
||||||
|
nop
|
||||||
nop
|
nop
|
||||||
push InterruptNumber
|
push InterruptNumber
|
||||||
jmp AsmCommonExceptionHandler
|
jmp AsmCommonExceptionHandler
|
||||||
@@ -53,26 +59,44 @@ endm
|
|||||||
; works as a hendler of the corresponding interrupt/exception in the host.
|
; works as a hendler of the corresponding interrupt/exception in the host.
|
||||||
;
|
;
|
||||||
AsmDefaultExceptionHandlers proc
|
AsmDefaultExceptionHandlers proc
|
||||||
repeat 8
|
;
|
||||||
INTERRUPT_HANDLER Index ; INT0-7
|
; INT0-7
|
||||||
endm
|
;
|
||||||
|
repeat 8
|
||||||
|
INTERRUPT_HANDLER Index
|
||||||
|
endm
|
||||||
|
|
||||||
INTERRUPT_HANDLER_WITH_CODE Index ; INT8
|
;
|
||||||
INTERRUPT_HANDLER Index ; INT9
|
; INT8, INT9
|
||||||
|
;
|
||||||
|
INTERRUPT_HANDLER_WITH_CODE Index
|
||||||
|
INTERRUPT_HANDLER Index
|
||||||
|
|
||||||
repeat 5
|
;
|
||||||
INTERRUPT_HANDLER_WITH_CODE Index ; INT10-14
|
; INT10-14
|
||||||
endm
|
;
|
||||||
|
repeat 5
|
||||||
|
INTERRUPT_HANDLER_WITH_CODE Index
|
||||||
|
endm
|
||||||
|
|
||||||
repeat 2
|
;
|
||||||
INTERRUPT_HANDLER Index ; INT15-16
|
; INT15-16
|
||||||
endm
|
;
|
||||||
|
repeat 2
|
||||||
|
INTERRUPT_HANDLER Index
|
||||||
|
endm
|
||||||
|
|
||||||
INTERRUPT_HANDLER_WITH_CODE Index ; INT17
|
;
|
||||||
|
; INT17
|
||||||
|
;
|
||||||
|
INTERRUPT_HANDLER_WITH_CODE Index
|
||||||
|
|
||||||
repeat 238
|
;
|
||||||
INTERRUPT_HANDLER Index ; INT18-255
|
; INT18-255
|
||||||
endm
|
;
|
||||||
|
repeat 238
|
||||||
|
INTERRUPT_HANDLER Index
|
||||||
|
endm
|
||||||
AsmDefaultExceptionHandlers endp
|
AsmDefaultExceptionHandlers endp
|
||||||
|
|
||||||
;
|
;
|
||||||
@@ -88,7 +112,11 @@ AsmCommonExceptionHandler proc
|
|||||||
call HandleHostException
|
call HandleHostException
|
||||||
add rsp, 20h
|
add rsp, 20h
|
||||||
POPAQ
|
POPAQ
|
||||||
add rsp, 10h ; Remove the error code and interrupt number.
|
|
||||||
|
;
|
||||||
|
; Remove the error code and interrupt number.
|
||||||
|
;
|
||||||
|
add rsp, 10h
|
||||||
iretq
|
iretq
|
||||||
AsmCommonExceptionHandler endp
|
AsmCommonExceptionHandler endp
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
VOID
|
VOID
|
||||||
AsmDefaultExceptionHandlers (
|
AsmDefaultExceptionHandlers (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -23,5 +22,4 @@ AsmDefaultExceptionHandlers (
|
|||||||
*/
|
*/
|
||||||
VOID
|
VOID
|
||||||
AsmNmiExceptionHandler (
|
AsmNmiExceptionHandler (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
reused once they are set, even after they are "cleared".
|
reused once they are set, even after they are "cleared".
|
||||||
|
|
||||||
For complete implementation, one can copy ReactOS's implementation if
|
For complete implementation, one can copy ReactOS's implementation if
|
||||||
licensing the project under GPL is acceptable. hvpp by wbenny has its own
|
licensing the project under GPL is acceptable. hvpp by Petr Beneš has its
|
||||||
implementation of bitmap but is actually influenced by ReactOS
|
own implementation of bitmap but is actually influenced by ReactOS
|
||||||
implementation and such should be treated as GPL.
|
implementation, and such, should be treated as GPL.
|
||||||
|
|
||||||
@author Satoshi Tanda
|
@author Satoshi Tanda
|
||||||
|
|
||||||
|
|||||||
@@ -34,21 +34,21 @@
|
|||||||
fault (null pointer access) when it fires in the host code. The author
|
fault (null pointer access) when it fires in the host code. The author
|
||||||
has not been able to find out the root cause and a fix.
|
has not been able to find out the root cause and a fix.
|
||||||
*/
|
*/
|
||||||
#if !defined(MDEPKG_NDEBUG)
|
#if defined(MDEPKG_NDEBUG)
|
||||||
|
#define MV_ASSERT(x)
|
||||||
|
#else
|
||||||
#define MV_ASSERT(x) \
|
#define MV_ASSERT(x) \
|
||||||
if (!(x)) \
|
if (!(x)) \
|
||||||
{ \
|
{ \
|
||||||
LOG_ERROR("ASSERT %a(%d): %a", __FILE__, __LINE__, #x); \
|
LOG_ERROR("ASSERT %a(%d): %a", __FILE__, __LINE__, #x); \
|
||||||
MV_PANIC(); \
|
MV_PANIC(); \
|
||||||
} (VOID*)NULL
|
} (VOID*)NULL
|
||||||
#else
|
|
||||||
#define MV_ASSERT(x)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(MDEPKG_NDEBUG)
|
#if defined(MDEPKG_NDEBUG)
|
||||||
#define MV_VERIFY(x) MV_ASSERT(x)
|
|
||||||
#else
|
|
||||||
#define MV_VERIFY(x) (x)
|
#define MV_VERIFY(x) (x)
|
||||||
|
#else
|
||||||
|
#define MV_VERIFY(x) MV_ASSERT(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MV_MAX(x, y) MAX((x), (y))
|
#define MV_MAX(x, y) MAX((x), (y))
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
.code
|
.code
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Read the value from LDTR.
|
; @brief Reads the value of LDTR.
|
||||||
;
|
;
|
||||||
; @return The value of LDTR.
|
; @return The value of LDTR.
|
||||||
;
|
;
|
||||||
@@ -20,7 +20,7 @@ AsmReadLdtr proc
|
|||||||
AsmReadLdtr endp
|
AsmReadLdtr endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Read the value from TR.
|
; @brief Reads the value of TR.
|
||||||
;
|
;
|
||||||
; @return The value of TR.
|
; @return The value of TR.
|
||||||
;
|
;
|
||||||
@@ -30,7 +30,7 @@ AsmReadTr proc
|
|||||||
AsmReadTr endp
|
AsmReadTr endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Read the value from ES.
|
; @brief Reads the value of ES.
|
||||||
;
|
;
|
||||||
; @return The value of ES.
|
; @return The value of ES.
|
||||||
;
|
;
|
||||||
@@ -40,7 +40,7 @@ AsmReadEs proc
|
|||||||
AsmReadEs endp
|
AsmReadEs endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Read the value from CS.
|
; @brief Reads the value of CS.
|
||||||
;
|
;
|
||||||
; @return The value of CS.
|
; @return The value of CS.
|
||||||
;
|
;
|
||||||
@@ -50,7 +50,7 @@ AsmReadCs proc
|
|||||||
AsmReadCs endp
|
AsmReadCs endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Read the value from SS.
|
; @brief Reads the value of SS.
|
||||||
;
|
;
|
||||||
; @return The value of SS.
|
; @return The value of SS.
|
||||||
;
|
;
|
||||||
@@ -60,7 +60,7 @@ AsmReadSs proc
|
|||||||
AsmReadSs endp
|
AsmReadSs endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Read the value from DS.
|
; @brief Reads the value of DS.
|
||||||
;
|
;
|
||||||
; @return The value of DS.
|
; @return The value of DS.
|
||||||
;
|
;
|
||||||
@@ -70,7 +70,7 @@ AsmReadDs proc
|
|||||||
AsmReadDs endp
|
AsmReadDs endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Read the value from FS.
|
; @brief Reads the value of FS.
|
||||||
;
|
;
|
||||||
; @return The value of FS.
|
; @return The value of FS.
|
||||||
;
|
;
|
||||||
@@ -80,7 +80,7 @@ AsmReadFs proc
|
|||||||
AsmReadFs endp
|
AsmReadFs endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Read the value from GS.
|
; @brief Reads the value of GS.
|
||||||
;
|
;
|
||||||
; @return The value of GS.
|
; @return The value of GS.
|
||||||
;
|
;
|
||||||
@@ -90,7 +90,7 @@ AsmReadGs proc
|
|||||||
AsmReadGs endp
|
AsmReadGs endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; @brief Write the value to TR.
|
; @brief Writes the value to TR.
|
||||||
;
|
;
|
||||||
; @param[in] RCX - The new TR value to write.
|
; @param[in] RCX - The new TR value to write.
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
UINT16
|
UINT16
|
||||||
AsmReadLdtr (
|
AsmReadLdtr (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -27,7 +26,6 @@ AsmReadLdtr (
|
|||||||
*/
|
*/
|
||||||
UINT16
|
UINT16
|
||||||
AsmReadTr (
|
AsmReadTr (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -37,7 +35,6 @@ AsmReadTr (
|
|||||||
*/
|
*/
|
||||||
UINT16
|
UINT16
|
||||||
AsmReadEs (
|
AsmReadEs (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -47,7 +44,6 @@ AsmReadEs (
|
|||||||
*/
|
*/
|
||||||
UINT16
|
UINT16
|
||||||
AsmReadCs (
|
AsmReadCs (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -57,7 +53,6 @@ AsmReadCs (
|
|||||||
*/
|
*/
|
||||||
UINT16
|
UINT16
|
||||||
AsmReadSs (
|
AsmReadSs (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -67,7 +62,6 @@ AsmReadSs (
|
|||||||
*/
|
*/
|
||||||
UINT16
|
UINT16
|
||||||
AsmReadDs (
|
AsmReadDs (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -77,7 +71,6 @@ AsmReadDs (
|
|||||||
*/
|
*/
|
||||||
UINT16
|
UINT16
|
||||||
AsmReadFs (
|
AsmReadFs (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -87,7 +80,6 @@ AsmReadFs (
|
|||||||
*/
|
*/
|
||||||
UINT16
|
UINT16
|
||||||
AsmReadGs (
|
AsmReadGs (
|
||||||
VOID
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -457,7 +457,7 @@ LogFlushThread (
|
|||||||
LOGGER_CONTEXT* logger;
|
LOGGER_CONTEXT* logger;
|
||||||
LARGE_INTEGER interval;
|
LARGE_INTEGER interval;
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
logger = (LOGGER_CONTEXT*)StartContext;
|
logger = (LOGGER_CONTEXT*)StartContext;
|
||||||
interval.QuadPart = -(10000ll * logger->FlushIntervalInMs);
|
interval.QuadPart = -(10000ll * logger->FlushIntervalInMs);
|
||||||
@@ -562,13 +562,6 @@ CleanupPairedLogBuffer (
|
|||||||
ExFreePoolWithTag(PairedLogBuffer->InactiveLogBuffer->LogEntries, LOGGER_POOL_TAG);
|
ExFreePoolWithTag(PairedLogBuffer->InactiveLogBuffer->LogEntries, LOGGER_POOL_TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Initializes the global logger.
|
|
||||||
|
|
||||||
@param[in] Configuration - The configuration for initialization.
|
|
||||||
|
|
||||||
@return STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
|
||||||
*/
|
|
||||||
LOGGER_INIT
|
LOGGER_INIT
|
||||||
_Use_decl_annotations_
|
_Use_decl_annotations_
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
@@ -581,7 +574,7 @@ InitializeLogger (
|
|||||||
HANDLE fileHandle;
|
HANDLE fileHandle;
|
||||||
HANDLE threadHandle;
|
HANDLE threadHandle;
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
MV_ASSERT(g_Logger == NULL);
|
MV_ASSERT(g_Logger == NULL);
|
||||||
|
|
||||||
@@ -615,7 +608,7 @@ InitializeLogger (
|
|||||||
&filePath,
|
&filePath,
|
||||||
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
|
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
|
||||||
NULL,
|
NULL,
|
||||||
NULL)
|
NULL);
|
||||||
status = ZwCreateFile(&fileHandle,
|
status = ZwCreateFile(&fileHandle,
|
||||||
FILE_APPEND_DATA | SYNCHRONIZE,
|
FILE_APPEND_DATA | SYNCHRONIZE,
|
||||||
&objectAttributes,
|
&objectAttributes,
|
||||||
@@ -709,7 +702,6 @@ InitializeLogger (
|
|||||||
//
|
//
|
||||||
g_Logger = logger;
|
g_Logger = logger;
|
||||||
|
|
||||||
|
|
||||||
Exit:
|
Exit:
|
||||||
if (NT_ERROR(status))
|
if (NT_ERROR(status))
|
||||||
{
|
{
|
||||||
@@ -729,9 +721,6 @@ Exit:
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Clean up the logger.
|
|
||||||
*/
|
|
||||||
LOGGER_PAGED
|
LOGGER_PAGED
|
||||||
_Use_decl_annotations_
|
_Use_decl_annotations_
|
||||||
VOID
|
VOID
|
||||||
@@ -742,7 +731,7 @@ CleanupLogger (
|
|||||||
LOGGER_CONTEXT* logger;
|
LOGGER_CONTEXT* logger;
|
||||||
SIZE_T maxOverflowedLogSize;
|
SIZE_T maxOverflowedLogSize;
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
MV_ASSERT(g_Logger != NULL);
|
MV_ASSERT(g_Logger != NULL);
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,13 @@ typedef struct _LOGGER_CONFIGURATION
|
|||||||
PCWSTR FilePath;
|
PCWSTR FilePath;
|
||||||
} LOGGER_CONFIGURATION;
|
} LOGGER_CONFIGURATION;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Initializes the global logger.
|
||||||
|
|
||||||
|
@param[in] Configuration - The configuration for initialization.
|
||||||
|
|
||||||
|
@return STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
||||||
|
*/
|
||||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||||
_Must_inspect_result_
|
_Must_inspect_result_
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
@@ -76,6 +83,9 @@ InitializeLogger (
|
|||||||
_In_ CONST LOGGER_CONFIGURATION* Configuration
|
_In_ CONST LOGGER_CONFIGURATION* Configuration
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Clean up the logger.
|
||||||
|
*/
|
||||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||||
VOID
|
VOID
|
||||||
CleanupLogger (
|
CleanupLogger (
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ ConvertNtToMvStatus (
|
|||||||
|
|
||||||
@param[in] DriverObject - The driver's driver object.
|
@param[in] DriverObject - The driver's driver object.
|
||||||
|
|
||||||
@param[in] RegistryPath - A path to the driver's registry key.
|
@param[in] RegistryPath - The path to the driver's registry key.
|
||||||
|
|
||||||
@return STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
@return STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
||||||
*/
|
*/
|
||||||
@@ -148,7 +148,7 @@ DriverUnload (
|
|||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(DriverObject);
|
UNREFERENCED_PARAMETER(DriverObject);
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Start cross-platform clean up.
|
// Start cross-platform clean up.
|
||||||
@@ -165,7 +165,7 @@ InitializePlatform (
|
|||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
LOGGER_CONFIGURATION loggerConfig;
|
LOGGER_CONFIGURATION loggerConfig;
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Initialize in-house logger. Enable all flags.
|
// Initialize in-house logger. Enable all flags.
|
||||||
@@ -192,7 +192,7 @@ VOID
|
|||||||
CleanupPlatform (
|
CleanupPlatform (
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
CleanupLogger();
|
CleanupLogger();
|
||||||
}
|
}
|
||||||
@@ -206,7 +206,7 @@ Sleep (
|
|||||||
{
|
{
|
||||||
LARGE_INTEGER interval;
|
LARGE_INTEGER interval;
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
interval.QuadPart = -(LONGLONG)(10000 * Milliseconds);
|
interval.QuadPart = -(LONGLONG)(10000 * Milliseconds);
|
||||||
(VOID)KeDelayExecutionThread(KernelMode, FALSE, &interval);
|
(VOID)KeDelayExecutionThread(KernelMode, FALSE, &interval);
|
||||||
@@ -296,7 +296,7 @@ ReserveVirtualAddress (
|
|||||||
UINT64 PageCount
|
UINT64 PageCount
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
return MmAllocateMappingAddress(PageCount * PAGE_SIZE, MV_POOL_TAG);
|
return MmAllocateMappingAddress(PageCount * PAGE_SIZE, MV_POOL_TAG);
|
||||||
}
|
}
|
||||||
@@ -309,7 +309,7 @@ FreeReservedVirtualAddress (
|
|||||||
UINT64 PageCount
|
UINT64 PageCount
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
UNREFERENCED_PARAMETER(PageCount);
|
UNREFERENCED_PARAMETER(PageCount);
|
||||||
|
|
||||||
@@ -326,7 +326,7 @@ RunOnAllProcessors (
|
|||||||
{
|
{
|
||||||
UINT32 numberOfProcessors;
|
UINT32 numberOfProcessors;
|
||||||
|
|
||||||
PAGED_CODE()
|
PAGED_CODE();
|
||||||
|
|
||||||
numberOfProcessors = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
|
numberOfProcessors = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
|
||||||
for (UINT32 index = 0; index < numberOfProcessors; ++index)
|
for (UINT32 index = 0; index < numberOfProcessors; ++index)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*!
|
/*!
|
||||||
@file Public.h
|
@file Public.h
|
||||||
|
|
||||||
@brief Interfaces to communicate with our hypervisor.
|
@brief Interfaces to communicate with the hypervisor.
|
||||||
|
|
||||||
@author Satoshi Tanda
|
@author Satoshi Tanda
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
/*!
|
/*!
|
||||||
@file Utils.c
|
@file Utils.c
|
||||||
|
|
||||||
@brief Utility functions that could be used by both on root and non-root
|
@brief Utility functions that could be used by both the host and non-host.
|
||||||
operations.
|
|
||||||
|
|
||||||
@author Satoshi Tanda
|
@author Satoshi Tanda
|
||||||
|
|
||||||
@@ -75,10 +74,10 @@ Exit:
|
|||||||
/*!
|
/*!
|
||||||
@brief Returns the segment descriptor corresponds to the SegmentSelector.
|
@brief Returns the segment descriptor corresponds to the SegmentSelector.
|
||||||
|
|
||||||
@param[in] DescriptorTableBase - An address of the base of the descriptor
|
@param[in] DescriptorTableBase - The address of the base of the descriptor
|
||||||
table.
|
table.
|
||||||
|
|
||||||
@param[in] SegmentSelector - A segment selector value.
|
@param[in] SegmentSelector - The segment selector value.
|
||||||
|
|
||||||
@return The segment descriptor corresponds to the SegmentSelector.
|
@return The segment descriptor corresponds to the SegmentSelector.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
/*!
|
/*!
|
||||||
@file Utils.h
|
@file Utils.h
|
||||||
|
|
||||||
@brief Utility functions that could be used by both on root and non-root
|
@brief Utility functions that could be used by both the host and non-host.
|
||||||
operations.
|
|
||||||
|
|
||||||
@author Satoshi Tanda
|
@author Satoshi Tanda
|
||||||
|
|
||||||
@@ -36,7 +35,7 @@ ComputeAddressFromIndexes (
|
|||||||
@brief Returns the access right of the segment specified by the SegmentSelector
|
@brief Returns the access right of the segment specified by the SegmentSelector
|
||||||
for VMX.
|
for VMX.
|
||||||
|
|
||||||
@param[in] SegmentSelector - A segment selector value.
|
@param[in] SegmentSelector - The segment selector value.
|
||||||
|
|
||||||
@return The access right of the segment for VMX.
|
@return The access right of the segment for VMX.
|
||||||
*/
|
*/
|
||||||
@@ -48,7 +47,7 @@ GetSegmentAccessRight (
|
|||||||
/*!
|
/*!
|
||||||
@brief Returns the base address of the segment specified by SegmentSelector.
|
@brief Returns the base address of the segment specified by SegmentSelector.
|
||||||
|
|
||||||
@param[in] DescriptorTableBase - An address of the base of the descriptor
|
@param[in] DescriptorTableBase - The address of the base of the descriptor
|
||||||
table.
|
table.
|
||||||
|
|
||||||
@param[in] SegmentSelector - The segment selector which points to the
|
@param[in] SegmentSelector - The segment selector which points to the
|
||||||
|
|||||||
Reference in New Issue
Block a user