]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: add new 'uki-url' bls type #1 menu items for booting remote UKIs
authorLennart Poettering <lennart@poettering.net>
Tue, 11 Feb 2025 08:34:20 +0000 (09:34 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 21 Feb 2025 09:04:15 +0000 (10:04 +0100)
Companion BLS spec PR:

https://github.com/uapi-group/specifications/pull/135

12 files changed:
src/boot/boot.c
src/boot/device-path-util.c
src/boot/device-path-util.h
src/boot/shim.c
src/boot/shim.h
src/boot/stub.c
src/boot/util.c
src/boot/util.h
src/bootctl/bootctl-status.c
src/fundamental/efivars-fundamental.h
src/fundamental/iovec-util-fundamental.h
src/fundamental/macro-fundamental.h

index e57c5986541b617c699dec0691e1867267848509..980fcff7c3209a5efe6dafe69b812703c177e423 100644 (file)
@@ -18,6 +18,7 @@
 #include "pe.h"
 #include "proto/block-io.h"
 #include "proto/device-path.h"
+#include "proto/load-file.h"
 #include "proto/simple-text-io.h"
 #include "random-seed.h"
 #include "sbat.h"
@@ -27,6 +28,7 @@
 #include "ticks.h"
 #include "tpm2-pcr.h"
 #include "uki.h"
+#include "url-discovery.h"
 #include "util.h"
 #include "version.h"
 #include "vmm.h"
@@ -50,6 +52,7 @@ typedef enum LoaderType {
         LOADER_EFI,           /* Boot loader spec type #1 entries with "efi" line */
         LOADER_LINUX,         /* Boot loader spec type #1 entries with "linux" line */
         LOADER_UKI,           /* Boot loader spec type #1 entries with "uki" line */
+        LOADER_UKI_URL,       /* Boot loader spec type #1 entries with "uki-url" line */
         LOADER_TYPE2_UKI,     /* Boot loader spec type #2 entries */
         LOADER_SECURE_BOOT_KEYS,
         LOADER_BAD,           /* Marker: this boot loader spec type #1 entry is invalid */
@@ -58,13 +61,13 @@ typedef enum LoaderType {
 } LoaderType;
 
 /* Which loader types permit command line editing */
-#define LOADER_TYPE_ALLOW_EDITOR(t) IN_SET(t, LOADER_EFI, LOADER_LINUX, LOADER_UKI, LOADER_TYPE2_UKI)
+#define LOADER_TYPE_ALLOW_EDITOR(t) IN_SET(t, LOADER_EFI, LOADER_LINUX, LOADER_UKI, LOADER_UKI_URL, LOADER_TYPE2_UKI)
 
 /* Which loader types allow command line editing in SecureBoot mode */
 #define LOADER_TYPE_ALLOW_EDITOR_IN_SB(t) IN_SET(t, LOADER_EFI, LOADER_LINUX)
 
 /* Which loader types shall be considered for automatic selection */
-#define LOADER_TYPE_MAY_AUTO_SELECT(t) IN_SET(t, LOADER_EFI, LOADER_LINUX, LOADER_UKI, LOADER_TYPE2_UKI)
+#define LOADER_TYPE_MAY_AUTO_SELECT(t) IN_SET(t, LOADER_EFI, LOADER_LINUX, LOADER_UKI, LOADER_UKI_URL, LOADER_TYPE2_UKI)
 
 typedef struct {
         char16_t *id;         /* The unique identifier for this entry (typically the filename of the file defining the entry, possibly suffixed with a profile id) */
@@ -77,6 +80,7 @@ typedef struct {
         EFI_HANDLE *device;
         LoaderType type;
         char16_t *loader;
+        char16_t *url;
         char16_t *devicetree;
         char16_t *options;
         bool options_implied; /* If true, these options are implied if we invoke the PE binary without any parameters (as in: UKI). If false we must specify these options explicitly. */
@@ -620,6 +624,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
                         printf("        device: %ls\n", dp_str);
                 if (entry->loader)
                         printf("        loader: %ls\n", entry->loader);
+                if (entry->url)
+                        printf("           url: %ls\n", entry->url);
                 STRV_FOREACH(initrd, entry->initrd)
                         printf("        initrd: %ls\n", *initrd);
                 if (entry->devicetree)
@@ -1211,6 +1217,7 @@ static BootEntry* boot_entry_free(BootEntry *entry) {
         free(entry->version);
         free(entry->machine_id);
         free(entry->loader);
+        free(entry->url);
         free(entry->devicetree);
         free(entry->options);
         strv_free(entry->initrd);
@@ -1508,6 +1515,32 @@ static void boot_entry_add_type1(
                         entry->loader = xstr8_to_path(value);
                         entry->key = 'l';
 
+                } else if (streq8(key, "uki-url")) {
+
+                        if (!IN_SET(entry->type, LOADER_UNDEFINED, LOADER_UKI_URL)) {
+                                entry->type = LOADER_BAD;
+                                break;
+                        }
+
+                        _cleanup_free_ char16_t *p = xstr8_to_16(value);
+
+                        const char16_t *e = startswith(p, u":");
+                        if (e) {
+                                _cleanup_free_ char16_t *origin = disk_get_url(device);
+
+                                if (!origin) {
+                                        /* Automatically hide entries that require an original URL but where none is available. */
+                                        entry->type = LOADER_IGNORE;
+                                        break;
+                                }
+
+                                entry->url = url_replace_last_component(origin, p);
+                        } else
+                                entry->url = TAKE_PTR(p);
+
+                        entry->type = LOADER_UKI_URL;
+                        entry->key = 'l';
+
                 } else if (streq8(key, "efi")) {
 
                         if (!IN_SET(entry->type, LOADER_UNDEFINED, LOADER_EFI)) {
@@ -1560,11 +1593,13 @@ static void boot_entry_add_type1(
         if (IN_SET(entry->type, LOADER_UNDEFINED, LOADER_BAD, LOADER_IGNORE))
                 return;
 
-        /* check existence */
-        _cleanup_file_close_ EFI_FILE *handle = NULL;
-        err = root_dir->Open(root_dir, &handle, entry->loader, EFI_FILE_MODE_READ, 0ULL);
-        if (err != EFI_SUCCESS)
-                return;
+        /* Check existence of loader file */
+        if (entry->loader) {
+                _cleanup_file_close_ EFI_FILE *handle = NULL;
+                err = root_dir->Open(root_dir, &handle, entry->loader, EFI_FILE_MODE_READ, 0ULL);
+                if (err != EFI_SUCCESS)
+                        return;
+        }
 
         entry->device = device;
         entry->id = xstrdup16(file);
@@ -2560,13 +2595,77 @@ static EFI_STATUS initrd_prepare(
         return EFI_SUCCESS;
 }
 
+static EFI_STATUS expand_path(
+                EFI_HANDLE parent_image,
+                EFI_DEVICE_PATH *path,
+                EFI_DEVICE_PATH **ret_expanded_path) {
+
+        EFI_STATUS err;
+
+        assert(parent_image);
+        assert(path);
+        assert(ret_expanded_path);
+
+        _cleanup_free_ EFI_HANDLE *handles = NULL;
+        size_t n_handles = 0;
+        err = BS->LocateHandleBuffer(
+                        ByProtocol,
+                        MAKE_GUID_PTR(EFI_LOAD_FILE_PROTOCOL),
+                        /* SearchKey= */ NULL,
+                        &n_handles,
+                        &handles);
+        if (!IN_SET(err, EFI_SUCCESS, EFI_NOT_FOUND))
+                return log_error_status(err, "Failed to get list of LoadFile protocol handles: %m");
+
+        FOREACH_ARRAY(h, handles, n_handles) {
+                EFI_LOAD_FILE_PROTOCOL *load_file = NULL;
+                err = BS->OpenProtocol(
+                                *h,
+                                MAKE_GUID_PTR(EFI_LOAD_FILE_PROTOCOL),
+                                (void**) &load_file,
+                                parent_image,
+                                /* ControllerHandler= */ NULL,
+                                EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+                if (IN_SET(err, EFI_NOT_FOUND, EFI_INVALID_PARAMETER))
+                        continue; /* Skip over LoadFile() handles that are not suitable for this kind of device path */
+                if (err != EFI_SUCCESS) {
+                        log_warning_status(err, "Failed to get LoadFile() protocol, ignoring: %m");
+                        continue;
+                }
+
+                /* Issue a LoadFile() request without interest in the actual data (i.e. size is zero and
+                 * buffer pointer is NULL), but with BootPolicy set to true, this has the effect of
+                 * downloading the URL and establishing a handle for it. */
+                size_t size = 0;
+                err = load_file->LoadFile(load_file, path, /* BootPolicy= */ true, &size, /* Buffer= */ NULL);
+                if (IN_SET(err, EFI_NOT_FOUND, EFI_INVALID_PARAMETER))
+                        continue; /* Skip over LoadFile() handles that after all don't consider themselves
+                                   * appropriate for this kind of path */
+                if (err != EFI_BUFFER_TOO_SMALL) {
+                        log_warning_status(err, "Failed to get file via LoadFile() protocol, ignoring: %m");
+                        continue;
+                }
+
+                /* Now read the updated file path */
+                EFI_DEVICE_PATH *load_file_path = NULL;
+                err = BS->HandleProtocol(*h, MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL), (void **) &load_file_path);
+                if (err != EFI_SUCCESS)
+                        return log_error_status(err, "Failed to get LoadFile() device path: %m");
+
+                /* And return a copy */
+                *ret_expanded_path = device_path_dup(load_file_path);
+                return EFI_SUCCESS;
+        }
+
+        return EFI_NOT_FOUND;
+}
+
 static EFI_STATUS image_start(
                 EFI_HANDLE parent_image,
                 const BootEntry *entry) {
 
         _cleanup_(devicetree_cleanup) struct devicetree_state dtstate = {};
         _cleanup_(unload_imagep) EFI_HANDLE image = NULL;
-        _cleanup_free_ EFI_DEVICE_PATH *path = NULL;
         EFI_STATUS err;
 
         assert(entry);
@@ -2576,38 +2675,94 @@ static EFI_STATUS image_start(
                 (void) entry->call();
 
         _cleanup_file_close_ EFI_FILE *image_root = NULL;
-        err = open_volume(entry->device, &image_root);
-        if (err != EFI_SUCCESS)
-                return log_error_status(err, "Error opening root path: %m");
+        _cleanup_free_ EFI_DEVICE_PATH *path = NULL;
+        bool boot_policy;
+        if (entry->url) {
+                /* Generate a device path that only contains the URL */
+                err = make_url_device_path(entry->url, &path);
+                if (err != EFI_SUCCESS)
+                        return log_error_status(err, "Error making URL device path: %m");
+
+                /* Try to expand this path on all available NICs and IP protocols */
+                _cleanup_free_ EFI_DEVICE_PATH *expanded_path = NULL;
+                for (unsigned n_attempt = 0;; n_attempt++) {
+                        err = expand_path(parent_image, path, &expanded_path);
+                        if (err == EFI_SUCCESS) {
+                                /* If this worked then let's try to boot with the expanded path. */
+                                free(path);
+                                path = TAKE_PTR(expanded_path);
+                                break;
+                        }
+                        if (err != EFI_NOT_FOUND || n_attempt > 5) {
+                                log_warning_status(err, "Failed to expand device path, ignoring: %m");
+                                break;
+                        }
 
-        err = make_file_device_path(entry->device, entry->loader, &path);
-        if (err != EFI_SUCCESS)
-                return log_error_status(err, "Error making file device path: %m");
+                        /* Maybe the network devices have been configured for this yet (because we are the
+                         * first piece of code trying to do networking)? Then let's connect them, and try
+                         * again. */
+                        reconnect_all_drivers();
+                }
 
-        size_t initrd_size = 0;
-        _cleanup_pages_ Pages initrd_pages = {};
-        _cleanup_free_ char16_t *options_initrd = NULL;
-        err = initrd_prepare(image_root, entry, &options_initrd, &initrd_pages, &initrd_size);
-        if (err != EFI_SUCCESS)
-                return log_error_status(err, "Error preparing initrd: %m");
+                /* Note: if the path expansion doesn't work, we'll continue with the unexpanded path. Which
+                 * will probably fail on many (most?) firmwares, but it's worth a try. */
 
-        err = shim_load_image(parent_image, path, &image);
-        if (err != EFI_SUCCESS)
-                return log_error_status(err, "Error loading %ls: %m", entry->loader);
+                boot_policy = true; /* Set BootPolicy parameter to LoadImage() to true, which ultimately
+                                     * controls whether the LoadFile (and thus HTTP boot) or LoadFile2 (which
+                                     * does not set up HTTP boot) protocol shall be used. */
+        } else {
+                assert(entry->device);
+                assert(entry->loader);
 
-        /* DTBs are loaded by the kernel before ExitBootServices, and they can be used to map and assign
-         * arbitrary memory ranges, so skip them when secure boot is enabled as the DTB here is unverified.
-         */
-        if (entry->devicetree && !secure_boot_enabled()) {
-                err = devicetree_install(&dtstate, image_root, entry->devicetree);
+                err = open_volume(entry->device, &image_root);
+                if (err != EFI_SUCCESS)
+                        return log_error_status(err, "Error opening root path: %m");
+
+                err = make_file_device_path(entry->device, entry->loader, &path);
                 if (err != EFI_SUCCESS)
-                        return log_error_status(err, "Error loading %ls: %m", entry->devicetree);
+                        return log_error_status(err, "Error making file device path: %m");
+
+                boot_policy = false;
+        }
+
+        /* Authenticate the image before we continue with initrd or DT stuff */
+        err = shim_load_image(parent_image, path, boot_policy, &image);
+        if (err != EFI_SUCCESS) {
+                if (entry->url) {
+                        /* EFI_NOT_FOUND typically indicates that no network stack or NIC was available, let's give the user a hint. */
+                        if (err == EFI_NOT_FOUND) {
+                                log_info("Unable to boot remote UKI %ls, is networking available?", entry->url);
+                                return err;
+                        }
+
+                        return log_error_status(err, "Error loading loading remote UKI %ls: %m", entry->url);
+                }
+
+                return log_error_status(err, "Error loading EFI binary %ls: %m", entry->loader);
         }
 
         _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
-        err = initrd_register(PHYSICAL_ADDRESS_TO_POINTER(initrd_pages.addr), initrd_size, &initrd_handle);
-        if (err != EFI_SUCCESS)
-                return log_error_status(err, "Error registering initrd: %m");
+        _cleanup_free_ char16_t *options_initrd = NULL;
+        _cleanup_pages_ Pages initrd_pages = {};
+        size_t initrd_size = 0;
+        if (image_root) {
+                err = initrd_prepare(image_root, entry, &options_initrd, &initrd_pages, &initrd_size);
+                if (err != EFI_SUCCESS)
+                        return log_error_status(err, "Error preparing initrd: %m");
+
+                /* DTBs are loaded by the kernel before ExitBootServices(), and they can be used to map and
+                 * assign arbitrary memory ranges, so skip them when secure boot is enabled as the DTB here
+                 * is unverified. */
+                if (entry->devicetree && !secure_boot_enabled()) {
+                        err = devicetree_install(&dtstate, image_root, entry->devicetree);
+                        if (err != EFI_SUCCESS)
+                                return log_error_status(err, "Error loading %ls: %m", entry->devicetree);
+                }
+
+                err = initrd_register(PHYSICAL_ADDRESS_TO_POINTER(initrd_pages.addr), initrd_size, &initrd_handle);
+                if (err != EFI_SUCCESS)
+                        return log_error_status(err, "Error registering initrd: %m");
+        }
 
         EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
         err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image);
@@ -2673,7 +2828,7 @@ static EFI_STATUS image_start(
                         err = EFI_UNSUPPORTED;
         }
 
-        return log_error_status(err, "Failed to execute %ls (%ls): %m", entry->title_show, entry->loader);
+        return log_error_status(err, "Failed to execute %ls (%ls): %m", entry->title_show, entry->loader ?: entry->url);
 }
 
 static void config_free(Config *config) {
@@ -2803,6 +2958,7 @@ static void export_loader_variables(
                 EFI_LOADER_FEATURE_MULTI_PROFILE_UKI |
                 EFI_LOADER_FEATURE_REPORT_URL |
                 EFI_LOADER_FEATURE_TYPE1_UKI |
+                EFI_LOADER_FEATURE_TYPE1_UKI_URL |
                 0;
 
         assert(loaded_image);
index 2a85e8bbfc0922923b59d3f5078b6af1027ddc6a..0e991f941f591b1091560f6abe753c595e0d9196 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "device-path-util.h"
+#include "efi-string.h"
 #include "util.h"
 
 EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp) {
@@ -38,6 +39,38 @@ EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DE
         return EFI_SUCCESS;
 }
 
+EFI_STATUS make_url_device_path(const char16_t *url, EFI_DEVICE_PATH **ret) {
+        assert(url);
+        assert(ret);
+
+        /* Turns a URL into a simple one-element URL device path. */
+
+        _cleanup_free_ char* u = xstr16_to_ascii(url);
+        if (!u)
+                return EFI_INVALID_PARAMETER;
+
+        size_t l = strlen8(u);
+
+        size_t t = offsetof(URI_DEVICE_PATH, Uri) + l + sizeof(EFI_DEVICE_PATH);
+        EFI_DEVICE_PATH *dp = xmalloc(t);
+
+        URI_DEVICE_PATH *udp = (URI_DEVICE_PATH*) dp;
+        udp->Header = (EFI_DEVICE_PATH) {
+                .Type = MESSAGING_DEVICE_PATH,
+                .SubType = MSG_URI_DP,
+                .Length = offsetof(URI_DEVICE_PATH, Uri) + l,
+        };
+        memcpy(udp->Uri, u, l);
+
+        EFI_DEVICE_PATH *end = device_path_next_node(dp);
+        *end = DEVICE_PATH_END_NODE;
+
+        assert(((uint8_t*) end + sizeof(EFI_DEVICE_PATH)) == ((uint8_t*) dp + t));
+
+        *ret = TAKE_PTR(dp);
+        return EFI_SUCCESS;
+}
+
 static char16_t *device_path_to_str_internal(const EFI_DEVICE_PATH *dp) {
         char16_t *str = NULL;
 
@@ -90,7 +123,7 @@ EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret) {
                 return EFI_SUCCESS;
         }
 
-        str = dp_to_text->ConvertDevicePathToText(dp, false, false);
+        str = dp_to_text->ConvertDevicePathToText(dp, /* DisplayOnly=*/ false, /* AllowShortcuts= */ false);
         if (!str)
                 return EFI_OUT_OF_RESOURCES;
 
@@ -136,3 +169,12 @@ EFI_DEVICE_PATH *device_path_replace_node(
         *end = DEVICE_PATH_END_NODE;
         return ret;
 }
+
+size_t device_path_size(const EFI_DEVICE_PATH *dp) {
+        const EFI_DEVICE_PATH *i = ASSERT_PTR(dp);
+
+        for (; !device_path_is_end(i); i = device_path_next_node(i))
+                ;
+
+        return (const uint8_t*) i - (const uint8_t*) dp + sizeof(EFI_DEVICE_PATH);
+}
index 08f1a9c216754056fecedb0f1c2f334f05d46b43..c33669a866b59673fcfbccbb7748b840f023583d 100644 (file)
@@ -2,8 +2,10 @@
 #pragma once
 
 #include "proto/device-path.h"
+#include "util.h"
 
 EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp);
+EFI_STATUS make_url_device_path(const char16_t *url, EFI_DEVICE_PATH **ret);
 EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret);
 bool device_path_startswith(const EFI_DEVICE_PATH *dp, const EFI_DEVICE_PATH *start);
 EFI_DEVICE_PATH *device_path_replace_node(
@@ -25,3 +27,9 @@ static inline bool device_path_is_end(const EFI_DEVICE_PATH *dp) {
                 .SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE, \
                 .Length = sizeof(EFI_DEVICE_PATH)          \
         }
+
+size_t device_path_size(const EFI_DEVICE_PATH *dp);
+
+static inline EFI_DEVICE_PATH *device_path_dup(const EFI_DEVICE_PATH *dp) {
+        return xmemdup(ASSERT_PTR(dp), device_path_size(dp));
+}
index 514a1d0eda363c22cc23d100326c42b958b633f0..87d7dff19153c0b394e2855b480f793ac76cedb8 100644 (file)
@@ -81,7 +81,12 @@ static bool shim_validate(
         return shim_lock->shim_verify(file_buffer, file_size) == EFI_SUCCESS;
 }
 
-EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image) {
+EFI_STATUS shim_load_image(
+                EFI_HANDLE parent,
+                const EFI_DEVICE_PATH *device_path,
+                bool boot_policy,
+                EFI_HANDLE *ret_image) {
+
         assert(device_path);
         assert(ret_image);
 
@@ -91,8 +96,12 @@ EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path
                 install_security_override(shim_validate, NULL);
 
         EFI_STATUS ret = BS->LoadImage(
-                        /*BootPolicy=*/false, parent, (EFI_DEVICE_PATH *) device_path, NULL, 0, ret_image);
-
+                        /* BootPolicy= */ boot_policy,
+                        parent,
+                        (EFI_DEVICE_PATH *) device_path,
+                        /* SourceBuffer= */ NULL,
+                        /* SourceSize= */ 0,
+                        ret_image);
         if (have_shim)
                 uninstall_security_override();
 
index e0cb39f79503dd778a3edfbac11af0f93054ec3e..ef9dc7ba276a32b532f0b2a14dc775756ebe3b2f 100644 (file)
@@ -12,5 +12,5 @@
 #include "efi.h"
 
 bool shim_loaded(void);
-EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image);
+EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, bool boot_policy, EFI_HANDLE *ret_image);
 void shim_retain_protocol(void);
index 3b3a5f6a42822e50ab4260689b84929e91db84ce..ac6c112459020e033079f37952f5b3ebcbe9e64e 100644 (file)
@@ -598,7 +598,7 @@ static EFI_STATUS load_addons(
 
                 /* By using shim_load_image, we cover both the case where the PE files are signed with MoK
                  * and with DB, and running with or without shim. */
-                err = shim_load_image(stub_image, addon_path, &addon);
+                err = shim_load_image(stub_image, addon_path, /* boot_policy= */ false, &addon);
                 if (err != EFI_SUCCESS) {
                         log_error_status(err,
                                          "Failed to read '%ls' from '%ls', ignoring: %m",
index 395c1f2abc8f69b7a9df78b469830cf09aa72d07..5dd111e7c5a053cb59f11036bb8996b31554f91b 100644 (file)
@@ -1,13 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "device-path-util.h"
+#include "efi-string.h"
+#include "efivars.h"
 #include "memory-util-fundamental.h"
 #include "proto/device-path.h"
 #include "proto/simple-text-io.h"
 #include "ticks.h"
 #include "util.h"
 #include "version.h"
-#include "efivars.h"
 
 /* Never try to read more than 16G into memory (and on 32bit 1G) */
 #define FILE_READ_MAX MIN(SIZE_MAX/4, UINT64_C(16)*1024U*1024U*1024U)
@@ -511,3 +512,40 @@ bool free_and_xstrdup16(char16_t **p, const char16_t *s) {
         *p = t;
         return true;
 }
+
+char16_t *url_replace_last_component(const char16_t *url, const char16_t *filename) {
+        assert(url);
+        assert(filename);
+
+        /* Find colon separating protocol and hostname */
+        const char16_t *d = strchr16(url, ':');
+        if (!d || url == d)
+                return NULL;
+        d++;
+
+        /* Skip slashes after colon */
+        d += strspn(d, u"/");
+
+        /* Skip everything till next slash or end (i.e. the hostname) */
+        size_t n = strcspn(d, u"/?#");
+        if (n == 0)
+                return NULL;
+
+        d += n;
+
+        const char16_t *e = d + strcspn(d, u"?#"); /* Cut off "Query" and "Fragment" */
+
+        while (e > d && e[-1] == '/') /* Eat trailing slashes */
+                e--;
+
+        const char16_t *p = e;
+        while (p > d && p[-1] != '/') /* Find component before that */
+                p--;
+
+        if (e <= p)
+                return NULL;
+
+        _cleanup_free_ char16_t *chopped = xstrndup16(url, p - url);
+
+        return xasprintf("%ls/%ls", chopped, filename);
+}
index 49ba11961379b2fb7ae7cb4cde9402044b97bfc4..2b5cc7213e93dae7179dd60f4370e3613b367982 100644 (file)
@@ -251,3 +251,5 @@ char16_t *get_extra_dir(const EFI_DEVICE_PATH *file_path);
 #define xnew0(type, n) ASSERT_PTR(new0(type, n))
 
 #endif
+
+char16_t *url_replace_last_component(const char16_t *url, const char16_t *filename);
index 541f7f25ccefc998883c935bba7beb7f832aed74..798c86dba292fdb4eeb0feda073ba3765807370e 100644 (file)
@@ -392,6 +392,7 @@ int verb_status(int argc, char *argv[], void *userdata) {
                         { EFI_LOADER_FEATURE_MULTI_PROFILE_UKI,       "Multi-Profile UKIs are supported"      },
                         { EFI_LOADER_FEATURE_REPORT_URL,              "Loader reports network boot URL"       },
                         { EFI_LOADER_FEATURE_TYPE1_UKI,               "Support Type #1 uki field"             },
+                        { EFI_LOADER_FEATURE_TYPE1_UKI_URL,           "Support Type #1 uki-url field"         },
                 };
                 static const struct {
                         uint64_t flag;
index b7197a874eddeb8d3956824bab496a9d1035a6d9..d556e7718f8552ac44d62975cac8d79cfeacef8f 100644 (file)
@@ -26,6 +26,7 @@
 #define EFI_LOADER_FEATURE_MULTI_PROFILE_UKI       (UINT64_C(1) << 14)
 #define EFI_LOADER_FEATURE_REPORT_URL              (UINT64_C(1) << 15)
 #define EFI_LOADER_FEATURE_TYPE1_UKI               (UINT64_C(1) << 16)
+#define EFI_LOADER_FEATURE_TYPE1_UKI_URL           (UINT64_C(1) << 17)
 
 /* Features of the stub, i.e. systemd-stub */
 #define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION     (UINT64_C(1) << 0)
index 68d5bf4ee0cc2f1eca426e41b36489227002bbf2..edd95fa4e9a4ef8e0a054c40da456e1cda3148e8 100644 (file)
@@ -8,7 +8,9 @@ struct iovec {
         size_t iov_len;
 };
 
+DISABLE_WARNING_REDUNDANT_DECLS;
 static inline void free(void *p);
+REENABLE_WARNING;
 #endif
 
 /* This accepts both const and non-const pointers */
index 78a87d150b5c33f3eaa2d6027af05e692cf2f6bc..d56dcb6eb6db8b4737bb1f982a0d7fb8ac93c3d3 100644 (file)
         _Pragma("GCC diagnostic push");                                 \
         _Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"")
 
+#define DISABLE_WARNING_REDUNDANT_DECLS                                 \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wredundant-decls\"")
+
 #if HAVE_WARNING_ZERO_LENGTH_BOUNDS
 #  define DISABLE_WARNING_ZERO_LENGTH_BOUNDS                            \
         _Pragma("GCC diagnostic push");                                 \