From 53ca88735db910c746d6190cca39fcd70d66b650 Mon Sep 17 00:00:00 2001 From: Ada Christine Date: Wed, 17 Mar 2021 12:38:27 +0000 Subject: initial commit --- loader/Makefile | 23 ++++ loader/entry_efi.c | 127 +++++++++++++++++ loader/loader_efi.c | 385 ++++++++++++++++++++++++++++++++++++++++++++++++++++ loader/loader_efi.h | 25 ++++ loader/memory_efi.c | 30 ++++ 5 files changed, 590 insertions(+) create mode 100644 loader/Makefile create mode 100644 loader/entry_efi.c create mode 100644 loader/loader_efi.c create mode 100644 loader/loader_efi.h create mode 100644 loader/memory_efi.c (limited to 'loader') diff --git a/loader/Makefile b/loader/Makefile new file mode 100644 index 0000000..2e849dc --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,23 @@ +include ../defaults.mk +include ../efi.mk + +IMAGE := loader.efi +BUILD := $(IMAGE) $(IMAGE:.efi=.debug.efi) + +CPPFLAGS += -I../api + +all: $(BUILD) + +OBJS := entry_efi.o loader_efi.o memory_efi.o +DEPS := $(OBJS:.o=.d) + +IMAGE_SO := $(IMAGE:.efi=_efi.so) +$(IMAGE_SO): $(OBJS) +.INTERMEDIATE: $(IMAGE_SO) + +CLEANLIST := $(wildcard $(BUILD) $(OBJS)) +DCLEANLIST := $(CLEANLIST) $(wildcard $(DEPS)) + +include ../rules.mk +-include $(DEPS) + diff --git a/loader/entry_efi.c b/loader/entry_efi.c new file mode 100644 index 0000000..fa27c02 --- /dev/null +++ b/loader/entry_efi.c @@ -0,0 +1,127 @@ +#include "loader_efi.h" + +#include +#include + +EFI_SYSTEM_TABLE *e_st; +EFI_BOOT_SERVICES *e_bs; +EFI_RUNTIME_SERVICES *e_rt; + +EFI_HANDLE e_image_handle; +EFI_LOADED_IMAGE_PROTOCOL *e_loaded_image; +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *e_system_partition; +EFI_GRAPHICS_OUTPUT_PROTOCOL *e_graphics_output; +EFI_STATUS e_last_error; + +extern int _text; +extern int _data; + +static EFI_LOADED_IMAGE_PROTOCOL *open_loaded_image(void); +static EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *open_system_partition(void); +static EFI_GRAPHICS_OUTPUT_PROTOCOL *open_graphics_output(void); + +EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table) +{ + e_image_handle = image_handle; + e_st = system_table; + e_bs = e_st->BootServices; + e_rt = e_st->RuntimeServices; + + InitializeLib(e_image_handle, e_st); + Print(L"loader starting\r\n"); + + e_loaded_image = open_loaded_image(); + e_system_partition = open_system_partition(); + e_graphics_output = open_graphics_output(); + + loader_main(); + + efi_exit(EFI_ABORTED); +} + +noreturn void efi_exit(EFI_STATUS status) +{ + if (EFI_ERROR(status)) + { + Print(L"loader exited: %r\r\n", status); + } + + e_bs->Exit(e_image_handle, status, 0, NULL); + + while (true); +} + +static EFI_LOADED_IMAGE_PROTOCOL *open_loaded_image(void) +{ + Print(L"opening loaded image\r\n"); + + EFI_STATUS status; + EFI_LOADED_IMAGE_PROTOCOL *image; + + status = e_bs->OpenProtocol(e_image_handle, + &LoadedImageProtocol, + (void **)&image, + e_image_handle, + NULL, + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); + + if (EFI_ERROR(status)) + { + Print(L"failed opening loaded image\r\n"); + efi_exit(status); + } + + Print(L"image base: 0x%16.0x\r\n", image->ImageBase); + Print(L"image .text segment: 0x%16.0x\r\n", &_text); + Print(L"image .data segment: 0x%16.0x\r\n", &_data); + + return image; +} + +static EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *open_system_partition(void) +{ + Print(L"opening system partition\r\n"); + + EFI_STATUS status; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *partition; + + status = e_bs->OpenProtocol(e_loaded_image->DeviceHandle, + &FileSystemProtocol, + (void **)&partition, + e_image_handle, + NULL, + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); + + if (EFI_ERROR(status)) + { + Print(L"failed opening system partition\r\n"); + efi_exit(status); + } + + Print(L"opened system partition\r\n"); + + return partition; +} + +static EFI_GRAPHICS_OUTPUT_PROTOCOL *open_graphics_output(void) +{ + EFI_STATUS status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *graphics; + + Print(L"opening graphics output\r\n"); + + status = e_bs->LocateProtocol(&GraphicsOutputProtocol, + NULL, + (VOID **)&graphics); + + if (EFI_ERROR(status)) + { + Print(L"failed opening graphics output: %r\r\n", status); + return NULL; + } + + Print(L"graphics output opened\r\n"); + + return graphics; +} + diff --git a/loader/loader_efi.c b/loader/loader_efi.c new file mode 100644 index 0000000..f3402d9 --- /dev/null +++ b/loader/loader_efi.c @@ -0,0 +1,385 @@ +#include "loader_efi.h" + +#include +#include + +#include +#include + +#define kernel_path L"\\adasoft\\sophia\\kernel.os" + +struct system_buffer +{ + EFI_PHYSICAL_ADDRESS base; + EFI_PHYSICAL_ADDRESS head; + UINTN pages; +}; + +struct system_image +{ + EFI_FILE_PROTOCOL *file; + EFI_PHYSICAL_ADDRESS base; + UINTN size; + Elf64_Ehdr ehdr; + Elf64_Phdr *phdrs; +}; + +static struct system_buffer get_system_buffer(size_t size); +static EFI_PHYSICAL_ADDRESS system_allocate(struct system_buffer *buffer, + size_t size); +static struct system_image *open_system_image(CHAR16 *path); +static bool load_system_image(struct system_buffer *buffer, + struct system_image *image); +static struct efi_memory_map_data *get_memory_map_data(void); +static struct efi_framebuffer_data *get_framebuffer_data(void); +static void *get_acpi_rsdp(void); + +void loader_main(void) +{ + struct efi_boot_data data; + + struct system_buffer kernel_buffer = get_system_buffer(0x400000); + struct system_image *kernel_image = open_system_image(kernel_path); + + if (load_system_image(&kernel_buffer, kernel_image)) + { + Print(L"loaded kernel image at base %16.0x size %d bytes\r\n", + kernel_image->base, + kernel_image->size); + } + else + { + Print(L"failed loading kernel image\r\n"); + efi_exit(EFI_ABORTED); + } + + kernel_entry_func kernel_entry; + kernel_entry = (kernel_entry_func)(kernel_image->base + + kernel_image->ehdr.e_entry); + + data.system_table = e_st; + data.memory_map = get_memory_map_data(); + data.framebuffer = get_framebuffer_data(); + data.acpi_rsdp = get_acpi_rsdp(); + + EFI_STATUS status = e_bs->ExitBootServices(e_image_handle, + data.memory_map->key); + + if (EFI_ERROR(status)) + { + Print(L"failed exiting boot services environment: %r\r\n", status); + efi_exit(status); + } + + kernel_entry(&data); +} + +static struct system_buffer get_system_buffer(size_t size) +{ + EFI_STATUS status; + struct system_buffer buffer; + + buffer.pages = EFI_SIZE_TO_PAGES(size); + + status = e_bs->AllocatePages(AllocateAnyPages, + SystemMemoryType, + buffer.pages, + &buffer.base); + + if (!EFI_ERROR(status)) + { + buffer.head = buffer.base; + } + else + { + Print(L"error allocating system buffer: %r\r\n", status); + buffer.base = 0; + buffer.pages = 0; + } + + return buffer; +} + +static EFI_PHYSICAL_ADDRESS system_allocate(struct system_buffer *buffer, + UINTN size) +{ + if (EFI_SIZE_TO_PAGES(buffer->head + size - buffer->base) < buffer->pages) + { + EFI_PHYSICAL_ADDRESS result = buffer->head; + buffer->head += size; + return result; + } + + return 0; +} + +static bool validate_image(Elf64_Ehdr *ehdr) +{ + if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || + ehdr->e_ident[EI_MAG1] != ELFMAG1 || + ehdr->e_ident[EI_MAG2] != ELFMAG2 || + ehdr->e_ident[EI_MAG3] != ELFMAG3 || + ehdr->e_ident[EI_CLASS] != ELFCLASS64 || + ehdr->e_ident[EI_DATA] != ELFDATA2LSB || + ehdr->e_ident[EI_VERSION] != EV_CURRENT || + ehdr->e_machine != EM_X86_64) + { + Print(L"invalid image format\r\n"); + return false; + } + + Print(L"elf x64 image detected\r\n"); + + return true; +} + +static UINTN get_image_size(struct system_image *image) +{ + UINTN size = 0; + Elf64_Phdr *phdrs = image->phdrs; + + for (int i = 0; i < image->ehdr.e_phnum; i++) + { + if (phdrs[i].p_type != PT_LOAD) + { + continue; + } + + if (phdrs[i].p_paddr + phdrs[i].p_memsz > size) + { + size = phdrs[i].p_paddr + phdrs[i].p_memsz; + if (phdrs[i].p_align > 1) + size = (size + phdrs[i].p_align - 1) & ~(phdrs[i].p_align - 1); + } + } + + return size; +} + +static struct system_image *open_system_image(CHAR16 *path) +{ + EFI_STATUS status; + EFI_FILE_PROTOCOL *root; + EFI_FILE_PROTOCOL *file; + struct system_image *image = NULL; + + status = e_system_partition->OpenVolume(e_system_partition, &root); + + if (EFI_ERROR(status)) + { + Print(L"failed opening system partition root: %r\r\n", status); + return NULL; + } + + status = root->Open(root, &file, path, EFI_FILE_MODE_READ, 0); + + if (EFI_ERROR(status)) + { + Print(L"failed opening image file %s: %r\r\n", path, status); + return NULL; + } + + image = efi_allocate(sizeof(*image)); + + if (!image) + { + Print(L"failed allocating image data: %r\r\n", e_last_error); + goto failure; + } + + image->file = file; + + UINTN ehdr_size = sizeof(image->ehdr); + status = file->Read(file, &ehdr_size, &image->ehdr); + + if (EFI_ERROR(status)) + { + Print(L"failed reading image file %s: %r\r\n", path, status); + goto failure; + } + + if (!validate_image(&image->ehdr)) + { + Print(L"failed validating image file %s: %r\r\n", path, status); + goto failure; + } + + UINTN phdrs_size = image->ehdr.e_phentsize * image->ehdr.e_phnum; + image->phdrs = efi_allocate(phdrs_size); + + if (!image->phdrs) + { + Print(L"failed allocating segment data: %r", e_last_error); + goto failure; + } + + status = file->Read(file, &phdrs_size, image->phdrs); + + if (EFI_ERROR(status)) + { + Print(L"failed reading segment data: %r", status); + goto failure; + } + + image->size = get_image_size(image); + image->base = 0; + + return image; + +failure: + file->Close(file); + return NULL; +} + +static bool load_system_image(struct system_buffer *buffer, + struct system_image *image) +{ + image->base = system_allocate(buffer, image->size); + + if (!image->base) + { + Print(L"system buffer is full\r\n"); + return false; + } + + e_bs->SetMem((VOID *)image->base, image->size, 0); + + Elf64_Ehdr *ehdr = &image->ehdr; + Elf64_Phdr *phdrs = image->phdrs; + + Print(L"found %d segments in image\r\n", ehdr->e_phnum); + + EFI_STATUS status = EFI_SUCCESS; + + for (UINTN i = 0; i < ehdr->e_phnum && !EFI_ERROR(status); i++) + { + switch (phdrs[i].p_type) + { + case PT_LOAD: + Print(L"loadable segment at offset %x of %d bytes\r\n", + phdrs[i].p_offset, + phdrs[i].p_memsz); + + void *segment = (void *)(image->base + phdrs[i].p_paddr); + UINTN segment_size = phdrs[i].p_filesz; + + image->file->SetPosition(image->file, phdrs[i].p_offset); + status = image->file->Read(image->file, &segment_size, segment); + break; + + default: + continue; + } + } + + if (EFI_ERROR(status)) + { + Print(L"failure reading segment from file: %r\r\n", status); + return false; + } + + return true; +} + + +static struct efi_memory_map_data *get_memory_map_data(void) +{ + EFI_STATUS status; + UINTN mapsize = 0; + struct efi_memory_map_data *map = NULL; + + status = e_bs->GetMemoryMap(&mapsize, NULL, NULL, NULL, NULL); + + if (status == EFI_BUFFER_TOO_SMALL) + { + status = e_bs->AllocatePool(EfiLoaderData, + mapsize + sizeof(*map), + (VOID **)&map); + } + + if (!EFI_ERROR(status)) + { + status = e_bs->GetMemoryMap(&map->size, + map->data, + &map->key, + &map->descsize, + &map->descver); + } + + if (EFI_ERROR(status)) + { + Print(L"failed getting memory map: %r\r\n", status); + return NULL; + } + + return map; +} + +static struct efi_framebuffer_data *get_framebuffer_data(void) +{ + EFI_STATUS status; + struct efi_framebuffer_data *framebuffer; + + if (!e_graphics_output) + { + return NULL; + } + + status = e_bs->AllocatePool(EfiLoaderData, + sizeof *framebuffer, + (VOID **)&framebuffer); + + if (!EFI_ERROR(status)) + { + EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode = e_graphics_output->Mode; + + framebuffer->bitmask = mode->Info->PixelInformation; + framebuffer->width = mode->Info->HorizontalResolution; + framebuffer->height = mode->Info->VerticalResolution; + framebuffer->base = mode->FrameBufferBase; + framebuffer->size = mode->FrameBufferSize; + framebuffer->pixel_format = mode->Info->PixelFormat; + + switch (framebuffer->pixel_format) + { + //falthrough + case PixelBlueGreenRedReserved8BitPerColor: + case PixelRedGreenBlueReserved8BitPerColor: + framebuffer->pixel_size = sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + break; + default: + // TODO: calculate pixel size for bitmask format + framebuffer->pixel_size = 0; + } + + framebuffer->pitch = framebuffer->width * framebuffer->pixel_size; + } + + else + { + Print(L"failed getting framebuffer data: %r\r\n", status); + return NULL; + } + + return framebuffer; +} + +static void *get_acpi_rsdp(void) +{ + EFI_GUID acpi_20_guid = ACPI_20_TABLE_GUID; + + for (UINTN i = 0; i < e_st->NumberOfTableEntries; i++) + { + if (!CompareGuid(&acpi_20_guid, &e_st->ConfigurationTable[i].VendorGuid)) + { + Print(L"acpi rsdp: 0x%16.0x\r\n", + e_st->ConfigurationTable[i].VendorTable); + + return e_st->ConfigurationTable[i].VendorTable; + } + } + + Print(L"failed getting acpi rsdp\r\n"); + return NULL; +} + diff --git a/loader/loader_efi.h b/loader/loader_efi.h new file mode 100644 index 0000000..a08f4c6 --- /dev/null +++ b/loader/loader_efi.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include +#include + +extern EFI_SYSTEM_TABLE *e_st; +extern EFI_BOOT_SERVICES *e_bs; +extern EFI_RUNTIME_SERVICES *e_rt; + +extern EFI_HANDLE e_image_handle; +extern EFI_LOADED_IMAGE_PROTOCOL *e_loaded_image; +extern EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *e_system_partition; +extern EFI_GRAPHICS_OUTPUT_PROTOCOL *e_graphics_output; +extern EFI_STATUS e_last_error; + +noreturn void efi_exit(EFI_STATUS status); + +void *efi_allocate(size_t size); +void efi_free(void *buffer); + +void loader_main(void); + diff --git a/loader/memory_efi.c b/loader/memory_efi.c new file mode 100644 index 0000000..3dae44f --- /dev/null +++ b/loader/memory_efi.c @@ -0,0 +1,30 @@ +#include "loader_efi.h" + +void *efi_allocate(size_t size) +{ + EFI_STATUS status; + void *buffer; + + status = e_bs->AllocatePool(EfiLoaderData, size, &buffer); + + if (EFI_ERROR(status)) + { + e_last_error = status; + buffer = NULL; + } + + return buffer; +} + +void efi_free(void *buffer) +{ + EFI_STATUS status; + + status = e_bs->FreePool(buffer); + + if (EFI_ERROR(status)) + { + e_last_error = status; + } +} + -- cgit v1.3.1-1-g115d