1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "bootspec-fundamental.h"
10 #include "devicetree.h"
13 #include "efivars-fundamental.h"
19 #include "random-seed.h"
20 #include "secure-boot.h"
26 #ifndef GNU_EFI_USE_MS_ABI
27 /* We do not use uefi_call_wrapper() in systemd-boot. As such, we rely on the
28 * compiler to do the calling convention conversion for us. This is check is
29 * to make sure the -DGNU_EFI_USE_MS_ABI was passed to the comiler. */
30 #error systemd-boot requires compilation with GNU_EFI_USE_MS_ABI defined.
33 #define TEXT_ATTR_SWAP(c) EFI_TEXT_ATTR(((c) & 0b11110000) >> 4, (c) & 0b1111)
35 /* Magic string for recognizing our own binaries */
36 _used_
_section_(".sdmagic") static const char magic
[] =
37 "#### LoaderInfo: systemd-boot " GIT_VERSION
" ####";
39 /* Makes systemd-boot available from \EFI\Linux\ for testing purposes. */
40 _used_
_section_(".osrel") static const char osrel
[] =
42 "VERSION=\"" GIT_VERSION
"\"\n"
43 "NAME=\"systemd-boot " GIT_VERSION
"\"\n";
49 LOADER_LINUX
, /* Boot loader spec type #1 entries */
50 LOADER_UNIFIED_LINUX
, /* Boot loader spec type #2 entries */
51 LOADER_SECURE_BOOT_KEYS
,
55 char16_t
*id
; /* The unique identifier for this entry (typically the filename of the file defining the entry) */
56 char16_t
*title_show
; /* The string to actually display (this is made unique before showing) */
57 char16_t
*title
; /* The raw (human readable) title string of the entry (not necessarily unique) */
58 char16_t
*sort_key
; /* The string to use as primary sort key, usually ID= from os-release, possibly suffixed */
59 char16_t
*version
; /* The raw (human readable) version string of the entry */
62 enum loader_type type
;
68 EFI_STATUS (*call
)(void);
72 char16_t
*current_name
;
77 ConfigEntry
**entries
;
80 UINTN idx_default_efivar
;
81 uint32_t timeout_sec
; /* Actual timeout used (efi_main() override > efivar > config). */
82 uint32_t timeout_sec_config
;
83 uint32_t timeout_sec_efivar
;
84 char16_t
*entry_default_config
;
85 char16_t
*entry_default_efivar
;
86 char16_t
*entry_oneshot
;
87 char16_t
*entry_saved
;
91 bool reboot_for_bitlocker
;
92 secure_boot_enroll secure_boot_enroll
;
95 bool use_saved_entry_efivar
;
98 int64_t console_mode_efivar
;
99 RandomSeedMode random_seed_mode
;
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_ALT_PRESSED
, 0, 'd'):
262 for (k
= first
+ cursor
; k
< len
&& line
[k
] == ' '; k
++)
264 for (; k
< len
&& line
[k
] != ' '; k
++)
267 for (UINTN i
= first
+ cursor
; i
+ clear
< len
; i
++)
268 line
[i
] = line
[i
+ clear
];
273 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'w'):
274 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('w')):
275 case KEYPRESS(EFI_ALT_PRESSED
, 0, CHAR_BACKSPACE
):
276 /* backward-kill-word */
278 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
279 cursor_left(&cursor
, &first
);
281 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ') {
282 cursor_left(&cursor
, &first
);
286 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ') {
287 cursor_left(&cursor
, &first
);
291 for (UINTN i
= first
+ cursor
; i
+ clear
< len
; i
++)
292 line
[i
] = line
[i
+ clear
];
297 case KEYPRESS(0, SCAN_DELETE
, 0):
298 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'd'):
299 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('d')):
302 if (first
+ cursor
== len
)
304 for (UINTN i
= first
+ cursor
; i
< len
; i
++)
310 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'k'):
311 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('k')):
313 line
[first
+ cursor
] = '\0';
314 clear
= len
- (first
+ cursor
);
315 len
= first
+ cursor
;
318 case KEYPRESS(0, 0, CHAR_LINEFEED
):
319 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
320 case KEYPRESS(0, CHAR_CARRIAGE_RETURN
, 0): /* EZpad Mini 4s firmware sends malformed events */
321 case KEYPRESS(0, CHAR_CARRIAGE_RETURN
, CHAR_CARRIAGE_RETURN
): /* Teclast X98+ II firmware sends malformed events */
322 if (!streq16(line
, *line_in
)) {
324 *line_in
= TAKE_PTR(line
);
328 case KEYPRESS(0, 0, CHAR_BACKSPACE
):
331 if (first
== 0 && cursor
== 0)
333 for (UINTN i
= first
+ cursor
-1; i
< len
; i
++)
339 if (cursor
> 0 || first
== 0)
341 /* show full line if it fits */
347 /* jump left to see what we delete */
357 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
358 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
361 for (UINTN i
= len
; i
> first
+ cursor
; i
--)
363 line
[first
+ cursor
] = KEYCHAR(key
);
366 if (cursor
+1 < x_max
)
368 else if (first
+ cursor
< len
)
375 static UINTN
entry_lookup_key(Config
*config
, UINTN start
, char16_t key
) {
381 /* select entry by number key */
382 if (key
>= '1' && key
<= '9') {
384 if (i
> config
->entry_count
)
385 i
= config
->entry_count
;
389 /* find matching key in config entries */
390 for (UINTN i
= start
; i
< config
->entry_count
; i
++)
391 if (config
->entries
[i
]->key
== key
)
394 for (UINTN i
= 0; i
< start
; i
++)
395 if (config
->entries
[i
]->key
== key
)
401 static char16_t
*update_timeout_efivar(uint32_t *t
, bool inc
) {
406 *t
= inc
? TIMEOUT_MAX
: (*t
- 1);
409 *t
= inc
? TIMEOUT_MENU_FORCE
: TIMEOUT_UNSET
;
411 case TIMEOUT_MENU_FORCE
:
412 *t
= inc
? TIMEOUT_MENU_HIDDEN
: TIMEOUT_UNSET
;
414 case TIMEOUT_MENU_HIDDEN
:
415 *t
= inc
? TIMEOUT_MIN
: TIMEOUT_MENU_FORCE
;
423 return xstrdup16(u
"Menu timeout defined by configuration file.");
424 case TIMEOUT_MENU_FORCE
:
425 return xstrdup16(u
"Timeout disabled, menu will always be shown.");
426 case TIMEOUT_MENU_HIDDEN
:
427 return xstrdup16(u
"Menu disabled. Hold down key at bootup to show menu.");
429 return xpool_print(L
"Menu timeout set to %u s.", *t
);
433 static bool unicode_supported(void) {
434 static int cache
= -1;
437 /* Basic unicode box drawing support is mandated by the spec, but it does
438 * not hurt to make sure it works. */
439 cache
= ST
->ConOut
->TestString(ST
->ConOut
, (char16_t
*) L
"─") == EFI_SUCCESS
;
444 static void ps_string(const char16_t
*fmt
, const void *value
) {
450 static void ps_bool(const char16_t
*fmt
, bool value
) {
452 Print(fmt
, yes_no(value
));
455 static bool ps_continue(void) {
456 if (unicode_supported())
457 Print(L
"\n─── Press any key to continue, ESC or q to quit. ───\n\n");
459 Print(L
"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
462 return console_key_read(&key
, UINT64_MAX
) == EFI_SUCCESS
&&
463 !IN_SET(key
, KEYPRESS(0, SCAN_ESC
, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q'));
466 static void print_status(Config
*config
, char16_t
*loaded_image_path
) {
468 uint32_t screen_width
= 0, screen_height
= 0;
469 SecureBootMode secure
;
470 _cleanup_free_ char16_t
*device_part_uuid
= NULL
;
473 assert(loaded_image_path
);
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
);
531 ps_string(L
" random-seed-mode: %s\n", random_seed_modes_table
[config
->random_seed_mode
]);
533 switch (config
->secure_boot_enroll
) {
535 Print(L
" secure-boot-enroll: off\n"); break;
537 Print(L
" secure-boot-enroll: manual\n"); break;
539 Print(L
" secure-boot-enroll: force\n"); break;
541 assert_not_reached();
544 switch (config
->console_mode
) {
545 case CONSOLE_MODE_AUTO
:
546 Print(L
" console-mode (config): %s\n", L
"auto"); break;
547 case CONSOLE_MODE_KEEP
:
548 Print(L
" console-mode (config): %s\n", L
"keep"); break;
549 case CONSOLE_MODE_FIRMWARE_MAX
:
550 Print(L
" console-mode (config): %s\n", L
"max"); break;
552 Print(L
" console-mode (config): %ld\n", config
->console_mode
); break;
555 /* EFI var console mode is always a concrete value or unset. */
556 if (config
->console_mode_efivar
!= CONSOLE_MODE_KEEP
)
557 Print(L
"console-mode (EFI var): %ld\n", config
->console_mode_efivar
);
562 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
563 ConfigEntry
*entry
= config
->entries
[i
];
565 Print(L
" config entry: %" PRIuN L
"/%" PRIuN L
"\n", i
+ 1, config
->entry_count
);
566 ps_string(L
" id: %s\n", entry
->id
);
567 ps_string(L
" title: %s\n", entry
->title
);
568 ps_string(L
" title show: %s\n", streq16(entry
->title
, entry
->title_show
) ? NULL
: entry
->title_show
);
569 ps_string(L
" sort key: %s\n", entry
->sort_key
);
570 ps_string(L
" version: %s\n", entry
->version
);
571 ps_string(L
" machine-id: %s\n", entry
->machine_id
);
573 Print(L
" device: %D\n", DevicePathFromHandle(entry
->device
));
574 ps_string(L
" loader: %s\n", entry
->loader
);
575 STRV_FOREACH(initrd
, entry
->initrd
)
576 Print(L
" initrd: %s\n", *initrd
);
577 ps_string(L
" devicetree: %s\n", entry
->devicetree
);
578 ps_string(L
" options: %s\n", entry
->options
);
579 ps_bool(L
" internal call: %s\n", !!entry
->call
);
581 ps_bool(L
"counting boots: %s\n", entry
->tries_left
>= 0);
582 if (entry
->tries_left
>= 0) {
583 Print(L
" tries: %u left, %u done\n", entry
->tries_left
, entry
->tries_done
);
584 Print(L
" current path: %s\\%s\n", entry
->path
, entry
->current_name
);
585 Print(L
" next path: %s\\%s\n", entry
->path
, entry
->next_name
);
593 static EFI_STATUS
reboot_into_firmware(void) {
597 if (!FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
))
598 return log_error_status_stall(EFI_UNSUPPORTED
, L
"Reboot to firmware interface not supported.");
600 (void) efivar_get_uint64_le(EFI_GLOBAL_GUID
, L
"OsIndications", &osind
);
601 osind
|= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
603 err
= efivar_set_uint64_le(EFI_GLOBAL_GUID
, L
"OsIndications", osind
, EFI_VARIABLE_NON_VOLATILE
);
604 if (err
!= EFI_SUCCESS
)
605 return log_error_status_stall(err
, L
"Error setting OsIndications: %r", err
);
607 RT
->ResetSystem(EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
608 assert_not_reached();
611 static bool menu_run(
613 ConfigEntry
**chosen_entry
,
614 char16_t
*loaded_image_path
) {
617 assert(chosen_entry
);
618 assert(loaded_image_path
);
621 UINTN visible_max
= 0;
622 UINTN idx_highlight
= config
->idx_default
;
623 UINTN idx_highlight_prev
= 0;
624 UINTN idx
, idx_first
= 0, idx_last
= 0;
625 bool new_mode
= true, clear
= true;
626 bool refresh
= true, highlight
= false;
627 UINTN x_start
= 0, y_start
= 0, y_status
= 0;
629 _cleanup_(strv_freep
) char16_t
**lines
= NULL
;
630 _cleanup_free_ char16_t
*clearline
= NULL
, *separator
= NULL
, *status
= NULL
;
631 uint32_t timeout_efivar_saved
= config
->timeout_sec_efivar
;
632 uint32_t timeout_remain
= config
->timeout_sec
== TIMEOUT_MENU_FORCE
? 0 : config
->timeout_sec
;
633 bool exit
= false, run
= true, firmware_setup
= false;
634 int64_t console_mode_initial
= ST
->ConOut
->Mode
->Mode
, console_mode_efivar_saved
= config
->console_mode_efivar
;
635 UINTN default_efivar_saved
= config
->idx_default_efivar
;
637 graphics_mode(false);
638 ST
->ConIn
->Reset(ST
->ConIn
, false);
639 ST
->ConOut
->EnableCursor(ST
->ConOut
, false);
641 /* draw a single character to make ClearScreen work on some firmware */
644 err
= console_set_mode(config
->console_mode_efivar
!= CONSOLE_MODE_KEEP
?
645 config
->console_mode_efivar
: config
->console_mode
);
646 if (err
!= EFI_SUCCESS
) {
647 clear_screen(COLOR_NORMAL
);
648 log_error_stall(L
"Error switching console mode: %r", err
);
651 UINTN line_width
= 0, entry_padding
= 3;
656 console_query_mode(&x_max
, &y_max
);
658 /* account for padding+status */
659 visible_max
= y_max
- 2;
661 /* Drawing entries starts at idx_first until idx_last. We want to make
662 * sure that idx_highlight is centered, but not if we are close to the
663 * beginning/end of the entry list. Otherwise we would have a half-empty
665 if (config
->entry_count
<= visible_max
|| idx_highlight
<= visible_max
/ 2)
667 else if (idx_highlight
>= config
->entry_count
- (visible_max
/ 2))
668 idx_first
= config
->entry_count
- visible_max
;
670 idx_first
= idx_highlight
- (visible_max
/ 2);
671 idx_last
= idx_first
+ visible_max
- 1;
673 /* length of the longest entry */
675 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
676 line_width
= MAX(line_width
, strlen16(config
->entries
[i
]->title_show
));
677 line_width
= MIN(line_width
+ 2 * entry_padding
, x_max
);
679 /* offsets to center the entries on the screen */
680 x_start
= (x_max
- (line_width
)) / 2;
681 if (config
->entry_count
< visible_max
)
682 y_start
= ((visible_max
- config
->entry_count
) / 2) + 1;
686 /* Put status line after the entry list, but give it some breathing room. */
687 y_status
= MIN(y_start
+ MIN(visible_max
, config
->entry_count
) + 1, y_max
- 1);
689 lines
= strv_free(lines
);
690 clearline
= mfree(clearline
);
691 separator
= mfree(separator
);
693 /* menu entries title lines */
694 lines
= xnew(char16_t
*, config
->entry_count
+ 1);
696 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
699 lines
[i
] = xnew(char16_t
, line_width
+ 1);
700 padding
= (line_width
- MIN(strlen16(config
->entries
[i
]->title_show
), line_width
)) / 2;
702 for (j
= 0; j
< padding
; j
++)
705 for (UINTN k
= 0; config
->entries
[i
]->title_show
[k
] != '\0' && j
< line_width
; j
++, k
++)
706 lines
[i
][j
] = config
->entries
[i
]->title_show
[k
];
708 for (; j
< line_width
; j
++)
710 lines
[i
][line_width
] = '\0';
712 lines
[config
->entry_count
] = NULL
;
714 clearline
= xnew(char16_t
, x_max
+ 1);
715 separator
= xnew(char16_t
, x_max
+ 1);
716 for (UINTN i
= 0; i
< x_max
; i
++) {
718 separator
[i
] = unicode_supported() ? L
'─' : L
'-';
720 clearline
[x_max
] = 0;
721 separator
[x_max
] = 0;
728 clear_screen(COLOR_NORMAL
);
734 for (UINTN i
= idx_first
; i
<= idx_last
&& i
< config
->entry_count
; i
++) {
735 print_at(x_start
, y_start
+ i
- idx_first
,
736 (i
== idx_highlight
) ? COLOR_HIGHLIGHT
: COLOR_ENTRY
,
738 if (i
== config
->idx_default_efivar
)
740 y_start
+ i
- idx_first
,
741 (i
== idx_highlight
) ? COLOR_HIGHLIGHT
: COLOR_ENTRY
,
742 unicode_supported() ? L
" ►" : L
"=>");
745 } else if (highlight
) {
746 print_at(x_start
, y_start
+ idx_highlight_prev
- idx_first
, COLOR_ENTRY
, lines
[idx_highlight_prev
]);
747 print_at(x_start
, y_start
+ idx_highlight
- idx_first
, COLOR_HIGHLIGHT
, lines
[idx_highlight
]);
748 if (idx_highlight_prev
== config
->idx_default_efivar
)
750 y_start
+ idx_highlight_prev
- idx_first
,
752 unicode_supported() ? L
" ►" : L
"=>");
753 if (idx_highlight
== config
->idx_default_efivar
)
755 y_start
+ idx_highlight
- idx_first
,
757 unicode_supported() ? L
" ►" : L
"=>");
761 if (timeout_remain
> 0) {
763 status
= xpool_print(L
"Boot in %u s.", timeout_remain
);
767 /* If we draw the last char of the last line, the screen will scroll and break our
768 * input. Therefore, draw one less character then we could for the status message.
769 * Note that the same does not apply for the separator line as it will never be drawn
770 * on the last line. */
771 UINTN len
= strnlen16(status
, x_max
- 1);
772 UINTN x
= (x_max
- len
) / 2;
774 print_at(0, y_status
, COLOR_NORMAL
, clearline
+ x_max
- x
);
775 ST
->ConOut
->OutputString(ST
->ConOut
, status
);
776 ST
->ConOut
->OutputString(ST
->ConOut
, clearline
+ 1 + x
+ len
);
778 len
= MIN(MAX(len
, line_width
) + 2 * entry_padding
, x_max
);
779 x
= (x_max
- len
) / 2;
780 print_at(x
, y_status
- 1, COLOR_NORMAL
, separator
+ x_max
- len
);
782 print_at(0, y_status
- 1, COLOR_NORMAL
, clearline
);
783 print_at(0, y_status
, COLOR_NORMAL
, clearline
+ 1); /* See comment above. */
786 /* Beep several times so that the selected entry can be distinguished. */
788 beep(idx_highlight
+ 1);
790 err
= console_key_read(&key
, timeout_remain
> 0 ? 1000 * 1000 : UINT64_MAX
);
791 if (err
== EFI_NOT_READY
)
792 /* No input device returned a key, try again. This
793 * normally should not happen. */
795 if (err
== EFI_TIMEOUT
) {
796 assert(timeout_remain
> 0);
798 if (timeout_remain
== 0) {
806 if (err
!= EFI_SUCCESS
) {
813 /* clear status after keystroke */
814 status
= mfree(status
);
816 idx_highlight_prev
= idx_highlight
;
818 if (firmware_setup
) {
819 firmware_setup
= false;
820 if (key
== KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
))
821 reboot_into_firmware();
826 case KEYPRESS(0, SCAN_UP
, 0):
827 case KEYPRESS(0, 0, 'k'):
828 case KEYPRESS(0, 0, 'K'):
829 if (idx_highlight
> 0)
833 case KEYPRESS(0, SCAN_DOWN
, 0):
834 case KEYPRESS(0, 0, 'j'):
835 case KEYPRESS(0, 0, 'J'):
836 if (idx_highlight
< config
->entry_count
-1)
840 case KEYPRESS(0, SCAN_HOME
, 0):
841 case KEYPRESS(EFI_ALT_PRESSED
, 0, '<'):
842 if (idx_highlight
> 0) {
848 case KEYPRESS(0, SCAN_END
, 0):
849 case KEYPRESS(EFI_ALT_PRESSED
, 0, '>'):
850 if (idx_highlight
< config
->entry_count
-1) {
852 idx_highlight
= config
->entry_count
-1;
856 case KEYPRESS(0, SCAN_PAGE_UP
, 0):
857 if (idx_highlight
> visible_max
)
858 idx_highlight
-= visible_max
;
863 case KEYPRESS(0, SCAN_PAGE_DOWN
, 0):
864 idx_highlight
+= visible_max
;
865 if (idx_highlight
> config
->entry_count
-1)
866 idx_highlight
= config
->entry_count
-1;
869 case KEYPRESS(0, 0, CHAR_LINEFEED
):
870 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
871 case KEYPRESS(0, CHAR_CARRIAGE_RETURN
, 0): /* EZpad Mini 4s firmware sends malformed events */
872 case KEYPRESS(0, CHAR_CARRIAGE_RETURN
, CHAR_CARRIAGE_RETURN
): /* Teclast X98+ II firmware sends malformed events */
873 case KEYPRESS(0, SCAN_RIGHT
, 0):
877 case KEYPRESS(0, SCAN_F1
, 0):
878 case KEYPRESS(0, 0, 'h'):
879 case KEYPRESS(0, 0, 'H'):
880 case KEYPRESS(0, 0, '?'):
881 /* This must stay below 80 characters! Q/v/Ctrl+l/f deliberately not advertised. */
882 status
= xstrdup16(u
"(d)efault (t/T)timeout (e)dit (r/R)resolution (p)rint (h)elp");
885 case KEYPRESS(0, 0, 'Q'):
890 case KEYPRESS(0, 0, 'd'):
891 case KEYPRESS(0, 0, 'D'):
892 if (config
->idx_default_efivar
!= idx_highlight
) {
893 free(config
->entry_default_efivar
);
894 config
->entry_default_efivar
= xstrdup16(config
->entries
[idx_highlight
]->id
);
895 config
->idx_default_efivar
= idx_highlight
;
896 status
= xstrdup16(u
"Default boot entry selected.");
898 config
->entry_default_efivar
= mfree(config
->entry_default_efivar
);
899 config
->idx_default_efivar
= IDX_INVALID
;
900 status
= xstrdup16(u
"Default boot entry cleared.");
902 config
->use_saved_entry_efivar
= false;
906 case KEYPRESS(0, 0, '-'):
907 case KEYPRESS(0, 0, 'T'):
908 status
= update_timeout_efivar(&config
->timeout_sec_efivar
, false);
911 case KEYPRESS(0, 0, '+'):
912 case KEYPRESS(0, 0, 't'):
913 status
= update_timeout_efivar(&config
->timeout_sec_efivar
, true);
916 case KEYPRESS(0, 0, 'e'):
917 case KEYPRESS(0, 0, 'E'):
918 /* only the options of configured entries can be edited */
919 if (!config
->editor
|| !IN_SET(config
->entries
[idx_highlight
]->type
,
920 LOADER_EFI
, LOADER_LINUX
, LOADER_UNIFIED_LINUX
))
923 /* Unified kernels that are signed as a whole will not accept command line options
924 * when secure boot is enabled unless there is none embedded in the image. Do not try
925 * to pretend we can edit it to only have it be ignored. */
926 if (config
->entries
[idx_highlight
]->type
== LOADER_UNIFIED_LINUX
&&
927 secure_boot_enabled() &&
928 config
->entries
[idx_highlight
]->options
)
931 /* The edit line may end up on the last line of the screen. And even though we're
932 * not telling the firmware to advance the line, it still does in this one case,
933 * causing a scroll to happen that screws with our beautiful boot loader output.
934 * Since we cannot paint the last character of the edit line, we simply start
935 * at x-offset 1 for symmetry. */
936 print_at(1, y_status
, COLOR_EDIT
, clearline
+ 2);
937 exit
= line_edit(&config
->entries
[idx_highlight
]->options
, x_max
- 2, y_status
);
938 print_at(1, y_status
, COLOR_NORMAL
, clearline
+ 2);
941 case KEYPRESS(0, 0, 'v'):
942 status
= xpool_print(
943 L
"systemd-boot " GIT_VERSION L
" (" EFI_MACHINE_TYPE_NAME L
"), "
944 L
"UEFI Specification %u.%02u, Vendor %s %u.%02u",
945 ST
->Hdr
.Revision
>> 16,
946 ST
->Hdr
.Revision
& 0xffff,
948 ST
->FirmwareRevision
>> 16,
949 ST
->FirmwareRevision
& 0xffff);
952 case KEYPRESS(0, 0, 'p'):
953 case KEYPRESS(0, 0, 'P'):
954 print_status(config
, loaded_image_path
);
958 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'l'):
959 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('l')):
963 case KEYPRESS(0, 0, 'r'):
964 err
= console_set_mode(CONSOLE_MODE_NEXT
);
965 if (err
!= EFI_SUCCESS
)
966 status
= xpool_print(L
"Error changing console mode: %r", err
);
968 config
->console_mode_efivar
= ST
->ConOut
->Mode
->Mode
;
969 status
= xpool_print(L
"Console mode changed to %ld.", config
->console_mode_efivar
);
974 case KEYPRESS(0, 0, 'R'):
975 config
->console_mode_efivar
= CONSOLE_MODE_KEEP
;
976 err
= console_set_mode(config
->console_mode
== CONSOLE_MODE_KEEP
?
977 console_mode_initial
: config
->console_mode
);
978 if (err
!= EFI_SUCCESS
)
979 status
= xpool_print(L
"Error resetting console mode: %r", err
);
981 status
= xpool_print(L
"Console mode reset to %s default.",
982 config
->console_mode
== CONSOLE_MODE_KEEP
? L
"firmware" : L
"configuration file");
986 case KEYPRESS(0, 0, 'f'):
987 case KEYPRESS(0, 0, 'F'):
988 case KEYPRESS(0, SCAN_F2
, 0): /* Most vendors. */
989 case KEYPRESS(0, SCAN_F10
, 0): /* HP and Lenovo. */
990 case KEYPRESS(0, SCAN_DELETE
, 0): /* Same as F2. */
991 case KEYPRESS(0, SCAN_ESC
, 0): /* HP. */
992 if (FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
)) {
993 firmware_setup
= true;
994 /* Let's make sure the user really wants to do this. */
995 status
= xpool_print(L
"Press Enter to reboot into firmware interface.");
997 status
= xpool_print(L
"Reboot into firmware interface not supported.");
1001 /* jump with a hotkey directly to a matching entry */
1002 idx
= entry_lookup_key(config
, idx_highlight
+1, KEYCHAR(key
));
1003 if (idx
== IDX_INVALID
)
1005 idx_highlight
= idx
;
1009 if (idx_highlight
> idx_last
) {
1010 idx_last
= idx_highlight
;
1011 idx_first
= 1 + idx_highlight
- visible_max
;
1013 } else if (idx_highlight
< idx_first
) {
1014 idx_first
= idx_highlight
;
1015 idx_last
= idx_highlight
+ visible_max
-1;
1019 if (!refresh
&& idx_highlight
!= idx_highlight_prev
)
1023 *chosen_entry
= config
->entries
[idx_highlight
];
1025 /* Update EFI vars after we left the menu to reduce NVRAM writes. */
1027 if (default_efivar_saved
!= config
->idx_default_efivar
)
1028 efivar_set(LOADER_GUID
, L
"LoaderEntryDefault", config
->entry_default_efivar
, EFI_VARIABLE_NON_VOLATILE
);
1030 if (console_mode_efivar_saved
!= config
->console_mode_efivar
) {
1031 if (config
->console_mode_efivar
== CONSOLE_MODE_KEEP
)
1032 efivar_set(LOADER_GUID
, L
"LoaderConfigConsoleMode", NULL
, EFI_VARIABLE_NON_VOLATILE
);
1034 efivar_set_uint_string(LOADER_GUID
, L
"LoaderConfigConsoleMode",
1035 config
->console_mode_efivar
, EFI_VARIABLE_NON_VOLATILE
);
1038 if (timeout_efivar_saved
!= config
->timeout_sec_efivar
) {
1039 if (config
->timeout_sec_efivar
== TIMEOUT_UNSET
)
1040 efivar_set(LOADER_GUID
, L
"LoaderConfigTimeout", NULL
, EFI_VARIABLE_NON_VOLATILE
);
1042 efivar_set_uint_string(LOADER_GUID
, L
"LoaderConfigTimeout",
1043 config
->timeout_sec_efivar
, EFI_VARIABLE_NON_VOLATILE
);
1046 clear_screen(COLOR_NORMAL
);
1050 static void config_add_entry(Config
*config
, ConfigEntry
*entry
) {
1054 /* This is just for paranoia. */
1055 assert(config
->entry_count
< IDX_MAX
);
1057 if ((config
->entry_count
& 15) == 0) {
1058 config
->entries
= xrealloc(
1060 sizeof(void *) * config
->entry_count
,
1061 sizeof(void *) * (config
->entry_count
+ 16));
1063 config
->entries
[config
->entry_count
++] = entry
;
1066 static void config_entry_free(ConfigEntry
*entry
) {
1071 free(entry
->title_show
);
1073 free(entry
->sort_key
);
1074 free(entry
->version
);
1075 free(entry
->machine_id
);
1076 free(entry
->loader
);
1077 free(entry
->devicetree
);
1078 free(entry
->options
);
1079 strv_free(entry
->initrd
);
1081 free(entry
->current_name
);
1082 free(entry
->next_name
);
1086 static inline void config_entry_freep(ConfigEntry
**entry
) {
1087 config_entry_free(*entry
);
1090 static char *line_get_key_value(
1107 line
= content
+ *pos
;
1112 while (line
[linelen
] && !strchr8("\n\r", line
[linelen
]))
1115 /* move pos to next line */
1124 /* terminate line */
1125 line
[linelen
] = '\0';
1127 /* remove leading whitespace */
1128 while (strchr8(" \t", *line
)) {
1133 /* remove trailing whitespace */
1134 while (linelen
> 0 && strchr8(" \t", line
[linelen
- 1]))
1136 line
[linelen
] = '\0';
1141 /* split key/value */
1143 while (*value
&& !strchr8(sep
, *value
))
1149 while (*value
&& strchr8(sep
, *value
))
1153 if (value
[0] == '"' && line
[linelen
- 1] == '"') {
1155 line
[linelen
- 1] = '\0';
1164 static void config_defaults_load_from_file(Config
*config
, char *content
) {
1173 while ((line
= line_get_key_value(content
, " \t", &pos
, &key
, &value
))) {
1174 if (streq8(key
, "timeout")) {
1175 if (streq8( value
, "menu-force"))
1176 config
->timeout_sec_config
= TIMEOUT_MENU_FORCE
;
1177 else if (streq8(value
, "menu-hidden"))
1178 config
->timeout_sec_config
= TIMEOUT_MENU_HIDDEN
;
1181 if (!parse_number8(value
, &u
, NULL
) || u
> TIMEOUT_TYPE_MAX
) {
1182 log_error_stall(L
"Error parsing 'timeout' config option: %a", value
);
1185 config
->timeout_sec_config
= u
;
1187 config
->timeout_sec
= config
->timeout_sec_config
;
1191 if (streq8(key
, "default")) {
1192 if (value
[0] == '@' && !strcaseeq8(value
, "@saved")) {
1193 log_error_stall(L
"Unsupported special entry identifier: %a", value
);
1196 free(config
->entry_default_config
);
1197 config
->entry_default_config
= xstra_to_str(value
);
1201 if (streq8(key
, "editor")) {
1202 err
= parse_boolean(value
, &config
->editor
);
1203 if (err
!= EFI_SUCCESS
)
1204 log_error_stall(L
"Error parsing 'editor' config option: %a", value
);
1208 if (streq8(key
, "auto-entries")) {
1209 err
= parse_boolean(value
, &config
->auto_entries
);
1210 if (err
!= EFI_SUCCESS
)
1211 log_error_stall(L
"Error parsing 'auto-entries' config option: %a", value
);
1215 if (streq8(key
, "auto-firmware")) {
1216 err
= parse_boolean(value
, &config
->auto_firmware
);
1217 if (err
!= EFI_SUCCESS
)
1218 log_error_stall(L
"Error parsing 'auto-firmware' config option: %a", value
);
1222 if (streq8(key
, "beep")) {
1223 err
= parse_boolean(value
, &config
->beep
);
1224 if (err
!= EFI_SUCCESS
)
1225 log_error_stall(L
"Error parsing 'beep' config option: %a", value
);
1229 if (streq8(key
, "reboot-for-bitlocker")) {
1230 err
= parse_boolean(value
, &config
->reboot_for_bitlocker
);
1231 if (err
!= EFI_SUCCESS
)
1232 log_error_stall(L
"Error parsing 'reboot-for-bitlocker' config option: %a", value
);
1235 if (streq8(key
, "secure-boot-enroll")) {
1236 if (streq8(value
, "manual"))
1237 config
->secure_boot_enroll
= ENROLL_MANUAL
;
1238 else if (streq8(value
, "force"))
1239 config
->secure_boot_enroll
= ENROLL_FORCE
;
1240 else if (streq8(value
, "off"))
1241 config
->secure_boot_enroll
= ENROLL_OFF
;
1243 log_error_stall(L
"Error parsing 'secure-boot-enroll' config option: %a", value
);
1247 if (streq8(key
, "console-mode")) {
1248 if (streq8(value
, "auto"))
1249 config
->console_mode
= CONSOLE_MODE_AUTO
;
1250 else if (streq8(value
, "max"))
1251 config
->console_mode
= CONSOLE_MODE_FIRMWARE_MAX
;
1252 else if (streq8(value
, "keep"))
1253 config
->console_mode
= CONSOLE_MODE_KEEP
;
1256 if (!parse_number8(value
, &u
, NULL
) || u
> CONSOLE_MODE_RANGE_MAX
) {
1257 log_error_stall(L
"Error parsing 'console-mode' config option: %a", value
);
1260 config
->console_mode
= u
;
1265 if (streq8(key
, "random-seed-mode")) {
1266 if (streq8(value
, "off"))
1267 config
->random_seed_mode
= RANDOM_SEED_OFF
;
1268 else if (streq8(value
, "with-system-token"))
1269 config
->random_seed_mode
= RANDOM_SEED_WITH_SYSTEM_TOKEN
;
1270 else if (streq8(value
, "always"))
1271 config
->random_seed_mode
= RANDOM_SEED_ALWAYS
;
1275 err
= parse_boolean(value
, &on
);
1276 if (err
!= EFI_SUCCESS
) {
1277 log_error_stall(L
"Error parsing 'random-seed-mode' config option: %a", value
);
1281 config
->random_seed_mode
= on
? RANDOM_SEED_ALWAYS
: RANDOM_SEED_OFF
;
1288 static void config_entry_parse_tries(
1290 const char16_t
*path
,
1291 const char16_t
*file
,
1292 const char16_t
*suffix
) {
1300 * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
1301 * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
1302 * which case that counter as assumed to be zero, i.e. the missing part is synonymous to "-0").
1304 * Names we grok, and the series they result in:
1306 * foobar+3.efi → foobar+2-1.efi → foobar+1-2.efi → foobar+0-3.efi → STOP!
1307 * foobar+4-0.efi → foobar+3-1.efi → foobar+2-2.efi → foobar+1-3.efi → foobar+0-4.efi → STOP!
1310 const char16_t
*counter
= NULL
;
1312 char16_t
*plus
= strchr16(counter
?: file
, '+');
1314 /* We want the last "+". */
1321 /* No boot counter found. */
1325 uint64_t tries_left
, tries_done
= 0;
1326 size_t prefix_len
= counter
- file
;
1328 if (!parse_number16(counter
, &tries_left
, &counter
) || tries_left
> INT_MAX
)
1331 /* Parse done counter only if present. */
1332 if (*counter
== '-' && (!parse_number16(counter
+ 1, &tries_done
, &counter
) || tries_done
> INT_MAX
))
1335 /* Boot counter in the middle of the name? */
1336 if (!streq16(counter
, suffix
))
1339 entry
->tries_left
= tries_left
;
1340 entry
->tries_done
= tries_done
;
1341 entry
->path
= xstrdup16(path
);
1342 entry
->current_name
= xstrdup16(file
);
1343 entry
->next_name
= xpool_print(
1347 LESS_BY(tries_left
, 1u),
1348 MIN(tries_done
+ 1, (uint64_t) INT_MAX
),
1352 static void config_entry_bump_counters(ConfigEntry
*entry
, EFI_FILE
*root_dir
) {
1353 _cleanup_free_ char16_t
* old_path
= NULL
, *new_path
= NULL
;
1354 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1355 _cleanup_free_ EFI_FILE_INFO
*file_info
= NULL
;
1356 UINTN file_info_size
;
1362 if (entry
->tries_left
< 0)
1365 if (!entry
->path
|| !entry
->current_name
|| !entry
->next_name
)
1368 old_path
= xpool_print(L
"%s\\%s", entry
->path
, entry
->current_name
);
1370 err
= root_dir
->Open(root_dir
, &handle
, old_path
, EFI_FILE_MODE_READ
|EFI_FILE_MODE_WRITE
, 0ULL);
1371 if (err
!= EFI_SUCCESS
)
1374 err
= get_file_info_harder(handle
, &file_info
, &file_info_size
);
1375 if (err
!= EFI_SUCCESS
)
1378 /* And rename the file */
1379 strcpy16(file_info
->FileName
, entry
->next_name
);
1380 err
= handle
->SetInfo(handle
, &GenericFileInfo
, file_info_size
, file_info
);
1381 if (err
!= EFI_SUCCESS
) {
1382 log_error_stall(L
"Failed to rename '%s' to '%s', ignoring: %r", old_path
, entry
->next_name
, err
);
1386 /* Flush everything to disk, just in case… */
1387 (void) handle
->Flush(handle
);
1389 /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
1391 new_path
= xpool_print(L
"%s\\%s", entry
->path
, entry
->next_name
);
1392 efivar_set(LOADER_GUID
, L
"LoaderBootCountPath", new_path
, 0);
1394 /* If the file we just renamed is the loader path, then let's update that. */
1395 if (streq16(entry
->loader
, old_path
)) {
1396 free(entry
->loader
);
1397 entry
->loader
= TAKE_PTR(new_path
);
1401 static void config_entry_add_type1(
1405 const char16_t
*path
,
1406 const char16_t
*file
,
1408 const char16_t
*loaded_image_path
) {
1410 _cleanup_(config_entry_freep
) ConfigEntry
*entry
= NULL
;
1412 UINTN pos
= 0, n_initrd
= 0;
1423 entry
= xnew(ConfigEntry
, 1);
1424 *entry
= (ConfigEntry
) {
1429 while ((line
= line_get_key_value(content
, " \t", &pos
, &key
, &value
))) {
1430 if (streq8(key
, "title")) {
1432 entry
->title
= xstra_to_str(value
);
1436 if (streq8(key
, "sort-key")) {
1437 free(entry
->sort_key
);
1438 entry
->sort_key
= xstra_to_str(value
);
1442 if (streq8(key
, "version")) {
1443 free(entry
->version
);
1444 entry
->version
= xstra_to_str(value
);
1448 if (streq8(key
, "machine-id")) {
1449 free(entry
->machine_id
);
1450 entry
->machine_id
= xstra_to_str(value
);
1454 if (streq8(key
, "linux")) {
1455 free(entry
->loader
);
1456 entry
->type
= LOADER_LINUX
;
1457 entry
->loader
= xstra_to_path(value
);
1462 if (streq8(key
, "efi")) {
1463 entry
->type
= LOADER_EFI
;
1464 free(entry
->loader
);
1465 entry
->loader
= xstra_to_path(value
);
1467 /* do not add an entry for ourselves */
1468 if (loaded_image_path
&& strcaseeq16(entry
->loader
, loaded_image_path
)) {
1469 entry
->type
= LOADER_UNDEFINED
;
1475 if (streq8(key
, "architecture")) {
1476 /* do not add an entry for an EFI image of architecture not matching with that of the image */
1477 if (!streq8(value
, EFI_MACHINE_TYPE_NAME
)) {
1478 entry
->type
= LOADER_UNDEFINED
;
1484 if (streq8(key
, "devicetree")) {
1485 free(entry
->devicetree
);
1486 entry
->devicetree
= xstra_to_path(value
);
1490 if (streq8(key
, "initrd")) {
1491 entry
->initrd
= xrealloc(
1493 n_initrd
== 0 ? 0 : (n_initrd
+ 1) * sizeof(uint16_t *),
1494 (n_initrd
+ 2) * sizeof(uint16_t *));
1495 entry
->initrd
[n_initrd
++] = xstra_to_path(value
);
1496 entry
->initrd
[n_initrd
] = NULL
;
1500 if (streq8(key
, "options")) {
1501 _cleanup_free_ char16_t
*new = NULL
;
1503 new = xstra_to_str(value
);
1504 if (entry
->options
) {
1505 char16_t
*s
= xpool_print(L
"%s %s", entry
->options
, new);
1506 free(entry
->options
);
1509 entry
->options
= TAKE_PTR(new);
1515 if (entry
->type
== LOADER_UNDEFINED
)
1518 /* check existence */
1519 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1520 err
= root_dir
->Open(root_dir
, &handle
, entry
->loader
, EFI_FILE_MODE_READ
, 0ULL);
1521 if (err
!= EFI_SUCCESS
)
1524 entry
->device
= device
;
1525 entry
->id
= xstrdup16(file
);
1526 strtolower16(entry
->id
);
1528 config_add_entry(config
, entry
);
1530 config_entry_parse_tries(entry
, path
, file
, L
".conf");
1534 static void config_load_defaults(Config
*config
, EFI_FILE
*root_dir
) {
1535 _cleanup_free_
char *content
= NULL
;
1541 *config
= (Config
) {
1543 .auto_entries
= true,
1544 .auto_firmware
= true,
1545 .reboot_for_bitlocker
= false,
1546 .secure_boot_enroll
= ENROLL_MANUAL
,
1547 .random_seed_mode
= RANDOM_SEED_WITH_SYSTEM_TOKEN
,
1548 .idx_default_efivar
= IDX_INVALID
,
1549 .console_mode
= CONSOLE_MODE_KEEP
,
1550 .console_mode_efivar
= CONSOLE_MODE_KEEP
,
1551 .timeout_sec_config
= TIMEOUT_UNSET
,
1552 .timeout_sec_efivar
= TIMEOUT_UNSET
,
1555 err
= file_read(root_dir
, L
"\\loader\\loader.conf", 0, 0, &content
, NULL
);
1556 if (err
== EFI_SUCCESS
)
1557 config_defaults_load_from_file(config
, content
);
1559 err
= efivar_get_uint_string(LOADER_GUID
, L
"LoaderConfigTimeout", &value
);
1560 if (err
== EFI_SUCCESS
) {
1561 config
->timeout_sec_efivar
= MIN(value
, TIMEOUT_TYPE_MAX
);
1562 config
->timeout_sec
= config
->timeout_sec_efivar
;
1565 err
= efivar_get_uint_string(LOADER_GUID
, L
"LoaderConfigTimeoutOneShot", &value
);
1566 if (err
== EFI_SUCCESS
) {
1567 /* Unset variable now, after all it's "one shot". */
1568 (void) efivar_set(LOADER_GUID
, L
"LoaderConfigTimeoutOneShot", NULL
, EFI_VARIABLE_NON_VOLATILE
);
1570 config
->timeout_sec
= MIN(value
, TIMEOUT_TYPE_MAX
);
1571 config
->force_menu
= true; /* force the menu when this is set */
1574 err
= efivar_get_uint_string(LOADER_GUID
, L
"LoaderConfigConsoleMode", &value
);
1575 if (err
== EFI_SUCCESS
)
1576 config
->console_mode_efivar
= value
;
1578 err
= efivar_get(LOADER_GUID
, L
"LoaderEntryOneShot", &config
->entry_oneshot
);
1579 if (err
== EFI_SUCCESS
)
1580 /* Unset variable now, after all it's "one shot". */
1581 (void) efivar_set(LOADER_GUID
, L
"LoaderEntryOneShot", NULL
, EFI_VARIABLE_NON_VOLATILE
);
1583 (void) efivar_get(LOADER_GUID
, L
"LoaderEntryDefault", &config
->entry_default_efivar
);
1585 strtolower16(config
->entry_default_config
);
1586 strtolower16(config
->entry_default_efivar
);
1587 strtolower16(config
->entry_oneshot
);
1588 strtolower16(config
->entry_saved
);
1590 config
->use_saved_entry
= streq16(config
->entry_default_config
, L
"@saved");
1591 config
->use_saved_entry_efivar
= streq16(config
->entry_default_efivar
, L
"@saved");
1592 if (config
->use_saved_entry
|| config
->use_saved_entry_efivar
)
1593 (void) efivar_get(LOADER_GUID
, L
"LoaderEntryLastBooted", &config
->entry_saved
);
1596 static void config_load_entries(
1600 const char16_t
*loaded_image_path
) {
1602 _cleanup_(file_closep
) EFI_FILE
*entries_dir
= NULL
;
1603 _cleanup_free_ EFI_FILE_INFO
*f
= NULL
;
1611 /* Adds Boot Loader Type #1 entries (i.e. /loader/entries/….conf) */
1613 err
= open_directory(root_dir
, L
"\\loader\\entries", &entries_dir
);
1614 if (err
!= EFI_SUCCESS
)
1618 _cleanup_free_
char *content
= NULL
;
1620 err
= readdir_harder(entries_dir
, &f
, &f_size
);
1621 if (err
!= EFI_SUCCESS
|| !f
)
1624 if (f
->FileName
[0] == '.')
1626 if (FLAGS_SET(f
->Attribute
, EFI_FILE_DIRECTORY
))
1629 if (!endswith_no_case(f
->FileName
, L
".conf"))
1631 if (startswith(f
->FileName
, L
"auto-"))
1634 err
= file_read(entries_dir
, f
->FileName
, 0, 0, &content
, NULL
);
1635 if (err
== EFI_SUCCESS
)
1636 config_entry_add_type1(config
, device
, root_dir
, L
"\\loader\\entries", f
->FileName
, content
, loaded_image_path
);
1640 static int config_entry_compare(const ConfigEntry
*a
, const ConfigEntry
*b
) {
1646 /* Order entries that have no tries left to the end of the list */
1647 r
= CMP(a
->tries_left
== 0, b
->tries_left
== 0);
1651 /* If there's a sort key defined for *both* entries, then we do new-style ordering, i.e. by
1652 * sort-key/machine-id/version, with a final fallback to id. If there's no sort key for either, we do
1653 * old-style ordering, i.e. by id only. If one has sort key and the other does not, we put new-style
1654 * before old-style. */
1655 r
= CMP(!a
->sort_key
, !b
->sort_key
);
1656 if (r
!= 0) /* one is old-style, one new-style */
1659 if (a
->sort_key
&& b
->sort_key
) {
1660 r
= strcmp16(a
->sort_key
, b
->sort_key
);
1664 /* If multiple installations of the same OS are around, group by machine ID */
1665 r
= strcmp16(a
->machine_id
, b
->machine_id
);
1669 /* If the sort key was defined, then order by version now (downwards, putting the newest first) */
1670 r
= -strverscmp_improved(a
->version
, b
->version
);
1675 /* Now order by ID. The version is likely part of the ID, thus note that this will generatelly put
1676 * the newer versions earlier. Specifying a sort key explicitly is preferable, because it gives an
1677 * explicit sort order. */
1678 r
= -strverscmp_improved(a
->id
, b
->id
);
1682 if (a
->tries_left
< 0 || b
->tries_left
< 0)
1685 /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
1686 r
= -CMP(a
->tries_left
, b
->tries_left
);
1690 /* If they have the same number of tries left, then let the one win which was tried fewer times so far */
1691 return CMP(a
->tries_done
, b
->tries_done
);
1694 static UINTN
config_entry_find(Config
*config
, const char16_t
*pattern
) {
1697 /* We expect pattern and entry IDs to be already case folded. */
1702 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
1703 if (efi_fnmatch(pattern
, config
->entries
[i
]->id
))
1709 static void config_default_entry_select(Config
*config
) {
1714 i
= config_entry_find(config
, config
->entry_oneshot
);
1715 if (i
!= IDX_INVALID
) {
1716 config
->idx_default
= i
;
1720 i
= config_entry_find(config
, config
->use_saved_entry_efivar
? config
->entry_saved
: config
->entry_default_efivar
);
1721 if (i
!= IDX_INVALID
) {
1722 config
->idx_default
= i
;
1723 config
->idx_default_efivar
= i
;
1727 if (config
->use_saved_entry
)
1728 /* No need to do the same thing twice. */
1729 i
= config
->use_saved_entry_efivar
? IDX_INVALID
: config_entry_find(config
, config
->entry_saved
);
1731 i
= config_entry_find(config
, config
->entry_default_config
);
1732 if (i
!= IDX_INVALID
) {
1733 config
->idx_default
= i
;
1737 /* select the first suitable entry */
1738 for (i
= 0; i
< config
->entry_count
; i
++) {
1739 if (config
->entries
[i
]->type
== LOADER_AUTO
|| config
->entries
[i
]->call
)
1741 config
->idx_default
= i
;
1745 /* If no configured entry to select from was found, enable the menu. */
1746 config
->idx_default
= 0;
1747 if (config
->timeout_sec
== 0)
1748 config
->timeout_sec
= 10;
1751 static bool entries_unique(ConfigEntry
**entries
, bool *unique
, UINTN entry_count
) {
1752 bool is_unique
= true;
1757 for (UINTN i
= 0; i
< entry_count
; i
++)
1758 for (UINTN k
= i
+ 1; k
< entry_count
; k
++) {
1759 if (!streq16(entries
[i
]->title_show
, entries
[k
]->title_show
))
1762 is_unique
= unique
[i
] = unique
[k
] = false;
1768 /* generate a unique title, avoiding non-distinguishable menu entries */
1769 static void config_title_generate(Config
*config
) {
1772 bool unique
[config
->entry_count
];
1775 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1776 assert(!config
->entries
[i
]->title_show
);
1778 config
->entries
[i
]->title_show
= xstrdup16(config
->entries
[i
]->title
?: config
->entries
[i
]->id
);
1781 if (entries_unique(config
->entries
, unique
, config
->entry_count
))
1784 /* add version to non-unique titles */
1785 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1791 if (!config
->entries
[i
]->version
)
1794 _cleanup_free_ char16_t
*t
= config
->entries
[i
]->title_show
;
1795 config
->entries
[i
]->title_show
= xpool_print(L
"%s (%s)", t
, config
->entries
[i
]->version
);
1798 if (entries_unique(config
->entries
, unique
, config
->entry_count
))
1801 /* add machine-id to non-unique titles */
1802 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1808 if (!config
->entries
[i
]->machine_id
)
1811 _cleanup_free_ char16_t
*t
= config
->entries
[i
]->title_show
;
1812 config
->entries
[i
]->title_show
= xpool_print(
1815 strnlen16(config
->entries
[i
]->machine_id
, 8),
1816 config
->entries
[i
]->machine_id
);
1819 if (entries_unique(config
->entries
, unique
, config
->entry_count
))
1822 /* add file name to non-unique titles */
1823 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
1827 _cleanup_free_ char16_t
*t
= config
->entries
[i
]->title_show
;
1828 config
->entries
[i
]->title_show
= xpool_print(L
"%s (%s)", t
, config
->entries
[i
]->id
);
1832 static bool is_sd_boot(EFI_FILE
*root_dir
, const char16_t
*loader_path
) {
1834 static const char * const sections
[] = {
1838 UINTN offset
= 0, size
= 0, read
;
1839 _cleanup_free_
char *content
= NULL
;
1842 assert(loader_path
);
1844 err
= pe_file_locate_sections(root_dir
, loader_path
, sections
, &offset
, &size
);
1845 if (err
!= EFI_SUCCESS
|| size
!= sizeof(magic
))
1848 err
= file_read(root_dir
, loader_path
, offset
, size
, &content
, &read
);
1849 if (err
!= EFI_SUCCESS
|| size
!= read
)
1852 return memcmp(content
, magic
, sizeof(magic
)) == 0;
1855 static ConfigEntry
*config_entry_add_loader_auto(
1859 const char16_t
*loaded_image_path
,
1862 const char16_t
*title
,
1863 const char16_t
*loader
) {
1870 assert(loader
|| loaded_image_path
);
1872 if (!config
->auto_entries
)
1875 if (loaded_image_path
) {
1876 loader
= L
"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME
".efi";
1878 /* We are trying to add the default EFI loader here,
1879 * but we do not want to do that if that would be us.
1881 * If the default loader is not us, it might be shim. It would
1882 * chainload GRUBX64.EFI in that case, which might be us.*/
1883 if (strcaseeq16(loader
, loaded_image_path
) ||
1884 is_sd_boot(root_dir
, loader
) ||
1885 is_sd_boot(root_dir
, L
"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L
".EFI"))
1889 /* check existence */
1890 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1891 EFI_STATUS err
= root_dir
->Open(root_dir
, &handle
, (char16_t
*) loader
, EFI_FILE_MODE_READ
, 0ULL);
1892 if (err
!= EFI_SUCCESS
)
1895 ConfigEntry
*entry
= xnew(ConfigEntry
, 1);
1896 *entry
= (ConfigEntry
) {
1897 .id
= xstrdup16(id
),
1898 .type
= LOADER_AUTO
,
1899 .title
= xstrdup16(title
),
1901 .loader
= xstrdup16(loader
),
1907 config_add_entry(config
, entry
);
1911 static void config_entry_add_osx(Config
*config
) {
1913 UINTN n_handles
= 0;
1914 _cleanup_free_ EFI_HANDLE
*handles
= NULL
;
1918 if (!config
->auto_entries
)
1921 err
= BS
->LocateHandleBuffer(ByProtocol
, &FileSystemProtocol
, NULL
, &n_handles
, &handles
);
1922 if (err
!= EFI_SUCCESS
)
1925 for (UINTN i
= 0; i
< n_handles
; i
++) {
1926 _cleanup_(file_closep
) EFI_FILE
*root
= NULL
;
1928 if (open_volume(handles
[i
], &root
) != EFI_SUCCESS
)
1931 if (config_entry_add_loader_auto(
1939 L
"\\System\\Library\\CoreServices\\boot.efi"))
1944 static EFI_STATUS
boot_windows_bitlocker(void) {
1945 _cleanup_free_ EFI_HANDLE
*handles
= NULL
;
1949 // FIXME: Experimental for now. Should be generalized, and become a per-entry option that can be
1950 // enabled independently of BitLocker, and without a BootXXXX entry pre-existing.
1952 /* BitLocker key cannot be sealed without a TPM present. */
1954 return EFI_NOT_FOUND
;
1956 err
= BS
->LocateHandleBuffer(ByProtocol
, &BlockIoProtocol
, NULL
, &n_handles
, &handles
);
1957 if (err
!= EFI_SUCCESS
)
1960 /* Look for BitLocker magic string on all block drives. */
1962 for (UINTN i
= 0; i
< n_handles
; i
++) {
1963 EFI_BLOCK_IO_PROTOCOL
*block_io
;
1964 err
= BS
->HandleProtocol(handles
[i
], &BlockIoProtocol
, (void **) &block_io
);
1965 if (err
!= EFI_SUCCESS
|| block_io
->Media
->BlockSize
< 512 || block_io
->Media
->BlockSize
> 4096)
1969 err
= block_io
->ReadBlocks(block_io
, block_io
->Media
->MediaId
, 0, sizeof(buf
), buf
);
1970 if (err
!= EFI_SUCCESS
)
1973 if (memcmp(buf
+ 3, "-FVE-FS-", STRLEN("-FVE-FS-")) == 0) {
1979 /* If no BitLocker drive was found, we can just chainload bootmgfw.efi directly. */
1981 return EFI_NOT_FOUND
;
1983 _cleanup_free_
uint16_t *boot_order
= NULL
;
1984 UINTN boot_order_size
;
1986 /* There can be gaps in Boot#### entries. Instead of iterating over the full
1987 * EFI var list or uint16_t namespace, just look for "Windows Boot Manager" in BootOrder. */
1988 err
= efivar_get_raw(EFI_GLOBAL_GUID
, L
"BootOrder", (char **) &boot_order
, &boot_order_size
);
1989 if (err
!= EFI_SUCCESS
|| boot_order_size
% sizeof(uint16_t) != 0)
1992 for (UINTN i
= 0; i
< boot_order_size
/ sizeof(uint16_t); i
++) {
1993 _cleanup_free_
char *buf
= NULL
;
1994 char16_t name
[sizeof(L
"Boot0000")];
1997 SPrint(name
, sizeof(name
), L
"Boot%04x", (uint32_t) boot_order
[i
]);
1998 err
= efivar_get_raw(EFI_GLOBAL_GUID
, name
, &buf
, &buf_size
);
1999 if (err
!= EFI_SUCCESS
)
2002 /* Boot#### are EFI_LOAD_OPTION. But we really are only interested
2003 * for the description, which is at this offset. */
2004 UINTN offset
= sizeof(uint32_t) + sizeof(uint16_t);
2005 if (buf_size
< offset
+ sizeof(char16_t
))
2008 if (streq16((char16_t
*) (buf
+ offset
), L
"Windows Boot Manager")) {
2009 err
= efivar_set_raw(
2013 sizeof(boot_order
[i
]),
2014 EFI_VARIABLE_NON_VOLATILE
);
2015 if (err
!= EFI_SUCCESS
)
2017 RT
->ResetSystem(EfiResetWarm
, EFI_SUCCESS
, 0, NULL
);
2018 assert_not_reached();
2022 return EFI_NOT_FOUND
;
2025 static void config_entry_add_windows(Config
*config
, EFI_HANDLE
*device
, EFI_FILE
*root_dir
) {
2026 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
2027 _cleanup_free_
char *bcd
= NULL
;
2028 char16_t
*title
= NULL
;
2036 if (!config
->auto_entries
)
2039 /* Try to find a better title. */
2040 err
= file_read(root_dir
, L
"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd
, &len
);
2041 if (err
== EFI_SUCCESS
)
2042 title
= get_bcd_title((uint8_t *) bcd
, len
);
2044 ConfigEntry
*e
= config_entry_add_loader_auto(config
, device
, root_dir
, NULL
,
2045 L
"auto-windows", 'w', title
?: L
"Windows Boot Manager",
2046 L
"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2048 if (config
->reboot_for_bitlocker
)
2049 e
->call
= boot_windows_bitlocker
;
2053 static void config_entry_add_unified(
2056 EFI_FILE
*root_dir
) {
2058 _cleanup_(file_closep
) EFI_FILE
*linux_dir
= NULL
;
2059 _cleanup_free_ EFI_FILE_INFO
*f
= NULL
;
2063 /* Adds Boot Loader Type #2 entries (i.e. /EFI/Linux/….efi) */
2069 err
= open_directory(root_dir
, L
"\\EFI\\Linux", &linux_dir
);
2070 if (err
!= EFI_SUCCESS
)
2080 static const char * const sections
[_SECTION_MAX
+ 1] = {
2081 [SECTION_CMDLINE
] = ".cmdline",
2082 [SECTION_OSREL
] = ".osrel",
2086 _cleanup_free_ char16_t
*os_pretty_name
= NULL
, *os_image_id
= NULL
, *os_name
= NULL
, *os_id
= NULL
,
2087 *os_image_version
= NULL
, *os_version
= NULL
, *os_version_id
= NULL
, *os_build_id
= NULL
;
2088 const char16_t
*good_name
, *good_version
, *good_sort_key
;
2089 _cleanup_free_
char *content
= NULL
;
2090 UINTN offs
[_SECTION_MAX
] = {};
2091 UINTN szs
[_SECTION_MAX
] = {};
2096 err
= readdir_harder(linux_dir
, &f
, &f_size
);
2097 if (err
!= EFI_SUCCESS
|| !f
)
2100 if (f
->FileName
[0] == '.')
2102 if (FLAGS_SET(f
->Attribute
, EFI_FILE_DIRECTORY
))
2104 if (!endswith_no_case(f
->FileName
, L
".efi"))
2106 if (startswith(f
->FileName
, L
"auto-"))
2109 /* look for .osrel and .cmdline sections in the .efi binary */
2110 err
= pe_file_locate_sections(linux_dir
, f
->FileName
, sections
, offs
, szs
);
2111 if (err
!= EFI_SUCCESS
|| szs
[SECTION_OSREL
] == 0)
2114 err
= file_read(linux_dir
, f
->FileName
, offs
[SECTION_OSREL
], szs
[SECTION_OSREL
], &content
, NULL
);
2115 if (err
!= EFI_SUCCESS
)
2118 /* read properties from the embedded os-release file */
2119 while ((line
= line_get_key_value(content
, "=", &pos
, &key
, &value
))) {
2120 if (streq8(key
, "PRETTY_NAME")) {
2121 free(os_pretty_name
);
2122 os_pretty_name
= xstra_to_str(value
);
2126 if (streq8(key
, "IMAGE_ID")) {
2128 os_image_id
= xstra_to_str(value
);
2132 if (streq8(key
, "NAME")) {
2134 os_name
= xstra_to_str(value
);
2138 if (streq8(key
, "ID")) {
2140 os_id
= xstra_to_str(value
);
2144 if (streq8(key
, "IMAGE_VERSION")) {
2145 free(os_image_version
);
2146 os_image_version
= xstra_to_str(value
);
2150 if (streq8(key
, "VERSION")) {
2152 os_version
= xstra_to_str(value
);
2156 if (streq8(key
, "VERSION_ID")) {
2157 free(os_version_id
);
2158 os_version_id
= xstra_to_str(value
);
2162 if (streq8(key
, "BUILD_ID")) {
2164 os_build_id
= xstra_to_str(value
);
2169 if (!bootspec_pick_name_version_sort_key(
2183 ConfigEntry
*entry
= xnew(ConfigEntry
, 1);
2184 *entry
= (ConfigEntry
) {
2185 .id
= xstrdup16(f
->FileName
),
2186 .type
= LOADER_UNIFIED_LINUX
,
2187 .title
= xstrdup16(good_name
),
2188 .version
= xstrdup16(good_version
),
2190 .loader
= xpool_print(L
"\\EFI\\Linux\\%s", f
->FileName
),
2191 .sort_key
= xstrdup16(good_sort_key
),
2197 strtolower16(entry
->id
);
2198 config_add_entry(config
, entry
);
2199 config_entry_parse_tries(entry
, L
"\\EFI\\Linux", f
->FileName
, L
".efi");
2201 if (szs
[SECTION_CMDLINE
] == 0)
2204 content
= mfree(content
);
2206 /* read the embedded cmdline file */
2207 err
= file_read(linux_dir
, f
->FileName
, offs
[SECTION_CMDLINE
], szs
[SECTION_CMDLINE
], &content
, NULL
);
2208 if (err
== EFI_SUCCESS
) {
2209 /* chomp the newline */
2210 if (content
[szs
[SECTION_CMDLINE
] - 1] == '\n')
2211 content
[szs
[SECTION_CMDLINE
] - 1] = '\0';
2213 entry
->options
= xstra_to_str(content
);
2218 static void config_load_xbootldr(
2220 EFI_HANDLE
*device
) {
2222 _cleanup_(file_closep
) EFI_FILE
*root_dir
= NULL
;
2223 EFI_HANDLE new_device
;
2229 err
= xbootldr_open(device
, &new_device
, &root_dir
);
2230 if (err
!= EFI_SUCCESS
)
2233 config_entry_add_unified(config
, new_device
, root_dir
);
2234 config_load_entries(config
, new_device
, root_dir
, NULL
);
2237 static EFI_STATUS
initrd_prepare(
2239 const ConfigEntry
*entry
,
2240 char16_t
**ret_options
,
2242 UINTN
*ret_initrd_size
) {
2246 assert(ret_options
);
2248 assert(ret_initrd_size
);
2250 if (entry
->type
!= LOADER_LINUX
|| !entry
->initrd
) {
2253 ret_initrd_size
= 0;
2257 /* Note that order of initrds matters. The kernel will only look for microcode updates in the very
2258 * first one it sees. */
2260 /* Add initrd= to options for older kernels that do not support LINUX_INITRD_MEDIA. Should be dropped
2261 * if linux_x86.c is dropped. */
2262 _cleanup_free_ char16_t
*options
= NULL
;
2266 _cleanup_free_
uint8_t *initrd
= NULL
;
2268 STRV_FOREACH(i
, entry
->initrd
) {
2269 _cleanup_free_ char16_t
*o
= options
;
2271 options
= xpool_print(L
"%s initrd=%s", o
, *i
);
2273 options
= xpool_print(L
"initrd=%s", *i
);
2275 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
2276 err
= root
->Open(root
, &handle
, *i
, EFI_FILE_MODE_READ
, 0);
2277 if (err
!= EFI_SUCCESS
)
2280 _cleanup_free_ EFI_FILE_INFO
*info
= NULL
;
2281 err
= get_file_info_harder(handle
, &info
, NULL
);
2282 if (err
!= EFI_SUCCESS
)
2285 UINTN new_size
, read_size
= info
->FileSize
;
2286 if (__builtin_add_overflow(size
, read_size
, &new_size
))
2287 return EFI_OUT_OF_RESOURCES
;
2288 initrd
= xrealloc(initrd
, size
, new_size
);
2290 err
= handle
->Read(handle
, &read_size
, initrd
+ size
);
2291 if (err
!= EFI_SUCCESS
)
2294 /* Make sure the actual read size is what we expected. */
2295 assert(size
+ read_size
== new_size
);
2299 if (entry
->options
) {
2300 _cleanup_free_ char16_t
*o
= options
;
2301 options
= xpool_print(L
"%s %s", o
, entry
->options
);
2304 *ret_options
= TAKE_PTR(options
);
2305 *ret_initrd
= TAKE_PTR(initrd
);
2306 *ret_initrd_size
= size
;
2310 static EFI_STATUS
image_start(
2311 EFI_HANDLE parent_image
,
2312 const ConfigEntry
*entry
) {
2314 _cleanup_(devicetree_cleanup
) struct devicetree_state dtstate
= {};
2315 _cleanup_(unload_imagep
) EFI_HANDLE image
= NULL
;
2316 _cleanup_free_ EFI_DEVICE_PATH
*path
= NULL
;
2321 /* If this loader entry has a special way to boot, try that first. */
2323 (void) entry
->call();
2325 _cleanup_(file_closep
) EFI_FILE
*image_root
= NULL
;
2326 err
= open_volume(entry
->device
, &image_root
);
2327 if (err
!= EFI_SUCCESS
)
2328 return log_error_status_stall(err
, L
"Error opening root path: %r", err
);
2330 err
= make_file_device_path(entry
->device
, entry
->loader
, &path
);
2331 if (err
!= EFI_SUCCESS
)
2332 return log_error_status_stall(err
, L
"Error making file device path: %r", err
);
2334 UINTN initrd_size
= 0;
2335 _cleanup_free_
void *initrd
= NULL
;
2336 _cleanup_free_ char16_t
*options_initrd
= NULL
;
2337 err
= initrd_prepare(image_root
, entry
, &options_initrd
, &initrd
, &initrd_size
);
2338 if (err
!= EFI_SUCCESS
)
2339 return log_error_status_stall(err
, L
"Error preparing initrd: %r", err
);
2341 err
= BS
->LoadImage(false, parent_image
, path
, NULL
, 0, &image
);
2342 if (err
!= EFI_SUCCESS
)
2343 return log_error_status_stall(err
, L
"Error loading %s: %r", entry
->loader
, err
);
2345 if (entry
->devicetree
) {
2346 err
= devicetree_install(&dtstate
, image_root
, entry
->devicetree
);
2347 if (err
!= EFI_SUCCESS
)
2348 return log_error_status_stall(err
, L
"Error loading %s: %r", entry
->devicetree
, err
);
2351 _cleanup_(cleanup_initrd
) EFI_HANDLE initrd_handle
= NULL
;
2352 err
= initrd_register(initrd
, initrd_size
, &initrd_handle
);
2353 if (err
!= EFI_SUCCESS
)
2354 return log_error_status_stall(err
, L
"Error registering initrd: %r", err
);
2356 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
;
2357 err
= BS
->HandleProtocol(image
, &LoadedImageProtocol
, (void **) &loaded_image
);
2358 if (err
!= EFI_SUCCESS
)
2359 return log_error_status_stall(err
, L
"Error getting LoadedImageProtocol handle: %r", err
);
2361 char16_t
*options
= options_initrd
?: entry
->options
;
2363 loaded_image
->LoadOptions
= options
;
2364 loaded_image
->LoadOptionsSize
= strsize16(options
);
2366 /* Try to log any options to the TPM, especially to catch manually edited options */
2367 (void) tpm_log_load_options(options
, NULL
);
2370 efivar_set_time_usec(LOADER_GUID
, L
"LoaderTimeExecUSec", 0);
2371 err
= BS
->StartImage(image
, NULL
, NULL
);
2372 graphics_mode(false);
2373 if (err
== EFI_SUCCESS
)
2376 /* Try calling the kernel compat entry point if one exists. */
2377 if (err
== EFI_UNSUPPORTED
&& entry
->type
== LOADER_LINUX
) {
2378 uint32_t kernel_entry_address
;
2380 err
= pe_alignment_info(loaded_image
->ImageBase
, &kernel_entry_address
, NULL
, NULL
);
2381 if (err
!= EFI_SUCCESS
) {
2382 if (err
!= EFI_UNSUPPORTED
)
2383 return log_error_status_stall(err
, L
"Error finding kernel compat entry address: %r", err
);
2385 EFI_IMAGE_ENTRY_POINT kernel_entry
=
2386 (EFI_IMAGE_ENTRY_POINT
) ((uint8_t *) loaded_image
->ImageBase
+ kernel_entry_address
);
2388 err
= kernel_entry(image
, ST
);
2389 graphics_mode(false);
2390 if (err
== EFI_SUCCESS
)
2395 return log_error_status_stall(err
, L
"Failed to execute %s (%s): %r", entry
->title_show
, entry
->loader
, err
);
2398 static void config_free(Config
*config
) {
2400 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
2401 config_entry_free(config
->entries
[i
]);
2402 free(config
->entries
);
2403 free(config
->entry_default_config
);
2404 free(config
->entry_oneshot
);
2407 static void config_write_entries_to_variable(Config
*config
) {
2408 _cleanup_free_
char *buffer
= NULL
;
2414 for (UINTN i
= 0; i
< config
->entry_count
; i
++)
2415 sz
+= strsize16(config
->entries
[i
]->id
);
2417 p
= buffer
= xmalloc(sz
);
2419 for (UINTN i
= 0; i
< config
->entry_count
; i
++) {
2422 l
= strsize16(config
->entries
[i
]->id
);
2423 memcpy(p
, config
->entries
[i
]->id
, l
);
2428 assert(p
== buffer
+ sz
);
2430 /* Store the full list of discovered entries. */
2431 (void) efivar_set_raw(LOADER_GUID
, L
"LoaderEntries", buffer
, sz
, 0);
2434 static void save_selected_entry(const Config
*config
, const ConfigEntry
*entry
) {
2437 assert(entry
->loader
|| !entry
->call
);
2439 /* Always export the selected boot entry to the system in a volatile var. */
2440 (void) efivar_set(LOADER_GUID
, L
"LoaderEntrySelected", entry
->id
, 0);
2442 /* Do not save or delete if this was a oneshot boot. */
2443 if (streq16(config
->entry_oneshot
, entry
->id
))
2446 if (config
->use_saved_entry_efivar
|| (!config
->entry_default_efivar
&& config
->use_saved_entry
)) {
2447 /* Avoid unnecessary NVRAM writes. */
2448 if (streq16(config
->entry_saved
, entry
->id
))
2451 (void) efivar_set(LOADER_GUID
, L
"LoaderEntryLastBooted", entry
->id
, EFI_VARIABLE_NON_VOLATILE
);
2453 /* Delete the non-volatile var if not needed. */
2454 (void) efivar_set(LOADER_GUID
, L
"LoaderEntryLastBooted", NULL
, EFI_VARIABLE_NON_VOLATILE
);
2457 static EFI_STATUS
secure_boot_discover_keys(Config
*config
, EFI_FILE
*root_dir
) {
2459 _cleanup_(file_closep
) EFI_FILE
*keys_basedir
= NULL
;
2461 if (secure_boot_mode() != SECURE_BOOT_SETUP
)
2464 /* the lack of a 'keys' directory is not fatal and is silently ignored */
2465 err
= open_directory(root_dir
, u
"\\loader\\keys", &keys_basedir
);
2466 if (err
== EFI_NOT_FOUND
)
2468 if (err
!= EFI_SUCCESS
)
2472 _cleanup_free_ EFI_FILE_INFO
*dirent
= NULL
;
2473 size_t dirent_size
= 0;
2474 ConfigEntry
*entry
= NULL
;
2476 err
= readdir_harder(keys_basedir
, &dirent
, &dirent_size
);
2477 if (err
!= EFI_SUCCESS
|| !dirent
)
2480 if (dirent
->FileName
[0] == '.')
2483 if (!FLAGS_SET(dirent
->Attribute
, EFI_FILE_DIRECTORY
))
2486 entry
= xnew(ConfigEntry
, 1);
2487 *entry
= (ConfigEntry
) {
2488 .id
= xpool_print(L
"secure-boot-keys-%s", dirent
->FileName
),
2489 .title
= xpool_print(L
"Enroll Secure Boot keys: %s", dirent
->FileName
),
2490 .path
= xpool_print(L
"\\loader\\keys\\%s", dirent
->FileName
),
2491 .type
= LOADER_SECURE_BOOT_KEYS
,
2495 config_add_entry(config
, entry
);
2497 if (config
->secure_boot_enroll
== ENROLL_FORCE
&& strcaseeq16(dirent
->FileName
, u
"auto"))
2498 /* if we auto enroll successfully this call does not return, if it fails we still
2499 * want to add other potential entries to the menu */
2500 secure_boot_enroll_at(root_dir
, entry
->path
);
2506 static void export_variables(
2507 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
,
2508 const char16_t
*loaded_image_path
,
2509 uint64_t init_usec
) {
2511 static const uint64_t loader_features
=
2512 EFI_LOADER_FEATURE_CONFIG_TIMEOUT
|
2513 EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT
|
2514 EFI_LOADER_FEATURE_ENTRY_DEFAULT
|
2515 EFI_LOADER_FEATURE_ENTRY_ONESHOT
|
2516 EFI_LOADER_FEATURE_BOOT_COUNTING
|
2517 EFI_LOADER_FEATURE_XBOOTLDR
|
2518 EFI_LOADER_FEATURE_RANDOM_SEED
|
2519 EFI_LOADER_FEATURE_LOAD_DRIVER
|
2520 EFI_LOADER_FEATURE_SORT_KEY
|
2521 EFI_LOADER_FEATURE_SAVED_ENTRY
|
2522 EFI_LOADER_FEATURE_DEVICETREE
|
2525 _cleanup_free_ char16_t
*infostr
= NULL
, *typestr
= NULL
;
2528 assert(loaded_image
);
2529 assert(loaded_image_path
);
2531 efivar_set_time_usec(LOADER_GUID
, L
"LoaderTimeInitUSec", init_usec
);
2532 efivar_set(LOADER_GUID
, L
"LoaderInfo", L
"systemd-boot " GIT_VERSION
, 0);
2534 infostr
= xpool_print(L
"%s %u.%02u", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
2535 efivar_set(LOADER_GUID
, L
"LoaderFirmwareInfo", infostr
, 0);
2537 typestr
= xpool_print(L
"UEFI %u.%02u", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
2538 efivar_set(LOADER_GUID
, L
"LoaderFirmwareType", typestr
, 0);
2540 (void) efivar_set_uint64_le(LOADER_GUID
, L
"LoaderFeatures", loader_features
, 0);
2542 /* the filesystem path to this image, to prevent adding ourselves to the menu */
2543 efivar_set(LOADER_GUID
, L
"LoaderImageIdentifier", loaded_image_path
, 0);
2545 /* export the device path this image is started from */
2546 if (disk_get_part_uuid(loaded_image
->DeviceHandle
, uuid
) == EFI_SUCCESS
)
2547 efivar_set(LOADER_GUID
, L
"LoaderDevicePartUUID", uuid
, 0);
2550 static void config_load_all_entries(
2552 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
,
2553 const char16_t
*loaded_image_path
,
2554 EFI_FILE
*root_dir
) {
2557 assert(loaded_image
);
2558 assert(loaded_image_path
);
2561 config_load_defaults(config
, root_dir
);
2563 /* scan /EFI/Linux/ directory */
2564 config_entry_add_unified(config
, loaded_image
->DeviceHandle
, root_dir
);
2566 /* scan /loader/entries/\*.conf files */
2567 config_load_entries(config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
);
2569 /* Similar, but on any XBOOTLDR partition */
2570 config_load_xbootldr(config
, loaded_image
->DeviceHandle
);
2572 /* sort entries after version number */
2573 sort_pointer_array((void **) config
->entries
, config
->entry_count
, (compare_pointer_func_t
) config_entry_compare
);
2575 /* if we find some well-known loaders, add them to the end of the list */
2576 config_entry_add_osx(config
);
2577 config_entry_add_windows(config
, loaded_image
->DeviceHandle
, root_dir
);
2578 config_entry_add_loader_auto(config
, loaded_image
->DeviceHandle
, root_dir
, NULL
,
2579 L
"auto-efi-shell", 's', L
"EFI Shell", L
"\\shell" EFI_MACHINE_TYPE_NAME
".efi");
2580 config_entry_add_loader_auto(config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
,
2581 L
"auto-efi-default", '\0', L
"EFI Default Loader", NULL
);
2583 if (config
->auto_firmware
&& FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
)) {
2584 ConfigEntry
*entry
= xnew(ConfigEntry
, 1);
2585 *entry
= (ConfigEntry
) {
2586 .id
= xstrdup16(u
"auto-reboot-to-firmware-setup"),
2587 .title
= xstrdup16(u
"Reboot Into Firmware Interface"),
2588 .call
= reboot_into_firmware
,
2592 config_add_entry(config
, entry
);
2595 /* find if secure boot signing keys exist and autoload them if necessary
2596 otherwise creates menu entries so that the user can load them manually
2597 if the secure-boot-enroll variable is set to no (the default), we do not
2598 even search for keys on the ESP */
2599 if (config
->secure_boot_enroll
!= ENROLL_OFF
)
2600 secure_boot_discover_keys(config
, root_dir
);
2602 if (config
->entry_count
== 0)
2605 config_write_entries_to_variable(config
);
2607 config_title_generate(config
);
2609 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
2610 config_default_entry_select(config
);
2613 EFI_STATUS
efi_main(EFI_HANDLE image
, EFI_SYSTEM_TABLE
*sys_table
) {
2614 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
;
2615 _cleanup_(file_closep
) EFI_FILE
*root_dir
= NULL
;
2616 _cleanup_(config_free
) Config config
= {};
2617 char16_t
*loaded_image_path
;
2622 InitializeLib(image
, sys_table
);
2623 init_usec
= time_usec();
2624 debug_hook(L
"systemd-boot");
2625 /* Uncomment the next line if you need to wait for debugger. */
2628 err
= BS
->OpenProtocol(image
,
2629 &LoadedImageProtocol
,
2630 (void **)&loaded_image
,
2633 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
2634 if (err
!= EFI_SUCCESS
)
2635 return log_error_status_stall(err
, L
"Error getting a LoadedImageProtocol handle: %r", err
);
2637 loaded_image_path
= DevicePathToStr(loaded_image
->FilePath
);
2638 if (!loaded_image_path
)
2641 export_variables(loaded_image
, loaded_image_path
, init_usec
);
2643 err
= open_volume(loaded_image
->DeviceHandle
, &root_dir
);
2644 if (err
!= EFI_SUCCESS
)
2645 return log_error_status_stall(err
, L
"Unable to open root directory: %r", err
);
2647 if (secure_boot_enabled() && shim_loaded()) {
2648 err
= security_policy_install();
2649 if (err
!= EFI_SUCCESS
)
2650 return log_error_status_stall(err
, L
"Error installing security policy: %r", err
);
2653 (void) load_drivers(image
, loaded_image
, root_dir
);
2655 config_load_all_entries(&config
, loaded_image
, loaded_image_path
, root_dir
);
2657 if (config
.entry_count
== 0) {
2658 log_error_stall(L
"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2662 /* select entry or show menu when key is pressed or timeout is set */
2663 if (config
.force_menu
|| config
.timeout_sec
> 0)
2668 /* Block up to 100ms to give firmware time to get input working. */
2669 err
= console_key_read(&key
, 100 * 1000);
2670 if (err
== EFI_SUCCESS
) {
2671 /* find matching key in config entries */
2672 UINTN idx
= entry_lookup_key(&config
, config
.idx_default
, KEYCHAR(key
));
2673 if (idx
!= IDX_INVALID
)
2674 config
.idx_default
= idx
;
2683 entry
= config
.entries
[config
.idx_default
];
2685 efivar_set_time_usec(LOADER_GUID
, L
"LoaderTimeMenuUSec", 0);
2686 if (!menu_run(&config
, &entry
, loaded_image_path
))
2690 /* if auto enrollment is activated, we try to load keys for the given entry. */
2691 if (entry
->type
== LOADER_SECURE_BOOT_KEYS
&& config
.secure_boot_enroll
!= ENROLL_OFF
) {
2692 err
= secure_boot_enroll_at(root_dir
, entry
->path
);
2693 if (err
!= EFI_SUCCESS
)
2698 /* Run special entry like "reboot" now. Those that have a loader
2699 * will be handled by image_start() instead. */
2700 if (entry
->call
&& !entry
->loader
) {
2705 config_entry_bump_counters(entry
, root_dir
);
2706 save_selected_entry(&config
, entry
);
2708 /* Optionally, read a random seed off the ESP and pass it to the OS */
2709 (void) process_random_seed(root_dir
, config
.random_seed_mode
);
2711 err
= image_start(image
, entry
);
2712 if (err
!= EFI_SUCCESS
)
2716 config
.timeout_sec
= 0;
2720 BS
->CloseProtocol(image
, &LoadedImageProtocol
, image
, NULL
);