xref: /freebsd/sys/kern/subr_efi_map.c (revision 8bfd5cefbc7143adffa34d74bc539802a01f0f26)
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