efimemory.c revision 1.8 1 /* $NetBSD: efimemory.c,v 1.8 2019/07/29 11:33:07 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 *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 /*
197 * get memory size below 1MB
198 */
199 int
200 getbasemem(void)
201 {
202 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
203 UINTN i, NoEntries, MapKey, DescriptorSize, MappingSize;
204 UINT32 DescriptorVersion;
205 EFI_PHYSICAL_ADDRESS basemem = 0, epa;
206
207 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
208 &DescriptorVersion, true);
209
210 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
211 next = NextMemoryDescriptor(md, DescriptorSize);
212 if (getmemtype(md) != BIM_Memory)
213 continue;
214 if (md->PhysicalStart >= 1 * 1024 * 1024)
215 continue;
216 if (basemem != md->PhysicalStart)
217 continue;
218
219 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
220 epa = md->PhysicalStart + MappingSize;
221 if (epa == 0 || epa > 1 * 1024 * 1024)
222 epa = 1 * 1024 * 1024;
223 basemem = epa;
224 }
225
226 FreePool(mdtop);
227
228 return basemem / 1024; /* KiB */
229 }
230
231 /*
232 * get memory size above 1MB below 4GB
233 */
234 int
235 getextmemx(void)
236 {
237 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
238 UINTN i, NoEntries, MapKey, DescriptorSize, MappingSize;
239 UINT32 DescriptorVersion;
240 EFI_PHYSICAL_ADDRESS extmem16m = 0; /* 0-16MB */
241 EFI_PHYSICAL_ADDRESS extmem4g = 0; /* 16MB-4GB */
242 EFI_PHYSICAL_ADDRESS pa, epa;
243 bool first16m = true, first4g = true;
244 int extmem;
245
246 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
247 &DescriptorVersion, true);
248
249 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
250 next = NextMemoryDescriptor(md, DescriptorSize);
251 if (getmemtype(md) == BIM_Reserved)
252 continue;
253 if (md->PhysicalStart >= 4 * 1024 * 1024 * 1024ULL)
254 continue;
255
256 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
257 epa = md->PhysicalStart + MappingSize;
258 if (epa == 0 || epa > 4 * 1024 * 1024 * 1024LL)
259 epa = 4 * 1024 * 1024 * 1024LL;
260
261 if (epa <= 1 * 1024 * 1024)
262 continue;
263
264 pa = md->PhysicalStart;
265 if (pa < 16 * 1024 * 1024) {
266 if (first16m || extmem16m == pa) {
267 first16m = false;
268 if (epa >= 16 * 1024 * 1024) {
269 extmem16m = 16 * 1024 * 1024;
270 pa = 16 * 1024 * 1024;
271 } else
272 extmem16m = epa;
273 }
274 }
275 if (pa >= 16 * 1024 * 1024) {
276 if (first4g || extmem4g == pa) {
277 first4g = false;
278 extmem4g = epa;
279 }
280 }
281 }
282
283 FreePool(mdtop);
284
285 if (extmem16m > 1 * 1024 * 1024)
286 extmem16m -= 1 * 1024 * 1024; /* below 1MB */
287
288 extmem = extmem16m / 1024;
289 if (extmem == 15 * 1024)
290 extmem += extmem4g / 1024;
291 return extmem;
292 }
293
294 void
295 efi_memory_probe(void)
296 {
297 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
298 EFI_STATUS status;
299 EFI_PHYSICAL_ADDRESS bouncebuf;
300 UINTN i, n, NoEntries, MapKey, DescriptorSize, MappingSize;
301 UINT32 DescriptorVersion;
302 int memtype;
303
304 bouncebuf = EFI_ALLOCATE_MAX_ADDRESS;
305 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress,
306 EfiLoaderData, EFI_SIZE_TO_PAGES(KERN_LOADSPACE_SIZE), &bouncebuf);
307 if (EFI_ERROR(status))
308 panic("couldn't allocate kernel space.");
309 efi_loadaddr = bouncebuf;
310
311 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
312 &DescriptorVersion, false);
313 printf(" mem[");
314 for (i = 0, n = 0, md = mdtop; i < NoEntries; i++, md = next) {
315 next = NextMemoryDescriptor(md, DescriptorSize);
316
317 memtype = getmemtype(md);
318 if (memtype != BIM_Memory)
319 continue;
320
321 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
322 if (MappingSize < 12 * 1024) /* XXX Why? from OpenBSD */
323 continue;
324
325 if (n++ > 0)
326 printf(" ");
327 printf("0x%" PRIxMAX "-0x%" PRIxMAX, (uintmax_t)md->PhysicalStart,
328 (uintmax_t)(md->PhysicalStart + MappingSize - 1));
329 }
330 printf("]\n");
331
332 FreePool(mdtop);
333 }
334
335 void
336 efi_memory_show_map(bool sorted, bool compact)
337 {
338 EFI_STATUS status;
339 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
340 UINTN i, NoEntries, MapKey, DescriptorSize;
341 UINT32 DescriptorVersion;
342 char efimemstr[32];
343 UINTN cols, rows, row;
344
345 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut,
346 ST->ConOut->Mode->Mode, &cols, &rows);
347 if (EFI_ERROR(status) || rows <= 2)
348 rows = 0;
349 else
350 rows -= 2;
351
352 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
353 &DescriptorVersion, sorted);
354 if (compact)
355 efi_memory_compact_map(mdtop, &NoEntries, DescriptorSize);
356
357 printf("%-22s %-16s %-16s %-16s\n", "Type", "Start", "End", "Attributes");
358 printf("---------------------- ---------------- ---------------- ----------------\n");
359 row = 2;
360
361 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
362 next = NextMemoryDescriptor(md, DescriptorSize);
363
364 if (md->Type >= __arraycount(efi_memory_type))
365 snprintf(efimemstr, sizeof(efimemstr), "unknown (%d)",
366 md->Type);
367 printf("%-22s %016" PRIxMAX " %016" PRIxMAX " %016" PRIxMAX "\n",
368 md->Type >= __arraycount(efi_memory_type) ?
369 efimemstr : efi_memory_type[md->Type],
370 (uintmax_t)md->PhysicalStart,
371 (uintmax_t)md->PhysicalStart +
372 md->NumberOfPages * EFI_PAGE_SIZE - 1,
373 (uintmax_t)md->Attribute);
374
375 if (++row >= rows) {
376 row = 0;
377 printf("Press Any Key to continue :");
378 (void) awaitkey(-1, 0);
379 printf("\n");
380 }
381 }
382
383 FreePool(mdtop);
384 }
385
386 void
387 vpbcopy(const void *va, void *pa, size_t n)
388 {
389 memmove(pa, va, n);
390 }
391
392 void
393 pvbcopy(const void *pa, void *va, size_t n)
394 {
395 memmove(va, pa, n);
396 }
397
398 void
399 pbzero(void *pa, size_t n)
400 {
401 memset(pa, 0, n);
402 }
403
404 physaddr_t
405 vtophys(void *va)
406 {
407 return (physaddr_t)va;
408 }
409