git: 1c98cd1569de - main - kboot: aarch64 memory enumeration enumerate_memory_arch()

From: Warner Losh <imp_at_FreeBSD.org>
Date: Fri, 03 Feb 2023 15:50:40 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=1c98cd1569de393ba3caa5e79042b5a12cdf5d23

commit 1c98cd1569de393ba3caa5e79042b5a12cdf5d23
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2023-02-03 15:38:36 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2023-02-03 15:41:39 +0000

    kboot: aarch64 memory enumeration enumerate_memory_arch()
    
    We have an odd situation with aarch64 memory enumeration. The fdt that
    we can get has a PA of the UEFI memory map, as modified by the current
    running Linux kernel so it can retain those pages it needs for EFI and
    other services. We have to pass in this EFI tablem but don't have access
    to it in the boot loader. We do in the trampoline code, so a forthcoming
    commit will copy it there for the kernel to use. All for want of /dev/mem
    in the target environment sometimes.
    
    However, we also have to find a place to load the kernel, so we have to
    fallback to /proc/iomem when we can't read the UEFI memory map directly
    from /dev/mem. It will give us good enough results to do this task. This
    table isn't quite suitable to be converted to the EFI table, so we use
    both methods. We'll fall back to this method also if there's no EFI
    table advertised in the fdt. There's no /sys file on aarch64 that has
    this information, hence using the old-style /proc/iomem. We're unlikely
    to work if there's no EFI, though.
    
    Note: The underlying Linux mechanism is different than the amd64 method
    which seems like it should be MI, but unimplemented on aarch64.
    
    Sponsored by:           Netflix
    Discussed with:         kevans
    Differential Revision:  https://reviews.freebsd.org/D38249
---
 stand/kboot/arch/aarch64/load_addr.c | 154 +++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/stand/kboot/arch/aarch64/load_addr.c b/stand/kboot/arch/aarch64/load_addr.c
index e69de29bb2d1..ad71a4f9fbb1 100644
--- a/stand/kboot/arch/aarch64/load_addr.c
+++ b/stand/kboot/arch/aarch64/load_addr.c
@@ -0,0 +1,154 @@
+/*-
+ * Copyright (c) 2022 Netflix, Inc
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/efi.h>
+#include <machine/metadata.h>
+#include <sys/linker.h>
+#include <fdt_platform.h>
+#include <libfdt.h>
+
+#include "kboot.h"
+#include "bootstrap.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;
+	uint32_t sz, ver, esz, efisz;
+	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);
+	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 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 = (sizeof(*efihdr) + 0xf) & ~0xf;
+	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;
+	efi_map_phys_src = mmap_pa;
+	efi_map_hdr = efihdr;
+	efi_map_size = sz + efisz;
+
+	return true;
+errout:
+	free(buf);
+	return false;
+}
+
+bool
+enumerate_memory_arch(void)
+{
+	int fd = -1;
+	bool rv = false;
+
+	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.
+		 */
+	}
+	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);
+
+	s = first_avail(KERN_ALIGN, HOLE_SIZE, SYSTEM_RAM);
+	if (s != 0)
+		return (s);
+	s = 0x40000000 | 0x4200000;	/* should never get here */
+	printf("Falling back to crazy address %#lx\n", s);
+	return (s);
+}