From 3c7acdd7ec2c526534a34442d4a0f533e6a3b62f Mon Sep 17 00:00:00 2001 From: David Sebek Date: Fri, 28 May 2021 10:58:36 -0400 Subject: [PATCH] efi: loader: amd64: Fix a boot crash or hang on some systems Instead of calling the efi_copy_finish function from amd64_tramp, include the copy instructions in the trampoline code itself. This avoids boot hangs and restarts in the cases when the EFI loader code and/or data segments happen to be located at a memory loacation that is overwritten during the copy process. Signed-off-by: David Sebek --- stand/efi/loader/arch/amd64/amd64_tramp.S | 53 +++++++++++++++++++++ stand/efi/loader/arch/amd64/elf64_freebsd.c | 18 ++++--- stand/efi/loader/copy.c | 8 ++++ stand/efi/loader/loader_efi.h | 1 + 4 files changed, 73 insertions(+), 7 deletions(-) diff --git a/stand/efi/loader/arch/amd64/amd64_tramp.S b/stand/efi/loader/arch/amd64/amd64_tramp.S index 877705407f9..88707ba7312 100644 --- a/stand/efi/loader/arch/amd64/amd64_tramp.S +++ b/stand/efi/loader/arch/amd64/amd64_tramp.S @@ -61,6 +61,55 @@ amd64_tramp: ALIGN_TEXT amd64_tramp_end: +/* + * void amd64_tramp_inline(uint64_t stack, uint64_t kernend, + * uint64_t modulep, uint64_t pagetable, uint64_t entry, + * uint64_t copy_dst, uint64_t copy_src, uint64_t copy_src_end) + */ + .globl amd64_tramp_inline +amd64_tramp_inline: + cli /* Make sure we don't get interrupted. */ + + movq %rsi,%r12 /* Stash the kernel values for later. */ + movq %rdx,%r13 + movq %rcx,%r14 + movq %r8,%r15 + + /* Copy the content of the staging area to the expected location. + * A code similar to efi_copy_finish from copy.c is inlined here + * instead of calling a copy_finish function like amd64_tramp did. + * In some cases, the UEFI bootloader may be loaded to a memory location + * that happens to be overwritten during this copy process. In such a case, + * calling copy_finish could result in a system reboot or a bootloader to + * hang. + * + * while (copy_src < copy_src_end) *copy_dst++ = *copy_src++; + */ + movq 8(%rsp), %rax /* rax = copy_src */ + movq 16(%rsp), %rcx /* rcx = copy_src_end */ + cmpq %rcx, %rax + jnb copy_done + subq %rax, %r9 /* r9 = copy_dst - copy_src */ +loop: + movq (%rax), %rdx + movq %rdx, (%rax,%r9) + addq $8, %rax + cmpq %rax, %rcx + ja loop +copy_done: + + movq %rdi,%rsp /* Switch to our temporary stack. */ + + 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_inline_end: + /* void multiboot2_exec(uint64_t entry, uint64_t multiboot_info, uint64_t stack) */ .globl multiboot2_exec multiboot2_exec: @@ -74,3 +123,7 @@ multiboot2_exec: .globl amd64_tramp_size amd64_tramp_size: .long amd64_tramp_end-amd64_tramp + + .globl amd64_tramp_inline_size +amd64_tramp_inline_size: + .long amd64_tramp_inline_end-amd64_tramp_inline diff --git a/stand/efi/loader/arch/amd64/elf64_freebsd.c b/stand/efi/loader/arch/amd64/elf64_freebsd.c index a950ca55e84..852199a43f5 100644 --- a/stand/efi/loader/arch/amd64/elf64_freebsd.c +++ b/stand/efi/loader/arch/amd64/elf64_freebsd.c @@ -84,11 +84,12 @@ 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); +static void (*trampoline)(uint64_t stack, uint64_t kernend, + uint64_t modulep, pml4_entry_t *pagetable, uint64_t entry, + uint64_t copy_dst, uint64_t copy_src, uint64_t copy_src_end); -extern uintptr_t amd64_tramp; -extern uint32_t amd64_tramp_size; +extern uintptr_t amd64_tramp_inline; +extern uint32_t amd64_tramp_inline_size; /* * There is an ELF kernel and one or more ELF modules loaded. @@ -101,6 +102,7 @@ elf64_exec(struct preloaded_file *fp) struct file_metadata *md; Elf_Ehdr *ehdr; vm_offset_t modulep, kernend, trampcode, trampstack; + uint64_t copy_dst, copy_src, copy_src_end; int err, i; ACPI_TABLE_RSDP *rsdp; char buf[24]; @@ -159,7 +161,7 @@ elf64_exec(struct preloaded_file *fp) (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); + bcopy((void *)&amd64_tramp_inline, (void *)trampcode, amd64_tramp_inline_size); trampoline = (void *)trampcode; PT4 = (pml4_entry_t *)0x0000000040000000; @@ -200,8 +202,10 @@ elf64_exec(struct preloaded_file *fp) dev_cleanup(); - trampoline(trampstack, efi_copy_finish, kernend, modulep, PT4, - ehdr->e_entry); + efi_copy_get_locations(©_dst, ©_src, ©_src_end); + + trampoline(trampstack, kernend, modulep, PT4, + ehdr->e_entry, copy_dst, copy_src, copy_src_end); panic("exec returned"); } diff --git a/stand/efi/loader/copy.c b/stand/efi/loader/copy.c index e723b61e3bc..f063b6615bc 100644 --- a/stand/efi/loader/copy.c +++ b/stand/efi/loader/copy.c @@ -364,3 +364,11 @@ efi_copy_finish(void) while (src < last) *dst++ = *src++; } + +void +efi_copy_get_locations(uint64_t * dst, uint64_t * src, uint64_t * src_end) +{ + *src = (uint64_t)staging; + *dst = (uint64_t)(staging - stage_offset); + *src_end = (uint64_t)staging_end; +} diff --git a/stand/efi/loader/loader_efi.h b/stand/efi/loader/loader_efi.h index 4d077514e42..f3a052a63cf 100644 --- a/stand/efi/loader/loader_efi.h +++ b/stand/efi/loader/loader_efi.h @@ -44,5 +44,6 @@ ssize_t efi_readin(readin_handle_t fd, vm_offset_t dest, const size_t len); void * efi_translate(vm_offset_t ptr); void efi_copy_finish(void); +void efi_copy_get_locations(uint64_t *dst, uint64_t *src, uint64_t *src_end); #endif /* _LOADER_EFI_COPY_H_ */ -- 2.31.1