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