From 1c98cd1569de393ba3caa5e79042b5a12cdf5d23 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Fri, 3 Feb 2023 08:38:36 -0700 Subject: [PATCH] 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 +#include +#include +#include +#include +#include + +#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); +}