Splitting out UEFI bootloader code from kernel

Now the bootloader should be responsible for all initial setup,
loading the kernel, and then handing off to the kernel with
proper data in place.
This commit is contained in:
Justin C. Miller
2017-07-26 01:41:46 -07:00
parent 9ae583b1ec
commit d02e1d97d9
14 changed files with 242 additions and 53 deletions

120
src/boot/console.c Normal file
View File

@@ -0,0 +1,120 @@
#include <efi.h>
#include <efilib.h>
#include <stdint.h>
#include "console.h"
#include "utility.h"
UINTN ROWS = 0;
UINTN COLS = 0;
EFI_STATUS
con_initialize (const CHAR16 *version)
{
EFI_STATUS status;
Print(L"Setting console display mode...\n");
EFI_GRAPHICS_OUTPUT_PROTOCOL *gfx_out_proto;
EFI_GUID gfx_out_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
status = ST->BootServices->LocateProtocol(
&gfx_out_guid, NULL, (void**)&gfx_out_proto);
CHECK_EFI_STATUS_OR_RETURN(status, "LocateProtocol gfx");
const uint32_t modes = gfx_out_proto->Mode->MaxMode;
uint32_t res =
gfx_out_proto->Mode->Info->HorizontalResolution *
gfx_out_proto->Mode->Info->VerticalResolution;
uint32_t best = gfx_out_proto->Mode->Mode;
for (uint32_t i = 0; i < modes; ++i) {
UINTN size = 0;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info = NULL;
status = gfx_out_proto->QueryMode(gfx_out_proto, i, &size, &info);
CHECK_EFI_STATUS_OR_RETURN(status, "QueryMode");
#ifdef MAX_HRES
if (info->HorizontalResolution > MAX_HRES)
continue;
#endif
const uint32_t new_res =
info->HorizontalResolution *
info->VerticalResolution;
if (new_res > res) {
best = i;
res = new_res;
}
}
status = gfx_out_proto->SetMode(gfx_out_proto, best);
CHECK_EFI_STATUS_OR_RETURN(status, "SetMode %d/%d", best, modes);
status = ST->ConOut->QueryMode(
ST->ConOut,
ST->ConOut->Mode->Mode,
&COLS, &ROWS);
CHECK_EFI_STATUS_OR_RETURN(status, "QueryMode");
status = ST->ConOut->ClearScreen(ST->ConOut);
CHECK_EFI_STATUS_OR_RETURN(status, "ClearScreen");
ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTCYAN);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)L"Popcorn OS ");
ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTMAGENTA);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)version);
ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTGRAY);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)L" booting...\r\n");
con_status_begin(L"Setting console display mode: ");
Print(L"%ux%u (%ux%u chars)",
gfx_out_proto->Mode->Info->HorizontalResolution,
gfx_out_proto->Mode->Info->VerticalResolution,
ROWS, COLS);
con_status_ok();
return status;
}
void
con_status_begin (const CHAR16 *message)
{
ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTGRAY);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)message);
}
void
con_status_ok ()
{
UINTN row = ST->ConOut->Mode->CursorRow;
ST->ConOut->SetCursorPosition(ST->ConOut, COLS-8, row);
ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTGRAY);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)L"[");
ST->ConOut->SetAttribute(ST->ConOut, EFI_GREEN);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)L" ok ");
ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTGRAY);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)L"]\r");
ST->ConOut->SetCursorPosition(ST->ConOut, 0, row+1);
}
void
con_status_fail (const CHAR16 *error)
{
UINTN row = ST->ConOut->Mode->CursorRow;
ST->ConOut->SetCursorPosition(ST->ConOut, COLS-8, row);
ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTGRAY);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)L"[");
ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTRED);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)L"failed");
ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTGRAY);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)L"]\r");
ST->ConOut->SetCursorPosition(ST->ConOut, 2, row+1);
ST->ConOut->SetAttribute(ST->ConOut, EFI_RED);
ST->ConOut->OutputString(ST->ConOut, (CHAR16*)error);
ST->ConOut->SetCursorPosition(ST->ConOut, 0, row+2);
ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTGRAY);
}

7
src/boot/console.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include <efi.h>
EFI_STATUS con_initialize (const CHAR16 *version);
void con_status_begin (const CHAR16 *message);
void con_status_ok ();
void con_status_fail (const CHAR16 *error);

75
src/boot/loader.c Normal file
View File

