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