All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/10] efi_loader: add capsule update support
@ 2020-04-27  9:48 AKASHI Takahiro
  2020-04-27  9:48 ` [PATCH 01/10] efi_loader: disk: add efi_disk_is_system_part() AKASHI Takahiro
                   ` (10 more replies)
  0 siblings, 11 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

Summary
=======
'UpdateCapsule' is one of runtime services defined in UEFI specification
and its aim is to allow a caller (OS) to pass information to the firmware,
i.e. U-Boot. This is mostly used to update firmware binary on devices by
instructions from OS.

While 'UpdateCapsule' is a runtime services function, it is, at least
initially, suported only before exiting boot services alike other runtime
functions, [Get/]SetVariable. This is because modifying storage which may
be shared with OS must be carefully designed and there is no general
assumption that we can do it.

Therefore, we practically support only "capsule on disk"; any capsule can
be handed over to UEFI subsystem as a file on a specific file system.

In this patch series, all the related definitions and structures are given
as UEFI specification describes, and basic framework for capsule support
is provided. Currently supported is
 * firmware update (Firmware Management Protocol or simply FMP)

Most of functionality of firmware update is provided by FMP driver and
it will be, by nature, system/platform-specific. So you can and should
implement your own FMP driver(s) based on your system requirements.
Only a simple FMP driver based on FIT image for a single region is
provided here.  (So it is "for reference only")
                           ^^^^^^^^^^^^^^^^^^
See more details in the commit, "efi_loader: capsule: add simple firmware
management protocol."

Patch structure
===============
Patch#1-#2: preparatory patches
Patch#3-#7: main part of implementation
Patch#8-#10: utilities and tests

[1] https://git.linaro.org/people/takahiro.akashi/u-boot.git efi/capsule

Prerequisite patches
====================
[2] part: detect EFI system partition
    https://lists.denx.de/pipermail/u-boot/2020-March/403562.html
[3] common: update_tftp: remove unnecessary build check
    https://lists.denx.de/pipermail/u-boot/2020-March/401727.html
[4] sandbox: drop CONFIG_SYS_RELOC_GD_ENV_ADDR
    https://lists.denx.de/pipermail/u-boot/2020-April/408711.html

Test
====
* passed all the pytests which are included in this patch series
  on sandbox build.
* passed Travis CI.

Please note that, while Travic CI passed, the capsule pytest
itself won't be run in the CI because some specific configuration
for sandbox build is required. See test_efi_capsule_firmware.py.

Issues
======
* Timing of executing capsules-on-disk
  Currently, processing a capsule is triggered only as part of
  UEFI subsystem initialization. This means that, for example,
  firmware update, may not take place at system booting time and
  will potentially be delayed until a first call of any UEFI functions.

    => See patch#4 for my proposal

TODO's
======
(May not be addressed in future versions of this series.)
* capsule authentication
* capsule dependency (dependency expression instruction set)
* loading drivers in a capsule
* handling RESET flag in a capsule and QeuryCapsuleCaps
* full semantics of ESRT (EFI System Resource Table)
* enabling capsule API at runtime
* json capsule
* recovery from update failure

