]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/boot.c
Merge pull request #23653 from aafeijoo-suse/ask-for-recovery-key
[thirdparty/systemd.git] / src / boot / efi / boot.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <efi.h>
4 #include <efigpt.h>
5 #include <efilib.h>
6
7 #include "bcd.h"
8 #include "bootspec-fundamental.h"
9 #include "console.h"
10 #include "devicetree.h"
11 #include "disk.h"
12 #include "drivers.h"
13 #include "efivars-fundamental.h"
14 #include "graphics.h"
15 #include "initrd.h"
16 #include "linux.h"
17 #include "measure.h"
18 #include "pe.h"
19 #include "random-seed.h"
20 #include "secure-boot.h"
21 #include "shim.h"
22 #include "ticks.h"
23 #include "util.h"
24 #include "xbootldr.h"
25
26 #ifndef GNU_EFI_USE_MS_ABI
27 /* We do not use uefi_call_wrapper() in systemd-boot. As such, we rely on the
28 * compiler to do the calling convention conversion for us. This is check is
29 * to make sure the -DGNU_EFI_USE_MS_ABI was passed to the comiler. */
30 #error systemd-boot requires compilation with GNU_EFI_USE_MS_ABI defined.
31 #endif
32
33 #define TEXT_ATTR_SWAP(c) EFI_TEXT_ATTR(((c) & 0b11110000) >> 4, (c) & 0b1111)
34
35 /* Magic string for recognizing our own binaries */
36 _used_ _section_(".sdmagic") static const char magic[] =
37 "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
38
39 /* Makes systemd-boot available from \EFI\Linux\ for testing purposes. */
40 _used_ _section_(".osrel") static const char osrel[] =
41 "ID=systemd-boot\n"
42 "VERSION=\"" GIT_VERSION "\"\n"
43 "NAME=\"systemd-boot " GIT_VERSION "\"\n";
44
45 enum loader_type {
46 LOADER_UNDEFINED,
47 LOADER_AUTO,
48 LOADER_EFI,
49 LOADER_LINUX, /* Boot loader spec type #1 entries */
50 LOADER_UNIFIED_LINUX, /* Boot loader spec type #2 entries */
51 LOADER_SECURE_BOOT_KEYS,
52 };
53
54 typedef struct {
55 char16_t *id; /* The unique identifier for this entry (typically the filename of the file defining the entry) */
56 char16_t *title_show; /* The string to actually display (this is made unique before showing) */
57 char16_t *title; /* The raw (human readable) title string of the entry (not necessarily unique) */
58 char16_t *sort_key; /* The string to use as primary sort key, usually ID= from os-release, possibly suffixed */
59 char16_t *version; /* The raw (human readable) version string of the entry */
60 char16_t *machine_id;
61 EFI_HANDLE *device;
62 enum loader_type type;
63 char16_t *loader;
64 char16_t *devicetree;
65 char16_t *options;
66 char16_t **initrd;
67 char16_t key;
68 EFI_STATUS (*call)(void);
69 int tries_done;
70 int tries_left;
71 char16_t *path;
72 char16_t *current_name;
73 char16_t *next_name;
74 } ConfigEntry;
75
76 typedef struct {
77 ConfigEntry **entries;
78 UINTN entry_count;
79 UINTN idx_default;
80 UINTN idx_default_efivar;
81 uint32_t timeout_sec; /* Actual timeout used (efi_main() override > efivar > config). */
82 uint32_t timeout_sec_config;
83 uint32_t timeout_sec_efivar;
84 char16_t *entry_default_config;
85 char16_t *entry_default_efivar;
86 char16_t *entry_oneshot;
87 char16_t *entry_saved;
88 bool editor;
89 bool auto_entries;
90 bool auto_firmware;
91 bool reboot_for_bitlocker;
92 secure_boot_enroll secure_boot_enroll;
93 bool force_menu;
94 bool use_saved_entry;
95 bool use_saved_entry_efivar;
96 bool beep;
97 int64_t console_mode;
98 int64_t console_mode_efivar;
99 RandomSeedMode random_seed_mode;
100 } Config;
101
102 /* These values have been chosen so that the transitions the user sees could
103 * employ unsigned over-/underflow like this:
104 * efivar unset ↔ force menu ↔ no timeout/skip menu ↔ 1 s ↔ 2 s ↔ … */
105 enum {
106 TIMEOUT_MIN = 1,
107 TIMEOUT_MAX = UINT32_MAX - 2U,
108 TIMEOUT_UNSET = UINT32_MAX - 1U,
109 TIMEOUT_MENU_FORCE = UINT32_MAX,
110 TIMEOUT_MENU_HIDDEN = 0,
111 TIMEOUT_TYPE_MAX = UINT32_MAX,
112 };
113
114 enum {
115 IDX_MAX = INT16_MAX,
116 IDX_INVALID,
117 };
118
119 static void cursor_left(UINTN *cursor, UINTN *first) {
120 assert(cursor);
121 assert(first);
122
123 if ((*cursor) > 0)
124 (*cursor)--;
125 else if ((*first) > 0)
126 (*first)--;
127 }
128
129 static void cursor_right(
130 UINTN *cursor,
131 UINTN *first,
132 UINTN x_max,
133 UINTN len) {
134
135 assert(cursor);
136 assert(first);
137
138 if ((*cursor)+1 < x_max)
139 (*cursor)++;
140 else if ((*first) + (*cursor) < len)
141 (*first)++;
142 }
143
144 static bool line_edit(
145 char16_t **line_in,
146 UINTN x_max,
147 UINTN y_pos) {
148
149 _cleanup_free_ char16_t *line = NULL, *print = NULL;
150 UINTN size, len, first = 0, cursor = 0, clear = 0;
151
152 assert(line_in);
153
154 len = strlen16(*line_in);
155 size = len + 1024;
156 line = xnew(char16_t, size);
157 print = xnew(char16_t, x_max + 1);
158 strcpy16(line, strempty(*line_in));
159
160 for (;;) {
161 EFI_STATUS err;
162 uint64_t key;
163 UINTN j;
164 UINTN cursor_color = TEXT_ATTR_SWAP(COLOR_EDIT);
165
166 j = MIN(len - first, x_max);
167 memcpy(print, line + first, j * sizeof(char16_t));
168 while (clear > 0 && j < x_max) {
169 clear--;
170 print[j++] = ' ';
171 }
172 print[j] = '\0';
173
174 /* See comment at edit_line() call site for why we start at 1. */
175 print_at(1, y_pos, COLOR_EDIT, print);
176
177 if (!print[cursor])
178 print[cursor] = ' ';
179 print[cursor+1] = '\0';
180 do {
181 print_at(cursor + 1, y_pos, cursor_color, print + cursor);
182 cursor_color = TEXT_ATTR_SWAP(cursor_color);
183
184 err = console_key_read(&key, 750 * 1000);
185 if (!IN_SET(err, EFI_SUCCESS, EFI_TIMEOUT, EFI_NOT_READY))
186 return false;
187
188 print_at(cursor + 1, y_pos, COLOR_EDIT, print + cursor);
189 } while (err != EFI_SUCCESS);
190
191 switch (key) {
192 case KEYPRESS(0, SCAN_ESC, 0):
193 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
194 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
195 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
196 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
197 return false;
198
199 case KEYPRESS(0, SCAN_HOME, 0):
200 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
201 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
202 /* beginning-of-line */
203 cursor = 0;
204 first = 0;
205 continue;
206
207 case KEYPRESS(0, SCAN_END, 0):
208 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
209 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
210 /* end-of-line */
211 cursor = len - first;
212 if (cursor+1 >= x_max) {
213 cursor = x_max-1;
214 first = len - (x_max-1);
215 }
216 continue;
217
218 case KEYPRESS(0, SCAN_DOWN, 0):
219 case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
220 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
221 /* forward-word */
222 while (line[first + cursor] == ' ')
223 cursor_right(&cursor, &first, x_max, len);
224 while (line[first + cursor] && line[first + cursor] != ' ')
225 cursor_right(&cursor, &first, x_max, len);
226 continue;
227
228 case KEYPRESS(0, SCAN_UP, 0):
229 case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
230 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
231 /* backward-word */
232 if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
233 cursor_left(&cursor, &first);
234 while ((first + cursor) > 0 && line[first + cursor] == ' ')
235 cursor_left(&cursor, &first);
236 }
237 while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
238 cursor_left(&cursor, &first);
239 continue;
240
241 case KEYPRESS(0, SCAN_RIGHT, 0):
242 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
243 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
244 /* forward-char */
245 if (first + cursor == len)
246 continue;
247 cursor_right(&cursor, &first, x_max, len);
248 continue;
249
250 case KEYPRESS(0, SCAN_LEFT, 0):
251 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
252 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
253 /* backward-char */
254 cursor_left(&cursor, &first);
255 continue;
256
257 case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
258 /* kill-word */
259 clear = 0;
260
261 UINTN k;
262 for (k = first + cursor; k < len && line[k] == ' '; k++)
263 clear++;
264 for (; k < len && line[k] != ' '; k++)
265 clear++;
266
267 for (UINTN i = first + cursor; i + clear < len; i++)
268 line[i] = line[i + clear];
269 len -= clear;
270 line[len] = '\0';
271 continue;
272
273 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'):
274 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')):
275 case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE):
276 /* backward-kill-word */
277 clear = 0;
278 if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
279 cursor_left(&cursor, &first);
280 clear++;
281 while ((first + cursor) > 0 && line[first + cursor] == ' ') {
282 cursor_left(&cursor, &first);
283 clear++;
284 }
285 }
286 while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
287 cursor_left(&cursor, &first);
288 clear++;
289 }
290
291 for (UINTN i = first + cursor; i + clear < len; i++)
292 line[i] = line[i + clear];
293 len -= clear;
294 line[len] = '\0';
295 continue;
296
297 case KEYPRESS(0, SCAN_DELETE, 0):
298 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
299 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
300 if (len == 0)
301 continue;
302 if (first + cursor == len)
303 continue;
304 for (UINTN i = first + cursor; i < len; i++)
305 line[i] = line[i+1];
306 clear = 1;
307 len--;
308 continue;
309
310 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
311 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
312 /* kill-line */
313 line[first + cursor] = '\0';
314 clear = len - (first + cursor);
315 len = first + cursor;
316 continue;
317
318 case KEYPRESS(0, 0, CHAR_LINEFEED):
319 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
320 case KEYPRESS(0, CHAR_CARRIAGE_RETURN, 0): /* EZpad Mini 4s firmware sends malformed events */
321 case KEYPRESS(0, CHAR_CARRIAGE_RETURN, CHAR_CARRIAGE_RETURN): /* Teclast X98+ II firmware sends malformed events */
322 if (!streq16(line, *line_in)) {
323 free(*line_in);
324 *line_in = TAKE_PTR(line);
325 }
326 return true;
327
328 case KEYPRESS(0, 0, CHAR_BACKSPACE):
329 if (len == 0)
330 continue;
331 if (first == 0 && cursor == 0)
332 continue;
333 for (UINTN i = first + cursor-1; i < len; i++)
334 line[i] = line[i+1];
335 clear = 1;
336 len--;
337 if (cursor > 0)
338 cursor--;
339 if (cursor > 0 || first == 0)
340 continue;
341 /* show full line if it fits */
342 if (len < x_max) {
343 cursor = first;
344 first = 0;
345 continue;
346 }
347 /* jump left to see what we delete */
348 if (first > 10) {
349 first -= 10;
350 cursor = 10;
351 } else {
352 cursor = first;
353 first = 0;
354 }
355 continue;
356
357 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
358 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
359 if (len+1 == size)
360 continue;
361 for (UINTN i = len; i > first + cursor; i--)
362 line[i] = line[i-1];
363 line[first + cursor] = KEYCHAR(key);
364 len++;
365 line[len] = '\0';
366 if (cursor+1 < x_max)
367 cursor++;
368 else if (first + cursor < len)
369 first++;
370 continue;
371 }
372 }
373 }
374
375 static UINTN entry_lookup_key(Config *config, UINTN start, char16_t key) {
376 assert(config);
377
378 if (key == 0)
379 return IDX_INVALID;
380
381 /* select entry by number key */
382 if (key >= '1' && key <= '9') {
383 UINTN i = key - '0';
384 if (i > config->entry_count)
385 i = config->entry_count;
386 return i-1;
387 }
388
389 /* find matching key in config entries */
390 for (UINTN i = start; i < config->entry_count; i++)
391 if (config->entries[i]->key == key)
392 return i;
393
394 for (UINTN i = 0; i < start; i++)
395 if (config->entries[i]->key == key)
396 return i;
397
398 return IDX_INVALID;
399 }
400
401 static char16_t *update_timeout_efivar(uint32_t *t, bool inc) {
402 assert(t);
403
404 switch (*t) {
405 case TIMEOUT_MAX:
406 *t = inc ? TIMEOUT_MAX : (*t - 1);
407 break;
408 case TIMEOUT_UNSET:
409 *t = inc ? TIMEOUT_MENU_FORCE : TIMEOUT_UNSET;
410 break;
411 case TIMEOUT_MENU_FORCE:
412 *t = inc ? TIMEOUT_MENU_HIDDEN : TIMEOUT_UNSET;
413 break;
414 case TIMEOUT_MENU_HIDDEN:
415 *t = inc ? TIMEOUT_MIN : TIMEOUT_MENU_FORCE;
416 break;
417 default:
418 *t += inc ? 1 : -1;
419 }
420
421 switch (*t) {
422 case TIMEOUT_UNSET:
423 return xstrdup16(u"Menu timeout defined by configuration file.");
424 case TIMEOUT_MENU_FORCE:
425 return xstrdup16(u"Timeout disabled, menu will always be shown.");
426 case TIMEOUT_MENU_HIDDEN:
427 return xstrdup16(u"Menu disabled. Hold down key at bootup to show menu.");
428 default:
429 return xpool_print(L"Menu timeout set to %u s.", *t);
430 }
431 }
432
433 static bool unicode_supported(void) {
434 static int cache = -1;
435
436 if (cache < 0)
437 /* Basic unicode box drawing support is mandated by the spec, but it does
438 * not hurt to make sure it works. */
439 cache = ST->ConOut->TestString(ST->ConOut, (char16_t *) L"─") == EFI_SUCCESS;
440
441 return cache;
442 }
443
444 static void ps_string(const char16_t *fmt, const void *value) {
445 assert(fmt);
446 if (value)
447 Print(fmt, value);
448 }
449
450 static void ps_bool(const char16_t *fmt, bool value) {
451 assert(fmt);
452 Print(fmt, yes_no(value));
453 }
454
455 static bool ps_continue(void) {
456 if (unicode_supported())
457 Print(L"\n─── Press any key to continue, ESC or q to quit. ───\n\n");
458 else
459 Print(L"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
460
461 uint64_t key;
462 return console_key_read(&key, UINT64_MAX) == EFI_SUCCESS &&
463 !IN_SET(key, KEYPRESS(0, SCAN_ESC, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q'));
464 }
465
466 static void print_status(Config *config, char16_t *loaded_image_path) {
467 UINTN x_max, y_max;
468 uint32_t screen_width = 0, screen_height = 0;
469 SecureBootMode secure;
470 _cleanup_free_ char16_t *device_part_uuid = NULL;
471
472 assert(config);
473 assert(loaded_image_path);
474
475 clear_screen(COLOR_NORMAL);
476 console_query_mode(&x_max, &y_max);
477 query_screen_resolution(&screen_width, &screen_height);
478
479 secure = secure_boot_mode();
480 (void) efivar_get(LOADER_GUID, L"LoaderDevicePartUUID", &device_part_uuid);
481
482 /* We employ some unusual indentation here for readability. */
483
484 ps_string(L" systemd-boot version: %a\n", GIT_VERSION);
485 ps_string(L" loaded image: %s\n", loaded_image_path);
486 ps_string(L" loader partition UUID: %s\n", device_part_uuid);
487 ps_string(L" architecture: %a\n", EFI_MACHINE_TYPE_NAME);
488 Print(L" UEFI specification: %u.%02u\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
489 ps_string(L" firmware vendor: %s\n", ST->FirmwareVendor);
490 Print(L" firmware version: %u.%02u\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
491 Print(L" OS indications: %lu\n", get_os_indications_supported());
492 Print(L" secure boot: %s (%s)\n", yes_no(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)), secure_boot_mode_to_string(secure));
493 ps_bool(L" shim: %s\n", shim_loaded());
494 ps_bool(L" TPM: %s\n", tpm_present());
495 Print(L" console mode: %d/%ld (%" PRIuN L"x%" PRIuN L" @%ux%u)\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - INT64_C(1), x_max, y_max, screen_width, screen_height);
496
497 if (!ps_continue())
498 return;
499
500 switch (config->timeout_sec_config) {
501 case TIMEOUT_UNSET:
502 break;
503 case TIMEOUT_MENU_FORCE:
504 Print(L" timeout (config): menu-force\n"); break;
505 case TIMEOUT_MENU_HIDDEN:
506 Print(L" timeout (config): menu-hidden\n"); break;
507 default:
508 Print(L" timeout (config): %u s\n", config->timeout_sec_config);
509 }
510
511 switch (config->timeout_sec_efivar) {
512 case TIMEOUT_UNSET:
513 break;
514 case TIMEOUT_MENU_FORCE:
515 Print(L" timeout (EFI var): menu-force\n"); break;
516 case TIMEOUT_MENU_HIDDEN:
517 Print(L" timeout (EFI var): menu-hidden\n"); break;
518 default:
519 Print(L" timeout (EFI var): %u s\n", config->timeout_sec_efivar);
520 }
521
522 ps_string(L" default (config): %s\n", config->entry_default_config);
523 ps_string(L" default (EFI var): %s\n", config->entry_default_efivar);
524 ps_string(L" default (one-shot): %s\n", config->entry_oneshot);
525 ps_string(L" saved entry: %s\n", config->entry_saved);
526 ps_bool(L" editor: %s\n", config->editor);
527 ps_bool(L" auto-entries: %s\n", config->auto_entries);
528 ps_bool(L" auto-firmware: %s\n", config->auto_firmware);
529 ps_bool(L" beep: %s\n", config->beep);
530 ps_bool(L" reboot-for-bitlocker: %s\n", config->reboot_for_bitlocker);
531 ps_string(L" random-seed-mode: %s\n", random_seed_modes_table[config->random_seed_mode]);
532
533 switch (config->secure_boot_enroll) {
534 case ENROLL_OFF:
535 Print(L" secure-boot-enroll: off\n"); break;
536 case ENROLL_MANUAL:
537 Print(L" secure-boot-enroll: manual\n"); break;
538 case ENROLL_FORCE:
539 Print(L" secure-boot-enroll: force\n"); break;
540 default:
541 assert_not_reached();
542 }
543
544 switch (config->console_mode) {
545 case CONSOLE_MODE_AUTO:
546 Print(L" console-mode (config): %s\n", L"auto"); break;
547 case CONSOLE_MODE_KEEP:
548 Print(L" console-mode (config): %s\n", L"keep"); break;
549 case CONSOLE_MODE_FIRMWARE_MAX:
550 Print(L" console-mode (config): %s\n", L"max"); break;
551 default:
552 Print(L" console-mode (config): %ld\n", config->console_mode); break;
553 }
554
555 /* EFI var console mode is always a concrete value or unset. */
556 if (config->console_mode_efivar != CONSOLE_MODE_KEEP)
557 Print(L"console-mode (EFI var): %ld\n", config->console_mode_efivar);
558
559 if (!ps_continue())
560 return;
561
562 for (UINTN i = 0; i < config->entry_count; i++) {
563 ConfigEntry *entry = config->entries[i];
564
565 Print(L" config entry: %" PRIuN L"/%" PRIuN L"\n", i + 1, config->entry_count);
566 ps_string(L" id: %s\n", entry->id);
567 ps_string(L" title: %s\n", entry->title);
568 ps_string(L" title show: %s\n", streq16(entry->title, entry->title_show) ? NULL : entry->title_show);
569 ps_string(L" sort key: %s\n", entry->sort_key);
570 ps_string(L" version: %s\n", entry->version);
571 ps_string(L" machine-id: %s\n", entry->machine_id);
572 if (entry->device)
573 Print(L" device: %D\n", DevicePathFromHandle(entry->device));
574 ps_string(L" loader: %s\n", entry->loader);
575 STRV_FOREACH(initrd, entry->initrd)
576 Print(L" initrd: %s\n", *initrd);
577 ps_string(L" devicetree: %s\n", entry->devicetree);
578 ps_string(L" options: %s\n", entry->options);
579 ps_bool(L" internal call: %s\n", !!entry->call);
580
581 ps_bool(L"counting boots: %s\n", entry->tries_left >= 0);
582 if (entry->tries_left >= 0) {
583 Print(L" tries: %u left, %u done\n", entry->tries_left, entry->tries_done);
584 Print(L" current path: %s\\%s\n", entry->path, entry->current_name);
585 Print(L" next path: %s\\%s\n", entry->path, entry->next_name);
586 }
587
588 if (!ps_continue())
589 return;
590 }
591 }
592
593 static EFI_STATUS reboot_into_firmware(void) {
594 uint64_t osind = 0;
595 EFI_STATUS err;
596
597 if (!FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
598 return log_error_status_stall(EFI_UNSUPPORTED, L"Reboot to firmware interface not supported.");
599
600 (void) efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndications", &osind);
601 osind |= EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
602
603 err = efivar_set_uint64_le(EFI_GLOBAL_GUID, L"OsIndications", osind, EFI_VARIABLE_NON_VOLATILE);
604 if (err != EFI_SUCCESS)
605 return log_error_status_stall(err, L"Error setting OsIndications: %r", err);
606
607 RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
608 assert_not_reached();
609 }
610
611 static bool menu_run(
612 Config *config,
613 ConfigEntry **chosen_entry,
614 char16_t *loaded_image_path) {
615
616 assert(config);
617 assert(chosen_entry);
618 assert(loaded_image_path);
619
620 EFI_STATUS err;
621 UINTN visible_max = 0;
622 UINTN idx_highlight = config->idx_default;
623 UINTN idx_highlight_prev = 0;
624 UINTN idx, idx_first = 0, idx_last = 0;
625 bool new_mode = true, clear = true;
626 bool refresh = true, highlight = false;
627 UINTN x_start = 0, y_start = 0, y_status = 0;
628 UINTN x_max, y_max;
629 _cleanup_(strv_freep) char16_t **lines = NULL;
630 _cleanup_free_ char16_t *clearline = NULL, *separator = NULL, *status = NULL;
631 uint32_t timeout_efivar_saved = config->timeout_sec_efivar;
632 uint32_t timeout_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec;
633 bool exit = false, run = true, firmware_setup = false;
634 int64_t console_mode_initial = ST->ConOut->Mode->Mode, console_mode_efivar_saved = config->console_mode_efivar;
635 UINTN default_efivar_saved = config->idx_default_efivar;
636
637 graphics_mode(false);
638 ST->ConIn->Reset(ST->ConIn, false);
639 ST->ConOut->EnableCursor(ST->ConOut, false);
640
641 /* draw a single character to make ClearScreen work on some firmware */
642 Print(L" ");
643
644 err = console_set_mode(config->console_mode_efivar != CONSOLE_MODE_KEEP ?
645 config->console_mode_efivar : config->console_mode);
646 if (err != EFI_SUCCESS) {
647 clear_screen(COLOR_NORMAL);
648 log_error_stall(L"Error switching console mode: %r", err);
649 }
650
651 UINTN line_width = 0, entry_padding = 3;
652 while (!exit) {
653 uint64_t key;
654
655 if (new_mode) {
656 console_query_mode(&x_max, &y_max);
657
658 /* account for padding+status */
659 visible_max = y_max - 2;
660
661 /* Drawing entries starts at idx_first until idx_last. We want to make
662 * sure that idx_highlight is centered, but not if we are close to the
663 * beginning/end of the entry list. Otherwise we would have a half-empty
664 * screen. */
665 if (config->entry_count <= visible_max || idx_highlight <= visible_max / 2)
666 idx_first = 0;
667 else if (idx_highlight >= config->entry_count - (visible_max / 2))
668 idx_first = config->entry_count - visible_max;
669 else
670 idx_first = idx_highlight - (visible_max / 2);
671 idx_last = idx_first + visible_max - 1;
672
673 /* length of the longest entry */
674 line_width = 0;
675 for (UINTN i = 0; i < config->entry_count; i++)
676 line_width = MAX(line_width, strlen16(config->entries[i]->title_show));
677 line_width = MIN(line_width + 2 * entry_padding, x_max);
678
679 /* offsets to center the entries on the screen */
680 x_start = (x_max - (line_width)) / 2;
681 if (config->entry_count < visible_max)
682 y_start = ((visible_max - config->entry_count) / 2) + 1;
683 else
684 y_start = 0;
685
686 /* Put status line after the entry list, but give it some breathing room. */
687 y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 1, y_max - 1);
688
689 lines = strv_free(lines);
690 clearline = mfree(clearline);
691 separator = mfree(separator);
692
693 /* menu entries title lines */
694 lines = xnew(char16_t *, config->entry_count + 1);
695
696 for (UINTN i = 0; i < config->entry_count; i++) {
697 UINTN j, padding;
698
699 lines[i] = xnew(char16_t, line_width + 1);
700 padding = (line_width - MIN(strlen16(config->entries[i]->title_show), line_width)) / 2;
701
702 for (j = 0; j < padding; j++)
703 lines[i][j] = ' ';
704
705 for (UINTN k = 0; config->entries[i]->title_show[k] != '\0' && j < line_width; j++, k++)
706 lines[i][j] = config->entries[i]->title_show[k];
707
708 for (; j < line_width; j++)
709 lines[i][j] = ' ';
710 lines[i][line_width] = '\0';
711 }
712 lines[config->entry_count] = NULL;
713
714 clearline = xnew(char16_t, x_max + 1);
715 separator = xnew(char16_t, x_max + 1);
716 for (UINTN i = 0; i < x_max; i++) {
717 clearline[i] = ' ';
718 separator[i] = unicode_supported() ? L'─' : L'-';
719 }
720 clearline[x_max] = 0;
721 separator[x_max] = 0;
722
723 new_mode = false;
724 clear = true;
725 }
726
727 if (clear) {
728 clear_screen(COLOR_NORMAL);
729 clear = false;
730 refresh = true;
731 }
732
733 if (refresh) {
734 for (UINTN i = idx_first; i <= idx_last && i < config->entry_count; i++) {
735 print_at(x_start, y_start + i - idx_first,
736 (i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
737 lines[i]);
738 if (i == config->idx_default_efivar)
739 print_at(x_start,
740 y_start + i - idx_first,
741 (i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
742 unicode_supported() ? L" ►" : L"=>");
743 }
744 refresh = false;
745 } else if (highlight) {
746 print_at(x_start, y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, lines[idx_highlight_prev]);
747 print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, lines[idx_highlight]);
748 if (idx_highlight_prev == config->idx_default_efivar)
749 print_at(x_start,
750 y_start + idx_highlight_prev - idx_first,
751 COLOR_ENTRY,
752 unicode_supported() ? L" ►" : L"=>");
753 if (idx_highlight == config->idx_default_efivar)
754 print_at(x_start,
755 y_start + idx_highlight - idx_first,
756 COLOR_HIGHLIGHT,
757 unicode_supported() ? L" ►" : L"=>");
758 highlight = false;
759 }
760
761 if (timeout_remain > 0) {
762 free(status);
763 status = xpool_print(L"Boot in %u s.", timeout_remain);
764 }
765
766 if (status) {
767 /* If we draw the last char of the last line, the screen will scroll and break our
768 * input. Therefore, draw one less character then we could for the status message.
769 * Note that the same does not apply for the separator line as it will never be drawn
770 * on the last line. */
771 UINTN len = strnlen16(status, x_max - 1);
772 UINTN x = (x_max - len) / 2;
773 status[len] = '\0';
774 print_at(0, y_status, COLOR_NORMAL, clearline + x_max - x);
775 ST->ConOut->OutputString(ST->ConOut, status);
776 ST->ConOut->OutputString(ST->ConOut, clearline + 1 + x + len);
777
778 len = MIN(MAX(len, line_width) + 2 * entry_padding, x_max);
779 x = (x_max - len) / 2;
780 print_at(x, y_status - 1, COLOR_NORMAL, separator + x_max - len);
781 } else {
782 print_at(0, y_status - 1, COLOR_NORMAL, clearline);
783 print_at(0, y_status, COLOR_NORMAL, clearline + 1); /* See comment above. */
784 }
785
786 /* Beep several times so that the selected entry can be distinguished. */
787 if (config->beep)
788 beep(idx_highlight + 1);
789
790 err = console_key_read(&key, timeout_remain > 0 ? 1000 * 1000 : UINT64_MAX);
791 if (err == EFI_NOT_READY)
792 /* No input device returned a key, try again. This
793 * normally should not happen. */
794 continue;
795 if (err == EFI_TIMEOUT) {
796 assert(timeout_remain > 0);
797 timeout_remain--;
798 if (timeout_remain == 0) {
799 exit = true;
800 break;
801 }
802
803 /* update status */
804 continue;
805 }
806 if (err != EFI_SUCCESS) {
807 exit = true;
808 break;
809 }
810
811 timeout_remain = 0;
812
813 /* clear status after keystroke */
814 status = mfree(status);
815
816 idx_highlight_prev = idx_highlight;
817
818 if (firmware_setup) {
819 firmware_setup = false;
820 if (key == KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN))
821 reboot_into_firmware();
822 continue;
823 }
824
825 switch (key) {
826 case KEYPRESS(0, SCAN_UP, 0):
827 case KEYPRESS(0, 0, 'k'):
828 case KEYPRESS(0, 0, 'K'):
829 if (idx_highlight > 0)
830 idx_highlight--;
831 break;
832
833 case KEYPRESS(0, SCAN_DOWN, 0):
834 case KEYPRESS(0, 0, 'j'):
835 case KEYPRESS(0, 0, 'J'):
836 if (idx_highlight < config->entry_count-1)
837 idx_highlight++;
838 break;
839
840 case KEYPRESS(0, SCAN_HOME, 0):
841 case KEYPRESS(EFI_ALT_PRESSED, 0, '<'):
842 if (idx_highlight > 0) {
843 refresh = true;
844 idx_highlight = 0;
845 }
846 break;
847
848 case KEYPRESS(0, SCAN_END, 0):
849 case KEYPRESS(EFI_ALT_PRESSED, 0, '>'):
850 if (idx_highlight < config->entry_count-1) {
851 refresh = true;
852 idx_highlight = config->entry_count-1;
853 }
854 break;
855
856 case KEYPRESS(0, SCAN_PAGE_UP, 0):
857 if (idx_highlight > visible_max)
858 idx_highlight -= visible_max;
859 else
860 idx_highlight = 0;
861 break;
862
863 case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
864 idx_highlight += visible_max;
865 if (idx_highlight > config->entry_count-1)
866 idx_highlight = config->entry_count-1;
867 break;
868
869 case KEYPRESS(0, 0, CHAR_LINEFEED):
870 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
871 case KEYPRESS(0, CHAR_CARRIAGE_RETURN, 0): /* EZpad Mini 4s firmware sends malformed events */
872 case KEYPRESS(0, CHAR_CARRIAGE_RETURN, CHAR_CARRIAGE_RETURN): /* Teclast X98+ II firmware sends malformed events */
873 case KEYPRESS(0, SCAN_RIGHT, 0):
874 exit = true;
875 break;
876
877 case KEYPRESS(0, SCAN_F1, 0):
878 case KEYPRESS(0, 0, 'h'):
879 case KEYPRESS(0, 0, 'H'):
880 case KEYPRESS(0, 0, '?'):
881 /* This must stay below 80 characters! Q/v/Ctrl+l/f deliberately not advertised. */
882 status = xstrdup16(u"(d)efault (t/T)timeout (e)dit (r/R)resolution (p)rint (h)elp");
883 break;
884
885 case KEYPRESS(0, 0, 'Q'):
886 exit = true;
887 run = false;
888 break;
889
890 case KEYPRESS(0, 0, 'd'):
891 case KEYPRESS(0, 0, 'D'):
892 if (config->idx_default_efivar != idx_highlight) {
893 free(config->entry_default_efivar);
894 config->entry_default_efivar = xstrdup16(config->entries[idx_highlight]->id);
895 config->idx_default_efivar = idx_highlight;
896 status = xstrdup16(u"Default boot entry selected.");
897 } else {
898 config->entry_default_efivar = mfree(config->entry_default_efivar);
899 config->idx_default_efivar = IDX_INVALID;
900 status = xstrdup16(u"Default boot entry cleared.");
901 }
902 config->use_saved_entry_efivar = false;
903 refresh = true;
904 break;
905
906 case KEYPRESS(0, 0, '-'):
907 case KEYPRESS(0, 0, 'T'):
908 status = update_timeout_efivar(&config->timeout_sec_efivar, false);
909 break;
910
911 case KEYPRESS(0, 0, '+'):
912 case KEYPRESS(0, 0, 't'):
913 status = update_timeout_efivar(&config->timeout_sec_efivar, true);
914 break;
915
916 case KEYPRESS(0, 0, 'e'):
917 case KEYPRESS(0, 0, 'E'):
918 /* only the options of configured entries can be edited */
919 if (!config->editor || !IN_SET(config->entries[idx_highlight]->type,
920 LOADER_EFI, LOADER_LINUX, LOADER_UNIFIED_LINUX))
921 break;
922
923 /* Unified kernels that are signed as a whole will not accept command line options
924 * when secure boot is enabled unless there is none embedded in the image. Do not try
925 * to pretend we can edit it to only have it be ignored. */
926 if (config->entries[idx_highlight]->type == LOADER_UNIFIED_LINUX &&
927 secure_boot_enabled() &&
928 config->entries[idx_highlight]->options)
929 break;
930
931 /* The edit line may end up on the last line of the screen. And even though we're
932 * not telling the firmware to advance the line, it still does in this one case,
933 * causing a scroll to happen that screws with our beautiful boot loader output.
934 * Since we cannot paint the last character of the edit line, we simply start
935 * at x-offset 1 for symmetry. */
936 print_at(1, y_status, COLOR_EDIT, clearline + 2);
937 exit = line_edit(&config->entries[idx_highlight]->options, x_max - 2, y_status);
938 print_at(1, y_status, COLOR_NORMAL, clearline + 2);
939 break;
940
941 case KEYPRESS(0, 0, 'v'):
942 status = xpool_print(
943 L"systemd-boot " GIT_VERSION L" (" EFI_MACHINE_TYPE_NAME L"), "
944 L"UEFI Specification %u.%02u, Vendor %s %u.%02u",
945 ST->Hdr.Revision >> 16,
946 ST->Hdr.Revision & 0xffff,
947 ST->FirmwareVendor,
948 ST->FirmwareRevision >> 16,
949 ST->FirmwareRevision & 0xffff);
950 break;
951
952 case KEYPRESS(0, 0, 'p'):
953 case KEYPRESS(0, 0, 'P'):
954 print_status(config, loaded_image_path);
955 clear = true;
956 break;
957
958 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
959 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
960 clear = true;
961 break;
962
963 case KEYPRESS(0, 0, 'r'):
964 err = console_set_mode(CONSOLE_MODE_NEXT);
965 if (err != EFI_SUCCESS)
966 status = xpool_print(L"Error changing console mode: %r", err);
967 else {
968 config->console_mode_efivar = ST->ConOut->Mode->Mode;
969 status = xpool_print(L"Console mode changed to %ld.", config->console_mode_efivar);
970 }
971 new_mode = true;
972 break;
973
974 case KEYPRESS(0, 0, 'R'):
975 config->console_mode_efivar = CONSOLE_MODE_KEEP;
976 err = console_set_mode(config->console_mode == CONSOLE_MODE_KEEP ?
977 console_mode_initial : config->console_mode);
978 if (err != EFI_SUCCESS)
979 status = xpool_print(L"Error resetting console mode: %r", err);
980 else
981 status = xpool_print(L"Console mode reset to %s default.",
982 config->console_mode == CONSOLE_MODE_KEEP ? L"firmware" : L"configuration file");
983 new_mode = true;
984 break;
985
986 case KEYPRESS(0, 0, 'f'):
987 case KEYPRESS(0, 0, 'F'):
988 case KEYPRESS(0, SCAN_F2, 0): /* Most vendors. */
989 case KEYPRESS(0, SCAN_F10, 0): /* HP and Lenovo. */
990 case KEYPRESS(0, SCAN_DELETE, 0): /* Same as F2. */
991 case KEYPRESS(0, SCAN_ESC, 0): /* HP. */
992 if (FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) {
993 firmware_setup = true;
994 /* Let's make sure the user really wants to do this. */
995 status = xpool_print(L"Press Enter to reboot into firmware interface.");
996 } else
997 status = xpool_print(L"Reboot into firmware interface not supported.");
998 break;
999
1000 default:
1001 /* jump with a hotkey directly to a matching entry */
1002 idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
1003 if (idx == IDX_INVALID)
1004 break;
1005 idx_highlight = idx;
1006 refresh = true;
1007 }
1008
1009 if (idx_highlight > idx_last) {
1010 idx_last = idx_highlight;
1011 idx_first = 1 + idx_highlight - visible_max;
1012 refresh = true;
1013 } else if (idx_highlight < idx_first) {
1014 idx_first = idx_highlight;
1015 idx_last = idx_highlight + visible_max-1;
1016 refresh = true;
1017 }
1018
1019 if (!refresh && idx_highlight != idx_highlight_prev)
1020 highlight = true;
1021 }
1022
1023 *chosen_entry = config->entries[idx_highlight];
1024
1025 /* Update EFI vars after we left the menu to reduce NVRAM writes. */
1026
1027 if (default_efivar_saved != config->idx_default_efivar)
1028 efivar_set(LOADER_GUID, L"LoaderEntryDefault", config->entry_default_efivar, EFI_VARIABLE_NON_VOLATILE);
1029
1030 if (console_mode_efivar_saved != config->console_mode_efivar) {
1031 if (config->console_mode_efivar == CONSOLE_MODE_KEEP)
1032 efivar_set(LOADER_GUID, L"LoaderConfigConsoleMode", NULL, EFI_VARIABLE_NON_VOLATILE);
1033 else
1034 efivar_set_uint_string(LOADER_GUID, L"LoaderConfigConsoleMode",
1035 config->console_mode_efivar, EFI_VARIABLE_NON_VOLATILE);
1036 }
1037
1038 if (timeout_efivar_saved != config->timeout_sec_efivar) {
1039 if (config->timeout_sec_efivar == TIMEOUT_UNSET)
1040 efivar_set(LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
1041 else
1042 efivar_set_uint_string(LOADER_GUID, L"LoaderConfigTimeout",
1043 config->timeout_sec_efivar, EFI_VARIABLE_NON_VOLATILE);
1044 }
1045
1046 clear_screen(COLOR_NORMAL);
1047 return run;
1048 }
1049
1050 static void config_add_entry(Config *config, ConfigEntry *entry) {
1051 assert(config);
1052 assert(entry);
1053
1054 /* This is just for paranoia. */
1055 assert(config->entry_count < IDX_MAX);
1056
1057 if ((config->entry_count & 15) == 0) {
1058 config->entries = xrealloc(
1059 config->entries,
1060 sizeof(void *) * config->entry_count,
1061 sizeof(void *) * (config->entry_count + 16));
1062 }
1063 config->entries[config->entry_count++] = entry;
1064 }
1065
1066 static void config_entry_free(ConfigEntry *entry) {
1067 if (!entry)
1068 return;
1069
1070 free(entry->id);
1071 free(entry->title_show);
1072 free(entry->title);
1073 free(entry->sort_key);
1074 free(entry->version);
1075 free(entry->machine_id);
1076 free(entry->loader);
1077 free(entry->devicetree);
1078 free(entry->options);
1079 strv_free(entry->initrd);
1080 free(entry->path);
1081 free(entry->current_name);
1082 free(entry->next_name);
1083 free(entry);
1084 }
1085
1086 static inline void config_entry_freep(ConfigEntry **entry) {
1087 config_entry_free(*entry);
1088 }
1089
1090 static char *line_get_key_value(
1091 char *content,
1092 const char *sep,
1093 UINTN *pos,
1094 char **key_ret,
1095 char **value_ret) {
1096
1097 char *line, *value;
1098 UINTN linelen;
1099
1100 assert(content);
1101 assert(sep);
1102 assert(pos);
1103 assert(key_ret);
1104 assert(value_ret);
1105
1106 for (;;) {
1107 line = content + *pos;
1108 if (*line == '\0')
1109 return NULL;
1110
1111 linelen = 0;
1112 while (line[linelen] && !strchr8("\n\r", line[linelen]))
1113 linelen++;
1114
1115 /* move pos to next line */
1116 *pos += linelen;
1117 if (content[*pos])
1118 (*pos)++;
1119
1120 /* empty line */
1121 if (linelen == 0)
1122 continue;
1123
1124 /* terminate line */
1125 line[linelen] = '\0';
1126
1127 /* remove leading whitespace */
1128 while (strchr8(" \t", *line)) {
1129 line++;
1130 linelen--;
1131 }
1132
1133 /* remove trailing whitespace */
1134 while (linelen > 0 && strchr8(" \t", line[linelen - 1]))
1135 linelen--;
1136 line[linelen] = '\0';
1137
1138 if (*line == '#')
1139 continue;
1140
1141 /* split key/value */
1142 value = line;
1143 while (*value && !strchr8(sep, *value))
1144 value++;
1145 if (*value == '\0')
1146 continue;
1147 *value = '\0';
1148 value++;
1149 while (*value && strchr8(sep, *value))
1150 value++;
1151
1152 /* unquote */
1153 if (value[0] == '"' && line[linelen - 1] == '"') {
1154 value++;
1155 line[linelen - 1] = '\0';
1156 }
1157
1158 *key_ret = line;
1159 *value_ret = value;
1160 return line;
1161 }
1162 }
1163
1164 static void config_defaults_load_from_file(Config *config, char *content) {
1165 char *line;
1166 UINTN pos = 0;
1167 char *key, *value;
1168 EFI_STATUS err;
1169
1170 assert(config);
1171 assert(content);
1172
1173 while ((line = line_get_key_value(content, " \t", &pos, &key, &value))) {
1174 if (streq8(key, "timeout")) {
1175 if (streq8( value, "menu-force"))
1176 config->timeout_sec_config = TIMEOUT_MENU_FORCE;
1177 else if (streq8(value, "menu-hidden"))
1178 config->timeout_sec_config = TIMEOUT_MENU_HIDDEN;
1179 else {
1180 uint64_t u;
1181 if (!parse_number8(value, &u, NULL) || u > TIMEOUT_TYPE_MAX) {
1182 log_error_stall(L"Error parsing 'timeout' config option: %a", value);
1183 continue;
1184 }
1185 config->timeout_sec_config = u;
1186 }
1187 config->timeout_sec = config->timeout_sec_config;
1188 continue;
1189 }
1190
1191 if (streq8(key, "default")) {
1192 if (value[0] == '@' && !strcaseeq8(value, "@saved")) {
1193 log_error_stall(L"Unsupported special entry identifier: %a", value);
1194 continue;
1195 }
1196 free(config->entry_default_config);
1197 config->entry_default_config = xstra_to_str(value);
1198 continue;
1199 }
1200
1201 if (streq8(key, "editor")) {
1202 err = parse_boolean(value, &config->editor);
1203 if (err != EFI_SUCCESS)
1204 log_error_stall(L"Error parsing 'editor' config option: %a", value);
1205 continue;
1206 }
1207
1208 if (streq8(key, "auto-entries")) {
1209 err = parse_boolean(value, &config->auto_entries);
1210 if (err != EFI_SUCCESS)
1211 log_error_stall(L"Error parsing 'auto-entries' config option: %a", value);
1212 continue;
1213 }
1214
1215 if (streq8(key, "auto-firmware")) {
1216 err = parse_boolean(value, &config->auto_firmware);
1217 if (err != EFI_SUCCESS)
1218 log_error_stall(L"Error parsing 'auto-firmware' config option: %a", value);
1219 continue;
1220 }
1221
1222 if (streq8(key, "beep")) {
1223 err = parse_boolean(value, &config->beep);
1224 if (err != EFI_SUCCESS)
1225 log_error_stall(L"Error parsing 'beep' config option: %a", value);
1226 continue;
1227 }
1228
1229 if (streq8(key, "reboot-for-bitlocker")) {
1230 err = parse_boolean(value, &config->reboot_for_bitlocker);
1231 if (err != EFI_SUCCESS)
1232 log_error_stall(L"Error parsing 'reboot-for-bitlocker' config option: %a", value);
1233 }
1234
1235 if (streq8(key, "secure-boot-enroll")) {
1236 if (streq8(value, "manual"))
1237 config->secure_boot_enroll = ENROLL_MANUAL;
1238 else if (streq8(value, "force"))
1239 config->secure_boot_enroll = ENROLL_FORCE;
1240 else if (streq8(value, "off"))
1241 config->secure_boot_enroll = ENROLL_OFF;
1242 else
1243 log_error_stall(L"Error parsing 'secure-boot-enroll' config option: %a", value);
1244 continue;
1245 }
1246
1247 if (streq8(key, "console-mode")) {
1248 if (streq8(value, "auto"))
1249 config->console_mode = CONSOLE_MODE_AUTO;
1250 else if (streq8(value, "max"))
1251 config->console_mode = CONSOLE_MODE_FIRMWARE_MAX;
1252 else if (streq8(value, "keep"))
1253 config->console_mode = CONSOLE_MODE_KEEP;
1254 else {
1255 uint64_t u;
1256 if (!parse_number8(value, &u, NULL) || u > CONSOLE_MODE_RANGE_MAX) {
1257 log_error_stall(L"Error parsing 'console-mode' config option: %a", value);
1258 continue;
1259 }
1260 config->console_mode = u;
1261 }
1262 continue;
1263 }
1264
1265 if (streq8(key, "random-seed-mode")) {
1266 if (streq8(value, "off"))
1267 config->random_seed_mode = RANDOM_SEED_OFF;
1268 else if (streq8(value, "with-system-token"))
1269 config->random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN;
1270 else if (streq8(value, "always"))
1271 config->random_seed_mode = RANDOM_SEED_ALWAYS;
1272 else {
1273 bool on;
1274
1275 err = parse_boolean(value, &on);
1276 if (err != EFI_SUCCESS) {
1277 log_error_stall(L"Error parsing 'random-seed-mode' config option: %a", value);
1278 continue;
1279 }
1280
1281 config->random_seed_mode = on ? RANDOM_SEED_ALWAYS : RANDOM_SEED_OFF;
1282 }
1283 continue;
1284 }
1285 }
1286 }
1287
1288 static void config_entry_parse_tries(
1289 ConfigEntry *entry,
1290 const char16_t *path,
1291 const char16_t *file,
1292 const char16_t *suffix) {
1293
1294 assert(entry);
1295 assert(path);
1296 assert(file);
1297 assert(suffix);
1298
1299 /*
1300 * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
1301 * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
1302 * which case that counter as assumed to be zero, i.e. the missing part is synonymous to "-0").
1303 *
1304 * Names we grok, and the series they result in:
1305 *
1306 * foobar+3.efi → foobar+2-1.efi → foobar+1-2.efi → foobar+0-3.efi → STOP!
1307 * foobar+4-0.efi → foobar+3-1.efi → foobar+2-2.efi → foobar+1-3.efi → foobar+0-4.efi → STOP!
1308 */
1309
1310 const char16_t *counter = NULL;
1311 for (;;) {
1312 char16_t *plus = strchr16(counter ?: file, '+');
1313 if (plus) {
1314 /* We want the last "+". */
1315 counter = plus + 1;
1316 continue;
1317 }
1318 if (counter)
1319 break;
1320
1321 /* No boot counter found. */
1322 return;
1323 }
1324
1325 uint64_t tries_left, tries_done = 0;
1326 size_t prefix_len = counter - file;
1327
1328 if (!parse_number16(counter, &tries_left, &counter) || tries_left > INT_MAX)
1329 return;
1330
1331 /* Parse done counter only if present. */
1332 if (*counter == '-' && (!parse_number16(counter + 1, &tries_done, &counter) || tries_done > INT_MAX))
1333 return;
1334
1335 /* Boot counter in the middle of the name? */
1336 if (!streq16(counter, suffix))
1337 return;
1338
1339 entry->tries_left = tries_left;
1340 entry->tries_done = tries_done;
1341 entry->path = xstrdup16(path);
1342 entry->current_name = xstrdup16(file);
1343 entry->next_name = xpool_print(
1344 L"%.*s%u-%u%s",
1345 prefix_len,
1346 file,
1347 LESS_BY(tries_left, 1u),
1348 MIN(tries_done + 1, (uint64_t) INT_MAX),
1349 suffix);
1350 }
1351
1352 static void config_entry_bump_counters(ConfigEntry *entry, EFI_FILE *root_dir) {
1353 _cleanup_free_ char16_t* old_path = NULL, *new_path = NULL;
1354 _cleanup_(file_closep) EFI_FILE *handle = NULL;
1355 _cleanup_free_ EFI_FILE_INFO *file_info = NULL;
1356 UINTN file_info_size;
1357 EFI_STATUS err;
1358
1359 assert(entry);
1360 assert(root_dir);
1361
1362 if (entry->tries_left < 0)
1363 return;
1364
1365 if (!entry->path || !entry->current_name || !entry->next_name)
1366 return;
1367
1368 old_path = xpool_print(L"%s\\%s", entry->path, entry->current_name);
1369
1370 err = root_dir->Open(root_dir, &handle, old_path, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL);
1371 if (err != EFI_SUCCESS)
1372 return;
1373
1374 err = get_file_info_harder(handle, &file_info, &file_info_size);
1375 if (err != EFI_SUCCESS)
1376 return;
1377
1378 /* And rename the file */
1379 strcpy16(file_info->FileName, entry->next_name);
1380 err = handle->SetInfo(handle, &GenericFileInfo, file_info_size, file_info);
1381 if (err != EFI_SUCCESS) {
1382 log_error_stall(L"Failed to rename '%s' to '%s', ignoring: %r", old_path, entry->next_name, err);
1383 return;
1384 }
1385
1386 /* Flush everything to disk, just in case… */
1387 (void) handle->Flush(handle);
1388
1389 /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
1390 * success */
1391 new_path = xpool_print(L"%s\\%s", entry->path, entry->next_name);
1392 efivar_set(LOADER_GUID, L"LoaderBootCountPath", new_path, 0);
1393
1394 /* If the file we just renamed is the loader path, then let's update that. */
1395 if (streq16(entry->loader, old_path)) {
1396 free(entry->loader);
1397 entry->loader = TAKE_PTR(new_path);
1398 }
1399 }
1400
1401 static void config_entry_add_type1(
1402 Config *config,
1403 EFI_HANDLE *device,
1404 EFI_FILE *root_dir,
1405 const char16_t *path,
1406 const char16_t *file,
1407 char *content,
1408 const char16_t *loaded_image_path) {
1409
1410 _cleanup_(config_entry_freep) ConfigEntry *entry = NULL;
1411 char *line;
1412 UINTN pos = 0, n_initrd = 0;
1413 char *key, *value;
1414 EFI_STATUS err;
1415
1416 assert(config);
1417 assert(device);
1418 assert(root_dir);
1419 assert(path);
1420 assert(file);
1421 assert(content);
1422
1423 entry = xnew(ConfigEntry, 1);
1424 *entry = (ConfigEntry) {
1425 .tries_done = -1,
1426 .tries_left = -1,
1427 };
1428
1429 while ((line = line_get_key_value(content, " \t", &pos, &key, &value))) {
1430 if (streq8(key, "title")) {
1431 free(entry->title);
1432 entry->title = xstra_to_str(value);
1433 continue;
1434 }
1435
1436 if (streq8(key, "sort-key")) {
1437 free(entry->sort_key);
1438 entry->sort_key = xstra_to_str(value);
1439 continue;
1440 }
1441
1442 if (streq8(key, "version")) {
1443 free(entry->version);
1444 entry->version = xstra_to_str(value);
1445 continue;
1446 }
1447
1448 if (streq8(key, "machine-id")) {
1449 free(entry->machine_id);
1450 entry->machine_id = xstra_to_str(value);
1451 continue;
1452 }
1453
1454 if (streq8(key, "linux")) {
1455 free(entry->loader);
1456 entry->type = LOADER_LINUX;
1457 entry->loader = xstra_to_path(value);
1458 entry->key = 'l';
1459 continue;
1460 }
1461
1462 if (streq8(key, "efi")) {
1463 entry->type = LOADER_EFI;
1464 free(entry->loader);
1465 entry->loader = xstra_to_path(value);
1466
1467 /* do not add an entry for ourselves */
1468 if (loaded_image_path && strcaseeq16(entry->loader, loaded_image_path)) {
1469 entry->type = LOADER_UNDEFINED;
1470 break;
1471 }
1472 continue;
1473 }
1474
1475 if (streq8(key, "architecture")) {
1476 /* do not add an entry for an EFI image of architecture not matching with that of the image */
1477 if (!streq8(value, EFI_MACHINE_TYPE_NAME)) {
1478 entry->type = LOADER_UNDEFINED;
1479 break;
1480 }
1481 continue;
1482 }
1483
1484 if (streq8(key, "devicetree")) {
1485 free(entry->devicetree);
1486 entry->devicetree = xstra_to_path(value);
1487 continue;
1488 }
1489
1490 if (streq8(key, "initrd")) {
1491 entry->initrd = xrealloc(
1492 entry->initrd,
1493 n_initrd == 0 ? 0 : (n_initrd + 1) * sizeof(uint16_t *),
1494 (n_initrd + 2) * sizeof(uint16_t *));
1495 entry->initrd[n_initrd++] = xstra_to_path(value);
1496 entry->initrd[n_initrd] = NULL;
1497 continue;
1498 }
1499
1500 if (streq8(key, "options")) {
1501 _cleanup_free_ char16_t *new = NULL;
1502
1503 new = xstra_to_str(value);
1504 if (entry->options) {
1505 char16_t *s = xpool_print(L"%s %s", entry->options, new);
1506 free(entry->options);
1507 entry->options = s;
1508 } else
1509 entry->options = TAKE_PTR(new);
1510
1511 continue;
1512 }
1513 }
1514
1515 if (entry->type == LOADER_UNDEFINED)
1516 return;
1517
1518 /* check existence */
1519 _cleanup_(file_closep) EFI_FILE *handle = NULL;
1520 err = root_dir->Open(root_dir, &handle, entry->loader, EFI_FILE_MODE_READ, 0ULL);
1521 if (err != EFI_SUCCESS)
1522 return;
1523
1524 entry->device = device;
1525 entry->id = xstrdup16(file);
1526 strtolower16(entry->id);
1527
1528 config_add_entry(config, entry);
1529
1530 config_entry_parse_tries(entry, path, file, L".conf");
1531 TAKE_PTR(entry);
1532 }
1533
1534 static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
1535 _cleanup_free_ char *content = NULL;
1536 UINTN value;
1537 EFI_STATUS err;
1538
1539 assert(root_dir);
1540
1541 *config = (Config) {
1542 .editor = true,
1543 .auto_entries = true,
1544 .auto_firmware = true,
1545 .reboot_for_bitlocker = false,
1546 .secure_boot_enroll = ENROLL_MANUAL,
1547 .random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN,
1548 .idx_default_efivar = IDX_INVALID,
1549 .console_mode = CONSOLE_MODE_KEEP,
1550 .console_mode_efivar = CONSOLE_MODE_KEEP,
1551 .timeout_sec_config = TIMEOUT_UNSET,
1552 .timeout_sec_efivar = TIMEOUT_UNSET,
1553 };
1554
1555 err = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content, NULL);
1556 if (err == EFI_SUCCESS)
1557 config_defaults_load_from_file(config, content);
1558
1559 err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeout", &value);
1560 if (err == EFI_SUCCESS) {
1561 config->timeout_sec_efivar = MIN(value, TIMEOUT_TYPE_MAX);
1562 config->timeout_sec = config->timeout_sec_efivar;
1563 }
1564
1565 err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeoutOneShot", &value);
1566 if (err == EFI_SUCCESS) {
1567 /* Unset variable now, after all it's "one shot". */
1568 (void) efivar_set(LOADER_GUID, L"LoaderConfigTimeoutOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
1569
1570 config->timeout_sec = MIN(value, TIMEOUT_TYPE_MAX);
1571 config->force_menu = true; /* force the menu when this is set */
1572 }
1573
1574 err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigConsoleMode", &value);
1575 if (err == EFI_SUCCESS)
1576 config->console_mode_efivar = value;
1577
1578 err = efivar_get(LOADER_GUID, L"LoaderEntryOneShot", &config->entry_oneshot);
1579 if (err == EFI_SUCCESS)
1580 /* Unset variable now, after all it's "one shot". */
1581 (void) efivar_set(LOADER_GUID, L"LoaderEntryOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
1582
1583 (void) efivar_get(LOADER_GUID, L"LoaderEntryDefault", &config->entry_default_efivar);
1584
1585 strtolower16(config->entry_default_config);
1586 strtolower16(config->entry_default_efivar);
1587 strtolower16(config->entry_oneshot);
1588 strtolower16(config->entry_saved);
1589
1590 config->use_saved_entry = streq16(config->entry_default_config, L"@saved");
1591 config->use_saved_entry_efivar = streq16(config->entry_default_efivar, L"@saved");
1592 if (config->use_saved_entry || config->use_saved_entry_efivar)
1593 (void) efivar_get(LOADER_GUID, L"LoaderEntryLastBooted", &config->entry_saved);
1594 }
1595
1596 static void config_load_entries(
1597 Config *config,
1598 EFI_HANDLE *device,
1599 EFI_FILE *root_dir,
1600 const char16_t *loaded_image_path) {
1601
1602 _cleanup_(file_closep) EFI_FILE *entries_dir = NULL;
1603 _cleanup_free_ EFI_FILE_INFO *f = NULL;
1604 UINTN f_size = 0;
1605 EFI_STATUS err;
1606
1607 assert(config);
1608 assert(device);
1609 assert(root_dir);
1610
1611 /* Adds Boot Loader Type #1 entries (i.e. /loader/entries/….conf) */
1612
1613 err = open_directory(root_dir, L"\\loader\\entries", &entries_dir);
1614 if (err != EFI_SUCCESS)
1615 return;
1616
1617 for (;;) {
1618 _cleanup_free_ char *content = NULL;
1619
1620 err = readdir_harder(entries_dir, &f, &f_size);
1621 if (err != EFI_SUCCESS || !f)
1622 break;
1623
1624 if (f->FileName[0] == '.')
1625 continue;
1626 if (FLAGS_SET(f->Attribute, EFI_FILE_DIRECTORY))
1627 continue;
1628
1629 if (!endswith_no_case(f->FileName, L".conf"))
1630 continue;
1631 if (startswith(f->FileName, L"auto-"))
1632 continue;
1633
1634 err = file_read(entries_dir, f->FileName, 0, 0, &content, NULL);
1635 if (err == EFI_SUCCESS)
1636 config_entry_add_type1(config, device, root_dir, L"\\loader\\entries", f->FileName, content, loaded_image_path);
1637 }
1638 }
1639
1640 static int config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) {
1641 int r;
1642
1643 assert(a);
1644 assert(b);
1645
1646 /* Order entries that have no tries left to the end of the list */
1647 r = CMP(a->tries_left == 0, b->tries_left == 0);
1648 if (r != 0)
1649 return r;
1650
1651 /* If there's a sort key defined for *both* entries, then we do new-style ordering, i.e. by
1652 * sort-key/machine-id/version, with a final fallback to id. If there's no sort key for either, we do
1653 * old-style ordering, i.e. by id only. If one has sort key and the other does not, we put new-style
1654 * before old-style. */
1655 r = CMP(!a->sort_key, !b->sort_key);
1656 if (r != 0) /* one is old-style, one new-style */
1657 return r;
1658
1659 if (a->sort_key && b->sort_key) {
1660 r = strcmp16(a->sort_key, b->sort_key);
1661 if (r != 0)
1662 return r;
1663
1664 /* If multiple installations of the same OS are around, group by machine ID */
1665 r = strcmp16(a->machine_id, b->machine_id);
1666 if (r != 0)
1667 return r;
1668
1669 /* If the sort key was defined, then order by version now (downwards, putting the newest first) */
1670 r = -strverscmp_improved(a->version, b->version);
1671 if (r != 0)
1672 return r;
1673 }
1674
1675 /* Now order by ID. The version is likely part of the ID, thus note that this will generatelly put
1676 * the newer versions earlier. Specifying a sort key explicitly is preferable, because it gives an
1677 * explicit sort order. */
1678 r = -strverscmp_improved(a->id, b->id);
1679 if (r != 0)
1680 return r;
1681
1682 if (a->tries_left < 0 || b->tries_left < 0)
1683 return 0;
1684
1685 /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
1686 r = -CMP(a->tries_left, b->tries_left);
1687 if (r != 0)
1688 return r;
1689
1690 /* If they have the same number of tries left, then let the one win which was tried fewer times so far */
1691 return CMP(a->tries_done, b->tries_done);
1692 }
1693
1694 static UINTN config_entry_find(Config *config, const char16_t *pattern) {
1695 assert(config);
1696
1697 /* We expect pattern and entry IDs to be already case folded. */
1698
1699 if (!pattern)
1700 return IDX_INVALID;
1701
1702 for (UINTN i = 0; i < config->entry_count; i++)
1703 if (efi_fnmatch(pattern, config->entries[i]->id))
1704 return i;
1705
1706 return IDX_INVALID;
1707 }
1708
1709 static void config_default_entry_select(Config *config) {
1710 UINTN i;
1711
1712 assert(config);
1713
1714 i = config_entry_find(config, config->entry_oneshot);
1715 if (i != IDX_INVALID) {
1716 config->idx_default = i;
1717 return;
1718 }
1719
1720 i = config_entry_find(config, config->use_saved_entry_efivar ? config->entry_saved : config->entry_default_efivar);
1721 if (i != IDX_INVALID) {
1722 config->idx_default = i;
1723 config->idx_default_efivar = i;
1724 return;
1725 }
1726
1727 if (config->use_saved_entry)
1728 /* No need to do the same thing twice. */
1729 i = config->use_saved_entry_efivar ? IDX_INVALID : config_entry_find(config, config->entry_saved);
1730 else
1731 i = config_entry_find(config, config->entry_default_config);
1732 if (i != IDX_INVALID) {
1733 config->idx_default = i;
1734 return;
1735 }
1736
1737 /* select the first suitable entry */
1738 for (i = 0; i < config->entry_count; i++) {
1739 if (config->entries[i]->type == LOADER_AUTO || config->entries[i]->call)
1740 continue;
1741 config->idx_default = i;
1742 return;
1743 }
1744
1745 /* If no configured entry to select from was found, enable the menu. */
1746 config->idx_default = 0;
1747 if (config->timeout_sec == 0)
1748 config->timeout_sec = 10;
1749 }
1750
1751 static bool entries_unique(ConfigEntry **entries, bool *unique, UINTN entry_count) {
1752 bool is_unique = true;
1753
1754 assert(entries);
1755 assert(unique);
1756
1757 for (UINTN i = 0; i < entry_count; i++)
1758 for (UINTN k = i + 1; k < entry_count; k++) {
1759 if (!streq16(entries[i]->title_show, entries[k]->title_show))
1760 continue;
1761
1762 is_unique = unique[i] = unique[k] = false;
1763 }
1764
1765 return is_unique;
1766 }
1767
1768 /* generate a unique title, avoiding non-distinguishable menu entries */
1769 static void config_title_generate(Config *config) {
1770 assert(config);
1771
1772 bool unique[config->entry_count];
1773
1774 /* set title */
1775 for (UINTN i = 0; i < config->entry_count; i++) {
1776 assert(!config->entries[i]->title_show);
1777 unique[i] = true;
1778 config->entries[i]->title_show = xstrdup16(config->entries[i]->title ?: config->entries[i]->id);
1779 }
1780
1781 if (entries_unique(config->entries, unique, config->entry_count))
1782 return;
1783
1784 /* add version to non-unique titles */
1785 for (UINTN i = 0; i < config->entry_count; i++) {
1786 if (unique[i])
1787 continue;
1788
1789 unique[i] = true;
1790
1791 if (!config->entries[i]->version)
1792 continue;
1793
1794 _cleanup_free_ char16_t *t = config->entries[i]->title_show;
1795 config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->version);
1796 }
1797
1798 if (entries_unique(config->entries, unique, config->entry_count))
1799 return;
1800
1801 /* add machine-id to non-unique titles */
1802 for (UINTN i = 0; i < config->entry_count; i++) {
1803 if (unique[i])
1804 continue;
1805
1806 unique[i] = true;
1807
1808 if (!config->entries[i]->machine_id)
1809 continue;
1810
1811 _cleanup_free_ char16_t *t = config->entries[i]->title_show;
1812 config->entries[i]->title_show = xpool_print(
1813 L"%s (%.*s)",
1814 t,
1815 strnlen16(config->entries[i]->machine_id, 8),
1816 config->entries[i]->machine_id);
1817 }
1818
1819 if (entries_unique(config->entries, unique, config->entry_count))
1820 return;
1821
1822 /* add file name to non-unique titles */
1823 for (UINTN i = 0; i < config->entry_count; i++) {
1824 if (unique[i])
1825 continue;
1826
1827 _cleanup_free_ char16_t *t = config->entries[i]->title_show;
1828 config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->id);
1829 }
1830 }
1831
1832 static bool is_sd_boot(EFI_FILE *root_dir, const char16_t *loader_path) {
1833 EFI_STATUS err;
1834 static const char * const sections[] = {
1835 ".sdmagic",
1836 NULL
1837 };
1838 UINTN offset = 0, size = 0, read;
1839 _cleanup_free_ char *content = NULL;
1840
1841 assert(root_dir);
1842 assert(loader_path);
1843
1844 err = pe_file_locate_sections(root_dir, loader_path, sections, &offset, &size);
1845 if (err != EFI_SUCCESS || size != sizeof(magic))
1846 return false;
1847
1848 err = file_read(root_dir, loader_path, offset, size, &content, &read);
1849 if (err != EFI_SUCCESS || size != read)
1850 return false;
1851
1852 return memcmp(content, magic, sizeof(magic)) == 0;
1853 }
1854
1855 static ConfigEntry *config_entry_add_loader_auto(
1856 Config *config,
1857 EFI_HANDLE *device,
1858 EFI_FILE *root_dir,
1859 const char16_t *loaded_image_path,
1860 const char16_t *id,
1861 char16_t key,
1862 const char16_t *title,
1863 const char16_t *loader) {
1864
1865 assert(config);
1866 assert(device);
1867 assert(root_dir);
1868 assert(id);
1869 assert(title);
1870 assert(loader || loaded_image_path);
1871
1872 if (!config->auto_entries)
1873 return NULL;
1874
1875 if (loaded_image_path) {
1876 loader = L"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME ".efi";
1877
1878 /* We are trying to add the default EFI loader here,
1879 * but we do not want to do that if that would be us.
1880 *
1881 * If the default loader is not us, it might be shim. It would
1882 * chainload GRUBX64.EFI in that case, which might be us.*/
1883 if (strcaseeq16(loader, loaded_image_path) ||
1884 is_sd_boot(root_dir, loader) ||
1885 is_sd_boot(root_dir, L"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L".EFI"))
1886 return NULL;
1887 }
1888
1889 /* check existence */
1890 _cleanup_(file_closep) EFI_FILE *handle = NULL;
1891 EFI_STATUS err = root_dir->Open(root_dir, &handle, (char16_t *) loader, EFI_FILE_MODE_READ, 0ULL);
1892 if (err != EFI_SUCCESS)
1893 return NULL;
1894
1895 ConfigEntry *entry = xnew(ConfigEntry, 1);
1896 *entry = (ConfigEntry) {
1897 .id = xstrdup16(id),
1898 .type = LOADER_AUTO,
1899 .title = xstrdup16(title),
1900 .device = device,
1901 .loader = xstrdup16(loader),
1902 .key = key,
1903 .tries_done = -1,
1904 .tries_left = -1,
1905 };
1906
1907 config_add_entry(config, entry);
1908 return entry;
1909 }
1910
1911 static void config_entry_add_osx(Config *config) {
1912 EFI_STATUS err;
1913 UINTN n_handles = 0;
1914 _cleanup_free_ EFI_HANDLE *handles = NULL;
1915
1916 assert(config);
1917
1918 if (!config->auto_entries)
1919 return;
1920
1921 err = BS->LocateHandleBuffer(ByProtocol, &FileSystemProtocol, NULL, &n_handles, &handles);
1922 if (err != EFI_SUCCESS)
1923 return;
1924
1925 for (UINTN i = 0; i < n_handles; i++) {
1926 _cleanup_(file_closep) EFI_FILE *root = NULL;
1927
1928 if (open_volume(handles[i], &root) != EFI_SUCCESS)
1929 continue;
1930
1931 if (config_entry_add_loader_auto(
1932 config,
1933 handles[i],
1934 root,
1935 NULL,
1936 L"auto-osx",
1937 'a',
1938 L"macOS",
1939 L"\\System\\Library\\CoreServices\\boot.efi"))
1940 break;
1941 }
1942 }
1943
1944 static EFI_STATUS boot_windows_bitlocker(void) {
1945 _cleanup_free_ EFI_HANDLE *handles = NULL;
1946 UINTN n_handles;
1947 EFI_STATUS err;
1948
1949 // FIXME: Experimental for now. Should be generalized, and become a per-entry option that can be
1950 // enabled independently of BitLocker, and without a BootXXXX entry pre-existing.
1951
1952 /* BitLocker key cannot be sealed without a TPM present. */
1953 if (!tpm_present())
1954 return EFI_NOT_FOUND;
1955
1956 err = BS->LocateHandleBuffer(ByProtocol, &BlockIoProtocol, NULL, &n_handles, &handles);
1957 if (err != EFI_SUCCESS)
1958 return err;
1959
1960 /* Look for BitLocker magic string on all block drives. */
1961 bool found = false;
1962 for (UINTN i = 0; i < n_handles; i++) {
1963 EFI_BLOCK_IO_PROTOCOL *block_io;
1964 err = BS->HandleProtocol(handles[i], &BlockIoProtocol, (void **) &block_io);
1965 if (err != EFI_SUCCESS || block_io->Media->BlockSize < 512 || block_io->Media->BlockSize > 4096)
1966 continue;
1967
1968 char buf[4096];
1969 err = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, sizeof(buf), buf);
1970 if (err != EFI_SUCCESS)
1971 continue;
1972
1973 if (memcmp(buf + 3, "-FVE-FS-", STRLEN("-FVE-FS-")) == 0) {
1974 found = true;
1975 break;
1976 }
1977 }
1978
1979 /* If no BitLocker drive was found, we can just chainload bootmgfw.efi directly. */
1980 if (!found)
1981 return EFI_NOT_FOUND;
1982
1983 _cleanup_free_ uint16_t *boot_order = NULL;
1984 UINTN boot_order_size;
1985
1986 /* There can be gaps in Boot#### entries. Instead of iterating over the full
1987 * EFI var list or uint16_t namespace, just look for "Windows Boot Manager" in BootOrder. */
1988 err = efivar_get_raw(EFI_GLOBAL_GUID, L"BootOrder", (char **) &boot_order, &boot_order_size);
1989 if (err != EFI_SUCCESS || boot_order_size % sizeof(uint16_t) != 0)
1990 return err;
1991
1992 for (UINTN i = 0; i < boot_order_size / sizeof(uint16_t); i++) {
1993 _cleanup_free_ char *buf = NULL;
1994 char16_t name[sizeof(L"Boot0000")];
1995 UINTN buf_size;
1996
1997 SPrint(name, sizeof(name), L"Boot%04x", (uint32_t) boot_order[i]);
1998 err = efivar_get_raw(EFI_GLOBAL_GUID, name, &buf, &buf_size);
1999 if (err != EFI_SUCCESS)
2000 continue;
2001
2002 /* Boot#### are EFI_LOAD_OPTION. But we really are only interested
2003 * for the description, which is at this offset. */
2004 UINTN offset = sizeof(uint32_t) + sizeof(uint16_t);
2005 if (buf_size < offset + sizeof(char16_t))
2006 continue;
2007
2008 if (streq16((char16_t *) (buf + offset), L"Windows Boot Manager")) {
2009 err = efivar_set_raw(
2010 EFI_GLOBAL_GUID,
2011 L"BootNext",
2012 boot_order + i,
2013 sizeof(boot_order[i]),
2014 EFI_VARIABLE_NON_VOLATILE);
2015 if (err != EFI_SUCCESS)
2016 return err;
2017 RT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL);
2018 assert_not_reached();
2019 }
2020 }
2021
2022 return EFI_NOT_FOUND;
2023 }
2024
2025 static void config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir) {
2026 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
2027 _cleanup_free_ char *bcd = NULL;
2028 char16_t *title = NULL;
2029 EFI_STATUS err;
2030 UINTN len;
2031
2032 assert(config);
2033 assert(device);
2034 assert(root_dir);
2035
2036 if (!config->auto_entries)
2037 return;
2038
2039 /* Try to find a better title. */
2040 err = file_read(root_dir, L"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd, &len);
2041 if (err == EFI_SUCCESS)
2042 title = get_bcd_title((uint8_t *) bcd, len);
2043
2044 ConfigEntry *e = config_entry_add_loader_auto(config, device, root_dir, NULL,
2045 L"auto-windows", 'w', title ?: L"Windows Boot Manager",
2046 L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2047
2048 if (config->reboot_for_bitlocker)
2049 e->call = boot_windows_bitlocker;
2050 #endif
2051 }
2052
2053 static void config_entry_add_unified(
2054 Config *config,
2055 EFI_HANDLE *device,
2056 EFI_FILE *root_dir) {
2057
2058 _cleanup_(file_closep) EFI_FILE *linux_dir = NULL;
2059 _cleanup_free_ EFI_FILE_INFO *f = NULL;
2060 UINTN f_size = 0;
2061 EFI_STATUS err;
2062
2063 /* Adds Boot Loader Type #2 entries (i.e. /EFI/Linux/….efi) */
2064
2065 assert(config);
2066 assert(device);
2067 assert(root_dir);
2068
2069 err = open_directory(root_dir, L"\\EFI\\Linux", &linux_dir);
2070 if (err != EFI_SUCCESS)
2071 return;
2072
2073 for (;;) {
2074 enum {
2075 SECTION_CMDLINE,
2076 SECTION_OSREL,
2077 _SECTION_MAX,
2078 };
2079
2080 static const char * const sections[_SECTION_MAX + 1] = {
2081 [SECTION_CMDLINE] = ".cmdline",
2082 [SECTION_OSREL] = ".osrel",
2083 NULL,
2084 };
2085
2086 _cleanup_free_ char16_t *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
2087 *os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL;
2088 const char16_t *good_name, *good_version, *good_sort_key;
2089 _cleanup_free_ char *content = NULL;
2090 UINTN offs[_SECTION_MAX] = {};
2091 UINTN szs[_SECTION_MAX] = {};
2092 char *line;
2093 UINTN pos = 0;
2094 char *key, *value;
2095
2096 err = readdir_harder(linux_dir, &f, &f_size);
2097 if (err != EFI_SUCCESS || !f)
2098 break;
2099
2100 if (f->FileName[0] == '.')
2101 continue;
2102 if (FLAGS_SET(f->Attribute, EFI_FILE_DIRECTORY))
2103 continue;
2104 if (!endswith_no_case(f->FileName, L".efi"))
2105 continue;
2106 if (startswith(f->FileName, L"auto-"))
2107 continue;
2108
2109 /* look for .osrel and .cmdline sections in the .efi binary */
2110 err = pe_file_locate_sections(linux_dir, f->FileName, sections, offs, szs);
2111 if (err != EFI_SUCCESS || szs[SECTION_OSREL] == 0)
2112 continue;
2113
2114 err = file_read(linux_dir, f->FileName, offs[SECTION_OSREL], szs[SECTION_OSREL], &content, NULL);
2115 if (err != EFI_SUCCESS)
2116 continue;
2117
2118 /* read properties from the embedded os-release file */
2119 while ((line = line_get_key_value(content, "=", &pos, &key, &value))) {
2120 if (streq8(key, "PRETTY_NAME")) {
2121 free(os_pretty_name);
2122 os_pretty_name = xstra_to_str(value);
2123 continue;
2124 }
2125
2126 if (streq8(key, "IMAGE_ID")) {
2127 free(os_image_id);
2128 os_image_id = xstra_to_str(value);
2129 continue;
2130 }
2131
2132 if (streq8(key, "NAME")) {
2133 free(os_name);
2134 os_name = xstra_to_str(value);
2135 continue;
2136 }
2137
2138 if (streq8(key, "ID")) {
2139 free(os_id);
2140 os_id = xstra_to_str(value);
2141 continue;
2142 }
2143
2144 if (streq8(key, "IMAGE_VERSION")) {
2145 free(os_image_version);
2146 os_image_version = xstra_to_str(value);
2147 continue;
2148 }
2149
2150 if (streq8(key, "VERSION")) {
2151 free(os_version);
2152 os_version = xstra_to_str(value);
2153 continue;
2154 }
2155
2156 if (streq8(key, "VERSION_ID")) {
2157 free(os_version_id);
2158 os_version_id = xstra_to_str(value);
2159 continue;
2160 }
2161
2162 if (streq8(key, "BUILD_ID")) {
2163 free(os_build_id);
2164 os_build_id = xstra_to_str(value);
2165 continue;
2166 }
2167 }
2168
2169 if (!bootspec_pick_name_version_sort_key(
2170 os_pretty_name,
2171 os_image_id,
2172 os_name,
2173 os_id,
2174 os_image_version,
2175 os_version,
2176 os_version_id,
2177 os_build_id,
2178 &good_name,
2179 &good_version,
2180 &good_sort_key))
2181 continue;
2182
2183 ConfigEntry *entry = xnew(ConfigEntry, 1);
2184 *entry = (ConfigEntry) {
2185 .id = xstrdup16(f->FileName),
2186 .type = LOADER_UNIFIED_LINUX,
2187 .title = xstrdup16(good_name),
2188 .version = xstrdup16(good_version),
2189 .device = device,
2190 .loader = xpool_print(L"\\EFI\\Linux\\%s", f->FileName),
2191 .sort_key = xstrdup16(good_sort_key),
2192 .key = 'l',
2193 .tries_done = -1,
2194 .tries_left = -1,
2195 };
2196
2197 strtolower16(entry->id);
2198 config_add_entry(config, entry);
2199 config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
2200
2201 if (szs[SECTION_CMDLINE] == 0)
2202 continue;
2203
2204 content = mfree(content);
2205
2206 /* read the embedded cmdline file */
2207 err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, NULL);
2208 if (err == EFI_SUCCESS) {
2209 /* chomp the newline */
2210 if (content[szs[SECTION_CMDLINE] - 1] == '\n')
2211 content[szs[SECTION_CMDLINE] - 1] = '\0';
2212
2213 entry->options = xstra_to_str(content);
2214 }
2215 }
2216 }
2217
2218 static void config_load_xbootldr(
2219 Config *config,
2220 EFI_HANDLE *device) {
2221
2222 _cleanup_(file_closep) EFI_FILE *root_dir = NULL;
2223 EFI_HANDLE new_device;
2224 EFI_STATUS err;
2225
2226 assert(config);
2227 assert(device);
2228
2229 err = xbootldr_open(device, &new_device, &root_dir);
2230 if (err != EFI_SUCCESS)
2231 return;
2232
2233 config_entry_add_unified(config, new_device, root_dir);
2234 config_load_entries(config, new_device, root_dir, NULL);
2235 }
2236
2237 static EFI_STATUS initrd_prepare(
2238 EFI_FILE *root,
2239 const ConfigEntry *entry,
2240 char16_t **ret_options,
2241 void **ret_initrd,
2242 UINTN *ret_initrd_size) {
2243
2244 assert(root);
2245 assert(entry);
2246 assert(ret_options);
2247 assert(ret_initrd);
2248 assert(ret_initrd_size);
2249
2250 if (entry->type != LOADER_LINUX || !entry->initrd) {
2251 ret_options = NULL;
2252 ret_initrd = NULL;
2253 ret_initrd_size = 0;
2254 return EFI_SUCCESS;
2255 }
2256
2257 /* Note that order of initrds matters. The kernel will only look for microcode updates in the very
2258 * first one it sees. */
2259
2260 /* Add initrd= to options for older kernels that do not support LINUX_INITRD_MEDIA. Should be dropped
2261 * if linux_x86.c is dropped. */
2262 _cleanup_free_ char16_t *options = NULL;
2263
2264 EFI_STATUS err;
2265 UINTN size = 0;
2266 _cleanup_free_ uint8_t *initrd = NULL;
2267
2268 STRV_FOREACH(i, entry->initrd) {
2269 _cleanup_free_ char16_t *o = options;
2270 if (o)
2271 options = xpool_print(L"%s initrd=%s", o, *i);
2272 else
2273 options = xpool_print(L"initrd=%s", *i);
2274
2275 _cleanup_(file_closep) EFI_FILE *handle = NULL;
2276 err = root->Open(root, &handle, *i, EFI_FILE_MODE_READ, 0);
2277 if (err != EFI_SUCCESS)
2278 return err;
2279
2280 _cleanup_free_ EFI_FILE_INFO *info = NULL;
2281 err = get_file_info_harder(handle, &info, NULL);
2282 if (err != EFI_SUCCESS)
2283 return err;
2284
2285 UINTN new_size, read_size = info->FileSize;
2286 if (__builtin_add_overflow(size, read_size, &new_size))
2287 return EFI_OUT_OF_RESOURCES;
2288 initrd = xrealloc(initrd, size, new_size);
2289
2290 err = handle->Read(handle, &read_size, initrd + size);
2291 if (err != EFI_SUCCESS)
2292 return err;
2293
2294 /* Make sure the actual read size is what we expected. */
2295 assert(size + read_size == new_size);
2296 size = new_size;
2297 }
2298
2299 if (entry->options) {
2300 _cleanup_free_ char16_t *o = options;
2301 options = xpool_print(L"%s %s", o, entry->options);
2302 }
2303
2304 *ret_options = TAKE_PTR(options);
2305 *ret_initrd = TAKE_PTR(initrd);
2306 *ret_initrd_size = size;
2307 return EFI_SUCCESS;
2308 }
2309
2310 static EFI_STATUS image_start(
2311 EFI_HANDLE parent_image,
2312 const ConfigEntry *entry) {
2313
2314 _cleanup_(devicetree_cleanup) struct devicetree_state dtstate = {};
2315 _cleanup_(unload_imagep) EFI_HANDLE image = NULL;
2316 _cleanup_free_ EFI_DEVICE_PATH *path = NULL;
2317 EFI_STATUS err;
2318
2319 assert(entry);
2320
2321 /* If this loader entry has a special way to boot, try that first. */
2322 if (entry->call)
2323 (void) entry->call();
2324
2325 _cleanup_(file_closep) EFI_FILE *image_root = NULL;
2326 err = open_volume(entry->device, &image_root);
2327 if (err != EFI_SUCCESS)
2328 return log_error_status_stall(err, L"Error opening root path: %r", err);
2329
2330 err = make_file_device_path(entry->device, entry->loader, &path);
2331 if (err != EFI_SUCCESS)
2332 return log_error_status_stall(err, L"Error making file device path: %r", err);
2333
2334 UINTN initrd_size = 0;
2335 _cleanup_free_ void *initrd = NULL;
2336 _cleanup_free_ char16_t *options_initrd = NULL;
2337 err = initrd_prepare(image_root, entry, &options_initrd, &initrd, &initrd_size);
2338 if (err != EFI_SUCCESS)
2339 return log_error_status_stall(err, L"Error preparing initrd: %r", err);
2340
2341 err = BS->LoadImage(false, parent_image, path, NULL, 0, &image);
2342 if (err != EFI_SUCCESS)
2343 return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err);
2344
2345 if (entry->devicetree) {
2346 err = devicetree_install(&dtstate, image_root, entry->devicetree);
2347 if (err != EFI_SUCCESS)
2348 return log_error_status_stall(err, L"Error loading %s: %r", entry->devicetree, err);
2349 }
2350
2351 _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
2352 err = initrd_register(initrd, initrd_size, &initrd_handle);
2353 if (err != EFI_SUCCESS)
2354 return log_error_status_stall(err, L"Error registering initrd: %r", err);
2355
2356 EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
2357 err = BS->HandleProtocol(image, &LoadedImageProtocol, (void **) &loaded_image);
2358 if (err != EFI_SUCCESS)
2359 return log_error_status_stall(err, L"Error getting LoadedImageProtocol handle: %r", err);
2360
2361 char16_t *options = options_initrd ?: entry->options;
2362 if (options) {
2363 loaded_image->LoadOptions = options;
2364 loaded_image->LoadOptionsSize = strsize16(options);
2365
2366 /* Try to log any options to the TPM, especially to catch manually edited options */
2367 (void) tpm_log_load_options(options, NULL);
2368 }
2369
2370 efivar_set_time_usec(LOADER_GUID, L"LoaderTimeExecUSec", 0);
2371 err = BS->StartImage(image, NULL, NULL);
2372 graphics_mode(false);
2373 if (err == EFI_SUCCESS)
2374 return EFI_SUCCESS;
2375
2376 /* Try calling the kernel compat entry point if one exists. */
2377 if (err == EFI_UNSUPPORTED && entry->type == LOADER_LINUX) {
2378 uint32_t kernel_entry_address;
2379
2380 err = pe_alignment_info(loaded_image->ImageBase, &kernel_entry_address, NULL, NULL);
2381 if (err != EFI_SUCCESS) {
2382 if (err != EFI_UNSUPPORTED)
2383 return log_error_status_stall(err, L"Error finding kernel compat entry address: %r", err);
2384 } else {
2385 EFI_IMAGE_ENTRY_POINT kernel_entry =
2386 (EFI_IMAGE_ENTRY_POINT) ((uint8_t *) loaded_image->ImageBase + kernel_entry_address);
2387
2388 err = kernel_entry(image, ST);
2389 graphics_mode(false);
2390 if (err == EFI_SUCCESS)
2391 return EFI_SUCCESS;
2392 }
2393 }
2394
2395 return log_error_status_stall(err, L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err);
2396 }
2397
2398 static void config_free(Config *config) {
2399 assert(config);
2400 for (UINTN i = 0; i < config->entry_count; i++)
2401 config_entry_free(config->entries[i]);
2402 free(config->entries);
2403 free(config->entry_default_config);
2404 free(config->entry_oneshot);
2405 }
2406
2407 static void config_write_entries_to_variable(Config *config) {
2408 _cleanup_free_ char *buffer = NULL;
2409 UINTN sz = 0;
2410 char *p;
2411
2412 assert(config);
2413
2414 for (UINTN i = 0; i < config->entry_count; i++)
2415 sz += strsize16(config->entries[i]->id);
2416
2417 p = buffer = xmalloc(sz);
2418
2419 for (UINTN i = 0; i < config->entry_count; i++) {
2420 UINTN l;
2421
2422 l = strsize16(config->entries[i]->id);
2423 memcpy(p, config->entries[i]->id, l);
2424
2425 p += l;
2426 }
2427
2428 assert(p == buffer + sz);
2429
2430 /* Store the full list of discovered entries. */
2431 (void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, sz, 0);
2432 }
2433
2434 static void save_selected_entry(const Config *config, const ConfigEntry *entry) {
2435 assert(config);
2436 assert(entry);
2437 assert(entry->loader || !entry->call);
2438
2439 /* Always export the selected boot entry to the system in a volatile var. */
2440 (void) efivar_set(LOADER_GUID, L"LoaderEntrySelected", entry->id, 0);
2441
2442 /* Do not save or delete if this was a oneshot boot. */
2443 if (streq16(config->entry_oneshot, entry->id))
2444 return;
2445
2446 if (config->use_saved_entry_efivar || (!config->entry_default_efivar && config->use_saved_entry)) {
2447 /* Avoid unnecessary NVRAM writes. */
2448 if (streq16(config->entry_saved, entry->id))
2449 return;
2450
2451 (void) efivar_set(LOADER_GUID, L"LoaderEntryLastBooted", entry->id, EFI_VARIABLE_NON_VOLATILE);
2452 } else
2453 /* Delete the non-volatile var if not needed. */
2454 (void) efivar_set(LOADER_GUID, L"LoaderEntryLastBooted", NULL, EFI_VARIABLE_NON_VOLATILE);
2455 }
2456
2457 static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir) {
2458 EFI_STATUS err;
2459 _cleanup_(file_closep) EFI_FILE *keys_basedir = NULL;
2460
2461 if (secure_boot_mode() != SECURE_BOOT_SETUP)
2462 return EFI_SUCCESS;
2463
2464 /* the lack of a 'keys' directory is not fatal and is silently ignored */
2465 err = open_directory(root_dir, u"\\loader\\keys", &keys_basedir);
2466 if (err == EFI_NOT_FOUND)
2467 return EFI_SUCCESS;
2468 if (err != EFI_SUCCESS)
2469 return err;
2470
2471 for (;;) {
2472 _cleanup_free_ EFI_FILE_INFO *dirent = NULL;
2473 size_t dirent_size = 0;
2474 ConfigEntry *entry = NULL;
2475
2476 err = readdir_harder(keys_basedir, &dirent, &dirent_size);
2477 if (err != EFI_SUCCESS || !dirent)
2478 return err;
2479
2480 if (dirent->FileName[0] == '.')
2481 continue;
2482
2483 if (!FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY))
2484 continue;
2485
2486 entry = xnew(ConfigEntry, 1);
2487 *entry = (ConfigEntry) {
2488 .id = xpool_print(L"secure-boot-keys-%s", dirent->FileName),
2489 .title = xpool_print(L"Enroll Secure Boot keys: %s", dirent->FileName),
2490 .path = xpool_print(L"\\loader\\keys\\%s", dirent->FileName),
2491 .type = LOADER_SECURE_BOOT_KEYS,
2492 .tries_done = -1,
2493 .tries_left = -1,
2494 };
2495 config_add_entry(config, entry);
2496
2497 if (config->secure_boot_enroll == ENROLL_FORCE && strcaseeq16(dirent->FileName, u"auto"))
2498 /* if we auto enroll successfully this call does not return, if it fails we still
2499 * want to add other potential entries to the menu */
2500 secure_boot_enroll_at(root_dir, entry->path);
2501 }
2502
2503 return EFI_SUCCESS;
2504 }
2505
2506 static void export_variables(
2507 EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
2508 const char16_t *loaded_image_path,
2509 uint64_t init_usec) {
2510
2511 static const uint64_t loader_features =
2512 EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
2513 EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT |
2514 EFI_LOADER_FEATURE_ENTRY_DEFAULT |
2515 EFI_LOADER_FEATURE_ENTRY_ONESHOT |
2516 EFI_LOADER_FEATURE_BOOT_COUNTING |
2517 EFI_LOADER_FEATURE_XBOOTLDR |
2518 EFI_LOADER_FEATURE_RANDOM_SEED |
2519 EFI_LOADER_FEATURE_LOAD_DRIVER |
2520 EFI_LOADER_FEATURE_SORT_KEY |
2521 EFI_LOADER_FEATURE_SAVED_ENTRY |
2522 EFI_LOADER_FEATURE_DEVICETREE |
2523 0;
2524
2525 _cleanup_free_ char16_t *infostr = NULL, *typestr = NULL;
2526 char16_t uuid[37];
2527
2528 assert(loaded_image);
2529 assert(loaded_image_path);
2530
2531 efivar_set_time_usec(LOADER_GUID, L"LoaderTimeInitUSec", init_usec);
2532 efivar_set(LOADER_GUID, L"LoaderInfo", L"systemd-boot " GIT_VERSION, 0);
2533
2534 infostr = xpool_print(L"%s %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
2535 efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", infostr, 0);
2536
2537 typestr = xpool_print(L"UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
2538 efivar_set(LOADER_GUID, L"LoaderFirmwareType", typestr, 0);
2539
2540 (void) efivar_set_uint64_le(LOADER_GUID, L"LoaderFeatures", loader_features, 0);
2541
2542 /* the filesystem path to this image, to prevent adding ourselves to the menu */
2543 efivar_set(LOADER_GUID, L"LoaderImageIdentifier", loaded_image_path, 0);
2544
2545 /* export the device path this image is started from */
2546 if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
2547 efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0);
2548 }
2549
2550 static void config_load_all_entries(
2551 Config *config,
2552 EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
2553 const char16_t *loaded_image_path,
2554 EFI_FILE *root_dir) {
2555
2556 assert(config);
2557 assert(loaded_image);
2558 assert(loaded_image_path);
2559 assert(root_dir);
2560
2561 config_load_defaults(config, root_dir);
2562
2563 /* scan /EFI/Linux/ directory */
2564 config_entry_add_unified(config, loaded_image->DeviceHandle, root_dir);
2565
2566 /* scan /loader/entries/\*.conf files */
2567 config_load_entries(config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
2568
2569 /* Similar, but on any XBOOTLDR partition */
2570 config_load_xbootldr(config, loaded_image->DeviceHandle);
2571
2572 /* sort entries after version number */
2573 sort_pointer_array((void **) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare);
2574
2575 /* if we find some well-known loaders, add them to the end of the list */
2576 config_entry_add_osx(config);
2577 config_entry_add_windows(config, loaded_image->DeviceHandle, root_dir);
2578 config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, NULL,
2579 L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
2580 config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2581 L"auto-efi-default", '\0', L"EFI Default Loader", NULL);
2582
2583 if (config->auto_firmware && FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) {
2584 ConfigEntry *entry = xnew(ConfigEntry, 1);
2585 *entry = (ConfigEntry) {
2586 .id = xstrdup16(u"auto-reboot-to-firmware-setup"),
2587 .title = xstrdup16(u"Reboot Into Firmware Interface"),
2588 .call = reboot_into_firmware,
2589 .tries_done = -1,
2590 .tries_left = -1,
2591 };
2592 config_add_entry(config, entry);
2593 }
2594
2595 /* find if secure boot signing keys exist and autoload them if necessary
2596 otherwise creates menu entries so that the user can load them manually
2597 if the secure-boot-enroll variable is set to no (the default), we do not
2598 even search for keys on the ESP */
2599 if (config->secure_boot_enroll != ENROLL_OFF)
2600 secure_boot_discover_keys(config, root_dir);
2601
2602 if (config->entry_count == 0)
2603 return;
2604
2605 config_write_entries_to_variable(config);
2606
2607 config_title_generate(config);
2608
2609 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
2610 config_default_entry_select(config);
2611 }
2612
2613 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
2614 EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
2615 _cleanup_(file_closep) EFI_FILE *root_dir = NULL;
2616 _cleanup_(config_free) Config config = {};
2617 char16_t *loaded_image_path;
2618 EFI_STATUS err;
2619 uint64_t init_usec;
2620 bool menu = false;
2621
2622 InitializeLib(image, sys_table);
2623 init_usec = time_usec();
2624 debug_hook(L"systemd-boot");
2625 /* Uncomment the next line if you need to wait for debugger. */
2626 // debug_break();
2627
2628 err = BS->OpenProtocol(image,
2629 &LoadedImageProtocol,
2630 (void **)&loaded_image,
2631 image,
2632 NULL,
2633 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2634 if (err != EFI_SUCCESS)
2635 return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
2636
2637 loaded_image_path = DevicePathToStr(loaded_image->FilePath);
2638 if (!loaded_image_path)
2639 return log_oom();
2640
2641 export_variables(loaded_image, loaded_image_path, init_usec);
2642
2643 err = open_volume(loaded_image->DeviceHandle, &root_dir);
2644 if (err != EFI_SUCCESS)
2645 return log_error_status_stall(err, L"Unable to open root directory: %r", err);
2646
2647 if (secure_boot_enabled() && shim_loaded()) {
2648 err = security_policy_install();
2649 if (err != EFI_SUCCESS)
2650 return log_error_status_stall(err, L"Error installing security policy: %r", err);
2651 }
2652
2653 (void) load_drivers(image, loaded_image, root_dir);
2654
2655 config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir);
2656
2657 if (config.entry_count == 0) {
2658 log_error_stall(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2659 goto out;
2660 }
2661
2662 /* select entry or show menu when key is pressed or timeout is set */
2663 if (config.force_menu || config.timeout_sec > 0)
2664 menu = true;
2665 else {
2666 uint64_t key;
2667
2668 /* Block up to 100ms to give firmware time to get input working. */
2669 err = console_key_read(&key, 100 * 1000);
2670 if (err == EFI_SUCCESS) {
2671 /* find matching key in config entries */
2672 UINTN idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key));
2673 if (idx != IDX_INVALID)
2674 config.idx_default = idx;
2675 else
2676 menu = true;
2677 }
2678 }
2679
2680 for (;;) {
2681 ConfigEntry *entry;
2682
2683 entry = config.entries[config.idx_default];
2684 if (menu) {
2685 efivar_set_time_usec(LOADER_GUID, L"LoaderTimeMenuUSec", 0);
2686 if (!menu_run(&config, &entry, loaded_image_path))
2687 break;
2688 }
2689
2690 /* if auto enrollment is activated, we try to load keys for the given entry. */
2691 if (entry->type == LOADER_SECURE_BOOT_KEYS && config.secure_boot_enroll != ENROLL_OFF) {
2692 err = secure_boot_enroll_at(root_dir, entry->path);
2693 if (err != EFI_SUCCESS)
2694 return err;
2695 continue;
2696 }
2697
2698 /* Run special entry like "reboot" now. Those that have a loader
2699 * will be handled by image_start() instead. */
2700 if (entry->call && !entry->loader) {
2701 entry->call();
2702 continue;
2703 }
2704
2705 config_entry_bump_counters(entry, root_dir);
2706 save_selected_entry(&config, entry);
2707
2708 /* Optionally, read a random seed off the ESP and pass it to the OS */
2709 (void) process_random_seed(root_dir, config.random_seed_mode);
2710
2711 err = image_start(image, entry);
2712 if (err != EFI_SUCCESS)
2713 goto out;
2714
2715 menu = true;
2716 config.timeout_sec = 0;
2717 }
2718 err = EFI_SUCCESS;
2719 out:
2720 BS->CloseProtocol(image, &LoadedImageProtocol, image, NULL);
2721 return err;
2722 }