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
33static 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
55static int
56getmemtype(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
90EFI_MEMORY_DESCRIPTOR *
91efi_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
126EFI_MEMORY_DESCRIPTOR *
127efi_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
196int
197efi_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 */
233int
234getbasemem(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 */
268int
269getextmemx(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
328void
329efi_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
369void
370efi_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
420void
421vpbcopy(const void *va, void *pa, size_t n)
422{
423	memmove(pa, va, n);
424}
425
426void
427pvbcopy(const void *pa, void *va, size_t n)
428{
429	memmove(va, pa, n);
430}
431
432void
433pbzero(void *pa, size_t n)
434{
435	memset(pa, 0, n);
436}
437
438physaddr_t
439vtophys(void *va)
440{
441	return (physaddr_t)va;
442}
443