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 void cpuid_read(UINT32 info, UINT32 *eax, UINT32 *ebx, UINT32 *ecx, UINT32 *edx) {
101 :"+a" (*eax), "=S" (*ebx), "=c" (*ecx), "=d" (*edx)
106 static UINT64 cpufreq_read(void) {
107 UINT32 eax, ebx, ecx, edx;
110 CHAR8 s[4 * 4 * 3 + 1];
121 for (i = 0; i < 3; i++) {
122 cpuid_read(0x80000002 + i, &eax, &ebx, &ecx, &edx);
128 brand.s[4 * 4 * 3] = '\0';
132 * “x.xxyHz” or “xxxxyHz”, where y=M,G,T
133 * from CPUID brand string:
134 * Intel(R) Core(TM) i5-2540M CPU @ 2.60GHz
136 * http://www.intel.com/content/dam/www/public/us/en/documents/
137 * application-notes/processor-identification-cpuid-instruction-note.pdf
140 for (i = 4; i < (4 * 4 * 3) - 2; i++) {
141 if (brand.s[i+1] == 'H' && brand.s[i+2] == 'z') {
170 str = stra_to_str(s);
171 usec = Atoi(str) * scale;
176 static UINT64 time_usec(void) {
180 ticks = ticks_read();
184 cpufreq = cpufreq_read();
188 return 1000 * 1000 * ticks / cpufreq;
191 static UINT64 time_usec(void) { return 0; }
194 static EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
197 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
199 flags |= EFI_VARIABLE_NON_VOLATILE;
201 return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf);
204 static EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
205 return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
208 static EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
213 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
214 buf = AllocatePool(l);
216 return EFI_OUT_OF_RESOURCES;
218 err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf);
219 if (EFI_ERROR(err) == EFI_SUCCESS) {
229 static EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
235 err = efivar_get_raw(&loader_guid, name, &buf, &size);
236 if (EFI_ERROR(err) != EFI_SUCCESS)
239 val = StrDuplicate((CHAR16 *)buf);
242 return EFI_OUT_OF_RESOURCES;
249 static EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
252 SPrint(str, 32, L"%d", i);
253 return efivar_set(name, str, persistent);
256 static EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
260 err = efivar_get(name, &val);
261 if (EFI_ERROR(err) == EFI_SUCCESS) {
268 static VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
276 SPrint(str, 32, L"%ld", usec);
277 efivar_set(name, str, FALSE);
280 #define EFI_SHIFT_STATE_VALID 0x80000000
281 #define EFI_RIGHT_CONTROL_PRESSED 0x00000004
282 #define EFI_LEFT_CONTROL_PRESSED 0x00000008
283 #define EFI_RIGHT_ALT_PRESSED 0x00000010
284 #define EFI_LEFT_ALT_PRESSED 0x00000020
285 #define EFI_CONTROL_PRESSED (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED)
286 #define EFI_ALT_PRESSED (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED)
287 #define KEYPRESS(keys, scan, uni) ((((UINT64)keys) << 32) | ((scan) << 16) | (uni))
288 #define KEYCHAR(k) ((k) & 0xffff)
289 #define CHAR_CTRL(c) ((c) - 'a' + 1)
291 static EFI_STATUS key_read(UINT64 *key) {
292 #define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
293 { 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } }
295 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
297 typedef EFI_STATUS (EFIAPI *EFI_INPUT_RESET_EX)(
298 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
299 BOOLEAN ExtendedVerification;
302 typedef UINT8 EFI_KEY_TOGGLE_STATE;
305 UINT32 KeyShiftState;
306 EFI_KEY_TOGGLE_STATE KeyToggleState;
311 EFI_KEY_STATE KeyState;
314 typedef EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY_EX)(
315 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
316 EFI_KEY_DATA *KeyData;
319 typedef EFI_STATUS (EFIAPI *EFI_SET_STATE)(
320 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
321 EFI_KEY_TOGGLE_STATE *KeyToggleState;
324 typedef EFI_STATUS (EFIAPI *EFI_KEY_NOTIFY_FUNCTION)(
325 EFI_KEY_DATA *KeyData;
328 typedef EFI_STATUS (EFIAPI *EFI_REGISTER_KEYSTROKE_NOTIFY)(
329 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
330 EFI_KEY_DATA KeyData;
331 EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction;
335 typedef EFI_STATUS (EFIAPI *EFI_UNREGISTER_KEYSTROKE_NOTIFY)(
336 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
337 VOID *NotificationHandle;
340 typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
341 EFI_INPUT_RESET_EX Reset;
342 EFI_INPUT_READ_KEY_EX ReadKeyStrokeEx;
343 EFI_EVENT WaitForKeyEx;
344 EFI_SET_STATE SetState;
345 EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify;
346 EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify;
347 } EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
349 EFI_GUID EfiSimpleTextInputExProtocolGuid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
350 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx;
351 static BOOLEAN checked;
352 EFI_KEY_DATA keydata;
357 err = LibLocateProtocol(&EfiSimpleTextInputExProtocolGuid, (VOID **)&TextInputEx);
366 err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
369 *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
374 err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata);
378 /* do not distinguish between left and right keys */
379 if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
380 if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
381 shift |= EFI_CONTROL_PRESSED;
382 if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
383 shift |= EFI_ALT_PRESSED;
386 /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
387 *key = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
391 static void cursor_left(UINTN *cursor, UINTN *first)
395 else if ((*first) > 0)
399 static void cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
401 if ((*cursor)+1 < x_max)
403 else if ((*first) + (*cursor) < len)
407 static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
420 size = StrLen(line_in) + 1024;
421 line = AllocatePool(size * sizeof(CHAR16));
422 StrCpy(line, line_in);
424 print = AllocatePool((x_max+1) * sizeof(CHAR16));
426 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
442 CopyMem(print, line + first, i * sizeof(CHAR16));
443 while (clear > 0 && i < x_max-1) {
449 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
450 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
451 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
453 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
455 err = key_read(&key);
460 case KEYPRESS(0, SCAN_ESC, 0):
461 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
462 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
463 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
464 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
468 case KEYPRESS(0, SCAN_HOME, 0):
469 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
470 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
471 /* beginning-of-line */
476 case KEYPRESS(0, SCAN_END, 0):
477 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
478 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
480 cursor = len - first;
481 if (cursor+1 >= x_max) {
483 first = len - (x_max-1);
487 case KEYPRESS(0, SCAN_DOWN, 0):
488 case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
489 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
491 while(line[first + cursor] && line[first + cursor] == ' ')
492 cursor_right(&cursor, &first, x_max, len);
493 while(line[first + cursor] && line[first + cursor] != ' ')
494 cursor_right(&cursor, &first, x_max, len);
495 while(line[first + cursor] && line[first + cursor] == ' ')
496 cursor_right(&cursor, &first, x_max, len);
497 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
500 case KEYPRESS(0, SCAN_UP, 0):
501 case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
502 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
504 while((first + cursor) && line[first + cursor] == ' ')
505 cursor_left(&cursor, &first);
506 while((first + cursor) && line[first + cursor] != ' ')
507 cursor_left(&cursor, &first);
508 while((first + cursor) && line[first + cursor] == ' ')
509 cursor_left(&cursor, &first);
510 if (first + cursor != len && first + cursor)
511 cursor_right(&cursor, &first, x_max, len);
512 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
515 case KEYPRESS(0, SCAN_RIGHT, 0):
516 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
517 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
519 if (first + cursor == len)
521 cursor_right(&cursor, &first, x_max, len);
522 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
525 case KEYPRESS(0, SCAN_LEFT, 0):
526 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
527 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
529 cursor_left(&cursor, &first);
530 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
533 case KEYPRESS(0, SCAN_DELETE, 0):
534 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
535 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
538 if (first + cursor == len)
540 for (i = first + cursor; i < len; i++)
546 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
547 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
549 line[first + cursor] = '\0';
550 clear = len - (first + cursor);
551 len = first + cursor;
554 case KEYPRESS(0, 0, CHAR_LINEFEED):
555 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
556 if (StrCmp(line, line_in) != 0) {
564 case KEYPRESS(0, 0, CHAR_BACKSPACE):
567 if (first == 0 && cursor == 0)
569 for (i = first + cursor-1; i < len; i++)
575 if (cursor > 0 || first == 0)
577 /* show full line if it fits */
583 /* jump left to see what we delete */
593 case KEYPRESS(0, 0, '\t'):
594 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
595 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
598 for (i = len; i > first + cursor; i--)
600 line[first + cursor] = KEYCHAR(key);
603 if (cursor+1 < x_max)
605 else if (first + cursor < len)
611 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
617 static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
620 /* select entry by number key */
621 if (key >= '1' && key <= '9') {
623 if (i > config->entry_count)
624 i = config->entry_count;
628 /* find matching key in config entries */
629 for (i = start; i < config->entry_count; i++) {
630 if (config->entries[i]->key == '\0')
632 if (config->entries[i]->key != key)
638 for (i = 0; i < start; i++) {
639 if (config->entries[i]->key == '\0')
641 if (config->entries[i]->key != key)
650 static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
658 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
659 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
661 Print(L"gummiboot version: " VERSION "\n");
662 Print(L"loaded image: %s\n", loaded_image_path);
663 Print(L"UEFI version: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
664 Print(L"firmware vendor: %s\n", ST->FirmwareVendor);
665 Print(L"firmware version: %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
666 if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
667 Print(L"SecureBoot: %s\n", *b > 0 ? L"enabled" : L"disabled");
671 if (efivar_get_raw(&global_guid, L"SetupMode", &b, &size) == EFI_SUCCESS) {
672 Print(L"SetupMode: %s\n", *b > 0 ? L"setup" : L"user");
676 if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
677 Print(L"OsIndicationsSupported: %d\n", (UINT64)*b);
682 Print(L"timeout: %d\n", config->timeout_sec);
683 if (config->timeout_sec_efivar >= 0)
684 Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar);
685 Print(L"timeout (config): %d\n", config->timeout_sec_config);
686 if (config->entry_default_pattern)
687 Print(L"default pattern: '%s'\n", config->entry_default_pattern);
690 Print(L"config entry count: %d\n", config->entry_count);
691 Print(L"entry selected idx: %d\n", config->idx_default);
692 if (config->idx_default_efivar >= 0)
693 Print(L"entry EFI var idx: %d\n", config->idx_default_efivar);
696 if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS)
697 Print(L"LoaderConfigTimeout: %d\n", i);
698 if (config->entry_oneshot)
699 Print(L"LoaderEntryOneShot: %s\n", config->entry_oneshot);
700 if (efivar_get(L"LoaderDeviceIdentifier", &s) == EFI_SUCCESS) {
701 Print(L"LoaderDeviceIdentifier: %s\n", s);
704 if (efivar_get(L"LoaderDevicePartUUID", &s) == EFI_SUCCESS) {
705 Print(L"LoaderDevicePartUUID: %s\n", s);
708 if (efivar_get(L"LoaderEntryDefault", &s) == EFI_SUCCESS) {
709 Print(L"LoaderEntryDefault: %s\n", s);
713 Print(L"\n--- press key ---\n\n");
714 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
715 uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
717 for (i = 0; i < config->entry_count; i++) {
720 if (key.ScanCode == SCAN_ESC || key.UnicodeChar == 'q')
723 entry = config->entries[i];
724 Print(L"config entry: %d/%d\n", i+1, config->entry_count);
726 Print(L"file '%s'\n", entry->file);
727 Print(L"title show '%s'\n", entry->title_show);
729 Print(L"title '%s'\n", entry->title);
731 Print(L"version '%s'\n", entry->version);
732 if (entry->machine_id)
733 Print(L"machine-id '%s'\n", entry->machine_id);
735 EFI_DEVICE_PATH *device_path;
738 device_path = DevicePathFromHandle(entry->device);
740 str = DevicePathToStr(device_path);
741 Print(L"device handle '%s'\n", str);
746 Print(L"loader '%s'\n", entry->loader);
748 Print(L"options '%s'\n", entry->options);
749 Print(L"auto-select %s\n", entry->no_autoselect ? L"no" : L"yes");
751 Print(L"internal call yes\n");
753 Print(L"\n--- press key ---\n\n");
754 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
755 uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
758 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
761 static EFI_STATUS console_text_mode(VOID) {
762 #define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \
763 { 0xf42f7782, 0x12e, 0x4c12, { 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21 } };
765 struct _EFI_CONSOLE_CONTROL_PROTOCOL;
768 EfiConsoleControlScreenText,
769 EfiConsoleControlScreenGraphics,
770 EfiConsoleControlScreenMaxValue,
771 } EFI_CONSOLE_CONTROL_SCREEN_MODE;
773 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE)(
774 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
775 EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode,
780 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE)(
781 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
782 EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
785 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN)(
786 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
790 typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
791 EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode;
792 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
793 EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn;
794 } EFI_CONSOLE_CONTROL_PROTOCOL;
796 EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
797 EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
800 err = LibLocateProtocol(&ConsoleControlProtocolGuid, (VOID **)&ConsoleControl);
803 return uefi_call_wrapper(ConsoleControl->SetMode, 2, ConsoleControl, EfiConsoleControlScreenText);
806 static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *loaded_image_path) {
810 UINTN idx_highlight_prev;
826 BOOLEAN exit = FALSE;
830 uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
831 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
832 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
833 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
835 err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
836 if (EFI_ERROR(err)) {
841 /* we check 10 times per second for a keystroke */
842 if (config->timeout_sec > 0)
843 timeout_remain = config->timeout_sec * 10;
847 idx_highlight = config->idx_default;
848 idx_highlight_prev = 0;
850 visible_max = y_max - 2;
852 if ((UINTN)config->idx_default >= visible_max)
853 idx_first = config->idx_default-1;
857 idx_last = idx_first + visible_max-1;
862 /* length of the longest entry */
864 for (i = 0; i < config->entry_count; i++) {
867 entry_len = StrLen(config->entries[i]->title_show);
868 if (line_width < entry_len)
869 line_width = entry_len;
871 if (line_width > x_max-6)
872 line_width = x_max-6;
874 /* offsets to center the entries on the screen */
875 x_start = (x_max - (line_width)) / 2;
876 if (config->entry_count < visible_max)
877 y_start = ((visible_max - config->entry_count) / 2) + 1;
881 /* menu entries title lines */
882 lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
883 for (i = 0; i < config->entry_count; i++) {
886 lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
887 for (j = 0; j < x_start; j++)
890 for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
891 lines[i][j] = config->entries[i]->title_show[k];
893 for (; j < x_max; j++)
895 lines[i][x_max] = '\0';
899 clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
900 for (i = 0; i < x_max; i++)
908 for (i = 0; i < config->entry_count; i++) {
909 if (i < idx_first || i > idx_last)
911 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
912 if (i == idx_highlight)
913 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
914 EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
916 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
917 EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
918 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
919 if ((INTN)i == config->idx_default_efivar) {
920 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
921 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
925 } else if (highlight) {
926 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
927 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
928 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
929 if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
930 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
931 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
934 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
935 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
936 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
937 if ((INTN)idx_highlight == config->idx_default_efivar) {
938 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
939 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
944 if (timeout_remain > 0) {
946 status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
949 /* print status at last line of screen */
955 len = StrLen(status);
957 x = (x_max - len) / 2;
960 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
961 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
962 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
963 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
964 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
967 err = key_read(&key);
968 if (err != EFI_SUCCESS) {
971 if (timeout_remain == 0) {
975 if (timeout_remain > 0) {
976 uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
980 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
985 /* clear status after keystroke */
989 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
990 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
991 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
994 idx_highlight_prev = idx_highlight;
997 case KEYPRESS(0, SCAN_UP, 0):
998 case KEYPRESS(0, 0, 'k'):
999 if (idx_highlight > 0)
1003 case KEYPRESS(0, SCAN_DOWN, 0):
1004 case KEYPRESS(0, 0, 'j'):
1005 if (idx_highlight < config->entry_count-1)
1009 case KEYPRESS(0, SCAN_HOME, 0):
1010 if (idx_highlight > 0) {
1016 case KEYPRESS(0, SCAN_END, 0):
1017 if (idx_highlight < config->entry_count-1) {
1019 idx_highlight = config->entry_count-1;
1023 case KEYPRESS(0, SCAN_PAGE_UP, 0):
1024 if (idx_highlight > visible_max)
1025 idx_highlight -= visible_max;
1030 case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
1031 idx_highlight += visible_max;
1032 if (idx_highlight > config->entry_count-1)
1033 idx_highlight = config->entry_count-1;
1036 case KEYPRESS(0, 0, CHAR_LINEFEED):
1037 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
1041 case KEYPRESS(0, SCAN_F1, 0):
1042 case KEYPRESS(0, 0, 'h'):
1043 case KEYPRESS(0, 0, '?'):
1044 status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
1047 case KEYPRESS(0, 0, 'Q'):
1052 case KEYPRESS(0, 0, 'd'):
1053 if (config->idx_default_efivar != (INTN)idx_highlight) {
1054 /* store the selected entry in a persistent EFI variable */
1055 efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
1056 config->idx_default_efivar = idx_highlight;
1057 status = StrDuplicate(L"Default boot entry selected.");
1059 /* clear the default entry EFI variable */
1060 efivar_set(L"LoaderEntryDefault", NULL, TRUE);
1061 config->idx_default_efivar = -1;
1062 status = StrDuplicate(L"Default boot entry cleared.");
1067 case KEYPRESS(0, 0, '-'):
1068 case KEYPRESS(0, 0, 'T'):
1069 if (config->timeout_sec_efivar > 0) {
1070 config->timeout_sec_efivar--;
1071 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
1072 if (config->timeout_sec_efivar > 0)
1073 status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
1075 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1076 } else if (config->timeout_sec_efivar <= 0){
1077 config->timeout_sec_efivar = -1;
1078 efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
1079 if (config->timeout_sec_config > 0)
1080 status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
1081 config->timeout_sec_config);
1083 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1087 case KEYPRESS(0, 0, '+'):
1088 case KEYPRESS(0, 0, 't'):
1089 if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
1090 config->timeout_sec_efivar++;
1091 config->timeout_sec_efivar++;
1092 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
1093 if (config->timeout_sec_efivar > 0)
1094 status = PoolPrint(L"Menu timeout set to %d sec.",
1095 config->timeout_sec_efivar);
1097 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1100 case KEYPRESS(0, 0, 'e'):
1101 /* only the options of configured entries can be edited */
1102 if (config->entries[idx_highlight]->type == LOADER_UNDEFINED)
1104 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
1105 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
1106 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
1107 if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
1109 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
1110 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
1113 case KEYPRESS(0, 0, 'v'):
1114 status = PoolPrint(L"gummiboot " VERSION ", UEFI %d.%02d, %s %d.%02d",
1115 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff,
1116 ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
1119 case KEYPRESS(0, 0, 'P'):
1120 print_status(config, loaded_image_path);
1125 /* jump with a hotkey directly to a matching entry */
1126 idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
1129 idx_highlight = idx;
1133 if (idx_highlight > idx_last) {
1134 idx_last = idx_highlight;
1135 idx_first = 1 + idx_highlight - visible_max;
1138 if (idx_highlight < idx_first) {
1139 idx_first = idx_highlight;
1140 idx_last = idx_highlight + visible_max-1;
1144 idx_last = idx_first + visible_max-1;
1146 if (!refresh && idx_highlight != idx_highlight_prev)
1150 *chosen_entry = config->entries[idx_highlight];
1152 for (i = 0; i < config->entry_count; i++)
1155 FreePool(clearline);
1157 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
1158 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
1162 static VOID config_add_entry(Config *config, ConfigEntry *entry) {
1163 if ((config->entry_count & 15) == 0) {
1166 i = config->entry_count + 16;
1167 if (config->entry_count == 0)
1168 config->entries = AllocatePool(sizeof(VOID *) * i);
1170 config->entries = ReallocatePool(config->entries,
1171 sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
1173 config->entries[config->entry_count++] = entry;
1176 static VOID config_entry_free(ConfigEntry *entry) {
1177 FreePool(entry->title_show);
1178 FreePool(entry->title);
1179 FreePool(entry->machine_id);
1180 FreePool(entry->loader);
1181 FreePool(entry->options);
1184 static BOOLEAN is_digit(CHAR16 c)
1186 return (c >= '0') && (c <= '9');
1189 static UINTN c_order(CHAR16 c)
1195 else if ((c >= 'a') && (c <= 'z'))
1201 static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
1206 while (*s1 || *s2) {
1209 while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
1212 order = c_order(*s1) - c_order(*s2);
1225 while (is_digit(*s1) && is_digit(*s2)) {
1241 return StrCmp(os1, os2);
1244 static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
1251 else if ((stra[0] & 0xe0) == 0xc0)
1253 else if ((stra[0] & 0xf0) == 0xe0)
1255 else if ((stra[0] & 0xf8) == 0xf0)
1257 else if ((stra[0] & 0xfc) == 0xf8)
1259 else if ((stra[0] & 0xfe) == 0xfc)
1269 unichar = stra[0] & 0x1f;
1272 unichar = stra[0] & 0x0f;
1275 unichar = stra[0] & 0x07;
1278 unichar = stra[0] & 0x03;
1281 unichar = stra[0] & 0x01;
1285 for (i = 1; i < len; i++) {
1286 if ((stra[i] & 0xc0) != 0x80)
1289 unichar |= stra[i] & 0x3f;
1296 static CHAR16 *stra_to_str(CHAR8 *stra) {
1302 len = strlena(stra);
1303 str = AllocatePool((len + 1) * sizeof(CHAR16));
1310 utf8len = utf8_to_16(stra + i, str + strlen);
1312 /* invalid utf8 sequence, skip the garbage */
1324 static CHAR16 *stra_to_path(CHAR8 *stra) {
1330 len = strlena(stra);
1331 str = AllocatePool((len + 2) * sizeof(CHAR16));
1339 utf8len = utf8_to_16(stra + i, str + strlen);
1341 /* invalid utf8 sequence, skip the garbage */
1346 if (str[strlen] == '/')
1348 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
1349 /* skip double slashes */
1361 static CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
1369 static CHAR8 *line_get_key_value(CHAR8 *content, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
1375 line = content + *pos;
1380 while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
1383 /* move pos to next line */
1392 /* terminate line */
1393 line[linelen] = '\0';
1395 /* remove leading whitespace */
1396 while (strchra((CHAR8 *)" \t", *line)) {
1401 /* remove trailing whitespace */
1402 while (linelen > 0 && strchra((CHAR8 *)" \t", line[linelen-1]))
1404 line[linelen] = '\0';
1409 /* split key/value */
1411 while (*value && !strchra((CHAR8 *)" \t", *value))
1417 while (*value && strchra((CHAR8 *)" \t", *value))
1425 static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
1431 while ((line = line_get_key_value(content, &pos, &key, &value))) {
1432 if (strcmpa((CHAR8 *)"timeout", key) == 0) {
1435 s = stra_to_str(value);
1436 config->timeout_sec_config = Atoi(s);
1437 config->timeout_sec = config->timeout_sec_config;
1441 if (strcmpa((CHAR8 *)"default", key) == 0) {
1442 config->entry_default_pattern = stra_to_str(value);
1443 StrLwr(config->entry_default_pattern);
1449 static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
1455 CHAR16 *initrd = NULL;
1457 entry = AllocateZeroPool(sizeof(ConfigEntry));
1460 while ((line = line_get_key_value(content, &pos, &key, &value))) {
1461 if (strcmpa((CHAR8 *)"title", key) == 0) {
1462 FreePool(entry->title);
1463 entry->title = stra_to_str(value);
1467 if (strcmpa((CHAR8 *)"version", key) == 0) {
1468 FreePool(entry->version);
1469 entry->version = stra_to_str(value);
1473 if (strcmpa((CHAR8 *)"machine-id", key) == 0) {
1474 FreePool(entry->machine_id);
1475 entry->machine_id = stra_to_str(value);
1479 if (strcmpa((CHAR8 *)"linux", key) == 0) {
1480 FreePool(entry->loader);
1481 entry->type = LOADER_LINUX;
1482 entry->loader = stra_to_path(value);
1487 if (strcmpa((CHAR8 *)"efi", key) == 0) {
1488 entry->type = LOADER_EFI;
1489 FreePool(entry->loader);
1490 entry->loader = stra_to_path(value);
1492 /* do not add an entry for ourselves */
1493 if (StriCmp(entry->loader, loaded_image_path) == 0) {
1494 entry->type = LOADER_UNDEFINED;
1500 if (strcmpa((CHAR8 *)"initrd", key) == 0) {
1503 new = stra_to_path(value);
1507 s = PoolPrint(L"%s initrd=%s", initrd, new);
1511 initrd = PoolPrint(L"initrd=%s", new);
1516 if (strcmpa((CHAR8 *)"options", key) == 0) {
1519 new = stra_to_str(value);
1520 if (entry->options) {
1523 s = PoolPrint(L"%s %s", entry->options, new);
1524 FreePool(entry->options);
1527 entry->options = new;
1535 if (entry->type == LOADER_UNDEFINED) {
1536 config_entry_free(entry);
1542 /* add initrd= to options */
1543 if (entry->type == LOADER_LINUX && initrd) {
1544 if (entry->options) {
1547 s = PoolPrint(L"%s %s", initrd, entry->options);
1548 FreePool(entry->options);
1551 entry->options = initrd;
1557 if (entry->machine_id) {
1560 /* append additional options from EFI variables for this machine-id */
1561 var = PoolPrint(L"LoaderEntryOptions-%s", entry->machine_id);
1565 if (efivar_get(var, &s) == EFI_SUCCESS) {
1566 if (entry->options) {
1569 s2 = PoolPrint(L"%s %s", entry->options, s);
1570 FreePool(entry->options);
1571 entry->options = s2;
1578 var = PoolPrint(L"LoaderEntryOptionsOneShot-%s", entry->machine_id);
1582 if (efivar_get(var, &s) == EFI_SUCCESS) {
1583 if (entry->options) {
1586 s2 = PoolPrint(L"%s %s", entry->options, s);
1587 FreePool(entry->options);
1588 entry->options = s2;
1591 efivar_set(var, NULL, TRUE);
1597 entry->device = device;
1598 entry->file = StrDuplicate(file);
1599 len = StrLen(entry->file);
1600 /* remove ".conf" */
1602 entry->file[len - 5] = '\0';
1603 StrLwr(entry->file);
1605 config_add_entry(config, entry);
1608 static UINTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, CHAR8 **content) {
1609 EFI_FILE_HANDLE handle;
1610 EFI_FILE_INFO *info;
1616 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
1620 info = LibFileInfo(handle);
1621 buflen = info->FileSize+1;
1622 buf = AllocatePool(buflen);
1624 err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
1625 if (EFI_ERROR(err) == EFI_SUCCESS) {
1633 uefi_call_wrapper(handle->Close, 1, handle);
1638 static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
1639 EFI_FILE_HANDLE entries_dir;
1641 CHAR8 *content = NULL;
1646 len = file_read(root_dir, L"\\loader\\loader.conf", &content);
1648 config_defaults_load_from_file(config, content);
1651 err = efivar_get_int(L"LoaderConfigTimeout", &sec);
1652 if (EFI_ERROR(err) == EFI_SUCCESS) {
1653 config->timeout_sec_efivar = sec;
1654 config->timeout_sec = sec;
1656 config->timeout_sec_efivar = -1;
1658 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
1659 if (EFI_ERROR(err) == EFI_SUCCESS) {
1664 CHAR8 *content = NULL;
1667 bufsize = sizeof(buf);
1668 err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
1669 if (bufsize == 0 || EFI_ERROR(err))
1672 f = (EFI_FILE_INFO *) buf;
1673 if (f->FileName[0] == '.')
1675 if (f->Attribute & EFI_FILE_DIRECTORY)
1677 len = StrLen(f->FileName);
1680 if (StriCmp(f->FileName + len - 5, L".conf") != 0)
1683 len = file_read(entries_dir, f->FileName, &content);
1685 config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path);
1688 uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
1691 /* sort entries after version number */
1692 for (i = 1; i < config->entry_count; i++) {
1697 for (k = 0; k < config->entry_count - i; k++) {
1700 if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0)
1702 entry = config->entries[k];
1703 config->entries[k] = config->entries[k+1];
1704 config->entries[k+1] = entry;
1712 static VOID config_default_entry_select(Config *config) {
1718 * The EFI variable to specify a boot entry for the next, and only the
1719 * next reboot. The variable is always cleared directly after it is read.
1721 err = efivar_get(L"LoaderEntryOneShot", &var);
1722 if (EFI_ERROR(err) == EFI_SUCCESS) {
1723 BOOLEAN found = FALSE;
1725 for (i = 0; i < config->entry_count; i++) {
1726 if (StrCmp(config->entries[i]->file, var) == 0) {
1727 config->idx_default = i;
1733 config->entry_oneshot = StrDuplicate(var);
1734 efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
1741 * The EFI variable to select the default boot entry overrides the
1742 * configured pattern. The variable can be set and cleared by pressing
1743 * the 'd' key in the loader selection menu, the entry is marked with
1746 err = efivar_get(L"LoaderEntryDefault", &var);
1747 if (EFI_ERROR(err) == EFI_SUCCESS) {
1748 BOOLEAN found = FALSE;
1750 for (i = 0; i < config->entry_count; i++) {
1751 if (StrCmp(config->entries[i]->file, var) == 0) {
1752 config->idx_default = i;
1753 config->idx_default_efivar = i;
1762 config->idx_default_efivar = -1;
1764 if (config->entry_count == 0)
1768 * Match the pattern from the end of the list to the start, find last
1769 * entry (largest number) matching the given pattern.
1771 if (config->entry_default_pattern) {
1772 i = config->entry_count;
1774 if (config->entries[i]->no_autoselect)
1776 if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
1777 config->idx_default = i;
1783 /* select the last suitable entry */
1784 i = config->entry_count;
1786 if (config->entries[i]->no_autoselect)
1788 config->idx_default = i;
1792 /* no entry found */
1793 config->idx_default = -1;
1796 /* generate a unique title, avoiding non-distinguishable menu entries */
1797 static VOID config_title_generate(Config *config) {
1802 for (i = 0; i < config->entry_count; i++) {
1805 FreePool(config->entries[i]->title_show);
1806 title = config->entries[i]->title;
1808 title = config->entries[i]->file;
1809 config->entries[i]->title_show = StrDuplicate(title);
1813 for (i = 0; i < config->entry_count; i++) {
1814 for (k = 0; k < config->entry_count; k++) {
1817 if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1821 config->entries[i]->non_unique = TRUE;
1822 config->entries[k]->non_unique = TRUE;
1828 /* add version to non-unique titles */
1829 for (i = 0; i < config->entry_count; i++) {
1832 if (!config->entries[i]->non_unique)
1834 if (!config->entries[i]->version)
1837 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
1838 FreePool(config->entries[i]->title_show);
1839 config->entries[i]->title_show = s;
1840 config->entries[i]->non_unique = FALSE;
1844 for (i = 0; i < config->entry_count; i++) {
1845 for (k = 0; k < config->entry_count; k++) {
1848 if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1852 config->entries[i]->non_unique = TRUE;
1853 config->entries[k]->non_unique = TRUE;
1859 /* add machine-id to non-unique titles */
1860 for (i = 0; i < config->entry_count; i++) {
1864 if (!config->entries[i]->non_unique)
1866 if (!config->entries[i]->machine_id)
1869 m = StrDuplicate(config->entries[i]->machine_id);
1871 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m);
1872 FreePool(config->entries[i]->title_show);
1873 config->entries[i]->title_show = s;
1874 config->entries[i]->non_unique = FALSE;
1879 for (i = 0; i < config->entry_count; i++) {
1880 for (k = 0; k < config->entry_count; k++) {
1883 if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1887 config->entries[i]->non_unique = TRUE;
1888 config->entries[k]->non_unique = TRUE;
1894 /* add file name to non-unique titles */
1895 for (i = 0; i < config->entry_count; i++) {
1898 if (!config->entries[i]->non_unique)
1900 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file);
1901 FreePool(config->entries[i]->title_show);
1902 config->entries[i]->title_show = s;
1903 config->entries[i]->non_unique = FALSE;
1907 static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(void)) {
1910 entry = AllocateZeroPool(sizeof(ConfigEntry));
1911 entry->title = StrDuplicate(title);
1913 entry->no_autoselect = TRUE;
1914 config_add_entry(config, entry);
1918 static BOOLEAN config_entry_add_loader(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
1919 CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
1920 EFI_FILE_HANDLE handle;
1924 /* do not add an entry for ourselves */
1925 if (loaded_image_path && StriCmp(loader, loaded_image_path) == 0)
1928 /* check existence */
1929 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL);
1932 uefi_call_wrapper(handle->Close, 1, handle);
1934 entry = AllocateZeroPool(sizeof(ConfigEntry));
1935 entry->title = StrDuplicate(title);
1936 entry->device = device;
1937 entry->loader = StrDuplicate(loader);
1938 entry->file = StrDuplicate(file);
1939 StrLwr(entry->file);
1940 entry->no_autoselect = TRUE;
1942 config_add_entry(config, entry);
1946 static VOID config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
1947 CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
1948 if (!config_entry_add_loader(config, device, root_dir, loaded_image_path, file, key, title, loader))
1951 /* export identifiers of automatically added entries */
1952 if (config->entries_auto) {
1955 s = PoolPrint(L"%s %s", config->entries_auto, file);
1956 FreePool(config->entries_auto);
1957 config->entries_auto = s;
1959 config->entries_auto = StrDuplicate(file);
1962 static VOID config_entry_add_osx(Config *config) {
1964 UINTN handle_count = 0;
1965 EFI_HANDLE *handles = NULL;
1967 err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles);
1968 if (EFI_ERROR(err) == EFI_SUCCESS) {
1971 for (i = 0; i < handle_count; i++) {
1974 root = LibOpenRoot(handles[i]);
1977 config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'm', L"OS X",
1978 L"\\System\\Library\\CoreServices\\boot.efi");
1979 uefi_call_wrapper(root->Close, 1, root);
1986 static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
1989 EFI_DEVICE_PATH *path;
1992 path = FileDevicePath(entry->device, entry->loader);
1994 Print(L"Error getting device path.");
1995 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1996 return EFI_INVALID_PARAMETER;
1999 err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
2000 if (EFI_ERROR(err)) {
2001 Print(L"Error loading %s: %r", entry->loader, err);
2002 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2006 if (config->options_edit)
2007 options = config->options_edit;
2008 else if (entry->options)
2009 options = entry->options;
2013 EFI_LOADED_IMAGE *loaded_image;
2015 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (void **)&loaded_image,
2016 parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2017 if (EFI_ERROR(err)) {
2018 Print(L"Error getting LoadedImageProtocol handle: %r", err);
2019 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2022 loaded_image->LoadOptions = options;
2023 loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
2026 efivar_set_time_usec(L"LoaderTimeExecUSec", 0);
2027 err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
2029 uefi_call_wrapper(BS->UnloadImage, 1, image);
2035 static EFI_STATUS reboot_into_firmware(VOID) {
2041 osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
2043 err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size);
2044 if (err == EFI_SUCCESS)
2045 osind |= (UINT64)*b;
2048 err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
2049 if (err != EFI_SUCCESS)
2052 err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
2053 Print(L"Error calling ResetSystem: %r", err);
2054 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2058 static VOID config_free(Config *config) {
2061 for (i = 0; i < config->entry_count; i++)
2062 config_entry_free(config->entries[i]);
2063 FreePool(config->entries);
2064 FreePool(config->entry_default_pattern);
2065 FreePool(config->options_edit);
2066 FreePool(config->entry_oneshot);
2067 FreePool(config->entries_auto);
2070 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
2074 EFI_LOADED_IMAGE *loaded_image;
2076 CHAR16 *loaded_image_path;
2077 EFI_DEVICE_PATH *device_path;
2081 BOOLEAN menu = FALSE;
2083 InitializeLib(image, sys_table);
2084 init_usec = time_usec();
2085 efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec);
2086 efivar_set(L"LoaderInfo", L"gummiboot " VERSION, FALSE);
2087 s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
2088 efivar_set(L"LoaderFirmwareInfo", s, FALSE);
2090 s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
2091 efivar_set(L"LoaderFirmwareType", s, FALSE);
2094 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (void **)&loaded_image,
2095 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2096 if (EFI_ERROR(err)) {
2097 Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
2098 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2102 /* export the device path this image is started from */
2103 device_path = DevicePathFromHandle(loaded_image->DeviceHandle);
2106 EFI_DEVICE_PATH *path, *paths;
2108 str = DevicePathToStr(device_path);
2109 efivar_set(L"LoaderDeviceIdentifier", str, FALSE);
2112 paths = UnpackDevicePath(device_path);
2113 for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
2114 HARDDRIVE_DEVICE_PATH *drive;
2117 if (DevicePathType(path) != MEDIA_DEVICE_PATH)
2119 if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
2121 drive = (HARDDRIVE_DEVICE_PATH *)path;
2122 if (drive->SignatureType != SIGNATURE_TYPE_GUID)
2125 GuidToString(uuid, (EFI_GUID *)&drive->Signature);
2126 efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
2132 root_dir = LibOpenRoot(loaded_image->DeviceHandle);
2134 Print(L"Unable to open root directory: %r ", err);
2135 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2136 return EFI_LOAD_ERROR;
2139 /* the filesystem path to this image, to prevent adding ourselves to the menu */
2140 loaded_image_path = DevicePathToStr(loaded_image->FilePath);
2141 efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
2143 /* scan "\loader\entries\*.conf" files */
2144 ZeroMem(&config, sizeof(Config));
2145 config_load(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
2147 /* if we find some well-known loaders, add them to the end of the list */
2148 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2149 L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2150 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2151 L"auto-efi-shell", 's', L"EFI Shell", L"\\shellx64.efi");
2152 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2153 L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\BOOT\\BOOTX64.EFI");
2154 config_entry_add_osx(&config);
2155 efivar_set(L"LoaderEntriesAuto", config.entries_auto, FALSE);
2157 if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
2158 UINT64 osind = (UINT64)*b;
2160 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
2161 config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware);
2165 if (config.entry_count == 0) {
2166 Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2167 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2171 config_title_generate(&config);
2173 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
2174 config_default_entry_select(&config);
2176 /* if no configured entry to select from was found, enable the menu */
2177 if (config.idx_default == -1) {
2178 config.idx_default = 0;
2179 if (config.timeout_sec == 0)
2180 config.timeout_sec = 10;
2183 /* select entry or show menu when key is pressed or timeout is set */
2184 if (config.timeout_sec == 0) {
2187 err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
2188 if (err == EFI_SUCCESS) {
2191 /* find matching key in config entries */
2192 idx = entry_lookup_key(&config, 0, k.UnicodeChar);
2194 config.idx_default = idx;
2204 entry = config.entries[config.idx_default];
2206 efivar_set_time_usec(L"LoaderTimeMenuUSec", 0);
2207 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL);
2208 if (!menu_run(&config, &entry, loaded_image_path))
2211 /* run special entry like "reboot" */
2218 /* export the selected boot entry to the system */
2219 efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
2221 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
2222 err = image_start(image, &config, entry);
2224 if (err == EFI_ACCESS_DENIED || err == EFI_SECURITY_VIOLATION) {
2225 /* Platform is secure boot and requested image isn't
2226 * trusted. Need to go back to prior boot system and
2227 * install more keys or hashes. Signal failure by
2228 * returning the error */
2229 Print(L"\nImage %s gives a security error\n", entry->title);
2230 Print(L"Please enrol the hash or signature of %s\n", entry->loader);
2231 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2236 config.timeout_sec = 0;
2240 FreePool(loaded_image_path);
2241 config_free(&config);
2242 uefi_call_wrapper(root_dir->Close, 1, root_dir);
2243 uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);