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