From a2f7d2d314d3857e6e9347b096495117d4b6cb23 Mon Sep 17 00:00:00 2001 From: mintsuki Date: Wed, 29 Jun 2022 07:26:06 +0200 Subject: [PATCH] multiboot1: Add ahead-of-time info size calculation --- common/protos/multiboot1.c | 88 ++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/common/protos/multiboot1.c b/common/protos/multiboot1.c index 0abea65a..ab426fa3 100644 --- a/common/protos/multiboot1.c +++ b/common/protos/multiboot1.c @@ -25,6 +25,29 @@ noreturn void multiboot1_spinup_32(uint32_t entry_point, uint32_t multiboot1_inf static uint32_t kernel_top; +#define LIMINE_BRAND "Limine " LIMINE_VERSION + +// Returns the size required to store the multiboot info. +static size_t get_multiboot1_info_size( + char *cmdline, + size_t modules_count, size_t modules_cmdlines_size, + uint32_t section_entry_size, uint32_t section_num +) { + return ALIGN_UP(sizeof(struct multiboot1_info), 16) + // base structure + ALIGN_UP(strlen(cmdline) + 1, 16) + // cmdline + ALIGN_UP(sizeof(LIMINE_BRAND), 16) + // bootloader brand + ALIGN_UP(sizeof(section_entry_size * section_num), 16) + // ELF info + ALIGN_UP(sizeof(struct multiboot1_module) * modules_count, 16) + // modules count + ALIGN_UP(modules_cmdlines_size, 16) + // modules command lines + ALIGN_UP(sizeof(struct multiboot1_mmap_entry) * 256, 16); // memory map +} + +static void *mb1_info_alloc(void **mb1_info_raw, size_t size) { + void *ret = *mb1_info_raw; + *mb1_info_raw += ALIGN_UP(size, 16); + return ret; +} + static bool mb1_overlap_check(uint64_t base1, uint64_t top1, uint64_t base2, uint64_t top2) { return ((base1 >= base2 && base1 < top2) @@ -183,20 +206,39 @@ retry_raw_load:; kernel_top = 0x100000; } + size_t n_modules; + size_t modules_cmdlines_size = 0; + + for (n_modules = 0;; n_modules++) { + struct conf_tuple conf_tuple = config_get_tuple(config, n_modules, "MODULE_PATH", "MODULE_STRING"); + if (!conf_tuple.value1) break; + + char *module_cmdline = conf_tuple.value2; + if (!module_cmdline) module_cmdline = ""; + modules_cmdlines_size += ALIGN_UP(strlen(module_cmdline) + 1, 16); + } + + size_t mb1_info_size = get_multiboot1_info_size( + cmdline, + n_modules, + modules_cmdlines_size, + section_hdr_info ? section_hdr_info->section_entry_size : 0, + section_hdr_info ? section_hdr_info->num : 0 + ); + // GRUB allocates boot info at 0x10000, *except* if the kernel happens // to overlap this region, then it gets moved to right after the // kernel, or whichever PHDR happens to sit at 0x10000. // Allocate it wherever, then move it to where GRUB puts it // afterwards. - size_t mb1_info_size = sizeof(struct multiboot1_info); - struct multiboot1_info *multiboot1_info = ext_mem_alloc(mb1_info_size); + void *mb1_info_raw = ext_mem_alloc(mb1_info_size); uint64_t mb1_info_final_loc = 0x10000; retry_mb1_info_reloc: for (size_t i = 0; i < elf_ranges_count; i++) { uint64_t mb1_info_top = mb1_info_final_loc + mb1_info_size; uint64_t base = elf_ranges[i].base - slide; - uint64_t length = elf_ranges[i].length - slide; + uint64_t length = elf_ranges[i].length; uint64_t top = base + length; // Do they overlap? @@ -216,18 +258,24 @@ retry_mb1_info_reloc: } } + size_t mb1_info_slide = (size_t)mb1_info_raw - mb1_info_final_loc; + if (mb1_info_final_loc + mb1_info_size > kernel_top) { kernel_top = mb1_info_final_loc + mb1_info_size; } + struct multiboot1_info *multiboot1_info = + mb1_info_alloc(&mb1_info_raw, sizeof(struct multiboot1_info)); + if (section_hdr_info != NULL) { multiboot1_info->elf_sect.num = section_hdr_info->num; multiboot1_info->elf_sect.size = section_hdr_info->section_entry_size; multiboot1_info->elf_sect.shndx = section_hdr_info->str_section_idx; - void *sections = conv_mem_alloc(section_hdr_info->section_entry_size * section_hdr_info->num); + void *sections = mb1_info_alloc(&mb1_info_raw, + section_hdr_info->section_entry_size * section_hdr_info->num); - multiboot1_info->elf_sect.addr = (uintptr_t)sections; + multiboot1_info->elf_sect.addr = (uintptr_t)sections - mb1_info_slide; memcpy(sections, kernel + section_hdr_info->section_offset, section_hdr_info->section_entry_size * section_hdr_info->num); @@ -247,18 +295,12 @@ retry_mb1_info_reloc: multiboot1_info->flags |= (1 << 5); } - 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 = conv_mem_alloc(sizeof(*mods) * n_modules); + struct multiboot1_module *mods = + mb1_info_alloc(&mb1_info_raw, sizeof(struct multiboot1_module) * n_modules); multiboot1_info->mods_count = n_modules; - multiboot1_info->mods_addr = (uint32_t)(size_t)mods; + multiboot1_info->mods_addr = (size_t)mods - mb1_info_slide; for (size_t i = 0; i < n_modules; i++) { struct multiboot1_module *m = mods + i; @@ -278,7 +320,7 @@ retry_mb1_info_reloc: if (module_cmdline == NULL) { module_cmdline = ""; } - char *lowmem_modstr = conv_mem_alloc(strlen(module_cmdline) + 1); + char *lowmem_modstr = mb1_info_alloc(&mb1_info_raw, strlen(module_cmdline) + 1); strcpy(lowmem_modstr, module_cmdline); void *module_addr = mb1_alloc(f->size); @@ -287,7 +329,7 @@ retry_mb1_info_reloc: m->begin = (uint32_t)(size_t)module_addr; m->end = m->begin + f->size; - m->cmdline = (uint32_t)(size_t)lowmem_modstr; + m->cmdline = (uint32_t)(size_t)lowmem_modstr - mb1_info_slide; m->pad = 0; fclose(f); @@ -304,17 +346,17 @@ retry_mb1_info_reloc: multiboot1_info->flags |= (1 << 3); } - char *lowmem_cmdline = conv_mem_alloc(strlen(cmdline) + 1); + char *lowmem_cmdline = mb1_info_alloc(&mb1_info_raw, strlen(cmdline) + 1); strcpy(lowmem_cmdline, cmdline); - multiboot1_info->cmdline = (uint32_t)(size_t)lowmem_cmdline; + multiboot1_info->cmdline = (uint32_t)(size_t)lowmem_cmdline - mb1_info_slide; if (cmdline) multiboot1_info->flags |= (1 << 2); - char *bootload_name = "Limine " LIMINE_VERSION; - char *lowmem_bootname = conv_mem_alloc(strlen(bootload_name) + 1); + char *bootload_name = LIMINE_BRAND; + char *lowmem_bootname = mb1_info_alloc(&mb1_info_raw, strlen(bootload_name) + 1); strcpy(lowmem_bootname, bootload_name); - multiboot1_info->bootloader_name = (uint32_t)(size_t)lowmem_bootname; + multiboot1_info->bootloader_name = (uint32_t)(size_t)lowmem_bootname - mb1_info_slide; multiboot1_info->flags |= (1 << 9); term_deinit(); @@ -388,7 +430,7 @@ nofb:; struct e820_entry_t *raw_memmap = get_raw_memmap(&mb_mmap_count); size_t mb_mmap_len = mb_mmap_count * sizeof(struct multiboot1_mmap_entry); - struct multiboot1_mmap_entry *mmap = conv_mem_alloc(mb_mmap_len); + struct multiboot1_mmap_entry *mmap = mb1_info_alloc(&mb1_info_raw, mb_mmap_len); // Multiboot is bad and passes raw memmap. We do the same to support it. for (size_t i = 0; i < mb_mmap_count; i++) { @@ -408,7 +450,7 @@ nofb:; } multiboot1_info->mmap_length = mb_mmap_len; - multiboot1_info->mmap_addr = ((uint32_t)(size_t)mmap); + multiboot1_info->mmap_addr = (uint32_t)(size_t)mmap - mb1_info_slide; multiboot1_info->flags |= (1 << 0) | (1 << 6); irq_flush_type = IRQ_PIC_ONLY_FLUSH;