[FREELDR] Enable UEFI boot for x86 and amd64 (#5267)

Co-authored-by: Stanislav Motylkov <x86corez@gmail.com>
Co-authored-by: Hermès BÉLUSCA - MAÏTO <hermes.belusca-maito@reactos.org>

- Allow to boot NT kernel on UEFI systems with our 2 primary supported architectures
- Implement remaining code needed to pass execution to x86 and amd64 kernels

CORE-11954
This commit is contained in:
Justin Miller 2023-10-11 12:45:08 -07:00 committed by GitHub
parent a6b281c228
commit ff3dadf89d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 435 additions and 65 deletions

View File

@ -0,0 +1,85 @@
/*
* PROJECT: FreeLoader UEFI Support
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: x64 assembly UEFI escape helper functions
* COPYRIGHT: Copyright 2023 Justin Miller <justinmiller100@gmail.com>
*/
#include <asm.inc>
#include <arch/pc/x86common.h>
#include <arch/pc/pcbios.h>
EXTERN UefiServiceStack:QWORD
EXTERN BasicStack:QWORD
EXTERN ExecuteLoaderCleanly:PROC
EXTERN UefiExitBootServices:PROC
.code64
// void _exituefi(VOID)
PUBLIC _exituefi
_exituefi:
/* Save non-volatile registers */
push rbp
push rsi
push rdi
push rbx
/* Save the old stack */
mov rbx, rsp
/* Load the new stack */
xor rbp, rbp
mov rsp, qword ptr UefiServiceStack[rip]
/* Call the entry routine, passing the parameters */
mov rax, UefiExitBootServices[rip]
call rax
/* Retore old stack */
mov rsp, rbx
/* Retore non-volatiles */
pop rbx
pop rdi
pop rsi
pop rbp
#ifdef _USE_ML
lgdt fword ptr [_gdtptr]
#else
lgdt cs:[_gdtptr][rip] /* GAS isn't my friend - avoid letting it generate absolute addressing */
#endif
/* All done */
ret
// void _changestack(VOID)
PUBLIC _changestack
_changestack:
mov rax, rsp
mov rsp, BasicStack[rip]
push rax
call ExecuteLoaderCleanly[rip]
ret
.align 8
gdt:
.word HEX(0000), HEX(0000), HEX(0000), HEX(0000) /* 00: NULL descriptor */
.word HEX(0000), HEX(0000), HEX(0000), HEX(0000) /* 08: */
.word HEX(0000), HEX(0000), HEX(9800), HEX(0020) /* 10: long mode CS */
.word HEX(FFFF), HEX(0000), HEX(F300), HEX(00CF) /* 18: long mode DS */
.word HEX(FFFF), HEX(0000), HEX(9E00), HEX(0000) /* 20: 16-bit real mode CS */
.word HEX(FFFF), HEX(0000), HEX(9200), HEX(0000) /* 28: 16-bit real mode DS */
.word HEX(FFFF), HEX(0000), HEX(9B00), HEX(00CF) /* 30: compat mode CS */
/* GDT table pointer */
_gdtptr:
.word HEX(37) /* Limit */
#ifdef _USE_ML
.quad gdt /* Base Address */
#else
.quad gdt, 0 /* Base Address */
#endif
END

View File

@ -0,0 +1,109 @@
/*
* PROJECT: FreeLoader UEFI Support
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: i386 assembly UEFI escape helper functions
* COPYRIGHT: Copyright 2023 Justin Miller <justinmiller100@gmail.com>
*/
#include <asm.inc>
#include <arch/pc/x86common.h>
#include <arch/pc/pcbios.h>
PUBLIC _gdtptr
PUBLIC _i386idtptr
PUBLIC __changestack
EXTERN _UefiServiceStack:DWORD
EXTERN _i386Idt:DWORD
EXTERN _ExecuteLoaderCleanly:PROC
EXTERN _UefiExitBootServices:PROC
EXTERN _BasicStack:DWORD
.code32
// void __exituefi(VOID)
PUBLIC __exituefi
__exituefi:
push ebp
push esi
push edi
push ebx
/* Save the old stack */
mov ebx, esp
/* Load the new stack */
xor ebp, ebp
mov esp, _UefiServiceStack
/* Call the entry routine, passing the parameters */
call _UefiExitBootServices
/* Retore old stack */
mov esp, ebx
/* Retore non-volatiles */
pop ebx
pop edi
pop esi
pop ebp
#ifdef _USE_ML
lidt fword ptr ds:[_i386idtptr]
lgdt fword ptr [_gdtptr]
#else
lgdt cs:[_gdtptr]
lidt _i386idtptr
#endif
/* All done */
ret
// void __reloadsegment(VOID)
PUBLIC __changestack
__changestack:
mov eax, esp
mov esp, _BasicStack
push eax
call _ExecuteLoaderCleanly
ret
.align 4 /* force 4-byte alignment */
gdt:
/* NULL Descriptor */
.word HEX(0000)
.word HEX(0000)
.word HEX(0000)
.word HEX(0000)
/* 32-bit flat CS */
.word HEX(FFFF)
.word HEX(0000)
.word HEX(9A00)
.word HEX(00CF)
/* 32-bit flat DS */
.word HEX(FFFF)
.word HEX(0000)
.word HEX(9200)
.word HEX(00CF)
/* 16-bit real mode CS */
.word HEX(FFFF)
.word HEX(0000)
.word HEX(9E00)
.word HEX(0000)
/* 16-bit real mode DS */
.word HEX(FFFF)
.word HEX(0000)
.word HEX(9200)
.word HEX(0000)
/* GDT table pointer */
_gdtptr:
.word HEX(27) /* Limit */
.long gdt, 0 /* Base Address */
_i386idtptr:
.word 255 /* Limit */
.long _i386Idt /* Base Address */
END

View File

@ -43,12 +43,6 @@ UefiGetExtendedBIOSData(PULONG ExtendedBIOSDataArea,
}
PCONFIGURATION_COMPONENT_DATA
UefiHwDetect(VOID)
{
return 0;
}
VOID
UefiPcBeep(VOID)
{

View File

@ -138,6 +138,5 @@ UefiConsGetCh(VOID)
/* UEFI will stack input requests, we have to clear it */
Key.UnicodeChar = 0;
Key.ScanCode = 0;
GlobalSystemTable->ConIn->Reset(GlobalSystemTable->ConIn, FALSE);
return KeyOutput;
}

View File

@ -0,0 +1,149 @@
/*
* PROJECT: FreeLoader UEFI Support
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Hardware detection routines
* COPYRIGHT: Copyright 2022 Justin Miller <justinmiller100@gmail.com>
*/
/* INCLUDES ******************************************************************/
#include <uefildr.h>
#include <debug.h>
DBG_DEFAULT_CHANNEL(WARNING);
/* GLOBALS *******************************************************************/
extern EFI_SYSTEM_TABLE * GlobalSystemTable;
extern EFI_HANDLE GlobalImageHandle;
extern UCHAR PcBiosDiskCount;
extern EFI_MEMORY_DESCRIPTOR* EfiMemoryMap;
extern UINT32 FreeldrDescCount;
BOOLEAN AcpiPresent = FALSE;
/* FUNCTIONS *****************************************************************/
static
PRSDP_DESCRIPTOR
FindAcpiBios(VOID)
{
UINTN i;
RSDP_DESCRIPTOR* rsdp = NULL;
EFI_GUID acpi2_guid = EFI_ACPI_20_TABLE_GUID;
for (i = 0; i < GlobalSystemTable->NumberOfTableEntries; i++)
{
if (!memcmp(&GlobalSystemTable->ConfigurationTable[i].VendorGuid,
&acpi2_guid, sizeof(acpi2_guid)))
{
rsdp = (RSDP_DESCRIPTOR*)GlobalSystemTable->ConfigurationTable[i].VendorTable;
break;
}
}
return rsdp;
}
VOID
DetectAcpiBios(PCONFIGURATION_COMPONENT_DATA SystemKey, ULONG *BusNumber)
{
PCONFIGURATION_COMPONENT_DATA BiosKey;
PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
PRSDP_DESCRIPTOR Rsdp;
PACPI_BIOS_DATA AcpiBiosData;
ULONG TableSize;
Rsdp = FindAcpiBios();
if (Rsdp)
{
/* Set up the flag in the loader block */
AcpiPresent = TRUE;
/* Calculate the table size */
TableSize = FreeldrDescCount * sizeof(BIOS_MEMORY_MAP) +
sizeof(ACPI_BIOS_DATA) - sizeof(BIOS_MEMORY_MAP);
/* Set 'Configuration Data' value */
PartialResourceList = FrLdrHeapAlloc(sizeof(CM_PARTIAL_RESOURCE_LIST) +
TableSize, TAG_HW_RESOURCE_LIST);
if (PartialResourceList == NULL)
{
ERR("Failed to allocate resource descriptor\n");
return;
}
RtlZeroMemory(PartialResourceList, sizeof(CM_PARTIAL_RESOURCE_LIST) + TableSize);
PartialResourceList->Version = 0;
PartialResourceList->Revision = 0;
PartialResourceList->Count = 1;
PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
PartialDescriptor->Type = CmResourceTypeDeviceSpecific;
PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
PartialDescriptor->u.DeviceSpecificData.DataSize = TableSize;
/* Fill the table */
AcpiBiosData = (PACPI_BIOS_DATA)&PartialResourceList->PartialDescriptors[1];
if (Rsdp->revision > 0)
{
TRACE("ACPI >1.0, using XSDT address\n");
AcpiBiosData->RSDTAddress.QuadPart = Rsdp->xsdt_physical_address;
}
else
{
TRACE("ACPI 1.0, using RSDT address\n");
AcpiBiosData->RSDTAddress.LowPart = Rsdp->rsdt_physical_address;
}
AcpiBiosData->Count = FreeldrDescCount;
memcpy(AcpiBiosData->MemoryMap, EfiMemoryMap,
FreeldrDescCount * sizeof(BIOS_MEMORY_MAP));
TRACE("RSDT %p, data size %x\n", Rsdp->rsdt_physical_address, TableSize);
/* Create new bus key */
FldrCreateComponentKey(SystemKey,
AdapterClass,
MultiFunctionAdapter,
0x0,
0x0,
0xFFFFFFFF,
"ACPI BIOS",
PartialResourceList,
sizeof(CM_PARTIAL_RESOURCE_LIST) + TableSize,
&BiosKey);
/* Increment bus number */
(*BusNumber)++;
}
}
PCONFIGURATION_COMPONENT_DATA
UefiHwDetect(VOID)
{
PCONFIGURATION_COMPONENT_DATA SystemKey;
ULONG BusNumber = 0;
TRACE("DetectHardware()\n");
/* Create the 'System' key */
#if defined(_M_IX86) || defined(_M_AMD64)
FldrCreateSystemKey(&SystemKey, "AT/AT COMPATIBLE");
#elif defined(_M_IA64)
FldrCreateSystemKey(&SystemKey, "Intel Itanium processor family");
#elif defined(_M_ARM) || defined(_M_ARM64)
FldrCreateSystemKey(&SystemKey, "ARM processor family");
#else
#error Please define a system key for your architecture
#endif
/* Detect ACPI */
DetectAcpiBios(SystemKey, &BusNumber);
TRACE("DetectHardware() Done\n");
return SystemKey;
}

View File

@ -8,11 +8,16 @@
#include <uefildr.h>
#include <debug.h>
DBG_DEFAULT_CHANNEL(WARNING);
/* GLOBALS ********************************************************************/
EFI_HANDLE GlobalImageHandle;
EFI_SYSTEM_TABLE *GlobalSystemTable;
PVOID UefiServiceStack;
PVOID BasicStack;
void _changestack(VOID);
/* FUNCTIONS ******************************************************************/
@ -25,15 +30,61 @@ EfiEntry(
GlobalImageHandle = ImageHandle;
GlobalSystemTable = SystemTable;
BootMain(NULL);
/* Needed for default settings */
CmdLineParse("");
/* Debugger pre-initialization */
DebugInit(0);
MachInit("");
/* UI pre-initialization */
if (!UiInitialize(FALSE))
{
UiMessageBoxCritical("Unable to initialize UI.");
goto Quit;
}
/* Initialize memory manager */
if (!MmInitializeMemoryManager())
{
UiMessageBoxCritical("Unable to initialize memory manager.");
goto Quit;
}
/* Initialize I/O subsystem */
FsInit();
/* 0x32000 is what UEFI defines, but we can go smaller if we want */
BasicStack = (PVOID)((ULONG_PTR)0x32000 + (ULONG_PTR)MmAllocateMemoryWithType(0x32000, LoaderOsloaderStack));
_changestack();
Quit:
/* If we reach this point, something went wrong before, therefore reboot */
Reboot();
UNREACHABLE;
return 0;
}
void
ExecuteLoaderCleanly(PVOID PreviousStack)
{
TRACE("ExecuteLoaderCleanly Entry\n");
UefiServiceStack = PreviousStack;
RunLoader();
UNREACHABLE;
}
#ifndef _M_ARM
VOID __cdecl Reboot(VOID)
{
//TODO: Replace with a true firmware reboot eventually
WARN("Something has gone wrong - halting FreeLoader\n");
for (;;)
{
NOTHING;
}
}
#endif

View File

@ -27,6 +27,7 @@ AddMemoryDescriptor(
/* GLOBALS *******************************************************************/
extern ULONG LoaderPagesSpanned;
extern EFI_SYSTEM_TABLE* GlobalSystemTable;
extern EFI_HANDLE GlobalImageHandle;
extern REACTOS_INTERNAL_BGCONTEXT framebufferData;
@ -39,6 +40,8 @@ EFI_HANDLE PublicBootHandle;
PVOID ExitStack;
PVOID EndofExitStack;
void _exituefi(VOID);
/* FUNCTIONS *****************************************************************/
static
@ -85,56 +88,13 @@ VOID
UefiSetMemory(
_Inout_ PFREELDR_MEMORY_DESCRIPTOR MemoryMap,
_In_ ULONG_PTR BaseAddress,
_In_ PFN_COUNT Size,
_In_ PFN_COUNT SizeInPages,
_In_ TYPE_OF_MEMORY MemoryType)
{
ULONG_PTR BasePage, PageCount;
BasePage = BaseAddress / EFI_PAGE_SIZE;
PageCount = Size;
/* Add the memory descriptor */
FreeldrDescCount = AddMemoryDescriptor(MemoryMap,
UNUSED_MAX_DESCRIPTOR_COUNT,
BasePage,
PageCount,
MemoryType);
}
VOID
ReserveMemory(
_Inout_ PFREELDR_MEMORY_DESCRIPTOR MemoryMap,
_In_ ULONG_PTR BaseAddress,
_In_ PFN_NUMBER Size,
_In_ TYPE_OF_MEMORY MemoryType,
_In_ PCHAR Usage)
{
ULONG_PTR BasePage, PageCount;
ULONG i;
BasePage = BaseAddress / PAGE_SIZE;
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(BaseAddress, Size);
for (i = 0; i < FreeldrDescCount; i++)
{
/* Check for conflicting descriptor */
if ((MemoryMap[i].BasePage < BasePage + PageCount) &&
(MemoryMap[i].BasePage + MemoryMap[i].PageCount > BasePage))
{
/* Check if the memory is free */
if (MemoryMap[i].MemoryType != LoaderFree)
{
FrLdrBugCheckWithMessage(
MEMORY_INIT_FAILURE,
__FILE__,
__LINE__,
"Failed to reserve memory in the range 0x%Ix - 0x%Ix for %s",
BaseAddress,
Size,
Usage);
}
}
}
PageCount = SizeInPages;
/* Add the memory descriptor */
FreeldrDescCount = AddMemoryDescriptor(MemoryMap,
@ -256,19 +216,36 @@ UefiMemGetMemoryMap(ULONG *MemoryMapSize)
}
}
UefiSetMemory(FreeldrMem,
MapEntry->PhysicalStart,
MapEntry->NumberOfPages,
MemoryType);
/* Sometimes our loader can be loaded into higher memory than we ever allocate */
if (MemoryType == LoaderLoadedProgram)
{
if (((MapEntry->PhysicalStart + (MapEntry->NumberOfPages * PAGE_SIZE)) >> EFI_PAGE_SHIFT) > LoaderPagesSpanned)
{
/* This value needs to be adjusted if this occurs */
LoaderPagesSpanned = ((MapEntry->PhysicalStart + (MapEntry->NumberOfPages * PAGE_SIZE)) >> EFI_PAGE_SHIFT);
}
}
/* We really don't want to touch these reserved spots at all */
if (MemoryType != LoaderReserve)
{
UefiSetMemory(FreeldrMem,
MapEntry->PhysicalStart,
MapEntry->NumberOfPages,
MemoryType);
}
MapEntry = NEXT_MEMORY_DESCRIPTOR(MapEntry, DescriptorSize);
}
/* Windows expects the first page to be reserved, otherwise it asserts.
* However it can be just a free page on some UEFI systems. */
UefiSetMemory(FreeldrMem, 0x000000, 1, LoaderFirmwarePermanent);
*MemoryMapSize = FreeldrDescCount;
return FreeldrMem;
}
static VOID
VOID
UefiExitBootServices(VOID)
{
UINTN MapKey;
@ -306,7 +283,5 @@ UefiExitBootServices(VOID)
VOID
UefiPrepareForReactOS(VOID)
{
UefiExitBootServices();
ExitStack = MmAllocateMemoryWithType(EXIT_STACK_SIZE, LoaderOsloaderStack);
EndofExitStack = (PVOID)((ULONG_PTR)ExitStack + EXIT_STACK_SIZE);
_exituefi();
}

View File

@ -14,7 +14,6 @@ DBG_DEFAULT_CHANNEL(WARNING);
extern EFI_SYSTEM_TABLE* GlobalSystemTable;
extern EFI_HANDLE GlobalImageHandle;
BOOLEAN AcpiPresent = FALSE;
/* FUNCTIONS ******************************************************************/

View File

@ -16,6 +16,7 @@ list(APPEND UEFILDR_ARC_SOURCE
arch/uefi/stubs.c
arch/uefi/ueficon.c
arch/uefi/uefidisk.c
arch/uefi/uefihw.c
arch/uefi/uefimem.c
arch/uefi/uefisetup.c
arch/uefi/uefiutil.c
@ -23,11 +24,14 @@ list(APPEND UEFILDR_ARC_SOURCE
arch/vgafont.c)
if(ARCH STREQUAL "i386")
list(APPEND UEFILDR_ARC_SOURCE
arch/i386/i386idt.c)
list(APPEND UEFILDR_COMMON_ASM_SOURCE
arch/uefi/i386/uefiasm.S
arch/i386/i386trap.S)
elseif(ARCH STREQUAL "amd64")
#TBD
list(APPEND UEFILDR_COMMON_ASM_SOURCE
arch/uefi/amd64/uefiasm.S)
elseif(ARCH STREQUAL "arm")
list(APPEND UEFILDR_ARC_SOURCE
arch/arm/macharm.c
@ -88,6 +92,11 @@ set_target_properties(uefildr PROPERTIES SUFFIX ".efi")
target_compile_definitions(uefildr PRIVATE UEFIBOOT)
# On AMD64 we only map 1GB with freeloader, tell UEFI to keep us low!
if(ARCH STREQUAL "amd64")
set_image_base(uefildr 0x10000)
endif()
if(MSVC)
if(NOT ARCH STREQUAL "arm")
target_link_options(uefildr PRIVATE /DYNAMICBASE:NO)