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 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * Copyright (C) 2012 Kay Sievers <kay.sievers@vrfy.org>
17 * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
19 * "Any intelligent fool can make things bigger, more complex, and more violent."
27 * Allocated random UUID, intended to be shared across tools that implement
28 * the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
29 * associated EFI variables.
31 static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
42 enum loader_type type;
50 ConfigEntry **entries;
53 INTN idx_default_efivar;
55 UINTN timeout_sec_config;
56 INTN timeout_sec_efivar;
57 CHAR16 *entry_default_pattern;
65 __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
69 static UINT64 tsc() { return 0; }
72 static EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
75 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
77 flags |= EFI_VARIABLE_NON_VOLATILE;
79 return uefi_call_wrapper(RT->SetVariable, 5, name, &loader_guid, flags,
80 value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, value);
83 static EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
88 size = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
89 val = AllocatePool(size);
91 return EFI_OUT_OF_RESOURCES;
93 err = uefi_call_wrapper(RT->GetVariable, 5, name, &loader_guid, NULL, &size, val);
94 if (EFI_ERROR(err) == 0)
102 static EFI_STATUS efivar_set_int(CHAR16 *name, INTN i, BOOLEAN persistent) {
105 SPrint(str, 32, L"%d", i);
106 return efivar_set(name, str, persistent);
109 static EFI_STATUS efivar_get_int(CHAR16 *name, INTN *i) {
113 err = efivar_get(name, &val);
114 if (EFI_ERROR(err) == 0) {
121 static VOID efivar_set_ticks(CHAR16 *name, UINT64 ticks) {
124 SPrint(str, 32, L"%ld", ticks ? ticks : tsc());
125 efivar_set(name, str, FALSE);
128 static BOOLEAN edit_line(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
140 size = StrLen(line_in) + 1024;
141 line = AllocatePool(size * sizeof(CHAR16));
142 StrCpy(line, line_in);
144 print = AllocatePool(x_max * sizeof(CHAR16));
146 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
161 CopyMem(print, line + first, i * sizeof(CHAR16));
165 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
166 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
167 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
169 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
170 err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
174 switch (key.ScanCode) {
186 if (cursor >= x_max) {
188 first = len - (x_max-2);
192 if (first + cursor == len)
194 if (cursor+2 < x_max)
196 else if (first + cursor < len)
198 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
205 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
210 if (first + cursor == len)
212 for (i = first + cursor; i < len; i++)
219 switch (key.UnicodeChar) {
221 case CHAR_CARRIAGE_RETURN:
222 if (StrCmp(line, line_in) != 0) {
232 if (first == 0 && cursor == 0)
234 for (i = first + cursor-1; i < len; i++)
239 if (cursor > 0 || first == 0)
241 /* show full line if it fits */
247 /* jump left to see what we delete */
258 case 0x80 ... 0xffff:
261 for (i = len; i > first + cursor; i--)
263 line[first + cursor] = key.UnicodeChar;
266 if (cursor+2 < x_max)
268 else if (first + cursor < len)
274 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
279 static VOID menu_run(Config *config, ConfigEntry **chosen_entry) {
283 INTN idx_highlight_prev;
296 BOOLEAN exit = FALSE;
298 uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
299 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
300 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
301 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
303 err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
304 if (EFI_ERROR(err)) {
309 /* we check 10 times per second for a keystroke */
310 if (config->timeout_sec > 0)
311 timeout_remain = config->timeout_sec * 10;
315 idx_highlight = config->idx_default;
316 idx_highlight_prev = 0;
318 visible_max = y_max - 2;
320 if (config->idx_default >= visible_max)
321 idx_first = config->idx_default-1;
325 idx_last = idx_first + visible_max-1;
330 /* length of higlighted selector bar */
332 for (i = 0; i < config->entry_count; i++) {
335 entry_len = StrLen(config->entries[i]->title);
336 if (line_width < entry_len)
337 line_width = entry_len;
339 if (line_width > x_max)
342 /* menu entries title lines */
343 lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
344 for (i = 0; i < config->entry_count; i++)
345 lines[i] = PoolPrint(L" %-.*s ", line_width, config->entries[i]->title);
348 clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
349 for (i = 0; i < x_max; i++)
357 for (i = 0; i < config->entry_count; i++) {
358 if (i < idx_first || i > idx_last)
360 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, i - idx_first);
361 if (i == idx_highlight)
362 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
363 EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
365 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
366 EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
367 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
368 if (i == config->idx_default_efivar) {
369 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, i - idx_first);
370 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"*");
374 } else if (highlight) {
375 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, idx_highlight_prev - idx_first);
376 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
377 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
378 if (idx_highlight_prev == config->idx_default_efivar) {
379 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, idx_highlight_prev - idx_first);
380 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"*");
383 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, idx_highlight - idx_first);
384 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
385 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
386 if (idx_highlight == config->idx_default_efivar) {
387 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, idx_highlight - idx_first);
388 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"*");
393 if (timeout_remain > 0) {
395 status = PoolPrint(L"Boot in %d seconds.", (timeout_remain + 5) / 10);
398 /* print status at last line of screen */
400 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
401 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
402 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
403 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + StrLen(status));
406 err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
407 if (err == EFI_NOT_READY) {
410 if (timeout_remain == 0) {
414 if (timeout_remain > 0) {
415 uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
419 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
424 /* clear status after keystroke */
428 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
429 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
430 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
433 idx_highlight_prev = idx_highlight;
435 switch (key.ScanCode) {
437 if (idx_highlight > 0)
441 if (idx_highlight < config->entry_count-1)
445 if (idx_highlight > 0) {
451 if (idx_highlight < config->entry_count-1) {
453 idx_highlight = config->entry_count-1;
457 idx_highlight -= visible_max;
458 if (idx_highlight < 0)
462 idx_highlight += visible_max;
463 if (idx_highlight > config->entry_count-1)
464 idx_highlight = config->entry_count-1;
467 status = StrDuplicate(L"(d)efault, (+/-)timeout, (o)ptions, (i)nitrd, (v)ersion");
471 if (idx_highlight > idx_last) {
472 idx_last = idx_highlight;
473 idx_first = 1 + idx_highlight - visible_max;
476 if (idx_highlight < idx_first) {
477 idx_first = idx_highlight;
478 idx_last = idx_highlight + visible_max-1;
481 idx_last = idx_first + visible_max-1;
483 if (!refresh && idx_highlight != idx_highlight_prev)
486 switch (key.UnicodeChar) {
488 case CHAR_CARRIAGE_RETURN:
492 if (config->idx_default_efivar != idx_highlight) {
493 /* store the selected entry in a persistent EFI variable */
494 efivar_set(L"LoaderConfigDefault", config->entries[idx_highlight]->file, TRUE);
495 config->idx_default_efivar = idx_highlight;
496 status = StrDuplicate(L"Default boot entry permanently stored.");
498 /* clear the default entry EFI variable */
499 efivar_set(L"LoaderConfigDefault", NULL, TRUE);
500 config->idx_default_efivar = -1;
501 status = StrDuplicate(L"Default boot entry cleared.");
506 if (config->timeout_sec_efivar > 0) {
507 config->timeout_sec_efivar--;
508 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
509 if (config->timeout_sec_efivar > 0)
510 status = PoolPrint(L"Timeout of %d sec permanently stored.",
511 config->timeout_sec_efivar);
513 status = StrDuplicate(L"Menu permanently disabled. "
514 "Hold down key at bootup to show menu.");
515 } else if (config->timeout_sec_efivar <= 0){
516 config->timeout_sec_efivar = -1;
517 efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
518 status = PoolPrint(L"Timeout permanantly set to the configured value of %d sec.",
519 config->timeout_sec_config);
523 config->timeout_sec_efivar++;
524 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
525 if (config->timeout_sec_efivar)
526 status = PoolPrint(L"Timeout of %d sec permanently stored.",
527 config->timeout_sec_efivar);
529 status = StrDuplicate(L"Menu permanently disabled. "
530 "Hold down key at bootup to show menu.");
533 if (!config->entries[idx_highlight]->initrd)
535 if (config->no_initrd) {
536 config->no_initrd = FALSE;
537 status = StrDuplicate(L"Initrd enabled for this bootup.");
539 config->no_initrd = TRUE;
540 status = StrDuplicate(L"Initrd disabled for this bootup.");
544 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
545 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
546 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
547 if (edit_line(config->entries[idx_highlight]->options, &config->options_edit, x_max, y_max-1))
549 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
550 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
553 status = PoolPrint(L"gummiboot %d, EFI %d.%02d", VERSION,
554 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
559 for (i = 0; i < config->entry_count; i++)
563 *chosen_entry = config->entries[idx_highlight];
565 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
566 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
569 static VOID config_add_entry(Config *config, ConfigEntry *entry) {
570 if ((config->entry_count & 15) == 0) {
573 i = config->entry_count + 16;
574 if (config->entry_count == 0)
575 config->entries = AllocatePool(sizeof(VOID *) * i);
577 config->entries = ReallocatePool(config->entries,
578 sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
580 config->entries[config->entry_count++] = entry;
583 static BOOLEAN is_digit(CHAR16 c)
585 return (c >= '0') && (c <= '9');
588 static UINTN c_order(CHAR16 c)
594 else if ((c >= 'a') && (c <= 'z'))
600 static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
610 while ((*s2 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
613 order = c_order(*s1) - c_order(*s2);
626 while (is_digit(*s1) && is_digit(*s2)) {
642 return StrCmp(os1, os2);
645 CHAR16 *stra_to_str(CHAR8 *stra) {
651 str = AllocatePool((len + 1) * sizeof(CHAR16));
653 for (i = 0; i < len; i++)
660 CHAR16 *stra_to_path(CHAR8 *stra) {
667 str = AllocatePool((len + 2) * sizeof(CHAR16));
671 for (i = 0; i < len; i++) {
672 if (stra[i] == '/' || stra[i] == '\\') {
673 if (str[strlen-1] == '\\')
675 str[strlen++] = '\\';
678 str[strlen++] = stra[i];
685 static CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
693 static CHAR8 *line_get_key_value(CHAR8 *line, CHAR8 **key_ret, CHAR8 **value_ret) {
701 while (*next && !strchra((CHAR8 *)"\n\r", *next))
705 linelen = next - line;
711 while (*next && strchra((CHAR8 *)"\n\r", *next))
714 /* trailing whitespace */
715 while (linelen && strchra((CHAR8 *)" \t", line[linelen-1]))
717 line[linelen] = '\0';
719 /* leading whitespace */
720 while (strchra((CHAR8 *)" \t", *line))
729 /* split key/value */
731 while (*value && !strchra((CHAR8 *)" \t", *value))
737 while (*value && strchra((CHAR8 *)" \t", *value))
745 static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
750 while ((line = line_get_key_value(line, &key, &value))) {
751 if (strcmpa((CHAR8 *)"timeout", key) == 0) {
754 s = stra_to_str(value);
755 config->timeout_sec_config = Atoi(s);
756 config->timeout_sec = config->timeout_sec_config;
760 if (strcmpa((CHAR8 *)"default", key) == 0) {
761 config->entry_default_pattern = stra_to_str(value);
762 StrLwr(config->entry_default_pattern);
768 static VOID config_entry_add_from_file(Config *config, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
774 entry = AllocateZeroPool(sizeof(ConfigEntry));
777 while ((line = line_get_key_value(line, &key, &value))) {
778 if (strcmpa((CHAR8 *)"title", key) == 0) {
779 entry->title = stra_to_str(value);
783 if (strcmpa((CHAR8 *)"linux", key) == 0) {
784 entry->type = LOADER_LINUX;
785 entry->loader = stra_to_path(value);
789 if (strcmpa((CHAR8 *)"efi", key) == 0) {
790 entry->type = LOADER_EFI;
791 entry->loader = stra_to_path(value);
792 /* do not add an entry for ourselves */
793 if (StrCmp(entry->loader, loaded_image_path) == 0) {
794 entry->type = LOADER_UNDEFINED;
800 if (strcmpa((CHAR8 *)"initrd", key) == 0) {
801 entry->initrd = stra_to_path(value);
805 if (strcmpa((CHAR8 *)"options", key) == 0) {
806 entry->options = stra_to_str(value);
811 if (entry->type == LOADER_UNDEFINED) {
812 FreePool(entry->title);
813 FreePool(entry->loader);
814 FreePool(entry->initrd);
815 FreePool(entry->options);
820 entry->file = StrDuplicate(file);
821 len = StrLen(entry->file);
824 entry->file[len - 5] = '\0';
828 entry->title = StrDuplicate(entry->loader);
830 config_add_entry(config, entry);
833 static UINTN file_read(Config *config, EFI_FILE_HANDLE dir, const CHAR16 *name, CHAR8 **content) {
834 EFI_FILE_HANDLE handle;
841 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0);
845 info = LibFileInfo(handle);
846 buflen = info->FileSize+1;
847 buf = AllocatePool(buflen);
849 err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
850 if (EFI_ERROR(err) == EFI_SUCCESS) {
858 uefi_call_wrapper(handle->Close, 1, handle);
863 static VOID config_load(Config *config, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
864 EFI_FILE_HANDLE entries_dir;
871 len = file_read(config, root_dir, L"\\loader\\loader.conf", &content);
873 config_defaults_load_from_file(config, content);
875 err = efivar_get_int(L"LoaderConfigTimeout", &sec);
876 if (EFI_ERROR(err) == EFI_SUCCESS) {
877 config->timeout_sec_efivar = sec;
878 config->timeout_sec = sec;
880 config->timeout_sec_efivar = -1;
882 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0);
883 if (EFI_ERROR(err) == EFI_SUCCESS) {
891 bufsize = sizeof(buf);
892 err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
893 if (bufsize == 0 || EFI_ERROR(err))
896 f = (EFI_FILE_INFO *) buf;
897 if (f->FileName[0] == '.')
899 if (f->Attribute & EFI_FILE_DIRECTORY)
901 len = StrLen(f->FileName);
904 if (StriCmp(f->FileName + len - 5, L".conf") != 0)
907 len = file_read(config, entries_dir, f->FileName, &content);
909 config_entry_add_from_file(config, f->FileName, content, loaded_image_path);
911 uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
914 /* sort entries after version number */
915 for (i = 1; i < config->entry_count; i++) {
920 for (j = 0; j < config->entry_count - i; j++) {
923 if (str_verscmp(config->entries[j]->file, config->entries[j+1]->file) <= 0)
925 entry = config->entries[j];
926 config->entries[j] = config->entries[j+1];
927 config->entries[j+1] = entry;
935 static VOID config_default_entry_select(Config *config) {
940 * The EFI variable to specify a boot entry for the next, and only the
941 * next reboot. The variable is always cleared directly after it is read.
943 err = efivar_get(L"LoaderEntryOneShot", &var);
944 if (EFI_ERROR(err) == EFI_SUCCESS) {
945 BOOLEAN found = FALSE;
948 for (i = 0; i < config->entry_count; i++) {
949 if (!config->entries[i]->file)
951 if (StrCmp(config->entries[i]->file, var) == 0) {
952 config->idx_default = i;
957 efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
964 * The EFI variable to select the default boot entry overrides the
965 * configured pattern. The variable can be set and cleared by pressing
966 * the 'd' key in the loader selection menu, the entry is marked with
969 err = efivar_get(L"LoaderConfigDefault", &var);
970 if (EFI_ERROR(err) == EFI_SUCCESS) {
971 BOOLEAN found = FALSE;
974 for (i = 0; i < config->entry_count; i++) {
975 if (!config->entries[i]->file)
977 if (StrCmp(config->entries[i]->file, var) == 0) {
978 config->idx_default = i;
979 config->idx_default_efivar = i;
988 config->idx_default_efivar = -1;
991 * Match the pattern from the end of the list to the start, find last
992 * entry (largest number) matching the given pattern.
994 if (config->entry_default_pattern) {
997 for (i = config->entry_count-1; i >= 0; i--) {
998 if (!config->entries[i]->file)
1000 if (config->entries[i]->no_default)
1002 if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
1003 config->idx_default = i;
1009 /* select the last entry */
1010 if (config->entry_count)
1011 config->idx_default = config->entry_count-1;
1014 static VOID config_entry_add_loader(Config *config, EFI_FILE *root_dir, CHAR16 *file, CHAR16 *title, CHAR16 *loader) {
1015 EFI_FILE_HANDLE handle;
1019 /* check existence */
1020 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0);
1023 uefi_call_wrapper(handle->Close, 1, handle);
1025 entry = AllocateZeroPool(sizeof(ConfigEntry));
1026 entry->title = StrDuplicate(title);
1027 entry->loader = StrDuplicate(loader);
1029 entry->file = StrDuplicate(file);
1030 entry->no_default = TRUE;
1031 config_add_entry(config, entry);
1034 static EFI_STATUS image_start(EFI_HANDLE parent_image, EFI_LOADED_IMAGE *parent_loaded_image,
1035 const Config *config, const ConfigEntry *entry) {
1038 EFI_DEVICE_PATH *path;
1039 EFI_LOADED_IMAGE *loaded_image;
1042 path = FileDevicePath(parent_loaded_image->DeviceHandle, entry->loader);
1044 Print(L"Error getting device path.");
1045 uefi_call_wrapper(BS->Stall, 1, 1000 * 1000);
1046 return EFI_INVALID_PARAMETER;
1049 err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
1050 if (EFI_ERROR(err)) {
1051 Print(L"Error loading %s: %r", entry->loader, err);
1052 uefi_call_wrapper(BS->Stall, 1, 1000 * 1000);
1056 if (config->options_edit)
1057 options = config->options_edit;
1058 else if (entry->options)
1059 options = entry->options;
1062 if (options || (entry->initrd && !config->no_initrd)) {
1063 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, &loaded_image,
1064 parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
1065 if (EFI_ERROR(err)) {
1066 Print(L"Error getting LoadedImageProtocol handle: %r", err);
1067 uefi_call_wrapper(BS->Stall, 1, 1000 * 1000);
1070 if (entry->type == LOADER_LINUX && entry->initrd && !config->no_initrd) {
1072 loaded_image->LoadOptions = PoolPrint(L"initrd=%s %s", entry->initrd, options);
1074 loaded_image->LoadOptions = PoolPrint(L"initrd=%s", entry->initrd);
1076 loaded_image->LoadOptions = options;
1077 loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
1080 efivar_set_ticks(L"LoaderTicksStartImage", 0);
1081 err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
1083 uefi_call_wrapper(BS->UnloadImage, 1, image);
1089 EFI_STATUS EFIAPI efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
1090 EFI_LOADED_IMAGE *loaded_image;
1092 CHAR16 *loaded_image_path;
1096 BOOLEAN menu = FALSE;
1099 InitializeLib(image, sys_table);
1100 efivar_set_ticks(L"LoaderTicksInit", ticks);
1102 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, &loaded_image,
1103 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
1104 if (EFI_ERROR(err)) {
1105 Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
1106 uefi_call_wrapper(BS->Stall, 1, 1000 * 1000);
1110 root_dir = LibOpenRoot(loaded_image->DeviceHandle);
1112 Print(L"Unable to open root directory: %r ", err);
1113 uefi_call_wrapper(BS->Stall, 1, 1000 * 1000);
1114 return EFI_LOAD_ERROR;
1117 ZeroMem(&config, sizeof(Config));
1119 /* scan "\loader\entries\*.conf" files */
1120 loaded_image_path = DevicePathToStr(loaded_image->FilePath);
1121 config_load(&config, root_dir, loaded_image_path);
1122 FreePool(loaded_image_path);
1124 /* add fallback entry to the end of the list */
1125 config_entry_add_loader(&config, root_dir, L"fallback", L"EFI default loader", L"\\EFI\\BOOT\\BOOTX64.EFI");
1127 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
1128 config_default_entry_select(&config);
1130 /* show menu when key is pressed or timeout is set */
1131 if (config.timeout_sec == 0) {
1134 err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
1135 menu = err != EFI_NOT_READY;
1142 entry = config.entries[config.idx_default];
1144 efivar_set_ticks(L"LoaderTicksStartMenu", 0);
1145 menu_run(&config, &entry);
1148 /* export the selected boot entry to the system */
1149 err = efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
1150 if (EFI_ERROR(err)) {
1151 Print(L"Error storing LoaderEntrySelected variable: %r ", err);
1152 uefi_call_wrapper(BS->Stall, 1, 1000 * 1000);
1155 image_start(image, loaded_image, &config, entry);
1158 config.timeout_sec = 0;
1161 uefi_call_wrapper(root_dir->Close, 1, root_dir);
1162 uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);