]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/boot/efi/boot.c
Merge pull request #13953 from SpencerMichaels/systemd-boot-efistub-id-fix
[thirdparty/systemd.git] / src / boot / efi / boot.c
index 7b3e7824546e7304ce467a790ed00ec6e90f5307..84293364b47eb03fd23cd0bed99d04bec966c520 100644 (file)
@@ -9,8 +9,10 @@
 #include "disk.h"
 #include "graphics.h"
 #include "linux.h"
+#include "loader-features.h"
 #include "measure.h"
 #include "pe.h"
+#include "random-seed.h"
 #include "shim.h"
 #include "util.h"
 
@@ -30,7 +32,7 @@ enum loader_type {
 };
 
 typedef struct {
-        CHAR16 *id; /* The identifier for this entry (note that this id is not necessarily unique though!) */
+        CHAR16 *id; /* The unique identifier for this entry */
         CHAR16 *title_show;
         CHAR16 *title;
         CHAR16 *version;
@@ -67,6 +69,7 @@ typedef struct {
         BOOLEAN force_menu;
         UINTN console_mode;
         enum console_mode_change_type console_mode_change;
+        RandomSeedMode random_seed_mode;
 } Config;
 
 static VOID cursor_left(UINTN *cursor, UINTN *first) {
@@ -269,10 +272,8 @@ static BOOLEAN line_edit(
 
                 case KEYPRESS(0, 0, CHAR_LINEFEED):
                 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
-                        if (StrCmp(line, line_in) != 0) {
-                                *line_out = line;
-                                line = NULL;
-                        }
+                        if (StrCmp(line, line_in) != 0)
+                                *line_out = TAKE_PTR(line);
                         enter = TRUE;
                         exit = TRUE;
                         break;
@@ -397,6 +398,21 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
         Print(L"editor:                 %s\n", yes_no(config->editor));
         Print(L"auto-entries:           %s\n", yes_no(config->auto_entries));
         Print(L"auto-firmware:          %s\n", yes_no(config->auto_firmware));
+
+        switch (config->random_seed_mode) {
+        case RANDOM_SEED_OFF:
+                Print(L"random-seed-mode:       off\n");
+                break;
+        case RANDOM_SEED_WITH_SYSTEM_TOKEN:
+                Print(L"random-seed-node:       with-system-token\n");
+                break;
+        case RANDOM_SEED_ALWAYS:
+                Print(L"random-seed-node:       always\n");
+                break;
+        default:
+                ;
+        }
+
         Print(L"\n");
 
         Print(L"config entry count:     %d\n", config->entry_count);
@@ -499,7 +515,6 @@ static BOOLEAN menu_run(
         BOOLEAN exit = FALSE;
         BOOLEAN run = TRUE;
         BOOLEAN wait = FALSE;
-        BOOLEAN cleared_screen = FALSE;
 
         graphics_mode(FALSE);
         uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
@@ -511,16 +526,13 @@ static BOOLEAN menu_run(
 
         if (config->console_mode_change != CONSOLE_MODE_KEEP) {
                 err = console_set_mode(&config->console_mode, config->console_mode_change);
-                if (!EFI_ERROR(err))
-                        cleared_screen = TRUE;
-        }
-
-        if (!cleared_screen)
+                if (EFI_ERROR(err)) {
+                        uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+                        Print(L"Error switching console mode to %ld: %r.\r", (UINT64)config->console_mode, err);
+                }
+        } else
                 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
 
-        if (config->console_mode_change != CONSOLE_MODE_KEEP && EFI_ERROR(err))
-                Print(L"Error switching console mode to %ld: %r.\r", (UINT64)config->console_mode, err);
-
         err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
         if (EFI_ERROR(err)) {
                 x_max = 80;
@@ -1039,7 +1051,9 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
 
                         if (EFI_ERROR(parse_boolean(value, &on)))
                                 continue;
+
                         config->editor = on;
+                        continue;
                 }
 
                 if (strcmpa((CHAR8 *)"auto-entries", key) == 0) {
@@ -1047,7 +1061,9 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
 
                         if (EFI_ERROR(parse_boolean(value, &on)))
                                 continue;
+
                         config->auto_entries = on;
+                        continue;
                 }
 
                 if (strcmpa((CHAR8 *)"auto-firmware", key) == 0) {
@@ -1055,7 +1071,9 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
 
                         if (EFI_ERROR(parse_boolean(value, &on)))
                                 continue;
+
                         config->auto_firmware = on;
+                        continue;
                 }
 
                 if (strcmpa((CHAR8 *)"console-mode", key) == 0) {
@@ -1075,6 +1093,23 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
 
                         continue;
                 }
+
+                if (strcmpa((CHAR8*) "random-seed-mode", key) == 0) {
+                        if (strcmpa((CHAR8*) "off", value) == 0)
+                                config->random_seed_mode = RANDOM_SEED_OFF;
+                        else if (strcmpa((CHAR8*) "with-system-token", value) == 0)
+                                config->random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN;
+                        else if (strcmpa((CHAR8*) "always", value) == 0)
+                                config->random_seed_mode = RANDOM_SEED_ALWAYS;
+                        else {
+                                BOOLEAN on;
+
+                                if (EFI_ERROR(parse_boolean(value, &on)))
+                                        continue;
+
+                                config->random_seed_mode = on ? RANDOM_SEED_ALWAYS : RANDOM_SEED_OFF;
+                        }
+                }
         }
 }
 
@@ -1258,14 +1293,14 @@ static VOID config_entry_bump_counters(
         /* If the file we just renamed is the loader path, then let's update that. */
         if (StrCmp(entry->loader, old_path) == 0) {
                 FreePool(entry->loader);
-                entry->loader = new_path;
-                new_path = NULL;
+                entry->loader = TAKE_PTR(new_path);
         }
 }
 
 static VOID config_entry_add_from_file(
                 Config *config,
                 EFI_HANDLE *device,
+                EFI_FILE *root_dir,
                 CHAR16 *path,
                 CHAR16 *file,
                 CHAR8 *content,
@@ -1275,7 +1310,8 @@ static VOID config_entry_add_from_file(
         CHAR8 *line;
         UINTN pos = 0;
         CHAR8 *key, *value;
-        UINTN len;
+        EFI_STATUS err;
+        EFI_FILE_HANDLE handle;
         _cleanup_freepool_ CHAR16 *initrd = NULL;
 
         entry = AllocatePool(sizeof(ConfigEntry));
@@ -1360,10 +1396,8 @@ static VOID config_entry_add_from_file(
                                 s = PoolPrint(L"%s %s", entry->options, new);
                                 FreePool(entry->options);
                                 entry->options = s;
-                        } else {
-                                entry->options = new;
-                                new = NULL;
-                        }
+                        } else
+                                entry->options = TAKE_PTR(new);
 
                         continue;
                 }
@@ -1374,6 +1408,14 @@ static VOID config_entry_add_from_file(
                 return;
         }
 
+        /* check existence */
+        err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, entry->loader, EFI_FILE_MODE_READ, 0ULL);
+        if (EFI_ERROR(err)) {
+                config_entry_free(entry);
+                return;
+        }
+        uefi_call_wrapper(handle->Close, 1, handle);
+
         /* add initrd= to options */
         if (entry->type == LOADER_LINUX && initrd) {
                 if (entry->options) {
@@ -1382,18 +1424,12 @@ static VOID config_entry_add_from_file(
                         s = PoolPrint(L"%s %s", initrd, entry->options);
                         FreePool(entry->options);
                         entry->options = s;
-                } else {
-                        entry->options = initrd;
-                        initrd = NULL;
-                }
+                } else
+                        entry->options = TAKE_PTR(initrd);
         }
 
         entry->device = device;
         entry->id = StrDuplicate(file);
-        len = StrLen(entry->id);
-        /* remove ".conf" */
-        if (len > 5)
-                entry->id[len - 5] = '\0';
         StrLwr(entry->id);
 
         config_add_entry(config, entry);
@@ -1410,6 +1446,7 @@ static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
                 .editor = TRUE,
                 .auto_entries = TRUE,
                 .auto_firmware = TRUE,
+                .random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN,
         };
 
         err = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content, NULL);
