summaryrefslogtreecommitdiff
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
initial commit
-rw-r--r--.gitignore7
-rw-r--r--api/boot/entry/entry_efi.h38
-rw-r--r--api/elf/elf.h83
-rw-r--r--api/elf/elf64.h86
-rwxr-xr-xdebug.sh12
-rw-r--r--defaults.mk21
-rw-r--r--efi.mk52
-rwxr-xr-ximage.sh16
-rw-r--r--kernel/Makefile40
-rw-r--r--kernel/entry_efi.c14
-rw-r--r--kernel/kernel.ld28
-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
-rw-r--r--rules.mk41
17 files changed, 1028 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3e4069e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+*.fd
+*.img
+*.o
+*.d
+*.os
+*.efi
+
diff --git a/api/boot/entry/entry_efi.h b/api/boot/entry/entry_efi.h
new file mode 100644
index 0000000..7f6b4a4
--- /dev/null
+++ b/api/boot/entry/entry_efi.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <efi.h>
+
+#define EfiUserMemoryType (EFI_MEMORY_TYPE)0x80000000
+#define SystemMemoryType EfiUserMemoryType | 0x01
+
+struct efi_memory_map_data
+{
+ UINTN size;
+ UINTN key;
+ UINTN descsize;
+ UINT32 descver;
+ EFI_MEMORY_DESCRIPTOR data[];
+};
+
+struct efi_framebuffer_data
+{
+ EFI_PIXEL_BITMASK bitmask;
+ EFI_PHYSICAL_ADDRESS base;
+ UINTN size;
+ UINT32 width;
+ UINT32 height;
+ UINT32 pitch;
+ EFI_GRAPHICS_PIXEL_FORMAT pixel_format;
+ UINT8 pixel_size;
+};
+
+struct efi_boot_data
+{
+ EFI_SYSTEM_TABLE *system_table;
+ struct efi_memory_map_data *memory_map;
+ struct efi_framebuffer_data *framebuffer;
+ void *acpi_rsdp;
+};
+
+typedef void (*kernel_entry_func)(struct efi_boot_data *data);
+
diff --git a/api/elf/elf.h b/api/elf/elf.h
new file mode 100644
index 0000000..3d82bc0
--- /dev/null
+++ b/api/elf/elf.h
@@ -0,0 +1,83 @@
+#pragma once
+
+#include <stdint.h>
+
+typedef uint8_t Elf_Byte;
+
+#define EI_MAG0 0
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_OSABI 7
+#define EI_ABIVERSION 8
+#define EI_PAD 9
+#define EI_NIDENT 16
+
+#define ELFMAG0 0x7f
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+
+#define ELFCLASSNONE 0
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+
+#define ELFDATANONE 0
+#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
+
+#define EI_VERSION 6
+#define EV_NONE 0
+#define EV_CURRENT 1
+
+#define ELFOSABI_NONE 0
+#define ELFOSABI_SYSV EI_OSABI_NONE
+
+#define ET_NONE 0
+#define ET_REL 1
+#define ET_EXEC 2
+#define ET_DYN 3
+#define ET_CORE 4
+#define ET_LOOS 0xfe00
+#define ET_HIOS 0xfeff
+
+#define EM_NONE 0
+
+#define PT_NULL 0
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+
+#define PF_X 0x01
+#define PF_W 0x02
+#define PF_R 0x04
+
+#define SHT_NULL 0
+#define SHT_PROGITS 1
+#define SHT_SYMTAB 2
+#define SHT_STRTAB 3
+#define SHT_RELA 4
+#define SHT_HASH 5
+#define SHT_DYNAMIC 6
+
+#define SHF_WRITE 0x1
+#define SHF_ALLOC 0x2
+#define SHF_EXEC 0x4
+#define SHF_MERGE 0x10
+#define SHF_STRINGS 0x20
+
+#define DT_NULL 0
+#define DT_NEEDED 1
+#define DT_PLTRELSZ 2
+#define DT_PLTGOT 3
+#define DT_HASH 4
+#define DT_STRTAB 5
+#define DT_SYMTAB 6
+#define DT_RELA 7
+#define DT_RELASZ 8
+#define DT_RELAENT 9
+#define DT_STRSZ 10
+#define DT_SYMENT 11
+
diff --git a/api/elf/elf64.h b/api/elf/elf64.h
new file mode 100644
index 0000000..4473d15
--- /dev/null
+++ b/api/elf/elf64.h
@@ -0,0 +1,86 @@
+#pragma once
+
+#include "elf.h"
+
+typedef uint64_t Elf64_Addr;
+typedef uint64_t Elf64_Off;
+typedef uint16_t Elf64_Section;
+typedef uint16_t Elf64_Versym;
+typedef uint16_t Elf64_Half;
+typedef int32_t Elf64_Sword;
+typedef uint32_t Elf64_Word;
+typedef int64_t Elf64_Sxword;
+typedef uint64_t Elf64_Xword;
+
+typedef struct Elf64_Ehdr Elf64_Ehdr;
+typedef struct Elf64_Phdr Elf64_Phdr;
+typedef struct Elf64_Shdr Elf64_Shdr;
+typedef struct Elf64_Sym Elf64_Sym;
+typedef struct Elf64_Dyn Elf64_Dyn;
+
+#define EM_X86_64 62
+
+struct Elf64_Ehdr
+{
+ Elf_Byte e_ident[EI_NIDENT];
+ Elf64_Half e_type;
+ Elf64_Half e_machine;
+ Elf64_Word e_version;
+ Elf64_Addr e_entry;
+ Elf64_Off e_phoff;
+ Elf64_Off e_shoff;
+ Elf64_Word e_flags;
+ Elf64_Half e_ehsize;
+ Elf64_Half e_phentsize;
+ Elf64_Half e_phnum;
+ Elf64_Half e_shentsize;
+ Elf64_Half e_shnum;
+ Elf64_Half e_shstrndx;
+};
+
+struct Elf64_Phdr
+{
+ Elf64_Word p_type;
+ Elf64_Word p_flags;
+ Elf64_Off p_offset;
+ Elf64_Addr p_vaddr;
+ Elf64_Addr p_paddr;
+ Elf64_Xword p_filesz;
+ Elf64_Xword p_memsz;
+ Elf64_Xword p_align;
+};
+
+struct Elf64_Shdr
+{
+ Elf64_Word sh_name;
+ Elf64_Word sh_type;
+ Elf64_Xword sh_flags;
+ Elf64_Addr sh_addr;
+ Elf64_Off sh_offset;
+ Elf64_Xword sh_size;
+ Elf64_Word sh_link;
+ Elf64_Word sh_info;
+ Elf64_Off sh_addralign;
+ Elf64_Xword sh_entsize;
+};
+
+struct Elf64_Sym
+{
+ Elf64_Word st_name;
+ Elf_Byte st_info;
+ Elf_Byte st_other;
+ Elf64_Half st_shndx;
+ Elf64_Addr st_value;
+ Elf64_Xword st_size;
+};
+
+struct Elf64_Dyn
+{
+ Elf64_Xword d_tag;
+ union
+ {
+ Elf64_Xword d_val;
+ Elf64_Addr d_ptr;
+ };
+};
+
diff --git a/debug.sh b/debug.sh
new file mode 100755
index 0000000..1ce95b7
--- /dev/null
+++ b/debug.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+make -C loader &&
+make -C kernel &&
+
+./image.sh &&
+
+qemu-system-x86_64 -cpu qemu64 \
+ -drive if=pflash,format=raw,unit=0,file=/usr/share/ovmf/x64/OVMF_CODE.fd,readonly=on \
+ -drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd \
+ -net none -drive file=uefi.img,if=ide -monitor stdio -vga none
+
diff --git a/defaults.mk b/defaults.mk
new file mode 100644
index 0000000..3172a75
--- /dev/null
+++ b/defaults.mk
@@ -0,0 +1,21 @@
+# vim: set ft=make:ts=4:sw=4:nowrap
+
+ARCH := x86_64
+ABI := elf
+TARGET := $(ARCH)-$(ABI)
+CC := $(TARGET)-gcc
+CXX := $(TARGET)-g++
+AS := $(TARGET)-as
+LD := $(TARGET)-ld
+OBJCOPY := objcopy
+
+CFLAGS += -ffreestanding -mno-red-zone -ggdb -std=c11 \
+ -Wall -Wextra -Werror -fno-stack-protector
+
+CXXFLAGS += -ffreestanding -mno-red-zone -ggdb \
+ -Wall -Wextra -Werror -fno-stack-protector
+
+LDFLAGS += -nostdlib
+LIBDIRS += -L$(dir $(shell $(CC) -print-libgcc-file-name))
+LOADLIBES += -lgcc
+
diff --git a/efi.mk b/efi.mk
new file mode 100644
index 0000000..530a372
--- /dev/null
+++ b/efi.mk
@@ -0,0 +1,52 @@
+# vim: ft=make:nowrap:ts=4:sw=4
+
+ifeq ($(ARCH),x86_64)
+ GNUEFIARCH := x86_64
+endif
+ifeq ($(ARCH),i686)
+ GNUEFIARCH := ia32
+endif
+
+ifndef EFIAPIDIR
+EFIAPIDIR := /usr/local/include/efi
+endif
+
+ifndef EFILIBDIR
+EFILIBDIR := /usr/local/$(TARGET)/lib
+endif
+
+EFICRT := $(EFILIBDIR)/crt0-efi-$(GNUEFIARCH).o
+EFILDSCRIPT := $(EFILIBDIR)/elf_$(GNUEFIARCH)_efi.lds
+
+CPPFLAGS += -I$(EFIAPIDIR) -I$(EFIAPIDIR)/protocol -I$(EFIAPIDIR)/$(GNUEFIARCH) \
+ -DGNU_EFI_USE_MS_ABI -DEFI_CALL_WRAPPER -DEFI_DEBUG
+
+CFLAGS += -fPIC -fshort-wchar -maccumulate-outgoing-args -mno-avx -mno-sse \
+ -mno-mmx
+
+OBJCOPY.efi = $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic \
+ -j .dynsm -j .rel -j .rela -j .reloc \
+ --target=efi-app-$(GNUEFIARCH) $< $@
+
+OBJCOPY.debug.efi = $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic \
+ -j .dynsym -j .rel -j .rela -j .reloc -j .debug_info \
+ -j .debug_abbrev -j .debug_loc -j .debug_aranges \
+ -j .debug_line -j .debug_macinfo -j .debug_str \
+ --target=efi-app-$(GNUEFIARCH) $< $@
+
+LINK._efi.so = $(LD) $(LDFLAGS) -znocombreloc -Bsymbolic -shared --no-undefined \
+ $(LIBDIRS) -L$(EFILIBDIR) -T $(EFILDSCRIPT) \
+ $(filter %.o, $^) -lgnuefi -lefi $(LOADLIBES) -o $@
+
+%.efi: %_efi.so
+ $(OBJCOPY.info)
+ $(OBJCOPY.efi)
+
+%.debug.efi: %_efi.so
+ $(OBJCOPY.info)
+ @$(OBJCOPY.debug.efi)
+
+%_efi.so: $(EFILDSCRIPT) $(EFICRT)
+ $(LINK.info)
+ $(LINK._efi.so)
+
diff --git a/image.sh b/image.sh
new file mode 100755
index 0000000..e15f4ca
--- /dev/null
+++ b/image.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+imagefile=uefi.img
+loaderfile=loader/loader.efi
+kernelfile=kernel/kernel.os
+
+if [[ ! -f ${imagefile} ]]; then
+ dd if=/dev/zero of=${imagefile} bs=1024 count=1440
+ mformat -i uefi.img -f1440 ::
+fi
+
+mcopy -i ${imagefile} -Do ${loaderfile} ::
+mmd -i ${imagefile} -Do ::adasoft
+mmd -i ${imagefile} -Do ::adasoft/sophia
+mcopy -i ${imagefile} ${kernelfile} -Do ::adasoft/sophia
+
diff --git a/kernel/Makefile b/kernel/Makefile
new file mode 100644
index 0000000..36cca4c
--- /dev/null
+++ b/kernel/Makefile
@@ -0,0 +1,40 @@
+include ../defaults.mk
+
+TARGET := kernel.os
+.DEFAULT: $(TARGET)
+
+# EFI API for temporary things
+
+EFIAPIDIR := /usr/local/include/efi
+
+CPPFLAGS += -I../api -I$(EFIAPIDIR) -I$(EFIAPIDIR)/$(ARCH) -I$(EFIAPIDIR)/protocol
+CFLAGS += -Wno-unused-const-variable -Wno-unused-function -fvisibility=hidden -g
+
+LIBDIRS += -L$(dir $(shell $(CC) -print-libgcc-file-name))
+LDSCRIPT := kernel.ld
+LDFLAGS += -z max-page-size=4096 --export-dynamic
+LOADLIBES := -lgcc
+
+SOBJS :=
+GOBJS := entry_efi.o
+IOBJS :=
+
+$(SOBJS): CFLAGS += -fPIC
+# __attribute__((interrupt)) requires -mgeneral-regs-only
+$(IOBJS): CFLAGS += -mgeneral-regs-only
+$(IOBJS) $(GOBJS): CFLAGS += -mcmodel=kernel
+
+OBJS := $(SOBJS) $(GOBJS) $(IOBJS)
+DEPS := $(patsubst %.o,%.d,$(OBJS))
+
+$(TARGET): $(LDSCRIPT) $(MAKEFILE_LIST)
+$(TARGET): $(OBJS)
+
+CLEANLIST := $(wildcard $(OBJS) $(TARGET))
+DCLEANLIST := $(wildcard $(DEPS))
+
+include ../rules.mk
+-include $(DEPS)
+
+.DEFAULT: $(TARGET)
+
diff --git a/kernel/entry_efi.c b/kernel/entry_efi.c
new file mode 100644
index 0000000..1acd399
--- /dev/null
+++ b/kernel/entry_efi.c
@@ -0,0 +1,14 @@
+#include <stdbool.h>
+#include <stdnoreturn.h>
+
+#include <boot/entry/entry_efi.h>
+
+static const char kernel_entry_data[4096];
+
+noreturn void kernel_entry(struct efi_boot_data *data)
+{
+ (void)data;
+ __asm__ ("cli\n\t");
+ while (true) __asm__ ("hlt\n\t");
+}
+
diff --git a/kernel/kernel.ld b/kernel/kernel.ld
new file mode 100644
index 0000000..9538056
--- /dev/null
+++ b/kernel/kernel.ld
@@ -0,0 +1,28 @@
+ENTRY(kernel_entry)
+
+SECTIONS
+{
+ . = SIZEOF_HEADERS;
+
+ .text 0 :
+ {
+ *(.text)
+ } =0x90
+
+ .data : ALIGN(4K)
+ {
+ *(.rodata)
+ *(.data)
+ }
+
+ .bss :
+ {
+ *(.bss)
+ }
+
+ /DISCARD/ :
+ {
+ *(.eh_frame)
+ }
+}
+
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;
+ }
+}
+
diff --git a/rules.mk b/rules.mk
new file mode 100644
index 0000000..be255ba
--- /dev/null
+++ b/rules.mk
@@ -0,0 +1,41 @@
+# vim: ft=make:nowrap:ts=4:sw=4
+OBJCOPY.info = $(info $$(OBJCOPY) $< $@)
+
+LINK.os = $(LD) $(LDFLAGS) $(LIBDIRS) -T $(LDSCRIPT) $(filter %.o, $^) $(LOADLIBES) -o $@
+
+LINK.info = $(info $$(LD) $@)
+
+COMPILE.c = $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
+
+COMPILE.S = $(CC) $(CPPFLAGS) $(ASFLAGS) -c $< -o $@
+
+COMPILE.cpp = $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
+
+COMPILE.info = $(info $$(CC) $@)
+
+%.os:
+ $(LINK.info)
+ @$(LINK.os)
+
+%.debug.os: %.os
+ $(OBJCOPY) --only-keep-debug $< $@
+ $(OBJCOPY) --strip-debug $<
+
+%.o: %.c
+ $(COMPILE.info)
+ $(COMPILE.c) -MMD -MP
+
+%.o: %.cpp
+ $(COMPILE.info)
+ $(COMPILE.cpp) -MMD -MP
+
+clean:
+ $(info $$(RM) $(CLEANLIST))
+ -@$(RM) $(CLEANLIST)
+
+distclean: clean
+ $(info $$(RM) $(DCLEANLIST))
+ -@$(RM) $(DCLEANLIST)
+
+.PHONY: clean distclean all
+