Publish the files
This commit is contained in:
105
Sources/Platform/EFI/EfiAsm.asm
Normal file
105
Sources/Platform/EFI/EfiAsm.asm
Normal file
@@ -0,0 +1,105 @@
|
||||
;
|
||||
; @file EfiAsm.asm
|
||||
;
|
||||
; @brief EFI specific MASM-written functions.
|
||||
;
|
||||
; @author Satoshi Tanda
|
||||
;
|
||||
; @copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
;
|
||||
include AsmCommon.inc
|
||||
.code
|
||||
|
||||
extern HandleHostException : proc
|
||||
|
||||
;
|
||||
; The index to track an interrupt number for generating AsmDefaultExceptionHandlers.
|
||||
;
|
||||
Index = 0
|
||||
|
||||
;
|
||||
; Generates the default exception handler code for the given interrupt/exception
|
||||
; number. The generated code assumes that the interrupt/exception does not push
|
||||
; error code.
|
||||
;
|
||||
; Index is incremented whenever this macro is used.
|
||||
;
|
||||
INTERRUPT_HANDLER macro InterruptNumber
|
||||
push 0 ; Push dummy error code for consistent stack layout.
|
||||
push InterruptNumber
|
||||
jmp AsmCommonExceptionHandler
|
||||
Index = Index + 1
|
||||
endm
|
||||
|
||||
;
|
||||
; Generates the default exception handler code for the given interrupt/exception
|
||||
; number. The generated code assumes that the interrupt/exception pushes error code.
|
||||
;
|
||||
; Index is incremented whenever this macro is used.
|
||||
;
|
||||
INTERRUPT_HANDLER_WITH_CODE macro InterruptNumber
|
||||
nop ; Error code is expected to be pushed by the processor.
|
||||
nop
|
||||
push InterruptNumber
|
||||
jmp AsmCommonExceptionHandler
|
||||
Index = Index + 1
|
||||
endm
|
||||
|
||||
;
|
||||
; @brief The default host exception handlers.
|
||||
;
|
||||
; @details This is the function containing actually 256 stub functions generated
|
||||
; with the INTERRUPT_HANDLER and INTERRUPT_HANDLER_WITH_CODE macros. Each function
|
||||
; works as a hendler of the corresponding interrupt/exception in the host.
|
||||
;
|
||||
AsmDefaultExceptionHandlers proc
|
||||
repeat 8
|
||||
INTERRUPT_HANDLER Index ; INT0-7
|
||||
endm
|
||||
|
||||
INTERRUPT_HANDLER_WITH_CODE Index ; INT8
|
||||
INTERRUPT_HANDLER Index ; INT9
|
||||
|
||||
repeat 5
|
||||
INTERRUPT_HANDLER_WITH_CODE Index ; INT10-14
|
||||
endm
|
||||
|
||||
repeat 2
|
||||
INTERRUPT_HANDLER Index ; INT15-16
|
||||
endm
|
||||
|
||||
INTERRUPT_HANDLER_WITH_CODE Index ; INT17
|
||||
|
||||
repeat 238
|
||||
INTERRUPT_HANDLER Index ; INT18-255
|
||||
endm
|
||||
AsmDefaultExceptionHandlers endp
|
||||
|
||||
;
|
||||
; @brief The common logic for the exception handlers.
|
||||
;
|
||||
; @details This function pushes register values into the stack and calls the
|
||||
; high-level handler written in C.
|
||||
;
|
||||
AsmCommonExceptionHandler proc
|
||||
PUSHAQ
|
||||
mov rcx, rsp
|
||||
sub rsp, 20h
|
||||
call HandleHostException
|
||||
add rsp, 20h
|
||||
POPAQ
|
||||
add rsp, 10h ; Remove the error code and interrupt number.
|
||||
iretq
|
||||
AsmCommonExceptionHandler endp
|
||||
|
||||
;
|
||||
; @brief The NMI handler for the host.
|
||||
;
|
||||
; @details This implementation is incomplete. When NMI occurs while the host is
|
||||
; executed, it should be injected to the guest.
|
||||
;
|
||||
AsmNmiExceptionHandler proc
|
||||
iretq
|
||||
AsmNmiExceptionHandler endp
|
||||
|
||||
end
|
||||
27
Sources/Platform/EFI/EfiAsm.h
Normal file
27
Sources/Platform/EFI/EfiAsm.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
@file EfiAsm.h
|
||||
|
||||
@brief EFI specific MASM-written functions.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
#include "EfiCommon.h"
|
||||
|
||||
/*!
|
||||
@brief The array of the default host exception handlers.
|
||||
*/
|
||||
VOID
|
||||
AsmDefaultExceptionHandlers (
|
||||
VOID
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief The host NMI handler.
|
||||
*/
|
||||
VOID
|
||||
AsmNmiExceptionHandler (
|
||||
VOID
|
||||
);
|
||||
98
Sources/Platform/EFI/EfiBitmap.c
Normal file
98
Sources/Platform/EFI/EfiBitmap.c
Normal file
@@ -0,0 +1,98 @@
|
||||
/*!
|
||||
@file EfiBitmap.c
|
||||
|
||||
@brief EFI specific implementation of bitmap algorithm.
|
||||
|
||||
@details Implementation of algorithm is good enough for the current use of
|
||||
those API but is incomplete and broken, for example, bits are NEVER
|
||||
reused once they are set, even after they are "cleared".
|
||||
|
||||
For complete implementation, one can copy ReactOS's implementation if
|
||||
licensing the project under GPL is acceptable. hvpp by wbenny has its own
|
||||
implementation of bitmap but is actually influenced by ReactOS
|
||||
implementation and such should be treated as GPL.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#include "EfiBitmap.h"
|
||||
|
||||
VOID
|
||||
RtlInitializeBitMap (
|
||||
RTL_BITMAP* BitMapHeader,
|
||||
UINT32* BitMapBuffer,
|
||||
UINT32 SizeOfBitMap
|
||||
)
|
||||
{
|
||||
BitMapHeader->SizeOfBitMap = SizeOfBitMap;
|
||||
BitMapHeader->Buffer = BitMapBuffer;
|
||||
BitMapHeader->NextAvailableBitIndex = 0;
|
||||
BitMapHeader->SetBitCount = 0;
|
||||
}
|
||||
|
||||
UINT32
|
||||
RtlFindClearBitsAndSet (
|
||||
RTL_BITMAP* BitMapHeader,
|
||||
UINT32 NumberToFind,
|
||||
UINT32 HintIndex
|
||||
)
|
||||
{
|
||||
UINT32 clearBitIndex;
|
||||
|
||||
//
|
||||
// Return error if the bitmap does not have enough bits after the current
|
||||
// index. In other words, it never search from the index 0 because implementation
|
||||
// never clears bits.
|
||||
//
|
||||
if (BitMapHeader->NextAvailableBitIndex + NumberToFind > BitMapHeader->SizeOfBitMap)
|
||||
{
|
||||
clearBitIndex = MAXUINT32;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// "Find" clear bits, which is just using bits from the current position.
|
||||
//
|
||||
clearBitIndex = BitMapHeader->NextAvailableBitIndex;
|
||||
|
||||
//
|
||||
// "Set" requested bits, which is just moving the index further.
|
||||
//
|
||||
BitMapHeader->SetBitCount += NumberToFind;
|
||||
BitMapHeader->NextAvailableBitIndex += NumberToFind;
|
||||
|
||||
Exit:
|
||||
return clearBitIndex;
|
||||
}
|
||||
|
||||
BOOLEAN
|
||||
RtlAreBitsClear (
|
||||
RTL_BITMAP* BitMapHeader,
|
||||
UINT32 StartingIndex,
|
||||
UINT32 Length
|
||||
)
|
||||
{
|
||||
//
|
||||
// This implementation support checking only whether an entire bitmap is
|
||||
// cleared.
|
||||
//
|
||||
ASSERT(StartingIndex == 0);
|
||||
ASSERT(Length == BitMapHeader->SizeOfBitMap);
|
||||
|
||||
return (BitMapHeader->SetBitCount == 0);
|
||||
}
|
||||
|
||||
VOID
|
||||
RtlClearBits (
|
||||
RTL_BITMAP* BitMapHeader,
|
||||
UINT32 StartingIndex,
|
||||
UINT32 NumberToClear
|
||||
)
|
||||
{
|
||||
//
|
||||
// This implementation only change this counter, and never actually clear
|
||||
// bits and let them to be re-set.
|
||||
//
|
||||
BitMapHeader->SetBitCount -= NumberToClear;
|
||||
}
|
||||
96
Sources/Platform/EFI/EfiBitmap.h
Normal file
96
Sources/Platform/EFI/EfiBitmap.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*!
|
||||
@file EfiBitmap.h
|
||||
|
||||
@brief EFI specific implementation of bitmap algorithm.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
#include "EfiCommon.h"
|
||||
|
||||
typedef struct _RTL_BITMAP
|
||||
{
|
||||
UINT32 SizeOfBitMap; // Number of bits in bit map
|
||||
UINT32* Buffer; // Pointer to the bit map itself
|
||||
UINT32 NextAvailableBitIndex; // Index of the next cleared bit
|
||||
UINT32 SetBitCount; // Number of bits currently set
|
||||
} RTL_BITMAP;
|
||||
|
||||
/*!
|
||||
@brief Initializes the header of a bitmap variable.
|
||||
|
||||
@param[out] BitMapHeader - The pointer to the bitmap variable to initialize.
|
||||
|
||||
@param[in] BitMapBuffer - The pointer to caller-allocated memory for the bitmap
|
||||
itself.
|
||||
|
||||
@param[in] SizeOfBitMap - The number of bits in the bitmap.
|
||||
*/
|
||||
VOID
|
||||
RtlInitializeBitMap (
|
||||
RTL_BITMAP* BitMapHeader,
|
||||
UINT32* BitMapBuffer,
|
||||
UINT32 SizeOfBitMap
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Searches for a range of clear bits of a requested size within a bitmap
|
||||
and sets all bits in the range when it has been located.
|
||||
|
||||
@param[out] BitMapHeader - The pointer to the RTL_BITMAP structure that
|
||||
describes the bitmap.
|
||||
|
||||
@param[in] NumberToFind - How many contiguous clear bits will satisfy this
|
||||
request.
|
||||
|
||||
@param[in] HintIndex - Unused.
|
||||
|
||||
@return The zero-based starting bit index for a clear bit range of the
|
||||
requested size that it set, or it returns 0xFFFFFFFF if it cannot find
|
||||
such a range within the given bitmap variable.
|
||||
*/
|
||||
UINT32
|
||||
RtlFindClearBitsAndSet (
|
||||
RTL_BITMAP* BitMapHeader,
|
||||
UINT32 NumberToFind,
|
||||
UINT32 HintIndex
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Determines whether a given range of bits within a bitmap variable is
|
||||
clear.
|
||||
|
||||
@param[in] BitMapHeader - The pointer to the RTL_BITMAP structure that
|
||||
describes the bitmap.
|
||||
|
||||
@param[in] StartingIndex - The start of the bit range to be tested.
|
||||
|
||||
@param[in] Length - How many bits to test.
|
||||
|
||||
@return Whether a given range of bits within a bitmap variable is clear.
|
||||
*/
|
||||
BOOLEAN
|
||||
RtlAreBitsClear (
|
||||
RTL_BITMAP* BitMapHeader,
|
||||
UINT32 StartingIndex,
|
||||
UINT32 Length
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Sets all bits in the specified range of bits in the bitmap to zero.
|
||||
|
||||
@param[out] BitMapHeader - The pointer to the RTL_BITMAP structure that
|
||||
describes the bitmap.
|
||||
|
||||
@param[in] StartingIndex - Unused.
|
||||
|
||||
@param[in] NumberToClear - How many bits to clear.
|
||||
*/
|
||||
VOID
|
||||
RtlClearBits (
|
||||
RTL_BITMAP* BitMapHeader,
|
||||
UINT32 StartingIndex,
|
||||
UINT32 NumberToClear
|
||||
);
|
||||
135
Sources/Platform/EFI/EfiCommon.h
Normal file
135
Sources/Platform/EFI/EfiCommon.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/*!
|
||||
@file EfiCommon.h
|
||||
|
||||
@brief EFI specific implementation of common things across the project.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
#include <Uefi.h>
|
||||
#include <Library/UefiLib.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
|
||||
//
|
||||
// "structure was padded due to alignment specifier"
|
||||
//
|
||||
#pragma warning(disable: 4324)
|
||||
|
||||
|
||||
/*!
|
||||
@brief Freezes execution of the processor by entering infinite busy loop.
|
||||
*/
|
||||
#define MV_PANIC() do { CpuDeadLoop(); } while (TRUE)
|
||||
#define MV_DEBUG_BREAK()
|
||||
#define MV_SECTION_INIT
|
||||
#define MV_SECTION_PAGED
|
||||
#define MV_ASSERT(x) ASSERT(x)
|
||||
#if !defined(MDEPKG_NDEBUG)
|
||||
#define MV_VERIFY(x) ASSERT(x)
|
||||
#else
|
||||
#define MV_VERIFY(x) (x)
|
||||
#endif
|
||||
#define MV_MAX(x, y) MAX((x), (y))
|
||||
#define MV_MIN(x, y) MIN((x), (y))
|
||||
|
||||
//
|
||||
// MSVC compatibility type definitions.
|
||||
//
|
||||
typedef CHAR8 CHAR;
|
||||
typedef CHAR16 WCHAR;
|
||||
|
||||
//
|
||||
// MSVC intrinsics.
|
||||
//
|
||||
unsigned __int64 __readcr0(void);
|
||||
unsigned __int64 __readcr2(void);
|
||||
unsigned __int64 __readcr3(void);
|
||||
unsigned __int64 __readcr4(void);
|
||||
unsigned __int64 __readdr(unsigned int);
|
||||
unsigned __int64 __readeflags(void);
|
||||
unsigned __int64 __readmsr(unsigned long);
|
||||
unsigned char __vmx_on(unsigned __int64 *);
|
||||
unsigned char __vmx_vmclear(unsigned __int64 *);
|
||||
unsigned char __vmx_vmlaunch(void);
|
||||
unsigned char __vmx_vmptrld(unsigned __int64 *);
|
||||
unsigned char __vmx_vmread(unsigned __int64, unsigned __int64 *);
|
||||
unsigned char __vmx_vmresume(void);
|
||||
unsigned char __vmx_vmwrite(unsigned __int64, unsigned __int64);
|
||||
unsigned long __segmentlimit(unsigned long);
|
||||
void __cpuid(int[4], int);
|
||||
void __cpuidex(int[4], int, int);
|
||||
void __invlpg(void *);
|
||||
void __lidt(void *);
|
||||
void __sidt(void *);
|
||||
void __stosq(unsigned __int64 *, unsigned __int64, unsigned __int64);
|
||||
void __vmx_off(void);
|
||||
void __vmx_vmptrst(unsigned __int64 *);
|
||||
void __writecr0(unsigned __int64);
|
||||
void __writecr2(unsigned __int64);
|
||||
void __writecr3(unsigned __int64);
|
||||
void __writecr4(unsigned __int64);
|
||||
void __writedr(unsigned int, unsigned __int64);
|
||||
void __writemsr(unsigned long, unsigned __int64);
|
||||
void _lgdt(void *);
|
||||
void _sgdt(void *);
|
||||
|
||||
//
|
||||
// MSVC compatibility macro definitions.
|
||||
//
|
||||
#define __drv_aliasesMem
|
||||
#define __drv_allocatesMem(x)
|
||||
#define __drv_freesMem(x)
|
||||
#define _Acquires_lock_(x)
|
||||
#define _In_
|
||||
#define _In_opt_
|
||||
#define _In_range_(x, y)
|
||||
#define _In_reads_bytes_(x)
|
||||
#define _Inout_
|
||||
#define _IRQL_raises_(x)
|
||||
#define _IRQL_requires_max_(x)
|
||||
#define _IRQL_restores_
|
||||
#define _IRQL_saves_
|
||||
#define _Must_inspect_result_
|
||||
#define _Out_
|
||||
#define _Out_opt_
|
||||
#define _Out_writes_bytes_(x)
|
||||
#define _Post_maybenull_
|
||||
#define _Post_writable_byte_size_(x)
|
||||
#define _Pre_notnull_
|
||||
#define _Printf_format_string_
|
||||
#define _Releases_lock_(x)
|
||||
#define _Requires_lock_held_(x)
|
||||
#define _Requires_lock_not_held_(x)
|
||||
#define _Return_type_success_(x)
|
||||
#define _Success_(x)
|
||||
#define _Use_decl_annotations_
|
||||
#define _When_(x, y)
|
||||
|
||||
#define ANSI_NULL ((CHAR)0)
|
||||
#define ANYSIZE_ARRAY (1)
|
||||
#define ARGUMENT_PRESENT(x) ((x) != NULL)
|
||||
#define BooleanFlagOn(F,SF) ((BOOLEAN)(((F) & (SF)) != 0))
|
||||
#define BYTES_TO_PAGES(x) EFI_SIZE_TO_PAGES(x)
|
||||
#define C_ASSERT(x) STATIC_ASSERT(x, #x)
|
||||
#define ClearFlag(_F,_SF) ((_F) &= ~(_SF))
|
||||
#define DBG_UNREFERENCED_PARAMETER(x)
|
||||
#define DECLSPEC_ALIGN(x) __declspec(align(x))
|
||||
#define FlagOn(_F,_SF) ((_F) & (_SF))
|
||||
#define KERNEL_STACK_SIZE (0x6000)
|
||||
#define MAXUINT16 MAX_UINT16
|
||||
#define MAXUINT32 MAX_UINT32
|
||||
#define MAXUINT64 MAX_UINT64
|
||||
#define MAXUINT8 MAX_UINT8
|
||||
#define NOTHING
|
||||
#define PAGE_ALIGN(Va) ((VOID*)((UINT64)(Va) & ~(PAGE_SIZE - 1)))
|
||||
#define PAGE_SIZE EFI_PAGE_SIZE
|
||||
#define PAGED_CODE()
|
||||
#define RTL_NUMBER_OF(x) ARRAY_SIZE(x)
|
||||
#define RtlCopyMemory CopyMem
|
||||
#define RtlZeroMemory ZeroMem
|
||||
#define SetFlag(_F,_SF) ((_F) |= (_SF))
|
||||
#define strcmp(x, y) AsciiStrCmp((x), (y))
|
||||
#define UNREFERENCED_PARAMETER(x)
|
||||
330
Sources/Platform/EFI/EfiHostInitialization.c
Normal file
330
Sources/Platform/EFI/EfiHostInitialization.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/*!
|
||||
@file EfiHostInitialization.c
|
||||
|
||||
@brief EFI specific implementation of host environment initialization.
|
||||
|
||||
@details On EFI, the host uses its own paging structures (CR3) and interrupt
|
||||
descriptor table (IDT).
|
||||
|
||||
Its own paging structures is preferable and the most straightforward
|
||||
approach to void impact from the physical mode to the virtual mode
|
||||
transition happens during OS startup time. After this transition (ie,
|
||||
SetVirtualAddressMap is called from a boot loader), the paging structures
|
||||
that is used at the physical mode and the host would be using it becomes
|
||||
invalid, as nothing runs on the physical mode anymore. This results in
|
||||
crash (triple fault) when VM-exit occurs. One solution could be to
|
||||
subscribe the SetVirtualAddressMap event and notify the host to switch
|
||||
to the new CR3 for the virtual mode, but this has to be done for all
|
||||
logical processors requiring some inter processor calls. The MP protocol
|
||||
could do the job but is no longer available at the moment of the transition
|
||||
notification because the system is already switched from the boot time to
|
||||
the run time.
|
||||
|
||||
Its own interrupt descriptor table is required for the same reason. After
|
||||
transitioning to the virtual mode, the existing IDT becomes invalid. One
|
||||
might think the host IDT is not relevant as interrupts are disabled. The
|
||||
fact is that NMI still occurs while the host is running, and also, having
|
||||
basic diagnose handlers are useful in case of access violation, for example.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#include "EfiHostInitialization.h"
|
||||
#include "EfiAsm.h"
|
||||
#include "EfiPlatform.h"
|
||||
#include "../../Utils.h"
|
||||
|
||||
//
|
||||
// The format of the IDT.
|
||||
//
|
||||
typedef struct _INTERRUPT_GATE_DESCRIPTOR
|
||||
{
|
||||
UINT16 Offset15To0 : 16;
|
||||
UINT16 SegmentSelector : 16;
|
||||
UINT8 Reserved0;
|
||||
UINT8 GateType;
|
||||
UINT16 Offset31To16;
|
||||
UINT32 Offset63To32;
|
||||
UINT32 Reserved1;
|
||||
} INTERRUPT_GATE_DESCRIPTOR;
|
||||
C_ASSERT(sizeof(INTERRUPT_GATE_DESCRIPTOR) == 16);
|
||||
|
||||
//
|
||||
// Collections of paging structures. Only the single PDPT is accommodated to
|
||||
// handle only up to 512GB of physical memory.
|
||||
//
|
||||
typedef struct _PAGING_STRUCTURES
|
||||
{
|
||||
//
|
||||
// There is only one PML4, unless 5-level page mapping is enabled.
|
||||
//
|
||||
DECLSPEC_ALIGN(PAGE_SIZE) PML4E_64 Pml4[PML4_ENTRY_COUNT];
|
||||
|
||||
//
|
||||
// Only one PDPT is used for PML4[0]. This covers 512GB of the physical memory
|
||||
// range and is sufficient for our purpose.
|
||||
//
|
||||
DECLSPEC_ALIGN(PAGE_SIZE) PDPTE_64 Pdpt[1][PDPT_ENTRY_COUNT];
|
||||
|
||||
//
|
||||
// PDs are assigned for each PDPT entry, meaning that 512 (PDPTEs) multiplied
|
||||
// by the PDT entry count.
|
||||
//
|
||||
DECLSPEC_ALIGN(PAGE_SIZE) PDE_2MB_64 Pdt[1][PDPT_ENTRY_COUNT][PDT_ENTRY_COUNT];
|
||||
} PAGING_STRUCTURES;
|
||||
|
||||
//
|
||||
// Paging related.
|
||||
//
|
||||
static PAGING_STRUCTURES g_HostPagingStructures;
|
||||
static CR3 g_HostCr3;
|
||||
|
||||
//
|
||||
// IDT related.
|
||||
//
|
||||
static INTERRUPT_GATE_DESCRIPTOR g_HostIdt[IDT_ENTRY_COUNT];
|
||||
static IDTR g_HostIdtr;
|
||||
|
||||
/*!
|
||||
@brief Initializes the host paging structures.
|
||||
|
||||
@details This function fills out the statically allocated paging structures
|
||||
and builds the identity mapping. All translation is done with 2MB pages
|
||||
since not 4KB granularity configuration is not needed.
|
||||
|
||||
The identity mapping works because when the host is loaded, the EFI system
|
||||
also uses the identity mapping, meaning that it is essentially making a
|
||||
clone of existing paging structures.
|
||||
|
||||
Note that page protections are all writable and executable. One may drop
|
||||
the executable attribute for outside the range of the .text section of
|
||||
this module and drop writable for the same range to be W^X.
|
||||
*/
|
||||
static
|
||||
VOID
|
||||
InitializeHostPagingStructures (
|
||||
)
|
||||
{
|
||||
PML4E_64* pml4;
|
||||
PDPTE_64* pdpt;
|
||||
PDE_2MB_64* pdt;
|
||||
UINT32 pml4Index;
|
||||
|
||||
pml4Index = 0;
|
||||
pml4 = g_HostPagingStructures.Pml4;
|
||||
pdpt = g_HostPagingStructures.Pdpt[pml4Index];
|
||||
|
||||
//
|
||||
// Fill out PML4, PDPT, PDT.
|
||||
//
|
||||
pml4[0].Present = TRUE;
|
||||
pml4[0].Write = TRUE;
|
||||
pml4[0].PageFrameNumber = GetPhysicalAddress(pdpt) >> PAGE_SHIFT;
|
||||
|
||||
for (UINT32 pdptIndex = 0; pdptIndex < PDPT_ENTRY_COUNT; ++pdptIndex)
|
||||
{
|
||||
pdt = g_HostPagingStructures.Pdt[pml4Index][pdptIndex];
|
||||
|
||||
pdpt[pdptIndex].Present = TRUE;
|
||||
pdpt[pdptIndex].Write = TRUE;
|
||||
pdpt[pdptIndex].PageFrameNumber = GetPhysicalAddress(pdt) >> PAGE_SHIFT;
|
||||
|
||||
for (UINT32 pdIndex = 0; pdIndex < PDT_ENTRY_COUNT; ++pdIndex)
|
||||
{
|
||||
UINT64 physicalAddress;
|
||||
|
||||
physicalAddress = ComputeAddressFromIndexes(pml4Index,
|
||||
pdptIndex,
|
||||
pdIndex,
|
||||
0);
|
||||
pdt[pdIndex].Present = TRUE;
|
||||
pdt[pdIndex].Write = TRUE;
|
||||
pdt[pdIndex].LargePage = TRUE;
|
||||
pdt[pdIndex].PageFrameNumber = physicalAddress >> PAGE_SHIFT_2BM;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Then initialize the CR3 to point to the PML4.
|
||||
//
|
||||
g_HostCr3.AddressOfPageDirectory = GetPhysicalAddress(pml4) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Initializes the host IDT.
|
||||
|
||||
@details This function fills out the IDT with AsmDefaultExceptionHandlers[N]
|
||||
where N is the interrupt number, updates IDT[2] with AsmNmiExceptionHandler,
|
||||
and initializes IDTR to point to the IDT.
|
||||
|
||||
AsmDefaultExceptionHandlers is the array of stub functions to transfer
|
||||
execution to the main common logic in AsmCommonExceptionHandler.
|
||||
*/
|
||||
static
|
||||
VOID
|
||||
InitializeHostIdt (
|
||||
)
|
||||
{
|
||||
UINT64 handlerBase;
|
||||
|
||||
//
|
||||
// Get the beginning of the AsmDefaultExceptionHandlers to index.
|
||||
//
|
||||
handlerBase = (UINT64)&AsmDefaultExceptionHandlers;
|
||||
|
||||
//
|
||||
// Fill out all IDT entries.
|
||||
//
|
||||
for (UINT32 i = 0; i < IDT_ENTRY_COUNT; ++i)
|
||||
{
|
||||
static const UINT64 sizeOfHandlerTill0x7f = 9;
|
||||
static const UINT64 sizeOfHandlerFrom0x80 = 12;
|
||||
UINT64 sizeOfHandler;
|
||||
UINT64 handlerAddress;
|
||||
|
||||
//
|
||||
// Compute the address of AsmDefaultExceptionHandlers[i]. Each stub
|
||||
// function is 9 bytes up to 0x7f, and 12 bytes after that.
|
||||
//
|
||||
if (i < 0x80)
|
||||
{
|
||||
sizeOfHandler = sizeOfHandlerTill0x7f;
|
||||
}
|
||||
else
|
||||
{
|
||||
sizeOfHandler = sizeOfHandlerFrom0x80;
|
||||
}
|
||||
handlerAddress = (handlerBase + i * sizeOfHandler);
|
||||
|
||||
//
|
||||
// Fill out the IDT entry. The type is 32-bit Interrupt gate: 0x8E
|
||||
// P=1, DPL=00b, S=0, type=1110b => type_attr=1000_1110b=0x8E)
|
||||
//
|
||||
g_HostIdt[i].Offset15To0 = (UINT16)handlerAddress;
|
||||
g_HostIdt[i].Offset31To16 = (UINT16)(handlerAddress >> 16);
|
||||
g_HostIdt[i].Offset63To32 = (UINT32)(handlerAddress >> 32);
|
||||
g_HostIdt[i].SegmentSelector = AsmReadCs();
|
||||
g_HostIdt[i].GateType = 0x8E;
|
||||
}
|
||||
|
||||
//
|
||||
// Interrupt 0x2 is NMI. This needs special handling.
|
||||
//
|
||||
g_HostIdt[2].Offset15To0 = (UINT16)((UINT64)&AsmNmiExceptionHandler);
|
||||
g_HostIdt[2].Offset31To16 = (UINT16)((UINT64)&AsmNmiExceptionHandler >> 16);
|
||||
g_HostIdt[2].Offset63To32 = (UINT32)((UINT64)&AsmNmiExceptionHandler >> 32);
|
||||
|
||||
//
|
||||
// Finally initialize the IDTR to point to the IDT.
|
||||
//
|
||||
g_HostIdtr.Limit = sizeof(g_HostIdt) - 1;
|
||||
g_HostIdtr.BaseAddress = (UINT64)(&g_HostIdt[0]);
|
||||
}
|
||||
|
||||
VOID
|
||||
InitializeHostEnvironment (
|
||||
)
|
||||
{
|
||||
InitializeHostPagingStructures();
|
||||
InitializeHostIdt();
|
||||
}
|
||||
|
||||
CR3
|
||||
GetHostCr3 (
|
||||
)
|
||||
{
|
||||
return g_HostCr3;
|
||||
}
|
||||
|
||||
CONST IDTR*
|
||||
GetHostIdtr (
|
||||
)
|
||||
{
|
||||
return &g_HostIdtr;
|
||||
}
|
||||
|
||||
VOID
|
||||
InitializeGdt (
|
||||
TASK_STATE_SEGMENT_64* NewTss,
|
||||
SEGMENT_DESCRIPTOR_64* NewGdt,
|
||||
UINT64 NewGdtSize,
|
||||
GDTR* OriginalGdtr
|
||||
)
|
||||
{
|
||||
GDTR newGdtr;
|
||||
SEGMENT_SELECTOR taskRegister;
|
||||
SEGMENT_DESCRIPTOR_64 tssDescriptor;
|
||||
SEGMENT_DESCRIPTOR_64* tssDescriptorInGdt;
|
||||
UINT64 tssAddress;
|
||||
SEGMENT_DESCRIPTOR_32* newGdt32;
|
||||
|
||||
//
|
||||
// Get the current GDTR.
|
||||
//
|
||||
_sgdt(&newGdtr);
|
||||
*OriginalGdtr = newGdtr;
|
||||
|
||||
//
|
||||
// Copy contents of the existing GDT to the new GDT in the processor context.
|
||||
//
|
||||
RtlCopyMemory(NewGdt, (VOID*)newGdtr.BaseAddress, newGdtr.Limit);
|
||||
|
||||
//
|
||||
// Set up TR pointing to the entry going to be added below in the GDT. Divide
|
||||
// by the size of SEGMENT_DESCRIPTOR_32 because the limit field is in bytes
|
||||
// while the index is index in the entry count.
|
||||
//
|
||||
taskRegister.Flags = 0;
|
||||
taskRegister.Index = (newGdtr.Limit + 1ull) / sizeof(SEGMENT_DESCRIPTOR_32);
|
||||
|
||||
//
|
||||
// Update the GDTR. Change the base to the new location and increase the
|
||||
// limit to add one more entry for TR. Make sure we have enough space
|
||||
// in the processor context to copy the contents of GDT.
|
||||
//
|
||||
newGdtr.BaseAddress = (UINT64)NewGdt;
|
||||
newGdtr.Limit += sizeof(SEGMENT_DESCRIPTOR_64);
|
||||
MV_ASSERT(newGdtr.Limit < NewGdtSize);
|
||||
|
||||
//
|
||||
// At this point, the TR points to uninitialized entry in the GDT. Set up
|
||||
// the Task State Segment Descriptor to be written to GDT.
|
||||
//
|
||||
tssAddress = (UINT64)NewTss;
|
||||
RtlZeroMemory(&tssDescriptor, sizeof(tssDescriptor));
|
||||
tssDescriptor.SegmentLimitLow = sizeof(*NewTss) - 1;
|
||||
tssDescriptor.BaseAddressLow = (tssAddress & MAXUINT16);
|
||||
tssDescriptor.BaseAddressMiddle = ((tssAddress >> 16) & MAXUINT8);
|
||||
tssDescriptor.BaseAddressHigh = ((tssAddress >> 24) & MAXUINT8);
|
||||
tssDescriptor.BaseAddressUpper = ((tssAddress >> 32) & MAXUINT32);
|
||||
tssDescriptor.Type = SEGMENT_DESCRIPTOR_TYPE_TSS_AVAILABLE;
|
||||
tssDescriptor.Present = TRUE;
|
||||
|
||||
//
|
||||
// Update the GDT by writing entry for TSS, which is pointed by the TR.
|
||||
//
|
||||
newGdt32 = (SEGMENT_DESCRIPTOR_32*)NewGdt;
|
||||
tssDescriptorInGdt = (SEGMENT_DESCRIPTOR_64*)(&newGdt32[taskRegister.Index]);
|
||||
*tssDescriptorInGdt = tssDescriptor;
|
||||
|
||||
//
|
||||
// Finally, update the GDTR and TR of the current processor. The VT-x
|
||||
// requires the guest task segment register to be configured correctly
|
||||
// and the UEFI platform typically does not (ie, TR being zero). Update TR
|
||||
// to point to the task segment just set up.
|
||||
//
|
||||
// See: 26.3.1.2 Checks on Guest Segment Registers
|
||||
//
|
||||
_lgdt(&newGdtr);
|
||||
AsmWriteTr(taskRegister.Flags);
|
||||
}
|
||||
|
||||
VOID
|
||||
CleanupGdt (
|
||||
CONST GDTR* OriginalGdtr
|
||||
)
|
||||
{
|
||||
MV_ASSERT(OriginalGdtr->BaseAddress != 0);
|
||||
_lgdt((VOID*)OriginalGdtr);
|
||||
}
|
||||
11
Sources/Platform/EFI/EfiHostInitialization.h
Normal file
11
Sources/Platform/EFI/EfiHostInitialization.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
@file EfiHostInitialization.h
|
||||
|
||||
@brief EFI specific implementation of host environment initialization.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
#include "../../HostInitialization.h"
|
||||
151
Sources/Platform/EFI/EfiLogger.c
Normal file
151
Sources/Platform/EFI/EfiLogger.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*!
|
||||
@file EfiLogger.c
|
||||
|
||||
@brief EFI specific implementation of the logger.
|
||||
|
||||
@details Logging becomes no-op at the runtime when UefiDebugLibConOut is used,
|
||||
ie, -D DEBUG_ON_SERIAL_PORT is not set. See use of mPostEBS in
|
||||
edk2/MdePkg/Library/UefiDebugLibConOut/DebugLib.c for this behavior.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#include "EfiLogger.h"
|
||||
#include <Guid/EventGroup.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
#include <Library/PrintLib.h>
|
||||
|
||||
//
|
||||
// The event handle for ExitBootServices event subscription.
|
||||
//
|
||||
static EFI_EVENT g_EfiExitBootServicesEvent;
|
||||
|
||||
//
|
||||
// FALSE during boot time. Once the system is transition to the run time, any
|
||||
// EFI API that depends on boot services directly or indirectly cannot be called.
|
||||
// The most significant implication is the console output cannot be used anymore.
|
||||
//
|
||||
static BOOLEAN g_AtRuntime;
|
||||
|
||||
/*!
|
||||
@brief Handles the ExitBootServices notification.
|
||||
|
||||
@details The solo purpose of this handler is to report the end of console
|
||||
debug output.
|
||||
|
||||
@param[in] Event - Unused.
|
||||
|
||||
@param[in] Context - Unused.
|
||||
*/
|
||||
static
|
||||
VOID
|
||||
EFIAPI
|
||||
ExitBootServicesHandler (
|
||||
EFI_EVENT Event,
|
||||
VOID* Context
|
||||
)
|
||||
{
|
||||
LOG_INFO("ExitBootServices was called. Ending console logging if used.");
|
||||
gBS->CloseEvent(g_EfiExitBootServicesEvent);
|
||||
g_AtRuntime = TRUE;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Registers ExitBootServices notification.
|
||||
|
||||
@return EFI_SUCCESS on success; otherwise, an appropriate error code.
|
||||
*/
|
||||
static
|
||||
EFI_STATUS
|
||||
RegisterNotification (
|
||||
)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
|
||||
status = gBS->CreateEventEx(EVT_NOTIFY_SIGNAL,
|
||||
TPL_NOTIFY,
|
||||
ExitBootServicesHandler,
|
||||
NULL,
|
||||
&gEfiEventExitBootServicesGuid,
|
||||
&g_EfiExitBootServicesEvent);
|
||||
if (EFI_ERROR(status))
|
||||
{
|
||||
LOG_ERROR("CreateEventEx failed : %r", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Exit:
|
||||
return status;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
InitializeLogger (
|
||||
)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
|
||||
status = RegisterNotification();
|
||||
if (EFI_ERROR(status))
|
||||
{
|
||||
LOG_ERROR("RegisterNotifications failed : %r", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Exit:
|
||||
return status;
|
||||
}
|
||||
|
||||
VOID
|
||||
CleanupLogger (
|
||||
)
|
||||
{
|
||||
if (g_AtRuntime == FALSE)
|
||||
{
|
||||
gBS->CloseEvent(g_EfiExitBootServicesEvent);
|
||||
}
|
||||
}
|
||||
|
||||
VOID
|
||||
LogMessage (
|
||||
LOG_LEVEL Level,
|
||||
CONST CHAR* FunctionName,
|
||||
CONST CHAR* Format,
|
||||
...
|
||||
)
|
||||
{
|
||||
//
|
||||
// Mapping from LOG_LEVEL to the EFI log level.
|
||||
//
|
||||
static CONST UINT64 debugLevelMapping[] =
|
||||
{
|
||||
0,
|
||||
DEBUG_ERROR,
|
||||
DEBUG_WARN,
|
||||
DEBUG_INFO,
|
||||
DEBUG_VERBOSE,
|
||||
};
|
||||
C_ASSERT(RTL_NUMBER_OF(debugLevelMapping) == LogLevelReserved);
|
||||
|
||||
VA_LIST args;
|
||||
CHAR8 message[400];
|
||||
|
||||
VA_START(args, Format);
|
||||
(VOID)AsciiVSPrint(message, sizeof(message), Format, args);
|
||||
VA_END(args);
|
||||
|
||||
DebugPrint(debugLevelMapping[Level], "%a: %a\n", FunctionName, message);
|
||||
}
|
||||
|
||||
VOID
|
||||
LogEarlyErrorMessage (
|
||||
CONST CHAR* Format,
|
||||
...
|
||||
)
|
||||
{
|
||||
VA_LIST args;
|
||||
|
||||
VA_START(args, Format);
|
||||
(VOID)DebugVPrint(DEBUG_ERROR, Format, args);
|
||||
VA_END(args);
|
||||
}
|
||||
26
Sources/Platform/EFI/EfiLogger.h
Normal file
26
Sources/Platform/EFI/EfiLogger.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*!
|
||||
@file EfiLogger.h
|
||||
|
||||
@brief EFI specific implementation of the logger.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#include "../../Logger.h"
|
||||
|
||||
/*!
|
||||
@brief Initializes the global logger.
|
||||
|
||||
@return EFI_SUCCESS on success; otherwise, an appropriate error code.
|
||||
*/
|
||||
EFI_STATUS
|
||||
InitializeLogger (
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Clean up the logger.
|
||||
*/
|
||||
VOID
|
||||
CleanupLogger (
|
||||
);
|
||||
398
Sources/Platform/EFI/EfiPlatform.c
Normal file
398
Sources/Platform/EFI/EfiPlatform.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/*!
|
||||
@file EfiPlatform.c
|
||||
|
||||
@details Some of API in this module can be called from the host. See the
|
||||
description of each API.
|
||||
|
||||
@brief EFI specific platform API.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#include "EfiPlatform.h"
|
||||
#include <Guid/EventGroup.h>
|
||||
#include <Library/DevicePathLib.h>
|
||||
#include <Library/MemoryAllocationLib.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
#include <Library/UefiRuntimeServicesTableLib.h>
|
||||
#include <Pi/PiDxeCis.h>
|
||||
#include <Protocol/MpService.h>
|
||||
#include <Protocol/LoadedImage.h>
|
||||
#include "EfiLogger.h"
|
||||
#include "../../MiniVisor.h"
|
||||
|
||||
//
|
||||
// Maps conversion between MV_STATUS and NTSTATUS.
|
||||
//
|
||||
typedef struct _STATUS_MAPPING
|
||||
{
|
||||
MV_STATUS MvStatus;
|
||||
EFI_STATUS EfiStatus;
|
||||
} STATUS_MAPPING;
|
||||
|
||||
static CONST STATUS_MAPPING k_StatusMapping[] =
|
||||
{
|
||||
{ MV_STATUS_SUCCESS, EFI_SUCCESS, },
|
||||
{ MV_STATUS_UNSUCCESSFUL, EFI_ABORTED, },
|
||||
{ MV_STATUS_ACCESS_DENIED, EFI_ACCESS_DENIED, },
|
||||
{ MV_STATUS_INSUFFICIENT_RESOURCES, EFI_OUT_OF_RESOURCES, },
|
||||
{ MV_STATUS_HV_OPERATION_FAILED, EFI_UNSUPPORTED, },
|
||||
};
|
||||
|
||||
//
|
||||
// The multi-processor protocol. Only available during the boot-time.
|
||||
//
|
||||
static EFI_MP_SERVICES_PROTOCOL* g_MpServices;
|
||||
|
||||
/*!
|
||||
@brief Converts MV_STATUS to EFI_STATUS.
|
||||
|
||||
@param[in] Status - The MV_STATUS to convert from.
|
||||
|
||||
@return The converted EFI_STATUS.
|
||||
*/
|
||||
static
|
||||
EFI_STATUS
|
||||
ConvertMvToEfiStatus (
|
||||
MV_STATUS Status
|
||||
)
|
||||
{
|
||||
for (UINT32 i = 0; i < RTL_NUMBER_OF(k_StatusMapping); ++i)
|
||||
{
|
||||
if (Status == k_StatusMapping[i].MvStatus)
|
||||
{
|
||||
return k_StatusMapping[i].EfiStatus;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Update the mapping when this assert hits.
|
||||
//
|
||||
MV_ASSERT(FALSE);
|
||||
return EFI_ABORTED;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Converts EFI_STATUS to MV_STATUS.
|
||||
|
||||
@param[in] Status - The EFI_STATUS to convert from.
|
||||
|
||||
@return The converted MV_STATUS.
|
||||
*/
|
||||
static
|
||||
MV_STATUS
|
||||
ConvertEfiToMvStatus (
|
||||
EFI_STATUS Status
|
||||
)
|
||||
{
|
||||
for (UINT32 i = 0; i < RTL_NUMBER_OF(k_StatusMapping); ++i)
|
||||
{
|
||||
if (Status == k_StatusMapping[i].EfiStatus)
|
||||
{
|
||||
return k_StatusMapping[i].MvStatus;
|
||||
}
|
||||
}
|
||||
return MV_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Displays information about the current module.
|
||||
|
||||
@details Use of this API at the run-time is not allowed.
|
||||
|
||||
@return EFI_SUCCESS on success; otherwise, an appropriate error code.
|
||||
*/
|
||||
static
|
||||
EFI_STATUS
|
||||
PrintLoadedImageInformation (
|
||||
)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
EFI_LOADED_IMAGE_PROTOCOL* loadedImageInfo;
|
||||
CHAR16* devicePath;
|
||||
|
||||
devicePath = NULL;
|
||||
|
||||
status = gBS->OpenProtocol(gImageHandle,
|
||||
&gEfiLoadedImageProtocolGuid,
|
||||
(VOID**)&loadedImageInfo,
|
||||
gImageHandle,
|
||||
NULL,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||||
if (EFI_ERROR(status))
|
||||
{
|
||||
LOG_ERROR("OpenProtocol failed : %r", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
devicePath = ConvertDevicePathToText(loadedImageInfo->FilePath, TRUE, TRUE);
|
||||
if (devicePath == NULL)
|
||||
{
|
||||
LOG_ERROR("ConvertDevicePathToText failed");
|
||||
status = EFI_OUT_OF_RESOURCES;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
LOG_INFO("%s - %llx:%llx",
|
||||
devicePath,
|
||||
loadedImageInfo->ImageBase,
|
||||
MV_ADD2PTR(loadedImageInfo->ImageBase, loadedImageInfo->ImageSize));
|
||||
|
||||
Exit:
|
||||
if (devicePath != NULL)
|
||||
{
|
||||
FreePool(devicePath);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief The platform specific module entry point.
|
||||
|
||||
@param[in] ImageHandle - The handle of this module.
|
||||
|
||||
@param[in] SystemTable - The boot service table pointer.
|
||||
|
||||
@return EFI_SUCCESS on success; otherwise, an appropriate error code.
|
||||
*/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
DriverEntry (
|
||||
EFI_HANDLE ImageHandle,
|
||||
EFI_SYSTEM_TABLE* SystemTable
|
||||
)
|
||||
{
|
||||
ASSERT(ImageHandle == gImageHandle);
|
||||
ASSERT(SystemTable->BootServices == gBS);
|
||||
|
||||
return ConvertMvToEfiStatus(InitializeMiniVisor());
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief The platform specific module unload callback.
|
||||
|
||||
@param[in] ImageHandle - The handle of this module.
|
||||
|
||||
@return Always EFI_SUCCESS.
|
||||
*/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
DriverUnload (
|
||||
EFI_HANDLE ImageHandle
|
||||
)
|
||||
{
|
||||
CleanupMiniVisor();
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
MV_STATUS
|
||||
InitializePlatform (
|
||||
)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
BOOLEAN isLoggerInitialized;
|
||||
|
||||
status = InitializeLogger();
|
||||
if (EFI_ERROR(status))
|
||||
{
|
||||
LOG_EARLY_ERROR("InitializeLogger failed : %r", status);
|
||||
goto Exit;
|
||||
}
|
||||
isLoggerInitialized = TRUE;
|
||||
|
||||
PrintLoadedImageInformation();
|
||||
|
||||
//
|
||||
// Locate the protocol for multi-processor handling. UEFI on a Hyper-V VM
|
||||
// does not implement this and fails.
|
||||
//
|
||||
status = gBS->LocateProtocol(&gEfiMpServiceProtocolGuid,
|
||||
NULL,
|
||||
&g_MpServices);
|
||||
if (EFI_ERROR(status))
|
||||
{
|
||||
LOG_ERROR("LocateProtocol failed : %r", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Exit:
|
||||
if (EFI_ERROR(status))
|
||||
{
|
||||
if (isLoggerInitialized != FALSE)
|
||||
{
|
||||
CleanupLogger();
|
||||
}
|
||||
}
|
||||
return ConvertEfiToMvStatus(status);
|
||||
}
|
||||
|
||||
VOID
|
||||
CleanupPlatform (
|
||||
)
|
||||
{
|
||||
CleanupLogger();
|
||||
}
|
||||
|
||||
UINT32
|
||||
GetActiveProcessorCount (
|
||||
)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
UINTN numberOfProcessors;
|
||||
UINTN numberOfEnabledProcessors;
|
||||
|
||||
status = g_MpServices->GetNumberOfProcessors(g_MpServices,
|
||||
&numberOfProcessors,
|
||||
&numberOfEnabledProcessors);
|
||||
if (EFI_ERROR(status))
|
||||
{
|
||||
MV_PANIC();
|
||||
}
|
||||
|
||||
return (UINT32)numberOfEnabledProcessors;
|
||||
}
|
||||
|
||||
UINT32
|
||||
GetCurrentProcessorNumber (
|
||||
)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
UINTN processorNumber;
|
||||
|
||||
status = g_MpServices->WhoAmI(g_MpServices, &processorNumber);
|
||||
if (EFI_ERROR(status))
|
||||
{
|
||||
MV_PANIC();
|
||||
}
|
||||
|
||||
return (UINT32)processorNumber;
|
||||
}
|
||||
|
||||
UINT64
|
||||
GetPhysicalAddress (
|
||||
VOID* VirualAddress
|
||||
)
|
||||
{
|
||||
//
|
||||
// Assume the current CR3 uses the identity mapping. This is the case during
|
||||
// the boot time or execution of the host.
|
||||
//
|
||||
return (UINT64)VirualAddress;
|
||||
}
|
||||
|
||||
VOID*
|
||||
GetVirtualAddress (
|
||||
UINT64 PhysicalAddress
|
||||
)
|
||||
{
|
||||
//
|
||||
// This function assume the current CR3 uses the identity mapping. This is
|
||||
// the case during the boot time or execution of the host.
|
||||
//
|
||||
return (VOID*)PhysicalAddress;
|
||||
}
|
||||
|
||||
VOID*
|
||||
AllocateSystemMemory (
|
||||
UINT64 PageCount
|
||||
)
|
||||
{
|
||||
VOID* pages;
|
||||
|
||||
pages = AllocateRuntimePages(PageCount);
|
||||
if (pages == NULL)
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
ZeroMem(pages, PageCount * EFI_PAGE_SIZE);
|
||||
|
||||
Exit:
|
||||
return pages;
|
||||
}
|
||||
|
||||
VOID
|
||||
FreeSystemMemory (
|
||||
VOID* Pages,
|
||||
UINT64 PageCount
|
||||
)
|
||||
{
|
||||
FreePages(Pages, PageCount);
|
||||
}
|
||||
|
||||
VOID*
|
||||
ReserveVirtualAddress (
|
||||
UINT64 PageCount
|
||||
)
|
||||
{
|
||||
return AllocateSystemMemory(PageCount);
|
||||
}
|
||||
|
||||
VOID
|
||||
FreeReservedVirtualAddress (
|
||||
VOID* BaseAddress,
|
||||
UINT64 PageCount
|
||||
)
|
||||
{
|
||||
FreeSystemMemory(BaseAddress, PageCount);
|
||||
}
|
||||
|
||||
VOID
|
||||
RunOnAllProcessors (
|
||||
USER_PASSIVE_CALLBACK* Callback,
|
||||
VOID* Context
|
||||
)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
|
||||
Callback(Context);
|
||||
if (GetActiveProcessorCount() == 1)
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
status = g_MpServices->StartupAllAPs(g_MpServices,
|
||||
Callback,
|
||||
TRUE,
|
||||
NULL,
|
||||
0,
|
||||
Context,
|
||||
NULL);
|
||||
if (EFI_ERROR(status))
|
||||
{
|
||||
MV_PANIC();
|
||||
}
|
||||
|
||||
Exit:
|
||||
return;
|
||||
}
|
||||
|
||||
VOID
|
||||
InitializeSystemSpinLock (
|
||||
SPIN_LOCK* SpinLock
|
||||
)
|
||||
{
|
||||
(VOID)InitializeSpinLock(SpinLock);
|
||||
}
|
||||
|
||||
UINT8
|
||||
AcquireSystemSpinLock (
|
||||
SPIN_LOCK* SpinLock
|
||||
)
|
||||
{
|
||||
//
|
||||
// This function does not raise TPL as it is not available at the run time.
|
||||
//
|
||||
(VOID)AcquireSpinLock(SpinLock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
VOID
|
||||
ReleaseSystemSpinLock (
|
||||
SPIN_LOCK* SpinLock,
|
||||
UINT8 PreviousContext
|
||||
)
|
||||
{
|
||||
ASSERT(PreviousContext == 0);
|
||||
(VOID)ReleaseSpinLock(SpinLock);
|
||||
}
|
||||
11
Sources/Platform/EFI/EfiPlatform.h
Normal file
11
Sources/Platform/EFI/EfiPlatform.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
@file EfiPlatform.h
|
||||
|
||||
@brief EFI specific platform API.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
#include "../../Platform.h"
|
||||
102
Sources/Platform/Windows/WinAsm.asm
Normal file
102
Sources/Platform/Windows/WinAsm.asm
Normal file
@@ -0,0 +1,102 @@
|
||||
;
|
||||
; @file WinAsm.asm
|
||||
;
|
||||
; @brief Windows specific MASM-written functions.
|
||||
;
|
||||
; @author Satoshi Tanda
|
||||
;
|
||||
; @copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
;
|
||||
.code
|
||||
|
||||
;
|
||||
; @brief Read the value from LDTR.
|
||||
;
|
||||
; @return The value of LDTR.
|
||||
;
|
||||
AsmReadLdtr proc
|
||||
sldt ax
|
||||
ret
|
||||
AsmReadLdtr endp
|
||||
|
||||
;
|
||||
; @brief Read the value from TR.
|
||||
;
|
||||
; @return The value of TR.
|
||||
;
|
||||
AsmReadTr proc
|
||||
str ax
|
||||
ret
|
||||
AsmReadTr endp
|
||||
|
||||
;
|
||||
; @brief Read the value from ES.
|
||||
;
|
||||
; @return The value of ES.
|
||||
;
|
||||
AsmReadEs proc
|
||||
mov ax, es
|
||||
ret
|
||||
AsmReadEs endp
|
||||
|
||||
;
|
||||
; @brief Read the value from CS.
|
||||
;
|
||||
; @return The value of CS.
|
||||
;
|
||||
AsmReadCs proc
|
||||
mov ax, cs
|
||||
ret
|
||||
AsmReadCs endp
|
||||
|
||||
;
|
||||
; @brief Read the value from SS.
|
||||
;
|
||||
; @return The value of SS.
|
||||
;
|
||||
AsmReadSs proc
|
||||
mov ax, ss
|
||||
ret
|
||||
AsmReadSs endp
|
||||
|
||||
;
|
||||
; @brief Read the value from DS.
|
||||
;
|
||||
; @return The value of DS.
|
||||
;
|
||||
AsmReadDs proc
|
||||
mov ax, ds
|
||||
ret
|
||||
AsmReadDs endp
|
||||
|
||||
;
|
||||
; @brief Read the value from FS.
|
||||
;
|
||||
; @return The value of FS.
|
||||
;
|
||||
AsmReadFs proc
|
||||
mov ax, fs
|
||||
ret
|
||||
AsmReadFs endp
|
||||
|
||||
;
|
||||
; @brief Read the value from GS.
|
||||
;
|
||||
; @return The value of GS.
|
||||
;
|
||||
AsmReadGs proc
|
||||
mov ax, gs
|
||||
ret
|
||||
AsmReadGs endp
|
||||
|
||||
;
|
||||
; @brief Write the value to TR.
|
||||
;
|
||||
; @param[in] RCX - The new TR value to write.
|
||||
;
|
||||
AsmWriteTr proc
|
||||
ltr cx
|
||||
ret
|
||||
AsmWriteTr endp
|
||||
|
||||
end
|
||||
101
Sources/Platform/Windows/WinAsm.h
Normal file
101
Sources/Platform/Windows/WinAsm.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*!
|
||||
@file WinAsm.h
|
||||
|
||||
@brief Windows specific MASM-written functions.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
#include "WinCommon.h"
|
||||
|
||||
/*!
|
||||
@brief Reads the value of LDTR.
|
||||
|
||||
@return The value of LDTR.
|
||||
*/
|
||||
UINT16
|
||||
AsmReadLdtr (
|
||||
VOID
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Reads the value of TR.
|
||||
|
||||
@return The value of TR.
|
||||
*/
|
||||
UINT16
|
||||
AsmReadTr (
|
||||
VOID
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Reads the value of ES.
|
||||
|
||||
@return The value of ES.
|
||||
*/
|
||||
UINT16
|
||||
AsmReadEs (
|
||||
VOID
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Reads the value of CS.
|
||||
|
||||
@return The value of CS.
|
||||
*/
|
||||
UINT16
|
||||
AsmReadCs (
|
||||
VOID
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Reads the value of SS.
|
||||
|
||||
@return The value of SS.
|
||||
*/
|
||||
UINT16
|
||||
AsmReadSs (
|
||||
VOID
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Reads the value of DS.
|
||||
|
||||
@return The value of DS.
|
||||
*/
|
||||
UINT16
|
||||
AsmReadDs (
|
||||
VOID
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Reads the value of FS.
|
||||
|
||||
@return The value of FS.
|
||||
*/
|
||||
UINT16
|
||||
AsmReadFs (
|
||||
VOID
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Reads the value of GS.
|
||||
|
||||
@return The value of GS.
|
||||
*/
|
||||
UINT16
|
||||
AsmReadGs (
|
||||
VOID
|
||||
);
|
||||
|
||||
/*!
|
||||
@brief Writes the value to TR.
|
||||
|
||||
@param[in] TaskSelector - The value to write to TR.
|
||||
*/
|
||||
VOID
|
||||
AsmWriteTr (
|
||||
_In_ UINT16 TaskSelector
|
||||
);
|
||||
60
Sources/Platform/Windows/WinCommon.h
Normal file
60
Sources/Platform/Windows/WinCommon.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*!
|
||||
@file WinCommon.h
|
||||
|
||||
@brief Windows specific implementation of common things across the project.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
#include <intrin.h>
|
||||
#include <ntifs.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
//
|
||||
// "Error annotation: Must succeed pool allocations are forbidden. Allocation
|
||||
// failures cause a system crash."
|
||||
//
|
||||
#pragma warning(disable: __WARNING_ERROR)
|
||||
|
||||
/*!
|
||||
@brief Breaks into a debugger if present, and then triggers bug check.
|
||||
*/
|
||||
#define MV_PANIC() \
|
||||
MV_DEBUG_BREAK(); \
|
||||
__pragma(warning(push)) \
|
||||
__pragma(warning(disable: __WARNING_USE_OTHER_FUNCTION)) \
|
||||
KeBugCheckEx(MANUALLY_INITIATED_CRASH, 0, 0, 0, 0) \
|
||||
__pragma(warning(pop))
|
||||
|
||||
/*!
|
||||
@brief Breaks into a kernel debugger if present.
|
||||
|
||||
@details This macro is emits software breakpoint that only hits when a
|
||||
kernel debugger is present. This macro is useful because it does not
|
||||
change the current frame unlike the DbgBreakPoint function, and
|
||||
breakpoint by this macro can be overwritten with NOP without impacting
|
||||
other breakpoints.
|
||||
*/
|
||||
#define MV_DEBUG_BREAK() \
|
||||
if (KD_DEBUGGER_NOT_PRESENT) \
|
||||
{ \
|
||||
NOTHING; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
__debugbreak(); \
|
||||
} \
|
||||
(VOID*)(0)
|
||||
|
||||
//
|
||||
// The handy macros to specify in which section the code should be placed.
|
||||
//
|
||||
#define MV_SECTION_INIT __declspec(code_seg("INIT"))
|
||||
#define MV_SECTION_PAGED __declspec(code_seg("PAGE"))
|
||||
|
||||
#define MV_ASSERT(x) NT_ASSERT(x)
|
||||
#define MV_VERIFY(x) NT_VERIFY(x)
|
||||
#define MV_MAX(x, y) max((x), (y))
|
||||
#define MV_MIN(x, y) min((x), (y))
|
||||
67
Sources/Platform/Windows/WinHostInitialization.c
Normal file
67
Sources/Platform/Windows/WinHostInitialization.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/*!
|
||||
@file WinHostInitialization.c
|
||||
|
||||
@brief Windows specific implementation of host environment initialization.
|
||||
|
||||
@details On Windows, no special set up is done because the host shares the
|
||||
System process CR3 and IDTR for ease of debugging, and other interactions
|
||||
with the guest as demanded.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#include "WinHostInitialization.h"
|
||||
|
||||
//
|
||||
// The host CR3 and IDTR on Windows are the same as that of the System process.
|
||||
// This allows the host to be debugged with Windbg.
|
||||
//
|
||||
static CR3 g_HostCr3;
|
||||
static IDTR g_HostIdtr;
|
||||
|
||||
VOID
|
||||
InitializeHostEnvironment (
|
||||
)
|
||||
{
|
||||
MV_ASSERT(PsGetCurrentProcess() == PsInitialSystemProcess);
|
||||
|
||||
g_HostCr3.Flags = __readcr3();
|
||||
__sidt(&g_HostIdtr);
|
||||
}
|
||||
|
||||
CR3
|
||||
GetHostCr3 (
|
||||
)
|
||||
{
|
||||
return g_HostCr3;
|
||||
}
|
||||
|
||||
CONST IDTR*
|
||||
GetHostIdtr (
|
||||
)
|
||||
{
|
||||
return &g_HostIdtr;
|
||||
}
|
||||
|
||||
VOID
|
||||
InitializeGdt (
|
||||
TASK_STATE_SEGMENT_64* NewTss,
|
||||
SEGMENT_DESCRIPTOR_64* NewGdt,
|
||||
UINT64 NewGdtSize,
|
||||
GDTR* OriginalGdtr
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(NewTss);
|
||||
UNREFERENCED_PARAMETER(NewGdt);
|
||||
UNREFERENCED_PARAMETER(NewGdtSize);
|
||||
UNREFERENCED_PARAMETER(OriginalGdtr);
|
||||
}
|
||||
|
||||
VOID
|
||||
CleanupGdt (
|
||||
CONST GDTR* OriginalGdtr
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(OriginalGdtr);
|
||||
}
|
||||
11
Sources/Platform/Windows/WinHostInitialization.h
Normal file
11
Sources/Platform/Windows/WinHostInitialization.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
@file WinHostInitialization.h
|
||||
|
||||
@brief Windows specific implementation of host environment initialization.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
#include "../../Platform.h"
|
||||
977
Sources/Platform/Windows/WinLogger.c
Normal file
977
Sources/Platform/Windows/WinLogger.c
Normal file
@@ -0,0 +1,977 @@
|
||||
/*!
|
||||
@file WinLogger.c
|
||||
|
||||
@brief Windows specific implementation of the logger.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#include "WinLogger.h"
|
||||
#include "WinPlatform.h"
|
||||
|
||||
//
|
||||
// Tells the CRT not to use a inline version of CRT functions, which use
|
||||
// internal functions that lead to linker errors.
|
||||
//
|
||||
#define _NO_CRT_STDIO_INLINE
|
||||
|
||||
#define NTSTRSAFE_NO_CB_FUNCTIONS
|
||||
#include <ntstrsafe.h>
|
||||
#include <ntintsafe.h>
|
||||
|
||||
//
|
||||
// "Error annotation: Must succeed pool allocations are forbidden. Allocation
|
||||
// failures cause a system crash."
|
||||
//
|
||||
#pragma warning(disable: __WARNING_ERROR)
|
||||
|
||||
//
|
||||
// The pool tag for logging.
|
||||
//
|
||||
#define LOGGER_POOL_TAG ((ULONG)'rgoL')
|
||||
|
||||
NTKERNELAPI
|
||||
PCHAR
|
||||
NTAPI
|
||||
PsGetProcessImageFileName (
|
||||
_In_ PEPROCESS Process
|
||||
);
|
||||
|
||||
//
|
||||
// The maximum characters the DbgPrint family can handle at once.
|
||||
//
|
||||
#define LOGGER_MAX_DBGPRINT_LENGTH 512
|
||||
|
||||
//
|
||||
// The format of a single debug log message stored in DEBUG_LOG_BUFFER::LogEntries.
|
||||
//
|
||||
#include <pshpack1.h>
|
||||
typedef struct _DEBUG_LOG_ENTRY
|
||||
{
|
||||
//
|
||||
// The system time of when this message is seen in the debug print callback.
|
||||
//
|
||||
LARGE_INTEGER Timestamp;
|
||||
|
||||
//
|
||||
// The level of this message.
|
||||
//
|
||||
LOG_LEVEL Level;
|
||||
|
||||
//
|
||||
// The number of the processor which generated this message.
|
||||
//
|
||||
ULONG ProcessorNumber;
|
||||
|
||||
//
|
||||
// The process and thread IDs which generated this message.
|
||||
//
|
||||
CLIENT_ID ClientId;
|
||||
|
||||
//
|
||||
// The name of the process which generated this message.
|
||||
//
|
||||
CHAR ProcessName[16];
|
||||
|
||||
//
|
||||
// The name of the function where generated this message.
|
||||
//
|
||||
CHAR FunctionName[32];
|
||||
|
||||
//
|
||||
// The length of the message stored in LogMessage in characters.
|
||||
//
|
||||
USHORT LogMessageLength;
|
||||
|
||||
//
|
||||
// The debug log message, not including terminating null.
|
||||
//
|
||||
CHAR LogMessage[ANYSIZE_ARRAY];
|
||||
} DEBUG_LOG_ENTRY;
|
||||
#include <poppack.h>
|
||||
|
||||
//
|
||||
// The active and inactive buffer layout.
|
||||
//
|
||||
typedef struct _DEBUG_LOG_BUFFER
|
||||
{
|
||||
//
|
||||
// The pointer to the buffer storing the sequence of DEBUG_LOG_ENTRYs (it is
|
||||
// not a pointer to a single entry or an array of entries either).
|
||||
//
|
||||
DEBUG_LOG_ENTRY* LogEntries;
|
||||
|
||||
//
|
||||
// The offset to the address where the next DEBUG_LOG_ENTRY should be saved,
|
||||
// counted from LogEntries.
|
||||
//
|
||||
UINT64 NextLogOffset;
|
||||
|
||||
//
|
||||
// How many bytes are not saved into LogEntries due to lack of space.
|
||||
//
|
||||
SIZE_T OverflowedLogSize;
|
||||
} DEBUG_LOG_BUFFER;
|
||||
|
||||
//
|
||||
// The pair of log buffers used to save log messages in memory.
|
||||
//
|
||||
typedef struct _PAIRED_DEBUG_LOG_BUFFER
|
||||
{
|
||||
//
|
||||
// Indicates whether ActiveLogBuffer and InactiveLogBuffer are usable.
|
||||
//
|
||||
BOOLEAN BufferValid;
|
||||
|
||||
//
|
||||
// The lock must be held before accessing any other fields of this structure.
|
||||
//
|
||||
SPIN_LOCK ActiveLogBufferLock;
|
||||
|
||||
//
|
||||
// The size of ActiveLogBuffer and InactiveLogBuffer.
|
||||
//
|
||||
SIZE_T BufferSize;
|
||||
|
||||
//
|
||||
// The maximum size of overflow observed during use of this
|
||||
// PAIRED_DEBUG_LOG_BUFFER. Useful to know how much BufferSize should be
|
||||
// increased.
|
||||
//
|
||||
SIZE_T MaxOverflowedLogSize;
|
||||
|
||||
//
|
||||
// The pointers to two buffers: active and inactive. Active buffer is used
|
||||
// to save new messages as they comes in. Inactive buffer is buffer accessed
|
||||
// and cleared up by the flush buffer thread. The flush buffer thread switches
|
||||
// them before flushing so that duration lock is held remains minimum.
|
||||
//
|
||||
DEBUG_LOG_BUFFER* ActiveLogBuffer;
|
||||
DEBUG_LOG_BUFFER* InactiveLogBuffer;
|
||||
|
||||
//
|
||||
// Actual log buffers. Those are pointed by ActiveLogBuffer and
|
||||
// InactiveLogBuffer.
|
||||
//
|
||||
DEBUG_LOG_BUFFER LogBuffers[2];
|
||||
} PAIRED_DEBUG_LOG_BUFFER;
|
||||
|
||||
//
|
||||
// The logger instance.
|
||||
//
|
||||
typedef struct _LOGGER_CONTEXT
|
||||
{
|
||||
LOG_LEVEL Level; // See LOGGER_CONFIGURATION.
|
||||
LOGGER_CONFIGURATION_FLAGS Flags; // See LOGGER_CONFIGURATION.
|
||||
ULONG FlushIntervalInMs; // See LOGGER_CONFIGURATION.
|
||||
|
||||
//
|
||||
// The log file handle. NULL if a log file is not used.
|
||||
//
|
||||
HANDLE LogFileHandle;
|
||||
|
||||
//
|
||||
// The flush buffer thread.
|
||||
//
|
||||
PKTHREAD FlushBufferThread;
|
||||
|
||||
//
|
||||
// The event to tell the flush buffer thread to exit.
|
||||
//
|
||||
KEVENT ThreadExitEvent;
|
||||
|
||||
//
|
||||
// The log buffers.
|
||||
//
|
||||
PAIRED_DEBUG_LOG_BUFFER PairedLogBuffer;
|
||||
} LOGGER_CONTEXT;
|
||||
|
||||
//
|
||||
// The empty logger instance. Used when the logger is initialized with
|
||||
// LogLevelNone. This is never "allocated" and "freed".
|
||||
//
|
||||
static LOGGER_CONTEXT k_EmptyLogger = { LogLevelNone, };
|
||||
|
||||
//
|
||||
// The string representation of the log levels.
|
||||
//
|
||||
static CONST PCSTR k_LogLevelStrings[] =
|
||||
{
|
||||
"NON",
|
||||
"ERR",
|
||||
"WRN",
|
||||
"INF",
|
||||
"DBG",
|
||||
};
|
||||
|
||||
//
|
||||
// The global logger instance.
|
||||
//
|
||||
static LOGGER_CONTEXT* g_Logger;
|
||||
|
||||
/*!
|
||||
@brief Flushes all save log messages.
|
||||
|
||||
@param[in,out] Logger - The logger instance.
|
||||
*/
|
||||
static
|
||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||
VOID
|
||||
FlushDebugLogEntries (
|
||||
_Inout_ LOGGER_CONTEXT* Logger
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
KIRQL oldIrql;
|
||||
DEBUG_LOG_BUFFER* logBufferToFlush;
|
||||
IO_STATUS_BLOCK ioStatusBlock;
|
||||
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
//
|
||||
// Swap active and inactive buffer.
|
||||
//
|
||||
oldIrql = AcquireSystemSpinLock(&Logger->PairedLogBuffer.ActiveLogBufferLock);
|
||||
logBufferToFlush = Logger->PairedLogBuffer.ActiveLogBuffer;
|
||||
Logger->PairedLogBuffer.ActiveLogBuffer = Logger->PairedLogBuffer.InactiveLogBuffer;
|
||||
Logger->PairedLogBuffer.InactiveLogBuffer = logBufferToFlush;
|
||||
ReleaseSystemSpinLock(&Logger->PairedLogBuffer.ActiveLogBufferLock, oldIrql);
|
||||
MV_ASSERT(Logger->PairedLogBuffer.ActiveLogBuffer !=
|
||||
Logger->PairedLogBuffer.InactiveLogBuffer);
|
||||
|
||||
//
|
||||
// Iterate all saved debug log messages (if exist).
|
||||
//
|
||||
for (ULONG offset = 0; offset < logBufferToFlush->NextLogOffset; /**/)
|
||||
{
|
||||
DEBUG_LOG_ENTRY* logEntry;
|
||||
CHAR logMessage[LOGGER_MAX_DBGPRINT_LENGTH];
|
||||
CHAR logTimestamp[20];
|
||||
CHAR logLevel[5];
|
||||
CHAR logProcessorNumber[5];
|
||||
CHAR logPidTid[13];
|
||||
CHAR logProcessName[17];
|
||||
CHAR logFunctionName[34];
|
||||
ANSI_STRING tmpLogLine;
|
||||
TIME_FIELDS timeFields;
|
||||
LARGE_INTEGER localTime;
|
||||
ULONG logMessageLength;
|
||||
|
||||
logTimestamp[0] = ANSI_NULL;
|
||||
logLevel[0] = ANSI_NULL;
|
||||
logProcessorNumber[0] = ANSI_NULL;
|
||||
logPidTid[0] = ANSI_NULL;
|
||||
logProcessName[0] = ANSI_NULL;
|
||||
logFunctionName[0] = ANSI_NULL;
|
||||
|
||||
logEntry = (DEBUG_LOG_ENTRY*)MV_ADD2PTR(logBufferToFlush->LogEntries, offset);
|
||||
|
||||
//
|
||||
// Build a temporal ANSI_STRING to stringify a non-null terminated string.
|
||||
//
|
||||
tmpLogLine.Buffer = logEntry->LogMessage;
|
||||
tmpLogLine.Length = logEntry->LogMessageLength;
|
||||
tmpLogLine.MaximumLength = logEntry->LogMessageLength;
|
||||
|
||||
if (Logger->Flags.u.EnableTimestamp != FALSE)
|
||||
{
|
||||
//
|
||||
// Convert the time stamp to the local time in the human readable format.
|
||||
//
|
||||
ExSystemTimeToLocalTime(&logEntry->Timestamp, &localTime);
|
||||
RtlTimeToTimeFields(&localTime, &timeFields);
|
||||
status = RtlStringCchPrintfA(logTimestamp,
|
||||
RTL_NUMBER_OF(logTimestamp),
|
||||
"%02hd-%02hd %02hd:%02hd:%02hd.%03hd\t",
|
||||
timeFields.Month,
|
||||
timeFields.Day,
|
||||
timeFields.Hour,
|
||||
timeFields.Minute,
|
||||
timeFields.Second,
|
||||
timeFields.Milliseconds);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
MV_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Logger->Flags.u.EnableTimestamp != FALSE)
|
||||
{
|
||||
status = RtlStringCchPrintfA(logLevel,
|
||||
RTL_NUMBER_OF(logLevel),
|
||||
"%s\t",
|
||||
k_LogLevelStrings[logEntry->Level]);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
MV_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Logger->Flags.u.EnableProcessorNumber != FALSE)
|
||||
{
|
||||
status = RtlStringCchPrintfA(logProcessorNumber,
|
||||
RTL_NUMBER_OF(logProcessorNumber),
|
||||
"%lu\t",
|
||||
logEntry->ProcessorNumber);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
MV_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Logger->Flags.u.EnablePidTid != FALSE)
|
||||
{
|
||||
status = RtlStringCchPrintfA(logPidTid,
|
||||
RTL_NUMBER_OF(logPidTid),
|
||||
"%5lu\t%5lu\t",
|
||||
HandleToULong(logEntry->ClientId.UniqueProcess),
|
||||
HandleToULong(logEntry->ClientId.UniqueThread));
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
MV_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Logger->Flags.u.EnableProcessName != FALSE)
|
||||
{
|
||||
status = RtlStringCchPrintfA(logProcessName,
|
||||
RTL_NUMBER_OF(logProcessName),
|
||||
"%-15s\t",
|
||||
logEntry->ProcessName);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
MV_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Logger->Flags.u.EnableFunctionName != FALSE)
|
||||
{
|
||||
status = RtlStringCchPrintfA(logFunctionName,
|
||||
RTL_NUMBER_OF(logFunctionName),
|
||||
"%-32s\t",
|
||||
logEntry->FunctionName);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
MV_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
status = RtlStringCchPrintfA(logMessage,
|
||||
RTL_NUMBER_OF(logMessage),
|
||||
"%s%s%s%s%s%s%Z\r\n",
|
||||
logTimestamp,
|
||||
logLevel,
|
||||
logProcessorNumber,
|
||||
logPidTid,
|
||||
logProcessName,
|
||||
logFunctionName,
|
||||
&tmpLogLine);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
//
|
||||
// This should not happen, but if it does, just discard all log
|
||||
// messages. The next attempt will very likely fail too.
|
||||
//
|
||||
MV_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
logMessageLength = (ULONG)strlen(logMessage);
|
||||
|
||||
if (Logger->LogFileHandle != NULL)
|
||||
{
|
||||
status = ZwWriteFile(Logger->LogFileHandle,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&ioStatusBlock,
|
||||
logMessage,
|
||||
logMessageLength,
|
||||
NULL,
|
||||
NULL);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
//
|
||||
// This can happen when the system is shutting down and the file
|
||||
// system was already unmounted. Nothing we can do here.
|
||||
//
|
||||
NOTHING;
|
||||
}
|
||||
}
|
||||
|
||||
logMessage[logMessageLength - 2] = '\n';
|
||||
logMessage[logMessageLength - 1] = ANSI_NULL;
|
||||
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "%s", logMessage);
|
||||
|
||||
//
|
||||
// Compute the offset to the next entry by adding the size of the current
|
||||
// entry.
|
||||
//
|
||||
offset += RTL_SIZEOF_THROUGH_FIELD(DEBUG_LOG_ENTRY, LogMessageLength) +
|
||||
logEntry->LogMessageLength;
|
||||
}
|
||||
|
||||
//
|
||||
// If the debug log messages exist, and no error happened before, flush the
|
||||
// log file. This may fail if the file system is unmounted after the last
|
||||
// successful write..
|
||||
//
|
||||
if ((Logger->LogFileHandle != NULL) &&
|
||||
(logBufferToFlush->NextLogOffset != 0) &&
|
||||
NT_SUCCESS(status))
|
||||
{
|
||||
(VOID)ZwFlushBuffersFile(Logger->LogFileHandle, &ioStatusBlock);
|
||||
}
|
||||
|
||||
//
|
||||
// Update the maximum overflow size as necessary.
|
||||
//
|
||||
Logger->PairedLogBuffer.MaxOverflowedLogSize = max(
|
||||
Logger->PairedLogBuffer.MaxOverflowedLogSize,
|
||||
logBufferToFlush->OverflowedLogSize);
|
||||
|
||||
//
|
||||
// Finally, clear the previously active buffer.
|
||||
//
|
||||
logBufferToFlush->NextLogOffset = 0;
|
||||
logBufferToFlush->OverflowedLogSize = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief The entry point of the flush buffer thread. Flushes logs at interval.
|
||||
|
||||
@param[in] StartContext - The logger instance.
|
||||
*/
|
||||
LOGGER_PAGED
|
||||
static
|
||||
_Function_class_(KSTART_ROUTINE)
|
||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||
VOID
|
||||
LogFlushThread (
|
||||
_In_ VOID* StartContext
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
LOGGER_CONTEXT* logger;
|
||||
LARGE_INTEGER interval;
|
||||
|
||||
PAGED_CODE()
|
||||
|
||||
logger = (LOGGER_CONTEXT*)StartContext;
|
||||
interval.QuadPart = -(10000ll * logger->FlushIntervalInMs);
|
||||
|
||||
do
|
||||
{
|
||||
//
|
||||
// Flush log buffer with interval, or exit when it is requested.
|
||||
//
|
||||
status = KeWaitForSingleObject(&logger->ThreadExitEvent,
|
||||
Executive,
|
||||
KernelMode,
|
||||
FALSE,
|
||||
&interval);
|
||||
FlushDebugLogEntries(logger);
|
||||
} while (status == STATUS_TIMEOUT);
|
||||
|
||||
//
|
||||
// It is probably a programming error if non STATUS_SUCCESS is returned. Let
|
||||
// us catch that.
|
||||
//
|
||||
MV_ASSERT(status == STATUS_SUCCESS);
|
||||
PsTerminateSystemThread(status);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Initializes paired log buffers.
|
||||
|
||||
@param[in] BufferSize - The size of each buffer to allocate.
|
||||
|
||||
@param[out] PairedLogBuffer - The pointer to the paired log buffers.
|
||||
|
||||
@return STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
||||
*/
|
||||
LOGGER_INIT
|
||||
static
|
||||
_Must_inspect_result_
|
||||
NTSTATUS
|
||||
InitializePairedLogBuffer (
|
||||
_In_ SIZE_T BufferSize,
|
||||
_Out_ PAIRED_DEBUG_LOG_BUFFER* PairedLogBuffer
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DEBUG_LOG_ENTRY* logEntries1;
|
||||
DEBUG_LOG_ENTRY* logEntries2;
|
||||
|
||||
RtlZeroMemory(PairedLogBuffer, sizeof(*PairedLogBuffer));
|
||||
|
||||
//
|
||||
// Create paired log buffer.
|
||||
//
|
||||
logEntries1 = ExAllocatePoolWithTag(NonPagedPool, BufferSize, LOGGER_POOL_TAG);
|
||||
logEntries2 = ExAllocatePoolWithTag(NonPagedPool, BufferSize, LOGGER_POOL_TAG);
|
||||
if ((logEntries1 == NULL) || (logEntries2 == NULL))
|
||||
{
|
||||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize buffer variables, and mark the paired buffer as valid. This
|
||||
// lets the debug print callback use this paired buffer.
|
||||
//
|
||||
PairedLogBuffer->LogBuffers[0].LogEntries = logEntries1;
|
||||
PairedLogBuffer->LogBuffers[1].LogEntries = logEntries2;
|
||||
PairedLogBuffer->ActiveLogBuffer = &PairedLogBuffer->LogBuffers[0];
|
||||
PairedLogBuffer->InactiveLogBuffer = &PairedLogBuffer->LogBuffers[1];
|
||||
PairedLogBuffer->BufferSize = BufferSize;
|
||||
PairedLogBuffer->BufferValid = TRUE;
|
||||
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
Exit:
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
if (logEntries2 != NULL)
|
||||
{
|
||||
ExFreePoolWithTag(logEntries2, LOGGER_POOL_TAG);
|
||||
}
|
||||
if (logEntries1 != NULL)
|
||||
{
|
||||
ExFreePoolWithTag(logEntries1, LOGGER_POOL_TAG);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Cleans up paired log buffers.
|
||||
|
||||
@param[in,out] PairedLogBuffer - The pointer to the paired log buffers to
|
||||
clean up.
|
||||
*/
|
||||
static
|
||||
VOID
|
||||
CleanupPairedLogBuffer (
|
||||
_Inout_ PAIRED_DEBUG_LOG_BUFFER* PairedLogBuffer
|
||||
)
|
||||
{
|
||||
ExFreePoolWithTag(PairedLogBuffer->ActiveLogBuffer->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
|
||||
_Use_decl_annotations_
|
||||
NTSTATUS
|
||||
InitializeLogger (
|
||||
CONST LOGGER_CONFIGURATION* Configuration
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
LOGGER_CONTEXT* logger;
|
||||
HANDLE fileHandle;
|
||||
HANDLE threadHandle;
|
||||
|
||||
PAGED_CODE()
|
||||
|
||||
MV_ASSERT(g_Logger == NULL);
|
||||
|
||||
logger = NULL;
|
||||
fileHandle = NULL;
|
||||
|
||||
//
|
||||
// Return the empty logger without any initialization if LogLevelNone is
|
||||
// specified.
|
||||
//
|
||||
if (Configuration->Level == LogLevelNone)
|
||||
{
|
||||
g_Logger = &k_EmptyLogger;
|
||||
status = STATUS_SUCCESS;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
MV_ASSERT(Configuration->BufferSize != 0);
|
||||
|
||||
//
|
||||
// Open the log file handle if requested.
|
||||
//
|
||||
if (Configuration->FilePath != NULL)
|
||||
{
|
||||
UNICODE_STRING filePath;
|
||||
OBJECT_ATTRIBUTES objectAttributes;
|
||||
IO_STATUS_BLOCK ioStatusBlock;
|
||||
|
||||
RtlInitUnicodeString(&filePath, Configuration->FilePath);
|
||||
InitializeObjectAttributes(&objectAttributes,
|
||||
&filePath,
|
||||
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
|
||||
NULL,
|
||||
NULL)
|
||||
status = ZwCreateFile(&fileHandle,
|
||||
FILE_APPEND_DATA | SYNCHRONIZE,
|
||||
&objectAttributes,
|
||||
&ioStatusBlock,
|
||||
NULL,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
FILE_SHARE_READ,
|
||||
FILE_OPEN_IF,
|
||||
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
|
||||
NULL,
|
||||
0);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
|
||||
DPFLTR_ERROR_LEVEL,
|
||||
"ZwCreateFile failed : %08x\n",
|
||||
status);
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Create the logger instance.
|
||||
//
|
||||
#pragma prefast(suppress: __WARNING_MEMORY_LEAK, "Ownership taken on success.")
|
||||
logger = ExAllocatePoolWithTag(NonPagedPool, sizeof(*logger), LOGGER_POOL_TAG);
|
||||
if (logger == NULL)
|
||||
{
|
||||
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
|
||||
DPFLTR_ERROR_LEVEL,
|
||||
"Memory allocation failed : %Iu\n",
|
||||
sizeof(*logger));
|
||||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto Exit;
|
||||
}
|
||||
RtlZeroMemory(logger, sizeof(*logger));
|
||||
|
||||
//
|
||||
// Initialize the created logger instance.
|
||||
//
|
||||
status = InitializePairedLogBuffer(Configuration->BufferSize,
|
||||
&logger->PairedLogBuffer);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
|
||||
DPFLTR_ERROR_LEVEL,
|
||||
"InitializePairedLogBuffer failed : %08x\n",
|
||||
status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
logger->Level = Configuration->Level;
|
||||
logger->Flags.AsUInt32 = Configuration->Flags.AsUInt32;
|
||||
logger->FlushIntervalInMs = Configuration->FlushIntervalInMs;
|
||||
logger->LogFileHandle = fileHandle;
|
||||
KeInitializeEvent(&logger->ThreadExitEvent, SynchronizationEvent, FALSE);
|
||||
|
||||
//
|
||||
// Create the log flush thread for this logger.
|
||||
//
|
||||
status = PsCreateSystemThread(&threadHandle,
|
||||
THREAD_ALL_ACCESS,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
LogFlushThread,
|
||||
logger);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
|
||||
DPFLTR_ERROR_LEVEL,
|
||||
"PsCreateSystemThread failed : %08x\n",
|
||||
status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Get the created thread object. This code should not fail.
|
||||
//
|
||||
status = ObReferenceObjectByHandle(threadHandle,
|
||||
THREAD_ALL_ACCESS,
|
||||
*PsThreadType,
|
||||
KernelMode,
|
||||
(VOID**)&logger->FlushBufferThread,
|
||||
NULL);
|
||||
MV_ASSERT(NT_SUCCESS(status));
|
||||
MV_VERIFY(NT_SUCCESS(ZwClose(threadHandle)));
|
||||
|
||||
//
|
||||
// We are good. Return the handle.
|
||||
//
|
||||
g_Logger = logger;
|
||||
|
||||
|
||||
Exit:
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
if (fileHandle != NULL)
|
||||
{
|
||||
MV_VERIFY(ZwClose(fileHandle));
|
||||
}
|
||||
if (logger != NULL)
|
||||
{
|
||||
if (logger->PairedLogBuffer.BufferValid != FALSE)
|
||||
{
|
||||
CleanupPairedLogBuffer(&logger->PairedLogBuffer);
|
||||
}
|
||||
ExFreePoolWithTag(logger, LOGGER_POOL_TAG);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Clean up the logger.
|
||||
*/
|
||||
LOGGER_PAGED
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
CleanupLogger (
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
LOGGER_CONTEXT* logger;
|
||||
SIZE_T maxOverflowedLogSize;
|
||||
|
||||
PAGED_CODE()
|
||||
|
||||
MV_ASSERT(g_Logger != NULL);
|
||||
|
||||
logger = g_Logger;
|
||||
|
||||
//
|
||||
// No need to do anything if the logger is an empty logger.
|
||||
//
|
||||
if (logger == &k_EmptyLogger)
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Signal the event to exit the flush buffer thread, and wait for termination.
|
||||
//
|
||||
(VOID)KeSetEvent(&logger->ThreadExitEvent, IO_NO_INCREMENT, FALSE);
|
||||
status = KeWaitForSingleObject(logger->FlushBufferThread,
|
||||
Executive,
|
||||
KernelMode,
|
||||
FALSE,
|
||||
NULL);
|
||||
MV_ASSERT(status == STATUS_SUCCESS);
|
||||
ObDereferenceObject(logger->FlushBufferThread);
|
||||
|
||||
maxOverflowedLogSize = logger->PairedLogBuffer.MaxOverflowedLogSize;
|
||||
|
||||
//
|
||||
// No one should be touching the log file now. Close it.
|
||||
//
|
||||
if (logger->LogFileHandle != NULL)
|
||||
{
|
||||
MV_VERIFY(NT_SUCCESS(ZwClose(logger->LogFileHandle)));
|
||||
}
|
||||
|
||||
//
|
||||
// Free resources and the logger itself.
|
||||
//
|
||||
CleanupPairedLogBuffer(&logger->PairedLogBuffer);
|
||||
ExFreePoolWithTag(logger, LOGGER_POOL_TAG);
|
||||
|
||||
if (maxOverflowedLogSize != 0)
|
||||
{
|
||||
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
|
||||
DPFLTR_ERROR_LEVEL,
|
||||
"Cleaning up the logger. Max overflowed logs during the"
|
||||
" session is %llu bytes.\n",
|
||||
maxOverflowedLogSize);
|
||||
}
|
||||
|
||||
Exit:
|
||||
g_Logger = NULL;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Buffers the debug-level message to the paired log buffer.
|
||||
|
||||
@param[in,out] Logger - The current logger instance.
|
||||
|
||||
@param[in] Level - The level of the message.
|
||||
|
||||
@param[in] FunctionName - The name of the function initiated this logging.
|
||||
|
||||
@param[in] LogMessage - The message to save.
|
||||
|
||||
@return STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
||||
*/
|
||||
static
|
||||
_Must_inspect_result_
|
||||
NTSTATUS
|
||||
BufferLog (
|
||||
_Inout_ LOGGER_CONTEXT* Logger,
|
||||
_In_ LOG_LEVEL Level,
|
||||
_In_ PCSTR FunctionName,
|
||||
_In_ PCSTR LogMessage
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
USHORT logMessageLength;
|
||||
SIZE_T logEntrySize;
|
||||
BOOLEAN lockAcquired;
|
||||
DEBUG_LOG_ENTRY* logEntry;
|
||||
LARGE_INTEGER timestamp;
|
||||
KIRQL oldIrql;
|
||||
|
||||
KeQuerySystemTime(×tamp);
|
||||
|
||||
oldIrql = 0; // Suppress compiler false positive warning.
|
||||
lockAcquired = FALSE;
|
||||
|
||||
//
|
||||
// Get the length of the message in characters.
|
||||
//
|
||||
status = RtlSizeTToUShort(strlen(LogMessage), &logMessageLength);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
logEntrySize = RTL_SIZEOF_THROUGH_FIELD(DEBUG_LOG_ENTRY, LogMessageLength) +
|
||||
logMessageLength;
|
||||
|
||||
//
|
||||
// Acquire the lock to safely modify active buffer.
|
||||
//
|
||||
oldIrql = AcquireSystemSpinLock(&Logger->PairedLogBuffer.ActiveLogBufferLock);
|
||||
lockAcquired = TRUE;
|
||||
|
||||
//
|
||||
// Bail out if a concurrent thread invalidated buffer.
|
||||
//
|
||||
if (Logger->PairedLogBuffer.BufferValid == FALSE)
|
||||
{
|
||||
status = STATUS_TOO_LATE;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// If the remaining buffer is not large enough to save this message, count
|
||||
// up the overflowed size and bail out.
|
||||
//
|
||||
if (Logger->PairedLogBuffer.ActiveLogBuffer->NextLogOffset + logEntrySize >
|
||||
Logger->PairedLogBuffer.BufferSize)
|
||||
{
|
||||
Logger->PairedLogBuffer.ActiveLogBuffer->OverflowedLogSize += logEntrySize;
|
||||
status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// There are sufficient room to save the message. Get the address to save
|
||||
// the message within active buffer. On debug build, the address should be
|
||||
// filled with 0xff, indicating no one has yet touched there.
|
||||
//
|
||||
logEntry = (DEBUG_LOG_ENTRY*)MV_ADD2PTR(
|
||||
Logger->PairedLogBuffer.ActiveLogBuffer->LogEntries,
|
||||
Logger->PairedLogBuffer.ActiveLogBuffer->NextLogOffset);
|
||||
|
||||
//
|
||||
// Save this message and update the offset to the address to save the next
|
||||
// message.
|
||||
//
|
||||
logEntry->Timestamp = timestamp;
|
||||
logEntry->Level = Level;
|
||||
logEntry->ProcessorNumber = KeGetCurrentProcessorNumberEx(NULL);
|
||||
logEntry->ClientId.UniqueProcess = PsGetCurrentProcessId();
|
||||
logEntry->ClientId.UniqueThread = PsGetCurrentThreadId();
|
||||
(VOID)RtlStringCchCopyA(logEntry->ProcessName,
|
||||
RTL_NUMBER_OF_FIELD(DEBUG_LOG_ENTRY, ProcessName),
|
||||
PsGetProcessImageFileName(PsGetCurrentProcess()));
|
||||
(VOID)RtlStringCchCopyA(logEntry->FunctionName,
|
||||
RTL_NUMBER_OF_FIELD(DEBUG_LOG_ENTRY, FunctionName),
|
||||
FunctionName);
|
||||
logEntry->LogMessageLength = logMessageLength;
|
||||
RtlCopyMemory(logEntry->LogMessage, LogMessage, logMessageLength);
|
||||
Logger->PairedLogBuffer.ActiveLogBuffer->NextLogOffset += logEntrySize;
|
||||
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
Exit:
|
||||
if (lockAcquired != FALSE)
|
||||
{
|
||||
ReleaseSystemSpinLock(&Logger->PairedLogBuffer.ActiveLogBufferLock, oldIrql);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
LogMessage (
|
||||
LOG_LEVEL Level,
|
||||
CONST CHAR* FunctionName,
|
||||
CONST CHAR* Format,
|
||||
...
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
LOGGER_CONTEXT* logger;
|
||||
va_list args;
|
||||
CHAR logMessage[400];
|
||||
|
||||
MV_ASSERT(Level != LogLevelNone);
|
||||
|
||||
logger = g_Logger;
|
||||
|
||||
//
|
||||
// Skip if the log is more verbose than the requested level.
|
||||
//
|
||||
if (logger->Level < Level)
|
||||
{
|
||||
status = STATUS_SUCCESS;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Build a log message string and buffer it.
|
||||
//
|
||||
va_start(args, Format);
|
||||
status = RtlStringCchVPrintfA(logMessage,
|
||||
RTL_NUMBER_OF(logMessage),
|
||||
Format,
|
||||
args);
|
||||
va_end(args);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
MV_ASSERT(FALSE);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
status = BufferLog(logger, Level, FunctionName, logMessage);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Exit:
|
||||
return;
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
LogEarlyErrorMessage (
|
||||
CONST CHAR* Format,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, Format);
|
||||
(VOID)vDbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, Format, args);
|
||||
va_end(args);
|
||||
}
|
||||
82
Sources/Platform/Windows/WinLogger.h
Normal file
82
Sources/Platform/Windows/WinLogger.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*!
|
||||
@file WinLogger.h
|
||||
|
||||
@brief Windows specific implementation of the logger.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
#include "../../Logger.h"
|
||||
|
||||
//
|
||||
// The handy macros to specify in which section the code should be placed.
|
||||
//
|
||||
#define LOGGER_INIT __declspec(code_seg("INIT"))
|
||||
#define LOGGER_PAGED __declspec(code_seg("PAGE"))
|
||||
|
||||
//
|
||||
// Extended configuration flags.
|
||||
//
|
||||
typedef union _LOGGER_CONFIGURATION_FLAGS
|
||||
{
|
||||
struct
|
||||
{
|
||||
UINT32 EnableTimestamp : 1;
|
||||
UINT32 EnableLevel : 1;
|
||||
UINT32 EnableProcessorNumber : 1;
|
||||
UINT32 EnablePidTid : 1;
|
||||
UINT32 EnableProcessName : 1;
|
||||
UINT32 EnableFunctionName : 1;
|
||||
} u;
|
||||
|
||||
UINT32 AsUInt32;
|
||||
} LOGGER_CONFIGURATION_FLAGS;
|
||||
|
||||
//
|
||||
// The configurations of the logger to initialize.
|
||||
//
|
||||
typedef struct _LOGGER_CONFIGURATION
|
||||
{
|
||||
//
|
||||
// The maximum level of the log this logger will log. For example, the
|
||||
// information-level logs are discarded when LogLevelWarning is specified.
|
||||
// If LogLevelNone is set, the logger is disabled and none of logs are logged.
|
||||
//
|
||||
LOG_LEVEL Level;
|
||||
|
||||
//
|
||||
// Extended configuration flags.
|
||||
//
|
||||
LOGGER_CONFIGURATION_FLAGS Flags;
|
||||
|
||||
//
|
||||
// An interval to flush logs saved into log message buffer.
|
||||
//
|
||||
UINT32 FlushIntervalInMs;
|
||||
|
||||
//
|
||||
// A size of log message buffer. The logger internally allocates two buffers
|
||||
// with this size.
|
||||
//
|
||||
SIZE_T BufferSize;
|
||||
|
||||
//
|
||||
// The path to the file to save logs. The logger do not save logs into a file
|
||||
// when NULL is specified.
|
||||
//
|
||||
PCWSTR FilePath;
|
||||
} LOGGER_CONFIGURATION;
|
||||
|
||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||
_Must_inspect_result_
|
||||
NTSTATUS
|
||||
InitializeLogger (
|
||||
_In_ CONST LOGGER_CONFIGURATION* Configuration
|
||||
);
|
||||
|
||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||
VOID
|
||||
CleanupLogger (
|
||||
);
|
||||
437
Sources/Platform/Windows/WinPlatform.c
Normal file
437
Sources/Platform/Windows/WinPlatform.c
Normal file
@@ -0,0 +1,437 @@
|
||||
/*!
|
||||
@file WinPlatform.c
|
||||
|
||||
@brief Windows specific platform API.
|
||||
|
||||
@details Some of API in this module can be called from the host. See the
|
||||
description of each API.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#include "WinPlatform.h"
|
||||
#include "WinLogger.h"
|
||||
#include "../../MiniVisor.h"
|
||||
|
||||
MV_SECTION_INIT DRIVER_INITIALIZE DriverEntry;
|
||||
|
||||
MV_SECTION_PAGED static DRIVER_UNLOAD DriverUnload;
|
||||
|
||||
//
|
||||
// The pool tag value used across the project.
|
||||
//
|
||||
#define MV_POOL_TAG ((UINT32)'vniM')
|
||||
|
||||
//
|
||||
// Maps conversion between MV_STATUS and NTSTATUS.
|
||||
//
|
||||
typedef struct _STATUS_MAPPING
|
||||
{
|
||||
MV_STATUS MvStatus;
|
||||
NTSTATUS NtStatus;
|
||||
} STATUS_MAPPING;
|
||||
|
||||
static CONST STATUS_MAPPING k_StatusMapping[] =
|
||||
{
|
||||
{ MV_STATUS_SUCCESS, STATUS_SUCCESS, },
|
||||
{ MV_STATUS_UNSUCCESSFUL, STATUS_UNSUCCESSFUL, },
|
||||
{ MV_STATUS_ACCESS_DENIED, STATUS_ACCESS_DENIED, },
|
||||
{ MV_STATUS_INSUFFICIENT_RESOURCES, STATUS_INSUFFICIENT_RESOURCES, },
|
||||
{ MV_STATUS_HV_OPERATION_FAILED, STATUS_HV_OPERATION_FAILED, },
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief Converts MV_STATUS to NTSTATUS.
|
||||
|
||||
@param[in] Status - The MV_STATUS to convert from.
|
||||
|
||||
@return The converted NTSTATUS.
|
||||
*/
|
||||
static
|
||||
NTSTATUS
|
||||
ConvertMvToNtStatus (
|
||||
_In_ MV_STATUS Status
|
||||
)
|
||||
{
|
||||
for (UINT32 i = 0; i < RTL_NUMBER_OF(k_StatusMapping); ++i)
|
||||
{
|
||||
if (Status == k_StatusMapping[i].MvStatus)
|
||||
{
|
||||
return k_StatusMapping[i].NtStatus;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Update the mapping when this assert hits.
|
||||
//
|
||||
MV_ASSERT(FALSE);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Converts NTSTATUS to MV_STATUS.
|
||||
|
||||
@param[in] Status - The NTSTATUS to convert from.
|
||||
|
||||
@return The converted MV_STATUS.
|
||||
*/
|
||||
static
|
||||
MV_STATUS
|
||||
ConvertNtToMvStatus (
|
||||
_In_ NTSTATUS Status
|
||||
)
|
||||
{
|
||||
for (UINT32 i = 0; i < RTL_NUMBER_OF(k_StatusMapping); ++i)
|
||||
{
|
||||
if (Status == k_StatusMapping[i].NtStatus)
|
||||
{
|
||||
return k_StatusMapping[i].MvStatus;
|
||||
}
|
||||
}
|
||||
return MV_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief The platform specific module entry point.
|
||||
|
||||
@param[in] DriverObject - The driver's driver object.
|
||||
|
||||
@param[in] RegistryPath - A path to the driver's registry key.
|
||||
|
||||
@return STATUS_SUCCESS on success; otherwise, an appropriate error code.
|
||||
*/
|
||||
MV_SECTION_INIT
|
||||
_Use_decl_annotations_
|
||||
NTSTATUS
|
||||
DriverEntry (
|
||||
PDRIVER_OBJECT DriverObject,
|
||||
PUNICODE_STRING RegistryPath
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistryPath);
|
||||
|
||||
DriverObject->DriverUnload = DriverUnload;
|
||||
|
||||
//
|
||||
// Opts-in no-execute (NX) non-paged pool for security when available.
|
||||
//
|
||||
// By defining POOL_NX_OPTIN as 1 and calling this function, non-paged pool
|
||||
// allocation by the ExAllocatePool family with the NonPagedPool flag
|
||||
// automatically allocates NX non-paged pool on Windows 8 and later versions
|
||||
// of Windows, while on Windows 7 where NX non-paged pool is unsupported,
|
||||
// executable non-paged pool is returned as usual. The merit of this is that
|
||||
// the NonPagedPoolNx flag does not have to be used. Since the flag is
|
||||
// unsupported on Windows 7, being able to stick with the NonPagedPool flag
|
||||
// help keep code concise.
|
||||
//
|
||||
ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
|
||||
|
||||
//
|
||||
// Start cross-platform initialization.
|
||||
//
|
||||
return ConvertMvToNtStatus(InitializeMiniVisor());
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief The platform specific module unload callback.
|
||||
|
||||
@param[in] DriverObject - The driver's driver object.
|
||||
*/
|
||||
MV_SECTION_PAGED
|
||||
_Use_decl_annotations_
|
||||
static
|
||||
VOID
|
||||
DriverUnload (
|
||||
PDRIVER_OBJECT DriverObject
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(DriverObject);
|
||||
|
||||
PAGED_CODE()
|
||||
|
||||
//
|
||||
// Start cross-platform clean up.
|
||||
//
|
||||
CleanupMiniVisor();
|
||||
}
|
||||
|
||||
MV_SECTION_INIT
|
||||
_Use_decl_annotations_
|
||||
MV_STATUS
|
||||
InitializePlatform (
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
LOGGER_CONFIGURATION loggerConfig;
|
||||
|
||||
PAGED_CODE()
|
||||
|
||||
//
|
||||
// Initialize in-house logger. Enable all flags.
|
||||
//
|
||||
loggerConfig.Level = LogLevelDebug;
|
||||
loggerConfig.Flags.AsUInt32 = MAXUINT32;
|
||||
loggerConfig.FlushIntervalInMs = 500;
|
||||
loggerConfig.BufferSize = (SIZE_T)(32 * PAGE_SIZE) * GetActiveProcessorCount();
|
||||
loggerConfig.FilePath = L"\\SystemRoot\\Minivisor.log";
|
||||
status = InitializeLogger(&loggerConfig);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
LOG_EARLY_ERROR("InitializeLogger failed : %08x", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Exit:
|
||||
return ConvertNtToMvStatus(status);
|
||||
}
|
||||
|
||||
MV_SECTION_PAGED
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
CleanupPlatform (
|
||||
)
|
||||
{
|
||||
PAGED_CODE()
|
||||
|
||||
CleanupLogger();
|
||||
}
|
||||
|
||||
MV_SECTION_PAGED
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
Sleep (
|
||||
UINT64 Milliseconds
|
||||
)
|
||||
{
|
||||
LARGE_INTEGER interval;
|
||||
|
||||
PAGED_CODE()
|
||||
|
||||
interval.QuadPart = -(LONGLONG)(10000 * Milliseconds);
|
||||
(VOID)KeDelayExecutionThread(KernelMode, FALSE, &interval);
|
||||
}
|
||||
|
||||
UINT32
|
||||
GetActiveProcessorCount (
|
||||
)
|
||||
{
|
||||
return KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
|
||||
}
|
||||
|
||||
UINT32
|
||||
GetCurrentProcessorNumber (
|
||||
)
|
||||
{
|
||||
return KeGetCurrentProcessorNumberEx(NULL);
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
UINT64
|
||||
GetPhysicalAddress (
|
||||
VOID* VirualAddress
|
||||
)
|
||||
{
|
||||
return (UINT64)MmGetPhysicalAddress(VirualAddress).QuadPart;
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
VOID*
|
||||
GetVirtualAddress (
|
||||
UINT64 PhysicalAddress
|
||||
)
|
||||
{
|
||||
PHYSICAL_ADDRESS pa;
|
||||
|
||||
pa.QuadPart = (LONGLONG)PhysicalAddress;
|
||||
return MmGetVirtualForPhysical(pa);
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
VOID*
|
||||
AllocateSystemMemory (
|
||||
UINT64 PageCount
|
||||
)
|
||||
{
|
||||
VOID* pages;
|
||||
SIZE_T allocationBytes;
|
||||
|
||||
MV_ASSERT(PageCount > 0);
|
||||
|
||||
allocationBytes = (SIZE_T)PageCount * PAGE_SIZE;
|
||||
|
||||
//
|
||||
// This is bogus.
|
||||
// "The current function is permitted to run at an IRQ level above the
|
||||
// maximum permitted for 'ExAllocatePoolWithTag' (1)."
|
||||
//
|
||||
#pragma warning(suppress: 28118)
|
||||
pages = ExAllocatePoolWithTag(NonPagedPool, allocationBytes, MV_POOL_TAG);
|
||||
if (pages == NULL)
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
RtlZeroMemory(pages, allocationBytes);
|
||||
|
||||
Exit:
|
||||
return pages;
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
FreeSystemMemory (
|
||||
VOID* Pages,
|
||||
UINT64 PageCount
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(PageCount);
|
||||
|
||||
ExFreePoolWithTag(Pages, MV_POOL_TAG);
|
||||
}
|
||||
|
||||
MV_SECTION_PAGED
|
||||
_Use_decl_annotations_
|
||||
VOID*
|
||||
ReserveVirtualAddress (
|
||||
UINT64 PageCount
|
||||
)
|
||||
{
|
||||
PAGED_CODE()
|
||||
|
||||
return MmAllocateMappingAddress(PageCount * PAGE_SIZE, MV_POOL_TAG);
|
||||
}
|
||||
|
||||
MV_SECTION_PAGED
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
FreeReservedVirtualAddress (
|
||||
VOID* Pages,
|
||||
UINT64 PageCount
|
||||
)
|
||||
{
|
||||
PAGED_CODE()
|
||||
|
||||
UNREFERENCED_PARAMETER(PageCount);
|
||||
|
||||
MmFreeMappingAddress(Pages, MV_POOL_TAG);
|
||||
}
|
||||
|
||||
MV_SECTION_PAGED
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
RunOnAllProcessors (
|
||||
USER_PASSIVE_CALLBACK* Callback,
|
||||
VOID* Context
|
||||
)
|
||||
{
|
||||
UINT32 numberOfProcessors;
|
||||
|
||||
PAGED_CODE()
|
||||
|
||||
numberOfProcessors = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
|
||||
for (UINT32 index = 0; index < numberOfProcessors; ++index)
|
||||
{
|
||||
NTSTATUS status;
|
||||
PROCESSOR_NUMBER processorNumber;
|
||||
GROUP_AFFINITY newAffinity, prevAffinity;
|
||||
|
||||
status = KeGetProcessorNumberFromIndex(index, &processorNumber);
|
||||
if (NT_ERROR(status))
|
||||
{
|
||||
MV_ASSERT(FALSE);
|
||||
continue;
|
||||
}
|
||||
|
||||
RtlZeroMemory(&newAffinity, sizeof(newAffinity));
|
||||
newAffinity.Group = processorNumber.Group;
|
||||
newAffinity.Mask = 1ull << processorNumber.Number;
|
||||
KeSetSystemGroupAffinityThread(&newAffinity, &prevAffinity);
|
||||
Callback(Context);
|
||||
KeRevertToUserGroupAffinityThread(&prevAffinity);
|
||||
}
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
InitializeSystemSpinLock (
|
||||
SPIN_LOCK* SpinLock
|
||||
)
|
||||
{
|
||||
*SpinLock = SpinLockReleased;
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
UINT8
|
||||
AcquireSystemSpinLock (
|
||||
SPIN_LOCK* SpinLock
|
||||
)
|
||||
{
|
||||
KIRQL oldIrql;
|
||||
|
||||
//
|
||||
// Raise IRQL if the current is lower than DISPATCH_LEVEL.
|
||||
//
|
||||
oldIrql = KeGetCurrentIrql();
|
||||
if (oldIrql < DISPATCH_LEVEL)
|
||||
{
|
||||
oldIrql = KeRaiseIrqlToDpcLevel();
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
//
|
||||
// Attempt to acquire the lock.
|
||||
//
|
||||
if (InterlockedBitTestAndSet64(SpinLock, 0) == SpinLockReleased)
|
||||
{
|
||||
//
|
||||
// Acquired the lock.
|
||||
//
|
||||
MV_ASSERT(*SpinLock == SpinLockAcquired);
|
||||
_Analysis_assume_lock_acquired_(*SpinLock);
|
||||
break;
|
||||
}
|
||||
|
||||
while (*SpinLock == SpinLockAcquired)
|
||||
{
|
||||
//
|
||||
// Someone already acquired it. Spin unless the some of release it.
|
||||
//
|
||||
YieldProcessor();
|
||||
}
|
||||
}
|
||||
|
||||
return oldIrql;
|
||||
}
|
||||
|
||||
//
|
||||
// "The IRQL in 'PreviousContext' was never restored."
|
||||
//
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: __WARNING_IRQL_NOT_USED)
|
||||
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
ReleaseSystemSpinLock (
|
||||
SPIN_LOCK* SpinLock,
|
||||
UINT8 PreviousContext
|
||||
)
|
||||
{
|
||||
//
|
||||
// Prevent CPU and compiler re-ordering, and make sure any operations are
|
||||
// done before releasing the spin lock.
|
||||
//
|
||||
MemoryBarrier();
|
||||
*SpinLock = SpinLockReleased;
|
||||
_Analysis_assume_lock_released_(*SpinLock);
|
||||
|
||||
//
|
||||
// Lowers IRQL if necessary.
|
||||
//
|
||||
if (PreviousContext < DISPATCH_LEVEL)
|
||||
{
|
||||
KeLowerIrql(PreviousContext);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
||||
11
Sources/Platform/Windows/WinPlatform.h
Normal file
11
Sources/Platform/Windows/WinPlatform.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
@file WinPlatform.h
|
||||
|
||||
@brief Windows specific platform API.
|
||||
|
||||
@author Satoshi Tanda
|
||||
|
||||
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
#include "../../Platform.h"
|
||||
Reference in New Issue
Block a user