chiark / gitweb /
8bdc7f8c63f35aaec9b239783de08cea76c11f04
[elogind.git] / src / boot / efi / boot.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /*
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 2.1 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * Copyright (C) 2012-2015 Kay Sievers <kay@vrfy.org>
15  * Copyright (C) 2012-2015 Harald Hoyer <harald@redhat.com>
16  */
17
18 #include <efi.h>
19 #include <efilib.h>
20
21 #include "util.h"
22 #include "console.h"
23 #include "graphics.h"
24 #include "pefile.h"
25 #include "linux.h"
26
27 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
28 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
29 #endif
30
31 /* magic string to find in the binary image */
32 static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " VERSION " ####";
33
34 static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
35
36 enum loader_type {
37         LOADER_UNDEFINED,
38         LOADER_EFI,
39         LOADER_LINUX
40 };
41
42 typedef struct {
43         CHAR16 *file;
44         CHAR16 *title_show;
45         CHAR16 *title;
46         CHAR16 *version;
47         CHAR16 *machine_id;
48         EFI_HANDLE *device;
49         enum loader_type type;
50         CHAR16 *loader;
51         CHAR16 *options;
52         CHAR16 *splash;
53         CHAR16 key;
54         EFI_STATUS (*call)(VOID);
55         BOOLEAN no_autoselect;
56         BOOLEAN non_unique;
57 } ConfigEntry;
58
59 typedef struct {
60         ConfigEntry **entries;
61         UINTN entry_count;
62         INTN idx_default;
63         INTN idx_default_efivar;
64         UINTN timeout_sec;
65         UINTN timeout_sec_config;
66         INTN timeout_sec_efivar;
67         CHAR16 *entry_default_pattern;
68         CHAR16 *entry_oneshot;
69         CHAR16 *options_edit;
70         CHAR16 *entries_auto;
71 } Config;
72
73 static VOID cursor_left(UINTN *cursor, UINTN *first)
74 {
75         if ((*cursor) > 0)
76                 (*cursor)--;
77         else if ((*first) > 0)
78                 (*first)--;
79 }
80
81 static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
82 {
83         if ((*cursor)+1 < x_max)
84                 (*cursor)++;
85         else if ((*first) + (*cursor) < len)
86                 (*first)++;
87 }
88
89 static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
90         CHAR16 *line;
91         UINTN size;
92         UINTN len;
93         UINTN first;
94         CHAR16 *print;
95         UINTN cursor;
96         UINTN clear;
97         BOOLEAN exit;
98         BOOLEAN enter;
99
100         if (!line_in)
101                 line_in = L"";
102         size = StrLen(line_in) + 1024;
103         line = AllocatePool(size * sizeof(CHAR16));
104         StrCpy(line, line_in);
105         len = StrLen(line);
106         print = AllocatePool((x_max+1) * sizeof(CHAR16));
107
108         uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
109
110         first = 0;
111         cursor = 0;
112         clear = 0;
113         enter = FALSE;
114         exit = FALSE;
115         while (!exit) {
116                 EFI_STATUS err;
117                 UINT64 key;
118                 UINTN i;
119
120                 i = len - first;
121                 if (i >= x_max-1)
122                         i = x_max-1;
123                 CopyMem(print, line + first, i * sizeof(CHAR16));
124                 while (clear > 0 && i < x_max-1) {
125                         clear--;
126                         print[i++] = ' ';
127                 }
128                 print[i] = '\0';
129
130                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
131                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
132                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
133
134                 err = console_key_read(&key, TRUE);
135                 if (EFI_ERROR(err))
136                         continue;
137
138                 switch (key) {
139                 case KEYPRESS(0, SCAN_ESC, 0):
140                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
141                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
142                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
143                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
144                         exit = TRUE;
145                         break;
146
147                 case KEYPRESS(0, SCAN_HOME, 0):
148                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
149                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
150                         /* beginning-of-line */
151                         cursor = 0;
152                         first = 0;
153                         continue;
154
155                 case KEYPRESS(0, SCAN_END, 0):
156                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
157                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
158                         /* end-of-line */
159                         cursor = len - first;
160                         if (cursor+1 >= x_max) {
161                                 cursor = x_max-1;
162                                 first = len - (x_max-1);
163                         }
164                         continue;
165
166                 case KEYPRESS(0, SCAN_DOWN, 0):
167                 case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
168                 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
169                         /* forward-word */
170                         while (line[first + cursor] && line[first + cursor] == ' ')
171                                 cursor_right(&cursor, &first, x_max, len);
172                         while (line[first + cursor] && line[first + cursor] != ' ')
173                                 cursor_right(&cursor, &first, x_max, len);
174                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
175                         continue;
176
177                 case KEYPRESS(0, SCAN_UP, 0):
178                 case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
179                 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
180                         /* backward-word */
181                         if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
182                                 cursor_left(&cursor, &first);
183                                 while ((first + cursor) > 0 && line[first + cursor] == ' ')
184                                         cursor_left(&cursor, &first);
185                         }
186                         while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
187                                 cursor_left(&cursor, &first);
188                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
189                         continue;
190
191                 case KEYPRESS(0, SCAN_RIGHT, 0):
192                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
193                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
194                         /* forward-char */
195                         if (first + cursor == len)
196                                 continue;
197                         cursor_right(&cursor, &first, x_max, len);
198                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
199                         continue;
200
201                 case KEYPRESS(0, SCAN_LEFT, 0):
202                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
203                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
204                         /* backward-char */
205                         cursor_left(&cursor, &first);
206                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
207                         continue;
208
209                 case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
210                         /* kill-word */
211                         clear = 0;
212                         for (i = first + cursor; i < len && line[i] == ' '; i++)
213                                 clear++;
214                         for (; i < len && line[i] != ' '; i++)
215                                 clear++;
216
217                         for (i = first + cursor; i + clear < len; i++)
218                                 line[i] = line[i + clear];
219                         len -= clear;
220                         line[len] = '\0';
221                         continue;
222
223                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'):
224                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')):
225                 case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE):
226                         /* backward-kill-word */
227                         clear = 0;
228                         if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
229                                 cursor_left(&cursor, &first);
230                                 clear++;
231                                 while ((first + cursor) > 0 && line[first + cursor] == ' ') {
232                                         cursor_left(&cursor, &first);
233                                         clear++;
234                                 }
235                         }
236                         while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
237                                 cursor_left(&cursor, &first);
238                                 clear++;
239                         }
240                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
241
242                         for (i = first + cursor; i + clear < len; i++)
243                                 line[i] = line[i + clear];
244                         len -= clear;
245                         line[len] = '\0';
246                         continue;
247
248                 case KEYPRESS(0, SCAN_DELETE, 0):
249                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
250                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
251                         if (len == 0)
252                                 continue;
253                         if (first + cursor == len)
254                                 continue;
255                         for (i = first + cursor; i < len; i++)
256                                 line[i] = line[i+1];
257                         clear = 1;
258                         len--;
259                         continue;
260
261                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
262                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
263                         /* kill-line */
264                         line[first + cursor] = '\0';
265                         clear = len - (first + cursor);
266                         len = first + cursor;
267                         continue;
268
269                 case KEYPRESS(0, 0, CHAR_LINEFEED):
270                 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
271                         if (StrCmp(line, line_in) != 0) {
272                                 *line_out = line;
273                                 line = NULL;
274                         }
275                         enter = TRUE;
276                         exit = TRUE;
277                         break;
278
279                 case KEYPRESS(0, 0, CHAR_BACKSPACE):
280                         if (len == 0)
281                                 continue;
282                         if (first == 0 && cursor == 0)
283                                 continue;
284                         for (i = first + cursor-1; i < len; i++)
285                                 line[i] = line[i+1];
286                         clear = 1;
287                         len--;
288                         if (cursor > 0)
289                                 cursor--;
290                         if (cursor > 0 || first == 0)
291                                 continue;
292                         /* show full line if it fits */
293                         if (len < x_max) {
294                                 cursor = first;
295                                 first = 0;
296                                 continue;
297                         }
298                         /* jump left to see what we delete */
299                         if (first > 10) {
300                                 first -= 10;
301                                 cursor = 10;
302                         } else {
303                                 cursor = first;
304                                 first = 0;
305                         }
306                         continue;
307
308                 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
309                 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
310                         if (len+1 == size)
311                                 continue;
312                         for (i = len; i > first + cursor; i--)
313                                 line[i] = line[i-1];
314                         line[first + cursor] = KEYCHAR(key);
315                         len++;
316                         line[len] = '\0';
317                         if (cursor+1 < x_max)
318                                 cursor++;
319                         else if (first + cursor < len)
320                                 first++;
321                         continue;
322                 }
323         }
324
325         uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
326         FreePool(print);
327         FreePool(line);
328         return enter;
329 }
330
331 static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
332         UINTN i;
333
334         if (key == 0)
335                 return -1;
336
337         /* select entry by number key */
338         if (key >= '1' && key <= '9') {
339                 i = key - '0';
340                 if (i > config->entry_count)
341                         i = config->entry_count;
342                 return i-1;
343         }
344
345         /* find matching key in config entries */
346         for (i = start; i < config->entry_count; i++)
347                 if (config->entries[i]->key == key)
348                         return i;
349
350         for (i = 0; i < start; i++)
351                 if (config->entries[i]->key == key)
352                         return i;
353
354         return -1;
355 }
356
357 static VOID print_status(Config *config, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
358         UINT64 key;
359         UINTN i;
360         CHAR16 *s;
361         CHAR8 *b;
362         UINTN x;
363         UINTN y;
364         UINTN size;
365         EFI_STATUS err;
366         UINTN color = 0;
367         const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel = NULL;
368
369         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
370         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
371
372         Print(L"systemd-boot version:   " VERSION "\n");
373         Print(L"architecture:           " EFI_MACHINE_TYPE_NAME "\n");
374         Print(L"loaded image:           %s\n", loaded_image_path);
375         Print(L"UEFI specification:     %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
376         Print(L"firmware vendor:        %s\n", ST->FirmwareVendor);
377         Print(L"firmware version:       %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
378
379         if (uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x, &y) == EFI_SUCCESS)
380                 Print(L"console size:           %d x %d\n", x, y);
381
382         if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
383                 Print(L"SecureBoot:             %s\n", *b > 0 ? L"enabled" : L"disabled");
384                 FreePool(b);
385         }
386
387         if (efivar_get_raw(&global_guid, L"SetupMode", &b, &size) == EFI_SUCCESS) {
388                 Print(L"SetupMode:              %s\n", *b > 0 ? L"setup" : L"user");
389                 FreePool(b);
390         }
391
392         if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
393                 Print(L"OsIndicationsSupported: %d\n", (UINT64)*b);
394                 FreePool(b);
395         }
396         Print(L"\n");
397
398         Print(L"timeout:                %d\n", config->timeout_sec);
399         if (config->timeout_sec_efivar >= 0)
400                 Print(L"timeout (EFI var):      %d\n", config->timeout_sec_efivar);
401         Print(L"timeout (config):       %d\n", config->timeout_sec_config);
402         if (config->entry_default_pattern)
403                 Print(L"default pattern:        '%s'\n", config->entry_default_pattern);
404         Print(L"\n");
405
406         Print(L"config entry count:     %d\n", config->entry_count);
407         Print(L"entry selected idx:     %d\n", config->idx_default);
408         if (config->idx_default_efivar >= 0)
409                 Print(L"entry EFI var idx:      %d\n", config->idx_default_efivar);
410         Print(L"\n");
411
412         if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS)
413                 Print(L"LoaderConfigTimeout:    %d\n", i);
414         if (config->entry_oneshot)
415                 Print(L"LoaderEntryOneShot:     %s\n", config->entry_oneshot);
416         if (efivar_get(L"LoaderDeviceIdentifier", &s) == EFI_SUCCESS) {
417                 Print(L"LoaderDeviceIdentifier: %s\n", s);
418                 FreePool(s);
419         }
420         if (efivar_get(L"LoaderDevicePartUUID", &s) == EFI_SUCCESS) {
421                 Print(L"LoaderDevicePartUUID:   %s\n", s);
422                 FreePool(s);
423         }
424         if (efivar_get(L"LoaderEntryDefault", &s) == EFI_SUCCESS) {
425                 Print(L"LoaderEntryDefault:     %s\n", s);
426                 FreePool(s);
427         }
428
429         Print(L"\n--- press key ---\n\n");
430         console_key_read(&key, TRUE);
431
432         for (i = 0; i < config->entry_count; i++) {
433                 ConfigEntry *entry;
434
435                 if (key == KEYPRESS(0, SCAN_ESC, 0) || key == KEYPRESS(0, 0, 'q'))
436                         break;
437
438                 entry = config->entries[i];
439
440                 if (entry->splash) {
441                         UINT8 *content = NULL;
442                         INTN len;
443
444                         len = file_read(root_dir, entry->splash, 0, 0, &content);
445                         if (len > 0) {
446                                 for (;;) {
447                                         static const EFI_GRAPHICS_OUTPUT_BLT_PIXEL colors[] = {
448                                                 { .Red = 0xff, .Green = 0xff, .Blue = 0xff },
449                                                 { .Red = 0xc0, .Green = 0xc0, .Blue = 0xc0 },
450                                                 { .Red = 0xff, .Green =    0, .Blue =    0 },
451                                                 { .Red =    0, .Green = 0xff, .Blue =    0 },
452                                                 { .Red =    0, .Green =    0, .Blue = 0xff },
453                                                 { .Red =    0, .Green =    0, .Blue =    0 },
454                                         };
455
456                                         err = graphics_splash(content, len, pixel);
457                                         if (EFI_ERROR(err))
458                                                 break;
459
460                                         /* 'b' rotates through background colors */
461                                         console_key_read(&key, TRUE);
462                                         if (key != KEYPRESS(0, 0, 'b'))
463                                                 break;
464                                         pixel = &colors[color++];
465                                         if (color == ELEMENTSOF(colors))
466                                                 color = 0;
467                                 }
468                         }
469
470                         FreePool(content);
471                         graphics_mode(FALSE);
472                         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
473                 }
474
475                 Print(L"config entry:           %d/%d\n", i+1, config->entry_count);
476                 if (entry->file)
477                         Print(L"file                    '%s'\n", entry->file);
478                 Print(L"title show              '%s'\n", entry->title_show);
479                 if (entry->title)
480                         Print(L"title                   '%s'\n", entry->title);
481                 if (entry->version)
482                         Print(L"version                 '%s'\n", entry->version);
483                 if (entry->machine_id)
484                         Print(L"machine-id              '%s'\n", entry->machine_id);
485                 if (entry->device) {
486                         EFI_DEVICE_PATH *device_path;
487                         CHAR16 *str;
488
489                         device_path = DevicePathFromHandle(entry->device);
490                         if (device_path) {
491                                 str = DevicePathToStr(device_path);
492                                 Print(L"device handle           '%s'\n", str);
493                                 FreePool(str);
494                         }
495                 }
496                 if (entry->loader)
497                         Print(L"loader                  '%s'\n", entry->loader);
498                 if (entry->options)
499                         Print(L"options                 '%s'\n", entry->options);
500                 if (entry->splash)
501                         Print(L"splash                  '%s'\n", entry->splash);
502                 Print(L"auto-select             %s\n", entry->no_autoselect ? L"no" : L"yes");
503                 if (entry->call)
504                         Print(L"internal call           yes\n");
505
506                 Print(L"\n--- press key ---\n\n");
507                 console_key_read(&key, TRUE);
508         }
509
510         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
511 }
512
513 static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
514         EFI_STATUS err;
515         UINTN visible_max;
516         UINTN idx_highlight;
517         UINTN idx_highlight_prev;
518         UINTN idx_first;
519         UINTN idx_last;
520         BOOLEAN refresh;
521         BOOLEAN highlight;
522         UINTN i;
523         UINTN line_width;
524         CHAR16 **lines;
525         UINTN x_start;
526         UINTN y_start;
527         UINTN x_max;
528         UINTN y_max;
529         CHAR16 *status;
530         CHAR16 *clearline;
531         INTN timeout_remain;
532         INT16 idx;
533         BOOLEAN exit = FALSE;
534         BOOLEAN run = TRUE;
535         BOOLEAN wait = FALSE;
536
537         graphics_mode(FALSE);
538         uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
539         uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
540         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
541
542         /* draw a single character to make ClearScreen work on some firmware */
543         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L" ");
544         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
545
546         err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
547         if (EFI_ERROR(err)) {
548                 x_max = 80;
549                 y_max = 25;
550         }
551
552         /* we check 10 times per second for a keystroke */
553         if (config->timeout_sec > 0)
554                 timeout_remain = config->timeout_sec * 10;
555         else
556                 timeout_remain = -1;
557
558         idx_highlight = config->idx_default;
559         idx_highlight_prev = 0;
560
561         visible_max = y_max - 2;
562
563         if ((UINTN)config->idx_default >= visible_max)
564                 idx_first = config->idx_default-1;
565         else
566                 idx_first = 0;
567
568         idx_last = idx_first + visible_max-1;
569
570         refresh = TRUE;
571         highlight = FALSE;
572
573         /* length of the longest entry */
574         line_width = 5;
575         for (i = 0; i < config->entry_count; i++) {
576                 UINTN entry_len;
577
578                 entry_len = StrLen(config->entries[i]->title_show);
579                 if (line_width < entry_len)
580                         line_width = entry_len;
581         }
582         if (line_width > x_max-6)
583                 line_width = x_max-6;
584
585         /* offsets to center the entries on the screen */
586         x_start = (x_max - (line_width)) / 2;
587         if (config->entry_count < visible_max)
588                 y_start = ((visible_max - config->entry_count) / 2) + 1;
589         else
590                 y_start = 0;
591
592         /* menu entries title lines */
593         lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
594         for (i = 0; i < config->entry_count; i++) {
595                 UINTN j, k;
596
597                 lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
598                 for (j = 0; j < x_start; j++)
599                         lines[i][j] = ' ';
600
601                 for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
602                         lines[i][j] = config->entries[i]->title_show[k];
603
604                 for (; j < x_max; j++)
605                         lines[i][j] = ' ';
606                 lines[i][x_max] = '\0';
607         }
608
609         status = NULL;
610         clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
611         for (i = 0; i < x_max; i++)
612                 clearline[i] = ' ';
613         clearline[i] = 0;
614
615         while (!exit) {
616                 UINT64 key;
617
618                 if (refresh) {
619                         for (i = 0; i < config->entry_count; i++) {
620                                 if (i < idx_first || i > idx_last)
621                                         continue;
622                                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
623                                 if (i == idx_highlight)
624                                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
625                                                           EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
626                                 else
627                                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
628                                                           EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
629                                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
630                                 if ((INTN)i == config->idx_default_efivar) {
631                                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
632                                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
633                                 }
634                         }
635                         refresh = FALSE;
636                 } else if (highlight) {
637                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
638                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
639                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
640                         if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
641                                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
642                                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
643                         }
644
645                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
646                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
647                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
648                         if ((INTN)idx_highlight == config->idx_default_efivar) {
649                                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
650                                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
651                         }
652                         highlight = FALSE;
653                 }
654
655                 if (timeout_remain > 0) {
656                         FreePool(status);
657                         status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
658                 }
659
660                 /* print status at last line of screen */
661                 if (status) {
662                         UINTN len;
663                         UINTN x;
664
665                         /* center line */
666                         len = StrLen(status);
667                         if (len < x_max)
668                                 x = (x_max - len) / 2;
669                         else
670                                 x = 0;
671                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
672                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
673                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
674                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
675                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
676                 }
677
678                 err = console_key_read(&key, wait);
679                 if (EFI_ERROR(err)) {
680                         /* timeout reached */
681                         if (timeout_remain == 0) {
682                                 exit = TRUE;
683                                 break;
684                         }
685
686                         /* sleep and update status */
687                         if (timeout_remain > 0) {
688                                 uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
689                                 timeout_remain--;
690                                 continue;
691                         }
692
693                         /* timeout disabled, wait for next key */
694                         wait = TRUE;
695                         continue;
696                 }
697
698                 timeout_remain = -1;
699
700                 /* clear status after keystroke */
701                 if (status) {
702                         FreePool(status);
703                         status = NULL;
704                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
705                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
706                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
707                 }
708
709                 idx_highlight_prev = idx_highlight;
710
711                 switch (key) {
712                 case KEYPRESS(0, SCAN_UP, 0):
713                 case KEYPRESS(0, 0, 'k'):
714                         if (idx_highlight > 0)
715                                 idx_highlight--;
716                         break;
717
718                 case KEYPRESS(0, SCAN_DOWN, 0):
719                 case KEYPRESS(0, 0, 'j'):
720                         if (idx_highlight < config->entry_count-1)
721                                 idx_highlight++;
722                         break;
723
724                 case KEYPRESS(0, SCAN_HOME, 0):
725                 case KEYPRESS(EFI_ALT_PRESSED, 0, '<'):
726                         if (idx_highlight > 0) {
727                                 refresh = TRUE;
728                                 idx_highlight = 0;
729                         }
730                         break;
731
732                 case KEYPRESS(0, SCAN_END, 0):
733                 case KEYPRESS(EFI_ALT_PRESSED, 0, '>'):
734                         if (idx_highlight < config->entry_count-1) {
735                                 refresh = TRUE;
736                                 idx_highlight = config->entry_count-1;
737                         }
738                         break;
739
740                 case KEYPRESS(0, SCAN_PAGE_UP, 0):
741                         if (idx_highlight > visible_max)
742                                 idx_highlight -= visible_max;
743                         else
744                                 idx_highlight = 0;
745                         break;
746
747                 case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
748                         idx_highlight += visible_max;
749                         if (idx_highlight > config->entry_count-1)
750                                 idx_highlight = config->entry_count-1;
751                         break;
752
753                 case KEYPRESS(0, 0, CHAR_LINEFEED):
754                 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
755                         exit = TRUE;
756                         break;
757
758                 case KEYPRESS(0, SCAN_F1, 0):
759                 case KEYPRESS(0, 0, 'h'):
760                 case KEYPRESS(0, 0, '?'):
761                         status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
762                         break;
763
764                 case KEYPRESS(0, 0, 'Q'):
765                         exit = TRUE;
766                         run = FALSE;
767                         break;
768
769                 case KEYPRESS(0, 0, 'd'):
770                         if (config->idx_default_efivar != (INTN)idx_highlight) {
771                                 /* store the selected entry in a persistent EFI variable */
772                                 efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
773                                 config->idx_default_efivar = idx_highlight;
774                                 status = StrDuplicate(L"Default boot entry selected.");
775                         } else {
776                                 /* clear the default entry EFI variable */
777                                 efivar_set(L"LoaderEntryDefault", NULL, TRUE);
778                                 config->idx_default_efivar = -1;
779                                 status = StrDuplicate(L"Default boot entry cleared.");
780                         }
781                         refresh = TRUE;
782                         break;
783
784                 case KEYPRESS(0, 0, '-'):
785                 case KEYPRESS(0, 0, 'T'):
786                         if (config->timeout_sec_efivar > 0) {
787                                 config->timeout_sec_efivar--;
788                                 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
789                                 if (config->timeout_sec_efivar > 0)
790                                         status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
791                                 else
792                                         status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
793                         } else if (config->timeout_sec_efivar <= 0){
794                                 config->timeout_sec_efivar = -1;
795                                 efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
796                                 if (config->timeout_sec_config > 0)
797                                         status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
798                                                            config->timeout_sec_config);
799                                 else
800                                         status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
801                         }
802                         break;
803
804                 case KEYPRESS(0, 0, '+'):
805                 case KEYPRESS(0, 0, 't'):
806                         if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
807                                 config->timeout_sec_efivar++;
808                         config->timeout_sec_efivar++;
809                         efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
810                         if (config->timeout_sec_efivar > 0)
811                                 status = PoolPrint(L"Menu timeout set to %d sec.",
812                                                    config->timeout_sec_efivar);
813                         else
814                                 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
815                         break;
816
817                 case KEYPRESS(0, 0, 'e'):
818                         /* only the options of configured entries can be edited */
819                         if (config->entries[idx_highlight]->type == LOADER_UNDEFINED)
820                                 break;
821                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
822                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
823                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
824                         if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
825                                 exit = TRUE;
826                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
827                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
828                         break;
829
830                 case KEYPRESS(0, 0, 'v'):
831                         status = PoolPrint(L"systemd-boot " VERSION " (" EFI_MACHINE_TYPE_NAME "), UEFI Specification %d.%02d, Vendor %s %d.%02d",
832                                            ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff,
833                                            ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
834                         break;
835
836                 case KEYPRESS(0, 0, 'P'):
837                         print_status(config, root_dir, loaded_image_path);
838                         refresh = TRUE;
839                         break;
840
841                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
842                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
843                         refresh = TRUE;
844                         break;
845
846                 default:
847                         /* jump with a hotkey directly to a matching entry */
848                         idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
849                         if (idx < 0)
850                                 break;
851                         idx_highlight = idx;
852                         refresh = TRUE;
853                 }
854
855                 if (idx_highlight > idx_last) {
856                         idx_last = idx_highlight;
857                         idx_first = 1 + idx_highlight - visible_max;
858                         refresh = TRUE;
859                 }
860                 if (idx_highlight < idx_first) {
861                         idx_first = idx_highlight;
862                         idx_last = idx_highlight + visible_max-1;
863                         refresh = TRUE;
864                 }
865
866                 idx_last = idx_first + visible_max-1;
867
868                 if (!refresh && idx_highlight != idx_highlight_prev)
869                         highlight = TRUE;
870         }
871
872         *chosen_entry = config->entries[idx_highlight];
873
874         for (i = 0; i < config->entry_count; i++)
875                 FreePool(lines[i]);
876         FreePool(lines);
877         FreePool(clearline);
878
879         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
880         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
881         return run;
882 }
883
884 static VOID config_add_entry(Config *config, ConfigEntry *entry) {
885         if ((config->entry_count & 15) == 0) {
886                 UINTN i;
887
888                 i = config->entry_count + 16;
889                 if (config->entry_count == 0)
890                         config->entries = AllocatePool(sizeof(VOID *) * i);
891                 else
892                         config->entries = ReallocatePool(config->entries,
893                                                          sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
894         }
895         config->entries[config->entry_count++] = entry;
896 }
897
898 static VOID config_entry_free(ConfigEntry *entry) {
899         FreePool(entry->title_show);
900         FreePool(entry->title);
901         FreePool(entry->machine_id);
902         FreePool(entry->loader);
903         FreePool(entry->options);
904 }
905
906 static BOOLEAN is_digit(CHAR16 c)
907 {
908         return (c >= '0') && (c <= '9');
909 }
910
911 static UINTN c_order(CHAR16 c)
912 {
913         if (c == '\0')
914                 return 0;
915         if (is_digit(c))
916                 return 0;
917         else if ((c >= 'a') && (c <= 'z'))
918                 return c;
919         else
920                 return c + 0x10000;
921 }
922
923 static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
924 {
925         CHAR16 *os1 = s1;
926         CHAR16 *os2 = s2;
927
928         while (*s1 || *s2) {
929                 INTN first;
930
931                 while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
932                         INTN order;
933
934                         order = c_order(*s1) - c_order(*s2);
935                         if (order)
936                                 return order;
937                         s1++;
938                         s2++;
939                 }
940
941                 while (*s1 == '0')
942                         s1++;
943                 while (*s2 == '0')
944                         s2++;
945
946                 first = 0;
947                 while (is_digit(*s1) && is_digit(*s2)) {
948                         if (first == 0)
949                                 first = *s1 - *s2;
950                         s1++;
951                         s2++;
952                 }
953
954                 if (is_digit(*s1))
955                         return 1;
956                 if (is_digit(*s2))
957                         return -1;
958
959                 if (first)
960                         return first;
961         }
962
963         return StrCmp(os1, os2);
964 }
965
966 static CHAR8 *line_get_key_value(CHAR8 *content, CHAR8 *sep, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
967         CHAR8 *line;
968         UINTN linelen;
969         CHAR8 *value;
970
971 skip:
972         line = content + *pos;
973         if (*line == '\0')
974                 return NULL;
975
976         linelen = 0;
977         while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
978                linelen++;
979
980         /* move pos to next line */
981         *pos += linelen;
982         if (content[*pos])
983                 (*pos)++;
984
985         /* empty line */
986         if (linelen == 0)
987                 goto skip;
988
989         /* terminate line */
990         line[linelen] = '\0';
991
992         /* remove leading whitespace */
993         while (strchra((CHAR8 *)" \t", *line)) {
994                 line++;
995                 linelen--;
996         }
997
998         /* remove trailing whitespace */
999         while (linelen > 0 && strchra(sep, line[linelen-1]))
1000                 linelen--;
1001         line[linelen] = '\0';
1002
1003         if (*line == '#')
1004                 goto skip;
1005
1006         /* split key/value */
1007         value = line;
1008         while (*value && !strchra(sep, *value))
1009                 value++;
1010         if (*value == '\0')
1011                 goto skip;
1012         *value = '\0';
1013         value++;
1014         while (*value && strchra(sep, *value))
1015                 value++;
1016
1017         /* unquote */
1018         if (value[0] == '\"' && line[linelen-1] == '\"') {
1019                 value++;
1020                 line[linelen-1] = '\0';
1021         }
1022
1023         *key_ret = line;
1024         *value_ret = value;
1025         return line;
1026 }
1027
1028 static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
1029         CHAR8 *line;
1030         UINTN pos = 0;
1031         CHAR8 *key, *value;
1032
1033         line = content;
1034         while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
1035                 if (strcmpa((CHAR8 *)"timeout", key) == 0) {
1036                         CHAR16 *s;
1037
1038                         s = stra_to_str(value);
1039                         config->timeout_sec_config = Atoi(s);
1040                         config->timeout_sec = config->timeout_sec_config;
1041                         FreePool(s);
1042                         continue;
1043                 }
1044
1045                 if (strcmpa((CHAR8 *)"default", key) == 0) {
1046                         FreePool(config->entry_default_pattern);
1047                         config->entry_default_pattern = stra_to_str(value);
1048                         StrLwr(config->entry_default_pattern);
1049                         continue;
1050                 }
1051         }
1052 }
1053
1054 static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
1055         ConfigEntry *entry;
1056         CHAR8 *line;
1057         UINTN pos = 0;
1058         CHAR8 *key, *value;
1059         UINTN len;
1060         CHAR16 *initrd = NULL;
1061
1062         entry = AllocateZeroPool(sizeof(ConfigEntry));
1063
1064         line = content;
1065         while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
1066                 if (strcmpa((CHAR8 *)"title", key) == 0) {
1067                         FreePool(entry->title);
1068                         entry->title = stra_to_str(value);
1069                         continue;
1070                 }
1071
1072                 if (strcmpa((CHAR8 *)"version", key) == 0) {
1073                         FreePool(entry->version);
1074                         entry->version = stra_to_str(value);
1075                         continue;
1076                 }
1077
1078                 if (strcmpa((CHAR8 *)"machine-id", key) == 0) {
1079                         FreePool(entry->machine_id);
1080                         entry->machine_id = stra_to_str(value);
1081                         continue;
1082                 }
1083
1084                 if (strcmpa((CHAR8 *)"linux", key) == 0) {
1085                         FreePool(entry->loader);
1086                         entry->type = LOADER_LINUX;
1087                         entry->loader = stra_to_path(value);
1088                         entry->key = 'l';
1089                         continue;
1090                 }
1091
1092                 if (strcmpa((CHAR8 *)"efi", key) == 0) {
1093                         entry->type = LOADER_EFI;
1094                         FreePool(entry->loader);
1095                         entry->loader = stra_to_path(value);
1096
1097                         /* do not add an entry for ourselves */
1098                         if (StriCmp(entry->loader, loaded_image_path) == 0) {
1099                                 entry->type = LOADER_UNDEFINED;
1100                                 break;
1101                         }
1102                         continue;
1103                 }
1104
1105                 if (strcmpa((CHAR8 *)"architecture", key) == 0) {
1106                         /* do not add an entry for an EFI image of architecture not matching with that of the image */
1107                         if (strcmpa((CHAR8 *)EFI_MACHINE_TYPE_NAME, value) != 0) {
1108                                 entry->type = LOADER_UNDEFINED;
1109                                 break;
1110                         }
1111                         continue;
1112                 }
1113
1114                 if (strcmpa((CHAR8 *)"initrd", key) == 0) {
1115                         CHAR16 *new;
1116
1117                         new = stra_to_path(value);
1118                         if (initrd) {
1119                                 CHAR16 *s;
1120
1121                                 s = PoolPrint(L"%s initrd=%s", initrd, new);
1122                                 FreePool(initrd);
1123                                 initrd = s;
1124                         } else
1125                                 initrd = PoolPrint(L"initrd=%s", new);
1126                         FreePool(new);
1127                         continue;
1128                 }
1129
1130                 if (strcmpa((CHAR8 *)"options", key) == 0) {
1131                         CHAR16 *new;
1132
1133                         new = stra_to_str(value);
1134                         if (entry->options) {
1135                                 CHAR16 *s;
1136
1137                                 s = PoolPrint(L"%s %s", entry->options, new);
1138                                 FreePool(entry->options);
1139                                 entry->options = s;
1140                         } else {
1141                                 entry->options = new;
1142                                 new = NULL;
1143                         }
1144                         FreePool(new);
1145                         continue;
1146                 }
1147
1148                 if (strcmpa((CHAR8 *)"splash", key) == 0) {
1149                         FreePool(entry->splash);
1150                         entry->splash = stra_to_path(value);
1151                         continue;
1152                 }
1153         }
1154
1155         if (entry->type == LOADER_UNDEFINED) {
1156                 config_entry_free(entry);
1157                 FreePool(initrd);
1158                 FreePool(entry);
1159                 return;
1160         }
1161
1162         /* add initrd= to options */
1163         if (entry->type == LOADER_LINUX && initrd) {
1164                 if (entry->options) {
1165                         CHAR16 *s;
1166
1167                         s = PoolPrint(L"%s %s", initrd, entry->options);
1168                         FreePool(entry->options);
1169                         entry->options = s;
1170                 } else {
1171                         entry->options = initrd;
1172                         initrd = NULL;
1173                 }
1174         }
1175         FreePool(initrd);
1176
1177         if (entry->machine_id) {
1178                 CHAR16 *var;
1179
1180                 /* append additional options from EFI variables for this machine-id */
1181                 var = PoolPrint(L"LoaderEntryOptions-%s", entry->machine_id);
1182                 if (var) {
1183                         CHAR16 *s;
1184
1185                         if (efivar_get(var, &s) == EFI_SUCCESS) {
1186                                 if (entry->options) {
1187                                         CHAR16 *s2;
1188
1189                                         s2 = PoolPrint(L"%s %s", entry->options, s);
1190                                         FreePool(entry->options);
1191                                         entry->options = s2;
1192                                 } else
1193                                         entry->options = s;
1194                         }
1195                         FreePool(var);
1196                 }
1197
1198                 var = PoolPrint(L"LoaderEntryOptionsOneShot-%s", entry->machine_id);
1199                 if (var) {
1200                         CHAR16 *s;
1201
1202                         if (efivar_get(var, &s) == EFI_SUCCESS) {
1203                                 if (entry->options) {
1204                                         CHAR16 *s2;
1205
1206                                         s2 = PoolPrint(L"%s %s", entry->options, s);
1207                                         FreePool(entry->options);
1208                                         entry->options = s2;
1209                                 } else
1210                                         entry->options = s;
1211                                 efivar_set(var, NULL, TRUE);
1212                         }
1213                         FreePool(var);
1214                 }
1215         }
1216
1217         entry->device = device;
1218         entry->file = StrDuplicate(file);
1219         len = StrLen(entry->file);
1220         /* remove ".conf" */
1221         if (len > 5)
1222                 entry->file[len - 5] = '\0';
1223         StrLwr(entry->file);
1224
1225         config_add_entry(config, entry);
1226 }
1227
1228 static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
1229         EFI_FILE_HANDLE entries_dir;
1230         EFI_STATUS err;
1231         CHAR8 *content = NULL;
1232         UINTN sec;
1233         UINTN len;
1234         UINTN i;
1235
1236         len = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content);
1237         if (len > 0)
1238                 config_defaults_load_from_file(config, content);
1239         FreePool(content);
1240
1241         err = efivar_get_int(L"LoaderConfigTimeout", &sec);
1242         if (!EFI_ERROR(err)) {
1243                 config->timeout_sec_efivar = sec;
1244                 config->timeout_sec = sec;
1245         } else
1246                 config->timeout_sec_efivar = -1;
1247
1248         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
1249         if (!EFI_ERROR(err)) {
1250                 for (;;) {
1251                         CHAR16 buf[256];
1252                         UINTN bufsize;
1253                         EFI_FILE_INFO *f;
1254                         CHAR8 *content = NULL;
1255                         UINTN len;
1256
1257                         bufsize = sizeof(buf);
1258                         err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
1259                         if (bufsize == 0 || EFI_ERROR(err))
1260                                 break;
1261
1262                         f = (EFI_FILE_INFO *) buf;
1263                         if (f->FileName[0] == '.')
1264                                 continue;
1265                         if (f->Attribute & EFI_FILE_DIRECTORY)
1266                                 continue;
1267                         len = StrLen(f->FileName);
1268                         if (len < 6)
1269                                 continue;
1270                         if (StriCmp(f->FileName + len - 5, L".conf") != 0)
1271                                 continue;
1272
1273                         len = file_read(entries_dir, f->FileName, 0, 0, &content);
1274                         if (len > 0)
1275                                 config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path);
1276                         FreePool(content);
1277                 }
1278                 uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
1279         }
1280
1281         /* sort entries after version number */
1282         for (i = 1; i < config->entry_count; i++) {
1283                 BOOLEAN more;
1284                 UINTN k;
1285
1286                 more = FALSE;
1287                 for (k = 0; k < config->entry_count - i; k++) {
1288                         ConfigEntry *entry;
1289
1290                         if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0)
1291                                 continue;
1292                         entry = config->entries[k];
1293                         config->entries[k] = config->entries[k+1];
1294                         config->entries[k+1] = entry;
1295                         more = TRUE;
1296                 }
1297                 if (!more)
1298                         break;
1299         }
1300 }
1301
1302 static VOID config_default_entry_select(Config *config) {
1303         CHAR16 *var;
1304         EFI_STATUS err;
1305         UINTN i;
1306
1307         /*
1308          * The EFI variable to specify a boot entry for the next, and only the
1309          * next reboot. The variable is always cleared directly after it is read.
1310          */
1311         err = efivar_get(L"LoaderEntryOneShot", &var);
1312         if (!EFI_ERROR(err)) {
1313                 BOOLEAN found = FALSE;
1314
1315                 for (i = 0; i < config->entry_count; i++) {
1316                         if (StrCmp(config->entries[i]->file, var) == 0) {
1317                                 config->idx_default = i;
1318                                 found = TRUE;
1319                                 break;
1320                         }
1321                 }
1322
1323                 config->entry_oneshot = StrDuplicate(var);
1324                 efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
1325                 FreePool(var);
1326                 if (found)
1327                         return;
1328         }
1329
1330         /*
1331          * The EFI variable to select the default boot entry overrides the
1332          * configured pattern. The variable can be set and cleared by pressing
1333          * the 'd' key in the loader selection menu, the entry is marked with
1334          * an '*'.
1335          */
1336         err = efivar_get(L"LoaderEntryDefault", &var);
1337         if (!EFI_ERROR(err)) {
1338                 BOOLEAN found = FALSE;
1339
1340                 for (i = 0; i < config->entry_count; i++) {
1341                         if (StrCmp(config->entries[i]->file, var) == 0) {
1342                                 config->idx_default = i;
1343                                 config->idx_default_efivar = i;
1344                                 found = TRUE;
1345                                 break;
1346                         }
1347                 }
1348                 FreePool(var);
1349                 if (found)
1350                         return;
1351         }
1352         config->idx_default_efivar = -1;
1353
1354         if (config->entry_count == 0)
1355                 return;
1356
1357         /*
1358          * Match the pattern from the end of the list to the start, find last
1359          * entry (largest number) matching the given pattern.
1360          */
1361         if (config->entry_default_pattern) {
1362                 i = config->entry_count;
1363                 while (i--) {
1364                         if (config->entries[i]->no_autoselect)
1365                                 continue;
1366                         if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
1367                                 config->idx_default = i;
1368                                 return;
1369                         }
1370                 }
1371         }
1372
1373         /* select the last suitable entry */
1374         i = config->entry_count;
1375         while (i--) {
1376                 if (config->entries[i]->no_autoselect)
1377                         continue;
1378                 config->idx_default = i;
1379                 return;
1380         }
1381
1382         /* no entry found */
1383         config->idx_default = -1;
1384 }
1385
1386 /* generate a unique title, avoiding non-distinguishable menu entries */
1387 static VOID config_title_generate(Config *config) {
1388         UINTN i, k;
1389         BOOLEAN unique;
1390
1391         /* set title */
1392         for (i = 0; i < config->entry_count; i++) {
1393                 CHAR16 *title;
1394
1395                 FreePool(config->entries[i]->title_show);
1396                 title = config->entries[i]->title;
1397                 if (!title)
1398                         title = config->entries[i]->file;
1399                 config->entries[i]->title_show = StrDuplicate(title);
1400         }
1401
1402         unique = TRUE;
1403         for (i = 0; i < config->entry_count; i++) {
1404                 for (k = 0; k < config->entry_count; k++) {
1405                         if (i == k)
1406                                 continue;
1407                         if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1408                                 continue;
1409
1410                         unique = FALSE;
1411                         config->entries[i]->non_unique = TRUE;
1412                         config->entries[k]->non_unique = TRUE;
1413                 }
1414         }
1415         if (unique)
1416                 return;
1417
1418         /* add version to non-unique titles */
1419         for (i = 0; i < config->entry_count; i++) {
1420                 CHAR16 *s;
1421
1422                 if (!config->entries[i]->non_unique)
1423                         continue;
1424                 if (!config->entries[i]->version)
1425                         continue;
1426
1427                 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
1428                 FreePool(config->entries[i]->title_show);
1429                 config->entries[i]->title_show = s;
1430                 config->entries[i]->non_unique = FALSE;
1431         }
1432
1433         unique = TRUE;
1434         for (i = 0; i < config->entry_count; i++) {
1435                 for (k = 0; k < config->entry_count; k++) {
1436                         if (i == k)
1437                                 continue;
1438                         if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1439                                 continue;
1440
1441                         unique = FALSE;
1442                         config->entries[i]->non_unique = TRUE;
1443                         config->entries[k]->non_unique = TRUE;
1444                 }
1445         }
1446         if (unique)
1447                 return;
1448
1449         /* add machine-id to non-unique titles */
1450         for (i = 0; i < config->entry_count; i++) {
1451                 CHAR16 *s;
1452                 CHAR16 *m;
1453
1454                 if (!config->entries[i]->non_unique)
1455                         continue;
1456                 if (!config->entries[i]->machine_id)
1457                         continue;
1458
1459                 m = StrDuplicate(config->entries[i]->machine_id);
1460                 m[8] = '\0';
1461                 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m);
1462                 FreePool(config->entries[i]->title_show);
1463                 config->entries[i]->title_show = s;
1464                 config->entries[i]->non_unique = FALSE;
1465                 FreePool(m);
1466         }
1467
1468         unique = TRUE;
1469         for (i = 0; i < config->entry_count; i++) {
1470                 for (k = 0; k < config->entry_count; k++) {
1471                         if (i == k)
1472                                 continue;
1473                         if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1474                                 continue;
1475
1476                         unique = FALSE;
1477                         config->entries[i]->non_unique = TRUE;
1478                         config->entries[k]->non_unique = TRUE;
1479                 }
1480         }
1481         if (unique)
1482                 return;
1483
1484         /* add file name to non-unique titles */
1485         for (i = 0; i < config->entry_count; i++) {
1486                 CHAR16 *s;
1487
1488                 if (!config->entries[i]->non_unique)
1489                         continue;
1490                 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file);
1491                 FreePool(config->entries[i]->title_show);
1492                 config->entries[i]->title_show = s;
1493                 config->entries[i]->non_unique = FALSE;
1494         }
1495 }
1496
1497 static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(VOID)) {
1498         ConfigEntry *entry;
1499
1500         entry = AllocateZeroPool(sizeof(ConfigEntry));
1501         entry->title = StrDuplicate(title);
1502         entry->call = call;
1503         entry->no_autoselect = TRUE;
1504         config_add_entry(config, entry);
1505         return TRUE;
1506 }
1507
1508 static ConfigEntry *config_entry_add_loader(Config *config, EFI_HANDLE *device,
1509                                             enum loader_type type,CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
1510         ConfigEntry *entry;
1511
1512         entry = AllocateZeroPool(sizeof(ConfigEntry));
1513         entry->type = type;
1514         entry->title = StrDuplicate(title);
1515         entry->device = device;
1516         entry->loader = StrDuplicate(loader);
1517         entry->file = StrDuplicate(file);
1518         StrLwr(entry->file);
1519         entry->key = key;
1520         config_add_entry(config, entry);
1521
1522         return entry;
1523 }
1524
1525 static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
1526                                          CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
1527         EFI_FILE_HANDLE handle;
1528         ConfigEntry *entry;
1529         EFI_STATUS err;
1530
1531         /* do not add an entry for ourselves */
1532         if (loaded_image_path && StriCmp(loader, loaded_image_path) == 0)
1533                 return FALSE;
1534
1535         /* check existence */
1536         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL);
1537         if (EFI_ERROR(err))
1538                 return FALSE;
1539         uefi_call_wrapper(handle->Close, 1, handle);
1540
1541         entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, file, key, title, loader);
1542         if (!entry)
1543                 return FALSE;
1544
1545         /* do not boot right away into auto-detected entries */
1546         entry->no_autoselect = TRUE;
1547
1548         /* export identifiers of automatically added entries */
1549         if (config->entries_auto) {
1550                 CHAR16 *s;
1551
1552                 s = PoolPrint(L"%s %s", config->entries_auto, file);
1553                 FreePool(config->entries_auto);
1554                 config->entries_auto = s;
1555         } else
1556                 config->entries_auto = StrDuplicate(file);
1557
1558         return TRUE;
1559 }
1560
1561 static VOID config_entry_add_osx(Config *config) {
1562         EFI_STATUS err;
1563         UINTN handle_count = 0;
1564         EFI_HANDLE *handles = NULL;
1565
1566         err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles);
1567         if (!EFI_ERROR(err)) {
1568                 UINTN i;
1569
1570                 for (i = 0; i < handle_count; i++) {
1571                         EFI_FILE *root;
1572                         BOOLEAN found;
1573
1574                         root = LibOpenRoot(handles[i]);
1575                         if (!root)
1576                                 continue;
1577                         found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"OS X",
1578                                                              L"\\System\\Library\\CoreServices\\boot.efi");
1579                         uefi_call_wrapper(root->Close, 1, root);
1580                         if (found)
1581                                 break;
1582                 }
1583
1584                 FreePool(handles);
1585         }
1586 }
1587
1588 static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_image, EFI_FILE *root_dir) {
1589         EFI_FILE_HANDLE linux_dir;
1590         EFI_STATUS err;
1591
1592         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
1593         if (!EFI_ERROR(err)) {
1594                 for (;;) {
1595                         CHAR16 buf[256];
1596                         UINTN bufsize;
1597                         EFI_FILE_INFO *f;
1598                         CHAR8 *sections[] = {
1599                                 (UINT8 *)".osrel",
1600                                 NULL
1601                         };
1602                         UINTN offs[ELEMENTSOF(sections)-1] = {};
1603                         UINTN szs[ELEMENTSOF(sections)-1] = {};
1604                         UINTN addrs[ELEMENTSOF(sections)-1] = {};
1605                         CHAR8 *content = NULL;
1606                         UINTN len;
1607                         CHAR8 *line;
1608                         UINTN pos = 0;
1609                         CHAR8 *key, *value;
1610                         CHAR16 *os_name = NULL;
1611                         CHAR16 *os_id = NULL;
1612                         CHAR16 *os_version = NULL;
1613
1614                         bufsize = sizeof(buf);
1615                         err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf);
1616                         if (bufsize == 0 || EFI_ERROR(err))
1617                                 break;
1618
1619                         f = (EFI_FILE_INFO *) buf;
1620                         if (f->FileName[0] == '.')
1621                                 continue;
1622                         if (f->Attribute & EFI_FILE_DIRECTORY)
1623                                 continue;
1624                         len = StrLen(f->FileName);
1625                         if (len < 5)
1626                                 continue;
1627                         if (StriCmp(f->FileName + len - 4, L".efi") != 0)
1628                                 continue;
1629
1630                         /* look for an .osrel section in the .efi binary */
1631                         err = pefile_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
1632                         if (EFI_ERROR(err))
1633                                 continue;
1634
1635                         len = file_read(linux_dir, f->FileName, offs[0], szs[0], &content);
1636                         if (len <= 0)
1637                                 continue;
1638
1639                         /* read properties from the embedded os-release file */
1640                         line = content;
1641                         while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
1642                                 if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
1643                                         os_name = stra_to_str(value);
1644                                         continue;
1645                                 }
1646
1647                                 if (strcmpa((CHAR8 *)"ID", key) == 0) {
1648                                         os_id = stra_to_str(value);
1649                                         continue;
1650                                 }
1651
1652                                 if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) {
1653                                         os_version = stra_to_str(value);
1654                                         continue;
1655                                 }
1656                         }
1657
1658                         if (os_name && os_id && os_version) {
1659                                 CHAR16 *conf;
1660                                 CHAR16 *path;
1661
1662                                 conf = PoolPrint(L"%s-%s", os_id, os_version);
1663                                 path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
1664                                 config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path);
1665                                 FreePool(conf);
1666                                 FreePool(path);
1667                                 FreePool(os_name);
1668                                 FreePool(os_id);
1669                                 FreePool(os_version);
1670                         }
1671
1672                         FreePool(content);
1673                 }
1674                 uefi_call_wrapper(linux_dir->Close, 1, linux_dir);
1675         }
1676 }
1677
1678 static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
1679         EFI_HANDLE image;
1680         EFI_DEVICE_PATH *path;
1681         CHAR16 *options;
1682         EFI_STATUS err;
1683
1684         path = FileDevicePath(entry->device, entry->loader);
1685         if (!path) {
1686                 Print(L"Error getting device path.");
1687                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1688                 return EFI_INVALID_PARAMETER;
1689         }
1690
1691         err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
1692         if (EFI_ERROR(err)) {
1693                 Print(L"Error loading %s: %r", entry->loader, err);
1694                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1695                 goto out;
1696         }
1697
1698         if (config->options_edit)
1699                 options = config->options_edit;
1700         else if (entry->options)
1701                 options = entry->options;
1702         else
1703                 options = NULL;
1704         if (options) {
1705                 EFI_LOADED_IMAGE *loaded_image;
1706
1707                 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
1708                                         parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
1709                 if (EFI_ERROR(err)) {
1710                         Print(L"Error getting LoadedImageProtocol handle: %r", err);
1711                         uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1712                         goto out_unload;
1713                 }
1714                 loaded_image->LoadOptions = options;
1715                 loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
1716         }
1717
1718         efivar_set_time_usec(L"LoaderTimeExecUSec", 0);
1719         err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
1720 out_unload:
1721         uefi_call_wrapper(BS->UnloadImage, 1, image);
1722 out:
1723         FreePool(path);
1724         return err;
1725 }
1726
1727 static EFI_STATUS reboot_into_firmware(VOID) {
1728         CHAR8 *b;
1729         UINTN size;
1730         UINT64 osind;
1731         EFI_STATUS err;
1732
1733         osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
1734
1735         err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size);
1736         if (!EFI_ERROR(err))
1737                 osind |= (UINT64)*b;
1738         FreePool(b);
1739
1740         err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
1741         if (EFI_ERROR(err))
1742                 return err;
1743
1744         err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
1745         Print(L"Error calling ResetSystem: %r", err);
1746         uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1747         return err;
1748 }
1749
1750 static VOID config_free(Config *config) {
1751         UINTN i;
1752
1753         for (i = 0; i < config->entry_count; i++)
1754                 config_entry_free(config->entries[i]);
1755         FreePool(config->entries);
1756         FreePool(config->entry_default_pattern);
1757         FreePool(config->options_edit);
1758         FreePool(config->entry_oneshot);
1759         FreePool(config->entries_auto);
1760 }
1761
1762 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
1763         CHAR16 *s;
1764         CHAR8 *b;
1765         UINTN size;
1766         EFI_LOADED_IMAGE *loaded_image;
1767         EFI_FILE *root_dir;
1768         CHAR16 *loaded_image_path;
1769         EFI_DEVICE_PATH *device_path;
1770         EFI_STATUS err;
1771         Config config;
1772         UINT64 init_usec;
1773         BOOLEAN menu = FALSE;
1774
1775         InitializeLib(image, sys_table);
1776         init_usec = time_usec();
1777         efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec);
1778         efivar_set(L"LoaderInfo", L"systemd-boot " VERSION, FALSE);
1779         s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
1780         efivar_set(L"LoaderFirmwareInfo", s, FALSE);
1781         FreePool(s);
1782         s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
1783         efivar_set(L"LoaderFirmwareType", s, FALSE);
1784         FreePool(s);
1785
1786         err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
1787                                 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
1788         if (EFI_ERROR(err)) {
1789                 Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
1790                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1791                 return err;
1792         }
1793
1794         /* export the device path this image is started from */
1795         device_path = DevicePathFromHandle(loaded_image->DeviceHandle);
1796         if (device_path) {
1797                 CHAR16 *str;
1798                 EFI_DEVICE_PATH *path, *paths;
1799
1800                 str = DevicePathToStr(device_path);
1801                 efivar_set(L"LoaderDeviceIdentifier", str, FALSE);
1802                 FreePool(str);
1803
1804                 paths = UnpackDevicePath(device_path);
1805                 for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
1806                         HARDDRIVE_DEVICE_PATH *drive;
1807                         CHAR16 uuid[37];
1808
1809                         if (DevicePathType(path) != MEDIA_DEVICE_PATH)
1810                                 continue;
1811                         if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
1812                                 continue;
1813                         drive = (HARDDRIVE_DEVICE_PATH *)path;
1814                         if (drive->SignatureType != SIGNATURE_TYPE_GUID)
1815                                 continue;
1816
1817                         GuidToString(uuid, (EFI_GUID *)&drive->Signature);
1818                         efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
1819                         break;
1820                 }
1821                 FreePool(paths);
1822         }
1823
1824         root_dir = LibOpenRoot(loaded_image->DeviceHandle);
1825         if (!root_dir) {
1826                 Print(L"Unable to open root directory: %r ", err);
1827                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1828                 return EFI_LOAD_ERROR;
1829         }
1830
1831
1832         /* the filesystem path to this image, to prevent adding ourselves to the menu */
1833         loaded_image_path = DevicePathToStr(loaded_image->FilePath);
1834         efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
1835
1836         /* scan "\loader\entries\*.conf" files */
1837         ZeroMem(&config, sizeof(Config));
1838         config_load(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
1839
1840         /* if we find some well-known loaders, add them to the end of the list */
1841         config_entry_add_linux(&config, loaded_image, root_dir);
1842         config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
1843                                      L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
1844         config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
1845                                      L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
1846         config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
1847                                      L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi");
1848         config_entry_add_osx(&config);
1849         efivar_set(L"LoaderEntriesAuto", config.entries_auto, FALSE);
1850
1851         if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
1852                 UINT64 osind = (UINT64)*b;
1853
1854                 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
1855                         config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware);
1856                 FreePool(b);
1857         }
1858
1859         if (config.entry_count == 0) {
1860                 Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
1861                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1862                 goto out;
1863         }
1864
1865         config_title_generate(&config);
1866
1867         /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
1868         config_default_entry_select(&config);
1869
1870         /* if no configured entry to select from was found, enable the menu */
1871         if (config.idx_default == -1) {
1872                 config.idx_default = 0;
1873                 if (config.timeout_sec == 0)
1874                         config.timeout_sec = 10;
1875         }
1876
1877         /* select entry or show menu when key is pressed or timeout is set */
1878         if (config.timeout_sec == 0) {
1879                 UINT64 key;
1880
1881                 err = console_key_read(&key, FALSE);
1882                 if (!EFI_ERROR(err)) {
1883                         INT16 idx;
1884
1885                         /* find matching key in config entries */
1886                         idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key));
1887                         if (idx >= 0)
1888                                 config.idx_default = idx;
1889                         else
1890                                 menu = TRUE;
1891                 }
1892         } else
1893                 menu = TRUE;
1894
1895         for (;;) {
1896                 ConfigEntry *entry;
1897
1898                 entry = config.entries[config.idx_default];
1899                 if (menu) {
1900                         efivar_set_time_usec(L"LoaderTimeMenuUSec", 0);
1901                         uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL);
1902                         if (!menu_run(&config, &entry, root_dir, loaded_image_path))
1903                                 break;
1904
1905                         /* run special entry like "reboot" */
1906                         if (entry->call) {
1907                                 entry->call();
1908                                 continue;
1909                         }
1910                 } else if (entry->splash) {
1911                         UINT8 *content = NULL;
1912                         INTN len;
1913
1914                         len = file_read(root_dir, entry->splash, 0, 0, &content);
1915                         if (len > 0)
1916                                 graphics_splash(content, len, NULL);
1917
1918                         FreePool(content);
1919                 }
1920
1921                 /* export the selected boot entry to the system */
1922                 efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
1923
1924                 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
1925                 err = image_start(image, &config, entry);
1926                 if (EFI_ERROR(err)) {
1927                         graphics_mode(FALSE);
1928                         Print(L"\nFailed to execute %s (%s): %r\n", entry->title, entry->loader, err);
1929                         uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1930                         goto out;
1931                 }
1932
1933                 menu = TRUE;
1934                 config.timeout_sec = 0;
1935         }
1936         err = EFI_SUCCESS;
1937 out:
1938         FreePool(loaded_image_path);
1939         config_free(&config);
1940         uefi_call_wrapper(root_dir->Close, 1, root_dir);
1941         uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
1942         return err;
1943 }