efimemory.c revision 1.7 1 /* $NetBSD: efimemory.c,v 1.7 2019/07/29 11:28:51 nonaka 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 *memtypes[] = {
34 "unknown",
35 "available",
36 "reserved",
37 "ACPI reclaimable",
38 "ACPI NVS",
39 "unusable",
40 "disabled",
41 "Persistent",
42 "undefined (8)",
43 "undefined (9)",
44 "undefined (10)",
45 "undefined (11)",
46 "Persistent (Legacy)"
47 };
48
49 static const char *efimemtypes[] = {
50 "Reserved",
51 "LoaderCode",
52 "LoaderData",
53 "BootServicesCode",
54 "BootServicesData",
55 "RuntimeServicesCode",
56 "RuntimeServicesData",
57 "ConventionalMemory",
58 "UnusableMemory",
59 "ACPIReclaimMemory",
60 "ACPIMemoryNVS",
61 "MemoryMappedIO",
62 "MemoryMappedIOPortSpace",
63 "PalCode",
64 "PersistentMemory",
65 };
66
67 #ifndef KERN_LOADSPACE_SIZE
68 #define KERN_LOADSPACE_SIZE (128 * 1024 * 1024) /* 128MiB */
69 #endif
70
71 static int
72 getmemtype(EFI_MEMORY_DESCRIPTOR *md)
73 {
74
75 switch (md->Type) {
76 case EfiLoaderCode:
77 case EfiLoaderData:
78 case EfiBootServicesCode:
79 case EfiBootServicesData:
80 case EfiConventionalMemory:
81 return (md->Attribute & EFI_MEMORY_WB) ?
82 BIM_Memory : BIM_Reserved;
83
84 case EfiACPIReclaimMemory:
85 return BIM_ACPI;
86
87 case EfiACPIMemoryNVS:
88 return BIM_NVS;
89
90 case EfiPersistentMemory:
91 return BIM_PMEM;
92
93 case EfiReservedMemoryType:
94 case EfiRuntimeServicesCode:
95 case EfiRuntimeServicesData:
96 case EfiUnusableMemory:
97 case EfiMemoryMappedIO:
98 case EfiMemoryMappedIOPortSpace:
99 case EfiPalCode:
100 case EfiMaxMemoryType:
101 default:
102 return BIM_Reserved;
103 }
104 }
105
106 EFI_MEMORY_DESCRIPTOR *
107 efi_memory_get_map(UINTN *NoEntries, UINTN *MapKey, UINTN *DescriptorSize,
108 UINT32 *DescriptorVersion, bool sorted)
109 {
110 EFI_MEMORY_DESCRIPTOR *desc, *md, *next, *target, *tmp;
111 UINTN i, j;
112
113 *NoEntries = 0;
114 desc = LibMemoryMap(NoEntries, MapKey, DescriptorSize,
115 DescriptorVersion);
116 if (desc == NULL)
117 panic("efi_memory_get_map failed");
118
119 if (!sorted)
120 return desc;
121
122 tmp = alloc(*DescriptorSize);
123 if (tmp == NULL)
124 return desc;
125
126 for (i = 0, md = desc; i < *NoEntries - 1; i++, md = next) {
127 target = next = NextMemoryDescriptor(md, *DescriptorSize);
128 for (j = i + 1; j < *NoEntries; j++) {
129 if (md->PhysicalStart > target->PhysicalStart) {
130 CopyMem(tmp, md, *DescriptorSize);
131 CopyMem(md, target, *DescriptorSize);
132 CopyMem(target, tmp, *DescriptorSize);
133 }
134 target = NextMemoryDescriptor(target, *DescriptorSize);
135 }
136 }
137 dealloc(tmp, *DescriptorSize);
138
139 return desc;
140 }
141
142 EFI_MEMORY_DESCRIPTOR *
143 efi_memory_compact_map(EFI_MEMORY_DESCRIPTOR *desc, UINTN *NoEntries,
144 UINTN DescriptorSize)
145 {
146 EFI_MEMORY_DESCRIPTOR *md, *next, *target, *tmp;
147 UINTN i, j;
148 UINT32 type;
149 bool first = true, do_compact;
150
151 for (i = 0, md = target = desc; i < *NoEntries; i++, md = next) {
152 type = md->Type;
153 switch (type) {
154 case EfiLoaderCode:
155 case EfiLoaderData:
156 case EfiBootServicesCode:
157 case EfiBootServicesData:
158 case EfiConventionalMemory:
159 if ((md->Attribute & EFI_MEMORY_WB) != 0)
160 type = EfiConventionalMemory;
161 if (md->Attribute == target->Attribute) {
162 do_compact = true;
163 break;
164 }
165 /* FALLTHROUGH */
166 case EfiACPIReclaimMemory:
167 case EfiACPIMemoryNVS:
168 case EfiPersistentMemory:
169 case EfiReservedMemoryType:
170 case EfiRuntimeServicesCode:
171 case EfiRuntimeServicesData:
172 case EfiUnusableMemory:
173 case EfiMemoryMappedIO:
174 case EfiMemoryMappedIOPortSpace:
175 case EfiPalCode:
176 default:
177 do_compact = false;
178 break;
179 }
180
181 if (first) {
182 first = false;
183 } else if (do_compact &&
184 type == target->Type &&
185 md->Attribute == target->Attribute &&
186 md->PhysicalStart == target->PhysicalStart + target->NumberOfPages * EFI_PAGE_SIZE) {
187 /* continuous region */
188 target->NumberOfPages += md->NumberOfPages;
189
190 tmp = md;
191 for (j = i + 1; j < *NoEntries; j++) {
192 next = NextMemoryDescriptor(md, DescriptorSize);
193 CopyMem(md, next, DescriptorSize);
194 md = next;
195 }
196 next = tmp;
197
198 i--;
199 (*NoEntries)--;
200 continue;
201 } else {
202 target = md;
203 }
204
205 target->Type = type;
206 next = NextMemoryDescriptor(md, DescriptorSize);
207 }
208
209 return desc;
210 }
211
212 /*
213 * get memory size below 1MB
214 */
215 int
216 getbasemem(void)
217 {
218 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
219 UINTN i, NoEntries, MapKey, DescriptorSize, MappingSize;
220 UINT32 DescriptorVersion;
221 EFI_PHYSICAL_ADDRESS basemem = 0, epa;
222
223 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
224 &DescriptorVersion, true);
225
226 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
227 next = NextMemoryDescriptor(md, DescriptorSize);
228 if (getmemtype(md) != BIM_Memory)
229 continue;
230 if (md->PhysicalStart >= 1 * 1024 * 1024)
231 continue;
232 if (basemem != md->PhysicalStart)
233 continue;
234
235 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
236 epa = md->PhysicalStart + MappingSize;
237 if (epa == 0 || epa > 1 * 1024 * 1024)
238 epa = 1 * 1024 * 1024;
239 basemem = epa;
240 }
241
242 FreePool(mdtop);
243
244 return basemem / 1024; /* KiB */
245 }
246
247 /*
248 * get memory size above 1MB below 4GB
249 */
250 int
251 getextmemx(void)
252 {
253 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
254 UINTN i, NoEntries, MapKey, DescriptorSize, MappingSize;
255 UINT32 DescriptorVersion;
256 EFI_PHYSICAL_ADDRESS extmem16m = 0; /* 0-16MB */
257 EFI_PHYSICAL_ADDRESS extmem4g = 0; /* 16MB-4GB */
258 EFI_PHYSICAL_ADDRESS pa, epa;
259 bool first16m = true, first4g = true;
260 int extmem;
261
262 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
263 &DescriptorVersion, true);
264
265 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
266 next = NextMemoryDescriptor(md, DescriptorSize);
267 if (getmemtype(md) == BIM_Reserved)
268 continue;
269 if (md->PhysicalStart >= 4 * 1024 * 1024 * 1024ULL)
270 continue;
271
272 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
273 epa = md->PhysicalStart + MappingSize;
274 if (epa == 0 || epa > 4 * 1024 * 1024 * 1024LL)
275 epa = 4 * 1024 * 1024 * 1024LL;
276
277 if (epa <= 1 * 1024 * 1024)
278 continue;
279
280 pa = md->PhysicalStart;
281 if (pa < 16 * 1024 * 1024) {
282 if (first16m || extmem16m == pa) {
283 first16m = false;
284 if (epa >= 16 * 1024 * 1024) {
285 extmem16m = 16 * 1024 * 1024;
286 pa = 16 * 1024 * 1024;
287 } else
288 extmem16m = epa;
289 }
290 }
291 if (pa >= 16 * 1024 * 1024) {
292 if (first4g || extmem4g == pa) {
293 first4g = false;
294 extmem4g = epa;
295 }
296 }
297 }
298
299 FreePool(mdtop);
300
301 if (extmem16m > 1 * 1024 * 1024)
302 extmem16m -= 1 * 1024 * 1024; /* below 1MB */
303
304 extmem = extmem16m / 1024;
305 if (extmem == 15 * 1024)
306 extmem += extmem4g / 1024;
307 return extmem;
308 }
309
310 void
311 efi_memory_probe(void)
312 {
313 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
314 EFI_STATUS status;
315 EFI_PHYSICAL_ADDRESS bouncebuf;
316 UINTN i, n, NoEntries, MapKey, DescriptorSize, MappingSize;
317 UINT32 DescriptorVersion;
318 int memtype;
319
320 bouncebuf = EFI_ALLOCATE_MAX_ADDRESS;
321 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress,
322 EfiLoaderData, EFI_SIZE_TO_PAGES(KERN_LOADSPACE_SIZE), &bouncebuf);
323 if (EFI_ERROR(status))
324 panic("couldn't allocate kernel space.");
325 efi_loadaddr = bouncebuf;
326
327 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
328 &DescriptorVersion, false);
329 printf(" mem[");
330 for (i = 0, n = 0, md = mdtop; i < NoEntries; i++, md = next) {
331 next = NextMemoryDescriptor(md, DescriptorSize);
332
333 memtype = getmemtype(md);
334 if (memtype != BIM_Memory)
335 continue;
336
337 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
338 if (MappingSize < 12 * 1024) /* XXX Why? from OpenBSD */
339 continue;
340
341 if (n++ > 0)
342 printf(" ");
343 printf("0x%" PRIxMAX "-0x%" PRIxMAX, (uintmax_t)md->PhysicalStart,
344 (uintmax_t)(md->PhysicalStart + MappingSize - 1));
345 }
346 printf("]\n");
347
348 FreePool(mdtop);
349 }
350
351 void
352 efi_memory_show_map(bool sorted, bool compact)
353 {
354 EFI_STATUS status;
355 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
356 UINTN i, NoEntries, MapKey, DescriptorSize;
357 UINT32 DescriptorVersion;
358 char memstr[32], efimemstr[32];
359 int memtype;
360 UINTN cols, rows, row = 0;
361
362 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut,
363 ST->ConOut->Mode->Mode, &cols, &rows);
364 if (EFI_ERROR(status) || rows <= 2)
365 rows = 0;
366 else
367 rows -= 2;
368
369 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
370 &DescriptorVersion, sorted);
371 if (compact)
372 efi_memory_compact_map(mdtop, &NoEntries, DescriptorSize);
373
374 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
375 next = NextMemoryDescriptor(md, DescriptorSize);
376
377 memtype = getmemtype(md);
378 if (memtype >= __arraycount(memtypes))
379 snprintf(memstr, sizeof(memstr), "unknown (%d)",
380 memtype);
381 if (md->Type >= __arraycount(efimemtypes))
382 snprintf(efimemstr, sizeof(efimemstr), "unknown (%d)",
383 md->Type);
384 printf("%016" PRIxMAX "/%016" PRIxMAX ": %s [%s]\n",
385 (uintmax_t)md->PhysicalStart,
386 (uintmax_t)md->PhysicalStart +
387 md->NumberOfPages * EFI_PAGE_SIZE - 1,
388 memtype >= __arraycount(memtypes) ?
389 memstr : memtypes[memtype],
390 md->Type >= __arraycount(efimemtypes) ?
391 efimemstr : efimemtypes[md->Type]);
392
393 if (++row >= rows) {
394 row = 0;
395 printf("Press Any Key to continue :");
396 (void) awaitkey(-1, 0);
397 printf("\n");
398 }
399 }
400
401 FreePool(mdtop);
402 }
403
404 void
405 vpbcopy(const void *va, void *pa, size_t n)
406 {
407 memmove(pa, va, n);
408 }
409
410 void
411 pvbcopy(const void *pa, void *va, size_t n)
412 {
413 memmove(va, pa, n);
414 }
415
416 void
417 pbzero(void *pa, size_t n)
418 {
419 memset(pa, 0, n);
420 }
421
422 physaddr_t
423 vtophys(void *va)
424 {
425 return (physaddr_t)va;
426 }
427