@@ -0,0 +1,75 @@
#include "loader.h"
#include "utility.h"
static CHAR16 kernel_name[] = KERNEL_FILENAME;
static EFI_STATUS loader_load_file(EFI_FILE_PROTOCOL *root, void **kernel_image, UINT64 *len) {
EFI_STATUS status;
EFI_FILE_PROTOCOL *handle = 0;
status = root->Open(root, &handle, kernel_name, EFI_FILE_MODE_READ, 0);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to open kernel file handle");
EFI_FILE_INFO *info = LibFileInfo(handle);
if (info->FileSize == 0)
return EFI_NOT_FOUND;
UINTN count = ((info->FileSize - 1) / 0x1000) + 1;
EFI_PHYSICAL_ADDRESS addr = 0x100000; // Try to load the kernel in at 1MiB
EFI_MEMORY_TYPE memType = 0xFFFFFFFF; // Special value to tell the kernel it's here
status = ST->BootServices->AllocatePages(AllocateAddress, memType, count, &addr);
if (status == EFI_NOT_FOUND) {
// couldn't get the address we wanted, try loading the kernel anywhere
status = ST->BootServices->AllocatePages(AllocateAnyPages, memType, count, &addr);
}
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to allocate kernel pages");
UINTN buffer_size = count * 0x1000;
void *buffer = (void*)addr;
status = handle->Read(handle, &buffer_size, &buffer);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to read kernel to memory");
*len = buffer_size;
*kernel_image = buffer;
return EFI_SUCCESS;
}
EFI_STATUS loader_load_kernel(void **kernel_image, UINT64 *len) {
if (kernel_image == 0 || len == 0)
CHECK_EFI_STATUS_OR_RETURN(EFI_INVALID_PARAMETER, "NULL kernel_image pointer or size");
EFI_STATUS status;
// First, find all the handles that support the filesystem protocol. Call
UINTN size = 0;
EFI_GUID fs_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
status = ST->BootServices->LocateHandle(ByProtocol, &fs_guid, NULL, &size, NULL);
if (status != EFI_BUFFER_TOO_SMALL) {
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to find number of filesystem handles");
} else if (size == 0) {
CHECK_EFI_STATUS_OR_RETURN(EFI_NO_MEDIA, "Found zero filesystem handles");
}
EFI_HANDLE *buffer = 0;
status = ST->BootServices->AllocatePool(EfiLoaderData, size, (void**)&buffer);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to allocate buffer of filesystem handles");
status = ST->BootServices->LocateHandle(ByProtocol, &fs_guid, NULL, &size, buffer);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to find filesystem handles");
unsigned num_fs = size / sizeof(EFI_HANDLE);
EFI_HANDLE *fss = (EFI_HANDLE*)buffer;
for (unsigned i = 0; i < num_fs; ++i) {
EFI_FILE_HANDLE root = LibOpenRoot(fss[i]);
status = loader_load_file(root, kernel_image, len);
if (status == EFI_NOT_FOUND)
continue;
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to load kernel");
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}

8
src/boot/loader.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include <efi.h>
#ifndef KERNEL_FILENAME
#define KERNEL_FILENAME L"kernel.bin"
#endif
EFI_STATUS loader_load_kernel(void **kernel_image, UINT64 *len);

76
src/boot/main.c Normal file
View File

@@ -0,0 +1,76 @@
#include <efi.h>
#include <efilib.h>
#include "console.h"
#include "loader.h"
#include "memory.h"
#include "utility.h"
#ifndef GIT_VERSION
#define GIT_VERSION L"no version"
#endif
EFI_STATUS
efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
EFI_STATUS status;
InitializeLib(ImageHandle, SystemTable);
// When checking the console initialization error,
// use CHECK_EFI_STATUS_OR_RETURN because we can't
// be sure if the console was fully set up
status = con_initialize(GIT_VERSION);
CHECK_EFI_STATUS_OR_RETURN(status, "con_initialize");
// From here on out, use CHECK_EFI_STATUS_OR_FAIL instead
// because the console is now set up
// Get info about the image
con_status_begin(L"Gathering image information...");
EFI_LOADED_IMAGE *info = 0;
EFI_GUID image_proto = EFI_LOADED_IMAGE_PROTOCOL_GUID;
status = ST->BootServices->HandleProtocol(ImageHandle, &image_proto, (void **)&info);
CHECK_EFI_STATUS_OR_FAIL(status);
con_status_ok();
con_status_begin(L"Virtualizing memory...");
status = memory_virtualize();
CHECK_EFI_STATUS_OR_FAIL(status);
con_status_ok();
con_status_begin(L"Loading kernel into memory...");
void *kernel_image = NULL;
UINT64 len = 0;
status = loader_load_kernel(&kernel_image, &len);
CHECK_EFI_STATUS_OR_FAIL(status);
Print(L" %u bytes at 0x%x", len, kernel_image);
con_status_ok();
/*
dump_memory_map();
UINTN memmap_size = 0;
EFI_MEMORY_DESCRIPTOR *memmap;
UINTN memmap_key;
UINTN desc_size;
UINT32 desc_version;
con_status_begin(L"Exiting boot services");
memmap = LibMemoryMap(&memmap_size, &memmap_key,
&desc_size, &desc_version);
status = ST->BootServices->ExitBootServices(ImageHandle, memmap_key);
CHECK_EFI_STATUS_OR_FAIL(status);
con_status_ok();
con_status_begin(L"Setting virtual address map");
status = ST->RuntimeServices->SetVirtualAddressMap(
memmap_size, desc_size, desc_version, memmap);
CHECK_EFI_STATUS_OR_FAIL(status);
con_status_ok();
*/
while (1) __asm__("hlt");
return status;
}

