blob: 31180df9e3a09e6eb462d778ef095204bf11400c [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
Heinrich Schuchardt955a3212025-01-16 20:26:59 +01008#define LOG_CATEGORY LOGC_EFI
9
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010010#include <efi_loader.h>
11#include <efi_variable.h>
12#include <u-boot/crc.h>
13
Ilias Apalodimas33521442021-01-16 17:28:04 +020014/*
15 * The variables efi_var_file and efi_var_entry must be static to avoid
16 * referencing them via the global offset table (section .got). The GOT
17 * is neither mapped as EfiRuntimeServicesData nor do we support its
18 * relocation during SetVirtualAddressMap().
19 */
20static struct efi_var_file __efi_runtime_data *efi_var_buf;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010021static struct efi_var_entry __efi_runtime_data *efi_current_var;
Ilias Apalodimas7d2cd3e2025-02-14 15:46:45 +020022static const u16 __efi_runtime_rodata vtf[] = u"VarToFile";
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010023
24/**
25 * efi_var_mem_compare() - compare GUID and name with a variable
26 *
27 * @var: variable to compare
28 * @guid: GUID to compare
29 * @name: variable name to compare
30 * @next: pointer to next variable
31 * Return: true if match
32 */
33static bool __efi_runtime
34efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
35 const u16 *name, struct efi_var_entry **next)
36{
37 int i;
38 u8 *guid1, *guid2;
39 const u16 *data, *var_name;
40 bool match = true;
41
42 for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0;
43 i < sizeof(efi_guid_t) && match; ++i)
44 match = (guid1[i] == guid2[i]);
45
Heinrich Schuchardt49390202023-02-13 19:22:33 +010046 for (data = var->name, var_name = name;; ++data) {
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010047 if (match)
48 match = (*data == *var_name);
49 if (!*data)
50 break;
Heinrich Schuchardt49390202023-02-13 19:22:33 +010051 if (*var_name)
52 ++var_name;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010053 }
54
55 ++data;
56
57 if (next)
58 *next = (struct efi_var_entry *)
59 ALIGN((uintptr_t)data + var->length, 8);
60
61 if (match)
62 efi_current_var = var;
63
64 return match;
65}
66
Ilias Apalodimasde708562024-04-18 15:54:52 +030067/**
68 * efi_var_entry_len() - Get the entry len including headers & name
69 *
70 * @var: pointer to variable start
71 *
72 * Return: 8-byte aligned variable entry length
73 */
74
75u32 __efi_runtime efi_var_entry_len(struct efi_var_entry *var)
76{
77 if (!var)
78 return 0;
79
80 return ALIGN((sizeof(u16) * (u16_strlen(var->name) + 1)) +
81 var->length + sizeof(*var), 8);
82}
83
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010084struct efi_var_entry __efi_runtime
85*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
86 struct efi_var_entry **next)
87{
88 struct efi_var_entry *var, *last;
89
90 last = (struct efi_var_entry *)
91 ((uintptr_t)efi_var_buf + efi_var_buf->length);
92
93 if (!*name) {
94 if (next) {
95 *next = efi_var_buf->var;
96 if (*next >= last)
97 *next = NULL;
98 }
99 return NULL;
100 }
101 if (efi_current_var &&
102 efi_var_mem_compare(efi_current_var, guid, name, next)) {
103 if (next && *next >= last)
104 *next = NULL;
105 return efi_current_var;
106 }
107
108 var = efi_var_buf->var;
109 if (var < last) {
110 for (; var;) {
111 struct efi_var_entry *pos;
112 bool match;
113
114 match = efi_var_mem_compare(var, guid, name, &pos);
115 if (pos >= last)
116 pos = NULL;
117 if (match) {
118 if (next)
119 *next = pos;
120 return var;
121 }
122 var = pos;
123 }
124 }
125 if (next)
126 *next = NULL;
127 return NULL;
128}
129
130void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
131{
132 u16 *data;
133 struct efi_var_entry *next, *last;
134
135 if (!var)
136 return;
137
138 last = (struct efi_var_entry *)
139 ((uintptr_t)efi_var_buf + efi_var_buf->length);
140 if (var <= efi_current_var)
141 efi_current_var = NULL;
142
143 for (data = var->name; *data; ++data)
144 ;
145 ++data;
146 next = (struct efi_var_entry *)
147 ALIGN((uintptr_t)data + var->length, 8);
148 efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
149
Heinrich Schuchardt447753e2020-07-22 07:56:14 +0200150 /* efi_memcpy_runtime() can be used because next >= var. */
151 efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100152 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
153 efi_var_buf->length -
154 sizeof(struct efi_var_file));
155}
156
157efi_status_t __efi_runtime efi_var_mem_ins(
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200158 const u16 *variable_name,
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100159 const efi_guid_t *vendor, u32 attributes,
160 const efi_uintn_t size1, const void *data1,
161 const efi_uintn_t size2, const void *data2,
162 const u64 time)
163{
164 u16 *data;
165 struct efi_var_entry *var;
166 u32 var_name_len;
167
168 var = (struct efi_var_entry *)
169 ((uintptr_t)efi_var_buf + efi_var_buf->length);
Heinrich Schuchardt57a295b2022-12-29 10:50:54 +0100170 var_name_len = u16_strlen(variable_name) + 1;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100171 data = var->name + var_name_len;
172
173 if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
174 EFI_VAR_BUF_SIZE)
175 return EFI_OUT_OF_RESOURCES;
176
177 var->attr = attributes;
178 var->length = size1 + size2;
179 var->time = time;
180
181 efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
182 efi_memcpy_runtime(var->name, variable_name,
183 sizeof(u16) * var_name_len);
184 efi_memcpy_runtime(data, data1, size1);
185 efi_memcpy_runtime((u8 *)data + size1, data2, size2);
186
187 var = (struct efi_var_entry *)
188 ALIGN((uintptr_t)data + var->length, 8);
189 efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
190 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
191 efi_var_buf->length -
192 sizeof(struct efi_var_file));
193
194 return EFI_SUCCESS;
195}
196
197u64 __efi_runtime efi_var_mem_free(void)
198{
Alper Nebi Yasakc49a6532023-07-08 18:23:05 +0300199 if (efi_var_buf->length + sizeof(struct efi_var_entry) >=
200 EFI_VAR_BUF_SIZE)
201 return 0;
202
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100203 return EFI_VAR_BUF_SIZE - efi_var_buf->length -
204 sizeof(struct efi_var_entry);
205}
206
207/**
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100208 * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
209 *
210 * @event: callback event
211 * @context: callback context
212 */
213static void EFIAPI __efi_runtime
214efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
215{
216 efi_convert_pointer(0, (void **)&efi_var_buf);
Heinrich Schuchardteb7dbe82020-07-22 06:29:38 +0200217 efi_current_var = NULL;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100218}
219
220efi_status_t efi_var_mem_init(void)
221{
222 u64 memory;
223 efi_status_t ret;
224 struct efi_event *event;
225
226 ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
227 EFI_RUNTIME_SERVICES_DATA,
228 efi_size_in_pages(EFI_VAR_BUF_SIZE),
229 &memory);
230 if (ret != EFI_SUCCESS)
231 return ret;
232 efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
233 memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
234 efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
235 efi_var_buf->length = (uintptr_t)efi_var_buf->var -
236 (uintptr_t)efi_var_buf;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100237
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100238 ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
239 efi_var_mem_notify_virtual_address_map, NULL,
240 NULL, &event);
241 if (ret != EFI_SUCCESS)
242 return ret;
243 return ret;
244}
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300245
Ilias Apalodimasde708562024-04-18 15:54:52 +0300246/**
247 * efi_var_collect_mem() - Copy EFI variables matching attributes mask from
248 * efi_var_buf
249 *
250 * @buf: buffer containing variable collection
251 * @lenp: buffer length
252 * @mask: mask of matched attributes
253 *
254 * Return: Status code
255 */
256efi_status_t __efi_runtime
257efi_var_collect_mem(struct efi_var_file *buf, efi_uintn_t *lenp, u32 mask)
258{
259 static struct efi_var_file __efi_runtime_data hdr = {
260 .magic = EFI_VAR_FILE_MAGIC,
261 };
262 struct efi_var_entry *last, *var, *var_to;
263
264 hdr.length = sizeof(struct efi_var_file);
265
266 var = efi_var_buf->var;
267 last = (struct efi_var_entry *)
268 ((uintptr_t)efi_var_buf + efi_var_buf->length);
269 if (buf)
270 var_to = buf->var;
271
272 while (var < last) {
273 u32 len = efi_var_entry_len(var);
274
275 if ((var->attr & mask) != mask) {
276 var = (void *)((uintptr_t)var + len);
277 continue;
278 }
279
280 hdr.length += len;
281
282 if (buf && hdr.length <= *lenp) {
283 efi_memcpy_runtime(var_to, var, len);
284 var_to = (void *)var_to + len;
285 }
286 var = (void *)var + len;
287 }
288
289 if (!buf && hdr.length <= *lenp) {
290 *lenp = hdr.length;
291 return EFI_INVALID_PARAMETER;
292 }
293
294 if (!buf || hdr.length > *lenp) {
295 *lenp = hdr.length;
296 return EFI_BUFFER_TOO_SMALL;
297 }
298 hdr.crc32 = crc32(0, (u8 *)buf->var,
299 hdr.length - sizeof(struct efi_var_file));
300
301 efi_memcpy_runtime(buf, &hdr, sizeof(hdr));
302 *lenp = hdr.length;
303
304 return EFI_SUCCESS;
305}
306
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300307efi_status_t __efi_runtime
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200308efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
309 u32 *attributes, efi_uintn_t *data_size, void *data,
Ilias Apalodimasde708562024-04-18 15:54:52 +0300310 u64 *timep, u32 mask)
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300311{
312 efi_uintn_t old_size;
313 struct efi_var_entry *var;
314 u16 *pdata;
315
316 if (!variable_name || !vendor || !data_size)
317 return EFI_INVALID_PARAMETER;
318 var = efi_var_mem_find(vendor, variable_name, NULL);
319 if (!var)
320 return EFI_NOT_FOUND;
321
Ilias Apalodimasde708562024-04-18 15:54:52 +0300322 /*
323 * This function is used at runtime to dump EFI variables.
324 * The memory backend we keep around has BS-only variables as
325 * well. At runtime we filter them here
326 */
327 if (mask && !((var->attr & mask) == mask))
328 return EFI_NOT_FOUND;
329
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300330 if (attributes)
331 *attributes = var->attr;
332 if (timep)
333 *timep = var->time;
334
Ilias Apalodimas7d2cd3e2025-02-14 15:46:45 +0200335 if (!u16_strcmp(variable_name, vtf))
Ilias Apalodimasde708562024-04-18 15:54:52 +0300336 return efi_var_collect_mem(data, data_size, EFI_VARIABLE_NON_VOLATILE);
337
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300338 old_size = *data_size;
339 *data_size = var->length;
340 if (old_size < var->length)
341 return EFI_BUFFER_TOO_SMALL;
342
343 if (!data)
344 return EFI_INVALID_PARAMETER;
345
346 for (pdata = var->name; *pdata; ++pdata)
347 ;
348 ++pdata;
349
350 efi_memcpy_runtime(data, pdata, var->length);
351
352 return EFI_SUCCESS;
353}
354
355efi_status_t __efi_runtime
Heinrich Schuchardtcd9f8602020-11-19 19:40:08 +0100356efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
Ilias Apalodimasde708562024-04-18 15:54:52 +0300357 u16 *variable_name, efi_guid_t *vendor,
358 u32 mask)
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300359{
360 struct efi_var_entry *var;
Heinrich Schuchardt4e82c2e2022-12-18 06:08:57 +0000361 efi_uintn_t len, old_size;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300362 u16 *pdata;
363
364 if (!variable_name_size || !variable_name || !vendor)
365 return EFI_INVALID_PARAMETER;
366
Ilias Apalodimasde708562024-04-18 15:54:52 +0300367skip:
Heinrich Schuchardt4e82c2e2022-12-18 06:08:57 +0000368 len = *variable_name_size >> 1;
369 if (u16_strnlen(variable_name, len) == len)
Heinrich Schuchardtcd9f8602020-11-19 19:40:08 +0100370 return EFI_INVALID_PARAMETER;
371
372 if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
373 return EFI_INVALID_PARAMETER;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300374
375 if (!var)
376 return EFI_NOT_FOUND;
377
378 for (pdata = var->name; *pdata; ++pdata)
379 ;
380 ++pdata;
381
382 old_size = *variable_name_size;
383 *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
384
385 if (old_size < *variable_name_size)
386 return EFI_BUFFER_TOO_SMALL;
387
388 efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
389 efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
390
Ilias Apalodimasde708562024-04-18 15:54:52 +0300391 if (mask && !((var->attr & mask) == mask)) {
392 *variable_name_size = old_size;
393 goto skip;
394 }
395
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300396 return EFI_SUCCESS;
397}
Ilias Apalodimas33521442021-01-16 17:28:04 +0200398
399void efi_var_buf_update(struct efi_var_file *var_buf)
400{
401 memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
402}