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