1091c255bSWarner Losh /*- 2091c255bSWarner Losh * Copyright (c) 2022 Netflix, Inc 3091c255bSWarner Losh * 4091c255bSWarner Losh * SPDX-License-Identifier: BSD-2-Clause 5091c255bSWarner Losh */ 6091c255bSWarner Losh 7091c255bSWarner Losh #include <sys/param.h> 8091c255bSWarner Losh #include <sys/efi.h> 9091c255bSWarner Losh #include <machine/metadata.h> 10091c255bSWarner Losh #include <sys/linker.h> 11091c255bSWarner Losh #include <fdt_platform.h> 12091c255bSWarner Losh #include <libfdt.h> 13091c255bSWarner Losh 14091c255bSWarner Losh #include "kboot.h" 15091c255bSWarner Losh #include "bootstrap.h" 16091c255bSWarner Losh 17091c255bSWarner Losh /* 18091c255bSWarner Losh * Info from dtb about the EFI system 19091c255bSWarner Losh */ 20091c255bSWarner Losh vm_paddr_t efi_systbl_phys; 21091c255bSWarner Losh struct efi_map_header *efi_map_hdr; 22091c255bSWarner Losh uint32_t efi_map_size; 23091c255bSWarner Losh vm_paddr_t efi_map_phys_src; /* From DTB */ 24091c255bSWarner Losh vm_paddr_t efi_map_phys_dst; /* From our memory map metadata module */ 25091c255bSWarner Losh 26*a9cd3b67SWarner Losh typedef void (*efi_map_entry_cb)(struct efi_md *, void *argp); 27*a9cd3b67SWarner Losh 28*a9cd3b67SWarner Losh static void 29*a9cd3b67SWarner Losh foreach_efi_map_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp) 30*a9cd3b67SWarner Losh { 31*a9cd3b67SWarner Losh struct efi_md *map, *p; 32*a9cd3b67SWarner Losh size_t efisz; 33*a9cd3b67SWarner Losh int ndesc, i; 34*a9cd3b67SWarner Losh 35*a9cd3b67SWarner Losh /* 36*a9cd3b67SWarner Losh * Memory map data provided by UEFI via the GetMemoryMap 37*a9cd3b67SWarner Losh * Boot Services API. 38*a9cd3b67SWarner Losh */ 39*a9cd3b67SWarner Losh efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; 40*a9cd3b67SWarner Losh map = (struct efi_md *)((uint8_t *)efihdr + efisz); 41*a9cd3b67SWarner Losh 42*a9cd3b67SWarner Losh if (efihdr->descriptor_size == 0) 43*a9cd3b67SWarner Losh return; 44*a9cd3b67SWarner Losh ndesc = efihdr->memory_size / efihdr->descriptor_size; 45*a9cd3b67SWarner Losh 46*a9cd3b67SWarner Losh for (i = 0, p = map; i < ndesc; i++, 47*a9cd3b67SWarner Losh p = efi_next_descriptor(p, efihdr->descriptor_size)) { 48*a9cd3b67SWarner Losh cb(p, argp); 49*a9cd3b67SWarner Losh } 50*a9cd3b67SWarner Losh } 51*a9cd3b67SWarner Losh 52*a9cd3b67SWarner Losh static void 53*a9cd3b67SWarner Losh print_efi_map_entry(struct efi_md *p, void *argp __unused) 54*a9cd3b67SWarner Losh { 55*a9cd3b67SWarner Losh const char *type; 56*a9cd3b67SWarner Losh static const char *types[] = { 57*a9cd3b67SWarner Losh "Reserved", 58*a9cd3b67SWarner Losh "LoaderCode", 59*a9cd3b67SWarner Losh "LoaderData", 60*a9cd3b67SWarner Losh "BootServicesCode", 61*a9cd3b67SWarner Losh "BootServicesData", 62*a9cd3b67SWarner Losh "RuntimeServicesCode", 63*a9cd3b67SWarner Losh "RuntimeServicesData", 64*a9cd3b67SWarner Losh "ConventionalMemory", 65*a9cd3b67SWarner Losh "UnusableMemory", 66*a9cd3b67SWarner Losh "ACPIReclaimMemory", 67*a9cd3b67SWarner Losh "ACPIMemoryNVS", 68*a9cd3b67SWarner Losh "MemoryMappedIO", 69*a9cd3b67SWarner Losh "MemoryMappedIOPortSpace", 70*a9cd3b67SWarner Losh "PalCode", 71*a9cd3b67SWarner Losh "PersistentMemory" 72*a9cd3b67SWarner Losh }; 73*a9cd3b67SWarner Losh 74*a9cd3b67SWarner Losh if (p->md_type < nitems(types)) 75*a9cd3b67SWarner Losh type = types[p->md_type]; 76*a9cd3b67SWarner Losh else 77*a9cd3b67SWarner Losh type = "<INVALID>"; 78*a9cd3b67SWarner Losh printf("%23s %012lx %012lx %08lx ", type, p->md_phys, 79*a9cd3b67SWarner Losh p->md_virt, p->md_pages); 80*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_UC) 81*a9cd3b67SWarner Losh printf("UC "); 82*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_WC) 83*a9cd3b67SWarner Losh printf("WC "); 84*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_WT) 85*a9cd3b67SWarner Losh printf("WT "); 86*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_WB) 87*a9cd3b67SWarner Losh printf("WB "); 88*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_UCE) 89*a9cd3b67SWarner Losh printf("UCE "); 90*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_WP) 91*a9cd3b67SWarner Losh printf("WP "); 92*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_RP) 93*a9cd3b67SWarner Losh printf("RP "); 94*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_XP) 95*a9cd3b67SWarner Losh printf("XP "); 96*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_NV) 97*a9cd3b67SWarner Losh printf("NV "); 98*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE) 99*a9cd3b67SWarner Losh printf("MORE_RELIABLE "); 100*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_RO) 101*a9cd3b67SWarner Losh printf("RO "); 102*a9cd3b67SWarner Losh if (p->md_attr & EFI_MD_ATTR_RT) 103*a9cd3b67SWarner Losh printf("RUNTIME"); 104*a9cd3b67SWarner Losh printf("\n"); 105*a9cd3b67SWarner Losh } 106*a9cd3b67SWarner Losh 107091c255bSWarner Losh static bool 108091c255bSWarner Losh do_memory_from_fdt(int fd) 109091c255bSWarner Losh { 110091c255bSWarner Losh struct stat sb; 111091c255bSWarner Losh char *buf = NULL; 112091c255bSWarner Losh int len, offset, fd2 = -1; 113091c255bSWarner Losh uint32_t sz, ver, esz, efisz; 114091c255bSWarner Losh uint64_t mmap_pa; 115091c255bSWarner Losh const uint32_t *u32p; 116091c255bSWarner Losh const uint64_t *u64p; 117091c255bSWarner Losh struct efi_map_header *efihdr; 118091c255bSWarner Losh struct efi_md *map; 119091c255bSWarner Losh 120091c255bSWarner Losh if (fstat(fd, &sb) < 0) 121091c255bSWarner Losh return false; 122091c255bSWarner Losh buf = malloc(sb.st_size); 123091c255bSWarner Losh if (buf == NULL) 124091c255bSWarner Losh return false; 125091c255bSWarner Losh len = read(fd, buf, sb.st_size); 126091c255bSWarner Losh /* NB: we're reading this from sysfs, so mismatch OK */ 127091c255bSWarner Losh if (len <= 0) 128091c255bSWarner Losh goto errout; 129091c255bSWarner Losh 130091c255bSWarner Losh /* 131091c255bSWarner Losh * Look for /chosen to find these values: 132091c255bSWarner Losh * linux,uefi-system-table PA of the UEFI System Table. 133091c255bSWarner Losh * linux,uefi-mmap-start PA of the UEFI memory map 134091c255bSWarner Losh * linux,uefi-mmap-size Size of mmap 135091c255bSWarner Losh * linux,uefi-mmap-desc-size Size of each entry of mmap 136091c255bSWarner Losh * linux,uefi-mmap-desc-ver Format version, should be 1 137091c255bSWarner Losh */ 138091c255bSWarner Losh offset = fdt_path_offset(buf, "/chosen"); 139091c255bSWarner Losh if (offset <= 0) 140091c255bSWarner Losh goto errout; 141091c255bSWarner Losh u64p = fdt_getprop(buf, offset, "linux,uefi-system-table", &len); 142091c255bSWarner Losh if (u64p == NULL) 143091c255bSWarner Losh goto errout; 144091c255bSWarner Losh efi_systbl_phys = fdt64_to_cpu(*u64p); 145091c255bSWarner Losh u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-ver", &len); 146091c255bSWarner Losh if (u32p == NULL) 147091c255bSWarner Losh goto errout; 148091c255bSWarner Losh ver = fdt32_to_cpu(*u32p); 149091c255bSWarner Losh u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-size", &len); 150091c255bSWarner Losh if (u32p == NULL) 151091c255bSWarner Losh goto errout; 152091c255bSWarner Losh esz = fdt32_to_cpu(*u32p); 153091c255bSWarner Losh u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-size", &len); 154091c255bSWarner Losh if (u32p == NULL) 155091c255bSWarner Losh goto errout; 156091c255bSWarner Losh sz = fdt32_to_cpu(*u32p); 157091c255bSWarner Losh u64p = fdt_getprop(buf, offset, "linux,uefi-mmap-start", &len); 158091c255bSWarner Losh if (u64p == NULL) 159091c255bSWarner Losh goto errout; 160091c255bSWarner Losh mmap_pa = fdt64_to_cpu(*u64p); 161091c255bSWarner Losh free(buf); 162091c255bSWarner Losh 163091c255bSWarner Losh printf("UEFI MMAP: Ver %d Ent Size %d Tot Size %d PA %#lx\n", 164091c255bSWarner Losh ver, esz, sz, mmap_pa); 165091c255bSWarner Losh 166091c255bSWarner Losh /* 167091c255bSWarner Losh * We may have no ability to read the PA that this map is in, so pass 168091c255bSWarner Losh * the address to FreeBSD via a rather odd flag entry as the first map 169091c255bSWarner Losh * so early boot can copy the memory map into this space and have the 170091c255bSWarner Losh * rest of the code cope. 171091c255bSWarner Losh */ 172091c255bSWarner Losh efisz = (sizeof(*efihdr) + 0xf) & ~0xf; 173091c255bSWarner Losh buf = malloc(sz + efisz); 174091c255bSWarner Losh if (buf == NULL) 175091c255bSWarner Losh return false; 176091c255bSWarner Losh efihdr = (struct efi_map_header *)buf; 177091c255bSWarner Losh map = (struct efi_md *)((uint8_t *)efihdr + efisz); 178091c255bSWarner Losh bzero(map, sz); 179091c255bSWarner Losh efihdr->memory_size = sz; 180091c255bSWarner Losh efihdr->descriptor_size = esz; 181091c255bSWarner Losh efihdr->descriptor_version = ver; 182091c255bSWarner Losh 183091c255bSWarner Losh /* 184091c255bSWarner Losh * Save EFI table. Either this will be an empty table filled in by the trampoline, 185091c255bSWarner Losh * or we'll read it below. Either way, set these two variables so we share the best 186091c255bSWarner Losh * UEFI memory map with the kernel. 187091c255bSWarner Losh */ 188091c255bSWarner Losh efi_map_hdr = efihdr; 189091c255bSWarner Losh efi_map_size = sz + efisz; 190091c255bSWarner Losh 191091c255bSWarner Losh /* 192091c255bSWarner Losh * Try to read in the actual UEFI map. 193091c255bSWarner Losh */ 194091c255bSWarner Losh fd2 = open("host:/dev/mem", O_RDONLY); 195091c255bSWarner Losh if (fd2 < 0) { 196091c255bSWarner Losh printf("Will read UEFI mem map in tramp: no /dev/mem, need CONFIG_DEVMEM=y\n"); 197091c255bSWarner Losh goto no_read; 198091c255bSWarner Losh } 199091c255bSWarner Losh if (lseek(fd2, mmap_pa, SEEK_SET) < 0) { 200091c255bSWarner Losh printf("Will read UEFI mem map in tramp: lseek failed\n"); 201091c255bSWarner Losh goto no_read; 202091c255bSWarner Losh } 203091c255bSWarner Losh len = read(fd2, map, sz); 204091c255bSWarner Losh if (len != sz) { 205091c255bSWarner Losh if (len < 0 && errno == EPERM) 206091c255bSWarner Losh printf("Will read UEFI mem map in tramp: kernel needs CONFIG_STRICT_DEVMEM=n\n"); 207091c255bSWarner Losh else 208091c255bSWarner Losh printf("Will read UEFI mem map in tramp: lean = %d errno = %d\n", len, errno); 209091c255bSWarner Losh goto no_read; 210091c255bSWarner Losh } 211091c255bSWarner Losh printf("Read UEFI mem map from physmem\n"); 212091c255bSWarner Losh efi_map_phys_src = 0; /* Mark MODINFOMD_EFI_MAP as valid */ 213091c255bSWarner Losh close(fd2); 214*a9cd3b67SWarner Losh printf("UEFI MAP:\n"); 215*a9cd3b67SWarner Losh printf("%23s %12s %12s %8s %4s\n", 216*a9cd3b67SWarner Losh "Type", "Physical", "Virtual", "#Pages", "Attr"); 217*a9cd3b67SWarner Losh foreach_efi_map_entry(efihdr, print_efi_map_entry, NULL); 218091c255bSWarner Losh return true; /* OK, we really have the memory map */ 219091c255bSWarner Losh 220091c255bSWarner Losh no_read: 221091c255bSWarner Losh efi_map_phys_src = mmap_pa; 222091c255bSWarner Losh close(fd2); 223091c255bSWarner Losh return true; /* We can get it the trampoline */ 224091c255bSWarner Losh 225091c255bSWarner Losh errout: 226091c255bSWarner Losh free(buf); 227091c255bSWarner Losh return false; 228091c255bSWarner Losh } 229091c255bSWarner Losh 230091c255bSWarner Losh bool 231091c255bSWarner Losh enumerate_memory_arch(void) 232091c255bSWarner Losh { 233091c255bSWarner Losh int fd = -1; 234091c255bSWarner Losh bool rv = false; 235091c255bSWarner Losh 236091c255bSWarner Losh fd = open("host:/sys/firmware/fdt", O_RDONLY); 237091c255bSWarner Losh if (fd != -1) { 238091c255bSWarner Losh rv = do_memory_from_fdt(fd); 239091c255bSWarner Losh close(fd); 240091c255bSWarner Losh /* 241091c255bSWarner Losh * So, we have physaddr to the memory table. However, we can't 242091c255bSWarner Losh * open /dev/mem on some platforms to get the actual table. So 243091c255bSWarner Losh * we have to fall through to get it from /proc/iomem. 244091c255bSWarner Losh */ 245091c255bSWarner Losh } 246091c255bSWarner Losh if (!rv) { 247091c255bSWarner Losh printf("Could not obtain UEFI memory tables, expect failure\n"); 248091c255bSWarner Losh } 249091c255bSWarner Losh 250091c255bSWarner Losh populate_avail_from_iomem(); 251091c255bSWarner Losh 252091c255bSWarner Losh print_avail(); 253091c255bSWarner Losh 254091c255bSWarner Losh return true; 255091c255bSWarner Losh } 256091c255bSWarner Losh 257091c255bSWarner Losh uint64_t 258091c255bSWarner Losh kboot_get_phys_load_segment(void) 259091c255bSWarner Losh { 260091c255bSWarner Losh #define HOLE_SIZE (64ul << 20) 261091c255bSWarner Losh #define KERN_ALIGN (2ul << 20) 262091c255bSWarner Losh static uint64_t s = 0; 263091c255bSWarner Losh 264091c255bSWarner Losh if (s != 0) 265091c255bSWarner Losh return (s); 266091c255bSWarner Losh 267091c255bSWarner Losh s = first_avail(KERN_ALIGN, HOLE_SIZE, SYSTEM_RAM); 268091c255bSWarner Losh if (s != 0) 269091c255bSWarner Losh return (s); 270091c255bSWarner Losh s = 0x40000000 | 0x4200000; /* should never get here */ 271091c255bSWarner Losh printf("Falling back to crazy address %#lx\n", s); 272091c255bSWarner Losh return (s); 273091c255bSWarner Losh } 274091c255bSWarner Losh 275091c255bSWarner Losh void 276091c255bSWarner Losh bi_loadsmap(struct preloaded_file *kfp) 277091c255bSWarner Losh { 278091c255bSWarner Losh 279091c255bSWarner Losh /* 280091c255bSWarner Losh * Make a note of a systbl. This is nearly mandatory on AARCH64. 281091c255bSWarner Losh */ 282091c255bSWarner Losh if (efi_systbl_phys) 283091c255bSWarner Losh file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(efi_systbl_phys), &efi_systbl_phys); 284091c255bSWarner Losh 285091c255bSWarner Losh /* 286091c255bSWarner Losh * If we have efi_map_hdr, then it's a pointer to the PA where this 287091c255bSWarner Losh * memory map lives. The trampoline code will copy it over. If we don't 288091c255bSWarner Losh * have it, we use whatever we found in /proc/iomap. 289091c255bSWarner Losh */ 290091c255bSWarner Losh if (efi_map_hdr != NULL) { 291091c255bSWarner Losh file_addmetadata(kfp, MODINFOMD_EFI_MAP, efi_map_size, efi_map_hdr); 292091c255bSWarner Losh return; 293091c255bSWarner Losh } 294091c255bSWarner Losh panic("Can't get UEFI memory map, nor a pointer to it, can't proceed.\n"); 295091c255bSWarner Losh } 296