git: 92ad79ec33fb - main - kboot: Move common EFI stuff from aarch64 to libkboot

From: Warner Losh <imp_at_FreeBSD.org>
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");
 }