Publish the files

This commit is contained in:
Satoshi Tanda
2020-02-22 13:54:50 -08:00
parent 83bd8d5f19
commit 791486327d
79 changed files with 36078 additions and 47 deletions

View 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

View 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
);

View 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;
}

View 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
);

View 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)

View 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);
}

View 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"

View 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);
}

View 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 (
);

View 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);
}

View 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"