summaryrefslogtreecommitdiff
path: root/loader
diff options
context:
space:
mode:
authorAda Christine <adachristine18@gmail.com>2021-03-17 12:38:27 +0000
committerAda Christine <adachristine18@gmail.com>2021-03-17 12:38:27 +0000
commit53ca88735db910c746d6190cca39fcd70d66b650 (patch)
treeab0da3a88009f0ddf0e6e2634d0962c364393203 /loader
initial commit
Diffstat (limited to 'loader')
-rw-r--r--loader/Makefile23
-rw-r--r--loader/entry_efi.c127
-rw-r--r--loader/loader_efi.c385
-rw-r--r--loader/loader_efi.h25
-rw-r--r--loader/memory_efi.c30
5 files changed, 590 insertions, 0 deletions
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 <stdbool.h>
+#include <stddef.h>
+
+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 <boot/entry/entry_efi.h>
+#include <elf/elf64.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#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 <efi.h>
+#include <efilib.h>
+
+#include <stddef.h>
+#include <stdnoreturn.h>
+
+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;
+ }
+}
+