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
efi_var_mem_compare(struct efi_var_entry * var,const efi_guid_t * guid,const u16 * name,struct efi_var_entry ** next)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, ++var_name) {
45 		if (match)
46 			match = (*data == *var_name);
47 		if (!*data)
48 			break;
49 	}
50 
51 	++data;
52 
53 	if (next)
54 		*next = (struct efi_var_entry *)
55 			ALIGN((uintptr_t)data + var->length, 8);
56 
57 	if (match)
58 		efi_current_var = var;
59 
60 	return match;
61 }
62 
63 struct efi_var_entry __efi_runtime
efi_var_mem_find(const efi_guid_t * guid,const u16 * name,struct efi_var_entry ** next)64 *efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
65 		  struct efi_var_entry **next)
66 {
67 	struct efi_var_entry *var, *last;
68 
69 	last = (struct efi_var_entry *)
70 	       ((uintptr_t)efi_var_buf + efi_var_buf->length);
71 
72 	if (!*name) {
73 		if (next) {
74 			*next = efi_var_buf->var;
75 			if (*next >= last)
76 				*next = NULL;
77 		}
78 		return NULL;
79 	}
80 	if (efi_current_var &&
81 	    efi_var_mem_compare(efi_current_var, guid, name, next)) {
82 		if (next && *next >= last)
83 			*next = NULL;
84 		return efi_current_var;
85 	}
86 
87 	var = efi_var_buf->var;
88 	if (var < last) {
89 		for (; var;) {
90 			struct efi_var_entry *pos;
91 			bool match;
92 
93 			match = efi_var_mem_compare(var, guid, name, &pos);
94 			if (pos >= last)
95 				pos = NULL;
96 			if (match) {
97 				if (next)
98 					*next = pos;
99 				return var;
100 			}
101 			var = pos;
102 		}
103 	}
104 	if (next)
105 		*next = NULL;
106 	return NULL;
107 }
108 
efi_var_mem_del(struct efi_var_entry * var)109 void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
110 {
111 	u16 *data;
112 	struct efi_var_entry *next, *last;
113 
114 	if (!var)
115 		return;
116 
117 	last = (struct efi_var_entry *)
118 	       ((uintptr_t)efi_var_buf + efi_var_buf->length);
119 	if (var <= efi_current_var)
120 		efi_current_var = NULL;
121 
122 	for (data = var->name; *data; ++data)
123 		;
124 	++data;
125 	next = (struct efi_var_entry *)
126 	       ALIGN((uintptr_t)data + var->length, 8);
127 	efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
128 
129 	/* efi_memcpy_runtime() can be used because next >= var. */
130 	efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
131 	efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
132 				   efi_var_buf->length -
133 				   sizeof(struct efi_var_file));
134 }
135 
efi_var_mem_ins(u16 * variable_name,const efi_guid_t * vendor,u32 attributes,const efi_uintn_t size1,const void * data1,const efi_uintn_t size2,const void * data2,const u64 time)136 efi_status_t __efi_runtime efi_var_mem_ins(
137 				u16 *variable_name,
138 				const efi_guid_t *vendor, u32 attributes,
139 				const efi_uintn_t size1, const void *data1,
140 				const efi_uintn_t size2, const void *data2,
141 				const u64 time)
142 {
143 	u16 *data;
144 	struct efi_var_entry *var;
145 	u32 var_name_len;
146 
147 	var = (struct efi_var_entry *)
148 	      ((uintptr_t)efi_var_buf + efi_var_buf->length);
149 	for (var_name_len = 0; variable_name[var_name_len]; ++var_name_len)
150 		;
151 	++var_name_len;
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 
efi_var_mem_free(void)178 u64 __efi_runtime efi_var_mem_free(void)
179 {
180 	return EFI_VAR_BUF_SIZE - efi_var_buf->length -
181 	       sizeof(struct efi_var_entry);
182 }
183 
184 /**
185  * efi_var_mem_bs_del() - delete boot service only variables
186  */
efi_var_mem_bs_del(void)187 static void efi_var_mem_bs_del(void)
188 {
189 	struct efi_var_entry *var = efi_var_buf->var;
190 
191 	for (;;) {
192 		struct efi_var_entry *last;
193 
194 		last = (struct efi_var_entry *)
195 		       ((uintptr_t)efi_var_buf + efi_var_buf->length);
196 		if (var >= last)
197 			break;
198 		if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
199 			u16 *data;
200 
201 			/* skip variable */
202 			for (data = var->name; *data; ++data)
203 				;
204 			++data;
205 			var = (struct efi_var_entry *)
206 			      ALIGN((uintptr_t)data + var->length, 8);
207 		} else {
208 			/* delete variable */
209 			efi_var_mem_del(var);
210 		}
211 	}
212 }
213 
214 /**
215  * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
216  *
217  * @event:	callback event
218  * @context:	callback context
219  */
220 static void EFIAPI
efi_var_mem_notify_exit_boot_services(struct efi_event * event,void * context)221 efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
222 {
223 	EFI_ENTRY("%p, %p", event, context);
224 
225 	/* Delete boot service only variables */
226 	efi_var_mem_bs_del();
227 
228 	EFI_EXIT(EFI_SUCCESS);
229 }
230 
231 /**
232  * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
233  *
234  * @event:	callback event
235  * @context:	callback context
236  */
237 static void EFIAPI __efi_runtime
efi_var_mem_notify_virtual_address_map(struct efi_event * event,void * context)238 efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
239 {
240 	efi_convert_pointer(0, (void **)&efi_var_buf);
241 	efi_current_var = NULL;
242 }
243 
efi_var_mem_init(void)244 efi_status_t efi_var_mem_init(void)
245 {
246 	u64 memory;
247 	efi_status_t ret;
248 	struct efi_event *event;
249 
250 	ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
251 				 EFI_RUNTIME_SERVICES_DATA,
252 				 efi_size_in_pages(EFI_VAR_BUF_SIZE),
253 				 &memory);
254 	if (ret != EFI_SUCCESS)
255 		return ret;
256 	efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
257 	memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
258 	efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
259 	efi_var_buf->length = (uintptr_t)efi_var_buf->var -
260 			      (uintptr_t)efi_var_buf;
261 	/* crc32 for 0 bytes = 0 */
262 
263 	ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
264 			       efi_var_mem_notify_exit_boot_services, NULL,
265 			       NULL, &event);
266 	if (ret != EFI_SUCCESS)
267 		return ret;
268 	ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
269 			       efi_var_mem_notify_virtual_address_map, NULL,
270 			       NULL, &event);
271 	if (ret != EFI_SUCCESS)
272 		return ret;
273 	return ret;
274 }
275 
276 efi_status_t __efi_runtime
efi_get_variable_mem(u16 * variable_name,const efi_guid_t * vendor,u32 * attributes,efi_uintn_t * data_size,void * data,u64 * timep)277 efi_get_variable_mem(u16 *variable_name, const efi_guid_t *vendor, u32 *attributes,
278 		     efi_uintn_t *data_size, void *data, u64 *timep)
279 {
280 	efi_uintn_t old_size;
281 	struct efi_var_entry *var;
282 	u16 *pdata;
283 
284 	if (!variable_name || !vendor || !data_size)
285 		return EFI_INVALID_PARAMETER;
286 	var = efi_var_mem_find(vendor, variable_name, NULL);
287 	if (!var)
288 		return EFI_NOT_FOUND;
289 
290 	if (attributes)
291 		*attributes = var->attr;
292 	if (timep)
293 		*timep = var->time;
294 
295 	old_size = *data_size;
296 	*data_size = var->length;
297 	if (old_size < var->length)
298 		return EFI_BUFFER_TOO_SMALL;
299 
300 	if (!data)
301 		return EFI_INVALID_PARAMETER;
302 
303 	for (pdata = var->name; *pdata; ++pdata)
304 		;
305 	++pdata;
306 
307 	efi_memcpy_runtime(data, pdata, var->length);
308 
309 	return EFI_SUCCESS;
310 }
311 
312 efi_status_t __efi_runtime
efi_get_next_variable_name_mem(efi_uintn_t * variable_name_size,u16 * variable_name,efi_guid_t * vendor)313 efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
314 			       u16 *variable_name, efi_guid_t *vendor)
315 {
316 	struct efi_var_entry *var;
317 	efi_uintn_t old_size;
318 	u16 *pdata;
319 
320 	if (!variable_name_size || !variable_name || !vendor)
321 		return EFI_INVALID_PARAMETER;
322 
323 	if (u16_strnlen(variable_name, *variable_name_size) ==
324 	    *variable_name_size)
325 		return EFI_INVALID_PARAMETER;
326 
327 	if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
328 		return EFI_INVALID_PARAMETER;
329 
330 	if (!var)
331 		return EFI_NOT_FOUND;
332 
333 	for (pdata = var->name; *pdata; ++pdata)
334 		;
335 	++pdata;
336 
337 	old_size = *variable_name_size;
338 	*variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
339 
340 	if (old_size < *variable_name_size)
341 		return EFI_BUFFER_TOO_SMALL;
342 
343 	efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
344 	efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
345 
346 	return EFI_SUCCESS;
347 }
348 
efi_var_buf_update(struct efi_var_file * var_buf)349 void efi_var_buf_update(struct efi_var_file *var_buf)
350 {
351 	memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
352 }
353