]>
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 <charset.h> |
11 | #include <efi_loader.h> | |
12 | #include <efi_load_initrd.h> | |
13 | #include <efi_config.h> | |
14 | #include <efi_variable.h> | |
15 | #include <log.h> | |
16 | #include <malloc.h> | |
17 | #include <menu.h> | |
18 | #include <sort.h> | |
19 | #include <watchdog.h> | |
20 | #include <asm/unaligned.h> | |
21 | #include <linux/delay.h> | |
22 | ||
23 | static struct efi_simple_text_input_protocol *cin; | |
cd160b27 | 24 | const char *eficonfig_menu_desc = |
45f5319f | 25 | " Press UP/DOWN to move, ENTER to select, ESC to quit"; |
87d79142 | 26 | |
0d590852 MK |
27 | static const char *eficonfig_change_boot_order_desc = |
28 | " Press UP/DOWN to move, +/- to change orde\n" | |
29 | " Press SPACE to activate or deactivate the entry\n" | |
88df3634 | 30 | " CTRL+S to save, ESC to quit"; |
0d590852 | 31 | |
8dbd0a0f MK |
32 | static struct efi_simple_text_output_protocol *cout; |
33 | static int avail_row; | |
34 | ||
87d79142 MK |
35 | #define EFICONFIG_DESCRIPTION_MAX 32 |
36 | #define EFICONFIG_OPTIONAL_DATA_MAX 64 | |
8dbd0a0f MK |
37 | #define EFICONFIG_MENU_HEADER_ROW_NUM 3 |
38 | #define EFICONFIG_MENU_DESC_ROW_NUM 5 | |
87d79142 MK |
39 | |
40 | /** | |
41 | * struct eficonfig_filepath_info - structure to be used to store file path | |
42 | * | |
43 | * @name: file or directory name | |
44 | * @list: list structure | |
45 | */ | |
46 | struct eficonfig_filepath_info { | |
47 | char *name; | |
48 | struct list_head list; | |
49 | }; | |
50 | ||
51 | /** | |
52 | * struct eficonfig_boot_option - structure to be used for updating UEFI boot option | |
53 | * | |
54 | * @file_info: user selected file info | |
55 | * @initrd_info: user selected initrd file info | |
56 | * @boot_index: index of the boot option | |
57 | * @description: pointer to the description string | |
58 | * @optional_data: pointer to the optional_data | |
59 | * @edit_completed: flag indicates edit complete | |
60 | */ | |
61 | struct eficonfig_boot_option { | |
62 | struct eficonfig_select_file_info file_info; | |
63 | struct eficonfig_select_file_info initrd_info; | |
64 | unsigned int boot_index; | |
65 | u16 *description; | |
66 | u16 *optional_data; | |
67 | bool edit_completed; | |
68 | }; | |
69 | ||
70 | /** | |
71 | * struct eficonfig_volume_entry_data - structure to be used to store volume info | |
72 | * | |
73 | * @file_info: pointer to file info structure | |
74 | * @v: pointer to the protocol interface | |
75 | * @dp: pointer to the device path | |
76 | */ | |
77 | struct eficonfig_volume_entry_data { | |
78 | struct eficonfig_select_file_info *file_info; | |
79 | struct efi_simple_file_system_protocol *v; | |
80 | struct efi_device_path *dp; | |
81 | }; | |
82 | ||
83 | /** | |
84 | * struct eficonfig_file_entry_data - structure to be used to store file info | |
85 | * | |
86 | * @file_info: pointer to file info structure | |
87 | * @is_directory: flag to identify the directory or file | |
88 | * @file_name: name of directory or file | |
89 | */ | |
90 | struct eficonfig_file_entry_data { | |
91 | struct eficonfig_select_file_info *file_info; | |
92 | bool is_directory; | |
93 | char *file_name; | |
94 | }; | |
95 | ||
e34158bc MK |
96 | /** |
97 | * struct eficonfig_boot_selection_data - structure to be used to select the boot option entry | |
98 | * | |
99 | * @boot_index: index of the boot option | |
100 | * @selected: pointer to store the selected index in the BootOrder variable | |
101 | */ | |
102 | struct eficonfig_boot_selection_data { | |
103 | u16 boot_index; | |
104 | int *selected; | |
105 | }; | |
106 | ||
e5948ee3 | 107 | /** |
d571f9b2 | 108 | * struct eficonfig_boot_order_data - structure to be used to update BootOrder variable |
e5948ee3 | 109 | * |
e5948ee3 MK |
110 | * @boot_index: boot option index |
111 | * @active: flag to include the boot option into BootOrder variable | |
e5948ee3 | 112 | */ |
d571f9b2 | 113 | struct eficonfig_boot_order_data { |
e5948ee3 MK |
114 | u32 boot_index; |
115 | bool active; | |
e5948ee3 MK |
116 | }; |
117 | ||
0d590852 MK |
118 | /** |
119 | * struct eficonfig_save_boot_order_data - structure to be used to change boot order | |
120 | * | |
121 | * @efi_menu: pointer to efimenu structure | |
122 | * @selected: flag to indicate user selects "Save" entry | |
123 | */ | |
124 | struct eficonfig_save_boot_order_data { | |
125 | struct efimenu *efi_menu; | |
126 | bool selected; | |
127 | }; | |
128 | ||
8dbd0a0f MK |
129 | /** |
130 | * struct eficonfig_menu_adjust - update start and end entry index | |
131 | * | |
132 | * @efi_menu: pointer to efimenu structure | |
133 | * @add: flag to add or substract the index | |
134 | */ | |
135 | static void eficonfig_menu_adjust(struct efimenu *efi_menu, bool add) | |
136 | { | |
137 | if (add) | |
138 | ++efi_menu->active; | |
139 | else | |
140 | --efi_menu->active; | |
141 | ||
142 | if (add && efi_menu->end < efi_menu->active) { | |
143 | efi_menu->start++; | |
144 | efi_menu->end++; | |
145 | } else if (!add && efi_menu->start > efi_menu->active) { | |
146 | efi_menu->start--; | |
147 | efi_menu->end--; | |
148 | } | |
149 | } | |
150 | #define eficonfig_menu_up(_a) eficonfig_menu_adjust(_a, false) | |
151 | #define eficonfig_menu_down(_a) eficonfig_menu_adjust(_a, true) | |
152 | ||
87d79142 MK |
153 | /** |
154 | * eficonfig_print_msg() - print message | |
155 | * | |
156 | * display the message to the user, user proceeds the screen | |
157 | * with any key press. | |
158 | * | |
159 | * @items: pointer to the structure of each menu entry | |
160 | * @count: the number of menu entry | |
161 | * @menu_header: pointer to the menu header string | |
162 | * Return: status code | |
163 | */ | |
164 | void eficonfig_print_msg(char *msg) | |
165 | { | |
166 | /* Flush input */ | |
167 | while (tstc()) | |
168 | getchar(); | |
169 | ||
170 | printf(ANSI_CURSOR_HIDE | |
171 | ANSI_CLEAR_CONSOLE | |
172 | ANSI_CURSOR_POSITION | |
173 | "%s\n\n Press any key to continue", 3, 4, msg); | |
174 | ||
175 | getchar(); | |
176 | } | |
177 | ||
178 | /** | |
179 | * eficonfig_print_entry() - print each menu entry | |
180 | * | |
181 | * @data: pointer to the data associated with each menu entry | |
182 | */ | |
cd160b27 | 183 | void eficonfig_print_entry(void *data) |
87d79142 MK |
184 | { |
185 | struct eficonfig_entry *entry = data; | |
cd160b27 | 186 | bool reverse = (entry->efi_menu->active == entry->num); |
87d79142 | 187 | |
8dbd0a0f MK |
188 | if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num) |
189 | return; | |
87d79142 | 190 | |
8dbd0a0f MK |
191 | printf(ANSI_CURSOR_POSITION, (entry->num - entry->efi_menu->start) + |
192 | EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7); | |
87d79142 MK |
193 | |
194 | if (reverse) | |
195 | puts(ANSI_COLOR_REVERSE); | |
196 | ||
8dbd0a0f | 197 | printf(ANSI_CLEAR_LINE "%s", entry->title); |
87d79142 MK |
198 | |
199 | if (reverse) | |
200 | puts(ANSI_COLOR_RESET); | |
201 | } | |
202 | ||
203 | /** | |
204 | * eficonfig_display_statusline() - print status line | |
205 | * | |
206 | * @m: pointer to the menu structure | |
207 | */ | |
cd160b27 | 208 | void eficonfig_display_statusline(struct menu *m) |
87d79142 MK |
209 | { |
210 | struct eficonfig_entry *entry; | |
211 | ||
212 | if (menu_default_choice(m, (void *)&entry) < 0) | |
213 | return; | |
214 | ||
215 | printf(ANSI_CURSOR_POSITION | |
216 | "\n%s\n" | |
217 | ANSI_CURSOR_POSITION ANSI_CLEAR_LINE ANSI_CURSOR_POSITION | |
cd160b27 | 218 | "%s" |
0d590852 | 219 | ANSI_CLEAR_LINE_TO_END, |
8dbd0a0f MK |
220 | 1, 1, entry->efi_menu->menu_header, avail_row + 4, 1, |
221 | avail_row + 5, 1, entry->efi_menu->menu_desc); | |
87d79142 MK |
222 | } |
223 | ||
224 | /** | |
225 | * eficonfig_choice_entry() - user key input handler | |
226 | * | |
227 | * @data: pointer to the efimenu structure | |
228 | * Return: key string to identify the selected entry | |
229 | */ | |
cd160b27 | 230 | char *eficonfig_choice_entry(void *data) |
87d79142 | 231 | { |
32bab0ea | 232 | struct cli_ch_state s_cch, *cch = &s_cch; |
87d79142 MK |
233 | struct list_head *pos, *n; |
234 | struct eficonfig_entry *entry; | |
2da4a15e | 235 | enum bootmenu_key key = BKEY_NONE; |
87d79142 MK |
236 | struct efimenu *efi_menu = data; |
237 | ||
32bab0ea SG |
238 | cli_ch_init(cch); |
239 | ||
87d79142 | 240 | while (1) { |
32bab0ea | 241 | key = bootmenu_loop((struct bootmenu_data *)efi_menu, cch); |
87d79142 MK |
242 | |
243 | switch (key) { | |
2da4a15e | 244 | case BKEY_UP: |
87d79142 | 245 | if (efi_menu->active > 0) |
8dbd0a0f MK |
246 | eficonfig_menu_up(efi_menu); |
247 | ||
87d79142 MK |
248 | /* no menu key selected, regenerate menu */ |
249 | return NULL; | |
2da4a15e | 250 | case BKEY_DOWN: |
87d79142 | 251 | if (efi_menu->active < efi_menu->count - 1) |
8dbd0a0f MK |
252 | eficonfig_menu_down(efi_menu); |
253 | ||
87d79142 MK |
254 | /* no menu key selected, regenerate menu */ |
255 | return NULL; | |
2da4a15e | 256 | case BKEY_SELECT: |
87d79142 MK |
257 | list_for_each_safe(pos, n, &efi_menu->list) { |
258 | entry = list_entry(pos, struct eficonfig_entry, list); | |
259 | if (entry->num == efi_menu->active) | |
260 | return entry->key; | |
261 | } | |
262 | break; | |
2da4a15e | 263 | case BKEY_QUIT: |
87d79142 MK |
264 | /* Quit by choosing the last entry */ |
265 | entry = list_last_entry(&efi_menu->list, struct eficonfig_entry, list); | |
266 | return entry->key; | |
267 | default: | |
268 | /* Pressed key is not valid, no need to regenerate the menu */ | |
269 | break; | |
270 | } | |
271 | } | |
272 | } | |
273 | ||
274 | /** | |
275 | * eficonfig_destroy() - destroy efimenu | |
276 | * | |
277 | * @efi_menu: pointer to the efimenu structure | |
278 | */ | |
279 | void eficonfig_destroy(struct efimenu *efi_menu) | |
280 | { | |
281 | struct list_head *pos, *n; | |
282 | struct eficonfig_entry *entry; | |
283 | ||
284 | if (!efi_menu) | |
285 | return; | |
286 | ||
287 | list_for_each_safe(pos, n, &efi_menu->list) { | |
288 | entry = list_entry(pos, struct eficonfig_entry, list); | |
289 | free(entry->title); | |
290 | list_del(&entry->list); | |
291 | free(entry); | |
292 | } | |
293 | free(efi_menu->menu_header); | |
294 | free(efi_menu); | |
295 | } | |
296 | ||
297 | /** | |
298 | * eficonfig_process_quit() - callback function for "Quit" entry | |
299 | * | |
300 | * @data: pointer to the data | |
301 | * Return: status code | |
302 | */ | |
303 | efi_status_t eficonfig_process_quit(void *data) | |
304 | { | |
305 | return EFI_ABORTED; | |
306 | } | |
307 | ||
308 | /** | |
8961e93e | 309 | * eficonfig_append_menu_entry() - append menu item |
87d79142 MK |
310 | * |
311 | * @efi_menu: pointer to the efimenu structure | |
312 | * @title: pointer to the entry title | |
313 | * @func: callback of each entry | |
314 | * @data: pointer to the data to be passed to each entry callback | |
315 | * Return: status code | |
316 | */ | |
8961e93e MK |
317 | efi_status_t eficonfig_append_menu_entry(struct efimenu *efi_menu, |
318 | char *title, eficonfig_entry_func func, | |
319 | void *data) | |
87d79142 MK |
320 | { |
321 | struct eficonfig_entry *entry; | |
322 | ||
323 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX) | |
324 | return EFI_OUT_OF_RESOURCES; | |
325 | ||
326 | entry = calloc(1, sizeof(struct eficonfig_entry)); | |
327 | if (!entry) | |
328 | return EFI_OUT_OF_RESOURCES; | |
329 | ||
330 | entry->title = title; | |
331 | sprintf(entry->key, "%d", efi_menu->count); | |
332 | entry->efi_menu = efi_menu; | |
333 | entry->func = func; | |
334 | entry->data = data; | |
335 | entry->num = efi_menu->count++; | |
336 | list_add_tail(&entry->list, &efi_menu->list); | |
337 | ||
338 | return EFI_SUCCESS; | |
339 | } | |
340 | ||
341 | /** | |
8961e93e | 342 | * eficonfig_append_quit_entry() - append quit entry |
87d79142 MK |
343 | * |
344 | * @efi_menu: pointer to the efimenu structure | |
345 | * Return: status code | |
346 | */ | |
8961e93e | 347 | efi_status_t eficonfig_append_quit_entry(struct efimenu *efi_menu) |
87d79142 MK |
348 | { |
349 | char *title; | |
350 | efi_status_t ret; | |
351 | ||
352 | title = strdup("Quit"); | |
353 | if (!title) | |
354 | return EFI_OUT_OF_RESOURCES; | |
355 | ||
8961e93e | 356 | ret = eficonfig_append_menu_entry(efi_menu, title, eficonfig_process_quit, NULL); |
87d79142 MK |
357 | if (ret != EFI_SUCCESS) |
358 | free(title); | |
359 | ||
360 | return ret; | |
361 | } | |
362 | ||
363 | /** | |
364 | * eficonfig_create_fixed_menu() - create fixed entry menu structure | |
365 | * | |
366 | * @items: pointer to the menu entry item | |
367 | * @count: the number of menu entry | |
368 | * Return: pointer to the efimenu structure | |
369 | */ | |
370 | void *eficonfig_create_fixed_menu(const struct eficonfig_item *items, int count) | |
371 | { | |
372 | u32 i; | |
373 | char *title; | |
374 | efi_status_t ret; | |
375 | struct efimenu *efi_menu; | |
376 | const struct eficonfig_item *iter = items; | |
377 | ||
378 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
379 | if (!efi_menu) | |
380 | return NULL; | |
381 | ||
382 | INIT_LIST_HEAD(&efi_menu->list); | |
383 | for (i = 0; i < count; i++, iter++) { | |
384 | title = strdup(iter->title); | |
385 | if (!title) | |
386 | goto out; | |
387 | ||
8961e93e | 388 | ret = eficonfig_append_menu_entry(efi_menu, title, iter->func, iter->data); |
87d79142 MK |
389 | if (ret != EFI_SUCCESS) { |
390 | free(title); | |
391 | goto out; | |
392 | } | |
393 | } | |
394 | ||
395 | return efi_menu; | |
396 | out: | |
397 | eficonfig_destroy(efi_menu); | |
398 | ||
399 | return NULL; | |
400 | } | |
401 | ||
402 | /** | |
403 | * eficonfig_process_common() - main handler for UEFI menu | |
404 | * | |
405 | * Construct the structures required to show the menu, then handle | |
406 | * the user input interacting with u-boot menu functions. | |
407 | * | |
408 | * @efi_menu: pointer to the efimenu structure | |
409 | * @menu_header: pointer to the menu header string | |
cd160b27 MK |
410 | * @menu_desc: pointer to the menu description |
411 | * @display_statusline: function pointer to draw statusline | |
412 | * @item_data_print: function pointer to draw the menu item | |
413 | * @item_choice: function pointer to handle the key press | |
87d79142 MK |
414 | * Return: status code |
415 | */ | |
cd160b27 MK |
416 | efi_status_t eficonfig_process_common(struct efimenu *efi_menu, |
417 | char *menu_header, const char *menu_desc, | |
418 | void (*display_statusline)(struct menu *), | |
419 | void (*item_data_print)(void *), | |
420 | char *(*item_choice)(void *)) | |
87d79142 MK |
421 | { |
422 | struct menu *menu; | |
423 | void *choice = NULL; | |
424 | struct list_head *pos, *n; | |
425 | struct eficonfig_entry *entry; | |
426 | efi_status_t ret = EFI_SUCCESS; | |
427 | ||
428 | if (efi_menu->count > EFICONFIG_ENTRY_NUM_MAX) | |
429 | return EFI_OUT_OF_RESOURCES; | |
430 | ||
431 | efi_menu->delay = -1; | |
432 | efi_menu->active = 0; | |
8dbd0a0f MK |
433 | efi_menu->start = 0; |
434 | efi_menu->end = avail_row - 1; | |
87d79142 MK |
435 | |
436 | if (menu_header) { | |
437 | efi_menu->menu_header = strdup(menu_header); | |
438 | if (!efi_menu->menu_header) | |
439 | return EFI_OUT_OF_RESOURCES; | |
440 | } | |
cd160b27 MK |
441 | if (menu_desc) |
442 | efi_menu->menu_desc = menu_desc; | |
87d79142 | 443 | |
cd160b27 MK |
444 | menu = menu_create(NULL, 0, 1, display_statusline, item_data_print, |
445 | item_choice, efi_menu); | |
87d79142 MK |
446 | if (!menu) |
447 | return EFI_INVALID_PARAMETER; | |
448 | ||
449 | list_for_each_safe(pos, n, &efi_menu->list) { | |
450 | entry = list_entry(pos, struct eficonfig_entry, list); | |
451 | if (!menu_item_add(menu, entry->key, entry)) { | |
452 | ret = EFI_INVALID_PARAMETER; | |
453 | goto out; | |
454 | } | |
455 | } | |
456 | ||
457 | entry = list_first_entry_or_null(&efi_menu->list, struct eficonfig_entry, list); | |
458 | if (entry) | |
459 | menu_default_set(menu, entry->key); | |
460 | ||
461 | printf(ANSI_CURSOR_HIDE | |
462 | ANSI_CLEAR_CONSOLE | |
463 | ANSI_CURSOR_POSITION, 1, 1); | |
464 | ||
465 | if (menu_get_choice(menu, &choice)) { | |
466 | entry = choice; | |
467 | if (entry->func) | |
468 | ret = entry->func(entry->data); | |
469 | } | |
470 | out: | |
471 | menu_destroy(menu); | |
472 | ||
473 | printf(ANSI_CLEAR_CONSOLE | |
474 | ANSI_CURSOR_POSITION | |
475 | ANSI_CURSOR_SHOW, 1, 1); | |
476 | ||
477 | return ret; | |
478 | } | |
479 | ||
480 | /** | |
481 | * eficonfig_volume_selected() - handler of volume selection | |
482 | * | |
483 | * @data: pointer to the data of selected entry | |
484 | * Return: status code | |
485 | */ | |
486 | static efi_status_t eficonfig_volume_selected(void *data) | |
487 | { | |
488 | struct eficonfig_volume_entry_data *info = data; | |
489 | ||
490 | if (info) { | |
491 | info->file_info->current_volume = info->v; | |
492 | info->file_info->dp_volume = info->dp; | |
493 | } | |
494 | ||
495 | return EFI_SUCCESS; | |
496 | } | |
497 | ||
498 | /** | |
d6566113 | 499 | * eficonfig_create_device_path() - create device path |
87d79142 | 500 | * |
d6566113 MK |
501 | * @dp_volume: pointer to the volume |
502 | * @current_path: pointer to the file path u16 string | |
87d79142 MK |
503 | * Return: |
504 | * device path or NULL. Caller must free the returned value | |
505 | */ | |
d6566113 MK |
506 | struct efi_device_path *eficonfig_create_device_path(struct efi_device_path *dp_volume, |
507 | u16 *current_path) | |
87d79142 MK |
508 | { |
509 | char *p; | |
510 | void *buf; | |
511 | efi_uintn_t fp_size; | |
512 | struct efi_device_path *dp; | |
513 | struct efi_device_path_file_path *fp; | |
514 | ||
78b1ccc4 | 515 | fp_size = sizeof(struct efi_device_path) + u16_strsize(current_path); |
87d79142 MK |
516 | buf = calloc(1, fp_size + sizeof(END)); |
517 | if (!buf) | |
518 | return NULL; | |
519 | ||
520 | fp = buf; | |
521 | fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE, | |
522 | fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, | |
523 | fp->dp.length = (u16)fp_size; | |
d6566113 | 524 | u16_strcpy(fp->str, current_path); |
87d79142 MK |
525 | |
526 | p = buf; | |
527 | p += fp_size; | |
528 | *((struct efi_device_path *)p) = END; | |
529 | ||
64658007 HS |
530 | dp = efi_dp_shorten(dp_volume); |
531 | if (!dp) | |
532 | dp = dp_volume; | |
f19171c9 | 533 | dp = efi_dp_concat(dp, &fp->dp, false); |
87d79142 MK |
534 | free(buf); |
535 | ||
536 | return dp; | |
537 | } | |
538 | ||
539 | /** | |
540 | * eficonfig_file_selected() - handler of file selection | |
541 | * | |
542 | * @data: pointer to the data of selected entry | |
543 | * Return: status code | |
544 | */ | |
545 | static efi_status_t eficonfig_file_selected(void *data) | |
546 | { | |
547 | u16 *tmp; | |
548 | struct eficonfig_file_entry_data *info = data; | |
549 | ||
550 | if (!info) | |
551 | return EFI_INVALID_PARAMETER; | |
552 | ||
c67d3c9e | 553 | if (!strcmp(info->file_name, "..\\")) { |
87d79142 MK |
554 | struct eficonfig_filepath_info *iter; |
555 | struct list_head *pos, *n; | |
556 | int is_last; | |
557 | char *filepath; | |
558 | tmp = info->file_info->current_path; | |
559 | ||
560 | memset(info->file_info->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE); | |
561 | filepath = calloc(1, EFICONFIG_FILE_PATH_MAX); | |
562 | if (!filepath) | |
563 | return EFI_OUT_OF_RESOURCES; | |
564 | ||
565 | list_for_each_safe(pos, n, &info->file_info->filepath_list) { | |
566 | iter = list_entry(pos, struct eficonfig_filepath_info, list); | |
567 | ||
568 | is_last = list_is_last(&iter->list, &info->file_info->filepath_list); | |
569 | if (is_last) { | |
570 | list_del(&iter->list); | |
571 | free(iter->name); | |
572 | free(iter); | |
573 | break; | |
574 | } | |
575 | strlcat(filepath, iter->name, EFICONFIG_FILE_PATH_MAX); | |
576 | } | |
577 | utf8_utf16_strcpy(&tmp, filepath); | |
578 | } else { | |
579 | size_t new_len; | |
580 | struct eficonfig_filepath_info *filepath_info; | |
581 | ||
582 | new_len = u16_strlen(info->file_info->current_path) + | |
583 | strlen(info->file_name); | |
584 | if (new_len >= EFICONFIG_FILE_PATH_MAX) { | |
585 | eficonfig_print_msg("File path is too long!"); | |
586 | return EFI_INVALID_PARAMETER; | |
587 | } | |
588 | tmp = &info->file_info->current_path[u16_strlen(info->file_info->current_path)]; | |
589 | utf8_utf16_strcpy(&tmp, info->file_name); | |
590 | ||
591 | filepath_info = calloc(1, sizeof(struct eficonfig_filepath_info)); | |
592 | if (!filepath_info) | |
593 | return EFI_OUT_OF_RESOURCES; | |
594 | ||
595 | filepath_info->name = strdup(info->file_name); | |
596 | if (!filepath_info->name) { | |
597 | free(filepath_info); | |
598 | return EFI_OUT_OF_RESOURCES; | |
599 | } | |
600 | list_add_tail(&filepath_info->list, &info->file_info->filepath_list); | |
601 | ||
602 | if (!info->is_directory) | |
603 | info->file_info->file_selected = true; | |
604 | } | |
605 | ||
606 | return EFI_SUCCESS; | |
607 | } | |
608 | ||
609 | /** | |
610 | * eficonfig_select_volume() - construct the volume selection menu | |
611 | * | |
612 | * @file_info: pointer to the file selection structure | |
613 | * Return: status code | |
614 | */ | |
615 | static efi_status_t eficonfig_select_volume(struct eficonfig_select_file_info *file_info) | |
616 | { | |
617 | u32 i; | |
618 | efi_status_t ret; | |
619 | efi_uintn_t count; | |
620 | struct efimenu *efi_menu; | |
621 | struct list_head *pos, *n; | |
622 | struct efi_handler *handler; | |
623 | struct eficonfig_entry *entry; | |
624 | struct efi_device_path *device_path; | |
625 | efi_handle_t *volume_handles = NULL; | |
626 | struct efi_simple_file_system_protocol *v; | |
627 | ||
628 | ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid, | |
629 | NULL, &count, (efi_handle_t **)&volume_handles); | |
630 | if (ret != EFI_SUCCESS) { | |
631 | eficonfig_print_msg("No block device found!"); | |
632 | return ret; | |
633 | } | |
634 | ||
635 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
636 | if (!efi_menu) | |
637 | return EFI_OUT_OF_RESOURCES; | |
638 | ||
639 | INIT_LIST_HEAD(&efi_menu->list); | |
640 | for (i = 0; i < count; i++) { | |
641 | char *devname; | |
642 | struct efi_block_io *block_io; | |
643 | struct eficonfig_volume_entry_data *info; | |
644 | ||
645 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1) | |
646 | break; | |
647 | ||
648 | ret = efi_search_protocol(volume_handles[i], | |
649 | &efi_simple_file_system_protocol_guid, &handler); | |
650 | if (ret != EFI_SUCCESS) | |
651 | continue; | |
652 | ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL, | |
653 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
654 | if (ret != EFI_SUCCESS) | |
655 | continue; | |
656 | ||
657 | ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler); | |
658 | if (ret != EFI_SUCCESS) | |
659 | continue; | |
660 | ret = efi_protocol_open(handler, (void **)&device_path, | |
661 | efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
662 | if (ret != EFI_SUCCESS) | |
663 | continue; | |
664 | ||
665 | ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler); | |
666 | if (ret != EFI_SUCCESS) | |
667 | continue; | |
668 | ret = efi_protocol_open(handler, (void **)&block_io, | |
669 | efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
670 | if (ret != EFI_SUCCESS) | |
671 | continue; | |
672 | ||
673 | info = calloc(1, sizeof(struct eficonfig_volume_entry_data)); | |
674 | if (!info) { | |
675 | ret = EFI_OUT_OF_RESOURCES; | |
676 | goto out; | |
677 | } | |
678 | ||
679 | devname = calloc(1, BOOTMENU_DEVICE_NAME_MAX); | |
680 | if (!devname) { | |
681 | free(info); | |
682 | ret = EFI_OUT_OF_RESOURCES; | |
683 | goto out; | |
684 | } | |
685 | ret = efi_disk_get_device_name(volume_handles[i], devname, | |
686 | BOOTMENU_DEVICE_NAME_MAX); | |
687 | if (ret != EFI_SUCCESS) { | |
688 | free(info); | |
689 | goto out; | |
690 | } | |
691 | ||
692 | info->v = v; | |
693 | info->dp = device_path; | |
694 | info->file_info = file_info; | |
8961e93e MK |
695 | ret = eficonfig_append_menu_entry(efi_menu, devname, eficonfig_volume_selected, |
696 | info); | |
87d79142 MK |
697 | if (ret != EFI_SUCCESS) { |
698 | free(info); | |
699 | goto out; | |
700 | } | |
701 | } | |
702 | ||
8961e93e | 703 | ret = eficonfig_append_quit_entry(efi_menu); |
87d79142 MK |
704 | if (ret != EFI_SUCCESS) |
705 | goto out; | |
706 | ||
cd160b27 MK |
707 | ret = eficonfig_process_common(efi_menu, " ** Select Volume **", |
708 | eficonfig_menu_desc, | |
709 | eficonfig_display_statusline, | |
710 | eficonfig_print_entry, | |
711 | eficonfig_choice_entry); | |
712 | ||
87d79142 MK |
713 | out: |
714 | efi_free_pool(volume_handles); | |
715 | list_for_each_safe(pos, n, &efi_menu->list) { | |
716 | entry = list_entry(pos, struct eficonfig_entry, list); | |
717 | free(entry->data); | |
718 | } | |
719 | eficonfig_destroy(efi_menu); | |
720 | ||
721 | return ret; | |
722 | } | |
723 | ||
724 | /** | |
725 | * sort_file() - sort the file name in ascii order | |
726 | * | |
727 | * @data1: pointer to the file entry data | |
728 | * @data2: pointer to the file entry data | |
729 | * Return: -1 if the data1 file name is less than data2 file name, | |
730 | * 0 if both file name match, | |
731 | * 1 if the data1 file name is greater thant data2 file name. | |
732 | */ | |
733 | static int sort_file(const void *arg1, const void *arg2) | |
734 | { | |
735 | const struct eficonfig_file_entry_data *data1, *data2; | |
736 | ||
737 | data1 = *((const struct eficonfig_file_entry_data **)arg1); | |
738 | data2 = *((const struct eficonfig_file_entry_data **)arg2); | |
739 | ||
740 | return strcasecmp(data1->file_name, data2->file_name); | |
741 | } | |
742 | ||
743 | /** | |
744 | * eficonfig_create_file_entry() - construct the file menu entry | |
745 | * | |
746 | * @efi_menu: pointer to the efimenu structure | |
747 | * @count: number of the directory and file | |
748 | * @tmp_infos: pointer to the entry data array | |
749 | * @f: pointer to the file handle | |
750 | * @buf: pointer to the buffer to store the directory information | |
751 | * @file_info: pointer to the file selection structure | |
752 | * Return: status code | |
753 | */ | |
754 | static efi_status_t | |
755 | eficonfig_create_file_entry(struct efimenu *efi_menu, u32 count, | |
756 | struct eficonfig_file_entry_data **tmp_infos, | |
757 | struct efi_file_handle *f, struct efi_file_info *buf, | |
758 | struct eficonfig_select_file_info *file_info) | |
759 | { | |
760 | char *name, *p; | |
761 | efi_uintn_t len; | |
762 | efi_status_t ret; | |
763 | u32 i, entry_num = 0; | |
764 | struct eficonfig_file_entry_data *info; | |
765 | ||
21faf4ef | 766 | EFI_CALL(f->setpos(f, 0)); |
87d79142 MK |
767 | /* Read directory and construct menu structure */ |
768 | for (i = 0; i < count; i++) { | |
769 | if (entry_num >= EFICONFIG_ENTRY_NUM_MAX - 1) | |
770 | break; | |
771 | ||
772 | len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE; | |
21faf4ef | 773 | ret = EFI_CALL(f->read(f, &len, buf)); |
87d79142 MK |
774 | if (ret != EFI_SUCCESS || len == 0) |
775 | break; | |
776 | ||
777 | info = calloc(1, sizeof(struct eficonfig_file_entry_data)); | |
778 | if (!info) { | |
779 | ret = EFI_OUT_OF_RESOURCES; | |
780 | goto out; | |
781 | } | |
782 | ||
783 | /* append '\\' at the end of directory name */ | |
784 | name = calloc(1, utf16_utf8_strlen(buf->file_name) + 2); | |
785 | if (!name) { | |
786 | ret = EFI_OUT_OF_RESOURCES; | |
787 | free(info); | |
788 | goto out; | |
789 | } | |
790 | p = name; | |
791 | utf16_utf8_strcpy(&p, buf->file_name); | |
792 | if (buf->attribute & EFI_FILE_DIRECTORY) { | |
793 | /* filter out u'.' */ | |
794 | if (!u16_strcmp(buf->file_name, u".")) { | |
795 | free(info); | |
796 | free(name); | |
797 | continue; | |
798 | } | |
799 | name[u16_strlen(buf->file_name)] = '\\'; | |
800 | info->is_directory = true; | |
801 | } | |
802 | ||
803 | info->file_name = name; | |
804 | info->file_info = file_info; | |
805 | tmp_infos[entry_num++] = info; | |
806 | } | |
807 | ||
808 | qsort(tmp_infos, entry_num, sizeof(*tmp_infos), | |
809 | (int (*)(const void *, const void *))sort_file); | |
810 | ||
811 | for (i = 0; i < entry_num; i++) { | |
8961e93e MK |
812 | ret = eficonfig_append_menu_entry(efi_menu, tmp_infos[i]->file_name, |
813 | eficonfig_file_selected, tmp_infos[i]); | |
87d79142 MK |
814 | if (ret != EFI_SUCCESS) |
815 | goto out; | |
816 | } | |
817 | ||
818 | out: | |
819 | return ret; | |
820 | } | |
821 | ||
822 | /** | |
a84040ab | 823 | * eficonfig_show_file_selection() - construct the file selection menu |
87d79142 MK |
824 | * |
825 | * @file_info: pointer to the file selection structure | |
826 | * @root: pointer to the file handle | |
827 | * Return: status code | |
828 | */ | |
a84040ab MK |
829 | static efi_status_t eficonfig_show_file_selection(struct eficonfig_select_file_info *file_info, |
830 | struct efi_file_handle *root) | |
87d79142 MK |
831 | { |
832 | u32 count = 0, i; | |
833 | efi_uintn_t len; | |
834 | efi_status_t ret; | |
835 | struct efimenu *efi_menu; | |
836 | struct efi_file_handle *f; | |
837 | struct efi_file_info *buf; | |
838 | struct eficonfig_file_entry_data **tmp_infos; | |
839 | ||
840 | buf = calloc(1, sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE); | |
841 | if (!buf) | |
842 | return EFI_OUT_OF_RESOURCES; | |
843 | ||
844 | while (!file_info->file_selected) { | |
845 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
846 | if (!efi_menu) { | |
847 | ret = EFI_OUT_OF_RESOURCES; | |
848 | goto out; | |
849 | } | |
850 | INIT_LIST_HEAD(&efi_menu->list); | |
851 | ||
21faf4ef MK |
852 | ret = EFI_CALL(root->open(root, &f, file_info->current_path, |
853 | EFI_FILE_MODE_READ, 0)); | |
87d79142 MK |
854 | if (ret != EFI_SUCCESS) { |
855 | eficonfig_print_msg("Reading volume failed!"); | |
856 | free(efi_menu); | |
857 | ret = EFI_ABORTED; | |
858 | goto out; | |
859 | } | |
860 | ||
861 | /* Count the number of directory entries */ | |
862 | for (;;) { | |
863 | len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE; | |
21faf4ef | 864 | ret = EFI_CALL(f->read(f, &len, buf)); |
87d79142 MK |
865 | if (ret != EFI_SUCCESS || len == 0) |
866 | break; | |
867 | ||
868 | count++; | |
869 | } | |
870 | ||
871 | /* allocate array to sort the entry */ | |
872 | tmp_infos = calloc(count, sizeof(*tmp_infos)); | |
873 | if (!tmp_infos) { | |
874 | ret = EFI_OUT_OF_RESOURCES; | |
875 | goto err; | |
876 | } | |
877 | ||
878 | ret = eficonfig_create_file_entry(efi_menu, count, tmp_infos, | |
879 | f, buf, file_info); | |
880 | if (ret != EFI_SUCCESS) | |
881 | goto err; | |
882 | ||
8961e93e | 883 | ret = eficonfig_append_quit_entry(efi_menu); |
87d79142 MK |
884 | if (ret != EFI_SUCCESS) |
885 | goto err; | |
886 | ||
cd160b27 MK |
887 | ret = eficonfig_process_common(efi_menu, " ** Select File **", |
888 | eficonfig_menu_desc, | |
889 | eficonfig_display_statusline, | |
890 | eficonfig_print_entry, | |
891 | eficonfig_choice_entry); | |
87d79142 | 892 | err: |
21faf4ef | 893 | EFI_CALL(f->close(f)); |
87d79142 MK |
894 | eficonfig_destroy(efi_menu); |
895 | ||
896 | if (tmp_infos) { | |
897 | for (i = 0; i < count; i++) | |
898 | free(tmp_infos[i]); | |
899 | } | |
900 | ||
901 | free(tmp_infos); | |
902 | ||
903 | if (ret != EFI_SUCCESS) | |
904 | break; | |
905 | } | |
906 | ||
907 | out: | |
908 | free(buf); | |
909 | ||
910 | return ret; | |
911 | } | |
912 | ||
913 | /** | |
914 | * handle_user_input() - handle user input | |
915 | * | |
916 | * @buf: pointer to the buffer | |
917 | * @buf_size: size of the buffer | |
918 | * @cursor_col: cursor column for user input | |
919 | * @msg: pointer to the string to display | |
920 | * Return: status code | |
921 | */ | |
922 | static efi_status_t handle_user_input(u16 *buf, int buf_size, | |
923 | int cursor_col, char *msg) | |
924 | { | |
925 | u16 *tmp; | |
926 | efi_status_t ret; | |
927 | ||
928 | printf(ANSI_CLEAR_CONSOLE | |
929 | ANSI_CURSOR_POSITION | |
930 | "%s" | |
931 | ANSI_CURSOR_POSITION | |
45f5319f | 932 | " Press ENTER to complete, ESC to quit", |
87d79142 MK |
933 | 0, 1, msg, 8, 1); |
934 | ||
935 | /* tmp is used to accept user cancel */ | |
936 | tmp = calloc(1, buf_size * sizeof(u16)); | |
937 | if (!tmp) | |
938 | return EFI_OUT_OF_RESOURCES; | |
939 | ||
940 | ret = efi_console_get_u16_string(cin, tmp, buf_size, NULL, 4, cursor_col); | |
941 | if (ret == EFI_SUCCESS) | |
942 | u16_strcpy(buf, tmp); | |
943 | ||
944 | free(tmp); | |
945 | ||
946 | /* to stay the parent menu */ | |
947 | ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; | |
948 | ||
949 | return ret; | |
950 | } | |
951 | ||
952 | /** | |
953 | * eficonfig_boot_add_enter_description() - handle user input for description | |
954 | * | |
955 | * @data: pointer to the internal boot option structure | |
956 | * Return: status code | |
957 | */ | |
958 | static efi_status_t eficonfig_boot_add_enter_description(void *data) | |
959 | { | |
960 | struct eficonfig_boot_option *bo = data; | |
961 | ||
962 | return handle_user_input(bo->description, EFICONFIG_DESCRIPTION_MAX, 22, | |
963 | "\n ** Edit Description **\n" | |
964 | "\n" | |
965 | " enter description: "); | |
966 | } | |
967 | ||
968 | /** | |
969 | * eficonfig_boot_add_optional_data() - handle user input for optional data | |
970 | * | |
971 | * @data: pointer to the internal boot option structure | |
972 | * Return: status code | |
973 | */ | |
974 | static efi_status_t eficonfig_boot_add_optional_data(void *data) | |
975 | { | |
976 | struct eficonfig_boot_option *bo = data; | |
977 | ||
978 | return handle_user_input(bo->optional_data, EFICONFIG_OPTIONAL_DATA_MAX, 24, | |
979 | "\n ** Edit Optional Data **\n" | |
980 | "\n" | |
981 | " enter optional data:"); | |
982 | } | |
983 | ||
984 | /** | |
985 | * eficonfig_boot_edit_save() - handler to save the boot option | |
986 | * | |
987 | * @data: pointer to the internal boot option structure | |
988 | * Return: status code | |
989 | */ | |
990 | static efi_status_t eficonfig_boot_edit_save(void *data) | |
991 | { | |
992 | struct eficonfig_boot_option *bo = data; | |
993 | ||
994 | if (u16_strlen(bo->description) == 0) { | |
995 | eficonfig_print_msg("Boot Description is empty!"); | |
996 | bo->edit_completed = false; | |
997 | return EFI_NOT_READY; | |
998 | } | |
999 | if (u16_strlen(bo->file_info.current_path) == 0) { | |
1000 | eficonfig_print_msg("File is not selected!"); | |
1001 | bo->edit_completed = false; | |
1002 | return EFI_NOT_READY; | |
1003 | } | |
1004 | ||
1005 | bo->edit_completed = true; | |
1006 | ||
1007 | return EFI_SUCCESS; | |
1008 | } | |
1009 | ||
87d79142 MK |
1010 | /** |
1011 | * eficonfig_process_clear_file_selection() - callback function for "Clear" entry | |
1012 | * | |
1013 | * @data: pointer to the data | |
1014 | * Return: status code | |
1015 | */ | |
1016 | efi_status_t eficonfig_process_clear_file_selection(void *data) | |
1017 | { | |
1018 | struct eficonfig_select_file_info *file_info = data; | |
1019 | ||
1020 | /* clear the existing file information */ | |
1021 | file_info->current_volume = NULL; | |
1022 | file_info->current_path[0] = u'\0'; | |
1023 | file_info->dp_volume = NULL; | |
1024 | ||
1025 | return EFI_ABORTED; | |
1026 | } | |
1027 | ||
1028 | static struct eficonfig_item select_file_menu_items[] = { | |
1029 | {"Select File", eficonfig_process_select_file}, | |
1030 | {"Clear", eficonfig_process_clear_file_selection}, | |
1031 | {"Quit", eficonfig_process_quit}, | |
1032 | }; | |
1033 | ||
87d79142 | 1034 | /** |
a84040ab | 1035 | * eficonfig_process_show_file_option() - display select file option |
87d79142 MK |
1036 | * |
1037 | * @file_info: pointer to the file information structure | |
1038 | * Return: status code | |
1039 | */ | |
a84040ab | 1040 | efi_status_t eficonfig_process_show_file_option(void *data) |
87d79142 MK |
1041 | { |
1042 | efi_status_t ret; | |
1043 | struct efimenu *efi_menu; | |
1044 | ||
a84040ab MK |
1045 | select_file_menu_items[0].data = data; |
1046 | select_file_menu_items[1].data = data; | |
87d79142 MK |
1047 | efi_menu = eficonfig_create_fixed_menu(select_file_menu_items, |
1048 | ARRAY_SIZE(select_file_menu_items)); | |
1049 | if (!efi_menu) | |
1050 | return EFI_OUT_OF_RESOURCES; | |
1051 | ||
cd160b27 MK |
1052 | ret = eficonfig_process_common(efi_menu, " ** Update File **", |
1053 | eficonfig_menu_desc, | |
1054 | eficonfig_display_statusline, | |
1055 | eficonfig_print_entry, | |
1056 | eficonfig_choice_entry); | |
87d79142 MK |
1057 | if (ret != EFI_SUCCESS) /* User selects "Clear" or "Quit" */ |
1058 | ret = EFI_NOT_READY; | |
1059 | ||
1060 | eficonfig_destroy(efi_menu); | |
1061 | ||
1062 | return ret; | |
1063 | } | |
1064 | ||
1065 | /** | |
a84040ab | 1066 | * eficonfig_process_select_file() - handle user file selection |
87d79142 MK |
1067 | * |
1068 | * @data: pointer to the data | |
1069 | * Return: status code | |
1070 | */ | |
a84040ab | 1071 | efi_status_t eficonfig_process_select_file(void *data) |
87d79142 MK |
1072 | { |
1073 | size_t len; | |
1074 | efi_status_t ret; | |
1075 | struct list_head *pos, *n; | |
1076 | struct efi_file_handle *root; | |
1077 | struct eficonfig_filepath_info *item; | |
1078 | struct eficonfig_select_file_info *tmp = NULL; | |
1079 | struct eficonfig_select_file_info *file_info = data; | |
1080 | ||
87d79142 MK |
1081 | tmp = calloc(1, sizeof(struct eficonfig_select_file_info)); |
1082 | if (!tmp) | |
1083 | return EFI_OUT_OF_RESOURCES; | |
1084 | ||
1085 | tmp->current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE); | |
1086 | if (!tmp->current_path) { | |
1087 | free(tmp); | |
1088 | return EFI_OUT_OF_RESOURCES; | |
1089 | } | |
1090 | INIT_LIST_HEAD(&tmp->filepath_list); | |
1091 | ||
1092 | while (!tmp->file_selected) { | |
1093 | tmp->current_volume = NULL; | |
1094 | memset(tmp->current_path, 0, EFICONFIG_FILE_PATH_BUF_SIZE); | |
1095 | ||
1096 | ret = eficonfig_select_volume(tmp); | |
1097 | if (ret != EFI_SUCCESS) | |
1098 | goto out; | |
1099 | ||
1100 | if (!tmp->current_volume) | |
1101 | return EFI_INVALID_PARAMETER; | |
1102 | ||
21faf4ef | 1103 | ret = EFI_CALL(tmp->current_volume->open_volume(tmp->current_volume, &root)); |
87d79142 MK |
1104 | if (ret != EFI_SUCCESS) |
1105 | goto out; | |
1106 | ||
a84040ab | 1107 | ret = eficonfig_show_file_selection(tmp, root); |
87d79142 MK |
1108 | if (ret == EFI_ABORTED) |
1109 | continue; | |
1110 | if (ret != EFI_SUCCESS) | |
1111 | goto out; | |
1112 | } | |
1113 | ||
1114 | out: | |
1115 | if (ret == EFI_SUCCESS) { | |
1116 | len = u16_strlen(tmp->current_path); | |
1117 | len = (len >= EFICONFIG_FILE_PATH_MAX) ? (EFICONFIG_FILE_PATH_MAX - 1) : len; | |
1118 | memcpy(file_info->current_path, tmp->current_path, len * sizeof(u16)); | |
1119 | file_info->current_path[len] = u'\0'; | |
1120 | file_info->current_volume = tmp->current_volume; | |
1121 | file_info->dp_volume = tmp->dp_volume; | |
1122 | } | |
1123 | ||
1124 | list_for_each_safe(pos, n, &tmp->filepath_list) { | |
1125 | item = list_entry(pos, struct eficonfig_filepath_info, list); | |
1126 | list_del(&item->list); | |
1127 | free(item->name); | |
1128 | free(item); | |
1129 | } | |
1130 | free(tmp->current_path); | |
1131 | free(tmp); | |
1132 | ||
1133 | /* to stay the parent menu */ | |
1134 | ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; | |
1135 | ||
1136 | return ret; | |
1137 | } | |
1138 | ||
87d79142 MK |
1139 | /** |
1140 | * eficonfig_set_boot_option() - set boot option | |
1141 | * | |
1142 | * @varname: pointer to variable name | |
1143 | * @dp: pointer to device path | |
1144 | * @label: pointer to label string | |
1145 | * @optional_data: pointer to optional data | |
1146 | * Return: status code | |
1147 | */ | |
1148 | static efi_status_t eficonfig_set_boot_option(u16 *varname, struct efi_device_path *dp, | |
1149 | efi_uintn_t dp_size, u16 *label, char *optional_data) | |
1150 | { | |
1151 | void *p = NULL; | |
1152 | efi_status_t ret; | |
1153 | efi_uintn_t size; | |
1154 | struct efi_load_option lo; | |
1155 | ||
1156 | lo.file_path = dp; | |
1157 | lo.file_path_length = dp_size; | |
1158 | lo.attributes = LOAD_OPTION_ACTIVE; | |
1159 | lo.optional_data = optional_data; | |
1160 | lo.label = label; | |
1161 | ||
1162 | size = efi_serialize_load_option(&lo, (u8 **)&p); | |
1163 | if (!size) | |
1164 | return EFI_INVALID_PARAMETER; | |
1165 | ||
1166 | ret = efi_set_variable_int(varname, &efi_global_variable_guid, | |
1167 | EFI_VARIABLE_NON_VOLATILE | | |
1168 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
1169 | EFI_VARIABLE_RUNTIME_ACCESS, | |
1170 | size, p, false); | |
1171 | free(p); | |
1172 | ||
1173 | return ret; | |
1174 | } | |
1175 | ||
87d79142 MK |
1176 | /** |
1177 | * create_boot_option_entry() - create boot option entry | |
1178 | * | |
1179 | * @efi_menu: pointer to the efimenu structure | |
1180 | * @title: pointer to the entry title | |
1181 | * @val: pointer to boot option label | |
1182 | * @func: callback of each entry | |
1183 | * @data: pointer to the data to be passed to each entry callback | |
1184 | * Return: status code | |
1185 | */ | |
1186 | static efi_status_t create_boot_option_entry(struct efimenu *efi_menu, char *title, u16 *val, | |
1187 | eficonfig_entry_func func, void *data) | |
1188 | { | |
1189 | u32 len; | |
1190 | char *p, *buf; | |
1191 | ||
1192 | len = strlen(title) + 1; | |
1193 | if (val) | |
1194 | len += utf16_utf8_strlen(val); | |
1195 | buf = calloc(1, len); | |
1196 | if (!buf) | |
1197 | return EFI_OUT_OF_RESOURCES; | |
1198 | ||
1199 | strcpy(buf, title); | |
1200 | if (val) { | |
1201 | p = buf + strlen(title); | |
1202 | utf16_utf8_strcpy(&p, val); | |
1203 | } | |
1204 | ||
8961e93e | 1205 | return eficonfig_append_menu_entry(efi_menu, buf, func, data); |
87d79142 MK |
1206 | } |
1207 | ||
1208 | /** | |
1209 | * prepare_file_selection_entry() - prepare file selection entry | |
1210 | * | |
1211 | * @efi_menu: pointer to the efimenu structure | |
1212 | * @title: pointer to the title string | |
1213 | * @file_info: pointer to the file info | |
1214 | * Return: status code | |
1215 | */ | |
1216 | static efi_status_t prepare_file_selection_entry(struct efimenu *efi_menu, char *title, | |
1217 | struct eficonfig_select_file_info *file_info) | |
1218 | { | |
1219 | u32 len; | |
1220 | efi_status_t ret; | |
e34158bc | 1221 | u16 *file_name = NULL, *p; |
87d79142 | 1222 | efi_handle_t handle; |
e34158bc MK |
1223 | char *devname; |
1224 | ||
1225 | devname = calloc(1, EFICONFIG_VOLUME_PATH_MAX + 1); | |
1226 | if (!devname) | |
1227 | return EFI_OUT_OF_RESOURCES; | |
87d79142 MK |
1228 | |
1229 | /* get the device name only when the user already selected the file path */ | |
1230 | handle = efi_dp_find_obj(file_info->dp_volume, NULL, NULL); | |
1231 | if (handle) { | |
e34158bc | 1232 | ret = efi_disk_get_device_name(handle, devname, EFICONFIG_VOLUME_PATH_MAX); |
87d79142 | 1233 | if (ret != EFI_SUCCESS) |
e34158bc MK |
1234 | goto out; |
1235 | } | |
1236 | ||
1237 | /* | |
1238 | * If the preconfigured volume does not exist in the system, display the text | |
1239 | * converted volume device path instead of U-Boot friendly name(e.g. "usb 0:1"). | |
1240 | */ | |
1241 | if (!handle && file_info->dp_volume) { | |
1242 | u16 *dp_str; | |
1243 | char *q = devname; | |
1244 | ||
1245 | dp_str = efi_dp_str(file_info->dp_volume); | |
1246 | if (dp_str) | |
1247 | utf16_utf8_strncpy(&q, dp_str, EFICONFIG_VOLUME_PATH_MAX); | |
1248 | ||
1249 | efi_free_pool(dp_str); | |
87d79142 MK |
1250 | } |
1251 | ||
1252 | /* append u'/' to devname, it is just for display purpose. */ | |
1253 | if (file_info->current_path[0] != u'\0' && file_info->current_path[0] != u'/') | |
e34158bc | 1254 | strlcat(devname, "/", EFICONFIG_VOLUME_PATH_MAX + 1); |
87d79142 MK |
1255 | |
1256 | len = strlen(devname); | |
1257 | len += utf16_utf8_strlen(file_info->current_path) + 1; | |
1258 | file_name = calloc(1, len * sizeof(u16)); | |
e34158bc MK |
1259 | if (!file_name) { |
1260 | ret = EFI_OUT_OF_RESOURCES; | |
1261 | goto out; | |
1262 | } | |
87d79142 MK |
1263 | |
1264 | p = file_name; | |
1265 | utf8_utf16_strcpy(&p, devname); | |
1266 | u16_strlcat(file_name, file_info->current_path, len); | |
1267 | ret = create_boot_option_entry(efi_menu, title, file_name, | |
a84040ab | 1268 | eficonfig_process_show_file_option, file_info); |
e34158bc MK |
1269 | out: |
1270 | free(devname); | |
87d79142 | 1271 | free(file_name); |
e34158bc | 1272 | |
87d79142 MK |
1273 | return ret; |
1274 | } | |
1275 | ||
1276 | /** | |
1277 | * eficonfig_show_boot_option() - prepare menu entry for editing boot option | |
1278 | * | |
1279 | * Construct the structures to create edit boot option menu | |
1280 | * | |
1281 | * @bo: pointer to the boot option | |
1282 | * @header_str: pointer to the header string | |
1283 | * Return: status code | |
1284 | */ | |
1285 | static efi_status_t eficonfig_show_boot_option(struct eficonfig_boot_option *bo, | |
1286 | char *header_str) | |
1287 | { | |
1288 | efi_status_t ret; | |
1289 | struct efimenu *efi_menu; | |
1290 | ||
1291 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
1292 | if (!efi_menu) | |
1293 | return EFI_OUT_OF_RESOURCES; | |
1294 | ||
1295 | INIT_LIST_HEAD(&efi_menu->list); | |
1296 | ||
1297 | ret = create_boot_option_entry(efi_menu, "Description: ", bo->description, | |
1298 | eficonfig_boot_add_enter_description, bo); | |
1299 | if (ret != EFI_SUCCESS) | |
1300 | goto out; | |
1301 | ||
1302 | ret = prepare_file_selection_entry(efi_menu, "File: ", &bo->file_info); | |
1303 | if (ret != EFI_SUCCESS) | |
1304 | goto out; | |
1305 | ||
1306 | ret = prepare_file_selection_entry(efi_menu, "Initrd File: ", &bo->initrd_info); | |
1307 | if (ret != EFI_SUCCESS) | |
1308 | goto out; | |
1309 | ||
1310 | ret = create_boot_option_entry(efi_menu, "Optional Data: ", bo->optional_data, | |
1311 | eficonfig_boot_add_optional_data, bo); | |
1312 | if (ret != EFI_SUCCESS) | |
1313 | goto out; | |
1314 | ||
1315 | ret = create_boot_option_entry(efi_menu, "Save", NULL, | |
1316 | eficonfig_boot_edit_save, bo); | |
1317 | if (ret != EFI_SUCCESS) | |
1318 | goto out; | |
1319 | ||
1320 | ret = create_boot_option_entry(efi_menu, "Quit", NULL, | |
1321 | eficonfig_process_quit, NULL); | |
1322 | if (ret != EFI_SUCCESS) | |
1323 | goto out; | |
1324 | ||
cd160b27 MK |
1325 | ret = eficonfig_process_common(efi_menu, header_str, |
1326 | eficonfig_menu_desc, | |
1327 | eficonfig_display_statusline, | |
1328 | eficonfig_print_entry, | |
1329 | eficonfig_choice_entry); | |
1330 | ||
87d79142 MK |
1331 | out: |
1332 | eficonfig_destroy(efi_menu); | |
1333 | ||
1334 | return ret; | |
1335 | } | |
1336 | ||
1337 | /** | |
1338 | * fill_file_info() - fill the file info from efi_device_path structure | |
1339 | * | |
1340 | * @dp: pointer to the device path | |
1341 | * @file_info: pointer to the file info structure | |
1342 | * @device_dp: pointer to the volume device path | |
1343 | */ | |
1344 | static void fill_file_info(struct efi_device_path *dp, | |
1345 | struct eficonfig_select_file_info *file_info, | |
1346 | struct efi_device_path *device_dp) | |
1347 | { | |
1348 | u16 *file_str, *p; | |
1349 | struct efi_device_path *file_dp = NULL; | |
1350 | ||
1351 | efi_dp_split_file_path(dp, &device_dp, &file_dp); | |
1352 | file_info->dp_volume = device_dp; | |
1353 | ||
1354 | if (file_dp) { | |
1355 | file_str = efi_dp_str(file_dp); | |
1356 | /* | |
1357 | * efi_convert_device_path_to_text() automatically adds u'/' at the | |
1358 | * beginning of file name, remove u'/' before copying to current_path | |
1359 | */ | |
1360 | p = file_str; | |
1361 | if (p[0] == u'/') | |
1362 | p++; | |
1363 | ||
1364 | u16_strcpy(file_info->current_path, p); | |
1365 | efi_free_pool(file_dp); | |
1366 | efi_free_pool(file_str); | |
1367 | } | |
1368 | } | |
1369 | ||
1370 | /** | |
1371 | * eficonfig_edit_boot_option() - prepare boot option structure for editing | |
1372 | * | |
1373 | * Construct the boot option structure and copy the existing value | |
1374 | * | |
1375 | * @varname: pointer to the UEFI variable name | |
1376 | * @bo: pointer to the boot option | |
1377 | * @load_option: pointer to the load option | |
1378 | * @load_option_size: size of the load option | |
1379 | * @header_str: pointer to the header string | |
1380 | * Return : status code | |
1381 | */ | |
1382 | static efi_status_t eficonfig_edit_boot_option(u16 *varname, struct eficonfig_boot_option *bo, | |
1383 | void *load_option, efi_uintn_t load_option_size, | |
1384 | char *header_str) | |
1385 | { | |
1386 | size_t len; | |
1387 | efi_status_t ret; | |
1388 | char *tmp = NULL, *p; | |
1389 | struct efi_load_option lo = {0}; | |
1390 | efi_uintn_t final_dp_size; | |
1391 | struct efi_device_path *dp = NULL; | |
1392 | efi_uintn_t size = load_option_size; | |
1393 | struct efi_device_path *final_dp = NULL; | |
1394 | struct efi_device_path *device_dp = NULL; | |
1395 | struct efi_device_path *initrd_dp = NULL; | |
1396 | struct efi_device_path *initrd_device_dp = NULL; | |
1397 | ||
1398 | const struct efi_initrd_dp id_dp = { | |
1399 | .vendor = { | |
1400 | { | |
1401 | DEVICE_PATH_TYPE_MEDIA_DEVICE, | |
1402 | DEVICE_PATH_SUB_TYPE_VENDOR_PATH, | |
1403 | sizeof(id_dp.vendor), | |
1404 | }, | |
1405 | EFI_INITRD_MEDIA_GUID, | |
1406 | }, | |
1407 | .end = { | |
1408 | DEVICE_PATH_TYPE_END, | |
1409 | DEVICE_PATH_SUB_TYPE_END, | |
1410 | sizeof(id_dp.end), | |
1411 | } | |
1412 | }; | |
1413 | ||
1414 | bo->file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE); | |
1415 | if (!bo->file_info.current_path) { | |
1416 | ret = EFI_OUT_OF_RESOURCES; | |
1417 | goto out; | |
1418 | } | |
1419 | ||
1420 | bo->initrd_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE); | |
8afeab4c | 1421 | if (!bo->initrd_info.current_path) { |
87d79142 MK |
1422 | ret = EFI_OUT_OF_RESOURCES; |
1423 | goto out; | |
1424 | } | |
1425 | ||
1426 | bo->description = calloc(1, EFICONFIG_DESCRIPTION_MAX * sizeof(u16)); | |
1427 | if (!bo->description) { | |
1428 | ret = EFI_OUT_OF_RESOURCES; | |
1429 | goto out; | |
1430 | } | |
1431 | ||
1432 | bo->optional_data = calloc(1, EFICONFIG_OPTIONAL_DATA_MAX * sizeof(u16)); | |
1433 | if (!bo->optional_data) { | |
1434 | ret = EFI_OUT_OF_RESOURCES; | |
1435 | goto out; | |
1436 | } | |
1437 | ||
1438 | /* copy the preset value */ | |
1439 | if (load_option) { | |
1440 | ret = efi_deserialize_load_option(&lo, load_option, &size); | |
1441 | if (ret != EFI_SUCCESS) | |
1442 | goto out; | |
1443 | ||
e34158bc | 1444 | if (!lo.label) { |
87d79142 MK |
1445 | ret = EFI_INVALID_PARAMETER; |
1446 | goto out; | |
1447 | } | |
e34158bc MK |
1448 | /* truncate the long label string */ |
1449 | if (u16_strlen(lo.label) >= EFICONFIG_DESCRIPTION_MAX) | |
1450 | lo.label[EFICONFIG_DESCRIPTION_MAX - 1] = u'\0'; | |
1451 | ||
87d79142 MK |
1452 | u16_strcpy(bo->description, lo.label); |
1453 | ||
1454 | /* EFI image file path is a first instance */ | |
1455 | if (lo.file_path) | |
1456 | fill_file_info(lo.file_path, &bo->file_info, device_dp); | |
1457 | ||
1458 | /* Initrd file path(optional) is placed at second instance. */ | |
1459 | initrd_dp = efi_dp_from_lo(&lo, &efi_lf2_initrd_guid); | |
1460 | if (initrd_dp) { | |
1461 | fill_file_info(initrd_dp, &bo->initrd_info, initrd_device_dp); | |
1462 | efi_free_pool(initrd_dp); | |
1463 | } | |
1464 | ||
1465 | if (size > 0) | |
1466 | memcpy(bo->optional_data, lo.optional_data, size); | |
1467 | } | |
1468 | ||
1469 | while (1) { | |
1470 | ret = eficonfig_show_boot_option(bo, header_str); | |
1471 | if (ret == EFI_SUCCESS && bo->edit_completed) | |
1472 | break; | |
1473 | if (ret == EFI_NOT_READY) | |
1474 | continue; | |
1475 | if (ret != EFI_SUCCESS) | |
1476 | goto out; | |
1477 | } | |
1478 | ||
1479 | if (bo->initrd_info.dp_volume) { | |
d6566113 MK |
1480 | dp = eficonfig_create_device_path(bo->initrd_info.dp_volume, |
1481 | bo->initrd_info.current_path); | |
87d79142 MK |
1482 | if (!dp) { |
1483 | ret = EFI_OUT_OF_RESOURCES; | |
1484 | goto out; | |
1485 | } | |
f19171c9 IA |
1486 | initrd_dp = efi_dp_concat((const struct efi_device_path *)&id_dp, |
1487 | dp, false); | |
87d79142 MK |
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) { | |
f19171c9 | 1498 | final_dp = efi_dp_concat(dp, initrd_dp, true); |
87d79142 MK |
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 | ); |