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 + +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 #endif +#include -noreturn void multiboot1_spinup_32(uint32_t entry_point, uint32_t multiboot1_info) { +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 + reloc_stub->magic = 0x2badb002; + reloc_stub->entry_point = entry_point; + reloc_stub->mb_info_target = mb_info_target; + asm volatile ( - "cld\n\t" - - "push %2\n\t" - - "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" + "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 #include +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 #endif - -struct reloc_stub { - char jmp[4]; - uint32_t magic; - uint32_t entry_point; - uint32_t mb_info_target; -}; +#include 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;