tianole/arch/x86/boot/main.c

268 lines
7.0 KiB
C

#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;
}