xref: /illumos-gate/usr/src/boot/efi/loader/memmap.c (revision 3fb2fe9fdd2e33737038a161631f2ab6d7050ecf)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2016 Toomas Soome <tsoome@me.com>
14  */
15 
16 /*
17  * Build smap like memory map from efi memmap.
18  */
19 
20 #include <stand.h>
21 #include <inttypes.h>
22 #include <efi.h>
23 #include <efilib.h>
24 #include <sys/param.h>
25 #include <sys/linker.h>
26 #include <sys/queue.h>
27 #include <sys/stddef.h>
28 #include <machine/metadata.h>
29 #include <machine/pc/bios.h>
30 #include "bootstrap.h"
31 
32 struct smap_buf {
33 	struct bios_smap	sb_smap;
34 	STAILQ_ENTRY(smap_buf)	sb_bufs;
35 };
36 
37 static struct bios_smap *smapbase;
38 static int smaplen;
39 
40 /*
41  * See "ACPI Specification Release 6.5 Errata A"
42  * Table 15-6 (page 785), UEFI Memory Types and mapping to ACPI address
43  * range types.
44  */
45 static int
smap_type(int type)46 smap_type(int type)
47 {
48 	switch (type) {
49 	case EfiLoaderCode:
50 	case EfiLoaderData:
51 	case EfiBootServicesCode:
52 	case EfiBootServicesData:
53 	case EfiConventionalMemory:
54 		return (SMAP_TYPE_MEMORY);
55 	case EfiReservedMemoryType:
56 	case EfiRuntimeServicesCode:
57 	case EfiRuntimeServicesData:
58 	case EfiMemoryMappedIO:
59 	case EfiMemoryMappedIOPortSpace:
60 	case EfiPalCode:
61 	case EfiUnusableMemory:
62 		return (SMAP_TYPE_RESERVED);
63 	case EfiACPIReclaimMemory:
64 		return (SMAP_TYPE_ACPI_RECLAIM);
65 	case EfiACPIMemoryNVS:
66 		return (SMAP_TYPE_ACPI_NVS);
67 	}
68 	return (SMAP_TYPE_RESERVED);
69 }
70 
71 void
efi_getsmap(void)72 efi_getsmap(void)
73 {
74 	UINTN size, desc_size, key;
75 	EFI_MEMORY_DESCRIPTOR *efi_mmap, *p;
76 	EFI_PHYSICAL_ADDRESS addr;
77 	EFI_STATUS status;
78 	STAILQ_HEAD(smap_head, smap_buf) head =
79 	    STAILQ_HEAD_INITIALIZER(head);
80 	struct smap_buf *cur, *next;
81 	int i, n, ndesc;
82 	int type = -1;
83 
84 	size = 0;
85 	efi_mmap = NULL;
86 	status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL);
87 	efi_mmap = malloc(size);
88 	status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL);
89 	if (EFI_ERROR(status)) {
90 		printf("GetMemoryMap: error %lu\n", DECODE_ERROR(status));
91 		free(efi_mmap);
92 		return;
93 	}
94 
95 	STAILQ_INIT(&head);
96 	n = 0;
97 	i = 0;
98 	p = efi_mmap;
99 	next = NULL;
100 	ndesc = size / desc_size;
101 	while (i < ndesc) {
102 		if (next == NULL) {
103 			next = malloc(sizeof (*next));
104 			if (next == NULL)
105 				break;
106 
107 			next->sb_smap.base = p->PhysicalStart;
108 			next->sb_smap.length =
109 			    p->NumberOfPages << EFI_PAGE_SHIFT;
110 			/*
111 			 * ACPI 6.1 tells the lower memory should be
112 			 * reported as normal memory, so we enforce
113 			 * page 0 type even as vmware maps it as
114 			 * acpi reclaimable.
115 			 */
116 			if (next->sb_smap.base == 0)
117 				type = SMAP_TYPE_MEMORY;
118 			else
119 				type = smap_type(p->Type);
120 			next->sb_smap.type = type;
121 
122 			STAILQ_INSERT_TAIL(&head, next, sb_bufs);
123 			n++;
124 			p = NextMemoryDescriptor(p, desc_size);
125 			i++;
126 			continue;
127 		}
128 		addr = next->sb_smap.base + next->sb_smap.length;
129 		if ((smap_type(p->Type) == type) &&
130 		    (p->PhysicalStart == addr)) {
131 			next->sb_smap.length +=
132 			    (p->NumberOfPages << EFI_PAGE_SHIFT);
133 			p = NextMemoryDescriptor(p, desc_size);
134 			i++;
135 		} else
136 			next = NULL;
137 	}
138 	smaplen = n;
139 	if (smaplen > 0) {
140 		smapbase = malloc(smaplen * sizeof (*smapbase));
141 		if (smapbase != NULL) {
142 			n = 0;
143 			STAILQ_FOREACH(cur, &head, sb_bufs)
144 				smapbase[n++] = cur->sb_smap;
145 		}
146 		cur = STAILQ_FIRST(&head);
147 		while (cur != NULL) {
148 			next = STAILQ_NEXT(cur, sb_bufs);
149 			free(cur);
150 			cur = next;
151 		}
152 	}
153 	free(efi_mmap);
154 }
155 
156 void
efi_addsmapdata(struct preloaded_file * kfp)157 efi_addsmapdata(struct preloaded_file *kfp)
158 {
159 	size_t size;
160 
161 	if (smapbase == NULL || smaplen == 0)
162 		return;
163 	size = smaplen * sizeof (*smapbase);
164 	file_addmetadata(kfp, MODINFOMD_SMAP, size, smapbase);
165 }
166 
167 COMMAND_SET(smap, "smap", "show BIOS SMAP", command_smap);
168 
169 static int
command_smap(int argc __unused,char * argv[]__unused)170 command_smap(int argc __unused, char *argv[] __unused)
171 {
172 	uint_t i;
173 
174 	if (smapbase == NULL || smaplen == 0)
175 		return (CMD_ERROR);
176 
177 	for (i = 0; i < smaplen; i++)
178 		printf("SMAP type=%02" PRIx32 " base=%016" PRIx64
179 		    " len=%016" PRIx64 "\n", smapbase[i].type,
180 		    smapbase[i].base, smapbase[i].length);
181 	return (CMD_OK);
182 }
183