Merge tag 'u-boot-atmel-fixes-2021.01-b' of https://gitlab.denx.de/u-boot/custodians...
[platform/kernel/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 struct efi_var_file __efi_runtime_data *efi_var_buf;
14 static struct efi_var_entry __efi_runtime_data *efi_current_var;
15
16 /**
17  * efi_var_mem_compare() - compare GUID and name with a variable
18  *
19  * @var:        variable to compare
20  * @guid:       GUID to compare
21  * @name:       variable name to compare
22  * @next:       pointer to next variable
23  * Return:      true if match
24  */
25 static bool __efi_runtime
26 efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
27                     const u16 *name, struct efi_var_entry **next)
28 {
29         int i;
30         u8 *guid1, *guid2;
31         const u16 *data, *var_name;
32         bool match = true;
33
34         for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0;
35              i < sizeof(efi_guid_t) && match; ++i)
36                 match = (guid1[i] == guid2[i]);
37
38         for (data = var->name, var_name = name;; ++data, ++var_name) {
39                 if (match)
40                         match = (*data == *var_name);
41                 if (!*data)
42                         break;
43         }
44
45         ++data;
46
47         if (next)
48                 *next = (struct efi_var_entry *)
49                         ALIGN((uintptr_t)data + var->length, 8);
50
51         if (match)
52                 efi_current_var = var;
53
54         return match;
55 }
56
57 struct efi_var_entry __efi_runtime
58 *efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
59                   struct efi_var_entry **next)
60 {
61         struct efi_var_entry *var, *last;
62
63         last = (struct efi_var_entry *)
64                ((uintptr_t)efi_var_buf + efi_var_buf->length);
65
66         if (!*name) {
67                 if (next) {
68                         *next = efi_var_buf->var;
69                         if (*next >= last)
70                                 *next = NULL;
71                 }
72                 return NULL;
73         }
74         if (efi_current_var &&
75             efi_var_mem_compare(efi_current_var, guid, name, next)) {
76                 if (next && *next >= last)
77                         *next = NULL;
78                 return efi_current_var;
79         }
80
81         var = efi_var_buf->var;
82         if (var < last) {
83                 for (; var;) {
84                         struct efi_var_entry *pos;
85                         bool match;
86
87                         match = efi_var_mem_compare(var, guid, name, &pos);
88                         if (pos >= last)
89                                 pos = NULL;
90                         if (match) {
91                                 if (next)
92                                         *next = pos;
93                                 return var;
94                         }
95                         var = pos;
96                 }
97         }
98         if (next)
99                 *next = NULL;
100         return NULL;
101 }
102
103 void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
104 {
105         u16 *data;
106         struct efi_var_entry *next, *last;
107
108         if (!var)
109                 return;
110
111         last = (struct efi_var_entry *)
112                ((uintptr_t)efi_var_buf + efi_var_buf->length);
113         if (var <= efi_current_var)
114                 efi_current_var = NULL;
115
116         for (data = var->name; *data; ++data)
117                 ;
118         ++data;
119         next = (struct efi_var_entry *)
120                ALIGN((uintptr_t)data + var->length, 8);
121         efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
122
123         /* efi_memcpy_runtime() can be used because next >= var. */
124         efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
125         efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
126                                    efi_var_buf->length -
127                                    sizeof(struct efi_var_file));
128 }
129
130 efi_status_t __efi_runtime efi_var_mem_ins(
131                                 u16 *variable_name,
132                                 const efi_guid_t *vendor, u32 attributes,
133                                 const efi_uintn_t size1, const void *data1,
134                                 const efi_uintn_t size2, const void *data2,
135                                 const u64 time)
136 {
137         u16 *data;
138         struct efi_var_entry *var;
139         u32 var_name_len;
140
141         var = (struct efi_var_entry *)
142               ((uintptr_t)efi_var_buf + efi_var_buf->length);
143         for (var_name_len = 0; variable_name[var_name_len]; ++var_name_len)
144                 ;
145         ++var_name_len;
146         data = var->name + var_name_len;
147
148         if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
149             EFI_VAR_BUF_SIZE)
150                 return EFI_OUT_OF_RESOURCES;
151
152         var->attr = attributes;
153         var->length = size1 + size2;
154         var->time = time;
155
156         efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
157         efi_memcpy_runtime(var->name, variable_name,
158                            sizeof(u16) * var_name_len);
159         efi_memcpy_runtime(data, data1, size1);
160         efi_memcpy_runtime((u8 *)data + size1, data2, size2);
161
162         var = (struct efi_var_entry *)
163               ALIGN((uintptr_t)data + var->length, 8);
164         efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
165         efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
166                                    efi_var_buf->length -
167                                    sizeof(struct efi_var_file));
168
169         return EFI_SUCCESS;
170 }
171
172 u64 __efi_runtime efi_var_mem_free(void)
173 {
174         return EFI_VAR_BUF_SIZE - efi_var_buf->length -
175                sizeof(struct efi_var_entry);
176 }
177
178 /**
179  * efi_var_mem_bs_del() - delete boot service only variables
180  */
181 static void efi_var_mem_bs_del(void)
182 {
183         struct efi_var_entry *var = efi_var_buf->var;
184
185         for (;;) {
186                 struct efi_var_entry *last;
187
188                 last = (struct efi_var_entry *)
189                        ((uintptr_t)efi_var_buf + efi_var_buf->length);
190                 if (var >= last)
191                         break;
192                 if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
193                         u16 *data;
194
195                         /* skip variable */
196                         for (data = var->name; *data; ++data)
197                                 ;
198                         ++data;
199                         var = (struct efi_var_entry *)
200                               ALIGN((uintptr_t)data + var->length, 8);
201                 } else {
202                         /* delete variable */
203                         efi_var_mem_del(var);
204                 }
205         }
206 }
207
208 /**
209  * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
210  *
211  * @event:      callback event
212  * @context:    callback context
213  */
214 static void EFIAPI
215 efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
216 {
217         EFI_ENTRY("%p, %p", event, context);
218
219         /* Delete boot service only variables */
220         efi_var_mem_bs_del();
221
222         EFI_EXIT(EFI_SUCCESS);
223 }
224
225 /**
226  * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
227  *
228  * @event:      callback event
229  * @context:    callback context
230  */
231 static void EFIAPI __efi_runtime
232 efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
233 {
234         efi_convert_pointer(0, (void **)&efi_var_buf);
235         efi_current_var = NULL;
236 }
237
238 efi_status_t efi_var_mem_init(void)
239 {
240         u64 memory;
241         efi_status_t ret;
242         struct efi_event *event;
243
244         ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
245                                  EFI_RUNTIME_SERVICES_DATA,
246                                  efi_size_in_pages(EFI_VAR_BUF_SIZE),
247                                  &memory);
248         if (ret != EFI_SUCCESS)
249                 return ret;
250         efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
251         memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
252         efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
253         efi_var_buf->length = (uintptr_t)efi_var_buf->var -
254                               (uintptr_t)efi_var_buf;
255         /* crc32 for 0 bytes = 0 */
256
257         ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
258                                efi_var_mem_notify_exit_boot_services, NULL,
259                                NULL, &event);
260         if (ret != EFI_SUCCESS)
261                 return ret;
262         ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
263                                efi_var_mem_notify_virtual_address_map, NULL,
264                                NULL, &event);
265         if (ret != EFI_SUCCESS)
266                 return ret;
267         return ret;
268 }
269
270 efi_status_t __efi_runtime
271 efi_get_variable_mem(u16 *variable_name, const efi_guid_t *vendor, u32 *attributes,
272                      efi_uintn_t *data_size, void *data, u64 *timep)
273 {
274         efi_uintn_t old_size;
275         struct efi_var_entry *var;
276         u16 *pdata;
277
278         if (!variable_name || !vendor || !data_size)
279                 return EFI_INVALID_PARAMETER;
280         var = efi_var_mem_find(vendor, variable_name, NULL);
281         if (!var)
282                 return EFI_NOT_FOUND;
283
284         if (attributes)
285                 *attributes = var->attr;
286         if (timep)
287                 *timep = var->time;
288
289         old_size = *data_size;
290         *data_size = var->length;
291         if (old_size < var->length)
292                 return EFI_BUFFER_TOO_SMALL;
293
294         if (!data)
295                 return EFI_INVALID_PARAMETER;
296
297         for (pdata = var->name; *pdata; ++pdata)
298                 ;
299         ++pdata;
300
301         efi_memcpy_runtime(data, pdata, var->length);
302
303         return EFI_SUCCESS;
304 }
305
306 efi_status_t __efi_runtime
307 efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
308                                u16 *variable_name, efi_guid_t *vendor)
309 {
310         struct efi_var_entry *var;
311         efi_uintn_t old_size;
312         u16 *pdata;
313
314         if (!variable_name_size || !variable_name || !vendor)
315                 return EFI_INVALID_PARAMETER;
316
317         if (u16_strnlen(variable_name, *variable_name_size) ==
318             *variable_name_size)
319                 return EFI_INVALID_PARAMETER;
320
321         if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
322                 return EFI_INVALID_PARAMETER;
323
324         if (!var)
325                 return EFI_NOT_FOUND;
326
327         for (pdata = var->name; *pdata; ++pdata)
328                 ;
329         ++pdata;
330
331         old_size = *variable_name_size;
332         *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
333
334         if (old_size < *variable_name_size)
335                 return EFI_BUFFER_TOO_SMALL;
336
337         efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
338         efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
339
340         return EFI_SUCCESS;
341 }