diff options
| author | Ada Christine <adachristine18@gmail.com> | 2021-03-17 12:38:27 +0000 |
|---|---|---|
| committer | Ada Christine <adachristine18@gmail.com> | 2021-03-17 12:38:27 +0000 |
| commit | 53ca88735db910c746d6190cca39fcd70d66b650 (patch) | |
| tree | ab0da3a88009f0ddf0e6e2634d0962c364393203 | |
initial commit
| -rw-r--r-- | .gitignore | 7 | ||||
| -rw-r--r-- | api/boot/entry/entry_efi.h | 38 | ||||
| -rw-r--r-- | api/elf/elf.h | 83 | ||||
| -rw-r--r-- | api/elf/elf64.h | 86 | ||||
| -rwxr-xr-x | debug.sh | 12 | ||||
| -rw-r--r-- | defaults.mk | 21 | ||||
| -rw-r--r-- | efi.mk | 52 | ||||
| -rwxr-xr-x | image.sh | 16 | ||||
| -rw-r--r-- | kernel/Makefile | 40 | ||||
| -rw-r--r-- | kernel/entry_efi.c | 14 | ||||
| -rw-r--r-- | kernel/kernel.ld | 28 | ||||
| -rw-r--r-- | loader/Makefile | 23 | ||||
| -rw-r--r-- | loader/entry_efi.c | 127 | ||||
| -rw-r--r-- | loader/loader_efi.c | 385 | ||||
| -rw-r--r-- | loader/loader_efi.h | 25 | ||||
| -rw-r--r-- | loader/memory_efi.c | 30 | ||||
| -rw-r--r-- | rules.mk | 41 |
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 + @@ -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 + |
