blob: 7a2dba7dc2632746c1e2e4bd7ea32ac6caf10eca [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
8#include <common.h>
9#include <efi_loader.h>
10#include <efi_variable.h>
11#include <u-boot/crc.h>
12
13static struct efi_var_file __efi_runtime_data *efi_var_buf;
14static 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 */
25static bool __efi_runtime
26efi_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
57struct 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
103void __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 memmove(var, next, (uintptr_t)last - (uintptr_t)next);
124 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
125 efi_var_buf->length -
126 sizeof(struct efi_var_file));
127}
128
129efi_status_t __efi_runtime efi_var_mem_ins(
130 u16 *variable_name,
131 const efi_guid_t *vendor, u32 attributes,
132 const efi_uintn_t size1, const void *data1,
133 const efi_uintn_t size2, const void *data2,
134 const u64 time)
135{
136 u16 *data;
137 struct efi_var_entry *var;
138 u32 var_name_len;
139
140 var = (struct efi_var_entry *)
141 ((uintptr_t)efi_var_buf + efi_var_buf->length);
142 for (var_name_len = 0; variable_name[var_name_len]; ++var_name_len)
143 ;
144 ++var_name_len;
145 data = var->name + var_name_len;
146
147 if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
148 EFI_VAR_BUF_SIZE)
149 return EFI_OUT_OF_RESOURCES;
150
151 var->attr = attributes;
152 var->length = size1 + size2;
153 var->time = time;
154
155 efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
156 efi_memcpy_runtime(var->name, variable_name,
157 sizeof(u16) * var_name_len);
158 efi_memcpy_runtime(data, data1, size1);
159 efi_memcpy_runtime((u8 *)data + size1, data2, size2);
160
161 var = (struct efi_var_entry *)
162 ALIGN((uintptr_t)data + var->length, 8);
163 efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
164 efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
165 efi_var_buf->length -
166 sizeof(struct efi_var_file));
167
168 return EFI_SUCCESS;
169}
170
171u64 __efi_runtime efi_var_mem_free(void)
172{
173 return EFI_VAR_BUF_SIZE - efi_var_buf->length -
174 sizeof(struct efi_var_entry);
175}
176
177/**
178 * efi_var_mem_bs_del() - delete boot service only variables
179 */
180static void efi_var_mem_bs_del(void)
181{
182 struct efi_var_entry *var = efi_var_buf->var;
183
184 for (;;) {
185 struct efi_var_entry *last;
186
187 last = (struct efi_var_entry *)
188 ((uintptr_t)efi_var_buf + efi_var_buf->length);
189 if (var >= last)
190 break;
191 if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
192 u16 *data;
193
194 /* skip variable */
195 for (data = var->name; *data; ++data)
196 ;
197 ++data;
198 var = (struct efi_var_entry *)
199 ALIGN((uintptr_t)data + var->length, 8);
200 } else {
201 /* delete variable */
202 efi_var_mem_del(var);
203 }
204 }
205}
206
207/**
208 * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
209 *
210 * @event: callback event
211 * @context: callback context
212 */
213static void EFIAPI __efi_runtime
214efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
215{
216 EFI_ENTRY("%p, %p", event, context);
217
218 /* Delete boot service only variables */
219 efi_var_mem_bs_del();
220
221 EFI_EXIT(EFI_SUCCESS);
222}
223
224/**
225 * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
226 *
227 * @event: callback event
228 * @context: callback context
229 */
230static void EFIAPI __efi_runtime
231efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
232{
233 efi_convert_pointer(0, (void **)&efi_var_buf);
234}
235
236efi_status_t efi_var_mem_init(void)
237{
238 u64 memory;
239 efi_status_t ret;
240 struct efi_event *event;
241
242 ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
243 EFI_RUNTIME_SERVICES_DATA,
244 efi_size_in_pages(EFI_VAR_BUF_SIZE),
245 &memory);
246 if (ret != EFI_SUCCESS)
247 return ret;
248 efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
249 memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
250 efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
251 efi_var_buf->length = (uintptr_t)efi_var_buf->var -
252 (uintptr_t)efi_var_buf;
253 /* crc32 for 0 bytes = 0 */
254
255 ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
256 efi_var_mem_notify_exit_boot_services, NULL,
257 NULL, &event);
258 if (ret != EFI_SUCCESS)
259 return ret;
260 ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
261 efi_var_mem_notify_virtual_address_map, NULL,
262 NULL, &event);
263 if (ret != EFI_SUCCESS)
264 return ret;
265 return ret;
266}