1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  Integrate UEFI variables to u-boot env interface
4  *
5  *  Copyright (c) 2018 AKASHI Takahiro, Linaro Limited
6  */
7 
8 #include <charset.h>
9 #include <command.h>
10 #include <efi_loader.h>
11 #include <efi_variable.h>
12 #include <env.h>
13 #include <exports.h>
14 #include <hexdump.h>
15 #include <malloc.h>
16 #include <mapmem.h>
17 #include <rtc.h>
18 #include <u-boot/uuid.h>
19 #include <linux/kernel.h>
20 
21 /*
22  * From efi_variable.c,
23  *
24  * Mapping between UEFI variables and u-boot variables:
25  *
26  *   efi_$guid_$varname = {attributes}(type)value
27  */
28 
29 static const struct {
30 	u32 mask;
31 	char *text;
32 } efi_var_attrs[] = {
33 	{EFI_VARIABLE_NON_VOLATILE, "NV"},
34 	{EFI_VARIABLE_BOOTSERVICE_ACCESS, "BS"},
35 	{EFI_VARIABLE_RUNTIME_ACCESS, "RT"},
36 	{EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, "AW"},
37 	{EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, "AT"},
38 	{EFI_VARIABLE_READ_ONLY, "RO"},
39 };
40 
41 /**
42  * efi_dump_single_var() - show information about a UEFI variable
43  *
44  * @name:	Name of the variable
45  * @guid:	Vendor GUID
46  * @verbose:	if true, dump data
47  *
48  * Show information encoded in one UEFI variable
49  */
efi_dump_single_var(u16 * name,const efi_guid_t * guid,bool verbose)50 static void efi_dump_single_var(u16 *name, const efi_guid_t *guid, bool verbose)
51 {
52 	u32 attributes;
53 	u8 *data;
54 	u64 time;
55 	struct rtc_time tm;
56 	efi_uintn_t size;
57 	int count, i;
58 	efi_status_t ret;
59 
60 	data = NULL;
61 	size = 0;
62 	ret = efi_get_variable_int(name, guid, &attributes, &size, data, &time);
63 	if (ret == EFI_BUFFER_TOO_SMALL) {
64 		data = malloc(size);
65 		if (!data)
66 			goto out;
67 
68 		ret = efi_get_variable_int(name, guid, &attributes, &size,
69 					   data, &time);
70 	}
71 	if (ret == EFI_NOT_FOUND) {
72 		printf("Error: \"%ls\" not defined\n", name);
73 		goto out;
74 	}
75 	if (ret != EFI_SUCCESS)
76 		goto out;
77 
78 	rtc_to_tm(time, &tm);
79 	printf("%ls:\n    %pUl (%pUs)\n", name, guid, guid);
80 	if (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
81 		printf("    %04d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year,
82 		       tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
83 	printf("    ");
84 	for (count = 0, i = 0; i < ARRAY_SIZE(efi_var_attrs); i++)
85 		if (attributes & efi_var_attrs[i].mask) {
86 			if (count)
87 				putc('|');
88 			count++;
89 			puts(efi_var_attrs[i].text);
90 		}
91 	printf(", DataSize = 0x%zx\n", size);
92 	if (verbose)
93 		print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
94 			       data, size, true);
95 
96 out:
97 	free(data);
98 }
99 
match_name(int argc,char * const argv[],u16 * var_name16)100 static bool match_name(int argc, char *const argv[], u16 *var_name16)
101 {
102 	char *buf, *p;
103 	size_t buflen;
104 	int i;
105 	bool result = false;
106 
107 	buflen = utf16_utf8_strlen(var_name16) + 1;
108 	buf = calloc(1, buflen);
109 	if (!buf)
110 		return result;
111 
112 	p = buf;
113 	utf16_utf8_strcpy(&p, var_name16);
114 
115 	for (i = 0; i < argc; argc--, argv++) {
116 		if (!strcmp(buf, argv[i])) {
117 			result = true;
118 			goto out;
119 		}
120 	}
121 
122 out:
123 	free(buf);
124 
125 	return result;
126 }
127 
128 /**
129  * efi_dump_var_all() - show information about all the UEFI variables
130  *
131  * @argc:	Number of arguments (variables)
132  * @argv:	Argument (variable name) array
133  * @verbose:	if true, dump data
134  * Return:	CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
135  *
136  * Show information encoded in all the UEFI variables
137  */
efi_dump_var_all(int argc,char * const argv[],const efi_guid_t * guid_p,bool verbose)138 static int efi_dump_var_all(int argc,  char *const argv[],
139 			    const efi_guid_t *guid_p, bool verbose)
140 {
141 	u16 *var_name16, *p;
142 	efi_uintn_t buf_size, size;
143 	efi_guid_t guid;
144 	efi_status_t ret;
145 	bool match = false;
146 
147 	buf_size = 128;
148 	var_name16 = malloc(buf_size);
149 	if (!var_name16)
150 		return CMD_RET_FAILURE;
151 
152 	var_name16[0] = 0;
153 	for (;;) {
154 		size = buf_size;
155 		ret = efi_get_next_variable_name_int(&size, var_name16,
156 						     &guid);
157 		if (ret == EFI_NOT_FOUND)
158 			break;
159 		if (ret == EFI_BUFFER_TOO_SMALL) {
160 			buf_size = size;
161 			p = realloc(var_name16, buf_size);
162 			if (!p) {
163 				free(var_name16);
164 				return CMD_RET_FAILURE;
165 			}
166 			var_name16 = p;
167 			ret = efi_get_next_variable_name_int(&size, var_name16,
168 							     &guid);
169 		}
170 		if (ret != EFI_SUCCESS) {
171 			free(var_name16);
172 			return CMD_RET_FAILURE;
173 		}
174 
175 		if (guid_p && guidcmp(guid_p, &guid))
176 			continue;
177 		if (!argc || match_name(argc, argv, var_name16)) {
178 			match = true;
179 			efi_dump_single_var(var_name16, &guid, verbose);
180 		}
181 	}
182 	free(var_name16);
183 
184 	if (!match && argc == 1) {
185 		printf("Error: \"%s\" not defined\n", argv[0]);
186 		return CMD_RET_FAILURE;
187 	}
188 
189 	return CMD_RET_SUCCESS;
190 }
191 
192 /**
193  * do_env_print_efi() - show information about UEFI variables
194  *
195  * @cmdtp:	Command table
196  * @flag:	Command flag
197  * @argc:	Number of arguments
198  * @argv:	Argument array
199  * Return:	CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
200  *
201  * This function is for "env print -e" or "printenv -e" command:
202  *   => env print -e [-n] [-guid <guid> | -all] [var [...]]
203  * If one or more variable names are specified, show information
204  * named UEFI variables, otherwise show all the UEFI variables.
205  */
do_env_print_efi(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])206 int do_env_print_efi(struct cmd_tbl *cmdtp, int flag, int argc,
207 		     char *const argv[])
208 {
209 	const efi_guid_t *guid_p = NULL;
210 	efi_guid_t guid;
211 	bool verbose = true;
212 	efi_status_t ret;
213 
214 	/* Initialize EFI drivers */
215 	ret = efi_init_obj_list();
216 	if (ret != EFI_SUCCESS) {
217 		printf("Error: Cannot initialize UEFI sub-system, r = %lu\n",
218 		       ret & ~EFI_ERROR_MASK);
219 		return CMD_RET_FAILURE;
220 	}
221 
222 	for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
223 		if (!strcmp(argv[0], "-guid")) {
224 			if (argc == 1)
225 				return CMD_RET_USAGE;
226 			argc--;
227 			argv++;
228 			if (uuid_str_to_bin(argv[0], guid.b,
229 					    UUID_STR_FORMAT_GUID))
230 				return CMD_RET_USAGE;
231 			guid_p = (const efi_guid_t *)guid.b;
232 		} else if (!strcmp(argv[0], "-n")) {
233 			verbose = false;
234 		} else {
235 			return CMD_RET_USAGE;
236 		}
237 	}
238 
239 	/* enumerate and show all UEFI variables */
240 	return efi_dump_var_all(argc, argv, guid_p, verbose);
241 }
242 
243 /**
244  * append_value() - encode UEFI variable's value
245  * @bufp:	Buffer of encoded UEFI variable's value
246  * @sizep:	Size of buffer
247  * @data:	data to be encoded into the value
248  * Return:	0 on success, -1 otherwise
249  *
250  * Interpret a given data string and append it to buffer.
251  * Buffer will be realloc'ed if necessary.
252  *
253  * Currently supported formats are:
254  *   =0x0123...:		Hexadecimal number
255  *   =H0123...:			Hexadecimal-byte array
256  *   ="...", =S"..." or <string>:
257  *				String
258  */
append_value(char ** bufp,size_t * sizep,char * data)259 static int append_value(char **bufp, size_t *sizep, char *data)
260 {
261 	char *tmp_buf = NULL, *new_buf = NULL, *value;
262 	unsigned long len = 0;
263 
264 	if (!strncmp(data, "=0x", 3)) { /* hexadecimal number */
265 		union {
266 			u8 u8;
267 			u16 u16;
268 			u32 u32;
269 			u64 u64;
270 		} tmp_data;
271 		unsigned long hex_value;
272 		void *hex_ptr;
273 
274 		data += 3;
275 		len = strlen(data);
276 		if ((len & 0x1)) /* not multiple of two */
277 			return -1;
278 
279 		len /= 2;
280 		if (len > 8)
281 			return -1;
282 		else if (len > 4)
283 			len = 8;
284 		else if (len > 2)
285 			len = 4;
286 
287 		/* convert hex hexadecimal number */
288 		if (strict_strtoul(data, 16, &hex_value) < 0)
289 			return -1;
290 
291 		tmp_buf = malloc(len);
292 		if (!tmp_buf)
293 			return -1;
294 
295 		if (len == 1) {
296 			tmp_data.u8 = hex_value;
297 			hex_ptr = &tmp_data.u8;
298 		} else if (len == 2) {
299 			tmp_data.u16 = hex_value;
300 			hex_ptr = &tmp_data.u16;
301 		} else if (len == 4) {
302 			tmp_data.u32 = hex_value;
303 			hex_ptr = &tmp_data.u32;
304 		} else {
305 			tmp_data.u64 = hex_value;
306 			hex_ptr = &tmp_data.u64;
307 		}
308 		memcpy(tmp_buf, hex_ptr, len);
309 		value = tmp_buf;
310 
311 	} else if (!strncmp(data, "=H", 2)) { /* hexadecimal-byte array */
312 		data += 2;
313 		len = strlen(data);
314 		if (len & 0x1) /* not multiple of two */
315 			return -1;
316 
317 		len /= 2;
318 		tmp_buf = malloc(len);
319 		if (!tmp_buf)
320 			return -1;
321 
322 		if (hex2bin((u8 *)tmp_buf, data, len) < 0) {
323 			printf("Error: illegal hexadecimal string\n");
324 			free(tmp_buf);
325 			return -1;
326 		}
327 
328 		value = tmp_buf;
329 	} else { /* string */
330 		if (!strncmp(data, "=\"", 2) || !strncmp(data, "=S\"", 3)) {
331 			if (data[1] == '"')
332 				data += 2;
333 			else
334 				data += 3;
335 			value = data;
336 			len = strlen(data) - 1;
337 			if (data[len] != '"')
338 				return -1;
339 		} else {
340 			value = data;
341 			len = strlen(data);
342 		}
343 	}
344 
345 	new_buf = realloc(*bufp, *sizep + len);
346 	if (!new_buf)
347 		goto out;
348 
349 	memcpy(new_buf + *sizep, value, len);
350 	*bufp = new_buf;
351 	*sizep += len;
352 
353 out:
354 	free(tmp_buf);
355 
356 	return 0;
357 }
358 
359 /**
360  * do_env_set_efi() - set UEFI variable
361  *
362  * @cmdtp:	Command table
363  * @flag:	Command flag
364  * @argc:	Number of arguments
365  * @argv:	Argument array
366  * Return:	CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
367  *
368  * This function is for "env set -e" or "setenv -e" command:
369  *   => env set -e [-guid guid][-nv][-bs][-rt][-at][-a][-v]
370  *		   [-i address:size] var, or
371  *                 var [value ...]
372  * Encode values specified and set given UEFI variable.
373  * If no value is specified, delete the variable.
374  */
do_env_set_efi(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])375 int do_env_set_efi(struct cmd_tbl *cmdtp, int flag, int argc,
376 		   char *const argv[])
377 {
378 	char *var_name, *value, *ep;
379 	ulong addr;
380 	efi_uintn_t size;
381 	efi_guid_t guid;
382 	u32 attributes;
383 	bool default_guid, verbose, value_on_memory;
384 	u16 *var_name16;
385 	efi_status_t ret;
386 
387 	if (argc == 1)
388 		return CMD_RET_USAGE;
389 
390 	/* Initialize EFI drivers */
391 	ret = efi_init_obj_list();
392 	if (ret != EFI_SUCCESS) {
393 		printf("Error: Cannot initialize UEFI sub-system, r = %lu\n",
394 		       ret & ~EFI_ERROR_MASK);
395 		return CMD_RET_FAILURE;
396 	}
397 
398 	/*
399 	 * attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
400 	 *	     EFI_VARIABLE_RUNTIME_ACCESS;
401 	 */
402 	value = NULL;
403 	size = 0;
404 	attributes = 0;
405 	guid = efi_global_variable_guid;
406 	default_guid = true;
407 	verbose = false;
408 	value_on_memory = false;
409 	for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
410 		if (!strcmp(argv[0], "-guid")) {
411 			if (argc == 1)
412 				return CMD_RET_USAGE;
413 
414 			argc--;
415 			argv++;
416 			if (uuid_str_to_bin(argv[0], guid.b,
417 					    UUID_STR_FORMAT_GUID)) {
418 				return CMD_RET_USAGE;
419 			}
420 			default_guid = false;
421 		} else if (!strcmp(argv[0], "-bs")) {
422 			attributes |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
423 		} else if (!strcmp(argv[0], "-rt")) {
424 			attributes |= EFI_VARIABLE_RUNTIME_ACCESS;
425 		} else if (!strcmp(argv[0], "-nv")) {
426 			attributes |= EFI_VARIABLE_NON_VOLATILE;
427 		} else if (!strcmp(argv[0], "-at")) {
428 			attributes |=
429 			  EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
430 		} else if (!strcmp(argv[0], "-a")) {
431 			attributes |= EFI_VARIABLE_APPEND_WRITE;
432 		} else if (!strcmp(argv[0], "-i")) {
433 			/* data comes from memory */
434 			if (argc == 1)
435 				return CMD_RET_USAGE;
436 
437 			argc--;
438 			argv++;
439 			addr = hextoul(argv[0], &ep);
440 			if (*ep != ':')
441 				return CMD_RET_USAGE;
442 
443 			/* 0 should be allowed for delete */
444 			size = hextoul(++ep, NULL);
445 
446 			value_on_memory = true;
447 		} else if (!strcmp(argv[0], "-v")) {
448 			verbose = true;
449 		} else {
450 			return CMD_RET_USAGE;
451 		}
452 	}
453 	if (!argc)
454 		return CMD_RET_USAGE;
455 
456 	var_name = argv[0];
457 	if (default_guid) {
458 		if (!strcmp(var_name, "db") || !strcmp(var_name, "dbx") ||
459 		    !strcmp(var_name, "dbt"))
460 			guid = efi_guid_image_security_database;
461 		else
462 			guid = efi_global_variable_guid;
463 	}
464 
465 	if (verbose) {
466 		printf("GUID: %pUl (%pUs)\n", &guid, &guid);
467 		printf("Attributes: 0x%x\n", attributes);
468 	}
469 
470 	/* for value */
471 	if (value_on_memory)
472 		value = map_sysmem(addr, 0);
473 	else if (argc > 1)
474 		for (argc--, argv++; argc > 0; argc--, argv++)
475 			if (append_value(&value, &size, argv[0]) < 0) {
476 				printf("## Failed to process an argument, %s\n",
477 				       argv[0]);
478 				ret = CMD_RET_FAILURE;
479 				goto out;
480 			}
481 
482 	if (size && verbose) {
483 		printf("Value:\n");
484 		print_hex_dump("    ", DUMP_PREFIX_OFFSET,
485 			       16, 1, value, size, true);
486 	}
487 
488 	var_name16 = efi_convert_string(var_name);
489 	if (!var_name16) {
490 		printf("## Out of memory\n");
491 		ret = CMD_RET_FAILURE;
492 		goto out;
493 	}
494 	ret = efi_set_variable_int(var_name16, &guid, attributes, size, value,
495 				   true);
496 	free(var_name16);
497 	unmap_sysmem(value);
498 	if (ret == EFI_SUCCESS) {
499 		ret = CMD_RET_SUCCESS;
500 	} else {
501 		const char *msg;
502 
503 		switch (ret) {
504 		case EFI_NOT_FOUND:
505 			msg = " (not found)";
506 			break;
507 		case EFI_WRITE_PROTECTED:
508 			msg = " (read only)";
509 			break;
510 		case EFI_INVALID_PARAMETER:
511 			msg = " (invalid parameter)";
512 			break;
513 		case EFI_SECURITY_VIOLATION:
514 			msg = " (validation failed)";
515 			break;
516 		case EFI_OUT_OF_RESOURCES:
517 			msg = " (out of memory)";
518 			break;
519 		default:
520 			msg = "";
521 			break;
522 		}
523 		printf("## Failed to set EFI variable%s\n", msg);
524 		ret = CMD_RET_FAILURE;
525 	}
526 out:
527 	if (value_on_memory)
528 		unmap_sysmem(value);
529 	else
530 		free(value);
531 
532 	return ret;
533 }
534