Module Name: src Committed By: manu Date: Thu Apr 20 00:42:24 UTC 2023
Modified Files: src/sys/arch/amd64/amd64: locore.S src/sys/arch/i386/stand/efiboot: boot.c efiboot.c efiboot.h src/sys/arch/i386/stand/efiboot/bootia32: efibootia32.c startprog32.S src/sys/arch/i386/stand/efiboot/bootx64: efibootx64.c startprog64.S src/sys/arch/i386/stand/lib: exec.c Log Message: Add reloc keyworkd to let EFI bootstrap load amd64 kernel at any address EFI bootstrap assumes it can copy the amd64 kernel to its ELF load address (that is KERNTEXTOFF - KERNBASE = 0x200000), but it can clash with previous UEFI memory allocation, as described here: http://mail-index.netbsd.org/tech-kern/2023/04/07/msg028833.html This change adds a reloc keyword for controling where the EFI boostrap will copy the kernel image. Possible values are: default - the default and prior behavior, copy at 0x200000. none - do not copy and use the kernel image where it was loaded. address - specify an explicit address where to copy the kernel. This comes with an amd64 kernel patch that makes it self-relocatable. It first discover where it was loaded in memory, and if this is different than the expected 0x200000, hhe the kernel relocates itself and start over at the right address. To generate a diff of this commit: cvs rdiff -u -r1.218 -r1.219 src/sys/arch/amd64/amd64/locore.S cvs rdiff -u -r1.21 -r1.22 src/sys/arch/i386/stand/efiboot/boot.c cvs rdiff -u -r1.12 -r1.13 src/sys/arch/i386/stand/efiboot/efiboot.c cvs rdiff -u -r1.11 -r1.12 src/sys/arch/i386/stand/efiboot/efiboot.h cvs rdiff -u -r1.5 -r1.6 \ src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c cvs rdiff -u -r1.2 -r1.3 \ src/sys/arch/i386/stand/efiboot/bootia32/startprog32.S cvs rdiff -u -r1.5 -r1.6 src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c cvs rdiff -u -r1.3 -r1.4 \ src/sys/arch/i386/stand/efiboot/bootx64/startprog64.S cvs rdiff -u -r1.78 -r1.79 src/sys/arch/i386/stand/lib/exec.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/amd64/amd64/locore.S diff -u src/sys/arch/amd64/amd64/locore.S:1.218 src/sys/arch/amd64/amd64/locore.S:1.219 --- src/sys/arch/amd64/amd64/locore.S:1.218 Fri Mar 3 14:32:48 2023 +++ src/sys/arch/amd64/amd64/locore.S Thu Apr 20 00:42:23 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: locore.S,v 1.218 2023/03/03 14:32:48 riastradh Exp $ */ +/* $NetBSD: locore.S,v 1.219 2023/04/20 00:42:23 manu Exp $ */ /* * Copyright-o-rama! @@ -456,6 +456,14 @@ ENTRY(start) #ifndef XENPV .code32 + call next +next: pop %edi + sub $(next - kernel_text), %edi + + /* If not KERNBASE, reloc ourselves to KERNBASE */ + cmpl $(KERNTEXTOFF_LO - KERNBASE_LO), %edi + jne selfreloc_start + /* Warm boot */ movw $0x1234,0x472 @@ -1757,3 +1765,138 @@ LABEL(mds_leave_end) LABEL(nomds_leave) NOMDS_LEAVE LABEL(nomds_leave_end) + +/* + * selfreloc(loadddr edi) + * This is adapted from sys/arch/i386/i386/locore.S + */ + .code32 +ENTRY(selfreloc_start) + movl %edi, %ebx /* loadaddr saved in ebx */ + movl %edi, %esi /* src */ + movl $_RELOC(kernel_text), %edi /* dest */ + movl 16(%esp),%ecx /* esym */ + subl $_RELOC(kernel_text), %ecx /* size */ + +#if defined(NO_OVERLAP) + movl %ecx, %eax +#else + movl %edi, %eax + subl %esi, %eax + cmpl %ecx, %eax /* overlapping? */ + movl %ecx, %eax + jb .Lbackwards +#endif + /* nope, copy forwards. */ + shrl $2, %ecx /* copy by words */ + rep + movsl + and $3, %eax /* any bytes left? */ + jnz .Ltrailing + jmp .Lcopy_done + +.Ltrailing: + cmp $2, %eax + jb 11f + movw (%esi), %ax + movw %ax, (%edi) + je .Lcopy_done + movb 2(%esi), %al + movb %al, 2(%edi) + jmp .Lcopy_done +11: movb (%esi), %al + movb %al, (%edi) + jmp .Lcopy_done + +#if !defined(NO_OVERLAP) +.Lbackwards: + addl %ecx, %edi /* copy backwards. */ + addl %ecx, %esi + and $3, %eax /* any fractional bytes? */ + jnz .Lback_align +.Lback_aligned: + shrl $2, %ecx + subl $4, %esi + subl $4, %edi + std + rep + movsl + cld + jmp .Lcopy_done + +.Lback_align: + sub %eax, %esi + sub %eax, %edi + cmp $2, %eax + jb 11f + je 12f + movb 2(%esi), %al + movb %al, 2(%edi) +12: movw (%esi), %ax + movw %ax, (%edi) + jmp .Lback_aligned +11: movb (%esi), %al + movb %al, (%edi) + jmp .Lback_aligned +#endif + /* End of copy kernel */ +.Lcopy_done: + cld /* LynxOS depends on it */ + + /* load current selfreloc_start addesss in $edi */ + movl %ebx, %edi /* loadaddr was saved in ebx */ + addl $(selfreloc_start - kernel_text), %edi + + /* Prepare jump address */ + lea (selfreloc_start32a - selfreloc_start)(%edi), %eax + movl %eax, (selfreloc_start32r - selfreloc_start)(%edi) + + /* Setup GDT */ + lea (gdt - selfreloc_start)(%edi), %eax + mov %eax, (gdtrr - selfreloc_start)(%edi) + lgdt (gdtr - selfreloc_start)(%edi) + + /* Jump to set %cs */ + ljmp *(selfreloc_start32r - selfreloc_start)(%edi) + + .align 4 +selfreloc_start32a: + movl $0x10, %eax /* #define DATA_SEGMENT 0x10 */ + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + + /* Disable Paging in CR0 */ + movl %cr0, %eax + andl $(~CR0_PG), %eax + movl %eax, %cr0 + + /* Disable PAE in CR4 */ + movl %cr4, %eax + andl $(~CR4_PAE), %eax + movl %eax, %cr4 + + jmp selfreloc_start32b + + .align 4 +selfreloc_start32b: + xor %eax, %eax + movl $_RELOC(start), %esi + jmp *%esi + + .align 16 +selfreloc_start32r: + .long 0 + .long 0x08 /* #define CODE_SEGMENT 0x08 */ + .align 16 +gdt: + .long 0, 0 + .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9f, 0xcf, 0x00 + .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x93, 0xcf, 0x00 +gdtr: + .word gdtr - gdt +gdtrr: + .quad +END(selfreloc_start) Index: src/sys/arch/i386/stand/efiboot/boot.c diff -u src/sys/arch/i386/stand/efiboot/boot.c:1.21 src/sys/arch/i386/stand/efiboot/boot.c:1.22 --- src/sys/arch/i386/stand/efiboot/boot.c:1.21 Wed Jun 8 21:43:45 2022 +++ src/sys/arch/i386/stand/efiboot/boot.c Thu Apr 20 00:42:24 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: boot.c,v 1.21 2022/06/08 21:43:45 wiz Exp $ */ +/* $NetBSD: boot.c,v 1.22 2023/04/20 00:42:24 manu Exp $ */ /*- * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org> @@ -83,6 +83,7 @@ void command_menu(char *); #endif void command_modules(char *); void command_multiboot(char *); +void command_reloc(char *); void command_text(char *); void command_version(char *); @@ -109,6 +110,7 @@ const struct bootblk_command commands[] #endif { "modules", command_modules }, { "multiboot", command_multiboot }, + { "reloc", command_reloc }, { "rndseed", rnd_add }, { "splash", splash_add }, { "text", command_text }, @@ -406,6 +408,7 @@ command_help(char *arg) #endif "modules {on|off|enabled|disabled}\n" "multiboot [dev:][filename] [<args>]\n" + "reloc {address|none|default}\n" "rndseed {path_to_rndseed_file}\n" "splash {path_to_image_file}\n" "text [{modenum|list}]\n" @@ -641,6 +644,48 @@ command_multiboot(char *arg) } void +command_reloc(char *arg) +{ + char *ep; + + if (*arg == '\0') { + switch (efi_reloc_type) { + case RELOC_NONE: + printf("reloc: none\n"); + break; + case RELOC_ADDR: + printf("reloc: %p\n", (void *)efi_kernel_reloc); + break; + case RELOC_DEFAULT: + default: + printf("reloc: default\n"); + break; + } + goto out; + } + + if (strcmp(arg, "default") == 0) { + efi_reloc_type = RELOC_DEFAULT; + goto out; + } + + if (strcmp(arg, "none") == 0) { + efi_reloc_type = RELOC_NONE; + goto out; + } + + errno = 0; + efi_kernel_reloc = strtoul(arg, &ep, 0); + if (ep == arg || *ep != '\0' || errno) + printf("could not parse address \"%s\"\n", arg); + else + efi_reloc_type = RELOC_ADDR; +out: + return; + +} + +void command_version(char *arg) { CHAR16 *path; Index: src/sys/arch/i386/stand/efiboot/efiboot.c diff -u src/sys/arch/i386/stand/efiboot/efiboot.c:1.12 src/sys/arch/i386/stand/efiboot/efiboot.c:1.13 --- src/sys/arch/i386/stand/efiboot/efiboot.c:1.12 Sun Feb 9 12:13:39 2020 +++ src/sys/arch/i386/stand/efiboot/efiboot.c Thu Apr 20 00:42:24 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: efiboot.c,v 1.12 2020/02/09 12:13:39 jmcneill Exp $ */ +/* $NetBSD: efiboot.c,v 1.13 2023/04/20 00:42:24 manu Exp $ */ /*- * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org> @@ -36,7 +36,9 @@ EFI_DEVICE_PATH *efi_bootdp; enum efi_boot_device_type efi_bootdp_type = BOOT_DEVICE_TYPE_HD; EFI_LOADED_IMAGE *efi_li; uintptr_t efi_main_sp; -physaddr_t efi_loadaddr, efi_kernel_start; +physaddr_t efi_loadaddr, efi_kernel_start, efi_load_start; +physaddr_t efi_kernel_reloc = 0; +enum efi_reloc_type efi_reloc_type = RELOC_DEFAULT; u_long efi_kernel_size; bool efi_cleanuped; struct btinfo_efimemmap *btinfo_efimemmap = NULL; Index: src/sys/arch/i386/stand/efiboot/efiboot.h diff -u src/sys/arch/i386/stand/efiboot/efiboot.h:1.11 src/sys/arch/i386/stand/efiboot/efiboot.h:1.12 --- src/sys/arch/i386/stand/efiboot/efiboot.h:1.11 Tue Sep 7 11:41:31 2021 +++ src/sys/arch/i386/stand/efiboot/efiboot.h Thu Apr 20 00:42:24 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: efiboot.h,v 1.11 2021/09/07 11:41:31 nia Exp $ */ +/* $NetBSD: efiboot.h,v 1.12 2023/04/20 00:42:24 manu Exp $ */ /*- * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org> @@ -52,7 +52,13 @@ extern enum efi_boot_device_type { } efi_bootdp_type; extern EFI_LOADED_IMAGE *efi_li; extern uintptr_t efi_main_sp; -extern physaddr_t efi_loadaddr, efi_kernel_start; +extern physaddr_t efi_loadaddr, efi_kernel_start, efi_load_start; +extern physaddr_t efi_kernel_reloc; +extern enum efi_reloc_type { + RELOC_DEFAULT, + RELOC_NONE, + RELOC_ADDR, +} efi_reloc_type; extern u_long efi_kernel_size; extern bool efi_cleanuped; void efi_cleanup(void); Index: src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c diff -u src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c:1.5 src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c:1.6 --- src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c:1.5 Fri Sep 13 02:19:45 2019 +++ src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c Thu Apr 20 00:42:24 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: efibootia32.c,v 1.5 2019/09/13 02:19:45 manu Exp $ */ +/* $NetBSD: efibootia32.c,v 1.6 2023/04/20 00:42:24 manu Exp $ */ /*- * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org> @@ -76,7 +76,7 @@ startprog(physaddr_t entry, uint32_t arg (*startprog32)(entry, argc, argv, (physaddr_t)startprog32 + startprog32_size, - efi_kernel_start, efi_kernel_start + efi_loadaddr, + efi_kernel_start, efi_load_start, efi_kernel_size, startprog32); } Index: src/sys/arch/i386/stand/efiboot/bootia32/startprog32.S diff -u src/sys/arch/i386/stand/efiboot/bootia32/startprog32.S:1.2 src/sys/arch/i386/stand/efiboot/bootia32/startprog32.S:1.3 --- src/sys/arch/i386/stand/efiboot/bootia32/startprog32.S:1.2 Fri Feb 24 12:24:25 2017 +++ src/sys/arch/i386/stand/efiboot/bootia32/startprog32.S Thu Apr 20 00:42:24 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: startprog32.S,v 1.2 2017/02/24 12:24:25 nonaka Exp $ */ +/* $NetBSD: startprog32.S,v 1.3 2023/04/20 00:42:24 manu Exp $ */ /* NetBSD: startprog.S,v 1.4 2016/12/04 08:21:08 maxv Exp */ /* @@ -117,6 +117,11 @@ start: movl 24(%ebp), %edi /* dest */ movl 28(%ebp), %esi /* src */ movl 32(%ebp), %ecx /* size */ + + /* skip copy if same source and destination */ + cmpl %edi,%esi + jz .Lcopy_done + #if defined(NO_OVERLAP) movl %ecx, %eax #else Index: src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c diff -u src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c:1.5 src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c:1.6 --- src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c:1.5 Fri Sep 13 02:19:46 2019 +++ src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c Thu Apr 20 00:42:24 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: efibootx64.c,v 1.5 2019/09/13 02:19:46 manu Exp $ */ +/* $NetBSD: efibootx64.c,v 1.6 2023/04/20 00:42:24 manu Exp $ */ /*- * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org> @@ -80,7 +80,7 @@ startprog(physaddr_t entry, uint32_t arg memcpy(newsp, argv, sizeof(*argv) * argc); } - (*startprog64)(efi_kernel_start, efi_kernel_start + efi_loadaddr, + (*startprog64)(efi_kernel_start, efi_load_start, (physaddr_t)newsp, efi_kernel_size, startprog64, entry); } Index: src/sys/arch/i386/stand/efiboot/bootx64/startprog64.S diff -u src/sys/arch/i386/stand/efiboot/bootx64/startprog64.S:1.3 src/sys/arch/i386/stand/efiboot/bootx64/startprog64.S:1.4 --- src/sys/arch/i386/stand/efiboot/bootx64/startprog64.S:1.3 Sat Feb 11 10:23:39 2017 +++ src/sys/arch/i386/stand/efiboot/bootx64/startprog64.S Thu Apr 20 00:42:24 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: startprog64.S,v 1.3 2017/02/11 10:23:39 nonaka Exp $ */ +/* $NetBSD: startprog64.S,v 1.4 2023/04/20 00:42:24 manu Exp $ */ /* NetBSD: startprog.S,v 1.3 2003/02/01 14:48:18 dsl Exp */ /* starts program in protected mode / flat space @@ -97,6 +97,10 @@ start: cli + /* skip copy if same source and destination */ + cmpq %rdi,%rsi + jz .Lcopy_done + /* Copy kernel */ mov %rcx, %r12 /* original kernel size */ movq %rdi, %r11 /* for misaligned check */ Index: src/sys/arch/i386/stand/lib/exec.c diff -u src/sys/arch/i386/stand/lib/exec.c:1.78 src/sys/arch/i386/stand/lib/exec.c:1.79 --- src/sys/arch/i386/stand/lib/exec.c:1.78 Wed Sep 21 14:29:45 2022 +++ src/sys/arch/i386/stand/lib/exec.c Thu Apr 20 00:42:24 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: exec.c,v 1.78 2022/09/21 14:29:45 riastradh Exp $ */ +/* $NetBSD: exec.c,v 1.79 2023/04/20 00:42:24 manu Exp $ */ /* * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. @@ -465,6 +465,7 @@ exec_netbsd(const char *file, physaddr_t struct btinfo_symtab btinfo_symtab; u_long extmem; u_long basemem; + u_long entry; int error; #ifdef EFIBOOT int i; @@ -497,6 +498,8 @@ exec_netbsd(const char *file, physaddr_t goto out; } #ifdef EFIBOOT + efi_load_start = marks[MARK_START]; + /* adjust to the real load address */ marks[MARK_START] -= efi_loadaddr; marks[MARK_ENTRY] -= efi_loadaddr; @@ -552,6 +555,8 @@ exec_netbsd(const char *file, physaddr_t if (callback != NULL) (*callback)(); + + entry = marks[MARK_ENTRY]; #ifdef EFIBOOT /* Copy bootinfo to safe arena. */ for (i = 0; i < bootinfo->nentries; i++) { @@ -563,8 +568,22 @@ exec_netbsd(const char *file, physaddr_t efi_kernel_start = marks[MARK_START]; efi_kernel_size = image_end - (efi_loadaddr + efi_kernel_start); + + switch (efi_reloc_type) { + case RELOC_NONE: + entry += (efi_load_start - efi_kernel_start); + efi_kernel_start = efi_load_start; + break; + case RELOC_ADDR: + entry += (efi_kernel_reloc - efi_kernel_start); + efi_kernel_start = efi_kernel_reloc; + break; + case RELOC_DEFAULT: + default: + break; + } #endif - startprog(marks[MARK_ENTRY], BOOT_NARGS, boot_argv, + startprog(entry, BOOT_NARGS, boot_argv, x86_trunc_page(basemem * 1024)); panic("exec returned");