git: c73795f9f44a - stable/13 - stand: Initial kboot support on amd64

From: Warner Losh <imp_at_FreeBSD.org>
Date: Tue, 24 Jan 2023 22:10:31 UTC
The branch stable/13 has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=c73795f9f44af8c4c0546cdf6aca4f382336e498

commit c73795f9f44af8c4c0546cdf6aca4f382336e498
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2022-05-01 17:13:18 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2023-01-24 21:49:21 +0000

    stand: Initial kboot support on amd64
    
    Get amd64 compiling. However, the current kboot supports an old way of
    enumerating memory and the new way needs to be incorporated as well. The
    powerpc folks could use either, it seems and newer powerpc platforms
    need some changes for kboot to work anyway.
    
    This commit includes the linker script, trampoline code to start the new
    kernel, Linux system calls and the necessary configuration glue needed
    to build the binaries.
    
    This includes a quick hack to get multiboot support, but we need to
    really share these defines. The multiiboot2.h is the minimum needed to
    build. We have multiboot information in three places now, so a
    refactoring is in order.
    
    This should be considered, at best, preliminary and experimental for
    anybody wishing to try it out.
    
    Sponsored by:           Netflix
    Reviewed by:            tsoome
    Differential Revision:  https://reviews.freebsd.org/D35100
    
    (cherry picked from commit 7df08a14e3fda5d0e7f3aef523ffcc7b3fae248d)
---
 share/mk/src.opts.mk                   |   4 +
 stand/kboot/arch/amd64/Makefile.inc    |   8 +
 stand/kboot/arch/amd64/amd64_tramp.S   |  76 ++++++++
 stand/kboot/arch/amd64/conf.c          | 104 ++++++++++
 stand/kboot/arch/amd64/elf64_freebsd.c | 345 +++++++++++++++++++++++++++++++++
 stand/kboot/arch/amd64/host_syscall.S  |  29 +++
 stand/kboot/arch/amd64/ldscript.amd64  |  72 +++++++
 stand/kboot/arch/amd64/multiboot2.h    |   1 +
 stand/kboot/arch/amd64/syscall_nr.h    |  15 ++
 9 files changed, 654 insertions(+)

diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk
index 3fa33f936df5..fc98ce6c032b 100644
--- a/share/mk/src.opts.mk
+++ b/share/mk/src.opts.mk
@@ -316,6 +316,10 @@ BROKEN_OPTIONS+=EFI
 .if ${__T:Mpowerpc*} == ""
 BROKEN_OPTIONS+=LOADER_OFW
 .endif
+# KBOOT is only for powerpc64 (powerpc64le broken) and kinda for amd64
+.if ${__T} != "powerpc64" && ${__T} != "amd64"
+BROKEN_OPTIONS+=LOADER_KBOOT
+.endif
 # UBOOT is only for arm, and big-endian powerpc
 .if (${__T:Marm*} == "" && ${__T:Mpowerpc*} == "") || ${__T} == "powerpc64le"
 BROKEN_OPTIONS+=LOADER_UBOOT
