xref: /netbsd-src/sys/arch/i386/stand/efiboot/efimemory.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: efimemory.c,v 1.5 2018/03/27 14:15:05 nonaka 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 *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 static int
68 getmemtype(EFI_MEMORY_DESCRIPTOR *md)
69 {
70 
71 	switch (md->Type) {
72 	case EfiLoaderCode:
73 	case EfiLoaderData:
74 	case EfiBootServicesCode:
75 	case EfiBootServicesData:
76 	case EfiConventionalMemory:
77 		return (md->Attribute & EFI_MEMORY_WB) ?
78 		    BIM_Memory : BIM_Reserved;
79 
80 	case EfiACPIReclaimMemory:
81 		return BIM_ACPI;
82 
83 	case EfiACPIMemoryNVS:
84 		return BIM_NVS;
85 
86 	case EfiPersistentMemory:
87 		return BIM_PMEM;
88 
89 	case EfiReservedMemoryType:
90 	case EfiRuntimeServicesCode:
91 	case EfiRuntimeServicesData:
92 	case EfiUnusableMemory:
93 	case EfiMemoryMappedIO:
94 	case EfiMemoryMappedIOPortSpace:
95 	case EfiPalCode:
96 	case EfiMaxMemoryType:
97 	default:
98 		return BIM_Reserved;
99 	}
100 }
101 
102 EFI_MEMORY_DESCRIPTOR *
103 efi_memory_get_map(UINTN *NoEntries, UINTN *MapKey, UINTN *DescriptorSize,
104     UINT32 *DescriptorVersion, bool sorted)
105 {
106 	EFI_MEMORY_DESCRIPTOR *desc, *md, *next, *target, tmp;
107 	UINTN i, j;
108 
109 	*NoEntries = 0;
110 	desc = LibMemoryMap(NoEntries, MapKey, DescriptorSize,
111 	    DescriptorVersion);
112 	if (desc == NULL)
113 		panic("efi_memory_get_map failed");
114 
115 	if (!sorted)
116 		return desc;
117 
118 	for (i = 0, md = desc; i < *NoEntries - 1; i++, md = next) {
119 		target = next = NextMemoryDescriptor(md, *DescriptorSize);
120 		for (j = i + 1; j < *NoEntries; j++) {
121 			if (md->PhysicalStart > target->PhysicalStart) {
122 				CopyMem(&tmp, md, sizeof(*md));
123 				CopyMem(md, target, sizeof(*md));
124 				CopyMem(target, &tmp, sizeof(*md));
125 			}
126 			target = NextMemoryDescriptor(target, *DescriptorSize);
127 		}
128 	}
129 	return desc;
130 }
131 
132 /*
133  * get memory size below 1MB
134  */
135 int
136 getbasemem(void)
137 {
138 	EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
139 	UINTN i, NoEntries, MapKey, DescriptorSize, MappingSize;
140 	UINT32 DescriptorVersion;
141 	EFI_PHYSICAL_ADDRESS basemem = 0, epa;
142 
143 	mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
144 	    &DescriptorVersion, true);
145 
146 	for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
147 		next = NextMemoryDescriptor(md, DescriptorSize);
148 		if (getmemtype(md) != BIM_Memory)
149 			continue;
150 		if (md->PhysicalStart >= 1 * 1024 * 1024)
151 			continue;
152 		if (basemem != md->PhysicalStart)
153 			continue;
154 
155 		MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
156 		epa = md->PhysicalStart + MappingSize;
157 		if (epa == 0 || epa > 1 * 1024 * 1024)
158 			epa = 1 * 1024 * 1024;
159 		basemem = epa;
160 	}
161 
162 	FreePool(mdtop);
163 
164 	return basemem / 1024;	/* KiB */
165 }
166 
167 /*
168  * get memory size above 1MB below 4GB
169  */
170 int
171 getextmemx(void)
172 {
173 	EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
174 	UINTN i, NoEntries, MapKey, DescriptorSize, MappingSize;
175 	UINT32 DescriptorVersion;
176 	EFI_PHYSICAL_ADDRESS extmem16m = 0;	/* 0-16MB */
177 	EFI_PHYSICAL_ADDRESS extmem4g = 0;	/* 16MB-4GB */
178 	EFI_PHYSICAL_ADDRESS pa, epa;
179 	bool first16m = true, first4g = true;
180 	int extmem;
181 
182 	mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
183 	    &DescriptorVersion, true);
184 
185 	for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
186 		next = NextMemoryDescriptor(md, DescriptorSize);
187 		if (getmemtype(md) == BIM_Reserved)
188 			continue;
189 		if (md->PhysicalStart >= 4 * 1024 * 1024 * 1024ULL)
190 			continue;
191 
192 		MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
193 		epa = md->PhysicalStart + MappingSize;
194 		if (epa == 0 || epa > 4 * 1024 * 1024 * 1024LL)
195 			epa = 4 * 1024 * 1024 * 1024LL;
196 
197 		if (epa <= 1 * 1024 * 1024)
198 			continue;
199 
200 		pa = md->PhysicalStart;
201 		if (pa < 16 * 1024 * 1024) {
202 			if (first16m || extmem16m == pa) {
203 				first16m = false;
204 				if (epa >= 16 * 1024 * 1024) {
205 					extmem16m = 16 * 1024 * 1024;
206 					pa = 16 * 1024 * 1024;
207 				} else
208 					extmem16m = epa;
209 			}
210 		}
211 		if (pa >= 16 * 1024 * 1024) {
212 			if (first4g || extmem4g == pa) {
213 				first4g = false;
214 				extmem4g = epa;
215 			}
216 		}
217 	}
218 
219 	FreePool(mdtop);
220 
221 	if (extmem16m > 1 * 1024 * 1024)
222 		extmem16m -= 1 * 1024 * 1024;	/* below 1MB */
223 
224 	extmem = extmem16m / 1024;
225 	if (extmem == 15 * 1024)
226 		extmem += extmem4g / 1024;
227 	return extmem;
228 }
229 
230 void
231 efi_memory_probe(void)
232 {
233 	EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
234 	UINTN i, n, NoEntries, MapKey, DescriptorSize, MappingSize;
235 	UINT32 DescriptorVersion;
236 	int memtype;
237 
238 	mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
239 	    &DescriptorVersion, false);
240 
241 	printf(" mem[");
242 	for (i = 0, n = 0, md = mdtop; i < NoEntries; i++, md = next) {
243 		next = NextMemoryDescriptor(md, DescriptorSize);
244 
245 		memtype = getmemtype(md);
246 		if (memtype != BIM_Memory)
247 			continue;
248 
249 		MappingSize = md->NumberOfPages * EFI_PAGE_SIZE;
250 		if (MappingSize < 12 * 1024)	/* XXX Why? from OpenBSD */
251 			continue;
252 
253 		if (n++ > 0)
254 			printf(" ");
255 		printf("0x%" PRIxMAX "-0x%" PRIxMAX, (uintmax_t)md->PhysicalStart,
256 		    (uintmax_t)(md->PhysicalStart + MappingSize - 1));
257 	}
258 	printf("]\n");
259 
260 	FreePool(mdtop);
261 }
262 
263 void
264 efi_memory_show_map(bool sorted)
265 {
266 	EFI_STATUS status;
267 	EFI_MEMORY_DESCRIPTOR *mdtop, *md, *next;
268 	UINTN i, NoEntries, MapKey, DescriptorSize;
269 	UINT32 DescriptorVersion;
270 	char memstr[32], efimemstr[32];
271 	int memtype;
272 	UINTN cols, rows, row = 0;
273 
274 	status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut,
275 	    ST->ConOut->Mode->Mode, &cols, &rows);
276 	if (EFI_ERROR(status) || rows <= 2)
277 		rows = 0;
278 	else
279 		rows -= 2;
280 
281 	mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
282 	    &DescriptorVersion, sorted);
283 
284 	for (i = 0, md = mdtop; i < NoEntries; i++, md = next) {
285 		next = NextMemoryDescriptor(md, DescriptorSize);
286 
287 		memtype = getmemtype(md);
288 		if (memtype >= __arraycount(memtypes))
289 			snprintf(memstr, sizeof(memstr), "unknown (%d)",
290 			    memtype);
291 		if (md->Type >= __arraycount(efimemtypes))
292 			snprintf(efimemstr, sizeof(efimemstr), "unknown (%d)",
293 			    md->Type);
294 		printf("%016" PRIxMAX "/%016" PRIxMAX ": %s [%s]\n",
295 		    (uintmax_t)md->PhysicalStart,
296 		    (uintmax_t)md->PhysicalStart +
297 		      md->NumberOfPages * EFI_PAGE_SIZE - 1,
298 		    memtype >= __arraycount(memtypes) ?
299 		      memstr : memtypes[memtype],
300 		    md->Type >= __arraycount(efimemtypes) ?
301 		      efimemstr : efimemtypes[md->Type]);
302 
303 		if (++row >= rows) {
304 			row = 0;
305 			printf("Press Any Key to continue :");
306 			(void) awaitkey(-1, 0);
307 			printf("\n");
308 		}
309 	}
310 
311 	FreePool(mdtop);
312 }
313 
314 void
315 vpbcopy(const void *va, void *pa, size_t n)
316 {
317 	memmove(pa, va, n);
318 }
319 
320 void
321 pvbcopy(const void *pa, void *va, size_t n)
322 {
323 	memmove(va, pa, n);
324 }
325 
326 void
327 pbzero(void *pa, size_t n)
328 {
329 	memset(pa, 0, n);
330 }
331 
332 physaddr_t
333 vtophys(void *va)
334 {
335 	return (physaddr_t)va;
336 }
337