Thu Apr 20 00:42:24 2023 UTC ()
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.


(manu)
diff -r1.218 -r1.219 src/sys/arch/amd64/amd64/locore.S
diff -r1.21 -r1.22 src/sys/arch/i386/stand/efiboot/boot.c
diff -r1.12 -r1.13 src/sys/arch/i386/stand/efiboot/efiboot.c
diff -r1.11 -r1.12 src/sys/arch/i386/stand/efiboot/efiboot.h
diff -r1.5 -r1.6 src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c
diff -r1.2 -r1.3 src/sys/arch/i386/stand/efiboot/bootia32/startprog32.S
diff -r1.5 -r1.6 src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c
diff -r1.3 -r1.4 src/sys/arch/i386/stand/efiboot/bootx64/startprog64.S
diff -r1.78 -r1.79 src/sys/arch/i386/stand/lib/exec.c

cvs diff -r1.218 -r1.219 src/sys/arch/amd64/amd64/locore.S (expand / switch to context diff)
--- src/sys/arch/amd64/amd64/locore.S 2023/03/03 14:32:48 1.218
+++ src/sys/arch/amd64/amd64/locore.S 2023/04/20 00:42:23 1.219
@@ -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 @@
 #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(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)

cvs diff -r1.21 -r1.22 src/sys/arch/i386/stand/efiboot/boot.c (expand / switch to context diff)
--- src/sys/arch/i386/stand/efiboot/boot.c 2022/06/08 21:43:45 1.21
+++ src/sys/arch/i386/stand/efiboot/boot.c 2023/04/20 00:42:24 1.22
@@ -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 <nonaka@netbsd.org>
@@ -83,6 +83,7 @@
 #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 @@
 #endif
 	{ "modules",	command_modules },
 	{ "multiboot",	command_multiboot },
+	{ "reloc",	command_reloc },
 	{ "rndseed",	rnd_add },
 	{ "splash",	splash_add },
 	{ "text",	command_text },
@@ -406,6 +408,7 @@
 #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"
@@ -638,6 +641,48 @@
 		       strerror(errno));
 	else
 		printf("boot returned\n");
+}
+
+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

cvs diff -r1.12 -r1.13 src/sys/arch/i386/stand/efiboot/efiboot.c (expand / switch to context diff)
--- src/sys/arch/i386/stand/efiboot/efiboot.c 2020/02/09 12:13:39 1.12
+++ src/sys/arch/i386/stand/efiboot/efiboot.c 2023/04/20 00:42:24 1.13
@@ -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 <nonaka@netbsd.org>
@@ -36,7 +36,9 @@
 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;

cvs diff -r1.11 -r1.12 src/sys/arch/i386/stand/efiboot/efiboot.h (expand / switch to context diff)
--- src/sys/arch/i386/stand/efiboot/efiboot.h 2021/09/07 11:41:31 1.11
+++ src/sys/arch/i386/stand/efiboot/efiboot.h 2023/04/20 00:42:24 1.12
@@ -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 <nonaka@netbsd.org>
@@ -52,7 +52,13 @@
 } 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);

cvs diff -r1.5 -r1.6 src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c (expand / switch to context diff)
--- src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c 2019/09/13 02:19:45 1.5
+++ src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c 2023/04/20 00:42:24 1.6
@@ -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 <nonaka@netbsd.org>
@@ -76,7 +76,7 @@
 
 	(*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);
 }
 

cvs diff -r1.2 -r1.3 src/sys/arch/i386/stand/efiboot/bootia32/startprog32.S (expand / switch to context diff)
--- src/sys/arch/i386/stand/efiboot/bootia32/startprog32.S 2017/02/24 12:24:25 1.2
+++ src/sys/arch/i386/stand/efiboot/bootia32/startprog32.S 2023/04/20 00:42:24 1.3
@@ -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 @@
 	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

cvs diff -r1.5 -r1.6 src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c (expand / switch to context diff)
--- src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c 2019/09/13 02:19:46 1.5
+++ src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c 2023/04/20 00:42:24 1.6
@@ -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 <nonaka@netbsd.org>
@@ -80,7 +80,7 @@
 		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);
 }
 

cvs diff -r1.3 -r1.4 src/sys/arch/i386/stand/efiboot/bootx64/startprog64.S (expand / switch to context diff)
--- src/sys/arch/i386/stand/efiboot/bootx64/startprog64.S 2017/02/11 10:23:39 1.3
+++ src/sys/arch/i386/stand/efiboot/bootx64/startprog64.S 2023/04/20 00:42:24 1.4
@@ -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
@@ -96,6 +96,10 @@
 	cld		/* LynxOS depends on it */
 
 	cli
+
+	/* skip copy if same source and destination */
+	cmpq	%rdi,%rsi
+	jz	.Lcopy_done
 
 	/* Copy kernel */
 	mov	%rcx, %r12		/* original kernel size */

cvs diff -r1.78 -r1.79 src/sys/arch/i386/stand/lib/exec.c (expand / switch to context diff)
--- src/sys/arch/i386/stand/lib/exec.c 2022/09/21 14:29:45 1.78
+++ src/sys/arch/i386/stand/lib/exec.c 2023/04/20 00:42:24 1.79
@@ -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 @@
 	struct btinfo_symtab btinfo_symtab;
 	u_long extmem;
 	u_long basemem;
+	u_long entry;
 	int error;
 #ifdef EFIBOOT
 	int i;
@@ -497,6 +498,8 @@
 		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 @@
 
 	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 @@
 
 	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");