Source of new Syncbootloader.
Needs commented. Badly. There will still be some weirdness with linebreaks missing in Print() statements. Overall, seems to work. It doesn't access QEMU's video buffers, though. Will work on adding a valid Makefile target for Syncboot soon.
This commit is contained in:
parent
581dd05244
commit
cfdb21e905
57
main.c
57
main.c
|
@ -1,57 +0,0 @@
|
|||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
|
||||
EFI_SYSTEM_TABLE *gST;
|
||||
EFI_BOOT_SERVICES *gBS;
|
||||
EFI_RUNTIME_SERVICES *gRT;
|
||||
|
||||
// Application entrypoint (must be set to 'efi_main' for gnu-efi crt0 compatibility)
|
||||
EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
|
||||
{
|
||||
UINTN Event;
|
||||
EFI_STATUS Status;
|
||||
EFI_TIME* Time;
|
||||
gST = SystemTable;
|
||||
gBS = gST->BootServices;
|
||||
gRT = gST->RuntimeServices;
|
||||
|
||||
#if defined(_GNU_EFI)
|
||||
InitializeLib(ImageHandle, SystemTable);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* In addition to the standard %-based flags, Print() supports the following:
|
||||
* %N Set output attribute to normal
|
||||
* %H Set output attribute to highlight
|
||||
* %E Set output attribute to error
|
||||
* %B Set output attribute to blue color
|
||||
* %V Set output attribute to green color
|
||||
* %r Human readable version of a status code
|
||||
*/
|
||||
Print(L"\n%H*** Sync Bootloading ***%N\n\n");
|
||||
|
||||
Status = gST->ConOut->OutputString(gST->ConOut, L"Hello World\n\r");
|
||||
if (EFI_ERROR(Status))
|
||||
return Status;
|
||||
|
||||
Status = gBS->AllocatePool(EfiBootServicesData,
|
||||
sizeof(EFI_TIME),
|
||||
(VOID**)&Time);
|
||||
|
||||
if (EFI_ERROR(Status))
|
||||
return Status;
|
||||
|
||||
Status = gRT->GetTime(Time, NULL);
|
||||
if (EFI_ERROR(Status))
|
||||
return Status;
|
||||
|
||||
Print(L"%EPress any key to exit.%N\n");
|
||||
SystemTable->ConIn->Reset(SystemTable->ConIn, FALSE);
|
||||
SystemTable->BootServices->WaitForEvent(1, &SystemTable->ConIn->WaitForKey, &Event);
|
||||
#if defined(_DEBUG)
|
||||
// If running in debug mode, use the EFI shut down call to close QEMU
|
||||
SystemTable->RuntimeServices->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
|
||||
#endif
|
||||
|
||||
return Status;
|
||||
}
|
1549
src/bootloader.c
Normal file
1549
src/bootloader.c
Normal file
File diff suppressed because it is too large
Load Diff
206
src/main.c
Normal file
206
src/main.c
Normal file
|
@ -0,0 +1,206 @@
|
|||
/********************
|
||||
* Sync *
|
||||
* UEFI *
|
||||
* Bootloader *
|
||||
********************/
|
||||
|
||||
/* This file is a basic UEFI system bootloader.
|
||||
* It loads a file, which must be PE32+ or ELF64.
|
||||
*
|
||||
* It also performs required startup tasks such as video mode configuration, memory configuration and
|
||||
* ACPI mapping.
|
||||
*
|
||||
* The bootloader is fully configurable with a text file.
|
||||
* The configuration file will be thoroughly documented, but the basics are as such:
|
||||
* There are two fields, file and params. They are exactly what you'd expect.
|
||||
*
|
||||
* The first parameter, file, takes a path relative to the root of the ESP (EFI System Partition) for the file to be booted.
|
||||
* The second parameter, params, takes a list of arguments for the kernel/file being booted.
|
||||
*
|
||||
* In effect, this file is analagous to the config.txt in the boot partition of most linux distributions.
|
||||
*
|
||||
* The bootloader, when calling the file, will pass a chunk of information relating to:
|
||||
* - The firmware being run
|
||||
* - The memory map, with size and a direct reference to the UEFI EFI_MEMORY_DESCRIPTOR struct.
|
||||
* - Where the kernel is, and how large it is.
|
||||
* - The drive the kernel is stored on,
|
||||
* - how big the drive is
|
||||
* - where in the drive it is.
|
||||
* - The options defined by the file
|
||||
* - A direct reference to the UEFI Runtime Services table
|
||||
* - Information about the graphics card, and about the framebuffer so that it can start drawing immediately
|
||||
* - EFI_CONFIGURATION_TABLES (ACPI, SMBIOS, etc.)
|
||||
*
|
||||
* Let's get started, then.
|
||||
*/
|
||||
|
||||
#include "BL.h"
|
||||
|
||||
EFI_STATUS AwaitKey(CHAR16* String) {
|
||||
EFI_STATUS Status;
|
||||
EFI_INPUT_KEY Key;
|
||||
Print(String);
|
||||
|
||||
Print(L"%EPress any key to continue..%N\r\n");
|
||||
|
||||
Status = ST->ConIn->Reset(ST->ConIn, FALSE);
|
||||
if (EFI_ERROR(Status))
|
||||
return Status;
|
||||
|
||||
while ((Status = ST->ConIn->ReadKeyStroke(ST->ConIn, &Key)) == EFI_NOT_READY);
|
||||
|
||||
Status = ST->ConIn->Reset(ST->ConIn, FALSE);
|
||||
if (EFI_ERROR(Status))
|
||||
return Status;
|
||||
|
||||
Print(L"\r\n");
|
||||
return Status;
|
||||
}
|
||||
|
||||
EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable) {
|
||||
InitializeLib(ImageHandle, SystemTable); // Initialize EFILIB.
|
||||
/* Provides the following definitions:
|
||||
- ST = SystemTable
|
||||
- BS = SystemTable->BootServices
|
||||
- RT = SystemTable->RuntimeServices
|
||||
*/
|
||||
|
||||
EFI_STATUS Status; /* Reusable status flag. The first of many. */
|
||||
|
||||
Print(L"\n%H========== Sync Bootloading ==========%N\r\n");
|
||||
|
||||
#ifdef DEBUG
|
||||
Print(L"%EDebug mode enabled. Extra output will be given.%N\r\nSync Bootloader version %u.%u\r\n", MAJOR_VER, MINOR_VER);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WATCHDOG_TIMER_DISABLE
|
||||
Print(L"Disabling UEFI Watchdog Timer..\r\n");
|
||||
Status = BS->SetWatchdogTimer(0, 0, 0, NULL);
|
||||
if (EFI_ERROR(Status))
|
||||
Print(L"Watchdog stop failed. Timeout will continue.\r\n");
|
||||
#endif
|
||||
|
||||
/* Get the time */
|
||||
EFI_TIME Now;
|
||||
Status = RT->GetTime(&Now, NULL);
|
||||
if (EFI_ERROR(Status))
|
||||
Print(L"Error fetching time.\r\n");
|
||||
else
|
||||
/* and then print it. */
|
||||
Print(L"%02hhu/%02hhu/%04hu - %02hhu:%02hhu:%02hhu.%u\r\n\n", Now.Month, Now.Day, Now.Year, Now.Hour, Now.Minute, Now.Second, Now.Nanosecond);
|
||||
|
||||
|
||||
/* Await a keypress. */
|
||||
size_t TimeoutCounter = 10 /*seconds*/;
|
||||
EFI_INPUT_KEY Key = { 0 };
|
||||
|
||||
Print(L"Continuing in 10 seconds. Press 's' to stop the timer, or press any other key to continue.. \r\n");
|
||||
while (TimeoutCounter) { // 10 messages.
|
||||
Print(L"Continuing in %llu\r", TimeoutCounter);
|
||||
Status = WaitForSingleEvent(ST->ConIn->WaitForKey, 10000000); // 1 second delay.
|
||||
if (Status != EFI_TIMEOUT) { // Make sure a key was pressed. If not, go around.
|
||||
Status = ST->ConIn->ReadKeyStroke(ST->ConIn, &Key); // Read a key
|
||||
if (EFI_ERROR(Status)) { // Error check
|
||||
Print(L"Error reading key. 0x%llx\r\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
if (Key.UnicodeChar == L's')
|
||||
AwaitKey(L"\r\nTimer stopped.\r\n");
|
||||
else {
|
||||
Status = ST->ConIn->Reset(ST->ConIn, FALSE);
|
||||
if (EFI_ERROR(Status)) {
|
||||
Print(L"Error resetting input buffer. 0x%llx\r\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
TimeoutCounter -= 1; // Decrease the counter and start again
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
Print(L"%HEFI System Table:%N\r\n Signature: 0x%1x\r\n Revision: 0x%08x\r\n Header Size: %u bytes\r\n CRC: 0x08x\r\n Reserved: 0x%x\r\n", ST->Hdr.Signature, ST->Hdr.Revision, ST->Hdr.HeaderSize, ST->Hdr.CRC32, ST->Hdr.Reserved);
|
||||
#else
|
||||
Print(L"%HEFI System Table:%N\r\n Signature: 0x%1x\r\n Revision: %u.%u", ST->Hdr.Signature, ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xFFFF) / 10);
|
||||
/* The spec is really inspecific about the minor version. It's limited to 99, but it gets a whole 2-byte segment to itself, so we just do some protection here.*/
|
||||
if ((ST->Hdr.Revision & 0xFFFF) % 10)
|
||||
Print(L".%u\r\n", (ST->Hdr.Revision & 0xFFFF) % 10);
|
||||
else
|
||||
Print(L"\r\n")
|
||||
#endif
|
||||
Print(L" Firmware Vendor: %s\r\n Firmware Revision: 0x%08x\r\n\n", ST->FirmwareVendor, ST->FirmwareRevision);
|
||||
Print(L"Found %llu system configuration tables.\r\n", ST->NumberOfTableEntries);
|
||||
|
||||
#ifdef DEBUG
|
||||
AwaitKey(L"Printing table information.\r\n");
|
||||
|
||||
uint8_t RSDP_Found = 0;
|
||||
uint8_t RSDP_Index = 0;
|
||||
|
||||
for (size_t i = 0; i < ST->NumberOfTableEntries; i++) {
|
||||
Print(L"Table %llu GUID: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\r\n", i,
|
||||
ST->ConfigurationTable[i].VendorGuid.Data1,
|
||||
ST->ConfigurationTable[i].VendorGuid.Data2,
|
||||
ST->ConfigurationTable[i].VendorGuid.Data3,
|
||||
ST->ConfigurationTable[i].VendorGuid.Data4[0],
|
||||
ST->ConfigurationTable[i].VendorGuid.Data4[1],
|
||||
ST->ConfigurationTable[i].VendorGuid.Data4[2],
|
||||
ST->ConfigurationTable[i].VendorGuid.Data4[3],
|
||||
ST->ConfigurationTable[i].VendorGuid.Data4[4],
|
||||
ST->ConfigurationTable[i].VendorGuid.Data4[5],
|
||||
ST->ConfigurationTable[i].VendorGuid.Data4[6],
|
||||
ST->ConfigurationTable[i].VendorGuid.Data4[7]);
|
||||
|
||||
if (strncmpa(&ST->ConfigurationTable[i].VendorGuid, &AcpiTableGuid, 16)) {
|
||||
Print(L"RSDP 2.0 Found!\r\n");
|
||||
RSDP_Index = i;
|
||||
RSDP_Found = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!RSDP_Found)
|
||||
Print(L"System has no RSDP.\r\n");
|
||||
|
||||
AwaitKey(L"Printing the memory map.\r\n");
|
||||
|
||||
PrintMemoryMap();
|
||||
|
||||
AwaitKey(L"Done.\r\n");
|
||||
#endif
|
||||
|
||||
GFX_INFO* Graphics;
|
||||
Status = ST->BootServices->AllocatePool(EfiLoaderData, sizeof(GFX_INFO), (void**)&Graphics);
|
||||
if (EFI_ERROR(Status)) {
|
||||
Print(L"Failed to allocate memory for the graphics table. 0x%llx\r\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
Print(L"Graphics struct allocated.\r\n");
|
||||
#endif
|
||||
|
||||
Status = InitGfx(ImageHandle, Graphics);
|
||||
if (EFI_ERROR(Status)) {
|
||||
Print(L"Initializing UEFI Gfx error. 0x%llx\r\n", Status);
|
||||
AwaitKey(L"\0");
|
||||
return Status;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
AwaitKey(L"Graphics stuff finished.\r\n");
|
||||
Print(L"Config table address: 0x%llx\r\n", ST->ConfigurationTable);
|
||||
//Print(L"Data at RSDP: 0x%016llx%0x16llx\r\n", *(EFI_PHYSICAL_ADDRESS*)(ST->ConfigurationTable[RSDP_Index].VendorTable + 8), *(EFI_PHYSICAL_ADDRESS*)ST->ConfigurationTable[RSDP_Index].VendorTable);
|
||||
#endif
|
||||
|
||||
/* It's go time. */
|
||||
Status = LoadKernel(ImageHandle, Graphics, ST->ConfigurationTable, ST->NumberOfTableEntries, ST->Hdr.Revision);
|
||||
|
||||
AwaitKey(L"It's not go time!\r\n");
|
||||
return Status;
|
||||
}
|
||||
|
186
src/memory.c
Normal file
186
src/memory.c
Normal file
|
@ -0,0 +1,186 @@
|
|||
/********************
|
||||
* Sync *
|
||||
* UEFI *
|
||||
* Bootloader *
|
||||
********************/
|
||||
|
||||
/* Memory management and utility functions. */
|
||||
|
||||
#include "BL.h"
|
||||
|
||||
uint8_t Compare(const void* a, const void* b, size_t Length) {
|
||||
|
||||
const uint8_t* one = a, *two = b;
|
||||
|
||||
for (size_t i = 0; i < Length; i++) {
|
||||
if (one[i] != two[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t VerifyZero(size_t Length, size_t Base) {
|
||||
for (size_t i = 0; i < Length; i++) {
|
||||
if (*(((uint8_t*)Base) + i) != 0)
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EFI_PHYSICAL_ADDRESS FindFreeAddress(size_t pages, EFI_PHYSICAL_ADDRESS OldAddress) {
|
||||
EFI_STATUS MemStatus;
|
||||
|
||||
size_t MemMapSize = 0, MemMapKey, MemMapDescriptorSize;
|
||||
uint32_t MemMapDescriptorVersion;
|
||||
|
||||
EFI_MEMORY_DESCRIPTOR* MemMap = NULL;
|
||||
EFI_MEMORY_DESCRIPTOR* Piece;
|
||||
|
||||
MemStatus = BS->GetMemoryMap(&MemMapSize, MemMap, &MemMapKey, &MemMapDescriptorSize, &MemMapDescriptorVersion);
|
||||
if (MemStatus == EFI_BUFFER_TOO_SMALL) {
|
||||
MemStatus = BS->AllocatePool(EfiBootServicesData, MemMapSize, (void**)&MemMap);
|
||||
if (EFI_ERROR(MemStatus)) {
|
||||
Print(L"FindFreeAddress MemMap AllocatePool error. 0x%llx\r\n", MemStatus);
|
||||
return ~0ULL;
|
||||
}
|
||||
|
||||
MemStatus = BS->GetMemoryMap(&MemMapSize, MemMap, &MemMapKey, &MemMapDescriptorSize, &MemMapDescriptorVersion);
|
||||
}
|
||||
|
||||
if (EFI_ERROR(MemStatus)) {
|
||||
Print(L"Error getting a memory address for FindFreeAddress. 0x%llx\r\n", MemStatus);
|
||||
return ~0ULL;
|
||||
}
|
||||
|
||||
for (Piece = MemMap; Piece < (EFI_MEMORY_DESCRIPTOR*)((uint8_t*)MemMap + MemMapSize); Piece = (EFI_MEMORY_DESCRIPTOR*)((uint8_t*)Piece + MemMapDescriptorSize)) {
|
||||
if ((Piece->Type == EfiConventionalMemory) && (Piece->NumberOfPages >= pages) && (Piece->PhysicalStart > OldAddress))
|
||||
break;
|
||||
}
|
||||
|
||||
if (Piece >= (EFI_MEMORY_DESCRIPTOR*)((uint8_t*)MemMap + MemMapSize))
|
||||
return ~0ULL;
|
||||
|
||||
MemStatus = BS->FreePool(MemMap);
|
||||
if (EFI_ERROR(MemStatus))
|
||||
Print(L"Error freeing FindMemoryAddress map pool. 0x%llx\r\n", MemStatus);
|
||||
|
||||
return Piece->PhysicalStart;
|
||||
}
|
||||
|
||||
static const CHAR16 Memory_Segments[16][27] = {
|
||||
L"EfiReservedMemoryType ",
|
||||
L"EfiLoaderCode ",
|
||||
L"EfiLoaderData ",
|
||||
L"EfiBootServicesCode ",
|
||||
L"EfiBootServicesData ",
|
||||
L"EfiRuntimeServicesCode ",
|
||||
L"EfiRuntimeServicesData ",
|
||||
L"EfiConventionalMemory ",
|
||||
L"EfiUnusableMemory ",
|
||||
L"EfiACPIReclaimMemory ",
|
||||
L"EfiACPIMemoryNVS ",
|
||||
L"EfiMemoryMappedIO ",
|
||||
L"EfiMemoryMappedIOPortSpace",
|
||||
L"EfiPalCode ",
|
||||
L"EfiPersistentMemory ",
|
||||
L"EfiMaxMemoryType "
|
||||
};
|
||||
|
||||
void PrintMemoryMap() {
|
||||
EFI_STATUS MemStatus;
|
||||
size_t MemMapSize = 0, MemMapKey, MemMapDescriptorSize;
|
||||
uint32_t MemMapDescriptorVersion;
|
||||
|
||||
EFI_MEMORY_DESCRIPTOR* MemMap = NULL;
|
||||
EFI_MEMORY_DESCRIPTOR* Piece;
|
||||
|
||||
uint16_t line = 0;
|
||||
|
||||
MemStatus = BS->GetMemoryMap(&MemMapSize, MemMap, &MemMapKey, &MemMapDescriptorSize, &MemMapDescriptorVersion);
|
||||
if (MemStatus == EFI_BUFFER_TOO_SMALL) {
|
||||
MemStatus = BS->AllocatePool(EfiBootServicesData, MemMapSize, (void**)&MemMap);
|
||||
if (EFI_ERROR(MemStatus)) {
|
||||
Print(L"MemMap AllocatePool error: 0x%llu\r\n", MemStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
MemStatus = BS->GetMemoryMap(&MemMapSize, MemMap, &MemMapKey, &MemMapDescriptorSize, &MemMapDescriptorVersion);
|
||||
}
|
||||
|
||||
if (EFI_ERROR(MemStatus))
|
||||
Print(L"Error getting memory map for print. 0x%llx\r\n", MemStatus);
|
||||
|
||||
Print(L"MemMapSize: %llu, MemMapDescriptorSize: %llu, MemMapDescriptorVersion: 0x%x\r\n", MemMapSize, MemMapDescriptorSize, MemMapDescriptorVersion);
|
||||
|
||||
for (Piece = MemMap; Piece < (EFI_MEMORY_DESCRIPTOR*)((uint8_t*)MemMap + MemMapSize); Piece = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)Piece + MemMapDescriptorSize)) {
|
||||
if (line % 20 == 0) {
|
||||
AwaitKey(L"\0");
|
||||
Print(L"# Memory Type Phys Addr Start Num Of Pages Attr\r\n");
|
||||
}
|
||||
|
||||
Print(L"%2hu: %s 0x%016llx 0x%llx 0x%llx\r\n", line, Memory_Segments[Piece->Type], Piece->PhysicalStart, Piece->NumberOfPages, Piece->Attribute);
|
||||
line++;
|
||||
}
|
||||
|
||||
MemStatus = BS->FreePool(MemMap);
|
||||
if (EFI_ERROR(MemStatus)) {
|
||||
Print(L"Error freeing MemoryMap Printing pool. 0x%llx\r\n", MemStatus);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
EFI_PHYSICAL_ADDRESS FindFreeAddress_ByPage(size_t pages, EFI_PHYSICAL_ADDRESS OldAddress) {
|
||||
EFI_STATUS MemStatus;
|
||||
size_t MemMapSize = 0, MemMapKey, MemMapDescriptorSize;
|
||||
uint32_t MemMapDescriptorVersion;
|
||||
EFI_MEMORY_DESCRIPTOR* MemMap = NULL;
|
||||
EFI_MEMORY_DESCRIPTOR* Piece;
|
||||
EFI_PHYSICAL_ADDRESS PhysicalEnd;
|
||||
EFI_PHYSICAL_ADDRESS DiscoveredAddress;
|
||||
|
||||
MemStatus = BS->GetMemoryMap(&MemMapSize, MemMap, &MemMapKey, &MemMapDescriptorSize, &MemMapDescriptorVersion);
|
||||
if (MemStatus == EFI_BUFFER_TOO_SMALL) {
|
||||
MemStatus = BS->AllocatePool(EfiBootServicesData, MemMapSize, (void **)&MemMap);
|
||||
if (EFI_ERROR(MemStatus)) {
|
||||
Print(L"FindFreeAddress_ByPage MemMap AllocatePool error. 0x%llx\r\n", MemStatus);
|
||||
return ~0ULL;
|
||||
}
|
||||
MemStatus = BS->GetMemoryMap(&MemMapSize, MemMap, &MemMapKey, &MemMapDescriptorSize, &MemMapDescriptorVersion);
|
||||
}
|
||||
if (EFI_ERROR(MemStatus)) {
|
||||
Print(L"Error getting memory map for FindFreeAddress_ByPage. 0x%llx\r\n", MemStatus);
|
||||
return ~0ULL;
|
||||
}
|
||||
|
||||
for (Piece = MemMap; Piece < (EFI_MEMORY_DESCRIPTOR*)((uint8_t*)MemMap + MemMapSize); Piece = (EFI_MEMORY_DESCRIPTOR*)((uint8_t*)Piece + MemMapDescriptorSize)) {
|
||||
if ((Piece->Type == EfiConventionalMemory) && (Piece->NumberOfPages >= pages)) {
|
||||
PhysicalEnd = Piece->PhysicalStart + (Piece->NumberOfPages << EFI_PAGE_SHIFT) - EFI_PAGE_MASK; // Get the end of this range, and use it to set a bound on the range (define a max returnable address).
|
||||
if ((OldAddress >= Piece->PhysicalStart) && ((OldAddress + (pages << EFI_PAGE_SHIFT)) < PhysicalEnd)) {
|
||||
DiscoveredAddress = OldAddress + EFI_PAGE_SIZE;
|
||||
break;
|
||||
}
|
||||
else if (Piece->PhysicalStart > OldAddress) {
|
||||
DiscoveredAddress = Piece->PhysicalStart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Piece >= (EFI_MEMORY_DESCRIPTOR*)((uint8_t*)MemMap + MemMapSize)){
|
||||
#ifdef MEMORY_CHECK
|
||||
Print(L"No more free addresses by page...\r\n");
|
||||
#endif
|
||||
return ~0ULL;
|
||||
}
|
||||
|
||||
MemStatus = BS->FreePool(MemMap);
|
||||
if (EFI_ERROR(MemStatus)) {
|
||||
Print(L"Error freeing FindfreeAddress_ByPage memmap pool. 0x%llx\r\n", MemStatus);
|
||||
}
|
||||
|
||||
return DiscoveredAddress;
|
||||
}
|
Loading…
Reference in New Issue
Block a user