All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests
@ 2021-03-05 22:22 Ilias Apalodimas
  2021-03-05 22:22 ` [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot#### Ilias Apalodimas
                   ` (5 more replies)
  0 siblings, 6 replies; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-05 22:22 UTC (permalink / raw)
  To: u-boot

We are redefining how u-boot locates the initrd to load via the kernel
LoadFile2 protocol.  This selftest is not relevant any more, so remove
it. A new one will be added later

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
 lib/efi_selftest/Makefile                   |   1 -
 lib/efi_selftest/efi_selftest_load_initrd.c | 221 --------------------
 2 files changed, 222 deletions(-)
 delete mode 100644 lib/efi_selftest/efi_selftest_load_initrd.c

diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
index b02fd56e0a79..50de581b7763 100644
--- a/lib/efi_selftest/Makefile
+++ b/lib/efi_selftest/Makefile
@@ -61,7 +61,6 @@ obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o
 obj-$(CONFIG_EFI_LOADER_HII) += efi_selftest_hii.o
 obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_selftest_rng.o
 obj-$(CONFIG_EFI_GET_TIME) += efi_selftest_rtc.o
-obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_selftest_load_initrd.o
 obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_selftest_tcg2.o
 
 ifeq ($(CONFIG_GENERATE_ACPI_TABLE),)
diff --git a/lib/efi_selftest/efi_selftest_load_initrd.c b/lib/efi_selftest/efi_selftest_load_initrd.c
deleted file mode 100644
index f591dcd2115e..000000000000
--- a/lib/efi_selftest/efi_selftest_load_initrd.c
+++ /dev/null
@@ -1,221 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * efi_selftest_load_initrd
- *
- * Copyright (c) 2020 Ilias Apalodimas <ilias.apalodimas@linaro.org>
- *
- * This test checks the FileLoad2 protocol.
- * A known file is read from the file system and verified.
- *
- * An example usage - given a file image with a file system in partition 1
- * holding file initrd - is:
- *
- * * Configure the sandbox with
- *
- *   CONFIG_EFI_SELFTEST=y
- *   CONFIG_EFI_LOAD_FILE2_INITRD=y
- *   CONFIG_EFI_INITRD_FILESPEC="host 0:1 initrd"
- *
- * * Run ./u-boot and execute
- *
- *   host bind 0 image
- *   setenv efi_selftest load initrd
- *   bootefi selftest
- *
- * This would provide a test output like:
- *
- *   Testing EFI API implementation
- *
- *   Selected test: 'load initrd'
- *
- *   Setting up 'load initrd'
- *   Setting up 'load initrd' succeeded
- *
- *   Executing 'load initrd'
- *   Loaded 12378613 bytes
- *   CRC32 2997478465
- *
- * Now the size and CRC32 can be compared to the provided file.
- */
-
-#include <efi_selftest.h>
-#include <efi_loader.h>
-#include <efi_load_initrd.h>
-
-static struct efi_boot_services *boottime;
-
-static struct efi_initrd_dp dp = {
-	.vendor = {
-		{
-		   DEVICE_PATH_TYPE_MEDIA_DEVICE,
-		   DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
-		   sizeof(dp.vendor),
-		},
-		EFI_INITRD_MEDIA_GUID,
-	},
-	.end = {
-		DEVICE_PATH_TYPE_END,
-		DEVICE_PATH_SUB_TYPE_END,
-		sizeof(dp.end),
-	}
-};
-
-static struct efi_initrd_dp dp_invalid = {
-	.vendor = {
-		{
-		   DEVICE_PATH_TYPE_MEDIA_DEVICE,
-		   DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
-		   sizeof(dp.vendor),
-		},
-		EFI_INITRD_MEDIA_GUID,
-	},
-	.end = {
-		0x8f, /* invalid */
-		0xfe, /* invalid */
-		sizeof(dp.end),
-	}
-};
-
-static int setup(const efi_handle_t handle,
-		 const struct efi_system_table *systable)
-{
-	boottime = systable->boottime;
-
-	return EFI_ST_SUCCESS;
-}
-
-static int execute(void)
-{
-	struct efi_load_file_protocol *lf2;
-	struct efi_device_path *dp2, *dp2_invalid;
-	efi_status_t status;
-	efi_handle_t handle;
-	char buffer[64];
-	efi_uintn_t buffer_size;
-	void *buf;
-	u32 crc32;
-
-	memset(buffer, 0, sizeof(buffer));
-
-	dp2 = (struct efi_device_path *)&dp;
-	status = boottime->locate_device_path(&efi_guid_load_file2_protocol,
-					      &dp2, &handle);
-	if (status != EFI_SUCCESS) {
-		efi_st_error("Unable to locate device path\n");
-		return EFI_ST_FAILURE;
-	}
-
-	status = boottime->handle_protocol(handle,
-					   &efi_guid_load_file2_protocol,
-					   (void **)&lf2);
-	if (status != EFI_SUCCESS) {
-		efi_st_error("Unable to locate protocol\n");
-		return EFI_ST_FAILURE;
-	}
-
-	/* Case 1:
-	 * buffer_size can't be NULL
-	 * protocol can't be NULL
-	 */
-	status = lf2->load_file(lf2, dp2, false, NULL, &buffer);
-	if (status != EFI_INVALID_PARAMETER) {
-		efi_st_error("Buffer size can't be NULL\n");
-		return EFI_ST_FAILURE;
-	}
-	buffer_size = sizeof(buffer);
-	status = lf2->load_file(NULL, dp2, false, &buffer_size, &buffer);
-	if (status != EFI_INVALID_PARAMETER) {
-		efi_st_error("Protocol can't be NULL\n");
-		return EFI_ST_FAILURE;
-	}
-
-	/*
-	 * Case 2: Match end node type/sub-type on device path
-	 */
-	dp2_invalid = (struct efi_device_path *)&dp_invalid;
-	buffer_size = sizeof(buffer);
-	status = lf2->load_file(lf2, dp2_invalid, false, &buffer_size, &buffer);
-	if (status != EFI_INVALID_PARAMETER) {
-		efi_st_error("Invalid device path type must return EFI_INVALID_PARAMETER\n");
-		return EFI_ST_FAILURE;
-	}
-
-	status = lf2->load_file(lf2, dp2_invalid, false, &buffer_size, &buffer);
-	if (status != EFI_INVALID_PARAMETER) {
-		efi_st_error("Invalid device path sub-type must return EFI_INVALID_PARAMETER\n");
-		return EFI_ST_FAILURE;
-	}
-
-	/*
-	 * Case 3:
-	 * BootPolicy 'true' must return EFI_UNSUPPORTED
-	 */
-	buffer_size = sizeof(buffer);
-	status = lf2->load_file(lf2, dp2, true, &buffer_size, &buffer);
-	if (status != EFI_UNSUPPORTED) {
-		efi_st_error("BootPolicy true must return EFI_UNSUPPORTED\n");
-		return EFI_ST_FAILURE;
-	}
-
-	/*
-	 * Case: Pass buffer size as zero, firmware must return
-	 * EFI_BUFFER_TOO_SMALL and an appropriate size
-	 */
-	buffer_size = 0;
-	status = lf2->load_file(lf2, dp2, false, &buffer_size, NULL);
-	if (status != EFI_BUFFER_TOO_SMALL || !buffer_size) {
-		efi_st_printf("buffer_size: %u\n", (unsigned int)buffer_size);
-		efi_st_printf("status: %x\n", (unsigned int)status);
-		efi_st_error("Buffer size not updated\n");
-		return EFI_ST_FAILURE;
-	}
-
-	/*
-	 * Case: Pass buffer size as smaller than the file_size,
-	 * firmware must return * EFI_BUFFER_TOO_SMALL and an appropriate size
-	 */
-	buffer_size = 1;
-	status = lf2->load_file(lf2, dp2, false, &buffer_size, &buffer);
-	if (status != EFI_BUFFER_TOO_SMALL || buffer_size <= 1) {
-		efi_st_error("Buffer size not updated\n");
-		return EFI_ST_FAILURE;
-	}
-
-	status = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
-					 &buf);
-	if (status != EFI_SUCCESS) {
-		efi_st_error("Cannot allocate buffer\n");
-		return EFI_ST_FAILURE;
-	}
-
-	/* Case: Pass correct buffer, load the file and verify checksum*/
-	status = lf2->load_file(lf2, dp2, false, &buffer_size, buf);
-	if (status != EFI_SUCCESS) {
-		efi_st_error("Loading initrd failed\n");
-		return EFI_ST_FAILURE;
-	}
-
-	efi_st_printf("Loaded %u bytes\n", (unsigned int)buffer_size);
-	status = boottime->calculate_crc32(buf, buffer_size, &crc32);
-	if (status != EFI_SUCCESS) {
-		efi_st_error("Could not determine CRC32\n");
-		return EFI_ST_FAILURE;
-	}
-	efi_st_printf("CRC32 %.8x\n", (unsigned int)crc32);
-
-	status = boottime->free_pool(buf);
-	if (status != EFI_SUCCESS) {
-		efi_st_error("Cannot free buffer\n");
-		return EFI_ST_FAILURE;
-	}
-
-	return EFI_ST_SUCCESS;
-}
-
-EFI_UNIT_TEST(load_initrd) = {
-	.name = "load initrd",
-	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
-	.setup = setup,
-	.execute = execute,
-	.on_request = true,
-};
-- 
2.30.1

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-05 22:22 [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests Ilias Apalodimas
@ 2021-03-05 22:22 ` Ilias Apalodimas
  2021-03-11  7:50   ` Heinrich Schuchardt
  2021-03-05 22:22 ` [PATCH 3/6] efi_loader: Introduce helper functions for EFI Ilias Apalodimas
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-05 22:22 UTC (permalink / raw)
  To: u-boot

On the following patches we allow for an initrd path to be stored in
Boot#### variables.  Specifically we encode in the FIlePathList[] of
the EFI_LOAD_OPTIONS for each Boot#### variable.

The FilePathList[] array looks like this:
kernel - 0xff - VenMedia(initrd GUID) - 0x01 - initrd1 - 0x01 initrd2 -0xff
So let's add the relevant functions to concatenate and retrieve a device
path based on a Vendor GUID.

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
 include/efi_loader.h             |  4 ++
 lib/efi_loader/efi_device_path.c | 72 ++++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index f470bbd636f4..eb11a8c7d4b1 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -738,6 +738,10 @@ struct efi_load_option {
 	const u8 *optional_data;
 };
 
+struct efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
+				       efi_uintn_t *size, efi_guid_t guid);
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+				      const struct efi_device_path *dp2);
 efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
 					 efi_uintn_t *size);
 unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index c9315dd45857..cf1321828e87 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -310,6 +310,41 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
 	return ret;
 }
 
+/** efi_dp_concat() - Concatenate 2 device paths. The final device path will contain
+ *                    two device paths separated by and end node (0xff).
+ *
+ * @dp1:	First device path
+ * @size:	Second device path
+ *
+ * Return:	concatenated device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+				      const struct efi_device_path *dp2)
+{
+	struct efi_device_path *ret;
+	efi_uintn_t sz1, sz2;
+	void *p;
+
+	if (!dp1 || !dp2)
+		return NULL;
+	sz1 = efi_dp_size(dp1);
+	sz2 = efi_dp_size(dp2);
+	p = dp_alloc(sz1 + sz2 + (2 * sizeof(END)));
+	/* both dp1 and dp2 are non-null */
+	if (!p)
+		return NULL;
+	ret = p;
+	memcpy(p, dp1, sz1);
+	p += sz1;
+	memcpy(p, &END, sizeof(END));
+	p += sizeof(END);
+	memcpy(p, dp2, sz2);
+	p += sz2;
+	memcpy(p, &END, sizeof(END));
+
+	return ret;
+}
+
 struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
 					   const struct efi_device_path *node)
 {
@@ -1160,3 +1195,40 @@ ssize_t efi_dp_check_length(const struct efi_device_path *dp,
 		dp = (const struct efi_device_path *)((const u8 *)dp + len);
 	}
 }
+
+/**
+ * efi_dp_from_lo() - Get the instance of a Vendor Device Path
+ *		      in a multi-instance device path that matches
+ *		      a specific GUID
+ *
+ * @load_option: device paths to search
+ * @size:	 size of the discovered device path
+ * @guid:	 guid to search for
+ *
+ * Return:	device path or NULL. Caller must free the returned value
+ */
+struct
+efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
+				efi_uintn_t *size, efi_guid_t guid)
+{
+	struct efi_device_path *fp = lo->file_path;
+	struct efi_device_path_vendor *vendor;
+	int lo_len = lo->file_path_length;
+
+	while (lo_len) {
+		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
+		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) {
+			lo_len -= fp->length;
+			fp = (void *)fp + fp->length;
+			continue;
+		}
+
+		vendor = (struct efi_device_path_vendor *)fp;
+		if (!guidcmp(&vendor->guid, &guid))
+			return efi_dp_dup(fp);
+		lo_len -= fp->length;
+		fp = (void *)fp + fp->length;
+	}
+
+	return NULL;
+}
-- 
2.30.1

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 3/6] efi_loader: Introduce helper functions for EFI
  2021-03-05 22:22 [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests Ilias Apalodimas
  2021-03-05 22:22 ` [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot#### Ilias Apalodimas
@ 2021-03-05 22:22 ` Ilias Apalodimas
  2021-03-11  9:15   ` Heinrich Schuchardt
  2021-03-05 22:23 ` [PATCH 4/6] efi_loader: Replace config option for initrd loading Ilias Apalodimas
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-05 22:22 UTC (permalink / raw)
  To: u-boot

A following patch introduces a different logic for loading initrd's
based on the EFI_LOAD_FILE2_PROTOCOL.
Since similar logic can be applied in the future for other system files
(i.e DTBs), let's add some helper functions which will retrieve and
parse file paths stored in EFI variables.

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
 include/efi_helper.h        |  15 +++++
 lib/efi_loader/Makefile     |   1 +
 lib/efi_loader/efi_helper.c | 118 ++++++++++++++++++++++++++++++++++++
 3 files changed, 134 insertions(+)
 create mode 100644 include/efi_helper.h
 create mode 100644 lib/efi_loader/efi_helper.c

diff --git a/include/efi_helper.h b/include/efi_helper.h
new file mode 100644
index 000000000000..4980a1bb35d7
--- /dev/null
+++ b/include/efi_helper.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#if !defined _EFI_HELPER_H_
+#define _EFI_HELPER_H
+
+#include <efi.h>
+#include <efi_api.h>
+
+void *efi_get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size);
+struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid);
+
+#endif
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 10b42e8847bf..da2741adecfa 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -23,6 +23,7 @@ endif
 obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
 obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += efi_bootmgr.o
 obj-y += efi_boottime.o
+obj-y += efi_helper.o
 obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
 obj-$(CONFIG_EFI_CAPSULE_FIRMWARE) += efi_firmware.o
 obj-y += efi_console.o
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
new file mode 100644
index 000000000000..5437faa3ec49
--- /dev/null
+++ b/lib/efi_loader/efi_helper.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#include <common.h>
+#include <env.h>
+#include <malloc.h>
+#include <dm.h>
+#include <fs.h>
+#include <efi_helper.h>
+#include <efi_load_initrd.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+
+/**
+ * efi_get_var() - read value of an EFI variable
+ *
+ * @name:	variable name
+ * @start:	vendor GUID
+ * @size:	size of allocated buffer
+ *
+ * Return:	buffer with variable data or NULL
+ */
+void *efi_get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size)
+{
+	efi_status_t ret;
+	void *buf = NULL;
+
+	*size = 0;
+	ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+	if (ret == EFI_BUFFER_TOO_SMALL) {
+		buf = malloc(*size);
+		if (!buf)
+			return NULL;
+		ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+	}
+
+	if (ret != EFI_SUCCESS) {
+		free(buf);
+		*size = 0;
+		return NULL;
+	}
+
+	return buf;
+}
+
+/**
+ * create_boot_var_indexed() - Return Boot#### name were #### is replaced by
+ *			       the value of BootCurrent
+ *
+ * @var_name:		variable name
+ * @var_name_size:	size of var_name
+ *
+ * Return:	Status code
+ */
+static efi_status_t create_boot_var_indexed(u16 var_name[], size_t var_name_size)
+{
+	efi_uintn_t boot_current_size;
+	efi_status_t ret;
+	u16 boot_current;
+	u16 *pos;
+
+	boot_current_size = sizeof(boot_current);
+	ret = efi_get_variable_int(L"BootCurrent",
+				   &efi_global_variable_guid, NULL,
+				   &boot_current_size, &boot_current, NULL);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	pos = efi_create_indexed_name(var_name, var_name_size, "Boot",
+				      boot_current);
+	if (!pos) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
+	}
+
+out:
+	return ret;
+}
+
+/**
+ * efi_get_dp_from_boot() - Retrieve and return a device path from an EFI
+ *			    Boot### variable
+ *
+ * @guid:	Vendor guid of the VenMedia DP
+ *
+ * Return:	device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid)
+{
+	struct efi_device_path *file_path;
+	struct efi_load_option lo;
+	void *var_value = NULL;
+	efi_uintn_t size;
+	efi_status_t ret;
+	u16 var_name[16];
+
+	ret = create_boot_var_indexed(var_name, sizeof(var_name));
+	if (ret != EFI_SUCCESS)
+		return NULL;
+
+	var_value = efi_get_var(var_name, &efi_global_variable_guid, &size);
+	if (!var_value)
+		return NULL;
+
+	ret = efi_deserialize_load_option(&lo, var_value, &size);
+	if (ret != EFI_SUCCESS)
+		return NULL;
+
+	file_path = efi_dp_from_lo(&lo, &size, guid);
+	if (!file_path) {
+		log_debug("Missing file path in %ls", var_name);
+		return NULL;
+	}
+
+	return file_path;
+}
-- 
2.30.1

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 4/6] efi_loader: Replace config option for initrd loading
  2021-03-05 22:22 [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests Ilias Apalodimas
  2021-03-05 22:22 ` [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot#### Ilias Apalodimas
  2021-03-05 22:22 ` [PATCH 3/6] efi_loader: Introduce helper functions for EFI Ilias Apalodimas
@ 2021-03-05 22:23 ` Ilias Apalodimas
  2021-03-11 12:23   ` Heinrich Schuchardt
  2021-03-05 22:23 ` [PATCH 5/6] efidebug: add multiple device path instances on Boot#### Ilias Apalodimas
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-05 22:23 UTC (permalink / raw)
  To: u-boot

Up to now we install EFI_LOAD_FILE2_PROTOCOL to load an initrd
unconditionally. Although we correctly return various EFI exit codes
depending on the file status (i.e EFI_NO_MEDIA, EFI_NOT_FOUND etc), the
kernel loader, only falls back to the cmdline interpreted initrd if the
protocol is not installed.

This creates a problem for EFI installers, since they won't be able to
load their own initrd and continue the installation. It also makes the
feature hard to use, since we can either have a single initrd or we have
to recompile u-boot if the filename changes.

So let's introduce a different logic that will decouple the initrd
path from the config option we currently have.
When defining a UEFI BootXXXX we can use the filepathlist and store
a file path pointing to our initrd. Specifically the EFI spec describes:

"The first element of the array is a device path that describes the device
and location of the Image for this load option. Other device paths may
optionally exist in the FilePathList, but their usage is OSV specific"

When the EFI application is launched through the bootmgr, we'll try to
interpret the extra device path. If that points to a file that exists on
our disk, we'll now install the load_file2 and the efi-stub will be able
to use it.

This opens up another path using U-Boot and defines a new boot flow.
A user will be able to control the kernel/initrd pairs without explicit
cmdline args or GRUB.

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
 cmd/bootefi.c                    |   3 +
 include/efi_loader.h             |   1 +
 lib/efi_loader/Kconfig           |  15 +--
 lib/efi_loader/efi_bootmgr.c     |   3 +
 lib/efi_loader/efi_load_initrd.c | 209 +++++++++++++++++++++----------
 5 files changed, 152 insertions(+), 79 deletions(-)

diff --git a/cmd/bootefi.c b/cmd/bootefi.c
index 271b385edea6..cba81ffe75e4 100644
--- a/cmd/bootefi.c
+++ b/cmd/bootefi.c
@@ -358,6 +358,9 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options)
 
 	free(load_options);
 
+	if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD))
+		efi_initrd_deregister();
+
 	return ret;
 }
 
diff --git a/include/efi_loader.h b/include/efi_loader.h
index eb11a8c7d4b1..0d4f5d9acc9f 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -431,6 +431,7 @@ efi_status_t efi_net_register(void);
 /* Called by bootefi to make the watchdog available */
 efi_status_t efi_watchdog_register(void);
 efi_status_t efi_initrd_register(void);
+void efi_initrd_deregister(void);
 /* Called by bootefi to make SMBIOS tables available */
 /**
  * efi_acpi_register() - write out ACPI tables
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index e729f727df11..029f0e515f57 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -317,16 +317,11 @@ config EFI_LOAD_FILE2_INITRD
 	bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk"
 	default n
 	help
-	  Expose a EFI_FILE_LOAD2_PROTOCOL that the Linux UEFI stub can
-	  use to load the initial ramdisk. Once this is enabled using
-	  initrd=<ramdisk> will stop working.
-
-config EFI_INITRD_FILESPEC
-	string "initramfs path"
-	default "host 0:1 initrd"
-	depends on EFI_LOAD_FILE2_INITRD
-	help
-	  Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz.
+		Linux v5.7 and later can make use of this option. If the boot option
+		selected by the UEFI boot manager specifies an existing file to be used
+		as initial RAM disk, a Linux specific Load File2 protocol will be
+		installed and Linux 5.7+ will ignore any initrd=<ramdisk> command line
+		argument.
 
 config EFI_SECURE_BOOT
 	bool "Enable EFI secure boot support"
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 25f5cebfdb67..d1baa8c71a4d 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -118,6 +118,9 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
 		ret = efi_set_variable_int(L"BootCurrent",
 					   &efi_global_variable_guid,
 					   attributes, sizeof(n), &n, false);
+		/* try to register load file2 for initrd's */
+		if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD))
+			ret |= efi_initrd_register();
 		if (ret != EFI_SUCCESS) {
 			if (EFI_CALL(efi_unload_image(*handle))
 			    != EFI_SUCCESS)
diff --git a/lib/efi_loader/efi_load_initrd.c b/lib/efi_loader/efi_load_initrd.c
index b9ee8839054f..b11c5ee293fe 100644
--- a/lib/efi_loader/efi_load_initrd.c
+++ b/lib/efi_loader/efi_load_initrd.c
@@ -5,7 +5,9 @@
 
 #include <common.h>
 #include <efi_loader.h>
+#include <efi_helper.h>
 #include <efi_load_initrd.h>
+#include <efi_variable.h>
 #include <fs.h>
 #include <malloc.h>
 #include <mapmem.h>
@@ -39,41 +41,71 @@ static const struct efi_initrd_dp dp = {
 	}
 };
 
+static efi_handle_t efi_initrd_handle;
+
 /**
- * get_file_size() - retrieve the size of initramfs, set efi status on error
+ * get_initrd_fp() - Get initrd device path from a FilePathList device path
  *
- * @dev:			device to read from, e.g. "mmc"
- * @part:			device partition, e.g. "0:1"
- * @file:			name of file
- * @status:			EFI exit code in case of failure
+ * @initrd_fp:			the final initrd filepath
  *
- * Return:			size of file
+ * Return:			status code
  */
-static loff_t get_file_size(const char *dev, const char *part, const char *file,
-			    efi_status_t *status)
+static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
 {
-	loff_t sz = 0;
-	int ret;
+	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
+	struct efi_device_path *cur = NULL;
+	struct efi_device_path *dp = NULL;
+	struct efi_device_path *tmp_dp;
+	efi_uintn_t ret;
+	efi_uintn_t size;
 
-	ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
-	if (ret) {
-		*status = EFI_NO_MEDIA;
+	/*
+	 * if bootmgr is setup with and initrd, the device path will be
+	 * in the FilePathList[] of our load options in Boot####.
+	 * The first device path of the multi instance device path will
+	 * start with a VenMedia and the initrds will follow.
+	 *
+	 * If the device path is not found return EFI_INVALID_PARAMETER.
+	 * We can then use this specific return value and not install the
+	 * protocol, while allowing the boot to continue
+	 */
+	dp = efi_get_dp_from_boot(lf2_initrd_guid);
+	if (!dp) {
+		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
 
-	ret = fs_size(file, &sz);
-	if (ret) {
-		sz = 0;
-		*status = EFI_NOT_FOUND;
+	tmp_dp = dp;
+	cur = efi_dp_get_next_instance(&tmp_dp, &size);
+	if (!cur) {
+		ret = EFI_OUT_OF_RESOURCES;
 		goto out;
 	}
 
+	/*
+	 * We don't care if the file path is eventually NULL here. The
+	 * callers will try to load a file from it and eventually fail
+	 * but let's be explicit with our return values
+	 */
+	if (!tmp_dp) {
+		*initrd_fp = NULL;
+		ret = EFI_NOT_FOUND;
+	} else {
+		*initrd_fp = efi_dp_dup(tmp_dp);
+		if (*initrd_fp)
+			ret = EFI_SUCCESS;
+		else
+			ret = EFI_OUT_OF_RESOURCES;
+	}
+
 out:
-	return sz;
+	efi_free_pool(cur);
+	efi_free_pool(dp);
+	return ret;
 }
 
 /**
- * efi_load_file2initrd() - load initial RAM disk
+ * efi_load_file2_initrd() - load initial RAM disk
  *
  * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
  * in order to load an initial RAM disk requested by the Linux kernel stub.
@@ -93,98 +125,131 @@ efi_load_file2_initrd(struct efi_load_file_protocol *this,
 		      struct efi_device_path *file_path, bool boot_policy,
 		      efi_uintn_t *buffer_size, void *buffer)
 {
-	char *filespec;
-	efi_status_t status = EFI_NOT_FOUND;
-	loff_t file_sz = 0, read_sz = 0;
-	char *dev, *part, *file;
-	char *pos;
-	int ret;
+	struct efi_device_path *initrd_fp = NULL;
+	struct efi_file_info *info = NULL;
+	efi_status_t ret = EFI_NOT_FOUND;
+	struct efi_file_handle *f;
+	efi_uintn_t bs;
 
 	EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
 		  buffer_size, buffer);
 
-	filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
-	if (!filespec)
-		goto out;
-	pos = filespec;
-
 	if (!this || this != &efi_lf2_protocol ||
 	    !buffer_size) {
-		status = EFI_INVALID_PARAMETER;
+		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
 
 	if (file_path->type != dp.end.type ||
 	    file_path->sub_type != dp.end.sub_type) {
-		status = EFI_INVALID_PARAMETER;
+		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
 
 	if (boot_policy) {
-		status = EFI_UNSUPPORTED;
+		ret = EFI_UNSUPPORTED;
 		goto out;
 	}
 
-	/*
-	 * expect a string with three space separated parts:
-	 *
-	 * * a block device type, e.g. "mmc"
-	 * * a device and partition identifier, e.g. "0:1"
-	 * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
-	 */
-	dev = strsep(&pos, " ");
-	if (!dev)
+	ret = get_initrd_fp(&initrd_fp);
+	if (ret != EFI_SUCCESS)
 		goto out;
-	part = strsep(&pos, " ");
-	if (!part)
+
+	/* Open file */
+	f = efi_file_from_path(initrd_fp);
+	if (!f) {
+		ret = EFI_NOT_FOUND;
 		goto out;
-	file = strsep(&pos, " ");
-	if (!file)
+	}
+
+	/* Get file size */
+	bs = 0;
+	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
+				  &bs, info));
+	if (ret != EFI_BUFFER_TOO_SMALL) {
+		ret = EFI_DEVICE_ERROR;
 		goto out;
+	}
 
-	file_sz = get_file_size(dev, part, file, &status);
-	if (!file_sz)
+	info = malloc(bs);
+	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs,
+				  info));
+	if (ret != EFI_SUCCESS)
 		goto out;
 
-	if (!buffer || *buffer_size < file_sz) {
-		status = EFI_BUFFER_TOO_SMALL;
-		*buffer_size = file_sz;
+	bs = info->file_size;
+	if (!buffer || *buffer_size < bs) {
+		ret = EFI_BUFFER_TOO_SMALL;
+		*buffer_size = bs;
 	} else {
-		ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
-		if (ret) {
-			status = EFI_NO_MEDIA;
-			goto out;
-		}
-
-		ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
-			      &read_sz);
-		if (ret || read_sz != file_sz)
-			goto out;
-		*buffer_size = read_sz;
-
-		status = EFI_SUCCESS;
+		EFI_CALL(ret = f->read(f, &bs, (void *)(uintptr_t)buffer));
+		*buffer_size = bs;
 	}
 
 out:
