:: commit 916994f07223472903d2597453c7be96f1419775

mintsuki <mintsuki@protonmail.com> — 2022-06-27 09:17

parents: 6ce8a9b403

multiboot: Backport b92d48e to multiboot1

diff --git a/common/protos/multiboot.h b/common/protos/multiboot.h
new file mode 100644
index 00000000..d1aaeff6
--- /dev/null
+++ b/common/protos/multiboot.h
@@ -0,0 +1,13 @@
+#ifndef __PROTOS__MULTIBOOT_H__
+#define __PROTOS__MULTIBOOT_H__
+
+#include <stdint.h>
+
+struct mb_reloc_stub {
+    char jmp[4];
+    uint32_t magic;
+    uint32_t entry_point;
+    uint32_t mb_info_target;
+};
+
+#endif
diff --git a/common/protos/multiboot1.32.c b/common/protos/multiboot1.32.c
index e4b77d19..b67eb3b0 100644
--- a/common/protos/multiboot1.32.c
+++ b/common/protos/multiboot1.32.c
@@ -6,8 +6,14 @@
 #if bios == 1
 #  include <sys/idt.h>
 #endif
-
-noreturn void multiboot1_spinup_32(uint32_t entry_point, uint32_t multiboot1_info) {
+#include <protos/multiboot.h>
+
+noreturn void multiboot1_spinup_32(uint32_t entry_point,
+                                   uint32_t multiboot1_info, uint32_t mb_info_target,
+                                   uint32_t mb_info_size,
+                                   uint32_t elf_ranges, uint32_t elf_ranges_count,
+                                   uint32_t slide,
+                                   struct mb_reloc_stub *reloc_stub) {
 #if bios == 1
     struct idtr idtr;
 
@@ -22,22 +28,16 @@ noreturn void multiboot1_spinup_32(uint32_t entry_point, uint32_t multiboot1_inf
     );
 #endif
 
-    asm volatile (
-        "cld\n\t"
-
-        "push %2\n\t"
+    reloc_stub->magic = 0x2badb002;
+    reloc_stub->entry_point = entry_point;
+    reloc_stub->mb_info_target = mb_info_target;
 
-        "xor %%ecx, %%ecx\n\t"
-        "xor %%edx, %%edx\n\t"
-        "xor %%esi, %%esi\n\t"
-        "xor %%edi, %%edi\n\t"
-        "xor %%ebp, %%ebp\n\t"
-
-        "ret\n\t"
+    asm volatile (
+        "jmp *%%ebx"
         :
-        : "a" (0x2badb002),
-          "b" (multiboot1_info),
-          "r" (entry_point)
+        : "b"(reloc_stub), "S"(multiboot1_info),
+          "c"(mb_info_size), "a"(elf_ranges), "d"(elf_ranges_count),
+          "D"(slide)
         : "memory"
     );
 
diff --git a/common/protos/multiboot1.c b/common/protos/multiboot1.c
index e562a203..9aacae80 100644
--- a/common/protos/multiboot1.c
+++ b/common/protos/multiboot1.c
@@ -19,10 +19,18 @@
 #include <mm/pmm.h>
 #include <drivers/vga_textmode.h>
 
+extern symbol multiboot_reloc_stub, multiboot_reloc_stub_end;
+
 noreturn void multiboot1_spinup_32(uint32_t entry_point, uint32_t multiboot1_info);
 
 static uint32_t kernel_top;
 
+static bool mb1_overlap_check(uint64_t base1, uint64_t top1,
+                              uint64_t base2, uint64_t top2) {
+    return ((base1 >= base2 && base1 <  top2)
+         || (top1  >  base2 && top1  <= top2));
+}
+
 static void *mb1_alloc(size_t size) {
     void *ret = (void *)(uintptr_t)ALIGN_UP(kernel_top, 4096);
 
@@ -70,8 +78,6 @@ bool multiboot1_load(char *config, char *cmdline) {
 
     print("multiboot1: Loading kernel `%s`...\n", kernel_path);
 
-    struct multiboot1_info *multiboot1_info = conv_mem_alloc(sizeof(struct multiboot1_info));
-
     if (header.magic + header.flags + header.checksum)
         panic(true, "multiboot1: Header checksum is invalid");
 
@@ -79,6 +85,9 @@ bool multiboot1_load(char *config, char *cmdline) {
 
     struct elf_section_hdr_info *section_hdr_info = NULL;
 
+    struct elf_range *elf_ranges;
+    uint64_t elf_ranges_count, slide;
+
     if (header.flags & (1 << 16)) {
         if (header.load_addr > header.header_addr)
             panic(true, "multiboot1: Illegal load address");
@@ -125,15 +134,21 @@ bool multiboot1_load(char *config, char *cmdline) {
 
         switch (bits) {
             case 32:
-                if (elf32_load(kernel, &entry_point, &kernel_top, MEMMAP_KERNEL_AND_MODULES))
+                if (elf32_load(kernel, &entry_point, &kernel_top, MEMMAP_BOOTLOADER_RECLAIMABLE))
                     panic(true, "multiboot1: ELF32 load failure");
                 break;
             case 64: {
                 uint64_t e, t;
-                if (elf64_load(kernel, &e, &t, NULL, MEMMAP_KERNEL_AND_MODULES, false, true, NULL, NULL, false, NULL, NULL, NULL, NULL))
+                if (elf64_load(kernel, &e, &t, &slide, MEMMAP_BOOTLOADER_RECLAIMABLE, false, true, &elf_ranges, &elf_ranges_count, false, NULL, NULL, NULL, NULL))
                     panic(true, "multiboot1: ELF64 load failure");
-                entry_point = e;
-                kernel_top = t;
+                entry_point = e - slide;
+
+                t -= slide;
+                if (t < 0x100000) {
+                    kernel_top = 0x100000;
+                } else {
+                    kernel_top = t;
+                }
 
                 break;
             }
@@ -142,6 +157,43 @@ bool multiboot1_load(char *config, char *cmdline) {
         }
     }
 
+    // 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);
+    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 top = base + length;
+
+        // Do they overlap?
+        if (mb1_overlap_check(base, top, mb1_info_final_loc, mb1_info_top)) {
+            mb1_info_final_loc = top;
+            goto retry_mb1_info_reloc;
+        }
+
+        // Make sure it is memory that actually exists.
+        if (!memmap_alloc_range(mb1_info_final_loc, mb1_info_size, MEMMAP_BOOTLOADER_RECLAIMABLE,
+                                MEMMAP_USABLE, false, true, false)) {
+            if (!memmap_alloc_range(mb1_info_final_loc, mb1_info_size, MEMMAP_BOOTLOADER_RECLAIMABLE,
+                                    MEMMAP_BOOTLOADER_RECLAIMABLE, false, true, false)) {
+                mb1_info_final_loc += 0x1000;
+                goto retry_mb1_info_reloc;
+            }
+        }
+    }
+
+    if (mb1_info_final_loc + mb1_info_size > kernel_top) {
+        kernel_top = mb1_info_final_loc + mb1_info_size;
+    }
+
     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;
@@ -169,7 +221,6 @@ bool multiboot1_load(char *config, char *cmdline) {
         multiboot1_info->flags |= (1 << 5);
     }
 
-    
     uint32_t n_modules;
 
     for (n_modules = 0; ; n_modules++) {
@@ -298,6 +349,11 @@ nofb:;
 #endif
     }
 
+    // Load relocation stub where it won't get overwritten
+    size_t reloc_stub_size = (size_t)multiboot_reloc_stub_end - (size_t)multiboot_reloc_stub;
+    void *reloc_stub = mb1_alloc(reloc_stub_size);
+    memcpy(reloc_stub, multiboot_reloc_stub, reloc_stub_size);
+
 #if uefi == 1
     efi_exit_boot_services();
 #endif
@@ -331,6 +387,11 @@ nofb:;
 
     irq_flush_type = IRQ_PIC_ONLY_FLUSH;
 
-    common_spinup(multiboot1_spinup_32, 2,
-                  entry_point, (uint32_t)(uintptr_t)multiboot1_info);
+    common_spinup(multiboot1_spinup_32, 8,
+                  entry_point,
+                  (uint32_t)(uintptr_t)multiboot1_info, (uint32_t)mb1_info_final_loc,
+                  (uint32_t)mb1_info_size,
+                  (uint32_t)(uintptr_t)elf_ranges, (uint32_t)elf_ranges_count,
+                  (uint32_t)slide,
+                  (uint32_t)(uintptr_t)reloc_stub);
 }
diff --git a/common/protos/multiboot2.32.c b/common/protos/multiboot2.32.c
index e4c79ff6..c73ee8b4 100644
--- a/common/protos/multiboot2.32.c
+++ b/common/protos/multiboot2.32.c
@@ -6,20 +6,14 @@
 #if bios == 1
 #  include <sys/idt.h>
 #endif
-
-struct reloc_stub {
-    char jmp[4];
-    uint32_t magic;
-    uint32_t entry_point;
-    uint32_t mb_info_target;
-};
+#include <protos/multiboot.h>
 
 noreturn void multiboot2_spinup_32(uint32_t entry_point,
                                    uint32_t multiboot2_info, uint32_t mb_info_target,
                                    uint32_t mb_info_size,
                                    uint32_t elf_ranges, uint32_t elf_ranges_count,
                                    uint32_t slide,
-                                   struct reloc_stub *reloc_stub) {
+                                   struct mb_reloc_stub *reloc_stub) {
 #if bios == 1
     struct idtr idtr;
 
tab: 248 wrap: offon