Changes
=======
v1 (April 27, 2020)
* rebased to v2020.07-rc
* removed already-merged patches (RFC's #1 to #4)
* dropped 'variable update' capsule support (RFC's patch#10)
* dropped 'variable configuration table' support (RFC's patch#11)
  (Those two should be discussed separately.)
* add preparatory patches (patch#1/#2)
* fix several build errors
* rename some Kconfig options to be aligned with UEFI specification's terms
  (patch#3,4,6,7)
* enforce UpdateCapsule API to be disabled after ExitBootServices (patch#3)
* use config table, runtime_services_supported, instead of variable (patch#3)
* make EFI_CAPSULE_ON_DISK buildable even if UpdateCapsule API is disabled
  (patch4)
* support OsIndications, invoking capsule-on-disk only if the variable
  indicates so (patch#4)
* introduced EFI_CAPSULE_ON_DISK_EARLY to invoke capsule-on-disk in U-Boot
  initialization (patch#4)
* detect capsule files only if they are on EFI system partition (patch#4)
* use printf, rather than EFI_PRINT, in error cases (patch#4)
* use 'header_size' field to retrieve capsule data, adding sanity checks
  against capsule size (patch#6)
* call fmpt driver interfaces with EFI_CALL (patch#6)
* remove 'variable update capsule'-related code form mkeficapsule (patch#9)
* add a test case of OsIndications not being set properly (patch#10)
* adjust test scenario for EFI_CAPSULE_ON_DISK_EARLY (patch#10)
* revise pytest scripts (patch#10)

Initial release as RFC (March 17, 2020)

AKASHI Takahiro (10):
  efi_loader: disk: add efi_disk_is_system_part()
  efi_loader: add option to initialise EFI subsystem early
  efi_loader: define UpdateCapsule api
  efi_loader: capsule: add capsule_on_disk support
  efi_loader: capsule: add memory range capsule definitions
  efi_loader: capsule: support firmware update
  efi_loader: add simple firmware management protocol for FIT image
  cmd: add "efidebug capsule" command
  tools: add mkeficapsule command for UEFI capsule update test
  test/py: add a test for efi firmware update capsule

 cmd/efidebug.c                                | 234 +++++
 common/board_r.c                              |   6 +
 common/main.c                                 |   4 +
 include/efi_api.h                             | 156 ++++
 include/efi_loader.h                          |  35 +
 lib/efi_loader/Kconfig                        |  69 ++
 lib/efi_loader/Makefile                       |   2 +
 lib/efi_loader/efi_capsule.c                  | 802 ++++++++++++++++++
 lib/efi_loader/efi_disk.c                     |  22 +
 lib/efi_loader/efi_firmware.c                 | 191 +++++
 lib/efi_loader/efi_runtime.c                  | 104 ++-
 lib/efi_loader/efi_setup.c                    |  44 +-
 test/py/tests/test_efi_capsule/conftest.py    |  73 ++
 test/py/tests/test_efi_capsule/defs.py        |  24 +
 .../test_efi_capsule/test_capsule_firmware.py | 198 +++++
 test/py/tests/test_efi_capsule/uboot_env.its  |  25 +
 tools/Makefile                                |   3 +
 tools/mkeficapsule.c                          | 209 +++++
 18 files changed, 2150 insertions(+), 51 deletions(-)
 create mode 100644 lib/efi_loader/efi_capsule.c
 create mode 100644 lib/efi_loader/efi_firmware.c
 create mode 100644 test/py/tests/test_efi_capsule/conftest.py
 create mode 100644 test/py/tests/test_efi_capsule/defs.py
 create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware.py
 create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its
 create mode 100644 tools/mkeficapsule.c

-- 
2.25.2

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

* [PATCH 01/10] efi_loader: disk: add efi_disk_is_system_part()
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
@ 2020-04-27  9:48 ` AKASHI Takahiro
  2020-04-27 19:57   ` Heinrich Schuchardt
  2020-04-27  9:48 ` [PATCH 02/10] efi_loader: add option to initialise EFI subsystem early AKASHI Takahiro
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

This function will check if a given handle to device is a EFI system
partition. It will be utilised in implementing capsule-on-disk feature.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_loader.h      |  2 ++
 lib/efi_loader/efi_disk.c | 22 ++++++++++++++++++++++
 2 files changed, 24 insertions(+)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index b7bccf50b30c..d4510462d616 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -393,6 +393,8 @@ efi_status_t efi_disk_register(void);
 int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
 			       const char *if_typename, int diskid,
 			       const char *pdevname);
+/* Check if it is EFI system partition */
+bool efi_disk_is_system_part(efi_handle_t handle);
 /* Called by bootefi to make GOP (graphical) interface available */
 efi_status_t efi_gop_register(void);
 /* Called by bootefi to make the network interface available */
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
index fd3df80b0b96..9f58a8642c85 100644
--- a/lib/efi_loader/efi_disk.c
+++ b/lib/efi_loader/efi_disk.c
@@ -588,3 +588,25 @@ efi_status_t efi_disk_register(void)
 
 	return EFI_SUCCESS;
 }
+
+bool efi_disk_is_system_part(efi_handle_t handle)
+{
+	struct efi_handler *handler;
+	struct efi_disk_obj *diskobj;
+	disk_partition_t info;
+	efi_status_t ret;
+	int r;
+
+	/* check if this is a block device */
+	ret = efi_search_protocol(handle, &efi_block_io_guid, &handler);
+	if (ret != EFI_SUCCESS)
+		return false;
+
+	diskobj = container_of(handle, struct efi_disk_obj, header);
+
+	r = part_get_info(diskobj->desc, diskobj->part, &info);
+	if (r)
+		return false;
+
+	return info.bootable & PART_EFI_SYSTEM_PARTITION;
+}
-- 
2.25.2

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

* [PATCH 02/10] efi_loader: add option to initialise EFI subsystem early
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
  2020-04-27  9:48 ` [PATCH 01/10] efi_loader: disk: add efi_disk_is_system_part() AKASHI Takahiro
@ 2020-04-27  9:48 ` AKASHI Takahiro
  2020-04-27 20:09   ` Heinrich Schuchardt
  2020-04-27  9:48 ` [PATCH 03/10] efi_loader: define UpdateCapsule api AKASHI Takahiro
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

If this option is enabled, the initialisation of UEFI subsystem will be
done as part of U-Boot initialisation.

This feature will be utilised in implementing capsule-on-disk feature.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 common/board_r.c       | 6 ++++++
 lib/efi_loader/Kconfig | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/common/board_r.c b/common/board_r.c
index 0bbeaa7594c6..7cf21a6078f9 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -64,6 +64,9 @@
 #if defined(CONFIG_GPIO_HOG)
 #include <asm/gpio.h>
 #endif
+#ifdef CONFIG_EFI_SETUP_EARLY
+#include <efi_loader.h>
+#endif
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -867,6 +870,9 @@ static init_fnc_t init_sequence_r[] = {
 #endif
 #if defined(CONFIG_M68K) && defined(CONFIG_BLOCK_CACHE)
 	blkcache_init,
+#endif
+#ifdef CONFIG_EFI_SETUP_EARLY
+	(init_fnc_t)efi_init_obj_list,
 #endif
 	run_main_loop,
 };
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 1cfa24ffcf72..7cc2d940f848 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -25,6 +25,10 @@ config EFI_LOADER
 
 if EFI_LOADER
 
+config EFI_SETUP_EARLY
+	bool
+	default n
+
 config EFI_GET_TIME
 	bool "GetTime() runtime service"
 	depends on DM_RTC
-- 
2.25.2

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

* [PATCH 03/10] efi_loader: define UpdateCapsule api
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
  2020-04-27  9:48 ` [PATCH 01/10] efi_loader: disk: add efi_disk_is_system_part() AKASHI Takahiro
  2020-04-27  9:48 ` [PATCH 02/10] efi_loader: add option to initialise EFI subsystem early AKASHI Takahiro
@ 2020-04-27  9:48 ` AKASHI Takahiro
  2020-05-17  8:02   ` Heinrich Schuchardt
  2020-04-27  9:48 ` [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support AKASHI Takahiro
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

In this commit, skeleton functions for capsule-related API's are
added under CONFIG_EFI_UPDATE_CAPSULE configuration.
Detailed implementation for a specific capsule type will be added
in the succeeding patches.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_api.h            |  12 +++
 include/efi_loader.h         |  15 ++++
 lib/efi_loader/Kconfig       |  11 +++
 lib/efi_loader/Makefile      |   1 +
 lib/efi_loader/efi_capsule.c | 153 +++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_runtime.c | 104 ++++++++++++++----------
 lib/efi_loader/efi_setup.c   |  31 +++++--
 7 files changed, 276 insertions(+), 51 deletions(-)
 create mode 100644 lib/efi_loader/efi_capsule.c

diff --git a/include/efi_api.h b/include/efi_api.h
index 77d6bf2660b9..6fa3f4a887d2 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -217,6 +217,10 @@ enum efi_reset_type {
 #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE	0x00020000
 #define CAPSULE_FLAGS_INITIATE_RESET		0x00040000
 
+#define EFI_CAPSULE_REPORT_GUID \
+	EFI_GUID(0x39b68c46, 0xf7fb, 0x441b, 0xb6, 0xec, \
+		 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3)
+
 struct efi_capsule_header {
 	efi_guid_t capsule_guid;
 	u32 header_size;
@@ -224,6 +228,14 @@ struct efi_capsule_header {
 	u32 capsule_image_size;
 } __packed;
 
+struct efi_capsule_result_variable_header {
+	u32 variable_total_size;
+	u32 reserved;
+	efi_guid_t capsule_guid;
+	struct efi_time capsule_processed;
+	efi_status_t capsule_status;
+} __packed;
+
 #define EFI_RT_SUPPORTED_GET_TIME			0x0001
 #define EFI_RT_SUPPORTED_SET_TIME			0x0002
 #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME		0x0004
diff --git a/include/efi_loader.h b/include/efi_loader.h
index d4510462d616..19ffc027c171 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -196,6 +196,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7;
 
 /* GUID of RNG protocol */
 extern const efi_guid_t efi_guid_rng_protocol;
+/* GUID of capsule update result */
+extern const efi_guid_t efi_guid_capsule_report;
 
 extern unsigned int __efi_runtime_start, __efi_runtime_stop;
 extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@@ -778,6 +780,19 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
 		     WIN_CERTIFICATE **auth, size_t *auth_len);
 #endif /* CONFIG_EFI_SECURE_BOOT */
 
+#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
+/* Capsule update */
+efi_status_t EFIAPI efi_update_capsule(
+		struct efi_capsule_header **capsule_header_array,
+		efi_uintn_t capsule_count,
+		u64 scatter_gather_list);
+efi_status_t EFIAPI efi_query_capsule_caps(
+		struct efi_capsule_header **capsule_header_array,
+		efi_uintn_t capsule_count,
+		u64 *maximum_capsule_size,
+		u32 *reset_type);
+#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */
+
 #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
 
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 7cc2d940f848..e2b08251f26a 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -45,6 +45,17 @@ config EFI_SET_TIME
 	  Provide the SetTime() runtime service at boottime. This service
 	  can be used by an EFI application to adjust the real time clock.
 
+config EFI_HAVE_CAPSULE_SUPPORT
+	bool
+
+config EFI_RUNTIME_UPDATE_CAPSULE
+	bool "UpdateCapsule() runtime service"
+	default n
+	select EFI_HAVE_CAPSULE_SUPPORT
+	help
+	  Select this option if you want to use UpdateCapsule and
+	  QueryCapsuleCapabilities API's.
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index eff3c25ec301..2ee1e683d9c5 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -22,6 +22,7 @@ endif
 obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
 obj-y += efi_bootmgr.o
 obj-y += efi_boottime.o
+obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
 obj-y += efi_console.o
 obj-y += efi_device_path.o
 obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
new file mode 100644
index 000000000000..fb104bb92a6c
--- /dev/null
+++ b/lib/efi_loader/efi_capsule.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  EFI Capsule
+ *
+ *  Copyright (c) 2018 Linaro Limited
+ *			Author: AKASHI Takahiro
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <fs.h>
+#include <malloc.h>
+#include <sort.h>
+
+const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
+
+static __maybe_unused int get_last_capsule(void)
+{
+	u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
+	char value[11], *p;
+	efi_uintn_t size;
+	unsigned long num = 0xffff;
+	efi_status_t ret;
+
+	size = sizeof(value16);
+	ret = EFI_CALL(efi_get_variable(L"CapsuleLast",
+					&efi_guid_capsule_report,
+					NULL, &size, value16));
+	if (ret != EFI_SUCCESS || u16_strncmp(value16, L"Capsule", 7))
+		goto err;
+
+	p = value;
+	utf16_utf8_strcpy(&p, value16);
+	strict_strtoul(&value[7], 16, &num);
+err:
+	return (int)num;
+}
+
+static __maybe_unused
+void set_capsule_result(int num, struct efi_capsule_header *capsule,
+			efi_status_t return_status)
+{
+	char variable_name[12];
+	u16 variable_name16[12], *p;
+	struct efi_capsule_result_variable_header result;
+	struct efi_time time;
+	efi_status_t ret;
+
+	sprintf(variable_name, "Capsule%04X", num);
+	p = variable_name16;
+	utf8_utf16_strncpy(&p, variable_name, 11);
+	result.variable_total_size = sizeof(result);
+	result.capsule_guid = capsule->capsule_guid;
+	ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL));
+	if (ret == EFI_SUCCESS)
+		memcpy(&result.capsule_processed, &time, sizeof(time));
+	else
+		memset(&result.capsule_processed, 0, sizeof(time));
+	result.capsule_status = return_status;
+	ret = EFI_CALL(efi_set_variable(variable_name16,
+					&efi_guid_capsule_report,
+					EFI_VARIABLE_NON_VOLATILE |
+					EFI_VARIABLE_BOOTSERVICE_ACCESS |
+					EFI_VARIABLE_RUNTIME_ACCESS,
+					sizeof(result), &result));
+	if (ret)
+		printf("EFI: creating %s failed\n", variable_name);
+}
+
+/*
+ * Launch a capsule
+ */
+/**
+ * efi_update_capsule() - process information from operating system
+ *
+ * This function implements the UpdateCapsule() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @capsule_header_array:	pointer to array of virtual pointers
+ * @capsule_count:		number of pointers in capsule_header_array
+ * @scatter_gather_list:	pointer to arry of physical pointers
+ * Return:			status code
+ */
+efi_status_t EFIAPI efi_update_capsule(
+		struct efi_capsule_header **capsule_header_array,
+		efi_uintn_t capsule_count,
+		u64 scatter_gather_list)
+{
+	struct efi_capsule_header *capsule;
+	unsigned int i;
+	efi_status_t ret;
+
+	EFI_ENTRY("%p, %lu, %llu\n", capsule_header_array, capsule_count,
+		  scatter_gather_list);
+
+	if (!capsule_count) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	ret = EFI_SUCCESS;
+	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
+	     i++, capsule = *(++capsule_header_array)) {
+	}
+out:
+	return EFI_EXIT(ret);
+}
+
+/**
+ * efi_query_capsule_caps() - check if capsule is supported
+ *
+ * This function implements the QueryCapsuleCapabilities() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @capsule_header_array:	pointer to array of virtual pointers
+ * @capsule_count:		number of pointers in capsule_header_array
+ * @maximum_capsule_size:	maximum capsule size
+ * @reset_type:			type of reset needed for capsule update
+ * Return:			status code
+ */
+efi_status_t EFIAPI efi_query_capsule_caps(
+		struct efi_capsule_header **capsule_header_array,
+		efi_uintn_t capsule_count,
+		u64 *maximum_capsule_size,
+		u32 *reset_type)
+{
+	struct efi_capsule_header *capsule __attribute__((unused));
+	unsigned int i;
+	efi_status_t ret;
+
+	EFI_ENTRY("%p, %lu, %p, %p\n", capsule_header_array, capsule_count,
+		  maximum_capsule_size, reset_type);
+
+	if (!maximum_capsule_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	*maximum_capsule_size = U64_MAX;
+	*reset_type = EFI_RESET_COLD;
+
+	ret = EFI_SUCCESS;
+	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
+	     i++, capsule = *(++capsule_header_array)) {
+		/* TODO */
+	}
+out:
+	return EFI_EXIT(ret);
+}
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index 6a25acbbcdf5..669e25a9589a 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -130,6 +130,11 @@ efi_status_t efi_init_runtime_supported(void)
 #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
 	rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
 #endif
+#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
+	rt_table->runtime_services_supported |=
+			(EFI_RT_SUPPORTED_UPDATE_CAPSULE |
+			 EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES);
+#endif
 
 	ret = efi_install_configuration_table(&efi_rt_properties_table_guid,
 					      rt_table);
@@ -410,6 +415,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
 	return EFI_UNSUPPORTED;
 }
 
+/**
+ * efi_update_capsule_unsupported() - process information from operating system
+ *
+ * This function implements the UpdateCapsule() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @capsule_header_array:	pointer to array of virtual pointers
+ * @capsule_count:		number of pointers in capsule_header_array
+ * @scatter_gather_list:	pointer to arry of physical pointers
+ * Returns:			status code
+ */
+efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported(
+			struct efi_capsule_header **capsule_header_array,
+			efi_uintn_t capsule_count,
+			u64 scatter_gather_list)
+{
+	return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_query_capsule_caps_unsupported() - check if capsule is supported
+ *
+ * This function implements the QueryCapsuleCapabilities() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @capsule_header_array:	pointer to array of virtual pointers
+ * @capsule_count:		number of pointers in capsule_header_array
+ * @maximum_capsule_size:	maximum capsule size
+ * @reset_type:			type of reset needed for capsule update
+ * Returns:			status code
+ */
+efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps_unsupported(
+			struct efi_capsule_header **capsule_header_array,
+			efi_uintn_t capsule_count,
+			u64 *maximum_capsule_size,
+			u32 *reset_type)
+{
+	return EFI_UNSUPPORTED;
+}
+
 /**
  * efi_is_runtime_service_pointer() - check if pointer points to runtime table
  *
@@ -433,6 +482,12 @@ void efi_runtime_detach(void)
 	efi_runtime_services.reset_system = efi_reset_system;
 	efi_runtime_services.get_time = efi_get_time;
 	efi_runtime_services.set_time = efi_set_time;
+#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
+	/* won't support at runtime */
+	efi_runtime_services.update_capsule = efi_update_capsule_unsupported;
+	efi_runtime_services.query_capsule_caps =
+					efi_query_capsule_caps_unsupported;
+#endif
 
 	/* Update CRC32 */
 	efi_update_table_header_crc32(&efi_runtime_services.hdr);
@@ -837,50 +892,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
 	return EFI_UNSUPPORTED;
 }
 
-/**
- * efi_update_capsule() - process information from operating system
- *
- * This function implements the UpdateCapsule() runtime service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * @capsule_header_array:	pointer to array of virtual pointers
- * @capsule_count:		number of pointers in capsule_header_array
- * @scatter_gather_list:	pointer to arry of physical pointers
- * Returns:			status code
- */
-efi_status_t __efi_runtime EFIAPI efi_update_capsule(
-			struct efi_capsule_header **capsule_header_array,
-			efi_uintn_t capsule_count,
-			u64 scatter_gather_list)
-{
-	return EFI_UNSUPPORTED;
-}
-
-/**
- * efi_query_capsule_caps() - check if capsule is supported
- *
- * This function implements the QueryCapsuleCapabilities() runtime service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * @capsule_header_array:	pointer to array of virtual pointers
- * @capsule_count:		number of pointers in capsule_header_array
- * @maximum_capsule_size:	maximum capsule size
- * @reset_type:			type of reset needed for capsule update
- * Returns:			status code
- */
-efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps(
-			struct efi_capsule_header **capsule_header_array,
-			efi_uintn_t capsule_count,
-			u64 *maximum_capsule_size,
-			u32 *reset_type)
-{
-	return EFI_UNSUPPORTED;
-}
-
 struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
 	.hdr = {
 		.signature = EFI_RUNTIME_SERVICES_SIGNATURE,
@@ -898,7 +909,12 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
 	.set_variable = efi_set_variable,
 	.get_next_high_mono_count = (void *)&efi_unimplemented,
 	.reset_system = &efi_reset_system_boottime,
+#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
 	.update_capsule = efi_update_capsule,
 	.query_capsule_caps = efi_query_capsule_caps,
+#else
+	.update_capsule = efi_update_capsule_unsupported,
+	.query_capsule_caps = efi_query_capsule_caps_unsupported,
+#endif
 	.query_variable_info = efi_query_variable_info,
 };
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index 1b648c84673a..8fe378bbfdfc 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -115,6 +115,29 @@ static efi_status_t efi_init_secure_boot(void)
 }
 #endif /* CONFIG_EFI_SECURE_BOOT */
 
+/**
+ * efi_init_os_indications() - indicate supported features for OS requests
+ *
+ * Set the OsIndicationsSupported variable.
+ *
+ * Return:	status code
+ */
+static efi_status_t efi_init_os_indications(void)
+{
+	u64 os_indications_supported = 0;
+
+#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
+	os_indications_supported |=
+			EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
+#endif
+	return EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
+					 &efi_global_variable_guid,
+					 EFI_VARIABLE_BOOTSERVICE_ACCESS |
+					 EFI_VARIABLE_RUNTIME_ACCESS,
+					 sizeof(os_indications_supported),
+					 &os_indications_supported));
+}
+
 /**
  * efi_init_obj_list() - Initialize and populate EFI object list
  *
@@ -122,7 +145,6 @@ static efi_status_t efi_init_secure_boot(void)
  */
 efi_status_t efi_init_obj_list(void)
 {
-	u64 os_indications_supported = 0; /* None */
 	efi_status_t ret = EFI_SUCCESS;
 
 	/* Initialize once only */
@@ -146,12 +168,7 @@ efi_status_t efi_init_obj_list(void)
 		goto out;
 
 	/* Indicate supported features */
-	ret = EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
-					&efi_global_variable_guid,
-					EFI_VARIABLE_BOOTSERVICE_ACCESS |
-					EFI_VARIABLE_RUNTIME_ACCESS,
-					sizeof(os_indications_supported),
-					&os_indications_supported));
+	ret = efi_init_os_indications();
 	if (ret != EFI_SUCCESS)
 		goto out;
 
-- 
2.25.2

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

* [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
                   ` (2 preceding siblings ...)
  2020-04-27  9:48 ` [PATCH 03/10] efi_loader: define UpdateCapsule api AKASHI Takahiro
@ 2020-04-27  9:48 ` AKASHI Takahiro
  2020-04-27 20:28   ` Heinrich Schuchardt
  2020-04-27  9:48 ` [PATCH 05/10] efi_loader: capsule: add memory range capsule definitions AKASHI Takahiro
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

Capsule data can be loaded into the system either via UpdateCapsule
runtime service or files on a file system (of boot device).
The latter case is called "capsules on disk", and actual updates will
take place at the next boot time.

In this commit, we will support capsule on disk mechanism.

Please note that U-Boot itself has no notion of "boot device" and
all the capsule files to be executed will be detected only if they
are located in a specific directory, \EFI\UpdateCapsule, on a device
that is identified as a boot device by "BootXXXX" variables.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 common/main.c                |   4 +
 include/efi_loader.h         |  16 ++
 lib/efi_loader/Kconfig       |  22 ++
 lib/efi_loader/efi_capsule.c | 449 +++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_setup.c   |   9 +
 5 files changed, 500 insertions(+)

diff --git a/common/main.c b/common/main.c
index 06d7ff56d60c..877ae63b708d 100644
--- a/common/main.c
+++ b/common/main.c
@@ -14,6 +14,7 @@
 #include <env.h>
 #include <init.h>
 #include <version.h>
+#include <efi_loader.h>
 
 static void run_preboot_environment_command(void)
 {
@@ -51,6 +52,9 @@ void main_loop(void)
 	if (IS_ENABLED(CONFIG_UPDATE_TFTP))
 		update_tftp(0UL, NULL, NULL);
 
+	if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
+		efi_launch_capsules();
+
 	s = bootdelay_process();
 	if (cli_process_fdt(&s))
 		cli_secure_boot_cmd(s);
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 19ffc027c171..d49ebcad53ec 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -793,6 +793,18 @@ efi_status_t EFIAPI efi_query_capsule_caps(
 		u32 *reset_type);
 #endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */
 
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK
+#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\"
+
+/* Hook at initialization */
+efi_status_t efi_launch_capsules(void);
+#else
+static inline efi_status_t efi_launch_capsules(void)
+{
+	return EFI_SUCCESS;
+}
+#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
+
 #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
 
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
@@ -809,6 +821,10 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr,
 				   const char *path) { }
 static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
 static inline void efi_print_image_infos(void *pc) { }
+static inline efi_status_t efi_launch_capsules(void)
+{
+	return EFI_SUCCESS;
+}
 
 #endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
 
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index e2b08251f26a..b48b95a32e03 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -56,6 +56,28 @@ config EFI_RUNTIME_UPDATE_CAPSULE
 	  Select this option if you want to use UpdateCapsule and
 	  QueryCapsuleCapabilities API's.
 
+config EFI_CAPSULE_ON_DISK
+	bool "Enable capsule-on-disk support"
+	select EFI_HAVE_CAPSULE_SUPPORT
+	default n
+	help
+	  Select this option if you want to use capsule-on-disk feature,
+	  that is, capsules can be fetched and executed from files
+	  under a specific directory on UEFI system partition instead of
+	  via UpdateCapsule API.
+
+config EFI_CAPSULE_ON_DISK_EARLY
+	bool "Initiate capsule-on-disk at U-Boot boottime"
+	depends on EFI_CAPSULE_ON_DISK
+	default y
+	select EFI_SETUP_EARLY
+	help
+	  Normally, without this option enabled, capsules will be
+	  executed only at the first time of invoking one of efi command.
+	  If this option is enabled, capsules will be enforced to be
+	  executed as part of U-Boot initialisation so that they will
+	  surely take place whatever is set to distro_bootcmd.
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index fb104bb92a6c..938129a41934 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -10,10 +10,16 @@
 #include <efi_loader.h>
 #include <fs.h>
 #include <malloc.h>
+#include <mapmem.h>
 #include <sort.h>
 
 const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
 
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK
+/* for file system access */
+static struct efi_file_handle *bootdev_root;
+#endif
+
 static __maybe_unused int get_last_capsule(void)
 {
 	u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
@@ -151,3 +157,446 @@ efi_status_t EFIAPI efi_query_capsule_caps(
 out:
 	return EFI_EXIT(ret);
 }
+
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK
+static efi_status_t get_dp_device(u16 *boot_var,
+				  struct efi_device_path **device_dp)
+{
+	void *buf = NULL;
+	efi_uintn_t size;
+	struct efi_load_option lo;
+	struct efi_device_path *file_dp;
+	efi_status_t ret;
+
+	size = 0;
+	ret = EFI_CALL(efi_get_variable(boot_var, &efi_global_variable_guid,
+					NULL, &size, NULL));
+	if (ret == EFI_BUFFER_TOO_SMALL) {
+		buf = malloc(size);
+		if (!buf)
+			return EFI_OUT_OF_RESOURCES;
+		ret = EFI_CALL(efi_get_variable(boot_var,
+						&efi_global_variable_guid,
+						NULL, &size, buf));
+	}
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	efi_deserialize_load_option(&lo, buf);
+
+	if (lo.attributes & LOAD_OPTION_ACTIVE) {
+		efi_dp_split_file_path(lo.file_path, device_dp, &file_dp);
+		efi_free_pool(file_dp);
+
+		ret = EFI_SUCCESS;
+	} else {
+		ret = EFI_NOT_FOUND;
+	}
+
+	free(buf);
+
+	return ret;
+}
+
+static bool device_is_present_and_system_part(struct efi_device_path *dp)
+{
+	efi_handle_t handle;
+
+	handle = efi_dp_find_obj(dp, NULL);
+	if (!handle)
+		return false;
+
+	return efi_disk_is_system_part(handle);
+}
+
+static efi_status_t find_boot_device(void)
+{
+	char boot_var[9];
+	u16 boot_var16[9], *p, bootnext, *boot_order = NULL;
+	efi_uintn_t size;
+	int i, num;
+	struct efi_simple_file_system_protocol *volume;
+	struct efi_device_path *boot_dev = NULL;
+	efi_status_t ret;
+
+	/* find active boot device in BootNext */
+	bootnext = 0;
+	size = sizeof(bootnext);
+	ret = EFI_CALL(efi_get_variable(L"BootNext",
+					(efi_guid_t *)&efi_global_variable_guid,
+					NULL, &size, &bootnext));
+	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+		/* BootNext does exist here */
+		if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) {
+			printf("BootNext must be 16-bit integer\n");
+			goto skip;
+		}
+		sprintf((char *)boot_var, "Boot%04X", bootnext);
+		p = boot_var16;
+		utf8_utf16_strcpy(&p, boot_var);
+
+		ret = get_dp_device(boot_var16, &boot_dev);
+		if (ret == EFI_SUCCESS) {
+			if (device_is_present_and_system_part(boot_dev)) {
+				goto out;
+			} else {
+				efi_free_pool(boot_dev);
+				boot_dev = NULL;
+			}
+		}
+	}
+
+skip:
+	/* find active boot device in BootOrder */
+	size = 0;
+	ret = EFI_CALL(efi_get_variable(L"BootOrder", &efi_global_variable_guid,
+					NULL, &size, NULL));
+	if (ret == EFI_BUFFER_TOO_SMALL) {
+		boot_order = malloc(size);
+		if (!boot_order) {
+			ret = EFI_OUT_OF_RESOURCES;
+			goto out;
+		}
+
+		ret = EFI_CALL(efi_get_variable(
+					L"BootOrder", &efi_global_variable_guid,
+					NULL, &size, boot_order));
+	}
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	/* check in higher order */
+	num = size / sizeof(u16);
+	for (i = 0; i < num; i++) {
+		sprintf((char *)boot_var, "Boot%04X", boot_order[i]);
+		p = boot_var16;
+		utf8_utf16_strcpy(&p, boot_var);
+		ret = get_dp_device(boot_var16, &boot_dev);
+		if (ret != EFI_SUCCESS)
+			continue;
+
+		if (device_is_present_and_system_part(boot_dev))
+			break;
+
+		efi_free_pool(boot_dev);
+		boot_dev = NULL;
+	}
+out:
+	if (boot_dev) {
+		u16 *path_str;
+
+		path_str = efi_dp_str(boot_dev);
+		EFI_PRINT("EFI Capsule: bootdev is %ls\n", path_str);
+		efi_free_pool(path_str);
+
+		volume = efi_fs_from_path(boot_dev);
+		if (!volume)
+			ret = EFI_DEVICE_ERROR;
+		else
+			ret = EFI_CALL(volume->open_volume(volume,
+							   &bootdev_root));
+		efi_free_pool(boot_dev);
+	} else {
+		ret = EFI_NOT_FOUND;
+	}
+	free(boot_order);
+
+	return ret;
+}
+
+/*
+ * Traverse a capsule directory in boot device
+ * Called by initialization code, and returns an array of capsule file
+ * names in @files
+ */
+static efi_status_t efi_capsule_scan_dir(u16 ***files, int *num)
+{
+	struct efi_file_handle *dirh;
+	struct efi_file_info *dirent;
+	efi_uintn_t dirent_size, tmp_size;
+	int count;
+	u16 **tmp_files;
+	efi_status_t ret;
+
+	ret = find_boot_device();
+	if (ret == EFI_NOT_FOUND) {
+		EFI_PRINT("EFI Capsule: bootdev is not set\n");
+		*num = 0;
+		return EFI_SUCCESS;
+	} else if (ret != EFI_SUCCESS) {
+		return EFI_DEVICE_ERROR;
+	}
+
+	/* count capsule files */
+	ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
+					     EFI_CAPSULE_DIR,
+					     EFI_FILE_MODE_READ, 0));
+	if (ret != EFI_SUCCESS) {
+		*num = 0;
+		return EFI_SUCCESS;
+	}
+
+	dirent_size = 256;
+	dirent = malloc(dirent_size);
+	if (!dirent)
+		return EFI_OUT_OF_RESOURCES;
+
+	count = 0;
+	while (1) {
+		tmp_size = dirent_size;
+		ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
+		if (ret == EFI_BUFFER_TOO_SMALL) {
+			dirent = realloc(dirent, tmp_size);
+			if (!dirent) {
+				ret = EFI_OUT_OF_RESOURCES;
+				goto err;
+			}
+			dirent_size = tmp_size;
+			ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
+		}
+		if (ret != EFI_SUCCESS)
+			goto err;
+		if (!tmp_size)
+			break;
+
+		if (!(dirent->attribute & EFI_FILE_DIRECTORY) &&
+		    u16_strcmp(dirent->file_name, L".") &&
+		    u16_strcmp(dirent->file_name, L".."))
+			count++;
+	}
+
+	ret = EFI_CALL((*dirh->setpos)(dirh, 0));
+	if (ret != EFI_SUCCESS)
+		goto err;
+
+	/* make a list */
+	tmp_files = malloc(count * sizeof(*files));
+	if (!tmp_files) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto err;
+	}
+
+	count = 0;
+	while (1) {
+		tmp_size = dirent_size;
+		ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
+		if (ret != EFI_SUCCESS)
+			goto err;
+		if (!tmp_size)
+			break;
+
+		if (!(dirent->attribute & EFI_FILE_DIRECTORY) &&
+		    u16_strcmp(dirent->file_name, L".") &&
+		    u16_strcmp(dirent->file_name, L".."))
+			tmp_files[count++] = u16_strdup(dirent->file_name);
+	}
+	/* ignore an error */
+	EFI_CALL((*dirh->close)(dirh));
+
+	/* in ascii order */
+	/* FIXME: u16 version of strcasecmp */
+	qsort(tmp_files, count, sizeof(*tmp_files),
+	      (int (*)(const void *, const void *))strcasecmp);
+	*files = tmp_files;
+	*num = count;
+	ret = EFI_SUCCESS;
+err:
+	free(dirent);
+
+	return ret;
+}
+
+/*
+ * Read in a capsule file
+ */
+static efi_status_t efi_capsule_read_file(u16 *filename,
+					  struct efi_capsule_header **capsule)
+{
+	struct efi_file_handle *dirh, *fh;
+	struct efi_file_info *file_info = NULL;
+	struct efi_capsule_header *buf = NULL;
+	efi_uintn_t size;
+	efi_status_t ret;
+
+	ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
+					     EFI_CAPSULE_DIR,
+					     EFI_FILE_MODE_READ, 0));
+	if (ret != EFI_SUCCESS)
+		return ret;
+	ret = EFI_CALL((*dirh->open)(dirh, &fh, filename,
+				     EFI_FILE_MODE_READ, 0));
+	/* ignore an error */
+	EFI_CALL((*dirh->close)(dirh));
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	/* file size */
+	size = 0;
+	ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid,
+				      &size, file_info));
+	if (ret == EFI_BUFFER_TOO_SMALL) {
+		file_info = malloc(size);
+		if (!file_info) {
+			ret = EFI_OUT_OF_RESOURCES;
+			goto err;
+		}
+		ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid,
+					      &size, file_info));
+	}
+	if (ret != EFI_SUCCESS)
+		goto err;
+	size = file_info->file_size;
+	free(file_info);
+	buf = malloc(size);
+	if (!buf) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto err;
+	}
+
+	/* fetch data */
+	ret = EFI_CALL((*fh->read)(fh, &size, buf));
+	if (ret == EFI_SUCCESS) {
+		if (size >= buf->capsule_image_size) {
+			*capsule = buf;
+		} else {
+			free(buf);
+			ret = EFI_INVALID_PARAMETER;
+		}
+	} else {
+		free(buf);
+	}
+err:
+	EFI_CALL((*fh->close)(fh));
+
+	return ret;
+}
+
+static efi_status_t efi_capsule_delete_file(u16 *filename)
+{
+	struct efi_file_handle *dirh, *fh;
+	efi_status_t ret;
+
+	ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
+					     EFI_CAPSULE_DIR,
+					     EFI_FILE_MODE_READ, 0));
+	if (ret != EFI_SUCCESS)
+		return ret;
+	ret = EFI_CALL((*dirh->open)(dirh, &fh, filename,
+				     EFI_FILE_MODE_READ, 0));
+	/* ignore an error */
+	EFI_CALL((*dirh->close)(dirh));
+
+	ret = EFI_CALL((*fh->delete)(fh));
+
+	return ret;
+}
+
+static void efi_capsule_scan_done(void)
+{
+	EFI_CALL((*bootdev_root->close)(bootdev_root));
+	bootdev_root = NULL;
+}
+
+efi_status_t __weak arch_efi_load_capsule_drivers(void)
+{
+	return EFI_SUCCESS;
+}
+
+/*
+ * Launch all the capsules in system at boot time
+ *
+ * Called by efi init code
+ */
+efi_status_t efi_launch_capsules(void)
+{
+	u64 os_indications;
+	efi_uintn_t size;
+	struct efi_capsule_header *capsule = NULL;
+	u16 **files;
+	int nfiles, num, i;
+	char variable_name[12];
+	u16 variable_name16[12], *p;
+	efi_status_t ret;
+
+	size = sizeof(os_indications);
+	ret = EFI_CALL(efi_get_variable(L"OsIndications",
+					&efi_global_variable_guid,
+					NULL, &size, &os_indications));
+	if (ret != EFI_SUCCESS ||
+	    !(os_indications
+	      & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED))
+		return EFI_SUCCESS;
+
+	num = get_last_capsule();
+
+	/* Load capsule drivers */
+	ret = arch_efi_load_capsule_drivers();
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	/*
+	 * Find capsules on disk.
+	 * All the capsules are collected at the beginning because
+	 * capsule files will be removed instantly.
+	 */
+	nfiles = 0;
+	files = NULL;
+	ret = efi_capsule_scan_dir(&files, &nfiles);
+	if (ret != EFI_SUCCESS)
+		return ret;
+	if (!nfiles)
+		return EFI_SUCCESS;
+
+	/* Launch capsules */
+	for (i = 0, ++num; i < nfiles; i++, num++) {
+		EFI_PRINT("capsule from %ls ...\n", files[i]);
+		if (num > 0xffff)
+			num = 0;
+		ret = efi_capsule_read_file(files[i], &capsule);
+		if (ret == EFI_SUCCESS) {
+			ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0));
+			if (ret != EFI_SUCCESS)
+				printf("EFI Capsule update failed at %ls\n",
+				       files[i]);
+
+			free(capsule);
+		} else {
+			printf("EFI: reading capsule failed: %ls\n",
+			       files[i]);
+		}
+		/* create CapsuleXXXX */
+		set_capsule_result(num, capsule, ret);
+
+		/* delete a capsule either in case of success or failure */
+		ret = efi_capsule_delete_file(files[i]);
+		if (ret != EFI_SUCCESS)
+			printf("EFI: deleting a capsule file failed: %ls\n",
+			       files[i]);
+	}
+	efi_capsule_scan_done();
+
+	for (i = 0; i < nfiles; i++)
+		free(files[i]);
+	free(files);
+
+	/* CapsuleMax */
+	p = variable_name16;
+	utf8_utf16_strncpy(&p, "CapsuleFFFF", 11);
+	EFI_CALL(efi_set_variable(L"CapsuleMax", &efi_guid_capsule_report,
+				  EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				  EFI_VARIABLE_RUNTIME_ACCESS,
+				  22, variable_name16));
+
+	/* CapsuleLast */
+	sprintf(variable_name, "Capsule%04X", num - 1);
+	p = variable_name16;
+	utf8_utf16_strncpy(&p, variable_name, 11);
+	EFI_CALL(efi_set_variable(L"CapsuleLast", &efi_guid_capsule_report,
+				  EFI_VARIABLE_NON_VOLATILE |
+				  EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				  EFI_VARIABLE_RUNTIME_ACCESS,
+				  22, variable_name16));
+
+	return ret;
+}
+#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index 8fe378bbfdfc..bb759976102a 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -129,6 +129,10 @@ static efi_status_t efi_init_os_indications(void)
 #ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
 	os_indications_supported |=
 			EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
+#endif
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK
+	os_indications_supported |=
+			EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED;
 #endif
 	return EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
 					 &efi_global_variable_guid,
@@ -239,6 +243,11 @@ efi_status_t efi_init_obj_list(void)
 	if (ret != EFI_SUCCESS)
 		goto out;
 
+#if defined(CONFIG_EFI_CAPSULE_ON_DISK) && \
+		!defined(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)
+	/* Execute capsules after reboot */
+	ret = efi_launch_capsules();
+#endif
 out:
 	efi_obj_list_initialized = ret;
 	return ret;
-- 
2.25.2

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

* [PATCH 05/10] efi_loader: capsule: add memory range capsule definitions
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
                   ` (3 preceding siblings ...)
  2020-04-27  9:48 ` [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support AKASHI Takahiro
@ 2020-04-27  9:48 ` AKASHI Takahiro
  2020-04-27  9:48 ` [PATCH 06/10] efi_loader: capsule: support firmware update AKASHI Takahiro
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

Memory range capsule gives us a way to notify that some memory regions
should be left untouched across the next reset.
See UEFI specification, section 8.5.3.

Since how we should handle this kind of capsule is totally up to
the system, no implementation will be added in this commit.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_api.h | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/include/efi_api.h b/include/efi_api.h
index 6fa3f4a887d2..dc602fe6707b 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -221,6 +221,10 @@ enum efi_reset_type {
 	EFI_GUID(0x39b68c46, 0xf7fb, 0x441b, 0xb6, 0xec, \
 		 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3)
 
+#define EFI_MEMORY_RANGE_CAPSULE_GUID \
+	EFI_GUID(0xde9f0ec, 0x88b6, 0x428f, 0x97, 0x7a, \
+		 0x25, 0x8f, 0x1d, 0xe, 0x5e, 0x72)
+
 struct efi_capsule_header {
 	efi_guid_t capsule_guid;
 	u32 header_size;
@@ -236,6 +240,19 @@ struct efi_capsule_result_variable_header {
 	efi_status_t capsule_status;
 } __packed;
 
+struct efi_memory_range {
+	efi_physical_addr_t	address;
+	u64			length;
+};
+
+struct efi_memory_range_capsule {
+	struct efi_capsule_header *header;
+	/* EFI_MEMORY_TYPE: 0x80000000-0xFFFFFFFF */
+	enum efi_mem_type os_requested_memory_type;
+	u64 number_of_memory_ranges;
+	struct efi_memory_range memory_ranges[];
+} __packed;
+
 #define EFI_RT_SUPPORTED_GET_TIME			0x0001
 #define EFI_RT_SUPPORTED_SET_TIME			0x0002
 #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME		0x0004
-- 
2.25.2

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

* [PATCH 06/10] efi_loader: capsule: support firmware update
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
                   ` (4 preceding siblings ...)
  2020-04-27  9:48 ` [PATCH 05/10] efi_loader: capsule: add memory range capsule definitions AKASHI Takahiro
@ 2020-04-27  9:48 ` AKASHI Takahiro
  2020-04-27  9:48 ` [PATCH 07/10] efi_loader: add simple firmware management protocol for FIT image AKASHI Takahiro
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

A capsule tagged with the guid, EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID,
is handled as a firmware update object.
What efi_update_capsule() basically does is to load any firmware management
protocol (or fmp) drivers contained in a capsule, find out an appropriate
fmp driver and then invoke its set_image() interface against each binary
in a capsule.
In this commit, however, loading drivers is not supported.

The result of applying a capsule is set to be stored in "CapsuleXXXX"
variable, but its implementation is deferred to a fmp driver.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_api.h            | 123 +++++++++++++++++++++
 include/efi_loader.h         |   2 +
 lib/efi_loader/Kconfig       |   8 ++
 lib/efi_loader/efi_capsule.c | 200 +++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_setup.c   |   4 +
 5 files changed, 337 insertions(+)

diff --git a/include/efi_api.h b/include/efi_api.h
index dc602fe6707b..7d7aab190657 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -225,6 +225,10 @@ enum efi_reset_type {
 	EFI_GUID(0xde9f0ec, 0x88b6, 0x428f, 0x97, 0x7a, \
 		 0x25, 0x8f, 0x1d, 0xe, 0x5e, 0x72)
 
+#define EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID \
+	EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \
+		 0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a)
+
 struct efi_capsule_header {
 	efi_guid_t capsule_guid;
 	u32 header_size;
@@ -253,6 +257,32 @@ struct efi_memory_range_capsule {
 	struct efi_memory_range memory_ranges[];
 } __packed;
 
+struct efi_firmware_management_capsule_header {
+	u32 version;
+	u16 embedded_driver_count;
+	u16 payload_item_count;
+	u64 item_offset_list[];
+} __packed;
+
+struct efi_firmware_management_capsule_image_header {
+	u32 version;
+	efi_guid_t update_image_type_id;
+	u8 update_image_index;
+	u8 reserved[3];
+	u32 update_image_size;
+	u32 update_vendor_code_size;
+	u64 update_hardware_instance;
+} __packed;
+
+struct efi_capsule_result_variable_fmp {
+	u16 version;
+	u8 payload_index;
+	u8 update_image_index;
+	efi_guid_t update_image_type_id;
+	// u16 capsule_file_name[];
+	// u16 capsule_target[];
+} __packed;
+
 #define EFI_RT_SUPPORTED_GET_TIME			0x0001
 #define EFI_RT_SUPPORTED_SET_TIME			0x0002
 #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME		0x0004
@@ -1798,4 +1828,97 @@ struct efi_signature_list {
 /*	struct efi_signature_data signatures[...][signature_size]; */
 } __attribute__((__packed__));
 
+/*
+ * Firmware management protocol
+ */
+#define EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID \
+	EFI_GUID(0x86c77a67, 0x0b97, 0x4633, 0xa1, 0x87, \
+		 0x49, 0x10, 0x4d, 0x06, 0x85, 0xc7)
+
+#define EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE		0x1
+#define EFI_IMAGE_ATTRIBUTE_RESET_REQUIRED		0x2
+#define EFI_IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED	0x4
+#define EFI_IMAGE_ATTRIBUTE_IN_USE			0x8
+#define EFI_IMAGE_ATTRIBUTE_UEFI_IMAGE			0x10
+
+#define EFI_IMAGE_COMPATIBILITY_CHECK_SUPPORTED		0x1
+#define EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION		4
+
+#define EFI_IMAGE_UPDATABLE_VALID			0x1
+#define EFI_IMAGE_UPDATABLE_INVALID			0x2
+#define EFI_IMAGE_UPDATABLE_INVALID_TYPE		0x4
+#define EFI_IMAGE_UPDATABLE_INVALID_OLLD		0x8
+#define EFI_IMAGE_UPDATABLE_VALID_WITH_VENDOR_CODE	0x10
+
+#define EFI_PACKAGE_ATTRIBUTE_VERSION_UPDATABLE		0x1
+#define EFI_PACKAGE_ATTRIBUTE_RESET_REQUIRED		0x2
+#define EFI_PACKAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED	0x4
+
+typedef struct efi_firmware_image_dependencies {
+	u8 dependencies[0];
+} efi_fmp_dep_t;
+
+struct efi_firmware_image_descriptor {
+	u8 image_index;
+	efi_guid_t image_type_id;
+	u64 image_id;
+	u16 *image_id_name;
+	u32 version;
+	u16 *version_name;
+	efi_uintn_t size;
+	u64 attributes_supported;
+	u64 attributes_setting;
+	u64 compatibilities;
+	u32 lowest_supported_image_version;
+	u32 last_attempt_version;
+	u32 last_attempt_status;
+	u64 hardware_instance;
+	efi_fmp_dep_t *dependencies;
+};
+
+struct efi_firmware_management_protocol {
+	efi_status_t (EFIAPI *get_image_info)(
+			struct efi_firmware_management_protocol *this,
+			efi_uintn_t *image_info_size,
+			struct efi_firmware_image_descriptor *image_info,
+			u32 *descriptor_version,
+			u8 *descriptor_count,
+			efi_uintn_t *descriptor_size,
+			u32 *package_version,
+			u16 **package_version_name);
+	efi_status_t (EFIAPI *get_image)(
+			struct efi_firmware_management_protocol *this,
+			u8 image_index,
+			void *image,
+			efi_uintn_t *image_size);
+	efi_status_t (EFIAPI *set_image)(
+			struct efi_firmware_management_protocol *this,
+			u8 image_index,
+			const void *image,
+			efi_uintn_t image_size,
+			const void *vendor_code,
+			efi_status_t (*progress)(efi_uintn_t completion),
+			u16 **abort_reason);
+	efi_status_t (EFIAPI *check_image)(
+			struct efi_firmware_management_protocol *this,
+			u8 image_index,
+			const void *image,
+			efi_uintn_t *image_size,
+			u32 *image_updatable);
+	efi_status_t (EFIAPI *get_package_info)(
+			struct efi_firmware_management_protocol *this,
+			u32 *package_version,
+			u16 **package_version_name,
+			u32 *package_version_name_maxlen,
+			u64 *attributes_supported,
+			u64 *attributes_setting);
+	efi_status_t (EFIAPI *set_package_info)(
+			struct efi_firmware_management_protocol *this,
+			const void *image,
+			efi_uintn_t *image_size,
+			const void *vendor_code,
+			u32 package_version,
+			const u16 *package_version_name);
+};
+
 #endif
diff --git a/include/efi_loader.h b/include/efi_loader.h
index d49ebcad53ec..ad99ab660f27 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -198,6 +198,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7;
 extern const efi_guid_t efi_guid_rng_protocol;
 /* GUID of capsule update result */
 extern const efi_guid_t efi_guid_capsule_report;
+/* GUID of firmware management protocol */
+extern const efi_guid_t efi_guid_firmware_management_protocol;
 
 extern unsigned int __efi_runtime_start, __efi_runtime_stop;
 extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index b48b95a32e03..2a8fe6310d4a 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -78,6 +78,14 @@ config EFI_CAPSULE_ON_DISK_EARLY
 	  executed as part of U-Boot initialisation so that they will
 	  surely take place whatever is set to distro_bootcmd.
 
+config EFI_CAPSULE_FIRMWARE_MANAGEMENT
+	bool "Capsule: Firmware Management Protocol"
+	depends on EFI_HAVE_CAPSULE_SUPPORT
+	default y
+	help
+	  Select this option if you want to enable capsule-based
+	  firmware update using Firmware Management Protocol.
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index 938129a41934..931d363edcb2 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -14,6 +14,10 @@
 #include <sort.h>
 
 const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
+static const efi_guid_t efi_guid_firmware_management_capsule_id =
+		EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
+const efi_guid_t efi_guid_firmware_management_protocol =
+		EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
 
 #ifdef CONFIG_EFI_CAPSULE_ON_DISK
 /* for file system access */
@@ -73,6 +77,182 @@ void set_capsule_result(int num, struct efi_capsule_header *capsule,
 		printf("EFI: creating %s failed\n", variable_name);
 }
 
+#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT
+/*
+ * Handle firmware management capsules
+ */
+static struct efi_firmware_management_protocol *
+efi_fmp_find(efi_guid_t *image_type, u64 instance, efi_handle_t *handles,
+	     efi_uintn_t no_handles)
+{
+	efi_handle_t *handle;
+	struct efi_firmware_management_protocol *fmp;
+	struct efi_firmware_image_descriptor *image_info, *desc;
+	efi_uintn_t info_size, descriptor_size;
+	u32 descriptor_version;
+	u8 descriptor_count;
+	bool found = false;
+	int i;
+	efi_status_t ret;
+
+	for (i = 0, handle = handles; i < no_handles; i++, handle++) {
+		ret = EFI_CALL(efi_handle_protocol(
+				*handle,
+				&efi_guid_firmware_management_protocol,
+				(void **)&fmp));
+		if (ret != EFI_SUCCESS)
+			continue;
+
+		/* get device's image info */
+		info_size = 0;
+		image_info = NULL;
+		descriptor_version = 0;
+		descriptor_count = 0;
+		descriptor_size = 0;
+		ret = EFI_CALL(fmp->get_image_info(fmp, &info_size,
+						   image_info,
+						   &descriptor_version,
+						   &descriptor_count,
+						   &descriptor_size,
+						   NULL, NULL));
+		if (ret != EFI_BUFFER_TOO_SMALL)
+			goto skip;
+		image_info = malloc(info_size);
+		if (!image_info)
+			goto skip;
+
+		ret = EFI_CALL(fmp->get_image_info(fmp, &info_size,
+						   image_info,
+						   &descriptor_version,
+						   &descriptor_count,
+						   &descriptor_size,
+						   NULL, NULL));
+		if (ret != EFI_SUCCESS)
+			goto skip;
+
+		/* matching */
+		for (i = 0, desc = image_info; i < descriptor_count;
+		     i++, desc = (void *)desc + descriptor_size)
+			if (!guidcmp(&desc->image_type_id, image_type) &&
+			    (!instance ||
+			     ((descriptor_version >= 3) &&
+			      (desc->hardware_instance == instance))))
+				found = true;
+
+skip:
+		free(image_info);
+		EFI_CALL(efi_close_protocol(
+				(efi_handle_t)fmp,
+				&efi_guid_firmware_management_protocol,
+				NULL, NULL));
+		if (found)
+			return fmp;
+	}
+
+	return NULL;
+}
+
+static efi_status_t efi_capsule_update_firmware(
+		struct efi_capsule_header *capsule_data)
+{
+	struct efi_firmware_management_capsule_header *capsule;
+	struct efi_firmware_management_capsule_image_header *image;
+	size_t capsule_size;
+	void *image_binary, *vendor_code;
+	efi_handle_t *handles;
+	efi_uintn_t no_handles;
+	int item;
+	struct efi_firmware_management_protocol *fmp;
+	u16 *abort_reason;
+	efi_status_t ret = EFI_SUCCESS;
+
+	/* sanity check */
+	if ((capsule_data->header_size < sizeof(*capsule)) ||
+	    (capsule_data->header_size >= capsule_data->capsule_image_size))
+		return EFI_INVALID_PARAMETER;
+
+	capsule = (void *)capsule_data + capsule_data->header_size;
+	capsule_size = capsule_data->capsule_image_size
+			- capsule_data->header_size;
+
+	if (capsule->version != 0x00000001)
+		return EFI_INVALID_PARAMETER;
+
+	/* Drivers */
+	/* TODO: support loading drivers */
+
+	handles = NULL;
+	ret = EFI_CALL(efi_locate_handle_buffer(
+			BY_PROTOCOL,
+			&efi_guid_firmware_management_protocol,
+			NULL, &no_handles, (efi_handle_t **)&handles));
+	if (ret != EFI_SUCCESS)
+		return EFI_UNSUPPORTED;
+
+	/* Payload */
+	for (item = capsule->embedded_driver_count;
+	     item < capsule->embedded_driver_count
+		    + capsule->payload_item_count; item++) {
+		/* sanity check */
+		if ((capsule->item_offset_list[item] + sizeof(*image)
+				 >= capsule_size)) {
+			printf("EFI: A capsule has not enough size of data\n");
+			ret = EFI_INVALID_PARAMETER;
+			goto out;
+		}
+
+		image = (void *)capsule + capsule->item_offset_list[item];
+
+		if (image->version != 0x00000001 &&
+		    image->version != 0x00000002 &&
+		    image->version != 0x00000003) {
+			ret = EFI_INVALID_PARAMETER;
+			goto out;
+		}
+
+		/* find a device for update firmware */
+		fmp = efi_fmp_find(&image->update_image_type_id,
+				   image->version == 0x1 ? 0 :
+					image->update_hardware_instance,
+				   handles, no_handles);
+		if (!fmp) {
+			printf("EFI Capsule: not support firmware type: %pUl\n",
+			       &image->update_image_type_id);
+			ret = EFI_UNSUPPORTED;
+			goto out;
+		}
+
+		/* do it */
+		image_binary = (void *)image + sizeof(*image);
+		vendor_code = image_binary + image->update_image_size;
+
+		abort_reason = NULL;
+		ret = EFI_CALL(fmp->set_image(fmp, image->update_image_index,
+					      image_binary,
+					      image->update_image_size,
+					      vendor_code, NULL,
+					      &abort_reason));
+		if (ret != EFI_SUCCESS) {
+			printf("EFI Capsule: firmware update failed: %ls\n",
+			       abort_reason);
+			efi_free_pool(abort_reason);
+			goto out;
+		}
+	}
+
+out:
+	efi_free_pool(handles);
+
+	return ret;
+}
+#else
+static efi_status_t efi_capsule_update_firmware(
+		struct efi_capsule_header *capsule_data)
+{
+	return EFI_UNSUPPORTED;
+}
+#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT */
+
 /*
  * Launch a capsule
  */
@@ -109,6 +289,26 @@ efi_status_t EFIAPI efi_update_capsule(
 	ret = EFI_SUCCESS;
 	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
 	     i++, capsule = *(++capsule_header_array)) {
+		/* sanity check */
+		if ((capsule->header_size < sizeof(*capsule)) ||
+		    (capsule->capsule_image_size < sizeof(*capsule))) {
+			printf("EFI: A capsule has not enough size of data\n");
+			continue;
+		}
+
+		EFI_PRINT("Capsule[%d] (guid:%pUl)\n",
+			  i, &capsule->capsule_guid);
+		if (!guidcmp(&capsule->capsule_guid,
+			     &efi_guid_firmware_management_capsule_id)) {
+			ret  = efi_capsule_update_firmware(capsule);
+		} else {
+			printf("EFI: not support capsule type: %pUl\n",
+			       &capsule->capsule_guid);
+			ret = EFI_UNSUPPORTED;
+		}
+
+		if (ret != EFI_SUCCESS)
+			goto out;
 	}
 out:
 	return EFI_EXIT(ret);
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index bb759976102a..b83e908969d6 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -133,6 +133,10 @@ static efi_status_t efi_init_os_indications(void)
 #ifdef CONFIG_EFI_CAPSULE_ON_DISK
 	os_indications_supported |=
 			EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED;
+#endif
+#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT
+	os_indications_supported |=
+			EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED;
 #endif
 	return EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
 					 &efi_global_variable_guid,
-- 
2.25.2

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

* [PATCH 07/10] efi_loader: add simple firmware management protocol for FIT image
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
                   ` (5 preceding siblings ...)
  2020-04-27  9:48 ` [PATCH 06/10] efi_loader: capsule: support firmware update AKASHI Takahiro
@ 2020-04-27  9:48 ` AKASHI Takahiro
  2020-04-27  9:48 ` [PATCH 08/10] cmd: add "efidebug capsule" command AKASHI Takahiro
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

In this commit, a very simple firmware management protocol driver
is implemented. It will take a single FIT image firmware in a capsule
and apply the data using an existing update_tftp() interface.

To specify a device and location to be updated,
   CONFIG_EFI_CAPSULE_FIT_INTERFACE, and
   CONFIG_EFI_CAPSULE_FIT_DEVICE
are used.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_api.h             |   4 +
 lib/efi_loader/Kconfig        |  24 +++++
 lib/efi_loader/Makefile       |   1 +
 lib/efi_loader/efi_firmware.c | 191 ++++++++++++++++++++++++++++++++++
 4 files changed, 220 insertions(+)
 create mode 100644 lib/efi_loader/efi_firmware.c

diff --git a/include/efi_api.h b/include/efi_api.h
index 7d7aab190657..e518ffa83839 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -1835,6 +1835,10 @@ struct efi_signature_list {
 	EFI_GUID(0x86c77a67, 0x0b97, 0x4633, 0xa1, 0x87, \
 		 0x49, 0x10, 0x4d, 0x06, 0x85, 0xc7)
 
+#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID \
+	EFI_GUID(0xae13ff2d, 0x9ad4, 0x4e25, 0x9a, 0xc8, \
+		 0x6d, 0x80, 0xb3, 0xb2, 0x21, 0x47)
+
 #define EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE		0x1
 #define EFI_IMAGE_ATTRIBUTE_RESET_REQUIRED		0x2
 #define EFI_IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED	0x4
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 2a8fe6310d4a..ec2976ceba7e 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -86,6 +86,30 @@ config EFI_CAPSULE_FIRMWARE_MANAGEMENT
 	  Select this option if you want to enable capsule-based
 	  firmware update using Firmware Management Protocol.
 
+config EFI_CAPSULE_FIT_SIMPLE
+	bool "FMP driver for simple FIT image"
+	depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
+	depends on FIT
+	select UPDATE_TFTP
+	select DFU
+	default n
+	help
+	  Select this option if you want to enable firmware management protocol
+	  driver for simple FIT image
+
+config EFI_CAPSULE_FIT_INTERFACE
+	string "Storage interface for storing FIT image"
+	depends on EFI_CAPSULE_FIT_SIMPLE
+	help
+	  Define storage interface (i.e. scsi, mmc and so on) for storing
+	  FIT image
+
+config EFI_CAPSULE_FIT_DEVICE
+	string "Storage device for storing FIT image"
+	depends on EFI_CAPSULE_FIT_SIMPLE
+	help
+	  Define storage device, say 1:1, for storing FIT image
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 2ee1e683d9c5..2828dbbaba61 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
 obj-y += efi_bootmgr.o
 obj-y += efi_boottime.o
 obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
+obj-$(CONFIG_EFI_CAPSULE_FIT_SIMPLE) += efi_firmware.o
 obj-y += efi_console.o
 obj-y += efi_device_path.o
 obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o
diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
new file mode 100644
index 000000000000..3c3864bd23b0
--- /dev/null
+++ b/lib/efi_loader/efi_firmware.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Firmware management protocol for FIT image
+ *
+ *  Copyright (c) 2020 Linaro Limited
+ *			Author: AKASHI Takahiro
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <net.h>
+
+/*
+ * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a simple
+ * firmware update method, and handles
+ *   - a single region of firmware via DFU
+ * but doesn't support
+ *   - versioning of firmware image
+ *   - package information
+ */
+const efi_guid_t efi_firmware_image_type_uboot_fit =
+	EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
+
+static
+efi_status_t EFIAPI efi_fmp_fit_get_image_info(
+	struct efi_firmware_management_protocol *this,
+	efi_uintn_t *image_info_size,
+	struct efi_firmware_image_descriptor *image_info,
+	u32 *descriptor_version,
+	u8 *descriptor_count,
+	efi_uintn_t *descriptor_size,
+	u32 *package_version,
+	u16 **package_version_name)
+{
+	efi_status_t ret = EFI_SUCCESS;
+
+	EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
+		  image_info_size, image_info,
+		  descriptor_version, descriptor_count, descriptor_size,
+		  package_version, package_version_name);
+
+	if (!image_info_size)
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+	if (*image_info_size < sizeof(*image_info)) {
+		*image_info_size = sizeof(*image_info);
+		return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+	}
+
+	if (!image_info)
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+	*image_info_size = sizeof(*image_info);
+	if (descriptor_version)
+		*descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
+	if (descriptor_count)
+		*descriptor_count = 1;
+	if (descriptor_size)
+		*descriptor_size = sizeof(*image_info);
+	if (package_version)
+		*package_version = 0xffffffff; /* not supported */
+	if (package_version_name)
+		*package_version_name = NULL; /* not supported */
+
+	image_info[0].image_index = 1;
+	image_info[0].image_type_id = efi_firmware_image_type_uboot_fit;
+	image_info[0].image_id = 0;
+	image_info[0].image_id_name = L"fw_simple";
+	image_info[0].version = 0; /* not supported */
+	image_info[0].version_name = NULL; /* not supported */
+	image_info[0].size = 0;
+#if defined(CONFIG_FIT) && defined(CONFIG_UPDATE_TFTP)
+	image_info[0].attributes_supported =
+			EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
+	image_info[0].attributes_setting = EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
+#else
+	image_info[0].attributes_supported = 0;
+	image_info[0].attributes_setting = 0;
+#endif
+	image_info[0].lowest_supported_image_version = 0;
+	image_info[0].last_attempt_version = 0;
+	image_info[0].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
+	image_info[0].hardware_instance = 1;
+	image_info[0].dependencies = NULL;
+
+	return EFI_EXIT(ret);
+}
+
+static
+efi_status_t EFIAPI efi_fmp_fit_get_image(
+	struct efi_firmware_management_protocol *this,
+	u8 image_index,
+	void *image,
+	efi_uintn_t *image_size)
+{
+	EFI_ENTRY("%p %d %p %p\n", this, image_index, image, image_size);
+
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static
+efi_status_t EFIAPI efi_fmp_fit_set_image(
+	struct efi_firmware_management_protocol *this,
+	u8 image_index,
+	const void *image,
+	efi_uintn_t image_size,
+	const void *vendor_code,
+	efi_status_t (*progress)(efi_uintn_t completion),
+	u16 **abort_reason)
+{
+	EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image,
+		  image_size, vendor_code, progress, abort_reason);
+
+#if defined(CONFIG_FIT) && defined(CONFIG_UPDATE_TFTP)
+	if (!image || image_index != 1)
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+	if (update_tftp((ulong)image, CONFIG_EFI_CAPSULE_FIT_INTERFACE,
+			CONFIG_EFI_CAPSULE_FIT_DEVICE))
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+
+	return EFI_EXIT(EFI_SUCCESS);
+#else
+	return EFI_EXIT(EFI_UNSUPPORTED);
+#endif
+}
+
+static
+efi_status_t EFIAPI efi_fmp_fit_check_image(
+	struct efi_firmware_management_protocol *this,
+	u8 image_index,
+	const void *image,
+	efi_uintn_t *image_size,
+	u32 *image_updatable)
+{
+	EFI_ENTRY("%p %d %p %p %p\n", this, image_index, image, image_size,
+		  image_updatable);
+
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static
+efi_status_t EFIAPI efi_fmp_fit_get_package_info(
+	struct efi_firmware_management_protocol *this,
+	u32 *package_version,
+	u16 **package_version_name,
+	u32 *package_version_name_maxlen,
+	u64 *attributes_supported,
+	u64 *attributes_setting)
+{
+	EFI_ENTRY("%p %p %p %p %p %p\n", this, package_version,
+		  package_version_name, package_version_name_maxlen,
+		  attributes_supported, attributes_setting);
+
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static
+efi_status_t EFIAPI efi_fmp_fit_set_package_info(
+	struct efi_firmware_management_protocol *this,
+	const void *image,
+	efi_uintn_t *image_size,
+	const void *vendor_code,
+	u32 package_version,
+	const u16 *package_version_name)
+{
+	EFI_ENTRY("%p %p %p %p %x %p\n", this, image, image_size, vendor_code,
+		  package_version, package_version_name);
+
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+const struct efi_firmware_management_protocol efi_fmp_fit_simple = {
+	.get_image_info = efi_fmp_fit_get_image_info,
+	.get_image = efi_fmp_fit_get_image,
+	.set_image = efi_fmp_fit_set_image,
+	.check_image = efi_fmp_fit_check_image,
+	.get_package_info = efi_fmp_fit_get_package_info,
+	.set_package_info = efi_fmp_fit_set_package_info,
+};
+
+efi_status_t arch_efi_load_capsule_drivers(void)
+{
+	efi_status_t ret;
+
+	ret = EFI_CALL(efi_install_multiple_protocol_interfaces(
+			&efi_root, &efi_guid_firmware_management_protocol,
+			&efi_fmp_fit_simple, NULL));
+
+	return ret;
+}
-- 
2.25.2

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

* [PATCH 08/10] cmd: add "efidebug capsule" command
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
                   ` (6 preceding siblings ...)
  2020-04-27  9:48 ` [PATCH 07/10] efi_loader: add simple firmware management protocol for FIT image AKASHI Takahiro
@ 2020-04-27  9:48 ` AKASHI Takahiro
  2020-04-30 12:38   ` Sughosh Ganu
  2020-04-27  9:48 ` [PATCH 09/10] tools: add mkeficapsule command for UEFI capsule update test AKASHI Takahiro
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

"efidebug capsule" is more or less a debugging utility.
  efidebug capsule update: invoke UpdateCapsule against data on memory
  efidebug capsule show: show a capsule header
  efidebug capsule result: dump a capsule result variable

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 cmd/efidebug.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 234 insertions(+)

diff --git a/cmd/efidebug.c b/cmd/efidebug.c
index 02ef01969443..8956fa1d50be 100644
--- a/cmd/efidebug.c
+++ b/cmd/efidebug.c
@@ -19,6 +19,227 @@
 #define BS systab.boottime
 #define RT systab.runtime
 
+#ifdef CONFIG_EFI_CAPSULE_UPDATE
+/**
+ * do_efi_capsule_update() - process a capsule update
+ *
+ * @cmdtp:	Command table
+ * @flag:	Command flag
+ * @argc:	Number of arguments
+ * @argv:	Argument array
+ * Return:	CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "capsule update" sub-command.
+ * process a capsule update.
+ *
+ *     efidebug capsule update [-v] <capsule address>
+ */
+static int do_efi_capsule_update(cmd_tbl_t *cmdtp, int flag,
+				 int argc, char * const argv[])
+{
+	struct efi_capsule_header *capsule;
+	int verbose = 0;
+	char *endp;
+	efi_status_t ret;
+
+	if (argc != 2 && argc != 3)
+		return CMD_RET_USAGE;
+
+	if (argc == 3) {
+		if (strcmp(argv[1], "-v"))
+			return CMD_RET_USAGE;
+
+		verbose = 1;
+		argc--;
+		argv++;
+	}
+
+	capsule = (typeof(capsule))simple_strtoul(argv[1], &endp, 16);
+	if (endp == argv[1]) {
+		printf("Invalid address: %s", argv[1]);
+		return CMD_RET_FAILURE;
+	}
+
+	if (verbose) {
+		printf("Capsule guid: %pUl\n", &capsule->capsule_guid);
+		printf("Capsule flags: 0x%x\n", capsule->flags);
+		printf("Capsule header size: 0x%x\n", capsule->header_size);
+		printf("Capsule image size: 0x%x\n",
+		       capsule->capsule_image_size);
+	}
+
+	ret = EFI_CALL(RT->update_capsule(&capsule, 1, (u64)NULL));
+	if (ret) {
+		printf("Cannot handle a capsule at %p", capsule);
+		return CMD_RET_FAILURE;
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_efi_capsule_show() - show capsule information
+ *
+ * @cmdtp:	Command table
+ * @flag:	Command flag
+ * @argc:	Number of arguments
+ * @argv:	Argument array
+ * Return:	CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "capsule show" sub-command.
+ * show capsule information.
+ *
+ *     efidebug capsule show <capsule address>
+ */
+static int do_efi_capsule_show(cmd_tbl_t *cmdtp, int flag,
+			       int argc, char * const argv[])
+{
+	struct efi_capsule_header *capsule;
+	char *endp;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	capsule = (typeof(capsule))simple_strtoul(argv[1], &endp, 16);
+	if (endp == argv[1]) {
+		printf("Invalid address: %s", argv[1]);
+		return CMD_RET_FAILURE;
+	}
+
+	printf("Capsule guid: %pUl\n", &capsule->capsule_guid);
+	printf("Capsule flags: 0x%x\n", capsule->flags);
+	printf("Capsule header size: 0x%x\n", capsule->header_size);
+	printf("Capsule image size: 0x%x\n",
+	       capsule->capsule_image_size);
+
+	return CMD_RET_SUCCESS;
+}
+
+/**
+ * do_efi_capsule_res() - show a capsule update result
+ *
+ * @cmdtp:	Command table
+ * @flag:	Command flag
+ * @argc:	Number of arguments
+ * @argv:	Argument array
+ * Return:	CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "capsule result" sub-command.
+ * show a capsule update result.
+ * If result number is not specified, CapsuleLast will be shown.
+ *
+ *     efidebug capsule result [<capsule result number>]
+ */
+static int do_efi_capsule_res(cmd_tbl_t *cmdtp, int flag,
+			      int argc, char * const argv[])
+{
+	int capsule_id;
+	char *endp;
+	char var_name[12];
+	u16 var_name16[12], *p;
+	efi_guid_t guid;
+	struct efi_capsule_result_variable_header *result = NULL;
+	efi_uintn_t size;
+	efi_status_t ret;
+
+	if (argc != 1 && argc != 2)
+		return CMD_RET_USAGE;
+
+	guid = efi_guid_capsule_report;
+	if (argc == 1) {
+		size = sizeof(var_name16);
+		ret = EFI_CALL(RT->get_variable(L"CapsuleLast", &guid, NULL,
+						&size, var_name16));
+		if (ret != EFI_SUCCESS) {
+			if (ret == EFI_NOT_FOUND)
+				printf("CapsuleLast doesn't exist\n");
+			else
+				printf("Failed to get CapsuleLast\n");
+
+			return CMD_RET_FAILURE;
+		}
+		printf("CapsuleLast is %ls\n", var_name16);
+	} else {
+		argc--;
+		argv++;
+
+		capsule_id = simple_strtoul(argv[0], &endp, 16);
+		if (capsule_id < 0 || capsule_id > 0xffff)
+			return CMD_RET_USAGE;
+
+		sprintf(var_name, "Capsule%04X", capsule_id);
+		p = var_name16;
+		utf8_utf16_strncpy(&p, var_name, 9);
+	}
+
+	size = 0;
+	ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size, NULL));
+	if (ret == EFI_BUFFER_TOO_SMALL) {
+		result = malloc(size);
+		ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size,
+						result));
+		if (ret != EFI_SUCCESS) {
+			free(result);
+			printf("Failed to get %ls\n", var_name16);
+
+			return CMD_RET_FAILURE;
+		}
+	}
+
+	printf("Result total size: 0x%x\n", result->variable_total_size);
+	printf("Capsule guid: %pUl\n", &result->capsule_guid);
+	printf("Time processed: %04d-%02d-%02d %02d:%02d:%02d\n",
+	       result->capsule_processed.year, result->capsule_processed.month,
+	       result->capsule_processed.day, result->capsule_processed.hour,
+	       result->capsule_processed.minute,
+	       result->capsule_processed.second);
+	printf("Capsule status: 0x%lx\n", result->capsule_status);
+
+	free(result);
+
+	return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t cmd_efidebug_capsule_sub[] = {
+	U_BOOT_CMD_MKENT(update, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_update,
+			 "", ""),
+	U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show,
+			 "", ""),
+	U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res,
+			 "", ""),
+};
+
+/**
+ * do_efi_capsule() - manage UEFI capsules
+ *
+ * @cmdtp:	Command table
+ * @flag:	Command flag
+ * @argc:	Number of arguments
+ * @argv:	Argument array
+ * Return:	CMD_RET_SUCCESS on success,
+ *		CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "capsule" sub-command.
+ */
+static int do_efi_capsule(cmd_tbl_t *cmdtp, int flag,
+			  int argc, char * const argv[])
+{
+	cmd_tbl_t *cp;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	argc--; argv++;
+
+	cp = find_cmd_tbl(argv[0], cmd_efidebug_capsule_sub,
+			  ARRAY_SIZE(cmd_efidebug_capsule_sub));
+	if (!cp)
+		return CMD_RET_USAGE;
+
+	return cp->cmd(cmdtp, flag, argc, argv);
+}
+#endif /* CONFIG_EFI_CAPSULE_UPDATE */
+
 /**
  * efi_get_device_handle_info() - get information of UEFI device
  *
@@ -1163,6 +1384,10 @@ static int do_efi_test(cmd_tbl_t *cmdtp, int flag,
 
 static cmd_tbl_t cmd_efidebug_sub[] = {
 	U_BOOT_CMD_MKENT(boot, CONFIG_SYS_MAXARGS, 1, do_efi_boot_opt, "", ""),
+#ifdef CONFIG_EFI_CAPSULE_UPDATE
+	U_BOOT_CMD_MKENT(capsule, CONFIG_SYS_MAXARGS, 1, do_efi_capsule,
+			 "", ""),
+#endif
 	U_BOOT_CMD_MKENT(devices, CONFIG_SYS_MAXARGS, 1, do_efi_show_devices,
 			 "", ""),
 	U_BOOT_CMD_MKENT(drivers, CONFIG_SYS_MAXARGS, 1, do_efi_show_drivers,
@@ -1235,6 +1460,15 @@ static char efidebug_help_text[] =
 	"efidebug boot order [<bootid#1> [<bootid#2> [<bootid#3> [...]]]]\n"
 	"  - set/show UEFI boot order\n"
 	"\n"
+#ifdef CONFIG_EFI_CAPSULE_UPDATE
+	"efidebug capsule update [-v] <capsule address>\n"
+	"  - process a capsule\n"
+	"efidebug capsule show <capsule address>\n"
+	"  - show capsule information\n"
+	"efidebug capsule result [<capsule result var>]\n"
+	"  - show a capsule update result\n"
+	"\n"
+#endif
 	"efidebug devices\n"
 	"  - show UEFI devices\n"
 	"efidebug drivers\n"
-- 
2.25.2

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

* [PATCH 09/10] tools: add mkeficapsule command for UEFI capsule update test
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
                   ` (7 preceding siblings ...)
  2020-04-27  9:48 ` [PATCH 08/10] cmd: add "efidebug capsule" command AKASHI Takahiro
@ 2020-04-27  9:48 ` AKASHI Takahiro
  2020-04-27 20:15   ` Heinrich Schuchardt
  2020-04-27  9:48 ` [PATCH 10/10] test/py: add a test for efi firmware update capsule AKASHI Takahiro
  2020-04-27 20:33 ` [PATCH 00/10] efi_loader: add capsule update support Heinrich Schuchardt
  10 siblings, 1 reply; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

This is a utility mainly for test purpose.
  mkeficapsule -f: create a test capsule file for FIT image firmware

Having said that, you will be able to customize the code to fit
your specific requirements for your platform.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 tools/Makefile       |   3 +
 tools/mkeficapsule.c | 209 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 212 insertions(+)
 create mode 100644 tools/mkeficapsule.c

diff --git a/tools/Makefile b/tools/Makefile
index c2b26340047a..08e97063d5fa 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -222,6 +222,9 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
 hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
 HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
 
+# TODO: only build this for capsule pytest
+hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
+
 # We build some files with extra pedantic flags to try to minimize things
 # that won't build on some weird host compiler -- though there are lots of
 # exceptions for files that aren't complaint.
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
new file mode 100644
index 000000000000..b26c88e42020
--- /dev/null
+++ b/tools/mkeficapsule.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2018 Linaro Limited
+ *		Author: AKASHI Takahiro
+ */
+
+#include <malloc.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+/*
+ * TODO: use libefi/libgnuefi headers
+ */
+
+typedef u_int8_t u8;
+typedef u_int16_t u16;
+typedef u_int32_t u32;
+typedef u_int64_t u64;
+
+/* include/efi.h */
+typedef struct {
+	u8 b[16];
+} efi_guid_t;
+
+#define EFI_GUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \
+	{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, \
+		((a) >> 24) & 0xff, \
+		(b) & 0xff, ((b) >> 8) & 0xff, \
+		(c) & 0xff, ((c) >> 8) & 0xff, \
+		(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } }
+
+#define EFI_VARIABLE_NON_VOLATILE	0x0000000000000001
+#define EFI_VARIABLE_BOOTSERVICE_ACCESS	0x0000000000000002
+#define EFI_VARIABLE_RUNTIME_ACCESS	0x0000000000000004
+
+/* include/efi_api.h */
+#define EFI_GLOBAL_VARIABLE_GUID \
+	EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
+		 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
+
+#define CAPSULE_FLAGS_PERSIST_ACROSS_RESET	0x00010000
+#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE	0x00020000
+#define CAPSULE_FLAGS_INITIATE_RESET		0x00040000
+
+#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID \
+	EFI_GUID(0xae13ff2d, 0x9ad4, 0x4e25, 0x9a, 0xc8, \
+		 0x6d, 0x80, 0xb3, 0xb2, 0x21, 0x47)
+
+#define EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID \
+	EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \
+		 0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a)
+
+struct efi_capsule_header {
+	efi_guid_t capsule_guid;
+	u32 header_size;
+	u32 flags;
+	u32 capsule_image_size;
+} __attribute__((packed));
+
+struct efi_capsule_block_descriptor {
+	u64 length;
+	union {
+		u64 data_block;
+		u64 continuation_ptr;
+	};
+} __attribute__((packed));
+
+struct efi_firmware_management_capsule_header {
+	u32 version;
+	u16 embedded_driver_count;
+	u16 payload_item_count;
+	u64 item_offset_list[];
+} __attribute__((packed));
+
+struct efi_firmware_management_capsule_image_header {
+	u32 version;
+	efi_guid_t update_image_type_id;
+	u8 update_image_index;
+	u8 reserved[3];
+	u32 update_image_size;
+	u32 update_vendor_code_size;
+	u64 update_hardware_instance;
+} __attribute__((packed));
+
+efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
+efi_guid_t efi_guid_image_type_uboot_fit =
+		EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
+
+static int create_fwbin(char *bin, char *path)
+{
+	struct efi_capsule_header header;
+	struct efi_firmware_management_capsule_header capsule;
+	struct efi_firmware_management_capsule_image_header image;
+	FILE *f, *g;
+	struct stat bin_stat;
+	u8 *data;
+	size_t size;
+
+	g = fopen(bin, "r");
+	if (!g) {
+		printf("cannot open %s\n", bin);
+		return -1;
+	}
+	if (stat(bin, &bin_stat) < 0) {
+		printf("cannot determine the size of %s\n", bin);
+		goto err_1;
+	}
+	data = malloc(bin_stat.st_size);
+	if (!data) {
+		printf("cannot allocate memory: %lx\n", bin_stat.st_size);
+		goto err_1;
+	}
+	f = fopen(path, "w");
+	if (!f) {
+		printf("cannot open %s\n", path);
+		goto err_2;
+	}
+	header.capsule_guid = efi_guid_fm_capsule;
+	header.header_size = sizeof(header);
+	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
+	header.capsule_image_size = sizeof(header)
+					+ sizeof(capsule) + sizeof(u64)
+					+ sizeof(image)
+					+ bin_stat.st_size;
+
+	size = fwrite(&header, 1, sizeof(header), f);
+	if (size < sizeof(header)) {
+		printf("write failed (%lx)\n", size);
+		goto err_3;
+	}
+
+	capsule.version = 0x00000001;
+	capsule.embedded_driver_count = 0;
+	capsule.payload_item_count = 1;
+	capsule.item_offset_list[0] = sizeof(capsule) + sizeof(u64);
+	size = fwrite(&capsule, 1, sizeof(capsule) + sizeof(u64), f);
+	if (size < (sizeof(capsule) + sizeof(u64))) {
+		printf("write failed (%lx)\n", size);
+		goto err_3;
+	}
+
+	image.version = 0x00000002;
+	image.update_image_type_id = efi_guid_image_type_uboot_fit;
+	image.update_image_index = 1;
+	image.update_image_size = bin_stat.st_size;
+	image.update_vendor_code_size = 0; /* none */
+	image.update_hardware_instance = 1;
+
+	size = fwrite(&image, 1, sizeof(image), f);
+	if (size < sizeof(image)) {
+		printf("write failed (%lx)\n", size);
+		goto err_3;
+	}
+	size = fread(data, 1, bin_stat.st_size, g);
+	if (size < bin_stat.st_size) {
+		printf("read failed (%lx)\n", size);
+		goto err_3;
+	}
+	size = fwrite(data, 1, bin_stat.st_size, f);
+	if (size < bin_stat.st_size) {
+		printf("write failed (%lx)\n", size);
+		goto err_3;
+	}
+
+	fclose(f);
+	fclose(g);
+	free(data);
+
+	return 0;
+
+err_3:
+	fclose(f);
+err_2:
+	free(data);
+err_1:
+	fclose(g);
+
+	return -1;
+}
+
+void print_usage(void)
+{
+	printf("mkeficapsule -f <firmware binary> <output file>\n");
+}
+
+/*
+ * Usage:
+ *   $ mkeficapsule -f <firmware binary> <output file>
+ */
+int main(int argc, char **argv)
+{
+	if (argc != 4) {
+		print_usage();
+		return -1;
+	}
+
+	if (!strcmp(argv[1], "-f")) {
+		if (create_fwbin(argv[2], argv[3]) < 0) {
+			printf("Creating firmware capsule for %s failed\n",
+			       argv[2]);
+			return -1;
+		}
+
+		return 0;
+	}
+
+	print_usage();
+	return -1;
+}
-- 
2.25.2

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

* [PATCH 10/10] test/py: add a test for efi firmware update capsule
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
                   ` (8 preceding siblings ...)
  2020-04-27  9:48 ` [PATCH 09/10] tools: add mkeficapsule command for UEFI capsule update test AKASHI Takahiro
@ 2020-04-27  9:48 ` AKASHI Takahiro
  2020-04-27 20:33 ` [PATCH 00/10] efi_loader: add capsule update support Heinrich Schuchardt
  10 siblings, 0 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27  9:48 UTC (permalink / raw)
  To: u-boot

The test can run on sandbox build and it attempts to execute a firmware
update via a capsule-on-disk, using a simple FIT image capsule,
CONFIG_EFI_CAPSULE_FIT_SIMPLE.

To run this test successfully, you need configure U-Boot specifically;
See test_capsule_firmware.py for requirements, and hence it won't run
on Travis CI.

In addition, it will give you some idea about how a capsule should work
on production system regarding firmware update.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 test/py/tests/test_efi_capsule/conftest.py    |  73 +++++++
 test/py/tests/test_efi_capsule/defs.py        |  24 +++
 .../test_efi_capsule/test_capsule_firmware.py | 198 ++++++++++++++++++
 test/py/tests/test_efi_capsule/uboot_env.its  |  25 +++
 4 files changed, 320 insertions(+)
 create mode 100644 test/py/tests/test_efi_capsule/conftest.py
 create mode 100644 test/py/tests/test_efi_capsule/defs.py
 create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware.py
 create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its

diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py
new file mode 100644
index 000000000000..6db7aa07ba97
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier:      GPL-2.0+
+# Copyright (c) 2020, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+import os
+import os.path
+import pytest
+import re
+from subprocess import call, check_call, check_output, CalledProcessError
+from defs import *
+
+#
+# Fixture for UEFI secure boot test
+#
+ at pytest.fixture(scope='session')
+def efi_capsule_data(request, u_boot_config):
+    """Set up a file system to be used in UEFI capsule test.
+
+    Args:
+        request: Pytest request object.
+	u_boot_config: U-boot configuration.
+
+    Return:
+        A path to disk image to be used for testing
+    """
+    image_path = u_boot_config.persistent_data_dir
+    image_path = image_path + '/' + EFI_BOOTDEV_IMAGE_NAME
+
+    try:
+        # create U-Boot environment storage
+        check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
+
+        # create a disk/partition
+        check_call('dd if=/dev/zero of=%s bs=1MiB count=%d'
+                            % (image_path, EFI_BOOTDEV_IMAGE_SIZE), shell=True)
+        check_call('sgdisk %s -n 1:0:+%dMiB -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B'
+                            % (image_path, EFI_BOOTDEV_PART_SIZE), shell=True)
+        # create a file system
+        check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d'
+                            % (image_path, EFI_BOOTDEV_PART_SIZE), shell=True)
+        check_call('mkfs -t %s %s.tmp'
+                            % (EFI_BOOTDEV_FS_TYPE, image_path), shell=True)
+        check_call('dd if=%s.tmp of=%s bs=1MiB seek=1 count=%d conv=notrunc'
+                            % (image_path, image_path, 1), shell=True)
+        check_call('rm %s.tmp' % image_path, shell=True)
+        loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"'
+                            % (EFI_BOOTDEV_PART_SIZE, image_path),
+                            shell=True).decode()
+        check_call('sudo mkdir -p %s' % MNT_PNT, shell=True)
+        check_call('sudo mount -t %s -o umask=000 %s %s'
+                            % (EFI_BOOTDEV_FS_TYPE, loop_dev, MNT_PNT),
+                            shell=True)
+        check_call('mkdir -p %s%s' % (MNT_PNT, CAPSULE_DATA_DIR), shell=True)
+        check_call('mkdir -p %s%s' % (MNT_PNT, CAPSULE_INSTALL_DIR), shell=True)
+
+        # Create its for FIT image
+        check_call('sed -e \"s?BINFILE?%s%s/%s?\" %s/test/py/tests/test_efi_capsule/uboot_env.its > %s%s/uboot_env.its'
+                            % (MNT_PNT, CAPSULE_DATA_DIR, FW_BIN,
+                               u_boot_config.source_dir,
+                               MNT_PNT, CAPSULE_DATA_DIR), shell=True)
+
+        call('sudo umount %s' % loop_dev, shell=True)
+        call('sudo losetup -d %s' % loop_dev, shell=True)
+
+    except CalledProcessError as e:
+        pytest.skip('Setup failed: %s' % e.cmd)
+        return
+    else:
+        yield image_path
+    finally:
+        call('sudo rm -rf %s' % MNT_PNT, shell=True)
+        call('rm -f %s' % image_path, shell=True)
+        call('rm -f ./spi.bin', shell=True)
diff --git a/test/py/tests/test_efi_capsule/defs.py b/test/py/tests/test_efi_capsule/defs.py
new file mode 100644
index 000000000000..2d6d43aea0b2
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/defs.py
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier:      GPL-2.0+
+
+# Disk image name
+EFI_BOOTDEV_IMAGE_NAME='test_efi_capsule.img'
+
+# Size in MiB
+EFI_BOOTDEV_IMAGE_SIZE=16
+EFI_BOOTDEV_PART_SIZE=8
+
+# Partition file system type
+EFI_BOOTDEV_FS_TYPE='vfat'
+
+# Mount Point for set-up
+MNT_PNT='/mnt/test_efi_capsule'
+
+# Owner guid
+GUID='11111111-2222-3333-4444-123456789abc'
+
+# Directories
+CAPSULE_DATA_DIR='/EFI/CapsuleTestData'
+CAPSULE_INSTALL_DIR='/EFI/UpdateCapsule'
+
+#
+FW_BIN='spi_sf.bin'
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
new file mode 100644
index 000000000000..887a7d1906a7
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
@@ -0,0 +1,198 @@
+# SPDX-License-Identifier:      GPL-2.0+
+# Copyright (c) 2020, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Capsule Update for Simple FIT Image Test
+
+"""
+This test verifies capsule-on-disk firmware update
+"""
+
+import pytest
+import re
+from defs import *
+from subprocess import call, check_call, check_output, CalledProcessError
+
+#
+# Setup for FMP driver
+#  CONFIG_EFI_CAPSULE_FIT_INTERFACE: sf
+#  CONFIG_EFI_CAPSULE_FIT_DEVICE:    1:1
+#
+ at pytest.mark.boardspec('sandbox')
+ at pytest.mark.buildconfigspec('efi_capsule_fit_simple')
+ at pytest.mark.buildconfigspec('efi_capsule_on_disk')
+ at pytest.mark.buildconfigspec('dfu')
+ at pytest.mark.buildconfigspec('dfu_sf')
+ at pytest.mark.buildconfigspec('dfu_tftp')
+ at pytest.mark.buildconfigspec('cmd_saveenv')
+ at pytest.mark.buildconfigspec('env_is_in_spi_flash')
+ at pytest.mark.buildconfigspec('cmd_efidebug')
+ at pytest.mark.buildconfigspec('cmd_fat')
+ at pytest.mark.buildconfigspec('cmd_nvedit_efi')
+ at pytest.mark.slow
+class TestEfiCapsuleFirmwareSimple(object):
+    def test_efi_capsule_fw1(self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 1 - Update U-Boot environment on SPI Flash
+        """
+        # "-T" (or "-D") is required to enable spi flash on sandbox
+        u_boot_console.restart_uboot_with_flags('-T')
+
+        disk_img = efi_capsule_data
+        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 order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info sf raw 0 0x200000',
+                'env set FW_STATUS This is Old environment',
+                'env print FW_STATUS',
+                'env save'])
+            assert('Old environment' in ''.join(output))
+
+            output = u_boot_console.run_command_list([
+                'env set FW_STATUS This is New environment',
+                'env export -c 5000000',
+                'fatwrite host 0:1 5000000 %s/%s $filesize'
+                        % (CAPSULE_DATA_DIR, FW_BIN),
+                'env set -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0000',
+                'fatls host 0:1 %s' % CAPSULE_DATA_DIR
+                ])
+            assert(('%s' % FW_BIN) in ''.join(output))
+
+        # create a capsule file
+        try:
+            loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"'
+                        % (EFI_BOOTDEV_PART_SIZE, disk_img),
+                        shell=True).decode()
+            check_call('sudo mount -t %s -o umask=000 %s %s'
+                        % (EFI_BOOTDEV_FS_TYPE, loop_dev, MNT_PNT), shell=True)
+            check_call('%s/tools/mkimage -f %s%s/uboot_env.its %s%s/uboot_env.itb'
+                        % (u_boot_config.build_dir,
+                           MNT_PNT, CAPSULE_DATA_DIR,
+                           MNT_PNT, CAPSULE_DATA_DIR), shell=True)
+            check_call('%s/tools/mkeficapsule -f %s%s/uboot_env.itb %s%s/Test01'
+                        % (u_boot_config.build_dir,
+                           MNT_PNT, CAPSULE_DATA_DIR,
+                           MNT_PNT, CAPSULE_INSTALL_DIR), shell=True)
+            check_call('ls %s/%s' % (MNT_PNT, CAPSULE_INSTALL_DIR), shell=True)
+            check_call('sudo umount %s' % loop_dev, shell=True)
+            check_call('sudo losetup -d %s' % loop_dev, shell=True)
+        except CalledProcessError as e:
+            assert('failed to create firmware capsule: %s' % e.cmd)
+
+        # reboot
+        u_boot_console.restart_uboot_with_flags('-T')
+
+        capsule_early = u_boot_config.buildconfig.get('config_efi_capsule_on_disk_early')
+        if not capsule_early:
+            with u_boot_console.log.section('Test Case 1-b, after reboot'):
+                output = u_boot_console.run_command_list([
+                    'host bind 0 %s' % disk_img,
+                    'env print FW_STATUS'])
+                assert('Old environment' in ''.join(output))
+
+                output = u_boot_console.run_command('fatls host 0:1 %s'
+                                                        % CAPSULE_INSTALL_DIR)
+                assert('Test01' in output)
+
+            # need to run uefi command to initiate capsule handling
+            output = u_boot_console.run_command('env print -e -all Capsule0000')
+
+            # reboot again
+            u_boot_console.restart_uboot_with_flags('-T')
+
+        with u_boot_console.log.section('Test Case 1-c, after reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'env print FW_STATUS'])
+            assert('New environment' in ''.join(output))
+
+            output = u_boot_console.run_command(
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR)
+            assert(not 'Test01' in output)
+
+    def test_efi_capsule_fw2(self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 2 - Update U-Boot environment on SPI Flash
+                      but with OsIndications unset
+                      No update should happen
+        """
+        # "-T" (or "-D") is required to enable spi flash on sandbox
+        u_boot_console.restart_uboot_with_flags('-T')
+
+        disk_img = efi_capsule_data
+        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 order 1',
+                'env set -e OsIndications',
+                'env set dfu_alt_info sf raw 0 0x200000',
+                'env set FW_STATUS This is Old environment',
+                'env print FW_STATUS',
+                'env save'])
+            assert('Old environment' in ''.join(output))
+
+            output = u_boot_console.run_command_list([
+                'env set FW_STATUS This is New environment',
+                'env export -c 5000000',
+                'fatwrite host 0:1 5000000 %s/%s $filesize'
+                        % (CAPSULE_DATA_DIR, FW_BIN),
+                'env set -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0000',
+                'fatls host 0:1 %s' % CAPSULE_DATA_DIR
+                ])
+            assert(('%s' % FW_BIN) in ''.join(output))
+
+        # create a capsule file
+        try:
+            loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"'
+                        % (EFI_BOOTDEV_PART_SIZE, disk_img),
+                        shell=True).decode()
+            check_call('sudo mount -t %s -o umask=000 %s %s'
+                        % (EFI_BOOTDEV_FS_TYPE, loop_dev, MNT_PNT), shell=True)
+            check_call('%s/tools/mkimage -f %s%s/uboot_env.its %s%s/uboot_env.itb'
+                        % (u_boot_config.build_dir,
+                           MNT_PNT, CAPSULE_DATA_DIR,
+                           MNT_PNT, CAPSULE_DATA_DIR), shell=True)
+            check_call('%s/tools/mkeficapsule -f %s%s/uboot_env.itb %s%s/Test01'
+                        % (u_boot_config.build_dir,
+                           MNT_PNT, CAPSULE_DATA_DIR,
+                           MNT_PNT, CAPSULE_INSTALL_DIR), shell=True)
+            check_call('ls %s/%s' % (MNT_PNT, CAPSULE_INSTALL_DIR), shell=True)
+            check_call('sudo umount %s' % loop_dev, shell=True)
+            check_call('sudo losetup -d %s' % loop_dev, shell=True)
+        except CalledProcessError as e:
+            assert('failed to create firmware capsule: %s' % e.cmd)
+
+        # reboot
+        u_boot_console.restart_uboot_with_flags('-T')
+
+        capsule_early = u_boot_config.buildconfig.get('config_efi_capsule_on_disk_early')
+        if not capsule_early:
+            with u_boot_console.log.section('Test Case 2-b, after reboot'):
+                output = u_boot_console.run_command_list([
+                    'host bind 0 %s' % disk_img,
+                    'env print FW_STATUS'])
+                assert('Old environment' in ''.join(output))
+
+                output = u_boot_console.run_command('fatls host 0:1 %s'
+                                                        % CAPSULE_INSTALL_DIR)
+                assert('Test01' in output)
+
+            # need to run uefi command to initiate capsule handling
+            output = u_boot_console.run_command('env print -e -all Capsule0000')
+
+            # reboot again
+            u_boot_console.restart_uboot_with_flags('-T')
+
+        with u_boot_console.log.section('Test Case 2-c, after reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'env print FW_STATUS'])
+            assert('Old environment' in ''.join(output))
+
+            output = u_boot_console.run_command(
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR)
+            assert('Test01' in output)
diff --git a/test/py/tests/test_efi_capsule/uboot_env.its b/test/py/tests/test_efi_capsule/uboot_env.its
new file mode 100644
index 000000000000..a4484db45834
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/uboot_env.its
@@ -0,0 +1,25 @@
+/*
+ * Automatic software update for U-Boot
+ * Make sure the flashing addresses ('load' prop) is correct for your board!
+ */
+
+/dts-v1/;
+
+/ {
+	description = "Automatic U-Boot environment update";
+	#address-cells = <2>;
+
+	images {
+		sf at 0 {
+			description = "U-Boot environment on SPI Flash";
+			data = /incbin/("BINFILE");
+			compression = "none";
+			type = "firmware";
+			arch = "sandbox";
+			load = <0>;
+			hash-1 {
+				algo = "sha1";
+			};
+		};
+	};
+};
-- 
2.25.2

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

* [PATCH 01/10] efi_loader: disk: add efi_disk_is_system_part()
  2020-04-27  9:48 ` [PATCH 01/10] efi_loader: disk: add efi_disk_is_system_part() AKASHI Takahiro
@ 2020-04-27 19:57   ` Heinrich Schuchardt
  2020-04-27 23:54     ` AKASHI Takahiro
  0 siblings, 1 reply; 33+ messages in thread
From: Heinrich Schuchardt @ 2020-04-27 19:57 UTC (permalink / raw)
  To: u-boot

On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> This function will check if a given handle to device is a EFI system
> partition. It will be utilised in implementing capsule-on-disk feature.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

Just a reminder for me:

This patch depends on
"part: detect EFI system partition"
https://lists.denx.de/pipermail/u-boot/2020-April/408150.html

> ---
>  include/efi_loader.h      |  2 ++
>  lib/efi_loader/efi_disk.c | 22 ++++++++++++++++++++++
>  2 files changed, 24 insertions(+)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index b7bccf50b30c..d4510462d616 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -393,6 +393,8 @@ efi_status_t efi_disk_register(void);
>  int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
>  			       const char *if_typename, int diskid,
>  			       const char *pdevname);
> +/* Check if it is EFI system partition */
> +bool efi_disk_is_system_part(efi_handle_t handle);
>  /* Called by bootefi to make GOP (graphical) interface available */
>  efi_status_t efi_gop_register(void);
>  /* Called by bootefi to make the network interface available */
> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
> index fd3df80b0b96..9f58a8642c85 100644
> --- a/lib/efi_loader/efi_disk.c
> +++ b/lib/efi_loader/efi_disk.c
> @@ -588,3 +588,25 @@ efi_status_t efi_disk_register(void)
>
>  	return EFI_SUCCESS;
>  }
> +

Please, provide a function description, cf.
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation

> +bool efi_disk_is_system_part(efi_handle_t handle)
> +{
> +	struct efi_handler *handler;
> +	struct efi_disk_obj *diskobj;
> +	disk_partition_t info;
> +	efi_status_t ret;
> +	int r;
> +
> +	/* check if this is a block device */
> +	ret = efi_search_protocol(handle, &efi_block_io_guid, &handler);
> +	if (ret != EFI_SUCCESS)
> +		return false;
> +
> +	diskobj = container_of(handle, struct efi_disk_obj, header);

This is just a complicated way of saying:

diskobj = (struct efi_disk_obj *)handle;

I would rather avoid container_of() here though it is not wrong.

Best regards

Heinrich

> +
> +	r = part_get_info(diskobj->desc, diskobj->part, &info);
> +	if (r)
> +		return false;
> +
> +	return info.bootable & PART_EFI_SYSTEM_PARTITION;
> +}
>

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

* [PATCH 02/10] efi_loader: add option to initialise EFI subsystem early
  2020-04-27  9:48 ` [PATCH 02/10] efi_loader: add option to initialise EFI subsystem early AKASHI Takahiro
@ 2020-04-27 20:09   ` Heinrich Schuchardt
  2020-04-28  0:16     ` AKASHI Takahiro
  0 siblings, 1 reply; 33+ messages in thread
From: Heinrich Schuchardt @ 2020-04-27 20:09 UTC (permalink / raw)
  To: u-boot

On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> If this option is enabled, the initialisation of UEFI subsystem will be
> done as part of U-Boot initialisation.
>
> This feature will be utilised in implementing capsule-on-disk feature.

This would mean that we allow unaligned access very early. Something
Siarhei was against:

https://lists.denx.de/pipermail/u-boot/2018-March/324242.html
https://patchwork.ozlabs.org/project/uboot/patch/20180329213350.7868-1-xypron.glpk at gmx.de/

Why can't you wait with the capsule update until any command initializes
the UEFI sub-system.

Best regards

Heinrich

>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  common/board_r.c       | 6 ++++++
>  lib/efi_loader/Kconfig | 4 ++++
>  2 files changed, 10 insertions(+)
>
> diff --git a/common/board_r.c b/common/board_r.c
> index 0bbeaa7594c6..7cf21a6078f9 100644
> --- a/common/board_r.c
> +++ b/common/board_r.c
> @@ -64,6 +64,9 @@
>  #if defined(CONFIG_GPIO_HOG)
>  #include <asm/gpio.h>
>  #endif
> +#ifdef CONFIG_EFI_SETUP_EARLY
> +#include <efi_loader.h>
> +#endif
>
>  DECLARE_GLOBAL_DATA_PTR;
>
> @@ -867,6 +870,9 @@ static init_fnc_t init_sequence_r[] = {
>  #endif
>  #if defined(CONFIG_M68K) && defined(CONFIG_BLOCK_CACHE)
>  	blkcache_init,
> +#endif
> +#ifdef CONFIG_EFI_SETUP_EARLY
> +	(init_fnc_t)efi_init_obj_list,
>  #endif
>  	run_main_loop,
>  };
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 1cfa24ffcf72..7cc2d940f848 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -25,6 +25,10 @@ config EFI_LOADER
>
>  if EFI_LOADER
>
> +config EFI_SETUP_EARLY
> +	bool
> +	default n
> +
>  config EFI_GET_TIME
>  	bool "GetTime() runtime service"
>  	depends on DM_RTC
>

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

* [PATCH 09/10] tools: add mkeficapsule command for UEFI capsule update test
  2020-04-27  9:48 ` [PATCH 09/10] tools: add mkeficapsule command for UEFI capsule update test AKASHI Takahiro
@ 2020-04-27 20:15   ` Heinrich Schuchardt
  2020-04-28  1:52     ` AKASHI Takahiro
  0 siblings, 1 reply; 33+ messages in thread
From: Heinrich Schuchardt @ 2020-04-27 20:15 UTC (permalink / raw)
  To: u-boot

On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> This is a utility mainly for test purpose.
>   mkeficapsule -f: create a test capsule file for FIT image firmware
>
> Having said that, you will be able to customize the code to fit
> your specific requirements for your platform.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  tools/Makefile       |   3 +
>  tools/mkeficapsule.c | 209 +++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 212 insertions(+)
>  create mode 100644 tools/mkeficapsule.c
>
> diff --git a/tools/Makefile b/tools/Makefile
> index c2b26340047a..08e97063d5fa 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -222,6 +222,9 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
>  hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
>  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
>
> +# TODO: only build this for capsule pytest

Do you plan to send a new version of the series to fix this issue?

We need a documentation page for this tool in doc/uefi/.

Best regards

Heinrich

> +hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> +
>  # We build some files with extra pedantic flags to try to minimize things
>  # that won't build on some weird host compiler -- though there are lots of
>  # exceptions for files that aren't complaint.
> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> new file mode 100644
> index 000000000000..b26c88e42020
> --- /dev/null
> +++ b/tools/mkeficapsule.c
> @@ -0,0 +1,209 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2018 Linaro Limited
> + *		Author: AKASHI Takahiro
> + */
> +
> +#include <malloc.h>
> +#include <stdio.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +/*
> + * TODO: use libefi/libgnuefi headers

Can't you use the definitions from include/efi_api.h?
I would prefer if you could avoid duplicate definitions in this file.

Best regards

Heinrich

> + */
> +
> +typedef u_int8_t u8;
> +typedef u_int16_t u16;
> +typedef u_int32_t u32;
> +typedef u_int64_t u64;
> +
> +/* include/efi.h */
> +typedef struct {
> +	u8 b[16];
> +} efi_guid_t;
> +
> +#define EFI_GUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \
> +	{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, \
> +		((a) >> 24) & 0xff, \
> +		(b) & 0xff, ((b) >> 8) & 0xff, \
> +		(c) & 0xff, ((c) >> 8) & 0xff, \
> +		(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } }
> +
> +#define EFI_VARIABLE_NON_VOLATILE	0x0000000000000001
> +#define EFI_VARIABLE_BOOTSERVICE_ACCESS	0x0000000000000002
> +#define EFI_VARIABLE_RUNTIME_ACCESS	0x0000000000000004
> +
> +/* include/efi_api.h */
> +#define EFI_GLOBAL_VARIABLE_GUID \
> +	EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
> +		 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
> +
> +#define CAPSULE_FLAGS_PERSIST_ACROSS_RESET	0x00010000
> +#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE	0x00020000
> +#define CAPSULE_FLAGS_INITIATE_RESET		0x00040000
> +
> +#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID \
> +	EFI_GUID(0xae13ff2d, 0x9ad4, 0x4e25, 0x9a, 0xc8, \
> +		 0x6d, 0x80, 0xb3, 0xb2, 0x21, 0x47)
> +
> +#define EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID \
> +	EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \
> +		 0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a)
> +
> +struct efi_capsule_header {
> +	efi_guid_t capsule_guid;
> +	u32 header_size;
> +	u32 flags;
> +	u32 capsule_image_size;
> +} __attribute__((packed));
> +
> +struct efi_capsule_block_descriptor {
> +	u64 length;
> +	union {
> +		u64 data_block;
> +		u64 continuation_ptr;
> +	};
> +} __attribute__((packed));
> +
> +struct efi_firmware_management_capsule_header {
> +	u32 version;
> +	u16 embedded_driver_count;
> +	u16 payload_item_count;
> +	u64 item_offset_list[];
> +} __attribute__((packed));
> +
> +struct efi_firmware_management_capsule_image_header {
> +	u32 version;
> +	efi_guid_t update_image_type_id;
> +	u8 update_image_index;
> +	u8 reserved[3];
> +	u32 update_image_size;
> +	u32 update_vendor_code_size;
> +	u64 update_hardware_instance;
> +} __attribute__((packed));
> +
> +efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> +efi_guid_t efi_guid_image_type_uboot_fit =
> +		EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
> +
> +static int create_fwbin(char *bin, char *path)
> +{
> +	struct efi_capsule_header header;
> +	struct efi_firmware_management_capsule_header capsule;
> +	struct efi_firmware_management_capsule_image_header image;
> +	FILE *f, *g;
> +	struct stat bin_stat;
> +	u8 *data;
> +	size_t size;
> +
> +	g = fopen(bin, "r");
> +	if (!g) {
> +		printf("cannot open %s\n", bin);
> +		return -1;
> +	}
> +	if (stat(bin, &bin_stat) < 0) {
> +		printf("cannot determine the size of %s\n", bin);
> +		goto err_1;
> +	}
> +	data = malloc(bin_stat.st_size);
> +	if (!data) {
> +		printf("cannot allocate memory: %lx\n", bin_stat.st_size);
> +		goto err_1;
> +	}
> +	f = fopen(path, "w");
> +	if (!f) {
> +		printf("cannot open %s\n", path);
> +		goto err_2;
> +	}
> +	header.capsule_guid = efi_guid_fm_capsule;
> +	header.header_size = sizeof(header);
> +	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
> +	header.capsule_image_size = sizeof(header)
> +					+ sizeof(capsule) + sizeof(u64)
> +					+ sizeof(image)
> +					+ bin_stat.st_size;
> +
> +	size = fwrite(&header, 1, sizeof(header), f);
> +	if (size < sizeof(header)) {
> +		printf("write failed (%lx)\n", size);
> +		goto err_3;
> +	}
> +
> +	capsule.version = 0x00000001;
> +	capsule.embedded_driver_count = 0;
> +	capsule.payload_item_count = 1;
> +	capsule.item_offset_list[0] = sizeof(capsule) + sizeof(u64);
> +	size = fwrite(&capsule, 1, sizeof(capsule) + sizeof(u64), f);
> +	if (size < (sizeof(capsule) + sizeof(u64))) {
> +		printf("write failed (%lx)\n", size);
> +		goto err_3;
> +	}
> +
> +	image.version = 0x00000002;
> +	image.update_image_type_id = efi_guid_image_type_uboot_fit;
> +	image.update_image_index = 1;
> +	image.update_image_size = bin_stat.st_size;
> +	image.update_vendor_code_size = 0; /* none */
> +	image.update_hardware_instance = 1;
> +
> +	size = fwrite(&image, 1, sizeof(image), f);
> +	if (size < sizeof(image)) {
> +		printf("write failed (%lx)\n", size);
> +		goto err_3;
> +	}
> +	size = fread(data, 1, bin_stat.st_size, g);
> +	if (size < bin_stat.st_size) {
> +		printf("read failed (%lx)\n", size);
> +		goto err_3;
> +	}
> +	size = fwrite(data, 1, bin_stat.st_size, f);
> +	if (size < bin_stat.st_size) {
> +		printf("write failed (%lx)\n", size);
> +		goto err_3;
> +	}
> +
> +	fclose(f);
> +	fclose(g);
> +	free(data);
> +
> +	return 0;
> +
> +err_3:
> +	fclose(f);
> +err_2:
> +	free(data);
> +err_1:
> +	fclose(g);
> +
> +	return -1;
> +}
> +
> +void print_usage(void)
> +{
> +	printf("mkeficapsule -f <firmware binary> <output file>\n");
> +}
> +
> +/*
> + * Usage:
> + *   $ mkeficapsule -f <firmware binary> <output file>
> + */
> +int main(int argc, char **argv)
> +{
> +	if (argc != 4) {
> +		print_usage();
> +		return -1;
> +	}
> +
> +	if (!strcmp(argv[1], "-f")) {
> +		if (create_fwbin(argv[2], argv[3]) < 0) {
> +			printf("Creating firmware capsule for %s failed\n",
> +			       argv[2]);
> +			return -1;
> +		}
> +
> +		return 0;
> +	}
> +
> +	print_usage();
> +	return -1;
> +}
>

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

* [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support
  2020-04-27  9:48 ` [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support AKASHI Takahiro
@ 2020-04-27 20:28   ` Heinrich Schuchardt
  2020-04-28  0:28     ` AKASHI Takahiro
  0 siblings, 1 reply; 33+ messages in thread
From: Heinrich Schuchardt @ 2020-04-27 20:28 UTC (permalink / raw)
  To: u-boot

On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> Capsule data can be loaded into the system either via UpdateCapsule
> runtime service or files on a file system (of boot device).
> The latter case is called "capsules on disk", and actual updates will
> take place at the next boot time.
>
> In this commit, we will support capsule on disk mechanism.
>
> Please note that U-Boot itself has no notion of "boot device" and
> all the capsule files to be executed will be detected only if they
> are located in a specific directory, \EFI\UpdateCapsule, on a device
> that is identified as a boot device by "BootXXXX" variables.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  common/main.c                |   4 +
>  include/efi_loader.h         |  16 ++
>  lib/efi_loader/Kconfig       |  22 ++
>  lib/efi_loader/efi_capsule.c | 449 +++++++++++++++++++++++++++++++++++
>  lib/efi_loader/efi_setup.c   |   9 +
>  5 files changed, 500 insertions(+)
>
> diff --git a/common/main.c b/common/main.c
> index 06d7ff56d60c..877ae63b708d 100644
> --- a/common/main.c
> +++ b/common/main.c
> @@ -14,6 +14,7 @@
>  #include <env.h>
>  #include <init.h>
>  #include <version.h>
> +#include <efi_loader.h>
>
>  static void run_preboot_environment_command(void)
>  {
> @@ -51,6 +52,9 @@ void main_loop(void)
>  	if (IS_ENABLED(CONFIG_UPDATE_TFTP))
>  		update_tftp(0UL, NULL, NULL);
>
> +	if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
> +		efi_launch_capsules();
> +

Can't we move this to efi_init_obj_list() and do away with
CONFIG_EFI_CAPSULE_ON_DISK_EARLY?

>  	s = bootdelay_process();
>  	if (cli_process_fdt(&s))
>  		cli_secure_boot_cmd(s);
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 19ffc027c171..d49ebcad53ec 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -793,6 +793,18 @@ efi_status_t EFIAPI efi_query_capsule_caps(
>  		u32 *reset_type);
>  #endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */
>
> +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> +#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\"
> +
> +/* Hook at initialization */
> +efi_status_t efi_launch_capsules(void);
> +#else
> +static inline efi_status_t efi_launch_capsules(void)
> +{
> +	return EFI_SUCCESS;
> +}
> +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
> +
>  #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
>
>  /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
> @@ -809,6 +821,10 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr,
>  				   const char *path) { }
>  static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
>  static inline void efi_print_image_infos(void *pc) { }
> +static inline efi_status_t efi_launch_capsules(void)
> +{
> +	return EFI_SUCCESS;
> +}
>
>  #endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
>
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index e2b08251f26a..b48b95a32e03 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -56,6 +56,28 @@ config EFI_RUNTIME_UPDATE_CAPSULE
>  	  Select this option if you want to use UpdateCapsule and
>  	  QueryCapsuleCapabilities API's.
>
> +config EFI_CAPSULE_ON_DISK
> +	bool "Enable capsule-on-disk support"
> +	select EFI_HAVE_CAPSULE_SUPPORT
> +	default n
> +	help
> +	  Select this option if you want to use capsule-on-disk feature,
> +	  that is, capsules can be fetched and executed from files
> +	  under a specific directory on UEFI system partition instead of
> +	  via UpdateCapsule API.
> +
> +config EFI_CAPSULE_ON_DISK_EARLY
> +	bool "Initiate capsule-on-disk at U-Boot boottime"
> +	depends on EFI_CAPSULE_ON_DISK
> +	default y
> +	select EFI_SETUP_EARLY
> +	help
> +	  Normally, without this option enabled, capsules will be
> +	  executed only at the first time of invoking one of efi command.
> +	  If this option is enabled, capsules will be enforced to be
> +	  executed as part of U-Boot initialisation so that they will
> +	  surely take place whatever is set to distro_bootcmd.

Why do we need this Kconfig variable if we have EFI_SETUP_EARLY available?

> +
>  config EFI_DEVICE_PATH_TO_TEXT
>  	bool "Device path to text protocol"
>  	default y
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> index fb104bb92a6c..938129a41934 100644
> --- a/lib/efi_loader/efi_capsule.c
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -10,10 +10,16 @@
>  #include <efi_loader.h>
>  #include <fs.h>
>  #include <malloc.h>
> +#include <mapmem.h>
>  #include <sort.h>
>
>  const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
>
> +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> +/* for file system access */
> +static struct efi_file_handle *bootdev_root;
> +#endif
> +
>  static __maybe_unused int get_last_capsule(void)
>  {
>  	u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
> @@ -151,3 +157,446 @@ efi_status_t EFIAPI efi_query_capsule_caps(
>  out:
>  	return EFI_EXIT(ret);
>  }
> +
> +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> +static efi_status_t get_dp_device(u16 *boot_var,
> +				  struct efi_device_path **device_dp)
> +{
> +	void *buf = NULL;
> +	efi_uintn_t size;
> +	struct efi_load_option lo;
> +	struct efi_device_path *file_dp;
> +	efi_status_t ret;
> +
> +	size = 0;
> +	ret = EFI_CALL(efi_get_variable(boot_var, &efi_global_variable_guid,
> +					NULL, &size, NULL));
> +	if (ret == EFI_BUFFER_TOO_SMALL) {
> +		buf = malloc(size);
> +		if (!buf)
> +			return EFI_OUT_OF_RESOURCES;
> +		ret = EFI_CALL(efi_get_variable(boot_var,
> +						&efi_global_variable_guid,
> +						NULL, &size, buf));
> +	}
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	efi_deserialize_load_option(&lo, buf);
> +
> +	if (lo.attributes & LOAD_OPTION_ACTIVE) {
> +		efi_dp_split_file_path(lo.file_path, device_dp, &file_dp);
> +		efi_free_pool(file_dp);
> +
> +		ret = EFI_SUCCESS;
> +	} else {
> +		ret = EFI_NOT_FOUND;
> +	}
> +
> +	free(buf);
> +
> +	return ret;
> +}
> +
> +static bool device_is_present_and_system_part(struct efi_device_path *dp)
> +{
> +	efi_handle_t handle;
> +
> +	handle = efi_dp_find_obj(dp, NULL);
> +	if (!handle)
> +		return false;
> +
> +	return efi_disk_is_system_part(handle);
> +}
> +
> +static efi_status_t find_boot_device(void)
> +{
> +	char boot_var[9];
> +	u16 boot_var16[9], *p, bootnext, *boot_order = NULL;
> +	efi_uintn_t size;
> +	int i, num;
> +	struct efi_simple_file_system_protocol *volume;
> +	struct efi_device_path *boot_dev = NULL;
> +	efi_status_t ret;
> +
> +	/* find active boot device in BootNext */
> +	bootnext = 0;
> +	size = sizeof(bootnext);
> +	ret = EFI_CALL(efi_get_variable(L"BootNext",
> +					(efi_guid_t *)&efi_global_variable_guid,
> +					NULL, &size, &bootnext));
> +	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
> +		/* BootNext does exist here */
> +		if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) {
> +			printf("BootNext must be 16-bit integer\n");
> +			goto skip;
> +		}
> +		sprintf((char *)boot_var, "Boot%04X", bootnext);
> +		p = boot_var16;
> +		utf8_utf16_strcpy(&p, boot_var);
> +
> +		ret = get_dp_device(boot_var16, &boot_dev);
> +		if (ret == EFI_SUCCESS) {
> +			if (device_is_present_and_system_part(boot_dev)) {
> +				goto out;
> +			} else {
> +				efi_free_pool(boot_dev);
> +				boot_dev = NULL;
> +			}
> +		}
> +	}
> +
> +skip:
> +	/* find active boot device in BootOrder */
> +	size = 0;
> +	ret = EFI_CALL(efi_get_variable(L"BootOrder", &efi_global_variable_guid,
> +					NULL, &size, NULL));
> +	if (ret == EFI_BUFFER_TOO_SMALL) {
> +		boot_order = malloc(size);
> +		if (!boot_order) {
> +			ret = EFI_OUT_OF_RESOURCES;
> +			goto out;
> +		}
> +
> +		ret = EFI_CALL(efi_get_variable(
> +					L"BootOrder", &efi_global_variable_guid,
> +					NULL, &size, boot_order));
> +	}
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	/* check in higher order */
> +	num = size / sizeof(u16);
> +	for (i = 0; i < num; i++) {
> +		sprintf((char *)boot_var, "Boot%04X", boot_order[i]);
> +		p = boot_var16;
> +		utf8_utf16_strcpy(&p, boot_var);
> +		ret = get_dp_device(boot_var16, &boot_dev);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +
> +		if (device_is_present_and_system_part(boot_dev))
> +			break;
> +
> +		efi_free_pool(boot_dev);
> +		boot_dev = NULL;
> +	}
> +out:
> +	if (boot_dev) {
> +		u16 *path_str;
> +
> +		path_str = efi_dp_str(boot_dev);
> +		EFI_PRINT("EFI Capsule: bootdev is %ls\n", path_str);
> +		efi_free_pool(path_str);
> +
> +		volume = efi_fs_from_path(boot_dev);
> +		if (!volume)
> +			ret = EFI_DEVICE_ERROR;
> +		else
> +			ret = EFI_CALL(volume->open_volume(volume,
> +							   &bootdev_root));
> +		efi_free_pool(boot_dev);
> +	} else {
> +		ret = EFI_NOT_FOUND;
> +	}
> +	free(boot_order);
> +
> +	return ret;
> +}
> +
> +/*
> + * Traverse a capsule directory in boot device
> + * Called by initialization code, and returns an array of capsule file
> + * names in @files
> + */
> +static efi_status_t efi_capsule_scan_dir(u16 ***files, int *num)
> +{
> +	struct efi_file_handle *dirh;
> +	struct efi_file_info *dirent;
> +	efi_uintn_t dirent_size, tmp_size;
> +	int count;
> +	u16 **tmp_files;
> +	efi_status_t ret;
> +
> +	ret = find_boot_device();
> +	if (ret == EFI_NOT_FOUND) {
> +		EFI_PRINT("EFI Capsule: bootdev is not set\n");
> +		*num = 0;
> +		return EFI_SUCCESS;
> +	} else if (ret != EFI_SUCCESS) {
> +		return EFI_DEVICE_ERROR;
> +	}
> +
> +	/* count capsule files */
> +	ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
> +					     EFI_CAPSULE_DIR,
> +					     EFI_FILE_MODE_READ, 0));
> +	if (ret != EFI_SUCCESS) {
> +		*num = 0;
> +		return EFI_SUCCESS;
> +	}
> +
> +	dirent_size = 256;
> +	dirent = malloc(dirent_size);
> +	if (!dirent)
> +		return EFI_OUT_OF_RESOURCES;
> +
> +	count = 0;
> +	while (1) {
> +		tmp_size = dirent_size;
> +		ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
> +		if (ret == EFI_BUFFER_TOO_SMALL) {
> +			dirent = realloc(dirent, tmp_size);
> +			if (!dirent) {
> +				ret = EFI_OUT_OF_RESOURCES;
> +				goto err;
> +			}
> +			dirent_size = tmp_size;
> +			ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
> +		}
> +		if (ret != EFI_SUCCESS)
> +			goto err;
> +		if (!tmp_size)
> +			break;
> +
> +		if (!(dirent->attribute & EFI_FILE_DIRECTORY) &&
> +		    u16_strcmp(dirent->file_name, L".") &&
> +		    u16_strcmp(dirent->file_name, L".."))
> +			count++;
> +	}
> +
> +	ret = EFI_CALL((*dirh->setpos)(dirh, 0));
> +	if (ret != EFI_SUCCESS)
> +		goto err;
> +
> +	/* make a list */
> +	tmp_files = malloc(count * sizeof(*files));
> +	if (!tmp_files) {
> +		ret = EFI_OUT_OF_RESOURCES;
> +		goto err;
> +	}
> +
> +	count = 0;
> +	while (1) {
> +		tmp_size = dirent_size;
> +		ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
> +		if (ret != EFI_SUCCESS)
> +			goto err;
> +		if (!tmp_size)
> +			break;
> +
> +		if (!(dirent->attribute & EFI_FILE_DIRECTORY) &&
> +		    u16_strcmp(dirent->file_name, L".") &&
> +		    u16_strcmp(dirent->file_name, L".."))
> +			tmp_files[count++] = u16_strdup(dirent->file_name);
> +	}
> +	/* ignore an error */
> +	EFI_CALL((*dirh->close)(dirh));
> +
> +	/* in ascii order */
> +	/* FIXME: u16 version of strcasecmp */
> +	qsort(tmp_files, count, sizeof(*tmp_files),
> +	      (int (*)(const void *, const void *))strcasecmp);
> +	*files = tmp_files;
> +	*num = count;
> +	ret = EFI_SUCCESS;
> +err:
> +	free(dirent);
> +
> +	return ret;
> +}
> +
> +/*
> + * Read in a capsule file
> + */
> +static efi_status_t efi_capsule_read_file(u16 *filename,
> +					  struct efi_capsule_header **capsule)
> +{
> +	struct efi_file_handle *dirh, *fh;
> +	struct efi_file_info *file_info = NULL;
> +	struct efi_capsule_header *buf = NULL;
> +	efi_uintn_t size;
> +	efi_status_t ret;
> +
> +	ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
> +					     EFI_CAPSULE_DIR,
> +					     EFI_FILE_MODE_READ, 0));
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +	ret = EFI_CALL((*dirh->open)(dirh, &fh, filename,
> +				     EFI_FILE_MODE_READ, 0));
> +	/* ignore an error */
> +	EFI_CALL((*dirh->close)(dirh));
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	/* file size */
> +	size = 0;
> +	ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid,
> +				      &size, file_info));
> +	if (ret == EFI_BUFFER_TOO_SMALL) {
> +		file_info = malloc(size);
> +		if (!file_info) {
> +			ret = EFI_OUT_OF_RESOURCES;
> +			goto err;
> +		}
> +		ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid,
> +					      &size, file_info));
> +	}
> +	if (ret != EFI_SUCCESS)
> +		goto err;
> +	size = file_info->file_size;
> +	free(file_info);
> +	buf = malloc(size);
> +	if (!buf) {
> +		ret = EFI_OUT_OF_RESOURCES;
> +		goto err;
> +	}
> +
> +	/* fetch data */
> +	ret = EFI_CALL((*fh->read)(fh, &size, buf));
> +	if (ret == EFI_SUCCESS) {
> +		if (size >= buf->capsule_image_size) {
> +			*capsule = buf;
> +		} else {
> +			free(buf);
> +			ret = EFI_INVALID_PARAMETER;
> +		}
> +	} else {
> +		free(buf);
> +	}
> +err:
> +	EFI_CALL((*fh->close)(fh));
> +
> +	return ret;
> +}
> +
> +static efi_status_t efi_capsule_delete_file(u16 *filename)
> +{
> +	struct efi_file_handle *dirh, *fh;
> +	efi_status_t ret;
> +
> +	ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
> +					     EFI_CAPSULE_DIR,
> +					     EFI_FILE_MODE_READ, 0));
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +	ret = EFI_CALL((*dirh->open)(dirh, &fh, filename,
> +				     EFI_FILE_MODE_READ, 0));
> +	/* ignore an error */
> +	EFI_CALL((*dirh->close)(dirh));
> +
> +	ret = EFI_CALL((*fh->delete)(fh));
> +
> +	return ret;
> +}
> +
> +static void efi_capsule_scan_done(void)
> +{
> +	EFI_CALL((*bootdev_root->close)(bootdev_root));
> +	bootdev_root = NULL;
> +}
> +
> +efi_status_t __weak arch_efi_load_capsule_drivers(void)
> +{
> +	return EFI_SUCCESS;
> +}
> +
> +/*
> + * Launch all the capsules in system at boot time
> + *
> + * Called by efi init code
> + */