-	free(filespec);
-	return EFI_EXIT(status);
+	free(info);
+	efi_free_pool(initrd_fp);
+	EFI_CALL(f->close(f));
+	return EFI_EXIT(ret);
+}
+
+/**
+ * check_initrd() - Determine if the file defined as an initrd in Boot####
+ *		    load_options device path is present
+ *
+ * Return:	status code
+ */
+static efi_status_t check_initrd(void)
+{
+	struct efi_device_path *initrd_fp = NULL;
+	struct efi_file_handle *f;
+	efi_status_t ret;
+
+	ret = get_initrd_fp(&initrd_fp);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	/*
+	 * If the file is not found, but the file path is set, return an error
+	 * and trigger the bootmgr fallback
+	 */
+	f = efi_file_from_path(initrd_fp);
+	if (!f) {
+		ret = EFI_NOT_FOUND;
+		goto out;
+	}
+
+	EFI_CALL(f->close(f));
+
+out:
+	efi_free_pool(initrd_fp);
+	return ret;
 }
 
 /**
  * efi_initrd_register() - create handle for loading initial RAM disk
  *
  * This function creates a new handle and installs a Linux specific vendor
- * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
+ * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
  * to identify the handle and then calls the LoadFile service of the
- * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
+ * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
  *
  * Return:	status code
  */
 efi_status_t efi_initrd_register(void)
 {
-	efi_handle_t efi_initrd_handle = NULL;
 	efi_status_t ret;
 
+	/*
+	 * Allow the user to continue if Boot#### file path is not set for
+	 * an initrd
+	 */
+	ret = check_initrd();
+	if (ret == EFI_INVALID_PARAMETER)
+		return EFI_SUCCESS;
+	if (ret != EFI_SUCCESS)
+		return ret;
+
 	ret = EFI_CALL(efi_install_multiple_protocol_interfaces
 		       (&efi_initrd_handle,
 			/* initramfs */
@@ -196,3 +261,9 @@ efi_status_t efi_initrd_register(void)
 
 	return ret;
 }
+
+void efi_initrd_deregister(void)
+{
+	efi_delete_handle(efi_initrd_handle);
+	efi_initrd_handle = NULL;
+}
-- 
2.30.1

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 5/6] efidebug: add multiple device path instances on Boot####
  2021-03-05 22:22 [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests Ilias Apalodimas
                   ` (2 preceding siblings ...)
  2021-03-05 22:23 ` [PATCH 4/6] efi_loader: Replace config option for initrd loading Ilias Apalodimas
@ 2021-03-05 22:23 ` Ilias Apalodimas
  2021-03-11 12:38   ` Heinrich Schuchardt
  2021-03-12  4:44   ` AKASHI Takahiro
  2021-03-05 22:23 ` [PATCH 6/6] doc: Update uefi documentation for initrd loading options Ilias Apalodimas
  2021-03-11  7:26 ` [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests Heinrich Schuchardt
  5 siblings, 2 replies; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-05 22:23 UTC (permalink / raw)
  To: u-boot

The UEFI spec allow a packed array of UEFI device paths in the
FilePathList[] of an EFI_LOAD_OPTION. The first file path must
describe the loaded image but the rest are OS specific.

Previous patches parse the device path and try to use the second
member of the array as an initrd. So let's modify efidebug slightly
and install the second file described in the command line as the
initrd device path.

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
 cmd/efidebug.c                                | 193 ++++++++++++++----
 doc/board/emulation/qemu_capsule_update.rst   |   4 +-
 doc/uefi/uefi.rst                             |   2 +-
 .../test_efi_capsule/test_capsule_firmware.py |   6 +-
 test/py/tests/test_efi_secboot/test_signed.py |  16 +-
 .../test_efi_secboot/test_signed_intca.py     |   8 +-
 .../tests/test_efi_secboot/test_unsigned.py   |   8 +-
 7 files changed, 179 insertions(+), 58 deletions(-)

diff --git a/cmd/efidebug.c b/cmd/efidebug.c
index bbbcb0a54643..9a1c471a3eaa 100644
--- a/cmd/efidebug.c
+++ b/cmd/efidebug.c
@@ -9,6 +9,8 @@
 #include <common.h>
 #include <command.h>
 #include <efi_dt_fixup.h>
+#include <efi_helper.h>
+#include <efi_load_initrd.h>
 #include <efi_loader.h>
 #include <efi_rng.h>
 #include <exports.h>
@@ -19,6 +21,7 @@
 #include <part.h>
 #include <search.h>
 #include <linux/ctype.h>
+#include <linux/err.h>
 
 #define BS systab.boottime
 #define RT systab.runtime
@@ -794,6 +797,65 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
 	return CMD_RET_SUCCESS;
 }
 
+/**
+ * add_initrd_instance() - Append a device path to load_options pointing to an
+ *			   inirtd
+ *
+ * @argc:	Number of arguments
+ * @argv:	Argument array
+ * @file_path	Existing device path, the new instance will be appended
+ * Return:	Pointer to the device path or ERR_PTR
+ *
+ */
+static
+struct efi_device_path *add_initrd_instance(const char *dev, const char *part,
+					    const char *file,
+					    const struct efi_device_path *fp,
+					    efi_uintn_t *fp_size)
+{
+	struct efi_device_path *tmp_dp = NULL, *tmp_fp = NULL;
+	struct efi_device_path *final_fp = NULL, *initrd_dp = NULL;
+	efi_status_t ret;
+	const struct efi_initrd_dp id_dp = {
+		.vendor = {
+			{
+			DEVICE_PATH_TYPE_MEDIA_DEVICE,
+			DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
+			sizeof(id_dp.vendor),
+			},
+			EFI_INITRD_MEDIA_GUID,
+		},
+		.end = {
+			DEVICE_PATH_TYPE_END,
+			DEVICE_PATH_SUB_TYPE_END,
+			sizeof(id_dp.end),
+		}
+	};
+
+	ret = efi_dp_from_name(dev, part, file, &tmp_dp, &tmp_fp);
+	if (ret != EFI_SUCCESS) {
+		printf("Cannot create device path for \"%s %s\"\n", part, file);
+		goto out;
+	}
+
+	initrd_dp =
+		efi_dp_append_instance((const struct efi_device_path *)&id_dp,
+				       tmp_fp);
+	if (!initrd_dp) {
+		printf("Cannot append media vendor device path path\n");
+		goto out;
+	}
+	final_fp = efi_dp_concat(fp, initrd_dp);
+	*fp_size = efi_dp_size(fp) + efi_dp_size(initrd_dp) +
+		(2 * sizeof(struct efi_device_path));
+
+out:
+	efi_free_pool(initrd_dp);
+	efi_free_pool(tmp_dp);
+	efi_free_pool(tmp_fp);
+	return final_fp ? final_fp : ERR_PTR(-EINVAL);
+}
+
 /**
  * do_efi_boot_add() - set UEFI load option
  *
@@ -806,7 +868,9 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
  *
  * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
  *
- *     efidebug boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
+ * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
+ *                   -i <file> <interface2> <devnum2>[:<part>] <initrd>
+ *                   -s '<options>'
  */
 static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
 			   int argc, char *const argv[])
@@ -819,55 +883,98 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
 	size_t label_len, label_len16;
 	u16 *label;
 	struct efi_device_path *device_path = NULL, *file_path = NULL;
+	struct efi_device_path *final_fp = NULL;
 	struct efi_load_option lo;
 	void *data = NULL;
 	efi_uintn_t size;
+	efi_uintn_t fp_size;
 	efi_status_t ret;
 	int r = CMD_RET_SUCCESS;
-
-	if (argc < 6 || argc > 7)
-		return CMD_RET_USAGE;
-
-	id = (int)simple_strtoul(argv[1], &endp, 16);
-	if (*endp != '\0' || id > 0xffff)
-		return CMD_RET_USAGE;
-
-	sprintf(var_name, "Boot%04X", id);
-	p = var_name16;
-	utf8_utf16_strncpy(&p, var_name, 9);
+	int i;
 
 	guid = efi_global_variable_guid;
 
 	/* attributes */
 	lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */
+	lo.optional_data = NULL;
+	lo.label = NULL;
 
-	/* label */
-	label_len = strlen(argv[2]);
-	label_len16 = utf8_utf16_strnlen(argv[2], label_len);
-	label = malloc((label_len16 + 1) * sizeof(u16));
-	if (!label)
-		return CMD_RET_FAILURE;
-	lo.label = label; /* label will be changed below */
-	utf8_utf16_strncpy(&label, argv[2], label_len);
+	/* search for -b first since the rest of the arguments depends on that */
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argv[i], "-b")) {
+			if (argc < i + 5 || lo.label) {
+				r = CMD_RET_USAGE;
+				goto out;
+			}
+			id = (int)simple_strtoul(argv[i + 1], &endp, 16);
+			if (*endp != '\0' || id > 0xffff)
+				return CMD_RET_USAGE;
+
+			sprintf(var_name, "Boot%04X", id);
+			p = var_name16;
+			utf8_utf16_strncpy(&p, var_name, 9);
+
+			/* label */
+			label_len = strlen(argv[i + 2]);
+			label_len16 = utf8_utf16_strnlen(argv[i + 2], label_len);
+			label = malloc((label_len16 + 1) * sizeof(u16));
+			if (!label)
+				return CMD_RET_FAILURE;
+			lo.label = label; /* label will be changed below */
+			utf8_utf16_strncpy(&label, argv[i + 2], label_len);
+
+			/* file path */
+			ret = efi_dp_from_name(argv[i + 3], argv[i + 4],
+					       argv[i + 5], &device_path,
+					       &file_path);
+			if (ret != EFI_SUCCESS) {
+				printf("Cannot create device path for \"%s %s\"\n",
+				       argv[3], argv[4]);
+				r = CMD_RET_FAILURE;
+				goto out;
+			break;
+			}
+			fp_size = efi_dp_size(file_path) +
+				sizeof(struct efi_device_path);
+		}
+	}
 
-	/* file path */
-	ret = efi_dp_from_name(argv[3], argv[4], argv[5], &device_path,
-			       &file_path);
-	if (ret != EFI_SUCCESS) {
-		printf("Cannot create device path for \"%s %s\"\n",
-		       argv[3], argv[4]);
+	if (!file_path) {
+		printf("You need to specify an image before an initrd.\n");
 		r = CMD_RET_FAILURE;
 		goto out;
 	}
-	lo.file_path = file_path;
-	lo.file_path_length = efi_dp_size(file_path)
-				+ sizeof(struct efi_device_path); /* for END */
 
-	/* optional data */
-	if (argc == 6)
-		lo.optional_data = NULL;
-	else
-		lo.optional_data = (const u8 *)argv[6];
+	/* add now add initrd and extra data */
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argv[i], "-i")) {
+			if (argc < i + 3 || final_fp) {
+				r = CMD_RET_USAGE;
+				goto out;
+			}
+
+			final_fp = add_initrd_instance(argv[i + 1], argv[i + 2],
+						       argv[i + 3], file_path,
+						       &fp_size);
+			if (IS_ERR(final_fp)) {
+				r = CMD_RET_FAILURE;
+				goto out;
+			}
+
+			/* add_initrd_instance allocates a new device path */
+			efi_free_pool(file_path);
+			file_path = final_fp;
+		} else if (!strcmp(argv[i], "-s")) {
+			if (argc < i + 1 || lo.optional_data) {
+				r = CMD_RET_USAGE;
+				goto out;
+			}
+			lo.optional_data = (const u8 *)argv[i + 1];
+		}
+	}
+
+	lo.file_path = file_path;
+	lo.file_path_length = fp_size;
 
 	size = efi_serialize_load_option(&lo, (u8 **)&data);
 	if (!size) {
@@ -951,11 +1058,14 @@ static int do_efi_boot_rm(struct cmd_tbl *cmdtp, int flag,
  */
 static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
 {
+	struct efi_device_path *initrd_path = NULL;
 	struct efi_load_option lo;
 	char *label, *p;
 	size_t label_len16, label_len;
 	u16 *dp_str;
 	efi_status_t ret;
+	efi_uintn_t initrd_dp_size;
+	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
 
 	ret = efi_deserialize_load_option(&lo, data, size);
 	if (ret != EFI_SUCCESS) {
@@ -986,6 +1096,14 @@ static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
 	printf("  file_path: %ls\n", dp_str);
 	efi_free_pool(dp_str);
 
+	initrd_path = efi_dp_from_lo(&lo, &initrd_dp_size, lf2_initrd_guid);
+	if (initrd_path) {
+		dp_str = efi_dp_str(initrd_path);
+		printf("  initrd_path: %ls\n", dp_str);
+		efi_free_pool(dp_str);
+		efi_free_pool(initrd_path);
+	}
+
 	printf("  data:\n");
 	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
 		       lo.optional_data, *size, true);
@@ -1555,7 +1673,10 @@ static int do_efidebug(struct cmd_tbl *cmdtp, int flag,
 static char efidebug_help_text[] =
 	"  - UEFI Shell-like interface to configure UEFI environment\n"
 	"\n"
-	"efidebug boot add <bootid> <label> <interface> <devnum>[:<part>] <file path> [<load options>]\n"
+	"efidebug boot add "
+	"-b <bootid> <label> <interface> <devnum>[:<part>] <file path> "
+	"-i <interface> <devnum>[:<part>] <initrd file path> "
+	"-s '<optional data>'\n"
 	"  - set UEFI BootXXXX variable\n"
 	"    <load options> will be passed to UEFI application\n"
 	"efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n"
@@ -1599,7 +1720,7 @@ static char efidebug_help_text[] =
 #endif
 
 U_BOOT_CMD(
-	efidebug, 10, 0, do_efidebug,
+	efidebug, CONFIG_SYS_MAXARGS, 0, do_efidebug,
 	"Configure UEFI environment",
 	efidebug_help_text
 );
diff --git a/doc/board/emulation/qemu_capsule_update.rst b/doc/board/emulation/qemu_capsule_update.rst
index 9fec75f8f1c9..33ce4bcd32ea 100644
--- a/doc/board/emulation/qemu_capsule_update.rst
+++ b/doc/board/emulation/qemu_capsule_update.rst
@@ -60,7 +60,7 @@ to be pointing to the EFI System Partition which contains the capsule
 file. The BootNext, BootXXXX and OsIndications variables can be set
 using the following commands::
 
-    => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
+    => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
     => efidebug boot next 0
     => setenv -e -nv -bs -rt -v OsIndications =0x04
     => saveenv
@@ -198,7 +198,7 @@ command line::
     3. Set the following environment and UEFI boot variables
 
         => setenv -e -nv -bs -rt -v OsIndications =0x04
-        => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
+        => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
         => efidebug boot next 0
         => saveenv
 
diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst
index 5a67737c1579..b3494c22e073 100644
--- a/doc/uefi/uefi.rst
+++ b/doc/uefi/uefi.rst
@@ -178,7 +178,7 @@ Now in U-Boot install the keys on your board::
 
 Set up boot parameters on your board::
 
-    efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed ""
+    efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed ""
 
 Now your board can run the signed image via the boot manager (see below).
 You can also try this sequence by running Pytest, test_efi_secboot,
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
index f006fa95d650..e8b0a1575453 100644
--- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
@@ -39,7 +39,7 @@ class TestEfiCapsuleFirmwareFit(object):
         with u_boot_console.log.section('Test Case 1-a, before reboot'):
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
                 'efidebug boot order 1',
                 'env set -e OsIndications',
                 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
@@ -114,7 +114,7 @@ class TestEfiCapsuleFirmwareFit(object):
         with u_boot_console.log.section('Test Case 2-a, before reboot'):
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
                 'efidebug boot order 1',
                 'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
                 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
@@ -188,7 +188,7 @@ class TestEfiCapsuleFirmwareFit(object):
         with u_boot_console.log.section('Test Case 3-a, before reboot'):
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
                 'efidebug boot order 1',
                 'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
                 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py
index 863685e215b7..75f5ea772300 100644
--- a/test/py/tests/test_efi_secboot/test_signed.py
+++ b/test/py/tests/test_efi_secboot/test_signed.py
@@ -28,7 +28,7 @@ class TestEfiSignedImage(object):
             # Test Case 1a, run signed image if no PK
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert 'Hello, world!' in ''.join(output)
@@ -36,7 +36,7 @@ class TestEfiSignedImage(object):
         with u_boot_console.log.section('Test Case 1b'):
             # Test Case 1b, run unsigned image if no PK
             output = u_boot_console.run_command_list([
-                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
                 'efidebug boot next 2',
                 'bootefi bootmgr'])
             assert 'Hello, world!' in ''.join(output)
@@ -58,13 +58,13 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
             assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert('\'HELLO1\' failed' in ''.join(output))
             assert('efi_start_image() returned: 26' in ''.join(output))
             output = u_boot_console.run_command_list([
-                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
                 'efidebug boot next 2',
                 'efidebug test bootmgr'])
             assert '\'HELLO2\' failed' in ''.join(output)
@@ -104,7 +104,7 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
             assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert '\'HELLO\' failed' in ''.join(output)
@@ -142,7 +142,7 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
             assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert '\'HELLO\' failed' in ''.join(output)
@@ -169,7 +169,7 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
             assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert 'Hello, world!' in ''.join(output)
@@ -227,7 +227,7 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
             assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert 'Hello, world!' in ''.join(output)
diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py
index 70d6be00e8a8..0849572a5143 100644
--- a/test/py/tests/test_efi_secboot/test_signed_intca.py
+++ b/test/py/tests/test_efi_secboot/test_signed_intca.py
@@ -39,7 +39,7 @@ class TestEfiSignedImageIntca(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
+                'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert '\'HELLO_a\' failed' in ''.join(output)
@@ -48,7 +48,7 @@ class TestEfiSignedImageIntca(object):
         with u_boot_console.log.section('Test Case 1b'):
             # Test Case 1b, signed and authenticated by root CA
             output = u_boot_console.run_command_list([
-                'efidebug boot add 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
+                'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
                 'efidebug boot next 2',
                 'bootefi bootmgr'])
             assert 'Hello, world!' in ''.join(output)
@@ -70,7 +70,7 @@ class TestEfiSignedImageIntca(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
+                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert '\'HELLO_abc\' failed' in ''.join(output)
@@ -116,7 +116,7 @@ class TestEfiSignedImageIntca(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
+                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert 'Hello, world!' in ''.join(output)
diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py
index 56f56e19eb84..8e026f7566ad 100644
--- a/test/py/tests/test_efi_secboot/test_unsigned.py
+++ b/test/py/tests/test_efi_secboot/test_unsigned.py
@@ -35,7 +35,7 @@ class TestEfiUnsignedImage(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert '\'HELLO\' failed' in ''.join(output)
@@ -64,7 +64,7 @@ class TestEfiUnsignedImage(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert 'Hello, world!' in ''.join(output)
@@ -88,7 +88,7 @@ class TestEfiUnsignedImage(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert '\'HELLO\' failed' in ''.join(output)
@@ -106,7 +106,7 @@ class TestEfiUnsignedImage(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert '\'HELLO\' failed' in ''.join(output)
-- 
2.30.1

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 6/6] doc: Update uefi documentation for initrd loading options
  2021-03-05 22:22 [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests Ilias Apalodimas
                   ` (3 preceding siblings ...)
  2021-03-05 22:23 ` [PATCH 5/6] efidebug: add multiple device path instances on Boot#### Ilias Apalodimas
@ 2021-03-05 22:23 ` Ilias Apalodimas
  2021-03-11 12:39   ` Heinrich Schuchardt
  2021-03-11  7:26 ` [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests Heinrich Schuchardt
  5 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-05 22:23 UTC (permalink / raw)
  To: u-boot

Document the command line options for efidebug and initrd loading

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
 doc/uefi/uefi.rst | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst
index b3494c22e073..b3330f8d7273 100644
--- a/doc/uefi/uefi.rst
+++ b/doc/uefi/uefi.rst
@@ -180,6 +180,12 @@ Set up boot parameters on your board::
 
     efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed ""
 
+Since kernel 5.7 there's an alternative way of loading an initrd using
+LoadFile2 protocol if CONFIG_EFI_LOAD_FILE2_INITRD is enabled.
+The initrd path can be specified with::
+
+    efidebug boot add -b ABE0 'kernel' mmc 0:1 Image -i mmc 0:1 initrd
+
 Now your board can run the signed image via the boot manager (see below).
 You can also try this sequence by running Pytest, test_efi_secboot,
 on the sandbox
-- 
2.30.1

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests
  2021-03-05 22:22 [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests Ilias Apalodimas
                   ` (4 preceding siblings ...)
  2021-03-05 22:23 ` [PATCH 6/6] doc: Update uefi documentation for initrd loading options Ilias Apalodimas
@ 2021-03-11  7:26 ` Heinrich Schuchardt
  5 siblings, 0 replies; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11  7:26 UTC (permalink / raw)
  To: u-boot

On 3/5/21 11:22 PM, Ilias Apalodimas wrote:
> We are redefining how u-boot locates the initrd to load via the kernel
> LoadFile2 protocol.  This selftest is not relevant any more, so remove
> it. A new one will be added later
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

We can use lib/efi_selftest/initrddump.c in a Python test.

Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-05 22:22 ` [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot#### Ilias Apalodimas
@ 2021-03-11  7:50   ` Heinrich Schuchardt
  2021-03-11  9:10     ` Ilias Apalodimas
  0 siblings, 1 reply; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11  7:50 UTC (permalink / raw)
  To: u-boot

On 3/5/21 11:22 PM, Ilias Apalodimas wrote:
> On the following patches we allow for an initrd path to be stored in
> Boot#### variables.  Specifically we encode in the FIlePathList[] of
> the EFI_LOAD_OPTIONS for each Boot#### variable.
>
> The FilePathList[] array looks like this:
> kernel - 0xff - VenMedia(initrd GUID) - 0x01 - initrd1 - 0x01 initrd2 -0xff
> So let's add the relevant functions to concatenate and retrieve a device
> path based on a Vendor GUID.
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> ---
>   include/efi_loader.h             |  4 ++
>   lib/efi_loader/efi_device_path.c | 72 ++++++++++++++++++++++++++++++++
>   2 files changed, 76 insertions(+)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index f470bbd636f4..eb11a8c7d4b1 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -738,6 +738,10 @@ struct efi_load_option {
>   	const u8 *optional_data;
>   };
>
> +struct efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
> +				       efi_uintn_t *size, efi_guid_t guid);
> +struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
> +				      const struct efi_device_path *dp2);
>   efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
>   					 efi_uintn_t *size);
>   unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
> diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
> index c9315dd45857..cf1321828e87 100644
> --- a/lib/efi_loader/efi_device_path.c
> +++ b/lib/efi_loader/efi_device_path.c
> @@ -310,6 +310,41 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
>   	return ret;
>   }
>
> +/** efi_dp_concat() - Concatenate 2 device paths. The final device path will contain
> + *                    two device paths separated by and end node (0xff).
> + *
> + * @dp1:	First device path
> + * @size:	Second device path
> + *
> + * Return:	concatenated device path or NULL. Caller must free the returned value
> + */
> +struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
> +				      const struct efi_device_path *dp2)
> +{
> +	struct efi_device_path *ret;
> +	efi_uintn_t sz1, sz2;
> +	void *p;
> +
> +	if (!dp1 || !dp2)
> +		return NULL;
> +	sz1 = efi_dp_size(dp1);
> +	sz2 = efi_dp_size(dp2);
> +	p = dp_alloc(sz1 + sz2 + (2 * sizeof(END)));
> +	/* both dp1 and dp2 are non-null */
> +	if (!p)
> +		return NULL;
> +	ret = p;
> +	memcpy(p, dp1, sz1);
> +	p += sz1;
> +	memcpy(p, &END, sizeof(END));
> +	p += sizeof(END);
> +	memcpy(p, dp2, sz2);
> +	p += sz2;
> +	memcpy(p, &END, sizeof(END));
> +
> +	return ret;
> +}
> +
>   struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
>   					   const struct efi_device_path *node)
>   {
> @@ -1160,3 +1195,40 @@ ssize_t efi_dp_check_length(const struct efi_device_path *dp,
>   		dp = (const struct efi_device_path *)((const u8 *)dp + len);
>   	}
>   }
> +
> +/**
> + * efi_dp_from_lo() - Get the instance of a Vendor Device Path
> + *		      in a multi-instance device path that matches
> + *		      a specific GUID

The description does make it clear that you are looking for a VenMedia()
node.

Please, describe what the function is good for (find the device path for
an initrd or dtb in a load option).

> + *
> + * @load_option: device paths to search
> + * @size:	 size of the discovered device path
> + * @guid:	 guid to search for
> + *
> + * Return:	device path or NULL. Caller must free the returned value

Please, keep the text aligned.

Do we need a copy? Isn't a pointer good enough?

> + */
> +struct
> +efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
> +				efi_uintn_t *size, efi_guid_t guid)
> +{
> +	struct efi_device_path *fp = lo->file_path;
> +	struct efi_device_path_vendor *vendor;
> +	int lo_len = lo->file_path_length;
> +
> +	while (lo_len) {

lo_len must be at least sizeof(struct efi_device_path).

> +		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
> +		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) {
> +			lo_len -= fp->length;

Could the last device path in the array be followed by zero bytes for
padding?
Should we check that fp->length >= sizeof(struct efi_device_path)?

> +			fp = (void *)fp + fp->length;

Please, avoid code duplication.

E.g.

for (; lo_len >= sizeof(struct efi_device_path);
      lo_len -= fp->length, fp = (void *)fp + fp->length) {

> +			continue;
> +		}
> +
> +		vendor = (struct efi_device_path_vendor *)fp;
> +		if (!guidcmp(&vendor->guid, &guid))
> +			return efi_dp_dup(fp);

Should we strip of the VenMedia() node here?

Best regards

Heinrich

> +		lo_len -= fp->length;
> +		fp = (void *)fp + fp->length;
> +	}
> +
> +	return NULL;
> +}
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11  7:50   ` Heinrich Schuchardt
@ 2021-03-11  9:10     ` Ilias Apalodimas
  2021-03-11 11:00       ` Heinrich Schuchardt
  0 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-11  9:10 UTC (permalink / raw)
  To: u-boot

On Thu, Mar 11, 2021 at 08:50:22AM +0100, Heinrich Schuchardt wrote:
> On 3/5/21 11:22 PM, Ilias Apalodimas wrote:
> > On the following patches we allow for an initrd path to be stored in
> > Boot#### variables.  Specifically we encode in the FIlePathList[] of
> > the EFI_LOAD_OPTIONS for each Boot#### variable.
> > 
> > The FilePathList[] array looks like this:
> > kernel - 0xff - VenMedia(initrd GUID) - 0x01 - initrd1 - 0x01 initrd2 -0xff
> > So let's add the relevant functions to concatenate and retrieve a device
> > path based on a Vendor GUID.
> > 
> > Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > ---
> >   include/efi_loader.h             |  4 ++
> >   lib/efi_loader/efi_device_path.c | 72 ++++++++++++++++++++++++++++++++
> >   2 files changed, 76 insertions(+)
> > 
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index f470bbd636f4..eb11a8c7d4b1 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -738,6 +738,10 @@ struct efi_load_option {
> >   	const u8 *optional_data;
> >   };
> > 
> > +struct efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
> > +				       efi_uintn_t *size, efi_guid_t guid);
> > +struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
> > +				      const struct efi_device_path *dp2);
> >   efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
> >   					 efi_uintn_t *size);
> >   unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
> > diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
> > index c9315dd45857..cf1321828e87 100644
> > --- a/lib/efi_loader/efi_device_path.c
> > +++ b/lib/efi_loader/efi_device_path.c
> > @@ -310,6 +310,41 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
> >   	return ret;
> >   }
> > 
> > +/** efi_dp_concat() - Concatenate 2 device paths. The final device path will contain
> > + *                    two device paths separated by and end node (0xff).
> > + *
> > + * @dp1:	First device path
> > + * @size:	Second device path
> > + *
> > + * Return:	concatenated device path or NULL. Caller must free the returned value
> > + */
> > +struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
> > +				      const struct efi_device_path *dp2)
> > +{
> > +	struct efi_device_path *ret;
> > +	efi_uintn_t sz1, sz2;
> > +	void *p;
> > +
> > +	if (!dp1 || !dp2)
> > +		return NULL;
> > +	sz1 = efi_dp_size(dp1);
> > +	sz2 = efi_dp_size(dp2);
> > +	p = dp_alloc(sz1 + sz2 + (2 * sizeof(END)));
> > +	/* both dp1 and dp2 are non-null */
> > +	if (!p)
> > +		return NULL;
> > +	ret = p;
> > +	memcpy(p, dp1, sz1);
> > +	p += sz1;
> > +	memcpy(p, &END, sizeof(END));
> > +	p += sizeof(END);
> > +	memcpy(p, dp2, sz2);
> > +	p += sz2;
> > +	memcpy(p, &END, sizeof(END));
> > +
> > +	return ret;
> > +}
> > +
> >   struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
> >   					   const struct efi_device_path *node)
> >   {
> > @@ -1160,3 +1195,40 @@ ssize_t efi_dp_check_length(const struct efi_device_path *dp,
> >   		dp = (const struct efi_device_path *)((const u8 *)dp + len);
> >   	}
> >   }
> > +
> > +/**
> > + * efi_dp_from_lo() - Get the instance of a Vendor Device Path
> > + *		      in a multi-instance device path that matches
> > + *		      a specific GUID
> 
> The description does make it clear that you are looking for a VenMedia()
> node.
> 
> Please, describe what the function is good for (find the device path for
> an initrd or dtb in a load option).
> 

Ok 

> > + *
> > + * @load_option: device paths to search
> > + * @size:	 size of the discovered device path
> > + * @guid:	 guid to search for
> > + *
> > + * Return:	device path or NULL. Caller must free the returned value
> 
> Please, keep the text aligned.
> 
> Do we need a copy? Isn't a pointer good enough?

A pointer to what? 
I think it's better to a copy here. This is a device path that might be used
out of a stack context were the load option might disappear.
Look at how the function is used in efi_get_dp_from_boot(). 

> 
> > + */
> > +struct
> > +efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
> > +				efi_uintn_t *size, efi_guid_t guid)
> > +{
> > +	struct efi_device_path *fp = lo->file_path;
> > +	struct efi_device_path_vendor *vendor;
> > +	int lo_len = lo->file_path_length;
> > +
> > +	while (lo_len) {
> 
> lo_len must be at least sizeof(struct efi_device_path).
> 
> > +		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
> > +		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) {
> > +			lo_len -= fp->length;
> 
> Could the last device path in the array be followed by zero bytes for
> padding?

How? Device paths are packed aren't they ?

> Should we check that fp->length >= sizeof(struct efi_device_path)?

Yea probably a good idea 

> 
> > +			fp = (void *)fp + fp->length;
> 
> Please, avoid code duplication.
> 
> E.g.
> 
> for (; lo_len >= sizeof(struct efi_device_path);
>      lo_len -= fp->length, fp = (void *)fp + fp->length) {

I can an switch to that, but I really never liked this format.
It always seemed way less readable to me for some reason.  Maybe because I
never got used to it ...

> 
> > +			continue;
> > +		}
> > +
> > +		vendor = (struct efi_device_path_vendor *)fp;
> > +		if (!guidcmp(&vendor->guid, &guid))
> > +			return efi_dp_dup(fp);
> 
> Should we strip of the VenMedia() node here?

Why? This is not supposed to get the file path. The function says "get device
path from load option" and that device includes the VenMedia node.
It would make more sense for me to strip in efi_get_dp_from_boot() for
example, if you want a helper function to get the initrd path *only*.

But really it's just one invocation of efi_dp_get_next_instance() after
whatever device path you get. Which also modifies the device path pointer, so
I'd really prefer keeping that call in a local context.


Thanks
/Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 3/6] efi_loader: Introduce helper functions for EFI
  2021-03-05 22:22 ` [PATCH 3/6] efi_loader: Introduce helper functions for EFI Ilias Apalodimas
@ 2021-03-11  9:15   ` Heinrich Schuchardt
  0 siblings, 0 replies; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11  9:15 UTC (permalink / raw)
  To: u-boot

On 05.03.21 23:22, Ilias Apalodimas wrote:
> A following patch introduces a different logic for loading initrd's
> based on the EFI_LOAD_FILE2_PROTOCOL.
> Since similar logic can be applied in the future for other system files
> (i.e DTBs), let's add some helper functions which will retrieve and
> parse file paths stored in EFI variables.
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> ---
>  include/efi_helper.h        |  15 +++++
>  lib/efi_loader/Makefile     |   1 +
>  lib/efi_loader/efi_helper.c | 118 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 134 insertions(+)
>  create mode 100644 include/efi_helper.h
>  create mode 100644 lib/efi_loader/efi_helper.c
>
> diff --git a/include/efi_helper.h b/include/efi_helper.h
> new file mode 100644
> index 000000000000..4980a1bb35d7
> --- /dev/null
> +++ b/include/efi_helper.h
> @@ -0,0 +1,15 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (c) 2020, Linaro Limited
> + */
> +
> +#if !defined _EFI_HELPER_H_
> +#define _EFI_HELPER_H
> +
> +#include <efi.h>
> +#include <efi_api.h>
> +
> +void *efi_get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size);
> +struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid);
> +
> +#endif
> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> index 10b42e8847bf..da2741adecfa 100644
> --- a/lib/efi_loader/Makefile
> +++ b/lib/efi_loader/Makefile
> @@ -23,6 +23,7 @@ endif
>  obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
>  obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += efi_bootmgr.o
>  obj-y += efi_boottime.o
> +obj-y += efi_helper.o
>  obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
>  obj-$(CONFIG_EFI_CAPSULE_FIRMWARE) += efi_firmware.o
>  obj-y += efi_console.o
> diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
> new file mode 100644
> index 000000000000..5437faa3ec49
> --- /dev/null
> +++ b/lib/efi_loader/efi_helper.c
> @@ -0,0 +1,118 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (c) 2020, Linaro Limited
> + */
> +
> +#include <common.h>
> +#include <env.h>
> +#include <malloc.h>
> +#include <dm.h>
> +#include <fs.h>
> +#include <efi_helper.h>
> +#include <efi_load_initrd.h>
> +#include <efi_loader.h>
> +#include <efi_variable.h>
> +
> +/**
> + * efi_get_var() - read value of an EFI variable
> + *
> + * @name:	variable name
> + * @start:	vendor GUID
> + * @size:	size of allocated buffer
> + *
> + * Return:	buffer with variable data or NULL
> + */
> +void *efi_get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size)

Please, move the function to lib/efi_loader/efi_var_common.c.

> +{
> +	efi_status_t ret;
> +	void *buf = NULL;
> +
> +	*size = 0;
> +	ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
> +	if (ret == EFI_BUFFER_TOO_SMALL) {
> +		buf = malloc(*size);
> +		if (!buf)
> +			return NULL;
> +		ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
> +	}
> +
> +	if (ret != EFI_SUCCESS) {
> +		free(buf);
> +		*size = 0;
> +		return NULL;
> +	}
> +
> +	return buf;
> +}
> +
> +/**
> + * create_boot_var_indexed() - Return Boot#### name were #### is replaced by
> + *			       the value of BootCurrent
> + *
> + * @var_name:		variable name
> + * @var_name_size:	size of var_name
> + *
> + * Return:	Status code
> + */
> +static efi_status_t create_boot_var_indexed(u16 var_name[], size_t var_name_size)

The function name does not convey that it is related to BootCurrent.

How about efi_create_current_boot_var()?

> +{
> +	efi_uintn_t boot_current_size;
> +	efi_status_t ret;
> +	u16 boot_current;
> +	u16 *pos;
> +
> +	boot_current_size = sizeof(boot_current);
> +	ret = efi_get_variable_int(L"BootCurrent",
> +				   &efi_global_variable_guid, NULL,
> +				   &boot_current_size, &boot_current, NULL);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	pos = efi_create_indexed_name(var_name, var_name_size, "Boot",
> +				      boot_current);
> +	if (!pos) {
> +		ret = EFI_OUT_OF_RESOURCES;
> +		goto out;
> +	}
> +
> +out:
> +	return ret;
> +}
> +
> +/**
> + * efi_get_dp_from_boot() - Retrieve and return a device path from an EFI
> + *			    Boot### variable
> + *
> + * @guid:	Vendor guid of the VenMedia DP
> + *
> + * Return:	device path or NULL. Caller must free the returned value
> + */
> +struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid)
> +{
> +	struct efi_device_path *file_path;
> +	struct efi_load_option lo;
> +	void *var_value = NULL;
> +	efi_uintn_t size;
> +	efi_status_t ret;
> +	u16 var_name[16];
> +
> +	ret = create_boot_var_indexed(var_name, sizeof(var_name));
> +	if (ret != EFI_SUCCESS)
> +		return NULL;
> +
> +	var_value = efi_get_var(var_name, &efi_global_variable_guid, &size);
> +	if (!var_value)
> +		return NULL;
> +
> +	ret = efi_deserialize_load_option(&lo, var_value, &size);
> +	if (ret != EFI_SUCCESS)
> +		return NULL;
> +
> +	file_path = efi_dp_from_lo(&lo, &size, guid);
> +	if (!file_path) {
> +		log_debug("Missing file path in %ls", var_name);

Missing LF. Knowing the GUID would help. I suggest:

log_debug("VenMedia(%pUl) not found in %ls\n", guid, var_name);

Better move the message to efi_dp_from_lo().

Please, add

    #define LOG_CATEGORY LOGC_EFI

before the first #include to allow filtering for EFI messages.

Best regards

Heinrich

> +		return NULL;
> +	}
> +
> +	return file_path;
> +}
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11  9:10     ` Ilias Apalodimas
@ 2021-03-11 11:00       ` Heinrich Schuchardt
  2021-03-11 11:36         ` Ilias Apalodimas
  0 siblings, 1 reply; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11 11:00 UTC (permalink / raw)
  To: u-boot

On 11.03.21 10:10, Ilias Apalodimas wrote:
> On Thu, Mar 11, 2021 at 08:50:22AM +0100, Heinrich Schuchardt wrote:
>> On 3/5/21 11:22 PM, Ilias Apalodimas wrote:
>>> On the following patches we allow for an initrd path to be stored in
>>> Boot#### variables.  Specifically we encode in the FIlePathList[] of
>>> the EFI_LOAD_OPTIONS for each Boot#### variable.
>>>
>>> The FilePathList[] array looks like this:
>>> kernel - 0xff - VenMedia(initrd GUID) - 0x01 - initrd1 - 0x01 initrd2 -0xff
>>> So let's add the relevant functions to concatenate and retrieve a device
>>> path based on a Vendor GUID.
>>>
>>> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
>>> ---
>>>   include/efi_loader.h             |  4 ++
>>>   lib/efi_loader/efi_device_path.c | 72 ++++++++++++++++++++++++++++++++
>>>   2 files changed, 76 insertions(+)
>>>
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index f470bbd636f4..eb11a8c7d4b1 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -738,6 +738,10 @@ struct efi_load_option {
>>>   	const u8 *optional_data;
>>>   };
>>>
>>> +struct efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
>>> +				       efi_uintn_t *size, efi_guid_t guid);
>>> +struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
>>> +				      const struct efi_device_path *dp2);
>>>   efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
>>>   					 efi_uintn_t *size);
>>>   unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
>>> diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
>>> index c9315dd45857..cf1321828e87 100644
>>> --- a/lib/efi_loader/efi_device_path.c
>>> +++ b/lib/efi_loader/efi_device_path.c
>>> @@ -310,6 +310,41 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
>>>   	return ret;
>>>   }
>>>
>>> +/** efi_dp_concat() - Concatenate 2 device paths. The final device path will contain
>>> + *                    two device paths separated by and end node (0xff).
>>> + *
>>> + * @dp1:	First device path
>>> + * @size:	Second device path
>>> + *
>>> + * Return:	concatenated device path or NULL. Caller must free the returned value
>>> + */
>>> +struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
>>> +				      const struct efi_device_path *dp2)
>>> +{
>>> +	struct efi_device_path *ret;
>>> +	efi_uintn_t sz1, sz2;
>>> +	void *p;
>>> +
>>> +	if (!dp1 || !dp2)
>>> +		return NULL;
>>> +	sz1 = efi_dp_size(dp1);
>>> +	sz2 = efi_dp_size(dp2);
>>> +	p = dp_alloc(sz1 + sz2 + (2 * sizeof(END)));
>>> +	/* both dp1 and dp2 are non-null */
>>> +	if (!p)
>>> +		return NULL;
>>> +	ret = p;
>>> +	memcpy(p, dp1, sz1);
>>> +	p += sz1;
>>> +	memcpy(p, &END, sizeof(END));
>>> +	p += sizeof(END);
>>> +	memcpy(p, dp2, sz2);
>>> +	p += sz2;
>>> +	memcpy(p, &END, sizeof(END));
>>> +
>>> +	return ret;
>>> +}
>>> +
>>>   struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
>>>   					   const struct efi_device_path *node)
>>>   {
>>> @@ -1160,3 +1195,40 @@ ssize_t efi_dp_check_length(const struct efi_device_path *dp,
>>>   		dp = (const struct efi_device_path *)((const u8 *)dp + len);
>>>   	}
>>>   }
>>> +
>>> +/**
>>> + * efi_dp_from_lo() - Get the instance of a Vendor Device Path
>>> + *		      in a multi-instance device path that matches
>>> + *		      a specific GUID
>>
>> The description does make it clear that you are looking for a VenMedia()
>> node.
>>
>> Please, describe what the function is good for (find the device path for
>> an initrd or dtb in a load option).
>>
>
> Ok
>
>>> + *
>>> + * @load_option: device paths to search
>>> + * @size:	 size of the discovered device path
>>> + * @guid:	 guid to search for
>>> + *
>>> + * Return:	device path or NULL. Caller must free the returned value
>>
>> Please, keep the text aligned.
>>
>> Do we need a copy? Isn't a pointer good enough?
>
> A pointer to what?
> I think it's better to a copy here. This is a device path that might be used
> out of a stack context were the load option might disappear.
> Look at how the function is used in efi_get_dp_from_boot().

You are duplicating in get_initrd_fp(). Why should we duplicate twice?

>
>>
>>> + */
>>> +struct
>>> +efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
>>> +				efi_uintn_t *size, efi_guid_t guid)
>>> +{
>>> +	struct efi_device_path *fp = lo->file_path;
>>> +	struct efi_device_path_vendor *vendor;
>>> +	int lo_len = lo->file_path_length;
>>> +
>>> +	while (lo_len) {
>>
>> lo_len must be at least sizeof(struct efi_device_path).
>>
>>> +		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
>>> +		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) {
>>> +			lo_len -= fp->length;
>>
>> Could the last device path in the array be followed by zero bytes for
>> padding?
>
> How? Device paths are packed aren't they ?
>
>> Should we check that fp->length >= sizeof(struct efi_device_path)?
>
> Yea probably a good idea

The content of the boot option comes from the user. Just assume that it
can contain malicious content.

We should also check that the identified device-path starting at
VenMedia() ends within fp->length using efi_dp_check_length().

>
>>
>>> +			fp = (void *)fp + fp->length;
>>
>> Please, avoid code duplication.
>>
>> E.g.
>>
>> for (; lo_len >= sizeof(struct efi_device_path);
>>      lo_len -= fp->length, fp = (void *)fp + fp->length) {
>
> I can an switch to that, but I really never liked this format.
> It always seemed way less readable to me for some reason.  Maybe because I
> never got used to it ...

Using "for" is only one option. You could use "goto next;" instead.

>
>>
>>> +			continue;
>>> +		}
>>> +
>>> +		vendor = (struct efi_device_path_vendor *)fp;
>>> +		if (!guidcmp(&vendor->guid, &guid))
>>> +			return efi_dp_dup(fp);
>>
>> Should we strip of the VenMedia() node here?
>
> Why? This is not supposed to get the file path. The function says "get device
> path from load option" and that device includes the VenMedia node.
> It would make more sense for me to strip in efi_get_dp_from_boot() for
> example, if you want a helper function to get the initrd path *only*.

The VenMedia() node is not needed anymore once you have found the entry.

>
> But really it's just one invocation of efi_dp_get_next_instance() after
> whatever device path you get. Which also modifies the device path pointer, so
> I'd really prefer keeping that call in a local context.

Why next instance? I thought next node.

My understanding is that we have:

kernel path,end(0xff),
VenMedia(), /* no end node here */
initrd1, end(0x01),
initrd2, end(0xff)

Please, document the structure.

Best regards

Heinrich

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11 11:00       ` Heinrich Schuchardt
@ 2021-03-11 11:36         ` Ilias Apalodimas
  2021-03-11 11:44           ` Heinrich Schuchardt
  2021-03-12  2:50           ` AKASHI Takahiro
  0 siblings, 2 replies; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-11 11:36 UTC (permalink / raw)
  To: u-boot

Hi Heinrich 

[...]

> >>> + * @load_option: device paths to search
> >>> + * @size:	 size of the discovered device path
> >>> + * @guid:	 guid to search for
> >>> + *
> >>> + * Return:	device path or NULL. Caller must free the returned value
> >>
> >> Please, keep the text aligned.
> >>
> >> Do we need a copy? Isn't a pointer good enough?
> >
> > A pointer to what?
> > I think it's better to a copy here. This is a device path that might be used
> > out of a stack context were the load option might disappear.
> > Look at how the function is used in efi_get_dp_from_boot().
> 
> You are duplicating in get_initrd_fp(). Why should we duplicate twice?
> 

That's irrelevant though isn't it?
I did that in the efi initrd implementation. If someone else does the DTB in
the future and device to use efi_dp_from_lo return directly?
I'd much prefer an API (since that function goes into an API-related file for
device paths), that's safe and requires the user to free the memory, rather
than allowing him to accidentally shoot himself in the foot, keeping in mind
it's a single copy on a device path, which roughly adds anything on our boot
time.

> >
> >>
> >>> + */
> >>> +struct
> >>> +efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
> >>> +				efi_uintn_t *size, efi_guid_t guid)
> >>> +{
> >>> +	struct efi_device_path *fp = lo->file_path;
> >>> +	struct efi_device_path_vendor *vendor;
> >>> +	int lo_len = lo->file_path_length;
> >>> +
> >>> +	while (lo_len) {
> >>
> >> lo_len must be at least sizeof(struct efi_device_path).
> >>
> >>> +		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
> >>> +		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) {
> >>> +			lo_len -= fp->length;
> >>
> >> Could the last device path in the array be followed by zero bytes for
> >> padding?
> >
> > How? Device paths are packed aren't they ?
> >
> >> Should we check that fp->length >= sizeof(struct efi_device_path)?
> >
> > Yea probably a good idea
> 
> The content of the boot option comes from the user. Just assume that it
> can contain malicious content.
> 

Yea the user doesn't add the device path directly though. The user adds
directories and a file, so the normalization is part of this function, 
applied randomly and locally on a single input? or the device path creation 
functions which this code uses? Since we use the pattern in a bunch of places
I assumed we did take care of that during the functions that create the device
paths. I haven't checked though ...

> We should also check that the identified device-path starting at
> VenMedia() ends within fp->length using efi_dp_check_length().

ok

> 
> >
> >>
> >>> +			fp = (void *)fp + fp->length;
> >>
> >> Please, avoid code duplication.
> >>
> >> E.g.
> >>
> >> for (; lo_len >= sizeof(struct efi_device_path);
> >>      lo_len -= fp->length, fp = (void *)fp + fp->length) {
> >
> > I can an switch to that, but I really never liked this format.
> > It always seemed way less readable to me for some reason.  Maybe because I
> > never got used to it ...
> 
> Using "for" is only one option. You could use "goto next;" instead.
> 

I really don't mind, I can just use what you propose.

> >
> >>
> >>> +			continue;
> >>> +		}
> >>> +
> >>> +		vendor = (struct efi_device_path_vendor *)fp;
> >>> +		if (!guidcmp(&vendor->guid, &guid))
> >>> +			return efi_dp_dup(fp);
> >>
> >> Should we strip of the VenMedia() node here?
> >
> > Why? This is not supposed to get the file path. The function says "get device
> > path from load option" and that device includes the VenMedia node.
> > It would make more sense for me to strip in efi_get_dp_from_boot() for
> > example, if you want a helper function to get the initrd path *only*.
> 
> The VenMedia() node is not needed anymore once you have found the entry.
> 

Yea it's not but as I said the name of the function says "get the *stored*
from a boot option. Not get the one that suits us.  
There's another reason for that btw, the initrd related functions use that 
(specifically get_initrd_fp()), to figure out if the Boot#### variable
contains an initrd path or not.
If the VenMedia is not present at all, the protocol is not installed allowing
the kernel to fallback in it's command line 'initrd=' option.
If the VenMedia is there though, we are checking the file path of the initrd
and if the file's not found we return an error allowing Bootmgr to fallback.

If we 'just' return the initrd path, we'll have to introduce another variable
in the function, indicating if the VenMedia is present or not so the rest
ofthe codepath can decide what to do.

> >
> > But really it's just one invocation of efi_dp_get_next_instance() after
> > whatever device path you get. Which also modifies the device path pointer, so
> > I'd really prefer keeping that call in a local context.
> 
> Why next instance? I thought next node.
> 
> My understanding is that we have:
> 
> kernel path,end(0xff),
> VenMedia(), /* no end node here */
> initrd1, end(0x01),
> initrd2, end(0xff)

No, the structure is added in cmd/efidebug.c code.
It's created with efi_dp_append_instance() on 
 - const struct efi_initrd_dp id_dp
 - file path of initrd
 
 which will create:
 kernel path,end(0xff),
 VenMedia(), end(0x01),
 initrd1, end(0x01),
 initrd2, end(0xff)

I know I originally proposed the one you have, but it seemed cleaner adding
an extra instance between VenMedia and the first initrd.

> 
> Please, document the structure.
> 

Sure

> Best regards
> 
> Heinrich

Thanks
/Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11 11:36         ` Ilias Apalodimas
@ 2021-03-11 11:44           ` Heinrich Schuchardt
  2021-03-11 12:31             ` Heinrich Schuchardt
  2021-03-11 13:31             ` Ilias Apalodimas
  2021-03-12  2:50           ` AKASHI Takahiro
  1 sibling, 2 replies; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11 11:44 UTC (permalink / raw)
  To: u-boot

Am 11. M?rz 2021 12:36:04 MEZ schrieb Ilias Apalodimas <ilias.apalodimas@linaro.org>:
>Hi Heinrich 
>
>[...]
>
>> >>> + * @load_option: device paths to search
>> >>> + * @size:	 size of the discovered device path
>> >>> + * @guid:	 guid to search for
>> >>> + *
>> >>> + * Return:	device path or NULL. Caller must free the returned
>value
>> >>
>> >> Please, keep the text aligned.
>> >>
>> >> Do we need a copy? Isn't a pointer good enough?
>> >
>> > A pointer to what?
>> > I think it's better to a copy here. This is a device path that
>might be used
>> > out of a stack context were the load option might disappear.
>> > Look at how the function is used in efi_get_dp_from_boot().
>> 
>> You are duplicating in get_initrd_fp(). Why should we duplicate
>twice?
>> 
>
>That's irrelevant though isn't it?
>I did that in the efi initrd implementation. If someone else does the
>DTB in
>the future and device to use efi_dp_from_lo return directly?
>I'd much prefer an API (since that function goes into an API-related
>file for
>device paths), that's safe and requires the user to free the memory,
>rather
>than allowing him to accidentally shoot himself in the foot, keeping in
>mind
>it's a single copy on a device path, which roughly adds anything on our
>boot
>time.
>
>> >
>> >>
>> >>> + */
>> >>> +struct
>> >>> +efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
>> >>> +				efi_uintn_t *size, efi_guid_t guid)
>> >>> +{
>> >>> +	struct efi_device_path *fp = lo->file_path;
>> >>> +	struct efi_device_path_vendor *vendor;
>> >>> +	int lo_len = lo->file_path_length;
>> >>> +
>> >>> +	while (lo_len) {
>> >>
>> >> lo_len must be at least sizeof(struct efi_device_path).
>> >>
>> >>> +		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
>> >>> +		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) {
>> >>> +			lo_len -= fp->length;
>> >>
>> >> Could the last device path in the array be followed by zero bytes
>for
>> >> padding?
>> >
>> > How? Device paths are packed aren't they ?
>> >
>> >> Should we check that fp->length >= sizeof(struct efi_device_path)?
>> >
>> > Yea probably a good idea
>> 
>> The content of the boot option comes from the user. Just assume that
>it
>> can contain malicious content.
>> 
>
>Yea the user doesn't add the device path directly though. The user adds
>directories and a file, so the normalization is part of this function, 
>applied randomly and locally on a single input? or the device path
>creation 
>functions which this code uses? Since we use the pattern in a bunch of
>places
>I assumed we did take care of that during the functions that create the
>device
>paths. I haven't checked though ...

I am not referring to efidebug.

The user can update EFI variables with any binary content using an EFI binary or OS functions.

E.g. copy a binary file to the efi variables file system in Linux.

>
>> We should also check that the identified device-path starting at
>> VenMedia() ends within fp->length using efi_dp_check_length().
>
>ok
>
>> 
>> >
>> >>
>> >>> +			fp = (void *)fp + fp->length;
>> >>
>> >> Please, avoid code duplication.
>> >>
>> >> E.g.
>> >>
>> >> for (; lo_len >= sizeof(struct efi_device_path);
>> >>      lo_len -= fp->length, fp = (void *)fp + fp->length) {
>> >
>> > I can an switch to that, but I really never liked this format.
>> > It always seemed way less readable to me for some reason.  Maybe
>because I
>> > never got used to it ...
>> 
>> Using "for" is only one option. You could use "goto next;" instead.
>> 
>
>I really don't mind, I can just use what you propose.

As long as you avoid code duplication I am fine.

Best regards

Heinrich

>
>> >
>> >>
>> >>> +			continue;
>> >>> +		}
>> >>> +
>> >>> +		vendor = (struct efi_device_path_vendor *)fp;
>> >>> +		if (!guidcmp(&vendor->guid, &guid))
>> >>> +			return efi_dp_dup(fp);
>> >>
>> >> Should we strip of the VenMedia() node here?
>> >
>> > Why? This is not supposed to get the file path. The function says
>"get device
>> > path from load option" and that device includes the VenMedia node.
>> > It would make more sense for me to strip in efi_get_dp_from_boot()
>for
>> > example, if you want a helper function to get the initrd path
>*only*.
>> 
>> The VenMedia() node is not needed anymore once you have found the
>entry.
>> 
>
>Yea it's not but as I said the name of the function says "get the
>*stored*
>from a boot option. Not get the one that suits us.  
>There's another reason for that btw, the initrd related functions use
>that 
>(specifically get_initrd_fp()), to figure out if the Boot#### variable
>contains an initrd path or not.
>If the VenMedia is not present at all, the protocol is not installed
>allowing
>the kernel to fallback in it's command line 'initrd=' option.
>If the VenMedia is there though, we are checking the file path of the
>initrd
>and if the file's not found we return an error allowing Bootmgr to
>fallback.
>
>If we 'just' return the initrd path, we'll have to introduce another
>variable
>in the function, indicating if the VenMedia is present or not so the
>rest
>ofthe codepath can decide what to do.
>
>> >
>> > But really it's just one invocation of efi_dp_get_next_instance()
>after
>> > whatever device path you get. Which also modifies the device path
>pointer, so
>> > I'd really prefer keeping that call in a local context.
>> 
>> Why next instance? I thought next node.
>> 
>> My understanding is that we have:
>> 
>> kernel path,end(0xff),
>> VenMedia(), /* no end node here */
>> initrd1, end(0x01),
>> initrd2, end(0xff)
>
>No, the structure is added in cmd/efidebug.c code.
>It's created with efi_dp_append_instance() on 
> - const struct efi_initrd_dp id_dp
> - file path of initrd
> 
> which will create:
> kernel path,end(0xff),
> VenMedia(), end(0x01),
> initrd1, end(0x01),
> initrd2, end(0xff)
>
>I know I originally proposed the one you have, but it seemed cleaner
>adding
>an extra instance between VenMedia and the first initrd.
>
>> 
>> Please, document the structure.
>> 
>
>Sure
>
>> Best regards
>> 
>> Heinrich
>
>Thanks
>/Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 4/6] efi_loader: Replace config option for initrd loading
  2021-03-05 22:23 ` [PATCH 4/6] efi_loader: Replace config option for initrd loading Ilias Apalodimas
@ 2021-03-11 12:23   ` Heinrich Schuchardt
  2021-03-11 12:30     ` Ilias Apalodimas
  0 siblings, 1 reply; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11 12:23 UTC (permalink / raw)
  To: u-boot

On 05.03.21 23:23, Ilias Apalodimas wrote:
> Up to now we install EFI_LOAD_FILE2_PROTOCOL to load an initrd
> unconditionally. Although we correctly return various EFI exit codes
> depending on the file status (i.e EFI_NO_MEDIA, EFI_NOT_FOUND etc), the
> kernel loader, only falls back to the cmdline interpreted initrd if the
> protocol is not installed.
>
> This creates a problem for EFI installers, since they won't be able to
> load their own initrd and continue the installation. It also makes the
> feature hard to use, since we can either have a single initrd or we have
> to recompile u-boot if the filename changes.
>
> So let's introduce a different logic that will decouple the initrd
> path from the config option we currently have.
> When defining a UEFI BootXXXX we can use the filepathlist and store
> a file path pointing to our initrd. Specifically the EFI spec describes:
>
> "The first element of the array is a device path that describes the device
> and location of the Image for this load option. Other device paths may
> optionally exist in the FilePathList, but their usage is OSV specific"
>
> When the EFI application is launched through the bootmgr, we'll try to
> interpret the extra device path. If that points to a file that exists on
> our disk, we'll now install the load_file2 and the efi-stub will be able
> to use it.
>
> This opens up another path using U-Boot and defines a new boot flow.
> A user will be able to control the kernel/initrd pairs without explicit
> cmdline args or GRUB.
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> ---
>  cmd/bootefi.c                    |   3 +
>  include/efi_loader.h             |   1 +
>  lib/efi_loader/Kconfig           |  15 +--
>  lib/efi_loader/efi_bootmgr.c     |   3 +
>  lib/efi_loader/efi_load_initrd.c | 209 +++++++++++++++++++++----------
>  5 files changed, 152 insertions(+), 79 deletions(-)
>
> diff --git a/cmd/bootefi.c b/cmd/bootefi.c
> index 271b385edea6..cba81ffe75e4 100644
> --- a/cmd/bootefi.c
> +++ b/cmd/bootefi.c
> @@ -358,6 +358,9 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options)
>
>  	free(load_options);
>
> +	if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD))
> +		efi_initrd_deregister();
> +
>  	return ret;
>  }
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index eb11a8c7d4b1..0d4f5d9acc9f 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -431,6 +431,7 @@ efi_status_t efi_net_register(void);
>  /* Called by bootefi to make the watchdog available */
>  efi_status_t efi_watchdog_register(void);
>  efi_status_t efi_initrd_register(void);
> +void efi_initrd_deregister(void);
>  /* Called by bootefi to make SMBIOS tables available */
>  /**
>   * efi_acpi_register() - write out ACPI tables
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index e729f727df11..029f0e515f57 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -317,16 +317,11 @@ config EFI_LOAD_FILE2_INITRD
>  	bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk"
>  	default n
>  	help
> -	  Expose a EFI_FILE_LOAD2_PROTOCOL that the Linux UEFI stub can
> -	  use to load the initial ramdisk. Once this is enabled using
> -	  initrd=<ramdisk> will stop working.
> -
> -config EFI_INITRD_FILESPEC
> -	string "initramfs path"
> -	default "host 0:1 initrd"
> -	depends on EFI_LOAD_FILE2_INITRD
> -	help
> -	  Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz.
> +		Linux v5.7 and later can make use of this option. If the boot option
> +		selected by the UEFI boot manager specifies an existing file to be used
> +		as initial RAM disk, a Linux specific Load File2 protocol will be
> +		installed and Linux 5.7+ will ignore any initrd=<ramdisk> command line
> +		argument.
>
>  config EFI_SECURE_BOOT
>  	bool "Enable EFI secure boot support"
> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> index 25f5cebfdb67..d1baa8c71a4d 100644
> --- a/lib/efi_loader/efi_bootmgr.c
> +++ b/lib/efi_loader/efi_bootmgr.c
> @@ -118,6 +118,9 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
>  		ret = efi_set_variable_int(L"BootCurrent",
>  					   &efi_global_variable_guid,
>  					   attributes, sizeof(n), &n, false);

Why would you continue if BootCurrent is not set?

> +		/* try to register load file2 for initrd's */
> +		if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD))
> +			ret |= efi_initrd_register();

