Date: Fri, 4 Apr 2014 00:16:47 +0000 (UTC) From: Ed Maste <emaste@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r264095 - in head/sys/boot: amd64 amd64/efi efi/include i386/efi Message-ID: <201404040016.s340GlJn072315@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: emaste Date: Fri Apr 4 00:16:46 2014 New Revision: 264095 URL: http://svnweb.freebsd.org/changeset/base/264095 Log: Support UEFI booting on amd64 via loader.efi This is largely the work from the projects/uefi branch, with some additional refinements. This is derived from (and replaces) the original i386 efi implementation; i386 support will be restored later. Specific revisions of note from projects/uefi: r247380: Adjust our load device when we boot from CD under UEFI. The process for booting from a CD under UEFI involves adding a FAT filesystem containing your loader code as an El Torito boot image. When UEFI detects this, it provides a block IO instance that points at the FAT filesystem as a child of the device that represents the CD itself. The problem being that the CD device is flagged as a "raw device" while the boot image is flagged as a "logical partition". The existing EFI partition code only looks for logical partitions and so the CD filesystem was rendered invisible. To fix this, check the type of each block IO device. If it's found to be a CD, and thus an El Torito boot image, look up its parent device and add that instead so that the loader will then load the kernel from the CD filesystem. This is done by using the handle for the boot filesystem as an alias. Something similar to this will be required for booting from other media as well as the loader will live in the EFI system partition, not on the partition containing the kernel. r246231: Add necessary code to hand off from loader to an amd64 kernel. r246335: Grab the EFI memory map and store it as module metadata on the kernel. This is the same approach used to provide the BIOS SMAP to the kernel. r246336: Pass the ACPI table metadata via hints so the kernel ACPI code can find them. r246608: Rework copy routines to ensure we always use memory allocated via EFI. The previous code assumed it could copy wherever it liked. This is not the case. The approach taken by this code is pretty ham-fisted in that it simply allocates a large (32MB) buffer area and stages into that, then copies the whole area into place when it's time to execute. A more elegant solution could be used but this works for now. r247214: Fix a number of problems preventing proper handover to the kernel. There were two issues at play here. Firstly, there was nothing preventing UEFI from placing the loader code above 1GB in RAM. This meant that when we switched in the page tables the kernel expects to be running on, we are suddenly unmapped and things no longer work. We solve this by making our trampoline code not dependent on being at any given position and simply copying it to a "safe" location before calling it. Secondly, UEFI could allocate our stack wherever it wants. As it happened on my PC, that was right where I was copying the kernel to. This did not cause happiness. The solution to this was to also switch to a temporary stack in a safe location before performing the final copy of the loaded kernel. r246231: Add necessary code to hand off from loader to an amd64 kernel. r246335: Grab the EFI memory map and store it as module metadata on the kernel. This is the same approach used to provide the BIOS SMAP to the kernel. r246336: Pass the ACPI table metadata via hints so the kernel ACPI code can find them. r246608: Rework copy routines to ensure we always use memory allocated via EFI. The previous code assumed it could copy wherever it liked. This is not the case. The approach taken by this code is pretty ham-fisted in that it simply allocates a large (32MB) buffer area and stages into that, then copies the whole area into place when it's time to execute. A more elegant solution could be used but this works for now. r247214: Fix a number of problems preventing proper handover to the kernel. There were two issues at play here. Firstly, there was nothing preventing UEFI from placing the loader code above 1GB in RAM. This meant that when we switched in the page tables the kernel expects to be running on, we are suddenly unmapped and things no longer work. We solve this by making our trampoline code not dependent on being at any given position and simply copying it to a "safe" location before calling it. Secondly, UEFI could allocate our stack wherever it wants. As it happened on my PC, that was right where I was copying the kernel to. This did not cause happiness. The solution to this was to also switch to a temporary stack in a safe location before performing the final copy of the loaded kernel. r247216: Use the UEFI Graphics Output Protocol to get the parameters of the framebuffer. Sponsored by: The FreeBSD Foundation Added: head/sys/boot/amd64/ head/sys/boot/amd64/Makefile (contents, props changed) head/sys/boot/amd64/Makefile.inc (contents, props changed) head/sys/boot/amd64/efi/ head/sys/boot/amd64/efi/Makefile - copied, changed from r264093, head/sys/boot/i386/efi/Makefile head/sys/boot/amd64/efi/amd64_tramp.S (contents, props changed) head/sys/boot/amd64/efi/autoload.c - copied, changed from r264093, head/sys/boot/i386/efi/autoload.c head/sys/boot/amd64/efi/bootinfo.c (contents, props changed) head/sys/boot/amd64/efi/conf.c - copied, changed from r264093, head/sys/boot/i386/efi/conf.c head/sys/boot/amd64/efi/copy.c (contents, props changed) head/sys/boot/amd64/efi/devicename.c - copied, changed from r264093, head/sys/boot/i386/efi/devicename.c head/sys/boot/amd64/efi/elf64_freebsd.c (contents, props changed) head/sys/boot/amd64/efi/framebuffer.c (contents, props changed) head/sys/boot/amd64/efi/framebuffer.h (contents, props changed) head/sys/boot/amd64/efi/ldscript.amd64 - copied, changed from r264093, head/sys/boot/i386/efi/ldscript.amd64 head/sys/boot/amd64/efi/main.c - copied, changed from r264093, head/sys/boot/i386/efi/main.c head/sys/boot/amd64/efi/reloc.c - copied unchanged from r264093, head/sys/boot/i386/efi/reloc.c head/sys/boot/amd64/efi/start.S (contents, props changed) head/sys/boot/amd64/efi/version - copied unchanged from r264093, head/sys/boot/i386/efi/version head/sys/boot/amd64/efi/x86_efi.h (contents, props changed) head/sys/boot/efi/include/efigop.h (contents, props changed) Deleted: head/sys/boot/i386/efi/Makefile head/sys/boot/i386/efi/autoload.c head/sys/boot/i386/efi/conf.c head/sys/boot/i386/efi/devicename.c head/sys/boot/i386/efi/ldscript.amd64 head/sys/boot/i386/efi/main.c head/sys/boot/i386/efi/reloc.c head/sys/boot/i386/efi/version Modified: head/sys/boot/efi/include/efi.h Added: head/sys/boot/amd64/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/amd64/Makefile Fri Apr 4 00:16:46 2014 (r264095) @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.include <bsd.own.mk> + +SUBDIR= efi + +.include <bsd.subdir.mk> Added: head/sys/boot/amd64/Makefile.inc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/amd64/Makefile.inc Fri Apr 4 00:16:46 2014 (r264095) @@ -0,0 +1,11 @@ +# Common defines for all of /sys/boot/amd64/ +# +# $FreeBSD$ + +BINDIR?= /boot + +CFLAGS+= -ffreestanding +CFLAGS+= -mno-mmx -mno-3dnow -mno-sse -mno-sse2 -mno-sse3 -msoft-float +LDFLAGS+= -nostdlib + +.include "../Makefile.inc" Copied and modified: head/sys/boot/amd64/efi/Makefile (from r264093, head/sys/boot/i386/efi/Makefile) ============================================================================== --- head/sys/boot/i386/efi/Makefile Thu Apr 3 23:22:04 2014 (r264093, copy source) +++ head/sys/boot/amd64/efi/Makefile Fri Apr 4 00:16:46 2014 (r264095) @@ -1,7 +1,6 @@ # $FreeBSD$ NO_MAN= -BUILDING_EFI= .include <bsd.own.mk> MK_SSP= no @@ -13,30 +12,30 @@ INTERNALPROG= SRCS= autoload.c \ bootinfo.c \ conf.c \ + copy.c \ devicename.c \ - efimd.c \ - elf32_freebsd.c \ - exec.c \ - i386_copy.c \ + elf64_freebsd.c \ + framebuffer.c \ main.c \ reloc.c \ vers.c -SRCS+= start.S +SRCS+= amd64_tramp.S \ + start.S +CFLAGS+= -fPIC +CFLAGS+= -I. CFLAGS+= -I${.CURDIR}/../../efi/include -CFLAGS+= -I${.CURDIR}/../../efi/include/i386 +CFLAGS+= -I${.CURDIR}/../../efi/include/${MACHINE_CPUARCH} +CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica/include +CFLAGS+= -I${.CURDIR}/../../.. .if ${MK_FORTH} != "no" BOOT_FORTH= yes CFLAGS+= -DBOOT_FORTH CFLAGS+= -I${.CURDIR}/../../ficl -CFLAGS+= -I${.CURDIR}/../../ficl/i386 -.if ${MACHINE_CPUARCH} == "amd64" -LIBFICL= ${.OBJDIR}/../../ficl32/libficl.a -.else +CFLAGS+= -I${.CURDIR}/../../ficl/${MACHINE_CPUARCH} LIBFICL= ${.OBJDIR}/../../ficl/libficl.a .endif -.endif # Include bcache code. HAVE_BCACHE= yes @@ -49,14 +48,14 @@ CFLAGS+= -I${.CURDIR}/../../common FILES= loader.efi FILESMODE_loader.efi= ${BINMODE} -LDSCRIPT= ${.CURDIR}/ldscript.i386 -LDFLAGS= -Wl,-T${LDSCRIPT} -Wl,-Bsymbolic -shared +LDSCRIPT= ${.CURDIR}/ldscript.${MACHINE_CPUARCH} +LDFLAGS= -Wl,-T${LDSCRIPT} -Wl,-Bsymbolic -shared -Wl,-znocombreloc ${PROG}: ${LDSCRIPT} CLEANFILES= vers.c loader.efi -NEWVERSWHAT= "EFI loader" x86 +NEWVERSWHAT= "EFI loader" ${MACHINE_CPUARCH} vers.c: ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/version sh ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/version ${NEWVERSWHAT} @@ -64,28 +63,36 @@ vers.c: ${.CURDIR}/../../common/newvers. OBJCOPY?= objcopy OBJDUMP?= objdump +.if ${MACHINE_CPUARCH} == "amd64" +EFI_TARGET= efi-app-x86_64 +.else +EFI_TARGET= efi-app-ia32 +.endif + loader.efi: loader.sym if [ `${OBJDUMP} -t ${.ALLSRC} | fgrep '*UND*' | wc -l` != 0 ]; then \ ${OBJDUMP} -t ${.ALLSRC} | fgrep '*UND*'; \ exit 1; \ fi - ${OBJCOPY} -j .data -j .dynamic -j .dynstr -j .dynsym -j .hash \ - -j .rel.dyn -j .reloc -j .sdata -j .text -j set_Xcommand_set \ - --target=efi-app-ia32 ${.ALLSRC} ${.TARGET} + ${OBJCOPY} -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 \ + --target=${EFI_TARGET} ${.ALLSRC} ${.TARGET} LIBEFI= ${.OBJDIR}/../../efi/libefi/libefi.a -LIBSTAND= ${.OBJDIR}/../../libstand32/libstand.a -CFLAGS+= -I${.CURDIR}/../libi386 -CFLAGS+= -I${.CURDIR}/../btx/lib +CFLAGS+= -I${.CURDIR}/../../common DPADD= ${LIBFICL} ${LIBEFI} ${LIBSTAND} LDADD= ${LIBFICL} ${LIBEFI} ${LIBSTAND} .include <bsd.prog.mk> -.if ${MACHINE_CPUARCH} == "amd64" -CFLAGS+= -I. -beforedepend ${OBJS}: machine +beforedepend ${OBJS}: machine x86 + +CLEANFILES+= machine x86 + machine: - ln -sf ${.CURDIR}/../../../i386/include machine -.endif + ln -sf ${.CURDIR}/../../../amd64/include machine + +x86: + ln -sf ${.CURDIR}/../../../x86/include x86 Added: head/sys/boot/amd64/efi/amd64_tramp.S ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/amd64/efi/amd64_tramp.S Fri Apr 4 00:16:46 2014 (r264095) @@ -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 Copied and modified: head/sys/boot/amd64/efi/autoload.c (from r264093, head/sys/boot/i386/efi/autoload.c) ============================================================================== --- head/sys/boot/i386/efi/autoload.c Thu Apr 3 23:22:04 2014 (r264093, copy source) +++ head/sys/boot/amd64/efi/autoload.c Fri Apr 4 00:16:46 2014 (r264095) @@ -28,7 +28,7 @@ __FBSDID("$FreeBSD$"); int -i386_autoload(void) +x86_efi_autoload(void) { return (0); Added: head/sys/boot/amd64/efi/bootinfo.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/amd64/efi/bootinfo.c Fri Apr 4 00:16:46 2014 (r264095) @@ -0,0 +1,360 @@ +/*- + * 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/metadata.h> +#include <machine/psl.h> +#include <machine/specialreg.h> + +#include <efi.h> +#include <efilib.h> + +#include "bootstrap.h" +#include "framebuffer.h" +#include "x86_efi.h" + +UINTN x86_efi_mapkey; + +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; + 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. + */ +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 (x86_efi_copyin(ep->ev_name, addr, len) != len) + break; + addr += len; + if (x86_efi_copyin("=", addr, 1) != 1) + break; + addr++; + if (ep->ev_value != NULL) { + len = strlen(ep->ev_value); + if (x86_efi_copyin(ep->ev_value, addr, len) != len) + break; + addr += len; + } + if (x86_efi_copyin("", addr, 1) != 1) + break; + last = ++addr; + } + + if (x86_efi_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) \ + x86_efi_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) \ + x86_efi_copyin(s, a, strlen(s) + 1); \ + a += roundup(strlen(s) + 1, sizeof(uint64_t)); \ +} + +#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) \ + x86_efi_copyin(&s, a, sizeof(s)); \ + a += roundup(sizeof(s), sizeof(uint64_t)); \ +} + +#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) \ + x86_efi_copyin(mm->md_data, a, mm->md_size); \ + a += roundup(mm->md_size, sizeof(uint64_t)); \ +} + +#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; + 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 mmsz, pages, sz; + UINT32 mmver; + struct efi_map_header *efihdr; + struct efi_fb efifb; + + if (efi_find_framebuffer(&efifb) == 0) + file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(efifb), &efifb); + + efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; + + /* + * 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, &x86_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() 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). + */ + efihdr = (struct efi_map_header *)addr; + mm = (void *)((uint8_t *)efihdr + efisz); + sz = (EFI_PAGE_SIZE * pages) - efisz; + status = BS->GetMemoryMap(&sz, mm, &x86_efi_mapkey, &mmsz, &mmver); + if (EFI_ERROR(status)) { + printf("%s: GetMemoryMap() returned 0x%lx\n", __func__, + (long)status); + return (EINVAL); + } + + efihdr->memory_size = sz; + efihdr->descriptor_size = mmsz; + efihdr->descriptor_version = mmver; + + file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz, efihdr); + + return (0); +} + +/* + * 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; + + 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"); + x86_efi_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(x86_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); + + 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); + file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); + + 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); + + /* Copy module list and metadata. */ + (void)bi_copymodules(addr); + + return (0); +} Copied and modified: head/sys/boot/amd64/efi/conf.c (from r264093, head/sys/boot/i386/efi/conf.c) ============================================================================== --- head/sys/boot/i386/efi/conf.c Thu Apr 3 23:22:04 2014 (r264093, copy source) +++ head/sys/boot/amd64/efi/conf.c Fri Apr 4 00:16:46 2014 (r264095) @@ -52,20 +52,12 @@ struct netif_driver *netif_drivers[] = { NULL }; -#ifdef notyet extern struct file_format amd64_elf; extern struct file_format amd64_elf_obj; -#endif -extern struct file_format i386_elf; -extern struct file_format i386_elf_obj; struct file_format *file_formats[] = { -#ifdef notyet &amd64_elf, &amd64_elf_obj, -#endif - &i386_elf, - &i386_elf_obj, NULL }; Added: head/sys/boot/amd64/efi/copy.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/amd64/efi/copy.c Fri Apr 4 00:16:46 2014 (r264095) @@ -0,0 +1,102 @@ +/*- + * 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> + +#define STAGE_PAGES 8192 /* 32MB */ + +EFI_PHYSICAL_ADDRESS staging; +int stage_offset_set = 0; +ssize_t stage_offset; + +int +x86_efi_copy_init(void) +{ + EFI_STATUS status; + + status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, + STAGE_PAGES, &staging); + if (EFI_ERROR(status)) { + printf("failed to allocate staging area: %d\n", + status & EFI_ERROR_MASK); + return (status); + } + + return (0); +} + +ssize_t +x86_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; + } + + bcopy(src, (void *)(dest + stage_offset), len); + return (len); +} + +ssize_t +x86_efi_copyout(const vm_offset_t src, void *dest, const size_t len) +{ + + bcopy((void *)(src + stage_offset), dest, len); + return (len); +} + + +ssize_t +x86_efi_readin(const int fd, vm_offset_t dest, const size_t len) +{ + + return (read(fd, (void *)(dest + stage_offset), len)); +} + +void +x86_efi_copy_finish(void) +{ + uint64_t *src, *dst, *last; + + src = (uint64_t *)staging; + dst = (uint64_t *)(staging - stage_offset); + last = (uint64_t *)(staging + STAGE_PAGES * EFI_PAGE_SIZE); + + while (src < last) + *dst++ = *src++; +} Copied and modified: head/sys/boot/amd64/efi/devicename.c (from r264093, head/sys/boot/i386/efi/devicename.c) ============================================================================== --- head/sys/boot/i386/efi/devicename.c Thu Apr 3 23:22:04 2014 (r264093, copy source) +++ head/sys/boot/amd64/efi/devicename.c Fri Apr 4 00:16:46 2014 (r264095) @@ -36,7 +36,7 @@ __FBSDID("$FreeBSD$"); #include <efi.h> #include <efilib.h> -static int i386_parsedev(struct devdesc **, const char *, const char **); +static int x86_efi_parsedev(struct devdesc **, const char *, const char **); /* * Point (dev) at an allocated device specifier for the device matching the @@ -44,7 +44,7 @@ static int i386_parsedev(struct devdesc * use that. If not, use the default device. */ int -i386_getdev(void **vdev, const char *devspec, const char **path) +x86_efi_getdev(void **vdev, const char *devspec, const char **path) { struct devdesc **dev = (struct devdesc **)vdev; int rv; @@ -54,14 +54,14 @@ i386_getdev(void **vdev, const char *dev * use the current device instead. */ if (devspec == NULL || *devspec == '/' || !strchr(devspec, ':')) { - rv = i386_parsedev(dev, getenv("currdev"), NULL); + rv = x86_efi_parsedev(dev, getenv("currdev"), NULL); if (rv == 0 && path != NULL) *path = devspec; return (rv); } /* Parse the device name off the beginning of the devspec. */ - return (i386_parsedev(dev, devspec, path)); + return (x86_efi_parsedev(dev, devspec, path)); } /* @@ -78,7 +78,7 @@ i386_getdev(void **vdev, const char *dev * fs<unit>: */ static int -i386_parsedev(struct devdesc **dev, const char *devspec, const char **path) +x86_efi_parsedev(struct devdesc **dev, const char *devspec, const char **path) { struct devdesc *idev; struct devsw *dv; @@ -132,7 +132,7 @@ i386_parsedev(struct devdesc **dev, cons } char * -i386_fmtdev(void *vdev) +x86_efi_fmtdev(void *vdev) { struct devdesc *dev = (struct devdesc *)vdev; static char buf[32]; /* XXX device length constant? */ @@ -154,12 +154,12 @@ i386_fmtdev(void *vdev) * Set currdev to suit the value being supplied in (value) */ int -i386_setcurrdev(struct env_var *ev, int flags, const void *value) +x86_efi_setcurrdev(struct env_var *ev, int flags, const void *value) { struct devdesc *ncurr; int rv; - rv = i386_parsedev(&ncurr, value, NULL); + rv = x86_efi_parsedev(&ncurr, value, NULL); if (rv != 0) return(rv); Added: head/sys/boot/amd64/efi/elf64_freebsd.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/amd64/efi/elf64_freebsd.c Fri Apr 4 00:16:46 2014 (r264095) @@ -0,0 +1,189 @@ +/*- + * 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 <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 "x86_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); + +struct file_format amd64_elf = { elf64_loadfile, elf64_exec }; +struct file_format amd64_elf_obj = { elf64_obj_loadfile, elf64_obj_exec }; + +#define PG_V 0x001 +#define PG_RW 0x002 +#define PG_U 0x004 +#define PG_PS 0x080 + +typedef u_int64_t p4_entry_t; +typedef u_int64_t p3_entry_t; +typedef u_int64_t p2_entry_t; +static p4_entry_t *PT4; +static p3_entry_t *PT3; +static p2_entry_t *PT2; + +static void (*trampoline)(uint64_t stack, void *copy_finish, uint64_t kernend, + uint64_t modulep, p4_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; + EFI_STATUS status; + + rsdp = efi_get_table(&acpi20_guid); + if (rsdp == NULL) { + rsdp = efi_get_table(&acpi_guid); + } + if (rsdp != NULL) { + sprintf(buf, "0x%016llx", 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", 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); + + 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 = (p4_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] = (p4_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] = (p3_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); + + err = bi_load(fp->f_args, &modulep, &kernend); + if (err != 0) + return(err); + + status = BS->ExitBootServices(IH, x86_efi_mapkey); + if (EFI_ERROR(status)) { + printf("%s: ExitBootServices() returned 0x%lx\n", __func__, + (long)status); + return (EINVAL); + } + + dev_cleanup(); + + trampoline(trampstack, x86_efi_copy_finish, kernend, modulep, PT4, + ehdr->e_entry); + + panic("exec returned"); +} + +static int +elf64_obj_exec(struct preloaded_file *fp) +{ + return (EFTYPE); +} Added: head/sys/boot/amd64/efi/framebuffer.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/amd64/efi/framebuffer.c Fri Apr 4 00:16:46 2014 (r264095) @@ -0,0 +1,85 @@ +/*- + * 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 *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201404040016.s340GlJn072315>