:: commit 7af5cf071fb2cae93e11553c029e2c780f78725b

mintsuki <mintsuki@protonmail.com> — 2022-06-29 05:26

parents: 1ea85ad852

multiboot1: Add ahead-of-time info size calculation

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;
tab: 248 wrap: offon