ret is not a boolean. So |= does not make sense to me here.

>  		if (ret != EFI_SUCCESS) {
>  			if (EFI_CALL(efi_unload_image(*handle))
>  			    != EFI_SUCCESS)
> diff --git a/lib/efi_loader/efi_load_initrd.c b/lib/efi_loader/efi_load_initrd.c
> index b9ee8839054f..b11c5ee293fe 100644
> --- a/lib/efi_loader/efi_load_initrd.c
> +++ b/lib/efi_loader/efi_load_initrd.c
> @@ -5,7 +5,9 @@
>
>  #include <common.h>
>  #include <efi_loader.h>
> +#include <efi_helper.h>
>  #include <efi_load_initrd.h>
> +#include <efi_variable.h>
>  #include <fs.h>
>  #include <malloc.h>
>  #include <mapmem.h>
> @@ -39,41 +41,71 @@ static const struct efi_initrd_dp dp = {
>  	}
>  };
>
> +static efi_handle_t efi_initrd_handle;
> +
>  /**
> - * get_file_size() - retrieve the size of initramfs, set efi status on error
> + * get_initrd_fp() - Get initrd device path from a FilePathList device path
>   *
> - * @dev:			device to read from, e.g. "mmc"
> - * @part:			device partition, e.g. "0:1"
> - * @file:			name of file
> - * @status:			EFI exit code in case of failure
> + * @initrd_fp:			the final initrd filepath
>   *
> - * Return:			size of file
> + * Return:			status code
>   */
> -static loff_t get_file_size(const char *dev, const char *part, const char *file,
> -			    efi_status_t *status)
> +static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
>  {
> -	loff_t sz = 0;
> -	int ret;
> +	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
> +	struct efi_device_path *cur = NULL;
> +	struct efi_device_path *dp = NULL;
> +	struct efi_device_path *tmp_dp;
> +	efi_uintn_t ret;
> +	efi_uintn_t size;
>
> -	ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
> -	if (ret) {
> -		*status = EFI_NO_MEDIA;
gg/> +	/*
> +	 * if bootmgr is setup with and initrd, the device path will be
> +	 * in the FilePathList[] of our load options in Boot####.
> +	 * The first device path of the multi instance device path will
> +	 * start with a VenMedia and the initrds will follow.
> +	 *
> +	 * If the device path is not found return EFI_INVALID_PARAMETER.
> +	 * We can then use this specific return value and not install the
> +	 * protocol, while allowing the boot to continue
> +	 */
> +	dp = efi_get_dp_from_boot(lf2_initrd_guid);
> +	if (!dp) {
> +		ret = EFI_INVALID_PARAMETER;
>  		goto out;
>  	}
>
> -	ret = fs_size(file, &sz);
> -	if (ret) {
> -		sz = 0;
> -		*status = EFI_NOT_FOUND;
> +	tmp_dp = dp;
> +	cur = efi_dp_get_next_instance(&tmp_dp, &size);
> +	if (!cur) {
> +		ret = EFI_OUT_OF_RESOURCES;
>  		goto out;
>  	}
>
> +	/*
> +	 * We don't care if the file path is eventually NULL here. The
> +	 * callers will try to load a file from it and eventually fail
> +	 * but let's be explicit with our return values
> +	 */
> +	if (!tmp_dp) {
> +		*initrd_fp = NULL;
> +		ret = EFI_NOT_FOUND;
> +	} else {
> +		*initrd_fp = efi_dp_dup(tmp_dp);
> +		if (*initrd_fp)
> +			ret = EFI_SUCCESS;
> +		else
> +			ret = EFI_OUT_OF_RESOURCES;
> +	}
> +
>  out:
> -	return sz;
> +	efi_free_pool(cur);
> +	efi_free_pool(dp);
> +	return ret;
>  }
>
>  /**
> - * efi_load_file2initrd() - load initial RAM disk
> + * efi_load_file2_initrd() - load initial RAM disk
>   *
>   * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
>   * in order to load an initial RAM disk requested by the Linux kernel stub.
> @@ -93,98 +125,131 @@ efi_load_file2_initrd(struct efi_load_file_protocol *this,
>  		      struct efi_device_path *file_path, bool boot_policy,
>  		      efi_uintn_t *buffer_size, void *buffer)
>  {
> -	char *filespec;
> -	efi_status_t status = EFI_NOT_FOUND;
> -	loff_t file_sz = 0, read_sz = 0;
> -	char *dev, *part, *file;
> -	char *pos;
> -	int ret;
> +	struct efi_device_path *initrd_fp = NULL;
> +	struct efi_file_info *info = NULL;
> +	efi_status_t ret = EFI_NOT_FOUND;
> +	struct efi_file_handle *f;
> +	efi_uintn_t bs;
>
>  	EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
>  		  buffer_size, buffer);
>
> -	filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
> -	if (!filespec)
> -		goto out;
> -	pos = filespec;
> -
>  	if (!this || this != &efi_lf2_protocol ||
>  	    !buffer_size) {
> -		status = EFI_INVALID_PARAMETER;
> +		ret = EFI_INVALID_PARAMETER;
>  		goto out;
>  	}
>
>  	if (file_path->type != dp.end.type ||
>  	    file_path->sub_type != dp.end.sub_type) {
> -		status = EFI_INVALID_PARAMETER;
> +		ret = EFI_INVALID_PARAMETER;
>  		goto out;
>  	}
>
>  	if (boot_policy) {
> -		status = EFI_UNSUPPORTED;
> +		ret = EFI_UNSUPPORTED;
>  		goto out;
>  	}
>
> -	/*
> -	 * expect a string with three space separated parts:
> -	 *
> -	 * * a block device type, e.g. "mmc"
> -	 * * a device and partition identifier, e.g. "0:1"
> -	 * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
> -	 */
> -	dev = strsep(&pos, " ");
> -	if (!dev)
> +	ret = get_initrd_fp(&initrd_fp);
> +	if (ret != EFI_SUCCESS)
>  		goto out;
> -	part = strsep(&pos, " ");
> -	if (!part)
> +
> +	/* Open file */
> +	f = efi_file_from_path(initrd_fp);
> +	if (!f) {
> +		ret = EFI_NOT_FOUND;
>  		goto out;
> -	file = strsep(&pos, " ");
> -	if (!file)
> +	}
> +
> +	/* Get file size */
> +	bs = 0;
> +	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
> +				  &bs, info));

Please, use

	ret = EFI_CALL(f-> ...);

througout the code.

In efi_load_image_from_file() and efi_capsule_read_file() we also
retrieve the file size. We should factor out a function efi_get_file_size().

> +	if (ret != EFI_BUFFER_TOO_SMALL) {
> +		ret = EFI_DEVICE_ERROR;
>  		goto out;
> +	}
>
> -	file_sz = get_file_size(dev, part, file, &status);
> -	if (!file_sz)
> +	info = malloc(bs);
> +	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs,
> +				  info));
> +	if (ret != EFI_SUCCESS)
>  		goto out;
>
> -	if (!buffer || *buffer_size < file_sz) {
> -		status = EFI_BUFFER_TOO_SMALL;
> -		*buffer_size = file_sz;
> +	bs = info->file_size;
> +	if (!buffer || *buffer_size < bs) {
> +		ret = EFI_BUFFER_TOO_SMALL;
> +		*buffer_size = bs;
>  	} else {
> -		ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
> -		if (ret) {
> -			status = EFI_NO_MEDIA;
> -			goto out;
> -		}
> -
> -		ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
> -			      &read_sz);
> -		if (ret || read_sz != file_sz)
> -			goto out;
> -		*buffer_size = read_sz;
> -
> -		status = EFI_SUCCESS;
> +		EFI_CALL(ret = f->read(f, &bs, (void *)(uintptr_t)buffer));
> +		*buffer_size = bs;
>  	}
>
>  out:
> -	free(filespec);
> -	return EFI_EXIT(status);
> +	free(info);
> +	efi_free_pool(initrd_fp);
> +	EFI_CALL(f->close(f));
> +	return EFI_EXIT(ret);
> +}
> +
> +/**
> + * check_initrd() - Determine if the file defined as an initrd in Boot####
> + *		    load_options device path is present
> + *
> + * Return:	status code
> + */
> +static efi_status_t check_initrd(void)
> +{
> +	struct efi_device_path *initrd_fp = NULL;
> +	struct efi_file_handle *f;
> +	efi_status_t ret;
> +
> +	ret = get_initrd_fp(&initrd_fp);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	/*
> +	 * If the file is not found, but the file path is set, return an error
> +	 * and trigger the bootmgr fallback
> +	 */
> +	f = efi_file_from_path(initrd_fp);
> +	if (!f) {

This deserves a log_warning().

> +		ret = EFI_NOT_FOUND;
> +		goto out;
> +	}
> +
> +	EFI_CALL(f->close(f));
> +
> +out:
> +	efi_free_pool(initrd_fp);
> +	return ret;
>  }
>
>  /**
>   * efi_initrd_register() - create handle for loading initial RAM disk
>   *
>   * This function creates a new handle and installs a Linux specific vendor
> - * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
> + * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
>   * to identify the handle and then calls the LoadFile service of the
> - * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
> + * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
>   *
>   * Return:	status code
>   */
>  efi_status_t efi_initrd_register(void)
>  {
> -	efi_handle_t efi_initrd_handle = NULL;
>  	efi_status_t ret;
>
> +	/*
> +	 * Allow the user to continue if Boot#### file path is not set for
> +	 * an initrd
> +	 */
> +	ret = check_initrd();
> +	if (ret == EFI_INVALID_PARAMETER)
> +		return EFI_SUCCESS;
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
>  	ret = EFI_CALL(efi_install_multiple_protocol_interfaces
>  		       (&efi_initrd_handle,
>  			/* initramfs */
> @@ -196,3 +261,9 @@ efi_status_t efi_initrd_register(void)
>
>  	return ret;
>  }
> +
> +void efi_initrd_deregister(void)

Missing description of an exported function.

Best regards

Heinrich

> +{
> +	efi_delete_handle(efi_initrd_handle);
> +	efi_initrd_handle = NULL;
> +}
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 4/6] efi_loader: Replace config option for initrd loading
  2021-03-11 12:23   ` Heinrich Schuchardt
@ 2021-03-11 12:30     ` Ilias Apalodimas
  2021-03-11 12:50       ` Heinrich Schuchardt
  0 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-11 12:30 UTC (permalink / raw)
  To: u-boot

On Thu, Mar 11, 2021 at 01:23:05PM +0100, Heinrich Schuchardt wrote:
> On 05.03.21 23:23, Ilias Apalodimas wrote:
> > Up to now we install EFI_LOAD_FILE2_PROTOCOL to load an initrd
> > unconditionally. Although we correctly return various EFI exit codes
> > depending on the file status (i.e EFI_NO_MEDIA, EFI_NOT_FOUND etc), the
> > kernel loader, only falls back to the cmdline interpreted initrd if the
> > protocol is not installed.
> >
> > This creates a problem for EFI installers, since they won't be able to
> > load their own initrd and continue the installation. It also makes the
> > feature hard to use, since we can either have a single initrd or we have
> > to recompile u-boot if the filename changes.
> >
> > So let's introduce a different logic that will decouple the initrd
> > path from the config option we currently have.
> > When defining a UEFI BootXXXX we can use the filepathlist and store
> > a file path pointing to our initrd. Specifically the EFI spec describes:
> >
> > "The first element of the array is a device path that describes the device
> > and location of the Image for this load option. Other device paths may
> > optionally exist in the FilePathList, but their usage is OSV specific"
> >
> > When the EFI application is launched through the bootmgr, we'll try to
> > interpret the extra device path. If that points to a file that exists on
> > our disk, we'll now install the load_file2 and the efi-stub will be able
> > to use it.
> >
> > This opens up another path using U-Boot and defines a new boot flow.
> > A user will be able to control the kernel/initrd pairs without explicit
> > cmdline args or GRUB.
> >
> > Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > ---
> >  cmd/bootefi.c                    |   3 +
> >  include/efi_loader.h             |   1 +
> >  lib/efi_loader/Kconfig           |  15 +--
> >  lib/efi_loader/efi_bootmgr.c     |   3 +
> >  lib/efi_loader/efi_load_initrd.c | 209 +++++++++++++++++++++----------
> >  5 files changed, 152 insertions(+), 79 deletions(-)
> >
> > diff --git a/cmd/bootefi.c b/cmd/bootefi.c
> > index 271b385edea6..cba81ffe75e4 100644
> > --- a/cmd/bootefi.c
> > +++ b/cmd/bootefi.c
> > @@ -358,6 +358,9 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options)
> >
> >  	free(load_options);
> >
> > +	if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD))
> > +		efi_initrd_deregister();
> > +
> >  	return ret;
> >  }
> >
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index eb11a8c7d4b1..0d4f5d9acc9f 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -431,6 +431,7 @@ efi_status_t efi_net_register(void);
> >  /* Called by bootefi to make the watchdog available */
> >  efi_status_t efi_watchdog_register(void);
> >  efi_status_t efi_initrd_register(void);
> > +void efi_initrd_deregister(void);
> >  /* Called by bootefi to make SMBIOS tables available */
> >  /**
> >   * efi_acpi_register() - write out ACPI tables
> > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > index e729f727df11..029f0e515f57 100644
> > --- a/lib/efi_loader/Kconfig
> > +++ b/lib/efi_loader/Kconfig
> > @@ -317,16 +317,11 @@ config EFI_LOAD_FILE2_INITRD
> >  	bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk"
> >  	default n
> >  	help
> > -	  Expose a EFI_FILE_LOAD2_PROTOCOL that the Linux UEFI stub can
> > -	  use to load the initial ramdisk. Once this is enabled using
> > -	  initrd=<ramdisk> will stop working.
> > -
> > -config EFI_INITRD_FILESPEC
> > -	string "initramfs path"
> > -	default "host 0:1 initrd"
> > -	depends on EFI_LOAD_FILE2_INITRD
> > -	help
> > -	  Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz.
> > +		Linux v5.7 and later can make use of this option. If the boot option
> > +		selected by the UEFI boot manager specifies an existing file to be used
> > +		as initial RAM disk, a Linux specific Load File2 protocol will be
> > +		installed and Linux 5.7+ will ignore any initrd=<ramdisk> command line
> > +		argument.
> >
> >  config EFI_SECURE_BOOT
> >  	bool "Enable EFI secure boot support"
> > diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> > index 25f5cebfdb67..d1baa8c71a4d 100644
> > --- a/lib/efi_loader/efi_bootmgr.c
> > +++ b/lib/efi_loader/efi_bootmgr.c
> > @@ -118,6 +118,9 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
> >  		ret = efi_set_variable_int(L"BootCurrent",
> >  					   &efi_global_variable_guid,
> >  					   attributes, sizeof(n), &n, false);
> 
> Why would you continue if BootCurrent is not set?

Because the bitops below is just trying to simplify the code 
reading, since both must call efi_unload_image(). Calling
efi_initrd_register() without a BootCurrent is guaranteed to fail as well.

> 
> > +		/* try to register load file2 for initrd's */
> > +		if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD))
> > +			ret |= efi_initrd_register();
> 
> ret is not a boolean. So |= does not make sense to me here.

Uhm? It's an unsigned long though and you can hav bitops properly?

> 
> >  		if (ret != EFI_SUCCESS) {
> >  			if (EFI_CALL(efi_unload_image(*handle))
> >  			    != EFI_SUCCESS)
> > diff --git a/lib/efi_loader/efi_load_initrd.c b/lib/efi_loader/efi_load_initrd.c
> > index b9ee8839054f..b11c5ee293fe 100644
> > --- a/lib/efi_loader/efi_load_initrd.c
> > +++ b/lib/efi_loader/efi_load_initrd.c
> > @@ -5,7 +5,9 @@
> >
> >  #include <common.h>
> >  #include <efi_loader.h>
> > +#include <efi_helper.h>
> >  #include <efi_load_initrd.h>
> > +#include <efi_variable.h>
> >  #include <fs.h>
> >  #include <malloc.h>
> >  #include <mapmem.h>
> > @@ -39,41 +41,71 @@ static const struct efi_initrd_dp dp = {
> >  	}
> >  };
> >
> > +static efi_handle_t efi_initrd_handle;
> > +
> >  /**
> > - * get_file_size() - retrieve the size of initramfs, set efi status on error
> > + * get_initrd_fp() - Get initrd device path from a FilePathList device path
> >   *
> > - * @dev:			device to read from, e.g. "mmc"
> > - * @part:			device partition, e.g. "0:1"
> > - * @file:			name of file
> > - * @status:			EFI exit code in case of failure
> > + * @initrd_fp:			the final initrd filepath
> >   *
> > - * Return:			size of file
> > + * Return:			status code
> >   */
> > -static loff_t get_file_size(const char *dev, const char *part, const char *file,
> > -			    efi_status_t *status)
> > +static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
> >  {
> > -	loff_t sz = 0;
> > -	int ret;
> > +	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
> > +	struct efi_device_path *cur = NULL;
> > +	struct efi_device_path *dp = NULL;
> > +	struct efi_device_path *tmp_dp;
> > +	efi_uintn_t ret;
> > +	efi_uintn_t size;
> >
> > -	ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
> > -	if (ret) {
> > -		*status = EFI_NO_MEDIA;
> gg/> +	/*
> > +	 * if bootmgr is setup with and initrd, the device path will be
> > +	 * in the FilePathList[] of our load options in Boot####.
> > +	 * The first device path of the multi instance device path will
> > +	 * start with a VenMedia and the initrds will follow.
> > +	 *
> > +	 * If the device path is not found return EFI_INVALID_PARAMETER.
> > +	 * We can then use this specific return value and not install the
> > +	 * protocol, while allowing the boot to continue
> > +	 */
> > +	dp = efi_get_dp_from_boot(lf2_initrd_guid);
> > +	if (!dp) {
> > +		ret = EFI_INVALID_PARAMETER;
> >  		goto out;
> >  	}
> >
> > -	ret = fs_size(file, &sz);
> > -	if (ret) {
> > -		sz = 0;
> > -		*status = EFI_NOT_FOUND;
> > +	tmp_dp = dp;
> > +	cur = efi_dp_get_next_instance(&tmp_dp, &size);
> > +	if (!cur) {
> > +		ret = EFI_OUT_OF_RESOURCES;
> >  		goto out;
> >  	}
> >
> > +	/*
> > +	 * We don't care if the file path is eventually NULL here. The
> > +	 * callers will try to load a file from it and eventually fail
> > +	 * but let's be explicit with our return values
> > +	 */
> > +	if (!tmp_dp) {
> > +		*initrd_fp = NULL;
> > +		ret = EFI_NOT_FOUND;
> > +	} else {
> > +		*initrd_fp = efi_dp_dup(tmp_dp);
> > +		if (*initrd_fp)
> > +			ret = EFI_SUCCESS;
> > +		else
> > +			ret = EFI_OUT_OF_RESOURCES;
> > +	}
> > +
> >  out:
> > -	return sz;
> > +	efi_free_pool(cur);
> > +	efi_free_pool(dp);
> > +	return ret;
> >  }
> >
> >  /**
> > - * efi_load_file2initrd() - load initial RAM disk
> > + * efi_load_file2_initrd() - load initial RAM disk
> >   *
> >   * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
> >   * in order to load an initial RAM disk requested by the Linux kernel stub.
> > @@ -93,98 +125,131 @@ efi_load_file2_initrd(struct efi_load_file_protocol *this,
> >  		      struct efi_device_path *file_path, bool boot_policy,
> >  		      efi_uintn_t *buffer_size, void *buffer)
> >  {
> > -	char *filespec;
> > -	efi_status_t status = EFI_NOT_FOUND;
> > -	loff_t file_sz = 0, read_sz = 0;
> > -	char *dev, *part, *file;
> > -	char *pos;
> > -	int ret;
> > +	struct efi_device_path *initrd_fp = NULL;
> > +	struct efi_file_info *info = NULL;
> > +	efi_status_t ret = EFI_NOT_FOUND;
> > +	struct efi_file_handle *f;
> > +	efi_uintn_t bs;
> >
> >  	EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
> >  		  buffer_size, buffer);
> >
> > -	filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
> > -	if (!filespec)
> > -		goto out;
> > -	pos = filespec;
> > -
> >  	if (!this || this != &efi_lf2_protocol ||
> >  	    !buffer_size) {
> > -		status = EFI_INVALID_PARAMETER;
> > +		ret = EFI_INVALID_PARAMETER;
> >  		goto out;
> >  	}
> >
> >  	if (file_path->type != dp.end.type ||
> >  	    file_path->sub_type != dp.end.sub_type) {
> > -		status = EFI_INVALID_PARAMETER;
> > +		ret = EFI_INVALID_PARAMETER;
> >  		goto out;
> >  	}
> >
> >  	if (boot_policy) {
> > -		status = EFI_UNSUPPORTED;
> > +		ret = EFI_UNSUPPORTED;
> >  		goto out;
> >  	}
> >
> > -	/*
> > -	 * expect a string with three space separated parts:
> > -	 *
> > -	 * * a block device type, e.g. "mmc"
> > -	 * * a device and partition identifier, e.g. "0:1"
> > -	 * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
> > -	 */
> > -	dev = strsep(&pos, " ");
> > -	if (!dev)
> > +	ret = get_initrd_fp(&initrd_fp);
> > +	if (ret != EFI_SUCCESS)
> >  		goto out;
> > -	part = strsep(&pos, " ");
> > -	if (!part)
> > +
> > +	/* Open file */
> > +	f = efi_file_from_path(initrd_fp);
> > +	if (!f) {
> > +		ret = EFI_NOT_FOUND;
> >  		goto out;
> > -	file = strsep(&pos, " ");
> > -	if (!file)
> > +	}
> > +
> > +	/* Get file size */
> > +	bs = 0;
> > +	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
> > +				  &bs, info));
> 
> Please, use
> 
> 	ret = EFI_CALL(f-> ...);
> 
> througout the code.
> 
> In efi_load_image_from_file() and efi_capsule_read_file() we also
> retrieve the file size. We should factor out a function efi_get_file_size().
> 
> > +	if (ret != EFI_BUFFER_TOO_SMALL) {
> > +		ret = EFI_DEVICE_ERROR;
> >  		goto out;
> > +	}
> >
> > -	file_sz = get_file_size(dev, part, file, &status);
> > -	if (!file_sz)
> > +	info = malloc(bs);
> > +	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs,
> > +				  info));
> > +	if (ret != EFI_SUCCESS)
> >  		goto out;
> >
> > -	if (!buffer || *buffer_size < file_sz) {
> > -		status = EFI_BUFFER_TOO_SMALL;
> > -		*buffer_size = file_sz;
> > +	bs = info->file_size;
> > +	if (!buffer || *buffer_size < bs) {
> > +		ret = EFI_BUFFER_TOO_SMALL;
> > +		*buffer_size = bs;
> >  	} else {
> > -		ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
> > -		if (ret) {
> > -			status = EFI_NO_MEDIA;
> > -			goto out;
> > -		}
> > -
> > -		ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
> > -			      &read_sz);
> > -		if (ret || read_sz != file_sz)
> > -			goto out;
> > -		*buffer_size = read_sz;
> > -
> > -		status = EFI_SUCCESS;
> > +		EFI_CALL(ret = f->read(f, &bs, (void *)(uintptr_t)buffer));
> > +		*buffer_size = bs;
> >  	}
> >
> >  out:
> > -	free(filespec);
> > -	return EFI_EXIT(status);
> > +	free(info);
> > +	efi_free_pool(initrd_fp);
> > +	EFI_CALL(f->close(f));
> > +	return EFI_EXIT(ret);
> > +}
> > +
> > +/**
> > + * check_initrd() - Determine if the file defined as an initrd in Boot####
> > + *		    load_options device path is present
> > + *
> > + * Return:	status code
> > + */
> > +static efi_status_t check_initrd(void)
> > +{
> > +	struct efi_device_path *initrd_fp = NULL;
> > +	struct efi_file_handle *f;
> > +	efi_status_t ret;
> > +
> > +	ret = get_initrd_fp(&initrd_fp);
> > +	if (ret != EFI_SUCCESS)
> > +		goto out;
> > +
> > +	/*
> > +	 * If the file is not found, but the file path is set, return an error
> > +	 * and trigger the bootmgr fallback
> > +	 */
> > +	f = efi_file_from_path(initrd_fp);
> > +	if (!f) {
> 
> This deserves a log_warning().
> 
> > +		ret = EFI_NOT_FOUND;
> > +		goto out;
> > +	}
> > +
> > +	EFI_CALL(f->close(f));
> > +
> > +out:
> > +	efi_free_pool(initrd_fp);
> > +	return ret;
> >  }
> >
> >  /**
> >   * efi_initrd_register() - create handle for loading initial RAM disk
> >   *
> >   * This function creates a new handle and installs a Linux specific vendor
> > - * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
> > + * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
> >   * to identify the handle and then calls the LoadFile service of the
> > - * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
> > + * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
> >   *
> >   * Return:	status code
> >   */
> >  efi_status_t efi_initrd_register(void)
> >  {
> > -	efi_handle_t efi_initrd_handle = NULL;
> >  	efi_status_t ret;
> >
> > +	/*
> > +	 * Allow the user to continue if Boot#### file path is not set for
> > +	 * an initrd
> > +	 */
> > +	ret = check_initrd();
> > +	if (ret == EFI_INVALID_PARAMETER)
> > +		return EFI_SUCCESS;
> > +	if (ret != EFI_SUCCESS)
> > +		return ret;
> > +
> >  	ret = EFI_CALL(efi_install_multiple_protocol_interfaces
> >  		       (&efi_initrd_handle,
> >  			/* initramfs */
> > @@ -196,3 +261,9 @@ efi_status_t efi_initrd_register(void)
> >
> >  	return ret;
> >  }
> > +
> > +void efi_initrd_deregister(void)
> 
> Missing description of an exported function.
> 
> Best regards
> 
> Heinrich
> 
> > +{
> > +	efi_delete_handle(efi_initrd_handle);
> > +	efi_initrd_handle = NULL;
> > +}
> >
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11 11:44           ` Heinrich Schuchardt
@ 2021-03-11 12:31             ` Heinrich Schuchardt
  2021-03-11 12:39               ` Ilias Apalodimas
  2021-03-11 13:31             ` Ilias Apalodimas
  1 sibling, 1 reply; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11 12:31 UTC (permalink / raw)
  To: u-boot

On 11.03.21 12:44, Heinrich Schuchardt wrote:
> Am 11. M?rz 2021 12:36:04 MEZ schrieb Ilias Apalodimas <ilias.apalodimas@linaro.org>:
>> Hi Heinrich
>>
>> [...]
>>
>>>>>> + * @load_option: device paths to search
>>>>>> + * @size:	 size of the discovered device path
>>>>>> + * @guid:	 guid to search for
>>>>>> + *
>>>>>> + * Return:	device path or NULL. Caller must free the returned
>> value
>>>>>
>>>>> Please, keep the text aligned.
>>>>>
>>>>> Do we need a copy? Isn't a pointer good enough?
>>>>
>>>> A pointer to what?
>>>> I think it's better to a copy here. This is a device path that
>> might be used
>>>> out of a stack context were the load option might disappear.
>>>> Look at how the function is used in efi_get_dp_from_boot().
>>>
>>> You are duplicating in get_initrd_fp(). Why should we duplicate
>> twice?
>>>
>>
>> That's irrelevant though isn't it?
>> I did that in the efi initrd implementation. If someone else does the
>> DTB in
>> the future and device to use efi_dp_from_lo return directly?
>> I'd much prefer an API (since that function goes into an API-related
>> file for
>> device paths), that's safe and requires the user to free the memory,
>> rather
>> than allowing him to accidentally shoot himself in the foot, keeping in
>> mind
>> it's a single copy on a device path, which roughly adds anything on our
>> boot
>> time.
>>
>>>>
>>>>>
>>>>>> + */
>>>>>> +struct
>>>>>> +efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
>>>>>> +				efi_uintn_t *size, efi_guid_t guid)
>>>>>> +{
>>>>>> +	struct efi_device_path *fp = lo->file_path;
>>>>>> +	struct efi_device_path_vendor *vendor;
>>>>>> +	int lo_len = lo->file_path_length;
>>>>>> +
>>>>>> +	while (lo_len) {
>>>>>
>>>>> lo_len must be at least sizeof(struct efi_device_path).
>>>>>
>>>>>> +		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
>>>>>> +		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) {
>>>>>> +			lo_len -= fp->length;
>>>>>
>>>>> Could the last device path in the array be followed by zero bytes
>> for
>>>>> padding?
>>>>
>>>> How? Device paths are packed aren't they ?
>>>>
>>>>> Should we check that fp->length >= sizeof(struct efi_device_path)?
>>>>
>>>> Yea probably a good idea
>>>
>>> The content of the boot option comes from the user. Just assume that
>> it
>>> can contain malicious content.
>>>
>>
>> Yea the user doesn't add the device path directly though. The user adds
>> directories and a file, so the normalization is part of this function,
>> applied randomly and locally on a single input? or the device path
>> creation
>> functions which this code uses? Since we use the pattern in a bunch of
>> places
>> I assumed we did take care of that during the functions that create the
>> device
>> paths. I haven't checked though ...
>
> I am not referring to efidebug.
>
> The user can update EFI variables with any binary content using an EFI binary or OS functions.
>
> E.g. copy a binary file to the efi variables file system in Linux.
>
>>
>>> We should also check that the identified device-path starting at
>>> VenMedia() ends within fp->length using efi_dp_check_length().
>>
>> ok
>>
>>>
>>>>
>>>>>
>>>>>> +			fp = (void *)fp + fp->length;
>>>>>
>>>>> Please, avoid code duplication.
>>>>>
>>>>> E.g.
>>>>>
>>>>> for (; lo_len >= sizeof(struct efi_device_path);
>>>>>      lo_len -= fp->length, fp = (void *)fp + fp->length) {
>>>>
>>>> I can an switch to that, but I really never liked this format.
>>>> It always seemed way less readable to me for some reason.  Maybe
>> because I
>>>> never got used to it ...
>>>
>>> Using "for" is only one option. You could use "goto next;" instead.
>>>
>>
>> I really don't mind, I can just use what you propose.
>
> As long as you avoid code duplication I am fine.
>
> Best regards
>
> Heinrich
>
>>
>>>>
>>>>>
>>>>>> +			continue;
>>>>>> +		}
>>>>>> +
>>>>>> +		vendor = (struct efi_device_path_vendor *)fp;
>>>>>> +		if (!guidcmp(&vendor->guid, &guid))
>>>>>> +			return efi_dp_dup(fp);
>>>>>
>>>>> Should we strip of the VenMedia() node here?
>>>>
>>>> Why? This is not supposed to get the file path. The function says
>> "get device
>>>> path from load option" and that device includes the VenMedia node.
>>>> It would make more sense for me to strip in efi_get_dp_from_boot()
>> for
>>>> example, if you want a helper function to get the initrd path
>> *only*.
>>>
>>> The VenMedia() node is not needed anymore once you have found the
>> entry.
>>>
>>
>> Yea it's not but as I said the name of the function says "get the
>> *stored*
>>from a boot option. Not get the one that suits us.
>> There's another reason for that btw, the initrd related functions use
>> that
>> (specifically get_initrd_fp()), to figure out if the Boot#### variable
>> contains an initrd path or not.
>> If the VenMedia is not present at all, the protocol is not installed
>> allowing
>> the kernel to fallback in it's command line 'initrd=' option.
>> If the VenMedia is there though, we are checking the file path of the
>> initrd
>> and if the file's not found we return an error allowing Bootmgr to
>> fallback.
>>
>> If we 'just' return the initrd path, we'll have to introduce another
>> variable
>> in the function, indicating if the VenMedia is present or not so the
>> rest
>> ofthe codepath can decide what to do.
>>
>>>>
>>>> But really it's just one invocation of efi_dp_get_next_instance()
>> after
>>>> whatever device path you get. Which also modifies the device path
>> pointer, so
>>>> I'd really prefer keeping that call in a local context.
>>>
>>> Why next instance? I thought next node.
>>>
>>> My understanding is that we have:
>>>
>>> kernel path,end(0xff),
>>> VenMedia(), /* no end node here */
>>> initrd1, end(0x01),
>>> initrd2, end(0xff)
>>
>> No, the structure is added in cmd/efidebug.c code.
>> It's created with efi_dp_append_instance() on
>> - const struct efi_initrd_dp id_dp
>> - file path of initrd
>>
>> which will create:
>> kernel path,end(0xff),
>> VenMedia(), end(0x01),

This end node is superfluous.

Best regards

Heinrich

>> initrd1, end(0x01),
>> initrd2, end(0xff)
>>
>> I know I originally proposed the one you have, but it seemed cleaner
>> adding
>> an extra instance between VenMedia and the first initrd.
>>
>>>
>>> Please, document the structure.
>>>
>>
>> Sure
>>
>>> Best regards
>>>
>>> Heinrich
>>
>> Thanks
>> /Ilias
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 5/6] efidebug: add multiple device path instances on Boot####
  2021-03-05 22:23 ` [PATCH 5/6] efidebug: add multiple device path instances on Boot#### Ilias Apalodimas
@ 2021-03-11 12:38   ` Heinrich Schuchardt
  2021-03-11 12:42     ` Ilias Apalodimas
  2021-03-12  4:44   ` AKASHI Takahiro
  1 sibling, 1 reply; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11 12:38 UTC (permalink / raw)
  To: u-boot

On 05.03.21 23:23, Ilias Apalodimas wrote:
> The UEFI spec allow a packed array of UEFI device paths in the
> FilePathList[] of an EFI_LOAD_OPTION. The first file path must
> describe the loaded image but the rest are OS specific.
>
> Previous patches parse the device path and try to use the second
> member of the array as an initrd. So let's modify efidebug slightly
> and install the second file described in the command line as the
> initrd device path.
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> ---
>  cmd/efidebug.c                                | 193 ++++++++++++++----
>  doc/board/emulation/qemu_capsule_update.rst   |   4 +-
>  doc/uefi/uefi.rst                             |   2 +-
>  .../test_efi_capsule/test_capsule_firmware.py |   6 +-
>  test/py/tests/test_efi_secboot/test_signed.py |  16 +-
>  .../test_efi_secboot/test_signed_intca.py     |   8 +-
>  .../tests/test_efi_secboot/test_unsigned.py   |   8 +-
>  7 files changed, 179 insertions(+), 58 deletions(-)
>
> diff --git a/cmd/efidebug.c b/cmd/efidebug.c
> index bbbcb0a54643..9a1c471a3eaa 100644
> --- a/cmd/efidebug.c
> +++ b/cmd/efidebug.c
> @@ -9,6 +9,8 @@
>  #include <common.h>
>  #include <command.h>
>  #include <efi_dt_fixup.h>
> +#include <efi_helper.h>
> +#include <efi_load_initrd.h>
>  #include <efi_loader.h>
>  #include <efi_rng.h>
>  #include <exports.h>
> @@ -19,6 +21,7 @@
>  #include <part.h>
>  #include <search.h>
>  #include <linux/ctype.h>
> +#include <linux/err.h>
>
>  #define BS systab.boottime
>  #define RT systab.runtime
> @@ -794,6 +797,65 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
>  	return CMD_RET_SUCCESS;
>  }
>
> +/**
> + * add_initrd_instance() - Append a device path to load_options pointing to an
> + *			   inirtd
> + *
> + * @argc:	Number of arguments
> + * @argv:	Argument array
> + * @file_path	Existing device path, the new instance will be appended
> + * Return:	Pointer to the device path or ERR_PTR
> + *
> + */
> +static
> +struct efi_device_path *add_initrd_instance(const char *dev, const char *part,
> +					    const char *file,
> +					    const struct efi_device_path *fp,
> +					    efi_uintn_t *fp_size)
> +{
> +	struct efi_device_path *tmp_dp = NULL, *tmp_fp = NULL;
> +	struct efi_device_path *final_fp = NULL, *initrd_dp = NULL;
> +	efi_status_t ret;
> +	const struct efi_initrd_dp id_dp = {
> +		.vendor = {
> +			{
> +			DEVICE_PATH_TYPE_MEDIA_DEVICE,
> +			DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
> +			sizeof(id_dp.vendor),
> +			},
> +			EFI_INITRD_MEDIA_GUID,
> +		},
> +		.end = {
> +			DEVICE_PATH_TYPE_END,
> +			DEVICE_PATH_SUB_TYPE_END,
> +			sizeof(id_dp.end),
> +		}
> +	};
> +
> +	ret = efi_dp_from_name(dev, part, file, &tmp_dp, &tmp_fp);
> +	if (ret != EFI_SUCCESS) {
> +		printf("Cannot create device path for \"%s %s\"\n", part, file);
> +		goto out;
> +	}
> +
> +	initrd_dp =
> +		efi_dp_append_instance((const struct efi_device_path *)&id_dp,

Please, pass &id_dp.end here to avoid a superfluous end node.

Best regards

Heinrich

> +				       tmp_fp);
> +	if (!initrd_dp) {
> +		printf("Cannot append media vendor device path path\n");
> +		goto out;
> +	}
> +	final_fp = efi_dp_concat(fp, initrd_dp);
> +	*fp_size = efi_dp_size(fp) + efi_dp_size(initrd_dp) +
> +		(2 * sizeof(struct efi_device_path));
> +
> +out:
> +	efi_free_pool(initrd_dp);
> +	efi_free_pool(tmp_dp);
> +	efi_free_pool(tmp_fp);
> +	return final_fp ? final_fp : ERR_PTR(-EINVAL);
> +}
> +
>  /**
>   * do_efi_boot_add() - set UEFI load option
>   *
> @@ -806,7 +868,9 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
>   *
>   * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
>   *
> - *     efidebug boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
> + * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
> + *                   -i <file> <interface2> <devnum2>[:<part>] <initrd>
> + *                   -s '<options>'
>   */
>  static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
>  			   int argc, char *const argv[])
> @@ -819,55 +883,98 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
>  	size_t label_len, label_len16;
>  	u16 *label;
>  	struct efi_device_path *device_path = NULL, *file_path = NULL;
> +	struct efi_device_path *final_fp = NULL;
>  	struct efi_load_option lo;
>  	void *data = NULL;
>  	efi_uintn_t size;
> +	efi_uintn_t fp_size;
>  	efi_status_t ret;
>  	int r = CMD_RET_SUCCESS;
> -
> -	if (argc < 6 || argc > 7)
> -		return CMD_RET_USAGE;
> -
> -	id = (int)simple_strtoul(argv[1], &endp, 16);
> -	if (*endp != '\0' || id > 0xffff)
> -		return CMD_RET_USAGE;
> -
> -	sprintf(var_name, "Boot%04X", id);
> -	p = var_name16;
> -	utf8_utf16_strncpy(&p, var_name, 9);
> +	int i;
>
>  	guid = efi_global_variable_guid;
>
>  	/* attributes */
>  	lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */
> +	lo.optional_data = NULL;
> +	lo.label = NULL;
>
> -	/* label */
> -	label_len = strlen(argv[2]);
> -	label_len16 = utf8_utf16_strnlen(argv[2], label_len);
> -	label = malloc((label_len16 + 1) * sizeof(u16));
> -	if (!label)
> -		return CMD_RET_FAILURE;
> -	lo.label = label; /* label will be changed below */
> -	utf8_utf16_strncpy(&label, argv[2], label_len);
> +	/* search for -b first since the rest of the arguments depends on that */
> +	for (i = 0; i < argc; i++) {
> +		if (!strcmp(argv[i], "-b")) {
> +			if (argc < i + 5 || lo.label) {
> +				r = CMD_RET_USAGE;
> +				goto out;
> +			}
> +			id = (int)simple_strtoul(argv[i + 1], &endp, 16);
> +			if (*endp != '\0' || id > 0xffff)
> +				return CMD_RET_USAGE;
> +
> +			sprintf(var_name, "Boot%04X", id);
> +			p = var_name16;
> +			utf8_utf16_strncpy(&p, var_name, 9);
> +
> +			/* label */
> +			label_len = strlen(argv[i + 2]);
> +			label_len16 = utf8_utf16_strnlen(argv[i + 2], label_len);
> +			label = malloc((label_len16 + 1) * sizeof(u16));
> +			if (!label)
> +				return CMD_RET_FAILURE;
> +			lo.label = label; /* label will be changed below */
> +			utf8_utf16_strncpy(&label, argv[i + 2], label_len);
> +
> +			/* file path */
> +			ret = efi_dp_from_name(argv[i + 3], argv[i + 4],
> +					       argv[i + 5], &device_path,
> +					       &file_path);
> +			if (ret != EFI_SUCCESS) {
> +				printf("Cannot create device path for \"%s %s\"\n",
> +				       argv[3], argv[4]);
> +				r = CMD_RET_FAILURE;
> +				goto out;
> +			break;
> +			}
> +			fp_size = efi_dp_size(file_path) +
> +				sizeof(struct efi_device_path);
> +		}
> +	}
>
> -	/* file path */
> -	ret = efi_dp_from_name(argv[3], argv[4], argv[5], &device_path,
> -			       &file_path);
> -	if (ret != EFI_SUCCESS) {
> -		printf("Cannot create device path for \"%s %s\"\n",
> -		       argv[3], argv[4]);
> +	if (!file_path) {
> +		printf("You need to specify an image before an initrd.\n");
>  		r = CMD_RET_FAILURE;
>  		goto out;
>  	}
> -	lo.file_path = file_path;
> -	lo.file_path_length = efi_dp_size(file_path)
> -				+ sizeof(struct efi_device_path); /* for END */
>
> -	/* optional data */
> -	if (argc == 6)
> -		lo.optional_data = NULL;
> -	else
> -		lo.optional_data = (const u8 *)argv[6];
> +	/* add now add initrd and extra data */
> +	for (i = 0; i < argc; i++) {
> +		if (!strcmp(argv[i], "-i")) {
> +			if (argc < i + 3 || final_fp) {
> +				r = CMD_RET_USAGE;
> +				goto out;
> +			}
> +
> +			final_fp = add_initrd_instance(argv[i + 1], argv[i + 2],
> +						       argv[i + 3], file_path,
> +						       &fp_size);
> +			if (IS_ERR(final_fp)) {
> +				r = CMD_RET_FAILURE;
> +				goto out;
> +			}
> +
> +			/* add_initrd_instance allocates a new device path */
> +			efi_free_pool(file_path);
> +			file_path = final_fp;
> +		} else if (!strcmp(argv[i], "-s")) {
> +			if (argc < i + 1 || lo.optional_data) {
> +				r = CMD_RET_USAGE;
> +				goto out;
> +			}
> +			lo.optional_data = (const u8 *)argv[i + 1];
> +		}
> +	}
> +
> +	lo.file_path = file_path;
> +	lo.file_path_length = fp_size;
>
>  	size = efi_serialize_load_option(&lo, (u8 **)&data);
>  	if (!size) {
> @@ -951,11 +1058,14 @@ static int do_efi_boot_rm(struct cmd_tbl *cmdtp, int flag,
>   */
>  static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
>  {
> +	struct efi_device_path *initrd_path = NULL;
>  	struct efi_load_option lo;
>  	char *label, *p;
>  	size_t label_len16, label_len;
>  	u16 *dp_str;
>  	efi_status_t ret;
> +	efi_uintn_t initrd_dp_size;
> +	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
>
>  	ret = efi_deserialize_load_option(&lo, data, size);
>  	if (ret != EFI_SUCCESS) {
> @@ -986,6 +1096,14 @@ static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
>  	printf("  file_path: %ls\n", dp_str);
>  	efi_free_pool(dp_str);
>
> +	initrd_path = efi_dp_from_lo(&lo, &initrd_dp_size, lf2_initrd_guid);
> +	if (initrd_path) {
> +		dp_str = efi_dp_str(initrd_path);
> +		printf("  initrd_path: %ls\n", dp_str);
> +		efi_free_pool(dp_str);
> +		efi_free_pool(initrd_path);
> +	}
> +
>  	printf("  data:\n");
>  	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
>  		       lo.optional_data, *size, true);
> @@ -1555,7 +1673,10 @@ static int do_efidebug(struct cmd_tbl *cmdtp, int flag,
>  static char efidebug_help_text[] =
>  	"  - UEFI Shell-like interface to configure UEFI environment\n"
>  	"\n"
> -	"efidebug boot add <bootid> <label> <interface> <devnum>[:<part>] <file path> [<load options>]\n"
> +	"efidebug boot add "
> +	"-b <bootid> <label> <interface> <devnum>[:<part>] <file path> "
> +	"-i <interface> <devnum>[:<part>] <initrd file path> "
> +	"-s '<optional data>'\n"
>  	"  - set UEFI BootXXXX variable\n"
>  	"    <load options> will be passed to UEFI application\n"
>  	"efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n"
> @@ -1599,7 +1720,7 @@ static char efidebug_help_text[] =
>  #endif
>
>  U_BOOT_CMD(
> -	efidebug, 10, 0, do_efidebug,
> +	efidebug, CONFIG_SYS_MAXARGS, 0, do_efidebug,
>  	"Configure UEFI environment",
>  	efidebug_help_text
>  );
> diff --git a/doc/board/emulation/qemu_capsule_update.rst b/doc/board/emulation/qemu_capsule_update.rst
> index 9fec75f8f1c9..33ce4bcd32ea 100644
> --- a/doc/board/emulation/qemu_capsule_update.rst
> +++ b/doc/board/emulation/qemu_capsule_update.rst
> @@ -60,7 +60,7 @@ to be pointing to the EFI System Partition which contains the capsule
>  file. The BootNext, BootXXXX and OsIndications variables can be set
>  using the following commands::
>
> -    => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
> +    => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
>      => efidebug boot next 0
>      => setenv -e -nv -bs -rt -v OsIndications =0x04
>      => saveenv
> @@ -198,7 +198,7 @@ command line::
>      3. Set the following environment and UEFI boot variables
>
>          => setenv -e -nv -bs -rt -v OsIndications =0x04
> -        => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
> +        => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
>          => efidebug boot next 0
>          => saveenv
>
> diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst
> index 5a67737c1579..b3494c22e073 100644
> --- a/doc/uefi/uefi.rst
> +++ b/doc/uefi/uefi.rst
> @@ -178,7 +178,7 @@ Now in U-Boot install the keys on your board::
>
>  Set up boot parameters on your board::
>
> -    efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed ""
> +    efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed ""
>
>  Now your board can run the signed image via the boot manager (see below).
>  You can also try this sequence by running Pytest, test_efi_secboot,
> diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> index f006fa95d650..e8b0a1575453 100644
> --- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> @@ -39,7 +39,7 @@ class TestEfiCapsuleFirmwareFit(object):
>          with u_boot_console.log.section('Test Case 1-a, before reboot'):
>              output = u_boot_console.run_command_list([
>                  'host bind 0 %s' % disk_img,
> -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
>                  'efidebug boot order 1',
>                  'env set -e OsIndications',
>                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> @@ -114,7 +114,7 @@ class TestEfiCapsuleFirmwareFit(object):
>          with u_boot_console.log.section('Test Case 2-a, before reboot'):
>              output = u_boot_console.run_command_list([
>                  'host bind 0 %s' % disk_img,
> -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
>                  'efidebug boot order 1',
>                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
>                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> @@ -188,7 +188,7 @@ class TestEfiCapsuleFirmwareFit(object):
>          with u_boot_console.log.section('Test Case 3-a, before reboot'):
>              output = u_boot_console.run_command_list([
>                  'host bind 0 %s' % disk_img,
> -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
>                  'efidebug boot order 1',
>                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
>                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py
> index 863685e215b7..75f5ea772300 100644
> --- a/test/py/tests/test_efi_secboot/test_signed.py
> +++ b/test/py/tests/test_efi_secboot/test_signed.py
> @@ -28,7 +28,7 @@ class TestEfiSignedImage(object):
>              # Test Case 1a, run signed image if no PK
>              output = u_boot_console.run_command_list([
>                  'host bind 0 %s' % disk_img,
> -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> @@ -36,7 +36,7 @@ class TestEfiSignedImage(object):
>          with u_boot_console.log.section('Test Case 1b'):
>              # Test Case 1b, run unsigned image if no PK
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 2',
>                  'bootefi bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> @@ -58,13 +58,13 @@ class TestEfiSignedImage(object):
>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>              assert 'Failed to set EFI variable' not in ''.join(output)
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert('\'HELLO1\' failed' in ''.join(output))
>              assert('efi_start_image() returned: 26' in ''.join(output))
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 2',
>                  'efidebug test bootmgr'])
>              assert '\'HELLO2\' failed' in ''.join(output)
> @@ -104,7 +104,7 @@ class TestEfiSignedImage(object):
>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>              assert 'Failed to set EFI variable' not in ''.join(output)
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert '\'HELLO\' failed' in ''.join(output)
> @@ -142,7 +142,7 @@ class TestEfiSignedImage(object):
>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>              assert 'Failed to set EFI variable' not in ''.join(output)
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert '\'HELLO\' failed' in ''.join(output)
> @@ -169,7 +169,7 @@ class TestEfiSignedImage(object):
>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>              assert 'Failed to set EFI variable' not in ''.join(output)
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> @@ -227,7 +227,7 @@ class TestEfiSignedImage(object):
>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>              assert 'Failed to set EFI variable' not in ''.join(output)
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py
> index 70d6be00e8a8..0849572a5143 100644
> --- a/test/py/tests/test_efi_secboot/test_signed_intca.py
> +++ b/test/py/tests/test_efi_secboot/test_signed_intca.py
> @@ -39,7 +39,7 @@ class TestEfiSignedImageIntca(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
> +                'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert '\'HELLO_a\' failed' in ''.join(output)
> @@ -48,7 +48,7 @@ class TestEfiSignedImageIntca(object):
>          with u_boot_console.log.section('Test Case 1b'):
>              # Test Case 1b, signed and authenticated by root CA
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
> +                'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
>                  'efidebug boot next 2',
>                  'bootefi bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> @@ -70,7 +70,7 @@ class TestEfiSignedImageIntca(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert '\'HELLO_abc\' failed' in ''.join(output)
> @@ -116,7 +116,7 @@ class TestEfiSignedImageIntca(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py
> index 56f56e19eb84..8e026f7566ad 100644
> --- a/test/py/tests/test_efi_secboot/test_unsigned.py
> +++ b/test/py/tests/test_efi_secboot/test_unsigned.py
> @@ -35,7 +35,7 @@ class TestEfiUnsignedImage(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert '\'HELLO\' failed' in ''.join(output)
> @@ -64,7 +64,7 @@ class TestEfiUnsignedImage(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> @@ -88,7 +88,7 @@ class TestEfiUnsignedImage(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert '\'HELLO\' failed' in ''.join(output)
> @@ -106,7 +106,7 @@ class TestEfiUnsignedImage(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert '\'HELLO\' failed' in ''.join(output)
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11 12:31             ` Heinrich Schuchardt
@ 2021-03-11 12:39               ` Ilias Apalodimas
  2021-03-11 12:44                 ` Heinrich Schuchardt
  0 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-11 12:39 UTC (permalink / raw)
  To: u-boot

Hi Heinrich,

[...]
> >> No, the structure is added in cmd/efidebug.c code.
> >> It's created with efi_dp_append_instance() on
> >> - const struct efi_initrd_dp id_dp
> >> - file path of initrd
> >>
> >> which will create:
> >> kernel path,end(0xff),
> >> VenMedia(), end(0x01),
> 
> This end node is superfluous.

Why?i It's not defined in any standard (in fact these patches will be used for
the USWG proposal), it's making our lives a lot easier during parsing, since
you only need to get the next instance for the *entire* device path.
On the other hand if you mix and match them, you'll have to skip the first
entry with dp->len and then start using code to get the next instance. Similar
logic applies to appending the instance obviously. Since you now how have to
account for your 'special' first node, which isn't that special to begin with.

