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