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