efimemory.c revision 1.6 1 /* $NetBSD: efimemory.c,v 1.6 2019/07/26 12:09:48 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 for (i = 0, md = desc; i < *NoEntries - 1; i++, md = next) {
123 target = next = NextMemoryDescriptor(md, *DescriptorSize);
124 for (j = i + 1; j < *NoEntries; j++) {
125 if (md->PhysicalStart > target->PhysicalStart) {
126 CopyMem(&tmp, md, sizeof(*md));
127 CopyMem(md, target, sizeof(*md));
128 CopyMem(target, &tmp, sizeof(*md));
129 }
130 target = NextMemoryDescriptor(target, *DescriptorSize);
131 }
132 }
133 return desc;
134 }
135
136 /*
137 * get memory size below 1MB
138 */
139 int
140 getbasemem(void)
141 {
142 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
143 UINTN i, NoEntries, MapKey, DescriptorSize, MappingSize;
144 UINT32 DescriptorVersion;
145 EFI_PHYSICAL_ADDRESS basemem = 0, epa;
146
147 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
148 &DescriptorVersion, true);
149
150 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
151 next = NextMemoryDescriptor(md, DescriptorSize);
152 if (getmemtype(md) != BIM_Memory)
153 continue;
154 if (md->PhysicalStart >= 1 * 1024 * 1024)
155 continue;
156 if (basemem != md->PhysicalStart)
157 continue;
158
159 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
160 epa = md->PhysicalStart + MappingSize;
161 if (epa == 0 || epa > 1 * 1024 * 1024)
162 epa = 1 * 1024 * 1024;
163 basemem = epa;
164 }
165
166 FreePool(mdtop);
167
168 return basemem / 1024; /* KiB */
169 }
170
171 /*
172 * get memory size above 1MB below 4GB
173 */
174 int
175 getextmemx(void)
176 {
177 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
178 UINTN i, NoEntries, MapKey, DescriptorSize, MappingSize;
179 UINT32 DescriptorVersion;
180 EFI_PHYSICAL_ADDRESS extmem16m = 0; /* 0-16MB */
181 EFI_PHYSICAL_ADDRESS extmem4g = 0; /* 16MB-4GB */
182 EFI_PHYSICAL_ADDRESS pa, epa;
183 bool first16m = true, first4g = true;
184 int extmem;
185
186 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
187 &DescriptorVersion, true);
188
189 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
190 next = NextMemoryDescriptor(md, DescriptorSize);
191 if (getmemtype(md) == BIM_Reserved)
192 continue;
193 if (md->PhysicalStart >= 4 * 1024 * 1024 * 1024ULL)
194 continue;
195
196 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
197 epa = md->PhysicalStart + MappingSize;
198 if (epa == 0 || epa > 4 * 1024 * 1024 * 1024LL)
199 epa = 4 * 1024 * 1024 * 1024LL;
200
201 if (epa <= 1 * 1024 * 1024)
202 continue;
203
204 pa = md->PhysicalStart;
205 if (pa < 16 * 1024 * 1024) {
206 if (first16m || extmem16m == pa) {
207 first16m = false;
208 if (epa >= 16 * 1024 * 1024) {
209 extmem16m = 16 * 1024 * 1024;
210 pa = 16 * 1024 * 1024;
211 } else
212 extmem16m = epa;
213 }
214 }
215 if (pa >= 16 * 1024 * 1024) {
216 if (first4g || extmem4g == pa) {
217 first4g = false;
218 extmem4g = epa;
219 }
220 }
221 }
222
223 FreePool(mdtop);
224
225 if (extmem16m > 1 * 1024 * 1024)
226 extmem16m -= 1 * 1024 * 1024; /* below 1MB */
227
228 extmem = extmem16m / 1024;
229 if (extmem == 15 * 1024)
230 extmem += extmem4g / 1024;
231 return extmem;
232 }
233
234 void
235 efi_memory_probe(void)
236 {
237 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
238 EFI_STATUS status;
239 EFI_PHYSICAL_ADDRESS bouncebuf;
240 UINTN i, n, NoEntries, MapKey, DescriptorSize, MappingSize;
241 UINT32 DescriptorVersion;
242 int memtype;
243
244 bouncebuf = EFI_ALLOCATE_MAX_ADDRESS;
245 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress,
246 EfiLoaderData, EFI_SIZE_TO_PAGES(KERN_LOADSPACE_SIZE), &bouncebuf);
247 if (EFI_ERROR(status))
248 panic("couldn't allocate kernel space.");
249 efi_loadaddr = bouncebuf;
250
251 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
252 &DescriptorVersion, false);
253 printf(" mem[");
254 for (i = 0, n = 0, md = mdtop; i < NoEntries; i++, md = next) {
255 next = NextMemoryDescriptor(md, DescriptorSize);
256
257 memtype = getmemtype(md);
258 if (memtype != BIM_Memory)
259 continue;
260
261 MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
262 if (MappingSize < 12 * 1024) /* XXX Why? from OpenBSD */
263 continue;
264
265 if (n++ > 0)
266 printf(" ");
267 printf("0x%" PRIxMAX "-0x%" PRIxMAX, (uintmax_t)md->PhysicalStart,
268 (uintmax_t)(md->PhysicalStart + MappingSize - 1));
269 }
270 printf("]\n");
271
272 FreePool(mdtop);
273 }
274
275 void
276 efi_memory_show_map(bool sorted)
277 {
278 EFI_STATUS status;
279 EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
280 UINTN i, NoEntries, MapKey, DescriptorSize;
281 UINT32 DescriptorVersion;
282 char memstr[32], efimemstr[32];
283 int memtype;
284 UINTN cols, rows, row = 0;
285
286 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut,
287 ST->ConOut->Mode->Mode, &cols, &rows);
288 if (EFI_ERROR(status) || rows <= 2)
289 rows = 0;
290 else
291 rows -= 2;
292
293 mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
294 &DescriptorVersion, sorted);
295
296 for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
297 next = NextMemoryDescriptor(md, DescriptorSize);
298
299 memtype = getmemtype(md);
300 if (memtype >= __arraycount(memtypes))
301 snprintf(memstr, sizeof(memstr), "unknown (%d)",
302 memtype);
303 if (md->Type >= __arraycount(efimemtypes))
304 snprintf(efimemstr, sizeof(efimemstr), "unknown (%d)",
305 md->Type);
306 printf("%016" PRIxMAX "/%016" PRIxMAX ": %s [%s]\n",
307 (uintmax_t)md->PhysicalStart,
308 (uintmax_t)md->PhysicalStart +
309 md->NumberOfPages * EFI_PAGE_SIZE - 1,
310 memtype >= __arraycount(memtypes) ?
311 memstr : memtypes[memtype],
312 md->Type >= __arraycount(efimemtypes) ?
313 efimemstr : efimemtypes[md->Type]);
314
315 if (++row >= rows) {
316 row = 0;
317 printf("Press Any Key to continue :");
318 (void) awaitkey(-1, 0);
319 printf("\n");
320 }
321 }
322
323 FreePool(mdtop);
324 }
325
326 void
327 vpbcopy(const void *va, void *pa, size_t n)
328 {
329 memmove(pa, va, n);
330 }
331
332 void
333 pvbcopy(const void *pa, void *va, size_t n)
334 {
335 memmove(va, pa, n);
336 }
337
338 void
339 pbzero(void *pa, size_t n)
340 {
341 memset(pa, 0, n);
342 }
343
344 physaddr_t
345 vtophys(void *va)
346 {
347 return (physaddr_t)va;
348 }
349