]>
Commit | Line | Data |
---|---|---|
87d79142 MK |
1 | // 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> | |
32bab0ea | 9 | #include <cli.h> |
87d79142 MK |
10 | #include <common.h> |
11 | #include <charset.h> | |
12 | #include <efi_loader.h> | |
13 | #include <efi_load_initrd.h> | |
14 | #include <efi_config.h> | |
15 | #include <efi_variable.h> | |
16 | #include <log.h> | |
17 | #include <malloc.h> | |
18 | #include <menu.h> | |
19 | #include <sort.h> | |
20 | #include <watchdog.h> | |
21 | #include <asm/unaligned.h> | |
22 | #include <linux/delay.h> | |
23 | ||
24 | static struct efi_simple_text_input_protocol *cin; | |
cd160b27 | 25 | const char *eficonfig_menu_desc = |
45f5319f | 26 | " Press UP/DOWN to move, ENTER to select, ESC to quit"; |
87d79142 | 27 | |
0d590852 MK |
28 | static 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" | |
88df3634 | 31 | " CTRL+S to save, ESC to quit"; |
0d590852 | 32 | |
8dbd0a0f MK |
33 | static struct efi_simple_text_output_protocol *cout; |
34 | static int avail_row; | |
35 | ||
87d79142 MK |
36 | #define EFICONFIG_DESCRIPTION_MAX 32 |
37 | #define EFICONFIG_OPTIONAL_DATA_MAX 64 | |
8dbd0a0f MK |
38 | #define EFICONFIG_MENU_HEADER_ROW_NUM 3 |
39 | #define EFICONFIG_MENU_DESC_ROW_NUM 5 | |
87d79142 MK |
40 | |
41 | /** | |
42 | * struct eficonfig_filepath_info - structure to be used to store file path | |
43 | * | |
44 | * @name: file or directory name | |
45 | * @list: list structure | |
46 | */ | |
47 | struct eficonfig_filepath_info { | |
48 | char *name; | |
49 | struct list_head list; | |
50 | }; | |
51 | ||
52 | /** | |
53 | * struct eficonfig_boot_option - structure to be used for updating UEFI boot option | |
54 | * | |
55 | * @file_info: user selected file info | |
56 | * @initrd_info: user selected initrd file info | |
57 | * @boot_index: index of the boot option | |
58 | * @description: pointer to the description string | |
59 | * @optional_data: pointer to the optional_data | |
60 | * @edit_completed: flag indicates edit complete | |
61 | */ | |
62 | struct eficonfig_boot_option { | |
63 | struct eficonfig_select_file_info file_info; | |
64 | struct eficonfig_select_file_info initrd_info; | |
65 | unsigned int boot_index; | |
66 | u16 *description; | |
67 | u16 *optional_data; | |
68 | bool edit_completed; | |
69 | }; | |
70 | ||
71 | /** | |
72 | * struct eficonfig_volume_entry_data - structure to be used to store volume info | |
73 | * | |
74 | * @file_info: pointer to file info structure | |
75 | * @v: pointer to the protocol interface | |
76 | * @dp: pointer to the device path | |
77 | */ | |
78 | struct eficonfig_volume_entry_data { | |
79 | struct eficonfig_select_file_info *file_info; | |
80 | struct efi_simple_file_system_protocol *v; | |
81 | struct efi_device_path *dp; | |
82 | }; | |
83 | ||
84 | /** | |
85 | * struct eficonfig_file_entry_data - structure to be used to store file info | |
86 | * | |
87 | * @file_info: pointer to file info structure | |
88 | * @is_directory: flag to identify the directory or file | |
89 | * @file_name: name of directory or file | |
90 | */ | |
91 | struct eficonfig_file_entry_data { | |
92 | struct eficonfig_select_file_info *file_info; | |
93 | bool is_directory; | |
94 | char *file_name; | |
95 | }; | |
96 | ||
e34158bc MK |
97 | /** |
98 | * struct eficonfig_boot_selection_data - structure to be used to select the boot option entry | |
99 | * | |
100 | * @boot_index: index of the boot option | |
101 | * @selected: pointer to store the selected index in the BootOrder variable | |
102 | */ | |
103 | struct eficonfig_boot_selection_data { | |
104 | u16 boot_index; | |
105 | int *selected; | |
106 | }; | |
107 | ||
e5948ee3 | 108 | /** |
d571f9b2 | 109 | * struct eficonfig_boot_order_data - structure to be used to update BootOrder variable |
e5948ee3 | 110 | * |
e5948ee3 MK |
111 | * @boot_index: boot option index |
112 | * @active: flag to include the boot option into BootOrder variable | |
e5948ee3 | 113 | */ |
d571f9b2 | 114 | struct eficonfig_boot_order_data { |
e5948ee3 MK |
115 | u32 boot_index; |
116 | bool active; | |
e5948ee3 MK |
117 | }; |
118 | ||
0d590852 MK |
119 | /** |
120 | * struct eficonfig_save_boot_order_data - structure to be used to change boot order | |
121 | * | |
122 | * @efi_menu: pointer to efimenu structure | |
123 | * @selected: flag to indicate user selects "Save" entry | |
124 | */ | |
125 | struct eficonfig_save_boot_order_data { | |
126 | struct efimenu *efi_menu; | |
127 | bool selected; | |
128 | }; | |
129 | ||
8dbd0a0f MK |
130 | /** |
131 | * struct eficonfig_menu_adjust - update start and end entry index | |
132 | * | |
133 | * @efi_menu: pointer to efimenu structure | |
134 | * @add: flag to add or substract the index | |
135 | */ | |
136 | static void eficonfig_menu_adjust(struct efimenu *efi_menu, bool add) | |
137 | { | |
138 | if (add) | |
139 | ++efi_menu->active; | |
140 | else | |
141 | --efi_menu->active; | |
142 | ||
143 | if (add && efi_menu->end < efi_menu->active) { | |
144 | efi_menu->start++; | |
145 | efi_menu->end++; | |
146 | } else if (!add && efi_menu->start > efi_menu->active) { | |
147 | efi_menu->start--; | |
148 | efi_menu->end--; | |
149 | } | |
150 | } | |
151 | #define eficonfig_menu_up(_a) eficonfig_menu_adjust(_a, false) | |
152 | #define eficonfig_menu_down(_a) eficonfig_menu_adjust(_a, true) | |
153 | ||
87d79142 MK |
154 | /** |
155 | * eficonfig_print_msg() - print message | |
156 | * | |
157 | * display the message to the user, user proceeds the screen | |
158 | * with any key press. | |
159 | * | |
160 | * @items: pointer to the structure of each menu entry | |
161 | * @count: the number of menu entry | |
162 | * @menu_header: pointer to the menu header string | |
163 | * Return: status code | |
164 | */ | |
165 | void eficonfig_print_msg(char *msg) | |
166 | { | |
167 | /* Flush input */ | |
168 | while (tstc()) | |
169 | getchar(); | |
170 | ||
171 | printf(ANSI_CURSOR_HIDE | |
172 | ANSI_CLEAR_CONSOLE | |
173 | ANSI_CURSOR_POSITION | |
174 | "%s\n\n Press any key to continue", 3, 4, msg); | |
175 | ||
176 | getchar(); | |
177 | } | |
178 | ||
179 | /** | |
180 | * eficonfig_print_entry() - print each menu entry | |
181 | * | |
182 | * @data: pointer to the data associated with each menu entry | |
183 | */ | |
cd160b27 | 184 | void eficonfig_print_entry(void *data) |
87d79142 MK |
185 | { |
186 | struct eficonfig_entry *entry = data; | |
cd160b27 | 187 | bool reverse = (entry->efi_menu->active == entry->num); |
87d79142 | 188 | |
8dbd0a0f MK |
189 | if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num) |
190 | return; | |
87d79142 | 191 | |
8dbd0a0f MK |
192 | printf(ANSI_CURSOR_POSITION, (entry->num - entry->efi_menu->start) + |
193 | EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7); | |
87d79142 MK |
194 | |
195 | if (reverse) | |
196 | puts(ANSI_COLOR_REVERSE); | |
197 | ||
8dbd0a0f | 198 | printf(ANSI_CLEAR_LINE "%s", entry->title); |
87d79142 MK |
199 | |
200 | if (reverse) | |
201 | puts(ANSI_COLOR_RESET); | |
202 | } | |
203 | ||
204 | /** | |
205 | * eficonfig_display_statusline() - print status line | |
206 | * | |
207 | * @m: pointer to the menu structure | |
208 | */ | |
cd160b27 | 209 | void eficonfig_display_statusline(struct menu *m) |
87d79142 MK |
210 | { |
211 | struct eficonfig_entry *entry; | |
212 | ||
213 | if (menu_default_choice(m, (void *)&entry) < 0) | |
214 | return; | |
215 | ||
216 | printf(ANSI_CURSOR_POSITION | |
217 | "\n%s\n" | |
218 | ANSI_CURSOR_POSITION ANSI_CLEAR_LINE ANSI_CURSOR_POSITION | |
cd160b27 | 219 | "%s" |
0d590852 | 220 | ANSI_CLEAR_LINE_TO_END, |
8dbd0a0f MK |
221 | 1, 1, entry->efi_menu->menu_header, avail_row + 4, 1, |
222 | avail_row + 5, 1, entry->efi_menu->menu_desc); | |
87d79142 MK |
223 | } |
224 | ||
225 | /** | |
226 | * eficonfig_choice_entry() - user key input handler | |
227 | * | |
228 | * @data: pointer to the efimenu structure | |
229 | * Return: key string to identify the selected entry | |
230 | */ | |
cd160b27 | 231 | char *eficonfig_choice_entry(void *data) |
87d79142 | 232 | { |
32bab0ea | 233 | struct cli_ch_state s_cch, *cch = &s_cch; |
87d79142 MK |
234 | struct list_head *pos, *n; |
235 | struct eficonfig_entry *entry; | |
2da4a15e | 236 | enum bootmenu_key key = BKEY_NONE; |
87d79142 MK |
237 | struct efimenu *efi_menu = data; |
238 | ||
32bab0ea SG |
239 | cli_ch_init(cch); |
240 | ||
87d79142 | 241 | while (1) { |
32bab0ea | 242 | key = bootmenu_loop((struct bootmenu_data *)efi_menu, cch); |
87d79142 MK |
243 | |
244 | switch (key) { | |
2da4a15e | 245 | case BKEY_UP: |
87d79142 | 246 | if (efi_menu->active > 0) |
8dbd0a0f MK |
247 | eficonfig_menu_up(efi_menu); |
248 | ||
87d79142 MK |
249 | /* no menu key selected, regenerate menu */ |
250 | return NULL; | |
2da4a15e | 251 | case BKEY_DOWN: |
87d79142 | 252 | if (efi_menu->active < efi_menu->count - 1) |
8dbd0a0f MK |
253 | eficonfig_menu_down(efi_menu); |
254 | ||
87d79142 MK |
255 | /* no menu key selected, regenerate menu */ |
256 | return NULL; | |
2da4a15e | 257 | case BKEY_SELECT: |
87d79142 MK |
258 | list_for_each_safe(pos, n, &efi_menu->list) { |
259 | entry = list_entry(pos, struct eficonfig_entry, list); | |
260 | if (entry->num == efi_menu->active) | |
261 | return entry->key; | |
262 | } | |
263 | break; | |
2da4a15e | 264 | case BKEY_QUIT: |
87d79142 MK |
265 | /* Quit by choosing the last entry */ |
266 | entry = list_last_entry(&efi_menu->list, struct eficonfig_entry, list); | |
267 | return entry->key; | |
268 | default: | |
269 | /* Pressed key is not valid, no need to regenerate the menu */ | |
270 | break; | |
271 | } | |
272 | } | |
273 | } | |
274 | ||
275 | /** | |
276 | * eficonfig_destroy() - destroy efimenu | |
277 | * | |
278 | * @efi_menu: pointer to the efimenu structure | |
279 | */ | |
280 | void eficonfig_destroy(struct efimenu *efi_menu) | |
281 | { | |
282 | struct list_head *pos, *n; | |
283 | struct eficonfig_entry *entry; | |
284 | ||
285 | if (!efi_menu) | |
286 | return; | |
287 | ||
288 | list_for_each_safe(pos, n, &efi_menu->list) { | |
289 | entry = list_entry(pos, struct eficonfig_entry, list); | |
290 | free(entry->title); | |
291 | list_del(&entry->list); | |
292 | free(entry); | |
293 | } | |
294 | free(efi_menu->menu_header); | |
295 | free(efi_menu); | |
296 | } | |
297 | ||
298 | /** | |
299 | * eficonfig_process_quit() - callback function for "Quit" entry | |
300 | * | |
301 | * @data: pointer to the data | |
302 | * Return: status code | |
303 | */ | |
304 | efi_status_t eficonfig_process_quit(void *data) | |
305 | { | |
306 | return EFI_ABORTED; | |
307 | } | |
308 | ||
309 | /** | |
8961e93e | 310 | * eficonfig_append_menu_entry() - append menu item |
87d79142 MK |
311 | * |
312 | * @efi_menu: pointer to the efimenu structure | |
313 | * @title: pointer to the entry title | |
314 | * @func: callback of each entry | |
315 | * @data: pointer to the data to be passed to each entry callback | |
316 | * Return: status code | |
317 | */ | |
8961e93e MK |
318 | efi_status_t eficonfig_append_menu_entry(struct efimenu *efi_menu, |
319 | char *title, eficonfig_entry_func func, | |
320 | void *data) | |
87d79142 MK |
321 | { |
322 | struct eficonfig_entry *entry; | |
323 | ||
324 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX) | |
325 | return EFI_OUT_OF_RESOURCES; | |
326 | ||
327 | entry = calloc(1, sizeof(struct eficonfig_entry)); | |
328 | if (!entry) | |
329 | return EFI_OUT_OF_RESOURCES; | |
330 | ||
331 | entry->title = title; | |
332 | sprintf(entry->key, "%d", efi_menu->count); | |
333 | entry->efi_menu = efi_menu; | |
334 | entry->func = func; | |
335 | entry->data = data; | |
336 | entry->num = efi_menu->count++; | |
337 | list_add_tail(&entry->list, &efi_menu->list); | |
338 | ||
339 | return EFI_SUCCESS; | |
340 | } | |
341 | ||
342 | /** | |
8961e93e | 343 | * eficonfig_append_quit_entry() - append quit entry |
87d79142 MK |
344 | * |
345 | * @efi_menu: pointer to the efimenu structure | |
346 | * Return: status code | |
347 | */ | |
8961e93e | 348 | efi_status_t eficonfig_append_quit_entry(struct efimenu *efi_menu) |
87d79142 MK |
349 | { |
350 | char *title; | |
351 | efi_status_t ret; | |
352 | ||
353 | title = strdup("Quit"); | |
354 | if (!title) | |
355 | return EFI_OUT_OF_RESOURCES; | |
356 | ||
8961e93e | 357 | ret = eficonfig_append_menu_entry(efi_menu, title, eficonfig_process_quit, NULL); |
87d79142 MK |
358 | if (ret != EFI_SUCCESS) |
359 | free(title); | |
360 | ||
361 | return ret; | |
362 | } | |
363 | ||
364 | /** | |
365 | * eficonfig_create_fixed_menu() - create fixed entry menu structure | |
366 | * | |
367 | * @items: pointer to the menu entry item | |
368 | * @count: the number of menu entry | |
369 | * Return: pointer to the efimenu structure | |
370 | */ | |
371 | void *eficonfig_create_fixed_menu(const struct eficonfig_item *items, int count) | |
372 | { | |
373 | u32 i; | |
374 | char *title; | |
375 | efi_status_t ret; | |
376 | struct efimenu *efi_menu; | |
377 | const struct eficonfig_item *iter = items; | |
378 | ||
379 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
380 | if (!efi_menu) | |
381 | return NULL; | |
382 | ||
383 | INIT_LIST_HEAD(&efi_menu->list); | |
384 | for (i = 0; i < count; i++, iter++) { | |
385 | title = strdup(iter->title); | |
386 | if (!title) | |
387 | goto out; | |
388 | ||
8961e93e | 389 | ret = eficonfig_append_menu_entry(efi_menu, title, iter->func, iter->data); |
87d79142 MK |
390 | if (ret != EFI_SUCCESS) { |
391 | free(title); | |
392 | goto out; | |
393 | } | |
394 | } | |
395 | ||
396 | return efi_menu; | |
397 | out: | |
398 | eficonfig_destroy(efi_menu); | |
399 | ||
400 | return NULL; | |
401 | } | |
402 | ||
403 | /** | |
404 | * eficonfig_process_common() - main handler for UEFI menu | |
405 | * | |
406 | * Construct the structures required to show the menu, then handle | |
407 | * the user input interacting with u-boot menu functions. | |
408 | * | |
409 | * @efi_menu: pointer to the efimenu structure | |
410 | * @menu_header: pointer to the menu header string | |
cd160b27 MK |
411 | * @menu_desc: pointer to the menu description |
412 | * @display_statusline: function pointer to draw statusline | |
413 | * @item_data_print: function pointer to draw the menu item | |
414 | * @item_choice: function pointer to handle the key press | |
87d79142 MK |
415 | * Return: status code |
416 | */ | |
cd160b27 MK |
417 | efi_status_t eficonfig_process_common(struct efimenu *efi_menu, |
418 | char *menu_header, const char *menu_desc, | |
419 | void (*display_statusline)(struct menu *), | |
420 | void (*item_data_print)(void *), | |
421 | char *(*item_choice)(void *)) | |
87d79142 MK |
422 | { |
423 | struct menu *menu; | |
424 | void *choice = NULL; | |
425 | struct list_head *pos, *n; | |
426 | struct eficonfig_entry *entry; | |
427 | efi_status_t ret = EFI_SUCCESS; | |
428 | ||
429 | if (efi_menu->count > EFICONFIG_ENTRY_NUM_MAX) | |
430 | return EFI_OUT_OF_RESOURCES; | |
431 | ||
432 | efi_menu->delay = -1; | |
433 | efi_menu->active = 0; | |
8dbd0a0f MK |
434 | efi_menu->start = 0; |
435 | efi_menu->end = avail_row - 1; | |
87d79142 MK |
436 | |
437 | if (menu_header) { | |
438 | efi_menu->menu_header = strdup(menu_header); | |
439 | if (!efi_menu->menu_header) | |
440 | return EFI_OUT_OF_RESOURCES; | |
441 | } | |
cd160b27 MK |
442 | if (menu_desc) |
443 | efi_menu->menu_desc = menu_desc; | |
87d79142 | 444 | |
cd160b27 MK |
445 | menu = menu_create(NULL, 0, 1, display_statusline, item_data_print, |
446 | item_choice, efi_menu); | |
87d79142 MK |
447 | if (!menu) |
448 | return EFI_INVALID_PARAMETER; | |
449 | ||
450 | list_for_each_safe(pos, n, &efi_menu->list) { | |
451 | entry = list_entry(pos, struct eficonfig_entry, list); | |
452 | if (!menu_item_add(menu, entry->key, entry)) { | |
453 | ret = EFI_INVALID_PARAMETER; | |
454 | goto out; | |
455 | } | |
456 | } | |
457 | ||
458 | entry = list_first_entry_or_null(&efi_menu->list, struct eficonfig_entry, list); | |
459 | if (entry) | |
460 | menu_default_set(menu, entry->key); | |
461 | ||
462 | printf(ANSI_CURSOR_HIDE | |
463 | ANSI_CLEAR_CONSOLE | |
464 | ANSI_CURSOR_POSITION, 1, 1); | |
465 | ||
466 | if (menu_get_choice(menu, &choice)) { | |
467 | entry = choice; | |
468 | if (entry->func) | |
469 | ret = entry->func(entry->data); | |
470 | } | |
471 | out: | |
472 | menu_destroy(menu); | |
473 | ||
474 | printf(ANSI_CLEAR_CONSOLE | |
475 | ANSI_CURSOR_POSITION | |
476 | ANSI_CURSOR_SHOW, 1, 1); | |
477 | ||
478 | return ret; | |
479 | } | |
480 | ||
481 | /** | |
482 | * eficonfig_volume_selected() - handler of volume selection | |
483 | * | |
484 | * @data: pointer to the data of selected entry | |
485 | * Return: status code | |
486 | */ | |
487 | static efi_status_t eficonfig_volume_selected(void *data) | |
488 | { | |
489 | struct eficonfig_volume_entry_data *info = data; | |
490 | ||
491 | if (info) { | |
492 | info->file_info->current_volume = info->v; | |
493 | info->file_info->dp_volume = info->dp; | |
494 | } | |
495 | ||
496 | return EFI_SUCCESS; | |
497 | } | |
498 | ||
499 | /** | |
d6566113 | 500 | * eficonfig_create_device_path() - create device path |
87d79142 | 501 | * |
d6566113 MK |
502 | * @dp_volume: pointer to the volume |
503 | * @current_path: pointer to the file path u16 string | |
87d79142 MK |
504 | * Return: |
505 | * device path or NULL. Caller must free the returned value | |
506 | */ | |
d6566113 MK |
507 | struct efi_device_path *eficonfig_create_device_path(struct efi_device_path *dp_volume, |
508 | u16 *current_path) | |
87d79142 MK |
509 | { |
510 | char *p; | |
511 | void *buf; | |
512 | efi_uintn_t fp_size; | |
513 | struct efi_device_path *dp; | |
514 | struct efi_device_path_file_path *fp; | |
515 | ||
78b1ccc4 | 516 | fp_size = sizeof(struct efi_device_path) + u16_strsize(current_path); |
87d79142 MK |
517 | buf = calloc(1, fp_size + sizeof(END)); |
518 | if (!buf) | |
519 | return NULL; | |
520 | ||
521 | fp = buf; | |
522 | fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE, | |
523 | fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, | |
524 | fp->dp.length = (u16)fp_size; | |
d6566113 | 525 | u16_strcpy(fp->str, current_path); |
87d79142 MK |
526 | |
527 | p = buf; | |
528 | p += fp_size; | |
529 | *((struct efi_device_path *)p) = END; | |
530 | ||
64658007 HS |
531 | dp = efi_dp_shorten(dp_volume); |
532 | if (!dp) | |
533 | dp = dp_volume; | |
534 | dp = efi_dp_append(dp, &fp->dp); | |
87d79142 MK |
535 | free(buf); |
536 | ||
537 | return dp; | |
538 | } | |
539 | ||
540 | /** | |
541 | * eficonfig_file_selected() - handler of file selection | |
542 | * | |
543 | * @data: pointer to the data of selected entry | |
544 | * Return: status code | |
545 | */ | |
546 | static efi_status_t eficonfig_file_selected(void *data) | |
547 | { | |
548 | u16 *tmp; | |
549 | struct eficonfig_file_entry_data *info = data; | |
550 | ||
551 | if (!info) | |
552 | return EFI_INVALID_PARAMETER; | |
553 | ||
c67d3c9e | 554 | if (!strcmp(info->file_name, "..\\")) { |
87d79142 MK |
555 | struct eficonfig_filepath_info *iter; |
556 | struct list_head *pos, *n; | |
557 | int is_last; | |
558 | char *filepath; | |
559 | tmp = info->file_info->current_path; | |
560 | ||
561 | memset(info->file_info->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE); | |
562 | filepath = calloc(1, EFICONFIG_FILE_PATH_MAX); | |
563 | if (!filepath) | |
564 | return EFI_OUT_OF_RESOURCES; | |
565 | ||
566 | list_for_each_safe(pos, n, &info->file_info->filepath_list) { | |
567 | iter = list_entry(pos, struct eficonfig_filepath_info, list); | |
568 | ||
569 | is_last = list_is_last(&iter->list, &info->file_info->filepath_list); | |
570 | if (is_last) { | |
571 | list_del(&iter->list); | |
572 | free(iter->name); | |
573 | free(iter); | |
574 | break; | |
575 | } | |
576 | strlcat(filepath, iter->name, EFICONFIG_FILE_PATH_MAX); | |
577 | } | |
578 | utf8_utf16_strcpy(&tmp, filepath); | |
579 | } else { | |
580 | size_t new_len; | |
581 | struct eficonfig_filepath_info *filepath_info; | |
582 | ||
583 | new_len = u16_strlen(info->file_info->current_path) + | |
584 | strlen(info->file_name); | |
585 | if (new_len >= EFICONFIG_FILE_PATH_MAX) { | |
586 | eficonfig_print_msg("File path is too long!"); | |
587 | return EFI_INVALID_PARAMETER; | |
588 | } | |
589 | tmp = &info->file_info->current_path[u16_strlen(info->file_info->current_path)]; | |
590 | utf8_utf16_strcpy(&tmp, info->file_name); | |
591 | ||
592 | filepath_info = calloc(1, sizeof(struct eficonfig_filepath_info)); | |
593 | if (!filepath_info) | |
594 | return EFI_OUT_OF_RESOURCES; | |
595 | ||
596 | filepath_info->name = strdup(info->file_name); | |
597 | if (!filepath_info->name) { | |
598 | free(filepath_info); | |
599 | return EFI_OUT_OF_RESOURCES; | |
600 | } | |
601 | list_add_tail(&filepath_info->list, &info->file_info->filepath_list); | |
602 | ||
603 | if (!info->is_directory) | |
604 | info->file_info->file_selected = true; | |
605 | } | |
606 | ||
607 | return EFI_SUCCESS; | |
608 | } | |
609 | ||
610 | /** | |
611 | * eficonfig_select_volume() - construct the volume selection menu | |
612 | * | |
613 | * @file_info: pointer to the file selection structure | |
614 | * Return: status code | |
615 | */ | |
616 | static efi_status_t eficonfig_select_volume(struct eficonfig_select_file_info *file_info) | |
617 | { | |
618 | u32 i; | |
619 | efi_status_t ret; | |
620 | efi_uintn_t count; | |
621 | struct efimenu *efi_menu; | |
622 | struct list_head *pos, *n; | |
623 | struct efi_handler *handler; | |
624 | struct eficonfig_entry *entry; | |
625 | struct efi_device_path *device_path; | |
626 | efi_handle_t *volume_handles = NULL; | |
627 | struct efi_simple_file_system_protocol *v; | |
628 | ||
629 | ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid, | |
630 | NULL, &count, (efi_handle_t **)&volume_handles); | |
631 | if (ret != EFI_SUCCESS) { | |
632 | eficonfig_print_msg("No block device found!"); | |
633 | return ret; | |
634 | } | |
635 | ||
636 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
637 | if (!efi_menu) | |
638 | return EFI_OUT_OF_RESOURCES; | |
639 | ||
640 | INIT_LIST_HEAD(&efi_menu->list); | |
641 | for (i = 0; i < count; i++) { | |
642 | char *devname; | |
643 | struct efi_block_io *block_io; | |
644 | struct eficonfig_volume_entry_data *info; | |
645 | ||
646 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1) | |
647 | break; | |
648 | ||
649 | ret = efi_search_protocol(volume_handles[i], | |
650 | &efi_simple_file_system_protocol_guid, &handler); | |
651 | if (ret != EFI_SUCCESS) | |
652 | continue; | |
653 | ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL, | |
654 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
655 | if (ret != EFI_SUCCESS) | |
656 | continue; | |
657 | ||
658 | ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler); | |
659 | if (ret != EFI_SUCCESS) | |
660 | continue; | |
661 | ret = efi_protocol_open(handler, (void **)&device_path, | |
662 | efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
663 | if (ret != EFI_SUCCESS) | |
664 | continue; | |
665 | ||
666 | ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler); | |
667 | if (ret != EFI_SUCCESS) | |
668 | continue; | |
669 | ret = efi_protocol_open(handler, (void **)&block_io, | |
670 | efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
671 | if (ret != EFI_SUCCESS) | |
672 | continue; | |
673 | ||
674 | info = calloc(1, sizeof(struct eficonfig_volume_entry_data)); | |
675 | if (!info) { | |
676 | ret = EFI_OUT_OF_RESOURCES; | |
677 | goto out; | |
678 | } | |
679 | ||
680 | devname = calloc(1, BOOTMENU_DEVICE_NAME_MAX); | |
681 | if (!devname) { | |
682 | free(info); | |
683 | ret = EFI_OUT_OF_RESOURCES; | |
684 | goto out; | |
685 | } | |
686 | ret = efi_disk_get_device_name(volume_handles[i], devname, | |
687 | BOOTMENU_DEVICE_NAME_MAX); | |
688 | if (ret != EFI_SUCCESS) { | |
689 | free(info); | |
690 | goto out; | |
691 | } | |
692 | ||
693 | info->v = v; | |
694 | info->dp = device_path; | |
695 | info->file_info = file_info; | |
8961e93e MK |
696 | ret = eficonfig_append_menu_entry(efi_menu, devname, eficonfig_volume_selected, |
697 | info); | |
87d79142 MK |
698 | if (ret != EFI_SUCCESS) { |
699 | free(info); | |
700 | goto out; | |
701 | } | |
702 | } | |
703 | ||
8961e93e | 704 | ret = eficonfig_append_quit_entry(efi_menu); |
87d79142 MK |
705 | if (ret != EFI_SUCCESS) |
706 | goto out; | |
707 | ||
cd160b27 MK |
708 | ret = eficonfig_process_common(efi_menu, " ** Select Volume **", |
709 | eficonfig_menu_desc, | |
710 | eficonfig_display_statusline, | |
711 | eficonfig_print_entry, | |
712 | eficonfig_choice_entry); | |
713 | ||
87d79142 MK |
714 | out: |
715 | efi_free_pool(volume_handles); | |
716 | list_for_each_safe(pos, n, &efi_menu->list) { | |
717 | entry = list_entry(pos, struct eficonfig_entry, list); | |
718 | free(entry->data); | |
719 | } | |
720 | eficonfig_destroy(efi_menu); | |
721 | ||
722 | return ret; | |
723 | } | |
724 | ||
725 | /** | |
726 | * sort_file() - sort the file name in ascii order | |
727 | * | |
728 | * @data1: pointer to the file entry data | |
729 | * @data2: pointer to the file entry data | |
730 | * Return: -1 if the data1 file name is less than data2 file name, | |
731 | * 0 if both file name match, | |
732 | * 1 if the data1 file name is greater thant data2 file name. | |
733 | */ | |
734 | static int sort_file(const void *arg1, const void *arg2) | |
735 | { | |
736 | const struct eficonfig_file_entry_data *data1, *data2; | |
737 | ||
738 | data1 = *((const struct eficonfig_file_entry_data **)arg1); | |
739 | data2 = *((const struct eficonfig_file_entry_data **)arg2); | |
740 | ||
741 | return strcasecmp(data1->file_name, data2->file_name); | |
742 | } | |
743 | ||
744 | /** | |
745 | * eficonfig_create_file_entry() - construct the file menu entry | |
746 | * | |
747 | * @efi_menu: pointer to the efimenu structure | |
748 | * @count: number of the directory and file | |
749 | * @tmp_infos: pointer to the entry data array | |
750 | * @f: pointer to the file handle | |
751 | * @buf: pointer to the buffer to store the directory information | |
752 | * @file_info: pointer to the file selection structure | |
753 | * Return: status code | |
754 | */ | |
755 | static efi_status_t | |
756 | eficonfig_create_file_entry(struct efimenu *efi_menu, u32 count, | |
757 | struct eficonfig_file_entry_data **tmp_infos, | |
758 | struct efi_file_handle *f, struct efi_file_info *buf, | |
759 | struct eficonfig_select_file_info *file_info) | |
760 | { | |
761 | char *name, *p; | |
762 | efi_uintn_t len; | |
763 | efi_status_t ret; | |
764 | u32 i, entry_num = 0; | |
765 | struct eficonfig_file_entry_data *info; | |
766 | ||
21faf4ef | 767 | EFI_CALL(f->setpos(f, 0)); |
87d79142 MK |
768 | /* Read directory and construct menu structure */ |
769 | for (i = 0; i < count; i++) { | |
770 | if (entry_num >= EFICONFIG_ENTRY_NUM_MAX - 1) | |
771 | break; | |
772 | ||
773 | len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE; | |
21faf4ef | 774 | ret = EFI_CALL(f->read(f, &len, buf)); |
87d79142 MK |
775 | if (ret != EFI_SUCCESS || len == 0) |
776 | break; | |
777 | ||
778 | info = calloc(1, sizeof(struct eficonfig_file_entry_data)); | |
779 | if (!info) { | |
780 | ret = EFI_OUT_OF_RESOURCES; | |
781 | goto out; | |
782 | } | |
783 | ||
784 | /* append '\\' at the end of directory name */ | |
785 | name = calloc(1, utf16_utf8_strlen(buf->file_name) + 2); | |
786 | if (!name) { | |
787 | ret = EFI_OUT_OF_RESOURCES; | |
788 | free(info); | |
789 | goto out; | |
790 | } | |
791 | p = name; | |
792 | utf16_utf8_strcpy(&p, buf->file_name); | |
793 | if (buf->attribute & EFI_FILE_DIRECTORY) { | |
794 | /* filter out u'.' */ | |
795 | if (!u16_strcmp(buf->file_name, u".")) { | |
796 | free(info); | |
797 | free(name); | |
798 | continue; | |
799 | } | |
800 | name[u16_strlen(buf->file_name)] = '\\'; | |
801 | info->is_directory = true; | |
802 | } | |
803 | ||
804 | info->file_name = name; | |
805 | info->file_info = file_info; | |
806 | tmp_infos[entry_num++] = info; | |
807 | } | |
808 | ||
809 | qsort(tmp_infos, entry_num, sizeof(*tmp_infos), | |
810 | (int (*)(const void *, const void *))sort_file); | |
811 | ||
812 | for (i = 0; i < entry_num; i++) { | |
8961e93e MK |
813 | ret = eficonfig_append_menu_entry(efi_menu, tmp_infos[i]->file_name, |
814 | eficonfig_file_selected, tmp_infos[i]); | |
87d79142 MK |
815 | if (ret != EFI_SUCCESS) |
816 | goto out; | |
817 | } | |
818 | ||
819 | out: | |
820 | return ret; | |
821 | } | |
822 | ||
823 | /** | |
a84040ab | 824 | * eficonfig_show_file_selection() - construct the file selection menu |
87d79142 MK |
825 | * |
826 | * @file_info: pointer to the file selection structure | |
827 | * @root: pointer to the file handle | |
828 | * Return: status code | |
829 | */ | |
a84040ab MK |
830 | static efi_status_t eficonfig_show_file_selection(struct eficonfig_select_file_info *file_info, |
831 | struct efi_file_handle *root) | |
87d79142 MK |
832 | { |
833 | u32 count = 0, i; | |
834 | efi_uintn_t len; | |
835 | efi_status_t ret; | |
836 | struct efimenu *efi_menu; | |
837 | struct efi_file_handle *f; | |
838 | struct efi_file_info *buf; | |
839 | struct eficonfig_file_entry_data **tmp_infos; | |
840 | ||
841 | buf = calloc(1, sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE); | |
842 | if (!buf) | |
843 | return EFI_OUT_OF_RESOURCES; | |
844 | ||
845 | while (!file_info->file_selected) { | |
846 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
847 | if (!efi_menu) { | |
848 | ret = EFI_OUT_OF_RESOURCES; | |
849 | goto out; | |
850 | } | |
851 | INIT_LIST_HEAD(&efi_menu->list); | |
852 | ||
21faf4ef MK |
853 | ret = EFI_CALL(root->open(root, &f, file_info->current_path, |
854 | EFI_FILE_MODE_READ, 0)); | |
87d79142 MK |
855 | if (ret != EFI_SUCCESS) { |
856 | eficonfig_print_msg("Reading volume failed!"); | |
857 | free(efi_menu); | |
858 | ret = EFI_ABORTED; | |
859 | goto out; | |
860 | } | |
861 | ||
862 | /* Count the number of directory entries */ | |
863 | for (;;) { | |
864 | len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE; | |
21faf4ef | 865 | ret = EFI_CALL(f->read(f, &len, buf)); |
87d79142 MK |
866 | if (ret != EFI_SUCCESS || len == 0) |
867 | break; | |
868 | ||
869 | count++; | |
870 | } | |
871 | ||
872 | /* allocate array to sort the entry */ | |
873 | tmp_infos = calloc(count, sizeof(*tmp_infos)); | |
874 | if (!tmp_infos) { | |
875 | ret = EFI_OUT_OF_RESOURCES; | |
876 | goto err; | |
877 | } | |
878 | ||
879 | ret = eficonfig_create_file_entry(efi_menu, count, tmp_infos, | |
880 | f, buf, file_info); | |
881 | if (ret != EFI_SUCCESS) | |
882 | goto err; | |
883 | ||
8961e93e | 884 | ret = eficonfig_append_quit_entry(efi_menu); |
87d79142 MK |
885 | if (ret != EFI_SUCCESS) |
886 | goto err; | |
887 | ||
cd160b27 MK |
888 | ret = eficonfig_process_common(efi_menu, " ** Select File **", |
889 | eficonfig_menu_desc, | |
890 | eficonfig_display_statusline, | |
891 | eficonfig_print_entry, | |
892 | eficonfig_choice_entry); | |
87d79142 | 893 | err: |
21faf4ef | 894 | EFI_CALL(f->close(f)); |
87d79142 MK |
895 | eficonfig_destroy(efi_menu); |
896 | ||
897 | if (tmp_infos) { | |
898 | for (i = 0; i < count; i++) | |
899 | free(tmp_infos[i]); | |
900 | } | |
901 | ||
902 | free(tmp_infos); | |
903 | ||
904 | if (ret != EFI_SUCCESS) | |
905 | break; | |
906 | } | |
907 | ||
908 | out: | |
909 | free(buf); | |
910 | ||
911 | return ret; | |
912 | } | |
913 | ||
914 | /** | |
915 | * handle_user_input() - handle user input | |
916 | * | |
917 | * @buf: pointer to the buffer | |
918 | * @buf_size: size of the buffer | |
919 | * @cursor_col: cursor column for user input | |
920 | * @msg: pointer to the string to display | |
921 | * Return: status code | |
922 | */ | |
923 | static efi_status_t handle_user_input(u16 *buf, int buf_size, | |
924 | int cursor_col, char *msg) | |
925 | { | |
926 | u16 *tmp; | |
927 | efi_status_t ret; | |
928 | ||
929 | printf(ANSI_CLEAR_CONSOLE | |
930 | ANSI_CURSOR_POSITION | |
931 | "%s" | |
932 | ANSI_CURSOR_POSITION | |
45f5319f | 933 | " Press ENTER to complete, ESC to quit", |
87d79142 MK |
934 | 0, 1, msg, 8, 1); |
935 | ||
936 | /* tmp is used to accept user cancel */ | |
937 | tmp = calloc(1, buf_size * sizeof(u16)); | |
938 | if (!tmp) | |
939 | return EFI_OUT_OF_RESOURCES; | |
940 | ||
941 | ret = efi_console_get_u16_string(cin, tmp, buf_size, NULL, 4, cursor_col); | |
942 | if (ret == EFI_SUCCESS) | |
943 | u16_strcpy(buf, tmp); | |
944 | ||
945 | free(tmp); | |
946 | ||
947 | /* to stay the parent menu */ | |
948 | ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; | |
949 | ||
950 | return ret; | |
951 | } | |
952 | ||
953 | /** | |
954 | * eficonfig_boot_add_enter_description() - handle user input for description | |
955 | * | |
956 | * @data: pointer to the internal boot option structure | |
957 | * Return: status code | |
958 | */ | |
959 | static efi_status_t eficonfig_boot_add_enter_description(void *data) | |
960 | { | |
961 | struct eficonfig_boot_option *bo = data; | |
962 | ||
963 | return handle_user_input(bo->description, EFICONFIG_DESCRIPTION_MAX, 22, | |
964 | "\n ** Edit Description **\n" | |
965 | "\n" | |
966 | " enter description: "); | |
967 | } | |
968 | ||
969 | /** | |
970 | * eficonfig_boot_add_optional_data() - handle user input for optional data | |
971 | * | |
972 | * @data: pointer to the internal boot option structure | |
973 | * Return: status code | |
974 | */ | |
975 | static efi_status_t eficonfig_boot_add_optional_data(void *data) | |
976 | { | |
977 | struct eficonfig_boot_option *bo = data; | |
978 | ||
979 | return handle_user_input(bo->optional_data, EFICONFIG_OPTIONAL_DATA_MAX, 24, | |
980 | "\n ** Edit Optional Data **\n" | |
981 | "\n" | |
982 | " enter optional data:"); | |
983 | } | |
984 | ||
985 | /** | |
986 | * eficonfig_boot_edit_save() - handler to save the boot option | |
987 | * | |
988 | * @data: pointer to the internal boot option structure | |
989 | * Return: status code | |
990 | */ | |
991 | static efi_status_t eficonfig_boot_edit_save(void *data) | |
992 | { | |
993 | struct eficonfig_boot_option *bo = data; | |
994 | ||
995 | if (u16_strlen(bo->description) == 0) { | |
996 | eficonfig_print_msg("Boot Description is empty!"); | |
997 | bo->edit_completed = false; | |
998 | return EFI_NOT_READY; | |
999 | } | |
1000 | if (u16_strlen(bo->file_info.current_path) == 0) { | |
1001 | eficonfig_print_msg("File is not selected!"); | |
1002 | bo->edit_completed = false; | |
1003 | return EFI_NOT_READY; | |
1004 | } | |
1005 | ||
1006 | bo->edit_completed = true; | |
1007 | ||
1008 | return EFI_SUCCESS; | |
1009 | } | |
1010 | ||
87d79142 MK |
1011 | /** |
1012 | * eficonfig_process_clear_file_selection() - callback function for "Clear" entry | |
1013 | * | |
1014 | * @data: pointer to the data | |
1015 | * Return: status code | |
1016 | */ | |
1017 | efi_status_t eficonfig_process_clear_file_selection(void *data) | |
1018 | { | |
1019 | struct eficonfig_select_file_info *file_info = data; | |
1020 | ||
1021 | /* clear the existing file information */ | |
1022 | file_info->current_volume = NULL; | |
1023 | file_info->current_path[0] = u'\0'; | |
1024 | file_info->dp_volume = NULL; | |
1025 | ||
1026 | return EFI_ABORTED; | |
1027 | } | |
1028 | ||
1029 | static struct eficonfig_item select_file_menu_items[] = { | |
1030 | {"Select File", eficonfig_process_select_file}, | |
1031 | {"Clear", eficonfig_process_clear_file_selection}, | |
1032 | {"Quit", eficonfig_process_quit}, | |
1033 | }; | |
1034 | ||
87d79142 | 1035 | /** |
a84040ab | 1036 | * eficonfig_process_show_file_option() - display select file option |
87d79142 MK |
1037 | * |
1038 | * @file_info: pointer to the file information structure | |
1039 | * Return: status code | |
1040 | */ | |
a84040ab | 1041 | efi_status_t eficonfig_process_show_file_option(void *data) |
87d79142 MK |
1042 | { |
1043 | efi_status_t ret; | |
1044 | struct efimenu *efi_menu; | |
1045 | ||
a84040ab MK |
1046 | select_file_menu_items[0].data = data; |
1047 | select_file_menu_items[1].data = data; | |
87d79142 MK |
1048 | efi_menu = eficonfig_create_fixed_menu(select_file_menu_items, |
1049 | ARRAY_SIZE(select_file_menu_items)); | |
1050 | if (!efi_menu) | |
1051 | return EFI_OUT_OF_RESOURCES; | |
1052 | ||
cd160b27 MK |
1053 | ret = eficonfig_process_common(efi_menu, " ** Update File **", |
1054 | eficonfig_menu_desc, | |
1055 | eficonfig_display_statusline, | |
1056 | eficonfig_print_entry, | |
1057 | eficonfig_choice_entry); | |
87d79142 MK |
1058 | if (ret != EFI_SUCCESS) /* User selects "Clear" or "Quit" */ |
1059 | ret = EFI_NOT_READY; | |
1060 | ||
1061 | eficonfig_destroy(efi_menu); | |
1062 | ||
1063 | return ret; | |
1064 | } | |
1065 | ||
1066 | /** | |
a84040ab | 1067 | * eficonfig_process_select_file() - handle user file selection |
87d79142 MK |
1068 | * |
1069 | * @data: pointer to the data | |
1070 | * Return: status code | |
1071 | */ | |
a84040ab | 1072 | efi_status_t eficonfig_process_select_file(void *data) |
87d79142 MK |
1073 | { |
1074 | size_t len; | |
1075 | efi_status_t ret; | |
1076 | struct list_head *pos, *n; | |
1077 | struct efi_file_handle *root; | |
1078 | struct eficonfig_filepath_info *item; | |
1079 | struct eficonfig_select_file_info *tmp = NULL; | |
1080 | struct eficonfig_select_file_info *file_info = data; | |
1081 | ||
87d79142 MK |
1082 | tmp = calloc(1, sizeof(struct eficonfig_select_file_info)); |
1083 | if (!tmp) | |
1084 | return EFI_OUT_OF_RESOURCES; | |
1085 | ||
1086 | tmp->current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE); | |
1087 | if (!tmp->current_path) { | |
1088 | free(tmp); | |
1089 | return EFI_OUT_OF_RESOURCES; | |
1090 | } | |
1091 | INIT_LIST_HEAD(&tmp->filepath_list); | |
1092 | ||
1093 | while (!tmp->file_selected) { | |
1094 | tmp->current_volume = NULL; | |
1095 | memset(tmp->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE); | |
1096 | ||
1097 | ret = eficonfig_select_volume(tmp); | |
1098 | if (ret != EFI_SUCCESS) | |
1099 | goto out; | |
1100 | ||
1101 | if (!tmp->current_volume) | |
1102 | return EFI_INVALID_PARAMETER; | |
1103 | ||
21faf4ef | 1104 | ret = EFI_CALL(tmp->current_volume->open_volume(tmp->current_volume, &root)); |
87d79142 MK |
1105 | if (ret != EFI_SUCCESS) |
1106 | goto out; | |
1107 | ||
a84040ab | 1108 | ret = eficonfig_show_file_selection(tmp, root); |
87d79142 MK |
1109 | if (ret == EFI_ABORTED) |
1110 | continue; | |
1111 | if (ret != EFI_SUCCESS) | |
1112 | goto out; | |
1113 | } | |
1114 | ||
1115 | out: | |
1116 | if (ret == EFI_SUCCESS) { | |
1117 | len = u16_strlen(tmp->current_path); | |
1118 | len = (len >= EFICONFIG_FILE_PATH_MAX) ? (EFICONFIG_FILE_PATH_MAX - 1) : len; | |
1119 | memcpy(file_info->current_path, tmp->current_path, len * sizeof(u16)); | |
1120 | file_info->current_path[len] = u'\0'; | |
1121 | file_info->current_volume = tmp->current_volume; | |
1122 | file_info->dp_volume = tmp->dp_volume; | |
1123 | } | |
1124 | ||
1125 | list_for_each_safe(pos, n, &tmp->filepath_list) { | |
1126 | item = list_entry(pos, struct eficonfig_filepath_info, list); | |
1127 | list_del(&item->list); | |
1128 | free(item->name); | |
1129 | free(item); | |
1130 | } | |
1131 | free(tmp->current_path); | |
1132 | free(tmp); | |
1133 | ||
1134 | /* to stay the parent menu */ | |
1135 | ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; | |
1136 | ||
1137 | return ret; | |
1138 | } | |
1139 | ||
87d79142 MK |
1140 | /** |
1141 | * eficonfig_set_boot_option() - set boot option | |
1142 | * | |
1143 | * @varname: pointer to variable name | |
1144 | * @dp: pointer to device path | |
1145 | * @label: pointer to label string | |
1146 | * @optional_data: pointer to optional data | |
1147 | * Return: status code | |
1148 | */ | |
1149 | static efi_status_t eficonfig_set_boot_option(u16 *varname, struct efi_device_path *dp, | |
1150 | efi_uintn_t dp_size, u16 *label, char *optional_data) | |
1151 | { | |
1152 | void *p = NULL; | |
1153 | efi_status_t ret; | |
1154 | efi_uintn_t size; | |
1155 | struct efi_load_option lo; | |
1156 | ||
1157 | lo.file_path = dp; | |
1158 | lo.file_path_length = dp_size; | |
1159 | lo.attributes = LOAD_OPTION_ACTIVE; | |
1160 | lo.optional_data = optional_data; | |
1161 | lo.label = label; | |
1162 | ||
1163 | size = efi_serialize_load_option(&lo, (u8 **)&p); | |
1164 | if (!size) | |
1165 | return EFI_INVALID_PARAMETER; | |
1166 | ||
1167 | ret = efi_set_variable_int(varname, &efi_global_variable_guid, | |
1168 | EFI_VARIABLE_NON_VOLATILE | | |
1169 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
1170 | EFI_VARIABLE_RUNTIME_ACCESS, | |
1171 | size, p, false); | |
1172 | free(p); | |
1173 | ||
1174 | return ret; | |
1175 | } | |
1176 | ||
87d79142 MK |
1177 | /** |
1178 | * create_boot_option_entry() - create boot option entry | |
1179 | * | |
1180 | * @efi_menu: pointer to the efimenu structure | |
1181 | * @title: pointer to the entry title | |
1182 | * @val: pointer to boot option label | |
1183 | * @func: callback of each entry | |
1184 | * @data: pointer to the data to be passed to each entry callback | |
1185 | * Return: status code | |
1186 | */ | |
1187 | static efi_status_t create_boot_option_entry(struct efimenu *efi_menu, char *title, u16 *val, | |
1188 | eficonfig_entry_func func, void *data) | |
1189 | { | |
1190 | u32 len; | |
1191 | char *p, *buf; | |
1192 | ||
1193 | len = strlen(title) + 1; | |
1194 | if (val) | |
1195 | len += utf16_utf8_strlen(val); | |
1196 | buf = calloc(1, len); | |
1197 | if (!buf) | |
1198 | return EFI_OUT_OF_RESOURCES; | |
1199 | ||
1200 | strcpy(buf, title); | |
1201 | if (val) { | |
1202 | p = buf + strlen(title); | |
1203 | utf16_utf8_strcpy(&p, val); | |
1204 | } | |
1205 | ||
8961e93e | 1206 | return eficonfig_append_menu_entry(efi_menu, buf, func, data); |
87d79142 MK |
1207 | } |
1208 | ||
1209 | /** | |
1210 | * prepare_file_selection_entry() - prepare file selection entry | |
1211 | * | |
1212 | * @efi_menu: pointer to the efimenu structure | |
1213 | * @title: pointer to the title string | |
1214 | * @file_info: pointer to the file info | |
1215 | * Return: status code | |
1216 | */ | |
1217 | static efi_status_t prepare_file_selection_entry(struct efimenu *efi_menu, char *title, | |
1218 | struct eficonfig_select_file_info *file_info) | |
1219 | { | |
1220 | u32 len; | |
1221 | efi_status_t ret; | |
e34158bc | 1222 | u16 *file_name = NULL, *p; |
87d79142 | 1223 | efi_handle_t handle; |
e34158bc MK |
1224 | char *devname; |
1225 | ||
1226 | devname = calloc(1, EFICONFIG_VOLUME_PATH_MAX + 1); | |
1227 | if (!devname) | |
1228 | return EFI_OUT_OF_RESOURCES; | |
87d79142 MK |
1229 | |
1230 | /* get the device name only when the user already selected the file path */ | |
1231 | handle = efi_dp_find_obj(file_info->dp_volume, NULL, NULL); | |
1232 | if (handle) { | |
e34158bc | 1233 | ret = efi_disk_get_device_name(handle, devname, EFICONFIG_VOLUME_PATH_MAX); |
87d79142 | 1234 | if (ret != EFI_SUCCESS) |
e34158bc MK |
1235 | goto out; |
1236 | } | |
1237 | ||
1238 | /* | |
1239 | * If the preconfigured volume does not exist in the system, display the text | |
1240 | * converted volume device path instead of U-Boot friendly name(e.g. "usb 0:1"). | |
1241 | */ | |
1242 | if (!handle && file_info->dp_volume) { | |
1243 | u16 *dp_str; | |
1244 | char *q = devname; | |
1245 | ||
1246 | dp_str = efi_dp_str(file_info->dp_volume); | |
1247 | if (dp_str) | |
1248 | utf16_utf8_strncpy(&q, dp_str, EFICONFIG_VOLUME_PATH_MAX); | |
1249 | ||
1250 | efi_free_pool(dp_str); | |
87d79142 MK |
1251 | } |
1252 | ||
1253 | /* append u'/' to devname, it is just for display purpose. */ | |
1254 | if (file_info->current_path[0] != u'\0' && file_info->current_path[0] != u'/') | |
e34158bc | 1255 | strlcat(devname, "/", EFICONFIG_VOLUME_PATH_MAX + 1); |
87d79142 MK |
1256 | |
1257 | len = strlen(devname); | |
1258 | len += utf16_utf8_strlen(file_info->current_path) + 1; | |
1259 | file_name = calloc(1, len * sizeof(u16)); | |
e34158bc MK |
1260 | if (!file_name) { |
1261 | ret = EFI_OUT_OF_RESOURCES; | |
1262 | goto out; | |
1263 | } | |
87d79142 MK |
1264 | |
1265 | p = file_name; | |
1266 | utf8_utf16_strcpy(&p, devname); | |
1267 | u16_strlcat(file_name, file_info->current_path, len); | |
1268 | ret = create_boot_option_entry(efi_menu, title, file_name, | |
a84040ab | 1269 | eficonfig_process_show_file_option, file_info); |
e34158bc MK |
1270 | out: |
1271 | free(devname); | |
87d79142 | 1272 | free(file_name); |
e34158bc | 1273 | |
87d79142 MK |
1274 | return ret; |
1275 | } | |
1276 | ||
1277 | /** | |
1278 | * eficonfig_show_boot_option() - prepare menu entry for editing boot option | |
1279 | * | |
1280 | * Construct the structures to create edit boot option menu | |
1281 | * | |
1282 | * @bo: pointer to the boot option | |
1283 | * @header_str: pointer to the header string | |
1284 | * Return: status code | |
1285 | */ | |
1286 | static efi_status_t eficonfig_show_boot_option(struct eficonfig_boot_option *bo, | |
1287 | char *header_str) | |
1288 | { | |
1289 | efi_status_t ret; | |
1290 | struct efimenu *efi_menu; | |
1291 | ||
1292 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
1293 | if (!efi_menu) | |
1294 | return EFI_OUT_OF_RESOURCES; | |
1295 | ||
1296 | INIT_LIST_HEAD(&efi_menu->list); | |
1297 | ||
1298 | ret = create_boot_option_entry(efi_menu, "Description: ", bo->description, | |
1299 | eficonfig_boot_add_enter_description, bo); | |
1300 | if (ret != EFI_SUCCESS) | |
1301 | goto out; | |
1302 | ||
1303 | ret = prepare_file_selection_entry(efi_menu, "File: ", &bo->file_info); | |
1304 | if (ret != EFI_SUCCESS) | |
1305 | goto out; | |
1306 | ||
1307 | ret = prepare_file_selection_entry(efi_menu, "Initrd File: ", &bo->initrd_info); | |
1308 | if (ret != EFI_SUCCESS) | |
1309 | goto out; | |
1310 | ||
1311 | ret = create_boot_option_entry(efi_menu, "Optional Data: ", bo->optional_data, | |
1312 | eficonfig_boot_add_optional_data, bo); | |
1313 | if (ret != EFI_SUCCESS) | |
1314 | goto out; | |
1315 | ||
1316 | ret = create_boot_option_entry(efi_menu, "Save", NULL, | |
1317 | eficonfig_boot_edit_save, bo); | |
1318 | if (ret != EFI_SUCCESS) | |
1319 | goto out; | |
1320 | ||
1321 | ret = create_boot_option_entry(efi_menu, "Quit", NULL, | |
1322 | eficonfig_process_quit, NULL); | |
1323 | if (ret != EFI_SUCCESS) | |
1324 | goto out; | |
1325 | ||
cd160b27 MK |
1326 | ret = eficonfig_process_common(efi_menu, header_str, |
1327 | eficonfig_menu_desc, | |
1328 | eficonfig_display_statusline, | |
1329 | eficonfig_print_entry, | |
1330 | eficonfig_choice_entry); | |
1331 | ||
87d79142 MK |
1332 | out: |
1333 | eficonfig_destroy(efi_menu); | |
1334 | ||
1335 | return ret; | |
1336 | } | |
1337 | ||
1338 | /** | |
1339 | * fill_file_info() - fill the file info from efi_device_path structure | |
1340 | * | |
1341 | * @dp: pointer to the device path | |
1342 | * @file_info: pointer to the file info structure | |
1343 | * @device_dp: pointer to the volume device path | |
1344 | */ | |
1345 | static void fill_file_info(struct efi_device_path *dp, | |
1346 | struct eficonfig_select_file_info *file_info, | |
1347 | struct efi_device_path *device_dp) | |
1348 | { | |
1349 | u16 *file_str, *p; | |
1350 | struct efi_device_path *file_dp = NULL; | |
1351 | ||
1352 | efi_dp_split_file_path(dp, &device_dp, &file_dp); | |
1353 | file_info->dp_volume = device_dp; | |
1354 | ||
1355 | if (file_dp) { | |
1356 | file_str = efi_dp_str(file_dp); | |
1357 | /* | |
1358 | * efi_convert_device_path_to_text() automatically adds u'/' at the | |
1359 | * beginning of file name, remove u'/' before copying to current_path | |
1360 | */ | |
1361 | p = file_str; | |
1362 | if (p[0] == u'/') | |
1363 | p++; | |
1364 | ||
1365 | u16_strcpy(file_info->current_path, p); | |
1366 | efi_free_pool(file_dp); | |
1367 | efi_free_pool(file_str); | |
1368 | } | |
1369 | } | |
1370 | ||
1371 | /** | |
1372 | * eficonfig_edit_boot_option() - prepare boot option structure for editing | |
1373 | * | |
1374 | * Construct the boot option structure and copy the existing value | |
1375 | * | |
1376 | * @varname: pointer to the UEFI variable name | |
1377 | * @bo: pointer to the boot option | |
1378 | * @load_option: pointer to the load option | |
1379 | * @load_option_size: size of the load option | |
1380 | * @header_str: pointer to the header string | |
1381 | * Return : status code | |
1382 | */ | |
1383 | static efi_status_t eficonfig_edit_boot_option(u16 *varname, struct eficonfig_boot_option *bo, | |
1384 | void *load_option, efi_uintn_t load_option_size, | |
1385 | char *header_str) | |
1386 | { | |
1387 | size_t len; | |
1388 | efi_status_t ret; | |
1389 | char *tmp = NULL, *p; | |
1390 | struct efi_load_option lo = {0}; | |
1391 | efi_uintn_t final_dp_size; | |
1392 | struct efi_device_path *dp = NULL; | |
1393 | efi_uintn_t size = load_option_size; | |
1394 | struct efi_device_path *final_dp = NULL; | |
1395 | struct efi_device_path *device_dp = NULL; | |
1396 | struct efi_device_path *initrd_dp = NULL; | |
1397 | struct efi_device_path *initrd_device_dp = NULL; | |
1398 | ||
1399 | const struct efi_initrd_dp id_dp = { | |
1400 | .vendor = { | |
1401 | { | |
1402 | DEVICE_PATH_TYPE_MEDIA_DEVICE, | |
1403 | DEVICE_PATH_SUB_TYPE_VENDOR_PATH, | |
1404 | sizeof(id_dp.vendor), | |
1405 | }, | |
1406 | EFI_INITRD_MEDIA_GUID, | |
1407 | }, | |
1408 | .end = { | |
1409 | DEVICE_PATH_TYPE_END, | |
1410 | DEVICE_PATH_SUB_TYPE_END, | |
1411 | sizeof(id_dp.end), | |
1412 | } | |
1413 | }; | |
1414 | ||
1415 | bo->file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE); | |
1416 | if (!bo->file_info.current_path) { | |
1417 | ret = EFI_OUT_OF_RESOURCES; | |
1418 | goto out; | |
1419 | } | |
1420 | ||
1421 | bo->initrd_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE); | |
1422 | if (!bo->file_info.current_path) { | |
1423 | ret = EFI_OUT_OF_RESOURCES; | |
1424 | goto out; | |
1425 | } | |
1426 | ||
1427 | bo->description = calloc(1, EFICONFIG_DESCRIPTION_MAX * sizeof(u16)); | |
1428 | if (!bo->description) { | |
1429 | ret = EFI_OUT_OF_RESOURCES; | |
1430 | goto out; | |
1431 | } | |
1432 | ||
1433 | bo->optional_data = calloc(1, EFICONFIG_OPTIONAL_DATA_MAX * sizeof(u16)); | |
1434 | if (!bo->optional_data) { | |
1435 | ret = EFI_OUT_OF_RESOURCES; | |
1436 | goto out; | |
1437 | } | |
1438 | ||
1439 | /* copy the preset value */ | |
1440 | if (load_option) { | |
1441 | ret = efi_deserialize_load_option(&lo, load_option, &size); | |
1442 | if (ret != EFI_SUCCESS) | |
1443 | goto out; | |
1444 | ||
e34158bc | 1445 | if (!lo.label) { |
87d79142 MK |
1446 | ret = EFI_INVALID_PARAMETER; |
1447 | goto out; | |
1448 | } | |
e34158bc MK |
1449 | /* truncate the long label string */ |
1450 | if (u16_strlen(lo.label) >= EFICONFIG_DESCRIPTION_MAX) | |
1451 | lo.label[EFICONFIG_DESCRIPTION_MAX - 1] = u'\0'; | |
1452 | ||
87d79142 MK |
1453 | u16_strcpy(bo->description, lo.label); |
1454 | ||
1455 | /* EFI image file path is a first instance */ | |
1456 | if (lo.file_path) | |
1457 | fill_file_info(lo.file_path, &bo->file_info, device_dp); | |
1458 | ||
1459 | /* Initrd file path(optional) is placed at second instance. */ | |
1460 | initrd_dp = efi_dp_from_lo(&lo, &efi_lf2_initrd_guid); | |
1461 | if (initrd_dp) { | |
1462 | fill_file_info(initrd_dp, &bo->initrd_info, initrd_device_dp); | |
1463 | efi_free_pool(initrd_dp); | |
1464 | } | |
1465 | ||
1466 | if (size > 0) | |
1467 | memcpy(bo->optional_data, lo.optional_data, size); | |
1468 | } | |
1469 | ||
1470 | while (1) { | |
1471 | ret = eficonfig_show_boot_option(bo, header_str); | |
1472 | if (ret == EFI_SUCCESS && bo->edit_completed) | |
1473 | break; | |
1474 | if (ret == EFI_NOT_READY) | |
1475 | continue; | |
1476 | if (ret != EFI_SUCCESS) | |
1477 | goto out; | |
1478 | } | |
1479 | ||
1480 | if (bo->initrd_info.dp_volume) { | |
d6566113 MK |
1481 | dp = eficonfig_create_device_path(bo->initrd_info.dp_volume, |
1482 | bo->initrd_info.current_path); | |
87d79142 MK |
1483 | if (!dp) { |
1484 | ret = EFI_OUT_OF_RESOURCES; | |
1485 | goto out; | |
1486 | } | |
1487 | initrd_dp = efi_dp_append((const struct efi_device_path *)&id_dp, dp); | |
1488 | efi_free_pool(dp); | |
1489 | } | |
1490 | ||
d6566113 | 1491 | dp = eficonfig_create_device_path(bo->file_info.dp_volume, bo->file_info.current_path); |
87d79142 MK |
1492 | if (!dp) { |
1493 | ret = EFI_OUT_OF_RESOURCES; | |
1494 | goto out; | |
1495 | } | |
1496 | final_dp_size = efi_dp_size(dp) + sizeof(END); | |
1497 | if (initrd_dp) { | |
1498 | final_dp = efi_dp_concat(dp, initrd_dp); | |
1499 | final_dp_size += efi_dp_size(initrd_dp) + sizeof(END); | |
1500 | } else { | |
1501 | final_dp = efi_dp_dup(dp); | |
1502 | } | |
1503 | efi_free_pool(dp); | |
1504 | ||
1505 | if (!final_dp) | |
1506 | goto out; | |
1507 | ||
1508 | if (utf16_utf8_strlen(bo->optional_data)) { | |
1509 | len = utf16_utf8_strlen(bo->optional_data) + 1; | |
1510 | tmp = calloc(1, len); | |
1511 | if (!tmp) | |
1512 | goto out; | |
1513 | p = tmp; | |
1514 | utf16_utf8_strncpy(&p, bo->optional_data, u16_strlen(bo->optional_data)); | |
1515 | } | |
1516 | ||
1517 | ret = eficonfig_set_boot_option(varname, final_dp, final_dp_size, bo->description, tmp); | |
87d79142 MK |
1518 | out: |
1519 | free(tmp); | |
1520 | free(bo->optional_data); | |
1521 | free(bo->description); | |
1522 | free(bo->file_info.current_path); | |
1523 | free(bo->initrd_info.current_path); | |
1524 | efi_free_pool(device_dp); | |
1525 | efi_free_pool(initrd_device_dp); | |
1526 | efi_free_pool(initrd_dp); | |
1527 | efi_free_pool(final_dp); | |
1528 | ||
1529 | return ret; | |
1530 | } | |
1531 | ||
1532 | /** | |
1533 | * eficonfig_process_add_boot_option() - handler to add boot option | |
1534 | * | |
1535 | * @data: pointer to the data for each entry | |
1536 | * Return: status code | |
1537 | */ | |
1538 | static efi_status_t eficonfig_process_add_boot_option(void *data) | |
1539 | { | |
1540 | u16 varname[9]; | |
1541 | efi_status_t ret; | |
1542 | struct eficonfig_boot_option *bo = NULL; | |
1543 | ||
1544 | bo = calloc(1, sizeof(struct eficonfig_boot_option)); | |
1545 | if (!bo) | |
1546 | return EFI_OUT_OF_RESOURCES; | |
1547 | ||
339b527b | 1548 | ret = efi_bootmgr_get_unused_bootoption(varname, sizeof(varname), &bo->boot_index); |
87d79142 MK |
1549 | if (ret != EFI_SUCCESS) |
1550 | return ret; | |
1551 | ||
1552 | ret = eficonfig_edit_boot_option(varname, bo, NULL, 0, " ** Add Boot Option ** "); | |
1553 | if (ret != EFI_SUCCESS) | |
1554 | goto out; | |
1555 | ||
339b527b | 1556 | ret = efi_bootmgr_append_bootorder((u16)bo->boot_index); |
87d79142 MK |
1557 | if (ret != EFI_SUCCESS) |
1558 | goto out; | |
1559 | ||
1560 | out: | |
1561 | free(bo); | |
1562 | ||
1563 | /* to stay the parent menu */ | |
1564 | ret = (ret == EFI_ABORTED) ? EFI_SUCCESS : ret; | |
1565 | ||
1566 | return ret; | |
1567 | } | |
1568 | ||
e34158bc MK |
1569 | /** |
1570 | * eficonfig_process_boot_selected() - handler to select boot option entry | |
1571 | * | |
1572 | * @data: pointer to the data for each entry | |
1573 | * Return: status code | |
1574 | */ | |
1575 | static efi_status_t eficonfig_process_boot_selected(void *data) | |
1576 | { | |
1577 | struct eficonfig_boot_selection_data *info = data; | |
1578 | ||
1579 | if (info) | |
1580 | *info->selected = info->boot_index; | |
1581 | ||
1582 | return EFI_SUCCESS; | |
1583 | } | |
1584 | ||
e34158bc MK |
1585 | /** |
1586 | * eficonfig_add_boot_selection_entry() - add boot option menu entry | |
1587 | * | |
1588 | * @efi_menu: pointer to store the efimenu structure | |
1589 | * @boot_index: boot option index to be added | |
1590 | * @selected: pointer to store the selected boot option index | |
1591 | * Return: status code | |
1592 | */ | |
1593 | static efi_status_t eficonfig_add_boot_selection_entry(struct efimenu *efi_menu, | |
1594 | unsigned int boot_index, | |
1595 | unsigned int *selected) | |
1596 | { | |
1597 | char *buf, *p; | |
1598 | efi_status_t ret; | |
1599 | efi_uintn_t size; | |
1600 | void *load_option; | |
1601 | struct efi_load_option lo; | |
1602 | u16 varname[] = u"Boot####"; | |
1603 | struct eficonfig_boot_selection_data *info; | |
1604 | ||
1605 | efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index); | |
1606 | load_option = efi_get_var(varname, &efi_global_variable_guid, &size); | |
1607 | if (!load_option) | |
1608 | return EFI_SUCCESS; | |
1609 | ||
1610 | ret = efi_deserialize_load_option(&lo, load_option, &size); | |
1611 | if (ret != EFI_SUCCESS) { | |
1612 | log_warning("Invalid load option for %ls\n", varname); | |
1613 | free(load_option); | |
1614 | return ret; | |
1615 | } | |
1616 | ||
1617 | if (size >= sizeof(efi_guid_t) && | |
1618 | !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) { | |
1619 | /* | |
1620 | * auto generated entry has GUID in optional_data, | |
1621 | * skip auto generated entry because it will be generated | |
1622 | * again even if it is edited or deleted. | |
1623 | */ | |
1624 | free(load_option); | |
1625 | return EFI_SUCCESS; | |
1626 | } | |
1627 | ||
1628 | info = calloc(1, sizeof(struct eficonfig_boot_selection_data)); | |
1629 | if (!info) { | |
1630 | free(load_option); | |
1631 | return EFI_OUT_OF_RESOURCES; | |
1632 | } | |
1633 | ||
1634 | buf = calloc(1, utf16_utf8_strlen(lo.label) + 1); | |
1635 | if (!buf) { | |
1636 | free(load_option); | |
1637 | free(info); | |
1638 | return EFI_OUT_OF_RESOURCES; | |
1639 | } | |
1640 | p = buf; | |
1641 | utf16_utf8_strcpy(&p, lo.label); | |
1642 | info->boot_index = boot_index; | |
1643 | info->selected = selected; | |
8961e93e | 1644 | ret = eficonfig_append_menu_entry(efi_menu, buf, eficonfig_process_boot_selected, info); |
e34158bc MK |
1645 | if (ret != EFI_SUCCESS) { |
1646 | free(load_option); | |
1647 | free(info); | |
1648 | return ret; | |
1649 | } | |
1650 | free(load_option); | |
1651 | ||
1652 | return EFI_SUCCESS; | |
1653 | } | |
1654 | ||
1655 | /** | |
1656 | * eficonfig_show_boot_selection() - construct boot option menu entry | |
1657 | * | |
1658 | * @selected: pointer to store the selected boot option index | |
1659 | * Return: status code | |
1660 | */ | |
1661 | static efi_status_t eficonfig_show_boot_selection(unsigned int *selected) | |
1662 | { | |
1663 | u32 i; | |
1664 | u16 *bootorder; | |
1665 | efi_status_t ret; | |
ce327084 | 1666 | u16 *var_name16 = NULL; |
140a8959 | 1667 | efi_uintn_t num, size, buf_size; |
e34158bc MK |
1668 | struct efimenu *efi_menu; |
1669 | struct list_head *pos, *n; | |
1670 | struct eficonfig_entry *entry; | |
1671 | ||
1672 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
1673 | if (!efi_menu) | |
1674 | return EFI_OUT_OF_RESOURCES; | |
1675 | ||
1676 | bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size); | |
1677 | ||
1678 | INIT_LIST_HEAD(&efi_menu->list); | |
1679 | num = size / sizeof(u16); | |
1680 | /* list the load option in the order of BootOrder variable */ | |
1681 | for (i = 0; i < num; i++) { | |
1682 | ret = eficonfig_add_boot_selection_entry(efi_menu, bootorder[i], selected); | |
1683 | if (ret != EFI_SUCCESS) | |
1684 | goto out; | |
1685 | ||
1686 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1) | |
1687 | break; | |
1688 | } | |
1689 | ||
1690 | /* list the remaining load option not included in the BootOrder */ | |
140a8959 MK |
1691 | buf_size = 128; |
1692 | var_name16 = malloc(buf_size); | |
1693 | if (!var_name16) | |
1694 | return EFI_OUT_OF_RESOURCES; | |
e34158bc | 1695 | |
140a8959 MK |
1696 | var_name16[0] = 0; |
1697 | for (;;) { | |
1698 | int index; | |
1699 | efi_guid_t guid; | |
1700 | ||
ce327084 | 1701 | ret = efi_next_variable_name(&buf_size, &var_name16, &guid); |
140a8959 MK |
1702 | if (ret == EFI_NOT_FOUND) |
1703 | break; | |
ce327084 MK |
1704 | if (ret != EFI_SUCCESS) |
1705 | goto out; | |
1706 | ||
140a8959 MK |
1707 | if (efi_varname_is_load_option(var_name16, &index)) { |
1708 | /* If the index is included in the BootOrder, skip it */ | |
339b527b | 1709 | if (efi_search_bootorder(bootorder, num, index, NULL)) |
140a8959 MK |
1710 | continue; |
1711 | ||
1712 | ret = eficonfig_add_boot_selection_entry(efi_menu, index, selected); | |
1713 | if (ret != EFI_SUCCESS) | |
1714 | goto out; | |
1715 | } | |
e34158bc MK |
1716 | |
1717 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1) | |
1718 | break; | |
1719 | } | |
1720 | ||
8961e93e | 1721 | ret = eficonfig_append_quit_entry(efi_menu); |
e34158bc MK |
1722 | if (ret != EFI_SUCCESS) |
1723 | goto out; | |
1724 | ||
cd160b27 MK |
1725 | ret = eficonfig_process_common(efi_menu, " ** Select Boot Option **", |
1726 | eficonfig_menu_desc, | |
1727 | eficonfig_display_statusline, | |
1728 | eficonfig_print_entry, | |
1729 | eficonfig_choice_entry); | |
e34158bc MK |
1730 | out: |
1731 | list_for_each_safe(pos, n, &efi_menu->list) { | |
1732 | entry = list_entry(pos, struct eficonfig_entry, list); | |
1733 | free(entry->data); | |
1734 | } | |
1735 | eficonfig_destroy(efi_menu); | |
1736 | ||
140a8959 MK |
1737 | free(var_name16); |
1738 | ||
e34158bc MK |
1739 | return ret; |
1740 | } | |
1741 | ||
1742 | /** | |
1743 | * eficonfig_process_edit_boot_option() - handler to edit boot option | |
1744 | * | |
1745 | * @data: pointer to the data for each entry | |
1746 | * Return: status code | |
1747 | */ | |
1748 | static efi_status_t eficonfig_process_edit_boot_option(void *data) | |
1749 | { | |
1750 | efi_status_t ret; | |
1751 | efi_uintn_t size; | |
1752 | struct eficonfig_boot_option *bo = NULL; | |
1753 | ||
1754 | while (1) { | |
1755 | unsigned int selected; | |
1756 | void *load_option; | |
1757 | u16 varname[] = u"Boot####"; | |
1758 | ||
1759 | ret = eficonfig_show_boot_selection(&selected); | |
1760 | if (ret != EFI_SUCCESS) | |
1761 | break; | |
1762 | ||
1763 | bo = calloc(1, sizeof(struct eficonfig_boot_option)); | |
1764 | if (!bo) { | |
1765 | ret = EFI_OUT_OF_RESOURCES; | |
1766 | goto out; | |
1767 | } | |
1768 | ||
1769 | bo->boot_index = selected; | |
1770 | efi_create_indexed_name(varname, sizeof(varname), "Boot", selected); | |
1771 | load_option = efi_get_var(varname, &efi_global_variable_guid, &size); | |
1772 | if (!load_option) { | |
1773 | free(bo); | |
1774 | ret = EFI_NOT_FOUND; | |
1775 | goto out; | |
1776 | } | |
1777 | ||
1778 | ret = eficonfig_edit_boot_option(varname, bo, load_option, size, | |
1779 | " ** Edit Boot Option ** "); | |
1780 | ||
1781 | free(load_option); | |
1782 | free(bo); | |
1783 | if (ret != EFI_SUCCESS && ret != EFI_ABORTED) | |
1784 | break; | |
1785 | } | |
1786 | out: | |
1787 | /* to stay the parent menu */ | |
1788 | ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; | |
1789 | ||
1790 | return ret; | |
1791 | } | |
1792 | ||
e5948ee3 | 1793 | /** |
0d590852 | 1794 | * eficonfig_print_change_boot_order_entry() - print the boot option entry |
e5948ee3 | 1795 | * |
0d590852 | 1796 | * @data: pointer to the data associated with each menu entry |
e5948ee3 | 1797 | */ |
0d590852 | 1798 | static void eficonfig_print_change_boot_order_entry(void *data) |
e5948ee3 | 1799 | { |
0d590852 MK |
1800 | struct eficonfig_entry *entry = data; |
1801 | bool reverse = (entry->efi_menu->active == entry->num); | |
e5948ee3 | 1802 | |
8dbd0a0f MK |
1803 | if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num) |
1804 | return; | |
1805 | ||
1806 | printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE, | |
1807 | (entry->num - entry->efi_menu->start) + EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7); | |
e5948ee3 | 1808 | |
0d590852 MK |
1809 | if (reverse) |
1810 | puts(ANSI_COLOR_REVERSE); | |
e5948ee3 | 1811 | |
0d590852 MK |
1812 | if (entry->num < entry->efi_menu->count - 2) { |
1813 | if (((struct eficonfig_boot_order_data *)entry->data)->active) | |
1814 | printf("[*] "); | |
1815 | else | |
1816 | printf("[ ] "); | |
1817 | } | |
e5948ee3 | 1818 | |
0d590852 | 1819 | printf("%s", entry->title); |
e5948ee3 | 1820 | |
0d590852 MK |
1821 | if (reverse) |
1822 | puts(ANSI_COLOR_RESET); | |
e5948ee3 MK |
1823 | } |
1824 | ||
1825 | /** | |
0d590852 | 1826 | * eficonfig_choice_change_boot_order() - user key input handler |
e5948ee3 | 1827 | * |
0d590852 MK |
1828 | * @data: pointer to the menu entry |
1829 | * Return: key string to identify the selected entry | |
e5948ee3 | 1830 | */ |
0d590852 | 1831 | char *eficonfig_choice_change_boot_order(void *data) |
e5948ee3 | 1832 | { |
32bab0ea | 1833 | struct cli_ch_state s_cch, *cch = &s_cch; |
e5948ee3 | 1834 | struct list_head *pos, *n; |
0d590852 | 1835 | struct efimenu *efi_menu = data; |
2da4a15e | 1836 | enum bootmenu_key key = BKEY_NONE; |
d571f9b2 | 1837 | struct eficonfig_entry *entry, *tmp; |
e5948ee3 | 1838 | |
32bab0ea | 1839 | cli_ch_init(cch); |
e5948ee3 | 1840 | while (1) { |
32bab0ea | 1841 | key = bootmenu_loop(NULL, cch); |
e5948ee3 MK |
1842 | |
1843 | switch (key) { | |
2da4a15e | 1844 | case BKEY_PLUS: |
8dbd0a0f MK |
1845 | if (efi_menu->active > 0 && |
1846 | efi_menu->active < efi_menu->count - 2) { | |
e5948ee3 | 1847 | list_for_each_safe(pos, n, &efi_menu->list) { |
d571f9b2 | 1848 | entry = list_entry(pos, struct eficonfig_entry, list); |
e5948ee3 MK |
1849 | if (entry->num == efi_menu->active) |
1850 | break; | |
1851 | } | |
d571f9b2 | 1852 | tmp = list_entry(pos->prev, struct eficonfig_entry, list); |
e5948ee3 MK |
1853 | entry->num--; |
1854 | tmp->num++; | |
1855 | list_del(&tmp->list); | |
1856 | list_add(&tmp->list, &entry->list); | |
8dbd0a0f MK |
1857 | |
1858 | eficonfig_menu_up(efi_menu); | |
e5948ee3 | 1859 | } |
8dbd0a0f | 1860 | return NULL; |
2da4a15e | 1861 | case BKEY_UP: |
e5948ee3 | 1862 | if (efi_menu->active > 0) |
8dbd0a0f MK |
1863 | eficonfig_menu_up(efi_menu); |
1864 | ||
0d590852 | 1865 | return NULL; |
2da4a15e | 1866 | case BKEY_MINUS: |
e5948ee3 MK |
1867 | if (efi_menu->active < efi_menu->count - 3) { |
1868 | list_for_each_safe(pos, n, &efi_menu->list) { | |
d571f9b2 | 1869 | entry = list_entry(pos, struct eficonfig_entry, list); |
e5948ee3 MK |
1870 | if (entry->num == efi_menu->active) |
1871 | break; | |
1872 | } | |
d571f9b2 | 1873 | tmp = list_entry(pos->next, struct eficonfig_entry, list); |
e5948ee3 MK |
1874 | entry->num++; |
1875 | tmp->num--; | |
1876 | list_del(&entry->list); | |
1877 | list_add(&entry->list, &tmp->list); | |
1878 | ||
8dbd0a0f | 1879 | eficonfig_menu_down(efi_menu); |
e5948ee3 | 1880 | } |
0d590852 | 1881 | return NULL; |
2da4a15e | 1882 | case BKEY_DOWN: |
e5948ee3 | 1883 | if (efi_menu->active < efi_menu->count - 1) |
8dbd0a0f MK |
1884 | eficonfig_menu_down(efi_menu); |
1885 | ||
0d590852 | 1886 | return NULL; |
88df3634 MK |
1887 | case BKEY_SAVE: |
1888 | /* force to select "Save" entry */ | |
1889 | efi_menu->active = efi_menu->count - 2; | |
1890 | fallthrough; | |
2da4a15e | 1891 | case BKEY_SELECT: |
e5948ee3 | 1892 | /* "Save" */ |
0d590852 MK |
1893 | if (efi_menu->active == efi_menu->count - 2) { |
1894 | list_for_each_prev_safe(pos, n, &efi_menu->list) { | |
1895 | entry = list_entry(pos, struct eficonfig_entry, list); | |
1896 | if (entry->num == efi_menu->active) | |
1897 | break; | |
1898 | } | |
1899 | return entry->key; | |
1900 | } | |
e5948ee3 | 1901 | /* "Quit" */ |
0d590852 MK |
1902 | if (efi_menu->active == efi_menu->count - 1) { |
1903 | entry = list_last_entry(&efi_menu->list, | |
1904 | struct eficonfig_entry, | |
1905 | list); | |
1906 | return entry->key; | |
1907 | } | |
1908 | /* Pressed key is not valid, wait next key press */ | |
e5948ee3 | 1909 | break; |
2da4a15e | 1910 | case BKEY_SPACE: |
e5948ee3 MK |
1911 | if (efi_menu->active < efi_menu->count - 2) { |
1912 | list_for_each_safe(pos, n, &efi_menu->list) { | |
d571f9b2 | 1913 | entry = list_entry(pos, struct eficonfig_entry, list); |
e5948ee3 | 1914 | if (entry->num == efi_menu->active) { |
d571f9b2 MK |
1915 | struct eficonfig_boot_order_data *data = entry->data; |
1916 | ||
1917 | data->active = !data->active; | |
0d590852 | 1918 | return NULL; |
e5948ee3 MK |
1919 | } |
1920 | } | |
1921 | } | |
0d590852 | 1922 | /* Pressed key is not valid, wait next key press */ |
e5948ee3 | 1923 | break; |
2da4a15e | 1924 | case BKEY_QUIT: |
0d590852 MK |
1925 | entry = list_last_entry(&efi_menu->list, |
1926 | struct eficonfig_entry, list); | |
1927 | return entry->key; | |
e5948ee3 | 1928 | default: |
0d590852 | 1929 | /* Pressed key is not valid, wait next key press */ |
e5948ee3 MK |
1930 | break; |
1931 | } | |
1932 | } | |
1933 | } | |
1934 | ||
0d590852 MK |
1935 | /** |
1936 | * eficonfig_process_save_boot_order() - callback function for "Save" entry | |
1937 | * | |
1938 | * @data: pointer to the data | |
1939 | * Return: status code | |
1940 | */ | |
1941 | static efi_status_t eficonfig_process_save_boot_order(void *data) | |
1942 | { | |
1943 | u32 count = 0; | |
1944 | efi_status_t ret; | |
1945 | efi_uintn_t size; | |
1946 | struct list_head *pos, *n; | |
1947 | u16 *new_bootorder; | |
1948 | struct efimenu *efi_menu; | |
1949 | struct eficonfig_entry *entry; | |
1950 | struct eficonfig_save_boot_order_data *save_data = data; | |
1951 | ||
1952 | efi_menu = save_data->efi_menu; | |
1953 | ||
1954 | /* | |
1955 | * The change boot order menu always has "Save" and "Quit" entries. | |
1956 | * !(efi_menu->count - 2) means there is no user defined boot option. | |
1957 | */ | |
1958 | if (!(efi_menu->count - 2)) | |
1959 | return EFI_SUCCESS; | |
1960 | ||
1961 | new_bootorder = calloc(1, (efi_menu->count - 2) * sizeof(u16)); | |
1962 | if (!new_bootorder) { | |
1963 | ret = EFI_OUT_OF_RESOURCES; | |
1964 | goto out; | |
1965 | } | |
1966 | ||
1967 | /* create new BootOrder */ | |
1968 | count = 0; | |
1969 | list_for_each_safe(pos, n, &efi_menu->list) { | |
1970 | struct eficonfig_boot_order_data *data; | |
1971 | ||
1972 | entry = list_entry(pos, struct eficonfig_entry, list); | |
1973 | /* exit the loop when iteration reaches "Save" */ | |
1974 | if (!strncmp(entry->title, "Save", strlen("Save"))) | |
1975 | break; | |
1976 | ||
1977 | data = entry->data; | |
1978 | if (data->active) | |
1979 | new_bootorder[count++] = data->boot_index; | |
1980 | } | |
1981 | ||
1982 | size = count * sizeof(u16); | |
1983 | ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid, | |
1984 | EFI_VARIABLE_NON_VOLATILE | | |
1985 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
1986 | EFI_VARIABLE_RUNTIME_ACCESS, | |
1987 | size, new_bootorder, false); | |
1988 | ||
1989 | save_data->selected = true; | |
1990 | out: | |
1991 | free(new_bootorder); | |
1992 | ||
1993 | return ret; | |
1994 | } | |
1995 | ||
e5948ee3 MK |
1996 | /** |
1997 | * eficonfig_add_change_boot_order_entry() - add boot order entry | |
1998 | * | |
1999 | * @efi_menu: pointer to the efimenu structure | |
2000 | * @boot_index: boot option index to be added | |
2001 | * @active: flag to include the boot option into BootOrder | |
2002 | * Return: status code | |
2003 | */ | |
2004 | static efi_status_t eficonfig_add_change_boot_order_entry(struct efimenu *efi_menu, | |
2005 | u32 boot_index, bool active) | |
2006 | { | |
d571f9b2 | 2007 | char *title, *p; |
e5948ee3 MK |
2008 | efi_status_t ret; |
2009 | efi_uintn_t size; | |
2010 | void *load_option; | |
2011 | struct efi_load_option lo; | |
2012 | u16 varname[] = u"Boot####"; | |
d571f9b2 | 2013 | struct eficonfig_boot_order_data *data; |
e5948ee3 MK |
2014 | |
2015 | efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index); | |
2016 | load_option = efi_get_var(varname, &efi_global_variable_guid, &size); | |
2017 | if (!load_option) | |
2018 | return EFI_SUCCESS; | |
2019 | ||
2020 | ret = efi_deserialize_load_option(&lo, load_option, &size); | |
d571f9b2 MK |
2021 | if (ret != EFI_SUCCESS) |
2022 | goto out; | |
2023 | ||
2024 | data = calloc(1, sizeof(*data)); | |
2025 | if (!data) { | |
2026 | ret = EFI_OUT_OF_RESOURCES; | |
2027 | goto out; | |
e5948ee3 MK |
2028 | } |
2029 | ||
d571f9b2 MK |
2030 | title = calloc(1, utf16_utf8_strlen(lo.label) + 1); |
2031 | if (!title) { | |
2032 | free(data); | |
2033 | ret = EFI_OUT_OF_RESOURCES; | |
2034 | goto out; | |
e5948ee3 | 2035 | } |
d571f9b2 MK |
2036 | p = title; |
2037 | utf16_utf8_strcpy(&p, lo.label); | |
e5948ee3 | 2038 | |
d571f9b2 MK |
2039 | data->boot_index = boot_index; |
2040 | data->active = active; | |
2041 | ||
2042 | ret = eficonfig_append_menu_entry(efi_menu, title, NULL, data); | |
2043 | if (ret != EFI_SUCCESS) { | |
2044 | free(data); | |
2045 | free(title); | |
2046 | goto out; | |
e5948ee3 | 2047 | } |
e5948ee3 | 2048 | |
d571f9b2 | 2049 | out: |
e5948ee3 MK |
2050 | free(load_option); |
2051 | ||
d571f9b2 | 2052 | return ret; |
e5948ee3 MK |
2053 | } |
2054 | ||
2055 | /** | |
2056 | * eficonfig_create_change_boot_order_entry() - create boot order entry | |
2057 | * | |
2058 | * @efi_menu: pointer to the efimenu structure | |
2059 | * @bootorder: pointer to the BootOrder variable | |
2060 | * @num: number of BootOrder entry | |
2061 | * Return: status code | |
2062 | */ | |
2063 | static efi_status_t eficonfig_create_change_boot_order_entry(struct efimenu *efi_menu, | |
2064 | u16 *bootorder, efi_uintn_t num) | |
2065 | { | |
2066 | u32 i; | |
d571f9b2 | 2067 | char *title; |
e5948ee3 | 2068 | efi_status_t ret; |
ce327084 | 2069 | u16 *var_name16 = NULL; |
140a8959 | 2070 | efi_uintn_t size, buf_size; |
0d590852 | 2071 | struct eficonfig_save_boot_order_data *save_data; |
e5948ee3 MK |
2072 | |
2073 | /* list the load option in the order of BootOrder variable */ | |
2074 | for (i = 0; i < num; i++) { | |
2075 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2) | |
2076 | break; | |
2077 | ||
2078 | ret = eficonfig_add_change_boot_order_entry(efi_menu, bootorder[i], true); | |
2079 | if (ret != EFI_SUCCESS) | |
2080 | goto out; | |
2081 | } | |
2082 | ||
2083 | /* list the remaining load option not included in the BootOrder */ | |
140a8959 MK |
2084 | buf_size = 128; |
2085 | var_name16 = malloc(buf_size); | |
2086 | if (!var_name16) | |
2087 | return EFI_OUT_OF_RESOURCES; | |
2088 | ||
2089 | var_name16[0] = 0; | |
2090 | for (;;) { | |
2091 | int index; | |
2092 | efi_guid_t guid; | |
2093 | ||
e5948ee3 MK |
2094 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2) |
2095 | break; | |
2096 | ||
140a8959 | 2097 | size = buf_size; |
ce327084 | 2098 | ret = efi_next_variable_name(&buf_size, &var_name16, &guid); |
140a8959 MK |
2099 | if (ret == EFI_NOT_FOUND) |
2100 | break; | |
e5948ee3 MK |
2101 | if (ret != EFI_SUCCESS) |
2102 | goto out; | |
140a8959 MK |
2103 | |
2104 | if (efi_varname_is_load_option(var_name16, &index)) { | |
2105 | /* If the index is included in the BootOrder, skip it */ | |
339b527b | 2106 | if (efi_search_bootorder(bootorder, num, index, NULL)) |
140a8959 MK |
2107 | continue; |
2108 | ||
2109 | ret = eficonfig_add_change_boot_order_entry(efi_menu, index, false); | |
2110 | if (ret != EFI_SUCCESS) | |
2111 | goto out; | |
2112 | } | |
e5948ee3 MK |
2113 | } |
2114 | ||
2115 | /* add "Save" and "Quit" entries */ | |
d571f9b2 MK |
2116 | title = strdup("Save"); |
2117 | if (!title) { | |
2118 | ret = EFI_OUT_OF_RESOURCES; | |
e5948ee3 | 2119 | goto out; |
d571f9b2 | 2120 | } |
e5948ee3 | 2121 | |
0d590852 MK |
2122 | save_data = malloc(sizeof(struct eficonfig_save_boot_order_data)); |
2123 | if (!save_data) { | |
2124 | ret = EFI_OUT_OF_RESOURCES; | |
2125 | goto out; | |
2126 | } | |
2127 | save_data->efi_menu = efi_menu; | |
2128 | save_data->selected = false; | |
2129 | ||
2130 | ret = eficonfig_append_menu_entry(efi_menu, title, | |
2131 | eficonfig_process_save_boot_order, | |
2132 | save_data); | |
d571f9b2 | 2133 | if (ret != EFI_SUCCESS) |
e5948ee3 MK |
2134 | goto out; |
2135 | ||
d571f9b2 MK |
2136 | ret = eficonfig_append_quit_entry(efi_menu); |
2137 | if (ret != EFI_SUCCESS) | |
2138 | goto out; | |
e5948ee3 MK |
2139 | |
2140 | efi_menu->active = 0; | |
e5948ee3 | 2141 | out: |
140a8959 MK |
2142 | free(var_name16); |
2143 | ||
d571f9b2 | 2144 | return ret; |
e5948ee3 MK |
2145 | } |
2146 | ||
2147 | /** | |
2148 | * eficonfig_process_change_boot_order() - handler to change boot order | |
2149 | * | |
2150 | * @data: pointer to the data for each entry | |
2151 | * Return: status code | |
2152 | */ | |
2153 | static efi_status_t eficonfig_process_change_boot_order(void *data) | |
2154 | { | |
e5948ee3 MK |
2155 | u16 *bootorder; |
2156 | efi_status_t ret; | |
2157 | efi_uintn_t num, size; | |
2158 | struct list_head *pos, *n; | |
d571f9b2 | 2159 | struct eficonfig_entry *entry; |
e5948ee3 MK |
2160 | struct efimenu *efi_menu; |
2161 | ||
2162 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
2163 | if (!efi_menu) | |
2164 | return EFI_OUT_OF_RESOURCES; | |
2165 | ||
2166 | bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size); | |
2167 | ||
2168 | INIT_LIST_HEAD(&efi_menu->list); | |
2169 | num = size / sizeof(u16); | |
2170 | ret = eficonfig_create_change_boot_order_entry(efi_menu, bootorder, num); | |
2171 | if (ret != EFI_SUCCESS) | |
2172 | goto out; | |
2173 | ||
2174 | while (1) { | |
0d590852 MK |
2175 | ret = eficonfig_process_common(efi_menu, |
2176 | " ** Change Boot Order **", | |
2177 | eficonfig_change_boot_order_desc, | |
2178 | eficonfig_display_statusline, | |
2179 | eficonfig_print_change_boot_order_entry, | |
2180 | eficonfig_choice_change_boot_order); | |
2181 | /* exit from the menu if user selects the "Save" entry. */ | |
2182 | if (ret == EFI_SUCCESS && efi_menu->active == (efi_menu->count - 2)) { | |
2183 | list_for_each_prev_safe(pos, n, &efi_menu->list) { | |
d571f9b2 | 2184 | entry = list_entry(pos, struct eficonfig_entry, list); |
0d590852 | 2185 | if (entry->num == efi_menu->active) |
d571f9b2 | 2186 | break; |
e5948ee3 | 2187 | } |
0d590852 MK |
2188 | if (((struct eficonfig_save_boot_order_data *)entry->data)->selected) |
2189 | break; | |
e5948ee3 | 2190 | } |
0d590852 MK |
2191 | if (ret != EFI_SUCCESS) |
2192 | break; | |
e5948ee3 MK |
2193 | } |
2194 | out: | |
d571f9b2 | 2195 | free(bootorder); |
e5948ee3 | 2196 | list_for_each_safe(pos, n, &efi_menu->list) { |
d571f9b2 MK |
2197 | entry = list_entry(pos, struct eficonfig_entry, list); |
2198 | free(entry->data); | |
e5948ee3 | 2199 | } |
d571f9b2 | 2200 | eficonfig_destroy(efi_menu); |
e5948ee3 MK |
2201 | |
2202 | /* to stay the parent menu */ | |
2203 | ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; | |
2204 | ||
2205 | return ret; | |
2206 | } | |
2207 | ||
bb8498aa MK |
2208 | /** |
2209 | * eficonfig_process_delete_boot_option() - handler to delete boot option | |
2210 | * | |
2211 | * @data: pointer to the data for each entry | |
2212 | * Return: status code | |
2213 | */ | |
2214 | static efi_status_t eficonfig_process_delete_boot_option(void *data) | |
2215 | { | |
2216 | efi_status_t ret; | |
2217 | unsigned int selected; | |
2218 | ||
2219 | while (1) { | |
2220 | ret = eficonfig_show_boot_selection(&selected); | |
2221 | if (ret == EFI_SUCCESS) | |
339b527b | 2222 | ret = efi_bootmgr_delete_boot_option(selected); |
bb8498aa MK |
2223 | |
2224 | if (ret != EFI_SUCCESS) | |
2225 | break; | |
2226 | } | |
2227 | ||
2228 | /* to stay the parent menu */ | |
2229 | ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; | |
2230 | ||
2231 | return ret; | |
2232 | } | |
2233 | ||
87d79142 MK |
2234 | /** |
2235 | * eficonfig_init() - do required initialization for eficonfig command | |
2236 | * | |
2237 | * Return: status code | |
2238 | */ | |
2239 | static efi_status_t eficonfig_init(void) | |
2240 | { | |
2241 | efi_status_t ret = EFI_SUCCESS; | |
2242 | static bool init; | |
2243 | struct efi_handler *handler; | |
8dbd0a0f | 2244 | unsigned long columns, rows; |
87d79142 MK |
2245 | |
2246 | if (!init) { | |
2247 | ret = efi_search_protocol(efi_root, &efi_guid_text_input_protocol, &handler); | |
2248 | if (ret != EFI_SUCCESS) | |
2249 | return ret; | |
2250 | ||
2251 | ret = efi_protocol_open(handler, (void **)&cin, efi_root, NULL, | |
2252 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
2253 | if (ret != EFI_SUCCESS) | |
2254 | return ret; | |
8dbd0a0f MK |
2255 | ret = efi_search_protocol(efi_root, &efi_guid_text_output_protocol, &handler); |
2256 | if (ret != EFI_SUCCESS) | |
2257 | return ret; | |
2258 | ||
2259 | ret = efi_protocol_open(handler, (void **)&cout, efi_root, NULL, | |
2260 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
2261 | if (ret != EFI_SUCCESS) | |
2262 | return ret; | |
2263 | ||
2264 | cout->query_mode(cout, cout->mode->mode, &columns, &rows); | |
2265 | avail_row = rows - (EFICONFIG_MENU_HEADER_ROW_NUM + | |
2266 | EFICONFIG_MENU_DESC_ROW_NUM); | |
2267 | if (avail_row <= 0) { | |
2268 | eficonfig_print_msg("Console size is too small!"); | |
2269 | return EFI_INVALID_PARAMETER; | |
2270 | } | |
2271 | /* TODO: Should we check the minimum column size? */ | |
87d79142 MK |
2272 | } |
2273 | ||
2274 | init = true; | |
2275 | ||
2276 | return ret; | |
2277 | } | |
2278 | ||
2279 | static const struct eficonfig_item maintenance_menu_items[] = { | |
2280 | {"Add Boot Option", eficonfig_process_add_boot_option}, | |
e34158bc | 2281 | {"Edit Boot Option", eficonfig_process_edit_boot_option}, |
e5948ee3 | 2282 | {"Change Boot Order", eficonfig_process_change_boot_order}, |
bb8498aa | 2283 | {"Delete Boot Option", eficonfig_process_delete_boot_option}, |
2c0b0c31 | 2284 | #if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT) && IS_ENABLED(CONFIG_EFI_MM_COMM_TEE)) |
c3b5af63 MK |
2285 | {"Secure Boot Configuration", eficonfig_process_secure_boot_config}, |
2286 | #endif | |
87d79142 MK |
2287 | {"Quit", eficonfig_process_quit}, |
2288 | }; | |
2289 | ||
2290 | /** | |
2291 | * do_eficonfig() - execute `eficonfig` command | |
2292 | * | |
2293 | * @cmdtp: table entry describing command | |
2294 | * @flag: bitmap indicating how the command was invoked | |
2295 | * @argc: number of arguments | |
2296 | * @argv: command line arguments | |
2297 | * Return: status code | |
2298 | */ | |
2299 | static int do_eficonfig(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) | |
2300 | { | |
2301 | efi_status_t ret; | |
2302 | struct efimenu *efi_menu; | |
2303 | ||
2304 | if (argc > 1) | |
2305 | return CMD_RET_USAGE; | |
2306 | ||
2307 | ret = efi_init_obj_list(); | |
2308 | if (ret != EFI_SUCCESS) { | |
2309 | log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", | |
2310 | ret & ~EFI_ERROR_MASK); | |
2311 | ||
2312 | return CMD_RET_FAILURE; | |
2313 | } | |
2314 | ||
2315 | ret = eficonfig_init(); | |
2316 | if (ret != EFI_SUCCESS) | |
2317 | return CMD_RET_FAILURE; | |
2318 | ||
339b527b | 2319 | ret = efi_bootmgr_update_media_device_boot_option(); |
9945bc4f | 2320 | if (ret != EFI_SUCCESS) |
b5135a1e MK |
2321 | return ret; |
2322 | ||
87d79142 MK |
2323 | while (1) { |
2324 | efi_menu = eficonfig_create_fixed_menu(maintenance_menu_items, | |
2325 | ARRAY_SIZE(maintenance_menu_items)); | |
2326 | if (!efi_menu) | |
2327 | return CMD_RET_FAILURE; | |
2328 | ||
cd160b27 MK |
2329 | ret = eficonfig_process_common(efi_menu, |
2330 | " ** UEFI Maintenance Menu **", | |
2331 | eficonfig_menu_desc, | |
2332 | eficonfig_display_statusline, | |
2333 | eficonfig_print_entry, | |
2334 | eficonfig_choice_entry); | |
87d79142 MK |
2335 | eficonfig_destroy(efi_menu); |
2336 | ||
2337 | if (ret == EFI_ABORTED) | |
2338 | break; | |
2339 | } | |
2340 | ||
2341 | return CMD_RET_SUCCESS; | |
2342 | } | |
2343 | ||
2344 | U_BOOT_CMD( | |
2345 | eficonfig, 1, 0, do_eficonfig, | |
2346 | "provide menu-driven UEFI variable maintenance interface", | |
2347 | "" | |
2348 | ); |