#include "efi.h" #include "tianole/elf.h" #include "tianole/boot_info.h" typedef void __attribute__((sysv_abi)) (*kernel_entry_fn_t)(const tianole_boot_info_t *); static efi_char16_t boot_banner_text[] = { 'T', 'i', 'a', 'n', 'o', 'l', 'e', ' ', 'x', '8', '6', ' ', 'b', 'o', 'o', 't', 'l', 'o', 'a', 'd', 'e', 'r', '.', '\r', '\n', 0 }; static efi_char16_t kernel_path_text[] = { '\\', 'k', 'e', 'r', 'n', 'e', 'l', '.', 'e', 'l', 'f', 0 }; static inline void debug_putc(char ch) { __asm__ volatile("outb %0, $0xe9" : : "a"(ch)); } static void debug_puts(const char *text) { while (*text != '\0') { if (*text == '\n') { debug_putc('\r'); } debug_putc(*text++); } } static void *mem_copy(void *dst, const void *src, uint64_t size) { uint8_t *out = (uint8_t *)dst; const uint8_t *in = (const uint8_t *)src; uint64_t i; for (i = 0; i < size; ++i) { out[i] = in[i]; } return dst; } static void mem_zero(void *dst, uint64_t size) { uint8_t *out = (uint8_t *)dst; uint64_t i; for (i = 0; i < size; ++i) { out[i] = 0; } } static efi_status open_root( efi_handle image_handle, efi_system_table_t *system_table, efi_file_protocol_t **root ) { efi_guid_t loaded_image_guid = efi_loaded_image_protocol_guid(); efi_guid_t simple_fs_guid = efi_simple_file_system_protocol_guid(); efi_loaded_image_protocol_t *loaded_image; efi_simple_file_system_protocol_t *fs; efi_status status; status = system_table->boot_services->handle_protocol( image_handle, &loaded_image_guid, (void **)&loaded_image ); if (status != EFI_SUCCESS) { return status; } status = system_table->boot_services->handle_protocol( loaded_image->device_handle, &simple_fs_guid, (void **)&fs ); if (status != EFI_SUCCESS) { return status; } return fs->open_volume(fs, root); } static efi_status read_entire_file( efi_system_table_t *system_table, efi_file_protocol_t *root, efi_char16_t *path, void **buffer, uint64_t *size ) { efi_guid_t file_info_guid = efi_file_info_guid(); efi_file_protocol_t *file; efi_file_info_t *file_info; efi_uintn_t info_size; efi_uintn_t read_size; efi_status status; status = root->open(root, &file, path, EFI_OPEN_MODE_READ, 0); if (status != EFI_SUCCESS) { return status; } info_size = 0; status = file->get_info(file, &file_info_guid, &info_size, 0); if (status != EFI_BUFFER_TOO_SMALL) { file->close(file); return status; } status = system_table->boot_services->allocate_pool(EFI_LOADER_DATA, info_size, (void **)&file_info); if (status != EFI_SUCCESS) { file->close(file); return status; } status = file->get_info(file, &file_info_guid, &info_size, file_info); if (status != EFI_SUCCESS) { file->close(file); return status; } *size = file_info->file_size; status = system_table->boot_services->allocate_pool(EFI_LOADER_DATA, *size, buffer); if (status != EFI_SUCCESS) { file->close(file); return status; } read_size = *size; status = file->read(file, &read_size, *buffer); file->close(file); if (status != EFI_SUCCESS) { return status; } if (read_size != *size) { return EFI_LOAD_ERROR; } return EFI_SUCCESS; } static efi_status validate_kernel_elf(const elf64_ehdr_t *ehdr) { if (*(const uint32_t *)ehdr->ident != ELF_MAGIC) { return EFI_LOAD_ERROR; } if (ehdr->ident[4] != ELFCLASS64 || ehdr->ident[5] != ELFDATA2LSB) { return EFI_LOAD_ERROR; } if (ehdr->ident[6] != EV_CURRENT || ehdr->version != EV_CURRENT) { return EFI_LOAD_ERROR; } if (ehdr->type != ET_EXEC || ehdr->machine != EM_X86_64) { return EFI_LOAD_ERROR; } if (ehdr->phentsize != sizeof(elf64_phdr_t)) { return EFI_LOAD_ERROR; } return EFI_SUCCESS; } static efi_status load_kernel_image( efi_system_table_t *system_table, const void *kernel_image, uint64_t kernel_size, kernel_entry_fn_t *entry_out ) { const elf64_ehdr_t *ehdr = (const elf64_ehdr_t *)kernel_image; const elf64_phdr_t *phdrs; uint16_t index; efi_status status; if (kernel_size < sizeof(*ehdr)) { return EFI_LOAD_ERROR; } status = validate_kernel_elf(ehdr); if (status != EFI_SUCCESS) { return status; } phdrs = (const elf64_phdr_t *)((const uint8_t *)kernel_image + ehdr->phoff); for (index = 0; index < ehdr->phnum; ++index) { const elf64_phdr_t *phdr = &phdrs[index]; efi_physical_address_t segment_base; uint64_t page_count; if (phdr->type != PT_LOAD) { continue; } if (phdr->memsz < phdr->filesz) { return EFI_LOAD_ERROR; } if (phdr->offset + phdr->filesz > kernel_size) { return EFI_LOAD_ERROR; } segment_base = phdr->paddr; page_count = (phdr->memsz + 0xfffULL) >> 12; status = system_table->boot_services->allocate_pages( EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, page_count, &segment_base ); if (status != EFI_SUCCESS) { return status; } mem_zero((void *)(uintptr_t)phdr->paddr, phdr->memsz); mem_copy( (void *)(uintptr_t)phdr->paddr, (const uint8_t *)kernel_image + phdr->offset, phdr->filesz ); } *entry_out = (kernel_entry_fn_t)(uintptr_t)ehdr->entry; return EFI_SUCCESS; } efi_status EFIAPI efi_main(efi_handle image_handle, efi_system_table_t *system_table) { efi_file_protocol_t *root; void *kernel_image; uint64_t kernel_size; kernel_entry_fn_t kernel_entry; tianole_boot_info_t boot_info = { .version = TIANOLE_BOOT_INFO_VERSION, .boot_flags = TIANOLE_BOOT_FLAG_BOOT_SERVICES_ACTIVE, }; efi_status status; system_table->con_out->output_string(system_table->con_out, boot_banner_text); debug_puts("Tianole x86 bootloader loaded.\n"); status = open_root(image_handle, system_table, &root); if (status != EFI_SUCCESS) { debug_puts("failed: open_root\n"); return status; } status = read_entire_file(system_table, root, kernel_path_text, &kernel_image, &kernel_size); root->close(root); if (status != EFI_SUCCESS) { debug_puts("failed: read kernel.elf\n"); return status; } status = load_kernel_image(system_table, kernel_image, kernel_size, &kernel_entry); if (status != EFI_SUCCESS) { debug_puts("failed: load kernel image\n"); return status; } debug_puts("jumping to kernel entry\n"); kernel_entry(&boot_info); debug_puts("kernel returned unexpectedly\n"); for (;;) { system_table->boot_services->stall(1000 * 1000); } return EFI_SUCCESS; }