Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102700286
D30828.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D30828.diff
View Options
diff --git a/stand/efi/loader/arch/amd64/amd64_tramp.S b/stand/efi/loader/arch/amd64/amd64_tramp.S
--- a/stand/efi/loader/arch/amd64/amd64_tramp.S
+++ b/stand/efi/loader/arch/amd64/amd64_tramp.S
@@ -61,6 +61,54 @@
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 kernel from the staging area to the expected location
+ * in memory. The following code is equivalent to the efi_copy_finish
+ * function that amd64_tramp used to call. Inlining this code avoids
+ * a scenario when the system froze because efi_copy_finish
+ * overwrote its own code that just happened to be located somewhere
+ * in the destination range.
+ *
+ * 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 +122,7 @@
.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
--- a/stand/efi/loader/arch/amd64/elf64_freebsd.c
+++ b/stand/efi/loader/arch/amd64/elf64_freebsd.c
@@ -84,11 +84,12 @@
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,8 @@
struct file_metadata *md;
Elf_Ehdr *ehdr;
vm_offset_t modulep, kernend, trampcode, trampstack;
+ uint64_t copy_dst, copy_src, copy_src_end;
+ EFI_STATUS status;
int err, i;
ACPI_TABLE_RSDP *rsdp;
char buf[24];
@@ -155,16 +158,26 @@
ehdr = (Elf_Ehdr *)&(md->md_data);
trampcode = (vm_offset_t)0x0000000040000000;
- err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 1,
+ status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 1,
(EFI_PHYSICAL_ADDRESS *)&trampcode);
+ if (EFI_ERROR(status)) {
+ printf("Failed to allocate pages for trampoline code: %lu\n",
+ EFI_ERROR_CODE(status));
+ return(ENOMEM);
+ }
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;
- err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 3,
+ status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 3,
(EFI_PHYSICAL_ADDRESS *)&PT4);
+ if (EFI_ERROR(status)) {
+ printf("Failed to allocate pages for PT4: %lu\n",
+ EFI_ERROR_CODE(status));
+ return(ENOMEM);
+ }
bzero(PT4, 3 * EFI_PAGE_SIZE);
PT3 = &PT4[512];
@@ -191,6 +204,15 @@
printf("Start @ 0x%lx ...\n", ehdr->e_entry);
+ /* Check the type of memory pages that will be overwritten
+ * by the trampoline and print a warning message for easier
+ * debugging. The memory map will most likely change until
+ * then, but I don't expect new reserved memory blocks to
+ * suddenly appear. */
+ if (!efi_verify_destination_type()) {
+ printf("Important memory pages may get overwritten!\n");
+ }
+
efi_time_fini();
err = bi_load(fp->f_args, &modulep, &kernend, true);
if (err != 0) {
@@ -200,8 +222,10 @@
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/bootinfo.c b/stand/efi/loader/bootinfo.c
--- a/stand/efi/loader/bootinfo.c
+++ b/stand/efi/loader/bootinfo.c
@@ -444,6 +444,7 @@
vm_offset_t size;
char *rootdevname;
int howto;
+ int err;
#if defined(LOADER_FDT_SUPPORT)
vm_offset_t dtbp;
int dtb_size;
@@ -539,7 +540,10 @@
#ifdef LOADER_GELI_SUPPORT
geli_export_key_metadata(kfp);
#endif
- bi_load_efi_data(kfp, exit_bs);
+ err = bi_load_efi_data(kfp, exit_bs);
+ if (err != 0) {
+ return(err);
+ }
size = bi_copymodules(0);
kernend = roundup(addr + size, PAGE_SIZE);
diff --git a/stand/efi/loader/copy.c b/stand/efi/loader/copy.c
--- a/stand/efi/loader/copy.c
+++ b/stand/efi/loader/copy.c
@@ -208,7 +208,7 @@
* memory: see elf64_exec() in
* boot/efi/loader/arch/amd64/elf64_freebsd.c.
*/
- staging = 1024*1024*1024;
+ staging = 1024*1024*1024 - 1;
status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
nr_pages, &staging);
#else
@@ -316,6 +316,9 @@
/* XXX: Callers do not check for failure. */
if (!efi_check_space(dest + stage_offset + len)) {
errno = ENOMEM;
+ /* XXX: For now, it is better to stop
+ * booting instead of ignoring it. */
+ panic("efi_copyin: Staging area is too small");
return (-1);
}
bcopy(src, (void *)(dest + stage_offset), len);
@@ -329,6 +332,9 @@
/* XXX: Callers do not check for failure. */
if (src + stage_offset + len > staging_end) {
errno = ENOMEM;
+ /* XXX: For now, it is better to stop
+ * booting instead of ignoring it. */
+ panic("efi_copyout: Staging area is too small");
return (-1);
}
bcopy((void *)(src + stage_offset), dest, len);
@@ -345,8 +351,13 @@
stage_offset_set = 1;
}
+ /* XXX: Callers don't seem to differentiate between a read failure
+ * from fd and an insufficient space in the staging area. */
if (!efi_check_space(dest + stage_offset + len)) {
errno = ENOMEM;
+ /* XXX: For now, at least print that the size of
+ * the staging area is at fault and should be larger. */
+ printf("efi_readin: Staging area is too small\n");
return (-1);
}
return (VECTX_READ(fd, (void *)(dest + stage_offset), len));
@@ -364,3 +375,79 @@
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;
+}
+
+bool
+efi_verify_destination_type(void)
+{
+ EFI_MEMORY_DESCRIPTOR *map = NULL, *p;
+ EFI_PHYSICAL_ADDRESS dest, dest_end;
+ EFI_PHYSICAL_ADDRESS page_start, page_end, overlap_start, overlap_end;
+ UINTN sz, key, dsz;
+ UINT32 dver;
+ EFI_STATUS status;
+ int i, ndesc;
+ bool safe;
+
+ dest = staging - stage_offset;
+ dest_end = dest + (staging_end - staging);
+ safe = false;
+
+ sz = 0;
+
+ for (;;) {
+ status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
+ if (!EFI_ERROR(status))
+ break;
+
+ if (status != EFI_BUFFER_TOO_SMALL) {
+ printf("Can't read memory map: %lu\n",
+ EFI_ERROR_CODE(status));
+ goto out;
+ }
+
+ free(map);
+
+ /* Allocate 10 descriptors more than the size reported,
+ * to allow for any fragmentation caused by calling
+ * malloc */
+ map = malloc(sz + (10 * dsz));
+ if (map == NULL) {
+ printf("Unable to allocate memory\n");
+ goto out;
+ }
+ }
+
+ safe = true;
+
+ ndesc = sz / dsz;
+ for (i = 0, p = map; i < ndesc;
+ i++, p = NextMemoryDescriptor(p, dsz)) {
+ page_start = p->PhysicalStart;
+ page_end = page_start + p->NumberOfPages * EFI_PAGE_SIZE;
+
+ overlap_start = page_start > dest ? page_start : dest;
+ overlap_end = page_end < dest_end ? page_end : dest_end;
+
+ if (overlap_start < overlap_end &&
+ p->Type != EfiLoaderCode &&
+ p->Type != EfiLoaderData &&
+ p->Type != EfiBootServicesCode &&
+ p->Type != EfiBootServicesData &&
+ p->Type != EfiConventionalMemory) {
+ safe = false;
+ break;
+ }
+ }
+
+out:
+ free(map);
+ return safe;
+}
\ No newline at end of file
diff --git a/stand/efi/loader/loader_efi.h b/stand/efi/loader/loader_efi.h
--- a/stand/efi/loader/loader_efi.h
+++ b/stand/efi/loader/loader_efi.h
@@ -44,5 +44,7 @@
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);
+bool efi_verify_destination_type(void);
#endif /* _LOADER_EFI_COPY_H_ */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Nov 17, 1:11 AM (21 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14668534
Default Alt Text
D30828.diff (9 KB)
Attached To
Mode
D30828: EFI boot: Fix boot freeze on some systems
Attached
Detach File
Event Timeline
Log In to Comment