So why exactly is it superflous? Because you can get the next initrd without
having to parse an end of instace? Well that's the case for the initrd's as
well then .... 
On the contrary I think it makes the entire device path look more generic, 
since each node is separated by an end of instance, no matter what the
contents are.

Thanks
/Ilias

> 
> Best regards
> 
> Heinrich
> 
> >> initrd1, end(0x01),
> >> initrd2, end(0xff)
> >>
> >> I know I originally proposed the one you have, but it seemed cleaner
> >> adding
> >> an extra instance between VenMedia and the first initrd.
> >>
> >>>
> >>> Please, document the structure.
> >>>
> >>
> >> Sure
> >>
> >>> Best regards
> >>>
> >>> Heinrich
> >>
> >> Thanks
> >> /Ilias
> >
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 6/6] doc: Update uefi documentation for initrd loading options
  2021-03-05 22:23 ` [PATCH 6/6] doc: Update uefi documentation for initrd loading options Ilias Apalodimas
@ 2021-03-11 12:39   ` Heinrich Schuchardt
  0 siblings, 0 replies; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11 12:39 UTC (permalink / raw)
  To: u-boot

On 05.03.21 23:23, Ilias Apalodimas wrote:
> Document the command line options for efidebug and initrd loading
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> ---
>  doc/uefi/uefi.rst | 6 ++++++
>  1 file changed, 6 insertions(+)
>
> diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst
> index b3494c22e073..b3330f8d7273 100644
> --- a/doc/uefi/uefi.rst
> +++ b/doc/uefi/uefi.rst
> @@ -180,6 +180,12 @@ Set up boot parameters on your board::
>
>      efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed ""
>
> +Since kernel 5.7 there's an alternative way of loading an initrd using
> +LoadFile2 protocol if CONFIG_EFI_LOAD_FILE2_INITRD is enabled.
> +The initrd path can be specified with::
> +
> +    efidebug boot add -b ABE0 'kernel' mmc 0:1 Image -i mmc 0:1 initrd
> +
>  Now your board can run the signed image via the boot manager (see below).
>  You can also try this sequence by running Pytest, test_efi_secboot,
>  on the sandbox
>

Please, add text to doc/uefi/uefi.rst describing how we use boot options
for initrds.

Best regards

Heinrich

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 5/6] efidebug: add multiple device path instances on Boot####
  2021-03-11 12:38   ` Heinrich Schuchardt
@ 2021-03-11 12:42     ` Ilias Apalodimas
  0 siblings, 0 replies; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-11 12:42 UTC (permalink / raw)
  To: u-boot

