]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/boot.c
Merge pull request #11933 from keszybz/coverity
[thirdparty/systemd.git] / src / boot / efi / boot.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <efi.h>
4 #include <efigpt.h>
5 #include <efilib.h>
6
7 #include "console.h"
8 #include "crc32.h"
9 #include "disk.h"
10 #include "graphics.h"
11 #include "linux.h"
12 #include "measure.h"
13 #include "pe.h"
14 #include "shim.h"
15 #include "util.h"
16
17 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
18 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
19 #endif
20
21 /* magic string to find in the binary image */
22 static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
23
24 static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
25
26 enum loader_type {
27 LOADER_UNDEFINED,
28 LOADER_EFI,
29 LOADER_LINUX,
30 };
31
32 typedef struct {
33 CHAR16 *id; /* The identifier for this entry (note that this id is not necessarily unique though!) */
34 CHAR16 *title_show;
35 CHAR16 *title;
36 CHAR16 *version;
37 CHAR16 *machine_id;
38 EFI_HANDLE *device;
39 enum loader_type type;
40 CHAR16 *loader;
41 CHAR16 *options;
42 CHAR16 key;
43 EFI_STATUS (*call)(VOID);
44 BOOLEAN no_autoselect;
45 BOOLEAN non_unique;
46 UINTN tries_done;
47 UINTN tries_left;
48 CHAR16 *path;
49 CHAR16 *current_name;
50 CHAR16 *next_name;
51 } ConfigEntry;
52
53 typedef struct {
54 ConfigEntry **entries;
55 UINTN entry_count;
56 INTN idx_default;
57 INTN idx_default_efivar;
58 UINTN timeout_sec;
59 UINTN timeout_sec_config;
60 INTN timeout_sec_efivar;
61 CHAR16 *entry_default_pattern;
62 CHAR16 *entry_oneshot;
63 CHAR16 *options_edit;
64 BOOLEAN editor;
65 BOOLEAN auto_entries;
66 BOOLEAN auto_firmware;
67 BOOLEAN force_menu;
68 UINTN console_mode;
69 enum console_mode_change_type console_mode_change;
70 } Config;
71
72 static VOID cursor_left(UINTN *cursor, UINTN *first) {
73 if ((*cursor) > 0)
74 (*cursor)--;
75 else if ((*first) > 0)
76 (*first)--;
77 }
78
79 static VOID cursor_right(
80 UINTN *cursor,
81 UINTN *first,
82 UINTN x_max,
83 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(
92 CHAR16 *line_in,
93 CHAR16 **line_out,
94 UINTN x_max,
95 UINTN y_pos) {
96
97 _cleanup_freepool_ CHAR16 *line = NULL, *print = NULL;
98 UINTN size, len, first, cursor, clear;
99 BOOLEAN exit, enter;
100
101 if (!line_in)
102 line_in = L"";
103 size = StrLen(line_in) + 1024;
104 line = AllocatePool(size * sizeof(CHAR16));
105 StrCpy(line, line_in);
106 len = StrLen(line);
107 print = AllocatePool((x_max+1) * sizeof(CHAR16));
108
109 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
110
111 first = 0;
112 cursor = 0;
113 clear = 0;
114 enter = FALSE;
115 exit = FALSE;
116 while (!exit) {
117 EFI_STATUS err;
118 UINT64 key;
119 UINTN i;
120
121 i = len - first;
122 if (i >= x_max-1)
123 i = x_max-1;
124 CopyMem(print, line + first, i * sizeof(CHAR16));
125 while (clear > 0 && i < x_max-1) {
126 clear--;
127 print[i++] = ' ';
128 }
129 print[i] = '\0';
130
131 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
132 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
133 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
134
135 err = console_key_read(&key, TRUE);
136 if (EFI_ERROR(err))
137 continue;
138
139 switch (key) {
140 case KEYPRESS(0, SCAN_ESC, 0):
141 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
142 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
143 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
144 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
145 exit = TRUE;
146 break;
147
148 case KEYPRESS(0, SCAN_HOME, 0):
149 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
150 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
151 /* beginning-of-line */
152 cursor = 0;
153 first = 0;
154 continue;
155
156 case KEYPRESS(0, SCAN_END, 0):
157 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
158 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
159 /* end-of-line */
160 cursor = len - first;
161 if (cursor+1 >= x_max) {
162 cursor = x_max-1;
163 first = len - (x_max-1);
164 }
165 continue;
166
167 case KEYPRESS(0, SCAN_DOWN, 0):
168 case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
169 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
170 /* forward-word */
171 while (line[first + cursor] == ' ')
172 cursor_right(&cursor, &first, x_max, len);
173 while (line[first + cursor] && line[first + cursor] != ' ')
174 cursor_right(&cursor, &first, x_max, len);
175 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
176 continue;
177
178 case KEYPRESS(0, SCAN_UP, 0):
179 case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
180 case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
181 /* backward-word */
182 if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
183 cursor_left(&cursor, &first);
184 while ((first + cursor) > 0 && line[first + cursor] == ' ')
185 cursor_left(&cursor, &first);
186 }
187 while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
188 cursor_left(&cursor, &first);
189 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
190 continue;
191
192 case KEYPRESS(0, SCAN_RIGHT, 0):
193 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
194 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
195 /* forward-char */
196 if (first + cursor == len)
197 continue;
198 cursor_right(&cursor, &first, x_max, len);
199 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
200 continue;
201
202 case KEYPRESS(0, SCAN_LEFT, 0):
203 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
204 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
205 /* backward-char */
206 cursor_left(&cursor, &first);
207 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
208 continue;
209
210 case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
211 /* kill-word */
212 clear = 0;
213 for (i = first + cursor; i < len && line[i] == ' '; i++)
214 clear++;
215 for (; i < len && line[i] != ' '; i++)
216 clear++;
217
218 for (i = first + cursor; i + clear < len; i++)
219 line[i] = line[i + clear];
220 len -= clear;
221 line[len] = '\0';
222 continue;
223
224 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'):
225 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')):
226 case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE):
227 /* backward-kill-word */
228 clear = 0;
229 if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
230 cursor_left(&cursor, &first);
231 clear++;
232 while ((first + cursor) > 0 && line[first + cursor] == ' ') {
233 cursor_left(&cursor, &first);
234 clear++;
235 }
236 }
237 while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
238 cursor_left(&cursor, &first);
239 clear++;
240 }
241 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
242
243 for (i = first + cursor; i + clear < len; i++)
244 line[i] = line[i + clear];
245 len -= clear;
246 line[len] = '\0';
247 continue;
248
249 case KEYPRESS(0, SCAN_DELETE, 0):
250 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
251 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
252 if (len == 0)
253 continue;
254 if (first + cursor == len)
255 continue;
256 for (i = first + cursor; i < len; i++)
257 line[i] = line[i+1];
258 clear = 1;
259 len--;
260 continue;
261
262 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
263 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
264 /* kill-line */
265 line[first + cursor] = '\0';
266 clear = len - (first + cursor);
267 len = first + cursor;
268 continue;
269
270 case KEYPRESS(0, 0, CHAR_LINEFEED):
271 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
272 if (StrCmp(line, line_in) != 0) {
273 *line_out = line;
274 line = NULL;
275 }
276 enter = TRUE;
277 exit = TRUE;
278 break;
279
280 case KEYPRESS(0, 0, CHAR_BACKSPACE):
281 if (len == 0)
282 continue;
283 if (first == 0 && cursor == 0)
284 continue;
285 for (i = first + cursor-1; i < len; i++)
286 line[i] = line[i+1];
287 clear = 1;
288 len--;
289 if (cursor > 0)
290 cursor--;
291 if (cursor > 0 || first == 0)
292 continue;
293 /* show full line if it fits */
294 if (len < x_max) {
295 cursor = first;
296 first = 0;
297 continue;
298 }
299 /* jump left to see what we delete */
300 if (first > 10) {
301 first -= 10;
302 cursor = 10;
303 } else {
304 cursor = first;
305 first = 0;
306 }
307 continue;
308
309 case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
310 case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
311 if (len+1 == size)
312 continue;
313 for (i = len; i > first + cursor; i--)
314 line[i] = line[i-1];
315 line[first + cursor] = KEYCHAR(key);
316 len++;
317 line[len] = '\0';
318 if (cursor+1 < x_max)
319 cursor++;
320 else if (first + cursor < len)
321 first++;
322 continue;
323 }
324 }
325
326 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
327 return enter;
328 }
329
330 static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
331 UINTN i;
332
333 if (key == 0)
334 return -1;
335
336 /* select entry by number key */
337 if (key >= '1' && key <= '9') {
338 i = key - '0';
339 if (i > config->entry_count)
340 i = config->entry_count;
341 return i-1;
342 }
343
344 /* find matching key in config entries */
345 for (i = start; i < config->entry_count; i++)
346 if (config->entries[i]->key == key)
347 return i;
348
349 for (i = 0; i < start; i++)
350 if (config->entries[i]->key == key)
351 return i;
352
353 return -1;
354 }
355
356 static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
357 UINT64 key;
358 UINTN i;
359 _cleanup_freepool_ CHAR8 *bootvar = NULL, *modevar = NULL, *indvar = NULL;
360 _cleanup_freepool_ CHAR16 *partstr = NULL, *defaultstr = NULL;
361 UINTN x, y, size;
362
363 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
364 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
365
366 Print(L"systemd-boot version: " GIT_VERSION "\n");
367 Print(L"architecture: " EFI_MACHINE_TYPE_NAME "\n");
368 Print(L"loaded image: %s\n", loaded_image_path);
369 Print(L"UEFI specification: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
370 Print(L"firmware vendor: %s\n", ST->FirmwareVendor);
371 Print(L"firmware version: %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
372
373 if (uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x, &y) == EFI_SUCCESS)
374 Print(L"console size: %d x %d\n", x, y);
375
376 if (efivar_get_raw(&global_guid, L"SecureBoot", &bootvar, &size) == EFI_SUCCESS)
377 Print(L"SecureBoot: %s\n", yes_no(*bootvar > 0));
378
379 if (efivar_get_raw(&global_guid, L"SetupMode", &modevar, &size) == EFI_SUCCESS)
380 Print(L"SetupMode: %s\n", *modevar > 0 ? L"setup" : L"user");
381
382 if (shim_loaded())
383 Print(L"Shim: present\n");
384
385 if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &indvar, &size) == EFI_SUCCESS)
386 Print(L"OsIndicationsSupported: %d\n", (UINT64)*indvar);
387
388 Print(L"\n--- press key ---\n\n");
389 console_key_read(&key, TRUE);
390
391 Print(L"timeout: %u\n", config->timeout_sec);
392 if (config->timeout_sec_efivar >= 0)
393 Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar);
394 Print(L"timeout (config): %u\n", config->timeout_sec_config);
395 if (config->entry_default_pattern)
396 Print(L"default pattern: '%s'\n", config->entry_default_pattern);
397 Print(L"editor: %s\n", yes_no(config->editor));
398 Print(L"auto-entries: %s\n", yes_no(config->auto_entries));
399 Print(L"auto-firmware: %s\n", yes_no(config->auto_firmware));
400 Print(L"\n");
401
402 Print(L"config entry count: %d\n", config->entry_count);
403 Print(L"entry selected idx: %d\n", config->idx_default);
404 if (config->idx_default_efivar >= 0)
405 Print(L"entry EFI var idx: %d\n", config->idx_default_efivar);
406 Print(L"\n");
407
408 if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS)
409 Print(L"LoaderConfigTimeout: %u\n", i);
410
411 if (config->entry_oneshot)
412 Print(L"LoaderEntryOneShot: %s\n", config->entry_oneshot);
413 if (efivar_get(L"LoaderDevicePartUUID", &partstr) == EFI_SUCCESS)
414 Print(L"LoaderDevicePartUUID: %s\n", partstr);
415 if (efivar_get(L"LoaderEntryDefault", &defaultstr) == EFI_SUCCESS)
416 Print(L"LoaderEntryDefault: %s\n", defaultstr);
417
418 Print(L"\n--- press key ---\n\n");
419 console_key_read(&key, TRUE);
420
421 for (i = 0; i < config->entry_count; i++) {
422 ConfigEntry *entry;
423
424 if (key == KEYPRESS(0, SCAN_ESC, 0) || key == KEYPRESS(0, 0, 'q'))
425 break;
426
427 entry = config->entries[i];
428 Print(L"config entry: %d/%d\n", i+1, config->entry_count);
429 if (entry->id)
430 Print(L"id '%s'\n", entry->id);
431 Print(L"title show '%s'\n", entry->title_show);
432 if (entry->title)
433 Print(L"title '%s'\n", entry->title);
434 if (entry->version)
435 Print(L"version '%s'\n", entry->version);
436 if (entry->machine_id)
437 Print(L"machine-id '%s'\n", entry->machine_id);
438 if (entry->device) {
439 EFI_DEVICE_PATH *device_path;
440
441 device_path = DevicePathFromHandle(entry->device);
442 if (device_path) {
443 _cleanup_freepool_ CHAR16 *str;
444
445 str = DevicePathToStr(device_path);
446 Print(L"device handle '%s'\n", str);
447 }
448 }
449 if (entry->loader)
450 Print(L"loader '%s'\n", entry->loader);
451 if (entry->options)
452 Print(L"options '%s'\n", entry->options);
453 Print(L"auto-select %s\n", yes_no(!entry->no_autoselect));
454 if (entry->call)
455 Print(L"internal call yes\n");
456
457 if (entry->tries_left != (UINTN) -1)
458 Print(L"counting boots yes\n"
459 "tries done %u\n"
460 "tries left %u\n"
461 "current path %s\\%s\n"
462 "next path %s\\%s\n",
463 entry->tries_done,
464 entry->tries_left,
465 entry->path, entry->current_name,
466 entry->path, entry->next_name);
467
468 Print(L"\n--- press key ---\n\n");
469 console_key_read(&key, TRUE);
470 }
471
472 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
473 }
474
475 static BOOLEAN menu_run(
476 Config *config,
477 ConfigEntry **chosen_entry,
478 CHAR16 *loaded_image_path) {
479
480 EFI_STATUS err;
481 UINTN visible_max;
482 UINTN idx_highlight;
483 UINTN idx_highlight_prev;
484 UINTN idx_first;
485 UINTN idx_last;
486 BOOLEAN refresh;
487 BOOLEAN highlight;
488 UINTN i;
489 UINTN line_width;
490 CHAR16 **lines;
491 UINTN x_start;
492 UINTN y_start;
493 UINTN x_max;
494 UINTN y_max;
495 CHAR16 *status;
496 CHAR16 *clearline;
497 INTN timeout_remain;
498 INT16 idx;
499 BOOLEAN exit = FALSE;
500 BOOLEAN run = TRUE;
501 BOOLEAN wait = FALSE;
502 BOOLEAN cleared_screen = FALSE;
503
504 graphics_mode(FALSE);
505 uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
506 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
507 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
508
509 /* draw a single character to make ClearScreen work on some firmware */
510 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L" ");
511
512 if (config->console_mode_change != CONSOLE_MODE_KEEP) {
513 err = console_set_mode(&config->console_mode, config->console_mode_change);
514 if (!EFI_ERROR(err))
515 cleared_screen = TRUE;
516 }
517
518 if (!cleared_screen)
519 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
520
521 if (config->console_mode_change != CONSOLE_MODE_KEEP && EFI_ERROR(err))
522 Print(L"Error switching console mode to %ld: %r.\r", (UINT64)config->console_mode, err);
523
524 err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
525 if (EFI_ERROR(err)) {
526 x_max = 80;
527 y_max = 25;
528 }
529
530 /* we check 10 times per second for a keystroke */
531 if (config->timeout_sec > 0)
532 timeout_remain = config->timeout_sec * 10;
533 else
534 timeout_remain = -1;
535
536 idx_highlight = config->idx_default;
537 idx_highlight_prev = 0;
538
539 visible_max = y_max - 2;
540
541 if ((UINTN)config->idx_default >= visible_max)
542 idx_first = config->idx_default-1;
543 else
544 idx_first = 0;
545
546 idx_last = idx_first + visible_max-1;
547
548 refresh = TRUE;
549 highlight = FALSE;
550
551 /* length of the longest entry */
552 line_width = 5;
553 for (i = 0; i < config->entry_count; i++) {
554 UINTN entry_len;
555
556 entry_len = StrLen(config->entries[i]->title_show);
557 if (line_width < entry_len)
558 line_width = entry_len;
559 }
560 if (line_width > x_max-6)
561 line_width = x_max-6;
562
563 /* offsets to center the entries on the screen */
564 x_start = (x_max - (line_width)) / 2;
565 if (config->entry_count < visible_max)
566 y_start = ((visible_max - config->entry_count) / 2) + 1;
567 else
568 y_start = 0;
569
570 /* menu entries title lines */
571 lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
572 for (i = 0; i < config->entry_count; i++) {
573 UINTN j, k;
574
575 lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
576 for (j = 0; j < x_start; j++)
577 lines[i][j] = ' ';
578
579 for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
580 lines[i][j] = config->entries[i]->title_show[k];
581
582 for (; j < x_max; j++)
583 lines[i][j] = ' ';
584 lines[i][x_max] = '\0';
585 }
586
587 status = NULL;
588 clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
589 for (i = 0; i < x_max; i++)
590 clearline[i] = ' ';
591 clearline[i] = 0;
592
593 while (!exit) {
594 UINT64 key;
595
596 if (refresh) {
597 for (i = 0; i < config->entry_count; i++) {
598 if (i < idx_first || i > idx_last)
599 continue;
600 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
601 if (i == idx_highlight)
602 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
603 EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
604 else
605 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
606 EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
607 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
608 if ((INTN)i == config->idx_default_efivar) {
609 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
610 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
611 }
612 }
613 refresh = FALSE;
614 } else if (highlight) {
615 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
616 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
617 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
618 if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
619 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
620 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
621 }
622
623 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
624 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
625 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
626 if ((INTN)idx_highlight == config->idx_default_efivar) {
627 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
628 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
629 }
630 highlight = FALSE;
631 }
632
633 if (timeout_remain > 0) {
634 FreePool(status);
635 status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
636 }
637
638 /* print status at last line of screen */
639 if (status) {
640 UINTN len;
641 UINTN x;
642
643 /* center line */
644 len = StrLen(status);
645 if (len < x_max)
646 x = (x_max - len) / 2;
647 else
648 x = 0;
649 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
650 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
651 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
652 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
653 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
654 }
655
656 err = console_key_read(&key, wait);
657 if (EFI_ERROR(err)) {
658 /* timeout reached */
659 if (timeout_remain == 0) {
660 exit = TRUE;
661 break;
662 }
663
664 /* sleep and update status */
665 if (timeout_remain > 0) {
666 uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
667 timeout_remain--;
668 continue;
669 }
670
671 /* timeout disabled, wait for next key */
672 wait = TRUE;
673 continue;
674 }
675
676 timeout_remain = -1;
677
678 /* clear status after keystroke */
679 if (status) {
680 FreePool(status);
681 status = NULL;
682 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
683 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
684 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
685 }
686
687 idx_highlight_prev = idx_highlight;
688
689 switch (key) {
690 case KEYPRESS(0, SCAN_UP, 0):
691 case KEYPRESS(0, 0, 'k'):
692 if (idx_highlight > 0)
693 idx_highlight--;
694 break;
695
696 case KEYPRESS(0, SCAN_DOWN, 0):
697 case KEYPRESS(0, 0, 'j'):
698 if (idx_highlight < config->entry_count-1)
699 idx_highlight++;
700 break;
701
702 case KEYPRESS(0, SCAN_HOME, 0):
703 case KEYPRESS(EFI_ALT_PRESSED, 0, '<'):
704 if (idx_highlight > 0) {
705 refresh = TRUE;
706 idx_highlight = 0;
707 }
708 break;
709
710 case KEYPRESS(0, SCAN_END, 0):
711 case KEYPRESS(EFI_ALT_PRESSED, 0, '>'):
712 if (idx_highlight < config->entry_count-1) {
713 refresh = TRUE;
714 idx_highlight = config->entry_count-1;
715 }
716 break;
717
718 case KEYPRESS(0, SCAN_PAGE_UP, 0):
719 if (idx_highlight > visible_max)
720 idx_highlight -= visible_max;
721 else
722 idx_highlight = 0;
723 break;
724
725 case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
726 idx_highlight += visible_max;
727 if (idx_highlight > config->entry_count-1)
728 idx_highlight = config->entry_count-1;
729 break;
730
731 case KEYPRESS(0, 0, CHAR_LINEFEED):
732 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
733 exit = TRUE;
734 break;
735
736 case KEYPRESS(0, SCAN_F1, 0):
737 case KEYPRESS(0, 0, 'h'):
738 case KEYPRESS(0, 0, '?'):
739 status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
740 break;
741
742 case KEYPRESS(0, 0, 'Q'):
743 exit = TRUE;
744 run = FALSE;
745 break;
746
747 case KEYPRESS(0, 0, 'd'):
748 if (config->idx_default_efivar != (INTN)idx_highlight) {
749 /* store the selected entry in a persistent EFI variable */
750 efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->id, TRUE);
751 config->idx_default_efivar = idx_highlight;
752 status = StrDuplicate(L"Default boot entry selected.");
753 } else {
754 /* clear the default entry EFI variable */
755 efivar_set(L"LoaderEntryDefault", NULL, TRUE);
756 config->idx_default_efivar = -1;
757 status = StrDuplicate(L"Default boot entry cleared.");
758 }
759 refresh = TRUE;
760 break;
761
762 case KEYPRESS(0, 0, '-'):
763 case KEYPRESS(0, 0, 'T'):
764 if (config->timeout_sec_efivar > 0) {
765 config->timeout_sec_efivar--;
766 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
767 if (config->timeout_sec_efivar > 0)
768 status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
769 else
770 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
771 } else if (config->timeout_sec_efivar <= 0){
772 config->timeout_sec_efivar = -1;
773 efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
774 if (config->timeout_sec_config > 0)
775 status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
776 config->timeout_sec_config);
777 else
778 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
779 }
780 break;
781
782 case KEYPRESS(0, 0, '+'):
783 case KEYPRESS(0, 0, 't'):
784 if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
785 config->timeout_sec_efivar++;
786 config->timeout_sec_efivar++;
787 efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
788 if (config->timeout_sec_efivar > 0)
789 status = PoolPrint(L"Menu timeout set to %d sec.",
790 config->timeout_sec_efivar);
791 else
792 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
793 break;
794
795 case KEYPRESS(0, 0, 'e'):
796 /* only the options of configured entries can be edited */
797 if (!config->editor || config->entries[idx_highlight]->type == LOADER_UNDEFINED)
798 break;
799 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
800 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
801 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
802 if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
803 exit = TRUE;
804 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
805 uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
806 break;
807
808 case KEYPRESS(0, 0, 'v'):
809 status = PoolPrint(L"systemd-boot " GIT_VERSION " (" EFI_MACHINE_TYPE_NAME "), UEFI Specification %d.%02d, Vendor %s %d.%02d",
810 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff,
811 ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
812 break;
813
814 case KEYPRESS(0, 0, 'P'):
815 print_status(config, loaded_image_path);
816 refresh = TRUE;
817 break;
818
819 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
820 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
821 refresh = TRUE;
822 break;
823
824 default:
825 /* jump with a hotkey directly to a matching entry */
826 idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
827 if (idx < 0)
828 break;
829 idx_highlight = idx;
830 refresh = TRUE;
831 }
832
833 if (idx_highlight > idx_last) {
834 idx_last = idx_highlight;
835 idx_first = 1 + idx_highlight - visible_max;
836 refresh = TRUE;
837 } else if (idx_highlight < idx_first) {
838 idx_first = idx_highlight;
839 idx_last = idx_highlight + visible_max-1;
840 refresh = TRUE;
841 }
842
843 if (!refresh && idx_highlight != idx_highlight_prev)
844 highlight = TRUE;
845 }
846
847 *chosen_entry = config->entries[idx_highlight];
848
849 for (i = 0; i < config->entry_count; i++)
850 FreePool(lines[i]);
851 FreePool(lines);
852 FreePool(clearline);
853
854 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
855 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
856 return run;
857 }
858
859 static VOID config_add_entry(Config *config, ConfigEntry *entry) {
860 if ((config->entry_count & 15) == 0) {
861 UINTN i;
862
863 i = config->entry_count + 16;
864 if (config->entry_count == 0)
865 config->entries = AllocatePool(sizeof(VOID *) * i);
866 else
867 config->entries = ReallocatePool(config->entries,
868 sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
869 }
870 config->entries[config->entry_count++] = entry;
871 }
872
873 static VOID config_entry_free(ConfigEntry *entry) {
874 if (!entry)
875 return;
876
877 FreePool(entry->id);
878 FreePool(entry->title_show);
879 FreePool(entry->title);
880 FreePool(entry->version);
881 FreePool(entry->machine_id);
882 FreePool(entry->loader);
883 FreePool(entry->options);
884 FreePool(entry->path);
885 FreePool(entry->current_name);
886 FreePool(entry->next_name);
887 FreePool(entry);
888 }
889
890 static BOOLEAN is_digit(CHAR16 c) {
891 return (c >= '0') && (c <= '9');
892 }
893
894 static UINTN c_order(CHAR16 c) {
895 if (c == '\0')
896 return 0;
897 if (is_digit(c))
898 return 0;
899 else if ((c >= 'a') && (c <= 'z'))
900 return c;
901 else
902 return c + 0x10000;
903 }
904
905 static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2) {
906 CHAR16 *os1 = s1;
907 CHAR16 *os2 = s2;
908
909 while (*s1 || *s2) {
910 INTN first;
911
912 while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
913 INTN order;
914
915 order = c_order(*s1) - c_order(*s2);
916 if (order != 0)
917 return order;
918 s1++;
919 s2++;
920 }
921
922 while (*s1 == '0')
923 s1++;
924 while (*s2 == '0')
925 s2++;
926
927 first = 0;
928 while (is_digit(*s1) && is_digit(*s2)) {
929 if (first == 0)
930 first = *s1 - *s2;
931 s1++;
932 s2++;
933 }
934
935 if (is_digit(*s1))
936 return 1;
937 if (is_digit(*s2))
938 return -1;
939
940 if (first != 0)
941 return first;
942 }
943
944 return StrCmp(os1, os2);
945 }
946
947 static CHAR8 *line_get_key_value(
948 CHAR8 *content,
949 CHAR8 *sep,
950 UINTN *pos,
951 CHAR8 **key_ret,
952 CHAR8 **value_ret) {
953
954 CHAR8 *line;
955 UINTN linelen;
956 CHAR8 *value;
957
958 skip:
959 line = content + *pos;
960 if (*line == '\0')
961 return NULL;
962
963 linelen = 0;
964 while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
965 linelen++;
966
967 /* move pos to next line */
968 *pos += linelen;
969 if (content[*pos])
970 (*pos)++;
971
972 /* empty line */
973 if (linelen == 0)
974 goto skip;
975
976 /* terminate line */
977 line[linelen] = '\0';
978
979 /* remove leading whitespace */
980 while (strchra((CHAR8 *)" \t", *line)) {
981 line++;
982 linelen--;
983 }
984
985 /* remove trailing whitespace */
986 while (linelen > 0 && strchra((CHAR8 *)" \t", line[linelen-1]))
987 linelen--;
988 line[linelen] = '\0';
989
990 if (*line == '#')
991 goto skip;
992
993 /* split key/value */
994 value = line;
995 while (*value && !strchra(sep, *value))
996 value++;
997 if (*value == '\0')
998 goto skip;
999 *value = '\0';
1000 value++;
1001 while (*value && strchra(sep, *value))
1002 value++;
1003
1004 /* unquote */
1005 if (value[0] == '"' && line[linelen-1] == '"') {
1006 value++;
1007 line[linelen-1] = '\0';
1008 }
1009
1010 *key_ret = line;
1011 *value_ret = value;
1012 return line;
1013 }
1014
1015 static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
1016 CHAR8 *line;
1017 UINTN pos = 0;
1018 CHAR8 *key, *value;
1019
1020 while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
1021 if (strcmpa((CHAR8 *)"timeout", key) == 0) {
1022 _cleanup_freepool_ CHAR16 *s = NULL;
1023
1024 s = stra_to_str(value);
1025 config->timeout_sec_config = Atoi(s);
1026 config->timeout_sec = config->timeout_sec_config;
1027 continue;
1028 }
1029
1030 if (strcmpa((CHAR8 *)"default", key) == 0) {
1031 FreePool(config->entry_default_pattern);
1032 config->entry_default_pattern = stra_to_str(value);
1033 StrLwr(config->entry_default_pattern);
1034 continue;
1035 }
1036
1037 if (strcmpa((CHAR8 *)"editor", key) == 0) {
1038 BOOLEAN on;
1039
1040 if (EFI_ERROR(parse_boolean(value, &on)))
1041 continue;
1042 config->editor = on;
1043 }
1044
1045 if (strcmpa((CHAR8 *)"auto-entries", key) == 0) {
1046 BOOLEAN on;
1047
1048 if (EFI_ERROR(parse_boolean(value, &on)))
1049 continue;
1050 config->auto_entries = on;
1051 }
1052
1053 if (strcmpa((CHAR8 *)"auto-firmware", key) == 0) {
1054 BOOLEAN on;
1055
1056 if (EFI_ERROR(parse_boolean(value, &on)))
1057 continue;
1058 config->auto_firmware = on;
1059 }
1060
1061 if (strcmpa((CHAR8 *)"console-mode", key) == 0) {
1062 if (strcmpa((CHAR8 *)"auto", value) == 0)
1063 config->console_mode_change = CONSOLE_MODE_AUTO;
1064 else if (strcmpa((CHAR8 *)"max", value) == 0)
1065 config->console_mode_change = CONSOLE_MODE_MAX;
1066 else if (strcmpa((CHAR8 *)"keep", value) == 0)
1067 config->console_mode_change = CONSOLE_MODE_KEEP;
1068 else {
1069 _cleanup_freepool_ CHAR16 *s = NULL;
1070
1071 s = stra_to_str(value);
1072 config->console_mode = Atoi(s);
1073 config->console_mode_change = CONSOLE_MODE_SET;
1074 }
1075
1076 continue;
1077 }
1078 }
1079 }
1080
1081 static VOID config_entry_parse_tries(
1082 ConfigEntry *entry,
1083 CHAR16 *path,
1084 CHAR16 *file,
1085 CHAR16 *suffix) {
1086
1087 UINTN left = (UINTN) -1, done = (UINTN) -1, factor = 1, i, next_left, next_done;
1088 _cleanup_freepool_ CHAR16 *prefix = NULL;
1089
1090 /*
1091 * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
1092 * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
1093 * which case that counter as assumed to be zero, i.e. the missing part is synonymous to "-0").
1094 *
1095 * Names we grok, and the series they result in:
1096 *
1097 * foobar+3.efi → foobar+2-1.efi → foobar+1-2.efi → foobar+0-3.efi → STOP!
1098 * foobar+4-0.efi → foobar+3-1.efi → foobar+2-2.efi → foobar+1-3.efi → foobar+0-4.efi → STOP!
1099 */
1100
1101 i = StrLen(file);
1102
1103 /* Chop off any suffix such as ".conf" or ".efi" */
1104 if (suffix) {
1105 UINTN suffix_length;
1106
1107 suffix_length = StrLen(suffix);
1108 if (i < suffix_length)
1109 return;
1110
1111 i -= suffix_length;
1112 }
1113
1114 /* Go backwards through the string and parse everything we encounter */
1115 for (;;) {
1116 if (i == 0)
1117 return;
1118
1119 i--;
1120
1121 switch (file[i]) {
1122
1123 case '+':
1124 if (left == (UINTN) -1) /* didn't read at least one digit for 'left'? */
1125 return;
1126
1127 if (done == (UINTN) -1) /* no 'done' counter? If so, it's equivalent to 0 */
1128 done = 0;
1129
1130 goto good;
1131
1132 case '-':
1133 if (left == (UINTN) -1) /* didn't parse any digit yet? */
1134 return;
1135
1136 if (done != (UINTN) -1) /* already encountered a dash earlier? */
1137 return;
1138
1139 /* So we encountered a dash. This means this counter is of the form +LEFT-DONE. Let's assign
1140 * what we already parsed to 'done', and start fresh for the 'left' part. */
1141
1142 done = left;
1143 left = (UINTN) -1;
1144 factor = 1;
1145 break;
1146
1147 case '0'...'9': {
1148 UINTN new_factor;
1149
1150 if (left == (UINTN) -1)
1151 left = file[i] - '0';
1152 else {
1153 UINTN new_left, digit;
1154
1155 digit = file[i] - '0';
1156 if (digit > (UINTN) -1 / factor) /* overflow check */
1157 return;
1158
1159 new_left = left + digit * factor;
1160 if (new_left < left) /* overflow check */
1161 return;
1162
1163 if (new_left == (UINTN) -1) /* don't allow us to be confused */
1164 return;
1165 }
1166
1167 new_factor = factor * 10;
1168 if (new_factor < factor) /* overflow chck */
1169 return;
1170
1171 factor = new_factor;
1172 break;
1173 }
1174
1175 default:
1176 return;
1177 }
1178 }
1179
1180 good:
1181 entry->tries_left = left;
1182 entry->tries_done = done;
1183
1184 entry->path = StrDuplicate(path);
1185 entry->current_name = StrDuplicate(file);
1186
1187 next_left = left <= 0 ? 0 : left - 1;
1188 next_done = done >= (UINTN) -2 ? (UINTN) -2 : done + 1;
1189
1190 prefix = StrDuplicate(file);
1191 prefix[i] = 0;
1192
1193 entry->next_name = PoolPrint(L"%s+%u-%u%s", prefix, next_left, next_done, suffix ?: L"");
1194 }
1195
1196 static VOID config_entry_bump_counters(
1197 ConfigEntry *entry,
1198 EFI_FILE_HANDLE root_dir) {
1199
1200 _cleanup_freepool_ CHAR16* old_path = NULL, *new_path = NULL;
1201 _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
1202 static EFI_GUID EfiFileInfoGuid = EFI_FILE_INFO_ID;
1203 _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL;
1204 UINTN file_info_size, a, b;
1205 EFI_STATUS r;
1206
1207 if (entry->tries_left == (UINTN) -1)
1208 return;
1209
1210 if (!entry->path || !entry->current_name || !entry->next_name)
1211 return;
1212
1213 old_path = PoolPrint(L"%s\\%s", entry->path, entry->current_name);
1214
1215 r = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, old_path, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL);
1216 if (EFI_ERROR(r))
1217 return;
1218
1219 a = StrLen(entry->current_name);
1220 b = StrLen(entry->next_name);
1221
1222 file_info_size = OFFSETOF(EFI_FILE_INFO, FileName) + (a > b ? a : b) + 1;
1223
1224 for (;;) {
1225 file_info = AllocatePool(file_info_size);
1226
1227 r = uefi_call_wrapper(handle->GetInfo, 4, handle, &EfiFileInfoGuid, &file_info_size, file_info);
1228 if (!EFI_ERROR(r))
1229 break;
1230
1231 if (r != EFI_BUFFER_TOO_SMALL || file_info_size * 2 < file_info_size) {
1232 Print(L"\nFailed to get file info for '%s': %r\n", old_path, r);
1233 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1234 return;
1235 }
1236
1237 file_info_size *= 2;
1238 FreePool(file_info);
1239 }
1240
1241 /* And rename the file */
1242 StrCpy(file_info->FileName, entry->next_name);
1243 r = uefi_call_wrapper(handle->SetInfo, 4, handle, &EfiFileInfoGuid, file_info_size, file_info);
1244 if (EFI_ERROR(r)) {
1245 Print(L"\nFailed to rename '%s' to '%s', ignoring: %r\n", old_path, entry->next_name, r);
1246 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
1247 return;
1248 }
1249
1250 /* Flush everything to disk, just in case… */
1251 (void) uefi_call_wrapper(handle->Flush, 1, handle);
1252
1253 /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
1254 * success */
1255 new_path = PoolPrint(L"%s\\%s", entry->path, entry->next_name);
1256 efivar_set(L"LoaderBootCountPath", new_path, FALSE);
1257
1258 /* If the file we just renamed is the loader path, then let's update that. */
1259 if (StrCmp(entry->loader, old_path) == 0) {
1260 FreePool(entry->loader);
1261 entry->loader = new_path;
1262 new_path = NULL;
1263 }
1264 }
1265
1266 static VOID config_entry_add_from_file(
1267 Config *config,
1268 EFI_HANDLE *device,
1269 CHAR16 *path,
1270 CHAR16 *file,
1271 CHAR8 *content,
1272 CHAR16 *loaded_image_path) {
1273
1274 ConfigEntry *entry;
1275 CHAR8 *line;
1276 UINTN pos = 0;
1277 CHAR8 *key, *value;
1278 UINTN len;
1279 _cleanup_freepool_ CHAR16 *initrd = NULL;
1280
1281 entry = AllocatePool(sizeof(ConfigEntry));
1282
1283 *entry = (ConfigEntry) {
1284 .tries_done = (UINTN) -1,
1285 .tries_left = (UINTN) -1,
1286 };
1287
1288 while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
1289 if (strcmpa((CHAR8 *)"title", key) == 0) {
1290 FreePool(entry->title);
1291 entry->title = stra_to_str(value);
1292 continue;
1293 }
1294
1295 if (strcmpa((CHAR8 *)"version", key) == 0) {
1296 FreePool(entry->version);
1297 entry->version = stra_to_str(value);
1298 continue;
1299 }
1300
1301 if (strcmpa((CHAR8 *)"machine-id", key) == 0) {
1302 FreePool(entry->machine_id);
1303 entry->machine_id = stra_to_str(value);
1304 continue;
1305 }
1306
1307 if (strcmpa((CHAR8 *)"linux", key) == 0) {
1308 FreePool(entry->loader);
1309 entry->type = LOADER_LINUX;
1310 entry->loader = stra_to_path(value);
1311 entry->key = 'l';
1312 continue;
1313 }
1314
1315 if (strcmpa((CHAR8 *)"efi", key) == 0) {
1316 entry->type = LOADER_EFI;
1317 FreePool(entry->loader);
1318 entry->loader = stra_to_path(value);
1319
1320 /* do not add an entry for ourselves */
1321 if (loaded_image_path && StriCmp(entry->loader, loaded_image_path) == 0) {
1322 entry->type = LOADER_UNDEFINED;
1323 break;
1324 }
1325 continue;
1326 }
1327
1328 if (strcmpa((CHAR8 *)"architecture", key) == 0) {
1329 /* do not add an entry for an EFI image of architecture not matching with that of the image */
1330 if (strcmpa((CHAR8 *)EFI_MACHINE_TYPE_NAME, value) != 0) {
1331 entry->type = LOADER_UNDEFINED;
1332 break;
1333 }
1334 continue;
1335 }
1336
1337 if (strcmpa((CHAR8 *)"initrd", key) == 0) {
1338 _cleanup_freepool_ CHAR16 *new = NULL;
1339
1340 new = stra_to_path(value);
1341 if (initrd) {
1342 CHAR16 *s;
1343
1344 s = PoolPrint(L"%s initrd=%s", initrd, new);
1345 FreePool(initrd);
1346 initrd = s;
1347 } else
1348 initrd = PoolPrint(L"initrd=%s", new);
1349
1350 continue;
1351 }
1352
1353 if (strcmpa((CHAR8 *)"options", key) == 0) {
1354 _cleanup_freepool_ CHAR16 *new = NULL;
1355
1356 new = stra_to_str(value);
1357 if (entry->options) {
1358 CHAR16 *s;
1359
1360 s = PoolPrint(L"%s %s", entry->options, new);
1361 FreePool(entry->options);
1362 entry->options = s;
1363 } else {
1364 entry->options = new;
1365 new = NULL;
1366 }
1367
1368 continue;
1369 }
1370 }
1371
1372 if (entry->type == LOADER_UNDEFINED) {
1373 config_entry_free(entry);
1374 return;
1375 }
1376
1377 /* add initrd= to options */
1378 if (entry->type == LOADER_LINUX && initrd) {
1379 if (entry->options) {
1380 CHAR16 *s;
1381
1382 s = PoolPrint(L"%s %s", initrd, entry->options);
1383 FreePool(entry->options);
1384 entry->options = s;
1385 } else {
1386 entry->options = initrd;
1387 initrd = NULL;
1388 }
1389 }
1390
1391 entry->device = device;
1392 entry->id = StrDuplicate(file);
1393 len = StrLen(entry->id);
1394 /* remove ".conf" */
1395 if (len > 5)
1396 entry->id[len - 5] = '\0';
1397 StrLwr(entry->id);
1398
1399 config_add_entry(config, entry);
1400
1401 config_entry_parse_tries(entry, path, file, L".conf");
1402 }
1403
1404 static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
1405 _cleanup_freepool_ CHAR8 *content = NULL;
1406 UINTN sec;
1407 EFI_STATUS err;
1408
1409 *config = (Config) {
1410 .editor = TRUE,
1411 .auto_entries = TRUE,
1412 .auto_firmware = TRUE,
1413 };
1414
1415 err = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content, NULL);
1416 if (!EFI_ERROR(err))
1417 config_defaults_load_from_file(config, content);
1418
1419 err = efivar_get_int(L"LoaderConfigTimeout", &sec);
1420 if (!EFI_ERROR(err)) {
1421 config->timeout_sec_efivar = sec > INTN_MAX ? INTN_MAX : sec;
1422 config->timeout_sec = sec;
1423 } else
1424 config->timeout_sec_efivar = -1;
1425
1426 err = efivar_get_int(L"LoaderConfigTimeoutOneShot", &sec);
1427 if (!EFI_ERROR(err)) {
1428 /* Unset variable now, after all it's "one shot". */
1429 (void) efivar_set(L"LoaderConfigTimeoutOneShot", NULL, TRUE);
1430
1431 config->timeout_sec = sec;
1432 config->force_menu = TRUE; /* force the menu when this is set */
1433 }
1434 }
1435
1436 static VOID config_load_entries(
1437 Config *config,
1438 EFI_HANDLE *device,
1439 EFI_FILE *root_dir,
1440 CHAR16 *loaded_image_path) {
1441
1442 EFI_FILE_HANDLE entries_dir;
1443 EFI_STATUS err;
1444
1445 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
1446 if (!EFI_ERROR(err)) {
1447 for (;;) {
1448 CHAR16 buf[256];
1449 UINTN bufsize;
1450 EFI_FILE_INFO *f;
1451 _cleanup_freepool_ CHAR8 *content = NULL;
1452 UINTN len;
1453
1454 bufsize = sizeof(buf);
1455 err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
1456 if (bufsize == 0 || EFI_ERROR(err))
1457 break;
1458
1459 f = (EFI_FILE_INFO *) buf;
1460 if (f->FileName[0] == '.')
1461 continue;
1462 if (f->Attribute & EFI_FILE_DIRECTORY)
1463 continue;
1464
1465 len = StrLen(f->FileName);
1466 if (len < 6)
1467 continue;
1468 if (StriCmp(f->FileName + len - 5, L".conf") != 0)
1469 continue;
1470 if (StrnCmp(f->FileName, L"auto-", 5) == 0)
1471 continue;
1472
1473 err = file_read(entries_dir, f->FileName, 0, 0, &content, NULL);
1474 if (!EFI_ERROR(err))
1475 config_entry_add_from_file(config, device, L"\\loader\\entries", f->FileName, content, loaded_image_path);
1476 }
1477 uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
1478 }
1479 }
1480
1481 static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
1482 INTN r;
1483
1484 /* Order entries that have no tries left to the end of the list */
1485 if (a->tries_left != 0 && b->tries_left == 0)
1486 return -1;
1487 if (a->tries_left == 0 && b->tries_left != 0)
1488 return 1;
1489
1490 r = str_verscmp(a->id, b->id);
1491 if (r != 0)
1492 return r;
1493
1494 if (a->tries_left == (UINTN) -1 ||
1495 b->tries_left == (UINTN) -1)
1496 return 0;
1497
1498 /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
1499 if (a->tries_left > b->tries_left)
1500 return -1;
1501 if (a->tries_left < b->tries_left)
1502 return 1;
1503
1504 /* If they have the same number of tries left, then let the one win which was tried fewer times so far */
1505 if (a->tries_done < b->tries_done)
1506 return -1;
1507 if (a->tries_done > b->tries_done)
1508 return 1;
1509
1510 return 0;
1511 }
1512
1513 static VOID config_sort_entries(Config *config) {
1514 UINTN i;
1515
1516 for (i = 1; i < config->entry_count; i++) {
1517 BOOLEAN more;
1518 UINTN k;
1519
1520 more = FALSE;
1521 for (k = 0; k < config->entry_count - i; k++) {
1522 ConfigEntry *entry;
1523
1524 if (config_entry_compare(config->entries[k], config->entries[k+1]) <= 0)
1525 continue;
1526
1527 entry = config->entries[k];
1528 config->entries[k] = config->entries[k+1];
1529 config->entries[k+1] = entry;
1530 more = TRUE;
1531 }
1532 if (!more)
1533 break;
1534 }
1535 }
1536
1537 static INTN config_entry_find(Config *config, CHAR16 *id) {
1538 UINTN i;
1539
1540 for (i = 0; i < config->entry_count; i++)
1541 if (StrCmp(config->entries[i]->id, id) == 0)
1542 return (INTN) i;
1543
1544 return -1;
1545 }
1546
1547 static VOID config_default_entry_select(Config *config) {
1548 _cleanup_freepool_ CHAR16 *entry_oneshot = NULL, *entry_default = NULL;
1549 EFI_STATUS err;
1550 INTN i;
1551
1552 /*
1553 * The EFI variable to specify a boot entry for the next, and only the
1554 * next reboot. The variable is always cleared directly after it is read.
1555 */
1556 err = efivar_get(L"LoaderEntryOneShot", &entry_oneshot);
1557 if (!EFI_ERROR(err)) {
1558
1559 config->entry_oneshot = StrDuplicate(entry_oneshot);
1560 efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
1561
1562 i = config_entry_find(config, entry_oneshot);
1563 if (i >= 0) {
1564 config->idx_default = i;
1565 return;
1566 }
1567 }
1568
1569 /*
1570 * The EFI variable to select the default boot entry overrides the
1571 * configured pattern. The variable can be set and cleared by pressing
1572 * the 'd' key in the loader selection menu, the entry is marked with
1573 * an '*'.
1574 */
1575 err = efivar_get(L"LoaderEntryDefault", &entry_default);
1576 if (!EFI_ERROR(err)) {
1577
1578 i = config_entry_find(config, entry_default);
1579 if (i >= 0) {
1580 config->idx_default = i;
1581 config->idx_default_efivar = i;
1582 return;
1583 }
1584 }
1585 config->idx_default_efivar = -1;
1586
1587 if (config->entry_count == 0)
1588 return;
1589
1590 /*
1591 * Match the pattern from the end of the list to the start, find last
1592 * entry (largest number) matching the given pattern.
1593 */
1594 if (config->entry_default_pattern) {
1595 i = config->entry_count;
1596 while (i--) {
1597 if (config->entries[i]->no_autoselect)
1598 continue;
1599 if (MetaiMatch(config->entries[i]->id, config->entry_default_pattern)) {
1600 config->idx_default = i;
1601 return;
1602 }
1603 }
1604 }
1605
1606 /* select the last suitable entry */
1607 i = config->entry_count;
1608 while (i--) {
1609 if (config->entries[i]->no_autoselect)
1610 continue;
1611 config->idx_default = i;
1612 return;
1613 }
1614
1615 /* no entry found */
1616 config->idx_default = -1;
1617 }
1618
1619 static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) {
1620 BOOLEAN non_unique = FALSE;
1621 UINTN i, k;
1622
1623 for (i = 0; i < entry_count; i++)
1624 entries[i]->non_unique = FALSE;
1625
1626 for (i = 0; i < entry_count; i++)
1627 for (k = 0; k < entry_count; k++) {
1628 if (i == k)
1629 continue;
1630 if (StrCmp(entries[i]->title_show, entries[k]->title_show) != 0)
1631 continue;
1632
1633 non_unique = entries[i]->non_unique = entries[k]->non_unique = TRUE;
1634 }
1635
1636 return non_unique;
1637 }
1638
1639 /* generate a unique title, avoiding non-distinguishable menu entries */
1640 static VOID config_title_generate(Config *config) {
1641 UINTN i;
1642
1643 /* set title */
1644 for (i = 0; i < config->entry_count; i++) {
1645 CHAR16 *title;
1646
1647 FreePool(config->entries[i]->title_show);
1648 title = config->entries[i]->title;
1649 if (!title)
1650 title = config->entries[i]->id;
1651 config->entries[i]->title_show = StrDuplicate(title);
1652 }
1653
1654 if (!find_nonunique(config->entries, config->entry_count))
1655 return;
1656
1657 /* add version to non-unique titles */
1658 for (i = 0; i < config->entry_count; i++) {
1659 CHAR16 *s;
1660
1661 if (!config->entries[i]->non_unique)
1662 continue;
1663 if (!config->entries[i]->version)
1664 continue;
1665
1666 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
1667 FreePool(config->entries[i]->title_show);
1668 config->entries[i]->title_show = s;
1669 }
1670
1671 if (!find_nonunique(config->entries, config->entry_count))
1672 return;
1673
1674 /* add machine-id to non-unique titles */
1675 for (i = 0; i < config->entry_count; i++) {
1676 CHAR16 *s;
1677 _cleanup_freepool_ CHAR16 *m = NULL;
1678
1679 if (!config->entries[i]->non_unique)
1680 continue;
1681 if (!config->entries[i]->machine_id)
1682 continue;
1683
1684 m = StrDuplicate(config->entries[i]->machine_id);
1685 m[8] = '\0';
1686 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m);
1687 FreePool(config->entries[i]->title_show);
1688 config->entries[i]->title_show = s;
1689 }
1690
1691 if (!find_nonunique(config->entries, config->entry_count))
1692 return;
1693
1694 /* add file name to non-unique titles */
1695 for (i = 0; i < config->entry_count; i++) {
1696 CHAR16 *s;
1697
1698 if (!config->entries[i]->non_unique)
1699 continue;
1700 s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->id);
1701 FreePool(config->entries[i]->title_show);
1702 config->entries[i]->title_show = s;
1703 config->entries[i]->non_unique = FALSE;
1704 }
1705 }
1706
1707 static BOOLEAN config_entry_add_call(
1708 Config *config,
1709 CHAR16 *id,
1710 CHAR16 *title,
1711 EFI_STATUS (*call)(VOID)) {
1712
1713 ConfigEntry *entry;
1714
1715 entry = AllocatePool(sizeof(ConfigEntry));
1716 *entry = (ConfigEntry) {
1717 .id = StrDuplicate(id),
1718 .title = StrDuplicate(title),
1719 .call = call,
1720 .no_autoselect = TRUE,
1721 .tries_done = (UINTN) -1,
1722 .tries_left = (UINTN) -1,
1723 };
1724
1725 config_add_entry(config, entry);
1726 return TRUE;
1727 }
1728
1729 static ConfigEntry *config_entry_add_loader(
1730 Config *config,
1731 EFI_HANDLE *device,
1732 enum loader_type type,
1733 CHAR16 *id,
1734 CHAR16 key,
1735 CHAR16 *title,
1736 CHAR16 *loader) {
1737
1738 ConfigEntry *entry;
1739
1740 entry = AllocatePool(sizeof(ConfigEntry));
1741 *entry = (ConfigEntry) {
1742 .type = type,
1743 .title = StrDuplicate(title),
1744 .device = device,
1745 .loader = StrDuplicate(loader),
1746 .id = StrDuplicate(id),
1747 .key = key,
1748 .tries_done = (UINTN) -1,
1749 .tries_left = (UINTN) -1,
1750 };
1751
1752 StrLwr(entry->id);
1753
1754 config_add_entry(config, entry);
1755 return entry;
1756 }
1757
1758 static BOOLEAN config_entry_add_loader_auto(
1759 Config *config,
1760 EFI_HANDLE *device,
1761 EFI_FILE *root_dir,
1762 CHAR16 *loaded_image_path,
1763 CHAR16 *id,
1764 CHAR16 key,
1765 CHAR16 *title,
1766 CHAR16 *loader) {
1767
1768 EFI_FILE_HANDLE handle;
1769 ConfigEntry *entry;
1770 EFI_STATUS err;
1771
1772 if (!config->auto_entries)
1773 return FALSE;
1774
1775 /* do not add an entry for ourselves */
1776 if (loaded_image_path) {
1777 UINTN len;
1778 _cleanup_freepool_ CHAR8 *content = NULL;
1779
1780 if (StriCmp(loader, loaded_image_path) == 0)
1781 return FALSE;
1782
1783 /* look for systemd-boot magic string */
1784 err = file_read(root_dir, loader, 0, 100*1024, &content, &len);
1785 if (!EFI_ERROR(err)) {
1786 CHAR8 *start = content;
1787 CHAR8 *last = content + len - sizeof(magic) - 1;
1788
1789 for (; start <= last; start++)
1790 if (start[0] == magic[0] && CompareMem(start, magic, sizeof(magic) - 1) == 0)
1791 return FALSE;
1792 }
1793 }
1794
1795 /* check existence */
1796 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL);
1797 if (EFI_ERROR(err))
1798 return FALSE;
1799 uefi_call_wrapper(handle->Close, 1, handle);
1800
1801 entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, id, key, title, loader);
1802 if (!entry)
1803 return FALSE;
1804
1805 /* do not boot right away into auto-detected entries */
1806 entry->no_autoselect = TRUE;
1807
1808 return TRUE;
1809 }
1810
1811 static VOID config_entry_add_osx(Config *config) {
1812 EFI_STATUS err;
1813 UINTN handle_count = 0;
1814 _cleanup_freepool_ EFI_HANDLE *handles = NULL;
1815
1816 if (!config->auto_entries)
1817 return;
1818
1819 err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles);
1820 if (!EFI_ERROR(err)) {
1821 UINTN i;
1822
1823 for (i = 0; i < handle_count; i++) {
1824 EFI_FILE *root;
1825 BOOLEAN found;
1826
1827 root = LibOpenRoot(handles[i]);
1828 if (!root)
1829 continue;
1830 found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"macOS",
1831 L"\\System\\Library\\CoreServices\\boot.efi");
1832 uefi_call_wrapper(root->Close, 1, root);
1833 if (found)
1834 break;
1835 }
1836 }
1837 }
1838
1839 static VOID config_entry_add_linux(
1840 Config *config,
1841 EFI_HANDLE *device,
1842 EFI_FILE *root_dir) {
1843
1844 EFI_FILE_HANDLE linux_dir;
1845 EFI_STATUS err;
1846 ConfigEntry *entry;
1847
1848 err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
1849 if (EFI_ERROR(err))
1850 return;
1851
1852 for (;;) {
1853 CHAR16 buf[256];
1854 UINTN bufsize = sizeof buf;
1855 EFI_FILE_INFO *f;
1856 CHAR8 *sections[] = {
1857 (UINT8 *)".osrel",
1858 (UINT8 *)".cmdline",
1859 NULL
1860 };
1861 UINTN offs[ELEMENTSOF(sections)-1] = {};
1862 UINTN szs[ELEMENTSOF(sections)-1] = {};
1863 UINTN addrs[ELEMENTSOF(sections)-1] = {};
1864 CHAR8 *content = NULL;
1865 UINTN len;
1866 CHAR8 *line;
1867 UINTN pos = 0;
1868 CHAR8 *key, *value;
1869 CHAR16 *os_name = NULL;
1870 CHAR16 *os_id = NULL;
1871 CHAR16 *os_version = NULL;
1872 CHAR16 *os_build = NULL;
1873
1874 err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf);
1875 if (bufsize == 0 || EFI_ERROR(err))
1876 break;
1877
1878 f = (EFI_FILE_INFO *) buf;
1879 if (f->FileName[0] == '.')
1880 continue;
1881 if (f->Attribute & EFI_FILE_DIRECTORY)
1882 continue;
1883 len = StrLen(f->FileName);
1884 if (len < 5)
1885 continue;
1886 if (StriCmp(f->FileName + len - 4, L".efi") != 0)
1887 continue;
1888
1889 /* look for .osrel and .cmdline sections in the .efi binary */
1890 err = pe_file_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
1891 if (EFI_ERROR(err))
1892 continue;
1893
1894 err = file_read(linux_dir, f->FileName, offs[0], szs[0], &content, NULL);
1895 if (EFI_ERROR(err))
1896 continue;
1897
1898 /* read properties from the embedded os-release file */
1899 while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
1900 if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
1901 FreePool(os_name);
1902 os_name = stra_to_str(value);
1903 continue;
1904 }
1905
1906 if (strcmpa((CHAR8 *)"ID", key) == 0) {
1907 FreePool(os_id);
1908 os_id = stra_to_str(value);
1909 continue;
1910 }
1911
1912 if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) {
1913 FreePool(os_version);
1914 os_version = stra_to_str(value);
1915 continue;
1916 }
1917
1918 if (strcmpa((CHAR8 *)"BUILD_ID", key) == 0) {
1919 FreePool(os_build);
1920 os_build = stra_to_str(value);
1921 continue;
1922 }
1923 }
1924
1925 if (os_name && os_id && (os_version || os_build)) {
1926 _cleanup_freepool_ CHAR16 *conf = NULL, *path = NULL;
1927
1928 conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build);
1929 path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
1930
1931 entry = config_entry_add_loader(config, device, LOADER_LINUX, conf, 'l', os_name, path);
1932
1933 FreePool(content);
1934 content = NULL;
1935
1936 /* read the embedded cmdline file */
1937 err = file_read(linux_dir, f->FileName, offs[1], szs[1], &content, NULL);
1938 if (!EFI_ERROR(err)) {
1939
1940 /* chomp the newline */
1941 if (content[szs[1]-1] == '\n')
1942 content[szs[1]-1] = '\0';
1943
1944 entry->options = stra_to_str(content);
1945 }
1946
1947 config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
1948 }
1949
1950 FreePool(os_name);
1951 FreePool(os_id);
1952 FreePool(os_version);
1953 FreePool(os_build);
1954 FreePool(content);
1955 }
1956
1957 uefi_call_wrapper(linux_dir->Close, 1, linux_dir);
1958 }
1959
1960 /* Note that this is in GUID format, i.e. the first 32bit, and the following pair of 16bit are byteswapped. */
1961 static const UINT8 xbootldr_guid[16] = {
1962 0xff, 0xc2, 0x13, 0xbc, 0xe6, 0x59, 0x62, 0x42, 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72
1963 };
1964
1965 EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) {
1966 EFI_DEVICE_PATH *parent;
1967 UINTN len;
1968
1969 len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
1970 parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH));
1971 CopyMem(parent, path, len);
1972 CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH));
1973
1974 return parent;
1975 }
1976
1977 static VOID config_load_xbootldr(
1978 Config *config,
1979 EFI_HANDLE *device) {
1980
1981 EFI_DEVICE_PATH *partition_path, *node, *disk_path, *copy;
1982 UINT32 found_partition_number = (UINT32) -1;
1983 UINT64 found_partition_start = (UINT64) -1;
1984 UINT64 found_partition_size = (UINT64) -1;
1985 UINT8 found_partition_signature[16] = {};
1986 EFI_HANDLE new_device;
1987 EFI_FILE *root_dir;
1988 EFI_STATUS r;
1989
1990 partition_path = DevicePathFromHandle(device);
1991 if (!partition_path)
1992 return;
1993
1994 for (node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
1995 EFI_HANDLE disk_handle;
1996 EFI_BLOCK_IO *block_io;
1997 EFI_DEVICE_PATH *p;
1998 UINTN nr;
1999
2000 /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media
2001 * devices */
2002 if (DevicePathType(node) != MESSAGING_DEVICE_PATH)
2003 continue;
2004
2005 /* Determine the device path one level up */
2006 disk_path = path_parent(partition_path, node);
2007 p = disk_path;
2008 r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &p, &disk_handle);
2009 if (EFI_ERROR(r))
2010 continue;
2011
2012 r = uefi_call_wrapper(BS->HandleProtocol, 3, disk_handle, &BlockIoProtocol, (VOID **)&block_io);
2013 if (EFI_ERROR(r))
2014 continue;
2015
2016 /* Filter out some block devices early. (We only care about block devices that aren't
2017 * partitions themselves — we look for GPT partition tables to parse after all —, and only
2018 * those which contain a medium and have at least 2 blocks.) */
2019 if (block_io->Media->LogicalPartition ||
2020 !block_io->Media->MediaPresent ||
2021 block_io->Media->LastBlock <= 1)
2022 continue;
2023
2024 /* Try both copies of the GPT header, in case one is corrupted */
2025 for (nr = 0; nr < 2; nr++) {
2026 _cleanup_freepool_ EFI_PARTITION_ENTRY* entries = NULL;
2027 union {
2028 EFI_PARTITION_TABLE_HEADER gpt_header;
2029 uint8_t space[((sizeof(EFI_PARTITION_TABLE_HEADER) + 511) / 512) * 512];
2030 } gpt_header_buffer;
2031 const EFI_PARTITION_TABLE_HEADER *h = &gpt_header_buffer.gpt_header;
2032 UINT64 where;
2033 UINTN i, sz;
2034 UINT32 c;
2035
2036 if (nr == 0)
2037 /* Read the first copy at LBA 1 */
2038 where = 1;
2039 else
2040 /* Read the second copy at the very last LBA of this block device */
2041 where = block_io->Media->LastBlock;
2042
2043 /* Read the GPT header */
2044 r = uefi_call_wrapper(block_io->ReadBlocks, 5,
2045 block_io,
2046 block_io->Media->MediaId,
2047 where,
2048 sizeof(gpt_header_buffer), &gpt_header_buffer);
2049 if (EFI_ERROR(r))
2050 continue;
2051
2052 /* Some superficial validation of the GPT header */
2053 c = CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature));
2054 if (c != 0)
2055 continue;
2056
2057 if (h->Header.HeaderSize < 92 ||
2058 h->Header.HeaderSize > 512)
2059 continue;
2060
2061 if (h->Header.Revision != 0x00010000U)
2062 continue;
2063
2064 /* Calculate CRC check */
2065 c = ~crc32_exclude_offset((UINT32) -1,
2066 (const UINT8*) &gpt_header_buffer,
2067 h->Header.HeaderSize,
2068 OFFSETOF(EFI_PARTITION_TABLE_HEADER, Header.CRC32),
2069 sizeof(h->Header.CRC32));
2070 if (c != h->Header.CRC32)
2071 continue;
2072
2073 if (h->MyLBA != where)
2074 continue;
2075
2076 if (h->SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY))
2077 continue;
2078
2079 if (h->NumberOfPartitionEntries <= 0 ||
2080 h->NumberOfPartitionEntries > 1024)
2081 continue;
2082
2083 if (h->SizeOfPartitionEntry > UINTN_MAX / h->NumberOfPartitionEntries) /* overflow check */
2084 continue;
2085
2086 /* Now load the GPT entry table */
2087 sz = ALIGN_TO((UINTN) h->SizeOfPartitionEntry * (UINTN) h->NumberOfPartitionEntries, 512);
2088 entries = AllocatePool(sz);
2089
2090 r = uefi_call_wrapper(block_io->ReadBlocks, 5,
2091 block_io,
2092 block_io->Media->MediaId,
2093 h->PartitionEntryLBA,
2094 sz, entries);
2095 if (EFI_ERROR(r))
2096 continue;
2097
2098 /* Calculate CRC of entries array, too */
2099 c = ~crc32((UINT32) -1, entries, sz);
2100 if (c != h->PartitionEntryArrayCRC32)
2101 continue;
2102
2103 for (i = 0; i < h->NumberOfPartitionEntries; i++) {
2104 EFI_PARTITION_ENTRY *entry;
2105
2106 entry = (EFI_PARTITION_ENTRY*) ((UINT8*) entries + h->SizeOfPartitionEntry * i);
2107
2108 if (CompareMem(&entry->PartitionTypeGUID, xbootldr_guid, 16) == 0) {
2109 UINT64 end;
2110
2111 /* Let's use memcpy(), in case the structs are not aligned (they really should be though) */
2112 CopyMem(&found_partition_start, &entry->StartingLBA, sizeof(found_partition_start));
2113 CopyMem(&end, &entry->EndingLBA, sizeof(end));
2114
2115 if (end < found_partition_start) /* Bogus? */
2116 continue;
2117
2118 found_partition_size = end - found_partition_start + 1;
2119 CopyMem(found_partition_signature, &entry->UniquePartitionGUID, sizeof(found_partition_signature));
2120
2121 found_partition_number = i + 1;
2122 goto found;
2123 }
2124 }
2125
2126 break; /* This GPT was fully valid, but we didn't find what we are looking for. This
2127 * means there's no reason to check the second copy of the GPT header */
2128 }
2129 }
2130
2131 return; /* Not found */
2132
2133 found:
2134 copy = DuplicateDevicePath(partition_path);
2135
2136 /* Patch in the data we found */
2137 for (node = copy; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
2138 HARDDRIVE_DEVICE_PATH *hd;
2139
2140 if (DevicePathType(node) != MEDIA_DEVICE_PATH)
2141 continue;
2142
2143 if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)
2144 continue;
2145
2146 hd = (HARDDRIVE_DEVICE_PATH*) node;
2147 hd->PartitionNumber = found_partition_number;
2148 hd->PartitionStart = found_partition_start;
2149 hd->PartitionSize = found_partition_size;
2150 CopyMem(hd->Signature, found_partition_signature, sizeof(hd->Signature));
2151 hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
2152 hd->SignatureType = SIGNATURE_TYPE_GUID;
2153 }
2154
2155 r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &copy, &new_device);
2156 if (EFI_ERROR(r))
2157 return;
2158
2159 root_dir = LibOpenRoot(new_device);
2160 if (!root_dir)
2161 return;
2162
2163 config_entry_add_linux(config, new_device, root_dir);
2164 config_load_entries(config, new_device, root_dir, NULL);
2165 }
2166
2167 static EFI_STATUS image_start(
2168 EFI_HANDLE parent_image,
2169 const Config *config,
2170 const ConfigEntry *entry) {
2171
2172 EFI_HANDLE image;
2173 _cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
2174 CHAR16 *options;
2175 EFI_STATUS err;
2176
2177 path = FileDevicePath(entry->device, entry->loader);
2178 if (!path) {
2179 Print(L"Error getting device path.");
2180 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2181 return EFI_INVALID_PARAMETER;
2182 }
2183
2184 err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
2185 if (EFI_ERROR(err)) {
2186 Print(L"Error loading %s: %r", entry->loader, err);
2187 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2188 return err;
2189 }
2190
2191 if (config->options_edit)
2192 options = config->options_edit;
2193 else if (entry->options)
2194 options = entry->options;
2195 else
2196 options = NULL;
2197 if (options) {
2198 EFI_LOADED_IMAGE *loaded_image;
2199
2200 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
2201 parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2202 if (EFI_ERROR(err)) {
2203 Print(L"Error getting LoadedImageProtocol handle: %r", err);
2204 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2205 goto out_unload;
2206 }
2207 loaded_image->LoadOptions = options;
2208 loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
2209
2210 #if ENABLE_TPM
2211 /* Try to log any options to the TPM, especially to catch manually edited options */
2212 err = tpm_log_event(SD_TPM_PCR,
2213 (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
2214 loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
2215 if (EFI_ERROR(err)) {
2216 Print(L"Unable to add image options measurement: %r", err);
2217 uefi_call_wrapper(BS->Stall, 1, 200 * 1000);
2218 }
2219 #endif
2220 }
2221
2222 efivar_set_time_usec(L"LoaderTimeExecUSec", 0);
2223 err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
2224 out_unload:
2225 uefi_call_wrapper(BS->UnloadImage, 1, image);
2226 return err;
2227 }
2228
2229 static EFI_STATUS reboot_into_firmware(VOID) {
2230 _cleanup_freepool_ CHAR8 *b = NULL;
2231 UINTN size;
2232 UINT64 osind;
2233 EFI_STATUS err;
2234
2235 osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
2236
2237 err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size);
2238 if (!EFI_ERROR(err))
2239 osind |= (UINT64)*b;
2240
2241 err = efivar_set_raw(&global_guid, L"OsIndications", &osind, sizeof(UINT64), TRUE);
2242 if (EFI_ERROR(err))
2243 return err;
2244
2245 err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
2246 Print(L"Error calling ResetSystem: %r", err);
2247 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2248 return err;
2249 }
2250
2251 static VOID config_free(Config *config) {
2252 UINTN i;
2253
2254 for (i = 0; i < config->entry_count; i++)
2255 config_entry_free(config->entries[i]);
2256 FreePool(config->entries);
2257 FreePool(config->entry_default_pattern);
2258 FreePool(config->options_edit);
2259 FreePool(config->entry_oneshot);
2260 }
2261
2262 static VOID config_write_entries_to_variable(Config *config) {
2263 _cleanup_freepool_ CHAR16 *buffer = NULL;
2264 UINTN i, sz = 0;
2265 CHAR16 *p;
2266
2267 for (i = 0; i < config->entry_count; i++)
2268 sz += StrLen(config->entries[i]->id) + 1;
2269
2270 p = buffer = AllocatePool(sz * sizeof(CHAR16));
2271
2272 for (i = 0; i < config->entry_count; i++) {
2273 UINTN l;
2274
2275 l = StrLen(config->entries[i]->id) + 1;
2276 CopyMem(p, config->entries[i]->id, l * sizeof(CHAR16));
2277
2278 p += l;
2279 }
2280
2281 /* Store the full list of discovered entries. */
2282 (void) efivar_set_raw(&loader_guid, L"LoaderEntries", buffer, (UINT8*) p - (UINT8*) buffer, FALSE);
2283 }
2284
2285 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
2286 static const UINT64 loader_features =
2287 (1ULL << 0) | /* I honour the LoaderConfigTimeout variable */
2288 (1ULL << 1) | /* I honour the LoaderConfigTimeoutOneShot variable */
2289 (1ULL << 2) | /* I honour the LoaderEntryDefault variable */
2290 (1ULL << 3) | /* I honour the LoaderEntryOneShot variable */
2291 (1ULL << 4) | /* I support boot counting */
2292 0;
2293
2294 _cleanup_freepool_ CHAR16 *infostr = NULL, *typestr = NULL;
2295 CHAR8 *b;
2296 UINTN size;
2297 EFI_LOADED_IMAGE *loaded_image;
2298 EFI_FILE *root_dir;
2299 CHAR16 *loaded_image_path;
2300 EFI_STATUS err;
2301 Config config;
2302 UINT64 init_usec;
2303 BOOLEAN menu = FALSE;
2304 CHAR16 uuid[37];
2305
2306 InitializeLib(image, sys_table);
2307 init_usec = time_usec();
2308 efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec);
2309 efivar_set(L"LoaderInfo", L"systemd-boot " GIT_VERSION, FALSE);
2310
2311 infostr = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
2312 efivar_set(L"LoaderFirmwareInfo", infostr, FALSE);
2313
2314 typestr = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
2315 efivar_set(L"LoaderFirmwareType", typestr, FALSE);
2316
2317 (void) efivar_set_raw(&loader_guid, L"LoaderFeatures", &loader_features, sizeof(loader_features), FALSE);
2318
2319 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
2320 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2321 if (EFI_ERROR(err)) {
2322 Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
2323 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2324 return err;
2325 }
2326
2327 /* export the device path this image is started from */
2328 if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
2329 efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
2330
2331 root_dir = LibOpenRoot(loaded_image->DeviceHandle);
2332 if (!root_dir) {
2333 Print(L"Unable to open root directory.");
2334 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2335 return EFI_LOAD_ERROR;
2336 }
2337
2338 if (secure_boot_enabled() && shim_loaded()) {
2339 err = security_policy_install();
2340 if (EFI_ERROR(err)) {
2341 Print(L"Error installing security policy: %r ", err);
2342 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2343 return err;
2344 }
2345 }
2346
2347 /* the filesystem path to this image, to prevent adding ourselves to the menu */
2348 loaded_image_path = DevicePathToStr(loaded_image->FilePath);
2349 efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
2350
2351 config_load_defaults(&config, root_dir);
2352
2353 /* scan /EFI/Linux/ directory */
2354 config_entry_add_linux(&config, loaded_image->DeviceHandle, root_dir);
2355
2356 /* scan /loader/entries/\*.conf files */
2357 config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
2358
2359 /* Similar, but on any XBOOTLDR partition */
2360 config_load_xbootldr(&config, loaded_image->DeviceHandle);
2361
2362 /* sort entries after version number */
2363 config_sort_entries(&config);
2364
2365 /* if we find some well-known loaders, add them to the end of the list */
2366 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL,
2367 L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
2368 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL,
2369 L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
2370 config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
2371 L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi");
2372 config_entry_add_osx(&config);
2373
2374 if (config.auto_firmware && efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
2375 UINT64 osind = (UINT64)*b;
2376
2377 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
2378 config_entry_add_call(&config,
2379 L"auto-reboot-to-firmware-setup",
2380 L"Reboot Into Firmware Interface",
2381 reboot_into_firmware);
2382 FreePool(b);
2383 }
2384
2385 if (config.entry_count == 0) {
2386 Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
2387 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2388 goto out;
2389 }
2390
2391 config_write_entries_to_variable(&config);
2392
2393 config_title_generate(&config);
2394
2395 /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
2396 config_default_entry_select(&config);
2397
2398 /* if no configured entry to select from was found, enable the menu */
2399 if (config.idx_default == -1) {
2400 config.idx_default = 0;
2401 if (config.timeout_sec == 0)
2402 config.timeout_sec = 10;
2403 }
2404
2405 /* select entry or show menu when key is pressed or timeout is set */
2406 if (config.force_menu || config.timeout_sec > 0)
2407 menu = TRUE;
2408 else {
2409 UINT64 key;
2410
2411 err = console_key_read(&key, FALSE);
2412 if (!EFI_ERROR(err)) {
2413 INT16 idx;
2414
2415 /* find matching key in config entries */
2416 idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key));
2417 if (idx >= 0)
2418 config.idx_default = idx;
2419 else
2420 menu = TRUE;
2421 }
2422 }
2423
2424 for (;;) {
2425 ConfigEntry *entry;
2426
2427 entry = config.entries[config.idx_default];
2428 if (menu) {
2429 efivar_set_time_usec(L"LoaderTimeMenuUSec", 0);
2430 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL);
2431 if (!menu_run(&config, &entry, loaded_image_path))
2432 break;
2433 }
2434
2435 /* run special entry like "reboot" */
2436 if (entry->call) {
2437 entry->call();
2438 continue;
2439 }
2440
2441 config_entry_bump_counters(entry, root_dir);
2442
2443 /* export the selected boot entry to the system */
2444 efivar_set(L"LoaderEntrySelected", entry->id, FALSE);
2445
2446 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
2447 err = image_start(image, &config, entry);
2448 if (EFI_ERROR(err)) {
2449 graphics_mode(FALSE);
2450 Print(L"\nFailed to execute %s (%s): %r\n", entry->title, entry->loader, err);
2451 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
2452 goto out;
2453 }
2454
2455 menu = TRUE;
2456 config.timeout_sec = 0;
2457 }
2458 err = EFI_SUCCESS;
2459 out:
2460 FreePool(loaded_image_path);
2461 config_free(&config);
2462 uefi_call_wrapper(root_dir->Close, 1, root_dir);
2463 uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
2464 return err;
2465 }