1 | /* $NetBSD: efi.c,v 1.4 2016/08/24 10:27:23 nonaka Exp $ */ |
2 | /*- |
3 | * Copyright (c) 2016 The NetBSD Foundation, Inc. |
4 | * All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
16 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
17 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
18 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
19 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 | * POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | #include <sys/cdefs.h> |
28 | __KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.4 2016/08/24 10:27:23 nonaka Exp $" ); |
29 | #include <sys/kmem.h> |
30 | #include <sys/param.h> |
31 | #include <sys/systm.h> |
32 | #include <sys/uuid.h> |
33 | |
34 | #include <uvm/uvm_extern.h> |
35 | |
36 | #include <machine/bootinfo.h> |
37 | #include <x86/efi.h> |
38 | |
39 | #include <dev/mm.h> |
40 | |
41 | const struct uuid EFI_UUID_ACPI20 = { |
42 | .time_low = 0x8868e871, |
43 | .time_mid = 0xe4f1, |
44 | .time_hi_and_version = 0x11d3, |
45 | .clock_seq_hi_and_reserved = 0xbc, |
46 | .clock_seq_low = 0x22, |
47 | .node = {0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81} |
48 | }; |
49 | const struct uuid EFI_UUID_ACPI10 = { |
50 | .time_low = 0xeb9d2d30, |
51 | .time_mid = 0x2d88, |
52 | .time_hi_and_version = 0x11d3, |
53 | .clock_seq_hi_and_reserved = 0x9a, |
54 | .clock_seq_low = 0x16, |
55 | .node = {0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} |
56 | }; |
57 | |
58 | vaddr_t efi_getva(paddr_t); |
59 | void efi_relva(vaddr_t); |
60 | struct efi_cfgtbl *efi_getcfgtblhead(void); |
61 | void efi_aprintcfgtbl(void); |
62 | void efi_aprintuuid(const struct uuid *); |
63 | bool efi_uuideq(const struct uuid *, const struct uuid *); |
64 | |
65 | static struct efi_systbl *efi_systbl_va = NULL; |
66 | static struct efi_cfgtbl *efi_cfgtblhead_va = NULL; |
67 | |
68 | /* |
69 | * Map a physical address (PA) to a newly allocated virtual address (VA). |
70 | * The VA must be freed using efi_relva(). |
71 | */ |
72 | vaddr_t |
73 | efi_getva(paddr_t pa) |
74 | { |
75 | vaddr_t va; |
76 | |
77 | #ifdef __HAVE_MM_MD_DIRECT_MAPPED_PHYS |
78 | mm_md_direct_mapped_phys(pa, &va); |
79 | #else |
80 | /* XXX This code path is not tested. */ |
81 | va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, |
82 | UVM_KMF_VAONLY | UVM_KMF_WAITVA); |
83 | if (va == 0) { |
84 | aprint_debug("efi: unable to allocate va\n" ); |
85 | return 0; |
86 | } |
87 | pmap_kenter_pa(va, pa, VM_PROT_READ, 0); |
88 | pmap_update(pmap_kernel()); |
89 | #endif |
90 | return va; |
91 | } |
92 | |
93 | /* |
94 | * Free a virtual address (VA) allocated using efi_getva(). |
95 | */ |
96 | void |
97 | efi_relva(vaddr_t va) |
98 | { |
99 | #ifdef __HAVE_MM_MD_DIRECT_MAPPED_PHYS |
100 | /* XXX Should we free the va? */ |
101 | #else |
102 | /* XXX This code path is not tested. */ |
103 | uvm_km_free(kernel_map, va, PAGE_SIZE, UVM_KMF_VAONLY); |
104 | #endif |
105 | } |
106 | |
107 | /* |
108 | * Test if 2 UUIDs matches. |
109 | */ |
110 | bool |
111 | efi_uuideq(const struct uuid * a, const struct uuid * b) |
112 | { |
113 | return !memcmp(a, b, sizeof(struct uuid)); |
114 | } |
115 | |
116 | /* |
117 | * Print an UUID in a human-readable manner. |
118 | */ |
119 | void |
120 | efi_aprintuuid(const struct uuid * uuid) |
121 | { |
122 | int i; |
123 | |
124 | aprint_debug(" %08" PRIx32 "" , uuid->time_low); |
125 | aprint_debug("-%04" PRIx16 "" , uuid->time_mid); |
126 | aprint_debug("-%04" PRIx16 "" , uuid->time_hi_and_version); |
127 | aprint_debug("-%02" PRIx8 "" , uuid->clock_seq_hi_and_reserved); |
128 | aprint_debug("-%02" PRIx8 "" , uuid->clock_seq_low); |
129 | aprint_debug("-" ); |
130 | for (i = 0; i < _UUID_NODE_LEN; i++) { |
131 | aprint_debug("%02" PRIx8 "" , uuid->node[i]); |
132 | } |
133 | /* If known, also print the human-readable name */ |
134 | if (efi_uuideq(uuid, &EFI_UUID_ACPI20)) { |
135 | aprint_debug(" ACPI 2.0" ); |
136 | } else if (efi_uuideq(uuid, &EFI_UUID_ACPI10)) { |
137 | aprint_debug(" ACPI 1.0" ); |
138 | } |
139 | } |
140 | |
141 | /* |
142 | * Return the VA of the cfgtbl. Must be freed using efi_relva(). |
143 | */ |
144 | struct efi_cfgtbl * |
145 | efi_getcfgtblhead(void) |
146 | { |
147 | paddr_t pa; |
148 | vaddr_t va; |
149 | |
150 | if (efi_cfgtblhead_va != NULL) |
151 | return efi_cfgtblhead_va; |
152 | |
153 | pa = (paddr_t) efi_systbl_va->st_cfgtbl; |
154 | aprint_debug("efi: cfgtbl at pa %" PRIxPADDR "\n" , pa); |
155 | va = efi_getva(pa); |
156 | aprint_debug("efi: cfgtbl mapped at va %" PRIxVADDR "\n" , va); |
157 | efi_cfgtblhead_va = (struct efi_cfgtbl *) va; |
158 | efi_aprintcfgtbl(); |
159 | |
160 | return efi_cfgtblhead_va; |
161 | } |
162 | |
163 | /* |
164 | * Print the config tables. |
165 | */ |
166 | void |
167 | efi_aprintcfgtbl(void) |
168 | { |
169 | struct efi_cfgtbl *ct; |
170 | unsigned long count; |
171 | |
172 | ct = efi_cfgtblhead_va; |
173 | count = efi_systbl_va->st_entries; |
174 | aprint_debug("efi: %lu cfgtbl entries:\n" , count); |
175 | for (; count; count--, ct++) { |
176 | aprint_debug("efi: %16" PRIx64 "" , ct->ct_data); |
177 | efi_aprintuuid(&ct->ct_uuid); |
178 | aprint_debug("\n" ); |
179 | } |
180 | } |
181 | |
182 | /* |
183 | * Return the VA of the config table with the given UUID if found. |
184 | * The VA must be freed using efi_relva(). |
185 | */ |
186 | void * |
187 | efi_getcfgtbl(const struct uuid * uuid) |
188 | { |
189 | paddr_t pa; |
190 | vaddr_t va; |
191 | |
192 | pa = efi_getcfgtblpa(uuid); |
193 | if (pa == 0) |
194 | return NULL; |
195 | va = efi_getva(pa); |
196 | return (void *) va; |
197 | } |
198 | |
199 | /* |
200 | * Return the PA of the first config table. |
201 | */ |
202 | paddr_t |
203 | efi_getcfgtblpa(const struct uuid * uuid) |
204 | { |
205 | struct efi_cfgtbl *ct; |
206 | unsigned long count; |
207 | |
208 | ct = efi_cfgtblhead_va; |
209 | count = efi_systbl_va->st_entries; |
210 | for (; count; count--, ct++) |
211 | if (efi_uuideq(&ct->ct_uuid, uuid)) |
212 | return (paddr_t) ct->ct_data; |
213 | |
214 | return 0; /* Not found. */ |
215 | } |
216 | |
217 | /* Return the PA of the EFI System Table. */ |
218 | paddr_t |
219 | efi_getsystblpa(void) |
220 | { |
221 | struct btinfo_efi *bi; |
222 | paddr_t pa; |
223 | |
224 | bi = lookup_bootinfo(BTINFO_EFI); |
225 | if (bi == NULL) { |
226 | /* Unable to locate the EFI System Table. */ |
227 | return 0; |
228 | } |
229 | pa = bi->bi_systbl; |
230 | return pa; |
231 | } |
232 | |
233 | /* |
234 | * Return a pointer to the EFI System Table. The pointer must be freed using |
235 | * efi_relva(). |
236 | */ |
237 | struct efi_systbl * |
238 | efi_getsystbl(void) |
239 | { |
240 | if (!efi_systbl_va) { |
241 | paddr_t pa; |
242 | vaddr_t va; |
243 | struct efi_systbl *systbl; |
244 | |
245 | pa = efi_getsystblpa(); |
246 | if (pa == 0) |
247 | return NULL; |
248 | aprint_normal("efi: systbl at pa %" PRIxPADDR "\n" , pa); |
249 | va = efi_getva(pa); |
250 | aprint_debug("efi: systbl mapped at va %" PRIxVADDR "\n" , va); |
251 | systbl = (struct efi_systbl *) va; |
252 | /* XXX Check the signature and the CRC32 */ |
253 | aprint_debug("efi: signature %" PRIx64 " revision %" PRIx32 |
254 | " crc32 %" PRIx32 "\n" , systbl->st_hdr.th_sig, |
255 | systbl->st_hdr.th_rev, systbl->st_hdr.th_crc32); |
256 | aprint_debug("efi: firmware revision %" PRIx32 "\n" , |
257 | systbl->st_fwrev); |
258 | /* |
259 | * XXX Also print fwvendor, which is an UCS-2 string (use |
260 | * some UTF-16 routine?) |
261 | */ |
262 | aprint_debug("efi: runtime services at pa %" PRIx64 "\n" , |
263 | systbl->st_rt); |
264 | aprint_debug("efi: boot services at pa %p\n" , |
265 | systbl->st_bs); |
266 | efi_systbl_va = systbl; |
267 | } |
268 | return efi_systbl_va; |
269 | } |
270 | |
271 | /* |
272 | * EFI is available if we are able to locate the EFI System Table. |
273 | */ |
274 | bool |
275 | efi_probe(void) |
276 | { |
277 | if (efi_getsystbl() == 0) { |
278 | aprint_debug("efi: missing or invalid systbl\n" ); |
279 | return false; |
280 | } |
281 | if (efi_getcfgtblhead() == 0) { |
282 | aprint_debug("efi: missing or invalid cfgtbl\n" ); |
283 | efi_relva((vaddr_t) efi_systbl_va); |
284 | return false; |
285 | } |
286 | return true; |
287 | } |
288 | |