100
src/boot/memory.c Normal file
View File

@@ -0,0 +1,100 @@
#include <efi.h>
#include <efilib.h>
#include "memory.h"
#include "utility.h"
const UINTN PAGE_SIZE = 4096;
const CHAR16 *memory_type_names[] = {
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",
};
static const CHAR16 *memory_type_name(UINT32 value) {
if (value >= (sizeof(memory_type_names)/sizeof(CHAR16*)))
return L"Bad Type Value";
return memory_type_names[value];
}
void EFIAPI memory_update_addresses(EFI_EVENT UNUSED *event, void UNUSED *context) {
ST->RuntimeServices->ConvertPointer(0, (void **)&BS);
ST->RuntimeServices->ConvertPointer(0, (void **)&ST);
}
EFI_STATUS memory_virtualize() {
EFI_STATUS status;
EFI_EVENT event;
status = ST->BootServices->CreateEvent(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
TPL_CALLBACK, (EFI_EVENT_NOTIFY)&memory_update_addresses, NULL, &event);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to create memory update event");
return status;
}
EFI_STATUS memory_get_map(EFI_MEMORY_DESCRIPTOR **buffer, UINTN *buffer_size,
UINTN *key, UINTN *desc_size, UINT32 *desc_version) {
EFI_STATUS status;
UINTN needs_size = 0;
status = ST->BootServices->GetMemoryMap(&needs_size, 0, key, desc_size, desc_version);
if (status != EFI_BUFFER_TOO_SMALL) {
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to load memory map");
}
// Give some extra buffer to account for changes.
*buffer_size = needs_size + 256;
Print(L"Trying to load memory map with size %d.\n", *buffer_size);
status = ST->BootServices->AllocatePool(EfiLoaderData, *buffer_size, (void**)buffer);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to allocate space for memory map");
status = ST->BootServices->GetMemoryMap(buffer_size, *buffer, key, desc_size, desc_version);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to load memory map");
return EFI_SUCCESS;
}
EFI_STATUS memory_dump_map() {
EFI_MEMORY_DESCRIPTOR *buffer;
UINTN buffer_size, desc_size, key;
UINT32 desc_version;
EFI_STATUS status = memory_get_map(&buffer, &buffer_size, &key,
&desc_size, &desc_version);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to get memory map");
const UINTN count = buffer_size / desc_size;
Print(L"Memory map:\n");
Print(L"\t Descriptor Count: %d (%d bytes)\n", count, buffer_size);
Print(L"\t Version Key: %d\n", key);
Print(L"\tDescriptor Version: %d (%d bytes)\n\n", desc_version, desc_size);
EFI_MEMORY_DESCRIPTOR *end = (EFI_MEMORY_DESCRIPTOR *)((uint8_t *)buffer + buffer_size);
EFI_MEMORY_DESCRIPTOR *d = buffer;
while (d < end) {
UINTN size_bytes = d->NumberOfPages * PAGE_SIZE;
Print(L"Type: %s Attr: 0x%x\n", memory_type_name(d->Type), d->Attribute);
Print(L"\t Physical %016llx - %016llx\n", d->PhysicalStart, d->PhysicalStart + size_bytes);
Print(L"\t Virtual %016llx - %016llx\n\n", d->VirtualStart, d->VirtualStart + size_bytes);
d = (EFI_MEMORY_DESCRIPTOR *)((uint8_t *)d + desc_size);
}
ST->BootServices->FreePool(buffer);
return EFI_SUCCESS;
}

13
src/boot/memory.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <efi.h>
EFI_STATUS memory_virtualize();
EFI_STATUS memory_get_map(
EFI_MEMORY_DESCRIPTOR **buffer,
UINTN *buffer_size,
UINTN *key,
UINTN *desc_size,
UINT32 *desc_version);
EFI_STATUS memory_dump_map();

18
src/boot/utility.c Normal file
View File

@@ -0,0 +1,18 @@
#include <efi.h>
struct ErrorCode {
EFI_STATUS code;
const CHAR16 *desc;
};
extern struct ErrorCode ErrorCodeTable[];
const CHAR16 *util_error_message(EFI_STATUS status) {
int32_t i = -1;
while (ErrorCodeTable[++i].desc != NULL) {
if (ErrorCodeTable[i].code == status)
return ErrorCodeTable[i].desc;
}
return L"Unknown";
}

20
src/boot/utility.h Normal file
View File

@@ -0,0 +1,20 @@
#include <efi.h>
#include <efilib.h>
#include "console.h"
#define UNUSED __attribute__((unused))
const CHAR16 *util_error_message(EFI_STATUS status);
#define CHECK_EFI_STATUS_OR_RETURN(s, msg, ...) \
if (EFI_ERROR((s))) { \
Print(L"EFI_ERROR: " msg L": %s\n", ## __VA_ARGS__, util_error_message(s)); \
return (s); \
}
#define CHECK_EFI_STATUS_OR_FAIL(s) \
if (EFI_ERROR((s))) { \
con_status_fail(util_error_message(s)); \
while (1) __asm__("hlt"); \
}