1 /* $NetBSD: efi_runtime.c,v 1.11 2023/05/22 16:27:48 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jared McNeill <jmcneill@invisible.ca>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "efi.h"
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: efi_runtime.c,v 1.11 2023/05/22 16:27:48 riastradh Exp $");
36
37 #include <sys/param.h>
38 #include <sys/mutex.h>
39 #include <sys/endian.h>
40
41 #include <uvm/uvm_extern.h>
42
43 #include <dev/efivar.h>
44
45 #include <arm/arm/efi_runtime.h>
46 #include <arm/bootconfig.h>
47
48 static kmutex_t efi_lock;
49 static struct efi_rt *RT;
50 #if BYTE_ORDER == LITTLE_ENDIAN
51 static struct efi_rt efi_rtcopy;
52
53 #if NEFI > 0
54 static struct efi_ops arm_efi_ops = {
55 .efi_gettime = arm_efirt_gettime,
56 .efi_settime = arm_efirt_settime,
57 .efi_getvar = arm_efirt_getvar,
58 .efi_setvar = arm_efirt_setvar,
59 .efi_nextvar = arm_efirt_nextvar,
60 };
61 #endif
62 #endif
63
64 int
arm_efirt_init(paddr_t efi_system_table)65 arm_efirt_init(paddr_t efi_system_table)
66 {
67 #if BYTE_ORDER == LITTLE_ENDIAN
68 struct efi_systbl *ST;
69 const size_t sz = PAGE_SIZE * 2;
70 vaddr_t va, cva;
71 paddr_t cpa;
72 int val;
73
74 if (get_bootconf_option(boot_args, "noefirt",
75 BOOTOPT_TYPE_BOOLEAN, &val) && val) {
76 return ENXIO;
77 }
78
79 va = uvm_km_alloc(kernel_map, sz, 0, UVM_KMF_VAONLY);
80 if (va == 0) {
81 aprint_error("%s: can't allocate VA\n", __func__);
82 return ENOMEM;
83 }
84 for (cva = va, cpa = trunc_page(efi_system_table);
85 cva < va + sz;
86 cva += PAGE_SIZE, cpa += PAGE_SIZE) {
87 pmap_kenter_pa(cva, cpa, VM_PROT_READ, 0);
88 }
89 pmap_update(pmap_kernel());
90
91 ST = (void *)(va + (efi_system_table - trunc_page(efi_system_table)));
92 if (ST->st_hdr.th_sig != EFI_SYSTBL_SIG) {
93 aprint_error("EFI: signature mismatch (%#" PRIx64 " != %#"
94 PRIx64 ")\n", ST->st_hdr.th_sig, EFI_SYSTBL_SIG);
95 return EINVAL;
96 }
97
98 struct efi_rt *rt = ST->st_rt;
99 mutex_init(&efi_lock, MUTEX_DEFAULT, IPL_HIGH);
100
101 pmap_activate_efirt();
102
103 memcpy(&efi_rtcopy, rt, sizeof(efi_rtcopy));
104 RT = &efi_rtcopy;
105
106 pmap_deactivate_efirt();
107
108 #if NEFI > 0
109 efi_register_ops(&arm_efi_ops);
110 #endif
111
112 return 0;
113 #else
114 /* EFI runtime not supported in big endian mode */
115 return ENXIO;
116 #endif
117 }
118
119 efi_status
arm_efirt_gettime(struct efi_tm * tm,struct efi_tmcap * tmcap)120 arm_efirt_gettime(struct efi_tm *tm, struct efi_tmcap *tmcap)
121 {
122 efi_status status = EFI_DEVICE_ERROR;
123
124 if (RT == NULL || RT->rt_gettime == NULL) {
125 return EFI_UNSUPPORTED;
126 }
127
128 mutex_enter(&efi_lock);
129 if (arm_efirt_md_enter() == 0) {
130 status = RT->rt_gettime(tm, tmcap);
131 }
132 arm_efirt_md_exit();
133 mutex_exit(&efi_lock);
134
135 return status;
136 }
137
138 efi_status
arm_efirt_settime(struct efi_tm * tm)139 arm_efirt_settime(struct efi_tm *tm)
140 {
141 efi_status status = EFI_DEVICE_ERROR;
142
143 if (RT == NULL || RT->rt_settime == NULL) {
144 return EFI_UNSUPPORTED;
145 }
146
147 mutex_enter(&efi_lock);
148 if (arm_efirt_md_enter() == 0) {
149 status = RT->rt_settime(tm);
150 }
151 arm_efirt_md_exit();
152 mutex_exit(&efi_lock);
153
154 return status;
155 }
156
157 efi_status
arm_efirt_getvar(uint16_t * name,struct uuid * vendor,uint32_t * attrib,u_long * datasize,void * data)158 arm_efirt_getvar(uint16_t *name, struct uuid *vendor, uint32_t *attrib,
159 u_long *datasize, void *data)
160 {
161 efi_status status = EFI_DEVICE_ERROR;
162
163 if (RT == NULL || RT->rt_getvar == NULL) {
164 return EFI_UNSUPPORTED;
165 }
166
167 mutex_enter(&efi_lock);
168 if (arm_efirt_md_enter() == 0) {
169 status = RT->rt_getvar(name, vendor, attrib, datasize, data);
170 }
171 arm_efirt_md_exit();
172 mutex_exit(&efi_lock);
173
174 return status;
175 }
176
177 efi_status
arm_efirt_nextvar(u_long * namesize,efi_char * name,struct uuid * vendor)178 arm_efirt_nextvar(u_long *namesize, efi_char *name, struct uuid *vendor)
179 {
180 efi_status status = EFI_DEVICE_ERROR;
181
182 if (RT == NULL || RT->rt_scanvar == NULL) {
183 return EFI_UNSUPPORTED;
184 }
185
186 mutex_enter(&efi_lock);
187 if (arm_efirt_md_enter() == 0) {
188 status = RT->rt_scanvar(namesize, name, vendor);
189 }
190 arm_efirt_md_exit();
191 mutex_exit(&efi_lock);
192
193 return status;
194 }
195
196 efi_status
arm_efirt_setvar(uint16_t * name,struct uuid * vendor,uint32_t attrib,u_long datasize,void * data)197 arm_efirt_setvar(uint16_t *name, struct uuid *vendor, uint32_t attrib,
198 u_long datasize, void *data)
199 {
200 efi_status status = EFI_DEVICE_ERROR;
201
202 if (RT == NULL || RT->rt_setvar == NULL) {
203 return EFI_UNSUPPORTED;
204 }
205
206 mutex_enter(&efi_lock);
207 if (arm_efirt_md_enter() == 0) {
208 status = RT->rt_setvar(name, vendor, attrib, datasize, data);
209 }
210 arm_efirt_md_exit();
211 mutex_exit(&efi_lock);
212
213 return status;
214 }
215
216 int
arm_efirt_reset(enum efi_reset type)217 arm_efirt_reset(enum efi_reset type)
218 {
219 static int reset_called = false;
220 int error;
221
222 if (RT == NULL || RT->rt_reset == NULL)
223 return ENXIO;
224
225 mutex_enter(&efi_lock);
226 if (reset_called == false) {
227 reset_called = true;
228 if ((error = arm_efirt_md_enter()) == 0) {
229 if (RT->rt_reset(type, 0, 0, NULL) != 0) {
230 error = EIO;
231 }
232 }
233 arm_efirt_md_exit();
234 } else {
235 error = EPERM;
236 }
237 mutex_exit(&efi_lock);
238
239 return error;
240 }
241