2 * Simple UEFI boot loader which executes configured EFI images, where the
3 * default entry is selected by a configured pattern (glob) or an on-screen
6 * All gummiboot code is LGPL not GPL, to stay out of politics and to give
7 * the freedom of copying code from programs to possible future libraries.
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
20 * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
22 * "Any intelligent fool can make things bigger, more complex, and more violent."
29 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
30 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
33 #ifndef EFI_SECURITY_VIOLATION
34 #define EFI_SECURITY_VIOLATION EFIERR(26)
37 /* magic string to find in the binary image */
38 static const char __attribute__((used)) magic[] = "#### LoaderInfo: gummiboot " VERSION " ####";
41 * Allocated random UUID, intended to be shared across tools that implement
42 * the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
43 * associated EFI variables.
45 static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
47 static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
62 enum loader_type type;
66 EFI_STATUS (*call)(void);
67 BOOLEAN no_autoselect;
72 ConfigEntry **entries;
75 INTN idx_default_efivar;
77 UINTN timeout_sec_config;
78 INTN timeout_sec_efivar;
79 CHAR16 *entry_default_pattern;
80 CHAR16 *entry_oneshot;
85 static CHAR16 *stra_to_str(CHAR8 *stra);
88 static UINT64 ticks_read(void) {
90 __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
94 static UINT64 ticks_read(void) {
96 __asm__ volatile ("rdtsc" : "=A" (val));
101 /* count TSC ticks during a millisecond delay */
102 static UINT64 ticks_freq(void) {
103 UINT64 ticks_start, ticks_end;
105 ticks_start = ticks_read();
106 uefi_call_wrapper(BS->Stall, 1, 1000);
107 ticks_end = ticks_read();
109 return (ticks_end - ticks_start) * 1000;
112 static UINT64 time_usec(void) {
116 ticks = ticks_read();
126 return 1000 * 1000 * ticks / freq;
129 static EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
132 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
134 flags |= EFI_VARIABLE_NON_VOLATILE;
136 return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf);
139 static EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
140 return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
143 static EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
148 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
149 buf = AllocatePool(l);
151 return EFI_OUT_OF_RESOURCES;
153 err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf);
154 if (!EFI_ERROR(err)) {
164 static EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
170 err = efivar_get_raw(&loader_guid, name, &buf, &size);
174 val = StrDuplicate((CHAR16 *)buf);
177 return EFI_OUT_OF_RESOURCES;
184 static EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
187 SPrint(str, 32, L"%d", i);
188 return efivar_set(name, str, persistent);
191 static EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
195 err = efivar_get(name, &val);
196 if (!EFI_ERROR(err)) {
203 static VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
211 SPrint(str, 32, L"%ld", usec);
212 efivar_set(name, str, FALSE);
215 #define EFI_SHIFT_STATE_VALID 0x80000000
216 #define EFI_RIGHT_CONTROL_PRESSED 0x00000004
217 #define EFI_LEFT_CONTROL_PRESSED 0x00000008
218 #define EFI_RIGHT_ALT_PRESSED 0x00000010
219 #define EFI_LEFT_ALT_PRESSED 0x00000020
220 #define EFI_CONTROL_PRESSED (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED)
221 #define EFI_ALT_PRESSED (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED)
222 #define KEYPRESS(keys, scan, uni) ((((UINT64)keys) << 32) | ((scan) << 16) | (uni))
223 #define KEYCHAR(k) ((k) & 0xffff)
224 #define CHAR_CTRL(c) ((c) - 'a' + 1)
226 static EFI_STATUS key_read(UINT64 *key, BOOLEAN wait) {
227 #define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
228 { 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } }
230 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
232 typedef EFI_STATUS (EFIAPI *EFI_INPUT_RESET_EX)(
233 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
234 BOOLEAN ExtendedVerification;
237 typedef UINT8 EFI_KEY_TOGGLE_STATE;
240 UINT32 KeyShiftState;
241 EFI_KEY_TOGGLE_STATE KeyToggleState;
246 EFI_KEY_STATE KeyState;
249 typedef EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY_EX)(
250 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
251 EFI_KEY_DATA *KeyData;
254 typedef EFI_STATUS (EFIAPI *EFI_SET_STATE)(
255 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
256 EFI_KEY_TOGGLE_STATE *KeyToggleState;
259 typedef EFI_STATUS (EFIAPI *EFI_KEY_NOTIFY_FUNCTION)(
260 EFI_KEY_DATA *KeyData;
263 typedef EFI_STATUS (EFIAPI *EFI_REGISTER_KEYSTROKE_NOTIFY)(
264 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
265 EFI_KEY_DATA KeyData;
266 EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction;
270 typedef EFI_STATUS (EFIAPI *EFI_UNREGISTER_KEYSTROKE_NOTIFY)(
271 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
272 VOID *NotificationHandle;
275 typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
276 EFI_INPUT_RESET_EX Reset;
277 EFI_INPUT_READ_KEY_EX ReadKeyStrokeEx;
278 EFI_EVENT WaitForKeyEx;
279 EFI_SET_STATE SetState;
280 EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify;
281 EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify;
282 } EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
284 EFI_GUID EfiSimpleTextInputExProtocolGuid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
285 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx;
286 static BOOLEAN checked;
292 err = LibLocateProtocol(&EfiSimpleTextInputExProtocolGuid, (VOID **)&TextInputEx);
299 /* wait until key is pressed */
302 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &TextInputEx->WaitForKeyEx, &index);
304 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
308 EFI_KEY_DATA keydata;
311 err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata);
312 if (!EFI_ERROR(err)) {
315 /* do not distinguish between left and right keys */
316 if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
317 if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
318 shift |= EFI_CONTROL_PRESSED;
319 if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
320 shift |= EFI_ALT_PRESSED;
323 /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
324 keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
332 /* fallback for firmware which does not support SimpleTextInputExProtocol
334 * This is also called in case ReadKeyStrokeEx did not return a key, because
335 * some broken firmwares offer SimpleTextInputExProtocol, but never acually
337 err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
341 *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
345 static void cursor_left(UINTN *cursor, UINTN *first)
349 else if ((*first) > 0)
353 static void cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
355 if ((*cursor)+1 < x_max)
357 else if ((*first) + (*cursor) < len)
361 static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
374 size = StrLen(line_in) + 1024;
375 line = AllocatePool(size * sizeof(CHAR16));
376 StrCpy(line, line_in);
378 print = AllocatePool((x_max+1) * sizeof(CHAR16));
380 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
395 CopyMem(print, line + first, i * sizeof(CHAR16));
396 while (clear > 0 && i < x_max-1) {
402 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
403 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
404 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
406 err = key_read(&key, TRUE);
411 case KEYPRESS(0, SCAN_ESC, 0):
412 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
413 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
414 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
415 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
419 case KEYPRESS(0, SCAN_HOME, 0):
420 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
421 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
422 /* beginning-of-line */
427 case KEYPRESS(0, SCAN_END, 0):
428 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
429 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
431 cursor = len - first;
432 if (cursor+1 >= x_max) {
434 first = len - (x_max-1);
438 case KEYPRESS(0, SCAN_DOWN, 0):
439 case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
440 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
442 while (line[first + cursor] && line[first + cursor] == ' ')
443 cursor_right(&cursor, &first, x_max, len);
444 while (line[first + cursor] && line[first + cursor] != ' ')
445 cursor_right(&cursor, &first, x_max, len);
446 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
449 case KEYPRESS(0, SCAN_UP, 0):
450 case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
451 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
453 if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
454 cursor_left(&cursor, &first);
455 while ((first + cursor) > 0 && line[first + cursor] == ' ')
456 cursor_left(&cursor, &first);
458 while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
459 cursor_left(&cursor, &first);
460 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
463 case KEYPRESS(0, SCAN_RIGHT, 0):
464 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
465 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
467 if (first + cursor == len)
469 cursor_right(&cursor, &first, x_max, len);
470 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
473 case KEYPRESS(0, SCAN_LEFT, 0):
474 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
475 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
477 cursor_left(&cursor, &first);
478 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
481 case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
484 for (i = first + cursor; i < len && line[i] == ' '; i++)
486 for (; i < len && line[i] != ' '; i++)
489 for (i = first + cursor; i + clear < len; i++)
490 line[i] = line[i + clear];
495 case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE):
496 /* backward-kill-word */
498 if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
499 cursor_left(&cursor, &first);
501 while ((first + cursor) > 0 && line[first + cursor] == ' ') {
502 cursor_left(&cursor, &first);
506 while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
507 cursor_left(&cursor, &first);
510 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
512 for (i = first + cursor; i + clear < len; i++)
513 line[i] = line[i + clear];
518 case KEYPRESS(0, SCAN_DELETE, 0):
519 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
520 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
523 if (first + cursor == len)
525 for (i = first + cursor; i < len; i++)
531 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
532 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
534 line[first + cursor] = '\0';
535 clear = len - (first + cursor);
536 len = first + cursor;
539 case KEYPRESS(0, 0, CHAR_LINEFEED):
540 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
541 if (StrCmp(line, line_in) != 0) {
549 case KEYPRESS(0, 0, CHAR_BACKSPACE):
552 if (first == 0 && cursor == 0)
554 for (i = first + cursor-1; i < len; i++)
560 if (cursor > 0 || first == 0)
562 /* show full line if it fits */
568 /* jump left to see what we delete */
578 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
579 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
582 for (i = len; i > first + cursor; i--)
584 line[first + cursor] = KEYCHAR(key);
587 if (cursor+1 < x_max)
589 else if (first + cursor < len)
595 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
601 static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
607 /* select entry by number key */
608 if (key >= '1' && key <= '9') {
610 if (i > config->entry_count)
611 i = config->entry_count;
615 /* find matching key in config entries */
616 for (i = start; i < config->entry_count; i++)
617 if (config->entries[i]->key == key)
620 for (i = 0; i < start; i++)
621 if (config->entries[i]->key == key)
627 static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
634 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
635 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
637 Print(L"gummiboot version: " VERSION "\n");
638 Print(L"loaded image: %s\n", loaded_image_path);
639 Print(L"UEFI version: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
640 Print(L"firmware vendor: %s\n", ST->FirmwareVendor);
641 Print(L"firmware version: %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
642 if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
643 Print(L"SecureBoot: %s\n", *b > 0 ? L"enabled" : L"disabled");
647 if (efivar_get_raw(&global_guid, L"SetupMode", &b, &size) == EFI_SUCCESS) {
648 Print(L"SetupMode: %s\n", *b > 0 ? L"setup" : L"user");
652 if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
653 Print(L"OsIndicationsSupported: %d\n", (UINT64)*b);
658 Print(L"timeout: %d\n", config->timeout_sec);
659 if (config->timeout_sec_efivar >= 0)
660 Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar);
661 Print(L"timeout (config): %d\n", config->timeout_sec_config);
662 if (config->entry_default_pattern)
663 Print(L"default pattern: '%s'\n", config->entry_default_pattern);
666 Print(L"config entry count: %d\n", config->entry_count);
667 Print(L"entry selected idx: %d\n", config->idx_default);
668 if (config->idx_default_efivar >= 0)
669 Print(L"entry EFI var idx: %d\n", config->idx_default_efivar);
672 if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS)
673 Print(L"LoaderConfigTimeout: %d\n", i);
674 if (config->entry_oneshot)
675 Print(L"LoaderEntryOneShot: %s\n", config->entry_oneshot);
676 if (efivar_get(L"LoaderDeviceIdentifier", &s) == EFI_SUCCESS) {
677 Print(L"LoaderDeviceIdentifier: %s\n", s);
680 if (efivar_get(L"LoaderDevicePartUUID", &s) == EFI_SUCCESS) {
681 Print(L"LoaderDevicePartUUID: %s\n", s);
684 if (efivar_get(L"LoaderEntryDefault", &s) == EFI_SUCCESS) {
685 Print(L"LoaderEntryDefault: %s\n", s);
689 Print(L"\n--- press key ---\n\n");
690 key_read(&key, TRUE);
692 for (i = 0; i < config->entry_count; i++) {
695 if (key == KEYPRESS(0, SCAN_ESC, 0) || key == KEYPRESS(0, 0, 'q'))
698 entry = config->entries[i];
699 Print(L"config entry: %d/%d\n", i+1, config->entry_count);
701 Print(L"file '%s'\n", entry->file);
702 Print(L"title show '%s'\n", entry->title_show);
704 Print(L"title '%s'\n", entry->title);
706 Print(L"version '%s'\n", entry->version);
707 if (entry->machine_id)
708 Print(L"machine-id '%s'\n", entry->machine_id);
710 EFI_DEVICE_PATH *device_path;
713 device_path = DevicePathFromHandle(entry->device);
715 str = DevicePathToStr(device_path);
716 Print(L"device handle '%s'\n", str);
721 Print(L"loader '%s'\n", entry->loader);
723 Print(L"options '%s'\n", entry->options);
724 Print(L"auto-select %s\n", entry->no_autoselect ? L"no" : L"yes");
726 Print(L"internal call yes\n");
728 Print(L"\n--- press key ---\n\n");
729 key_read(&key, TRUE);
732 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
735 static EFI_STATUS console_text_mode(VOID) {
736 #define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \
737 { 0xf42f7782, 0x12e, 0x4c12, { 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21 } };
739 struct _EFI_CONSOLE_CONTROL_PROTOCOL;
742 EfiConsoleControlScreenText,
743 EfiConsoleControlScreenGraphics,
744 EfiConsoleControlScreenMaxValue,
745 } EFI_CONSOLE_CONTROL_SCREEN_MODE;
747 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE)(
748 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
749 EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode,
754 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE)(
755 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
756 EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
759 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN)(
760 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
764 typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
765 EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode;
766 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
767 EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn;
768 } EFI_CONSOLE_CONTROL_PROTOCOL;
770 EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
771 EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
774 err = LibLocateProtocol(&ConsoleControlProtocolGuid, (VOID **)&ConsoleControl);
777 return uefi_call_wrapper(ConsoleControl->SetMode, 2, ConsoleControl, EfiConsoleControlScreenText);
780 static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *loaded_image_path) {
784 UINTN idx_highlight_prev;
800 BOOLEAN exit = FALSE;
802 BOOLEAN wait = FALSE;
805 uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
806 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
807 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
808 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
810 err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
811 if (EFI_ERROR(err)) {
816 /* we check 10 times per second for a keystroke */
817 if (config->timeout_sec > 0)
818 timeout_remain = config->timeout_sec * 10;
822 idx_highlight = config->idx_default;
823 idx_highlight_prev = 0;
825 visible_max = y_max - 2;
827 if ((UINTN)config->idx_default >= visible_max)
828 idx_first = config->idx_default-1;
832 idx_last = idx_first + visible_max-1;
837 /* length of the longest entry */
839 for (i = 0; i < config->entry_count; i++) {
842 entry_len = StrLen(config->entries[i]->title_show);
843 if (line_width < entry_len)
844 line_width = entry_len;
846 if (line_width > x_max-6)
847 line_width = x_max-6;
849 /* offsets to center the entries on the screen */
850 x_start = (x_max - (line_width)) / 2;
851 if (config->entry_count < visible_max)
852 y_start = ((visible_max - config->entry_count) / 2) + 1;
856 /* menu entries title lines */
857 lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
858 for (i = 0; i < config->entry_count; i++) {
861 lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
862 for (j = 0; j < x_start; j++)
865 for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
866 lines[i][j] = config->entries[i]->title_show[k];
868 for (; j < x_max; j++)
870 lines[i][x_max] = '\0';
874 clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
875 for (i = 0; i < x_max; i++)
883 for (i = 0; i < config->entry_count; i++) {
884 if (i < idx_first || i > idx_last)
886 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
887 if (i == idx_highlight)
888 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
889 EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
891 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
892 EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
893 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
894 if ((INTN)i == config->idx_default_efivar) {
895 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
896 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
900 } else if (highlight) {
901 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
902 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
903 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
904 if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
905 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
906 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
909 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
910 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
911 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
912 if ((INTN)idx_highlight == config->idx_default_efivar) {
913 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
914 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
919 if (timeout_remain > 0) {
921 status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
924 /* print status at last line of screen */
930 len = StrLen(status);
932 x = (x_max - len) / 2;
935 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
936 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
937 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
938 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
939 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
942 err = key_read(&key, wait);
943 if (EFI_ERROR(err)) {
944 /* timeout reached */
945 if (timeout_remain == 0) {
950 /* sleep and update status */
951 if (timeout_remain > 0) {
952 uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
957 /* timeout disabled, wait for next key */
964 /* clear status after keystroke */
968 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
969 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
970 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
973 idx_highlight_prev = idx_highlight;
976 case KEYPRESS(0, SCAN_UP, 0):
977 case KEYPRESS(0, 0, 'k'):
978 if (idx_highlight > 0)
982 case KEYPRESS(0, SCAN_DOWN, 0):
983 case KEYPRESS(0, 0, 'j'):
984 if (idx_highlight < config->entry_count-1)
988 case KEYPRESS(0, SCAN_HOME, 0):
989 case KEYPRESS(EFI_ALT_PRESSED, 0, '<'):
990 if (idx_highlight > 0) {
996 case KEYPRESS(0, SCAN_END, 0):
997 case KEYPRESS(EFI_ALT_PRESSED, 0, '>'):
998 if (idx_highlight < config->entry_count-1) {
1000 idx_highlight = config->entry_count-1;
1004 case KEYPRESS(0, SCAN_PAGE_UP, 0):
1005 if (idx_highlight > visible_max)
1006 idx_highlight -= visible_max;
1011 case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
1012 idx_highlight += visible_max;
1013 if (idx_highlight > config->entry_count-1)
1014 idx_highlight = config->entry_count-1;
1017 case KEYPRESS(0, 0, CHAR_LINEFEED):
1018 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
1022 case KEYPRESS(0, SCAN_F1, 0):
1023 case KEYPRESS(0, 0, 'h'):
1024 case KEYPRESS(0, 0, '?'):
1025 status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
1028 case KEYPRESS(0, 0, 'Q'):
1033 case KEYPRESS(0, 0, 'd'):
1034 if (config->idx_default_efivar != (INTN)idx_highlight) {
1035 /* store the selected entry in a persistent EFI variable */
1036 efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
1037 config->idx_default_efivar = idx_highlight;
1038 status = StrDuplicate(L"Default boot entry selected.");
1040 /* clear the default entry EFI variable */
1041 efivar_set(L"LoaderEntryDefault", NULL, TRUE);
1042 config->idx_default_efivar = -1;
1043 status = StrDuplicate(L"Default boot entry cleared.");
1048 case KEYPRESS(0, 0, '-'):
1049 case KEYPRESS(0, 0, 'T'):
1050 if (config->timeout_sec_efivar > 0) {
1051 config->timeout_sec_efivar--;
1052 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
1053 if (config->timeout_sec_efivar > 0)
1054 status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
1056 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1057 } else if (config->timeout_sec_efivar <= 0){
1058 config->timeout_sec_efivar = -1;
1059 efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
1060 if (config->timeout_sec_config > 0)
1061 status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
1062 config->timeout_sec_config);
1064 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1068 case KEYPRESS(0, 0, '+'):
1069 case KEYPRESS(0, 0, 't'):
1070 if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
1071 config->timeout_sec_efivar++;
1072 config->timeout_sec_efivar++;
1073 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
1074 if (config->timeout_sec_efivar > 0)
1075 status = PoolPrint(L"Menu timeout set to %d sec.",
1076 config->timeout_sec_efivar);
1078 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1081 case KEYPRESS(0, 0, 'e'):
1082 /* only the options of configured entries can be edited */
1083 if (config->entries[idx_highlight]->type == LOADER_UNDEFINED)
1085 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
1086 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
1087 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
1088 if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
1090 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
1091 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
1094 case KEYPRESS(0, 0, 'v'):
1095 status = PoolPrint(L"gummiboot " VERSION ", UEFI %d.%02d, %s %d.%02d",
1096 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff,
1097 ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
1100 case KEYPRESS(0, 0, 'P'):
1101 print_status(config, loaded_image_path);
1105 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
1106 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
1111 /* jump with a hotkey directly to a matching entry */
1112 idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
1115 idx_highlight = idx;
1119 if (idx_highlight > idx_last) {
1120 idx_last = idx_highlight;
1121 idx_first = 1 + idx_highlight - visible_max;
1124 if (idx_highlight < idx_first) {
1125 idx_first = idx_highlight;
1126 idx_last = idx_highlight + visible_max-1;
1130 idx_last = idx_first + visible_max-1;
1132 if (!refresh && idx_highlight != idx_highlight_prev)
1136 *chosen_entry = config->entries[idx_highlight];
1138 for (i = 0; i < config->entry_count; i++)
1141 FreePool(clearline);
1143 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
1144 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
1148 static VOID config_add_entry(Config *config, ConfigEntry *entry) {
1149 if ((config->entry_count & 15) == 0) {
1152 i = config->entry_count + 16;
1153 if (config->entry_count == 0)
1154 config->entries = AllocatePool(sizeof(VOID *) * i);
1156 config->entries = ReallocatePool(config->entries,
1157 sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
1159 config->entries[config->entry_count++] = entry;
1162 static VOID config_entry_free(ConfigEntry *entry) {
1163 FreePool(entry->title_show);
1164 FreePool(entry->title);
1165 FreePool(entry->machine_id);
1166 FreePool(entry->loader);
1167 FreePool(entry->options);
1170 static BOOLEAN is_digit(CHAR16 c)
1172 return (c >= '0') && (c <= '9');
1175 static UINTN c_order(CHAR16 c)
1181 else if ((c >= 'a') && (c <= 'z'))
1187 static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
1192 while (*s1 || *s2) {
1195 while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
1198 order = c_order(*s1) - c_order(*s2);
1211 while (is_digit(*s1) && is_digit(*s2)) {
1227 return StrCmp(os1, os2);
1230 static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
1237 else if ((stra[0] & 0xe0) == 0xc0)
1239 else if ((stra[0] & 0xf0) == 0xe0)
1241 else if ((stra[0] & 0xf8) == 0xf0)
1243 else if ((stra[0] & 0xfc) == 0xf8)
1245 else if ((stra[0] & 0xfe) == 0xfc)
1255 unichar = stra[0] & 0x1f;
1258 unichar = stra[0] & 0x0f;
1261 unichar = stra[0] & 0x07;
1264 unichar = stra[0] & 0x03;
1267 unichar = stra[0] & 0x01;
1271 for (i = 1; i < len; i++) {
1272 if ((stra[i] & 0xc0) != 0x80)
1275 unichar |= stra[i] & 0x3f;
1282 static CHAR16 *stra_to_str(CHAR8 *stra) {
1288 len = strlena(stra);
1289 str = AllocatePool((len + 1) * sizeof(CHAR16));
1296 utf8len = utf8_to_16(stra + i, str + strlen);
1298 /* invalid utf8 sequence, skip the garbage */
1310 static CHAR16 *stra_to_path(CHAR8 *stra) {
1316 len = strlena(stra);
1317 str = AllocatePool((len + 2) * sizeof(CHAR16));
1325 utf8len = utf8_to_16(stra + i, str + strlen);
1327 /* invalid utf8 sequence, skip the garbage */
1332 if (str[strlen] == '/')
1334 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
1335 /* skip double slashes */
1347 static CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
1355 static CHAR8 *line_get_key_value(CHAR8 *content, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
1361 line = content + *pos;
1366 while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
1369 /* move pos to next line */
1378 /* terminate line */
1379 line[linelen] = '\0';
1381 /* remove leading whitespace */
1382 while (strchra((CHAR8 *)" \t", *line)) {
1387 /* remove trailing whitespace */
1388 while (linelen > 0 && strchra((CHAR8 *)" \t", line[linelen-1]))
1390 line[linelen] = '\0';
1395 /* split key/value */
1397 while (*value && !strchra((CHAR8 *)" \t", *value))
1403 while (*value && strchra((CHAR8 *)" \t", *value))
1411 static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
1417 while ((line = line_get_key_value(content, &pos, &key, &value))) {
1418 if (strcmpa((CHAR8 *)"timeout", key) == 0) {
1421 s = stra_to_str(value);
1422 config->timeout_sec_config = Atoi(s);
1423 config->timeout_sec = config->timeout_sec_config;
1427 if (strcmpa((CHAR8 *)"default", key) == 0) {
1428 config->entry_default_pattern = stra_to_str(value);
1429 StrLwr(config->entry_default_pattern);
1435 static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
1441 CHAR16 *initrd = NULL;
1443 entry = AllocateZeroPool(sizeof(ConfigEntry));
1446 while ((line = line_get_key_value(content, &pos, &key, &value))) {
1447 if (strcmpa((CHAR8 *)"title", key) == 0) {
1448 FreePool(entry->title);
1449 entry->title = stra_to_str(value);
1453 if (strcmpa((CHAR8 *)"version", key) == 0) {
1454 FreePool(entry->version);
1455 entry->version = stra_to_str(value);
1459 if (strcmpa((CHAR8 *)"machine-id", key) == 0) {
1460 FreePool(entry->machine_id);
1461 entry->machine_id = stra_to_str(value);
1465 if (strcmpa((CHAR8 *)"linux", key) == 0) {
1466 FreePool(entry->loader);
1467 entry->type = LOADER_LINUX;
1468 entry->loader = stra_to_path(value);
1473 if (strcmpa((CHAR8 *)"efi", key) == 0) {
1474 entry->type = LOADER_EFI;
1475 FreePool(entry->loader);
1476 entry->loader = stra_to_path(value);
1478 /* do not add an entry for ourselves */
1479 if (StriCmp(entry->loader, loaded_image_path) == 0) {
1480 entry->type = LOADER_UNDEFINED;
1486 if (strcmpa((CHAR8 *)"initrd", key) == 0) {
1489 new = stra_to_path(value);
1493 s = PoolPrint(L"%s initrd=%s", initrd, new);
1497 initrd = PoolPrint(L"initrd=%s", new);
1502 if (strcmpa((CHAR8 *)"options", key) == 0) {
1505 new = stra_to_str(value);
1506 if (entry->options) {
1509 s = PoolPrint(L"%s %s", entry->options, new);
1510 FreePool(entry->options);
1513 entry->options = new;
1521 if (entry->type == LOADER_UNDEFINED) {
1522 config_entry_free(entry);
1528 /* add initrd= to options */
1529 if (entry->type == LOADER_LINUX && initrd) {
1530 if (entry->options) {
1533 s = PoolPrint(L"%s %s", initrd, entry->options);
1534 FreePool(entry->options);
1537 entry->options = initrd;
1543 if (entry->machine_id) {
1546 /* append additional options from EFI variables for this machine-id */
1547 var = PoolPrint(L"LoaderEntryOptions-%s", entry->machine_id);
1551 if (efivar_get(var, &s) == EFI_SUCCESS) {
1552 if (entry->options) {
1555 s2 = PoolPrint(L"%s %s", entry->options, s);
1556 FreePool(entry->options);
1557 entry->options = s2;
1564 var = PoolPrint(L"LoaderEntryOptionsOneShot-%s", entry->machine_id);
1568 if (efivar_get(var, &s) == EFI_SUCCESS) {
1569 if (entry->options) {
1572 s2 = PoolPrint(L"%s %s", entry->options, s);
1573 FreePool(entry->options);
1574 entry->options = s2;
1577 efivar_set(var, NULL, TRUE);
1583 entry->device = device;
1584 entry->file = StrDuplicate(file);
1585 len = StrLen(entry->file);
1586 /* remove ".conf" */
1588 entry->file[len - 5] = '\0';
1589 StrLwr(entry->file);
1591 config_add_entry(config, entry);
1594 static UINTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, CHAR8 **content) {
1595 EFI_FILE_HANDLE handle;
1596 EFI_FILE_INFO *info;
1602 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
1606 info = LibFileInfo(handle);
1607 buflen = info->FileSize+1;
1608 buf = AllocatePool(buflen);
1610 err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
1611 if (!EFI_ERROR(err)) {
1619 uefi_call_wrapper(handle->Close, 1, handle);
1624 static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
1625 EFI_FILE_HANDLE entries_dir;
1627 CHAR8 *content = NULL;
1632 len = file_read(root_dir, L"\\loader\\loader.conf", &content);
1634 config_defaults_load_from_file(config, content);
1637 err = efivar_get_int(L"LoaderConfigTimeout", &sec);
1638 if (!EFI_ERROR(err)) {
1639 config->timeout_sec_efivar = sec;
1640 config->timeout_sec = sec;
1642 config->timeout_sec_efivar = -1;
1644 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
1645 if (!EFI_ERROR(err)) {
1650 CHAR8 *content = NULL;
1653 bufsize = sizeof(buf);
1654 err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
1655 if (bufsize == 0 || EFI_ERROR(err))
1658 f = (EFI_FILE_INFO *) buf;
1659 if (f->FileName[0] == '.')
1661 if (f->Attribute & EFI_FILE_DIRECTORY)
1663 len = StrLen(f->FileName);
1666 if (StriCmp(f->FileName + len - 5, L".conf") != 0)
1669 len = file_read(entries_dir, f->FileName, &content);
1671 config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path);
1674 uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
1677 /* sort entries after version number */
1678 for (i = 1; i < config->entry_count; i++) {
1683 for (k = 0; k < config->entry_count - i; k++) {
1686 if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0)
1688 entry = config->entries[k];
1689 config->entries[k] = config->entries[k+1];
1690 config->entries[k+1] = entry;
1698 static VOID config_default_entry_select(Config *config) {
1704 * The EFI variable to specify a boot entry for the next, and only the
1705 * next reboot. The variable is always cleared directly after it is read.
1707 err = efivar_get(L"LoaderEntryOneShot", &var);
1708 if (!EFI_ERROR(err)) {
1709 BOOLEAN found = FALSE;
1711 for (i = 0; i < config->entry_count; i++) {
1712 if (StrCmp(config->entries[i]->file, var) == 0) {
1713 config->idx_default = i;
1719 config->entry_oneshot = StrDuplicate(var);
1720 efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
1727 * The EFI variable to select the default boot entry overrides the
1728 * configured pattern. The variable can be set and cleared by pressing
1729 * the 'd' key in the loader selection menu, the entry is marked with
1732 err = efivar_get(L"LoaderEntryDefault", &var);
1733 if (!EFI_ERROR(err)) {
1734 BOOLEAN found = FALSE;
1736 for (i = 0; i < config->entry_count; i++) {
1737 if (StrCmp(config->entries[i]->file, var) == 0) {
1738 config->idx_default = i;
1739 config->idx_default_efivar = i;
1748 config->idx_default_efivar = -1;
1750 if (config->entry_count == 0)
1754 * Match the pattern from the end of the list to the start, find last
1755 * entry (largest number) matching the given pattern.
1757 if (config->entry_default_pattern) {
1758 i = config->entry_count;
1760 if (config->entries[i]->no_autoselect)
1762 if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
1763 config->idx_default = i;
1769 /* select the last suitable entry */
1770 i = config->entry_count;
1772 if (config->entries[i]->no_autoselect)
1774 config->idx_default = i;
1778 /* no entry found */
1779 config->idx_default = -1;
1782 /* generate a unique title, avoiding non-distinguishable menu entries */
1783 static VOID config_title_generate(Config *config) {
1788 for (i = 0; i < config->entry_count; i++) {
1791 FreePool(config->entries[i]->title_show);
1792 title = config->entries[i]->title;
1794 title = config->entries[i]->file;
1795 config->entries[i]->title_show = StrDuplicate(title);
1799 for (i = 0; i < config->entry_count; i++) {
1800 for (k = 0; k < config->entry_count; k++) {
1803 if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1807 config->entries[i]->non_unique = TRUE;
1808 config->entries[k]->non_unique = TRUE;
1814 /* add version to non-unique titles */
1815 for (i = 0; i < config->entry_count; i++) {
1818 if (!config->entries[i]->non_unique)
1820 if (!config->entries[i]->version)
1823 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
1824 FreePool(config->entries[i]->title_show);
1825 config->entries[i]->title_show = s;
1826 config->entries[i]->non_unique = FALSE;
1830 for (i = 0; i < config->entry_count; i++) {
1831 for (k = 0; k < config->entry_count; k++) {
1834 if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1838 config->entries[i]->non_unique = TRUE;
1839 config->entries[k]->non_unique = TRUE;
1845 /* add machine-id to non-unique titles */
1846 for (i = 0; i < config->entry_count; i++) {
1850 if (!config->entries[i]->non_unique)
1852 if (!config->entries[i]->machine_id)
1855 m = StrDuplicate(config->entries[i]->machine_id);
1857 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m);
1858 FreePool(config->entries[i]->title_show);
1859 config->entries[i]->title_show = s;
1860 config->entries[i]->non_unique = FALSE;
1865 for (i = 0; i < config->entry_count; i++) {
1866 for (k = 0; k < config->entry_count; k++) {
1869 if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1873 config->entries[i]->non_unique = TRUE;
1874 config->entries[k]->non_unique = TRUE;
1880 /* add file name to non-unique titles */
1881 for (i = 0; i < config->entry_count; i++) {
1884 if (!config->entries[i]->non_unique)
1886 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file);
1887 FreePool(config->entries[i]->title_show);
1888 config->entries[i]->title_show = s;
1889 config->entries[i]->non_unique = FALSE;
1893 static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(void)) {
1896 entry = AllocateZeroPool(sizeof(ConfigEntry));
1897 entry->title = StrDuplicate(title);
1899 entry->no_autoselect = TRUE;
1900 config_add_entry(config, entry);
1904 static BOOLEAN config_entry_add_loader(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
1905 CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
1906 EFI_FILE_HANDLE handle;
1910 /* do not add an entry for ourselves */
1911 if (loaded_image_path && StriCmp(loader, loaded_image_path) == 0)
1914 /* check existence */
1915 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL);
1918 uefi_call_wrapper(handle->Close, 1, handle);
1920 entry = AllocateZeroPool(sizeof(ConfigEntry));
1921 entry->title = StrDuplicate(title);
1922 entry->device = device;
1923 entry->loader = StrDuplicate(loader);
1924 entry->file = StrDuplicate(file);
1925 StrLwr(entry->file);
1926 entry->no_autoselect = TRUE;
1928 config_add_entry(config, entry);
1932 static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
1933 CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
1934 if (!config_entry_add_loader(config, device, root_dir, loaded_image_path, file, key, title, loader))
1937 /* export identifiers of automatically added entries */
1938 if (config->entries_auto) {
1941 s = PoolPrint(L"%s %s", config->entries_auto, file);
1942 FreePool(config->entries_auto);
1943 config->entries_auto = s;
1945 config->entries_auto = StrDuplicate(file);
1950 static VOID config_entry_add_osx(Config *config) {
1952 UINTN handle_count = 0;
1953 EFI_HANDLE *handles = NULL;
1955 err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles);
1956 if (!EFI_ERROR(err)) {
1959 for (i = 0; i < handle_count; i++) {
1963 root = LibOpenRoot(handles[i]);
1966 found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"OS X",
1967 L"\\System\\Library\\CoreServices\\boot.efi");
1968 uefi_call_wrapper(root->Close, 1, root);
1977 static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
1980 EFI_DEVICE_PATH *path;
1983 path = FileDevicePath(entry->device, entry->loader);
1985 Print(L"Error getting device path.");
1986 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1987 return EFI_INVALID_PARAMETER;
1990 err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
1991 if (EFI_ERROR(err)) {
1992 Print(L"Error loading %s: %r", entry->loader, err);
1993 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1997 if (config->options_edit)
1998 options = config->options_edit;
1999 else if (entry->options)
2000 options = entry->options;
2004 EFI_LOADED_IMAGE *loaded_image;
2006 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (void **)&loaded_image,
2007 parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2008 if (EFI_ERROR(err)) {
2009 Print(L"Error getting LoadedImageProtocol handle: %r", err);
2010 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2013 loaded_image->LoadOptions = options;
2014 loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
2017 efivar_set_time_usec(L"LoaderTimeExecUSec", 0);
2018 err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
2020 uefi_call_wrapper(BS->UnloadImage, 1, image);
2026 static EFI_STATUS reboot_into_firmware(VOID) {
2032 osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
2034 err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size);
2035 if (!EFI_ERROR(err))
2036 osind |= (UINT64)*b;
2039 err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
2043 err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
2044 Print(L"Error calling ResetSystem: %r", err);
2045 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2049 static VOID config_free(Config *config) {
2052 for (i = 0; i < config->entry_count; i++)
2053 config_entry_free(config->entries[i]);
2054 FreePool(config->entries);
2055 FreePool(config->entry_default_pattern);
2056 FreePool(config->options_edit);
2057 FreePool(config->entry_oneshot);
2058 FreePool(config->entries_auto);
2061 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
2065 EFI_LOADED_IMAGE *loaded_image;
2067 CHAR16 *loaded_image_path;
2068 EFI_DEVICE_PATH *device_path;
2072 BOOLEAN menu = FALSE;
2074 InitializeLib(image, sys_table);
2075 init_usec = time_usec();
2076 efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec);
2077 efivar_set(L"LoaderInfo", L"gummiboot " VERSION, FALSE);
2078 s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
2079 efivar_set(L"LoaderFirmwareInfo", s, FALSE);
2081 s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
2082 efivar_set(L"LoaderFirmwareType", s, FALSE);
2085 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (void **)&loaded_image,
2086 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2087 if (EFI_ERROR(err)) {
2088 Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
2089 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2093 /* export the device path this image is started from */
2094 device_path = DevicePathFromHandle(loaded_image->DeviceHandle);
2097 EFI_DEVICE_PATH *path, *paths;
2099 str = DevicePathToStr(device_path);
2100 efivar_set(L"LoaderDeviceIdentifier", str, FALSE);
2103 paths = UnpackDevicePath(device_path);
2104 for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
2105 HARDDRIVE_DEVICE_PATH *drive;
2108 if (DevicePathType(path) != MEDIA_DEVICE_PATH)
2110 if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
2112 drive = (HARDDRIVE_DEVICE_PATH *)path;
2113 if (drive->SignatureType != SIGNATURE_TYPE_GUID)
2116 GuidToString(uuid, (EFI_GUID *)&drive->Signature);
2117 efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
2123 root_dir = LibOpenRoot(loaded_image->DeviceHandle);
2125 Print(L"Unable to open root directory: %r ", err);
2126 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2127 return EFI_LOAD_ERROR;
2130 /* the filesystem path to this image, to prevent adding ourselves to the menu */
2131 loaded_image_path = DevicePathToStr(loaded_image->FilePath);
2132 efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
2134 /* scan "\loader\entries\*.conf" files */
2135 ZeroMem(&config, sizeof(Config));
2136 config_load(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
2138 /* if we find some well-known loaders, add them to the end of the list */
2139 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2140 L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2141 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2142 L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" MACHINE_TYPE_NAME ".efi");
2143 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2144 L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" MACHINE_TYPE_NAME ".efi");
2145 config_entry_add_osx(&config);
2146 efivar_set(L"LoaderEntriesAuto", config.entries_auto, FALSE);
2148 if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
2149 UINT64 osind = (UINT64)*b;
2151 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
2152 config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware);
2156 if (config.entry_count == 0) {
2157 Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2158 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2162 config_title_generate(&config);
2164 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
2165 config_default_entry_select(&config);
2167 /* if no configured entry to select from was found, enable the menu */
2168 if (config.idx_default == -1) {
2169 config.idx_default = 0;
2170 if (config.timeout_sec == 0)
2171 config.timeout_sec = 10;
2174 /* select entry or show menu when key is pressed or timeout is set */
2175 if (config.timeout_sec == 0) {
2178 err = key_read(&key, FALSE);
2179 if (!EFI_ERROR(err)) {
2182 /* find matching key in config entries */
2183 idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key));
2185 config.idx_default = idx;
2195 entry = config.entries[config.idx_default];
2197 efivar_set_time_usec(L"LoaderTimeMenuUSec", 0);
2198 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL);
2199 if (!menu_run(&config, &entry, loaded_image_path))
2202 /* run special entry like "reboot" */
2209 /* export the selected boot entry to the system */
2210 efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
2212 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
2213 err = image_start(image, &config, entry);
2215 if (err == EFI_ACCESS_DENIED || err == EFI_SECURITY_VIOLATION) {
2216 /* Platform is secure boot and requested image isn't
2217 * trusted. Need to go back to prior boot system and
2218 * install more keys or hashes. Signal failure by
2219 * returning the error */
2220 Print(L"\nImage %s gives a security error\n", entry->title);
2221 Print(L"Please enrol the hash or signature of %s\n", entry->loader);
2222 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2227 config.timeout_sec = 0;
2231 FreePool(loaded_image_path);
2232 config_free(&config);
2233 uefi_call_wrapper(root_dir->Close, 1, root_dir);
2234 uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);