On Thu, Mar 11, 2021 at 01:38:33PM +0100, Heinrich Schuchardt wrote:
> > +	}
> > +
> > +	initrd_dp =
> > +		efi_dp_append_instance((const struct efi_device_path *)&id_dp,
> 
> Please, pass &id_dp.end here to avoid a superfluous end node.

Wont this make efi_dp_size() loop endlessly?


Regards
/Ilias
> 
> Best regards
> 
> Heinrich
> 
> > +				       tmp_fp);
> > +	if (!initrd_dp) {
> > +		printf("Cannot append media vendor device path path\n");
> > +		goto out;
> > +	}
> > +	final_fp = efi_dp_concat(fp, initrd_dp);
> > +	*fp_size = efi_dp_size(fp) + efi_dp_size(initrd_dp) +
> > +		(2 * sizeof(struct efi_device_path));
> > +
> > +out:
> > +	efi_free_pool(initrd_dp);
> > +	efi_free_pool(tmp_dp);
> > +	efi_free_pool(tmp_fp);
> > +	return final_fp ? final_fp : ERR_PTR(-EINVAL);
> > +}
> > +
> >  /**
> >   * do_efi_boot_add() - set UEFI load option
> >   *
> > @@ -806,7 +868,9 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
> >   *
> >   * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
> >   *
> > - *     efidebug boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
> > + * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
> > + *                   -i <file> <interface2> <devnum2>[:<part>] <initrd>
> > + *                   -s '<options>'
> >   */
> >  static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
> >  			   int argc, char *const argv[])
> > @@ -819,55 +883,98 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
> >  	size_t label_len, label_len16;
> >  	u16 *label;
> >  	struct efi_device_path *device_path = NULL, *file_path = NULL;
> > +	struct efi_device_path *final_fp = NULL;
> >  	struct efi_load_option lo;
> >  	void *data = NULL;
> >  	efi_uintn_t size;
> > +	efi_uintn_t fp_size;
> >  	efi_status_t ret;
> >  	int r = CMD_RET_SUCCESS;
> > -
> > -	if (argc < 6 || argc > 7)
> > -		return CMD_RET_USAGE;
> > -
> > -	id = (int)simple_strtoul(argv[1], &endp, 16);
> > -	if (*endp != '\0' || id > 0xffff)
> > -		return CMD_RET_USAGE;
> > -
> > -	sprintf(var_name, "Boot%04X", id);
> > -	p = var_name16;
> > -	utf8_utf16_strncpy(&p, var_name, 9);
> > +	int i;
> >
> >  	guid = efi_global_variable_guid;
> >
> >  	/* attributes */
> >  	lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */
> > +	lo.optional_data = NULL;
> > +	lo.label = NULL;
> >
> > -	/* label */
> > -	label_len = strlen(argv[2]);
> > -	label_len16 = utf8_utf16_strnlen(argv[2], label_len);
> > -	label = malloc((label_len16 + 1) * sizeof(u16));
> > -	if (!label)
> > -		return CMD_RET_FAILURE;
> > -	lo.label = label; /* label will be changed below */
> > -	utf8_utf16_strncpy(&label, argv[2], label_len);
> > +	/* search for -b first since the rest of the arguments depends on that */
> > +	for (i = 0; i < argc; i++) {
> > +		if (!strcmp(argv[i], "-b")) {
> > +			if (argc < i + 5 || lo.label) {
> > +				r = CMD_RET_USAGE;
> > +				goto out;
> > +			}
> > +			id = (int)simple_strtoul(argv[i + 1], &endp, 16);
> > +			if (*endp != '\0' || id > 0xffff)
> > +				return CMD_RET_USAGE;
> > +
> > +			sprintf(var_name, "Boot%04X", id);
> > +			p = var_name16;
> > +			utf8_utf16_strncpy(&p, var_name, 9);
> > +
> > +			/* label */
> > +			label_len = strlen(argv[i + 2]);
> > +			label_len16 = utf8_utf16_strnlen(argv[i + 2], label_len);
> > +			label = malloc((label_len16 + 1) * sizeof(u16));
> > +			if (!label)
> > +				return CMD_RET_FAILURE;
> > +			lo.label = label; /* label will be changed below */
> > +			utf8_utf16_strncpy(&label, argv[i + 2], label_len);
> > +
> > +			/* file path */
> > +			ret = efi_dp_from_name(argv[i + 3], argv[i + 4],
> > +					       argv[i + 5], &device_path,
> > +					       &file_path);
> > +			if (ret != EFI_SUCCESS) {
> > +				printf("Cannot create device path for \"%s %s\"\n",
> > +				       argv[3], argv[4]);
> > +				r = CMD_RET_FAILURE;
> > +				goto out;
> > +			break;
> > +			}
> > +			fp_size = efi_dp_size(file_path) +
> > +				sizeof(struct efi_device_path);
> > +		}
> > +	}
> >
> > -	/* file path */
> > -	ret = efi_dp_from_name(argv[3], argv[4], argv[5], &device_path,
> > -			       &file_path);
> > -	if (ret != EFI_SUCCESS) {
> > -		printf("Cannot create device path for \"%s %s\"\n",
> > -		       argv[3], argv[4]);
> > +	if (!file_path) {
> > +		printf("You need to specify an image before an initrd.\n");
> >  		r = CMD_RET_FAILURE;
> >  		goto out;
> >  	}
> > -	lo.file_path = file_path;
> > -	lo.file_path_length = efi_dp_size(file_path)
> > -				+ sizeof(struct efi_device_path); /* for END */
> >
> > -	/* optional data */
> > -	if (argc == 6)
> > -		lo.optional_data = NULL;
> > -	else
> > -		lo.optional_data = (const u8 *)argv[6];
> > +	/* add now add initrd and extra data */
> > +	for (i = 0; i < argc; i++) {
> > +		if (!strcmp(argv[i], "-i")) {
> > +			if (argc < i + 3 || final_fp) {
> > +				r = CMD_RET_USAGE;
> > +				goto out;
> > +			}
> > +
> > +			final_fp = add_initrd_instance(argv[i + 1], argv[i + 2],
> > +						       argv[i + 3], file_path,
> > +						       &fp_size);
> > +			if (IS_ERR(final_fp)) {
> > +				r = CMD_RET_FAILURE;
> > +				goto out;
> > +			}
> > +
> > +			/* add_initrd_instance allocates a new device path */
> > +			efi_free_pool(file_path);
> > +			file_path = final_fp;
> > +		} else if (!strcmp(argv[i], "-s")) {
> > +			if (argc < i + 1 || lo.optional_data) {
> > +				r = CMD_RET_USAGE;
> > +				goto out;
> > +			}
> > +			lo.optional_data = (const u8 *)argv[i + 1];
> > +		}
> > +	}
> > +
> > +	lo.file_path = file_path;
> > +	lo.file_path_length = fp_size;
> >
> >  	size = efi_serialize_load_option(&lo, (u8 **)&data);
> >  	if (!size) {
> > @@ -951,11 +1058,14 @@ static int do_efi_boot_rm(struct cmd_tbl *cmdtp, int flag,
> >   */
> >  static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
> >  {
> > +	struct efi_device_path *initrd_path = NULL;
> >  	struct efi_load_option lo;
> >  	char *label, *p;
> >  	size_t label_len16, label_len;
> >  	u16 *dp_str;
> >  	efi_status_t ret;
> > +	efi_uintn_t initrd_dp_size;
> > +	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
> >
> >  	ret = efi_deserialize_load_option(&lo, data, size);
> >  	if (ret != EFI_SUCCESS) {
> > @@ -986,6 +1096,14 @@ static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
> >  	printf("  file_path: %ls\n", dp_str);
> >  	efi_free_pool(dp_str);
> >
> > +	initrd_path = efi_dp_from_lo(&lo, &initrd_dp_size, lf2_initrd_guid);
> > +	if (initrd_path) {
> > +		dp_str = efi_dp_str(initrd_path);
> > +		printf("  initrd_path: %ls\n", dp_str);
> > +		efi_free_pool(dp_str);
> > +		efi_free_pool(initrd_path);
> > +	}
> > +
> >  	printf("  data:\n");
> >  	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
> >  		       lo.optional_data, *size, true);
> > @@ -1555,7 +1673,10 @@ static int do_efidebug(struct cmd_tbl *cmdtp, int flag,
> >  static char efidebug_help_text[] =
> >  	"  - UEFI Shell-like interface to configure UEFI environment\n"
> >  	"\n"
> > -	"efidebug boot add <bootid> <label> <interface> <devnum>[:<part>] <file path> [<load options>]\n"
> > +	"efidebug boot add "
> > +	"-b <bootid> <label> <interface> <devnum>[:<part>] <file path> "
> > +	"-i <interface> <devnum>[:<part>] <initrd file path> "
> > +	"-s '<optional data>'\n"
> >  	"  - set UEFI BootXXXX variable\n"
> >  	"    <load options> will be passed to UEFI application\n"
> >  	"efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n"
> > @@ -1599,7 +1720,7 @@ static char efidebug_help_text[] =
> >  #endif
> >
> >  U_BOOT_CMD(
> > -	efidebug, 10, 0, do_efidebug,
> > +	efidebug, CONFIG_SYS_MAXARGS, 0, do_efidebug,
> >  	"Configure UEFI environment",
> >  	efidebug_help_text
> >  );
> > diff --git a/doc/board/emulation/qemu_capsule_update.rst b/doc/board/emulation/qemu_capsule_update.rst
> > index 9fec75f8f1c9..33ce4bcd32ea 100644
> > --- a/doc/board/emulation/qemu_capsule_update.rst
> > +++ b/doc/board/emulation/qemu_capsule_update.rst
> > @@ -60,7 +60,7 @@ to be pointing to the EFI System Partition which contains the capsule
> >  file. The BootNext, BootXXXX and OsIndications variables can be set
> >  using the following commands::
> >
> > -    => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
> > +    => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
> >      => efidebug boot next 0
> >      => setenv -e -nv -bs -rt -v OsIndications =0x04
> >      => saveenv
> > @@ -198,7 +198,7 @@ command line::
> >      3. Set the following environment and UEFI boot variables
> >
> >          => setenv -e -nv -bs -rt -v OsIndications =0x04
> > -        => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
> > +        => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
> >          => efidebug boot next 0
> >          => saveenv
> >
> > diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst
> > index 5a67737c1579..b3494c22e073 100644
> > --- a/doc/uefi/uefi.rst
> > +++ b/doc/uefi/uefi.rst
> > @@ -178,7 +178,7 @@ Now in U-Boot install the keys on your board::
> >
> >  Set up boot parameters on your board::
> >
> > -    efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed ""
> > +    efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed ""
> >
> >  Now your board can run the signed image via the boot manager (see below).
> >  You can also try this sequence by running Pytest, test_efi_secboot,
> > diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> > index f006fa95d650..e8b0a1575453 100644
> > --- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> > +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> > @@ -39,7 +39,7 @@ class TestEfiCapsuleFirmwareFit(object):
> >          with u_boot_console.log.section('Test Case 1-a, before reboot'):
> >              output = u_boot_console.run_command_list([
> >                  'host bind 0 %s' % disk_img,
> > -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
> >                  'efidebug boot order 1',
> >                  'env set -e OsIndications',
> >                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> > @@ -114,7 +114,7 @@ class TestEfiCapsuleFirmwareFit(object):
> >          with u_boot_console.log.section('Test Case 2-a, before reboot'):
> >              output = u_boot_console.run_command_list([
> >                  'host bind 0 %s' % disk_img,
> > -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
> >                  'efidebug boot order 1',
> >                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> >                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> > @@ -188,7 +188,7 @@ class TestEfiCapsuleFirmwareFit(object):
> >          with u_boot_console.log.section('Test Case 3-a, before reboot'):
> >              output = u_boot_console.run_command_list([
> >                  'host bind 0 %s' % disk_img,
> > -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
> >                  'efidebug boot order 1',
> >                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> >                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> > diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py
> > index 863685e215b7..75f5ea772300 100644
> > --- a/test/py/tests/test_efi_secboot/test_signed.py
> > +++ b/test/py/tests/test_efi_secboot/test_signed.py
> > @@ -28,7 +28,7 @@ class TestEfiSignedImage(object):
> >              # Test Case 1a, run signed image if no PK
> >              output = u_boot_console.run_command_list([
> >                  'host bind 0 %s' % disk_img,
> > -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> > +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > @@ -36,7 +36,7 @@ class TestEfiSignedImage(object):
> >          with u_boot_console.log.section('Test Case 1b'):
> >              # Test Case 1b, run unsigned image if no PK
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 2',
> >                  'bootefi bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > @@ -58,13 +58,13 @@ class TestEfiSignedImage(object):
> >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> > +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert('\'HELLO1\' failed' in ''.join(output))
> >              assert('efi_start_image() returned: 26' in ''.join(output))
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 2',
> >                  'efidebug test bootmgr'])
> >              assert '\'HELLO2\' failed' in ''.join(output)
> > @@ -104,7 +104,7 @@ class TestEfiSignedImage(object):
> >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert '\'HELLO\' failed' in ''.join(output)
> > @@ -142,7 +142,7 @@ class TestEfiSignedImage(object):
> >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert '\'HELLO\' failed' in ''.join(output)
> > @@ -169,7 +169,7 @@ class TestEfiSignedImage(object):
> >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > @@ -227,7 +227,7 @@ class TestEfiSignedImage(object):
> >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py
> > index 70d6be00e8a8..0849572a5143 100644
> > --- a/test/py/tests/test_efi_secboot/test_signed_intca.py
> > +++ b/test/py/tests/test_efi_secboot/test_signed_intca.py
> > @@ -39,7 +39,7 @@ class TestEfiSignedImageIntca(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
> > +                'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert '\'HELLO_a\' failed' in ''.join(output)
> > @@ -48,7 +48,7 @@ class TestEfiSignedImageIntca(object):
> >          with u_boot_console.log.section('Test Case 1b'):
> >              # Test Case 1b, signed and authenticated by root CA
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
> > +                'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
> >                  'efidebug boot next 2',
> >                  'bootefi bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > @@ -70,7 +70,7 @@ class TestEfiSignedImageIntca(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> > +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert '\'HELLO_abc\' failed' in ''.join(output)
> > @@ -116,7 +116,7 @@ class TestEfiSignedImageIntca(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> > +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py
> > index 56f56e19eb84..8e026f7566ad 100644
> > --- a/test/py/tests/test_efi_secboot/test_unsigned.py
> > +++ b/test/py/tests/test_efi_secboot/test_unsigned.py
> > @@ -35,7 +35,7 @@ class TestEfiUnsignedImage(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert '\'HELLO\' failed' in ''.join(output)
> > @@ -64,7 +64,7 @@ class TestEfiUnsignedImage(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > @@ -88,7 +88,7 @@ class TestEfiUnsignedImage(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert '\'HELLO\' failed' in ''.join(output)
> > @@ -106,7 +106,7 @@ class TestEfiUnsignedImage(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert '\'HELLO\' failed' in ''.join(output)
> >
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11 12:39               ` Ilias Apalodimas
@ 2021-03-11 12:44                 ` Heinrich Schuchardt
  2021-03-11 12:49                   ` Ilias Apalodimas
  0 siblings, 1 reply; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11 12:44 UTC (permalink / raw)
  To: u-boot

On 11.03.21 13:39, Ilias Apalodimas wrote:
> Hi Heinrich,
>
> [...]
>>>> No, the structure is added in cmd/efidebug.c code.
>>>> It's created with efi_dp_append_instance() on
>>>> - const struct efi_initrd_dp id_dp
>>>> - file path of initrd
>>>>
>>>> which will create:
>>>> kernel path,end(0xff),
>>>> VenMedia(), end(0x01),
>>
>> This end node is superfluous.
>
> Why?i It's not defined in any standard (in fact these patches will be used for
> the USWG proposal), it's making our lives a lot easier during parsing, since
> you only need to get the next instance for the *entire* device path.
> On the other hand if you mix and match them, you'll have to skip the first
> entry with dp->len and then start using code to get the next instance. Similar
> logic applies to appending the instance obviously. Since you now how have to
> account for your 'special' first node, which isn't that special to begin with.
>
> So why exactly is it superflous? Because you can get the next initrd without
> having to parse an end of instace? Well that's the case for the initrd's as
> well then ....
> On the contrary I think it makes the entire device path look more generic,
> since each node is separated by an end of instance, no matter what the
> contents are.

We use the VenMedia() node to identify a multi-part device tree
containing one or multiple initrd.

An end node does not convey any additional information.

The first initrd device path can start directly behind the VenMedia()
node. This saves space for non-volatile variables which is limited.

Best regards

Heinrich

>
> Thanks
> /Ilias
>
>>
>> Best regards
>>
>> Heinrich
>>
>>>> initrd1, end(0x01),
>>>> initrd2, end(0xff)
>>>>
>>>> I know I originally proposed the one you have, but it seemed cleaner
>>>> adding
>>>> an extra instance between VenMedia and the first initrd.
>>>>
>>>>>
>>>>> Please, document the structure.
>>>>>
>>>>
>>>> Sure
>>>>
>>>>> Best regards
>>>>>
>>>>> Heinrich
>>>>
>>>> Thanks
>>>> /Ilias
>>>
>>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11 12:44                 ` Heinrich Schuchardt
@ 2021-03-11 12:49                   ` Ilias Apalodimas
  0 siblings, 0 replies; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-11 12:49 UTC (permalink / raw)
  To: u-boot

On Thu, Mar 11, 2021 at 01:44:05PM +0100, Heinrich Schuchardt wrote:
> On 11.03.21 13:39, Ilias Apalodimas wrote:
> > Hi Heinrich,
> >
> > [...]
> >>>> No, the structure is added in cmd/efidebug.c code.
> >>>> It's created with efi_dp_append_instance() on
> >>>> - const struct efi_initrd_dp id_dp
> >>>> - file path of initrd
> >>>>
> >>>> which will create:
> >>>> kernel path,end(0xff),
> >>>> VenMedia(), end(0x01),
> >>
> >> This end node is superfluous.
> >
> > Why?i It's not defined in any standard (in fact these patches will be used for
> > the USWG proposal), it's making our lives a lot easier during parsing, since
> > you only need to get the next instance for the *entire* device path.
> > On the other hand if you mix and match them, you'll have to skip the first
> > entry with dp->len and then start using code to get the next instance. Similar
> > logic applies to appending the instance obviously. Since you now how have to
> > account for your 'special' first node, which isn't that special to begin with.
> >
> > So why exactly is it superflous? Because you can get the next initrd without
> > having to parse an end of instace? Well that's the case for the initrd's as
> > well then ....
> > On the contrary I think it makes the entire device path look more generic,
> > since each node is separated by an end of instance, no matter what the
> > contents are.
> 
> We use the VenMedia() node to identify a multi-part device tree
> containing one or multiple initrd.
> 
> An end node does not convey any additional information.
> 
> The first initrd device path can start directly behind the VenMedia()
> node. This saves space for non-volatile variables which is limited.
> 

Again, it can. But we can also remove the end node after each initrd no?
Then we'd save even more space...

My point is, I can't understand why we should complicate the adding
code/parsing code, by adding a special use case.
I don't think saving 4bytes is really worth the complexity.

Thanks
/Ilias
> Best regards
> 
> Heinrich
> 
> >
> > Thanks
> > /Ilias
> >
> >>
> >> Best regards
> >>
> >> Heinrich
> >>
> >>>> initrd1, end(0x01),
> >>>> initrd2, end(0xff)
> >>>>
> >>>> I know I originally proposed the one you have, but it seemed cleaner
> >>>> adding
> >>>> an extra instance between VenMedia and the first initrd.
> >>>>
> >>>>>
> >>>>> Please, document the structure.
> >>>>>
> >>>>
> >>>> Sure
> >>>>
> >>>>> Best regards
> >>>>>
> >>>>> Heinrich
> >>>>
> >>>> Thanks
> >>>> /Ilias
> >>>
> >>
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 4/6] efi_loader: Replace config option for initrd loading
  2021-03-11 12:30     ` Ilias Apalodimas
@ 2021-03-11 12:50       ` Heinrich Schuchardt
  0 siblings, 0 replies; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11 12:50 UTC (permalink / raw)
  To: u-boot

On 11.03.21 13:30, Ilias Apalodimas wrote:
> On Thu, Mar 11, 2021 at 01:23:05PM +0100, Heinrich Schuchardt wrote:
>> On 05.03.21 23:23, Ilias Apalodimas wrote:
>>> Up to now we install EFI_LOAD_FILE2_PROTOCOL to load an initrd
>>> unconditionally. Although we correctly return various EFI exit codes
>>> depending on the file status (i.e EFI_NO_MEDIA, EFI_NOT_FOUND etc), the
>>> kernel loader, only falls back to the cmdline interpreted initrd if the
>>> protocol is not installed.
>>>
>>> This creates a problem for EFI installers, since they won't be able to
>>> load their own initrd and continue the installation. It also makes the
>>> feature hard to use, since we can either have a single initrd or we have
>>> to recompile u-boot if the filename changes.
>>>
>>> So let's introduce a different logic that will decouple the initrd
>>> path from the config option we currently have.
>>> When defining a UEFI BootXXXX we can use the filepathlist and store
>>> a file path pointing to our initrd. Specifically the EFI spec describes:
>>>
>>> "The first element of the array is a device path that describes the device
>>> and location of the Image for this load option. Other device paths may
>>> optionally exist in the FilePathList, but their usage is OSV specific"
>>>
>>> When the EFI application is launched through the bootmgr, we'll try to
>>> interpret the extra device path. If that points to a file that exists on
>>> our disk, we'll now install the load_file2 and the efi-stub will be able
>>> to use it.
>>>
>>> This opens up another path using U-Boot and defines a new boot flow.
>>> A user will be able to control the kernel/initrd pairs without explicit
>>> cmdline args or GRUB.
>>>
>>> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
>>> ---
>>>  cmd/bootefi.c                    |   3 +
>>>  include/efi_loader.h             |   1 +
>>>  lib/efi_loader/Kconfig           |  15 +--
>>>  lib/efi_loader/efi_bootmgr.c     |   3 +
>>>  lib/efi_loader/efi_load_initrd.c | 209 +++++++++++++++++++++----------
>>>  5 files changed, 152 insertions(+), 79 deletions(-)
>>>
>>> diff --git a/cmd/bootefi.c b/cmd/bootefi.c
>>> index 271b385edea6..cba81ffe75e4 100644
>>> --- a/cmd/bootefi.c
>>> +++ b/cmd/bootefi.c
>>> @@ -358,6 +358,9 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options)
>>>
>>>  	free(load_options);
>>>
>>> +	if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD))
>>> +		efi_initrd_deregister();
>>> +
>>>  	return ret;
>>>  }
>>>
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index eb11a8c7d4b1..0d4f5d9acc9f 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -431,6 +431,7 @@ efi_status_t efi_net_register(void);
>>>  /* Called by bootefi to make the watchdog available */
>>>  efi_status_t efi_watchdog_register(void);
>>>  efi_status_t efi_initrd_register(void);
>>> +void efi_initrd_deregister(void);
>>>  /* Called by bootefi to make SMBIOS tables available */
>>>  /**
>>>   * efi_acpi_register() - write out ACPI tables
>>> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
>>> index e729f727df11..029f0e515f57 100644
>>> --- a/lib/efi_loader/Kconfig
>>> +++ b/lib/efi_loader/Kconfig
>>> @@ -317,16 +317,11 @@ config EFI_LOAD_FILE2_INITRD
>>>  	bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk"
>>>  	default n
>>>  	help
>>> -	  Expose a EFI_FILE_LOAD2_PROTOCOL that the Linux UEFI stub can
>>> -	  use to load the initial ramdisk. Once this is enabled using
>>> -	  initrd=<ramdisk> will stop working.
>>> -
>>> -config EFI_INITRD_FILESPEC
>>> -	string "initramfs path"
>>> -	default "host 0:1 initrd"
>>> -	depends on EFI_LOAD_FILE2_INITRD
>>> -	help
>>> -	  Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz.
>>> +		Linux v5.7 and later can make use of this option. If the boot option
>>> +		selected by the UEFI boot manager specifies an existing file to be used
>>> +		as initial RAM disk, a Linux specific Load File2 protocol will be
>>> +		installed and Linux 5.7+ will ignore any initrd=<ramdisk> command line
>>> +		argument.
>>>
>>>  config EFI_SECURE_BOOT
>>>  	bool "Enable EFI secure boot support"
>>> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
>>> index 25f5cebfdb67..d1baa8c71a4d 100644
>>> --- a/lib/efi_loader/efi_bootmgr.c
>>> +++ b/lib/efi_loader/efi_bootmgr.c
>>> @@ -118,6 +118,9 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
>>>  		ret = efi_set_variable_int(L"BootCurrent",
>>>  					   &efi_global_variable_guid,
>>>  					   attributes, sizeof(n), &n, false);
>>
>> Why would you continue if BootCurrent is not set?
>
> Because the bitops below is just trying to simplify the code
> reading, since both must call efi_unload_image(). Calling
> efi_initrd_register() without a BootCurrent is guaranteed to fail as well.
>
>>
>>> +		/* try to register load file2 for initrd's */
>>> +		if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD))
>>> +			ret |= efi_initrd_register();
>>
>> ret is not a boolean. So |= does not make sense to me here.
>
> Uhm? It's an unsigned long though and you can hav bitops properly?

EFI_DEVICE_ERROR | EFI_INVALID_PARAMETER => EFI_ACCESS_DENIED

That does not make much sense.

Best regards

Heinrich

>
>>
>>>  		if (ret != EFI_SUCCESS) {
>>>  			if (EFI_CALL(efi_unload_image(*handle))
>>>  			    != EFI_SUCCESS)
>>> diff --git a/lib/efi_loader/efi_load_initrd.c b/lib/efi_loader/efi_load_initrd.c
>>> index b9ee8839054f..b11c5ee293fe 100644
>>> --- a/lib/efi_loader/efi_load_initrd.c
>>> +++ b/lib/efi_loader/efi_load_initrd.c
>>> @@ -5,7 +5,9 @@
>>>
>>>  #include <common.h>
>>>  #include <efi_loader.h>
>>> +#include <efi_helper.h>
>>>  #include <efi_load_initrd.h>
>>> +#include <efi_variable.h>
>>>  #include <fs.h>
>>>  #include <malloc.h>
>>>  #include <mapmem.h>
>>> @@ -39,41 +41,71 @@ static const struct efi_initrd_dp dp = {
>>>  	}
>>>  };
>>>
>>> +static efi_handle_t efi_initrd_handle;
>>> +
>>>  /**
>>> - * get_file_size() - retrieve the size of initramfs, set efi status on error
>>> + * get_initrd_fp() - Get initrd device path from a FilePathList device path
>>>   *
>>> - * @dev:			device to read from, e.g. "mmc"
>>> - * @part:			device partition, e.g. "0:1"
>>> - * @file:			name of file
>>> - * @status:			EFI exit code in case of failure
>>> + * @initrd_fp:			the final initrd filepath
>>>   *
>>> - * Return:			size of file
>>> + * Return:			status code
>>>   */
>>> -static loff_t get_file_size(const char *dev, const char *part, const char *file,
>>> -			    efi_status_t *status)
>>> +static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
>>>  {
>>> -	loff_t sz = 0;
>>> -	int ret;
>>> +	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
>>> +	struct efi_device_path *cur = NULL;
>>> +	struct efi_device_path *dp = NULL;
>>> +	struct efi_device_path *tmp_dp;
>>> +	efi_uintn_t ret;
>>> +	efi_uintn_t size;
>>>
>>> -	ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
>>> -	if (ret) {
>>> -		*status = EFI_NO_MEDIA;
>> gg/> +	/*
>>> +	 * if bootmgr is setup with and initrd, the device path will be
>>> +	 * in the FilePathList[] of our load options in Boot####.
>>> +	 * The first device path of the multi instance device path will
>>> +	 * start with a VenMedia and the initrds will follow.
>>> +	 *
>>> +	 * If the device path is not found return EFI_INVALID_PARAMETER.
>>> +	 * We can then use this specific return value and not install the
>>> +	 * protocol, while allowing the boot to continue
>>> +	 */
>>> +	dp = efi_get_dp_from_boot(lf2_initrd_guid);
>>> +	if (!dp) {
>>> +		ret = EFI_INVALID_PARAMETER;
>>>  		goto out;
>>>  	}
>>>
>>> -	ret = fs_size(file, &sz);
>>> -	if (ret) {
>>> -		sz = 0;
>>> -		*status = EFI_NOT_FOUND;
>>> +	tmp_dp = dp;
>>> +	cur = efi_dp_get_next_instance(&tmp_dp, &size);
>>> +	if (!cur) {
>>> +		ret = EFI_OUT_OF_RESOURCES;
>>>  		goto out;
>>>  	}
>>>
>>> +	/*
>>> +	 * We don't care if the file path is eventually NULL here. The
>>> +	 * callers will try to load a file from it and eventually fail
>>> +	 * but let's be explicit with our return values
>>> +	 */
>>> +	if (!tmp_dp) {
>>> +		*initrd_fp = NULL;
>>> +		ret = EFI_NOT_FOUND;
>>> +	} else {
>>> +		*initrd_fp = efi_dp_dup(tmp_dp);
>>> +		if (*initrd_fp)
>>> +			ret = EFI_SUCCESS;
>>> +		else
>>> +			ret = EFI_OUT_OF_RESOURCES;
>>> +	}
>>> +
>>>  out:
>>> -	return sz;
>>> +	efi_free_pool(cur);
>>> +	efi_free_pool(dp);
>>> +	return ret;
>>>  }
>>>
>>>  /**
>>> - * efi_load_file2initrd() - load initial RAM disk
>>> + * efi_load_file2_initrd() - load initial RAM disk
>>>   *
>>>   * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
>>>   * in order to load an initial RAM disk requested by the Linux kernel stub.
>>> @@ -93,98 +125,131 @@ efi_load_file2_initrd(struct efi_load_file_protocol *this,
>>>  		      struct efi_device_path *file_path, bool boot_policy,
>>>  		      efi_uintn_t *buffer_size, void *buffer)
>>>  {
>>> -	char *filespec;
>>> -	efi_status_t status = EFI_NOT_FOUND;
>>> -	loff_t file_sz = 0, read_sz = 0;
>>> -	char *dev, *part, *file;
>>> -	char *pos;
>>> -	int ret;
>>> +	struct efi_device_path *initrd_fp = NULL;
>>> +	struct efi_file_info *info = NULL;
>>> +	efi_status_t ret = EFI_NOT_FOUND;
>>> +	struct efi_file_handle *f;
>>> +	efi_uintn_t bs;
>>>
>>>  	EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
>>>  		  buffer_size, buffer);
>>>
>>> -	filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
>>> -	if (!filespec)
>>> -		goto out;
>>> -	pos = filespec;
>>> -
>>>  	if (!this || this != &efi_lf2_protocol ||
>>>  	    !buffer_size) {
>>> -		status = EFI_INVALID_PARAMETER;
>>> +		ret = EFI_INVALID_PARAMETER;
>>>  		goto out;
>>>  	}
>>>
>>>  	if (file_path->type != dp.end.type ||
>>>  	    file_path->sub_type != dp.end.sub_type) {
>>> -		status = EFI_INVALID_PARAMETER;
>>> +		ret = EFI_INVALID_PARAMETER;
>>>  		goto out;
>>>  	}
>>>
>>>  	if (boot_policy) {
>>> -		status = EFI_UNSUPPORTED;
>>> +		ret = EFI_UNSUPPORTED;
>>>  		goto out;
>>>  	}
>>>
>>> -	/*
>>> -	 * expect a string with three space separated parts:
>>> -	 *
>>> -	 * * a block device type, e.g. "mmc"
>>> -	 * * a device and partition identifier, e.g. "0:1"
>>> -	 * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
>>> -	 */
>>> -	dev = strsep(&pos, " ");
>>> -	if (!dev)
>>> +	ret = get_initrd_fp(&initrd_fp);
>>> +	if (ret != EFI_SUCCESS)
>>>  		goto out;
>>> -	part = strsep(&pos, " ");
>>> -	if (!part)
>>> +
>>> +	/* Open file */
>>> +	f = efi_file_from_path(initrd_fp);
>>> +	if (!f) {
>>> +		ret = EFI_NOT_FOUND;
>>>  		goto out;
>>> -	file = strsep(&pos, " ");
>>> -	if (!file)
>>> +	}
>>> +
>>> +	/* Get file size */
>>> +	bs = 0;
>>> +	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
>>> +				  &bs, info));
>>
>> Please, use
>>
>> 	ret = EFI_CALL(f-> ...);
>>
>> througout the code.
>>
>> In efi_load_image_from_file() and efi_capsule_read_file() we also
>> retrieve the file size. We should factor out a function efi_get_file_size().
>>
>>> +	if (ret != EFI_BUFFER_TOO_SMALL) {
>>> +		ret = EFI_DEVICE_ERROR;
>>>  		goto out;
>>> +	}
>>>
>>> -	file_sz = get_file_size(dev, part, file, &status);
>>> -	if (!file_sz)
>>> +	info = malloc(bs);
>>> +	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs,
>>> +				  info));
>>> +	if (ret != EFI_SUCCESS)
>>>  		goto out;
>>>
>>> -	if (!buffer || *buffer_size < file_sz) {
>>> -		status = EFI_BUFFER_TOO_SMALL;
>>> -		*buffer_size = file_sz;
>>> +	bs = info->file_size;
>>> +	if (!buffer || *buffer_size < bs) {
>>> +		ret = EFI_BUFFER_TOO_SMALL;
>>> +		*buffer_size = bs;
>>>  	} else {
>>> -		ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
>>> -		if (ret) {
>>> -			status = EFI_NO_MEDIA;
>>> -			goto out;
>>> -		}
>>> -
>>> -		ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
>>> -			      &read_sz);
>>> -		if (ret || read_sz != file_sz)
>>> -			goto out;
>>> -		*buffer_size = read_sz;
>>> -
>>> -		status = EFI_SUCCESS;
>>> +		EFI_CALL(ret = f->read(f, &bs, (void *)(uintptr_t)buffer));
>>> +		*buffer_size = bs;
>>>  	}
>>>
>>>  out:
>>> -	free(filespec);
>>> -	return EFI_EXIT(status);
>>> +	free(info);
>>> +	efi_free_pool(initrd_fp);
>>> +	EFI_CALL(f->close(f));
>>> +	return EFI_EXIT(ret);
>>> +}
>>> +
>>> +/**
>>> + * check_initrd() - Determine if the file defined as an initrd in Boot####
>>> + *		    load_options device path is present
>>> + *
>>> + * Return:	status code
>>> + */
>>> +static efi_status_t check_initrd(void)
>>> +{
>>> +	struct efi_device_path *initrd_fp = NULL;
>>> +	struct efi_file_handle *f;
>>> +	efi_status_t ret;
>>> +
>>> +	ret = get_initrd_fp(&initrd_fp);
>>> +	if (ret != EFI_SUCCESS)
>>> +		goto out;
>>> +
>>> +	/*
>>> +	 * If the file is not found, but the file path is set, return an error
>>> +	 * and trigger the bootmgr fallback
>>> +	 */
>>> +	f = efi_file_from_path(initrd_fp);
>>> +	if (!f) {
>>
>> This deserves a log_warning().
>>
>>> +		ret = EFI_NOT_FOUND;
>>> +		goto out;
>>> +	}
>>> +
>>> +	EFI_CALL(f->close(f));
>>> +
>>> +out:
>>> +	efi_free_pool(initrd_fp);
>>> +	return ret;
>>>  }
>>>
>>>  /**
>>>   * efi_initrd_register() - create handle for loading initial RAM disk
>>>   *
>>>   * This function creates a new handle and installs a Linux specific vendor
>>> - * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
>>> + * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
>>>   * to identify the handle and then calls the LoadFile service of the
>>> - * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
>>> + * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
>>>   *
>>>   * Return:	status code
>>>   */
>>>  efi_status_t efi_initrd_register(void)
>>>  {
>>> -	efi_handle_t efi_initrd_handle = NULL;
>>>  	efi_status_t ret;
>>>
>>> +	/*
>>> +	 * Allow the user to continue if Boot#### file path is not set for
>>> +	 * an initrd
>>> +	 */
>>> +	ret = check_initrd();
>>> +	if (ret == EFI_INVALID_PARAMETER)
>>> +		return EFI_SUCCESS;
>>> +	if (ret != EFI_SUCCESS)
>>> +		return ret;
>>> +
>>>  	ret = EFI_CALL(efi_install_multiple_protocol_interfaces
>>>  		       (&efi_initrd_handle,
>>>  			/* initramfs */
>>> @@ -196,3 +261,9 @@ efi_status_t efi_initrd_register(void)
>>>
>>>  	return ret;
>>>  }
>>> +
>>> +void efi_initrd_deregister(void)
>>
>> Missing description of an exported function.
>>
>> Best regards
>>
>> Heinrich
>>
>>> +{
>>> +	efi_delete_handle(efi_initrd_handle);
>>> +	efi_initrd_handle = NULL;
>>> +}
>>>
>>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11 11:44           ` Heinrich Schuchardt
  2021-03-11 12:31             ` Heinrich Schuchardt
@ 2021-03-11 13:31             ` Ilias Apalodimas
  2021-03-11 20:25               ` Heinrich Schuchardt
  1 sibling, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-11 13:31 UTC (permalink / raw)
  To: u-boot

> I am not referring to efidebug.
> 
> The user can update EFI variables with any binary content using an EFI binary or OS functions.
> 
> E.g. copy a binary file to the efi variables file system in Linux.

Right, I was mostly thinking SetVariable which still goes through u-boot. 
Copying an entire file is possiible though I guess

Cheers
/Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11 13:31             ` Ilias Apalodimas
@ 2021-03-11 20:25               ` Heinrich Schuchardt
  0 siblings, 0 replies; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-11 20:25 UTC (permalink / raw)
  To: u-boot

On 3/11/21 2:31 PM, Ilias Apalodimas wrote:
>> I am not referring to efidebug.
>>
>> The user can update EFI variables with any binary content using an EFI binary or OS functions.
>>
>> E.g. copy a binary file to the efi variables file system in Linux.
>
> Right, I was mostly thinking SetVariable which still goes through u-boot.
> Copying an entire file is possiible though I guess

dd if=foo
of=/sys/firmware/efi/efivars/Boot0123-8be4df61-93ca-11d2-aa0d-00e098032b8c

But Linux makes some consistency checks.

Best regards

Heinrich

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-11 11:36         ` Ilias Apalodimas
  2021-03-11 11:44           ` Heinrich Schuchardt
@ 2021-03-12  2:50           ` AKASHI Takahiro
  2021-03-12  4:10             ` Ilias Apalodimas
  1 sibling, 1 reply; 41+ messages in thread
From: AKASHI Takahiro @ 2021-03-12  2:50 UTC (permalink / raw)
  To: u-boot

Ilias,

I may have missed your past discussions, but any way,

On Thu, Mar 11, 2021 at 01:36:04PM +0200, Ilias Apalodimas wrote:
> Hi Heinrich 
> 
> [...]
> 
> > >>> + * @load_option: device paths to search
> > >>> + * @size:	 size of the discovered device path
> > >>> + * @guid:	 guid to search for
> > >>> + *
> > >>> + * Return:	device path or NULL. Caller must free the returned value
> > >>
> > >> Please, keep the text aligned.
> > >>
> > >> Do we need a copy? Isn't a pointer good enough?
> > >
> > > A pointer to what?
> > > I think it's better to a copy here. This is a device path that might be used
> > > out of a stack context were the load option might disappear.
> > > Look at how the function is used in efi_get_dp_from_boot().
> > 
> > You are duplicating in get_initrd_fp(). Why should we duplicate twice?
> > 
> 
> That's irrelevant though isn't it?
> I did that in the efi initrd implementation. If someone else does the DTB in
> the future and device to use efi_dp_from_lo return directly?
> I'd much prefer an API (since that function goes into an API-related file for
> device paths), that's safe and requires the user to free the memory, rather
> than allowing him to accidentally shoot himself in the foot, keeping in mind
> it's a single copy on a device path, which roughly adds anything on our boot
> time.
> 
> > >
> > >>
> > >>> + */
> > >>> +struct
> > >>> +efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
> > >>> +				efi_uintn_t *size, efi_guid_t guid)
> > >>> +{
> > >>> +	struct efi_device_path *fp = lo->file_path;
> > >>> +	struct efi_device_path_vendor *vendor;
> > >>> +	int lo_len = lo->file_path_length;
> > >>> +
> > >>> +	while (lo_len) {
> > >>
> > >> lo_len must be at least sizeof(struct efi_device_path).
> > >>
> > >>> +		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
> > >>> +		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) {
> > >>> +			lo_len -= fp->length;
> > >>
> > >> Could the last device path in the array be followed by zero bytes for
> > >> padding?
> > >
> > > How? Device paths are packed aren't they ?
> > >
> > >> Should we check that fp->length >= sizeof(struct efi_device_path)?
> > >
> > > Yea probably a good idea
> > 
> > The content of the boot option comes from the user. Just assume that it
> > can contain malicious content.
> > 
> 
> Yea the user doesn't add the device path directly though. The user adds
> directories and a file, so the normalization is part of this function, 
> applied randomly and locally on a single input? or the device path creation 
> functions which this code uses? Since we use the pattern in a bunch of places
> I assumed we did take care of that during the functions that create the device
> paths. I haven't checked though ...
> 
> > We should also check that the identified device-path starting at
> > VenMedia() ends within fp->length using efi_dp_check_length().
> 
> ok
> 
> > 
> > >
> > >>
> > >>> +			fp = (void *)fp + fp->length;
> > >>
> > >> Please, avoid code duplication.
> > >>
> > >> E.g.
> > >>
> > >> for (; lo_len >= sizeof(struct efi_device_path);
> > >>      lo_len -= fp->length, fp = (void *)fp + fp->length) {
> > >
> > > I can an switch to that, but I really never liked this format.
> > > It always seemed way less readable to me for some reason.  Maybe because I
> > > never got used to it ...
> > 
> > Using "for" is only one option. You could use "goto next;" instead.
> > 
> 
> I really don't mind, I can just use what you propose.
> 
> > >
> > >>
> > >>> +			continue;
> > >>> +		}
> > >>> +
> > >>> +		vendor = (struct efi_device_path_vendor *)fp;
> > >>> +		if (!guidcmp(&vendor->guid, &guid))
> > >>> +			return efi_dp_dup(fp);
> > >>
> > >> Should we strip of the VenMedia() node here?
> > >
> > > Why? This is not supposed to get the file path. The function says "get device
> > > path from load option" and that device includes the VenMedia node.
> > > It would make more sense for me to strip in efi_get_dp_from_boot() for
> > > example, if you want a helper function to get the initrd path *only*.
> > 
> > The VenMedia() node is not needed anymore once you have found the entry.
> > 
> 
> Yea it's not but as I said the name of the function says "get the *stored*
> from a boot option. Not get the one that suits us.  
> There's another reason for that btw, the initrd related functions use that 
> (specifically get_initrd_fp()), to figure out if the Boot#### variable
> contains an initrd path or not.
> If the VenMedia is not present at all, the protocol is not installed allowing
> the kernel to fallback in it's command line 'initrd=' option.
> If the VenMedia is there though, we are checking the file path of the initrd
> and if the file's not found we return an error allowing Bootmgr to fallback.
> 
> If we 'just' return the initrd path, we'll have to introduce another variable
> in the function, indicating if the VenMedia is present or not so the rest
> ofthe codepath can decide what to do.
> 
> > >
> > > But really it's just one invocation of efi_dp_get_next_instance() after
> > > whatever device path you get. Which also modifies the device path pointer, so
> > > I'd really prefer keeping that call in a local context.
> > 
> > Why next instance? I thought next node.
> > 
> > My understanding is that we have:
> > 
> > kernel path,end(0xff),
> > VenMedia(), /* no end node here */
> > initrd1, end(0x01),
> > initrd2, end(0xff)
> 
> No, the structure is added in cmd/efidebug.c code.
> It's created with efi_dp_append_instance() on 
>  - const struct efi_initrd_dp id_dp
>  - file path of initrd
>  
>  which will create:
>  kernel path,end(0xff),
>  VenMedia(), end(0x01),
>  initrd1, end(0x01),
>  initrd2, end(0xff)

What is the difference between end(0xff) and end(0x01)?

If the first argument of a load option is a list of device paths,
I would expect the format would look like:
  kernel path,end(0xff),
  VenMedia(INITRD),initrd1 path,end(0xff),
  VenMedia(INITRD),initrd2 path,end(0xff),

so that VenMedia can work as an identify of the succeeding path.
Is it simple enough, isn't it?

-Takahiro Akashi

> I know I originally proposed the one you have, but it seemed cleaner adding
> an extra instance between VenMedia and the first initrd.
> 
> > 
> > Please, document the structure.
> > 
> 
> Sure
> 
> > Best regards
> > 
> > Heinrich
> 
> Thanks
> /Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-12  2:50           ` AKASHI Takahiro
@ 2021-03-12  4:10             ` Ilias Apalodimas
  2021-03-12  4:32               ` AKASHI Takahiro
  0 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-12  4:10 UTC (permalink / raw)
  To: u-boot

Akashi-san, 

On Fri, Mar 12, 2021 at 11:50:32AM +0900, AKASHI Takahiro wrote:
> Ilias,
> 
> I may have missed your past discussions, but any way,

It's on the boot-architecture mailing list [1] 

> 
> On Thu, Mar 11, 2021 at 01:36:04PM +0200, Ilias Apalodimas wrote:
> > > 

[...]

> > > My understanding is that we have:
> > > 
> > > kernel path,end(0xff),
> > > VenMedia(), /* no end node here */
> > > initrd1, end(0x01),
> > > initrd2, end(0xff)
> > 
> > No, the structure is added in cmd/efidebug.c code.
> > It's created with efi_dp_append_instance() on 
> >  - const struct efi_initrd_dp id_dp
> >  - file path of initrd
> >  
> >  which will create:
> >  kernel path,end(0xff),
> >  VenMedia(), end(0x01),
> >  initrd1, end(0x01),
> >  initrd2, end(0xff)
> 
> What is the difference between end(0xff) and end(0x01)?
> 

0xff is a subtype of 'end the entire device path', while 0x01 is an 'end of
instance of a device path and start a new device path'

> If the first argument of a load option is a list of device paths,
> I would expect the format would look like:
>   kernel path,end(0xff),
>   VenMedia(INITRD),initrd1 path,end(0xff),
>   VenMedia(INITRD),initrd2 path,end(0xff),
> 
> so that VenMedia can work as an identify of the succeeding path.
> Is it simple enough, isn't it?

It's essentially the same thing. It has an effect on the EFI spec and how you
interpret it, but honestly it feels as an implementation detail to me, since
none of those are standardized anyway.

In fact what you are saying was part of my proposal in the original mail
(check proposal 1.)

Anyway the difference between the two is that what I coded looks like this:
FilePathList[0] -> kernel
FilePathList[1] -> initrd1 - initrdn

while whe other is
FilePathList[0] -> kernel
FilePathList[1] -> initrd1 
FilePathList[2] -> initrd2
FilePathList[n] -> initrdn

If we ever manage to wire in the DTBs in there as well it may look like:

FilePathList[0] -> kernel
FilePathList[1] -> initrd1 - initrdn
FilePathList[2] -> dtb1 - dtbn

Vs

FilePathList[0] -> kernel
FilePathList[1] -> initrd1 
FilePathList[2] -> initrd2
FilePathList[3] -> dtb1
FilePathList[n] -> initrdn
FilePathList[n+1] -> dtb2


> 
> -Takahiro Akashi
> 
> > I know I originally proposed the one you have, but it seemed cleaner adding
> > an extra instance between VenMedia and the first initrd.
> > 
> > > 
> > > Please, document the structure.
> > > 
> > 
> > Sure
> > 
> > > Best regards
> > > 
> > > Heinrich
> > 
> > Thanks
> > /Ilias

[1] https://lists.linaro.org/pipermail/boot-architecture/2021-February/001686.html

Cheers
/Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-12  4:10             ` Ilias Apalodimas
@ 2021-03-12  4:32               ` AKASHI Takahiro
  2021-03-12  4:42                 ` Ilias Apalodimas
  0 siblings, 1 reply; 41+ messages in thread
From: AKASHI Takahiro @ 2021-03-12  4:32 UTC (permalink / raw)
  To: u-boot

On Fri, Mar 12, 2021 at 06:10:29AM +0200, Ilias Apalodimas wrote:
> Akashi-san, 
> 
> On Fri, Mar 12, 2021 at 11:50:32AM +0900, AKASHI Takahiro wrote:
> > Ilias,
> > 
> > I may have missed your past discussions, but any way,
> 
> It's on the boot-architecture mailing list [1] 
> 
> > 
> > On Thu, Mar 11, 2021 at 01:36:04PM +0200, Ilias Apalodimas wrote:
> > > > 
> 
> [...]
> 
> > > > My understanding is that we have:
> > > > 
> > > > kernel path,end(0xff),
> > > > VenMedia(), /* no end node here */
> > > > initrd1, end(0x01),
> > > > initrd2, end(0xff)
> > > 
> > > No, the structure is added in cmd/efidebug.c code.
> > > It's created with efi_dp_append_instance() on 
> > >  - const struct efi_initrd_dp id_dp
> > >  - file path of initrd
> > >  
> > >  which will create:
> > >  kernel path,end(0xff),
> > >  VenMedia(), end(0x01),
> > >  initrd1, end(0x01),
> > >  initrd2, end(0xff)
> > 
> > What is the difference between end(0xff) and end(0x01)?
> > 
> 
> 0xff is a subtype of 'end the entire device path', while 0x01 is an 'end of
> instance of a device path and start a new device path'
> 
> > If the first argument of a load option is a list of device paths,
> > I would expect the format would look like:
> >   kernel path,end(0xff),
> >   VenMedia(INITRD),initrd1 path,end(0xff),
> >   VenMedia(INITRD),initrd2 path,end(0xff),
> > 
> > so that VenMedia can work as an identify of the succeeding path.
> > Is it simple enough, isn't it?
> 
> It's essentially the same thing. It has an effect on the EFI spec and how you
> interpret it, but honestly it feels as an implementation detail to me, since
> none of those are standardized anyway.
> 
> In fact what you are saying was part of my proposal in the original mail
> (check proposal 1.)
> 
> Anyway the difference between the two is that what I coded looks like this:
> FilePathList[0] -> kernel
> FilePathList[1] -> initrd1 - initrdn
> 
> while whe other is
> FilePathList[0] -> kernel
> FilePathList[1] -> initrd1 
> FilePathList[2] -> initrd2
> FilePathList[n] -> initrdn
> 
> If we ever manage to wire in the DTBs in there as well it may look like:
> 
> FilePathList[0] -> kernel
> FilePathList[1] -> initrd1 - initrdn
> FilePathList[2] -> dtb1 - dtbn
> 
> Vs
> 
> FilePathList[0] -> kernel
> FilePathList[1] -> initrd1 
> FilePathList[2] -> initrd2
> FilePathList[3] -> dtb1
> FilePathList[n] -> initrdn
> FilePathList[n+1] -> dtb2

What is the semantics?
Which do you want to do?
a) boot one of combinations:
     1.kernel+initrd1+dtb1, or
     2.kernel+initrd2+dtb2
b) boot
     kernel + (initrd1 + initrd2) + (dtb1 + dtb2)

I assume you meant (a).
In that case, how can you specify (a-1) or (a-2) at boot time?

Is there any clear description about that?
(I"m simply asking here.)

-Takahiro Akashi

> 
> 
> > 
> > -Takahiro Akashi
> > 
> > > I know I originally proposed the one you have, but it seemed cleaner adding
> > > an extra instance between VenMedia and the first initrd.
> > > 
> > > > 
> > > > Please, document the structure.
> > > > 
> > > 
> > > Sure
> > > 
> > > > Best regards
> > > > 
> > > > Heinrich
> > > 
> > > Thanks
> > > /Ilias
> 
> [1] https://lists.linaro.org/pipermail/boot-architecture/2021-February/001686.html
> 
> Cheers
> /Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-12  4:32               ` AKASHI Takahiro
@ 2021-03-12  4:42                 ` Ilias Apalodimas
  2021-03-12  5:02                   ` AKASHI Takahiro
  0 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-12  4:42 UTC (permalink / raw)
  To: u-boot

On Fri, Mar 12, 2021 at 01:32:50PM +0900, AKASHI Takahiro wrote:
[...]
> > > > > My understanding is that we have:
> > > > > 
> > > > > kernel path,end(0xff),
> > > > > VenMedia(), /* no end node here */
> > > > > initrd1, end(0x01),
> > > > > initrd2, end(0xff)
> > > > 
> > > > No, the structure is added in cmd/efidebug.c code.
> > > > It's created with efi_dp_append_instance() on 
> > > >  - const struct efi_initrd_dp id_dp
> > > >  - file path of initrd
> > > >  
> > > >  which will create:
> > > >  kernel path,end(0xff),
> > > >  VenMedia(), end(0x01),
> > > >  initrd1, end(0x01),
> > > >  initrd2, end(0xff)
> > > 
> > > What is the difference between end(0xff) and end(0x01)?
> > > 
> > 
> > 0xff is a subtype of 'end the entire device path', while 0x01 is an 'end of
> > instance of a device path and start a new device path'
> > 
> > > If the first argument of a load option is a list of device paths,
> > > I would expect the format would look like:
> > >   kernel path,end(0xff),
> > >   VenMedia(INITRD),initrd1 path,end(0xff),
> > >   VenMedia(INITRD),initrd2 path,end(0xff),
> > > 
> > > so that VenMedia can work as an identify of the succeeding path.
> > > Is it simple enough, isn't it?
> > 
> > It's essentially the same thing. It has an effect on the EFI spec and how you
> > interpret it, but honestly it feels as an implementation detail to me, since
> > none of those are standardized anyway.
> > 
> > In fact what you are saying was part of my proposal in the original mail
> > (check proposal 1.)
> > 
> > Anyway the difference between the two is that what I coded looks like this:
> > FilePathList[0] -> kernel
> > FilePathList[1] -> initrd1 - initrdn
> > 
> > while whe other is
> > FilePathList[0] -> kernel
> > FilePathList[1] -> initrd1 
> > FilePathList[2] -> initrd2
> > FilePathList[n] -> initrdn
> > 
> > If we ever manage to wire in the DTBs in there as well it may look like:
> > 
> > FilePathList[0] -> kernel
> > FilePathList[1] -> initrd1 - initrdn
> > FilePathList[2] -> dtb1 - dtbn
> > 
> > Vs
> > 
> > FilePathList[0] -> kernel
> > FilePathList[1] -> initrd1 
> > FilePathList[2] -> initrd2
> > FilePathList[3] -> dtb1
> > FilePathList[n] -> initrdn
> > FilePathList[n+1] -> dtb2
> 
> What is the semantics?
> Which do you want to do?
> a) boot one of combinations:
>      1.kernel+initrd1+dtb1, or
>      2.kernel+initrd2+dtb2
> b) boot
>      kernel + (initrd1 + initrd2) + (dtb1 + dtb2)
> 
> I assume you meant (a).
> In that case, how can you specify (a-1) or (a-2) at boot time?
> 
> Is there any clear description about that?
> (I"m simply asking here.)

it's b) 
if you want different combinations of kernel/initrds (as described in a) you
can add another Boot#### variable.

So let's assume you got three boot options
Boot0000, Boot0001 and Boot0002

Boot0000 efi_load_options: kernel1 + (initrd1 + initrd2) + (dtb1 + dtb2). 
The bootloader will concat initrd1+initrd2 when the kernel requests it.
Similar behavior can be coded for dtb before installing the table.

Boot0001: kernel1 + initrd1 + dtb1
Load a kernel with a single initrd and dtb.

Boot0002: kernel2 + initrd2 + dtb2
ditto.

Hope that's clear now

Cheers
/Ilias


> 
> -Takahiro Akashi
> 
> > 
> > 
> > > 
> > > -Takahiro Akashi
> > > 
> > > > I know I originally proposed the one you have, but it seemed cleaner adding
> > > > an extra instance between VenMedia and the first initrd.
> > > > 
> > > > > 
> > > > > Please, document the structure.
> > > > > 
> > > > 
> > > > Sure
> > > > 
> > > > > Best regards
> > > > > 
> > > > > Heinrich
> > > > 
> > > > Thanks
> > > > /Ilias
> > 
> > [1] https://lists.linaro.org/pipermail/boot-architecture/2021-February/001686.html
> > 
> > Cheers
> > /Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 5/6] efidebug: add multiple device path instances on Boot####
  2021-03-05 22:23 ` [PATCH 5/6] efidebug: add multiple device path instances on Boot#### Ilias Apalodimas
  2021-03-11 12:38   ` Heinrich Schuchardt
@ 2021-03-12  4:44   ` AKASHI Takahiro
  2021-03-12  4:55     ` Ilias Apalodimas
  2021-03-12 16:25     ` Heinrich Schuchardt
  1 sibling, 2 replies; 41+ messages in thread
From: AKASHI Takahiro @ 2021-03-12  4:44 UTC (permalink / raw)
  To: u-boot

Ilias,

Sorry, but I will have to repeat my question for better understandings.

On Sat, Mar 06, 2021 at 12:23:01AM +0200, Ilias Apalodimas wrote:
> The UEFI spec allow a packed array of UEFI device paths in the
> FilePathList[] of an EFI_LOAD_OPTION. The first file path must
> describe the loaded image but the rest are OS specific.
> 
> Previous patches parse the device path and try to use the second
> member of the array as an initrd. So let's modify efidebug slightly
> and install the second file described in the command line as the
> initrd device path.
> 
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> ---
>  cmd/efidebug.c                                | 193 ++++++++++++++----
>  doc/board/emulation/qemu_capsule_update.rst   |   4 +-
>  doc/uefi/uefi.rst                             |   2 +-
>  .../test_efi_capsule/test_capsule_firmware.py |   6 +-
>  test/py/tests/test_efi_secboot/test_signed.py |  16 +-
>  .../test_efi_secboot/test_signed_intca.py     |   8 +-
>  .../tests/test_efi_secboot/test_unsigned.py   |   8 +-
>  7 files changed, 179 insertions(+), 58 deletions(-)
> 
> diff --git a/cmd/efidebug.c b/cmd/efidebug.c
> index bbbcb0a54643..9a1c471a3eaa 100644
> --- a/cmd/efidebug.c
> +++ b/cmd/efidebug.c
> @@ -9,6 +9,8 @@
>  #include <common.h>
>  #include <command.h>
>  #include <efi_dt_fixup.h>
> +#include <efi_helper.h>
> +#include <efi_load_initrd.h>
>  #include <efi_loader.h>
>  #include <efi_rng.h>
>  #include <exports.h>
> @@ -19,6 +21,7 @@
>  #include <part.h>
>  #include <search.h>
>  #include <linux/ctype.h>
> +#include <linux/err.h>
>  
>  #define BS systab.boottime
>  #define RT systab.runtime
> @@ -794,6 +797,65 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
>  	return CMD_RET_SUCCESS;
>  }
>  
> +/**
> + * add_initrd_instance() - Append a device path to load_options pointing to an
> + *			   inirtd
> + *
> + * @argc:	Number of arguments
> + * @argv:	Argument array
> + * @file_path	Existing device path, the new instance will be appended
> + * Return:	Pointer to the device path or ERR_PTR
> + *
> + */
> +static
> +struct efi_device_path *add_initrd_instance(const char *dev, const char *part,
> +					    const char *file,
> +					    const struct efi_device_path *fp,
> +					    efi_uintn_t *fp_size)
> +{
> +	struct efi_device_path *tmp_dp = NULL, *tmp_fp = NULL;
> +	struct efi_device_path *final_fp = NULL, *initrd_dp = NULL;
> +	efi_status_t ret;
> +	const struct efi_initrd_dp id_dp = {
> +		.vendor = {
> +			{
> +			DEVICE_PATH_TYPE_MEDIA_DEVICE,
> +			DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
> +			sizeof(id_dp.vendor),
> +			},
> +			EFI_INITRD_MEDIA_GUID,
> +		},
> +		.end = {
> +			DEVICE_PATH_TYPE_END,
> +			DEVICE_PATH_SUB_TYPE_END,
> +			sizeof(id_dp.end),
> +		}
> +	};
> +
> +	ret = efi_dp_from_name(dev, part, file, &tmp_dp, &tmp_fp);
> +	if (ret != EFI_SUCCESS) {
> +		printf("Cannot create device path for \"%s %s\"\n", part, file);
> +		goto out;
> +	}
> +
> +	initrd_dp =
> +		efi_dp_append_instance((const struct efi_device_path *)&id_dp,
> +				       tmp_fp);
> +	if (!initrd_dp) {
> +		printf("Cannot append media vendor device path path\n");
> +		goto out;
> +	}
> +	final_fp = efi_dp_concat(fp, initrd_dp);
> +	*fp_size = efi_dp_size(fp) + efi_dp_size(initrd_dp) +
> +		(2 * sizeof(struct efi_device_path));
> +
> +out:
> +	efi_free_pool(initrd_dp);
> +	efi_free_pool(tmp_dp);
> +	efi_free_pool(tmp_fp);
> +	return final_fp ? final_fp : ERR_PTR(-EINVAL);
> +}
> +
>  /**
>   * do_efi_boot_add() - set UEFI load option
>   *
> @@ -806,7 +868,9 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
>   *
>   * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
>   *
> - *     efidebug boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
> + * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
> + *                   -i <file> <interface2> <devnum2>[:<part>] <initrd>
> + *                   -s '<options>'

We discussed another syntax:
efidebug boot add <id> ...
efidebug boot add-initrd <id> <initrd path>
(Please don't care detailed syntax for now.)

What is the difficulty that you have had to implement this type of
interface?

Even if we follow your new syntax,
Why do we need '-b' option?
"<id> <label> <interface> <devnum>[:<part>] <file>" are all mandatory arguments,
aren't they?

-Takahiro Akashi



>   */
>  static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
>  			   int argc, char *const argv[])
> @@ -819,55 +883,98 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
>  	size_t label_len, label_len16;
>  	u16 *label;
>  	struct efi_device_path *device_path = NULL, *file_path = NULL;
> +	struct efi_device_path *final_fp = NULL;
>  	struct efi_load_option lo;
>  	void *data = NULL;
>  	efi_uintn_t size;
> +	efi_uintn_t fp_size;
>  	efi_status_t ret;
>  	int r = CMD_RET_SUCCESS;
> -
> -	if (argc < 6 || argc > 7)
> -		return CMD_RET_USAGE;
> -
> -	id = (int)simple_strtoul(argv[1], &endp, 16);
> -	if (*endp != '\0' || id > 0xffff)
> -		return CMD_RET_USAGE;
> -
> -	sprintf(var_name, "Boot%04X", id);
> -	p = var_name16;
> -	utf8_utf16_strncpy(&p, var_name, 9);
> +	int i;
>  
>  	guid = efi_global_variable_guid;
>  
>  	/* attributes */
>  	lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */
> +	lo.optional_data = NULL;
> +	lo.label = NULL;
>  
> -	/* label */
> -	label_len = strlen(argv[2]);
> -	label_len16 = utf8_utf16_strnlen(argv[2], label_len);
> -	label = malloc((label_len16 + 1) * sizeof(u16));
> -	if (!label)
> -		return CMD_RET_FAILURE;
> -	lo.label = label; /* label will be changed below */
> -	utf8_utf16_strncpy(&label, argv[2], label_len);
> +	/* search for -b first since the rest of the arguments depends on that */
> +	for (i = 0; i < argc; i++) {
> +		if (!strcmp(argv[i], "-b")) {
> +			if (argc < i + 5 || lo.label) {
> +				r = CMD_RET_USAGE;
> +				goto out;
> +			}
> +			id = (int)simple_strtoul(argv[i + 1], &endp, 16);
> +			if (*endp != '\0' || id > 0xffff)
> +				return CMD_RET_USAGE;
> +
> +			sprintf(var_name, "Boot%04X", id);
> +			p = var_name16;
> +			utf8_utf16_strncpy(&p, var_name, 9);
> +
> +			/* label */
> +			label_len = strlen(argv[i + 2]);
> +			label_len16 = utf8_utf16_strnlen(argv[i + 2], label_len);
> +			label = malloc((label_len16 + 1) * sizeof(u16));
> +			if (!label)
> +				return CMD_RET_FAILURE;
> +			lo.label = label; /* label will be changed below */
> +			utf8_utf16_strncpy(&label, argv[i + 2], label_len);
> +
> +			/* file path */
> +			ret = efi_dp_from_name(argv[i + 3], argv[i + 4],
> +					       argv[i + 5], &device_path,
> +					       &file_path);
> +			if (ret != EFI_SUCCESS) {
> +				printf("Cannot create device path for \"%s %s\"\n",
> +				       argv[3], argv[4]);
> +				r = CMD_RET_FAILURE;
> +				goto out;
> +			break;
> +			}
> +			fp_size = efi_dp_size(file_path) +
> +				sizeof(struct efi_device_path);
> +		}
> +	}
>  
> -	/* file path */
> -	ret = efi_dp_from_name(argv[3], argv[4], argv[5], &device_path,
> -			       &file_path);
> -	if (ret != EFI_SUCCESS) {
> -		printf("Cannot create device path for \"%s %s\"\n",
> -		       argv[3], argv[4]);
> +	if (!file_path) {
> +		printf("You need to specify an image before an initrd.\n");
>  		r = CMD_RET_FAILURE;
>  		goto out;
>  	}
> -	lo.file_path = file_path;
> -	lo.file_path_length = efi_dp_size(file_path)
> -				+ sizeof(struct efi_device_path); /* for END */
>  
> -	/* optional data */
> -	if (argc == 6)
> -		lo.optional_data = NULL;
> -	else
> -		lo.optional_data = (const u8 *)argv[6];
> +	/* add now add initrd and extra data */
> +	for (i = 0; i < argc; i++) {
> +		if (!strcmp(argv[i], "-i")) {
> +			if (argc < i + 3 || final_fp) {
> +				r = CMD_RET_USAGE;
> +				goto out;
> +			}
> +
> +			final_fp = add_initrd_instance(argv[i + 1], argv[i + 2],
> +						       argv[i + 3], file_path,
> +						       &fp_size);
> +			if (IS_ERR(final_fp)) {
> +				r = CMD_RET_FAILURE;
> +				goto out;
> +			}
> +
> +			/* add_initrd_instance allocates a new device path */
> +			efi_free_pool(file_path);
> +			file_path = final_fp;
> +		} else if (!strcmp(argv[i], "-s")) {
> +			if (argc < i + 1 || lo.optional_data) {
> +				r = CMD_RET_USAGE;
> +				goto out;
> +			}
> +			lo.optional_data = (const u8 *)argv[i + 1];
> +		}
> +	}
> +
> +	lo.file_path = file_path;
> +	lo.file_path_length = fp_size;
>  
>  	size = efi_serialize_load_option(&lo, (u8 **)&data);
>  	if (!size) {
> @@ -951,11 +1058,14 @@ static int do_efi_boot_rm(struct cmd_tbl *cmdtp, int flag,
>   */
>  static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
>  {
> +	struct efi_device_path *initrd_path = NULL;
>  	struct efi_load_option lo;
>  	char *label, *p;
>  	size_t label_len16, label_len;
>  	u16 *dp_str;
>  	efi_status_t ret;
> +	efi_uintn_t initrd_dp_size;
> +	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
>  
>  	ret = efi_deserialize_load_option(&lo, data, size);
>  	if (ret != EFI_SUCCESS) {
> @@ -986,6 +1096,14 @@ static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
>  	printf("  file_path: %ls\n", dp_str);
>  	efi_free_pool(dp_str);
>  
> +	initrd_path = efi_dp_from_lo(&lo, &initrd_dp_size, lf2_initrd_guid);
> +	if (initrd_path) {
> +		dp_str = efi_dp_str(initrd_path);
> +		printf("  initrd_path: %ls\n", dp_str);
> +		efi_free_pool(dp_str);
> +		efi_free_pool(initrd_path);
> +	}
> +
>  	printf("  data:\n");
>  	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
>  		       lo.optional_data, *size, true);
> @@ -1555,7 +1673,10 @@ static int do_efidebug(struct cmd_tbl *cmdtp, int flag,
>  static char efidebug_help_text[] =
>  	"  - UEFI Shell-like interface to configure UEFI environment\n"
>  	"\n"
> -	"efidebug boot add <bootid> <label> <interface> <devnum>[:<part>] <file path> [<load options>]\n"
> +	"efidebug boot add "
> +	"-b <bootid> <label> <interface> <devnum>[:<part>] <file path> "
> +	"-i <interface> <devnum>[:<part>] <initrd file path> "
> +	"-s '<optional data>'\n"
>  	"  - set UEFI BootXXXX variable\n"
>  	"    <load options> will be passed to UEFI application\n"
>  	"efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n"
> @@ -1599,7 +1720,7 @@ static char efidebug_help_text[] =
>  #endif
>  
>  U_BOOT_CMD(
> -	efidebug, 10, 0, do_efidebug,
> +	efidebug, CONFIG_SYS_MAXARGS, 0, do_efidebug,
>  	"Configure UEFI environment",
>  	efidebug_help_text
>  );
> diff --git a/doc/board/emulation/qemu_capsule_update.rst b/doc/board/emulation/qemu_capsule_update.rst
> index 9fec75f8f1c9..33ce4bcd32ea 100644
> --- a/doc/board/emulation/qemu_capsule_update.rst
> +++ b/doc/board/emulation/qemu_capsule_update.rst
> @@ -60,7 +60,7 @@ to be pointing to the EFI System Partition which contains the capsule
>  file. The BootNext, BootXXXX and OsIndications variables can be set
>  using the following commands::
>  
> -    => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
> +    => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
>      => efidebug boot next 0
>      => setenv -e -nv -bs -rt -v OsIndications =0x04
>      => saveenv
> @@ -198,7 +198,7 @@ command line::
>      3. Set the following environment and UEFI boot variables
>  
>          => setenv -e -nv -bs -rt -v OsIndications =0x04
> -        => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
> +        => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
>          => efidebug boot next 0
>          => saveenv
>  
> diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst
> index 5a67737c1579..b3494c22e073 100644
> --- a/doc/uefi/uefi.rst
> +++ b/doc/uefi/uefi.rst
> @@ -178,7 +178,7 @@ Now in U-Boot install the keys on your board::
>  
>  Set up boot parameters on your board::
>  
> -    efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed ""
> +    efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed ""
>  
>  Now your board can run the signed image via the boot manager (see below).
>  You can also try this sequence by running Pytest, test_efi_secboot,
> diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> index f006fa95d650..e8b0a1575453 100644
> --- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> @@ -39,7 +39,7 @@ class TestEfiCapsuleFirmwareFit(object):
>          with u_boot_console.log.section('Test Case 1-a, before reboot'):
>              output = u_boot_console.run_command_list([
>                  'host bind 0 %s' % disk_img,
> -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
>                  'efidebug boot order 1',
>                  'env set -e OsIndications',
>                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> @@ -114,7 +114,7 @@ class TestEfiCapsuleFirmwareFit(object):
>          with u_boot_console.log.section('Test Case 2-a, before reboot'):
>              output = u_boot_console.run_command_list([
>                  'host bind 0 %s' % disk_img,
> -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
>                  'efidebug boot order 1',
>                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
>                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> @@ -188,7 +188,7 @@ class TestEfiCapsuleFirmwareFit(object):
>          with u_boot_console.log.section('Test Case 3-a, before reboot'):
>              output = u_boot_console.run_command_list([
>                  'host bind 0 %s' % disk_img,
> -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
>                  'efidebug boot order 1',
>                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
>                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py
> index 863685e215b7..75f5ea772300 100644
> --- a/test/py/tests/test_efi_secboot/test_signed.py
> +++ b/test/py/tests/test_efi_secboot/test_signed.py
> @@ -28,7 +28,7 @@ class TestEfiSignedImage(object):
>              # Test Case 1a, run signed image if no PK
>              output = u_boot_console.run_command_list([
>                  'host bind 0 %s' % disk_img,
> -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> @@ -36,7 +36,7 @@ class TestEfiSignedImage(object):
>          with u_boot_console.log.section('Test Case 1b'):
>              # Test Case 1b, run unsigned image if no PK
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 2',
>                  'bootefi bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> @@ -58,13 +58,13 @@ class TestEfiSignedImage(object):
>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>              assert 'Failed to set EFI variable' not in ''.join(output)
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert('\'HELLO1\' failed' in ''.join(output))
>              assert('efi_start_image() returned: 26' in ''.join(output))
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 2',
>                  'efidebug test bootmgr'])
>              assert '\'HELLO2\' failed' in ''.join(output)
> @@ -104,7 +104,7 @@ class TestEfiSignedImage(object):
>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>              assert 'Failed to set EFI variable' not in ''.join(output)
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert '\'HELLO\' failed' in ''.join(output)
> @@ -142,7 +142,7 @@ class TestEfiSignedImage(object):
>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>              assert 'Failed to set EFI variable' not in ''.join(output)
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert '\'HELLO\' failed' in ''.join(output)
> @@ -169,7 +169,7 @@ class TestEfiSignedImage(object):
>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>              assert 'Failed to set EFI variable' not in ''.join(output)
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> @@ -227,7 +227,7 @@ class TestEfiSignedImage(object):
>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>              assert 'Failed to set EFI variable' not in ''.join(output)
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py
> index 70d6be00e8a8..0849572a5143 100644
> --- a/test/py/tests/test_efi_secboot/test_signed_intca.py
> +++ b/test/py/tests/test_efi_secboot/test_signed_intca.py
> @@ -39,7 +39,7 @@ class TestEfiSignedImageIntca(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>  
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
> +                'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert '\'HELLO_a\' failed' in ''.join(output)
> @@ -48,7 +48,7 @@ class TestEfiSignedImageIntca(object):
>          with u_boot_console.log.section('Test Case 1b'):
>              # Test Case 1b, signed and authenticated by root CA
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
> +                'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
>                  'efidebug boot next 2',
>                  'bootefi bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> @@ -70,7 +70,7 @@ class TestEfiSignedImageIntca(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>  
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert '\'HELLO_abc\' failed' in ''.join(output)
> @@ -116,7 +116,7 @@ class TestEfiSignedImageIntca(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>  
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
>                  'efidebug boot next 1',
>                  'efidebug test bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py
> index 56f56e19eb84..8e026f7566ad 100644
> --- a/test/py/tests/test_efi_secboot/test_unsigned.py
> +++ b/test/py/tests/test_efi_secboot/test_unsigned.py
> @@ -35,7 +35,7 @@ class TestEfiUnsignedImage(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>  
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert '\'HELLO\' failed' in ''.join(output)
> @@ -64,7 +64,7 @@ class TestEfiUnsignedImage(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>  
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert 'Hello, world!' in ''.join(output)
> @@ -88,7 +88,7 @@ class TestEfiUnsignedImage(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>  
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert '\'HELLO\' failed' in ''.join(output)
> @@ -106,7 +106,7 @@ class TestEfiUnsignedImage(object):
>              assert 'Failed to set EFI variable' not in ''.join(output)
>  
>              output = u_boot_console.run_command_list([
> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>                  'efidebug boot next 1',
>                  'bootefi bootmgr'])
>              assert '\'HELLO\' failed' in ''.join(output)
> -- 
> 2.30.1
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 5/6] efidebug: add multiple device path instances on Boot####
  2021-03-12  4:44   ` AKASHI Takahiro
@ 2021-03-12  4:55     ` Ilias Apalodimas
  2021-03-12  5:23       ` AKASHI Takahiro
  2021-03-12 16:25     ` Heinrich Schuchardt
  1 sibling, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-12  4:55 UTC (permalink / raw)
  To: u-boot

Akashi-san

> >  
[...]
> > +/**
> > + * add_initrd_instance() - Append a device path to load_options pointing to an
> > + *			   inirtd
> > +	if (!initrd_dp) {
> > +		printf("Cannot append media vendor device path path\n");
> > +		goto out;
> > +	}
> > +	final_fp = efi_dp_concat(fp, initrd_dp);
> > +	*fp_size = efi_dp_size(fp) + efi_dp_size(initrd_dp) +
> > +		(2 * sizeof(struct efi_device_path));
> > +
> > +out:
> > +	efi_free_pool(initrd_dp);
> > +	efi_free_pool(tmp_dp);
> > +	efi_free_pool(tmp_fp);
> > +	return final_fp ? final_fp : ERR_PTR(-EINVAL);
> > +}
> > +
> >  /**
> >   * do_efi_boot_add() - set UEFI load option
> >   *
> > @@ -806,7 +868,9 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
> >   *
> >   * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
> >   *
> > - *     efidebug boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
> > + * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
> > + *                   -i <file> <interface2> <devnum2>[:<part>] <initrd>
> > + *                   -s '<options>'
> 
> We discussed another syntax:
> efidebug boot add <id> ...
> efidebug boot add-initrd <id> <initrd path>
> (Please don't care detailed syntax for now.)

Yep and I completely agree this is a better format but ...

> 
> What is the difficulty that you have had to implement this type of
> interface?

The problem is that the initrd and kernel eventually go into *one* Boot####
variable.  So it's much easier to treat them in a single command as a bundle.

Otherwise you'll have to start adding checks on the initrd code to make
sure the Boot### variable exists before you append an initrd.
Then you have to deserialize the existing stored device path in Boot####,
append the initrd and serialize it again (and last time I checked this is not
as easy as it sounds).
Also what happens if you edit a Boot#### option and that option has an initrd? 
You have to pick up the existing initrd and move it over? Or do we silently 
delete it?
Something like:
efidebug boot add 0001 
efidebug boot add-initrd 0001 
efidebug boot add 0001

or even

efidebug boot add 0001 
efidebug boot add-initrd 0001 
efidebug boot add-initrd 0001 

Again it looks better. It's probably easier to use, but this is an efidebug
command. efibootmgr etc is supposed to set this correctly, so does the extra
effort and code worth it?

> 
> Even if we follow your new syntax,
> Why do we need '-b' option?
> "<id> <label> <interface> <devnum>[:<part>] <file>" are all mandatory arguments,
> aren't they?

Yes, it's just felt a bit more natural to add args on everything.  If other
feel the same way I can obviously remove it.


Regards
/Ilias
> 
> -Takahiro Akashi
> 
> 
> 
> >   */
> >  static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
> >  			   int argc, char *const argv[])
> > @@ -819,55 +883,98 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
> >  	size_t label_len, label_len16;
> >  	u16 *label;
> >  	struct efi_device_path *device_path = NULL, *file_path = NULL;
> > +	struct efi_device_path *final_fp = NULL;
> >  	struct efi_load_option lo;
> >  	void *data = NULL;
> >  	efi_uintn_t size;
> > +	efi_uintn_t fp_size;
> >  	efi_status_t ret;
> >  	int r = CMD_RET_SUCCESS;
> > -
> > -	if (argc < 6 || argc > 7)
> > -		return CMD_RET_USAGE;
> > -
> > -	id = (int)simple_strtoul(argv[1], &endp, 16);
> > -	if (*endp != '\0' || id > 0xffff)
> > -		return CMD_RET_USAGE;
> > -
> > -	sprintf(var_name, "Boot%04X", id);
> > -	p = var_name16;
> > -	utf8_utf16_strncpy(&p, var_name, 9);
> > +	int i;
> >  
> >  	guid = efi_global_variable_guid;
> >  
> >  	/* attributes */
> >  	lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */
> > +	lo.optional_data = NULL;
> > +	lo.label = NULL;
> >  
> > -	/* label */
> > -	label_len = strlen(argv[2]);
> > -	label_len16 = utf8_utf16_strnlen(argv[2], label_len);
> > -	label = malloc((label_len16 + 1) * sizeof(u16));
> > -	if (!label)
> > -		return CMD_RET_FAILURE;
> > -	lo.label = label; /* label will be changed below */
> > -	utf8_utf16_strncpy(&label, argv[2], label_len);
> > +	/* search for -b first since the rest of the arguments depends on that */
> > +	for (i = 0; i < argc; i++) {
> > +		if (!strcmp(argv[i], "-b")) {
> > +			if (argc < i + 5 || lo.label) {
> > +				r = CMD_RET_USAGE;
> > +				goto out;
> > +			}
> > +			id = (int)simple_strtoul(argv[i + 1], &endp, 16);
> > +			if (*endp != '\0' || id > 0xffff)
> > +				return CMD_RET_USAGE;
> > +
> > +			sprintf(var_name, "Boot%04X", id);
> > +			p = var_name16;
> > +			utf8_utf16_strncpy(&p, var_name, 9);
> > +
> > +			/* label */
> > +			label_len = strlen(argv[i + 2]);
> > +			label_len16 = utf8_utf16_strnlen(argv[i + 2], label_len);
> > +			label = malloc((label_len16 + 1) * sizeof(u16));
> > +			if (!label)
> > +				return CMD_RET_FAILURE;
> > +			lo.label = label; /* label will be changed below */
> > +			utf8_utf16_strncpy(&label, argv[i + 2], label_len);
> > +
> > +			/* file path */
> > +			ret = efi_dp_from_name(argv[i + 3], argv[i + 4],
> > +					       argv[i + 5], &device_path,
> > +					       &file_path);
> > +			if (ret != EFI_SUCCESS) {
> > +				printf("Cannot create device path for \"%s %s\"\n",
> > +				       argv[3], argv[4]);
> > +				r = CMD_RET_FAILURE;
> > +				goto out;
> > +			break;
> > +			}
> > +			fp_size = efi_dp_size(file_path) +
> > +				sizeof(struct efi_device_path);
> > +		}
> > +	}
> >  
> > -	/* file path */
> > -	ret = efi_dp_from_name(argv[3], argv[4], argv[5], &device_path,
> > -			       &file_path);
> > -	if (ret != EFI_SUCCESS) {
> > -		printf("Cannot create device path for \"%s %s\"\n",
> > -		       argv[3], argv[4]);
> > +	if (!file_path) {
> > +		printf("You need to specify an image before an initrd.\n");
> >  		r = CMD_RET_FAILURE;
> >  		goto out;
> >  	}
> > -	lo.file_path = file_path;
> > -	lo.file_path_length = efi_dp_size(file_path)
> > -				+ sizeof(struct efi_device_path); /* for END */
> >  
> > -	/* optional data */
> > -	if (argc == 6)
> > -		lo.optional_data = NULL;
> > -	else
> > -		lo.optional_data = (const u8 *)argv[6];
> > +	/* add now add initrd and extra data */
> > +	for (i = 0; i < argc; i++) {
> > +		if (!strcmp(argv[i], "-i")) {
> > +			if (argc < i + 3 || final_fp) {
> > +				r = CMD_RET_USAGE;
> > +				goto out;
> > +			}
> > +
> > +			final_fp = add_initrd_instance(argv[i + 1], argv[i + 2],
> > +						       argv[i + 3], file_path,
> > +						       &fp_size);
> > +			if (IS_ERR(final_fp)) {
> > +				r = CMD_RET_FAILURE;
> > +				goto out;
> > +			}
> > +
> > +			/* add_initrd_instance allocates a new device path */
> > +			efi_free_pool(file_path);
> > +			file_path = final_fp;
> > +		} else if (!strcmp(argv[i], "-s")) {
> > +			if (argc < i + 1 || lo.optional_data) {
> > +				r = CMD_RET_USAGE;
> > +				goto out;
> > +			}
> > +			lo.optional_data = (const u8 *)argv[i + 1];
> > +		}
> > +	}
> > +
> > +	lo.file_path = file_path;
> > +	lo.file_path_length = fp_size;
> >  
> >  	size = efi_serialize_load_option(&lo, (u8 **)&data);
> >  	if (!size) {
> > @@ -951,11 +1058,14 @@ static int do_efi_boot_rm(struct cmd_tbl *cmdtp, int flag,
> >   */
> >  static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
> >  {
> > +	struct efi_device_path *initrd_path = NULL;
> >  	struct efi_load_option lo;
> >  	char *label, *p;
> >  	size_t label_len16, label_len;
> >  	u16 *dp_str;
> >  	efi_status_t ret;
> > +	efi_uintn_t initrd_dp_size;
> > +	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
> >  
> >  	ret = efi_deserialize_load_option(&lo, data, size);
> >  	if (ret != EFI_SUCCESS) {
> > @@ -986,6 +1096,14 @@ static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
> >  	printf("  file_path: %ls\n", dp_str);
> >  	efi_free_pool(dp_str);
> >  
> > +	initrd_path = efi_dp_from_lo(&lo, &initrd_dp_size, lf2_initrd_guid);
> > +	if (initrd_path) {
> > +		dp_str = efi_dp_str(initrd_path);
> > +		printf("  initrd_path: %ls\n", dp_str);
> > +		efi_free_pool(dp_str);
> > +		efi_free_pool(initrd_path);
> > +	}
> > +
> >  	printf("  data:\n");
> >  	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
> >  		       lo.optional_data, *size, true);
> > @@ -1555,7 +1673,10 @@ static int do_efidebug(struct cmd_tbl *cmdtp, int flag,
> >  static char efidebug_help_text[] =
> >  	"  - UEFI Shell-like interface to configure UEFI environment\n"
> >  	"\n"
> > -	"efidebug boot add <bootid> <label> <interface> <devnum>[:<part>] <file path> [<load options>]\n"
> > +	"efidebug boot add "
> > +	"-b <bootid> <label> <interface> <devnum>[:<part>] <file path> "
> > +	"-i <interface> <devnum>[:<part>] <initrd file path> "
> > +	"-s '<optional data>'\n"
> >  	"  - set UEFI BootXXXX variable\n"
> >  	"    <load options> will be passed to UEFI application\n"
> >  	"efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n"
> > @@ -1599,7 +1720,7 @@ static char efidebug_help_text[] =
> >  #endif
> >  
> >  U_BOOT_CMD(
> > -	efidebug, 10, 0, do_efidebug,
> > +	efidebug, CONFIG_SYS_MAXARGS, 0, do_efidebug,
> >  	"Configure UEFI environment",
> >  	efidebug_help_text
> >  );
> > diff --git a/doc/board/emulation/qemu_capsule_update.rst b/doc/board/emulation/qemu_capsule_update.rst
> > index 9fec75f8f1c9..33ce4bcd32ea 100644
> > --- a/doc/board/emulation/qemu_capsule_update.rst
> > +++ b/doc/board/emulation/qemu_capsule_update.rst
> > @@ -60,7 +60,7 @@ to be pointing to the EFI System Partition which contains the capsule
> >  file. The BootNext, BootXXXX and OsIndications variables can be set
> >  using the following commands::
> >  
> > -    => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
> > +    => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
> >      => efidebug boot next 0
> >      => setenv -e -nv -bs -rt -v OsIndications =0x04
> >      => saveenv
> > @@ -198,7 +198,7 @@ command line::
> >      3. Set the following environment and UEFI boot variables
> >  
> >          => setenv -e -nv -bs -rt -v OsIndications =0x04
> > -        => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
> > +        => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
> >          => efidebug boot next 0
> >          => saveenv
> >  
> > diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst
> > index 5a67737c1579..b3494c22e073 100644
> > --- a/doc/uefi/uefi.rst
> > +++ b/doc/uefi/uefi.rst
> > @@ -178,7 +178,7 @@ Now in U-Boot install the keys on your board::
> >  
> >  Set up boot parameters on your board::
> >  
> > -    efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed ""
> > +    efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed ""
> >  
> >  Now your board can run the signed image via the boot manager (see below).
> >  You can also try this sequence by running Pytest, test_efi_secboot,
> > diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> > index f006fa95d650..e8b0a1575453 100644
> > --- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> > +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> > @@ -39,7 +39,7 @@ class TestEfiCapsuleFirmwareFit(object):
> >          with u_boot_console.log.section('Test Case 1-a, before reboot'):
> >              output = u_boot_console.run_command_list([
> >                  'host bind 0 %s' % disk_img,
> > -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
> >                  'efidebug boot order 1',
> >                  'env set -e OsIndications',
> >                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> > @@ -114,7 +114,7 @@ class TestEfiCapsuleFirmwareFit(object):
> >          with u_boot_console.log.section('Test Case 2-a, before reboot'):
> >              output = u_boot_console.run_command_list([
> >                  'host bind 0 %s' % disk_img,
> > -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
> >                  'efidebug boot order 1',
> >                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> >                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> > @@ -188,7 +188,7 @@ class TestEfiCapsuleFirmwareFit(object):
> >          with u_boot_console.log.section('Test Case 3-a, before reboot'):
> >              output = u_boot_console.run_command_list([
> >                  'host bind 0 %s' % disk_img,
> > -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
> >                  'efidebug boot order 1',
> >                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> >                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> > diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py
> > index 863685e215b7..75f5ea772300 100644
> > --- a/test/py/tests/test_efi_secboot/test_signed.py
> > +++ b/test/py/tests/test_efi_secboot/test_signed.py
> > @@ -28,7 +28,7 @@ class TestEfiSignedImage(object):
> >              # Test Case 1a, run signed image if no PK
> >              output = u_boot_console.run_command_list([
> >                  'host bind 0 %s' % disk_img,
> > -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> > +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > @@ -36,7 +36,7 @@ class TestEfiSignedImage(object):
> >          with u_boot_console.log.section('Test Case 1b'):
> >              # Test Case 1b, run unsigned image if no PK
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 2',
> >                  'bootefi bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > @@ -58,13 +58,13 @@ class TestEfiSignedImage(object):
> >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> > +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert('\'HELLO1\' failed' in ''.join(output))
> >              assert('efi_start_image() returned: 26' in ''.join(output))
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 2',
> >                  'efidebug test bootmgr'])
> >              assert '\'HELLO2\' failed' in ''.join(output)
> > @@ -104,7 +104,7 @@ class TestEfiSignedImage(object):
> >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert '\'HELLO\' failed' in ''.join(output)
> > @@ -142,7 +142,7 @@ class TestEfiSignedImage(object):
> >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert '\'HELLO\' failed' in ''.join(output)
> > @@ -169,7 +169,7 @@ class TestEfiSignedImage(object):
> >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > @@ -227,7 +227,7 @@ class TestEfiSignedImage(object):
> >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py
> > index 70d6be00e8a8..0849572a5143 100644
> > --- a/test/py/tests/test_efi_secboot/test_signed_intca.py
> > +++ b/test/py/tests/test_efi_secboot/test_signed_intca.py
> > @@ -39,7 +39,7 @@ class TestEfiSignedImageIntca(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >  
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
> > +                'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert '\'HELLO_a\' failed' in ''.join(output)
> > @@ -48,7 +48,7 @@ class TestEfiSignedImageIntca(object):
> >          with u_boot_console.log.section('Test Case 1b'):
> >              # Test Case 1b, signed and authenticated by root CA
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
> > +                'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
> >                  'efidebug boot next 2',
> >                  'bootefi bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > @@ -70,7 +70,7 @@ class TestEfiSignedImageIntca(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >  
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> > +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert '\'HELLO_abc\' failed' in ''.join(output)
> > @@ -116,7 +116,7 @@ class TestEfiSignedImageIntca(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >  
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> > +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> >                  'efidebug boot next 1',
> >                  'efidebug test bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py
> > index 56f56e19eb84..8e026f7566ad 100644
> > --- a/test/py/tests/test_efi_secboot/test_unsigned.py
> > +++ b/test/py/tests/test_efi_secboot/test_unsigned.py
> > @@ -35,7 +35,7 @@ class TestEfiUnsignedImage(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >  
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert '\'HELLO\' failed' in ''.join(output)
> > @@ -64,7 +64,7 @@ class TestEfiUnsignedImage(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >  
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert 'Hello, world!' in ''.join(output)
> > @@ -88,7 +88,7 @@ class TestEfiUnsignedImage(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >  
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert '\'HELLO\' failed' in ''.join(output)
> > @@ -106,7 +106,7 @@ class TestEfiUnsignedImage(object):
> >              assert 'Failed to set EFI variable' not in ''.join(output)
> >  
> >              output = u_boot_console.run_command_list([
> > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> >                  'efidebug boot next 1',
> >                  'bootefi bootmgr'])
> >              assert '\'HELLO\' failed' in ''.join(output)
> > -- 
> > 2.30.1
> > 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-12  4:42                 ` Ilias Apalodimas
@ 2021-03-12  5:02                   ` AKASHI Takahiro
  2021-03-12  5:19                     ` Ilias Apalodimas
  0 siblings, 1 reply; 41+ messages in thread
From: AKASHI Takahiro @ 2021-03-12  5:02 UTC (permalink / raw)
  To: u-boot

On Fri, Mar 12, 2021 at 06:42:14AM +0200, Ilias Apalodimas wrote:
> On Fri, Mar 12, 2021 at 01:32:50PM +0900, AKASHI Takahiro wrote:
> [...]
> > > > > > My understanding is that we have:
> > > > > > 
> > > > > > kernel path,end(0xff),
> > > > > > VenMedia(), /* no end node here */
> > > > > > initrd1, end(0x01),
> > > > > > initrd2, end(0xff)
> > > > > 
> > > > > No, the structure is added in cmd/efidebug.c code.
> > > > > It's created with efi_dp_append_instance() on 
> > > > >  - const struct efi_initrd_dp id_dp
> > > > >  - file path of initrd
> > > > >  
> > > > >  which will create:
> > > > >  kernel path,end(0xff),
> > > > >  VenMedia(), end(0x01),
> > > > >  initrd1, end(0x01),
> > > > >  initrd2, end(0xff)
> > > > 
> > > > What is the difference between end(0xff) and end(0x01)?
> > > > 
> > > 
> > > 0xff is a subtype of 'end the entire device path', while 0x01 is an 'end of
> > > instance of a device path and start a new device path'

If I correctly remember, you had some discussions about this point
that UEFI specification is ambiguous here. Right?

> > > 
> > > > If the first argument of a load option is a list of device paths,
> > > > I would expect the format would look like:
> > > >   kernel path,end(0xff),
> > > >   VenMedia(INITRD),initrd1 path,end(0xff),
> > > >   VenMedia(INITRD),initrd2 path,end(0xff),
> > > > 
> > > > so that VenMedia can work as an identify of the succeeding path.
> > > > Is it simple enough, isn't it?
> > > 
> > > It's essentially the same thing. It has an effect on the EFI spec and how you
> > > interpret it, but honestly it feels as an implementation detail to me, since
> > > none of those are standardized anyway.
> > > 
> > > In fact what you are saying was part of my proposal in the original mail
> > > (check proposal 1.)
> > > 
> > > Anyway the difference between the two is that what I coded looks like this:
> > > FilePathList[0] -> kernel
> > > FilePathList[1] -> initrd1 - initrdn
> > > 
> > > while whe other is
> > > FilePathList[0] -> kernel
> > > FilePathList[1] -> initrd1 
> > > FilePathList[2] -> initrd2
> > > FilePathList[n] -> initrdn
> > > 
> > > If we ever manage to wire in the DTBs in there as well it may look like:
> > > 
> > > FilePathList[0] -> kernel
> > > FilePathList[1] -> initrd1 - initrdn
> > > FilePathList[2] -> dtb1 - dtbn
> > > 
> > > Vs
> > > 
> > > FilePathList[0] -> kernel
> > > FilePathList[1] -> initrd1 
> > > FilePathList[2] -> initrd2
> > > FilePathList[3] -> dtb1
> > > FilePathList[n] -> initrdn
> > > FilePathList[n+1] -> dtb2
> > 
> > What is the semantics?
> > Which do you want to do?
> > a) boot one of combinations:
> >      1.kernel+initrd1+dtb1, or
> >      2.kernel+initrd2+dtb2
> > b) boot
> >      kernel + (initrd1 + initrd2) + (dtb1 + dtb2)
> > 
> > I assume you meant (a).
> > In that case, how can you specify (a-1) or (a-2) at boot time?
> > 
> > Is there any clear description about that?
> > (I"m simply asking here.)
> 
> it's b) 

OK.

> if you want different combinations of kernel/initrds (as described in a) you
> can add another Boot#### variable.

Indeed.

> So let's assume you got three boot options
> Boot0000, Boot0001 and Boot0002
> 
> Boot0000 efi_load_options: kernel1 + (initrd1 + initrd2) + (dtb1 + dtb2). 
> The bootloader will concat initrd1+initrd2 when the kernel requests it.
> Similar behavior can be coded for dtb before installing the table.

My understanding is that none of existing boot loaders has not yet
supported such a concatenation (of either initrd or dtb) right now.
Do you (or linux's efistub?) intend to add a new feature?

-Takahiro Akashi


> Boot0001: kernel1 + initrd1 + dtb1
> Load a kernel with a single initrd and dtb.
> 
> Boot0002: kernel2 + initrd2 + dtb2
> ditto.
> 
> Hope that's clear now
> 
> Cheers
> /Ilias
> 
> 
> > 
> > -Takahiro Akashi
> > 
> > > 
> > > 
> > > > 
> > > > -Takahiro Akashi
> > > > 
> > > > > I know I originally proposed the one you have, but it seemed cleaner adding
> > > > > an extra instance between VenMedia and the first initrd.
> > > > > 
> > > > > > 
> > > > > > Please, document the structure.
> > > > > > 
> > > > > 
> > > > > Sure
> > > > > 
> > > > > > Best regards
> > > > > > 
> > > > > > Heinrich
> > > > > 
> > > > > Thanks
> > > > > /Ilias
> > > 
> > > [1] https://lists.linaro.org/pipermail/boot-architecture/2021-February/001686.html
> > > 
> > > Cheers
> > > /Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-12  5:02                   ` AKASHI Takahiro
@ 2021-03-12  5:19                     ` Ilias Apalodimas
  0 siblings, 0 replies; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-12  5:19 UTC (permalink / raw)
  To: u-boot

> > > > > >  
[...]
> > > > > >  which will create:
> > > > > >  kernel path,end(0xff),
> > > > > >  VenMedia(), end(0x01),
> > > > > >  initrd1, end(0x01),
> > > > > >  initrd2, end(0xff)
> > > > > 
> > > > > What is the difference between end(0xff) and end(0x01)?
> > > > > 
> > > > 
> > > > 0xff is a subtype of 'end the entire device path', while 0x01 is an 'end of
> > > > instance of a device path and start a new device path'
> 
> If I correctly remember, you had some discussions about this point
> that UEFI specification is ambiguous here. Right?

Yea, just by reading it wasn't clear if the delimiter between FilePathList[]
array elements was supposed to be 0xff or 0x01.
We finally figured out the spec means 0xff though, so that's (hopefully) clear
now.

> 
[...]
> > > > FilePathList[0] -> kernel
> > > > FilePathList[1] -> initrd1 - initrdn
> > > > FilePathList[2] -> dtb1 - dtbn
> > > > 
> > > > Vs
> > > > 
> > > > FilePathList[0] -> kernel
> > > > FilePathList[1] -> initrd1 
> > > > FilePathList[2] -> initrd2
> > > > FilePathList[3] -> dtb1
> > > > FilePathList[n] -> initrdn
> > > > FilePathList[n+1] -> dtb2
> > > 
> > > What is the semantics?
> > > Which do you want to do?
> > > a) boot one of combinations:
> > >      1.kernel+initrd1+dtb1, or
> > >      2.kernel+initrd2+dtb2
> > > b) boot
> > >      kernel + (initrd1 + initrd2) + (dtb1 + dtb2)
> > > 
> > > I assume you meant (a).
> > > In that case, how can you specify (a-1) or (a-2) at boot time?
> > > 
> > > Is there any clear description about that?
> > > (I"m simply asking here.)
> > 
> > it's b) 
> 
> OK.
> 
> > if you want different combinations of kernel/initrds (as described in a) you
> > can add another Boot#### variable.
> 
> Indeed.
> 
> > So let's assume you got three boot options
> > Boot0000, Boot0001 and Boot0002
> > 
> > Boot0000 efi_load_options: kernel1 + (initrd1 + initrd2) + (dtb1 + dtb2). 
> > The bootloader will concat initrd1+initrd2 when the kernel requests it.
> > Similar behavior can be coded for dtb before installing the table.
> 
> My understanding is that none of existing boot loaders has not yet
> supported such a concatenation (of either initrd or dtb) right now.
> Do you (or linux's efistub?) intend to add a new feature?

For u-boot we intend to add it in the future. I just wanted to keep the
initial patchset simpler, since 99.9% of the use cases are for a single
initrd.

The efistub doesn't have to add anything to support this, that's the whole
point of trying to standardize this.
Since there's some confusion around let me try and explain that here as well.

The EFI stub does two things.  It searches for the protocol with a specific 
DP (which includes a unique GUID). The DP our case is <VenMedia for initrd>/<end node>.
Since locate protocol will strip the first part, the stub now calls LoadFile2 
protocol with a device end node and a kernel provided buffer (and there's a 
mechanism to discover the size).
It's pretty much tells the bootloader 'gimme my initrd'.  So it's entirely up
to the bootloader to select a mechanism for discovering were those initrds
are and concatenate them


Cheers
/Ilias

> 
> -Takahiro Akashi
> 
> 
> > Boot0001: kernel1 + initrd1 + dtb1
> > Load a kernel with a single initrd and dtb.
> > 
> > Boot0002: kernel2 + initrd2 + dtb2
> > ditto.
> > 
> > Hope that's clear now
> > 
> > Cheers
> > /Ilias
> > 
> > 
> > > 
> > > -Takahiro Akashi
> > > 
> > > > 
> > > > 
> > > > > 
> > > > > -Takahiro Akashi
> > > > > 
> > > > > > I know I originally proposed the one you have, but it seemed cleaner adding
> > > > > > an extra instance between VenMedia and the first initrd.
> > > > > > 
> > > > > > > 
> > > > > > > Please, document the structure.
> > > > > > > 
> > > > > > 
> > > > > > Sure
> > > > > > 
> > > > > > > Best regards
> > > > > > > 
> > > > > > > Heinrich
> > > > > > 
> > > > > > Thanks
> > > > > > /Ilias
> > > > 
> > > > [1] https://lists.linaro.org/pipermail/boot-architecture/2021-February/001686.html
> > > > 
> > > > Cheers
> > > > /Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 5/6] efidebug: add multiple device path instances on Boot####
  2021-03-12  4:55     ` Ilias Apalodimas
@ 2021-03-12  5:23       ` AKASHI Takahiro
  2021-03-12  5:37         ` Ilias Apalodimas
  0 siblings, 1 reply; 41+ messages in thread
From: AKASHI Takahiro @ 2021-03-12  5:23 UTC (permalink / raw)
  To: u-boot

On Fri, Mar 12, 2021 at 06:55:57AM +0200, Ilias Apalodimas wrote:
> Akashi-san
> 
> > >  
> [...]
> > > +/**
> > > + * add_initrd_instance() - Append a device path to load_options pointing to an
> > > + *			   inirtd
> > > +	if (!initrd_dp) {
> > > +		printf("Cannot append media vendor device path path\n");
> > > +		goto out;
> > > +	}
> > > +	final_fp = efi_dp_concat(fp, initrd_dp);
> > > +	*fp_size = efi_dp_size(fp) + efi_dp_size(initrd_dp) +
> > > +		(2 * sizeof(struct efi_device_path));
> > > +
> > > +out:
> > > +	efi_free_pool(initrd_dp);
> > > +	efi_free_pool(tmp_dp);
> > > +	efi_free_pool(tmp_fp);
> > > +	return final_fp ? final_fp : ERR_PTR(-EINVAL);
> > > +}
> > > +
> > >  /**
> > >   * do_efi_boot_add() - set UEFI load option
> > >   *
> > > @@ -806,7 +868,9 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
> > >   *
> > >   * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
> > >   *
> > > - *     efidebug boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
> > > + * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
> > > + *                   -i <file> <interface2> <devnum2>[:<part>] <initrd>
> > > + *                   -s '<options>'
> > 
> > We discussed another syntax:
> > efidebug boot add <id> ...
> > efidebug boot add-initrd <id> <initrd path>
> > (Please don't care detailed syntax for now.)
> 
> Yep and I completely agree this is a better format but ...
> 
> > 
> > What is the difficulty that you have had to implement this type of
> > interface?
> 
> The problem is that the initrd and kernel eventually go into *one* Boot####
> variable.  So it's much easier to treat them in a single command as a bundle.
> 
> Otherwise you'll have to start adding checks on the initrd code to make
> sure the Boot### variable exists before you append an initrd.
> Then you have to deserialize the existing stored device path in Boot####,
> append the initrd and serialize it again (and last time I checked this is not
> as easy as it sounds).

If we take the format like:
  kernel path,end(0xff),
  VenMedia(INITRD),initrd1 path,end(0xff),
  VenMedia(INITRD),initrd2 path,end(0xff),

all that we have to do is
- deserialize the boot option variable,
- append initrd device path to a list (after kernel device path),
- serialize all the option together,

Appending is quite simple, isn't it?
(because we will not have to parse a device path list.)

> Also what happens if you edit a Boot#### option and that option has an initrd? 
If I were you,

> You have to pick up the existing initrd and move it over? Or do we silently 
> delete it?
> Something like:
> efidebug boot add 0001 
> efidebug boot add-initrd 0001 
> efidebug boot add 0001

even with the current implementation,
> efidebug boot add 0001 
> efidebug boot add 0001 
those sequence will overwrite the existing variable and,
deeleting initrd by the second "efidebug boot add" would make sense.

> or even
> 
> efidebug boot add 0001 
> efidebug boot add-initrd 0001 
> efidebug boot add-initrd 0001 

I think that it will imply "appending initrd to a device path list."

-Takahiro Akashi

> Again it looks better. It's probably easier to use, but this is an efidebug
> command. efibootmgr etc is supposed to set this correctly, so does the extra
> effort and code worth it?
> 
> > 
> > Even if we follow your new syntax,
> > Why do we need '-b' option?
> > "<id> <label> <interface> <devnum>[:<part>] <file>" are all mandatory arguments,
> > aren't they?
> 
> Yes, it's just felt a bit more natural to add args on everything.  If other
> feel the same way I can obviously remove it.
> 
> 
> Regards
> /Ilias
> > 
> > -Takahiro Akashi
> > 
> > 
> > 
> > >   */
> > >  static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
> > >  			   int argc, char *const argv[])
> > > @@ -819,55 +883,98 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
> > >  	size_t label_len, label_len16;
> > >  	u16 *label;
> > >  	struct efi_device_path *device_path = NULL, *file_path = NULL;
> > > +	struct efi_device_path *final_fp = NULL;
> > >  	struct efi_load_option lo;
> > >  	void *data = NULL;
> > >  	efi_uintn_t size;
> > > +	efi_uintn_t fp_size;
> > >  	efi_status_t ret;
> > >  	int r = CMD_RET_SUCCESS;
> > > -
> > > -	if (argc < 6 || argc > 7)
> > > -		return CMD_RET_USAGE;
> > > -
> > > -	id = (int)simple_strtoul(argv[1], &endp, 16);
> > > -	if (*endp != '\0' || id > 0xffff)
> > > -		return CMD_RET_USAGE;
> > > -
> > > -	sprintf(var_name, "Boot%04X", id);
> > > -	p = var_name16;
> > > -	utf8_utf16_strncpy(&p, var_name, 9);
> > > +	int i;
> > >  
> > >  	guid = efi_global_variable_guid;
> > >  
> > >  	/* attributes */
> > >  	lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */
> > > +	lo.optional_data = NULL;
> > > +	lo.label = NULL;
> > >  
> > > -	/* label */
> > > -	label_len = strlen(argv[2]);
> > > -	label_len16 = utf8_utf16_strnlen(argv[2], label_len);
> > > -	label = malloc((label_len16 + 1) * sizeof(u16));
> > > -	if (!label)
> > > -		return CMD_RET_FAILURE;
> > > -	lo.label = label; /* label will be changed below */
> > > -	utf8_utf16_strncpy(&label, argv[2], label_len);
> > > +	/* search for -b first since the rest of the arguments depends on that */
> > > +	for (i = 0; i < argc; i++) {
> > > +		if (!strcmp(argv[i], "-b")) {
> > > +			if (argc < i + 5 || lo.label) {
> > > +				r = CMD_RET_USAGE;
> > > +				goto out;
> > > +			}
> > > +			id = (int)simple_strtoul(argv[i + 1], &endp, 16);
> > > +			if (*endp != '\0' || id > 0xffff)
> > > +				return CMD_RET_USAGE;
> > > +
> > > +			sprintf(var_name, "Boot%04X", id);
> > > +			p = var_name16;
> > > +			utf8_utf16_strncpy(&p, var_name, 9);
> > > +
> > > +			/* label */
> > > +			label_len = strlen(argv[i + 2]);
> > > +			label_len16 = utf8_utf16_strnlen(argv[i + 2], label_len);
> > > +			label = malloc((label_len16 + 1) * sizeof(u16));
> > > +			if (!label)
> > > +				return CMD_RET_FAILURE;
> > > +			lo.label = label; /* label will be changed below */
> > > +			utf8_utf16_strncpy(&label, argv[i + 2], label_len);
> > > +
> > > +			/* file path */
> > > +			ret = efi_dp_from_name(argv[i + 3], argv[i + 4],
> > > +					       argv[i + 5], &device_path,
> > > +					       &file_path);
> > > +			if (ret != EFI_SUCCESS) {
> > > +				printf("Cannot create device path for \"%s %s\"\n",
> > > +				       argv[3], argv[4]);
> > > +				r = CMD_RET_FAILURE;
> > > +				goto out;
> > > +			break;
> > > +			}
> > > +			fp_size = efi_dp_size(file_path) +
> > > +				sizeof(struct efi_device_path);
> > > +		}
> > > +	}
> > >  
> > > -	/* file path */
> > > -	ret = efi_dp_from_name(argv[3], argv[4], argv[5], &device_path,
> > > -			       &file_path);
> > > -	if (ret != EFI_SUCCESS) {
> > > -		printf("Cannot create device path for \"%s %s\"\n",
> > > -		       argv[3], argv[4]);
> > > +	if (!file_path) {
> > > +		printf("You need to specify an image before an initrd.\n");
> > >  		r = CMD_RET_FAILURE;
> > >  		goto out;
> > >  	}
> > > -	lo.file_path = file_path;
> > > -	lo.file_path_length = efi_dp_size(file_path)
> > > -				+ sizeof(struct efi_device_path); /* for END */
> > >  
> > > -	/* optional data */
> > > -	if (argc == 6)
> > > -		lo.optional_data = NULL;
> > > -	else
> > > -		lo.optional_data = (const u8 *)argv[6];
> > > +	/* add now add initrd and extra data */
> > > +	for (i = 0; i < argc; i++) {
> > > +		if (!strcmp(argv[i], "-i")) {
> > > +			if (argc < i + 3 || final_fp) {
> > > +				r = CMD_RET_USAGE;
> > > +				goto out;
> > > +			}
> > > +
> > > +			final_fp = add_initrd_instance(argv[i + 1], argv[i + 2],
> > > +						       argv[i + 3], file_path,
> > > +						       &fp_size);
> > > +			if (IS_ERR(final_fp)) {
> > > +				r = CMD_RET_FAILURE;
> > > +				goto out;
> > > +			}
> > > +
> > > +			/* add_initrd_instance allocates a new device path */
> > > +			efi_free_pool(file_path);
> > > +			file_path = final_fp;
> > > +		} else if (!strcmp(argv[i], "-s")) {
> > > +			if (argc < i + 1 || lo.optional_data) {
> > > +				r = CMD_RET_USAGE;
> > > +				goto out;
> > > +			}
> > > +			lo.optional_data = (const u8 *)argv[i + 1];
> > > +		}
> > > +	}
> > > +
> > > +	lo.file_path = file_path;
> > > +	lo.file_path_length = fp_size;
> > >  
> > >  	size = efi_serialize_load_option(&lo, (u8 **)&data);
> > >  	if (!size) {
> > > @@ -951,11 +1058,14 @@ static int do_efi_boot_rm(struct cmd_tbl *cmdtp, int flag,
> > >   */
> > >  static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
> > >  {
> > > +	struct efi_device_path *initrd_path = NULL;
> > >  	struct efi_load_option lo;
> > >  	char *label, *p;
> > >  	size_t label_len16, label_len;
> > >  	u16 *dp_str;
> > >  	efi_status_t ret;
> > > +	efi_uintn_t initrd_dp_size;
> > > +	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
> > >  
> > >  	ret = efi_deserialize_load_option(&lo, data, size);
> > >  	if (ret != EFI_SUCCESS) {
> > > @@ -986,6 +1096,14 @@ static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
> > >  	printf("  file_path: %ls\n", dp_str);
> > >  	efi_free_pool(dp_str);
> > >  
> > > +	initrd_path = efi_dp_from_lo(&lo, &initrd_dp_size, lf2_initrd_guid);
> > > +	if (initrd_path) {
> > > +		dp_str = efi_dp_str(initrd_path);
> > > +		printf("  initrd_path: %ls\n", dp_str);
> > > +		efi_free_pool(dp_str);
> > > +		efi_free_pool(initrd_path);
> > > +	}
> > > +
> > >  	printf("  data:\n");
> > >  	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
> > >  		       lo.optional_data, *size, true);
> > > @@ -1555,7 +1673,10 @@ static int do_efidebug(struct cmd_tbl *cmdtp, int flag,
> > >  static char efidebug_help_text[] =
> > >  	"  - UEFI Shell-like interface to configure UEFI environment\n"
> > >  	"\n"
> > > -	"efidebug boot add <bootid> <label> <interface> <devnum>[:<part>] <file path> [<load options>]\n"
> > > +	"efidebug boot add "
> > > +	"-b <bootid> <label> <interface> <devnum>[:<part>] <file path> "
> > > +	"-i <interface> <devnum>[:<part>] <initrd file path> "
> > > +	"-s '<optional data>'\n"
> > >  	"  - set UEFI BootXXXX variable\n"
> > >  	"    <load options> will be passed to UEFI application\n"
> > >  	"efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n"
> > > @@ -1599,7 +1720,7 @@ static char efidebug_help_text[] =
> > >  #endif
> > >  
> > >  U_BOOT_CMD(
> > > -	efidebug, 10, 0, do_efidebug,
> > > +	efidebug, CONFIG_SYS_MAXARGS, 0, do_efidebug,
> > >  	"Configure UEFI environment",
> > >  	efidebug_help_text
> > >  );
> > > diff --git a/doc/board/emulation/qemu_capsule_update.rst b/doc/board/emulation/qemu_capsule_update.rst
> > > index 9fec75f8f1c9..33ce4bcd32ea 100644
> > > --- a/doc/board/emulation/qemu_capsule_update.rst
> > > +++ b/doc/board/emulation/qemu_capsule_update.rst
> > > @@ -60,7 +60,7 @@ to be pointing to the EFI System Partition which contains the capsule
> > >  file. The BootNext, BootXXXX and OsIndications variables can be set
> > >  using the following commands::
> > >  
> > > -    => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
> > > +    => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
> > >      => efidebug boot next 0
> > >      => setenv -e -nv -bs -rt -v OsIndications =0x04
> > >      => saveenv
> > > @@ -198,7 +198,7 @@ command line::
> > >      3. Set the following environment and UEFI boot variables
> > >  
> > >          => setenv -e -nv -bs -rt -v OsIndications =0x04
> > > -        => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
> > > +        => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
> > >          => efidebug boot next 0
> > >          => saveenv
> > >  
> > > diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst
> > > index 5a67737c1579..b3494c22e073 100644
> > > --- a/doc/uefi/uefi.rst
> > > +++ b/doc/uefi/uefi.rst
> > > @@ -178,7 +178,7 @@ Now in U-Boot install the keys on your board::
> > >  
> > >  Set up boot parameters on your board::
> > >  
> > > -    efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed ""
> > > +    efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed ""
> > >  
> > >  Now your board can run the signed image via the boot manager (see below).
> > >  You can also try this sequence by running Pytest, test_efi_secboot,
> > > diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> > > index f006fa95d650..e8b0a1575453 100644
> > > --- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> > > +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
> > > @@ -39,7 +39,7 @@ class TestEfiCapsuleFirmwareFit(object):
> > >          with u_boot_console.log.section('Test Case 1-a, before reboot'):
> > >              output = u_boot_console.run_command_list([
> > >                  'host bind 0 %s' % disk_img,
> > > -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> > > +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
> > >                  'efidebug boot order 1',
> > >                  'env set -e OsIndications',
> > >                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> > > @@ -114,7 +114,7 @@ class TestEfiCapsuleFirmwareFit(object):
> > >          with u_boot_console.log.section('Test Case 2-a, before reboot'):
> > >              output = u_boot_console.run_command_list([
> > >                  'host bind 0 %s' % disk_img,
> > > -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> > > +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
> > >                  'efidebug boot order 1',
> > >                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> > >                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> > > @@ -188,7 +188,7 @@ class TestEfiCapsuleFirmwareFit(object):
> > >          with u_boot_console.log.section('Test Case 3-a, before reboot'):
> > >              output = u_boot_console.run_command_list([
> > >                  'host bind 0 %s' % disk_img,
> > > -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
> > > +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
> > >                  'efidebug boot order 1',
> > >                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> > >                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> > > diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py
> > > index 863685e215b7..75f5ea772300 100644
> > > --- a/test/py/tests/test_efi_secboot/test_signed.py
> > > +++ b/test/py/tests/test_efi_secboot/test_signed.py
> > > @@ -28,7 +28,7 @@ class TestEfiSignedImage(object):
> > >              # Test Case 1a, run signed image if no PK
> > >              output = u_boot_console.run_command_list([
> > >                  'host bind 0 %s' % disk_img,
> > > -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> > > +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> > >                  'efidebug boot next 1',
> > >                  'bootefi bootmgr'])
> > >              assert 'Hello, world!' in ''.join(output)
> > > @@ -36,7 +36,7 @@ class TestEfiSignedImage(object):
> > >          with u_boot_console.log.section('Test Case 1b'):
> > >              # Test Case 1b, run unsigned image if no PK
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
> > > +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
> > >                  'efidebug boot next 2',
> > >                  'bootefi bootmgr'])
> > >              assert 'Hello, world!' in ''.join(output)
> > > @@ -58,13 +58,13 @@ class TestEfiSignedImage(object):
> > >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> > > +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
> > >                  'efidebug boot next 1',
> > >                  'efidebug test bootmgr'])
> > >              assert('\'HELLO1\' failed' in ''.join(output))
> > >              assert('efi_start_image() returned: 26' in ''.join(output))
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
> > > +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
> > >                  'efidebug boot next 2',
> > >                  'efidebug test bootmgr'])
> > >              assert '\'HELLO2\' failed' in ''.join(output)
> > > @@ -104,7 +104,7 @@ class TestEfiSignedImage(object):
> > >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > >                  'efidebug boot next 1',
> > >                  'efidebug test bootmgr'])
> > >              assert '\'HELLO\' failed' in ''.join(output)
> > > @@ -142,7 +142,7 @@ class TestEfiSignedImage(object):
> > >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > >                  'efidebug boot next 1',
> > >                  'efidebug test bootmgr'])
> > >              assert '\'HELLO\' failed' in ''.join(output)
> > > @@ -169,7 +169,7 @@ class TestEfiSignedImage(object):
> > >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
> > > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
> > >                  'efidebug boot next 1',
> > >                  'efidebug test bootmgr'])
> > >              assert 'Hello, world!' in ''.join(output)
> > > @@ -227,7 +227,7 @@ class TestEfiSignedImage(object):
> > >                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
> > >                  'efidebug boot next 1',
> > >                  'bootefi bootmgr'])
> > >              assert 'Hello, world!' in ''.join(output)
> > > diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py
> > > index 70d6be00e8a8..0849572a5143 100644
> > > --- a/test/py/tests/test_efi_secboot/test_signed_intca.py
> > > +++ b/test/py/tests/test_efi_secboot/test_signed_intca.py
> > > @@ -39,7 +39,7 @@ class TestEfiSignedImageIntca(object):
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >  
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
> > > +                'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
> > >                  'efidebug boot next 1',
> > >                  'efidebug test bootmgr'])
> > >              assert '\'HELLO_a\' failed' in ''.join(output)
> > > @@ -48,7 +48,7 @@ class TestEfiSignedImageIntca(object):
> > >          with u_boot_console.log.section('Test Case 1b'):
> > >              # Test Case 1b, signed and authenticated by root CA
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
> > > +                'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
> > >                  'efidebug boot next 2',
> > >                  'bootefi bootmgr'])
> > >              assert 'Hello, world!' in ''.join(output)
> > > @@ -70,7 +70,7 @@ class TestEfiSignedImageIntca(object):
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >  
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> > > +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> > >                  'efidebug boot next 1',
> > >                  'efidebug test bootmgr'])
> > >              assert '\'HELLO_abc\' failed' in ''.join(output)
> > > @@ -116,7 +116,7 @@ class TestEfiSignedImageIntca(object):
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >  
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> > > +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
> > >                  'efidebug boot next 1',
> > >                  'efidebug test bootmgr'])
> > >              assert 'Hello, world!' in ''.join(output)
> > > diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py
> > > index 56f56e19eb84..8e026f7566ad 100644
> > > --- a/test/py/tests/test_efi_secboot/test_unsigned.py
> > > +++ b/test/py/tests/test_efi_secboot/test_unsigned.py
> > > @@ -35,7 +35,7 @@ class TestEfiUnsignedImage(object):
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >  
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> > >                  'efidebug boot next 1',
> > >                  'bootefi bootmgr'])
> > >              assert '\'HELLO\' failed' in ''.join(output)
> > > @@ -64,7 +64,7 @@ class TestEfiUnsignedImage(object):
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >  
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> > >                  'efidebug boot next 1',
> > >                  'bootefi bootmgr'])
> > >              assert 'Hello, world!' in ''.join(output)
> > > @@ -88,7 +88,7 @@ class TestEfiUnsignedImage(object):
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >  
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> > >                  'efidebug boot next 1',
> > >                  'bootefi bootmgr'])
> > >              assert '\'HELLO\' failed' in ''.join(output)
> > > @@ -106,7 +106,7 @@ class TestEfiUnsignedImage(object):
> > >              assert 'Failed to set EFI variable' not in ''.join(output)
> > >  
> > >              output = u_boot_console.run_command_list([
> > > -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
> > > +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
> > >                  'efidebug boot next 1',
> > >                  'bootefi bootmgr'])
> > >              assert '\'HELLO\' failed' in ''.join(output)
> > > -- 
> > > 2.30.1
> > > 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 5/6] efidebug: add multiple device path instances on Boot####
  2021-03-12  5:23       ` AKASHI Takahiro
@ 2021-03-12  5:37         ` Ilias Apalodimas
  2021-03-12  5:58           ` AKASHI Takahiro
  0 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-12  5:37 UTC (permalink / raw)
  To: u-boot

Akashi-san,
> On Fri, Mar 12, 2021 at 02:23:13PM +0900, AKASHI Takahiro wrote:
> On Fri, Mar 12, 2021 at 06:55:57AM +0200, Ilias Apalodimas wrote:
> > Akashi-san
> > 
> > > >  
> > [...]
> > > > +/**
> > > > + * add_initrd_instance() - Append a device path to load_options pointing to an
> > > > + *			   inirtd
> > > > +	if (!initrd_dp) {
> > > > +		printf("Cannot append media vendor device path path\n");
> > > > +		goto out;
> > > > +	}
> > > > +	final_fp = efi_dp_concat(fp, initrd_dp);
> > > > +	*fp_size = efi_dp_size(fp) + efi_dp_size(initrd_dp) +
> > > > +		(2 * sizeof(struct efi_device_path));
> > > > +
> > > > +out:
> > > > +	efi_free_pool(initrd_dp);
> > > > +	efi_free_pool(tmp_dp);
> > > > +	efi_free_pool(tmp_fp);
> > > > +	return final_fp ? final_fp : ERR_PTR(-EINVAL);
> > > > +}
> > > > +
> > > >  /**
> > > >   * do_efi_boot_add() - set UEFI load option
> > > >   *
> > > > @@ -806,7 +868,9 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
> > > >   *
> > > >   * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
> > > >   *
> > > > - *     efidebug boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
> > > > + * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
> > > > + *                   -i <file> <interface2> <devnum2>[:<part>] <initrd>
> > > > + *                   -s '<options>'
> > > 
> > > We discussed another syntax:
> > > efidebug boot add <id> ...
> > > efidebug boot add-initrd <id> <initrd path>
> > > (Please don't care detailed syntax for now.)
> > 
> > Yep and I completely agree this is a better format but ...
> > 
> > > 
> > > What is the difficulty that you have had to implement this type of
> > > interface?
> > 
> > The problem is that the initrd and kernel eventually go into *one* Boot####
> > variable.  So it's much easier to treat them in a single command as a bundle.
> > 
> > Otherwise you'll have to start adding checks on the initrd code to make
> > sure the Boot### variable exists before you append an initrd.
> > Then you have to deserialize the existing stored device path in Boot####,
> > append the initrd and serialize it again (and last time I checked this is not
> > as easy as it sounds).
> 
> If we take the format like:
>   kernel path,end(0xff),
>   VenMedia(INITRD),initrd1 path,end(0xff),
>   VenMedia(INITRD),initrd2 path,end(0xff),
> 
> all that we have to do is
> - deserialize the boot option variable,
> - append initrd device path to a list (after kernel device path),
> - serialize all the option together,

Sure but the serialize/deserialize is not as easy as it sounds and requires
code as well for the optional data etc.
Again I am not saying it's not doable, or even more elegant.  I am saying the
extra code doesn't seemt to worth the effort right now. Especially since we
support a *single* initrd on the loading anyway and no dtbs.

> 
> Appending is quite simple, isn't it?
> (because we will not have to parse a device path list.)
> 

Yes and i've mentioned most of this on the mailing list. 
We did choose the other format though...

> > Also what happens if you edit a Boot#### option and that option has an initrd? 
> If I were you,
> 
> > You have to pick up the existing initrd and move it over? Or do we silently 
> > delete it?
> > Something like:
> > efidebug boot add 0001 
> > efidebug boot add-initrd 0001 
> > efidebug boot add 0001
> 
> even with the current implementation,
> > efidebug boot add 0001 
> > efidebug boot add 0001 
> those sequence will overwrite the existing variable and,
> deeleting initrd by the second "efidebug boot add" would make sense.

Yea but that feels 'natural' because it's a single command. Which is also the
case with the current code. In multiple commands you wouldn't expect the
initrd to away, unless you knew it was stored in a Boot option.


Thanks
/Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 5/6] efidebug: add multiple device path instances on Boot####
  2021-03-12  5:37         ` Ilias Apalodimas
@ 2021-03-12  5:58           ` AKASHI Takahiro
  2021-03-12  7:19             ` Ilias Apalodimas
  0 siblings, 1 reply; 41+ messages in thread
From: AKASHI Takahiro @ 2021-03-12  5:58 UTC (permalink / raw)
  To: u-boot

On Fri, Mar 12, 2021 at 07:37:55AM +0200, Ilias Apalodimas wrote:
> Akashi-san,
> > On Fri, Mar 12, 2021 at 02:23:13PM +0900, AKASHI Takahiro wrote:
> > On Fri, Mar 12, 2021 at 06:55:57AM +0200, Ilias Apalodimas wrote:
> > > Akashi-san
> > > 
> > > > >  
> > > [...]
> > > > > +/**
> > > > > + * add_initrd_instance() - Append a device path to load_options pointing to an
> > > > > + *			   inirtd
> > > > > +	if (!initrd_dp) {
> > > > > +		printf("Cannot append media vendor device path path\n");
> > > > > +		goto out;
> > > > > +	}
> > > > > +	final_fp = efi_dp_concat(fp, initrd_dp);
> > > > > +	*fp_size = efi_dp_size(fp) + efi_dp_size(initrd_dp) +
> > > > > +		(2 * sizeof(struct efi_device_path));
> > > > > +
> > > > > +out:
> > > > > +	efi_free_pool(initrd_dp);
> > > > > +	efi_free_pool(tmp_dp);
> > > > > +	efi_free_pool(tmp_fp);
> > > > > +	return final_fp ? final_fp : ERR_PTR(-EINVAL);
> > > > > +}
> > > > > +
> > > > >  /**
> > > > >   * do_efi_boot_add() - set UEFI load option
> > > > >   *
> > > > > @@ -806,7 +868,9 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
> > > > >   *
> > > > >   * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
> > > > >   *
> > > > > - *     efidebug boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
> > > > > + * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
> > > > > + *                   -i <file> <interface2> <devnum2>[:<part>] <initrd>
> > > > > + *                   -s '<options>'
> > > > 
> > > > We discussed another syntax:
> > > > efidebug boot add <id> ...
> > > > efidebug boot add-initrd <id> <initrd path>
> > > > (Please don't care detailed syntax for now.)
> > > 
> > > Yep and I completely agree this is a better format but ...
> > > 
> > > > 
> > > > What is the difficulty that you have had to implement this type of
> > > > interface?
> > > 
> > > The problem is that the initrd and kernel eventually go into *one* Boot####
> > > variable.  So it's much easier to treat them in a single command as a bundle.
> > > 
> > > Otherwise you'll have to start adding checks on the initrd code to make
> > > sure the Boot### variable exists before you append an initrd.
> > > Then you have to deserialize the existing stored device path in Boot####,
> > > append the initrd and serialize it again (and last time I checked this is not
> > > as easy as it sounds).
> > 
> > If we take the format like:
> >   kernel path,end(0xff),
> >   VenMedia(INITRD),initrd1 path,end(0xff),
> >   VenMedia(INITRD),initrd2 path,end(0xff),
> > 
> > all that we have to do is
> > - deserialize the boot option variable,
> > - append initrd device path to a list (after kernel device path),
> > - serialize all the option together,
> 
> Sure but the serialize/deserialize is not as easy as it sounds and requires
> code as well for the optional data etc.

I don't still understand why it's not so easy.
Because I'm the author of that function?

> Again I am not saying it's not doable, or even more elegant.  I am saying the
> extra code doesn't seemt to worth the effort right now. Especially since we
> support a *single* initrd on the loading anyway and no dtbs.

Better user interface would pay the efforts of implementation.

> > 
> > Appending is quite simple, isn't it?
> > (because we will not have to parse a device path list.)
> > 
> 
> Yes and i've mentioned most of this on the mailing list. 
> We did choose the other format though...

Simply, who objected it?
I remember that Heinrich has a similar idea.
So who else?

> > > Also what happens if you edit a Boot#### option and that option has an initrd? 
> > If I were you,
> > 
> > > You have to pick up the existing initrd and move it over? Or do we silently 
> > > delete it?
> > > Something like:
> > > efidebug boot add 0001 
> > > efidebug boot add-initrd 0001 
> > > efidebug boot add 0001
> > 
> > even with the current implementation,
> > > efidebug boot add 0001 
> > > efidebug boot add 0001 
> > those sequence will overwrite the existing variable and,
> > deeleting initrd by the second "efidebug boot add" would make sense.
> 
> Yea but that feels 'natural' because it's a single command. Which is also the
> case with the current code. In multiple commands you wouldn't expect the
> initrd to away, unless you knew it was stored in a Boot option.

Well, if it's your concern, we can print a warning, say
  You're trying to rewrite BootXXXX variable (you will lost the existing data).
  Are you sure [y/n]?

But I don't think it's even worth doing so
because you can simply type "Ctrl-p" twice at the command line
if you want to run "efidebug boot add-initrd ..." again :)

-Takahiro Akashi


> 
> Thanks
> /Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 5/6] efidebug: add multiple device path instances on Boot####
  2021-03-12  5:58           ` AKASHI Takahiro
@ 2021-03-12  7:19             ` Ilias Apalodimas
  0 siblings, 0 replies; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-12  7:19 UTC (permalink / raw)
  To: u-boot

Akashi-san,

[...]
> > > > Then you have to deserialize the existing stored device path in Boot####,
> > > > append the initrd and serialize it again (and last time I checked this is not
> > > > as easy as it sounds).
> > > 
> > > If we take the format like:
> > >   kernel path,end(0xff),
> > >   VenMedia(INITRD),initrd1 path,end(0xff),
> > >   VenMedia(INITRD),initrd2 path,end(0xff),
> > > 
> > > all that we have to do is
> > > - deserialize the boot option variable,
> > > - append initrd device path to a list (after kernel device path),
> > > - serialize all the option together,
> > 
> > Sure but the serialize/deserialize is not as easy as it sounds and requires
> > code as well for the optional data etc.
> 
> I don't still understand why it's not so easy.
> Because I'm the author of that function?

You have to deserialize the load option, split it on the device path end node,
attach a new device path, and append the optional data, while doing the
necessary utf8/16 conversions at the same time. The alternative is do nothing
at all and serialize it in one go.  It's not hard, but it's not something it 
would be my first priority to code. Especially if 99.9% of the use cases will 
have a single initrd.

> 
> > Again I am not saying it's not doable, or even more elegant.  I am saying the
> > extra code doesn't seemt to worth the effort right now. Especially since we
> > support a *single* initrd on the loading anyway and no dtbs.
> 
> Better user interface would pay the efforts of implementation.
> 
> > > 
> > > Appending is quite simple, isn't it?
> > > (because we will not have to parse a device path list.)
> > > 
> > 
> > Yes and i've mentioned most of this on the mailing list. 
> > We did choose the other format though...
> 
> Simply, who objected it?
> I remember that Heinrich has a similar idea.
> So who else?

Again that proposal is part of my mail as well. 
Heinrich, Samer and I agreed this is the format we want to use since
since you dont need to replicate the VenMEdia node for each array
element.

> 
> > > > Also what happens if you edit a Boot#### option and that option has an initrd? 
> > > If I were you,
> > > 
> > > > You have to pick up the existing initrd and move it over? Or do we silently 
> > > > delete it?
> > > > Something like:
> > > > efidebug boot add 0001 
> > > > efidebug boot add-initrd 0001 
> > > > efidebug boot add 0001
> > > 
> > > even with the current implementation,
> > > > efidebug boot add 0001 
> > > > efidebug boot add 0001 
> > > those sequence will overwrite the existing variable and,
> > > deeleting initrd by the second "efidebug boot add" would make sense.
> > 
> > Yea but that feels 'natural' because it's a single command. Which is also the
> > case with the current code. In multiple commands you wouldn't expect the
> > initrd to away, unless you knew it was stored in a Boot option.
> 
> Well, if it's your concern, we can print a warning, say
>   You're trying to rewrite BootXXXX variable (you will lost the existing data).
>   Are you sure [y/n]?
> 
> But I don't think it's even worth doing so
> because you can simply type "Ctrl-p" twice at the command line
> if you want to run "efidebug boot add-initrd ..." again :)

That's the least of my worries tbh. I don't really mind if we delete it
silenmtly, as long as it's documented/justified.

> 
> -Takahiro Akashi
> 
> 
> > 
Thanks
/Ilias

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 5/6] efidebug: add multiple device path instances on Boot####
  2021-03-12  4:44   ` AKASHI Takahiro
  2021-03-12  4:55     ` Ilias Apalodimas
@ 2021-03-12 16:25     ` Heinrich Schuchardt
  1 sibling, 0 replies; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-12 16:25 UTC (permalink / raw)
  To: u-boot

On 12.03.21 05:44, AKASHI Takahiro wrote:
> Ilias,
>
> Sorry, but I will have to repeat my question for better understandings.
>
> On Sat, Mar 06, 2021 at 12:23:01AM +0200, Ilias Apalodimas wrote:
>> The UEFI spec allow a packed array of UEFI device paths in the
>> FilePathList[] of an EFI_LOAD_OPTION. The first file path must
>> describe the loaded image but the rest are OS specific.
>>
>> Previous patches parse the device path and try to use the second
>> member of the array as an initrd. So let's modify efidebug slightly
>> and install the second file described in the command line as the
>> initrd device path.
>>
>> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
>> ---
>>  cmd/efidebug.c                                | 193 ++++++++++++++----
>>  doc/board/emulation/qemu_capsule_update.rst   |   4 +-
>>  doc/uefi/uefi.rst                             |   2 +-
>>  .../test_efi_capsule/test_capsule_firmware.py |   6 +-
>>  test/py/tests/test_efi_secboot/test_signed.py |  16 +-
>>  .../test_efi_secboot/test_signed_intca.py     |   8 +-
>>  .../tests/test_efi_secboot/test_unsigned.py   |   8 +-
>>  7 files changed, 179 insertions(+), 58 deletions(-)
>>
>> diff --git a/cmd/efidebug.c b/cmd/efidebug.c
>> index bbbcb0a54643..9a1c471a3eaa 100644
>> --- a/cmd/efidebug.c
>> +++ b/cmd/efidebug.c
>> @@ -9,6 +9,8 @@
>>  #include <common.h>
>>  #include <command.h>
>>  #include <efi_dt_fixup.h>
>> +#include <efi_helper.h>
>> +#include <efi_load_initrd.h>
>>  #include <efi_loader.h>
>>  #include <efi_rng.h>
>>  #include <exports.h>
>> @@ -19,6 +21,7 @@
>>  #include <part.h>
>>  #include <search.h>
>>  #include <linux/ctype.h>
>> +#include <linux/err.h>
>>
>>  #define BS systab.boottime
>>  #define RT systab.runtime
>> @@ -794,6 +797,65 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
>>  	return CMD_RET_SUCCESS;
>>  }
>>
>> +/**
>> + * add_initrd_instance() - Append a device path to load_options pointing to an
>> + *			   inirtd
>> + *
>> + * @argc:	Number of arguments
>> + * @argv:	Argument array
>> + * @file_path	Existing device path, the new instance will be appended
>> + * Return:	Pointer to the device path or ERR_PTR
>> + *
>> + */
>> +static
>> +struct efi_device_path *add_initrd_instance(const char *dev, const char *part,
>> +					    const char *file,
>> +					    const struct efi_device_path *fp,
>> +					    efi_uintn_t *fp_size)
>> +{
>> +	struct efi_device_path *tmp_dp = NULL, *tmp_fp = NULL;
>> +	struct efi_device_path *final_fp = NULL, *initrd_dp = NULL;
>> +	efi_status_t ret;
>> +	const struct efi_initrd_dp id_dp = {
>> +		.vendor = {
>> +			{
>> +			DEVICE_PATH_TYPE_MEDIA_DEVICE,
>> +			DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
>> +			sizeof(id_dp.vendor),
>> +			},
>> +			EFI_INITRD_MEDIA_GUID,
>> +		},
>> +		.end = {
>> +			DEVICE_PATH_TYPE_END,
>> +			DEVICE_PATH_SUB_TYPE_END,
>> +			sizeof(id_dp.end),
>> +		}
>> +	};
>> +
>> +	ret = efi_dp_from_name(dev, part, file, &tmp_dp, &tmp_fp);
>> +	if (ret != EFI_SUCCESS) {
>> +		printf("Cannot create device path for \"%s %s\"\n", part, file);
>> +		goto out;
>> +	}
>> +
>> +	initrd_dp =
>> +		efi_dp_append_instance((const struct efi_device_path *)&id_dp,
>> +				       tmp_fp);
>> +	if (!initrd_dp) {
>> +		printf("Cannot append media vendor device path path\n");
>> +		goto out;
>> +	}
>> +	final_fp = efi_dp_concat(fp, initrd_dp);
>> +	*fp_size = efi_dp_size(fp) + efi_dp_size(initrd_dp) +
>> +		(2 * sizeof(struct efi_device_path));
>> +
>> +out:
>> +	efi_free_pool(initrd_dp);
>> +	efi_free_pool(tmp_dp);
>> +	efi_free_pool(tmp_fp);
>> +	return final_fp ? final_fp : ERR_PTR(-EINVAL);
>> +}
>> +
>>  /**
>>   * do_efi_boot_add() - set UEFI load option
>>   *
>> @@ -806,7 +868,9 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
>>   *
>>   * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
>>   *
>> - *     efidebug boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
>> + * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
>> + *                   -i <file> <interface2> <devnum2>[:<part>] <initrd>
>> + *                   -s '<options>'
>
> We discussed another syntax:
> efidebug boot add <id> ...
> efidebug boot add-initrd <id> <initrd path>
> (Please don't care detailed syntax for now.)

Thanks for revieweing.

Ilias and I discussed the syntax several times. As Ilias pointed out the
code and the test cases will be a bit simpler if there is only one command.

There are less corner cases that we have to test like what happens if
add-initrd is called without a prior definition of a binary.

I think using multiple commands is harder for the user to comprehend.

Setting the Boot#### variables will typically done from Linux when a new
kernel arrives or by a capsule update. The efidebug command will be less
common.

I prefer to stick with Ilias suggestion.

>
> What is the difficulty that you have had to implement this type of
> interface?
>
> Even if we follow your new syntax,
> Why do we need '-b' option?
> "<id> <label> <interface> <devnum>[:<part>] <file>" are all mandatory arguments,
> aren't they?

Yes, a boot option without binary is not allowable. There is no real
need for '-b'.

We had the same discussion for requiring -bs when setting a variable
with setenv -e which is equally superfluous where you insisted on adding
the parameter for clarity.

Same here. For clarity -b is nice. For code size reduction dropping it
makes sense.

Best regards

Heinrich

>
> -Takahiro Akashi
>
>
>
>>   */
>>  static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
>>  			   int argc, char *const argv[])
>> @@ -819,55 +883,98 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
>>  	size_t label_len, label_len16;
>>  	u16 *label;
>>  	struct efi_device_path *device_path = NULL, *file_path = NULL;
>> +	struct efi_device_path *final_fp = NULL;
>>  	struct efi_load_option lo;
>>  	void *data = NULL;
>>  	efi_uintn_t size;
>> +	efi_uintn_t fp_size;
>>  	efi_status_t ret;
>>  	int r = CMD_RET_SUCCESS;
>> -
>> -	if (argc < 6 || argc > 7)
>> -		return CMD_RET_USAGE;
>> -
>> -	id = (int)simple_strtoul(argv[1], &endp, 16);
>> -	if (*endp != '\0' || id > 0xffff)
>> -		return CMD_RET_USAGE;
>> -
>> -	sprintf(var_name, "Boot%04X", id);
>> -	p = var_name16;
>> -	utf8_utf16_strncpy(&p, var_name, 9);
>> +	int i;
>>
>>  	guid = efi_global_variable_guid;
>>
>>  	/* attributes */
>>  	lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */
>> +	lo.optional_data = NULL;
>> +	lo.label = NULL;
>>
>> -	/* label */
>> -	label_len = strlen(argv[2]);
>> -	label_len16 = utf8_utf16_strnlen(argv[2], label_len);
>> -	label = malloc((label_len16 + 1) * sizeof(u16));
>> -	if (!label)
>> -		return CMD_RET_FAILURE;
>> -	lo.label = label; /* label will be changed below */
>> -	utf8_utf16_strncpy(&label, argv[2], label_len);
>> +	/* search for -b first since the rest of the arguments depends on that */
>> +	for (i = 0; i < argc; i++) {
>> +		if (!strcmp(argv[i], "-b")) {
>> +			if (argc < i + 5 || lo.label) {
>> +				r = CMD_RET_USAGE;
>> +				goto out;
>> +			}
>> +			id = (int)simple_strtoul(argv[i + 1], &endp, 16);
>> +			if (*endp != '\0' || id > 0xffff)
>> +				return CMD_RET_USAGE;
>> +
>> +			sprintf(var_name, "Boot%04X", id);
>> +			p = var_name16;
>> +			utf8_utf16_strncpy(&p, var_name, 9);
>> +
>> +			/* label */
>> +			label_len = strlen(argv[i + 2]);
>> +			label_len16 = utf8_utf16_strnlen(argv[i + 2], label_len);
>> +			label = malloc((label_len16 + 1) * sizeof(u16));
>> +			if (!label)
>> +				return CMD_RET_FAILURE;
>> +			lo.label = label; /* label will be changed below */
>> +			utf8_utf16_strncpy(&label, argv[i + 2], label_len);
>> +
>> +			/* file path */
>> +			ret = efi_dp_from_name(argv[i + 3], argv[i + 4],
>> +					       argv[i + 5], &device_path,
>> +					       &file_path);
>> +			if (ret != EFI_SUCCESS) {
>> +				printf("Cannot create device path for \"%s %s\"\n",
>> +				       argv[3], argv[4]);
>> +				r = CMD_RET_FAILURE;
>> +				goto out;
>> +			break;
>> +			}
>> +			fp_size = efi_dp_size(file_path) +
>> +				sizeof(struct efi_device_path);
>> +		}
>> +	}
>>
>> -	/* file path */
>> -	ret = efi_dp_from_name(argv[3], argv[4], argv[5], &device_path,
>> -			       &file_path);
>> -	if (ret != EFI_SUCCESS) {
>> -		printf("Cannot create device path for \"%s %s\"\n",
>> -		       argv[3], argv[4]);
>> +	if (!file_path) {
>> +		printf("You need to specify an image before an initrd.\n");
>>  		r = CMD_RET_FAILURE;
>>  		goto out;
>>  	}
>> -	lo.file_path = file_path;
>> -	lo.file_path_length = efi_dp_size(file_path)
>> -				+ sizeof(struct efi_device_path); /* for END */
>>
>> -	/* optional data */
>> -	if (argc == 6)
>> -		lo.optional_data = NULL;
>> -	else
>> -		lo.optional_data = (const u8 *)argv[6];
>> +	/* add now add initrd and extra data */
>> +	for (i = 0; i < argc; i++) {
>> +		if (!strcmp(argv[i], "-i")) {
>> +			if (argc < i + 3 || final_fp) {
>> +				r = CMD_RET_USAGE;
>> +				goto out;
>> +			}
>> +
>> +			final_fp = add_initrd_instance(argv[i + 1], argv[i + 2],
>> +						       argv[i + 3], file_path,
>> +						       &fp_size);
>> +			if (IS_ERR(final_fp)) {
>> +				r = CMD_RET_FAILURE;
>> +				goto out;
>> +			}
>> +
>> +			/* add_initrd_instance allocates a new device path */
>> +			efi_free_pool(file_path);
>> +			file_path = final_fp;
>> +		} else if (!strcmp(argv[i], "-s")) {
>> +			if (argc < i + 1 || lo.optional_data) {
>> +				r = CMD_RET_USAGE;
>> +				goto out;
>> +			}
>> +			lo.optional_data = (const u8 *)argv[i + 1];
>> +		}
>> +	}
>> +
>> +	lo.file_path = file_path;
>> +	lo.file_path_length = fp_size;
>>
>>  	size = efi_serialize_load_option(&lo, (u8 **)&data);
>>  	if (!size) {
>> @@ -951,11 +1058,14 @@ static int do_efi_boot_rm(struct cmd_tbl *cmdtp, int flag,
>>   */
>>  static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
>>  {
>> +	struct efi_device_path *initrd_path = NULL;
>>  	struct efi_load_option lo;
>>  	char *label, *p;
>>  	size_t label_len16, label_len;
>>  	u16 *dp_str;
>>  	efi_status_t ret;
>> +	efi_uintn_t initrd_dp_size;
>> +	const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
>>
>>  	ret = efi_deserialize_load_option(&lo, data, size);
>>  	if (ret != EFI_SUCCESS) {
>> @@ -986,6 +1096,14 @@ static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
>>  	printf("  file_path: %ls\n", dp_str);
>>  	efi_free_pool(dp_str);
>>
>> +	initrd_path = efi_dp_from_lo(&lo, &initrd_dp_size, lf2_initrd_guid);
>> +	if (initrd_path) {
>> +		dp_str = efi_dp_str(initrd_path);
>> +		printf("  initrd_path: %ls\n", dp_str);
>> +		efi_free_pool(dp_str);
>> +		efi_free_pool(initrd_path);
>> +	}
>> +
>>  	printf("  data:\n");
>>  	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
>>  		       lo.optional_data, *size, true);
>> @@ -1555,7 +1673,10 @@ static int do_efidebug(struct cmd_tbl *cmdtp, int flag,
>>  static char efidebug_help_text[] =
>>  	"  - UEFI Shell-like interface to configure UEFI environment\n"
>>  	"\n"
>> -	"efidebug boot add <bootid> <label> <interface> <devnum>[:<part>] <file path> [<load options>]\n"
>> +	"efidebug boot add "
>> +	"-b <bootid> <label> <interface> <devnum>[:<part>] <file path> "
>> +	"-i <interface> <devnum>[:<part>] <initrd file path> "
>> +	"-s '<optional data>'\n"
>>  	"  - set UEFI BootXXXX variable\n"
>>  	"    <load options> will be passed to UEFI application\n"
>>  	"efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n"
>> @@ -1599,7 +1720,7 @@ static char efidebug_help_text[] =
>>  #endif
>>
>>  U_BOOT_CMD(
>> -	efidebug, 10, 0, do_efidebug,
>> +	efidebug, CONFIG_SYS_MAXARGS, 0, do_efidebug,
>>  	"Configure UEFI environment",
>>  	efidebug_help_text
>>  );
>> diff --git a/doc/board/emulation/qemu_capsule_update.rst b/doc/board/emulation/qemu_capsule_update.rst
>> index 9fec75f8f1c9..33ce4bcd32ea 100644
>> --- a/doc/board/emulation/qemu_capsule_update.rst
>> +++ b/doc/board/emulation/qemu_capsule_update.rst
>> @@ -60,7 +60,7 @@ to be pointing to the EFI System Partition which contains the capsule
>>  file. The BootNext, BootXXXX and OsIndications variables can be set
>>  using the following commands::
>>
>> -    => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
>> +    => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
>>      => efidebug boot next 0
>>      => setenv -e -nv -bs -rt -v OsIndications =0x04
>>      => saveenv
>> @@ -198,7 +198,7 @@ command line::
>>      3. Set the following environment and UEFI boot variables
>>
>>          => setenv -e -nv -bs -rt -v OsIndications =0x04
>> -        => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
>> +        => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
>>          => efidebug boot next 0
>>          => saveenv
>>
>> diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst
>> index 5a67737c1579..b3494c22e073 100644
>> --- a/doc/uefi/uefi.rst
>> +++ b/doc/uefi/uefi.rst
>> @@ -178,7 +178,7 @@ Now in U-Boot install the keys on your board::
>>
>>  Set up boot parameters on your board::
>>
>> -    efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed ""
>> +    efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed ""
>>
>>  Now your board can run the signed image via the boot manager (see below).
>>  You can also try this sequence by running Pytest, test_efi_secboot,
>> diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
>> index f006fa95d650..e8b0a1575453 100644
>> --- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py
>> +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
>> @@ -39,7 +39,7 @@ class TestEfiCapsuleFirmwareFit(object):
>>          with u_boot_console.log.section('Test Case 1-a, before reboot'):
>>              output = u_boot_console.run_command_list([
>>                  'host bind 0 %s' % disk_img,
>> -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
>> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
>>                  'efidebug boot order 1',
>>                  'env set -e OsIndications',
>>                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
>> @@ -114,7 +114,7 @@ class TestEfiCapsuleFirmwareFit(object):
>>          with u_boot_console.log.section('Test Case 2-a, before reboot'):
>>              output = u_boot_console.run_command_list([
>>                  'host bind 0 %s' % disk_img,
>> -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
>> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
>>                  'efidebug boot order 1',
>>                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
>>                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
>> @@ -188,7 +188,7 @@ class TestEfiCapsuleFirmwareFit(object):
>>          with u_boot_console.log.section('Test Case 3-a, before reboot'):
>>              output = u_boot_console.run_command_list([
>>                  'host bind 0 %s' % disk_img,
>> -                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
>> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
>>                  'efidebug boot order 1',
>>                  'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
>>                  'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
>> diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py
>> index 863685e215b7..75f5ea772300 100644
>> --- a/test/py/tests/test_efi_secboot/test_signed.py
>> +++ b/test/py/tests/test_efi_secboot/test_signed.py
>> @@ -28,7 +28,7 @@ class TestEfiSignedImage(object):
>>              # Test Case 1a, run signed image if no PK
>>              output = u_boot_console.run_command_list([
>>                  'host bind 0 %s' % disk_img,
>> -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
>> +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
>>                  'efidebug boot next 1',
>>                  'bootefi bootmgr'])
>>              assert 'Hello, world!' in ''.join(output)
>> @@ -36,7 +36,7 @@ class TestEfiSignedImage(object):
>>          with u_boot_console.log.section('Test Case 1b'):
>>              # Test Case 1b, run unsigned image if no PK
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
>> +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
>>                  'efidebug boot next 2',
>>                  'bootefi bootmgr'])
>>              assert 'Hello, world!' in ''.join(output)
>> @@ -58,13 +58,13 @@ class TestEfiSignedImage(object):
>>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
>> +                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
>>                  'efidebug boot next 1',
>>                  'efidebug test bootmgr'])
>>              assert('\'HELLO1\' failed' in ''.join(output))
>>              assert('efi_start_image() returned: 26' in ''.join(output))
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
>> +                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
>>                  'efidebug boot next 2',
>>                  'efidebug test bootmgr'])
>>              assert '\'HELLO2\' failed' in ''.join(output)
>> @@ -104,7 +104,7 @@ class TestEfiSignedImage(object):
>>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
>> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
>>                  'efidebug boot next 1',
>>                  'efidebug test bootmgr'])
>>              assert '\'HELLO\' failed' in ''.join(output)
>> @@ -142,7 +142,7 @@ class TestEfiSignedImage(object):
>>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
>> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
>>                  'efidebug boot next 1',
>>                  'efidebug test bootmgr'])
>>              assert '\'HELLO\' failed' in ''.join(output)
>> @@ -169,7 +169,7 @@ class TestEfiSignedImage(object):
>>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
>> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
>>                  'efidebug boot next 1',
>>                  'efidebug test bootmgr'])
>>              assert 'Hello, world!' in ''.join(output)
>> @@ -227,7 +227,7 @@ class TestEfiSignedImage(object):
>>                  'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
>> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
>>                  'efidebug boot next 1',
>>                  'bootefi bootmgr'])
>>              assert 'Hello, world!' in ''.join(output)
>> diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py
>> index 70d6be00e8a8..0849572a5143 100644
>> --- a/test/py/tests/test_efi_secboot/test_signed_intca.py
>> +++ b/test/py/tests/test_efi_secboot/test_signed_intca.py
>> @@ -39,7 +39,7 @@ class TestEfiSignedImageIntca(object):
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
>> +                'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
>>                  'efidebug boot next 1',
>>                  'efidebug test bootmgr'])
>>              assert '\'HELLO_a\' failed' in ''.join(output)
>> @@ -48,7 +48,7 @@ class TestEfiSignedImageIntca(object):
>>          with u_boot_console.log.section('Test Case 1b'):
>>              # Test Case 1b, signed and authenticated by root CA
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
>> +                'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
>>                  'efidebug boot next 2',
>>                  'bootefi bootmgr'])
>>              assert 'Hello, world!' in ''.join(output)
>> @@ -70,7 +70,7 @@ class TestEfiSignedImageIntca(object):
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
>> +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
>>                  'efidebug boot next 1',
>>                  'efidebug test bootmgr'])
>>              assert '\'HELLO_abc\' failed' in ''.join(output)
>> @@ -116,7 +116,7 @@ class TestEfiSignedImageIntca(object):
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
>> +                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
>>                  'efidebug boot next 1',
>>                  'efidebug test bootmgr'])
>>              assert 'Hello, world!' in ''.join(output)
>> diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py
>> index 56f56e19eb84..8e026f7566ad 100644
>> --- a/test/py/tests/test_efi_secboot/test_unsigned.py
>> +++ b/test/py/tests/test_efi_secboot/test_unsigned.py
>> @@ -35,7 +35,7 @@ class TestEfiUnsignedImage(object):
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
>> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>>                  'efidebug boot next 1',
>>                  'bootefi bootmgr'])
>>              assert '\'HELLO\' failed' in ''.join(output)
>> @@ -64,7 +64,7 @@ class TestEfiUnsignedImage(object):
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
>> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>>                  'efidebug boot next 1',
>>                  'bootefi bootmgr'])
>>              assert 'Hello, world!' in ''.join(output)
>> @@ -88,7 +88,7 @@ class TestEfiUnsignedImage(object):
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
>> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>>                  'efidebug boot next 1',
>>                  'bootefi bootmgr'])
>>              assert '\'HELLO\' failed' in ''.join(output)
>> @@ -106,7 +106,7 @@ class TestEfiUnsignedImage(object):
>>              assert 'Failed to set EFI variable' not in ''.join(output)
>>
>>              output = u_boot_console.run_command_list([
>> -                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
>> +                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
>>                  'efidebug boot next 1',
>>                  'bootefi bootmgr'])
>>              assert '\'HELLO\' failed' in ''.join(output)
>> --
>> 2.30.1
>>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-14  7:19   ` Heinrich Schuchardt
@ 2021-03-14  7:32     ` Ilias Apalodimas
  0 siblings, 0 replies; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-14  7:32 UTC (permalink / raw)
  To: u-boot

