From owner-svn-src-projects@FreeBSD.ORG Sun Feb 24 10:55:51 2013 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.FreeBSD.org [8.8.178.115]) by hub.freebsd.org (Postfix) with ESMTP id 02EE3263; Sun, 24 Feb 2013 10:55:51 +0000 (UTC) (envelope-from benno@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id E7F871C44; Sun, 24 Feb 2013 10:55:50 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.5/8.14.5) with ESMTP id r1OAto3H001422; Sun, 24 Feb 2013 10:55:50 GMT (envelope-from benno@svn.freebsd.org) Received: (from benno@localhost) by svn.freebsd.org (8.14.5/8.14.5/Submit) id r1OAtoFh001417; Sun, 24 Feb 2013 10:55:50 GMT (envelope-from benno@svn.freebsd.org) Message-Id: <201302241055.r1OAtoFh001417@svn.freebsd.org> From: Benno Rice Date: Sun, 24 Feb 2013 10:55:50 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r247214 - projects/uefi/sys/boot/i386/efi X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 24 Feb 2013 10:55:51 -0000 Author: benno Date: Sun Feb 24 10:55:49 2013 New Revision: 247214 URL: http://svnweb.freebsd.org/changeset/base/247214 Log: 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. Added: projects/uefi/sys/boot/i386/efi/amd64_tramp.S Modified: projects/uefi/sys/boot/i386/efi/Makefile projects/uefi/sys/boot/i386/efi/elf64_freebsd.c projects/uefi/sys/boot/i386/efi/x86_efi_copy.c Modified: projects/uefi/sys/boot/i386/efi/Makefile ============================================================================== --- projects/uefi/sys/boot/i386/efi/Makefile Sun Feb 24 08:00:35 2013 (r247213) +++ projects/uefi/sys/boot/i386/efi/Makefile Sun Feb 24 10:55:49 2013 (r247214) @@ -12,6 +12,7 @@ INTERNALPROG= # architecture-specific loader code SRCS= main.c exec.c conf.c vers.c reloc.c elf32_freebsd.c elf64_freebsd.c SRCS+= x86_efi_copy.c bootinfo.c bootinfo64.c autoload.c devicename.c efimd.c +SRCS+= amd64_tramp.S .PATH: ${.CURDIR}/${MACHINE_CPUARCH} SRCS+= start.S Added: projects/uefi/sys/boot/i386/efi/amd64_tramp.S ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/uefi/sys/boot/i386/efi/amd64_tramp.S Sun Feb 24 10:55:49 2013 (r247214) @@ -0,0 +1,34 @@ +#include + + .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 + + call %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 Modified: projects/uefi/sys/boot/i386/efi/elf64_freebsd.c ============================================================================== --- projects/uefi/sys/boot/i386/efi/elf64_freebsd.c Sun Feb 24 08:00:35 2013 (r247213) +++ projects/uefi/sys/boot/i386/efi/elf64_freebsd.c Sun Feb 24 10:55:49 2013 (r247214) @@ -74,6 +74,13 @@ 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 @@ -84,8 +91,7 @@ elf64_exec(struct preloaded_file *fp) { struct file_metadata *md; Elf_Ehdr *ehdr; - vm_offset_t modulep, kernend, pagetable; - uint32_t mp, ke; + vm_offset_t modulep, kernend, trampcode, trampstack; int err, i; ACPI_TABLE_RSDP *rsdp; char buf[24]; @@ -121,10 +127,18 @@ elf64_exec(struct preloaded_file *fp) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); - PT4 = (p4_entry_t *)0x00000000fffff000; + 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 * PAGE_SIZE); + bzero(PT4, 3 * EFI_PAGE_SIZE); PT3 = &PT4[512]; PT2 = &PT3[512]; @@ -157,23 +171,8 @@ elf64_exec(struct preloaded_file *fp) dev_cleanup(); - x86_efi_copy_finish(); - - mp = modulep & 0xffffffff; - ke = kernend & 0xffffffff; - pagetable = (uintptr_t)PT4; - __asm __volatile( - "movl %0, %%eax;" - "pushq %%rax;" - "movl %1, %%eax;" - "salq $32, %%rax;" - "pushq %%rax;" - "movq %2, %%rax;" - "pushq %%rax;" - "movq %3, %%rax;" - "movq %%rax, %%cr3;" - "ret" - :: "r" (ke), "r" (mp), "r" (ehdr->e_entry), "r" (PT4)); + trampoline(trampstack, x86_efi_copy_finish, kernend, modulep, PT4, + ehdr->e_entry); panic("exec returned"); } Modified: projects/uefi/sys/boot/i386/efi/x86_efi_copy.c ============================================================================== --- projects/uefi/sys/boot/i386/efi/x86_efi_copy.c Sun Feb 24 08:00:35 2013 (r247213) +++ projects/uefi/sys/boot/i386/efi/x86_efi_copy.c Sun Feb 24 10:55:49 2013 (r247214) @@ -37,7 +37,7 @@ __FBSDID("$FreeBSD$"); #include #include -#define STAGE_PAGES 8000 /* 32MB */ +#define STAGE_PAGES 8192 /* 32MB */ EFI_PHYSICAL_ADDRESS staging; int stage_offset_set = 0; @@ -92,7 +92,12 @@ x86_efi_readin(const int fd, vm_offset_t void x86_efi_copy_finish(void) { + uint64_t *src, *dst, *last; - bcopy((void *)staging, (void *)(staging - stage_offset), - STAGE_PAGES * EFI_PAGE_SIZE); + src = (uint64_t *)staging; + dst = (uint64_t *)(staging - stage_offset); + last = (uint64_t *)(staging + STAGE_PAGES * EFI_PAGE_SIZE); + + while (src < last) + *dst++ = *src++; }