1/* $NetBSD: efimemory.c,v 1.10 2023/05/14 09:07:54 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka@netbsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include "efiboot.h" 30 31#include <bootinfo.h> 32 33static const char *efi_memory_type[] = { 34 [EfiReservedMemoryType] = "Reserved Memory Type", 35 [EfiLoaderCode] = "Loader Code", 36 [EfiLoaderData] = "Loader Data", 37 [EfiBootServicesCode] = "Boot Services Code", 38 [EfiBootServicesData] = "Boot Services Data", 39 [EfiRuntimeServicesCode] = "Runtime Services Code", 40 [EfiRuntimeServicesData] = "Runtime Services Data", 41 [EfiConventionalMemory] = "Conventional Memory", 42 [EfiUnusableMemory] = "Unusable Memory", 43 [EfiACPIReclaimMemory] = "ACPI Reclaim Memory", 44 [EfiACPIMemoryNVS] = "ACPI Memory NVS", 45 [EfiMemoryMappedIO] = "MMIO", 46 [EfiMemoryMappedIOPortSpace] = "MMIO (Port Space)", 47 [EfiPalCode] = "Pal Code", 48 [EfiPersistentMemory] = "Persistent Memory", 49}; 50 51#ifndef KERN_LOADSPACE_SIZE 52#define KERN_LOADSPACE_SIZE (128 * 1024 * 1024) /* 128MiB */ 53#endif 54 55static int 56getmemtype(EFI_MEMORY_DESCRIPTOR *md) 57{ 58 59 switch (md->Type) { 60 case EfiLoaderCode: 61 case EfiLoaderData: 62 case EfiBootServicesCode: 63 case EfiBootServicesData: 64 case EfiConventionalMemory: 65 return (md->Attribute & EFI_MEMORY_WB) ? 66 BIM_Memory : BIM_Reserved; 67 68 case EfiACPIReclaimMemory: 69 return BIM_ACPI; 70 71 case EfiACPIMemoryNVS: 72 return BIM_NVS; 73 74 case EfiPersistentMemory: 75 return BIM_PMEM; 76 77 case EfiReservedMemoryType: 78 case EfiRuntimeServicesCode: 79 case EfiRuntimeServicesData: 80 case EfiUnusableMemory: 81 case EfiMemoryMappedIO: 82 case EfiMemoryMappedIOPortSpace: 83 case EfiPalCode: 84 case EfiMaxMemoryType: 85 default: 86 return BIM_Reserved; 87 } 88} 89 90EFI_MEMORY_DESCRIPTOR * 91efi_memory_get_map(UINTN *NoEntries, UINTN *MapKey, UINTN *DescriptorSize, 92 UINT32 *DescriptorVersion, bool sorted) 93{ 94 EFI_MEMORY_DESCRIPTOR *desc, *md, *next, *target, *tmp; 95 UINTN i, j; 96 97 *NoEntries = 0; 98 desc = LibMemoryMap(NoEntries, MapKey, DescriptorSize, 99 DescriptorVersion); 100 if (desc == NULL) 101 panic("efi_memory_get_map failed"); 102 103 if (!sorted) 104 return desc; 105 106 tmp = alloc(*DescriptorSize); 107 if (tmp == NULL) 108 return desc; 109 110 for (i = 0, md = desc; i < *NoEntries - 1; i++, md = next) { 111 target = next = NextMemoryDescriptor(md, *DescriptorSize); 112 for (j = i + 1; j < *NoEntries; j++) { 113 if (md->PhysicalStart > target->PhysicalStart) { 114 CopyMem(tmp, md, *DescriptorSize); 115 CopyMem(md, target, *DescriptorSize); 116 CopyMem(target, tmp, *DescriptorSize); 117 } 118 target = NextMemoryDescriptor(target, *DescriptorSize); 119 } 120 } 121 dealloc(tmp, *DescriptorSize); 122 123 return desc; 124} 125 126EFI_MEMORY_DESCRIPTOR * 127efi_memory_compact_map(EFI_MEMORY_DESCRIPTOR *desc, UINTN *NoEntries, 128 UINTN DescriptorSize) 129{ 130 EFI_MEMORY_DESCRIPTOR *md, *next, *target, *tmp; 131 UINTN i, j; 132 UINT32 type; 133 bool first = true, do_compact; 134 135 for (i = 0, md = target = desc; i < *NoEntries; i++, md = next) { 136 type = md->Type; 137 switch (type) { 138 case EfiLoaderCode: 139 case EfiLoaderData: 140 case EfiBootServicesCode: 141 case EfiBootServicesData: 142 case EfiConventionalMemory: 143 if ((md->Attribute & EFI_MEMORY_WB) != 0) 144 type = EfiConventionalMemory; 145 if (md->Attribute == target->Attribute) { 146 do_compact = true; 147 break; 148 } 149 /* FALLTHROUGH */ 150 case EfiACPIReclaimMemory: 151 case EfiACPIMemoryNVS: 152 case EfiPersistentMemory: 153 case EfiReservedMemoryType: 154 case EfiRuntimeServicesCode: 155 case EfiRuntimeServicesData: 156 case EfiUnusableMemory: 157 case EfiMemoryMappedIO: 158 case EfiMemoryMappedIOPortSpace: 159 case EfiPalCode: 160 default: 161 do_compact = false; 162 break; 163 } 164 165 if (first) { 166 first = false; 167 } else if (do_compact && 168 type == target->Type && 169 md->Attribute == target->Attribute && 170 md->PhysicalStart == target->PhysicalStart + target->NumberOfPages * EFI_PAGE_SIZE) { 171 /* continuous region */ 172 target->NumberOfPages += md->NumberOfPages; 173 174 tmp = md; 175 for (j = i + 1; j < *NoEntries; j++) { 176 next = NextMemoryDescriptor(md, DescriptorSize); 177 CopyMem(md, next, DescriptorSize); 178 md = next; 179 } 180 next = tmp; 181 182 i--; 183 (*NoEntries)--; 184 continue; 185 } else { 186 target = md; 187 } 188 189 target->Type = type; 190 next = NextMemoryDescriptor(md, DescriptorSize); 191 } 192 193 return desc; 194} 195 196int 197efi_memory_get_memmap(struct bi_memmap_entry **memmapp, size_t *num) 198{ 199 EFI_STATUS status; 200 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next; 201 UINTN i, NoEntries, MapKey, DescriptorSize; 202 UINT32 DescriptorVersion; 203 UINTN cols, rows; 204 struct bi_memmap_entry *memmap; 205 206 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, 207 ST->ConOut->Mode->Mode, &cols, &rows); 208 if (EFI_ERROR(status) || rows <= 2) 209 return -1; 210 211 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize, 212 &DescriptorVersion, true); 213 efi_memory_compact_map(mdtop, &NoEntries, DescriptorSize); 214 215 memmap = alloc(sizeof(*memmap) * NoEntries); 216 217 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) { 218 memmap[i].addr = md->PhysicalStart; 219 memmap[i].size = md->NumberOfPages * EFI_PAGE_SIZE; 220 memmap[i].type = getmemtype(md); 221 222 next = NextMemoryDescriptor(md, DescriptorSize); 223 } 224 225 *memmapp = memmap; 226 *num = NoEntries; 227 return 0; 228} 229 230/* 231 * get memory size below 1MB 232 */ 233int 234getbasemem(void) 235{ 236 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next; 237 UINTN i, NoEntries, MapKey, DescriptorSize, MappingSize; 238 UINT32 DescriptorVersion; 239 EFI_PHYSICAL_ADDRESS basemem = 0, epa; 240 241 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize, 242 &DescriptorVersion, true); 243 244 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) { 245 next = NextMemoryDescriptor(md, DescriptorSize); 246 if (getmemtype(md) != BIM_Memory) 247 continue; 248 if (md->PhysicalStart >= 1 * 1024 * 1024) 249 continue; 250 if (basemem != md->PhysicalStart) 251 continue; 252 253 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE; 254 epa = md->PhysicalStart + MappingSize; 255 if (epa == 0 || epa > 1 * 1024 * 1024) 256 epa = 1 * 1024 * 1024; 257 basemem = epa; 258 } 259 260 FreePool(mdtop); 261 262 return basemem / 1024; /* KiB */ 263} 264 265/* 266 * get memory size above 1MB below 4GB 267 */ 268int 269getextmemx(void) 270{ 271 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next; 272 UINTN i, NoEntries, MapKey, DescriptorSize, MappingSize; 273 UINT32 DescriptorVersion; 274 EFI_PHYSICAL_ADDRESS extmem16m = 0; /* 0-16MB */ 275 EFI_PHYSICAL_ADDRESS extmem4g = 0; /* 16MB-4GB */ 276 EFI_PHYSICAL_ADDRESS pa, epa; 277 bool first16m = true, first4g = true; 278 int extmem; 279 280 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize, 281 &DescriptorVersion, true); 282 283 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) { 284 next = NextMemoryDescriptor(md, DescriptorSize); 285 if (getmemtype(md) == BIM_Reserved) 286 continue; 287 if (md->PhysicalStart >= 4 * 1024 * 1024 * 1024ULL) 288 continue; 289 290 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE; 291 epa = md->PhysicalStart + MappingSize; 292 if (epa == 0 || epa > 4 * 1024 * 1024 * 1024LL) 293 epa = 4 * 1024 * 1024 * 1024LL; 294 295 if (epa <= 1 * 1024 * 1024) 296 continue; 297 298 pa = md->PhysicalStart; 299 if (pa < 16 * 1024 * 1024) { 300 if (first16m || extmem16m == pa) { 301 first16m = false; 302 if (epa >= 16 * 1024 * 1024) { 303 extmem16m = 16 * 1024 * 1024; 304 pa = 16 * 1024 * 1024; 305 } else 306 extmem16m = epa; 307 } 308 } 309 if (pa >= 16 * 1024 * 1024) { 310 if (first4g || extmem4g == pa) { 311 first4g = false; 312 extmem4g = epa; 313 } 314 } 315 } 316 317 FreePool(mdtop); 318 319 if (extmem16m > 1 * 1024 * 1024) 320 extmem16m -= 1 * 1024 * 1024; /* below 1MB */ 321 322 extmem = extmem16m / 1024; 323 if (extmem == 15 * 1024) 324 extmem += extmem4g / 1024; 325 return extmem; 326} 327 328void 329efi_memory_probe(void) 330{ 331 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next; 332 EFI_STATUS status; 333 EFI_PHYSICAL_ADDRESS bouncebuf; 334 UINTN i, n, NoEntries, MapKey, DescriptorSize, MappingSize; 335 UINT32 DescriptorVersion; 336 int memtype; 337 338 bouncebuf = EFI_ALLOCATE_MAX_ADDRESS; 339 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, 340 EfiLoaderData, EFI_SIZE_TO_PAGES(KERN_LOADSPACE_SIZE), &bouncebuf); 341 if (EFI_ERROR(status)) 342 panic("couldn't allocate kernel space."); 343 efi_loadaddr = bouncebuf; 344 345 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize, 346 &DescriptorVersion, false); 347 printf(" mem["); 348 for (i = 0, n = 0, md = mdtop; i < NoEntries; i++, md = next) { 349 next = NextMemoryDescriptor(md, DescriptorSize); 350 351 memtype = getmemtype(md); 352 if (memtype != BIM_Memory) 353 continue; 354 355 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE; 356 if (MappingSize < 12 * 1024) /* XXX Why? from OpenBSD */ 357 continue; 358 359 if (n++ > 0) 360 printf(" "); 361 printf("0x%" PRIxMAX "-0x%" PRIxMAX, (uintmax_t)md->PhysicalStart, 362 (uintmax_t)(md->PhysicalStart + MappingSize - 1)); 363 } 364 printf("]\n"); 365 366 FreePool(mdtop); 367} 368 369void 370efi_memory_show_map(bool sorted, bool compact) 371{ 372 EFI_STATUS status; 373 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next; 374 UINTN i, NoEntries, MapKey, DescriptorSize; 375 UINT32 DescriptorVersion; 376 char efimemstr[32]; 377 UINTN cols, rows, row; 378 379 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, 380 ST->ConOut->Mode->Mode, &cols, &rows); 381 if (EFI_ERROR(status) || rows <= 2) 382 rows = 0; 383 else 384 rows -= 2; 385 386 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize, 387 &DescriptorVersion, sorted); 388 if (compact) 389 efi_memory_compact_map(mdtop, &NoEntries, DescriptorSize); 390 391 printf("%-22s %-16s %-16s %-16s\n", "Type", "Start", "End", "Attributes"); 392 printf("---------------------- ---------------- ---------------- ----------------\n"); 393 row = 2; 394 395 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) { 396 next = NextMemoryDescriptor(md, DescriptorSize); 397 398 if (md->Type >= __arraycount(efi_memory_type)) 399 snprintf(efimemstr, sizeof(efimemstr), "unknown (%d)", 400 md->Type); 401 printf("%-22s %016" PRIxMAX " %016" PRIxMAX " %016" PRIxMAX "\n", 402 md->Type >= __arraycount(efi_memory_type) ? 403 efimemstr : efi_memory_type[md->Type], 404 (uintmax_t)md->PhysicalStart, 405 (uintmax_t)md->PhysicalStart + 406 md->NumberOfPages * EFI_PAGE_SIZE - 1, 407 (uintmax_t)md->Attribute); 408 409 if (++row >= rows) { 410 row = 0; 411 printf("Press Any Key to continue :"); 412 (void) awaitkey(-1, 0); 413 printf("\n"); 414 } 415 } 416 417 FreePool(mdtop); 418} 419 420void 421vpbcopy(const void *va, void *pa, size_t n) 422{ 423 memmove(pa, va, n); 424} 425 426void 427pvbcopy(const void *pa, void *va, size_t n) 428{ 429 memmove(va, pa, n); 430} 431 432void 433pbzero(void *pa, size_t n) 434{ 435 memset(pa, 0, n); 436} 437 438physaddr_t 439vtophys(void *va) 440{ 441 return (physaddr_t)va; 442} 443