blob: e1058e3c6aafd14e6e7a7daddcaff67aed02701c [file] [log] [blame]
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * File interface for UEFI variables
4 *
5 * Copyright (c) 2020, Heinrich Schuchardt
6 */
7
8#include <common.h>
9#include <efi_loader.h>
10#include <efi_variable.h>
11#include <u-boot/crc.h>
12
Ilias Apalodimas33521442021-01-16 17:28:04 +020013/*
14 * The variables efi_var_file and efi_var_entry must be static to avoid
15 * referencing them via the global offset table (section .got). The GOT
16 * is neither mapped as EfiRuntimeServicesData nor do we support its
17 * relocation during SetVirtualAddressMap().
18 */
19static struct efi_var_file __efi_runtime_data *efi_var_buf;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010020static struct efi_var_entry __efi_runtime_data *efi_current_var;
21
22/**
23 * efi_var_mem_compare() - compare GUID and name with a variable
24 *
25 * @var: variable to compare
26 * @guid: GUID to compare
27 * @name: variable name to compare
28 * @next: pointer to next variable
29 * Return: true if match
30 */
31static bool __efi_runtime
32efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
33 const u16 *name, struct efi_var_entry **next)
34{
35 int i;
36 u8 *guid1, *guid2;
37 const u16 *data, *var_name;
38 bool match = true;
39
40 for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0;
41 i < sizeof(efi_guid_t) && match; ++i)
42 match = (guid1[i] == guid2[i]);
43
44 for (data = var->name, var_name = name;; ++data, ++var_name) {
45 if (match)
46 match = (*data == *var_name);
47 if (!*data)
48 break;
49 }
50
51 ++data;
52
53 if (next)
54 *next = (struct efi_var_entry *)
55 ALIGN((uintptr_t)data + var->length, 8);
56
57 if (match)
58 efi_current_var = var;
59
60 return match;
61}
62
63struct efi_var_entry __efi_runtime
64*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
65 struct efi_var_entry **next)
66{
67 struct efi_var_entry *var, *last;
68
69 last = (struct efi_var_entry *)
70 ((uintptr_t)efi_var_buf + efi_var_buf->length);
71
72 if (!*name) {
73 if (next) {
74 *next = efi_var_buf->var;
75 if (*next >= last)
76 *next = NULL;
77 }
78 return NULL;
79 }
80 if (efi_current_var &&
81 efi_var_mem_compare(efi_current_var, guid, name, next)) {
82 if (next && *next >= last)
83 *next = NULL;
84 return efi_current_var;
85 }
86
87 var = efi_var_buf->var;
88 if (var < last) {
89 for (; var;) {
90 struct efi_var_entry *pos;
91 bool match;
92
93 match = efi_var_mem_compare(var, guid, name, &pos);
94 if (pos >= last)
95 pos = NULL;
96 if (match) {
97 if (next)
98 *next = pos;
99 return var;
100 }
101 var = pos;
102 }
103 }
104 if (next)
105 *next = NULL;
106 return NULL;
107}
108
109void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
110{
111 u16 *data;
112 struct efi_var_entry *next, *last;
113
114 if (!var)
115 return;
116
117 last = (struct efi_var_entry *)
118 ((uintptr_t)efi_var_buf + efi_var_buf->length);
119 if (var <= efi_current_var)
120 efi_current_var = NULL;
121
122 for (data = var->name; *data; ++data)
123 ;
124 ++data;
125 next = (struct efi_var_entry *)
126 ALIGN((uintptr_t)data + var->length, 8);
127 efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
128
Heinrich Schuchardt447753e2020-07-22 07:56:14 +0200129 /* efi_memcpy_runtime() can be used because next >= var. */
130 efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100131 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
132 efi_var_buf->length -
133 sizeof(struct efi_var_file));
134}
135
136efi_status_t __efi_runtime efi_var_mem_ins(
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200137 const u16 *variable_name,
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100138 const efi_guid_t *vendor, u32 attributes,
139 const efi_uintn_t size1, const void *data1,
140 const efi_uintn_t size2, const void *data2,
141 const u64 time)
142{
143 u16 *data;
144 struct efi_var_entry *var;
145 u32 var_name_len;
146
147 var = (struct efi_var_entry *)
148 ((uintptr_t)efi_var_buf + efi_var_buf->length);
Heinrich Schuchardt57a295b2022-12-29 10:50:54 +0100149 var_name_len = u16_strlen(variable_name) + 1;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100150 data = var->name + var_name_len;
151
152 if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
153 EFI_VAR_BUF_SIZE)
154 return EFI_OUT_OF_RESOURCES;
155
156 var->attr = attributes;
157 var->length = size1 + size2;
158 var->time = time;
159
160 efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
161 efi_memcpy_runtime(var->name, variable_name,
162 sizeof(u16) * var_name_len);
163 efi_memcpy_runtime(data, data1, size1);
164 efi_memcpy_runtime((u8 *)data + size1, data2, size2);
165
166 var = (struct efi_var_entry *)
167 ALIGN((uintptr_t)data + var->length, 8);
168 efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
169 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
170 efi_var_buf->length -
171 sizeof(struct efi_var_file));
172
173 return EFI_SUCCESS;
174}
175
176u64 __efi_runtime efi_var_mem_free(void)
177{
178 return EFI_VAR_BUF_SIZE - efi_var_buf->length -
179 sizeof(struct efi_var_entry);
180}
181
182/**
183 * efi_var_mem_bs_del() - delete boot service only variables
184 */
185static void efi_var_mem_bs_del(void)
186{
187 struct efi_var_entry *var = efi_var_buf->var;
188
189 for (;;) {
190 struct efi_var_entry *last;
191
192 last = (struct efi_var_entry *)
193 ((uintptr_t)efi_var_buf + efi_var_buf->length);
194 if (var >= last)
195 break;
196 if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
197 u16 *data;
198
199 /* skip variable */
200 for (data = var->name; *data; ++data)
201 ;
202 ++data;
203 var = (struct efi_var_entry *)
204 ALIGN((uintptr_t)data + var->length, 8);
205 } else {
206 /* delete variable */
207 efi_var_mem_del(var);
208 }
209 }
210}
211
212/**
213 * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
214 *
215 * @event: callback event
216 * @context: callback context
217 */
Heinrich Schuchardt6dc64282020-09-08 10:51:27 +0000218static void EFIAPI
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100219efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
220{
221 EFI_ENTRY("%p, %p", event, context);
222
223 /* Delete boot service only variables */
224 efi_var_mem_bs_del();
225
226 EFI_EXIT(EFI_SUCCESS);
227}
228
229/**
230 * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
231 *
232 * @event: callback event
233 * @context: callback context
234 */
235static void EFIAPI __efi_runtime
236efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
237{
238 efi_convert_pointer(0, (void **)&efi_var_buf);
Heinrich Schuchardteb7dbe82020-07-22 06:29:38 +0200239 efi_current_var = NULL;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100240}
241
242efi_status_t efi_var_mem_init(void)
243{
244 u64 memory;
245 efi_status_t ret;
246 struct efi_event *event;
247
248 ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
249 EFI_RUNTIME_SERVICES_DATA,
250 efi_size_in_pages(EFI_VAR_BUF_SIZE),
251 &memory);
252 if (ret != EFI_SUCCESS)
253 return ret;
254 efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
255 memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
256 efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
257 efi_var_buf->length = (uintptr_t)efi_var_buf->var -
258 (uintptr_t)efi_var_buf;
259 /* crc32 for 0 bytes = 0 */
260
261 ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
262 efi_var_mem_notify_exit_boot_services, NULL,
263 NULL, &event);
264 if (ret != EFI_SUCCESS)
265 return ret;
266 ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
267 efi_var_mem_notify_virtual_address_map, NULL,
268 NULL, &event);
269 if (ret != EFI_SUCCESS)
270 return ret;
271 return ret;
272}
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300273
274efi_status_t __efi_runtime
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200275efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
276 u32 *attributes, efi_uintn_t *data_size, void *data,
277 u64 *timep)
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300278{
279 efi_uintn_t old_size;
280 struct efi_var_entry *var;
281 u16 *pdata;
282
283 if (!variable_name || !vendor || !data_size)
284 return EFI_INVALID_PARAMETER;
285 var = efi_var_mem_find(vendor, variable_name, NULL);
286 if (!var)
287 return EFI_NOT_FOUND;
288
289 if (attributes)
290 *attributes = var->attr;
291 if (timep)
292 *timep = var->time;
293
294 old_size = *data_size;
295 *data_size = var->length;
296 if (old_size < var->length)
297 return EFI_BUFFER_TOO_SMALL;
298
299 if (!data)
300 return EFI_INVALID_PARAMETER;
301
302 for (pdata = var->name; *pdata; ++pdata)
303 ;
304 ++pdata;
305
306 efi_memcpy_runtime(data, pdata, var->length);
307
308 return EFI_SUCCESS;
309}
310
311efi_status_t __efi_runtime
Heinrich Schuchardtcd9f8602020-11-19 19:40:08 +0100312efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
313 u16 *variable_name, efi_guid_t *vendor)
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300314{
315 struct efi_var_entry *var;
Heinrich Schuchardt4e82c2e2022-12-18 06:08:57 +0000316 efi_uintn_t len, old_size;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300317 u16 *pdata;
318
319 if (!variable_name_size || !variable_name || !vendor)
320 return EFI_INVALID_PARAMETER;
321
Heinrich Schuchardt4e82c2e2022-12-18 06:08:57 +0000322 len = *variable_name_size >> 1;
323 if (u16_strnlen(variable_name, len) == len)
Heinrich Schuchardtcd9f8602020-11-19 19:40:08 +0100324 return EFI_INVALID_PARAMETER;
325
326 if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
327 return EFI_INVALID_PARAMETER;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300328
329 if (!var)
330 return EFI_NOT_FOUND;
331
332 for (pdata = var->name; *pdata; ++pdata)
333 ;
334 ++pdata;
335
336 old_size = *variable_name_size;
337 *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
338
339 if (old_size < *variable_name_size)
340 return EFI_BUFFER_TOO_SMALL;
341
342 efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
343 efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
344
345 return EFI_SUCCESS;
346}
Ilias Apalodimas33521442021-01-16 17:28:04 +0200347
348void efi_var_buf_update(struct efi_var_file *var_buf)
349{
350 memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
351}