1 // SPDX-License-Identifier: GPL-2.0+
3 * File interface for UEFI variables
5 * Copyright (c) 2020, Heinrich Schuchardt
9 #include <efi_loader.h>
10 #include <efi_variable.h>
11 #include <u-boot/crc.h>
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().
19 static struct efi_var_file __efi_runtime_data *efi_var_buf;
20 static struct efi_var_entry __efi_runtime_data *efi_current_var;
23 * efi_var_mem_compare() - compare GUID and name with a variable
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
31 static bool __efi_runtime
32 efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
33 const u16 *name, struct efi_var_entry **next)
37 const u16 *data, *var_name;
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]);
44 for (data = var->name, var_name = name;; ++data) {
46 match = (*data == *var_name);
56 *next = (struct efi_var_entry *)
57 ALIGN((uintptr_t)data + var->length, 8);
60 efi_current_var = var;
65 struct efi_var_entry __efi_runtime
66 *efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
67 struct efi_var_entry **next)
69 struct efi_var_entry *var, *last;
71 last = (struct efi_var_entry *)
72 ((uintptr_t)efi_var_buf + efi_var_buf->length);
76 *next = efi_var_buf->var;
82 if (efi_current_var &&
83 efi_var_mem_compare(efi_current_var, guid, name, next)) {
84 if (next && *next >= last)
86 return efi_current_var;
89 var = efi_var_buf->var;
92 struct efi_var_entry *pos;
95 match = efi_var_mem_compare(var, guid, name, &pos);
111 void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
114 struct efi_var_entry *next, *last;
119 last = (struct efi_var_entry *)
120 ((uintptr_t)efi_var_buf + efi_var_buf->length);
121 if (var <= efi_current_var)
122 efi_current_var = NULL;
124 for (data = var->name; *data; ++data)
127 next = (struct efi_var_entry *)
128 ALIGN((uintptr_t)data + var->length, 8);
129 efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
131 /* efi_memcpy_runtime() can be used because next >= var. */
132 efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
133 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
134 efi_var_buf->length -
135 sizeof(struct efi_var_file));
138 efi_status_t __efi_runtime efi_var_mem_ins(
139 const u16 *variable_name,
140 const efi_guid_t *vendor, u32 attributes,
141 const efi_uintn_t size1, const void *data1,
142 const efi_uintn_t size2, const void *data2,
146 struct efi_var_entry *var;
149 var = (struct efi_var_entry *)
150 ((uintptr_t)efi_var_buf + efi_var_buf->length);
151 var_name_len = u16_strlen(variable_name) + 1;
152 data = var->name + var_name_len;
154 if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
156 return EFI_OUT_OF_RESOURCES;
158 var->attr = attributes;
159 var->length = size1 + size2;
162 efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
163 efi_memcpy_runtime(var->name, variable_name,
164 sizeof(u16) * var_name_len);
165 efi_memcpy_runtime(data, data1, size1);
166 efi_memcpy_runtime((u8 *)data + size1, data2, size2);
168 var = (struct efi_var_entry *)
169 ALIGN((uintptr_t)data + var->length, 8);
170 efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
171 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
172 efi_var_buf->length -
173 sizeof(struct efi_var_file));
178 u64 __efi_runtime efi_var_mem_free(void)
180 if (efi_var_buf->length + sizeof(struct efi_var_entry) >=
184 return EFI_VAR_BUF_SIZE - efi_var_buf->length -
185 sizeof(struct efi_var_entry);
189 * efi_var_mem_bs_del() - delete boot service only variables
191 static void efi_var_mem_bs_del(void)
193 struct efi_var_entry *var = efi_var_buf->var;
196 struct efi_var_entry *last;
198 last = (struct efi_var_entry *)
199 ((uintptr_t)efi_var_buf + efi_var_buf->length);
202 if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
206 for (data = var->name; *data; ++data)
209 var = (struct efi_var_entry *)
210 ALIGN((uintptr_t)data + var->length, 8);
212 /* delete variable */
213 efi_var_mem_del(var);
219 * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
221 * @event: callback event
222 * @context: callback context
225 efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
227 EFI_ENTRY("%p, %p", event, context);
229 /* Delete boot service only variables */
230 efi_var_mem_bs_del();
232 EFI_EXIT(EFI_SUCCESS);
236 * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
238 * @event: callback event
239 * @context: callback context
241 static void EFIAPI __efi_runtime
242 efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
244 efi_convert_pointer(0, (void **)&efi_var_buf);
245 efi_current_var = NULL;
248 efi_status_t efi_var_mem_init(void)
252 struct efi_event *event;
254 ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
255 EFI_RUNTIME_SERVICES_DATA,
256 efi_size_in_pages(EFI_VAR_BUF_SIZE),
258 if (ret != EFI_SUCCESS)
260 efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
261 memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
262 efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
263 efi_var_buf->length = (uintptr_t)efi_var_buf->var -
264 (uintptr_t)efi_var_buf;
265 /* crc32 for 0 bytes = 0 */
267 ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
268 efi_var_mem_notify_exit_boot_services, NULL,
270 if (ret != EFI_SUCCESS)
272 ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
273 efi_var_mem_notify_virtual_address_map, NULL,
275 if (ret != EFI_SUCCESS)
280 efi_status_t __efi_runtime
281 efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
282 u32 *attributes, efi_uintn_t *data_size, void *data,
285 efi_uintn_t old_size;
286 struct efi_var_entry *var;
289 if (!variable_name || !vendor || !data_size)
290 return EFI_INVALID_PARAMETER;
291 var = efi_var_mem_find(vendor, variable_name, NULL);
293 return EFI_NOT_FOUND;
296 *attributes = var->attr;
300 old_size = *data_size;
301 *data_size = var->length;
302 if (old_size < var->length)
303 return EFI_BUFFER_TOO_SMALL;
306 return EFI_INVALID_PARAMETER;
308 for (pdata = var->name; *pdata; ++pdata)
312 efi_memcpy_runtime(data, pdata, var->length);
317 efi_status_t __efi_runtime
318 efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
319 u16 *variable_name, efi_guid_t *vendor)
321 struct efi_var_entry *var;
322 efi_uintn_t len, old_size;
325 if (!variable_name_size || !variable_name || !vendor)
326 return EFI_INVALID_PARAMETER;
328 len = *variable_name_size >> 1;
329 if (u16_strnlen(variable_name, len) == len)
330 return EFI_INVALID_PARAMETER;
332 if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
333 return EFI_INVALID_PARAMETER;
336 return EFI_NOT_FOUND;
338 for (pdata = var->name; *pdata; ++pdata)
342 old_size = *variable_name_size;
343 *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
345 if (old_size < *variable_name_size)
346 return EFI_BUFFER_TOO_SMALL;
348 efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
349 efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
354 void efi_var_buf_update(struct efi_var_file *var_buf)
356 memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);