1 /*
2 * Copyright (c) 2014 The FreeBSD Foundation
3 * Copyright (c) 2018 Andrew Turner
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7 #include <sys/param.h>
8 #include <sys/systm.h>
9 #include <sys/efi.h>
10 #include <sys/efi_map.h>
11 #include <sys/physmem.h>
12
13 #include <machine/efi.h>
14 #include <machine/vmparam.h>
15
16 void
efi_map_foreach_entry(struct efi_map_header * efihdr,efi_map_entry_cb cb,void * argp)17 efi_map_foreach_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp)
18 {
19 struct efi_md *map, *p;
20 size_t efisz;
21 int ndesc, i;
22
23 /*
24 * Memory map data provided by UEFI via the GetMemoryMap
25 * Boot Services API.
26 */
27 efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
28 map = (struct efi_md *)((uint8_t *)efihdr + efisz);
29
30 if (efihdr->descriptor_size == 0)
31 return;
32 ndesc = efihdr->memory_size / efihdr->descriptor_size;
33
34 for (i = 0, p = map; i < ndesc; i++,
35 p = efi_next_descriptor(p, efihdr->descriptor_size)) {
36 cb(p, argp);
37 }
38 }
39
40 /*
41 * Handle the EFI memory map list.
42 *
43 * We will make two passes at this, the first (exclude == false) to populate
44 * physmem with valid physical memory ranges from recognized map entry types.
45 * In the second pass we will exclude memory ranges from physmem which must not
46 * be used for general allocations, either because they are used by runtime
47 * firmware or otherwise reserved.
48 *
49 * Adding the runtime-reserved memory ranges to physmem and excluding them
50 * later ensures that they are included in the DMAP, but excluded from
51 * phys_avail[].
52 *
53 * Entry types not explicitly listed here are ignored and not mapped.
54 */
55 static void
handle_efi_map_entry(struct efi_md * p,void * argp)56 handle_efi_map_entry(struct efi_md *p, void *argp)
57 {
58 bool exclude = *(bool *)argp;
59
60 switch (p->md_type) {
61 case EFI_MD_TYPE_RECLAIM:
62 /*
63 * The recomended location for ACPI tables. Map into the
64 * DMAP so we can access them from userspace via /dev/mem.
65 */
66 case EFI_MD_TYPE_RT_CODE:
67 /*
68 * Some UEFI implementations put the system table in the
69 * runtime code section. Include it in the DMAP, but will
70 * be excluded from phys_avail.
71 */
72 case EFI_MD_TYPE_RT_DATA:
73 /*
74 * Runtime data will be excluded after the DMAP
75 * region is created to stop it from being added
76 * to phys_avail.
77 */
78 if (exclude) {
79 physmem_exclude_region(p->md_phys,
80 p->md_pages * EFI_PAGE_SIZE, EXFLAG_NOALLOC);
81 break;
82 }
83 /* FALLTHROUGH */
84 case EFI_MD_TYPE_CODE:
85 case EFI_MD_TYPE_DATA:
86 case EFI_MD_TYPE_BS_CODE:
87 case EFI_MD_TYPE_BS_DATA:
88 case EFI_MD_TYPE_FREE:
89 /*
90 * We're allowed to use any entry with these types.
91 */
92 if (!exclude)
93 physmem_hardware_region(p->md_phys,
94 p->md_pages * EFI_PAGE_SIZE);
95 break;
96 default:
97 /* Other types shall not be handled by physmem. */
98 break;
99 }
100 }
101
102 void
efi_map_add_entries(struct efi_map_header * efihdr)103 efi_map_add_entries(struct efi_map_header *efihdr)
104 {
105 bool exclude = false;
106 efi_map_foreach_entry(efihdr, handle_efi_map_entry, &exclude);
107 }
108
109 void
efi_map_exclude_entries(struct efi_map_header * efihdr)110 efi_map_exclude_entries(struct efi_map_header *efihdr)
111 {
112 bool exclude = true;
113 efi_map_foreach_entry(efihdr, handle_efi_map_entry, &exclude);
114 }
115
116 static void
print_efi_map_entry(struct efi_md * p,void * argp __unused)117 print_efi_map_entry(struct efi_md *p, void *argp __unused)
118 {
119 const char *type;
120 static const char *types[] = {
121 "Reserved",
122 "LoaderCode",
123 "LoaderData",
124 "BootServicesCode",
125 "BootServicesData",
126 "RuntimeServicesCode",
127 "RuntimeServicesData",
128 "ConventionalMemory",
129 "UnusableMemory",
130 "ACPIReclaimMemory",
131 "ACPIMemoryNVS",
132 "MemoryMappedIO",
133 "MemoryMappedIOPortSpace",
134 "PalCode",
135 "PersistentMemory"
136 };
137
138 if (p->md_type < nitems(types))
139 type = types[p->md_type];
140 else
141 type = "<INVALID>";
142 printf("%23s %012jx %012jx %08jx ", type, (uintmax_t)p->md_phys,
143 (uintmax_t)p->md_virt, (uintmax_t)p->md_pages);
144 if (p->md_attr & EFI_MD_ATTR_UC)
145 printf("UC ");
146 if (p->md_attr & EFI_MD_ATTR_WC)
147 printf("WC ");
148 if (p->md_attr & EFI_MD_ATTR_WT)
149 printf("WT ");
150 if (p->md_attr & EFI_MD_ATTR_WB)
151 printf("WB ");
152 if (p->md_attr & EFI_MD_ATTR_UCE)
153 printf("UCE ");
154 if (p->md_attr & EFI_MD_ATTR_WP)
155 printf("WP ");
156 if (p->md_attr & EFI_MD_ATTR_RP)
157 printf("RP ");
158 if (p->md_attr & EFI_MD_ATTR_XP)
159 printf("XP ");
160 if (p->md_attr & EFI_MD_ATTR_NV)
161 printf("NV ");
162 if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE)
163 printf("MORE_RELIABLE ");
164 if (p->md_attr & EFI_MD_ATTR_RO)
165 printf("RO ");
166 if (p->md_attr & EFI_MD_ATTR_RT)
167 printf("RUNTIME");
168 printf("\n");
169 }
170
171 void
efi_map_print_entries(struct efi_map_header * efihdr)172 efi_map_print_entries(struct efi_map_header *efihdr)
173 {
174
175 printf("%23s %12s %12s %8s %4s\n",
176 "Type", "Physical", "Virtual", "#Pages", "Attr");
177 efi_map_foreach_entry(efihdr, print_efi_map_entry, NULL);
178 }
179