blob: a1e0832bc43efd7a0c7194c906c5f8b42bd72fc3 [file] [log] [blame]
AKASHI Takahiroe7c08832019-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
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 */
34static 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);
102out:
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 */
124static 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 */
164static 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 */
208static 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 */
250static 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 */
296static 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 }
373out:
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 */
393static 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);
419out:
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 */
436static 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);
474out:
475 free(bootorder);
476
477 return ret;
478}
479
480static 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 */
502static 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
520static 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 */
538static 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
566static 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
583U_BOOT_CMD(
584 efidebug, 10, 0, do_efidebug,
585 "Configure UEFI environment",
586 efidebug_help_text
587);