cmd: add efidebug command
[platform/kernel/u-boot.git] / cmd / efidebug.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  UEFI Shell-like command
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 <environment.h>
13 #include <exports.h>
14 #include <malloc.h>
15 #include <search.h>
16 #include <linux/ctype.h>
17
18 #define RT systab.runtime
19
20 /**
21  * do_efi_boot_add() - set UEFI load option
22  *
23  * @cmdtp:      Command table
24  * @flag:       Command flag
25  * @argc:       Number of arguments
26  * @argv:       Argument array
27  * Return:      CMD_RET_SUCCESS on success,
28  *              CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
29  *
30  * Implement efidebug "boot add" sub-command.
31  * Create or change UEFI load option.
32  *   - boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
33  */
34 static int do_efi_boot_add(cmd_tbl_t *cmdtp, int flag,
35                            int argc, char * const argv[])
36 {
37         int id;
38         char *endp;
39         char var_name[9];
40         u16 var_name16[9], *p;
41         efi_guid_t guid;
42         size_t label_len, label_len16;
43         u16 *label;
44         struct efi_device_path *device_path = NULL, *file_path = NULL;
45         struct efi_load_option lo;
46         void *data = NULL;
47         efi_uintn_t size;
48         int ret;
49
50         if (argc < 6 || argc > 7)
51                 return CMD_RET_USAGE;
52
53         id = (int)simple_strtoul(argv[1], &endp, 16);
54         if (*endp != '\0' || id > 0xffff)
55                 return CMD_RET_FAILURE;
56
57         sprintf(var_name, "Boot%04X", id);
58         p = var_name16;
59         utf8_utf16_strncpy(&p, var_name, 9);
60
61         guid = efi_global_variable_guid;
62
63         /* attributes */
64         lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */
65
66         /* label */
67         label_len = strlen(argv[2]);
68         label_len16 = utf8_utf16_strnlen(argv[2], label_len);
69         label = malloc((label_len16 + 1) * sizeof(u16));
70         if (!label)
71                 return CMD_RET_FAILURE;
72         lo.label = label; /* label will be changed below */
73         utf8_utf16_strncpy(&label, argv[2], label_len);
74
75         /* file path */
76         ret = efi_dp_from_name(argv[3], argv[4], argv[5], &device_path,
77                                &file_path);
78         if (ret != EFI_SUCCESS) {
79                 printf("Cannot create device path for \"%s %s\"\n",
80                        argv[3], argv[4]);
81                 ret = CMD_RET_FAILURE;
82                 goto out;
83         }
84         lo.file_path = file_path;
85         lo.file_path_length = efi_dp_size(file_path)
86                                 + sizeof(struct efi_device_path); /* for END */
87
88         /* optional data */
89         lo.optional_data = (u8 *)(argc == 6 ? "" : argv[6]);
90
91         size = efi_serialize_load_option(&lo, (u8 **)&data);
92         if (!size) {
93                 ret = CMD_RET_FAILURE;
94                 goto out;
95         }
96
97         ret = EFI_CALL(RT->set_variable(var_name16, &guid,
98                                         EFI_VARIABLE_BOOTSERVICE_ACCESS |
99                                         EFI_VARIABLE_RUNTIME_ACCESS,
100                                         size, data));
101         ret = (ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE);
102 out:
103         free(data);
104         efi_free_pool(device_path);
105         efi_free_pool(file_path);
106         free(lo.label);
107
108         return ret;
109 }
110
111 /**
112  * do_efi_boot_rm() - delete UEFI load options
113  *
114  * @cmdtp:      Command table
115  * @flag:       Command flag
116  * @argc:       Number of arguments
117  * @argv:       Argument array
118  * Return:      CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
119  *
120  * Implement efidebug "boot rm" sub-command.
121  * Delete UEFI load options.
122  *   - boot rm <id> ...
123  */
124 static int do_efi_boot_rm(cmd_tbl_t *cmdtp, int flag,
125                           int argc, char * const argv[])
126 {
127         efi_guid_t guid;
128         int id, i;
129         char *endp;
130         char var_name[9];
131         u16 var_name16[9];
132         efi_status_t ret;
133
134         if (argc == 1)
135                 return CMD_RET_USAGE;
136
137         guid = efi_global_variable_guid;
138         for (i = 1; i < argc; i++, argv++) {
139                 id = (int)simple_strtoul(argv[1], &endp, 16);
140                 if (*endp != '\0' || id > 0xffff)
141                         return CMD_RET_FAILURE;
142
143                 sprintf(var_name, "Boot%04X", id);
144                 utf8_utf16_strncpy((u16 **)&var_name16, var_name, 9);
145
146                 ret = EFI_CALL(RT->set_variable(var_name16, &guid, 0, 0, NULL));
147                 if (ret) {
148                         printf("cannot remove Boot%04X", id);
149                         return CMD_RET_FAILURE;
150                 }
151         }
152
153         return CMD_RET_SUCCESS;
154 }
155
156 /**
157  * show_efi_boot_opt_data() - dump UEFI load option
158  *
159  * @id:         Load option number
160  * @data:       Value of UEFI load option variable
161  *
162  * Decode the value of UEFI load option variable and print information.
163  */
164 static void show_efi_boot_opt_data(int id, void *data)
165 {
166         struct efi_load_option lo;
167         char *label, *p;
168         size_t label_len16, label_len;
169         u16 *dp_str;
170
171         efi_deserialize_load_option(&lo, data);
172
173         label_len16 = u16_strlen(lo.label);
174         label_len = utf16_utf8_strnlen(lo.label, label_len16);
175         label = malloc(label_len + 1);
176         if (!label)
177                 return;
178         p = label;
179         utf16_utf8_strncpy(&p, lo.label, label_len16);
180
181         printf("Boot%04X:\n", id);
182         printf("\tattributes: %c%c%c (0x%08x)\n",
183                /* ACTIVE */
184                lo.attributes & LOAD_OPTION_ACTIVE ? 'A' : '-',
185                /* FORCE RECONNECT */
186                lo.attributes & LOAD_OPTION_FORCE_RECONNECT ? 'R' : '-',
187                /* HIDDEN */
188                lo.attributes & LOAD_OPTION_HIDDEN ? 'H' : '-',
189                lo.attributes);
190         printf("\tlabel: %s\n", label);
191
192         dp_str = efi_dp_str(lo.file_path);
193         printf("\tfile_path: %ls\n", dp_str);
194         efi_free_pool(dp_str);
195
196         printf("\tdata: %s\n", lo.optional_data);
197
198         free(label);
199 }
200
201 /**
202  * show_efi_boot_opt() - dump UEFI load option
203  *
204  * @id:         Load option number
205  *
206  * Dump information defined by UEFI load option.
207  */
208 static void show_efi_boot_opt(int id)
209 {
210         char var_name[9];
211         u16 var_name16[9], *p;
212         efi_guid_t guid;
213         void *data = NULL;
214         efi_uintn_t size;
215         int ret;
216
217         sprintf(var_name, "Boot%04X", id);
218         p = var_name16;
219         utf8_utf16_strncpy(&p, var_name, 9);
220         guid = efi_global_variable_guid;
221
222         size = 0;
223         ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size, NULL));
224         if (ret == (int)EFI_BUFFER_TOO_SMALL) {
225                 data = malloc(size);
226                 ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size,
227                                                 data));
228         }
229         if (ret == EFI_SUCCESS)
230                 show_efi_boot_opt_data(id, data);
231         else if (ret == EFI_NOT_FOUND)
232                 printf("Boot%04X: not found\n", id);
233
234         free(data);
235 }
236
237 /**
238  * show_efi_boot_dump() - dump all UEFI load options
239  *
240  * @cmdtp:      Command table
241  * @flag:       Command flag
242  * @argc:       Number of arguments
243  * @argv:       Argument array
244  * Return:      CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
245  *
246  * Implement efidebug "boot dump" sub-command.
247  * Dump information of all UEFI load options defined.
248  *   - boot dump
249  */
250 static int do_efi_boot_dump(cmd_tbl_t *cmdtp, int flag,
251                             int argc, char * const argv[])
252 {
253         char regex[256];
254         char * const regexlist[] = {regex};
255         char *variables = NULL, *boot, *value;
256         int len;
257         int id;
258
259         if (argc > 1)
260                 return CMD_RET_USAGE;
261
262         snprintf(regex, 256, "efi_.*-.*-.*-.*-.*_Boot[0-9A-F]+");
263
264         /* TODO: use GetNextVariableName? */
265         len = hexport_r(&env_htab, '\n', H_MATCH_REGEX | H_MATCH_KEY,
266                         &variables, 0, 1, regexlist);
267
268         if (!len)
269                 return CMD_RET_SUCCESS;
270
271         if (len < 0)
272                 return CMD_RET_FAILURE;
273
274         boot = variables;
275         while (*boot) {
276                 value = strstr(boot, "Boot") + 4;
277                 id = (int)simple_strtoul(value, NULL, 16);
278                 show_efi_boot_opt(id);
279                 boot = strchr(boot, '\n');
280                 if (!*boot)
281                         break;
282                 boot++;
283         }
284         free(variables);
285
286         return CMD_RET_SUCCESS;
287 }
288
289 /**
290  * show_efi_boot_order() - show order of UEFI load options
291  *
292  * Return:      CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
293  *
294  * Show order of UEFI load options defined by BootOrder variable.
295  */
296 static int show_efi_boot_order(void)
297 {
298         efi_guid_t guid;
299         u16 *bootorder = NULL;
300         efi_uintn_t size;
301         int num, i;
302         char var_name[9];
303         u16 var_name16[9], *p16;
304         void *data;
305         struct efi_load_option lo;
306         char *label, *p;
307         size_t label_len16, label_len;
308         efi_status_t ret;
309
310         guid = efi_global_variable_guid;
311         size = 0;
312         ret = EFI_CALL(RT->get_variable(L"BootOrder", &guid, NULL, &size,
313                                         NULL));
314         if (ret == EFI_BUFFER_TOO_SMALL) {
315                 bootorder = malloc(size);
316                 ret = EFI_CALL(RT->get_variable(L"BootOrder", &guid, NULL,
317                                                 &size, bootorder));
318         }
319         if (ret == EFI_NOT_FOUND) {
320                 printf("BootOrder not defined\n");
321                 ret = CMD_RET_SUCCESS;
322                 goto out;
323         } else if (ret != EFI_SUCCESS) {
324                 ret = CMD_RET_FAILURE;
325                 goto out;
326         }
327
328         num = size / sizeof(u16);
329         for (i = 0; i < num; i++) {
330                 sprintf(var_name, "Boot%04X", bootorder[i]);
331                 p16 = var_name16;
332                 utf8_utf16_strncpy(&p16, var_name, 9);
333
334                 size = 0;
335                 ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size,
336                                                 NULL));
337                 if (ret != EFI_BUFFER_TOO_SMALL) {
338                         printf("%2d: Boot%04X: (not defined)\n",
339                                i + 1, bootorder[i]);
340                         continue;
341                 }
342
343                 data = malloc(size);
344                 if (!data) {
345                         ret = CMD_RET_FAILURE;
346                         goto out;
347                 }
348                 ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size,
349                                                 data));
350                 if (ret != EFI_SUCCESS) {
351                         free(data);
352                         ret = CMD_RET_FAILURE;
353                         goto out;
354                 }
355
356                 efi_deserialize_load_option(&lo, data);
357
358                 label_len16 = u16_strlen(lo.label);
359                 label_len = utf16_utf8_strnlen(lo.label, label_len16);
360                 label = malloc(label_len + 1);
361                 if (!label) {
362                         free(data);
363                         ret = CMD_RET_FAILURE;
364                         goto out;
365                 }
366                 p = label;
367                 utf16_utf8_strncpy(&p, lo.label, label_len16);
368                 printf("%2d: Boot%04X: %s\n", i + 1, bootorder[i], label);
369                 free(label);
370
371                 free(data);
372         }
373 out:
374         free(bootorder);
375
376         return ret;
377 }
378
379 /**
380  * do_efi_boot_next() - manage UEFI BootNext variable
381  *
382  * @cmdtp:      Command table
383  * @flag:       Command flag
384  * @argc:       Number of arguments
385  * @argv:       Argument array
386  * Return:      CMD_RET_SUCCESS on success,
387  *              CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
388  *
389  * Implement efidebug "boot next" sub-command.
390  * Set BootNext variable.
391  *   - boot next <id>
392  */
393 static int do_efi_boot_next(cmd_tbl_t *cmdtp, int flag,
394                             int argc, char * const argv[])
395 {
396         u16 bootnext;
397         efi_uintn_t size;
398         char *endp;
399         efi_guid_t guid;
400         efi_status_t ret;
401
402         if (argc != 2)
403                 return CMD_RET_USAGE;
404
405         bootnext = (u16)simple_strtoul(argv[1], &endp, 16);
406         if (*endp != '\0' || bootnext > 0xffff) {
407                 printf("invalid value: %s\n", argv[1]);
408                 ret = CMD_RET_FAILURE;
409                 goto out;
410         }
411
412         guid = efi_global_variable_guid;
413         size = sizeof(u16);
414         ret = EFI_CALL(RT->set_variable(L"BootNext", &guid,
415                                         EFI_VARIABLE_BOOTSERVICE_ACCESS |
416                                         EFI_VARIABLE_RUNTIME_ACCESS,
417                                         size, &bootnext));
418         ret = (ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE);
419 out:
420         return ret;
421 }
422
423 /**
424  * do_efi_boot_order() - manage UEFI BootOrder variable
425  *
426  * @cmdtp:      Command table
427  * @flag:       Command flag
428  * @argc:       Number of arguments
429  * @argv:       Argument array
430  * Return:      CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
431  *
432  * Implement efidebug "boot order" sub-command.
433  * Show order of UEFI load options, or change it in BootOrder variable.
434  *   - boot order [<id> ...]
435  */
436 static int do_efi_boot_order(cmd_tbl_t *cmdtp, int flag,
437                              int argc, char * const argv[])
438 {
439         u16 *bootorder = NULL;
440         efi_uintn_t size;
441         int id, i;
442         char *endp;
443         efi_guid_t guid;
444         efi_status_t ret;
445
446         if (argc == 1)
447                 return show_efi_boot_order();
448
449         argc--;
450         argv++;
451
452         size = argc * sizeof(u16);
453         bootorder = malloc(size);
454         if (!bootorder)
455                 return CMD_RET_FAILURE;
456
457         for (i = 0; i < argc; i++) {
458                 id = (int)simple_strtoul(argv[i], &endp, 16);
459                 if (*endp != '\0' || id > 0xffff) {
460                         printf("invalid value: %s\n", argv[i]);
461                         ret = CMD_RET_FAILURE;
462                         goto out;
463                 }
464
465                 bootorder[i] = (u16)id;
466         }
467
468         guid = efi_global_variable_guid;
469         ret = EFI_CALL(RT->set_variable(L"BootOrder", &guid,
470                                         EFI_VARIABLE_BOOTSERVICE_ACCESS |
471                                         EFI_VARIABLE_RUNTIME_ACCESS,
472                                         size, bootorder));
473         ret = (ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE);
474 out:
475         free(bootorder);
476
477         return ret;
478 }
479
480 static cmd_tbl_t cmd_efidebug_boot_sub[] = {
481         U_BOOT_CMD_MKENT(add, CONFIG_SYS_MAXARGS, 1, do_efi_boot_add, "", ""),
482         U_BOOT_CMD_MKENT(rm, CONFIG_SYS_MAXARGS, 1, do_efi_boot_rm, "", ""),
483         U_BOOT_CMD_MKENT(dump, CONFIG_SYS_MAXARGS, 1, do_efi_boot_dump, "", ""),
484         U_BOOT_CMD_MKENT(next, CONFIG_SYS_MAXARGS, 1, do_efi_boot_next, "", ""),
485         U_BOOT_CMD_MKENT(order, CONFIG_SYS_MAXARGS, 1, do_efi_boot_order,
486                          "", ""),
487 };
488
489 /**
490  * do_efi_boot_opt() - manage UEFI load options
491  *
492  * @cmdtp:      Command table
493  * @flag:       Command flag
494  * @argc:       Number of arguments
495  * @argv:       Argument array
496  * Return:      CMD_RET_SUCCESS on success,
497  *              CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
498  *
499  * Implement efidebug "boot" sub-command.
500  * See above for details of sub-commands.
501  */
502 static int do_efi_boot_opt(cmd_tbl_t *cmdtp, int flag,
503                            int argc, char * const argv[])
504 {
505         cmd_tbl_t *cp;
506
507         if (argc < 2)
508                 return CMD_RET_USAGE;
509
510         argc--; argv++;
511
512         cp = find_cmd_tbl(argv[0], cmd_efidebug_boot_sub,
513                           ARRAY_SIZE(cmd_efidebug_boot_sub));
514         if (!cp)
515                 return CMD_RET_USAGE;
516
517         return cp->cmd(cmdtp, flag, argc, argv);
518 }
519
520 static cmd_tbl_t cmd_efidebug_sub[] = {
521         U_BOOT_CMD_MKENT(boot, CONFIG_SYS_MAXARGS, 1, do_efi_boot_opt, "", ""),
522 };
523
524 /**
525  * do_efidebug() - display and configure UEFI environment
526  *
527  * @cmdtp:      Command table
528  * @flag:       Command flag
529  * @argc:       Number of arguments
530  * @argv:       Argument array
531  * Return:      CMD_RET_SUCCESS on success,
532  *              CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
533  *
534  * Implement efidebug command which allows us to display and
535  * configure UEFI environment.
536  * See above for details of sub-commands.
537  */
538 static int do_efidebug(cmd_tbl_t *cmdtp, int flag,
539                        int argc, char * const argv[])
540 {
541         cmd_tbl_t *cp;
542         efi_status_t r;
543
544         if (argc < 2)
545                 return CMD_RET_USAGE;
546
547         argc--; argv++;
548
549         /* Initialize UEFI drivers */
550         r = efi_init_obj_list();
551         if (r != EFI_SUCCESS) {
552                 printf("Error: Cannot initialize UEFI sub-system, r = %lu\n",
553                        r & ~EFI_ERROR_MASK);
554                 return CMD_RET_FAILURE;
555         }
556
557         cp = find_cmd_tbl(argv[0], cmd_efidebug_sub,
558                           ARRAY_SIZE(cmd_efidebug_sub));
559         if (!cp)
560                 return CMD_RET_USAGE;
561
562         return cp->cmd(cmdtp, flag, argc, argv);
563 }
564
565 #ifdef CONFIG_SYS_LONGHELP
566 static char efidebug_help_text[] =
567         "  - UEFI Shell-like interface to configure UEFI environment\n"
568         "\n"
569         "efidebug boot add <bootid> <label> <interface> <devnum>[:<part>] <file path> [<load options>]\n"
570         "  - set UEFI BootXXXX variable\n"
571         "    <load options> will be passed to UEFI application\n"
572         "efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n"
573         "  - delete UEFI BootXXXX variables\n"
574         "efidebug boot dump\n"
575         "  - dump all UEFI BootXXXX variables\n"
576         "efidebug boot next <bootid>\n"
577         "  - set UEFI BootNext variable\n"
578         "efidebug boot order [<bootid#1> [<bootid#2> [<bootid#3> [...]]]]\n"
579         "  - set/show UEFI boot order\n"
580         "\n";
581 #endif
582
583 U_BOOT_CMD(
584         efidebug, 10, 0, do_efidebug,
585         "Configure UEFI environment",
586         efidebug_help_text
587 );