diff --git a/stand/kboot/arch/amd64/Makefile.inc b/stand/kboot/arch/amd64/Makefile.inc
new file mode 100644
index 000000000000..fb954e798599
--- /dev/null
+++ b/stand/kboot/arch/amd64/Makefile.inc
@@ -0,0 +1,8 @@
+SRCS+=		conf.c host_syscall.S amd64_tramp.S elf64_freebsd.c
+
+CFLAGS+=	-I${SYSDIR}/contrib/dev/acpica/include
+# load address. set in linker script
+RELOC?=		0x0
+CFLAGS+=	-DRELOC=${RELOC}
+
+LDFLAGS=	-nostdlib -static -T ${.CURDIR}/arch/${MACHINE_ARCH}/ldscript.amd64
diff --git a/stand/kboot/arch/amd64/amd64_tramp.S b/stand/kboot/arch/amd64/amd64_tramp.S
new file mode 100644
index 000000000000..877705407f92
--- /dev/null
+++ b/stand/kboot/arch/amd64/amd64_tramp.S
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Benno Rice under sponsorship from
+ * the FreeBSD Foundation.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asmacros.h>
+
+#define ASM_FILE
+#include "multiboot2.h"
+
+	.text
+	.globl	amd64_tramp
+
+/*
+ * void amd64_tramp(uint64_t stack, void *copy_finish, uint64_t kernend,
+ *		    uint64_t modulep, uint64_t pagetable, uint64_t entry)
+ */
+amd64_tramp:
+	cli			/* Make sure we don't get interrupted. */
+	movq	%rdi,%rsp	/* Switch to our temporary stack. */
+
+	movq	%rdx,%r12	/* Stash the kernel values for later. */
+	movq	%rcx,%r13
+	movq	%r8,%r14
+	movq	%r9,%r15
+
+	callq	*%rsi		/* Call copy_finish so we're all ready to go. */
+
+	pushq	%r12		/* Push kernend. */
+	salq	$32,%r13	/* Shift modulep and push it. */
+	pushq	%r13
+	pushq	%r15		/* Push the entry address. */
+	movq	%r14,%cr3	/* Switch page tables. */
+	ret			/* "Return" to kernel entry. */
+
+	ALIGN_TEXT
+amd64_tramp_end:
+
+/* void multiboot2_exec(uint64_t entry, uint64_t multiboot_info, uint64_t stack) */
+	.globl	multiboot2_exec
+multiboot2_exec:
+	movq	%rdx,%rsp
+	pushq	%rdi
+	movq	%rsi,%rbx
+	movq	$MULTIBOOT2_BOOTLOADER_MAGIC,%rax
+	ret
+
+	.data
+	.globl	amd64_tramp_size
+amd64_tramp_size:
+	.long	amd64_tramp_end-amd64_tramp
diff --git a/stand/kboot/arch/amd64/conf.c b/stand/kboot/arch/amd64/conf.c
new file mode 100644
index 000000000000..b840d008a347
--- /dev/null
+++ b/stand/kboot/arch/amd64/conf.c
@@ -0,0 +1,104 @@
+/*-
+ * Copyright (C) 1999 Michael Smith <msmith@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "bootstrap.h"
+
+#if defined(LOADER_NET_SUPPORT)
+#include "dev_net.h"
+#endif
+
+extern struct devsw hostdisk;
+
+/*
+ * We could use linker sets for some or all of these, but
+ * then we would have to control what ended up linked into
+ * the bootstrap.  So it's easier to conditionalise things
+ * here.
+ *
+ * XXX rename these arrays to be consistent and less namespace-hostile
+ */
+
+/* Exported for libsa */
+struct devsw *devsw[] = {
+#if defined(LOADER_DISK_SUPPORT) || defined(LOADER_CD9660_SUPPORT)
+    &hostdisk,
+#endif
+#if defined(LOADER_NET_SUPPORT)
+    &netdev,
+#endif
+    NULL
+};
+
+struct fs_ops *file_system[] = {
+#if defined(LOADER_UFS_SUPPORT)
+    &ufs_fsops,
+#endif
+#if defined(LOADER_CD9660_SUPPORT)
+    &cd9660_fsops,
+#endif
+#if defined(LOADER_EXT2FS_SUPPORT)
+    &ext2fs_fsops,
+#endif
+#if defined(LOADER_NFS_SUPPORT)
+    &nfs_fsops,
+#endif
+#if defined(LOADER_TFTP_SUPPORT)
+    &tftp_fsops,
+#endif
+#if defined(LOADER_GZIP_SUPPORT)
+    &gzipfs_fsops,
+#endif
+#if defined(LOADER_BZIP2_SUPPORT)
+    &bzipfs_fsops,
+#endif
+    &dosfs_fsops,
+    NULL
+};
+
+extern struct netif_driver kbootnet;
+
+struct netif_driver *netif_drivers[] = {
+#if 0 /* XXX */
+#if defined(LOADER_NET_SUPPORT)
+	&kbootnet,
+#endif
+#endif
+	NULL,
+};
+
+/*
+ * Consoles
+ */
+extern struct console hostconsole;
+
+struct console *consoles[] = {
+    &hostconsole,
+    NULL
+};
diff --git a/stand/kboot/arch/amd64/elf64_freebsd.c b/stand/kboot/arch/amd64/elf64_freebsd.c
new file mode 100644
index 000000000000..a45a0db32e44
--- /dev/null
+++ b/stand/kboot/arch/amd64/elf64_freebsd.c
@@ -0,0 +1,345 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <string.h>
+#include <machine/elf.h>
+#include <stand.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#ifdef EFI
+#include <efi.h>
+#include <efilib.h>
+#endif
+
+#include "bootstrap.h"
+
+#include "platform/acfreebsd.h"
+#include "acconfig.h"
+#define ACPI_SYSTEM_XFACE
+#include "actypes.h"
+#include "actbl.h"
+
+#ifdef EFI
+#include "loader_efi.h"
+#endif
+
+#ifdef EFI
+static EFI_GUID acpi_guid = ACPI_TABLE_GUID;
+static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID;
+#endif
+
+#ifdef EFI
+#define LOADER_PAGE_SIZE EFI_PAGE_SIZE
+#else
+#define LOADER_PAGE_SIZE 8192
+#endif
+
+extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp,
+    bool exit_bs);
+
+static int	elf64_exec(struct preloaded_file *amp);
+static int	elf64_obj_exec(struct preloaded_file *amp);
+
+static struct file_format amd64_elf = {
+	.l_load = elf64_loadfile,
+	.l_exec = elf64_exec,
+};
+static struct file_format amd64_elf_obj = {
+	.l_load = elf64_obj_loadfile,
+	.l_exec = elf64_obj_exec,
+};
+
+#if 0
+extern struct file_format multiboot2;
+extern struct file_format multiboot2_obj;
+#endif
+
+struct file_format *file_formats[] = {
+#if 0
+	&multiboot2,
+	&multiboot2_obj,
+#endif
+	&amd64_elf,
+	&amd64_elf_obj,
+	NULL
+};
+
+#ifdef EFI
+static pml4_entry_t *PT4;
+static pdp_entry_t *PT3;
+static pdp_entry_t *PT3_l, *PT3_u;
+static pd_entry_t *PT2;
+static pd_entry_t *PT2_l0, *PT2_l1, *PT2_l2, *PT2_l3, *PT2_u0, *PT2_u1;
+
+extern EFI_PHYSICAL_ADDRESS staging;
+
+static void (*trampoline)(uint64_t stack, void *copy_finish, uint64_t kernend,
+    uint64_t modulep, pml4_entry_t *pagetable, uint64_t entry);
+#endif
+
+extern uintptr_t amd64_tramp;
+extern uint32_t amd64_tramp_size;
+
+/*
+ * There is an ELF kernel and one or more ELF modules loaded.
+ * We wish to start executing the kernel image, so make such
+ * preparations as are required, and do so.
+ */
+static int
+elf64_exec(struct preloaded_file *fp)
+{
+#ifdef EFI
+	struct file_metadata	*md;
+	Elf_Ehdr 		*ehdr;
+	vm_offset_t		modulep, kernend, trampcode, trampstack;
+	int			err, i;
+	ACPI_TABLE_RSDP		*rsdp;
+	char			buf[24];
+	int			revision;
+	bool			copy_auto;
+
+#ifdef EFI
+	copy_auto = copy_staging == COPY_STAGING_AUTO;
+	if (copy_auto)
+		copy_staging = fp->f_kernphys_relocatable ?
+		    COPY_STAGING_DISABLE : COPY_STAGING_ENABLE;
+#else
+	copy_auto = COPY_STAGING_DISABLE; /* XXX */
+#endif
+
+	/*
+	 * Report the RSDP to the kernel. While this can be found with
+	 * a BIOS boot, the RSDP may be elsewhere when booted from UEFI.
+	 * The old code used the 'hints' method to communite this to
+	 * the kernel. However, while convenient, the 'hints' method
+	 * is fragile and does not work when static hints are compiled
+	 * into the kernel. Instead, move to setting different tunables
+	 * that start with acpi. The old 'hints' can be removed before
+	 * we branch for FreeBSD 12.
+	 */
+
+#ifdef EFI
+	rsdp = efi_get_table(&acpi20_guid);
+	if (rsdp == NULL) {
+		rsdp = efi_get_table(&acpi_guid);
+	}
+#else
+	rsdp = NULL;
+#warning "write me"
+#endif
+	if (rsdp != NULL) {
+		sprintf(buf, "0x%016llx", (unsigned long long)rsdp);
+		setenv("hint.acpi.0.rsdp", buf, 1);
+		setenv("acpi.rsdp", buf, 1);
+		revision = rsdp->Revision;
+		if (revision == 0)
+			revision = 1;
+		sprintf(buf, "%d", revision);
+		setenv("hint.acpi.0.revision", buf, 1);
+		setenv("acpi.revision", buf, 1);
+		strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId));
+		buf[sizeof(rsdp->OemId)] = '\0';
+		setenv("hint.acpi.0.oem", buf, 1);
+		setenv("acpi.oem", buf, 1);
+		sprintf(buf, "0x%016x", rsdp->RsdtPhysicalAddress);
+		setenv("hint.acpi.0.rsdt", buf, 1);
+		setenv("acpi.rsdt", buf, 1);
+		if (revision >= 2) {
+			/* XXX extended checksum? */
+			sprintf(buf, "0x%016llx",
+			    (unsigned long long)rsdp->XsdtPhysicalAddress);
+			setenv("hint.acpi.0.xsdt", buf, 1);
+			setenv("acpi.xsdt", buf, 1);
+			sprintf(buf, "%d", rsdp->Length);
+			setenv("hint.acpi.0.xsdt_length", buf, 1);
+			setenv("acpi.xsdt_length", buf, 1);
+		}
+	}
+
+	if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+		return (EFTYPE);
+	ehdr = (Elf_Ehdr *)&(md->md_data);
+
+	trampcode = copy_staging == COPY_STAGING_ENABLE ?
+	    (vm_offset_t)0x0000000040000000 /* 1G */ :
+	    (vm_offset_t)0x0000000100000000; /* 4G */;
+#ifdef EFI
+	err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 1,
+	    (EFI_PHYSICAL_ADDRESS *)&trampcode);
+	if (EFI_ERROR(err)) {
+		printf("Unable to allocate trampoline\n");
+		if (copy_auto)
+			copy_staging = COPY_STAGING_AUTO;
+		return (ENOMEM);
+	}
+#else
+#warning "Write me"
+#endif
+	bzero((void *)trampcode, LOADER_PAGE_SIZE);
+	trampstack = trampcode + LOADER_PAGE_SIZE - 8;
+	bcopy((void *)&amd64_tramp, (void *)trampcode, amd64_tramp_size);
+	trampoline = (void *)trampcode;
+
+	if (copy_staging == COPY_STAGING_ENABLE) {
+		PT4 = (pml4_entry_t *)0x0000000040000000;
+#ifdef EFI
+		err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 3,
+		    (EFI_PHYSICAL_ADDRESS *)&PT4);
+		if (EFI_ERROR(err)) {
+			printf("Unable to allocate trampoline page table\n");
+			BS->FreePages(trampcode, 1);
+			if (copy_auto)
+				copy_staging = COPY_STAGING_AUTO;
+			return (ENOMEM);
+		}
+#else
+#warning "Write me"
+#endif
+		bzero(PT4, 3 * LOADER_PAGE_SIZE);
+		PT3 = &PT4[512];
+		PT2 = &PT3[512];
+
+		/*
+		 * This is kinda brutal, but every single 1GB VM
+		 * memory segment points to the same first 1GB of
+		 * physical memory.  But it is more than adequate.
+		 */
+		for (i = 0; i < NPTEPG; i++) {
+			/*
+			 * Each slot of the L4 pages points to the
+			 * same L3 page.
+			 */
+			PT4[i] = (pml4_entry_t)PT3;
+			PT4[i] |= PG_V | PG_RW;
+
+			/*
+			 * Each slot of the L3 pages points to the
+			 * same L2 page.
+			 */
+			PT3[i] = (pdp_entry_t)PT2;
+			PT3[i] |= PG_V | PG_RW;
+
+			/*
+			 * The L2 page slots are mapped with 2MB pages for 1GB.
+			 */
+			PT2[i] = (pd_entry_t)i * (2 * 1024 * 1024);
+			PT2[i] |= PG_V | PG_RW | PG_PS;
+		}
+	} else {
+		PT4 = (pml4_entry_t *)0x0000000100000000; /* 4G */
+#ifdef EFI
+		err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 9,
+		    (EFI_PHYSICAL_ADDRESS *)&PT4);
+		if (EFI_ERROR(err)) {
+			printf("Unable to allocate trampoline page table\n");
+			BS->FreePages(trampcode, 9);
+			if (copy_auto)
+				copy_staging = COPY_STAGING_AUTO;
+			return (ENOMEM);
+		}
+#else
+#warning "Write me"
+#endif
+
+		bzero(PT4, 9 * LOADER_PAGE_SIZE);
+
+		PT3_l = &PT4[NPML4EPG * 1];
+		PT3_u = &PT4[NPML4EPG * 2];
+		PT2_l0 = &PT4[NPML4EPG * 3];
+		PT2_l1 = &PT4[NPML4EPG * 4];
+		PT2_l2 = &PT4[NPML4EPG * 5];
+		PT2_l3 = &PT4[NPML4EPG * 6];
+		PT2_u0 = &PT4[NPML4EPG * 7];
+		PT2_u1 = &PT4[NPML4EPG * 8];
+
+		/* 1:1 mapping of lower 4G */
+		PT4[0] = (pml4_entry_t)PT3_l | PG_V | PG_RW;
+		PT3_l[0] = (pdp_entry_t)PT2_l0 | PG_V | PG_RW;
+		PT3_l[1] = (pdp_entry_t)PT2_l1 | PG_V | PG_RW;
+		PT3_l[2] = (pdp_entry_t)PT2_l2 | PG_V | PG_RW;
+		PT3_l[3] = (pdp_entry_t)PT2_l3 | PG_V | PG_RW;
+		for (i = 0; i < 4 * NPDEPG; i++) {
+			PT2_l0[i] = ((pd_entry_t)i << PDRSHIFT) | PG_V |
+			    PG_RW | PG_PS;
+		}
+
+		/* mapping of kernel 2G below top */
+		PT4[NPML4EPG - 1] = (pml4_entry_t)PT3_u | PG_V | PG_RW;
+		PT3_u[NPDPEPG - 2] = (pdp_entry_t)PT2_u0 | PG_V | PG_RW;
+		PT3_u[NPDPEPG - 1] = (pdp_entry_t)PT2_u1 | PG_V | PG_RW;
+		/* compat mapping of phys @0 */
+		PT2_u0[0] = PG_PS | PG_V | PG_RW;
+		/* this maps past staging area */
+		for (i = 1; i < 2 * NPDEPG; i++) {
+			PT2_u0[i] = ((pd_entry_t)staging +
+			    ((pd_entry_t)i - 1) * NBPDR) |
+			    PG_V | PG_RW | PG_PS;
+		}
+	}
+
+	printf("staging %#lx (%scopying) tramp %p PT4 %p\n",
+	    staging, copy_staging == COPY_STAGING_ENABLE ? "" : "not ",
+	    trampoline, PT4);
+	printf("Start @ 0x%lx ...\n", ehdr->e_entry);
+
+#ifdef EFI
+	efi_time_fini();
+#endif
+	err = bi_load(fp->f_args, &modulep, &kernend, true);
+	if (err != 0) {
+#ifdef EFI
+		efi_time_init();
+#endif
+		if (copy_auto)
+			copy_staging = COPY_STAGING_AUTO;
+		return (err);
+	}
+
+	dev_cleanup();
+
+	trampoline(trampstack, copy_staging == COPY_STAGING_ENABLE ?
+	    efi_copy_finish : efi_copy_finish_nop, kernend, modulep,
+	    PT4, ehdr->e_entry);
+#endif
+
+	panic("exec returned");
+}
+
+static int
+elf64_obj_exec(struct preloaded_file *fp)
+{
+
+	return (EFTYPE);
+}
diff --git a/stand/kboot/arch/amd64/host_syscall.S b/stand/kboot/arch/amd64/host_syscall.S
new file mode 100644
index 000000000000..5bf0fca0cec1
--- /dev/null
+++ b/stand/kboot/arch/amd64/host_syscall.S
@@ -0,0 +1,29 @@
+#include <machine/asm.h>
+
+/*
+ * Emulate the Linux system call interface. The system call number is set in
+ * %rax, and %rdi, %rsi, %rdx, %r10, %r8, %r9 have the 6 system call
+ * arguments. errno is returned as a negative value, but we use it more as a
+ * flag something went wrong rather than using its value.
+ *
+ * Note: For system calls, we use %r10 instead of %rcx for the 4th argument.
+ * See section A.2.1 for the Linux calling conventions of the ABI spec
+ * https://web.archive.org/web/20160801075146/http://www.x86-64.org/documentation/abi.pdf
+ * In addition to the below, %r11 and %rcx are destroyed, negative
+ * values are ERRNO for %rax between -1 and -4095 otherwise the system
+ * call is successful. Unlike other Unix systems, carry isn't used to
+ * signal an error in the system call. We expose the raw system call
+ * result, rather than do the POSIX converion to -1 and setting errno.
+ */
+ENTRY(host_syscall)
+	movq %rdi, %rax		/* SYS_ number in %rax */
+	movq %rsi, %rdi		/* arg2 -> 1 */
+	movq %rdx, %rsi		/* arg3 -> 2 */
+	movq %rcx, %rdx		/* arg4 -> 3 */
+	movq %r8, %r10		/* arg5 -> 4 */
+	movq %r9, %r8		/* arg6 -> 5 */
+        movq 8(%rsp),%r9        /* arg7 -> 6 from stack.  */
+        syscall
+	ret
+/* Note: We're exposing the raw return value to the caller */
+END(host_syscall)
diff --git a/stand/kboot/arch/amd64/ldscript.amd64 b/stand/kboot/arch/amd64/ldscript.amd64
new file mode 100644
index 000000000000..bbfe47cd4ef5
--- /dev/null
+++ b/stand/kboot/arch/amd64/ldscript.amd64
@@ -0,0 +1,72 @@
+/* $FreeBSD$ */
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(_start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0x401000;
+  ImageBase = .;
+  .hash : { *(.hash) }  /* this MUST come first! */
+  . = ALIGN(4096);
+  .eh_frame :
+  {
+    *(.eh_frame)
+  }
+  . = ALIGN(4096);
+  .text		: {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf32.em. */
+    *(.gnu.warning)
+    *(.plt)
+  } =0xCCCCCCCC
+  . = ALIGN(4096);
+  .data		: {
+    *(.rodata .rodata.* .gnu.linkonce.r.*)
+    *(.rodata1)
+    *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+    *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
+    *(.opd)
+    *(.data .data.* .gnu.linkonce.d.*)
+    *(.data1)
+    *(.plabel)
+    *(.dynbss)
+    *(.bss .bss.* .gnu.linkonce.b.*)
+    *(COMMON)
+  }
+  . = ALIGN(4096);
+  set_Xcommand_set	: {
+    __start_set_Xcommand_set = .;
+    *(set_Xcommand_set)
+    __stop_set_Xcommand_set = .;
+  }
+  set_Xficl_compile_set	: {
+    __start_set_Xficl_compile_set = .;
+    *(set_Xficl_compile_set)
+    __stop_set_Xficl_compile_set = .;
+  }
+  . = ALIGN(4096);
+  __gp = .;
+  .sdata	: {
+    *(.got.plt .got)
+    *(.sdata .sdata.* .gnu.linkonce.s.*)
+    *(dynsbss)
+    *(.sbss .sbss.* .gnu.linkonce.sb.*)
+    *(.scommon)
+  }
+  . = ALIGN(4096);
+  .dynamic	: { *(.dynamic) }
+  . = ALIGN(4096);
+  .rela.dyn	: {
+    *(.rela.data*)
+    *(.rela.got)
+    *(.rela.stab)
+    *(.relaset_*)
+  }
+  . = ALIGN(4096);
+  .reloc	: { *(.reloc) }
+  . = ALIGN(4096);
+  .dynsym	: { *(.dynsym) }
+  . = ALIGN(4096);
+  .dynstr	: { *(.dynstr) }
+}
diff --git a/stand/kboot/arch/amd64/multiboot2.h b/stand/kboot/arch/amd64/multiboot2.h
new file mode 100644
index 000000000000..bf673da66c2c
--- /dev/null
+++ b/stand/kboot/arch/amd64/multiboot2.h
@@ -0,0 +1 @@
+#define MULTIBOOT2_BOOTLOADER_MAGIC		0x36d76289
diff --git a/stand/kboot/arch/amd64/syscall_nr.h b/stand/kboot/arch/amd64/syscall_nr.h
new file mode 100644
index 000000000000..193368364bf3
--- /dev/null
+++ b/stand/kboot/arch/amd64/syscall_nr.h
@@ -0,0 +1,15 @@
+#define SYS_read		  0
+#define SYS_write		  1
+#define SYS_open		  2
+#define SYS_close		  3
+#define SYS_gettimeofday	 96
+#define SYS_reboot		169
+#define SYS_mmap		  9
+#define SYS_uname		 63
+#define SYS_lseek		  8
+#define SYS_getdents		 78
+#define SYS_select		 23
+#define __NR_kexec_load		246
+
+#define KEXEC_ARCH_X86_64	 62
+#define KEXEC_ARCH		KEXEC_ARCH_X86_64