From cfdb21e905d4d07d4f0d08287b2e1be3cf536f5e Mon Sep 17 00:00:00 2001 From: Curle Date: Sat, 20 Jul 2019 20:17:07 +0100 Subject: [PATCH] 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. --- main.c | 57 -- src/bootloader.c | 1549 ++++++++++++++++++++++++++++++++++++++++++++++ src/gfx.c | 1194 +++++++++++++++++++++++++++++++++++ src/main.c | 206 ++++++ src/memory.c | 186 ++++++ 5 files changed, 3135 insertions(+), 57 deletions(-) delete mode 100644 main.c create mode 100644 src/bootloader.c create mode 100644 src/gfx.c create mode 100644 src/main.c create mode 100644 src/memory.c diff --git a/main.c b/main.c deleted file mode 100644 index 4890a9c..0000000 --- a/main.c +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include - -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; -} diff --git a/src/bootloader.c b/src/bootloader.c new file mode 100644 index 0000000..34ac507 --- /dev/null +++ b/src/bootloader.c @@ -0,0 +1,1549 @@ +/******************** + * Sync * + * UEFI * + * Bootloader * + ********************/ + +#include "BL.h" + +/* LoadKernel. + * Locates, unpacks and loads the kernel into memory, + * Exits the boot services provided by UEFI, + * and jumps to the kernel's entry point. */ + +EFI_STATUS LoadKernel(EFI_HANDLE ImageHandle, GFX_INFO* Graphics, EFI_CONFIGURATION_TABLE* ConfigTables, size_t ConfigTables_Length, uint32_t UEFIVer) { + +#ifdef GFX_DEBUG_MAIN + + /* Iterate the array of GPUs, gathering all the available data. */ + for (size_t k = 0; k < Graphics->FBCount; k++) { + Print(L"GPU Mode: %u of %u\r\n", Graphics->GPUs[k].Mode, Graphics->GPUs[k].MaxMode - 1); + Print(L"GPU FB: 0x%016llx\r\n", Graphics->GPUs[k].FrameBufferBase); + Print(L"GPU FB Size: 0x%016llx\r\n", Graphics->GPUs[k].FrameBufferSize); + Print(L"GPU SizeOfInfo: %u Bytes\r\n", Graphics->GPUs[k].SizeOfInfo); + Print(L"GPU Info Ver: 0x%x\r\n", Graphics->GPUs[k].Info->Version); + Print(L"GPU Info Res: %ux%u\r\n", Graphics->GPUs[k].Info->HorizontalResolution, Graphics->GPUs[k].Info->VerticalResolution); + Print(L"GPU Info PxFormat: 0x%x\r\n", Graphics->GPUs[k].Info->PixelFormat); + Print(L"GPU Info PxInfo (R,G,B,Rsvd Masks): 0x%08x, 0x%08x, 0x%08x, 0x%08x\r\n", Graphics->GPUs[k].Info->PixelInformation.RedMask, Graphics->GPUs[k].Info->PixelInformation.GreenMask, Graphics->GPUs[k].Info->PixelInformation.BlueMask, Graphics->GPUs[k].Info->PixelInformation.ReservedMask); + Print(L"GPU Info PxPerScanLine: %u\r\n", Graphics->GPUs[k].Info->PixelsPerScanLine); + AwaitKey(L"\0"); + } +#endif + + EFI_STATUS KernelStatus; // Throwaway status flag. + + EFI_PHYSICAL_ADDRESS KernelBase = 0; // Only used much later, but setup here. + size_t KernelPages = 0; + + EFI_LOADED_IMAGE_PROTOCOL* LoadedImage; // The image here is the BOOTX64.EFI file. + + /* Connect to the device which contains the file being currently run */ + KernelStatus = ST->BootServices->OpenProtocol(ImageHandle, &LoadedImageProtocol, (void**)&LoadedImage, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(KernelStatus)) { + Print(L"Loaded Image Protocol Error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + /* Get the path to the device */ + CHAR16* ESPRootTemp = DevicePathToStr(DevicePathFromHandle(LoadedImage->DeviceHandle)); + size_t ESPRootSize = StrSize(ESPRootTemp); + + CHAR16* ESPRoot; + /* Reinitialize the path to a form we can use */ + KernelStatus = ST->BootServices->AllocatePool(EfiLoaderData, ESPRootSize, (void**)&ESPRoot); + + if (EFI_ERROR(KernelStatus)) { + Print(L"ESPRoot Allocate error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + CopyMem(ESPRoot, ESPRootTemp, ESPRootSize); + + KernelStatus = BS->FreePool(ESPRootTemp); + if (EFI_ERROR(KernelStatus)) { + Print(L"ESPRoot Free error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + // Get a reference to the FileSystem protocol, which allows us to access files. + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* FileSystem; + + KernelStatus = ST->BootServices->OpenProtocol(LoadedImage->DeviceHandle, &FileSystemProtocol, (void**)&FileSystem, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(KernelStatus)) { + Print(L"FileSystem OpenProtocol error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + // Get the root of the current drive ("/") + EFI_FILE* CurrentDriveRoot; + + // Open it + KernelStatus = FileSystem->OpenVolume(FileSystem, &CurrentDriveRoot); + if (EFI_ERROR(KernelStatus)) { + Print(L"OpenVolume Error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + /* ==================== Boot Configuration File ==================== */ + + CHAR16* BootFilePath = ((FILEPATH_DEVICE_PATH*)LoadedImage->FilePath)->PathName; + +#ifdef LOADER_DEBUG_MAIN + Print(L"Path to main file: %s\r\n", BootFilePath); +#endif + + /* We need the length and size (in bytes) of the path of the boot file and the + * config file, so there's a few calculations to be done here. */ + size_t ConfigFilePrefixLength = 0; + size_t BootFilePathLength = 0; + + while (BootFilePath[BootFilePathLength] != L'\0') { + if (BootFilePath[BootFilePathLength] == L'\\') { + ConfigFilePrefixLength = BootFilePathLength; + } + BootFilePathLength++; + } + + BootFilePathLength += 1; // Null terminator + ConfigFilePrefixLength += 1; // Path ends with '\' + +#ifdef LOADER_DEBUG_MAIN + Print(L"BootFilePathLength: %llu, TextFilePathLength: %llu, BootfilePath size: %llu\r\n", BootFilePathLength, ConfigFilePrefixLength, StrSize(BootFilePath)); + AwaitKey(L'\0'); +#endif + + const CHAR16 ConfigFileName[13] = L"bootconf.txt"; // The config file is called bootconf.txt + + size_t ConfigFilePrefixSize = ConfigFilePrefixLength * sizeof(CHAR16); + size_t ConfigFilePathSize = ConfigFilePrefixLength + sizeof(ConfigFileName); + + CHAR16* ConfigFilePath; + + KernelStatus = ST->BootServices->AllocatePool(EfiBootServicesData, ConfigFilePathSize, (void**)&ConfigFilePath); + if (EFI_ERROR(KernelStatus)) { + Print(L"ConfigFile Path Allocate error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + CopyMem(ConfigFilePath, BootFilePath, ConfigFilePathSize); + CopyMem(&ConfigFilePath[ConfigFilePrefixLength], ConfigFileName, sizeof(ConfigFileName)); + +#ifdef LOADER_DEBUG_MAIN + Print(L"ConfigFilePath: %s, ConfigFilePath Size: %llu\r\n", ConfigFilePath, ConfigFilePathSize); + AwaitKey(L"\0"); +#endif + + // Now we start opening the file. + EFI_FILE* KernelConfigFile; + + KernelStatus = CurrentDriveRoot->Open(CurrentDriveRoot, &KernelConfigFile, ConfigFilePath, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY); + if (EFI_ERROR(KernelStatus)) { + // The only reason an Open function can fail is because the file is missing. + // This is firmware, it has no idea what permissions are. + AwaitKey(L"bootconf.txt is missing. \r\n"); + return KernelStatus; + } + +#ifdef LOADER_DEBUG_MAIN + AwaitKey(L"bootconf.txt file opened.\r\n"); +#endif + + + // We need to know how long the file is. + size_t ConfigFileInfo_Size; + + // To get the length of the file, we need to talk to the file metadata. + // To talk to the metadata, we need to know how long the metadata is. + // To get the length of the metadata, we need to talk to the firmware. + // + // However, UEFI knows this, and if we try to get the length of the file + // without telling it how long the metadata is (meaning this is the first + // time we've looked at the file), it will give us all the information + // we need in one go. Handy. + + KernelStatus = KernelConfigFile->GetInfo(KernelConfigFile, &gEfiFileInfoGuid, &ConfigFileInfo_Size, NULL); + +#ifdef LOADER_DEBUG_MAIN + Print(L"ConfigFileInfo_Size: %llu Bytes\r\n", ConfigFileInfo_Size); +#endif + + EFI_FILE_INFO *ConfigFileInfo; + + KernelStatus = BS->AllocatePool(EfiBootServicesData, ConfigFileInfo_Size, (void**)&ConfigFileInfo); + if (EFI_ERROR(KernelStatus)) { + Print(L"ConfigFile Allocate error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = KernelConfigFile->GetInfo(KernelConfigFile, &gEfiFileInfoGuid, &ConfigFileInfo_Size, ConfigFileInfo); + if (EFI_ERROR(KernelStatus)) { + Print(L"GetInfo error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + +#ifdef METADATA_SHOW + + Print(L"Filename: %s\r\n", ConfigFileInfo->FileName); + Print(L"Size: %llu\r\n", ConfigFileInfo->Size); + Print(L"Filesize: %llu\r\n", ConfigFileInfo->FileName); + Print(L"PhysicalSize: %llu\r\n", ConfigFileInfo->PhysicalSize); + Print(L"Attribute: %llx\r\n", ConfigFileInfo->Attribute); + + Print(L"Created: %02hhu/%02hhu/%04hu - %02hhu:%02hhu:%02hhu.%u\r\n", ConfigFileInfo->CreateTime.Month, ConfigFileInfo->CreateTime.Day, ConfigFileInfo->CreateTime.Year, ConfigFileInfo->CreateTime.Hour, ConfigFileInfo->CreateTime.Minute, ConfigFileInfo->CreateTime.Second, ConfigFileInfo->CreateTime.Nanosecond); + Print(L"Last Modified: %02hhu/%02hhu/%04hu - %02hhu:%02hhu:%02hhu.%u\r\n", ConfigFileInfo->ModificationTime.Month, ConfigFileInfo->ModificationTime.Day, ConfigFileInfo->ModificationTime.Year, ConfigFileInfo->ModificationTime.Hour, ConfigFileInfo->ModificationTime.Minute, ConfigFileInfo->ModificationTime.Second, ConfigFileInfo->ModificationTime.Nanosecond); + AwaitKey(L"\0"); +#endif + + // Now that we know how big the file is, we can copy it into memory. + CHAR16* KernelConfigArray; + + KernelStatus = BS->AllocatePool(EfiBootServicesData, ConfigFileInfo->FileSize, (void**)&KernelConfigArray); + if (EFI_ERROR(KernelStatus)) { + Print(L"Kernel Config AllocatePool error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = KernelConfigFile->Read(KernelConfigFile, &ConfigFileInfo->FileSize, KernelConfigArray); + if (EFI_ERROR(KernelStatus)) { + Print(L"KernelConfigArray read error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + +#ifdef LOADER_DEBUG_MAIN + AwaitKey(L"bootconf.txt loaded into memory.\r\n"); +#endif + + + // Check that the BOM exists in the file. + + uint16_t BOM = UTF16_BOM_LE; + + if (!Compare(KernelConfigArray, BOM, 2)) { + // The file doesn't have the BOM mark. + // Just to make sure, we should check if the file was encoded on a Big Endian system. + BOM = UTF16_BOM_BE; + + if (Compare(KernelConfigArray, BOM, 2)) + Print(L"%EError: bootconf.txt was made on a Big-Endian system. Please convert it or use a Little-Endian system.%N\r\n"); + else + // There is no BOM mark. + Print(L"%EError: bootconf.txt does not have a BOM identification, or it was not encoded as UTF-16/UCS-2.\r\nWithout the BOM, the bootloader cannot tell whether or not the file is UTF-16.\r\nPlease recreate or re-encode the file as Unicode (Windows) or UTF-16 (Linux) and restart.%N\r\n"); + + AwaitKey(L"Please fix the issue and restart.\r\n"); + return KernelStatus; + } + + size_t KernelPathSize = 0, FirstLineLength = 0; + + for (size_t i = 1 /* Skip the BOM */; i < ((ConfigFileInfo->FileSize) >> 1) /* UTF-16 characters are 2 bytes long, so we divide by 2 to get the number of characters */; i++) { + if (KernelConfigArray[i] == L'\n') { + FirstLineLength = i + 1; // Account for the extra character. + break; + } + else if (KernelConfigArray[i] = L'\r') { + FirstLineLength = i + 2; // There will be an \n after the \r + break; + } + + if (KernelConfigArray[i] != L' ') + KernelPathSize++; + + } + + size_t KernelPathLength = KernelPathSize; + + KernelPathSize = (KernelPathSize + 1 /*Null term*/) << 1; // Multiply by 2 to get the length of CHAR16 units + + +#ifdef LOADER_DEBUG_MAIN + Print(L"Kernel Path Size: %llu bytes, Length: %llu characters\r\n", KernelPathSize, KernelPathLength); +#endif + + // Now for the option line + + size_t OptionLineSize = 0; + + for (size_t j = FirstLineLength; j < ((ConfigFileInfo->FileSize) >> 1); j++) { + if ((KernelConfigArray[j] == L'\n') || (KernelConfigArray[j] == L'\r')) + break; + + OptionLineSize++; + } + + size_t OptionLineLength = OptionLineSize; + + OptionLineSize = (OptionLineSize + 1) << 1; + +#ifdef LOADER_DEBUG_MAIN + Print(L"Option line size: %llu bytes, Length: %llu\r\n", OptionLineSize, OptionLineLength); +#endif + + CHAR16* KernelPath; + KernelStatus = BS->AllocatePool(EfiLoaderData, KernelPathSize, (void**)&KernelPath); + if (EFI_ERROR(KernelStatus)) { + Print(L"KernelPath Allocate error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + CHAR16* OptionLine; + KernelStatus = BS->AllocatePool(EfiLoaderData, OptionLineSize, (void**)&OptionLine); + if (EFI_ERROR(KernelStatus)) { + Print(L"OptionLine Allocate error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + for (size_t i = 1; i < FirstLineLength; i++) { + if ((KernelConfigArray[i] == L'\n') || (KernelConfigArray[i] == L'\r')) + break; + + if (KernelConfigArray[i] != L' ') + KernelPath[i - 1] = KernelConfigArray[i]; + } + + KernelPath[KernelPathLength] = L'\0'; + + AwaitKey(L"Loading kernel image.\r\n"); + + KernelStatus = BS->FreePool(ConfigFilePath); + if (EFI_ERROR(KernelStatus)) { + Print(L"ConfigFilePath Free error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = BS->FreePool(KernelConfigArray); + if (EFI_ERROR(KernelStatus)) { + Print(L"KernelConfigArray Free error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = BS->FreePool(ConfigFileInfo); + if (EFI_ERROR(KernelStatus)) { + Print(L"ConfigFileInfo Free error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + + + EFI_FILE* KernelFile; + + KernelStatus = CurrentDriveRoot->Open(CurrentDriveRoot, &KernelFile, KernelPath, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY); + if (EFI_ERROR(KernelStatus)) { + Print(L"%s file missing.\r\n", KernelPath); + return KernelStatus; + } + +#ifdef LOADER_DEBUG_MAIN + Print(L"Kernel file opened.\r\n"); +#endif + + EFI_PHYSICAL_ADDRESS HeaderLocation = 0x400000; + size_t FileInfoSize; + + KernelStatus = KernelFile->GetInfo(KernelFile, &gEfiFileInfoGuid, &FileInfoSize, NULL); + +#ifdef LOADER_DEBUG_MAIN + Print(L"Kernel FileInfo is %llu bytes large.\r\n", FileInfoSize); +#endif + + EFI_FILE_INFO* KernelFileInfo; + + KernelStatus = BS->AllocatePool(EfiLoaderData, FileInfoSize, (void**)&KernelFileInfo); + if (EFI_ERROR(KernelStatus)) { + Print(L"FileInfo Allocate error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = KernelFile->GetInfo(KernelFile, &gEfiFileInfoGuid, &FileInfoSize, KernelFileInfo); + if (EFI_ERROR(KernelStatus)) { + Print(L"GetInfo error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + +#ifdef METADATA_SHOW + Print(L"FileName: %s\r\n", KernelFileInfo->FileName); + Print(L"Size: %llu\r\n", KernelFileInfo->Size); + Print(L"FileSize: %llu\r\n", KernelFileInfo->FileSize); + Print(L"PhysicalSize: %llu\r\n", KernelFileInfo->PhysicalSize); + Print(L"Attribute: %llx\r\n", KernelFileInfo->Attribute); + Print(L"Created: %02hhu/%02hhu/%04hu - %02hhu:%02hhu:%02hhu.%u\r\n", KernelFileInfo->CreateTime.Month, KernelFileInfo->CreateTime.Day, KernelFileInfo->CreateTime.Year, KernelFileInfo->CreateTime.Hour, KernelFileInfo->CreateTime.Minute, KernelFileInfo->CreateTime.Second, KernelFileInfo->CreateTime.Nanosecond); + Print(L"Last Modified: %02hhu/%02hhu/%04hu - %02hhu:%02hhu:%02hhu.%u\r\n", KernelFileInfo->ModificationTime.Month, KernelFileInfo->ModificationTime.Day, KernelFileInfo->ModificationTime.Year, KernelFileInfo->ModificationTime.Hour, KernelFileInfo->ModificationTime.Minute, KernelFileInfo->ModificationTime.Second, KernelFileInfo->ModificationTime.Nanosecond); +#endif + +#ifdef LOADER_DEBUG_MAIN + AwaitKey("Kernel FileInfo allocated and populated.\r\n"); +#endif + + + size_t HeaderSize = sizeof(IMAGE_DOS_HEADER); + IMAGE_DOS_HEADER DOSHeader; + + KernelStatus = KernelFile->Read(KernelFile, &HeaderSize, &DOSHeader); + if (EFI_ERROR(KernelStatus)) { + Print(L"DOSHeader Read error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + size_t KernelFileLocation; + KernelFile->GetPosition(KernelFile, &KernelFileLocation); + + + // To accurately know where to jump to, we need to know whether to use MS_ABI (PE) or SYSV_ABI (Nix) calling convention. + uint8_t KernelIsMSABI = 0; + + if (DOSHeader.e_magic == IMAGE_DOS_SIGNATURE) { + // Load a 64-bit PE32+ Image. + +#ifdef LOADER_DEBUG_MAIN + AwaitKey(L"DOS Header passed. Loading PE32+ Image..\r\n"); + Print(L"e_lfanew: 0x%x\r\n", DOSHeader.e_lfanew); + + Print(L"File starts at 0x%llx\r\nKernel starts at 0x%llx\r\n", &KernelFileLocation, &KernelFileLocation + (size_t)DOSHeader.e_lfanew); +#endif + + KernelStatus = KernelFile->SetPosition(KernelFile, (size_t)DOSHeader.e_lfanew); + if (EFI_ERROR(KernelStatus)) { + Print(L"SetPosition error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + HeaderSize = sizeof(IMAGE_NT_HEADERS); + IMAGE_NT_HEADERS PEHeader; + + KernelStatus = KernelFile->Read(KernelFile, &HeaderSize, &PEHeader); + if (EFI_ERROR(KernelStatus)) { + Print(L"PEHeader Read error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + +#ifdef LOADER_DEBUG_MAIN + Print(L"PE Header Signature: 0x%x\r\n", PEHeader.Signature); +#endif + + if (PEHeader.Signature == IMAGE_NT_SIGNATURE) { + // PE File. +#ifdef LOADER_DEBUG_MAIN + AwaitKey(L"PE Header found, checking format..\r\n"); +#endif + + if (PEHeader.FileHeader.Machine == IMAGE_FILE_MACHINE_X64 && PEHeader.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC) { + // PE32+ File (64 bit). +#ifdef LOADER_DEBUG_MAIN + AwaitKey(L"PE32+ Header found..\r\n"); +#endif + + if (PEHeader.OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_EFI_APPLICATION) { + // If we get here, it was not linked as a Subsystem 10 application + // AKA, the compiler was not told to allocate UEFI compatibility. + // Thus, we error. + KernelStatus = EFI_INVALID_PARAMETER; + Print(L"Not a valid UEFI PE32+ application.\r\n"); + Print(L"File was linked as a Subsystem %hu application.\r\n", PEHeader.OptionalHeader.Subsystem); + return KernelStatus; + } + + KernelIsMSABI = 1; + +#ifdef LOADER_DEBUG_MAIN + AwaitKey(L"UEFI PE32+ Header found..\r\n"); +#endif + + size_t Iter, DataSize = 0, NumDataSections = (size_t)PEHeader.FileHeader.NumberOfSections; + HeaderSize = IMAGE_SIZEOF_SECTION_HEADER * NumDataSections; + +#ifdef LOADER_DEBUG_PE + Print(L"DataSections: %llu, Size of SectionTable: %llu\r\n", NumDataSections, HeaderSize); + AwaitKey(L"\0"); +#endif + + IMAGE_SECTION_HEADER* SectionTable; + + KernelStatus = BS->AllocatePool(EfiBootServicesData, HeaderSize, (void**)&SectionTable); + if (EFI_ERROR(KernelStatus)) { + Print(L"SectionTable Allocate error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + + KernelStatus = KernelFile->Read(KernelFile, &HeaderSize, &SectionTable[0]); + if (EFI_ERROR(KernelStatus)) { + Print(L"SectionTable Read error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + for (Iter = 0; Iter < NumDataSections; Iter++) { + IMAGE_SECTION_HEADER* CurrentSection = &SectionTable[Iter]; + +#ifdef LOADER_DEBUG_PE + Print(L"Current Section: Address = 0x%x, Size = 0x%x, Address + Size = 0x%x\r\n", CurrentSection->VirtualAddress, CurrentSection->Misc.VirtualSize, CurrentSection->VirtualAddress + CurrentSection->Misc.VirtualSize); +#endif + + DataSize = (DataSize > (size_t)(CurrentSection->VirtualAddress + CurrentSection->Misc.VirtualSize) ? DataSize : (size_t)(CurrentSection->VirtualAddress + CurrentSection->Misc.VirtualSize)); + + } + +#ifdef LOADER_DEBUG_PE + Print(L"Virtual Size: 0x%llx\r\n", DataSize); + AwaitKey(L"Section Header table passed.\r\n"); +#endif + + size_t Header_Size = (size_t)PEHeader.OptionalHeader.SizeOfHeaders; + +#ifdef LOADER_DEBUG_PE + Print(L"Total image size: %llu Bytes\r\nHeaders total size: %llu Bytes\r\n", (UINT64)PEHeader.OptionalHeader.SizeOfImage, Header_Size); +#endif + + // Kernel file is max 4GB. + + size_t Pages = EFI_SIZE_TO_PAGES(DataSize); + KernelPages = Pages; + +#ifdef LOADER_DEBUG_PE + Print(L"Kernel is %llu pages long.\r\n", Pages); + Print(L"The Kernel is expected to start at 0x%llx\r\n", PEHeader.OptionalHeader.ImageBase); + AwaitKey(L"\0"); +#ifdef MEMORY_SHOW + PrintMemoryMap(); + AwaitKey(L"Done printing memory map.\r\n"); +#endif +#endif + EFI_PHYSICAL_ADDRESS KernelAllocatedMemory = PEHeader.OptionalHeader.ImageBase; + + KernelStatus = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, Pages, &KernelAllocatedMemory); + if (EFI_ERROR(KernelStatus)) { + Print(L"Section Pages Allocate error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + +#ifdef LOADER_DEBUG_PE + Print(L"Allocated Memory starts at 0x%llx\r\n", KernelAllocatedMemory); +#ifdef MEMORY_SHOW + PrintMemoryMap(); + AwaitKey(L"Done printing memory map.\r\n"); +#endif + AwaitKey(L"Zeroing allocated memory.\r\n"); + +#endif + + ZeroMem((void*)KernelAllocatedMemory, (Pages << EFI_PAGE_SHIFT)); + +#ifndef MEMORY_CHECKING_DISABLED + if (VerifyZero(Pages << EFI_PAGE_SHIFT, KernelAllocatedMemory)) { +#ifdef MEMORY_CHECK + Print(L"Data left in kernel space. Stand by..\r\n"); +#endif + + size_t MemoryCheck = IMAGE_DOS_SIGNATURE; + + if (Compare((EFI_PHYSICAL_ADDRESS*)KernelAllocatedMemory, &MemoryCheck, 2)) { + + Print(L"System was reset. No issues.\r\n"); + } + else { + +#ifdef MEMORY_CHECK + Print(L"Data remains. Data does not belong to kernel. Searching for next available space.\r\n"); +#endif + KernelStatus = BS->FreePages(KernelAllocatedMemory, KernelPages); + if (EFI_ERROR(KernelStatus)) { + Print(L"Kernel Section Pages free error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + // Find a new address, bottom-up. + EFI_PHYSICAL_ADDRESS TempAddress = 0; + EFI_PHYSICAL_ADDRESS OldLocation = KernelAllocatedMemory; + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + while (KernelStatus != EFI_SUCCESS) { // Keep going until we break even + if (KernelStatus == EFI_NOT_FOUND) { // Some firmwares start at > 0 + + TempAddress = FindFreeAddress(Pages, TempAddress); // Find the next free pages + + if (TempAddress == OldLocation) // We know that the next available space, which is OldLocation, is not valid. Continue.. + TempAddress = FindFreeAddress(Pages, TempAddress); + + else if (TempAddress >= 0x100000000) // We need to stay below 4GB for PE32+ to work. + TempAddress = ~0ULL; + + } + else if (EFI_ERROR(KernelStatus)) { + Print(L"Could not find an address for the PE32+ file in memory. Error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + if (TempAddress == ~0ULL) { + Print(L"No memory marked as EfiConventionalMemory. Reboot.\r\n"); + return KernelStatus; + } + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + } + + KernelAllocatedMemory = TempAddress; + + while ((TempAddress != ~0ULL) && VerifyZero(Pages << EFI_PAGE_SHIFT, KernelAllocatedMemory)) { + if (Compare((EFI_PHYSICAL_ADDRESS*)KernelAllocatedMemory, &MemoryCheck, 2)) { +#ifdef MEMORY_CHECK + Print(L"System has been reset. No issues.\r\n"); +#endif + break; + } + else { +#ifdef MEMORY_CHECK + Print(L"Still searching.. Currently at 0x%llx\r\n", KernelAllocatedMemory); +#endif + + KernelStatus = BS->FreePages(KernelAllocatedMemory, Pages); + if (EFI_ERROR(KernelStatus)) { + Print(L"Could not free pages for PE32+ sections (loop). Error code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = EFI_NOT_FOUND; + while ((KernelStatus != EFI_SUCCESS) && (KernelStatus != ~0ULL)) { + if (KernelStatus == EFI_NOT_FOUND) { + TempAddress = FindFreeAddress(Pages, TempAddress); + // Make sure the new address isn't the known bad one + if (TempAddress == OldLocation) + TempAddress = FindFreeAddress(Pages, TempAddress); + + else if (TempAddress >= 0x100000000) {// Need to stay under 4GB for PE32+ + TempAddress = ~0ULL; // Get out of this loop, do a more thorough check + break; + } + // This loop will run until we get a good address (shouldn't be more than once, if ever) + } + else if (EFI_ERROR(TempAddress)) { + // EFI_OUT_OF_RESOURCES means the firmware's just not gonna load anything. + Print(L"Could not get an address for PE32+ pages (loop). Error code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + } + + KernelAllocatedMemory = TempAddress; + } // Else + } // VerifyZero while + + if (KernelAllocatedMemory == ~0ULL) { + +#ifndef NO_BY_PAGE_SEARCH + + Print(L"Preparing a search for free memory on a page-by-page basis.\r\nThis typically takes a few seconds."); + Print(L"About to search.."); +#endif + + TempAddress = 0x80000000 - EFI_PAGE_SIZE; + KernelStatus = EFI_NOT_FOUND; + while (KernelStatus != EFI_SUCCESS) { + if (KernelStatus == EFI_NOT_FOUND) { + TempAddress = FindFreeAddress_ByPage(Pages, TempAddress); + + if (TempAddress == OldLocation) + TempAddress = FindFreeAddress_ByPage(Pages, TempAddress); + else if (TempAddress >= 0x100000000) + TempAddress = FindFreeAddress(Pages, 0); + } + + else if (EFI_ERROR(KernelStatus)) { + Print(L"Could not get an address for PE32+ file when searching page-by-page. Please reboot. Error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + if (TempAddress == ~0ULL) { + Print(L"There is no free memory. Something is wrong. Investigate.\r\n"); + return KernelStatus; + } + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + + } + + KernelAllocatedMemory = TempAddress; + + // Last checks. + + while (VerifyZero(Pages << EFI_PAGE_SHIFT, KernelAllocatedMemory)) { + if (Compare((EFI_PHYSICAL_ADDRESS*)KernelAllocatedMemory, &MemoryCheck, 2)) { + + Print(L"System has probably been reset, but there may be some issues in future.\r\n"); + break; + } + else { + Print(L"Still searching. Now at 0x%llx", KernelAllocatedMemory); + + KernelStatus = BS->FreePages(KernelAllocatedMemory, Pages); + if (EFI_ERROR(KernelStatus)) { + Print(L"Could not free pages for the PE32+ file. Error 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = EFI_NOT_FOUND; + while (KernelStatus != EFI_SUCCESS) { + if (KernelStatus == EFI_NOT_FOUND) { + TempAddress = FindFreeAddress_ByPage(Pages, TempAddress); + + if (TempAddress == OldLocation) + TempAddress = FindFreeAddress_ByPage(Pages, TempAddress); + else if (TempAddress >= 0x100000000) { + Print(L"There is too much junk memory below 4GB. We are 3 levels deep, and still nothing. Use a 64-bit ELF or a MACH-0 binary.\r\n"); + TempAddress = FindFreeAddress(Pages, 0); + } + + else if (EFI_ERROR(KernelStatus)) { + Print(L"Could not get an address for the PE32+ binary. Error 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + if (TempAddress == ~0ULL) { + Print(L"Something is wrong with the first 4GB of RAM. PE32+ does not support being loaded into memory >4GB, so use a 64-bit ELF or a MACH-0 kernel.\r\n"); + return KernelStatus; + } + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + } + + KernelAllocatedMemory = TempAddress; + + } // While + } // Else +#endif + } // End final + +#ifdef MEMORY_CHECK + Print(L"Found a good memory address at 0x%llx\r\n", KernelAllocatedMemory); +#endif + } // End finding memory + } // End workaround + } + else { // The first address we used was valid + + Print(L"Allocated memory is zero.\r\n"); + } +#ifdef LOADER_DEBUG_PE + AwaitKey(L"Allocated pages okay.\r\n"); +#ifdef MEMORY_SHOW + PrintMemoryMap(); + AwaitKey(L"Done printing memory map.\r\n"); +#endif + + Print(L"\nLoading file headers..\r\n"); + Print(L"Check this.\r\n The Section address is 0x%llx.\r\n The data there is 0x%016llx%016llx, which should be 0.\r\n", KernelAllocatedMemory, *(EFI_PHYSICAL_ADDRESS*)(KernelAllocatedMemory + 8), *(EFI_PHYSICAL_ADDRESS*)KernelAllocatedMemory); + AwaitKey(L"\0"); +#endif + + KernelStatus = KernelFile->SetPosition(KernelFile, 0); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error setting position for mapping: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = KernelFile->Read(KernelFile, &HeaderSize, (EFI_PHYSICAL_ADDRESS*)KernelAllocatedMemory); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error reading header data for mapping: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + +#ifdef LOADER_DEBUG_PE + // The target is Little-Endian, so we have to do some weird stuff when printing. + Print(L"\r\nCheck this.\r\n The Section address is 32 bytes long. It is at 0x%llx. \r\n The first 16 bytes are 0x016llx%016llx.\r\n", KernelAllocatedMemory, *(EFI_PHYSICAL_ADDRESS*)(KernelAllocatedMemory + 8), *(EFI_PHYSICAL_ADDRESS*)KernelAllocatedMemory); + Print(L"The next 16 bytes are 0x%016llx%016llx\r\n", *(EFI_PHYSICAL_ADDRESS*)(KernelAllocatedMemory + HeaderSize + 8), *(EFI_PHYSICAL_ADDRESS*)(KernelAllocatedMemory + HeaderSize)); + Print(L"The next 16 bytes should be 0s: 0x%016llx%016llx\r\n", *(EFI_PHYSICAL_ADDRESS *)(KernelAllocatedMemory + HeaderSize + 8), *(EFI_PHYSICAL_ADDRESS *)(KernelAllocatedMemory + HeaderSize)); + AwaitKey(L"\0"); +#endif + for (Iter = 0; Iter < NumDataSections; Iter++) { + IMAGE_SECTION_HEADER* CurrentHeader = &SectionTable[Iter]; + size_t RawSize = (size_t)CurrentHeader->SizeOfRawData; + EFI_PHYSICAL_ADDRESS SectionAddress = KernelAllocatedMemory + (size_t)CurrentHeader->VirtualAddress; + +#ifdef LOADER_DEBUG_PE + Print(L"\n%llu. current section address: 0x%x, RawDataSize: 0x%llx\r\n", Iter + 1, CurrentHeader->VirtualAddress, RawSize); + Print(L"current destination address: 0x%llx, AllocatedMemory base: 0x%llx\r\n", SectionAddress, KernelAllocatedMemory); + Print(L"PointerToRawData: 0x%llx\r\n", (UINT64)CurrentHeader->PointerToRawData); + Print(L"Check:\r\nSectionAddress: 0x%llx\r\nData there: 0x%016llx%016llx (should be 0)\r\n", SectionAddress, *(EFI_PHYSICAL_ADDRESS*)(SectionAddress + 8), *(EFI_PHYSICAL_ADDRESS *)SectionAddress); // Print the first 128 bits of data at that address to compare + Print(L"About to load section %llu of %llu...\r\n", Iter + 1, NumDataSections); + AwaitKey(L"\0"); +#endif + + // Skip past the headers, we're done with them. + + KernelStatus = KernelFile->SetPosition(KernelFile, (size_t)CurrentHeader->PointerToRawData); + if (EFI_ERROR(KernelStatus)) { + Print(L"Section SetPosition error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + if (RawSize != 0) { + KernelStatus = KernelFile->Read(KernelFile, &RawSize, (EFI_PHYSICAL_ADDRESS*)SectionAddress); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error reading section: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + } + } + + if (SectionTable) { + KernelStatus = BS->FreePool(SectionTable); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error freeing SectionTable: 0x%llx\r\n", KernelStatus); + AwaitKey(L"\0"); + } + } + +#ifdef LOADER_DEBUG_PE + AwaitKey(L"\nLoaded file sections into pages.\r\n"); +#endif + + if ((KernelAllocatedMemory != PEHeader.OptionalHeader.ImageBase) && (PEHeader.OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_BASERELOC)) { + // UEFI says we need to relocate a few things.. + + IMAGE_BASE_RELOCATION* RelocationDirectoryBase; + IMAGE_BASE_RELOCATION* RelocationTableEnd; + + RelocationDirectoryBase = (IMAGE_BASE_RELOCATION*)(KernelAllocatedMemory + (size_t)PEHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); + RelocationTableEnd = (IMAGE_BASE_RELOCATION*)(KernelAllocatedMemory + (size_t)PEHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size + (size_t)PEHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); + + size_t Difference; + if (KernelAllocatedMemory > PEHeader.OptionalHeader.ImageBase) { + Difference = KernelAllocatedMemory - PEHeader.OptionalHeader.ImageBase; + +#ifdef LOADER_DEBUG_PE + Print(L"The kernel was allocated space starting at 0x%llx, but the Header says it needs to be at 0x%llx, which is 0x%llx bytes off.\r\n", KernelAllocatedMemory, PEHeader.OptionalHeader.ImageBase, Difference); +#endif + } + else { + Difference = PEHeader.OptionalHeader.ImageBase - KernelAllocatedMemory; + +#ifdef LOADER_DEBUG_PE + Print(L"The kernel was allocated space starting at 0x%llx, but the Header says it needs to be at 0x%llx, which is -0x%llx bytes off.\r\n", KernelAllocatedMemory, PEHeader.OptionalHeader.ImageBase, Difference); +#endif + + } + + size_t NumRelocations_PerChunk; + + for (; (RelocationDirectoryBase->SizeOfBlock) && (RelocationDirectoryBase < RelocationTableEnd); ) { + +#ifdef LOADER_DEBUG_PE + Print(L"\nSize of a single block: %u bytes.\r\nThe relocation table starts at 0x%llx and ends at 0x%llx.\r\n", RelocationDirectoryBase->SizeOfBlock, RelocationDirectoryBase, RelocationTableEnd); +#endif + + EFI_PHYSICAL_ADDRESS CurrentPage = KernelAllocatedMemory + (size_t)RelocationDirectoryBase->VirtualAddress; + uint16_t* RelocationData = (uint16_t*)((uint8_t*)RelocationDirectoryBase + IMAGE_SIZEOF_BASE_RELOCATION); + NumRelocations_PerChunk = (RelocationDirectoryBase->SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION) / sizeof(uint16_t); + +#ifdef LOADER_DEBUG_PE + Print(L"Data to be relocated: 0x%hx, which starts at page 0x%llx.\r\nThere are %llu relocations in this chunk of data.\r\nAbout to start relocating..", RelocationData, CurrentPage, NumRelocations_PerChunk); + AwaitKey(L"\0"); +#endif + + for (Iter = 0; Iter < NumRelocations_PerChunk; Iter++) { + if (RelocationData[Iter] >> EFI_PAGE_SHIFT == IMAGE_REL_BASED_ABSOLUTE /* == 0 */) { + +#ifdef LOADER_DEBUG_PE + Print(L"%llu of %llu, this is padding.\r\n", Iter + 1, NumRelocations_PerChunk); +#endif + } + else if (RelocationData[Iter] >> EFI_PAGE_SHIFT == IMAGE_REL_BASED_DIR64 /* == 10 */) { + +#ifdef LOADER_DEBUG_PE + Print(L"%llu of %llu, data to be relocated. Data at %llu is 0x%hx.\r\n", Iter + 1, NumRelocations_PerChunk, Iter, RelocationData[Iter]); +#endif + if (KernelAllocatedMemory > PEHeader.OptionalHeader.ImageBase) { +#ifdef LOADER_DEBUG_PE + Print(L"We are at address 0x%llx, which is in page 0x%llx, and contains the data 0x%llx\r\n", (size_t*)((uint8_t*)CurrentPage + (RelocationData[Iter] & EFI_PAGE_MASK)), CurrentPage, *((size_t*)((uint8_t*)CurrentPage + (RelocationData[Iter] & EFI_PAGE_MASK)))); +#endif + + // Modify the pointer to point to the correct location. + // This is dangerous, but it works in QEMU and on hardware, and fixes a bug on the latter. + *((size_t*)((uint8_t*)CurrentPage + (RelocationData[Iter] & EFI_PAGE_MASK))) += Difference; + +#ifdef LOADER_DEBUG_PE + Print(L"The difference between the data and its destination was 0x%llx, and the corrected data is 0x%llx\r\n", Difference, *((size_t*)((uint8_t*)CurrentPage + (RelocationData[Iter] & EFI_PAGE_MASK)))); +#endif + + } + else { +#ifdef LOADER_DEBUG_PE + Print(L"Page: 0x%llx, Current Address: 0x%llx, Data there: 0x%llx\r\n", CurrentPage, (size_t*)((uint8_t*)CurrentPage + (RelocationData[Iter] & EFI_PAGE_MASK)), *((size_t*)((uint8_t*)CurrentPage + (RelocationData[Iter] & EFI_PAGE_MASK)))); +#endif + + *((size_t*)((uint8_t*)CurrentPage + (RelocationData[Iter] & EFI_PAGE_MASK))) -= Difference; + +#ifdef LOADER_DEBUG_PE + Print(L"Delta: -0x%llx, Corrected Data there: 0x%llx\r\n", Difference, *((size_t*)((uint8_t*)CurrentPage + (RelocationData[Iter] & EFI_PAGE_MASK)))); +#endif + } + } + else { + KernelStatus = EFI_INVALID_PARAMETER; + Print(L"Something happened while relocating. i: %llu, Relocation starting at 0x%llx\r\n", Iter, RelocationDirectoryBase); + } + } + + RelocationDirectoryBase = (IMAGE_BASE_RELOCATION*)((uint8_t*)RelocationDirectoryBase + RelocationDirectoryBase->SizeOfBlock); + + } + } + else { +#ifdef LOADER_DEBUG_PE + Print(L"No relocation necessary, skipping..\r\n"); +#endif + } + + if (KernelStatus == EFI_INVALID_PARAMETER) { + KernelStatus = BS->FreePages(KernelAllocatedMemory, Pages); + if (EFI_ERROR(KernelStatus)) + Print(L"Error freeing pages, code 0x%llx\r\n", KernelStatus); + + KernelStatus = EFI_INVALID_PARAMETER; + Print(L"Relocation failed.\r\n"); + return KernelStatus; + } + + KernelBase = KernelAllocatedMemory; + // HeaderLocation now contains the entry point of the kernel. + HeaderLocation = KernelAllocatedMemory + (size_t)PEHeader.OptionalHeader.AddressOfEntryPoint; + } + else { + KernelStatus = EFI_INVALID_PARAMETER; + Print(L"The kernel file is 32-bit. Syncboot only accepts 64-bit files.\r\n"); + return KernelStatus; + } + } // End of PE32+ + else { + // Not PE32+, but still PE. We can at least try. + + AwaitKey(L"Loading kernel, but no promises."); + + HeaderSize = (size_t)(512 * DOSHeader.e_cp + DOSHeader.e_cblp - 16 * DOSHeader.e_cparhdr); + size_t Pages = EFI_SIZE_TO_PAGES(HeaderSize); + KernelPages = Pages; + +#ifdef LOADER_DEBUG_DOS + Print(L"e_cp: %hu, e_cblp: %hu, e_cparhdr: %hu\r\n", DOSHeader.e_cp, DOSHeader.e_cblp, DOSHeader.e_cparhdr); + Print(L"File size: %llu, Load Module size: %llu, Pages taken: %llu\r\n", HeaderSize + 16 * (size_t)DOSHeader.e_cparhdr, HeaderSize, Pages); +#endif + + EFI_PHYSICAL_ADDRESS DOSMem = 0x100; + + KernelStatus = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, Pages, &DOSMem); + if (EFI_ERROR(KernelStatus)) { + Print(L"Could not allocate any pages for MZ load. Error: 0x%llu\r\n", KernelStatus); + return KernelStatus; + } + +#ifdef LOADER_DEBUG_DOS + Print(L"DOSMem located at 0x%llx\r\n", DOSMem); +#ifdef MEMORY_SHOW + PrintMemoryMap(); + AwaitKey(L"Done printing memory map.\r\n"); +#endif + AwaitKey(L"Zeroing space for kernel..\r\n"); +#endif + ZeroMem((void*)DOSMem, (Pages << EFI_PAGE_SHIFT)); + +#ifdef LOADER_DEBUG_DOS + AwaitKey(L"Memory allocated and prepared for kernel."); +#endif + + KernelStatus = KernelFile->SetPosition(KernelFile, (size_t)DOSHeader.e_cparhdr * 16); + if (EFI_ERROR(KernelStatus)) { + Print(L"Reset SetPosition error: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + +#ifdef LOADER_DEBUG_DOS + Print(L"Current destination: 0x%llx. DOSMem starts at 0x%llx, and is 0x%llx bytes long.\r\n", DOSMem, DOSMem, HeaderSize); + Print(L"Check this: DOSMem is at 0x%llx and contains the data 0x%016llx%016llx, which should be 0.\r\n", DOSMem, *(EFI_PHYSICAL_ADDRESS*)(DOSMem + 8), *(EFI_PHYSICAL_ADDRESS*)DOSMem); + AwaitKey(L"\0"); +#endif + + KernelStatus = KernelFile->Read(KernelFile, &HeaderSize, (EFI_PHYSICAL_ADDRESS*)DOSMem); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error reading load module. Code 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelBase = DOSMem; + HeaderLocation = DOSMem + (size_t)DOSHeader.e_ip * 16; + +#ifdef LOADER_DEBUG_DOS + Print(L"\r\nHeader located at 0x%llx. DOS Memory located at 0x%llx. Kernel entry point at 0x%llx.\r\n", HeaderLocation, DOSMem, (size_t)DOSHeader.e_ip * 16); + AwaitKey(L"\0"); +#endif + + KernelStatus = IMAGE_FILE_EXECUTABLE_IMAGE; + Print(L"\r\n Error 0x%llx: This file cannot be run in UEFI mode.\r\n", KernelStatus); + return KernelStatus; + } + } // End of PE Loader + else { + // Load ELF file + KernelStatus = KernelFile->SetPosition(KernelFile, 0); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error resetting position in kernel file. Code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + Elf64_Ehdr ELFHeader; + HeaderSize = sizeof(ELFHeader); + + KernelStatus = KernelFile->Read(KernelFile, &HeaderSize, &ELFHeader); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error reading ELF header. Code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + if (Compare(&ELFHeader.e_ident[EI_MAG0], ELFMAG, SELFMAG)) { + // ELF Header Matches ( \177ELF ) Magic Number + +#ifdef LOADER_DEBUG_ELF + AwaitKey(L"Kernel file is ELF.\r\n"); +#endif + + if (ELFHeader.e_ident[EI_CLASS] == ELFCLASS64 && ELFHeader.e_machine == EM_X86_64) { + +#ifdef LOADER_DEBUG_ELF + AwaitKey(L"Kernel ELF file is 64 bit.\r\n"); +#endif + + if (ELFHeader.e_type != ET_DYN) { + KernelStatus = EFI_INVALID_PARAMETER; + Print(L"Not a PIE ELF64 Executable.\r\ne_type is 0x%hx\r\n", ELFHeader.e_type); + return KernelStatus; + } + +#ifdef LOADER_DEBUG_ELF + AwaitKey(L"Kernel ELF64 file is executable and PIE.\r\n"); +#endif + + size_t Iter, + VirtualSize = 0, + VirtualMin = ~0ULL, + ProgramHeaders = (size_t)ELFHeader.e_phnum; + + HeaderSize = ProgramHeaders * (size_t)ELFHeader.e_phentsize; + + Elf64_Phdr* HeadersTable; + + KernelStatus = BS->AllocatePool(EfiBootServicesData, HeaderSize, (void**)&HeadersTable); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error allocating Header Table. Code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = KernelFile->SetPosition(KernelFile, ELFHeader.e_phoff); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error setting file position for mapping. Code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = KernelFile->Read(KernelFile, &HeaderSize, &HeadersTable[0]); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error reading ELF headers. Code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + for (Iter = 0; Iter < ProgramHeaders; Iter++) { + Elf64_Phdr* CurrentHeader = &HeadersTable[Iter]; + if (CurrentHeader->p_type == PT_LOAD) { +#ifdef LOADER_DEBUG_ELF + Print(L"Current header starts at 0x%x, is 0x%x bytes long, and finishes at 0x%x.\r\n", CurrentHeader->p_vaddr, CurrentHeader->p_memsz, CurrentHeader->p_vaddr + CurrentHeader->p_memsz); +#endif + VirtualSize = (VirtualSize > (CurrentHeader->p_vaddr + CurrentHeader->p_memsz) ? VirtualSize : (CurrentHeader->p_vaddr + CurrentHeader->p_memsz)); + VirtualMin = (VirtualMin < (CurrentHeader->p_vaddr) ? VirtualMin : (CurrentHeader->p_vaddr)); + } + } + + size_t Pages = EFI_SIZE_TO_PAGES(VirtualSize - VirtualMin); + KernelPages = Pages; + + EFI_PHYSICAL_ADDRESS KernelAllocatedMemory = 0x400000; +#ifdef LOADER_DEBUG_ELF + Print(L"Kernel will be loaded into 0x%llx\r\n", KernelAllocatedMemory); +#endif + + KernelStatus = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, Pages, &KernelAllocatedMemory); + if (EFI_ERROR(KernelStatus)) { + Print(L"Could not allocate pages for ELF kernel. Code 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + +#ifdef LOADER_DEBUG_ELF + Print(L"Kernel was loaded into 0x%llx\r\n", KernelAllocatedMemory); +#ifdef MEMORY_SHOW + PrintMemoryMap(); + AwaitKey(L"Done printing memory map.\r\n"); +#endif +#endif + + ZeroMem((void*)KernelAllocatedMemory, (Pages << EFI_PAGE_SHIFT)); + +#ifndef MEMORY_CHECKING_DISABLED + if (VerifyZero(Pages << EFI_PAGE_SHIFT, KernelAllocatedMemory)) { + +#ifdef MEMORY_CHECK + Print(L"Data remains in space allocated for kernel. Checking cause..\r\n"); +#endif + + if (Compare((EFI_PHYSICAL_ADDRESS*)KernelAllocatedMemory, ELFMAG, SELFMAG)) { +#ifdef MEMORY_CHECK + Print(L"Header found. System was likely reset and memory was not purged. Boot can continue.\r\n"); +#endif + } + else { +#ifdef MEMORY_CHECK + Print(L"Data does not belong to Sync. Searching for next available space.\r\n"); +#endif + KernelStatus = BS->FreePages(KernelAllocatedMemory, Pages); + if (EFI_ERROR(KernelStatus)) { + Print(L"Could not free ELF file pages. Error 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + EFI_PHYSICAL_ADDRESS TempAddress = 0; + EFI_PHYSICAL_ADDRESS OldLocation = KernelAllocatedMemory; + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &OldLocation); + while (KernelStatus != EFI_SUCCESS) { + if (KernelStatus == EFI_NOT_FOUND) { + TempAddress = FindFreeAddress(Pages, TempAddress); + if (TempAddress == OldLocation) + TempAddress = FindFreeAddress(Pages, TempAddress); + } + else if (EFI_ERROR(KernelStatus)) { + Print(L"Could not get an address for the kernel. Please reboot. Code 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + if (TempAddress == ~0ULL) { + Print(L"There is no memory marked as EfiConventionalMemory. This is a problem. Please restart.\r\n"); + return KernelStatus; + } + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + } + KernelAllocatedMemory = TempAddress; + + // Double check we've found a good address + + while ((TempAddress != ~0ULL) && VerifyZero(Pages << EFI_PAGE_SHIFT, KernelAllocatedMemory)) { + // See if there's a kernel in there + if (Compare((EFI_PHYSICAL_ADDRESS*)KernelAllocatedMemory, ELFMAG, SELFMAG)) { +#ifdef MEMORY_CHECK + Print(L"Data belongs to kernel. System was probably reset. Boot can continue, but be careful.\r\n"); +#endif + break; + } + else { + // More junk. Keep looking. +#ifdef MEMORY_CHECK + Print(L"Data does not belong to Sync. Will keep searching for available space.\r\n"); + +#endif + + KernelStatus = BS->FreePages(KernelAllocatedMemory, Pages); + if (EFI_ERROR(KernelStatus)) { + Print(L"Could not free ELF kernel pages. Code 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = EFI_NOT_FOUND; + while ((KernelStatus != EFI_SUCCESS) && (TempAddress != ~0ULL)) { + if (KernelStatus == EFI_NOT_FOUND) { + + TempAddress = FindFreeAddress(Pages, TempAddress); + if (TempAddress == OldLocation) + TempAddress = FindFreeAddress(Pages, TempAddress); + } + else if (EFI_ERROR(KernelStatus)) { + Print(L"Could not get an address for the EFI file. Error 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + } + + KernelAllocatedMemory = TempAddress; + } + } + + if (KernelAllocatedMemory == ~0ULL) { +#ifdef NO_BY_PAGE_SEARCH + Print(L"No space available for kernel. Enable page searching, or check RAM.\r\n"); + return KernelStatus; +#endif + +#ifndef NO_BY_PAGE_SEARCH + +#ifdef MEMORY_CHECK + AwaitKey(L"Starting page-wise search.\r\n"); +#endif + + TempAddress = FindFreeAddress(Pages, 0); + + KernelStatus = EFI_NOT_FOUND; + while (KernelStatus != EFI_SUCCESS) { + if (KernelStatus == EFI_NOT_FOUND) { + TempAddress = FindFreeAddress_ByPage(Pages, TempAddress); + if (TempAddress == OldLocation) + TempAddress = FindFreeAddress_ByPage(Pages, TempAddress); + } + else if (EFI_ERROR(KernelStatus)) { + Print(L"Could not get space for the kernel, even by searching pagewise. Consider checking the system RAM. Code 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + if (TempAddress == ~0ULL) { + Print(L"System has no free memory. Investigate.\r\n"); + return KernelStatus; + } + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + } + + KernelAllocatedMemory = TempAddress; + + while ((TempAddress != ~0ULL) && VerifyZero(Pages << EFI_PAGE_SHIFT, KernelAllocatedMemory)) {// Loop this in case the firmware is really screwed + // It's not empty :( + + if (Compare((EFI_PHYSICAL_ADDRESS*)KernelAllocatedMemory, ELFMAG, SELFMAG)) { +#ifdef MEMORY_CHECK + Print(L"System appears to have been reset. No issues.\r\n"); +#endif + + break; + } + else + { // Gotta keep looking for a good address + +#ifdef MEMORY_CHECK + Print(L"Still searching... 0x%llx\r\n", KernelAllocatedMemory); +#endif + + KernelStatus = BS->FreePages(KernelAllocatedMemory, Pages); + if (EFI_ERROR(KernelStatus)) { + Print(L"Could not free Pages for ELF sections (loop). Error code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + // Allocate a new address + KernelStatus = EFI_NOT_FOUND; + while ((KernelStatus != EFI_SUCCESS) && (TempAddress != ~0ULL)) { + if (KernelStatus == EFI_NOT_FOUND) { + TempAddress = FindFreeAddress(Pages, TempAddress); + if (TempAddress == OldLocation) { + // Get a new address if it is + TempAddress = FindFreeAddress(Pages, TempAddress); + } + } + else if (EFI_ERROR(KernelStatus)) { + // EFI_OUT_OF_RESOURCES means the firmware's just not gonna load anything. + Print(L"Could not get an address for ELF Pages (loop). Error code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + } // loop + + KernelAllocatedMemory = TempAddress; + + } // else + } // End VerifyZeroMem while loop + + if (KernelAllocatedMemory == ~0ULL) { // TempAddress is also -1 + +#ifdef NO_PAGE_BY_PAGE_SEARCH + Print(L"No easy addresses found with enough space and containing only zeros.\r\nConsider enabling page-by-page search.\r\n"); + return KernelStatus; +#endif + +#ifdef MEMORY_CHECK + Print(L"Performing page-by-page search.\r\nThis might take a while...\r\n"); +#endif + +#ifdef MEMORY_CHECK + AwaitKey(L"About to search page by page\r\n"); +#endif + + TempAddress = FindFreeAddress(Pages, 0); // Start from the first suitable EfiConventionalMemory address. + // Allocate the page's address to the kernel + KernelStatus = EFI_NOT_FOUND; + while (KernelStatus != EFI_SUCCESS) { + if (KernelStatus == EFI_NOT_FOUND) { + TempAddress = FindFreeAddress_ByPage(Pages, TempAddress); + // Make sure the new address isn't the known bad one + if (TempAddress == OldLocation) { + // Get a new address if it is + TempAddress = FindFreeAddress_ByPage(Pages, TempAddress); + } + // Adresses very well might be > 4GB with the filesizes these are allowed to be + } + else if (EFI_ERROR(KernelStatus)) { + Print(L"Could not get an address for ELF Pages by page. Error code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + if (TempAddress == ~0ULL) { + Print(L"System has no free RAM. Investigate.\r\n"); + return KernelStatus; + } + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + } + + KernelAllocatedMemory = TempAddress; + + while (VerifyZero(Pages << EFI_PAGE_SHIFT, KernelAllocatedMemory)) { + if (Compare((EFI_PHYSICAL_ADDRESS*)KernelAllocatedMemory, ELFMAG, SELFMAG)) { +#ifdef MEMORY_CHECK + Print(L"System might have been reset. Hopefully no issues.\r\n"); +#endif + break; + } + else { + +#ifdef MEMORY_CHECK + Print(L"Still searching by page... 0x%llx\r\n", KernelAllocatedMemory); +#endif + + KernelStatus = BS->FreePages(KernelAllocatedMemory, Pages); + if (EFI_ERROR(KernelStatus)) { + Print(L"Could not free Pages for ELF sections by page (loop). Error code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + KernelStatus = EFI_NOT_FOUND; + while (KernelStatus != EFI_SUCCESS) { + if (KernelStatus == EFI_NOT_FOUND) { + TempAddress = FindFreeAddress_ByPage(Pages, TempAddress); + if (TempAddress == OldLocation) { + TempAddress = FindFreeAddress_ByPage(Pages, TempAddress); + } + } + else if (EFI_ERROR(KernelStatus)) { + Print(L"Could not get an address for ELF Pages by page (loop). Error code: 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + if (KernelAllocatedMemory == ~0ULL) { + return KernelStatus; + } + + KernelStatus = BS->AllocatePages(AllocateAddress, EfiLoaderData, Pages, &TempAddress); + } + + KernelAllocatedMemory = TempAddress; + + } // else + } // end ByPage VerifyZeroMem loop +#endif // End BY_PAGE_SEARCH section + } // End workarounds +#endif +#ifdef MEMORY_CHECK + Print(L"Found an address capable of storing the kernel: %llx\r\n", KernelAllocatedMemory); +#endif + } + } + } + else { + Print(L"The address 0x%llx is good enough to hold the kernel.\r\n", KernelAllocatedMemory); + + } + + Print(L"Kernel was allocated space starting at 0x%llx\r\n", KernelAllocatedMemory); + + // We don't need to copy ELF headers.. + for (Iter = 0; Iter < ProgramHeaders; Iter++) { + Elf64_Phdr* CurrentHeader = &HeadersTable[Iter]; + size_t RawSize = CurrentHeader->p_filesz; + EFI_PHYSICAL_ADDRESS SectionAddress = KernelAllocatedMemory + CurrentHeader->p_vaddr; + + if (CurrentHeader->p_type == PT_LOAD) { + KernelStatus = KernelFile->SetPosition(KernelFile, CurrentHeader->p_offset); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error setting position of ELF Segment. Code 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + if (RawSize != 0) { + KernelStatus = KernelFile->Read(KernelFile, &RawSize, (EFI_PHYSICAL_ADDRESS*)SectionAddress); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error reading ELF segment. Code 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + } + } + else { +#ifdef LOADER_DEBUG_ELF + Print(L"Section %x is not a PT_LOAD section. It is of type 0x%x.\r\n", Iter, CurrentHeader->p_type); +#endif + } + } + + if (HeadersTable) { + KernelStatus = BS->FreePool(HeadersTable); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error freeing headers pool. Code 0x%llx\r\n", KernelStatus); + AwaitKey(L"\0"); + } + } + + KernelBase = KernelAllocatedMemory; + HeaderLocation = KernelAllocatedMemory + ELFHeader.e_entry; + +#ifdef LOADER_DEBUG_ELF + Print(L"Kernel file starts at 0x%llx, and the executable entry point is at 0x%x.\r\n", HeaderLocation, ELFHeader.e_entry); +#endif + } + else { + // 32 bit ELF + KernelStatus = EFI_INVALID_PARAMETER; + Print(L"Syncboot can only load 64 bit ELF files."); + return KernelStatus; + } + } + else { + // LOAD MACH-O file. + + KernelStatus = EFI_INVALID_PARAMETER; + Print(L"Unsupported file type. Please use 64-bit PE32+ or ELF64 files.\r\n"); + return KernelStatus; + + } + } + +#ifdef LOADER_DEBUG_FINAL + Print(L"Header starts at 0x%llx\r\n", HeaderLocation); + Print(L"The first 16 bytes of the header are: 0x%016llx%016llx\r\n", *(EFI_PHYSICAL_ADDRESS*)(HeaderLocation + 8), *(EFI_PHYSICAL_ADDRESS*)HeaderLocation); + + if (KernelIsMSABI) + Print(L"Kernel uses MS_ABI calling convention.\r\n"); + else + Print(L"Kernel uses SysV ABI calling convention.\r\n"); + + AwaitKey(L"\0"); + + for (size_t i = 0; i < Graphics->FBCount; i++) { + + Print(L"GPU Mode: %u of %u\r\n", Graphics->GPUs[i].Mode, Graphics->GPUs[i].MaxMode - 1); + Print(L"GPU FB: 0x%016llx\r\n", Graphics->GPUs[i].FrameBufferBase); + Print(L"GPU FB Size: 0x%016llx\r\n", Graphics->GPUs[i].FrameBufferSize); + Print(L"GPU SizeOfInfo: %u Bytes\r\n", Graphics->GPUs[i].SizeOfInfo); + Print(L"GPU Info Ver: 0x%x\r\n", Graphics->GPUs[i].Info->Version); + Print(L"GPU Info Res: %ux%u\r\n", Graphics->GPUs[i].Info->HorizontalResolution, Graphics->GPUs[i].Info->VerticalResolution); + Print(L"GPU Info PxFormat: 0x%x\r\n", Graphics->GPUs[i].Info->PixelFormat); + Print(L"GPU Info PxInfo (R,G,B,Rsvd Masks): 0x%08x, 0x%08x, 0x%08x, 0x%08x\r\n", Graphics->GPUs[i].Info->PixelInformation.RedMask, Graphics->GPUs[i].Info->PixelInformation.GreenMask, Graphics->GPUs[i].Info->PixelInformation.BlueMask, Graphics->GPUs[i].Info->PixelInformation.ReservedMask); + Print(L"GPU Info PxPerScanLine: %u\r\n", Graphics->GPUs[i].Info->PixelsPerScanLine); + AwaitKey(L"\0"); + } +#endif + + FILELOADER_PARAMS* LoaderBlock; + + KernelStatus = BS->AllocatePool(EfiLoaderData, sizeof(FILELOADER_PARAMS), (void**)LoaderBlock); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error allocating loaderblock pool. Code 0x%llx\r\n", KernelStatus); + return KernelStatus; + } + + AwaitKey(L"Preparing to exit UEFI services and call kernel..\r\n"); + + size_t MapSize = 0, MapKey, MapDescSize; + uint32_t MapDescriptorVersion; + + EFI_MEMORY_DESCRIPTOR* Map = NULL; + + // Get the map of memory after all our shenanigans, and exit the nurturing hands of mother UEFI. + KernelStatus = BS->GetMemoryMap(&MapSize, Map, &MapKey, &MapDescSize, &MapDescriptorVersion); + if (KernelStatus == EFI_BUFFER_TOO_SMALL) { + KernelStatus = BS->AllocatePool(EfiLoaderData, MapSize, (void**)&Map); + if (EFI_ERROR(KernelStatus)) { + Print(L"Memory map AllocatePool error at final stage.\r\n"); + return KernelStatus; + } + + KernelStatus = BS->GetMemoryMap(&MapSize, Map, &MapKey, &MapDescSize, &MapDescriptorVersion); + + } + + KernelStatus = BS->ExitBootServices(ImageHandle, MapKey); + if (EFI_ERROR(KernelStatus)) { + KernelStatus = BS->FreePool(Map); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error freeing Memory Map from failed EBS. Code 0x%llx\r\n", KernelStatus); + AwaitKey(L"\0"); + } + +#ifdef LOADER_DEBUG_FINAL + Print(L"First attempt at exiting Boot Services has failed with error 0x%llx. Trying again.\r\n", KernelStatus); + AwaitKey(L"\0"); +#endif + + MapSize = 0; + KernelStatus = BS->GetMemoryMap(&MapSize, Map, &MapKey, &MapDescSize, &MapDescriptorVersion); + if (KernelStatus == EFI_BUFFER_TOO_SMALL) { + KernelStatus = BS->AllocatePool(EfiLoaderData, MapSize, (void**)&Map); + if (EFI_ERROR(KernelStatus)) { + Print(L"Memory map AllocatePool error at final stage, second attempt\r\n"); + return KernelStatus; + } + KernelStatus = BS->GetMemoryMap(&MapSize, Map, &MapKey, &MapDescSize, &MapDescriptorVersion); + } + + KernelStatus = BS->ExitBootServices(ImageHandle, MapKey); + } + + if(EFI_ERROR(KernelStatus)) { + Print(L"Could not exit boot serivces. Code 0x%llx\r\n", KernelStatus); + KernelStatus = BS->FreePool(Map); + if (EFI_ERROR(KernelStatus)) { + Print(L"Error freeing Memory Map pool. Code 0x%llx\r\n", KernelStatus); + Print(L"The memory map is %llx bytes long. The key is: %llx\r\n", MapSize, MapKey); + Print(L"The map descriptor is %llx bytes long, and is of version %x.\r\n", MapDescSize, MapDescriptorVersion); + return KernelStatus; + } + } + + // Call the kernel! + LoaderBlock->UEFI_Version = UEFIVer; + LoaderBlock->Bootloader_MajorVersion = MAJOR_VER; + LoaderBlock->Bootloader_MinorVersion = MINOR_VER; + LoaderBlock->Memory_Descriptor_Version = MapDescriptorVersion; + LoaderBlock->Memory_Descriptor_Size = MapDescSize; + + LoaderBlock->Kernel_Base = KernelBase; + LoaderBlock->Kernel_Pages = KernelPages; + + LoaderBlock->ESP_Path = ESPRoot; + LoaderBlock->ESP_Path_Length = ESPRootSize; + LoaderBlock->Kernel_Path = KernelPath; + LoaderBlock->Kernel_Path_Length = KernelPathSize; + LoaderBlock->Kernel_Options = OptionLine; + LoaderBlock->Kernel_Options_Length = OptionLineLength; + LoaderBlock->RTServices = RT; + LoaderBlock->GPU_INFO = Graphics; + LoaderBlock->FileMeta = KernelFileInfo; + + LoaderBlock->ConfigTables = ConfigTables; + LoaderBlock->ConfigTables_Length = ConfigTables_Length; + + if (KernelIsMSABI) { + typedef void (__attribute__((ms_abi)) *EntryPoint)(FILELOADER_PARAMS* LP); + EntryPoint KernelEntryPoint = (EntryPoint)(HeaderLocation); + KernelEntryPoint(LoaderBlock); // Jump to kernel. Au revoir! + } + else { + // SysV ABI + typedef void(__attribute__((sysv_abi)) *EntryPoint)(FILELOADER_PARAMS* LP); + EntryPoint KernelEntryPoint = (EntryPoint)(HeaderLocation); + KernelEntryPoint(LoaderBlock); + } + + return KernelStatus; + +} + + + diff --git a/src/gfx.c b/src/gfx.c new file mode 100644 index 0000000..46e9f74 --- /dev/null +++ b/src/gfx.c @@ -0,0 +1,1194 @@ +/******************** + * Sync * + * UEFI * + * Bootloader * + ********************/ + +/* ========== GFX.C ========== */ + +/* Provides all of the functions required to talk to the graphics card. + * Therefore allows configuration of resolution before the kernel boots. + * This is done exclusively with UEFI Boot Services, so we need to do this + * before the kernel starts. + * If we do, we can change it later after the kernel is ready, making it so + * much easier for us. */ + +#include "BL.h" + +#define GFX_TIMEOUT 90 + +/* ========== InitGfx ========== */ +/* This is where we poll the system for the available graphical output devices + * (graphics cards and ports), gather information about them, and try to select + * the best option. Usually it's the first option given by UEFI, which tends to + * be the native resolution of the "main" output device. + * + * The main output device is either determined by BIOS (UEFI) settings, or it's + * determined programmatically by finding the connected device with the highest + * resolution. */ + +static const CHAR16 pixelFormats[5][17] = { + L"RGBReserved 8bpp", + L"BGRReserved 8bpp", + L"PixelBitMask ", + L"PixelBltOnly ", + L"PixelFormatMax " +}; + +EFI_STATUS InitGfx(EFI_HANDLE ImageHandle, GFX_INFO* Graphics) { + + /* ==================== Init ==================== */ + + Graphics->FBCount = 0; + EFI_STATUS GfxStatus; + + size_t GfxInfoSize; + uint32_t GfxMode; + size_t NumGfxHandles; + size_t NumName2Handles; + size_t NumDevicePathHandles; + size_t DeviceInd; + EFI_INPUT_KEY Key; + + Key.UnicodeChar = 0; + + uint8_t Language[6] = { 'e','n','-','U','S','\0' }; + uint8_t Language2[3] = { 'e','n','\0' }; + uint8_t Language3[4] = { 'e','n','g','\0' }; + + CHAR16 DefaultDriverName[10] = L"No Driver"; + CHAR16 DefaultControllerName[14] = L"No Controller"; + CHAR16 DefaultChildName[9] = L"No Child"; + + CHAR16* DriverDisplayName = DefaultDriverName; + CHAR16* ControllerDisplayName = DefaultControllerName; + CHAR16* ChildDisplayName = DefaultChildName; + + /* ==================== Sanity Checks ==================== */ + +#define PROBLEM_DRIVERS 4 // There are 4 known drivers that claim to control anything you ask it. This causes problems. + + const CHAR16 AmiPS2Driver[16] = L"AMI PS/2 Driver"; // This driver controls a "Generic PS/2 Keyboard" + const CHAR16 AsixUSBEthDriver[34] = L"ASIX AX88772B Ethernet Driver 1.0"; // This driver controls "ASIX AX88772B USB Fast Ethernet Controller" + const CHAR16 SocketLayerDriver[20] = L"Socket Layer Driver"; // This driver controls a "Socket Layer" + const CHAR16 Asix10100EthernetDriver[24] = L"AX88772 Ethernet Driver"; // This driver controls "AX88772 10/100 Ethernet" + + const CHAR16* const Problematic_Drivers[PROBLEM_DRIVERS] = { AmiPS2Driver, AsixUSBEthDriver, SocketLayerDriver, Asix10100EthernetDriver }; + + /* ==================== Information Gathering ==================== */ + + EFI_HANDLE* GraphicsHandles; + + // Start finding the output devices + GfxStatus = BS->LocateHandleBuffer(ByProtocol, &GraphicsOutputProtocol, NULL, &NumGfxHandles, &GraphicsHandles); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable LocateHandle error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + Print(L"\r\n"); + + // Gotta have the correct grammar. + if (NumGfxHandles == 1) + Print(L"There is 1 UEFI graphics device:\r\n\n"); + else + Print(L"There are %llu UEFI graphics devices:\r\n\n", NumGfxHandles); + +#ifdef GFX_DEBUG_MAIN + Print(L"NameBuffer size: %llu\r\n", sizeof(CHAR16*)* NumGfxHandles); +#endif + + CHAR16** NameBuffer; // A list of strings + GfxStatus = BS->AllocatePool(EfiBootServicesData, sizeof(CHAR16*)*NumGfxHandles, (void**)&NameBuffer); + if (EFI_ERROR(GfxStatus)) { + Print(L"NameBuffer Allocate error: %llx\r\n", GfxStatus); + return GfxStatus; + } + + /* After we exit the BootServices, the names we have above are meaningless. + * That means that there's no point in passing them to the OS, as the names will only + * differ from those derived by ACPI or ID lookups. + * Our GfxHandles[dev] array is useless too, as it resides in EfiBootServicesData. */ + + EFI_HANDLE* Name2Handles; // NAME2 is a standard + + GfxStatus = BS->LocateHandleBuffer(ByProtocol, &ComponentName2Protocol, NULL, &NumName2Handles, &Name2Handles); + if (EFI_ERROR(GfxStatus)) { + Print(L"Name2Handles LocateBuffer error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + EFI_HANDLE* DevicePathHandles; + + GfxStatus = BS->LocateHandleBuffer(ByProtocol, &DevicePathProtocol, NULL, &NumDevicePathHandles, &DevicePathHandles); + if (EFI_ERROR(GfxStatus)) { + Print(L"DevicePathHandles LocateBuffer error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_NAMING + Print(L"Number of NAME2 handles: %llu\r\n", NumName2Handles); + Print(L"Number of Device Path handles: %llu\r\n", NumDevicePathHandles); +#endif + + for (DeviceInd = 0; DeviceInd < NumGfxHandles; DeviceInd++) { + DriverDisplayName = DefaultDriverName; + ControllerDisplayName = DefaultControllerName; + ChildDisplayName = DefaultChildName; + + EFI_DEVICE_PATH* DevicePath_Graphics; + + GfxStatus = BS->OpenProtocol(GraphicsHandles[DeviceInd], &DevicePathProtocol, (void**)&DevicePath_Graphics, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (GfxStatus == EFI_SUCCESS) { + size_t ControllerPathSize = DevicePathSize(DevicePath_Graphics) - DevicePathNodeLength(DevicePath_Graphics) + 4; // + 4 accounts for the end node. + + EFI_DEVICE_PATH* DevicePath_GraphicsHandle; + size_t ControllerIndex = 0; + + + for (ControllerIndex = 0; ControllerIndex < NumDevicePathHandles; ControllerIndex++) { +#ifdef GFX_DEBUG_NAMING + Print(L"a. ControllerIndex: %llu\r\n", ControllerIndex); +#endif + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &DriverBindingProtocol, NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (!EFI_ERROR(GfxStatus)) + continue; + + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &LoadedImageProtocol, NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (!EFI_ERROR(GfxStatus)) + continue; + + // Graphics controllers don't have SimpleFileSystem, so we need to load a proper filesystem. + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &FileSystemProtocol, NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (!EFI_ERROR(GfxStatus)) + continue; + + // I'll be honest, i don't know why. There are no PS/2 keyboard ports on UEFI2 compatible PCIe GPUs, but without this check it fails to boot. + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &SerialIoProtocol, NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (!EFI_ERROR(GfxStatus)) + continue; + + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &DevicePathProtocol, (void**)&DevicePath_GraphicsHandle, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(GfxStatus)) { + Print(L"DevicePathHandles OpenProtocol error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + size_t CurrentControllerPathSize = DevicePathSize(DevicePath_GraphicsHandle); + + if (CurrentControllerPathSize != ControllerPathSize) + continue; // PciRoot(0) is an edge case. This needs to happen. + + if (LibMatchDevicePaths(DevicePath_GraphicsHandle, DevicePath_Graphics)) { + // This device is a graphics card. +#ifdef GFX_DEBUG_NAMING + Print(L"b. DevicePath matches DevicePathGraphics %llu, ControllerIndex: %llu\r\n", DeviceInd, ControllerIndex); +#endif + // Find the NAME2 driver for the controller. + + for (size_t Name2DriverIndex = 0; Name2DriverIndex < NumName2Handles; Name2DriverIndex++) { +#ifdef GFX_DEBUG_NAMING + Print(L"c. Name2DriverIndex: %llu\r\n", Name2DriverIndex); +#endif + + // Ask the firmware if the driver (Name2Driver) manages the controller (DevicePathHandles[ControllerIndex]) + void* ManagedInterface; + + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &PciIoProtocol, &ManagedInterface, Name2Handles[Name2DriverIndex], DevicePathHandles[ControllerIndex], EFI_OPEN_PROTOCOL_BY_DRIVER); + if (!EFI_ERROR(GfxStatus)) { + GfxStatus = BS->CloseProtocol(DevicePathHandles[ControllerIndex], &PciIoProtocol, Name2Handles[Name2DriverIndex], DevicePathHandles[ControllerIndex]); + if (EFI_ERROR(GfxStatus)) { + Print(L"DevicePathHandles Name2Handles CloseProtocol error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + continue; // It isn't. + } + else if (GfxStatus != EFI_ALREADY_STARTED) + continue; // Ditto. + +#ifdef GFX_DEBUG_NAMING + Print(L"d. Success. ControllerIndex %llu, Name2DriverIndex %llu, DeviceNum %llu\r\n", ControllerIndex, Name2DriverIndex, DeviceInd); +#endif + EFI_COMPONENT_NAME2_PROTOCOL* Name2Device; + + GfxStatus = BS->OpenProtocol(Name2Handles[Name2DriverIndex], &ComponentName2Protocol, (void**)&Name2Device, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(GfxStatus)) { + Print(L"Name2Device OpenProtocol error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + // Here is where those 3 language options came in handy earlier. + GfxStatus = Name2Device->GetDriverName(Name2Device, Language, &DriverDisplayName); + if (GfxStatus == EFI_UNSUPPORTED) { // Wrong language. + GfxStatus = Name2Device->GetDriverName(Name2Device, Language2, &DriverDisplayName); + if (GfxStatus == EFI_UNSUPPORTED) { + GfxStatus = Name2Device->GetDriverName(Name2Device, Language3, &DriverDisplayName); + + } + } + if (EFI_ERROR(GfxStatus)) { +#ifdef GFX_DEBUG_NAMING + Print(L"Name2Device GetDriverName error: 0x%llx\r\n", GfxStatus); + + if (GfxStatus == EFI_UNSUPPORTED) { // None of our languages will work. + Print(L"The first 10 characters of the supported language are:\r\n"); + for (uint32_t p = 0; p < 10; p++) { + Print(L"%c", Name2Device->SupportedLanguages[p]); + } + Print(L"\r\n"); + AwaitKey(L"\0"); + } +#endif + // The driver doesn't follow specifications if we get this far, so we give it the default. + DriverDisplayName = DefaultDriverName; + } + +#ifdef GFX_DEBUG_NAMING + Print(L"e. Driver name: %s\r\n", DriverDisplayName); +#endif + // Now we need to do the same to get the name of the controller. + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], NULL, Language, &ControllerDisplayName); // The child should be NULL to get the controller's name. + if (GfxStatus == EFI_UNSUPPORTED) + { + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], NULL, Language2, &ControllerDisplayName); + if (GfxStatus == EFI_UNSUPPORTED) + { + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], NULL, Language3, &ControllerDisplayName); + } + } + if (EFI_ERROR(GfxStatus)) + { +#ifdef GFX_DEBUG_NAMING + Print(L"Name2Device GetControllerName error: 0x%llx\r\n", GfxStatus); +#endif + ControllerDisplayName = DefaultControllerName; + } + +#ifdef GFX_DEBUG_NAMING + Print(L"f. Controller name: %s\r\n", ControllerDisplayName); +#endif + + // And again, for the child of the controller. + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], GraphicsHandles[DeviceInd], Language, &ChildDisplayName); + if (GfxStatus == EFI_UNSUPPORTED) + { + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], GraphicsHandles[DeviceInd], Language2, &ChildDisplayName); + if (GfxStatus == EFI_UNSUPPORTED) + { + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], GraphicsHandles[DeviceInd], Language3, &ChildDisplayName); + } + } + if (EFI_ERROR(GfxStatus)) + { + +#ifdef GFX_DEBUG_NAMING + Print(L"Name2Device GetController ChildName error: 0x%llu\r\n", GfxStatus); +#endif + ChildDisplayName = DefaultChildName; + } + +#ifdef GFX_DEBUG_NAMING + Print(L"g. Got names.\r\n"); +#endif + break; + } // End of Name2DriverIndex + break; + + } // End of matching controller to the Graphics handle + } // End of ControllerIndex + + /* At this point, we should have all of the names we need. + * However, if the device doesn't have a PCIe bus, such as emulators or + * virtual machines, it may not have a child name. We can take this + * into account, though. + */ + + if ((ControllerDisplayName == DefaultControllerName) && (DriverDisplayName == DefaultDriverName) && (ChildDisplayName == DefaultChildName)) { +#ifdef GFX_DEBUG_NAMING + Print(L"\r\nWeird graphics card. Is this running in a virtual machine?\r\n"); + AwaitKey(L"\0"); +#endif + + // Now we do it the long way around. + + for (ControllerIndex = 0; ControllerIndex < NumDevicePathHandles; ControllerIndex++) { + // Controllers don't have DriverBinding or LoadedImage + +#ifdef GFX_DEBUG_NAMING + Print(L"bf. ControllerIndex: %llu\r\n", ControllerIndex); +#endif + + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &DriverBindingProtocol, NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (!EFI_ERROR(GfxStatus)) + continue; + + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &LoadedImageProtocol, NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (!EFI_ERROR(GfxStatus)) + continue; + + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &FileSystemProtocol, NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (!EFI_ERROR(GfxStatus)) + continue; + + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &SerialIoProtocol, NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (!EFI_ERROR(GfxStatus)) + continue; +#ifdef GFX_DEBUG_NAMING + Print(L"cf. Filtered ControllerIndex: %llu\r\n", ControllerIndex); +#endif + + GfxStatus = BS->OpenProtocol(DevicePathHandles[ControllerIndex], &DevicePathProtocol, (void**)&DevicePath_GraphicsHandle, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(GfxStatus)) { + Print(L"Virtual DevicePathHandles OpenProtocol error: %llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_NAMING + Print(L"df. DevicePath_GraphicsHandle: %s\r\n", DevicePathToStr(DevicePath_GraphicsHandle)); + AwaitKey(L"\0"); +#endif + if (LibMatchDevicePaths(DevicePath_GraphicsHandle, DevicePath_Graphics)) { + // We have something in the controller + +#ifdef GFX_DEBUG_NAMING + Print(L"ef. DevicePath_GraphicsHandle matched DevicePath_Graphics &llu, ControllerIndex: &llu\r\n", DeviceInd, ControllerIndex); +#endif + for (size_t Name2DriverIndex = 0; Name2DriverIndex < NumName2Handles; Name2DriverIndex++) { + EFI_COMPONENT_NAME2_PROTOCOL* Name2Device; + + GfxStatus = BS->OpenProtocol(Name2Handles[Name2DriverIndex], &ComponentName2Protocol, (void**)&Name2Device, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(GfxStatus)) { + Print(L"Virtual Name2Device OpenProtocolError. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + GfxStatus = Name2Device->GetDriverName(Name2Device, Language, &DriverDisplayName); + if (GfxStatus == EFI_UNSUPPORTED) { + GfxStatus = Name2Device->GetDriverName(Name2Device, Language2, &DriverDisplayName); + if (GfxStatus == EFI_UNSUPPORTED) { + GfxStatus = Name2Device->GetDriverName(Name2Device, Language3, &DriverDisplayName); + } + } + + if (EFI_ERROR(GfxStatus)) { +#ifdef GFX_DEBUG_NAMING + Print(L"Virtual Name2Device GetDriverName error. 0x%llx\r\n", GfxStatus); + if (GfxStatus == EFI_UNSUPPORTED) { + Print(L"First 10 characters of the supported language:\r\n"); + for (uint32_t p = 0; p < 10; p++) { + Print(L"%c", Name2Device->SupportedLanguages[p]); + } + Print(L"\r\n"); + AwaitKey(L"\0"); + } +#endif + DriverDisplayName = DefaultDriverName; + } + else { // If we get here, we're dealing with one of the aforementioned problematic drivers. + + size_t BadDriverIterator; + size_t DriverNameLength = StrSize(DriverDisplayName); + + for (BadDriverIterator = 0; BadDriverIterator < PROBLEM_DRIVERS; BadDriverIterator++) { +#ifdef GFX_DEBUG_NAMING + Print(L"%s - %s\r\n", DriverDisplayName, Problematic_Drivers[BadDriverIterator]); +#endif + size_t BadDriverNameLength = StrSize(Problematic_Drivers[BadDriverIterator]); + + if (Compare(DriverDisplayName, Problematic_Drivers[BadDriverIterator], (DriverNameLength < BadDriverNameLength) ? DriverNameLength : BadDriverNameLength)) { +#ifdef GFX_DEBUG_NAMING + Print(L"Matched a known bad driver: %s\r\n", Problematic_Drivers[BadDriverIterator]); +#endif + // I don't want your damn lemons! What am I supposed to do with these?! + DriverDisplayName = DefaultDriverName; + break; + } + } + if (BadDriverIterator < PROBLEM_DRIVERS) + continue; + } + // We have the driver name now + +#ifdef GFX_DEBUG_NAMING + Print(L"jf. Got driver name.\r\n"); +#endif + + // Get controller's name + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], NULL, Language, &ControllerDisplayName); // The child should be NULL to get the controller's name. + if (GfxStatus == EFI_UNSUPPORTED) + { + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], NULL, Language2, &ControllerDisplayName); + if (GfxStatus == EFI_UNSUPPORTED) + { + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], NULL, Language3, &ControllerDisplayName); + } + } + if (EFI_ERROR(GfxStatus)) + { +#ifdef GFX_DEBUG_NAMING + Print(L"Virtual Name2Device GetControllerName error. 0x%llx\r\n", GfxStatus); +#endif + ControllerDisplayName = DefaultControllerName; + } + // Got controller's name + +#ifdef GFX_DEBUG_NAMING + Print(L"kf. Got controller name\r\n"); +#endif + // Get child's name + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], GraphicsHandles[DeviceInd], Language, &ChildDisplayName); + if (GfxStatus == EFI_UNSUPPORTED) + { + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], GraphicsHandles[DeviceInd], Language2, &ChildDisplayName); + if (GfxStatus == EFI_UNSUPPORTED) + { + GfxStatus = Name2Device->GetControllerName(Name2Device, DevicePathHandles[ControllerIndex], GraphicsHandles[DeviceInd], Language3, &ChildDisplayName); + } + } + if (EFI_ERROR(GfxStatus)) + { +#ifdef GFX_DEBUG_NAMING + Print(L"Virtual Name2Device GetControllerName ChildName error. 0x%llx\r\n", GfxStatus); +#endif + ChildDisplayName = DefaultChildName; + } + +#ifdef GFX_DEBUG_NAMING + Print(L"lf. Got names\r\n"); + Print(L"%s: %s: %s\r\n", ControllerDisplayName, DriverDisplayName, ChildDisplayName); + AwaitKey(L"\0"); +#endif + /* At this point, we've handled the drivers that claim to be God. + * However, there are a few edge cases left. Hyper-V reports the "Hyper-V Video Controller" as a child of itself. + * That's okay, though, because we use separate strings to contain them, so in our case the Controller and Child would be identical.*/ + + if (ChildDisplayName != DefaultChildName) + break; // A child name was given, so we can stop here + } // End of Name2DriverIndex + + if (ChildDisplayName != DefaultChildName) + break; + // Try another controller. + } // End of matching controllers to GraphicsHandle + } // End of ControllerIndex + } // End of handling virtual machine controllers + + + /* This is a utility function that prints the memory address of the graphics controller. + * God only knows why you'd use this, perhaps beta testing GPU firmware? + */ + // Print(L"%c. %s: %s @ Memory Address 0x%llx, using %s\r\n", DevNum + 0x30, ControllerDisplayName, ChildDisplayName, GraphicsHandles[DevNum], DriverDisplayName); + + // CatPrint solves an edgecase in GNU-EFI relating to an off-by-one error. + + POOL_PRINT StringName = { 0 }; + CatPrint(&StringName, L"%c. %s: %s @ Memory Address 0xllx, using %s\r\n", DeviceInd + 0x30 /* Shift into the ASCII numbers */, ControllerDisplayName, ChildDisplayName, GraphicsHandles[DeviceInd], DriverDisplayName); + NameBuffer[DeviceInd] = StringName.str; + +#ifdef GFX_DEBUG_NAMING + Print(L"%s", NameBuffer[DeviceInd]); + AwaitKey(L"\0"); +#endif + + } + else if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsHandles DevicePath_Graphics OpenProtocol error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + } // End of for(DeviceInd) + + // Free the array of device paths + GfxStatus = BS->FreePool(DevicePathHandles); + if (EFI_ERROR(GfxStatus)) { + Print(L"DevicePathHandles FreePool error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + GfxStatus = BS->FreePool(Name2Handles); + if (EFI_ERROR(GfxStatus)) { + Print(L"Name2Handles FreePool error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + // If we have more than one GPU, we need to select the best one. + if (NumGfxHandles > 1) { + DeviceInd = 2; + size_t Timeout = GFX_TIMEOUT; // There's a 5 minute watchdog timer, but in debug mode that's disabled. + + while (0x30 > Key.UnicodeChar || Key.UnicodeChar > 0x33) { + for (size_t DeviceNumber = 0; DeviceNumber < NumGfxHandles; DeviceNumber++) + Print(L"%s", NameBuffer[DeviceNumber]); + Print(L"\r\n"); + + Print(L"%E==================== Graphics Configuration ==================== %N \r\n"); + Print(L"Choose an option: \r\n"); + Print(L"0. Configure each device individually\r\n"); + Print(L"1. Configure one device\r\n"); + Print(L"2. Configure all devices to use the native resolution of the device they're connected to \r\n"); + Print(L"3. Configure all devices to use a 1024x768 resolution.\r\n"); + Print(L"%ENote: Not all displays may be active. The ones which are, are determined by the GPU firmware. I have no control over them.%N\r\n"); + + while (Timeout) { + Print(L"Please select an option. Defaulting to %llu in %llu.\r", DeviceInd, Timeout); + if (GfxStatus != EFI_TIMEOUT) { + GfxStatus = ST->ConIn->ReadKeyStroke(ST->ConIn, &Key); + if (EFI_ERROR(GfxStatus)) { + + Print(L"\nError reading keystroke. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + Print(L"\n\nOption %c.\r\n\n", Key.UnicodeChar); + break; + } + Timeout -= 1; + } + + if (Timeout) + DeviceInd = (size_t)(Key.UnicodeChar - 0x30); + + Key.UnicodeChar = 0; + GfxStatus = ST->ConIn->Reset(ST->ConIn, FALSE); + if (EFI_ERROR(GfxStatus)) { + Print(L"Error resetting input buffer: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + } + + if ((NumGfxHandles > 1) && (DeviceInd == 0)) { + // Configure each device individually + Graphics->FBCount = NumGfxHandles; + GfxStatus = BS->AllocatePool(EfiLoaderData, Graphics->FBCount * sizeof(EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE), (void**)&Graphics->GPUs); + if (EFI_ERROR(GfxStatus)) { + Print(L"GPUs AllocatePool error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + for (DeviceInd = 0; DeviceInd < NumGfxHandles; DeviceInd++) { + EFI_GRAPHICS_OUTPUT_PROTOCOL* GOPTable; + + GfxStatus = BS->OpenProtocol(GraphicsHandles[DeviceInd], &GraphicsOutputProtocol, (void**)&GOPTable, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable OpenProtocol error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"OpenProtocol Passed.\r\n"); + Print(L"Current GOP Mode Info: \r\n"); + Print(L"Max supported: %u Current Mode: %u\r\nSize of Mode Info struct: %llu bytes\r\n", GOPTable->Mode->MaxMode - 1, GOPTable->Mode->Mode, GOPTable->Mode->SizeOfInfo); + Print(L"FrameBuffer starts at: 0x%016llx, Framebuffer is 0x%11x bytes long.\r\n", GOPTable->Mode->FrameBufferBase, GOPTable->Mode->FrameBufferSize); + + AwaitKey(L"\0"); + + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* GOPInfo; + size_t GOPInfoSize; + + for (GfxMode = 0; GfxMode < GOPTable->Mode->MaxMode; GfxMode++) { + GfxStatus = GOPTable->QueryMode(GOPTable, GfxMode, &GOPInfoSize, &GOPInfo); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable QueryMode error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + Print(L"Mode %u of %u (%llu bytes):\r\n Ver 0x%x, Resolution: %ux%u\r\n", GfxMode, GOPTable->Mode->MaxMode - 1, GOPInfoSize, GOPInfo->Version, GOPInfo->HorizontalResolution, GOPInfo->VerticalResolution); + Print(L"at %u pixels per scan line.\r\n", GOPInfo->PixelsPerScanLine); + Print(L"Pixel format: 0x%x, Pixel info (R,G,B, Reserved Masks): 0x%08x, 0x%08x, 0x%08x, 0x%08x\r\n", GOPInfo->PixelFormat, GOPInfo->PixelInformation.RedMask, GOPInfo->PixelInformation.GreenMask, GOPInfo->PixelInformation.BlueMask, GOPInfo->PixelInformation.ReservedMask); + AwaitKey(L"\0"); + + GfxStatus = BS->FreePool(GOPInfo); + if (EFI_ERROR(GfxStatus)) { + Print(L"Error freeing the GOPInfo pool: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + } + + AwaitKey(L"Getting list of supported video modes:\r\n"); +#endif + if (GOPTable->Mode->MaxMode == 1) { +#ifdef GFX_DEBUG_MAIN + Print(L"1 available graphics mode. Setting and continuing.\r\n"); +#endif + GfxMode = 0; + } + else { + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* GOPInfo2; + while (0x30 > Key.UnicodeChar || Key.UnicodeChar > (0x30 + GOPTable->Mode->MaxMode - 1)) { + Print(L"%s", NameBuffer[DeviceInd]); + Print(L"\r\n"); + + Print(L"%u available graphics modes.\r\n\n", GOPTable->Mode->MaxMode); + + Print(L"Current mode: %c\r\n", GOPTable->Mode->Mode + 0x30); + + for (GfxMode = 0; GfxMode < GOPTable->Mode->MaxMode; GfxMode++) { + GfxStatus = GOPTable->QueryMode(GOPTable, GfxMode, &GOPInfoSize, &GOPInfo2); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable QueryMode error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + Print(L"%c. %ux%u, Pixels Per SCNLN: &u, Pixel format: %s\r\n", GfxMode + 0x30, GOPInfo2->HorizontalResolution, GOPInfo2->VerticalResolution, GOPInfo2->PixelsPerScanLine, GOPInfo2->PixelFormat); + + GfxStatus = BS->FreePool(GOPInfo2); + if (EFI_ERROR(GfxStatus)) { + Print(L"Error freeing GOPInfo2: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + } + + Print(L"\r\nPlease select a graphics mode. (0-%c)\r\n", 0x30 + GOPTable->Mode->MaxMode - 1); + GfxStatus = ST->ConIn->Reset(ST->ConIn, NULL); + + //while (Key.UnicodeChar < 0x30 || Key.UnicodeChar - 0x30 < GOPTable->Mode->MaxMode) { + GfxStatus = ST->ConIn->Reset(ST->ConIn, NULL); + + while ((GfxStatus = ST->ConIn->ReadKeyStroke(ST->ConIn, &Key)) == EFI_NOT_READY); + //} + + Print(L"\r\nSelected graphics mode %c.\r\n", Key.UnicodeChar); + + } + + GfxMode = (uint32_t)(Key.UnicodeChar - 0x30); + + Print(L"Setting graphics mode %u.\r\n", GfxMode + 1); + } + + GfxStatus = GOPTable->SetMode(GOPTable, GfxMode); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable SetMode error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + Print(L"Current GOP Info length: %llu bytes.\r\n", GOPTable->Mode->SizeOfInfo); +#endif + GfxStatus = BS->AllocatePool(EfiLoaderData, GOPTable->Mode->SizeOfInfo, (void**)&Graphics->GPUs[DeviceInd].Info); + if (EFI_ERROR(GfxStatus)) { + Print(L"GOPMode->Info AllocatePool error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"Current mode info allocated space.\r\n"); +#endif + + Graphics->GPUs[DeviceInd].MaxMode = GOPTable->Mode->MaxMode; + Graphics->GPUs[DeviceInd].Mode = GOPTable->Mode->Mode; + Graphics->GPUs[DeviceInd].SizeOfInfo = GOPTable->Mode->SizeOfInfo; + Graphics->GPUs[DeviceInd].FrameBufferSize = GOPTable->Mode->FrameBufferSize; + Graphics->GPUs[DeviceInd].FrameBufferBase = GOPTable->Mode->FrameBufferBase; + + *(Graphics->GPUs[DeviceInd].Info) = *(GOPTable->Mode->Info); + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"Current mode info assigned.\r\n"); +#endif + } // End loop + } // End of configure each device individually + else if ((NumGfxHandles > 1) && (DeviceInd == 1)) { + // Configure one device + Graphics->FBCount = 1; + GfxStatus = BS->AllocatePool(EfiLoaderData, Graphics->FBCount * sizeof(EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE), (void**)&Graphics->GPUs); + if (EFI_ERROR(GfxStatus)) { + Print(L"GPUs AllocatePool error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + while (0x30 > Key.UnicodeChar || Key.UnicodeChar > (0x30 + NumGfxHandles - 1)) { + for (size_t DeviceNumIterator = 0; DeviceNumIterator < NumGfxHandles; DeviceNumIterator++) { + Print(L"%s", NameBuffer[DeviceNumIterator]); + } + Print(L"\r\n"); + Print(L"Please select an output device to configure. (0-%llu)\r\n", NumGfxHandles - 1); + + while ((GfxStatus = ST->ConIn->ReadKeyStroke(ST->ConIn, &Key)) == EFI_NOT_READY); + Print(L"\r\nDevice %c selected.\r\n\n", Key.UnicodeChar); + } + + DeviceInd = (size_t)(Key.UnicodeChar - 0x30); + Key.UnicodeChar = 0; + +#ifdef GFX_DEBUG_MAIN + Print(L"Using handle %llu..\r\n", DeviceInd); +#endif + EFI_GRAPHICS_OUTPUT_PROTOCOL *GOPTable; + + GfxStatus = BS->OpenProtocol(GraphicsHandles[DeviceInd], &GraphicsOutputProtocol, (void**)&GOPTable, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable OpenProtocol error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"OpenProtocol passed.\r\n"); + + Print(L"Current GOP Mode Info:\r\n"); + Print(L"Max Mode supported: %u, Current Mode: %u\r\nSize of Mode Info Structure: %llu Bytes\r\n", GOPTable->Mode->MaxMode - 1, GOPTable->Mode->Mode, GOPTable->Mode->SizeOfInfo); + Print(L"FrameBufferBase: 0x%016llx, FrameBufferSize: 0x%llx\r\n", GOPTable->Mode->FrameBufferBase, GOPTable->Mode->FrameBufferSize); // Per spec, the FrameBufferBase might be 0 until SetMode is called + + AwaitKey(L"\0"); + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GOPInfo; // Querymode allocates GOPInfo + size_t GOPInfoSize; + // Get detailed info about supported graphics modes + for (GfxMode = 0; GfxMode < GOPTable->Mode->MaxMode; GfxMode++) // Valid modes are from 0 to MaxMode - 1 + { + GfxStatus = GOPTable->QueryMode(GOPTable, GfxMode, &GOPInfoSize, &GOPInfo); // IN IN OUT OUT + if (EFI_ERROR(GfxStatus)) + { + Print(L"GraphicsTable QueryMode error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + Print(L"Mode %u of %u (%llu Bytes):\r\n Ver: 0x%x, Res: %ux%u\r\n", GfxMode, GOPTable->Mode->MaxMode - 1, GOPInfoSize, GOPInfo->Version, GOPInfo->HorizontalResolution, GOPInfo->VerticalResolution); + Print(L"PxPerScanLine: %u\r\n", GOPInfo->PixelsPerScanLine); + Print(L"PxFormat: 0x%x, PxInfo (R,G,B,Rsvd Masks): 0x%08x, 0x%08x, 0x%08x, 0x%08x\r\n", GOPInfo->PixelFormat, GOPInfo->PixelInformation.RedMask, GOPInfo->PixelInformation.GreenMask, GOPInfo->PixelInformation.BlueMask, GOPInfo->PixelInformation.ReservedMask); + AwaitKey(L"\0"); + + // Don't need GOPInfo anymore + GfxStatus = BS->FreePool(GOPInfo); + if (EFI_ERROR(GfxStatus)) + { + Print(L"Error freeing GOPInfo pool. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + } + + AwaitKey(L"\r\nGetting list of supported modes...\r\n"); +#endif + if (GOPTable->Mode->MaxMode == 1) { +#ifdef GFX_DEBUG_MAIN + Print(L"%u available graphics modes.\r\n", GOPTable->Mode->MaxMode); +#endif + GfxMode = 0; + } + else { + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* GOPInfo2; + while (0x30 > Key.UnicodeChar || Key.UnicodeChar > (0x30 + GOPTable->Mode->MaxMode - 1)) { + Print(L"%s\r\n", NameBuffer[DeviceInd]); + + Print(L"%u available graphics modes.\r\n\n", GOPTable->Mode->MaxMode); + Print(L"Current mode: %c\r\n", GOPTable->Mode->Mode + 0x30); + + for (GfxMode = 0; GfxMode < GOPTable->Mode->MaxMode; GfxMode++) { + GfxStatus = GOPTable->QueryMode(GOPTable, GfxMode, &GOPInfoSize, &GOPInfo2); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable QueryMode error: 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + Print(L"%c. %ux%u, PxPerScanLine: %u, PxFormat: %s\r\n", GfxMode + 0x30, GOPInfo2->HorizontalResolution, GOPInfo2->VerticalResolution, GOPInfo2->PixelsPerScanLine, pixelFormats[GOPInfo2->PixelFormat]); + + // Don't need GOPInfo2 anymore + GfxStatus = BS->FreePool(GOPInfo2); + if (EFI_ERROR(GfxStatus)) + { + Print(L"Error freeing GOPInfo2 pool. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + } + + Print(L"\r\nPlease select a graphics mode. (0 - %c)\r\n", 0x30 + GOPTable->Mode->MaxMode - 1); + + while ((GfxStatus = ST->ConIn->ReadKeyStroke(ST->ConIn, &Key)) == EFI_NOT_READY); + Print(L"\r\nSelected graphics mode %c.\r\n\n", Key.UnicodeChar); + } + GfxMode = (uint32_t)(Key.UnicodeChar - 0x30); + Key.UnicodeChar = 0; + + Print(L"Setting graphics mode %u of %u.\r\n\n", GfxMode + 1, GOPTable->Mode->MaxMode); + } + + GfxStatus = GOPTable->SetMode(GOPTable, GfxMode); + if (EFI_ERROR(GfxStatus)) + { + Print(L"GraphicsTable SetMode error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + Print(L"Current GOP Info Size: %llu\r\n", GOPTable->Mode->SizeOfInfo); +#endif + DeviceInd = 0; + + GfxStatus = BS->AllocatePool(EfiLoaderData, GOPTable->Mode->SizeOfInfo, (void**)&Graphics->GPUs[DeviceInd].Info); + if (EFI_ERROR(GfxStatus)) + { + Print(L"GOP Mode->Info AllocatePool error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"Current mode info allocated.\r\n"); +#endif + + Graphics->GPUs[DeviceInd].MaxMode = GOPTable->Mode->MaxMode; + Graphics->GPUs[DeviceInd].Mode = GOPTable->Mode->Mode; + Graphics->GPUs[DeviceInd].SizeOfInfo = GOPTable->Mode->SizeOfInfo; + Graphics->GPUs[DeviceInd].FrameBufferSize = GOPTable->Mode->FrameBufferSize; + Graphics->GPUs[DeviceInd].FrameBufferBase = GOPTable->Mode->FrameBufferBase; + + *(Graphics->GPUs[DeviceInd].Info) = *(GOPTable->Mode->Info); + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"Current mode info assigned.\r\n"); +#endif + } // End of Configuring one device + else if ((NumGfxHandles > 1) && (DeviceInd == 2)) { + // Configure each device to use the native resolution of the connected display + + Graphics->FBCount = NumGfxHandles; + GfxStatus = BS->AllocatePool(EfiLoaderData, Graphics->FBCount * sizeof(EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE), (void**)&Graphics->GPUs); + if (EFI_ERROR(GfxStatus)) + { + Print(L"GPUs AllocatePool error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + for (DeviceInd = 0; DeviceInd < NumGfxHandles; DeviceInd++) { + EFI_GRAPHICS_OUTPUT_PROTOCOL* GOPTable; + + GfxStatus = BS->OpenProtocol(GraphicsHandles[DeviceInd], &GraphicsOutputProtocol, (void**)&GOPTable, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(GfxStatus)) + { + Print(L"GraphicsTable OpenProtocol error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"OpenProtocol passed.\r\n"); + Print(L"Current GOP mode info:\r\n"); + Print(L"Max mode: %u, Current mode: %u\r\nSize of Mode Info struct: %llu bytes\r\n", GOPTable->Mode->MaxMode - 1, GOPTable->Mode->Mode, GOPTable->Mode->SizeOfInfo); + Print(L"Framebuffer starts at 0x%016llx, and is 0x%llx bytes long.\r\n", GOPTable->Mode->FrameBufferBase, GOPTable->Mode->FrameBufferSize); + + AwaitKey(L"\0"); + +#endif + GfxMode = 0; + + Print(L"Setting graphics mode %u of %u.\r\n\n", GfxMode + 1, GOPTable->Mode->MaxMode); + + GfxStatus = GOPTable->SetMode(GOPTable, GfxMode); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable SetMode error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + +#ifdef GFX_DEBUG_MAIN + Print(L"Mode %u of %u (%llu Bytes):\r\n Ver: 0x%x, Res: %ux%u\r\n", GOPTable->Mode->Mode, GOPTable->Mode->MaxMode - 1, GOPTable->Mode->SizeOfInfo, GOPTable->Mode->Info->Version, GOPTable->Mode->Info->HorizontalResolution, GOPTable->Mode->Info->VerticalResolution); + Print(L"PxPerScanLine: %u\r\n", GOPTable->Mode->Info->PixelsPerScanLine); + Print(L"PxFormat: 0x%x, PxInfo (R,G,B,Rsvd Masks): 0x%08x, 0x%08x, 0x%08x, 0x%08x\r\n", GOPTable->Mode->Info->PixelFormat, GOPTable->Mode->Info->PixelInformation.RedMask, GOPTable->Mode->Info->PixelInformation.GreenMask, GOPTable->Mode->Info->PixelInformation.BlueMask, GOPTable->Mode->Info->PixelInformation.ReservedMask); + AwaitKey(L"\0"); +#endif + GfxStatus = BS->AllocatePool(EfiLoaderData, GOPTable->Mode->SizeOfInfo, (void**)&Graphics->GPUs[DeviceInd].Info); + if (EFI_ERROR(GfxStatus)) + { + Print(L"GOP Mode->Info AllocatePool error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"Current mode info allocated."); +#endif + Graphics->GPUs[DeviceInd].MaxMode = GOPTable->Mode->MaxMode; + Graphics->GPUs[DeviceInd].Mode = GOPTable->Mode->Mode; + Graphics->GPUs[DeviceInd].SizeOfInfo = GOPTable->Mode->SizeOfInfo; + Graphics->GPUs[DeviceInd].FrameBufferSize = GOPTable->Mode->FrameBufferSize; + Graphics->GPUs[DeviceInd].FrameBufferBase = GOPTable->Mode->FrameBufferBase; + + *(Graphics->GPUs[DeviceInd].Info) = *(GOPTable->Mode->Info); + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"Current mode info assigned."); +#endif + } // End loop of DeviceInds + + } // End of "Configure each device to use the native resolution of the connected display" + + else if ((NumGfxHandles > 1) && (DeviceInd == 3)) { + // Configure all device to use a resolution of 1024x768 + // This is the default resolution of Windows, so all UEFI devices must support it, + // Despite the UEFI specification only requiring 640x480 and 800x600. + + Graphics->FBCount = NumGfxHandles; + GfxStatus = BS->AllocatePool(EfiLoaderData, Graphics->FBCount * sizeof(EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE), (void**)&Graphics->GPUs); + if (EFI_ERROR(GfxStatus)) { + Print(L"GPUs AllocatePool error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + for (DeviceInd = 0; DeviceInd < NumGfxHandles; DeviceInd++) { + EFI_GRAPHICS_OUTPUT_PROTOCOL* GOPTable; + GfxStatus = BS->OpenProtocol(GraphicsHandles[DeviceInd], &GraphicsOutputProtocol, (void**)&GOPTable, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable OpenProtocol error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"OpenProtocol passed.\r\n"); + + Print(L"Current GOP Mode Info:\r\n"); + Print(L"Max Mode supported: %u, Current Mode: %u\r\nSize of Mode Info Structure: %llu Bytes\r\n", GOPTable->Mode->MaxMode - 1, GOPTable->Mode->Mode, GOPTable->Mode->SizeOfInfo); + Print(L"FrameBufferBase: 0x%016llx, FrameBufferSize: 0x%llx\r\n", GOPTable->Mode->FrameBufferBase, GOPTable->Mode->FrameBufferSize); // Per spec, the FrameBufferBase might be 0 until SetMode is called + + AwaitKey(L"\0"); +#endif + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* GOPInfo2; + size_t GOPInfoSize; + for (GfxMode = 0; GfxMode < GOPTable->Mode->MaxMode; GfxMode++) { + GfxStatus = GOPTable->QueryMode(GOPTable, GfxMode, &GOPInfoSize, &GOPInfo2); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable QueryMode error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + if ((GOPInfo2->HorizontalResolution == 1024) + && (GOPInfo2->VerticalResolution == 768)) { + GfxStatus = BS->FreePool(GOPInfo2); + if (EFI_ERROR(GfxStatus)) { + Print(L"Error freeing GOPInfo2 pool.0x%llx\r\n", GfxStatus); + return GfxStatus; + } + break; // Stop the loop, we're done + } + + GfxStatus = BS->FreePool(GOPInfo2); + if (EFI_ERROR(GfxStatus)) { + Print(L"Error freeing GOPInfo2 pool. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + } + + if (GfxMode == GOPTable->Mode->MaxMode) { + Print(L"No 1024x768 mode found. We must be in a virtual machine, so defaulting to mode 0.\r\n"); + GfxMode = 0; + } + + Print(L"Setting graphics mode %u of %u\r\n", GfxMode + 1, GOPTable->Mode->MaxMode); + + GfxStatus = GOPTable->SetMode(GOPTable, GfxMode); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable SetMode error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + Print(L"Mode %u of %u (%llu Bytes):\r\n Ver: 0x%x, Res: %ux%u\r\n", GOPTable->Mode->Mode, GOPTable->Mode->MaxMode - 1, GOPTable->Mode->SizeOfInfo, GOPTable->Mode->Info->Version, GOPTable->Mode->Info->HorizontalResolution, GOPTable->Mode->Info->VerticalResolution); + Print(L"PxPerScanLine: %u\r\n", GOPTable->Mode->Info->PixelsPerScanLine); + Print(L"PxFormat: 0x%x, PxInfo (R,G,B,Rsvd Masks): 0x%08x, 0x%08x, 0x%08x, 0x%08x\r\n", GOPTable->Mode->Info->PixelFormat, GOPTable->Mode->Info->PixelInformation.RedMask, GOPTable->Mode->Info->PixelInformation.GreenMask, GOPTable->Mode->Info->PixelInformation.BlueMask, GOPTable->Mode->Info->PixelInformation.ReservedMask); + AwaitKey(L"\0"); +#endif + GfxStatus = BS->AllocatePool(EfiLoaderData, GOPTable->Mode->SizeOfInfo, (void**)&Graphics->GPUs[DeviceInd].Info); + if (EFI_ERROR(GfxStatus)) { + Print(L"GOP Mode->Info AllocatePool error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"Current mode info allocated.\r\n"); +#endif + Graphics->GPUs[DeviceInd].MaxMode = GOPTable->Mode->MaxMode; + Graphics->GPUs[DeviceInd].Mode = GOPTable->Mode->Mode; + Graphics->GPUs[DeviceInd].SizeOfInfo = GOPTable->Mode->SizeOfInfo; + Graphics->GPUs[DeviceInd].FrameBufferSize = GOPTable->Mode->FrameBufferSize; + Graphics->GPUs[DeviceInd].FrameBufferBase = GOPTable->Mode->FrameBufferBase; + + *(Graphics->GPUs[DeviceInd].Info) = *(GOPTable->Mode->Info); + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"Current mode info assigned.\r\n"); +#endif + } // End of DeviceInd loop + } // End of setting every device to 1024x768 + else { + // There is only 1 GPU available. The most common case. + + Graphics->FBCount = 1; + + GfxStatus = BS->AllocatePool(EfiLoaderData, Graphics->FBCount * sizeof(EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE), (void**)&Graphics->GPUs); + if (EFI_ERROR(GfxStatus)) { + Print(L"GPUs AllocatePool error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + DeviceInd = 0; + +#ifdef GFX_DEBUG_MAIN + Print(L"One GPU detected.\r\n"); +#endif + EFI_GRAPHICS_OUTPUT_PROTOCOL* GOPTable; + + GfxStatus = BS->OpenProtocol(GraphicsHandles[DeviceInd], &GraphicsOutputProtocol, (void**)&GOPTable, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable OpenProtocol error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"OpenProtocol passed."); + Print(L"Current GOP Mode Info:\r\n"); + Print(L"Max Mode supported: %u, Current Mode: %u\r\nSize of Mode Info Structure: %llu Bytes\r\n", GOPTable->Mode->MaxMode - 1, GOPTable->Mode->Mode, GOPTable->Mode->SizeOfInfo); + Print(L"FrameBufferBase: 0x%016llx, FrameBufferSize: 0x%llx\r\n", GOPTable->Mode->FrameBufferBase, GOPTable->Mode->FrameBufferSize); // Per spec, the FrameBufferBase might be 0 until SetMode is called + + AwaitKey(L"\0"); + + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* GOPInfo; + size_t GOPInfoSize; + + for (GfxMode = 0; GfxMode < GOPTable->Mode->MaxMode; GfxMode++) { + GfxStatus = GOPTable->QueryMode(GOPTable, GfxMode, &GOPInfoSize, &GOPInfo); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable QueryMode error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + Print(L"Mode %u of %u (%llu Bytes):\r\n Ver: 0x%x, Res: %ux%u\r\n", GfxMode, GOPTable->Mode->MaxMode - 1, GOPInfoSize, GOPInfo->Version, GOPInfo->HorizontalResolution, GOPInfo->VerticalResolution); + Print(L"PxPerScanLine: %u\r\n", GOPInfo->PixelsPerScanLine); + Print(L"PxFormat: 0x%x, PxInfo (R,G,B,Rsvd Masks): 0x%08x, 0x%08x, 0x%08x, 0x%08x\r\n", GOPInfo->PixelFormat, GOPInfo->PixelInformation.RedMask, GOPInfo->PixelInformation.GreenMask, GOPInfo->PixelInformation.BlueMask, GOPInfo->PixelInformation.ReservedMask); + + AwaitKey(L"\0"); + GfxStatus = BS->FreePool(GOPInfo); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable QueryMode error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + } + + AwaitKey(L"\r\nGetting list of supported modes..\r\n"); + +#endif + if (GOPTable->Mode->MaxMode == 1) { +#ifdef GFX_DEBUG_MAIN + Print(L"1 available graphics mode for device %s\r\n", NameBuffer[DeviceInd]); +#endif + GfxMode = 0; + } + else { + uint32_t DefaultMode = 0; + size_t Timeout = GFX_TIMEOUT; + + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* GOPInfo2; + + while (0x30 > Key.UnicodeChar || Key.UnicodeChar > (0x30 + GOPTable->Mode->MaxMode - 1)) { + Print(L"%s\r\n", NameBuffer[DeviceInd]); + Print(L"%u available graphics modes.\r\n\n", GOPTable->Mode->MaxMode); + Print(L"Current mode: %c\r\n", GOPTable->Mode->Mode + 0x30); + + for (GfxMode = 0; GfxMode < GOPTable->Mode->MaxMode; GfxMode++) { + GfxStatus = GOPTable->QueryMode(GOPTable, GfxMode, &GOPInfoSize, &GOPInfo2); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable QueryMode error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + Print(L"%c, %ux%u, %u pixels per scan line, Pixel format %s\r\n", GfxMode + 0x30, GOPInfo2->HorizontalResolution, GOPInfo2->VerticalResolution, GOPInfo2->PixelsPerScanLine, pixelFormats[GOPInfo2->PixelFormat]); + + GfxStatus = BS->FreePool(GOPInfo2); + if (EFI_ERROR(GfxStatus)) { + Print(L"Error freeing GOPInfo2 pool. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + } + Print(L"\r\n"); + + while (Timeout) { + Print(L"Please select a graphics mode. (0 - %c). Defaulting to mode %c in %llu... \r", 0x30 + GOPTable->Mode->MaxMode - 1, DefaultMode + 0x30, Timeout); + GfxStatus = WaitForSingleEvent(ST->ConIn->WaitForKey, 10000000); + if (GfxStatus != EFI_TIMEOUT) { + GfxStatus = ST->ConIn->ReadKeyStroke(ST->ConIn, &Key); + if (EFI_ERROR(GfxStatus)) { + Print(L"Error reading keystroke. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + Print(L"\n\nSelected graphics mode %c.\r\n\n", Key.UnicodeChar); + break; + } + Timeout -= 1; + } + if (!Timeout) { + Print(L"\n\nDefaulting to mode %c..\r\n\n", DefaultMode + 0x30); + GfxMode = DefaultMode; + break; + } + } + + if (Timeout) + GfxMode = (uint32_t)(Key.UnicodeChar - 0x30); + + Key.UnicodeChar = 0; + + Print(L"Setting graphics mode %u of %u.\r\n\n", GfxMode + 1, GOPTable->Mode->MaxMode); + } + + GfxStatus = GOPTable->SetMode(GOPTable, GfxMode); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsTable SetMode error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + Print(L"Current GOP Info Size: %llu\r\n", GOPTable->Mode->SizeOfInfo); +#endif + GfxStatus = BS->AllocatePool(EfiLoaderData, GOPTable->Mode->SizeOfInfo, (void**)&Graphics->GPUs[DeviceInd].Info); + if (EFI_ERROR(GfxStatus)) { + Print(L"GOP Mode->Info AllocatePool error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"Current mode info allocated.\r\n"); +#endif + Graphics->GPUs[DeviceInd].MaxMode = GOPTable->Mode->MaxMode; + Graphics->GPUs[DeviceInd].Mode = GOPTable->Mode->Mode; + Graphics->GPUs[DeviceInd].SizeOfInfo = GOPTable->Mode->SizeOfInfo; + Graphics->GPUs[DeviceInd].FrameBufferSize = GOPTable->Mode->FrameBufferSize; + Graphics->GPUs[DeviceInd].FrameBufferBase = GOPTable->Mode->FrameBufferBase; + + *(Graphics->GPUs[DeviceInd].Info) = *(GOPTable->Mode->Info); + +#ifdef GFX_DEBUG_MAIN + AwaitKey(L"Current mode info assigned.\r\n"); +#endif + } // End of single GPU. + + for (size_t StringNameFree = 0; StringNameFree < NumGfxHandles; StringNameFree++) { + GfxStatus = BS->FreePool(NameBuffer[StringNameFree]); + if (EFI_ERROR(GfxStatus)) { + Print(L"NameBuffer[%llu] (%s) FreePool error. 0x%llx\r\n", StringNameFree, NameBuffer[StringNameFree], GfxStatus); + return GfxStatus; + } + } + + GfxStatus = BS->FreePool(NameBuffer); + if (EFI_ERROR(GfxStatus)) { + Print(L"NameBuffer FreePool error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + GfxStatus = BS->FreePool(GraphicsHandles); + if (EFI_ERROR(GfxStatus)) { + Print(L"GraphicsHandles FreePool error. 0x%llx\r\n", GfxStatus); + return GfxStatus; + } + + +#ifdef GFX_DEBUG_MAIN + // One last time, to make sure everything is sane. + for (DeviceInd = 0; DeviceInd < Graphics->FBCount; DeviceInd++) { + Print(L"\r\nCurrent GOP Mode Info:\r\n"); + Print(L"Max Mode supported: %u, Current Mode: %u\r\nSize of Mode Info Structure: %llu Bytes\r\n", Graphics->GPUs[DeviceInd].MaxMode - 1, Graphics->GPUs[DeviceInd].Mode, Graphics->GPUs[DeviceInd].SizeOfInfo); + Print(L"FrameBufferBase: 0x%016llx, FrameBufferSize: 0x%llx\r\n", Graphics->GPUs[DeviceInd].FrameBufferBase, Graphics->GPUs[DeviceInd].FrameBufferSize); + + Print(L"Mode %u of %u (%llu Bytes):\r\n Ver: 0x%x, Res: %ux%u\r\n", Graphics->GPUs[DeviceInd].Mode, Graphics->GPUs[DeviceInd].MaxMode - 1, Graphics->GPUs[DeviceInd].SizeOfInfo, Graphics->GPUs[DeviceInd].Info->Version, Graphics->GPUs[DeviceInd].Info->HorizontalResolution, Graphics->GPUs[DeviceInd].Info->VerticalResolution); + Print(L"PxPerScanLine: %u\r\n", Graphics->GPUs[DeviceInd].Info->PixelsPerScanLine); + Print(L"PxFormat: 0x%x, PxInfo (R,G,B,Rsvd Masks): 0x%08x, 0x%08x, 0x%08x, 0x%08x\r\n", Graphics->GPUs[DeviceInd].Info->PixelFormat, Graphics->GPUs[DeviceInd].Info->PixelInformation.RedMask, Graphics->GPUs[DeviceInd].Info->PixelInformation.GreenMask, Graphics->GPUs[DeviceInd].Info->PixelInformation.BlueMask, Graphics->GPUs[DeviceInd].Info->PixelInformation.ReservedMask); + } +#endif + + } + + return GfxStatus; +} + + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..0ebca55 --- /dev/null +++ b/src/main.c @@ -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; +} + diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..b91b510 --- /dev/null +++ b/src/memory.c @@ -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; +} \ No newline at end of file