git: 92ad79ec33fb - main - kboot: Move common EFI stuff from aarch64 to libkboot
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 17 Apr 2025 21:59:17 UTC
The branch main has been updated by imp: URL: https://cgit.FreeBSD.org/src/commit/?id=92ad79ec33fb5caf9a79c0bd8b33697b34c8e26d commit 92ad79ec33fb5caf9a79c0bd8b33697b34c8e26d Author: Warner Losh <imp@FreeBSD.org> AuthorDate: 2025-04-17 04:04:31 +0000 Commit: Warner Losh <imp@FreeBSD.org> CommitDate: 2025-04-17 21:56:46 +0000 kboot: Move common EFI stuff from aarch64 to libkboot Move efi_read_from_pa, efi_set_systbl and efi_bi_loadsmap into efi.c Move prototype for populate_avail_from_efi as well, though that arrived previously. Move efi memory map variables into efi.c. Add efi_read_from_sysfs, but if 0 it out. We'll want to use it if we can get the proposed /sys/firmware/efi/memmap data published in the same format and the code works, it's just that the current /sys/firmware/efi/runtime-memmap isn't complete enough to use. Sponsored by: Netflix Reviewed by: kevans, jhibbits Differential Revision: https://reviews.freebsd.org/D49863 --- stand/kboot/include/efi.h | 11 +- stand/kboot/kboot/arch/aarch64/exec.c | 7 +- stand/kboot/kboot/arch/aarch64/load_addr.c | 121 ++++--------------- stand/kboot/libkboot/Makefile | 1 + stand/kboot/libkboot/efi.c | 187 +++++++++++++++++++++++++---- 5 files changed, 200 insertions(+), 127 deletions(-) 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 @@ -13,6 +13,15 @@ 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 @@ -41,6 +41,7 @@ #include <machine/metadata.h> #include "bootstrap.h" +#include "efi.h" #include "kboot.h" #include "platform/acfreebsd.h" @@ -65,11 +66,7 @@ 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); 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 @@ -11,30 +11,18 @@ #include <libfdt.h> #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; @@ -60,7 +48,7 @@ do_memory_from_fdt(int fd) 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; @@ -82,62 +70,8 @@ do_memory_from_fdt(int fd) 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); @@ -150,22 +84,32 @@ 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; @@ -181,32 +125,19 @@ kboot_get_phys_load_segment(void) 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 @@ -5,6 +5,7 @@ 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 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 @@ -5,9 +5,156 @@ */ #include <sys/param.h> +#include <sys/linker.h> #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) @@ -98,37 +245,25 @@ print_efi_map(struct efi_map_header *efihdr) 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"); }