blob: b265d95dd6babee762ca8c5b9713d47ca7094a81 [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;
22
23/**
24 * efi_var_mem_compare() - compare GUID and name with a variable
25 *
26 * @var: variable to compare
27 * @guid: GUID to compare
28 * @name: variable name to compare
29 * @next: pointer to next variable
30 * Return: true if match
31 */
32static bool __efi_runtime
33efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
34 const u16 *name, struct efi_var_entry **next)
35{
36 int i;
37 u8 *guid1, *guid2;
38 const u16 *data, *var_name;
39 bool match = true;
40
41 for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0;
42 i < sizeof(efi_guid_t) && match; ++i)
43 match = (guid1[i] == guid2[i]);
44
Heinrich Schuchardt49390202023-02-13 19:22:33 +010045 for (data = var->name, var_name = name;; ++data) {
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010046 if (match)
47 match = (*data == *var_name);
48 if (!*data)
49 break;
Heinrich Schuchardt49390202023-02-13 19:22:33 +010050 if (*var_name)
51 ++var_name;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010052 }
53
54 ++data;
55
56 if (next)
57 *next = (struct efi_var_entry *)
58 ALIGN((uintptr_t)data + var->length, 8);
59
60 if (match)
61 efi_current_var = var;
62
63 return match;
64}
65
Ilias Apalodimasde708562024-04-18 15:54:52 +030066/**
67 * efi_var_entry_len() - Get the entry len including headers & name
68 *
69 * @var: pointer to variable start
70 *
71 * Return: 8-byte aligned variable entry length
72 */
73
74u32 __efi_runtime efi_var_entry_len(struct efi_var_entry *var)
75{
76 if (!var)
77 return 0;
78
79 return ALIGN((sizeof(u16) * (u16_strlen(var->name) + 1)) +
80 var->length + sizeof(*var), 8);
81}
82
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010083struct efi_var_entry __efi_runtime
84*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
85 struct efi_var_entry **next)
86{
87 struct efi_var_entry *var, *last;
88
89 last = (struct efi_var_entry *)
90 ((uintptr_t)efi_var_buf + efi_var_buf->length);
91
92 if (!*name) {
93 if (next) {
94 *next = efi_var_buf->var;
95 if (*next >= last)
96 *next = NULL;
97 }
98 return NULL;
99 }
100 if (efi_current_var &&
101 efi_var_mem_compare(efi_current_var, guid, name, next)) {
102 if (next && *next >= last)
103 *next = NULL;
104 return efi_current_var;
105 }
106
107 var = efi_var_buf->var;
108 if (var < last) {
109 for (; var;) {
110 struct efi_var_entry *pos;
111 bool match;
112
113 match = efi_var_mem_compare(var, guid, name, &pos);
114 if (pos >= last)
115 pos = NULL;
116 if (match) {
117 if (next)
118 *next = pos;
119 return var;
120 }
121 var = pos;
122 }
123 }
124 if (next)
125 *next = NULL;
126 return NULL;
127}
128
129void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
130{
131 u16 *data;
132 struct efi_var_entry *next, *last;
133
134 if (!var)
135 return;
136
137 last = (struct efi_var_entry *)
138 ((uintptr_t)efi_var_buf + efi_var_buf->length);
139 if (var <= efi_current_var)
140 efi_current_var = NULL;
141
142 for (data = var->name; *data; ++data)
143 ;
144 ++data;
145 next = (struct efi_var_entry *)
146 ALIGN((uintptr_t)data + var->length, 8);
147 efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
148
Heinrich Schuchardt447753e2020-07-22 07:56:14 +0200149 /* efi_memcpy_runtime() can be used because next >= var. */
150 efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100151 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
152 efi_var_buf->length -
153 sizeof(struct efi_var_file));
154}
155
156efi_status_t __efi_runtime efi_var_mem_ins(
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200157 const u16 *variable_name,
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100158 const efi_guid_t *vendor, u32 attributes,
159 const efi_uintn_t size1, const void *data1,
160 const efi_uintn_t size2, const void *data2,
161 const u64 time)
162{
163 u16 *data;
164 struct efi_var_entry *var;
165 u32 var_name_len;
166
167 var = (struct efi_var_entry *)
168 ((uintptr_t)efi_var_buf + efi_var_buf->length);
Heinrich Schuchardt57a295b2022-12-29 10:50:54 +0100169 var_name_len = u16_strlen(variable_name) + 1;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100170 data = var->name + var_name_len;
171
172 if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
173 EFI_VAR_BUF_SIZE)
174 return EFI_OUT_OF_RESOURCES;
175
176 var->attr = attributes;
177 var->length = size1 + size2;
178 var->time = time;
179
180 efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
181 efi_memcpy_runtime(var->name, variable_name,
182 sizeof(u16) * var_name_len);
183 efi_memcpy_runtime(data, data1, size1);
184 efi_memcpy_runtime((u8 *)data + size1, data2, size2);
185
186 var = (struct efi_var_entry *)
187 ALIGN((uintptr_t)data + var->length, 8);
188 efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
189 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
190 efi_var_buf->length -
191 sizeof(struct efi_var_file));
192
193 return EFI_SUCCESS;
194}
195
196u64 __efi_runtime efi_var_mem_free(void)
197{
Alper Nebi Yasakc49a6532023-07-08 18:23:05 +0300198 if (efi_var_buf->length + sizeof(struct efi_var_entry) >=
199 EFI_VAR_BUF_SIZE)
200 return 0;
201
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100202 return EFI_VAR_BUF_SIZE - efi_var_buf->length -
203 sizeof(struct efi_var_entry);
204}
205
206/**
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100207 * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
208 *
209 * @event: callback event
210 * @context: callback context
211 */
212static void EFIAPI __efi_runtime
213efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
214{
215 efi_convert_pointer(0, (void **)&efi_var_buf);
Heinrich Schuchardteb7dbe82020-07-22 06:29:38 +0200216 efi_current_var = NULL;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100217}
218
219efi_status_t efi_var_mem_init(void)
220{
221 u64 memory;
222 efi_status_t ret;
223 struct efi_event *event;
224
225 ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
226 EFI_RUNTIME_SERVICES_DATA,
227 efi_size_in_pages(EFI_VAR_BUF_SIZE),
228 &memory);
229 if (ret != EFI_SUCCESS)
230 return ret;
231 efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
232 memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
233 efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
234 efi_var_buf->length = (uintptr_t)efi_var_buf->var -
235 (uintptr_t)efi_var_buf;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100236
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100237 ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
238 efi_var_mem_notify_virtual_address_map, NULL,
239 NULL, &event);
240 if (ret != EFI_SUCCESS)
241 return ret;
242 return ret;
243}
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300244
Ilias Apalodimasde708562024-04-18 15:54:52 +0300245/**
246 * efi_var_collect_mem() - Copy EFI variables matching attributes mask from
247 * efi_var_buf
248 *
249 * @buf: buffer containing variable collection
250 * @lenp: buffer length
251 * @mask: mask of matched attributes
252 *
253 * Return: Status code
254 */
255efi_status_t __efi_runtime
256efi_var_collect_mem(struct efi_var_file *buf, efi_uintn_t *lenp, u32 mask)
257{
258 static struct efi_var_file __efi_runtime_data hdr = {
259 .magic = EFI_VAR_FILE_MAGIC,
260 };
261 struct efi_var_entry *last, *var, *var_to;
262
263 hdr.length = sizeof(struct efi_var_file);
264
265 var = efi_var_buf->var;
266 last = (struct efi_var_entry *)
267 ((uintptr_t)efi_var_buf + efi_var_buf->length);
268 if (buf)
269 var_to = buf->var;
270
271 while (var < last) {
272 u32 len = efi_var_entry_len(var);
273
274 if ((var->attr & mask) != mask) {
275 var = (void *)((uintptr_t)var + len);
276 continue;
277 }
278
279 hdr.length += len;
280
281 if (buf && hdr.length <= *lenp) {
282 efi_memcpy_runtime(var_to, var, len);
283 var_to = (void *)var_to + len;
284 }
285 var = (void *)var + len;
286 }
287
288 if (!buf && hdr.length <= *lenp) {
289 *lenp = hdr.length;
290 return EFI_INVALID_PARAMETER;
291 }
292
293 if (!buf || hdr.length > *lenp) {
294 *lenp = hdr.length;
295 return EFI_BUFFER_TOO_SMALL;
296 }
297 hdr.crc32 = crc32(0, (u8 *)buf->var,
298 hdr.length - sizeof(struct efi_var_file));
299
300 efi_memcpy_runtime(buf, &hdr, sizeof(hdr));
301 *lenp = hdr.length;
302
303 return EFI_SUCCESS;
304}
305
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300306efi_status_t __efi_runtime
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200307efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
308 u32 *attributes, efi_uintn_t *data_size, void *data,
Ilias Apalodimasde708562024-04-18 15:54:52 +0300309 u64 *timep, u32 mask)
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300310{
311 efi_uintn_t old_size;
312 struct efi_var_entry *var;
313 u16 *pdata;
314
315 if (!variable_name || !vendor || !data_size)
316 return EFI_INVALID_PARAMETER;
317 var = efi_var_mem_find(vendor, variable_name, NULL);
318 if (!var)
319 return EFI_NOT_FOUND;
320
Ilias Apalodimasde708562024-04-18 15:54:52 +0300321 /*
322 * This function is used at runtime to dump EFI variables.
323 * The memory backend we keep around has BS-only variables as
324 * well. At runtime we filter them here
325 */
326 if (mask && !((var->attr & mask) == mask))
327 return EFI_NOT_FOUND;
328
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300329 if (attributes)
330 *attributes = var->attr;
331 if (timep)
332 *timep = var->time;
333
Ilias Apalodimasde708562024-04-18 15:54:52 +0300334 if (!u16_strcmp(variable_name, u"VarToFile"))
335 return efi_var_collect_mem(data, data_size, EFI_VARIABLE_NON_VOLATILE);
336
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300337 old_size = *data_size;
338 *data_size = var->length;
339 if (old_size < var->length)
340 return EFI_BUFFER_TOO_SMALL;
341
342 if (!data)
343 return EFI_INVALID_PARAMETER;
344
345 for (pdata = var->name; *pdata; ++pdata)
346 ;
347 ++pdata;
348
349 efi_memcpy_runtime(data, pdata, var->length);
350
351 return EFI_SUCCESS;
352}
353
354efi_status_t __efi_runtime
Heinrich Schuchardtcd9f8602020-11-19 19:40:08 +0100355efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
Ilias Apalodimasde708562024-04-18 15:54:52 +0300356 u16 *variable_name, efi_guid_t *vendor,
357 u32 mask)
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300358{
359 struct efi_var_entry *var;
Heinrich Schuchardt4e82c2e2022-12-18 06:08:57 +0000360 efi_uintn_t len, old_size;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300361 u16 *pdata;
362
363 if (!variable_name_size || !variable_name || !vendor)
364 return EFI_INVALID_PARAMETER;
365
Ilias Apalodimasde708562024-04-18 15:54:52 +0300366skip:
Heinrich Schuchardt4e82c2e2022-12-18 06:08:57 +0000367 len = *variable_name_size >> 1;
368 if (u16_strnlen(variable_name, len) == len)
Heinrich Schuchardtcd9f8602020-11-19 19:40:08 +0100369 return EFI_INVALID_PARAMETER;
370
371 if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
372 return EFI_INVALID_PARAMETER;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300373
374 if (!var)
375 return EFI_NOT_FOUND;
376
377 for (pdata = var->name; *pdata; ++pdata)
378 ;
379 ++pdata;
380
381 old_size = *variable_name_size;
382 *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
383
384 if (old_size < *variable_name_size)
385 return EFI_BUFFER_TOO_SMALL;
386
387 efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
388 efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
389
Ilias Apalodimasde708562024-04-18 15:54:52 +0300390 if (mask && !((var->attr & mask) == mask)) {
391 *variable_name_size = old_size;
392 goto skip;
393 }
394
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300395 return EFI_SUCCESS;
396}
Ilias Apalodimas33521442021-01-16 17:28:04 +0200397
398void efi_var_buf_update(struct efi_var_file *var_buf)
399{
400 memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
401}