gummiboot: Add 32 bit compatible rdtsc asm
[platform/upstream/gummiboot.git] / src / efi / 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  * All gummiboot code is LGPL not GPL, to stay out of politics and to give
7  * the freedom of copying code from programs to possible future libraries.
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
20  * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
21  *
22  * "Any intelligent fool can make things bigger, more complex, and more violent."
23  *   -- Albert Einstein
24  */
25
26 #include <efi.h>
27 #include <efilib.h>
28
29 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
30 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
31 #endif
32
33 #ifndef EFI_SECURITY_VIOLATION
34 #define EFI_SECURITY_VIOLATION      EFIERR(26)
35 #endif
36
37 /* magic string to find in the binary image */
38 static const char __attribute__((used)) magic[] = "#### LoaderInfo: gummiboot " VERSION " ####";
39
40 /*
41  * Allocated random UUID, intended to be shared across tools that implement
42  * the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
43  * associated EFI variables.
44  */
45 static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
46
47 static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
48
49 enum loader_type {
50         LOADER_UNDEFINED,
51         LOADER_EFI,
52         LOADER_LINUX
53 };
54
55 typedef struct {
56         CHAR16 *file;
57         CHAR16 *title_show;
58         CHAR16 *title;
59         CHAR16 *version;
60         CHAR16 *machine_id;
61         EFI_HANDLE *device;
62         enum loader_type type;
63         CHAR16 *loader;
64         CHAR16 *options;
65         CHAR16 key;
66         EFI_STATUS (*call)(void);
67         BOOLEAN no_autoselect;
68         BOOLEAN non_unique;
69 } ConfigEntry;
70
71 typedef struct {
72         ConfigEntry **entries;
73         UINTN entry_count;
74         INTN idx_default;
75         INTN idx_default_efivar;
76         UINTN timeout_sec;
77         UINTN timeout_sec_config;
78         INTN timeout_sec_efivar;
79         CHAR16 *entry_default_pattern;
80         CHAR16 *entry_oneshot;
81         CHAR16 *options_edit;
82         CHAR16 *entries_auto;
83 } Config;
84
85 static CHAR16 *stra_to_str(CHAR8 *stra);
86
87 #ifdef __x86_64__
88 static UINT64 ticks_read(void) {
89         UINT64 a, d;
90         __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
91         return (d << 32) | a;
92 }
93 #else
94 static UINT64 ticks_read(void) {
95         UINT64 val;
96         __asm__ volatile ("rdtsc" : "=A" (val));
97         return val;
98 }
99 #endif
100
101 static void cpuid_read(UINT32 info, UINT32 *eax, UINT32 *ebx, UINT32 *ecx, UINT32 *edx) {
102         *eax = info;
103         __asm__ volatile (
104                 "mov %%ebx, %%edi;"
105                 "cpuid;"
106                 "mov %%ebx, %%esi;"
107                 "mov %%edi, %%ebx;"
108                 :"+a" (*eax), "=S" (*ebx), "=c" (*ecx), "=d" (*edx)
109                 : :"edi"
110         );
111 }
112
113 static UINT64 cpufreq_read(void) {
114         UINT32 eax, ebx, ecx, edx;
115         union {
116                 UINT32 i[3][4];
117                 CHAR8 s[4 * 4 * 3 + 1];
118         } brand;
119         UINTN i;
120         CHAR8 *s;
121         UINT64 scale;
122         CHAR16 *str;
123         static UINT64 usec;
124
125         if (usec > 0)
126                 return usec;
127
128         for (i = 0; i < 3; i++) {
129                 cpuid_read(0x80000002 + i, &eax, &ebx, &ecx, &edx);
130                 brand.i[i][0] = eax;
131                 brand.i[i][1] = ebx;
132                 brand.i[i][2] = ecx;
133                 brand.i[i][3] = edx;
134         }
135         brand.s[4 * 4 * 3] = '\0';
136
137         /*
138          * Extract:
139          *   “x.xxyHz” or “xxxxyHz”, where y=M,G,T
140          * from CPUID brand string:
141          *   Intel(R) Core(TM) i5-2540M CPU @ 2.60GHz
142          *
143          * http://www.intel.com/content/dam/www/public/us/en/documents/
144          *   application-notes/processor-identification-cpuid-instruction-note.pdf
145          */
146         s = NULL;
147         for (i = 4; i < (4 * 4 * 3) - 2; i++) {
148                 if (brand.s[i+1] == 'H' && brand.s[i+2] == 'z') {
149                         s = brand.s + i;
150                         break;
151                 }
152         }
153         if (!s)
154                 return 0;
155
156         scale = 1000;
157         switch(*s){
158         case 'T':
159                 scale *= 1000;
160         case 'G':
161                 scale *= 1000;
162         case 'M':
163                 scale *= 1000;
164                 break;
165         default:
166                 return 0;
167         }
168
169         s -= 4;
170         s[4] = '\0';
171         if (s[1] == '.') {
172                 s[1] = s[0];
173                 s++;
174                 scale /= 100;
175         }
176
177         str = stra_to_str(s);
178         usec = Atoi(str) * scale;
179         FreePool(str);
180         return usec;
181 }
182
183 static UINT64 time_usec(void) {
184         UINT64 ticks;
185         UINT64 cpufreq;
186
187         ticks = ticks_read();
188         if (ticks == 0)
189                 return 0;
190
191         cpufreq = cpufreq_read();
192         if (cpufreq == 0)
193                 return 0;
194
195         return 1000 * 1000 * ticks / cpufreq;
196 }
197
198 static EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
199         UINT32 flags;
200
201         flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
202         if (persistent)
203                 flags |= EFI_VARIABLE_NON_VOLATILE;
204
205         return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf);
206 }
207
208 static EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
209         return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
210 }
211
212 static EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
213         CHAR8 *buf;
214         UINTN l;
215         EFI_STATUS err;
216
217         l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
218         buf = AllocatePool(l);
219         if (!buf)
220                 return EFI_OUT_OF_RESOURCES;
221
222         err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf);
223         if (!EFI_ERROR(err)) {
224                 *buffer = buf;
225                 if (size)
226                         *size = l;
227         } else
228                 FreePool(buf);
229         return err;
230
231 }
232
233 static EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
234         CHAR8 *buf;
235         CHAR16 *val;
236         UINTN size;
237         EFI_STATUS err;
238
239         err = efivar_get_raw(&loader_guid, name, &buf, &size);
240         if (EFI_ERROR(err))
241                 return err;
242
243         val = StrDuplicate((CHAR16 *)buf);
244         if (!val) {
245                 FreePool(val);
246                 return EFI_OUT_OF_RESOURCES;
247         }
248
249         *value = val;
250         return EFI_SUCCESS;
251 }
252
253 static EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
254         CHAR16 str[32];
255
256         SPrint(str, 32, L"%d", i);
257         return efivar_set(name, str, persistent);
258 }
259
260 static EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
261         CHAR16 *val;
262         EFI_STATUS err;
263
264         err = efivar_get(name, &val);
265         if (!EFI_ERROR(err)) {
266                 *i = Atoi(val);
267                 FreePool(val);
268         }
269         return err;
270 }
271
272 static VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
273         CHAR16 str[32];
274
275         if (usec == 0)
276                 usec = time_usec();
277         if (usec == 0)
278                 return;
279
280         SPrint(str, 32, L"%ld", usec);
281         efivar_set(name, str, FALSE);
282 }
283
284 #define EFI_SHIFT_STATE_VALID           0x80000000
285 #define EFI_RIGHT_CONTROL_PRESSED       0x00000004
286 #define EFI_LEFT_CONTROL_PRESSED        0x00000008
287 #define EFI_RIGHT_ALT_PRESSED           0x00000010
288 #define EFI_LEFT_ALT_PRESSED            0x00000020
289 #define EFI_CONTROL_PRESSED             (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED)
290 #define EFI_ALT_PRESSED                 (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED)
291 #define KEYPRESS(keys, scan, uni) ((((UINT64)keys) << 32) | ((scan) << 16) | (uni))
292 #define KEYCHAR(k) ((k) & 0xffff)
293 #define CHAR_CTRL(c) ((c) - 'a' + 1)
294
295 static EFI_STATUS key_read(UINT64 *key, BOOLEAN wait) {
296         #define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
297                 { 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } }
298
299         struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
300
301         typedef EFI_STATUS (EFIAPI *EFI_INPUT_RESET_EX)(
302                 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
303                 BOOLEAN ExtendedVerification;
304         );
305
306         typedef UINT8 EFI_KEY_TOGGLE_STATE;
307
308         typedef struct {
309                 UINT32 KeyShiftState;
310                 EFI_KEY_TOGGLE_STATE KeyToggleState;
311         } EFI_KEY_STATE;
312
313         typedef struct {
314                 EFI_INPUT_KEY Key;
315                 EFI_KEY_STATE KeyState;
316         } EFI_KEY_DATA;
317
318         typedef EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY_EX)(
319                 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
320                 EFI_KEY_DATA *KeyData;
321         );
322
323         typedef EFI_STATUS (EFIAPI *EFI_SET_STATE)(
324                 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
325                 EFI_KEY_TOGGLE_STATE *KeyToggleState;
326         );
327
328         typedef EFI_STATUS (EFIAPI *EFI_KEY_NOTIFY_FUNCTION)(
329                 EFI_KEY_DATA *KeyData;
330         );
331
332         typedef EFI_STATUS (EFIAPI *EFI_REGISTER_KEYSTROKE_NOTIFY)(
333                 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
334                 EFI_KEY_DATA KeyData;
335                 EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction;
336                 VOID **NotifyHandle;
337         );
338
339         typedef EFI_STATUS (EFIAPI *EFI_UNREGISTER_KEYSTROKE_NOTIFY)(
340                 struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
341                 VOID *NotificationHandle;
342         );
343
344         typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
345                 EFI_INPUT_RESET_EX Reset;
346                 EFI_INPUT_READ_KEY_EX ReadKeyStrokeEx;
347                 EFI_EVENT WaitForKeyEx;
348                 EFI_SET_STATE SetState;
349                 EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify;
350                 EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify;
351         } EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
352
353         EFI_GUID EfiSimpleTextInputExProtocolGuid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
354         static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx;
355         static BOOLEAN checked;
356         EFI_KEY_DATA keydata;
357         UINT32 shift = 0;
358         UINTN index;
359         EFI_STATUS err;
360
361         if (!checked) {
362                 err = LibLocateProtocol(&EfiSimpleTextInputExProtocolGuid, (VOID **)&TextInputEx);
363                 if (EFI_ERROR(err))
364                         TextInputEx = NULL;
365
366                 checked = TRUE;
367         }
368
369         if (!TextInputEx) {
370                 EFI_INPUT_KEY k;
371
372                 /* fallback for firmware which does not support SIMPLE_TEXT_INPUT_EX_PROTOCOL */
373                 if (wait)
374                         uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
375                 err  = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
376                 if (EFI_ERROR(err))
377                         return err;
378
379                 *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
380                 return 0;
381         }
382
383         if (wait)
384                 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &TextInputEx->WaitForKeyEx, &index);
385         err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata);
386         if (EFI_ERROR(err)) {
387                 /* hmm, we waited but we could read a key; some firmwares seem
388                  * to provide SimpleTextInputExProtocol but it does not do the
389                  * right thing; just fall back to SimpleTextInputProtocol. */
390                 if (wait)
391                         TextInputEx = NULL;
392                 return err;
393         }
394
395         /* do not distinguish between left and right keys */
396         if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
397                 if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
398                         shift |= EFI_CONTROL_PRESSED;
399                 if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
400                         shift |= EFI_ALT_PRESSED;
401         };
402
403         /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
404         *key = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
405         return 0;
406 }
407
408 static void cursor_left(UINTN *cursor, UINTN *first)
409 {
410         if ((*cursor) > 0)
411                 (*cursor)--;
412         else if ((*first) > 0)
413                 (*first)--;
414 }
415
416 static void cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
417 {
418         if ((*cursor)+1 < x_max)
419                 (*cursor)++;
420         else if ((*first) + (*cursor) < len)
421                 (*first)++;
422 }
423
424 static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
425         CHAR16 *line;
426         UINTN size;
427         UINTN len;
428         UINTN first;
429         CHAR16 *print;
430         UINTN cursor;
431         UINTN clear;
432         BOOLEAN exit;
433         BOOLEAN enter;
434
435         if (!line_in)
436                 line_in = L"";
437         size = StrLen(line_in) + 1024;
438         line = AllocatePool(size * sizeof(CHAR16));
439         StrCpy(line, line_in);
440         len = StrLen(line);
441         print = AllocatePool((x_max+1) * sizeof(CHAR16));
442
443         uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
444
445         first = 0;
446         cursor = 0;
447         clear = 0;
448         enter = FALSE;
449         exit = FALSE;
450         while (!exit) {
451                 EFI_STATUS err;
452                 UINT64 key;
453                 UINTN i;
454
455                 i = len - first;
456                 if (i >= x_max-1)
457                         i = x_max-1;
458                 CopyMem(print, line + first, i * sizeof(CHAR16));
459                 while (clear > 0 && i < x_max-1) {
460                         clear--;
461                         print[i++] = ' ';
462                 }
463                 print[i] = '\0';
464
465                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
466                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
467                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
468
469                 err = key_read(&key, TRUE);
470                 if (EFI_ERROR(err))
471                         continue;
472
473                 switch (key) {
474                 case KEYPRESS(0, SCAN_ESC, 0):
475                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
476                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
477                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
478                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
479                         exit = TRUE;
480                         break;
481
482                 case KEYPRESS(0, SCAN_HOME, 0):
483                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
484                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
485                         /* beginning-of-line */
486                         cursor = 0;
487                         first = 0;
488                         continue;
489
490                 case KEYPRESS(0, SCAN_END, 0):
491                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
492                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
493                         /* end-of-line */
494                         cursor = len - first;
495                         if (cursor+1 >= x_max) {
496                                 cursor = x_max-1;
497                                 first = len - (x_max-1);
498                         }
499                         continue;
500
501                 case KEYPRESS(0, SCAN_DOWN, 0):
502                 case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
503                 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
504                         /* forward-word */
505                         while (line[first + cursor] && line[first + cursor] == ' ')
506                                 cursor_right(&cursor, &first, x_max, len);
507                         while (line[first + cursor] && line[first + cursor] != ' ')
508                                 cursor_right(&cursor, &first, x_max, len);
509                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
510                         continue;
511
512                 case KEYPRESS(0, SCAN_UP, 0):
513                 case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
514                 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
515                         /* backward-word */
516                         if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
517                                 cursor_left(&cursor, &first);
518                                 while ((first + cursor) > 0 && line[first + cursor] == ' ')
519                                         cursor_left(&cursor, &first);
520                         }
521                         while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
522                                 cursor_left(&cursor, &first);
523                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
524                         continue;
525
526                 case KEYPRESS(0, SCAN_RIGHT, 0):
527                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
528                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
529                         /* forward-char */
530                         if (first + cursor == len)
531                                 continue;
532                         cursor_right(&cursor, &first, x_max, len);
533                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
534                         continue;
535
536                 case KEYPRESS(0, SCAN_LEFT, 0):
537                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
538                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
539                         /* backward-char */
540                         cursor_left(&cursor, &first);
541                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
542                         continue;
543
544                 case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
545                         /* kill-word */
546                         clear = 0;
547                         for (i = first + cursor; i < len && line[i] == ' '; i++)
548                                 clear++;
549                         for (; i < len && line[i] != ' '; i++)
550                                 clear++;
551
552                         for (i = first + cursor; i + clear < len; i++)
553                                 line[i] = line[i + clear];
554                         len -= clear;
555                         line[len] = '\0';
556                         continue;
557
558                 case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE):
559                         /* backward-kill-word */
560                         clear = 0;
561                         if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
562                                 cursor_left(&cursor, &first);
563                                 clear++;
564                                 while ((first + cursor) > 0 && line[first + cursor] == ' ') {
565                                         cursor_left(&cursor, &first);
566                                         clear++;
567                                 }
568                         }
569                         while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
570                                 cursor_left(&cursor, &first);
571                                 clear++;
572                         }
573                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
574
575                         for (i = first + cursor; i + clear < len; i++)
576                                 line[i] = line[i + clear];
577                         len -= clear;
578                         line[len] = '\0';
579                         continue;
580
581                 case KEYPRESS(0, SCAN_DELETE, 0):
582                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
583                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
584                         if (len == 0)
585                                 continue;
586                         if (first + cursor == len)
587                                 continue;
588                         for (i = first + cursor; i < len; i++)
589                                 line[i] = line[i+1];
590                         clear = 1;
591                         len--;
592                         continue;
593
594                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
595                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
596                         /* kill-line */
597                         line[first + cursor] = '\0';
598                         clear = len - (first + cursor);
599                         len = first + cursor;
600                         continue;
601
602                 case KEYPRESS(0, 0, CHAR_LINEFEED):
603                 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
604                         if (StrCmp(line, line_in) != 0) {
605                                 *line_out = line;
606                                 line = NULL;
607                         }
608                         enter = TRUE;
609                         exit = TRUE;
610                         break;
611
612                 case KEYPRESS(0, 0, CHAR_BACKSPACE):
613                         if (len == 0)
614                                 continue;
615                         if (first == 0 && cursor == 0)
616                                 continue;
617                         for (i = first + cursor-1; i < len; i++)
618                                 line[i] = line[i+1];
619                         clear = 1;
620                         len--;
621                         if (cursor > 0)
622                                 cursor--;
623                         if (cursor > 0 || first == 0)
624                                 continue;
625                         /* show full line if it fits */
626                         if (len < x_max) {
627                                 cursor = first;
628                                 first = 0;
629                                 continue;
630                         }
631                         /* jump left to see what we delete */
632                         if (first > 10) {
633                                 first -= 10;
634                                 cursor = 10;
635                         } else {
636                                 cursor = first;
637                                 first = 0;
638                         }
639                         continue;
640
641                 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
642                 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
643                         if (len+1 == size)
644                                 continue;
645                         for (i = len; i > first + cursor; i--)
646                                 line[i] = line[i-1];
647                         line[first + cursor] = KEYCHAR(key);
648                         len++;
649                         line[len] = '\0';
650                         if (cursor+1 < x_max)
651                                 cursor++;
652                         else if (first + cursor < len)
653                                 first++;
654                         continue;
655                 }
656         }
657
658         uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
659         FreePool(print);
660         FreePool(line);
661         return enter;
662 }
663
664 static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
665         UINTN i;
666
667         if (key == 0)
668                 return -1;
669
670         /* select entry by number key */
671         if (key >= '1' && key <= '9') {
672                 i = key - '0';
673                 if (i > config->entry_count)
674                         i = config->entry_count;
675                 return i-1;
676         }
677
678         /* find matching key in config entries */
679         for (i = start; i < config->entry_count; i++)
680                 if (config->entries[i]->key == key)
681                         return i;
682
683         for (i = 0; i < start; i++)
684                 if (config->entries[i]->key == key)
685                         return i;
686
687         return -1;
688 }
689
690 static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
691         UINT64 key;
692         UINTN i;
693         CHAR16 *s;
694         CHAR8 *b;
695         UINTN size;
696
697         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
698         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
699
700         Print(L"gummiboot version:      " VERSION "\n");
701         Print(L"loaded image:           %s\n", loaded_image_path);
702         Print(L"UEFI version:           %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
703         Print(L"firmware vendor:        %s\n", ST->FirmwareVendor);
704         Print(L"firmware version:       %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
705         if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
706                 Print(L"SecureBoot:             %s\n", *b > 0 ? L"enabled" : L"disabled");
707                 FreePool(b);
708         }
709
710         if (efivar_get_raw(&global_guid, L"SetupMode", &b, &size) == EFI_SUCCESS) {
711                 Print(L"SetupMode:              %s\n", *b > 0 ? L"setup" : L"user");
712                 FreePool(b);
713         }
714
715         if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
716                 Print(L"OsIndicationsSupported: %d\n", (UINT64)*b);
717                 FreePool(b);
718         }
719         Print(L"\n");
720
721         Print(L"timeout:                %d\n", config->timeout_sec);
722         if (config->timeout_sec_efivar >= 0)
723                 Print(L"timeout (EFI var):      %d\n", config->timeout_sec_efivar);
724         Print(L"timeout (config):       %d\n", config->timeout_sec_config);
725         if (config->entry_default_pattern)
726                 Print(L"default pattern:        '%s'\n", config->entry_default_pattern);
727         Print(L"\n");
728
729         Print(L"config entry count:     %d\n", config->entry_count);
730         Print(L"entry selected idx:     %d\n", config->idx_default);
731         if (config->idx_default_efivar >= 0)
732                 Print(L"entry EFI var idx:      %d\n", config->idx_default_efivar);
733         Print(L"\n");
734
735         if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS)
736                 Print(L"LoaderConfigTimeout:    %d\n", i);
737         if (config->entry_oneshot)
738                 Print(L"LoaderEntryOneShot:     %s\n", config->entry_oneshot);
739         if (efivar_get(L"LoaderDeviceIdentifier", &s) == EFI_SUCCESS) {
740                 Print(L"LoaderDeviceIdentifier: %s\n", s);
741                 FreePool(s);
742         }
743         if (efivar_get(L"LoaderDevicePartUUID", &s) == EFI_SUCCESS) {
744                 Print(L"LoaderDevicePartUUID:   %s\n", s);
745                 FreePool(s);
746         }
747         if (efivar_get(L"LoaderEntryDefault", &s) == EFI_SUCCESS) {
748                 Print(L"LoaderEntryDefault:     %s\n", s);
749                 FreePool(s);
750         }
751
752         Print(L"\n--- press key ---\n\n");
753         key_read(&key, TRUE);
754
755         for (i = 0; i < config->entry_count; i++) {
756                 ConfigEntry *entry;
757
758                 if (key == KEYPRESS(0, SCAN_ESC, 0) || key == KEYPRESS(0, 0, 'q'))
759                         break;
760
761                 entry = config->entries[i];
762                 Print(L"config entry:           %d/%d\n", i+1, config->entry_count);
763                 if (entry->file)
764                         Print(L"file                    '%s'\n", entry->file);
765                 Print(L"title show              '%s'\n", entry->title_show);
766                 if (entry->title)
767                         Print(L"title                   '%s'\n", entry->title);
768                 if (entry->version)
769                         Print(L"version                 '%s'\n", entry->version);
770                 if (entry->machine_id)
771                         Print(L"machine-id              '%s'\n", entry->machine_id);
772                 if (entry->device) {
773                         EFI_DEVICE_PATH *device_path;
774                         CHAR16 *str;
775
776                         device_path = DevicePathFromHandle(entry->device);
777                         if (device_path) {
778                                 str = DevicePathToStr(device_path);
779                                 Print(L"device handle           '%s'\n", str);
780                                 FreePool(str);
781                         }
782                 }
783                 if (entry->loader)
784                         Print(L"loader                  '%s'\n", entry->loader);
785                 if (entry->options)
786                         Print(L"options                 '%s'\n", entry->options);
787                 Print(L"auto-select             %s\n", entry->no_autoselect ? L"no" : L"yes");
788                 if (entry->call)
789                         Print(L"internal call           yes\n");
790
791                 Print(L"\n--- press key ---\n\n");
792                 key_read(&key, TRUE);
793         }
794
795         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
796 }
797
798 static EFI_STATUS console_text_mode(VOID) {
799         #define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \
800                 { 0xf42f7782, 0x12e, 0x4c12, { 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21 } };
801
802         struct _EFI_CONSOLE_CONTROL_PROTOCOL;
803
804         typedef enum {
805                 EfiConsoleControlScreenText,
806                 EfiConsoleControlScreenGraphics,
807                 EfiConsoleControlScreenMaxValue,
808         } EFI_CONSOLE_CONTROL_SCREEN_MODE;
809
810         typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE)(
811                 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
812                 EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode,
813                 BOOLEAN *UgaExists,
814                 BOOLEAN *StdInLocked
815         );
816
817         typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE)(
818                 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
819                 EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
820         );
821
822         typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN)(
823                 struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
824                 CHAR16 *Password
825         );
826
827         typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
828                 EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode;
829                 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
830                 EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn;
831         } EFI_CONSOLE_CONTROL_PROTOCOL;
832
833         EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
834         EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
835         EFI_STATUS err;
836
837         err = LibLocateProtocol(&ConsoleControlProtocolGuid, (VOID **)&ConsoleControl);
838         if (EFI_ERROR(err))
839                 return err;
840         return uefi_call_wrapper(ConsoleControl->SetMode, 2, ConsoleControl, EfiConsoleControlScreenText);
841 }
842
843 static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *loaded_image_path) {
844         EFI_STATUS err;
845         UINTN visible_max;
846         UINTN idx_highlight;
847         UINTN idx_highlight_prev;
848         UINTN idx_first;
849         UINTN idx_last;
850         BOOLEAN refresh;
851         BOOLEAN highlight;
852         UINTN i;
853         UINTN line_width;
854         CHAR16 **lines;
855         UINTN x_start;
856         UINTN y_start;
857         UINTN x_max;
858         UINTN y_max;
859         CHAR16 *status;
860         CHAR16 *clearline;
861         INTN timeout_remain;
862         INT16 idx;
863         BOOLEAN exit = FALSE;
864         BOOLEAN run = TRUE;
865         BOOLEAN wait = FALSE;
866
867         console_text_mode();
868         uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
869         uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
870         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
871         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
872
873         err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
874         if (EFI_ERROR(err)) {
875                 x_max = 80;
876                 y_max = 25;
877         }
878
879         /* we check 10 times per second for a keystroke */
880         if (config->timeout_sec > 0)
881                 timeout_remain = config->timeout_sec * 10;
882         else
883                 timeout_remain = -1;
884
885         idx_highlight = config->idx_default;
886         idx_highlight_prev = 0;
887
888         visible_max = y_max - 2;
889
890         if ((UINTN)config->idx_default >= visible_max)
891                 idx_first = config->idx_default-1;
892         else
893                 idx_first = 0;
894
895         idx_last = idx_first + visible_max-1;
896
897         refresh = TRUE;
898         highlight = FALSE;
899
900         /* length of the longest entry */
901         line_width = 5;
902         for (i = 0; i < config->entry_count; i++) {
903                 UINTN entry_len;
904
905                 entry_len = StrLen(config->entries[i]->title_show);
906                 if (line_width < entry_len)
907                         line_width = entry_len;
908         }
909         if (line_width > x_max-6)
910                 line_width = x_max-6;
911
912         /* offsets to center the entries on the screen */
913         x_start = (x_max - (line_width)) / 2;
914         if (config->entry_count < visible_max)
915                 y_start = ((visible_max - config->entry_count) / 2) + 1;
916         else
917                 y_start = 0;
918
919         /* menu entries title lines */
920         lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
921         for (i = 0; i < config->entry_count; i++) {
922                 UINTN j, k;
923
924                 lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
925                 for (j = 0; j < x_start; j++)
926                         lines[i][j] = ' ';
927
928                 for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
929                         lines[i][j] = config->entries[i]->title_show[k];
930
931                 for (; j < x_max; j++)
932                         lines[i][j] = ' ';
933                 lines[i][x_max] = '\0';
934         }
935
936         status = NULL;
937         clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
938         for (i = 0; i < x_max; i++)
939                 clearline[i] = ' ';
940         clearline[i] = 0;
941
942         while (!exit) {
943                 UINT64 key;
944
945                 if (refresh) {
946                         for (i = 0; i < config->entry_count; i++) {
947                                 if (i < idx_first || i > idx_last)
948                                         continue;
949                                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
950                                 if (i == idx_highlight)
951                                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
952                                                           EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
953                                 else
954                                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
955                                                           EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
956                                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
957                                 if ((INTN)i == config->idx_default_efivar) {
958                                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
959                                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
960                                 }
961                         }
962                         refresh = FALSE;
963                 } else if (highlight) {
964                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
965                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
966                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
967                         if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
968                                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
969                                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
970                         }
971
972                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
973                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
974                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
975                         if ((INTN)idx_highlight == config->idx_default_efivar) {
976                                 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
977                                 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
978                         }
979                         highlight = FALSE;
980                 }
981
982                 if (timeout_remain > 0) {
983                         FreePool(status);
984                         status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
985                 }
986
987                 /* print status at last line of screen */
988                 if (status) {
989                         UINTN len;
990                         UINTN x;
991
992                         /* center line */
993                         len = StrLen(status);
994                         if (len < x_max)
995                                 x = (x_max - len) / 2;
996                         else
997                                 x = 0;
998                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
999                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
1000                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
1001                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
1002                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
1003                 }
1004
1005                 err = key_read(&key, wait);
1006                 if (EFI_ERROR(err)) {
1007                         /* timeout reached */
1008                         if (timeout_remain == 0) {
1009                                 exit = TRUE;
1010                                 break;
1011                         }
1012
1013                         /* sleep and update status */
1014                         if (timeout_remain > 0) {
1015                                 uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
1016                                 timeout_remain--;
1017                                 continue;
1018                         }
1019
1020                         /* timeout disabled, wait for next key */
1021                         wait = TRUE;
1022                         continue;
1023                 }
1024
1025                 timeout_remain = -1;
1026
1027                 /* clear status after keystroke */
1028                 if (status) {
1029                         FreePool(status);
1030                         status = NULL;
1031                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
1032                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
1033                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
1034                 }
1035
1036                 idx_highlight_prev = idx_highlight;
1037
1038                 switch (key) {
1039                 case KEYPRESS(0, SCAN_UP, 0):
1040                 case KEYPRESS(0, 0, 'k'):
1041                         if (idx_highlight > 0)
1042                                 idx_highlight--;
1043                         break;
1044
1045                 case KEYPRESS(0, SCAN_DOWN, 0):
1046                 case KEYPRESS(0, 0, 'j'):
1047                         if (idx_highlight < config->entry_count-1)
1048                                 idx_highlight++;
1049                         break;
1050
1051                 case KEYPRESS(0, SCAN_HOME, 0):
1052                 case KEYPRESS(EFI_ALT_PRESSED, 0, '<'):
1053                         if (idx_highlight > 0) {
1054                                 refresh = TRUE;
1055                                 idx_highlight = 0;
1056                         }
1057                         break;
1058
1059                 case KEYPRESS(0, SCAN_END, 0):
1060                 case KEYPRESS(EFI_ALT_PRESSED, 0, '>'):
1061                         if (idx_highlight < config->entry_count-1) {
1062                                 refresh = TRUE;
1063                                 idx_highlight = config->entry_count-1;
1064                         }
1065                         break;
1066
1067                 case KEYPRESS(0, SCAN_PAGE_UP, 0):
1068                         if (idx_highlight > visible_max)
1069                                 idx_highlight -= visible_max;
1070                         else
1071                                 idx_highlight = 0;
1072                         break;
1073
1074                 case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
1075                         idx_highlight += visible_max;
1076                         if (idx_highlight > config->entry_count-1)
1077                                 idx_highlight = config->entry_count-1;
1078                         break;
1079
1080                 case KEYPRESS(0, 0, CHAR_LINEFEED):
1081                 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
1082                         exit = TRUE;
1083                         break;
1084
1085                 case KEYPRESS(0, SCAN_F1, 0):
1086                 case KEYPRESS(0, 0, 'h'):
1087                 case KEYPRESS(0, 0, '?'):
1088                         status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
1089                         break;
1090
1091                 case KEYPRESS(0, 0, 'Q'):
1092                         exit = TRUE;
1093                         run = FALSE;
1094                         break;
1095
1096                 case KEYPRESS(0, 0, 'd'):
1097                         if (config->idx_default_efivar != (INTN)idx_highlight) {
1098                                 /* store the selected entry in a persistent EFI variable */
1099                                 efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
1100                                 config->idx_default_efivar = idx_highlight;
1101                                 status = StrDuplicate(L"Default boot entry selected.");
1102                         } else {
1103                                 /* clear the default entry EFI variable */
1104                                 efivar_set(L"LoaderEntryDefault", NULL, TRUE);
1105                                 config->idx_default_efivar = -1;
1106                                 status = StrDuplicate(L"Default boot entry cleared.");
1107                         }
1108                         refresh = TRUE;
1109                         break;
1110
1111                 case KEYPRESS(0, 0, '-'):
1112                 case KEYPRESS(0, 0, 'T'):
1113                         if (config->timeout_sec_efivar > 0) {
1114                                 config->timeout_sec_efivar--;
1115                                 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
1116                                 if (config->timeout_sec_efivar > 0)
1117                                         status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
1118                                 else
1119                                         status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1120                         } else if (config->timeout_sec_efivar <= 0){
1121                                 config->timeout_sec_efivar = -1;
1122                                 efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
1123                                 if (config->timeout_sec_config > 0)
1124                                         status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
1125                                                            config->timeout_sec_config);
1126                                 else
1127                                         status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1128                         }
1129                         break;
1130
1131                 case KEYPRESS(0, 0, '+'):
1132                 case KEYPRESS(0, 0, 't'):
1133                         if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
1134                                 config->timeout_sec_efivar++;
1135                         config->timeout_sec_efivar++;
1136                         efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
1137                         if (config->timeout_sec_efivar > 0)
1138                                 status = PoolPrint(L"Menu timeout set to %d sec.",
1139                                                    config->timeout_sec_efivar);
1140                         else
1141                                 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
1142                         break;
1143
1144                 case KEYPRESS(0, 0, 'e'):
1145                         /* only the options of configured entries can be edited */
1146                         if (config->entries[idx_highlight]->type == LOADER_UNDEFINED)
1147                                 break;
1148                         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
1149                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
1150                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
1151                         if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
1152                                 exit = TRUE;
1153                         uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
1154                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
1155                         break;
1156
1157                 case KEYPRESS(0, 0, 'v'):
1158                         status = PoolPrint(L"gummiboot " VERSION ", UEFI %d.%02d, %s %d.%02d",
1159                                            ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff,
1160                                            ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
1161                         break;
1162
1163                 case KEYPRESS(0, 0, 'P'):
1164                         print_status(config, loaded_image_path);
1165                         refresh = TRUE;
1166                         break;
1167
1168                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
1169                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
1170                         refresh = TRUE;
1171                         break;
1172
1173                 default:
1174                         /* jump with a hotkey directly to a matching entry */
1175                         idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
1176                         if (idx < 0)
1177                                 break;
1178                         idx_highlight = idx;
1179                         refresh = TRUE;
1180                 }
1181
1182                 if (idx_highlight > idx_last) {
1183                         idx_last = idx_highlight;
1184                         idx_first = 1 + idx_highlight - visible_max;
1185                         refresh = TRUE;
1186                 }
1187                 if (idx_highlight < idx_first) {
1188                         idx_first = idx_highlight;
1189                         idx_last = idx_highlight + visible_max-1;
1190                         refresh = TRUE;
1191                 }
1192
1193                 idx_last = idx_first + visible_max-1;
1194
1195                 if (!refresh && idx_highlight != idx_highlight_prev)
1196                         highlight = TRUE;
1197         }
1198
1199         *chosen_entry = config->entries[idx_highlight];
1200
1201         for (i = 0; i < config->entry_count; i++)
1202                 FreePool(lines[i]);
1203         FreePool(lines);
1204         FreePool(clearline);
1205
1206         uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
1207         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
1208         return run;
1209 }
1210
1211 static VOID config_add_entry(Config *config, ConfigEntry *entry) {
1212         if ((config->entry_count & 15) == 0) {
1213                 UINTN i;
1214
1215                 i = config->entry_count + 16;
1216                 if (config->entry_count == 0)
1217                         config->entries = AllocatePool(sizeof(VOID *) * i);
1218                 else
1219                         config->entries = ReallocatePool(config->entries,
1220                                                          sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
1221         }
1222         config->entries[config->entry_count++] = entry;
1223 }
1224
1225 static VOID config_entry_free(ConfigEntry *entry) {
1226         FreePool(entry->title_show);
1227         FreePool(entry->title);
1228         FreePool(entry->machine_id);
1229         FreePool(entry->loader);
1230         FreePool(entry->options);
1231 }
1232
1233 static BOOLEAN is_digit(CHAR16 c)
1234 {
1235         return (c >= '0') && (c <= '9');
1236 }
1237
1238 static UINTN c_order(CHAR16 c)
1239 {
1240         if (c == '\0')
1241                 return 0;
1242         if (is_digit(c))
1243                 return 0;
1244         else if ((c >= 'a') && (c <= 'z'))
1245                 return c;
1246         else
1247                 return c + 0x10000;
1248 }
1249
1250 static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
1251 {
1252         CHAR16 *os1 = s1;
1253         CHAR16 *os2 = s2;
1254
1255         while (*s1 || *s2) {
1256                 INTN first;
1257
1258                 while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
1259                         INTN order;
1260
1261                         order = c_order(*s1) - c_order(*s2);
1262                         if (order)
1263                                 return order;
1264                         s1++;
1265                         s2++;
1266                 }
1267
1268                 while (*s1 == '0')
1269                         s1++;
1270                 while (*s2 == '0')
1271                         s2++;
1272
1273                 first = 0;
1274                 while (is_digit(*s1) && is_digit(*s2)) {
1275                         if (first == 0)
1276                                 first = *s1 - *s2;
1277                         s1++;
1278                         s2++;
1279                 }
1280
1281                 if (is_digit(*s1))
1282                         return 1;
1283                 if (is_digit(*s2))
1284                         return -1;
1285
1286                 if (first)
1287                         return first;
1288         }
1289
1290         return StrCmp(os1, os2);
1291 }
1292
1293 static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
1294         CHAR16 unichar;
1295         UINTN len;
1296         UINTN i;
1297
1298         if (stra[0] < 0x80)
1299                 len = 1;
1300         else if ((stra[0] & 0xe0) == 0xc0)
1301                 len = 2;
1302         else if ((stra[0] & 0xf0) == 0xe0)
1303                 len = 3;
1304         else if ((stra[0] & 0xf8) == 0xf0)
1305                 len = 4;
1306         else if ((stra[0] & 0xfc) == 0xf8)
1307                 len = 5;
1308         else if ((stra[0] & 0xfe) == 0xfc)
1309                 len = 6;
1310         else
1311                 return -1;
1312
1313         switch (len) {
1314         case 1:
1315                 unichar = stra[0];
1316                 break;
1317         case 2:
1318                 unichar = stra[0] & 0x1f;
1319                 break;
1320         case 3:
1321                 unichar = stra[0] & 0x0f;
1322                 break;
1323         case 4:
1324                 unichar = stra[0] & 0x07;
1325                 break;
1326         case 5:
1327                 unichar = stra[0] & 0x03;
1328                 break;
1329         case 6:
1330                 unichar = stra[0] & 0x01;
1331                 break;
1332         }
1333
1334         for (i = 1; i < len; i++) {
1335                 if ((stra[i] & 0xc0) != 0x80)
1336                         return -1;
1337                 unichar <<= 6;
1338                 unichar |= stra[i] & 0x3f;
1339         }
1340
1341         *c = unichar;
1342         return len;
1343 }
1344
1345 static CHAR16 *stra_to_str(CHAR8 *stra) {
1346         UINTN strlen;
1347         UINTN len;
1348         UINTN i;
1349         CHAR16 *str;
1350
1351         len = strlena(stra);
1352         str = AllocatePool((len + 1) * sizeof(CHAR16));
1353
1354         strlen = 0;
1355         i = 0;
1356         while (i < len) {
1357                 INTN utf8len;
1358
1359                 utf8len = utf8_to_16(stra + i, str + strlen);
1360                 if (utf8len <= 0) {
1361                         /* invalid utf8 sequence, skip the garbage */
1362                         i++;
1363                         continue;
1364                 }
1365
1366                 strlen++;
1367                 i += utf8len;
1368         }
1369         str[strlen] = '\0';
1370         return str;
1371 }
1372
1373 static CHAR16 *stra_to_path(CHAR8 *stra) {
1374         CHAR16 *str;
1375         UINTN strlen;
1376         UINTN len;
1377         UINTN i;
1378
1379         len = strlena(stra);
1380         str = AllocatePool((len + 2) * sizeof(CHAR16));
1381
1382         str[0] = '\\';
1383         strlen = 1;
1384         i = 0;
1385         while (i < len) {
1386                 INTN utf8len;
1387
1388                 utf8len = utf8_to_16(stra + i, str + strlen);
1389                 if (utf8len <= 0) {
1390                         /* invalid utf8 sequence, skip the garbage */
1391                         i++;
1392                         continue;
1393                 }
1394
1395                 if (str[strlen] == '/')
1396                         str[strlen] = '\\';
1397                 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
1398                         /* skip double slashes */
1399                         i += utf8len;
1400                         continue;
1401                 }
1402
1403                 strlen++;
1404                 i += utf8len;
1405         }
1406         str[strlen] = '\0';
1407         return str;
1408 }
1409
1410 static CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
1411         do {
1412                 if (*s == c)
1413                         return s;
1414         } while (*s++);
1415         return NULL;
1416 }
1417
1418 static CHAR8 *line_get_key_value(CHAR8 *content, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
1419         CHAR8 *line;
1420         UINTN linelen;
1421         CHAR8 *value;
1422
1423 skip:
1424         line = content + *pos;
1425         if (*line == '\0')
1426                 return NULL;
1427
1428         linelen = 0;
1429         while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
1430                linelen++;
1431
1432         /* move pos to next line */
1433         *pos += linelen;
1434         if (content[*pos])
1435                 (*pos)++;
1436
1437         /* empty line */
1438         if (linelen == 0)
1439                 goto skip;
1440
1441         /* terminate line */
1442         line[linelen] = '\0';
1443
1444         /* remove leading whitespace */
1445         while (strchra((CHAR8 *)" \t", *line)) {
1446                 line++;
1447                 linelen--;
1448         }
1449
1450         /* remove trailing whitespace */
1451         while (linelen > 0 && strchra((CHAR8 *)" \t", line[linelen-1]))
1452                 linelen--;
1453         line[linelen] = '\0';
1454
1455         if (*line == '#')
1456                 goto skip;
1457
1458         /* split key/value */
1459         value = line;
1460         while (*value && !strchra((CHAR8 *)" \t", *value))
1461                 value++;
1462         if (*value == '\0')
1463                 goto skip;
1464         *value = '\0';
1465         value++;
1466         while (*value && strchra((CHAR8 *)" \t", *value))
1467                 value++;
1468
1469         *key_ret = line;
1470         *value_ret = value;
1471         return line;
1472 }
1473
1474 static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
1475         CHAR8 *line;
1476         UINTN pos = 0;
1477         CHAR8 *key, *value;
1478
1479         line = content;
1480         while ((line = line_get_key_value(content, &pos, &key, &value))) {
1481                 if (strcmpa((CHAR8 *)"timeout", key) == 0) {
1482                         CHAR16 *s;
1483
1484                         s = stra_to_str(value);
1485                         config->timeout_sec_config = Atoi(s);
1486                         config->timeout_sec = config->timeout_sec_config;
1487                         FreePool(s);
1488                         continue;
1489                 }
1490                 if (strcmpa((CHAR8 *)"default", key) == 0) {
1491                         config->entry_default_pattern = stra_to_str(value);
1492                         StrLwr(config->entry_default_pattern);
1493                         continue;
1494                 }
1495         }
1496 }
1497
1498 static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
1499         ConfigEntry *entry;
1500         CHAR8 *line;
1501         UINTN pos = 0;
1502         CHAR8 *key, *value;
1503         UINTN len;
1504         CHAR16 *initrd = NULL;
1505
1506         entry = AllocateZeroPool(sizeof(ConfigEntry));
1507
1508         line = content;
1509         while ((line = line_get_key_value(content, &pos, &key, &value))) {
1510                 if (strcmpa((CHAR8 *)"title", key) == 0) {
1511                         FreePool(entry->title);
1512                         entry->title = stra_to_str(value);
1513                         continue;
1514                 }
1515
1516                 if (strcmpa((CHAR8 *)"version", key) == 0) {
1517                         FreePool(entry->version);
1518                         entry->version = stra_to_str(value);
1519                         continue;
1520                 }
1521
1522                 if (strcmpa((CHAR8 *)"machine-id", key) == 0) {
1523                         FreePool(entry->machine_id);
1524                         entry->machine_id = stra_to_str(value);
1525                         continue;
1526                 }
1527
1528                 if (strcmpa((CHAR8 *)"linux", key) == 0) {
1529                         FreePool(entry->loader);
1530                         entry->type = LOADER_LINUX;
1531                         entry->loader = stra_to_path(value);
1532                         entry->key = 'l';
1533                         continue;
1534                 }
1535
1536                 if (strcmpa((CHAR8 *)"efi", key) == 0) {
1537                         entry->type = LOADER_EFI;
1538                         FreePool(entry->loader);
1539                         entry->loader = stra_to_path(value);
1540
1541                         /* do not add an entry for ourselves */
1542                         if (StriCmp(entry->loader, loaded_image_path) == 0) {
1543                                 entry->type = LOADER_UNDEFINED;
1544                                 break;
1545                         }
1546                         continue;
1547                 }
1548
1549                 if (strcmpa((CHAR8 *)"initrd", key) == 0) {
1550                         CHAR16 *new;
1551
1552                         new = stra_to_path(value);
1553                         if (initrd) {
1554                                 CHAR16 *s;
1555
1556                                 s = PoolPrint(L"%s initrd=%s", initrd, new);
1557                                 FreePool(initrd);
1558                                 initrd = s;
1559                         } else
1560                                 initrd = PoolPrint(L"initrd=%s", new);
1561                         FreePool(new);
1562                         continue;
1563                 }
1564
1565                 if (strcmpa((CHAR8 *)"options", key) == 0) {
1566                         CHAR16 *new;
1567
1568                         new = stra_to_str(value);
1569                         if (entry->options) {
1570                                 CHAR16 *s;
1571
1572                                 s = PoolPrint(L"%s %s", entry->options, new);
1573                                 FreePool(entry->options);
1574                                 entry->options = s;
1575                         } else {
1576                                 entry->options = new;
1577                                 new = NULL;
1578                         }
1579                         FreePool(new);
1580                         continue;
1581                 }
1582         }
1583
1584         if (entry->type == LOADER_UNDEFINED) {
1585                 config_entry_free(entry);
1586                 FreePool(initrd);
1587                 FreePool(entry);
1588                 return;
1589         }
1590
1591         /* add initrd= to options */
1592         if (entry->type == LOADER_LINUX && initrd) {
1593                 if (entry->options) {
1594                         CHAR16 *s;
1595
1596                         s = PoolPrint(L"%s %s", initrd, entry->options);
1597                         FreePool(entry->options);
1598                         entry->options = s;
1599                 } else {
1600                         entry->options = initrd;
1601                         initrd = NULL;
1602                 }
1603         }
1604         FreePool(initrd);
1605
1606         if (entry->machine_id) {
1607                 CHAR16 *var;
1608
1609                 /* append additional options from EFI variables for this machine-id */
1610                 var = PoolPrint(L"LoaderEntryOptions-%s", entry->machine_id);
1611                 if (var) {
1612                         CHAR16 *s;
1613
1614                         if (efivar_get(var, &s) == EFI_SUCCESS) {
1615                                 if (entry->options) {
1616                                         CHAR16 *s2;
1617
1618                                         s2 = PoolPrint(L"%s %s", entry->options, s);
1619                                         FreePool(entry->options);
1620                                         entry->options = s2;
1621                                 } else
1622                                         entry->options = s;
1623                         }
1624                         FreePool(var);
1625                 }
1626
1627                 var = PoolPrint(L"LoaderEntryOptionsOneShot-%s", entry->machine_id);
1628                 if (var) {
1629                         CHAR16 *s;
1630
1631                         if (efivar_get(var, &s) == EFI_SUCCESS) {
1632                                 if (entry->options) {
1633                                         CHAR16 *s2;
1634
1635                                         s2 = PoolPrint(L"%s %s", entry->options, s);
1636                                         FreePool(entry->options);
1637                                         entry->options = s2;
1638                                 } else
1639                                         entry->options = s;
1640                                 efivar_set(var, NULL, TRUE);
1641                         }
1642                         FreePool(var);
1643                 }
1644         }
1645
1646         entry->device = device;
1647         entry->file = StrDuplicate(file);
1648         len = StrLen(entry->file);
1649         /* remove ".conf" */
1650         if (len > 5)
1651                 entry->file[len - 5] = '\0';
1652         StrLwr(entry->file);
1653
1654         config_add_entry(config, entry);
1655 }
1656
1657 static UINTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, CHAR8 **content) {
1658         EFI_FILE_HANDLE handle;
1659         EFI_FILE_INFO *info;
1660         CHAR8 *buf;
1661         UINTN buflen;
1662         EFI_STATUS err;
1663         UINTN len = 0;
1664
1665         err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
1666         if (EFI_ERROR(err))
1667                 goto out;
1668
1669         info = LibFileInfo(handle);
1670         buflen = info->FileSize+1;
1671         buf = AllocatePool(buflen);
1672
1673         err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
1674         if (!EFI_ERROR(err)) {
1675                 buf[buflen] = '\0';
1676                 *content = buf;
1677                 len = buflen;
1678         } else
1679                 FreePool(buf);
1680
1681         FreePool(info);
1682         uefi_call_wrapper(handle->Close, 1, handle);
1683 out:
1684         return len;
1685 }
1686
1687 static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
1688         EFI_FILE_HANDLE entries_dir;
1689         EFI_STATUS err;
1690         CHAR8 *content = NULL;
1691         UINTN sec;
1692         UINTN len;
1693         UINTN i;
1694
1695         len = file_read(root_dir, L"\\loader\\loader.conf", &content);
1696         if (len > 0)
1697                 config_defaults_load_from_file(config, content);
1698         FreePool(content);
1699
1700         err = efivar_get_int(L"LoaderConfigTimeout", &sec);
1701         if (!EFI_ERROR(err)) {
1702                 config->timeout_sec_efivar = sec;
1703                 config->timeout_sec = sec;
1704         } else
1705                 config->timeout_sec_efivar = -1;
1706
1707         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
1708         if (!EFI_ERROR(err)) {
1709                 for (;;) {
1710                         CHAR16 buf[256];
1711                         UINTN bufsize;
1712                         EFI_FILE_INFO *f;
1713                         CHAR8 *content = NULL;
1714                         UINTN len;
1715
1716                         bufsize = sizeof(buf);
1717                         err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
1718                         if (bufsize == 0 || EFI_ERROR(err))
1719                                 break;
1720
1721                         f = (EFI_FILE_INFO *) buf;
1722                         if (f->FileName[0] == '.')
1723                                 continue;
1724                         if (f->Attribute & EFI_FILE_DIRECTORY)
1725                                 continue;
1726                         len = StrLen(f->FileName);
1727                         if (len < 6)
1728                                 continue;
1729                         if (StriCmp(f->FileName + len - 5, L".conf") != 0)
1730                                 continue;
1731
1732                         len = file_read(entries_dir, f->FileName, &content);
1733                         if (len > 0)
1734                                 config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path);
1735                         FreePool(content);
1736                 }
1737                 uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
1738         }
1739
1740         /* sort entries after version number */
1741         for (i = 1; i < config->entry_count; i++) {
1742                 BOOLEAN more;
1743                 UINTN k;
1744
1745                 more = FALSE;
1746                 for (k = 0; k < config->entry_count - i; k++) {
1747                         ConfigEntry *entry;
1748
1749                         if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0)
1750                                 continue;
1751                         entry = config->entries[k];
1752                         config->entries[k] = config->entries[k+1];
1753                         config->entries[k+1] = entry;
1754                         more = TRUE;
1755                 }
1756                 if (!more)
1757                         break;
1758         }
1759 }
1760
1761 static VOID config_default_entry_select(Config *config) {
1762         CHAR16 *var;
1763         EFI_STATUS err;
1764         UINTN i;
1765
1766         /*
1767          * The EFI variable to specify a boot entry for the next, and only the
1768          * next reboot. The variable is always cleared directly after it is read.
1769          */
1770         err = efivar_get(L"LoaderEntryOneShot", &var);
1771         if (!EFI_ERROR(err)) {
1772                 BOOLEAN found = FALSE;
1773
1774                 for (i = 0; i < config->entry_count; i++) {
1775                         if (StrCmp(config->entries[i]->file, var) == 0) {
1776                                 config->idx_default = i;
1777                                 found = TRUE;
1778                                 break;
1779                         }
1780                 }
1781
1782                 config->entry_oneshot = StrDuplicate(var);
1783                 efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
1784                 FreePool(var);
1785                 if (found)
1786                         return;
1787         }
1788
1789         /*
1790          * The EFI variable to select the default boot entry overrides the
1791          * configured pattern. The variable can be set and cleared by pressing
1792          * the 'd' key in the loader selection menu, the entry is marked with
1793          * an '*'.
1794          */
1795         err = efivar_get(L"LoaderEntryDefault", &var);
1796         if (!EFI_ERROR(err)) {
1797                 BOOLEAN found = FALSE;
1798
1799                 for (i = 0; i < config->entry_count; i++) {
1800                         if (StrCmp(config->entries[i]->file, var) == 0) {
1801                                 config->idx_default = i;
1802                                 config->idx_default_efivar = i;
1803                                 found = TRUE;
1804                                 break;
1805                         }
1806                 }
1807                 FreePool(var);
1808                 if (found)
1809                         return;
1810         }
1811         config->idx_default_efivar = -1;
1812
1813         if (config->entry_count == 0)
1814                 return;
1815
1816         /*
1817          * Match the pattern from the end of the list to the start, find last
1818          * entry (largest number) matching the given pattern.
1819          */
1820         if (config->entry_default_pattern) {
1821                 i = config->entry_count;
1822                 while (i--) {
1823                         if (config->entries[i]->no_autoselect)
1824                                 continue;
1825                         if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
1826                                 config->idx_default = i;
1827                                 return;
1828                         }
1829                 }
1830         }
1831
1832         /* select the last suitable entry */
1833         i = config->entry_count;
1834         while (i--) {
1835                 if (config->entries[i]->no_autoselect)
1836                         continue;
1837                 config->idx_default = i;
1838                 return;
1839         }
1840
1841         /* no entry found */
1842         config->idx_default = -1;
1843 }
1844
1845 /* generate a unique title, avoiding non-distinguishable menu entries */
1846 static VOID config_title_generate(Config *config) {
1847         UINTN i, k;
1848         BOOLEAN unique;
1849
1850         /* set title */
1851         for (i = 0; i < config->entry_count; i++) {
1852                 CHAR16 *title;
1853
1854                 FreePool(config->entries[i]->title_show);
1855                 title = config->entries[i]->title;
1856                 if (!title)
1857                         title = config->entries[i]->file;
1858                 config->entries[i]->title_show = StrDuplicate(title);
1859         }
1860
1861         unique = TRUE;
1862         for (i = 0; i < config->entry_count; i++) {
1863                 for (k = 0; k < config->entry_count; k++) {
1864                         if (i == k)
1865                                 continue;
1866                         if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1867                                 continue;
1868
1869                         unique = FALSE;
1870                         config->entries[i]->non_unique = TRUE;
1871                         config->entries[k]->non_unique = TRUE;
1872                 }
1873         }
1874         if (unique)
1875                 return;
1876
1877         /* add version to non-unique titles */
1878         for (i = 0; i < config->entry_count; i++) {
1879                 CHAR16 *s;
1880
1881                 if (!config->entries[i]->non_unique)
1882                         continue;
1883                 if (!config->entries[i]->version)
1884                         continue;
1885
1886                 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
1887                 FreePool(config->entries[i]->title_show);
1888                 config->entries[i]->title_show = s;
1889                 config->entries[i]->non_unique = FALSE;
1890         }
1891
1892         unique = TRUE;
1893         for (i = 0; i < config->entry_count; i++) {
1894                 for (k = 0; k < config->entry_count; k++) {
1895                         if (i == k)
1896                                 continue;
1897                         if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1898                                 continue;
1899
1900                         unique = FALSE;
1901                         config->entries[i]->non_unique = TRUE;
1902                         config->entries[k]->non_unique = TRUE;
1903                 }
1904         }
1905         if (unique)
1906                 return;
1907
1908         /* add machine-id to non-unique titles */
1909         for (i = 0; i < config->entry_count; i++) {
1910                 CHAR16 *s;
1911                 CHAR16 *m;
1912
1913                 if (!config->entries[i]->non_unique)
1914                         continue;
1915                 if (!config->entries[i]->machine_id)
1916                         continue;
1917
1918                 m = StrDuplicate(config->entries[i]->machine_id);
1919                 m[8] = '\0';
1920                 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m);
1921                 FreePool(config->entries[i]->title_show);
1922                 config->entries[i]->title_show = s;
1923                 config->entries[i]->non_unique = FALSE;
1924                 FreePool(m);
1925         }
1926
1927         unique = TRUE;
1928         for (i = 0; i < config->entry_count; i++) {
1929                 for (k = 0; k < config->entry_count; k++) {
1930                         if (i == k)
1931                                 continue;
1932                         if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
1933                                 continue;
1934
1935                         unique = FALSE;
1936                         config->entries[i]->non_unique = TRUE;
1937                         config->entries[k]->non_unique = TRUE;
1938                 }
1939         }
1940         if (unique)
1941                 return;
1942
1943         /* add file name to non-unique titles */
1944         for (i = 0; i < config->entry_count; i++) {
1945                 CHAR16 *s;
1946
1947                 if (!config->entries[i]->non_unique)
1948                         continue;
1949                 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file);
1950                 FreePool(config->entries[i]->title_show);
1951                 config->entries[i]->title_show = s;
1952                 config->entries[i]->non_unique = FALSE;
1953         }
1954 }
1955
1956 static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(void)) {
1957         ConfigEntry *entry;
1958
1959         entry = AllocateZeroPool(sizeof(ConfigEntry));
1960         entry->title = StrDuplicate(title);
1961         entry->call = call;
1962         entry->no_autoselect = TRUE;
1963         config_add_entry(config, entry);
1964         return TRUE;
1965 }
1966
1967 static BOOLEAN config_entry_add_loader(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
1968                                        CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
1969         EFI_FILE_HANDLE handle;
1970         EFI_STATUS err;
1971         ConfigEntry *entry;
1972
1973         /* do not add an entry for ourselves */
1974         if (loaded_image_path && StriCmp(loader, loaded_image_path) == 0)
1975                 return FALSE;
1976
1977         /* check existence */
1978         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL);
1979         if (EFI_ERROR(err))
1980                 return FALSE;
1981         uefi_call_wrapper(handle->Close, 1, handle);
1982
1983         entry = AllocateZeroPool(sizeof(ConfigEntry));
1984         entry->title = StrDuplicate(title);
1985         entry->device = device;
1986         entry->loader = StrDuplicate(loader);
1987         entry->file = StrDuplicate(file);
1988         StrLwr(entry->file);
1989         entry->no_autoselect = TRUE;
1990         entry->key = key;
1991         config_add_entry(config, entry);
1992         return TRUE;
1993 }
1994
1995 static VOID config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
1996                                          CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
1997         if (!config_entry_add_loader(config, device, root_dir, loaded_image_path, file, key, title, loader))
1998                 return;
1999
2000         /* export identifiers of automatically added entries */
2001         if (config->entries_auto) {
2002                 CHAR16 *s;
2003
2004                 s = PoolPrint(L"%s %s", config->entries_auto, file);
2005                 FreePool(config->entries_auto);
2006                 config->entries_auto = s;
2007         } else
2008                 config->entries_auto = StrDuplicate(file);
2009 }
2010
2011 static VOID config_entry_add_osx(Config *config) {
2012         EFI_STATUS err;
2013         UINTN handle_count = 0;
2014         EFI_HANDLE *handles = NULL;
2015
2016         err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles);
2017         if (!EFI_ERROR(err)) {
2018                 UINTN i;
2019
2020                 for (i = 0; i < handle_count; i++) {
2021                         EFI_FILE *root;
2022
2023                         root = LibOpenRoot(handles[i]);
2024                         if (!root)
2025                                 continue;
2026                         config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"OS X",
2027                                                      L"\\System\\Library\\CoreServices\\boot.efi");
2028                         uefi_call_wrapper(root->Close, 1, root);
2029                 }
2030
2031                 FreePool(handles);
2032         }
2033 }
2034
2035 static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
2036         EFI_STATUS err;
2037         EFI_HANDLE image;
2038         EFI_DEVICE_PATH *path;
2039         CHAR16 *options;
2040
2041         path = FileDevicePath(entry->device, entry->loader);
2042         if (!path) {
2043                 Print(L"Error getting device path.");
2044                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2045                 return EFI_INVALID_PARAMETER;
2046         }
2047
2048         err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
2049         if (EFI_ERROR(err)) {
2050                 Print(L"Error loading %s: %r", entry->loader, err);
2051                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2052                 goto out;
2053         }
2054
2055         if (config->options_edit)
2056                 options = config->options_edit;
2057         else if (entry->options)
2058                 options = entry->options;
2059         else
2060                 options = NULL;
2061         if (options) {
2062                 EFI_LOADED_IMAGE *loaded_image;
2063
2064                 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (void **)&loaded_image,
2065                                         parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2066                 if (EFI_ERROR(err)) {
2067                         Print(L"Error getting LoadedImageProtocol handle: %r", err);
2068                         uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2069                         goto out_unload;
2070                 }
2071                 loaded_image->LoadOptions = options;
2072                 loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
2073         }
2074
2075         efivar_set_time_usec(L"LoaderTimeExecUSec", 0);
2076         err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
2077 out_unload:
2078         uefi_call_wrapper(BS->UnloadImage, 1, image);
2079 out:
2080         FreePool(path);
2081         return err;
2082 }
2083
2084 static EFI_STATUS reboot_into_firmware(VOID) {
2085         CHAR8 *b;
2086         UINTN size;
2087         UINT64 osind;
2088         EFI_STATUS err;
2089
2090         osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
2091
2092         err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size);
2093         if (!EFI_ERROR(err))
2094                 osind |= (UINT64)*b;
2095         FreePool(b);
2096
2097         err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
2098         if (EFI_ERROR(err))
2099                 return err;
2100
2101         err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
2102         Print(L"Error calling ResetSystem: %r", err);
2103         uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2104         return err;
2105 }
2106
2107 static VOID config_free(Config *config) {
2108         UINTN i;
2109
2110         for (i = 0; i < config->entry_count; i++)
2111                 config_entry_free(config->entries[i]);
2112         FreePool(config->entries);
2113         FreePool(config->entry_default_pattern);
2114         FreePool(config->options_edit);
2115         FreePool(config->entry_oneshot);
2116         FreePool(config->entries_auto);
2117 }
2118
2119 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
2120         CHAR16 *s;
2121         CHAR8 *b;
2122         UINTN size;
2123         EFI_LOADED_IMAGE *loaded_image;
2124         EFI_FILE *root_dir;
2125         CHAR16 *loaded_image_path;
2126         EFI_DEVICE_PATH *device_path;
2127         EFI_STATUS err;
2128         Config config;
2129         UINT64 init_usec;
2130         BOOLEAN menu = FALSE;
2131
2132         InitializeLib(image, sys_table);
2133         init_usec = time_usec();
2134         efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec);
2135         efivar_set(L"LoaderInfo", L"gummiboot " VERSION, FALSE);
2136         s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
2137         efivar_set(L"LoaderFirmwareInfo", s, FALSE);
2138         FreePool(s);
2139         s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
2140         efivar_set(L"LoaderFirmwareType", s, FALSE);
2141         FreePool(s);
2142
2143         err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (void **)&loaded_image,
2144                                 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2145         if (EFI_ERROR(err)) {
2146                 Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
2147                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2148                 return err;
2149         }
2150
2151         /* export the device path this image is started from */
2152         device_path = DevicePathFromHandle(loaded_image->DeviceHandle);
2153         if (device_path) {
2154                 CHAR16 *str;
2155                 EFI_DEVICE_PATH *path, *paths;
2156
2157                 str = DevicePathToStr(device_path);
2158                 efivar_set(L"LoaderDeviceIdentifier", str, FALSE);
2159                 FreePool(str);
2160
2161                 paths = UnpackDevicePath(device_path);
2162                 for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
2163                         HARDDRIVE_DEVICE_PATH *drive;
2164                         CHAR16 uuid[37];
2165
2166                         if (DevicePathType(path) != MEDIA_DEVICE_PATH)
2167                                 continue;
2168                         if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
2169                                 continue;
2170                         drive = (HARDDRIVE_DEVICE_PATH *)path;
2171                         if (drive->SignatureType != SIGNATURE_TYPE_GUID)
2172                                 continue;
2173
2174                         GuidToString(uuid, (EFI_GUID *)&drive->Signature);
2175                         efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
2176                         break;
2177                 }
2178                 FreePool(paths);
2179         }
2180
2181         root_dir = LibOpenRoot(loaded_image->DeviceHandle);
2182         if (!root_dir) {
2183                 Print(L"Unable to open root directory: %r ", err);
2184                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2185                 return EFI_LOAD_ERROR;
2186         }
2187
2188         /* the filesystem path to this image, to prevent adding ourselves to the menu */
2189         loaded_image_path = DevicePathToStr(loaded_image->FilePath);
2190         efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
2191
2192         /* scan "\loader\entries\*.conf" files */
2193         ZeroMem(&config, sizeof(Config));
2194         config_load(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
2195
2196         /* if we find some well-known loaders, add them to the end of the list */
2197         config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2198                                      L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2199         config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2200                                      L"auto-efi-shell", 's', L"EFI Shell", L"\\shellx64.efi");
2201         config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2202                                      L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\BOOT\\BOOTX64.EFI");
2203         config_entry_add_osx(&config);
2204         efivar_set(L"LoaderEntriesAuto", config.entries_auto, FALSE);
2205
2206         if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
2207                 UINT64 osind = (UINT64)*b;
2208
2209                 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
2210                         config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware);
2211                 FreePool(b);
2212         }
2213
2214         if (config.entry_count == 0) {
2215                 Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2216                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2217                 goto out;
2218         }
2219
2220         config_title_generate(&config);
2221
2222         /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
2223         config_default_entry_select(&config);
2224
2225         /* if no configured entry to select from was found, enable the menu */
2226         if (config.idx_default == -1) {
2227                 config.idx_default = 0;
2228                 if (config.timeout_sec == 0)
2229                         config.timeout_sec = 10;
2230         }
2231
2232         /* select entry or show menu when key is pressed or timeout is set */
2233         if (config.timeout_sec == 0) {
2234                 UINT64 key;
2235
2236                 err = key_read(&key, FALSE);
2237                 if (!EFI_ERROR(err)) {
2238                         INT16 idx;
2239
2240                         /* find matching key in config entries */
2241                         idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key));
2242                         if (idx >= 0)
2243                                 config.idx_default = idx;
2244                         else
2245                                 menu = TRUE;
2246                 }
2247         } else
2248                 menu = TRUE;
2249
2250         for (;;) {
2251                 ConfigEntry *entry;
2252
2253                 entry = config.entries[config.idx_default];
2254                 if (menu) {
2255                         efivar_set_time_usec(L"LoaderTimeMenuUSec", 0);
2256                         uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL);
2257                         if (!menu_run(&config, &entry, loaded_image_path))
2258                                 break;
2259
2260                         /* run special entry like "reboot" */
2261                         if (entry->call) {
2262                                 entry->call();
2263                                 continue;
2264                         }
2265                 }
2266
2267                 /* export the selected boot entry to the system */
2268                 efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
2269
2270                 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
2271                 err = image_start(image, &config, entry);
2272
2273                 if (err == EFI_ACCESS_DENIED || err == EFI_SECURITY_VIOLATION) {
2274                         /* Platform is secure boot and requested image isn't
2275                          * trusted. Need to go back to prior boot system and
2276                          * install more keys or hashes. Signal failure by
2277                          * returning the error */
2278                         Print(L"\nImage %s gives a security error\n", entry->title);
2279                         Print(L"Please enrol the hash or signature of %s\n", entry->loader);
2280                         uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2281                         goto out;
2282                 }
2283
2284                 menu = TRUE;
2285                 config.timeout_sec = 0;
2286         }
2287         err = EFI_SUCCESS;
2288 out:
2289         FreePool(loaded_image_path);
2290         config_free(&config);
2291         uefi_call_wrapper(root_dir->Close, 1, root_dir);
2292         uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
2293         return err;
2294 }