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