Where are the function descriptions?

https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation

Best regards

Heinrich

> +efi_status_t efi_launch_capsules(void)
> +{
> +	u64 os_indications;
> +	efi_uintn_t size;
> +	struct efi_capsule_header *capsule = NULL;
> +	u16 **files;
> +	int nfiles, num, i;
> +	char variable_name[12];
> +	u16 variable_name16[12], *p;
> +	efi_status_t ret;
> +
> +	size = sizeof(os_indications);
> +	ret = EFI_CALL(efi_get_variable(L"OsIndications",
> +					&efi_global_variable_guid,
> +					NULL, &size, &os_indications));
> +	if (ret != EFI_SUCCESS ||
> +	    !(os_indications
> +	      & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED))
> +		return EFI_SUCCESS;
> +
> +	num = get_last_capsule();
> +
> +	/* Load capsule drivers */
> +	ret = arch_efi_load_capsule_drivers();
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	/*
> +	 * Find capsules on disk.
> +	 * All the capsules are collected at the beginning because
> +	 * capsule files will be removed instantly.
> +	 */
> +	nfiles = 0;
> +	files = NULL;
> +	ret = efi_capsule_scan_dir(&files, &nfiles);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +	if (!nfiles)
> +		return EFI_SUCCESS;
> +
> +	/* Launch capsules */
> +	for (i = 0, ++num; i < nfiles; i++, num++) {
> +		EFI_PRINT("capsule from %ls ...\n", files[i]);
> +		if (num > 0xffff)
> +			num = 0;
> +		ret = efi_capsule_read_file(files[i], &capsule);
> +		if (ret == EFI_SUCCESS) {
> +			ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0));
> +			if (ret != EFI_SUCCESS)
> +				printf("EFI Capsule update failed at %ls\n",
> +				       files[i]);
> +
> +			free(capsule);
> +		} else {
> +			printf("EFI: reading capsule failed: %ls\n",
> +			       files[i]);
> +		}
> +		/* create CapsuleXXXX */
> +		set_capsule_result(num, capsule, ret);
> +
> +		/* delete a capsule either in case of success or failure */
> +		ret = efi_capsule_delete_file(files[i]);
> +		if (ret != EFI_SUCCESS)
> +			printf("EFI: deleting a capsule file failed: %ls\n",
> +			       files[i]);
> +	}
> +	efi_capsule_scan_done();
> +
> +	for (i = 0; i < nfiles; i++)
> +		free(files[i]);
> +	free(files);
> +
> +	/* CapsuleMax */
> +	p = variable_name16;
> +	utf8_utf16_strncpy(&p, "CapsuleFFFF", 11);
> +	EFI_CALL(efi_set_variable(L"CapsuleMax", &efi_guid_capsule_report,
> +				  EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +				  EFI_VARIABLE_RUNTIME_ACCESS,
> +				  22, variable_name16));
> +
> +	/* CapsuleLast */
> +	sprintf(variable_name, "Capsule%04X", num - 1);
> +	p = variable_name16;
> +	utf8_utf16_strncpy(&p, variable_name, 11);
> +	EFI_CALL(efi_set_variable(L"CapsuleLast", &efi_guid_capsule_report,
> +				  EFI_VARIABLE_NON_VOLATILE |
> +				  EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +				  EFI_VARIABLE_RUNTIME_ACCESS,
> +				  22, variable_name16));
> +
> +	return ret;
> +}
> +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
> diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
> index 8fe378bbfdfc..bb759976102a 100644
> --- a/lib/efi_loader/efi_setup.c
> +++ b/lib/efi_loader/efi_setup.c
> @@ -129,6 +129,10 @@ static efi_status_t efi_init_os_indications(void)
>  #ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
>  	os_indications_supported |=
>  			EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
> +#endif
> +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> +	os_indications_supported |=
> +			EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED;
>  #endif
>  	return EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
>  					 &efi_global_variable_guid,
> @@ -239,6 +243,11 @@ efi_status_t efi_init_obj_list(void)
>  	if (ret != EFI_SUCCESS)
>  		goto out;
>
> +#if defined(CONFIG_EFI_CAPSULE_ON_DISK) && \
> +		!defined(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)
> +	/* Execute capsules after reboot */
> +	ret = efi_launch_capsules();
> +#endif
>  out:
>  	efi_obj_list_initialized = ret;
>  	return ret;
>

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

