]>
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; | |
58bef195 | 64 | struct eficonfig_select_file_info fdt_info; |
87d79142 MK |
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 | 445 | menu = menu_create(NULL, 0, 1, display_statusline, item_data_print, |
ccdd7948 | 446 | item_choice, NULL, 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; | |
bf03333d | 534 | dp = efi_dp_concat(dp, &fp->dp, 0); |
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" | |
a01e7d25 | 966 | " Enter description: "); |
87d79142 MK |
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 | ||
58bef195 HS |
1311 | ret = prepare_file_selection_entry(efi_menu, "Fdt File: ", &bo->fdt_info); |
1312 | if (ret != EFI_SUCCESS) | |
1313 | goto out; | |
1314 | ||
87d79142 MK |
1315 | ret = create_boot_option_entry(efi_menu, "Optional Data: ", bo->optional_data, |
1316 | eficonfig_boot_add_optional_data, bo); | |
1317 | if (ret != EFI_SUCCESS) | |
1318 | goto out; | |
1319 | ||
1320 | ret = create_boot_option_entry(efi_menu, "Save", NULL, | |
1321 | eficonfig_boot_edit_save, bo); | |
1322 | if (ret != EFI_SUCCESS) | |
1323 | goto out; | |
1324 | ||
1325 | ret = create_boot_option_entry(efi_menu, "Quit", NULL, | |
1326 | eficonfig_process_quit, NULL); | |
1327 | if (ret != EFI_SUCCESS) | |
1328 | goto out; | |
1329 | ||
cd160b27 MK |
1330 | ret = eficonfig_process_common(efi_menu, header_str, |
1331 | eficonfig_menu_desc, | |
1332 | eficonfig_display_statusline, | |
1333 | eficonfig_print_entry, | |
1334 | eficonfig_choice_entry); | |
1335 | ||
87d79142 MK |
1336 | out: |
1337 | eficonfig_destroy(efi_menu); | |
1338 | ||
1339 | return ret; | |
1340 | } | |
1341 | ||
1342 | /** | |
1343 | * fill_file_info() - fill the file info from efi_device_path structure | |
1344 | * | |
1345 | * @dp: pointer to the device path | |
1346 | * @file_info: pointer to the file info structure | |
1347 | * @device_dp: pointer to the volume device path | |
1348 | */ | |
1349 | static void fill_file_info(struct efi_device_path *dp, | |
1350 | struct eficonfig_select_file_info *file_info, | |
1351 | struct efi_device_path *device_dp) | |
1352 | { | |
1353 | u16 *file_str, *p; | |
1354 | struct efi_device_path *file_dp = NULL; | |
1355 | ||
1356 | efi_dp_split_file_path(dp, &device_dp, &file_dp); | |
1357 | file_info->dp_volume = device_dp; | |
1358 | ||
1359 | if (file_dp) { | |
1360 | file_str = efi_dp_str(file_dp); | |
1361 | /* | |
1362 | * efi_convert_device_path_to_text() automatically adds u'/' at the | |
1363 | * beginning of file name, remove u'/' before copying to current_path | |
1364 | */ | |
1365 | p = file_str; | |
1366 | if (p[0] == u'/') | |
1367 | p++; | |
1368 | ||
1369 | u16_strcpy(file_info->current_path, p); | |
1370 | efi_free_pool(file_dp); | |
1371 | efi_free_pool(file_str); | |
1372 | } | |
1373 | } | |
1374 | ||
1375 | /** | |
1376 | * eficonfig_edit_boot_option() - prepare boot option structure for editing | |
1377 | * | |
1378 | * Construct the boot option structure and copy the existing value | |
1379 | * | |
1380 | * @varname: pointer to the UEFI variable name | |
1381 | * @bo: pointer to the boot option | |
1382 | * @load_option: pointer to the load option | |
1383 | * @load_option_size: size of the load option | |
1384 | * @header_str: pointer to the header string | |
1385 | * Return : status code | |
1386 | */ | |
1387 | static efi_status_t eficonfig_edit_boot_option(u16 *varname, struct eficonfig_boot_option *bo, | |
1388 | void *load_option, efi_uintn_t load_option_size, | |
1389 | char *header_str) | |
1390 | { | |
1391 | size_t len; | |
1392 | efi_status_t ret; | |
1393 | char *tmp = NULL, *p; | |
1394 | struct efi_load_option lo = {0}; | |
58bef195 | 1395 | efi_uintn_t dp_size; |
87d79142 MK |
1396 | struct efi_device_path *dp = NULL; |
1397 | efi_uintn_t size = load_option_size; | |
87d79142 MK |
1398 | struct efi_device_path *device_dp = NULL; |
1399 | struct efi_device_path *initrd_dp = NULL; | |
58bef195 | 1400 | struct efi_device_path *fdt_dp = NULL; |
87d79142 | 1401 | struct efi_device_path *initrd_device_dp = NULL; |
58bef195 | 1402 | struct efi_device_path *fdt_device_dp = NULL; |
87d79142 | 1403 | |
535321c2 | 1404 | const struct efi_lo_dp_prefix initrd_prefix = { |
87d79142 MK |
1405 | .vendor = { |
1406 | { | |
1407 | DEVICE_PATH_TYPE_MEDIA_DEVICE, | |
1408 | DEVICE_PATH_SUB_TYPE_VENDOR_PATH, | |
58bef195 | 1409 | sizeof(initrd_prefix.vendor), |
87d79142 MK |
1410 | }, |
1411 | EFI_INITRD_MEDIA_GUID, | |
1412 | }, | |
1413 | .end = { | |
1414 | DEVICE_PATH_TYPE_END, | |
1415 | DEVICE_PATH_SUB_TYPE_END, | |
58bef195 HS |
1416 | sizeof(initrd_prefix.end), |
1417 | } | |
1418 | }; | |
1419 | ||
535321c2 | 1420 | const struct efi_lo_dp_prefix fdt_prefix = { |
58bef195 HS |
1421 | .vendor = { |
1422 | { | |
1423 | DEVICE_PATH_TYPE_MEDIA_DEVICE, | |
1424 | DEVICE_PATH_SUB_TYPE_VENDOR_PATH, | |
1425 | sizeof(fdt_prefix.vendor), | |
1426 | }, | |
1427 | EFI_FDT_GUID, | |
1428 | }, | |
1429 | .end = { | |
1430 | DEVICE_PATH_TYPE_END, | |
1431 | DEVICE_PATH_SUB_TYPE_END, | |
1432 | sizeof(initrd_prefix.end), | |
87d79142 MK |
1433 | } |
1434 | }; | |
1435 | ||
1436 | bo->file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE); | |
1437 | if (!bo->file_info.current_path) { | |
1438 | ret = EFI_OUT_OF_RESOURCES; | |
1439 | goto out; | |
1440 | } | |
1441 | ||
1442 | bo->initrd_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE); | |
8afeab4c | 1443 | if (!bo->initrd_info.current_path) { |
87d79142 MK |
1444 | ret = EFI_OUT_OF_RESOURCES; |
1445 | goto out; | |
1446 | } | |
1447 | ||
58bef195 HS |
1448 | bo->fdt_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE); |
1449 | if (!bo->fdt_info.current_path) { | |
1450 | ret = EFI_OUT_OF_RESOURCES; | |
1451 | goto out; | |
1452 | } | |
1453 | ||
87d79142 MK |
1454 | bo->description = calloc(1, EFICONFIG_DESCRIPTION_MAX * sizeof(u16)); |
1455 | if (!bo->description) { | |
1456 | ret = EFI_OUT_OF_RESOURCES; | |
1457 | goto out; | |
1458 | } | |
1459 | ||
1460 | bo->optional_data = calloc(1, EFICONFIG_OPTIONAL_DATA_MAX * sizeof(u16)); | |
1461 | if (!bo->optional_data) { | |
1462 | ret = EFI_OUT_OF_RESOURCES; | |
1463 | goto out; | |
1464 | } | |
1465 | ||
1466 | /* copy the preset value */ | |
1467 | if (load_option) { | |
1468 | ret = efi_deserialize_load_option(&lo, load_option, &size); | |
1469 | if (ret != EFI_SUCCESS) | |
1470 | goto out; | |
1471 | ||
e34158bc | 1472 | if (!lo.label) { |
87d79142 MK |
1473 | ret = EFI_INVALID_PARAMETER; |
1474 | goto out; | |
1475 | } | |
e34158bc MK |
1476 | /* truncate the long label string */ |
1477 | if (u16_strlen(lo.label) >= EFICONFIG_DESCRIPTION_MAX) | |
1478 | lo.label[EFICONFIG_DESCRIPTION_MAX - 1] = u'\0'; | |
1479 | ||
87d79142 MK |
1480 | u16_strcpy(bo->description, lo.label); |
1481 | ||
1482 | /* EFI image file path is a first instance */ | |
1483 | if (lo.file_path) | |
1484 | fill_file_info(lo.file_path, &bo->file_info, device_dp); | |
1485 | ||
58bef195 | 1486 | /* Initrd file path (optional) is placed at second instance. */ |
87d79142 MK |
1487 | initrd_dp = efi_dp_from_lo(&lo, &efi_lf2_initrd_guid); |
1488 | if (initrd_dp) { | |
1489 | fill_file_info(initrd_dp, &bo->initrd_info, initrd_device_dp); | |
1490 | efi_free_pool(initrd_dp); | |
1491 | } | |
1492 | ||
58bef195 HS |
1493 | /* Fdt file path (optional) is placed as third instance. */ |
1494 | fdt_dp = efi_dp_from_lo(&lo, &efi_guid_fdt); | |
1495 | if (fdt_dp) { | |
1496 | fill_file_info(fdt_dp, &bo->fdt_info, fdt_device_dp); | |
1497 | efi_free_pool(fdt_dp); | |
1498 | } | |
1499 | ||
87d79142 MK |
1500 | if (size > 0) |
1501 | memcpy(bo->optional_data, lo.optional_data, size); | |
1502 | } | |
1503 | ||
1504 | while (1) { | |
1505 | ret = eficonfig_show_boot_option(bo, header_str); | |
1506 | if (ret == EFI_SUCCESS && bo->edit_completed) | |
1507 | break; | |
1508 | if (ret == EFI_NOT_READY) | |
1509 | continue; | |
1510 | if (ret != EFI_SUCCESS) | |
1511 | goto out; | |
1512 | } | |
1513 | ||
1514 | if (bo->initrd_info.dp_volume) { | |
d6566113 MK |
1515 | dp = eficonfig_create_device_path(bo->initrd_info.dp_volume, |
1516 | bo->initrd_info.current_path); | |
87d79142 MK |
1517 | if (!dp) { |
1518 | ret = EFI_OUT_OF_RESOURCES; | |
1519 | goto out; | |
1520 | } | |
58bef195 | 1521 | initrd_dp = efi_dp_concat((const struct efi_device_path *)&initrd_prefix, |
bf03333d | 1522 | dp, 0); |
87d79142 MK |
1523 | efi_free_pool(dp); |
1524 | } | |
1525 | ||
58bef195 HS |
1526 | if (bo->fdt_info.dp_volume) { |
1527 | dp = eficonfig_create_device_path(bo->fdt_info.dp_volume, | |
1528 | bo->fdt_info.current_path); | |
1529 | if (!dp) { | |
1530 | ret = EFI_OUT_OF_RESOURCES; | |
1531 | goto out; | |
1532 | } | |
1533 | fdt_dp = efi_dp_concat((const struct efi_device_path *)&fdt_prefix, | |
1534 | dp, 0); | |
1535 | efi_free_pool(dp); | |
1536 | } | |
1537 | ||
d6566113 | 1538 | dp = eficonfig_create_device_path(bo->file_info.dp_volume, bo->file_info.current_path); |
87d79142 MK |
1539 | if (!dp) { |
1540 | ret = EFI_OUT_OF_RESOURCES; | |
1541 | goto out; | |
1542 | } | |
87d79142 | 1543 | |
58bef195 HS |
1544 | ret = efi_load_option_dp_join(&dp, &dp_size, initrd_dp, fdt_dp); |
1545 | if (ret != EFI_SUCCESS) | |
87d79142 MK |
1546 | goto out; |
1547 | ||
1548 | if (utf16_utf8_strlen(bo->optional_data)) { | |
1549 | len = utf16_utf8_strlen(bo->optional_data) + 1; | |
1550 | tmp = calloc(1, len); | |
1551 | if (!tmp) | |
1552 | goto out; | |
1553 | p = tmp; | |
1554 | utf16_utf8_strncpy(&p, bo->optional_data, u16_strlen(bo->optional_data)); | |
1555 | } | |
1556 | ||
58bef195 | 1557 | ret = eficonfig_set_boot_option(varname, dp, dp_size, bo->description, tmp); |
87d79142 MK |
1558 | out: |
1559 | free(tmp); | |
1560 | free(bo->optional_data); | |
1561 | free(bo->description); | |
1562 | free(bo->file_info.current_path); | |
1563 | free(bo->initrd_info.current_path); | |
58bef195 | 1564 | free(bo->fdt_info.current_path); |
87d79142 MK |
1565 | efi_free_pool(device_dp); |
1566 | efi_free_pool(initrd_device_dp); | |
1567 | efi_free_pool(initrd_dp); | |
58bef195 HS |
1568 | efi_free_pool(fdt_device_dp); |
1569 | efi_free_pool(fdt_dp); | |
1570 | efi_free_pool(dp); | |
87d79142 MK |
1571 | |
1572 | return ret; | |
1573 | } | |
1574 | ||
1575 | /** | |
1576 | * eficonfig_process_add_boot_option() - handler to add boot option | |
1577 | * | |
1578 | * @data: pointer to the data for each entry | |
1579 | * Return: status code | |
1580 | */ | |
1581 | static efi_status_t eficonfig_process_add_boot_option(void *data) | |
1582 | { | |
1583 | u16 varname[9]; | |
1584 | efi_status_t ret; | |
1585 | struct eficonfig_boot_option *bo = NULL; | |
1586 | ||
1587 | bo = calloc(1, sizeof(struct eficonfig_boot_option)); | |
1588 | if (!bo) | |
1589 | return EFI_OUT_OF_RESOURCES; | |
1590 | ||
339b527b | 1591 | ret = efi_bootmgr_get_unused_bootoption(varname, sizeof(varname), &bo->boot_index); |
87d79142 MK |
1592 | if (ret != EFI_SUCCESS) |
1593 | return ret; | |
1594 | ||
1595 | ret = eficonfig_edit_boot_option(varname, bo, NULL, 0, " ** Add Boot Option ** "); | |
1596 | if (ret != EFI_SUCCESS) | |
1597 | goto out; | |
1598 | ||
339b527b | 1599 | ret = efi_bootmgr_append_bootorder((u16)bo->boot_index); |
87d79142 MK |
1600 | if (ret != EFI_SUCCESS) |
1601 | goto out; | |
1602 | ||
1603 | out: | |
1604 | free(bo); | |
1605 | ||
1606 | /* to stay the parent menu */ | |
1607 | ret = (ret == EFI_ABORTED) ? EFI_SUCCESS : ret; | |
1608 | ||
1609 | return ret; | |
1610 | } | |
1611 | ||
e34158bc MK |
1612 | /** |
1613 | * eficonfig_process_boot_selected() - handler to select boot option entry | |
1614 | * | |
1615 | * @data: pointer to the data for each entry | |
1616 | * Return: status code | |
1617 | */ | |
1618 | static efi_status_t eficonfig_process_boot_selected(void *data) | |
1619 | { | |
1620 | struct eficonfig_boot_selection_data *info = data; | |
1621 | ||
1622 | if (info) | |
1623 | *info->selected = info->boot_index; | |
1624 | ||
1625 | return EFI_SUCCESS; | |
1626 | } | |
1627 | ||
e34158bc MK |
1628 | /** |
1629 | * eficonfig_add_boot_selection_entry() - add boot option menu entry | |
1630 | * | |
1631 | * @efi_menu: pointer to store the efimenu structure | |
1632 | * @boot_index: boot option index to be added | |
1633 | * @selected: pointer to store the selected boot option index | |
1634 | * Return: status code | |
1635 | */ | |
1636 | static efi_status_t eficonfig_add_boot_selection_entry(struct efimenu *efi_menu, | |
1637 | unsigned int boot_index, | |
1638 | unsigned int *selected) | |
1639 | { | |
1640 | char *buf, *p; | |
1641 | efi_status_t ret; | |
1642 | efi_uintn_t size; | |
1643 | void *load_option; | |
1644 | struct efi_load_option lo; | |
1645 | u16 varname[] = u"Boot####"; | |
1646 | struct eficonfig_boot_selection_data *info; | |
1647 | ||
1648 | efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index); | |
1649 | load_option = efi_get_var(varname, &efi_global_variable_guid, &size); | |
1650 | if (!load_option) | |
1651 | return EFI_SUCCESS; | |
1652 | ||
1653 | ret = efi_deserialize_load_option(&lo, load_option, &size); | |
1654 | if (ret != EFI_SUCCESS) { | |
1655 | log_warning("Invalid load option for %ls\n", varname); | |
1656 | free(load_option); | |
1657 | return ret; | |
1658 | } | |
1659 | ||
1660 | if (size >= sizeof(efi_guid_t) && | |
1661 | !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) { | |
1662 | /* | |
1663 | * auto generated entry has GUID in optional_data, | |
1664 | * skip auto generated entry because it will be generated | |
1665 | * again even if it is edited or deleted. | |
1666 | */ | |
1667 | free(load_option); | |
1668 | return EFI_SUCCESS; | |
1669 | } | |
1670 | ||
1671 | info = calloc(1, sizeof(struct eficonfig_boot_selection_data)); | |
1672 | if (!info) { | |
1673 | free(load_option); | |
1674 | return EFI_OUT_OF_RESOURCES; | |
1675 | } | |
1676 | ||
1677 | buf = calloc(1, utf16_utf8_strlen(lo.label) + 1); | |
1678 | if (!buf) { | |
1679 | free(load_option); | |
1680 | free(info); | |
1681 | return EFI_OUT_OF_RESOURCES; | |
1682 | } | |
1683 | p = buf; | |
1684 | utf16_utf8_strcpy(&p, lo.label); | |
1685 | info->boot_index = boot_index; | |
1686 | info->selected = selected; | |
8961e93e | 1687 | ret = eficonfig_append_menu_entry(efi_menu, buf, eficonfig_process_boot_selected, info); |
e34158bc MK |
1688 | if (ret != EFI_SUCCESS) { |
1689 | free(load_option); | |
1690 | free(info); | |
1691 | return ret; | |
1692 | } | |
1693 | free(load_option); | |
1694 | ||
1695 | return EFI_SUCCESS; | |
1696 | } | |
1697 | ||
1698 | /** | |
1699 | * eficonfig_show_boot_selection() - construct boot option menu entry | |
1700 | * | |
1701 | * @selected: pointer to store the selected boot option index | |
1702 | * Return: status code | |
1703 | */ | |
1704 | static efi_status_t eficonfig_show_boot_selection(unsigned int *selected) | |
1705 | { | |
1706 | u32 i; | |
1707 | u16 *bootorder; | |
1708 | efi_status_t ret; | |
ce327084 | 1709 | u16 *var_name16 = NULL; |
140a8959 | 1710 | efi_uintn_t num, size, buf_size; |
e34158bc MK |
1711 | struct efimenu *efi_menu; |
1712 | struct list_head *pos, *n; | |
1713 | struct eficonfig_entry *entry; | |
1714 | ||
1715 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
1716 | if (!efi_menu) | |
1717 | return EFI_OUT_OF_RESOURCES; | |
1718 | ||
1719 | bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size); | |
1720 | ||
1721 | INIT_LIST_HEAD(&efi_menu->list); | |
1722 | num = size / sizeof(u16); | |
1723 | /* list the load option in the order of BootOrder variable */ | |
1724 | for (i = 0; i < num; i++) { | |
1725 | ret = eficonfig_add_boot_selection_entry(efi_menu, bootorder[i], selected); | |
1726 | if (ret != EFI_SUCCESS) | |
1727 | goto out; | |
1728 | ||
1729 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1) | |
1730 | break; | |
1731 | } | |
1732 | ||
1733 | /* list the remaining load option not included in the BootOrder */ | |
140a8959 MK |
1734 | buf_size = 128; |
1735 | var_name16 = malloc(buf_size); | |
1736 | if (!var_name16) | |
1737 | return EFI_OUT_OF_RESOURCES; | |
e34158bc | 1738 | |
140a8959 MK |
1739 | var_name16[0] = 0; |
1740 | for (;;) { | |
1741 | int index; | |
1742 | efi_guid_t guid; | |
1743 | ||
ce327084 | 1744 | ret = efi_next_variable_name(&buf_size, &var_name16, &guid); |
140a8959 MK |
1745 | if (ret == EFI_NOT_FOUND) |
1746 | break; | |
ce327084 MK |
1747 | if (ret != EFI_SUCCESS) |
1748 | goto out; | |
1749 | ||
140a8959 MK |
1750 | if (efi_varname_is_load_option(var_name16, &index)) { |
1751 | /* If the index is included in the BootOrder, skip it */ | |
339b527b | 1752 | if (efi_search_bootorder(bootorder, num, index, NULL)) |
140a8959 MK |
1753 | continue; |
1754 | ||
1755 | ret = eficonfig_add_boot_selection_entry(efi_menu, index, selected); | |
1756 | if (ret != EFI_SUCCESS) | |
1757 | goto out; | |
1758 | } | |
e34158bc MK |
1759 | |
1760 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1) | |
1761 | break; | |
1762 | } | |
1763 | ||
8961e93e | 1764 | ret = eficonfig_append_quit_entry(efi_menu); |
e34158bc MK |
1765 | if (ret != EFI_SUCCESS) |
1766 | goto out; | |
1767 | ||
cd160b27 MK |
1768 | ret = eficonfig_process_common(efi_menu, " ** Select Boot Option **", |
1769 | eficonfig_menu_desc, | |
1770 | eficonfig_display_statusline, | |
1771 | eficonfig_print_entry, | |
1772 | eficonfig_choice_entry); | |
e34158bc MK |
1773 | out: |
1774 | list_for_each_safe(pos, n, &efi_menu->list) { | |
1775 | entry = list_entry(pos, struct eficonfig_entry, list); | |
1776 | free(entry->data); | |
1777 | } | |
1778 | eficonfig_destroy(efi_menu); | |
1779 | ||
140a8959 MK |
1780 | free(var_name16); |
1781 | ||
e34158bc MK |
1782 | return ret; |
1783 | } | |
1784 | ||
1785 | /** | |
1786 | * eficonfig_process_edit_boot_option() - handler to edit boot option | |
1787 | * | |
1788 | * @data: pointer to the data for each entry | |
1789 | * Return: status code | |
1790 | */ | |
1791 | static efi_status_t eficonfig_process_edit_boot_option(void *data) | |
1792 | { | |
1793 | efi_status_t ret; | |
1794 | efi_uintn_t size; | |
1795 | struct eficonfig_boot_option *bo = NULL; | |
1796 | ||
1797 | while (1) { | |
1798 | unsigned int selected; | |
1799 | void *load_option; | |
1800 | u16 varname[] = u"Boot####"; | |
1801 | ||
1802 | ret = eficonfig_show_boot_selection(&selected); | |
1803 | if (ret != EFI_SUCCESS) | |
1804 | break; | |
1805 | ||
1806 | bo = calloc(1, sizeof(struct eficonfig_boot_option)); | |
1807 | if (!bo) { | |
1808 | ret = EFI_OUT_OF_RESOURCES; | |
1809 | goto out; | |
1810 | } | |
1811 | ||
1812 | bo->boot_index = selected; | |
1813 | efi_create_indexed_name(varname, sizeof(varname), "Boot", selected); | |
1814 | load_option = efi_get_var(varname, &efi_global_variable_guid, &size); | |
1815 | if (!load_option) { | |
1816 | free(bo); | |
1817 | ret = EFI_NOT_FOUND; | |
1818 | goto out; | |
1819 | } | |
1820 | ||
1821 | ret = eficonfig_edit_boot_option(varname, bo, load_option, size, | |
1822 | " ** Edit Boot Option ** "); | |
1823 | ||
1824 | free(load_option); | |
1825 | free(bo); | |
1826 | if (ret != EFI_SUCCESS && ret != EFI_ABORTED) | |
1827 | break; | |
1828 | } | |
1829 | out: | |
1830 | /* to stay the parent menu */ | |
1831 | ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; | |
1832 | ||
1833 | return ret; | |
1834 | } | |
1835 | ||
e5948ee3 | 1836 | /** |
0d590852 | 1837 | * eficonfig_print_change_boot_order_entry() - print the boot option entry |
e5948ee3 | 1838 | * |
0d590852 | 1839 | * @data: pointer to the data associated with each menu entry |
e5948ee3 | 1840 | */ |
0d590852 | 1841 | static void eficonfig_print_change_boot_order_entry(void *data) |
e5948ee3 | 1842 | { |
0d590852 MK |
1843 | struct eficonfig_entry *entry = data; |
1844 | bool reverse = (entry->efi_menu->active == entry->num); | |
e5948ee3 | 1845 | |
8dbd0a0f MK |
1846 | if (entry->efi_menu->start > entry->num || entry->efi_menu->end < entry->num) |
1847 | return; | |
1848 | ||
1849 | printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE, | |
1850 | (entry->num - entry->efi_menu->start) + EFICONFIG_MENU_HEADER_ROW_NUM + 1, 7); | |
e5948ee3 | 1851 | |
0d590852 MK |
1852 | if (reverse) |
1853 | puts(ANSI_COLOR_REVERSE); | |
e5948ee3 | 1854 | |
0d590852 MK |
1855 | if (entry->num < entry->efi_menu->count - 2) { |
1856 | if (((struct eficonfig_boot_order_data *)entry->data)->active) | |
1857 | printf("[*] "); | |
1858 | else | |
1859 | printf("[ ] "); | |
1860 | } | |
e5948ee3 | 1861 | |
0d590852 | 1862 | printf("%s", entry->title); |
e5948ee3 | 1863 | |
0d590852 MK |
1864 | if (reverse) |
1865 | puts(ANSI_COLOR_RESET); | |
e5948ee3 MK |
1866 | } |
1867 | ||
1868 | /** | |
0d590852 | 1869 | * eficonfig_choice_change_boot_order() - user key input handler |
e5948ee3 | 1870 | * |
0d590852 MK |
1871 | * @data: pointer to the menu entry |
1872 | * Return: key string to identify the selected entry | |
e5948ee3 | 1873 | */ |
0d590852 | 1874 | char *eficonfig_choice_change_boot_order(void *data) |
e5948ee3 | 1875 | { |
32bab0ea | 1876 | struct cli_ch_state s_cch, *cch = &s_cch; |
e5948ee3 | 1877 | struct list_head *pos, *n; |
0d590852 | 1878 | struct efimenu *efi_menu = data; |
2da4a15e | 1879 | enum bootmenu_key key = BKEY_NONE; |
d571f9b2 | 1880 | struct eficonfig_entry *entry, *tmp; |
e5948ee3 | 1881 | |
32bab0ea | 1882 | cli_ch_init(cch); |
e5948ee3 | 1883 | while (1) { |
32bab0ea | 1884 | key = bootmenu_loop(NULL, cch); |
e5948ee3 MK |
1885 | |
1886 | switch (key) { | |
2da4a15e | 1887 | case BKEY_PLUS: |
8dbd0a0f MK |
1888 | if (efi_menu->active > 0 && |
1889 | efi_menu->active < efi_menu->count - 2) { | |
e5948ee3 | 1890 | list_for_each_safe(pos, n, &efi_menu->list) { |
d571f9b2 | 1891 | entry = list_entry(pos, struct eficonfig_entry, list); |
e5948ee3 MK |
1892 | if (entry->num == efi_menu->active) |
1893 | break; | |
1894 | } | |
d571f9b2 | 1895 | tmp = list_entry(pos->prev, struct eficonfig_entry, list); |
e5948ee3 MK |
1896 | entry->num--; |
1897 | tmp->num++; | |
1898 | list_del(&tmp->list); | |
1899 | list_add(&tmp->list, &entry->list); | |
8dbd0a0f MK |
1900 | |
1901 | eficonfig_menu_up(efi_menu); | |
e5948ee3 | 1902 | } |
8dbd0a0f | 1903 | return NULL; |
2da4a15e | 1904 | case BKEY_UP: |
e5948ee3 | 1905 | if (efi_menu->active > 0) |
8dbd0a0f MK |
1906 | eficonfig_menu_up(efi_menu); |
1907 | ||
0d590852 | 1908 | return NULL; |
2da4a15e | 1909 | case BKEY_MINUS: |
e5948ee3 MK |
1910 | if (efi_menu->active < efi_menu->count - 3) { |
1911 | list_for_each_safe(pos, n, &efi_menu->list) { | |
d571f9b2 | 1912 | entry = list_entry(pos, struct eficonfig_entry, list); |
e5948ee3 MK |
1913 | if (entry->num == efi_menu->active) |
1914 | break; | |
1915 | } | |
d571f9b2 | 1916 | tmp = list_entry(pos->next, struct eficonfig_entry, list); |
e5948ee3 MK |
1917 | entry->num++; |
1918 | tmp->num--; | |
1919 | list_del(&entry->list); | |
1920 | list_add(&entry->list, &tmp->list); | |
1921 | ||
8dbd0a0f | 1922 | eficonfig_menu_down(efi_menu); |
e5948ee3 | 1923 | } |
0d590852 | 1924 | return NULL; |
2da4a15e | 1925 | case BKEY_DOWN: |
e5948ee3 | 1926 | if (efi_menu->active < efi_menu->count - 1) |
8dbd0a0f MK |
1927 | eficonfig_menu_down(efi_menu); |
1928 | ||
0d590852 | 1929 | return NULL; |
88df3634 MK |
1930 | case BKEY_SAVE: |
1931 | /* force to select "Save" entry */ | |
1932 | efi_menu->active = efi_menu->count - 2; | |
1933 | fallthrough; | |
2da4a15e | 1934 | case BKEY_SELECT: |
e5948ee3 | 1935 | /* "Save" */ |
0d590852 MK |
1936 | if (efi_menu->active == efi_menu->count - 2) { |
1937 | list_for_each_prev_safe(pos, n, &efi_menu->list) { | |
1938 | entry = list_entry(pos, struct eficonfig_entry, list); | |
1939 | if (entry->num == efi_menu->active) | |
1940 | break; | |
1941 | } | |
1942 | return entry->key; | |
1943 | } | |
e5948ee3 | 1944 | /* "Quit" */ |
0d590852 MK |
1945 | if (efi_menu->active == efi_menu->count - 1) { |
1946 | entry = list_last_entry(&efi_menu->list, | |
1947 | struct eficonfig_entry, | |
1948 | list); | |
1949 | return entry->key; | |
1950 | } | |
1951 | /* Pressed key is not valid, wait next key press */ | |
e5948ee3 | 1952 | break; |
2da4a15e | 1953 | case BKEY_SPACE: |
e5948ee3 MK |
1954 | if (efi_menu->active < efi_menu->count - 2) { |
1955 | list_for_each_safe(pos, n, &efi_menu->list) { | |
d571f9b2 | 1956 | entry = list_entry(pos, struct eficonfig_entry, list); |
e5948ee3 | 1957 | if (entry->num == efi_menu->active) { |
d571f9b2 MK |
1958 | struct eficonfig_boot_order_data *data = entry->data; |
1959 | ||
1960 | data->active = !data->active; | |
0d590852 | 1961 | return NULL; |
e5948ee3 MK |
1962 | } |
1963 | } | |
1964 | } | |
0d590852 | 1965 | /* Pressed key is not valid, wait next key press */ |
e5948ee3 | 1966 | break; |
2da4a15e | 1967 | case BKEY_QUIT: |
0d590852 MK |
1968 | entry = list_last_entry(&efi_menu->list, |
1969 | struct eficonfig_entry, list); | |
1970 | return entry->key; | |
e5948ee3 | 1971 | default: |
0d590852 | 1972 | /* Pressed key is not valid, wait next key press */ |
e5948ee3 MK |
1973 | break; |
1974 | } | |
1975 | } | |
1976 | } | |
1977 | ||
0d590852 MK |
1978 | /** |
1979 | * eficonfig_process_save_boot_order() - callback function for "Save" entry | |
1980 | * | |
1981 | * @data: pointer to the data | |
1982 | * Return: status code | |
1983 | */ | |
1984 | static efi_status_t eficonfig_process_save_boot_order(void *data) | |
1985 | { | |
1986 | u32 count = 0; | |
1987 | efi_status_t ret; | |
1988 | efi_uintn_t size; | |
1989 | struct list_head *pos, *n; | |
1990 | u16 *new_bootorder; | |
1991 | struct efimenu *efi_menu; | |
1992 | struct eficonfig_entry *entry; | |
1993 | struct eficonfig_save_boot_order_data *save_data = data; | |
1994 | ||
1995 | efi_menu = save_data->efi_menu; | |
1996 | ||
1997 | /* | |
1998 | * The change boot order menu always has "Save" and "Quit" entries. | |
1999 | * !(efi_menu->count - 2) means there is no user defined boot option. | |
2000 | */ | |
2001 | if (!(efi_menu->count - 2)) | |
2002 | return EFI_SUCCESS; | |
2003 | ||
2004 | new_bootorder = calloc(1, (efi_menu->count - 2) * sizeof(u16)); | |
2005 | if (!new_bootorder) { | |
2006 | ret = EFI_OUT_OF_RESOURCES; | |
2007 | goto out; | |
2008 | } | |
2009 | ||
2010 | /* create new BootOrder */ | |
2011 | count = 0; | |
2012 | list_for_each_safe(pos, n, &efi_menu->list) { | |
2013 | struct eficonfig_boot_order_data *data; | |
2014 | ||
2015 | entry = list_entry(pos, struct eficonfig_entry, list); | |
2016 | /* exit the loop when iteration reaches "Save" */ | |
2017 | if (!strncmp(entry->title, "Save", strlen("Save"))) | |
2018 | break; | |
2019 | ||
2020 | data = entry->data; | |
2021 | if (data->active) | |
2022 | new_bootorder[count++] = data->boot_index; | |
2023 | } | |
2024 | ||
2025 | size = count * sizeof(u16); | |
2026 | ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid, | |
2027 | EFI_VARIABLE_NON_VOLATILE | | |
2028 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
2029 | EFI_VARIABLE_RUNTIME_ACCESS, | |
2030 | size, new_bootorder, false); | |
2031 | ||
2032 | save_data->selected = true; | |
2033 | out: | |
2034 | free(new_bootorder); | |
2035 | ||
2036 | return ret; | |
2037 | } | |
2038 | ||
e5948ee3 MK |
2039 | /** |
2040 | * eficonfig_add_change_boot_order_entry() - add boot order entry | |
2041 | * | |
2042 | * @efi_menu: pointer to the efimenu structure | |
2043 | * @boot_index: boot option index to be added | |
2044 | * @active: flag to include the boot option into BootOrder | |
2045 | * Return: status code | |
2046 | */ | |
2047 | static efi_status_t eficonfig_add_change_boot_order_entry(struct efimenu *efi_menu, | |
2048 | u32 boot_index, bool active) | |
2049 | { | |
d571f9b2 | 2050 | char *title, *p; |
e5948ee3 MK |
2051 | efi_status_t ret; |
2052 | efi_uintn_t size; | |
2053 | void *load_option; | |
2054 | struct efi_load_option lo; | |
2055 | u16 varname[] = u"Boot####"; | |
d571f9b2 | 2056 | struct eficonfig_boot_order_data *data; |
e5948ee3 MK |
2057 | |
2058 | efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index); | |
2059 | load_option = efi_get_var(varname, &efi_global_variable_guid, &size); | |
2060 | if (!load_option) | |
2061 | return EFI_SUCCESS; | |
2062 | ||
2063 | ret = efi_deserialize_load_option(&lo, load_option, &size); | |
d571f9b2 MK |
2064 | if (ret != EFI_SUCCESS) |
2065 | goto out; | |
2066 | ||
2067 | data = calloc(1, sizeof(*data)); | |
2068 | if (!data) { | |
2069 | ret = EFI_OUT_OF_RESOURCES; | |
2070 | goto out; | |
e5948ee3 MK |
2071 | } |
2072 | ||
d571f9b2 MK |
2073 | title = calloc(1, utf16_utf8_strlen(lo.label) + 1); |
2074 | if (!title) { | |
2075 | free(data); | |
2076 | ret = EFI_OUT_OF_RESOURCES; | |
2077 | goto out; | |
e5948ee3 | 2078 | } |
d571f9b2 MK |
2079 | p = title; |
2080 | utf16_utf8_strcpy(&p, lo.label); | |
e5948ee3 | 2081 | |
d571f9b2 MK |
2082 | data->boot_index = boot_index; |
2083 | data->active = active; | |
2084 | ||
2085 | ret = eficonfig_append_menu_entry(efi_menu, title, NULL, data); | |
2086 | if (ret != EFI_SUCCESS) { | |
2087 | free(data); | |
2088 | free(title); | |
2089 | goto out; | |
e5948ee3 | 2090 | } |
e5948ee3 | 2091 | |
d571f9b2 | 2092 | out: |
e5948ee3 MK |
2093 | free(load_option); |
2094 | ||
d571f9b2 | 2095 | return ret; |
e5948ee3 MK |
2096 | } |
2097 | ||
2098 | /** | |
2099 | * eficonfig_create_change_boot_order_entry() - create boot order entry | |
2100 | * | |
2101 | * @efi_menu: pointer to the efimenu structure | |
2102 | * @bootorder: pointer to the BootOrder variable | |
2103 | * @num: number of BootOrder entry | |
2104 | * Return: status code | |
2105 | */ | |
2106 | static efi_status_t eficonfig_create_change_boot_order_entry(struct efimenu *efi_menu, | |
2107 | u16 *bootorder, efi_uintn_t num) | |
2108 | { | |
2109 | u32 i; | |
d571f9b2 | 2110 | char *title; |
e5948ee3 | 2111 | efi_status_t ret; |
ce327084 | 2112 | u16 *var_name16 = NULL; |
140a8959 | 2113 | efi_uintn_t size, buf_size; |
0d590852 | 2114 | struct eficonfig_save_boot_order_data *save_data; |
e5948ee3 MK |
2115 | |
2116 | /* list the load option in the order of BootOrder variable */ | |
2117 | for (i = 0; i < num; i++) { | |
2118 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2) | |
2119 | break; | |
2120 | ||
2121 | ret = eficonfig_add_change_boot_order_entry(efi_menu, bootorder[i], true); | |
2122 | if (ret != EFI_SUCCESS) | |
2123 | goto out; | |
2124 | } | |
2125 | ||
2126 | /* list the remaining load option not included in the BootOrder */ | |
140a8959 MK |
2127 | buf_size = 128; |
2128 | var_name16 = malloc(buf_size); | |
2129 | if (!var_name16) | |
2130 | return EFI_OUT_OF_RESOURCES; | |
2131 | ||
2132 | var_name16[0] = 0; | |
2133 | for (;;) { | |
2134 | int index; | |
2135 | efi_guid_t guid; | |
2136 | ||
e5948ee3 MK |
2137 | if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2) |
2138 | break; | |
2139 | ||
140a8959 | 2140 | size = buf_size; |
ce327084 | 2141 | ret = efi_next_variable_name(&buf_size, &var_name16, &guid); |
140a8959 MK |
2142 | if (ret == EFI_NOT_FOUND) |
2143 | break; | |
e5948ee3 MK |
2144 | if (ret != EFI_SUCCESS) |
2145 | goto out; | |
140a8959 MK |
2146 | |
2147 | if (efi_varname_is_load_option(var_name16, &index)) { | |
2148 | /* If the index is included in the BootOrder, skip it */ | |
339b527b | 2149 | if (efi_search_bootorder(bootorder, num, index, NULL)) |
140a8959 MK |
2150 | continue; |
2151 | ||
2152 | ret = eficonfig_add_change_boot_order_entry(efi_menu, index, false); | |
2153 | if (ret != EFI_SUCCESS) | |
2154 | goto out; | |
2155 | } | |
e5948ee3 MK |
2156 | } |
2157 | ||
2158 | /* add "Save" and "Quit" entries */ | |
d571f9b2 MK |
2159 | title = strdup("Save"); |
2160 | if (!title) { | |
2161 | ret = EFI_OUT_OF_RESOURCES; | |
e5948ee3 | 2162 | goto out; |
d571f9b2 | 2163 | } |
e5948ee3 | 2164 | |
0d590852 MK |
2165 | save_data = malloc(sizeof(struct eficonfig_save_boot_order_data)); |
2166 | if (!save_data) { | |
2167 | ret = EFI_OUT_OF_RESOURCES; | |
2168 | goto out; | |
2169 | } | |
2170 | save_data->efi_menu = efi_menu; | |
2171 | save_data->selected = false; | |
2172 | ||
2173 | ret = eficonfig_append_menu_entry(efi_menu, title, | |
2174 | eficonfig_process_save_boot_order, | |
2175 | save_data); | |
d571f9b2 | 2176 | if (ret != EFI_SUCCESS) |
e5948ee3 MK |
2177 | goto out; |
2178 | ||
d571f9b2 MK |
2179 | ret = eficonfig_append_quit_entry(efi_menu); |
2180 | if (ret != EFI_SUCCESS) | |
2181 | goto out; | |
e5948ee3 MK |
2182 | |
2183 | efi_menu->active = 0; | |
e5948ee3 | 2184 | out: |
140a8959 MK |
2185 | free(var_name16); |
2186 | ||
d571f9b2 | 2187 | return ret; |
e5948ee3 MK |
2188 | } |
2189 | ||
2190 | /** | |
2191 | * eficonfig_process_change_boot_order() - handler to change boot order | |
2192 | * | |
2193 | * @data: pointer to the data for each entry | |
2194 | * Return: status code | |
2195 | */ | |
2196 | static efi_status_t eficonfig_process_change_boot_order(void *data) | |
2197 | { | |
e5948ee3 MK |
2198 | u16 *bootorder; |
2199 | efi_status_t ret; | |
2200 | efi_uintn_t num, size; | |
2201 | struct list_head *pos, *n; | |
d571f9b2 | 2202 | struct eficonfig_entry *entry; |
e5948ee3 MK |
2203 | struct efimenu *efi_menu; |
2204 | ||
2205 | efi_menu = calloc(1, sizeof(struct efimenu)); | |
2206 | if (!efi_menu) | |
2207 | return EFI_OUT_OF_RESOURCES; | |
2208 | ||
2209 | bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size); | |
2210 | ||
2211 | INIT_LIST_HEAD(&efi_menu->list); | |
2212 | num = size / sizeof(u16); | |
2213 | ret = eficonfig_create_change_boot_order_entry(efi_menu, bootorder, num); | |
2214 | if (ret != EFI_SUCCESS) | |
2215 | goto out; | |
2216 | ||
2217 | while (1) { | |
0d590852 MK |
2218 | ret = eficonfig_process_common(efi_menu, |
2219 | " ** Change Boot Order **", | |
2220 | eficonfig_change_boot_order_desc, | |
2221 | eficonfig_display_statusline, | |
2222 | eficonfig_print_change_boot_order_entry, | |
2223 | eficonfig_choice_change_boot_order); | |
2224 | /* exit from the menu if user selects the "Save" entry. */ | |
2225 | if (ret == EFI_SUCCESS && efi_menu->active == (efi_menu->count - 2)) { | |
2226 | list_for_each_prev_safe(pos, n, &efi_menu->list) { | |
d571f9b2 | 2227 | entry = list_entry(pos, struct eficonfig_entry, list); |
0d590852 | 2228 | if (entry->num == efi_menu->active) |
d571f9b2 | 2229 | break; |
e5948ee3 | 2230 | } |
0d590852 MK |
2231 | if (((struct eficonfig_save_boot_order_data *)entry->data)->selected) |
2232 | break; | |
e5948ee3 | 2233 | } |
0d590852 MK |
2234 | if (ret != EFI_SUCCESS) |
2235 | break; | |
e5948ee3 MK |
2236 | } |
2237 | out: | |
d571f9b2 | 2238 | free(bootorder); |
e5948ee3 | 2239 | list_for_each_safe(pos, n, &efi_menu->list) { |
d571f9b2 MK |
2240 | entry = list_entry(pos, struct eficonfig_entry, list); |
2241 | free(entry->data); | |
e5948ee3 | 2242 | } |
d571f9b2 | 2243 | eficonfig_destroy(efi_menu); |
e5948ee3 MK |
2244 | |
2245 | /* to stay the parent menu */ | |
2246 | ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; | |
2247 | ||
2248 | return ret; | |
2249 | } | |
2250 | ||
bb8498aa MK |
2251 | /** |
2252 | * eficonfig_process_delete_boot_option() - handler to delete boot option | |
2253 | * | |
2254 | * @data: pointer to the data for each entry | |
2255 | * Return: status code | |
2256 | */ | |
2257 | static efi_status_t eficonfig_process_delete_boot_option(void *data) | |
2258 | { | |
2259 | efi_status_t ret; | |
2260 | unsigned int selected; | |
2261 | ||
2262 | while (1) { | |
2263 | ret = eficonfig_show_boot_selection(&selected); | |
2264 | if (ret == EFI_SUCCESS) | |
339b527b | 2265 | ret = efi_bootmgr_delete_boot_option(selected); |
bb8498aa MK |
2266 | |
2267 | if (ret != EFI_SUCCESS) | |
2268 | break; | |
2269 | } | |
2270 | ||
2271 | /* to stay the parent menu */ | |
2272 | ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; | |
2273 | ||
2274 | return ret; | |
2275 | } | |
2276 | ||
87d79142 MK |
2277 | /** |
2278 | * eficonfig_init() - do required initialization for eficonfig command | |
2279 | * | |
2280 | * Return: status code | |
2281 | */ | |
2282 | static efi_status_t eficonfig_init(void) | |
2283 | { | |
2284 | efi_status_t ret = EFI_SUCCESS; | |
2285 | static bool init; | |
2286 | struct efi_handler *handler; | |
8dbd0a0f | 2287 | unsigned long columns, rows; |
87d79142 MK |
2288 | |
2289 | if (!init) { | |
2290 | ret = efi_search_protocol(efi_root, &efi_guid_text_input_protocol, &handler); | |
2291 | if (ret != EFI_SUCCESS) | |
2292 | return ret; | |
2293 | ||
2294 | ret = efi_protocol_open(handler, (void **)&cin, efi_root, NULL, | |
2295 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
2296 | if (ret != EFI_SUCCESS) | |
2297 | return ret; | |
8dbd0a0f MK |
2298 | ret = efi_search_protocol(efi_root, &efi_guid_text_output_protocol, &handler); |
2299 | if (ret != EFI_SUCCESS) | |
2300 | return ret; | |
2301 | ||
2302 | ret = efi_protocol_open(handler, (void **)&cout, efi_root, NULL, | |
2303 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
2304 | if (ret != EFI_SUCCESS) | |
2305 | return ret; | |
2306 | ||
2307 | cout->query_mode(cout, cout->mode->mode, &columns, &rows); | |
2308 | avail_row = rows - (EFICONFIG_MENU_HEADER_ROW_NUM + | |
2309 | EFICONFIG_MENU_DESC_ROW_NUM); | |
2310 | if (avail_row <= 0) { | |
2311 | eficonfig_print_msg("Console size is too small!"); | |
2312 | return EFI_INVALID_PARAMETER; | |
2313 | } | |
2314 | /* TODO: Should we check the minimum column size? */ | |
87d79142 MK |
2315 | } |
2316 | ||
2317 | init = true; | |
2318 | ||
2319 | return ret; | |
2320 | } | |
2321 | ||
2322 | static const struct eficonfig_item maintenance_menu_items[] = { | |
2323 | {"Add Boot Option", eficonfig_process_add_boot_option}, | |
e34158bc | 2324 | {"Edit Boot Option", eficonfig_process_edit_boot_option}, |
e5948ee3 | 2325 | {"Change Boot Order", eficonfig_process_change_boot_order}, |
bb8498aa | 2326 | {"Delete Boot Option", eficonfig_process_delete_boot_option}, |
2c0b0c31 | 2327 | #if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT) && IS_ENABLED(CONFIG_EFI_MM_COMM_TEE)) |
c3b5af63 MK |
2328 | {"Secure Boot Configuration", eficonfig_process_secure_boot_config}, |
2329 | #endif | |
87d79142 MK |
2330 | {"Quit", eficonfig_process_quit}, |
2331 | }; | |
2332 | ||
2333 | /** | |
2334 | * do_eficonfig() - execute `eficonfig` command | |
2335 | * | |
2336 | * @cmdtp: table entry describing command | |
2337 | * @flag: bitmap indicating how the command was invoked | |
2338 | * @argc: number of arguments | |
2339 | * @argv: command line arguments | |
2340 | * Return: status code | |
2341 | */ | |
2342 | static int do_eficonfig(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) | |
2343 | { | |
2344 | efi_status_t ret; | |
2345 | struct efimenu *efi_menu; | |
2346 | ||
2347 | if (argc > 1) | |
2348 | return CMD_RET_USAGE; | |
2349 | ||
2350 | ret = efi_init_obj_list(); | |
2351 | if (ret != EFI_SUCCESS) { | |
2352 | log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", | |
2353 | ret & ~EFI_ERROR_MASK); | |
2354 | ||
2355 | return CMD_RET_FAILURE; | |
2356 | } | |
2357 | ||
2358 | ret = eficonfig_init(); | |
2359 | if (ret != EFI_SUCCESS) | |
2360 | return CMD_RET_FAILURE; | |
2361 | ||
339b527b | 2362 | ret = efi_bootmgr_update_media_device_boot_option(); |
9945bc4f | 2363 | if (ret != EFI_SUCCESS) |
b5135a1e MK |
2364 | return ret; |
2365 | ||
87d79142 MK |
2366 | while (1) { |
2367 | efi_menu = eficonfig_create_fixed_menu(maintenance_menu_items, | |
2368 | ARRAY_SIZE(maintenance_menu_items)); | |
2369 | if (!efi_menu) | |
2370 | return CMD_RET_FAILURE; | |
2371 | ||
cd160b27 MK |
2372 | ret = eficonfig_process_common(efi_menu, |
2373 | " ** UEFI Maintenance Menu **", | |
2374 | eficonfig_menu_desc, | |
2375 | eficonfig_display_statusline, | |
2376 | eficonfig_print_entry, | |
2377 | eficonfig_choice_entry); | |
87d79142 MK |
2378 | eficonfig_destroy(efi_menu); |
2379 | ||
2380 | if (ret == EFI_ABORTED) | |
2381 | break; | |
2382 | } | |
2383 | ||
2384 | return CMD_RET_SUCCESS; | |
2385 | } | |
2386 | ||
2387 | U_BOOT_CMD( | |
2388 | eficonfig, 1, 0, do_eficonfig, | |
2389 | "provide menu-driven UEFI variable maintenance interface", | |
2390 | "" | |
2391 | ); |