1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include "bootspec-fundamental.h"
6 #include "device-path-util.h"
7 #include "devicetree.h"
9 #include "efivars-fundamental.h"
14 #include "part-discovery.h"
16 #include "proto/block-io.h"
17 #include "proto/device-path.h"
18 #include "proto/simple-text-io.h"
19 #include "random-seed.h"
21 #include "secure-boot.h"
28 /* Magic string for recognizing our own binaries */
29 #define SD_MAGIC "#### LoaderInfo: systemd-boot " GIT_VERSION " ####"
30 DECLARE_NOALLOC_SECTION(".sdmagic", SD_MAGIC
);
32 /* Makes systemd-boot available from \EFI\Linux\ for testing purposes. */
33 DECLARE_NOALLOC_SECTION(
36 "VERSION=\"" GIT_VERSION
"\"\n"
37 "NAME=\"systemd-boot " GIT_VERSION
"\"\n");
39 DECLARE_SBAT(SBAT_BOOT_SECTION_TEXT
);
41 typedef enum LoaderType
{
45 LOADER_LINUX
, /* Boot loader spec type #1 entries */
46 LOADER_UNIFIED_LINUX
, /* Boot loader spec type #2 entries */
47 LOADER_SECURE_BOOT_KEYS
,
52 char16_t
*id
; /* The unique identifier for this entry (typically the filename of the file defining the entry) */
53 char16_t
*title_show
; /* The string to actually display (this is made unique before showing) */
54 char16_t
*title
; /* The raw (human readable) title string of the entry (not necessarily unique) */
55 char16_t
*sort_key
; /* The string to use as primary sort key, usually ID= from os-release, possibly suffixed */
56 char16_t
*version
; /* The raw (human readable) version string of the entry */
63 bool options_implied
; /* If true, these options are implied if we invoke the PE binary without any parameters (as in: UKI). If false we must specify these options explicitly. */
66 EFI_STATUS (*call
)(void);
70 char16_t
*current_name
;
75 ConfigEntry
**entries
;
78 size_t idx_default_efivar
;
79 uint32_t timeout_sec
; /* Actual timeout used (efi_main() override > efivar > config). */
80 uint32_t timeout_sec_config
;
81 uint32_t timeout_sec_efivar
;
82 char16_t
*entry_default_config
;
83 char16_t
*entry_default_efivar
;
84 char16_t
*entry_oneshot
;
85 char16_t
*entry_saved
;
89 bool reboot_for_bitlocker
;
90 secure_boot_enroll secure_boot_enroll
;
93 bool use_saved_entry_efivar
;
96 int64_t console_mode_efivar
;
99 /* These values have been chosen so that the transitions the user sees could
100 * employ unsigned over-/underflow like this:
101 * efivar unset ↔ force menu ↔ no timeout/skip menu ↔ 1 s ↔ 2 s ↔ … */
104 TIMEOUT_MAX
= UINT32_MAX
- 2U,
105 TIMEOUT_UNSET
= UINT32_MAX
- 1U,
106 TIMEOUT_MENU_FORCE
= UINT32_MAX
,
107 TIMEOUT_MENU_HIDDEN
= 0,
108 TIMEOUT_TYPE_MAX
= UINT32_MAX
,
116 static void cursor_left(size_t *cursor
, size_t *first
) {
122 else if ((*first
) > 0)
126 static void cursor_right(size_t *cursor
, size_t *first
, size_t x_max
, size_t len
) {
130 if ((*cursor
)+1 < x_max
)
132 else if ((*first
) + (*cursor
) < len
)
136 static bool line_edit(char16_t
**line_in
, size_t x_max
, size_t y_pos
) {
137 _cleanup_free_ char16_t
*line
= NULL
, *print
= NULL
;
138 size_t size
, len
, first
= 0, cursor
= 0, clear
= 0;
142 len
= strlen16(*line_in
);
144 line
= xnew(char16_t
, size
);
145 print
= xnew(char16_t
, x_max
+ 1);
146 strcpy16(line
, strempty(*line_in
));
151 size_t j
, cursor_color
= EFI_TEXT_ATTR_SWAP(COLOR_EDIT
);
153 j
= MIN(len
- first
, x_max
);
154 memcpy(print
, line
+ first
, j
* sizeof(char16_t
));
155 while (clear
> 0 && j
< x_max
) {
161 /* See comment at edit_line() call site for why we start at 1. */
162 print_at(1, y_pos
, COLOR_EDIT
, print
);
166 print
[cursor
+1] = '\0';
168 print_at(cursor
+ 1, y_pos
, cursor_color
, print
+ cursor
);
169 cursor_color
= EFI_TEXT_ATTR_SWAP(cursor_color
);
171 err
= console_key_read(&key
, 750 * 1000);
172 if (!IN_SET(err
, EFI_SUCCESS
, EFI_TIMEOUT
, EFI_NOT_READY
))
175 print_at(cursor
+ 1, y_pos
, COLOR_EDIT
, print
+ cursor
);
176 } while (err
!= EFI_SUCCESS
);
179 case KEYPRESS(0, SCAN_ESC
, 0):
180 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'c'):
181 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'g'):
182 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('c')):
183 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('g')):
186 case KEYPRESS(0, SCAN_HOME
, 0):
187 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'a'):
188 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('a')):
189 /* beginning-of-line */
194 case KEYPRESS(0, SCAN_END
, 0):
195 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'e'):
196 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('e')):
198 cursor
= len
- first
;
199 if (cursor
+1 >= x_max
) {
201 first
= len
- (x_max
-1);
205 case KEYPRESS(0, SCAN_DOWN
, 0):
206 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'f'):
207 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_RIGHT
, 0):
209 while (line
[first
+ cursor
] == ' ')
210 cursor_right(&cursor
, &first
, x_max
, len
);
211 while (line
[first
+ cursor
] && line
[first
+ cursor
] != ' ')
212 cursor_right(&cursor
, &first
, x_max
, len
);
215 case KEYPRESS(0, SCAN_UP
, 0):
216 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'b'):
217 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_LEFT
, 0):
219 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
220 cursor_left(&cursor
, &first
);
221 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ')
222 cursor_left(&cursor
, &first
);
224 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ')
225 cursor_left(&cursor
, &first
);
228 case KEYPRESS(0, SCAN_RIGHT
, 0):
229 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'f'):
230 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('f')):
232 if (first
+ cursor
== len
)
234 cursor_right(&cursor
, &first
, x_max
, len
);
237 case KEYPRESS(0, SCAN_LEFT
, 0):
238 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'b'):
239 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('b')):
241 cursor_left(&cursor
, &first
);
244 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_DELETE
, 0):
245 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'd'):
250 for (k
= first
+ cursor
; k
< len
&& line
[k
] == ' '; k
++)
252 for (; k
< len
&& line
[k
] != ' '; k
++)
255 for (size_t i
= first
+ cursor
; i
+ clear
< len
; i
++)
256 line
[i
] = line
[i
+ clear
];
261 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'w'):
262 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('w')):
263 case KEYPRESS(EFI_ALT_PRESSED
, 0, '\b'):
264 /* backward-kill-word */
266 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
267 cursor_left(&cursor
, &first
);
269 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ') {
270 cursor_left(&cursor
, &first
);
274 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ') {
275 cursor_left(&cursor
, &first
);
279 for (size_t i
= first
+ cursor
; i
+ clear
< len
; i
++)
280 line
[i
] = line
[i
+ clear
];
285 case KEYPRESS(0, SCAN_DELETE
, 0):
286 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'd'):
287 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('d')):
290 if (first
+ cursor
== len
)
292 for (size_t i
= first
+ cursor
; i
< len
; i
++)
298 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'k'):
299 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('k')):
301 line
[first
+ cursor
] = '\0';
302 clear
= len
- (first
+ cursor
);
303 len
= first
+ cursor
;
306 case KEYPRESS(0, 0, '\n'):
307 case KEYPRESS(0, 0, '\r'):
308 case KEYPRESS(0, SCAN_F3
, 0): /* EZpad Mini 4s firmware sends malformed events */
309 case KEYPRESS(0, SCAN_F3
, '\r'): /* Teclast X98+ II firmware sends malformed events */
310 if (!streq16(line
, *line_in
)) {
312 *line_in
= TAKE_PTR(line
);
316 case KEYPRESS(0, 0, '\b'):
319 if (first
== 0 && cursor
== 0)
321 for (size_t i
= first
+ cursor
-1; i
< len
; i
++)
327 if (cursor
> 0 || first
== 0)
329 /* show full line if it fits */
335 /* jump left to see what we delete */
345 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
346 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
349 for (size_t i
= len
; i
> first
+ cursor
; i
--)
351 line
[first
+ cursor
] = KEYCHAR(key
);
354 if (cursor
+1 < x_max
)
356 else if (first
+ cursor
< len
)
363 static size_t entry_lookup_key(Config
*config
, size_t start
, char16_t key
) {
369 /* select entry by number key */
370 if (key
>= '1' && key
<= '9') {
371 size_t i
= key
- '0';
372 if (i
> config
->n_entries
)
373 i
= config
->n_entries
;
377 /* find matching key in config entries */
378 for (size_t i
= start
; i
< config
->n_entries
; i
++)
379 if (config
->entries
[i
]->key
== key
)
382 for (size_t i
= 0; i
< start
; i
++)
383 if (config
->entries
[i
]->key
== key
)
389 static char16_t
*update_timeout_efivar(uint32_t *t
, bool inc
) {
394 *t
= inc
? TIMEOUT_MAX
: (*t
- 1);
397 *t
= inc
? TIMEOUT_MENU_FORCE
: TIMEOUT_UNSET
;
399 case TIMEOUT_MENU_FORCE
:
400 *t
= inc
? TIMEOUT_MENU_HIDDEN
: TIMEOUT_UNSET
;
402 case TIMEOUT_MENU_HIDDEN
:
403 *t
= inc
? TIMEOUT_MIN
: TIMEOUT_MENU_FORCE
;
411 return xstrdup16(u
"Menu timeout defined by configuration file.");
412 case TIMEOUT_MENU_FORCE
:
413 return xstrdup16(u
"Timeout disabled, menu will always be shown.");
414 case TIMEOUT_MENU_HIDDEN
:
415 return xstrdup16(u
"Menu disabled. Hold down key at bootup to show menu.");
417 return xasprintf("Menu timeout set to %u s.", *t
);
421 static bool unicode_supported(void) {
422 static int cache
= -1;
425 /* Basic unicode box drawing support is mandated by the spec, but it does
426 * not hurt to make sure it works. */
427 cache
= ST
->ConOut
->TestString(ST
->ConOut
, (char16_t
*) u
"─") == EFI_SUCCESS
;
432 static bool ps_continue(void) {
433 const char16_t
*sep
= unicode_supported() ? u
"───" : u
"---";
434 printf("\n%ls Press any key to continue, ESC or q to quit. %ls\n\n", sep
, sep
);
437 return console_key_read(&key
, UINT64_MAX
) == EFI_SUCCESS
&&
438 !IN_SET(key
, KEYPRESS(0, SCAN_ESC
, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q'));
441 static void print_status(Config
*config
, char16_t
*loaded_image_path
) {
443 uint32_t screen_width
= 0, screen_height
= 0;
444 SecureBootMode secure
;
445 _cleanup_free_ char16_t
*device_part_uuid
= NULL
;
449 clear_screen(COLOR_NORMAL
);
450 console_query_mode(&x_max
, &y_max
);
451 query_screen_resolution(&screen_width
, &screen_height
);
453 secure
= secure_boot_mode();
454 (void) efivar_get(MAKE_GUID_PTR(LOADER
), u
"LoaderDevicePartUUID", &device_part_uuid
);
456 printf(" systemd-boot version: " GIT_VERSION
"\n");
457 if (loaded_image_path
)
458 printf(" loaded image: %ls\n", loaded_image_path
);
459 if (device_part_uuid
)
460 printf(" loader partition UUID: %ls\n", device_part_uuid
);
461 printf(" architecture: " EFI_MACHINE_TYPE_NAME
"\n");
462 printf(" UEFI specification: %u.%02u\n", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
463 printf(" firmware vendor: %ls\n", ST
->FirmwareVendor
);
464 printf(" firmware version: %u.%02u\n", ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
465 printf(" OS indications: %#" PRIx64
"\n", get_os_indications_supported());
466 printf(" secure boot: %ls (%ls)\n",
467 yes_no(IN_SET(secure
, SECURE_BOOT_USER
, SECURE_BOOT_DEPLOYED
)),
468 secure_boot_mode_to_string(secure
));
469 printf(" shim: %ls\n", yes_no(shim_loaded()));
470 printf(" TPM: %ls\n", yes_no(tpm_present()));
471 printf(" console mode: %i/%" PRIi64
" (%zux%zu @%ux%u)\n",
472 ST
->ConOut
->Mode
->Mode
, ST
->ConOut
->Mode
->MaxMode
- INT64_C(1),
473 x_max
, y_max
, screen_width
, screen_height
);
478 switch (config
->timeout_sec_config
) {
481 case TIMEOUT_MENU_FORCE
:
482 printf(" timeout (config): menu-force\n");
484 case TIMEOUT_MENU_HIDDEN
:
485 printf(" timeout (config): menu-hidden\n");
488 printf(" timeout (config): %u s\n", config
->timeout_sec_config
);
491 switch (config
->timeout_sec_efivar
) {
494 case TIMEOUT_MENU_FORCE
:
495 printf(" timeout (EFI var): menu-force\n");
497 case TIMEOUT_MENU_HIDDEN
:
498 printf(" timeout (EFI var): menu-hidden\n");
501 printf(" timeout (EFI var): %u s\n", config
->timeout_sec_efivar
);
504 if (config
->entry_default_config
)
505 printf(" default (config): %ls\n", config
->entry_default_config
);
506 if (config
->entry_default_efivar
)
507 printf(" default (EFI var): %ls\n", config
->entry_default_efivar
);
508 if (config
->entry_oneshot
)
509 printf(" default (one-shot): %ls\n", config
->entry_oneshot
);
510 if (config
->entry_saved
)
511 printf(" saved entry: %ls\n", config
->entry_saved
);
512 printf(" editor: %ls\n", yes_no(config
->editor
));
513 printf(" auto-entries: %ls\n", yes_no(config
->auto_entries
));
514 printf(" auto-firmware: %ls\n", yes_no(config
->auto_firmware
));
515 printf(" beep: %ls\n", yes_no(config
->beep
));
516 printf(" reboot-for-bitlocker: %ls\n", yes_no(config
->reboot_for_bitlocker
));
518 switch (config
->secure_boot_enroll
) {
520 printf(" secure-boot-enroll: off\n");
523 printf(" secure-boot-enroll: manual\n");
526 printf(" secure-boot-enroll: if-safe\n");
529 printf(" secure-boot-enroll: force\n");
532 assert_not_reached();
535 switch (config
->console_mode
) {
536 case CONSOLE_MODE_AUTO
:
537 printf(" console-mode (config): auto\n");
539 case CONSOLE_MODE_KEEP
:
540 printf(" console-mode (config): keep\n");
542 case CONSOLE_MODE_FIRMWARE_MAX
:
543 printf(" console-mode (config): max\n");
546 printf(" console-mode (config): %" PRIi64
"\n", config
->console_mode
);
550 /* EFI var console mode is always a concrete value or unset. */
551 if (config
->console_mode_efivar
!= CONSOLE_MODE_KEEP
)
552 printf("console-mode (EFI var): %" PRIi64
"\n", config
->console_mode_efivar
);
557 for (size_t i
= 0; i
< config
->n_entries
; i
++) {
558 ConfigEntry
*entry
= config
->entries
[i
];
559 EFI_DEVICE_PATH
*dp
= NULL
;
560 _cleanup_free_ char16_t
*dp_str
= NULL
;
563 BS
->HandleProtocol(entry
->device
, MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL
), (void **) &dp
) ==
565 (void) device_path_to_str(dp
, &dp_str
);
567 printf(" config entry: %zu/%zu\n", i
+ 1, config
->n_entries
);
568 printf(" id: %ls\n", entry
->id
);
570 printf(" title: %ls\n", entry
->title
);
571 if (entry
->title_show
&& !streq16(entry
->title
, entry
->title_show
))
572 printf(" title show: %ls\n", entry
->title_show
);
574 printf(" sort key: %ls\n", entry
->sort_key
);
576 printf(" version: %ls\n", entry
->version
);
577 if (entry
->machine_id
)
578 printf(" machine-id: %ls\n", entry
->machine_id
);
580 printf(" device: %ls\n", dp_str
);
582 printf(" loader: %ls\n", entry
->loader
);
583 STRV_FOREACH(initrd
, entry
->initrd
)
584 printf(" initrd: %ls\n", *initrd
);
585 if (entry
->devicetree
)
586 printf(" devicetree: %ls\n", entry
->devicetree
);
588 printf(" options: %ls\n", entry
->options
);
589 printf(" internal call: %ls\n", yes_no(!!entry
->call
));
591 printf("counting boots: %ls\n", yes_no(entry
->tries_left
>= 0));
592 if (entry
->tries_left
>= 0) {
593 printf(" tries: %i left, %i done\n", entry
->tries_left
, entry
->tries_done
);
594 printf(" current path: %ls\\%ls\n", entry
->path
, entry
->current_name
);
595 printf(" next path: %ls\\%ls\n", entry
->path
, entry
->next_name
);
603 static EFI_STATUS
reboot_into_firmware(void) {
607 if (!FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
))
608 return log_error_status(EFI_UNSUPPORTED
, "Reboot to firmware interface not supported.");
610 (void) efivar_get_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE
), u
"OsIndications", &osind
);
611 osind
|= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
613 err
= efivar_set_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE
), u
"OsIndications", osind
, EFI_VARIABLE_NON_VOLATILE
);
614 if (err
!= EFI_SUCCESS
)
615 return log_error_status(err
, "Error setting OsIndications: %m");
617 RT
->ResetSystem(EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
618 assert_not_reached();
621 static bool menu_run(
623 ConfigEntry
**chosen_entry
,
624 char16_t
*loaded_image_path
) {
627 assert(chosen_entry
);
630 size_t visible_max
= 0;
631 size_t idx_highlight
= config
->idx_default
, idx_highlight_prev
= 0;
632 size_t idx
, idx_first
= 0, idx_last
= 0;
633 bool new_mode
= true, clear
= true;
634 bool refresh
= true, highlight
= false;
635 size_t x_start
= 0, y_start
= 0, y_status
= 0, x_max
, y_max
;
636 _cleanup_(strv_freep
) char16_t
**lines
= NULL
;
637 _cleanup_free_ char16_t
*clearline
= NULL
, *separator
= NULL
, *status
= NULL
;
638 uint32_t timeout_efivar_saved
= config
->timeout_sec_efivar
;
639 uint32_t timeout_remain
= config
->timeout_sec
== TIMEOUT_MENU_FORCE
? 0 : config
->timeout_sec
;
640 bool exit
= false, run
= true, firmware_setup
= false;
641 int64_t console_mode_initial
= ST
->ConOut
->Mode
->Mode
, console_mode_efivar_saved
= config
->console_mode_efivar
;
642 size_t default_efivar_saved
= config
->idx_default_efivar
;
644 graphics_mode(false);
645 ST
->ConIn
->Reset(ST
->ConIn
, false);
646 ST
->ConOut
->EnableCursor(ST
->ConOut
, false);
648 /* draw a single character to make ClearScreen work on some firmware */
649 ST
->ConOut
->OutputString(ST
->ConOut
, (char16_t
*) u
" ");
651 err
= console_set_mode(config
->console_mode_efivar
!= CONSOLE_MODE_KEEP
?
652 config
->console_mode_efivar
: config
->console_mode
);
653 if (err
!= EFI_SUCCESS
) {
654 clear_screen(COLOR_NORMAL
);
655 log_error_status(err
, "Error switching console mode: %m");
658 size_t line_width
= 0, entry_padding
= 3;
663 console_query_mode(&x_max
, &y_max
);
665 /* account for padding+status */
666 visible_max
= y_max
- 2;
668 /* Drawing entries starts at idx_first until idx_last. We want to make
669 * sure that idx_highlight is centered, but not if we are close to the
670 * beginning/end of the entry list. Otherwise we would have a half-empty
672 if (config
->n_entries
<= visible_max
|| idx_highlight
<= visible_max
/ 2)
674 else if (idx_highlight
>= config
->n_entries
- (visible_max
/ 2))
675 idx_first
= config
->n_entries
- visible_max
;
677 idx_first
= idx_highlight
- (visible_max
/ 2);
678 idx_last
= idx_first
+ visible_max
- 1;
680 /* length of the longest entry */
682 for (size_t i
= 0; i
< config
->n_entries
; i
++)
683 line_width
= MAX(line_width
, strlen16(config
->entries
[i
]->title_show
));
684 line_width
= MIN(line_width
+ 2 * entry_padding
, x_max
);
686 /* offsets to center the entries on the screen */
687 x_start
= (x_max
- (line_width
)) / 2;
688 if (config
->n_entries
< visible_max
)
689 y_start
= ((visible_max
- config
->n_entries
) / 2) + 1;
693 /* Put status line after the entry list, but give it some breathing room. */
694 y_status
= MIN(y_start
+ MIN(visible_max
, config
->n_entries
) + 1, y_max
- 1);
696 lines
= strv_free(lines
);
697 clearline
= mfree(clearline
);
698 separator
= mfree(separator
);
700 /* menu entries title lines */
701 lines
= xnew(char16_t
*, config
->n_entries
+ 1);
703 for (size_t i
= 0; i
< config
->n_entries
; i
++) {
706 lines
[i
] = xnew(char16_t
, line_width
+ 1);
707 padding
= (line_width
- MIN(strlen16(config
->entries
[i
]->title_show
), line_width
)) / 2;
709 for (j
= 0; j
< padding
; j
++)
712 for (size_t k
= 0; config
->entries
[i
]->title_show
[k
] != '\0' && j
< line_width
; j
++, k
++)
713 lines
[i
][j
] = config
->entries
[i
]->title_show
[k
];
715 for (; j
< line_width
; j
++)
717 lines
[i
][line_width
] = '\0';
719 lines
[config
->n_entries
] = NULL
;
721 clearline
= xnew(char16_t
, x_max
+ 1);
722 separator
= xnew(char16_t
, x_max
+ 1);
723 for (size_t i
= 0; i
< x_max
; i
++) {
725 separator
[i
] = unicode_supported() ? L
'─' : L
'-';
727 clearline
[x_max
] = 0;
728 separator
[x_max
] = 0;
735 clear_screen(COLOR_NORMAL
);
741 for (size_t i
= idx_first
; i
<= idx_last
&& i
< config
->n_entries
; i
++) {
742 print_at(x_start
, y_start
+ i
- idx_first
,
743 i
== idx_highlight
? COLOR_HIGHLIGHT
: COLOR_ENTRY
,
745 if (i
== config
->idx_default_efivar
)
747 y_start
+ i
- idx_first
,
748 i
== idx_highlight
? COLOR_HIGHLIGHT
: COLOR_ENTRY
,
749 unicode_supported() ? u
" ►" : u
"=>");
752 } else if (highlight
) {
753 print_at(x_start
, y_start
+ idx_highlight_prev
- idx_first
, COLOR_ENTRY
, lines
[idx_highlight_prev
]);
754 print_at(x_start
, y_start
+ idx_highlight
- idx_first
, COLOR_HIGHLIGHT
, lines
[idx_highlight
]);
755 if (idx_highlight_prev
== config
->idx_default_efivar
)
757 y_start
+ idx_highlight_prev
- idx_first
,
759 unicode_supported() ? u
" ►" : u
"=>");
760 if (idx_highlight
== config
->idx_default_efivar
)
762 y_start
+ idx_highlight
- idx_first
,
764 unicode_supported() ? u
" ►" : u
"=>");
768 if (timeout_remain
> 0) {
770 status
= xasprintf("Boot in %u s.", timeout_remain
);
774 /* If we draw the last char of the last line, the screen will scroll and break our
775 * input. Therefore, draw one less character then we could for the status message.
776 * Note that the same does not apply for the separator line as it will never be drawn
777 * on the last line. */
778 size_t len
= strnlen16(status
, x_max
- 1);
779 size_t x
= (x_max
- len
) / 2;
781 print_at(0, y_status
, COLOR_NORMAL
, clearline
+ x_max
- x
);
782 ST
->ConOut
->OutputString(ST
->ConOut
, status
);
783 ST
->ConOut
->OutputString(ST
->ConOut
, clearline
+ 1 + x
+ len
);
785 len
= MIN(MAX(len
, line_width
) + 2 * entry_padding
, x_max
);
786 x
= (x_max
- len
) / 2;
787 print_at(x
, y_status
- 1, COLOR_NORMAL
, separator
+ x_max
- len
);
789 print_at(0, y_status
- 1, COLOR_NORMAL
, clearline
);
790 print_at(0, y_status
, COLOR_NORMAL
, clearline
+ 1); /* See comment above. */
793 /* Beep several times so that the selected entry can be distinguished. */
795 beep(idx_highlight
+ 1);
797 err
= console_key_read(&key
, timeout_remain
> 0 ? 1000 * 1000 : UINT64_MAX
);
798 if (err
== EFI_NOT_READY
)
799 /* No input device returned a key, try again. This
800 * normally should not happen. */
802 if (err
== EFI_TIMEOUT
) {
803 assert(timeout_remain
> 0);
805 if (timeout_remain
== 0) {
813 if (err
!= EFI_SUCCESS
) {
820 /* clear status after keystroke */
821 status
= mfree(status
);
823 idx_highlight_prev
= idx_highlight
;
825 if (firmware_setup
) {
826 firmware_setup
= false;
827 if (IN_SET(key
, KEYPRESS(0, 0, '\r'), KEYPRESS(0, 0, '\n')))
828 reboot_into_firmware();
833 case KEYPRESS(0, SCAN_UP
, 0):
834 case KEYPRESS(0, 0, 'k'):
835 case KEYPRESS(0, 0, 'K'):
836 if (idx_highlight
> 0)
840 case KEYPRESS(0, SCAN_DOWN
, 0):
841 case KEYPRESS(0, 0, 'j'):
842 case KEYPRESS(0, 0, 'J'):
843 if (idx_highlight
< config
->n_entries
-1)
847 case KEYPRESS(0, SCAN_HOME
, 0):
848 case KEYPRESS(EFI_ALT_PRESSED
, 0, '<'):
849 if (idx_highlight
> 0) {
855 case KEYPRESS(0, SCAN_END
, 0):
856 case KEYPRESS(EFI_ALT_PRESSED
, 0, '>'):
857 if (idx_highlight
< config
->n_entries
-1) {
859 idx_highlight
= config
->n_entries
-1;
863 case KEYPRESS(0, SCAN_PAGE_UP
, 0):
864 if (idx_highlight
> visible_max
)
865 idx_highlight
-= visible_max
;
870 case KEYPRESS(0, SCAN_PAGE_DOWN
, 0):
871 idx_highlight
+= visible_max
;
872 if (idx_highlight
> config
->n_entries
-1)
873 idx_highlight
= config
->n_entries
-1;
876 case KEYPRESS(0, 0, '\n'):
877 case KEYPRESS(0, 0, '\r'):
878 case KEYPRESS(0, SCAN_F3
, 0): /* EZpad Mini 4s firmware sends malformed events */
879 case KEYPRESS(0, SCAN_F3
, '\r'): /* Teclast X98+ II firmware sends malformed events */
880 case KEYPRESS(0, SCAN_RIGHT
, 0):
884 case KEYPRESS(0, SCAN_F1
, 0):
885 case KEYPRESS(0, 0, 'h'):
886 case KEYPRESS(0, 0, 'H'):
887 case KEYPRESS(0, 0, '?'):
888 /* This must stay below 80 characters! Q/v/Ctrl+l/f deliberately not advertised. */
889 status
= xstrdup16(u
"(d)efault (t/T)imeout (e)dit (r/R)esolution (p)rint (O)ff re(B)oot (h)elp");
892 case KEYPRESS(0, 0, 'Q'):
897 case KEYPRESS(0, 0, 'd'):
898 case KEYPRESS(0, 0, 'D'):
899 if (config
->idx_default_efivar
!= idx_highlight
) {
900 free(config
->entry_default_efivar
);
901 config
->entry_default_efivar
= xstrdup16(config
->entries
[idx_highlight
]->id
);
902 config
->idx_default_efivar
= idx_highlight
;
903 status
= xstrdup16(u
"Default boot entry selected.");
905 config
->entry_default_efivar
= mfree(config
->entry_default_efivar
);
906 config
->idx_default_efivar
= IDX_INVALID
;
907 status
= xstrdup16(u
"Default boot entry cleared.");
909 config
->use_saved_entry_efivar
= false;
913 case KEYPRESS(0, 0, '-'):
914 case KEYPRESS(0, 0, 'T'):
915 status
= update_timeout_efivar(&config
->timeout_sec_efivar
, false);
918 case KEYPRESS(0, 0, '+'):
919 case KEYPRESS(0, 0, 't'):
920 status
= update_timeout_efivar(&config
->timeout_sec_efivar
, true);
923 case KEYPRESS(0, 0, 'e'):
924 case KEYPRESS(0, 0, 'E'):
925 /* only the options of configured entries can be edited */
926 if (!config
->editor
||
927 !IN_SET(config
->entries
[idx_highlight
]->type
, LOADER_EFI
, LOADER_LINUX
, LOADER_UNIFIED_LINUX
)) {
928 status
= xstrdup16(u
"Entry does not support editing the command line.");
932 /* Unified kernels that are signed as a whole will not accept command line options
933 * when secure boot is enabled unless there is none embedded in the image. Do not try
934 * to pretend we can edit it to only have it be ignored. */
935 if (config
->entries
[idx_highlight
]->type
== LOADER_UNIFIED_LINUX
&&
936 secure_boot_enabled() &&
937 config
->entries
[idx_highlight
]->options
) {
938 status
= xstrdup16(u
"Entry not editable in SecureBoot mode.");
942 /* The edit line may end up on the last line of the screen. And even though we're
943 * not telling the firmware to advance the line, it still does in this one case,
944 * causing a scroll to happen that screws with our beautiful boot loader output.
945 * Since we cannot paint the last character of the edit line, we simply start
946 * at x-offset 1 for symmetry. */
947 print_at(1, y_status
, COLOR_EDIT
, clearline
+ 2);
948 exit
= line_edit(&config
->entries
[idx_highlight
]->options
, x_max
- 2, y_status
);
949 print_at(1, y_status
, COLOR_NORMAL
, clearline
+ 2);
951 /* The options string was now edited, hence we have to pass it to the invoked
953 config
->entries
[idx_highlight
]->options_implied
= false;
956 case KEYPRESS(0, 0, 'v'):
958 "systemd-boot " GIT_VERSION
" (" EFI_MACHINE_TYPE_NAME
"), "
959 "UEFI Specification %u.%02u, Vendor %ls %u.%02u",
960 ST
->Hdr
.Revision
>> 16,
961 ST
->Hdr
.Revision
& 0xffff,
963 ST
->FirmwareRevision
>> 16,
964 ST
->FirmwareRevision
& 0xffff);
967 case KEYPRESS(0, 0, 'p'):
968 case KEYPRESS(0, 0, 'P'):
969 print_status(config
, loaded_image_path
);
973 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'l'):
974 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('l')):
975 case 'L': /* only uppercase, do not conflict with lower-case 'l' which picks first Linux entry */
979 case KEYPRESS(0, 0, 'r'):
980 err
= console_set_mode(CONSOLE_MODE_NEXT
);
981 if (err
!= EFI_SUCCESS
)
982 status
= xasprintf_status(err
, "Error changing console mode: %m");
984 config
->console_mode_efivar
= ST
->ConOut
->Mode
->Mode
;
986 "Console mode changed to %" PRIi64
".",
987 config
->console_mode_efivar
);
992 case KEYPRESS(0, 0, 'R'):
993 config
->console_mode_efivar
= CONSOLE_MODE_KEEP
;
994 err
= console_set_mode(config
->console_mode
== CONSOLE_MODE_KEEP
?
995 console_mode_initial
: config
->console_mode
);
996 if (err
!= EFI_SUCCESS
)
997 status
= xasprintf_status(err
, "Error resetting console mode: %m");
1000 "Console mode reset to %s default.",
1001 config
->console_mode
== CONSOLE_MODE_KEEP
?
1003 "configuration file");
1007 case KEYPRESS(0, 0, 'f'):
1008 case KEYPRESS(0, 0, 'F'):
1009 case KEYPRESS(0, SCAN_F2
, 0): /* Most vendors. */
1010 case KEYPRESS(0, SCAN_F10
, 0): /* HP and Lenovo. */
1011 case KEYPRESS(0, SCAN_DELETE
, 0): /* Same as F2. */
1012 case KEYPRESS(0, SCAN_ESC
, 0): /* HP. */
1013 if (FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
)) {
1014 firmware_setup
= true;
1015 /* Let's make sure the user really wants to do this. */
1016 status
= xstrdup16(u
"Press Enter to reboot into firmware interface.");
1018 status
= xstrdup16(u
"Reboot into firmware interface not supported.");
1021 case KEYPRESS(0, 0, 'O'): /* Only uppercase, so that it can't be hit so easily fat-fingered, but still works safely over serial */
1022 RT
->ResetSystem(EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1025 case KEYPRESS(0, 0, 'B'): /* ditto */
1026 RT
->ResetSystem(EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1030 /* jump with a hotkey directly to a matching entry */
1031 idx
= entry_lookup_key(config
, idx_highlight
+1, KEYCHAR(key
));
1032 if (idx
== IDX_INVALID
)
1034 idx_highlight
= idx
;
1038 if (idx_highlight
> idx_last
) {
1039 idx_last
= idx_highlight
;
1040 idx_first
= 1 + idx_highlight
- visible_max
;
1042 } else if (idx_highlight
< idx_first
) {
1043 idx_first
= idx_highlight
;
1044 idx_last
= idx_highlight
+ visible_max
-1;
1048 if (!refresh
&& idx_highlight
!= idx_highlight_prev
)
1052 *chosen_entry
= config
->entries
[idx_highlight
];
1054 /* Update EFI vars after we left the menu to reduce NVRAM writes. */
1056 if (default_efivar_saved
!= config
->idx_default_efivar
)
1057 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderEntryDefault", config
->entry_default_efivar
, EFI_VARIABLE_NON_VOLATILE
);
1059 if (console_mode_efivar_saved
!= config
->console_mode_efivar
) {
1060 if (config
->console_mode_efivar
== CONSOLE_MODE_KEEP
)
1061 efivar_unset(MAKE_GUID_PTR(LOADER
), u
"LoaderConfigConsoleMode", EFI_VARIABLE_NON_VOLATILE
);
1063 efivar_set_uint_string(MAKE_GUID_PTR(LOADER
), u
"LoaderConfigConsoleMode",
1064 config
->console_mode_efivar
, EFI_VARIABLE_NON_VOLATILE
);
1067 if (timeout_efivar_saved
!= config
->timeout_sec_efivar
) {
1068 switch (config
->timeout_sec_efivar
) {
1070 efivar_unset(MAKE_GUID_PTR(LOADER
), u
"LoaderConfigTimeout", EFI_VARIABLE_NON_VOLATILE
);
1072 case TIMEOUT_MENU_FORCE
:
1073 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderConfigTimeout", u
"menu-force", EFI_VARIABLE_NON_VOLATILE
);
1075 case TIMEOUT_MENU_HIDDEN
:
1076 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderConfigTimeout", u
"menu-hidden", EFI_VARIABLE_NON_VOLATILE
);
1079 efivar_set_uint_string(MAKE_GUID_PTR(LOADER
), u
"LoaderConfigTimeout",
1080 config
->timeout_sec_efivar
, EFI_VARIABLE_NON_VOLATILE
);
1084 clear_screen(COLOR_NORMAL
);
1088 static void config_add_entry(Config
*config
, ConfigEntry
*entry
) {
1092 /* This is just for paranoia. */
1093 assert(config
->n_entries
< IDX_MAX
);
1095 if ((config
->n_entries
& 15) == 0) {
1096 config
->entries
= xrealloc(
1098 sizeof(void *) * config
->n_entries
,
1099 sizeof(void *) * (config
->n_entries
+ 16));
1101 config
->entries
[config
->n_entries
++] = entry
;
1104 static void config_entry_free(ConfigEntry
*entry
) {
1109 free(entry
->title_show
);
1111 free(entry
->sort_key
);
1112 free(entry
->version
);
1113 free(entry
->machine_id
);
1114 free(entry
->loader
);
1115 free(entry
->devicetree
);
1116 free(entry
->options
);
1117 strv_free(entry
->initrd
);
1119 free(entry
->current_name
);
1120 free(entry
->next_name
);
1124 static void config_entry_freep(ConfigEntry
**entry
) {
1125 config_entry_free(*entry
);
1128 static char *line_get_key_value(
1145 line
= content
+ *pos
;
1150 while (line
[linelen
] && !strchr8("\n\r", line
[linelen
]))
1153 /* move pos to next line */
1162 /* terminate line */
1163 line
[linelen
] = '\0';
1165 /* remove leading whitespace */
1166 while (strchr8(" \t", *line
)) {
1171 /* remove trailing whitespace */
1172 while (linelen
> 0 && strchr8(" \t", line
[linelen
- 1]))
1174 line
[linelen
] = '\0';
1179 /* split key/value */
1181 while (*value
&& !strchr8(sep
, *value
))
1187 while (*value
&& strchr8(sep
, *value
))
1191 if (value
[0] == '"' && line
[linelen
- 1] == '"') {
1193 line
[linelen
- 1] = '\0';
1202 static void config_defaults_load_from_file(Config
*config
, char *content
) {
1211 while ((line
= line_get_key_value(content
, " \t", &pos
, &key
, &value
))) {
1212 if (streq8(key
, "timeout")) {
1213 if (streq8( value
, "menu-force"))
1214 config
->timeout_sec_config
= TIMEOUT_MENU_FORCE
;
1215 else if (streq8(value
, "menu-hidden"))
1216 config
->timeout_sec_config
= TIMEOUT_MENU_HIDDEN
;
1219 if (!parse_number8(value
, &u
, NULL
) || u
> TIMEOUT_TYPE_MAX
) {
1220 log_error("Error parsing 'timeout' config option: %s", value
);
1223 config
->timeout_sec_config
= u
;
1225 config
->timeout_sec
= config
->timeout_sec_config
;
1229 if (streq8(key
, "default")) {
1230 if (value
[0] == '@' && !strcaseeq8(value
, "@saved")) {
1231 log_error("Unsupported special entry identifier: %s", value
);
1234 free(config
->entry_default_config
);
1235 config
->entry_default_config
= xstr8_to_16(value
);
1239 if (streq8(key
, "editor")) {
1240 err
= parse_boolean(value
, &config
->editor
);
1241 if (err
!= EFI_SUCCESS
)
1242 log_error("Error parsing 'editor' config option: %s", value
);
1246 if (streq8(key
, "auto-entries")) {
1247 err
= parse_boolean(value
, &config
->auto_entries
);
1248 if (err
!= EFI_SUCCESS
)
1249 log_error("Error parsing 'auto-entries' config option: %s", value
);
1253 if (streq8(key
, "auto-firmware")) {
1254 err
= parse_boolean(value
, &config
->auto_firmware
);
1255 if (err
!= EFI_SUCCESS
)
1256 log_error("Error parsing 'auto-firmware' config option: %s", value
);
1260 if (streq8(key
, "beep")) {
1261 err
= parse_boolean(value
, &config
->beep
);
1262 if (err
!= EFI_SUCCESS
)
1263 log_error("Error parsing 'beep' config option: %s", value
);
1267 if (streq8(key
, "reboot-for-bitlocker")) {
1268 err
= parse_boolean(value
, &config
->reboot_for_bitlocker
);
1269 if (err
!= EFI_SUCCESS
)
1270 log_error("Error parsing 'reboot-for-bitlocker' config option: %s", value
);
1273 if (streq8(key
, "secure-boot-enroll")) {
1274 if (streq8(value
, "manual"))
1275 config
->secure_boot_enroll
= ENROLL_MANUAL
;
1276 else if (streq8(value
, "force"))
1277 config
->secure_boot_enroll
= ENROLL_FORCE
;
1278 else if (streq8(value
, "if-safe"))
1279 config
->secure_boot_enroll
= ENROLL_IF_SAFE
;
1280 else if (streq8(value
, "off"))
1281 config
->secure_boot_enroll
= ENROLL_OFF
;
1283 log_error("Error parsing 'secure-boot-enroll' config option: %s", value
);
1287 if (streq8(key
, "console-mode")) {
1288 if (streq8(value
, "auto"))
1289 config
->console_mode
= CONSOLE_MODE_AUTO
;
1290 else if (streq8(value
, "max"))
1291 config
->console_mode
= CONSOLE_MODE_FIRMWARE_MAX
;
1292 else if (streq8(value
, "keep"))
1293 config
->console_mode
= CONSOLE_MODE_KEEP
;
1296 if (!parse_number8(value
, &u
, NULL
) || u
> CONSOLE_MODE_RANGE_MAX
) {
1297 log_error("Error parsing 'console-mode' config option: %s", value
);
1300 config
->console_mode
= u
;
1307 static void config_entry_parse_tries(
1309 const char16_t
*path
,
1310 const char16_t
*file
,
1311 const char16_t
*suffix
) {
1319 * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
1320 * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
1321 * which case that counter as assumed to be zero, i.e. the missing part is synonymous to "-0").
1323 * Names we grok, and the series they result in:
1325 * foobar+3.efi → foobar+2-1.efi → foobar+1-2.efi → foobar+0-3.efi → STOP!
1326 * foobar+4-0.efi → foobar+3-1.efi → foobar+2-2.efi → foobar+1-3.efi → foobar+0-4.efi → STOP!
1329 const char16_t
*counter
= NULL
;
1331 char16_t
*plus
= strchr16(counter
?: file
, '+');
1333 /* We want the last "+". */
1340 /* No boot counter found. */
1344 uint64_t tries_left
, tries_done
= 0;
1345 size_t prefix_len
= counter
- file
;
1347 if (!parse_number16(counter
, &tries_left
, &counter
) || tries_left
> INT_MAX
)
1350 /* Parse done counter only if present. */
1351 if (*counter
== '-' && (!parse_number16(counter
+ 1, &tries_done
, &counter
) || tries_done
> INT_MAX
))
1354 /* Boot counter in the middle of the name? */
1355 if (!streq16(counter
, suffix
))
1358 entry
->tries_left
= tries_left
;
1359 entry
->tries_done
= tries_done
;
1360 entry
->path
= xstrdup16(path
);
1361 entry
->current_name
= xstrdup16(file
);
1362 entry
->next_name
= xasprintf(
1363 "%.*ls%" PRIu64
"-%" PRIu64
"%ls",
1366 LESS_BY(tries_left
, 1u),
1367 MIN(tries_done
+ 1, (uint64_t) INT_MAX
),
1371 static EFI_STATUS
config_entry_bump_counters(ConfigEntry
*entry
) {
1372 _cleanup_free_ char16_t
* old_path
= NULL
, *new_path
= NULL
;
1373 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1374 _cleanup_free_ EFI_FILE_INFO
*file_info
= NULL
;
1375 size_t file_info_size
;
1380 if (entry
->tries_left
< 0)
1383 if (!entry
->path
|| !entry
->current_name
|| !entry
->next_name
)
1386 _cleanup_(file_closep
) EFI_FILE
*root
= NULL
;
1387 err
= open_volume(entry
->device
, &root
);
1388 if (err
!= EFI_SUCCESS
)
1389 return log_error_status(err
, "Error opening entry root path: %m");
1391 old_path
= xasprintf("%ls\\%ls", entry
->path
, entry
->current_name
);
1393 err
= root
->Open(root
, &handle
, old_path
, EFI_FILE_MODE_READ
|EFI_FILE_MODE_WRITE
, 0ULL);
1394 if (err
!= EFI_SUCCESS
)
1395 return log_error_status(err
, "Error opening boot entry: %m");
1397 err
= get_file_info(handle
, &file_info
, &file_info_size
);
1398 if (err
!= EFI_SUCCESS
)
1399 return log_error_status(err
, "Error getting boot entry file info: %m");
1401 /* And rename the file */
1402 strcpy16(file_info
->FileName
, entry
->next_name
);
1403 err
= handle
->SetInfo(handle
, MAKE_GUID_PTR(EFI_FILE_INFO
), file_info_size
, file_info
);
1404 if (err
!= EFI_SUCCESS
)
1405 return log_error_status(
1406 err
, "Failed to rename '%ls' to '%ls', ignoring: %m", old_path
, entry
->next_name
);
1408 /* Flush everything to disk, just in case… */
1409 err
= handle
->Flush(handle
);
1410 if (err
!= EFI_SUCCESS
)
1411 return log_error_status(err
, "Error flushing boot entry file info: %m");
1413 /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
1415 new_path
= xasprintf("%ls\\%ls", entry
->path
, entry
->next_name
);
1416 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderBootCountPath", new_path
, 0);
1418 /* If the file we just renamed is the loader path, then let's update that. */
1419 if (streq16(entry
->loader
, old_path
)) {
1420 free(entry
->loader
);
1421 entry
->loader
= TAKE_PTR(new_path
);
1427 static void config_entry_add_type1(
1431 const char16_t
*path
,
1432 const char16_t
*file
,
1434 const char16_t
*loaded_image_path
) {
1436 _cleanup_(config_entry_freep
) ConfigEntry
*entry
= NULL
;
1438 size_t pos
= 0, n_initrd
= 0;
1449 entry
= xnew(ConfigEntry
, 1);
1450 *entry
= (ConfigEntry
) {
1455 while ((line
= line_get_key_value(content
, " \t", &pos
, &key
, &value
))) {
1456 if (streq8(key
, "title")) {
1458 entry
->title
= xstr8_to_16(value
);
1462 if (streq8(key
, "sort-key")) {
1463 free(entry
->sort_key
);
1464 entry
->sort_key
= xstr8_to_16(value
);
1468 if (streq8(key
, "version")) {
1469 free(entry
->version
);
1470 entry
->version
= xstr8_to_16(value
);
1474 if (streq8(key
, "machine-id")) {
1475 free(entry
->machine_id
);
1476 entry
->machine_id
= xstr8_to_16(value
);
1480 if (streq8(key
, "linux")) {
1481 free(entry
->loader
);
1482 entry
->type
= LOADER_LINUX
;
1483 entry
->loader
= xstr8_to_path(value
);
1488 if (streq8(key
, "efi")) {
1489 entry
->type
= LOADER_EFI
;
1490 free(entry
->loader
);
1491 entry
->loader
= xstr8_to_path(value
);
1493 /* do not add an entry for ourselves */
1494 if (strcaseeq16(entry
->loader
, loaded_image_path
)) {
1495 entry
->type
= LOADER_UNDEFINED
;
1501 if (streq8(key
, "architecture")) {
1502 /* do not add an entry for an EFI image of architecture not matching with that of the image */
1503 if (!streq8(value
, EFI_MACHINE_TYPE_NAME
)) {
1504 entry
->type
= LOADER_UNDEFINED
;
1510 if (streq8(key
, "devicetree")) {
1511 free(entry
->devicetree
);
1512 entry
->devicetree
= xstr8_to_path(value
);
1516 if (streq8(key
, "initrd")) {
1517 entry
->initrd
= xrealloc(
1519 n_initrd
== 0 ? 0 : (n_initrd
+ 1) * sizeof(uint16_t *),
1520 (n_initrd
+ 2) * sizeof(uint16_t *));
1521 entry
->initrd
[n_initrd
++] = xstr8_to_path(value
);
1522 entry
->initrd
[n_initrd
] = NULL
;
1526 if (streq8(key
, "options")) {
1527 _cleanup_free_ char16_t
*new = NULL
;
1529 new = xstr8_to_16(value
);
1530 if (entry
->options
) {
1531 char16_t
*s
= xasprintf("%ls %ls", entry
->options
, new);
1532 free(entry
->options
);
1535 entry
->options
= TAKE_PTR(new);
1541 if (entry
->type
== LOADER_UNDEFINED
)
1544 /* check existence */
1545 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1546 err
= root_dir
->Open(root_dir
, &handle
, entry
->loader
, EFI_FILE_MODE_READ
, 0ULL);
1547 if (err
!= EFI_SUCCESS
)
1550 entry
->device
= device
;
1551 entry
->id
= xstrdup16(file
);
1552 strtolower16(entry
->id
);
1554 config_add_entry(config
, entry
);
1556 config_entry_parse_tries(entry
, path
, file
, u
".conf");
1560 static EFI_STATUS
efivar_get_timeout(const char16_t
*var
, uint32_t *ret_value
) {
1561 _cleanup_free_ char16_t
*value
= NULL
;
1567 err
= efivar_get(MAKE_GUID_PTR(LOADER
), var
, &value
);
1568 if (err
!= EFI_SUCCESS
)
1571 if (streq16(value
, u
"menu-force")) {
1572 *ret_value
= TIMEOUT_MENU_FORCE
;
1575 if (streq16(value
, u
"menu-hidden")) {
1576 *ret_value
= TIMEOUT_MENU_HIDDEN
;
1581 if (!parse_number16(value
, &timeout
, NULL
))
1582 return EFI_INVALID_PARAMETER
;
1584 *ret_value
= MIN(timeout
, TIMEOUT_TYPE_MAX
);
1588 static void config_load_defaults(Config
*config
, EFI_FILE
*root_dir
) {
1589 _cleanup_free_
char *content
= NULL
;
1590 size_t value
= 0; /* avoid false maybe-uninitialized warning */
1595 *config
= (Config
) {
1597 .auto_entries
= true,
1598 .auto_firmware
= true,
1599 .reboot_for_bitlocker
= false,
1600 .secure_boot_enroll
= ENROLL_IF_SAFE
,
1601 .idx_default_efivar
= IDX_INVALID
,
1602 .console_mode
= CONSOLE_MODE_KEEP
,
1603 .console_mode_efivar
= CONSOLE_MODE_KEEP
,
1604 .timeout_sec_config
= TIMEOUT_UNSET
,
1605 .timeout_sec_efivar
= TIMEOUT_UNSET
,
1608 err
= file_read(root_dir
, u
"\\loader\\loader.conf", 0, 0, &content
, NULL
);
1609 if (err
== EFI_SUCCESS
)
1610 config_defaults_load_from_file(config
, content
);
1612 err
= efivar_get_timeout(u
"LoaderConfigTimeout", &config
->timeout_sec_efivar
);
1613 if (err
== EFI_SUCCESS
)
1614 config
->timeout_sec
= config
->timeout_sec_efivar
;
1615 else if (err
!= EFI_NOT_FOUND
)
1616 log_error_status(err
, "Error reading LoaderConfigTimeout EFI variable: %m");
1618 err
= efivar_get_timeout(u
"LoaderConfigTimeoutOneShot", &config
->timeout_sec
);
1619 if (err
== EFI_SUCCESS
) {
1620 /* Unset variable now, after all it's "one shot". */
1621 (void) efivar_unset(MAKE_GUID_PTR(LOADER
), u
"LoaderConfigTimeoutOneShot", EFI_VARIABLE_NON_VOLATILE
);
1623 config
->force_menu
= true; /* force the menu when this is set */
1624 } else if (err
!= EFI_NOT_FOUND
)
1625 log_error_status(err
, "Error reading LoaderConfigTimeoutOneShot EFI variable: %m");
1627 err
= efivar_get_uint_string(MAKE_GUID_PTR(LOADER
), u
"LoaderConfigConsoleMode", &value
);
1628 if (err
== EFI_SUCCESS
)
1629 config
->console_mode_efivar
= value
;
1631 err
= efivar_get(MAKE_GUID_PTR(LOADER
), u
"LoaderEntryOneShot", &config
->entry_oneshot
);
1632 if (err
== EFI_SUCCESS
)
1633 /* Unset variable now, after all it's "one shot". */
1634 (void) efivar_unset(MAKE_GUID_PTR(LOADER
), u
"LoaderEntryOneShot", EFI_VARIABLE_NON_VOLATILE
);
1636 (void) efivar_get(MAKE_GUID_PTR(LOADER
), u
"LoaderEntryDefault", &config
->entry_default_efivar
);
1638 strtolower16(config
->entry_default_config
);
1639 strtolower16(config
->entry_default_efivar
);
1640 strtolower16(config
->entry_oneshot
);
1641 strtolower16(config
->entry_saved
);
1643 config
->use_saved_entry
= streq16(config
->entry_default_config
, u
"@saved");
1644 config
->use_saved_entry_efivar
= streq16(config
->entry_default_efivar
, u
"@saved");
1645 if (config
->use_saved_entry
|| config
->use_saved_entry_efivar
)
1646 (void) efivar_get(MAKE_GUID_PTR(LOADER
), u
"LoaderEntryLastBooted", &config
->entry_saved
);
1649 static void config_load_entries(
1653 const char16_t
*loaded_image_path
) {
1655 _cleanup_(file_closep
) EFI_FILE
*entries_dir
= NULL
;
1656 _cleanup_free_ EFI_FILE_INFO
*f
= NULL
;
1664 /* Adds Boot Loader Type #1 entries (i.e. /loader/entries/….conf) */
1666 err
= open_directory(root_dir
, u
"\\loader\\entries", &entries_dir
);
1667 if (err
!= EFI_SUCCESS
)
1671 _cleanup_free_
char *content
= NULL
;
1673 err
= readdir(entries_dir
, &f
, &f_size
);
1674 if (err
!= EFI_SUCCESS
|| !f
)
1677 if (f
->FileName
[0] == '.')
1679 if (FLAGS_SET(f
->Attribute
, EFI_FILE_DIRECTORY
))
1682 if (!endswith_no_case(f
->FileName
, u
".conf"))
1684 if (startswith(f
->FileName
, u
"auto-"))
1687 err
= file_read(entries_dir
, f
->FileName
, 0, 0, &content
, NULL
);
1688 if (err
== EFI_SUCCESS
)
1689 config_entry_add_type1(config
, device
, root_dir
, u
"\\loader\\entries", f
->FileName
, content
, loaded_image_path
);
1693 static int config_entry_compare(const ConfigEntry
*a
, const ConfigEntry
*b
) {
1699 /* Order entries that have no tries left to the end of the list */
1700 r
= CMP(a
->tries_left
== 0, b
->tries_left
== 0);
1704 /* If there's a sort key defined for *both* entries, then we do new-style ordering, i.e. by
1705 * sort-key/machine-id/version, with a final fallback to id. If there's no sort key for either, we do
1706 * old-style ordering, i.e. by id only. If one has sort key and the other does not, we put new-style
1707 * before old-style. */
1708 r
= CMP(!a
->sort_key
, !b
->sort_key
);
1709 if (r
!= 0) /* one is old-style, one new-style */
1712 if (a
->sort_key
&& b
->sort_key
) {
1713 r
= strcmp16(a
->sort_key
, b
->sort_key
);
1717 /* If multiple installations of the same OS are around, group by machine ID */
1718 r
= strcmp16(a
->machine_id
, b
->machine_id
);
1722 /* If the sort key was defined, then order by version now (downwards, putting the newest first) */
1723 r
= -strverscmp_improved(a
->version
, b
->version
);
1728 /* Now order by ID. The version is likely part of the ID, thus note that this will generatelly put
1729 * the newer versions earlier. Specifying a sort key explicitly is preferable, because it gives an
1730 * explicit sort order. */
1731 r
= -strverscmp_improved(a
->id
, b
->id
);
1735 if (a
->tries_left
< 0 || b
->tries_left
< 0)
1738 /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
1739 r
= -CMP(a
->tries_left
, b
->tries_left
);
1743 /* If they have the same number of tries left, then let the one win which was tried fewer times so far */
1744 return CMP(a
->tries_done
, b
->tries_done
);
1747 static size_t config_entry_find(Config
*config
, const char16_t
*pattern
) {
1750 /* We expect pattern and entry IDs to be already case folded. */
1755 for (size_t i
= 0; i
< config
->n_entries
; i
++)
1756 if (efi_fnmatch(pattern
, config
->entries
[i
]->id
))
1762 static void config_default_entry_select(Config
*config
) {
1767 i
= config_entry_find(config
, config
->entry_oneshot
);
1768 if (i
!= IDX_INVALID
) {
1769 config
->idx_default
= i
;
1773 i
= config_entry_find(config
, config
->use_saved_entry_efivar
? config
->entry_saved
: config
->entry_default_efivar
);
1774 if (i
!= IDX_INVALID
) {
1775 config
->idx_default
= i
;
1776 config
->idx_default_efivar
= i
;
1780 if (config
->use_saved_entry
)
1781 /* No need to do the same thing twice. */
1782 i
= config
->use_saved_entry_efivar
? IDX_INVALID
: config_entry_find(config
, config
->entry_saved
);
1784 i
= config_entry_find(config
, config
->entry_default_config
);
1785 if (i
!= IDX_INVALID
) {
1786 config
->idx_default
= i
;
1790 /* select the first suitable entry */
1791 for (i
= 0; i
< config
->n_entries
; i
++) {
1792 if (config
->entries
[i
]->type
== LOADER_AUTO
|| config
->entries
[i
]->call
)
1794 config
->idx_default
= i
;
1798 /* If no configured entry to select from was found, enable the menu. */
1799 config
->idx_default
= 0;
1800 if (config
->timeout_sec
== 0)
1801 config
->timeout_sec
= 10;
1804 static bool entries_unique(ConfigEntry
**entries
, bool *unique
, size_t n_entries
) {
1805 bool is_unique
= true;
1810 for (size_t i
= 0; i
< n_entries
; i
++)
1811 for (size_t k
= i
+ 1; k
< n_entries
; k
++) {
1812 if (!streq16(entries
[i
]->title_show
, entries
[k
]->title_show
))
1815 is_unique
= unique
[i
] = unique
[k
] = false;
1821 /* generate a unique title, avoiding non-distinguishable menu entries */
1822 static void config_title_generate(Config
*config
) {
1825 bool unique
[config
->n_entries
];
1828 for (size_t i
= 0; i
< config
->n_entries
; i
++) {
1829 assert(!config
->entries
[i
]->title_show
);
1831 config
->entries
[i
]->title_show
= xstrdup16(config
->entries
[i
]->title
?: config
->entries
[i
]->id
);
1834 if (entries_unique(config
->entries
, unique
, config
->n_entries
))
1837 /* add version to non-unique titles */
1838 for (size_t i
= 0; i
< config
->n_entries
; i
++) {
1844 if (!config
->entries
[i
]->version
)
1847 _cleanup_free_ char16_t
*t
= config
->entries
[i
]->title_show
;
1848 config
->entries
[i
]->title_show
= xasprintf("%ls (%ls)", t
, config
->entries
[i
]->version
);
1851 if (entries_unique(config
->entries
, unique
, config
->n_entries
))
1854 /* add machine-id to non-unique titles */
1855 for (size_t i
= 0; i
< config
->n_entries
; i
++) {
1861 if (!config
->entries
[i
]->machine_id
)
1864 _cleanup_free_ char16_t
*t
= config
->entries
[i
]->title_show
;
1865 config
->entries
[i
]->title_show
= xasprintf("%ls (%.8ls)", t
, config
->entries
[i
]->machine_id
);
1868 if (entries_unique(config
->entries
, unique
, config
->n_entries
))
1871 /* add file name to non-unique titles */
1872 for (size_t i
= 0; i
< config
->n_entries
; i
++) {
1876 _cleanup_free_ char16_t
*t
= config
->entries
[i
]->title_show
;
1877 config
->entries
[i
]->title_show
= xasprintf("%ls (%ls)", t
, config
->entries
[i
]->id
);
1881 static bool is_sd_boot(EFI_FILE
*root_dir
, const char16_t
*loader_path
) {
1883 static const char * const sections
[] = {
1887 size_t offset
= 0, size
= 0, read
;
1888 _cleanup_free_
char *content
= NULL
;
1891 assert(loader_path
);
1893 err
= pe_file_locate_sections(root_dir
, loader_path
, sections
, &offset
, &size
);
1894 if (err
!= EFI_SUCCESS
|| size
!= sizeof(SD_MAGIC
))
1897 err
= file_read(root_dir
, loader_path
, offset
, size
, &content
, &read
);
1898 if (err
!= EFI_SUCCESS
|| size
!= read
)
1901 return memcmp(content
, SD_MAGIC
, sizeof(SD_MAGIC
)) == 0;
1904 static ConfigEntry
*config_entry_add_loader_auto(
1908 const char16_t
*loaded_image_path
,
1911 const char16_t
*title
,
1912 const char16_t
*loader
) {
1920 if (!config
->auto_entries
)
1924 loader
= u
"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME
".efi";
1926 /* We are trying to add the default EFI loader here,
1927 * but we do not want to do that if that would be us.
1929 * If the default loader is not us, it might be shim. It would
1930 * chainload GRUBX64.EFI in that case, which might be us. */
1931 if (strcaseeq16(loader
, loaded_image_path
) ||
1932 is_sd_boot(root_dir
, loader
) ||
1933 is_sd_boot(root_dir
, u
"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME u
".EFI"))
1937 /* check existence */
1938 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
1939 EFI_STATUS err
= root_dir
->Open(root_dir
, &handle
, (char16_t
*) loader
, EFI_FILE_MODE_READ
, 0ULL);
1940 if (err
!= EFI_SUCCESS
)
1943 ConfigEntry
*entry
= xnew(ConfigEntry
, 1);
1944 *entry
= (ConfigEntry
) {
1945 .id
= xstrdup16(id
),
1946 .type
= LOADER_AUTO
,
1947 .title
= xstrdup16(title
),
1949 .loader
= xstrdup16(loader
),
1955 config_add_entry(config
, entry
);
1959 static void config_entry_add_osx(Config
*config
) {
1961 size_t n_handles
= 0;
1962 _cleanup_free_ EFI_HANDLE
*handles
= NULL
;
1966 if (!config
->auto_entries
)
1969 err
= BS
->LocateHandleBuffer(
1970 ByProtocol
, MAKE_GUID_PTR(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
), NULL
, &n_handles
, &handles
);
1971 if (err
!= EFI_SUCCESS
)
1974 for (size_t i
= 0; i
< n_handles
; i
++) {
1975 _cleanup_(file_closep
) EFI_FILE
*root
= NULL
;
1977 if (open_volume(handles
[i
], &root
) != EFI_SUCCESS
)
1980 if (config_entry_add_loader_auto(
1988 u
"\\System\\Library\\CoreServices\\boot.efi"))
1993 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
1994 static EFI_STATUS
boot_windows_bitlocker(void) {
1995 _cleanup_free_ EFI_HANDLE
*handles
= NULL
;
1999 // FIXME: Experimental for now. Should be generalized, and become a per-entry option that can be
2000 // enabled independently of BitLocker, and without a BootXXXX entry pre-existing.
2002 /* BitLocker key cannot be sealed without a TPM present. */
2004 return EFI_NOT_FOUND
;
2006 err
= BS
->LocateHandleBuffer(
2007 ByProtocol
, MAKE_GUID_PTR(EFI_BLOCK_IO_PROTOCOL
), NULL
, &n_handles
, &handles
);
2008 if (err
!= EFI_SUCCESS
)
2011 /* Look for BitLocker magic string on all block drives. */
2013 for (size_t i
= 0; i
< n_handles
; i
++) {
2014 EFI_BLOCK_IO_PROTOCOL
*block_io
;
2015 err
= BS
->HandleProtocol(handles
[i
], MAKE_GUID_PTR(EFI_BLOCK_IO_PROTOCOL
), (void **) &block_io
);
2016 if (err
!= EFI_SUCCESS
|| block_io
->Media
->BlockSize
< 512 || block_io
->Media
->BlockSize
> 4096)
2020 err
= block_io
->ReadBlocks(block_io
, block_io
->Media
->MediaId
, 0, sizeof(buf
), buf
);
2021 if (err
!= EFI_SUCCESS
)
2024 if (memcmp(buf
+ 3, "-FVE-FS-", STRLEN("-FVE-FS-")) == 0) {
2030 /* If no BitLocker drive was found, we can just chainload bootmgfw.efi directly. */
2032 return EFI_NOT_FOUND
;
2034 _cleanup_free_
uint16_t *boot_order
= NULL
;
2035 size_t boot_order_size
;
2037 /* There can be gaps in Boot#### entries. Instead of iterating over the full
2038 * EFI var list or uint16_t namespace, just look for "Windows Boot Manager" in BootOrder. */
2039 err
= efivar_get_raw(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE
), u
"BootOrder", (char **) &boot_order
, &boot_order_size
);
2040 if (err
!= EFI_SUCCESS
|| boot_order_size
% sizeof(uint16_t) != 0)
2043 for (size_t i
= 0; i
< boot_order_size
/ sizeof(uint16_t); i
++) {
2044 _cleanup_free_
char *buf
= NULL
;
2047 _cleanup_free_ char16_t
*name
= xasprintf("Boot%04x", boot_order
[i
]);
2048 err
= efivar_get_raw(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE
), name
, &buf
, &buf_size
);
2049 if (err
!= EFI_SUCCESS
)
2052 /* Boot#### are EFI_LOAD_OPTION. But we really are only interested
2053 * for the description, which is at this offset. */
2054 size_t offset
= sizeof(uint32_t) + sizeof(uint16_t);
2055 if (buf_size
< offset
+ sizeof(char16_t
))
2058 if (streq16((char16_t
*) (buf
+ offset
), u
"Windows Boot Manager")) {
2059 err
= efivar_set_raw(
2060 MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE
),
2063 sizeof(boot_order
[i
]),
2064 EFI_VARIABLE_NON_VOLATILE
);
2065 if (err
!= EFI_SUCCESS
)
2067 RT
->ResetSystem(EfiResetWarm
, EFI_SUCCESS
, 0, NULL
);
2068 assert_not_reached();
2072 return EFI_NOT_FOUND
;
2076 static void config_entry_add_windows(Config
*config
, EFI_HANDLE
*device
, EFI_FILE
*root_dir
) {
2077 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
2078 _cleanup_free_
char *bcd
= NULL
;
2079 char16_t
*title
= NULL
;
2087 if (!config
->auto_entries
)
2090 /* Try to find a better title. */
2091 err
= file_read(root_dir
, u
"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd
, &len
);
2092 if (err
== EFI_SUCCESS
)
2093 title
= get_bcd_title((uint8_t *) bcd
, len
);
2095 ConfigEntry
*e
= config_entry_add_loader_auto(config
, device
, root_dir
, NULL
,
2096 u
"auto-windows", 'w', title
?: u
"Windows Boot Manager",
2097 u
"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2099 if (config
->reboot_for_bitlocker
)
2100 e
->call
= boot_windows_bitlocker
;
2104 static void config_entry_add_unified(
2107 EFI_FILE
*root_dir
) {
2109 _cleanup_(file_closep
) EFI_FILE
*linux_dir
= NULL
;
2110 _cleanup_free_ EFI_FILE_INFO
*f
= NULL
;
2114 /* Adds Boot Loader Type #2 entries (i.e. /EFI/Linux/….efi) */
2120 err
= open_directory(root_dir
, u
"\\EFI\\Linux", &linux_dir
);
2121 if (err
!= EFI_SUCCESS
)
2131 static const char * const sections
[_SECTION_MAX
+ 1] = {
2132 [SECTION_CMDLINE
] = ".cmdline",
2133 [SECTION_OSREL
] = ".osrel",
2137 _cleanup_free_ char16_t
*os_pretty_name
= NULL
, *os_image_id
= NULL
, *os_name
= NULL
, *os_id
= NULL
,
2138 *os_image_version
= NULL
, *os_version
= NULL
, *os_version_id
= NULL
, *os_build_id
= NULL
;
2139 const char16_t
*good_name
, *good_version
, *good_sort_key
;
2140 _cleanup_free_
char *content
= NULL
;
2141 size_t offs
[_SECTION_MAX
] = {}, szs
[_SECTION_MAX
] = {}, pos
= 0;
2142 char *line
, *key
, *value
;
2144 err
= readdir(linux_dir
, &f
, &f_size
);
2145 if (err
!= EFI_SUCCESS
|| !f
)
2148 if (f
->FileName
[0] == '.')
2150 if (FLAGS_SET(f
->Attribute
, EFI_FILE_DIRECTORY
))
2152 if (!endswith_no_case(f
->FileName
, u
".efi"))
2154 if (startswith(f
->FileName
, u
"auto-"))
2157 /* look for .osrel and .cmdline sections in the .efi binary */
2158 err
= pe_file_locate_sections(linux_dir
, f
->FileName
, sections
, offs
, szs
);
2159 if (err
!= EFI_SUCCESS
|| szs
[SECTION_OSREL
] == 0)
2162 err
= file_read(linux_dir
, f
->FileName
, offs
[SECTION_OSREL
], szs
[SECTION_OSREL
], &content
, NULL
);
2163 if (err
!= EFI_SUCCESS
)
2166 /* read properties from the embedded os-release file */
2167 while ((line
= line_get_key_value(content
, "=", &pos
, &key
, &value
))) {
2168 if (streq8(key
, "PRETTY_NAME")) {
2169 free(os_pretty_name
);
2170 os_pretty_name
= xstr8_to_16(value
);
2174 if (streq8(key
, "IMAGE_ID")) {
2176 os_image_id
= xstr8_to_16(value
);
2180 if (streq8(key
, "NAME")) {
2182 os_name
= xstr8_to_16(value
);
2186 if (streq8(key
, "ID")) {
2188 os_id
= xstr8_to_16(value
);
2192 if (streq8(key
, "IMAGE_VERSION")) {
2193 free(os_image_version
);
2194 os_image_version
= xstr8_to_16(value
);
2198 if (streq8(key
, "VERSION")) {
2200 os_version
= xstr8_to_16(value
);
2204 if (streq8(key
, "VERSION_ID")) {
2205 free(os_version_id
);
2206 os_version_id
= xstr8_to_16(value
);
2210 if (streq8(key
, "BUILD_ID")) {
2212 os_build_id
= xstr8_to_16(value
);
2217 if (!bootspec_pick_name_version_sort_key(
2231 ConfigEntry
*entry
= xnew(ConfigEntry
, 1);
2232 *entry
= (ConfigEntry
) {
2233 .id
= xstrdup16(f
->FileName
),
2234 .type
= LOADER_UNIFIED_LINUX
,
2235 .title
= xstrdup16(good_name
),
2236 .version
= xstrdup16(good_version
),
2238 .loader
= xasprintf("\\EFI\\Linux\\%ls", f
->FileName
),
2239 .sort_key
= xstrdup16(good_sort_key
),
2245 strtolower16(entry
->id
);
2246 config_add_entry(config
, entry
);
2247 config_entry_parse_tries(entry
, u
"\\EFI\\Linux", f
->FileName
, u
".efi");
2249 if (szs
[SECTION_CMDLINE
] == 0)
2252 content
= mfree(content
);
2254 /* read the embedded cmdline file */
2256 err
= file_read(linux_dir
, f
->FileName
, offs
[SECTION_CMDLINE
], szs
[SECTION_CMDLINE
], &content
, &cmdline_len
);
2257 if (err
== EFI_SUCCESS
) {
2258 entry
->options
= xstrn8_to_16(content
, cmdline_len
);
2259 mangle_stub_cmdline(entry
->options
);
2260 entry
->options_implied
= true;
2265 static void config_load_xbootldr(
2267 EFI_HANDLE
*device
) {
2269 _cleanup_(file_closep
) EFI_FILE
*root_dir
= NULL
;
2270 EFI_HANDLE new_device
= NULL
; /* avoid false maybe-uninitialized warning */
2276 err
= partition_open(MAKE_GUID_PTR(XBOOTLDR
), device
, &new_device
, &root_dir
);
2277 if (err
!= EFI_SUCCESS
)
2280 config_entry_add_unified(config
, new_device
, root_dir
);
2281 config_load_entries(config
, new_device
, root_dir
, NULL
);
2284 static EFI_STATUS
initrd_prepare(
2286 const ConfigEntry
*entry
,
2287 char16_t
**ret_options
,
2289 size_t *ret_initrd_size
) {
2293 assert(ret_options
);
2295 assert(ret_initrd_size
);
2297 if (entry
->type
!= LOADER_LINUX
|| !entry
->initrd
) {
2300 ret_initrd_size
= 0;
2304 /* Note that order of initrds matters. The kernel will only look for microcode updates in the very
2305 * first one it sees. */
2307 /* Add initrd= to options for older kernels that do not support LINUX_INITRD_MEDIA. Should be dropped
2308 * if linux_x86.c is dropped. */
2309 _cleanup_free_ char16_t
*options
= NULL
;
2313 _cleanup_free_
uint8_t *initrd
= NULL
;
2315 STRV_FOREACH(i
, entry
->initrd
) {
2316 _cleanup_free_ char16_t
*o
= options
;
2318 options
= xasprintf("%ls initrd=%ls", o
, *i
);
2320 options
= xasprintf("initrd=%ls", *i
);
2322 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
2323 err
= root
->Open(root
, &handle
, *i
, EFI_FILE_MODE_READ
, 0);
2324 if (err
!= EFI_SUCCESS
)
2327 _cleanup_free_ EFI_FILE_INFO
*info
= NULL
;
2328 err
= get_file_info(handle
, &info
, NULL
);
2329 if (err
!= EFI_SUCCESS
)
2332 if (info
->FileSize
== 0) /* Automatically skip over empty files */
2335 size_t new_size
, read_size
= info
->FileSize
;
2336 if (__builtin_add_overflow(size
, read_size
, &new_size
))
2337 return EFI_OUT_OF_RESOURCES
;
2338 initrd
= xrealloc(initrd
, size
, new_size
);
2340 err
= chunked_read(handle
, &read_size
, initrd
+ size
);
2341 if (err
!= EFI_SUCCESS
)
2344 /* Make sure the actual read size is what we expected. */
2345 assert(size
+ read_size
== new_size
);
2349 if (entry
->options
) {
2350 _cleanup_free_ char16_t
*o
= options
;
2351 options
= xasprintf("%ls %ls", o
, entry
->options
);
2354 *ret_options
= TAKE_PTR(options
);
2355 *ret_initrd
= TAKE_PTR(initrd
);
2356 *ret_initrd_size
= size
;
2360 static EFI_STATUS
image_start(
2361 EFI_HANDLE parent_image
,
2362 const ConfigEntry
*entry
) {
2364 _cleanup_(devicetree_cleanup
) struct devicetree_state dtstate
= {};
2365 _cleanup_(unload_imagep
) EFI_HANDLE image
= NULL
;
2366 _cleanup_free_ EFI_DEVICE_PATH
*path
= NULL
;
2371 /* If this loader entry has a special way to boot, try that first. */
2373 (void) entry
->call();
2375 _cleanup_(file_closep
) EFI_FILE
*image_root
= NULL
;
2376 err
= open_volume(entry
->device
, &image_root
);
2377 if (err
!= EFI_SUCCESS
)
2378 return log_error_status(err
, "Error opening root path: %m");
2380 err
= make_file_device_path(entry
->device
, entry
->loader
, &path
);
2381 if (err
!= EFI_SUCCESS
)
2382 return log_error_status(err
, "Error making file device path: %m");
2384 size_t initrd_size
= 0;
2385 _cleanup_free_
void *initrd
= NULL
;
2386 _cleanup_free_ char16_t
*options_initrd
= NULL
;
2387 err
= initrd_prepare(image_root
, entry
, &options_initrd
, &initrd
, &initrd_size
);
2388 if (err
!= EFI_SUCCESS
)
2389 return log_error_status(err
, "Error preparing initrd: %m");
2391 err
= shim_load_image(parent_image
, path
, &image
);
2392 if (err
!= EFI_SUCCESS
)
2393 return log_error_status(err
, "Error loading %ls: %m", entry
->loader
);
2395 /* DTBs are loaded by the kernel before ExitBootServices, and they can be used to map and assign
2396 * arbitrary memory ranges, so skip it when secure boot is enabled as the DTB here is unverified. */
2397 if (entry
->devicetree
&& !secure_boot_enabled()) {
2398 err
= devicetree_install(&dtstate
, image_root
, entry
->devicetree
);
2399 if (err
!= EFI_SUCCESS
)
2400 return log_error_status(err
, "Error loading %ls: %m", entry
->devicetree
);
2403 _cleanup_(cleanup_initrd
) EFI_HANDLE initrd_handle
= NULL
;
2404 err
= initrd_register(initrd
, initrd_size
, &initrd_handle
);
2405 if (err
!= EFI_SUCCESS
)
2406 return log_error_status(err
, "Error registering initrd: %m");
2408 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
;
2409 err
= BS
->HandleProtocol(image
, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL
), (void **) &loaded_image
);
2410 if (err
!= EFI_SUCCESS
)
2411 return log_error_status(err
, "Error getting LoadedImageProtocol handle: %m");
2413 /* If we had to append an initrd= entry to the command line, we have to pass it, and measure
2414 * it. Otherwise, only pass/measure it if it is not implicit anyway (i.e. embedded into the UKI or
2416 char16_t
*options
= options_initrd
?: entry
->options_implied
? NULL
: entry
->options
;
2418 loaded_image
->LoadOptions
= options
;
2419 loaded_image
->LoadOptionsSize
= strsize16(options
);
2421 /* Try to log any options to the TPM, especially to catch manually edited options */
2422 (void) tpm_log_load_options(options
, NULL
);
2425 efivar_set_time_usec(MAKE_GUID_PTR(LOADER
), u
"LoaderTimeExecUSec", 0);
2426 err
= BS
->StartImage(image
, NULL
, NULL
);
2427 graphics_mode(false);
2428 if (err
== EFI_SUCCESS
)
2431 /* Try calling the kernel compat entry point if one exists. */
2432 if (err
== EFI_UNSUPPORTED
&& entry
->type
== LOADER_LINUX
) {
2433 uint32_t compat_address
;
2435 err
= pe_kernel_info(loaded_image
->ImageBase
, &compat_address
);
2436 if (err
!= EFI_SUCCESS
) {
2437 if (err
!= EFI_UNSUPPORTED
)
2438 return log_error_status(err
, "Error finding kernel compat entry address: %m");
2439 } else if (compat_address
> 0) {
2440 EFI_IMAGE_ENTRY_POINT kernel_entry
=
2441 (EFI_IMAGE_ENTRY_POINT
) ((uint8_t *) loaded_image
->ImageBase
+ compat_address
);
2443 err
= kernel_entry(image
, ST
);
2444 graphics_mode(false);
2445 if (err
== EFI_SUCCESS
)
2448 err
= EFI_UNSUPPORTED
;
2451 return log_error_status(err
, "Failed to execute %ls (%ls): %m", entry
->title_show
, entry
->loader
);
2454 static void config_free(Config
*config
) {
2456 for (size_t i
= 0; i
< config
->n_entries
; i
++)
2457 config_entry_free(config
->entries
[i
]);
2458 free(config
->entries
);
2459 free(config
->entry_default_config
);
2460 free(config
->entry_default_efivar
);
2461 free(config
->entry_oneshot
);
2462 free(config
->entry_saved
);
2465 static void config_write_entries_to_variable(Config
*config
) {
2466 _cleanup_free_
char *buffer
= NULL
;
2472 for (size_t i
= 0; i
< config
->n_entries
; i
++)
2473 sz
+= strsize16(config
->entries
[i
]->id
);
2475 p
= buffer
= xmalloc(sz
);
2477 for (size_t i
= 0; i
< config
->n_entries
; i
++)
2478 p
= mempcpy(p
, config
->entries
[i
]->id
, strsize16(config
->entries
[i
]->id
));
2480 assert(p
== buffer
+ sz
);
2482 /* Store the full list of discovered entries. */
2483 (void) efivar_set_raw(MAKE_GUID_PTR(LOADER
), u
"LoaderEntries", buffer
, sz
, 0);
2486 static void save_selected_entry(const Config
*config
, const ConfigEntry
*entry
) {
2489 assert(entry
->loader
|| !entry
->call
);
2491 /* Always export the selected boot entry to the system in a volatile var. */
2492 (void) efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderEntrySelected", entry
->id
, 0);
2494 /* Do not save or delete if this was a oneshot boot. */
2495 if (streq16(config
->entry_oneshot
, entry
->id
))
2498 if (config
->use_saved_entry_efivar
|| (!config
->entry_default_efivar
&& config
->use_saved_entry
)) {
2499 /* Avoid unnecessary NVRAM writes. */
2500 if (streq16(config
->entry_saved
, entry
->id
))
2503 (void) efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderEntryLastBooted", entry
->id
, EFI_VARIABLE_NON_VOLATILE
);
2505 /* Delete the non-volatile var if not needed. */
2506 (void) efivar_unset(MAKE_GUID_PTR(LOADER
), u
"LoaderEntryLastBooted", EFI_VARIABLE_NON_VOLATILE
);
2509 static EFI_STATUS
secure_boot_discover_keys(Config
*config
, EFI_FILE
*root_dir
) {
2511 _cleanup_(file_closep
) EFI_FILE
*keys_basedir
= NULL
;
2513 if (secure_boot_mode() != SECURE_BOOT_SETUP
)
2516 /* the lack of a 'keys' directory is not fatal and is silently ignored */
2517 err
= open_directory(root_dir
, u
"\\loader\\keys", &keys_basedir
);
2518 if (err
== EFI_NOT_FOUND
)
2520 if (err
!= EFI_SUCCESS
)
2524 _cleanup_free_ EFI_FILE_INFO
*dirent
= NULL
;
2525 size_t dirent_size
= 0;
2526 ConfigEntry
*entry
= NULL
;
2528 err
= readdir(keys_basedir
, &dirent
, &dirent_size
);
2529 if (err
!= EFI_SUCCESS
|| !dirent
)
2532 if (dirent
->FileName
[0] == '.')
2535 if (!FLAGS_SET(dirent
->Attribute
, EFI_FILE_DIRECTORY
))
2538 entry
= xnew(ConfigEntry
, 1);
2539 *entry
= (ConfigEntry
) {
2540 .id
= xasprintf("secure-boot-keys-%ls", dirent
->FileName
),
2541 .title
= xasprintf("Enroll Secure Boot keys: %ls", dirent
->FileName
),
2542 .path
= xasprintf("\\loader\\keys\\%ls", dirent
->FileName
),
2543 .type
= LOADER_SECURE_BOOT_KEYS
,
2547 config_add_entry(config
, entry
);
2549 if (IN_SET(config
->secure_boot_enroll
, ENROLL_IF_SAFE
, ENROLL_FORCE
) &&
2550 strcaseeq16(dirent
->FileName
, u
"auto"))
2551 /* if we auto enroll successfully this call does not return, if it fails we still
2552 * want to add other potential entries to the menu */
2553 secure_boot_enroll_at(root_dir
, entry
->path
, config
->secure_boot_enroll
== ENROLL_FORCE
);
2559 static void export_variables(
2560 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
,
2561 const char16_t
*loaded_image_path
,
2562 uint64_t init_usec
) {
2564 static const uint64_t loader_features
=
2565 EFI_LOADER_FEATURE_CONFIG_TIMEOUT
|
2566 EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT
|
2567 EFI_LOADER_FEATURE_ENTRY_DEFAULT
|
2568 EFI_LOADER_FEATURE_ENTRY_ONESHOT
|
2569 EFI_LOADER_FEATURE_BOOT_COUNTING
|
2570 EFI_LOADER_FEATURE_XBOOTLDR
|
2571 EFI_LOADER_FEATURE_RANDOM_SEED
|
2572 EFI_LOADER_FEATURE_LOAD_DRIVER
|
2573 EFI_LOADER_FEATURE_SORT_KEY
|
2574 EFI_LOADER_FEATURE_SAVED_ENTRY
|
2575 EFI_LOADER_FEATURE_DEVICETREE
|
2576 EFI_LOADER_FEATURE_SECUREBOOT_ENROLL
|
2577 EFI_LOADER_FEATURE_RETAIN_SHIM
|
2580 _cleanup_free_ char16_t
*infostr
= NULL
, *typestr
= NULL
;
2582 assert(loaded_image
);
2584 efivar_set_time_usec(MAKE_GUID_PTR(LOADER
), u
"LoaderTimeInitUSec", init_usec
);
2585 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderInfo", u
"systemd-boot " GIT_VERSION
, 0);
2587 infostr
= xasprintf("%ls %u.%02u", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
2588 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderFirmwareInfo", infostr
, 0);
2590 typestr
= xasprintf("UEFI %u.%02u", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
2591 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderFirmwareType", typestr
, 0);
2593 (void) efivar_set_uint64_le(MAKE_GUID_PTR(LOADER
), u
"LoaderFeatures", loader_features
, 0);
2595 /* the filesystem path to this image, to prevent adding ourselves to the menu */
2596 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderImageIdentifier", loaded_image_path
, 0);
2598 /* export the device path this image is started from */
2599 _cleanup_free_ char16_t
*uuid
= disk_get_part_uuid(loaded_image
->DeviceHandle
);
2601 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderDevicePartUUID", uuid
, 0);
2604 static void config_load_all_entries(
2606 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
,
2607 const char16_t
*loaded_image_path
,
2608 EFI_FILE
*root_dir
) {
2611 assert(loaded_image
);
2614 config_load_defaults(config
, root_dir
);
2616 /* scan /EFI/Linux/ directory */
2617 config_entry_add_unified(config
, loaded_image
->DeviceHandle
, root_dir
);
2619 /* scan /loader/entries/\*.conf files */
2620 config_load_entries(config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
);
2622 /* Similar, but on any XBOOTLDR partition */
2623 config_load_xbootldr(config
, loaded_image
->DeviceHandle
);
2625 /* sort entries after version number */
2626 sort_pointer_array((void **) config
->entries
, config
->n_entries
, (compare_pointer_func_t
) config_entry_compare
);
2628 /* if we find some well-known loaders, add them to the end of the list */
2629 config_entry_add_osx(config
);
2630 config_entry_add_windows(config
, loaded_image
->DeviceHandle
, root_dir
);
2631 config_entry_add_loader_auto(config
, loaded_image
->DeviceHandle
, root_dir
, NULL
,
2632 u
"auto-efi-shell", 's', u
"EFI Shell", u
"\\shell" EFI_MACHINE_TYPE_NAME
".efi");
2633 config_entry_add_loader_auto(config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
,
2634 u
"auto-efi-default", '\0', u
"EFI Default Loader", NULL
);
2636 if (config
->auto_firmware
&& FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI
)) {
2637 ConfigEntry
*entry
= xnew(ConfigEntry
, 1);
2638 *entry
= (ConfigEntry
) {
2639 .id
= xstrdup16(u
"auto-reboot-to-firmware-setup"),
2640 .title
= xstrdup16(u
"Reboot Into Firmware Interface"),
2641 .call
= reboot_into_firmware
,
2645 config_add_entry(config
, entry
);
2648 /* find if secure boot signing keys exist and autoload them if necessary
2649 otherwise creates menu entries so that the user can load them manually
2650 if the secure-boot-enroll variable is set to no (the default), we do not
2651 even search for keys on the ESP */
2652 if (config
->secure_boot_enroll
!= ENROLL_OFF
)
2653 secure_boot_discover_keys(config
, root_dir
);
2655 if (config
->n_entries
== 0)
2658 config_write_entries_to_variable(config
);
2660 config_title_generate(config
);
2662 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
2663 config_default_entry_select(config
);
2666 static EFI_STATUS
discover_root_dir(EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
, EFI_FILE
**ret_dir
) {
2667 if (is_direct_boot(loaded_image
->DeviceHandle
))
2668 return vmm_open(&loaded_image
->DeviceHandle
, ret_dir
);
2670 return open_volume(loaded_image
->DeviceHandle
, ret_dir
);
2673 static EFI_STATUS
run(EFI_HANDLE image
) {
2674 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
;
2675 _cleanup_(file_closep
) EFI_FILE
*root_dir
= NULL
;
2676 _cleanup_(config_free
) Config config
= {};
2677 _cleanup_free_ char16_t
*loaded_image_path
= NULL
;
2682 init_usec
= time_usec();
2684 /* Ask Shim to leave its protocol around, so that the stub can use it to validate PEs.
2685 * By default, Shim uninstalls its protocol when calling StartImage(). */
2686 shim_retain_protocol();
2688 err
= BS
->HandleProtocol(image
, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL
), (void **) &loaded_image
);
2689 if (err
!= EFI_SUCCESS
)
2690 return log_error_status(err
, "Error getting a LoadedImageProtocol handle: %m");
2692 (void) device_path_to_str(loaded_image
->FilePath
, &loaded_image_path
);
2694 export_variables(loaded_image
, loaded_image_path
, init_usec
);
2696 err
= discover_root_dir(loaded_image
, &root_dir
);
2697 if (err
!= EFI_SUCCESS
)
2698 return log_error_status(err
, "Unable to open root directory: %m");
2700 (void) load_drivers(image
, loaded_image
, root_dir
);
2702 config_load_all_entries(&config
, loaded_image
, loaded_image_path
, root_dir
);
2704 if (config
.n_entries
== 0)
2705 return log_error_status(
2707 "No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2709 /* select entry or show menu when key is pressed or timeout is set */
2710 if (config
.force_menu
|| config
.timeout_sec
> 0)
2715 /* Block up to 100ms to give firmware time to get input working. */
2716 err
= console_key_read(&key
, 100 * 1000);
2717 if (err
== EFI_SUCCESS
) {
2718 /* find matching key in config entries */
2719 size_t idx
= entry_lookup_key(&config
, config
.idx_default
, KEYCHAR(key
));
2720 if (idx
!= IDX_INVALID
)
2721 config
.idx_default
= idx
;
2730 entry
= config
.entries
[config
.idx_default
];
2732 efivar_set_time_usec(MAKE_GUID_PTR(LOADER
), u
"LoaderTimeMenuUSec", 0);
2733 if (!menu_run(&config
, &entry
, loaded_image_path
))
2737 /* if auto enrollment is activated, we try to load keys for the given entry. */
2738 if (entry
->type
== LOADER_SECURE_BOOT_KEYS
&& config
.secure_boot_enroll
!= ENROLL_OFF
) {
2739 err
= secure_boot_enroll_at(root_dir
, entry
->path
, /*force=*/ true);
2740 if (err
!= EFI_SUCCESS
)
2745 /* Run special entry like "reboot" now. Those that have a loader
2746 * will be handled by image_start() instead. */
2747 if (entry
->call
&& !entry
->loader
) {
2752 (void) config_entry_bump_counters(entry
);
2753 save_selected_entry(&config
, entry
);
2755 /* Optionally, read a random seed off the ESP and pass it to the OS */
2756 (void) process_random_seed(root_dir
);
2758 err
= image_start(image
, entry
);
2759 if (err
!= EFI_SUCCESS
)
2763 config
.timeout_sec
= 0;
2767 DEFINE_EFI_MAIN_FUNCTION(run
, "systemd-boot", /*wait_for_debugger=*/false);