xref: /netbsd-src/sys/arch/i386/stand/efiboot/efimemory.c (revision 84da3ae6142cd2ce869b45a8fee82a30bf70a46d)
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 
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
getmemtype(EFI_MEMORY_DESCRIPTOR * md)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 *
efi_memory_get_map(UINTN * NoEntries,UINTN * MapKey,UINTN * DescriptorSize,UINT32 * DescriptorVersion,bool sorted)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 *
efi_memory_compact_map(EFI_MEMORY_DESCRIPTOR * desc,UINTN * NoEntries,UINTN DescriptorSize)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
efi_memory_get_memmap(struct bi_memmap_entry ** memmapp,size_t * num)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
getbasemem(void)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
getextmemx(void)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
efi_memory_probe(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
efi_memory_show_map(bool sorted,bool compact)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
vpbcopy(const void * va,void * pa,size_t n)421 vpbcopy(const void *va, void *pa, size_t n)
422 {
423 	memmove(pa, va, n);
424 }
425 
426 void
pvbcopy(const void * pa,void * va,size_t n)427 pvbcopy(const void *pa, void *va, size_t n)
428 {
429 	memmove(va, pa, n);
430 }
431 
432 void
pbzero(void * pa,size_t n)433 pbzero(void *pa, size_t n)
434 {
435 	memset(pa, 0, n);
436 }
437 
438 physaddr_t
vtophys(void * va)439 vtophys(void *va)
440 {
441 	return (physaddr_t)va;
442 }
443