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;
287 EFI_KEY_DATA keydata;
294 err = LibLocateProtocol(&EfiSimpleTextInputExProtocolGuid, (VOID **)&TextInputEx);
305 /* fallback for firmware which does not support SimpleTextInputExProtocol */
307 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
308 err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
312 *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
317 /* wait for key press */
318 err = uefi_call_wrapper(BS->WaitForEvent, 3, 1, &TextInputEx->WaitForKeyEx, &index);
319 if (EFI_ERROR(err)) {
320 /* some firmware exposes SimpleTextInputExProtocol, but it doesn't work */
326 err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata);
327 if (EFI_ERROR(err)) {
328 if (err != EFI_NOT_READY) {
329 /* some firmware exposes SimpleTextInputExProtocol, but it doesn't work */
337 /* do not distinguish between left and right keys */
338 if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
339 if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
340 shift |= EFI_CONTROL_PRESSED;
341 if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
342 shift |= EFI_ALT_PRESSED;
345 /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
346 keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
348 /* some firmware exposes SimpleTextInputExProtocol, but it doesn't work */
357 static void cursor_left(UINTN *cursor, UINTN *first)
361 else if ((*first) > 0)
365 static void cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
367 if ((*cursor)+1 < x_max)
369 else if ((*first) + (*cursor) < len)
373 static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
386 size = StrLen(line_in) + 1024;
387 line = AllocatePool(size * sizeof(CHAR16));
388 StrCpy(line, line_in);
390 print = AllocatePool((x_max+1) * sizeof(CHAR16));
392 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
407 CopyMem(print, line + first, i * sizeof(CHAR16));
408 while (clear > 0 && i < x_max-1) {
414 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
415 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
416 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
418 err = key_read(&key, TRUE);
423 case KEYPRESS(0, SCAN_ESC, 0):
424 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
425 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
426 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
427 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
431 case KEYPRESS(0, SCAN_HOME, 0):
432 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
433 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
434 /* beginning-of-line */
439 case KEYPRESS(0, SCAN_END, 0):
440 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
441 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
443 cursor = len - first;
444 if (cursor+1 >= x_max) {
446 first = len - (x_max-1);
450 case KEYPRESS(0, SCAN_DOWN, 0):
451 case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
452 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
454 while (line[first + cursor] && line[first + cursor] == ' ')
455 cursor_right(&cursor, &first, x_max, len);
456 while (line[first + cursor] && line[first + cursor] != ' ')
457 cursor_right(&cursor, &first, x_max, len);
458 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
461 case KEYPRESS(0, SCAN_UP, 0):
462 case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
463 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
465 if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
466 cursor_left(&cursor, &first);
467 while ((first + cursor) > 0 && line[first + cursor] == ' ')
468 cursor_left(&cursor, &first);
470 while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
471 cursor_left(&cursor, &first);
472 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
475 case KEYPRESS(0, SCAN_RIGHT, 0):
476 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
477 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
479 if (first + cursor == len)
481 cursor_right(&cursor, &first, x_max, len);
482 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
485 case KEYPRESS(0, SCAN_LEFT, 0):
486 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
487 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
489 cursor_left(&cursor, &first);
490 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
493 case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
496 for (i = first + cursor; i < len && line[i] == ' '; i++)
498 for (; i < len && line[i] != ' '; i++)
501 for (i = first + cursor; i + clear < len; i++)
502 line[i] = line[i + clear];
507 case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE):
508 /* backward-kill-word */
510 if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
511 cursor_left(&cursor, &first);
513 while ((first + cursor) > 0 && line[first + cursor] == ' ') {
514 cursor_left(&cursor, &first);
518 while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
519 cursor_left(&cursor, &first);
522 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
524 for (i = first + cursor; i + clear < len; i++)
525 line[i] = line[i + clear];
530 case KEYPRESS(0, SCAN_DELETE, 0):
531 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
532 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
535 if (first + cursor == len)
537 for (i = first + cursor; i < len; i++)
543 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
544 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
546 line[first + cursor] = '\0';
547 clear = len - (first + cursor);
548 len = first + cursor;
551 case KEYPRESS(0, 0, CHAR_LINEFEED):
552 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
553 if (StrCmp(line, line_in) != 0) {
561 case KEYPRESS(0, 0, CHAR_BACKSPACE):
564 if (first == 0 && cursor == 0)
566 for (i = first + cursor-1; i < len; i++)
572 if (cursor > 0 || first == 0)
574 /* show full line if it fits */
580 /* jump left to see what we delete */
590 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
591 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
594 for (i = len; i > first + cursor; i--)
596 line[first + cursor] = KEYCHAR(key);
599 if (cursor+1 < x_max)
601 else if (first + cursor < len)
607 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
613 static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
619 /* select entry by number key */
620 if (key >= '1' && key <= '9') {
622 if (i > config->entry_count)
623 i = config->entry_count;
627 /* find matching key in config entries */
628 for (i = start; i < config->entry_count; i++)
629 if (config->entries[i]->key == key)
632 for (i = 0; i < start; i++)
633 if (config->entries[i]->key == key)
639 static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
646 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
647 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
649 Print(L"gummiboot version: " VERSION "\n");
650 Print(L"loaded image: %s\n", loaded_image_path);
651 Print(L"UEFI version: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
652 Print(L"firmware vendor: %s\n", ST->FirmwareVendor);
653 Print(L"firmware version: %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
654 if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
655 Print(L"SecureBoot: %s\n", *b > 0 ? L"enabled" : L"disabled");
659 if (efivar_get_raw(&global_guid, L"SetupMode", &b, &size) == EFI_SUCCESS) {
660 Print(L"SetupMode: %s\n", *b > 0 ? L"setup" : L"user");
664 if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
665 Print(L"OsIndicationsSupported: %d\n", (UINT64)*b);
670 Print(L"timeout: %d\n", config->timeout_sec);
671 if (config->timeout_sec_efivar >= 0)
672 Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar);
673 Print(L"timeout (config): %d\n", config->timeout_sec_config);
674 if (config->entry_default_pattern)
675 Print(L"default pattern: '%s'\n", config->entry_default_pattern);
678 Print(L"config entry count: %d\n", config->entry_count);
679 Print(L"entry selected idx: %d\n", config->idx_default);
680 if (config->idx_default_efivar >= 0)
681 Print(L"entry EFI var idx: %d\n", config->idx_default_efivar);
684 if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS)
685 Print(L"LoaderConfigTimeout: %d\n", i);
686 if (config->entry_oneshot)
687 Print(L"LoaderEntryOneShot: %s\n", config->entry_oneshot);
688 if (efivar_get(L"LoaderDeviceIdentifier", &s) == EFI_SUCCESS) {
689 Print(L"LoaderDeviceIdentifier: %s\n", s);
692 if (efivar_get(L"LoaderDevicePartUUID", &s) == EFI_SUCCESS) {
693 Print(L"LoaderDevicePartUUID: %s\n", s);
696 if (efivar_get(L"LoaderEntryDefault", &s) == EFI_SUCCESS) {
697 Print(L"LoaderEntryDefault: %s\n", s);
701 Print(L"\n--- press key ---\n\n");
702 key_read(&key, TRUE);
704 for (i = 0; i < config->entry_count; i++) {
707 if (key == KEYPRESS(0, SCAN_ESC, 0) || key == KEYPRESS(0, 0, 'q'))
710 entry = config->entries[i];
711 Print(L"config entry: %d/%d\n", i+1, config->entry_count);
713 Print(L"file '%s'\n", entry->file);
714 Print(L"title show '%s'\n", entry->title_show);
716 Print(L"title '%s'\n", entry->title);
718 Print(L"version '%s'\n", entry->version);
719 if (entry->machine_id)
720 Print(L"machine-id '%s'\n", entry->machine_id);
722 EFI_DEVICE_PATH *device_path;
725 device_path = DevicePathFromHandle(entry->device);
727 str = DevicePathToStr(device_path);
728 Print(L"device handle '%s'\n", str);
733 Print(L"loader '%s'\n", entry->loader);
735 Print(L"options '%s'\n", entry->options);
736 Print(L"auto-select %s\n", entry->no_autoselect ? L"no" : L"yes");
738 Print(L"internal call yes\n");
740 Print(L"\n--- press key ---\n\n");
741 key_read(&key, TRUE);
744 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
747 static EFI_STATUS console_text_mode(VOID) {
748 #define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \
749 { 0xf42f7782, 0x12e, 0x4c12, { 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21 } };
751 struct _EFI_CONSOLE_CONTROL_PROTOCOL;
754 EfiConsoleControlScreenText,
755 EfiConsoleControlScreenGraphics,
756 EfiConsoleControlScreenMaxValue,
757 } EFI_CONSOLE_CONTROL_SCREEN_MODE;
759 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE)(
760 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
761 EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode,
766 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE)(
767 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
768 EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
771 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN)(
772 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
776 typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
777 EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode;
778 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
779 EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn;
780 } EFI_CONSOLE_CONTROL_PROTOCOL;
782 EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
783 EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
786 err = LibLocateProtocol(&ConsoleControlProtocolGuid, (VOID **)&ConsoleControl);
789 return uefi_call_wrapper(ConsoleControl->SetMode, 2, ConsoleControl, EfiConsoleControlScreenText);
792 static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *loaded_image_path) {
796 UINTN idx_highlight_prev;
812 BOOLEAN exit = FALSE;
814 BOOLEAN wait = FALSE;
817 uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
818 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
819 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
820 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
822 err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
823 if (EFI_ERROR(err)) {
828 /* we check 10 times per second for a keystroke */
829 if (config->timeout_sec > 0)
830 timeout_remain = config->timeout_sec * 10;
834 idx_highlight = config->idx_default;
835 idx_highlight_prev = 0;
837 visible_max = y_max - 2;
839 if ((UINTN)config->idx_default >= visible_max)
840 idx_first = config->idx_default-1;
844 idx_last = idx_first + visible_max-1;
849 /* length of the longest entry */
851 for (i = 0; i < config->entry_count; i++) {
854 entry_len = StrLen(config->entries[i]->title_show);
855 if (line_width < entry_len)
856 line_width = entry_len;
858 if (line_width > x_max-6)
859 line_width = x_max-6;
861 /* offsets to center the entries on the screen */
862 x_start = (x_max - (line_width)) / 2;
863 if (config->entry_count < visible_max)
864 y_start = ((visible_max - config->entry_count) / 2) + 1;
868 /* menu entries title lines */
869 lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
870 for (i = 0; i < config->entry_count; i++) {
873 lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
874 for (j = 0; j < x_start; j++)
877 for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
878 lines[i][j] = config->entries[i]->title_show[k];
880 for (; j < x_max; j++)
882 lines[i][x_max] = '\0';
886 clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
887 for (i = 0; i < x_max; i++)
895 for (i = 0; i < config->entry_count; i++) {
896 if (i < idx_first || i > idx_last)
898 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
899 if (i == idx_highlight)
900 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
901 EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
903 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
904 EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
905 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
906 if ((INTN)i == config->idx_default_efivar) {
907 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
908 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
912 } else if (highlight) {
913 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
914 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
915 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
916 if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
917 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
918 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
921 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
922 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
923 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
924 if ((INTN)idx_highlight == config->idx_default_efivar) {
925 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
926 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
931 if (timeout_remain > 0) {
933 status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
936 /* print status at last line of screen */
942 len = StrLen(status);
944 x = (x_max - len) / 2;
947 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
948 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
949 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
950 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
951 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
954 err = key_read(&key, wait);
955 if (EFI_ERROR(err)) {
956 /* timeout reached */
957 if (timeout_remain == 0) {
962 /* sleep and update status */
963 if (timeout_remain > 0) {
964 uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
969 /* timeout disabled, wait for next key */
976 /* clear status after keystroke */
980 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
981 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
982 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
985 idx_highlight_prev = idx_highlight;
988 case KEYPRESS(0, SCAN_UP, 0):
989 case KEYPRESS(0, 0, 'k'):
990 if (idx_highlight > 0)
994 case KEYPRESS(0, SCAN_DOWN, 0):
995 case KEYPRESS(0, 0, 'j'):
996 if (idx_highlight < config->entry_count-1)
1000 case KEYPRESS(0, SCAN_HOME, 0):
1001 case KEYPRESS(EFI_ALT_PRESSED, 0, '<'):
1002 if (idx_highlight > 0) {
1008 case KEYPRESS(0, SCAN_END, 0):
1009 case KEYPRESS(EFI_ALT_PRESSED, 0, '>'):
1010 if (idx_highlight < config->entry_count-1) {
1012 idx_highlight = config->entry_count-1;
1016 case KEYPRESS(0, SCAN_PAGE_UP, 0):
1017 if (idx_highlight > visible_max)
1018 idx_highlight -= visible_max;
1023 case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
1024 idx_highlight += visible_max;
1025 if (idx_highlight > config->entry_count-1)
1026 idx_highlight = config->entry_count-1;
1029 case KEYPRESS(0, 0, CHAR_LINEFEED):
1030 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
1034 case KEYPRESS(0, SCAN_F1, 0):
1035 case KEYPRESS(0, 0, 'h'):
1036 case KEYPRESS(0, 0, '?'):
1037 status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
1040 case KEYPRESS(0, 0, 'Q'):
1045 case KEYPRESS(0, 0, 'd'):
1046 if (config->idx_default_efivar != (INTN)idx_highlight) {
1047 /* store the selected entry in a persistent EFI variable */
1048 efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
1049 config->idx_default_efivar = idx_highlight;
1050 status = StrDuplicate(L"Default boot entry selected.");
1052 /* clear the default entry EFI variable */
1053 efivar_set(L"LoaderEntryDefault", NULL, TRUE);
1054 config->idx_default_efivar = -1;
1055 status = StrDuplicate(L"Default boot entry cleared.");
1060 case KEYPRESS(0, 0, '-'):
1061 case KEYPRESS(0, 0, 'T'):
1062 if (config->timeout_sec_efivar > 0) {
1063 config->timeout_sec_efivar--;
1064 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
1065 if (config->timeout_sec_efivar > 0)
1066 status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
1068 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1069 } else if (config->timeout_sec_efivar <= 0){
1070 config->timeout_sec_efivar = -1;
1071 efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
1072 if (config->timeout_sec_config > 0)
1073 status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
1074 config->timeout_sec_config);
1076 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1080 case KEYPRESS(0, 0, '+'):
1081 case KEYPRESS(0, 0, 't'):
1082 if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
1083 config->timeout_sec_efivar++;
1084 config->timeout_sec_efivar++;
1085 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
1086 if (config->timeout_sec_efivar > 0)
1087 status = PoolPrint(L"Menu timeout set to %d sec.",
1088 config->timeout_sec_efivar);
1090 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1093 case KEYPRESS(0, 0, 'e'):
1094 /* only the options of configured entries can be edited */
1095 if (config->entries[idx_highlight]->type == LOADER_UNDEFINED)
1097 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
1098 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
1099 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
1100 if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
1102 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
1103 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
1106 case KEYPRESS(0, 0, 'v'):
1107 status = PoolPrint(L"gummiboot " VERSION ", UEFI %d.%02d, %s %d.%02d",
1108 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff,
1109 ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
1112 case KEYPRESS(0, 0, 'P'):
1113 print_status(config, loaded_image_path);
1117 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
1118 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
1123 /* jump with a hotkey directly to a matching entry */
1124 idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
1127 idx_highlight = idx;
1131 if (idx_highlight > idx_last) {
1132 idx_last = idx_highlight;
1133 idx_first = 1 + idx_highlight - visible_max;
1136 if (idx_highlight < idx_first) {
1137 idx_first = idx_highlight;
1138 idx_last = idx_highlight + visible_max-1;
1142 idx_last = idx_first + visible_max-1;
1144 if (!refresh && idx_highlight != idx_highlight_prev)
1148 *chosen_entry = config->entries[idx_highlight];
1150 for (i = 0; i < config->entry_count; i++)
1153 FreePool(clearline);
1155 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
1156 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
1160 static VOID config_add_entry(Config *config, ConfigEntry *entry) {
1161 if ((config->entry_count & 15) == 0) {
1164 i = config->entry_count + 16;
1165 if (config->entry_count == 0)
1166 config->entries = AllocatePool(sizeof(VOID *) * i);
1168 config->entries = ReallocatePool(config->entries,
1169 sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
1171 config->entries[config->entry_count++] = entry;
1174 static VOID config_entry_free(ConfigEntry *entry) {
1175 FreePool(entry->title_show);
1176 FreePool(entry->title);
1177 FreePool(entry->machine_id);
1178 FreePool(entry->loader);
1179 FreePool(entry->options);
1182 static BOOLEAN is_digit(CHAR16 c)
1184 return (c >= '0') && (c <= '9');
1187 static UINTN c_order(CHAR16 c)
1193 else if ((c >= 'a') && (c <= 'z'))
1199 static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
1204 while (*s1 || *s2) {
1207 while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
1210 order = c_order(*s1) - c_order(*s2);
1223 while (is_digit(*s1) && is_digit(*s2)) {
1239 return StrCmp(os1, os2);
1242 static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
1249 else if ((stra[0] & 0xe0) == 0xc0)
1251 else if ((stra[0] & 0xf0) == 0xe0)
1253 else if ((stra[0] & 0xf8) == 0xf0)
1255 else if ((stra[0] & 0xfc) == 0xf8)
1257 else if ((stra[0] & 0xfe) == 0xfc)
1267 unichar = stra[0] & 0x1f;
1270 unichar = stra[0] & 0x0f;
1273 unichar = stra[0] & 0x07;
1276 unichar = stra[0] & 0x03;
1279 unichar = stra[0] & 0x01;
1283 for (i = 1; i < len; i++) {
1284 if ((stra[i] & 0xc0) != 0x80)
1287 unichar |= stra[i] & 0x3f;
1294 static CHAR16 *stra_to_str(CHAR8 *stra) {
1300 len = strlena(stra);
1301 str = AllocatePool((len + 1) * sizeof(CHAR16));
1308 utf8len = utf8_to_16(stra + i, str + strlen);
1310 /* invalid utf8 sequence, skip the garbage */
1322 static CHAR16 *stra_to_path(CHAR8 *stra) {
1328 len = strlena(stra);
1329 str = AllocatePool((len + 2) * sizeof(CHAR16));
1337 utf8len = utf8_to_16(stra + i, str + strlen);
1339 /* invalid utf8 sequence, skip the garbage */
1344 if (str[strlen] == '/')
1346 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
1347 /* skip double slashes */
1359 static CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
1367 static CHAR8 *line_get_key_value(CHAR8 *content, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
1373 line = content + *pos;
1378 while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
1381 /* move pos to next line */
1390 /* terminate line */
1391 line[linelen] = '\0';
1393 /* remove leading whitespace */
1394 while (strchra((CHAR8 *)" \t", *line)) {
1399 /* remove trailing whitespace */
1400 while (linelen > 0 && strchra((CHAR8 *)" \t", line[linelen-1]))
1402 line[linelen] = '\0';
1407 /* split key/value */
1409 while (*value && !strchra((CHAR8 *)" \t", *value))
1415 while (*value && strchra((CHAR8 *)" \t", *value))
1423 static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
1429 while ((line = line_get_key_value(content, &pos, &key, &value))) {
1430 if (strcmpa((CHAR8 *)"timeout", key) == 0) {
1433 s = stra_to_str(value);
1434 config->timeout_sec_config = Atoi(s);
1435 config->timeout_sec = config->timeout_sec_config;
1439 if (strcmpa((CHAR8 *)"default", key) == 0) {
1440 config->entry_default_pattern = stra_to_str(value);
1441 StrLwr(config->entry_default_pattern);
1447 static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
1453 CHAR16 *initrd = NULL;
1455 entry = AllocateZeroPool(sizeof(ConfigEntry));
1458 while ((line = line_get_key_value(content, &pos, &key, &value))) {
1459 if (strcmpa((CHAR8 *)"title", key) == 0) {
1460 FreePool(entry->title);
1461 entry->title = stra_to_str(value);
1465 if (strcmpa((CHAR8 *)"version", key) == 0) {
1466 FreePool(entry->version);
1467 entry->version = stra_to_str(value);
1471 if (strcmpa((CHAR8 *)"machine-id", key) == 0) {
1472 FreePool(entry->machine_id);
1473 entry->machine_id = stra_to_str(value);
1477 if (strcmpa((CHAR8 *)"linux", key) == 0) {
1478 FreePool(entry->loader);
1479 entry->type = LOADER_LINUX;
1480 entry->loader = stra_to_path(value);
1485 if (strcmpa((CHAR8 *)"efi", key) == 0) {
1486 entry->type = LOADER_EFI;
1487 FreePool(entry->loader);
1488 entry->loader = stra_to_path(value);
1490 /* do not add an entry for ourselves */
1491 if (StriCmp(entry->loader, loaded_image_path) == 0) {
1492 entry->type = LOADER_UNDEFINED;
1498 if (strcmpa((CHAR8 *)"initrd", key) == 0) {
1501 new = stra_to_path(value);
1505 s = PoolPrint(L"%s initrd=%s", initrd, new);
1509 initrd = PoolPrint(L"initrd=%s", new);
1514 if (strcmpa((CHAR8 *)"options", key) == 0) {
1517 new = stra_to_str(value);
1518 if (entry->options) {
1521 s = PoolPrint(L"%s %s", entry->options, new);
1522 FreePool(entry->options);
1525 entry->options = new;
1533 if (entry->type == LOADER_UNDEFINED) {
1534 config_entry_free(entry);
1540 /* add initrd= to options */
1541 if (entry->type == LOADER_LINUX && initrd) {
1542 if (entry->options) {
1545 s = PoolPrint(L"%s %s", initrd, entry->options);
1546 FreePool(entry->options);
1549 entry->options = initrd;
1555 if (entry->machine_id) {
1558 /* append additional options from EFI variables for this machine-id */
1559 var = PoolPrint(L"LoaderEntryOptions-%s", entry->machine_id);
1563 if (efivar_get(var, &s) == EFI_SUCCESS) {
1564 if (entry->options) {
1567 s2 = PoolPrint(L"%s %s", entry->options, s);
1568 FreePool(entry->options);
1569 entry->options = s2;
1576 var = PoolPrint(L"LoaderEntryOptionsOneShot-%s", entry->machine_id);
1580 if (efivar_get(var, &s) == EFI_SUCCESS) {
1581 if (entry->options) {
1584 s2 = PoolPrint(L"%s %s", entry->options, s);
1585 FreePool(entry->options);
1586 entry->options = s2;
1589 efivar_set(var, NULL, TRUE);
1595 entry->device = device;
1596 entry->file = StrDuplicate(file);
1597 len = StrLen(entry->file);
1598 /* remove ".conf" */
1600 entry->file[len - 5] = '\0';
1601 StrLwr(entry->file);
1603 config_add_entry(config, entry);
1606 static UINTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, CHAR8 **content) {
1607 EFI_FILE_HANDLE handle;
1608 EFI_FILE_INFO *info;
1614 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
1618 info = LibFileInfo(handle);
1619 buflen = info->FileSize+1;
1620 buf = AllocatePool(buflen);
1622 err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
1623 if (!EFI_ERROR(err)) {
1631 uefi_call_wrapper(handle->Close, 1, handle);
1636 static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
1637 EFI_FILE_HANDLE entries_dir;
1639 CHAR8 *content = NULL;
1644 len = file_read(root_dir, L"\\loader\\loader.conf", &content);
1646 config_defaults_load_from_file(config, content);
1649 err = efivar_get_int(L"LoaderConfigTimeout", &sec);
1650 if (!EFI_ERROR(err)) {
1651 config->timeout_sec_efivar = sec;
1652 config->timeout_sec = sec;
1654 config->timeout_sec_efivar = -1;
1656 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
1657 if (!EFI_ERROR(err)) {
1662 CHAR8 *content = NULL;
1665 bufsize = sizeof(buf);
1666 err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
1667 if (bufsize == 0 || EFI_ERROR(err))
1670 f = (EFI_FILE_INFO *) buf;
1671 if (f->FileName[0] == '.')
1673 if (f->Attribute & EFI_FILE_DIRECTORY)
1675 len = StrLen(f->FileName);
1678 if (StriCmp(f->FileName + len - 5, L".conf") != 0)
1681 len = file_read(entries_dir, f->FileName, &content);
1683 config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path);
1686 uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
1689 /* sort entries after version number */
1690 for (i = 1; i < config->entry_count; i++) {
1695 for (k = 0; k < config->entry_count - i; k++) {
1698 if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0)
1700 entry = config->entries[k];
1701 config->entries[k] = config->entries[k+1];
1702 config->entries[k+1] = entry;
1710 static VOID config_default_entry_select(Config *config) {
1716 * The EFI variable to specify a boot entry for the next, and only the
1717 * next reboot. The variable is always cleared directly after it is read.
1719 err = efivar_get(L"LoaderEntryOneShot", &var);
1720 if (!EFI_ERROR(err)) {
1721 BOOLEAN found = FALSE;
1723 for (i = 0; i < config->entry_count; i++) {
1724 if (StrCmp(config->entries[i]->file, var) == 0) {
1725 config->idx_default = i;
1731 config->entry_oneshot = StrDuplicate(var);
1732 efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
1739 * The EFI variable to select the default boot entry overrides the
1740 * configured pattern. The variable can be set and cleared by pressing
1741 * the 'd' key in the loader selection menu, the entry is marked with
1744 err = efivar_get(L"LoaderEntryDefault", &var);
1745 if (!EFI_ERROR(err)) {
1746 BOOLEAN found = FALSE;
1748 for (i = 0; i < config->entry_count; i++) {
1749 if (StrCmp(config->entries[i]->file, var) == 0) {
1750 config->idx_default = i;
1751 config->idx_default_efivar = i;
1760 config->idx_default_efivar = -1;
1762 if (config->entry_count == 0)
1766 * Match the pattern from the end of the list to the start, find last
1767 * entry (largest number) matching the given pattern.
1769 if (config->entry_default_pattern) {
1770 i = config->entry_count;
1772 if (config->entries[i]->no_autoselect)
1774 if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
1775 config->idx_default = i;
1781 /* select the last suitable entry */
1782 i = config->entry_count;
1784 if (config->entries[i]->no_autoselect)
1786 config->idx_default = i;
1790 /* no entry found */
1791 config->idx_default = -1;
1794 /* generate a unique title, avoiding non-distinguishable menu entries */
1795 static VOID config_title_generate(Config *config) {
1800 for (i = 0; i < config->entry_count; i++) {
1803 FreePool(config->entries[i]->title_show);
1804 title = config->entries[i]->title;
1806 title = config->entries[i]->file;
1807 config->entries[i]->title_show = StrDuplicate(title);
1811 for (i = 0; i < config->entry_count; i++) {
1812 for (k = 0; k < config->entry_count; k++) {
1815 if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1819 config->entries[i]->non_unique = TRUE;
1820 config->entries[k]->non_unique = TRUE;
1826 /* add version to non-unique titles */
1827 for (i = 0; i < config->entry_count; i++) {
1830 if (!config->entries[i]->non_unique)
1832 if (!config->entries[i]->version)
1835 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
1836 FreePool(config->entries[i]->title_show);
1837 config->entries[i]->title_show = s;
1838 config->entries[i]->non_unique = FALSE;
1842 for (i = 0; i < config->entry_count; i++) {
1843 for (k = 0; k < config->entry_count; k++) {
1846 if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1850 config->entries[i]->non_unique = TRUE;
1851 config->entries[k]->non_unique = TRUE;
1857 /* add machine-id to non-unique titles */
1858 for (i = 0; i < config->entry_count; i++) {
1862 if (!config->entries[i]->non_unique)
1864 if (!config->entries[i]->machine_id)
1867 m = StrDuplicate(config->entries[i]->machine_id);
1869 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m);
1870 FreePool(config->entries[i]->title_show);
1871 config->entries[i]->title_show = s;
1872 config->entries[i]->non_unique = FALSE;
1877 for (i = 0; i < config->entry_count; i++) {
1878 for (k = 0; k < config->entry_count; k++) {
1881 if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1885 config->entries[i]->non_unique = TRUE;
1886 config->entries[k]->non_unique = TRUE;
1892 /* add file name to non-unique titles */
1893 for (i = 0; i < config->entry_count; i++) {
1896 if (!config->entries[i]->non_unique)
1898 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file);
1899 FreePool(config->entries[i]->title_show);
1900 config->entries[i]->title_show = s;
1901 config->entries[i]->non_unique = FALSE;
1905 static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(void)) {
1908 entry = AllocateZeroPool(sizeof(ConfigEntry));
1909 entry->title = StrDuplicate(title);
1911 entry->no_autoselect = TRUE;
1912 config_add_entry(config, entry);
1916 static BOOLEAN config_entry_add_loader(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
1917 CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
1918 EFI_FILE_HANDLE handle;
1922 /* do not add an entry for ourselves */
1923 if (loaded_image_path && StriCmp(loader, loaded_image_path) == 0)
1926 /* check existence */
1927 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL);
1930 uefi_call_wrapper(handle->Close, 1, handle);
1932 entry = AllocateZeroPool(sizeof(ConfigEntry));
1933 entry->title = StrDuplicate(title);
1934 entry->device = device;
1935 entry->loader = StrDuplicate(loader);
1936 entry->file = StrDuplicate(file);
1937 StrLwr(entry->file);
1938 entry->no_autoselect = TRUE;
1940 config_add_entry(config, entry);
1944 static VOID config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
1945 CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
1946 if (!config_entry_add_loader(config, device, root_dir, loaded_image_path, file, key, title, loader))
1949 /* export identifiers of automatically added entries */
1950 if (config->entries_auto) {
1953 s = PoolPrint(L"%s %s", config->entries_auto, file);
1954 FreePool(config->entries_auto);
1955 config->entries_auto = s;
1957 config->entries_auto = StrDuplicate(file);
1960 static VOID config_entry_add_osx(Config *config) {
1962 UINTN handle_count = 0;
1963 EFI_HANDLE *handles = NULL;
1965 err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles);
1966 if (!EFI_ERROR(err)) {
1969 for (i = 0; i < handle_count; i++) {
1972 root = LibOpenRoot(handles[i]);
1975 config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"OS X",
1976 L"\\System\\Library\\CoreServices\\boot.efi");
1977 uefi_call_wrapper(root->Close, 1, root);
1984 static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
1987 EFI_DEVICE_PATH *path;
1990 path = FileDevicePath(entry->device, entry->loader);
1992 Print(L"Error getting device path.");
1993 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1994 return EFI_INVALID_PARAMETER;
1997 err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
1998 if (EFI_ERROR(err)) {
1999 Print(L"Error loading %s: %r", entry->loader, err);
2000 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2004 if (config->options_edit)
2005 options = config->options_edit;
2006 else if (entry->options)
2007 options = entry->options;
2011 EFI_LOADED_IMAGE *loaded_image;
2013 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (void **)&loaded_image,
2014 parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2015 if (EFI_ERROR(err)) {
2016 Print(L"Error getting LoadedImageProtocol handle: %r", err);
2017 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2020 loaded_image->LoadOptions = options;
2021 loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
2024 efivar_set_time_usec(L"LoaderTimeExecUSec", 0);
2025 err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
2027 uefi_call_wrapper(BS->UnloadImage, 1, image);
2033 static EFI_STATUS reboot_into_firmware(VOID) {
2039 osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
2041 err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size);
2042 if (!EFI_ERROR(err))
2043 osind |= (UINT64)*b;
2046 err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
2050 err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
2051 Print(L"Error calling ResetSystem: %r", err);
2052 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2056 static VOID config_free(Config *config) {
2059 for (i = 0; i < config->entry_count; i++)
2060 config_entry_free(config->entries[i]);
2061 FreePool(config->entries);
2062 FreePool(config->entry_default_pattern);
2063 FreePool(config->options_edit);
2064 FreePool(config->entry_oneshot);
2065 FreePool(config->entries_auto);
2068 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
2072 EFI_LOADED_IMAGE *loaded_image;
2074 CHAR16 *loaded_image_path;
2075 EFI_DEVICE_PATH *device_path;
2079 BOOLEAN menu = FALSE;
2081 InitializeLib(image, sys_table);
2082 init_usec = time_usec();
2083 efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec);
2084 efivar_set(L"LoaderInfo", L"gummiboot " VERSION, FALSE);
2085 s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
2086 efivar_set(L"LoaderFirmwareInfo", s, FALSE);
2088 s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
2089 efivar_set(L"LoaderFirmwareType", s, FALSE);
2092 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (void **)&loaded_image,
2093 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2094 if (EFI_ERROR(err)) {
2095 Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
2096 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2100 /* export the device path this image is started from */
2101 device_path = DevicePathFromHandle(loaded_image->DeviceHandle);
2104 EFI_DEVICE_PATH *path, *paths;
2106 str = DevicePathToStr(device_path);
2107 efivar_set(L"LoaderDeviceIdentifier", str, FALSE);
2110 paths = UnpackDevicePath(device_path);
2111 for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
2112 HARDDRIVE_DEVICE_PATH *drive;
2115 if (DevicePathType(path) != MEDIA_DEVICE_PATH)
2117 if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
2119 drive = (HARDDRIVE_DEVICE_PATH *)path;
2120 if (drive->SignatureType != SIGNATURE_TYPE_GUID)
2123 GuidToString(uuid, (EFI_GUID *)&drive->Signature);
2124 efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
2130 root_dir = LibOpenRoot(loaded_image->DeviceHandle);
2132 Print(L"Unable to open root directory: %r ", err);
2133 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2134 return EFI_LOAD_ERROR;
2137 /* the filesystem path to this image, to prevent adding ourselves to the menu */
2138 loaded_image_path = DevicePathToStr(loaded_image->FilePath);
2139 efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
2141 /* scan "\loader\entries\*.conf" files */
2142 ZeroMem(&config, sizeof(Config));
2143 config_load(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
2145 /* if we find some well-known loaders, add them to the end of the list */
2146 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2147 L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2148 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2149 L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" MACHINE_TYPE_NAME ".efi");
2150 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2151 L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" MACHINE_TYPE_NAME ".efi");
2152 config_entry_add_osx(&config);
2153 efivar_set(L"LoaderEntriesAuto", config.entries_auto, FALSE);
2155 if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
2156 UINT64 osind = (UINT64)*b;
2158 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
2159 config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware);
2163 if (config.entry_count == 0) {
2164 Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2165 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2169 config_title_generate(&config);
2171 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
2172 config_default_entry_select(&config);
2174 /* if no configured entry to select from was found, enable the menu */
2175 if (config.idx_default == -1) {
2176 config.idx_default = 0;
2177 if (config.timeout_sec == 0)
2178 config.timeout_sec = 10;
2181 /* select entry or show menu when key is pressed or timeout is set */
2182 if (config.timeout_sec == 0) {
2185 err = key_read(&key, FALSE);
2186 if (!EFI_ERROR(err)) {
2189 /* find matching key in config entries */
2190 idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key));
2192 config.idx_default = idx;
2202 entry = config.entries[config.idx_default];
2204 efivar_set_time_usec(L"LoaderTimeMenuUSec", 0);
2205 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL);
2206 if (!menu_run(&config, &entry, loaded_image_path))
2209 /* run special entry like "reboot" */
2216 /* export the selected boot entry to the system */
2217 efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
2219 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
2220 err = image_start(image, &config, entry);
2222 if (err == EFI_ACCESS_DENIED || err == EFI_SECURITY_VIOLATION) {
2223 /* Platform is secure boot and requested image isn't
2224 * trusted. Need to go back to prior boot system and
2225 * install more keys or hashes. Signal failure by
2226 * returning the error */
2227 Print(L"\nImage %s gives a security error\n", entry->title);
2228 Print(L"Please enrol the hash or signature of %s\n", entry->loader);
2229 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2234 config.timeout_sec = 0;
2238 FreePool(loaded_image_path);
2239 config_free(&config);
2240 uefi_call_wrapper(root_dir->Close, 1, root_dir);
2241 uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);