blob: 0a17b8cf343b495fca5313f04f945209287ca326 [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/**
1138 * eficonfig_get_unused_bootoption() - get unused "Boot####" index
1139 *
1140 * @buf: pointer to the buffer to store boot option variable name
1141 * @buf_size: buffer size
1142 * @index: pointer to store the index in the BootOrder variable
1143 * Return: status code
1144 */
1145efi_status_t eficonfig_get_unused_bootoption(u16 *buf, efi_uintn_t buf_size,
1146 unsigned int *index)
1147{
1148 u32 i;
1149 efi_status_t ret;
1150 efi_uintn_t size;
1151
1152 if (buf_size < u16_strsize(u"Boot####"))
1153 return EFI_BUFFER_TOO_SMALL;
1154
1155 for (i = 0; i <= 0xFFFF; i++) {
1156 size = 0;
1157 efi_create_indexed_name(buf, buf_size, "Boot", i);
1158 ret = efi_get_variable_int(buf, &efi_global_variable_guid,
1159 NULL, &size, NULL, NULL);
1160 if (ret == EFI_BUFFER_TOO_SMALL)
1161 continue;
1162 else
1163 break;
1164 }
1165
1166 if (i > 0xFFFF)
1167 return EFI_OUT_OF_RESOURCES;
1168
1169 *index = i;
1170
1171 return EFI_SUCCESS;
1172}
1173
1174/**
1175 * eficonfig_set_boot_option() - set boot option
1176 *
1177 * @varname: pointer to variable name
1178 * @dp: pointer to device path
1179 * @label: pointer to label string
1180 * @optional_data: pointer to optional data
1181 * Return: status code
1182 */
1183static efi_status_t eficonfig_set_boot_option(u16 *varname, struct efi_device_path *dp,
1184 efi_uintn_t dp_size, u16 *label, char *optional_data)
1185{
1186 void *p = NULL;
1187 efi_status_t ret;
1188 efi_uintn_t size;
1189 struct efi_load_option lo;
1190
1191 lo.file_path = dp;
1192 lo.file_path_length = dp_size;
1193 lo.attributes = LOAD_OPTION_ACTIVE;
1194 lo.optional_data = optional_data;
1195 lo.label = label;
1196
1197 size = efi_serialize_load_option(&lo, (u8 **)&p);
1198 if (!size)
1199 return EFI_INVALID_PARAMETER;
1200
1201 ret = efi_set_variable_int(varname, &efi_global_variable_guid,
1202 EFI_VARIABLE_NON_VOLATILE |
1203 EFI_VARIABLE_BOOTSERVICE_ACCESS |
1204 EFI_VARIABLE_RUNTIME_ACCESS,
1205 size, p, false);
1206 free(p);
1207
1208 return ret;
1209}
1210
1211/**
1212 * eficonfig_append_bootorder() - append new boot option in BootOrder variable
1213 *
1214 * @index: "Boot####" index to append to BootOrder variable
1215 * Return: status code
1216 */
1217efi_status_t eficonfig_append_bootorder(u16 index)
1218{
1219 u16 *bootorder;
1220 efi_status_t ret;
1221 u16 *new_bootorder = NULL;
1222 efi_uintn_t last, size, new_size;
1223
1224 /* append new boot option */
1225 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
1226 last = size / sizeof(u16);
1227 new_size = size + sizeof(u16);
1228 new_bootorder = calloc(1, new_size);
1229 if (!new_bootorder) {
1230 ret = EFI_OUT_OF_RESOURCES;
1231 goto out;
1232 }
1233 memcpy(new_bootorder, bootorder, size);
1234 new_bootorder[last] = index;
1235
1236 ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
1237 EFI_VARIABLE_NON_VOLATILE |
1238 EFI_VARIABLE_BOOTSERVICE_ACCESS |
1239 EFI_VARIABLE_RUNTIME_ACCESS,
1240 new_size, new_bootorder, false);
1241 if (ret != EFI_SUCCESS)
1242 goto out;
1243
1244out:
1245 free(bootorder);
1246 free(new_bootorder);
1247
1248 return ret;
1249}
1250
1251/**
1252 * create_boot_option_entry() - create boot option entry
1253 *
1254 * @efi_menu: pointer to the efimenu structure
1255 * @title: pointer to the entry title
1256 * @val: pointer to boot option label
1257 * @func: callback of each entry
1258 * @data: pointer to the data to be passed to each entry callback
1259 * Return: status code
1260 */
1261static efi_status_t create_boot_option_entry(struct efimenu *efi_menu, char *title, u16 *val,
1262 eficonfig_entry_func func, void *data)
1263{
1264 u32 len;
1265 char *p, *buf;
1266
1267 len = strlen(title) + 1;
1268 if (val)
1269 len += utf16_utf8_strlen(val);
1270 buf = calloc(1, len);
1271 if (!buf)
1272 return EFI_OUT_OF_RESOURCES;
1273
1274 strcpy(buf, title);
1275 if (val) {
1276 p = buf + strlen(title);
1277 utf16_utf8_strcpy(&p, val);
1278 }
1279
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001280 return eficonfig_append_menu_entry(efi_menu, buf, func, data);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001281}
1282
1283/**
1284 * prepare_file_selection_entry() - prepare file selection entry
1285 *
1286 * @efi_menu: pointer to the efimenu structure
1287 * @title: pointer to the title string
1288 * @file_info: pointer to the file info
1289 * Return: status code
1290 */
1291static efi_status_t prepare_file_selection_entry(struct efimenu *efi_menu, char *title,
1292 struct eficonfig_select_file_info *file_info)
1293{
1294 u32 len;
1295 efi_status_t ret;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001296 u16 *file_name = NULL, *p;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001297 efi_handle_t handle;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001298 char *devname;
1299
1300 devname = calloc(1, EFICONFIG_VOLUME_PATH_MAX + 1);
1301 if (!devname)
1302 return EFI_OUT_OF_RESOURCES;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001303
1304 /* get the device name only when the user already selected the file path */
1305 handle = efi_dp_find_obj(file_info->dp_volume, NULL, NULL);
1306 if (handle) {
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001307 ret = efi_disk_get_device_name(handle, devname, EFICONFIG_VOLUME_PATH_MAX);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001308 if (ret != EFI_SUCCESS)
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001309 goto out;
1310 }
1311
1312 /*
1313 * If the preconfigured volume does not exist in the system, display the text
1314 * converted volume device path instead of U-Boot friendly name(e.g. "usb 0:1").
1315 */
1316 if (!handle && file_info->dp_volume) {
1317 u16 *dp_str;
1318 char *q = devname;
1319
1320 dp_str = efi_dp_str(file_info->dp_volume);
1321 if (dp_str)
1322 utf16_utf8_strncpy(&q, dp_str, EFICONFIG_VOLUME_PATH_MAX);
1323
1324 efi_free_pool(dp_str);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001325 }
1326
1327 /* append u'/' to devname, it is just for display purpose. */
1328 if (file_info->current_path[0] != u'\0' && file_info->current_path[0] != u'/')
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001329 strlcat(devname, "/", EFICONFIG_VOLUME_PATH_MAX + 1);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001330
1331 len = strlen(devname);
1332 len += utf16_utf8_strlen(file_info->current_path) + 1;
1333 file_name = calloc(1, len * sizeof(u16));
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001334 if (!file_name) {
1335 ret = EFI_OUT_OF_RESOURCES;
1336 goto out;
1337 }
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001338
1339 p = file_name;
1340 utf8_utf16_strcpy(&p, devname);
1341 u16_strlcat(file_name, file_info->current_path, len);
1342 ret = create_boot_option_entry(efi_menu, title, file_name,
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001343 eficonfig_process_show_file_option, file_info);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001344out:
1345 free(devname);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001346 free(file_name);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001347
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001348 return ret;
1349}
1350
1351/**
1352 * eficonfig_show_boot_option() - prepare menu entry for editing boot option
1353 *
1354 * Construct the structures to create edit boot option menu
1355 *
1356 * @bo: pointer to the boot option
1357 * @header_str: pointer to the header string
1358 * Return: status code
1359 */
1360static efi_status_t eficonfig_show_boot_option(struct eficonfig_boot_option *bo,
1361 char *header_str)
1362{
1363 efi_status_t ret;
1364 struct efimenu *efi_menu;
1365
1366 efi_menu = calloc(1, sizeof(struct efimenu));
1367 if (!efi_menu)
1368 return EFI_OUT_OF_RESOURCES;
1369
1370 INIT_LIST_HEAD(&efi_menu->list);
1371
1372 ret = create_boot_option_entry(efi_menu, "Description: ", bo->description,
1373 eficonfig_boot_add_enter_description, bo);
1374 if (ret != EFI_SUCCESS)
1375 goto out;
1376
1377 ret = prepare_file_selection_entry(efi_menu, "File: ", &bo->file_info);
1378 if (ret != EFI_SUCCESS)
1379 goto out;
1380
1381 ret = prepare_file_selection_entry(efi_menu, "Initrd File: ", &bo->initrd_info);
1382 if (ret != EFI_SUCCESS)
1383 goto out;
1384
1385 ret = create_boot_option_entry(efi_menu, "Optional Data: ", bo->optional_data,
1386 eficonfig_boot_add_optional_data, bo);
1387 if (ret != EFI_SUCCESS)
1388 goto out;
1389
1390 ret = create_boot_option_entry(efi_menu, "Save", NULL,
1391 eficonfig_boot_edit_save, bo);
1392 if (ret != EFI_SUCCESS)
1393 goto out;
1394
1395 ret = create_boot_option_entry(efi_menu, "Quit", NULL,
1396 eficonfig_process_quit, NULL);
1397 if (ret != EFI_SUCCESS)
1398 goto out;
1399
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001400 ret = eficonfig_process_common(efi_menu, header_str,
1401 eficonfig_menu_desc,
1402 eficonfig_display_statusline,
1403 eficonfig_print_entry,
1404 eficonfig_choice_entry);
1405
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001406out:
1407 eficonfig_destroy(efi_menu);
1408
1409 return ret;
1410}
1411
1412/**
1413 * fill_file_info() - fill the file info from efi_device_path structure
1414 *
1415 * @dp: pointer to the device path
1416 * @file_info: pointer to the file info structure
1417 * @device_dp: pointer to the volume device path
1418 */
1419static void fill_file_info(struct efi_device_path *dp,
1420 struct eficonfig_select_file_info *file_info,
1421 struct efi_device_path *device_dp)
1422{
1423 u16 *file_str, *p;
1424 struct efi_device_path *file_dp = NULL;
1425
1426 efi_dp_split_file_path(dp, &device_dp, &file_dp);
1427 file_info->dp_volume = device_dp;
1428
1429 if (file_dp) {
1430 file_str = efi_dp_str(file_dp);
1431 /*
1432 * efi_convert_device_path_to_text() automatically adds u'/' at the
1433 * beginning of file name, remove u'/' before copying to current_path
1434 */
1435 p = file_str;
1436 if (p[0] == u'/')
1437 p++;
1438
1439 u16_strcpy(file_info->current_path, p);
1440 efi_free_pool(file_dp);
1441 efi_free_pool(file_str);
1442 }
1443}
1444
1445/**
1446 * eficonfig_edit_boot_option() - prepare boot option structure for editing
1447 *
1448 * Construct the boot option structure and copy the existing value
1449 *
1450 * @varname: pointer to the UEFI variable name
1451 * @bo: pointer to the boot option
1452 * @load_option: pointer to the load option
1453 * @load_option_size: size of the load option
1454 * @header_str: pointer to the header string
1455 * Return : status code
1456 */
1457static efi_status_t eficonfig_edit_boot_option(u16 *varname, struct eficonfig_boot_option *bo,
1458 void *load_option, efi_uintn_t load_option_size,
1459 char *header_str)
1460{
1461 size_t len;
1462 efi_status_t ret;
1463 char *tmp = NULL, *p;
1464 struct efi_load_option lo = {0};
1465 efi_uintn_t final_dp_size;
1466 struct efi_device_path *dp = NULL;
1467 efi_uintn_t size = load_option_size;
1468 struct efi_device_path *final_dp = NULL;
1469 struct efi_device_path *device_dp = NULL;
1470 struct efi_device_path *initrd_dp = NULL;
1471 struct efi_device_path *initrd_device_dp = NULL;
1472
1473 const struct efi_initrd_dp id_dp = {
1474 .vendor = {
1475 {
1476 DEVICE_PATH_TYPE_MEDIA_DEVICE,
1477 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
1478 sizeof(id_dp.vendor),
1479 },
1480 EFI_INITRD_MEDIA_GUID,
1481 },
1482 .end = {
1483 DEVICE_PATH_TYPE_END,
1484 DEVICE_PATH_SUB_TYPE_END,
1485 sizeof(id_dp.end),
1486 }
1487 };
1488
1489 bo->file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1490 if (!bo->file_info.current_path) {
1491 ret = EFI_OUT_OF_RESOURCES;
1492 goto out;
1493 }
1494
1495 bo->initrd_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1496 if (!bo->file_info.current_path) {
1497 ret = EFI_OUT_OF_RESOURCES;
1498 goto out;
1499 }
1500
1501 bo->description = calloc(1, EFICONFIG_DESCRIPTION_MAX * sizeof(u16));
1502 if (!bo->description) {
1503 ret = EFI_OUT_OF_RESOURCES;
1504 goto out;
1505 }
1506
1507 bo->optional_data = calloc(1, EFICONFIG_OPTIONAL_DATA_MAX * sizeof(u16));
1508 if (!bo->optional_data) {
1509 ret = EFI_OUT_OF_RESOURCES;
1510 goto out;
1511 }
1512
1513 /* copy the preset value */
1514 if (load_option) {
1515 ret = efi_deserialize_load_option(&lo, load_option, &size);
1516 if (ret != EFI_SUCCESS)
1517 goto out;
1518
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001519 if (!lo.label) {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001520 ret = EFI_INVALID_PARAMETER;
1521 goto out;
1522 }
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001523 /* truncate the long label string */
1524 if (u16_strlen(lo.label) >= EFICONFIG_DESCRIPTION_MAX)
1525 lo.label[EFICONFIG_DESCRIPTION_MAX - 1] = u'\0';
1526
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001527 u16_strcpy(bo->description, lo.label);
1528
1529 /* EFI image file path is a first instance */
1530 if (lo.file_path)
1531 fill_file_info(lo.file_path, &bo->file_info, device_dp);
1532
1533 /* Initrd file path(optional) is placed at second instance. */
1534 initrd_dp = efi_dp_from_lo(&lo, &efi_lf2_initrd_guid);
1535 if (initrd_dp) {
1536 fill_file_info(initrd_dp, &bo->initrd_info, initrd_device_dp);
1537 efi_free_pool(initrd_dp);
1538 }
1539
1540 if (size > 0)
1541 memcpy(bo->optional_data, lo.optional_data, size);
1542 }
1543
1544 while (1) {
1545 ret = eficonfig_show_boot_option(bo, header_str);
1546 if (ret == EFI_SUCCESS && bo->edit_completed)
1547 break;
1548 if (ret == EFI_NOT_READY)
1549 continue;
1550 if (ret != EFI_SUCCESS)
1551 goto out;
1552 }
1553
1554 if (bo->initrd_info.dp_volume) {
Masahisa Kojima3360183a2022-11-20 09:21:16 +09001555 dp = eficonfig_create_device_path(bo->initrd_info.dp_volume,
1556 bo->initrd_info.current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001557 if (!dp) {
1558 ret = EFI_OUT_OF_RESOURCES;
1559 goto out;
1560 }
1561 initrd_dp = efi_dp_append((const struct efi_device_path *)&id_dp, dp);
1562 efi_free_pool(dp);
1563 }
1564
Masahisa Kojima3360183a2022-11-20 09:21:16 +09001565 dp = eficonfig_create_device_path(bo->file_info.dp_volume, bo->file_info.current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001566 if (!dp) {
1567 ret = EFI_OUT_OF_RESOURCES;
1568 goto out;
1569 }
1570 final_dp_size = efi_dp_size(dp) + sizeof(END);
1571 if (initrd_dp) {
1572 final_dp = efi_dp_concat(dp, initrd_dp);
1573 final_dp_size += efi_dp_size(initrd_dp) + sizeof(END);
1574 } else {
1575 final_dp = efi_dp_dup(dp);
1576 }
1577 efi_free_pool(dp);
1578
1579 if (!final_dp)
1580 goto out;
1581
1582 if (utf16_utf8_strlen(bo->optional_data)) {
1583 len = utf16_utf8_strlen(bo->optional_data) + 1;
1584 tmp = calloc(1, len);
1585 if (!tmp)
1586 goto out;
1587 p = tmp;
1588 utf16_utf8_strncpy(&p, bo->optional_data, u16_strlen(bo->optional_data));
1589 }
1590
1591 ret = eficonfig_set_boot_option(varname, final_dp, final_dp_size, bo->description, tmp);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001592out:
1593 free(tmp);
1594 free(bo->optional_data);
1595 free(bo->description);
1596 free(bo->file_info.current_path);
1597 free(bo->initrd_info.current_path);
1598 efi_free_pool(device_dp);
1599 efi_free_pool(initrd_device_dp);
1600 efi_free_pool(initrd_dp);
1601 efi_free_pool(final_dp);
1602
1603 return ret;
1604}
1605
1606/**
1607 * eficonfig_process_add_boot_option() - handler to add boot option
1608 *
1609 * @data: pointer to the data for each entry
1610 * Return: status code
1611 */
1612static efi_status_t eficonfig_process_add_boot_option(void *data)
1613{
1614 u16 varname[9];
1615 efi_status_t ret;
1616 struct eficonfig_boot_option *bo = NULL;
1617
1618 bo = calloc(1, sizeof(struct eficonfig_boot_option));
1619 if (!bo)
1620 return EFI_OUT_OF_RESOURCES;
1621
1622 ret = eficonfig_get_unused_bootoption(varname, sizeof(varname), &bo->boot_index);
1623 if (ret != EFI_SUCCESS)
1624 return ret;
1625
1626 ret = eficonfig_edit_boot_option(varname, bo, NULL, 0, " ** Add Boot Option ** ");
1627 if (ret != EFI_SUCCESS)
1628 goto out;
1629
1630 ret = eficonfig_append_bootorder((u16)bo->boot_index);
1631 if (ret != EFI_SUCCESS)
1632 goto out;
1633
1634out:
1635 free(bo);
1636
1637 /* to stay the parent menu */
1638 ret = (ret == EFI_ABORTED) ? EFI_SUCCESS : ret;
1639
1640 return ret;
1641}
1642
1643/**
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001644 * eficonfig_process_boot_selected() - handler to select boot option entry
1645 *
1646 * @data: pointer to the data for each entry
1647 * Return: status code
1648 */
1649static efi_status_t eficonfig_process_boot_selected(void *data)
1650{
1651 struct eficonfig_boot_selection_data *info = data;
1652
1653 if (info)
1654 *info->selected = info->boot_index;
1655
1656 return EFI_SUCCESS;
1657}
1658
1659/**
1660 * search_bootorder() - search the boot option index in BootOrder
1661 *
1662 * @bootorder: pointer to the BootOrder variable
1663 * @num: number of BootOrder entry
1664 * @target: target boot option index to search
1665 * @index: pointer to store the index of BootOrder variable
1666 * Return: true if exists, false otherwise
1667 */
1668static bool search_bootorder(u16 *bootorder, efi_uintn_t num, u32 target, u32 *index)
1669{
1670 u32 i;
1671
1672 for (i = 0; i < num; i++) {
1673 if (target == bootorder[i]) {
1674 if (index)
1675 *index = i;
1676
1677 return true;
1678 }
1679 }
1680
1681 return false;
1682}
1683
1684/**
1685 * eficonfig_add_boot_selection_entry() - add boot option menu entry
1686 *
1687 * @efi_menu: pointer to store the efimenu structure
1688 * @boot_index: boot option index to be added
1689 * @selected: pointer to store the selected boot option index
1690 * Return: status code
1691 */
1692static efi_status_t eficonfig_add_boot_selection_entry(struct efimenu *efi_menu,
1693 unsigned int boot_index,
1694 unsigned int *selected)
1695{
1696 char *buf, *p;
1697 efi_status_t ret;
1698 efi_uintn_t size;
1699 void *load_option;
1700 struct efi_load_option lo;
1701 u16 varname[] = u"Boot####";
1702 struct eficonfig_boot_selection_data *info;
1703
1704 efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
1705 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
1706 if (!load_option)
1707 return EFI_SUCCESS;
1708
1709 ret = efi_deserialize_load_option(&lo, load_option, &size);
1710 if (ret != EFI_SUCCESS) {
1711 log_warning("Invalid load option for %ls\n", varname);
1712 free(load_option);
1713 return ret;
1714 }
1715
1716 if (size >= sizeof(efi_guid_t) &&
1717 !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) {
1718 /*
1719 * auto generated entry has GUID in optional_data,
1720 * skip auto generated entry because it will be generated
1721 * again even if it is edited or deleted.
1722 */
1723 free(load_option);
1724 return EFI_SUCCESS;
1725 }
1726
1727 info = calloc(1, sizeof(struct eficonfig_boot_selection_data));
1728 if (!info) {
1729 free(load_option);
1730 return EFI_OUT_OF_RESOURCES;
1731 }
1732
1733 buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
1734 if (!buf) {
1735 free(load_option);
1736 free(info);
1737 return EFI_OUT_OF_RESOURCES;
1738 }
1739 p = buf;
1740 utf16_utf8_strcpy(&p, lo.label);
1741 info->boot_index = boot_index;
1742 info->selected = selected;
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001743 ret = eficonfig_append_menu_entry(efi_menu, buf, eficonfig_process_boot_selected, info);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001744 if (ret != EFI_SUCCESS) {
1745 free(load_option);
1746 free(info);
1747 return ret;
1748 }
1749 free(load_option);
1750
1751 return EFI_SUCCESS;
1752}
1753
1754/**
1755 * eficonfig_show_boot_selection() - construct boot option menu entry
1756 *
1757 * @selected: pointer to store the selected boot option index
1758 * Return: status code
1759 */
1760static efi_status_t eficonfig_show_boot_selection(unsigned int *selected)
1761{
1762 u32 i;
1763 u16 *bootorder;
1764 efi_status_t ret;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001765 u16 *var_name16 = NULL;
Masahisa Kojimad414f572022-12-02 13:59:36 +09001766 efi_uintn_t num, size, buf_size;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001767 struct efimenu *efi_menu;
1768 struct list_head *pos, *n;
1769 struct eficonfig_entry *entry;
1770
1771 efi_menu = calloc(1, sizeof(struct efimenu));
1772 if (!efi_menu)
1773 return EFI_OUT_OF_RESOURCES;
1774
1775 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
1776
1777 INIT_LIST_HEAD(&efi_menu->list);
1778 num = size / sizeof(u16);
1779 /* list the load option in the order of BootOrder variable */
1780 for (i = 0; i < num; i++) {
1781 ret = eficonfig_add_boot_selection_entry(efi_menu, bootorder[i], selected);
1782 if (ret != EFI_SUCCESS)
1783 goto out;
1784
1785 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
1786 break;
1787 }
1788
1789 /* list the remaining load option not included in the BootOrder */
Masahisa Kojimad414f572022-12-02 13:59:36 +09001790 buf_size = 128;
1791 var_name16 = malloc(buf_size);
1792 if (!var_name16)
1793 return EFI_OUT_OF_RESOURCES;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001794
Masahisa Kojimad414f572022-12-02 13:59:36 +09001795 var_name16[0] = 0;
1796 for (;;) {
1797 int index;
1798 efi_guid_t guid;
1799
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001800 ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
Masahisa Kojimad414f572022-12-02 13:59:36 +09001801 if (ret == EFI_NOT_FOUND)
1802 break;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001803 if (ret != EFI_SUCCESS)
1804 goto out;
1805
Masahisa Kojimad414f572022-12-02 13:59:36 +09001806 if (efi_varname_is_load_option(var_name16, &index)) {
1807 /* If the index is included in the BootOrder, skip it */
1808 if (search_bootorder(bootorder, num, index, NULL))
1809 continue;
1810
1811 ret = eficonfig_add_boot_selection_entry(efi_menu, index, selected);
1812 if (ret != EFI_SUCCESS)
1813 goto out;
1814 }
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001815
1816 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
1817 break;
1818 }
1819
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001820 ret = eficonfig_append_quit_entry(efi_menu);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001821 if (ret != EFI_SUCCESS)
1822 goto out;
1823
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001824 ret = eficonfig_process_common(efi_menu, " ** Select Boot Option **",
1825 eficonfig_menu_desc,
1826 eficonfig_display_statusline,
1827 eficonfig_print_entry,
1828 eficonfig_choice_entry);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001829out:
1830 list_for_each_safe(pos, n, &efi_menu->list) {
1831 entry = list_entry(pos, struct eficonfig_entry, list);
1832 free(entry->data);
1833 }
1834 eficonfig_destroy(efi_menu);
1835
Masahisa Kojimad414f572022-12-02 13:59:36 +09001836 free(var_name16);
1837
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001838 return ret;
1839}
1840
1841/**
1842 * eficonfig_process_edit_boot_option() - handler to edit boot option
1843 *
1844 * @data: pointer to the data for each entry
1845 * Return: status code
1846 */
1847static efi_status_t eficonfig_process_edit_boot_option(void *data)
1848{
1849 efi_status_t ret;
1850 efi_uintn_t size;
1851 struct eficonfig_boot_option *bo = NULL;
1852
1853 while (1) {
1854 unsigned int selected;
1855 void *load_option;
1856 u16 varname[] = u"Boot####";
1857
1858 ret = eficonfig_show_boot_selection(&selected);
1859 if (ret != EFI_SUCCESS)
1860 break;
1861
1862 bo = calloc(1, sizeof(struct eficonfig_boot_option));
1863 if (!bo) {
1864 ret = EFI_OUT_OF_RESOURCES;
1865 goto out;
1866 }
1867
1868 bo->boot_index = selected;
1869 efi_create_indexed_name(varname, sizeof(varname), "Boot", selected);
1870 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
1871 if (!load_option) {
1872 free(bo);
1873 ret = EFI_NOT_FOUND;
1874 goto out;
1875 }
1876
1877 ret = eficonfig_edit_boot_option(varname, bo, load_option, size,
1878 " ** Edit Boot Option ** ");
1879
1880 free(load_option);
1881 free(bo);
1882 if (ret != EFI_SUCCESS && ret != EFI_ABORTED)
1883 break;
1884 }
1885out:
1886 /* to stay the parent menu */
1887 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
1888
1889 return ret;
1890}
1891
1892/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001893 * eficonfig_print_change_boot_order_entry() - print the boot option entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001894 *
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001895 * @data: pointer to the data associated with each menu entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001896 */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001897static void eficonfig_print_change_boot_order_entry(void *data)
Masahisa Kojimae8753692022-09-12 17:33:56 +09001898{
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001899 struct eficonfig_entry *entry = data;
1900 bool reverse = (entry->efi_menu->active == entry->num);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001901
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001902 if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num)
1903 return;
1904
1905 printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE,
1906 (entry->num - entry->efi_menu->start) + EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001907
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001908 if (reverse)
1909 puts(ANSI_COLOR_REVERSE);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001910
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001911 if (entry->num < entry->efi_menu->count - 2) {
1912 if (((struct eficonfig_boot_order_data *)entry->data)->active)
1913 printf("[*] ");
1914 else
1915 printf("[ ] ");
1916 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09001917
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001918 printf("%s", entry->title);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001919
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001920 if (reverse)
1921 puts(ANSI_COLOR_RESET);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001922}
1923
1924/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001925 * eficonfig_choice_change_boot_order() - user key input handler
Masahisa Kojimae8753692022-09-12 17:33:56 +09001926 *
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001927 * @data: pointer to the menu entry
1928 * Return: key string to identify the selected entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001929 */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001930char *eficonfig_choice_change_boot_order(void *data)
Masahisa Kojimae8753692022-09-12 17:33:56 +09001931{
Simon Glass9d8d3872023-01-06 08:52:26 -06001932 struct cli_ch_state s_cch, *cch = &s_cch;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001933 struct list_head *pos, *n;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001934 struct efimenu *efi_menu = data;
Simon Glass05ecdf82023-01-06 08:52:22 -06001935 enum bootmenu_key key = BKEY_NONE;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001936 struct eficonfig_entry *entry, *tmp;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001937
Simon Glass9d8d3872023-01-06 08:52:26 -06001938 cli_ch_init(cch);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001939 while (1) {
Simon Glass9d8d3872023-01-06 08:52:26 -06001940 key = bootmenu_loop(NULL, cch);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001941
1942 switch (key) {
Simon Glass05ecdf82023-01-06 08:52:22 -06001943 case BKEY_PLUS:
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001944 if (efi_menu->active > 0 &&
1945 efi_menu->active < efi_menu->count - 2) {
Masahisa Kojimae8753692022-09-12 17:33:56 +09001946 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001947 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001948 if (entry->num == efi_menu->active)
1949 break;
1950 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001951 tmp = list_entry(pos->prev, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001952 entry->num--;
1953 tmp->num++;
1954 list_del(&tmp->list);
1955 list_add(&tmp->list, &entry->list);
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001956
1957 eficonfig_menu_up(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001958 }
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001959 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06001960 case BKEY_UP:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001961 if (efi_menu->active > 0)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001962 eficonfig_menu_up(efi_menu);
1963
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001964 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06001965 case BKEY_MINUS:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001966 if (efi_menu->active < efi_menu->count - 3) {
1967 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001968 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001969 if (entry->num == efi_menu->active)
1970 break;
1971 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001972 tmp = list_entry(pos->next, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001973 entry->num++;
1974 tmp->num--;
1975 list_del(&entry->list);
1976 list_add(&entry->list, &tmp->list);
1977
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001978 eficonfig_menu_down(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001979 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001980 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06001981 case BKEY_DOWN:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001982 if (efi_menu->active < efi_menu->count - 1)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001983 eficonfig_menu_down(efi_menu);
1984
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001985 return NULL;
Masahisa Kojimad38ecb72023-02-02 18:24:44 +09001986 case BKEY_SAVE:
1987 /* force to select "Save" entry */
1988 efi_menu->active = efi_menu->count - 2;
1989 fallthrough;
Simon Glass05ecdf82023-01-06 08:52:22 -06001990 case BKEY_SELECT:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001991 /* "Save" */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001992 if (efi_menu->active == efi_menu->count - 2) {
1993 list_for_each_prev_safe(pos, n, &efi_menu->list) {
1994 entry = list_entry(pos, struct eficonfig_entry, list);
1995 if (entry->num == efi_menu->active)
1996 break;
1997 }
1998 return entry->key;
1999 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09002000 /* "Quit" */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002001 if (efi_menu->active == efi_menu->count - 1) {
2002 entry = list_last_entry(&efi_menu->list,
2003 struct eficonfig_entry,
2004 list);
2005 return entry->key;
2006 }
2007 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09002008 break;
Simon Glass05ecdf82023-01-06 08:52:22 -06002009 case BKEY_SPACE:
Masahisa Kojimae8753692022-09-12 17:33:56 +09002010 if (efi_menu->active < efi_menu->count - 2) {
2011 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002012 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002013 if (entry->num == efi_menu->active) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002014 struct eficonfig_boot_order_data *data = entry->data;
2015
2016 data->active = !data->active;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002017 return NULL;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002018 }
2019 }
2020 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002021 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09002022 break;
Simon Glass05ecdf82023-01-06 08:52:22 -06002023 case BKEY_QUIT:
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002024 entry = list_last_entry(&efi_menu->list,
2025 struct eficonfig_entry, list);
2026 return entry->key;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002027 default:
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002028 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09002029 break;
2030 }
2031 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002032}
2033
2034/**
2035 * eficonfig_process_save_boot_order() - callback function for "Save" entry
2036 *
2037 * @data: pointer to the data
2038 * Return: status code
2039 */
2040static efi_status_t eficonfig_process_save_boot_order(void *data)
2041{
2042 u32 count = 0;
2043 efi_status_t ret;
2044 efi_uintn_t size;
2045 struct list_head *pos, *n;
2046 u16 *new_bootorder;
2047 struct efimenu *efi_menu;
2048 struct eficonfig_entry *entry;
2049 struct eficonfig_save_boot_order_data *save_data = data;
2050
2051 efi_menu = save_data->efi_menu;
2052
2053 /*
2054 * The change boot order menu always has "Save" and "Quit" entries.
2055 * !(efi_menu->count - 2) means there is no user defined boot option.
2056 */
2057 if (!(efi_menu->count - 2))
2058 return EFI_SUCCESS;
2059
2060 new_bootorder = calloc(1, (efi_menu->count - 2) * sizeof(u16));
2061 if (!new_bootorder) {
2062 ret = EFI_OUT_OF_RESOURCES;
2063 goto out;
2064 }
2065
2066 /* create new BootOrder */
2067 count = 0;
2068 list_for_each_safe(pos, n, &efi_menu->list) {
2069 struct eficonfig_boot_order_data *data;
2070
2071 entry = list_entry(pos, struct eficonfig_entry, list);
2072 /* exit the loop when iteration reaches "Save" */
2073 if (!strncmp(entry->title, "Save", strlen("Save")))
2074 break;
2075
2076 data = entry->data;
2077 if (data->active)
2078 new_bootorder[count++] = data->boot_index;
2079 }
2080
2081 size = count * sizeof(u16);
2082 ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
2083 EFI_VARIABLE_NON_VOLATILE |
2084 EFI_VARIABLE_BOOTSERVICE_ACCESS |
2085 EFI_VARIABLE_RUNTIME_ACCESS,
2086 size, new_bootorder, false);
2087
2088 save_data->selected = true;
2089out:
2090 free(new_bootorder);
2091
2092 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002093}
2094
2095/**
2096 * eficonfig_add_change_boot_order_entry() - add boot order entry
2097 *
2098 * @efi_menu: pointer to the efimenu structure
2099 * @boot_index: boot option index to be added
2100 * @active: flag to include the boot option into BootOrder
2101 * Return: status code
2102 */
2103static efi_status_t eficonfig_add_change_boot_order_entry(struct efimenu *efi_menu,
2104 u32 boot_index, bool active)
2105{
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002106 char *title, *p;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002107 efi_status_t ret;
2108 efi_uintn_t size;
2109 void *load_option;
2110 struct efi_load_option lo;
2111 u16 varname[] = u"Boot####";
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002112 struct eficonfig_boot_order_data *data;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002113
2114 efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
2115 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
2116 if (!load_option)
2117 return EFI_SUCCESS;
2118
2119 ret = efi_deserialize_load_option(&lo, load_option, &size);
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002120 if (ret != EFI_SUCCESS)
2121 goto out;
2122
2123 data = calloc(1, sizeof(*data));
2124 if (!data) {
2125 ret = EFI_OUT_OF_RESOURCES;
2126 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002127 }
2128
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002129 title = calloc(1, utf16_utf8_strlen(lo.label) + 1);
2130 if (!title) {
2131 free(data);
2132 ret = EFI_OUT_OF_RESOURCES;
2133 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002134 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002135 p = title;
2136 utf16_utf8_strcpy(&p, lo.label);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002137
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002138 data->boot_index = boot_index;
2139 data->active = active;
2140
2141 ret = eficonfig_append_menu_entry(efi_menu, title, NULL, data);
2142 if (ret != EFI_SUCCESS) {
2143 free(data);
2144 free(title);
2145 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002146 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09002147
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002148out:
Masahisa Kojimae8753692022-09-12 17:33:56 +09002149 free(load_option);
2150
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002151 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002152}
2153
2154/**
2155 * eficonfig_create_change_boot_order_entry() - create boot order entry
2156 *
2157 * @efi_menu: pointer to the efimenu structure
2158 * @bootorder: pointer to the BootOrder variable
2159 * @num: number of BootOrder entry
2160 * Return: status code
2161 */
2162static efi_status_t eficonfig_create_change_boot_order_entry(struct efimenu *efi_menu,
2163 u16 *bootorder, efi_uintn_t num)
2164{
2165 u32 i;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002166 char *title;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002167 efi_status_t ret;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002168 u16 *var_name16 = NULL;
Masahisa Kojimad414f572022-12-02 13:59:36 +09002169 efi_uintn_t size, buf_size;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002170 struct eficonfig_save_boot_order_data *save_data;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002171
2172 /* list the load option in the order of BootOrder variable */
2173 for (i = 0; i < num; i++) {
2174 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
2175 break;
2176
2177 ret = eficonfig_add_change_boot_order_entry(efi_menu, bootorder[i], true);
2178 if (ret != EFI_SUCCESS)
2179 goto out;
2180 }
2181
2182 /* list the remaining load option not included in the BootOrder */
Masahisa Kojimad414f572022-12-02 13:59:36 +09002183 buf_size = 128;
2184 var_name16 = malloc(buf_size);
2185 if (!var_name16)
2186 return EFI_OUT_OF_RESOURCES;
2187
2188 var_name16[0] = 0;
2189 for (;;) {
2190 int index;
2191 efi_guid_t guid;
2192
Masahisa Kojimae8753692022-09-12 17:33:56 +09002193 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
2194 break;
2195
Masahisa Kojimad414f572022-12-02 13:59:36 +09002196 size = buf_size;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002197 ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
Masahisa Kojimad414f572022-12-02 13:59:36 +09002198 if (ret == EFI_NOT_FOUND)
2199 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002200 if (ret != EFI_SUCCESS)
2201 goto out;
Masahisa Kojimad414f572022-12-02 13:59:36 +09002202
2203 if (efi_varname_is_load_option(var_name16, &index)) {
2204 /* If the index is included in the BootOrder, skip it */
2205 if (search_bootorder(bootorder, num, index, NULL))
2206 continue;
2207
2208 ret = eficonfig_add_change_boot_order_entry(efi_menu, index, false);
2209 if (ret != EFI_SUCCESS)
2210 goto out;
2211 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09002212 }
2213
2214 /* add "Save" and "Quit" entries */
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002215 title = strdup("Save");
2216 if (!title) {
2217 ret = EFI_OUT_OF_RESOURCES;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002218 goto out;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002219 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002220
2221 save_data = malloc(sizeof(struct eficonfig_save_boot_order_data));
2222 if (!save_data) {
2223 ret = EFI_OUT_OF_RESOURCES;
2224 goto out;
2225 }
2226 save_data->efi_menu = efi_menu;
2227 save_data->selected = false;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002228
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002229 ret = eficonfig_append_menu_entry(efi_menu, title,
2230 eficonfig_process_save_boot_order,
2231 save_data);
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002232 if (ret != EFI_SUCCESS)
Masahisa Kojimae8753692022-09-12 17:33:56 +09002233 goto out;
2234
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002235 ret = eficonfig_append_quit_entry(efi_menu);
2236 if (ret != EFI_SUCCESS)
2237 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002238
2239 efi_menu->active = 0;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002240out:
Masahisa Kojimad414f572022-12-02 13:59:36 +09002241 free(var_name16);
2242
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002243 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002244}
2245
2246/**
2247 * eficonfig_process_change_boot_order() - handler to change boot order
2248 *
2249 * @data: pointer to the data for each entry
2250 * Return: status code
2251 */
2252static efi_status_t eficonfig_process_change_boot_order(void *data)
2253{
Masahisa Kojimae8753692022-09-12 17:33:56 +09002254 u16 *bootorder;
2255 efi_status_t ret;
2256 efi_uintn_t num, size;
2257 struct list_head *pos, *n;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002258 struct eficonfig_entry *entry;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002259 struct efimenu *efi_menu;
2260
2261 efi_menu = calloc(1, sizeof(struct efimenu));
2262 if (!efi_menu)
2263 return EFI_OUT_OF_RESOURCES;
2264
2265 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
2266
2267 INIT_LIST_HEAD(&efi_menu->list);
2268 num = size / sizeof(u16);
2269 ret = eficonfig_create_change_boot_order_entry(efi_menu, bootorder, num);
2270 if (ret != EFI_SUCCESS)
2271 goto out;
2272
2273 while (1) {
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002274 ret = eficonfig_process_common(efi_menu,
2275 " ** Change Boot Order **",
2276 eficonfig_change_boot_order_desc,
2277 eficonfig_display_statusline,
2278 eficonfig_print_change_boot_order_entry,
2279 eficonfig_choice_change_boot_order);
2280 /* exit from the menu if user selects the "Save" entry. */
2281 if (ret == EFI_SUCCESS && efi_menu->active == (efi_menu->count - 2)) {
2282 list_for_each_prev_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002283 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002284 if (entry->num == efi_menu->active)
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002285 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002286 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002287 if (((struct eficonfig_save_boot_order_data *)entry->data)->selected)
2288 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002289 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002290 if (ret != EFI_SUCCESS)
2291 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002292 }
2293out:
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002294 free(bootorder);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002295 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002296 entry = list_entry(pos, struct eficonfig_entry, list);
2297 free(entry->data);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002298 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002299 eficonfig_destroy(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002300
2301 /* to stay the parent menu */
2302 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
2303
2304 return ret;
2305}
2306
2307/**
Masahisa Kojima703ad322022-09-12 17:33:53 +09002308 * delete_boot_option() - delete selected boot option
2309 *
2310 * @boot_index: boot option index to delete
2311 * Return: status code
2312 */
2313static efi_status_t delete_boot_option(u16 boot_index)
2314{
2315 u16 *bootorder;
2316 u16 varname[9];
2317 efi_status_t ret;
2318 unsigned int index;
2319 efi_uintn_t num, size;
2320
2321 efi_create_indexed_name(varname, sizeof(varname),
2322 "Boot", boot_index);
2323 ret = efi_set_variable_int(varname, &efi_global_variable_guid,
2324 0, 0, NULL, false);
2325 if (ret != EFI_SUCCESS) {
2326 log_err("delete boot option(%ls) failed\n", varname);
2327 return ret;
2328 }
2329
2330 /* update BootOrder if necessary */
2331 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
2332 if (!bootorder)
2333 return EFI_SUCCESS;
2334
2335 num = size / sizeof(u16);
2336 if (!search_bootorder(bootorder, num, boot_index, &index))
2337 return EFI_SUCCESS;
2338
2339 memmove(&bootorder[index], &bootorder[index + 1],
2340 (num - index - 1) * sizeof(u16));
2341 size -= sizeof(u16);
2342 ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
2343 EFI_VARIABLE_NON_VOLATILE |
2344 EFI_VARIABLE_BOOTSERVICE_ACCESS |
2345 EFI_VARIABLE_RUNTIME_ACCESS,
2346 size, bootorder, false);
2347
2348 return ret;
2349}
2350
2351/**
2352 * eficonfig_process_delete_boot_option() - handler to delete boot option
2353 *
2354 * @data: pointer to the data for each entry
2355 * Return: status code
2356 */
2357static efi_status_t eficonfig_process_delete_boot_option(void *data)
2358{
2359 efi_status_t ret;
2360 unsigned int selected;
2361
2362 while (1) {
2363 ret = eficonfig_show_boot_selection(&selected);
2364 if (ret == EFI_SUCCESS)
2365 ret = delete_boot_option(selected);
2366
2367 if (ret != EFI_SUCCESS)
2368 break;
2369 }
2370
2371 /* to stay the parent menu */
2372 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
2373
2374 return ret;
2375}
2376
2377/**
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002378 * eficonfig_enumerate_boot_option() - enumerate the possible bootable media
2379 *
2380 * @opt: pointer to the media boot option structure
2381 * @volume_handles: pointer to the efi handles
2382 * @count: number of efi handle
2383 * Return: status code
2384 */
2385efi_status_t eficonfig_enumerate_boot_option(struct eficonfig_media_boot_option *opt,
2386 efi_handle_t *volume_handles, efi_status_t count)
2387{
2388 u32 i;
2389 struct efi_handler *handler;
2390 efi_status_t ret = EFI_SUCCESS;
2391
2392 for (i = 0; i < count; i++) {
2393 u16 *p;
2394 u16 dev_name[BOOTMENU_DEVICE_NAME_MAX];
2395 char *optional_data;
2396 struct efi_load_option lo;
2397 char buf[BOOTMENU_DEVICE_NAME_MAX];
2398 struct efi_device_path *device_path;
2399
2400 ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
2401 if (ret != EFI_SUCCESS)
2402 continue;
2403 ret = efi_protocol_open(handler, (void **)&device_path,
2404 efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2405 if (ret != EFI_SUCCESS)
2406 continue;
2407
2408 ret = efi_disk_get_device_name(volume_handles[i], buf, BOOTMENU_DEVICE_NAME_MAX);
2409 if (ret != EFI_SUCCESS)
2410 continue;
2411
2412 p = dev_name;
2413 utf8_utf16_strncpy(&p, buf, strlen(buf));
2414
2415 lo.label = dev_name;
2416 lo.attributes = LOAD_OPTION_ACTIVE;
2417 lo.file_path = device_path;
2418 lo.file_path_length = efi_dp_size(device_path) + sizeof(END);
2419 /*
2420 * Set the dedicated guid to optional_data, it is used to identify
2421 * the boot option that automatically generated by the bootmenu.
2422 * efi_serialize_load_option() expects optional_data is null-terminated
2423 * utf8 string, so set the "1234567" string to allocate enough space
2424 * to store guid, instead of realloc the load_option.
2425 */
2426 lo.optional_data = "1234567";
2427 opt[i].size = efi_serialize_load_option(&lo, (u8 **)&opt[i].lo);
2428 if (!opt[i].size) {
2429 ret = EFI_OUT_OF_RESOURCES;
2430 goto out;
2431 }
2432 /* set the guid */
2433 optional_data = (char *)opt[i].lo + (opt[i].size - u16_strsize(u"1234567"));
2434 memcpy(optional_data, &efi_guid_bootmenu_auto_generated, sizeof(efi_guid_t));
2435 }
2436
2437out:
2438 return ret;
2439}
2440
2441/**
2442 * eficonfig_delete_invalid_boot_option() - delete non-existing boot option
2443 *
2444 * @opt: pointer to the media boot option structure
2445 * @count: number of media boot option structure
2446 * Return: status code
2447 */
2448efi_status_t eficonfig_delete_invalid_boot_option(struct eficonfig_media_boot_option *opt,
2449 efi_status_t count)
2450{
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002451 efi_uintn_t size;
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002452 void *load_option;
Masahisa Kojima68e698b2022-12-19 11:33:13 +09002453 u32 i, list_size = 0;
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002454 struct efi_load_option lo;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002455 u16 *var_name16 = NULL;
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002456 u16 varname[] = u"Boot####";
Masahisa Kojima036f7992022-11-14 19:00:47 +09002457 efi_status_t ret = EFI_SUCCESS;
Masahisa Kojima68e698b2022-12-19 11:33:13 +09002458 u16 *delete_index_list = NULL, *p;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002459 efi_uintn_t buf_size;
Masahisa Kojimad414f572022-12-02 13:59:36 +09002460
2461 buf_size = 128;
2462 var_name16 = malloc(buf_size);
2463 if (!var_name16)
2464 return EFI_OUT_OF_RESOURCES;
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002465
Masahisa Kojimad414f572022-12-02 13:59:36 +09002466 var_name16[0] = 0;
2467 for (;;) {
2468 int index;
2469 efi_guid_t guid;
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002470 efi_uintn_t tmp;
2471
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002472 ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
Masahisa Kojima68e698b2022-12-19 11:33:13 +09002473 if (ret == EFI_NOT_FOUND) {
2474 /*
2475 * EFI_NOT_FOUND indicates we retrieved all EFI variables.
2476 * This should be treated as success.
2477 */
2478 ret = EFI_SUCCESS;
Masahisa Kojimad414f572022-12-02 13:59:36 +09002479 break;
Masahisa Kojima68e698b2022-12-19 11:33:13 +09002480 }
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002481 if (ret != EFI_SUCCESS)
2482 goto out;
2483
Masahisa Kojimad414f572022-12-02 13:59:36 +09002484 if (!efi_varname_is_load_option(var_name16, &index))
2485 continue;
2486
2487 efi_create_indexed_name(varname, sizeof(varname), "Boot", index);
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002488 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
2489 if (!load_option)
2490 continue;
2491
2492 tmp = size;
2493 ret = efi_deserialize_load_option(&lo, load_option, &size);
2494 if (ret != EFI_SUCCESS)
2495 goto next;
2496
Masahisa Kojima68e698b2022-12-19 11:33:13 +09002497 if (size >= sizeof(efi_guid_bootmenu_auto_generated) &&
2498 !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) {
2499 for (i = 0; i < count; i++) {
2500 if (opt[i].size == tmp &&
2501 memcmp(opt[i].lo, load_option, tmp) == 0) {
2502 opt[i].exist = true;
2503 break;
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002504 }
Masahisa Kojima68e698b2022-12-19 11:33:13 +09002505 }
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002506
Masahisa Kojima68e698b2022-12-19 11:33:13 +09002507 /*
2508 * The entire list of variables must be retrieved by
2509 * efi_get_next_variable_name_int() before deleting the invalid
2510 * boot option, just save the index here.
2511 */
2512 if (i == count) {
2513 p = realloc(delete_index_list, sizeof(u32) *
2514 (list_size + 1));
2515 if (!p) {
2516 ret = EFI_OUT_OF_RESOURCES;
2517 goto out;
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002518 }
Masahisa Kojima68e698b2022-12-19 11:33:13 +09002519 delete_index_list = p;
2520 delete_index_list[list_size++] = index;
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002521 }
2522 }
2523next:
2524 free(load_option);
2525 }
2526
Masahisa Kojima68e698b2022-12-19 11:33:13 +09002527 /* delete all invalid boot options */
2528 for (i = 0; i < list_size; i++) {
2529 ret = delete_boot_option(delete_index_list[i]);
2530 if (ret != EFI_SUCCESS)
2531 goto out;
2532 }
2533
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002534out:
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002535 free(var_name16);
Masahisa Kojima68e698b2022-12-19 11:33:13 +09002536 free(delete_index_list);
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002537
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002538 return ret;
2539}
2540
2541/**
2542 * eficonfig_generate_media_device_boot_option() - generate the media device boot option
2543 *
2544 * This function enumerates all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
2545 * and generate the bootmenu entries.
2546 * This function also provide the BOOT#### variable maintenance for
2547 * the media device entries.
2548 * - Automatically create the BOOT#### variable for the newly detected device,
2549 * this BOOT#### variable is distinguished by the special GUID
2550 * stored in the EFI_LOAD_OPTION.optional_data
2551 * - If the device is not attached to the system, the associated BOOT#### variable
2552 * is automatically deleted.
2553 *
2554 * Return: status code
2555 */
2556efi_status_t eficonfig_generate_media_device_boot_option(void)
2557{
2558 u32 i;
2559 efi_status_t ret;
2560 efi_uintn_t count;
2561 efi_handle_t *volume_handles = NULL;
2562 struct eficonfig_media_boot_option *opt = NULL;
2563
2564 ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
2565 NULL, &count, (efi_handle_t **)&volume_handles);
2566 if (ret != EFI_SUCCESS)
2567 return ret;
2568
2569 opt = calloc(count, sizeof(struct eficonfig_media_boot_option));
2570 if (!opt)
2571 goto out;
2572
2573 /* enumerate all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL */
2574 ret = eficonfig_enumerate_boot_option(opt, volume_handles, count);
2575 if (ret != EFI_SUCCESS)
2576 goto out;
2577
2578 /*
2579 * System hardware configuration may vary depending on the user setup.
2580 * The boot option is automatically added by the bootmenu.
2581 * If the device is not attached to the system, the boot option needs
2582 * to be deleted.
2583 */
2584 ret = eficonfig_delete_invalid_boot_option(opt, count);
2585 if (ret != EFI_SUCCESS)
2586 goto out;
2587
2588 /* add non-existent boot option */
2589 for (i = 0; i < count; i++) {
2590 u32 boot_index;
2591 u16 var_name[9];
2592
2593 if (!opt[i].exist) {
2594 ret = eficonfig_get_unused_bootoption(var_name, sizeof(var_name),
2595 &boot_index);
2596 if (ret != EFI_SUCCESS)
2597 goto out;
2598
2599 ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
2600 EFI_VARIABLE_NON_VOLATILE |
2601 EFI_VARIABLE_BOOTSERVICE_ACCESS |
2602 EFI_VARIABLE_RUNTIME_ACCESS,
2603 opt[i].size, opt[i].lo, false);
2604 if (ret != EFI_SUCCESS)
2605 goto out;
2606
2607 ret = eficonfig_append_bootorder(boot_index);
2608 if (ret != EFI_SUCCESS) {
2609 efi_set_variable_int(var_name, &efi_global_variable_guid,
2610 0, 0, NULL, false);
2611 goto out;
2612 }
2613 }
2614 }
2615
2616out:
2617 if (opt) {
2618 for (i = 0; i < count; i++)
2619 free(opt[i].lo);
2620 }
2621 free(opt);
2622 efi_free_pool(volume_handles);
2623
2624 return ret;
2625}
2626
Masahisa Kojima767a9e62022-09-12 17:33:54 +09002627/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002628 * eficonfig_init() - do required initialization for eficonfig command
2629 *
2630 * Return: status code
2631 */
2632static efi_status_t eficonfig_init(void)
2633{
2634 efi_status_t ret = EFI_SUCCESS;
2635 static bool init;
2636 struct efi_handler *handler;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002637 unsigned long columns, rows;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002638
2639 if (!init) {
2640 ret = efi_search_protocol(efi_root, &efi_guid_text_input_protocol, &handler);
2641 if (ret != EFI_SUCCESS)
2642 return ret;
2643
2644 ret = efi_protocol_open(handler, (void **)&cin, efi_root, NULL,
2645 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2646 if (ret != EFI_SUCCESS)
2647 return ret;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002648 ret = efi_search_protocol(efi_root, &efi_guid_text_output_protocol, &handler);
2649 if (ret != EFI_SUCCESS)
2650 return ret;
2651
2652 ret = efi_protocol_open(handler, (void **)&cout, efi_root, NULL,
2653 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2654 if (ret != EFI_SUCCESS)
2655 return ret;
2656
2657 cout->query_mode(cout, cout->mode->mode, &columns, &rows);
2658 avail_row = rows - (EFICONFIG_MENU_HEADER_ROW_NUM +
2659 EFICONFIG_MENU_DESC_ROW_NUM);
2660 if (avail_row <= 0) {
2661 eficonfig_print_msg("Console size is too small!");
2662 return EFI_INVALID_PARAMETER;
2663 }
2664 /* TODO: Should we check the minimum column size? */
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002665 }
2666
2667 init = true;
2668
2669 return ret;
2670}
2671
2672static const struct eficonfig_item maintenance_menu_items[] = {
2673 {"Add Boot Option", eficonfig_process_add_boot_option},
Masahisa Kojimabb052b92022-09-12 17:33:51 +09002674 {"Edit Boot Option", eficonfig_process_edit_boot_option},
Masahisa Kojimae8753692022-09-12 17:33:56 +09002675 {"Change Boot Order", eficonfig_process_change_boot_order},
Masahisa Kojima703ad322022-09-12 17:33:53 +09002676 {"Delete Boot Option", eficonfig_process_delete_boot_option},
Masahisa Kojimacbdbcb42022-11-20 09:21:18 +09002677#if (CONFIG_IS_ENABLED(EFI_SECURE_BOOT) && CONFIG_IS_ENABLED(EFI_MM_COMM_TEE))
2678 {"Secure Boot Configuration", eficonfig_process_secure_boot_config},
2679#endif
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002680 {"Quit", eficonfig_process_quit},
2681};
2682
2683/**
2684 * do_eficonfig() - execute `eficonfig` command
2685 *
2686 * @cmdtp: table entry describing command
2687 * @flag: bitmap indicating how the command was invoked
2688 * @argc: number of arguments
2689 * @argv: command line arguments
2690 * Return: status code
2691 */
2692static int do_eficonfig(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
2693{
2694 efi_status_t ret;
2695 struct efimenu *efi_menu;
2696
2697 if (argc > 1)
2698 return CMD_RET_USAGE;
2699
2700 ret = efi_init_obj_list();
2701 if (ret != EFI_SUCCESS) {
2702 log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
2703 ret & ~EFI_ERROR_MASK);
2704
2705 return CMD_RET_FAILURE;
2706 }
2707
2708 ret = eficonfig_init();
2709 if (ret != EFI_SUCCESS)
2710 return CMD_RET_FAILURE;
2711
Masahisa Kojimaf648fa32022-09-12 17:33:55 +09002712 ret = eficonfig_generate_media_device_boot_option();
2713 if (ret != EFI_SUCCESS && ret != EFI_NOT_FOUND)
2714 return ret;
2715
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002716 while (1) {
2717 efi_menu = eficonfig_create_fixed_menu(maintenance_menu_items,
2718 ARRAY_SIZE(maintenance_menu_items));
2719 if (!efi_menu)
2720 return CMD_RET_FAILURE;
2721
Masahisa Kojimafc811d12023-01-24 15:56:13 +09002722 ret = eficonfig_process_common(efi_menu,
2723 " ** UEFI Maintenance Menu **",
2724 eficonfig_menu_desc,
2725 eficonfig_display_statusline,
2726 eficonfig_print_entry,
2727 eficonfig_choice_entry);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002728 eficonfig_destroy(efi_menu);
2729
2730 if (ret == EFI_ABORTED)
2731 break;
2732 }
2733
2734 return CMD_RET_SUCCESS;
2735}
2736
2737U_BOOT_CMD(
2738 eficonfig, 1, 0, do_eficonfig,
2739 "provide menu-driven UEFI variable maintenance interface",
2740 ""
2741);