blob: de1647b943cf08654725dffdb780ae56e6998231 [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 <charset.h>
Simon Glass37972f42025-05-24 11:28:21 -060011#include <efi_device_path.h>
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +090012#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;
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +020065 struct eficonfig_select_file_info fdt_info;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +090066 unsigned int boot_index;
67 u16 *description;
68 u16 *optional_data;
69 bool edit_completed;
70};
71
72/**
73 * struct eficonfig_volume_entry_data - structure to be used to store volume info
74 *
75 * @file_info: pointer to file info structure
76 * @v: pointer to the protocol interface
77 * @dp: pointer to the device path
78 */
79struct eficonfig_volume_entry_data {
80 struct eficonfig_select_file_info *file_info;
81 struct efi_simple_file_system_protocol *v;
82 struct efi_device_path *dp;
83};
84
85/**
86 * struct eficonfig_file_entry_data - structure to be used to store file info
87 *
88 * @file_info: pointer to file info structure
89 * @is_directory: flag to identify the directory or file
90 * @file_name: name of directory or file
91 */
92struct eficonfig_file_entry_data {
93 struct eficonfig_select_file_info *file_info;
94 bool is_directory;
95 char *file_name;
96};
97
98/**
Masahisa Kojimabb052b92022-09-12 17:33:51 +090099 * struct eficonfig_boot_selection_data - structure to be used to select the boot option entry
100 *
101 * @boot_index: index of the boot option
102 * @selected: pointer to store the selected index in the BootOrder variable
103 */
104struct eficonfig_boot_selection_data {
105 u16 boot_index;
106 int *selected;
107};
108
109/**
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +0900110 * struct eficonfig_boot_order_data - structure to be used to update BootOrder variable
Masahisa Kojimae8753692022-09-12 17:33:56 +0900111 *
Masahisa Kojimae8753692022-09-12 17:33:56 +0900112 * @boot_index: boot option index
113 * @active: flag to include the boot option into BootOrder variable
Masahisa Kojimae8753692022-09-12 17:33:56 +0900114 */
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +0900115struct eficonfig_boot_order_data {
Masahisa Kojimae8753692022-09-12 17:33:56 +0900116 u32 boot_index;
117 bool active;
Masahisa Kojimae8753692022-09-12 17:33:56 +0900118};
119
120/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +0900121 * struct eficonfig_save_boot_order_data - structure to be used to change boot order
122 *
123 * @efi_menu: pointer to efimenu structure
124 * @selected: flag to indicate user selects "Save" entry
125 */
126struct eficonfig_save_boot_order_data {
127 struct efimenu *efi_menu;
128 bool selected;
129};
130
131/**
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900132 * struct eficonfig_menu_adjust - update start and end entry index
133 *
134 * @efi_menu: pointer to efimenu structure
135 * @add: flag to add or substract the index
136 */
137static void eficonfig_menu_adjust(struct efimenu *efi_menu, bool add)
138{
139 if (add)
140 ++efi_menu->active;
141 else
142 --efi_menu->active;
143
144 if (add && efi_menu->end < efi_menu->active) {
145 efi_menu->start++;
146 efi_menu->end++;
147 } else if (!add && efi_menu->start > efi_menu->active) {
148 efi_menu->start--;
149 efi_menu->end--;
150 }
151}
152#define eficonfig_menu_up(_a) eficonfig_menu_adjust(_a, false)
153#define eficonfig_menu_down(_a) eficonfig_menu_adjust(_a, true)
154
155/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900156 * eficonfig_print_msg() - print message
157 *
158 * display the message to the user, user proceeds the screen
159 * with any key press.
160 *
161 * @items: pointer to the structure of each menu entry
162 * @count: the number of menu entry
163 * @menu_header: pointer to the menu header string
164 * Return: status code
165 */
166void eficonfig_print_msg(char *msg)
167{
168 /* Flush input */
169 while (tstc())
170 getchar();
171
172 printf(ANSI_CURSOR_HIDE
173 ANSI_CLEAR_CONSOLE
174 ANSI_CURSOR_POSITION
175 "%s\n\n Press any key to continue", 3, 4, msg);
176
177 getchar();
178}
179
180/**
181 * eficonfig_print_entry() - print each menu entry
182 *
183 * @data: pointer to the data associated with each menu entry
184 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900185void eficonfig_print_entry(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900186{
187 struct eficonfig_entry *entry = data;
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900188 bool reverse = (entry->efi_menu->active == entry->num);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900189
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900190 if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num)
191 return;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900192
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900193 printf(ANSI_CURSOR_POSITION, (entry->num - entry->efi_menu->start) +
194 EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900195
196 if (reverse)
197 puts(ANSI_COLOR_REVERSE);
198
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900199 printf(ANSI_CLEAR_LINE "%s", entry->title);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900200
201 if (reverse)
202 puts(ANSI_COLOR_RESET);
203}
204
205/**
206 * eficonfig_display_statusline() - print status line
207 *
208 * @m: pointer to the menu structure
209 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900210void eficonfig_display_statusline(struct menu *m)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900211{
212 struct eficonfig_entry *entry;
213
214 if (menu_default_choice(m, (void *)&entry) < 0)
215 return;
216
217 printf(ANSI_CURSOR_POSITION
218 "\n%s\n"
219 ANSI_CURSOR_POSITION ANSI_CLEAR_LINE ANSI_CURSOR_POSITION
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900220 "%s"
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +0900221 ANSI_CLEAR_LINE_TO_END,
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900222 1, 1, entry->efi_menu->menu_header, avail_row + 4, 1,
223 avail_row + 5, 1, entry->efi_menu->menu_desc);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900224}
225
226/**
227 * eficonfig_choice_entry() - user key input handler
228 *
229 * @data: pointer to the efimenu structure
230 * Return: key string to identify the selected entry
231 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900232char *eficonfig_choice_entry(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900233{
Simon Glass9d8d3872023-01-06 08:52:26 -0600234 struct cli_ch_state s_cch, *cch = &s_cch;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900235 struct list_head *pos, *n;
236 struct eficonfig_entry *entry;
Simon Glass05ecdf82023-01-06 08:52:22 -0600237 enum bootmenu_key key = BKEY_NONE;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900238 struct efimenu *efi_menu = data;
239
Simon Glass9d8d3872023-01-06 08:52:26 -0600240 cli_ch_init(cch);
241
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900242 while (1) {
Simon Glass9d8d3872023-01-06 08:52:26 -0600243 key = bootmenu_loop((struct bootmenu_data *)efi_menu, cch);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900244
245 switch (key) {
Simon Glass05ecdf82023-01-06 08:52:22 -0600246 case BKEY_UP:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900247 if (efi_menu->active > 0)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900248 eficonfig_menu_up(efi_menu);
249
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900250 /* no menu key selected, regenerate menu */
251 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -0600252 case BKEY_DOWN:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900253 if (efi_menu->active < efi_menu->count - 1)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900254 eficonfig_menu_down(efi_menu);
255
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900256 /* no menu key selected, regenerate menu */
257 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -0600258 case BKEY_SELECT:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900259 list_for_each_safe(pos, n, &efi_menu->list) {
260 entry = list_entry(pos, struct eficonfig_entry, list);
261 if (entry->num == efi_menu->active)
262 return entry->key;
263 }
264 break;
Simon Glass05ecdf82023-01-06 08:52:22 -0600265 case BKEY_QUIT:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900266 /* Quit by choosing the last entry */
267 entry = list_last_entry(&efi_menu->list, struct eficonfig_entry, list);
268 return entry->key;
269 default:
270 /* Pressed key is not valid, no need to regenerate the menu */
271 break;
272 }
273 }
274}
275
276/**
277 * eficonfig_destroy() - destroy efimenu
278 *
279 * @efi_menu: pointer to the efimenu structure
280 */
281void eficonfig_destroy(struct efimenu *efi_menu)
282{
283 struct list_head *pos, *n;
284 struct eficonfig_entry *entry;
285
286 if (!efi_menu)
287 return;
288
289 list_for_each_safe(pos, n, &efi_menu->list) {
290 entry = list_entry(pos, struct eficonfig_entry, list);
291 free(entry->title);
292 list_del(&entry->list);
293 free(entry);
294 }
295 free(efi_menu->menu_header);
296 free(efi_menu);
297}
298
299/**
300 * eficonfig_process_quit() - callback function for "Quit" entry
301 *
302 * @data: pointer to the data
303 * Return: status code
304 */
305efi_status_t eficonfig_process_quit(void *data)
306{
307 return EFI_ABORTED;
308}
309
310/**
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900311 * eficonfig_append_menu_entry() - append menu item
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900312 *
313 * @efi_menu: pointer to the efimenu structure
314 * @title: pointer to the entry title
315 * @func: callback of each entry
316 * @data: pointer to the data to be passed to each entry callback
317 * Return: status code
318 */
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900319efi_status_t eficonfig_append_menu_entry(struct efimenu *efi_menu,
320 char *title, eficonfig_entry_func func,
321 void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900322{
323 struct eficonfig_entry *entry;
324
325 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX)
326 return EFI_OUT_OF_RESOURCES;
327
328 entry = calloc(1, sizeof(struct eficonfig_entry));
329 if (!entry)
330 return EFI_OUT_OF_RESOURCES;
331
332 entry->title = title;
333 sprintf(entry->key, "%d", efi_menu->count);
334 entry->efi_menu = efi_menu;
335 entry->func = func;
336 entry->data = data;
337 entry->num = efi_menu->count++;
338 list_add_tail(&entry->list, &efi_menu->list);
339
340 return EFI_SUCCESS;
341}
342
343/**
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900344 * eficonfig_append_quit_entry() - append quit entry
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900345 *
346 * @efi_menu: pointer to the efimenu structure
347 * Return: status code
348 */
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900349efi_status_t eficonfig_append_quit_entry(struct efimenu *efi_menu)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900350{
351 char *title;
352 efi_status_t ret;
353
354 title = strdup("Quit");
355 if (!title)
356 return EFI_OUT_OF_RESOURCES;
357
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900358 ret = eficonfig_append_menu_entry(efi_menu, title, eficonfig_process_quit, NULL);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900359 if (ret != EFI_SUCCESS)
360 free(title);
361
362 return ret;
363}
364
365/**
366 * eficonfig_create_fixed_menu() - create fixed entry menu structure
367 *
368 * @items: pointer to the menu entry item
369 * @count: the number of menu entry
370 * Return: pointer to the efimenu structure
371 */
372void *eficonfig_create_fixed_menu(const struct eficonfig_item *items, int count)
373{
374 u32 i;
375 char *title;
376 efi_status_t ret;
377 struct efimenu *efi_menu;
378 const struct eficonfig_item *iter = items;
379
380 efi_menu = calloc(1, sizeof(struct efimenu));
381 if (!efi_menu)
382 return NULL;
383
384 INIT_LIST_HEAD(&efi_menu->list);
385 for (i = 0; i < count; i++, iter++) {
386 title = strdup(iter->title);
387 if (!title)
388 goto out;
389
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900390 ret = eficonfig_append_menu_entry(efi_menu, title, iter->func, iter->data);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900391 if (ret != EFI_SUCCESS) {
392 free(title);
393 goto out;
394 }
395 }
396
397 return efi_menu;
398out:
399 eficonfig_destroy(efi_menu);
400
401 return NULL;
402}
403
404/**
405 * eficonfig_process_common() - main handler for UEFI menu
406 *
407 * Construct the structures required to show the menu, then handle
408 * the user input interacting with u-boot menu functions.
409 *
410 * @efi_menu: pointer to the efimenu structure
411 * @menu_header: pointer to the menu header string
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900412 * @menu_desc: pointer to the menu description
413 * @display_statusline: function pointer to draw statusline
414 * @item_data_print: function pointer to draw the menu item
415 * @item_choice: function pointer to handle the key press
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900416 * Return: status code
417 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900418efi_status_t eficonfig_process_common(struct efimenu *efi_menu,
419 char *menu_header, const char *menu_desc,
420 void (*display_statusline)(struct menu *),
421 void (*item_data_print)(void *),
422 char *(*item_choice)(void *))
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900423{
424 struct menu *menu;
425 void *choice = NULL;
426 struct list_head *pos, *n;
427 struct eficonfig_entry *entry;
428 efi_status_t ret = EFI_SUCCESS;
429
430 if (efi_menu->count > EFICONFIG_ENTRY_NUM_MAX)
431 return EFI_OUT_OF_RESOURCES;
432
433 efi_menu->delay = -1;
434 efi_menu->active = 0;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900435 efi_menu->start = 0;
436 efi_menu->end = avail_row - 1;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900437
438 if (menu_header) {
439 efi_menu->menu_header = strdup(menu_header);
440 if (!efi_menu->menu_header)
441 return EFI_OUT_OF_RESOURCES;
442 }
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900443 if (menu_desc)
444 efi_menu->menu_desc = menu_desc;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900445
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900446 menu = menu_create(NULL, 0, 1, display_statusline, item_data_print,
developerc2a1e9e2024-10-29 17:47:16 +0800447 item_choice, NULL, efi_menu);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900448 if (!menu)
449 return EFI_INVALID_PARAMETER;
450
451 list_for_each_safe(pos, n, &efi_menu->list) {
452 entry = list_entry(pos, struct eficonfig_entry, list);
453 if (!menu_item_add(menu, entry->key, entry)) {
454 ret = EFI_INVALID_PARAMETER;
455 goto out;
456 }
457 }
458
459 entry = list_first_entry_or_null(&efi_menu->list, struct eficonfig_entry, list);
460 if (entry)
461 menu_default_set(menu, entry->key);
462
463 printf(ANSI_CURSOR_HIDE
464 ANSI_CLEAR_CONSOLE
465 ANSI_CURSOR_POSITION, 1, 1);
466
467 if (menu_get_choice(menu, &choice)) {
468 entry = choice;
469 if (entry->func)
470 ret = entry->func(entry->data);
471 }
472out:
473 menu_destroy(menu);
474
475 printf(ANSI_CLEAR_CONSOLE
476 ANSI_CURSOR_POSITION
477 ANSI_CURSOR_SHOW, 1, 1);
478
479 return ret;
480}
481
482/**
483 * eficonfig_volume_selected() - handler of volume selection
484 *
485 * @data: pointer to the data of selected entry
486 * Return: status code
487 */
488static efi_status_t eficonfig_volume_selected(void *data)
489{
490 struct eficonfig_volume_entry_data *info = data;
491
492 if (info) {
493 info->file_info->current_volume = info->v;
494 info->file_info->dp_volume = info->dp;
495 }
496
497 return EFI_SUCCESS;
498}
499
500/**
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900501 * eficonfig_create_device_path() - create device path
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900502 *
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900503 * @dp_volume: pointer to the volume
504 * @current_path: pointer to the file path u16 string
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900505 * Return:
506 * device path or NULL. Caller must free the returned value
507 */
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900508struct efi_device_path *eficonfig_create_device_path(struct efi_device_path *dp_volume,
509 u16 *current_path)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900510{
511 char *p;
512 void *buf;
513 efi_uintn_t fp_size;
514 struct efi_device_path *dp;
515 struct efi_device_path_file_path *fp;
516
Masahisa Kojimaf2d191a2022-12-02 13:59:34 +0900517 fp_size = sizeof(struct efi_device_path) + u16_strsize(current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900518 buf = calloc(1, fp_size + sizeof(END));
519 if (!buf)
520 return NULL;
521
522 fp = buf;
523 fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
524 fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
525 fp->dp.length = (u16)fp_size;
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900526 u16_strcpy(fp->str, current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900527
528 p = buf;
529 p += fp_size;
530 *((struct efi_device_path *)p) = END;
531
Heinrich Schuchardtf71c6942023-11-18 12:40:32 +0100532 dp = efi_dp_shorten(dp_volume);
533 if (!dp)
534 dp = dp_volume;
Heinrich Schuchardtf8de0092024-05-24 14:54:26 +0200535 dp = efi_dp_concat(dp, &fp->dp, 0);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900536 free(buf);
537
538 return dp;
539}
540
541/**
542 * eficonfig_file_selected() - handler of file selection
543 *
544 * @data: pointer to the data of selected entry
545 * Return: status code
546 */
547static efi_status_t eficonfig_file_selected(void *data)
548{
549 u16 *tmp;
550 struct eficonfig_file_entry_data *info = data;
551
552 if (!info)
553 return EFI_INVALID_PARAMETER;
554
Masahisa Kojima5d13e9d2022-12-02 13:59:33 +0900555 if (!strcmp(info->file_name, "..\\")) {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900556 struct eficonfig_filepath_info *iter;
557 struct list_head *pos, *n;
558 int is_last;
559 char *filepath;
560 tmp = info->file_info->current_path;
561
562 memset(info->file_info->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE);
563 filepath = calloc(1, EFICONFIG_FILE_PATH_MAX);
564 if (!filepath)
565 return EFI_OUT_OF_RESOURCES;
566
567 list_for_each_safe(pos, n, &info->file_info->filepath_list) {
568 iter = list_entry(pos, struct eficonfig_filepath_info, list);
569
570 is_last = list_is_last(&iter->list, &info->file_info->filepath_list);
571 if (is_last) {
572 list_del(&iter->list);
573 free(iter->name);
574 free(iter);
575 break;
576 }
577 strlcat(filepath, iter->name, EFICONFIG_FILE_PATH_MAX);
578 }
579 utf8_utf16_strcpy(&tmp, filepath);
580 } else {
581 size_t new_len;
582 struct eficonfig_filepath_info *filepath_info;
583
584 new_len = u16_strlen(info->file_info->current_path) +
585 strlen(info->file_name);
586 if (new_len >= EFICONFIG_FILE_PATH_MAX) {
587 eficonfig_print_msg("File path is too long!");
588 return EFI_INVALID_PARAMETER;
589 }
590 tmp = &info->file_info->current_path[u16_strlen(info->file_info->current_path)];
591 utf8_utf16_strcpy(&tmp, info->file_name);
592
593 filepath_info = calloc(1, sizeof(struct eficonfig_filepath_info));
594 if (!filepath_info)
595 return EFI_OUT_OF_RESOURCES;
596
597 filepath_info->name = strdup(info->file_name);
598 if (!filepath_info->name) {
599 free(filepath_info);
600 return EFI_OUT_OF_RESOURCES;
601 }
602 list_add_tail(&filepath_info->list, &info->file_info->filepath_list);
603
604 if (!info->is_directory)
605 info->file_info->file_selected = true;
606 }
607
608 return EFI_SUCCESS;
609}
610
611/**
612 * eficonfig_select_volume() - construct the volume selection menu
613 *
614 * @file_info: pointer to the file selection structure
615 * Return: status code
616 */
617static efi_status_t eficonfig_select_volume(struct eficonfig_select_file_info *file_info)
618{
619 u32 i;
620 efi_status_t ret;
621 efi_uintn_t count;
622 struct efimenu *efi_menu;
623 struct list_head *pos, *n;
624 struct efi_handler *handler;
625 struct eficonfig_entry *entry;
626 struct efi_device_path *device_path;
627 efi_handle_t *volume_handles = NULL;
628 struct efi_simple_file_system_protocol *v;
629
630 ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
631 NULL, &count, (efi_handle_t **)&volume_handles);
632 if (ret != EFI_SUCCESS) {
633 eficonfig_print_msg("No block device found!");
634 return ret;
635 }
636
637 efi_menu = calloc(1, sizeof(struct efimenu));
638 if (!efi_menu)
639 return EFI_OUT_OF_RESOURCES;
640
641 INIT_LIST_HEAD(&efi_menu->list);
642 for (i = 0; i < count; i++) {
643 char *devname;
644 struct efi_block_io *block_io;
645 struct eficonfig_volume_entry_data *info;
646
647 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
648 break;
649
650 ret = efi_search_protocol(volume_handles[i],
651 &efi_simple_file_system_protocol_guid, &handler);
652 if (ret != EFI_SUCCESS)
653 continue;
654 ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL,
655 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
656 if (ret != EFI_SUCCESS)
657 continue;
658
659 ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
660 if (ret != EFI_SUCCESS)
661 continue;
662 ret = efi_protocol_open(handler, (void **)&device_path,
663 efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
664 if (ret != EFI_SUCCESS)
665 continue;
666
667 ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
668 if (ret != EFI_SUCCESS)
669 continue;
670 ret = efi_protocol_open(handler, (void **)&block_io,
671 efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
672 if (ret != EFI_SUCCESS)
673 continue;
674
675 info = calloc(1, sizeof(struct eficonfig_volume_entry_data));
676 if (!info) {
677 ret = EFI_OUT_OF_RESOURCES;
678 goto out;
679 }
680
681 devname = calloc(1, BOOTMENU_DEVICE_NAME_MAX);
682 if (!devname) {
683 free(info);
684 ret = EFI_OUT_OF_RESOURCES;
685 goto out;
686 }
687 ret = efi_disk_get_device_name(volume_handles[i], devname,
688 BOOTMENU_DEVICE_NAME_MAX);
689 if (ret != EFI_SUCCESS) {
690 free(info);
691 goto out;
692 }
693
694 info->v = v;
695 info->dp = device_path;
696 info->file_info = file_info;
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900697 ret = eficonfig_append_menu_entry(efi_menu, devname, eficonfig_volume_selected,
698 info);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900699 if (ret != EFI_SUCCESS) {
700 free(info);
701 goto out;
702 }
703 }
704
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900705 ret = eficonfig_append_quit_entry(efi_menu);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900706 if (ret != EFI_SUCCESS)
707 goto out;
708
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900709 ret = eficonfig_process_common(efi_menu, " ** Select Volume **",
710 eficonfig_menu_desc,
711 eficonfig_display_statusline,
712 eficonfig_print_entry,
713 eficonfig_choice_entry);
714
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900715out:
716 efi_free_pool(volume_handles);
717 list_for_each_safe(pos, n, &efi_menu->list) {
718 entry = list_entry(pos, struct eficonfig_entry, list);
719 free(entry->data);
720 }
721 eficonfig_destroy(efi_menu);
722
723 return ret;
724}
725
726/**
727 * sort_file() - sort the file name in ascii order
728 *
729 * @data1: pointer to the file entry data
730 * @data2: pointer to the file entry data
731 * Return: -1 if the data1 file name is less than data2 file name,
732 * 0 if both file name match,
733 * 1 if the data1 file name is greater thant data2 file name.
734 */
735static int sort_file(const void *arg1, const void *arg2)
736{
737 const struct eficonfig_file_entry_data *data1, *data2;
738
739 data1 = *((const struct eficonfig_file_entry_data **)arg1);
740 data2 = *((const struct eficonfig_file_entry_data **)arg2);
741
742 return strcasecmp(data1->file_name, data2->file_name);
743}
744
745/**
746 * eficonfig_create_file_entry() - construct the file menu entry
747 *
748 * @efi_menu: pointer to the efimenu structure
749 * @count: number of the directory and file
750 * @tmp_infos: pointer to the entry data array
751 * @f: pointer to the file handle
752 * @buf: pointer to the buffer to store the directory information
753 * @file_info: pointer to the file selection structure
754 * Return: status code
755 */
756static efi_status_t
757eficonfig_create_file_entry(struct efimenu *efi_menu, u32 count,
758 struct eficonfig_file_entry_data **tmp_infos,
759 struct efi_file_handle *f, struct efi_file_info *buf,
760 struct eficonfig_select_file_info *file_info)
761{
762 char *name, *p;
763 efi_uintn_t len;
764 efi_status_t ret;
765 u32 i, entry_num = 0;
766 struct eficonfig_file_entry_data *info;
767
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900768 EFI_CALL(f->setpos(f, 0));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900769 /* Read directory and construct menu structure */
770 for (i = 0; i < count; i++) {
771 if (entry_num >= EFICONFIG_ENTRY_NUM_MAX - 1)
772 break;
773
774 len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE;
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900775 ret = EFI_CALL(f->read(f, &len, buf));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900776 if (ret != EFI_SUCCESS || len == 0)
777 break;
778
779 info = calloc(1, sizeof(struct eficonfig_file_entry_data));
780 if (!info) {
781 ret = EFI_OUT_OF_RESOURCES;
782 goto out;
783 }
784
785 /* append '\\' at the end of directory name */
786 name = calloc(1, utf16_utf8_strlen(buf->file_name) + 2);
787 if (!name) {
788 ret = EFI_OUT_OF_RESOURCES;
789 free(info);
790 goto out;
791 }
792 p = name;
793 utf16_utf8_strcpy(&p, buf->file_name);
794 if (buf->attribute & EFI_FILE_DIRECTORY) {
795 /* filter out u'.' */
796 if (!u16_strcmp(buf->file_name, u".")) {
797 free(info);
798 free(name);
799 continue;
800 }
801 name[u16_strlen(buf->file_name)] = '\\';
802 info->is_directory = true;
803 }
804
805 info->file_name = name;
806 info->file_info = file_info;
807 tmp_infos[entry_num++] = info;
808 }
809
810 qsort(tmp_infos, entry_num, sizeof(*tmp_infos),
811 (int (*)(const void *, const void *))sort_file);
812
813 for (i = 0; i < entry_num; i++) {
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900814 ret = eficonfig_append_menu_entry(efi_menu, tmp_infos[i]->file_name,
815 eficonfig_file_selected, tmp_infos[i]);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900816 if (ret != EFI_SUCCESS)
817 goto out;
818 }
819
820out:
821 return ret;
822}
823
824/**
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +0900825 * eficonfig_show_file_selection() - construct the file selection menu
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900826 *
827 * @file_info: pointer to the file selection structure
828 * @root: pointer to the file handle
829 * Return: status code
830 */
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +0900831static efi_status_t eficonfig_show_file_selection(struct eficonfig_select_file_info *file_info,
832 struct efi_file_handle *root)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900833{
834 u32 count = 0, i;
835 efi_uintn_t len;
836 efi_status_t ret;
837 struct efimenu *efi_menu;
838 struct efi_file_handle *f;
839 struct efi_file_info *buf;
840 struct eficonfig_file_entry_data **tmp_infos;
841
842 buf = calloc(1, sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE);
843 if (!buf)
844 return EFI_OUT_OF_RESOURCES;
845
846 while (!file_info->file_selected) {
847 efi_menu = calloc(1, sizeof(struct efimenu));
848 if (!efi_menu) {
849 ret = EFI_OUT_OF_RESOURCES;
850 goto out;
851 }
852 INIT_LIST_HEAD(&efi_menu->list);
853
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900854 ret = EFI_CALL(root->open(root, &f, file_info->current_path,
855 EFI_FILE_MODE_READ, 0));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900856 if (ret != EFI_SUCCESS) {
857 eficonfig_print_msg("Reading volume failed!");
858 free(efi_menu);
859 ret = EFI_ABORTED;
860 goto out;
861 }
862
863 /* Count the number of directory entries */
864 for (;;) {
865 len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE;
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900866 ret = EFI_CALL(f->read(f, &len, buf));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900867 if (ret != EFI_SUCCESS || len == 0)
868 break;
869
870 count++;
871 }
872
873 /* allocate array to sort the entry */
874 tmp_infos = calloc(count, sizeof(*tmp_infos));
875 if (!tmp_infos) {
876 ret = EFI_OUT_OF_RESOURCES;
877 goto err;
878 }
879
880 ret = eficonfig_create_file_entry(efi_menu, count, tmp_infos,
881 f, buf, file_info);
882 if (ret != EFI_SUCCESS)
883 goto err;
884
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900885 ret = eficonfig_append_quit_entry(efi_menu);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900886 if (ret != EFI_SUCCESS)
887 goto err;
888
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900889 ret = eficonfig_process_common(efi_menu, " ** Select File **",
890 eficonfig_menu_desc,
891 eficonfig_display_statusline,
892 eficonfig_print_entry,
893 eficonfig_choice_entry);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900894err:
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900895 EFI_CALL(f->close(f));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900896 eficonfig_destroy(efi_menu);
897
898 if (tmp_infos) {
899 for (i = 0; i < count; i++)
900 free(tmp_infos[i]);
901 }
902
903 free(tmp_infos);
904
905 if (ret != EFI_SUCCESS)
906 break;
907 }
908
909out:
910 free(buf);
911
912 return ret;
913}
914
915/**
916 * handle_user_input() - handle user input
917 *
918 * @buf: pointer to the buffer
919 * @buf_size: size of the buffer
920 * @cursor_col: cursor column for user input
921 * @msg: pointer to the string to display
922 * Return: status code
923 */
924static efi_status_t handle_user_input(u16 *buf, int buf_size,
925 int cursor_col, char *msg)
926{
927 u16 *tmp;
928 efi_status_t ret;
929
930 printf(ANSI_CLEAR_CONSOLE
931 ANSI_CURSOR_POSITION
932 "%s"
933 ANSI_CURSOR_POSITION
Masahisa Kojima3a9eb072023-02-02 18:24:43 +0900934 " Press ENTER to complete, ESC to quit",
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900935 0, 1, msg, 8, 1);
936
937 /* tmp is used to accept user cancel */
938 tmp = calloc(1, buf_size * sizeof(u16));
939 if (!tmp)
940 return EFI_OUT_OF_RESOURCES;
941
942 ret = efi_console_get_u16_string(cin, tmp, buf_size, NULL, 4, cursor_col);
943 if (ret == EFI_SUCCESS)
944 u16_strcpy(buf, tmp);
945
946 free(tmp);
947
948 /* to stay the parent menu */
949 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
950
951 return ret;
952}
953
954/**
955 * eficonfig_boot_add_enter_description() - handle user input for description
956 *
957 * @data: pointer to the internal boot option structure
958 * Return: status code
959 */
960static efi_status_t eficonfig_boot_add_enter_description(void *data)
961{
962 struct eficonfig_boot_option *bo = data;
963
964 return handle_user_input(bo->description, EFICONFIG_DESCRIPTION_MAX, 22,
965 "\n ** Edit Description **\n"
966 "\n"
Heinrich Schuchardt3a783202024-10-25 23:15:05 +0200967 " Enter description: ");
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900968}
969
970/**
971 * eficonfig_boot_add_optional_data() - handle user input for optional data
972 *
973 * @data: pointer to the internal boot option structure
974 * Return: status code
975 */
976static efi_status_t eficonfig_boot_add_optional_data(void *data)
977{
978 struct eficonfig_boot_option *bo = data;
979
980 return handle_user_input(bo->optional_data, EFICONFIG_OPTIONAL_DATA_MAX, 24,
981 "\n ** Edit Optional Data **\n"
982 "\n"
983 " enter optional data:");
984}
985
986/**
987 * eficonfig_boot_edit_save() - handler to save the boot option
988 *
989 * @data: pointer to the internal boot option structure
990 * Return: status code
991 */
992static efi_status_t eficonfig_boot_edit_save(void *data)
993{
994 struct eficonfig_boot_option *bo = data;
995
996 if (u16_strlen(bo->description) == 0) {
997 eficonfig_print_msg("Boot Description is empty!");
998 bo->edit_completed = false;
999 return EFI_NOT_READY;
1000 }
1001 if (u16_strlen(bo->file_info.current_path) == 0) {
1002 eficonfig_print_msg("File is not selected!");
1003 bo->edit_completed = false;
1004 return EFI_NOT_READY;
1005 }
1006
1007 bo->edit_completed = true;
1008
1009 return EFI_SUCCESS;
1010}
1011
1012/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001013 * eficonfig_process_clear_file_selection() - callback function for "Clear" entry
1014 *
1015 * @data: pointer to the data
1016 * Return: status code
1017 */
1018efi_status_t eficonfig_process_clear_file_selection(void *data)
1019{
1020 struct eficonfig_select_file_info *file_info = data;
1021
1022 /* clear the existing file information */
1023 file_info->current_volume = NULL;
1024 file_info->current_path[0] = u'\0';
1025 file_info->dp_volume = NULL;
1026
1027 return EFI_ABORTED;
1028}
1029
1030static struct eficonfig_item select_file_menu_items[] = {
1031 {"Select File", eficonfig_process_select_file},
1032 {"Clear", eficonfig_process_clear_file_selection},
1033 {"Quit", eficonfig_process_quit},
1034};
1035
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001036/**
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001037 * eficonfig_process_show_file_option() - display select file option
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001038 *
1039 * @file_info: pointer to the file information structure
1040 * Return: status code
1041 */
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001042efi_status_t eficonfig_process_show_file_option(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001043{
1044 efi_status_t ret;
1045 struct efimenu *efi_menu;
1046
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001047 select_file_menu_items[0].data = data;
1048 select_file_menu_items[1].data = data;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001049 efi_menu = eficonfig_create_fixed_menu(select_file_menu_items,
1050 ARRAY_SIZE(select_file_menu_items));
1051 if (!efi_menu)
1052 return EFI_OUT_OF_RESOURCES;
1053
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001054 ret = eficonfig_process_common(efi_menu, " ** Update File **",
1055 eficonfig_menu_desc,
1056 eficonfig_display_statusline,
1057 eficonfig_print_entry,
1058 eficonfig_choice_entry);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001059 if (ret != EFI_SUCCESS) /* User selects "Clear" or "Quit" */
1060 ret = EFI_NOT_READY;
1061
1062 eficonfig_destroy(efi_menu);
1063
1064 return ret;
1065}
1066
1067/**
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001068 * eficonfig_process_select_file() - handle user file selection
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001069 *
1070 * @data: pointer to the data
1071 * Return: status code
1072 */
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001073efi_status_t eficonfig_process_select_file(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001074{
1075 size_t len;
1076 efi_status_t ret;
1077 struct list_head *pos, *n;
1078 struct efi_file_handle *root;
1079 struct eficonfig_filepath_info *item;
1080 struct eficonfig_select_file_info *tmp = NULL;
1081 struct eficonfig_select_file_info *file_info = data;
1082
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001083 tmp = calloc(1, sizeof(struct eficonfig_select_file_info));
1084 if (!tmp)
1085 return EFI_OUT_OF_RESOURCES;
1086
1087 tmp->current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1088 if (!tmp->current_path) {
1089 free(tmp);
1090 return EFI_OUT_OF_RESOURCES;
1091 }
1092 INIT_LIST_HEAD(&tmp->filepath_list);
1093
1094 while (!tmp->file_selected) {
1095 tmp->current_volume = NULL;
1096 memset(tmp->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE);
1097
1098 ret = eficonfig_select_volume(tmp);
1099 if (ret != EFI_SUCCESS)
1100 goto out;
1101
1102 if (!tmp->current_volume)
1103 return EFI_INVALID_PARAMETER;
1104
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +09001105 ret = EFI_CALL(tmp->current_volume->open_volume(tmp->current_volume, &root));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001106 if (ret != EFI_SUCCESS)
1107 goto out;
1108
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001109 ret = eficonfig_show_file_selection(tmp, root);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001110 if (ret == EFI_ABORTED)
1111 continue;
1112 if (ret != EFI_SUCCESS)
1113 goto out;
1114 }
1115
1116out:
1117 if (ret == EFI_SUCCESS) {
1118 len = u16_strlen(tmp->current_path);
1119 len = (len >= EFICONFIG_FILE_PATH_MAX) ? (EFICONFIG_FILE_PATH_MAX - 1) : len;
1120 memcpy(file_info->current_path, tmp->current_path, len * sizeof(u16));
1121 file_info->current_path[len] = u'\0';
1122 file_info->current_volume = tmp->current_volume;
1123 file_info->dp_volume = tmp->dp_volume;
1124 }
1125
1126 list_for_each_safe(pos, n, &tmp->filepath_list) {
1127 item = list_entry(pos, struct eficonfig_filepath_info, list);
1128 list_del(&item->list);
1129 free(item->name);
1130 free(item);
1131 }
1132 free(tmp->current_path);
1133 free(tmp);
1134
1135 /* to stay the parent menu */
1136 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
1137
1138 return ret;
1139}
1140
1141/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001142 * eficonfig_set_boot_option() - set boot option
1143 *
1144 * @varname: pointer to variable name
1145 * @dp: pointer to device path
1146 * @label: pointer to label string
1147 * @optional_data: pointer to optional data
1148 * Return: status code
1149 */
1150static efi_status_t eficonfig_set_boot_option(u16 *varname, struct efi_device_path *dp,
1151 efi_uintn_t dp_size, u16 *label, char *optional_data)
1152{
1153 void *p = NULL;
1154 efi_status_t ret;
1155 efi_uintn_t size;
1156 struct efi_load_option lo;
1157
1158 lo.file_path = dp;
1159 lo.file_path_length = dp_size;
1160 lo.attributes = LOAD_OPTION_ACTIVE;
1161 lo.optional_data = optional_data;
1162 lo.label = label;
1163
1164 size = efi_serialize_load_option(&lo, (u8 **)&p);
1165 if (!size)
1166 return EFI_INVALID_PARAMETER;
1167
1168 ret = efi_set_variable_int(varname, &efi_global_variable_guid,
1169 EFI_VARIABLE_NON_VOLATILE |
1170 EFI_VARIABLE_BOOTSERVICE_ACCESS |
1171 EFI_VARIABLE_RUNTIME_ACCESS,
1172 size, p, false);
1173 free(p);
1174
1175 return ret;
1176}
1177
1178/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001179 * create_boot_option_entry() - create boot option entry
1180 *
1181 * @efi_menu: pointer to the efimenu structure
1182 * @title: pointer to the entry title
1183 * @val: pointer to boot option label
1184 * @func: callback of each entry
1185 * @data: pointer to the data to be passed to each entry callback
1186 * Return: status code
1187 */
1188static efi_status_t create_boot_option_entry(struct efimenu *efi_menu, char *title, u16 *val,
1189 eficonfig_entry_func func, void *data)
1190{
1191 u32 len;
1192 char *p, *buf;
1193
1194 len = strlen(title) + 1;
1195 if (val)
1196 len += utf16_utf8_strlen(val);
1197 buf = calloc(1, len);
1198 if (!buf)
1199 return EFI_OUT_OF_RESOURCES;
1200
1201 strcpy(buf, title);
1202 if (val) {
1203 p = buf + strlen(title);
1204 utf16_utf8_strcpy(&p, val);
1205 }
1206
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001207 return eficonfig_append_menu_entry(efi_menu, buf, func, data);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001208}
1209
1210/**
1211 * prepare_file_selection_entry() - prepare file selection entry
1212 *
1213 * @efi_menu: pointer to the efimenu structure
1214 * @title: pointer to the title string
1215 * @file_info: pointer to the file info
1216 * Return: status code
1217 */
1218static efi_status_t prepare_file_selection_entry(struct efimenu *efi_menu, char *title,
1219 struct eficonfig_select_file_info *file_info)
1220{
1221 u32 len;
1222 efi_status_t ret;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001223 u16 *file_name = NULL, *p;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001224 efi_handle_t handle;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001225 char *devname;
1226
1227 devname = calloc(1, EFICONFIG_VOLUME_PATH_MAX + 1);
1228 if (!devname)
1229 return EFI_OUT_OF_RESOURCES;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001230
1231 /* get the device name only when the user already selected the file path */
1232 handle = efi_dp_find_obj(file_info->dp_volume, NULL, NULL);
1233 if (handle) {
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001234 ret = efi_disk_get_device_name(handle, devname, EFICONFIG_VOLUME_PATH_MAX);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001235 if (ret != EFI_SUCCESS)
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001236 goto out;
1237 }
1238
1239 /*
1240 * If the preconfigured volume does not exist in the system, display the text
1241 * converted volume device path instead of U-Boot friendly name(e.g. "usb 0:1").
1242 */
1243 if (!handle && file_info->dp_volume) {
1244 u16 *dp_str;
1245 char *q = devname;
1246
1247 dp_str = efi_dp_str(file_info->dp_volume);
1248 if (dp_str)
1249 utf16_utf8_strncpy(&q, dp_str, EFICONFIG_VOLUME_PATH_MAX);
1250
1251 efi_free_pool(dp_str);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001252 }
1253
1254 /* append u'/' to devname, it is just for display purpose. */
1255 if (file_info->current_path[0] != u'\0' && file_info->current_path[0] != u'/')
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001256 strlcat(devname, "/", EFICONFIG_VOLUME_PATH_MAX + 1);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001257
1258 len = strlen(devname);
1259 len += utf16_utf8_strlen(file_info->current_path) + 1;
1260 file_name = calloc(1, len * sizeof(u16));
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001261 if (!file_name) {
1262 ret = EFI_OUT_OF_RESOURCES;
1263 goto out;
1264 }
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001265
1266 p = file_name;
1267 utf8_utf16_strcpy(&p, devname);
1268 u16_strlcat(file_name, file_info->current_path, len);
1269 ret = create_boot_option_entry(efi_menu, title, file_name,
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001270 eficonfig_process_show_file_option, file_info);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001271out:
1272 free(devname);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001273 free(file_name);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001274
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001275 return ret;
1276}
1277
1278/**
1279 * eficonfig_show_boot_option() - prepare menu entry for editing boot option
1280 *
1281 * Construct the structures to create edit boot option menu
1282 *
1283 * @bo: pointer to the boot option
1284 * @header_str: pointer to the header string
1285 * Return: status code
1286 */
1287static efi_status_t eficonfig_show_boot_option(struct eficonfig_boot_option *bo,
1288 char *header_str)
1289{
1290 efi_status_t ret;
1291 struct efimenu *efi_menu;
1292
1293 efi_menu = calloc(1, sizeof(struct efimenu));
1294 if (!efi_menu)
1295 return EFI_OUT_OF_RESOURCES;
1296
1297 INIT_LIST_HEAD(&efi_menu->list);
1298
1299 ret = create_boot_option_entry(efi_menu, "Description: ", bo->description,
1300 eficonfig_boot_add_enter_description, bo);
1301 if (ret != EFI_SUCCESS)
1302 goto out;
1303
1304 ret = prepare_file_selection_entry(efi_menu, "File: ", &bo->file_info);
1305 if (ret != EFI_SUCCESS)
1306 goto out;
1307
1308 ret = prepare_file_selection_entry(efi_menu, "Initrd File: ", &bo->initrd_info);
1309 if (ret != EFI_SUCCESS)
1310 goto out;
1311
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001312 ret = prepare_file_selection_entry(efi_menu, "Fdt File: ", &bo->fdt_info);
1313 if (ret != EFI_SUCCESS)
1314 goto out;
1315
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001316 ret = create_boot_option_entry(efi_menu, "Optional Data: ", bo->optional_data,
1317 eficonfig_boot_add_optional_data, bo);
1318 if (ret != EFI_SUCCESS)
1319 goto out;
1320
1321 ret = create_boot_option_entry(efi_menu, "Save", NULL,
1322 eficonfig_boot_edit_save, bo);
1323 if (ret != EFI_SUCCESS)
1324 goto out;
1325
1326 ret = create_boot_option_entry(efi_menu, "Quit", NULL,
1327 eficonfig_process_quit, NULL);
1328 if (ret != EFI_SUCCESS)
1329 goto out;
1330
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001331 ret = eficonfig_process_common(efi_menu, header_str,
1332 eficonfig_menu_desc,
1333 eficonfig_display_statusline,
1334 eficonfig_print_entry,
1335 eficonfig_choice_entry);
1336
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001337out:
1338 eficonfig_destroy(efi_menu);
1339
1340 return ret;
1341}
1342
1343/**
1344 * fill_file_info() - fill the file info from efi_device_path structure
1345 *
1346 * @dp: pointer to the device path
1347 * @file_info: pointer to the file info structure
1348 * @device_dp: pointer to the volume device path
1349 */
1350static void fill_file_info(struct efi_device_path *dp,
1351 struct eficonfig_select_file_info *file_info,
1352 struct efi_device_path *device_dp)
1353{
1354 u16 *file_str, *p;
1355 struct efi_device_path *file_dp = NULL;
1356
1357 efi_dp_split_file_path(dp, &device_dp, &file_dp);
1358 file_info->dp_volume = device_dp;
1359
1360 if (file_dp) {
1361 file_str = efi_dp_str(file_dp);
1362 /*
1363 * efi_convert_device_path_to_text() automatically adds u'/' at the
1364 * beginning of file name, remove u'/' before copying to current_path
1365 */
1366 p = file_str;
1367 if (p[0] == u'/')
1368 p++;
1369
1370 u16_strcpy(file_info->current_path, p);
1371 efi_free_pool(file_dp);
1372 efi_free_pool(file_str);
1373 }
1374}
1375
1376/**
1377 * eficonfig_edit_boot_option() - prepare boot option structure for editing
1378 *
1379 * Construct the boot option structure and copy the existing value
1380 *
1381 * @varname: pointer to the UEFI variable name
1382 * @bo: pointer to the boot option
1383 * @load_option: pointer to the load option
1384 * @load_option_size: size of the load option
1385 * @header_str: pointer to the header string
1386 * Return : status code
1387 */
1388static efi_status_t eficonfig_edit_boot_option(u16 *varname, struct eficonfig_boot_option *bo,
1389 void *load_option, efi_uintn_t load_option_size,
1390 char *header_str)
1391{
1392 size_t len;
1393 efi_status_t ret;
1394 char *tmp = NULL, *p;
1395 struct efi_load_option lo = {0};
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001396 efi_uintn_t dp_size;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001397 struct efi_device_path *dp = NULL;
1398 efi_uintn_t size = load_option_size;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001399 struct efi_device_path *device_dp = NULL;
1400 struct efi_device_path *initrd_dp = NULL;
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001401 struct efi_device_path *fdt_dp = NULL;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001402 struct efi_device_path *initrd_device_dp = NULL;
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001403 struct efi_device_path *fdt_device_dp = NULL;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001404
Heinrich Schuchardt40da9e62024-06-10 10:00:01 +02001405 const struct efi_lo_dp_prefix initrd_prefix = {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001406 .vendor = {
1407 {
1408 DEVICE_PATH_TYPE_MEDIA_DEVICE,
1409 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001410 sizeof(initrd_prefix.vendor),
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001411 },
1412 EFI_INITRD_MEDIA_GUID,
1413 },
1414 .end = {
1415 DEVICE_PATH_TYPE_END,
1416 DEVICE_PATH_SUB_TYPE_END,
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001417 sizeof(initrd_prefix.end),
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001418 }
1419 };
1420
Heinrich Schuchardt40da9e62024-06-10 10:00:01 +02001421 const struct efi_lo_dp_prefix fdt_prefix = {
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001422 .vendor = {
1423 {
1424 DEVICE_PATH_TYPE_MEDIA_DEVICE,
1425 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
1426 sizeof(fdt_prefix.vendor),
1427 },
1428 EFI_FDT_GUID,
1429 },
1430 .end = {
1431 DEVICE_PATH_TYPE_END,
1432 DEVICE_PATH_SUB_TYPE_END,
1433 sizeof(initrd_prefix.end),
1434 }
1435 };
1436
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001437 bo->file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1438 if (!bo->file_info.current_path) {
1439 ret = EFI_OUT_OF_RESOURCES;
1440 goto out;
1441 }
1442
1443 bo->initrd_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
Heinrich Schuchardtfc22ac22024-04-17 18:01:25 +02001444 if (!bo->initrd_info.current_path) {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001445 ret = EFI_OUT_OF_RESOURCES;
1446 goto out;
1447 }
1448
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001449 bo->fdt_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1450 if (!bo->fdt_info.current_path) {
1451 ret = EFI_OUT_OF_RESOURCES;
1452 goto out;
1453 }
1454
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001455 bo->description = calloc(1, EFICONFIG_DESCRIPTION_MAX * sizeof(u16));
1456 if (!bo->description) {
1457 ret = EFI_OUT_OF_RESOURCES;
1458 goto out;
1459 }
1460
1461 bo->optional_data = calloc(1, EFICONFIG_OPTIONAL_DATA_MAX * sizeof(u16));
1462 if (!bo->optional_data) {
1463 ret = EFI_OUT_OF_RESOURCES;
1464 goto out;
1465 }
1466
1467 /* copy the preset value */
1468 if (load_option) {
1469 ret = efi_deserialize_load_option(&lo, load_option, &size);
1470 if (ret != EFI_SUCCESS)
1471 goto out;
1472
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001473 if (!lo.label) {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001474 ret = EFI_INVALID_PARAMETER;
1475 goto out;
1476 }
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001477 /* truncate the long label string */
1478 if (u16_strlen(lo.label) >= EFICONFIG_DESCRIPTION_MAX)
1479 lo.label[EFICONFIG_DESCRIPTION_MAX - 1] = u'\0';
1480
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001481 u16_strcpy(bo->description, lo.label);
1482
1483 /* EFI image file path is a first instance */
1484 if (lo.file_path)
1485 fill_file_info(lo.file_path, &bo->file_info, device_dp);
1486
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001487 /* Initrd file path (optional) is placed at second instance. */
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001488 initrd_dp = efi_dp_from_lo(&lo, &efi_lf2_initrd_guid);
1489 if (initrd_dp) {
1490 fill_file_info(initrd_dp, &bo->initrd_info, initrd_device_dp);
1491 efi_free_pool(initrd_dp);
1492 }
1493
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001494 /* Fdt file path (optional) is placed as third instance. */
1495 fdt_dp = efi_dp_from_lo(&lo, &efi_guid_fdt);
1496 if (fdt_dp) {
1497 fill_file_info(fdt_dp, &bo->fdt_info, fdt_device_dp);
1498 efi_free_pool(fdt_dp);
1499 }
1500
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001501 if (size > 0)
1502 memcpy(bo->optional_data, lo.optional_data, size);
1503 }
1504
1505 while (1) {
1506 ret = eficonfig_show_boot_option(bo, header_str);
1507 if (ret == EFI_SUCCESS && bo->edit_completed)
1508 break;
1509 if (ret == EFI_NOT_READY)
1510 continue;
1511 if (ret != EFI_SUCCESS)
1512 goto out;
1513 }
1514
1515 if (bo->initrd_info.dp_volume) {
Masahisa Kojima3360183a2022-11-20 09:21:16 +09001516 dp = eficonfig_create_device_path(bo->initrd_info.dp_volume,
1517 bo->initrd_info.current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001518 if (!dp) {
1519 ret = EFI_OUT_OF_RESOURCES;
1520 goto out;
1521 }
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001522 initrd_dp = efi_dp_concat((const struct efi_device_path *)&initrd_prefix,
Heinrich Schuchardtf8de0092024-05-24 14:54:26 +02001523 dp, 0);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001524 efi_free_pool(dp);
1525 }
1526
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001527 if (bo->fdt_info.dp_volume) {
1528 dp = eficonfig_create_device_path(bo->fdt_info.dp_volume,
1529 bo->fdt_info.current_path);
1530 if (!dp) {
1531 ret = EFI_OUT_OF_RESOURCES;
1532 goto out;
1533 }
1534 fdt_dp = efi_dp_concat((const struct efi_device_path *)&fdt_prefix,
1535 dp, 0);
1536 efi_free_pool(dp);
1537 }
1538
Masahisa Kojima3360183a2022-11-20 09:21:16 +09001539 dp = eficonfig_create_device_path(bo->file_info.dp_volume, bo->file_info.current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001540 if (!dp) {
1541 ret = EFI_OUT_OF_RESOURCES;
1542 goto out;
1543 }
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001544
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001545 ret = efi_load_option_dp_join(&dp, &dp_size, initrd_dp, fdt_dp);
1546 if (ret != EFI_SUCCESS)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001547 goto out;
1548
1549 if (utf16_utf8_strlen(bo->optional_data)) {
1550 len = utf16_utf8_strlen(bo->optional_data) + 1;
1551 tmp = calloc(1, len);
1552 if (!tmp)
1553 goto out;
1554 p = tmp;
1555 utf16_utf8_strncpy(&p, bo->optional_data, u16_strlen(bo->optional_data));
1556 }
1557
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001558 ret = eficonfig_set_boot_option(varname, dp, dp_size, bo->description, tmp);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001559out:
1560 free(tmp);
1561 free(bo->optional_data);
1562 free(bo->description);
1563 free(bo->file_info.current_path);
1564 free(bo->initrd_info.current_path);
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001565 free(bo->fdt_info.current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001566 efi_free_pool(device_dp);
1567 efi_free_pool(initrd_device_dp);
1568 efi_free_pool(initrd_dp);
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001569 efi_free_pool(fdt_device_dp);
1570 efi_free_pool(fdt_dp);
1571 efi_free_pool(dp);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001572
1573 return ret;
1574}
1575
1576/**
1577 * eficonfig_process_add_boot_option() - handler to add boot option
1578 *
1579 * @data: pointer to the data for each entry
1580 * Return: status code
1581 */
1582static efi_status_t eficonfig_process_add_boot_option(void *data)
1583{
1584 u16 varname[9];
1585 efi_status_t ret;
1586 struct eficonfig_boot_option *bo = NULL;
1587
1588 bo = calloc(1, sizeof(struct eficonfig_boot_option));
1589 if (!bo)
1590 return EFI_OUT_OF_RESOURCES;
1591
Raymond Mao70a76c52023-06-19 14:22:58 -07001592 ret = efi_bootmgr_get_unused_bootoption(varname, sizeof(varname), &bo->boot_index);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001593 if (ret != EFI_SUCCESS)
1594 return ret;
1595
1596 ret = eficonfig_edit_boot_option(varname, bo, NULL, 0, " ** Add Boot Option ** ");
1597 if (ret != EFI_SUCCESS)
1598 goto out;
1599
Raymond Mao70a76c52023-06-19 14:22:58 -07001600 ret = efi_bootmgr_append_bootorder((u16)bo->boot_index);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001601 if (ret != EFI_SUCCESS)
1602 goto out;
1603
1604out:
1605 free(bo);
1606
1607 /* to stay the parent menu */
1608 ret = (ret == EFI_ABORTED) ? EFI_SUCCESS : ret;
1609
1610 return ret;
1611}
1612
1613/**
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001614 * eficonfig_process_boot_selected() - handler to select boot option entry
1615 *
1616 * @data: pointer to the data for each entry
1617 * Return: status code
1618 */
1619static efi_status_t eficonfig_process_boot_selected(void *data)
1620{
1621 struct eficonfig_boot_selection_data *info = data;
1622
1623 if (info)
1624 *info->selected = info->boot_index;
1625
1626 return EFI_SUCCESS;
1627}
1628
1629/**
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001630 * eficonfig_add_boot_selection_entry() - add boot option menu entry
1631 *
1632 * @efi_menu: pointer to store the efimenu structure
1633 * @boot_index: boot option index to be added
1634 * @selected: pointer to store the selected boot option index
1635 * Return: status code
1636 */
1637static efi_status_t eficonfig_add_boot_selection_entry(struct efimenu *efi_menu,
1638 unsigned int boot_index,
1639 unsigned int *selected)
1640{
1641 char *buf, *p;
1642 efi_status_t ret;
1643 efi_uintn_t size;
1644 void *load_option;
1645 struct efi_load_option lo;
1646 u16 varname[] = u"Boot####";
1647 struct eficonfig_boot_selection_data *info;
1648
1649 efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
1650 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
1651 if (!load_option)
1652 return EFI_SUCCESS;
1653
1654 ret = efi_deserialize_load_option(&lo, load_option, &size);
1655 if (ret != EFI_SUCCESS) {
1656 log_warning("Invalid load option for %ls\n", varname);
1657 free(load_option);
1658 return ret;
1659 }
1660
1661 if (size >= sizeof(efi_guid_t) &&
1662 !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) {
1663 /*
1664 * auto generated entry has GUID in optional_data,
1665 * skip auto generated entry because it will be generated
1666 * again even if it is edited or deleted.
1667 */
1668 free(load_option);
1669 return EFI_SUCCESS;
1670 }
1671
1672 info = calloc(1, sizeof(struct eficonfig_boot_selection_data));
1673 if (!info) {
1674 free(load_option);
1675 return EFI_OUT_OF_RESOURCES;
1676 }
1677
1678 buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
1679 if (!buf) {
1680 free(load_option);
1681 free(info);
1682 return EFI_OUT_OF_RESOURCES;
1683 }
1684 p = buf;
1685 utf16_utf8_strcpy(&p, lo.label);
1686 info->boot_index = boot_index;
1687 info->selected = selected;
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001688 ret = eficonfig_append_menu_entry(efi_menu, buf, eficonfig_process_boot_selected, info);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001689 if (ret != EFI_SUCCESS) {
1690 free(load_option);
1691 free(info);
1692 return ret;
1693 }
1694 free(load_option);
1695
1696 return EFI_SUCCESS;
1697}
1698
1699/**
1700 * eficonfig_show_boot_selection() - construct boot option menu entry
1701 *
1702 * @selected: pointer to store the selected boot option index
1703 * Return: status code
1704 */
1705static efi_status_t eficonfig_show_boot_selection(unsigned int *selected)
1706{
1707 u32 i;
1708 u16 *bootorder;
1709 efi_status_t ret;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001710 u16 *var_name16 = NULL;
Masahisa Kojimad414f572022-12-02 13:59:36 +09001711 efi_uintn_t num, size, buf_size;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001712 struct efimenu *efi_menu;
1713 struct list_head *pos, *n;
1714 struct eficonfig_entry *entry;
1715
1716 efi_menu = calloc(1, sizeof(struct efimenu));
1717 if (!efi_menu)
1718 return EFI_OUT_OF_RESOURCES;
1719
1720 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
1721
1722 INIT_LIST_HEAD(&efi_menu->list);
1723 num = size / sizeof(u16);
1724 /* list the load option in the order of BootOrder variable */
1725 for (i = 0; i < num; i++) {
1726 ret = eficonfig_add_boot_selection_entry(efi_menu, bootorder[i], selected);
1727 if (ret != EFI_SUCCESS)
1728 goto out;
1729
1730 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
1731 break;
1732 }
1733
1734 /* list the remaining load option not included in the BootOrder */
Masahisa Kojimad414f572022-12-02 13:59:36 +09001735 buf_size = 128;
1736 var_name16 = malloc(buf_size);
1737 if (!var_name16)
1738 return EFI_OUT_OF_RESOURCES;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001739
Masahisa Kojimad414f572022-12-02 13:59:36 +09001740 var_name16[0] = 0;
1741 for (;;) {
1742 int index;
1743 efi_guid_t guid;
1744
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001745 ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
Masahisa Kojimad414f572022-12-02 13:59:36 +09001746 if (ret == EFI_NOT_FOUND)
1747 break;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001748 if (ret != EFI_SUCCESS)
1749 goto out;
1750
Masahisa Kojimad414f572022-12-02 13:59:36 +09001751 if (efi_varname_is_load_option(var_name16, &index)) {
1752 /* If the index is included in the BootOrder, skip it */
Raymond Mao70a76c52023-06-19 14:22:58 -07001753 if (efi_search_bootorder(bootorder, num, index, NULL))
Masahisa Kojimad414f572022-12-02 13:59:36 +09001754 continue;
1755
1756 ret = eficonfig_add_boot_selection_entry(efi_menu, index, selected);
1757 if (ret != EFI_SUCCESS)
1758 goto out;
1759 }
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001760
1761 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
1762 break;
1763 }
1764
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001765 ret = eficonfig_append_quit_entry(efi_menu);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001766 if (ret != EFI_SUCCESS)
1767 goto out;
1768
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001769 ret = eficonfig_process_common(efi_menu, " ** Select Boot Option **",
1770 eficonfig_menu_desc,
1771 eficonfig_display_statusline,
1772 eficonfig_print_entry,
1773 eficonfig_choice_entry);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001774out:
1775 list_for_each_safe(pos, n, &efi_menu->list) {
1776 entry = list_entry(pos, struct eficonfig_entry, list);
1777 free(entry->data);
1778 }
1779 eficonfig_destroy(efi_menu);
1780
Masahisa Kojimad414f572022-12-02 13:59:36 +09001781 free(var_name16);
1782
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001783 return ret;
1784}
1785
1786/**
1787 * eficonfig_process_edit_boot_option() - handler to edit boot option
1788 *
1789 * @data: pointer to the data for each entry
1790 * Return: status code
1791 */
1792static efi_status_t eficonfig_process_edit_boot_option(void *data)
1793{
1794 efi_status_t ret;
1795 efi_uintn_t size;
1796 struct eficonfig_boot_option *bo = NULL;
1797
1798 while (1) {
1799 unsigned int selected;
1800 void *load_option;
1801 u16 varname[] = u"Boot####";
1802
1803 ret = eficonfig_show_boot_selection(&selected);
1804 if (ret != EFI_SUCCESS)
1805 break;
1806
1807 bo = calloc(1, sizeof(struct eficonfig_boot_option));
1808 if (!bo) {
1809 ret = EFI_OUT_OF_RESOURCES;
1810 goto out;
1811 }
1812
1813 bo->boot_index = selected;
1814 efi_create_indexed_name(varname, sizeof(varname), "Boot", selected);
1815 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
1816 if (!load_option) {
1817 free(bo);
1818 ret = EFI_NOT_FOUND;
1819 goto out;
1820 }
1821
1822 ret = eficonfig_edit_boot_option(varname, bo, load_option, size,
1823 " ** Edit Boot Option ** ");
1824
1825 free(load_option);
1826 free(bo);
1827 if (ret != EFI_SUCCESS && ret != EFI_ABORTED)
1828 break;
1829 }
1830out:
1831 /* to stay the parent menu */
1832 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
1833
1834 return ret;
1835}
1836
1837/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001838 * eficonfig_print_change_boot_order_entry() - print the boot option entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001839 *
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001840 * @data: pointer to the data associated with each menu entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001841 */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001842static void eficonfig_print_change_boot_order_entry(void *data)
Masahisa Kojimae8753692022-09-12 17:33:56 +09001843{
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001844 struct eficonfig_entry *entry = data;
1845 bool reverse = (entry->efi_menu->active == entry->num);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001846
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001847 if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num)
1848 return;
1849
1850 printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE,
1851 (entry->num - entry->efi_menu->start) + EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001852
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001853 if (reverse)
1854 puts(ANSI_COLOR_REVERSE);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001855
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001856 if (entry->num < entry->efi_menu->count - 2) {
1857 if (((struct eficonfig_boot_order_data *)entry->data)->active)
1858 printf("[*] ");
1859 else
1860 printf("[ ] ");
1861 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09001862
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001863 printf("%s", entry->title);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001864
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001865 if (reverse)
1866 puts(ANSI_COLOR_RESET);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001867}
1868
1869/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001870 * eficonfig_choice_change_boot_order() - user key input handler
Masahisa Kojimae8753692022-09-12 17:33:56 +09001871 *
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001872 * @data: pointer to the menu entry
1873 * Return: key string to identify the selected entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001874 */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001875char *eficonfig_choice_change_boot_order(void *data)
Masahisa Kojimae8753692022-09-12 17:33:56 +09001876{
Simon Glass9d8d3872023-01-06 08:52:26 -06001877 struct cli_ch_state s_cch, *cch = &s_cch;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001878 struct list_head *pos, *n;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001879 struct efimenu *efi_menu = data;
Simon Glass05ecdf82023-01-06 08:52:22 -06001880 enum bootmenu_key key = BKEY_NONE;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001881 struct eficonfig_entry *entry, *tmp;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001882
Simon Glass9d8d3872023-01-06 08:52:26 -06001883 cli_ch_init(cch);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001884 while (1) {
Simon Glass9d8d3872023-01-06 08:52:26 -06001885 key = bootmenu_loop(NULL, cch);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001886
1887 switch (key) {
Simon Glass05ecdf82023-01-06 08:52:22 -06001888 case BKEY_PLUS:
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001889 if (efi_menu->active > 0 &&
1890 efi_menu->active < efi_menu->count - 2) {
Masahisa Kojimae8753692022-09-12 17:33:56 +09001891 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001892 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001893 if (entry->num == efi_menu->active)
1894 break;
1895 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001896 tmp = list_entry(pos->prev, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001897 entry->num--;
1898 tmp->num++;
1899 list_del(&tmp->list);
1900 list_add(&tmp->list, &entry->list);
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001901
1902 eficonfig_menu_up(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001903 }
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001904 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06001905 case BKEY_UP:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001906 if (efi_menu->active > 0)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001907 eficonfig_menu_up(efi_menu);
1908
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001909 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06001910 case BKEY_MINUS:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001911 if (efi_menu->active < efi_menu->count - 3) {
1912 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001913 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001914 if (entry->num == efi_menu->active)
1915 break;
1916 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001917 tmp = list_entry(pos->next, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001918 entry->num++;
1919 tmp->num--;
1920 list_del(&entry->list);
1921 list_add(&entry->list, &tmp->list);
1922
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001923 eficonfig_menu_down(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001924 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001925 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06001926 case BKEY_DOWN:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001927 if (efi_menu->active < efi_menu->count - 1)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001928 eficonfig_menu_down(efi_menu);
1929
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001930 return NULL;
Masahisa Kojimad38ecb72023-02-02 18:24:44 +09001931 case BKEY_SAVE:
1932 /* force to select "Save" entry */
1933 efi_menu->active = efi_menu->count - 2;
1934 fallthrough;
Simon Glass05ecdf82023-01-06 08:52:22 -06001935 case BKEY_SELECT:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001936 /* "Save" */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001937 if (efi_menu->active == efi_menu->count - 2) {
1938 list_for_each_prev_safe(pos, n, &efi_menu->list) {
1939 entry = list_entry(pos, struct eficonfig_entry, list);
1940 if (entry->num == efi_menu->active)
1941 break;
1942 }
1943 return entry->key;
1944 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09001945 /* "Quit" */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001946 if (efi_menu->active == efi_menu->count - 1) {
1947 entry = list_last_entry(&efi_menu->list,
1948 struct eficonfig_entry,
1949 list);
1950 return entry->key;
1951 }
1952 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09001953 break;
Simon Glass05ecdf82023-01-06 08:52:22 -06001954 case BKEY_SPACE:
Masahisa Kojimae8753692022-09-12 17:33:56 +09001955 if (efi_menu->active < efi_menu->count - 2) {
1956 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001957 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001958 if (entry->num == efi_menu->active) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09001959 struct eficonfig_boot_order_data *data = entry->data;
1960
1961 data->active = !data->active;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001962 return NULL;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001963 }
1964 }
1965 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001966 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09001967 break;
Simon Glass05ecdf82023-01-06 08:52:22 -06001968 case BKEY_QUIT:
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001969 entry = list_last_entry(&efi_menu->list,
1970 struct eficonfig_entry, list);
1971 return entry->key;
Masahisa Kojimae8753692022-09-12 17:33:56 +09001972 default:
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001973 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09001974 break;
1975 }
1976 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001977}
1978
1979/**
1980 * eficonfig_process_save_boot_order() - callback function for "Save" entry
1981 *
1982 * @data: pointer to the data
1983 * Return: status code
1984 */
1985static efi_status_t eficonfig_process_save_boot_order(void *data)
1986{
1987 u32 count = 0;
1988 efi_status_t ret;
1989 efi_uintn_t size;
1990 struct list_head *pos, *n;
1991 u16 *new_bootorder;
1992 struct efimenu *efi_menu;
1993 struct eficonfig_entry *entry;
1994 struct eficonfig_save_boot_order_data *save_data = data;
1995
1996 efi_menu = save_data->efi_menu;
1997
1998 /*
1999 * The change boot order menu always has "Save" and "Quit" entries.
2000 * !(efi_menu->count - 2) means there is no user defined boot option.
2001 */
2002 if (!(efi_menu->count - 2))
2003 return EFI_SUCCESS;
2004
2005 new_bootorder = calloc(1, (efi_menu->count - 2) * sizeof(u16));
2006 if (!new_bootorder) {
2007 ret = EFI_OUT_OF_RESOURCES;
2008 goto out;
2009 }
2010
2011 /* create new BootOrder */
2012 count = 0;
2013 list_for_each_safe(pos, n, &efi_menu->list) {
2014 struct eficonfig_boot_order_data *data;
2015
2016 entry = list_entry(pos, struct eficonfig_entry, list);
2017 /* exit the loop when iteration reaches "Save" */
2018 if (!strncmp(entry->title, "Save", strlen("Save")))
2019 break;
2020
2021 data = entry->data;
2022 if (data->active)
2023 new_bootorder[count++] = data->boot_index;
2024 }
2025
2026 size = count * sizeof(u16);
2027 ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
2028 EFI_VARIABLE_NON_VOLATILE |
2029 EFI_VARIABLE_BOOTSERVICE_ACCESS |
2030 EFI_VARIABLE_RUNTIME_ACCESS,
2031 size, new_bootorder, false);
2032
2033 save_data->selected = true;
2034out:
2035 free(new_bootorder);
2036
2037 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002038}
2039
2040/**
2041 * eficonfig_add_change_boot_order_entry() - add boot order entry
2042 *
2043 * @efi_menu: pointer to the efimenu structure
2044 * @boot_index: boot option index to be added
2045 * @active: flag to include the boot option into BootOrder
2046 * Return: status code
2047 */
2048static efi_status_t eficonfig_add_change_boot_order_entry(struct efimenu *efi_menu,
2049 u32 boot_index, bool active)
2050{
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002051 char *title, *p;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002052 efi_status_t ret;
2053 efi_uintn_t size;
2054 void *load_option;
2055 struct efi_load_option lo;
2056 u16 varname[] = u"Boot####";
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002057 struct eficonfig_boot_order_data *data;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002058
2059 efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
2060 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
2061 if (!load_option)
2062 return EFI_SUCCESS;
2063
2064 ret = efi_deserialize_load_option(&lo, load_option, &size);
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002065 if (ret != EFI_SUCCESS)
2066 goto out;
2067
2068 data = calloc(1, sizeof(*data));
2069 if (!data) {
2070 ret = EFI_OUT_OF_RESOURCES;
2071 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002072 }
2073
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002074 title = calloc(1, utf16_utf8_strlen(lo.label) + 1);
2075 if (!title) {
2076 free(data);
2077 ret = EFI_OUT_OF_RESOURCES;
2078 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002079 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002080 p = title;
2081 utf16_utf8_strcpy(&p, lo.label);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002082
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002083 data->boot_index = boot_index;
2084 data->active = active;
2085
2086 ret = eficonfig_append_menu_entry(efi_menu, title, NULL, data);
2087 if (ret != EFI_SUCCESS) {
2088 free(data);
2089 free(title);
2090 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002091 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09002092
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002093out:
Masahisa Kojimae8753692022-09-12 17:33:56 +09002094 free(load_option);
2095
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002096 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002097}
2098
2099/**
2100 * eficonfig_create_change_boot_order_entry() - create boot order entry
2101 *
2102 * @efi_menu: pointer to the efimenu structure
2103 * @bootorder: pointer to the BootOrder variable
2104 * @num: number of BootOrder entry
2105 * Return: status code
2106 */
2107static efi_status_t eficonfig_create_change_boot_order_entry(struct efimenu *efi_menu,
2108 u16 *bootorder, efi_uintn_t num)
2109{
2110 u32 i;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002111 char *title;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002112 efi_status_t ret;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002113 u16 *var_name16 = NULL;
Masahisa Kojimad414f572022-12-02 13:59:36 +09002114 efi_uintn_t size, buf_size;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002115 struct eficonfig_save_boot_order_data *save_data;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002116
2117 /* list the load option in the order of BootOrder variable */
2118 for (i = 0; i < num; i++) {
2119 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
2120 break;
2121
2122 ret = eficonfig_add_change_boot_order_entry(efi_menu, bootorder[i], true);
2123 if (ret != EFI_SUCCESS)
2124 goto out;
2125 }
2126
2127 /* list the remaining load option not included in the BootOrder */
Masahisa Kojimad414f572022-12-02 13:59:36 +09002128 buf_size = 128;
2129 var_name16 = malloc(buf_size);
2130 if (!var_name16)
2131 return EFI_OUT_OF_RESOURCES;
2132
2133 var_name16[0] = 0;
2134 for (;;) {
2135 int index;
2136 efi_guid_t guid;
2137
Masahisa Kojimae8753692022-09-12 17:33:56 +09002138 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
2139 break;
2140
Masahisa Kojimad414f572022-12-02 13:59:36 +09002141 size = buf_size;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002142 ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
Masahisa Kojimad414f572022-12-02 13:59:36 +09002143 if (ret == EFI_NOT_FOUND)
2144 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002145 if (ret != EFI_SUCCESS)
2146 goto out;
Masahisa Kojimad414f572022-12-02 13:59:36 +09002147
2148 if (efi_varname_is_load_option(var_name16, &index)) {
2149 /* If the index is included in the BootOrder, skip it */
Raymond Mao70a76c52023-06-19 14:22:58 -07002150 if (efi_search_bootorder(bootorder, num, index, NULL))
Masahisa Kojimad414f572022-12-02 13:59:36 +09002151 continue;
2152
2153 ret = eficonfig_add_change_boot_order_entry(efi_menu, index, false);
2154 if (ret != EFI_SUCCESS)
2155 goto out;
2156 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09002157 }
2158
2159 /* add "Save" and "Quit" entries */
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002160 title = strdup("Save");
2161 if (!title) {
2162 ret = EFI_OUT_OF_RESOURCES;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002163 goto out;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002164 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002165
2166 save_data = malloc(sizeof(struct eficonfig_save_boot_order_data));
2167 if (!save_data) {
2168 ret = EFI_OUT_OF_RESOURCES;
2169 goto out;
2170 }
2171 save_data->efi_menu = efi_menu;
2172 save_data->selected = false;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002173
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002174 ret = eficonfig_append_menu_entry(efi_menu, title,
2175 eficonfig_process_save_boot_order,
2176 save_data);
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002177 if (ret != EFI_SUCCESS)
Masahisa Kojimae8753692022-09-12 17:33:56 +09002178 goto out;
2179
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002180 ret = eficonfig_append_quit_entry(efi_menu);
2181 if (ret != EFI_SUCCESS)
2182 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002183
2184 efi_menu->active = 0;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002185out:
Masahisa Kojimad414f572022-12-02 13:59:36 +09002186 free(var_name16);
2187
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002188 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002189}
2190
2191/**
2192 * eficonfig_process_change_boot_order() - handler to change boot order
2193 *
2194 * @data: pointer to the data for each entry
2195 * Return: status code
2196 */
2197static efi_status_t eficonfig_process_change_boot_order(void *data)
2198{
Masahisa Kojimae8753692022-09-12 17:33:56 +09002199 u16 *bootorder;
2200 efi_status_t ret;
2201 efi_uintn_t num, size;
2202 struct list_head *pos, *n;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002203 struct eficonfig_entry *entry;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002204 struct efimenu *efi_menu;
2205
2206 efi_menu = calloc(1, sizeof(struct efimenu));
2207 if (!efi_menu)
2208 return EFI_OUT_OF_RESOURCES;
2209
2210 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
2211
2212 INIT_LIST_HEAD(&efi_menu->list);
2213 num = size / sizeof(u16);
2214 ret = eficonfig_create_change_boot_order_entry(efi_menu, bootorder, num);
2215 if (ret != EFI_SUCCESS)
2216 goto out;
2217
2218 while (1) {
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002219 ret = eficonfig_process_common(efi_menu,
2220 " ** Change Boot Order **",
2221 eficonfig_change_boot_order_desc,
2222 eficonfig_display_statusline,
2223 eficonfig_print_change_boot_order_entry,
2224 eficonfig_choice_change_boot_order);
2225 /* exit from the menu if user selects the "Save" entry. */
2226 if (ret == EFI_SUCCESS && efi_menu->active == (efi_menu->count - 2)) {
2227 list_for_each_prev_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002228 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002229 if (entry->num == efi_menu->active)
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002230 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002231 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002232 if (((struct eficonfig_save_boot_order_data *)entry->data)->selected)
2233 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002234 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002235 if (ret != EFI_SUCCESS)
2236 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002237 }
2238out:
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002239 free(bootorder);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002240 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002241 entry = list_entry(pos, struct eficonfig_entry, list);
2242 free(entry->data);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002243 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002244 eficonfig_destroy(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002245
2246 /* to stay the parent menu */
2247 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
2248
2249 return ret;
2250}
2251
2252/**
Masahisa Kojima703ad322022-09-12 17:33:53 +09002253 * eficonfig_process_delete_boot_option() - handler to delete boot option
2254 *
2255 * @data: pointer to the data for each entry
2256 * Return: status code
2257 */
2258static efi_status_t eficonfig_process_delete_boot_option(void *data)
2259{
2260 efi_status_t ret;
2261 unsigned int selected;
2262
2263 while (1) {
2264 ret = eficonfig_show_boot_selection(&selected);
2265 if (ret == EFI_SUCCESS)
Raymond Mao70a76c52023-06-19 14:22:58 -07002266 ret = efi_bootmgr_delete_boot_option(selected);
Masahisa Kojima703ad322022-09-12 17:33:53 +09002267
2268 if (ret != EFI_SUCCESS)
2269 break;
2270 }
2271
2272 /* to stay the parent menu */
2273 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
2274
2275 return ret;
2276}
2277
2278/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002279 * eficonfig_init() - do required initialization for eficonfig command
2280 *
2281 * Return: status code
2282 */
2283static efi_status_t eficonfig_init(void)
2284{
2285 efi_status_t ret = EFI_SUCCESS;
2286 static bool init;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002287 unsigned long columns, rows;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002288
2289 if (!init) {
Heinrich Schuchardtac8d48a2025-03-10 07:13:43 +01002290 cout = systab.con_out;
2291 cin = systab.con_in;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002292
2293 cout->query_mode(cout, cout->mode->mode, &columns, &rows);
2294 avail_row = rows - (EFICONFIG_MENU_HEADER_ROW_NUM +
2295 EFICONFIG_MENU_DESC_ROW_NUM);
2296 if (avail_row <= 0) {
2297 eficonfig_print_msg("Console size is too small!");
2298 return EFI_INVALID_PARAMETER;
2299 }
2300 /* TODO: Should we check the minimum column size? */
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002301 }
2302
2303 init = true;
2304
2305 return ret;
2306}
2307
2308static const struct eficonfig_item maintenance_menu_items[] = {
2309 {"Add Boot Option", eficonfig_process_add_boot_option},
Masahisa Kojimabb052b92022-09-12 17:33:51 +09002310 {"Edit Boot Option", eficonfig_process_edit_boot_option},
Masahisa Kojimae8753692022-09-12 17:33:56 +09002311 {"Change Boot Order", eficonfig_process_change_boot_order},
Masahisa Kojima703ad322022-09-12 17:33:53 +09002312 {"Delete Boot Option", eficonfig_process_delete_boot_option},
Simon Glasseba70582023-02-05 15:39:45 -07002313#if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT) && IS_ENABLED(CONFIG_EFI_MM_COMM_TEE))
Masahisa Kojimacbdbcb42022-11-20 09:21:18 +09002314 {"Secure Boot Configuration", eficonfig_process_secure_boot_config},
2315#endif
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002316 {"Quit", eficonfig_process_quit},
2317};
2318
2319/**
2320 * do_eficonfig() - execute `eficonfig` command
2321 *
2322 * @cmdtp: table entry describing command
2323 * @flag: bitmap indicating how the command was invoked
2324 * @argc: number of arguments
2325 * @argv: command line arguments
2326 * Return: status code
2327 */
2328static int do_eficonfig(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
2329{
2330 efi_status_t ret;
2331 struct efimenu *efi_menu;
2332
2333 if (argc > 1)
2334 return CMD_RET_USAGE;
2335
2336 ret = efi_init_obj_list();
2337 if (ret != EFI_SUCCESS) {
2338 log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
2339 ret & ~EFI_ERROR_MASK);
2340
2341 return CMD_RET_FAILURE;
2342 }
2343
2344 ret = eficonfig_init();
2345 if (ret != EFI_SUCCESS)
2346 return CMD_RET_FAILURE;
2347
Raymond Mao70a76c52023-06-19 14:22:58 -07002348 ret = efi_bootmgr_update_media_device_boot_option();
Raymond Maoa35784d2023-06-19 14:22:59 -07002349 if (ret != EFI_SUCCESS)
Masahisa Kojimaf648fa32022-09-12 17:33:55 +09002350 return ret;
2351
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002352 while (1) {
2353 efi_menu = eficonfig_create_fixed_menu(maintenance_menu_items,
2354 ARRAY_SIZE(maintenance_menu_items));
2355 if (!efi_menu)
2356 return CMD_RET_FAILURE;
2357
Masahisa Kojimafc811d12023-01-24 15:56:13 +09002358 ret = eficonfig_process_common(efi_menu,
2359 " ** UEFI Maintenance Menu **",
2360 eficonfig_menu_desc,
2361 eficonfig_display_statusline,
2362 eficonfig_print_entry,
2363 eficonfig_choice_entry);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002364 eficonfig_destroy(efi_menu);
2365
2366 if (ret == EFI_ABORTED)
2367 break;
2368 }
2369
2370 return CMD_RET_SUCCESS;
2371}
2372
2373U_BOOT_CMD(
2374 eficonfig, 1, 0, do_eficonfig,
2375 "provide menu-driven UEFI variable maintenance interface",
2376 ""
2377);