2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation; either version 2.1 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * Copyright (C) 2012-2015 Kay Sievers <kay@vrfy.org>
13 * Copyright (C) 2012-2015 Harald Hoyer <harald@redhat.com>
28 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
29 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
32 /* magic string to find in the binary image */
33 static const char __attribute__((used
)) magic
[] = "#### LoaderInfo: systemd-boot " PACKAGE_VERSION
" ####";
35 static const EFI_GUID global_guid
= EFI_GLOBAL_VARIABLE
;
50 enum loader_type type
;
54 EFI_STATUS (*call
)(VOID
);
55 BOOLEAN no_autoselect
;
60 ConfigEntry
**entries
;
63 INTN idx_default_efivar
;
65 UINTN timeout_sec_config
;
66 INTN timeout_sec_efivar
;
67 CHAR16
*entry_default_pattern
;
68 CHAR16
*entry_oneshot
;
73 static VOID
cursor_left(UINTN
*cursor
, UINTN
*first
) {
76 else if ((*first
) > 0)
80 static VOID
cursor_right(UINTN
*cursor
, UINTN
*first
, UINTN x_max
, UINTN len
) {
81 if ((*cursor
)+1 < x_max
)
83 else if ((*first
) + (*cursor
) < len
)
87 static BOOLEAN
line_edit(CHAR16
*line_in
, CHAR16
**line_out
, UINTN x_max
, UINTN y_pos
) {
100 size
= StrLen(line_in
) + 1024;
101 line
= AllocatePool(size
* sizeof(CHAR16
));
102 StrCpy(line
, line_in
);
104 print
= AllocatePool((x_max
+1) * sizeof(CHAR16
));
106 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, TRUE
);
121 CopyMem(print
, line
+ first
, i
* sizeof(CHAR16
));
122 while (clear
> 0 && i
< x_max
-1) {
128 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_pos
);
129 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, print
);
130 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
132 err
= console_key_read(&key
, TRUE
);
137 case KEYPRESS(0, SCAN_ESC
, 0):
138 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'c'):
139 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'g'):
140 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('c')):
141 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('g')):
145 case KEYPRESS(0, SCAN_HOME
, 0):
146 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'a'):
147 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('a')):
148 /* beginning-of-line */
153 case KEYPRESS(0, SCAN_END
, 0):
154 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'e'):
155 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('e')):
157 cursor
= len
- first
;
158 if (cursor
+1 >= x_max
) {
160 first
= len
- (x_max
-1);
164 case KEYPRESS(0, SCAN_DOWN
, 0):
165 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'f'):
166 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_RIGHT
, 0):
168 while (line
[first
+ cursor
] && line
[first
+ cursor
] == ' ')
169 cursor_right(&cursor
, &first
, x_max
, len
);
170 while (line
[first
+ cursor
] && line
[first
+ cursor
] != ' ')
171 cursor_right(&cursor
, &first
, x_max
, len
);
172 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
175 case KEYPRESS(0, SCAN_UP
, 0):
176 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'b'):
177 case KEYPRESS(EFI_CONTROL_PRESSED
, SCAN_LEFT
, 0):
179 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
180 cursor_left(&cursor
, &first
);
181 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ')
182 cursor_left(&cursor
, &first
);
184 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ')
185 cursor_left(&cursor
, &first
);
186 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
189 case KEYPRESS(0, SCAN_RIGHT
, 0):
190 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'f'):
191 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('f')):
193 if (first
+ cursor
== len
)
195 cursor_right(&cursor
, &first
, x_max
, len
);
196 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
199 case KEYPRESS(0, SCAN_LEFT
, 0):
200 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'b'):
201 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('b')):
203 cursor_left(&cursor
, &first
);
204 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
207 case KEYPRESS(EFI_ALT_PRESSED
, 0, 'd'):
210 for (i
= first
+ cursor
; i
< len
&& line
[i
] == ' '; i
++)
212 for (; i
< len
&& line
[i
] != ' '; i
++)
215 for (i
= first
+ cursor
; i
+ clear
< len
; i
++)
216 line
[i
] = line
[i
+ clear
];
221 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'w'):
222 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('w')):
223 case KEYPRESS(EFI_ALT_PRESSED
, 0, CHAR_BACKSPACE
):
224 /* backward-kill-word */
226 if ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] == ' ') {
227 cursor_left(&cursor
, &first
);
229 while ((first
+ cursor
) > 0 && line
[first
+ cursor
] == ' ') {
230 cursor_left(&cursor
, &first
);
234 while ((first
+ cursor
) > 0 && line
[first
+ cursor
-1] != ' ') {
235 cursor_left(&cursor
, &first
);
238 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, cursor
, y_pos
);
240 for (i
= first
+ cursor
; i
+ clear
< len
; i
++)
241 line
[i
] = line
[i
+ clear
];
246 case KEYPRESS(0, SCAN_DELETE
, 0):
247 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'd'):
248 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('d')):
251 if (first
+ cursor
== len
)
253 for (i
= first
+ cursor
; i
< len
; i
++)
259 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'k'):
260 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('k')):
262 line
[first
+ cursor
] = '\0';
263 clear
= len
- (first
+ cursor
);
264 len
= first
+ cursor
;
267 case KEYPRESS(0, 0, CHAR_LINEFEED
):
268 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
269 if (StrCmp(line
, line_in
) != 0) {
277 case KEYPRESS(0, 0, CHAR_BACKSPACE
):
280 if (first
== 0 && cursor
== 0)
282 for (i
= first
+ cursor
-1; i
< len
; i
++)
288 if (cursor
> 0 || first
== 0)
290 /* show full line if it fits */
296 /* jump left to see what we delete */
306 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
307 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
310 for (i
= len
; i
> first
+ cursor
; i
--)
312 line
[first
+ cursor
] = KEYCHAR(key
);
315 if (cursor
+1 < x_max
)
317 else if (first
+ cursor
< len
)
323 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, FALSE
);
329 static UINTN
entry_lookup_key(Config
*config
, UINTN start
, CHAR16 key
) {
335 /* select entry by number key */
336 if (key
>= '1' && key
<= '9') {
338 if (i
> config
->entry_count
)
339 i
= config
->entry_count
;
343 /* find matching key in config entries */
344 for (i
= start
; i
< config
->entry_count
; i
++)
345 if (config
->entries
[i
]->key
== key
)
348 for (i
= 0; i
< start
; i
++)
349 if (config
->entries
[i
]->key
== key
)
355 static VOID
print_status(Config
*config
, CHAR16
*loaded_image_path
) {
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: " PACKAGE_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", &b
, &size
) == EFI_SUCCESS
) {
378 Print(L
"SecureBoot: %s\n", yes_no(*b
> 0));
382 if (efivar_get_raw(&global_guid
, L
"SetupMode", &b
, &size
) == EFI_SUCCESS
) {
383 Print(L
"SetupMode: %s\n", *b
> 0 ? L
"setup" : L
"user");
388 Print(L
"Shim: present\n");
390 if (efivar_get_raw(&global_guid
, L
"OsIndicationsSupported", &b
, &size
) == EFI_SUCCESS
) {
391 Print(L
"OsIndicationsSupported: %d\n", (UINT64
)*b
);
396 Print(L
"timeout: %d\n", config
->timeout_sec
);
397 if (config
->timeout_sec_efivar
>= 0)
398 Print(L
"timeout (EFI var): %d\n", config
->timeout_sec_efivar
);
399 Print(L
"timeout (config): %d\n", config
->timeout_sec_config
);
400 if (config
->entry_default_pattern
)
401 Print(L
"default pattern: '%s'\n", config
->entry_default_pattern
);
402 Print(L
"editor: %s\n", yes_no(!config
->no_editor
));
405 Print(L
"config entry count: %d\n", config
->entry_count
);
406 Print(L
"entry selected idx: %d\n", config
->idx_default
);
407 if (config
->idx_default_efivar
>= 0)
408 Print(L
"entry EFI var idx: %d\n", config
->idx_default_efivar
);
411 if (efivar_get_int(L
"LoaderConfigTimeout", &i
) == EFI_SUCCESS
)
412 Print(L
"LoaderConfigTimeout: %d\n", i
);
413 if (config
->entry_oneshot
)
414 Print(L
"LoaderEntryOneShot: %s\n", config
->entry_oneshot
);
415 if (efivar_get(L
"LoaderDevicePartUUID", &s
) == EFI_SUCCESS
) {
416 Print(L
"LoaderDevicePartUUID: %s\n", s
);
419 if (efivar_get(L
"LoaderEntryDefault", &s
) == EFI_SUCCESS
) {
420 Print(L
"LoaderEntryDefault: %s\n", s
);
424 Print(L
"\n--- press key ---\n\n");
425 console_key_read(&key
, TRUE
);
427 for (i
= 0; i
< config
->entry_count
; i
++) {
430 if (key
== KEYPRESS(0, SCAN_ESC
, 0) || key
== KEYPRESS(0, 0, 'q'))
433 entry
= config
->entries
[i
];
434 Print(L
"config entry: %d/%d\n", i
+1, config
->entry_count
);
436 Print(L
"file '%s'\n", entry
->file
);
437 Print(L
"title show '%s'\n", entry
->title_show
);
439 Print(L
"title '%s'\n", entry
->title
);
441 Print(L
"version '%s'\n", entry
->version
);
442 if (entry
->machine_id
)
443 Print(L
"machine-id '%s'\n", entry
->machine_id
);
445 EFI_DEVICE_PATH
*device_path
;
448 device_path
= DevicePathFromHandle(entry
->device
);
450 str
= DevicePathToStr(device_path
);
451 Print(L
"device handle '%s'\n", str
);
456 Print(L
"loader '%s'\n", entry
->loader
);
458 Print(L
"options '%s'\n", entry
->options
);
459 Print(L
"auto-select %s\n", yes_no(!entry
->no_autoselect
));
461 Print(L
"internal call yes\n");
463 Print(L
"\n--- press key ---\n\n");
464 console_key_read(&key
, TRUE
);
467 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
470 static BOOLEAN
menu_run(Config
*config
, ConfigEntry
**chosen_entry
, CHAR16
*loaded_image_path
) {
474 UINTN idx_highlight_prev
;
490 BOOLEAN exit
= FALSE
;
492 BOOLEAN wait
= FALSE
;
494 graphics_mode(FALSE
);
495 uefi_call_wrapper(ST
->ConIn
->Reset
, 2, ST
->ConIn
, FALSE
);
496 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, FALSE
);
497 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
499 /* draw a single character to make ClearScreen work on some firmware */
500 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
" ");
501 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
503 err
= uefi_call_wrapper(ST
->ConOut
->QueryMode
, 4, ST
->ConOut
, ST
->ConOut
->Mode
->Mode
, &x_max
, &y_max
);
504 if (EFI_ERROR(err
)) {
509 /* we check 10 times per second for a keystroke */
510 if (config
->timeout_sec
> 0)
511 timeout_remain
= config
->timeout_sec
* 10;
515 idx_highlight
= config
->idx_default
;
516 idx_highlight_prev
= 0;
518 visible_max
= y_max
- 2;
520 if ((UINTN
)config
->idx_default
>= visible_max
)
521 idx_first
= config
->idx_default
-1;
525 idx_last
= idx_first
+ visible_max
-1;
530 /* length of the longest entry */
532 for (i
= 0; i
< config
->entry_count
; i
++) {
535 entry_len
= StrLen(config
->entries
[i
]->title_show
);
536 if (line_width
< entry_len
)
537 line_width
= entry_len
;
539 if (line_width
> x_max
-6)
540 line_width
= x_max
-6;
542 /* offsets to center the entries on the screen */
543 x_start
= (x_max
- (line_width
)) / 2;
544 if (config
->entry_count
< visible_max
)
545 y_start
= ((visible_max
- config
->entry_count
) / 2) + 1;
549 /* menu entries title lines */
550 lines
= AllocatePool(sizeof(CHAR16
*) * config
->entry_count
);
551 for (i
= 0; i
< config
->entry_count
; i
++) {
554 lines
[i
] = AllocatePool(((x_max
+1) * sizeof(CHAR16
)));
555 for (j
= 0; j
< x_start
; j
++)
558 for (k
= 0; config
->entries
[i
]->title_show
[k
] != '\0' && j
< x_max
; j
++, k
++)
559 lines
[i
][j
] = config
->entries
[i
]->title_show
[k
];
561 for (; j
< x_max
; j
++)
563 lines
[i
][x_max
] = '\0';
567 clearline
= AllocatePool((x_max
+1) * sizeof(CHAR16
));
568 for (i
= 0; i
< x_max
; i
++)
576 for (i
= 0; i
< config
->entry_count
; i
++) {
577 if (i
< idx_first
|| i
> idx_last
)
579 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_start
+ i
- idx_first
);
580 if (i
== idx_highlight
)
581 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
582 EFI_BLACK
|EFI_BACKGROUND_LIGHTGRAY
);
584 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
585 EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
586 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, lines
[i
]);
587 if ((INTN
)i
== config
->idx_default_efivar
) {
588 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, x_start
-3, y_start
+ i
- idx_first
);
589 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
"=>");
593 } else if (highlight
) {
594 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_start
+ idx_highlight_prev
- idx_first
);
595 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
596 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, lines
[idx_highlight_prev
]);
597 if ((INTN
)idx_highlight_prev
== config
->idx_default_efivar
) {
598 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, x_start
-3, y_start
+ idx_highlight_prev
- idx_first
);
599 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
"=>");
602 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_start
+ idx_highlight
- idx_first
);
603 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_BLACK
|EFI_BACKGROUND_LIGHTGRAY
);
604 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, lines
[idx_highlight
]);
605 if ((INTN
)idx_highlight
== config
->idx_default_efivar
) {
606 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, x_start
-3, y_start
+ idx_highlight
- idx_first
);
607 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, L
"=>");
612 if (timeout_remain
> 0) {
614 status
= PoolPrint(L
"Boot in %d sec.", (timeout_remain
+ 5) / 10);
617 /* print status at last line of screen */
623 len
= StrLen(status
);
625 x
= (x_max
- len
) / 2;
628 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
629 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
630 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+ (x_max
- x
));
631 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, status
);
632 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1 + x
+ len
);
635 err
= console_key_read(&key
, wait
);
636 if (EFI_ERROR(err
)) {
637 /* timeout reached */
638 if (timeout_remain
== 0) {
643 /* sleep and update status */
644 if (timeout_remain
> 0) {
645 uefi_call_wrapper(BS
->Stall
, 1, 100 * 1000);
650 /* timeout disabled, wait for next key */
657 /* clear status after keystroke */
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
+1);
666 idx_highlight_prev
= idx_highlight
;
669 case KEYPRESS(0, SCAN_UP
, 0):
670 case KEYPRESS(0, 0, 'k'):
671 if (idx_highlight
> 0)
675 case KEYPRESS(0, SCAN_DOWN
, 0):
676 case KEYPRESS(0, 0, 'j'):
677 if (idx_highlight
< config
->entry_count
-1)
681 case KEYPRESS(0, SCAN_HOME
, 0):
682 case KEYPRESS(EFI_ALT_PRESSED
, 0, '<'):
683 if (idx_highlight
> 0) {
689 case KEYPRESS(0, SCAN_END
, 0):
690 case KEYPRESS(EFI_ALT_PRESSED
, 0, '>'):
691 if (idx_highlight
< config
->entry_count
-1) {
693 idx_highlight
= config
->entry_count
-1;
697 case KEYPRESS(0, SCAN_PAGE_UP
, 0):
698 if (idx_highlight
> visible_max
)
699 idx_highlight
-= visible_max
;
704 case KEYPRESS(0, SCAN_PAGE_DOWN
, 0):
705 idx_highlight
+= visible_max
;
706 if (idx_highlight
> config
->entry_count
-1)
707 idx_highlight
= config
->entry_count
-1;
710 case KEYPRESS(0, 0, CHAR_LINEFEED
):
711 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN
):
715 case KEYPRESS(0, SCAN_F1
, 0):
716 case KEYPRESS(0, 0, 'h'):
717 case KEYPRESS(0, 0, '?'):
718 status
= StrDuplicate(L
"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
721 case KEYPRESS(0, 0, 'Q'):
726 case KEYPRESS(0, 0, 'd'):
727 if (config
->idx_default_efivar
!= (INTN
)idx_highlight
) {
728 /* store the selected entry in a persistent EFI variable */
729 efivar_set(L
"LoaderEntryDefault", config
->entries
[idx_highlight
]->file
, TRUE
);
730 config
->idx_default_efivar
= idx_highlight
;
731 status
= StrDuplicate(L
"Default boot entry selected.");
733 /* clear the default entry EFI variable */
734 efivar_set(L
"LoaderEntryDefault", NULL
, TRUE
);
735 config
->idx_default_efivar
= -1;
736 status
= StrDuplicate(L
"Default boot entry cleared.");
741 case KEYPRESS(0, 0, '-'):
742 case KEYPRESS(0, 0, 'T'):
743 if (config
->timeout_sec_efivar
> 0) {
744 config
->timeout_sec_efivar
--;
745 efivar_set_int(L
"LoaderConfigTimeout", config
->timeout_sec_efivar
, TRUE
);
746 if (config
->timeout_sec_efivar
> 0)
747 status
= PoolPrint(L
"Menu timeout set to %d sec.", config
->timeout_sec_efivar
);
749 status
= StrDuplicate(L
"Menu disabled. Hold down key at bootup to show menu.");
750 } else if (config
->timeout_sec_efivar
<= 0){
751 config
->timeout_sec_efivar
= -1;
752 efivar_set(L
"LoaderConfigTimeout", NULL
, TRUE
);
753 if (config
->timeout_sec_config
> 0)
754 status
= PoolPrint(L
"Menu timeout of %d sec is defined by configuration file.",
755 config
->timeout_sec_config
);
757 status
= StrDuplicate(L
"Menu disabled. Hold down key at bootup to show menu.");
761 case KEYPRESS(0, 0, '+'):
762 case KEYPRESS(0, 0, 't'):
763 if (config
->timeout_sec_efivar
== -1 && config
->timeout_sec_config
== 0)
764 config
->timeout_sec_efivar
++;
765 config
->timeout_sec_efivar
++;
766 efivar_set_int(L
"LoaderConfigTimeout", config
->timeout_sec_efivar
, TRUE
);
767 if (config
->timeout_sec_efivar
> 0)
768 status
= PoolPrint(L
"Menu timeout set to %d sec.",
769 config
->timeout_sec_efivar
);
771 status
= StrDuplicate(L
"Menu disabled. Hold down key at bootup to show menu.");
774 case KEYPRESS(0, 0, 'e'):
775 /* only the options of configured entries can be edited */
776 if (config
->no_editor
|| config
->entries
[idx_highlight
]->type
== LOADER_UNDEFINED
)
778 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_LIGHTGRAY
|EFI_BACKGROUND_BLACK
);
779 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
780 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1);
781 if (line_edit(config
->entries
[idx_highlight
]->options
, &config
->options_edit
, x_max
-1, y_max
-1))
783 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, y_max
-1);
784 uefi_call_wrapper(ST
->ConOut
->OutputString
, 2, ST
->ConOut
, clearline
+1);
787 case KEYPRESS(0, 0, 'v'):
788 status
= PoolPrint(L
"systemd-boot " PACKAGE_VERSION
" (" EFI_MACHINE_TYPE_NAME
"), UEFI Specification %d.%02d, Vendor %s %d.%02d",
789 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff,
790 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
793 case KEYPRESS(0, 0, 'P'):
794 print_status(config
, loaded_image_path
);
798 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, 'l'):
799 case KEYPRESS(EFI_CONTROL_PRESSED
, 0, CHAR_CTRL('l')):
804 /* jump with a hotkey directly to a matching entry */
805 idx
= entry_lookup_key(config
, idx_highlight
+1, KEYCHAR(key
));
812 if (idx_highlight
> idx_last
) {
813 idx_last
= idx_highlight
;
814 idx_first
= 1 + idx_highlight
- visible_max
;
816 } else if (idx_highlight
< idx_first
) {
817 idx_first
= idx_highlight
;
818 idx_last
= idx_highlight
+ visible_max
-1;
822 if (!refresh
&& idx_highlight
!= idx_highlight_prev
)
826 *chosen_entry
= config
->entries
[idx_highlight
];
828 for (i
= 0; i
< config
->entry_count
; i
++)
833 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
, EFI_WHITE
|EFI_BACKGROUND_BLACK
);
834 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
838 static VOID
config_add_entry(Config
*config
, ConfigEntry
*entry
) {
839 if ((config
->entry_count
& 15) == 0) {
842 i
= config
->entry_count
+ 16;
843 if (config
->entry_count
== 0)
844 config
->entries
= AllocatePool(sizeof(VOID
*) * i
);
846 config
->entries
= ReallocatePool(config
->entries
,
847 sizeof(VOID
*) * config
->entry_count
, sizeof(VOID
*) * i
);
849 config
->entries
[config
->entry_count
++] = entry
;
852 static VOID
config_entry_free(ConfigEntry
*entry
) {
853 FreePool(entry
->title_show
);
854 FreePool(entry
->title
);
855 FreePool(entry
->machine_id
);
856 FreePool(entry
->loader
);
857 FreePool(entry
->options
);
860 static BOOLEAN
is_digit(CHAR16 c
) {
861 return (c
>= '0') && (c
<= '9');
864 static UINTN
c_order(CHAR16 c
) {
869 else if ((c
>= 'a') && (c
<= 'z'))
875 static INTN
str_verscmp(CHAR16
*s1
, CHAR16
*s2
) {
882 while ((*s1
&& !is_digit(*s1
)) || (*s2
&& !is_digit(*s2
))) {
885 order
= c_order(*s1
) - c_order(*s2
);
898 while (is_digit(*s1
) && is_digit(*s2
)) {
914 return StrCmp(os1
, os2
);
917 static CHAR8
*line_get_key_value(CHAR8
*content
, CHAR8
*sep
, UINTN
*pos
, CHAR8
**key_ret
, CHAR8
**value_ret
) {
923 line
= content
+ *pos
;
928 while (line
[linelen
] && !strchra((CHAR8
*)"\n\r", line
[linelen
]))
931 /* move pos to next line */
941 line
[linelen
] = '\0';
943 /* remove leading whitespace */
944 while (strchra((CHAR8
*)" \t", *line
)) {
949 /* remove trailing whitespace */
950 while (linelen
> 0 && strchra(sep
, line
[linelen
-1]))
952 line
[linelen
] = '\0';
957 /* split key/value */
959 while (*value
&& !strchra(sep
, *value
))
965 while (*value
&& strchra(sep
, *value
))
969 if (value
[0] == '\"' && line
[linelen
-1] == '\"') {
971 line
[linelen
-1] = '\0';
979 static VOID
config_defaults_load_from_file(Config
*config
, CHAR8
*content
) {
985 while ((line
= line_get_key_value(content
, (CHAR8
*)" \t", &pos
, &key
, &value
))) {
986 if (strcmpa((CHAR8
*)"timeout", key
) == 0) {
989 s
= stra_to_str(value
);
990 config
->timeout_sec_config
= Atoi(s
);
991 config
->timeout_sec
= config
->timeout_sec_config
;
996 if (strcmpa((CHAR8
*)"default", key
) == 0) {
997 FreePool(config
->entry_default_pattern
);
998 config
->entry_default_pattern
= stra_to_str(value
);
999 StrLwr(config
->entry_default_pattern
);
1003 if (strcmpa((CHAR8
*)"editor", key
) == 0) {
1006 if (EFI_ERROR(parse_boolean(value
, &on
)))
1008 config
->no_editor
= !on
;
1013 static VOID
config_entry_add_from_file(Config
*config
, EFI_HANDLE
*device
, CHAR16
*file
, CHAR8
*content
, CHAR16
*loaded_image_path
) {
1019 CHAR16
*initrd
= NULL
;
1021 entry
= AllocateZeroPool(sizeof(ConfigEntry
));
1024 while ((line
= line_get_key_value(content
, (CHAR8
*)" \t", &pos
, &key
, &value
))) {
1025 if (strcmpa((CHAR8
*)"title", key
) == 0) {
1026 FreePool(entry
->title
);
1027 entry
->title
= stra_to_str(value
);
1031 if (strcmpa((CHAR8
*)"version", key
) == 0) {
1032 FreePool(entry
->version
);
1033 entry
->version
= stra_to_str(value
);
1037 if (strcmpa((CHAR8
*)"machine-id", key
) == 0) {
1038 FreePool(entry
->machine_id
);
1039 entry
->machine_id
= stra_to_str(value
);
1043 if (strcmpa((CHAR8
*)"linux", key
) == 0) {
1044 FreePool(entry
->loader
);
1045 entry
->type
= LOADER_LINUX
;
1046 entry
->loader
= stra_to_path(value
);
1051 if (strcmpa((CHAR8
*)"efi", key
) == 0) {
1052 entry
->type
= LOADER_EFI
;
1053 FreePool(entry
->loader
);
1054 entry
->loader
= stra_to_path(value
);
1056 /* do not add an entry for ourselves */
1057 if (StriCmp(entry
->loader
, loaded_image_path
) == 0) {
1058 entry
->type
= LOADER_UNDEFINED
;
1064 if (strcmpa((CHAR8
*)"architecture", key
) == 0) {
1065 /* do not add an entry for an EFI image of architecture not matching with that of the image */
1066 if (strcmpa((CHAR8
*)EFI_MACHINE_TYPE_NAME
, value
) != 0) {
1067 entry
->type
= LOADER_UNDEFINED
;
1073 if (strcmpa((CHAR8
*)"initrd", key
) == 0) {
1076 new = stra_to_path(value
);
1080 s
= PoolPrint(L
"%s initrd=%s", initrd
, new);
1084 initrd
= PoolPrint(L
"initrd=%s", new);
1089 if (strcmpa((CHAR8
*)"options", key
) == 0) {
1092 new = stra_to_str(value
);
1093 if (entry
->options
) {
1096 s
= PoolPrint(L
"%s %s", entry
->options
, new);
1097 FreePool(entry
->options
);
1100 entry
->options
= new;
1108 if (entry
->type
== LOADER_UNDEFINED
) {
1109 config_entry_free(entry
);
1115 /* add initrd= to options */
1116 if (entry
->type
== LOADER_LINUX
&& initrd
) {
1117 if (entry
->options
) {
1120 s
= PoolPrint(L
"%s %s", initrd
, entry
->options
);
1121 FreePool(entry
->options
);
1124 entry
->options
= initrd
;
1130 entry
->device
= device
;
1131 entry
->file
= StrDuplicate(file
);
1132 len
= StrLen(entry
->file
);
1133 /* remove ".conf" */
1135 entry
->file
[len
- 5] = '\0';
1136 StrLwr(entry
->file
);
1138 config_add_entry(config
, entry
);
1141 static VOID
config_load_defaults(Config
*config
, EFI_FILE
*root_dir
) {
1142 CHAR8
*content
= NULL
;
1147 len
= file_read(root_dir
, L
"\\loader\\loader.conf", 0, 0, &content
);
1149 config_defaults_load_from_file(config
, content
);
1152 err
= efivar_get_int(L
"LoaderConfigTimeout", &sec
);
1153 if (!EFI_ERROR(err
)) {
1154 config
->timeout_sec_efivar
= sec
;
1155 config
->timeout_sec
= sec
;
1157 config
->timeout_sec_efivar
= -1;
1160 static VOID
config_load_entries(Config
*config
, EFI_HANDLE
*device
, EFI_FILE
*root_dir
, CHAR16
*loaded_image_path
) {
1161 EFI_FILE_HANDLE entries_dir
;
1164 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &entries_dir
, L
"\\loader\\entries", EFI_FILE_MODE_READ
, 0ULL);
1165 if (!EFI_ERROR(err
)) {
1170 CHAR8
*content
= NULL
;
1173 bufsize
= sizeof(buf
);
1174 err
= uefi_call_wrapper(entries_dir
->Read
, 3, entries_dir
, &bufsize
, buf
);
1175 if (bufsize
== 0 || EFI_ERROR(err
))
1178 f
= (EFI_FILE_INFO
*) buf
;
1179 if (f
->FileName
[0] == '.')
1181 if (f
->Attribute
& EFI_FILE_DIRECTORY
)
1184 len
= StrLen(f
->FileName
);
1187 if (StriCmp(f
->FileName
+ len
- 5, L
".conf") != 0)
1189 if (StrnCmp(f
->FileName
, L
"auto-", 5) == 0)
1192 len
= file_read(entries_dir
, f
->FileName
, 0, 0, &content
);
1194 config_entry_add_from_file(config
, device
, f
->FileName
, content
, loaded_image_path
);
1197 uefi_call_wrapper(entries_dir
->Close
, 1, entries_dir
);
1201 static VOID
config_sort_entries(Config
*config
) {
1204 for (i
= 1; i
< config
->entry_count
; i
++) {
1209 for (k
= 0; k
< config
->entry_count
- i
; k
++) {
1212 if (str_verscmp(config
->entries
[k
]->file
, config
->entries
[k
+1]->file
) <= 0)
1214 entry
= config
->entries
[k
];
1215 config
->entries
[k
] = config
->entries
[k
+1];
1216 config
->entries
[k
+1] = entry
;
1224 static VOID
config_default_entry_select(Config
*config
) {
1230 * The EFI variable to specify a boot entry for the next, and only the
1231 * next reboot. The variable is always cleared directly after it is read.
1233 err
= efivar_get(L
"LoaderEntryOneShot", &var
);
1234 if (!EFI_ERROR(err
)) {
1235 BOOLEAN found
= FALSE
;
1237 for (i
= 0; i
< config
->entry_count
; i
++) {
1238 if (StrCmp(config
->entries
[i
]->file
, var
) == 0) {
1239 config
->idx_default
= i
;
1245 config
->entry_oneshot
= StrDuplicate(var
);
1246 efivar_set(L
"LoaderEntryOneShot", NULL
, TRUE
);
1253 * The EFI variable to select the default boot entry overrides the
1254 * configured pattern. The variable can be set and cleared by pressing
1255 * the 'd' key in the loader selection menu, the entry is marked with
1258 err
= efivar_get(L
"LoaderEntryDefault", &var
);
1259 if (!EFI_ERROR(err
)) {
1260 BOOLEAN found
= FALSE
;
1262 for (i
= 0; i
< config
->entry_count
; i
++) {
1263 if (StrCmp(config
->entries
[i
]->file
, var
) == 0) {
1264 config
->idx_default
= i
;
1265 config
->idx_default_efivar
= i
;
1274 config
->idx_default_efivar
= -1;
1276 if (config
->entry_count
== 0)
1280 * Match the pattern from the end of the list to the start, find last
1281 * entry (largest number) matching the given pattern.
1283 if (config
->entry_default_pattern
) {
1284 i
= config
->entry_count
;
1286 if (config
->entries
[i
]->no_autoselect
)
1288 if (MetaiMatch(config
->entries
[i
]->file
, config
->entry_default_pattern
)) {
1289 config
->idx_default
= i
;
1295 /* select the last suitable entry */
1296 i
= config
->entry_count
;
1298 if (config
->entries
[i
]->no_autoselect
)
1300 config
->idx_default
= i
;
1304 /* no entry found */
1305 config
->idx_default
= -1;
1308 /* generate a unique title, avoiding non-distinguishable menu entries */
1309 static VOID
config_title_generate(Config
*config
) {
1314 for (i
= 0; i
< config
->entry_count
; i
++) {
1317 FreePool(config
->entries
[i
]->title_show
);
1318 title
= config
->entries
[i
]->title
;
1320 title
= config
->entries
[i
]->file
;
1321 config
->entries
[i
]->title_show
= StrDuplicate(title
);
1325 for (i
= 0; i
< config
->entry_count
; i
++) {
1326 for (k
= 0; k
< config
->entry_count
; k
++) {
1329 if (StrCmp(config
->entries
[i
]->title_show
, config
->entries
[k
]->title_show
) != 0)
1333 config
->entries
[i
]->non_unique
= TRUE
;
1334 config
->entries
[k
]->non_unique
= TRUE
;
1340 /* add version to non-unique titles */
1341 for (i
= 0; i
< config
->entry_count
; i
++) {
1344 if (!config
->entries
[i
]->non_unique
)
1346 if (!config
->entries
[i
]->version
)
1349 s
= PoolPrint(L
"%s (%s)", config
->entries
[i
]->title_show
, config
->entries
[i
]->version
);
1350 FreePool(config
->entries
[i
]->title_show
);
1351 config
->entries
[i
]->title_show
= s
;
1352 config
->entries
[i
]->non_unique
= FALSE
;
1356 for (i
= 0; i
< config
->entry_count
; i
++) {
1357 for (k
= 0; k
< config
->entry_count
; k
++) {
1360 if (StrCmp(config
->entries
[i
]->title_show
, config
->entries
[k
]->title_show
) != 0)
1364 config
->entries
[i
]->non_unique
= TRUE
;
1365 config
->entries
[k
]->non_unique
= TRUE
;
1371 /* add machine-id to non-unique titles */
1372 for (i
= 0; i
< config
->entry_count
; i
++) {
1376 if (!config
->entries
[i
]->non_unique
)
1378 if (!config
->entries
[i
]->machine_id
)
1381 m
= StrDuplicate(config
->entries
[i
]->machine_id
);
1383 s
= PoolPrint(L
"%s (%s)", config
->entries
[i
]->title_show
, m
);
1384 FreePool(config
->entries
[i
]->title_show
);
1385 config
->entries
[i
]->title_show
= s
;
1386 config
->entries
[i
]->non_unique
= FALSE
;
1391 for (i
= 0; i
< config
->entry_count
; i
++) {
1392 for (k
= 0; k
< config
->entry_count
; k
++) {
1395 if (StrCmp(config
->entries
[i
]->title_show
, config
->entries
[k
]->title_show
) != 0)
1399 config
->entries
[i
]->non_unique
= TRUE
;
1400 config
->entries
[k
]->non_unique
= TRUE
;
1406 /* add file name to non-unique titles */
1407 for (i
= 0; i
< config
->entry_count
; i
++) {
1410 if (!config
->entries
[i
]->non_unique
)
1412 s
= PoolPrint(L
"%s (%s)", config
->entries
[i
]->title_show
, config
->entries
[i
]->file
);
1413 FreePool(config
->entries
[i
]->title_show
);
1414 config
->entries
[i
]->title_show
= s
;
1415 config
->entries
[i
]->non_unique
= FALSE
;
1419 static BOOLEAN
config_entry_add_call(Config
*config
, CHAR16
*title
, EFI_STATUS (*call
)(VOID
)) {
1422 entry
= AllocateZeroPool(sizeof(ConfigEntry
));
1423 entry
->title
= StrDuplicate(title
);
1425 entry
->no_autoselect
= TRUE
;
1426 config_add_entry(config
, entry
);
1430 static ConfigEntry
*config_entry_add_loader(Config
*config
, EFI_HANDLE
*device
,
1431 enum loader_type type
,CHAR16
*file
, CHAR16 key
, CHAR16
*title
, CHAR16
*loader
) {
1434 entry
= AllocateZeroPool(sizeof(ConfigEntry
));
1436 entry
->title
= StrDuplicate(title
);
1437 entry
->device
= device
;
1438 entry
->loader
= StrDuplicate(loader
);
1439 entry
->file
= StrDuplicate(file
);
1440 StrLwr(entry
->file
);
1442 config_add_entry(config
, entry
);
1447 static BOOLEAN
config_entry_add_loader_auto(Config
*config
, EFI_HANDLE
*device
, EFI_FILE
*root_dir
, CHAR16
*loaded_image_path
,
1448 CHAR16
*file
, CHAR16 key
, CHAR16
*title
, CHAR16
*loader
) {
1449 EFI_FILE_HANDLE handle
;
1453 /* do not add an entry for ourselves */
1454 if (loaded_image_path
&& StriCmp(loader
, loaded_image_path
) == 0)
1457 /* check existence */
1458 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &handle
, loader
, EFI_FILE_MODE_READ
, 0ULL);
1461 uefi_call_wrapper(handle
->Close
, 1, handle
);
1463 entry
= config_entry_add_loader(config
, device
, LOADER_UNDEFINED
, file
, key
, title
, loader
);
1467 /* do not boot right away into auto-detected entries */
1468 entry
->no_autoselect
= TRUE
;
1473 static VOID
config_entry_add_osx(Config
*config
) {
1475 UINTN handle_count
= 0;
1476 EFI_HANDLE
*handles
= NULL
;
1478 err
= LibLocateHandle(ByProtocol
, &FileSystemProtocol
, NULL
, &handle_count
, &handles
);
1479 if (!EFI_ERROR(err
)) {
1482 for (i
= 0; i
< handle_count
; i
++) {
1486 root
= LibOpenRoot(handles
[i
]);
1489 found
= config_entry_add_loader_auto(config
, handles
[i
], root
, NULL
, L
"auto-osx", 'a', L
"macOS",
1490 L
"\\System\\Library\\CoreServices\\boot.efi");
1491 uefi_call_wrapper(root
->Close
, 1, root
);
1500 static VOID
config_entry_add_linux( Config
*config
, EFI_LOADED_IMAGE
*loaded_image
, EFI_FILE
*root_dir
) {
1501 EFI_FILE_HANDLE linux_dir
;
1505 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &linux_dir
, L
"\\EFI\\Linux", EFI_FILE_MODE_READ
, 0ULL);
1506 if (!EFI_ERROR(err
)) {
1511 CHAR8
*sections
[] = {
1513 (UINT8
*)".cmdline",
1516 UINTN offs
[ELEMENTSOF(sections
)-1] = {};
1517 UINTN szs
[ELEMENTSOF(sections
)-1] = {};
1518 UINTN addrs
[ELEMENTSOF(sections
)-1] = {};
1519 CHAR8
*content
= NULL
;
1524 CHAR16
*os_name
= NULL
;
1525 CHAR16
*os_id
= NULL
;
1526 CHAR16
*os_version
= NULL
;
1527 CHAR16
*os_build
= NULL
;
1529 bufsize
= sizeof(buf
);
1530 err
= uefi_call_wrapper(linux_dir
->Read
, 3, linux_dir
, &bufsize
, buf
);
1531 if (bufsize
== 0 || EFI_ERROR(err
))
1534 f
= (EFI_FILE_INFO
*) buf
;
1535 if (f
->FileName
[0] == '.')
1537 if (f
->Attribute
& EFI_FILE_DIRECTORY
)
1539 len
= StrLen(f
->FileName
);
1542 if (StriCmp(f
->FileName
+ len
- 4, L
".efi") != 0)
1545 /* look for .osrel and .cmdline sections in the .efi binary */
1546 err
= pe_file_locate_sections(linux_dir
, f
->FileName
, sections
, addrs
, offs
, szs
);
1550 len
= file_read(linux_dir
, f
->FileName
, offs
[0], szs
[0], &content
);
1554 /* read properties from the embedded os-release file */
1556 while ((line
= line_get_key_value(content
, (CHAR8
*)"=", &pos
, &key
, &value
))) {
1557 if (strcmpa((CHAR8
*)"PRETTY_NAME", key
) == 0) {
1559 os_name
= stra_to_str(value
);
1563 if (strcmpa((CHAR8
*)"ID", key
) == 0) {
1565 os_id
= stra_to_str(value
);
1569 if (strcmpa((CHAR8
*)"VERSION_ID", key
) == 0) {
1570 FreePool(os_version
);
1571 os_version
= stra_to_str(value
);
1575 if (strcmpa((CHAR8
*)"BUILD_ID", key
) == 0) {
1577 os_build
= stra_to_str(value
);
1582 if (os_name
&& os_id
&& (os_version
|| os_build
)) {
1587 conf
= PoolPrint(L
"%s-%s", os_id
, os_version
? : os_build
);
1588 path
= PoolPrint(L
"\\EFI\\Linux\\%s", f
->FileName
);
1589 entry
= config_entry_add_loader(config
, loaded_image
->DeviceHandle
, LOADER_LINUX
, conf
, 'l', os_name
, path
);
1592 /* read the embedded cmdline file */
1593 len
= file_read(linux_dir
, f
->FileName
, offs
[1], szs
[1] - 1 , &content
);
1595 cmdline
= stra_to_str(content
);
1596 entry
->options
= cmdline
;
1606 FreePool(os_version
);
1610 uefi_call_wrapper(linux_dir
->Close
, 1, linux_dir
);
1614 static EFI_STATUS
image_start(EFI_HANDLE parent_image
, const Config
*config
, const ConfigEntry
*entry
) {
1616 EFI_DEVICE_PATH
*path
;
1620 path
= FileDevicePath(entry
->device
, entry
->loader
);
1622 Print(L
"Error getting device path.");
1623 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1624 return EFI_INVALID_PARAMETER
;
1627 err
= uefi_call_wrapper(BS
->LoadImage
, 6, FALSE
, parent_image
, path
, NULL
, 0, &image
);
1628 if (EFI_ERROR(err
)) {
1629 Print(L
"Error loading %s: %r", entry
->loader
, err
);
1630 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1634 if (config
->options_edit
)
1635 options
= config
->options_edit
;
1636 else if (entry
->options
)
1637 options
= entry
->options
;
1641 EFI_LOADED_IMAGE
*loaded_image
;
1643 err
= uefi_call_wrapper(BS
->OpenProtocol
, 6, image
, &LoadedImageProtocol
, (VOID
**)&loaded_image
,
1644 parent_image
, NULL
, EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
1645 if (EFI_ERROR(err
)) {
1646 Print(L
"Error getting LoadedImageProtocol handle: %r", err
);
1647 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1650 loaded_image
->LoadOptions
= options
;
1651 loaded_image
->LoadOptionsSize
= (StrLen(loaded_image
->LoadOptions
)+1) * sizeof(CHAR16
);
1654 /* Try to log any options to the TPM, especially to catch manually edited options */
1655 err
= tpm_log_event(SD_TPM_PCR
,
1656 (EFI_PHYSICAL_ADDRESS
) loaded_image
->LoadOptions
,
1657 loaded_image
->LoadOptionsSize
, loaded_image
->LoadOptions
);
1658 if (EFI_ERROR(err
)) {
1659 Print(L
"Unable to add image options measurement: %r", err
);
1660 uefi_call_wrapper(BS
->Stall
, 1, 200 * 1000);
1665 efivar_set_time_usec(L
"LoaderTimeExecUSec", 0);
1666 err
= uefi_call_wrapper(BS
->StartImage
, 3, image
, NULL
, NULL
);
1668 uefi_call_wrapper(BS
->UnloadImage
, 1, image
);
1674 static EFI_STATUS
reboot_into_firmware(VOID
) {
1680 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
1682 err
= efivar_get_raw(&global_guid
, L
"OsIndications", &b
, &size
);
1683 if (!EFI_ERROR(err
))
1684 osind
|= (UINT64
)*b
;
1687 err
= efivar_set_raw(&global_guid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
1691 err
= uefi_call_wrapper(RT
->ResetSystem
, 4, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1692 Print(L
"Error calling ResetSystem: %r", err
);
1693 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1697 static VOID
config_free(Config
*config
) {
1700 for (i
= 0; i
< config
->entry_count
; i
++)
1701 config_entry_free(config
->entries
[i
]);
1702 FreePool(config
->entries
);
1703 FreePool(config
->entry_default_pattern
);
1704 FreePool(config
->options_edit
);
1705 FreePool(config
->entry_oneshot
);
1708 EFI_STATUS
efi_main(EFI_HANDLE image
, EFI_SYSTEM_TABLE
*sys_table
) {
1712 EFI_LOADED_IMAGE
*loaded_image
;
1714 CHAR16
*loaded_image_path
;
1718 BOOLEAN menu
= FALSE
;
1721 InitializeLib(image
, sys_table
);
1722 init_usec
= time_usec();
1723 efivar_set_time_usec(L
"LoaderTimeInitUSec", init_usec
);
1724 efivar_set(L
"LoaderInfo", L
"systemd-boot " PACKAGE_VERSION
, FALSE
);
1725 s
= PoolPrint(L
"%s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
1726 efivar_set(L
"LoaderFirmwareInfo", s
, FALSE
);
1728 s
= PoolPrint(L
"UEFI %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
1729 efivar_set(L
"LoaderFirmwareType", s
, FALSE
);
1732 err
= uefi_call_wrapper(BS
->OpenProtocol
, 6, image
, &LoadedImageProtocol
, (VOID
**)&loaded_image
,
1733 image
, NULL
, EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
1734 if (EFI_ERROR(err
)) {
1735 Print(L
"Error getting a LoadedImageProtocol handle: %r ", err
);
1736 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1740 /* export the device path this image is started from */
1741 if (disk_get_part_uuid(loaded_image
->DeviceHandle
, uuid
) == EFI_SUCCESS
)
1742 efivar_set(L
"LoaderDevicePartUUID", uuid
, FALSE
);
1744 root_dir
= LibOpenRoot(loaded_image
->DeviceHandle
);
1746 Print(L
"Unable to open root directory: %r ", err
);
1747 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1748 return EFI_LOAD_ERROR
;
1751 if (secure_boot_enabled() && shim_loaded()) {
1752 err
= security_policy_install();
1753 if (EFI_ERROR(err
)) {
1754 Print(L
"Error installing security policy: %r ", err
);
1755 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1760 /* the filesystem path to this image, to prevent adding ourselves to the menu */
1761 loaded_image_path
= DevicePathToStr(loaded_image
->FilePath
);
1762 efivar_set(L
"LoaderImageIdentifier", loaded_image_path
, FALSE
);
1764 ZeroMem(&config
, sizeof(Config
));
1765 config_load_defaults(&config
, root_dir
);
1767 /* scan /EFI/Linux/ directory */
1768 config_entry_add_linux(&config
, loaded_image
, root_dir
);
1770 /* scan /loader/entries/\*.conf files */
1771 config_load_entries(&config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
);
1773 /* sort entries after version number */
1774 config_sort_entries(&config
);
1776 /* if we find some well-known loaders, add them to the end of the list */
1777 config_entry_add_loader_auto(&config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
,
1778 L
"auto-windows", 'w', L
"Windows Boot Manager", L
"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
1779 config_entry_add_loader_auto(&config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
,
1780 L
"auto-efi-shell", 's', L
"EFI Shell", L
"\\shell" EFI_MACHINE_TYPE_NAME
".efi");
1781 config_entry_add_loader_auto(&config
, loaded_image
->DeviceHandle
, root_dir
, loaded_image_path
,
1782 L
"auto-efi-default", '\0', L
"EFI Default Loader", L
"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME
".efi");
1783 config_entry_add_osx(&config
);
1785 if (efivar_get_raw(&global_guid
, L
"OsIndicationsSupported", &b
, &size
) == EFI_SUCCESS
) {
1786 UINT64 osind
= (UINT64
)*b
;
1788 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
)
1789 config_entry_add_call(&config
, L
"Reboot Into Firmware Interface", reboot_into_firmware
);
1793 if (config
.entry_count
== 0) {
1794 Print(L
"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
1795 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1799 config_title_generate(&config
);
1801 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
1802 config_default_entry_select(&config
);
1804 /* if no configured entry to select from was found, enable the menu */
1805 if (config
.idx_default
== -1) {
1806 config
.idx_default
= 0;
1807 if (config
.timeout_sec
== 0)
1808 config
.timeout_sec
= 10;
1811 /* select entry or show menu when key is pressed or timeout is set */
1812 if (config
.timeout_sec
== 0) {
1815 err
= console_key_read(&key
, FALSE
);
1816 if (!EFI_ERROR(err
)) {
1819 /* find matching key in config entries */
1820 idx
= entry_lookup_key(&config
, config
.idx_default
, KEYCHAR(key
));
1822 config
.idx_default
= idx
;
1832 entry
= config
.entries
[config
.idx_default
];
1834 efivar_set_time_usec(L
"LoaderTimeMenuUSec", 0);
1835 uefi_call_wrapper(BS
->SetWatchdogTimer
, 4, 0, 0x10000, 0, NULL
);
1836 if (!menu_run(&config
, &entry
, loaded_image_path
))
1839 /* run special entry like "reboot" */
1846 /* export the selected boot entry to the system */
1847 efivar_set(L
"LoaderEntrySelected", entry
->file
, FALSE
);
1849 uefi_call_wrapper(BS
->SetWatchdogTimer
, 4, 5 * 60, 0x10000, 0, NULL
);
1850 err
= image_start(image
, &config
, entry
);
1851 if (EFI_ERROR(err
)) {
1852 graphics_mode(FALSE
);
1853 Print(L
"\nFailed to execute %s (%s): %r\n", entry
->title
, entry
->loader
, err
);
1854 uefi_call_wrapper(BS
->Stall
, 1, 3 * 1000 * 1000);
1859 config
.timeout_sec
= 0;
1863 FreePool(loaded_image_path
);
1864 config_free(&config
);
1865 uefi_call_wrapper(root_dir
->Close
, 1, root_dir
);
1866 uefi_call_wrapper(BS
->CloseProtocol
, 4, image
, &LoadedImageProtocol
, image
, NULL
);