* [PATCH 00/10] efi_loader: add capsule update support
  2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
                   ` (9 preceding siblings ...)
  2020-04-27  9:48 ` [PATCH 10/10] test/py: add a test for efi firmware update capsule AKASHI Takahiro
@ 2020-04-27 20:33 ` Heinrich Schuchardt
  2020-04-27 23:45   ` AKASHI Takahiro
  10 siblings, 1 reply; 33+ messages in thread
From: Heinrich Schuchardt @ 2020-04-27 20:33 UTC (permalink / raw)
  To: u-boot

On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> Summary
> =======
> 'UpdateCapsule' is one of runtime services defined in UEFI specification
> and its aim is to allow a caller (OS) to pass information to the firmware,
> i.e. U-Boot. This is mostly used to update firmware binary on devices by
> instructions from OS.
>
> While 'UpdateCapsule' is a runtime services function, it is, at least
> initially, suported only before exiting boot services alike other runtime
> functions, [Get/]SetVariable. This is because modifying storage which may
> be shared with OS must be carefully designed and there is no general
> assumption that we can do it.
>
> Therefore, we practically support only "capsule on disk"; any capsule can
> be handed over to UEFI subsystem as a file on a specific file system.
>
> In this patch series, all the related definitions and structures are given
> as UEFI specification describes, and basic framework for capsule support
> is provided. Currently supported is
>  * firmware update (Firmware Management Protocol or simply FMP)
>
> Most of functionality of firmware update is provided by FMP driver and
> it will be, by nature, system/platform-specific. So you can and should
> implement your own FMP driver(s) based on your system requirements.
> Only a simple FMP driver based on FIT image for a single region is
> provided here.  (So it is "for reference only")

