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