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