call Exit() when quitting
[platform/upstream/gummiboot.git] / gummiboot.c
1 /*
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
4  * menu.
5  *
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.
10  *
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.
15  *
16  * Copyright (C) 2012 Kay Sievers <kay.sievers@vrfy.org>
17  * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
18  *
19  * "Any intelligent fool can make things bigger, more complex, and more violent."
20  *   -- Albert Einstein
21  */
22
23 #include "efi.h"
24 #include "efilib.h"
25
26 /*
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.
30  */
31 static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
32
33 enum loader_type {
34         LOADER_UNDEFINED,
35         LOADER_EFI,
36         LOADER_LINUX
37 };
38
39 typedef struct {
40         CHAR16 *file;
41         CHAR16 *title;
42         enum loader_type type;
43         CHAR16 *loader;
44         CHAR16 *options;
45         BOOLEAN no_autoselect;
46 } ConfigEntry;
47
48 typedef struct {
49         ConfigEntry **entries;
50         UINTN entry_count;
51         UINTN idx_default;
52         INTN idx_default_efivar;
53         UINTN timeout_sec;
54         UINTN timeout_sec_config;
55         INTN timeout_sec_efivar;
56         CHAR16 *entry_default_pattern;
57         CHAR16 *options_edit;
58 } Config;
59
60 #ifdef __x86_64__
61 static UINT64 ticks_read() {
62         UINT64 a, d;
63         __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
64         return (d << 32) | a;
65 }
66 #else
67 static UINT64 ticks_read() { return 0; }
68 #endif
69
70 static EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
71         UINT32 flags;
72
73         flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
74         if (persistent)
75                 flags |= EFI_VARIABLE_NON_VOLATILE;
76
77         return uefi_call_wrapper(RT->SetVariable, 5, name, &loader_guid, flags,
78                                  value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, value);
79 }
80
81 static EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
82         CHAR16 *val;
83         UINTN size;
84         EFI_STATUS err;
85
86         size = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
87         val = AllocatePool(size);
88         if (!val)
89                 return EFI_OUT_OF_RESOURCES;
90
91         err = uefi_call_wrapper(RT->GetVariable, 5, name, &loader_guid, NULL, &size, val);
92         if (EFI_ERROR(err) == 0)
93                 *value = val;
94         else
95                 FreePool(val);
96         return err;
97
98 }
99
100 static EFI_STATUS efivar_set_int(CHAR16 *name, INTN i, BOOLEAN persistent) {
101         CHAR16 str[32];
102
103         SPrint(str, 32, L"%d", i);
104         return efivar_set(name, str, persistent);
105 }
106
107 static EFI_STATUS efivar_get_int(CHAR16 *name, INTN *i) {
108         CHAR16 *val;
109         EFI_STATUS err;
110
111         err = efivar_get(name, &val);
112         if (EFI_ERROR(err) == 0) {
113                 *i = Atoi(val);
114                 FreePool(val);
115         }
116         return err;
117 }
118
119 static VOID efivar_set_ticks(CHAR16 *name, UINT64 ticks) {
120         CHAR16 str[32];
121
122         if (ticks == 0)
123                 ticks = ticks_read();
124         if (ticks == 0)
125                 return;
126
127         SPrint(str, 32, L"%ld", ticks ? ticks : ticks_read());
128         efivar_set(name, str, FALSE);
129 }
130
131 static void cursor_left(UINTN *cursor, UINTN *first)
132 {
133         if ((*cursor) > 0)
134                 (*cursor)--;
135         else if ((*first) > 0)
136                 (*first)--;
137 }
138
139 static void cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
140 {
141         if ((*cursor)+2 < x_max)
142                 (*cursor)++;
143         else if ((*first) + (*cursor) < len)
144                 (*first)++;
145 }
146
147 static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
148         CHAR16 *line;
149         UINTN size;
150         UINTN len;
151         UINTN first;
152         CHAR16 *print;
153         UINTN cursor;
154         BOOLEAN exit;
155         BOOLEAN enter;
156
157         if (!line_in)
158                 line_in = L"";
159         size = StrLen(line_in) + 1024;
160         line = AllocatePool(size * sizeof(CHAR16));
161         StrCpy(line, line_in);
162         len = StrLen(line);
163         print = AllocatePool(x_max * sizeof(CHAR16));
164
165         uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
166
167         first = 0;
168         cursor = 0;
169         enter = FALSE;
170         exit = FALSE;
171         while (!exit) {
172                 UINTN index;
173                 EFI_STATUS err;
174                 EFI_INPUT_KEY key;
175                 UINTN i;
176
177                 i = len - first;
178                 if (i >= x_max-2)
179                         i = x_max-2;
180                 CopyMem(print, line + first, i * sizeof(CHAR16));
181                 print[i++] = ' ';
182                 print[i] = '\0';
183
184                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
185                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
186                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
187
188                 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
189                 err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
190                 if (EFI_ERROR(err))
191                         continue;
192
193                 switch (key.ScanCode) {
194                 case SCAN_ESC:
195                         exit = TRUE;
196                         break;
197                 case SCAN_HOME:
198                         cursor = 0;
199                         first = 0;
200                         continue;
201                 case SCAN_END:
202                         cursor = len;
203                         if (cursor >= x_max) {
204                                 cursor = x_max-2;
205                                 first = len - (x_max-2);
206                         }
207                         continue;
208                 case SCAN_UP:
209                         while((first + cursor) && line[first + cursor] == ' ')
210                                 cursor_left(&cursor, &first);
211                         while((first + cursor) && line[first + cursor] != ' ')
212                                 cursor_left(&cursor, &first);
213                         while((first + cursor) && line[first + cursor] == ' ')
214                                 cursor_left(&cursor, &first);
215                         if (first + cursor != len && first + cursor)
216                                 cursor_right(&cursor, &first, x_max, len);
217                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
218                         continue;
219                 case SCAN_DOWN:
220                         while(line[first + cursor] && line[first + cursor] == ' ')
221                                 cursor_right(&cursor, &first, x_max, len);
222                         while(line[first + cursor] && line[first + cursor] != ' ')
223                                 cursor_right(&cursor, &first, x_max, len);
224                         while(line[first + cursor] && line[first + cursor] == ' ')
225                                 cursor_right(&cursor, &first, x_max, len);
226                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
227                         continue;
228                 case SCAN_RIGHT:
229                         if (first + cursor == len)
230                                 continue;
231                         cursor_right(&cursor, &first, x_max, len);
232                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
233                         continue;
234                 case SCAN_LEFT:
235                         cursor_left(&cursor, &first);
236                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
237                         continue;
238                 case SCAN_DELETE:
239                         if (len == 0)
240                                 continue;
241                         if (first + cursor == len)
242                                 continue;
243                         for (i = first + cursor; i < len; i++)
244                                 line[i] = line[i+1];
245                         line[len-1] = ' ';
246                         len--;
247                         continue;
248                 }
249
250                 switch (key.UnicodeChar) {
251                 case CHAR_LINEFEED:
252                 case CHAR_CARRIAGE_RETURN:
253                         if (StrCmp(line, line_in) != 0) {
254                                 *line_out = line;
255                                 line = NULL;
256                         }
257                         enter = TRUE;
258                         exit = TRUE;
259                         break;
260                 case CHAR_BACKSPACE:
261                         if (len == 0)
262                                 continue;
263                         if (first == 0 && cursor == 0)
264                                 continue;
265                         for (i = first + cursor-1; i < len; i++)
266                                 line[i] = line[i+1];
267                         len--;
268                         if (cursor > 0)
269                                 cursor--;
270                         if (cursor > 0 || first == 0)
271                                 continue;
272                         /* show full line if it fits */
273                         if (len < x_max-2) {
274                                 cursor = first;
275                                 first = 0;
276                                 continue;
277                         }
278                         /* jump left to see what we delete */
279                         if (first > 10) {
280                                 first -= 10;
281                                 cursor = 10;
282                         } else {
283                                 cursor = first;
284                                 first = 0;
285                         }
286                         continue;
287                 case '\t':
288                 case ' ' ... '~':
289                 case 0x80 ... 0xffff:
290                         if (len+1 == size)
291                                 continue;
292                         for (i = len; i > first + cursor; i--)
293                                 line[i] = line[i-1];
294                         line[first + cursor] = key.UnicodeChar;
295                         len++;
296                         line[len] = '\0';
297                         if (cursor+2 < x_max)
298                                 cursor++;
299                         else if (first + cursor < len)
300                                 first++;
301                         continue;
302                 }
303         }
304
305         uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
306         FreePool(line);
307         return enter;
308 }
309
310 static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry) {
311         EFI_STATUS err;
312         INTN visible_max;
313         INTN idx_highlight;
314         INTN idx_highlight_prev;
315         INTN idx_first;
316         INTN idx_last;
317         BOOLEAN refresh;
318         BOOLEAN highlight;
319         INTN i;
320         UINTN line_width;
321         CHAR16 **lines;
322         UINTN x_max;
323         UINTN y_max;
324         CHAR16 *status;
325         CHAR16 *clearline;
326         INTN timeout_remain;
327         BOOLEAN exit = FALSE;
328         BOOLEAN run = TRUE;
329
330         uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
331         uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
332         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
333         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
334
335         err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
336         if (EFI_ERROR(err)) {
337                 x_max = 80;
338                 y_max = 25;
339         }
340
341         /* we check 10 times per second for a keystroke */
342         if (config->timeout_sec > 0)
343                 timeout_remain = config->timeout_sec * 10;
344         else
345                 timeout_remain = -1;
346
347         idx_highlight = config->idx_default;
348         idx_highlight_prev = 0;
349
350         visible_max = y_max - 2;
351
352         if (config->idx_default >= visible_max)
353                 idx_first = config->idx_default-1;
354         else
355                 idx_first = 0;
356
357         idx_last = idx_first + visible_max-1;
358
359         refresh = TRUE;
360         highlight = FALSE;
361
362         /* length of higlighted selector bar */
363         line_width = 20;
364         for (i = 0; i < config->entry_count; i++) {
365                 UINTN entry_len;
366
367                 entry_len = StrLen(config->entries[i]->title);
368                 if (line_width < entry_len)
369                         line_width = entry_len;
370         }
371         if (line_width > x_max)
372                 line_width = x_max;
373
374         /* menu entries title lines */
375         lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
376         for (i = 0; i < config->entry_count; i++)
377                 lines[i] = PoolPrint(L"  %-.*s ", line_width, config->entries[i]->title);
378
379         status = NULL;
380         clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
381         for (i = 0; i < x_max; i++)
382                 clearline[i] = ' ';
383         clearline[i] = 0;
384
385         while (!exit) {
386                 EFI_INPUT_KEY key;
387
388                 if (refresh) {
389                         for (i = 0; i < config->entry_count; i++) {
390                                 if (i < idx_first || i > idx_last)
391                                         continue;
392                                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, i - idx_first);
393                                 if (i == idx_highlight)
394                                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
395                                                           EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
396                                 else
397                                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
398                                                           EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
399                                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
400                                 if (i == config->idx_default_efivar) {
401                                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, i - idx_first);
402                                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"*");
403                                 }
404                         }
405                         refresh = FALSE;
406                 } else if (highlight) {
407                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, idx_highlight_prev - idx_first);
408                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
409                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
410                         if (idx_highlight_prev == config->idx_default_efivar) {
411                                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, idx_highlight_prev - idx_first);
412                                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"*");
413                         }
414
415                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, idx_highlight - idx_first);
416                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
417                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
418                         if (idx_highlight == config->idx_default_efivar) {
419                                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, idx_highlight - idx_first);
420                                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"*");
421                         }
422                         highlight = FALSE;
423                 }
424
425                 if (timeout_remain > 0) {
426                         FreePool(status);
427                         status = PoolPrint(L"Boot in %d seconds.", (timeout_remain + 5) / 10);
428                 }
429
430                 /* print status at last line of screen */
431                 if (status) {
432                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
433                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
434                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
435                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + StrLen(status));
436                 }
437
438                 err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
439                 if (err == EFI_NOT_READY) {
440                         UINTN index;
441
442                         if (timeout_remain == 0) {
443                                 exit = TRUE;
444                                 break;
445                         }
446                         if (timeout_remain > 0) {
447                                 uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
448                                 timeout_remain--;
449                                 continue;
450                         }
451                         uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
452                         continue;
453                 }
454                 timeout_remain = -1;
455
456                 /* clear status after keystroke */
457                 if (status) {
458                         FreePool(status);
459                         status = NULL;
460                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
461                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
462                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
463                 }
464
465                 idx_highlight_prev = idx_highlight;
466
467                 switch (key.ScanCode) {
468                 case SCAN_UP:
469                         if (idx_highlight > 0)
470                                 idx_highlight--;
471                         break;
472                 case SCAN_DOWN:
473                         if (idx_highlight < config->entry_count-1)
474                                 idx_highlight++;
475                         break;
476                 case SCAN_HOME:
477                         if (idx_highlight > 0) {
478                                 refresh = TRUE;
479                                 idx_highlight = 0;
480                         }
481                         break;
482                 case SCAN_END:
483                         if (idx_highlight < config->entry_count-1) {
484                                 refresh = TRUE;
485                                 idx_highlight = config->entry_count-1;
486                         }
487                         break;
488                 case SCAN_PAGE_UP:
489                         idx_highlight -= visible_max;
490                         if (idx_highlight < 0)
491                                 idx_highlight = 0;
492                         break;
493                 case SCAN_PAGE_DOWN:
494                         idx_highlight += visible_max;
495                         if (idx_highlight > config->entry_count-1)
496                                 idx_highlight = config->entry_count-1;
497                         break;
498                 case SCAN_F1:
499                         status = StrDuplicate(L"(d)efault, (+/-)timeout, (e)dit, (v)ersion (q)uit");
500                         break;
501                 }
502
503                 if (idx_highlight > idx_last) {
504                         idx_last = idx_highlight;
505                         idx_first = 1 + idx_highlight - visible_max;
506                         refresh = TRUE;
507                 }
508                 if (idx_highlight < idx_first) {
509                         idx_first = idx_highlight;
510                         idx_last = idx_highlight + visible_max-1;
511                         refresh = TRUE;
512                 }
513                 idx_last = idx_first + visible_max-1;
514
515                 if (!refresh && idx_highlight != idx_highlight_prev)
516                         highlight = TRUE;
517
518                 switch (key.UnicodeChar) {
519                 case CHAR_LINEFEED:
520                 case CHAR_CARRIAGE_RETURN:
521                         exit = TRUE;
522                         break;
523                 case 'q':
524                         exit = TRUE;
525                         run = FALSE;
526                         break;
527                 case 'd':
528                         if (config->idx_default_efivar != idx_highlight) {
529                                 /* store the selected entry in a persistent EFI variable */
530                                 efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
531                                 config->idx_default_efivar = idx_highlight;
532                                 status = StrDuplicate(L"Default boot entry permanently stored.");
533                         } else {
534                                 /* clear the default entry EFI variable */
535                                 efivar_set(L"LoaderEntryDefault", NULL, TRUE);
536                                 config->idx_default_efivar = -1;
537                                 status = StrDuplicate(L"Default boot entry cleared.");
538                         }
539                         refresh = TRUE;
540                         break;
541                 case '-':
542                         if (config->timeout_sec_efivar > 0) {
543                                 config->timeout_sec_efivar--;
544                                 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
545                                 if (config->timeout_sec_efivar > 0)
546                                         status = PoolPrint(L"Menu timeout of %d sec permanently stored.",
547                                                            config->timeout_sec_efivar);
548                                 else
549                                         status = StrDuplicate(L"Menu permanently disabled. "
550                                                               "Hold down key at bootup to show menu.");
551                         } else if (config->timeout_sec_efivar <= 0){
552                                 config->timeout_sec_efivar = -1;
553                                 efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
554                                 if (config->timeout_sec_config > 0)
555                                         status = PoolPrint(L"Menu timeout of %d sec defined by configuration file.",
556                                                            config->timeout_sec_config);
557                                 else
558                                         status = StrDuplicate(L"Menu permanently disabled. "
559                                                               "Hold down key at bootup to show menu.");
560                         }
561                         break;
562                 case '+':
563                         if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
564                                 config->timeout_sec_efivar++;
565                         config->timeout_sec_efivar++;
566                         efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
567                         if (config->timeout_sec_efivar > 0)
568                                 status = PoolPrint(L"Menu timeout of %d sec permanently stored.",
569                                                    config->timeout_sec_efivar);
570                         else
571                                 status = StrDuplicate(L"Menu permanently disabled. "
572                                                       "Hold down key at bootup to show menu.");
573                         break;
574                 case 'e':
575                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
576                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
577                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
578                         if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max, y_max-1))
579                                 exit = TRUE;
580                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
581                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
582                         break;
583                 case 'v':
584                         status = PoolPrint(L"gummiboot %d, UEFI %d.%02d, %s %d.%02d",
585                                            VERSION,
586                                            ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff,
587                                            ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
588                         break;
589                 }
590         }
591
592         for (i = 0; i < config->entry_count; i++)
593                 FreePool(lines[i]);
594         FreePool(lines);
595         FreePool(clearline);
596         *chosen_entry = config->entries[idx_highlight];
597
598         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
599         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
600         return run;
601 }
602
603 static VOID config_add_entry(Config *config, ConfigEntry *entry) {
604         if ((config->entry_count & 15) == 0) {
605                 UINTN i;
606
607                 i = config->entry_count + 16;
608                 if (config->entry_count == 0)
609                         config->entries = AllocatePool(sizeof(VOID *) * i);
610                 else
611                         config->entries = ReallocatePool(config->entries,
612                                                          sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
613         }
614         config->entries[config->entry_count++] = entry;
615 }
616
617 static VOID config_entry_free(ConfigEntry *entry) {
618         FreePool(entry->title);
619         FreePool(entry->loader);
620         FreePool(entry->options);
621 }
622
623 static BOOLEAN is_digit(CHAR16 c)
624 {
625         return (c >= '0') && (c <= '9');
626 }
627
628 static UINTN c_order(CHAR16 c)
629 {
630         if (c == '\0')
631                 return 0;
632         if (is_digit(c))
633                 return 0;
634         else if ((c >= 'a') && (c <= 'z'))
635                 return c;
636         else
637                 return c + 0x10000;
638 }
639
640 static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
641 {
642         CHAR16 *os1 = s1;
643         CHAR16 *os2 = s2;
644
645         while (*s1 || *s2) {
646                 INTN first;
647
648                 while ((*s2 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
649                         INTN order;
650
651                         order = c_order(*s1) - c_order(*s2);
652                         if (order)
653                                 return order;
654                         s1++;
655                         s2++;
656                 }
657
658                 while (*s1 == '0')
659                         s1++;
660                 while (*s2 == '0')
661                         s2++;
662
663                 first = 0;
664                 while (is_digit(*s1) && is_digit(*s2)) {
665                         if (first == 0)
666                                 first = *s1 - *s2;
667                         s1++;
668                         s2++;
669                 }
670
671                 if (is_digit(*s1))
672                         return 1;
673                 if (is_digit(*s2))
674                         return -1;
675
676                 if (first)
677                         return first;
678         }
679
680         return StrCmp(os1, os2);
681 }
682
683 static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
684         CHAR16 unichar;
685         UINTN len;
686         UINTN i;
687
688         if (stra[0] < 0x80)
689                 len = 1;
690         else if ((stra[0] & 0xe0) == 0xc0)
691                 len = 2;
692         else if ((stra[0] & 0xf0) == 0xe0)
693                 len = 3;
694         else if ((stra[0] & 0xf8) == 0xf0)
695                 len = 4;
696         else if ((stra[0] & 0xfc) == 0xf8)
697                 len = 5;
698         else if ((stra[0] & 0xfe) == 0xfc)
699                 len = 6;
700         else
701                 return -1;
702
703         switch (len) {
704         case 1:
705                 unichar = stra[0];
706                 break;
707         case 2:
708                 unichar = stra[0] & 0x1f;
709                 break;
710         case 3:
711                 unichar = stra[0] & 0x0f;
712                 break;
713         case 4:
714                 unichar = stra[0] & 0x07;
715                 break;
716         case 5:
717                 unichar = stra[0] & 0x03;
718                 break;
719         case 6:
720                 unichar = stra[0] & 0x01;
721                 break;
722         }
723
724         for (i = 1; i < len; i++) {
725                 if ((stra[i] & 0xc0) != 0x80)
726                         return -1;
727                 unichar <<= 6;
728                 unichar |= stra[i] & 0x3f;
729         }
730
731         *c = unichar;
732         return len;
733 }
734
735 CHAR16 *stra_to_str(CHAR8 *stra) {
736         UINTN strlen;
737         UINTN len;
738         UINTN i;
739         CHAR16 *str;
740
741         len = strlena(stra);
742         str = AllocatePool((len + 1) * sizeof(CHAR16));
743
744         strlen = 0;
745         i = 0;
746         while (i < len) {
747                 INTN utf8len;
748
749                 utf8len = utf8_to_16(stra + i, str + strlen);
750                 if (utf8len <= 0) {
751                         /* invalid utf8 sequence, skip the garbage */
752                         i++;
753                         continue;
754                 }
755
756                 strlen++;
757                 i += utf8len;
758         }
759         str[strlen] = '\0';
760         return str;
761 }
762
763 CHAR16 *stra_to_path(CHAR8 *stra) {
764         CHAR16 *str;
765         UINTN strlen;
766         UINTN len;
767         UINTN i;
768
769         len = strlena(stra);
770         str = AllocatePool((len + 2) * sizeof(CHAR16));
771
772         str[0] = '\\';
773         strlen = 1;
774         i = 0;
775         while (i < len) {
776                 INTN utf8len;
777
778                 utf8len = utf8_to_16(stra + i, str + strlen);
779                 if (utf8len <= 0) {
780                         /* invalid utf8 sequence, skip the garbage */
781                         i++;
782                         continue;
783                 }
784
785                 if (str[strlen] == '/')
786                         str[strlen] = '\\';
787                 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
788                         /* skip double slashes */
789                         i += utf8len;
790                         continue;
791                 }
792
793                 strlen++;
794                 i += utf8len;
795         }
796         str[strlen] = '\0';
797         return str;
798 }
799
800 static CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
801         do {
802                 if (*s == c)
803                         return s;
804         } while (*s++);
805         return NULL;
806 }
807
808 static CHAR8 *line_get_key_value(CHAR8 *line, CHAR8 **key_ret, CHAR8 **value_ret) {
809         CHAR8 *next;
810         CHAR8 *key, *value;
811         UINTN linelen;
812
813         /* terminate */
814 skip:
815         next = line;
816         while (*next && !strchra((CHAR8 *)"\n\r", *next))
817                 next++;
818         *next = '\0';
819
820         linelen = next - line;
821         if (linelen == 0)
822                 return NULL;
823
824         /* next line */
825         next++;
826         while (*next && strchra((CHAR8 *)"\n\r", *next))
827                 next++;
828
829         /* trailing whitespace */
830         while (linelen && strchra((CHAR8 *)" \t", line[linelen-1]))
831                 linelen--;
832         line[linelen] = '\0';
833
834         /* leading whitespace */
835         while (strchra((CHAR8 *)" \t", *line))
836                 line++;
837
838         key = line;
839         line = next;
840
841         if (*key == '#')
842                 goto skip;
843
844         /* split key/value */
845         value = key;
846         while (*value && !strchra((CHAR8 *)" \t", *value))
847                 value++;
848         if (*value == '\0')
849                 goto skip;
850         *value = '\0';
851         value++;
852         while (*value && strchra((CHAR8 *)" \t", *value))
853                 value++;
854
855         *key_ret = key;
856         *value_ret = value;
857         return next;
858 }
859
860 static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
861         CHAR8 *line;
862         CHAR8 *key, *value;
863
864         line = content;
865         while ((line = line_get_key_value(line, &key, &value))) {
866                 if (strcmpa((CHAR8 *)"timeout", key) == 0) {
867                         CHAR16 *s;
868
869                         s = stra_to_str(value);
870                         config->timeout_sec_config = Atoi(s);
871                         config->timeout_sec = config->timeout_sec_config;
872                         FreePool(s);
873                         continue;
874                 }
875                 if (strcmpa((CHAR8 *)"default", key) == 0) {
876                         config->entry_default_pattern = stra_to_str(value);
877                         StrLwr(config->entry_default_pattern);
878                         continue;
879                 }
880         }
881 }
882
883 static VOID config_entry_add_from_file(Config *config, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
884         ConfigEntry *entry;
885         CHAR8 *line;
886         CHAR8 *key, *value;
887         UINTN len;
888         CHAR16 *initrd = NULL;
889
890         entry = AllocateZeroPool(sizeof(ConfigEntry));
891
892         line = content;
893         while ((line = line_get_key_value(line, &key, &value))) {
894                 if (strcmpa((CHAR8 *)"title", key) == 0) {
895                         FreePool(entry->title);
896                         entry->title = stra_to_str(value);
897                         continue;
898                 }
899
900                 if (strcmpa((CHAR8 *)"linux", key) == 0) {
901                         FreePool(entry->loader);
902                         entry->type = LOADER_LINUX;
903                         entry->loader = stra_to_path(value);
904                         continue;
905                 }
906
907                 if (strcmpa((CHAR8 *)"efi", key) == 0) {
908                         entry->type = LOADER_EFI;
909                         FreePool(entry->loader);
910                         entry->loader = stra_to_path(value);
911
912                         /* do not add an entry for ourselves */
913                         if (StrCmp(entry->loader, loaded_image_path) == 0) {
914                                 entry->type = LOADER_UNDEFINED;
915                                 break;
916                         }
917                         continue;
918                 }
919
920                 if (strcmpa((CHAR8 *)"initrd", key) == 0) {
921                         CHAR16 *new;
922
923                         new = stra_to_path(value);
924                         if (initrd) {
925                                 CHAR16 *s;
926
927                                 s = PoolPrint(L"%s initrd=%s", initrd, new);
928                                 FreePool(initrd);
929                                 initrd = s;
930                         } else
931                                 initrd = PoolPrint(L"initrd=%s", new);
932                         FreePool(new);
933                         continue;
934                 }
935
936                 if (strcmpa((CHAR8 *)"options", key) == 0) {
937                         CHAR16 *new;
938
939                         new = stra_to_str(value);
940                         if (entry->options) {
941                                 CHAR16 *s;
942
943                                 s = PoolPrint(L"%s %s", entry->options, new);
944                                 FreePool(entry->options);
945                                 entry->options = s;
946                         } else {
947                                 entry->options = new;
948                                 new = NULL;
949                         }
950                         FreePool(new);
951                         continue;
952                 }
953         }
954
955         if (entry->type == LOADER_UNDEFINED) {
956                 config_entry_free(entry);
957                 FreePool(initrd);
958                 FreePool(entry);
959                 return;
960         }
961
962         /* add initrd= to options */
963         if (entry->type == LOADER_LINUX && initrd) {
964                 if (entry->options) {
965                         CHAR16 *s;
966
967                         s = PoolPrint(L"%s %s", initrd, entry->options);
968                         FreePool(entry->options);
969                         entry->options = s;
970                 } else {
971                         entry->options = initrd;
972                         initrd = NULL;
973                 }
974         }
975         FreePool(initrd);
976
977         entry->file = StrDuplicate(file);
978         len = StrLen(entry->file);
979         /* remove ".conf" */
980         if (len > 5)
981                 entry->file[len - 5] = '\0';
982         StrLwr(entry->file);
983
984         if (!entry->title)
985                 entry->title = StrDuplicate(entry->loader);
986
987         config_add_entry(config, entry);
988 }
989
990 static UINTN file_read(Config *config, EFI_FILE_HANDLE dir, const CHAR16 *name, CHAR8 **content) {
991         EFI_FILE_HANDLE handle;
992         EFI_FILE_INFO *info;
993         CHAR8 *buf;
994         UINTN buflen;
995         EFI_STATUS err;
996         UINTN len = 0;
997
998         err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0);
999         if (EFI_ERROR(err))
1000                 goto out;
1001
1002         info = LibFileInfo(handle);
1003         buflen = info->FileSize+1;
1004         buf = AllocatePool(buflen);
1005
1006         err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
1007         if (EFI_ERROR(err) == EFI_SUCCESS) {
1008                 buf[buflen] = '\0';
1009                 *content = buf;
1010                 len = buflen;
1011         } else
1012                 FreePool(buf);
1013
1014         FreePool(info);
1015         uefi_call_wrapper(handle->Close, 1, handle);
1016 out:
1017         return len;
1018 }
1019
1020 static VOID config_load(Config *config, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
1021         EFI_FILE_HANDLE entries_dir;
1022         EFI_STATUS err;
1023         CHAR8 *content;
1024         INTN sec;
1025         UINTN len;
1026         UINTN i;
1027
1028         len = file_read(config, root_dir, L"\\loader\\loader.conf", &content);
1029         if (len > 0)
1030                 config_defaults_load_from_file(config, content);
1031
1032         err = efivar_get_int(L"LoaderConfigTimeout", &sec);
1033         if (EFI_ERROR(err) == EFI_SUCCESS) {
1034                 config->timeout_sec_efivar = sec;
1035                 config->timeout_sec = sec;
1036         } else
1037                 config->timeout_sec_efivar = -1;
1038
1039         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0);
1040         if (EFI_ERROR(err) == EFI_SUCCESS) {
1041                 for (;;) {
1042                         CHAR16 buf[256];
1043                         UINTN bufsize;
1044                         EFI_FILE_INFO *f;
1045                         CHAR8 *content;
1046                         UINTN len;
1047
1048                         bufsize = sizeof(buf);
1049                         err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
1050                         if (bufsize == 0 || EFI_ERROR(err))
1051                                 break;
1052
1053                         f = (EFI_FILE_INFO *) buf;
1054                         if (f->FileName[0] == '.')
1055                                 continue;
1056                         if (f->Attribute & EFI_FILE_DIRECTORY)
1057                                 continue;
1058                         len = StrLen(f->FileName);
1059                         if (len < 6)
1060                                 continue;
1061                         if (StriCmp(f->FileName + len - 5, L".conf") != 0)
1062                                 continue;
1063
1064                         len = file_read(config, entries_dir, f->FileName, &content);
1065                         if (len > 0)
1066                                 config_entry_add_from_file(config, f->FileName, content, loaded_image_path);
1067                 }
1068                 uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
1069         }
1070
1071         /* sort entries after version number */
1072         for (i = 1; i < config->entry_count; i++) {
1073                 BOOLEAN more;
1074                 UINTN j;
1075
1076                 more = FALSE;
1077                 for (j = 0; j < config->entry_count - i; j++) {
1078                         ConfigEntry *entry;
1079
1080                         if (str_verscmp(config->entries[j]->file, config->entries[j+1]->file) <= 0)
1081                                 continue;
1082                         entry = config->entries[j];
1083                         config->entries[j] = config->entries[j+1];
1084                         config->entries[j+1] = entry;
1085                         more = TRUE;
1086                 }
1087                 if (!more)
1088                         break;
1089         }
1090 }
1091
1092 static VOID config_default_entry_select(Config *config) {
1093         CHAR16 *var;
1094         EFI_STATUS err;
1095
1096         /*
1097          * The EFI variable to specify a boot entry for the next, and only the
1098          * next reboot. The variable is always cleared directly after it is read.
1099          */
1100         err = efivar_get(L"LoaderEntryOneShot", &var);
1101         if (EFI_ERROR(err) == EFI_SUCCESS) {
1102                 BOOLEAN found = FALSE;
1103                 UINTN i;
1104
1105                 for (i = 0; i < config->entry_count; i++) {
1106                         if (!config->entries[i]->file)
1107                                 continue;
1108                         if (StrCmp(config->entries[i]->file, var) == 0) {
1109                                 config->idx_default = i;
1110                                 found = TRUE;
1111                                 break;
1112                         }
1113                 }
1114                 efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
1115                 FreePool(var);
1116                 if (found)
1117                         return;
1118         }
1119
1120         /*
1121          * The EFI variable to select the default boot entry overrides the
1122          * configured pattern. The variable can be set and cleared by pressing
1123          * the 'd' key in the loader selection menu, the entry is marked with
1124          * an '*'.
1125          */
1126         err = efivar_get(L"LoaderEntryDefault", &var);
1127         if (EFI_ERROR(err) == EFI_SUCCESS) {
1128                 BOOLEAN found = FALSE;
1129                 UINTN i;
1130
1131                 for (i = 0; i < config->entry_count; i++) {
1132                         if (!config->entries[i]->file)
1133                                 continue;
1134                         if (StrCmp(config->entries[i]->file, var) == 0) {
1135                                 config->idx_default = i;
1136                                 config->idx_default_efivar = i;
1137                                 found = TRUE;
1138                                 break;
1139                         }
1140                 }
1141                 FreePool(var);
1142                 if (found)
1143                         return;
1144         }
1145         config->idx_default_efivar = -1;
1146
1147         /*
1148          * Match the pattern from the end of the list to the start, find last
1149          * entry (largest number) matching the given pattern.
1150          */
1151         if (config->entry_default_pattern) {
1152                 UINTN i;
1153
1154                 for (i = config->entry_count-1; i >= 0; i--) {
1155                         if (!config->entries[i]->file)
1156                                 continue;
1157                         if (config->entries[i]->no_autoselect)
1158                                 continue;
1159                         if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
1160                                 config->idx_default = i;
1161                                 return;
1162                         }
1163                 }
1164         }
1165
1166         /* select the last entry */
1167         if (config->entry_count)
1168                 config->idx_default = config->entry_count-1;
1169 }
1170
1171 static VOID config_entry_add_loader(Config *config, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
1172                                     CHAR16 *file, CHAR16 *title, CHAR16 *loader) {
1173         EFI_FILE_HANDLE handle;
1174         EFI_STATUS err;
1175         ConfigEntry *entry;
1176
1177         /* check existence */
1178         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0);
1179         if (EFI_ERROR(err))
1180                 return;
1181         uefi_call_wrapper(handle->Close, 1, handle);
1182
1183         /* do not add an entry for ourselves */
1184         if (StrCmp(loader, loaded_image_path) == 0)
1185                 return;
1186
1187         entry = AllocateZeroPool(sizeof(ConfigEntry));
1188         entry->title = StrDuplicate(title);
1189         entry->loader = StrDuplicate(loader);
1190         if (file)
1191                 entry->file = StrDuplicate(file);
1192         entry->no_autoselect = TRUE;
1193         config_add_entry(config, entry);
1194 }
1195
1196 static EFI_STATUS image_start(EFI_HANDLE parent_image, EFI_LOADED_IMAGE *parent_loaded_image,
1197                               const Config *config, const ConfigEntry *entry) {
1198         EFI_STATUS err;
1199         EFI_HANDLE image;
1200         EFI_DEVICE_PATH *path;
1201         CHAR16 *options;
1202
1203         path = FileDevicePath(parent_loaded_image->DeviceHandle, entry->loader);
1204         if (!path) {
1205                 Print(L"Error getting device path.");
1206                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1207                 return EFI_INVALID_PARAMETER;
1208         }
1209
1210         err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
1211         if (EFI_ERROR(err)) {
1212                 Print(L"Error loading %s: %r", entry->loader, err);
1213                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1214                 goto out;
1215         }
1216
1217         if (config->options_edit)
1218                 options = config->options_edit;
1219         else if (entry->options)
1220                 options = entry->options;
1221         else
1222                 options = NULL;
1223         if (options) {
1224                 EFI_LOADED_IMAGE *loaded_image;
1225
1226                 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, &loaded_image,
1227                                         parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
1228                 if (EFI_ERROR(err)) {
1229                         Print(L"Error getting LoadedImageProtocol handle: %r", err);
1230                         uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1231                         goto out_unload;
1232                 }
1233                 loaded_image->LoadOptions = options;
1234                 loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
1235         }
1236
1237         efivar_set_ticks(L"LoaderTicksExec", 0);
1238         err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
1239 out_unload:
1240         uefi_call_wrapper(BS->UnloadImage, 1, image);
1241 out:
1242         FreePool(path);
1243         return err;
1244 }
1245
1246 static VOID config_free(Config *config) {
1247         UINTN i;
1248
1249         for (i = 0; i < config->entry_count; i++)
1250                 config_entry_free(config->entries[i]);
1251         FreePool(config->entries);
1252         FreePool(config->entry_default_pattern);
1253         FreePool(config->options_edit);
1254 }
1255
1256 EFI_STATUS EFIAPI efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
1257         EFI_LOADED_IMAGE *loaded_image;
1258         EFI_FILE *root_dir;
1259         CHAR16 *loaded_image_path;
1260         EFI_DEVICE_PATH *device_path;
1261         EFI_STATUS err;
1262         Config config;
1263         UINT64 ticks;
1264         BOOLEAN menu = FALSE;
1265
1266         ticks = ticks_read();
1267         InitializeLib(image, sys_table);
1268         efivar_set_ticks(L"LoaderTicksInit", ticks);
1269
1270         err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, &loaded_image,
1271                                 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
1272         if (EFI_ERROR(err)) {
1273                 Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
1274                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1275                 return err;
1276         }
1277
1278         /* export the device path this image is started from */
1279         device_path = DevicePathFromHandle(loaded_image->DeviceHandle);
1280         if (device_path)
1281                 efivar_set(L"LoaderDeviceIdentifier", DevicePathToStr(device_path), FALSE);
1282
1283         root_dir = LibOpenRoot(loaded_image->DeviceHandle);
1284         if (!root_dir) {
1285                 Print(L"Unable to open root directory: %r ", err);
1286                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1287                 return EFI_LOAD_ERROR;
1288         }
1289
1290         /* the filesystem path to this image, to prevent adding ourselves to the menu */
1291         loaded_image_path = DevicePathToStr(loaded_image->FilePath);
1292
1293         /* scan "\loader\entries\*.conf" files */
1294         ZeroMem(&config, sizeof(Config));
1295         config_load(&config, root_dir, loaded_image_path);
1296
1297         /* if we find some well-known loaders, add them to the end of the list */
1298         config_entry_add_loader(&config, root_dir, loaded_image_path,
1299                                 L"loader-bootmgfw", L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
1300         config_entry_add_loader(&config, root_dir, loaded_image_path,
1301                                 L"loader-bootx86", L"EFI default loader", L"\\EFI\\BOOT\\BOOTX64.EFI");
1302         FreePool(loaded_image_path);
1303
1304         /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
1305         config_default_entry_select(&config);
1306
1307         /* show menu when key is pressed or timeout is set */
1308         if (config.timeout_sec == 0) {
1309                 EFI_INPUT_KEY key;
1310
1311                 err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
1312                 menu = err != EFI_NOT_READY;
1313         } else
1314                 menu = TRUE;
1315
1316         for (;;) {
1317                 ConfigEntry *entry;
1318
1319                 entry = config.entries[config.idx_default];
1320                 if (menu) {
1321                         efivar_set_ticks(L"LoaderTicksStartMenu", 0);
1322                         if (!menu_run(&config, &entry))
1323                                 break;
1324                 }
1325
1326                 /* export the selected boot entry to the system */
1327                 err = efivar_set(L"LoaderEntrySelected",  entry->file, FALSE);
1328                 if (EFI_ERROR(err)) {
1329                        Print(L"Error storing LoaderEntrySelected variable: %r ", err);
1330                        uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1331                 }
1332
1333                 image_start(image, loaded_image, &config, entry);
1334
1335                 menu = TRUE;
1336                 config.timeout_sec = 0;
1337         }
1338
1339         config_free(&config);
1340         uefi_call_wrapper(root_dir->Close, 1, root_dir);
1341         uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
1342         return uefi_call_wrapper(BS->Exit, 4, image, EFI_SUCCESS, 0, NULL);
1343 }