268 lines
7.0 KiB
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;
|
|
}
|