multiboot(2): Initial support for arbitrary kernel load addresses
This commit is contained in:
parent
ee688073be
commit
b92d48e44f
|
@ -568,6 +568,11 @@ int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_sl
|
|||
}
|
||||
}
|
||||
|
||||
if (use_paddr) {
|
||||
simulation = true;
|
||||
goto final;
|
||||
}
|
||||
|
||||
if (!elf64_is_relocatable(elf, &hdr)) {
|
||||
simulation = false;
|
||||
goto final;
|
||||
|
@ -678,7 +683,7 @@ final:
|
|||
memset(ptr, 0, to_zero);
|
||||
}
|
||||
|
||||
if (elf64_apply_relocations(elf, &hdr, (void *)(uintptr_t)load_addr, phdr.p_vaddr, phdr.p_memsz, slide)) {
|
||||
if (!use_paddr && elf64_apply_relocations(elf, &hdr, (void *)(uintptr_t)load_addr, phdr.p_vaddr, phdr.p_memsz, slide)) {
|
||||
panic(true, "elf: Failed to apply relocations");
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ void init_memmap(void);
|
|||
struct e820_entry_t *get_memmap(size_t *entries);
|
||||
struct e820_entry_t *get_raw_memmap(size_t *entry_count);
|
||||
void print_memmap(struct e820_entry_t *mm, size_t size);
|
||||
bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, bool free_only, bool panic, bool simulation, bool new_entry);
|
||||
bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, uint32_t overlay_type, bool panic, bool simulation, bool new_entry);
|
||||
void pmm_randomise_memory(void);
|
||||
|
||||
void *ext_mem_alloc(size_t count);
|
||||
|
|
|
@ -808,7 +808,7 @@ static bool pmm_new_entry(uint64_t base, uint64_t length, uint32_t type) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, bool free_only, bool do_panic, bool simulation, bool new_entry) {
|
||||
bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, uint32_t overlay_type, bool do_panic, bool simulation, bool new_entry) {
|
||||
if (length == 0)
|
||||
return true;
|
||||
|
||||
|
@ -819,7 +819,7 @@ bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, bool free
|
|||
uint64_t top = base + length;
|
||||
|
||||
for (size_t i = 0; i < memmap_entries; i++) {
|
||||
if (free_only && memmap[i].type != MEMMAP_USABLE)
|
||||
if (overlay_type != 0 && memmap[i].type != overlay_type)
|
||||
continue;
|
||||
|
||||
uint64_t entry_base = memmap[i].base;
|
||||
|
|
|
@ -7,7 +7,19 @@
|
|||
# include <sys/idt.h>
|
||||
#endif
|
||||
|
||||
noreturn void multiboot2_spinup_32(uint32_t entry_point, uint32_t multiboot2_info) {
|
||||
struct reloc_stub {
|
||||
char jmp[4];
|
||||
uint32_t magic;
|
||||
uint32_t entry_point;
|
||||
uint32_t mb_info_target;
|
||||
};
|
||||
|
||||
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) {
|
||||
#if bios == 1
|
||||
struct idtr idtr;
|
||||
|
||||
|
@ -22,22 +34,16 @@ noreturn void multiboot2_spinup_32(uint32_t entry_point, uint32_t multiboot2_inf
|
|||
);
|
||||
#endif
|
||||
|
||||
reloc_stub->magic = 0x36d76289;
|
||||
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" (0x36d76289),
|
||||
"b" (multiboot2_info),
|
||||
"r" (entry_point)
|
||||
: "b"(reloc_stub), "S"(multiboot2_info),
|
||||
"c"(mb_info_size), "a"(elf_ranges), "d"(elf_ranges_count),
|
||||
"D"(slide)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <lib/blib.h>
|
||||
#include <drivers/vga_textmode.h>
|
||||
|
||||
extern symbol multiboot_reloc_stub, multiboot_reloc_stub_end;
|
||||
|
||||
#define LIMINE_BRAND "Limine " LIMINE_VERSION
|
||||
|
||||
/// Returns the size required to store the multiboot2 info.
|
||||
|
@ -57,6 +59,12 @@ static size_t get_multiboot2_info_size(
|
|||
|
||||
static uint32_t kernel_top;
|
||||
|
||||
static bool mb2_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 *mb2_alloc(size_t size) {
|
||||
void *ret = (void *)(uintptr_t)ALIGN_UP(kernel_top, 4096);
|
||||
|
||||
|
@ -189,6 +197,9 @@ bool multiboot2_load(char *config, char* cmdline) {
|
|||
}
|
||||
}
|
||||
|
||||
struct elf_range *elf_ranges;
|
||||
uint64_t elf_ranges_count, slide;
|
||||
|
||||
if (addresstag != NULL) {
|
||||
if (addresstag->load_addr > addresstag->header_addr)
|
||||
panic(true, "multiboot2: Illegal load address");
|
||||
|
@ -226,12 +237,12 @@ bool multiboot2_load(char *config, char* cmdline) {
|
|||
|
||||
switch (bits) {
|
||||
case 32:
|
||||
if (elf32_load(kernel, (uint32_t *)&e, (uint32_t *)&t, MEMMAP_KERNEL_AND_MODULES))
|
||||
if (elf32_load(kernel, (uint32_t *)&e, (uint32_t *)&t, MEMMAP_BOOTLOADER_RECLAIMABLE))
|
||||
panic(true, "multiboot2: ELF32 load failure");
|
||||
|
||||
break;
|
||||
case 64: {
|
||||
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, "multiboot2: ELF64 load failure");
|
||||
|
||||
break;
|
||||
|
@ -240,10 +251,17 @@ bool multiboot2_load(char *config, char* cmdline) {
|
|||
panic(true, "multiboot2: Invalid ELF file bitness");
|
||||
}
|
||||
|
||||
e -= slide;
|
||||
if (entry_point == 0xffffffff) {
|
||||
entry_point = e;
|
||||
}
|
||||
kernel_top = t;
|
||||
|
||||
t -= slide;
|
||||
if (t < 0x100000) {
|
||||
kernel_top = 0x100000;
|
||||
} else {
|
||||
kernel_top = t;
|
||||
}
|
||||
}
|
||||
|
||||
struct elf_section_hdr_info *section_hdr_info = NULL;
|
||||
|
@ -292,7 +310,42 @@ bool multiboot2_load(char *config, char* cmdline) {
|
|||
);
|
||||
|
||||
size_t info_idx = 0;
|
||||
uint8_t *mb2_info = conv_mem_alloc(mb2_info_size);
|
||||
|
||||
// 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.
|
||||
uint8_t *mb2_info = ext_mem_alloc(mb2_info_size);
|
||||
uint64_t mb2_info_final_loc = 0x10000;
|
||||
retry_mb2_info_reloc:
|
||||
for (size_t i = 0; i < elf_ranges_count; i++) {
|
||||
uint64_t mb2_info_top = mb2_info_final_loc + mb2_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 (mb2_overlap_check(base, top, mb2_info_final_loc, mb2_info_top)) {
|
||||
mb2_info_final_loc = top;
|
||||
goto retry_mb2_info_reloc;
|
||||
}
|
||||
|
||||
// Make sure it is memory that actually exists.
|
||||
if (!memmap_alloc_range(mb2_info_final_loc, mb2_info_size, MEMMAP_BOOTLOADER_RECLAIMABLE,
|
||||
MEMMAP_USABLE, false, true, false)) {
|
||||
if (!memmap_alloc_range(mb2_info_final_loc, mb2_info_size, MEMMAP_BOOTLOADER_RECLAIMABLE,
|
||||
MEMMAP_BOOTLOADER_RECLAIMABLE, false, true, false)) {
|
||||
mb2_info_final_loc += 0x1000;
|
||||
goto retry_mb2_info_reloc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mb2_info_final_loc + mb2_info_size > kernel_top) {
|
||||
kernel_top = mb2_info_final_loc + mb2_info_size;
|
||||
}
|
||||
|
||||
struct multiboot2_start_tag *mbi_start = (struct multiboot2_start_tag *)mb2_info;
|
||||
info_idx += sizeof(struct multiboot2_start_tag);
|
||||
|
@ -592,6 +645,11 @@ bool multiboot2_load(char *config, char* cmdline) {
|
|||
}
|
||||
#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 = mb2_alloc(reloc_stub_size);
|
||||
memcpy(reloc_stub, multiboot_reloc_stub, reloc_stub_size);
|
||||
|
||||
#if uefi == 1
|
||||
efi_exit_boot_services();
|
||||
#endif
|
||||
|
@ -685,6 +743,11 @@ bool multiboot2_load(char *config, char* cmdline) {
|
|||
|
||||
irq_flush_type = IRQ_PIC_ONLY_FLUSH;
|
||||
|
||||
common_spinup(multiboot2_spinup_32, 2,
|
||||
entry_point, (uint32_t)(uintptr_t)mbi_start);
|
||||
common_spinup(multiboot2_spinup_32, 8,
|
||||
entry_point,
|
||||
(uint32_t)(uintptr_t)mb2_info, (uint32_t)mb2_info_final_loc,
|
||||
(uint32_t)mb2_info_size,
|
||||
(uint32_t)(uintptr_t)elf_ranges, (uint32_t)elf_ranges_count,
|
||||
(uint32_t)slide,
|
||||
(uint32_t)(uintptr_t)reloc_stub);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
section .data
|
||||
|
||||
global multiboot_reloc_stub
|
||||
multiboot_reloc_stub:
|
||||
jmp .code
|
||||
|
||||
times 4-($-multiboot_reloc_stub) db 0
|
||||
|
||||
.magic_value: dd 0
|
||||
.entry_point: dd 0
|
||||
.mb_info_target: dd 0
|
||||
|
||||
; EBX = self
|
||||
; ESI = multiboot info (original)
|
||||
; ECX = multiboot info (size)
|
||||
|
||||
; EAX = elf ranges
|
||||
; EDX = elf ranges count
|
||||
; EDI = slide
|
||||
|
||||
.code:
|
||||
mov ebp, edi
|
||||
|
||||
mov edi, [ebx + (.mb_info_target - multiboot_reloc_stub)]
|
||||
|
||||
; Copy multiboot info; frees ESI, EDI, and ECX
|
||||
rep movsb
|
||||
|
||||
.elf_ranges_loop:
|
||||
mov esi, [eax] ; ESI = elf_range.base
|
||||
mov edi, esi ; EDI = elf_range.base - slide
|
||||
sub edi, ebp
|
||||
mov ecx, [eax+8] ; ECX = elf_range.length
|
||||
rep movsb ; Copy range to target location
|
||||
|
||||
add eax, 24 ; Move to the next elf_range
|
||||
|
||||
dec edx ; Loop until we're done
|
||||
jnz .elf_ranges_loop
|
||||
|
||||
; We're done relocating!
|
||||
|
||||
push dword [ebx + (.entry_point - multiboot_reloc_stub)]
|
||||
|
||||
mov eax, [ebx + (.magic_value - multiboot_reloc_stub)]
|
||||
mov ebx, [ebx + (.mb_info_target - multiboot_reloc_stub)]
|
||||
xor ecx, ecx
|
||||
xor edx, edx
|
||||
xor esi, esi
|
||||
xor edi, edi
|
||||
xor ebp, ebp
|
||||
|
||||
ret
|
||||
|
||||
global multiboot_reloc_stub_end
|
||||
multiboot_reloc_stub_end:
|
Loading…
Reference in New Issue