diff options
Diffstat (limited to 'loader/image_efi.c')
| -rw-r--r-- | loader/image_efi.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/loader/image_efi.c b/loader/image_efi.c new file mode 100644 index 0000000..04d0a9b --- /dev/null +++ b/loader/image_efi.c @@ -0,0 +1,211 @@ +#include "loader_efi.h" + +static bool validate_image(Elf64_Ehdr *ehdr); +static size_t image_size(struct system_image *image); +static enum page_type get_page_type(Elf64_Phdr *phdr); + +struct system_image *system_image_open(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; + } + + return image; + +failure: + file->Close(file); + return NULL; +} + +bool system_image_load(struct system_image *image) +{ + image->buffer = system_allocate(image_size(image)); + + if (image->buffer.type != SYSTEM_MEMORY) + { + return false; + } + + e_bs->SetMem((VOID *)image->buffer.base, image->buffer.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 %16.0lx at offset %x of %d bytes\r\n", + phdrs[i].p_vaddr, + phdrs[i].p_offset, + phdrs[i].p_memsz); + + void *segment = (void *)(image->buffer.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; +} + +void system_image_map(struct system_image *image) +{ + Elf64_Ehdr *ehdr = &image->ehdr; + Elf64_Phdr *phdrs = image->phdrs; + + for (int i = 0; i < ehdr->e_phnum; i++) + { + if (phdrs[i].p_type == PT_LOAD) + { + void *virt_begin = (void *)phdrs[i].p_vaddr; + uint64_t phys_begin = image->buffer.base + phdrs[i].p_paddr; + size_t size = phdrs[i].p_memsz; + Print(L"mapping segment %16.0lx to %16.0lx %d bytes\r\n", + virt_begin, + phys_begin, + size); + + paging_map_pages(virt_begin, + phys_begin, + get_page_type(&phdrs[i]), + size); + } + } +} + + +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 size_t image_size(struct system_image *image) +{ + size_t 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 enum page_type get_page_type(Elf64_Phdr *phdr) +{ + switch (phdr->p_flags) + { + case (PF_R|PF_X): + return CODE_PAGE_TYPE; + case (PF_R): + return RODATA_PAGE_TYPE; + case (PF_R|PF_W): + return DATA_PAGE_TYPE; + } + + return INVALID_PAGE_TYPE; +} |
