1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "bootspec-fundamental.h"
10 #include "devicetree.h"
13 #include "efivars-fundamental.h"
18 #include "random-seed.h"
19 #include "secure-boot.h"
25 #ifndef GNU_EFI_USE_MS_ABI
26 /* We do not use uefi_call_wrapper() in systemd-boot. As such, we rely on the
27 * compiler to do the calling convention conversion for us. This is check is
28 * to make sure the -DGNU_EFI_USE_MS_ABI was passed to the comiler. */
29 #error systemd-boot requires compilation with GNU_EFI_USE_MS_ABI defined.
32 #define TEXT_ATTR_SWAP(c) EFI_TEXT_ATTR(((c) & 0b11110000) >> 4, (c) & 0b1111)
34 /* Magic string for recognizing our own binaries */
35 _used_
_section_(".sdmagic") static const char magic
[] =
36 "#### LoaderInfo: systemd-boot " GIT_VERSION
" ####";
38 /* Makes systemd-boot available from \EFI\Linux\ for testing purposes. */
39 _used_
_section_(".osrel") static const char osrel
[] =
41 "VERSION=\"" GIT_VERSION
"\"\n"
42 "NAME=\"systemd-boot " GIT_VERSION
"\"\n";
48 LOADER_LINUX
, /* Boot loader spec type #1 entries */
49 LOADER_UNIFIED_LINUX
, /* Boot loader spec type #2 entries */
53 CHAR16
*id
; /* The unique identifier for this entry (typically the filename of the file defining the entry) */
54 CHAR16
*title_show
; /* The string to actually display (this is made unique before showing) */
55 CHAR16
*title
; /* The raw (human readable) title string of the entry (not necessarily unique) */
56 CHAR16
*sort_key
; /* The string to use as primary sory key, usually ID= from os-release, possibly suffixed */
57 CHAR16
*version
; /* The raw (human readable) version string of the entry */
60 enum loader_type type
;
65 EFI_STATUS (*call
)(void);
74 ConfigEntry
**entries
;
77 UINTN idx_default_efivar
;
78 UINT32 timeout_sec
; /* Actual timeout used (efi_main() override > efivar > config). */
79 UINT32 timeout_sec_config
;
80 UINT32 timeout_sec_efivar
;
81 CHAR16
*entry_default_config
;
82 CHAR16
*entry_default_efivar
;
83 CHAR16
*entry_oneshot
;
88 BOOLEAN auto_firmware
;
89 BOOLEAN reboot_for_bitlocker
;
91 BOOLEAN use_saved_entry
;
92 BOOLEAN use_saved_entry_efivar
;
95 INT64 console_mode_efivar
;
96 RandomSeedMode random_seed_mode
;
99 /* These values have been chosen so that the transitions the user sees could
100 * employ unsigned over-/underflow like this:
101 * efivar unset ↔ force menu ↔ no timeout/skip menu ↔ 1 s ↔ 2 s ↔ … */
104 TIMEOUT_MAX
= UINT32_MAX
- 2U,
105 TIMEOUT_UNSET
= UINT32_MAX
- 1U,
106 TIMEOUT_MENU_FORCE
= UINT32_MAX
,
107 TIMEOUT_MENU_HIDDEN
= 0,
108 TIMEOUT_TYPE_MAX
= UINT32_MAX
,
116 static void cursor_left(UINTN
*cursor
, UINTN
*first
) {
122 else if ((*first
) > 0)
126 static void cursor_right(
135 if ((*cursor
)+1 < x_max
)
137 else if ((*first
) + (*cursor
) < len
)
141 static BOOLEAN
line_edit(
142 const CHAR16
*line_in
,
147 _cleanup_freepool_ CHAR16
*line
= NULL
, *print
= NULL
;
148 UINTN size
, len
, first
= 0, cursor
= 0, clear
= 0;
155 len
= StrLen(line_in
);
157 line
= xnew(CHAR16
, size
);
158 print
= xnew(CHAR16
, x_max
+ 1);
159 StrCpy(line
, line_in
);
165 UINTN cursor_color
= TEXT_ATTR_SWAP(COLOR_EDIT
);
167 j
= MIN(len
- first
, x_max
);
168 CopyMem(print
, line
+ first
, j
* sizeof(CHAR16
));
169 while (clear
> 0 && j
< x_max
) {
175 /* See comment at edit_line() call site for why we start at 1. */
176 print_at(1, y_pos
, COLOR_EDIT
, print
);
180 print
[cursor
+1] = '\0';
182 print_at(cursor
+ 1, y_pos
, cursor_color
, print
+ cursor
);
183 cursor_color
= TEXT_ATTR_SWAP(cursor_color
);
185 err
= console_key_read(&key
, 750 * 1000);
186 if (!IN_SET(err
, EFI_SUCCESS
, EFI_TIMEOUT
, EFI_NOT_READY
))
189 print_at(cursor
+ 1, y_pos
, COLOR_EDIT
, print
+ cursor
);
190 } while (EFI_ERROR(err
));
193 case KEYPRESS(0, SCAN_ESC
, 0):
194 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'c'):
195 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'g'):
196 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('c')):
197 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('g')):
200 case KEYPRESS(0, SCAN_HOME
, 0):
201 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'a'):
202 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('a')):
203 /* beginning-of-line */
208 case KEYPRESS(0, SCAN_END
, 0):
209 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'e'):
210 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('e')):
212 cursor
= len
- first
;
213 if (cursor
+1 >= x_max
) {
215 first
= len
- (x_max
-1);
219 case KEYPRESS(0, SCAN_DOWN
, 0):
220 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'f'):
221 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_RIGHT
, 0):
223 while (line
[first
+ cursor
] == ' ')
224 cursor_right(&cursor
, &first
, x_max
, len
);
225 while (line
[first
+ cursor
] && line
[first
+ cursor
] != ' ')
226 cursor_right(&cursor
, &first
, x_max
, len
);
229 case KEYPRESS(0, SCAN_UP
, 0):
230 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'b'):
231 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_LEFT
, 0):
233 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
234 cursor_left(&cursor
, &first
);
235 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ')
236 cursor_left(&cursor
, &first
);
238 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ')
239 cursor_left(&cursor
, &first
);
242 case KEYPRESS(0, SCAN_RIGHT
, 0):
243 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'f'):
244 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('f')):
246 if (first
+ cursor
== len
)
248 cursor_right(&cursor
, &first
, x_max
, len
);
251 case KEYPRESS(0, SCAN_LEFT
, 0):
252 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'b'):
253 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('b')):
255 cursor_left(&cursor
, &first
);
258 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'd'):
263 for (k
= first
+ cursor
; k
< len
&& line
[k
] == ' '; k
++)
265 for (; k
< len
&& line
[k
] != ' '; k
++)
268 for (UINTN i
= first
+ cursor
; i
+ clear
< len
; i
++)
269 line
[i
] = line
[i
+ clear
];
274 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'w'):
275 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('w')):
276 case KEYPRESS(EFI_ALT_PRESSED
, 0, CHAR_BACKSPACE
):
277 /* backward-kill-word */
279 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
280 cursor_left(&cursor
, &first
);
282 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ') {
283 cursor_left(&cursor
, &first
);
287 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ') {
288 cursor_left(&cursor
, &first
);
292 for (UINTN i
= first
+ cursor
; i
+ clear
< len
; i
++)
293 line
[i
] = line
[i
+ clear
];
298 case KEYPRESS(0, SCAN_DELETE
, 0):
299 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'd'):
300 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('d')):
303 if (first
+ cursor
== len
)
305 for (UINTN i
= first
+ cursor
; i
< len
; i
++)
311 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'k'):
312 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('k')):
314 line
[first
+ cursor
] = '\0';
315 clear
= len
- (first
+ cursor
);
316 len
= first
+ cursor
;
319 case KEYPRESS(0, 0, CHAR_LINEFEED
):
320 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
321 case KEYPRESS(0, CHAR_CARRIAGE_RETURN
, 0): /* EZpad Mini 4s firmware sends malformed events */
322 case KEYPRESS(0, CHAR_CARRIAGE_RETURN
, CHAR_CARRIAGE_RETURN
): /* Teclast X98+ II firmware sends malformed events */
323 if (StrCmp(line
, line_in
) != 0)
324 *line_out
= TAKE_PTR(line
);
327 case KEYPRESS(0, 0, CHAR_BACKSPACE
):
330 if (first
== 0 && cursor
== 0)
332 for (UINTN i
= first
+ cursor
-1; i
< len
; i
++)
338 if (cursor
> 0 || first
== 0)
340 /* show full line if it fits */
346 /* jump left to see what we delete */
356 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
357 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
360 for (UINTN i
= len
; i
> first
+ cursor
; i
--)
362 line
[first
+ cursor
] = KEYCHAR(key
);
365 if (cursor
+1 < x_max
)
367 else if (first
+ cursor
< len
)
374 static UINTN
entry_lookup_key(Config
*config
, UINTN start
, CHAR16 key
) {
380 /* select entry by number key */
381 if (key
>= '1' && key
<= '9') {
383 if (i
> config
->entry_count
)
384 i
= config
->entry_count
;
388 /* find matching key in config entries */
389 for (UINTN i
= start
; i
< config
->entry_count
; i
++)
390 if (config
->entries
[i
]->key
== key
)
393 for (UINTN i
= 0; i
< start
; i
++)
394 if (config
->entries
[i
]->key
== key
)
400 static CHAR16
*update_timeout_efivar(UINT32
*t
, BOOLEAN inc
) {
405 *t
= inc
? TIMEOUT_MAX
: (*t
- 1);
408 *t
= inc
? TIMEOUT_MENU_FORCE
: TIMEOUT_UNSET
;
410 case TIMEOUT_MENU_FORCE
:
411 *t
= inc
? TIMEOUT_MENU_HIDDEN
: TIMEOUT_UNSET
;
413 case TIMEOUT_MENU_HIDDEN
:
414 *t
= inc
? TIMEOUT_MIN
: TIMEOUT_MENU_FORCE
;
422 return xstrdup(L
"Menu timeout defined by configuration file.");
423 case TIMEOUT_MENU_FORCE
:
424 return xstrdup(L
"Timeout disabled, menu will always be shown.");
425 case TIMEOUT_MENU_HIDDEN
:
426 return xstrdup(L
"Menu disabled. Hold down key at bootup to show menu.");
428 return xpool_print(L
"Menu timeout set to %u s.", *t
);
432 static void ps_string(const CHAR16
*fmt
, const void *value
) {
438 static void ps_bool(const CHAR16
*fmt
, BOOLEAN value
) {
440 Print(fmt
, yes_no(value
));
443 static BOOLEAN
ps_continue(void) {
447 Print(L
"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
448 err
= console_key_read(&key
, UINT64_MAX
);
449 return !EFI_ERROR(err
) && !IN_SET(key
, KEYPRESS(0, SCAN_ESC
, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q'));
452 static void print_status(Config
*config
, CHAR16
*loaded_image_path
) {
454 UINT32 screen_width
= 0, screen_height
= 0;
455 SecureBootMode secure
;
456 _cleanup_freepool_ CHAR16
*device_part_uuid
= NULL
;
459 assert(loaded_image_path
);
461 clear_screen(COLOR_NORMAL
);
462 console_query_mode(&x_max
, &y_max
);
463 query_screen_resolution(&screen_width
, &screen_height
);
465 secure
= secure_boot_mode();
466 (void) efivar_get(LOADER_GUID
, L
"LoaderDevicePartUUID", &device_part_uuid
);
468 /* We employ some unusual indentation here for readability. */
470 ps_string(L
" systemd-boot version: %a\n", GIT_VERSION
);
471 ps_string(L
" loaded image: %s\n", loaded_image_path
);
472 ps_string(L
" loader partition UUID: %s\n", device_part_uuid
);
473 ps_string(L
" architecture: %a\n", EFI_MACHINE_TYPE_NAME
);
474 Print(L
" UEFI specification: %u.%02u\n", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
475 ps_string(L
" firmware vendor: %s\n", ST
->FirmwareVendor
);
476 Print(L
" firmware version: %u.%02u\n", ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
477 Print(L
" OS indications: %lu\n", get_os_indications_supported());
478 Print(L
" secure boot: %s (%s)\n", yes_no(IN_SET(secure
, SECURE_BOOT_USER
, SECURE_BOOT_DEPLOYED
)), secure_boot_mode_to_string(secure
));
479 ps_bool(L
" shim: %s\n", shim_loaded());
480 ps_bool(L
" TPM: %s\n", tpm_present());
481 Print(L
" console mode: %d/%d (%lux%lu @%ux%u)\n", ST
->ConOut
->Mode
->Mode
, ST
->ConOut
->Mode
->MaxMode
- 1LL, x_max
, y_max
, screen_width
, screen_height
);
486 switch (config
->timeout_sec_config
) {
489 case TIMEOUT_MENU_FORCE
:
490 Print(L
" timeout: menu-force\n"); break;
491 case TIMEOUT_MENU_HIDDEN
:
492 Print(L
" timeout: menu-hidden\n"); break;
494 Print(L
" timeout: %lu s\n", config
->timeout_sec_config
);
497 switch (config
->timeout_sec_efivar
) {
500 case TIMEOUT_MENU_FORCE
:
501 Print(L
" timeout (EFI var): menu-force\n"); break;
502 case TIMEOUT_MENU_HIDDEN
:
503 Print(L
" timeout (EFI var): menu-hidden\n"); break;
505 Print(L
" timeout (EFI var): %lu s\n", config
->timeout_sec_efivar
);
508 ps_string(L
" default: %s\n", config
->entry_default_config
);
509 ps_string(L
" default (EFI var): %s\n", config
->entry_default_efivar
);
510 ps_string(L
" default (one-shot): %s\n", config
->entry_oneshot
);
511 ps_string(L
" saved entry: %s\n", config
->entry_saved
);
512 ps_bool(L
" editor: %s\n", config
->editor
);
513 ps_bool(L
" auto-entries: %s\n", config
->auto_entries
);
514 ps_bool(L
" auto-firmware: %s\n", config
->auto_firmware
);
515 ps_bool(L
" beep: %s\n", config
->beep
);
516 ps_bool(L
" reboot-for-bitlocker: %s\n", config
->reboot_for_bitlocker
);
517 ps_string(L
" random-seed-mode: %s\n", random_seed_modes_table
[config
->random_seed_mode
]);
519 switch (config
->console_mode
) {
520 case CONSOLE_MODE_AUTO
:
521 Print(L
" console-mode: %s\n", L
"auto"); break;
522 case CONSOLE_MODE_KEEP
:
523 Print(L
" console-mode: %s\n", L
"keep"); break;
524 case CONSOLE_MODE_FIRMWARE_MAX
:
525 Print(L
" console-mode: %s\n", L
"max"); break;
527 Print(L
" console-mode: %ld\n", config
->console_mode
); break;
530 /* EFI var console mode is always a concrete value or unset. */
531 if (config
->console_mode_efivar
!= CONSOLE_MODE_KEEP
)
532 Print(L
"console-mode (EFI var): %ld\n", config
->console_mode_efivar
);
537 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
538 ConfigEntry
*entry
= config
->entries
[i
];
540 Print(L
" config entry: %lu/%lu\n", i
+ 1, config
->entry_count
);
541 ps_string(L
" id: %s\n", entry
->id
);
542 ps_string(L
" title: %s\n", entry
->title
);
543 ps_string(L
" title show: %s\n", streq_ptr(entry
->title
, entry
->title_show
) ? NULL
: entry
->title_show
);
544 ps_string(L
" sort key: %s\n", entry
->sort_key
);
545 ps_string(L
" version: %s\n", entry
->version
);
546 ps_string(L
" machine-id: %s\n", entry
->machine_id
);
548 Print(L
" device: %D\n", DevicePathFromHandle(entry
->device
));
549 ps_string(L
" loader: %s\n", entry
->loader
);
550 ps_string(L
" devicetree: %s\n", entry
->devicetree
);
551 ps_string(L
" options: %s\n", entry
->options
);
552 ps_bool(L
" internal call: %s\n", !!entry
->call
);
554 ps_bool(L
"counting boots: %s\n", entry
->tries_left
!= UINTN_MAX
);
555 if (entry
->tries_left
!= UINTN_MAX
) {
556 Print(L
" tries: %lu done, %lu left\n", entry
->tries_done
, entry
->tries_left
);
557 Print(L
" current path: %s\\%s\n", entry
->path
, entry
->current_name
);
558 Print(L
" next path: %s\\%s\n", entry
->path
, entry
->next_name
);
566 static EFI_STATUS
reboot_into_firmware(void) {
570 if (!FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
))
571 return log_error_status_stall(EFI_UNSUPPORTED
, L
"Reboot to firmware interface not supported.");
573 (void) efivar_get_uint64_le(EFI_GLOBAL_GUID
, L
"OsIndications", &osind
);
574 osind
|= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
576 err
= efivar_set_uint64_le(EFI_GLOBAL_GUID
, L
"OsIndications", osind
, EFI_VARIABLE_NON_VOLATILE
);
578 return log_error_status_stall(err
, L
"Error setting OsIndications: %r", err
);
580 err
= RT
->ResetSystem(EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
581 return log_error_status_stall(err
, L
"Error calling ResetSystem: %r", err
);
584 static BOOLEAN
menu_run(
586 ConfigEntry
**chosen_entry
,
587 CHAR16
*loaded_image_path
) {
590 assert(chosen_entry
);
591 assert(loaded_image_path
);
594 UINTN visible_max
= 0;
595 UINTN idx_highlight
= config
->idx_default
;
596 UINTN idx_highlight_prev
= 0;
597 UINTN idx
, idx_first
= 0, idx_last
= 0;
598 BOOLEAN new_mode
= TRUE
, clear
= TRUE
;
599 BOOLEAN refresh
= TRUE
, highlight
= FALSE
;
600 UINTN x_start
= 0, y_start
= 0, y_status
= 0;
602 _cleanup_(strv_freep
) CHAR16
**lines
= NULL
;
603 _cleanup_freepool_ CHAR16
*clearline
= NULL
, *status
= NULL
;
604 UINT32 timeout_efivar_saved
= config
->timeout_sec_efivar
;
605 UINT32 timeout_remain
= config
->timeout_sec
== TIMEOUT_MENU_FORCE
? 0 : config
->timeout_sec
;
606 BOOLEAN exit
= FALSE
, run
= TRUE
, firmware_setup
= FALSE
;
607 INT64 console_mode_initial
= ST
->ConOut
->Mode
->Mode
, console_mode_efivar_saved
= config
->console_mode_efivar
;
608 UINTN default_efivar_saved
= config
->idx_default_efivar
;
610 graphics_mode(FALSE
);
611 ST
->ConIn
->Reset(ST
->ConIn
, FALSE
);
612 ST
->ConOut
->EnableCursor(ST
->ConOut
, FALSE
);
614 /* draw a single character to make ClearScreen work on some firmware */
617 err
= console_set_mode(config
->console_mode_efivar
!= CONSOLE_MODE_KEEP
?
618 config
->console_mode_efivar
: config
->console_mode
);
619 if (EFI_ERROR(err
)) {
620 clear_screen(COLOR_NORMAL
);
621 log_error_stall(L
"Error switching console mode: %r", err
);
628 UINTN line_width
= 0, entry_padding
= 3;
630 console_query_mode(&x_max
, &y_max
);
632 /* account for padding+status */
633 visible_max
= y_max
- 2;
635 /* Drawing entries starts at idx_first until idx_last. We want to make
636 * sure that idx_highlight is centered, but not if we are close to the
637 * beginning/end of the entry list. Otherwise we would have a half-empty
639 if (config
->entry_count
<= visible_max
|| idx_highlight
<= visible_max
/ 2)
641 else if (idx_highlight
>= config
->entry_count
- (visible_max
/ 2))
642 idx_first
= config
->entry_count
- visible_max
;
644 idx_first
= idx_highlight
- (visible_max
/ 2);
645 idx_last
= idx_first
+ visible_max
- 1;
647 /* length of the longest entry */
648 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
649 line_width
= MAX(line_width
, StrLen(config
->entries
[i
]->title_show
));
650 line_width
= MIN(line_width
+ 2 * entry_padding
, x_max
);
652 /* offsets to center the entries on the screen */
653 x_start
= (x_max
- (line_width
)) / 2;
654 if (config
->entry_count
< visible_max
)
655 y_start
= ((visible_max
- config
->entry_count
) / 2) + 1;
659 /* Put status line after the entry list, but give it some breathing room. */
660 y_status
= MIN(y_start
+ MIN(visible_max
, config
->entry_count
) + 4, y_max
- 1);
662 lines
= strv_free(lines
);
663 clearline
= mfree(clearline
);
665 /* menu entries title lines */
666 lines
= xnew(CHAR16
*, config
->entry_count
+ 1);
668 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
671 lines
[i
] = xnew(CHAR16
, line_width
+ 1);
672 padding
= (line_width
- MIN(StrLen(config
->entries
[i
]->title_show
), line_width
)) / 2;
674 for (j
= 0; j
< padding
; j
++)
677 for (UINTN k
= 0; config
->entries
[i
]->title_show
[k
] != '\0' && j
< line_width
; j
++, k
++)
678 lines
[i
][j
] = config
->entries
[i
]->title_show
[k
];
680 for (; j
< line_width
; j
++)
682 lines
[i
][line_width
] = '\0';
684 lines
[config
->entry_count
] = NULL
;
686 clearline
= xnew(CHAR16
, x_max
+ 1);
687 for (UINTN i
= 0; i
< x_max
; i
++)
689 clearline
[x_max
] = 0;
696 clear_screen(COLOR_NORMAL
);
702 for (UINTN i
= idx_first
; i
<= idx_last
&& i
< config
->entry_count
; i
++) {
703 print_at(x_start
, y_start
+ i
- idx_first
,
704 (i
== idx_highlight
) ? COLOR_HIGHLIGHT
: COLOR_ENTRY
,
706 if (i
== config
->idx_default_efivar
)
707 print_at(x_start
, y_start
+ i
- idx_first
,
708 (i
== idx_highlight
) ? COLOR_HIGHLIGHT
: COLOR_ENTRY
,
712 } else if (highlight
) {
713 print_at(x_start
, y_start
+ idx_highlight_prev
- idx_first
, COLOR_ENTRY
, lines
[idx_highlight_prev
]);
714 print_at(x_start
, y_start
+ idx_highlight
- idx_first
, COLOR_HIGHLIGHT
, lines
[idx_highlight
]);
715 if (idx_highlight_prev
== config
->idx_default_efivar
)
716 print_at(x_start
, y_start
+ idx_highlight_prev
- idx_first
, COLOR_ENTRY
, (CHAR16
*) L
"=>");
717 if (idx_highlight
== config
->idx_default_efivar
)
718 print_at(x_start
, y_start
+ idx_highlight
- idx_first
, COLOR_HIGHLIGHT
, (CHAR16
*) L
"=>");
722 if (timeout_remain
> 0) {
724 status
= xpool_print(L
"Boot in %u s.", timeout_remain
);
727 /* print status at last line of screen */
733 len
= StrLen(status
);
735 x
= (x_max
- len
) / 2;
738 print_at(0, y_status
, COLOR_NORMAL
, clearline
+ (x_max
- x
));
739 ST
->ConOut
->OutputString(ST
->ConOut
, status
);
740 ST
->ConOut
->OutputString(ST
->ConOut
, clearline
+ 1 + x
+ len
);
743 /* Beep several times so that the selected entry can be distinguished. */
745 beep(idx_highlight
+ 1);
747 err
= console_key_read(&key
, timeout_remain
> 0 ? 1000 * 1000 : UINT64_MAX
);
748 if (err
== EFI_NOT_READY
)
749 /* No input device returned a key, try again. This
750 * normally should not happen. */
752 if (err
== EFI_TIMEOUT
) {
753 assert(timeout_remain
> 0);
755 if (timeout_remain
== 0) {
763 if (EFI_ERROR(err
)) {
770 /* clear status after keystroke */
774 print_at(0, y_status
, COLOR_NORMAL
, clearline
+ 1);
777 idx_highlight_prev
= idx_highlight
;
779 if (firmware_setup
) {
780 firmware_setup
= FALSE
;
781 if (key
== KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
))
782 reboot_into_firmware();
787 case KEYPRESS(0, SCAN_UP
, 0):
788 case KEYPRESS(0, 0, 'k'):
789 case KEYPRESS(0, 0, 'K'):
790 if (idx_highlight
> 0)
794 case KEYPRESS(0, SCAN_DOWN
, 0):
795 case KEYPRESS(0, 0, 'j'):
796 case KEYPRESS(0, 0, 'J'):
797 if (idx_highlight
< config
->entry_count
-1)
801 case KEYPRESS(0, SCAN_HOME
, 0):
802 case KEYPRESS(EFI_ALT_PRESSED
, 0, '<'):
803 if (idx_highlight
> 0) {
809 case KEYPRESS(0, SCAN_END
, 0):
810 case KEYPRESS(EFI_ALT_PRESSED
, 0, '>'):
811 if (idx_highlight
< config
->entry_count
-1) {
813 idx_highlight
= config
->entry_count
-1;
817 case KEYPRESS(0, SCAN_PAGE_UP
, 0):
818 if (idx_highlight
> visible_max
)
819 idx_highlight
-= visible_max
;
824 case KEYPRESS(0, SCAN_PAGE_DOWN
, 0):
825 idx_highlight
+= visible_max
;
826 if (idx_highlight
> config
->entry_count
-1)
827 idx_highlight
= config
->entry_count
-1;
830 case KEYPRESS(0, 0, CHAR_LINEFEED
):
831 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
832 case KEYPRESS(0, CHAR_CARRIAGE_RETURN
, 0): /* EZpad Mini 4s firmware sends malformed events */
833 case KEYPRESS(0, CHAR_CARRIAGE_RETURN
, CHAR_CARRIAGE_RETURN
): /* Teclast X98+ II firmware sends malformed events */
834 case KEYPRESS(0, SCAN_RIGHT
, 0):
838 case KEYPRESS(0, SCAN_F1
, 0):
839 case KEYPRESS(0, 0, 'h'):
840 case KEYPRESS(0, 0, 'H'):
841 case KEYPRESS(0, 0, '?'):
842 /* This must stay below 80 characters! Q/v/Ctrl+l/f deliberately not advertised. */
843 status
= xstrdup(L
"(d)efault (t/T)timeout (e)dit (r/R)resolution (p)rint (h)elp");
846 case KEYPRESS(0, 0, 'Q'):
851 case KEYPRESS(0, 0, 'd'):
852 case KEYPRESS(0, 0, 'D'):
853 if (config
->idx_default_efivar
!= idx_highlight
) {
854 FreePool(config
->entry_default_efivar
);
855 config
->entry_default_efivar
= xstrdup(config
->entries
[idx_highlight
]->id
);
856 config
->idx_default_efivar
= idx_highlight
;
857 status
= xstrdup(L
"Default boot entry selected.");
859 config
->entry_default_efivar
= mfree(config
->entry_default_efivar
);
860 config
->idx_default_efivar
= IDX_INVALID
;
861 status
= xstrdup(L
"Default boot entry cleared.");
863 config
->use_saved_entry_efivar
= FALSE
;
867 case KEYPRESS(0, 0, '-'):
868 case KEYPRESS(0, 0, 'T'):
869 status
= update_timeout_efivar(&config
->timeout_sec_efivar
, FALSE
);
872 case KEYPRESS(0, 0, '+'):
873 case KEYPRESS(0, 0, 't'):
874 status
= update_timeout_efivar(&config
->timeout_sec_efivar
, TRUE
);
877 case KEYPRESS(0, 0, 'e'):
878 case KEYPRESS(0, 0, 'E'):
879 /* only the options of configured entries can be edited */
880 if (!config
->editor
|| !IN_SET(config
->entries
[idx_highlight
]->type
,
881 LOADER_EFI
, LOADER_LINUX
, LOADER_UNIFIED_LINUX
))
884 /* Unified kernels that are signed as a whole will not accept command line options
885 * when secure boot is enabled unless there is none embedded in the image. Do not try
886 * to pretend we can edit it to only have it be ignored. */
887 if (config
->entries
[idx_highlight
]->type
== LOADER_UNIFIED_LINUX
&&
888 secure_boot_enabled() &&
889 config
->entries
[idx_highlight
]->options
)
892 /* The edit line may end up on the last line of the screen. And even though we're
893 * not telling the firmware to advance the line, it still does in this one case,
894 * causing a scroll to happen that screws with our beautiful boot loader output.
895 * Since we cannot paint the last character of the edit line, we simply start
896 * at x-offset 1 for symmetry. */
897 print_at(1, y_status
, COLOR_EDIT
, clearline
+ 2);
898 exit
= line_edit(config
->entries
[idx_highlight
]->options
, &config
->options_edit
, x_max
- 2, y_status
);
899 print_at(1, y_status
, COLOR_NORMAL
, clearline
+ 2);
902 case KEYPRESS(0, 0, 'v'):
903 status
= xpool_print(L
"systemd-boot " GIT_VERSION
" (" EFI_MACHINE_TYPE_NAME
"), "
904 L
"UEFI Specification %d.%02d, Vendor %s %d.%02d",
905 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff,
906 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
909 case KEYPRESS(0, 0, 'p'):
910 case KEYPRESS(0, 0, 'P'):
911 print_status(config
, loaded_image_path
);
915 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'l'):
916 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('l')):
920 case KEYPRESS(0, 0, 'r'):
921 err
= console_set_mode(CONSOLE_MODE_NEXT
);
923 status
= xpool_print(L
"Error changing console mode: %r", err
);
925 config
->console_mode_efivar
= ST
->ConOut
->Mode
->Mode
;
926 status
= xpool_print(L
"Console mode changed to %ld.", config
->console_mode_efivar
);
931 case KEYPRESS(0, 0, 'R'):
932 config
->console_mode_efivar
= CONSOLE_MODE_KEEP
;
933 err
= console_set_mode(config
->console_mode
== CONSOLE_MODE_KEEP
?
934 console_mode_initial
: config
->console_mode
);
936 status
= xpool_print(L
"Error resetting console mode: %r", err
);
938 status
= xpool_print(L
"Console mode reset to %s default.",
939 config
->console_mode
== CONSOLE_MODE_KEEP
? L
"firmware" : L
"configuration file");
943 case KEYPRESS(0, 0, 'f'):
944 case KEYPRESS(0, 0, 'F'):
945 case KEYPRESS(0, SCAN_F2
, 0): /* Most vendors. */
946 case KEYPRESS(0, SCAN_F10
, 0): /* HP and Lenovo. */
947 case KEYPRESS(0, SCAN_DELETE
, 0): /* Same as F2. */
948 case KEYPRESS(0, SCAN_ESC
, 0): /* HP. */
949 if (FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
)) {
950 firmware_setup
= TRUE
;
951 /* Let's make sure the user really wants to do this. */
952 status
= xpool_print(L
"Press Enter to reboot into firmware interface.");
954 status
= xpool_print(L
"Reboot into firmware interface not supported.");
958 /* jump with a hotkey directly to a matching entry */
959 idx
= entry_lookup_key(config
, idx_highlight
+1, KEYCHAR(key
));
960 if (idx
== IDX_INVALID
)
966 if (idx_highlight
> idx_last
) {
967 idx_last
= idx_highlight
;
968 idx_first
= 1 + idx_highlight
- visible_max
;
970 } else if (idx_highlight
< idx_first
) {
971 idx_first
= idx_highlight
;
972 idx_last
= idx_highlight
+ visible_max
-1;
976 if (!refresh
&& idx_highlight
!= idx_highlight_prev
)
980 *chosen_entry
= config
->entries
[idx_highlight
];
982 /* Update EFI vars after we left the menu to reduce NVRAM writes. */
984 if (default_efivar_saved
!= config
->idx_default_efivar
)
985 efivar_set(LOADER_GUID
, L
"LoaderEntryDefault", config
->entry_default_efivar
, EFI_VARIABLE_NON_VOLATILE
);
987 if (console_mode_efivar_saved
!= config
->console_mode_efivar
) {
988 if (config
->console_mode_efivar
== CONSOLE_MODE_KEEP
)
989 efivar_set(LOADER_GUID
, L
"LoaderConfigConsoleMode", NULL
, EFI_VARIABLE_NON_VOLATILE
);
991 efivar_set_uint_string(LOADER_GUID
, L
"LoaderConfigConsoleMode",
992 config
->console_mode_efivar
, EFI_VARIABLE_NON_VOLATILE
);
995 if (timeout_efivar_saved
!= config
->timeout_sec_efivar
) {
996 if (config
->timeout_sec_efivar
== TIMEOUT_UNSET
)
997 efivar_set(LOADER_GUID
, L
"LoaderConfigTimeout", NULL
, EFI_VARIABLE_NON_VOLATILE
);
999 efivar_set_uint_string(LOADER_GUID
, L
"LoaderConfigTimeout",
1000 config
->timeout_sec_efivar
, EFI_VARIABLE_NON_VOLATILE
);
1003 clear_screen(COLOR_NORMAL
);
1007 static void config_add_entry(Config
*config
, ConfigEntry
*entry
) {
1011 /* This is just for paranoia. */
1012 assert(config
->entry_count
< IDX_MAX
);
1014 if ((config
->entry_count
& 15) == 0) {
1015 UINTN i
= config
->entry_count
+ 16;
1016 config
->entries
= xreallocate_pool(
1018 sizeof(void *) * config
->entry_count
,
1019 sizeof(void *) * i
);
1021 config
->entries
[config
->entry_count
++] = entry
;
1024 static void config_entry_free(ConfigEntry
*entry
) {
1028 FreePool(entry
->id
);
1029 FreePool(entry
->title_show
);
1030 FreePool(entry
->title
);
1031 FreePool(entry
->sort_key
);
1032 FreePool(entry
->version
);
1033 FreePool(entry
->machine_id
);
1034 FreePool(entry
->loader
);
1035 FreePool(entry
->devicetree
);
1036 FreePool(entry
->options
);
1037 FreePool(entry
->path
);
1038 FreePool(entry
->current_name
);
1039 FreePool(entry
->next_name
);
1043 static inline void config_entry_freep(ConfigEntry
**entry
) {
1044 config_entry_free(*entry
);
1047 static CHAR8
*line_get_key_value(
1052 CHAR8
**value_ret
) {
1054 CHAR8
*line
, *value
;
1064 line
= content
+ *pos
;
1069 while (line
[linelen
] && !strchra((CHAR8
*) "\n\r", line
[linelen
]))
1072 /* move pos to next line */
1081 /* terminate line */
1082 line
[linelen
] = '\0';
1084 /* remove leading whitespace */
1085 while (strchra((CHAR8
*) " \t", *line
)) {
1090 /* remove trailing whitespace */
1091 while (linelen
> 0 && strchra((CHAR8
*) " \t", line
[linelen
- 1]))
1093 line
[linelen
] = '\0';
1098 /* split key/value */
1100 while (*value
&& !strchra(sep
, *value
))
1106 while (*value
&& strchra(sep
, *value
))
1110 if (value
[0] == '"' && line
[linelen
- 1] == '"') {
1112 line
[linelen
- 1] = '\0';
1121 static void config_defaults_load_from_file(Config
*config
, CHAR8
*content
) {
1130 while ((line
= line_get_key_value(content
, (CHAR8
*)" \t", &pos
, &key
, &value
))) {
1131 if (strcmpa((CHAR8
*)"timeout", key
) == 0) {
1132 if (strcmpa((CHAR8
*) "menu-force", value
) == 0)
1133 config
->timeout_sec_config
= TIMEOUT_MENU_FORCE
;
1134 else if (strcmpa((CHAR8
*) "menu-hidden", value
) == 0)
1135 config
->timeout_sec_config
= TIMEOUT_MENU_HIDDEN
;
1137 _cleanup_freepool_ CHAR16
*s
= NULL
;
1139 s
= xstra_to_str(value
);
1140 config
->timeout_sec_config
= MIN(Atoi(s
), TIMEOUT_TYPE_MAX
);
1142 config
->timeout_sec
= config
->timeout_sec_config
;
1146 if (strcmpa((CHAR8
*)"default", key
) == 0) {
1147 if (value
[0] == '@' && strcmpa((CHAR8
*)"@saved", value
) != 0) {
1148 log_error_stall(L
"Unsupported special entry identifier: %a", value
);
1151 FreePool(config
->entry_default_config
);
1152 config
->entry_default_config
= xstra_to_str(value
);
1156 if (strcmpa((CHAR8
*)"editor", key
) == 0) {
1157 err
= parse_boolean(value
, &config
->editor
);
1159 log_error_stall(L
"Error parsing 'editor' config option: %a", value
);
1163 if (strcmpa((CHAR8
*)"auto-entries", key
) == 0) {
1164 err
= parse_boolean(value
, &config
->auto_entries
);
1166 log_error_stall(L
"Error parsing 'auto-entries' config option: %a", value
);
1170 if (strcmpa((CHAR8
*)"auto-firmware", key
) == 0) {
1171 err
= parse_boolean(value
, &config
->auto_firmware
);
1173 log_error_stall(L
"Error parsing 'auto-firmware' config option: %a", value
);
1177 if (strcmpa((CHAR8
*)"beep", key
) == 0) {
1178 err
= parse_boolean(value
, &config
->beep
);
1180 log_error_stall(L
"Error parsing 'beep' config option: %a", value
);
1183 if (strcmpa((CHAR8
*)"reboot-for-bitlocker", key
) == 0) {
1184 err
= parse_boolean(value
, &config
->reboot_for_bitlocker
);
1186 log_error_stall(L
"Error parsing 'reboot-for-bitlocker' config option: %a", value
);
1190 if (strcmpa((CHAR8
*)"console-mode", key
) == 0) {
1191 if (strcmpa((CHAR8
*)"auto", value
) == 0)
1192 config
->console_mode
= CONSOLE_MODE_AUTO
;
1193 else if (strcmpa((CHAR8
*)"max", value
) == 0)
1194 config
->console_mode
= CONSOLE_MODE_FIRMWARE_MAX
;
1195 else if (strcmpa((CHAR8
*)"keep", value
) == 0)
1196 config
->console_mode
= CONSOLE_MODE_KEEP
;
1198 _cleanup_freepool_ CHAR16
*s
= NULL
;
1200 s
= xstra_to_str(value
);
1201 config
->console_mode
= MIN(Atoi(s
), (UINTN
)CONSOLE_MODE_RANGE_MAX
);
1207 if (strcmpa((CHAR8
*) "random-seed-mode", key
) == 0) {
1208 if (strcmpa((CHAR8
*) "off", value
) == 0)
1209 config
->random_seed_mode
= RANDOM_SEED_OFF
;
1210 else if (strcmpa((CHAR8
*) "with-system-token", value
) == 0)
1211 config
->random_seed_mode
= RANDOM_SEED_WITH_SYSTEM_TOKEN
;
1212 else if (strcmpa((CHAR8
*) "always", value
) == 0)
1213 config
->random_seed_mode
= RANDOM_SEED_ALWAYS
;
1217 err
= parse_boolean(value
, &on
);
1218 if (EFI_ERROR(err
)) {
1219 log_error_stall(L
"Error parsing 'random-seed-mode' config option: %a", value
);
1223 config
->random_seed_mode
= on
? RANDOM_SEED_ALWAYS
: RANDOM_SEED_OFF
;
1229 static void config_entry_parse_tries(
1233 const CHAR16
*suffix
) {
1235 UINTN left
= UINTN_MAX
, done
= UINTN_MAX
, factor
= 1, i
, next_left
, next_done
;
1236 _cleanup_freepool_ CHAR16
*prefix
= NULL
;
1243 * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
1244 * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
1245 * which case that counter as assumed to be zero, i.e. the missing part is synonymous to "-0").
1247 * Names we grok, and the series they result in:
1249 * foobar+3.efi → foobar+2-1.efi → foobar+1-2.efi → foobar+0-3.efi → STOP!
1250 * foobar+4-0.efi → foobar+3-1.efi → foobar+2-2.efi → foobar+1-3.efi → foobar+0-4.efi → STOP!
1255 /* Chop off any suffix such as ".conf" or ".efi" */
1257 UINTN suffix_length
;
1259 suffix_length
= StrLen(suffix
);
1260 if (i
< suffix_length
)
1266 /* Go backwards through the string and parse everything we encounter */
1276 if (left
== UINTN_MAX
) /* didn't read at least one digit for 'left'? */
1279 if (done
== UINTN_MAX
) /* no 'done' counter? If so, it's equivalent to 0 */
1285 if (left
== UINTN_MAX
) /* didn't parse any digit yet? */
1288 if (done
!= UINTN_MAX
) /* already encountered a dash earlier? */
1291 /* So we encountered a dash. This means this counter is of the form +LEFT-DONE. Let's assign
1292 * what we already parsed to 'done', and start fresh for the 'left' part. */
1302 if (left
== UINTN_MAX
)
1303 left
= file
[i
] - '0';
1305 UINTN new_left
, digit
;
1307 digit
= file
[i
] - '0';
1308 if (digit
> UINTN_MAX
/ factor
) /* overflow check */
1311 new_left
= left
+ digit
* factor
;
1312 if (new_left
< left
) /* overflow check */
1315 if (new_left
== UINTN_MAX
) /* don't allow us to be confused */
1319 new_factor
= factor
* 10;
1320 if (new_factor
< factor
) /* overflow check */
1323 factor
= new_factor
;
1333 entry
->tries_left
= left
;
1334 entry
->tries_done
= done
;
1336 entry
->path
= xstrdup(path
);
1337 entry
->current_name
= xstrdup(file
);
1339 next_left
= left
<= 0 ? 0 : left
- 1;
1340 next_done
= done
>= (UINTN
) -2 ? (UINTN
) -2 : done
+ 1;
1342 prefix
= xstrdup(file
);
1345 entry
->next_name
= xpool_print(L
"%s+%u-%u%s", prefix
, next_left
, next_done
, suffix
?: L
"");
1348 static void config_entry_bump_counters(ConfigEntry
*entry
, EFI_FILE
*root_dir
) {
1349 _cleanup_freepool_ CHAR16
* old_path
= NULL
, *new_path
= NULL
;
1350 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1351 _cleanup_freepool_ EFI_FILE_INFO
*file_info
= NULL
;
1352 UINTN file_info_size
;
1358 if (entry
->tries_left
== UINTN_MAX
)
1361 if (!entry
->path
|| !entry
->current_name
|| !entry
->next_name
)
1364 old_path
= xpool_print(L
"%s\\%s", entry
->path
, entry
->current_name
);
1366 err
= root_dir
->Open(root_dir
, &handle
, old_path
, EFI_FILE_MODE_READ
|EFI_FILE_MODE_WRITE
, 0ULL);
1370 err
= get_file_info_harder(handle
, &file_info
, &file_info_size
);
1374 /* And rename the file */
1375 StrCpy(file_info
->FileName
, entry
->next_name
);
1376 err
= handle
->SetInfo(handle
, &GenericFileInfo
, file_info_size
, file_info
);
1377 if (EFI_ERROR(err
)) {
1378 log_error_stall(L
"Failed to rename '%s' to '%s', ignoring: %r", old_path
, entry
->next_name
, err
);
1382 /* Flush everything to disk, just in case… */
1383 (void) handle
->Flush(handle
);
1385 /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
1387 new_path
= xpool_print(L
"%s\\%s", entry
->path
, entry
->next_name
);
1388 efivar_set(LOADER_GUID
, L
"LoaderBootCountPath", new_path
, 0);
1390 /* If the file we just renamed is the loader path, then let's update that. */
1391 if (StrCmp(entry
->loader
, old_path
) == 0) {
1392 FreePool(entry
->loader
);
1393 entry
->loader
= TAKE_PTR(new_path
);
1397 static void config_entry_add_from_file(
1404 const CHAR16
*loaded_image_path
) {
1406 _cleanup_(config_entry_freep
) ConfigEntry
*entry
= NULL
;
1411 _cleanup_freepool_ CHAR16
*initrd
= NULL
;
1420 entry
= xnew(ConfigEntry
, 1);
1421 *entry
= (ConfigEntry
) {
1422 .tries_done
= UINTN_MAX
,
1423 .tries_left
= UINTN_MAX
,
1426 while ((line
= line_get_key_value(content
, (CHAR8
*)" \t", &pos
, &key
, &value
))) {
1427 if (strcmpa((CHAR8
*)"title", key
) == 0) {
1428 FreePool(entry
->title
);
1429 entry
->title
= xstra_to_str(value
);
1433 if (strcmpa((CHAR8
*)"sort-key", key
) == 0) {
1434 FreePool(entry
->sort_key
);
1435 entry
->sort_key
= xstra_to_str(value
);
1439 if (strcmpa((CHAR8
*)"version", key
) == 0) {
1440 FreePool(entry
->version
);
1441 entry
->version
= xstra_to_str(value
);
1445 if (strcmpa((CHAR8
*)"machine-id", key
) == 0) {
1446 FreePool(entry
->machine_id
);
1447 entry
->machine_id
= xstra_to_str(value
);
1451 if (strcmpa((CHAR8
*)"linux", key
) == 0) {
1452 FreePool(entry
->loader
);
1453 entry
->type
= LOADER_LINUX
;
1454 entry
->loader
= xstra_to_path(value
);
1459 if (strcmpa((CHAR8
*)"efi", key
) == 0) {
1460 entry
->type
= LOADER_EFI
;
1461 FreePool(entry
->loader
);
1462 entry
->loader
= xstra_to_path(value
);
1464 /* do not add an entry for ourselves */
1465 if (loaded_image_path
&& StriCmp(entry
->loader
, loaded_image_path
) == 0) {
1466 entry
->type
= LOADER_UNDEFINED
;
1472 if (strcmpa((CHAR8
*)"architecture", key
) == 0) {
1473 /* do not add an entry for an EFI image of architecture not matching with that of the image */
1474 if (strcmpa((CHAR8
*)EFI_MACHINE_TYPE_NAME
, value
) != 0) {
1475 entry
->type
= LOADER_UNDEFINED
;
1481 if (strcmpa((CHAR8
*)"devicetree", key
) == 0) {
1482 FreePool(entry
->devicetree
);
1483 entry
->devicetree
= xstra_to_path(value
);
1487 if (strcmpa((CHAR8
*)"initrd", key
) == 0) {
1488 _cleanup_freepool_ CHAR16
*new = NULL
;
1490 new = xstra_to_path(value
);
1494 s
= xpool_print(L
"%s initrd=%s", initrd
, new);
1498 initrd
= xpool_print(L
"initrd=%s", new);
1503 if (strcmpa((CHAR8
*)"options", key
) == 0) {
1504 _cleanup_freepool_ CHAR16
*new = NULL
;
1506 new = xstra_to_str(value
);
1507 if (entry
->options
) {
1510 s
= xpool_print(L
"%s %s", entry
->options
, new);
1511 FreePool(entry
->options
);
1514 entry
->options
= TAKE_PTR(new);
1520 if (entry
->type
== LOADER_UNDEFINED
)
1523 /* check existence */
1524 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1525 err
= root_dir
->Open(root_dir
, &handle
, entry
->loader
, EFI_FILE_MODE_READ
, 0ULL);
1529 /* add initrd= to options */
1530 if (entry
->type
== LOADER_LINUX
&& initrd
) {
1531 if (entry
->options
) {
1534 s
= xpool_print(L
"%s %s", initrd
, entry
->options
);
1535 FreePool(entry
->options
);
1538 entry
->options
= TAKE_PTR(initrd
);
1541 entry
->device
= device
;
1542 entry
->id
= xstrdup(file
);
1545 config_add_entry(config
, entry
);
1547 config_entry_parse_tries(entry
, path
, file
, L
".conf");
1551 static void config_load_defaults(Config
*config
, EFI_FILE
*root_dir
) {
1552 _cleanup_freepool_ CHAR8
*content
= NULL
;
1558 *config
= (Config
) {
1560 .auto_entries
= TRUE
,
1561 .auto_firmware
= TRUE
,
1562 .reboot_for_bitlocker
= FALSE
,
1563 .random_seed_mode
= RANDOM_SEED_WITH_SYSTEM_TOKEN
,
1564 .idx_default_efivar
= IDX_INVALID
,
1565 .console_mode
= CONSOLE_MODE_KEEP
,
1566 .console_mode_efivar
= CONSOLE_MODE_KEEP
,
1567 .timeout_sec_config
= TIMEOUT_UNSET
,
1568 .timeout_sec_efivar
= TIMEOUT_UNSET
,
1571 err
= file_read(root_dir
, L
"\\loader\\loader.conf", 0, 0, &content
, NULL
);
1572 if (!EFI_ERROR(err
))
1573 config_defaults_load_from_file(config
, content
);
1575 err
= efivar_get_uint_string(LOADER_GUID
, L
"LoaderConfigTimeout", &value
);
1576 if (!EFI_ERROR(err
)) {
1577 config
->timeout_sec_efivar
= MIN(value
, TIMEOUT_TYPE_MAX
);
1578 config
->timeout_sec
= config
->timeout_sec_efivar
;
1581 err
= efivar_get_uint_string(LOADER_GUID
, L
"LoaderConfigTimeoutOneShot", &value
);
1582 if (!EFI_ERROR(err
)) {
1583 /* Unset variable now, after all it's "one shot". */
1584 (void) efivar_set(LOADER_GUID
, L
"LoaderConfigTimeoutOneShot", NULL
, EFI_VARIABLE_NON_VOLATILE
);
1586 config
->timeout_sec
= MIN(value
, TIMEOUT_TYPE_MAX
);
1587 config
->force_menu
= TRUE
; /* force the menu when this is set */
1590 err
= efivar_get_uint_string(LOADER_GUID
, L
"LoaderConfigConsoleMode", &value
);
1591 if (!EFI_ERROR(err
))
1592 config
->console_mode_efivar
= value
;
1594 err
= efivar_get(LOADER_GUID
, L
"LoaderEntryOneShot", &config
->entry_oneshot
);
1595 if (!EFI_ERROR(err
))
1596 /* Unset variable now, after all it's "one shot". */
1597 (void) efivar_set(LOADER_GUID
, L
"LoaderEntryOneShot", NULL
, EFI_VARIABLE_NON_VOLATILE
);
1599 (void) efivar_get(LOADER_GUID
, L
"LoaderEntryDefault", &config
->entry_default_efivar
);
1601 config
->use_saved_entry
= streq_ptr(config
->entry_default_config
, L
"@saved");
1602 config
->use_saved_entry_efivar
= streq_ptr(config
->entry_default_efivar
, L
"@saved");
1603 if (config
->use_saved_entry
|| config
->use_saved_entry_efivar
)
1604 (void) efivar_get(LOADER_GUID
, L
"LoaderEntryLastBooted", &config
->entry_saved
);
1607 static void config_load_entries(
1611 const CHAR16
*loaded_image_path
) {
1613 _cleanup_(file_closep
) EFI_FILE
*entries_dir
= NULL
;
1614 _cleanup_freepool_ EFI_FILE_INFO
*f
= NULL
;
1622 /* Adds Boot Loader Type #1 entries (i.e. /loader/entries/….conf) */
1624 err
= open_directory(root_dir
, L
"\\loader\\entries", &entries_dir
);
1629 _cleanup_freepool_ CHAR8
*content
= NULL
;
1631 err
= readdir_harder(entries_dir
, &f
, &f_size
);
1632 if (EFI_ERROR(err
) || !f
)
1635 if (f
->FileName
[0] == '.')
1637 if (FLAGS_SET(f
->Attribute
, EFI_FILE_DIRECTORY
))
1640 if (!endswith_no_case(f
->FileName
, L
".conf"))
1642 if (startswith(f
->FileName
, L
"auto-"))
1645 err
= file_read(entries_dir
, f
->FileName
, 0, 0, &content
, NULL
);
1646 if (!EFI_ERROR(err
))
1647 config_entry_add_from_file(config
, device
, root_dir
, L
"\\loader\\entries", f
->FileName
, content
, loaded_image_path
);
1651 static INTN
config_entry_compare(const ConfigEntry
*a
, const ConfigEntry
*b
) {
1657 /* Order entries that have no tries left to the end of the list */
1658 r
= CMP(a
->tries_left
== 0, b
->tries_left
== 0);
1662 /* If there's a sort key defined for *both* entries, then we do new-style ordering, i.e. by
1663 * sort-key/machine-id/version, with a final fallback to id. If there's no sort key for either, we do
1664 * old-style ordering, i.e. by id only. If one has sort key and the other does not, we put new-style
1665 * before old-style. */
1666 r
= CMP(!a
->sort_key
, !b
->sort_key
);
1667 if (r
!= 0) /* one is old-style, one new-style */
1670 if (a
->sort_key
&& b
->sort_key
) {
1671 r
= strcmp(a
->sort_key
, b
->sort_key
);
1675 /* If multiple installations of the same OS are around, group by machine ID */
1676 r
= strcmp_ptr(a
->machine_id
, b
->machine_id
);
1680 /* If the sort key was defined, then order by version now (downwards, putting the newest first) */
1681 r
= -strverscmp_improved(a
->version
, b
->version
);
1686 /* Now order by ID. The version is likely part of the ID, thus note that this will generatelly put
1687 * the newer versions earlier. Specifying a sort key explicitly is preferable, because it gives an
1688 * explicit sort order. */
1689 r
= -strverscmp_improved(a
->id
, b
->id
);
1693 if (a
->tries_left
== UINTN_MAX
|| b
->tries_left
== UINTN_MAX
)
1696 /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
1697 r
= -CMP(a
->tries_left
, b
->tries_left
);
1701 /* If they have the same number of tries left, then let the one win which was tried fewer times so far */
1702 return CMP(a
->tries_done
, b
->tries_done
);
1705 static UINTN
config_entry_find(Config
*config
, const CHAR16
*needle
) {
1711 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
1712 if (MetaiMatch(config
->entries
[i
]->id
, (CHAR16
*) needle
))
1718 static void config_default_entry_select(Config
*config
) {
1723 i
= config_entry_find(config
, config
->entry_oneshot
);
1724 if (i
!= IDX_INVALID
) {
1725 config
->idx_default
= i
;
1729 i
= config_entry_find(config
, config
->use_saved_entry_efivar
? config
->entry_saved
: config
->entry_default_efivar
);
1730 if (i
!= IDX_INVALID
) {
1731 config
->idx_default
= i
;
1732 config
->idx_default_efivar
= i
;
1736 if (config
->use_saved_entry
)
1737 /* No need to do the same thing twice. */
1738 i
= config
->use_saved_entry_efivar
? IDX_INVALID
: config_entry_find(config
, config
->entry_saved
);
1740 i
= config_entry_find(config
, config
->entry_default_config
);
1741 if (i
!= IDX_INVALID
) {
1742 config
->idx_default
= i
;
1746 /* select the first suitable entry */
1747 for (i
= 0; i
< config
->entry_count
; i
++) {
1748 if (config
->entries
[i
]->type
== LOADER_AUTO
|| config
->entries
[i
]->call
)
1750 config
->idx_default
= i
;
1754 /* If no configured entry to select from was found, enable the menu. */
1755 config
->idx_default
= 0;
1756 if (config
->timeout_sec
== 0)
1757 config
->timeout_sec
= 10;
1760 static BOOLEAN
entries_unique(ConfigEntry
**entries
, BOOLEAN
*unique
, UINTN entry_count
) {
1761 BOOLEAN is_unique
= TRUE
;
1766 for (UINTN i
= 0; i
< entry_count
; i
++)
1767 for (UINTN k
= i
+ 1; k
< entry_count
; k
++) {
1768 if (StrCmp(entries
[i
]->title_show
, entries
[k
]->title_show
) != 0)
1771 is_unique
= unique
[i
] = unique
[k
] = FALSE
;
1777 /* generate a unique title, avoiding non-distinguishable menu entries */
1778 static void config_title_generate(Config
*config
) {
1781 BOOLEAN unique
[config
->entry_count
];
1784 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1785 assert(!config
->entries
[i
]->title_show
);
1787 config
->entries
[i
]->title_show
= xstrdup(config
->entries
[i
]->title
?: config
->entries
[i
]->id
);
1790 if (entries_unique(config
->entries
, unique
, config
->entry_count
))
1793 /* add version to non-unique titles */
1794 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1800 if (!config
->entries
[i
]->version
)
1803 _cleanup_freepool_ CHAR16
*t
= config
->entries
[i
]->title_show
;
1804 config
->entries
[i
]->title_show
= xpool_print(L
"%s (%s)", t
, config
->entries
[i
]->version
);
1807 if (entries_unique(config
->entries
, unique
, config
->entry_count
))
1810 /* add machine-id to non-unique titles */
1811 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1817 if (!config
->entries
[i
]->machine_id
)
1820 _cleanup_freepool_ CHAR16
*t
= config
->entries
[i
]->title_show
;
1821 config
->entries
[i
]->title_show
= xpool_print(
1824 StrnLen(config
->entries
[i
]->machine_id
, 8),
1825 config
->entries
[i
]->machine_id
);
1828 if (entries_unique(config
->entries
, unique
, config
->entry_count
))
1831 /* add file name to non-unique titles */
1832 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1836 _cleanup_freepool_ CHAR16
*t
= config
->entries
[i
]->title_show
;
1837 config
->entries
[i
]->title_show
= xpool_print(L
"%s (%s)", t
, config
->entries
[i
]->id
);
1841 static BOOLEAN
config_entry_add_call(
1844 const CHAR16
*title
,
1845 EFI_STATUS (*call
)(void)) {
1854 entry
= xnew(ConfigEntry
, 1);
1855 *entry
= (ConfigEntry
) {
1857 .title
= xstrdup(title
),
1859 .tries_done
= UINTN_MAX
,
1860 .tries_left
= UINTN_MAX
,
1863 config_add_entry(config
, entry
);
1867 static ConfigEntry
*config_entry_add_loader(
1870 enum loader_type type
,
1873 const CHAR16
*title
,
1874 const CHAR16
*loader
,
1875 const CHAR16
*sort_key
,
1876 const CHAR16
*version
) {
1886 entry
= xnew(ConfigEntry
, 1);
1887 *entry
= (ConfigEntry
) {
1889 .title
= xstrdup(title
),
1890 .version
= version
? xstrdup(version
) : NULL
,
1892 .loader
= xstrdup(loader
),
1894 .sort_key
= xstrdup(sort_key
),
1896 .tries_done
= UINTN_MAX
,
1897 .tries_left
= UINTN_MAX
,
1902 config_add_entry(config
, entry
);
1906 static BOOLEAN
is_sd_boot(EFI_FILE
*root_dir
, const CHAR16
*loader_path
) {
1908 const CHAR8
*sections
[] = {
1909 (CHAR8
*)".sdmagic",
1912 UINTN offset
= 0, size
= 0, read
;
1913 _cleanup_freepool_ CHAR8
*content
= NULL
;
1916 assert(loader_path
);
1918 err
= pe_file_locate_sections(root_dir
, loader_path
, sections
, &offset
, &size
);
1919 if (EFI_ERROR(err
) || size
!= sizeof(magic
))
1922 err
= file_read(root_dir
, loader_path
, offset
, size
, &content
, &read
);
1923 if (EFI_ERROR(err
) || size
!= read
)
1926 return CompareMem(content
, magic
, sizeof(magic
)) == 0;
1929 static ConfigEntry
*config_entry_add_loader_auto(
1933 const CHAR16
*loaded_image_path
,
1936 const CHAR16
*title
,
1937 const CHAR16
*loader
) {
1944 assert(loader
|| loaded_image_path
);
1946 if (!config
->auto_entries
)
1949 if (loaded_image_path
) {
1950 loader
= L
"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME
".efi";
1952 /* We are trying to add the default EFI loader here,
1953 * but we do not want to do that if that would be us.
1955 * If the default loader is not us, it might be shim. It would
1956 * chainload GRUBX64.EFI in that case, which might be us.*/
1957 if (StriCmp(loader
, loaded_image_path
) == 0 ||
1958 is_sd_boot(root_dir
, loader
) ||
1959 is_sd_boot(root_dir
, L
"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L
".EFI"))
1963 /* check existence */
1964 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1965 EFI_STATUS err
= root_dir
->Open(root_dir
, &handle
, (CHAR16
*) loader
, EFI_FILE_MODE_READ
, 0ULL);
1969 return config_entry_add_loader(config
, device
, LOADER_AUTO
, id
, key
, title
, loader
, NULL
, NULL
);
1972 static void config_entry_add_osx(Config
*config
) {
1974 UINTN n_handles
= 0;
1975 _cleanup_freepool_ EFI_HANDLE
*handles
= NULL
;
1979 if (!config
->auto_entries
)
1982 err
= LibLocateHandle(ByProtocol
, &FileSystemProtocol
, NULL
, &n_handles
, &handles
);
1986 for (UINTN i
= 0; i
< n_handles
; i
++) {
1987 _cleanup_(file_closep
) EFI_FILE
*root
= LibOpenRoot(handles
[i
]);
1991 if (config_entry_add_loader_auto(
1999 L
"\\System\\Library\\CoreServices\\boot.efi"))
2004 static EFI_STATUS
boot_windows_bitlocker(void) {
2005 _cleanup_freepool_ EFI_HANDLE
*handles
= NULL
;
2009 // FIXME: Experimental for now. Should be generalized, and become a per-entry option that can be
2010 // enabled independently of BitLocker, and without a BootXXXX entry pre-existing.
2012 /* BitLocker key cannot be sealed without a TPM present. */
2014 return EFI_NOT_FOUND
;
2016 err
= BS
->LocateHandleBuffer(ByProtocol
, &BlockIoProtocol
, NULL
, &n_handles
, &handles
);
2020 /* Look for BitLocker magic string on all block drives. */
2021 BOOLEAN found
= FALSE
;
2022 for (UINTN i
= 0; i
< n_handles
; i
++) {
2023 EFI_BLOCK_IO
*block_io
;
2024 err
= BS
->HandleProtocol(handles
[i
], &BlockIoProtocol
, (void **) &block_io
);
2025 if (EFI_ERROR(err
) || block_io
->Media
->BlockSize
< 512)
2028 CHAR8 buf
[block_io
->Media
->BlockSize
];
2029 err
= block_io
->ReadBlocks(block_io
, block_io
->Media
->MediaId
, 0, sizeof(buf
), buf
);
2033 if (CompareMem(buf
+ 3, "-FVE-FS-", STRLEN("-FVE-FS-")) == 0) {
2039 /* If no BitLocker drive was found, we can just chainload bootmgfw.efi directly. */
2041 return EFI_NOT_FOUND
;
2043 _cleanup_freepool_ UINT16
*boot_order
= NULL
;
2044 UINTN boot_order_size
;
2046 /* There can be gaps in Boot#### entries. Instead of iterating over the full
2047 * EFI var list or UINT16 namespace, just look for "Windows Boot Manager" in BootOrder. */
2048 err
= efivar_get_raw(EFI_GLOBAL_GUID
, L
"BootOrder", (CHAR8
**) &boot_order
, &boot_order_size
);
2049 if (EFI_ERROR(err
) || boot_order_size
% sizeof(UINT16
) != 0)
2052 for (UINTN i
= 0; i
< boot_order_size
/ sizeof(UINT16
); i
++) {
2053 _cleanup_freepool_ CHAR8
*buf
= NULL
;
2054 CHAR16 name
[sizeof(L
"Boot0000")];
2057 SPrint(name
, sizeof(name
), L
"Boot%04x", boot_order
[i
]);
2058 err
= efivar_get_raw(EFI_GLOBAL_GUID
, name
, &buf
, &buf_size
);
2062 /* Boot#### are EFI_LOAD_OPTION. But we really are only interested
2063 * for the description, which is at this offset. */
2064 UINTN offset
= sizeof(UINT32
) + sizeof(UINT16
);
2065 if (buf_size
< offset
+ sizeof(CHAR16
))
2068 if (streq((CHAR16
*) (buf
+ offset
), L
"Windows Boot Manager")) {
2069 err
= efivar_set_raw(
2073 sizeof(boot_order
[i
]),
2074 EFI_VARIABLE_NON_VOLATILE
);
2077 return RT
->ResetSystem(EfiResetWarm
, EFI_SUCCESS
, 0, NULL
);
2081 return EFI_NOT_FOUND
;
2084 static void config_entry_add_windows(Config
*config
, EFI_HANDLE
*device
, EFI_FILE
*root_dir
) {
2085 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
2086 _cleanup_freepool_ CHAR8
*bcd
= NULL
;
2087 CHAR16
*title
= NULL
;
2095 if (!config
->auto_entries
)
2098 /* Try to find a better title. */
2099 err
= file_read(root_dir
, L
"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd
, &len
);
2100 if (!EFI_ERROR(err
))
2101 title
= get_bcd_title((UINT8
*) bcd
, len
);
2103 ConfigEntry
*e
= config_entry_add_loader_auto(config
, device
, root_dir
, NULL
,
2104 L
"auto-windows", 'w', title
?: L
"Windows Boot Manager",
2105 L
"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2107 if (config
->reboot_for_bitlocker
)
2108 e
->call
= boot_windows_bitlocker
;
2112 static void config_entry_add_linux(
2115 EFI_FILE
*root_dir
) {
2117 _cleanup_(file_closep
) EFI_FILE
*linux_dir
= NULL
;
2118 _cleanup_freepool_ EFI_FILE_INFO
*f
= NULL
;
2123 /* Adds Boot Loader Type #2 entries (i.e. /EFI/Linux/….efi) */
2129 err
= open_directory(root_dir
, L
"\\EFI\\Linux", &linux_dir
);
2140 static const CHAR8
* const sections
[_SECTION_MAX
+ 1] = {
2141 [SECTION_CMDLINE
] = (const CHAR8
*) ".cmdline",
2142 [SECTION_OSREL
] = (const CHAR8
*) ".osrel",
2146 _cleanup_freepool_ CHAR16
*os_pretty_name
= NULL
, *os_image_id
= NULL
, *os_name
= NULL
, *os_id
= NULL
,
2147 *os_image_version
= NULL
, *os_version
= NULL
, *os_version_id
= NULL
, *os_build_id
= NULL
,
2149 const CHAR16
*good_name
, *good_version
, *good_sort_key
;
2150 _cleanup_freepool_ CHAR8
*content
= NULL
;
2151 UINTN offs
[_SECTION_MAX
] = {};
2152 UINTN szs
[_SECTION_MAX
] = {};
2157 err
= readdir_harder(linux_dir
, &f
, &f_size
);
2158 if (EFI_ERROR(err
) || !f
)
2161 if (f
->FileName
[0] == '.')
2163 if (FLAGS_SET(f
->Attribute
, EFI_FILE_DIRECTORY
))
2165 if (!endswith_no_case(f
->FileName
, L
".efi"))
2167 if (startswith(f
->FileName
, L
"auto-"))
2170 /* look for .osrel and .cmdline sections in the .efi binary */
2171 err
= pe_file_locate_sections(linux_dir
, f
->FileName
, (const CHAR8
**) sections
, offs
, szs
);
2172 if (EFI_ERROR(err
) || szs
[SECTION_OSREL
] == 0)
2175 err
= file_read(linux_dir
, f
->FileName
, offs
[SECTION_OSREL
], szs
[SECTION_OSREL
], &content
, NULL
);
2179 /* read properties from the embedded os-release file */
2180 while ((line
= line_get_key_value(content
, (CHAR8
*)"=", &pos
, &key
, &value
))) {
2181 if (strcmpa((const CHAR8
*) "PRETTY_NAME", key
) == 0) {
2182 FreePool(os_pretty_name
);
2183 os_pretty_name
= xstra_to_str(value
);
2187 if (strcmpa((const CHAR8
*) "IMAGE_ID", key
) == 0) {
2188 FreePool(os_image_id
);
2189 os_image_id
= xstra_to_str(value
);
2193 if (strcmpa((const CHAR8
*) "NAME", key
) == 0) {
2195 os_name
= xstra_to_str(value
);
2199 if (strcmpa((const CHAR8
*) "ID", key
) == 0) {
2201 os_id
= xstra_to_str(value
);
2205 if (strcmpa((const CHAR8
*) "IMAGE_VERSION", key
) == 0) {
2206 FreePool(os_image_version
);
2207 os_image_version
= xstra_to_str(value
);
2211 if (strcmpa((const CHAR8
*) "VERSION", key
) == 0) {
2212 FreePool(os_version
);
2213 os_version
= xstra_to_str(value
);
2217 if (strcmpa((const CHAR8
*) "VERSION_ID", key
) == 0) {
2218 FreePool(os_version_id
);
2219 os_version_id
= xstra_to_str(value
);
2223 if (strcmpa((const CHAR8
*) "BUILD_ID", key
) == 0) {
2224 FreePool(os_build_id
);
2225 os_build_id
= xstra_to_str(value
);
2230 if (!bootspec_pick_name_version_sort_key(
2244 path
= xpool_print(L
"\\EFI\\Linux\\%s", f
->FileName
);
2245 entry
= config_entry_add_loader(
2248 LOADER_UNIFIED_LINUX
,
2249 /* id= */ f
->FileName
,
2251 /* title= */ good_name
,
2253 /* sort_key= */ good_sort_key
,
2256 config_entry_parse_tries(entry
, L
"\\EFI\\Linux", f
->FileName
, L
".efi");
2258 if (szs
[SECTION_CMDLINE
] == 0)
2261 content
= mfree(content
);
2263 /* read the embedded cmdline file */
2264 err
= file_read(linux_dir
, f
->FileName
, offs
[SECTION_CMDLINE
], szs
[SECTION_CMDLINE
], &content
, NULL
);
2265 if (!EFI_ERROR(err
)) {
2266 /* chomp the newline */
2267 if (content
[szs
[SECTION_CMDLINE
] - 1] == '\n')
2268 content
[szs
[SECTION_CMDLINE
] - 1] = '\0';
2270 entry
->options
= xstra_to_str(content
);
2275 static void config_load_xbootldr(
2277 EFI_HANDLE
*device
) {
2279 _cleanup_(file_closep
) EFI_FILE
*root_dir
= NULL
;
2280 EFI_HANDLE new_device
;
2286 err
= xbootldr_open(device
, &new_device
, &root_dir
);
2290 config_entry_add_linux(config
, new_device
, root_dir
);
2291 config_load_entries(config
, new_device
, root_dir
, NULL
);
2294 static EFI_STATUS
image_start(
2296 EFI_HANDLE parent_image
,
2297 const Config
*config
,
2298 const ConfigEntry
*entry
) {
2300 _cleanup_(devicetree_cleanup
) struct devicetree_state dtstate
= {};
2302 _cleanup_freepool_ EFI_DEVICE_PATH
*path
= NULL
;
2309 /* If this loader entry has a special way to boot, try that first. */
2311 (void) entry
->call();
2313 path
= FileDevicePath(entry
->device
, entry
->loader
);
2315 return log_error_status_stall(EFI_INVALID_PARAMETER
, L
"Error getting device path.");
2317 err
= BS
->LoadImage(FALSE
, parent_image
, path
, NULL
, 0, &image
);
2319 return log_error_status_stall(err
, L
"Error loading %s: %r", entry
->loader
, err
);
2321 if (entry
->devicetree
) {
2322 err
= devicetree_install(&dtstate
, root_dir
, entry
->devicetree
);
2324 return log_error_status_stall(err
, L
"Error loading %s: %r", entry
->devicetree
, err
);
2327 if (config
->options_edit
)
2328 options
= config
->options_edit
;
2329 else if (entry
->options
)
2330 options
= entry
->options
;
2334 EFI_LOADED_IMAGE
*loaded_image
;
2336 err
= BS
->OpenProtocol(image
, &LoadedImageProtocol
, (void **)&loaded_image
,
2337 parent_image
, NULL
, EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
2338 if (EFI_ERROR(err
)) {
2339 log_error_stall(L
"Error getting LoadedImageProtocol handle: %r", err
);
2342 loaded_image
->LoadOptions
= options
;
2343 loaded_image
->LoadOptionsSize
= StrSize(loaded_image
->LoadOptions
);
2345 /* Try to log any options to the TPM, especially to catch manually edited options */
2346 (void) tpm_log_load_options(options
);
2349 efivar_set_time_usec(LOADER_GUID
, L
"LoaderTimeExecUSec", 0);
2350 err
= BS
->StartImage(image
, NULL
, NULL
);
2352 BS
->UnloadImage(image
);
2356 static void config_free(Config
*config
) {
2358 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
2359 config_entry_free(config
->entries
[i
]);
2360 FreePool(config
->entries
);
2361 FreePool(config
->entry_default_config
);
2362 FreePool(config
->options_edit
);
2363 FreePool(config
->entry_oneshot
);
2366 static void config_write_entries_to_variable(Config
*config
) {
2367 _cleanup_freepool_ CHAR8
*buffer
= NULL
;
2373 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
2374 sz
+= StrSize(config
->entries
[i
]->id
);
2376 p
= buffer
= xallocate_pool(sz
);
2378 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
2381 l
= StrSize(config
->entries
[i
]->id
);
2382 CopyMem(p
, config
->entries
[i
]->id
, l
);
2387 assert(p
== buffer
+ sz
);
2389 /* Store the full list of discovered entries. */
2390 (void) efivar_set_raw(LOADER_GUID
, L
"LoaderEntries", buffer
, sz
, 0);
2393 static void save_selected_entry(const Config
*config
, const ConfigEntry
*entry
) {
2396 assert(entry
->loader
|| !entry
->call
);
2398 /* Always export the selected boot entry to the system in a volatile var. */
2399 (void) efivar_set(LOADER_GUID
, L
"LoaderEntrySelected", entry
->id
, 0);
2401 /* Do not save or delete if this was a oneshot boot. */
2402 if (streq_ptr(config
->entry_oneshot
, entry
->id
))
2405 if (config
->use_saved_entry_efivar
|| (!config
->entry_default_efivar
&& config
->use_saved_entry
)) {
2406 /* Avoid unnecessary NVRAM writes. */
2407 if (streq_ptr(config
->entry_saved
, entry
->id
))
2410 (void) efivar_set(LOADER_GUID
, L
"LoaderEntryLastBooted", entry
->id
, EFI_VARIABLE_NON_VOLATILE
);
2412 /* Delete the non-volatile var if not needed. */
2413 (void) efivar_set(LOADER_GUID
, L
"LoaderEntryLastBooted", NULL
, EFI_VARIABLE_NON_VOLATILE
);
2416 static void export_variables(
2417 EFI_LOADED_IMAGE
*loaded_image
,
2418 const CHAR16
*loaded_image_path
,
2421 static const UINT64 loader_features
=
2422 EFI_LOADER_FEATURE_CONFIG_TIMEOUT
|
2423 EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT
|
2424 EFI_LOADER_FEATURE_ENTRY_DEFAULT
|
2425 EFI_LOADER_FEATURE_ENTRY_ONESHOT
|
2426 EFI_LOADER_FEATURE_BOOT_COUNTING
|
2427 EFI_LOADER_FEATURE_XBOOTLDR
|
2428 EFI_LOADER_FEATURE_RANDOM_SEED
|
2429 EFI_LOADER_FEATURE_LOAD_DRIVER
|
2432 _cleanup_freepool_ CHAR16
*infostr
= NULL
, *typestr
= NULL
;
2435 assert(loaded_image
);
2436 assert(loaded_image_path
);
2438 efivar_set_time_usec(LOADER_GUID
, L
"LoaderTimeInitUSec", init_usec
);
2439 efivar_set(LOADER_GUID
, L
"LoaderInfo", L
"systemd-boot " GIT_VERSION
, 0);
2441 infostr
= xpool_print(L
"%s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
2442 efivar_set(LOADER_GUID
, L
"LoaderFirmwareInfo", infostr
, 0);
2444 typestr
= xpool_print(L
"UEFI %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
2445 efivar_set(LOADER_GUID
, L
"LoaderFirmwareType", typestr
, 0);
2447 (void) efivar_set_uint64_le(LOADER_GUID
, L
"LoaderFeatures", loader_features
, 0);
2449 /* the filesystem path to this image, to prevent adding ourselves to the menu */
2450 efivar_set(LOADER_GUID
, L
"LoaderImageIdentifier", loaded_image_path
, 0);
2452 /* export the device path this image is started from */
2453 if (disk_get_part_uuid(loaded_image
->DeviceHandle
, uuid
) == EFI_SUCCESS
)
2454 efivar_set(LOADER_GUID
, L
"LoaderDevicePartUUID", uuid
, 0);
2457 static void config_load_all_entries(
2459 EFI_LOADED_IMAGE
*loaded_image
,
2460 const CHAR16
*loaded_image_path
,
2461 EFI_FILE
*root_dir
) {
2464 assert(loaded_image
);
2465 assert(loaded_image_path
);
2468 config_load_defaults(config
, root_dir
);
2470 /* scan /EFI/Linux/ directory */
2471 config_entry_add_linux(config
, loaded_image
->DeviceHandle
, root_dir
);
2473 /* scan /loader/entries/\*.conf files */
2474 config_load_entries(config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
);
2476 /* Similar, but on any XBOOTLDR partition */
2477 config_load_xbootldr(config
, loaded_image
->DeviceHandle
);
2479 /* sort entries after version number */
2480 sort_pointer_array((void **) config
->entries
, config
->entry_count
, (compare_pointer_func_t
) config_entry_compare
);
2482 /* if we find some well-known loaders, add them to the end of the list */
2483 config_entry_add_osx(config
);
2484 config_entry_add_windows(config
, loaded_image
->DeviceHandle
, root_dir
);
2485 config_entry_add_loader_auto(config
, loaded_image
->DeviceHandle
, root_dir
, NULL
,
2486 L
"auto-efi-shell", 's', L
"EFI Shell", L
"\\shell" EFI_MACHINE_TYPE_NAME
".efi");
2487 config_entry_add_loader_auto(config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
,
2488 L
"auto-efi-default", '\0', L
"EFI Default Loader", NULL
);
2490 if (config
->auto_firmware
&& FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
))
2491 config_entry_add_call(config
,
2492 L
"auto-reboot-to-firmware-setup",
2493 L
"Reboot Into Firmware Interface",
2494 reboot_into_firmware
);
2496 if (config
->entry_count
== 0)
2499 config_write_entries_to_variable(config
);
2501 config_title_generate(config
);
2503 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
2504 config_default_entry_select(config
);
2507 EFI_STATUS
efi_main(EFI_HANDLE image
, EFI_SYSTEM_TABLE
*sys_table
) {
2508 EFI_LOADED_IMAGE
*loaded_image
;
2509 _cleanup_(file_closep
) EFI_FILE
*root_dir
= NULL
;
2510 _cleanup_(config_free
) Config config
= {};
2511 CHAR16
*loaded_image_path
;
2514 BOOLEAN menu
= FALSE
;
2516 InitializeLib(image
, sys_table
);
2517 init_usec
= time_usec();
2518 debug_hook(L
"systemd-boot");
2519 /* Uncomment the next line if you need to wait for debugger. */
2522 err
= BS
->OpenProtocol(image
,
2523 &LoadedImageProtocol
,
2524 (void **)&loaded_image
,
2527 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
2529 return log_error_status_stall(err
, L
"Error getting a LoadedImageProtocol handle: %r", err
);
2531 loaded_image_path
= DevicePathToStr(loaded_image
->FilePath
);
2532 if (!loaded_image_path
)
2535 export_variables(loaded_image
, loaded_image_path
, init_usec
);
2537 root_dir
= LibOpenRoot(loaded_image
->DeviceHandle
);
2539 return log_error_status_stall(EFI_LOAD_ERROR
, L
"Unable to open root directory.", EFI_LOAD_ERROR
);
2541 if (secure_boot_enabled() && shim_loaded()) {
2542 err
= security_policy_install();
2544 return log_error_status_stall(err
, L
"Error installing security policy: %r", err
);
2547 (void) load_drivers(image
, loaded_image
, root_dir
);
2549 config_load_all_entries(&config
, loaded_image
, loaded_image_path
, root_dir
);
2551 if (config
.entry_count
== 0) {
2552 log_error_stall(L
"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2556 /* select entry or show menu when key is pressed or timeout is set */
2557 if (config
.force_menu
|| config
.timeout_sec
> 0)
2562 /* Block up to 100ms to give firmware time to get input working. */
2563 err
= console_key_read(&key
, 100 * 1000);
2564 if (!EFI_ERROR(err
)) {
2565 /* find matching key in config entries */
2566 UINTN idx
= entry_lookup_key(&config
, config
.idx_default
, KEYCHAR(key
));
2567 if (idx
!= IDX_INVALID
)
2568 config
.idx_default
= idx
;
2577 entry
= config
.entries
[config
.idx_default
];
2579 efivar_set_time_usec(LOADER_GUID
, L
"LoaderTimeMenuUSec", 0);
2580 if (!menu_run(&config
, &entry
, loaded_image_path
))
2584 /* Run special entry like "reboot" now. Those that have a loader
2585 * will be handled by image_start() instead. */
2586 if (entry
->call
&& !entry
->loader
) {
2591 config_entry_bump_counters(entry
, root_dir
);
2592 save_selected_entry(&config
, entry
);
2594 /* Optionally, read a random seed off the ESP and pass it to the OS */
2595 (void) process_random_seed(root_dir
, config
.random_seed_mode
);
2597 err
= image_start(root_dir
, image
, &config
, entry
);
2598 if (EFI_ERROR(err
)) {
2599 graphics_mode(FALSE
);
2600 log_error_stall(L
"Failed to execute %s (%s): %r", entry
->title_show
, entry
->loader
, err
);
2605 config
.timeout_sec
= 0;
2609 BS
->CloseProtocol(image
, &LoadedImageProtocol
, image
, NULL
);