diff --git a/stand/kboot/include/efi.h b/stand/kboot/include/efi.h index f75a9ea055d3..26a6cf8944ca 100644 --- a/stand/kboot/include/efi.h +++ b/stand/kboot/include/efi.h @@ -1,18 +1,27 @@ /*- * Copyright (c) 2024, Netflix, Inc. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include /* Note, we mix and match FreeBSD types and EFI standard defined types */ typedef void (*efi_map_entry_cb)(struct efi_md *, void *argp); +struct preloaded_file; + +bool efi_read_from_pa(uint64_t pa, uint32_t map_size, uint32_t desc_size, uint32_t vers); +void efi_read_from_sysfs(void); +void efi_set_systbl(uint64_t tbl); void foreach_efi_map_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp); void print_efi_map(struct efi_map_header *efihdr); -bool populate_avail_from_efi(struct efi_map_header *efihdr); +void efi_bi_loadsmap(struct preloaded_file *kfp); + +extern uint32_t efi_map_size; +extern vm_paddr_t efi_map_phys_src; /* From DTB */ +extern vm_paddr_t efi_map_phys_dst; /* From our memory map metadata module */ diff --git a/stand/kboot/kboot/arch/aarch64/exec.c b/stand/kboot/kboot/arch/aarch64/exec.c index bbb97cc20979..40eb57f371a9 100644 --- a/stand/kboot/kboot/arch/aarch64/exec.c +++ b/stand/kboot/kboot/arch/aarch64/exec.c @@ -1,292 +1,289 @@ /*- * 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 #include #include #include #include #ifdef EFI #include #include #include "loader_efi.h" #else #include "host_syscall.h" #endif #include #include "bootstrap.h" +#include "efi.h" #include "kboot.h" #include "platform/acfreebsd.h" #include "acconfig.h" #define ACPI_SYSTEM_XFACE #include "actypes.h" #include "actbl.h" #include "cache.h" #ifndef EFI #define LOADER_PAGE_SIZE PAGE_SIZE #endif #ifdef EFI static EFI_GUID acpi_guid = ACPI_TABLE_GUID; static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID; #endif static int elf64_exec(struct preloaded_file *amp); static int elf64_obj_exec(struct preloaded_file *amp); bool do_mem_map = false; -extern uint32_t efi_map_size; -extern vm_paddr_t efi_map_phys_src; /* From DTB */ -extern vm_paddr_t efi_map_phys_dst; /* From our memory map metadata module */ - -/* Usually provided by loader_efi.h */ +/* Usually provided by loader_efi.h -- maybe just delete? */ #ifndef EFI int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp, bool exit_bs); #endif static struct file_format arm64_elf = { elf64_loadfile, elf64_exec }; struct file_format *file_formats[] = { &arm64_elf, NULL }; #ifndef EFI extern uintptr_t tramp; extern uint32_t tramp_size; extern uint32_t tramp_data_offset; struct trampoline_data { uint64_t entry; // 0 (PA where kernel loaded) uint64_t modulep; // 8 module metadata uint64_t memmap_src; // 16 Linux-provided memory map PA uint64_t memmap_dst; // 24 Module data copy PA uint64_t memmap_len; // 32 Length to copy }; #endif static int elf64_exec(struct preloaded_file *fp) { vm_offset_t modulep, kernendp; #ifdef EFI vm_offset_t clean_addr; size_t clean_size; void (*entry)(vm_offset_t); #else vm_offset_t trampolinebase; vm_offset_t staging; void *trampcode; uint64_t *trampoline; struct trampoline_data *trampoline_data; int nseg; void *kseg; #endif struct file_metadata *md; Elf_Ehdr *ehdr; int error; #ifdef EFI ACPI_TABLE_RSDP *rsdp; char buf[24]; int revision; /* * Report the RSDP to the kernel. 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 15. */ 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); } } #else vm_offset_t rsdp; rsdp = acpi_rsdp(); if (rsdp != 0) { char buf[24]; printf("Found ACPI 2.0 at %#016lx\n", rsdp); sprintf(buf, "0x%016llx", (unsigned long long)rsdp); setenv("hint.acpi.0.rsdp", buf, 1); /* For 13.1R bootability */ setenv("acpi.rsdp", buf, 1); /* Nobody uses the rest of that stuff */ } // XXX Question: why not just use malloc? trampcode = host_getmem(LOADER_PAGE_SIZE); if (trampcode == NULL) { printf("Unable to allocate trampoline\n"); return (ENOMEM); } bzero((void *)trampcode, LOADER_PAGE_SIZE); bcopy((void *)&tramp, (void *)trampcode, tramp_size); trampoline = (void *)trampcode; /* * Figure out where to put it. * * Linux does not allow us to kexec_load into any part of memory. Ask * arch_loadaddr to resolve the first available chunk of physical memory * where loading is possible (load_addr). * * The kernel is loaded at the 'base' address in continguous physical * memory. We use the 2MB in front of the kernel as a place to put our * trampoline, but that's really overkill since we only need ~100 bytes. * The arm64 kernel's entry requirements are only 'load the kernel at a * 2MB alignment' and it figures out the rest, creates the right page * tables, etc. */ staging = kboot_get_phys_load_segment(); printf("Load address at %#jx\n", (uintmax_t)staging); printf("Relocation offset is %#jx\n", (uintmax_t)elf64_relocation_offset); #endif if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); #ifdef EFI entry = efi_translate(ehdr->e_entry); efi_time_fini(); #endif error = bi_load(fp->f_args, &modulep, &kernendp, true); if (error != 0) { #ifdef EFI efi_time_init(); #endif return (error); } dev_cleanup(); #ifdef EFI /* 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(); (*entry)(modulep); #else /* Linux will flush the caches, just pass this data into our trampoline and go */ trampoline_data = (void *)trampoline + tramp_data_offset; memset(trampoline_data, 0, sizeof(*trampoline_data)); trampoline_data->entry = ehdr->e_entry - fp->f_addr + staging; trampoline_data->modulep = modulep; printf("Modulep = %jx\n", (uintmax_t)modulep); if (efi_map_phys_src != 0) { md = file_findmetadata(fp, MODINFOMD_EFI_MAP); if (md == NULL || md->md_addr == 0) { printf("Need to copy EFI MAP, but EFI MAP not found. %p\n", md); } else { printf("Metadata EFI map loaded at VA %lx\n", md->md_addr); efi_map_phys_dst = md->md_addr + staging + roundup2(sizeof(struct efi_map_header), 16) - fp->f_addr; trampoline_data->memmap_src = efi_map_phys_src; trampoline_data->memmap_dst = efi_map_phys_dst; trampoline_data->memmap_len = efi_map_size - roundup2(sizeof(struct efi_map_header), 16); printf("Copying UEFI Memory Map data from %#lx to %#lx %ld bytes\n", efi_map_phys_src, trampoline_data->memmap_dst, trampoline_data->memmap_len); } } /* * Copy the trampoline to the ksegs. Since we're just bouncing off of * this into the kernel, no need to preserve the pages. On arm64, the * kernel sets up the initial page table, so we don't have to preserve * the memory used for the trampoline past when it calls the kernel. */ printf("kernendp = %#llx\n", (long long)kernendp); trampolinebase = staging + (kernendp - fp->f_addr); printf("trampolinebase = %#llx\n", (long long)trampolinebase); archsw.arch_copyin((void *)trampcode, kernendp, tramp_size); printf("Trampoline bouncing to %#llx\n", (long long)trampoline_data->entry); kboot_kseg_get(&nseg, &kseg); error = host_kexec_load(trampolinebase, nseg, kseg, HOST_KEXEC_ARCH_AARCH64); if (error != 0) panic("kexec_load returned error: %d", error); host_reboot(HOST_REBOOT_MAGIC1, HOST_REBOOT_MAGIC2, HOST_REBOOT_CMD_KEXEC, 0); #endif 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/kboot/kboot/arch/aarch64/load_addr.c b/stand/kboot/kboot/arch/aarch64/load_addr.c index 8ceb21007c45..c458adc8dd05 100644 --- a/stand/kboot/kboot/arch/aarch64/load_addr.c +++ b/stand/kboot/kboot/arch/aarch64/load_addr.c @@ -1,212 +1,143 @@ /*- * Copyright (c) 2022 Netflix, Inc * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include "kboot.h" -#include "bootstrap.h" #include "efi.h" -/* - * Info from dtb about the EFI system - */ -vm_paddr_t efi_systbl_phys; -struct efi_map_header *efi_map_hdr; -uint32_t efi_map_size; -vm_paddr_t efi_map_phys_src; /* From DTB */ -vm_paddr_t efi_map_phys_dst; /* From our memory map metadata module */ - static bool do_memory_from_fdt(int fd) { struct stat sb; char *buf = NULL; - int len, offset, fd2 = -1; - uint32_t sz, ver, esz, efisz; + int len, offset; + uint32_t sz, ver, esz; uint64_t mmap_pa; const uint32_t *u32p; const uint64_t *u64p; - struct efi_map_header *efihdr; - struct efi_md *map; if (fstat(fd, &sb) < 0) return false; buf = malloc(sb.st_size); if (buf == NULL) return false; len = read(fd, buf, sb.st_size); /* NB: we're reading this from sysfs, so mismatch OK */ if (len <= 0) goto errout; /* * Look for /chosen to find these values: * linux,uefi-system-table PA of the UEFI System Table. * linux,uefi-mmap-start PA of the UEFI memory map * linux,uefi-mmap-size Size of mmap * linux,uefi-mmap-desc-size Size of each entry of mmap * linux,uefi-mmap-desc-ver Format version, should be 1 */ offset = fdt_path_offset(buf, "/chosen"); if (offset <= 0) goto errout; u64p = fdt_getprop(buf, offset, "linux,uefi-system-table", &len); if (u64p == NULL) goto errout; - efi_systbl_phys = fdt64_to_cpu(*u64p); + efi_set_systbl(fdt64_to_cpu(*u64p)); u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-ver", &len); if (u32p == NULL) goto errout; ver = fdt32_to_cpu(*u32p); u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-size", &len); if (u32p == NULL) goto errout; esz = fdt32_to_cpu(*u32p); u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-size", &len); if (u32p == NULL) goto errout; sz = fdt32_to_cpu(*u32p); u64p = fdt_getprop(buf, offset, "linux,uefi-mmap-start", &len); if (u64p == NULL) goto errout; mmap_pa = fdt64_to_cpu(*u64p); free(buf); printf("UEFI MMAP: Ver %d Ent Size %d Tot Size %d PA %#lx\n", ver, esz, sz, mmap_pa); - /* - * We may have no ability to read the PA that this map is in, so pass - * the address to FreeBSD via a rather odd flag entry as the first map - * so early boot can copy the memory map into this space and have the - * rest of the code cope. - */ - efisz = roundup2(sizeof(*efihdr), 16); - buf = malloc(sz + efisz); - if (buf == NULL) - return false; - efihdr = (struct efi_map_header *)buf; - map = (struct efi_md *)((uint8_t *)efihdr + efisz); - bzero(map, sz); - efihdr->memory_size = sz; - efihdr->descriptor_size = esz; - efihdr->descriptor_version = ver; - - /* - * Save EFI table. Either this will be an empty table filled in by the trampoline, - * or we'll read it below. Either way, set these two variables so we share the best - * UEFI memory map with the kernel. - */ - efi_map_hdr = efihdr; - efi_map_size = sz + efisz; - - /* - * Try to read in the actual UEFI map. - */ - fd2 = open("host:/dev/mem", O_RDONLY); - if (fd2 < 0) { - printf("Will read UEFI mem map in tramp: no /dev/mem, need CONFIG_DEVMEM=y\n"); - goto no_read; - } - if (lseek(fd2, mmap_pa, SEEK_SET) < 0) { - printf("Will read UEFI mem map in tramp: lseek failed\n"); - goto no_read; - } - len = read(fd2, map, sz); - if (len != sz) { - if (len < 0 && errno == EPERM) - printf("Will read UEFI mem map in tramp: kernel needs CONFIG_STRICT_DEVMEM=n\n"); - else - printf("Will read UEFI mem map in tramp: lean = %d errno = %d\n", len, errno); - goto no_read; - } - printf("Read UEFI mem map from physmem\n"); - efi_map_phys_src = 0; /* Mark MODINFOMD_EFI_MAP as valid */ - close(fd2); - printf("UEFI MAP:\n"); - print_efi_map(efihdr); - return true; /* OK, we really have the memory map */ - -no_read: - efi_map_phys_src = mmap_pa; - close(fd2); - return true; /* We can get it the trampoline */ + efi_read_from_pa(mmap_pa, sz, esz, ver); + return true; errout: free(buf); return false; } bool enumerate_memory_arch(void) { int fd = -1; bool rv = false; + /* + * FDT publishes the parameters for the memory table in a series of + * nodes in the DTB. One of them is the physical address for the memory + * table. Try to open the fdt nblob to find this information if we can + * and try to grab things from memory. If we return rv == TRUE then + * we found it. The global efi_map_phys_src is set != 0 when we know + * the PA but can't read it. + */ fd = open("host:/sys/firmware/fdt", O_RDONLY); if (fd != -1) { rv = do_memory_from_fdt(fd); close(fd); - /* - * So, we have physaddr to the memory table. However, we can't - * open /dev/mem on some platforms to get the actual table. So - * we have to fall through to get it from /proc/iomem. - */ } + + /* + * One would think that one could use the raw EFI map to find memory + * that's free to boot the kernel with. However, Linux reserves some + * areas that it needs to properly. I'm not sure if the printf should be + * a panic, but for now, so I can debug (maybe at the loader prompt), + * I'm printing and carrying on. + */ if (!rv) { printf("Could not obtain UEFI memory tables, expect failure\n"); } populate_avail_from_iomem(); - print_avail(); return true; } uint64_t kboot_get_phys_load_segment(void) { #define HOLE_SIZE (64ul << 20) #define KERN_ALIGN (2ul << 20) static uint64_t s = 0; if (s != 0) return (s); + print_avail(); s = first_avail(KERN_ALIGN, HOLE_SIZE, SYSTEM_RAM); + printf("KBOOT GET PHYS Using %#llx\n", (long long)s); if (s != 0) return (s); s = 0x40000000 | 0x4200000; /* should never get here */ - printf("Falling back to crazy address %#lx\n", s); + /* XXX PANIC? XXX */ + printf("Falling back to the crazy address %#lx which works in qemu\n", s); return (s); } void bi_loadsmap(struct preloaded_file *kfp) { - - /* - * Make a note of a systbl. This is nearly mandatory on AARCH64. - */ - if (efi_systbl_phys) - file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(efi_systbl_phys), &efi_systbl_phys); - - /* - * If we have efi_map_hdr, then it's a pointer to the PA where this - * memory map lives. The trampoline code will copy it over. If we don't - * have it, we use whatever we found in /proc/iomap. - */ - if (efi_map_hdr != NULL) { - file_addmetadata(kfp, MODINFOMD_EFI_MAP, efi_map_size, efi_map_hdr); - return; - } - panic("Can't get UEFI memory map, nor a pointer to it, can't proceed.\n"); + efi_bi_loadsmap(kfp); } diff --git a/stand/kboot/libkboot/Makefile b/stand/kboot/libkboot/Makefile index e23ae9bb9215..dc85ffe8afb2 100644 --- a/stand/kboot/libkboot/Makefile +++ b/stand/kboot/libkboot/Makefile @@ -1,22 +1,23 @@ .include LIB= kboot WARNS?= 4 .PATH: ${.CURDIR}/arch/${MACHINE_ARCH} CFLAGS+=-I${.CURDIR} -I${.CURDIR}/arch/${MACHINE_ARCH} +CFLAGS+=-I${LDRSRC} SRCS= crt1.c SRCS+= host_syscall.S SRCS+= host_syscalls.c SRCS+= seg.c SRCS+= termios.c SRCS+= util.c .if ${MACHINE_ARCH} != "powerpc64" SRCS+= efi.c .endif .sinclude "${.CURDIR}/arch/${MACHINE_ARCH}/Makefile.inc" .include diff --git a/stand/kboot/libkboot/efi.c b/stand/kboot/libkboot/efi.c index 1c1d9a34d297..3f1055f6d538 100644 --- a/stand/kboot/libkboot/efi.c +++ b/stand/kboot/libkboot/efi.c @@ -1,134 +1,269 @@ /* * Copyright (c) 2024 Netflix, Inc * * SPDX-License-Identifier: BSD-2-Clause */ #include +#include #include "stand.h" +#include "bootstrap.h" #include "efi.h" #include "seg.h" +#include "util.h" + +vm_paddr_t efi_systbl_phys; +struct efi_map_header *efi_map_hdr; +uint32_t efi_map_size; +vm_paddr_t efi_map_phys_src; /* From DTB */ +vm_paddr_t efi_map_phys_dst; /* From our memory map metadata module */ + +void +efi_set_systbl(uint64_t tbl) +{ + efi_systbl_phys = tbl; +} + +#if 0 +/* Note: This is useless since runtime-map is a subset */ +void +efi_read_from_sysfs(void) +{ + uint32_t efisz, sz, map_size; + int entries = 0; + struct efi_md *map; /* Really an array */ + char *buf; + struct stat sb; + char fn[100]; + + /* + * Count the number of entries we have. They are numbered from 0 + * through entries - 1. + */ + do { + printf("Looking at index %d\n", entries); + snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/phys_addr", entries++); + } while (stat(fn, &sb) == 0); + + /* + * We incremented entries one past the first failure, so we need to + * adjust the count and the test for 'nothing found' is against 1. + */ + if (entries == 1) + goto err; + entries--; + + /* XXX lots of copied code, refactor? */ + map_size = sizeof(struct efi_md) * entries; + efisz = roundup2(sizeof(*efi_map_hdr), 16); + sz = efisz + map_size; + buf = malloc(efisz + map_size); + if (buf == NULL) + return; + efi_map_hdr = (struct efi_map_header *)buf; + efi_map_size = sz; + map = (struct efi_md *)(buf + efisz); + bzero(map, sz); + efi_map_hdr->memory_size = map_size; + efi_map_hdr->descriptor_size = sizeof(struct efi_md); + efi_map_hdr->descriptor_version = EFI_MEMORY_DESCRIPTOR_VERSION; + for (int i = 0; i < entries; i++) { + struct efi_md *m; + + printf("Populating index %d\n", i); + m = map + i; + snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/type", i); + if (!file2u32(fn, &m->md_type)) + goto err; + snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/phys_addr", i); + if (!file2u64(fn, &m->md_phys)) + goto err; + snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/virt_addr", i); + if (!file2u64(fn, &m->md_virt)) + goto err; + snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/num_pages", i); + if (!file2u64(fn, &m->md_pages)) + goto err; + snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/attribute", i); + if (!file2u64(fn, &m->md_attr)) + goto err; + } + efi_map_phys_src = 0; + printf("UEFI MAP:\n"); + print_efi_map(efi_map_hdr); + printf("DONE\n"); + return; +err: + printf("Parse error in reading current memory map\n"); +} +#endif + +/* + * We may have no ability to read the PA that this map is in, so pass + * the address to FreeBSD via a rather odd flag entry as the first map + * so early boot can copy the memory map into this space and have the + * rest of the code cope. + */ +bool +efi_read_from_pa(uint64_t pa, uint32_t map_size, uint32_t desc_size, uint32_t vers) +{ + uint32_t efisz, sz; + char *buf; + int fd2, len; + struct efi_md *map; /* Really an array */ + + /* + * We may have no ability to read the PA that this map is in, so pass + * the address to FreeBSD via a rather odd flag entry as the first map + * so early boot can copy the memory map into this space and have the + * rest of the code cope. We also have to round the size of the header + * to 16 byte boundary. + */ + efisz = roundup2(sizeof(*efi_map_hdr), 16); + sz = efisz + map_size; + buf = malloc(efisz + map_size); + if (buf == NULL) + return false; + efi_map_hdr = (struct efi_map_header *)buf; + efi_map_size = sz; + map = (struct efi_md *)(buf + efisz); + bzero(map, sz); + efi_map_hdr->memory_size = map_size; + efi_map_hdr->descriptor_size = desc_size; + efi_map_hdr->descriptor_version = vers; + + /* + * Try to read in the actual UEFI map. This may fail, and that's OK. We just + * won't print the map. + */ + fd2 = open("host:/dev/mem", O_RDONLY); + if (fd2 < 0) + goto no_read; + if (lseek(fd2, pa, SEEK_SET) < 0) + goto no_read; + len = read(fd2, map, sz); + if (len != sz) + goto no_read; + efi_map_phys_src = 0; /* Mark MODINFOMD_EFI_MAP as valid */ + close(fd2); + printf("UEFI MAP:\n"); + print_efi_map(efi_map_hdr); + return (true); + +no_read: /* Just get it the trampoline */ + efi_map_phys_src = pa; + close(fd2); + return (true); +} void foreach_efi_map_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp) { struct efi_md *map, *p; size_t efisz; int ndesc, i; /* * Memory map data provided by UEFI via the GetMemoryMap * Boot Services API. */ efisz = roundup2(sizeof(struct efi_map_header), 16); map = (struct efi_md *)((uint8_t *)efihdr + efisz); if (efihdr->descriptor_size == 0) return; ndesc = efihdr->memory_size / efihdr->descriptor_size; for (i = 0, p = map; i < ndesc; i++, p = efi_next_descriptor(p, efihdr->descriptor_size)) { cb(p, argp); } } /* XXX REFACTOR WITH KERNEL */ static void print_efi_map_entry(struct efi_md *p, void *argp __unused) { const char *type; static const char *types[] = { "Reserved", "LoaderCode", "LoaderData", "BootServicesCode", "BootServicesData", "RuntimeServicesCode", "RuntimeServicesData", "ConventionalMemory", "UnusableMemory", "ACPIReclaimMemory", "ACPIMemoryNVS", "MemoryMappedIO", "MemoryMappedIOPortSpace", "PalCode", "PersistentMemory" }; if (p->md_type < nitems(types)) type = types[p->md_type]; else type = ""; printf("%23s %012lx %012lx %08lx ", type, p->md_phys, p->md_virt, p->md_pages); if (p->md_attr & EFI_MD_ATTR_UC) printf("UC "); if (p->md_attr & EFI_MD_ATTR_WC) printf("WC "); if (p->md_attr & EFI_MD_ATTR_WT) printf("WT "); if (p->md_attr & EFI_MD_ATTR_WB) printf("WB "); if (p->md_attr & EFI_MD_ATTR_UCE) printf("UCE "); if (p->md_attr & EFI_MD_ATTR_WP) printf("WP "); if (p->md_attr & EFI_MD_ATTR_RP) printf("RP "); if (p->md_attr & EFI_MD_ATTR_XP) printf("XP "); if (p->md_attr & EFI_MD_ATTR_NV) printf("NV "); if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE) printf("MORE_RELIABLE "); if (p->md_attr & EFI_MD_ATTR_RO) printf("RO "); if (p->md_attr & EFI_MD_ATTR_RT) printf("RUNTIME"); printf("\n"); } void print_efi_map(struct efi_map_header *efihdr) { printf("%23s %12s %12s %8s %4s\n", "Type", "Physical", "Virtual", "#Pages", "Attr"); foreach_efi_map_entry(efihdr, print_efi_map_entry, NULL); } -static void -efi_map_entry_add_avail(struct efi_md *p, void *argp) +void +efi_bi_loadsmap(struct preloaded_file *kfp) { - bool *retval = argp; - /* - * The kernel itself uses a lot more types as memory it can use. Be - * conservative here so we don't overwrite anything during the reboot - * process which copies the new kernel (so we can't use the Linux kenrel - * space for example). Anything that's not free, we simply don't add to - * the system ram space. We just need to find a big enough place we can - * land the kernel, and most of the other types we might use are - * typically too small anyway, even if we could safely use them. + * Make a note of a systbl. This is nearly mandatory on AARCH64. */ - if (p->md_type != EFI_MD_TYPE_FREE) - return; + if (efi_systbl_phys) + file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(efi_systbl_phys), &efi_systbl_phys); /* - * The memory map is always disjoint, so we never have to remove avail. + * If we have efi_map_hdr, then it's a pointer to the PA where this + * memory map lives. The trampoline code will copy it over. If we don't + * have it, panic because /proc/iomem isn't sufficient and there's no + * hope. */ - add_avail(p->md_phys, p->md_phys + p->md_pages * EFI_PAGE_SIZE - 1, - SYSTEM_RAM); - *retval = true; -} - -bool -populate_avail_from_efi(struct efi_map_header *efihdr) -{ - bool retval = false; + if (efi_map_hdr != NULL) { + file_addmetadata(kfp, MODINFOMD_EFI_MAP, efi_map_size, efi_map_hdr); + return; + } - init_avail(); - foreach_efi_map_entry(efihdr, efi_map_entry_add_avail, &retval); - return retval; + panic("Can't get UEFI memory map, nor a pointer to it, can't proceed.\n"); }