blob: 8ac0fb98e02e5f1b16420e521de414b341515e88 [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
Sughosh Ganu885f7a22025-07-03 12:13:07 +053038#define EFICONFIG_URI_MAX 512
Masahisa Kojimaa93282c2023-01-24 15:56:15 +090039#define EFICONFIG_MENU_HEADER_ROW_NUM 3
40#define EFICONFIG_MENU_DESC_ROW_NUM 5
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +090041
42/**
43 * struct eficonfig_filepath_info - structure to be used to store file path
44 *
45 * @name: file or directory name
46 * @list: list structure
47 */
48struct eficonfig_filepath_info {
49 char *name;
50 struct list_head list;
51};
52
53/**
54 * struct eficonfig_boot_option - structure to be used for updating UEFI boot option
55 *
56 * @file_info: user selected file info
57 * @initrd_info: user selected initrd file info
58 * @boot_index: index of the boot option
59 * @description: pointer to the description string
60 * @optional_data: pointer to the optional_data
61 * @edit_completed: flag indicates edit complete
62 */
63struct eficonfig_boot_option {
64 struct eficonfig_select_file_info file_info;
65 struct eficonfig_select_file_info initrd_info;
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +020066 struct eficonfig_select_file_info fdt_info;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +090067 unsigned int boot_index;
68 u16 *description;
69 u16 *optional_data;
70 bool edit_completed;
71};
72
73/**
74 * struct eficonfig_volume_entry_data - structure to be used to store volume info
75 *
76 * @file_info: pointer to file info structure
77 * @v: pointer to the protocol interface
78 * @dp: pointer to the device path
79 */
80struct eficonfig_volume_entry_data {
81 struct eficonfig_select_file_info *file_info;
82 struct efi_simple_file_system_protocol *v;
83 struct efi_device_path *dp;
84};
85
86/**
87 * struct eficonfig_file_entry_data - structure to be used to store file info
88 *
89 * @file_info: pointer to file info structure
90 * @is_directory: flag to identify the directory or file
91 * @file_name: name of directory or file
92 */
93struct eficonfig_file_entry_data {
94 struct eficonfig_select_file_info *file_info;
95 bool is_directory;
96 char *file_name;
97};
98
99/**
Masahisa Kojimabb052b92022-09-12 17:33:51 +0900100 * struct eficonfig_boot_selection_data - structure to be used to select the boot option entry
101 *
102 * @boot_index: index of the boot option
103 * @selected: pointer to store the selected index in the BootOrder variable
104 */
105struct eficonfig_boot_selection_data {
106 u16 boot_index;
107 int *selected;
108};
109
110/**
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +0900111 * struct eficonfig_boot_order_data - structure to be used to update BootOrder variable
Masahisa Kojimae8753692022-09-12 17:33:56 +0900112 *
Masahisa Kojimae8753692022-09-12 17:33:56 +0900113 * @boot_index: boot option index
114 * @active: flag to include the boot option into BootOrder variable
Masahisa Kojimae8753692022-09-12 17:33:56 +0900115 */
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +0900116struct eficonfig_boot_order_data {
Masahisa Kojimae8753692022-09-12 17:33:56 +0900117 u32 boot_index;
118 bool active;
Masahisa Kojimae8753692022-09-12 17:33:56 +0900119};
120
121/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +0900122 * struct eficonfig_save_boot_order_data - structure to be used to change boot order
123 *
124 * @efi_menu: pointer to efimenu structure
125 * @selected: flag to indicate user selects "Save" entry
126 */
127struct eficonfig_save_boot_order_data {
128 struct efimenu *efi_menu;
129 bool selected;
130};
131
132/**
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900133 * struct eficonfig_menu_adjust - update start and end entry index
134 *
135 * @efi_menu: pointer to efimenu structure
136 * @add: flag to add or substract the index
137 */
138static void eficonfig_menu_adjust(struct efimenu *efi_menu, bool add)
139{
140 if (add)
141 ++efi_menu->active;
142 else
143 --efi_menu->active;
144
145 if (add && efi_menu->end < efi_menu->active) {
146 efi_menu->start++;
147 efi_menu->end++;
148 } else if (!add && efi_menu->start > efi_menu->active) {
149 efi_menu->start--;
150 efi_menu->end--;
151 }
152}
153#define eficonfig_menu_up(_a) eficonfig_menu_adjust(_a, false)
154#define eficonfig_menu_down(_a) eficonfig_menu_adjust(_a, true)
155
156/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900157 * eficonfig_print_msg() - print message
158 *
159 * display the message to the user, user proceeds the screen
160 * with any key press.
161 *
162 * @items: pointer to the structure of each menu entry
163 * @count: the number of menu entry
164 * @menu_header: pointer to the menu header string
165 * Return: status code
166 */
167void eficonfig_print_msg(char *msg)
168{
169 /* Flush input */
170 while (tstc())
171 getchar();
172
173 printf(ANSI_CURSOR_HIDE
174 ANSI_CLEAR_CONSOLE
175 ANSI_CURSOR_POSITION
176 "%s\n\n Press any key to continue", 3, 4, msg);
177
178 getchar();
179}
180
181/**
182 * eficonfig_print_entry() - print each menu entry
183 *
184 * @data: pointer to the data associated with each menu entry
185 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900186void eficonfig_print_entry(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900187{
188 struct eficonfig_entry *entry = data;
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900189 bool reverse = (entry->efi_menu->active == entry->num);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900190
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900191 if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num)
192 return;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900193
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900194 printf(ANSI_CURSOR_POSITION, (entry->num - entry->efi_menu->start) +
195 EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900196
197 if (reverse)
198 puts(ANSI_COLOR_REVERSE);
199
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900200 printf(ANSI_CLEAR_LINE "%s", entry->title);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900201
202 if (reverse)
203 puts(ANSI_COLOR_RESET);
204}
205
206/**
207 * eficonfig_display_statusline() - print status line
208 *
209 * @m: pointer to the menu structure
210 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900211void eficonfig_display_statusline(struct menu *m)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900212{
213 struct eficonfig_entry *entry;
214
215 if (menu_default_choice(m, (void *)&entry) < 0)
216 return;
217
218 printf(ANSI_CURSOR_POSITION
219 "\n%s\n"
220 ANSI_CURSOR_POSITION ANSI_CLEAR_LINE ANSI_CURSOR_POSITION
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900221 "%s"
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +0900222 ANSI_CLEAR_LINE_TO_END,
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900223 1, 1, entry->efi_menu->menu_header, avail_row + 4, 1,
224 avail_row + 5, 1, entry->efi_menu->menu_desc);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900225}
226
227/**
228 * eficonfig_choice_entry() - user key input handler
229 *
230 * @data: pointer to the efimenu structure
231 * Return: key string to identify the selected entry
232 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900233char *eficonfig_choice_entry(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900234{
Simon Glass9d8d3872023-01-06 08:52:26 -0600235 struct cli_ch_state s_cch, *cch = &s_cch;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900236 struct list_head *pos, *n;
237 struct eficonfig_entry *entry;
Simon Glass05ecdf82023-01-06 08:52:22 -0600238 enum bootmenu_key key = BKEY_NONE;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900239 struct efimenu *efi_menu = data;
240
Simon Glass9d8d3872023-01-06 08:52:26 -0600241 cli_ch_init(cch);
242
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900243 while (1) {
Simon Glass9d8d3872023-01-06 08:52:26 -0600244 key = bootmenu_loop((struct bootmenu_data *)efi_menu, cch);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900245
246 switch (key) {
Simon Glass05ecdf82023-01-06 08:52:22 -0600247 case BKEY_UP:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900248 if (efi_menu->active > 0)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900249 eficonfig_menu_up(efi_menu);
250
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900251 /* no menu key selected, regenerate menu */
252 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -0600253 case BKEY_DOWN:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900254 if (efi_menu->active < efi_menu->count - 1)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900255 eficonfig_menu_down(efi_menu);
256
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900257 /* no menu key selected, regenerate menu */
258 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -0600259 case BKEY_SELECT:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900260 list_for_each_safe(pos, n, &efi_menu->list) {
261 entry = list_entry(pos, struct eficonfig_entry, list);
262 if (entry->num == efi_menu->active)
263 return entry->key;
264 }
265 break;
Simon Glass05ecdf82023-01-06 08:52:22 -0600266 case BKEY_QUIT:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900267 /* Quit by choosing the last entry */
268 entry = list_last_entry(&efi_menu->list, struct eficonfig_entry, list);
269 return entry->key;
270 default:
271 /* Pressed key is not valid, no need to regenerate the menu */
272 break;
273 }
274 }
275}
276
277/**
278 * eficonfig_destroy() - destroy efimenu
279 *
280 * @efi_menu: pointer to the efimenu structure
281 */
282void eficonfig_destroy(struct efimenu *efi_menu)
283{
284 struct list_head *pos, *n;
285 struct eficonfig_entry *entry;
286
287 if (!efi_menu)
288 return;
289
290 list_for_each_safe(pos, n, &efi_menu->list) {
291 entry = list_entry(pos, struct eficonfig_entry, list);
292 free(entry->title);
293 list_del(&entry->list);
294 free(entry);
295 }
296 free(efi_menu->menu_header);
297 free(efi_menu);
298}
299
300/**
301 * eficonfig_process_quit() - callback function for "Quit" entry
302 *
303 * @data: pointer to the data
304 * Return: status code
305 */
306efi_status_t eficonfig_process_quit(void *data)
307{
308 return EFI_ABORTED;
309}
310
311/**
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900312 * eficonfig_append_menu_entry() - append menu item
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900313 *
314 * @efi_menu: pointer to the efimenu structure
315 * @title: pointer to the entry title
316 * @func: callback of each entry
317 * @data: pointer to the data to be passed to each entry callback
318 * Return: status code
319 */
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900320efi_status_t eficonfig_append_menu_entry(struct efimenu *efi_menu,
321 char *title, eficonfig_entry_func func,
322 void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900323{
324 struct eficonfig_entry *entry;
325
326 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX)
327 return EFI_OUT_OF_RESOURCES;
328
329 entry = calloc(1, sizeof(struct eficonfig_entry));
330 if (!entry)
331 return EFI_OUT_OF_RESOURCES;
332
333 entry->title = title;
334 sprintf(entry->key, "%d", efi_menu->count);
335 entry->efi_menu = efi_menu;
336 entry->func = func;
337 entry->data = data;
338 entry->num = efi_menu->count++;
339 list_add_tail(&entry->list, &efi_menu->list);
340
341 return EFI_SUCCESS;
342}
343
344/**
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900345 * eficonfig_append_quit_entry() - append quit entry
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900346 *
347 * @efi_menu: pointer to the efimenu structure
348 * Return: status code
349 */
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900350efi_status_t eficonfig_append_quit_entry(struct efimenu *efi_menu)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900351{
352 char *title;
353 efi_status_t ret;
354
355 title = strdup("Quit");
356 if (!title)
357 return EFI_OUT_OF_RESOURCES;
358
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900359 ret = eficonfig_append_menu_entry(efi_menu, title, eficonfig_process_quit, NULL);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900360 if (ret != EFI_SUCCESS)
361 free(title);
362
363 return ret;
364}
365
366/**
367 * eficonfig_create_fixed_menu() - create fixed entry menu structure
368 *
369 * @items: pointer to the menu entry item
370 * @count: the number of menu entry
371 * Return: pointer to the efimenu structure
372 */
373void *eficonfig_create_fixed_menu(const struct eficonfig_item *items, int count)
374{
375 u32 i;
376 char *title;
377 efi_status_t ret;
378 struct efimenu *efi_menu;
379 const struct eficonfig_item *iter = items;
380
381 efi_menu = calloc(1, sizeof(struct efimenu));
382 if (!efi_menu)
383 return NULL;
384
385 INIT_LIST_HEAD(&efi_menu->list);
386 for (i = 0; i < count; i++, iter++) {
387 title = strdup(iter->title);
388 if (!title)
389 goto out;
390
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900391 ret = eficonfig_append_menu_entry(efi_menu, title, iter->func, iter->data);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900392 if (ret != EFI_SUCCESS) {
393 free(title);
394 goto out;
395 }
396 }
397
398 return efi_menu;
399out:
400 eficonfig_destroy(efi_menu);
401
402 return NULL;
403}
404
405/**
406 * eficonfig_process_common() - main handler for UEFI menu
407 *
408 * Construct the structures required to show the menu, then handle
409 * the user input interacting with u-boot menu functions.
410 *
411 * @efi_menu: pointer to the efimenu structure
412 * @menu_header: pointer to the menu header string
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900413 * @menu_desc: pointer to the menu description
414 * @display_statusline: function pointer to draw statusline
415 * @item_data_print: function pointer to draw the menu item
416 * @item_choice: function pointer to handle the key press
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900417 * Return: status code
418 */
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900419efi_status_t eficonfig_process_common(struct efimenu *efi_menu,
420 char *menu_header, const char *menu_desc,
421 void (*display_statusline)(struct menu *),
422 void (*item_data_print)(void *),
423 char *(*item_choice)(void *))
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900424{
425 struct menu *menu;
426 void *choice = NULL;
427 struct list_head *pos, *n;
428 struct eficonfig_entry *entry;
429 efi_status_t ret = EFI_SUCCESS;
430
431 if (efi_menu->count > EFICONFIG_ENTRY_NUM_MAX)
432 return EFI_OUT_OF_RESOURCES;
433
434 efi_menu->delay = -1;
435 efi_menu->active = 0;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +0900436 efi_menu->start = 0;
437 efi_menu->end = avail_row - 1;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900438
439 if (menu_header) {
440 efi_menu->menu_header = strdup(menu_header);
441 if (!efi_menu->menu_header)
442 return EFI_OUT_OF_RESOURCES;
443 }
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900444 if (menu_desc)
445 efi_menu->menu_desc = menu_desc;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900446
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900447 menu = menu_create(NULL, 0, 1, display_statusline, item_data_print,
developerc2a1e9e2024-10-29 17:47:16 +0800448 item_choice, NULL, efi_menu);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900449 if (!menu)
450 return EFI_INVALID_PARAMETER;
451
452 list_for_each_safe(pos, n, &efi_menu->list) {
453 entry = list_entry(pos, struct eficonfig_entry, list);
454 if (!menu_item_add(menu, entry->key, entry)) {
455 ret = EFI_INVALID_PARAMETER;
456 goto out;
457 }
458 }
459
460 entry = list_first_entry_or_null(&efi_menu->list, struct eficonfig_entry, list);
461 if (entry)
462 menu_default_set(menu, entry->key);
463
464 printf(ANSI_CURSOR_HIDE
465 ANSI_CLEAR_CONSOLE
466 ANSI_CURSOR_POSITION, 1, 1);
467
468 if (menu_get_choice(menu, &choice)) {
469 entry = choice;
470 if (entry->func)
471 ret = entry->func(entry->data);
472 }
473out:
474 menu_destroy(menu);
475
476 printf(ANSI_CLEAR_CONSOLE
477 ANSI_CURSOR_POSITION
478 ANSI_CURSOR_SHOW, 1, 1);
479
480 return ret;
481}
482
483/**
484 * eficonfig_volume_selected() - handler of volume selection
485 *
486 * @data: pointer to the data of selected entry
487 * Return: status code
488 */
489static efi_status_t eficonfig_volume_selected(void *data)
490{
491 struct eficonfig_volume_entry_data *info = data;
492
493 if (info) {
494 info->file_info->current_volume = info->v;
495 info->file_info->dp_volume = info->dp;
496 }
497
498 return EFI_SUCCESS;
499}
500
501/**
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900502 * eficonfig_create_device_path() - create device path
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900503 *
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900504 * @dp_volume: pointer to the volume
505 * @current_path: pointer to the file path u16 string
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900506 * Return:
507 * device path or NULL. Caller must free the returned value
508 */
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900509struct efi_device_path *eficonfig_create_device_path(struct efi_device_path *dp_volume,
510 u16 *current_path)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900511{
512 char *p;
513 void *buf;
514 efi_uintn_t fp_size;
515 struct efi_device_path *dp;
516 struct efi_device_path_file_path *fp;
517
Masahisa Kojimaf2d191a2022-12-02 13:59:34 +0900518 fp_size = sizeof(struct efi_device_path) + u16_strsize(current_path);
Simon Glassc88552c2025-05-24 11:28:23 -0600519 buf = calloc(1, fp_size + sizeof(EFI_DP_END));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900520 if (!buf)
521 return NULL;
522
523 fp = buf;
524 fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
525 fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
526 fp->dp.length = (u16)fp_size;
Masahisa Kojima3360183a2022-11-20 09:21:16 +0900527 u16_strcpy(fp->str, current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900528
529 p = buf;
530 p += fp_size;
Simon Glassc88552c2025-05-24 11:28:23 -0600531 *((struct efi_device_path *)p) = EFI_DP_END;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900532
Heinrich Schuchardtf71c6942023-11-18 12:40:32 +0100533 dp = efi_dp_shorten(dp_volume);
534 if (!dp)
535 dp = dp_volume;
Heinrich Schuchardtf8de0092024-05-24 14:54:26 +0200536 dp = efi_dp_concat(dp, &fp->dp, 0);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900537 free(buf);
538
539 return dp;
540}
541
542/**
Sughosh Ganu885f7a22025-07-03 12:13:07 +0530543 * eficonfig_create_uri_device_path() - Create an URI based device path
544 * @uri_str: URI string to be added to the device path
545 *
546 * Take the u16 string, convert it to a u8 string, and create a URI
547 * device path. This will be used for the EFI HTTP boot.
548 *
549 * Return: pointer to the URI device path on success, NULL on failure
550 */
551static struct efi_device_path *eficonfig_create_uri_device_path(u16 *uri_str)
552{
553 char *pos, *p;
554 u32 len = 0;
555 efi_uintn_t uridp_len;
556 struct efi_device_path_uri *uridp;
557
558 len = utf16_utf8_strlen(uri_str);
559
560 uridp_len = sizeof(struct efi_device_path) + len + 1;
561 uridp = efi_alloc(uridp_len + sizeof(EFI_DP_END));
562 if (!uridp)
563 return NULL;
564
565 uridp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
566 uridp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_URI;
567 uridp->dp.length = uridp_len;
568 p = (char *)&uridp->uri;
569 utf16_utf8_strcpy(&p, uri_str);
570 pos = (char *)uridp + uridp_len;
571 memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END));
572
573 return &uridp->dp;
574}
575
576/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900577 * eficonfig_file_selected() - handler of file selection
578 *
579 * @data: pointer to the data of selected entry
580 * Return: status code
581 */
582static efi_status_t eficonfig_file_selected(void *data)
583{
584 u16 *tmp;
585 struct eficonfig_file_entry_data *info = data;
586
587 if (!info)
588 return EFI_INVALID_PARAMETER;
589
Masahisa Kojima5d13e9d2022-12-02 13:59:33 +0900590 if (!strcmp(info->file_name, "..\\")) {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900591 struct eficonfig_filepath_info *iter;
592 struct list_head *pos, *n;
593 int is_last;
594 char *filepath;
595 tmp = info->file_info->current_path;
596
597 memset(info->file_info->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE);
598 filepath = calloc(1, EFICONFIG_FILE_PATH_MAX);
599 if (!filepath)
600 return EFI_OUT_OF_RESOURCES;
601
602 list_for_each_safe(pos, n, &info->file_info->filepath_list) {
603 iter = list_entry(pos, struct eficonfig_filepath_info, list);
604
605 is_last = list_is_last(&iter->list, &info->file_info->filepath_list);
606 if (is_last) {
607 list_del(&iter->list);
608 free(iter->name);
609 free(iter);
610 break;
611 }
612 strlcat(filepath, iter->name, EFICONFIG_FILE_PATH_MAX);
613 }
614 utf8_utf16_strcpy(&tmp, filepath);
615 } else {
616 size_t new_len;
617 struct eficonfig_filepath_info *filepath_info;
618
619 new_len = u16_strlen(info->file_info->current_path) +
620 strlen(info->file_name);
621 if (new_len >= EFICONFIG_FILE_PATH_MAX) {
622 eficonfig_print_msg("File path is too long!");
623 return EFI_INVALID_PARAMETER;
624 }
625 tmp = &info->file_info->current_path[u16_strlen(info->file_info->current_path)];
626 utf8_utf16_strcpy(&tmp, info->file_name);
627
628 filepath_info = calloc(1, sizeof(struct eficonfig_filepath_info));
629 if (!filepath_info)
630 return EFI_OUT_OF_RESOURCES;
631
632 filepath_info->name = strdup(info->file_name);
633 if (!filepath_info->name) {
634 free(filepath_info);
635 return EFI_OUT_OF_RESOURCES;
636 }
637 list_add_tail(&filepath_info->list, &info->file_info->filepath_list);
638
639 if (!info->is_directory)
640 info->file_info->file_selected = true;
641 }
642
643 return EFI_SUCCESS;
644}
645
646/**
647 * eficonfig_select_volume() - construct the volume selection menu
648 *
649 * @file_info: pointer to the file selection structure
650 * Return: status code
651 */
652static efi_status_t eficonfig_select_volume(struct eficonfig_select_file_info *file_info)
653{
654 u32 i;
655 efi_status_t ret;
656 efi_uintn_t count;
657 struct efimenu *efi_menu;
658 struct list_head *pos, *n;
659 struct efi_handler *handler;
660 struct eficonfig_entry *entry;
661 struct efi_device_path *device_path;
662 efi_handle_t *volume_handles = NULL;
663 struct efi_simple_file_system_protocol *v;
664
665 ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
666 NULL, &count, (efi_handle_t **)&volume_handles);
667 if (ret != EFI_SUCCESS) {
668 eficonfig_print_msg("No block device found!");
669 return ret;
670 }
671
672 efi_menu = calloc(1, sizeof(struct efimenu));
673 if (!efi_menu)
674 return EFI_OUT_OF_RESOURCES;
675
676 INIT_LIST_HEAD(&efi_menu->list);
677 for (i = 0; i < count; i++) {
678 char *devname;
679 struct efi_block_io *block_io;
680 struct eficonfig_volume_entry_data *info;
681
682 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
683 break;
684
685 ret = efi_search_protocol(volume_handles[i],
686 &efi_simple_file_system_protocol_guid, &handler);
687 if (ret != EFI_SUCCESS)
688 continue;
689 ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL,
690 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
691 if (ret != EFI_SUCCESS)
692 continue;
693
694 ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
695 if (ret != EFI_SUCCESS)
696 continue;
697 ret = efi_protocol_open(handler, (void **)&device_path,
698 efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
699 if (ret != EFI_SUCCESS)
700 continue;
701
702 ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
703 if (ret != EFI_SUCCESS)
704 continue;
705 ret = efi_protocol_open(handler, (void **)&block_io,
706 efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
707 if (ret != EFI_SUCCESS)
708 continue;
709
710 info = calloc(1, sizeof(struct eficonfig_volume_entry_data));
711 if (!info) {
712 ret = EFI_OUT_OF_RESOURCES;
713 goto out;
714 }
715
716 devname = calloc(1, BOOTMENU_DEVICE_NAME_MAX);
717 if (!devname) {
718 free(info);
719 ret = EFI_OUT_OF_RESOURCES;
720 goto out;
721 }
722 ret = efi_disk_get_device_name(volume_handles[i], devname,
723 BOOTMENU_DEVICE_NAME_MAX);
724 if (ret != EFI_SUCCESS) {
725 free(info);
726 goto out;
727 }
728
729 info->v = v;
730 info->dp = device_path;
731 info->file_info = file_info;
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900732 ret = eficonfig_append_menu_entry(efi_menu, devname, eficonfig_volume_selected,
733 info);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900734 if (ret != EFI_SUCCESS) {
735 free(info);
736 goto out;
737 }
738 }
739
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900740 ret = eficonfig_append_quit_entry(efi_menu);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900741 if (ret != EFI_SUCCESS)
742 goto out;
743
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900744 ret = eficonfig_process_common(efi_menu, " ** Select Volume **",
745 eficonfig_menu_desc,
746 eficonfig_display_statusline,
747 eficonfig_print_entry,
748 eficonfig_choice_entry);
749
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900750out:
751 efi_free_pool(volume_handles);
752 list_for_each_safe(pos, n, &efi_menu->list) {
753 entry = list_entry(pos, struct eficonfig_entry, list);
754 free(entry->data);
755 }
756 eficonfig_destroy(efi_menu);
757
758 return ret;
759}
760
761/**
762 * sort_file() - sort the file name in ascii order
763 *
764 * @data1: pointer to the file entry data
765 * @data2: pointer to the file entry data
766 * Return: -1 if the data1 file name is less than data2 file name,
767 * 0 if both file name match,
768 * 1 if the data1 file name is greater thant data2 file name.
769 */
770static int sort_file(const void *arg1, const void *arg2)
771{
772 const struct eficonfig_file_entry_data *data1, *data2;
773
774 data1 = *((const struct eficonfig_file_entry_data **)arg1);
775 data2 = *((const struct eficonfig_file_entry_data **)arg2);
776
777 return strcasecmp(data1->file_name, data2->file_name);
778}
779
780/**
781 * eficonfig_create_file_entry() - construct the file menu entry
782 *
783 * @efi_menu: pointer to the efimenu structure
784 * @count: number of the directory and file
785 * @tmp_infos: pointer to the entry data array
786 * @f: pointer to the file handle
787 * @buf: pointer to the buffer to store the directory information
788 * @file_info: pointer to the file selection structure
789 * Return: status code
790 */
791static efi_status_t
792eficonfig_create_file_entry(struct efimenu *efi_menu, u32 count,
793 struct eficonfig_file_entry_data **tmp_infos,
794 struct efi_file_handle *f, struct efi_file_info *buf,
795 struct eficonfig_select_file_info *file_info)
796{
797 char *name, *p;
798 efi_uintn_t len;
799 efi_status_t ret;
800 u32 i, entry_num = 0;
801 struct eficonfig_file_entry_data *info;
802
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900803 EFI_CALL(f->setpos(f, 0));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900804 /* Read directory and construct menu structure */
805 for (i = 0; i < count; i++) {
806 if (entry_num >= EFICONFIG_ENTRY_NUM_MAX - 1)
807 break;
808
809 len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE;
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900810 ret = EFI_CALL(f->read(f, &len, buf));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900811 if (ret != EFI_SUCCESS || len == 0)
812 break;
813
814 info = calloc(1, sizeof(struct eficonfig_file_entry_data));
815 if (!info) {
816 ret = EFI_OUT_OF_RESOURCES;
817 goto out;
818 }
819
820 /* append '\\' at the end of directory name */
821 name = calloc(1, utf16_utf8_strlen(buf->file_name) + 2);
822 if (!name) {
823 ret = EFI_OUT_OF_RESOURCES;
824 free(info);
825 goto out;
826 }
827 p = name;
828 utf16_utf8_strcpy(&p, buf->file_name);
829 if (buf->attribute & EFI_FILE_DIRECTORY) {
830 /* filter out u'.' */
831 if (!u16_strcmp(buf->file_name, u".")) {
832 free(info);
833 free(name);
834 continue;
835 }
836 name[u16_strlen(buf->file_name)] = '\\';
837 info->is_directory = true;
838 }
839
840 info->file_name = name;
841 info->file_info = file_info;
842 tmp_infos[entry_num++] = info;
843 }
844
845 qsort(tmp_infos, entry_num, sizeof(*tmp_infos),
846 (int (*)(const void *, const void *))sort_file);
847
848 for (i = 0; i < entry_num; i++) {
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900849 ret = eficonfig_append_menu_entry(efi_menu, tmp_infos[i]->file_name,
850 eficonfig_file_selected, tmp_infos[i]);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900851 if (ret != EFI_SUCCESS)
852 goto out;
853 }
854
855out:
856 return ret;
857}
858
859/**
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +0900860 * eficonfig_show_file_selection() - construct the file selection menu
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900861 *
862 * @file_info: pointer to the file selection structure
863 * @root: pointer to the file handle
864 * Return: status code
865 */
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +0900866static efi_status_t eficonfig_show_file_selection(struct eficonfig_select_file_info *file_info,
867 struct efi_file_handle *root)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900868{
869 u32 count = 0, i;
870 efi_uintn_t len;
871 efi_status_t ret;
872 struct efimenu *efi_menu;
873 struct efi_file_handle *f;
874 struct efi_file_info *buf;
875 struct eficonfig_file_entry_data **tmp_infos;
876
877 buf = calloc(1, sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE);
878 if (!buf)
879 return EFI_OUT_OF_RESOURCES;
880
881 while (!file_info->file_selected) {
882 efi_menu = calloc(1, sizeof(struct efimenu));
883 if (!efi_menu) {
884 ret = EFI_OUT_OF_RESOURCES;
885 goto out;
886 }
887 INIT_LIST_HEAD(&efi_menu->list);
888
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900889 ret = EFI_CALL(root->open(root, &f, file_info->current_path,
890 EFI_FILE_MODE_READ, 0));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900891 if (ret != EFI_SUCCESS) {
892 eficonfig_print_msg("Reading volume failed!");
893 free(efi_menu);
894 ret = EFI_ABORTED;
895 goto out;
896 }
897
898 /* Count the number of directory entries */
899 for (;;) {
900 len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE;
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900901 ret = EFI_CALL(f->read(f, &len, buf));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900902 if (ret != EFI_SUCCESS || len == 0)
903 break;
904
905 count++;
906 }
907
908 /* allocate array to sort the entry */
909 tmp_infos = calloc(count, sizeof(*tmp_infos));
910 if (!tmp_infos) {
911 ret = EFI_OUT_OF_RESOURCES;
912 goto err;
913 }
914
915 ret = eficonfig_create_file_entry(efi_menu, count, tmp_infos,
916 f, buf, file_info);
917 if (ret != EFI_SUCCESS)
918 goto err;
919
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +0900920 ret = eficonfig_append_quit_entry(efi_menu);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900921 if (ret != EFI_SUCCESS)
922 goto err;
923
Masahisa Kojimafc811d12023-01-24 15:56:13 +0900924 ret = eficonfig_process_common(efi_menu, " ** Select File **",
925 eficonfig_menu_desc,
926 eficonfig_display_statusline,
927 eficonfig_print_entry,
928 eficonfig_choice_entry);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900929err:
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +0900930 EFI_CALL(f->close(f));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900931 eficonfig_destroy(efi_menu);
932
933 if (tmp_infos) {
934 for (i = 0; i < count; i++)
935 free(tmp_infos[i]);
936 }
937
938 free(tmp_infos);
939
940 if (ret != EFI_SUCCESS)
941 break;
942 }
943
944out:
945 free(buf);
946
947 return ret;
948}
949
950/**
951 * handle_user_input() - handle user input
952 *
953 * @buf: pointer to the buffer
954 * @buf_size: size of the buffer
955 * @cursor_col: cursor column for user input
956 * @msg: pointer to the string to display
957 * Return: status code
958 */
959static efi_status_t handle_user_input(u16 *buf, int buf_size,
960 int cursor_col, char *msg)
961{
962 u16 *tmp;
963 efi_status_t ret;
964
965 printf(ANSI_CLEAR_CONSOLE
966 ANSI_CURSOR_POSITION
967 "%s"
968 ANSI_CURSOR_POSITION
Masahisa Kojima3a9eb072023-02-02 18:24:43 +0900969 " Press ENTER to complete, ESC to quit",
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900970 0, 1, msg, 8, 1);
971
972 /* tmp is used to accept user cancel */
973 tmp = calloc(1, buf_size * sizeof(u16));
974 if (!tmp)
975 return EFI_OUT_OF_RESOURCES;
976
977 ret = efi_console_get_u16_string(cin, tmp, buf_size, NULL, 4, cursor_col);
978 if (ret == EFI_SUCCESS)
979 u16_strcpy(buf, tmp);
980
981 free(tmp);
982
983 /* to stay the parent menu */
984 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
985
986 return ret;
987}
988
989/**
990 * eficonfig_boot_add_enter_description() - handle user input for description
991 *
992 * @data: pointer to the internal boot option structure
993 * Return: status code
994 */
995static efi_status_t eficonfig_boot_add_enter_description(void *data)
996{
997 struct eficonfig_boot_option *bo = data;
998
999 return handle_user_input(bo->description, EFICONFIG_DESCRIPTION_MAX, 22,
1000 "\n ** Edit Description **\n"
1001 "\n"
Heinrich Schuchardt3a783202024-10-25 23:15:05 +02001002 " Enter description: ");
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001003}
1004
1005/**
1006 * eficonfig_boot_add_optional_data() - handle user input for optional data
1007 *
1008 * @data: pointer to the internal boot option structure
1009 * Return: status code
1010 */
1011static efi_status_t eficonfig_boot_add_optional_data(void *data)
1012{
1013 struct eficonfig_boot_option *bo = data;
1014
1015 return handle_user_input(bo->optional_data, EFICONFIG_OPTIONAL_DATA_MAX, 24,
1016 "\n ** Edit Optional Data **\n"
1017 "\n"
1018 " enter optional data:");
1019}
1020
1021/**
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301022 * eficonfig_boot_add_uri() - handle user input for HTTP Boot URI
1023 *
1024 * @data: pointer to the internal boot option structure
1025 * Return: status code
1026 */
1027static efi_status_t eficonfig_boot_add_uri(void *data)
1028{
1029 struct eficonfig_select_file_info *file_info = data;
1030
1031 return handle_user_input(file_info->uri, EFICONFIG_URI_MAX, 24,
1032 "\n ** Edit URI **\n"
1033 "\n"
1034 " enter HTTP Boot URI:");
1035}
1036
1037/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001038 * eficonfig_boot_edit_save() - handler to save the boot option
1039 *
1040 * @data: pointer to the internal boot option structure
1041 * Return: status code
1042 */
1043static efi_status_t eficonfig_boot_edit_save(void *data)
1044{
1045 struct eficonfig_boot_option *bo = data;
1046
1047 if (u16_strlen(bo->description) == 0) {
1048 eficonfig_print_msg("Boot Description is empty!");
1049 bo->edit_completed = false;
1050 return EFI_NOT_READY;
1051 }
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301052 if (u16_strlen(bo->file_info.current_path) == 0 &&
1053 u16_strlen(bo->file_info.uri) == 0) {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001054 eficonfig_print_msg("File is not selected!");
1055 bo->edit_completed = false;
1056 return EFI_NOT_READY;
1057 }
1058
1059 bo->edit_completed = true;
1060
1061 return EFI_SUCCESS;
1062}
1063
1064/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001065 * eficonfig_process_clear_file_selection() - callback function for "Clear" entry
1066 *
1067 * @data: pointer to the data
1068 * Return: status code
1069 */
1070efi_status_t eficonfig_process_clear_file_selection(void *data)
1071{
1072 struct eficonfig_select_file_info *file_info = data;
1073
1074 /* clear the existing file information */
1075 file_info->current_volume = NULL;
1076 file_info->current_path[0] = u'\0';
1077 file_info->dp_volume = NULL;
1078
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301079 if (file_info->uri)
1080 file_info->uri[0] = u'\0';
1081
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001082 return EFI_ABORTED;
1083}
1084
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301085static struct eficonfig_item select_boot_file_menu_items[] = {
1086 {"Select File", eficonfig_process_select_file},
1087 {"Enter URI", eficonfig_boot_add_uri},
1088 {"Clear", eficonfig_process_clear_file_selection},
1089 {"Quit", eficonfig_process_quit},
1090};
1091
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001092static struct eficonfig_item select_file_menu_items[] = {
1093 {"Select File", eficonfig_process_select_file},
1094 {"Clear", eficonfig_process_clear_file_selection},
1095 {"Quit", eficonfig_process_quit},
1096};
1097
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001098/**
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001099 * eficonfig_process_show_file_option() - display select file option
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001100 *
1101 * @file_info: pointer to the file information structure
1102 * Return: status code
1103 */
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001104efi_status_t eficonfig_process_show_file_option(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001105{
1106 efi_status_t ret;
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301107 unsigned int menu_count;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001108 struct efimenu *efi_menu;
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301109 struct eficonfig_item *menu_items;
1110 struct eficonfig_select_file_info *file_info = data;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001111
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301112 menu_items = file_info->uri ? select_boot_file_menu_items :
1113 select_file_menu_items;
1114
1115 menu_count = file_info->uri ?
1116 ARRAY_SIZE(select_boot_file_menu_items) :
1117 ARRAY_SIZE(select_file_menu_items);
1118
1119 menu_items[0].data = data;
1120 menu_items[1].data = data;
1121 menu_items[2].data = data;
1122
1123 efi_menu = eficonfig_create_fixed_menu(menu_items, menu_count);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001124 if (!efi_menu)
1125 return EFI_OUT_OF_RESOURCES;
1126
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301127 ret = eficonfig_process_common(efi_menu,
1128 file_info->uri ?
1129 " ** Update File/URI **" :
1130 " ** Update File **",
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001131 eficonfig_menu_desc,
1132 eficonfig_display_statusline,
1133 eficonfig_print_entry,
1134 eficonfig_choice_entry);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001135 if (ret != EFI_SUCCESS) /* User selects "Clear" or "Quit" */
1136 ret = EFI_NOT_READY;
1137
1138 eficonfig_destroy(efi_menu);
1139
1140 return ret;
1141}
1142
1143/**
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001144 * eficonfig_process_select_file() - handle user file selection
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001145 *
1146 * @data: pointer to the data
1147 * Return: status code
1148 */
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001149efi_status_t eficonfig_process_select_file(void *data)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001150{
1151 size_t len;
1152 efi_status_t ret;
1153 struct list_head *pos, *n;
1154 struct efi_file_handle *root;
1155 struct eficonfig_filepath_info *item;
1156 struct eficonfig_select_file_info *tmp = NULL;
1157 struct eficonfig_select_file_info *file_info = data;
1158
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001159 tmp = calloc(1, sizeof(struct eficonfig_select_file_info));
1160 if (!tmp)
1161 return EFI_OUT_OF_RESOURCES;
1162
1163 tmp->current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1164 if (!tmp->current_path) {
1165 free(tmp);
1166 return EFI_OUT_OF_RESOURCES;
1167 }
1168 INIT_LIST_HEAD(&tmp->filepath_list);
1169
1170 while (!tmp->file_selected) {
1171 tmp->current_volume = NULL;
1172 memset(tmp->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE);
1173
1174 ret = eficonfig_select_volume(tmp);
1175 if (ret != EFI_SUCCESS)
1176 goto out;
1177
1178 if (!tmp->current_volume)
1179 return EFI_INVALID_PARAMETER;
1180
Masahisa Kojimaa75c8c92022-11-20 09:21:17 +09001181 ret = EFI_CALL(tmp->current_volume->open_volume(tmp->current_volume, &root));
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001182 if (ret != EFI_SUCCESS)
1183 goto out;
1184
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001185 ret = eficonfig_show_file_selection(tmp, root);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001186 if (ret == EFI_ABORTED)
1187 continue;
1188 if (ret != EFI_SUCCESS)
1189 goto out;
1190 }
1191
1192out:
1193 if (ret == EFI_SUCCESS) {
1194 len = u16_strlen(tmp->current_path);
1195 len = (len >= EFICONFIG_FILE_PATH_MAX) ? (EFICONFIG_FILE_PATH_MAX - 1) : len;
1196 memcpy(file_info->current_path, tmp->current_path, len * sizeof(u16));
1197 file_info->current_path[len] = u'\0';
1198 file_info->current_volume = tmp->current_volume;
1199 file_info->dp_volume = tmp->dp_volume;
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301200
1201 /*
1202 * File being selected, set the URI string to
1203 * null so that the file gets picked as the
1204 * boot image.
1205 */
1206 if (file_info->uri)
1207 file_info->uri[0] = u'\0';
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001208 }
1209
1210 list_for_each_safe(pos, n, &tmp->filepath_list) {
1211 item = list_entry(pos, struct eficonfig_filepath_info, list);
1212 list_del(&item->list);
1213 free(item->name);
1214 free(item);
1215 }
1216 free(tmp->current_path);
1217 free(tmp);
1218
1219 /* to stay the parent menu */
1220 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
1221
1222 return ret;
1223}
1224
1225/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001226 * eficonfig_set_boot_option() - set boot option
1227 *
1228 * @varname: pointer to variable name
1229 * @dp: pointer to device path
1230 * @label: pointer to label string
1231 * @optional_data: pointer to optional data
1232 * Return: status code
1233 */
1234static efi_status_t eficonfig_set_boot_option(u16 *varname, struct efi_device_path *dp,
1235 efi_uintn_t dp_size, u16 *label, char *optional_data)
1236{
1237 void *p = NULL;
1238 efi_status_t ret;
1239 efi_uintn_t size;
1240 struct efi_load_option lo;
1241
1242 lo.file_path = dp;
1243 lo.file_path_length = dp_size;
1244 lo.attributes = LOAD_OPTION_ACTIVE;
1245 lo.optional_data = optional_data;
1246 lo.label = label;
1247
1248 size = efi_serialize_load_option(&lo, (u8 **)&p);
1249 if (!size)
1250 return EFI_INVALID_PARAMETER;
1251
1252 ret = efi_set_variable_int(varname, &efi_global_variable_guid,
1253 EFI_VARIABLE_NON_VOLATILE |
1254 EFI_VARIABLE_BOOTSERVICE_ACCESS |
1255 EFI_VARIABLE_RUNTIME_ACCESS,
1256 size, p, false);
1257 free(p);
1258
1259 return ret;
1260}
1261
1262/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001263 * create_boot_option_entry() - create boot option entry
1264 *
1265 * @efi_menu: pointer to the efimenu structure
1266 * @title: pointer to the entry title
1267 * @val: pointer to boot option label
1268 * @func: callback of each entry
1269 * @data: pointer to the data to be passed to each entry callback
1270 * Return: status code
1271 */
1272static efi_status_t create_boot_option_entry(struct efimenu *efi_menu, char *title, u16 *val,
1273 eficonfig_entry_func func, void *data)
1274{
1275 u32 len;
1276 char *p, *buf;
1277
1278 len = strlen(title) + 1;
1279 if (val)
1280 len += utf16_utf8_strlen(val);
1281 buf = calloc(1, len);
1282 if (!buf)
1283 return EFI_OUT_OF_RESOURCES;
1284
1285 strcpy(buf, title);
1286 if (val) {
1287 p = buf + strlen(title);
1288 utf16_utf8_strcpy(&p, val);
1289 }
1290
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001291 return eficonfig_append_menu_entry(efi_menu, buf, func, data);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001292}
1293
1294/**
1295 * prepare_file_selection_entry() - prepare file selection entry
1296 *
1297 * @efi_menu: pointer to the efimenu structure
1298 * @title: pointer to the title string
1299 * @file_info: pointer to the file info
1300 * Return: status code
1301 */
1302static efi_status_t prepare_file_selection_entry(struct efimenu *efi_menu, char *title,
1303 struct eficonfig_select_file_info *file_info)
1304{
1305 u32 len;
1306 efi_status_t ret;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001307 u16 *file_name = NULL, *p;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001308 efi_handle_t handle;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001309 char *devname;
1310
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301311 /* Check for URI based boot file */
1312 if (file_info->uri && utf16_utf8_strlen(file_info->uri))
1313 return create_boot_option_entry(efi_menu, title, file_info->uri,
1314 eficonfig_process_show_file_option,
1315 file_info);
1316
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001317 devname = calloc(1, EFICONFIG_VOLUME_PATH_MAX + 1);
1318 if (!devname)
1319 return EFI_OUT_OF_RESOURCES;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001320
1321 /* get the device name only when the user already selected the file path */
1322 handle = efi_dp_find_obj(file_info->dp_volume, NULL, NULL);
1323 if (handle) {
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001324 ret = efi_disk_get_device_name(handle, devname, EFICONFIG_VOLUME_PATH_MAX);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001325 if (ret != EFI_SUCCESS)
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001326 goto out;
1327 }
1328
1329 /*
1330 * If the preconfigured volume does not exist in the system, display the text
1331 * converted volume device path instead of U-Boot friendly name(e.g. "usb 0:1").
1332 */
1333 if (!handle && file_info->dp_volume) {
1334 u16 *dp_str;
1335 char *q = devname;
1336
1337 dp_str = efi_dp_str(file_info->dp_volume);
1338 if (dp_str)
1339 utf16_utf8_strncpy(&q, dp_str, EFICONFIG_VOLUME_PATH_MAX);
1340
1341 efi_free_pool(dp_str);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001342 }
1343
1344 /* append u'/' to devname, it is just for display purpose. */
1345 if (file_info->current_path[0] != u'\0' && file_info->current_path[0] != u'/')
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001346 strlcat(devname, "/", EFICONFIG_VOLUME_PATH_MAX + 1);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001347
1348 len = strlen(devname);
1349 len += utf16_utf8_strlen(file_info->current_path) + 1;
1350 file_name = calloc(1, len * sizeof(u16));
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001351 if (!file_name) {
1352 ret = EFI_OUT_OF_RESOURCES;
1353 goto out;
1354 }
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001355
1356 p = file_name;
1357 utf8_utf16_strcpy(&p, devname);
1358 u16_strlcat(file_name, file_info->current_path, len);
1359 ret = create_boot_option_entry(efi_menu, title, file_name,
Masahisa Kojima0be1b2a2022-11-20 09:21:13 +09001360 eficonfig_process_show_file_option, file_info);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001361out:
1362 free(devname);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001363 free(file_name);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001364
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001365 return ret;
1366}
1367
1368/**
1369 * eficonfig_show_boot_option() - prepare menu entry for editing boot option
1370 *
1371 * Construct the structures to create edit boot option menu
1372 *
1373 * @bo: pointer to the boot option
1374 * @header_str: pointer to the header string
1375 * Return: status code
1376 */
1377static efi_status_t eficonfig_show_boot_option(struct eficonfig_boot_option *bo,
1378 char *header_str)
1379{
1380 efi_status_t ret;
1381 struct efimenu *efi_menu;
1382
1383 efi_menu = calloc(1, sizeof(struct efimenu));
1384 if (!efi_menu)
1385 return EFI_OUT_OF_RESOURCES;
1386
1387 INIT_LIST_HEAD(&efi_menu->list);
1388
1389 ret = create_boot_option_entry(efi_menu, "Description: ", bo->description,
1390 eficonfig_boot_add_enter_description, bo);
1391 if (ret != EFI_SUCCESS)
1392 goto out;
1393
1394 ret = prepare_file_selection_entry(efi_menu, "File: ", &bo->file_info);
1395 if (ret != EFI_SUCCESS)
1396 goto out;
1397
1398 ret = prepare_file_selection_entry(efi_menu, "Initrd File: ", &bo->initrd_info);
1399 if (ret != EFI_SUCCESS)
1400 goto out;
1401
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001402 ret = prepare_file_selection_entry(efi_menu, "Fdt File: ", &bo->fdt_info);
1403 if (ret != EFI_SUCCESS)
1404 goto out;
1405
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001406 ret = create_boot_option_entry(efi_menu, "Optional Data: ", bo->optional_data,
1407 eficonfig_boot_add_optional_data, bo);
1408 if (ret != EFI_SUCCESS)
1409 goto out;
1410
1411 ret = create_boot_option_entry(efi_menu, "Save", NULL,
1412 eficonfig_boot_edit_save, bo);
1413 if (ret != EFI_SUCCESS)
1414 goto out;
1415
1416 ret = create_boot_option_entry(efi_menu, "Quit", NULL,
1417 eficonfig_process_quit, NULL);
1418 if (ret != EFI_SUCCESS)
1419 goto out;
1420
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001421 ret = eficonfig_process_common(efi_menu, header_str,
1422 eficonfig_menu_desc,
1423 eficonfig_display_statusline,
1424 eficonfig_print_entry,
1425 eficonfig_choice_entry);
1426
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001427out:
1428 eficonfig_destroy(efi_menu);
1429
1430 return ret;
1431}
1432
1433/**
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301434 * fill_dp_uri() - copy the URI string in the device path
1435 * @dp: pointer to the URI device path
1436 * @uri_str: URI string to be copied
1437 *
1438 * Copy the passed URI string to the URI device path. This
1439 * requires utf8_utf16_strcpy() to copy the u16 string to
1440 * the u8 array in the device path structure.
1441 *
1442 * Return: None
1443 */
1444static void fill_dp_uri(struct efi_device_path *dp, u16 **uri_str)
1445{
1446 u16 *p = *uri_str;
1447 struct efi_device_path_uri *uridp;
1448
1449 uridp = (struct efi_device_path_uri *)dp;
1450
1451 utf8_utf16_strcpy(&p, uridp->uri);
1452}
1453
1454/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001455 * fill_file_info() - fill the file info from efi_device_path structure
1456 *
1457 * @dp: pointer to the device path
1458 * @file_info: pointer to the file info structure
1459 * @device_dp: pointer to the volume device path
1460 */
1461static void fill_file_info(struct efi_device_path *dp,
1462 struct eficonfig_select_file_info *file_info,
1463 struct efi_device_path *device_dp)
1464{
1465 u16 *file_str, *p;
1466 struct efi_device_path *file_dp = NULL;
1467
1468 efi_dp_split_file_path(dp, &device_dp, &file_dp);
1469 file_info->dp_volume = device_dp;
1470
1471 if (file_dp) {
1472 file_str = efi_dp_str(file_dp);
1473 /*
1474 * efi_convert_device_path_to_text() automatically adds u'/' at the
1475 * beginning of file name, remove u'/' before copying to current_path
1476 */
1477 p = file_str;
1478 if (p[0] == u'/')
1479 p++;
1480
1481 u16_strcpy(file_info->current_path, p);
1482 efi_free_pool(file_dp);
1483 efi_free_pool(file_str);
1484 }
1485}
1486
1487/**
1488 * eficonfig_edit_boot_option() - prepare boot option structure for editing
1489 *
1490 * Construct the boot option structure and copy the existing value
1491 *
1492 * @varname: pointer to the UEFI variable name
1493 * @bo: pointer to the boot option
1494 * @load_option: pointer to the load option
1495 * @load_option_size: size of the load option
1496 * @header_str: pointer to the header string
1497 * Return : status code
1498 */
1499static efi_status_t eficonfig_edit_boot_option(u16 *varname, struct eficonfig_boot_option *bo,
1500 void *load_option, efi_uintn_t load_option_size,
1501 char *header_str)
1502{
1503 size_t len;
1504 efi_status_t ret;
1505 char *tmp = NULL, *p;
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301506 u16 *current_path = NULL;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001507 struct efi_load_option lo = {0};
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001508 efi_uintn_t dp_size;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001509 struct efi_device_path *dp = NULL;
1510 efi_uintn_t size = load_option_size;
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301511 struct efi_device_path *dp_volume = NULL;
1512 struct efi_device_path *uri_dp = NULL;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001513 struct efi_device_path *device_dp = NULL;
1514 struct efi_device_path *initrd_dp = NULL;
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001515 struct efi_device_path *fdt_dp = NULL;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001516 struct efi_device_path *initrd_device_dp = NULL;
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001517 struct efi_device_path *fdt_device_dp = NULL;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001518
Heinrich Schuchardt40da9e62024-06-10 10:00:01 +02001519 const struct efi_lo_dp_prefix initrd_prefix = {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001520 .vendor = {
1521 {
1522 DEVICE_PATH_TYPE_MEDIA_DEVICE,
1523 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001524 sizeof(initrd_prefix.vendor),
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001525 },
1526 EFI_INITRD_MEDIA_GUID,
1527 },
1528 .end = {
1529 DEVICE_PATH_TYPE_END,
1530 DEVICE_PATH_SUB_TYPE_END,
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001531 sizeof(initrd_prefix.end),
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001532 }
1533 };
1534
Heinrich Schuchardt40da9e62024-06-10 10:00:01 +02001535 const struct efi_lo_dp_prefix fdt_prefix = {
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001536 .vendor = {
1537 {
1538 DEVICE_PATH_TYPE_MEDIA_DEVICE,
1539 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
1540 sizeof(fdt_prefix.vendor),
1541 },
1542 EFI_FDT_GUID,
1543 },
1544 .end = {
1545 DEVICE_PATH_TYPE_END,
1546 DEVICE_PATH_SUB_TYPE_END,
1547 sizeof(initrd_prefix.end),
1548 }
1549 };
1550
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001551 bo->file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1552 if (!bo->file_info.current_path) {
1553 ret = EFI_OUT_OF_RESOURCES;
1554 goto out;
1555 }
1556
1557 bo->initrd_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
Heinrich Schuchardtfc22ac22024-04-17 18:01:25 +02001558 if (!bo->initrd_info.current_path) {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001559 ret = EFI_OUT_OF_RESOURCES;
1560 goto out;
1561 }
1562
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001563 bo->fdt_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
1564 if (!bo->fdt_info.current_path) {
1565 ret = EFI_OUT_OF_RESOURCES;
1566 goto out;
1567 }
1568
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001569 bo->description = calloc(1, EFICONFIG_DESCRIPTION_MAX * sizeof(u16));
1570 if (!bo->description) {
1571 ret = EFI_OUT_OF_RESOURCES;
1572 goto out;
1573 }
1574
1575 bo->optional_data = calloc(1, EFICONFIG_OPTIONAL_DATA_MAX * sizeof(u16));
1576 if (!bo->optional_data) {
1577 ret = EFI_OUT_OF_RESOURCES;
1578 goto out;
1579 }
1580
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301581 bo->file_info.uri = calloc(1, EFICONFIG_URI_MAX * sizeof(u16));
1582 if (!bo->file_info.uri) {
1583 ret = EFI_OUT_OF_RESOURCES;
1584 goto out;
1585 }
1586
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001587 /* copy the preset value */
1588 if (load_option) {
1589 ret = efi_deserialize_load_option(&lo, load_option, &size);
1590 if (ret != EFI_SUCCESS)
1591 goto out;
1592
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001593 if (!lo.label) {
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001594 ret = EFI_INVALID_PARAMETER;
1595 goto out;
1596 }
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001597 /* truncate the long label string */
1598 if (u16_strlen(lo.label) >= EFICONFIG_DESCRIPTION_MAX)
1599 lo.label[EFICONFIG_DESCRIPTION_MAX - 1] = u'\0';
1600
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001601 u16_strcpy(bo->description, lo.label);
1602
1603 /* EFI image file path is a first instance */
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301604 if (lo.file_path && EFI_DP_TYPE(lo.file_path, MESSAGING_DEVICE,
1605 MSG_URI))
1606 fill_dp_uri(lo.file_path, &bo->file_info.uri);
1607 else if (lo.file_path)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001608 fill_file_info(lo.file_path, &bo->file_info, device_dp);
1609
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001610 /* Initrd file path (optional) is placed at second instance. */
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001611 initrd_dp = efi_dp_from_lo(&lo, &efi_lf2_initrd_guid);
1612 if (initrd_dp) {
1613 fill_file_info(initrd_dp, &bo->initrd_info, initrd_device_dp);
1614 efi_free_pool(initrd_dp);
1615 }
1616
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001617 /* Fdt file path (optional) is placed as third instance. */
1618 fdt_dp = efi_dp_from_lo(&lo, &efi_guid_fdt);
1619 if (fdt_dp) {
1620 fill_file_info(fdt_dp, &bo->fdt_info, fdt_device_dp);
1621 efi_free_pool(fdt_dp);
1622 }
1623
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001624 if (size > 0)
1625 memcpy(bo->optional_data, lo.optional_data, size);
1626 }
1627
1628 while (1) {
1629 ret = eficonfig_show_boot_option(bo, header_str);
1630 if (ret == EFI_SUCCESS && bo->edit_completed)
1631 break;
1632 if (ret == EFI_NOT_READY)
1633 continue;
1634 if (ret != EFI_SUCCESS)
1635 goto out;
1636 }
1637
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301638 if (utf16_utf8_strlen(bo->file_info.uri))
1639 uri_dp = eficonfig_create_uri_device_path(bo->file_info.uri);
1640
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001641 if (bo->initrd_info.dp_volume) {
Masahisa Kojima3360183a2022-11-20 09:21:16 +09001642 dp = eficonfig_create_device_path(bo->initrd_info.dp_volume,
1643 bo->initrd_info.current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001644 if (!dp) {
1645 ret = EFI_OUT_OF_RESOURCES;
1646 goto out;
1647 }
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001648 initrd_dp = efi_dp_concat((const struct efi_device_path *)&initrd_prefix,
Heinrich Schuchardtf8de0092024-05-24 14:54:26 +02001649 dp, 0);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001650 efi_free_pool(dp);
1651 }
1652
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001653 if (bo->fdt_info.dp_volume) {
1654 dp = eficonfig_create_device_path(bo->fdt_info.dp_volume,
1655 bo->fdt_info.current_path);
1656 if (!dp) {
1657 ret = EFI_OUT_OF_RESOURCES;
1658 goto out;
1659 }
1660 fdt_dp = efi_dp_concat((const struct efi_device_path *)&fdt_prefix,
1661 dp, 0);
1662 efi_free_pool(dp);
1663 }
1664
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301665 dp_volume = bo->file_info.dp_volume;
1666 current_path = bo->file_info.current_path;
1667 dp = uri_dp ?
1668 uri_dp : eficonfig_create_device_path(dp_volume, current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001669 if (!dp) {
1670 ret = EFI_OUT_OF_RESOURCES;
1671 goto out;
1672 }
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001673
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001674 ret = efi_load_option_dp_join(&dp, &dp_size, initrd_dp, fdt_dp);
1675 if (ret != EFI_SUCCESS)
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001676 goto out;
1677
1678 if (utf16_utf8_strlen(bo->optional_data)) {
1679 len = utf16_utf8_strlen(bo->optional_data) + 1;
1680 tmp = calloc(1, len);
1681 if (!tmp)
1682 goto out;
1683 p = tmp;
1684 utf16_utf8_strncpy(&p, bo->optional_data, u16_strlen(bo->optional_data));
1685 }
1686
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001687 ret = eficonfig_set_boot_option(varname, dp, dp_size, bo->description, tmp);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001688out:
1689 free(tmp);
1690 free(bo->optional_data);
1691 free(bo->description);
Sughosh Ganu885f7a22025-07-03 12:13:07 +05301692 free(bo->file_info.uri);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001693 free(bo->file_info.current_path);
1694 free(bo->initrd_info.current_path);
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001695 free(bo->fdt_info.current_path);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001696 efi_free_pool(device_dp);
1697 efi_free_pool(initrd_device_dp);
1698 efi_free_pool(initrd_dp);
Heinrich Schuchardtf7529f72024-04-26 16:13:11 +02001699 efi_free_pool(fdt_device_dp);
1700 efi_free_pool(fdt_dp);
1701 efi_free_pool(dp);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001702
1703 return ret;
1704}
1705
1706/**
1707 * eficonfig_process_add_boot_option() - handler to add boot option
1708 *
1709 * @data: pointer to the data for each entry
1710 * Return: status code
1711 */
1712static efi_status_t eficonfig_process_add_boot_option(void *data)
1713{
1714 u16 varname[9];
1715 efi_status_t ret;
1716 struct eficonfig_boot_option *bo = NULL;
1717
1718 bo = calloc(1, sizeof(struct eficonfig_boot_option));
1719 if (!bo)
1720 return EFI_OUT_OF_RESOURCES;
1721
Raymond Mao70a76c52023-06-19 14:22:58 -07001722 ret = efi_bootmgr_get_unused_bootoption(varname, sizeof(varname), &bo->boot_index);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001723 if (ret != EFI_SUCCESS)
1724 return ret;
1725
1726 ret = eficonfig_edit_boot_option(varname, bo, NULL, 0, " ** Add Boot Option ** ");
1727 if (ret != EFI_SUCCESS)
1728 goto out;
1729
Raymond Mao70a76c52023-06-19 14:22:58 -07001730 ret = efi_bootmgr_append_bootorder((u16)bo->boot_index);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001731 if (ret != EFI_SUCCESS)
1732 goto out;
1733
1734out:
1735 free(bo);
1736
1737 /* to stay the parent menu */
1738 ret = (ret == EFI_ABORTED) ? EFI_SUCCESS : ret;
1739
1740 return ret;
1741}
1742
1743/**
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001744 * eficonfig_process_boot_selected() - handler to select boot option entry
1745 *
1746 * @data: pointer to the data for each entry
1747 * Return: status code
1748 */
1749static efi_status_t eficonfig_process_boot_selected(void *data)
1750{
1751 struct eficonfig_boot_selection_data *info = data;
1752
1753 if (info)
1754 *info->selected = info->boot_index;
1755
1756 return EFI_SUCCESS;
1757}
1758
1759/**
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001760 * eficonfig_add_boot_selection_entry() - add boot option menu entry
1761 *
1762 * @efi_menu: pointer to store the efimenu structure
1763 * @boot_index: boot option index to be added
1764 * @selected: pointer to store the selected boot option index
1765 * Return: status code
1766 */
1767static efi_status_t eficonfig_add_boot_selection_entry(struct efimenu *efi_menu,
1768 unsigned int boot_index,
1769 unsigned int *selected)
1770{
1771 char *buf, *p;
1772 efi_status_t ret;
1773 efi_uintn_t size;
1774 void *load_option;
1775 struct efi_load_option lo;
1776 u16 varname[] = u"Boot####";
1777 struct eficonfig_boot_selection_data *info;
1778
1779 efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
1780 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
1781 if (!load_option)
1782 return EFI_SUCCESS;
1783
1784 ret = efi_deserialize_load_option(&lo, load_option, &size);
1785 if (ret != EFI_SUCCESS) {
1786 log_warning("Invalid load option for %ls\n", varname);
1787 free(load_option);
1788 return ret;
1789 }
1790
1791 if (size >= sizeof(efi_guid_t) &&
1792 !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) {
1793 /*
1794 * auto generated entry has GUID in optional_data,
1795 * skip auto generated entry because it will be generated
1796 * again even if it is edited or deleted.
1797 */
1798 free(load_option);
1799 return EFI_SUCCESS;
1800 }
1801
1802 info = calloc(1, sizeof(struct eficonfig_boot_selection_data));
1803 if (!info) {
1804 free(load_option);
1805 return EFI_OUT_OF_RESOURCES;
1806 }
1807
1808 buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
1809 if (!buf) {
1810 free(load_option);
1811 free(info);
1812 return EFI_OUT_OF_RESOURCES;
1813 }
1814 p = buf;
1815 utf16_utf8_strcpy(&p, lo.label);
1816 info->boot_index = boot_index;
1817 info->selected = selected;
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001818 ret = eficonfig_append_menu_entry(efi_menu, buf, eficonfig_process_boot_selected, info);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001819 if (ret != EFI_SUCCESS) {
1820 free(load_option);
1821 free(info);
1822 return ret;
1823 }
1824 free(load_option);
1825
1826 return EFI_SUCCESS;
1827}
1828
1829/**
1830 * eficonfig_show_boot_selection() - construct boot option menu entry
1831 *
1832 * @selected: pointer to store the selected boot option index
1833 * Return: status code
1834 */
1835static efi_status_t eficonfig_show_boot_selection(unsigned int *selected)
1836{
1837 u32 i;
1838 u16 *bootorder;
1839 efi_status_t ret;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001840 u16 *var_name16 = NULL;
Masahisa Kojimad414f572022-12-02 13:59:36 +09001841 efi_uintn_t num, size, buf_size;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001842 struct efimenu *efi_menu;
1843 struct list_head *pos, *n;
1844 struct eficonfig_entry *entry;
1845
1846 efi_menu = calloc(1, sizeof(struct efimenu));
1847 if (!efi_menu)
1848 return EFI_OUT_OF_RESOURCES;
1849
1850 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
1851
1852 INIT_LIST_HEAD(&efi_menu->list);
1853 num = size / sizeof(u16);
1854 /* list the load option in the order of BootOrder variable */
1855 for (i = 0; i < num; i++) {
1856 ret = eficonfig_add_boot_selection_entry(efi_menu, bootorder[i], selected);
1857 if (ret != EFI_SUCCESS)
1858 goto out;
1859
1860 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
1861 break;
1862 }
1863
1864 /* list the remaining load option not included in the BootOrder */
Masahisa Kojimad414f572022-12-02 13:59:36 +09001865 buf_size = 128;
1866 var_name16 = malloc(buf_size);
1867 if (!var_name16)
1868 return EFI_OUT_OF_RESOURCES;
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001869
Masahisa Kojimad414f572022-12-02 13:59:36 +09001870 var_name16[0] = 0;
1871 for (;;) {
1872 int index;
1873 efi_guid_t guid;
1874
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001875 ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
Masahisa Kojimad414f572022-12-02 13:59:36 +09001876 if (ret == EFI_NOT_FOUND)
1877 break;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09001878 if (ret != EFI_SUCCESS)
1879 goto out;
1880
Masahisa Kojimad414f572022-12-02 13:59:36 +09001881 if (efi_varname_is_load_option(var_name16, &index)) {
1882 /* If the index is included in the BootOrder, skip it */
Raymond Mao70a76c52023-06-19 14:22:58 -07001883 if (efi_search_bootorder(bootorder, num, index, NULL))
Masahisa Kojimad414f572022-12-02 13:59:36 +09001884 continue;
1885
1886 ret = eficonfig_add_boot_selection_entry(efi_menu, index, selected);
1887 if (ret != EFI_SUCCESS)
1888 goto out;
1889 }
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001890
1891 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
1892 break;
1893 }
1894
Masahisa Kojima6be1d1f2022-11-20 09:21:14 +09001895 ret = eficonfig_append_quit_entry(efi_menu);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001896 if (ret != EFI_SUCCESS)
1897 goto out;
1898
Masahisa Kojimafc811d12023-01-24 15:56:13 +09001899 ret = eficonfig_process_common(efi_menu, " ** Select Boot Option **",
1900 eficonfig_menu_desc,
1901 eficonfig_display_statusline,
1902 eficonfig_print_entry,
1903 eficonfig_choice_entry);
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001904out:
1905 list_for_each_safe(pos, n, &efi_menu->list) {
1906 entry = list_entry(pos, struct eficonfig_entry, list);
1907 free(entry->data);
1908 }
1909 eficonfig_destroy(efi_menu);
1910
Masahisa Kojimad414f572022-12-02 13:59:36 +09001911 free(var_name16);
1912
Masahisa Kojimabb052b92022-09-12 17:33:51 +09001913 return ret;
1914}
1915
1916/**
1917 * eficonfig_process_edit_boot_option() - handler to edit boot option
1918 *
1919 * @data: pointer to the data for each entry
1920 * Return: status code
1921 */
1922static efi_status_t eficonfig_process_edit_boot_option(void *data)
1923{
1924 efi_status_t ret;
1925 efi_uintn_t size;
1926 struct eficonfig_boot_option *bo = NULL;
1927
1928 while (1) {
1929 unsigned int selected;
1930 void *load_option;
1931 u16 varname[] = u"Boot####";
1932
1933 ret = eficonfig_show_boot_selection(&selected);
1934 if (ret != EFI_SUCCESS)
1935 break;
1936
1937 bo = calloc(1, sizeof(struct eficonfig_boot_option));
1938 if (!bo) {
1939 ret = EFI_OUT_OF_RESOURCES;
1940 goto out;
1941 }
1942
1943 bo->boot_index = selected;
1944 efi_create_indexed_name(varname, sizeof(varname), "Boot", selected);
1945 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
1946 if (!load_option) {
1947 free(bo);
1948 ret = EFI_NOT_FOUND;
1949 goto out;
1950 }
1951
1952 ret = eficonfig_edit_boot_option(varname, bo, load_option, size,
1953 " ** Edit Boot Option ** ");
1954
1955 free(load_option);
1956 free(bo);
1957 if (ret != EFI_SUCCESS && ret != EFI_ABORTED)
1958 break;
1959 }
1960out:
1961 /* to stay the parent menu */
1962 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
1963
1964 return ret;
1965}
1966
1967/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001968 * eficonfig_print_change_boot_order_entry() - print the boot option entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001969 *
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001970 * @data: pointer to the data associated with each menu entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09001971 */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001972static void eficonfig_print_change_boot_order_entry(void *data)
Masahisa Kojimae8753692022-09-12 17:33:56 +09001973{
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001974 struct eficonfig_entry *entry = data;
1975 bool reverse = (entry->efi_menu->active == entry->num);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001976
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09001977 if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num)
1978 return;
1979
1980 printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE,
1981 (entry->num - entry->efi_menu->start) + EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001982
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001983 if (reverse)
1984 puts(ANSI_COLOR_REVERSE);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001985
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001986 if (entry->num < entry->efi_menu->count - 2) {
1987 if (((struct eficonfig_boot_order_data *)entry->data)->active)
1988 printf("[*] ");
1989 else
1990 printf("[ ] ");
1991 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09001992
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001993 printf("%s", entry->title);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001994
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09001995 if (reverse)
1996 puts(ANSI_COLOR_RESET);
Masahisa Kojimae8753692022-09-12 17:33:56 +09001997}
1998
1999/**
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002000 * eficonfig_choice_change_boot_order() - user key input handler
Masahisa Kojimae8753692022-09-12 17:33:56 +09002001 *
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002002 * @data: pointer to the menu entry
2003 * Return: key string to identify the selected entry
Masahisa Kojimae8753692022-09-12 17:33:56 +09002004 */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002005char *eficonfig_choice_change_boot_order(void *data)
Masahisa Kojimae8753692022-09-12 17:33:56 +09002006{
Simon Glass9d8d3872023-01-06 08:52:26 -06002007 struct cli_ch_state s_cch, *cch = &s_cch;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002008 struct list_head *pos, *n;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002009 struct efimenu *efi_menu = data;
Simon Glass05ecdf82023-01-06 08:52:22 -06002010 enum bootmenu_key key = BKEY_NONE;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002011 struct eficonfig_entry *entry, *tmp;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002012
Simon Glass9d8d3872023-01-06 08:52:26 -06002013 cli_ch_init(cch);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002014 while (1) {
Simon Glass9d8d3872023-01-06 08:52:26 -06002015 key = bootmenu_loop(NULL, cch);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002016
2017 switch (key) {
Simon Glass05ecdf82023-01-06 08:52:22 -06002018 case BKEY_PLUS:
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002019 if (efi_menu->active > 0 &&
2020 efi_menu->active < efi_menu->count - 2) {
Masahisa Kojimae8753692022-09-12 17:33:56 +09002021 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002022 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002023 if (entry->num == efi_menu->active)
2024 break;
2025 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002026 tmp = list_entry(pos->prev, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002027 entry->num--;
2028 tmp->num++;
2029 list_del(&tmp->list);
2030 list_add(&tmp->list, &entry->list);
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002031
2032 eficonfig_menu_up(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002033 }
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002034 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06002035 case BKEY_UP:
Masahisa Kojimae8753692022-09-12 17:33:56 +09002036 if (efi_menu->active > 0)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002037 eficonfig_menu_up(efi_menu);
2038
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002039 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06002040 case BKEY_MINUS:
Masahisa Kojimae8753692022-09-12 17:33:56 +09002041 if (efi_menu->active < efi_menu->count - 3) {
2042 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002043 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002044 if (entry->num == efi_menu->active)
2045 break;
2046 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002047 tmp = list_entry(pos->next, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002048 entry->num++;
2049 tmp->num--;
2050 list_del(&entry->list);
2051 list_add(&entry->list, &tmp->list);
2052
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002053 eficonfig_menu_down(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002054 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002055 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -06002056 case BKEY_DOWN:
Masahisa Kojimae8753692022-09-12 17:33:56 +09002057 if (efi_menu->active < efi_menu->count - 1)
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002058 eficonfig_menu_down(efi_menu);
2059
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002060 return NULL;
Masahisa Kojimad38ecb72023-02-02 18:24:44 +09002061 case BKEY_SAVE:
2062 /* force to select "Save" entry */
2063 efi_menu->active = efi_menu->count - 2;
2064 fallthrough;
Simon Glass05ecdf82023-01-06 08:52:22 -06002065 case BKEY_SELECT:
Masahisa Kojimae8753692022-09-12 17:33:56 +09002066 /* "Save" */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002067 if (efi_menu->active == efi_menu->count - 2) {
2068 list_for_each_prev_safe(pos, n, &efi_menu->list) {
2069 entry = list_entry(pos, struct eficonfig_entry, list);
2070 if (entry->num == efi_menu->active)
2071 break;
2072 }
2073 return entry->key;
2074 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09002075 /* "Quit" */
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002076 if (efi_menu->active == efi_menu->count - 1) {
2077 entry = list_last_entry(&efi_menu->list,
2078 struct eficonfig_entry,
2079 list);
2080 return entry->key;
2081 }
2082 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09002083 break;
Simon Glass05ecdf82023-01-06 08:52:22 -06002084 case BKEY_SPACE:
Masahisa Kojimae8753692022-09-12 17:33:56 +09002085 if (efi_menu->active < efi_menu->count - 2) {
2086 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002087 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002088 if (entry->num == efi_menu->active) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002089 struct eficonfig_boot_order_data *data = entry->data;
2090
2091 data->active = !data->active;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002092 return NULL;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002093 }
2094 }
2095 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002096 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09002097 break;
Simon Glass05ecdf82023-01-06 08:52:22 -06002098 case BKEY_QUIT:
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002099 entry = list_last_entry(&efi_menu->list,
2100 struct eficonfig_entry, list);
2101 return entry->key;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002102 default:
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002103 /* Pressed key is not valid, wait next key press */
Masahisa Kojimae8753692022-09-12 17:33:56 +09002104 break;
2105 }
2106 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002107}
2108
2109/**
2110 * eficonfig_process_save_boot_order() - callback function for "Save" entry
2111 *
2112 * @data: pointer to the data
2113 * Return: status code
2114 */
2115static efi_status_t eficonfig_process_save_boot_order(void *data)
2116{
2117 u32 count = 0;
2118 efi_status_t ret;
2119 efi_uintn_t size;
2120 struct list_head *pos, *n;
2121 u16 *new_bootorder;
2122 struct efimenu *efi_menu;
2123 struct eficonfig_entry *entry;
2124 struct eficonfig_save_boot_order_data *save_data = data;
2125
2126 efi_menu = save_data->efi_menu;
2127
2128 /*
2129 * The change boot order menu always has "Save" and "Quit" entries.
2130 * !(efi_menu->count - 2) means there is no user defined boot option.
2131 */
2132 if (!(efi_menu->count - 2))
2133 return EFI_SUCCESS;
2134
2135 new_bootorder = calloc(1, (efi_menu->count - 2) * sizeof(u16));
2136 if (!new_bootorder) {
2137 ret = EFI_OUT_OF_RESOURCES;
2138 goto out;
2139 }
2140
2141 /* create new BootOrder */
2142 count = 0;
2143 list_for_each_safe(pos, n, &efi_menu->list) {
2144 struct eficonfig_boot_order_data *data;
2145
2146 entry = list_entry(pos, struct eficonfig_entry, list);
2147 /* exit the loop when iteration reaches "Save" */
2148 if (!strncmp(entry->title, "Save", strlen("Save")))
2149 break;
2150
2151 data = entry->data;
2152 if (data->active)
2153 new_bootorder[count++] = data->boot_index;
2154 }
2155
2156 size = count * sizeof(u16);
2157 ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
2158 EFI_VARIABLE_NON_VOLATILE |
2159 EFI_VARIABLE_BOOTSERVICE_ACCESS |
2160 EFI_VARIABLE_RUNTIME_ACCESS,
2161 size, new_bootorder, false);
2162
2163 save_data->selected = true;
2164out:
2165 free(new_bootorder);
2166
2167 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002168}
2169
2170/**
2171 * eficonfig_add_change_boot_order_entry() - add boot order entry
2172 *
2173 * @efi_menu: pointer to the efimenu structure
2174 * @boot_index: boot option index to be added
2175 * @active: flag to include the boot option into BootOrder
2176 * Return: status code
2177 */
2178static efi_status_t eficonfig_add_change_boot_order_entry(struct efimenu *efi_menu,
2179 u32 boot_index, bool active)
2180{
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002181 char *title, *p;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002182 efi_status_t ret;
2183 efi_uintn_t size;
2184 void *load_option;
2185 struct efi_load_option lo;
2186 u16 varname[] = u"Boot####";
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002187 struct eficonfig_boot_order_data *data;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002188
2189 efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
2190 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
2191 if (!load_option)
2192 return EFI_SUCCESS;
2193
2194 ret = efi_deserialize_load_option(&lo, load_option, &size);
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002195 if (ret != EFI_SUCCESS)
2196 goto out;
2197
2198 data = calloc(1, sizeof(*data));
2199 if (!data) {
2200 ret = EFI_OUT_OF_RESOURCES;
2201 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002202 }
2203
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002204 title = calloc(1, utf16_utf8_strlen(lo.label) + 1);
2205 if (!title) {
2206 free(data);
2207 ret = EFI_OUT_OF_RESOURCES;
2208 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002209 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002210 p = title;
2211 utf16_utf8_strcpy(&p, lo.label);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002212
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002213 data->boot_index = boot_index;
2214 data->active = active;
2215
2216 ret = eficonfig_append_menu_entry(efi_menu, title, NULL, data);
2217 if (ret != EFI_SUCCESS) {
2218 free(data);
2219 free(title);
2220 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002221 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09002222
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002223out:
Masahisa Kojimae8753692022-09-12 17:33:56 +09002224 free(load_option);
2225
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002226 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002227}
2228
2229/**
2230 * eficonfig_create_change_boot_order_entry() - create boot order entry
2231 *
2232 * @efi_menu: pointer to the efimenu structure
2233 * @bootorder: pointer to the BootOrder variable
2234 * @num: number of BootOrder entry
2235 * Return: status code
2236 */
2237static efi_status_t eficonfig_create_change_boot_order_entry(struct efimenu *efi_menu,
2238 u16 *bootorder, efi_uintn_t num)
2239{
2240 u32 i;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002241 char *title;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002242 efi_status_t ret;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002243 u16 *var_name16 = NULL;
Masahisa Kojimad414f572022-12-02 13:59:36 +09002244 efi_uintn_t size, buf_size;
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002245 struct eficonfig_save_boot_order_data *save_data;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002246
2247 /* list the load option in the order of BootOrder variable */
2248 for (i = 0; i < num; i++) {
2249 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
2250 break;
2251
2252 ret = eficonfig_add_change_boot_order_entry(efi_menu, bootorder[i], true);
2253 if (ret != EFI_SUCCESS)
2254 goto out;
2255 }
2256
2257 /* list the remaining load option not included in the BootOrder */
Masahisa Kojimad414f572022-12-02 13:59:36 +09002258 buf_size = 128;
2259 var_name16 = malloc(buf_size);
2260 if (!var_name16)
2261 return EFI_OUT_OF_RESOURCES;
2262
2263 var_name16[0] = 0;
2264 for (;;) {
2265 int index;
2266 efi_guid_t guid;
2267
Masahisa Kojimae8753692022-09-12 17:33:56 +09002268 if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
2269 break;
2270
Masahisa Kojimad414f572022-12-02 13:59:36 +09002271 size = buf_size;
Masahisa Kojima7ec3c6f2022-12-19 11:33:12 +09002272 ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
Masahisa Kojimad414f572022-12-02 13:59:36 +09002273 if (ret == EFI_NOT_FOUND)
2274 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002275 if (ret != EFI_SUCCESS)
2276 goto out;
Masahisa Kojimad414f572022-12-02 13:59:36 +09002277
2278 if (efi_varname_is_load_option(var_name16, &index)) {
2279 /* If the index is included in the BootOrder, skip it */
Raymond Mao70a76c52023-06-19 14:22:58 -07002280 if (efi_search_bootorder(bootorder, num, index, NULL))
Masahisa Kojimad414f572022-12-02 13:59:36 +09002281 continue;
2282
2283 ret = eficonfig_add_change_boot_order_entry(efi_menu, index, false);
2284 if (ret != EFI_SUCCESS)
2285 goto out;
2286 }
Masahisa Kojimae8753692022-09-12 17:33:56 +09002287 }
2288
2289 /* add "Save" and "Quit" entries */
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002290 title = strdup("Save");
2291 if (!title) {
2292 ret = EFI_OUT_OF_RESOURCES;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002293 goto out;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002294 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002295
2296 save_data = malloc(sizeof(struct eficonfig_save_boot_order_data));
2297 if (!save_data) {
2298 ret = EFI_OUT_OF_RESOURCES;
2299 goto out;
2300 }
2301 save_data->efi_menu = efi_menu;
2302 save_data->selected = false;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002303
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002304 ret = eficonfig_append_menu_entry(efi_menu, title,
2305 eficonfig_process_save_boot_order,
2306 save_data);
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002307 if (ret != EFI_SUCCESS)
Masahisa Kojimae8753692022-09-12 17:33:56 +09002308 goto out;
2309
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002310 ret = eficonfig_append_quit_entry(efi_menu);
2311 if (ret != EFI_SUCCESS)
2312 goto out;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002313
2314 efi_menu->active = 0;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002315out:
Masahisa Kojimad414f572022-12-02 13:59:36 +09002316 free(var_name16);
2317
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002318 return ret;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002319}
2320
2321/**
2322 * eficonfig_process_change_boot_order() - handler to change boot order
2323 *
2324 * @data: pointer to the data for each entry
2325 * Return: status code
2326 */
2327static efi_status_t eficonfig_process_change_boot_order(void *data)
2328{
Masahisa Kojimae8753692022-09-12 17:33:56 +09002329 u16 *bootorder;
2330 efi_status_t ret;
2331 efi_uintn_t num, size;
2332 struct list_head *pos, *n;
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002333 struct eficonfig_entry *entry;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002334 struct efimenu *efi_menu;
2335
2336 efi_menu = calloc(1, sizeof(struct efimenu));
2337 if (!efi_menu)
2338 return EFI_OUT_OF_RESOURCES;
2339
2340 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
2341
2342 INIT_LIST_HEAD(&efi_menu->list);
2343 num = size / sizeof(u16);
2344 ret = eficonfig_create_change_boot_order_entry(efi_menu, bootorder, num);
2345 if (ret != EFI_SUCCESS)
2346 goto out;
2347
2348 while (1) {
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002349 ret = eficonfig_process_common(efi_menu,
2350 " ** Change Boot Order **",
2351 eficonfig_change_boot_order_desc,
2352 eficonfig_display_statusline,
2353 eficonfig_print_change_boot_order_entry,
2354 eficonfig_choice_change_boot_order);
2355 /* exit from the menu if user selects the "Save" entry. */
2356 if (ret == EFI_SUCCESS && efi_menu->active == (efi_menu->count - 2)) {
2357 list_for_each_prev_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002358 entry = list_entry(pos, struct eficonfig_entry, list);
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002359 if (entry->num == efi_menu->active)
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002360 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002361 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002362 if (((struct eficonfig_save_boot_order_data *)entry->data)->selected)
2363 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002364 }
Masahisa Kojimab3a96ee2023-01-24 15:56:14 +09002365 if (ret != EFI_SUCCESS)
2366 break;
Masahisa Kojimae8753692022-09-12 17:33:56 +09002367 }
2368out:
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002369 free(bootorder);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002370 list_for_each_safe(pos, n, &efi_menu->list) {
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002371 entry = list_entry(pos, struct eficonfig_entry, list);
2372 free(entry->data);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002373 }
Masahisa Kojima5c9c2fc2022-11-20 09:21:15 +09002374 eficonfig_destroy(efi_menu);
Masahisa Kojimae8753692022-09-12 17:33:56 +09002375
2376 /* to stay the parent menu */
2377 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
2378
2379 return ret;
2380}
2381
2382/**
Masahisa Kojima703ad322022-09-12 17:33:53 +09002383 * eficonfig_process_delete_boot_option() - handler to delete boot option
2384 *
2385 * @data: pointer to the data for each entry
2386 * Return: status code
2387 */
2388static efi_status_t eficonfig_process_delete_boot_option(void *data)
2389{
2390 efi_status_t ret;
2391 unsigned int selected;
2392
2393 while (1) {
2394 ret = eficonfig_show_boot_selection(&selected);
2395 if (ret == EFI_SUCCESS)
Raymond Mao70a76c52023-06-19 14:22:58 -07002396 ret = efi_bootmgr_delete_boot_option(selected);
Masahisa Kojima703ad322022-09-12 17:33:53 +09002397
2398 if (ret != EFI_SUCCESS)
2399 break;
2400 }
2401
2402 /* to stay the parent menu */
2403 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
2404
2405 return ret;
2406}
2407
2408/**
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002409 * eficonfig_init() - do required initialization for eficonfig command
2410 *
2411 * Return: status code
2412 */
2413static efi_status_t eficonfig_init(void)
2414{
2415 efi_status_t ret = EFI_SUCCESS;
2416 static bool init;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002417 unsigned long columns, rows;
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002418
2419 if (!init) {
Heinrich Schuchardtac8d48a2025-03-10 07:13:43 +01002420 cout = systab.con_out;
2421 cin = systab.con_in;
Masahisa Kojimaa93282c2023-01-24 15:56:15 +09002422
2423 cout->query_mode(cout, cout->mode->mode, &columns, &rows);
2424 avail_row = rows - (EFICONFIG_MENU_HEADER_ROW_NUM +
2425 EFICONFIG_MENU_DESC_ROW_NUM);
2426 if (avail_row <= 0) {
2427 eficonfig_print_msg("Console size is too small!");
2428 return EFI_INVALID_PARAMETER;
2429 }
2430 /* TODO: Should we check the minimum column size? */
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002431 }
2432
2433 init = true;
2434
2435 return ret;
2436}
2437
2438static const struct eficonfig_item maintenance_menu_items[] = {
2439 {"Add Boot Option", eficonfig_process_add_boot_option},
Masahisa Kojimabb052b92022-09-12 17:33:51 +09002440 {"Edit Boot Option", eficonfig_process_edit_boot_option},
Masahisa Kojimae8753692022-09-12 17:33:56 +09002441 {"Change Boot Order", eficonfig_process_change_boot_order},
Masahisa Kojima703ad322022-09-12 17:33:53 +09002442 {"Delete Boot Option", eficonfig_process_delete_boot_option},
Simon Glasseba70582023-02-05 15:39:45 -07002443#if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT) && IS_ENABLED(CONFIG_EFI_MM_COMM_TEE))
Masahisa Kojimacbdbcb42022-11-20 09:21:18 +09002444 {"Secure Boot Configuration", eficonfig_process_secure_boot_config},
2445#endif
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002446 {"Quit", eficonfig_process_quit},
2447};
2448
2449/**
2450 * do_eficonfig() - execute `eficonfig` command
2451 *
2452 * @cmdtp: table entry describing command
2453 * @flag: bitmap indicating how the command was invoked
2454 * @argc: number of arguments
2455 * @argv: command line arguments
2456 * Return: status code
2457 */
2458static int do_eficonfig(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
2459{
2460 efi_status_t ret;
2461 struct efimenu *efi_menu;
2462
2463 if (argc > 1)
2464 return CMD_RET_USAGE;
2465
2466 ret = efi_init_obj_list();
2467 if (ret != EFI_SUCCESS) {
2468 log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
2469 ret & ~EFI_ERROR_MASK);
2470
2471 return CMD_RET_FAILURE;
2472 }
2473
2474 ret = eficonfig_init();
2475 if (ret != EFI_SUCCESS)
2476 return CMD_RET_FAILURE;
2477
Raymond Mao70a76c52023-06-19 14:22:58 -07002478 ret = efi_bootmgr_update_media_device_boot_option();
Raymond Maoa35784d2023-06-19 14:22:59 -07002479 if (ret != EFI_SUCCESS)
Masahisa Kojimaf648fa32022-09-12 17:33:55 +09002480 return ret;
2481
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002482 while (1) {
2483 efi_menu = eficonfig_create_fixed_menu(maintenance_menu_items,
2484 ARRAY_SIZE(maintenance_menu_items));
2485 if (!efi_menu)
2486 return CMD_RET_FAILURE;
2487
Masahisa Kojimafc811d12023-01-24 15:56:13 +09002488 ret = eficonfig_process_common(efi_menu,
2489 " ** UEFI Maintenance Menu **",
2490 eficonfig_menu_desc,
2491 eficonfig_display_statusline,
2492 eficonfig_print_entry,
2493 eficonfig_choice_entry);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09002494 eficonfig_destroy(efi_menu);
2495
2496 if (ret == EFI_ABORTED)
2497 break;
2498 }
2499
2500 return CMD_RET_SUCCESS;
2501}
2502
2503U_BOOT_CMD(
2504 eficonfig, 1, 0, do_eficonfig,
2505 "provide menu-driven UEFI variable maintenance interface",
2506 ""
2507);