1 /* SPDX-License-Identifier: LGPL-2.1+ */
12 #include "loader-features.h"
15 #include "random-seed.h"
19 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
20 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
23 /* magic string to find in the binary image */
24 static const char __attribute__((used
)) magic
[] = "#### LoaderInfo: systemd-boot " GIT_VERSION
" ####";
26 static const EFI_GUID global_guid
= EFI_GLOBAL_VARIABLE
;
35 CHAR16
*id
; /* The unique identifier for this entry */
41 enum loader_type type
;
45 EFI_STATUS (*call
)(VOID
);
46 BOOLEAN no_autoselect
;
56 ConfigEntry
**entries
;
59 INTN idx_default_efivar
;
61 UINTN timeout_sec_config
;
62 INTN timeout_sec_efivar
;
63 CHAR16
*entry_default_pattern
;
64 CHAR16
*entry_oneshot
;
68 BOOLEAN auto_firmware
;
71 enum console_mode_change_type console_mode_change
;
72 RandomSeedMode random_seed_mode
;
75 static VOID
cursor_left(UINTN
*cursor
, UINTN
*first
) {
78 else if ((*first
) > 0)
82 static VOID
cursor_right(
88 if ((*cursor
)+1 < x_max
)
90 else if ((*first
) + (*cursor
) < len
)
94 static BOOLEAN
line_edit(
100 _cleanup_freepool_ CHAR16
*line
= NULL
, *print
= NULL
;
101 UINTN size
, len
, first
, cursor
, clear
;
106 size
= StrLen(line_in
) + 1024;
107 line
= AllocatePool(size
* sizeof(CHAR16
));
108 StrCpy(line
, line_in
);
110 print
= AllocatePool((x_max
+1) * sizeof(CHAR16
));
112 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, TRUE
);
127 CopyMem(print
, line
+ first
, i
* sizeof(CHAR16
));
128 while (clear
> 0 && i
< x_max
-1) {
134 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_pos
);
135 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, print
);
136 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
138 err
= console_key_read(&key
, TRUE
);
143 case KEYPRESS(0, SCAN_ESC
, 0):
144 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'c'):
145 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'g'):
146 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('c')):
147 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('g')):
151 case KEYPRESS(0, SCAN_HOME
, 0):
152 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'a'):
153 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('a')):
154 /* beginning-of-line */
159 case KEYPRESS(0, SCAN_END
, 0):
160 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'e'):
161 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('e')):
163 cursor
= len
- first
;
164 if (cursor
+1 >= x_max
) {
166 first
= len
- (x_max
-1);
170 case KEYPRESS(0, SCAN_DOWN
, 0):
171 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'f'):
172 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_RIGHT
, 0):
174 while (line
[first
+ cursor
] == ' ')
175 cursor_right(&cursor
, &first
, x_max
, len
);
176 while (line
[first
+ cursor
] && line
[first
+ cursor
] != ' ')
177 cursor_right(&cursor
, &first
, x_max
, len
);
178 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
181 case KEYPRESS(0, SCAN_UP
, 0):
182 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'b'):
183 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_LEFT
, 0):
185 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
186 cursor_left(&cursor
, &first
);
187 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ')
188 cursor_left(&cursor
, &first
);
190 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ')
191 cursor_left(&cursor
, &first
);
192 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
195 case KEYPRESS(0, SCAN_RIGHT
, 0):
196 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'f'):
197 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('f')):
199 if (first
+ cursor
== len
)
201 cursor_right(&cursor
, &first
, x_max
, len
);
202 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
205 case KEYPRESS(0, SCAN_LEFT
, 0):
206 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'b'):
207 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('b')):
209 cursor_left(&cursor
, &first
);
210 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
213 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'd'):
216 for (i
= first
+ cursor
; i
< len
&& line
[i
] == ' '; i
++)
218 for (; i
< len
&& line
[i
] != ' '; i
++)
221 for (i
= first
+ cursor
; i
+ clear
< len
; i
++)
222 line
[i
] = line
[i
+ clear
];
227 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'w'):
228 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('w')):
229 case KEYPRESS(EFI_ALT_PRESSED
, 0, CHAR_BACKSPACE
):
230 /* backward-kill-word */
232 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
233 cursor_left(&cursor
, &first
);
235 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ') {
236 cursor_left(&cursor
, &first
);
240 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ') {
241 cursor_left(&cursor
, &first
);
244 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
246 for (i
= first
+ cursor
; i
+ clear
< len
; i
++)
247 line
[i
] = line
[i
+ clear
];
252 case KEYPRESS(0, SCAN_DELETE
, 0):
253 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'd'):
254 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('d')):
257 if (first
+ cursor
== len
)
259 for (i
= first
+ cursor
; i
< len
; i
++)
265 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'k'):
266 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('k')):
268 line
[first
+ cursor
] = '\0';
269 clear
= len
- (first
+ cursor
);
270 len
= first
+ cursor
;
273 case KEYPRESS(0, 0, CHAR_LINEFEED
):
274 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
275 if (StrCmp(line
, line_in
) != 0)
276 *line_out
= TAKE_PTR(line
);
281 case KEYPRESS(0, 0, CHAR_BACKSPACE
):
284 if (first
== 0 && cursor
== 0)
286 for (i
= first
+ cursor
-1; i
< len
; i
++)
292 if (cursor
> 0 || first
== 0)
294 /* show full line if it fits */
300 /* jump left to see what we delete */
310 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
311 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
314 for (i
= len
; i
> first
+ cursor
; i
--)
316 line
[first
+ cursor
] = KEYCHAR(key
);
319 if (cursor
+1 < x_max
)
321 else if (first
+ cursor
< len
)
327 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, FALSE
);
331 static UINTN
entry_lookup_key(Config
*config
, UINTN start
, CHAR16 key
) {
337 /* select entry by number key */
338 if (key
>= '1' && key
<= '9') {
340 if (i
> config
->entry_count
)
341 i
= config
->entry_count
;
345 /* find matching key in config entries */
346 for (i
= start
; i
< config
->entry_count
; i
++)
347 if (config
->entries
[i
]->key
== key
)
350 for (i
= 0; i
< start
; i
++)
351 if (config
->entries
[i
]->key
== key
)
357 static VOID
print_status(Config
*config
, CHAR16
*loaded_image_path
) {
360 _cleanup_freepool_ CHAR8
*bootvar
= NULL
, *modevar
= NULL
, *indvar
= NULL
;
361 _cleanup_freepool_ CHAR16
*partstr
= NULL
, *defaultstr
= NULL
;
364 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
365 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
367 Print(L
"systemd-boot version: " GIT_VERSION
"\n");
368 Print(L
"architecture: " EFI_MACHINE_TYPE_NAME
"\n");
369 Print(L
"loaded image: %s\n", loaded_image_path
);
370 Print(L
"UEFI specification: %d.%02d\n", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
371 Print(L
"firmware vendor: %s\n", ST
->FirmwareVendor
);
372 Print(L
"firmware version: %d.%02d\n", ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
374 if (uefi_call_wrapper(ST
->ConOut
->QueryMode
, 4, ST
->ConOut
, ST
->ConOut
->Mode
->Mode
, &x
, &y
) == EFI_SUCCESS
)
375 Print(L
"console size: %d x %d\n", x
, y
);
377 if (efivar_get_raw(&global_guid
, L
"SecureBoot", &bootvar
, &size
) == EFI_SUCCESS
)
378 Print(L
"SecureBoot: %s\n", yes_no(*bootvar
> 0));
380 if (efivar_get_raw(&global_guid
, L
"SetupMode", &modevar
, &size
) == EFI_SUCCESS
)
381 Print(L
"SetupMode: %s\n", *modevar
> 0 ? L
"setup" : L
"user");
384 Print(L
"Shim: present\n");
386 if (efivar_get_raw(&global_guid
, L
"OsIndicationsSupported", &indvar
, &size
) == EFI_SUCCESS
)
387 Print(L
"OsIndicationsSupported: %d\n", (UINT64
)*indvar
);
389 Print(L
"\n--- press key ---\n\n");
390 console_key_read(&key
, TRUE
);
392 Print(L
"timeout: %u\n", config
->timeout_sec
);
393 if (config
->timeout_sec_efivar
>= 0)
394 Print(L
"timeout (EFI var): %d\n", config
->timeout_sec_efivar
);
395 Print(L
"timeout (config): %u\n", config
->timeout_sec_config
);
396 if (config
->entry_default_pattern
)
397 Print(L
"default pattern: '%s'\n", config
->entry_default_pattern
);
398 Print(L
"editor: %s\n", yes_no(config
->editor
));
399 Print(L
"auto-entries: %s\n", yes_no(config
->auto_entries
));
400 Print(L
"auto-firmware: %s\n", yes_no(config
->auto_firmware
));
402 switch (config
->random_seed_mode
) {
403 case RANDOM_SEED_OFF
:
404 Print(L
"random-seed-mode: off\n");
406 case RANDOM_SEED_WITH_SYSTEM_TOKEN
:
407 Print(L
"random-seed-mode: with-system-token\n");
409 case RANDOM_SEED_ALWAYS
:
410 Print(L
"random-seed-mode: always\n");
418 Print(L
"config entry count: %d\n", config
->entry_count
);
419 Print(L
"entry selected idx: %d\n", config
->idx_default
);
420 if (config
->idx_default_efivar
>= 0)
421 Print(L
"entry EFI var idx: %d\n", config
->idx_default_efivar
);
424 if (efivar_get_int(L
"LoaderConfigTimeout", &i
) == EFI_SUCCESS
)
425 Print(L
"LoaderConfigTimeout: %u\n", i
);
427 if (config
->entry_oneshot
)
428 Print(L
"LoaderEntryOneShot: %s\n", config
->entry_oneshot
);
429 if (efivar_get(L
"LoaderDevicePartUUID", &partstr
) == EFI_SUCCESS
)
430 Print(L
"LoaderDevicePartUUID: %s\n", partstr
);
431 if (efivar_get(L
"LoaderEntryDefault", &defaultstr
) == EFI_SUCCESS
)
432 Print(L
"LoaderEntryDefault: %s\n", defaultstr
);
434 Print(L
"\n--- press key ---\n\n");
435 console_key_read(&key
, TRUE
);
437 for (i
= 0; i
< config
->entry_count
; i
++) {
440 if (key
== KEYPRESS(0, SCAN_ESC
, 0) || key
== KEYPRESS(0, 0, 'q'))
443 entry
= config
->entries
[i
];
444 Print(L
"config entry: %d/%d\n", i
+1, config
->entry_count
);
446 Print(L
"id '%s'\n", entry
->id
);
447 Print(L
"title show '%s'\n", entry
->title_show
);
449 Print(L
"title '%s'\n", entry
->title
);
451 Print(L
"version '%s'\n", entry
->version
);
452 if (entry
->machine_id
)
453 Print(L
"machine-id '%s'\n", entry
->machine_id
);
455 EFI_DEVICE_PATH
*device_path
;
457 device_path
= DevicePathFromHandle(entry
->device
);
459 _cleanup_freepool_ CHAR16
*str
;
461 str
= DevicePathToStr(device_path
);
462 Print(L
"device handle '%s'\n", str
);
466 Print(L
"loader '%s'\n", entry
->loader
);
468 Print(L
"options '%s'\n", entry
->options
);
469 Print(L
"auto-select %s\n", yes_no(!entry
->no_autoselect
));
471 Print(L
"internal call yes\n");
473 if (entry
->tries_left
!= (UINTN
) -1)
474 Print(L
"counting boots yes\n"
477 "current path %s\\%s\n"
478 "next path %s\\%s\n",
481 entry
->path
, entry
->current_name
,
482 entry
->path
, entry
->next_name
);
484 Print(L
"\n--- press key ---\n\n");
485 console_key_read(&key
, TRUE
);
488 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
491 static BOOLEAN
menu_run(
493 ConfigEntry
**chosen_entry
,
494 CHAR16
*loaded_image_path
) {
499 UINTN idx_highlight_prev
;
515 BOOLEAN exit
= FALSE
;
517 BOOLEAN wait
= FALSE
;
519 graphics_mode(FALSE
);
520 uefi_call_wrapper(ST
->ConIn
->Reset
, 2, ST
->ConIn
, FALSE
);
521 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, FALSE
);
522 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
524 /* draw a single character to make ClearScreen work on some firmware */
525 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
" ");
527 if (config
->console_mode_change
!= CONSOLE_MODE_KEEP
) {
528 err
= console_set_mode(&config
->console_mode
, config
->console_mode_change
);
529 if (EFI_ERROR(err
)) {
530 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
531 Print(L
"Error switching console mode to %ld: %r.\r", (UINT64
)config
->console_mode
, err
);
534 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
536 err
= uefi_call_wrapper(ST
->ConOut
->QueryMode
, 4, ST
->ConOut
, ST
->ConOut
->Mode
->Mode
, &x_max
, &y_max
);
537 if (EFI_ERROR(err
)) {
542 /* we check 10 times per second for a keystroke */
543 if (config
->timeout_sec
> 0)
544 timeout_remain
= config
->timeout_sec
* 10;
548 idx_highlight
= config
->idx_default
;
549 idx_highlight_prev
= 0;
551 visible_max
= y_max
- 2;
553 if ((UINTN
)config
->idx_default
>= visible_max
)
554 idx_first
= config
->idx_default
-1;
558 idx_last
= idx_first
+ visible_max
-1;
563 /* length of the longest entry */
565 for (i
= 0; i
< config
->entry_count
; i
++) {
568 entry_len
= StrLen(config
->entries
[i
]->title_show
);
569 if (line_width
< entry_len
)
570 line_width
= entry_len
;
572 if (line_width
> x_max
-6)
573 line_width
= x_max
-6;
575 /* offsets to center the entries on the screen */
576 x_start
= (x_max
- (line_width
)) / 2;
577 if (config
->entry_count
< visible_max
)
578 y_start
= ((visible_max
- config
->entry_count
) / 2) + 1;
582 /* menu entries title lines */
583 lines
= AllocatePool(sizeof(CHAR16
*) * config
->entry_count
);
584 for (i
= 0; i
< config
->entry_count
; i
++) {
587 lines
[i
] = AllocatePool(((x_max
+1) * sizeof(CHAR16
)));
588 for (j
= 0; j
< x_start
; j
++)
591 for (k
= 0; config
->entries
[i
]->title_show
[k
] != '\0' && j
< x_max
; j
++, k
++)
592 lines
[i
][j
] = config
->entries
[i
]->title_show
[k
];
594 for (; j
< x_max
; j
++)
596 lines
[i
][x_max
] = '\0';
600 clearline
= AllocatePool((x_max
+1) * sizeof(CHAR16
));
601 for (i
= 0; i
< x_max
; i
++)
609 for (i
= 0; i
< config
->entry_count
; i
++) {
610 if (i
< idx_first
|| i
> idx_last
)
612 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_start
+ i
- idx_first
);
613 if (i
== idx_highlight
)
614 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
615 EFI_BLACK
|EFI_BACKGROUND_LIGHTGRAY
);
617 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
618 EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
619 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, lines
[i
]);
620 if ((INTN
)i
== config
->idx_default_efivar
) {
621 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, x_start
-3, y_start
+ i
- idx_first
);
622 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
"=>");
626 } else if (highlight
) {
627 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_start
+ idx_highlight_prev
- idx_first
);
628 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
629 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, lines
[idx_highlight_prev
]);
630 if ((INTN
)idx_highlight_prev
== config
->idx_default_efivar
) {
631 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, x_start
-3, y_start
+ idx_highlight_prev
- idx_first
);
632 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
"=>");
635 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_start
+ idx_highlight
- idx_first
);
636 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_BLACK
|EFI_BACKGROUND_LIGHTGRAY
);
637 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, lines
[idx_highlight
]);
638 if ((INTN
)idx_highlight
== config
->idx_default_efivar
) {
639 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, x_start
-3, y_start
+ idx_highlight
- idx_first
);
640 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
"=>");
645 if (timeout_remain
> 0) {
647 status
= PoolPrint(L
"Boot in %d sec.", (timeout_remain
+ 5) / 10);
650 /* print status at last line of screen */
656 len
= StrLen(status
);
658 x
= (x_max
- len
) / 2;
661 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
662 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
663 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+ (x_max
- x
));
664 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, status
);
665 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1 + x
+ len
);
668 err
= console_key_read(&key
, wait
);
669 if (EFI_ERROR(err
)) {
670 /* timeout reached */
671 if (timeout_remain
== 0) {
676 /* sleep and update status */
677 if (timeout_remain
> 0) {
678 uefi_call_wrapper(BS
->Stall
, 1, 100 * 1000);
683 /* timeout disabled, wait for next key */
690 /* clear status after keystroke */
694 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
695 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
696 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1);
699 idx_highlight_prev
= idx_highlight
;
702 case KEYPRESS(0, SCAN_UP
, 0):
703 case KEYPRESS(0, 0, 'k'):
704 if (idx_highlight
> 0)
708 case KEYPRESS(0, SCAN_DOWN
, 0):
709 case KEYPRESS(0, 0, 'j'):
710 if (idx_highlight
< config
->entry_count
-1)
714 case KEYPRESS(0, SCAN_HOME
, 0):
715 case KEYPRESS(EFI_ALT_PRESSED
, 0, '<'):
716 if (idx_highlight
> 0) {
722 case KEYPRESS(0, SCAN_END
, 0):
723 case KEYPRESS(EFI_ALT_PRESSED
, 0, '>'):
724 if (idx_highlight
< config
->entry_count
-1) {
726 idx_highlight
= config
->entry_count
-1;
730 case KEYPRESS(0, SCAN_PAGE_UP
, 0):
731 if (idx_highlight
> visible_max
)
732 idx_highlight
-= visible_max
;
737 case KEYPRESS(0, SCAN_PAGE_DOWN
, 0):
738 idx_highlight
+= visible_max
;
739 if (idx_highlight
> config
->entry_count
-1)
740 idx_highlight
= config
->entry_count
-1;
743 case KEYPRESS(0, 0, CHAR_LINEFEED
):
744 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
745 case KEYPRESS(0, SCAN_RIGHT
, 0):
749 case KEYPRESS(0, SCAN_F1
, 0):
750 case KEYPRESS(0, 0, 'h'):
751 case KEYPRESS(0, 0, '?'):
752 status
= StrDuplicate(L
"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
755 case KEYPRESS(0, 0, 'Q'):
760 case KEYPRESS(0, 0, 'd'):
761 if (config
->idx_default_efivar
!= (INTN
)idx_highlight
) {
762 /* store the selected entry in a persistent EFI variable */
763 efivar_set(L
"LoaderEntryDefault", config
->entries
[idx_highlight
]->id
, TRUE
);
764 config
->idx_default_efivar
= idx_highlight
;
765 status
= StrDuplicate(L
"Default boot entry selected.");
767 /* clear the default entry EFI variable */
768 efivar_set(L
"LoaderEntryDefault", NULL
, TRUE
);
769 config
->idx_default_efivar
= -1;
770 status
= StrDuplicate(L
"Default boot entry cleared.");
775 case KEYPRESS(0, 0, '-'):
776 case KEYPRESS(0, 0, 'T'):
777 if (config
->timeout_sec_efivar
> 0) {
778 config
->timeout_sec_efivar
--;
779 efivar_set_int(L
"LoaderConfigTimeout", config
->timeout_sec_efivar
, TRUE
);
780 if (config
->timeout_sec_efivar
> 0)
781 status
= PoolPrint(L
"Menu timeout set to %d sec.", config
->timeout_sec_efivar
);
783 status
= StrDuplicate(L
"Menu disabled. Hold down key at bootup to show menu.");
784 } else if (config
->timeout_sec_efivar
<= 0){
785 config
->timeout_sec_efivar
= -1;
786 efivar_set(L
"LoaderConfigTimeout", NULL
, TRUE
);
787 if (config
->timeout_sec_config
> 0)
788 status
= PoolPrint(L
"Menu timeout of %d sec is defined by configuration file.",
789 config
->timeout_sec_config
);
791 status
= StrDuplicate(L
"Menu disabled. Hold down key at bootup to show menu.");
795 case KEYPRESS(0, 0, '+'):
796 case KEYPRESS(0, 0, 't'):
797 if (config
->timeout_sec_efivar
== -1 && config
->timeout_sec_config
== 0)
798 config
->timeout_sec_efivar
++;
799 config
->timeout_sec_efivar
++;
800 efivar_set_int(L
"LoaderConfigTimeout", config
->timeout_sec_efivar
, TRUE
);
801 if (config
->timeout_sec_efivar
> 0)
802 status
= PoolPrint(L
"Menu timeout set to %d sec.",
803 config
->timeout_sec_efivar
);
805 status
= StrDuplicate(L
"Menu disabled. Hold down key at bootup to show menu.");
808 case KEYPRESS(0, 0, 'e'):
809 /* only the options of configured entries can be edited */
810 if (!config
->editor
|| config
->entries
[idx_highlight
]->type
== LOADER_UNDEFINED
)
812 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
813 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
814 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1);
815 if (line_edit(config
->entries
[idx_highlight
]->options
, &config
->options_edit
, x_max
-1, y_max
-1))
817 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
818 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1);
821 case KEYPRESS(0, 0, 'v'):
822 status
= PoolPrint(L
"systemd-boot " GIT_VERSION
" (" EFI_MACHINE_TYPE_NAME
"), UEFI Specification %d.%02d, Vendor %s %d.%02d",
823 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff,
824 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
827 case KEYPRESS(0, 0, 'P'):
828 print_status(config
, loaded_image_path
);
832 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'l'):
833 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('l')):
838 /* jump with a hotkey directly to a matching entry */
839 idx
= entry_lookup_key(config
, idx_highlight
+1, KEYCHAR(key
));
846 if (idx_highlight
> idx_last
) {
847 idx_last
= idx_highlight
;
848 idx_first
= 1 + idx_highlight
- visible_max
;
850 } else if (idx_highlight
< idx_first
) {
851 idx_first
= idx_highlight
;
852 idx_last
= idx_highlight
+ visible_max
-1;
856 if (!refresh
&& idx_highlight
!= idx_highlight_prev
)
860 *chosen_entry
= config
->entries
[idx_highlight
];
862 for (i
= 0; i
< config
->entry_count
; i
++)
867 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_WHITE
|EFI_BACKGROUND_BLACK
);
868 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
872 static VOID
config_add_entry(Config
*config
, ConfigEntry
*entry
) {
873 if ((config
->entry_count
& 15) == 0) {
876 i
= config
->entry_count
+ 16;
877 if (config
->entry_count
== 0)
878 config
->entries
= AllocatePool(sizeof(VOID
*) * i
);
880 config
->entries
= ReallocatePool(config
->entries
,
881 sizeof(VOID
*) * config
->entry_count
, sizeof(VOID
*) * i
);
883 config
->entries
[config
->entry_count
++] = entry
;
886 static VOID
config_entry_free(ConfigEntry
*entry
) {
891 FreePool(entry
->title_show
);
892 FreePool(entry
->title
);
893 FreePool(entry
->version
);
894 FreePool(entry
->machine_id
);
895 FreePool(entry
->loader
);
896 FreePool(entry
->options
);
897 FreePool(entry
->path
);
898 FreePool(entry
->current_name
);
899 FreePool(entry
->next_name
);
903 static BOOLEAN
is_digit(CHAR16 c
) {
904 return (c
>= '0') && (c
<= '9');
907 static UINTN
c_order(CHAR16 c
) {
912 else if ((c
>= 'a') && (c
<= 'z'))
918 static INTN
str_verscmp(CHAR16
*s1
, CHAR16
*s2
) {
925 while ((*s1
&& !is_digit(*s1
)) || (*s2
&& !is_digit(*s2
))) {
928 order
= c_order(*s1
) - c_order(*s2
);
941 while (is_digit(*s1
) && is_digit(*s2
)) {
957 return StrCmp(os1
, os2
);
960 static CHAR8
*line_get_key_value(
972 line
= content
+ *pos
;
977 while (line
[linelen
] && !strchra((CHAR8
*)"\n\r", line
[linelen
]))
980 /* move pos to next line */
990 line
[linelen
] = '\0';
992 /* remove leading whitespace */
993 while (strchra((CHAR8
*)" \t", *line
)) {
998 /* remove trailing whitespace */
999 while (linelen
> 0 && strchra((CHAR8
*)" \t", line
[linelen
-1]))
1001 line
[linelen
] = '\0';
1006 /* split key/value */
1008 while (*value
&& !strchra(sep
, *value
))
1014 while (*value
&& strchra(sep
, *value
))
1018 if (value
[0] == '"' && line
[linelen
-1] == '"') {
1020 line
[linelen
-1] = '\0';
1028 static VOID
config_defaults_load_from_file(Config
*config
, CHAR8
*content
) {
1033 while ((line
= line_get_key_value(content
, (CHAR8
*)" \t", &pos
, &key
, &value
))) {
1034 if (strcmpa((CHAR8
*)"timeout", key
) == 0) {
1035 _cleanup_freepool_ CHAR16
*s
= NULL
;
1037 s
= stra_to_str(value
);
1038 config
->timeout_sec_config
= Atoi(s
);
1039 config
->timeout_sec
= config
->timeout_sec_config
;
1043 if (strcmpa((CHAR8
*)"default", key
) == 0) {
1044 FreePool(config
->entry_default_pattern
);
1045 config
->entry_default_pattern
= stra_to_str(value
);
1046 StrLwr(config
->entry_default_pattern
);
1050 if (strcmpa((CHAR8
*)"editor", key
) == 0) {
1053 if (EFI_ERROR(parse_boolean(value
, &on
)))
1056 config
->editor
= on
;
1060 if (strcmpa((CHAR8
*)"auto-entries", key
) == 0) {
1063 if (EFI_ERROR(parse_boolean(value
, &on
)))
1066 config
->auto_entries
= on
;
1070 if (strcmpa((CHAR8
*)"auto-firmware", key
) == 0) {
1073 if (EFI_ERROR(parse_boolean(value
, &on
)))
1076 config
->auto_firmware
= on
;
1080 if (strcmpa((CHAR8
*)"console-mode", key
) == 0) {
1081 if (strcmpa((CHAR8
*)"auto", value
) == 0)
1082 config
->console_mode_change
= CONSOLE_MODE_AUTO
;
1083 else if (strcmpa((CHAR8
*)"max", value
) == 0)
1084 config
->console_mode_change
= CONSOLE_MODE_MAX
;
1085 else if (strcmpa((CHAR8
*)"keep", value
) == 0)
1086 config
->console_mode_change
= CONSOLE_MODE_KEEP
;
1088 _cleanup_freepool_ CHAR16
*s
= NULL
;
1090 s
= stra_to_str(value
);
1091 config
->console_mode
= Atoi(s
);
1092 config
->console_mode_change
= CONSOLE_MODE_SET
;
1098 if (strcmpa((CHAR8
*) "random-seed-mode", key
) == 0) {
1099 if (strcmpa((CHAR8
*) "off", value
) == 0)
1100 config
->random_seed_mode
= RANDOM_SEED_OFF
;
1101 else if (strcmpa((CHAR8
*) "with-system-token", value
) == 0)
1102 config
->random_seed_mode
= RANDOM_SEED_WITH_SYSTEM_TOKEN
;
1103 else if (strcmpa((CHAR8
*) "always", value
) == 0)
1104 config
->random_seed_mode
= RANDOM_SEED_ALWAYS
;
1108 if (EFI_ERROR(parse_boolean(value
, &on
)))
1111 config
->random_seed_mode
= on
? RANDOM_SEED_ALWAYS
: RANDOM_SEED_OFF
;
1117 static VOID
config_entry_parse_tries(
1123 UINTN left
= (UINTN
) -1, done
= (UINTN
) -1, factor
= 1, i
, next_left
, next_done
;
1124 _cleanup_freepool_ CHAR16
*prefix
= NULL
;
1127 * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
1128 * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
1129 * which case that counter as assumed to be zero, i.e. the missing part is synonymous to "-0").
1131 * Names we grok, and the series they result in:
1133 * foobar+3.efi → foobar+2-1.efi → foobar+1-2.efi → foobar+0-3.efi → STOP!
1134 * foobar+4-0.efi → foobar+3-1.efi → foobar+2-2.efi → foobar+1-3.efi → foobar+0-4.efi → STOP!
1139 /* Chop off any suffix such as ".conf" or ".efi" */
1141 UINTN suffix_length
;
1143 suffix_length
= StrLen(suffix
);
1144 if (i
< suffix_length
)
1150 /* Go backwards through the string and parse everything we encounter */
1160 if (left
== (UINTN
) -1) /* didn't read at least one digit for 'left'? */
1163 if (done
== (UINTN
) -1) /* no 'done' counter? If so, it's equivalent to 0 */
1169 if (left
== (UINTN
) -1) /* didn't parse any digit yet? */
1172 if (done
!= (UINTN
) -1) /* already encountered a dash earlier? */
1175 /* So we encountered a dash. This means this counter is of the form +LEFT-DONE. Let's assign
1176 * what we already parsed to 'done', and start fresh for the 'left' part. */
1186 if (left
== (UINTN
) -1)
1187 left
= file
[i
] - '0';
1189 UINTN new_left
, digit
;
1191 digit
= file
[i
] - '0';
1192 if (digit
> (UINTN
) -1 / factor
) /* overflow check */
1195 new_left
= left
+ digit
* factor
;
1196 if (new_left
< left
) /* overflow check */
1199 if (new_left
== (UINTN
) -1) /* don't allow us to be confused */
1203 new_factor
= factor
* 10;
1204 if (new_factor
< factor
) /* overflow check */
1207 factor
= new_factor
;
1217 entry
->tries_left
= left
;
1218 entry
->tries_done
= done
;
1220 entry
->path
= StrDuplicate(path
);
1221 entry
->current_name
= StrDuplicate(file
);
1223 next_left
= left
<= 0 ? 0 : left
- 1;
1224 next_done
= done
>= (UINTN
) -2 ? (UINTN
) -2 : done
+ 1;
1226 prefix
= StrDuplicate(file
);
1229 entry
->next_name
= PoolPrint(L
"%s+%u-%u%s", prefix
, next_left
, next_done
, suffix
?: L
"");
1232 static VOID
config_entry_bump_counters(
1234 EFI_FILE_HANDLE root_dir
) {
1236 _cleanup_freepool_ CHAR16
* old_path
= NULL
, *new_path
= NULL
;
1237 _cleanup_(FileHandleClosep
) EFI_FILE_HANDLE handle
= NULL
;
1238 static EFI_GUID EfiFileInfoGuid
= EFI_FILE_INFO_ID
;
1239 _cleanup_freepool_ EFI_FILE_INFO
*file_info
= NULL
;
1240 UINTN file_info_size
, a
, b
;
1243 if (entry
->tries_left
== (UINTN
) -1)
1246 if (!entry
->path
|| !entry
->current_name
|| !entry
->next_name
)
1249 old_path
= PoolPrint(L
"%s\\%s", entry
->path
, entry
->current_name
);
1251 r
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &handle
, old_path
, EFI_FILE_MODE_READ
|EFI_FILE_MODE_WRITE
, 0ULL);
1255 a
= StrLen(entry
->current_name
);
1256 b
= StrLen(entry
->next_name
);
1258 file_info_size
= OFFSETOF(EFI_FILE_INFO
, FileName
) + (a
> b
? a
: b
) + 1;
1261 file_info
= AllocatePool(file_info_size
);
1263 r
= uefi_call_wrapper(handle
->GetInfo
, 4, handle
, &EfiFileInfoGuid
, &file_info_size
, file_info
);
1267 if (r
!= EFI_BUFFER_TOO_SMALL
|| file_info_size
* 2 < file_info_size
) {
1268 Print(L
"\nFailed to get file info for '%s': %r\n", old_path
, r
);
1269 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1273 file_info_size
*= 2;
1274 FreePool(file_info
);
1277 /* And rename the file */
1278 StrCpy(file_info
->FileName
, entry
->next_name
);
1279 r
= uefi_call_wrapper(handle
->SetInfo
, 4, handle
, &EfiFileInfoGuid
, file_info_size
, file_info
);
1281 Print(L
"\nFailed to rename '%s' to '%s', ignoring: %r\n", old_path
, entry
->next_name
, r
);
1282 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1286 /* Flush everything to disk, just in case… */
1287 (void) uefi_call_wrapper(handle
->Flush
, 1, handle
);
1289 /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
1291 new_path
= PoolPrint(L
"%s\\%s", entry
->path
, entry
->next_name
);
1292 efivar_set(L
"LoaderBootCountPath", new_path
, FALSE
);
1294 /* If the file we just renamed is the loader path, then let's update that. */
1295 if (StrCmp(entry
->loader
, old_path
) == 0) {
1296 FreePool(entry
->loader
);
1297 entry
->loader
= TAKE_PTR(new_path
);
1301 static VOID
config_entry_add_from_file(
1308 CHAR16
*loaded_image_path
) {
1315 EFI_FILE_HANDLE handle
;
1316 _cleanup_freepool_ CHAR16
*initrd
= NULL
;
1318 entry
= AllocatePool(sizeof(ConfigEntry
));
1320 *entry
= (ConfigEntry
) {
1321 .tries_done
= (UINTN
) -1,
1322 .tries_left
= (UINTN
) -1,
1325 while ((line
= line_get_key_value(content
, (CHAR8
*)" \t", &pos
, &key
, &value
))) {
1326 if (strcmpa((CHAR8
*)"title", key
) == 0) {
1327 FreePool(entry
->title
);
1328 entry
->title
= stra_to_str(value
);
1332 if (strcmpa((CHAR8
*)"version", key
) == 0) {
1333 FreePool(entry
->version
);
1334 entry
->version
= stra_to_str(value
);
1338 if (strcmpa((CHAR8
*)"machine-id", key
) == 0) {
1339 FreePool(entry
->machine_id
);
1340 entry
->machine_id
= stra_to_str(value
);
1344 if (strcmpa((CHAR8
*)"linux", key
) == 0) {
1345 FreePool(entry
->loader
);
1346 entry
->type
= LOADER_LINUX
;
1347 entry
->loader
= stra_to_path(value
);
1352 if (strcmpa((CHAR8
*)"efi", key
) == 0) {
1353 entry
->type
= LOADER_EFI
;
1354 FreePool(entry
->loader
);
1355 entry
->loader
= stra_to_path(value
);
1357 /* do not add an entry for ourselves */
1358 if (loaded_image_path
&& StriCmp(entry
->loader
, loaded_image_path
) == 0) {
1359 entry
->type
= LOADER_UNDEFINED
;
1365 if (strcmpa((CHAR8
*)"architecture", key
) == 0) {
1366 /* do not add an entry for an EFI image of architecture not matching with that of the image */
1367 if (strcmpa((CHAR8
*)EFI_MACHINE_TYPE_NAME
, value
) != 0) {
1368 entry
->type
= LOADER_UNDEFINED
;
1374 if (strcmpa((CHAR8
*)"initrd", key
) == 0) {
1375 _cleanup_freepool_ CHAR16
*new = NULL
;
1377 new = stra_to_path(value
);
1381 s
= PoolPrint(L
"%s initrd=%s", initrd
, new);
1385 initrd
= PoolPrint(L
"initrd=%s", new);
1390 if (strcmpa((CHAR8
*)"options", key
) == 0) {
1391 _cleanup_freepool_ CHAR16
*new = NULL
;
1393 new = stra_to_str(value
);
1394 if (entry
->options
) {
1397 s
= PoolPrint(L
"%s %s", entry
->options
, new);
1398 FreePool(entry
->options
);
1401 entry
->options
= TAKE_PTR(new);
1407 if (entry
->type
== LOADER_UNDEFINED
) {
1408 config_entry_free(entry
);
1412 /* check existence */
1413 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &handle
, entry
->loader
, EFI_FILE_MODE_READ
, 0ULL);
1414 if (EFI_ERROR(err
)) {
1415 config_entry_free(entry
);
1418 uefi_call_wrapper(handle
->Close
, 1, handle
);
1420 /* add initrd= to options */
1421 if (entry
->type
== LOADER_LINUX
&& initrd
) {
1422 if (entry
->options
) {
1425 s
= PoolPrint(L
"%s %s", initrd
, entry
->options
);
1426 FreePool(entry
->options
);
1429 entry
->options
= TAKE_PTR(initrd
);
1432 entry
->device
= device
;
1433 entry
->id
= StrDuplicate(file
);
1436 config_add_entry(config
, entry
);
1438 config_entry_parse_tries(entry
, path
, file
, L
".conf");
1441 static VOID
config_load_defaults(Config
*config
, EFI_FILE
*root_dir
) {
1442 _cleanup_freepool_ CHAR8
*content
= NULL
;
1446 *config
= (Config
) {
1448 .auto_entries
= TRUE
,
1449 .auto_firmware
= TRUE
,
1450 .random_seed_mode
= RANDOM_SEED_WITH_SYSTEM_TOKEN
,
1453 err
= file_read(root_dir
, L
"\\loader\\loader.conf", 0, 0, &content
, NULL
);
1454 if (!EFI_ERROR(err
))
1455 config_defaults_load_from_file(config
, content
);
1457 err
= efivar_get_int(L
"LoaderConfigTimeout", &sec
);
1458 if (!EFI_ERROR(err
)) {
1459 config
->timeout_sec_efivar
= sec
> INTN_MAX
? INTN_MAX
: sec
;
1460 config
->timeout_sec
= sec
;
1462 config
->timeout_sec_efivar
= -1;
1464 err
= efivar_get_int(L
"LoaderConfigTimeoutOneShot", &sec
);
1465 if (!EFI_ERROR(err
)) {
1466 /* Unset variable now, after all it's "one shot". */
1467 (void) efivar_set(L
"LoaderConfigTimeoutOneShot", NULL
, TRUE
);
1469 config
->timeout_sec
= sec
;
1470 config
->force_menu
= TRUE
; /* force the menu when this is set */
1474 static VOID
config_load_entries(
1478 CHAR16
*loaded_image_path
) {
1480 EFI_FILE_HANDLE entries_dir
;
1483 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &entries_dir
, L
"\\loader\\entries", EFI_FILE_MODE_READ
, 0ULL);
1484 if (!EFI_ERROR(err
)) {
1489 _cleanup_freepool_ CHAR8
*content
= NULL
;
1492 bufsize
= sizeof(buf
);
1493 err
= uefi_call_wrapper(entries_dir
->Read
, 3, entries_dir
, &bufsize
, buf
);
1494 if (bufsize
== 0 || EFI_ERROR(err
))
1497 f
= (EFI_FILE_INFO
*) buf
;
1498 if (f
->FileName
[0] == '.')
1500 if (f
->Attribute
& EFI_FILE_DIRECTORY
)
1503 len
= StrLen(f
->FileName
);
1506 if (StriCmp(f
->FileName
+ len
- 5, L
".conf") != 0)
1508 if (StrnCmp(f
->FileName
, L
"auto-", 5) == 0)
1511 err
= file_read(entries_dir
, f
->FileName
, 0, 0, &content
, NULL
);
1512 if (!EFI_ERROR(err
))
1513 config_entry_add_from_file(config
, device
, root_dir
, L
"\\loader\\entries", f
->FileName
, content
, loaded_image_path
);
1515 uefi_call_wrapper(entries_dir
->Close
, 1, entries_dir
);
1519 static INTN
config_entry_compare(ConfigEntry
*a
, ConfigEntry
*b
) {
1522 /* Order entries that have no tries left to the end of the list */
1523 if (a
->tries_left
!= 0 && b
->tries_left
== 0)
1525 if (a
->tries_left
== 0 && b
->tries_left
!= 0)
1528 r
= str_verscmp(a
->id
, b
->id
);
1532 if (a
->tries_left
== (UINTN
) -1 ||
1533 b
->tries_left
== (UINTN
) -1)
1536 /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
1537 if (a
->tries_left
> b
->tries_left
)
1539 if (a
->tries_left
< b
->tries_left
)
1542 /* If they have the same number of tries left, then let the one win which was tried fewer times so far */
1543 if (a
->tries_done
< b
->tries_done
)
1545 if (a
->tries_done
> b
->tries_done
)
1551 static VOID
config_sort_entries(Config
*config
) {
1554 for (i
= 1; i
< config
->entry_count
; i
++) {
1559 for (k
= 0; k
< config
->entry_count
- i
; k
++) {
1562 if (config_entry_compare(config
->entries
[k
], config
->entries
[k
+1]) <= 0)
1565 entry
= config
->entries
[k
];
1566 config
->entries
[k
] = config
->entries
[k
+1];
1567 config
->entries
[k
+1] = entry
;
1575 static INTN
config_entry_find(Config
*config
, CHAR16
*id
) {
1578 for (i
= 0; i
< config
->entry_count
; i
++)
1579 if (StrCmp(config
->entries
[i
]->id
, id
) == 0)
1585 static VOID
config_default_entry_select(Config
*config
) {
1586 _cleanup_freepool_ CHAR16
*entry_oneshot
= NULL
, *entry_default
= NULL
;
1591 * The EFI variable to specify a boot entry for the next, and only the
1592 * next reboot. The variable is always cleared directly after it is read.
1594 err
= efivar_get(L
"LoaderEntryOneShot", &entry_oneshot
);
1595 if (!EFI_ERROR(err
)) {
1597 config
->entry_oneshot
= StrDuplicate(entry_oneshot
);
1598 efivar_set(L
"LoaderEntryOneShot", NULL
, TRUE
);
1600 i
= config_entry_find(config
, entry_oneshot
);
1602 config
->idx_default
= i
;
1608 * The EFI variable to select the default boot entry overrides the
1609 * configured pattern. The variable can be set and cleared by pressing
1610 * the 'd' key in the loader selection menu, the entry is marked with
1613 err
= efivar_get(L
"LoaderEntryDefault", &entry_default
);
1614 if (!EFI_ERROR(err
)) {
1616 i
= config_entry_find(config
, entry_default
);
1618 config
->idx_default
= i
;
1619 config
->idx_default_efivar
= i
;
1623 config
->idx_default_efivar
= -1;
1625 if (config
->entry_count
== 0)
1629 * Match the pattern from the end of the list to the start, find last
1630 * entry (largest number) matching the given pattern.
1632 if (config
->entry_default_pattern
) {
1633 i
= config
->entry_count
;
1635 if (config
->entries
[i
]->no_autoselect
)
1637 if (MetaiMatch(config
->entries
[i
]->id
, config
->entry_default_pattern
)) {
1638 config
->idx_default
= i
;
1644 /* select the last suitable entry */
1645 i
= config
->entry_count
;
1647 if (config
->entries
[i
]->no_autoselect
)
1649 config
->idx_default
= i
;
1653 /* no entry found */
1654 config
->idx_default
= -1;
1657 static BOOLEAN
find_nonunique(ConfigEntry
**entries
, UINTN entry_count
) {
1658 BOOLEAN non_unique
= FALSE
;
1661 for (i
= 0; i
< entry_count
; i
++)
1662 entries
[i
]->non_unique
= FALSE
;
1664 for (i
= 0; i
< entry_count
; i
++)
1665 for (k
= 0; k
< entry_count
; k
++) {
1668 if (StrCmp(entries
[i
]->title_show
, entries
[k
]->title_show
) != 0)
1671 non_unique
= entries
[i
]->non_unique
= entries
[k
]->non_unique
= TRUE
;
1677 /* generate a unique title, avoiding non-distinguishable menu entries */
1678 static VOID
config_title_generate(Config
*config
) {
1682 for (i
= 0; i
< config
->entry_count
; i
++) {
1685 FreePool(config
->entries
[i
]->title_show
);
1686 title
= config
->entries
[i
]->title
;
1688 title
= config
->entries
[i
]->id
;
1689 config
->entries
[i
]->title_show
= StrDuplicate(title
);
1692 if (!find_nonunique(config
->entries
, config
->entry_count
))
1695 /* add version to non-unique titles */
1696 for (i
= 0; i
< config
->entry_count
; i
++) {
1699 if (!config
->entries
[i
]->non_unique
)
1701 if (!config
->entries
[i
]->version
)
1704 s
= PoolPrint(L
"%s (%s)", config
->entries
[i
]->title_show
, config
->entries
[i
]->version
);
1705 FreePool(config
->entries
[i
]->title_show
);
1706 config
->entries
[i
]->title_show
= s
;
1709 if (!find_nonunique(config
->entries
, config
->entry_count
))
1712 /* add machine-id to non-unique titles */
1713 for (i
= 0; i
< config
->entry_count
; i
++) {
1715 _cleanup_freepool_ CHAR16
*m
= NULL
;
1717 if (!config
->entries
[i
]->non_unique
)
1719 if (!config
->entries
[i
]->machine_id
)
1722 m
= StrDuplicate(config
->entries
[i
]->machine_id
);
1724 s
= PoolPrint(L
"%s (%s)", config
->entries
[i
]->title_show
, m
);
1725 FreePool(config
->entries
[i
]->title_show
);
1726 config
->entries
[i
]->title_show
= s
;
1729 if (!find_nonunique(config
->entries
, config
->entry_count
))
1732 /* add file name to non-unique titles */
1733 for (i
= 0; i
< config
->entry_count
; i
++) {
1736 if (!config
->entries
[i
]->non_unique
)
1738 s
= PoolPrint(L
"%s (%s)", config
->entries
[i
]->title_show
, config
->entries
[i
]->id
);
1739 FreePool(config
->entries
[i
]->title_show
);
1740 config
->entries
[i
]->title_show
= s
;
1741 config
->entries
[i
]->non_unique
= FALSE
;
1745 static BOOLEAN
config_entry_add_call(
1749 EFI_STATUS (*call
)(VOID
)) {
1753 entry
= AllocatePool(sizeof(ConfigEntry
));
1754 *entry
= (ConfigEntry
) {
1755 .id
= StrDuplicate(id
),
1756 .title
= StrDuplicate(title
),
1758 .no_autoselect
= TRUE
,
1759 .tries_done
= (UINTN
) -1,
1760 .tries_left
= (UINTN
) -1,
1763 config_add_entry(config
, entry
);
1767 static ConfigEntry
*config_entry_add_loader(
1770 enum loader_type type
,
1779 entry
= AllocatePool(sizeof(ConfigEntry
));
1780 *entry
= (ConfigEntry
) {
1782 .title
= StrDuplicate(title
),
1783 .version
= StrDuplicate(version
),
1785 .loader
= StrDuplicate(loader
),
1786 .id
= StrDuplicate(id
),
1788 .tries_done
= (UINTN
) -1,
1789 .tries_left
= (UINTN
) -1,
1794 config_add_entry(config
, entry
);
1798 static BOOLEAN
config_entry_add_loader_auto(
1802 CHAR16
*loaded_image_path
,
1808 EFI_FILE_HANDLE handle
;
1812 if (!config
->auto_entries
)
1815 /* do not add an entry for ourselves */
1816 if (loaded_image_path
) {
1818 _cleanup_freepool_ CHAR8
*content
= NULL
;
1820 if (StriCmp(loader
, loaded_image_path
) == 0)
1823 /* look for systemd-boot magic string */
1824 err
= file_read(root_dir
, loader
, 0, 100*1024, &content
, &len
);
1825 if (!EFI_ERROR(err
)) {
1826 CHAR8
*start
= content
;
1827 CHAR8
*last
= content
+ len
- sizeof(magic
) - 1;
1829 for (; start
<= last
; start
++)
1830 if (start
[0] == magic
[0] && CompareMem(start
, magic
, sizeof(magic
) - 1) == 0)
1835 /* check existence */
1836 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &handle
, loader
, EFI_FILE_MODE_READ
, 0ULL);
1839 uefi_call_wrapper(handle
->Close
, 1, handle
);
1841 entry
= config_entry_add_loader(config
, device
, LOADER_UNDEFINED
, id
, key
, title
, loader
, NULL
);
1845 /* do not boot right away into auto-detected entries */
1846 entry
->no_autoselect
= TRUE
;
1851 static VOID
config_entry_add_osx(Config
*config
) {
1853 UINTN handle_count
= 0;
1854 _cleanup_freepool_ EFI_HANDLE
*handles
= NULL
;
1856 if (!config
->auto_entries
)
1859 err
= LibLocateHandle(ByProtocol
, &FileSystemProtocol
, NULL
, &handle_count
, &handles
);
1860 if (!EFI_ERROR(err
)) {
1863 for (i
= 0; i
< handle_count
; i
++) {
1867 root
= LibOpenRoot(handles
[i
]);
1870 found
= config_entry_add_loader_auto(config
, handles
[i
], root
, NULL
, L
"auto-osx", 'a', L
"macOS",
1871 L
"\\System\\Library\\CoreServices\\boot.efi");
1872 uefi_call_wrapper(root
->Close
, 1, root
);
1879 static VOID
config_entry_add_linux(
1882 EFI_FILE
*root_dir
) {
1884 EFI_FILE_HANDLE linux_dir
;
1888 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &linux_dir
, L
"\\EFI\\Linux", EFI_FILE_MODE_READ
, 0ULL);
1894 UINTN bufsize
= sizeof buf
;
1896 CHAR8
*sections
[] = {
1898 (CHAR8
*)".cmdline",
1901 UINTN offs
[ELEMENTSOF(sections
)-1] = {};
1902 UINTN szs
[ELEMENTSOF(sections
)-1] = {};
1903 UINTN addrs
[ELEMENTSOF(sections
)-1] = {};
1904 CHAR8
*content
= NULL
;
1909 CHAR16
*os_name_pretty
= NULL
;
1910 CHAR16
*os_name
= NULL
;
1911 CHAR16
*os_id
= NULL
;
1912 CHAR16
*os_version
= NULL
;
1913 CHAR16
*os_version_id
= NULL
;
1914 CHAR16
*os_build_id
= NULL
;
1916 err
= uefi_call_wrapper(linux_dir
->Read
, 3, linux_dir
, &bufsize
, buf
);
1917 if (bufsize
== 0 || EFI_ERROR(err
))
1920 f
= (EFI_FILE_INFO
*) buf
;
1921 if (f
->FileName
[0] == '.')
1923 if (f
->Attribute
& EFI_FILE_DIRECTORY
)
1925 len
= StrLen(f
->FileName
);
1928 if (StriCmp(f
->FileName
+ len
- 4, L
".efi") != 0)
1930 if (StrnCmp(f
->FileName
, L
"auto-", 5) == 0)
1933 /* look for .osrel and .cmdline sections in the .efi binary */
1934 err
= pe_file_locate_sections(linux_dir
, f
->FileName
, sections
, addrs
, offs
, szs
);
1938 err
= file_read(linux_dir
, f
->FileName
, offs
[0], szs
[0], &content
, NULL
);
1942 /* read properties from the embedded os-release file */
1943 while ((line
= line_get_key_value(content
, (CHAR8
*)"=", &pos
, &key
, &value
))) {
1944 if (strcmpa((CHAR8
*)"PRETTY_NAME", key
) == 0) {
1945 FreePool(os_name_pretty
);
1946 os_name_pretty
= stra_to_str(value
);
1950 if (strcmpa((CHAR8
*)"NAME", key
) == 0) {
1952 os_name
= stra_to_str(value
);
1956 if (strcmpa((CHAR8
*)"ID", key
) == 0) {
1958 os_id
= stra_to_str(value
);
1962 if (strcmpa((CHAR8
*)"VERSION", key
) == 0) {
1963 FreePool(os_version
);
1964 os_version
= stra_to_str(value
);
1968 if (strcmpa((CHAR8
*)"VERSION_ID", key
) == 0) {
1969 FreePool(os_version_id
);
1970 os_version_id
= stra_to_str(value
);
1974 if (strcmpa((CHAR8
*)"BUILD_ID", key
) == 0) {
1975 FreePool(os_build_id
);
1976 os_build_id
= stra_to_str(value
);
1981 if ((os_name_pretty
|| os_name
) && os_id
&& (os_version
|| os_version_id
|| os_build_id
)) {
1982 _cleanup_freepool_ CHAR16
*path
= NULL
;
1984 path
= PoolPrint(L
"\\EFI\\Linux\\%s", f
->FileName
);
1986 entry
= config_entry_add_loader(config
, device
, LOADER_LINUX
, f
->FileName
, 'l',
1987 os_name_pretty
? : (os_name
? : os_id
), path
,
1988 os_version
? : (os_version_id
? : os_build_id
));
1993 /* read the embedded cmdline file */
1994 err
= file_read(linux_dir
, f
->FileName
, offs
[1], szs
[1], &content
, NULL
);
1995 if (!EFI_ERROR(err
)) {
1997 /* chomp the newline */
1998 if (content
[szs
[1]-1] == '\n')
1999 content
[szs
[1]-1] = '\0';
2001 entry
->options
= stra_to_str(content
);
2004 config_entry_parse_tries(entry
, L
"\\EFI\\Linux", f
->FileName
, L
".efi");
2007 FreePool(os_name_pretty
);
2010 FreePool(os_version
);
2011 FreePool(os_version_id
);
2012 FreePool(os_build_id
);
2016 uefi_call_wrapper(linux_dir
->Close
, 1, linux_dir
);
2019 /* Note that this is in GUID format, i.e. the first 32bit, and the following pair of 16bit are byteswapped. */
2020 static const UINT8 xbootldr_guid
[16] = {
2021 0xff, 0xc2, 0x13, 0xbc, 0xe6, 0x59, 0x62, 0x42, 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72
2024 EFI_DEVICE_PATH
*path_parent(EFI_DEVICE_PATH
*path
, EFI_DEVICE_PATH
*node
) {
2025 EFI_DEVICE_PATH
*parent
;
2028 len
= (UINT8
*) NextDevicePathNode(node
) - (UINT8
*) path
;
2029 parent
= (EFI_DEVICE_PATH
*) AllocatePool(len
+ sizeof(EFI_DEVICE_PATH
));
2030 CopyMem(parent
, path
, len
);
2031 CopyMem((UINT8
*) parent
+ len
, EndDevicePath
, sizeof(EFI_DEVICE_PATH
));
2036 static VOID
config_load_xbootldr(
2038 EFI_HANDLE
*device
) {
2040 EFI_DEVICE_PATH
*partition_path
, *node
, *disk_path
, *copy
;
2041 UINT32 found_partition_number
= (UINT32
) -1;
2042 UINT64 found_partition_start
= (UINT64
) -1;
2043 UINT64 found_partition_size
= (UINT64
) -1;
2044 UINT8 found_partition_signature
[16] = {};
2045 EFI_HANDLE new_device
;
2049 partition_path
= DevicePathFromHandle(device
);
2050 if (!partition_path
)
2053 for (node
= partition_path
; !IsDevicePathEnd(node
); node
= NextDevicePathNode(node
)) {
2054 EFI_HANDLE disk_handle
;
2055 EFI_BLOCK_IO
*block_io
;
2059 /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media
2061 if (DevicePathType(node
) != MESSAGING_DEVICE_PATH
)
2064 /* Determine the device path one level up */
2065 disk_path
= path_parent(partition_path
, node
);
2067 r
= uefi_call_wrapper(BS
->LocateDevicePath
, 3, &BlockIoProtocol
, &p
, &disk_handle
);
2071 r
= uefi_call_wrapper(BS
->HandleProtocol
, 3, disk_handle
, &BlockIoProtocol
, (VOID
**)&block_io
);
2075 /* Filter out some block devices early. (We only care about block devices that aren't
2076 * partitions themselves — we look for GPT partition tables to parse after all —, and only
2077 * those which contain a medium and have at least 2 blocks.) */
2078 if (block_io
->Media
->LogicalPartition
||
2079 !block_io
->Media
->MediaPresent
||
2080 block_io
->Media
->LastBlock
<= 1)
2083 /* Try both copies of the GPT header, in case one is corrupted */
2084 for (nr
= 0; nr
< 2; nr
++) {
2085 _cleanup_freepool_ EFI_PARTITION_ENTRY
* entries
= NULL
;
2087 EFI_PARTITION_TABLE_HEADER gpt_header
;
2088 uint8_t space
[((sizeof(EFI_PARTITION_TABLE_HEADER
) + 511) / 512) * 512];
2089 } gpt_header_buffer
;
2090 const EFI_PARTITION_TABLE_HEADER
*h
= &gpt_header_buffer
.gpt_header
;
2096 /* Read the first copy at LBA 1 */
2099 /* Read the second copy at the very last LBA of this block device */
2100 where
= block_io
->Media
->LastBlock
;
2102 /* Read the GPT header */
2103 r
= uefi_call_wrapper(block_io
->ReadBlocks
, 5,
2105 block_io
->Media
->MediaId
,
2107 sizeof(gpt_header_buffer
), &gpt_header_buffer
);
2111 /* Some superficial validation of the GPT header */
2112 c
= CompareMem(&h
->Header
.Signature
, "EFI PART", sizeof(h
->Header
.Signature
));
2116 if (h
->Header
.HeaderSize
< 92 ||
2117 h
->Header
.HeaderSize
> 512)
2120 if (h
->Header
.Revision
!= 0x00010000U
)
2123 /* Calculate CRC check */
2124 c
= ~crc32_exclude_offset((UINT32
) -1,
2125 (const UINT8
*) &gpt_header_buffer
,
2126 h
->Header
.HeaderSize
,
2127 OFFSETOF(EFI_PARTITION_TABLE_HEADER
, Header
.CRC32
),
2128 sizeof(h
->Header
.CRC32
));
2129 if (c
!= h
->Header
.CRC32
)
2132 if (h
->MyLBA
!= where
)
2135 if (h
->SizeOfPartitionEntry
< sizeof(EFI_PARTITION_ENTRY
))
2138 if (h
->NumberOfPartitionEntries
<= 0 ||
2139 h
->NumberOfPartitionEntries
> 1024)
2142 if (h
->SizeOfPartitionEntry
> UINTN_MAX
/ h
->NumberOfPartitionEntries
) /* overflow check */
2145 /* Now load the GPT entry table */
2146 sz
= ALIGN_TO((UINTN
) h
->SizeOfPartitionEntry
* (UINTN
) h
->NumberOfPartitionEntries
, 512);
2147 entries
= AllocatePool(sz
);
2149 r
= uefi_call_wrapper(block_io
->ReadBlocks
, 5,
2151 block_io
->Media
->MediaId
,
2152 h
->PartitionEntryLBA
,
2157 /* Calculate CRC of entries array, too */
2158 c
= ~crc32((UINT32
) -1, entries
, sz
);
2159 if (c
!= h
->PartitionEntryArrayCRC32
)
2162 for (i
= 0; i
< h
->NumberOfPartitionEntries
; i
++) {
2163 EFI_PARTITION_ENTRY
*entry
;
2165 entry
= (EFI_PARTITION_ENTRY
*) ((UINT8
*) entries
+ h
->SizeOfPartitionEntry
* i
);
2167 if (CompareMem(&entry
->PartitionTypeGUID
, xbootldr_guid
, 16) == 0) {
2170 /* Let's use memcpy(), in case the structs are not aligned (they really should be though) */
2171 CopyMem(&found_partition_start
, &entry
->StartingLBA
, sizeof(found_partition_start
));
2172 CopyMem(&end
, &entry
->EndingLBA
, sizeof(end
));
2174 if (end
< found_partition_start
) /* Bogus? */
2177 found_partition_size
= end
- found_partition_start
+ 1;
2178 CopyMem(found_partition_signature
, &entry
->UniquePartitionGUID
, sizeof(found_partition_signature
));
2180 found_partition_number
= i
+ 1;
2185 break; /* This GPT was fully valid, but we didn't find what we are looking for. This
2186 * means there's no reason to check the second copy of the GPT header */
2190 return; /* Not found */
2193 copy
= DuplicateDevicePath(partition_path
);
2195 /* Patch in the data we found */
2196 for (node
= copy
; !IsDevicePathEnd(node
); node
= NextDevicePathNode(node
)) {
2197 HARDDRIVE_DEVICE_PATH
*hd
;
2199 if (DevicePathType(node
) != MEDIA_DEVICE_PATH
)
2202 if (DevicePathSubType(node
) != MEDIA_HARDDRIVE_DP
)
2205 hd
= (HARDDRIVE_DEVICE_PATH
*) node
;
2206 hd
->PartitionNumber
= found_partition_number
;
2207 hd
->PartitionStart
= found_partition_start
;
2208 hd
->PartitionSize
= found_partition_size
;
2209 CopyMem(hd
->Signature
, found_partition_signature
, sizeof(hd
->Signature
));
2210 hd
->MBRType
= MBR_TYPE_EFI_PARTITION_TABLE_HEADER
;
2211 hd
->SignatureType
= SIGNATURE_TYPE_GUID
;
2214 r
= uefi_call_wrapper(BS
->LocateDevicePath
, 3, &BlockIoProtocol
, ©
, &new_device
);
2218 root_dir
= LibOpenRoot(new_device
);
2222 config_entry_add_linux(config
, new_device
, root_dir
);
2223 config_load_entries(config
, new_device
, root_dir
, NULL
);
2226 static EFI_STATUS
image_start(
2227 EFI_HANDLE parent_image
,
2228 const Config
*config
,
2229 const ConfigEntry
*entry
) {
2232 _cleanup_freepool_ EFI_DEVICE_PATH
*path
= NULL
;
2236 path
= FileDevicePath(entry
->device
, entry
->loader
);
2238 Print(L
"Error getting device path.");
2239 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
2240 return EFI_INVALID_PARAMETER
;
2243 err
= uefi_call_wrapper(BS
->LoadImage
, 6, FALSE
, parent_image
, path
, NULL
, 0, &image
);
2244 if (EFI_ERROR(err
)) {
2245 Print(L
"Error loading %s: %r", entry
->loader
, err
);
2246 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
2250 if (config
->options_edit
)
2251 options
= config
->options_edit
;
2252 else if (entry
->options
)
2253 options
= entry
->options
;
2257 EFI_LOADED_IMAGE
*loaded_image
;
2259 err
= uefi_call_wrapper(BS
->OpenProtocol
, 6, image
, &LoadedImageProtocol
, (VOID
**)&loaded_image
,
2260 parent_image
, NULL
, EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
2261 if (EFI_ERROR(err
)) {
2262 Print(L
"Error getting LoadedImageProtocol handle: %r", err
);
2263 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
2266 loaded_image
->LoadOptions
= options
;
2267 loaded_image
->LoadOptionsSize
= (StrLen(loaded_image
->LoadOptions
)+1) * sizeof(CHAR16
);
2270 /* Try to log any options to the TPM, especially to catch manually edited options */
2271 err
= tpm_log_event(SD_TPM_PCR
,
2272 (EFI_PHYSICAL_ADDRESS
) (UINTN
) loaded_image
->LoadOptions
,
2273 loaded_image
->LoadOptionsSize
, loaded_image
->LoadOptions
);
2274 if (EFI_ERROR(err
)) {
2275 Print(L
"Unable to add image options measurement: %r", err
);
2276 uefi_call_wrapper(BS
->Stall
, 1, 200 * 1000);
2281 efivar_set_time_usec(L
"LoaderTimeExecUSec", 0);
2282 err
= uefi_call_wrapper(BS
->StartImage
, 3, image
, NULL
, NULL
);
2284 uefi_call_wrapper(BS
->UnloadImage
, 1, image
);
2288 static EFI_STATUS
reboot_into_firmware(VOID
) {
2289 _cleanup_freepool_ CHAR8
*b
= NULL
;
2294 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
2296 err
= efivar_get_raw(&global_guid
, L
"OsIndications", &b
, &size
);
2297 if (!EFI_ERROR(err
))
2298 osind
|= (UINT64
)*b
;
2300 err
= efivar_set_raw(&global_guid
, L
"OsIndications", &osind
, sizeof(UINT64
), TRUE
);
2304 err
= uefi_call_wrapper(RT
->ResetSystem
, 4, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2305 Print(L
"Error calling ResetSystem: %r", err
);
2306 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
2310 static VOID
config_free(Config
*config
) {
2313 for (i
= 0; i
< config
->entry_count
; i
++)
2314 config_entry_free(config
->entries
[i
]);
2315 FreePool(config
->entries
);
2316 FreePool(config
->entry_default_pattern
);
2317 FreePool(config
->options_edit
);
2318 FreePool(config
->entry_oneshot
);
2321 static VOID
config_write_entries_to_variable(Config
*config
) {
2322 _cleanup_freepool_ CHAR16
*buffer
= NULL
;
2326 for (i
= 0; i
< config
->entry_count
; i
++)
2327 sz
+= StrLen(config
->entries
[i
]->id
) + 1;
2329 p
= buffer
= AllocatePool(sz
* sizeof(CHAR16
));
2331 for (i
= 0; i
< config
->entry_count
; i
++) {
2334 l
= StrLen(config
->entries
[i
]->id
) + 1;
2335 CopyMem(p
, config
->entries
[i
]->id
, l
* sizeof(CHAR16
));
2340 /* Store the full list of discovered entries. */
2341 (void) efivar_set_raw(&loader_guid
, L
"LoaderEntries", buffer
, (UINT8
*) p
- (UINT8
*) buffer
, FALSE
);
2344 EFI_STATUS
efi_main(EFI_HANDLE image
, EFI_SYSTEM_TABLE
*sys_table
) {
2345 static const UINT64 loader_features
=
2346 EFI_LOADER_FEATURE_CONFIG_TIMEOUT
|
2347 EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT
|
2348 EFI_LOADER_FEATURE_ENTRY_DEFAULT
|
2349 EFI_LOADER_FEATURE_ENTRY_ONESHOT
|
2350 EFI_LOADER_FEATURE_BOOT_COUNTING
|
2351 EFI_LOADER_FEATURE_XBOOTLDR
|
2352 EFI_LOADER_FEATURE_RANDOM_SEED
|
2355 _cleanup_freepool_ CHAR16
*infostr
= NULL
, *typestr
= NULL
;
2358 EFI_LOADED_IMAGE
*loaded_image
;
2360 CHAR16
*loaded_image_path
;
2364 BOOLEAN menu
= FALSE
;
2367 InitializeLib(image
, sys_table
);
2368 init_usec
= time_usec();
2369 efivar_set_time_usec(L
"LoaderTimeInitUSec", init_usec
);
2370 efivar_set(L
"LoaderInfo", L
"systemd-boot " GIT_VERSION
, FALSE
);
2372 infostr
= PoolPrint(L
"%s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
2373 efivar_set(L
"LoaderFirmwareInfo", infostr
, FALSE
);
2375 typestr
= PoolPrint(L
"UEFI %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
2376 efivar_set(L
"LoaderFirmwareType", typestr
, FALSE
);
2378 (void) efivar_set_raw(&loader_guid
, L
"LoaderFeatures", &loader_features
, sizeof(loader_features
), FALSE
);
2380 err
= uefi_call_wrapper(BS
->OpenProtocol
, 6, image
, &LoadedImageProtocol
, (VOID
**)&loaded_image
,
2381 image
, NULL
, EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
2382 if (EFI_ERROR(err
)) {
2383 Print(L
"Error getting a LoadedImageProtocol handle: %r", err
);
2384 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
2388 /* export the device path this image is started from */
2389 if (disk_get_part_uuid(loaded_image
->DeviceHandle
, uuid
) == EFI_SUCCESS
)
2390 efivar_set(L
"LoaderDevicePartUUID", uuid
, FALSE
);
2392 root_dir
= LibOpenRoot(loaded_image
->DeviceHandle
);
2394 Print(L
"Unable to open root directory.");
2395 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
2396 return EFI_LOAD_ERROR
;
2399 if (secure_boot_enabled() && shim_loaded()) {
2400 err
= security_policy_install();
2401 if (EFI_ERROR(err
)) {
2402 Print(L
"Error installing security policy: %r ", err
);
2403 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
2408 /* the filesystem path to this image, to prevent adding ourselves to the menu */
2409 loaded_image_path
= DevicePathToStr(loaded_image
->FilePath
);
2410 efivar_set(L
"LoaderImageIdentifier", loaded_image_path
, FALSE
);
2412 config_load_defaults(&config
, root_dir
);
2414 /* scan /EFI/Linux/ directory */
2415 config_entry_add_linux(&config
, loaded_image
->DeviceHandle
, root_dir
);
2417 /* scan /loader/entries/\*.conf files */
2418 config_load_entries(&config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
);
2420 /* Similar, but on any XBOOTLDR partition */
2421 config_load_xbootldr(&config
, loaded_image
->DeviceHandle
);
2423 /* sort entries after version number */
2424 config_sort_entries(&config
);
2426 /* if we find some well-known loaders, add them to the end of the list */
2427 config_entry_add_loader_auto(&config
, loaded_image
->DeviceHandle
, root_dir
, NULL
,
2428 L
"auto-windows", 'w', L
"Windows Boot Manager", L
"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2429 config_entry_add_loader_auto(&config
, loaded_image
->DeviceHandle
, root_dir
, NULL
,
2430 L
"auto-efi-shell", 's', L
"EFI Shell", L
"\\shell" EFI_MACHINE_TYPE_NAME
".efi");
2431 config_entry_add_loader_auto(&config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
,
2432 L
"auto-efi-default", '\0', L
"EFI Default Loader", L
"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME
".efi");
2433 config_entry_add_osx(&config
);
2435 if (config
.auto_firmware
&& efivar_get_raw(&global_guid
, L
"OsIndicationsSupported", &b
, &size
) == EFI_SUCCESS
) {
2436 UINT64 osind
= (UINT64
)*b
;
2438 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
)
2439 config_entry_add_call(&config
,
2440 L
"auto-reboot-to-firmware-setup",
2441 L
"Reboot Into Firmware Interface",
2442 reboot_into_firmware
);
2446 if (config
.entry_count
== 0) {
2447 Print(L
"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2448 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
2452 config_write_entries_to_variable(&config
);
2454 config_title_generate(&config
);
2456 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
2457 config_default_entry_select(&config
);
2459 /* if no configured entry to select from was found, enable the menu */
2460 if (config
.idx_default
== -1) {
2461 config
.idx_default
= 0;
2462 if (config
.timeout_sec
== 0)
2463 config
.timeout_sec
= 10;
2466 /* select entry or show menu when key is pressed or timeout is set */
2467 if (config
.force_menu
|| config
.timeout_sec
> 0)
2472 err
= console_key_read(&key
, FALSE
);
2474 if (err
== EFI_NOT_READY
) {
2475 uefi_call_wrapper(BS
->Stall
, 1, 100 * 1000);
2476 err
= console_key_read(&key
, FALSE
);
2479 if (!EFI_ERROR(err
)) {
2482 /* find matching key in config entries */
2483 idx
= entry_lookup_key(&config
, config
.idx_default
, KEYCHAR(key
));
2485 config
.idx_default
= idx
;
2494 entry
= config
.entries
[config
.idx_default
];
2496 efivar_set_time_usec(L
"LoaderTimeMenuUSec", 0);
2497 uefi_call_wrapper(BS
->SetWatchdogTimer
, 4, 0, 0x10000, 0, NULL
);
2498 if (!menu_run(&config
, &entry
, loaded_image_path
))
2502 /* run special entry like "reboot" */
2508 config_entry_bump_counters(entry
, root_dir
);
2510 /* Export the selected boot entry to the system */
2511 (VOID
) efivar_set(L
"LoaderEntrySelected", entry
->id
, FALSE
);
2513 /* Optionally, read a random seed off the ESP and pass it to the OS */
2514 (VOID
) process_random_seed(root_dir
, config
.random_seed_mode
);
2516 uefi_call_wrapper(BS
->SetWatchdogTimer
, 4, 5 * 60, 0x10000, 0, NULL
);
2517 err
= image_start(image
, &config
, entry
);
2518 if (EFI_ERROR(err
)) {
2519 graphics_mode(FALSE
);
2520 Print(L
"\nFailed to execute %s (%s): %r\n", entry
->title
, entry
->loader
, err
);
2521 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
2526 config
.timeout_sec
= 0;
2530 FreePool(loaded_image_path
);
2531 config_free(&config
);
2532 uefi_call_wrapper(root_dir
->Close
, 1, root_dir
);
2533 uefi_call_wrapper(BS
->CloseProtocol
, 4, image
, &LoadedImageProtocol
, image
, NULL
);