1 /* SPDX-License-Identifier: LGPL-2.1+ */
15 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
16 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
19 /* magic string to find in the binary image */
20 static const char __attribute__((used
)) magic
[] = "#### LoaderInfo: systemd-boot " PACKAGE_VERSION
" ####";
22 static const EFI_GUID global_guid
= EFI_GLOBAL_VARIABLE
;
31 CHAR16
*id
; /* The identifier for this entry (note that this id is not necessarily unique though!) */
37 enum loader_type type
;
41 EFI_STATUS (*call
)(VOID
);
42 BOOLEAN no_autoselect
;
47 ConfigEntry
**entries
;
50 INTN idx_default_efivar
;
52 UINTN timeout_sec_config
;
53 INTN timeout_sec_efivar
;
54 CHAR16
*entry_default_pattern
;
55 CHAR16
*entry_oneshot
;
59 BOOLEAN auto_firmware
;
61 enum console_mode_change_type console_mode_change
;
64 static VOID
cursor_left(UINTN
*cursor
, UINTN
*first
) {
67 else if ((*first
) > 0)
71 static VOID
cursor_right(
77 if ((*cursor
)+1 < x_max
)
79 else if ((*first
) + (*cursor
) < len
)
83 static BOOLEAN
line_edit(
89 _cleanup_freepool_ CHAR16
*line
= NULL
, *print
= NULL
;
90 UINTN size
, len
, first
, cursor
, clear
;
95 size
= StrLen(line_in
) + 1024;
96 line
= AllocatePool(size
* sizeof(CHAR16
));
97 StrCpy(line
, line_in
);
99 print
= AllocatePool((x_max
+1) * sizeof(CHAR16
));
101 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, TRUE
);
116 CopyMem(print
, line
+ first
, i
* sizeof(CHAR16
));
117 while (clear
> 0 && i
< x_max
-1) {
123 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_pos
);
124 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, print
);
125 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
127 err
= console_key_read(&key
, TRUE
);
132 case KEYPRESS(0, SCAN_ESC
, 0):
133 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'c'):
134 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'g'):
135 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('c')):
136 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('g')):
140 case KEYPRESS(0, SCAN_HOME
, 0):
141 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'a'):
142 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('a')):
143 /* beginning-of-line */
148 case KEYPRESS(0, SCAN_END
, 0):
149 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'e'):
150 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('e')):
152 cursor
= len
- first
;
153 if (cursor
+1 >= x_max
) {
155 first
= len
- (x_max
-1);
159 case KEYPRESS(0, SCAN_DOWN
, 0):
160 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'f'):
161 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_RIGHT
, 0):
163 while (line
[first
+ cursor
] == ' ')
164 cursor_right(&cursor
, &first
, x_max
, len
);
165 while (line
[first
+ cursor
] && line
[first
+ cursor
] != ' ')
166 cursor_right(&cursor
, &first
, x_max
, len
);
167 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
170 case KEYPRESS(0, SCAN_UP
, 0):
171 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'b'):
172 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_LEFT
, 0):
174 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
175 cursor_left(&cursor
, &first
);
176 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ')
177 cursor_left(&cursor
, &first
);
179 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ')
180 cursor_left(&cursor
, &first
);
181 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
184 case KEYPRESS(0, SCAN_RIGHT
, 0):
185 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'f'):
186 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('f')):
188 if (first
+ cursor
== len
)
190 cursor_right(&cursor
, &first
, x_max
, len
);
191 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
194 case KEYPRESS(0, SCAN_LEFT
, 0):
195 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'b'):
196 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('b')):
198 cursor_left(&cursor
, &first
);
199 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
202 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'd'):
205 for (i
= first
+ cursor
; i
< len
&& line
[i
] == ' '; i
++)
207 for (; i
< len
&& line
[i
] != ' '; i
++)
210 for (i
= first
+ cursor
; i
+ clear
< len
; i
++)
211 line
[i
] = line
[i
+ clear
];
216 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'w'):
217 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('w')):
218 case KEYPRESS(EFI_ALT_PRESSED
, 0, CHAR_BACKSPACE
):
219 /* backward-kill-word */
221 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
222 cursor_left(&cursor
, &first
);
224 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ') {
225 cursor_left(&cursor
, &first
);
229 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ') {
230 cursor_left(&cursor
, &first
);
233 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
235 for (i
= first
+ cursor
; i
+ clear
< len
; i
++)
236 line
[i
] = line
[i
+ clear
];
241 case KEYPRESS(0, SCAN_DELETE
, 0):
242 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'd'):
243 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('d')):
246 if (first
+ cursor
== len
)
248 for (i
= first
+ cursor
; i
< len
; i
++)
254 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'k'):
255 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('k')):
257 line
[first
+ cursor
] = '\0';
258 clear
= len
- (first
+ cursor
);
259 len
= first
+ cursor
;
262 case KEYPRESS(0, 0, CHAR_LINEFEED
):
263 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
264 if (StrCmp(line
, line_in
) != 0) {
272 case KEYPRESS(0, 0, CHAR_BACKSPACE
):
275 if (first
== 0 && cursor
== 0)
277 for (i
= first
+ cursor
-1; i
< len
; i
++)
283 if (cursor
> 0 || first
== 0)
285 /* show full line if it fits */
291 /* jump left to see what we delete */
301 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
302 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
305 for (i
= len
; i
> first
+ cursor
; i
--)
307 line
[first
+ cursor
] = KEYCHAR(key
);
310 if (cursor
+1 < x_max
)
312 else if (first
+ cursor
< len
)
318 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, FALSE
);
322 static UINTN
entry_lookup_key(Config
*config
, UINTN start
, CHAR16 key
) {
328 /* select entry by number key */
329 if (key
>= '1' && key
<= '9') {
331 if (i
> config
->entry_count
)
332 i
= config
->entry_count
;
336 /* find matching key in config entries */
337 for (i
= start
; i
< config
->entry_count
; i
++)
338 if (config
->entries
[i
]->key
== key
)
341 for (i
= 0; i
< start
; i
++)
342 if (config
->entries
[i
]->key
== key
)
348 static VOID
print_status(Config
*config
, CHAR16
*loaded_image_path
) {
351 _cleanup_freepool_ CHAR8
*bootvar
= NULL
, *modevar
= NULL
, *indvar
= NULL
;
352 _cleanup_freepool_ CHAR16
*partstr
= NULL
, *defaultstr
= NULL
;
355 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
356 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
358 Print(L
"systemd-boot version: " PACKAGE_VERSION
"\n");
359 Print(L
"architecture: " EFI_MACHINE_TYPE_NAME
"\n");
360 Print(L
"loaded image: %s\n", loaded_image_path
);
361 Print(L
"UEFI specification: %d.%02d\n", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
362 Print(L
"firmware vendor: %s\n", ST
->FirmwareVendor
);
363 Print(L
"firmware version: %d.%02d\n", ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
365 if (uefi_call_wrapper(ST
->ConOut
->QueryMode
, 4, ST
->ConOut
, ST
->ConOut
->Mode
->Mode
, &x
, &y
) == EFI_SUCCESS
)
366 Print(L
"console size: %d x %d\n", x
, y
);
368 if (efivar_get_raw(&global_guid
, L
"SecureBoot", &bootvar
, &size
) == EFI_SUCCESS
)
369 Print(L
"SecureBoot: %s\n", yes_no(*bootvar
> 0));
371 if (efivar_get_raw(&global_guid
, L
"SetupMode", &modevar
, &size
) == EFI_SUCCESS
)
372 Print(L
"SetupMode: %s\n", *modevar
> 0 ? L
"setup" : L
"user");
375 Print(L
"Shim: present\n");
377 if (efivar_get_raw(&global_guid
, L
"OsIndicationsSupported", &indvar
, &size
) == EFI_SUCCESS
)
378 Print(L
"OsIndicationsSupported: %d\n", (UINT64
)*indvar
);
380 Print(L
"\n--- press key ---\n\n");
381 console_key_read(&key
, TRUE
);
383 Print(L
"timeout: %d\n", config
->timeout_sec
);
384 if (config
->timeout_sec_efivar
>= 0)
385 Print(L
"timeout (EFI var): %d\n", config
->timeout_sec_efivar
);
386 Print(L
"timeout (config): %d\n", config
->timeout_sec_config
);
387 if (config
->entry_default_pattern
)
388 Print(L
"default pattern: '%s'\n", config
->entry_default_pattern
);
389 Print(L
"editor: %s\n", yes_no(config
->editor
));
390 Print(L
"auto-entries: %s\n", yes_no(config
->auto_entries
));
391 Print(L
"auto-firmware: %s\n", yes_no(config
->auto_firmware
));
394 Print(L
"config entry count: %d\n", config
->entry_count
);
395 Print(L
"entry selected idx: %d\n", config
->idx_default
);
396 if (config
->idx_default_efivar
>= 0)
397 Print(L
"entry EFI var idx: %d\n", config
->idx_default_efivar
);
400 if (efivar_get_int(L
"LoaderConfigTimeout", &i
) == EFI_SUCCESS
)
401 Print(L
"LoaderConfigTimeout: %d\n", i
);
402 if (config
->entry_oneshot
)
403 Print(L
"LoaderEntryOneShot: %s\n", config
->entry_oneshot
);
404 if (efivar_get(L
"LoaderDevicePartUUID", &partstr
) == EFI_SUCCESS
)
405 Print(L
"LoaderDevicePartUUID: %s\n", partstr
);
406 if (efivar_get(L
"LoaderEntryDefault", &defaultstr
) == EFI_SUCCESS
)
407 Print(L
"LoaderEntryDefault: %s\n", defaultstr
);
409 Print(L
"\n--- press key ---\n\n");
410 console_key_read(&key
, TRUE
);
412 for (i
= 0; i
< config
->entry_count
; i
++) {
415 if (key
== KEYPRESS(0, SCAN_ESC
, 0) || key
== KEYPRESS(0, 0, 'q'))
418 entry
= config
->entries
[i
];
419 Print(L
"config entry: %d/%d\n", i
+1, config
->entry_count
);
421 Print(L
"id '%s'\n", entry
->id
);
422 Print(L
"title show '%s'\n", entry
->title_show
);
424 Print(L
"title '%s'\n", entry
->title
);
426 Print(L
"version '%s'\n", entry
->version
);
427 if (entry
->machine_id
)
428 Print(L
"machine-id '%s'\n", entry
->machine_id
);
430 EFI_DEVICE_PATH
*device_path
;
432 device_path
= DevicePathFromHandle(entry
->device
);
434 _cleanup_freepool_ CHAR16
*str
;
436 str
= DevicePathToStr(device_path
);
437 Print(L
"device handle '%s'\n", str
);
441 Print(L
"loader '%s'\n", entry
->loader
);
443 Print(L
"options '%s'\n", entry
->options
);
444 Print(L
"auto-select %s\n", yes_no(!entry
->no_autoselect
));
446 Print(L
"internal call yes\n");
448 Print(L
"\n--- press key ---\n\n");
449 console_key_read(&key
, TRUE
);
452 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
455 static BOOLEAN
menu_run(
457 ConfigEntry
**chosen_entry
,
458 CHAR16
*loaded_image_path
) {
463 UINTN idx_highlight_prev
;
479 BOOLEAN exit
= FALSE
;
481 BOOLEAN wait
= FALSE
;
482 BOOLEAN cleared_screen
= FALSE
;
484 graphics_mode(FALSE
);
485 uefi_call_wrapper(ST
->ConIn
->Reset
, 2, ST
->ConIn
, FALSE
);
486 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, FALSE
);
487 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
489 /* draw a single character to make ClearScreen work on some firmware */
490 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
" ");
492 if (config
->console_mode_change
!= CONSOLE_MODE_KEEP
) {
493 err
= console_set_mode(&config
->console_mode
, config
->console_mode_change
);
495 cleared_screen
= TRUE
;
499 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
501 if (config
->console_mode_change
!= CONSOLE_MODE_KEEP
&& EFI_ERROR(err
))
502 Print(L
"Error switching console mode to %ld: %r.\r", (UINT64
)config
->console_mode
, err
);
504 err
= uefi_call_wrapper(ST
->ConOut
->QueryMode
, 4, ST
->ConOut
, ST
->ConOut
->Mode
->Mode
, &x_max
, &y_max
);
505 if (EFI_ERROR(err
)) {
510 /* we check 10 times per second for a keystroke */
511 if (config
->timeout_sec
> 0)
512 timeout_remain
= config
->timeout_sec
* 10;
516 idx_highlight
= config
->idx_default
;
517 idx_highlight_prev
= 0;
519 visible_max
= y_max
- 2;
521 if ((UINTN
)config
->idx_default
>= visible_max
)
522 idx_first
= config
->idx_default
-1;
526 idx_last
= idx_first
+ visible_max
-1;
531 /* length of the longest entry */
533 for (i
= 0; i
< config
->entry_count
; i
++) {
536 entry_len
= StrLen(config
->entries
[i
]->title_show
);
537 if (line_width
< entry_len
)
538 line_width
= entry_len
;
540 if (line_width
> x_max
-6)
541 line_width
= x_max
-6;
543 /* offsets to center the entries on the screen */
544 x_start
= (x_max
- (line_width
)) / 2;
545 if (config
->entry_count
< visible_max
)
546 y_start
= ((visible_max
- config
->entry_count
) / 2) + 1;
550 /* menu entries title lines */
551 lines
= AllocatePool(sizeof(CHAR16
*) * config
->entry_count
);
552 for (i
= 0; i
< config
->entry_count
; i
++) {
555 lines
[i
] = AllocatePool(((x_max
+1) * sizeof(CHAR16
)));
556 for (j
= 0; j
< x_start
; j
++)
559 for (k
= 0; config
->entries
[i
]->title_show
[k
] != '\0' && j
< x_max
; j
++, k
++)
560 lines
[i
][j
] = config
->entries
[i
]->title_show
[k
];
562 for (; j
< x_max
; j
++)
564 lines
[i
][x_max
] = '\0';
568 clearline
= AllocatePool((x_max
+1) * sizeof(CHAR16
));
569 for (i
= 0; i
< x_max
; i
++)
577 for (i
= 0; i
< config
->entry_count
; i
++) {
578 if (i
< idx_first
|| i
> idx_last
)
580 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_start
+ i
- idx_first
);
581 if (i
== idx_highlight
)
582 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
583 EFI_BLACK
|EFI_BACKGROUND_LIGHTGRAY
);
585 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
586 EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
587 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, lines
[i
]);
588 if ((INTN
)i
== config
->idx_default_efivar
) {
589 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, x_start
-3, y_start
+ i
- idx_first
);
590 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
"=>");
594 } else if (highlight
) {
595 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_start
+ idx_highlight_prev
- idx_first
);
596 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
597 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, lines
[idx_highlight_prev
]);
598 if ((INTN
)idx_highlight_prev
== config
->idx_default_efivar
) {
599 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, x_start
-3, y_start
+ idx_highlight_prev
- idx_first
);
600 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
"=>");
603 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_start
+ idx_highlight
- idx_first
);
604 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_BLACK
|EFI_BACKGROUND_LIGHTGRAY
);
605 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, lines
[idx_highlight
]);
606 if ((INTN
)idx_highlight
== config
->idx_default_efivar
) {
607 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, x_start
-3, y_start
+ idx_highlight
- idx_first
);
608 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
"=>");
613 if (timeout_remain
> 0) {
615 status
= PoolPrint(L
"Boot in %d sec.", (timeout_remain
+ 5) / 10);
618 /* print status at last line of screen */
624 len
= StrLen(status
);
626 x
= (x_max
- len
) / 2;
629 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
630 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
631 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+ (x_max
- x
));
632 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, status
);
633 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1 + x
+ len
);
636 err
= console_key_read(&key
, wait
);
637 if (EFI_ERROR(err
)) {
638 /* timeout reached */
639 if (timeout_remain
== 0) {
644 /* sleep and update status */
645 if (timeout_remain
> 0) {
646 uefi_call_wrapper(BS
->Stall
, 1, 100 * 1000);
651 /* timeout disabled, wait for next key */
658 /* clear status after keystroke */
662 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
663 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
664 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1);
667 idx_highlight_prev
= idx_highlight
;
670 case KEYPRESS(0, SCAN_UP
, 0):
671 case KEYPRESS(0, 0, 'k'):
672 if (idx_highlight
> 0)
676 case KEYPRESS(0, SCAN_DOWN
, 0):
677 case KEYPRESS(0, 0, 'j'):
678 if (idx_highlight
< config
->entry_count
-1)
682 case KEYPRESS(0, SCAN_HOME
, 0):
683 case KEYPRESS(EFI_ALT_PRESSED
, 0, '<'):
684 if (idx_highlight
> 0) {
690 case KEYPRESS(0, SCAN_END
, 0):
691 case KEYPRESS(EFI_ALT_PRESSED
, 0, '>'):
692 if (idx_highlight
< config
->entry_count
-1) {
694 idx_highlight
= config
->entry_count
-1;
698 case KEYPRESS(0, SCAN_PAGE_UP
, 0):
699 if (idx_highlight
> visible_max
)
700 idx_highlight
-= visible_max
;
705 case KEYPRESS(0, SCAN_PAGE_DOWN
, 0):
706 idx_highlight
+= visible_max
;
707 if (idx_highlight
> config
->entry_count
-1)
708 idx_highlight
= config
->entry_count
-1;
711 case KEYPRESS(0, 0, CHAR_LINEFEED
):
712 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
716 case KEYPRESS(0, SCAN_F1
, 0):
717 case KEYPRESS(0, 0, 'h'):
718 case KEYPRESS(0, 0, '?'):
719 status
= StrDuplicate(L
"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
722 case KEYPRESS(0, 0, 'Q'):
727 case KEYPRESS(0, 0, 'd'):
728 if (config
->idx_default_efivar
!= (INTN
)idx_highlight
) {
729 /* store the selected entry in a persistent EFI variable */
730 efivar_set(L
"LoaderEntryDefault", config
->entries
[idx_highlight
]->id
, TRUE
);
731 config
->idx_default_efivar
= idx_highlight
;
732 status
= StrDuplicate(L
"Default boot entry selected.");
734 /* clear the default entry EFI variable */
735 efivar_set(L
"LoaderEntryDefault", NULL
, TRUE
);
736 config
->idx_default_efivar
= -1;
737 status
= StrDuplicate(L
"Default boot entry cleared.");
742 case KEYPRESS(0, 0, '-'):
743 case KEYPRESS(0, 0, 'T'):
744 if (config
->timeout_sec_efivar
> 0) {
745 config
->timeout_sec_efivar
--;
746 efivar_set_int(L
"LoaderConfigTimeout", config
->timeout_sec_efivar
, TRUE
);
747 if (config
->timeout_sec_efivar
> 0)
748 status
= PoolPrint(L
"Menu timeout set to %d sec.", config
->timeout_sec_efivar
);
750 status
= StrDuplicate(L
"Menu disabled. Hold down key at bootup to show menu.");
751 } else if (config
->timeout_sec_efivar
<= 0){
752 config
->timeout_sec_efivar
= -1;
753 efivar_set(L
"LoaderConfigTimeout", NULL
, TRUE
);
754 if (config
->timeout_sec_config
> 0)
755 status
= PoolPrint(L
"Menu timeout of %d sec is defined by configuration file.",
756 config
->timeout_sec_config
);
758 status
= StrDuplicate(L
"Menu disabled. Hold down key at bootup to show menu.");
762 case KEYPRESS(0, 0, '+'):
763 case KEYPRESS(0, 0, 't'):
764 if (config
->timeout_sec_efivar
== -1 && config
->timeout_sec_config
== 0)
765 config
->timeout_sec_efivar
++;
766 config
->timeout_sec_efivar
++;
767 efivar_set_int(L
"LoaderConfigTimeout", config
->timeout_sec_efivar
, TRUE
);
768 if (config
->timeout_sec_efivar
> 0)
769 status
= PoolPrint(L
"Menu timeout set to %d sec.",
770 config
->timeout_sec_efivar
);
772 status
= StrDuplicate(L
"Menu disabled. Hold down key at bootup to show menu.");
775 case KEYPRESS(0, 0, 'e'):
776 /* only the options of configured entries can be edited */
777 if (!config
->editor
|| config
->entries
[idx_highlight
]->type
== LOADER_UNDEFINED
)
779 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
780 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
781 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1);
782 if (line_edit(config
->entries
[idx_highlight
]->options
, &config
->options_edit
, x_max
-1, y_max
-1))
784 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
785 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1);
788 case KEYPRESS(0, 0, 'v'):
789 status
= PoolPrint(L
"systemd-boot " PACKAGE_VERSION
" (" EFI_MACHINE_TYPE_NAME
"), UEFI Specification %d.%02d, Vendor %s %d.%02d",
790 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff,
791 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
794 case KEYPRESS(0, 0, 'P'):
795 print_status(config
, loaded_image_path
);
799 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'l'):
800 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('l')):
805 /* jump with a hotkey directly to a matching entry */
806 idx
= entry_lookup_key(config
, idx_highlight
+1, KEYCHAR(key
));
813 if (idx_highlight
> idx_last
) {
814 idx_last
= idx_highlight
;
815 idx_first
= 1 + idx_highlight
- visible_max
;
817 } else if (idx_highlight
< idx_first
) {
818 idx_first
= idx_highlight
;
819 idx_last
= idx_highlight
+ visible_max
-1;
823 if (!refresh
&& idx_highlight
!= idx_highlight_prev
)
827 *chosen_entry
= config
->entries
[idx_highlight
];
829 for (i
= 0; i
< config
->entry_count
; i
++)
834 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_WHITE
|EFI_BACKGROUND_BLACK
);
835 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
839 static VOID
config_add_entry(Config
*config
, ConfigEntry
*entry
) {
840 if ((config
->entry_count
& 15) == 0) {
843 i
= config
->entry_count
+ 16;
844 if (config
->entry_count
== 0)
845 config
->entries
= AllocatePool(sizeof(VOID
*) * i
);
847 config
->entries
= ReallocatePool(config
->entries
,
848 sizeof(VOID
*) * config
->entry_count
, sizeof(VOID
*) * i
);
850 config
->entries
[config
->entry_count
++] = entry
;
853 static VOID
config_entry_free(ConfigEntry
*entry
) {
858 FreePool(entry
->title_show
);
859 FreePool(entry
->title
);
860 FreePool(entry
->version
);
861 FreePool(entry
->machine_id
);
862 FreePool(entry
->loader
);
863 FreePool(entry
->options
);
867 static BOOLEAN
is_digit(CHAR16 c
) {
868 return (c
>= '0') && (c
<= '9');
871 static UINTN
c_order(CHAR16 c
) {
876 else if ((c
>= 'a') && (c
<= 'z'))
882 static INTN
str_verscmp(CHAR16
*s1
, CHAR16
*s2
) {
889 while ((*s1
&& !is_digit(*s1
)) || (*s2
&& !is_digit(*s2
))) {
892 order
= c_order(*s1
) - c_order(*s2
);
905 while (is_digit(*s1
) && is_digit(*s2
)) {
921 return StrCmp(os1
, os2
);
924 static CHAR8
*line_get_key_value(
936 line
= content
+ *pos
;
941 while (line
[linelen
] && !strchra((CHAR8
*)"\n\r", line
[linelen
]))
944 /* move pos to next line */
954 line
[linelen
] = '\0';
956 /* remove leading whitespace */
957 while (strchra((CHAR8
*)" \t", *line
)) {
962 /* remove trailing whitespace */
963 while (linelen
> 0 && strchra(sep
, line
[linelen
-1]))
965 line
[linelen
] = '\0';
970 /* split key/value */
972 while (*value
&& !strchra(sep
, *value
))
978 while (*value
&& strchra(sep
, *value
))
982 if (value
[0] == '\"' && line
[linelen
-1] == '\"') {
984 line
[linelen
-1] = '\0';
992 static VOID
config_defaults_load_from_file(Config
*config
, CHAR8
*content
) {
997 while ((line
= line_get_key_value(content
, (CHAR8
*)" \t", &pos
, &key
, &value
))) {
998 if (strcmpa((CHAR8
*)"timeout", key
) == 0) {
999 _cleanup_freepool_ CHAR16
*s
= NULL
;
1001 s
= stra_to_str(value
);
1002 config
->timeout_sec_config
= Atoi(s
);
1003 config
->timeout_sec
= config
->timeout_sec_config
;
1007 if (strcmpa((CHAR8
*)"default", key
) == 0) {
1008 FreePool(config
->entry_default_pattern
);
1009 config
->entry_default_pattern
= stra_to_str(value
);
1010 StrLwr(config
->entry_default_pattern
);
1014 if (strcmpa((CHAR8
*)"editor", key
) == 0) {
1017 if (EFI_ERROR(parse_boolean(value
, &on
)))
1019 config
->editor
= on
;
1022 if (strcmpa((CHAR8
*)"auto-entries", key
) == 0) {
1025 if (EFI_ERROR(parse_boolean(value
, &on
)))
1027 config
->auto_entries
= on
;
1030 if (strcmpa((CHAR8
*)"auto-firmware", key
) == 0) {
1033 if (EFI_ERROR(parse_boolean(value
, &on
)))
1035 config
->auto_firmware
= on
;
1038 if (strcmpa((CHAR8
*)"console-mode", key
) == 0) {
1039 if (strcmpa((CHAR8
*)"auto", value
) == 0)
1040 config
->console_mode_change
= CONSOLE_MODE_AUTO
;
1041 else if (strcmpa((CHAR8
*)"max", value
) == 0)
1042 config
->console_mode_change
= CONSOLE_MODE_MAX
;
1043 else if (strcmpa((CHAR8
*)"keep", value
) == 0)
1044 config
->console_mode_change
= CONSOLE_MODE_KEEP
;
1046 _cleanup_freepool_ CHAR16
*s
= NULL
;
1048 s
= stra_to_str(value
);
1049 config
->console_mode
= Atoi(s
);
1050 config
->console_mode_change
= CONSOLE_MODE_SET
;
1058 static VOID
config_entry_add_from_file(
1063 CHAR16
*loaded_image_path
) {
1070 _cleanup_freepool_ CHAR16
*initrd
= NULL
;
1072 entry
= AllocateZeroPool(sizeof(ConfigEntry
));
1074 while ((line
= line_get_key_value(content
, (CHAR8
*)" \t", &pos
, &key
, &value
))) {
1075 if (strcmpa((CHAR8
*)"title", key
) == 0) {
1076 FreePool(entry
->title
);
1077 entry
->title
= stra_to_str(value
);
1081 if (strcmpa((CHAR8
*)"version", key
) == 0) {
1082 FreePool(entry
->version
);
1083 entry
->version
= stra_to_str(value
);
1087 if (strcmpa((CHAR8
*)"machine-id", key
) == 0) {
1088 FreePool(entry
->machine_id
);
1089 entry
->machine_id
= stra_to_str(value
);
1093 if (strcmpa((CHAR8
*)"linux", key
) == 0) {
1094 FreePool(entry
->loader
);
1095 entry
->type
= LOADER_LINUX
;
1096 entry
->loader
= stra_to_path(value
);
1101 if (strcmpa((CHAR8
*)"efi", key
) == 0) {
1102 entry
->type
= LOADER_EFI
;
1103 FreePool(entry
->loader
);
1104 entry
->loader
= stra_to_path(value
);
1106 /* do not add an entry for ourselves */
1107 if (StriCmp(entry
->loader
, loaded_image_path
) == 0) {
1108 entry
->type
= LOADER_UNDEFINED
;
1114 if (strcmpa((CHAR8
*)"architecture", key
) == 0) {
1115 /* do not add an entry for an EFI image of architecture not matching with that of the image */
1116 if (strcmpa((CHAR8
*)EFI_MACHINE_TYPE_NAME
, value
) != 0) {
1117 entry
->type
= LOADER_UNDEFINED
;
1123 if (strcmpa((CHAR8
*)"initrd", key
) == 0) {
1124 _cleanup_freepool_ CHAR16
*new = NULL
;
1126 new = stra_to_path(value
);
1130 s
= PoolPrint(L
"%s initrd=%s", initrd
, new);
1134 initrd
= PoolPrint(L
"initrd=%s", new);
1139 if (strcmpa((CHAR8
*)"options", key
) == 0) {
1140 _cleanup_freepool_ CHAR16
*new = NULL
;
1142 new = stra_to_str(value
);
1143 if (entry
->options
) {
1146 s
= PoolPrint(L
"%s %s", entry
->options
, new);
1147 FreePool(entry
->options
);
1150 entry
->options
= new;
1158 if (entry
->type
== LOADER_UNDEFINED
) {
1159 config_entry_free(entry
);
1163 /* add initrd= to options */
1164 if (entry
->type
== LOADER_LINUX
&& initrd
) {
1165 if (entry
->options
) {
1168 s
= PoolPrint(L
"%s %s", initrd
, entry
->options
);
1169 FreePool(entry
->options
);
1172 entry
->options
= initrd
;
1177 entry
->device
= device
;
1178 entry
->id
= StrDuplicate(file
);
1179 len
= StrLen(entry
->id
);
1180 /* remove ".conf" */
1182 entry
->id
[len
- 5] = '\0';
1185 config_add_entry(config
, entry
);
1188 static VOID
config_load_defaults(Config
*config
, EFI_FILE
*root_dir
) {
1189 _cleanup_freepool_ CHAR8
*content
= NULL
;
1193 config
->editor
= TRUE
;
1194 config
->auto_entries
= TRUE
;
1195 config
->auto_firmware
= TRUE
;
1197 err
= file_read(root_dir
, L
"\\loader\\loader.conf", 0, 0, &content
, NULL
);
1198 if (!EFI_ERROR(err
))
1199 config_defaults_load_from_file(config
, content
);
1201 err
= efivar_get_int(L
"LoaderConfigTimeout", &sec
);
1202 if (!EFI_ERROR(err
)) {
1203 config
->timeout_sec_efivar
= sec
;
1204 config
->timeout_sec
= sec
;
1206 config
->timeout_sec_efivar
= -1;
1209 static VOID
config_load_entries(
1213 CHAR16
*loaded_image_path
) {
1215 EFI_FILE_HANDLE entries_dir
;
1218 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &entries_dir
, L
"\\loader\\entries", EFI_FILE_MODE_READ
, 0ULL);
1219 if (!EFI_ERROR(err
)) {
1224 _cleanup_freepool_ CHAR8
*content
= NULL
;
1227 bufsize
= sizeof(buf
);
1228 err
= uefi_call_wrapper(entries_dir
->Read
, 3, entries_dir
, &bufsize
, buf
);
1229 if (bufsize
== 0 || EFI_ERROR(err
))
1232 f
= (EFI_FILE_INFO
*) buf
;
1233 if (f
->FileName
[0] == '.')
1235 if (f
->Attribute
& EFI_FILE_DIRECTORY
)
1238 len
= StrLen(f
->FileName
);
1241 if (StriCmp(f
->FileName
+ len
- 5, L
".conf") != 0)
1243 if (StrnCmp(f
->FileName
, L
"auto-", 5) == 0)
1246 err
= file_read(entries_dir
, f
->FileName
, 0, 0, &content
, NULL
);
1247 if (!EFI_ERROR(err
))
1248 config_entry_add_from_file(config
, device
, f
->FileName
, content
, loaded_image_path
);
1250 uefi_call_wrapper(entries_dir
->Close
, 1, entries_dir
);
1254 static VOID
config_sort_entries(Config
*config
) {
1257 for (i
= 1; i
< config
->entry_count
; i
++) {
1262 for (k
= 0; k
< config
->entry_count
- i
; k
++) {
1265 if (str_verscmp(config
->entries
[k
]->id
, config
->entries
[k
+1]->id
) <= 0)
1267 entry
= config
->entries
[k
];
1268 config
->entries
[k
] = config
->entries
[k
+1];
1269 config
->entries
[k
+1] = entry
;
1277 static VOID
config_default_entry_select(Config
*config
) {
1278 _cleanup_freepool_ CHAR16
*entry_oneshot
= NULL
, *entry_default
= NULL
;
1283 * The EFI variable to specify a boot entry for the next, and only the
1284 * next reboot. The variable is always cleared directly after it is read.
1286 err
= efivar_get(L
"LoaderEntryOneShot", &entry_oneshot
);
1287 if (!EFI_ERROR(err
)) {
1288 BOOLEAN found
= FALSE
;
1290 for (i
= 0; i
< config
->entry_count
; i
++)
1291 if (StrCmp(config
->entries
[i
]->id
, entry_oneshot
) == 0) {
1292 config
->idx_default
= i
;
1297 config
->entry_oneshot
= StrDuplicate(entry_oneshot
);
1298 efivar_set(L
"LoaderEntryOneShot", NULL
, TRUE
);
1304 * The EFI variable to select the default boot entry overrides the
1305 * configured pattern. The variable can be set and cleared by pressing
1306 * the 'd' key in the loader selection menu, the entry is marked with
1309 err
= efivar_get(L
"LoaderEntryDefault", &entry_default
);
1310 if (!EFI_ERROR(err
)) {
1311 for (i
= 0; i
< config
->entry_count
; i
++)
1312 if (StrCmp(config
->entries
[i
]->id
, entry_default
) == 0) {
1313 config
->idx_default
= i
;
1314 config
->idx_default_efivar
= i
;
1318 config
->idx_default_efivar
= -1;
1320 if (config
->entry_count
== 0)
1324 * Match the pattern from the end of the list to the start, find last
1325 * entry (largest number) matching the given pattern.
1327 if (config
->entry_default_pattern
) {
1328 i
= config
->entry_count
;
1330 if (config
->entries
[i
]->no_autoselect
)
1332 if (MetaiMatch(config
->entries
[i
]->id
, config
->entry_default_pattern
)) {
1333 config
->idx_default
= i
;
1339 /* select the last suitable entry */
1340 i
= config
->entry_count
;
1342 if (config
->entries
[i
]->no_autoselect
)
1344 config
->idx_default
= i
;
1348 /* no entry found */
1349 config
->idx_default
= -1;
1352 static BOOLEAN
find_nonunique(ConfigEntry
**entries
, UINTN entry_count
) {
1353 BOOLEAN non_unique
= FALSE
;
1356 for (i
= 0; i
< entry_count
; i
++)
1357 entries
[i
]->non_unique
= FALSE
;
1359 for (i
= 0; i
< entry_count
; i
++)
1360 for (k
= 0; k
< entry_count
; k
++) {
1363 if (StrCmp(entries
[i
]->title_show
, entries
[k
]->title_show
) != 0)
1366 non_unique
= entries
[i
]->non_unique
= entries
[k
]->non_unique
= TRUE
;
1372 /* generate a unique title, avoiding non-distinguishable menu entries */
1373 static VOID
config_title_generate(Config
*config
) {
1377 for (i
= 0; i
< config
->entry_count
; i
++) {
1380 FreePool(config
->entries
[i
]->title_show
);
1381 title
= config
->entries
[i
]->title
;
1383 title
= config
->entries
[i
]->id
;
1384 config
->entries
[i
]->title_show
= StrDuplicate(title
);
1387 if (!find_nonunique(config
->entries
, config
->entry_count
))
1390 /* add version to non-unique titles */
1391 for (i
= 0; i
< config
->entry_count
; i
++) {
1394 if (!config
->entries
[i
]->non_unique
)
1396 if (!config
->entries
[i
]->version
)
1399 s
= PoolPrint(L
"%s (%s)", config
->entries
[i
]->title_show
, config
->entries
[i
]->version
);
1400 FreePool(config
->entries
[i
]->title_show
);
1401 config
->entries
[i
]->title_show
= s
;
1404 if (!find_nonunique(config
->entries
, config
->entry_count
))
1407 /* add machine-id to non-unique titles */
1408 for (i
= 0; i
< config
->entry_count
; i
++) {
1410 _cleanup_freepool_ CHAR16
*m
= NULL
;
1412 if (!config
->entries
[i
]->non_unique
)
1414 if (!config
->entries
[i
]->machine_id
)
1417 m
= StrDuplicate(config
->entries
[i
]->machine_id
);
1419 s
= PoolPrint(L
"%s (%s)", config
->entries
[i
]->title_show
, m
);
1420 FreePool(config
->entries
[i
]->title_show
);
1421 config
->entries
[i
]->title_show
= s
;
1424 if (!find_nonunique(config
->entries
, config
->entry_count
))
1427 /* add file name to non-unique titles */
1428 for (i
= 0; i
< config
->entry_count
; i
++) {
1431 if (!config
->entries
[i
]->non_unique
)
1433 s
= PoolPrint(L
"%s (%s)", config
->entries
[i
]->title_show
, config
->entries
[i
]->id
);
1434 FreePool(config
->entries
[i
]->title_show
);
1435 config
->entries
[i
]->title_show
= s
;
1436 config
->entries
[i
]->non_unique
= FALSE
;
1440 static BOOLEAN
config_entry_add_call(
1443 EFI_STATUS (*call
)(VOID
)) {
1447 entry
= AllocateZeroPool(sizeof(ConfigEntry
));
1448 entry
->title
= StrDuplicate(title
);
1450 entry
->no_autoselect
= TRUE
;
1451 config_add_entry(config
, entry
);
1455 static ConfigEntry
*config_entry_add_loader(
1458 enum loader_type type
,
1466 entry
= AllocateZeroPool(sizeof(ConfigEntry
));
1468 entry
->title
= StrDuplicate(title
);
1469 entry
->device
= device
;
1470 entry
->loader
= StrDuplicate(loader
);
1471 entry
->id
= StrDuplicate(id
);
1474 config_add_entry(config
, entry
);
1479 static BOOLEAN
config_entry_add_loader_auto(
1483 CHAR16
*loaded_image_path
,
1489 EFI_FILE_HANDLE handle
;
1493 if (!config
->auto_entries
)
1496 /* do not add an entry for ourselves */
1497 if (loaded_image_path
) {
1499 _cleanup_freepool_ CHAR8
*content
= NULL
;
1501 if (StriCmp(loader
, loaded_image_path
) == 0)
1504 /* look for systemd-boot magic string */
1505 err
= file_read(root_dir
, loader
, 0, 100*1024, &content
, &len
);
1506 if (!EFI_ERROR(err
)) {
1507 CHAR8
*start
= content
;
1508 CHAR8
*last
= content
+ len
- sizeof(magic
) - 1;
1510 for (; start
<= last
; start
++)
1511 if (start
[0] == magic
[0] && CompareMem(start
, magic
, sizeof(magic
) - 1) == 0)
1516 /* check existence */
1517 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &handle
, loader
, EFI_FILE_MODE_READ
, 0ULL);
1520 uefi_call_wrapper(handle
->Close
, 1, handle
);
1522 entry
= config_entry_add_loader(config
, device
, LOADER_UNDEFINED
, id
, key
, title
, loader
);
1526 /* do not boot right away into auto-detected entries */
1527 entry
->no_autoselect
= TRUE
;
1532 static VOID
config_entry_add_osx(Config
*config
) {
1534 UINTN handle_count
= 0;
1535 _cleanup_freepool_ EFI_HANDLE
*handles
= NULL
;
1537 if (!config
->auto_entries
)
1540 err
= LibLocateHandle(ByProtocol
, &FileSystemProtocol
, NULL
, &handle_count
, &handles
);
1541 if (!EFI_ERROR(err
)) {
1544 for (i
= 0; i
< handle_count
; i
++) {
1548 root
= LibOpenRoot(handles
[i
]);
1551 found
= config_entry_add_loader_auto(config
, handles
[i
], root
, NULL
, L
"auto-osx", 'a', L
"macOS",
1552 L
"\\System\\Library\\CoreServices\\boot.efi");
1553 uefi_call_wrapper(root
->Close
, 1, root
);
1560 static VOID
config_entry_add_linux(
1562 EFI_LOADED_IMAGE
*loaded_image
,
1563 EFI_FILE
*root_dir
) {
1565 EFI_FILE_HANDLE linux_dir
;
1569 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &linux_dir
, L
"\\EFI\\Linux", EFI_FILE_MODE_READ
, 0ULL);
1575 UINTN bufsize
= sizeof buf
;
1577 CHAR8
*sections
[] = {
1579 (UINT8
*)".cmdline",
1582 UINTN offs
[ELEMENTSOF(sections
)-1] = {};
1583 UINTN szs
[ELEMENTSOF(sections
)-1] = {};
1584 UINTN addrs
[ELEMENTSOF(sections
)-1] = {};
1585 CHAR8
*content
= NULL
;
1590 CHAR16
*os_name
= NULL
;
1591 CHAR16
*os_id
= NULL
;
1592 CHAR16
*os_version
= NULL
;
1593 CHAR16
*os_build
= NULL
;
1595 err
= uefi_call_wrapper(linux_dir
->Read
, 3, linux_dir
, &bufsize
, buf
);
1596 if (bufsize
== 0 || EFI_ERROR(err
))
1599 f
= (EFI_FILE_INFO
*) buf
;
1600 if (f
->FileName
[0] == '.')
1602 if (f
->Attribute
& EFI_FILE_DIRECTORY
)
1604 len
= StrLen(f
->FileName
);
1607 if (StriCmp(f
->FileName
+ len
- 4, L
".efi") != 0)
1610 /* look for .osrel and .cmdline sections in the .efi binary */
1611 err
= pe_file_locate_sections(linux_dir
, f
->FileName
, sections
, addrs
, offs
, szs
);
1615 err
= file_read(linux_dir
, f
->FileName
, offs
[0], szs
[0], &content
, NULL
);
1619 /* read properties from the embedded os-release file */
1620 while ((line
= line_get_key_value(content
, (CHAR8
*)"=", &pos
, &key
, &value
))) {
1621 if (strcmpa((CHAR8
*)"PRETTY_NAME", key
) == 0) {
1623 os_name
= stra_to_str(value
);
1627 if (strcmpa((CHAR8
*)"ID", key
) == 0) {
1629 os_id
= stra_to_str(value
);
1633 if (strcmpa((CHAR8
*)"VERSION_ID", key
) == 0) {
1634 FreePool(os_version
);
1635 os_version
= stra_to_str(value
);
1639 if (strcmpa((CHAR8
*)"BUILD_ID", key
) == 0) {
1641 os_build
= stra_to_str(value
);
1646 if (os_name
&& os_id
&& (os_version
|| os_build
)) {
1650 conf
= PoolPrint(L
"%s-%s", os_id
, os_version
? : os_build
);
1651 path
= PoolPrint(L
"\\EFI\\Linux\\%s", f
->FileName
);
1652 entry
= config_entry_add_loader(config
, loaded_image
->DeviceHandle
, LOADER_LINUX
, conf
, 'l', os_name
, path
);
1656 /* read the embedded cmdline file */
1657 err
= file_read(linux_dir
, f
->FileName
, offs
[1], szs
[1], &content
, NULL
);
1658 if (!EFI_ERROR(err
)) {
1660 /* chomp the newline */
1661 if (content
[szs
[1]-1] == '\n')
1662 content
[szs
[1]-1] = '\0';
1664 entry
->options
= stra_to_str(content
);
1673 FreePool(os_version
);
1678 uefi_call_wrapper(linux_dir
->Close
, 1, linux_dir
);
1681 static EFI_STATUS
image_start(
1682 EFI_HANDLE parent_image
,
1683 const Config
*config
,
1684 const ConfigEntry
*entry
) {
1687 _cleanup_freepool_ EFI_DEVICE_PATH
*path
= NULL
;
1691 path
= FileDevicePath(entry
->device
, entry
->loader
);
1693 Print(L
"Error getting device path.");
1694 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1695 return EFI_INVALID_PARAMETER
;
1698 err
= uefi_call_wrapper(BS
->LoadImage
, 6, FALSE
, parent_image
, path
, NULL
, 0, &image
);
1699 if (EFI_ERROR(err
)) {
1700 Print(L
"Error loading %s: %r", entry
->loader
, err
);
1701 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1705 if (config
->options_edit
)
1706 options
= config
->options_edit
;
1707 else if (entry
->options
)
1708 options
= entry
->options
;
1712 EFI_LOADED_IMAGE
*loaded_image
;
1714 err
= uefi_call_wrapper(BS
->OpenProtocol
, 6, image
, &LoadedImageProtocol
, (VOID
**)&loaded_image
,
1715 parent_image
, NULL
, EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
1716 if (EFI_ERROR(err
)) {
1717 Print(L
"Error getting LoadedImageProtocol handle: %r", err
);
1718 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1721 loaded_image
->LoadOptions
= options
;
1722 loaded_image
->LoadOptionsSize
= (StrLen(loaded_image
->LoadOptions
)+1) * sizeof(CHAR16
);
1725 /* Try to log any options to the TPM, especially to catch manually edited options */
1726 err
= tpm_log_event(SD_TPM_PCR
,
1727 (EFI_PHYSICAL_ADDRESS
) (UINTN
) loaded_image
->LoadOptions
,
1728 loaded_image
->LoadOptionsSize
, loaded_image
->LoadOptions
);
1729 if (EFI_ERROR(err
)) {
1730 Print(L
"Unable to add image options measurement: %r", err
);
1731 uefi_call_wrapper(BS
->Stall
, 1, 200 * 1000);
1736 efivar_set_time_usec(L
"LoaderTimeExecUSec", 0);
1737 err
= uefi_call_wrapper(BS
->StartImage
, 3, image
, NULL
, NULL
);
1739 uefi_call_wrapper(BS
->UnloadImage
, 1, image
);
1743 static EFI_STATUS
reboot_into_firmware(VOID
) {
1744 _cleanup_freepool_ CHAR8
*b
= NULL
;
1749 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
1751 err
= efivar_get_raw(&global_guid
, L
"OsIndications", &b
, &size
);
1752 if (!EFI_ERROR(err
))
1753 osind
|= (UINT64
)*b
;
1755 err
= efivar_set_raw(&global_guid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
1759 err
= uefi_call_wrapper(RT
->ResetSystem
, 4, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1760 Print(L
"Error calling ResetSystem: %r", err
);
1761 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1765 static VOID
config_free(Config
*config
) {
1768 for (i
= 0; i
< config
->entry_count
; i
++)
1769 config_entry_free(config
->entries
[i
]);
1770 FreePool(config
->entries
);
1771 FreePool(config
->entry_default_pattern
);
1772 FreePool(config
->options_edit
);
1773 FreePool(config
->entry_oneshot
);
1776 EFI_STATUS
efi_main(EFI_HANDLE image
, EFI_SYSTEM_TABLE
*sys_table
) {
1777 _cleanup_freepool_ CHAR16
*infostr
= NULL
, *typestr
= NULL
;
1780 EFI_LOADED_IMAGE
*loaded_image
;
1782 CHAR16
*loaded_image_path
;
1786 BOOLEAN menu
= FALSE
;
1789 InitializeLib(image
, sys_table
);
1790 init_usec
= time_usec();
1791 efivar_set_time_usec(L
"LoaderTimeInitUSec", init_usec
);
1792 efivar_set(L
"LoaderInfo", L
"systemd-boot " PACKAGE_VERSION
, FALSE
);
1794 infostr
= PoolPrint(L
"%s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
1795 efivar_set(L
"LoaderFirmwareInfo", infostr
, FALSE
);
1797 typestr
= PoolPrint(L
"UEFI %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
1798 efivar_set(L
"LoaderFirmwareType", typestr
, FALSE
);
1800 err
= uefi_call_wrapper(BS
->OpenProtocol
, 6, image
, &LoadedImageProtocol
, (VOID
**)&loaded_image
,
1801 image
, NULL
, EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
1802 if (EFI_ERROR(err
)) {
1803 Print(L
"Error getting a LoadedImageProtocol handle: %r ", err
);
1804 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1808 /* export the device path this image is started from */
1809 if (disk_get_part_uuid(loaded_image
->DeviceHandle
, uuid
) == EFI_SUCCESS
)
1810 efivar_set(L
"LoaderDevicePartUUID", uuid
, FALSE
);
1812 root_dir
= LibOpenRoot(loaded_image
->DeviceHandle
);
1814 Print(L
"Unable to open root directory: %r ", err
);
1815 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1816 return EFI_LOAD_ERROR
;
1819 if (secure_boot_enabled() && shim_loaded()) {
1820 err
= security_policy_install();
1821 if (EFI_ERROR(err
)) {
1822 Print(L
"Error installing security policy: %r ", err
);
1823 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1828 /* the filesystem path to this image, to prevent adding ourselves to the menu */
1829 loaded_image_path
= DevicePathToStr(loaded_image
->FilePath
);
1830 efivar_set(L
"LoaderImageIdentifier", loaded_image_path
, FALSE
);
1832 ZeroMem(&config
, sizeof(Config
));
1833 config_load_defaults(&config
, root_dir
);
1835 /* scan /EFI/Linux/ directory */
1836 config_entry_add_linux(&config
, loaded_image
, root_dir
);
1838 /* scan /loader/entries/\*.conf files */
1839 config_load_entries(&config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
);
1841 /* sort entries after version number */
1842 config_sort_entries(&config
);
1844 /* if we find some well-known loaders, add them to the end of the list */
1845 config_entry_add_loader_auto(&config
, loaded_image
->DeviceHandle
, root_dir
, NULL
,
1846 L
"auto-windows", 'w', L
"Windows Boot Manager", L
"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
1847 config_entry_add_loader_auto(&config
, loaded_image
->DeviceHandle
, root_dir
, NULL
,
1848 L
"auto-efi-shell", 's', L
"EFI Shell", L
"\\shell" EFI_MACHINE_TYPE_NAME
".efi");
1849 config_entry_add_loader_auto(&config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
,
1850 L
"auto-efi-default", '\0', L
"EFI Default Loader", L
"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME
".efi");
1851 config_entry_add_osx(&config
);
1853 if (config
.auto_firmware
&& efivar_get_raw(&global_guid
, L
"OsIndicationsSupported", &b
, &size
) == EFI_SUCCESS
) {
1854 UINT64 osind
= (UINT64
)*b
;
1856 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
)
1857 config_entry_add_call(&config
, L
"Reboot Into Firmware Interface", reboot_into_firmware
);
1861 if (config
.entry_count
== 0) {
1862 Print(L
"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
1863 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1867 config_title_generate(&config
);
1869 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
1870 config_default_entry_select(&config
);
1872 /* if no configured entry to select from was found, enable the menu */
1873 if (config
.idx_default
== -1) {
1874 config
.idx_default
= 0;
1875 if (config
.timeout_sec
== 0)
1876 config
.timeout_sec
= 10;
1879 /* select entry or show menu when key is pressed or timeout is set */
1880 if (config
.timeout_sec
== 0) {
1883 err
= console_key_read(&key
, FALSE
);
1884 if (!EFI_ERROR(err
)) {
1887 /* find matching key in config entries */
1888 idx
= entry_lookup_key(&config
, config
.idx_default
, KEYCHAR(key
));
1890 config
.idx_default
= idx
;
1900 entry
= config
.entries
[config
.idx_default
];
1902 efivar_set_time_usec(L
"LoaderTimeMenuUSec", 0);
1903 uefi_call_wrapper(BS
->SetWatchdogTimer
, 4, 0, 0x10000, 0, NULL
);
1904 if (!menu_run(&config
, &entry
, loaded_image_path
))
1907 /* run special entry like "reboot" */
1914 /* export the selected boot entry to the system */
1915 efivar_set(L
"LoaderEntrySelected", entry
->id
, FALSE
);
1917 uefi_call_wrapper(BS
->SetWatchdogTimer
, 4, 5 * 60, 0x10000, 0, NULL
);
1918 err
= image_start(image
, &config
, entry
);
1919 if (EFI_ERROR(err
)) {
1920 graphics_mode(FALSE
);
1921 Print(L
"\nFailed to execute %s (%s): %r\n", entry
->title
, entry
->loader
, err
);
1922 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1927 config
.timeout_sec
= 0;
1931 FreePool(loaded_image_path
);
1932 config_free(&config
);
1933 uefi_call_wrapper(root_dir
->Close
, 1, root_dir
);
1934 uefi_call_wrapper(BS
->CloseProtocol
, 4, image
, &LoadedImageProtocol
, image
, NULL
);