From 4fb782449f39918a0a87ec81e6c0d86e22bdcc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20S=C5=82omi=C5=84ski?= Date: Tue, 29 Jun 2021 15:16:25 +0200 Subject: [PATCH] protos: add initial multiboot1 implementation --- stage23/entry.s3.c | 3 + stage23/protos/multiboot1.32.c | 26 ++++ stage23/protos/multiboot1.c | 222 +++++++++++++++++++++++++++++++++ stage23/protos/multiboot1.h | 101 +++++++++++++++ 4 files changed, 352 insertions(+) create mode 100644 stage23/protos/multiboot1.32.c create mode 100644 stage23/protos/multiboot1.c create mode 100644 stage23/protos/multiboot1.h diff --git a/stage23/entry.s3.c b/stage23/entry.s3.c index e79fbbc9..15520774 100644 --- a/stage23/entry.s3.c +++ b/stage23/entry.s3.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,8 @@ void stage3_common(void) { linux_load(config, cmdline); } else if (!strcmp(proto, "chainload")) { chainload(config); + } else if (!strcmp(proto, "multiboot1")) { + multiboot1_load(config, cmdline); } panic("Invalid protocol specified"); diff --git a/stage23/protos/multiboot1.32.c b/stage23/protos/multiboot1.32.c new file mode 100644 index 00000000..dacbb633 --- /dev/null +++ b/stage23/protos/multiboot1.32.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +__attribute__((noreturn)) void multiboot1_spinup_32( + uint32_t entry_point, uint32_t multiboot1_info) { + asm volatile ( + "cli\n\t" + "cld\n\t" + + "pushfl\n\t" + "pushl $0x18\n\t" + "pushl %%edi\n\t" + + "movl $0x2BADB002, %%eax\n\t" + + "iretl\n\t" + : + : "D" (entry_point), + "b" (multiboot1_info) + : "memory" + ); + + __builtin_unreachable(); +} diff --git a/stage23/protos/multiboot1.c b/stage23/protos/multiboot1.c new file mode 100644 index 00000000..0121d356 --- /dev/null +++ b/stage23/protos/multiboot1.c @@ -0,0 +1,222 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct multiboot1_info multiboot1_info = {0}; + +void multiboot1_load(char *config, char *cmdline) { + struct file_handle *kernel_file = ext_mem_alloc(sizeof(*kernel_file)); + + char *kernel_path = config_get_value(config, 0, "KERNEL_PATH"); + if (kernel_path == NULL) + panic("multiboot1: KERNEL_PATH not specified"); + + print("multiboot1: Loading kernel `%s`...\n", kernel_path); + + if (!uri_open(kernel_file, kernel_path)) + panic("multiboot1: Could not open kernel resource"); + + uint8_t *kernel = freadall(kernel_file, MEMMAP_USABLE); + + struct multiboot1_header header = {0}; + + for (size_t i = 0; i < 8192; i += 4) { + uint32_t v; + memcpy(&v, kernel + i, 4); + + if (v == MULTIBOOT1_HEADER_MAGIC) { + memcpy(&header, kernel + i, sizeof(header)); + break; + } + } + + if (header.magic != MULTIBOOT1_HEADER_MAGIC) + panic("multiboot1: Could not find header"); + + if (header.magic + header.flags + header.checksum) + panic("multiboot1: Header checksum is invalid"); + + if (header.flags & (1 << 16)) + panic("multiboot1: Aout kludge not supported"); + + if (elf_bits(kernel) != 32) + panic("multiboot1: Kernel binary must be 32-bit"); + + uint32_t entry_point = 0; + + if (elf32_load(kernel, (uint32_t *)&entry_point, MEMMAP_USABLE)) + panic("multiboot1: ELF32 load failure"); + + uint32_t n_modules; + + for (n_modules = 0; ; n_modules++) { + if (config_get_value(config, n_modules, "MODULE_PATH") == NULL) + break; + } + + if (n_modules) { + struct multiboot1_module *mods = ext_mem_alloc(sizeof(*mods) * n_modules); + + multiboot1_info.mods_count = n_modules; + multiboot1_info.mods_addr = (uint32_t)(size_t)mods; + + for (size_t i = 0; i < n_modules; i++) { + struct multiboot1_module *m = mods + i; + + char *module_path = config_get_value(config, i, "MODULE_PATH"); + if (module_path == NULL) + panic("multiboot1: Module disappeared unexpectedly"); + + print("multiboot1: Loading module `%s`...\n", module_path); + + struct file_handle f; + if (!uri_open(&f, module_path)) + panic("multiboot1: Requested module with path \"%s\" not found!", module_path); + + char *cmdline = config_get_value(config, i, "MODULE_STRING"); + + m->begin = (uint32_t)(size_t)freadall(&f, MEMMAP_USABLE); + m->end = m->begin + f.size; + m->cmdline = (uint32_t)(size_t)cmdline; + m->pad = 0; + + if (verbose) { + print("multiboot1: Requested module %u:\n", i); + print(" Path: %s\n", module_path); + print(" String: \"%s\"\n", cmdline ?: ""); + print(" Begin: %x\n", m->begin); + print(" End: %x\n", m->end); + } + } + + multiboot1_info.flags |= (1 << 3); + } + + multiboot1_info.cmdline = (uint32_t)(size_t)cmdline; + if (cmdline) + multiboot1_info.flags |= (1 << 2); + + multiboot1_info.bootloader_name = (uint32_t)(size_t)"Limine"; + multiboot1_info.flags |= (1 << 9); + + term_deinit(); + + if (header.flags & (1 << 2)) { + int req_width = header.fb_width; + int req_height = header.fb_height; + int req_bpp = header.fb_bpp; + + if (header.fb_mode == 0) { + char *resolution = config_get_value(config, 0, "RESOLUTION"); + if (resolution != NULL) + parse_resolution(&req_width, &req_height, &req_bpp, resolution); + + struct fb_info fbinfo; + if (!fb_init(&fbinfo, req_width, req_height, req_bpp)) + panic("multiboot1: Unable to set video mode"); + + multiboot1_info.fb_addr = (uint64_t)fbinfo.framebuffer_addr; + multiboot1_info.fb_width = fbinfo.framebuffer_width; + multiboot1_info.fb_height = fbinfo.framebuffer_height; + multiboot1_info.fb_bpp = fbinfo.framebuffer_bpp; + multiboot1_info.fb_pitch = fbinfo.framebuffer_pitch; + multiboot1_info.fb_type = 1; + multiboot1_info.fb_red_mask_size = fbinfo.red_mask_size; + multiboot1_info.fb_red_mask_shift = fbinfo.red_mask_shift; + multiboot1_info.fb_green_mask_size = fbinfo.green_mask_size; + multiboot1_info.fb_green_mask_shift = fbinfo.green_mask_shift; + multiboot1_info.fb_blue_mask_size = fbinfo.blue_mask_size; + multiboot1_info.fb_blue_mask_shift = fbinfo.blue_mask_shift; + } else if (header.fb_mode == 1) { +#if defined (uefi) + panic("multiboot1: Cannot use text mode with UEFI."); +#elif defined (bios) + int rows, cols; + init_vga_textmode(&rows, &cols, false); + + multiboot1_info.fb_addr = 0xB8000; + multiboot1_info.fb_width = cols; + multiboot1_info.fb_height = rows; + multiboot1_info.fb_bpp = 16; + multiboot1_info.fb_pitch = 2 * cols; + multiboot1_info.fb_type = 2; +#endif + } else { + panic("multiboot1: Illegal framebuffer type requested"); + } + + multiboot1_info.flags |= (1 << 12); + } + +#if defined (uefi) + efi_exit_boot_services(); +#endif + + size_t memmap_entries; + struct e820_entry_t *memmap = get_memmap(&memmap_entries); + + // The layouts of the e820_entry_t and multiboot1_mmap_entry structs match almost perfectly + // apart from the padding/size being in the wrong place (at the end and beginning respectively). + // To be able to use the memmap directly, we offset it back by 4 so the fields align properly. + // Since we're about to exit we don't really care about what we've clobbered by doing this. + struct multiboot1_mmap_entry *mmap = (void *)((size_t)memmap - 4); + + size_t memory_lower = 0, memory_upper = 0; + + for (size_t i = 0; i < memmap_entries; i++ ){ + mmap[i].size = sizeof(*mmap) - 4; + + if (memmap[i].type == MEMMAP_BOOTLOADER_RECLAIMABLE) + memmap[i].type = MEMMAP_USABLE; + + if (memmap[i].type == MEMMAP_USABLE) { + if (memmap[i].base < 0x100000) + memory_lower += memmap[i].length; + else + memory_upper += memmap[i].length; + } + } + + multiboot1_info.mem_lower = memory_lower / 1024; + multiboot1_info.mem_upper = memory_upper / 1024; + + multiboot1_info.mmap_length = sizeof(*mmap) * memmap_entries; + multiboot1_info.mmap_addr = ((uint32_t)(size_t)mmap); + multiboot1_info.flags |= (1 << 0) | (1 << 6); + + multiboot1_spinup(entry_point, (uint32_t)(uintptr_t)&multiboot1_info); +} + +__attribute__((noreturn)) void multiboot1_spinup_32( + uint32_t entry_point, + uint32_t multiboot1_info); + +__attribute__((noreturn)) void multiboot1_spinup( + uint32_t entry_point, uint32_t multiboot1_info) { + pic_mask_all(); + pic_flush(); + +#if defined (uefi) + do_32(multiboot1_spinup_32, 2, entry_point, multiboot1_info); +#endif + +#if defined (bios) + multiboot1_spinup_32(entry_point, multiboot1_info); +#endif + + __builtin_unreachable(); +} diff --git a/stage23/protos/multiboot1.h b/stage23/protos/multiboot1.h new file mode 100644 index 00000000..acc0ba17 --- /dev/null +++ b/stage23/protos/multiboot1.h @@ -0,0 +1,101 @@ +#ifndef __PROTOS__MULTIBOOT1_H__ +#define __PROTOS__MULTIBOOT1_H__ + +#include +#include + +#define MULTIBOOT1_HEADER_MAGIC 0x1BADB002 + +struct multiboot1_header { + uint32_t magic; + uint32_t flags; + uint32_t checksum; + + uint32_t header_addr; + uint32_t load_addr; + uint32_t load_end_addr; + uint32_t bss_end_addr; + uint32_t entry_addr; + + uint32_t fb_mode; + uint32_t fb_width; + uint32_t fb_height; + uint32_t fb_bpp; +}; + +struct multiboot1_elf_sections { + uint32_t num; + uint32_t size; + uint32_t addr; + uint32_t shndx; +}; + +struct multiboot1_info { + uint32_t flags; + + uint32_t mem_lower; + uint32_t mem_upper; + + uint32_t boot_device; + + uint32_t cmdline; + + uint32_t mods_count; + uint32_t mods_addr; + + struct multiboot1_elf_sections elf_sect; + + uint32_t mmap_length; + uint32_t mmap_addr; + + uint32_t drives_length; + uint32_t drivers_addr; + + uint32_t rom_config_table; + + uint32_t bootloader_name; + + uint32_t apm_table; + + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; + + uint64_t fb_addr; + uint32_t fb_pitch; + uint32_t fb_width; + uint32_t fb_height; + uint8_t fb_bpp; + uint8_t fb_type; + + uint8_t fb_red_mask_shift; + uint8_t fb_red_mask_size; + uint8_t fb_green_mask_shift; + uint8_t fb_green_mask_size; + uint8_t fb_blue_mask_shift; + uint8_t fb_blue_mask_size; +}; + +struct multiboot1_module { + uint32_t begin; + uint32_t end; + uint32_t cmdline; + uint32_t pad; +}; + +struct multiboot1_mmap_entry { + uint32_t size; + uint64_t addr; + uint64_t len; + uint32_t type; +} __attribute__((packed)); + +void multiboot1_load(char *config, char *cmdline); + +__attribute__((noreturn)) void multiboot1_spinup( + uint32_t entry_point, uint32_t multiboot1_info); + +#endif