Do think this FMP driver will be actually used?
Or should it be moved to lib/efi_selftest?

>                            ^^^^^^^^^^^^^^^^^^
> See more details in the commit, "efi_loader: capsule: add simple firmware
> management protocol."

In this patch series I am missing the update of the documentation in
doc/uefi/.

Best regards

Heinrich

>
> Patch structure
> ===============
> Patch#1-#2: preparatory patches
> Patch#3-#7: main part of implementation
> Patch#8-#10: utilities and tests
>
> [1] https://git.linaro.org/people/takahiro.akashi/u-boot.git efi/capsule
>
> Prerequisite patches
> ====================
> [2] part: detect EFI system partition
>     https://lists.denx.de/pipermail/u-boot/2020-March/403562.html
> [3] common: update_tftp: remove unnecessary build check
>     https://lists.denx.de/pipermail/u-boot/2020-March/401727.html
> [4] sandbox: drop CONFIG_SYS_RELOC_GD_ENV_ADDR
>     https://lists.denx.de/pipermail/u-boot/2020-April/408711.html
>
> Test
> ====
> * passed all the pytests which are included in this patch series
>   on sandbox build.
> * passed Travis CI.
>
> Please note that, while Travic CI passed, the capsule pytest
> itself won't be run in the CI because some specific configuration
> for sandbox build is required. See test_efi_capsule_firmware.py.
>
> Issues
> ======
> * Timing of executing capsules-on-disk
>   Currently, processing a capsule is triggered only as part of
>   UEFI subsystem initialization. This means that, for example,
>   firmware update, may not take place at system booting time and
>   will potentially be delayed until a first call of any UEFI functions.
>
>     => See patch#4 for my proposal
>
> TODO's
> ======
> (May not be addressed in future versions of this series.)
> * capsule authentication
> * capsule dependency (dependency expression instruction set)
> * loading drivers in a capsule
> * handling RESET flag in a capsule and QeuryCapsuleCaps
> * full semantics of ESRT (EFI System Resource Table)
> * enabling capsule API at runtime
> * json capsule
> * recovery from update failure
>
> Changes
> =======
> v1 (April 27, 2020)
> * rebased to v2020.07-rc
> * removed already-merged patches (RFC's #1 to #4)
> * dropped 'variable update' capsule support (RFC's patch#10)
> * dropped 'variable configuration table' support (RFC's patch#11)
>   (Those two should be discussed separately.)
> * add preparatory patches (patch#1/#2)
> * fix several build errors
> * rename some Kconfig options to be aligned with UEFI specification's terms
>   (patch#3,4,6,7)
> * enforce UpdateCapsule API to be disabled after ExitBootServices (patch#3)
> * use config table, runtime_services_supported, instead of variable (patch#3)
> * make EFI_CAPSULE_ON_DISK buildable even if UpdateCapsule API is disabled
>   (patch4)
> * support OsIndications, invoking capsule-on-disk only if the variable
>   indicates so (patch#4)
> * introduced EFI_CAPSULE_ON_DISK_EARLY to invoke capsule-on-disk in U-Boot
>   initialization (patch#4)
> * detect capsule files only if they are on EFI system partition (patch#4)
> * use printf, rather than EFI_PRINT, in error cases (patch#4)
> * use 'header_size' field to retrieve capsule data, adding sanity checks
>   against capsule size (patch#6)
> * call fmpt driver interfaces with EFI_CALL (patch#6)
> * remove 'variable update capsule'-related code form mkeficapsule (patch#9)
> * add a test case of OsIndications not being set properly (patch#10)
> * adjust test scenario for EFI_CAPSULE_ON_DISK_EARLY (patch#10)
> * revise pytest scripts (patch#10)
>
> Initial release as RFC (March 17, 2020)
>
> AKASHI Takahiro (10):
>   efi_loader: disk: add efi_disk_is_system_part()
>   efi_loader: add option to initialise EFI subsystem early
>   efi_loader: define UpdateCapsule api
>   efi_loader: capsule: add capsule_on_disk support
>   efi_loader: capsule: add memory range capsule definitions
>   efi_loader: capsule: support firmware update
>   efi_loader: add simple firmware management protocol for FIT image
>   cmd: add "efidebug capsule" command
>   tools: add mkeficapsule command for UEFI capsule update test
>   test/py: add a test for efi firmware update capsule
>
>  cmd/efidebug.c                                | 234 +++++
>  common/board_r.c                              |   6 +
>  common/main.c                                 |   4 +
>  include/efi_api.h                             | 156 ++++
>  include/efi_loader.h                          |  35 +
>  lib/efi_loader/Kconfig                        |  69 ++
>  lib/efi_loader/Makefile                       |   2 +
>  lib/efi_loader/efi_capsule.c                  | 802 ++++++++++++++++++
>  lib/efi_loader/efi_disk.c                     |  22 +
>  lib/efi_loader/efi_firmware.c                 | 191 +++++
>  lib/efi_loader/efi_runtime.c                  | 104 ++-
>  lib/efi_loader/efi_setup.c                    |  44 +-
>  test/py/tests/test_efi_capsule/conftest.py    |  73 ++
>  test/py/tests/test_efi_capsule/defs.py        |  24 +
>  .../test_efi_capsule/test_capsule_firmware.py | 198 +++++
>  test/py/tests/test_efi_capsule/uboot_env.its  |  25 +
>  tools/Makefile                                |   3 +
>  tools/mkeficapsule.c                          | 209 +++++
>  18 files changed, 2150 insertions(+), 51 deletions(-)
>  create mode 100644 lib/efi_loader/efi_capsule.c
>  create mode 100644 lib/efi_loader/efi_firmware.c
>  create mode 100644 test/py/tests/test_efi_capsule/conftest.py
>  create mode 100644 test/py/tests/test_efi_capsule/defs.py
>  create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware.py
>  create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its
>  create mode 100644 tools/mkeficapsule.c
>

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

* [PATCH 00/10] efi_loader: add capsule update support
  2020-04-27 20:33 ` [PATCH 00/10] efi_loader: add capsule update support Heinrich Schuchardt
@ 2020-04-27 23:45   ` AKASHI Takahiro
  0 siblings, 0 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27 23:45 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Mon, Apr 27, 2020 at 10:33:41PM +0200, Heinrich Schuchardt wrote:
> On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> > Summary
> > =======
> > 'UpdateCapsule' is one of runtime services defined in UEFI specification
> > and its aim is to allow a caller (OS) to pass information to the firmware,
> > i.e. U-Boot. This is mostly used to update firmware binary on devices by
> > instructions from OS.
> >
> > While 'UpdateCapsule' is a runtime services function, it is, at least
> > initially, suported only before exiting boot services alike other runtime
> > functions, [Get/]SetVariable. This is because modifying storage which may
> > be shared with OS must be carefully designed and there is no general
> > assumption that we can do it.
> >
> > Therefore, we practically support only "capsule on disk"; any capsule can
> > be handed over to UEFI subsystem as a file on a specific file system.
> >
> > In this patch series, all the related definitions and structures are given
> > as UEFI specification describes, and basic framework for capsule support
> > is provided. Currently supported is
> >  * firmware update (Firmware Management Protocol or simply FMP)
> >
> > Most of functionality of firmware update is provided by FMP driver and
> > it will be, by nature, system/platform-specific. So you can and should
> > implement your own FMP driver(s) based on your system requirements.
> > Only a simple FMP driver based on FIT image for a single region is
> > provided here.  (So it is "for reference only")
> 
> Do think this FMP driver will be actually used?

It's totally up to users (and their requirements).

> Or should it be moved to lib/efi_selftest?

Well, I hope that someone will come in and add more features
that will meet additional requirements.
So I would like you to keep it under lib/efi_loader for now.

> >                            ^^^^^^^^^^^^^^^^^^
> > See more details in the commit, "efi_loader: capsule: add simple firmware
> > management protocol."
> 
> In this patch series I am missing the update of the documentation in
> doc/uefi/.

Okay.

-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> >
> > Patch structure
> > ===============
> > Patch#1-#2: preparatory patches
> > Patch#3-#7: main part of implementation
> > Patch#8-#10: utilities and tests
> >
> > [1] https://git.linaro.org/people/takahiro.akashi/u-boot.git efi/capsule
> >
> > Prerequisite patches
> > ====================
> > [2] part: detect EFI system partition
> >     https://lists.denx.de/pipermail/u-boot/2020-March/403562.html
> > [3] common: update_tftp: remove unnecessary build check
> >     https://lists.denx.de/pipermail/u-boot/2020-March/401727.html
> > [4] sandbox: drop CONFIG_SYS_RELOC_GD_ENV_ADDR
> >     https://lists.denx.de/pipermail/u-boot/2020-April/408711.html
> >
> > Test
> > ====
> > * passed all the pytests which are included in this patch series
> >   on sandbox build.
> > * passed Travis CI.
> >
> > Please note that, while Travic CI passed, the capsule pytest
> > itself won't be run in the CI because some specific configuration
> > for sandbox build is required. See test_efi_capsule_firmware.py.
> >
> > Issues
> > ======
> > * Timing of executing capsules-on-disk
> >   Currently, processing a capsule is triggered only as part of
> >   UEFI subsystem initialization. This means that, for example,
> >   firmware update, may not take place at system booting time and
> >   will potentially be delayed until a first call of any UEFI functions.
> >
> >     => See patch#4 for my proposal
> >
> > TODO's
> > ======
> > (May not be addressed in future versions of this series.)
> > * capsule authentication
> > * capsule dependency (dependency expression instruction set)
> > * loading drivers in a capsule
> > * handling RESET flag in a capsule and QeuryCapsuleCaps
> > * full semantics of ESRT (EFI System Resource Table)
> > * enabling capsule API at runtime
> > * json capsule
> > * recovery from update failure
> >
> > Changes
> > =======
> > v1 (April 27, 2020)
> > * rebased to v2020.07-rc
> > * removed already-merged patches (RFC's #1 to #4)
> > * dropped 'variable update' capsule support (RFC's patch#10)
> > * dropped 'variable configuration table' support (RFC's patch#11)
> >   (Those two should be discussed separately.)
> > * add preparatory patches (patch#1/#2)
> > * fix several build errors
> > * rename some Kconfig options to be aligned with UEFI specification's terms
> >   (patch#3,4,6,7)
> > * enforce UpdateCapsule API to be disabled after ExitBootServices (patch#3)
> > * use config table, runtime_services_supported, instead of variable (patch#3)
> > * make EFI_CAPSULE_ON_DISK buildable even if UpdateCapsule API is disabled
> >   (patch4)
> > * support OsIndications, invoking capsule-on-disk only if the variable
> >   indicates so (patch#4)
> > * introduced EFI_CAPSULE_ON_DISK_EARLY to invoke capsule-on-disk in U-Boot
> >   initialization (patch#4)
> > * detect capsule files only if they are on EFI system partition (patch#4)
> > * use printf, rather than EFI_PRINT, in error cases (patch#4)
> > * use 'header_size' field to retrieve capsule data, adding sanity checks
> >   against capsule size (patch#6)
> > * call fmpt driver interfaces with EFI_CALL (patch#6)
> > * remove 'variable update capsule'-related code form mkeficapsule (patch#9)
> > * add a test case of OsIndications not being set properly (patch#10)
> > * adjust test scenario for EFI_CAPSULE_ON_DISK_EARLY (patch#10)
> > * revise pytest scripts (patch#10)
> >
> > Initial release as RFC (March 17, 2020)
> >
> > AKASHI Takahiro (10):
> >   efi_loader: disk: add efi_disk_is_system_part()
> >   efi_loader: add option to initialise EFI subsystem early
> >   efi_loader: define UpdateCapsule api
> >   efi_loader: capsule: add capsule_on_disk support
> >   efi_loader: capsule: add memory range capsule definitions
> >   efi_loader: capsule: support firmware update
> >   efi_loader: add simple firmware management protocol for FIT image
> >   cmd: add "efidebug capsule" command
> >   tools: add mkeficapsule command for UEFI capsule update test
> >   test/py: add a test for efi firmware update capsule
> >
> >  cmd/efidebug.c                                | 234 +++++
> >  common/board_r.c                              |   6 +
> >  common/main.c                                 |   4 +
> >  include/efi_api.h                             | 156 ++++
> >  include/efi_loader.h                          |  35 +
> >  lib/efi_loader/Kconfig                        |  69 ++
> >  lib/efi_loader/Makefile                       |   2 +
> >  lib/efi_loader/efi_capsule.c                  | 802 ++++++++++++++++++
> >  lib/efi_loader/efi_disk.c                     |  22 +
> >  lib/efi_loader/efi_firmware.c                 | 191 +++++
> >  lib/efi_loader/efi_runtime.c                  | 104 ++-
> >  lib/efi_loader/efi_setup.c                    |  44 +-
> >  test/py/tests/test_efi_capsule/conftest.py    |  73 ++
> >  test/py/tests/test_efi_capsule/defs.py        |  24 +
> >  .../test_efi_capsule/test_capsule_firmware.py | 198 +++++
> >  test/py/tests/test_efi_capsule/uboot_env.its  |  25 +
> >  tools/Makefile                                |   3 +
> >  tools/mkeficapsule.c                          | 209 +++++
> >  18 files changed, 2150 insertions(+), 51 deletions(-)
> >  create mode 100644 lib/efi_loader/efi_capsule.c
> >  create mode 100644 lib/efi_loader/efi_firmware.c
> >  create mode 100644 test/py/tests/test_efi_capsule/conftest.py
> >  create mode 100644 test/py/tests/test_efi_capsule/defs.py
> >  create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware.py
> >  create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its
> >  create mode 100644 tools/mkeficapsule.c
> >
> 

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

* [PATCH 01/10] efi_loader: disk: add efi_disk_is_system_part()
  2020-04-27 19:57   ` Heinrich Schuchardt
@ 2020-04-27 23:54     ` AKASHI Takahiro
  2020-05-01  7:06       ` Heinrich Schuchardt
  0 siblings, 1 reply; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-27 23:54 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Mon, Apr 27, 2020 at 09:57:25PM +0200, Heinrich Schuchardt wrote:
> On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> > This function will check if a given handle to device is a EFI system
> > partition. It will be utilised in implementing capsule-on-disk feature.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> 
> Just a reminder for me:
> 
> This patch depends on
> "part: detect EFI system partition"
> https://lists.denx.de/pipermail/u-boot/2020-April/408150.html

Yes, and I explicitly mentioned it in "prerequisite patches"
of the cover letter.
(I objected to the patch 2/2 though.)

> > ---
> >  include/efi_loader.h      |  2 ++
> >  lib/efi_loader/efi_disk.c | 22 ++++++++++++++++++++++
> >  2 files changed, 24 insertions(+)
> >
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index b7bccf50b30c..d4510462d616 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -393,6 +393,8 @@ efi_status_t efi_disk_register(void);
> >  int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
> >  			       const char *if_typename, int diskid,
> >  			       const char *pdevname);
> > +/* Check if it is EFI system partition */
> > +bool efi_disk_is_system_part(efi_handle_t handle);
> >  /* Called by bootefi to make GOP (graphical) interface available */
> >  efi_status_t efi_gop_register(void);
> >  /* Called by bootefi to make the network interface available */
> > diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
> > index fd3df80b0b96..9f58a8642c85 100644
> > --- a/lib/efi_loader/efi_disk.c
> > +++ b/lib/efi_loader/efi_disk.c
> > @@ -588,3 +588,25 @@ efi_status_t efi_disk_register(void)
> >
> >  	return EFI_SUCCESS;
> >  }
> > +
> 
> Please, provide a function description, cf.
> https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation

That is the thing that I've forgot to address in this version.

> > +bool efi_disk_is_system_part(efi_handle_t handle)
> > +{
> > +	struct efi_handler *handler;
> > +	struct efi_disk_obj *diskobj;
> > +	disk_partition_t info;
> > +	efi_status_t ret;
> > +	int r;
> > +
> > +	/* check if this is a block device */
> > +	ret = efi_search_protocol(handle, &efi_block_io_guid, &handler);
> > +	if (ret != EFI_SUCCESS)
> > +		return false;
> > +
> > +	diskobj = container_of(handle, struct efi_disk_obj, header);
> 
> This is just a complicated way of saying:
> 
> diskobj = (struct efi_disk_obj *)handle;
> 
> I would rather avoid container_of() here though it is not wrong.

My code doesn't rely on the fact that 'handle' is the first
element of 'struct efi_disk_obj'
So use of container_of has no disadvantage.

Thanks,
-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> > +
> > +	r = part_get_info(diskobj->desc, diskobj->part, &info);
> > +	if (r)
> > +		return false;
> > +
> > +	return info.bootable & PART_EFI_SYSTEM_PARTITION;
> > +}
> >

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

* [PATCH 02/10] efi_loader: add option to initialise EFI subsystem early
  2020-04-27 20:09   ` Heinrich Schuchardt
@ 2020-04-28  0:16     ` AKASHI Takahiro
  2020-05-17  7:29       ` Heinrich Schuchardt
  0 siblings, 1 reply; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-28  0:16 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Mon, Apr 27, 2020 at 10:09:11PM +0200, Heinrich Schuchardt wrote:
> On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> > If this option is enabled, the initialisation of UEFI subsystem will be
> > done as part of U-Boot initialisation.
> >
> > This feature will be utilised in implementing capsule-on-disk feature.
> 
> This would mean that we allow unaligned access very early. Something
> Siarhei was against:

?
Even with CONFIG_EFI_CAPSULE_ON_DISK_EARLY enabled,
efi_init_obj_list() is called at the last of "init" list
and efi_launch_capsules() is called just before the main
command loop.
So "unalignment" issue won't happen.

> https://lists.denx.de/pipermail/u-boot/2018-March/324242.html
> https://patchwork.ozlabs.org/project/uboot/patch/20180329213350.7868-1-xypron.glpk at gmx.de/
> 
> Why can't you wait with the capsule update until any command initializes
> the UEFI sub-system.

This topic is the one the I mentioned in RFC's cover letter
and asked you for comments several time.
Anyway, there are a couple of reasons:
1. Updated firmware may have some effects on not only UEFI
   subsystem but also U-Boot's other features.
2. Firmware update should surely take place after reboot
   as UEFI specification expects.
3. Firmware update should not rely on user's interactions
   or whatever "bootcmd" is set to.
4. In case of failure of firmware update, some recovery should
   be automatically taken "before" the command line is handed over
   to users. (The feature is not implemented yet though.)

-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  common/board_r.c       | 6 ++++++
> >  lib/efi_loader/Kconfig | 4 ++++
> >  2 files changed, 10 insertions(+)
> >
> > diff --git a/common/board_r.c b/common/board_r.c
> > index 0bbeaa7594c6..7cf21a6078f9 100644
> > --- a/common/board_r.c
> > +++ b/common/board_r.c
> > @@ -64,6 +64,9 @@
> >  #if defined(CONFIG_GPIO_HOG)
> >  #include <asm/gpio.h>
> >  #endif
> > +#ifdef CONFIG_EFI_SETUP_EARLY
> > +#include <efi_loader.h>
> > +#endif
> >
> >  DECLARE_GLOBAL_DATA_PTR;
> >
> > @@ -867,6 +870,9 @@ static init_fnc_t init_sequence_r[] = {
> >  #endif
> >  #if defined(CONFIG_M68K) && defined(CONFIG_BLOCK_CACHE)
> >  	blkcache_init,
> > +#endif
> > +#ifdef CONFIG_EFI_SETUP_EARLY
> > +	(init_fnc_t)efi_init_obj_list,
> >  #endif
> >  	run_main_loop,
> >  };
> > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > index 1cfa24ffcf72..7cc2d940f848 100644
> > --- a/lib/efi_loader/Kconfig
> > +++ b/lib/efi_loader/Kconfig
> > @@ -25,6 +25,10 @@ config EFI_LOADER
> >
> >  if EFI_LOADER
> >
> > +config EFI_SETUP_EARLY
> > +	bool
> > +	default n
> > +
> >  config EFI_GET_TIME
> >  	bool "GetTime() runtime service"
> >  	depends on DM_RTC
> >
> 

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

* [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support
  2020-04-27 20:28   ` Heinrich Schuchardt
@ 2020-04-28  0:28     ` AKASHI Takahiro
  2020-04-30 12:52       ` Sughosh Ganu
  0 siblings, 1 reply; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-28  0:28 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Mon, Apr 27, 2020 at 10:28:35PM +0200, Heinrich Schuchardt wrote:
> On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> > Capsule data can be loaded into the system either via UpdateCapsule
> > runtime service or files on a file system (of boot device).
> > The latter case is called "capsules on disk", and actual updates will
> > take place at the next boot time.
> >
> > In this commit, we will support capsule on disk mechanism.
> >
> > Please note that U-Boot itself has no notion of "boot device" and
> > all the capsule files to be executed will be detected only if they
> > are located in a specific directory, \EFI\UpdateCapsule, on a device
> > that is identified as a boot device by "BootXXXX" variables.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  common/main.c                |   4 +
> >  include/efi_loader.h         |  16 ++
> >  lib/efi_loader/Kconfig       |  22 ++
> >  lib/efi_loader/efi_capsule.c | 449 +++++++++++++++++++++++++++++++++++
> >  lib/efi_loader/efi_setup.c   |   9 +
> >  5 files changed, 500 insertions(+)
> >
> > diff --git a/common/main.c b/common/main.c
> > index 06d7ff56d60c..877ae63b708d 100644
> > --- a/common/main.c
> > +++ b/common/main.c
> > @@ -14,6 +14,7 @@
> >  #include <env.h>
> >  #include <init.h>
> >  #include <version.h>
> > +#include <efi_loader.h>
> >
> >  static void run_preboot_environment_command(void)
> >  {
> > @@ -51,6 +52,9 @@ void main_loop(void)
> >  	if (IS_ENABLED(CONFIG_UPDATE_TFTP))
> >  		update_tftp(0UL, NULL, NULL);
> >
> > +	if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
> > +		efi_launch_capsules();
> > +
> 
> Can't we move this to efi_init_obj_list() and do away with
> CONFIG_EFI_CAPSULE_ON_DISK_EARLY?

With CONFIG_EFI_CAPSULE_ON_DISK_EARLY disabled,
efi_launch_capsules() will be called in efi_init_obj_list()
as you expect. See the code below in efi_setup.c.

> >  	s = bootdelay_process();
> >  	if (cli_process_fdt(&s))
> >  		cli_secure_boot_cmd(s);
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index 19ffc027c171..d49ebcad53ec 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -793,6 +793,18 @@ efi_status_t EFIAPI efi_query_capsule_caps(
> >  		u32 *reset_type);
> >  #endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */
> >
> > +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> > +#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\"
> > +
> > +/* Hook at initialization */
> > +efi_status_t efi_launch_capsules(void);
> > +#else
> > +static inline efi_status_t efi_launch_capsules(void)
> > +{
> > +	return EFI_SUCCESS;
> > +}
> > +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
> > +
> >  #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
> >
> >  /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
> > @@ -809,6 +821,10 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr,
> >  				   const char *path) { }
> >  static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
> >  static inline void efi_print_image_infos(void *pc) { }
> > +static inline efi_status_t efi_launch_capsules(void)
> > +{
> > +	return EFI_SUCCESS;
> > +}
> >
> >  #endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
> >
> > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > index e2b08251f26a..b48b95a32e03 100644
> > --- a/lib/efi_loader/Kconfig
> > +++ b/lib/efi_loader/Kconfig
> > @@ -56,6 +56,28 @@ config EFI_RUNTIME_UPDATE_CAPSULE
> >  	  Select this option if you want to use UpdateCapsule and
> >  	  QueryCapsuleCapabilities API's.
> >
> > +config EFI_CAPSULE_ON_DISK
> > +	bool "Enable capsule-on-disk support"
> > +	select EFI_HAVE_CAPSULE_SUPPORT
> > +	default n
> > +	help
> > +	  Select this option if you want to use capsule-on-disk feature,
> > +	  that is, capsules can be fetched and executed from files
> > +	  under a specific directory on UEFI system partition instead of
> > +	  via UpdateCapsule API.
> > +
> > +config EFI_CAPSULE_ON_DISK_EARLY
> > +	bool "Initiate capsule-on-disk at U-Boot boottime"
> > +	depends on EFI_CAPSULE_ON_DISK
> > +	default y
> > +	select EFI_SETUP_EARLY
> > +	help
> > +	  Normally, without this option enabled, capsules will be
> > +	  executed only at the first time of invoking one of efi command.
> > +	  If this option is enabled, capsules will be enforced to be
> > +	  executed as part of U-Boot initialisation so that they will
> > +	  surely take place whatever is set to distro_bootcmd.
> 
> Why do we need this Kconfig variable if we have EFI_SETUP_EARLY available?

Good point.
My intent here was to split efi_launch_capsules() from
efi_init_obj_list() so that users can start UEFI early
for some reason without enabling capsule feature in the future.
EFI_SETUP_EARLY is the hook for that.

> > +
> >  config EFI_DEVICE_PATH_TO_TEXT
> >  	bool "Device path to text protocol"
> >  	default y
> > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> > index fb104bb92a6c..938129a41934 100644
> > --- a/lib/efi_loader/efi_capsule.c
> > +++ b/lib/efi_loader/efi_capsule.c
> > @@ -10,10 +10,16 @@
> >  #include <efi_loader.h>
> >  #include <fs.h>
> >  #include <malloc.h>
> > +#include <mapmem.h>
> >  #include <sort.h>
> >
> >  const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
> >
> > +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> > +/* for file system access */
> > +static struct efi_file_handle *bootdev_root;
> > +#endif
> > +
> >  static __maybe_unused int get_last_capsule(void)
> >  {
> >  	u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
> > @@ -151,3 +157,446 @@ efi_status_t EFIAPI efi_query_capsule_caps(
> >  out:
> >  	return EFI_EXIT(ret);
> >  }
> > +
> > +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> > +static efi_status_t get_dp_device(u16 *boot_var,
> > +				  struct efi_device_path **device_dp)
> > +{
> > +	void *buf = NULL;
> > +	efi_uintn_t size;
> > +	struct efi_load_option lo;
> > +	struct efi_device_path *file_dp;
> > +	efi_status_t ret;
> > +
> > +	size = 0;
> > +	ret = EFI_CALL(efi_get_variable(boot_var, &efi_global_variable_guid,
> > +					NULL, &size, NULL));
> > +	if (ret == EFI_BUFFER_TOO_SMALL) {
> > +		buf = malloc(size);
> > +		if (!buf)
> > +			return EFI_OUT_OF_RESOURCES;
> > +		ret = EFI_CALL(efi_get_variable(boot_var,
> > +						&efi_global_variable_guid,
> > +						NULL, &size, buf));
> > +	}
> > +	if (ret != EFI_SUCCESS)
> > +		return ret;
> > +
> > +	efi_deserialize_load_option(&lo, buf);
> > +
> > +	if (lo.attributes & LOAD_OPTION_ACTIVE) {
> > +		efi_dp_split_file_path(lo.file_path, device_dp, &file_dp);
> > +		efi_free_pool(file_dp);
> > +
> > +		ret = EFI_SUCCESS;
> > +	} else {
> > +		ret = EFI_NOT_FOUND;
> > +	}
> > +
> > +	free(buf);
> > +
> > +	return ret;
> > +}
> > +
> > +static bool device_is_present_and_system_part(struct efi_device_path *dp)
> > +{
> > +	efi_handle_t handle;
> > +
> > +	handle = efi_dp_find_obj(dp, NULL);
> > +	if (!handle)
> > +		return false;
> > +
> > +	return efi_disk_is_system_part(handle);
> > +}
> > +
> > +static efi_status_t find_boot_device(void)
> > +{
> > +	char boot_var[9];
> > +	u16 boot_var16[9], *p, bootnext, *boot_order = NULL;
> > +	efi_uintn_t size;
> > +	int i, num;
> > +	struct efi_simple_file_system_protocol *volume;
> > +	struct efi_device_path *boot_dev = NULL;
> > +	efi_status_t ret;
> > +
> > +	/* find active boot device in BootNext */
> > +	bootnext = 0;
> > +	size = sizeof(bootnext);
> > +	ret = EFI_CALL(efi_get_variable(L"BootNext",
> > +					(efi_guid_t *)&efi_global_variable_guid,
> > +					NULL, &size, &bootnext));
> > +	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
> > +		/* BootNext does exist here */
> > +		if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) {
> > +			printf("BootNext must be 16-bit integer\n");
> > +			goto skip;
> > +		}
> > +		sprintf((char *)boot_var, "Boot%04X", bootnext);
> > +		p = boot_var16;
> > +		utf8_utf16_strcpy(&p, boot_var);
> > +
> > +		ret = get_dp_device(boot_var16, &boot_dev);
> > +		if (ret == EFI_SUCCESS) {
> > +			if (device_is_present_and_system_part(boot_dev)) {
> > +				goto out;
> > +			} else {
> > +				efi_free_pool(boot_dev);
> > +				boot_dev = NULL;
> > +			}
> > +		}
> > +	}
> > +
> > +skip:
> > +	/* find active boot device in BootOrder */
> > +	size = 0;
> > +	ret = EFI_CALL(efi_get_variable(L"BootOrder", &efi_global_variable_guid,
> > +					NULL, &size, NULL));
> > +	if (ret == EFI_BUFFER_TOO_SMALL) {
> > +		boot_order = malloc(size);
> > +		if (!boot_order) {
> > +			ret = EFI_OUT_OF_RESOURCES;
> > +			goto out;
> > +		}
> > +
> > +		ret = EFI_CALL(efi_get_variable(
> > +					L"BootOrder", &efi_global_variable_guid,
> > +					NULL, &size, boot_order));
> > +	}
> > +	if (ret != EFI_SUCCESS)
> > +		goto out;
> > +
> > +	/* check in higher order */
> > +	num = size / sizeof(u16);
> > +	for (i = 0; i < num; i++) {
> > +		sprintf((char *)boot_var, "Boot%04X", boot_order[i]);
> > +		p = boot_var16;
> > +		utf8_utf16_strcpy(&p, boot_var);
> > +		ret = get_dp_device(boot_var16, &boot_dev);
> > +		if (ret != EFI_SUCCESS)
> > +			continue;
> > +
> > +		if (device_is_present_and_system_part(boot_dev))
> > +			break;
> > +
> > +		efi_free_pool(boot_dev);
> > +		boot_dev = NULL;
> > +	}
> > +out:
> > +	if (boot_dev) {
> > +		u16 *path_str;
> > +
> > +		path_str = efi_dp_str(boot_dev);
> > +		EFI_PRINT("EFI Capsule: bootdev is %ls\n", path_str);
> > +		efi_free_pool(path_str);
> > +
> > +		volume = efi_fs_from_path(boot_dev);
> > +		if (!volume)
> > +			ret = EFI_DEVICE_ERROR;
> > +		else
> > +			ret = EFI_CALL(volume->open_volume(volume,
> > +							   &bootdev_root));
> > +		efi_free_pool(boot_dev);
> > +	} else {
> > +		ret = EFI_NOT_FOUND;
> > +	}
> > +	free(boot_order);
> > +
> > +	return ret;
> > +}
> > +
> > +/*
> > + * Traverse a capsule directory in boot device
> > + * Called by initialization code, and returns an array of capsule file
> > + * names in @files
> > + */
> > +static efi_status_t efi_capsule_scan_dir(u16 ***files, int *num)
> > +{
> > +	struct efi_file_handle *dirh;
> > +	struct efi_file_info *dirent;
> > +	efi_uintn_t dirent_size, tmp_size;
> > +	int count;
> > +	u16 **tmp_files;
> > +	efi_status_t ret;
> > +
> > +	ret = find_boot_device();
> > +	if (ret == EFI_NOT_FOUND) {
> > +		EFI_PRINT("EFI Capsule: bootdev is not set\n");
> > +		*num = 0;
> > +		return EFI_SUCCESS;
> > +	} else if (ret != EFI_SUCCESS) {
> > +		return EFI_DEVICE_ERROR;
> > +	}
> > +
> > +	/* count capsule files */
> > +	ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
> > +					     EFI_CAPSULE_DIR,
> > +					     EFI_FILE_MODE_READ, 0));
> > +	if (ret != EFI_SUCCESS) {
> > +		*num = 0;
> > +		return EFI_SUCCESS;
> > +	}
> > +
> > +	dirent_size = 256;
> > +	dirent = malloc(dirent_size);
> > +	if (!dirent)
> > +		return EFI_OUT_OF_RESOURCES;
> > +
> > +	count = 0;
> > +	while (1) {
> > +		tmp_size = dirent_size;
> > +		ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
> > +		if (ret == EFI_BUFFER_TOO_SMALL) {
> > +			dirent = realloc(dirent, tmp_size);
> > +			if (!dirent) {
> > +				ret = EFI_OUT_OF_RESOURCES;
> > +				goto err;
> > +			}
> > +			dirent_size = tmp_size;
> > +			ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
> > +		}
> > +		if (ret != EFI_SUCCESS)
> > +			goto err;
> > +		if (!tmp_size)
> > +			break;
> > +
> > +		if (!(dirent->attribute & EFI_FILE_DIRECTORY) &&
> > +		    u16_strcmp(dirent->file_name, L".") &&
> > +		    u16_strcmp(dirent->file_name, L".."))
> > +			count++;
> > +	}
> > +
> > +	ret = EFI_CALL((*dirh->setpos)(dirh, 0));
> > +	if (ret != EFI_SUCCESS)
> > +		goto err;
> > +
> > +	/* make a list */
> > +	tmp_files = malloc(count * sizeof(*files));
> > +	if (!tmp_files) {
> > +		ret = EFI_OUT_OF_RESOURCES;
> > +		goto err;
> > +	}
> > +
> > +	count = 0;
> > +	while (1) {
> > +		tmp_size = dirent_size;
> > +		ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
> > +		if (ret != EFI_SUCCESS)
> > +			goto err;
> > +		if (!tmp_size)
> > +			break;
> > +
> > +		if (!(dirent->attribute & EFI_FILE_DIRECTORY) &&
> > +		    u16_strcmp(dirent->file_name, L".") &&
> > +		    u16_strcmp(dirent->file_name, L".."))
> > +			tmp_files[count++] = u16_strdup(dirent->file_name);
> > +	}
> > +	/* ignore an error */
> > +	EFI_CALL((*dirh->close)(dirh));
> > +
> > +	/* in ascii order */
> > +	/* FIXME: u16 version of strcasecmp */
> > +	qsort(tmp_files, count, sizeof(*tmp_files),
> > +	      (int (*)(const void *, const void *))strcasecmp);
> > +	*files = tmp_files;
> > +	*num = count;
> > +	ret = EFI_SUCCESS;
> > +err:
> > +	free(dirent);
> > +
> > +	return ret;
> > +}
> > +
> > +/*
> > + * Read in a capsule file
> > + */
> > +static efi_status_t efi_capsule_read_file(u16 *filename,
> > +					  struct efi_capsule_header **capsule)
> > +{
> > +	struct efi_file_handle *dirh, *fh;
> > +	struct efi_file_info *file_info = NULL;
> > +	struct efi_capsule_header *buf = NULL;
> > +	efi_uintn_t size;
> > +	efi_status_t ret;
> > +
> > +	ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
> > +					     EFI_CAPSULE_DIR,
> > +					     EFI_FILE_MODE_READ, 0));
> > +	if (ret != EFI_SUCCESS)
> > +		return ret;
> > +	ret = EFI_CALL((*dirh->open)(dirh, &fh, filename,
> > +				     EFI_FILE_MODE_READ, 0));
> > +	/* ignore an error */
> > +	EFI_CALL((*dirh->close)(dirh));
> > +	if (ret != EFI_SUCCESS)
> > +		return ret;
> > +
> > +	/* file size */
> > +	size = 0;
> > +	ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid,
> > +				      &size, file_info));
> > +	if (ret == EFI_BUFFER_TOO_SMALL) {
> > +		file_info = malloc(size);
> > +		if (!file_info) {
> > +			ret = EFI_OUT_OF_RESOURCES;
> > +			goto err;
> > +		}
> > +		ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid,
> > +					      &size, file_info));
> > +	}
> > +	if (ret != EFI_SUCCESS)
> > +		goto err;
> > +	size = file_info->file_size;
> > +	free(file_info);
> > +	buf = malloc(size);
> > +	if (!buf) {
> > +		ret = EFI_OUT_OF_RESOURCES;
> > +		goto err;
> > +	}
> > +
> > +	/* fetch data */
> > +	ret = EFI_CALL((*fh->read)(fh, &size, buf));
> > +	if (ret == EFI_SUCCESS) {
> > +		if (size >= buf->capsule_image_size) {
> > +			*capsule = buf;
> > +		} else {
> > +			free(buf);
> > +			ret = EFI_INVALID_PARAMETER;
> > +		}
> > +	} else {
> > +		free(buf);
> > +	}
> > +err:
> > +	EFI_CALL((*fh->close)(fh));
> > +
> > +	return ret;
> > +}
> > +
> > +static efi_status_t efi_capsule_delete_file(u16 *filename)
> > +{
> > +	struct efi_file_handle *dirh, *fh;
> > +	efi_status_t ret;
> > +
> > +	ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
> > +					     EFI_CAPSULE_DIR,
> > +					     EFI_FILE_MODE_READ, 0));
> > +	if (ret != EFI_SUCCESS)
> > +		return ret;
> > +	ret = EFI_CALL((*dirh->open)(dirh, &fh, filename,
> > +				     EFI_FILE_MODE_READ, 0));
> > +	/* ignore an error */
> > +	EFI_CALL((*dirh->close)(dirh));
> > +
> > +	ret = EFI_CALL((*fh->delete)(fh));
> > +
> > +	return ret;
> > +}
> > +
> > +static void efi_capsule_scan_done(void)
> > +{
> > +	EFI_CALL((*bootdev_root->close)(bootdev_root));
> > +	bootdev_root = NULL;
> > +}
> > +
> > +efi_status_t __weak arch_efi_load_capsule_drivers(void)
> > +{
> > +	return EFI_SUCCESS;
> > +}
> > +
> > +/*
> > + * Launch all the capsules in system at boot time
> > + *
> > + * Called by efi init code
> > + */
> 
> Where are the function descriptions?

Okay.

Thanks,
-Takahiro Akashi

> https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation
> 
> Best regards
> 
> Heinrich
> 
> > +efi_status_t efi_launch_capsules(void)
> > +{
> > +	u64 os_indications;
> > +	efi_uintn_t size;
> > +	struct efi_capsule_header *capsule = NULL;
> > +	u16 **files;
> > +	int nfiles, num, i;
> > +	char variable_name[12];
> > +	u16 variable_name16[12], *p;
> > +	efi_status_t ret;
> > +
> > +	size = sizeof(os_indications);
> > +	ret = EFI_CALL(efi_get_variable(L"OsIndications",
> > +					&efi_global_variable_guid,
> > +					NULL, &size, &os_indications));
> > +	if (ret != EFI_SUCCESS ||
> > +	    !(os_indications
> > +	      & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED))
> > +		return EFI_SUCCESS;
> > +
> > +	num = get_last_capsule();
> > +
> > +	/* Load capsule drivers */
> > +	ret = arch_efi_load_capsule_drivers();
> > +	if (ret != EFI_SUCCESS)
> > +		return ret;
> > +
> > +	/*
> > +	 * Find capsules on disk.
> > +	 * All the capsules are collected at the beginning because
> > +	 * capsule files will be removed instantly.
> > +	 */
> > +	nfiles = 0;
> > +	files = NULL;
> > +	ret = efi_capsule_scan_dir(&files, &nfiles);
> > +	if (ret != EFI_SUCCESS)
> > +		return ret;
> > +	if (!nfiles)
> > +		return EFI_SUCCESS;
> > +
> > +	/* Launch capsules */
> > +	for (i = 0, ++num; i < nfiles; i++, num++) {
> > +		EFI_PRINT("capsule from %ls ...\n", files[i]);
> > +		if (num > 0xffff)
> > +			num = 0;
> > +		ret = efi_capsule_read_file(files[i], &capsule);
> > +		if (ret == EFI_SUCCESS) {
> > +			ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0));
> > +			if (ret != EFI_SUCCESS)
> > +				printf("EFI Capsule update failed at %ls\n",
> > +				       files[i]);
> > +
> > +			free(capsule);
> > +		} else {
> > +			printf("EFI: reading capsule failed: %ls\n",
> > +			       files[i]);
> > +		}
> > +		/* create CapsuleXXXX */
> > +		set_capsule_result(num, capsule, ret);
> > +
> > +		/* delete a capsule either in case of success or failure */
> > +		ret = efi_capsule_delete_file(files[i]);
> > +		if (ret != EFI_SUCCESS)
> > +			printf("EFI: deleting a capsule file failed: %ls\n",
> > +			       files[i]);
> > +	}
> > +	efi_capsule_scan_done();
> > +
> > +	for (i = 0; i < nfiles; i++)
> > +		free(files[i]);
> > +	free(files);
> > +
> > +	/* CapsuleMax */
> > +	p = variable_name16;
> > +	utf8_utf16_strncpy(&p, "CapsuleFFFF", 11);
> > +	EFI_CALL(efi_set_variable(L"CapsuleMax", &efi_guid_capsule_report,
> > +				  EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +				  EFI_VARIABLE_RUNTIME_ACCESS,
> > +				  22, variable_name16));
> > +
> > +	/* CapsuleLast */
> > +	sprintf(variable_name, "Capsule%04X", num - 1);
> > +	p = variable_name16;
> > +	utf8_utf16_strncpy(&p, variable_name, 11);
> > +	EFI_CALL(efi_set_variable(L"CapsuleLast", &efi_guid_capsule_report,
> > +				  EFI_VARIABLE_NON_VOLATILE |
> > +				  EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +				  EFI_VARIABLE_RUNTIME_ACCESS,
> > +				  22, variable_name16));
> > +
> > +	return ret;
> > +}
> > +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
> > diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
> > index 8fe378bbfdfc..bb759976102a 100644
> > --- a/lib/efi_loader/efi_setup.c
> > +++ b/lib/efi_loader/efi_setup.c
> > @@ -129,6 +129,10 @@ static efi_status_t efi_init_os_indications(void)
> >  #ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
> >  	os_indications_supported |=
> >  			EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
> > +#endif
> > +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> > +	os_indications_supported |=
> > +			EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED;
> >  #endif
> >  	return EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
> >  					 &efi_global_variable_guid,
> > @@ -239,6 +243,11 @@ efi_status_t efi_init_obj_list(void)
> >  	if (ret != EFI_SUCCESS)
> >  		goto out;
> >
> > +#if defined(CONFIG_EFI_CAPSULE_ON_DISK) && \
> > +		!defined(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)
> > +	/* Execute capsules after reboot */
> > +	ret = efi_launch_capsules();
> > +#endif
> >  out:
> >  	efi_obj_list_initialized = ret;
> >  	return ret;
> >
> 

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

* [PATCH 09/10] tools: add mkeficapsule command for UEFI capsule update test
  2020-04-27 20:15   ` Heinrich Schuchardt
@ 2020-04-28  1:52     ` AKASHI Takahiro
  0 siblings, 0 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-04-28  1:52 UTC (permalink / raw)
  To: u-boot

On Mon, Apr 27, 2020 at 10:15:41PM +0200, Heinrich Schuchardt wrote:
> On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> > This is a utility mainly for test purpose.
> >   mkeficapsule -f: create a test capsule file for FIT image firmware
> >
> > Having said that, you will be able to customize the code to fit
> > your specific requirements for your platform.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  tools/Makefile       |   3 +
> >  tools/mkeficapsule.c | 209 +++++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 212 insertions(+)
> >  create mode 100644 tools/mkeficapsule.c
> >
> > diff --git a/tools/Makefile b/tools/Makefile
> > index c2b26340047a..08e97063d5fa 100644
> > --- a/tools/Makefile
> > +++ b/tools/Makefile
> > @@ -222,6 +222,9 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> >  hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
> >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> >
> > +# TODO: only build this for capsule pytest
> 
> Do you plan to send a new version of the series to fix this issue?

No.

> We need a documentation page for this tool in doc/uefi/.

Even currently used only in a test?

> Best regards
> 
> Heinrich
> 
> > +hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > +
> >  # We build some files with extra pedantic flags to try to minimize things
> >  # that won't build on some weird host compiler -- though there are lots of
> >  # exceptions for files that aren't complaint.
> > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > new file mode 100644
> > index 000000000000..b26c88e42020
> > --- /dev/null
> > +++ b/tools/mkeficapsule.c
> > @@ -0,0 +1,209 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright 2018 Linaro Limited
> > + *		Author: AKASHI Takahiro
> > + */
> > +
> > +#include <malloc.h>
> > +#include <stdio.h>
> > +#include <sys/stat.h>
> > +#include <sys/types.h>
> > +/*
> > + * TODO: use libefi/libgnuefi headers
> 
> Can't you use the definitions from include/efi_api.h?
> I would prefer if you could avoid duplicate definitions in this file.

Including efi.h and efi_api.h causes a bunch of compiler errors.
But now I could sort them out.
Will fix in the next version.

Thanks,
-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> > + */
> > +
> > +typedef u_int8_t u8;
> > +typedef u_int16_t u16;
> > +typedef u_int32_t u32;
> > +typedef u_int64_t u64;
> > +
> > +/* include/efi.h */
> > +typedef struct {
> > +	u8 b[16];
> > +} efi_guid_t;
> > +
> > +#define EFI_GUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \
> > +	{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, \
> > +		((a) >> 24) & 0xff, \
> > +		(b) & 0xff, ((b) >> 8) & 0xff, \
> > +		(c) & 0xff, ((c) >> 8) & 0xff, \
> > +		(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } }
> > +
> > +#define EFI_VARIABLE_NON_VOLATILE	0x0000000000000001
> > +#define EFI_VARIABLE_BOOTSERVICE_ACCESS	0x0000000000000002
> > +#define EFI_VARIABLE_RUNTIME_ACCESS	0x0000000000000004
> > +
> > +/* include/efi_api.h */
> > +#define EFI_GLOBAL_VARIABLE_GUID \
> > +	EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
> > +		 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
> > +
> > +#define CAPSULE_FLAGS_PERSIST_ACROSS_RESET	0x00010000
> > +#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE	0x00020000
> > +#define CAPSULE_FLAGS_INITIATE_RESET		0x00040000
> > +
> > +#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID \
> > +	EFI_GUID(0xae13ff2d, 0x9ad4, 0x4e25, 0x9a, 0xc8, \
> > +		 0x6d, 0x80, 0xb3, 0xb2, 0x21, 0x47)
> > +
> > +#define EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID \
> > +	EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \
> > +		 0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a)
> > +
> > +struct efi_capsule_header {
> > +	efi_guid_t capsule_guid;
> > +	u32 header_size;
> > +	u32 flags;
> > +	u32 capsule_image_size;
> > +} __attribute__((packed));
> > +
> > +struct efi_capsule_block_descriptor {
> > +	u64 length;
> > +	union {
> > +		u64 data_block;
> > +		u64 continuation_ptr;
> > +	};
> > +} __attribute__((packed));
> > +
> > +struct efi_firmware_management_capsule_header {
> > +	u32 version;
> > +	u16 embedded_driver_count;
> > +	u16 payload_item_count;
> > +	u64 item_offset_list[];
> > +} __attribute__((packed));
> > +
> > +struct efi_firmware_management_capsule_image_header {
> > +	u32 version;
> > +	efi_guid_t update_image_type_id;
> > +	u8 update_image_index;
> > +	u8 reserved[3];
> > +	u32 update_image_size;
> > +	u32 update_vendor_code_size;
> > +	u64 update_hardware_instance;
> > +} __attribute__((packed));
> > +
> > +efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> > +efi_guid_t efi_guid_image_type_uboot_fit =
> > +		EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
> > +
> > +static int create_fwbin(char *bin, char *path)
> > +{
> > +	struct efi_capsule_header header;
> > +	struct efi_firmware_management_capsule_header capsule;
> > +	struct efi_firmware_management_capsule_image_header image;
> > +	FILE *f, *g;
> > +	struct stat bin_stat;
> > +	u8 *data;
> > +	size_t size;
> > +
> > +	g = fopen(bin, "r");
> > +	if (!g) {
> > +		printf("cannot open %s\n", bin);
> > +		return -1;
> > +	}
> > +	if (stat(bin, &bin_stat) < 0) {
> > +		printf("cannot determine the size of %s\n", bin);
> > +		goto err_1;
> > +	}
> > +	data = malloc(bin_stat.st_size);
> > +	if (!data) {
> > +		printf("cannot allocate memory: %lx\n", bin_stat.st_size);
> > +		goto err_1;
> > +	}
> > +	f = fopen(path, "w");
> > +	if (!f) {
> > +		printf("cannot open %s\n", path);
> > +		goto err_2;
> > +	}
> > +	header.capsule_guid = efi_guid_fm_capsule;
> > +	header.header_size = sizeof(header);
> > +	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
> > +	header.capsule_image_size = sizeof(header)
> > +					+ sizeof(capsule) + sizeof(u64)
> > +					+ sizeof(image)
> > +					+ bin_stat.st_size;
> > +
> > +	size = fwrite(&header, 1, sizeof(header), f);
> > +	if (size < sizeof(header)) {
> > +		printf("write failed (%lx)\n", size);
> > +		goto err_3;
> > +	}
> > +
> > +	capsule.version = 0x00000001;
> > +	capsule.embedded_driver_count = 0;
> > +	capsule.payload_item_count = 1;
> > +	capsule.item_offset_list[0] = sizeof(capsule) + sizeof(u64);
> > +	size = fwrite(&capsule, 1, sizeof(capsule) + sizeof(u64), f);
> > +	if (size < (sizeof(capsule) + sizeof(u64))) {
> > +		printf("write failed (%lx)\n", size);
> > +		goto err_3;
> > +	}
> > +
> > +	image.version = 0x00000002;
> > +	image.update_image_type_id = efi_guid_image_type_uboot_fit;
> > +	image.update_image_index = 1;
> > +	image.update_image_size = bin_stat.st_size;
> > +	image.update_vendor_code_size = 0; /* none */
> > +	image.update_hardware_instance = 1;
> > +
> > +	size = fwrite(&image, 1, sizeof(image), f);
> > +	if (size < sizeof(image)) {
> > +		printf("write failed (%lx)\n", size);
> > +		goto err_3;
> > +	}
> > +	size = fread(data, 1, bin_stat.st_size, g);
> > +	if (size < bin_stat.st_size) {
> > +		printf("read failed (%lx)\n", size);
> > +		goto err_3;
> > +	}
> > +	size = fwrite(data, 1, bin_stat.st_size, f);
> > +	if (size < bin_stat.st_size) {
> > +		printf("write failed (%lx)\n", size);
> > +		goto err_3;
> > +	}
> > +
> > +	fclose(f);
> > +	fclose(g);
> > +	free(data);
> > +
> > +	return 0;
> > +
> > +err_3:
> > +	fclose(f);
> > +err_2:
> > +	free(data);
> > +err_1:
> > +	fclose(g);
> > +
> > +	return -1;
> > +}
> > +
> > +void print_usage(void)
> > +{
> > +	printf("mkeficapsule -f <firmware binary> <output file>\n");
> > +}
> > +
> > +/*
> > + * Usage:
> > + *   $ mkeficapsule -f <firmware binary> <output file>
> > + */
> > +int main(int argc, char **argv)
> > +{
> > +	if (argc != 4) {
> > +		print_usage();
> > +		return -1;
> > +	}
> > +
> > +	if (!strcmp(argv[1], "-f")) {
> > +		if (create_fwbin(argv[2], argv[3]) < 0) {
> > +			printf("Creating firmware capsule for %s failed\n",
> > +			       argv[2]);
> > +			return -1;
> > +		}
> > +
> > +		return 0;
> > +	}
> > +
> > +	print_usage();
> > +	return -1;
> > +}
> >
> 

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

* [PATCH 08/10] cmd: add "efidebug capsule" command
  2020-04-27  9:48 ` [PATCH 08/10] cmd: add "efidebug capsule" command AKASHI Takahiro
@ 2020-04-30 12:38   ` Sughosh Ganu
  2020-05-07  1:58     ` AKASHI Takahiro
  0 siblings, 1 reply; 33+ messages in thread
From: Sughosh Ganu @ 2020-04-30 12:38 UTC (permalink / raw)
  To: u-boot

On Mon, 27 Apr 2020 at 15:19, AKASHI Takahiro <takahiro.akashi@linaro.org>
wrote:

> "efidebug capsule" is more or less a debugging utility.
>   efidebug capsule update: invoke UpdateCapsule against data on memory
>   efidebug capsule show: show a capsule header
>   efidebug capsule result: dump a capsule result variable
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  cmd/efidebug.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 234 insertions(+)
>
> diff --git a/cmd/efidebug.c b/cmd/efidebug.c
> index 02ef01969443..8956fa1d50be 100644
> --- a/cmd/efidebug.c
> +++ b/cmd/efidebug.c
> @@ -19,6 +19,227 @@
>  #define BS systab.boottime
>  #define RT systab.runtime
>
> +#ifdef CONFIG_EFI_CAPSULE_UPDATE
>

This config option is no longer used in the non-rfc series. I think this
needs to be CONFIG_EFI_HAVE_CAPSULE_SUPPORT.

-sughosh

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

* [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support
  2020-04-28  0:28     ` AKASHI Takahiro
@ 2020-04-30 12:52       ` Sughosh Ganu
  2020-04-30 19:51         ` Heinrich Schuchardt
  0 siblings, 1 reply; 33+ messages in thread
From: Sughosh Ganu @ 2020-04-30 12:52 UTC (permalink / raw)
  To: u-boot

On Tue, 28 Apr 2020 at 05:58, AKASHI Takahiro <takahiro.akashi@linaro.org>
wrote:

> Heinrich,
>
> On Mon, Apr 27, 2020 at 10:28:35PM +0200, Heinrich Schuchardt wrote:
> > On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> > > Capsule data can be loaded into the system either via UpdateCapsule
> > > runtime service or files on a file system (of boot device).
> > > The latter case is called "capsules on disk", and actual updates will
> > > take place at the next boot time.
> > >
> > > In this commit, we will support capsule on disk mechanism.
> > >
> > > Please note that U-Boot itself has no notion of "boot device" and
> > > all the capsule files to be executed will be detected only if they
> > > are located in a specific directory, \EFI\UpdateCapsule, on a device
> > > that is identified as a boot device by "BootXXXX" variables.
> > >
> > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > ---
> > >  common/main.c                |   4 +
> > >  include/efi_loader.h         |  16 ++
> > >  lib/efi_loader/Kconfig       |  22 ++
> > >  lib/efi_loader/efi_capsule.c | 449 +++++++++++++++++++++++++++++++++++
> > >  lib/efi_loader/efi_setup.c   |   9 +
> > >  5 files changed, 500 insertions(+)
> > >
> > > diff --git a/common/main.c b/common/main.c
> > > index 06d7ff56d60c..877ae63b708d 100644
> > > --- a/common/main.c
> > > +++ b/common/main.c
> > > @@ -14,6 +14,7 @@
> > >  #include <env.h>
> > >  #include <init.h>
> > >  #include <version.h>
> > > +#include <efi_loader.h>
> > >
> > >  static void run_preboot_environment_command(void)
> > >  {
> > > @@ -51,6 +52,9 @@ void main_loop(void)
> > >     if (IS_ENABLED(CONFIG_UPDATE_TFTP))
> > >             update_tftp(0UL, NULL, NULL);
> > >
> > > +   if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
> > > +           efi_launch_capsules();
> > > +
> >
> > Can't we move this to efi_init_obj_list() and do away with
> > CONFIG_EFI_CAPSULE_ON_DISK_EARLY?
>
> With CONFIG_EFI_CAPSULE_ON_DISK_EARLY disabled,
> efi_launch_capsules() will be called in efi_init_obj_list()
> as you expect. See the code below in efi_setup.c.
>

Instead of calling efi_launch_capsules in efi_init_obj_list, can we invoke
the function explicitly through a dedicated command line, under the
'efidebug capsule' class of commands. I think that would be a cleaner
approach, since efi_init_obj_list gets called for a lot of efi functions,
which are unrelated to capsule update.

-sughosh

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

* [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support
  2020-04-30 12:52       ` Sughosh Ganu
@ 2020-04-30 19:51         ` Heinrich Schuchardt
  2020-05-07  2:50           ` AKASHI Takahiro
  2020-05-07 12:05           ` Sughosh Ganu
  0 siblings, 2 replies; 33+ messages in thread
From: Heinrich Schuchardt @ 2020-04-30 19:51 UTC (permalink / raw)
  To: u-boot

On 4/30/20 2:52 PM, Sughosh Ganu wrote:
>
> On Tue, 28 Apr 2020 at 05:58, AKASHI Takahiro
> <takahiro.akashi at linaro.org <mailto:takahiro.akashi@linaro.org>> wrote:
>
>     Heinrich,
>
>     On Mon, Apr 27, 2020 at 10:28:35PM +0200, Heinrich Schuchardt wrote:
>     > On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
>     > > Capsule data can be loaded into the system either via UpdateCapsule
>     > > runtime service or files on a file system (of boot device).
>     > > The latter case is called "capsules on disk", and actual updates
>     will
>     > > take place at the next boot time.
>     > >
>     > > In this commit, we will support capsule on disk mechanism.
>     > >
>     > > Please note that U-Boot itself has no notion of "boot device" and
>     > > all the capsule files to be executed will be detected only if they
>     > > are located in a specific directory, \EFI\UpdateCapsule, on a device
>     > > that is identified as a boot device by "BootXXXX" variables.
>     > >
>     > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org
>     <mailto:takahiro.akashi@linaro.org>>
>     > > ---
>     > >? common/main.c? ? ? ? ? ? ? ? |? ?4 +
>     > >? include/efi_loader.h? ? ? ? ?|? 16 ++
>     > >? lib/efi_loader/Kconfig? ? ? ?|? 22 ++
>     > >? lib/efi_loader/efi_capsule.c | 449
>     +++++++++++++++++++++++++++++++++++
>     > >? lib/efi_loader/efi_setup.c? ?|? ?9 +
>     > >? 5 files changed, 500 insertions(+)
>     > >
>     > > diff --git a/common/main.c b/common/main.c
>     > > index 06d7ff56d60c..877ae63b708d 100644
>     > > --- a/common/main.c
>     > > +++ b/common/main.c
>     > > @@ -14,6 +14,7 @@
>     > >? #include <env.h>
>     > >? #include <init.h>
>     > >? #include <version.h>
>     > > +#include <efi_loader.h>
>     > >
>     > >? static void run_preboot_environment_command(void)
>     > >? {
>     > > @@ -51,6 +52,9 @@ void main_loop(void)
>     > >? ? ?if (IS_ENABLED(CONFIG_UPDATE_TFTP))
>     > >? ? ? ? ? ? ?update_tftp(0UL, NULL, NULL);
>     > >
>     > > +? ?if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
>     > > +? ? ? ? ? ?efi_launch_capsules();
>     > > +
>     >
>     > Can't we move this to efi_init_obj_list() and do away with
>     > CONFIG_EFI_CAPSULE_ON_DISK_EARLY?
>
>     With CONFIG_EFI_CAPSULE_ON_DISK_EARLY disabled,
>     efi_launch_capsules() will be called in efi_init_obj_list()
>     as you expect. See the code below in efi_setup.c.
>
>
> Instead of calling efi_launch_capsules in efi_init_obj_list, can we
> invoke the function explicitly through a dedicated command line, under
> the 'efidebug?capsule' class of commands.?I think that would be a
> cleaner approach, since efi_init_obj_list gets called for a lot of efi
> functions, which are unrelated to capsule update.

Who would invoke that command line on an IoT device?

My understanding of the UEFI spec is that capsule updates should be
invoked automatically.

I suggested to Takahiro to use the first EFI system partition that we
find when scanning the available block devices to identify the boot
device holding the capsules but he dismissed it as contradicting the
UEFI spec.

According to the UEFI 2.8 spec we have to first check BootNext and then
BootOrder to find the boot option with the highest priority (just like
the boot manager does). When analysing BootNext and BootOrder we have to
ignore entries pointing to devices that are not present. This gives us
the active boot entry.

On the device identified by the FilePathList field of the active boot
entry we look for the directory \EFI\UpdateCapsule.

The UEFI spec says it does not require to check for other EFI system
partitions. - This could mean it is not forbidden to check other EFI
system partitions for update capsules.

The problem with the UEFI spec is that it assumes that variables
BootNext and BootOrder exist. If they do not exist, the UEFI spec gives
no hint what to do.

One way to solve this is to populate BootOrder with all block devices.
This is exactly what my laptop does:

BootOrder: 0001,0000,0016,0017,0018,0019,001A,001B
Boot0000* Windows Boot Manager
Boot0001* debian
Boot0010  Setup
Boot0011  Boot Menu
Boot0012  Diagnostic Splash Screen
Boot0013  Diagnostics
Boot0014  Startup Interrupt Menu
Boot0015  Rescue and Recovery
Boot0016* USB CD
Boot0017* USB FDD
Boot0018* NVMe0
Boot0019* ATA HDD0
Boot001A* USB HDD
Boot001B* PCI LAN

Please, observe that this list contains entries USB CD, USB FDD, USB HDD
that aren't or even never were physically present on my laptop.

Another approach is just to wait until bootefi or bootm (for EFI FIT
images) is invoked. After loading the boot image but before starting it
we know the active boot device. This will reduce the code size because
we do not have to implement the logic of the boot manager to analyze
BootNext and BootOrder twice.

Best regards

Heinrich

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

* [PATCH 01/10] efi_loader: disk: add efi_disk_is_system_part()
  2020-04-27 23:54     ` AKASHI Takahiro
@ 2020-05-01  7:06       ` Heinrich Schuchardt
  2020-05-05 22:33         ` Heinrich Schuchardt
  0 siblings, 1 reply; 33+ messages in thread
From: Heinrich Schuchardt @ 2020-05-01  7:06 UTC (permalink / raw)
  To: u-boot

On 4/28/20 1:54 AM, AKASHI Takahiro wrote:
> Heinrich,
>
> On Mon, Apr 27, 2020 at 09:57:25PM +0200, Heinrich Schuchardt wrote:
>> On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
>>> This function will check if a given handle to device is a EFI system
>>> partition. It will be utilised in implementing capsule-on-disk feature.
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>
>> Just a reminder for me:
>>
>> This patch depends on
>> "part: detect EFI system partition"
>> https://lists.denx.de/pipermail/u-boot/2020-April/408150.html
>
> Yes, and I explicitly mentioned it in "prerequisite patches"
> of the cover letter.
> (I objected to the patch 2/2 though.)
>
>>> ---
>>>  include/efi_loader.h      |  2 ++
>>>  lib/efi_loader/efi_disk.c | 22 ++++++++++++++++++++++
>>>  2 files changed, 24 insertions(+)
>>>
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index b7bccf50b30c..d4510462d616 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -393,6 +393,8 @@ efi_status_t efi_disk_register(void);
>>>  int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
>>>  			       const char *if_typename, int diskid,
>>>  			       const char *pdevname);
>>> +/* Check if it is EFI system partition */
>>> +bool efi_disk_is_system_part(efi_handle_t handle);
>>>  /* Called by bootefi to make GOP (graphical) interface available */
>>>  efi_status_t efi_gop_register(void);
>>>  /* Called by bootefi to make the network interface available */
>>> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
>>> index fd3df80b0b96..9f58a8642c85 100644
>>> --- a/lib/efi_loader/efi_disk.c
>>> +++ b/lib/efi_loader/efi_disk.c
>>> @@ -588,3 +588,25 @@ efi_status_t efi_disk_register(void)
>>>
>>>  	return EFI_SUCCESS;
>>>  }
>>> +
>>
>> Please, provide a function description, cf.
>> https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation
>
> That is the thing that I've forgot to address in this version.
>
>>> +bool efi_disk_is_system_part(efi_handle_t handle)
>>> +{
>>> +	struct efi_handler *handler;
>>> +	struct efi_disk_obj *diskobj;
>>> +	disk_partition_t info;
>>> +	efi_status_t ret;
>>> +	int r;
>>> +
>>> +	/* check if this is a block device */
>>> +	ret = efi_search_protocol(handle, &efi_block_io_guid, &handler);
>>> +	if (ret != EFI_SUCCESS)
>>> +		return false;
>>> +
>>> +	diskobj = container_of(handle, struct efi_disk_obj, header);
>>
>> This is just a complicated way of saying:
>>
>> diskobj = (struct efi_disk_obj *)handle;
>>
>> I would rather avoid container_of() here though it is not wrong.
>
> My code doesn't rely on the fact that 'handle' is the first
> element of 'struct efi_disk_obj'

It is efi_delete_handle() that relies on handle pointing to the start of
the EFI objects.

> So use of container_of has no disadvantage.

The generated assembler code is the same.

>
> Thanks,
> -Takahiro Akashi
>
>
>> Best regards
>>
>> Heinrich
>>
>>> +
>>> +	r = part_get_info(diskobj->desc, diskobj->part, &info);
>>> +	if (r)
>>> +		return false;
>>> +
>>> +	return info.bootable & PART_EFI_SYSTEM_PARTITION;

As you decided that the return value shall be boolean you have to use:

return !!(info.bootable & PART_EFI_SYSTEM_PARTITION);

Best regards

Heinrich

>>> +}
>>>

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

* [PATCH 01/10] efi_loader: disk: add efi_disk_is_system_part()
  2020-05-01  7:06       ` Heinrich Schuchardt
@ 2020-05-05 22:33         ` Heinrich Schuchardt
  0 siblings, 0 replies; 33+ messages in thread
From: Heinrich Schuchardt @ 2020-05-05 22:33 UTC (permalink / raw)
  To: u-boot

On 5/1/20 9:06 AM, Heinrich Schuchardt wrote:
> On 4/28/20 1:54 AM, AKASHI Takahiro wrote:
>> Heinrich,
>>
>> On Mon, Apr 27, 2020 at 09:57:25PM +0200, Heinrich Schuchardt wrote:
>>> On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
>>>> This function will check if a given handle to device is a EFI system
>>>> partition. It will be utilised in implementing capsule-on-disk feature.
>>>>
>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>>
>>> Just a reminder for me:
>>>
>>> This patch depends on
>>> "part: detect EFI system partition"
>>> https://lists.denx.de/pipermail/u-boot/2020-April/408150.html
>>
>> Yes, and I explicitly mentioned it in "prerequisite patches"
>> of the cover letter.
>> (I objected to the patch 2/2 though.)
>>
>>>> ---
>>>>  include/efi_loader.h      |  2 ++
>>>>  lib/efi_loader/efi_disk.c | 22 ++++++++++++++++++++++
>>>>  2 files changed, 24 insertions(+)
>>>>
>>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>>> index b7bccf50b30c..d4510462d616 100644
>>>> --- a/include/efi_loader.h
>>>> +++ b/include/efi_loader.h
>>>> @@ -393,6 +393,8 @@ efi_status_t efi_disk_register(void);
>>>>  int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
>>>>  			       const char *if_typename, int diskid,
>>>>  			       const char *pdevname);
>>>> +/* Check if it is EFI system partition */
>>>> +bool efi_disk_is_system_part(efi_handle_t handle);
>>>>  /* Called by bootefi to make GOP (graphical) interface available */
>>>>  efi_status_t efi_gop_register(void);
>>>>  /* Called by bootefi to make the network interface available */
>>>> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
>>>> index fd3df80b0b96..9f58a8642c85 100644
>>>> --- a/lib/efi_loader/efi_disk.c
>>>> +++ b/lib/efi_loader/efi_disk.c
>>>> @@ -588,3 +588,25 @@ efi_status_t efi_disk_register(void)
>>>>
>>>>  	return EFI_SUCCESS;
>>>>  }
>>>> +
>>>
>>> Please, provide a function description, cf.
>>> https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation
>>
>> That is the thing that I've forgot to address in this version.
>>
>>>> +bool efi_disk_is_system_part(efi_handle_t handle)
>>>> +{
>>>> +	struct efi_handler *handler;
>>>> +	struct efi_disk_obj *diskobj;
>>>> +	disk_partition_t info;
>>>> +	efi_status_t ret;
>>>> +	int r;
>>>> +
>>>> +	/* check if this is a block device */
>>>> +	ret = efi_search_protocol(handle, &efi_block_io_guid, &handler);
>>>> +	if (ret != EFI_SUCCESS)
>>>> +		return false;
>>>> +
>>>> +	diskobj = container_of(handle, struct efi_disk_obj, header);
>>>
>>> This is just a complicated way of saying:
>>>
>>> diskobj = (struct efi_disk_obj *)handle;
>>>
>>> I would rather avoid container_of() here though it is not wrong.
>>
>> My code doesn't rely on the fact that 'handle' is the first
>> element of 'struct efi_disk_obj'
>
> It is efi_delete_handle() that relies on handle pointing to the start of
> the EFI objects.
>
>> So use of container_of has no disadvantage.
>
> The generated assembler code is the same.
>
>>
>> Thanks,
>> -Takahiro Akashi
>>
>>
>>> Best regards
>>>
>>> Heinrich
>>>
>>>> +
>>>> +	r = part_get_info(diskobj->desc, diskobj->part, &info);
>>>> +	if (r)
>>>> +		return false;
>>>> +
>>>> +	return info.bootable & PART_EFI_SYSTEM_PARTITION;
>
> As you decided that the return value shall be boolean you have to use:
>
> return !!(info.bootable & PART_EFI_SYSTEM_PARTITION);
>
> Best regards
>
> Heinrich
>
>>>> +}
>>>>
>

This patch is merged adding function description and adjusting return to
be bool. Thanks Heinrich

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

* [PATCH 08/10] cmd: add "efidebug capsule" command
  2020-04-30 12:38   ` Sughosh Ganu
@ 2020-05-07  1:58     ` AKASHI Takahiro
  0 siblings, 0 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-05-07  1:58 UTC (permalink / raw)
  To: u-boot

On Thu, Apr 30, 2020 at 06:08:11PM +0530, Sughosh Ganu wrote:
> On Mon, 27 Apr 2020 at 15:19, AKASHI Takahiro <takahiro.akashi@linaro.org>
> wrote:
> 
> > "efidebug capsule" is more or less a debugging utility.
> >   efidebug capsule update: invoke UpdateCapsule against data on memory
> >   efidebug capsule show: show a capsule header
> >   efidebug capsule result: dump a capsule result variable
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  cmd/efidebug.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 234 insertions(+)
> >
> > diff --git a/cmd/efidebug.c b/cmd/efidebug.c
> > index 02ef01969443..8956fa1d50be 100644
> > --- a/cmd/efidebug.c
> > +++ b/cmd/efidebug.c
> > @@ -19,6 +19,227 @@
> >  #define BS systab.boottime
> >  #define RT systab.runtime
> >
> > +#ifdef CONFIG_EFI_CAPSULE_UPDATE
> >
> 
> This config option is no longer used in the non-rfc series. I think this
> needs to be CONFIG_EFI_HAVE_CAPSULE_SUPPORT.

Correct.

Thanks,
-Takahiro Akashi


> -sughosh

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

* [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support
  2020-04-30 19:51         ` Heinrich Schuchardt
@ 2020-05-07  2:50           ` AKASHI Takahiro
  2020-05-07 12:05           ` Sughosh Ganu
  1 sibling, 0 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-05-07  2:50 UTC (permalink / raw)
  To: u-boot

On Thu, Apr 30, 2020 at 09:51:51PM +0200, Heinrich Schuchardt wrote:
> On 4/30/20 2:52 PM, Sughosh Ganu wrote:
> >
> > On Tue, 28 Apr 2020 at 05:58, AKASHI Takahiro
> > <takahiro.akashi at linaro.org <mailto:takahiro.akashi@linaro.org>> wrote:
> >
> >     Heinrich,
> >
> >     On Mon, Apr 27, 2020 at 10:28:35PM +0200, Heinrich Schuchardt wrote:
> >     > On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> >     > > Capsule data can be loaded into the system either via UpdateCapsule
> >     > > runtime service or files on a file system (of boot device).
> >     > > The latter case is called "capsules on disk", and actual updates
> >     will
> >     > > take place at the next boot time.
> >     > >
> >     > > In this commit, we will support capsule on disk mechanism.
> >     > >
> >     > > Please note that U-Boot itself has no notion of "boot device" and
> >     > > all the capsule files to be executed will be detected only if they
> >     > > are located in a specific directory, \EFI\UpdateCapsule, on a device
> >     > > that is identified as a boot device by "BootXXXX" variables.
> >     > >
> >     > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org
> >     <mailto:takahiro.akashi@linaro.org>>
> >     > > ---
> >     > >? common/main.c? ? ? ? ? ? ? ? |? ?4 +
> >     > >? include/efi_loader.h? ? ? ? ?|? 16 ++
> >     > >? lib/efi_loader/Kconfig? ? ? ?|? 22 ++
> >     > >? lib/efi_loader/efi_capsule.c | 449
> >     +++++++++++++++++++++++++++++++++++
> >     > >? lib/efi_loader/efi_setup.c? ?|? ?9 +
> >     > >? 5 files changed, 500 insertions(+)
> >     > >
> >     > > diff --git a/common/main.c b/common/main.c
> >     > > index 06d7ff56d60c..877ae63b708d 100644
> >     > > --- a/common/main.c
> >     > > +++ b/common/main.c
> >     > > @@ -14,6 +14,7 @@
> >     > >? #include <env.h>
> >     > >? #include <init.h>
> >     > >? #include <version.h>
> >     > > +#include <efi_loader.h>
> >     > >
> >     > >? static void run_preboot_environment_command(void)
> >     > >? {
> >     > > @@ -51,6 +52,9 @@ void main_loop(void)
> >     > >? ? ?if (IS_ENABLED(CONFIG_UPDATE_TFTP))
> >     > >? ? ? ? ? ? ?update_tftp(0UL, NULL, NULL);
> >     > >
> >     > > +? ?if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
> >     > > +? ? ? ? ? ?efi_launch_capsules();
> >     > > +
> >     >
> >     > Can't we move this to efi_init_obj_list() and do away with
> >     > CONFIG_EFI_CAPSULE_ON_DISK_EARLY?
> >
> >     With CONFIG_EFI_CAPSULE_ON_DISK_EARLY disabled,
> >     efi_launch_capsules() will be called in efi_init_obj_list()
> >     as you expect. See the code below in efi_setup.c.
> >
> >
> > Instead of calling efi_launch_capsules in efi_init_obj_list, can we
> > invoke the function explicitly through a dedicated command line, under
> > the 'efidebug?capsule' class of commands.?I think that would be a
> > cleaner approach, since efi_init_obj_list gets called for a lot of efi
> > functions, which are unrelated to capsule update.
> 
> Who would invoke that command line on an IoT device?
> 
> My understanding of the UEFI spec is that capsule updates should be
> invoked automatically.

Right. We must ensure that capsule updates immediately must happen
after reboot.

> I suggested to Takahiro to use the first EFI system partition that we
> find when scanning the available block devices to identify the boot
> device holding the capsules but he dismissed it as contradicting the
> UEFI spec.

Yeah ...

> According to the UEFI 2.8 spec we have to first check BootNext and then
> BootOrder to find the boot option with the highest priority (just like
> the boot manager does). When analysing BootNext and BootOrder we have to
> ignore entries pointing to devices that are not present. This gives us
> the active boot entry.
> 
> On the device identified by the FilePathList field of the active boot
> entry we look for the directory \EFI\UpdateCapsule.
> 
> The UEFI spec says it does not require to check for other EFI system
> partitions. - This could mean it is not forbidden to check other EFI
> system partitions for update capsules.
> 
> The problem with the UEFI spec is that it assumes that variables
> BootNext and BootOrder exist. If they do not exist, the UEFI spec gives
> no hint what to do.

Thank you for detailed explanation instead of me!
The UEFI specification sounds a bit odd, but I can't read it
differently than my interpretation.

> One way to solve this is to populate BootOrder with all block devices.
> This is exactly what my laptop does:
> 
> BootOrder: 0001,0000,0016,0017,0018,0019,001A,001B
> Boot0000* Windows Boot Manager
> Boot0001* debian
> Boot0010  Setup
> Boot0011  Boot Menu
> Boot0012  Diagnostic Splash Screen
> Boot0013  Diagnostics
> Boot0014  Startup Interrupt Menu
> Boot0015  Rescue and Recovery
> Boot0016* USB CD
> Boot0017* USB FDD
> Boot0018* NVMe0
> Boot0019* ATA HDD0
> Boot001A* USB HDD
> Boot001B* PCI LAN
> 
> Please, observe that this list contains entries USB CD, USB FDD, USB HDD
> that aren't or even never were physically present on my laptop.
> 
> Another approach is just to wait until bootefi or bootm (for EFI FIT
> images) is invoked. After loading the boot image but before starting it
> we know the active boot device. This will reduce the code size because
> we do not have to implement the logic of the boot manager to analyze
> BootNext and BootOrder twice.

I didn't take this approach because firmware update may affect
not only UEFI subsystem but also other U-Boot functionality.
This is why "capsule updates must happen immediately after reboot."

Thanks,
-Takahiro Akashi

> Best regards
> 
> Heinrich

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

* [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support
  2020-04-30 19:51         ` Heinrich Schuchardt
  2020-05-07  2:50           ` AKASHI Takahiro
@ 2020-05-07 12:05           ` Sughosh Ganu
  1 sibling, 0 replies; 33+ messages in thread
From: Sughosh Ganu @ 2020-05-07 12:05 UTC (permalink / raw)
  To: u-boot

On Fri, 1 May 2020 at 01:22, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:

> On 4/30/20 2:52 PM, Sughosh Ganu wrote:
> >
> > On Tue, 28 Apr 2020 at 05:58, AKASHI Takahiro
> > <takahiro.akashi at linaro.org <mailto:takahiro.akashi@linaro.org>> wrote:
> >
> >     Heinrich,
> >
> >     On Mon, Apr 27, 2020 at 10:28:35PM +0200, Heinrich Schuchardt wrote:
> >     > On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> >     > > Capsule data can be loaded into the system either via
> UpdateCapsule
> >     > > runtime service or files on a file system (of boot device).
> >     > > The latter case is called "capsules on disk", and actual updates
> >     will
> >     > > take place at the next boot time.
> >     > >
> >     > > In this commit, we will support capsule on disk mechanism.
> >     > >
> >     > > Please note that U-Boot itself has no notion of "boot device" and
> >     > > all the capsule files to be executed will be detected only if
> they
> >     > > are located in a specific directory, \EFI\UpdateCapsule, on a
> device
> >     > > that is identified as a boot device by "BootXXXX" variables.
> >     > >
> >     > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org
> >     <mailto:takahiro.akashi@linaro.org>>
> >     > > ---
> >     > >  common/main.c                |   4 +
> >     > >  include/efi_loader.h         |  16 ++
> >     > >  lib/efi_loader/Kconfig       |  22 ++
> >     > >  lib/efi_loader/efi_capsule.c | 449
> >     +++++++++++++++++++++++++++++++++++
> >     > >  lib/efi_loader/efi_setup.c   |   9 +
> >     > >  5 files changed, 500 insertions(+)
> >     > >
> >     > > diff --git a/common/main.c b/common/main.c
> >     > > index 06d7ff56d60c..877ae63b708d 100644
> >     > > --- a/common/main.c
> >     > > +++ b/common/main.c
> >     > > @@ -14,6 +14,7 @@
> >     > >  #include <env.h>
> >     > >  #include <init.h>
> >     > >  #include <version.h>
> >     > > +#include <efi_loader.h>
> >     > >
> >     > >  static void run_preboot_environment_command(void)
> >     > >  {
> >     > > @@ -51,6 +52,9 @@ void main_loop(void)
> >     > >     if (IS_ENABLED(CONFIG_UPDATE_TFTP))
> >     > >             update_tftp(0UL, NULL, NULL);
> >     > >
> >     > > +   if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
> >     > > +           efi_launch_capsules();
> >     > > +
> >     >
> >     > Can't we move this to efi_init_obj_list() and do away with
> >     > CONFIG_EFI_CAPSULE_ON_DISK_EARLY?
> >
> >     With CONFIG_EFI_CAPSULE_ON_DISK_EARLY disabled,
> >     efi_launch_capsules() will be called in efi_init_obj_list()
> >     as you expect. See the code below in efi_setup.c.
> >
> >
> > Instead of calling efi_launch_capsules in efi_init_obj_list, can we
> > invoke the function explicitly through a dedicated command line, under
> > the 'efidebug capsule' class of commands. I think that would be a
> > cleaner approach, since efi_init_obj_list gets called for a lot of efi
> > functions, which are unrelated to capsule update.
>
> Who would invoke that command line on an IoT device?
>

Understand your point, but the 'efidebug capsule' class of commands are
anyways going to be used for debug purpose -- there is also an 'efidebug
capsule update' command that is being added as part of this patch series.
My point was that it is better to call the capsule update explicitly rather
than through efi_init_obj_list, which also gets called for all types of
unrelated commands like printenv -e.


> My understanding of the UEFI spec is that capsule updates should be
> invoked automatically.
>

Right, and in that case the capsule update should be invoked automatically
at some point during the boot. Takahiro has added a patch which invokes the
capsule update as part of the main_loop. In that case, why should it be
invoked from efi_init_obj_list. What is the scenario where the capsule
update is required to be invoked as part of the efi_init_obj_list function.

-sughosh

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

* [PATCH 02/10] efi_loader: add option to initialise EFI subsystem early
  2020-04-28  0:16     ` AKASHI Takahiro
@ 2020-05-17  7:29       ` Heinrich Schuchardt
  2020-05-18  1:43         ` AKASHI Takahiro
  0 siblings, 1 reply; 33+ messages in thread
From: Heinrich Schuchardt @ 2020-05-17  7:29 UTC (permalink / raw)
  To: u-boot

On 4/28/20 2:16 AM, AKASHI Takahiro wrote:
> Heinrich,
>
> On Mon, Apr 27, 2020 at 10:09:11PM +0200, Heinrich Schuchardt wrote:
>> On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
>>> If this option is enabled, the initialisation of UEFI subsystem will be
>>> done as part of U-Boot initialisation.
>>>
>>> This feature will be utilised in implementing capsule-on-disk feature.
>>
>> This would mean that we allow unaligned access very early. Something
>> Siarhei was against:
>
> ?
> Even with CONFIG_EFI_CAPSULE_ON_DISK_EARLY enabled,
> efi_init_obj_list() is called at the last of "init" list
> and efi_launch_capsules() is called just before the main
> command loop.
> So "unalignment" issue won't happen.

efi_init_obj_list() is even called when booting via booti and therefore
before a lot of other code.

Best regards

Heinrich

>
>> https://lists.denx.de/pipermail/u-boot/2018-March/324242.html
>> https://patchwork.ozlabs.org/project/uboot/patch/20180329213350.7868-1-xypron.glpk at gmx.de/
>>
>> Why can't you wait with the capsule update until any command initializes
>> the UEFI sub-system.
>
> This topic is the one the I mentioned in RFC's cover letter
> and asked you for comments several time.
> Anyway, there are a couple of reasons:
> 1. Updated firmware may have some effects on not only UEFI
>    subsystem but also U-Boot's other features.
> 2. Firmware update should surely take place after reboot
>    as UEFI specification expects.
> 3. Firmware update should not rely on user's interactions
>    or whatever "bootcmd" is set to.
> 4. In case of failure of firmware update, some recovery should
>    be automatically taken "before" the command line is handed over
>    to users. (The feature is not implemented yet though.)
>
> -Takahiro Akashi
>
>
>> Best regards
>>
>> Heinrich
>>
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>  common/board_r.c       | 6 ++++++
>>>  lib/efi_loader/Kconfig | 4 ++++
>>>  2 files changed, 10 insertions(+)
>>>
>>> diff --git a/common/board_r.c b/common/board_r.c
>>> index 0bbeaa7594c6..7cf21a6078f9 100644
>>> --- a/common/board_r.c
>>> +++ b/common/board_r.c
>>> @@ -64,6 +64,9 @@
>>>  #if defined(CONFIG_GPIO_HOG)
>>>  #include <asm/gpio.h>
>>>  #endif
>>> +#ifdef CONFIG_EFI_SETUP_EARLY
>>> +#include <efi_loader.h>
>>> +#endif
>>>
>>>  DECLARE_GLOBAL_DATA_PTR;
>>>
>>> @@ -867,6 +870,9 @@ static init_fnc_t init_sequence_r[] = {
>>>  #endif
>>>  #if defined(CONFIG_M68K) && defined(CONFIG_BLOCK_CACHE)
>>>  	blkcache_init,
>>> +#endif
>>> +#ifdef CONFIG_EFI_SETUP_EARLY
>>> +	(init_fnc_t)efi_init_obj_list,
>>>  #endif
>>>  	run_main_loop,
>>>  };
>>> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
>>> index 1cfa24ffcf72..7cc2d940f848 100644
>>> --- a/lib/efi_loader/Kconfig
>>> +++ b/lib/efi_loader/Kconfig
>>> @@ -25,6 +25,10 @@ config EFI_LOADER
>>>
>>>  if EFI_LOADER
>>>
>>> +config EFI_SETUP_EARLY
>>> +	bool
>>> +	default n
>>> +
>>>  config EFI_GET_TIME
>>>  	bool "GetTime() runtime service"
>>>  	depends on DM_RTC
>>>
>>

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

* [PATCH 03/10] efi_loader: define UpdateCapsule api
  2020-04-27  9:48 ` [PATCH 03/10] efi_loader: define UpdateCapsule api AKASHI Takahiro
@ 2020-05-17  8:02   ` Heinrich Schuchardt
  2020-05-18  1:34     ` AKASHI Takahiro
  0 siblings, 1 reply; 33+ messages in thread
From: Heinrich Schuchardt @ 2020-05-17  8:02 UTC (permalink / raw)
  To: u-boot

On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> In this commit, skeleton functions for capsule-related API's are
> added under CONFIG_EFI_UPDATE_CAPSULE configuration.
> Detailed implementation for a specific capsule type will be added
> in the succeeding patches.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  include/efi_api.h            |  12 +++
>  include/efi_loader.h         |  15 ++++
>  lib/efi_loader/Kconfig       |  11 +++
>  lib/efi_loader/Makefile      |   1 +
>  lib/efi_loader/efi_capsule.c | 153 +++++++++++++++++++++++++++++++++++
>  lib/efi_loader/efi_runtime.c | 104 ++++++++++++++----------
>  lib/efi_loader/efi_setup.c   |  31 +++++--
>  7 files changed, 276 insertions(+), 51 deletions(-)
>  create mode 100644 lib/efi_loader/efi_capsule.c
>
> diff --git a/include/efi_api.h b/include/efi_api.h
> index 77d6bf2660b9..6fa3f4a887d2 100644
> --- a/include/efi_api.h
> +++ b/include/efi_api.h
> @@ -217,6 +217,10 @@ enum efi_reset_type {
>  #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE	0x00020000
>  #define CAPSULE_FLAGS_INITIATE_RESET		0x00040000
>
> +#define EFI_CAPSULE_REPORT_GUID \
> +	EFI_GUID(0x39b68c46, 0xf7fb, 0x441b, 0xb6, 0xec, \
> +		 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3)
> +
>  struct efi_capsule_header {
>  	efi_guid_t capsule_guid;
>  	u32 header_size;
> @@ -224,6 +228,14 @@ struct efi_capsule_header {
>  	u32 capsule_image_size;
>  } __packed;
>
> +struct efi_capsule_result_variable_header {
> +	u32 variable_total_size;
> +	u32 reserved;
> +	efi_guid_t capsule_guid;
> +	struct efi_time capsule_processed;
> +	efi_status_t capsule_status;
> +} __packed;
> +
>  #define EFI_RT_SUPPORTED_GET_TIME			0x0001
>  #define EFI_RT_SUPPORTED_SET_TIME			0x0002
>  #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME		0x0004
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index d4510462d616..19ffc027c171 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -196,6 +196,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7;
>
>  /* GUID of RNG protocol */
>  extern const efi_guid_t efi_guid_rng_protocol;
> +/* GUID of capsule update result */
> +extern const efi_guid_t efi_guid_capsule_report;
>
>  extern unsigned int __efi_runtime_start, __efi_runtime_stop;
>  extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
> @@ -778,6 +780,19 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
>  		     WIN_CERTIFICATE **auth, size_t *auth_len);
>  #endif /* CONFIG_EFI_SECURE_BOOT */

This line does not exist in upstream. Instead we have:

bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
????????????????     WIN_CERTIFICATE **auth, size_t *auth_len);

#else /* CONFIG_IS_ENABLED(EFI_LOADER) */

Am I missing patches? Or do you have to rebase your series?

>
> +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
> +/* Capsule update */
> +efi_status_t EFIAPI efi_update_capsule(
> +		struct efi_capsule_header **capsule_header_array,
> +		efi_uintn_t capsule_count,
> +		u64 scatter_gather_list);
> +efi_status_t EFIAPI efi_query_capsule_caps(
> +	 efi_capsule_header **capsule_header_array,
> +		efi_uintn_t capsule_count,
> +		u64 *maximum_capsule_size,
> +		u32 *reset_type);
> +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */
> +
>  #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
>
>  /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 7cc2d940f848..e2b08251f26a 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -45,6 +45,17 @@ config EFI_SET_TIME
>  	  Provide the SetTime() runtime service at boottime. This service
>  	  can be used by an EFI application to adjust the real time clock.
>
> +config EFI_HAVE_CAPSULE_SUPPORT
> +	bool
> +
> +config EFI_RUNTIME_UPDATE_CAPSULE
> +	bool "UpdateCapsule() runtime service"
> +	default n
> +	select EFI_HAVE_CAPSULE_SUPPORT
> +	help
> +	  Select this option if you want to use UpdateCapsule and
> +	  QueryCapsuleCapabilities API's.
> +
>  config EFI_DEVICE_PATH_TO_TEXT
>  	bool "Device path to text protocol"
>  	default y
> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> index eff3c25ec301..2ee1e683d9c5 100644
> --- a/lib/efi_loader/Makefile
> +++ b/lib/efi_loader/Makefile
> @@ -22,6 +22,7 @@ endif
>  obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
>  obj-y += efi_bootmgr.o
>  obj-y += efi_boottime.o
> +obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
>  obj-y += efi_console.o
>  obj-y += efi_device_path.o
>  obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> new file mode 100644
> index 000000000000..fb104bb92a6c
> --- /dev/null
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -0,0 +1,153 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + *  EFI Capsule
> + *
> + *  Copyright (c) 2018 Linaro Limited
> + *			Author: AKASHI Takahiro
> + */
> +
> +#include <common.h>
> +#include <efi_loader.h>
> +#include <fs.h>
> +#include <malloc.h>
> +#include <sort.h>
> +
> +const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
> +
> +static __maybe_unused int get_last_capsule(void)
> +{
> +	u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
> +	char value[11], *p;
> +	efi_uintn_t size;
> +	unsigned long num = 0xffff;
> +	efi_status_t ret;
> +
> +	size = sizeof(value16);
> +	ret = EFI_CALL(efi_get_variable(L"CapsuleLast",
> +					&efi_guid_capsule_report,
> +					NULL, &size, value16));
> +	if (ret != EFI_SUCCESS || u16_strncmp(value16, L"Capsule", 7))
> +		goto err;
> +
> +	p = value;
> +	utf16_utf8_strcpy(&p, value16);
> +	strict_strtoul(&value[7], 16, &num);
> +err:
> +	return (int)num;
> +}
> +

Missing function description

> +static __maybe_unused
> +void set_capsule_result(int num, struct efi_capsule_header *capsule,
> +			efi_status_t return_status)
> +{
> +	char variable_name[12];
> +	u16 variable_name16[12], *p;
> +	struct efi_capsule_result_variable_header result;
> +	struct efi_time time;
> +	efi_status_t ret;
> +
> +	sprintf(variable_name, "Capsule%04X", num);
> +	p = variable_name16;
> +	utf8_utf16_strncpy(&p, variable_name, 11);
> +	result.variable_total_size = sizeof(result);
> +	result.capsule_guid = capsule->capsule_guid;
> +	ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL));
> +	if (ret == EFI_SUCCESS)
> +		memcpy(&result.capsule_processed, &time, sizeof(time));
> +	else
> +		memset(&result.capsule_processed, 0, sizeof(time));
> +	result.capsule_status = return_status;
> +	ret = EFI_CALL(efi_set_variable(variable_name16,
> +					&efi_guid_capsule_report,
> +					EFI_VARIABLE_NON_VOLATILE |
> +					EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +					EFI_VARIABLE_RUNTIME_ACCESS,
> +					sizeof(result), &result));
> +	if (ret)
> +		printf("EFI: creating %s failed\n", variable_name);
> +}
> +
> +/*
> + * Launch a capsule

Please, stick to the style described in
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation

> + */
> +/**
> + * efi_update_capsule() - process information from operating system
> + *
> + * This function implements the UpdateCapsule() runtime service.
> + *
> + * See the Unified Extensible Firmware Interface (UEFI) specification for
> + * details.
> + *
> + * @capsule_header_array:	pointer to array of virtual pointers
> + * @capsule_count:		number of pointers in capsule_header_array
> + * @scatter_gather_list:	pointer to arry of physical pointers
> + * Return:			status code
> + */
> +efi_status_t EFIAPI efi_update_capsule(
> +		struct efi_capsule_header **capsule_header_array,
> +		efi_uintn_t capsule_count,
> +		u64 scatter_gather_list)
> +{
> +	struct efi_capsule_header *capsule;
> +	unsigned int i;
> +	efi_status_t ret;
> +
> +	EFI_ENTRY("%p, %lu, %llu\n", capsule_header_array, capsule_count,
> +		  scatter_gather_list);
> +
> +	if (!capsule_count) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	ret = EFI_SUCCESS;
> +	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
> +	     i++, capsule = *(++capsule_header_array)) {
> +	}
> +out:
> +	return EFI_EXIT(ret);
> +}
> +
> +/**
> + * efi_query_capsule_caps() - check if capsule is supported
> + *
> + * This function implements the QueryCapsuleCapabilities() runtime service.
> + *
> + * See the Unified Extensible Firmware Interface (UEFI) specification for
> + * details.
> + *
> + * @capsule_header_array:	pointer to array of virtual pointers
> + * @capsule_count:		number of pointers in capsule_header_array
> + * @maximum_capsule_size:	maximum capsule size
> + * @reset_type:			type of reset needed for capsule update
> + * Return:			status code
> + */
> +efi_status_t EFIAPI efi_query_capsule_caps(
> +		struct efi_capsule_header **capsule_header_array,
> +		efi_uintn_t capsule_count,
> +		u64 *maximum_capsule_size,
> +		u32 *reset_type)
> +{
> +	struct efi_capsule_header *capsule __attribute__((unused));
> +	unsigned int i;
> +	efi_status_t ret;
> +
> +	EFI_ENTRY("%p, %lu, %p, %p\n", capsule_header_array, capsule_count,
> +		  maximum_capsule_size, reset_type);
> +
> +	if (!maximum_capsule_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	*maximum_capsule_size = U64_MAX;
> +	*reset_type = EFI_RESET_COLD;
> +
> +	ret = EFI_SUCCESS;
> +	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
> +	     i++, capsule = *(++capsule_header_array)) {
> +		/* TODO */
> +	}
> +out:
> +	return EFI_EXIT(ret);
> +}
> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> index 6a25acbbcdf5..669e25a9589a 100644
> --- a/lib/efi_loader/efi_runtime.c
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -130,6 +130,11 @@ efi_status_t efi_init_runtime_supported(void)
>  #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
>  	rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
>  #endif
> +#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
> +	rt_table->runtime_services_supported |=
> +			(EFI_RT_SUPPORTED_UPDATE_CAPSULE |
> +			 EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES);
> +#endif
>
>  	ret = efi_install_configuration_table(&efi_rt_properties_table_guid,
>  					      rt_table);
> @@ -410,6 +415,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
>  	return EFI_UNSUPPORTED;
>  }
>
> +/**
> + * efi_update_capsule_unsupported() - process information from operating system
> + *
> + * This function implements the UpdateCapsule() runtime service.
> + *
> + * See the Unified Extensible Firmware Interface (UEFI) specification for
> + * details.

The description should differ from the description of efi_update_capsule().

> + *
> + * @capsule_header_array:	pointer to array of virtual pointers
> + * @capsule_count:		number of pointers in capsule_header_array
> + * @scatter_gather_list:	pointer to arry of physical pointers
> + * Returns:			status code
> + */
> +efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported(
> +			struct efi_capsule_header **capsule_header_array,
> +			efi_uintn_t capsule_count,
> +			u64 scatter_gather_list)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +/**
> + * efi_query_capsule_caps_unsupported() - check if capsule is supported
> + *
> + * This function implements the QueryCapsuleCapabilities() runtime service.
> + *
> + * See the Unified Extensible Firmware Interface (UEFI) specification for
> + * details.

The description should differ from the description of
efi_query_capsule_caps().

> + *
> + * @capsule_header_array:	pointer to array of virtual pointers
> + * @capsule_count:		number of pointers in capsule_header_array
> + * @maximum_capsule_size:	maximum capsule size
> + * @reset_type:			type of reset needed for capsule update
> + * Returns:			status code
> + */
> +efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps_unsupported(
> +			struct efi_capsule_header **capsule_header_array,
> +			efi_uintn_t capsule_count,
> +			u64 *maximum_capsule_size,
> +			u32 *reset_type)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
>  /**
>   * efi_is_runtime_service_pointer() - check if pointer points to runtime table
>   *
> @@ -433,6 +482,12 @@ void efi_runtime_detach(void)
>  	efi_runtime_services.reset_system = efi_reset_system;
>  	efi_runtime_services.get_time = efi_get_time;
>  	efi_runtime_services.set_time = efi_set_time;
> +#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
> +	/* won't support at runtime */
> +	efi_runtime_services.update_capsule = efi_update_capsule_unsupported;
> +	efi_runtime_services.query_capsule_caps =
> +					efi_query_capsule_caps_unsupported;
> +#endif
>
>  	/* Update CRC32 */
>  	efi_update_table_header_crc32(&efi_runtime_services.hdr);
> @@ -837,50 +892,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
>  	return EFI_UNSUPPORTED;
>  }
>
> -/**
> - * efi_update_capsule() - process information from operating system
> - *
> - * This function implements the UpdateCapsule() runtime service.
> - *
> - * See the Unified Extensible Firmware Interface (UEFI) specification for
> - * details.
> - *
> - * @capsule_header_array:	pointer to array of virtual pointers
> - * @capsule_count:		number of pointers in capsule_header_array
> - * @scatter_gather_list:	pointer to arry of physical pointers
> - * Returns:			status code
> - */
> -efi_status_t __efi_runtime EFIAPI efi_update_capsule(
> -			struct efi_capsule_header **capsule_header_array,
> -			efi_uintn_t capsule_count,
> -			u64 scatter_gather_list)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -/**
> - * efi_query_capsule_caps() - check if capsule is supported
> - *
> - * This function implements the QueryCapsuleCapabilities() runtime service.
> - *
> - * See the Unified Extensible Firmware Interface (UEFI) specification for
> - * details.
> - *
> - * @capsule_header_array:	pointer to array of virtual pointers
> - * @capsule_count:		number of pointers in capsule_header_array
> - * @maximum_capsule_size:	maximum capsule size
> - * @reset_type:			type of reset needed for capsule update
> - * Returns:			status code
> - */
> -efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps(
> -			struct efi_capsule_header **capsule_header_array,
> -			efi_uintn_t capsule_count,
> -			u64 *maximum_capsule_size,
> -			u32 *reset_type)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
>  struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
>  	.hdr = {
>  		.signature = EFI_RUNTIME_SERVICES_SIGNATURE,
> @@ -898,7 +909,12 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
>  	.set_variable = efi_set_variable,
>  	.get_next_high_mono_count = (void *)&efi_unimplemented,
>  	.reset_system = &efi_reset_system_boottime,
> +#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
>  	.update_capsule = efi_update_capsule,
>  	.query_capsule_caps = efi_query_capsule_caps,
> +#else
> +	.update_capsule = efi_update_capsule_unsupported,
> +	.query_capsule_caps = efi_query_capsule_caps_unsupported,
> +#endif
>  	.query_variable_info = efi_query_variable_info,
>  };
> diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
> index 1b648c84673a..8fe378bbfdfc 100644
> --- a/lib/efi_loader/efi_setup.c
> +++ b/lib/efi_loader/efi_setup.c
> @@ -115,6 +115,29 @@ static efi_status_t efi_init_secure_boot(void)
>  }
>  #endif /* CONFIG_EFI_SECURE_BOOT */
>
> +/**
> + * efi_init_os_indications() - indicate supported features for OS requests
> + *
> + * Set the OsIndicationsSupported variable.
> + *
> + * Return:	status code
> + */
> +static efi_status_t efi_init_os_indications(void)
> +{
> +	u64 os_indications_supported = 0;
> +
> +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
> +	os_indications_supported |=
> +			EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
> +#endif

Though the UEFI spec does not mention it I guess we will have to treat
OsIndicationsSupported as read-only once we get that properly supported.

Best regards

Heinrich

> +	return EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
> +					 &efi_global_variable_guid,
> +					 EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +					 EFI_VARIABLE_RUNTIME_ACCESS,
> +					 sizeof(os_indications_supported),
> +					 &os_indications_supported));
> +}
> +
>  /**
>   * efi_init_obj_list() - Initialize and populate EFI object list
>   *
> @@ -122,7 +145,6 @@ static efi_status_t efi_init_secure_boot(void)
>   */
>  efi_status_t efi_init_obj_list(void)
>  {
> -	u64 os_indications_supported = 0; /* None */
>  	efi_status_t ret = EFI_SUCCESS;
>
>  	/* Initialize once only */
> @@ -146,12 +168,7 @@ efi_status_t efi_init_obj_list(void)
>  		goto out;
>
>  	/* Indicate supported features */
> -	ret = EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
> -					&efi_global_variable_guid,
> -					EFI_VARIABLE_BOOTSERVICE_ACCESS |
> -					EFI_VARIABLE_RUNTIME_ACCESS,
> -					sizeof(os_indications_supported),
> -					&os_indications_supported));
> +	ret = efi_init_os_indications();
>  	if (ret != EFI_SUCCESS)
>  		goto out;
>
>

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

* [PATCH 03/10] efi_loader: define UpdateCapsule api
  2020-05-17  8:02   ` Heinrich Schuchardt
@ 2020-05-18  1:34     ` AKASHI Takahiro
  0 siblings, 0 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-05-18  1:34 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Sun, May 17, 2020 at 10:02:08AM +0200, Heinrich Schuchardt wrote:
> On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> > In this commit, skeleton functions for capsule-related API's are
> > added under CONFIG_EFI_UPDATE_CAPSULE configuration.
> > Detailed implementation for a specific capsule type will be added
> > in the succeeding patches.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  include/efi_api.h            |  12 +++
> >  include/efi_loader.h         |  15 ++++
> >  lib/efi_loader/Kconfig       |  11 +++
> >  lib/efi_loader/Makefile      |   1 +
> >  lib/efi_loader/efi_capsule.c | 153 +++++++++++++++++++++++++++++++++++
> >  lib/efi_loader/efi_runtime.c | 104 ++++++++++++++----------
> >  lib/efi_loader/efi_setup.c   |  31 +++++--
> >  7 files changed, 276 insertions(+), 51 deletions(-)
> >  create mode 100644 lib/efi_loader/efi_capsule.c
> >
> > diff --git a/include/efi_api.h b/include/efi_api.h
> > index 77d6bf2660b9..6fa3f4a887d2 100644
> > --- a/include/efi_api.h
> > +++ b/include/efi_api.h
> > @@ -217,6 +217,10 @@ enum efi_reset_type {
> >  #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE	0x00020000
> >  #define CAPSULE_FLAGS_INITIATE_RESET		0x00040000
> >
> > +#define EFI_CAPSULE_REPORT_GUID \
> > +	EFI_GUID(0x39b68c46, 0xf7fb, 0x441b, 0xb6, 0xec, \
> > +		 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3)
> > +
> >  struct efi_capsule_header {
> >  	efi_guid_t capsule_guid;
> >  	u32 header_size;
> > @@ -224,6 +228,14 @@ struct efi_capsule_header {
> >  	u32 capsule_image_size;
> >  } __packed;
> >
> > +struct efi_capsule_result_variable_header {
> > +	u32 variable_total_size;
> > +	u32 reserved;
> > +	efi_guid_t capsule_guid;
> > +	struct efi_time capsule_processed;
> > +	efi_status_t capsule_status;
> > +} __packed;
> > +
> >  #define EFI_RT_SUPPORTED_GET_TIME			0x0001
> >  #define EFI_RT_SUPPORTED_SET_TIME			0x0002
> >  #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME		0x0004
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index d4510462d616..19ffc027c171 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -196,6 +196,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7;
> >
> >  /* GUID of RNG protocol */
> >  extern const efi_guid_t efi_guid_rng_protocol;
> > +/* GUID of capsule update result */
> > +extern const efi_guid_t efi_guid_capsule_report;
> >
> >  extern unsigned int __efi_runtime_start, __efi_runtime_stop;
> >  extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
> > @@ -778,6 +780,19 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
> >  		     WIN_CERTIFICATE **auth, size_t *auth_len);
> >  #endif /* CONFIG_EFI_SECURE_BOOT */
> 
> This line does not exist in upstream. Instead we have:
> 
> bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
> ????????????????     WIN_CERTIFICATE **auth, size_t *auth_len);
> 
> #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
> 
> Am I missing patches? Or do you have to rebase your series?

The lines above come from my secure boot patch, while my capsule
patch is based on pre-v2020.07-rc1 in which my secure boot patch
has not been merged when I posted capsule patch.

> >
> > +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
> > +/* Capsule update */
> > +efi_status_t EFIAPI efi_update_capsule(
> > +		struct efi_capsule_header **capsule_header_array,
> > +		efi_uintn_t capsule_count,
> > +		u64 scatter_gather_list);
> > +efi_status_t EFIAPI efi_query_capsule_caps(
> > +	 efi_capsule_header **capsule_header_array,
> > +		efi_uintn_t capsule_count,
> > +		u64 *maximum_capsule_size,
> > +		u32 *reset_type);
> > +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */
> > +
> >  #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
> >
> >  /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
> > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > index 7cc2d940f848..e2b08251f26a 100644
> > --- a/lib/efi_loader/Kconfig
> > +++ b/lib/efi_loader/Kconfig
> > @@ -45,6 +45,17 @@ config EFI_SET_TIME
> >  	  Provide the SetTime() runtime service at boottime. This service
> >  	  can be used by an EFI application to adjust the real time clock.
> >
> > +config EFI_HAVE_CAPSULE_SUPPORT
> > +	bool
> > +
> > +config EFI_RUNTIME_UPDATE_CAPSULE
> > +	bool "UpdateCapsule() runtime service"
> > +	default n
> > +	select EFI_HAVE_CAPSULE_SUPPORT
> > +	help
> > +	  Select this option if you want to use UpdateCapsule and
> > +	  QueryCapsuleCapabilities API's.
> > +
> >  config EFI_DEVICE_PATH_TO_TEXT
> >  	bool "Device path to text protocol"
> >  	default y
> > diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> > index eff3c25ec301..2ee1e683d9c5 100644
> > --- a/lib/efi_loader/Makefile
> > +++ b/lib/efi_loader/Makefile
> > @@ -22,6 +22,7 @@ endif
> >  obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
> >  obj-y += efi_bootmgr.o
> >  obj-y += efi_boottime.o
> > +obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
> >  obj-y += efi_console.o
> >  obj-y += efi_device_path.o
> >  obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o
> > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> > new file mode 100644
> > index 000000000000..fb104bb92a6c
> > --- /dev/null
> > +++ b/lib/efi_loader/efi_capsule.c
> > @@ -0,0 +1,153 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + *  EFI Capsule
> > + *
> > + *  Copyright (c) 2018 Linaro Limited
> > + *			Author: AKASHI Takahiro
> > + */
> > +
> > +#include <common.h>
> > +#include <efi_loader.h>
> > +#include <fs.h>
> > +#include <malloc.h>
> > +#include <sort.h>
> > +
> > +const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
> > +
> > +static __maybe_unused int get_last_capsule(void)
> > +{
> > +	u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
> > +	char value[11], *p;
> > +	efi_uintn_t size;
> > +	unsigned long num = 0xffff;
> > +	efi_status_t ret;
> > +
> > +	size = sizeof(value16);
> > +	ret = EFI_CALL(efi_get_variable(L"CapsuleLast",
> > +					&efi_guid_capsule_report,
> > +					NULL, &size, value16));
> > +	if (ret != EFI_SUCCESS || u16_strncmp(value16, L"Capsule", 7))
> > +		goto err;
> > +
> > +	p = value;
> > +	utf16_utf8_strcpy(&p, value16);
> > +	strict_strtoul(&value[7], 16, &num);
> > +err:
> > +	return (int)num;
> > +}
> > +
> 
> Missing function description

Okay, as I said somewhere else, I've forgot to add some function
descriptions in v1.

> > +static __maybe_unused
> > +void set_capsule_result(int num, struct efi_capsule_header *capsule,
> > +			efi_status_t return_status)
> > +{
> > +	char variable_name[12];
> > +	u16 variable_name16[12], *p;
> > +	struct efi_capsule_result_variable_header result;
> > +	struct efi_time time;
> > +	efi_status_t ret;
> > +
> > +	sprintf(variable_name, "Capsule%04X", num);
> > +	p = variable_name16;
> > +	utf8_utf16_strncpy(&p, variable_name, 11);
> > +	result.variable_total_size = sizeof(result);
> > +	result.capsule_guid = capsule->capsule_guid;
> > +	ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL));
> > +	if (ret == EFI_SUCCESS)
> > +		memcpy(&result.capsule_processed, &time, sizeof(time));
> > +	else
> > +		memset(&result.capsule_processed, 0, sizeof(time));
> > +	result.capsule_status = return_status;
> > +	ret = EFI_CALL(efi_set_variable(variable_name16,
> > +					&efi_guid_capsule_report,
> > +					EFI_VARIABLE_NON_VOLATILE |
> > +					EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +					EFI_VARIABLE_RUNTIME_ACCESS,
> > +					sizeof(result), &result));
> > +	if (ret)
> > +		printf("EFI: creating %s failed\n", variable_name);
> > +}
> > +
> > +/*
> > + * Launch a capsule
> 
> Please, stick to the style described in
> https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation
> 
> > + */
> > +/**
> > + * efi_update_capsule() - process information from operating system
> > + *
> > + * This function implements the UpdateCapsule() runtime service.
> > + *
> > + * See the Unified Extensible Firmware Interface (UEFI) specification for
> > + * details.
> > + *
> > + * @capsule_header_array:	pointer to array of virtual pointers
> > + * @capsule_count:		number of pointers in capsule_header_array
> > + * @scatter_gather_list:	pointer to arry of physical pointers
> > + * Return:			status code
> > + */
> > +efi_status_t EFIAPI efi_update_capsule(
> > +		struct efi_capsule_header **capsule_header_array,
> > +		efi_uintn_t capsule_count,
> > +		u64 scatter_gather_list)
> > +{
> > +	struct efi_capsule_header *capsule;
> > +	unsigned int i;
> > +	efi_status_t ret;
> > +
> > +	EFI_ENTRY("%p, %lu, %llu\n", capsule_header_array, capsule_count,
> > +		  scatter_gather_list);
> > +
> > +	if (!capsule_count) {
> > +		ret = EFI_INVALID_PARAMETER;
> > +		goto out;
> > +	}
> > +
> > +	ret = EFI_SUCCESS;
> > +	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
> > +	     i++, capsule = *(++capsule_header_array)) {
> > +	}
> > +out:
> > +	return EFI_EXIT(ret);
> > +}
> > +
> > +/**
> > + * efi_query_capsule_caps() - check if capsule is supported
> > + *
> > + * This function implements the QueryCapsuleCapabilities() runtime service.
> > + *
> > + * See the Unified Extensible Firmware Interface (UEFI) specification for
> > + * details.
> > + *
> > + * @capsule_header_array:	pointer to array of virtual pointers
> > + * @capsule_count:		number of pointers in capsule_header_array
> > + * @maximum_capsule_size:	maximum capsule size
> > + * @reset_type:			type of reset needed for capsule update
> > + * Return:			status code
> > + */
> > +efi_status_t EFIAPI efi_query_capsule_caps(
> > +		struct efi_capsule_header **capsule_header_array,
> > +		efi_uintn_t capsule_count,
> > +		u64 *maximum_capsule_size,
> > +		u32 *reset_type)
> > +{
> > +	struct efi_capsule_header *capsule __attribute__((unused));
> > +	unsigned int i;
> > +	efi_status_t ret;
> > +
> > +	EFI_ENTRY("%p, %lu, %p, %p\n", capsule_header_array, capsule_count,
> > +		  maximum_capsule_size, reset_type);
> > +
> > +	if (!maximum_capsule_size) {
> > +		ret = EFI_INVALID_PARAMETER;
> > +		goto out;
> > +	}
> > +
> > +	*maximum_capsule_size = U64_MAX;
> > +	*reset_type = EFI_RESET_COLD;
> > +
> > +	ret = EFI_SUCCESS;
> > +	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
> > +	     i++, capsule = *(++capsule_header_array)) {
> > +		/* TODO */
> > +	}
> > +out:
> > +	return EFI_EXIT(ret);
> > +}
> > diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> > index 6a25acbbcdf5..669e25a9589a 100644
> > --- a/lib/efi_loader/efi_runtime.c
> > +++ b/lib/efi_loader/efi_runtime.c
> > @@ -130,6 +130,11 @@ efi_status_t efi_init_runtime_supported(void)
> >  #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
> >  	rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
> >  #endif
> > +#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
> > +	rt_table->runtime_services_supported |=
> > +			(EFI_RT_SUPPORTED_UPDATE_CAPSULE |
> > +			 EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES);
> > +#endif
> >
> >  	ret = efi_install_configuration_table(&efi_rt_properties_table_guid,
> >  					      rt_table);
> > @@ -410,6 +415,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
> >  	return EFI_UNSUPPORTED;
> >  }
> >
> > +/**
> > + * efi_update_capsule_unsupported() - process information from operating system
> > + *
> > + * This function implements the UpdateCapsule() runtime service.
> > + *
> > + * See the Unified Extensible Firmware Interface (UEFI) specification for
> > + * details.
> 
> The description should differ from the description of efi_update_capsule().

I don't get your point.
This kind of description, like "See the UEFI ...," are often seen
in efi_boottime.c and others.

> > + *
> > + * @capsule_header_array:	pointer to array of virtual pointers
> > + * @capsule_count:		number of pointers in capsule_header_array
> > + * @scatter_gather_list:	pointer to arry of physical pointers
> > + * Returns:			status code
> > + */
> > +efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported(
> > +			struct efi_capsule_header **capsule_header_array,
> > +			efi_uintn_t capsule_count,
> > +			u64 scatter_gather_list)
> > +{
> > +	return EFI_UNSUPPORTED;
> > +}
> > +
> > +/**
> > + * efi_query_capsule_caps_unsupported() - check if capsule is supported
> > + *
> > + * This function implements the QueryCapsuleCapabilities() runtime service.
> > + *
> > + * See the Unified Extensible Firmware Interface (UEFI) specification for
> > + * details.
> 
> The description should differ from the description of
> efi_query_capsule_caps().
> 
> > + *
> > + * @capsule_header_array:	pointer to array of virtual pointers
> > + * @capsule_count:		number of pointers in capsule_header_array
> > + * @maximum_capsule_size:	maximum capsule size
> > + * @reset_type:			type of reset needed for capsule update
> > + * Returns:			status code
> > + */
> > +efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps_unsupported(
> > +			struct efi_capsule_header **capsule_header_array,
> > +			efi_uintn_t capsule_count,
> > +			u64 *maximum_capsule_size,
> > +			u32 *reset_type)
> > +{
> > +	return EFI_UNSUPPORTED;
> > +}
> > +
> >  /**
> >   * efi_is_runtime_service_pointer() - check if pointer points to runtime table
> >   *
> > @@ -433,6 +482,12 @@ void efi_runtime_detach(void)
> >  	efi_runtime_services.reset_system = efi_reset_system;
> >  	efi_runtime_services.get_time = efi_get_time;
> >  	efi_runtime_services.set_time = efi_set_time;
> > +#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
> > +	/* won't support at runtime */
> > +	efi_runtime_services.update_capsule = efi_update_capsule_unsupported;
> > +	efi_runtime_services.query_capsule_caps =
> > +					efi_query_capsule_caps_unsupported;
> > +#endif
> >
> >  	/* Update CRC32 */
> >  	efi_update_table_header_crc32(&efi_runtime_services.hdr);
> > @@ -837,50 +892,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
> >  	return EFI_UNSUPPORTED;
> >  }
> >
> > -/**
> > - * efi_update_capsule() - process information from operating system
> > - *
> > - * This function implements the UpdateCapsule() runtime service.
> > - *
> > - * See the Unified Extensible Firmware Interface (UEFI) specification for
> > - * details.
> > - *
> > - * @capsule_header_array:	pointer to array of virtual pointers
> > - * @capsule_count:		number of pointers in capsule_header_array
> > - * @scatter_gather_list:	pointer to arry of physical pointers
> > - * Returns:			status code
> > - */
> > -efi_status_t __efi_runtime EFIAPI efi_update_capsule(
> > -			struct efi_capsule_header **capsule_header_array,
> > -			efi_uintn_t capsule_count,
> > -			u64 scatter_gather_list)
> > -{
> > -	return EFI_UNSUPPORTED;
> > -}
> > -
> > -/**
> > - * efi_query_capsule_caps() - check if capsule is supported
> > - *
> > - * This function implements the QueryCapsuleCapabilities() runtime service.
> > - *
> > - * See the Unified Extensible Firmware Interface (UEFI) specification for
> > - * details.
> > - *
> > - * @capsule_header_array:	pointer to array of virtual pointers
> > - * @capsule_count:		number of pointers in capsule_header_array
> > - * @maximum_capsule_size:	maximum capsule size
> > - * @reset_type:			type of reset needed for capsule update
> > - * Returns:			status code
> > - */
> > -efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps(
> > -			struct efi_capsule_header **capsule_header_array,
> > -			efi_uintn_t capsule_count,
> > -			u64 *maximum_capsule_size,
> > -			u32 *reset_type)
> > -{
> > -	return EFI_UNSUPPORTED;
> > -}
> > -
> >  struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
> >  	.hdr = {
> >  		.signature = EFI_RUNTIME_SERVICES_SIGNATURE,
> > @@ -898,7 +909,12 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
> >  	.set_variable = efi_set_variable,
> >  	.get_next_high_mono_count = (void *)&efi_unimplemented,
> >  	.reset_system = &efi_reset_system_boottime,
> > +#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
> >  	.update_capsule = efi_update_capsule,
> >  	.query_capsule_caps = efi_query_capsule_caps,
> > +#else
> > +	.update_capsule = efi_update_capsule_unsupported,
> > +	.query_capsule_caps = efi_query_capsule_caps_unsupported,
> > +#endif
> >  	.query_variable_info = efi_query_variable_info,
> >  };
> > diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
> > index 1b648c84673a..8fe378bbfdfc 100644
> > --- a/lib/efi_loader/efi_setup.c
> > +++ b/lib/efi_loader/efi_setup.c
> > @@ -115,6 +115,29 @@ static efi_status_t efi_init_secure_boot(void)
> >  }
> >  #endif /* CONFIG_EFI_SECURE_BOOT */
> >
> > +/**
> > + * efi_init_os_indications() - indicate supported features for OS requests
> > + *
> > + * Set the OsIndicationsSupported variable.
> > + *
> > + * Return:	status code
> > + */
> > +static efi_status_t efi_init_os_indications(void)
> > +{
> > +	u64 os_indications_supported = 0;
> > +
> > +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
> > +	os_indications_supported |=
> > +			EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
> > +#endif
> 
> Though the UEFI spec does not mention it I guess we will have to treat
> OsIndicationsSupported as read-only once we get that properly supported.

The only thing that we should do now is to export "efi_set_variable_common."
(I don't know if Ilias' StandaloneMM will accept READ_ONLY attribute.)

Please note that there is also an instance that should be read-only
in efi_init_secure_boot().

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> > +	return EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
> > +					 &efi_global_variable_guid,
> > +					 EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +					 EFI_VARIABLE_RUNTIME_ACCESS,
> > +					 sizeof(os_indications_supported),
> > +					 &os_indications_supported));
> > +}
> > +
> >  /**
> >   * efi_init_obj_list() - Initialize and populate EFI object list
> >   *
> > @@ -122,7 +145,6 @@ static efi_status_t efi_init_secure_boot(void)
> >   */
> >  efi_status_t efi_init_obj_list(void)
> >  {
> > -	u64 os_indications_supported = 0; /* None */
> >  	efi_status_t ret = EFI_SUCCESS;
> >
> >  	/* Initialize once only */
> > @@ -146,12 +168,7 @@ efi_status_t efi_init_obj_list(void)
> >  		goto out;
> >
> >  	/* Indicate supported features */
> > -	ret = EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
> > -					&efi_global_variable_guid,
> > -					EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > -					EFI_VARIABLE_RUNTIME_ACCESS,
> > -					sizeof(os_indications_supported),
> > -					&os_indications_supported));
> > +	ret = efi_init_os_indications();
> >  	if (ret != EFI_SUCCESS)
> >  		goto out;
> >
> >
> 

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

* [PATCH 02/10] efi_loader: add option to initialise EFI subsystem early
  2020-05-17  7:29       ` Heinrich Schuchardt
@ 2020-05-18  1:43         ` AKASHI Takahiro
  0 siblings, 0 replies; 33+ messages in thread
From: AKASHI Takahiro @ 2020-05-18  1:43 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Sun, May 17, 2020 at 09:29:44AM +0200, Heinrich Schuchardt wrote:
> On 4/28/20 2:16 AM, AKASHI Takahiro wrote:
> > Heinrich,
> >
> > On Mon, Apr 27, 2020 at 10:09:11PM +0200, Heinrich Schuchardt wrote:
> >> On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> >>> If this option is enabled, the initialisation of UEFI subsystem will be
> >>> done as part of U-Boot initialisation.
> >>>
> >>> This feature will be utilised in implementing capsule-on-disk feature.
> >>
> >> This would mean that we allow unaligned access very early. Something
> >> Siarhei was against:
> >
> > ?
> > Even with CONFIG_EFI_CAPSULE_ON_DISK_EARLY enabled,
> > efi_init_obj_list() is called at the last of "init" list
> > and efi_launch_capsules() is called just before the main
> > command loop.
> > So "unalignment" issue won't happen.
> 
> efi_init_obj_list() is even called when booting via booti and therefore
> before a lot of other code.

How is booti related to "unalignment" issue?

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> >
> >> https://lists.denx.de/pipermail/u-boot/2018-March/324242.html
> >> https://patchwork.ozlabs.org/project/uboot/patch/20180329213350.7868-1-xypron.glpk at gmx.de/
> >>
> >> Why can't you wait with the capsule update until any command initializes
> >> the UEFI sub-system.
> >
> > This topic is the one the I mentioned in RFC's cover letter
> > and asked you for comments several time.
> > Anyway, there are a couple of reasons:
> > 1. Updated firmware may have some effects on not only UEFI
> >    subsystem but also U-Boot's other features.
> > 2. Firmware update should surely take place after reboot
> >    as UEFI specification expects.
> > 3. Firmware update should not rely on user's interactions
> >    or whatever "bootcmd" is set to.
> > 4. In case of failure of firmware update, some recovery should
> >    be automatically taken "before" the command line is handed over
> >    to users. (The feature is not implemented yet though.)
> >
> > -Takahiro Akashi
> >
> >
> >> Best regards
> >>
> >> Heinrich
> >>
> >>>
> >>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>> ---
> >>>  common/board_r.c       | 6 ++++++
> >>>  lib/efi_loader/Kconfig | 4 ++++
> >>>  2 files changed, 10 insertions(+)
> >>>
> >>> diff --git a/common/board_r.c b/common/board_r.c
> >>> index 0bbeaa7594c6..7cf21a6078f9 100644
> >>> --- a/common/board_r.c
> >>> +++ b/common/board_r.c
> >>> @@ -64,6 +64,9 @@
> >>>  #if defined(CONFIG_GPIO_HOG)
> >>>  #include <asm/gpio.h>
> >>>  #endif
> >>> +#ifdef CONFIG_EFI_SETUP_EARLY
> >>> +#include <efi_loader.h>
> >>> +#endif
> >>>
> >>>  DECLARE_GLOBAL_DATA_PTR;
> >>>
> >>> @@ -867,6 +870,9 @@ static init_fnc_t init_sequence_r[] = {
> >>>  #endif
> >>>  #if defined(CONFIG_M68K) && defined(CONFIG_BLOCK_CACHE)
> >>>  	blkcache_init,
> >>> +#endif
> >>> +#ifdef CONFIG_EFI_SETUP_EARLY
> >>> +	(init_fnc_t)efi_init_obj_list,
> >>>  #endif
> >>>  	run_main_loop,
> >>>  };
> >>> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> >>> index 1cfa24ffcf72..7cc2d940f848 100644
> >>> --- a/lib/efi_loader/Kconfig
> >>> +++ b/lib/efi_loader/Kconfig
> >>> @@ -25,6 +25,10 @@ config EFI_LOADER
> >>>
> >>>  if EFI_LOADER
> >>>
> >>> +config EFI_SETUP_EARLY
> >>> +	bool
> >>> +	default n
> >>> +
> >>>  config EFI_GET_TIME
> >>>  	bool "GetTime() runtime service"
> >>>  	depends on DM_RTC
> >>>
> >>
> 

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

end of thread, other threads:[~2020-05-18  1:43 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-27  9:48 [PATCH 00/10] efi_loader: add capsule update support AKASHI Takahiro
2020-04-27  9:48 ` [PATCH 01/10] efi_loader: disk: add efi_disk_is_system_part() AKASHI Takahiro
2020-04-27 19:57   ` Heinrich Schuchardt
2020-04-27 23:54     ` AKASHI Takahiro
2020-05-01  7:06       ` Heinrich Schuchardt
2020-05-05 22:33         ` Heinrich Schuchardt
2020-04-27  9:48 ` [PATCH 02/10] efi_loader: add option to initialise EFI subsystem early AKASHI Takahiro
2020-04-27 20:09   ` Heinrich Schuchardt
2020-04-28  0:16     ` AKASHI Takahiro
2020-05-17  7:29       ` Heinrich Schuchardt
2020-05-18  1:43         ` AKASHI Takahiro
2020-04-27  9:48 ` [PATCH 03/10] efi_loader: define UpdateCapsule api AKASHI Takahiro
2020-05-17  8:02   ` Heinrich Schuchardt
2020-05-18  1:34     ` AKASHI Takahiro
2020-04-27  9:48 ` [PATCH 04/10] efi_loader: capsule: add capsule_on_disk support AKASHI Takahiro
2020-04-27 20:28   ` Heinrich Schuchardt
2020-04-28  0:28     ` AKASHI Takahiro
2020-04-30 12:52       ` Sughosh Ganu
2020-04-30 19:51         ` Heinrich Schuchardt
2020-05-07  2:50           ` AKASHI Takahiro
2020-05-07 12:05           ` Sughosh Ganu
2020-04-27  9:48 ` [PATCH 05/10] efi_loader: capsule: add memory range capsule definitions AKASHI Takahiro
2020-04-27  9:48 ` [PATCH 06/10] efi_loader: capsule: support firmware update AKASHI Takahiro
2020-04-27  9:48 ` [PATCH 07/10] efi_loader: add simple firmware management protocol for FIT image AKASHI Takahiro
2020-04-27  9:48 ` [PATCH 08/10] cmd: add "efidebug capsule" command AKASHI Takahiro
2020-04-30 12:38   ` Sughosh Ganu
2020-05-07  1:58     ` AKASHI Takahiro
2020-04-27  9:48 ` [PATCH 09/10] tools: add mkeficapsule command for UEFI capsule update test AKASHI Takahiro
2020-04-27 20:15   ` Heinrich Schuchardt
2020-04-28  1:52     ` AKASHI Takahiro
2020-04-27  9:48 ` [PATCH 10/10] test/py: add a test for efi firmware update capsule AKASHI Takahiro
2020-04-27 20:33 ` [PATCH 00/10] efi_loader: add capsule update support Heinrich Schuchardt
2020-04-27 23:45   ` AKASHI Takahiro

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.