blob: 6c21cec5d457e99a85d28822434fdc0e9ca70f46 [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
64struct efi_var_entry __efi_runtime
65*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
66 struct efi_var_entry **next)
67{
68 struct efi_var_entry *var, *last;
69
70 last = (struct efi_var_entry *)
71 ((uintptr_t)efi_var_buf + efi_var_buf->length);
72
73 if (!*name) {
74 if (next) {
75 *next = efi_var_buf->var;
76 if (*next >= last)
77 *next = NULL;
78 }
79 return NULL;
80 }
81 if (efi_current_var &&
82 efi_var_mem_compare(efi_current_var, guid, name, next)) {
83 if (next && *next >= last)
84 *next = NULL;
85 return efi_current_var;
86 }
87
88 var = efi_var_buf->var;
89 if (var < last) {
90 for (; var;) {
91 struct efi_var_entry *pos;
92 bool match;
93
94 match = efi_var_mem_compare(var, guid, name, &pos);
95 if (pos >= last)
96 pos = NULL;
97 if (match) {
98 if (next)
99 *next = pos;
100 return var;
101 }
102 var = pos;
103 }
104 }
105 if (next)
106 *next = NULL;
107 return NULL;
108}
109
110void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
111{
112 u16 *data;
113 struct efi_var_entry *next, *last;
114
115 if (!var)
116 return;
117
118 last = (struct efi_var_entry *)
119 ((uintptr_t)efi_var_buf + efi_var_buf->length);
120 if (var <= efi_current_var)
121 efi_current_var = NULL;
122
123 for (data = var->name; *data; ++data)
124 ;
125 ++data;
126 next = (struct efi_var_entry *)
127 ALIGN((uintptr_t)data + var->length, 8);
128 efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
129
Heinrich Schuchardt447753e2020-07-22 07:56:14 +0200130 /* efi_memcpy_runtime() can be used because next >= var. */
131 efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100132 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
133 efi_var_buf->length -
134 sizeof(struct efi_var_file));
135}
136
137efi_status_t __efi_runtime efi_var_mem_ins(
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200138 const u16 *variable_name,
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100139 const efi_guid_t *vendor, u32 attributes,
140 const efi_uintn_t size1, const void *data1,
141 const efi_uintn_t size2, const void *data2,
142 const u64 time)
143{
144 u16 *data;
145 struct efi_var_entry *var;
146 u32 var_name_len;
147
148 var = (struct efi_var_entry *)
149 ((uintptr_t)efi_var_buf + efi_var_buf->length);
Heinrich Schuchardt57a295b2022-12-29 10:50:54 +0100150 var_name_len = u16_strlen(variable_name) + 1;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100151 data = var->name + var_name_len;
152
153 if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
154 EFI_VAR_BUF_SIZE)
155 return EFI_OUT_OF_RESOURCES;
156
157 var->attr = attributes;
158 var->length = size1 + size2;
159 var->time = time;
160
161 efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
162 efi_memcpy_runtime(var->name, variable_name,
163 sizeof(u16) * var_name_len);
164 efi_memcpy_runtime(data, data1, size1);
165 efi_memcpy_runtime((u8 *)data + size1, data2, size2);
166
167 var = (struct efi_var_entry *)
168 ALIGN((uintptr_t)data + var->length, 8);
169 efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
170 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
171 efi_var_buf->length -
172 sizeof(struct efi_var_file));
173
174 return EFI_SUCCESS;
175}
176
177u64 __efi_runtime efi_var_mem_free(void)
178{
Alper Nebi Yasakc49a6532023-07-08 18:23:05 +0300179 if (efi_var_buf->length + sizeof(struct efi_var_entry) >=
180 EFI_VAR_BUF_SIZE)
181 return 0;
182
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100183 return EFI_VAR_BUF_SIZE - efi_var_buf->length -
184 sizeof(struct efi_var_entry);
185}
186
187/**
188 * efi_var_mem_bs_del() - delete boot service only variables
189 */
190static void efi_var_mem_bs_del(void)
191{
192 struct efi_var_entry *var = efi_var_buf->var;
193
194 for (;;) {
195 struct efi_var_entry *last;
196
197 last = (struct efi_var_entry *)
198 ((uintptr_t)efi_var_buf + efi_var_buf->length);
199 if (var >= last)
200 break;
201 if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
202 u16 *data;
203
204 /* skip variable */
205 for (data = var->name; *data; ++data)
206 ;
207 ++data;
208 var = (struct efi_var_entry *)
209 ALIGN((uintptr_t)data + var->length, 8);
210 } else {
211 /* delete variable */
212 efi_var_mem_del(var);
213 }
214 }
215}
216
217/**
218 * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
219 *
220 * @event: callback event
221 * @context: callback context
222 */
Heinrich Schuchardt6dc64282020-09-08 10:51:27 +0000223static void EFIAPI
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100224efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
225{
226 EFI_ENTRY("%p, %p", event, context);
227
228 /* Delete boot service only variables */
229 efi_var_mem_bs_del();
230
231 EFI_EXIT(EFI_SUCCESS);
232}
233
234/**
235 * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
236 *
237 * @event: callback event
238 * @context: callback context
239 */
240static void EFIAPI __efi_runtime
241efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
242{
243 efi_convert_pointer(0, (void **)&efi_var_buf);
Heinrich Schuchardteb7dbe82020-07-22 06:29:38 +0200244 efi_current_var = NULL;
Heinrich Schuchardt29ef99c2020-03-22 09:07:50 +0100245}
246
247efi_status_t efi_var_mem_init(void)
248{
249 u64 memory;
250 efi_status_t ret;
251 struct efi_event *event;
252
253 ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
254 EFI_RUNTIME_SERVICES_DATA,
255 efi_size_in_pages(EFI_VAR_BUF_SIZE),
256 &memory);
257 if (ret != EFI_SUCCESS)
258 return ret;
259 efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
260 memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
261 efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
262 efi_var_buf->length = (uintptr_t)efi_var_buf->var -
263 (uintptr_t)efi_var_buf;
264 /* crc32 for 0 bytes = 0 */
265
266 ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
267 efi_var_mem_notify_exit_boot_services, NULL,
268 NULL, &event);
269 if (ret != EFI_SUCCESS)
270 return ret;
271 ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
272 efi_var_mem_notify_virtual_address_map, NULL,
273 NULL, &event);
274 if (ret != EFI_SUCCESS)
275 return ret;
276 return ret;
277}
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300278
279efi_status_t __efi_runtime
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200280efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
281 u32 *attributes, efi_uintn_t *data_size, void *data,
282 u64 *timep)
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300283{
284 efi_uintn_t old_size;
285 struct efi_var_entry *var;
286 u16 *pdata;
287
288 if (!variable_name || !vendor || !data_size)
289 return EFI_INVALID_PARAMETER;
290 var = efi_var_mem_find(vendor, variable_name, NULL);
291 if (!var)
292 return EFI_NOT_FOUND;
293
294 if (attributes)
295 *attributes = var->attr;
296 if (timep)
297 *timep = var->time;
298
299 old_size = *data_size;
300 *data_size = var->length;
301 if (old_size < var->length)
302 return EFI_BUFFER_TOO_SMALL;
303
304 if (!data)
305 return EFI_INVALID_PARAMETER;
306
307 for (pdata = var->name; *pdata; ++pdata)
308 ;
309 ++pdata;
310
311 efi_memcpy_runtime(data, pdata, var->length);
312
313 return EFI_SUCCESS;
314}
315
316efi_status_t __efi_runtime
Heinrich Schuchardtcd9f8602020-11-19 19:40:08 +0100317efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
318 u16 *variable_name, efi_guid_t *vendor)
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300319{
320 struct efi_var_entry *var;
Heinrich Schuchardt4e82c2e2022-12-18 06:08:57 +0000321 efi_uintn_t len, old_size;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300322 u16 *pdata;
323
324 if (!variable_name_size || !variable_name || !vendor)
325 return EFI_INVALID_PARAMETER;
326
Heinrich Schuchardt4e82c2e2022-12-18 06:08:57 +0000327 len = *variable_name_size >> 1;
328 if (u16_strnlen(variable_name, len) == len)
Heinrich Schuchardtcd9f8602020-11-19 19:40:08 +0100329 return EFI_INVALID_PARAMETER;
330
331 if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
332 return EFI_INVALID_PARAMETER;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300333
334 if (!var)
335 return EFI_NOT_FOUND;
336
337 for (pdata = var->name; *pdata; ++pdata)
338 ;
339 ++pdata;
340
341 old_size = *variable_name_size;
342 *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
343
344 if (old_size < *variable_name_size)
345 return EFI_BUFFER_TOO_SMALL;
346
347 efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
348 efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
349
350 return EFI_SUCCESS;
351}
Ilias Apalodimas33521442021-01-16 17:28:04 +0200352
353void efi_var_buf_update(struct efi_var_file *var_buf)
354{
355 memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
356}