blob: 139e16aad7c6e68696d9b4df002fa5c016c7fb6e [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 Schuchardt29ef99c2020-03-22 09:07:50 +01008#include <efi_loader.h>
9#include <efi_variable.h>
10#include <u-boot/crc.h>
11
Ilias Apalodimas33521442021-01-16 17:28:04 +020012/*
13 * The variables efi_var_file and efi_var_entry must be static to avoid
14 * referencing them via the global offset table (section .got). The GOT
15 * is neither mapped as EfiRuntimeServicesData nor do we support its
16 * relocation during SetVirtualAddressMap().
17 */
18static struct efi_var_file __efi_runtime_data *efi_var_buf;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010019static struct efi_var_entry __efi_runtime_data *efi_current_var;
20
21/**
22 * efi_var_mem_compare() - compare GUID and name with a variable
23 *
24 * @var: variable to compare
25 * @guid: GUID to compare
26 * @name: variable name to compare
27 * @next: pointer to next variable
28 * Return: true if match
29 */
30static bool __efi_runtime
31efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
32 const u16 *name, struct efi_var_entry **next)
33{
34 int i;
35 u8 *guid1, *guid2;
36 const u16 *data, *var_name;
37 bool match = true;
38
39 for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0;
40 i < sizeof(efi_guid_t) && match; ++i)
41 match = (guid1[i] == guid2[i]);
42
Heinrich Schuchardt49390202023-02-13 19:22:33 +010043 for (data = var->name, var_name = name;; ++data) {
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010044 if (match)
45 match = (*data == *var_name);
46 if (!*data)
47 break;
Heinrich Schuchardt49390202023-02-13 19:22:33 +010048 if (*var_name)
49 ++var_name;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010050 }
51
52 ++data;
53
54 if (next)
55 *next = (struct efi_var_entry *)
56 ALIGN((uintptr_t)data + var->length, 8);
57
58 if (match)
59 efi_current_var = var;
60
61 return match;
62}
63
Ilias Apalodimasde708562024-04-18 15:54:52 +030064/**
65 * efi_var_entry_len() - Get the entry len including headers & name
66 *
67 * @var: pointer to variable start
68 *
69 * Return: 8-byte aligned variable entry length
70 */
71
72u32 __efi_runtime efi_var_entry_len(struct efi_var_entry *var)
73{
74 if (!var)
75 return 0;
76
77 return ALIGN((sizeof(u16) * (u16_strlen(var->name) + 1)) +
78 var->length + sizeof(*var), 8);
79}
80
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +010081struct efi_var_entry __efi_runtime
82*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
83 struct efi_var_entry **next)
84{
85 struct efi_var_entry *var, *last;
86
87 last = (struct efi_var_entry *)
88 ((uintptr_t)efi_var_buf + efi_var_buf->length);
89
90 if (!*name) {
91 if (next) {
92 *next = efi_var_buf->var;
93 if (*next >= last)
94 *next = NULL;
95 }
96 return NULL;
97 }
98 if (efi_current_var &&
99 efi_var_mem_compare(efi_current_var, guid, name, next)) {
100 if (next && *next >= last)
101 *next = NULL;
102 return efi_current_var;
103 }
104
105 var = efi_var_buf->var;
106 if (var < last) {
107 for (; var;) {
108 struct efi_var_entry *pos;
109 bool match;
110
111 match = efi_var_mem_compare(var, guid, name, &pos);
112 if (pos >= last)
113 pos = NULL;
114 if (match) {
115 if (next)
116 *next = pos;
117 return var;
118 }
119 var = pos;
120 }
121 }
122 if (next)
123 *next = NULL;
124 return NULL;
125}
126
127void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
128{
129 u16 *data;
130 struct efi_var_entry *next, *last;
131
132 if (!var)
133 return;
134
135 last = (struct efi_var_entry *)
136 ((uintptr_t)efi_var_buf + efi_var_buf->length);
137 if (var <= efi_current_var)
138 efi_current_var = NULL;
139
140 for (data = var->name; *data; ++data)
141 ;
142 ++data;
143 next = (struct efi_var_entry *)
144 ALIGN((uintptr_t)data + var->length, 8);
145 efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
146
Heinrich Schuchardt447753e2020-07-22 07:56:14 +0200147 /* efi_memcpy_runtime() can be used because next >= var. */
148 efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100149 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
150 efi_var_buf->length -
151 sizeof(struct efi_var_file));
152}
153
154efi_status_t __efi_runtime efi_var_mem_ins(
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200155 const u16 *variable_name,
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100156 const efi_guid_t *vendor, u32 attributes,
157 const efi_uintn_t size1, const void *data1,
158 const efi_uintn_t size2, const void *data2,
159 const u64 time)
160{
161 u16 *data;
162 struct efi_var_entry *var;
163 u32 var_name_len;
164
165 var = (struct efi_var_entry *)
166 ((uintptr_t)efi_var_buf + efi_var_buf->length);
Heinrich Schuchardt57a295b2022-12-29 10:50:54 +0100167 var_name_len = u16_strlen(variable_name) + 1;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100168 data = var->name + var_name_len;
169
170 if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
171 EFI_VAR_BUF_SIZE)
172 return EFI_OUT_OF_RESOURCES;
173
174 var->attr = attributes;
175 var->length = size1 + size2;
176 var->time = time;
177
178 efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
179 efi_memcpy_runtime(var->name, variable_name,
180 sizeof(u16) * var_name_len);
181 efi_memcpy_runtime(data, data1, size1);
182 efi_memcpy_runtime((u8 *)data + size1, data2, size2);
183
184 var = (struct efi_var_entry *)
185 ALIGN((uintptr_t)data + var->length, 8);
186 efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
187 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
188 efi_var_buf->length -
189 sizeof(struct efi_var_file));
190
191 return EFI_SUCCESS;
192}
193
194u64 __efi_runtime efi_var_mem_free(void)
195{
Alper Nebi Yasakc49a6532023-07-08 18:23:05 +0300196 if (efi_var_buf->length + sizeof(struct efi_var_entry) >=
197 EFI_VAR_BUF_SIZE)
198 return 0;
199
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100200 return EFI_VAR_BUF_SIZE - efi_var_buf->length -
201 sizeof(struct efi_var_entry);
202}
203
204/**
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100205 * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
206 *
207 * @event: callback event
208 * @context: callback context
209 */
210static void EFIAPI __efi_runtime
211efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
212{
213 efi_convert_pointer(0, (void **)&efi_var_buf);
Heinrich Schuchardteb7dbe82020-07-22 06:29:38 +0200214 efi_current_var = NULL;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100215}
216
217efi_status_t efi_var_mem_init(void)
218{
219 u64 memory;
220 efi_status_t ret;
221 struct efi_event *event;
222
223 ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
224 EFI_RUNTIME_SERVICES_DATA,
225 efi_size_in_pages(EFI_VAR_BUF_SIZE),
226 &memory);
227 if (ret != EFI_SUCCESS)
228 return ret;
229 efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
230 memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
231 efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
232 efi_var_buf->length = (uintptr_t)efi_var_buf->var -
233 (uintptr_t)efi_var_buf;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100234
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100235 ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
236 efi_var_mem_notify_virtual_address_map, NULL,
237 NULL, &event);
238 if (ret != EFI_SUCCESS)
239 return ret;
240 return ret;
241}
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300242
Ilias Apalodimasde708562024-04-18 15:54:52 +0300243/**
244 * efi_var_collect_mem() - Copy EFI variables matching attributes mask from
245 * efi_var_buf
246 *
247 * @buf: buffer containing variable collection
248 * @lenp: buffer length
249 * @mask: mask of matched attributes
250 *
251 * Return: Status code
252 */
253efi_status_t __efi_runtime
254efi_var_collect_mem(struct efi_var_file *buf, efi_uintn_t *lenp, u32 mask)
255{
256 static struct efi_var_file __efi_runtime_data hdr = {
257 .magic = EFI_VAR_FILE_MAGIC,
258 };
259 struct efi_var_entry *last, *var, *var_to;
260
261 hdr.length = sizeof(struct efi_var_file);
262
263 var = efi_var_buf->var;
264 last = (struct efi_var_entry *)
265 ((uintptr_t)efi_var_buf + efi_var_buf->length);
266 if (buf)
267 var_to = buf->var;
268
269 while (var < last) {
270 u32 len = efi_var_entry_len(var);
271
272 if ((var->attr & mask) != mask) {
273 var = (void *)((uintptr_t)var + len);
274 continue;
275 }
276
277 hdr.length += len;
278
279 if (buf && hdr.length <= *lenp) {
280 efi_memcpy_runtime(var_to, var, len);
281 var_to = (void *)var_to + len;
282 }
283 var = (void *)var + len;
284 }
285
286 if (!buf && hdr.length <= *lenp) {
287 *lenp = hdr.length;
288 return EFI_INVALID_PARAMETER;
289 }
290
291 if (!buf || hdr.length > *lenp) {
292 *lenp = hdr.length;
293 return EFI_BUFFER_TOO_SMALL;
294 }
295 hdr.crc32 = crc32(0, (u8 *)buf->var,
296 hdr.length - sizeof(struct efi_var_file));
297
298 efi_memcpy_runtime(buf, &hdr, sizeof(hdr));
299 *lenp = hdr.length;
300
301 return EFI_SUCCESS;
302}
303
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300304efi_status_t __efi_runtime
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200305efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
306 u32 *attributes, efi_uintn_t *data_size, void *data,
Ilias Apalodimasde708562024-04-18 15:54:52 +0300307 u64 *timep, u32 mask)
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300308{
309 efi_uintn_t old_size;
310 struct efi_var_entry *var;
311 u16 *pdata;
312
313 if (!variable_name || !vendor || !data_size)
314 return EFI_INVALID_PARAMETER;
315 var = efi_var_mem_find(vendor, variable_name, NULL);
316 if (!var)
317 return EFI_NOT_FOUND;
318
Ilias Apalodimasde708562024-04-18 15:54:52 +0300319 /*
320 * This function is used at runtime to dump EFI variables.
321 * The memory backend we keep around has BS-only variables as
322 * well. At runtime we filter them here
323 */
324 if (mask && !((var->attr & mask) == mask))
325 return EFI_NOT_FOUND;
326
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300327 if (attributes)
328 *attributes = var->attr;
329 if (timep)
330 *timep = var->time;
331
Ilias Apalodimasde708562024-04-18 15:54:52 +0300332 if (!u16_strcmp(variable_name, u"VarToFile"))
333 return efi_var_collect_mem(data, data_size, EFI_VARIABLE_NON_VOLATILE);
334
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300335 old_size = *data_size;
336 *data_size = var->length;
337 if (old_size < var->length)
338 return EFI_BUFFER_TOO_SMALL;
339
340 if (!data)
341 return EFI_INVALID_PARAMETER;
342
343 for (pdata = var->name; *pdata; ++pdata)
344 ;
345 ++pdata;
346
347 efi_memcpy_runtime(data, pdata, var->length);
348
349 return EFI_SUCCESS;
350}
351
352efi_status_t __efi_runtime
Heinrich Schuchardtcd9f8602020-11-19 19:40:08 +0100353efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
Ilias Apalodimasde708562024-04-18 15:54:52 +0300354 u16 *variable_name, efi_guid_t *vendor,
355 u32 mask)
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300356{
357 struct efi_var_entry *var;
Heinrich Schuchardt4e82c2e2022-12-18 06:08:57 +0000358 efi_uintn_t len, old_size;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300359 u16 *pdata;
360
361 if (!variable_name_size || !variable_name || !vendor)
362 return EFI_INVALID_PARAMETER;
363
Ilias Apalodimasde708562024-04-18 15:54:52 +0300364skip:
Heinrich Schuchardt4e82c2e2022-12-18 06:08:57 +0000365 len = *variable_name_size >> 1;
366 if (u16_strnlen(variable_name, len) == len)
Heinrich Schuchardtcd9f8602020-11-19 19:40:08 +0100367 return EFI_INVALID_PARAMETER;
368
369 if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
370 return EFI_INVALID_PARAMETER;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300371
372 if (!var)
373 return EFI_NOT_FOUND;
374
375 for (pdata = var->name; *pdata; ++pdata)
376 ;
377 ++pdata;
378
379 old_size = *variable_name_size;
380 *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
381
382 if (old_size < *variable_name_size)
383 return EFI_BUFFER_TOO_SMALL;
384
385 efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
386 efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
387
Ilias Apalodimasde708562024-04-18 15:54:52 +0300388 if (mask && !((var->attr & mask) == mask)) {
389 *variable_name_size = old_size;
390 goto skip;
391 }
392
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300393 return EFI_SUCCESS;
394}
Ilias Apalodimas33521442021-01-16 17:28:04 +0200395
396void efi_var_buf_update(struct efi_var_file *var_buf)
397{
398 memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
399}