1 /* 2 * Copyright (c) 2024 Netflix, Inc 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #include <sys/param.h> 8 #include <sys/linker.h> 9 #include "stand.h" 10 #include "bootstrap.h" 11 #include "efi.h" 12 #include "seg.h" 13 #include "util.h" 14 15 vm_paddr_t efi_systbl_phys; 16 struct efi_map_header *efi_map_hdr; 17 uint32_t efi_map_size; 18 vm_paddr_t efi_map_phys_src; /* From DTB */ 19 vm_paddr_t efi_map_phys_dst; /* From our memory map metadata module */ 20 21 void 22 efi_set_systbl(uint64_t tbl) 23 { 24 efi_systbl_phys = tbl; 25 } 26 27 #if 0 28 /* Note: This is useless since runtime-map is a subset */ 29 void 30 efi_read_from_sysfs(void) 31 { 32 uint32_t efisz, sz, map_size; 33 int entries = 0; 34 struct efi_md *map; /* Really an array */ 35 char *buf; 36 struct stat sb; 37 char fn[100]; 38 39 /* 40 * Count the number of entries we have. They are numbered from 0 41 * through entries - 1. 42 */ 43 do { 44 printf("Looking at index %d\n", entries); 45 snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/phys_addr", entries++); 46 } while (stat(fn, &sb) == 0); 47 48 /* 49 * We incremented entries one past the first failure, so we need to 50 * adjust the count and the test for 'nothing found' is against 1. 51 */ 52 if (entries == 1) 53 goto err; 54 entries--; 55 56 /* XXX lots of copied code, refactor? */ 57 map_size = sizeof(struct efi_md) * entries; 58 efisz = roundup2(sizeof(*efi_map_hdr), 16); 59 sz = efisz + map_size; 60 buf = malloc(efisz + map_size); 61 if (buf == NULL) 62 return; 63 efi_map_hdr = (struct efi_map_header *)buf; 64 efi_map_size = sz; 65 map = (struct efi_md *)(buf + efisz); 66 bzero(map, sz); 67 efi_map_hdr->memory_size = map_size; 68 efi_map_hdr->descriptor_size = sizeof(struct efi_md); 69 efi_map_hdr->descriptor_version = EFI_MEMORY_DESCRIPTOR_VERSION; 70 for (int i = 0; i < entries; i++) { 71 struct efi_md *m; 72 73 printf("Populating index %d\n", i); 74 m = map + i; 75 snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/type", i); 76 if (!file2u32(fn, &m->md_type)) 77 goto err; 78 snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/phys_addr", i); 79 if (!file2u64(fn, &m->md_phys)) 80 goto err; 81 snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/virt_addr", i); 82 if (!file2u64(fn, &m->md_virt)) 83 goto err; 84 snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/num_pages", i); 85 if (!file2u64(fn, &m->md_pages)) 86 goto err; 87 snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/attribute", i); 88 if (!file2u64(fn, &m->md_attr)) 89 goto err; 90 } 91 efi_map_phys_src = 0; 92 printf("UEFI MAP:\n"); 93 print_efi_map(efi_map_hdr); 94 printf("DONE\n"); 95 return; 96 err: 97 printf("Parse error in reading current memory map\n"); 98 } 99 #endif 100 101 /* 102 * We may have no ability to read the PA that this map is in, so pass 103 * the address to FreeBSD via a rather odd flag entry as the first map 104 * so early boot can copy the memory map into this space and have the 105 * rest of the code cope. 106 */ 107 bool 108 efi_read_from_pa(uint64_t pa, uint32_t map_size, uint32_t desc_size, uint32_t vers) 109 { 110 uint32_t efisz, sz; 111 char *buf; 112 int fd2, len; 113 struct efi_md *map; /* Really an array */ 114 115 /* 116 * We may have no ability to read the PA that this map is in, so pass 117 * the address to FreeBSD via a rather odd flag entry as the first map 118 * so early boot can copy the memory map into this space and have the 119 * rest of the code cope. We also have to round the size of the header 120 * to 16 byte boundary. 121 */ 122 efisz = roundup2(sizeof(*efi_map_hdr), 16); 123 sz = efisz + map_size; 124 buf = malloc(efisz + map_size); 125 if (buf == NULL) 126 return false; 127 efi_map_hdr = (struct efi_map_header *)buf; 128 efi_map_size = sz; 129 map = (struct efi_md *)(buf + efisz); 130 bzero(map, sz); 131 efi_map_hdr->memory_size = map_size; 132 efi_map_hdr->descriptor_size = desc_size; 133 efi_map_hdr->descriptor_version = vers; 134 135 /* 136 * Try to read in the actual UEFI map. This may fail, and that's OK. We just 137 * won't print the map. 138 */ 139 fd2 = open("host:/dev/mem", O_RDONLY); 140 if (fd2 < 0) 141 goto no_read; 142 if (lseek(fd2, pa, SEEK_SET) < 0) 143 goto no_read; 144 len = read(fd2, map, sz); 145 if (len != sz) 146 goto no_read; 147 efi_map_phys_src = 0; /* Mark MODINFOMD_EFI_MAP as valid */ 148 close(fd2); 149 printf("UEFI MAP:\n"); 150 print_efi_map(efi_map_hdr); 151 return (true); 152 153 no_read: /* Just get it the trampoline */ 154 efi_map_phys_src = pa; 155 close(fd2); 156 return (true); 157 } 158 159 void 160 foreach_efi_map_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp) 161 { 162 struct efi_md *map, *p; 163 size_t efisz; 164 int ndesc, i; 165 166 /* 167 * Memory map data provided by UEFI via the GetMemoryMap 168 * Boot Services API. 169 */ 170 efisz = roundup2(sizeof(struct efi_map_header), 16); 171 map = (struct efi_md *)((uint8_t *)efihdr + efisz); 172 173 if (efihdr->descriptor_size == 0) 174 return; 175 ndesc = efihdr->memory_size / efihdr->descriptor_size; 176 177 for (i = 0, p = map; i < ndesc; i++, 178 p = efi_next_descriptor(p, efihdr->descriptor_size)) { 179 cb(p, argp); 180 } 181 } 182 183 /* XXX REFACTOR WITH KERNEL */ 184 static void 185 print_efi_map_entry(struct efi_md *p, void *argp __unused) 186 { 187 const char *type; 188 static const char *types[] = { 189 "Reserved", 190 "LoaderCode", 191 "LoaderData", 192 "BootServicesCode", 193 "BootServicesData", 194 "RuntimeServicesCode", 195 "RuntimeServicesData", 196 "ConventionalMemory", 197 "UnusableMemory", 198 "ACPIReclaimMemory", 199 "ACPIMemoryNVS", 200 "MemoryMappedIO", 201 "MemoryMappedIOPortSpace", 202 "PalCode", 203 "PersistentMemory" 204 }; 205 206 if (p->md_type < nitems(types)) 207 type = types[p->md_type]; 208 else 209 type = "<INVALID>"; 210 printf("%23s %012lx %012lx %08lx ", type, p->md_phys, 211 p->md_virt, p->md_pages); 212 if (p->md_attr & EFI_MD_ATTR_UC) 213 printf("UC "); 214 if (p->md_attr & EFI_MD_ATTR_WC) 215 printf("WC "); 216 if (p->md_attr & EFI_MD_ATTR_WT) 217 printf("WT "); 218 if (p->md_attr & EFI_MD_ATTR_WB) 219 printf("WB "); 220 if (p->md_attr & EFI_MD_ATTR_UCE) 221 printf("UCE "); 222 if (p->md_attr & EFI_MD_ATTR_WP) 223 printf("WP "); 224 if (p->md_attr & EFI_MD_ATTR_RP) 225 printf("RP "); 226 if (p->md_attr & EFI_MD_ATTR_XP) 227 printf("XP "); 228 if (p->md_attr & EFI_MD_ATTR_NV) 229 printf("NV "); 230 if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE) 231 printf("MORE_RELIABLE "); 232 if (p->md_attr & EFI_MD_ATTR_RO) 233 printf("RO "); 234 if (p->md_attr & EFI_MD_ATTR_RT) 235 printf("RUNTIME"); 236 printf("\n"); 237 } 238 239 void 240 print_efi_map(struct efi_map_header *efihdr) 241 { 242 printf("%23s %12s %12s %8s %4s\n", 243 "Type", "Physical", "Virtual", "#Pages", "Attr"); 244 245 foreach_efi_map_entry(efihdr, print_efi_map_entry, NULL); 246 } 247 248 void 249 efi_bi_loadsmap(struct preloaded_file *kfp) 250 { 251 /* 252 * Make a note of a systbl. This is nearly mandatory on AARCH64. 253 */ 254 if (efi_systbl_phys) 255 file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(efi_systbl_phys), &efi_systbl_phys); 256 257 /* 258 * If we have efi_map_hdr, then it's a pointer to the PA where this 259 * memory map lives. The trampoline code will copy it over. If we don't 260 * have it, panic because /proc/iomem isn't sufficient and there's no 261 * hope. 262 */ 263 if (efi_map_hdr != NULL) { 264 file_addmetadata(kfp, MODINFOMD_EFI_MAP, efi_map_size, efi_map_hdr); 265 return; 266 } 267 268 panic("Can't get UEFI memory map, nor a pointer to it, can't proceed.\n"); 269 } 270