@@ -1472,7 +1509,7 @@ static VOID config_load_entries(
 
                         err = file_read(entries_dir, f->FileName, 0, 0, &content, NULL);
                         if (!EFI_ERROR(err))
-                                config_entry_add_from_file(config, device, L"\\loader\\entries", f->FileName, content, loaded_image_path);
+                                config_entry_add_from_file(config, device, root_dir, L"\\loader\\entries", f->FileName, content, loaded_image_path);
                 }
                 uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
         }
@@ -1733,7 +1770,8 @@ static ConfigEntry *config_entry_add_loader(
                 CHAR16 *id,
                 CHAR16 key,
                 CHAR16 *title,
-                CHAR16 *loader) {
+                CHAR16 *loader,
+                CHAR16 *version) {
 
         ConfigEntry *entry;
 
@@ -1741,6 +1779,7 @@ static ConfigEntry *config_entry_add_loader(
         *entry = (ConfigEntry) {
                 .type = type,
                 .title = StrDuplicate(title),
+                .version = StrDuplicate(version),
                 .device = device,
                 .loader = StrDuplicate(loader),
                 .id = StrDuplicate(id),
@@ -1798,7 +1837,7 @@ static BOOLEAN config_entry_add_loader_auto(
                 return FALSE;
         uefi_call_wrapper(handle->Close, 1, handle);
 
-        entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, id, key, title, loader);
+        entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, id, key, title, loader, NULL);
         if (!entry)
                 return FALSE;
 
@@ -1866,10 +1905,12 @@ static VOID config_entry_add_linux(
                 CHAR8 *line;
                 UINTN pos = 0;
                 CHAR8 *key, *value;
+                CHAR16 *os_name_pretty = NULL;
                 CHAR16 *os_name = NULL;
                 CHAR16 *os_id = NULL;
                 CHAR16 *os_version = NULL;
-                CHAR16 *os_build = NULL;
+                CHAR16 *os_version_id = NULL;
+                CHAR16 *os_build_id = NULL;
 
                 err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf);
                 if (bufsize == 0 || EFI_ERROR(err))
@@ -1885,6 +1926,8 @@ static VOID config_entry_add_linux(
                         continue;
                 if (StriCmp(f->FileName + len - 4, L".efi") != 0)
                         continue;
+                if (StrnCmp(f->FileName, L"auto-", 5) == 0)
+                        continue;
 
                 /* look for .osrel and .cmdline sections in the .efi binary */
                 err = pe_file_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
@@ -1898,6 +1941,12 @@ static VOID config_entry_add_linux(
                 /* read properties from the embedded os-release file */
                 while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
                         if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
+                                FreePool(os_name_pretty);
+                                os_name_pretty = stra_to_str(value);
+                                continue;
+                        }
+
+                        if (strcmpa((CHAR8 *)"NAME", key) == 0) {
                                 FreePool(os_name);
                                 os_name = stra_to_str(value);
                                 continue;
@@ -1915,20 +1964,27 @@ static VOID config_entry_add_linux(
                                 continue;
                         }
 
+                        if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) {
+                                FreePool(os_version_id);
+                                os_version_id = stra_to_str(value);
+                                continue;
+                        }
+
                         if (strcmpa((CHAR8 *)"BUILD_ID", key) == 0) {
-                                FreePool(os_build);
-                                os_build = stra_to_str(value);
+                                FreePool(os_build_id);
+                                os_build_id = stra_to_str(value);
                                 continue;
                         }
                 }
 
-                if (os_name && os_id && (os_version || os_build)) {
-                        _cleanup_freepool_ CHAR16 *conf = NULL, *path = NULL;
+                if ((os_name_pretty || os_name) && os_id && (os_version || os_version_id || os_build_id)) {
+                        _cleanup_freepool_ CHAR16 *path = NULL;
 
-                        conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build);
                         path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
 
-                        entry = config_entry_add_loader(config, device, LOADER_LINUX, conf, 'l', os_name, path);
+                        entry = config_entry_add_loader(config, device, LOADER_LINUX, f->FileName, 'l',
+                                                        os_name_pretty ? : (os_name ? : os_id), path,
+                                                        os_version ? : (os_version_id ? : os_build_id));
 
                         FreePool(content);
                         content = NULL;
@@ -1947,10 +2003,12 @@ static VOID config_entry_add_linux(
                         config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
                 }
 
+                FreePool(os_name_pretty);
                 FreePool(os_name);
                 FreePool(os_id);
                 FreePool(os_version);
-                FreePool(os_build);
+                FreePool(os_version_id);
+                FreePool(os_build_id);
                 FreePool(content);
         }
 
@@ -2284,11 +2342,13 @@ static VOID config_write_entries_to_variable(Config *config) {
 
 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         static const UINT64 loader_features =
-                (1ULL << 0) | /* I honour the LoaderConfigTimeout variable */
-                (1ULL << 1) | /* I honour the LoaderConfigTimeoutOneShot variable */
-                (1ULL << 2) | /* I honour the LoaderEntryDefault variable */
-                (1ULL << 3) | /* I honour the LoaderEntryOneShot variable */
-                (1ULL << 4) | /* I support boot counting */
+                EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
+                EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT |
+                EFI_LOADER_FEATURE_ENTRY_DEFAULT |
+                EFI_LOADER_FEATURE_ENTRY_ONESHOT |
+                EFI_LOADER_FEATURE_BOOT_COUNTING |
+                EFI_LOADER_FEATURE_XBOOTLDR |
+                EFI_LOADER_FEATURE_RANDOM_SEED |
                 0;
 
         _cleanup_freepool_ CHAR16 *infostr = NULL, *typestr = NULL;
@@ -2319,7 +2379,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
                                 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
         if (EFI_ERROR(err)) {
-                Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
+                Print(L"Error getting a LoadedImageProtocol handle: %r", err);
                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
                 return err;
         }
@@ -2409,6 +2469,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                 UINT64 key;
 
                 err = console_key_read(&key, FALSE);
+
+                if (err == EFI_NOT_READY) {
+                        uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
+                        err = console_key_read(&key, FALSE);
+                }
+
                 if (!EFI_ERROR(err)) {
                         INT16 idx;
 
@@ -2440,8 +2506,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
 
                 config_entry_bump_counters(entry, root_dir);
 
-                /* export the selected boot entry to the system */
-                efivar_set(L"LoaderEntrySelected", entry->id, FALSE);
+                /* Export the selected boot entry to the system */
+                (VOID) efivar_set(L"LoaderEntrySelected", entry->id, FALSE);
+
+                /* Optionally, read a random seed off the ESP and pass it to the OS */
+                (VOID) process_random_seed(root_dir, config.random_seed_mode);
 
                 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
                 err = image_start(image, &config, entry);