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
41const 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};
49const 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
58vaddr_t efi_getva(paddr_t);
59void efi_relva(vaddr_t);
60struct efi_cfgtbl *efi_getcfgtblhead(void);
61void efi_aprintcfgtbl(void);
62void efi_aprintuuid(const struct uuid *);
63bool efi_uuideq(const struct uuid *, const struct uuid *);
64
65static struct efi_systbl *efi_systbl_va = NULL;
66static 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 */
72vaddr_t
73efi_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 */
96void
97efi_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 */
110bool
111efi_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 */
119void
120efi_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 */
144struct efi_cfgtbl *
145efi_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 */
166void
167efi_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 */
186void *
187efi_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 */
202paddr_t
203efi_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. */
218paddr_t
219efi_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 */
237struct efi_systbl *
238efi_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 */
274bool
275efi_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