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 "part-discovery.h"
21 #include "random-seed.h"
22 #include "secure-boot.h"
27 #ifndef GNU_EFI_USE_MS_ABI
28 /* We do not use uefi_call_wrapper() in systemd-boot. As such, we rely on the
29 * compiler to do the calling convention conversion for us. This is check is
30 * to make sure the -DGNU_EFI_USE_MS_ABI was passed to the comiler. */
31 #error systemd-boot requires compilation with GNU_EFI_USE_MS_ABI defined.
34 #define TEXT_ATTR_SWAP(c) EFI_TEXT_ATTR(((c) & 0b11110000) >> 4, (c) & 0b1111)
36 /* Magic string for recognizing our own binaries */
37 _used_
_section_(".sdmagic") static const char magic
[] =
38 "#### LoaderInfo: systemd-boot " GIT_VERSION
" ####";
40 /* Makes systemd-boot available from \EFI\Linux\ for testing purposes. */
41 _used_
_section_(".osrel") static const char osrel
[] =
43 "VERSION=\"" GIT_VERSION
"\"\n"
44 "NAME=\"systemd-boot " GIT_VERSION
"\"\n";
50 LOADER_LINUX
, /* Boot loader spec type #1 entries */
51 LOADER_UNIFIED_LINUX
, /* Boot loader spec type #2 entries */
52 LOADER_SECURE_BOOT_KEYS
,
56 char16_t
*id
; /* The unique identifier for this entry (typically the filename of the file defining the entry) */
57 char16_t
*title_show
; /* The string to actually display (this is made unique before showing) */
58 char16_t
*title
; /* The raw (human readable) title string of the entry (not necessarily unique) */
59 char16_t
*sort_key
; /* The string to use as primary sort key, usually ID= from os-release, possibly suffixed */
60 char16_t
*version
; /* The raw (human readable) version string of the entry */
63 enum loader_type type
;
69 EFI_STATUS (*call
)(void);
73 char16_t
*current_name
;
78 ConfigEntry
**entries
;
81 UINTN idx_default_efivar
;
82 uint32_t timeout_sec
; /* Actual timeout used (efi_main() override > efivar > config). */
83 uint32_t timeout_sec_config
;
84 uint32_t timeout_sec_efivar
;
85 char16_t
*entry_default_config
;
86 char16_t
*entry_default_efivar
;
87 char16_t
*entry_oneshot
;
88 char16_t
*entry_saved
;
92 bool reboot_for_bitlocker
;
93 secure_boot_enroll secure_boot_enroll
;
96 bool use_saved_entry_efivar
;
99 int64_t console_mode_efivar
;
102 /* These values have been chosen so that the transitions the user sees could
103 * employ unsigned over-/underflow like this:
104 * efivar unset ↔ force menu ↔ no timeout/skip menu ↔ 1 s ↔ 2 s ↔ … */
107 TIMEOUT_MAX
= UINT32_MAX
- 2U,
108 TIMEOUT_UNSET
= UINT32_MAX
- 1U,
109 TIMEOUT_MENU_FORCE
= UINT32_MAX
,
110 TIMEOUT_MENU_HIDDEN
= 0,
111 TIMEOUT_TYPE_MAX
= UINT32_MAX
,
119 static void cursor_left(UINTN
*cursor
, UINTN
*first
) {
125 else if ((*first
) > 0)
129 static void cursor_right(
138 if ((*cursor
)+1 < x_max
)
140 else if ((*first
) + (*cursor
) < len
)
144 static bool line_edit(
149 _cleanup_free_ char16_t
*line
= NULL
, *print
= NULL
;
150 UINTN size
, len
, first
= 0, cursor
= 0, clear
= 0;
154 len
= strlen16(*line_in
);
156 line
= xnew(char16_t
, size
);
157 print
= xnew(char16_t
, x_max
+ 1);
158 strcpy16(line
, strempty(*line_in
));
164 UINTN cursor_color
= TEXT_ATTR_SWAP(COLOR_EDIT
);
166 j
= MIN(len
- first
, x_max
);
167 memcpy(print
, line
+ first
, j
* sizeof(char16_t
));
168 while (clear
> 0 && j
< x_max
) {
174 /* See comment at edit_line() call site for why we start at 1. */
175 print_at(1, y_pos
, COLOR_EDIT
, print
);
179 print
[cursor
+1] = '\0';
181 print_at(cursor
+ 1, y_pos
, cursor_color
, print
+ cursor
);
182 cursor_color
= TEXT_ATTR_SWAP(cursor_color
);
184 err
= console_key_read(&key
, 750 * 1000);
185 if (!IN_SET(err
, EFI_SUCCESS
, EFI_TIMEOUT
, EFI_NOT_READY
))
188 print_at(cursor
+ 1, y_pos
, COLOR_EDIT
, print
+ cursor
);
189 } while (err
!= EFI_SUCCESS
);
192 case KEYPRESS(0, SCAN_ESC
, 0):
193 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'c'):
194 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'g'):
195 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('c')):
196 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('g')):
199 case KEYPRESS(0, SCAN_HOME
, 0):
200 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'a'):
201 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('a')):
202 /* beginning-of-line */
207 case KEYPRESS(0, SCAN_END
, 0):
208 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'e'):
209 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('e')):
211 cursor
= len
- first
;
212 if (cursor
+1 >= x_max
) {
214 first
= len
- (x_max
-1);
218 case KEYPRESS(0, SCAN_DOWN
, 0):
219 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'f'):
220 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_RIGHT
, 0):
222 while (line
[first
+ cursor
] == ' ')
223 cursor_right(&cursor
, &first
, x_max
, len
);
224 while (line
[first
+ cursor
] && line
[first
+ cursor
] != ' ')
225 cursor_right(&cursor
, &first
, x_max
, len
);
228 case KEYPRESS(0, SCAN_UP
, 0):
229 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'b'):
230 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_LEFT
, 0):
232 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
233 cursor_left(&cursor
, &first
);
234 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ')
235 cursor_left(&cursor
, &first
);
237 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ')
238 cursor_left(&cursor
, &first
);
241 case KEYPRESS(0, SCAN_RIGHT
, 0):
242 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'f'):
243 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('f')):
245 if (first
+ cursor
== len
)
247 cursor_right(&cursor
, &first
, x_max
, len
);
250 case KEYPRESS(0, SCAN_LEFT
, 0):
251 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'b'):
252 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('b')):
254 cursor_left(&cursor
, &first
);
257 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_DELETE
, 0):
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, SCAN_F3
, 0): /* EZpad Mini 4s firmware sends malformed events */
322 case KEYPRESS(0, SCAN_F3
, CHAR_CARRIAGE_RETURN
): /* Teclast X98+ II firmware sends malformed events */
323 if (!streq16(line
, *line_in
)) {
325 *line_in
= TAKE_PTR(line
);
329 case KEYPRESS(0, 0, CHAR_BACKSPACE
):
332 if (first
== 0 && cursor
== 0)
334 for (UINTN i
= first
+ cursor
-1; i
< len
; i
++)
340 if (cursor
> 0 || first
== 0)
342 /* show full line if it fits */
348 /* jump left to see what we delete */
358 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
359 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
362 for (UINTN i
= len
; i
> first
+ cursor
; i
--)
364 line
[first
+ cursor
] = KEYCHAR(key
);
367 if (cursor
+1 < x_max
)
369 else if (first
+ cursor
< len
)
376 static UINTN
entry_lookup_key(Config
*config
, UINTN start
, char16_t key
) {
382 /* select entry by number key */
383 if (key
>= '1' && key
<= '9') {
385 if (i
> config
->entry_count
)
386 i
= config
->entry_count
;
390 /* find matching key in config entries */
391 for (UINTN i
= start
; i
< config
->entry_count
; i
++)
392 if (config
->entries
[i
]->key
== key
)
395 for (UINTN i
= 0; i
< start
; i
++)
396 if (config
->entries
[i
]->key
== key
)
402 static char16_t
*update_timeout_efivar(uint32_t *t
, bool inc
) {
407 *t
= inc
? TIMEOUT_MAX
: (*t
- 1);
410 *t
= inc
? TIMEOUT_MENU_FORCE
: TIMEOUT_UNSET
;
412 case TIMEOUT_MENU_FORCE
:
413 *t
= inc
? TIMEOUT_MENU_HIDDEN
: TIMEOUT_UNSET
;
415 case TIMEOUT_MENU_HIDDEN
:
416 *t
= inc
? TIMEOUT_MIN
: TIMEOUT_MENU_FORCE
;
424 return xstrdup16(u
"Menu timeout defined by configuration file.");
425 case TIMEOUT_MENU_FORCE
:
426 return xstrdup16(u
"Timeout disabled, menu will always be shown.");
427 case TIMEOUT_MENU_HIDDEN
:
428 return xstrdup16(u
"Menu disabled. Hold down key at bootup to show menu.");
430 return xpool_print(L
"Menu timeout set to %u s.", *t
);
434 static bool unicode_supported(void) {
435 static int cache
= -1;
438 /* Basic unicode box drawing support is mandated by the spec, but it does
439 * not hurt to make sure it works. */
440 cache
= ST
->ConOut
->TestString(ST
->ConOut
, (char16_t
*) L
"─") == EFI_SUCCESS
;
445 static void ps_string(const char16_t
*fmt
, const void *value
) {
451 static void ps_bool(const char16_t
*fmt
, bool value
) {
453 Print(fmt
, yes_no(value
));
456 static bool ps_continue(void) {
457 if (unicode_supported())
458 Print(L
"\n─── Press any key to continue, ESC or q to quit. ───\n\n");
460 Print(L
"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
463 return console_key_read(&key
, UINT64_MAX
) == EFI_SUCCESS
&&
464 !IN_SET(key
, KEYPRESS(0, SCAN_ESC
, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q'));
467 static void print_status(Config
*config
, char16_t
*loaded_image_path
) {
469 uint32_t screen_width
= 0, screen_height
= 0;
470 SecureBootMode secure
;
471 _cleanup_free_ char16_t
*device_part_uuid
= NULL
;
475 clear_screen(COLOR_NORMAL
);
476 console_query_mode(&x_max
, &y_max
);
477 query_screen_resolution(&screen_width
, &screen_height
);
479 secure
= secure_boot_mode();
480 (void) efivar_get(LOADER_GUID
, L
"LoaderDevicePartUUID", &device_part_uuid
);
482 /* We employ some unusual indentation here for readability. */
484 ps_string(L
" systemd-boot version: %a\n", GIT_VERSION
);
485 ps_string(L
" loaded image: %s\n", loaded_image_path
);
486 ps_string(L
" loader partition UUID: %s\n", device_part_uuid
);
487 ps_string(L
" architecture: %a\n", EFI_MACHINE_TYPE_NAME
);
488 Print(L
" UEFI specification: %u.%02u\n", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
489 ps_string(L
" firmware vendor: %s\n", ST
->FirmwareVendor
);
490 Print(L
" firmware version: %u.%02u\n", ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
491 Print(L
" OS indications: %lu\n", get_os_indications_supported());
492 Print(L
" secure boot: %s (%s)\n", yes_no(IN_SET(secure
, SECURE_BOOT_USER
, SECURE_BOOT_DEPLOYED
)), secure_boot_mode_to_string(secure
));
493 ps_bool(L
" shim: %s\n", shim_loaded());
494 ps_bool(L
" TPM: %s\n", tpm_present());
495 Print(L
" console mode: %d/%ld (%" PRIuN L
"x%" PRIuN L
" @%ux%u)\n", ST
->ConOut
->Mode
->Mode
, ST
->ConOut
->Mode
->MaxMode
- INT64_C(1), x_max
, y_max
, screen_width
, screen_height
);
500 switch (config
->timeout_sec_config
) {
503 case TIMEOUT_MENU_FORCE
:
504 Print(L
" timeout (config): menu-force\n"); break;
505 case TIMEOUT_MENU_HIDDEN
:
506 Print(L
" timeout (config): menu-hidden\n"); break;
508 Print(L
" timeout (config): %u s\n", config
->timeout_sec_config
);
511 switch (config
->timeout_sec_efivar
) {
514 case TIMEOUT_MENU_FORCE
:
515 Print(L
" timeout (EFI var): menu-force\n"); break;
516 case TIMEOUT_MENU_HIDDEN
:
517 Print(L
" timeout (EFI var): menu-hidden\n"); break;
519 Print(L
" timeout (EFI var): %u s\n", config
->timeout_sec_efivar
);
522 ps_string(L
" default (config): %s\n", config
->entry_default_config
);
523 ps_string(L
" default (EFI var): %s\n", config
->entry_default_efivar
);
524 ps_string(L
" default (one-shot): %s\n", config
->entry_oneshot
);
525 ps_string(L
" saved entry: %s\n", config
->entry_saved
);
526 ps_bool(L
" editor: %s\n", config
->editor
);
527 ps_bool(L
" auto-entries: %s\n", config
->auto_entries
);
528 ps_bool(L
" auto-firmware: %s\n", config
->auto_firmware
);
529 ps_bool(L
" beep: %s\n", config
->beep
);
530 ps_bool(L
" reboot-for-bitlocker: %s\n", config
->reboot_for_bitlocker
);
532 switch (config
->secure_boot_enroll
) {
534 Print(L
" secure-boot-enroll: off\n"); break;
536 Print(L
" secure-boot-enroll: manual\n"); break;
538 Print(L
" secure-boot-enroll: force\n"); break;
540 assert_not_reached();
543 switch (config
->console_mode
) {
544 case CONSOLE_MODE_AUTO
:
545 Print(L
" console-mode (config): %s\n", L
"auto"); break;
546 case CONSOLE_MODE_KEEP
:
547 Print(L
" console-mode (config): %s\n", L
"keep"); break;
548 case CONSOLE_MODE_FIRMWARE_MAX
:
549 Print(L
" console-mode (config): %s\n", L
"max"); break;
551 Print(L
" console-mode (config): %ld\n", config
->console_mode
); break;
554 /* EFI var console mode is always a concrete value or unset. */
555 if (config
->console_mode_efivar
!= CONSOLE_MODE_KEEP
)
556 Print(L
"console-mode (EFI var): %ld\n", config
->console_mode_efivar
);
561 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
562 ConfigEntry
*entry
= config
->entries
[i
];
564 _cleanup_free_ char16_t
*dp
= NULL
;
566 (void) device_path_to_str(DevicePathFromHandle(entry
->device
), &dp
);
568 Print(L
" config entry: %" PRIuN L
"/%" PRIuN L
"\n", i
+ 1, config
->entry_count
);
569 ps_string(L
" id: %s\n", entry
->id
);
570 ps_string(L
" title: %s\n", entry
->title
);
571 ps_string(L
" title show: %s\n", streq16(entry
->title
, entry
->title_show
) ? NULL
: entry
->title_show
);
572 ps_string(L
" sort key: %s\n", entry
->sort_key
);
573 ps_string(L
" version: %s\n", entry
->version
);
574 ps_string(L
" machine-id: %s\n", entry
->machine_id
);
575 ps_string(L
" device: %s\n", dp
);
576 ps_string(L
" loader: %s\n", entry
->loader
);
577 STRV_FOREACH(initrd
, entry
->initrd
)
578 Print(L
" initrd: %s\n", *initrd
);
579 ps_string(L
" devicetree: %s\n", entry
->devicetree
);
580 ps_string(L
" options: %s\n", entry
->options
);
581 ps_bool(L
" internal call: %s\n", !!entry
->call
);
583 ps_bool(L
"counting boots: %s\n", entry
->tries_left
>= 0);
584 if (entry
->tries_left
>= 0) {
585 Print(L
" tries: %u left, %u done\n", entry
->tries_left
, entry
->tries_done
);
586 Print(L
" current path: %s\\%s\n", entry
->path
, entry
->current_name
);
587 Print(L
" next path: %s\\%s\n", entry
->path
, entry
->next_name
);
595 static EFI_STATUS
reboot_into_firmware(void) {
599 if (!FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
))
600 return log_error_status_stall(EFI_UNSUPPORTED
, L
"Reboot to firmware interface not supported.");
602 (void) efivar_get_uint64_le(EFI_GLOBAL_GUID
, L
"OsIndications", &osind
);
603 osind
|= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
605 err
= efivar_set_uint64_le(EFI_GLOBAL_GUID
, L
"OsIndications", osind
, EFI_VARIABLE_NON_VOLATILE
);
606 if (err
!= EFI_SUCCESS
)
607 return log_error_status_stall(err
, L
"Error setting OsIndications: %r", err
);
609 RT
->ResetSystem(EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
610 assert_not_reached();
613 static bool menu_run(
615 ConfigEntry
**chosen_entry
,
616 char16_t
*loaded_image_path
) {
619 assert(chosen_entry
);
622 UINTN visible_max
= 0;
623 UINTN idx_highlight
= config
->idx_default
;
624 UINTN idx_highlight_prev
= 0;
625 UINTN idx
, idx_first
= 0, idx_last
= 0;
626 bool new_mode
= true, clear
= true;
627 bool refresh
= true, highlight
= false;
628 UINTN x_start
= 0, y_start
= 0, y_status
= 0;
630 _cleanup_(strv_freep
) char16_t
**lines
= NULL
;
631 _cleanup_free_ char16_t
*clearline
= NULL
, *separator
= NULL
, *status
= NULL
;
632 uint32_t timeout_efivar_saved
= config
->timeout_sec_efivar
;
633 uint32_t timeout_remain
= config
->timeout_sec
== TIMEOUT_MENU_FORCE
? 0 : config
->timeout_sec
;
634 bool exit
= false, run
= true, firmware_setup
= false;
635 int64_t console_mode_initial
= ST
->ConOut
->Mode
->Mode
, console_mode_efivar_saved
= config
->console_mode_efivar
;
636 UINTN default_efivar_saved
= config
->idx_default_efivar
;
638 graphics_mode(false);
639 ST
->ConIn
->Reset(ST
->ConIn
, false);
640 ST
->ConOut
->EnableCursor(ST
->ConOut
, false);
642 /* draw a single character to make ClearScreen work on some firmware */
645 err
= console_set_mode(config
->console_mode_efivar
!= CONSOLE_MODE_KEEP
?
646 config
->console_mode_efivar
: config
->console_mode
);
647 if (err
!= EFI_SUCCESS
) {
648 clear_screen(COLOR_NORMAL
);
649 log_error_stall(L
"Error switching console mode: %r", err
);
652 UINTN line_width
= 0, entry_padding
= 3;
657 console_query_mode(&x_max
, &y_max
);
659 /* account for padding+status */
660 visible_max
= y_max
- 2;
662 /* Drawing entries starts at idx_first until idx_last. We want to make
663 * sure that idx_highlight is centered, but not if we are close to the
664 * beginning/end of the entry list. Otherwise we would have a half-empty
666 if (config
->entry_count
<= visible_max
|| idx_highlight
<= visible_max
/ 2)
668 else if (idx_highlight
>= config
->entry_count
- (visible_max
/ 2))
669 idx_first
= config
->entry_count
- visible_max
;
671 idx_first
= idx_highlight
- (visible_max
/ 2);
672 idx_last
= idx_first
+ visible_max
- 1;
674 /* length of the longest entry */
676 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
677 line_width
= MAX(line_width
, strlen16(config
->entries
[i
]->title_show
));
678 line_width
= MIN(line_width
+ 2 * entry_padding
, x_max
);
680 /* offsets to center the entries on the screen */
681 x_start
= (x_max
- (line_width
)) / 2;
682 if (config
->entry_count
< visible_max
)
683 y_start
= ((visible_max
- config
->entry_count
) / 2) + 1;
687 /* Put status line after the entry list, but give it some breathing room. */
688 y_status
= MIN(y_start
+ MIN(visible_max
, config
->entry_count
) + 1, y_max
- 1);
690 lines
= strv_free(lines
);
691 clearline
= mfree(clearline
);
692 separator
= mfree(separator
);
694 /* menu entries title lines */
695 lines
= xnew(char16_t
*, config
->entry_count
+ 1);
697 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
700 lines
[i
] = xnew(char16_t
, line_width
+ 1);
701 padding
= (line_width
- MIN(strlen16(config
->entries
[i
]->title_show
), line_width
)) / 2;
703 for (j
= 0; j
< padding
; j
++)
706 for (UINTN k
= 0; config
->entries
[i
]->title_show
[k
] != '\0' && j
< line_width
; j
++, k
++)
707 lines
[i
][j
] = config
->entries
[i
]->title_show
[k
];
709 for (; j
< line_width
; j
++)
711 lines
[i
][line_width
] = '\0';
713 lines
[config
->entry_count
] = NULL
;
715 clearline
= xnew(char16_t
, x_max
+ 1);
716 separator
= xnew(char16_t
, x_max
+ 1);
717 for (UINTN i
= 0; i
< x_max
; i
++) {
719 separator
[i
] = unicode_supported() ? L
'─' : L
'-';
721 clearline
[x_max
] = 0;
722 separator
[x_max
] = 0;
729 clear_screen(COLOR_NORMAL
);
735 for (UINTN i
= idx_first
; i
<= idx_last
&& i
< config
->entry_count
; i
++) {
736 print_at(x_start
, y_start
+ i
- idx_first
,
737 i
== idx_highlight
? COLOR_HIGHLIGHT
: COLOR_ENTRY
,
739 if (i
== config
->idx_default_efivar
)
741 y_start
+ i
- idx_first
,
742 i
== idx_highlight
? COLOR_HIGHLIGHT
: COLOR_ENTRY
,
743 unicode_supported() ? L
" ►" : L
"=>");
746 } else if (highlight
) {
747 print_at(x_start
, y_start
+ idx_highlight_prev
- idx_first
, COLOR_ENTRY
, lines
[idx_highlight_prev
]);
748 print_at(x_start
, y_start
+ idx_highlight
- idx_first
, COLOR_HIGHLIGHT
, lines
[idx_highlight
]);
749 if (idx_highlight_prev
== config
->idx_default_efivar
)
751 y_start
+ idx_highlight_prev
- idx_first
,
753 unicode_supported() ? L
" ►" : L
"=>");
754 if (idx_highlight
== config
->idx_default_efivar
)
756 y_start
+ idx_highlight
- idx_first
,
758 unicode_supported() ? L
" ►" : L
"=>");
762 if (timeout_remain
> 0) {
764 status
= xpool_print(L
"Boot in %u s.", timeout_remain
);
768 /* If we draw the last char of the last line, the screen will scroll and break our
769 * input. Therefore, draw one less character then we could for the status message.
770 * Note that the same does not apply for the separator line as it will never be drawn
771 * on the last line. */
772 UINTN len
= strnlen16(status
, x_max
- 1);
773 UINTN x
= (x_max
- len
) / 2;
775 print_at(0, y_status
, COLOR_NORMAL
, clearline
+ x_max
- x
);
776 ST
->ConOut
->OutputString(ST
->ConOut
, status
);
777 ST
->ConOut
->OutputString(ST
->ConOut
, clearline
+ 1 + x
+ len
);
779 len
= MIN(MAX(len
, line_width
) + 2 * entry_padding
, x_max
);
780 x
= (x_max
- len
) / 2;
781 print_at(x
, y_status
- 1, COLOR_NORMAL
, separator
+ x_max
- len
);
783 print_at(0, y_status
- 1, COLOR_NORMAL
, clearline
);
784 print_at(0, y_status
, COLOR_NORMAL
, clearline
+ 1); /* See comment above. */
787 /* Beep several times so that the selected entry can be distinguished. */
789 beep(idx_highlight
+ 1);
791 err
= console_key_read(&key
, timeout_remain
> 0 ? 1000 * 1000 : UINT64_MAX
);
792 if (err
== EFI_NOT_READY
)
793 /* No input device returned a key, try again. This
794 * normally should not happen. */
796 if (err
== EFI_TIMEOUT
) {
797 assert(timeout_remain
> 0);
799 if (timeout_remain
== 0) {
807 if (err
!= EFI_SUCCESS
) {
814 /* clear status after keystroke */
815 status
= mfree(status
);
817 idx_highlight_prev
= idx_highlight
;
819 if (firmware_setup
) {
820 firmware_setup
= false;
821 if (key
== KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
))
822 reboot_into_firmware();
827 case KEYPRESS(0, SCAN_UP
, 0):
828 case KEYPRESS(0, 0, 'k'):
829 case KEYPRESS(0, 0, 'K'):
830 if (idx_highlight
> 0)
834 case KEYPRESS(0, SCAN_DOWN
, 0):
835 case KEYPRESS(0, 0, 'j'):
836 case KEYPRESS(0, 0, 'J'):
837 if (idx_highlight
< config
->entry_count
-1)
841 case KEYPRESS(0, SCAN_HOME
, 0):
842 case KEYPRESS(EFI_ALT_PRESSED
, 0, '<'):
843 if (idx_highlight
> 0) {
849 case KEYPRESS(0, SCAN_END
, 0):
850 case KEYPRESS(EFI_ALT_PRESSED
, 0, '>'):
851 if (idx_highlight
< config
->entry_count
-1) {
853 idx_highlight
= config
->entry_count
-1;
857 case KEYPRESS(0, SCAN_PAGE_UP
, 0):
858 if (idx_highlight
> visible_max
)
859 idx_highlight
-= visible_max
;
864 case KEYPRESS(0, SCAN_PAGE_DOWN
, 0):
865 idx_highlight
+= visible_max
;
866 if (idx_highlight
> config
->entry_count
-1)
867 idx_highlight
= config
->entry_count
-1;
870 case KEYPRESS(0, 0, CHAR_LINEFEED
):
871 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
872 case KEYPRESS(0, SCAN_F3
, 0): /* EZpad Mini 4s firmware sends malformed events */
873 case KEYPRESS(0, SCAN_F3
, CHAR_CARRIAGE_RETURN
): /* Teclast X98+ II firmware sends malformed events */
874 case KEYPRESS(0, SCAN_RIGHT
, 0):
878 case KEYPRESS(0, SCAN_F1
, 0):
879 case KEYPRESS(0, 0, 'h'):
880 case KEYPRESS(0, 0, 'H'):
881 case KEYPRESS(0, 0, '?'):
882 /* This must stay below 80 characters! Q/v/Ctrl+l/f deliberately not advertised. */
883 status
= xstrdup16(u
"(d)efault (t/T)timeout (e)dit (r/R)resolution (p)rint (h)elp");
886 case KEYPRESS(0, 0, 'Q'):
891 case KEYPRESS(0, 0, 'd'):
892 case KEYPRESS(0, 0, 'D'):
893 if (config
->idx_default_efivar
!= idx_highlight
) {
894 free(config
->entry_default_efivar
);
895 config
->entry_default_efivar
= xstrdup16(config
->entries
[idx_highlight
]->id
);
896 config
->idx_default_efivar
= idx_highlight
;
897 status
= xstrdup16(u
"Default boot entry selected.");
899 config
->entry_default_efivar
= mfree(config
->entry_default_efivar
);
900 config
->idx_default_efivar
= IDX_INVALID
;
901 status
= xstrdup16(u
"Default boot entry cleared.");
903 config
->use_saved_entry_efivar
= false;
907 case KEYPRESS(0, 0, '-'):
908 case KEYPRESS(0, 0, 'T'):
909 status
= update_timeout_efivar(&config
->timeout_sec_efivar
, false);
912 case KEYPRESS(0, 0, '+'):
913 case KEYPRESS(0, 0, 't'):
914 status
= update_timeout_efivar(&config
->timeout_sec_efivar
, true);
917 case KEYPRESS(0, 0, 'e'):
918 case KEYPRESS(0, 0, 'E'):
919 /* only the options of configured entries can be edited */
920 if (!config
->editor
|| !IN_SET(config
->entries
[idx_highlight
]->type
,
921 LOADER_EFI
, LOADER_LINUX
, LOADER_UNIFIED_LINUX
))
924 /* Unified kernels that are signed as a whole will not accept command line options
925 * when secure boot is enabled unless there is none embedded in the image. Do not try
926 * to pretend we can edit it to only have it be ignored. */
927 if (config
->entries
[idx_highlight
]->type
== LOADER_UNIFIED_LINUX
&&
928 secure_boot_enabled() &&
929 config
->entries
[idx_highlight
]->options
)
932 /* The edit line may end up on the last line of the screen. And even though we're
933 * not telling the firmware to advance the line, it still does in this one case,
934 * causing a scroll to happen that screws with our beautiful boot loader output.
935 * Since we cannot paint the last character of the edit line, we simply start
936 * at x-offset 1 for symmetry. */
937 print_at(1, y_status
, COLOR_EDIT
, clearline
+ 2);
938 exit
= line_edit(&config
->entries
[idx_highlight
]->options
, x_max
- 2, y_status
);
939 print_at(1, y_status
, COLOR_NORMAL
, clearline
+ 2);
942 case KEYPRESS(0, 0, 'v'):
943 status
= xpool_print(
944 L
"systemd-boot " GIT_VERSION L
" (" EFI_MACHINE_TYPE_NAME L
"), "
945 L
"UEFI Specification %u.%02u, Vendor %s %u.%02u",
946 ST
->Hdr
.Revision
>> 16,
947 ST
->Hdr
.Revision
& 0xffff,
949 ST
->FirmwareRevision
>> 16,
950 ST
->FirmwareRevision
& 0xffff);
953 case KEYPRESS(0, 0, 'p'):
954 case KEYPRESS(0, 0, 'P'):
955 print_status(config
, loaded_image_path
);
959 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'l'):
960 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('l')):
964 case KEYPRESS(0, 0, 'r'):
965 err
= console_set_mode(CONSOLE_MODE_NEXT
);
966 if (err
!= EFI_SUCCESS
)
967 status
= xpool_print(L
"Error changing console mode: %r", err
);
969 config
->console_mode_efivar
= ST
->ConOut
->Mode
->Mode
;
970 status
= xpool_print(L
"Console mode changed to %ld.", config
->console_mode_efivar
);
975 case KEYPRESS(0, 0, 'R'):
976 config
->console_mode_efivar
= CONSOLE_MODE_KEEP
;
977 err
= console_set_mode(config
->console_mode
== CONSOLE_MODE_KEEP
?
978 console_mode_initial
: config
->console_mode
);
979 if (err
!= EFI_SUCCESS
)
980 status
= xpool_print(L
"Error resetting console mode: %r", err
);
982 status
= xpool_print(L
"Console mode reset to %s default.",
983 config
->console_mode
== CONSOLE_MODE_KEEP
? L
"firmware" : L
"configuration file");
987 case KEYPRESS(0, 0, 'f'):
988 case KEYPRESS(0, 0, 'F'):
989 case KEYPRESS(0, SCAN_F2
, 0): /* Most vendors. */
990 case KEYPRESS(0, SCAN_F10
, 0): /* HP and Lenovo. */
991 case KEYPRESS(0, SCAN_DELETE
, 0): /* Same as F2. */
992 case KEYPRESS(0, SCAN_ESC
, 0): /* HP. */
993 if (FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
)) {
994 firmware_setup
= true;
995 /* Let's make sure the user really wants to do this. */
996 status
= xpool_print(L
"Press Enter to reboot into firmware interface.");
998 status
= xpool_print(L
"Reboot into firmware interface not supported.");
1002 /* jump with a hotkey directly to a matching entry */
1003 idx
= entry_lookup_key(config
, idx_highlight
+1, KEYCHAR(key
));
1004 if (idx
== IDX_INVALID
)
1006 idx_highlight
= idx
;
1010 if (idx_highlight
> idx_last
) {
1011 idx_last
= idx_highlight
;
1012 idx_first
= 1 + idx_highlight
- visible_max
;
1014 } else if (idx_highlight
< idx_first
) {
1015 idx_first
= idx_highlight
;
1016 idx_last
= idx_highlight
+ visible_max
-1;
1020 if (!refresh
&& idx_highlight
!= idx_highlight_prev
)
1024 *chosen_entry
= config
->entries
[idx_highlight
];
1026 /* Update EFI vars after we left the menu to reduce NVRAM writes. */
1028 if (default_efivar_saved
!= config
->idx_default_efivar
)
1029 efivar_set(LOADER_GUID
, L
"LoaderEntryDefault", config
->entry_default_efivar
, EFI_VARIABLE_NON_VOLATILE
);
1031 if (console_mode_efivar_saved
!= config
->console_mode_efivar
) {
1032 if (config
->console_mode_efivar
== CONSOLE_MODE_KEEP
)
1033 efivar_set(LOADER_GUID
, L
"LoaderConfigConsoleMode", NULL
, EFI_VARIABLE_NON_VOLATILE
);
1035 efivar_set_uint_string(LOADER_GUID
, L
"LoaderConfigConsoleMode",
1036 config
->console_mode_efivar
, EFI_VARIABLE_NON_VOLATILE
);
1039 if (timeout_efivar_saved
!= config
->timeout_sec_efivar
) {
1040 switch (config
->timeout_sec_efivar
) {
1042 efivar_set(LOADER_GUID
, L
"LoaderConfigTimeout", NULL
, EFI_VARIABLE_NON_VOLATILE
);
1044 case TIMEOUT_MENU_FORCE
:
1045 efivar_set(LOADER_GUID
, u
"LoaderConfigTimeout", u
"menu-force", EFI_VARIABLE_NON_VOLATILE
);
1047 case TIMEOUT_MENU_HIDDEN
:
1048 efivar_set(LOADER_GUID
, u
"LoaderConfigTimeout", u
"menu-hidden", EFI_VARIABLE_NON_VOLATILE
);
1051 efivar_set_uint_string(LOADER_GUID
, L
"LoaderConfigTimeout",
1052 config
->timeout_sec_efivar
, EFI_VARIABLE_NON_VOLATILE
);
1056 clear_screen(COLOR_NORMAL
);
1060 static void config_add_entry(Config
*config
, ConfigEntry
*entry
) {
1064 /* This is just for paranoia. */
1065 assert(config
->entry_count
< IDX_MAX
);
1067 if ((config
->entry_count
& 15) == 0) {
1068 config
->entries
= xrealloc(
1070 sizeof(void *) * config
->entry_count
,
1071 sizeof(void *) * (config
->entry_count
+ 16));
1073 config
->entries
[config
->entry_count
++] = entry
;
1076 static void config_entry_free(ConfigEntry
*entry
) {
1081 free(entry
->title_show
);
1083 free(entry
->sort_key
);
1084 free(entry
->version
);
1085 free(entry
->machine_id
);
1086 free(entry
->loader
);
1087 free(entry
->devicetree
);
1088 free(entry
->options
);
1089 strv_free(entry
->initrd
);
1091 free(entry
->current_name
);
1092 free(entry
->next_name
);
1096 static inline void config_entry_freep(ConfigEntry
**entry
) {
1097 config_entry_free(*entry
);
1100 static char *line_get_key_value(
1117 line
= content
+ *pos
;
1122 while (line
[linelen
] && !strchr8("\n\r", line
[linelen
]))
1125 /* move pos to next line */
1134 /* terminate line */
1135 line
[linelen
] = '\0';
1137 /* remove leading whitespace */
1138 while (strchr8(" \t", *line
)) {
1143 /* remove trailing whitespace */
1144 while (linelen
> 0 && strchr8(" \t", line
[linelen
- 1]))
1146 line
[linelen
] = '\0';
1151 /* split key/value */
1153 while (*value
&& !strchr8(sep
, *value
))
1159 while (*value
&& strchr8(sep
, *value
))
1163 if (value
[0] == '"' && line
[linelen
- 1] == '"') {
1165 line
[linelen
- 1] = '\0';
1174 static void config_defaults_load_from_file(Config
*config
, char *content
) {
1183 while ((line
= line_get_key_value(content
, " \t", &pos
, &key
, &value
))) {
1184 if (streq8(key
, "timeout")) {
1185 if (streq8( value
, "menu-force"))
1186 config
->timeout_sec_config
= TIMEOUT_MENU_FORCE
;
1187 else if (streq8(value
, "menu-hidden"))
1188 config
->timeout_sec_config
= TIMEOUT_MENU_HIDDEN
;
1191 if (!parse_number8(value
, &u
, NULL
) || u
> TIMEOUT_TYPE_MAX
) {
1192 log_error_stall(L
"Error parsing 'timeout' config option: %a", value
);
1195 config
->timeout_sec_config
= u
;
1197 config
->timeout_sec
= config
->timeout_sec_config
;
1201 if (streq8(key
, "default")) {
1202 if (value
[0] == '@' && !strcaseeq8(value
, "@saved")) {
1203 log_error_stall(L
"Unsupported special entry identifier: %a", value
);
1206 free(config
->entry_default_config
);
1207 config
->entry_default_config
= xstr8_to_16(value
);
1211 if (streq8(key
, "editor")) {
1212 err
= parse_boolean(value
, &config
->editor
);
1213 if (err
!= EFI_SUCCESS
)
1214 log_error_stall(L
"Error parsing 'editor' config option: %a", value
);
1218 if (streq8(key
, "auto-entries")) {
1219 err
= parse_boolean(value
, &config
->auto_entries
);
1220 if (err
!= EFI_SUCCESS
)
1221 log_error_stall(L
"Error parsing 'auto-entries' config option: %a", value
);
1225 if (streq8(key
, "auto-firmware")) {
1226 err
= parse_boolean(value
, &config
->auto_firmware
);
1227 if (err
!= EFI_SUCCESS
)
1228 log_error_stall(L
"Error parsing 'auto-firmware' config option: %a", value
);
1232 if (streq8(key
, "beep")) {
1233 err
= parse_boolean(value
, &config
->beep
);
1234 if (err
!= EFI_SUCCESS
)
1235 log_error_stall(L
"Error parsing 'beep' config option: %a", value
);
1239 if (streq8(key
, "reboot-for-bitlocker")) {
1240 err
= parse_boolean(value
, &config
->reboot_for_bitlocker
);
1241 if (err
!= EFI_SUCCESS
)
1242 log_error_stall(L
"Error parsing 'reboot-for-bitlocker' config option: %a", value
);
1245 if (streq8(key
, "secure-boot-enroll")) {
1246 if (streq8(value
, "manual"))
1247 config
->secure_boot_enroll
= ENROLL_MANUAL
;
1248 else if (streq8(value
, "force"))
1249 config
->secure_boot_enroll
= ENROLL_FORCE
;
1250 else if (streq8(value
, "off"))
1251 config
->secure_boot_enroll
= ENROLL_OFF
;
1253 log_error_stall(L
"Error parsing 'secure-boot-enroll' config option: %a", value
);
1257 if (streq8(key
, "console-mode")) {
1258 if (streq8(value
, "auto"))
1259 config
->console_mode
= CONSOLE_MODE_AUTO
;
1260 else if (streq8(value
, "max"))
1261 config
->console_mode
= CONSOLE_MODE_FIRMWARE_MAX
;
1262 else if (streq8(value
, "keep"))
1263 config
->console_mode
= CONSOLE_MODE_KEEP
;
1266 if (!parse_number8(value
, &u
, NULL
) || u
> CONSOLE_MODE_RANGE_MAX
) {
1267 log_error_stall(L
"Error parsing 'console-mode' config option: %a", value
);
1270 config
->console_mode
= u
;
1277 static void config_entry_parse_tries(
1279 const char16_t
*path
,
1280 const char16_t
*file
,
1281 const char16_t
*suffix
) {
1289 * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
1290 * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
1291 * which case that counter as assumed to be zero, i.e. the missing part is synonymous to "-0").
1293 * Names we grok, and the series they result in:
1295 * foobar+3.efi → foobar+2-1.efi → foobar+1-2.efi → foobar+0-3.efi → STOP!
1296 * foobar+4-0.efi → foobar+3-1.efi → foobar+2-2.efi → foobar+1-3.efi → foobar+0-4.efi → STOP!
1299 const char16_t
*counter
= NULL
;
1301 char16_t
*plus
= strchr16(counter
?: file
, '+');
1303 /* We want the last "+". */
1310 /* No boot counter found. */
1314 uint64_t tries_left
, tries_done
= 0;
1315 size_t prefix_len
= counter
- file
;
1317 if (!parse_number16(counter
, &tries_left
, &counter
) || tries_left
> INT_MAX
)
1320 /* Parse done counter only if present. */
1321 if (*counter
== '-' && (!parse_number16(counter
+ 1, &tries_done
, &counter
) || tries_done
> INT_MAX
))
1324 /* Boot counter in the middle of the name? */
1325 if (!streq16(counter
, suffix
))
1328 entry
->tries_left
= tries_left
;
1329 entry
->tries_done
= tries_done
;
1330 entry
->path
= xstrdup16(path
);
1331 entry
->current_name
= xstrdup16(file
);
1332 entry
->next_name
= xpool_print(
1336 LESS_BY(tries_left
, 1u),
1337 MIN(tries_done
+ 1, (uint64_t) INT_MAX
),
1341 static void config_entry_bump_counters(ConfigEntry
*entry
, EFI_FILE
*root_dir
) {
1342 _cleanup_free_ char16_t
* old_path
= NULL
, *new_path
= NULL
;
1343 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1344 _cleanup_free_ EFI_FILE_INFO
*file_info
= NULL
;
1345 UINTN file_info_size
;
1351 if (entry
->tries_left
< 0)
1354 if (!entry
->path
|| !entry
->current_name
|| !entry
->next_name
)
1357 old_path
= xpool_print(L
"%s\\%s", entry
->path
, entry
->current_name
);
1359 err
= root_dir
->Open(root_dir
, &handle
, old_path
, EFI_FILE_MODE_READ
|EFI_FILE_MODE_WRITE
, 0ULL);
1360 if (err
!= EFI_SUCCESS
)
1363 err
= get_file_info_harder(handle
, &file_info
, &file_info_size
);
1364 if (err
!= EFI_SUCCESS
)
1367 /* And rename the file */
1368 strcpy16(file_info
->FileName
, entry
->next_name
);
1369 err
= handle
->SetInfo(handle
, &GenericFileInfo
, file_info_size
, file_info
);
1370 if (err
!= EFI_SUCCESS
) {
1371 log_error_stall(L
"Failed to rename '%s' to '%s', ignoring: %r", old_path
, entry
->next_name
, err
);
1375 /* Flush everything to disk, just in case… */
1376 (void) handle
->Flush(handle
);
1378 /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
1380 new_path
= xpool_print(L
"%s\\%s", entry
->path
, entry
->next_name
);
1381 efivar_set(LOADER_GUID
, L
"LoaderBootCountPath", new_path
, 0);
1383 /* If the file we just renamed is the loader path, then let's update that. */
1384 if (streq16(entry
->loader
, old_path
)) {
1385 free(entry
->loader
);
1386 entry
->loader
= TAKE_PTR(new_path
);
1390 static void config_entry_add_type1(
1394 const char16_t
*path
,
1395 const char16_t
*file
,
1397 const char16_t
*loaded_image_path
) {
1399 _cleanup_(config_entry_freep
) ConfigEntry
*entry
= NULL
;
1401 UINTN pos
= 0, n_initrd
= 0;
1412 entry
= xnew(ConfigEntry
, 1);
1413 *entry
= (ConfigEntry
) {
1418 while ((line
= line_get_key_value(content
, " \t", &pos
, &key
, &value
))) {
1419 if (streq8(key
, "title")) {
1421 entry
->title
= xstr8_to_16(value
);
1425 if (streq8(key
, "sort-key")) {
1426 free(entry
->sort_key
);
1427 entry
->sort_key
= xstr8_to_16(value
);
1431 if (streq8(key
, "version")) {
1432 free(entry
->version
);
1433 entry
->version
= xstr8_to_16(value
);
1437 if (streq8(key
, "machine-id")) {
1438 free(entry
->machine_id
);
1439 entry
->machine_id
= xstr8_to_16(value
);
1443 if (streq8(key
, "linux")) {
1444 free(entry
->loader
);
1445 entry
->type
= LOADER_LINUX
;
1446 entry
->loader
= xstr8_to_path(value
);
1451 if (streq8(key
, "efi")) {
1452 entry
->type
= LOADER_EFI
;
1453 free(entry
->loader
);
1454 entry
->loader
= xstr8_to_path(value
);
1456 /* do not add an entry for ourselves */
1457 if (strcaseeq16(entry
->loader
, loaded_image_path
)) {
1458 entry
->type
= LOADER_UNDEFINED
;
1464 if (streq8(key
, "architecture")) {
1465 /* do not add an entry for an EFI image of architecture not matching with that of the image */
1466 if (!streq8(value
, EFI_MACHINE_TYPE_NAME
)) {
1467 entry
->type
= LOADER_UNDEFINED
;
1473 if (streq8(key
, "devicetree")) {
1474 free(entry
->devicetree
);
1475 entry
->devicetree
= xstr8_to_path(value
);
1479 if (streq8(key
, "initrd")) {
1480 entry
->initrd
= xrealloc(
1482 n_initrd
== 0 ? 0 : (n_initrd
+ 1) * sizeof(uint16_t *),
1483 (n_initrd
+ 2) * sizeof(uint16_t *));
1484 entry
->initrd
[n_initrd
++] = xstr8_to_path(value
);
1485 entry
->initrd
[n_initrd
] = NULL
;
1489 if (streq8(key
, "options")) {
1490 _cleanup_free_ char16_t
*new = NULL
;
1492 new = xstr8_to_16(value
);
1493 if (entry
->options
) {
1494 char16_t
*s
= xpool_print(L
"%s %s", entry
->options
, new);
1495 free(entry
->options
);
1498 entry
->options
= TAKE_PTR(new);
1504 if (entry
->type
== LOADER_UNDEFINED
)
1507 /* check existence */
1508 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1509 err
= root_dir
->Open(root_dir
, &handle
, entry
->loader
, EFI_FILE_MODE_READ
, 0ULL);
1510 if (err
!= EFI_SUCCESS
)
1513 entry
->device
= device
;
1514 entry
->id
= xstrdup16(file
);
1515 strtolower16(entry
->id
);
1517 config_add_entry(config
, entry
);
1519 config_entry_parse_tries(entry
, path
, file
, L
".conf");
1523 static EFI_STATUS
efivar_get_timeout(const char16_t
*var
, uint32_t *ret_value
) {
1524 _cleanup_free_ char16_t
*value
= NULL
;
1530 err
= efivar_get(LOADER_GUID
, var
, &value
);
1531 if (err
!= EFI_SUCCESS
)
1534 if (streq16(value
, u
"menu-force")) {
1535 *ret_value
= TIMEOUT_MENU_FORCE
;
1538 if (streq16(value
, u
"menu-hidden")) {
1539 *ret_value
= TIMEOUT_MENU_HIDDEN
;
1544 if (!parse_number16(value
, &timeout
, NULL
))
1545 return EFI_INVALID_PARAMETER
;
1547 *ret_value
= MIN(timeout
, TIMEOUT_TYPE_MAX
);
1551 static void config_load_defaults(Config
*config
, EFI_FILE
*root_dir
) {
1552 _cleanup_free_
char *content
= NULL
;
1553 UINTN value
= 0; /* avoid false maybe-uninitialized warning */
1558 *config
= (Config
) {
1560 .auto_entries
= true,
1561 .auto_firmware
= true,
1562 .reboot_for_bitlocker
= false,
1563 .secure_boot_enroll
= ENROLL_MANUAL
,
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 (err
== EFI_SUCCESS
)
1573 config_defaults_load_from_file(config
, content
);
1575 err
= efivar_get_timeout(u
"LoaderConfigTimeout", &config
->timeout_sec_efivar
);
1576 if (err
== EFI_SUCCESS
)
1577 config
->timeout_sec
= config
->timeout_sec_efivar
;
1578 else if (err
!= EFI_NOT_FOUND
)
1579 log_error_stall(u
"Error reading LoaderConfigTimeout EFI variable: %r", err
);
1581 err
= efivar_get_timeout(u
"LoaderConfigTimeoutOneShot", &config
->timeout_sec
);
1582 if (err
== EFI_SUCCESS
) {
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
->force_menu
= true; /* force the menu when this is set */
1587 } else if (err
!= EFI_NOT_FOUND
)
1588 log_error_stall(u
"Error reading LoaderConfigTimeoutOneShot EFI variable: %r", err
);
1590 err
= efivar_get_uint_string(LOADER_GUID
, L
"LoaderConfigConsoleMode", &value
);
1591 if (err
== EFI_SUCCESS
)
1592 config
->console_mode_efivar
= value
;
1594 err
= efivar_get(LOADER_GUID
, L
"LoaderEntryOneShot", &config
->entry_oneshot
);
1595 if (err
== EFI_SUCCESS
)
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 strtolower16(config
->entry_default_config
);
1602 strtolower16(config
->entry_default_efivar
);
1603 strtolower16(config
->entry_oneshot
);
1604 strtolower16(config
->entry_saved
);
1606 config
->use_saved_entry
= streq16(config
->entry_default_config
, L
"@saved");
1607 config
->use_saved_entry_efivar
= streq16(config
->entry_default_efivar
, L
"@saved");
1608 if (config
->use_saved_entry
|| config
->use_saved_entry_efivar
)
1609 (void) efivar_get(LOADER_GUID
, L
"LoaderEntryLastBooted", &config
->entry_saved
);
1612 static void config_load_entries(
1616 const char16_t
*loaded_image_path
) {
1618 _cleanup_(file_closep
) EFI_FILE
*entries_dir
= NULL
;
1619 _cleanup_free_ EFI_FILE_INFO
*f
= NULL
;
1627 /* Adds Boot Loader Type #1 entries (i.e. /loader/entries/….conf) */
1629 err
= open_directory(root_dir
, L
"\\loader\\entries", &entries_dir
);
1630 if (err
!= EFI_SUCCESS
)
1634 _cleanup_free_
char *content
= NULL
;
1636 err
= readdir_harder(entries_dir
, &f
, &f_size
);
1637 if (err
!= EFI_SUCCESS
|| !f
)
1640 if (f
->FileName
[0] == '.')
1642 if (FLAGS_SET(f
->Attribute
, EFI_FILE_DIRECTORY
))
1645 if (!endswith_no_case(f
->FileName
, L
".conf"))
1647 if (startswith(f
->FileName
, L
"auto-"))
1650 err
= file_read(entries_dir
, f
->FileName
, 0, 0, &content
, NULL
);
1651 if (err
== EFI_SUCCESS
)
1652 config_entry_add_type1(config
, device
, root_dir
, L
"\\loader\\entries", f
->FileName
, content
, loaded_image_path
);
1656 static int config_entry_compare(const ConfigEntry
*a
, const ConfigEntry
*b
) {
1662 /* Order entries that have no tries left to the end of the list */
1663 r
= CMP(a
->tries_left
== 0, b
->tries_left
== 0);
1667 /* If there's a sort key defined for *both* entries, then we do new-style ordering, i.e. by
1668 * sort-key/machine-id/version, with a final fallback to id. If there's no sort key for either, we do
1669 * old-style ordering, i.e. by id only. If one has sort key and the other does not, we put new-style
1670 * before old-style. */
1671 r
= CMP(!a
->sort_key
, !b
->sort_key
);
1672 if (r
!= 0) /* one is old-style, one new-style */
1675 if (a
->sort_key
&& b
->sort_key
) {
1676 r
= strcmp16(a
->sort_key
, b
->sort_key
);
1680 /* If multiple installations of the same OS are around, group by machine ID */
1681 r
= strcmp16(a
->machine_id
, b
->machine_id
);
1685 /* If the sort key was defined, then order by version now (downwards, putting the newest first) */
1686 r
= -strverscmp_improved(a
->version
, b
->version
);
1691 /* Now order by ID. The version is likely part of the ID, thus note that this will generatelly put
1692 * the newer versions earlier. Specifying a sort key explicitly is preferable, because it gives an
1693 * explicit sort order. */
1694 r
= -strverscmp_improved(a
->id
, b
->id
);
1698 if (a
->tries_left
< 0 || b
->tries_left
< 0)
1701 /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
1702 r
= -CMP(a
->tries_left
, b
->tries_left
);
1706 /* If they have the same number of tries left, then let the one win which was tried fewer times so far */
1707 return CMP(a
->tries_done
, b
->tries_done
);
1710 static UINTN
config_entry_find(Config
*config
, const char16_t
*pattern
) {
1713 /* We expect pattern and entry IDs to be already case folded. */
1718 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
1719 if (efi_fnmatch(pattern
, config
->entries
[i
]->id
))
1725 static void config_default_entry_select(Config
*config
) {
1730 i
= config_entry_find(config
, config
->entry_oneshot
);
1731 if (i
!= IDX_INVALID
) {
1732 config
->idx_default
= i
;
1736 i
= config_entry_find(config
, config
->use_saved_entry_efivar
? config
->entry_saved
: config
->entry_default_efivar
);
1737 if (i
!= IDX_INVALID
) {
1738 config
->idx_default
= i
;
1739 config
->idx_default_efivar
= i
;
1743 if (config
->use_saved_entry
)
1744 /* No need to do the same thing twice. */
1745 i
= config
->use_saved_entry_efivar
? IDX_INVALID
: config_entry_find(config
, config
->entry_saved
);
1747 i
= config_entry_find(config
, config
->entry_default_config
);
1748 if (i
!= IDX_INVALID
) {
1749 config
->idx_default
= i
;
1753 /* select the first suitable entry */
1754 for (i
= 0; i
< config
->entry_count
; i
++) {
1755 if (config
->entries
[i
]->type
== LOADER_AUTO
|| config
->entries
[i
]->call
)
1757 config
->idx_default
= i
;
1761 /* If no configured entry to select from was found, enable the menu. */
1762 config
->idx_default
= 0;
1763 if (config
->timeout_sec
== 0)
1764 config
->timeout_sec
= 10;
1767 static bool entries_unique(ConfigEntry
**entries
, bool *unique
, UINTN entry_count
) {
1768 bool is_unique
= true;
1773 for (UINTN i
= 0; i
< entry_count
; i
++)
1774 for (UINTN k
= i
+ 1; k
< entry_count
; k
++) {
1775 if (!streq16(entries
[i
]->title_show
, entries
[k
]->title_show
))
1778 is_unique
= unique
[i
] = unique
[k
] = false;
1784 /* generate a unique title, avoiding non-distinguishable menu entries */
1785 static void config_title_generate(Config
*config
) {
1788 bool unique
[config
->entry_count
];
1791 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1792 assert(!config
->entries
[i
]->title_show
);
1794 config
->entries
[i
]->title_show
= xstrdup16(config
->entries
[i
]->title
?: config
->entries
[i
]->id
);
1797 if (entries_unique(config
->entries
, unique
, config
->entry_count
))
1800 /* add version to non-unique titles */
1801 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1807 if (!config
->entries
[i
]->version
)
1810 _cleanup_free_ char16_t
*t
= config
->entries
[i
]->title_show
;
1811 config
->entries
[i
]->title_show
= xpool_print(L
"%s (%s)", t
, config
->entries
[i
]->version
);
1814 if (entries_unique(config
->entries
, unique
, config
->entry_count
))
1817 /* add machine-id to non-unique titles */
1818 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1824 if (!config
->entries
[i
]->machine_id
)
1827 _cleanup_free_ char16_t
*t
= config
->entries
[i
]->title_show
;
1828 config
->entries
[i
]->title_show
= xpool_print(
1831 strnlen16(config
->entries
[i
]->machine_id
, 8),
1832 config
->entries
[i
]->machine_id
);
1835 if (entries_unique(config
->entries
, unique
, config
->entry_count
))
1838 /* add file name to non-unique titles */
1839 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1843 _cleanup_free_ char16_t
*t
= config
->entries
[i
]->title_show
;
1844 config
->entries
[i
]->title_show
= xpool_print(L
"%s (%s)", t
, config
->entries
[i
]->id
);
1848 static bool is_sd_boot(EFI_FILE
*root_dir
, const char16_t
*loader_path
) {
1850 static const char * const sections
[] = {
1854 UINTN offset
= 0, size
= 0, read
;
1855 _cleanup_free_
char *content
= NULL
;
1858 assert(loader_path
);
1860 err
= pe_file_locate_sections(root_dir
, loader_path
, sections
, &offset
, &size
);
1861 if (err
!= EFI_SUCCESS
|| size
!= sizeof(magic
))
1864 err
= file_read(root_dir
, loader_path
, offset
, size
, &content
, &read
);
1865 if (err
!= EFI_SUCCESS
|| size
!= read
)
1868 return memcmp(content
, magic
, sizeof(magic
)) == 0;
1871 static ConfigEntry
*config_entry_add_loader_auto(
1875 const char16_t
*loaded_image_path
,
1878 const char16_t
*title
,
1879 const char16_t
*loader
) {
1887 if (!config
->auto_entries
)
1891 loader
= L
"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME
".efi";
1893 /* We are trying to add the default EFI loader here,
1894 * but we do not want to do that if that would be us.
1896 * If the default loader is not us, it might be shim. It would
1897 * chainload GRUBX64.EFI in that case, which might be us.*/
1898 if (strcaseeq16(loader
, loaded_image_path
) ||
1899 is_sd_boot(root_dir
, loader
) ||
1900 is_sd_boot(root_dir
, L
"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L
".EFI"))
1904 /* check existence */
1905 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1906 EFI_STATUS err
= root_dir
->Open(root_dir
, &handle
, (char16_t
*) loader
, EFI_FILE_MODE_READ
, 0ULL);
1907 if (err
!= EFI_SUCCESS
)
1910 ConfigEntry
*entry
= xnew(ConfigEntry
, 1);
1911 *entry
= (ConfigEntry
) {
1912 .id
= xstrdup16(id
),
1913 .type
= LOADER_AUTO
,
1914 .title
= xstrdup16(title
),
1916 .loader
= xstrdup16(loader
),
1922 config_add_entry(config
, entry
);
1926 static void config_entry_add_osx(Config
*config
) {
1928 UINTN n_handles
= 0;
1929 _cleanup_free_ EFI_HANDLE
*handles
= NULL
;
1933 if (!config
->auto_entries
)
1936 err
= BS
->LocateHandleBuffer(ByProtocol
, &FileSystemProtocol
, NULL
, &n_handles
, &handles
);
1937 if (err
!= EFI_SUCCESS
)
1940 for (UINTN i
= 0; i
< n_handles
; i
++) {
1941 _cleanup_(file_closep
) EFI_FILE
*root
= NULL
;
1943 if (open_volume(handles
[i
], &root
) != EFI_SUCCESS
)
1946 if (config_entry_add_loader_auto(
1954 L
"\\System\\Library\\CoreServices\\boot.efi"))
1959 static EFI_STATUS
boot_windows_bitlocker(void) {
1960 _cleanup_free_ EFI_HANDLE
*handles
= NULL
;
1964 // FIXME: Experimental for now. Should be generalized, and become a per-entry option that can be
1965 // enabled independently of BitLocker, and without a BootXXXX entry pre-existing.
1967 /* BitLocker key cannot be sealed without a TPM present. */
1969 return EFI_NOT_FOUND
;
1971 err
= BS
->LocateHandleBuffer(ByProtocol
, &BlockIoProtocol
, NULL
, &n_handles
, &handles
);
1972 if (err
!= EFI_SUCCESS
)
1975 /* Look for BitLocker magic string on all block drives. */
1977 for (UINTN i
= 0; i
< n_handles
; i
++) {
1978 EFI_BLOCK_IO_PROTOCOL
*block_io
;
1979 err
= BS
->HandleProtocol(handles
[i
], &BlockIoProtocol
, (void **) &block_io
);
1980 if (err
!= EFI_SUCCESS
|| block_io
->Media
->BlockSize
< 512 || block_io
->Media
->BlockSize
> 4096)
1984 err
= block_io
->ReadBlocks(block_io
, block_io
->Media
->MediaId
, 0, sizeof(buf
), buf
);
1985 if (err
!= EFI_SUCCESS
)
1988 if (memcmp(buf
+ 3, "-FVE-FS-", STRLEN("-FVE-FS-")) == 0) {
1994 /* If no BitLocker drive was found, we can just chainload bootmgfw.efi directly. */
1996 return EFI_NOT_FOUND
;
1998 _cleanup_free_
uint16_t *boot_order
= NULL
;
1999 UINTN boot_order_size
;
2001 /* There can be gaps in Boot#### entries. Instead of iterating over the full
2002 * EFI var list or uint16_t namespace, just look for "Windows Boot Manager" in BootOrder. */
2003 err
= efivar_get_raw(EFI_GLOBAL_GUID
, L
"BootOrder", (char **) &boot_order
, &boot_order_size
);
2004 if (err
!= EFI_SUCCESS
|| boot_order_size
% sizeof(uint16_t) != 0)
2007 for (UINTN i
= 0; i
< boot_order_size
/ sizeof(uint16_t); i
++) {
2008 _cleanup_free_
char *buf
= NULL
;
2009 char16_t name
[sizeof(L
"Boot0000")];
2012 SPrint(name
, sizeof(name
), L
"Boot%04x", (uint32_t) boot_order
[i
]);
2013 err
= efivar_get_raw(EFI_GLOBAL_GUID
, name
, &buf
, &buf_size
);
2014 if (err
!= EFI_SUCCESS
)
2017 /* Boot#### are EFI_LOAD_OPTION. But we really are only interested
2018 * for the description, which is at this offset. */
2019 UINTN offset
= sizeof(uint32_t) + sizeof(uint16_t);
2020 if (buf_size
< offset
+ sizeof(char16_t
))
2023 if (streq16((char16_t
*) (buf
+ offset
), L
"Windows Boot Manager")) {
2024 err
= efivar_set_raw(
2028 sizeof(boot_order
[i
]),
2029 EFI_VARIABLE_NON_VOLATILE
);
2030 if (err
!= EFI_SUCCESS
)
2032 RT
->ResetSystem(EfiResetWarm
, EFI_SUCCESS
, 0, NULL
);
2033 assert_not_reached();
2037 return EFI_NOT_FOUND
;
2040 static void config_entry_add_windows(Config
*config
, EFI_HANDLE
*device
, EFI_FILE
*root_dir
) {
2041 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
2042 _cleanup_free_
char *bcd
= NULL
;
2043 char16_t
*title
= NULL
;
2051 if (!config
->auto_entries
)
2054 /* Try to find a better title. */
2055 err
= file_read(root_dir
, L
"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd
, &len
);
2056 if (err
== EFI_SUCCESS
)
2057 title
= get_bcd_title((uint8_t *) bcd
, len
);
2059 ConfigEntry
*e
= config_entry_add_loader_auto(config
, device
, root_dir
, NULL
,
2060 L
"auto-windows", 'w', title
?: L
"Windows Boot Manager",
2061 L
"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2063 if (config
->reboot_for_bitlocker
)
2064 e
->call
= boot_windows_bitlocker
;
2068 static void config_entry_add_unified(
2071 EFI_FILE
*root_dir
) {
2073 _cleanup_(file_closep
) EFI_FILE
*linux_dir
= NULL
;
2074 _cleanup_free_ EFI_FILE_INFO
*f
= NULL
;
2078 /* Adds Boot Loader Type #2 entries (i.e. /EFI/Linux/….efi) */
2084 err
= open_directory(root_dir
, L
"\\EFI\\Linux", &linux_dir
);
2085 if (err
!= EFI_SUCCESS
)
2095 static const char * const sections
[_SECTION_MAX
+ 1] = {
2096 [SECTION_CMDLINE
] = ".cmdline",
2097 [SECTION_OSREL
] = ".osrel",
2101 _cleanup_free_ char16_t
*os_pretty_name
= NULL
, *os_image_id
= NULL
, *os_name
= NULL
, *os_id
= NULL
,
2102 *os_image_version
= NULL
, *os_version
= NULL
, *os_version_id
= NULL
, *os_build_id
= NULL
;
2103 const char16_t
*good_name
, *good_version
, *good_sort_key
;
2104 _cleanup_free_
char *content
= NULL
;
2105 UINTN offs
[_SECTION_MAX
] = {};
2106 UINTN szs
[_SECTION_MAX
] = {};
2111 err
= readdir_harder(linux_dir
, &f
, &f_size
);
2112 if (err
!= EFI_SUCCESS
|| !f
)
2115 if (f
->FileName
[0] == '.')
2117 if (FLAGS_SET(f
->Attribute
, EFI_FILE_DIRECTORY
))
2119 if (!endswith_no_case(f
->FileName
, L
".efi"))
2121 if (startswith(f
->FileName
, L
"auto-"))
2124 /* look for .osrel and .cmdline sections in the .efi binary */
2125 err
= pe_file_locate_sections(linux_dir
, f
->FileName
, sections
, offs
, szs
);
2126 if (err
!= EFI_SUCCESS
|| szs
[SECTION_OSREL
] == 0)
2129 err
= file_read(linux_dir
, f
->FileName
, offs
[SECTION_OSREL
], szs
[SECTION_OSREL
], &content
, NULL
);
2130 if (err
!= EFI_SUCCESS
)
2133 /* read properties from the embedded os-release file */
2134 while ((line
= line_get_key_value(content
, "=", &pos
, &key
, &value
))) {
2135 if (streq8(key
, "PRETTY_NAME")) {
2136 free(os_pretty_name
);
2137 os_pretty_name
= xstr8_to_16(value
);
2141 if (streq8(key
, "IMAGE_ID")) {
2143 os_image_id
= xstr8_to_16(value
);
2147 if (streq8(key
, "NAME")) {
2149 os_name
= xstr8_to_16(value
);
2153 if (streq8(key
, "ID")) {
2155 os_id
= xstr8_to_16(value
);
2159 if (streq8(key
, "IMAGE_VERSION")) {
2160 free(os_image_version
);
2161 os_image_version
= xstr8_to_16(value
);
2165 if (streq8(key
, "VERSION")) {
2167 os_version
= xstr8_to_16(value
);
2171 if (streq8(key
, "VERSION_ID")) {
2172 free(os_version_id
);
2173 os_version_id
= xstr8_to_16(value
);
2177 if (streq8(key
, "BUILD_ID")) {
2179 os_build_id
= xstr8_to_16(value
);
2184 if (!bootspec_pick_name_version_sort_key(
2198 ConfigEntry
*entry
= xnew(ConfigEntry
, 1);
2199 *entry
= (ConfigEntry
) {
2200 .id
= xstrdup16(f
->FileName
),
2201 .type
= LOADER_UNIFIED_LINUX
,
2202 .title
= xstrdup16(good_name
),
2203 .version
= xstrdup16(good_version
),
2205 .loader
= xpool_print(L
"\\EFI\\Linux\\%s", f
->FileName
),
2206 .sort_key
= xstrdup16(good_sort_key
),
2212 strtolower16(entry
->id
);
2213 config_add_entry(config
, entry
);
2214 config_entry_parse_tries(entry
, L
"\\EFI\\Linux", f
->FileName
, L
".efi");
2216 if (szs
[SECTION_CMDLINE
] == 0)
2219 content
= mfree(content
);
2221 /* read the embedded cmdline file */
2223 err
= file_read(linux_dir
, f
->FileName
, offs
[SECTION_CMDLINE
], szs
[SECTION_CMDLINE
], &content
, &cmdline_len
);
2224 if (err
== EFI_SUCCESS
) {
2225 entry
->options
= xstrn8_to_16(content
, cmdline_len
);
2226 mangle_stub_cmdline(entry
->options
);
2231 static void config_load_xbootldr(
2233 EFI_HANDLE
*device
) {
2235 _cleanup_(file_closep
) EFI_FILE
*root_dir
= NULL
;
2236 EFI_HANDLE new_device
= NULL
; /* avoid false maybe-uninitialized warning */
2242 err
= partition_open(XBOOTLDR_GUID
, device
, &new_device
, &root_dir
);
2243 if (err
!= EFI_SUCCESS
)
2246 config_entry_add_unified(config
, new_device
, root_dir
);
2247 config_load_entries(config
, new_device
, root_dir
, NULL
);
2250 static EFI_STATUS
initrd_prepare(
2252 const ConfigEntry
*entry
,
2253 char16_t
**ret_options
,
2255 UINTN
*ret_initrd_size
) {
2259 assert(ret_options
);
2261 assert(ret_initrd_size
);
2263 if (entry
->type
!= LOADER_LINUX
|| !entry
->initrd
) {
2266 ret_initrd_size
= 0;
2270 /* Note that order of initrds matters. The kernel will only look for microcode updates in the very
2271 * first one it sees. */
2273 /* Add initrd= to options for older kernels that do not support LINUX_INITRD_MEDIA. Should be dropped
2274 * if linux_x86.c is dropped. */
2275 _cleanup_free_ char16_t
*options
= NULL
;
2279 _cleanup_free_
uint8_t *initrd
= NULL
;
2281 STRV_FOREACH(i
, entry
->initrd
) {
2282 _cleanup_free_ char16_t
*o
= options
;
2284 options
= xpool_print(L
"%s initrd=%s", o
, *i
);
2286 options
= xpool_print(L
"initrd=%s", *i
);
2288 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
2289 err
= root
->Open(root
, &handle
, *i
, EFI_FILE_MODE_READ
, 0);
2290 if (err
!= EFI_SUCCESS
)
2293 _cleanup_free_ EFI_FILE_INFO
*info
= NULL
;
2294 err
= get_file_info_harder(handle
, &info
, NULL
);
2295 if (err
!= EFI_SUCCESS
)
2298 if (info
->FileSize
== 0) /* Automatically skip over empty files */
2301 UINTN new_size
, read_size
= info
->FileSize
;
2302 if (__builtin_add_overflow(size
, read_size
, &new_size
))
2303 return EFI_OUT_OF_RESOURCES
;
2304 initrd
= xrealloc(initrd
, size
, new_size
);
2306 err
= handle
->Read(handle
, &read_size
, initrd
+ size
);
2307 if (err
!= EFI_SUCCESS
)
2310 /* Make sure the actual read size is what we expected. */
2311 assert(size
+ read_size
== new_size
);
2315 if (entry
->options
) {
2316 _cleanup_free_ char16_t
*o
= options
;
2317 options
= xpool_print(L
"%s %s", o
, entry
->options
);
2320 *ret_options
= TAKE_PTR(options
);
2321 *ret_initrd
= TAKE_PTR(initrd
);
2322 *ret_initrd_size
= size
;
2326 static EFI_STATUS
image_start(
2327 EFI_HANDLE parent_image
,
2328 const ConfigEntry
*entry
) {
2330 _cleanup_(devicetree_cleanup
) struct devicetree_state dtstate
= {};
2331 _cleanup_(unload_imagep
) EFI_HANDLE image
= NULL
;
2332 _cleanup_free_ EFI_DEVICE_PATH
*path
= NULL
;
2337 /* If this loader entry has a special way to boot, try that first. */
2339 (void) entry
->call();
2341 _cleanup_(file_closep
) EFI_FILE
*image_root
= NULL
;
2342 err
= open_volume(entry
->device
, &image_root
);
2343 if (err
!= EFI_SUCCESS
)
2344 return log_error_status_stall(err
, L
"Error opening root path: %r", err
);
2346 err
= make_file_device_path(entry
->device
, entry
->loader
, &path
);
2347 if (err
!= EFI_SUCCESS
)
2348 return log_error_status_stall(err
, L
"Error making file device path: %r", err
);
2350 UINTN initrd_size
= 0;
2351 _cleanup_free_
void *initrd
= NULL
;
2352 _cleanup_free_ char16_t
*options_initrd
= NULL
;
2353 err
= initrd_prepare(image_root
, entry
, &options_initrd
, &initrd
, &initrd_size
);
2354 if (err
!= EFI_SUCCESS
)
2355 return log_error_status_stall(err
, L
"Error preparing initrd: %r", err
);
2357 err
= shim_load_image(parent_image
, path
, &image
);
2358 if (err
!= EFI_SUCCESS
)
2359 return log_error_status_stall(err
, L
"Error loading %s: %r", entry
->loader
, err
);
2361 if (entry
->devicetree
) {
2362 err
= devicetree_install(&dtstate
, image_root
, entry
->devicetree
);
2363 if (err
!= EFI_SUCCESS
)
2364 return log_error_status_stall(err
, L
"Error loading %s: %r", entry
->devicetree
, err
);
2367 _cleanup_(cleanup_initrd
) EFI_HANDLE initrd_handle
= NULL
;
2368 err
= initrd_register(initrd
, initrd_size
, &initrd_handle
);
2369 if (err
!= EFI_SUCCESS
)
2370 return log_error_status_stall(err
, L
"Error registering initrd: %r", err
);
2372 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
;
2373 err
= BS
->HandleProtocol(image
, &LoadedImageProtocol
, (void **) &loaded_image
);
2374 if (err
!= EFI_SUCCESS
)
2375 return log_error_status_stall(err
, L
"Error getting LoadedImageProtocol handle: %r", err
);
2377 char16_t
*options
= options_initrd
?: entry
->options
;
2379 loaded_image
->LoadOptions
= options
;
2380 loaded_image
->LoadOptionsSize
= strsize16(options
);
2382 /* Try to log any options to the TPM, especially to catch manually edited options */
2383 (void) tpm_log_load_options(options
, NULL
);
2386 efivar_set_time_usec(LOADER_GUID
, L
"LoaderTimeExecUSec", 0);
2387 err
= BS
->StartImage(image
, NULL
, NULL
);
2388 graphics_mode(false);
2389 if (err
== EFI_SUCCESS
)
2392 /* Try calling the kernel compat entry point if one exists. */
2393 if (err
== EFI_UNSUPPORTED
&& entry
->type
== LOADER_LINUX
) {
2394 uint32_t compat_address
;
2396 err
= pe_kernel_info(loaded_image
->ImageBase
, &compat_address
);
2397 if (err
!= EFI_SUCCESS
) {
2398 if (err
!= EFI_UNSUPPORTED
)
2399 return log_error_status_stall(err
, L
"Error finding kernel compat entry address: %r", err
);
2400 } else if (compat_address
> 0) {
2401 EFI_IMAGE_ENTRY_POINT kernel_entry
=
2402 (EFI_IMAGE_ENTRY_POINT
) ((uint8_t *) loaded_image
->ImageBase
+ compat_address
);
2404 err
= kernel_entry(image
, ST
);
2405 graphics_mode(false);
2406 if (err
== EFI_SUCCESS
)
2409 err
= EFI_UNSUPPORTED
;
2412 return log_error_status_stall(err
, L
"Failed to execute %s (%s): %r", entry
->title_show
, entry
->loader
, err
);
2415 static void config_free(Config
*config
) {
2417 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
2418 config_entry_free(config
->entries
[i
]);
2419 free(config
->entries
);
2420 free(config
->entry_default_config
);
2421 free(config
->entry_oneshot
);
2424 static void config_write_entries_to_variable(Config
*config
) {
2425 _cleanup_free_
char *buffer
= NULL
;
2431 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
2432 sz
+= strsize16(config
->entries
[i
]->id
);
2434 p
= buffer
= xmalloc(sz
);
2436 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
2437 p
= mempcpy(p
, config
->entries
[i
]->id
, strsize16(config
->entries
[i
]->id
));
2439 assert(p
== buffer
+ sz
);
2441 /* Store the full list of discovered entries. */
2442 (void) efivar_set_raw(LOADER_GUID
, L
"LoaderEntries", buffer
, sz
, 0);
2445 static void save_selected_entry(const Config
*config
, const ConfigEntry
*entry
) {
2448 assert(entry
->loader
|| !entry
->call
);
2450 /* Always export the selected boot entry to the system in a volatile var. */
2451 (void) efivar_set(LOADER_GUID
, L
"LoaderEntrySelected", entry
->id
, 0);
2453 /* Do not save or delete if this was a oneshot boot. */
2454 if (streq16(config
->entry_oneshot
, entry
->id
))
2457 if (config
->use_saved_entry_efivar
|| (!config
->entry_default_efivar
&& config
->use_saved_entry
)) {
2458 /* Avoid unnecessary NVRAM writes. */
2459 if (streq16(config
->entry_saved
, entry
->id
))
2462 (void) efivar_set(LOADER_GUID
, L
"LoaderEntryLastBooted", entry
->id
, EFI_VARIABLE_NON_VOLATILE
);
2464 /* Delete the non-volatile var if not needed. */
2465 (void) efivar_set(LOADER_GUID
, L
"LoaderEntryLastBooted", NULL
, EFI_VARIABLE_NON_VOLATILE
);
2468 static EFI_STATUS
secure_boot_discover_keys(Config
*config
, EFI_FILE
*root_dir
) {
2470 _cleanup_(file_closep
) EFI_FILE
*keys_basedir
= NULL
;
2472 if (secure_boot_mode() != SECURE_BOOT_SETUP
)
2475 /* the lack of a 'keys' directory is not fatal and is silently ignored */
2476 err
= open_directory(root_dir
, u
"\\loader\\keys", &keys_basedir
);
2477 if (err
== EFI_NOT_FOUND
)
2479 if (err
!= EFI_SUCCESS
)
2483 _cleanup_free_ EFI_FILE_INFO
*dirent
= NULL
;
2484 size_t dirent_size
= 0;
2485 ConfigEntry
*entry
= NULL
;
2487 err
= readdir_harder(keys_basedir
, &dirent
, &dirent_size
);
2488 if (err
!= EFI_SUCCESS
|| !dirent
)
2491 if (dirent
->FileName
[0] == '.')
2494 if (!FLAGS_SET(dirent
->Attribute
, EFI_FILE_DIRECTORY
))
2497 entry
= xnew(ConfigEntry
, 1);
2498 *entry
= (ConfigEntry
) {
2499 .id
= xpool_print(L
"secure-boot-keys-%s", dirent
->FileName
),
2500 .title
= xpool_print(L
"Enroll Secure Boot keys: %s", dirent
->FileName
),
2501 .path
= xpool_print(L
"\\loader\\keys\\%s", dirent
->FileName
),
2502 .type
= LOADER_SECURE_BOOT_KEYS
,
2506 config_add_entry(config
, entry
);
2508 if (config
->secure_boot_enroll
== ENROLL_FORCE
&& strcaseeq16(dirent
->FileName
, u
"auto"))
2509 /* if we auto enroll successfully this call does not return, if it fails we still
2510 * want to add other potential entries to the menu */
2511 secure_boot_enroll_at(root_dir
, entry
->path
);
2517 static void export_variables(
2518 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
,
2519 const char16_t
*loaded_image_path
,
2520 uint64_t init_usec
) {
2522 static const uint64_t loader_features
=
2523 EFI_LOADER_FEATURE_CONFIG_TIMEOUT
|
2524 EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT
|
2525 EFI_LOADER_FEATURE_ENTRY_DEFAULT
|
2526 EFI_LOADER_FEATURE_ENTRY_ONESHOT
|
2527 EFI_LOADER_FEATURE_BOOT_COUNTING
|
2528 EFI_LOADER_FEATURE_XBOOTLDR
|
2529 EFI_LOADER_FEATURE_RANDOM_SEED
|
2530 EFI_LOADER_FEATURE_LOAD_DRIVER
|
2531 EFI_LOADER_FEATURE_SORT_KEY
|
2532 EFI_LOADER_FEATURE_SAVED_ENTRY
|
2533 EFI_LOADER_FEATURE_DEVICETREE
|
2536 _cleanup_free_ char16_t
*infostr
= NULL
, *typestr
= NULL
;
2539 assert(loaded_image
);
2541 efivar_set_time_usec(LOADER_GUID
, L
"LoaderTimeInitUSec", init_usec
);
2542 efivar_set(LOADER_GUID
, L
"LoaderInfo", L
"systemd-boot " GIT_VERSION
, 0);
2544 infostr
= xpool_print(L
"%s %u.%02u", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
2545 efivar_set(LOADER_GUID
, L
"LoaderFirmwareInfo", infostr
, 0);
2547 typestr
= xpool_print(L
"UEFI %u.%02u", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
2548 efivar_set(LOADER_GUID
, L
"LoaderFirmwareType", typestr
, 0);
2550 (void) efivar_set_uint64_le(LOADER_GUID
, L
"LoaderFeatures", loader_features
, 0);
2552 /* the filesystem path to this image, to prevent adding ourselves to the menu */
2553 efivar_set(LOADER_GUID
, L
"LoaderImageIdentifier", loaded_image_path
, 0);
2555 /* export the device path this image is started from */
2556 if (disk_get_part_uuid(loaded_image
->DeviceHandle
, uuid
) == EFI_SUCCESS
)
2557 efivar_set(LOADER_GUID
, L
"LoaderDevicePartUUID", uuid
, 0);
2560 static void config_load_all_entries(
2562 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
,
2563 const char16_t
*loaded_image_path
,
2564 EFI_FILE
*root_dir
) {
2567 assert(loaded_image
);
2570 config_load_defaults(config
, root_dir
);
2572 /* scan /EFI/Linux/ directory */
2573 config_entry_add_unified(config
, loaded_image
->DeviceHandle
, root_dir
);
2575 /* scan /loader/entries/\*.conf files */
2576 config_load_entries(config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
);
2578 /* Similar, but on any XBOOTLDR partition */
2579 config_load_xbootldr(config
, loaded_image
->DeviceHandle
);
2581 /* sort entries after version number */
2582 sort_pointer_array((void **) config
->entries
, config
->entry_count
, (compare_pointer_func_t
) config_entry_compare
);
2584 /* if we find some well-known loaders, add them to the end of the list */
2585 config_entry_add_osx(config
);
2586 config_entry_add_windows(config
, loaded_image
->DeviceHandle
, root_dir
);
2587 config_entry_add_loader_auto(config
, loaded_image
->DeviceHandle
, root_dir
, NULL
,
2588 L
"auto-efi-shell", 's', L
"EFI Shell", L
"\\shell" EFI_MACHINE_TYPE_NAME
".efi");
2589 config_entry_add_loader_auto(config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
,
2590 L
"auto-efi-default", '\0', L
"EFI Default Loader", NULL
);
2592 if (config
->auto_firmware
&& FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
)) {
2593 ConfigEntry
*entry
= xnew(ConfigEntry
, 1);
2594 *entry
= (ConfigEntry
) {
2595 .id
= xstrdup16(u
"auto-reboot-to-firmware-setup"),
2596 .title
= xstrdup16(u
"Reboot Into Firmware Interface"),
2597 .call
= reboot_into_firmware
,
2601 config_add_entry(config
, entry
);
2604 /* find if secure boot signing keys exist and autoload them if necessary
2605 otherwise creates menu entries so that the user can load them manually
2606 if the secure-boot-enroll variable is set to no (the default), we do not
2607 even search for keys on the ESP */
2608 if (config
->secure_boot_enroll
!= ENROLL_OFF
)
2609 secure_boot_discover_keys(config
, root_dir
);
2611 if (config
->entry_count
== 0)
2614 config_write_entries_to_variable(config
);
2616 config_title_generate(config
);
2618 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
2619 config_default_entry_select(config
);
2622 static EFI_STATUS
discover_root_dir(EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
, EFI_FILE
**ret_dir
) {
2623 if (is_direct_boot(loaded_image
->DeviceHandle
))
2624 return vmm_open(&loaded_image
->DeviceHandle
, ret_dir
);
2626 return open_volume(loaded_image
->DeviceHandle
, ret_dir
);
2629 EFI_STATUS
efi_main(EFI_HANDLE image
, EFI_SYSTEM_TABLE
*sys_table
) {
2630 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
;
2631 _cleanup_(file_closep
) EFI_FILE
*root_dir
= NULL
;
2632 _cleanup_(config_free
) Config config
= {};
2633 _cleanup_free_ char16_t
*loaded_image_path
= NULL
;
2638 InitializeLib(image
, sys_table
);
2639 init_usec
= time_usec();
2640 debug_hook(L
"systemd-boot");
2641 /* Uncomment the next line if you need to wait for debugger. */
2644 err
= BS
->OpenProtocol(image
,
2645 &LoadedImageProtocol
,
2646 (void **)&loaded_image
,
2649 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
2650 if (err
!= EFI_SUCCESS
)
2651 return log_error_status_stall(err
, L
"Error getting a LoadedImageProtocol handle: %r", err
);
2653 (void) device_path_to_str(loaded_image
->FilePath
, &loaded_image_path
);
2655 export_variables(loaded_image
, loaded_image_path
, init_usec
);
2657 err
= discover_root_dir(loaded_image
, &root_dir
);
2658 if (err
!= EFI_SUCCESS
)
2659 return log_error_status_stall(err
, L
"Unable to open root directory: %r", err
);
2661 (void) load_drivers(image
, loaded_image
, root_dir
);
2663 config_load_all_entries(&config
, loaded_image
, loaded_image_path
, root_dir
);
2665 if (config
.entry_count
== 0) {
2666 log_error_stall(L
"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2670 /* select entry or show menu when key is pressed or timeout is set */
2671 if (config
.force_menu
|| config
.timeout_sec
> 0)
2676 /* Block up to 100ms to give firmware time to get input working. */
2677 err
= console_key_read(&key
, 100 * 1000);
2678 if (err
== EFI_SUCCESS
) {
2679 /* find matching key in config entries */
2680 UINTN idx
= entry_lookup_key(&config
, config
.idx_default
, KEYCHAR(key
));
2681 if (idx
!= IDX_INVALID
)
2682 config
.idx_default
= idx
;
2691 entry
= config
.entries
[config
.idx_default
];
2693 efivar_set_time_usec(LOADER_GUID
, L
"LoaderTimeMenuUSec", 0);
2694 if (!menu_run(&config
, &entry
, loaded_image_path
))
2698 /* if auto enrollment is activated, we try to load keys for the given entry. */
2699 if (entry
->type
== LOADER_SECURE_BOOT_KEYS
&& config
.secure_boot_enroll
!= ENROLL_OFF
) {
2700 err
= secure_boot_enroll_at(root_dir
, entry
->path
);
2701 if (err
!= EFI_SUCCESS
)
2706 /* Run special entry like "reboot" now. Those that have a loader
2707 * will be handled by image_start() instead. */
2708 if (entry
->call
&& !entry
->loader
) {
2713 config_entry_bump_counters(entry
, root_dir
);
2714 save_selected_entry(&config
, entry
);
2716 /* Optionally, read a random seed off the ESP and pass it to the OS */
2717 (void) process_random_seed(root_dir
);
2719 err
= image_start(image
, entry
);
2720 if (err
!= EFI_SUCCESS
)
2724 config
.timeout_sec
= 0;
2728 BS
->CloseProtocol(image
, &LoadedImageProtocol
, image
, NULL
);