We already support creating a load option where the device-path
field contains the concatenation of the binary device-path and
optionally the device path of the initrd which we expose via the
EFI_LOAD_FILE2_PROTOCOL.

Allow to append another device-path pointing to the device-tree
identified by the device-tree GUID.

Signed-off-by: Heinrich Schuchardt <heinrich.schucha...@canonical.com>
Reviewed-by: Ilias Apalodimas <ilias.apalodi...@linaro.org>
---
v3:
        No change
v2:
        Carve out a function efi_load_option_dp_join() which we can
        use both for the eficonfig and the efidebug command.
        Rename variables id_dp, final_dp_size.
---
 cmd/eficonfig.c             | 79 ++++++++++++++++++++++++++++---------
 include/efi_loader.h        | 14 +++++++
 lib/efi_loader/efi_helper.c | 44 +++++++++++++++++++++
 3 files changed, 119 insertions(+), 18 deletions(-)

diff --git a/cmd/eficonfig.c b/cmd/eficonfig.c
index b13d9a3d2d9..a12df1e7d59 100644
--- a/cmd/eficonfig.c
+++ b/cmd/eficonfig.c
@@ -62,6 +62,7 @@ struct eficonfig_filepath_info {
 struct eficonfig_boot_option {
        struct eficonfig_select_file_info file_info;
        struct eficonfig_select_file_info initrd_info;
+       struct eficonfig_select_file_info fdt_info;
        unsigned int boot_index;
        u16 *description;
        u16 *optional_data;
@@ -1308,6 +1309,10 @@ static efi_status_t eficonfig_show_boot_option(struct 
eficonfig_boot_option *bo,
        if (ret != EFI_SUCCESS)
                goto out;
 
+       ret = prepare_file_selection_entry(efi_menu, "Fdt File: ", 
&bo->fdt_info);
+       if (ret != EFI_SUCCESS)
+               goto out;
+
        ret = create_boot_option_entry(efi_menu, "Optional Data: ", 
bo->optional_data,
                                       eficonfig_boot_add_optional_data, bo);
        if (ret != EFI_SUCCESS)
@@ -1388,27 +1393,44 @@ static efi_status_t eficonfig_edit_boot_option(u16 
*varname, struct eficonfig_bo
        efi_status_t ret;
        char *tmp = NULL, *p;
        struct efi_load_option lo = {0};
-       efi_uintn_t final_dp_size;
+       efi_uintn_t dp_size;
        struct efi_device_path *dp = NULL;
        efi_uintn_t size = load_option_size;
-       struct efi_device_path *final_dp = NULL;
        struct efi_device_path *device_dp = NULL;
        struct efi_device_path *initrd_dp = NULL;
+       struct efi_device_path *fdt_dp = NULL;
        struct efi_device_path *initrd_device_dp = NULL;
+       struct efi_device_path *fdt_device_dp = NULL;
 
-       const struct efi_initrd_dp id_dp = {
+       const struct efi_initrd_dp initrd_prefix = {
                .vendor = {
                        {
                        DEVICE_PATH_TYPE_MEDIA_DEVICE,
                        DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
-                       sizeof(id_dp.vendor),
+                       sizeof(initrd_prefix.vendor),
                        },
                        EFI_INITRD_MEDIA_GUID,
                },
                .end = {
                        DEVICE_PATH_TYPE_END,
                        DEVICE_PATH_SUB_TYPE_END,
-                       sizeof(id_dp.end),
+                       sizeof(initrd_prefix.end),
+               }
+       };
+
+       const struct efi_initrd_dp fdt_prefix = {
+               .vendor = {
+                       {
+                       DEVICE_PATH_TYPE_MEDIA_DEVICE,
+                       DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
+                       sizeof(fdt_prefix.vendor),
+                       },
+                       EFI_FDT_GUID,
+               },
+               .end = {
+                       DEVICE_PATH_TYPE_END,
+                       DEVICE_PATH_SUB_TYPE_END,
+                       sizeof(initrd_prefix.end),
                }
        };
 
@@ -1424,6 +1446,12 @@ static efi_status_t eficonfig_edit_boot_option(u16 
*varname, struct eficonfig_bo
                goto out;
        }
 
+       bo->fdt_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
+       if (!bo->fdt_info.current_path) {
+               ret =  EFI_OUT_OF_RESOURCES;
+               goto out;
+       }
+
        bo->description = calloc(1, EFICONFIG_DESCRIPTION_MAX * sizeof(u16));
        if (!bo->description) {
                ret =  EFI_OUT_OF_RESOURCES;
@@ -1456,13 +1484,20 @@ static efi_status_t eficonfig_edit_boot_option(u16 
*varname, struct eficonfig_bo
                if (lo.file_path)
                        fill_file_info(lo.file_path, &bo->file_info, device_dp);
 
-               /* Initrd file path(optional) is placed at second instance. */
+               /* Initrd file path (optional) is placed at second instance. */
                initrd_dp = efi_dp_from_lo(&lo, &efi_lf2_initrd_guid);
                if (initrd_dp) {
                        fill_file_info(initrd_dp, &bo->initrd_info, 
initrd_device_dp);
                        efi_free_pool(initrd_dp);
                }
 
+               /* Fdt file path (optional) is placed as third instance. */
+               fdt_dp = efi_dp_from_lo(&lo, &efi_guid_fdt);
+               if (fdt_dp) {
+                       fill_file_info(fdt_dp, &bo->fdt_info, fdt_device_dp);
+                       efi_free_pool(fdt_dp);
+               }
+
                if (size > 0)
                        memcpy(bo->optional_data, lo.optional_data, size);
        }
@@ -1484,26 +1519,31 @@ static efi_status_t eficonfig_edit_boot_option(u16 
*varname, struct eficonfig_bo
                        ret = EFI_OUT_OF_RESOURCES;
                        goto out;
                }
-               initrd_dp = efi_dp_concat((const struct efi_device_path 
*)&id_dp,
+               initrd_dp = efi_dp_concat((const struct efi_device_path 
*)&initrd_prefix,
                                          dp, 0);
                efi_free_pool(dp);
        }
 
+       if (bo->fdt_info.dp_volume) {
+               dp = eficonfig_create_device_path(bo->fdt_info.dp_volume,
+                                                 bo->fdt_info.current_path);
+               if (!dp) {
+                       ret = EFI_OUT_OF_RESOURCES;
+                       goto out;
+               }
+               fdt_dp = efi_dp_concat((const struct efi_device_path 
*)&fdt_prefix,
+                                      dp, 0);
+               efi_free_pool(dp);
+       }
+
        dp = eficonfig_create_device_path(bo->file_info.dp_volume, 
bo->file_info.current_path);
        if (!dp) {
                ret = EFI_OUT_OF_RESOURCES;
                goto out;
        }
-       final_dp_size = efi_dp_size(dp) + sizeof(END);
-       if (initrd_dp) {
-               final_dp = efi_dp_concat(dp, initrd_dp, 1);
-               final_dp_size += efi_dp_size(initrd_dp) + sizeof(END);
-       } else {
-               final_dp = efi_dp_dup(dp);
-       }
-       efi_free_pool(dp);
 
-       if (!final_dp)
+       ret = efi_load_option_dp_join(&dp, &dp_size, initrd_dp, fdt_dp);
+       if (ret != EFI_SUCCESS)
                goto out;
 
        if (utf16_utf8_strlen(bo->optional_data)) {
@@ -1515,17 +1555,20 @@ static efi_status_t eficonfig_edit_boot_option(u16 
*varname, struct eficonfig_bo
                utf16_utf8_strncpy(&p, bo->optional_data, 
u16_strlen(bo->optional_data));
        }
 
-       ret = eficonfig_set_boot_option(varname, final_dp, final_dp_size, 
bo->description, tmp);
+       ret = eficonfig_set_boot_option(varname, dp, dp_size, bo->description, 
tmp);
 out:
        free(tmp);
        free(bo->optional_data);
        free(bo->description);
        free(bo->file_info.current_path);
        free(bo->initrd_info.current_path);
+       free(bo->fdt_info.current_path);
        efi_free_pool(device_dp);
        efi_free_pool(initrd_device_dp);
        efi_free_pool(initrd_dp);
-       efi_free_pool(final_dp);
+       efi_free_pool(fdt_device_dp);
+       efi_free_pool(fdt_dp);
+       efi_free_pool(dp);
 
        return ret;
 }
diff --git a/include/efi_loader.h b/include/efi_loader.h
index ddf2e41a95c..1236eecff0f 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -1185,4 +1185,18 @@ efi_status_t efi_disk_get_device_name(const efi_handle_t 
handle, char *buf, int
  */
 void efi_add_known_memory(void);
 
+/**
+ * efi_load_option_dp_join() - join device-paths for load option
+ *
+ * @dp:                in: binary device-path, out: joined device-path
+ * @dp_size:   size of joined device-path
+ * @initrd_dp: initrd device-path or NULL
+ * @fdt_dp:    device-tree device-path or NULL
+ * Return:     status_code
+ */
+efi_status_t efi_load_option_dp_join(struct efi_device_path **dp,
+                                    size_t *dp_size,
+                                    struct efi_device_path *initrd_dp,
+                                    struct efi_device_path *fdt_dp);
+
 #endif /* _EFI_LOADER_H */
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
index 73d0279e843..348612c3dad 100644
--- a/lib/efi_loader/efi_helper.c
+++ b/lib/efi_loader/efi_helper.c
@@ -99,6 +99,50 @@ err:
        return NULL;
 }
 
+/**
+ * efi_load_option_dp_join() - join device-paths for load option
+ *
+ * @dp:                in: binary device-path, out: joined device-path
+ * @dp_size:   size of joined device-path
+ * @initrd_dp: initrd device-path or NULL
+ * @fdt_dp:    device-tree device-path or NULL
+ * Return:     status_code
+ */
+efi_status_t efi_load_option_dp_join(struct efi_device_path **dp,
+                                    size_t *dp_size,
+                                    struct efi_device_path *initrd_dp,
+                                    struct efi_device_path *fdt_dp)
+{
+       if (!dp)
+               return EFI_INVALID_PARAMETER;
+
+       *dp_size = efi_dp_size(*dp);
+
+       if (initrd_dp) {
+               struct efi_device_path *tmp_dp = *dp;
+
+               *dp = efi_dp_concat(tmp_dp, initrd_dp, *dp_size);
+               efi_free_pool(tmp_dp);
+               if (!*dp)
+                       return EFI_OUT_OF_RESOURCES;
+               *dp_size += efi_dp_size(initrd_dp) + sizeof(END);
+       }
+
+       if (fdt_dp) {
+               struct efi_device_path *tmp_dp = *dp;
+
+               *dp = efi_dp_concat(tmp_dp, fdt_dp, *dp_size);
+               efi_free_pool(tmp_dp);
+               if (!dp)
+                       return EFI_OUT_OF_RESOURCES;
+               *dp_size += efi_dp_size(fdt_dp) + sizeof(END);
+       }
+
+       *dp_size += sizeof(END);
+
+       return EFI_SUCCESS;
+}
+
 const struct guid_to_hash_map {
        efi_guid_t guid;
        const char algo[32];
-- 
2.43.0

Reply via email to