]> Git Repo - u-boot.git/blob - lib/efi_loader/efi_var_mem.c
Merge branch '2023-07-24-introduce-FF-A-suppport'
[u-boot.git] / lib / efi_loader / efi_var_mem.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * File interface for UEFI variables
4  *
5  * Copyright (c) 2020, Heinrich Schuchardt
6  */
7
8 #include <common.h>
9 #include <efi_loader.h>
10 #include <efi_variable.h>
11 #include <u-boot/crc.h>
12
13 /*
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().
18  */
19 static struct efi_var_file __efi_runtime_data *efi_var_buf;
20 static struct efi_var_entry __efi_runtime_data *efi_current_var;
21
22 /**
23  * efi_var_mem_compare() - compare GUID and name with a variable
24  *
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
30  */
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)
34 {
35         int i;
36         u8 *guid1, *guid2;
37         const u16 *data, *var_name;
38         bool match = true;
39
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]);
43
44         for (data = var->name, var_name = name;; ++data) {
45                 if (match)
46                         match = (*data == *var_name);
47                 if (!*data)
48                         break;
49                 if (*var_name)
50                         ++var_name;
51         }
52
53         ++data;
54
55         if (next)
56                 *next = (struct efi_var_entry *)
57                         ALIGN((uintptr_t)data + var->length, 8);
58
59         if (match)
60                 efi_current_var = var;
61
62         return match;
63 }
64
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)
68 {
69         struct efi_var_entry *var, *last;
70
71         last = (struct efi_var_entry *)
72                ((uintptr_t)efi_var_buf + efi_var_buf->length);
73
74         if (!*name) {
75                 if (next) {
76                         *next = efi_var_buf->var;
77                         if (*next >= last)
78                                 *next = NULL;
79                 }
80                 return NULL;
81         }
82         if (efi_current_var &&
83             efi_var_mem_compare(efi_current_var, guid, name, next)) {
84                 if (next && *next >= last)
85                         *next = NULL;
86                 return efi_current_var;
87         }
88
89         var = efi_var_buf->var;
90         if (var < last) {
91                 for (; var;) {
92                         struct efi_var_entry *pos;
93                         bool match;
94
95                         match = efi_var_mem_compare(var, guid, name, &pos);
96                         if (pos >= last)
97                                 pos = NULL;
98                         if (match) {
99                                 if (next)
100                                         *next = pos;
101                                 return var;
102                         }
103                         var = pos;
104                 }
105         }
106         if (next)
107                 *next = NULL;
108         return NULL;
109 }
110
111 void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
112 {
113         u16 *data;
114         struct efi_var_entry *next, *last;
115
116         if (!var)
117                 return;
118
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;
123
124         for (data = var->name; *data; ++data)
125                 ;
126         ++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;
130
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));
136 }
137
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,
143                                 const u64 time)
144 {
145         u16 *data;
146         struct efi_var_entry *var;
147         u32 var_name_len;
148
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;
153
154         if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
155             EFI_VAR_BUF_SIZE)
156                 return EFI_OUT_OF_RESOURCES;
157
158         var->attr = attributes;
159         var->length = size1 + size2;
160         var->time = time;
161
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);
167
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));
174
175         return EFI_SUCCESS;
176 }
177
178 u64 __efi_runtime efi_var_mem_free(void)
179 {
180         if (efi_var_buf->length + sizeof(struct efi_var_entry) >=
181             EFI_VAR_BUF_SIZE)
182                 return 0;
183
184         return EFI_VAR_BUF_SIZE - efi_var_buf->length -
185                sizeof(struct efi_var_entry);
186 }
187
188 /**
189  * efi_var_mem_bs_del() - delete boot service only variables
190  */
191 static void efi_var_mem_bs_del(void)
192 {
193         struct efi_var_entry *var = efi_var_buf->var;
194
195         for (;;) {
196                 struct efi_var_entry *last;
197
198                 last = (struct efi_var_entry *)
199                        ((uintptr_t)efi_var_buf + efi_var_buf->length);
200                 if (var >= last)
201                         break;
202                 if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
203                         u16 *data;
204
205                         /* skip variable */
206                         for (data = var->name; *data; ++data)
207                                 ;
208                         ++data;
209                         var = (struct efi_var_entry *)
210                               ALIGN((uintptr_t)data + var->length, 8);
211                 } else {
212                         /* delete variable */
213                         efi_var_mem_del(var);
214                 }
215         }
216 }
217
218 /**
219  * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
220  *
221  * @event:      callback event
222  * @context:    callback context
223  */
224 static void EFIAPI
225 efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
226 {
227         EFI_ENTRY("%p, %p", event, context);
228
229         /* Delete boot service only variables */
230         efi_var_mem_bs_del();
231
232         EFI_EXIT(EFI_SUCCESS);
233 }
234
235 /**
236  * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
237  *
238  * @event:      callback event
239  * @context:    callback context
240  */
241 static void EFIAPI __efi_runtime
242 efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
243 {
244         efi_convert_pointer(0, (void **)&efi_var_buf);
245         efi_current_var = NULL;
246 }
247
248 efi_status_t efi_var_mem_init(void)
249 {
250         u64 memory;
251         efi_status_t ret;
252         struct efi_event *event;
253
254         ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
255                                  EFI_RUNTIME_SERVICES_DATA,
256                                  efi_size_in_pages(EFI_VAR_BUF_SIZE),
257                                  &memory);
258         if (ret != EFI_SUCCESS)
259                 return ret;
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 */
266
267         ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
268                                efi_var_mem_notify_exit_boot_services, NULL,
269                                NULL, &event);
270         if (ret != EFI_SUCCESS)
271                 return ret;
272         ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
273                                efi_var_mem_notify_virtual_address_map, NULL,
274                                NULL, &event);
275         if (ret != EFI_SUCCESS)
276                 return ret;
277         return ret;
278 }
279
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,
283                      u64 *timep)
284 {
285         efi_uintn_t old_size;
286         struct efi_var_entry *var;
287         u16 *pdata;
288
289         if (!variable_name || !vendor || !data_size)
290                 return EFI_INVALID_PARAMETER;
291         var = efi_var_mem_find(vendor, variable_name, NULL);
292         if (!var)
293                 return EFI_NOT_FOUND;
294
295         if (attributes)
296                 *attributes = var->attr;
297         if (timep)
298                 *timep = var->time;
299
300         old_size = *data_size;
301         *data_size = var->length;
302         if (old_size < var->length)
303                 return EFI_BUFFER_TOO_SMALL;
304
305         if (!data)
306                 return EFI_INVALID_PARAMETER;
307
308         for (pdata = var->name; *pdata; ++pdata)
309                 ;
310         ++pdata;
311
312         efi_memcpy_runtime(data, pdata, var->length);
313
314         return EFI_SUCCESS;
315 }
316
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)
320 {
321         struct efi_var_entry *var;
322         efi_uintn_t len, old_size;
323         u16 *pdata;
324
325         if (!variable_name_size || !variable_name || !vendor)
326                 return EFI_INVALID_PARAMETER;
327
328         len = *variable_name_size >> 1;
329         if (u16_strnlen(variable_name, len) == len)
330                 return EFI_INVALID_PARAMETER;
331
332         if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
333                 return EFI_INVALID_PARAMETER;
334
335         if (!var)
336                 return EFI_NOT_FOUND;
337
338         for (pdata = var->name; *pdata; ++pdata)
339                 ;
340         ++pdata;
341
342         old_size = *variable_name_size;
343         *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
344
345         if (old_size < *variable_name_size)
346                 return EFI_BUFFER_TOO_SMALL;
347
348         efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
349         efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
350
351         return EFI_SUCCESS;
352 }
353
354 void efi_var_buf_update(struct efi_var_file *var_buf)
355 {
356         memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
357 }
This page took 0.045049 seconds and 4 git commands to generate.