blob: e6e8a0a488e71f472e1b64ad7c00df0440eb2ef9 [file] [log] [blame]
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Menu-driven UEFI Variable maintenance
4 *
5 * Copyright (c) 2022 Masahisa Kojima, Linaro Limited
6 */
7
8#include <ansi.h>
Simon Glass9d8d3872023-01-06 08:52:26 -06009#include <cli.h>
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +090010#include <common.h>
11#include <charset.h>
12#include <efi_loader.h>
13#include <efi_load_initrd.h>
14#include <efi_config.h>
15#include <efi_variable.h>
16#include <log.h>
17#include <malloc.h>
18#include <menu.h>
19#include <sort.h>
20#include <watchdog.h>
21#include <asm/unaligned.h>
22#include <linux/delay.h>
23
24static struct efi_simple_text_input_protocol *cin;
Masahisa Kojimafc811d12023-01-24 15:56:13 +090025const char *eficonfig_menu_desc =
Masahisa Kojima3a9eb072023-02-02 18:24:43 +090026 " Press UP/DOWN to move, ENTER to select, ESC to quit";
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +090027
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +090028static const char *eficonfig_change_boot_order_desc =
29 " Press UP/DOWN to move, +/- to change orde\n"
30 " Press SPACE to activate or deactivate the entry\n"
Masahisa Kojimad38ecb72023-02-02 18:24:44 +090031 " CTRL+S to save, ESC to quit";
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +090032
Masahisa Kojimaa93282c2023-01-24 15:56:15 +090033static struct efi_simple_text_output_protocol *cout;
34static int avail_row;
35
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +090036#define EFICONFIG_DESCRIPTION_MAX 32
37#define EFICONFIG_OPTIONAL_DATA_MAX 64
Masahisa Kojimaa93282c2023-01-24 15:56:15 +090038#define EFICONFIG_MENU_HEADER_ROW_NUM 3
39#define EFICONFIG_MENU_DESC_ROW_NUM 5
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +090040
41/**
42 * struct eficonfig_filepath_info - structure to be used to store file path
43 *
44 * @name: file or directory name
45 * @list: list structure
46 */
47struct eficonfig_filepath_info {
48 char *name;
49 struct list_head list;
50};
51
52/**
53 * struct eficonfig_boot_option - structure to be used for updating UEFI boot option
54 *
55 * @file_info: user selected file info
56 * @initrd_info: user selected initrd file info
57 * @boot_index: index of the boot option
58 * @description: pointer to the description string
59 * @optional_data: pointer to the optional_data
60 * @edit_completed: flag indicates edit complete
61 */
62struct eficonfig_boot_option {
63 struct eficonfig_select_file_info file_info;
64 struct eficonfig_select_file_info initrd_info;
65 unsigned int boot_index;
66 u16 *description;
67 u16 *optional_data;
68 bool edit_completed;
69};
70
71/**
72 * struct eficonfig_volume_entry_data - structure to be used to store volume info
73 *
74 * @file_info: pointer to file info structure
75 * @v: pointer to the protocol interface
76 * @dp: pointer to the device path
77 */
78struct eficonfig_volume_entry_data {
79 struct eficonfig_select_file_info *file_info;
80 struct efi_simple_file_system_protocol *v;
81 struct efi_device_path *dp;
82};
83
84/**
85 * struct eficonfig_file_entry_data - structure to be used to store file info
86 *
87 * @file_info: pointer to file info structure
88 * @is_directory: flag to identify the directory or file
89 * @file_name: name of directory or file
90 */
91struct eficonfig_file_entry_data {
92 struct eficonfig_select_file_info *file_info;
93 bool is_directory;
94 char *file_name;
95};
96
97/**
Masahisa Kojimabb052b92022-09-12 17:33:51 +090098 * struct eficonfig_boot_selection_data - structure to be used to select the boot option entry
99 *
100 * @boot_index: index of the boot option
101 * @selected: pointer to store the selected index in the BootOrder variable
102 */
103struct eficonfig_boot_selection_data {
104 u16 boot_index;
105 int *selected;
106};
107
108/**
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +0900109 * struct eficonfig_boot_order_data - structure to be used to update BootOrder variable
Masahisa Kojimae8753692022-09-12 17:33:56 +0900110 *
Masahisa Kojimae8753692022-09-12 17:33:56 +0900111 * @boot_index: boot option index
112 * @active: flag to include the boot option into BootOrder variable
Masahisa Kojimae8753692022-09-12 17:33:56 +0900113 */
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +0900114struct eficonfig_boot_order_data {
Masahisa Kojimae8753692022-09-12 17:33:56 +0900115 u32 boot_index;
116 bool active;
Masahisa Kojimae8753692022-09-12 17:33:56 +0900117};
118
119/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +0900120 * struct eficonfig_save_boot_order_data - structure to be used to change boot order
121 *
122 * @efi_menu: pointer to efimenu structure
123 * @selected: flag to indicate user selects "Save" entry
124 */
125struct eficonfig_save_boot_order_data {
126 struct efimenu *efi_menu;
127 bool selected;
128};
129
130/**
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900131 * struct eficonfig_menu_adjust - update start and end entry index
132 *
133 * @efi_menu: pointer to efimenu structure
134 * @add: flag to add or substract the index
135 */
136static void eficonfig_menu_adjust(struct efimenu *efi_menu, bool add)
137{
138 if (add)
139 ++efi_menu->active;
140 else
141 --efi_menu->active;
142
143 if (add && efi_menu->end < efi_menu->active) {
144 efi_menu->start++;
145 efi_menu->end++;
146 } else if (!add && efi_menu->start > efi_menu->active) {
147 efi_menu->start--;
148 efi_menu->end--;
149 }
150}
151#define eficonfig_menu_up(_a) eficonfig_menu_adjust(_a, false)
152#define eficonfig_menu_down(_a) eficonfig_menu_adjust(_a, true)
153
154/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900155 * eficonfig_print_msg() - print message
156 *
157 * display the message to the user, user proceeds the screen
158 * with any key press.
159 *
160 * @items: pointer to the structure of each menu entry
161 * @count: the number of menu entry
162 * @menu_header: pointer to the menu header string
163 * Return: status code
164 */
165void eficonfig_print_msg(char *msg)
166{
167 /* Flush input */
168 while (tstc())
169 getchar();
170
171 printf(ANSI_CURSOR_HIDE
172 ANSI_CLEAR_CONSOLE
173 ANSI_CURSOR_POSITION
174 "%s\n\n Press any key to continue", 3, 4, msg);
175
176 getchar();
177}
178
179/**
180 * eficonfig_print_entry() - print each menu entry
181 *
182 * @data: pointer to the data associated with each menu entry
183 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900184void eficonfig_print_entry(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900185{
186 struct eficonfig_entry *entry = data;
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900187 bool reverse = (entry->efi_menu->active == entry->num);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900188
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900189 if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num)
190 return;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900191
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900192 printf(ANSI_CURSOR_POSITION, (entry->num - entry->efi_menu->start) +
193 EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900194
195 if (reverse)
196 puts(ANSI_COLOR_REVERSE);
197
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900198 printf(ANSI_CLEAR_LINE "%s", entry->title);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900199
200 if (reverse)
201 puts(ANSI_COLOR_RESET);
202}
203
204/**
205 * eficonfig_display_statusline() - print status line
206 *
207 * @m: pointer to the menu structure
208 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900209void eficonfig_display_statusline(struct menu *m)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900210{
211 struct eficonfig_entry *entry;
212
213 if (menu_default_choice(m, (void *)&entry) < 0)
214 return;
215
216 printf(ANSI_CURSOR_POSITION
217 "\n%s\n"
218 ANSI_CURSOR_POSITION ANSI_CLEAR_LINE ANSI_CURSOR_POSITION
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900219 "%s"
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +0900220 ANSI_CLEAR_LINE_TO_END,
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900221 1, 1, entry->efi_menu->menu_header, avail_row + 4, 1,
222 avail_row + 5, 1, entry->efi_menu->menu_desc);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900223}
224
225/**
226 * eficonfig_choice_entry() - user key input handler
227 *
228 * @data: pointer to the efimenu structure
229 * Return: key string to identify the selected entry
230 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900231char *eficonfig_choice_entry(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900232{
Simon Glass9d8d3872023-01-06 08:52:26 -0600233 struct cli_ch_state s_cch, *cch = &s_cch;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900234 struct list_head *pos, *n;
235 struct eficonfig_entry *entry;
Simon Glass05ecdf82023-01-06 08:52:22 -0600236 enum bootmenu_key key = BKEY_NONE;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900237 struct efimenu *efi_menu = data;
238
Simon Glass9d8d3872023-01-06 08:52:26 -0600239 cli_ch_init(cch);
240
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900241 while (1) {
Simon Glass9d8d3872023-01-06 08:52:26 -0600242 key = bootmenu_loop((struct bootmenu_data *)efi_menu, cch);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900243
244 switch (key) {
Simon Glass05ecdf82023-01-06 08:52:22 -0600245 case BKEY_UP:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900246 if (efi_menu->active > 0)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900247 eficonfig_menu_up(efi_menu);
248
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900249 /* no menu key selected, regenerate menu */
250 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -0600251 case BKEY_DOWN:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900252 if (efi_menu->active < efi_menu->count - 1)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900253 eficonfig_menu_down(efi_menu);
254
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900255 /* no menu key selected, regenerate menu */
256 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -0600257 case BKEY_SELECT:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900258 list_for_each_safe(pos, n, &efi_menu->list) {
259 entry = list_entry(pos, struct eficonfig_entry, list);
260 if (entry->num == efi_menu->active)
261 return entry->key;
262 }
263 break;
Simon Glass05ecdf82023-01-06 08:52:22 -0600264 case BKEY_QUIT:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900265 /* Quit by choosing the last entry */
266 entry = list_last_entry(&efi_menu->list, struct eficonfig_entry, list);
267 return entry->key;
268 default:
269 /* Pressed key is not valid, no need to regenerate the menu */
270 break;
271 }
272 }
273}
274
275/**
276 * eficonfig_destroy() - destroy efimenu
277 *
278 * @efi_menu: pointer to the efimenu structure
279 */
280void eficonfig_destroy(struct efimenu *efi_menu)
281{
282 struct list_head *pos, *n;
283 struct eficonfig_entry *entry;
284
285 if (!efi_menu)
286 return;
287
288 list_for_each_safe(pos, n, &efi_menu->list) {
289 entry = list_entry(pos, struct eficonfig_entry, list);
290 free(entry->title);
291 list_del(&entry->list);
292 free(entry);
293 }
294 free(efi_menu->menu_header);
295 free(efi_menu);
296}
297
298/**
299 * eficonfig_process_quit() - callback function for "Quit" entry
300 *
301 * @data: pointer to the data
302 * Return: status code
303 */
304efi_status_t eficonfig_process_quit(void *data)
305{
306 return EFI_ABORTED;
307}
308
309/**
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900310 * eficonfig_append_menu_entry() - append menu item
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900311 *
312 * @efi_menu: pointer to the efimenu structure
313 * @title: pointer to the entry title
314 * @func: callback of each entry
315 * @data: pointer to the data to be passed to each entry callback
316 * Return: status code
317 */
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900318efi_status_t eficonfig_append_menu_entry(struct efimenu *efi_menu,
319 char *title, eficonfig_entry_func func,
320 void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900321{
322 struct eficonfig_entry *entry;
323
324 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX)
325 return EFI_OUT_OF_RESOURCES;
326
327 entry = calloc(1, sizeof(struct eficonfig_entry));
328 if (!entry)
329 return EFI_OUT_OF_RESOURCES;
330
331 entry->title = title;
332 sprintf(entry->key, "%d", efi_menu->count);
333 entry->efi_menu = efi_menu;
334 entry->func = func;
335 entry->data = data;
336 entry->num = efi_menu->count++;
337 list_add_tail(&entry->list, &efi_menu->list);
338
339 return EFI_SUCCESS;
340}
341
342/**
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900343 * eficonfig_append_quit_entry() - append quit entry
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900344 *
345 * @efi_menu: pointer to the efimenu structure
346 * Return: status code
347 */
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900348efi_status_t eficonfig_append_quit_entry(struct efimenu *efi_menu)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900349{
350 char *title;
351 efi_status_t ret;
352
353 title = strdup("Quit");
354 if (!title)
355 return EFI_OUT_OF_RESOURCES;
356
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900357 ret = eficonfig_append_menu_entry(efi_menu, title, eficonfig_process_quit, NULL);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900358 if (ret != EFI_SUCCESS)
359 free(title);
360
361 return ret;
362}
363
364/**
365 * eficonfig_create_fixed_menu() - create fixed entry menu structure
366 *
367 * @items: pointer to the menu entry item
368 * @count: the number of menu entry
369 * Return: pointer to the efimenu structure
370 */
371void *eficonfig_create_fixed_menu(const struct eficonfig_item *items, int count)
372{
373 u32 i;
374 char *title;
375 efi_status_t ret;
376 struct efimenu *efi_menu;
377 const struct eficonfig_item *iter = items;
378
379 efi_menu = calloc(1, sizeof(struct efimenu));
380 if (!efi_menu)
381 return NULL;
382
383 INIT_LIST_HEAD(&efi_menu->list);
384 for (i = 0; i < count; i++, iter++) {
385 title = strdup(iter->title);
386 if (!title)
387 goto out;
388
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900389 ret = eficonfig_append_menu_entry(efi_menu, title, iter->func, iter->data);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900390 if (ret != EFI_SUCCESS) {
391 free(title);
392 goto out;
393 }
394 }
395
396 return efi_menu;
397out:
398 eficonfig_destroy(efi_menu);
399
400 return NULL;
401}
402
403/**
404 * eficonfig_process_common() - main handler for UEFI menu
405 *
406 * Construct the structures required to show the menu, then handle
407 * the user input interacting with u-boot menu functions.
408 *
409 * @efi_menu: pointer to the efimenu structure
410 * @menu_header: pointer to the menu header string
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900411 * @menu_desc: pointer to the menu description
412 * @display_statusline: function pointer to draw statusline
413 * @item_data_print: function pointer to draw the menu item
414 * @item_choice: function pointer to handle the key press
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900415 * Return: status code
416 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900417efi_status_t eficonfig_process_common(struct efimenu *efi_menu,
418 char *menu_header, const char *menu_desc,
419 void (*display_statusline)(struct menu *),
420 void (*item_data_print)(void *),
421 char *(*item_choice)(void *))
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900422{
423 struct menu *menu;
424 void *choice = NULL;
425 struct list_head *pos, *n;
426 struct eficonfig_entry *entry;
427 efi_status_t ret = EFI_SUCCESS;
428
429 if (efi_menu->count > EFICONFIG_ENTRY_NUM_MAX)
430 return EFI_OUT_OF_RESOURCES;
431
432 efi_menu->delay = -1;
433 efi_menu->active = 0;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900434 efi_menu->start = 0;
435 efi_menu->end = avail_row - 1;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900436
437 if (menu_header) {
438 efi_menu->menu_header = strdup(menu_header);
439 if (!efi_menu->menu_header)
440 return EFI_OUT_OF_RESOURCES;
441 }
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900442 if (menu_desc)
443 efi_menu->menu_desc = menu_desc;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900444
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900445 menu = menu_create(NULL, 0, 1, display_statusline, item_data_print,
446 item_choice, efi_menu);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900447 if (!menu)
448 return EFI_INVALID_PARAMETER;
449
450 list_for_each_safe(pos, n, &efi_menu->list) {
451 entry = list_entry(pos, struct eficonfig_entry, list);
452 if (!menu_item_add(menu, entry->key, entry)) {
453 ret = EFI_INVALID_PARAMETER;
454 goto out;
455 }
456 }
457
458 entry = list_first_entry_or_null(&efi_menu->list, struct eficonfig_entry, list);
459 if (entry)
460 menu_default_set(menu, entry->key);
461
462 printf(ANSI_CURSOR_HIDE
463 ANSI_CLEAR_CONSOLE
464 ANSI_CURSOR_POSITION, 1, 1);
465
466 if (menu_get_choice(menu, &choice)) {
467 entry = choice;
468 if (entry->func)
469 ret = entry->func(entry->data);
470 }
471out:
472 menu_destroy(menu);
473
474 printf(ANSI_CLEAR_CONSOLE
475 ANSI_CURSOR_POSITION
476 ANSI_CURSOR_SHOW, 1, 1);
477
478 return ret;
479}
480
481/**
482 * eficonfig_volume_selected() - handler of volume selection
483 *
484 * @data: pointer to the data of selected entry
485 * Return: status code
486 */
487static efi_status_t eficonfig_volume_selected(void *data)
488{
489 struct eficonfig_volume_entry_data *info = data;
490
491 if (info) {
492 info->file_info->current_volume = info->v;
493 info->file_info->dp_volume = info->dp;
494 }
495
496 return EFI_SUCCESS;
497}
498
499/**
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900500 * eficonfig_create_device_path() - create device path
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900501 *
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900502 * @dp_volume: pointer to the volume
503 * @current_path: pointer to the file path u16 string
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900504 * Return:
505 * device path or NULL. Caller must free the returned value
506 */
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900507struct efi_device_path *eficonfig_create_device_path(struct efi_device_path *dp_volume,
508 u16 *current_path)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900509{
510 char *p;
511 void *buf;
512 efi_uintn_t fp_size;
513 struct efi_device_path *dp;
514 struct efi_device_path_file_path *fp;
515
Masahisa Kojimaf2d191a2022-12-02 13:59:34 +0900516 fp_size = sizeof(struct efi_device_path) + u16_strsize(current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900517 buf = calloc(1, fp_size + sizeof(END));
518 if (!buf)
519 return NULL;
520
521 fp = buf;
522 fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
523 fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
524 fp->dp.length = (u16)fp_size;
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900525 u16_strcpy(fp->str, current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900526
527 p = buf;
528 p += fp_size;
529 *((struct efi_device_path *)p) = END;
530
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900531 dp = efi_dp_append(dp_volume, (struct efi_device_path *)buf);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900532 free(buf);
533
534 return dp;
535}
536
537/**
538 * eficonfig_file_selected() - handler of file selection
539 *
540 * @data: pointer to the data of selected entry
541 * Return: status code
542 */
543static efi_status_t eficonfig_file_selected(void *data)
544{
545 u16 *tmp;
546 struct eficonfig_file_entry_data *info = data;
547
548 if (!info)
549 return EFI_INVALID_PARAMETER;
550
Masahisa Kojima5d13e9d2022-12-02 13:59:33 +0900551 if (!strcmp(info->file_name, "..\\")) {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900552 struct eficonfig_filepath_info *iter;
553 struct list_head *pos, *n;
554 int is_last;
555 char *filepath;
556 tmp = info->file_info->current_path;
557
558 memset(info->file_info->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE);
559 filepath = calloc(1, EFICONFIG_FILE_PATH_MAX);
560 if (!filepath)
561 return EFI_OUT_OF_RESOURCES;
562
563 list_for_each_safe(pos, n, &info->file_info->filepath_list) {
564 iter = list_entry(pos, struct eficonfig_filepath_info, list);
565
566 is_last = list_is_last(&iter->list, &info->file_info->filepath_list);
567 if (is_last) {
568 list_del(&iter->list);
569 free(iter->name);
570 free(iter);
571 break;
572 }
573 strlcat(filepath, iter->name, EFICONFIG_FILE_PATH_MAX);
574 }
575 utf8_utf16_strcpy(&tmp, filepath);
576 } else {
577 size_t new_len;
578 struct eficonfig_filepath_info *filepath_info;
579
580 new_len = u16_strlen(info->file_info->current_path) +
581 strlen(info->file_name);
582 if (new_len >= EFICONFIG_FILE_PATH_MAX) {
583 eficonfig_print_msg("File path is too long!");
584 return EFI_INVALID_PARAMETER;
585 }
586 tmp = &info->file_info->current_path[u16_strlen(info->file_info->current_path)];
587 utf8_utf16_strcpy(&tmp, info->file_name);
588
589 filepath_info = calloc(1, sizeof(struct eficonfig_filepath_info));
590 if (!filepath_info)
591 return EFI_OUT_OF_RESOURCES;
592
593 filepath_info->name = strdup(info->file_name);
594 if (!filepath_info->name) {
595 free(filepath_info);
596 return EFI_OUT_OF_RESOURCES;
597 }
598 list_add_tail(&filepath_info->list, &info->file_info->filepath_list);
599
600 if (!info->is_directory)
601 info->file_info->file_selected = true;
602 }
603
604 return EFI_SUCCESS;
605}
606
607/**
608 * eficonfig_select_volume() - construct the volume selection menu
609 *
610 * @file_info: pointer to the file selection structure
611 * Return: status code
612 */
613static efi_status_t eficonfig_select_volume(struct eficonfig_select_file_info *file_info)
614{
615 u32 i;
616 efi_status_t ret;
617 efi_uintn_t count;
618 struct efimenu *efi_menu;
619 struct list_head *pos, *n;
620 struct efi_handler *handler;
621 struct eficonfig_entry *entry;
622 struct efi_device_path *device_path;
623 efi_handle_t *volume_handles = NULL;
624 struct efi_simple_file_system_protocol *v;
625
626 ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
627 NULL, &count, (efi_handle_t **)&volume_handles);
628 if (ret != EFI_SUCCESS) {
629 eficonfig_print_msg("No block device found!");
630 return ret;
631 }
632
633 efi_menu = calloc(1, sizeof(struct efimenu));
634 if (!efi_menu)
635 return EFI_OUT_OF_RESOURCES;
636
637 INIT_LIST_HEAD(&efi_menu->list);
638 for (i = 0; i < count; i++) {
639 char *devname;
640 struct efi_block_io *block_io;
641 struct eficonfig_volume_entry_data *info;
642
643 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
644 break;
645
646 ret = efi_search_protocol(volume_handles[i],
647 &efi_simple_file_system_protocol_guid, &handler);
648 if (ret != EFI_SUCCESS)
649 continue;
650 ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL,
651 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
652 if (ret != EFI_SUCCESS)
653 continue;
654
655 ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
656 if (ret != EFI_SUCCESS)
657 continue;
658 ret = efi_protocol_open(handler, (void **)&device_path,
659 efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
660 if (ret != EFI_SUCCESS)
661 continue;
662
663 ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
664 if (ret != EFI_SUCCESS)
665 continue;
666 ret = efi_protocol_open(handler, (void **)&block_io,
667 efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
668 if (ret != EFI_SUCCESS)
669 continue;
670
671 info = calloc(1, sizeof(struct eficonfig_volume_entry_data));
672 if (!info) {
673 ret = EFI_OUT_OF_RESOURCES;
674 goto out;
675 }
676
677 devname = calloc(1, BOOTMENU_DEVICE_NAME_MAX);
678 if (!devname) {
679 free(info);
680 ret = EFI_OUT_OF_RESOURCES;
681 goto out;
682 }
683 ret = efi_disk_get_device_name(volume_handles[i], devname,
684 BOOTMENU_DEVICE_NAME_MAX);
685 if (ret != EFI_SUCCESS) {
686 free(info);
687 goto out;
688 }
689
690 info->v = v;
691 info->dp = device_path;
692 info->file_info = file_info;
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900693 ret = eficonfig_append_menu_entry(efi_menu, devname, eficonfig_volume_selected,
694 info);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900695 if (ret != EFI_SUCCESS) {
696 free(info);
697 goto out;
698 }
699 }
700
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900701 ret = eficonfig_append_quit_entry(efi_menu);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900702 if (ret != EFI_SUCCESS)
703 goto out;
704
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900705 ret = eficonfig_process_common(efi_menu, " ** Select Volume **",
706 eficonfig_menu_desc,
707 eficonfig_display_statusline,
708 eficonfig_print_entry,
709 eficonfig_choice_entry);
710
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900711out:
712 efi_free_pool(volume_handles);
713 list_for_each_safe(pos, n, &efi_menu->list) {
714 entry = list_entry(pos, struct eficonfig_entry, list);
715 free(entry->data);
716 }
717 eficonfig_destroy(efi_menu);
718
719 return ret;
720}
721
722/**
723 * sort_file() - sort the file name in ascii order
724 *
725 * @data1: pointer to the file entry data
726 * @data2: pointer to the file entry data
727 * Return: -1 if the data1 file name is less than data2 file name,
728 * 0 if both file name match,
729 * 1 if the data1 file name is greater thant data2 file name.
730 */
731static int sort_file(const void *arg1, const void *arg2)
732{
733 const struct eficonfig_file_entry_data *data1, *data2;
734
735 data1 = *((const struct eficonfig_file_entry_data **)arg1);
736 data2 = *((const struct eficonfig_file_entry_data **)arg2);
737
738 return strcasecmp(data1->file_name, data2->file_name);
739}
740
741/**
742 * eficonfig_create_file_entry() - construct the file menu entry
743 *
744 * @efi_menu: pointer to the efimenu structure
745 * @count: number of the directory and file
746 * @tmp_infos: pointer to the entry data array
747 * @f: pointer to the file handle
748 * @buf: pointer to the buffer to store the directory information
749 * @file_info: pointer to the file selection structure
750 * Return: status code
751 */
752static efi_status_t
753eficonfig_create_file_entry(struct efimenu *efi_menu, u32 count,
754 struct eficonfig_file_entry_data **tmp_infos,
755 struct efi_file_handle *f, struct efi_file_info *buf,
756 struct eficonfig_select_file_info *file_info)
757{
758 char *name, *p;
759 efi_uintn_t len;
760 efi_status_t ret;
761 u32 i, entry_num = 0;
762 struct eficonfig_file_entry_data *info;
763
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900764 EFI_CALL(f->setpos(f, 0));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900765 /* Read directory and construct menu structure */
766 for (i = 0; i < count; i++) {
767 if (entry_num >= EFICONFIG_ENTRY_NUM_MAX - 1)
768 break;
769
770 len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE;
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900771 ret = EFI_CALL(f->read(f, &len, buf));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900772 if (ret != EFI_SUCCESS || len == 0)
773 break;
774
775 info = calloc(1, sizeof(struct eficonfig_file_entry_data));
776 if (!info) {
777 ret = EFI_OUT_OF_RESOURCES;
778 goto out;
779 }
780
781 /* append '\\' at the end of directory name */
782 name = calloc(1, utf16_utf8_strlen(buf->file_name) + 2);
783 if (!name) {
784 ret = EFI_OUT_OF_RESOURCES;
785 free(info);
786 goto out;
787 }
788 p = name;
789 utf16_utf8_strcpy(&p, buf->file_name);
790 if (buf->attribute & EFI_FILE_DIRECTORY) {
791 /* filter out u'.' */
792 if (!u16_strcmp(buf->file_name, u".")) {
793 free(info);
794 free(name);
795 continue;
796 }
797 name[u16_strlen(buf->file_name)] = '\\';
798 info->is_directory = true;
799 }
800
801 info->file_name = name;
802 info->file_info = file_info;
803 tmp_infos[entry_num++] = info;
804 }
805
806 qsort(tmp_infos, entry_num, sizeof(*tmp_infos),
807 (int (*)(const void *, const void *))sort_file);
808
809 for (i = 0; i < entry_num; i++) {
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900810 ret = eficonfig_append_menu_entry(efi_menu, tmp_infos[i]->file_name,
811 eficonfig_file_selected, tmp_infos[i]);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900812 if (ret != EFI_SUCCESS)
813 goto out;
814 }
815
816out:
817 return ret;
818}
819
820/**
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +0900821 * eficonfig_show_file_selection() - construct the file selection menu
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900822 *
823 * @file_info: pointer to the file selection structure
824 * @root: pointer to the file handle
825 * Return: status code
826 */
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +0900827static efi_status_t eficonfig_show_file_selection(struct eficonfig_select_file_info *file_info,
828 struct efi_file_handle *root)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900829{
830 u32 count = 0, i;
831 efi_uintn_t len;
832 efi_status_t ret;
833 struct efimenu *efi_menu;
834 struct efi_file_handle *f;
835 struct efi_file_info *buf;
836 struct eficonfig_file_entry_data **tmp_infos;
837
838 buf = calloc(1, sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE);
839 if (!buf)
840 return EFI_OUT_OF_RESOURCES;
841
842 while (!file_info->file_selected) {
843 efi_menu = calloc(1, sizeof(struct efimenu));
844 if (!efi_menu) {
845 ret = EFI_OUT_OF_RESOURCES;
846 goto out;
847 }
848 INIT_LIST_HEAD(&efi_menu->list);
849
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900850 ret = EFI_CALL(root->open(root, &f, file_info->current_path,
851 EFI_FILE_MODE_READ, 0));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900852 if (ret != EFI_SUCCESS) {
853 eficonfig_print_msg("Reading volume failed!");
854 free(efi_menu);
855 ret = EFI_ABORTED;
856 goto out;
857 }
858
859 /* Count the number of directory entries */
860 for (;;) {
861 len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE;
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900862 ret = EFI_CALL(f->read(f, &len, buf));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900863 if (ret != EFI_SUCCESS || len == 0)
864 break;
865
866 count++;
867 }
868
869 /* allocate array to sort the entry */
870 tmp_infos = calloc(count, sizeof(*tmp_infos));
871 if (!tmp_infos) {
872 ret = EFI_OUT_OF_RESOURCES;
873 goto err;
874 }
875
876 ret = eficonfig_create_file_entry(efi_menu, count, tmp_infos,
877 f, buf, file_info);
878 if (ret != EFI_SUCCESS)
879 goto err;
880
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900881 ret = eficonfig_append_quit_entry(efi_menu);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900882 if (ret != EFI_SUCCESS)
883 goto err;
884
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900885 ret = eficonfig_process_common(efi_menu, " ** Select File **",
886 eficonfig_menu_desc,
887 eficonfig_display_statusline,
888 eficonfig_print_entry,
889 eficonfig_choice_entry);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900890err:
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900891 EFI_CALL(f->close(f));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900892 eficonfig_destroy(efi_menu);
893
894 if (tmp_infos) {
895 for (i = 0; i < count; i++)
896 free(tmp_infos[i]);
897 }
898
899 free(tmp_infos);
900
901 if (ret != EFI_SUCCESS)
902 break;
903 }
904
905out:
906 free(buf);
907
908 return ret;
909}
910
911/**
912 * handle_user_input() - handle user input
913 *
914 * @buf: pointer to the buffer
915 * @buf_size: size of the buffer
916 * @cursor_col: cursor column for user input
917 * @msg: pointer to the string to display
918 * Return: status code
919 */
920static efi_status_t handle_user_input(u16 *buf, int buf_size,
921 int cursor_col, char *msg)
922{
923 u16 *tmp;
924 efi_status_t ret;
925
926 printf(ANSI_CLEAR_CONSOLE
927 ANSI_CURSOR_POSITION
928 "%s"
929 ANSI_CURSOR_POSITION
Masahisa Kojima3a9eb072023-02-02 18:24:43 +0900930 " Press ENTER to complete, ESC to quit",
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900931 0, 1, msg, 8, 1);
932
933 /* tmp is used to accept user cancel */
934 tmp = calloc(1, buf_size * sizeof(u16));
935 if (!tmp)
936 return EFI_OUT_OF_RESOURCES;
937
938 ret = efi_console_get_u16_string(cin, tmp, buf_size, NULL, 4, cursor_col);
939 if (ret == EFI_SUCCESS)
940 u16_strcpy(buf, tmp);
941
942 free(tmp);
943
944 /* to stay the parent menu */
945 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
946
947 return ret;
948}
949
950/**
951 * eficonfig_boot_add_enter_description() - handle user input for description
952 *
953 * @data: pointer to the internal boot option structure
954 * Return: status code
955 */
956static efi_status_t eficonfig_boot_add_enter_description(void *data)
957{
958 struct eficonfig_boot_option *bo = data;
959
960 return handle_user_input(bo->description, EFICONFIG_DESCRIPTION_MAX, 22,
961 "\n ** Edit Description **\n"
962 "\n"
963 " enter description: ");
964}
965
966/**
967 * eficonfig_boot_add_optional_data() - handle user input for optional data
968 *
969 * @data: pointer to the internal boot option structure
970 * Return: status code
971 */
972static efi_status_t eficonfig_boot_add_optional_data(void *data)
973{
974 struct eficonfig_boot_option *bo = data;
975
976 return handle_user_input(bo->optional_data, EFICONFIG_OPTIONAL_DATA_MAX, 24,
977 "\n ** Edit Optional Data **\n"
978 "\n"
979 " enter optional data:");
980}
981
982/**
983 * eficonfig_boot_edit_save() - handler to save the boot option
984 *
985 * @data: pointer to the internal boot option structure
986 * Return: status code
987 */
988static efi_status_t eficonfig_boot_edit_save(void *data)
989{
990 struct eficonfig_boot_option *bo = data;
991
992 if (u16_strlen(bo->description) == 0) {
993 eficonfig_print_msg("Boot Description is empty!");
994 bo->edit_completed = false;
995 return EFI_NOT_READY;
996 }
997 if (u16_strlen(bo->file_info.current_path) == 0) {
998 eficonfig_print_msg("File is not selected!");
999 bo->edit_completed = false;
1000 return EFI_NOT_READY;
1001 }
1002
1003 bo->edit_completed = true;
1004
1005 return EFI_SUCCESS;
1006}
1007
1008/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001009 * eficonfig_process_clear_file_selection() - callback function for "Clear" entry
1010 *
1011 * @data: pointer to the data
1012 * Return: status code
1013 */
1014efi_status_t eficonfig_process_clear_file_selection(void *data)
1015{
1016 struct eficonfig_select_file_info *file_info = data;
1017
1018 /* clear the existing file information */
1019 file_info->current_volume = NULL;
1020 file_info->current_path[0] = u'\0';
1021 file_info->dp_volume = NULL;
1022
1023 return EFI_ABORTED;
1024}
1025
1026static struct eficonfig_item select_file_menu_items[] = {
1027 {"Select File", eficonfig_process_select_file},
1028 {"Clear", eficonfig_process_clear_file_selection},
1029 {"Quit", eficonfig_process_quit},
1030};
1031
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001032/**
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001033 * eficonfig_process_show_file_option() - display select file option
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001034 *
1035 * @file_info: pointer to the file information structure
1036 * Return: status code
1037 */
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001038efi_status_t eficonfig_process_show_file_option(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001039{
1040 efi_status_t ret;
1041 struct efimenu *efi_menu;
1042
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001043 select_file_menu_items[0].data = data;
1044 select_file_menu_items[1].data = data;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001045 efi_menu = eficonfig_create_fixed_menu(select_file_menu_items,
1046 ARRAY_SIZE(select_file_menu_items));
1047 if (!efi_menu)
1048 return EFI_OUT_OF_RESOURCES;
1049
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001050 ret = eficonfig_process_common(efi_menu, " ** Update File **",
1051 eficonfig_menu_desc,
1052 eficonfig_display_statusline,
1053 eficonfig_print_entry,
1054 eficonfig_choice_entry);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001055 if (ret != EFI_SUCCESS) /* User selects "Clear" or "Quit" */
1056 ret = EFI_NOT_READY;
1057
1058 eficonfig_destroy(efi_menu);
1059
1060 return ret;
1061}
1062
1063/**
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001064 * eficonfig_process_select_file() - handle user file selection
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001065 *
1066 * @data: pointer to the data
1067 * Return: status code
1068 */
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001069efi_status_t eficonfig_process_select_file(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001070{
1071 size_t len;
1072 efi_status_t ret;
1073 struct list_head *pos, *n;
1074 struct efi_file_handle *root;
1075 struct eficonfig_filepath_info *item;
1076 struct eficonfig_select_file_info *tmp = NULL;
1077 struct eficonfig_select_file_info *file_info = data;
1078
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001079 tmp = calloc(1, sizeof(struct eficonfig_select_file_info));
1080 if (!tmp)
1081 return EFI_OUT_OF_RESOURCES;
1082
1083 tmp->current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1084 if (!tmp->current_path) {
1085 free(tmp);
1086 return EFI_OUT_OF_RESOURCES;
1087 }
1088 INIT_LIST_HEAD(&tmp->filepath_list);
1089
1090 while (!tmp->file_selected) {
1091 tmp->current_volume = NULL;
1092 memset(tmp->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE);
1093
1094 ret = eficonfig_select_volume(tmp);
1095 if (ret != EFI_SUCCESS)
1096 goto out;
1097
1098 if (!tmp->current_volume)
1099 return EFI_INVALID_PARAMETER;
1100
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +09001101 ret = EFI_CALL(tmp->current_volume->open_volume(tmp->current_volume, &root));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001102 if (ret != EFI_SUCCESS)
1103 goto out;
1104
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001105 ret = eficonfig_show_file_selection(tmp, root);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001106 if (ret == EFI_ABORTED)
1107 continue;
1108 if (ret != EFI_SUCCESS)
1109 goto out;
1110 }
1111
1112out:
1113 if (ret == EFI_SUCCESS) {
1114 len = u16_strlen(tmp->current_path);
1115 len = (len >= EFICONFIG_FILE_PATH_MAX) ? (EFICONFIG_FILE_PATH_MAX - 1) : len;
1116 memcpy(file_info->current_path, tmp->current_path, len * sizeof(u16));
1117 file_info->current_path[len] = u'\0';
1118 file_info->current_volume = tmp->current_volume;
1119 file_info->dp_volume = tmp->dp_volume;
1120 }
1121
1122 list_for_each_safe(pos, n, &tmp->filepath_list) {
1123 item = list_entry(pos, struct eficonfig_filepath_info, list);
1124 list_del(&item->list);
1125 free(item->name);
1126 free(item);
1127 }
1128 free(tmp->current_path);
1129 free(tmp);
1130
1131 /* to stay the parent menu */
1132 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
1133
1134 return ret;
1135}
1136
1137/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001138 * eficonfig_set_boot_option() - set boot option
1139 *
1140 * @varname: pointer to variable name
1141 * @dp: pointer to device path
1142 * @label: pointer to label string
1143 * @optional_data: pointer to optional data
1144 * Return: status code
1145 */
1146static efi_status_t eficonfig_set_boot_option(u16 *varname, struct efi_device_path *dp,
1147 efi_uintn_t dp_size, u16 *label, char *optional_data)
1148{
1149 void *p = NULL;
1150 efi_status_t ret;
1151 efi_uintn_t size;
1152 struct efi_load_option lo;
1153
1154 lo.file_path = dp;
1155 lo.file_path_length = dp_size;
1156 lo.attributes = LOAD_OPTION_ACTIVE;
1157 lo.optional_data = optional_data;
1158 lo.label = label;
1159
1160 size = efi_serialize_load_option(&lo, (u8 **)&p);
1161 if (!size)
1162 return EFI_INVALID_PARAMETER;
1163
1164 ret = efi_set_variable_int(varname, &efi_global_variable_guid,
1165 EFI_VARIABLE_NON_VOLATILE |
1166 EFI_VARIABLE_BOOTSERVICE_ACCESS |
1167 EFI_VARIABLE_RUNTIME_ACCESS,
1168 size, p, false);
1169 free(p);
1170
1171 return ret;
1172}
1173
1174/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001175 * create_boot_option_entry() - create boot option entry
1176 *
1177 * @efi_menu: pointer to the efimenu structure
1178 * @title: pointer to the entry title
1179 * @val: pointer to boot option label
1180 * @func: callback of each entry
1181 * @data: pointer to the data to be passed to each entry callback
1182 * Return: status code
1183 */
1184static efi_status_t create_boot_option_entry(struct efimenu *efi_menu, char *title, u16 *val,
1185 eficonfig_entry_func func, void *data)
1186{
1187 u32 len;
1188 char *p, *buf;
1189
1190 len = strlen(title) + 1;
1191 if (val)
1192 len += utf16_utf8_strlen(val);
1193 buf = calloc(1, len);
1194 if (!buf)
1195 return EFI_OUT_OF_RESOURCES;
1196
1197 strcpy(buf, title);
1198 if (val) {
1199 p = buf + strlen(title);
1200 utf16_utf8_strcpy(&p, val);
1201 }
1202
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001203 return eficonfig_append_menu_entry(efi_menu, buf, func, data);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001204}
1205
1206/**
1207 * prepare_file_selection_entry() - prepare file selection entry
1208 *
1209 * @efi_menu: pointer to the efimenu structure
1210 * @title: pointer to the title string
1211 * @file_info: pointer to the file info
1212 * Return: status code
1213 */
1214static efi_status_t prepare_file_selection_entry(struct efimenu *efi_menu, char *title,
1215 struct eficonfig_select_file_info *file_info)
1216{
1217 u32 len;
1218 efi_status_t ret;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001219 u16 *file_name = NULL, *p;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001220 efi_handle_t handle;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001221 char *devname;
1222
1223 devname = calloc(1, EFICONFIG_VOLUME_PATH_MAX + 1);
1224 if (!devname)
1225 return EFI_OUT_OF_RESOURCES;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001226
1227 /* get the device name only when the user already selected the file path */
1228 handle = efi_dp_find_obj(file_info->dp_volume, NULL, NULL);
1229 if (handle) {
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001230 ret = efi_disk_get_device_name(handle, devname, EFICONFIG_VOLUME_PATH_MAX);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001231 if (ret != EFI_SUCCESS)
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001232 goto out;
1233 }
1234
1235 /*
1236 * If the preconfigured volume does not exist in the system, display the text
1237 * converted volume device path instead of U-Boot friendly name(e.g. "usb 0:1").
1238 */
1239 if (!handle && file_info->dp_volume) {
1240 u16 *dp_str;
1241 char *q = devname;
1242
1243 dp_str = efi_dp_str(file_info->dp_volume);
1244 if (dp_str)
1245 utf16_utf8_strncpy(&q, dp_str, EFICONFIG_VOLUME_PATH_MAX);
1246
1247 efi_free_pool(dp_str);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001248 }
1249
1250 /* append u'/' to devname, it is just for display purpose. */
1251 if (file_info->current_path[0] != u'\0' && file_info->current_path[0] != u'/')
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001252 strlcat(devname, "/", EFICONFIG_VOLUME_PATH_MAX + 1);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001253
1254 len = strlen(devname);
1255 len += utf16_utf8_strlen(file_info->current_path) + 1;
1256 file_name = calloc(1, len * sizeof(u16));
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001257 if (!file_name) {
1258 ret = EFI_OUT_OF_RESOURCES;
1259 goto out;
1260 }
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001261
1262 p = file_name;
1263 utf8_utf16_strcpy(&p, devname);
1264 u16_strlcat(file_name, file_info->current_path, len);
1265 ret = create_boot_option_entry(efi_menu, title, file_name,
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001266 eficonfig_process_show_file_option, file_info);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001267out:
1268 free(devname);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001269 free(file_name);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001270
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001271 return ret;
1272}
1273
1274/**
1275 * eficonfig_show_boot_option() - prepare menu entry for editing boot option
1276 *
1277 * Construct the structures to create edit boot option menu
1278 *
1279 * @bo: pointer to the boot option
1280 * @header_str: pointer to the header string
1281 * Return: status code
1282 */
1283static efi_status_t eficonfig_show_boot_option(struct eficonfig_boot_option *bo,
1284 char *header_str)
1285{
1286 efi_status_t ret;
1287 struct efimenu *efi_menu;
1288
1289 efi_menu = calloc(1, sizeof(struct efimenu));
1290 if (!efi_menu)
1291 return EFI_OUT_OF_RESOURCES;
1292
1293 INIT_LIST_HEAD(&efi_menu->list);
1294
1295 ret = create_boot_option_entry(efi_menu, "Description: ", bo->description,
1296 eficonfig_boot_add_enter_description, bo);
1297 if (ret != EFI_SUCCESS)
1298 goto out;
1299
1300 ret = prepare_file_selection_entry(efi_menu, "File: ", &bo->file_info);
1301 if (ret != EFI_SUCCESS)
1302 goto out;
1303
1304 ret = prepare_file_selection_entry(efi_menu, "Initrd File: ", &bo->initrd_info);
1305 if (ret != EFI_SUCCESS)
1306 goto out;
1307
1308 ret = create_boot_option_entry(efi_menu, "Optional Data: ", bo->optional_data,
1309 eficonfig_boot_add_optional_data, bo);
1310 if (ret != EFI_SUCCESS)
1311 goto out;
1312
1313 ret = create_boot_option_entry(efi_menu, "Save", NULL,
1314 eficonfig_boot_edit_save, bo);
1315 if (ret != EFI_SUCCESS)
1316 goto out;
1317
1318 ret = create_boot_option_entry(efi_menu, "Quit", NULL,
1319 eficonfig_process_quit, NULL);
1320 if (ret != EFI_SUCCESS)
1321 goto out;
1322
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001323 ret = eficonfig_process_common(efi_menu, header_str,
1324 eficonfig_menu_desc,
1325 eficonfig_display_statusline,
1326 eficonfig_print_entry,
1327 eficonfig_choice_entry);
1328
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001329out:
1330 eficonfig_destroy(efi_menu);
1331
1332 return ret;
1333}
1334
1335/**
1336 * fill_file_info() - fill the file info from efi_device_path structure
1337 *
1338 * @dp: pointer to the device path
1339 * @file_info: pointer to the file info structure
1340 * @device_dp: pointer to the volume device path
1341 */
1342static void fill_file_info(struct efi_device_path *dp,
1343 struct eficonfig_select_file_info *file_info,
1344 struct efi_device_path *device_dp)
1345{
1346 u16 *file_str, *p;
1347 struct efi_device_path *file_dp = NULL;
1348
1349 efi_dp_split_file_path(dp, &device_dp, &file_dp);
1350 file_info->dp_volume = device_dp;
1351
1352 if (file_dp) {
1353 file_str = efi_dp_str(file_dp);
1354 /*
1355 * efi_convert_device_path_to_text() automatically adds u'/' at the
1356 * beginning of file name, remove u'/' before copying to current_path
1357 */
1358 p = file_str;
1359 if (p[0] == u'/')
1360 p++;
1361
1362 u16_strcpy(file_info->current_path, p);
1363 efi_free_pool(file_dp);
1364 efi_free_pool(file_str);
1365 }
1366}
1367
1368/**
1369 * eficonfig_edit_boot_option() - prepare boot option structure for editing
1370 *
1371 * Construct the boot option structure and copy the existing value
1372 *
1373 * @varname: pointer to the UEFI variable name
1374 * @bo: pointer to the boot option
1375 * @load_option: pointer to the load option
1376 * @load_option_size: size of the load option
1377 * @header_str: pointer to the header string
1378 * Return : status code
1379 */
1380static efi_status_t eficonfig_edit_boot_option(u16 *varname, struct eficonfig_boot_option *bo,
1381 void *load_option, efi_uintn_t load_option_size,
1382 char *header_str)
1383{
1384 size_t len;
1385 efi_status_t ret;
1386 char *tmp = NULL, *p;
1387 struct efi_load_option lo = {0};
1388 efi_uintn_t final_dp_size;
1389 struct efi_device_path *dp = NULL;
1390 efi_uintn_t size = load_option_size;
1391 struct efi_device_path *final_dp = NULL;
1392 struct efi_device_path *device_dp = NULL;
1393 struct efi_device_path *initrd_dp = NULL;
1394 struct efi_device_path *initrd_device_dp = NULL;
1395
1396 const struct efi_initrd_dp id_dp = {
1397 .vendor = {
1398 {
1399 DEVICE_PATH_TYPE_MEDIA_DEVICE,
1400 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
1401 sizeof(id_dp.vendor),
1402 },
1403 EFI_INITRD_MEDIA_GUID,
1404 },
1405 .end = {
1406 DEVICE_PATH_TYPE_END,
1407 DEVICE_PATH_SUB_TYPE_END,
1408 sizeof(id_dp.end),
1409 }
1410 };
1411
1412 bo->file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1413 if (!bo->file_info.current_path) {
1414 ret = EFI_OUT_OF_RESOURCES;
1415 goto out;
1416 }
1417
1418 bo->initrd_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1419 if (!bo->file_info.current_path) {
1420 ret = EFI_OUT_OF_RESOURCES;
1421 goto out;
1422 }
1423
1424 bo->description = calloc(1, EFICONFIG_DESCRIPTION_MAX * sizeof(u16));
1425 if (!bo->description) {
1426 ret = EFI_OUT_OF_RESOURCES;
1427 goto out;
1428 }
1429
1430 bo->optional_data = calloc(1, EFICONFIG_OPTIONAL_DATA_MAX * sizeof(u16));
1431 if (!bo->optional_data) {
1432 ret = EFI_OUT_OF_RESOURCES;
1433 goto out;
1434 }
1435
1436 /* copy the preset value */
1437 if (load_option) {
1438 ret = efi_deserialize_load_option(&lo, load_option, &size);
1439 if (ret != EFI_SUCCESS)
1440 goto out;
1441
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001442 if (!lo.label) {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001443 ret = EFI_INVALID_PARAMETER;
1444 goto out;
1445 }
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001446 /* truncate the long label string */
1447 if (u16_strlen(lo.label) >= EFICONFIG_DESCRIPTION_MAX)
1448 lo.label[EFICONFIG_DESCRIPTION_MAX - 1] = u'\0';
1449
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001450 u16_strcpy(bo->description, lo.label);
1451
1452 /* EFI image file path is a first instance */
1453 if (lo.file_path)
1454 fill_file_info(lo.file_path, &bo->file_info, device_dp);
1455
1456 /* Initrd file path(optional) is placed at second instance. */
1457 initrd_dp = efi_dp_from_lo(&lo, &efi_lf2_initrd_guid);
1458 if (initrd_dp) {
1459 fill_file_info(initrd_dp, &bo->initrd_info, initrd_device_dp);
1460 efi_free_pool(initrd_dp);
1461 }
1462
1463 if (size > 0)
1464 memcpy(bo->optional_data, lo.optional_data, size);
1465 }
1466
1467 while (1) {
1468 ret = eficonfig_show_boot_option(bo, header_str);
1469 if (ret == EFI_SUCCESS && bo->edit_completed)
1470 break;
1471 if (ret == EFI_NOT_READY)
1472 continue;
1473 if (ret != EFI_SUCCESS)
1474 goto out;
1475 }
1476
1477 if (bo->initrd_info.dp_volume) {
Masahisa Kojima3360183a2022-11-20 09:21:16 +09001478 dp = eficonfig_create_device_path(bo->initrd_info.dp_volume,
1479 bo->initrd_info.current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001480 if (!dp) {
1481 ret = EFI_OUT_OF_RESOURCES;
1482 goto out;
1483 }
1484 initrd_dp = efi_dp_append((const struct efi_device_path *)&id_dp, dp);
1485 efi_free_pool(dp);
1486 }
1487
Masahisa Kojima3360183a2022-11-20 09:21:16 +09001488 dp = eficonfig_create_device_path(bo->file_info.dp_volume, bo->file_info.current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001489 if (!dp) {
1490 ret = EFI_OUT_OF_RESOURCES;
1491 goto out;
1492 }
1493 final_dp_size = efi_dp_size(dp) + sizeof(END);
1494 if (initrd_dp) {
1495 final_dp = efi_dp_concat(dp, initrd_dp);
1496 final_dp_size += efi_dp_size(initrd_dp) + sizeof(END);
1497 } else {
1498 final_dp = efi_dp_dup(dp);
1499 }
1500 efi_free_pool(dp);
1501
1502 if (!final_dp)
1503 goto out;
1504
1505 if (utf16_utf8_strlen(bo->optional_data)) {
1506 len = utf16_utf8_strlen(bo->optional_data) + 1;
1507 tmp = calloc(1, len);
1508 if (!tmp)
1509 goto out;
1510 p = tmp;
1511 utf16_utf8_strncpy(&p, bo->optional_data, u16_strlen(bo->optional_data));
1512 }
1513
1514 ret = eficonfig_set_boot_option(varname, final_dp, final_dp_size, bo->description, tmp);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001515out:
1516 free(tmp);
1517 free(bo->optional_data);
1518 free(bo->description);
1519 free(bo->file_info.current_path);
1520 free(bo->initrd_info.current_path);
1521 efi_free_pool(device_dp);
1522 efi_free_pool(initrd_device_dp);
1523 efi_free_pool(initrd_dp);
1524 efi_free_pool(final_dp);
1525
1526 return ret;
1527}
1528
1529/**
1530 * eficonfig_process_add_boot_option() - handler to add boot option
1531 *
1532 * @data: pointer to the data for each entry
1533 * Return: status code
1534 */
1535static efi_status_t eficonfig_process_add_boot_option(void *data)
1536{
1537 u16 varname[9];
1538 efi_status_t ret;
1539 struct eficonfig_boot_option *bo = NULL;
1540
1541 bo = calloc(1, sizeof(struct eficonfig_boot_option));
1542 if (!bo)
1543 return EFI_OUT_OF_RESOURCES;
1544
Raymond Mao70a76c52023-06-19 14:22:58 -07001545 ret = efi_bootmgr_get_unused_bootoption(varname, sizeof(varname), &bo->boot_index);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001546 if (ret != EFI_SUCCESS)
1547 return ret;
1548
1549 ret = eficonfig_edit_boot_option(varname, bo, NULL, 0, " ** Add Boot Option ** ");
1550 if (ret != EFI_SUCCESS)
1551 goto out;
1552
Raymond Mao70a76c52023-06-19 14:22:58 -07001553 ret = efi_bootmgr_append_bootorder((u16)bo->boot_index);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001554 if (ret != EFI_SUCCESS)
1555 goto out;
1556
1557out:
1558 free(bo);
1559
1560 /* to stay the parent menu */
1561 ret = (ret == EFI_ABORTED) ? EFI_SUCCESS : ret;
1562
1563 return ret;
1564}
1565
1566/**
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001567 * eficonfig_process_boot_selected() - handler to select boot option entry
1568 *
1569 * @data: pointer to the data for each entry
1570 * Return: status code
1571 */
1572static efi_status_t eficonfig_process_boot_selected(void *data)
1573{
1574 struct eficonfig_boot_selection_data *info = data;
1575
1576 if (info)
1577 *info->selected = info->boot_index;
1578
1579 return EFI_SUCCESS;
1580}
1581
1582/**
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001583 * eficonfig_add_boot_selection_entry() - add boot option menu entry
1584 *
1585 * @efi_menu: pointer to store the efimenu structure
1586 * @boot_index: boot option index to be added
1587 * @selected: pointer to store the selected boot option index
1588 * Return: status code
1589 */
1590static efi_status_t eficonfig_add_boot_selection_entry(struct efimenu *efi_menu,
1591 unsigned int boot_index,
1592 unsigned int *selected)
1593{
1594 char *buf, *p;
1595 efi_status_t ret;
1596 efi_uintn_t size;
1597 void *load_option;
1598 struct efi_load_option lo;
1599 u16 varname[] = u"Boot####";
1600 struct eficonfig_boot_selection_data *info;
1601
1602 efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
1603 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
1604 if (!load_option)
1605 return EFI_SUCCESS;
1606
1607 ret = efi_deserialize_load_option(&lo, load_option, &size);
1608 if (ret != EFI_SUCCESS) {
1609 log_warning("Invalid load option for %ls\n", varname);
1610 free(load_option);
1611 return ret;
1612 }
1613
1614 if (size >= sizeof(efi_guid_t) &&
1615 !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) {
1616 /*
1617 * auto generated entry has GUID in optional_data,
1618 * skip auto generated entry because it will be generated
1619 * again even if it is edited or deleted.
1620 */
1621 free(load_option);
1622 return EFI_SUCCESS;
1623 }
1624
1625 info = calloc(1, sizeof(struct eficonfig_boot_selection_data));
1626 if (!info) {
1627 free(load_option);
1628 return EFI_OUT_OF_RESOURCES;
1629 }
1630
1631 buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
1632 if (!buf) {
1633 free(load_option);
1634 free(info);
1635 return EFI_OUT_OF_RESOURCES;
1636 }
1637 p = buf;
1638 utf16_utf8_strcpy(&p, lo.label);
1639 info->boot_index = boot_index;
1640 info->selected = selected;
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001641 ret = eficonfig_append_menu_entry(efi_menu, buf, eficonfig_process_boot_selected, info);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001642 if (ret != EFI_SUCCESS) {
1643 free(load_option);
1644 free(info);
1645 return ret;
1646 }
1647 free(load_option);
1648
1649 return EFI_SUCCESS;
1650}
1651
1652/**
1653 * eficonfig_show_boot_selection() - construct boot option menu entry
1654 *
1655 * @selected: pointer to store the selected boot option index
1656 * Return: status code
1657 */
1658static efi_status_t eficonfig_show_boot_selection(unsigned int *selected)
1659{
1660 u32 i;
1661 u16 *bootorder;
1662 efi_status_t ret;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001663 u16 *var_name16 = NULL;
Masahisa Kojimad414f572022-12-02 13:59:36 +09001664 efi_uintn_t num, size, buf_size;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001665 struct efimenu *efi_menu;
1666 struct list_head *pos, *n;
1667 struct eficonfig_entry *entry;
1668
1669 efi_menu = calloc(1, sizeof(struct efimenu));
1670 if (!efi_menu)
1671 return EFI_OUT_OF_RESOURCES;
1672
1673 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
1674
1675 INIT_LIST_HEAD(&efi_menu->list);
1676 num = size / sizeof(u16);
1677 /* list the load option in the order of BootOrder variable */
1678 for (i = 0; i < num; i++) {
1679 ret = eficonfig_add_boot_selection_entry(efi_menu, bootorder[i], selected);
1680 if (ret != EFI_SUCCESS)
1681 goto out;
1682
1683 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
1684 break;
1685 }
1686
1687 /* list the remaining load option not included in the BootOrder */
Masahisa Kojimad414f572022-12-02 13:59:36 +09001688 buf_size = 128;
1689 var_name16 = malloc(buf_size);
1690 if (!var_name16)
1691 return EFI_OUT_OF_RESOURCES;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001692
Masahisa Kojimad414f572022-12-02 13:59:36 +09001693 var_name16[0] = 0;
1694 for (;;) {
1695 int index;
1696 efi_guid_t guid;
1697
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001698 ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
Masahisa Kojimad414f572022-12-02 13:59:36 +09001699 if (ret == EFI_NOT_FOUND)
1700 break;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001701 if (ret != EFI_SUCCESS)
1702 goto out;
1703
Masahisa Kojimad414f572022-12-02 13:59:36 +09001704 if (efi_varname_is_load_option(var_name16, &index)) {
1705 /* If the index is included in the BootOrder, skip it */
Raymond Mao70a76c52023-06-19 14:22:58 -07001706 if (efi_search_bootorder(bootorder, num, index, NULL))
Masahisa Kojimad414f572022-12-02 13:59:36 +09001707 continue;
1708
1709 ret = eficonfig_add_boot_selection_entry(efi_menu, index, selected);
1710 if (ret != EFI_SUCCESS)
1711 goto out;
1712 }
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001713
1714 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
1715 break;
1716 }
1717
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001718 ret = eficonfig_append_quit_entry(efi_menu);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001719 if (ret != EFI_SUCCESS)
1720 goto out;
1721
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001722 ret = eficonfig_process_common(efi_menu, " ** Select Boot Option **",
1723 eficonfig_menu_desc,
1724 eficonfig_display_statusline,
1725 eficonfig_print_entry,
1726 eficonfig_choice_entry);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001727out:
1728 list_for_each_safe(pos, n, &efi_menu->list) {
1729 entry = list_entry(pos, struct eficonfig_entry, list);
1730 free(entry->data);
1731 }
1732 eficonfig_destroy(efi_menu);
1733
Masahisa Kojimad414f572022-12-02 13:59:36 +09001734 free(var_name16);
1735
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001736 return ret;
1737}
1738
1739/**
1740 * eficonfig_process_edit_boot_option() - handler to edit boot option
1741 *
1742 * @data: pointer to the data for each entry
1743 * Return: status code
1744 */
1745static efi_status_t eficonfig_process_edit_boot_option(void *data)
1746{
1747 efi_status_t ret;
1748 efi_uintn_t size;
1749 struct eficonfig_boot_option *bo = NULL;
1750
1751 while (1) {
1752 unsigned int selected;
1753 void *load_option;
1754 u16 varname[] = u"Boot####";
1755
1756 ret = eficonfig_show_boot_selection(&selected);
1757 if (ret != EFI_SUCCESS)
1758 break;
1759
1760 bo = calloc(1, sizeof(struct eficonfig_boot_option));
1761 if (!bo) {
1762 ret = EFI_OUT_OF_RESOURCES;
1763 goto out;
1764 }
1765
1766 bo->boot_index = selected;
1767 efi_create_indexed_name(varname, sizeof(varname), "Boot", selected);
1768 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
1769 if (!load_option) {
1770 free(bo);
1771 ret = EFI_NOT_FOUND;
1772 goto out;
1773 }
1774
1775 ret = eficonfig_edit_boot_option(varname, bo, load_option, size,
1776 " ** Edit Boot Option ** ");
1777
1778 free(load_option);
1779 free(bo);
1780 if (ret != EFI_SUCCESS && ret != EFI_ABORTED)
1781 break;
1782 }
1783out:
1784 /* to stay the parent menu */
1785 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
1786
1787 return ret;
1788}
1789
1790/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001791 * eficonfig_print_change_boot_order_entry() - print the boot option entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001792 *
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001793 * @data: pointer to the data associated with each menu entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001794 */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001795static void eficonfig_print_change_boot_order_entry(void *data)
Masahisa Kojimae8753692022-09-12 17:33:56 +09001796{
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001797 struct eficonfig_entry *entry = data;
1798 bool reverse = (entry->efi_menu->active == entry->num);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001799
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001800 if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num)
1801 return;
1802
1803 printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE,
1804 (entry->num - entry->efi_menu->start) + EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001805
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001806 if (reverse)
1807 puts(ANSI_COLOR_REVERSE);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001808
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001809 if (entry->num < entry->efi_menu->count - 2) {
1810 if (((struct eficonfig_boot_order_data *)entry->data)->active)
1811 printf("[*] ");
1812 else
1813 printf("[ ] ");
1814 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09001815
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001816 printf("%s", entry->title);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001817
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001818 if (reverse)
1819 puts(ANSI_COLOR_RESET);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001820}
1821
1822/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001823 * eficonfig_choice_change_boot_order() - user key input handler
Masahisa Kojimae8753692022-09-12 17:33:56 +09001824 *
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001825 * @data: pointer to the menu entry
1826 * Return: key string to identify the selected entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001827 */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001828char *eficonfig_choice_change_boot_order(void *data)
Masahisa Kojimae8753692022-09-12 17:33:56 +09001829{
Simon Glass9d8d3872023-01-06 08:52:26 -06001830 struct cli_ch_state s_cch, *cch = &s_cch;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001831 struct list_head *pos, *n;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001832 struct efimenu *efi_menu = data;
Simon Glass05ecdf82023-01-06 08:52:22 -06001833 enum bootmenu_key key = BKEY_NONE;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001834 struct eficonfig_entry *entry, *tmp;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001835
Simon Glass9d8d3872023-01-06 08:52:26 -06001836 cli_ch_init(cch);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001837 while (1) {
Simon Glass9d8d3872023-01-06 08:52:26 -06001838 key = bootmenu_loop(NULL, cch);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001839
1840 switch (key) {
Simon Glass05ecdf82023-01-06 08:52:22 -06001841 case BKEY_PLUS:
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001842 if (efi_menu->active > 0 &&
1843 efi_menu->active < efi_menu->count - 2) {
Masahisa Kojimae8753692022-09-12 17:33:56 +09001844 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001845 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001846 if (entry->num == efi_menu->active)
1847 break;
1848 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001849 tmp = list_entry(pos->prev, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001850 entry->num--;
1851 tmp->num++;
1852 list_del(&tmp->list);
1853 list_add(&tmp->list, &entry->list);
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001854
1855 eficonfig_menu_up(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001856 }
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001857 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06001858 case BKEY_UP:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001859 if (efi_menu->active > 0)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001860 eficonfig_menu_up(efi_menu);
1861
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001862 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06001863 case BKEY_MINUS:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001864 if (efi_menu->active < efi_menu->count - 3) {
1865 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001866 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001867 if (entry->num == efi_menu->active)
1868 break;
1869 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001870 tmp = list_entry(pos->next, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001871 entry->num++;
1872 tmp->num--;
1873 list_del(&entry->list);
1874 list_add(&entry->list, &tmp->list);
1875
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001876 eficonfig_menu_down(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001877 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001878 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06001879 case BKEY_DOWN:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001880 if (efi_menu->active < efi_menu->count - 1)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001881 eficonfig_menu_down(efi_menu);
1882
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001883 return NULL;
Masahisa Kojimad38ecb72023-02-02 18:24:44 +09001884 case BKEY_SAVE:
1885 /* force to select "Save" entry */
1886 efi_menu->active = efi_menu->count - 2;
1887 fallthrough;
Simon Glass05ecdf82023-01-06 08:52:22 -06001888 case BKEY_SELECT:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001889 /* "Save" */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001890 if (efi_menu->active == efi_menu->count - 2) {
1891 list_for_each_prev_safe(pos, n, &efi_menu->list) {
1892 entry = list_entry(pos, struct eficonfig_entry, list);
1893 if (entry->num == efi_menu->active)
1894 break;
1895 }
1896 return entry->key;
1897 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09001898 /* "Quit" */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001899 if (efi_menu->active == efi_menu->count - 1) {
1900 entry = list_last_entry(&efi_menu->list,
1901 struct eficonfig_entry,
1902 list);
1903 return entry->key;
1904 }
1905 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09001906 break;
Simon Glass05ecdf82023-01-06 08:52:22 -06001907 case BKEY_SPACE:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001908 if (efi_menu->active < efi_menu->count - 2) {
1909 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001910 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001911 if (entry->num == efi_menu->active) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001912 struct eficonfig_boot_order_data *data = entry->data;
1913
1914 data->active = !data->active;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001915 return NULL;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001916 }
1917 }
1918 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001919 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09001920 break;
Simon Glass05ecdf82023-01-06 08:52:22 -06001921 case BKEY_QUIT:
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001922 entry = list_last_entry(&efi_menu->list,
1923 struct eficonfig_entry, list);
1924 return entry->key;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001925 default:
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001926 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09001927 break;
1928 }
1929 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001930}
1931
1932/**
1933 * eficonfig_process_save_boot_order() - callback function for "Save" entry
1934 *
1935 * @data: pointer to the data
1936 * Return: status code
1937 */
1938static efi_status_t eficonfig_process_save_boot_order(void *data)
1939{
1940 u32 count = 0;
1941 efi_status_t ret;
1942 efi_uintn_t size;
1943 struct list_head *pos, *n;
1944 u16 *new_bootorder;
1945 struct efimenu *efi_menu;
1946 struct eficonfig_entry *entry;
1947 struct eficonfig_save_boot_order_data *save_data = data;
1948
1949 efi_menu = save_data->efi_menu;
1950
1951 /*
1952 * The change boot order menu always has "Save" and "Quit" entries.
1953 * !(efi_menu->count - 2) means there is no user defined boot option.
1954 */
1955 if (!(efi_menu->count - 2))
1956 return EFI_SUCCESS;
1957
1958 new_bootorder = calloc(1, (efi_menu->count - 2) * sizeof(u16));
1959 if (!new_bootorder) {
1960 ret = EFI_OUT_OF_RESOURCES;
1961 goto out;
1962 }
1963
1964 /* create new BootOrder */
1965 count = 0;
1966 list_for_each_safe(pos, n, &efi_menu->list) {
1967 struct eficonfig_boot_order_data *data;
1968
1969 entry = list_entry(pos, struct eficonfig_entry, list);
1970 /* exit the loop when iteration reaches "Save" */
1971 if (!strncmp(entry->title, "Save", strlen("Save")))
1972 break;
1973
1974 data = entry->data;
1975 if (data->active)
1976 new_bootorder[count++] = data->boot_index;
1977 }
1978
1979 size = count * sizeof(u16);
1980 ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
1981 EFI_VARIABLE_NON_VOLATILE |
1982 EFI_VARIABLE_BOOTSERVICE_ACCESS |
1983 EFI_VARIABLE_RUNTIME_ACCESS,
1984 size, new_bootorder, false);
1985
1986 save_data->selected = true;
1987out:
1988 free(new_bootorder);
1989
1990 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001991}
1992
1993/**
1994 * eficonfig_add_change_boot_order_entry() - add boot order entry
1995 *
1996 * @efi_menu: pointer to the efimenu structure
1997 * @boot_index: boot option index to be added
1998 * @active: flag to include the boot option into BootOrder
1999 * Return: status code
2000 */
2001static efi_status_t eficonfig_add_change_boot_order_entry(struct efimenu *efi_menu,
2002 u32 boot_index, bool active)
2003{
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002004 char *title, *p;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002005 efi_status_t ret;
2006 efi_uintn_t size;
2007 void *load_option;
2008 struct efi_load_option lo;
2009 u16 varname[] = u"Boot####";
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002010 struct eficonfig_boot_order_data *data;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002011
2012 efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
2013 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
2014 if (!load_option)
2015 return EFI_SUCCESS;
2016
2017 ret = efi_deserialize_load_option(&lo, load_option, &size);
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002018 if (ret != EFI_SUCCESS)
2019 goto out;
2020
2021 data = calloc(1, sizeof(*data));
2022 if (!data) {
2023 ret = EFI_OUT_OF_RESOURCES;
2024 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002025 }
2026
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002027 title = calloc(1, utf16_utf8_strlen(lo.label) + 1);
2028 if (!title) {
2029 free(data);
2030 ret = EFI_OUT_OF_RESOURCES;
2031 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002032 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002033 p = title;
2034 utf16_utf8_strcpy(&p, lo.label);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002035
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002036 data->boot_index = boot_index;
2037 data->active = active;
2038
2039 ret = eficonfig_append_menu_entry(efi_menu, title, NULL, data);
2040 if (ret != EFI_SUCCESS) {
2041 free(data);
2042 free(title);
2043 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002044 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09002045
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002046out:
Masahisa Kojimae8753692022-09-12 17:33:56 +09002047 free(load_option);
2048
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002049 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002050}
2051
2052/**
2053 * eficonfig_create_change_boot_order_entry() - create boot order entry
2054 *
2055 * @efi_menu: pointer to the efimenu structure
2056 * @bootorder: pointer to the BootOrder variable
2057 * @num: number of BootOrder entry
2058 * Return: status code
2059 */
2060static efi_status_t eficonfig_create_change_boot_order_entry(struct efimenu *efi_menu,
2061 u16 *bootorder, efi_uintn_t num)
2062{
2063 u32 i;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002064 char *title;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002065 efi_status_t ret;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002066 u16 *var_name16 = NULL;
Masahisa Kojimad414f572022-12-02 13:59:36 +09002067 efi_uintn_t size, buf_size;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002068 struct eficonfig_save_boot_order_data *save_data;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002069
2070 /* list the load option in the order of BootOrder variable */
2071 for (i = 0; i < num; i++) {
2072 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
2073 break;
2074
2075 ret = eficonfig_add_change_boot_order_entry(efi_menu, bootorder[i], true);
2076 if (ret != EFI_SUCCESS)
2077 goto out;
2078 }
2079
2080 /* list the remaining load option not included in the BootOrder */
Masahisa Kojimad414f572022-12-02 13:59:36 +09002081 buf_size = 128;
2082 var_name16 = malloc(buf_size);
2083 if (!var_name16)
2084 return EFI_OUT_OF_RESOURCES;
2085
2086 var_name16[0] = 0;
2087 for (;;) {
2088 int index;
2089 efi_guid_t guid;
2090
Masahisa Kojimae8753692022-09-12 17:33:56 +09002091 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
2092 break;
2093
Masahisa Kojimad414f572022-12-02 13:59:36 +09002094 size = buf_size;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002095 ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
Masahisa Kojimad414f572022-12-02 13:59:36 +09002096 if (ret == EFI_NOT_FOUND)
2097 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002098 if (ret != EFI_SUCCESS)
2099 goto out;
Masahisa Kojimad414f572022-12-02 13:59:36 +09002100
2101 if (efi_varname_is_load_option(var_name16, &index)) {
2102 /* If the index is included in the BootOrder, skip it */
Raymond Mao70a76c52023-06-19 14:22:58 -07002103 if (efi_search_bootorder(bootorder, num, index, NULL))
Masahisa Kojimad414f572022-12-02 13:59:36 +09002104 continue;
2105
2106 ret = eficonfig_add_change_boot_order_entry(efi_menu, index, false);
2107 if (ret != EFI_SUCCESS)
2108 goto out;
2109 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09002110 }
2111
2112 /* add "Save" and "Quit" entries */
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002113 title = strdup("Save");
2114 if (!title) {
2115 ret = EFI_OUT_OF_RESOURCES;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002116 goto out;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002117 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002118
2119 save_data = malloc(sizeof(struct eficonfig_save_boot_order_data));
2120 if (!save_data) {
2121 ret = EFI_OUT_OF_RESOURCES;
2122 goto out;
2123 }
2124 save_data->efi_menu = efi_menu;
2125 save_data->selected = false;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002126
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002127 ret = eficonfig_append_menu_entry(efi_menu, title,
2128 eficonfig_process_save_boot_order,
2129 save_data);
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002130 if (ret != EFI_SUCCESS)
Masahisa Kojimae8753692022-09-12 17:33:56 +09002131 goto out;
2132
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002133 ret = eficonfig_append_quit_entry(efi_menu);
2134 if (ret != EFI_SUCCESS)
2135 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002136
2137 efi_menu->active = 0;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002138out:
Masahisa Kojimad414f572022-12-02 13:59:36 +09002139 free(var_name16);
2140
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002141 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002142}
2143
2144/**
2145 * eficonfig_process_change_boot_order() - handler to change boot order
2146 *
2147 * @data: pointer to the data for each entry
2148 * Return: status code
2149 */
2150static efi_status_t eficonfig_process_change_boot_order(void *data)
2151{
Masahisa Kojimae8753692022-09-12 17:33:56 +09002152 u16 *bootorder;
2153 efi_status_t ret;
2154 efi_uintn_t num, size;
2155 struct list_head *pos, *n;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002156 struct eficonfig_entry *entry;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002157 struct efimenu *efi_menu;
2158
2159 efi_menu = calloc(1, sizeof(struct efimenu));
2160 if (!efi_menu)
2161 return EFI_OUT_OF_RESOURCES;
2162
2163 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
2164
2165 INIT_LIST_HEAD(&efi_menu->list);
2166 num = size / sizeof(u16);
2167 ret = eficonfig_create_change_boot_order_entry(efi_menu, bootorder, num);
2168 if (ret != EFI_SUCCESS)
2169 goto out;
2170
2171 while (1) {
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002172 ret = eficonfig_process_common(efi_menu,
2173 " ** Change Boot Order **",
2174 eficonfig_change_boot_order_desc,
2175 eficonfig_display_statusline,
2176 eficonfig_print_change_boot_order_entry,
2177 eficonfig_choice_change_boot_order);
2178 /* exit from the menu if user selects the "Save" entry. */
2179 if (ret == EFI_SUCCESS && efi_menu->active == (efi_menu->count - 2)) {
2180 list_for_each_prev_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002181 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002182 if (entry->num == efi_menu->active)
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002183 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002184 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002185 if (((struct eficonfig_save_boot_order_data *)entry->data)->selected)
2186 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002187 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002188 if (ret != EFI_SUCCESS)
2189 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002190 }
2191out:
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002192 free(bootorder);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002193 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002194 entry = list_entry(pos, struct eficonfig_entry, list);
2195 free(entry->data);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002196 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002197 eficonfig_destroy(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002198
2199 /* to stay the parent menu */
2200 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
2201
2202 return ret;
2203}
2204
2205/**
Masahisa Kojima703ad322022-09-12 17:33:53 +09002206 * eficonfig_process_delete_boot_option() - handler to delete boot option
2207 *
2208 * @data: pointer to the data for each entry
2209 * Return: status code
2210 */
2211static efi_status_t eficonfig_process_delete_boot_option(void *data)
2212{
2213 efi_status_t ret;
2214 unsigned int selected;
2215
2216 while (1) {
2217 ret = eficonfig_show_boot_selection(&selected);
2218 if (ret == EFI_SUCCESS)
Raymond Mao70a76c52023-06-19 14:22:58 -07002219 ret = efi_bootmgr_delete_boot_option(selected);
Masahisa Kojima703ad322022-09-12 17:33:53 +09002220
2221 if (ret != EFI_SUCCESS)
2222 break;
2223 }
2224
2225 /* to stay the parent menu */
2226 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
2227
2228 return ret;
2229}
2230
2231/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002232 * eficonfig_init() - do required initialization for eficonfig command
2233 *
2234 * Return: status code
2235 */
2236static efi_status_t eficonfig_init(void)
2237{
2238 efi_status_t ret = EFI_SUCCESS;
2239 static bool init;
2240 struct efi_handler *handler;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002241 unsigned long columns, rows;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002242
2243 if (!init) {
2244 ret = efi_search_protocol(efi_root, &efi_guid_text_input_protocol, &handler);
2245 if (ret != EFI_SUCCESS)
2246 return ret;
2247
2248 ret = efi_protocol_open(handler, (void **)&cin, efi_root, NULL,
2249 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2250 if (ret != EFI_SUCCESS)
2251 return ret;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002252 ret = efi_search_protocol(efi_root, &efi_guid_text_output_protocol, &handler);
2253 if (ret != EFI_SUCCESS)
2254 return ret;
2255
2256 ret = efi_protocol_open(handler, (void **)&cout, efi_root, NULL,
2257 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2258 if (ret != EFI_SUCCESS)
2259 return ret;
2260
2261 cout->query_mode(cout, cout->mode->mode, &columns, &rows);
2262 avail_row = rows - (EFICONFIG_MENU_HEADER_ROW_NUM +
2263 EFICONFIG_MENU_DESC_ROW_NUM);
2264 if (avail_row <= 0) {
2265 eficonfig_print_msg("Console size is too small!");
2266 return EFI_INVALID_PARAMETER;
2267 }
2268 /* TODO: Should we check the minimum column size? */
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002269 }
2270
2271 init = true;
2272
2273 return ret;
2274}
2275
2276static const struct eficonfig_item maintenance_menu_items[] = {
2277 {"Add Boot Option", eficonfig_process_add_boot_option},
Masahisa Kojimabb052b92022-09-12 17:33:51 +09002278 {"Edit Boot Option", eficonfig_process_edit_boot_option},
Masahisa Kojimae8753692022-09-12 17:33:56 +09002279 {"Change Boot Order", eficonfig_process_change_boot_order},
Masahisa Kojima703ad322022-09-12 17:33:53 +09002280 {"Delete Boot Option", eficonfig_process_delete_boot_option},
Simon Glasseba70582023-02-05 15:39:45 -07002281#if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT) && IS_ENABLED(CONFIG_EFI_MM_COMM_TEE))
Masahisa Kojimacbdbcb42022-11-20 09:21:18 +09002282 {"Secure Boot Configuration", eficonfig_process_secure_boot_config},
2283#endif
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002284 {"Quit", eficonfig_process_quit},
2285};
2286
2287/**
2288 * do_eficonfig() - execute `eficonfig` command
2289 *
2290 * @cmdtp: table entry describing command
2291 * @flag: bitmap indicating how the command was invoked
2292 * @argc: number of arguments
2293 * @argv: command line arguments
2294 * Return: status code
2295 */
2296static int do_eficonfig(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
2297{
2298 efi_status_t ret;
2299 struct efimenu *efi_menu;
2300
2301 if (argc > 1)
2302 return CMD_RET_USAGE;
2303
2304 ret = efi_init_obj_list();
2305 if (ret != EFI_SUCCESS) {
2306 log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
2307 ret & ~EFI_ERROR_MASK);
2308
2309 return CMD_RET_FAILURE;
2310 }
2311
2312 ret = eficonfig_init();
2313 if (ret != EFI_SUCCESS)
2314 return CMD_RET_FAILURE;
2315
Raymond Mao70a76c52023-06-19 14:22:58 -07002316 ret = efi_bootmgr_update_media_device_boot_option();
Raymond Maoa35784d2023-06-19 14:22:59 -07002317 if (ret != EFI_SUCCESS)
Masahisa Kojimaf648fa32022-09-12 17:33:55 +09002318 return ret;
2319
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002320 while (1) {
2321 efi_menu = eficonfig_create_fixed_menu(maintenance_menu_items,
2322 ARRAY_SIZE(maintenance_menu_items));
2323 if (!efi_menu)
2324 return CMD_RET_FAILURE;
2325
Masahisa Kojimafc811d12023-01-24 15:56:13 +09002326 ret = eficonfig_process_common(efi_menu,
2327 " ** UEFI Maintenance Menu **",
2328 eficonfig_menu_desc,
2329 eficonfig_display_statusline,
2330 eficonfig_print_entry,
2331 eficonfig_choice_entry);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002332 eficonfig_destroy(efi_menu);
2333
2334 if (ret == EFI_ABORTED)
2335 break;
2336 }
2337
2338 return CMD_RET_SUCCESS;
2339}
2340
2341U_BOOT_CMD(
2342 eficonfig, 1, 0, do_eficonfig,
2343 "provide menu-driven UEFI variable maintenance interface",
2344 ""
2345);