aboutsummaryrefslogtreecommitdiff
path: root/stand/efi/loader
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>2017-11-14 23:02:19 +0000
committerWarner Losh <imp@FreeBSD.org>2017-11-14 23:02:19 +0000
commitca987d4641cdcd7f27e153db17c5bf064934faf5 (patch)
tree6c3860e3ba8949be9528d644fbb7fa88d8bbbb79 /stand/efi/loader
parent6eac7115560381ce5c9e2939ab3fce82bb9b6a95 (diff)
downloadsrc-ca987d4641cdcd7f27e153db17c5bf064934faf5.tar.gz
src-ca987d4641cdcd7f27e153db17c5bf064934faf5.zip
Move sys/boot to stand. Fix all references to new location
Sponsored by: Netflix
Notes
Notes: svn path=/head/; revision=325834
Diffstat (limited to 'stand/efi/loader')
-rw-r--r--stand/efi/loader/Makefile131
-rw-r--r--stand/efi/loader/Makefile.depend17
-rw-r--r--stand/efi/loader/arch/amd64/Makefile.inc15
-rw-r--r--stand/efi/loader/arch/amd64/amd64_tramp.S64
-rw-r--r--stand/efi/loader/arch/amd64/elf64_freebsd.c208
-rw-r--r--stand/efi/loader/arch/amd64/exc.S165
-rw-r--r--stand/efi/loader/arch/amd64/ldscript.amd6472
-rw-r--r--stand/efi/loader/arch/amd64/start.S76
-rw-r--r--stand/efi/loader/arch/amd64/trap.c408
-rw-r--r--stand/efi/loader/arch/arm/Makefile.inc6
-rw-r--r--stand/efi/loader/arch/arm/exec.c103
-rw-r--r--stand/efi/loader/arch/arm/ldscript.arm67
-rw-r--r--stand/efi/loader/arch/arm/start.S189
-rw-r--r--stand/efi/loader/arch/arm64/Makefile.inc12
-rw-r--r--stand/efi/loader/arch/arm64/exec.c144
-rw-r--r--stand/efi/loader/arch/arm64/ldscript.arm6485
-rw-r--r--stand/efi/loader/arch/arm64/start.S165
-rw-r--r--stand/efi/loader/arch/i386/Makefile.inc14
-rw-r--r--stand/efi/loader/arch/i386/bootinfo.c275
-rw-r--r--stand/efi/loader/arch/i386/efimd.c142
-rw-r--r--stand/efi/loader/arch/i386/elf32_freebsd.c100
-rw-r--r--stand/efi/loader/arch/i386/exec.c49
-rw-r--r--stand/efi/loader/arch/i386/i386_copy.c59
-rw-r--r--stand/efi/loader/arch/i386/ldscript.i38677
-rw-r--r--stand/efi/loader/arch/i386/start.S68
-rw-r--r--stand/efi/loader/autoload.c37
-rw-r--r--stand/efi/loader/bootinfo.c471
-rw-r--r--stand/efi/loader/conf.c83
-rw-r--r--stand/efi/loader/copy.c287
-rw-r--r--stand/efi/loader/efi_main.c188
-rw-r--r--stand/efi/loader/framebuffer.c568
-rw-r--r--stand/efi/loader/framebuffer.h36
-rw-r--r--stand/efi/loader/loader_efi.h47
-rw-r--r--stand/efi/loader/main.c936
-rw-r--r--stand/efi/loader/version7
35 files changed, 5371 insertions, 0 deletions
diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile
new file mode 100644
index 000000000000..a3a23c87dfe7
--- /dev/null
+++ b/stand/efi/loader/Makefile
@@ -0,0 +1,131 @@
+# $FreeBSD$
+
+MAN=
+
+LOADER_NET_SUPPORT?= yes
+LOADER_MSDOS_SUPPORT?= yes
+LOADER_UFS_SUPPORT?= yes
+LOADER_CD9660_SUPPORT?= no
+LOADER_EXT2FS_SUPPORT?= no
+
+.include <bsd.init.mk>
+
+MK_SSP= no
+
+PROG= loader.sym
+INTERNALPROG=
+WARNS?= 3
+
+# architecture-specific loader code
+SRCS= autoload.c \
+ bootinfo.c \
+ conf.c \
+ copy.c \
+ efi_main.c \
+ framebuffer.c \
+ main.c \
+ self_reloc.c \
+ smbios.c \
+ vers.c
+
+.if ${MK_ZFS} != "no"
+LIBZFSBOOT= ${BOOTOBJ}/zfs/libzfsboot.a
+CFLAGS+= -I${ZFSSRC}
+CFLAGS+= -DEFI_ZFS_BOOT
+.endif
+
+.if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} > 40201
+CWARNFLAGS.self_reloc.c+= -Wno-error=maybe-uninitialized
+.endif
+
+# We implement a slightly non-standard %S in that it always takes a
+# CHAR16 that's common in UEFI-land instead of a wchar_t. This only
+# seems to matter on arm64 where wchar_t defaults to an int instead
+# of a short. There's no good cast to use here so just ignore the
+# warnings for now.
+CWARNFLAGS.main.c+= -Wno-format
+
+.PATH: ${.CURDIR}/arch/${MACHINE}
+# For smbios.c
+.PATH: ${BOOTSRC}/i386/libi386
+.include "${.CURDIR}/arch/${MACHINE}/Makefile.inc"
+
+CFLAGS+= -I${.CURDIR}
+CFLAGS+= -I${.CURDIR}/arch/${MACHINE}
+CFLAGS+= -I${EFISRC}/include
+CFLAGS+= -I${EFISRC}/include/${MACHINE}
+CFLAGS+= -I${SYSDIR}/contrib/dev/acpica/include
+CFLAGS+= -I${BOOTSRC}/i386/libi386
+CFLAGS+= -DNO_PCI -DEFI
+
+.if !defined(BOOT_HIDE_SERIAL_NUMBERS)
+# Export serial numbers, UUID, and asset tag from loader.
+CFLAGS+= -DSMBIOS_SERIAL_NUMBERS
+.if defined(BOOT_LITTLE_ENDIAN_UUID)
+# Use little-endian UUID format as defined in SMBIOS 2.6.
+CFLAGS+= -DSMBIOS_LITTLE_ENDIAN_UUID
+.elif defined(BOOT_NETWORK_ENDIAN_UUID)
+# Use network-endian UUID format for backward compatibility.
+CFLAGS+= -DSMBIOS_NETWORK_ENDIAN_UUID
+.endif
+.endif
+
+.if defined(HAVE_FDT) && ${MK_FDT} != "no"
+.include "${BOOTSRC}/fdt.mk"
+LIBEFI_FDT= ${BOOTOBJ}/efi/fdt/libefi_fdt.a
+.endif
+
+# Include bcache code.
+HAVE_BCACHE= yes
+
+.if defined(EFI_STAGING_SIZE)
+CFLAGS+= -DEFI_STAGING_SIZE=${EFI_STAGING_SIZE}
+.endif
+
+# Always add MI sources
+HELP_FILES=
+.include "${BOOTSRC}/loader.mk"
+
+FILES+= loader.efi
+FILESMODE_loader.efi= ${BINMODE}
+
+LDSCRIPT= ${.CURDIR}/arch/${MACHINE}/ldscript.${MACHINE}
+LDFLAGS+= -Wl,-T${LDSCRIPT},-Bsymbolic,-znotext -shared
+
+CLEANFILES+= loader.efi
+
+NEWVERSWHAT= "EFI loader" ${MACHINE}
+
+NM?= nm
+OBJCOPY?= objcopy
+
+.if ${MACHINE_CPUARCH} == "amd64"
+EFI_TARGET= efi-app-x86_64
+.elif ${MACHINE_CPUARCH} == "i386"
+EFI_TARGET= efi-app-ia32
+.else
+EFI_TARGET= binary
+.endif
+
+# Arbitrarily set the PE/COFF header timestamps to 1 Jan 2016 00:00:00
+# for build reproducibility.
+SOURCE_DATE_EPOCH?=1451606400
+loader.efi: ${PROG}
+ if ${NM} ${.ALLSRC} | grep ' U '; then \
+ echo "Undefined symbols in ${.ALLSRC}"; \
+ exit 1; \
+ fi
+ SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \
+ ${OBJCOPY} -j .peheader -j .text -j .sdata -j .data \
+ -j .dynamic -j .dynsym -j .rel.dyn \
+ -j .rela.dyn -j .reloc -j .eh_frame -j set_Xcommand_set \
+ -j set_Xficl_compile_set \
+ --output-target=${EFI_TARGET} ${.ALLSRC} ${.TARGET}
+
+LIBEFI= ${BOOTOBJ}/efi/libefi/libefi.a
+
+DPADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBZFSBOOT} ${LIBSA} \
+ ${LDSCRIPT}
+LDADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBZFSBOOT} ${LIBSA}
+
+.include <bsd.prog.mk>
diff --git a/stand/efi/loader/Makefile.depend b/stand/efi/loader/Makefile.depend
new file mode 100644
index 000000000000..657c40116b4d
--- /dev/null
+++ b/stand/efi/loader/Makefile.depend
@@ -0,0 +1,17 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ stand/efi/libefi \
+ stand/ficl \
+ stand/libsa \
+ stand/zfs \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/efi/loader/arch/amd64/Makefile.inc b/stand/efi/loader/arch/amd64/Makefile.inc
new file mode 100644
index 000000000000..b6d824ce57e4
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/Makefile.inc
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+SRCS+= amd64_tramp.S \
+ start.S \
+ elf64_freebsd.c \
+ trap.c \
+ exc.S
+
+.PATH: ${BOOTSRC}/i386/libi386
+SRCS+= nullconsole.c \
+ comconsole.c \
+ spinconsole.c
+
+CFLAGS+= -fPIC -DTERM_EMU
+LDFLAGS+= -Wl,-znocombreloc
diff --git a/stand/efi/loader/arch/amd64/amd64_tramp.S b/stand/efi/loader/arch/amd64/amd64_tramp.S
new file mode 100644
index 000000000000..c102d9243589
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/amd64_tramp.S
@@ -0,0 +1,64 @@
+/*-
+ * 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>
+
+ .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:
+
+ .data
+ .globl amd64_tramp_size
+amd64_tramp_size:
+ .long amd64_tramp_end-amd64_tramp
diff --git a/stand/efi/loader/arch/amd64/elf64_freebsd.c b/stand/efi/loader/arch/amd64/elf64_freebsd.c
new file mode 100644
index 000000000000..37e9fe1b4b18
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/elf64_freebsd.c
@@ -0,0 +1,208 @@
+/*-
+ * 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>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+
+#include "platform/acfreebsd.h"
+#include "acconfig.h"
+#define ACPI_SYSTEM_XFACE
+#include "actypes.h"
+#include "actbl.h"
+
+#include "loader_efi.h"
+
+static EFI_GUID acpi_guid = ACPI_TABLE_GUID;
+static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID;
+
+extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
+
+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,
+};
+
+struct file_format *file_formats[] = {
+ &amd64_elf,
+ &amd64_elf_obj,
+ NULL
+};
+
+static pml4_entry_t *PT4;
+static pdp_entry_t *PT3;
+static pd_entry_t *PT2;
+
+static void (*trampoline)(uint64_t stack, void *copy_finish, uint64_t kernend,
+ uint64_t modulep, pml4_entry_t *pagetable, uint64_t entry);
+
+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)
+{
+ 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;
+
+ /*
+ * 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.
+ */
+
+ rsdp = efi_get_table(&acpi20_guid);
+ if (rsdp == NULL) {
+ rsdp = efi_get_table(&acpi_guid);
+ }
+ 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 = (vm_offset_t)0x0000000040000000;
+ err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 1,
+ (EFI_PHYSICAL_ADDRESS *)&trampcode);
+ bzero((void *)trampcode, EFI_PAGE_SIZE);
+ trampstack = trampcode + EFI_PAGE_SIZE - 8;
+ bcopy((void *)&amd64_tramp, (void *)trampcode, amd64_tramp_size);
+ trampoline = (void *)trampcode;
+
+ PT4 = (pml4_entry_t *)0x0000000040000000;
+ err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 3,
+ (EFI_PHYSICAL_ADDRESS *)&PT4);
+ bzero(PT4, 3 * EFI_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 < 512; 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 | PG_U;
+
+ /* Each slot of the L3 pages points to the same L2 page. */
+ PT3[i] = (pdp_entry_t)PT2;
+ PT3[i] |= PG_V | PG_RW | PG_U;
+
+ /* The L2 page slots are mapped with 2MB pages for 1GB. */
+ PT2[i] = i * (2 * 1024 * 1024);
+ PT2[i] |= PG_V | PG_RW | PG_PS | PG_U;
+ }
+
+ printf("Start @ 0x%lx ...\n", ehdr->e_entry);
+
+ efi_time_fini();
+ err = bi_load(fp->f_args, &modulep, &kernend);
+ if (err != 0) {
+ efi_time_init();
+ return(err);
+ }
+
+ dev_cleanup();
+
+ trampoline(trampstack, efi_copy_finish, kernend, modulep, PT4,
+ ehdr->e_entry);
+
+ panic("exec returned");
+}
+
+static int
+elf64_obj_exec(struct preloaded_file *fp)
+{
+
+ return (EFTYPE);
+}
diff --git a/stand/efi/loader/arch/amd64/exc.S b/stand/efi/loader/arch/amd64/exc.S
new file mode 100644
index 000000000000..0035d4a37e20
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/exc.S
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Konstantin Belousov 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$
+ */
+
+ .macro EH N, err=1
+ .align 8
+ .globl EXC\N\()_handler
+EXC\N\()_handler:
+ .if \err != 1
+ pushq $0
+ .endif
+ pushq %rax
+ pushq %rdx
+ pushq %rcx
+ movl $\N,%ecx
+ jmp all_handlers
+ .endm
+
+ .text
+ EH 0,0
+ EH 1,0
+ EH 2,0
+ EH 3,0
+ EH 4,0
+ EH 5,0
+ EH 6,0
+ EH 7,0
+ EH 8
+ EH 9,0
+ EH 10
+ EH 11
+ EH 12
+ EH 13
+ EH 14
+ EH 16,0
+ EH 17
+ EH 18,0
+ EH 19,0
+ EH 20,0
+
+ .globl exc_rsp
+all_handlers:
+ cmpq %rsp,exc_rsp(%rip)
+ je exception
+
+ /*
+ * Interrupt, not exception.
+ * First, copy the hardware interrupt frame to the previous stack.
+ * Our handler always has private IST stack.
+ */
+ movq (6*8)(%rsp),%rax /* saved %rsp value, AKA old stack */
+ subq (5*8),%rax
+ movq (3*8)(%rsp),%rdx /* copy %rip to old stack */
+ movq %rdx,(%rax)
+ movq (4*8)(%rsp),%rdx /* copy %cs */
+ movq %rdx,(1*8)(%rax)
+ movq (5*8)(%rsp),%rdx /* copy %rflags */
+ movq %rdx,(2*8)(%rax)
+ movq (6*8)(%rsp),%rdx /* copy %rsp */
+ movq %rdx,(3*8)(%rax)
+ movq (7*8)(%rsp),%rdx /* copy %ss */
+ movq %rdx,(4*8)(%rax)
+
+ /*
+ * Now simulate invocation of the original interrupt handler
+ * with retq. We switch stacks and execute retq from the old
+ * stack since there is no free registers at the last moment.
+ */
+ subq $16,%rax
+ leaq fw_intr_handlers(%rip),%rdx
+ movq (%rdx,%rcx,8),%rdx /* push intr handler address on old stack */
+ movq %rdx,8(%rax)
+ movq (2*8)(%rsp),%rcx /* saved %rax is put on top of old stack */
+ movq %rcx,(%rax)
+ movq (%rsp),%rcx
+ movq 8(%rsp),%rdx
+
+ movq 32(%rsp),%rsp /* switch to old stack */
+ popq %rax
+ retq
+
+exception:
+ /*
+ * Form the struct trapframe on our IST stack.
+ * Skip three words, which are currently busy with temporal
+ * saves.
+ */
+ pushq %r15
+ pushq %r14
+ pushq %r13
+ pushq %r12
+ pushq %r11
+ pushq %r10
+ pushq %rbp
+ pushq %rbx
+ pushq $0 /* %rax */
+ pushq %r9
+ pushq %r8
+ pushq $0 /* %rcx */
+ pushq $0 /* %rdx */
+ pushq %rsi
+ pushq %rdi
+
+ /*
+ * Move %rax, %rdx, %rcx values into the final location,
+ * from the three words which were skipped above.
+ */
+ movq 0x88(%rsp),%rax
+ movq %rax,0x30(%rsp) /* tf_rax */
+ movq 0x78(%rsp),%rax
+ movq %rax,0x18(%rsp) /* tf_rcx */
+ movq 0x80(%rsp),%rax
+ movq %rax,0x10(%rsp) /* tf_rdx */
+
+ /*
+ * And fill the three words themself.
+ */
+ movq %cr2,%rax
+ movq %rax,0x80(%rsp) /* tf_addr */
+ movl %ecx,0x78(%rsp) /* tf_trapno */
+ movw %ds,0x8e(%rsp)
+ movw %es,0x8c(%rsp)
+ movw %fs,0x7c(%rsp)
+ movw %gs,0x7e(%rsp)
+ movw $0,0x88(%rsp) /* tf_flags */
+
+ /*
+ * Call dump routine.
+ */
+ movq %rsp,%rdi
+ callq report_exc
+
+ /*
+ * Hang after reporting. Interrupts are already disabled.
+ */
+1:
+ hlt
+ jmp 1b
diff --git a/stand/efi/loader/arch/amd64/ldscript.amd64 b/stand/efi/loader/arch/amd64/ldscript.amd64
new file mode 100644
index 000000000000..874df9b08f9e
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/ldscript.amd64
@@ -0,0 +1,72 @@
+/* $FreeBSD$ */
+OUTPUT_FORMAT("elf64-x86-64-freebsd", "elf64-x86-64-freebsd", "elf64-x86-64-freebsd")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0;
+ 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/efi/loader/arch/amd64/start.S b/stand/efi/loader/arch/amd64/start.S
new file mode 100644
index 000000000000..774ef4fa7901
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/start.S
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (C) 1999 Hewlett-Packard Co.
+ * Contributed by David Mosberger <davidm@hpl.hp.com>.
+ * Copyright (C) 2005 Intel Co.
+ * Contributed by Fenghua Yu <fenghua.yu@intel.com>.
+ * 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.
+ * 3. Neither the name of Hewlett-Packard Co. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
+ */
+
+/*
+ * crt0-efi-x86_64.S - x86_64 EFI startup code.
+ * $FreeBSD$
+ */
+
+ .text
+ .align 4
+
+ .globl _start
+_start:
+ subq $8, %rsp
+ pushq %rcx
+ pushq %rdx
+
+0:
+ lea ImageBase(%rip), %rdi
+ lea _DYNAMIC(%rip), %rsi
+
+ popq %rcx
+ popq %rdx
+ pushq %rcx
+ pushq %rdx
+ call self_reloc
+
+ popq %rdi
+ popq %rsi
+
+ call efi_main
+ addq $8, %rsp
+
+.exit:
+ ret
+
+ /*
+ * hand-craft a dummy .reloc section so EFI knows it's a relocatable
+ * executable:
+ */
+
+ .data
+ .section .reloc, "a"
+ .long 0
+ .long 10
+ .word 0
diff --git a/stand/efi/loader/arch/amd64/trap.c b/stand/efi/loader/arch/amd64/trap.c
new file mode 100644
index 000000000000..e8cf188cf22f
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/trap.c
@@ -0,0 +1,408 @@
+/*-
+ * Copyright (c) 2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Konstantin Belousov 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include <sys/param.h>
+#include <machine/cpufunc.h>
+#include <machine/psl.h>
+#include <machine/segments.h>
+#include <machine/frame.h>
+#include <machine/tss.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+#include "loader_efi.h"
+
+#define NUM_IST 8
+#define NUM_EXC 32
+
+/*
+ * This code catches exceptions but forwards hardware interrupts to
+ * handlers installed by firmware. It differentiates exceptions
+ * vs. interrupts by presence of the error code on the stack, which
+ * causes different stack pointer value on trap handler entry.
+ *
+ * Use kernel layout for the trapframe just to not be original.
+ *
+ * Use free IST slot in existing TSS, or create our own TSS if
+ * firmware did not configured any, to have stack switched to
+ * IST-specified one, e.g. to handle #SS. If hand-off cannot find
+ * unused IST slot, or create a new descriptor in GDT, we bail out.
+ */
+
+static struct region_descriptor fw_idt; /* Descriptor for pristine fw IDT */
+static struct region_descriptor loader_idt;/* Descriptor for loader
+ shadow IDT */
+static EFI_PHYSICAL_ADDRESS lidt_pa; /* Address of loader shadow IDT */
+static EFI_PHYSICAL_ADDRESS tss_pa; /* Address of TSS */
+static EFI_PHYSICAL_ADDRESS exc_stack_pa;/* Address of IST stack for loader */
+EFI_PHYSICAL_ADDRESS exc_rsp; /* %rsp value on our IST stack when
+ exception happens */
+EFI_PHYSICAL_ADDRESS fw_intr_handlers[NUM_EXC]; /* fw handlers for < 32 IDT
+ vectors */
+static int intercepted[NUM_EXC];
+static int ist; /* IST for exception handlers */
+static uint32_t tss_fw_seg; /* Fw TSS segment */
+static uint32_t loader_tss; /* Loader TSS segment */
+static struct region_descriptor fw_gdt; /* Descriptor of pristine GDT */
+static EFI_PHYSICAL_ADDRESS loader_gdt_pa; /* Address of loader shadow GDT */
+
+void report_exc(struct trapframe *tf);
+void
+report_exc(struct trapframe *tf)
+{
+
+ /*
+ * printf() depends on loader runtime and UEFI firmware health
+ * to produce the console output, in case of exception, the
+ * loader or firmware runtime may fail to support the printf().
+ */
+ printf("===================================================="
+ "============================\n");
+ printf("Exception %u\n", tf->tf_trapno);
+ printf("ss 0x%04hx cs 0x%04hx ds 0x%04hx es 0x%04hx fs 0x%04hx "
+ "gs 0x%04hx\n",
+ (uint16_t)tf->tf_ss, (uint16_t)tf->tf_cs, (uint16_t)tf->tf_ds,
+ (uint16_t)tf->tf_es, (uint16_t)tf->tf_fs, (uint16_t)tf->tf_gs);
+ printf("err 0x%08x rfl 0x%08x addr 0x%016lx\n"
+ "rsp 0x%016lx rip 0x%016lx\n",
+ (uint32_t)tf->tf_err, (uint32_t)tf->tf_rflags, tf->tf_addr,
+ tf->tf_rsp, tf->tf_rip);
+ printf(
+ "rdi 0x%016lx rsi 0x%016lx rdx 0x%016lx\n"
+ "rcx 0x%016lx r8 0x%016lx r9 0x%016lx\n"
+ "rax 0x%016lx rbx 0x%016lx rbp 0x%016lx\n"
+ "r10 0x%016lx r11 0x%016lx r12 0x%016lx\n"
+ "r13 0x%016lx r14 0x%016lx r15 0x%016lx\n",
+ tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_rcx, tf->tf_r8,
+ tf->tf_r9, tf->tf_rax, tf->tf_rbx, tf->tf_rbp, tf->tf_r10,
+ tf->tf_r11, tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15);
+ printf("Machine stopped.\n");
+}
+
+static void
+prepare_exception(unsigned idx, uint64_t my_handler,
+ int ist_use_table[static NUM_IST])
+{
+ struct gate_descriptor *fw_idt_e, *loader_idt_e;
+
+ fw_idt_e = &((struct gate_descriptor *)fw_idt.rd_base)[idx];
+ loader_idt_e = &((struct gate_descriptor *)loader_idt.rd_base)[idx];
+ fw_intr_handlers[idx] = fw_idt_e->gd_looffset +
+ (fw_idt_e->gd_hioffset << 16);
+ intercepted[idx] = 1;
+ ist_use_table[fw_idt_e->gd_ist]++;
+ loader_idt_e->gd_looffset = my_handler;
+ loader_idt_e->gd_hioffset = my_handler >> 16;
+ /*
+ * We reuse uefi selector for the code segment for the exception
+ * handler code, while the reason for the fault might be the
+ * corruption of that gdt entry. On the other hand, allocating
+ * our own descriptor might be not much better, if gdt is corrupted.
+ */
+ loader_idt_e->gd_selector = fw_idt_e->gd_selector;
+ loader_idt_e->gd_ist = 0;
+ loader_idt_e->gd_type = SDT_SYSIGT;
+ loader_idt_e->gd_dpl = 0;
+ loader_idt_e->gd_p = 1;
+ loader_idt_e->gd_xx = 0;
+ loader_idt_e->sd_xx1 = 0;
+}
+#define PREPARE_EXCEPTION(N) \
+ extern char EXC##N##_handler[]; \
+ prepare_exception(N, (uintptr_t)EXC##N##_handler, ist_use_table);
+
+static void
+free_tables(void)
+{
+
+ if (lidt_pa != 0) {
+ BS->FreePages(lidt_pa, EFI_SIZE_TO_PAGES(fw_idt.rd_limit));
+ lidt_pa = 0;
+ }
+ if (exc_stack_pa != 0) {
+ BS->FreePages(exc_stack_pa, 1);
+ exc_stack_pa = 0;
+ }
+ if (tss_pa != 0 && tss_fw_seg == 0) {
+ BS->FreePages(tss_pa, EFI_SIZE_TO_PAGES(sizeof(struct
+ amd64tss)));
+ tss_pa = 0;
+ }
+ if (loader_gdt_pa != 0) {
+ BS->FreePages(tss_pa, 2);
+ loader_gdt_pa = 0;
+ }
+ ist = 0;
+ loader_tss = 0;
+}
+
+static int
+efi_setup_tss(struct region_descriptor *gdt, uint32_t loader_tss_idx,
+ struct amd64tss **tss)
+{
+ EFI_STATUS status;
+ struct system_segment_descriptor *tss_desc;
+
+ tss_desc = (struct system_segment_descriptor *)(gdt->rd_base +
+ (loader_tss_idx << 3));
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+ EFI_SIZE_TO_PAGES(sizeof(struct amd64tss)), &tss_pa);
+ if (EFI_ERROR(status)) {
+ printf("efi_setup_tss: AllocatePages tss error %lu\n",
+ EFI_ERROR_CODE(status));
+ return (0);
+ }
+ *tss = (struct amd64tss *)tss_pa;
+ bzero(*tss, sizeof(**tss));
+ tss_desc->sd_lolimit = sizeof(struct amd64tss);
+ tss_desc->sd_lobase = tss_pa;
+ tss_desc->sd_type = SDT_SYSTSS;
+ tss_desc->sd_dpl = 0;
+ tss_desc->sd_p = 1;
+ tss_desc->sd_hilimit = sizeof(struct amd64tss) >> 16;
+ tss_desc->sd_gran = 0;
+ tss_desc->sd_hibase = tss_pa >> 24;
+ tss_desc->sd_xx0 = 0;
+ tss_desc->sd_xx1 = 0;
+ tss_desc->sd_mbz = 0;
+ tss_desc->sd_xx2 = 0;
+ return (1);
+}
+
+static int
+efi_redirect_exceptions(void)
+{
+ int ist_use_table[NUM_IST];
+ struct gate_descriptor *loader_idt_e;
+ struct system_segment_descriptor *tss_desc, *gdt_desc;
+ struct amd64tss *tss;
+ struct region_descriptor *gdt_rd, loader_gdt;
+ uint32_t i;
+ EFI_STATUS status;
+ register_t rfl;
+
+ sidt(&fw_idt);
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+ EFI_SIZE_TO_PAGES(fw_idt.rd_limit), &lidt_pa);
+ if (EFI_ERROR(status)) {
+ printf("efi_redirect_exceptions: AllocatePages IDT error %lu\n",
+ EFI_ERROR_CODE(status));
+ lidt_pa = 0;
+ return (0);
+ }
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 1,
+ &exc_stack_pa);
+ if (EFI_ERROR(status)) {
+ printf("efi_redirect_exceptions: AllocatePages stk error %lu\n",
+ EFI_ERROR_CODE(status));
+ exc_stack_pa = 0;
+ free_tables();
+ return (0);
+ }
+ loader_idt.rd_limit = fw_idt.rd_limit;
+ bcopy((void *)fw_idt.rd_base, (void *)loader_idt.rd_base,
+ loader_idt.rd_limit);
+ bzero(ist_use_table, sizeof(ist_use_table));
+ bzero(fw_intr_handlers, sizeof(fw_intr_handlers));
+ bzero(intercepted, sizeof(intercepted));
+
+ sgdt(&fw_gdt);
+ tss_fw_seg = read_tr();
+ gdt_rd = NULL;
+ if (tss_fw_seg == 0) {
+ for (i = 2; (i << 3) + sizeof(*gdt_desc) <= fw_gdt.rd_limit;
+ i += 2) {
+ gdt_desc = (struct system_segment_descriptor *)(
+ fw_gdt.rd_base + (i << 3));
+ if (gdt_desc->sd_type == 0 && gdt_desc->sd_mbz == 0) {
+ gdt_rd = &fw_gdt;
+ break;
+ }
+ }
+ if (gdt_rd == NULL) {
+ if (i >= 8190) {
+ printf("efi_redirect_exceptions: all slots "
+ "in gdt are used\n");
+ free_tables();
+ return (0);
+ }
+ loader_gdt.rd_limit = roundup2(fw_gdt.rd_limit +
+ sizeof(struct system_segment_descriptor),
+ sizeof(struct system_segment_descriptor)) - 1;
+ i = (loader_gdt.rd_limit + 1 -
+ sizeof(struct system_segment_descriptor)) /
+ sizeof(struct system_segment_descriptor) * 2;
+ status = BS->AllocatePages(AllocateAnyPages,
+ EfiLoaderData,
+ EFI_SIZE_TO_PAGES(loader_gdt.rd_limit),
+ &loader_gdt_pa);
+ if (EFI_ERROR(status)) {
+ printf("efi_setup_tss: AllocatePages gdt error "
+ "%lu\n", EFI_ERROR_CODE(status));
+ loader_gdt_pa = 0;
+ free_tables();
+ return (0);
+ }
+ loader_gdt.rd_base = loader_gdt_pa;
+ bzero((void *)loader_gdt.rd_base, loader_gdt.rd_limit);
+ bcopy((void *)fw_gdt.rd_base,
+ (void *)loader_gdt.rd_base, fw_gdt.rd_limit);
+ gdt_rd = &loader_gdt;
+ }
+ loader_tss = i << 3;
+ if (!efi_setup_tss(gdt_rd, i, &tss)) {
+ tss_pa = 0;
+ free_tables();
+ return (0);
+ }
+ } else {
+ tss_desc = (struct system_segment_descriptor *)((char *)
+ fw_gdt.rd_base + tss_fw_seg);
+ if (tss_desc->sd_type != SDT_SYSTSS &&
+ tss_desc->sd_type != SDT_SYSBSY) {
+ printf("LTR points to non-TSS descriptor\n");
+ free_tables();
+ return (0);
+ }
+ tss_pa = tss_desc->sd_lobase + (tss_desc->sd_hibase << 16);
+ tss = (struct amd64tss *)tss_pa;
+ tss_desc->sd_type = SDT_SYSTSS; /* unbusy */
+ }
+
+ PREPARE_EXCEPTION(0);
+ PREPARE_EXCEPTION(1);
+ PREPARE_EXCEPTION(2);
+ PREPARE_EXCEPTION(3);
+ PREPARE_EXCEPTION(4);
+ PREPARE_EXCEPTION(5);
+ PREPARE_EXCEPTION(6);
+ PREPARE_EXCEPTION(7);
+ PREPARE_EXCEPTION(8);
+ PREPARE_EXCEPTION(9);
+ PREPARE_EXCEPTION(10);
+ PREPARE_EXCEPTION(11);
+ PREPARE_EXCEPTION(12);
+ PREPARE_EXCEPTION(13);
+ PREPARE_EXCEPTION(14);
+ PREPARE_EXCEPTION(16);
+ PREPARE_EXCEPTION(17);
+ PREPARE_EXCEPTION(18);
+ PREPARE_EXCEPTION(19);
+ PREPARE_EXCEPTION(20);
+
+ exc_rsp = exc_stack_pa + PAGE_SIZE -
+ (6 /* hw exception frame */ + 3 /* scratch regs */) * 8;
+
+ /* Find free IST and use it */
+ for (ist = 1; ist < NUM_IST; ist++) {
+ if (ist_use_table[ist] == 0)
+ break;
+ }
+ if (ist == NUM_IST) {
+ printf("efi_redirect_exceptions: all ISTs used\n");
+ free_tables();
+ lidt_pa = 0;
+ return (0);
+ }
+ for (i = 0; i < NUM_EXC; i++) {
+ loader_idt_e = &((struct gate_descriptor *)loader_idt.
+ rd_base)[i];
+ if (intercepted[i])
+ loader_idt_e->gd_ist = ist;
+ }
+ (&(tss->tss_ist1))[ist - 1] = exc_stack_pa + PAGE_SIZE;
+
+ /* Switch to new IDT */
+ rfl = intr_disable();
+ if (loader_gdt_pa != 0)
+ bare_lgdt(&loader_gdt);
+ if (loader_tss != 0)
+ ltr(loader_tss);
+ lidt(&loader_idt);
+ intr_restore(rfl);
+ return (1);
+}
+
+static void
+efi_unredirect_exceptions(void)
+{
+ register_t rfl;
+
+ if (lidt_pa == 0)
+ return;
+
+ rfl = intr_disable();
+ if (ist != 0)
+ (&(((struct amd64tss *)tss_pa)->tss_ist1))[ist - 1] = 0;
+ if (loader_gdt_pa != 0)
+ bare_lgdt(&fw_gdt);
+ if (loader_tss != 0)
+ ltr(tss_fw_seg);
+ lidt(&fw_idt);
+ intr_restore(rfl);
+ free_tables();
+}
+
+static int
+command_grab_faults(int argc, char *argv[])
+{
+ int res;
+
+ res = efi_redirect_exceptions();
+ if (!res)
+ printf("failed\n");
+ return (CMD_OK);
+}
+COMMAND_SET(grap_faults, "grab_faults", "grab faults", command_grab_faults);
+
+static int
+command_ungrab_faults(int argc, char *argv[])
+{
+
+ efi_unredirect_exceptions();
+ return (CMD_OK);
+}
+COMMAND_SET(ungrab_faults, "ungrab_faults", "ungrab faults",
+ command_ungrab_faults);
+
+static int
+command_fault(int argc, char *argv[])
+{
+
+ __asm("ud2");
+ return (CMD_OK);
+}
+COMMAND_SET(fault, "fault", "generate fault", command_fault);
diff --git a/stand/efi/loader/arch/arm/Makefile.inc b/stand/efi/loader/arch/arm/Makefile.inc
new file mode 100644
index 000000000000..74e6616e991b
--- /dev/null
+++ b/stand/efi/loader/arch/arm/Makefile.inc
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SRCS+= exec.c \
+ start.S
+
+HAVE_FDT=yes
diff --git a/stand/efi/loader/arch/arm/exec.c b/stand/efi/loader/arch/arm/exec.c
new file mode 100644
index 000000000000..83d3f2b1140a
--- /dev/null
+++ b/stand/efi/loader/arch/arm/exec.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 2001 Benno Rice <benno@FreeBSD.org>
+ * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 <sys/param.h>
+#include <sys/linker.h>
+
+#include <machine/md_var.h>
+#include <machine/metadata.h>
+#include <machine/elf.h>
+
+#include <stand.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+#include "loader_efi.h"
+
+extern vm_offset_t md_load(char *, vm_offset_t *);
+extern int bi_load(char *, vm_offset_t *, vm_offset_t *);
+
+static int
+__elfN(arm_load)(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ int r;
+
+ r = __elfN(loadfile)(filename, dest, result);
+ if (r != 0)
+ return (r);
+
+ return (0);
+}
+
+static int
+__elfN(arm_exec)(struct preloaded_file *fp)
+{
+ struct file_metadata *fmp;
+ vm_offset_t modulep, kernend;
+ Elf_Ehdr *e;
+ int error;
+ void (*entry)(void *);
+
+ if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return (EFTYPE);
+
+ e = (Elf_Ehdr *)&fmp->md_data;
+
+ efi_time_fini();
+ if ((error = bi_load(fp->f_args, &modulep, &kernend)) != 0) {
+ efi_time_init();
+ return (error);
+ }
+
+ entry = efi_translate(e->e_entry);
+ printf("Kernel entry at 0x%x...\n", (unsigned)entry);
+ printf("Kernel args: %s\n", fp->f_args);
+ printf("modulep: %#x\n", modulep);
+ printf("relocation_offset %llx\n", __elfN(relocation_offset));
+
+ dev_cleanup();
+
+ (*entry)((void *)modulep);
+ panic("exec returned");
+}
+
+static struct file_format arm_elf = {
+ __elfN(arm_load),
+ __elfN(arm_exec)
+};
+
+struct file_format *file_formats[] = {
+ &arm_elf,
+ NULL
+};
+
diff --git a/stand/efi/loader/arch/arm/ldscript.arm b/stand/efi/loader/arch/arm/ldscript.arm
new file mode 100644
index 000000000000..9f6e7c29117e
--- /dev/null
+++ b/stand/efi/loader/arch/arm/ldscript.arm
@@ -0,0 +1,67 @@
+/* $FreeBSD$ */
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0;
+ ImageBase = .;
+ .text : {
+ *(.peheader)
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ PROVIDE (etext = .);
+ . = ALIGN(16);
+ .data :
+ {
+ *(.data .data.*)
+ *(.gnu.linkonce.d*)
+ *(.rodata)
+ *(.rodata.*)
+ CONSTRUCTORS
+
+ . = ALIGN(4);
+ PROVIDE (__bss_start = .);
+ *(.sbss)
+ *(.scommon)
+ *(.dynsbss)
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(4);
+ PROVIDE (__bss_end = .);
+ }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : {
+ *(.got.plt .got)
+ *(.sdata*.sdata.* .gnu.linkonce.s.*)
+ }
+ 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 = .;
+ }
+ __gp = .;
+ .plt : { *(.plt) }
+ .dynamic : { *(.dynamic) }
+ .reloc : { *(.reloc) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .rel.dyn : {
+ *(.rel.*)
+ *(.relset_*)
+ }
+ _edata = .;
+ .hash : { *(.hash) }
+}
diff --git a/stand/efi/loader/arch/arm/start.S b/stand/efi/loader/arch/arm/start.S
new file mode 100644
index 000000000000..5b6182d67d0a
--- /dev/null
+++ b/stand/efi/loader/arch/arm/start.S
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 2014, 2015 Andrew Turner
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+/*
+ * We need to be a PE32 file for EFI. On some architectures we can use
+ * objcopy to create the correct file, however on arm we need to do
+ * it ourselves.
+ */
+
+#define IMAGE_FILE_MACHINE_ARM 0x01c2
+
+#define IMAGE_SCN_CNT_CODE 0x00000020
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000
+#define IMAGE_SCN_MEM_READ 0x40000000
+
+ .section .peheader,"a"
+efi_start:
+ /* The MS-DOS Stub, only used to get the offset of the COFF header */
+ .ascii "MZ"
+ .short 0
+ .space 0x38
+ .long pe_sig - efi_start
+
+ /* The PE32 Signature. Needs to be 8-byte aligned */
+ .align 3
+pe_sig:
+ .ascii "PE"
+ .short 0
+coff_head:
+ .short IMAGE_FILE_MACHINE_ARM /* ARM file */
+ .short 2 /* 2 Sections */
+ .long 0 /* Timestamp */
+ .long 0 /* No symbol table */
+ .long 0 /* No symbols */
+ .short section_table - optional_header /* Optional header size */
+ .short 0 /* Characteristics TODO: Fill in */
+
+optional_header:
+ .short 0x010b /* PE32 (32-bit addressing) */
+ .byte 0 /* Major linker version */
+ .byte 0 /* Minor linker version */
+ .long _edata - _end_header /* Code size */
+ .long 0 /* No initialized data */
+ .long 0 /* No uninitialized data */
+ .long _start - efi_start /* Entry point */
+ .long _end_header - efi_start /* Start of code */
+ .long 0 /* Start of data */
+
+optional_windows_header:
+ .long 0 /* Image base */
+ .long 32 /* Section Alignment */
+ .long 8 /* File alignment */
+ .short 0 /* Major OS version */
+ .short 0 /* Minor OS version */
+ .short 0 /* Major image version */
+ .short 0 /* Minor image version */
+ .short 0 /* Major subsystem version */
+ .short 0 /* Minor subsystem version */
+ .long 0 /* Win32 version */
+ .long _edata - efi_start /* Image size */
+ .long _end_header - efi_start /* Header size */
+ .long 0 /* Checksum */
+ .short 0xa /* Subsystem (EFI app) */
+ .short 0 /* DLL Characteristics */
+ .long 0 /* Stack reserve */
+ .long 0 /* Stack commit */
+ .long 0 /* Heap reserve */
+ .long 0 /* Heap commit */
+ .long 0 /* Loader flags */
+ .long 6 /* Number of RVAs */
+
+ /* RVAs: */
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+
+section_table:
+ /* We need a .reloc section for EFI */
+ .ascii ".reloc"
+ .byte 0
+ .byte 0 /* Pad to 8 bytes */
+ .long 0 /* Virtual size */
+ .long 0 /* Virtual address */
+ .long 0 /* Size of raw data */
+ .long 0 /* Pointer to raw data */
+ .long 0 /* Pointer to relocations */
+ .long 0 /* Pointer to line numbers */
+ .short 0 /* Number of relocations */
+ .short 0 /* Number of line numbers */
+ .long (IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | \
+ IMAGE_SCN_MEM_DISCARDABLE) /* Characteristics */
+
+ /* The contents of the loader */
+ .ascii ".text"
+ .byte 0
+ .byte 0
+ .byte 0 /* Pad to 8 bytes */
+ .long _edata - _end_header /* Virtual size */
+ .long _end_header - efi_start /* Virtual address */
+ .long _edata - _end_header /* Size of raw data */
+ .long _end_header - efi_start /* Pointer to raw data */
+ .long 0 /* Pointer to relocations */
+ .long 0 /* Pointer to line numbers */
+ .short 0 /* Number of relocations */
+ .short 0 /* Number of line numbers */
+ .long (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | \
+ IMAGE_SCN_MEM_READ) /* Characteristics */
+_end_header:
+
+ .text
+_start:
+ /* Save the boot params to the stack */
+ push {r0, r1}
+
+ adr r0, .Lbase
+ ldr r1, [r0]
+ sub r5, r0, r1
+
+ ldr r0, .Limagebase
+ add r0, r0, r5
+ ldr r1, .Ldynamic
+ add r1, r1, r5
+
+ bl _C_LABEL(self_reloc)
+
+ /* Zero the BSS, _reloc fixed the values for us */
+ ldr r0, .Lbss
+ ldr r1, .Lbssend
+ mov r2, #0
+
+1: cmp r0, r1
+ bge 2f
+ str r2, [r0], #4
+ b 1b
+2:
+
+ pop {r0, r1}
+ bl _C_LABEL(efi_main)
+
+1: b 1b
+
+.Lbase:
+ .word .
+.Limagebase:
+ .word ImageBase
+.Ldynamic:
+ .word _DYNAMIC
+.Lbss:
+ .word __bss_start
+.Lbssend:
+ .word __bss_end
+
+.align 3
+stack:
+ .space 512
+stack_end:
+
diff --git a/stand/efi/loader/arch/arm64/Makefile.inc b/stand/efi/loader/arch/arm64/Makefile.inc
new file mode 100644
index 000000000000..a71bcc2e1a1f
--- /dev/null
+++ b/stand/efi/loader/arch/arm64/Makefile.inc
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+HAVE_FDT=yes
+
+SRCS+= exec.c \
+ start.S
+
+.PATH: ${BOOTSRC}/arm64/libarm64
+CFLAGS+=-I${BOOTSRC}/arm64/libarm64
+SRCS+= cache.c
+
+CFLAGS+= -mgeneral-regs-only
diff --git a/stand/efi/loader/arch/arm64/exec.c b/stand/efi/loader/arch/arm64/exec.c
new file mode 100644
index 000000000000..45321261def0
--- /dev/null
+++ b/stand/efi/loader/arch/arm64/exec.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 ``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 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 <string.h>
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <machine/elf.h>
+
+#include <bootstrap.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "loader_efi.h"
+#include "cache.h"
+
+#include "platform/acfreebsd.h"
+#include "acconfig.h"
+#define ACPI_SYSTEM_XFACE
+#define ACPI_USE_SYSTEM_INTTYPES
+#include "actypes.h"
+#include "actbl.h"
+
+static EFI_GUID acpi_guid = ACPI_TABLE_GUID;
+static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID;
+
+static int elf64_exec(struct preloaded_file *amp);
+static int elf64_obj_exec(struct preloaded_file *amp);
+
+int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
+
+static struct file_format arm64_elf = {
+ elf64_loadfile,
+ elf64_exec
+};
+
+struct file_format *file_formats[] = {
+ &arm64_elf,
+ NULL
+};
+
+static int
+elf64_exec(struct preloaded_file *fp)
+{
+ vm_offset_t modulep, kernendp;
+ vm_offset_t clean_addr;
+ size_t clean_size;
+ struct file_metadata *md;
+ ACPI_TABLE_RSDP *rsdp;
+ Elf_Ehdr *ehdr;
+ char buf[24];
+ int err, revision;
+ void (*entry)(vm_offset_t);
+
+ rsdp = efi_get_table(&acpi20_guid);
+ if (rsdp == NULL) {
+ rsdp = efi_get_table(&acpi_guid);
+ }
+ if (rsdp != NULL) {
+ sprintf(buf, "0x%016llx", (unsigned long long)rsdp);
+ setenv("hint.acpi.0.rsdp", buf, 1);
+ revision = rsdp->Revision;
+ if (revision == 0)
+ revision = 1;
+ sprintf(buf, "%d", revision);
+ setenv("hint.acpi.0.revision", buf, 1);
+ strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId));
+ buf[sizeof(rsdp->OemId)] = '\0';
+ setenv("hint.acpi.0.oem", buf, 1);
+ sprintf(buf, "0x%016x", rsdp->RsdtPhysicalAddress);
+ setenv("hint.acpi.0.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);
+ sprintf(buf, "%d", rsdp->Length);
+ setenv("hint.acpi.0.xsdt_length", buf, 1);
+ }
+ }
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE);
+
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+ entry = efi_translate(ehdr->e_entry);
+
+ efi_time_fini();
+ err = bi_load(fp->f_args, &modulep, &kernendp);
+ if (err != 0) {
+ efi_time_init();
+ return (err);
+ }
+
+ dev_cleanup();
+
+ /* Clean D-cache under kernel area and invalidate whole I-cache */
+ clean_addr = (vm_offset_t)efi_translate(fp->f_addr);
+ clean_size = (vm_offset_t)efi_translate(kernendp) - clean_addr;
+
+ cpu_flush_dcache((void *)clean_addr, clean_size);
+ cpu_inval_icache(NULL, 0);
+
+ (*entry)(modulep);
+ panic("exec returned");
+}
+
+static int
+elf64_obj_exec(struct preloaded_file *fp)
+{
+
+ printf("%s called for preloaded file %p (=%s):\n", __func__, fp,
+ fp->f_name);
+ return (ENOSYS);
+}
+
diff --git a/stand/efi/loader/arch/arm64/ldscript.arm64 b/stand/efi/loader/arch/arm64/ldscript.arm64
new file mode 100644
index 000000000000..685c0967840d
--- /dev/null
+++ b/stand/efi/loader/arch/arm64/ldscript.arm64
@@ -0,0 +1,85 @@
+/* $FreeBSD$ */
+/*
+OUTPUT_FORMAT("elf64-aarch64-freebsd", "elf64-aarch64-freebsd", "elf64-aarch64-freebsd")
+*/
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0;
+ ImageBase = .;
+ .text : {
+ *(.peheader)
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.plt)
+ } =0xD4200000
+ . = ALIGN(16);
+ .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)
+
+ . = ALIGN(16);
+ __bss_start = .;
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ *(.dynbss)
+ *(.bss *.bss.*)
+ *(COMMON)
+ . = ALIGN(16);
+ __bss_end = .;
+ }
+ . = ALIGN(16);
+ 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(16);
+ __gp = .;
+ .sdata : {
+ *(.got.plt .got)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ *(dynsbss)
+ *(.scommon)
+ }
+ . = ALIGN(16);
+ .dynamic : { *(.dynamic) }
+ . = ALIGN(16);
+ .rela.dyn : {
+ *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+ *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+ *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+ *(.rela.got)
+ *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
+ *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
+ *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
+ *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
+ *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+ *(.rela.plt)
+ *(.relset_*)
+ *(.rela.dyn .rela.dyn.*)
+ }
+ . = ALIGN(16);
+ .reloc : { *(.reloc) }
+ . = ALIGN(16);
+ .dynsym : { *(.dynsym) }
+ _edata = .;
+
+ /* Unused sections */
+ .dynstr : { *(.dynstr) }
+ .hash : { *(.hash) }
+}
diff --git a/stand/efi/loader/arch/arm64/start.S b/stand/efi/loader/arch/arm64/start.S
new file mode 100644
index 000000000000..b58c2c50be9f
--- /dev/null
+++ b/stand/efi/loader/arch/arm64/start.S
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 2014 Andrew Turner
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * We need to be a PE32+ file for EFI. On some architectures we can use
+ * objcopy to create the correct file, however on arm64 we need to do
+ * it ourselves.
+ */
+
+#define IMAGE_FILE_MACHINE_ARM64 0xaa64
+
+#define IMAGE_SCN_CNT_CODE 0x00000020
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000
+#define IMAGE_SCN_MEM_READ 0x40000000
+
+ .section .peheader,"a"
+efi_start:
+ /* The MS-DOS Stub, only used to get the offset of the COFF header */
+ .ascii "MZ"
+ .short 0
+ .space 0x38
+ .long pe_sig - efi_start
+
+ /* The PE32 Signature. Needs to be 8-byte aligned */
+ .align 3
+pe_sig:
+ .ascii "PE"
+ .short 0
+coff_head:
+ .short IMAGE_FILE_MACHINE_ARM64 /* AArch64 file */
+ .short 2 /* 2 Sections */
+ .long 0 /* Timestamp */
+ .long 0 /* No symbol table */
+ .long 0 /* No symbols */
+ .short section_table - optional_header /* Optional header size */
+ .short 0 /* Characteristics TODO: Fill in */
+
+optional_header:
+ .short 0x020b /* PE32+ (64-bit addressing) */
+ .byte 0 /* Major linker version */
+ .byte 0 /* Minor linker version */
+ .long _edata - _end_header /* Code size */
+ .long 0 /* No initialized data */
+ .long 0 /* No uninitialized data */
+ .long _start - efi_start /* Entry point */
+ .long _end_header - efi_start /* Start of code */
+
+optional_windows_header:
+ .quad 0 /* Image base */
+ .long 32 /* Section Alignment */
+ .long 8 /* File alignment */
+ .short 0 /* Major OS version */
+ .short 0 /* Minor OS version */
+ .short 0 /* Major image version */
+ .short 0 /* Minor image version */
+ .short 0 /* Major subsystem version */
+ .short 0 /* Minor subsystem version */
+ .long 0 /* Win32 version */
+ .long _edata - efi_start /* Image size */
+ .long _end_header - efi_start /* Header size */
+ .long 0 /* Checksum */
+ .short 0xa /* Subsystem (EFI app) */
+ .short 0 /* DLL Characteristics */
+ .quad 0 /* Stack reserve */
+ .quad 0 /* Stack commit */
+ .quad 0 /* Heap reserve */
+ .quad 0 /* Heap commit */
+ .long 0 /* Loader flags */
+ .long 6 /* Number of RVAs */
+
+ /* RVAs: */
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+
+section_table:
+ /* We need a .reloc section for EFI */
+ .ascii ".reloc"
+ .byte 0
+ .byte 0 /* Pad to 8 bytes */
+ .long 0 /* Virtual size */
+ .long 0 /* Virtual address */
+ .long 0 /* Size of raw data */
+ .long 0 /* Pointer to raw data */
+ .long 0 /* Pointer to relocations */
+ .long 0 /* Pointer to line numbers */
+ .short 0 /* Number of relocations */
+ .short 0 /* Number of line numbers */
+ .long (IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | \
+ IMAGE_SCN_MEM_DISCARDABLE) /* Characteristics */
+
+ /* The contents of the loader */
+ .ascii ".text"
+ .byte 0
+ .byte 0
+ .byte 0 /* Pad to 8 bytes */
+ .long _edata - _end_header /* Virtual size */
+ .long _end_header - efi_start /* Virtual address */
+ .long _edata - _end_header /* Size of raw data */
+ .long _end_header - efi_start /* Pointer to raw data */
+ .long 0 /* Pointer to relocations */
+ .long 0 /* Pointer to line numbers */
+ .short 0 /* Number of relocations */
+ .short 0 /* Number of line numbers */
+ .long (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | \
+ IMAGE_SCN_MEM_READ) /* Characteristics */
+_end_header:
+
+ .text
+ .globl _start
+_start:
+ /* Save the boot params to the stack */
+ stp x0, x1, [sp, #-16]!
+
+ adr x0, __bss_start
+ adr x1, __bss_end
+
+ b 2f
+
+1:
+ stp xzr, xzr, [x0], #16
+2:
+ cmp x0, x1
+ b.lo 1b
+
+ adr x0, ImageBase
+ adr x1, _DYNAMIC
+
+ bl self_reloc
+
+ ldp x0, x1, [sp], #16
+
+ bl efi_main
+
+1: b 1b
diff --git a/stand/efi/loader/arch/i386/Makefile.inc b/stand/efi/loader/arch/i386/Makefile.inc
new file mode 100644
index 000000000000..70d2848ba26f
--- /dev/null
+++ b/stand/efi/loader/arch/i386/Makefile.inc
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+SRCS+= start.S \
+ efimd.c \
+ elf32_freebsd.c \
+ exec.c
+
+.PATH: ${BOOTSRC}/i386/libi386
+SRCS+= nullconsole.c \
+ comconsole.c \
+ spinconsole.c
+
+CFLAGS+= -fPIC -DTERM_EMU
+LDFLAGS+= -Wl,-znocombreloc
diff --git a/stand/efi/loader/arch/i386/bootinfo.c b/stand/efi/loader/arch/i386/bootinfo.c
new file mode 100644
index 000000000000..cbd6e4ebe977
--- /dev/null
+++ b/stand/efi/loader/arch/i386/bootinfo.c
@@ -0,0 +1,275 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 <string.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+#include "libi386.h"
+#include <machine/bootinfo.h>
+
+static const char howto_switches[] = "aCdrgDmphsv";
+static int howto_masks[] = {
+ RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE,
+ RB_MUTE, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE
+};
+
+int
+bi_getboothowto(char *kargs)
+{
+ const char *sw;
+ char *opts;
+ int howto, i;
+
+ howto = 0;
+
+ /* Get the boot options from the environment first. */
+ for (i = 0; howto_names[i].ev != NULL; i++) {
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ }
+
+ /* Parse kargs */
+ if (kargs == NULL)
+ return (howto);
+
+ opts = strchr(kargs, '-');
+ while (opts != NULL) {
+ while (*(++opts) != '\0') {
+ sw = strchr(howto_switches, *opts);
+ if (sw == NULL)
+ break;
+ howto |= howto_masks[sw - howto_switches];
+ }
+ opts = strchr(opts, '-');
+ }
+
+ return (howto);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+vm_offset_t
+bi_copyenv(vm_offset_t start)
+{
+ struct env_var *ep;
+ vm_offset_t addr, last;
+ size_t len;
+
+ addr = last = start;
+
+ /* Traverse the environment. */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ len = strlen(ep->ev_name);
+ if (i386_copyin(ep->ev_name, addr, len) != len)
+ break;
+ addr += len;
+ if (i386_copyin("=", addr, 1) != 1)
+ break;
+ addr++;
+ if (ep->ev_value != NULL) {
+ len = strlen(ep->ev_value);
+ if (i386_copyin(ep->ev_value, addr, len) != len)
+ break;
+ addr += len;
+ }
+ if (i386_copyin("", addr, 1) != 1)
+ break;
+ last = ++addr;
+ }
+
+ if (i386_copyin("", last++, 1) != 1)
+ last = start;
+ return(last);
+}
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a) { \
+ u_int32_t x = (v); \
+ i386_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s) { \
+ COPY32(t, a); \
+ COPY32(strlen(s) + 1, a); \
+ i386_copyin(s, a, strlen(s) + 1); \
+ a += roundup(strlen(s) + 1, sizeof(u_int64_t));\
+}
+
+#define MOD_NAME(a, s) MOD_STR(MODINFO_NAME, a, s)
+#define MOD_TYPE(a, s) MOD_STR(MODINFO_TYPE, a, s)
+#define MOD_ARGS(a, s) MOD_STR(MODINFO_ARGS, a, s)
+
+#define MOD_VAR(t, a, s) { \
+ COPY32(t, a); \
+ COPY32(sizeof(s), a); \
+ i386_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_int64_t)); \
+}
+
+#define MOD_ADDR(a, s) MOD_VAR(MODINFO_ADDR, a, s)
+#define MOD_SIZE(a, s) MOD_VAR(MODINFO_SIZE, a, s)
+
+#define MOD_METADATA(a, mm) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a); \
+ COPY32(mm->md_size, a); \
+ i386_copyin(mm->md_data, a, mm->md_size); \
+ a += roundup(mm->md_size, sizeof(u_int64_t));\
+}
+
+#define MOD_END(a) { \
+ COPY32(MODINFO_END, a); \
+ COPY32(0, a); \
+}
+
+vm_offset_t
+bi_copymodules(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+
+ /* Start with the first module on the list, should be the kernel. */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+ /* The name field must come first. */
+ MOD_NAME(addr, fp->f_name);
+ MOD_TYPE(addr, fp->f_type);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args);
+ MOD_ADDR(addr, fp->f_addr);
+ MOD_SIZE(addr, fp->f_size);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md);
+ }
+ }
+ MOD_END(addr);
+ return(addr);
+}
+
+/*
+ * Load the information expected by the kernel.
+ *
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+bi_load(struct preloaded_file *fp, uint64_t *bi_addr)
+{
+ struct bootinfo bi;
+ struct preloaded_file *xp;
+ struct file_metadata *md;
+ struct devdesc *rootdev;
+ char *rootdevname;
+ vm_offset_t addr, ssym, esym;
+
+ bzero(&bi, sizeof(struct bootinfo));
+ bi.bi_version = 1;
+// bi.bi_boothowto = bi_getboothowto(fp->f_args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied
+ * device. This should perhaps go to MI code and/or have $rootdev
+ * tested/set by MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ i386_getdev((void**)&rootdev, rootdevname, NULL);
+ if (rootdev != NULL) {
+ /* Try reading /etc/fstab to select the root device. */
+ getrootmount(i386_fmtdev(rootdev));
+ free(rootdev);
+ }
+
+ md = file_findmetadata(fp, MODINFOMD_SSYM);
+ ssym = (md != NULL) ? *((vm_offset_t *)&(md->md_data)) : 0;
+ md = file_findmetadata(fp, MODINFOMD_ESYM);
+ esym = (md != NULL) ? *((vm_offset_t *)&(md->md_data)) : 0;
+ if (ssym != 0 && esym != 0) {
+ bi.bi_symtab = ssym;
+ bi.bi_esymtab = esym;
+ }
+
+ /* Find the last module in the chain. */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+
+ addr = (addr + 15) & ~15;
+
+ /* Copy module list and metadata. */
+ bi.bi_modulep = addr;
+ addr = bi_copymodules(addr);
+ if (addr <= bi.bi_modulep) {
+ addr = bi.bi_modulep;
+ bi.bi_modulep = 0;
+ }
+
+ addr = (addr + 15) & ~15;
+
+ /* Copy our environment. */
+ bi.bi_envp = addr;
+ addr = bi_copyenv(addr);
+ if (addr <= bi.bi_envp) {
+ addr = bi.bi_envp;
+ bi.bi_envp = 0;
+ }
+
+ addr = (addr + PAGE_MASK) & ~PAGE_MASK;
+ bi.bi_kernend = addr;
+
+ return (ldr_bootinfo(&bi, bi_addr));
+}
diff --git a/stand/efi/loader/arch/i386/efimd.c b/stand/efi/loader/arch/i386/efimd.c
new file mode 100644
index 000000000000..8e1d850d82cc
--- /dev/null
+++ b/stand/efi/loader/arch/i386/efimd.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2004, 2006 Marcel Moolenaar
+ * 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 ``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 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 <efi.h>
+#include <efilib.h>
+
+#include <libi386.h>
+#include <machine/bootinfo.h>
+
+#define EFI_INTEL_FPSWA \
+ {0xc41b6531,0x97b9,0x11d3,{0x9a,0x29,0x00,0x90,0x27,0x3f,0xc1,0x4d}}
+
+static EFI_GUID fpswa_guid = EFI_INTEL_FPSWA;
+
+/* DIG64 Headless Console & Debug Port Table. */
+#define HCDP_TABLE_GUID \
+ {0xf951938d,0x620b,0x42ef,{0x82,0x79,0xa8,0x4b,0x79,0x61,0x78,0x98}}
+
+static EFI_GUID hcdp_guid = HCDP_TABLE_GUID;
+
+static UINTN mapkey;
+
+int ldr_bootinfo(struct bootinfo *, uint64_t *);
+int ldr_enter(const char *);
+
+static uint64_t
+ldr_alloc(vm_offset_t va)
+{
+
+ return (0);
+}
+
+int
+ldr_bootinfo(struct bootinfo *bi, uint64_t *bi_addr)
+{
+ VOID *fpswa;
+ EFI_MEMORY_DESCRIPTOR *mm;
+ EFI_PHYSICAL_ADDRESS addr;
+ EFI_HANDLE handle;
+ EFI_STATUS status;
+ size_t bisz;
+ UINTN mmsz, pages, sz;
+ UINT32 mmver;
+
+ bi->bi_systab = (uint64_t)ST;
+ bi->bi_hcdp = (uint64_t)efi_get_table(&hcdp_guid);
+
+ sz = sizeof(EFI_HANDLE);
+ status = BS->LocateHandle(ByProtocol, &fpswa_guid, 0, &sz, &handle);
+ if (status == 0)
+ status = BS->HandleProtocol(handle, &fpswa_guid, &fpswa);
+ bi->bi_fpswa = (status == 0) ? (uint64_t)fpswa : 0;
+
+ bisz = (sizeof(struct bootinfo) + 0x0f) & ~0x0f;
+
+ /*
+ * Allocate enough pages to hold the bootinfo block and the memory
+ * map EFI will return to us. The memory map has an unknown size,
+ * so we have to determine that first. Note that the AllocatePages
+ * call can itself modify the memory map, so we have to take that
+ * into account as well. The changes to the memory map are caused
+ * by splitting a range of free memory into two (AFAICT), so that
+ * one is marked as being loader data.
+ */
+ sz = 0;
+ BS->GetMemoryMap(&sz, NULL, &mapkey, &mmsz, &mmver);
+ sz += mmsz;
+ sz = (sz + 15) & ~15;
+ pages = EFI_SIZE_TO_PAGES(sz + bisz);
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages,
+ &addr);
+ if (EFI_ERROR(status)) {
+ printf("%s: AllocatePages() returned 0x%lx\n", __func__,
+ (long)status);
+ return (ENOMEM);
+ }
+
+ /*
+ * Read the memory map and stash it after bootinfo. Align the
+ * memory map on a 16-byte boundary (the bootinfo block is page
+ * aligned).
+ */
+ *bi_addr = addr;
+ mm = (void *)(addr + bisz);
+ sz = (EFI_PAGE_SIZE * pages) - bisz;
+ status = BS->GetMemoryMap(&sz, mm, &mapkey, &mmsz, &mmver);
+ if (EFI_ERROR(status)) {
+ printf("%s: GetMemoryMap() returned 0x%lx\n", __func__,
+ (long)status);
+ return (EINVAL);
+ }
+ bi->bi_memmap = (uint64_t)mm;
+ bi->bi_memmap_size = sz;
+ bi->bi_memdesc_size = mmsz;
+ bi->bi_memdesc_version = mmver;
+
+ bcopy(bi, (void *)(*bi_addr), sizeof(*bi));
+ return (0);
+}
+
+int
+ldr_enter(const char *kernel)
+{
+ EFI_STATUS status;
+
+ status = BS->ExitBootServices(IH, mapkey);
+ if (EFI_ERROR(status)) {
+ printf("%s: ExitBootServices() returned 0x%lx\n", __func__,
+ (long)status);
+ return (EINVAL);
+ }
+
+ return (0);
+}
diff --git a/stand/efi/loader/arch/i386/elf32_freebsd.c b/stand/efi/loader/arch/i386/elf32_freebsd.c
new file mode 100644
index 000000000000..d3c42613108e
--- /dev/null
+++ b/stand/efi/loader/arch/i386/elf32_freebsd.c
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 1998 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 <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <stand.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+#include "../libi386/libi386.h"
+#include "../btx/lib/btxv86.h"
+
+extern void __exec(caddr_t addr, ...);
+extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
+extern int ldr_enter(const char *kernel);
+
+static int elf32_exec(struct preloaded_file *amp);
+static int elf32_obj_exec(struct preloaded_file *amp);
+
+struct file_format i386_elf = { elf32_loadfile, elf32_exec };
+struct file_format i386_elf_obj = { elf32_obj_loadfile, elf32_obj_exec };
+
+struct file_format *file_formats[] = {
+ &i386_elf,
+ &i386_elf_obj,
+ NULL
+};
+
+/*
+ * 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
+elf32_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ vm_offset_t entry, bootinfop, modulep, kernend;
+ int boothowto, err, bootdev;
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE);
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+
+ efi_time_fini();
+ err = bi_load(fp->f_args, &modulep, &kernend);
+ if (err != 0) {
+ efi_time_init();
+ return(err);
+ }
+ entry = ehdr->e_entry & 0xffffff;
+
+ printf("Start @ 0x%x ...\n", entry);
+
+ ldr_enter(fp->f_name);
+
+ dev_cleanup();
+ __exec((void *)entry, boothowto, bootdev, 0, 0, 0, bootinfop, modulep, kernend);
+
+ panic("exec returned");
+}
+
+static int
+elf32_obj_exec(struct preloaded_file *fp)
+{
+ return (EFTYPE);
+}
diff --git a/stand/efi/loader/arch/i386/exec.c b/stand/efi/loader/arch/i386/exec.c
new file mode 100644
index 000000000000..579f5593b24b
--- /dev/null
+++ b/stand/efi/loader/arch/i386/exec.c
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2010 Rui Paulo <rpaulo@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 ``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 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 <machine/elf.h>
+#include "../btx/lib/btxv86.h"
+
+#include "../../common/bootstrap.h"
+
+uint32_t __base;
+struct __v86 __v86;
+
+void
+__v86int()
+{
+ printf("%s\n", __func__);
+ exit(1);
+}
+
+void
+__exec(caddr_t addr, ...)
+{
+}
diff --git a/stand/efi/loader/arch/i386/i386_copy.c b/stand/efi/loader/arch/i386/i386_copy.c
new file mode 100644
index 000000000000..522913f5da43
--- /dev/null
+++ b/stand/efi/loader/arch/i386/i386_copy.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 1998 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$");
+
+/*
+ * MD primitives supporting placement of module data
+ *
+ * XXX should check load address/size against memory top.
+ */
+#include <stand.h>
+
+#include "libi386.h"
+#include "btxv86.h"
+
+ssize_t
+i386_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+ bcopy(src, PTOV(dest), len);
+ return(len);
+}
+
+ssize_t
+i386_copyout(const vm_offset_t src, void *dest, const size_t len)
+{
+ bcopy(PTOV(src), dest, len);
+ return(len);
+}
+
+
+ssize_t
+i386_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+ return (read(fd, PTOV(dest), len));
+}
diff --git a/stand/efi/loader/arch/i386/ldscript.i386 b/stand/efi/loader/arch/i386/ldscript.i386
new file mode 100644
index 000000000000..e17212a1bddd
--- /dev/null
+++ b/stand/efi/loader/arch/i386/ldscript.i386
@@ -0,0 +1,77 @@
+/* $FreeBSD$ */
+OUTPUT_FORMAT("elf32-i386-freebsd", "elf32-i386-freebsd", "elf32-i386-freebsd")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0;
+ ImageBase = .;
+ . = SIZEOF_HEADERS;
+ . = 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);
+ .rel.dyn : {
+ *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+ *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+ *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+ *(.rel.got)
+ *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*)
+ *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*)
+ *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*)
+ *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*)
+ *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+ *(.rel.plt)
+ *(.relset_*)
+ *(.rel.dyn .rel.dyn.*)
+ }
+ . = ALIGN(4096);
+ .reloc : { *(.reloc) }
+ . = ALIGN(4096);
+ .hash : { *(.hash) }
+ . = ALIGN(4096);
+ .dynsym : { *(.dynsym) }
+ . = ALIGN(4096);
+ .dynstr : { *(.dynstr) }
+}
diff --git a/stand/efi/loader/arch/i386/start.S b/stand/efi/loader/arch/i386/start.S
new file mode 100644
index 000000000000..b597f419d4a1
--- /dev/null
+++ b/stand/efi/loader/arch/i386/start.S
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2008-2010 Rui Paulo <rpaulo@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.
+ *
+ * $FreeBSD$
+ */
+
+ .text
+
+#include <machine/asm.h>
+
+#define EFI_SUCCESS 0
+
+/*
+ * EFI entry point.
+ * _start(EFI_IMAGE image_handle, EFI_SYSTEM_TABLE *system_table);
+ *
+ * We calculate the base address along with _DYNAMIC, relocate us and finally
+ * pass control to efi_main.
+ */
+
+ENTRY(_start)
+ pushl %ebp
+ movl %esp, %ebp
+
+ pushl 12(%ebp) /* image_handle */
+ pushl 8(%ebp) /* system_table */
+ call 0f
+0: popl %eax
+ movl %eax, %ebx
+ addl $ImageBase-0b, %eax
+ addl $_DYNAMIC-0b, %ebx
+ pushl %ebx /* dynamic */
+ pushl %eax /* ImageBase */
+ call self_reloc
+ popl %ebx /* remove ImageBase from the stack */
+ popl %ebx /* remove dynamic from the stack */
+ call efi_main
+1: leave
+ ret
+END(_start)
+
+ .data
+ .section .reloc, "a"
+ .long 0
+ .long 10
+ .word 0
diff --git a/stand/efi/loader/autoload.c b/stand/efi/loader/autoload.c
new file mode 100644
index 000000000000..c1eb84928ed8
--- /dev/null
+++ b/stand/efi/loader/autoload.c
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 2010 Rui Paulo <rpaulo@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 ``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 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 "loader_efi.h"
+
+int
+efi_autoload(void)
+{
+
+ return (0);
+}
diff --git a/stand/efi/loader/bootinfo.c b/stand/efi/loader/bootinfo.c
new file mode 100644
index 000000000000..ca06a6194f8d
--- /dev/null
+++ b/stand/efi/loader/bootinfo.c
@@ -0,0 +1,471 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2004, 2006 Marcel Moolenaar
+ * 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$");
+
+#include <stand.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+#include <machine/cpufunc.h>
+#include <machine/elf.h>
+#include <machine/metadata.h>
+#include <machine/psl.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+#include "loader_efi.h"
+
+#if defined(__amd64__)
+#include <machine/specialreg.h>
+#endif
+
+#include "framebuffer.h"
+
+#if defined(LOADER_FDT_SUPPORT)
+#include <fdt_platform.h>
+#endif
+
+int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
+
+extern EFI_SYSTEM_TABLE *ST;
+
+static const char howto_switches[] = "aCdrgDmphsv";
+static int howto_masks[] = {
+ RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE,
+ RB_MUTE, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE
+};
+
+static int
+bi_getboothowto(char *kargs)
+{
+ const char *sw;
+ char *opts;
+ char *console;
+ int howto, i;
+
+ howto = 0;
+
+ /* Get the boot options from the environment first. */
+ for (i = 0; howto_names[i].ev != NULL; i++) {
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ }
+
+ console = getenv("console");
+ if (console != NULL) {
+ if (strcmp(console, "comconsole") == 0)
+ howto |= RB_SERIAL;
+ if (strcmp(console, "nullconsole") == 0)
+ howto |= RB_MUTE;
+ }
+
+ /* Parse kargs */
+ if (kargs == NULL)
+ return (howto);
+
+ opts = strchr(kargs, '-');
+ while (opts != NULL) {
+ while (*(++opts) != '\0') {
+ sw = strchr(howto_switches, *opts);
+ if (sw == NULL)
+ break;
+ howto |= howto_masks[sw - howto_switches];
+ }
+ opts = strchr(opts, '-');
+ }
+
+ return (howto);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+static vm_offset_t
+bi_copyenv(vm_offset_t start)
+{
+ struct env_var *ep;
+ vm_offset_t addr, last;
+ size_t len;
+
+ addr = last = start;
+
+ /* Traverse the environment. */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ len = strlen(ep->ev_name);
+ if ((size_t)archsw.arch_copyin(ep->ev_name, addr, len) != len)
+ break;
+ addr += len;
+ if (archsw.arch_copyin("=", addr, 1) != 1)
+ break;
+ addr++;
+ if (ep->ev_value != NULL) {
+ len = strlen(ep->ev_value);
+ if ((size_t)archsw.arch_copyin(ep->ev_value, addr, len) != len)
+ break;
+ addr += len;
+ }
+ if (archsw.arch_copyin("", addr, 1) != 1)
+ break;
+ last = ++addr;
+ }
+
+ if (archsw.arch_copyin("", last++, 1) != 1)
+ last = start;
+ return(last);
+}
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a, c) { \
+ uint32_t x = (v); \
+ if (c) \
+ archsw.arch_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c); \
+ if (c) \
+ archsw.arch_copyin(s, a, strlen(s) + 1); \
+ a += roundup(strlen(s) + 1, sizeof(u_long)); \
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ archsw.arch_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_long)); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c); \
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ archsw.arch_copyin(mm->md_data, a, mm->md_size); \
+ a += roundup(mm->md_size, sizeof(u_long)); \
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+static vm_offset_t
+bi_copymodules(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ int c;
+ uint64_t v;
+
+ c = addr != 0;
+ /* Start with the first module on the list, should be the kernel. */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+ MOD_NAME(addr, fp->f_name, c); /* This must come first. */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ v = fp->f_addr;
+#if defined(__arm__)
+ v -= __elfN(relocation_offset);
+#endif
+ MOD_ADDR(addr, v, c);
+ v = fp->f_size;
+ MOD_SIZE(addr, v, c);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next)
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md, c);
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+static int
+bi_load_efi_data(struct preloaded_file *kfp)
+{
+ EFI_MEMORY_DESCRIPTOR *mm;
+ EFI_PHYSICAL_ADDRESS addr;
+ EFI_STATUS status;
+ size_t efisz;
+ UINTN efi_mapkey;
+ UINTN mmsz, pages, retry, sz;
+ UINT32 mmver;
+ struct efi_map_header *efihdr;
+
+#if defined(__amd64__) || defined(__aarch64__)
+ struct efi_fb efifb;
+
+ if (efi_find_framebuffer(&efifb) == 0) {
+ printf("EFI framebuffer information:\n");
+ printf("addr, size 0x%jx, 0x%jx\n", efifb.fb_addr,
+ efifb.fb_size);
+ printf("dimensions %d x %d\n", efifb.fb_width,
+ efifb.fb_height);
+ printf("stride %d\n", efifb.fb_stride);
+ printf("masks 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
+ efifb.fb_mask_red, efifb.fb_mask_green, efifb.fb_mask_blue,
+ efifb.fb_mask_reserved);
+
+ file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(efifb), &efifb);
+ }
+#endif
+
+ efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
+
+ /*
+ * Assgin size of EFI_MEMORY_DESCRIPTOR to keep compatible with
+ * u-boot which doesn't fill this value when buffer for memory
+ * descriptors is too small (eg. 0 to obtain memory map size)
+ */
+ mmsz = sizeof(EFI_MEMORY_DESCRIPTOR);
+
+ /*
+ * It is possible that the first call to ExitBootServices may change
+ * the map key. Fetch a new map key and retry ExitBootServices in that
+ * case.
+ */
+ for (retry = 2; retry > 0; retry--) {
+ /*
+ * Allocate enough pages to hold the bootinfo block and the
+ * memory map EFI will return to us. The memory map has an
+ * unknown size, so we have to determine that first. Note that
+ * the AllocatePages call can itself modify the memory map, so
+ * we have to take that into account as well. The changes to
+ * the memory map are caused by splitting a range of free
+ * memory into two (AFAICT), so that one is marked as being
+ * loader data.
+ */
+ sz = 0;
+ BS->GetMemoryMap(&sz, NULL, &efi_mapkey, &mmsz, &mmver);
+ sz += mmsz;
+ sz = (sz + 0xf) & ~0xf;
+ pages = EFI_SIZE_TO_PAGES(sz + efisz);
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+ pages, &addr);
+ if (EFI_ERROR(status)) {
+ printf("%s: AllocatePages error %lu\n", __func__,
+ EFI_ERROR_CODE(status));
+ return (ENOMEM);
+ }
+
+ /*
+ * Read the memory map and stash it after bootinfo. Align the
+ * memory map on a 16-byte boundary (the bootinfo block is page
+ * aligned).
+ */
+ efihdr = (struct efi_map_header *)addr;
+ mm = (void *)((uint8_t *)efihdr + efisz);
+ sz = (EFI_PAGE_SIZE * pages) - efisz;
+
+ status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver);
+ if (EFI_ERROR(status)) {
+ printf("%s: GetMemoryMap error %lu\n", __func__,
+ EFI_ERROR_CODE(status));
+ return (EINVAL);
+ }
+ status = BS->ExitBootServices(IH, efi_mapkey);
+ if (EFI_ERROR(status) == 0) {
+ efihdr->memory_size = sz;
+ efihdr->descriptor_size = mmsz;
+ efihdr->descriptor_version = mmver;
+ file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz,
+ efihdr);
+ return (0);
+ }
+ BS->FreePages(addr, pages);
+ }
+ printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status));
+ return (EINVAL);
+}
+
+/*
+ * Load the information expected by an amd64 kernel.
+ *
+ * - The 'boothowto' argument is constructed.
+ * - The 'bootdev' argument is constructed.
+ * - The 'bootinfo' struct is constructed, and copied into the kernel space.
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp)
+{
+ struct preloaded_file *xp, *kfp;
+ struct devdesc *rootdev;
+ struct file_metadata *md;
+ vm_offset_t addr;
+ uint64_t kernend;
+ uint64_t envp;
+ vm_offset_t size;
+ char *rootdevname;
+ int howto;
+#if defined(LOADER_FDT_SUPPORT)
+ vm_offset_t dtbp;
+ int dtb_size;
+#endif
+#if defined(__arm__)
+ vm_offset_t vaddr;
+ size_t i;
+ /*
+ * These metadata addreses must be converted for kernel after
+ * relocation.
+ */
+ uint32_t mdt[] = {
+ MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND,
+ MODINFOMD_ENVP,
+#if defined(LOADER_FDT_SUPPORT)
+ MODINFOMD_DTBP
+#endif
+ };
+#endif
+
+ howto = bi_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied
+ * device. This should perhaps go to MI code and/or have $rootdev
+ * tested/set by MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ archsw.arch_getdev((void**)(&rootdev), rootdevname, NULL);
+ if (rootdev == NULL) {
+ printf("Can't determine root device.\n");
+ return(EINVAL);
+ }
+
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(efi_fmtdev((void *)rootdev));
+
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+
+ /* Pad to a page boundary. */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* Copy our environment. */
+ envp = addr;
+ addr = bi_copyenv(addr);
+
+ /* Pad to a page boundary. */
+ addr = roundup(addr, PAGE_SIZE);
+
+#if defined(LOADER_FDT_SUPPORT)
+ /* Handle device tree blob */
+ dtbp = addr;
+ dtb_size = fdt_copy(addr);
+
+ /* Pad to a page boundary */
+ if (dtb_size)
+ addr += roundup(dtb_size, PAGE_SIZE);
+#endif
+
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf64 kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ kernend = 0; /* fill it in later */
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+#if defined(LOADER_FDT_SUPPORT)
+ if (dtb_size)
+ file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
+ else
+ printf("WARNING! Trying to fire up the kernel, but no "
+ "device tree blob found!\n");
+#endif
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST);
+
+ bi_load_efi_data(kfp);
+
+ /* Figure out the size and location of the metadata. */
+ *modulep = addr;
+ size = bi_copymodules(0);
+ kernend = roundup(addr + size, PAGE_SIZE);
+ *kernendp = kernend;
+
+ /* patch MODINFOMD_KERNEND */
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+#if defined(__arm__)
+ *modulep -= __elfN(relocation_offset);
+
+ /* Do relocation fixup on metadata of each module. */
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ for (i = 0; i < nitems(mdt); i++) {
+ md = file_findmetadata(xp, mdt[i]);
+ if (md) {
+ bcopy(md->md_data, &vaddr, sizeof vaddr);
+ vaddr -= __elfN(relocation_offset);
+ bcopy(&vaddr, md->md_data, sizeof vaddr);
+ }
+ }
+ }
+#endif
+
+ /* Copy module list and metadata. */
+ (void)bi_copymodules(addr);
+
+ return (0);
+}
diff --git a/stand/efi/loader/conf.c b/stand/efi/loader/conf.c
new file mode 100644
index 000000000000..cea95b3db104
--- /dev/null
+++ b/stand/efi/loader/conf.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 ``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 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>
+#include <efi.h>
+#include <efilib.h>
+#ifdef EFI_ZFS_BOOT
+#include <libzfs.h>
+#endif
+
+struct devsw *devsw[] = {
+ &efipart_fddev,
+ &efipart_cddev,
+ &efipart_hddev,
+ &efinet_dev,
+#ifdef EFI_ZFS_BOOT
+ &zfs_dev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#ifdef EFI_ZFS_BOOT
+ &zfs_fsops,
+#endif
+ &dosfs_fsops,
+ &ufs_fsops,
+ &cd9660_fsops,
+ &tftp_fsops,
+ &nfs_fsops,
+ &gzipfs_fsops,
+ &bzipfs_fsops,
+ NULL
+};
+
+struct netif_driver *netif_drivers[] = {
+ &efinetif,
+ NULL
+};
+
+extern struct console efi_console;
+#if defined(__amd64__) || defined(__i386__)
+extern struct console comconsole;
+extern struct console nullconsole;
+extern struct console spinconsole;
+#endif
+
+struct console *consoles[] = {
+ &efi_console,
+#if defined(__amd64__) || defined(__i386__)
+ &comconsole,
+ &nullconsole,
+ &spinconsole,
+#endif
+ NULL
+};
diff --git a/stand/efi/loader/copy.c b/stand/efi/loader/copy.c
new file mode 100644
index 000000000000..efa42b99d66e
--- /dev/null
+++ b/stand/efi/loader/copy.c
@@ -0,0 +1,287 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <stand.h>
+#include <bootstrap.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "loader_efi.h"
+
+#if defined(__i386__) || defined(__amd64__)
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+
+/*
+ * The code is excerpted from sys/x86/x86/identcpu.c: identify_cpu(),
+ * identify_hypervisor(), and dev/hyperv/vmbus/hyperv.c: hyperv_identify().
+ */
+#define CPUID_LEAF_HV_MAXLEAF 0x40000000
+#define CPUID_LEAF_HV_INTERFACE 0x40000001
+#define CPUID_LEAF_HV_FEATURES 0x40000003
+#define CPUID_LEAF_HV_LIMITS 0x40000005
+#define CPUID_HV_IFACE_HYPERV 0x31237648 /* HV#1 */
+#define CPUID_HV_MSR_TIME_REFCNT 0x0002 /* MSR_HV_TIME_REF_COUNT */
+#define CPUID_HV_MSR_HYPERCALL 0x0020
+
+static int
+running_on_hyperv(void)
+{
+ char hv_vendor[16];
+ uint32_t regs[4];
+
+ do_cpuid(1, regs);
+ if ((regs[2] & CPUID2_HV) == 0)
+ return (0);
+
+ do_cpuid(CPUID_LEAF_HV_MAXLEAF, regs);
+ if (regs[0] < CPUID_LEAF_HV_LIMITS)
+ return (0);
+
+ ((uint32_t *)&hv_vendor)[0] = regs[1];
+ ((uint32_t *)&hv_vendor)[1] = regs[2];
+ ((uint32_t *)&hv_vendor)[2] = regs[3];
+ hv_vendor[12] = '\0';
+ if (strcmp(hv_vendor, "Microsoft Hv") != 0)
+ return (0);
+
+ do_cpuid(CPUID_LEAF_HV_INTERFACE, regs);
+ if (regs[0] != CPUID_HV_IFACE_HYPERV)
+ return (0);
+
+ do_cpuid(CPUID_LEAF_HV_FEATURES, regs);
+ if ((regs[0] & CPUID_HV_MSR_HYPERCALL) == 0)
+ return (0);
+ if ((regs[0] & CPUID_HV_MSR_TIME_REFCNT) == 0)
+ return (0);
+
+ return (1);
+}
+
+#define KERNEL_PHYSICAL_BASE (2*1024*1024)
+
+static void
+efi_verify_staging_size(unsigned long *nr_pages)
+{
+ UINTN sz;
+ EFI_MEMORY_DESCRIPTOR *map, *p;
+ EFI_PHYSICAL_ADDRESS start, end;
+ UINTN key, dsz;
+ UINT32 dver;
+ EFI_STATUS status;
+ int i, ndesc;
+ unsigned long available_pages = 0;
+
+ sz = 0;
+ status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
+ if (status != EFI_BUFFER_TOO_SMALL) {
+ printf("Can't determine memory map size\n");
+ return;
+ }
+
+ map = malloc(sz);
+ status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
+ if (EFI_ERROR(status)) {
+ printf("Can't read memory map\n");
+ goto out;
+ }
+
+ ndesc = sz / dsz;
+ for (i = 0, p = map; i < ndesc;
+ i++, p = NextMemoryDescriptor(p, dsz)) {
+ start = p->PhysicalStart;
+ end = start + p->NumberOfPages * EFI_PAGE_SIZE;
+
+ if (KERNEL_PHYSICAL_BASE < start ||
+ KERNEL_PHYSICAL_BASE >= end)
+ continue;
+
+ available_pages = p->NumberOfPages -
+ ((KERNEL_PHYSICAL_BASE - start) >> EFI_PAGE_SHIFT);
+ break;
+ }
+
+ if (available_pages == 0) {
+ printf("Can't find valid memory map for staging area!\n");
+ goto out;
+ }
+
+ i++;
+ p = NextMemoryDescriptor(p, dsz);
+
+ for ( ; i < ndesc;
+ i++, p = NextMemoryDescriptor(p, dsz)) {
+ if (p->Type != EfiConventionalMemory &&
+ p->Type != EfiLoaderData)
+ break;
+
+ if (p->PhysicalStart != end)
+ break;
+
+ end = p->PhysicalStart + p->NumberOfPages * EFI_PAGE_SIZE;
+
+ available_pages += p->NumberOfPages;
+ }
+
+ if (*nr_pages > available_pages) {
+ printf("Staging area's size is reduced: %ld -> %ld!\n",
+ *nr_pages, available_pages);
+ *nr_pages = available_pages;
+ }
+out:
+ free(map);
+}
+#endif /* __i386__ || __amd64__ */
+
+#ifndef EFI_STAGING_SIZE
+#define EFI_STAGING_SIZE 64
+#endif
+
+EFI_PHYSICAL_ADDRESS staging, staging_end;
+int stage_offset_set = 0;
+ssize_t stage_offset;
+
+int
+efi_copy_init(void)
+{
+ EFI_STATUS status;
+
+ unsigned long nr_pages;
+
+ nr_pages = EFI_SIZE_TO_PAGES((EFI_STAGING_SIZE) * 1024 * 1024);
+
+#if defined(__i386__) || defined(__amd64__)
+ /*
+ * We'll decrease nr_pages, if it's too big. Currently we only
+ * apply this to FreeBSD VM running on Hyper-V. Why? Please see
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=211746#c28
+ */
+ if (running_on_hyperv())
+ efi_verify_staging_size(&nr_pages);
+
+ /*
+ * The staging area must reside in the the first 1GB physical
+ * memory: see elf64_exec() in
+ * boot/efi/loader/arch/amd64/elf64_freebsd.c.
+ */
+ staging = 1024*1024*1024;
+ status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
+ nr_pages, &staging);
+#else
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+ nr_pages, &staging);
+#endif
+ if (EFI_ERROR(status)) {
+ printf("failed to allocate staging area: %lu\n",
+ EFI_ERROR_CODE(status));
+ return (status);
+ }
+ staging_end = staging + nr_pages * EFI_PAGE_SIZE;
+
+#if defined(__aarch64__) || defined(__arm__)
+ /*
+ * Round the kernel load address to a 2MiB value. This is needed
+ * because the kernel builds a page table based on where it has
+ * been loaded in physical address space. As the kernel will use
+ * either a 1MiB or 2MiB page for this we need to make sure it
+ * is correctly aligned for both cases.
+ */
+ staging = roundup2(staging, 2 * 1024 * 1024);
+#endif
+
+ return (0);
+}
+
+void *
+efi_translate(vm_offset_t ptr)
+{
+
+ return ((void *)(ptr + stage_offset));
+}
+
+ssize_t
+efi_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+
+ if (!stage_offset_set) {
+ stage_offset = (vm_offset_t)staging - dest;
+ stage_offset_set = 1;
+ }
+
+ /* XXX: Callers do not check for failure. */
+ if (dest + stage_offset + len > staging_end) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ bcopy(src, (void *)(dest + stage_offset), len);
+ return (len);
+}
+
+ssize_t
+efi_copyout(const vm_offset_t src, void *dest, const size_t len)
+{
+
+ /* XXX: Callers do not check for failure. */
+ if (src + stage_offset + len > staging_end) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ bcopy((void *)(src + stage_offset), dest, len);
+ return (len);
+}
+
+
+ssize_t
+efi_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+
+ if (dest + stage_offset + len > staging_end) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ return (read(fd, (void *)(dest + stage_offset), len));
+}
+
+void
+efi_copy_finish(void)
+{
+ uint64_t *src, *dst, *last;
+
+ src = (uint64_t *)staging;
+ dst = (uint64_t *)(staging - stage_offset);
+ last = (uint64_t *)staging_end;
+
+ while (src < last)
+ *dst++ = *src++;
+}
diff --git a/stand/efi/loader/efi_main.c b/stand/efi/loader/efi_main.c
new file mode 100644
index 000000000000..e424d89666ec
--- /dev/null
+++ b/stand/efi/loader/efi_main.c
@@ -0,0 +1,188 @@
+/*-
+ * Copyright (c) 2000 Doug Rabson
+ * 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 <efi.h>
+#include <eficonsctl.h>
+#include <efilib.h>
+#include <stand.h>
+
+static EFI_PHYSICAL_ADDRESS heap;
+static UINTN heapsize;
+
+void
+efi_exit(EFI_STATUS exit_code)
+{
+
+ BS->FreePages(heap, EFI_SIZE_TO_PAGES(heapsize));
+ BS->Exit(IH, exit_code, 0, NULL);
+}
+
+void
+exit(int status)
+{
+
+ efi_exit(EFI_LOAD_ERROR);
+}
+
+static CHAR16 *
+arg_skipsep(CHAR16 *argp)
+{
+
+ while (*argp == ' ' || *argp == '\t' || *argp == '\n')
+ argp++;
+ return (argp);
+}
+
+static CHAR16 *
+arg_skipword(CHAR16 *argp)
+{
+
+ while (*argp && *argp != ' ' && *argp != '\t' && *argp != '\n')
+ argp++;
+ return (argp);
+}
+
+EFI_STATUS
+efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
+{
+ static EFI_GUID image_protocol = LOADED_IMAGE_PROTOCOL;
+ static EFI_GUID console_control_protocol =
+ EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
+ EFI_CONSOLE_CONTROL_PROTOCOL *console_control = NULL;
+ EFI_LOADED_IMAGE *img;
+ CHAR16 *argp, *args, **argv;
+ EFI_STATUS status;
+ int argc, addprog;
+
+ IH = image_handle;
+ ST = system_table;
+ BS = ST->BootServices;
+ RS = ST->RuntimeServices;
+
+ status = BS->LocateProtocol(&console_control_protocol, NULL,
+ (VOID **)&console_control);
+ if (status == EFI_SUCCESS)
+ (void)console_control->SetMode(console_control,
+ EfiConsoleControlScreenText);
+
+ heapsize = 64 * 1024 * 1024;
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+ EFI_SIZE_TO_PAGES(heapsize), &heap);
+ if (status != EFI_SUCCESS)
+ BS->Exit(IH, status, 0, NULL);
+
+ setheap((void *)(uintptr_t)heap, (void *)(uintptr_t)(heap + heapsize));
+
+ /* Use efi_exit() from here on... */
+
+ status = BS->HandleProtocol(IH, &image_protocol, (VOID**)&img);
+ if (status != EFI_SUCCESS)
+ efi_exit(status);
+
+ /*
+ * Pre-process the (optional) load options. If the option string
+ * is given as an ASCII string, we use a poor man's ASCII to
+ * Unicode-16 translation. The size of the option string as given
+ * to us includes the terminating null character. We assume the
+ * string is an ASCII string if strlen() plus the terminating
+ * '\0' is less than LoadOptionsSize. Even if all Unicode-16
+ * characters have the upper 8 bits non-zero, the terminating
+ * null character will cause a one-off.
+ * If the string is already in Unicode-16, we make a copy so that
+ * we know we can always modify the string.
+ */
+ if (img->LoadOptionsSize > 0 && img->LoadOptions != NULL) {
+ if (img->LoadOptionsSize == strlen(img->LoadOptions) + 1) {
+ args = malloc(img->LoadOptionsSize << 1);
+ for (argc = 0; argc < (int)img->LoadOptionsSize; argc++)
+ args[argc] = ((char*)img->LoadOptions)[argc];
+ } else {
+ args = malloc(img->LoadOptionsSize);
+ memcpy(args, img->LoadOptions, img->LoadOptionsSize);
+ }
+ } else
+ args = NULL;
+
+ /*
+ * Use a quick and dirty algorithm to build the argv vector. We
+ * first count the number of words. Then, after allocating the
+ * vector, we split the string up. We don't deal with quotes or
+ * other more advanced shell features.
+ * The EFI shell will pass the name of the image as the first
+ * word in the argument list. This does not happen if we're
+ * loaded by the boot manager. This is not so easy to figure
+ * out though. The ParentHandle is not always NULL, because
+ * there can be a function (=image) that will perform the task
+ * for the boot manager.
+ */
+ /* Part 1: Figure out if we need to add our program name. */
+ addprog = (args == NULL || img->ParentHandle == NULL ||
+ img->FilePath == NULL) ? 1 : 0;
+ if (!addprog) {
+ addprog =
+ (DevicePathType(img->FilePath) != MEDIA_DEVICE_PATH ||
+ DevicePathSubType(img->FilePath) != MEDIA_FILEPATH_DP ||
+ DevicePathNodeLength(img->FilePath) <=
+ sizeof(FILEPATH_DEVICE_PATH)) ? 1 : 0;
+ if (!addprog) {
+ /* XXX todo. */
+ }
+ }
+ /* Part 2: count words. */
+ argc = (addprog) ? 1 : 0;
+ argp = args;
+ while (argp != NULL && *argp != 0) {
+ argp = arg_skipsep(argp);
+ if (*argp == 0)
+ break;
+ argc++;
+ argp = arg_skipword(argp);
+ }
+ /* Part 3: build vector. */
+ argv = malloc((argc + 1) * sizeof(CHAR16*));
+ argc = 0;
+ if (addprog)
+ argv[argc++] = (CHAR16 *)L"loader.efi";
+ argp = args;
+ while (argp != NULL && *argp != 0) {
+ argp = arg_skipsep(argp);
+ if (*argp == 0)
+ break;
+ argv[argc++] = argp;
+ argp = arg_skipword(argp);
+ /* Terminate the words. */
+ if (*argp != 0)
+ *argp++ = 0;
+ }
+ argv[argc] = NULL;
+
+ status = main(argc, argv);
+ efi_exit(status);
+ return (status);
+}
diff --git a/stand/efi/loader/framebuffer.c b/stand/efi/loader/framebuffer.c
new file mode 100644
index 000000000000..37999ea1d372
--- /dev/null
+++ b/stand/efi/loader/framebuffer.c
@@ -0,0 +1,568 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <bootstrap.h>
+#include <sys/endian.h>
+#include <stand.h>
+
+#include <efi.h>
+#include <efilib.h>
+#include <efiuga.h>
+#include <efipciio.h>
+#include <machine/metadata.h>
+
+#include "framebuffer.h"
+
+static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID;
+static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID;
+
+static u_int
+efifb_color_depth(struct efi_fb *efifb)
+{
+ uint32_t mask;
+ u_int depth;
+
+ mask = efifb->fb_mask_red | efifb->fb_mask_green |
+ efifb->fb_mask_blue | efifb->fb_mask_reserved;
+ if (mask == 0)
+ return (0);
+ for (depth = 1; mask != 1; depth++)
+ mask >>= 1;
+ return (depth);
+}
+
+static int
+efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt,
+ EFI_PIXEL_BITMASK *pixinfo)
+{
+ int result;
+
+ result = 0;
+ switch (pixfmt) {
+ case PixelRedGreenBlueReserved8BitPerColor:
+ efifb->fb_mask_red = 0x000000ff;
+ efifb->fb_mask_green = 0x0000ff00;
+ efifb->fb_mask_blue = 0x00ff0000;
+ efifb->fb_mask_reserved = 0xff000000;
+ break;
+ case PixelBlueGreenRedReserved8BitPerColor:
+ efifb->fb_mask_red = 0x00ff0000;
+ efifb->fb_mask_green = 0x0000ff00;
+ efifb->fb_mask_blue = 0x000000ff;
+ efifb->fb_mask_reserved = 0xff000000;
+ break;
+ case PixelBitMask:
+ efifb->fb_mask_red = pixinfo->RedMask;
+ efifb->fb_mask_green = pixinfo->GreenMask;
+ efifb->fb_mask_blue = pixinfo->BlueMask;
+ efifb->fb_mask_reserved = pixinfo->ReservedMask;
+ break;
+ default:
+ result = 1;
+ break;
+ }
+ return (result);
+}
+
+static int
+efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode,
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
+{
+ int result;
+
+ efifb->fb_addr = mode->FrameBufferBase;
+ efifb->fb_size = mode->FrameBufferSize;
+ efifb->fb_height = info->VerticalResolution;
+ efifb->fb_width = info->HorizontalResolution;
+ efifb->fb_stride = info->PixelsPerScanLine;
+ result = efifb_mask_from_pixfmt(efifb, info->PixelFormat,
+ &info->PixelInformation);
+ return (result);
+}
+
+static ssize_t
+efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, u_int line,
+ EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size)
+{
+ EFI_UGA_PIXEL pix0, pix1;
+ uint8_t *data1, *data2;
+ size_t count, maxcount = 1024;
+ ssize_t ofs;
+ EFI_STATUS status;
+ u_int idx;
+
+ status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer,
+ 0, line, 0, 0, 1, 1, 0);
+ if (EFI_ERROR(status)) {
+ printf("UGA BLT operation failed (video->buffer)");
+ return (-1);
+ }
+ pix1.Red = ~pix0.Red;
+ pix1.Green = ~pix0.Green;
+ pix1.Blue = ~pix0.Blue;
+ pix1.Reserved = 0;
+
+ data1 = calloc(maxcount, 2);
+ if (data1 == NULL) {
+ printf("Unable to allocate memory");
+ return (-1);
+ }
+ data2 = data1 + maxcount;
+
+ ofs = 0;
+ while (size > 0) {
+ count = min(size, maxcount);
+
+ status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32,
+ EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2,
+ data1);
+ if (EFI_ERROR(status)) {
+ printf("Error reading frame buffer (before)");
+ goto fail;
+ }
+ status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo,
+ 0, 0, 0, line, 1, 1, 0);
+ if (EFI_ERROR(status)) {
+ printf("UGA BLT operation failed (modify)");
+ goto fail;
+ }
+ status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32,
+ EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2,
+ data2);
+ if (EFI_ERROR(status)) {
+ printf("Error reading frame buffer (after)");
+ goto fail;
+ }
+ status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo,
+ 0, 0, 0, line, 1, 1, 0);
+ if (EFI_ERROR(status)) {
+ printf("UGA BLT operation failed (restore)");
+ goto fail;
+ }
+ for (idx = 0; idx < count; idx++) {
+ if (data1[idx] != data2[idx]) {
+ free(data1);
+ return (ofs + (idx & ~3));
+ }
+ }
+ ofs += count;
+ size -= count;
+ }
+ printf("No change detected in frame buffer");
+
+ fail:
+ printf(" -- error %lu\n", EFI_ERROR_CODE(status));
+ free(data1);
+ return (-1);
+}
+
+static EFI_PCI_IO_PROTOCOL *
+efifb_uga_get_pciio(void)
+{
+ EFI_PCI_IO_PROTOCOL *pciio;
+ EFI_HANDLE *buf, *hp;
+ EFI_STATUS status;
+ UINTN bufsz;
+
+ /* Get all handles that support the UGA protocol. */
+ bufsz = 0;
+ status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL);
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return (NULL);
+ buf = malloc(bufsz);
+ status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, buf);
+ if (status != EFI_SUCCESS) {
+ free(buf);
+ return (NULL);
+ }
+ bufsz /= sizeof(EFI_HANDLE);
+
+ /* Get the PCI I/O interface of the first handle that supports it. */
+ pciio = NULL;
+ for (hp = buf; hp < buf + bufsz; hp++) {
+ status = BS->HandleProtocol(*hp, &pciio_guid, (void **)&pciio);
+ if (status == EFI_SUCCESS) {
+ free(buf);
+ return (pciio);
+ }
+ }
+ free(buf);
+ return (NULL);
+}
+
+static EFI_STATUS
+efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp,
+ uint64_t *sizep)
+{
+ uint8_t *resattr;
+ uint64_t addr, size;
+ EFI_STATUS status;
+ u_int bar;
+
+ if (pciio == NULL)
+ return (EFI_DEVICE_ERROR);
+
+ /* Attempt to get the frame buffer address (imprecise). */
+ *addrp = 0;
+ *sizep = 0;
+ for (bar = 0; bar < 6; bar++) {
+ status = pciio->GetBarAttributes(pciio, bar, NULL,
+ (void **)&resattr);
+ if (status != EFI_SUCCESS)
+ continue;
+ /* XXX magic offsets and constants. */
+ if (resattr[0] == 0x87 && resattr[3] == 0) {
+ /* 32-bit address space descriptor (MEMIO) */
+ addr = le32dec(resattr + 10);
+ size = le32dec(resattr + 22);
+ } else if (resattr[0] == 0x8a && resattr[3] == 0) {
+ /* 64-bit address space descriptor (MEMIO) */
+ addr = le64dec(resattr + 14);
+ size = le64dec(resattr + 38);
+ } else {
+ addr = 0;
+ size = 0;
+ }
+ BS->FreePool(resattr);
+ if (addr == 0 || size == 0)
+ continue;
+
+ /* We assume the largest BAR is the frame buffer. */
+ if (size > *sizep) {
+ *addrp = addr;
+ *sizep = size;
+ }
+ }
+ return ((*addrp == 0 || *sizep == 0) ? EFI_DEVICE_ERROR : 0);
+}
+
+static int
+efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga)
+{
+ EFI_PCI_IO_PROTOCOL *pciio;
+ char *ev, *p;
+ EFI_STATUS status;
+ ssize_t offset;
+ uint64_t fbaddr;
+ uint32_t horiz, vert, stride;
+ uint32_t np, depth, refresh;
+
+ status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh);
+ if (EFI_ERROR(status))
+ return (1);
+ efifb->fb_height = vert;
+ efifb->fb_width = horiz;
+ /* Paranoia... */
+ if (efifb->fb_height == 0 || efifb->fb_width == 0)
+ return (1);
+
+ /* The color masks are fixed AFAICT. */
+ efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor,
+ NULL);
+
+ /* pciio can be NULL on return! */
+ pciio = efifb_uga_get_pciio();
+
+ /* Try to find the frame buffer. */
+ status = efifb_uga_locate_framebuffer(pciio, &efifb->fb_addr,
+ &efifb->fb_size);
+ if (EFI_ERROR(status)) {
+ efifb->fb_addr = 0;
+ efifb->fb_size = 0;
+ }
+
+ /*
+ * There's no reliable way to detect the frame buffer or the
+ * offset within the frame buffer of the visible region, nor
+ * the stride. Our only option is to look at the system and
+ * fill in the blanks based on that. Luckily, UGA was mostly
+ * only used on Apple hardware.
+ */
+ offset = -1;
+ ev = getenv("smbios.system.maker");
+ if (ev != NULL && !strcmp(ev, "Apple Inc.")) {
+ ev = getenv("smbios.system.product");
+ if (ev != NULL && !strcmp(ev, "iMac7,1")) {
+ /* These are the expected values we should have. */
+ horiz = 1680;
+ vert = 1050;
+ fbaddr = 0xc0000000;
+ /* These are the missing bits. */
+ offset = 0x10000;
+ stride = 1728;
+ } else if (ev != NULL && !strcmp(ev, "MacBook3,1")) {
+ /* These are the expected values we should have. */
+ horiz = 1280;
+ vert = 800;
+ fbaddr = 0xc0000000;
+ /* These are the missing bits. */
+ offset = 0x0;
+ stride = 2048;
+ }
+ }
+
+ /*
+ * If this is hardware we know, make sure that it looks familiar
+ * before we accept our hardcoded values.
+ */
+ if (offset >= 0 && efifb->fb_width == horiz &&
+ efifb->fb_height == vert && efifb->fb_addr == fbaddr) {
+ efifb->fb_addr += offset;
+ efifb->fb_size -= offset;
+ efifb->fb_stride = stride;
+ return (0);
+ } else if (offset >= 0) {
+ printf("Hardware make/model known, but graphics not "
+ "as expected.\n");
+ printf("Console may not work!\n");
+ }
+
+ /*
+ * The stride is equal or larger to the width. Often it's the
+ * next larger power of two. We'll start with that...
+ */
+ efifb->fb_stride = efifb->fb_width;
+ do {
+ np = efifb->fb_stride & (efifb->fb_stride - 1);
+ if (np) {
+ efifb->fb_stride |= (np - 1);
+ efifb->fb_stride++;
+ }
+ } while (np);
+
+ ev = getenv("hw.efifb.address");
+ if (ev == NULL) {
+ if (efifb->fb_addr == 0) {
+ printf("Please set hw.efifb.address and "
+ "hw.efifb.stride.\n");
+ return (1);
+ }
+
+ /*
+ * The visible part of the frame buffer may not start at
+ * offset 0, so try to detect it. Note that we may not
+ * always be able to read from the frame buffer, which
+ * means that we may not be able to detect anything. In
+ * that case, we would take a long time scanning for a
+ * pixel change in the frame buffer, which would have it
+ * appear that we're hanging, so we limit the scan to
+ * 1/256th of the frame buffer. This number is mostly
+ * based on PR 202730 and the fact that on a MacBoook,
+ * where we can't read from the frame buffer the offset
+ * of the visible region is 0. In short: we want to scan
+ * enough to handle all adapters that have an offset
+ * larger than 0 and we want to scan as little as we can
+ * to not appear to hang when we can't read from the
+ * frame buffer.
+ */
+ offset = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr,
+ efifb->fb_size >> 8);
+ if (offset == -1) {
+ printf("Unable to reliably detect frame buffer.\n");
+ } else if (offset > 0) {
+ efifb->fb_addr += offset;
+ efifb->fb_size -= offset;
+ }
+ } else {
+ offset = 0;
+ efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
+ efifb->fb_addr = strtoul(ev, &p, 0);
+ if (*p != '\0')
+ return (1);
+ }
+
+ ev = getenv("hw.efifb.stride");
+ if (ev == NULL) {
+ if (pciio != NULL && offset != -1) {
+ /* Determine the stride. */
+ offset = efifb_uga_find_pixel(uga, 1, pciio,
+ efifb->fb_addr, horiz * 8);
+ if (offset != -1)
+ efifb->fb_stride = offset >> 2;
+ } else {
+ printf("Unable to reliably detect the stride.\n");
+ }
+ } else {
+ efifb->fb_stride = strtoul(ev, &p, 0);
+ if (*p != '\0')
+ return (1);
+ }
+
+ /*
+ * We finalized on the stride, so recalculate the size of the
+ * frame buffer.
+ */
+ efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
+ return (0);
+}
+
+int
+efi_find_framebuffer(struct efi_fb *efifb)
+{
+ EFI_GRAPHICS_OUTPUT *gop;
+ EFI_UGA_DRAW_PROTOCOL *uga;
+ EFI_STATUS status;
+
+ status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop);
+ if (status == EFI_SUCCESS)
+ return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info));
+
+ status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
+ if (status == EFI_SUCCESS)
+ return (efifb_from_uga(efifb, uga));
+
+ return (1);
+}
+
+static void
+print_efifb(int mode, struct efi_fb *efifb, int verbose)
+{
+ u_int depth;
+
+ if (mode >= 0)
+ printf("mode %d: ", mode);
+ depth = efifb_color_depth(efifb);
+ printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height,
+ depth, efifb->fb_stride);
+ if (verbose) {
+ printf("\n frame buffer: address=%jx, size=%jx",
+ (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size);
+ printf("\n color mask: R=%08x, G=%08x, B=%08x\n",
+ efifb->fb_mask_red, efifb->fb_mask_green,
+ efifb->fb_mask_blue);
+ }
+}
+
+COMMAND_SET(gop, "gop", "graphics output protocol", command_gop);
+
+static int
+command_gop(int argc, char *argv[])
+{
+ struct efi_fb efifb;
+ EFI_GRAPHICS_OUTPUT *gop;
+ EFI_STATUS status;
+ u_int mode;
+
+ status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop);
+ if (EFI_ERROR(status)) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "%s: Graphics Output Protocol not present (error=%lu)",
+ argv[0], EFI_ERROR_CODE(status));
+ return (CMD_ERROR);
+ }
+
+ if (argc < 2)
+ goto usage;
+
+ if (!strcmp(argv[1], "set")) {
+ char *cp;
+
+ if (argc != 3)
+ goto usage;
+ mode = strtol(argv[2], &cp, 0);
+ if (cp[0] != '\0') {
+ sprintf(command_errbuf, "mode is an integer");
+ return (CMD_ERROR);
+ }
+ status = gop->SetMode(gop, mode);
+ if (EFI_ERROR(status)) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "%s: Unable to set mode to %u (error=%lu)",
+ argv[0], mode, EFI_ERROR_CODE(status));
+ return (CMD_ERROR);
+ }
+ } else if (!strcmp(argv[1], "get")) {
+ if (argc != 2)
+ goto usage;
+ efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
+ print_efifb(gop->Mode->Mode, &efifb, 1);
+ printf("\n");
+ } else if (!strcmp(argv[1], "list")) {
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
+ UINTN infosz;
+
+ if (argc != 2)
+ goto usage;
+ pager_open();
+ for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
+ status = gop->QueryMode(gop, mode, &infosz, &info);
+ if (EFI_ERROR(status))
+ continue;
+ efifb_from_gop(&efifb, gop->Mode, info);
+ print_efifb(mode, &efifb, 0);
+ if (pager_output("\n"))
+ break;
+ }
+ pager_close();
+ }
+ return (CMD_OK);
+
+ usage:
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "usage: %s [list | get | set <mode>]", argv[0]);
+ return (CMD_ERROR);
+}
+
+COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga);
+
+static int
+command_uga(int argc, char *argv[])
+{
+ struct efi_fb efifb;
+ EFI_UGA_DRAW_PROTOCOL *uga;
+ EFI_STATUS status;
+
+ status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
+ if (EFI_ERROR(status)) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "%s: UGA Protocol not present (error=%lu)",
+ argv[0], EFI_ERROR_CODE(status));
+ return (CMD_ERROR);
+ }
+
+ if (argc != 1)
+ goto usage;
+
+ if (efifb_from_uga(&efifb, uga) != CMD_OK) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "%s: Unable to get UGA information", argv[0]);
+ return (CMD_ERROR);
+ }
+
+ print_efifb(-1, &efifb, 1);
+ printf("\n");
+ return (CMD_OK);
+
+ usage:
+ snprintf(command_errbuf, sizeof(command_errbuf), "usage: %s", argv[0]);
+ return (CMD_ERROR);
+}
diff --git a/stand/efi/loader/framebuffer.h b/stand/efi/loader/framebuffer.h
new file mode 100644
index 000000000000..2ec9017dc3ee
--- /dev/null
+++ b/stand/efi/loader/framebuffer.h
@@ -0,0 +1,36 @@
+/*-
+ * 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$
+ */
+
+#ifndef _EFIFB_H_
+#define _EFIFB_H_
+
+int efi_find_framebuffer(struct efi_fb *efifb);
+
+#endif /* _EFIFB_H_ */
diff --git a/stand/efi/loader/loader_efi.h b/stand/efi/loader/loader_efi.h
new file mode 100644
index 000000000000..780fbfe4c6aa
--- /dev/null
+++ b/stand/efi/loader/loader_efi.h
@@ -0,0 +1,47 @@
+/*-
+ * 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$
+ */
+
+#ifndef _LOADER_EFI_COPY_H_
+#define _LOADER_EFI_COPY_H_
+
+#include <stand.h>
+
+int efi_autoload(void);
+
+int efi_copy_init(void);
+
+ssize_t efi_copyin(const void *src, vm_offset_t dest, const size_t len);
+ssize_t efi_copyout(const vm_offset_t src, void *dest, const size_t len);
+ssize_t efi_readin(const int fd, vm_offset_t dest, const size_t len);
+void * efi_translate(vm_offset_t ptr);
+
+void efi_copy_finish(void);
+
+#endif /* _LOADER_EFI_COPY_H_ */
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
new file mode 100644
index 000000000000..129fd72068ba
--- /dev/null
+++ b/stand/efi/loader/main.c
@@ -0,0 +1,936 @@
+/*-
+ * Copyright (c) 2008-2010 Rui Paulo
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 ``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 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 <sys/disk.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/boot.h>
+#include <inttypes.h>
+#include <stand.h>
+#include <string.h>
+#include <setjmp.h>
+#include <disk.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <uuid.h>
+
+#include <bootstrap.h>
+#include <smbios.h>
+
+#ifdef EFI_ZFS_BOOT
+#include <libzfs.h>
+
+#include "efizfs.h"
+#endif
+
+#include "loader_efi.h"
+
+extern char bootprog_info[];
+
+struct arch_switch archsw; /* MI/MD interface boundary */
+
+EFI_GUID acpi = ACPI_TABLE_GUID;
+EFI_GUID acpi20 = ACPI_20_TABLE_GUID;
+EFI_GUID devid = DEVICE_PATH_PROTOCOL;
+EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
+EFI_GUID mps = MPS_TABLE_GUID;
+EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL;
+EFI_GUID smbios = SMBIOS_TABLE_GUID;
+EFI_GUID dxe = DXE_SERVICES_TABLE_GUID;
+EFI_GUID hoblist = HOB_LIST_TABLE_GUID;
+EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID;
+EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID;
+EFI_GUID fdtdtb = FDT_TABLE_GUID;
+EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
+
+static EFI_LOADED_IMAGE *img;
+
+#ifdef EFI_ZFS_BOOT
+bool
+efi_zfs_is_preferred(EFI_HANDLE *h)
+{
+ return (h == img->DeviceHandle);
+}
+#endif
+
+static int
+has_keyboard(void)
+{
+ EFI_STATUS status;
+ EFI_DEVICE_PATH *path;
+ EFI_HANDLE *hin, *hin_end, *walker;
+ UINTN sz;
+ int retval = 0;
+
+ /*
+ * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
+ * do the typical dance to get the right sized buffer.
+ */
+ sz = 0;
+ hin = NULL;
+ status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0);
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ hin = (EFI_HANDLE *)malloc(sz);
+ status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz,
+ hin);
+ if (EFI_ERROR(status))
+ free(hin);
+ }
+ if (EFI_ERROR(status))
+ return retval;
+
+ /*
+ * Look at each of the handles. If it supports the device path protocol,
+ * use it to get the device path for this handle. Then see if that
+ * device path matches either the USB device path for keyboards or the
+ * legacy device path for keyboards.
+ */
+ hin_end = &hin[sz / sizeof(*hin)];
+ for (walker = hin; walker < hin_end; walker++) {
+ status = BS->HandleProtocol(*walker, &devid, (VOID **)&path);
+ if (EFI_ERROR(status))
+ continue;
+
+ while (!IsDevicePathEnd(path)) {
+ /*
+ * Check for the ACPI keyboard node. All PNP3xx nodes
+ * are keyboards of different flavors. Note: It is
+ * unclear of there's always a keyboard node when
+ * there's a keyboard controller, or if there's only one
+ * when a keyboard is detected at boot.
+ */
+ if (DevicePathType(path) == ACPI_DEVICE_PATH &&
+ (DevicePathSubType(path) == ACPI_DP ||
+ DevicePathSubType(path) == ACPI_EXTENDED_DP)) {
+ ACPI_HID_DEVICE_PATH *acpi;
+
+ acpi = (ACPI_HID_DEVICE_PATH *)(void *)path;
+ if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 &&
+ (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) {
+ retval = 1;
+ goto out;
+ }
+ /*
+ * Check for USB keyboard node, if present. Unlike a
+ * PS/2 keyboard, these definitely only appear when
+ * connected to the system.
+ */
+ } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH &&
+ DevicePathSubType(path) == MSG_USB_CLASS_DP) {
+ USB_CLASS_DEVICE_PATH *usb;
+
+ usb = (USB_CLASS_DEVICE_PATH *)(void *)path;
+ if (usb->DeviceClass == 3 && /* HID */
+ usb->DeviceSubClass == 1 && /* Boot devices */
+ usb->DeviceProtocol == 1) { /* Boot keyboards */
+ retval = 1;
+ goto out;
+ }
+ }
+ path = NextDevicePathNode(path);
+ }
+ }
+out:
+ free(hin);
+ return retval;
+}
+
+static void
+set_devdesc_currdev(struct devsw *dev, int unit)
+{
+ struct devdesc currdev;
+ char *devname;
+
+ currdev.d_dev = dev;
+ currdev.d_type = currdev.d_dev->dv_type;
+ currdev.d_unit = unit;
+ currdev.d_opendata = NULL;
+ devname = efi_fmtdev(&currdev);
+
+ env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
+ env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset);
+}
+
+static int
+find_currdev(EFI_LOADED_IMAGE *img)
+{
+ pdinfo_list_t *pdi_list;
+ pdinfo_t *dp, *pp;
+ EFI_DEVICE_PATH *devpath, *copy;
+ EFI_HANDLE h;
+ char *devname;
+ struct devsw *dev;
+ int unit;
+ uint64_t extra;
+
+#ifdef EFI_ZFS_BOOT
+ /* Did efi_zfs_probe() detect the boot pool? */
+ if (pool_guid != 0) {
+ struct zfs_devdesc currdev;
+
+ currdev.d_dev = &zfs_dev;
+ currdev.d_unit = 0;
+ currdev.d_type = currdev.d_dev->dv_type;
+ currdev.d_opendata = NULL;
+ currdev.pool_guid = pool_guid;
+ currdev.root_guid = 0;
+ devname = efi_fmtdev(&currdev);
+
+ env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
+ env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, devname, env_noset,
+ env_nounset);
+ init_zfs_bootenv(devname);
+ return (0);
+ }
+#endif /* EFI_ZFS_BOOT */
+
+ /* We have device lists for hd, cd, fd, walk them all. */
+ pdi_list = efiblk_get_pdinfo_list(&efipart_hddev);
+ STAILQ_FOREACH(dp, pdi_list, pd_link) {
+ struct disk_devdesc currdev;
+
+ currdev.d_dev = &efipart_hddev;
+ currdev.d_type = currdev.d_dev->dv_type;
+ currdev.d_unit = dp->pd_unit;
+ currdev.d_opendata = NULL;
+ currdev.d_slice = -1;
+ currdev.d_partition = -1;
+
+ if (dp->pd_handle == img->DeviceHandle) {
+ devname = efi_fmtdev(&currdev);
+
+ env_setenv("currdev", EV_VOLATILE, devname,
+ efi_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, devname,
+ env_noset, env_nounset);
+ return (0);
+ }
+ /* Assuming GPT partitioning. */
+ STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
+ if (pp->pd_handle == img->DeviceHandle) {
+ currdev.d_slice = pp->pd_unit;
+ currdev.d_partition = 255;
+ devname = efi_fmtdev(&currdev);
+
+ env_setenv("currdev", EV_VOLATILE, devname,
+ efi_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, devname,
+ env_noset, env_nounset);
+ return (0);
+ }
+ }
+ }
+
+ pdi_list = efiblk_get_pdinfo_list(&efipart_cddev);
+ STAILQ_FOREACH(dp, pdi_list, pd_link) {
+ if (dp->pd_handle == img->DeviceHandle ||
+ dp->pd_alias == img->DeviceHandle) {
+ set_devdesc_currdev(&efipart_cddev, dp->pd_unit);
+ return (0);
+ }
+ }
+
+ pdi_list = efiblk_get_pdinfo_list(&efipart_fddev);
+ STAILQ_FOREACH(dp, pdi_list, pd_link) {
+ if (dp->pd_handle == img->DeviceHandle) {
+ set_devdesc_currdev(&efipart_fddev, dp->pd_unit);
+ return (0);
+ }
+ }
+
+ /*
+ * Try the device handle from our loaded image first. If that
+ * fails, use the device path from the loaded image and see if
+ * any of the nodes in that path match one of the enumerated
+ * handles.
+ */
+ if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) {
+ set_devdesc_currdev(dev, unit);
+ return (0);
+ }
+
+ copy = NULL;
+ devpath = efi_lookup_image_devpath(IH);
+ while (devpath != NULL) {
+ h = efi_devpath_handle(devpath);
+ if (h == NULL)
+ break;
+
+ free(copy);
+ copy = NULL;
+
+ if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) {
+ set_devdesc_currdev(dev, unit);
+ return (0);
+ }
+
+ devpath = efi_lookup_devpath(h);
+ if (devpath != NULL) {
+ copy = efi_devpath_trim(devpath);
+ devpath = copy;
+ }
+ }
+ free(copy);
+
+ return (ENOENT);
+}
+
+EFI_STATUS
+main(int argc, CHAR16 *argv[])
+{
+ char var[128];
+ EFI_GUID *guid;
+ int i, j, vargood, howto;
+ UINTN k;
+ int has_kbd;
+#if !defined(__arm__)
+ char buf[40];
+#endif
+
+ archsw.arch_autoload = efi_autoload;
+ archsw.arch_getdev = efi_getdev;
+ archsw.arch_copyin = efi_copyin;
+ archsw.arch_copyout = efi_copyout;
+ archsw.arch_readin = efi_readin;
+#ifdef EFI_ZFS_BOOT
+ /* Note this needs to be set before ZFS init. */
+ archsw.arch_zfs_probe = efi_zfs_probe;
+#endif
+
+ /* Get our loaded image protocol interface structure. */
+ BS->HandleProtocol(IH, &imgid, (VOID**)&img);
+
+ /* Init the time source */
+ efi_time_init();
+
+ has_kbd = has_keyboard();
+
+ /*
+ * XXX Chicken-and-egg problem; we want to have console output
+ * early, but some console attributes may depend on reading from
+ * eg. the boot device, which we can't do yet. We can use
+ * printf() etc. once this is done.
+ */
+ cons_probe();
+
+ /*
+ * Initialise the block cache. Set the upper limit.
+ */
+ bcache_init(32768, 512);
+
+ /*
+ * Parse the args to set the console settings, etc
+ * boot1.efi passes these in, if it can read /boot.config or /boot/config
+ * or iPXE may be setup to pass these in.
+ *
+ * Loop through the args, and for each one that contains an '=' that is
+ * not the first character, add it to the environment. This allows
+ * loader and kernel env vars to be passed on the command line. Convert
+ * args from UCS-2 to ASCII (16 to 8 bit) as they are copied.
+ */
+ howto = 0;
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ for (j = 1; argv[i][j] != 0; j++) {
+ int ch;
+
+ ch = argv[i][j];
+ switch (ch) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'P':
+ if (!has_kbd)
+ howto |= RB_SERIAL | RB_MULTIPLE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'S':
+ if (argv[i][j + 1] == 0) {
+ if (i + 1 == argc) {
+ setenv("comconsole_speed", "115200", 1);
+ } else {
+ cpy16to8(&argv[i + 1][0], var,
+ sizeof(var));
+ setenv("comconsole_speed", var, 1);
+ }
+ i++;
+ break;
+ } else {
+ cpy16to8(&argv[i][j + 1], var,
+ sizeof(var));
+ setenv("comconsole_speed", var, 1);
+ break;
+ }
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ }
+ }
+ } else {
+ vargood = 0;
+ for (j = 0; argv[i][j] != 0; j++) {
+ if (j == sizeof(var)) {
+ vargood = 0;
+ break;
+ }
+ if (j > 0 && argv[i][j] == '=')
+ vargood = 1;
+ var[j] = (char)argv[i][j];
+ }
+ if (vargood) {
+ var[j] = 0;
+ putenv(var);
+ }
+ }
+ }
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (howto & howto_names[i].mask)
+ setenv(howto_names[i].ev, "YES", 1);
+ if (howto & RB_MULTIPLE) {
+ if (howto & RB_SERIAL)
+ setenv("console", "comconsole efi" , 1);
+ else
+ setenv("console", "efi comconsole" , 1);
+ } else if (howto & RB_SERIAL) {
+ setenv("console", "comconsole" , 1);
+ }
+
+ if (efi_copy_init()) {
+ printf("failed to allocate staging area\n");
+ return (EFI_BUFFER_TOO_SMALL);
+ }
+
+ /*
+ * March through the device switch probing for things.
+ */
+ for (i = 0; devsw[i] != NULL; i++)
+ if (devsw[i]->dv_init != NULL)
+ (devsw[i]->dv_init)();
+
+ printf("Command line arguments:");
+ for (i = 0; i < argc; i++)
+ printf(" %S", argv[i]);
+ printf("\n");
+
+ printf("Image base: 0x%lx\n", (u_long)img->ImageBase);
+ printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
+ ST->Hdr.Revision & 0xffff);
+ printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
+ ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+
+ printf("\n%s", bootprog_info);
+
+ /*
+ * Disable the watchdog timer. By default the boot manager sets
+ * the timer to 5 minutes before invoking a boot option. If we
+ * want to return to the boot manager, we have to disable the
+ * watchdog timer and since we're an interactive program, we don't
+ * want to wait until the user types "quit". The timer may have
+ * fired by then. We don't care if this fails. It does not prevent
+ * normal functioning in any way...
+ */
+ BS->SetWatchdogTimer(0, 0, 0, NULL);
+
+ if (find_currdev(img) != 0)
+ return (EFI_NOT_FOUND);
+
+ efi_init_environment();
+ setenv("LINES", "24", 1); /* optional */
+
+ for (k = 0; k < ST->NumberOfTableEntries; k++) {
+ guid = &ST->ConfigurationTable[k].VendorGuid;
+#if !defined(__arm__)
+ if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) {
+ snprintf(buf, sizeof(buf), "%p",
+ ST->ConfigurationTable[k].VendorTable);
+ setenv("hint.smbios.0.mem", buf, 1);
+ smbios_detect(ST->ConfigurationTable[k].VendorTable);
+ break;
+ }
+#endif
+ }
+
+ interact(NULL); /* doesn't return */
+
+ return (EFI_SUCCESS); /* keep compiler happy */
+}
+
+COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
+
+static int
+command_reboot(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; devsw[i] != NULL; ++i)
+ if (devsw[i]->dv_cleanup != NULL)
+ (devsw[i]->dv_cleanup)();
+
+ RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
+
+ /* NOTREACHED */
+ return (CMD_ERROR);
+}
+
+COMMAND_SET(quit, "quit", "exit the loader", command_quit);
+
+static int
+command_quit(int argc, char *argv[])
+{
+ exit(0);
+ return (CMD_OK);
+}
+
+COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);
+
+static int
+command_memmap(int argc, char *argv[])
+{
+ UINTN sz;
+ EFI_MEMORY_DESCRIPTOR *map, *p;
+ UINTN key, dsz;
+ UINT32 dver;
+ EFI_STATUS status;
+ int i, ndesc;
+ char line[80];
+ static char *types[] = {
+ "Reserved",
+ "LoaderCode",
+ "LoaderData",
+ "BootServicesCode",
+ "BootServicesData",
+ "RuntimeServicesCode",
+ "RuntimeServicesData",
+ "ConventionalMemory",
+ "UnusableMemory",
+ "ACPIReclaimMemory",
+ "ACPIMemoryNVS",
+ "MemoryMappedIO",
+ "MemoryMappedIOPortSpace",
+ "PalCode"
+ };
+
+ sz = 0;
+ status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
+ if (status != EFI_BUFFER_TOO_SMALL) {
+ printf("Can't determine memory map size\n");
+ return (CMD_ERROR);
+ }
+ map = malloc(sz);
+ status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
+ if (EFI_ERROR(status)) {
+ printf("Can't read memory map\n");
+ return (CMD_ERROR);
+ }
+
+ ndesc = sz / dsz;
+ snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n",
+ "Type", "Physical", "Virtual", "#Pages", "Attr");
+ pager_open();
+ if (pager_output(line)) {
+ pager_close();
+ return (CMD_OK);
+ }
+
+ for (i = 0, p = map; i < ndesc;
+ i++, p = NextMemoryDescriptor(p, dsz)) {
+ printf("%23s %012jx %012jx %08jx ", types[p->Type],
+ (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart,
+ (uintmax_t)p->NumberOfPages);
+ if (p->Attribute & EFI_MEMORY_UC)
+ printf("UC ");
+ if (p->Attribute & EFI_MEMORY_WC)
+ printf("WC ");
+ if (p->Attribute & EFI_MEMORY_WT)
+ printf("WT ");
+ if (p->Attribute & EFI_MEMORY_WB)
+ printf("WB ");
+ if (p->Attribute & EFI_MEMORY_UCE)
+ printf("UCE ");
+ if (p->Attribute & EFI_MEMORY_WP)
+ printf("WP ");
+ if (p->Attribute & EFI_MEMORY_RP)
+ printf("RP ");
+ if (p->Attribute & EFI_MEMORY_XP)
+ printf("XP ");
+ if (pager_output("\n"))
+ break;
+ }
+
+ pager_close();
+ return (CMD_OK);
+}
+
+COMMAND_SET(configuration, "configuration", "print configuration tables",
+ command_configuration);
+
+static const char *
+guid_to_string(EFI_GUID *guid)
+{
+ static char buf[40];
+
+ sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ guid->Data1, guid->Data2, guid->Data3, guid->Data4[0],
+ guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4],
+ guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+ return (buf);
+}
+
+static int
+command_configuration(int argc, char *argv[])
+{
+ char line[80];
+ UINTN i;
+
+ snprintf(line, sizeof(line), "NumberOfTableEntries=%lu\n",
+ (unsigned long)ST->NumberOfTableEntries);
+ pager_open();
+ if (pager_output(line)) {
+ pager_close();
+ return (CMD_OK);
+ }
+
+ for (i = 0; i < ST->NumberOfTableEntries; i++) {
+ EFI_GUID *guid;
+
+ printf(" ");
+ guid = &ST->ConfigurationTable[i].VendorGuid;
+ if (!memcmp(guid, &mps, sizeof(EFI_GUID)))
+ printf("MPS Table");
+ else if (!memcmp(guid, &acpi, sizeof(EFI_GUID)))
+ printf("ACPI Table");
+ else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID)))
+ printf("ACPI 2.0 Table");
+ else if (!memcmp(guid, &smbios, sizeof(EFI_GUID)))
+ printf("SMBIOS Table %p",
+ ST->ConfigurationTable[i].VendorTable);
+ else if (!memcmp(guid, &dxe, sizeof(EFI_GUID)))
+ printf("DXE Table");
+ else if (!memcmp(guid, &hoblist, sizeof(EFI_GUID)))
+ printf("HOB List Table");
+ else if (!memcmp(guid, &memtype, sizeof(EFI_GUID)))
+ printf("Memory Type Information Table");
+ else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID)))
+ printf("Debug Image Info Table");
+ else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID)))
+ printf("FDT Table");
+ else
+ printf("Unknown Table (%s)", guid_to_string(guid));
+ snprintf(line, sizeof(line), " at %p\n",
+ ST->ConfigurationTable[i].VendorTable);
+ if (pager_output(line))
+ break;
+ }
+
+ pager_close();
+ return (CMD_OK);
+}
+
+
+COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode);
+
+static int
+command_mode(int argc, char *argv[])
+{
+ UINTN cols, rows;
+ unsigned int mode;
+ int i;
+ char *cp;
+ char rowenv[8];
+ EFI_STATUS status;
+ SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
+ extern void HO(void);
+
+ conout = ST->ConOut;
+
+ if (argc > 1) {
+ mode = strtol(argv[1], &cp, 0);
+ if (cp[0] != '\0') {
+ printf("Invalid mode\n");
+ return (CMD_ERROR);
+ }
+ status = conout->QueryMode(conout, mode, &cols, &rows);
+ if (EFI_ERROR(status)) {
+ printf("invalid mode %d\n", mode);
+ return (CMD_ERROR);
+ }
+ status = conout->SetMode(conout, mode);
+ if (EFI_ERROR(status)) {
+ printf("couldn't set mode %d\n", mode);
+ return (CMD_ERROR);
+ }
+ sprintf(rowenv, "%u", (unsigned)rows);
+ setenv("LINES", rowenv, 1);
+ HO(); /* set cursor */
+ return (CMD_OK);
+ }
+
+ printf("Current mode: %d\n", conout->Mode->Mode);
+ for (i = 0; i <= conout->Mode->MaxMode; i++) {
+ status = conout->QueryMode(conout, i, &cols, &rows);
+ if (EFI_ERROR(status))
+ continue;
+ printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols,
+ (unsigned)rows);
+ }
+
+ if (i != 0)
+ printf("Select a mode with the command \"mode <number>\"\n");
+
+ return (CMD_OK);
+}
+
+#ifdef EFI_ZFS_BOOT
+COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
+ command_lszfs);
+
+static int
+command_lszfs(int argc, char *argv[])
+{
+ int err;
+
+ if (argc != 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ err = zfs_list(argv[1]);
+ if (err != 0) {
+ command_errmsg = strerror(err);
+ return (CMD_ERROR);
+ }
+ return (CMD_OK);
+}
+
+COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
+ command_reloadbe);
+
+static int
+command_reloadbe(int argc, char *argv[])
+{
+ int err;
+ char *root;
+
+ if (argc > 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ if (argc == 2) {
+ err = zfs_bootenv(argv[1]);
+ } else {
+ root = getenv("zfs_be_root");
+ if (root == NULL) {
+ return (CMD_OK);
+ }
+ err = zfs_bootenv(root);
+ }
+
+ if (err != 0) {
+ command_errmsg = strerror(err);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_OK);
+}
+#endif
+
+#ifdef LOADER_FDT_SUPPORT
+extern int command_fdt_internal(int argc, char *argv[]);
+
+/*
+ * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
+ * and declaring it as extern is in contradiction with COMMAND_SET() macro
+ * (which uses static pointer), we're defining wrapper function, which
+ * calls the proper fdt handling routine.
+ */
+static int
+command_fdt(int argc, char *argv[])
+{
+
+ return (command_fdt_internal(argc, argv));
+}
+
+COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
+#endif
+
+/*
+ * Chain load another efi loader.
+ */
+static int
+command_chain(int argc, char *argv[])
+{
+ EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
+ EFI_HANDLE loaderhandle;
+ EFI_LOADED_IMAGE *loaded_image;
+ EFI_STATUS status;
+ struct stat st;
+ struct devdesc *dev;
+ char *name, *path;
+ void *buf;
+ int fd;
+
+ if (argc < 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ name = argv[1];
+
+ if ((fd = open(name, O_RDONLY)) < 0) {
+ command_errmsg = "no such file";
+ return (CMD_ERROR);
+ }
+
+ if (fstat(fd, &st) < -1) {
+ command_errmsg = "stat failed";
+ close(fd);
+ return (CMD_ERROR);
+ }
+
+ status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "failed to allocate buffer";
+ close(fd);
+ return (CMD_ERROR);
+ }
+ if (read(fd, buf, st.st_size) != st.st_size) {
+ command_errmsg = "error while reading the file";
+ (void)BS->FreePool(buf);
+ close(fd);
+ return (CMD_ERROR);
+ }
+ close(fd);
+ status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle);
+ (void)BS->FreePool(buf);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "LoadImage failed";
+ return (CMD_ERROR);
+ }
+ status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
+ (void **)&loaded_image);
+
+ if (argc > 2) {
+ int i, len = 0;
+ CHAR16 *argp;
+
+ for (i = 2; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+
+ len *= sizeof (*argp);
+ loaded_image->LoadOptions = argp = malloc (len);
+ loaded_image->LoadOptionsSize = len;
+ for (i = 2; i < argc; i++) {
+ char *ptr = argv[i];
+ while (*ptr)
+ *(argp++) = *(ptr++);
+ *(argp++) = ' ';
+ }
+ *(--argv) = 0;
+ }
+
+ if (efi_getdev((void **)&dev, name, (const char **)&path) == 0) {
+#ifdef EFI_ZFS_BOOT
+ struct zfs_devdesc *z_dev;
+#endif
+ struct disk_devdesc *d_dev;
+ pdinfo_t *hd, *pd;
+
+ switch (dev->d_type) {
+#ifdef EFI_ZFS_BOOT
+ case DEVT_ZFS:
+ z_dev = (struct zfs_devdesc *)dev;
+ loaded_image->DeviceHandle =
+ efizfs_get_handle_by_guid(z_dev->pool_guid);
+ break;
+#endif
+ case DEVT_NET:
+ loaded_image->DeviceHandle =
+ efi_find_handle(dev->d_dev, dev->d_unit);
+ break;
+ default:
+ hd = efiblk_get_pdinfo(dev);
+ if (STAILQ_EMPTY(&hd->pd_part)) {
+ loaded_image->DeviceHandle = hd->pd_handle;
+ break;
+ }
+ d_dev = (struct disk_devdesc *)dev;
+ STAILQ_FOREACH(pd, &hd->pd_part, pd_link) {
+ /*
+ * d_partition should be 255
+ */
+ if (pd->pd_unit == (uint32_t)d_dev->d_slice) {
+ loaded_image->DeviceHandle =
+ pd->pd_handle;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ dev_cleanup();
+ status = BS->StartImage(loaderhandle, NULL, NULL);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "StartImage failed";
+ free(loaded_image->LoadOptions);
+ loaded_image->LoadOptions = NULL;
+ status = BS->UnloadImage(loaded_image);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_ERROR); /* not reached */
+}
+
+COMMAND_SET(chain, "chain", "chain load file", command_chain);
diff --git a/stand/efi/loader/version b/stand/efi/loader/version
new file mode 100644
index 000000000000..3a4c47c5efb1
--- /dev/null
+++ b/stand/efi/loader/version
@@ -0,0 +1,7 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+1.1: Keep in sync with i386 version.
+0.1: Initial i386 version. Derived from ia64.