On Sun, Mar 14, 2021 at 08:19:49AM +0100, Heinrich Schuchardt wrote:
> > + *               Caller must free the returned value

[...]

> > + */
> > +struct
> > +efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
> > +				efi_uintn_t *size, efi_guid_t guid)
> > +{
> > +	struct efi_device_path *fp = lo->file_path;
> > +	struct efi_device_path_vendor *vendor;
> > +	int lo_len = lo->file_path_length;
> > +
> > +	for (; lo_len >=  sizeof(struct efi_device_path);
> > +	     lo_len -= fp->length, fp = (void *)fp + fp->length) {
> > +		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
> > +		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH)
> > +			continue;
> 
> The device path is provided by the user and may be constructed incorrectly.
> 
> lo_len might be negative here. Or the remaining device path might not
> fit into lo_len.
> 
> Function efi_dp_check_length() can be used to check the size but it
> currently accepts only positive values of maxlen. Maybe we should change
> the type of maxlen to ssize() in that function.
> 

Yea, I forgot to fix this one. 

Regards
/Ilias
> Best regards
> 
> Heinrich
> 
> > +
> > +		vendor = (struct efi_device_path_vendor *)fp;
> > +		if (!guidcmp(&vendor->guid, &guid))
> > +			return efi_dp_dup(fp);
> > +	}
> > +	log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label);
> > +
> > +	return NULL;
> > +}
> > 
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-13 21:47 ` [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot#### Ilias Apalodimas
@ 2021-03-14  7:19   ` Heinrich Schuchardt
  2021-03-14  7:32     ` Ilias Apalodimas
  0 siblings, 1 reply; 41+ messages in thread
From: Heinrich Schuchardt @ 2021-03-14  7:19 UTC (permalink / raw)
  To: u-boot

On 3/13/21 10:47 PM, Ilias Apalodimas wrote:
> On the following patches we allow for an initrd path to be stored in
> Boot#### variables.  Specifically we encode in the FIlePathList[] of
> the EFI_LOAD_OPTIONS for each Boot#### variable.
>
> The FilePathList[] array looks like this:
> kernel - 0xff - VenMedia(initrd GUID) - initrd1 - 0x01 initrd2 - 0xff
> So let's add the relevant functions to concatenate and retrieve a device
> path based on a Vendor GUID.
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> ---
>   include/efi_loader.h             |  4 ++
>   lib/efi_loader/efi_device_path.c | 99 ++++++++++++++++++++++++++++++--
>   2 files changed, 98 insertions(+), 5 deletions(-)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index f470bbd636f4..eb11a8c7d4b1 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -738,6 +738,10 @@ struct efi_load_option {
>   	const u8 *optional_data;
>   };
>
> +struct efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
> +				       efi_uintn_t *size, efi_guid_t guid);
> +struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
> +				      const struct efi_device_path *dp2);
>   efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
>   					 efi_uintn_t *size);
>   unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
> diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
> index c9315dd45857..1f55c772dc83 100644
> --- a/lib/efi_loader/efi_device_path.c
> +++ b/lib/efi_loader/efi_device_path.c
> @@ -282,11 +282,25 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
>   	return ndp;
>   }
>
> -struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
> -				      const struct efi_device_path *dp2)
> +/** _efi_dp_append() - Append or concatenate two device paths.

Please, add an empty line.

> + *                     Concatenated device path will be separated by a 0xff end

... by a sub-type 0xff end node.

> + *                     node.
> + *
> + * @dp1:	First device path
> + * @dp2:	Second device path

Please, describe argument @concat.

> + *
> + * Return:	concatenated device path or NULL. Caller must free the returned
> + *              value
> + */
> +static struct efi_device_path *_efi_dp_append(const struct efi_device_path *dp1,

This function name is very close to efi_dp_append. Maybe
efi_dp_append_or_concatenate() would be a better function name.

> +					      const struct efi_device_path *dp2,
> +					      bool concat)
>   {
>   	struct efi_device_path *ret;
> +	size_t end_size = sizeof(END);
>
> +	if (concat)
> +		end_size = 2 * sizeof(END);
>   	if (!dp1 && !dp2) {
>   		/* return an end node */
>   		ret = efi_dp_dup(&END);
> @@ -298,18 +312,56 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
>   		/* both dp1 and dp2 are non-null */
>   		unsigned sz1 = efi_dp_size(dp1);
>   		unsigned sz2 = efi_dp_size(dp2);
> -		void *p = dp_alloc(sz1 + sz2 + sizeof(END));
> +		void *p = dp_alloc(sz1 + sz2 + end_size);
>   		if (!p)
>   			return NULL;
> +		ret = p;
>   		memcpy(p, dp1, sz1);
> +		p += sz1;
> +
> +		if (concat) {
> +			memcpy(p, &END, sizeof(END));
> +			p += sizeof(END);
> +		}
> +
>   		/* the end node of the second device path has to be retained */
> -		memcpy(p + sz1, dp2, sz2 + sizeof(END));
> -		ret = p;
> +		memcpy(p, dp2, sz2);
> +		p += sz2;
> +		memcpy(p, &END, sizeof(END));
>   	}
>
>   	return ret;
>   }
>
> +/** efi_dp_append() - Append a device to an existing device path.
> + *
> + * @dp1:	First device path
> + * @dp2:	Second device path
> + *
> + * Return:	concatenated device path or NULL. Caller must free the returned
> + *              value
> + */
> +struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
> +				      const struct efi_device_path *dp2)
> +{
> +	return _efi_dp_append(dp1, dp2, false);
> +}
> +
> +/** efi_dp_concat() - Concatenate 2 device paths. The final device path will
> + *                    contain two device paths separated by and end node (0xff).
> + *
> + * @dp1:	First device path
> + * @dp2:	Second device path
> + *
> + * Return:	concatenated device path or NULL. Caller must free the returned
> + *              value
> + */
> +struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
> +				      const struct efi_device_path *dp2)
> +{
> +	return _efi_dp_append(dp1, dp2, true);
> +}
> +
>   struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
>   					   const struct efi_device_path *node)
>   {
> @@ -1160,3 +1212,40 @@ ssize_t efi_dp_check_length(const struct efi_device_path *dp,
>   		dp = (const struct efi_device_path *)((const u8 *)dp + len);
>   	}
>   }
> +
> +/**
> + * efi_dp_from_lo() - Get the instance of a VenMedia node in a
> + *                    multi-instance device path that matches
> + *                    a specific GUID. This kind of device paths
> + *                    is found in Boot#### options describing an
> + *                    initrd location
> + *
> + * @load_option: EFI_LOAD_OPTION containing a valid device path
> + * @size:	 size of the discovered device path
> + * @guid:	 guid to search for
> + *
> + * Return:	 device path including the VenMedia node or NULL.
> + *               Caller must free the returned value
> + */
> +struct
> +efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
> +				efi_uintn_t *size, efi_guid_t guid)
> +{
> +	struct efi_device_path *fp = lo->file_path;
> +	struct efi_device_path_vendor *vendor;
> +	int lo_len = lo->file_path_length;
> +
> +	for (; lo_len >=  sizeof(struct efi_device_path);
> +	     lo_len -= fp->length, fp = (void *)fp + fp->length) {
> +		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
> +		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH)
> +			continue;

The device path is provided by the user and may be constructed incorrectly.

lo_len might be negative here. Or the remaining device path might not
fit into lo_len.

Function efi_dp_check_length() can be used to check the size but it
currently accepts only positive values of maxlen. Maybe we should change
the type of maxlen to ssize() in that function.

Best regards

Heinrich

> +
> +		vendor = (struct efi_device_path_vendor *)fp;
> +		if (!guidcmp(&vendor->guid, &guid))
> +			return efi_dp_dup(fp);
> +	}
> +	log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label);
> +
> +	return NULL;
> +}
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot####
  2021-03-13 21:47 [PATCH 0/6 v2] Loadfile2 for initrd loading Ilias Apalodimas
@ 2021-03-13 21:47 ` Ilias Apalodimas
  2021-03-14  7:19   ` Heinrich Schuchardt
  0 siblings, 1 reply; 41+ messages in thread
From: Ilias Apalodimas @ 2021-03-13 21:47 UTC (permalink / raw)
  To: u-boot

On the following patches we allow for an initrd path to be stored in
Boot#### variables.  Specifically we encode in the FIlePathList[] of
the EFI_LOAD_OPTIONS for each Boot#### variable.

The FilePathList[] array looks like this:
kernel - 0xff - VenMedia(initrd GUID) - initrd1 - 0x01 initrd2 - 0xff
So let's add the relevant functions to concatenate and retrieve a device
path based on a Vendor GUID.

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
 include/efi_loader.h             |  4 ++
 lib/efi_loader/efi_device_path.c | 99 ++++++++++++++++++++++++++++++--
 2 files changed, 98 insertions(+), 5 deletions(-)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index f470bbd636f4..eb11a8c7d4b1 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -738,6 +738,10 @@ struct efi_load_option {
 	const u8 *optional_data;
 };
 
+struct efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
+				       efi_uintn_t *size, efi_guid_t guid);
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+				      const struct efi_device_path *dp2);
 efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
 					 efi_uintn_t *size);
 unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index c9315dd45857..1f55c772dc83 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -282,11 +282,25 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
 	return ndp;
 }
 
-struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
-				      const struct efi_device_path *dp2)
+/** _efi_dp_append() - Append or concatenate two device paths.
+ *                     Concatenated device path will be separated by a 0xff end
+ *                     node.
+ *
+ * @dp1:	First device path
+ * @dp2:	Second device path
+ *
+ * Return:	concatenated device path or NULL. Caller must free the returned
+ *              value
+ */
+static struct efi_device_path *_efi_dp_append(const struct efi_device_path *dp1,
+					      const struct efi_device_path *dp2,
+					      bool concat)
 {
 	struct efi_device_path *ret;
+	size_t end_size = sizeof(END);
 
+	if (concat)
+		end_size = 2 * sizeof(END);
 	if (!dp1 && !dp2) {
 		/* return an end node */
 		ret = efi_dp_dup(&END);
@@ -298,18 +312,56 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
 		/* both dp1 and dp2 are non-null */
 		unsigned sz1 = efi_dp_size(dp1);
 		unsigned sz2 = efi_dp_size(dp2);
-		void *p = dp_alloc(sz1 + sz2 + sizeof(END));
+		void *p = dp_alloc(sz1 + sz2 + end_size);
 		if (!p)
 			return NULL;
+		ret = p;
 		memcpy(p, dp1, sz1);
+		p += sz1;
+
+		if (concat) {
+			memcpy(p, &END, sizeof(END));
+			p += sizeof(END);
+		}
+
 		/* the end node of the second device path has to be retained */
-		memcpy(p + sz1, dp2, sz2 + sizeof(END));
-		ret = p;
+		memcpy(p, dp2, sz2);
+		p += sz2;
+		memcpy(p, &END, sizeof(END));
 	}
 
 	return ret;
 }
 
+/** efi_dp_append() - Append a device to an existing device path.
+ *
+ * @dp1:	First device path
+ * @dp2:	Second device path
+ *
+ * Return:	concatenated device path or NULL. Caller must free the returned
+ *              value
+ */
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+				      const struct efi_device_path *dp2)
+{
+	return _efi_dp_append(dp1, dp2, false);
+}
+
+/** efi_dp_concat() - Concatenate 2 device paths. The final device path will
+ *                    contain two device paths separated by and end node (0xff).
+ *
+ * @dp1:	First device path
+ * @dp2:	Second device path
+ *
+ * Return:	concatenated device path or NULL. Caller must free the returned
+ *              value
+ */
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+				      const struct efi_device_path *dp2)
+{
+	return _efi_dp_append(dp1, dp2, true);
+}
+
 struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
 					   const struct efi_device_path *node)
 {
@@ -1160,3 +1212,40 @@ ssize_t efi_dp_check_length(const struct efi_device_path *dp,
 		dp = (const struct efi_device_path *)((const u8 *)dp + len);
 	}
 }
+
+/**
+ * efi_dp_from_lo() - Get the instance of a VenMedia node in a
+ *                    multi-instance device path that matches
+ *                    a specific GUID. This kind of device paths
+ *                    is found in Boot#### options describing an
+ *                    initrd location
+ *
+ * @load_option: EFI_LOAD_OPTION containing a valid device path
+ * @size:	 size of the discovered device path
+ * @guid:	 guid to search for
+ *
+ * Return:	 device path including the VenMedia node or NULL.
+ *               Caller must free the returned value
+ */
+struct
+efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
+				efi_uintn_t *size, efi_guid_t guid)
+{
+	struct efi_device_path *fp = lo->file_path;
+	struct efi_device_path_vendor *vendor;
+	int lo_len = lo->file_path_length;
+
+	for (; lo_len >=  sizeof(struct efi_device_path);
+	     lo_len -= fp->length, fp = (void *)fp + fp->length) {
+		if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
+		    fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH)
+			continue;
+
+		vendor = (struct efi_device_path_vendor *)fp;
+		if (!guidcmp(&vendor->guid, &guid))
+			return efi_dp_dup(fp);
+	}
+	log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label);
+
+	return NULL;
+}
-- 
2.30.1

^ permalink raw reply related	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2021-03-14  7:32 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-05 22:22 [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests Ilias Apalodimas
2021-03-05 22:22 ` [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot#### Ilias Apalodimas
2021-03-11  7:50   ` Heinrich Schuchardt
2021-03-11  9:10     ` Ilias Apalodimas
2021-03-11 11:00       ` Heinrich Schuchardt
2021-03-11 11:36         ` Ilias Apalodimas
2021-03-11 11:44           ` Heinrich Schuchardt
2021-03-11 12:31             ` Heinrich Schuchardt
2021-03-11 12:39               ` Ilias Apalodimas
2021-03-11 12:44                 ` Heinrich Schuchardt
2021-03-11 12:49                   ` Ilias Apalodimas
2021-03-11 13:31             ` Ilias Apalodimas
2021-03-11 20:25               ` Heinrich Schuchardt
2021-03-12  2:50           ` AKASHI Takahiro
2021-03-12  4:10             ` Ilias Apalodimas
2021-03-12  4:32               ` AKASHI Takahiro
2021-03-12  4:42                 ` Ilias Apalodimas
2021-03-12  5:02                   ` AKASHI Takahiro
2021-03-12  5:19                     ` Ilias Apalodimas
2021-03-05 22:22 ` [PATCH 3/6] efi_loader: Introduce helper functions for EFI Ilias Apalodimas
2021-03-11  9:15   ` Heinrich Schuchardt
2021-03-05 22:23 ` [PATCH 4/6] efi_loader: Replace config option for initrd loading Ilias Apalodimas
2021-03-11 12:23   ` Heinrich Schuchardt
2021-03-11 12:30     ` Ilias Apalodimas
2021-03-11 12:50       ` Heinrich Schuchardt
2021-03-05 22:23 ` [PATCH 5/6] efidebug: add multiple device path instances on Boot#### Ilias Apalodimas
2021-03-11 12:38   ` Heinrich Schuchardt
2021-03-11 12:42     ` Ilias Apalodimas
2021-03-12  4:44   ` AKASHI Takahiro
2021-03-12  4:55     ` Ilias Apalodimas
2021-03-12  5:23       ` AKASHI Takahiro
2021-03-12  5:37         ` Ilias Apalodimas
2021-03-12  5:58           ` AKASHI Takahiro
2021-03-12  7:19             ` Ilias Apalodimas
2021-03-12 16:25     ` Heinrich Schuchardt
2021-03-05 22:23 ` [PATCH 6/6] doc: Update uefi documentation for initrd loading options Ilias Apalodimas
2021-03-11 12:39   ` Heinrich Schuchardt
2021-03-11  7:26 ` [PATCH 1/6] efi_selftest: Remove loadfile2 for initrd selftests Heinrich Schuchardt
2021-03-13 21:47 [PATCH 0/6 v2] Loadfile2 for initrd loading Ilias Apalodimas
2021-03-13 21:47 ` [PATCH 2/6] efi_loader: Add device path related functions for initrd via Boot#### Ilias Apalodimas
2021-03-14  7:19   ` Heinrich Schuchardt
2021-03-14  7:32     ` Ilias Apalodimas

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.