Home | History | Annotate | Line # | Download | only in efiboot
      1 /*	$NetBSD: efimemory.c,v 1.10 2023/05/14 09:07:54 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2016 Kimihiro Nonaka <nonaka (at) 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 
     33 static 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 
     55 static int
     56 getmemtype(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 
     90 EFI_MEMORY_DESCRIPTOR *
     91 efi_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 
    126 EFI_MEMORY_DESCRIPTOR *
    127 efi_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 
    196 int
    197 efi_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  */
    233 int
    234 getbasemem(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  */
    268 int
    269 getextmemx(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 
    328 void
    329 efi_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 
    369 void
    370 efi_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 
    420 void
    421 vpbcopy(const void *va, void *pa, size_t n)
    422 {
    423 	memmove(pa, va, n);
    424 }
    425 
    426 void
    427 pvbcopy(const void *pa, void *va, size_t n)
    428 {
    429 	memmove(va, pa, n);
    430 }
    431 
    432 void
    433 pbzero(void *pa, size_t n)
    434 {
    435 	memset(pa, 0, n);
    436 }
    437 
    438 physaddr_t
    439 vtophys(void *va)
    440 {
    441 	return (physaddr_t)va;
    442 }
    443