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 6.1 Table 15-330 UEFI Memory Types and mapping to ACPI address
42 * range types.
43 */
44 static int
smap_type(int type)45 smap_type(int type)
46 {
47 switch (type) {
48 case EfiLoaderCode:
49 case EfiLoaderData:
50 case EfiBootServicesCode:
51 case EfiBootServicesData:
52 case EfiConventionalMemory:
53 return (SMAP_TYPE_MEMORY);
54 case EfiReservedMemoryType:
55 case EfiRuntimeServicesCode:
56 case EfiRuntimeServicesData:
57 case EfiMemoryMappedIO:
58 case EfiMemoryMappedIOPortSpace:
59 case EfiPalCode:
60 case EfiUnusableMemory:
61 return (SMAP_TYPE_RESERVED);
62 case EfiACPIReclaimMemory:
63 return (SMAP_TYPE_ACPI_RECLAIM);
64 case EfiACPIMemoryNVS:
65 return (SMAP_TYPE_ACPI_NVS);
66 }
67 return (SMAP_TYPE_RESERVED);
68 }
69
70 void
efi_getsmap(void)71 efi_getsmap(void)
72 {
73 UINTN size, desc_size, key;
74 EFI_MEMORY_DESCRIPTOR *efi_mmap, *p;
75 EFI_PHYSICAL_ADDRESS addr;
76 EFI_STATUS status;
77 STAILQ_HEAD(smap_head, smap_buf) head =
78 STAILQ_HEAD_INITIALIZER(head);
79 struct smap_buf *cur, *next;
80 int i, n, ndesc;
81 int type = -1;
82
83 size = 0;
84 efi_mmap = NULL;
85 status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL);
86 efi_mmap = malloc(size);
87 status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL);
88 if (EFI_ERROR(status)) {
89 printf("GetMemoryMap: error %lu\n", DECODE_ERROR(status));
90 free(efi_mmap);
91 return;
92 }
93
94 STAILQ_INIT(&head);
95 n = 0;
96 i = 0;
97 p = efi_mmap;
98 next = NULL;
99 ndesc = size / desc_size;
100 while (i < ndesc) {
101 if (next == NULL) {
102 next = malloc(sizeof (*next));
103 if (next == NULL)
104 break;
105
106 next->sb_smap.base = p->PhysicalStart;
107 next->sb_smap.length =
108 p->NumberOfPages << EFI_PAGE_SHIFT;
109 /*
110 * ACPI 6.1 tells the lower memory should be
111 * reported as normal memory, so we enforce
112 * page 0 type even as vmware maps it as
113 * acpi reclaimable.
114 */
115 if (next->sb_smap.base == 0)
116 type = SMAP_TYPE_MEMORY;
117 else
118 type = smap_type(p->Type);
119 next->sb_smap.type = type;
120
121 STAILQ_INSERT_TAIL(&head, next, sb_bufs);
122 n++;
123 p = NextMemoryDescriptor(p, desc_size);
124 i++;
125 continue;
126 }
127 addr = next->sb_smap.base + next->sb_smap.length;
128 if ((smap_type(p->Type) == type) &&
129 (p->PhysicalStart == addr)) {
130 next->sb_smap.length +=
131 (p->NumberOfPages << EFI_PAGE_SHIFT);
132 p = NextMemoryDescriptor(p, desc_size);
133 i++;
134 } else
135 next = NULL;
136 }
137 smaplen = n;
138 if (smaplen > 0) {
139 smapbase = malloc(smaplen * sizeof (*smapbase));
140 if (smapbase != NULL) {
141 n = 0;
142 STAILQ_FOREACH(cur, &head, sb_bufs)
143 smapbase[n++] = cur->sb_smap;
144 }
145 cur = STAILQ_FIRST(&head);
146 while (cur != NULL) {
147 next = STAILQ_NEXT(cur, sb_bufs);
148 free(cur);
149 cur = next;
150 }
151 }
152 free(efi_mmap);
153 }
154
155 void
efi_addsmapdata(struct preloaded_file * kfp)156 efi_addsmapdata(struct preloaded_file *kfp)
157 {
158 size_t size;
159
160 if (smapbase == NULL || smaplen == 0)
161 return;
162 size = smaplen * sizeof (*smapbase);
163 file_addmetadata(kfp, MODINFOMD_SMAP, size, smapbase);
164 }
165
166 COMMAND_SET(smap, "smap", "show BIOS SMAP", command_smap);
167
168 static int
command_smap(int argc __unused,char * argv[]__unused)169 command_smap(int argc __unused, char *argv[] __unused)
170 {
171 uint_t i;
172
173 if (smapbase == NULL || smaplen == 0)
174 return (CMD_ERROR);
175
176 for (i = 0; i < smaplen; i++)
177 printf("SMAP type=%02" PRIx32 " base=%016" PRIx64
178 " len=%016" PRIx64 "\n", smapbase[i].type,
179 smapbase[i].base, smapbase[i].length);
180 return (CMD_OK);
181 }
182