diff options
author | Mike Larkin <mlarkin@cvs.openbsd.org> | 2019-05-15 06:52:34 +0000 |
---|---|---|
committer | Mike Larkin <mlarkin@cvs.openbsd.org> | 2019-05-15 06:52:34 +0000 |
commit | bf5ab008b624e055d4f345e1fc499cd78cce8eb6 (patch) | |
tree | a7430d172da7865ad13f46662905b673a0fa8dd8 /sys/arch/amd64 | |
parent | 2a6f03d90f6061ad7ffefc8e5be51a91a384b83c (diff) |
Add support to the BIOS bootloader for random kernel base VA
This diff adds support to be able to load a randomly linked kernel VA
(subject to some range restrictions). This change has been in snaps for
a few days without any fallout.
ok deraadt@
Diffstat (limited to 'sys/arch/amd64')
-rw-r--r-- | sys/arch/amd64/include/loadfile_machdep.h | 4 | ||||
-rw-r--r-- | sys/arch/amd64/stand/boot/Makefile | 4 | ||||
-rw-r--r-- | sys/arch/amd64/stand/boot/conf.c | 4 | ||||
-rw-r--r-- | sys/arch/amd64/stand/cdboot/Makefile | 4 | ||||
-rw-r--r-- | sys/arch/amd64/stand/cdboot/conf.c | 4 | ||||
-rw-r--r-- | sys/arch/amd64/stand/libsa/exec_i386.c | 204 | ||||
-rw-r--r-- | sys/arch/amd64/stand/libsa/run_amd64.S | 175 | ||||
-rw-r--r-- | sys/arch/amd64/stand/pxeboot/Makefile | 4 | ||||
-rw-r--r-- | sys/arch/amd64/stand/pxeboot/conf.c | 4 |
9 files changed, 380 insertions, 27 deletions
diff --git a/sys/arch/amd64/include/loadfile_machdep.h b/sys/arch/amd64/include/loadfile_machdep.h index 5ee23dc8661..109ad40d990 100644 --- a/sys/arch/amd64/include/loadfile_machdep.h +++ b/sys/arch/amd64/include/loadfile_machdep.h @@ -1,5 +1,5 @@ /* XXX - DSR */ -/* $OpenBSD: loadfile_machdep.h,v 1.7 2019/04/10 04:17:33 deraadt Exp $ */ +/* $OpenBSD: loadfile_machdep.h,v 1.8 2019/05/15 06:52:33 mlarkin Exp $ */ /* $NetBSD: loadfile_machdep.h,v 1.1 1999/04/29 03:17:12 tsubai Exp $ */ /*- @@ -46,7 +46,7 @@ extern u_long efi_loadaddr; #define LOADADDR(a) (((((u_long)(a)) + offset)&0xfffffff) + \ efi_loadaddr) #else -#define LOADADDR(a) ((((u_long)(a)) + offset)&0xfffffff) +#define LOADADDR(a) ((a) + offset) #endif #define ALIGNENTRY(a) ((u_long)(a)) #define READ(f, b, c) read((f), (void *)LOADADDR(b), (c)) diff --git a/sys/arch/amd64/stand/boot/Makefile b/sys/arch/amd64/stand/boot/Makefile index c75a1956efe..a96ae53fb18 100644 --- a/sys/arch/amd64/stand/boot/Makefile +++ b/sys/arch/amd64/stand/boot/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.40 2019/04/20 22:59:03 deraadt Exp $ +# $OpenBSD: Makefile,v 1.41 2019/05/15 06:52:33 mlarkin Exp $ COPTS?= MAN?= boot.8 @@ -24,7 +24,7 @@ LDFLAGS+=-melf_i386 -L/usr/libdata SRCS+= boot.c bootarg.c cmd.c vars.c .PATH: ${SADIR}/libsa -SRCS+= gidt.S random_i386.S +SRCS+= gidt.S random_i386.S run_amd64.S SRCS+= cmd_i386.c dev_i386.c exec_i386.c gateA20.c machdep.c SRCS+= bioscons.c biosdev.c diskprobe.c memprobe.c time.c .if ${SOFTRAID:L} == "yes" diff --git a/sys/arch/amd64/stand/boot/conf.c b/sys/arch/amd64/stand/boot/conf.c index d784a7eacac..7709bfeb608 100644 --- a/sys/arch/amd64/stand/boot/conf.c +++ b/sys/arch/amd64/stand/boot/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.45 2019/05/11 22:09:48 mlarkin Exp $ */ +/* $OpenBSD: conf.c,v 1.46 2019/05/15 06:52:33 mlarkin Exp $ */ /* * Copyright (c) 1996 Michael Shalayeff @@ -40,7 +40,7 @@ #include <biosdev.h> #include <dev/cons.h> -const char version[] = "3.43"; +const char version[] = "3.44"; int debug = 1; diff --git a/sys/arch/amd64/stand/cdboot/Makefile b/sys/arch/amd64/stand/cdboot/Makefile index ff43909149e..fd09735657e 100644 --- a/sys/arch/amd64/stand/cdboot/Makefile +++ b/sys/arch/amd64/stand/cdboot/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.35 2019/04/20 22:59:03 deraadt Exp $ +# $OpenBSD: Makefile,v 1.36 2019/05/15 06:52:33 mlarkin Exp $ MAN= cdboot.8 @@ -18,7 +18,7 @@ BINMODE=644 .PATH: ${SADIR}/libsa SRCS+= machdep.c dev_i386.c exec_i386.c cmd_i386.c SRCS+= gidt.S random_i386.S biosdev.c bioscons.c gateA20.c \ - memprobe.c diskprobe.c time.c + memprobe.c diskprobe.c time.c run_amd64.S SRCS+= softraid_amd64.c .PATH: ${S}/stand/boot diff --git a/sys/arch/amd64/stand/cdboot/conf.c b/sys/arch/amd64/stand/cdboot/conf.c index 582ad251ff2..9731de7c436 100644 --- a/sys/arch/amd64/stand/cdboot/conf.c +++ b/sys/arch/amd64/stand/cdboot/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.39 2019/05/11 22:12:18 mlarkin Exp $ */ +/* $OpenBSD: conf.c,v 1.40 2019/05/15 06:52:33 mlarkin Exp $ */ /* * Copyright (c) 2004 Tom Cosgrove @@ -41,7 +41,7 @@ #include <biosdev.h> #include <dev/cons.h> -const char version[] = "3.42"; +const char version[] = "3.43"; int debug = 1; diff --git a/sys/arch/amd64/stand/libsa/exec_i386.c b/sys/arch/amd64/stand/libsa/exec_i386.c index 4b6aed12123..e7b7fc24826 100644 --- a/sys/arch/amd64/stand/libsa/exec_i386.c +++ b/sys/arch/amd64/stand/libsa/exec_i386.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_i386.c,v 1.29 2019/05/10 21:20:43 mlarkin Exp $ */ +/* $OpenBSD: exec_i386.c,v 1.30 2019/05/15 06:52:33 mlarkin Exp $ */ /* * Copyright (c) 1997-1998 Michael Shalayeff @@ -33,6 +33,7 @@ #include <dev/cons.h> #include <lib/libsa/loadfile.h> #include <machine/biosvar.h> +#include <machine/pte.h> #include <machine/specialreg.h> #include <stand/boot/bootarg.h> @@ -46,21 +47,48 @@ #include "softraid_amd64.h" #endif +#define BOOT_DEBUG + +#ifdef BOOT_DEBUG +#define DPRINTF(x...) do { printf(x); } while(0) +#else +#define DPRINTF(x...) +#endif /* BOOT_DEBUG */ + +#define LEGACY_KERNEL_ENTRY_POINT 0xffffffff81001000ULL + typedef void (*startfuncp)(int, int, int, int, int, int, int, int) __attribute__ ((noreturn)); +extern void launch_amd64_kernel_long(caddr_t, caddr_t, caddr_t, uint64_t, int, + int, int, uint64_t, int, int, int, uint64_t); + +caddr_t boot_alloc(void); +caddr_t make_kernel_page_tables(uint64_t); + void ucode_load(void); extern struct cmd_state cmd; char *bootmac = NULL; +extern char end[], _start[]; + +caddr_t pt_base_addr; + +#define PAGE_MASK (PAGE_SIZE - 1) +#define LONG_KERN_PML4_ADDR1 0x1000 +#define LONG_KERN_PML4_ADDR2 (((uint64_t)(end) + PAGE_MASK) & ~PAGE_MASK) + +/* + * N.B. - The following must stay in sync with pmap.h (including that here + * causes compile errors related to RBT_HEAD. + */ +#define NKL2_KIMG_ENTRIES 64 void run_loadfile(uint64_t *marks, int howto) { - u_long entry; -#ifdef EXEC_DEBUG - extern int debug; -#endif + uint64_t entry; + int long_kernel; dev_t bootdev = bootdev_dip->bootdev; size_t ac = BOOTARG_LEN; caddr_t av = (caddr_t)BOOTARG_OFF; @@ -70,10 +98,11 @@ run_loadfile(uint64_t *marks, int howto) bios_ddb_t ddb; extern int db_console; bios_bootduid_t bootduid; + caddr_t pml4, stack, new_av; #ifdef SOFTRAID bios_bootsr_t bootsr; struct sr_boot_volume *bv; -#endif +#endif /* SOFTRAID */ if (sa_cleanup != NULL) (*sa_cleanup)(); @@ -109,21 +138,58 @@ run_loadfile(uint64_t *marks, int howto) } sr_clear_keys(); -#endif +#endif /* SOFTRAID */ + + entry = marks[MARK_ENTRY]; - entry = marks[MARK_ENTRY] & 0x0fffffff; + printf("entry point at 0x%llx\n", entry); - printf("entry point at 0x%lx\n", entry); + pt_base_addr = (caddr_t)LONG_KERN_PML4_ADDR1; /* Pass memory map to the kernel */ mem_pass(); makebootargs(av, &ac); - /* stack and the gung is ok at this point, so, no need for asm setup */ - (*(startfuncp)entry)(howto, bootdev, BOOTARG_APIVER, marks[MARK_END], - extmem, cnvmem, ac, (int)av); - + /* + * Legacy kernels have entry set to 0xffffffff81001000. + * Other entry values indicate kernels that have random + * base VA and launch in 64 bit (long) mode. + */ + if (marks[MARK_ENTRY] == LEGACY_KERNEL_ENTRY_POINT) { + /* + * Legacy boot code expects entry 0x1001000, so mask + * off the high bits. + */ + entry &= 0xFFFFFFF; + long_kernel = 0; + } else + long_kernel = 1; + + /* + * Launch a long mode/randomly linked (post-6.5) kernel? + */ + if (long_kernel) { + new_av = boot_alloc(); /* Replaces old heap */ + memcpy((void *)new_av, av, ac); + + /* Stack grows down, so grab two pages. We'll waste the 2nd */ + stack = boot_alloc(); + stack = boot_alloc(); + + pml4 = make_kernel_page_tables(marks[MARK_ENTRY]); + launch_amd64_kernel_long((void *)launch_amd64_kernel_long, + pml4, stack, marks[MARK_ENTRY], howto, + bootdev, BOOTARG_APIVER, marks[MARK_END], + extmem, cnvmem, ac, (uint64_t)new_av); + } else { + /* + * Launch a legacy kernel + */ + + (*(startfuncp)entry)(howto, bootdev, BOOTARG_APIVER, + marks[MARK_END] & 0xfffffff, extmem, cnvmem, ac, (int)av); + } /* not reached */ } @@ -183,3 +249,115 @@ ucode_load(void) close(fd); } + +/* + * boot_alloc + * + * Special allocator for page table pages and kernel stack + * + * Allocates 1 page (PAGE_SIZE) of data. + * + * We have 2 regions available to us: + * 0x1000 ... 0xF000 : range 1 (stack is at 0xF000) + * end ... 0xA0000 (640KB) : range 2 + * + * We allocate from range 1 until it is complete, then skip to range 2. If + * range 2 is exhausted, we panic. + * + * Return value: + * VA of requested allocation + */ +caddr_t +boot_alloc(void) +{ + caddr_t ret; + static caddr_t cur = 0; + static int skipped = 0; + + /* First time? */ + if (cur == 0) + cur = (caddr_t)pt_base_addr; + + ret = cur; + + if (((uint64_t)cur + PAGE_SIZE >= 0xF000) && !skipped) { + cur = (caddr_t)LONG_KERN_PML4_ADDR2; + skipped = 1; + } else + cur += PAGE_SIZE; + + if ((uint64_t)cur >= 640 * 1024) + panic("out of memory"); + + return ret; +} + +/* + * make_kernel_page_tables + * + * Sets up a minimal set of page tables for early use in the kernel. In + * pre_init_x86_64, the kernel will rebuild its page tables, so the + * table constructed here only needs the minimal mapping. + * + * [entry ... end] => PA 0x1000000 (16MB, the current phys loadaddr) + * + * In BIOS boot mode, this function overwrites the heap with the long + * mode kernel boostrap page tables and thus must be called immediately + * before switching to long mode and starting the kernel. + * + * Parameters: + * entry_lo: the low byte (masked) of the kernel entry point + * + * Return value: + * PML4 PA of the new table + */ +caddr_t +make_kernel_page_tables(uint64_t entry) +{ + uint64_t *pml4, *pml3, *pml2, *pml1; + int i, j, k, kern_pml4, kern_pml3, kern_pml2, kern_pml1; + + kern_pml4 = (entry & L4_MASK) >> L4_SHIFT; + kern_pml3 = (entry & L3_MASK) >> L3_SHIFT; + kern_pml2 = (entry & L2_MASK) >> L2_SHIFT; + kern_pml1 = (entry & L1_MASK) >> L1_SHIFT; + + pml4 = (uint64_t *)boot_alloc(); + + /* Map kernel */ + pml3 = (uint64_t *)boot_alloc(); + pml4[kern_pml4] = (uint64_t)pml3 | PG_V | PG_RW; + + pml2 = (uint64_t *)boot_alloc(); + pml3[kern_pml3] = (uint64_t)pml2 | PG_V | PG_RW; + + for (i = 0; i < NKL2_KIMG_ENTRIES; i++) { + pml1 = (uint64_t *)boot_alloc(); + pml2[i + kern_pml2] = (uint64_t)pml1 | PG_V | PG_RW; + + /* The first page of PTEs may start at a different offset */ + if (i == kern_pml2) + k = kern_pml1; + else + k = 0; + + /* + * Map [k...511] PTEs. + */ + for (j = k; j < 512; j++) + pml1[j] = (uint64_t)(((8 + i) * NBPD_L2) + + (j - kern_pml1) * PAGE_SIZE) | PG_V | PG_RW; + } + + /* Map first 4GB phys for kernel page table, stack, and bootstrap */ + pml3 = (uint64_t *)boot_alloc(); + pml4[0] = (uint64_t)pml3 | PG_V | PG_RW; /* Covers 0-512GB */ + + pml2 = (uint64_t *)boot_alloc(); + pml3[0] = (uint64_t)pml2 | PG_V | PG_RW; /* Covers 0-1GB */ + + for (i = 0; i < 512; i++) + pml2[i] = (i << 21) | PG_V | PG_RW | PG_PS; + + return (caddr_t)pml4; +} diff --git a/sys/arch/amd64/stand/libsa/run_amd64.S b/sys/arch/amd64/stand/libsa/run_amd64.S new file mode 100644 index 00000000000..d6640cdc585 --- /dev/null +++ b/sys/arch/amd64/stand/libsa/run_amd64.S @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019 Mike Larkin <mlarkin@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <machine/asm.h> +#include <machine/specialreg.h> + +#define CODE_SEGMENT 0x8 +#define DATA_SEGMENT 0x10 + + .text + .code32 + .global _C_LABEL(launch_amd64_kernel_long) + /* + * void launch_amd64_kernel_long(caddr_t base, caddr_t pml4, + * caddr_t rsp, uint64_t entry, int boothowto, int bootdev, + * int bootapiver, uint64_t end, int extmem, int cnvmem, + * int ac, uint64_t av); + */ +_C_LABEL(launch_amd64_kernel_long): +asm_start: + xchg %bx, %bx + + /* + * We are in 32 bit mode + * - compute indirect jump targets + * - compute GDT locations + */ + popl %edi /* Discard return address */ + popl %edi /* %edi = bootloader base address */ + + /* Current mode -> 32 bit jump target */ + leal (prot_mode - asm_start)(%edi), %eax + movl %eax, (start32r - asm_start)(%edi) + + /* 32 bit -> 64 bit jump target */ + leal (long_mode - asm_start)(%edi), %eax + movl %eax, (start64r - asm_start)(%edi) + + /* Current mode -> 32 bit GDT */ + leal (gdt32 - asm_start)(%edi), %eax + movl %eax, (gdtrr32 - asm_start)(%edi) + + /* 32 bit -> 64 bit GDT */ + leal (gdt64 - asm_start)(%edi), %eax + movl %eax, (gdtrr64 - asm_start)(%edi) + + cli + + lgdtl (gdtr32 - asm_start)(%edi) + + mov %cr0, %eax + orl $(CR0_PE), %eax + mov %eax, %cr0 + + ljmpl *(start32r - asm_start)(%edi) + + .align 4 +prot_mode: + movw $DATA_SEGMENT, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %gs + movw %ax, %ss + movw %ax, %fs + + mov $(CR4_PAE | CR4_PSE), %eax + mov %eax, %cr4 + + lgdtl (gdtr64 - asm_start)(%edi) + movl $MSR_EFER, %ecx + xorl %edx, %edx + movl $(EFER_LME | EFER_NXE | EFER_SCE), %eax + wrmsr + + movl (%esp), %eax /* first arg - PML4 */ + movl 4(%esp), %ebx /* second arg - kernel stack */ + movl %eax, %cr3 + + jmp 1f +1: jmp 1f +1: movl %cr0, %eax + orl $CR0_DEFAULT, %eax + movl %eax, %cr0 + + jmp 1f +1: jmp 1f +1: ljmpl *(start64r - asm_start)(%edi) + +long_mode: + .code64 + movq %rsp, %rax + movq %rbx, %rsp + + /* + * params stack at %rax right now: + * + * +0 32 bit PML4 ptr + * +4 32 bit kernel stack + * +8 start va low bytes + + +12 start va high bytes + * +16 boothowto (%rdi) + * +20 bootdev (%rsi) + * +24 BOOTARG_APIVER (%rdx) + * +28 marks[MARK_END] low bytes (%rcx) + * +32 marks[MARK_END] high bytes (%rcx) + * +36 extmem (%r8) + * +40 cnvmem (%r9) + * +44 ac (%rsp, will be %rsp + 8 after call below) + * +48 av low bytes (%rsp + 8, will be %rsp + 16 after call below) + * +52 av high bytes (%rsp + 16, will be %rsp + 24 after call below) + */ + + movl 16(%rax), %edi + movl 20(%rax), %esi + movl 24(%rax), %edx + movq 28(%rax), %rcx + movl 36(%rax), %r8d + movl 40(%rax), %r9d + movq 48(%rax), %rbx /* av */ + pushq %rbx + movl 44(%rax), %ebx /* ac */ + pushq %rbx + movq 8(%rax), %r11 + + /* + * start(howto, bootdev, BOOTARG_APIVER, marks[MARK_END], + * extmem, cnvmem, ac, av); + */ + call *%r11 + + cli + hlt + /* NOTREACHED */ + + .align 4 +start32r: + .long 0 + .long CODE_SEGMENT + +start64r: + .long 0 + .long CODE_SEGMENT + +gdt32: + .long 0, 0 + .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9f, 0xcf, 0x00 + .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x93, 0xcf, 0x00 +gdtr32: + .word gdtr32 - gdt32 +gdtrr32: + .quad + + .align 8 +gdt64: + .quad 0x0000000000000000 + .quad 0x00af9a000000ffff + .quad 0x00cf92000000ffff + +gdtr64: + .word gdtr64 - gdt64 +gdtrr64: + .quad diff --git a/sys/arch/amd64/stand/pxeboot/Makefile b/sys/arch/amd64/stand/pxeboot/Makefile index 9b76c21d153..12948210cee 100644 --- a/sys/arch/amd64/stand/pxeboot/Makefile +++ b/sys/arch/amd64/stand/pxeboot/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.33 2019/04/20 22:59:03 deraadt Exp $ +# $OpenBSD: Makefile,v 1.34 2019/05/15 06:52:33 mlarkin Exp $ MAN= pxeboot.8 @@ -17,7 +17,7 @@ BINMODE=644 .PATH: ${SADIR}/libsa # i386 stuff (so, it will possibly load in the same 64k) -SRCS+= machdep.c exec_i386.c cmd_i386.c +SRCS+= machdep.c exec_i386.c cmd_i386.c run_amd64.S SRCS+= gidt.S random_i386.S biosdev.c bioscons.c gateA20.c \ memprobe.c diskprobe.c time.c ## biosprobe.c SRCS+= pxe.c pxe_call.S pxe_net.c pxe_udp.c diff --git a/sys/arch/amd64/stand/pxeboot/conf.c b/sys/arch/amd64/stand/pxeboot/conf.c index 00ec2c2a40e..bef9e5f520f 100644 --- a/sys/arch/amd64/stand/pxeboot/conf.c +++ b/sys/arch/amd64/stand/pxeboot/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.43 2019/05/11 22:13:26 mlarkin Exp $ */ +/* $OpenBSD: conf.c,v 1.44 2019/05/15 06:52:33 mlarkin Exp $ */ /* * Copyright (c) 2004 Tom Cosgrove @@ -43,7 +43,7 @@ #include "pxeboot.h" #include "pxe_net.h" -const char version[] = "3.42"; +const char version[] = "3.43"; int debug = 0; void (*sa_cleanup)(void) = pxe_shutdown; |