All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 00/14] efi_loader: add capsule update support
@ 2020-03-17  2:12 AKASHI Takahiro
  2020-03-17  2:12 ` [RFC 01/14] efi_loader: define OsIndicationsSupported flags AKASHI Takahiro
                   ` (15 more replies)
  0 siblings, 16 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 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 to pass information to the firmware.
This is mostly used to update firmware binary on devices by instructions
from OS.

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

UpdateCapsule is a runtime services function, but is, at least initially,
provided only before exiting boot services alike other runtime functions.
This is because modifying storage which may be shared with OS must be
carefully designed and there is no general assumption to do that as in
the case of [Get/]SetVariable.
Instead, any capsule can be handed over to the firmware as a file on
a specific file system. In other words, we only support "capsules on disk"
for now.

Regarding firmware update, most of functionality is provided by FMP
driver and it will be by nature system/platform-specific. So you can and
should implement FMP drivers based on your system requirements.
In this patch series, only a simple FMP driver based on FIT image for
a single region is supported.  (So it is "for reference only")
See more details in "efi_loader: capsule: add simple firmware management
protocol."

Regarding variable update, the implementation here is based on a draft
proposal[1] by Peter in Boot-arch ML. The specification should be discussed
and finalized first. So the code doesn't fully implement Peter's idea.

[1] https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html

Patch structure
===============
Patch#1-#4: preparatory patches
Patch#5-#11: main part of implementation
Patch#12-#14: utilities and tests

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

Currently, my pytest requires the following prerequisite patch to
run without errors:
[2] https://lists.denx.de/pipermail/u-boot/2020-March/401726.html
[3] https://lists.denx.de/pipermail/u-boot/2020-March/401727.html

In addition, you have to enable CONFIG_ENV_IS_IN_SPI_FLASH and
CONFIG_SPI_FLASH_SANDBOX to successfully run the test.

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.

TODO's
======
(May or 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
=======
Initial release as RFC (March 17, 2020)

AKASHI Takahiro (14):
  efi_loader: define OsIndicationsSupported flags
  efi_loader: define System Resource Table macros
  efi_loader: export a couple of protocol related functions
  efi_loader: correct a definition of struct efi_capsule_header
  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
  efi_loader: capsule: support variable update
  efi_loader: variable: export variables table for runtime access
  cmd: add "efidebug capsule" command
  tools: add mkeficapsule command for UEFI capsule update test
  test/py: add efi capsule test

 cmd/efidebug.c                                | 234 +++++
 include/efi_api.h                             | 214 ++++-
 include/efi_loader.h                          |  53 ++
 lib/efi_loader/Kconfig                        |  65 ++
 lib/efi_loader/Makefile                       |   2 +
 lib/efi_loader/efi_boottime.c                 |  29 +-
 lib/efi_loader/efi_capsule.c                  | 860 ++++++++++++++++++
 lib/efi_loader/efi_firmware.c                 | 191 ++++
 lib/efi_loader/efi_runtime.c                  | 104 ++-
 lib/efi_loader/efi_setup.c                    |  41 +-
 lib/efi_loader/efi_variable.c                 | 109 +++
 test/py/tests/test_efi_capsule/conftest.py    | 109 +++
 test/py/tests/test_efi_capsule/defs.py        |  21 +
 .../test_efi_capsule/test_capsule_firmware.py | 102 +++
 .../test_efi_capsule/test_capsule_variable.py | 141 +++
 test/py/tests/test_efi_capsule/uboot_env.its  |  25 +
 tools/Makefile                                |   3 +
 tools/mkeficapsule.c                          | 501 ++++++++++
 18 files changed, 2744 insertions(+), 60 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/test_capsule_variable.py
 create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its
 create mode 100644 tools/mkeficapsule.c

-- 
2.25.1

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

* [RFC 01/14] efi_loader: define OsIndicationsSupported flags
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  7:03   ` Heinrich Schuchardt
  2020-03-17  2:12 ` [RFC 02/14] efi_loader: define System Resource Table macros AKASHI Takahiro
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 UTC (permalink / raw)
  To: u-boot

These flags are expected to be set in OsIndicationsSupported variable
if corresponding features are supported. See UEFI specification,
section 8.5.4.

In particular, capsule-related flags will be used in my capsule
update patch.

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

diff --git a/include/efi_api.h b/include/efi_api.h
index 22396172e15f..73c0722b0168 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -57,6 +57,17 @@ typedef u16 efi_form_id_t;
 
 struct efi_event;
 
+/* OsIndicationsSupported flags */
+#define EFI_OS_INDICATIONS_BOOT_TO_RW_UI		0x0000000000000001
+#define EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION		0x0000000000000002
+#define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED \
+							0x0000000000000004
+#define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED	0x0000000000000008
+#define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED	0x0000000000000010
+#define EFI_OS_INDICATIONS_START_OS_RECOVERY		0x0000000000000020
+#define EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY	0x0000000000000040
+#define EFI_OS_INDICATIONS_JSON_CONFIG_DATA_REFRESH	0x0000000000000080
+
 /* EFI Boot Services table */
 #define EFI_BOOT_SERVICES_SIGNATURE 0x56524553544f4f42
 struct efi_boot_services {
-- 
2.25.1

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

* [RFC 02/14] efi_loader: define System Resource Table macros
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
  2020-03-17  2:12 ` [RFC 01/14] efi_loader: define OsIndicationsSupported flags AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  7:06   ` Heinrich Schuchardt
  2020-03-17  2:12 ` [RFC 03/14] efi_loader: export a couple of protocol related functions AKASHI Takahiro
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 UTC (permalink / raw)
  To: u-boot

Some of those values will be used in an implementation of UEFI firmware
management protocol as part of my capsule update patch.

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

diff --git a/include/efi_api.h b/include/efi_api.h
index 73c0722b0168..a99cc7acc475 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -1627,4 +1627,31 @@ struct efi_unicode_collation_protocol {
 #define LOAD_OPTION_CATEGORY_BOOT	0x00000000
 #define LOAD_OPTION_CATEGORY_APP	0x00000100
 
+/*
+ * System Resource Table
+ */
+/* Firmware Type Definitions */
+#define ESRT_FW_TYPE_UNKNOWN		0x00000000
+#define ESRT_FW_TYPE_SYSTEMFIRMWARE	0x00000001
+#define ESRT_FW_TYPE_DEVICEFIRMWARE	0x00000002
+#define ESRT_FW_TYPE_UEFIDRIVER		0x00000003
+
+/* Last Attempt Status Values */
+#define LAST_ATTEMPT_STATUS_SUCCESS			0x00000000
+#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL		0x00000001
+#define LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES 0x00000002
+#define LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION	0x00000003
+#define LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT	0x00000004
+#define LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR		0x00000005
+#define LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_AC		0x00000006
+#define LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_BATT		0x00000007
+#define LAST_ATTEMPT_STATUS_ERROR_UNSATISFIED_DEPENDENCIES 0x00000008
+
+/*
+ * The LastAttemptStatus values of 0x1000 - 0x4000 are reserved for vendor
+ * usage.
+ */
+#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MIN 0x00001000
+#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MAX 0x00004000
+
 #endif
-- 
2.25.1

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

* [RFC 03/14] efi_loader: export a couple of protocol related functions
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
  2020-03-17  2:12 ` [RFC 01/14] efi_loader: define OsIndicationsSupported flags AKASHI Takahiro
  2020-03-17  2:12 ` [RFC 02/14] efi_loader: define System Resource Table macros AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  7:19   ` Heinrich Schuchardt
  2020-03-18 18:03   ` Heinrich Schuchardt
  2020-03-17  2:12 ` [RFC 04/14] efi_loader: correct a definition of struct efi_capsule_header AKASHI Takahiro
                   ` (12 subsequent siblings)
  15 siblings, 2 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 UTC (permalink / raw)
  To: u-boot

This is a preparatory patch.
Those functions will be used in an implementation of UEFI firmware
management protocol as part of my capsule update patch.

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

diff --git a/include/efi_loader.h b/include/efi_loader.h
index d4c59b54c48b..c55bade6a508 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -456,6 +456,20 @@ efi_status_t efi_remove_all_protocols(const efi_handle_t handle);
 /* Install multiple protocol interfaces */
 efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
 				(efi_handle_t *handle, ...);
+/* Get handles that support a given protocol */
+efi_status_t EFIAPI efi_locate_handle_buffer(
+			enum efi_locate_search_type search_type,
+			const efi_guid_t *protocol, void *search_key,
+			efi_uintn_t *no_handles, efi_handle_t **buffer);
+/* Close an previously opened protocol interface */
+efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
+				       const efi_guid_t *protocol,
+				       efi_handle_t agent_handle,
+				       efi_handle_t controller_handle);
+/* Open a protocol interface */
+efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
+					const efi_guid_t *protocol,
+					void **protocol_interface);
 /* Call this to create an event */
 efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
 			      void (EFIAPI *notify_function) (
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 1f598b357a5c..9860d5047502 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -2106,10 +2106,10 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
-					      const efi_guid_t *protocol,
-					      efi_handle_t agent_handle,
-					      efi_handle_t controller_handle)
+efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
+				       const efi_guid_t *protocol,
+				       efi_handle_t agent_handle,
+				       efi_handle_t controller_handle)
 {
 	struct efi_handler *handler;
 	struct efi_open_protocol_info_item *item;
@@ -2282,7 +2282,7 @@ static efi_status_t EFIAPI efi_protocols_per_handle(
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_locate_handle_buffer(
+efi_status_t EFIAPI efi_locate_handle_buffer(
 			enum efi_locate_search_type search_type,
 			const efi_guid_t *protocol, void *search_key,
 			efi_uintn_t *no_handles, efi_handle_t **buffer)
@@ -3182,9 +3182,9 @@ out:
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
-					       const efi_guid_t *protocol,
-					       void **protocol_interface)
+efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
+					const efi_guid_t *protocol,
+					void **protocol_interface)
 {
 	return efi_open_protocol(handle, protocol, protocol_interface, efi_root,
 				 NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
-- 
2.25.1

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

* [RFC 04/14] efi_loader: correct a definition of struct efi_capsule_header
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (2 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 03/14] efi_loader: export a couple of protocol related functions AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  7:25   ` Heinrich Schuchardt
  2020-03-18 18:03   ` Heinrich Schuchardt
  2020-03-17  2:12 ` [RFC 05/14] efi_loader: define UpdateCapsule api AKASHI Takahiro
                   ` (11 subsequent siblings)
  15 siblings, 2 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 UTC (permalink / raw)
  To: u-boot

See UEFI specification, section 8.5.3.
In addition, the structure, efi_capsule_header, should be "packed"
as it is a serialized binary format in a capsule file.

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

diff --git a/include/efi_api.h b/include/efi_api.h
index a99cc7acc475..4040c44855fa 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -218,11 +218,11 @@ enum efi_reset_type {
 #define CAPSULE_FLAGS_INITIATE_RESET		0x00040000
 
 struct efi_capsule_header {
-	efi_guid_t *capsule_guid;
+	efi_guid_t capsule_guid;
 	u32 header_size;
 	u32 flags;
 	u32 capsule_image_size;
-};
+} __packed;
 
 #define EFI_RT_SUPPORTED_GET_TIME			0x0001
 #define EFI_RT_SUPPORTED_SET_TIME			0x0002
-- 
2.25.1

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

* [RFC 05/14] efi_loader: define UpdateCapsule api
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (3 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 04/14] efi_loader: correct a definition of struct efi_capsule_header AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  2:12 ` [RFC 06/14] efi_loader: capsule: add capsule_on_disk support AKASHI Takahiro
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 UTC (permalink / raw)
  To: u-boot

In this commit, skeleton functions for capsule-related API's are
added under CONFIG_EFI_CAPSULE_UPDATE configuration.
Detailed implementation 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         |  11 ++++
 lib/efi_loader/Kconfig       |   7 +++
 lib/efi_loader/Makefile      |   1 +
 lib/efi_loader/efi_capsule.c |  98 +++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_runtime.c | 104 ++++++++++++++++++++---------------
 lib/efi_loader/efi_setup.c   |  31 ++++++++---
 7 files changed, 213 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 4040c44855fa..ac2b38801c0c 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 c55bade6a508..c3cb7735bf50 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -679,6 +679,17 @@ void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data);
 unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
 efi_status_t efi_bootmgr_load(efi_handle_t *handle);
 
+/* 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);
+
 #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 a7afa3f29e88..2ef6cb124f3a 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -90,6 +90,13 @@ config EFI_UNICODE_COLLATION_PROTOCOL
 
 endif
 
+config EFI_CAPSULE_UPDATE
+	bool "Enable capsule update support"
+	default n
+	help
+	  Select this option if you want to use capsule update feature,
+	  including firmware updates and variable updates.
+
 config EFI_LOADER_BOUNCE_BUFFER
 	bool "EFI Applications use bounce buffers for DMA operations"
 	depends on ARM64
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 04dc8648512b..f19096924bef 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_CAPSULE_UPDATE) += 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..d3f931910d10
--- /dev/null
+++ b/lib/efi_loader/efi_capsule.c
@@ -0,0 +1,98 @@
+// 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>
+
+/*
+ * 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 4b3c843b2ced..1dce8d06abdb 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -107,6 +107,11 @@ efi_status_t efi_init_runtime_supported(void)
 #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
 	efi_runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
 #endif
+#ifdef CONFIG_EFI_CAPSULE_UPDATE
+	efi_runtime_services_supported |=
+			(EFI_RT_SUPPORTED_UPDATE_CAPSULE |
+			 EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES);
+#endif
 
 	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
 					 &efi_global_variable_guid,
@@ -390,6 +395,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
  *
@@ -413,6 +462,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_CAPSULE_UPDATE
+	/* won't support@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);
@@ -817,50 +872,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,
@@ -878,7 +889,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_CAPSULE_UPDATE
 	.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 de7b616c6daa..c485cad34022 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -82,6 +82,29 @@ out:
 	return ret;
 }
 
+/**
+ * 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_CAPSULE_UPDATE
+	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
  *
@@ -89,7 +112,6 @@ out:
  */
 efi_status_t efi_init_obj_list(void)
 {
-	u64 os_indications_supported = 0; /* None */
 	efi_status_t ret = EFI_SUCCESS;
 
 	/* Initialize once only */
@@ -113,12 +135,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.1

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

* [RFC 06/14] efi_loader: capsule: add capsule_on_disk support
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (4 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 05/14] efi_loader: define UpdateCapsule api AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-18  8:55   ` Heinrich Schuchardt
  2020-03-17  2:12 ` [RFC 07/14] efi_loader: capsule: add memory range capsule definitions AKASHI Takahiro
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 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 identified only if they
are located in a specific directory on a device that is determined
by "BootXXXX" variables.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_loader.h          |  18 ++
 lib/efi_loader/Kconfig        |   7 +
 lib/efi_loader/efi_boottime.c |   3 +
 lib/efi_loader/efi_capsule.c  | 548 ++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_setup.c    |   6 +
 5 files changed, 582 insertions(+)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index c3cb7735bf50..c701672e18db 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -178,6 +178,8 @@ extern const efi_guid_t efi_guid_hii_config_routing_protocol;
 extern const efi_guid_t efi_guid_hii_config_access_protocol;
 extern const efi_guid_t efi_guid_hii_database_protocol;
 extern const efi_guid_t efi_guid_hii_string_protocol;
+/* GUID of capsule update result */
+extern const efi_guid_t efi_guid_capsule_report;
 
 /* GUID of RNG protocol */
 extern const efi_guid_t efi_guid_rng_protocol;
@@ -690,6 +692,22 @@ efi_status_t EFIAPI efi_query_capsule_caps(
 		u64 *maximum_capsule_size,
 		u32 *reset_type);
 
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK
+#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\"
+
+/* Hook at initialization */
+efi_status_t efi_launch_capsules(void);
+/* Notify ExitBootServices() is called */
+void efi_capsule_boot_exit_notify(void);
+#else
+static inline efi_status_t efi_launch_capsules(void)
+{
+	return EFI_SUCCESS;
+}
+
+static inline efi_capsule_boot_exit_notify(void) {}
+#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 */
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 2ef6cb124f3a..95e10f7d981b 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -97,6 +97,13 @@ config EFI_CAPSULE_UPDATE
 	  Select this option if you want to use capsule update feature,
 	  including firmware updates and variable updates.
 
+config EFI_CAPSULE_ON_DISK
+	bool "Enable capsule-on-disk support"
+	depends on EFI_CAPSULE_UPDATE
+	default n
+	help
+	  Select this option if you want to use capsule-on-disk feature.
+
 config EFI_LOADER_BOUNCE_BUFFER
 	bool "EFI Applications use bounce buffers for DMA operations"
 	depends on ARM64
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 9860d5047502..c2a789b4f910 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1981,6 +1981,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
 	/* Notify variable services */
 	efi_variables_boot_exit_notify();
 
+	/* Notify capsule services */
+	efi_capsule_boot_exit_notify();
+
 	/* Remove all events except EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
 	list_for_each_entry_safe(evt, next_event, &efi_events, link) {
 		if (evt->type != EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index d3f931910d10..f3e2a555a6b9 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -10,8 +10,14 @@
 #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;
+
+/* for file system access */
+static struct efi_file_handle *bootdev_root;
+
 /*
  * Launch a capsule
  */
@@ -96,3 +102,545 @@ efi_status_t EFIAPI efi_query_capsule_caps(
 out:
 	return EFI_EXIT(ret);
 }
+
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK
+static void efi_capsule_result_variable(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)
+		EFI_PRINT("EFI Capsule: creating %s failed\n", variable_name);
+}
+
+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(struct efi_device_path *dp)
+{
+	efi_handle_t handle;
+	struct efi_handler *handler;
+	efi_status_t ret;
+
+	handle = efi_dp_find_obj(dp, NULL);
+	if (!handle)
+		return false;
+
+	/* check if this is a block device */
+	ret = efi_search_protocol(handle, &efi_block_io_guid, &handler);
+	if (ret != EFI_SUCCESS)
+		return false;
+
+	return true;
+}
+
+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(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(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;
+}
+
+static 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;
+}
+
+/*
+ * Launch all the capsules in system at boot time
+ *
+ * Called by efi init code
+ */
+efi_status_t efi_launch_capsules(void)
+{
+	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;
+
+	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("EFI 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)
+				EFI_PRINT("EFI Capsule update failed at %ls\n",
+					  files[i]);
+
+			free(capsule);
+		} else {
+			EFI_PRINT("EFI Capsule read failed\n");
+		}
+		/* create CapsuleXXXX */
+		efi_capsule_result_variable(num, capsule, ret);
+
+		/* delete a capsule either in case of success or failure */
+		ret = efi_capsule_delete_file(files[i]);
+		if (ret != EFI_SUCCESS)
+			EFI_PRINT("EFI Capsule deletion of capsule failed at %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;
+}
+
+/*
+ * Dummy functions after ExitBootServices()
+ */
+efi_status_t EFIAPI efi_update_capsule_runtime(
+		struct efi_capsule_header **capsule_header_array,
+		efi_uintn_t capsule_count,
+		u64 scatter_gather_list)
+{
+	return EFI_UNSUPPORTED;
+}
+
+efi_status_t EFIAPI efi_query_capsule_caps_runtime(
+		struct efi_capsule_header **capsule_header_array,
+		efi_uintn_t capsule_count,
+		u64 *maximum_capsule_size,
+		u32 *reset_type)
+{
+	return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_capsule_boot_exit_notify() - notify ExitBootServices() is called
+ */
+void efi_capsule_boot_exit_notify(void)
+{
+	efi_runtime_services.update_capsule = efi_update_capsule_runtime;
+	efi_runtime_services.query_capsule_caps =
+				efi_query_capsule_caps_runtime;
+	efi_update_table_header_crc32(&efi_runtime_services.hdr);
+}
+#else
+/*
+ * Dummy functions for runtime services
+ */
+efi_status_t EFIAPI efi_update_capsule(
+		struct efi_capsule_header **capsule_header_array,
+		efi_uintn_t capsule_count,
+		u64 scatter_gather_list)
+{
+	return EFI_UNSUPPORTED;
+}
+
+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)
+{
+	return EFI_UNSUPPORTED;
+}
+#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index c485cad34022..309defc5e40d 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -96,6 +96,10 @@ static efi_status_t efi_init_os_indications(void)
 #ifdef CONFIG_EFI_CAPSULE_UPDATE
 	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,
@@ -196,6 +200,8 @@ efi_status_t efi_init_obj_list(void)
 	if (ret != EFI_SUCCESS)
 		goto out;
 
+	/* Execute capsules after reboot */
+	ret = efi_launch_capsules();
 out:
 	efi_obj_list_initialized = ret;
 	return ret;
-- 
2.25.1

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

* [RFC 07/14] efi_loader: capsule: add memory range capsule definitions
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (5 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 06/14] efi_loader: capsule: add capsule_on_disk support AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  8:11   ` Heinrich Schuchardt
  2020-03-17  2:12 ` [RFC 08/14] efi_loader: capsule: support firmware update AKASHI Takahiro
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 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 ac2b38801c0c..b7bf21cac7ad 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.1

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

* [RFC 08/14] efi_loader: capsule: support firmware update
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (6 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 07/14] efi_loader: capsule: add memory range capsule definitions AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-18 14:09   ` Sughosh Ganu
  2020-03-17  2:12 ` [RFC 09/14] efi_loader: add simple firmware management protocol for FIT image AKASHI Takahiro
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 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, installing drivers is not supported yet.

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            | 127 +++++++++++++++++++++++++++
 lib/efi_loader/Kconfig       |  12 +++
 lib/efi_loader/efi_capsule.c | 165 +++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_setup.c   |   4 +
 4 files changed, 308 insertions(+)

diff --git a/include/efi_api.h b/include/efi_api.h
index b7bf21cac7ad..e103369186a2 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
@@ -1683,4 +1713,101 @@ struct efi_unicode_collation_protocol {
 #define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MIN 0x00001000
 #define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MAX 0x00004000
 
+/*
+ * Firmware management protocol
+ */
+#define EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID \
+	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
+#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/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 95e10f7d981b..43d6f75d557a 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -97,6 +97,18 @@ config EFI_CAPSULE_UPDATE
 	  Select this option if you want to use capsule update feature,
 	  including firmware updates and variable updates.
 
+
+if EFI_CAPSULE_UPDATE
+
+config EFI_CAPSULE_UPDATE_FIRMWARE
+	bool "Capsule based firmware update"
+	default n
+	help
+	  Select this option if you want to enable capsule-based
+	  firmware update
+
+endif
+
 config EFI_CAPSULE_ON_DISK
 	bool "Enable capsule-on-disk support"
 	depends on EFI_CAPSULE_UPDATE
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index f3e2a555a6b9..f3526beed681 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -14,10 +14,164 @@
 #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;
 
 /* for file system access */
 static struct efi_file_handle *bootdev_root;
 
+#ifdef CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE
+/*
+ * 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 = 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 = 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_firmware_management_capsule_header *capsule)
+{
+	struct efi_firmware_management_capsule_image_header *image;
+	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;
+
+	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++) {
+		image = (struct efi_firmware_management_capsule_image_header *)
+			((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 = 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_firmware_management_capsule_header *capsule)
+{
+	return EFI_UNSUPPORTED;
+}
+#endif /* CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE */
+
 /*
  * Launch a capsule
  */
@@ -54,6 +208,17 @@ 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)) {
+		EFI_PRINT("EFI Capsule (guid:%pUl)\n", &capsule->capsule_guid);
+		if (!guidcmp(&capsule->capsule_guid,
+			     &efi_guid_firmware_management_capsule_id))
+			ret  = efi_capsule_update_firmware(
+					(struct efi_firmware_management_capsule_header *)
+					((void *)capsule + sizeof(*capsule)));
+		else
+			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 309defc5e40d..ca2d03c44610 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -100,6 +100,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_UPDATE_FIRMWARE
+	os_indications_supported |=
+			EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED;
 #endif
 	return EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
 					 &efi_global_variable_guid,
-- 
2.25.1

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

* [RFC 09/14] efi_loader: add simple firmware management protocol for FIT image
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (7 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 08/14] efi_loader: capsule: support firmware update AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-18  8:04   ` Heinrich Schuchardt
  2020-03-17  2:12 ` [RFC 10/14] efi_loader: capsule: support variable update AKASHI Takahiro
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 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_loader.h          |   3 +
 lib/efi_loader/Kconfig        |  24 ++++-
 lib/efi_loader/Makefile       |   1 +
 lib/efi_loader/efi_firmware.c | 191 ++++++++++++++++++++++++++++++++++
 4 files changed, 218 insertions(+), 1 deletion(-)
 create mode 100644 lib/efi_loader/efi_firmware.c

diff --git a/include/efi_loader.h b/include/efi_loader.h
index c701672e18db..79bdf9586d24 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -134,6 +134,7 @@ extern const struct efi_hii_config_access_protocol efi_hii_config_access;
 extern const struct efi_hii_database_protocol efi_hii_database;
 extern const struct efi_hii_string_protocol efi_hii_string;
 extern const struct efi_rng_protocol efi_rng_protocol;
+extern const struct efi_firmware_management_protocol efi_fmp_fit;
 
 uint16_t *efi_dp_str(struct efi_device_path *dp);
 
@@ -180,6 +181,8 @@ extern const efi_guid_t efi_guid_hii_database_protocol;
 extern const efi_guid_t efi_guid_hii_string_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;
 
 /* GUID of RNG protocol */
 extern const efi_guid_t efi_guid_rng_protocol;
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 43d6f75d557a..41b1e9b5543c 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -97,7 +97,6 @@ config EFI_CAPSULE_UPDATE
 	  Select this option if you want to use capsule update feature,
 	  including firmware updates and variable updates.
 
-
 if EFI_CAPSULE_UPDATE
 
 config EFI_CAPSULE_UPDATE_FIRMWARE
@@ -107,6 +106,29 @@ config EFI_CAPSULE_UPDATE_FIRMWARE
 	  Select this option if you want to enable capsule-based
 	  firmware update
 
+config EFI_CAPSULE_FIT_SIMPLE
+	bool "Firmware management protocol for simple FIT image"
+	depends on EFI_CAPSULE_UPDATE_FIRMWARE
+	depends on FIT
+	select UPDATE_TFTP
+	select DFU
+	default n
+	help
+	  Select this option if you want to enable firmware management protocol
+	  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 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 for storing FIT image
+
 endif
 
 config EFI_CAPSULE_ON_DISK
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index f19096924bef..50da10e0e3d9 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_CAPSULE_UPDATE) += 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..021c93196242
--- /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) 2018 Linaro Limited
+ *			Author: AKASHI Takahiro
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <net.h>
+
+/*
+ * This FIMRWARE_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.1

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

* [RFC 10/14] efi_loader: capsule: support variable update
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (8 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 09/14] efi_loader: add simple firmware management protocol for FIT image AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  2:12 ` [RFC 11/14] efi_loader: variable: export variables table for runtime access AKASHI Takahiro
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 UTC (permalink / raw)
  To: u-boot

See EBBR specification v1.0.

A capsule tagged with the guid, EFI_VARIABLE_STORAGE_GUID, will be handled
as a variable update object.
What efi_update_capsule() basically does is to re-play SetVariable
against each variable entry in a capsule.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_api.h            | 18 +++++++++++++++
 lib/efi_loader/Kconfig       |  7 ++++++
 lib/efi_loader/efi_capsule.c | 45 ++++++++++++++++++++++++++++++++++++
 3 files changed, 70 insertions(+)

diff --git a/include/efi_api.h b/include/efi_api.h
index e103369186a2..30807942380b 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -229,6 +229,10 @@ enum efi_reset_type {
 	EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \
 		 0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a)
 
+#define EFI_VARIABLE_STORAGE_GUID \
+	EFI_GUID(0x1a3fb419, 0x2171, 0x458d, 0xb8, 0xb4, \
+		 0xbe, 0xa3, 0x0c, 0x9f, 0x6b, 0xab)
+
 struct efi_capsule_header {
 	efi_guid_t capsule_guid;
 	u32 header_size;
@@ -283,6 +287,20 @@ struct efi_capsule_result_variable_fmp {
 	// u16 capsule_target[];
 } __packed;
 
+struct efi_ebbr_variable {
+	u16 variable_name[64];
+	efi_guid_t vendor_guid;
+	u32 attributes;
+	u32 data_size;
+	u8 data[];
+};
+
+struct efi_ebbr_variable_bundle {
+	struct efi_capsule_header header;
+	u8 reserved[0];
+	struct efi_ebbr_variable variables[];
+} __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/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 41b1e9b5543c..616e2acbe102 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -129,6 +129,13 @@ config EFI_CAPSULE_FIT_DEVICE
 	help
 	  Define storage device for storing FIT image
 
+config EFI_CAPSULE_UPDATE_VARIABLE
+	bool "Capsule based variable update"
+	default n
+	help
+	  Select this option if you want to enable capsule-based
+	  variable update support
+
 endif
 
 config EFI_CAPSULE_ON_DISK
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index f3526beed681..1293270aea95 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -18,6 +18,7 @@ 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;
+static const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
 
 /* for file system access */
 static struct efi_file_handle *bootdev_root;
@@ -172,6 +173,45 @@ static efi_status_t efi_capsule_update_firmware(
 }
 #endif /* CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE */
 
+#ifdef CONFIG_EFI_CAPSULE_UPDATE_VARIABLE
+/*
+ * Execute a log of variable changes
+ */
+static efi_status_t
+efi_capsule_update_variables(struct efi_ebbr_variable_bundle *bundle)
+{
+	struct efi_ebbr_variable *variable;
+	efi_status_t ret = EFI_SUCCESS;
+
+	for (variable = (void *)bundle + bundle->header.header_size;
+	     (void *)variable
+		< ((void *)bundle + bundle->header.capsule_image_size);
+	     variable = (struct efi_ebbr_variable *)
+			((void *)variable
+			 + sizeof(*variable) + variable->data_size)) {
+		ret = efi_set_variable(variable->variable_name,
+				       &variable->vendor_guid,
+				       variable->attributes,
+				       variable->data_size,
+				       &variable->data);
+		/* Should NOT_FOUND always be treated as success? */
+		if (ret == EFI_NOT_FOUND)
+			ret = EFI_SUCCESS;
+		EFI_PRINT("Capsule variable update %s: %ls\n",
+			  ret == EFI_SUCCESS ? "succeeded" : "failed",
+			  variable->variable_name);
+	}
+
+	return ret;
+}
+#else
+static efi_status_t
+efi_capsule_update_variables(struct efi_ebbr_variable_bundle *bundle)
+{
+	return EFI_UNSUPPORTED;
+}
+#endif /* CONFIG_EFI_CAPSULE_UPDATE_VARIABLE */
+
 /*
  * Launch a capsule
  */
@@ -214,6 +254,11 @@ efi_status_t EFIAPI efi_update_capsule(
 			ret  = efi_capsule_update_firmware(
 					(struct efi_firmware_management_capsule_header *)
 					((void *)capsule + sizeof(*capsule)));
+		else if (!guidcmp(&capsule->capsule_guid,
+				  &efi_guid_variable_storage))
+			ret = efi_capsule_update_variables(
+					(struct efi_ebbr_variable_bundle *)
+					capsule);
 		else
 			ret = EFI_UNSUPPORTED;
 
-- 
2.25.1

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

* [RFC 11/14] efi_loader: variable: export variables table for runtime access
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (9 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 10/14] efi_loader: capsule: support variable update AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  7:37   ` Heinrich Schuchardt
  2020-03-18 13:54   ` Sughosh Ganu
  2020-03-17  2:12 ` [RFC 12/14] cmd: add "efidebug capsule" command AKASHI Takahiro
                   ` (4 subsequent siblings)
  15 siblings, 2 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 UTC (permalink / raw)
  To: u-boot

There are platforms on which OS's won't be able to access UEFI variables
at runtime (i.e. after ExitBootServices).
With this patch, all the UEFI variables are exported as a configuration
table so as to enable retrieving variables' values from the table, and
later modifying them via capsule-on-disk if necessary.

The idea is based on Peter's proposal[1] in boot-arch ML.

[1] https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_loader.h          |   7 +++
 lib/efi_loader/Kconfig        |  10 ++++
 lib/efi_loader/efi_boottime.c |  10 ++++
 lib/efi_loader/efi_capsule.c  |   4 ++
 lib/efi_loader/efi_variable.c | 109 ++++++++++++++++++++++++++++++++++
 5 files changed, 140 insertions(+)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 79bdf9586d24..93ed5502821c 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -183,6 +183,8 @@ extern const efi_guid_t efi_guid_hii_string_protocol;
 extern const efi_guid_t efi_guid_capsule_report;
 /* GUID of firmware management protocol */
 extern const efi_guid_t efi_guid_firmware_management_protocol;
+/* GUID of runtime variable access */
+extern const efi_guid_t efi_guid_variable_storage;
 
 /* GUID of RNG protocol */
 extern const efi_guid_t efi_guid_rng_protocol;
@@ -659,6 +661,10 @@ efi_status_t EFIAPI efi_query_variable_info(
 			u64 *remaining_variable_storage_size,
 			u64 *maximum_variable_size);
 
+#ifdef CONFIG_EFI_VARIABLE_EXPORT
+efi_status_t efi_install_variables_table(void);
+#endif
+
 /*
  * See section 3.1.3 in the v2.7 UEFI spec for more details on
  * the layout of EFI_LOAD_OPTION.  In short it is:
@@ -683,6 +689,7 @@ struct efi_load_option {
 void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data);
 unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
 efi_status_t efi_bootmgr_load(efi_handle_t *handle);
+efi_status_t efi_install_variables_table(void);
 
 /* Capsule update */
 efi_status_t EFIAPI efi_update_capsule(
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 616e2acbe102..edb8d19059ea 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -41,6 +41,16 @@ 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_VARIABLE_EXPORT
+	bool "Export variables table as configuration table"
+	depends on !EFI_VARIABLE_RUNTIME_ACCESS
+	depends on  EFI_CAPSULE_UPDATE_VARIABLE
+	default y
+	help
+	  Select this option if you want to export UEFI variables as
+	  configuration table so that OS can still get UEFI variable's
+	  value.
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index c2a789b4f910..406644e4da67 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -2898,6 +2898,16 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
 	if (ret != EFI_SUCCESS)
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
 
+#ifdef CONFIG_EFI_VARIABLE_EXPORT
+	/* Install variables database as a configuration table */
+	ret = efi_install_variables_table();
+	if (ret != EFI_SUCCESS)
+		return EFI_EXIT(ret);
+#endif
+	/*
+	 * TODO: remove a table after image is terminated.
+	 */
+
 	image_obj->exit_data_size = exit_data_size;
 	image_obj->exit_data = exit_data;
 
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index 1293270aea95..6b737aec1b28 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -18,7 +18,11 @@ 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_VARIABLE_EXPORT
+const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
+#else
 static const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
+#endif
 
 /* for file system access */
 static struct efi_file_handle *bootdev_root;
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index c316bdfec0e4..270e5211f633 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -641,3 +641,112 @@ efi_status_t efi_init_variables(void)
 {
 	return EFI_SUCCESS;
 }
+
+#ifdef CONFIG_EFI_VARIABLE_EXPORT
+/**
+ * efi_install_variables_table() - install variables table
+ *
+ * This function adds a configuration table that contains all the cache
+ * data of UEFI variables for runtime access.
+ *
+ * Return:	status code
+ */
+efi_status_t efi_install_variables_table(void)
+{
+	u16 var_name16[64];
+	efi_uintn_t var_name16_len;
+	efi_guid_t guid;
+	u32 attributes;
+	struct efi_capsule_header capsule;
+	u8 *data = NULL, *p;
+	efi_uintn_t capsule_size;
+	struct efi_ebbr_variable variable;
+	u8 *var_buf;
+	efi_uintn_t var_size, var_max_size;
+	efi_status_t ret;
+
+	capsule.capsule_guid = efi_guid_variable_storage;
+	capsule.header_size = sizeof(capsule);
+	capsule.flags = CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE;
+	capsule_size = sizeof(capsule);
+
+	/* Determine size of capsule */
+	var_name16[0] = 0;
+	var_name16_len = 64;
+	var_buf = NULL;
+	var_max_size = 0;
+	for (;;) {
+		ret = EFI_CALL(efi_get_next_variable_name(&var_name16_len,
+							  var_name16,
+							  &guid));
+		if (ret == EFI_NOT_FOUND)
+			break;
+		else if (ret != EFI_SUCCESS)
+			goto out;
+
+		var_size = 0;
+		ret = EFI_CALL(efi_get_variable(var_name16, &guid, &attributes,
+						&var_size, var_buf));
+		if (ret != EFI_BUFFER_TOO_SMALL)
+			goto out;
+
+		capsule_size += sizeof(variable) + var_size;
+		if (var_size > var_max_size)
+			var_max_size = var_size;
+	}
+	capsule.capsule_image_size = capsule_size;
+
+	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
+				capsule_size, (void **)&data);
+	if (ret != EFI_SUCCESS)
+		return EFI_OUT_OF_RESOURCES;
+
+	var_buf = malloc(var_max_size);
+	if (!var_buf) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
+	}
+
+	/* Copy data */
+	p = data;
+	memcpy(p, &capsule, sizeof(capsule));
+	p += sizeof(capsule);
+
+	var_name16[0] = 0;
+	for (;;) {
+		ret = EFI_CALL(efi_get_next_variable_name(&var_name16_len,
+							  var_name16, &guid));
+		if (ret == EFI_NOT_FOUND)
+			break;
+		else if (ret != EFI_SUCCESS)
+			goto out;
+
+		var_size = var_max_size;
+		ret = EFI_CALL(efi_get_variable(var_name16, &guid, &attributes,
+						&var_size, var_buf));
+		if (ret != EFI_SUCCESS)
+			/* should not happen */
+			goto out;
+
+		u16_strcpy(variable.variable_name, var_name16);
+		variable.vendor_guid = guid;
+		variable.attributes = attributes;
+		variable.data_size = var_size;
+
+		memcpy(p, &variable, sizeof(variable));
+		p += sizeof(variable);
+		memcpy(p, var_buf, var_size);
+		p += var_size;
+	}
+
+	ret = EFI_CALL(efi_install_configuration_table(
+				&efi_guid_variable_storage, data));
+
+out:
+	free(var_buf);
+	if (ret != EFI_SUCCESS && data)
+		efi_free_pool(data);
+
+	return ret;
+}
+#endif /* CONFIG_EFI_VARIABLE_EXPORT */
-- 
2.25.1

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

* [RFC 12/14] cmd: add "efidebug capsule" command
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (10 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 11/14] efi_loader: variable: export variables table for runtime access AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  2:12 ` [RFC 13/14] tools: add mkeficapsule command for UEFI capsule update test AKASHI Takahiro
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 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 header of a capsule
  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 3a50dafbbca6..604f4b42e9ad 100644
--- a/cmd/efidebug.c
+++ b/cmd/efidebug.c
@@ -18,6 +18,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
  *
@@ -1079,6 +1300,10 @@ static int do_efi_boot_opt(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,
@@ -1149,6 +1374,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.1

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

* [RFC 13/14] tools: add mkeficapsule command for UEFI capsule update test
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (11 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 12/14] cmd: add "efidebug capsule" command AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  7:58   ` Heinrich Schuchardt
  2020-03-17  2:12 ` [RFC 14/14] test/py: add efi capsule test AKASHI Takahiro
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 UTC (permalink / raw)
  To: u-boot

This is a utility mainly for test purpose.
  mkeficapsule -f: create a FIT image firmware capsule
  mkeficapsule -v: create a specific test case capsule for variables

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 | 501 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 504 insertions(+)
 create mode 100644 tools/mkeficapsule.c

diff --git a/tools/Makefile b/tools/Makefile
index 99be724b82a5..fe07f8952f49 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_CAPSULE_UPDATE) += 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..732a54811df9
--- /dev/null
+++ b/tools/mkeficapsule.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2018 Linaro Limited
+ *		Author: AKASHI Takahiro
+ */
+
+#include <malloc.h>
+#include <stdio.h>
+#include <string.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)
+
+#define EFI_VARIABLE_STORAGE_GUID \
+	EFI_GUID(0x1a3fb419, 0x2171, 0x458d, 0xb8, 0xb4, \
+		 0xbe, 0xa3, 0x0c, 0x9f, 0x6b, 0xab)
+
+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));
+
+struct efi_ebbr_variable {
+	u16 variable_name[64];
+	efi_guid_t vendor_guid;
+	u32 attributes;
+	u32 data_size;
+	u8 data[];
+};
+
+struct efi_ebbr_variable_bundle {
+	struct efi_capsule_header header;
+	u8 reserved[0];
+	struct efi_ebbr_variable variables[];
+} __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;
+efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
+efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
+
+/* generated by efi boot add 3 TEST scsi 0:0 /EFI/Images/hello.efi "" */
+char boot_var3[] = "0100000052005400450053005400000001041400b9731de684a3cc4aaeab82e828f3628b0302080000000000040432005c005c004500460049005c0049006d0061006700650073005c00680065006c006c006f002e0065006600690000007fff040000";
+u16 boot_order[] = {
+	0x0003,
+	0x0002,
+};
+
+/* efi_variable.c */
+static int hex(int ch)
+{
+	if (ch >= 'a' && ch <= 'f')
+		return ch - 'a' + 10;
+	if (ch >= '0' && ch <= '9')
+		return ch - '0';
+	if (ch >= 'A' && ch <= 'F')
+		return ch - 'A' + 10;
+	return -1;
+}
+
+static int hex2mem(u8 *mem, const char *hexstr, int size)
+{
+	int nibble;
+	int i;
+
+	for (i = 0; i < size; i++) {
+		if (*hexstr == '\0')
+			break;
+
+		nibble = hex(*hexstr);
+		if (nibble < 0)
+			return -1;
+
+		*mem = nibble;
+		hexstr++;
+
+		nibble = hex(*hexstr);
+		if (nibble < 0)
+			return -1;
+
+		*mem = (*mem << 4) | nibble;
+		hexstr++;
+		mem++;
+	}
+
+	return i;
+}
+
+void wcharcpy(u16 *dst, wchar_t *src, size_t n)
+{
+	int i;
+
+	for (i = 0; i < n; i++)
+		*dst++ = *src++;
+}
+
+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;
+}
+
+static int create_test1(char *path)
+{
+	FILE *f;
+	size_t size;
+	struct efi_capsule_header header;
+	struct efi_ebbr_variable variable;
+	u8 *data;
+	size_t data_size;
+
+	f = fopen(path, "w");
+	if (!f) {
+		printf("cannot open %s\n", path);
+		return -1;
+	}
+	header.capsule_guid = efi_guid_variable_storage;
+	header.header_size = sizeof(header);
+	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
+	header.capsule_image_size = sizeof(header);
+
+	size = fwrite(&header, 1, sizeof(header), f);
+	if (size < sizeof(header)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+
+	/* add a new variable */
+	data_size = sizeof(boot_var3) / 2;
+	data = malloc(data_size);
+	if (!data) {
+		printf("memory allocation failed\n");
+		return -1;
+	}
+	hex2mem(data, boot_var3, data_size);
+	wcharcpy(variable.variable_name, L"Boot0003", 9);
+	variable.vendor_guid = efi_global_variable_guid;
+	variable.attributes = (EFI_VARIABLE_NON_VOLATILE |
+				EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				EFI_VARIABLE_RUNTIME_ACCESS);
+	variable.data_size = data_size;
+	header.capsule_image_size += sizeof(variable) + variable.data_size;
+
+	size = fwrite(&variable, 1, sizeof(variable), f);
+	if (size < sizeof(variable)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+	size = fwrite(data, 1, data_size, f);
+	if (size < data_size) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+
+	/* update capsule image size */
+	rewind(f);
+	size = fwrite(&header, 1, sizeof(header), f);
+	if (size < sizeof(header)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+
+	fclose(f);
+
+	return 0;
+}
+
+static int create_test2(char *path)
+{
+	FILE *f;
+	size_t size;
+	struct efi_capsule_header header;
+	struct efi_ebbr_variable variable;
+
+	f = fopen(path, "w");
+	if (!f) {
+		printf("cannot open %s\n", path);
+		return -1;
+	}
+	header.capsule_guid = efi_guid_variable_storage;
+	header.header_size = sizeof(header);
+	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
+	header.capsule_image_size = sizeof(header);
+
+	size = fwrite(&header, 1, sizeof(header), f);
+	if (size < sizeof(header)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+
+	/* modify an existing variable */
+	wcharcpy(variable.variable_name, L"BootOrder", 10);
+	variable.vendor_guid = efi_global_variable_guid;
+	variable.attributes = (EFI_VARIABLE_NON_VOLATILE |
+				EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				EFI_VARIABLE_RUNTIME_ACCESS);
+	variable.data_size = sizeof(boot_order);
+	header.capsule_image_size += sizeof(variable) + variable.data_size;
+
+	size = fwrite(&variable, 1, sizeof(variable), f);
+	if (size < sizeof(variable)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+	size = fwrite(boot_order, 1, sizeof(boot_order), f);
+	if (size < sizeof(boot_order)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+
+	/* delete an existing variable */
+	wcharcpy(variable.variable_name, L"Boot0001", 9);
+	variable.vendor_guid = efi_global_variable_guid;
+	variable.attributes = (EFI_VARIABLE_NON_VOLATILE |
+				EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				EFI_VARIABLE_RUNTIME_ACCESS);
+	variable.data_size = 0;
+	header.capsule_image_size += sizeof(variable) + variable.data_size;
+
+	size = fwrite(&variable, 1, sizeof(variable), f);
+	if (size < sizeof(variable)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+
+	/* update capsule image size */
+	rewind(f);
+	size = fwrite(&header, 1, sizeof(header), f);
+	if (size < sizeof(header)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+
+	fclose(f);
+
+	return 0;
+}
+
+static int create_test3(char *path)
+{
+	FILE *f;
+	size_t size;
+	struct efi_capsule_header header;
+	struct efi_ebbr_variable variable;
+
+	f = fopen(path, "w");
+	if (!f) {
+		printf("cannot open %s\n", path);
+		return -1;
+	}
+	header.capsule_guid = efi_guid_variable_storage;
+	header.header_size = sizeof(header);
+	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
+	header.capsule_image_size = sizeof(header);
+
+	size = fwrite(&header, 1, sizeof(header), f);
+	if (size < sizeof(header)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+
+	/* delete a non-existing variable */
+	wcharcpy(variable.variable_name, L"FooVar", 9);
+	variable.vendor_guid = efi_global_variable_guid;
+	variable.attributes = (EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				EFI_VARIABLE_RUNTIME_ACCESS);
+	variable.data_size = 0;
+	header.capsule_image_size += sizeof(variable) + variable.data_size;
+
+	size = fwrite(&variable, 1, sizeof(variable), f);
+	if (size < sizeof(variable)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+
+	/* update capsule image size */
+	rewind(f);
+	size = fwrite(&header, 1, sizeof(header), f);
+	if (size < sizeof(header)) {
+		printf("write failed (%lx)\n", size);
+		return -1;
+	}
+
+	fclose(f);
+
+	return 0;
+}
+
+void print_usage(void)
+{
+	printf("mkeficapsule [-f <firmware binary> | -t <variable test no>]\n");
+}
+
+/*
+ * Usage:
+ *   $ mkeficapsule [-f <firmware binary> | -t <variable test no>] <output file>
+ */
+int main(int argc, char **argv)
+{
+	int test_no;
+
+	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;
+	} else if (!strcmp(argv[1], "-v")) {
+		test_no = atoi(argv[2]);
+
+		switch (test_no) {
+		case 1:
+			if (create_test1(argv[3]) < 0) {
+				printf("Creating test%02x failed\n", test_no);
+				return -1;
+			}
+			break;
+		case 2:
+			if (create_test2(argv[3]) < 0) {
+				printf("Creating test%02x failed\n", test_no);
+				return -1;
+			}
+			break;
+		case 3:
+			if (create_test3(argv[3]) < 0) {
+				printf("Creating test%02x failed\n", test_no);
+				return -1;
+			}
+			break;
+		default:
+			printf("Invalid test number: %s\n", argv[2]);
+			return -1;
+		}
+
+		return 0;
+	}
+
+	print_usage();
+	return -1;
+}
-- 
2.25.1

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

* [RFC 14/14] test/py: add efi capsule test
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (12 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 13/14] tools: add mkeficapsule command for UEFI capsule update test AKASHI Takahiro
@ 2020-03-17  2:12 ` AKASHI Takahiro
  2020-03-17  7:49 ` [RFC 00/14] efi_loader: add capsule update support Heinrich Schuchardt
  2020-03-18 18:16 ` Sughosh Ganu
  15 siblings, 0 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-17  2:12 UTC (permalink / raw)
  To: u-boot

We will have two pytest scenarios for capsule update here.
  * for firmware update (based on simple firmware management protocol)
  * for variable update

Both can run on sandbox build and gives you some idea about how
they should work on production system.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 test/py/tests/test_efi_capsule/conftest.py    | 109 ++++++++++++++
 test/py/tests/test_efi_capsule/defs.py        |  21 +++
 .../test_efi_capsule/test_capsule_firmware.py | 102 +++++++++++++
 .../test_efi_capsule/test_capsule_variable.py | 141 ++++++++++++++++++
 test/py/tests/test_efi_capsule/uboot_env.its  |  25 ++++
 5 files changed, 398 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/test_capsule_variable.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..5d50df95cdf0
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -0,0 +1,109 @@
+# 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 *
+
+# from test/py/conftest.py
+def tool_is_in_path(tool):
+    for path in os.environ["PATH"].split(os.pathsep):
+        fn = os.path.join(path, tool)
+        if os.path.isfile(fn) and os.access(fn, os.X_OK):
+            return True
+    return False
+
+#
+# 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
+    image_size = EFI_BOOTDEV_IMAGE_SIZE
+    part_size = EFI_BOOTDEV_PART_SIZE
+    fs_type = EFI_BOOTDEV_FS_TYPE
+
+    try:
+        # non_root = tool_is_in_path('udisksctl')
+        non_root = False
+
+        # create a disk/partition
+        check_call('dd if=/dev/zero of=%s bs=1MiB count=%d'
+                            % (image_path, image_size), shell=True)
+        check_call('sgdisk %s -n 1:0:+%dMiB'
+                            % (image_path, part_size), shell=True)
+        # create a file system
+        check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d'
+                            % (image_path, part_size), shell=True)
+        check_call('mkfs -t %s %s.tmp' % (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)
+        if non_root:
+            out_data = check_output('udisksctl loop-setup -f %s -o %d'
+                                % (image_path, 1048576), shell=True).decode()
+            m = re.search('(?<= as )(.*)\.', out_data)
+            loop_dev = m.group(1)
+            # print('loop device is: %s' % loop_dev)
+            out_data = check_output('udisksctl info -b %s'
+                                % loop_dev, shell=True).decode()
+            m = re.search('MountPoints:[ \t]+(.*)', out_data)
+            mnt_point = m.group(1)
+        else:
+            loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"'
+                                % (part_size, image_path), shell=True).decode()
+            mnt_point = '/mnt'
+            check_call('sudo mount -t %s -o umask=000 %s %s'
+                                % (fs_type, loop_dev, mnt_point), shell=True)
+
+        # print('1: mount point is: %s' % mnt_point)
+
+        # Add variable capsules
+        check_call('mkdir -p %s%s' % (mnt_point, CAPSULE_DATA_DIR), shell=True)
+        check_call('mkdir -p %s%s' % (mnt_point, CAPSULE_INSTALL_DIR), shell=True)
+        check_call('%s/tools/mkeficapsule -v 1 %s%s/tESt01'
+                                 % (u_boot_config.build_dir, mnt_point,
+                                    CAPSULE_DATA_DIR), shell=True)
+        check_call('%s/tools/mkeficapsule -v 2 %s%s/Test02'
+                                 % (u_boot_config.build_dir,
+                                    mnt_point, CAPSULE_DATA_DIR), shell=True)
+        check_call('%s/tools/mkeficapsule -v 3 %s%s/TeST03'
+                                 % (u_boot_config.build_dir,
+                                    mnt_point, CAPSULE_DATA_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_point, CAPSULE_DATA_DIR, FW_BIN,
+                                   u_boot_config.source_dir,
+                                   mnt_point, CAPSULE_DATA_DIR), shell=True)
+
+        if non_root:
+            call('udisksctl unmount -b %s' % loop_dev, shell=True)
+            # not needed
+            # check_call('udisksctl loop-delete -b %s' % loop_dev, shell=True)
+        else:
+            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:
+        print('1: mount point is: %s' % mnt_point)
+        # call('rm -f %s' % image_path, 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..c2083b59c19f
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/defs.py
@@ -0,0 +1,21 @@
+# 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'
+
+# 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..52f8fa108ebd
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
@@ -0,0 +1,102 @@
+# 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
+
+ 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_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
+        """
+        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 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),
+                'setenv -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0000',
+                'fatls host 0:1 %s' % CAPSULE_DATA_DIR
+                ])
+            assert(('%s' % FW_BIN) in ''.join(output))
+
+
+        try:
+            part_size = EFI_BOOTDEV_PART_SIZE
+            fs_type = EFI_BOOTDEV_FS_TYPE
+            mnt_point = '/mnt'
+
+            loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"'
+                        % (part_size, disk_img), shell=True).decode()
+            check_call('sudo mount -t %s -o umask=000 %s %s'
+                        % (fs_type, loop_dev, mnt_point), shell=True)
+            check_call('%s/tools/mkimage -f %s%s/uboot_env.its %s%s/uboot_env.itb'
+                        % (u_boot_config.build_dir,
+                           mnt_point, CAPSULE_DATA_DIR,
+                           mnt_point, 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_point, CAPSULE_DATA_DIR,
+                           mnt_point, 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:
+            assert('failed to create firmware capsule')
+
+        u_boot_console.restart_uboot()
+
+        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)
+
+            output = u_boot_console.run_command('env print -e -all Capsule0000')
+            # assert('Capsule0000 not found' in output)
+
+            # output = u_boot_console.run_command('printenv -e -all Capsule0000')
+            # assert('00000000: 01' in output)
+
+        u_boot_console.restart_uboot()
+
+        with u_boot_console.log.section('Test Case 1-c, after reboot'):
+            output = u_boot_console.run_command('env print FW_STATUS')
+            assert('New environment' in ''.join(output))
+
+            output = u_boot_console.run_command(
+                'fatls host 0:1 %s/Test01' % CAPSULE_INSTALL_DIR)
+            assert('' == output)
diff --git a/test/py/tests/test_efi_capsule/test_capsule_variable.py b/test/py/tests/test_efi_capsule/test_capsule_variable.py
new file mode 100644
index 000000000000..baf4329e9a27
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/test_capsule_variable.py
@@ -0,0 +1,141 @@
+# SPDX-License-Identifier:      GPL-2.0+
+# Copyright (c) 2020, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Capsule Update for Variables Test
+
+"""
+This test verifies capsule-on-disk variable update
+"""
+
+import pytest
+import re
+from defs import *
+
+ at pytest.mark.boardspec('sandbox')
+ at pytest.mark.buildconfigspec('efi_capsule_update_variable')
+ at pytest.mark.buildconfigspec('efi_capsule_on_disk')
+ at pytest.mark.buildconfigspec('cmd_fat')
+ at pytest.mark.buildconfigspec('cmd_nvedit_efi')
+ at pytest.mark.slow
+class TestEfiCapsuleVariable(object):
+    def test_efi_capsule_var1(self, u_boot_console, efi_capsule_data):
+        """
+        Test Case 1 - Add a new UEFI variable
+        """
+        u_boot_console.restart_uboot()
+
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 1, 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',
+                'setenv -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0000',
+                'setenv -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0001',
+                'setenv -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0002',
+                'setenv -e Boot0003',
+                'saveenv',
+                'fatload host 0:1 0x5000000 %s/tESt01' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 0x5000000 %s/tESt01 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert('tESt01' in ''.join(output))
+
+        u_boot_console.restart_uboot()
+
+        with u_boot_console.log.section('Test Case 1, after reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e Boot0003'])
+            assert('Boot0003:' in ''.join(output))
+
+            output = u_boot_console.run_command(
+                'printenv -e -all')
+
+            output = u_boot_console.run_command(
+                'efidebug capsule result')
+            assert('CapsuleLast is' in output)
+            assert('Capsule status: 0x0' in output)
+
+            output = u_boot_console.run_command(
+                'fatls host 0:1 %s/tESt01' % CAPSULE_INSTALL_DIR)
+            u_boot_console.run_command('fatrm host 0:1 %s/tESt01'
+                % CAPSULE_INSTALL_DIR)
+            assert('' == output)
+
+    def test_efi_capsule_var2(self, u_boot_console, efi_capsule_data):
+        """
+        Test Case 2 - Modify/Delete existing variables
+        """
+        u_boot_console.restart_uboot()
+
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 2, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'efidebug boot add 1 TEST1 host 0:1 /helloworld.efi ""',
+                'efidebug boot add 2 TEST2 host 0:1 /helloworld.efi ""',
+                'efidebug boot order 1 2',
+                'saveenv',
+                'fatload host 0:1 0x5000000 %s/Test02' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 0x5000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert('Test02' in ''.join(output))
+
+        u_boot_console.restart_uboot()
+
+        with u_boot_console.log.section('Test Case 2, after reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e BootOrder'])
+            assert('00000000: 03 00 02 00' in ''.join(output))
+
+            output = u_boot_console.run_command(
+                'printenv -e Boot0001')
+            assert('"Boot0001" not defined' in output)
+
+            output = u_boot_console.run_command(
+                'efidebug capsule result')
+            assert('CapsuleLast is' in output)
+            assert('Capsule status: 0x0' in output)
+
+            output = u_boot_console.run_command(
+                'fatls host 0:1 %s/Test02' % CAPSULE_INSTALL_DIR)
+            u_boot_console.run_command('fatrm host 0:1 %s/Test02' % CAPSULE_INSTALL_DIR)
+            assert('' == ''.join(output))
+
+    def test_efi_capsule_var3(self, u_boot_console, efi_capsule_data):
+        """
+        Test Case 3 - Modify a non-existing variable
+        """
+        u_boot_console.restart_uboot()
+
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 3, 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',
+                'saveenv',
+                'fatload host 0:1 0x5000000 %s/TeST03' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 0x5000000 %s/TeST03 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert('TeST03' in ''.join(output))
+
+        u_boot_console.restart_uboot()
+
+        with u_boot_console.log.section('Test Case 3, after reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e FooVar'])
+            assert('"FooVar" not defined' in ''.join(output))
+
+            output = u_boot_console.run_command(
+                'efidebug capsule result')
+            assert('CapsuleLast is' in output)
+            assert('Capsule status: 0x0' in output)
+
+            output = u_boot_console.run_command(
+                'fatls host 0:1 %s/TeST03' % CAPSULE_INSTALL_DIR)
+            u_boot_console.run_command('fatrm host 0:1 %s/TeST03' % CAPSULE_INSTALL_DIR)
+            assert('' == ''.join(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.1

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

* [RFC 01/14] efi_loader: define OsIndicationsSupported flags
  2020-03-17  2:12 ` [RFC 01/14] efi_loader: define OsIndicationsSupported flags AKASHI Takahiro
@ 2020-03-17  7:03   ` Heinrich Schuchardt
  2020-03-18  1:18     ` AKASHI Takahiro
  0 siblings, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-17  7:03 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> These flags are expected to be set in OsIndicationsSupported variable
> if corresponding features are supported. See UEFI specification,
> section 8.5.4.
>
> In particular, capsule-related flags will be used in my capsule
> update patch.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/efi_api.h | 11 +++++++++++
>   1 file changed, 11 insertions(+)
>
> diff --git a/include/efi_api.h b/include/efi_api.h
> index 22396172e15f..73c0722b0168 100644
> --- a/include/efi_api.h
> +++ b/include/efi_api.h
> @@ -57,6 +57,17 @@ typedef u16 efi_form_id_t;
>
>   struct efi_event;
>
> +/* OsIndicationsSupported flags */
> +#define EFI_OS_INDICATIONS_BOOT_TO_RW_UI		0x0000000000000001
This is called
EFI_OS_INDICATIONS_BOOT_TO_FW_UI
in the spec.

Otherwise

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

> +#define EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION		0x0000000000000002
> +#define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED \
> +							0x0000000000000004
> +#define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED	0x0000000000000008
> +#define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED	0x0000000000000010
> +#define EFI_OS_INDICATIONS_START_OS_RECOVERY		0x0000000000000020
> +#define EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY	0x0000000000000040
> +#define EFI_OS_INDICATIONS_JSON_CONFIG_DATA_REFRESH	0x0000000000000080
> +
>   /* EFI Boot Services table */
>   #define EFI_BOOT_SERVICES_SIGNATURE 0x56524553544f4f42
>   struct efi_boot_services {
>

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

* [RFC 02/14] efi_loader: define System Resource Table macros
  2020-03-17  2:12 ` [RFC 02/14] efi_loader: define System Resource Table macros AKASHI Takahiro
@ 2020-03-17  7:06   ` Heinrich Schuchardt
  2020-03-18 18:02     ` Heinrich Schuchardt
  0 siblings, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-17  7:06 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> Some of those values will be used in an implementation of UEFI firmware
> management protocol as part of my capsule update patch.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

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

> ---
>   include/efi_api.h | 27 +++++++++++++++++++++++++++
>   1 file changed, 27 insertions(+)
>
> diff --git a/include/efi_api.h b/include/efi_api.h
> index 73c0722b0168..a99cc7acc475 100644
> --- a/include/efi_api.h
> +++ b/include/efi_api.h
> @@ -1627,4 +1627,31 @@ struct efi_unicode_collation_protocol {
>   #define LOAD_OPTION_CATEGORY_BOOT	0x00000000
>   #define LOAD_OPTION_CATEGORY_APP	0x00000100
>
> +/*
> + * System Resource Table
> + */
> +/* Firmware Type Definitions */
> +#define ESRT_FW_TYPE_UNKNOWN		0x00000000
> +#define ESRT_FW_TYPE_SYSTEMFIRMWARE	0x00000001
> +#define ESRT_FW_TYPE_DEVICEFIRMWARE	0x00000002
> +#define ESRT_FW_TYPE_UEFIDRIVER		0x00000003
> +
> +/* Last Attempt Status Values */
> +#define LAST_ATTEMPT_STATUS_SUCCESS			0x00000000
> +#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL		0x00000001
> +#define LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES 0x00000002
> +#define LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION	0x00000003
> +#define LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT	0x00000004
> +#define LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR		0x00000005
> +#define LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_AC		0x00000006
> +#define LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_BATT		0x00000007
> +#define LAST_ATTEMPT_STATUS_ERROR_UNSATISFIED_DEPENDENCIES 0x00000008
> +
> +/*
> + * The LastAttemptStatus values of 0x1000 - 0x4000 are reserved for vendor
> + * usage.
> + */
> +#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MIN 0x00001000
> +#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MAX 0x00004000
> +
>   #endif
>

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

* [RFC 03/14] efi_loader: export a couple of protocol related functions
  2020-03-17  2:12 ` [RFC 03/14] efi_loader: export a couple of protocol related functions AKASHI Takahiro
@ 2020-03-17  7:19   ` Heinrich Schuchardt
  2020-03-18 18:03   ` Heinrich Schuchardt
  1 sibling, 0 replies; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-17  7:19 UTC (permalink / raw)
  To: u-boot

  On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> This is a preparatory patch.
> Those functions will be used in an implementation of UEFI firmware
> management protocol as part of my capsule update patch.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

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

> ---
>   include/efi_loader.h          | 14 ++++++++++++++
>   lib/efi_loader/efi_boottime.c | 16 ++++++++--------
>   2 files changed, 22 insertions(+), 8 deletions(-)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index d4c59b54c48b..c55bade6a508 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -456,6 +456,20 @@ efi_status_t efi_remove_all_protocols(const efi_handle_t handle);
>   /* Install multiple protocol interfaces */
>   efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
>   				(efi_handle_t *handle, ...);
> +/* Get handles that support a given protocol */
> +efi_status_t EFIAPI efi_locate_handle_buffer(
> +			enum efi_locate_search_type search_type,
> +			const efi_guid_t *protocol, void *search_key,
> +			efi_uintn_t *no_handles, efi_handle_t **buffer);
> +/* Close an previously opened protocol interface */
> +efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
> +				       const efi_guid_t *protocol,
> +				       efi_handle_t agent_handle,
> +				       efi_handle_t controller_handle);
> +/* Open a protocol interface */
> +efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
> +					const efi_guid_t *protocol,
> +					void **protocol_interface);
>   /* Call this to create an event */
>   efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
>   			      void (EFIAPI *notify_function) (
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 1f598b357a5c..9860d5047502 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -2106,10 +2106,10 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
>    *
>    * Return: status code
>    */
> -static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
> -					      const efi_guid_t *protocol,
> -					      efi_handle_t agent_handle,
> -					      efi_handle_t controller_handle)
> +efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
> +				       const efi_guid_t *protocol,
> +				       efi_handle_t agent_handle,
> +				       efi_handle_t controller_handle)
>   {
>   	struct efi_handler *handler;
>   	struct efi_open_protocol_info_item *item;
> @@ -2282,7 +2282,7 @@ static efi_status_t EFIAPI efi_protocols_per_handle(
>    *
>    * Return: status code
>    */
> -static efi_status_t EFIAPI efi_locate_handle_buffer(
> +efi_status_t EFIAPI efi_locate_handle_buffer(
>   			enum efi_locate_search_type search_type,
>   			const efi_guid_t *protocol, void *search_key,
>   			efi_uintn_t *no_handles, efi_handle_t **buffer)
> @@ -3182,9 +3182,9 @@ out:
>    *
>    * Return: status code
>    */
> -static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
> -					       const efi_guid_t *protocol,
> -					       void **protocol_interface)
> +efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
> +					const efi_guid_t *protocol,
> +					void **protocol_interface)
>   {
>   	return efi_open_protocol(handle, protocol, protocol_interface, efi_root,
>   				 NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
>

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

* [RFC 04/14] efi_loader: correct a definition of struct efi_capsule_header
  2020-03-17  2:12 ` [RFC 04/14] efi_loader: correct a definition of struct efi_capsule_header AKASHI Takahiro
@ 2020-03-17  7:25   ` Heinrich Schuchardt
  2020-03-18 18:03   ` Heinrich Schuchardt
  1 sibling, 0 replies; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-17  7:25 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> See UEFI specification, section 8.5.3.
> In addition, the structure, efi_capsule_header, should be "packed"
> as it is a serialized binary format in a capsule file.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

Fixes: 0c2307431e6e ("efi_loader: add missing runtime services stubs")
Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>

> ---
>   include/efi_api.h | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/include/efi_api.h b/include/efi_api.h
> index a99cc7acc475..4040c44855fa 100644
> --- a/include/efi_api.h
> +++ b/include/efi_api.h
> @@ -218,11 +218,11 @@ enum efi_reset_type {
>   #define CAPSULE_FLAGS_INITIATE_RESET		0x00040000
>
>   struct efi_capsule_header {
> -	efi_guid_t *capsule_guid;
> +	efi_guid_t capsule_guid;
>   	u32 header_size;
>   	u32 flags;
>   	u32 capsule_image_size;
> -};
> +} __packed;
>
>   #define EFI_RT_SUPPORTED_GET_TIME			0x0001
>   #define EFI_RT_SUPPORTED_SET_TIME			0x0002
>

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

* [RFC 11/14] efi_loader: variable: export variables table for runtime access
  2020-03-17  2:12 ` [RFC 11/14] efi_loader: variable: export variables table for runtime access AKASHI Takahiro
@ 2020-03-17  7:37   ` Heinrich Schuchardt
  2020-03-18  1:53     ` AKASHI Takahiro
  2020-03-18 13:54   ` Sughosh Ganu
  1 sibling, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-17  7:37 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> There are platforms on which OS's won't be able to access UEFI variables
> at runtime (i.e. after ExitBootServices).
> With this patch, all the UEFI variables are exported as a configuration
> table so as to enable retrieving variables' values from the table, and
> later modifying them via capsule-on-disk if necessary.

I do not understand why we should expose our internal memory for holding
UEFI variables to the operating system. This might end up in users
trying to access the variables directly.

I do not understand why we should not keep the pointer in our private
memory.

Best regards

Heinrich

>
> The idea is based on Peter's proposal[1] in boot-arch ML.
>
> [1] https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/efi_loader.h          |   7 +++
>   lib/efi_loader/Kconfig        |  10 ++++
>   lib/efi_loader/efi_boottime.c |  10 ++++
>   lib/efi_loader/efi_capsule.c  |   4 ++
>   lib/efi_loader/efi_variable.c | 109 ++++++++++++++++++++++++++++++++++
>   5 files changed, 140 insertions(+)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 79bdf9586d24..93ed5502821c 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -183,6 +183,8 @@ extern const efi_guid_t efi_guid_hii_string_protocol;
>   extern const efi_guid_t efi_guid_capsule_report;
>   /* GUID of firmware management protocol */
>   extern const efi_guid_t efi_guid_firmware_management_protocol;
> +/* GUID of runtime variable access */
> +extern const efi_guid_t efi_guid_variable_storage;
>
>   /* GUID of RNG protocol */
>   extern const efi_guid_t efi_guid_rng_protocol;
> @@ -659,6 +661,10 @@ efi_status_t EFIAPI efi_query_variable_info(
>   			u64 *remaining_variable_storage_size,
>   			u64 *maximum_variable_size);
>
> +#ifdef CONFIG_EFI_VARIABLE_EXPORT
> +efi_status_t efi_install_variables_table(void);
> +#endif
> +
>   /*
>    * See section 3.1.3 in the v2.7 UEFI spec for more details on
>    * the layout of EFI_LOAD_OPTION.  In short it is:
> @@ -683,6 +689,7 @@ struct efi_load_option {
>   void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data);
>   unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
>   efi_status_t efi_bootmgr_load(efi_handle_t *handle);
> +efi_status_t efi_install_variables_table(void);
>
>   /* Capsule update */
>   efi_status_t EFIAPI efi_update_capsule(
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 616e2acbe102..edb8d19059ea 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -41,6 +41,16 @@ 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_VARIABLE_EXPORT
> +	bool "Export variables table as configuration table"
> +	depends on !EFI_VARIABLE_RUNTIME_ACCESS
> +	depends on  EFI_CAPSULE_UPDATE_VARIABLE
> +	default y
> +	help
> +	  Select this option if you want to export UEFI variables as
> +	  configuration table so that OS can still get UEFI variable's
> +	  value.
> +
>   config EFI_DEVICE_PATH_TO_TEXT
>   	bool "Device path to text protocol"
>   	default y
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index c2a789b4f910..406644e4da67 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -2898,6 +2898,16 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
>   	if (ret != EFI_SUCCESS)
>   		return EFI_EXIT(EFI_INVALID_PARAMETER);
>
> +#ifdef CONFIG_EFI_VARIABLE_EXPORT
> +	/* Install variables database as a configuration table */
> +	ret = efi_install_variables_table();
> +	if (ret != EFI_SUCCESS)
> +		return EFI_EXIT(ret);
> +#endif
> +	/*
> +	 * TODO: remove a table after image is terminated.
> +	 */
> +
>   	image_obj->exit_data_size = exit_data_size;
>   	image_obj->exit_data = exit_data;
>
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> index 1293270aea95..6b737aec1b28 100644
> --- a/lib/efi_loader/efi_capsule.c
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -18,7 +18,11 @@ 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_VARIABLE_EXPORT
> +const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
> +#else
>   static const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
> +#endif
>
>   /* for file system access */
>   static struct efi_file_handle *bootdev_root;
> diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
> index c316bdfec0e4..270e5211f633 100644
> --- a/lib/efi_loader/efi_variable.c
> +++ b/lib/efi_loader/efi_variable.c
> @@ -641,3 +641,112 @@ efi_status_t efi_init_variables(void)
>   {
>   	return EFI_SUCCESS;
>   }
> +
> +#ifdef CONFIG_EFI_VARIABLE_EXPORT
> +/**
> + * efi_install_variables_table() - install variables table
> + *
> + * This function adds a configuration table that contains all the cache
> + * data of UEFI variables for runtime access.
> + *
> + * Return:	status code
> + */
> +efi_status_t efi_install_variables_table(void)
> +{
> +	u16 var_name16[64];
> +	efi_uintn_t var_name16_len;
> +	efi_guid_t guid;
> +	u32 attributes;
> +	struct efi_capsule_header capsule;
> +	u8 *data = NULL, *p;
> +	efi_uintn_t capsule_size;
> +	struct efi_ebbr_variable variable;
> +	u8 *var_buf;
> +	efi_uintn_t var_size, var_max_size;
> +	efi_status_t ret;
> +
> +	capsule.capsule_guid = efi_guid_variable_storage;
> +	capsule.header_size = sizeof(capsule);
> +	capsule.flags = CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE;
> +	capsule_size = sizeof(capsule);
> +
> +	/* Determine size of capsule */
> +	var_name16[0] = 0;
> +	var_name16_len = 64;
> +	var_buf = NULL;
> +	var_max_size = 0;
> +	for (;;) {
> +		ret = EFI_CALL(efi_get_next_variable_name(&var_name16_len,
> +							  var_name16,
> +							  &guid));
> +		if (ret == EFI_NOT_FOUND)
> +			break;
> +		else if (ret != EFI_SUCCESS)
> +			goto out;
> +
> +		var_size = 0;
> +		ret = EFI_CALL(efi_get_variable(var_name16, &guid, &attributes,
> +						&var_size, var_buf));
> +		if (ret != EFI_BUFFER_TOO_SMALL)
> +			goto out;
> +
> +		capsule_size += sizeof(variable) + var_size;
> +		if (var_size > var_max_size)
> +			var_max_size = var_size;
> +	}
> +	capsule.capsule_image_size = capsule_size;
> +
> +	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
> +				capsule_size, (void **)&data);
> +	if (ret != EFI_SUCCESS)
> +		return EFI_OUT_OF_RESOURCES;
> +
> +	var_buf = malloc(var_max_size);
> +	if (!var_buf) {
> +		ret = EFI_OUT_OF_RESOURCES;
> +		goto out;
> +	}
> +
> +	/* Copy data */
> +	p = data;
> +	memcpy(p, &capsule, sizeof(capsule));
> +	p += sizeof(capsule);
> +
> +	var_name16[0] = 0;
> +	for (;;) {
> +		ret = EFI_CALL(efi_get_next_variable_name(&var_name16_len,
> +							  var_name16, &guid));
> +		if (ret == EFI_NOT_FOUND)
> +			break;
> +		else if (ret != EFI_SUCCESS)
> +			goto out;
> +
> +		var_size = var_max_size;
> +		ret = EFI_CALL(efi_get_variable(var_name16, &guid, &attributes,
> +						&var_size, var_buf));
> +		if (ret != EFI_SUCCESS)
> +			/* should not happen */
> +			goto out;
> +
> +		u16_strcpy(variable.variable_name, var_name16);
> +		variable.vendor_guid = guid;
> +		variable.attributes = attributes;
> +		variable.data_size = var_size;
> +
> +		memcpy(p, &variable, sizeof(variable));
> +		p += sizeof(variable);
> +		memcpy(p, var_buf, var_size);
> +		p += var_size;
> +	}
> +
> +	ret = EFI_CALL(efi_install_configuration_table(
> +				&efi_guid_variable_storage, data));
> +
> +out:
> +	free(var_buf);
> +	if (ret != EFI_SUCCESS && data)
> +		efi_free_pool(data);
> +
> +	return ret;
> +}
> +#endif /* CONFIG_EFI_VARIABLE_EXPORT */
>

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

* [RFC 00/14] efi_loader: add capsule update support
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (13 preceding siblings ...)
  2020-03-17  2:12 ` [RFC 14/14] test/py: add efi capsule test AKASHI Takahiro
@ 2020-03-17  7:49 ` Heinrich Schuchardt
  2020-03-18  2:04   ` AKASHI Takahiro
  2020-03-18 18:16 ` Sughosh Ganu
  15 siblings, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-17  7:49 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> Summary
> =======
> 'UpdateCapsule' is one of runtime services defined in UEFI specification
> and its aim is to allow a caller to pass information to the firmware.
> This is mostly used to update firmware binary on devices by instructions
> from OS.
>
> In this patch series, all the related definitions and structures are given
> as UEFI specification describes and basic framework for capsule support is
> implemented. Currently supported types of capsule are
>   * firmware update (Firmware Management Protocol or simply FMP)
>   * variable update
>
> UpdateCapsule is a runtime services function, but is, at least initially,
> provided only before exiting boot services alike other runtime functions.
> This is because modifying storage which may be shared with OS must be
> carefully designed and there is no general assumption to do that as in
> the case of [Get/]SetVariable.
> Instead, any capsule can be handed over to the firmware as a file on
> a specific file system. In other words, we only support "capsules on disk"
> for now.
>
> Regarding firmware update, most of functionality is provided by FMP
> driver and it will be by nature system/platform-specific. So you can and
> should implement FMP drivers based on your system requirements.
> In this patch series, only a simple FMP driver based on FIT image for
> a single region is supported.  (So it is "for reference only")
> See more details in "efi_loader: capsule: add simple firmware management
> protocol."
>
> Regarding variable update, the implementation here is based on a draft
> proposal[1] by Peter in Boot-arch ML. The specification should be discussed
> and finalized first. So the code doesn't fully implement Peter's idea.
>
> [1] https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html

I do not like the idea of exposing variables as a configuration table as
operating system users may start doing strange things like accessing
variables via the configuration table. Why not keep the pointer to the
internal storage of variable hidden in your runtime code and expose the
runtime services defined in the UEFI spec?

Please, elaborate on the benefit of your approach.

Best regards

Heirnich

>
> Patch structure
> ===============
> Patch#1-#4: preparatory patches
> Patch#5-#11: main part of implementation
> Patch#12-#14: utilities and tests
>
> Test
> ====
> * passed all the pytests which are included in this patch series
>    on sandbox build.
> * passed Travis CI.
>
> Currently, my pytest requires the following prerequisite patch to
> run without errors:
> [2] https://lists.denx.de/pipermail/u-boot/2020-March/401726.html
> [3] https://lists.denx.de/pipermail/u-boot/2020-March/401727.html
>
> In addition, you have to enable CONFIG_ENV_IS_IN_SPI_FLASH and
> CONFIG_SPI_FLASH_SANDBOX to successfully run the test.
>
> 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.
>
> TODO's
> ======
> (May or 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
> =======
> Initial release as RFC (March 17, 2020)
>
> AKASHI Takahiro (14):
>    efi_loader: define OsIndicationsSupported flags
>    efi_loader: define System Resource Table macros
>    efi_loader: export a couple of protocol related functions
>    efi_loader: correct a definition of struct efi_capsule_header
>    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
>    efi_loader: capsule: support variable update
>    efi_loader: variable: export variables table for runtime access
>    cmd: add "efidebug capsule" command
>    tools: add mkeficapsule command for UEFI capsule update test
>    test/py: add efi capsule test
>
>   cmd/efidebug.c                                | 234 +++++
>   include/efi_api.h                             | 214 ++++-
>   include/efi_loader.h                          |  53 ++
>   lib/efi_loader/Kconfig                        |  65 ++
>   lib/efi_loader/Makefile                       |   2 +
>   lib/efi_loader/efi_boottime.c                 |  29 +-
>   lib/efi_loader/efi_capsule.c                  | 860 ++++++++++++++++++
>   lib/efi_loader/efi_firmware.c                 | 191 ++++
>   lib/efi_loader/efi_runtime.c                  | 104 ++-
>   lib/efi_loader/efi_setup.c                    |  41 +-
>   lib/efi_loader/efi_variable.c                 | 109 +++
>   test/py/tests/test_efi_capsule/conftest.py    | 109 +++
>   test/py/tests/test_efi_capsule/defs.py        |  21 +
>   .../test_efi_capsule/test_capsule_firmware.py | 102 +++
>   .../test_efi_capsule/test_capsule_variable.py | 141 +++
>   test/py/tests/test_efi_capsule/uboot_env.its  |  25 +
>   tools/Makefile                                |   3 +
>   tools/mkeficapsule.c                          | 501 ++++++++++
>   18 files changed, 2744 insertions(+), 60 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/test_capsule_variable.py
>   create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its
>   create mode 100644 tools/mkeficapsule.c
>

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

* [RFC 13/14] tools: add mkeficapsule command for UEFI capsule update test
  2020-03-17  2:12 ` [RFC 13/14] tools: add mkeficapsule command for UEFI capsule update test AKASHI Takahiro
@ 2020-03-17  7:58   ` Heinrich Schuchardt
  2020-03-18  1:32     ` AKASHI Takahiro
  0 siblings, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-17  7:58 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> This is a utility mainly for test purpose.
>    mkeficapsule -f: create a FIT image firmware capsule

The UEFI spec defines a capsule format and how to invoke it by placing
the file in the right directory before reboot. Why do we need a FIT
image firmware capsule?

Best regards

Heinrich

>    mkeficapsule -v: create a specific test case capsule for variables
>
> 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 | 501 +++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 504 insertions(+)
>   create mode 100644 tools/mkeficapsule.c
>
> diff --git a/tools/Makefile b/tools/Makefile
> index 99be724b82a5..fe07f8952f49 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_CAPSULE_UPDATE) += 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..732a54811df9
> --- /dev/null
> +++ b/tools/mkeficapsule.c
> @@ -0,0 +1,501 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2018 Linaro Limited
> + *		Author: AKASHI Takahiro
> + */
> +
> +#include <malloc.h>
> +#include <stdio.h>
> +#include <string.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)
> +
> +#define EFI_VARIABLE_STORAGE_GUID \
> +	EFI_GUID(0x1a3fb419, 0x2171, 0x458d, 0xb8, 0xb4, \
> +		 0xbe, 0xa3, 0x0c, 0x9f, 0x6b, 0xab)
> +
> +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));
> +
> +struct efi_ebbr_variable {
> +	u16 variable_name[64];
> +	efi_guid_t vendor_guid;
> +	u32 attributes;
> +	u32 data_size;
> +	u8 data[];
> +};
> +
> +struct efi_ebbr_variable_bundle {
> +	struct efi_capsule_header header;
> +	u8 reserved[0];
> +	struct efi_ebbr_variable variables[];
> +} __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;
> +efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
> +efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
> +
> +/* generated by efi boot add 3 TEST scsi 0:0 /EFI/Images/hello.efi "" */
> +char boot_var3[] = "0100000052005400450053005400000001041400b9731de684a3cc4aaeab82e828f3628b0302080000000000040432005c005c004500460049005c0049006d0061006700650073005c00680065006c006c006f002e0065006600690000007fff040000";
> +u16 boot_order[] = {
> +	0x0003,
> +	0x0002,
> +};
> +
> +/* efi_variable.c */
> +static int hex(int ch)
> +{
> +	if (ch >= 'a' && ch <= 'f')
> +		return ch - 'a' + 10;
> +	if (ch >= '0' && ch <= '9')
> +		return ch - '0';
> +	if (ch >= 'A' && ch <= 'F')
> +		return ch - 'A' + 10;
> +	return -1;
> +}
> +
> +static int hex2mem(u8 *mem, const char *hexstr, int size)
> +{
> +	int nibble;
> +	int i;
> +
> +	for (i = 0; i < size; i++) {
> +		if (*hexstr == '\0')
> +			break;
> +
> +		nibble = hex(*hexstr);
> +		if (nibble < 0)
> +			return -1;
> +
> +		*mem = nibble;
> +		hexstr++;
> +
> +		nibble = hex(*hexstr);
> +		if (nibble < 0)
> +			return -1;
> +
> +		*mem = (*mem << 4) | nibble;
> +		hexstr++;
> +		mem++;
> +	}
> +
> +	return i;
> +}
> +
> +void wcharcpy(u16 *dst, wchar_t *src, size_t n)
> +{
> +	int i;
> +
> +	for (i = 0; i < n; i++)
> +		*dst++ = *src++;
> +}
> +
> +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;
> +}
> +
> +static int create_test1(char *path)
> +{
> +	FILE *f;
> +	size_t size;
> +	struct efi_capsule_header header;
> +	struct efi_ebbr_variable variable;
> +	u8 *data;
> +	size_t data_size;
> +
> +	f = fopen(path, "w");
> +	if (!f) {
> +		printf("cannot open %s\n", path);
> +		return -1;
> +	}
> +	header.capsule_guid = efi_guid_variable_storage;
> +	header.header_size = sizeof(header);
> +	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
> +	header.capsule_image_size = sizeof(header);
> +
> +	size = fwrite(&header, 1, sizeof(header), f);
> +	if (size < sizeof(header)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +
> +	/* add a new variable */
> +	data_size = sizeof(boot_var3) / 2;
> +	data = malloc(data_size);
> +	if (!data) {
> +		printf("memory allocation failed\n");
> +		return -1;
> +	}
> +	hex2mem(data, boot_var3, data_size);
> +	wcharcpy(variable.variable_name, L"Boot0003", 9);
> +	variable.vendor_guid = efi_global_variable_guid;
> +	variable.attributes = (EFI_VARIABLE_NON_VOLATILE |
> +				EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +				EFI_VARIABLE_RUNTIME_ACCESS);
> +	variable.data_size = data_size;
> +	header.capsule_image_size += sizeof(variable) + variable.data_size;
> +
> +	size = fwrite(&variable, 1, sizeof(variable), f);
> +	if (size < sizeof(variable)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +	size = fwrite(data, 1, data_size, f);
> +	if (size < data_size) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +
> +	/* update capsule image size */
> +	rewind(f);
> +	size = fwrite(&header, 1, sizeof(header), f);
> +	if (size < sizeof(header)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +
> +	fclose(f);
> +
> +	return 0;
> +}
> +
> +static int create_test2(char *path)
> +{
> +	FILE *f;
> +	size_t size;
> +	struct efi_capsule_header header;
> +	struct efi_ebbr_variable variable;
> +
> +	f = fopen(path, "w");
> +	if (!f) {
> +		printf("cannot open %s\n", path);
> +		return -1;
> +	}
> +	header.capsule_guid = efi_guid_variable_storage;
> +	header.header_size = sizeof(header);
> +	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
> +	header.capsule_image_size = sizeof(header);
> +
> +	size = fwrite(&header, 1, sizeof(header), f);
> +	if (size < sizeof(header)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +
> +	/* modify an existing variable */
> +	wcharcpy(variable.variable_name, L"BootOrder", 10);
> +	variable.vendor_guid = efi_global_variable_guid;
> +	variable.attributes = (EFI_VARIABLE_NON_VOLATILE |
> +				EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +				EFI_VARIABLE_RUNTIME_ACCESS);
> +	variable.data_size = sizeof(boot_order);
> +	header.capsule_image_size += sizeof(variable) + variable.data_size;
> +
> +	size = fwrite(&variable, 1, sizeof(variable), f);
> +	if (size < sizeof(variable)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +	size = fwrite(boot_order, 1, sizeof(boot_order), f);
> +	if (size < sizeof(boot_order)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +
> +	/* delete an existing variable */
> +	wcharcpy(variable.variable_name, L"Boot0001", 9);
> +	variable.vendor_guid = efi_global_variable_guid;
> +	variable.attributes = (EFI_VARIABLE_NON_VOLATILE |
> +				EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +				EFI_VARIABLE_RUNTIME_ACCESS);
> +	variable.data_size = 0;
> +	header.capsule_image_size += sizeof(variable) + variable.data_size;
> +
> +	size = fwrite(&variable, 1, sizeof(variable), f);
> +	if (size < sizeof(variable)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +
> +	/* update capsule image size */
> +	rewind(f);
> +	size = fwrite(&header, 1, sizeof(header), f);
> +	if (size < sizeof(header)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +
> +	fclose(f);
> +
> +	return 0;
> +}
> +
> +static int create_test3(char *path)
> +{
> +	FILE *f;
> +	size_t size;
> +	struct efi_capsule_header header;
> +	struct efi_ebbr_variable variable;
> +
> +	f = fopen(path, "w");
> +	if (!f) {
> +		printf("cannot open %s\n", path);
> +		return -1;
> +	}
> +	header.capsule_guid = efi_guid_variable_storage;
> +	header.header_size = sizeof(header);
> +	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
> +	header.capsule_image_size = sizeof(header);
> +
> +	size = fwrite(&header, 1, sizeof(header), f);
> +	if (size < sizeof(header)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +
> +	/* delete a non-existing variable */
> +	wcharcpy(variable.variable_name, L"FooVar", 9);
> +	variable.vendor_guid = efi_global_variable_guid;
> +	variable.attributes = (EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +				EFI_VARIABLE_RUNTIME_ACCESS);
> +	variable.data_size = 0;
> +	header.capsule_image_size += sizeof(variable) + variable.data_size;
> +
> +	size = fwrite(&variable, 1, sizeof(variable), f);
> +	if (size < sizeof(variable)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +
> +	/* update capsule image size */
> +	rewind(f);
> +	size = fwrite(&header, 1, sizeof(header), f);
> +	if (size < sizeof(header)) {
> +		printf("write failed (%lx)\n", size);
> +		return -1;
> +	}
> +
> +	fclose(f);
> +
> +	return 0;
> +}
> +
> +void print_usage(void)
> +{
> +	printf("mkeficapsule [-f <firmware binary> | -t <variable test no>]\n");
> +}
> +
> +/*
> + * Usage:
> + *   $ mkeficapsule [-f <firmware binary> | -t <variable test no>] <output file>
> + */
> +int main(int argc, char **argv)
> +{
> +	int test_no;
> +
> +	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;
> +	} else if (!strcmp(argv[1], "-v")) {
> +		test_no = atoi(argv[2]);
> +
> +		switch (test_no) {
> +		case 1:
> +			if (create_test1(argv[3]) < 0) {
> +				printf("Creating test%02x failed\n", test_no);
> +				return -1;
> +			}
> +			break;
> +		case 2:
> +			if (create_test2(argv[3]) < 0) {
> +				printf("Creating test%02x failed\n", test_no);
> +				return -1;
> +			}
> +			break;
> +		case 3:
> +			if (create_test3(argv[3]) < 0) {
> +				printf("Creating test%02x failed\n", test_no);
> +				return -1;
> +			}
> +			break;
> +		default:
> +			printf("Invalid test number: %s\n", argv[2]);
> +			return -1;
> +		}
> +
> +		return 0;
> +	}
> +
> +	print_usage();
> +	return -1;
> +}
>

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

* [RFC 07/14] efi_loader: capsule: add memory range capsule definitions
  2020-03-17  2:12 ` [RFC 07/14] efi_loader: capsule: add memory range capsule definitions AKASHI Takahiro
@ 2020-03-17  8:11   ` Heinrich Schuchardt
  2020-03-18  1:22     ` AKASHI Takahiro
  0 siblings, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-17  8:11 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> 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.

There are multiple places where RAM availability is tested by writing
during board bring up. Please, describe which changes are needed in ATF
and U-Boot to make memory range capsules work reliably.

Consider also memory encryption provided by some CPUs.
https://www.tomshardware.com/news/intel-mktme-amd-memory-encryption,39467.html

Aren't file based capsules a much more reliable way for the OS to
communicate to the firmware?

Best regards

Heinrich

>
> 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 ac2b38801c0c..b7bf21cac7ad 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
>

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

* [RFC 01/14] efi_loader: define OsIndicationsSupported flags
  2020-03-17  7:03   ` Heinrich Schuchardt
@ 2020-03-18  1:18     ` AKASHI Takahiro
  2020-03-18 18:01       ` Heinrich Schuchardt
  0 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-18  1:18 UTC (permalink / raw)
  To: u-boot

On Tue, Mar 17, 2020 at 08:03:21AM +0100, Heinrich Schuchardt wrote:
> On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > These flags are expected to be set in OsIndicationsSupported variable
> > if corresponding features are supported. See UEFI specification,
> > section 8.5.4.
> > 
> > In particular, capsule-related flags will be used in my capsule
> > update patch.
> > 
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >   include/efi_api.h | 11 +++++++++++
> >   1 file changed, 11 insertions(+)
> > 
> > diff --git a/include/efi_api.h b/include/efi_api.h
> > index 22396172e15f..73c0722b0168 100644
> > --- a/include/efi_api.h
> > +++ b/include/efi_api.h
> > @@ -57,6 +57,17 @@ typedef u16 efi_form_id_t;
> > 
> >   struct efi_event;
> > 
> > +/* OsIndicationsSupported flags */
> > +#define EFI_OS_INDICATIONS_BOOT_TO_RW_UI		0x0000000000000001
> This is called
> EFI_OS_INDICATIONS_BOOT_TO_FW_UI
> in the spec.

Yes, it's a typo.

Thanks,
-Takahiro Akashi

> 
> Otherwise
> 
> Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
> 
> > +#define EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION		0x0000000000000002
> > +#define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED \
> > +							0x0000000000000004
> > +#define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED	0x0000000000000008
> > +#define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED	0x0000000000000010
> > +#define EFI_OS_INDICATIONS_START_OS_RECOVERY		0x0000000000000020
> > +#define EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY	0x0000000000000040
> > +#define EFI_OS_INDICATIONS_JSON_CONFIG_DATA_REFRESH	0x0000000000000080
> > +
> >   /* EFI Boot Services table */
> >   #define EFI_BOOT_SERVICES_SIGNATURE 0x56524553544f4f42
> >   struct efi_boot_services {
> > 
> 

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

* [RFC 07/14] efi_loader: capsule: add memory range capsule definitions
  2020-03-17  8:11   ` Heinrich Schuchardt
@ 2020-03-18  1:22     ` AKASHI Takahiro
  2020-03-18  7:35       ` Heinrich Schuchardt
  0 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-18  1:22 UTC (permalink / raw)
  To: u-boot

On Tue, Mar 17, 2020 at 09:11:43AM +0100, Heinrich Schuchardt wrote:
> On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > 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.
> 
> There are multiple places where RAM availability is tested by writing
> during board bring up. Please, describe which changes are needed in ATF
> and U-Boot to make memory range capsules work reliably.

I think that they are quite system/platform-specific
and that it's beyond the scope of this patch.

Thanks,
-Takahiro Akashi


> Consider also memory encryption provided by some CPUs.
> https://www.tomshardware.com/news/intel-mktme-amd-memory-encryption,39467.html
> 
> Aren't file based capsules a much more reliable way for the OS to
> communicate to the firmware?
> 
> Best regards
> 
> Heinrich
> 
> > 
> > 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 ac2b38801c0c..b7bf21cac7ad 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
> > 
> 

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

* [RFC 13/14] tools: add mkeficapsule command for UEFI capsule update test
  2020-03-17  7:58   ` Heinrich Schuchardt
@ 2020-03-18  1:32     ` AKASHI Takahiro
  2020-03-19  8:55       ` Ilias Apalodimas
  0 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-18  1:32 UTC (permalink / raw)
  To: u-boot

On Tue, Mar 17, 2020 at 08:58:55AM +0100, Heinrich Schuchardt wrote:
> On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > This is a utility mainly for test purpose.
> >    mkeficapsule -f: create a FIT image firmware capsule
> 
> The UEFI spec defines a capsule format and how to invoke it by placing
> the file in the right directory before reboot. Why do we need a FIT
> image firmware capsule?

??? I don't get your point.

A capsule is just a wrapper that can hold any kind (or format) of
binary data. In fact, an associated capsule driver should know
how the data should correctly be handled on U-Boot.

So if you provide your own driver (more specifically, "firmware
management protocol" for firmware update), you can encapsulate
any firmware in your proprietary format.

In this RFC, I provide a *sample* driver for a single FIT image
in patch#9.

Thanks,
-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> >    mkeficapsule -v: create a specific test case capsule for variables
> > 
> > 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 | 501 +++++++++++++++++++++++++++++++++++++++++++
> >   2 files changed, 504 insertions(+)
> >   create mode 100644 tools/mkeficapsule.c
> > 
> > diff --git a/tools/Makefile b/tools/Makefile
> > index 99be724b82a5..fe07f8952f49 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_CAPSULE_UPDATE) += 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..732a54811df9
> > --- /dev/null
> > +++ b/tools/mkeficapsule.c
> > @@ -0,0 +1,501 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright 2018 Linaro Limited
> > + *		Author: AKASHI Takahiro
> > + */
> > +
> > +#include <malloc.h>
> > +#include <stdio.h>
> > +#include <string.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)
> > +
> > +#define EFI_VARIABLE_STORAGE_GUID \
> > +	EFI_GUID(0x1a3fb419, 0x2171, 0x458d, 0xb8, 0xb4, \
> > +		 0xbe, 0xa3, 0x0c, 0x9f, 0x6b, 0xab)
> > +
> > +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));
> > +
> > +struct efi_ebbr_variable {
> > +	u16 variable_name[64];
> > +	efi_guid_t vendor_guid;
> > +	u32 attributes;
> > +	u32 data_size;
> > +	u8 data[];
> > +};
> > +
> > +struct efi_ebbr_variable_bundle {
> > +	struct efi_capsule_header header;
> > +	u8 reserved[0];
> > +	struct efi_ebbr_variable variables[];
> > +} __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;
> > +efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
> > +efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
> > +
> > +/* generated by efi boot add 3 TEST scsi 0:0 /EFI/Images/hello.efi "" */
> > +char boot_var3[] = "0100000052005400450053005400000001041400b9731de684a3cc4aaeab82e828f3628b0302080000000000040432005c005c004500460049005c0049006d0061006700650073005c00680065006c006c006f002e0065006600690000007fff040000";
> > +u16 boot_order[] = {
> > +	0x0003,
> > +	0x0002,
> > +};
> > +
> > +/* efi_variable.c */
> > +static int hex(int ch)
> > +{
> > +	if (ch >= 'a' && ch <= 'f')
> > +		return ch - 'a' + 10;
> > +	if (ch >= '0' && ch <= '9')
> > +		return ch - '0';
> > +	if (ch >= 'A' && ch <= 'F')
> > +		return ch - 'A' + 10;
> > +	return -1;
> > +}
> > +
> > +static int hex2mem(u8 *mem, const char *hexstr, int size)
> > +{
> > +	int nibble;
> > +	int i;
> > +
> > +	for (i = 0; i < size; i++) {
> > +		if (*hexstr == '\0')
> > +			break;
> > +
> > +		nibble = hex(*hexstr);
> > +		if (nibble < 0)
> > +			return -1;
> > +
> > +		*mem = nibble;
> > +		hexstr++;
> > +
> > +		nibble = hex(*hexstr);
> > +		if (nibble < 0)
> > +			return -1;
> > +
> > +		*mem = (*mem << 4) | nibble;
> > +		hexstr++;
> > +		mem++;
> > +	}
> > +
> > +	return i;
> > +}
> > +
> > +void wcharcpy(u16 *dst, wchar_t *src, size_t n)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < n; i++)
> > +		*dst++ = *src++;
> > +}
> > +
> > +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;
> > +}
> > +
> > +static int create_test1(char *path)
> > +{
> > +	FILE *f;
> > +	size_t size;
> > +	struct efi_capsule_header header;
> > +	struct efi_ebbr_variable variable;
> > +	u8 *data;
> > +	size_t data_size;
> > +
> > +	f = fopen(path, "w");
> > +	if (!f) {
> > +		printf("cannot open %s\n", path);
> > +		return -1;
> > +	}
> > +	header.capsule_guid = efi_guid_variable_storage;
> > +	header.header_size = sizeof(header);
> > +	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
> > +	header.capsule_image_size = sizeof(header);
> > +
> > +	size = fwrite(&header, 1, sizeof(header), f);
> > +	if (size < sizeof(header)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +
> > +	/* add a new variable */
> > +	data_size = sizeof(boot_var3) / 2;
> > +	data = malloc(data_size);
> > +	if (!data) {
> > +		printf("memory allocation failed\n");
> > +		return -1;
> > +	}
> > +	hex2mem(data, boot_var3, data_size);
> > +	wcharcpy(variable.variable_name, L"Boot0003", 9);
> > +	variable.vendor_guid = efi_global_variable_guid;
> > +	variable.attributes = (EFI_VARIABLE_NON_VOLATILE |
> > +				EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +				EFI_VARIABLE_RUNTIME_ACCESS);
> > +	variable.data_size = data_size;
> > +	header.capsule_image_size += sizeof(variable) + variable.data_size;
> > +
> > +	size = fwrite(&variable, 1, sizeof(variable), f);
> > +	if (size < sizeof(variable)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +	size = fwrite(data, 1, data_size, f);
> > +	if (size < data_size) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +
> > +	/* update capsule image size */
> > +	rewind(f);
> > +	size = fwrite(&header, 1, sizeof(header), f);
> > +	if (size < sizeof(header)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +
> > +	fclose(f);
> > +
> > +	return 0;
> > +}
> > +
> > +static int create_test2(char *path)
> > +{
> > +	FILE *f;
> > +	size_t size;
> > +	struct efi_capsule_header header;
> > +	struct efi_ebbr_variable variable;
> > +
> > +	f = fopen(path, "w");
> > +	if (!f) {
> > +		printf("cannot open %s\n", path);
> > +		return -1;
> > +	}
> > +	header.capsule_guid = efi_guid_variable_storage;
> > +	header.header_size = sizeof(header);
> > +	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
> > +	header.capsule_image_size = sizeof(header);
> > +
> > +	size = fwrite(&header, 1, sizeof(header), f);
> > +	if (size < sizeof(header)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +
> > +	/* modify an existing variable */
> > +	wcharcpy(variable.variable_name, L"BootOrder", 10);
> > +	variable.vendor_guid = efi_global_variable_guid;
> > +	variable.attributes = (EFI_VARIABLE_NON_VOLATILE |
> > +				EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +				EFI_VARIABLE_RUNTIME_ACCESS);
> > +	variable.data_size = sizeof(boot_order);
> > +	header.capsule_image_size += sizeof(variable) + variable.data_size;
> > +
> > +	size = fwrite(&variable, 1, sizeof(variable), f);
> > +	if (size < sizeof(variable)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +	size = fwrite(boot_order, 1, sizeof(boot_order), f);
> > +	if (size < sizeof(boot_order)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +
> > +	/* delete an existing variable */
> > +	wcharcpy(variable.variable_name, L"Boot0001", 9);
> > +	variable.vendor_guid = efi_global_variable_guid;
> > +	variable.attributes = (EFI_VARIABLE_NON_VOLATILE |
> > +				EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +				EFI_VARIABLE_RUNTIME_ACCESS);
> > +	variable.data_size = 0;
> > +	header.capsule_image_size += sizeof(variable) + variable.data_size;
> > +
> > +	size = fwrite(&variable, 1, sizeof(variable), f);
> > +	if (size < sizeof(variable)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +
> > +	/* update capsule image size */
> > +	rewind(f);
> > +	size = fwrite(&header, 1, sizeof(header), f);
> > +	if (size < sizeof(header)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +
> > +	fclose(f);
> > +
> > +	return 0;
> > +}
> > +
> > +static int create_test3(char *path)
> > +{
> > +	FILE *f;
> > +	size_t size;
> > +	struct efi_capsule_header header;
> > +	struct efi_ebbr_variable variable;
> > +
> > +	f = fopen(path, "w");
> > +	if (!f) {
> > +		printf("cannot open %s\n", path);
> > +		return -1;
> > +	}
> > +	header.capsule_guid = efi_guid_variable_storage;
> > +	header.header_size = sizeof(header);
> > +	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */
> > +	header.capsule_image_size = sizeof(header);
> > +
> > +	size = fwrite(&header, 1, sizeof(header), f);
> > +	if (size < sizeof(header)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +
> > +	/* delete a non-existing variable */
> > +	wcharcpy(variable.variable_name, L"FooVar", 9);
> > +	variable.vendor_guid = efi_global_variable_guid;
> > +	variable.attributes = (EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +				EFI_VARIABLE_RUNTIME_ACCESS);
> > +	variable.data_size = 0;
> > +	header.capsule_image_size += sizeof(variable) + variable.data_size;
> > +
> > +	size = fwrite(&variable, 1, sizeof(variable), f);
> > +	if (size < sizeof(variable)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +
> > +	/* update capsule image size */
> > +	rewind(f);
> > +	size = fwrite(&header, 1, sizeof(header), f);
> > +	if (size < sizeof(header)) {
> > +		printf("write failed (%lx)\n", size);
> > +		return -1;
> > +	}
> > +
> > +	fclose(f);
> > +
> > +	return 0;
> > +}
> > +
> > +void print_usage(void)
> > +{
> > +	printf("mkeficapsule [-f <firmware binary> | -t <variable test no>]\n");
> > +}
> > +
> > +/*
> > + * Usage:
> > + *   $ mkeficapsule [-f <firmware binary> | -t <variable test no>] <output file>
> > + */
> > +int main(int argc, char **argv)
> > +{
> > +	int test_no;
> > +
> > +	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;
> > +	} else if (!strcmp(argv[1], "-v")) {
> > +		test_no = atoi(argv[2]);
> > +
> > +		switch (test_no) {
> > +		case 1:
> > +			if (create_test1(argv[3]) < 0) {
> > +				printf("Creating test%02x failed\n", test_no);
> > +				return -1;
> > +			}
> > +			break;
> > +		case 2:
> > +			if (create_test2(argv[3]) < 0) {
> > +				printf("Creating test%02x failed\n", test_no);
> > +				return -1;
> > +			}
> > +			break;
> > +		case 3:
> > +			if (create_test3(argv[3]) < 0) {
> > +				printf("Creating test%02x failed\n", test_no);
> > +				return -1;
> > +			}
> > +			break;
> > +		default:
> > +			printf("Invalid test number: %s\n", argv[2]);
> > +			return -1;
> > +		}
> > +
> > +		return 0;
> > +	}
> > +
> > +	print_usage();
> > +	return -1;
> > +}
> > 
> 

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

* [RFC 11/14] efi_loader: variable: export variables table for runtime access
  2020-03-17  7:37   ` Heinrich Schuchardt
@ 2020-03-18  1:53     ` AKASHI Takahiro
  2020-03-19  9:30       ` Ilias Apalodimas
  0 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-18  1:53 UTC (permalink / raw)
  To: u-boot

On Tue, Mar 17, 2020 at 08:37:47AM +0100, Heinrich Schuchardt wrote:
> On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > There are platforms on which OS's won't be able to access UEFI variables
> > at runtime (i.e. after ExitBootServices).
> > With this patch, all the UEFI variables are exported as a configuration
> > table so as to enable retrieving variables' values from the table, and
> > later modifying them via capsule-on-disk if necessary.
> 
> I do not understand why we should expose our internal memory for holding
> UEFI variables to the operating system. This might end up in users
> trying to access the variables directly.

I think that you somehow misunderstand my code as it never exposes
any "internal memory," although I don't know what it exactly means in
this context.
This configuration table is nothing but a list of data that represent
all the UEFI variables in implementation-agnostic format.

> I do not understand why we should not keep the pointer in our private
> memory.

Anyway, this patch naively implements Peter's proposal while I also
submitted another patch[1] that allows HL-OS to use GetVariable
interface directly via *caching*.

Since how we should enable accessing UEFI variables at runtime is
one of key issues, I'd rather discuss in boot-arch ML as I suggested
in the cover letter.
I have already re-activated the discussion there[2].
Please make your comments there for wider audience.

[1] https://lists.denx.de/pipermail/u-boot/2019-June/371769.html
[2] https://lists.linaro.org/pipermail/boot-architecture/2020-March/001149.html

Thanks,
-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> > 
> > The idea is based on Peter's proposal[1] in boot-arch ML.
> > 
> > [1] https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html
> > 
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >   include/efi_loader.h          |   7 +++
> >   lib/efi_loader/Kconfig        |  10 ++++
> >   lib/efi_loader/efi_boottime.c |  10 ++++
> >   lib/efi_loader/efi_capsule.c  |   4 ++
> >   lib/efi_loader/efi_variable.c | 109 ++++++++++++++++++++++++++++++++++
> >   5 files changed, 140 insertions(+)
> > 
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index 79bdf9586d24..93ed5502821c 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -183,6 +183,8 @@ extern const efi_guid_t efi_guid_hii_string_protocol;
> >   extern const efi_guid_t efi_guid_capsule_report;
> >   /* GUID of firmware management protocol */
> >   extern const efi_guid_t efi_guid_firmware_management_protocol;
> > +/* GUID of runtime variable access */
> > +extern const efi_guid_t efi_guid_variable_storage;
> > 
> >   /* GUID of RNG protocol */
> >   extern const efi_guid_t efi_guid_rng_protocol;
> > @@ -659,6 +661,10 @@ efi_status_t EFIAPI efi_query_variable_info(
> >   			u64 *remaining_variable_storage_size,
> >   			u64 *maximum_variable_size);
> > 
> > +#ifdef CONFIG_EFI_VARIABLE_EXPORT
> > +efi_status_t efi_install_variables_table(void);
> > +#endif
> > +
> >   /*
> >    * See section 3.1.3 in the v2.7 UEFI spec for more details on
> >    * the layout of EFI_LOAD_OPTION.  In short it is:
> > @@ -683,6 +689,7 @@ struct efi_load_option {
> >   void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data);
> >   unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
> >   efi_status_t efi_bootmgr_load(efi_handle_t *handle);
> > +efi_status_t efi_install_variables_table(void);
> > 
> >   /* Capsule update */
> >   efi_status_t EFIAPI efi_update_capsule(
> > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > index 616e2acbe102..edb8d19059ea 100644
> > --- a/lib/efi_loader/Kconfig
> > +++ b/lib/efi_loader/Kconfig
> > @@ -41,6 +41,16 @@ 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_VARIABLE_EXPORT
> > +	bool "Export variables table as configuration table"
> > +	depends on !EFI_VARIABLE_RUNTIME_ACCESS
> > +	depends on  EFI_CAPSULE_UPDATE_VARIABLE
> > +	default y
> > +	help
> > +	  Select this option if you want to export UEFI variables as
> > +	  configuration table so that OS can still get UEFI variable's
> > +	  value.
> > +
> >   config EFI_DEVICE_PATH_TO_TEXT
> >   	bool "Device path to text protocol"
> >   	default y
> > diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> > index c2a789b4f910..406644e4da67 100644
> > --- a/lib/efi_loader/efi_boottime.c
> > +++ b/lib/efi_loader/efi_boottime.c
> > @@ -2898,6 +2898,16 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
> >   	if (ret != EFI_SUCCESS)
> >   		return EFI_EXIT(EFI_INVALID_PARAMETER);
> > 
> > +#ifdef CONFIG_EFI_VARIABLE_EXPORT
> > +	/* Install variables database as a configuration table */
> > +	ret = efi_install_variables_table();
> > +	if (ret != EFI_SUCCESS)
> > +		return EFI_EXIT(ret);
> > +#endif
> > +	/*
> > +	 * TODO: remove a table after image is terminated.
> > +	 */
> > +
> >   	image_obj->exit_data_size = exit_data_size;
> >   	image_obj->exit_data = exit_data;
> > 
> > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> > index 1293270aea95..6b737aec1b28 100644
> > --- a/lib/efi_loader/efi_capsule.c
> > +++ b/lib/efi_loader/efi_capsule.c
> > @@ -18,7 +18,11 @@ 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_VARIABLE_EXPORT
> > +const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
> > +#else
> >   static const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
> > +#endif
> > 
> >   /* for file system access */
> >   static struct efi_file_handle *bootdev_root;
> > diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
> > index c316bdfec0e4..270e5211f633 100644
> > --- a/lib/efi_loader/efi_variable.c
> > +++ b/lib/efi_loader/efi_variable.c
> > @@ -641,3 +641,112 @@ efi_status_t efi_init_variables(void)
> >   {
> >   	return EFI_SUCCESS;
> >   }
> > +
> > +#ifdef CONFIG_EFI_VARIABLE_EXPORT
> > +/**
> > + * efi_install_variables_table() - install variables table
> > + *
> > + * This function adds a configuration table that contains all the cache
> > + * data of UEFI variables for runtime access.
> > + *
> > + * Return:	status code
> > + */
> > +efi_status_t efi_install_variables_table(void)
> > +{
> > +	u16 var_name16[64];
> > +	efi_uintn_t var_name16_len;
> > +	efi_guid_t guid;
> > +	u32 attributes;
> > +	struct efi_capsule_header capsule;
> > +	u8 *data = NULL, *p;
> > +	efi_uintn_t capsule_size;
> > +	struct efi_ebbr_variable variable;
> > +	u8 *var_buf;
> > +	efi_uintn_t var_size, var_max_size;
> > +	efi_status_t ret;
> > +
> > +	capsule.capsule_guid = efi_guid_variable_storage;
> > +	capsule.header_size = sizeof(capsule);
> > +	capsule.flags = CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE;
> > +	capsule_size = sizeof(capsule);
> > +
> > +	/* Determine size of capsule */
> > +	var_name16[0] = 0;
> > +	var_name16_len = 64;
> > +	var_buf = NULL;
> > +	var_max_size = 0;
> > +	for (;;) {
> > +		ret = EFI_CALL(efi_get_next_variable_name(&var_name16_len,
> > +							  var_name16,
> > +							  &guid));
> > +		if (ret == EFI_NOT_FOUND)
> > +			break;
> > +		else if (ret != EFI_SUCCESS)
> > +			goto out;
> > +
> > +		var_size = 0;
> > +		ret = EFI_CALL(efi_get_variable(var_name16, &guid, &attributes,
> > +						&var_size, var_buf));
> > +		if (ret != EFI_BUFFER_TOO_SMALL)
> > +			goto out;
> > +
> > +		capsule_size += sizeof(variable) + var_size;
> > +		if (var_size > var_max_size)
> > +			var_max_size = var_size;
> > +	}
> > +	capsule.capsule_image_size = capsule_size;
> > +
> > +	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
> > +				capsule_size, (void **)&data);
> > +	if (ret != EFI_SUCCESS)
> > +		return EFI_OUT_OF_RESOURCES;
> > +
> > +	var_buf = malloc(var_max_size);
> > +	if (!var_buf) {
> > +		ret = EFI_OUT_OF_RESOURCES;
> > +		goto out;
> > +	}
> > +
> > +	/* Copy data */
> > +	p = data;
> > +	memcpy(p, &capsule, sizeof(capsule));
> > +	p += sizeof(capsule);
> > +
> > +	var_name16[0] = 0;
> > +	for (;;) {
> > +		ret = EFI_CALL(efi_get_next_variable_name(&var_name16_len,
> > +							  var_name16, &guid));
> > +		if (ret == EFI_NOT_FOUND)
> > +			break;
> > +		else if (ret != EFI_SUCCESS)
> > +			goto out;
> > +
> > +		var_size = var_max_size;
> > +		ret = EFI_CALL(efi_get_variable(var_name16, &guid, &attributes,
> > +						&var_size, var_buf));
> > +		if (ret != EFI_SUCCESS)
> > +			/* should not happen */
> > +			goto out;
> > +
> > +		u16_strcpy(variable.variable_name, var_name16);
> > +		variable.vendor_guid = guid;
> > +		variable.attributes = attributes;
> > +		variable.data_size = var_size;
> > +
> > +		memcpy(p, &variable, sizeof(variable));
> > +		p += sizeof(variable);
> > +		memcpy(p, var_buf, var_size);
> > +		p += var_size;
> > +	}
> > +
> > +	ret = EFI_CALL(efi_install_configuration_table(
> > +				&efi_guid_variable_storage, data));
> > +
> > +out:
> > +	free(var_buf);
> > +	if (ret != EFI_SUCCESS && data)
> > +		efi_free_pool(data);
> > +
> > +	return ret;
> > +}
> > +#endif /* CONFIG_EFI_VARIABLE_EXPORT */
> > 
> 

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

* [RFC 00/14] efi_loader: add capsule update support
  2020-03-17  7:49 ` [RFC 00/14] efi_loader: add capsule update support Heinrich Schuchardt
@ 2020-03-18  2:04   ` AKASHI Takahiro
  2020-03-31  4:36     ` AKASHI Takahiro
  0 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-18  2:04 UTC (permalink / raw)
  To: u-boot

Heinrich,

Thank you for your quick review.

On Tue, Mar 17, 2020 at 08:49:12AM +0100, Heinrich Schuchardt wrote:
> On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > Summary
> > =======
> > 'UpdateCapsule' is one of runtime services defined in UEFI specification
> > and its aim is to allow a caller to pass information to the firmware.
> > This is mostly used to update firmware binary on devices by instructions
> > from OS.
> > 
> > In this patch series, all the related definitions and structures are given
> > as UEFI specification describes and basic framework for capsule support is
> > implemented. Currently supported types of capsule are
> >   * firmware update (Firmware Management Protocol or simply FMP)
> >   * variable update
> > 
> > UpdateCapsule is a runtime services function, but is, at least initially,
> > provided only before exiting boot services alike other runtime functions.
> > This is because modifying storage which may be shared with OS must be
> > carefully designed and there is no general assumption to do that as in
> > the case of [Get/]SetVariable.
> > Instead, any capsule can be handed over to the firmware as a file on
> > a specific file system. In other words, we only support "capsules on disk"
> > for now.
> > 
> > Regarding firmware update, most of functionality is provided by FMP
> > driver and it will be by nature system/platform-specific. So you can and
> > should implement FMP drivers based on your system requirements.
> > In this patch series, only a simple FMP driver based on FIT image for
> > a single region is supported.  (So it is "for reference only")
> > See more details in "efi_loader: capsule: add simple firmware management
> > protocol."
> > 
> > Regarding variable update, the implementation here is based on a draft
> > proposal[1] by Peter in Boot-arch ML. The specification should be discussed
> > and finalized first. So the code doesn't fully implement Peter's idea.
> > 
> > [1] https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html
> 
> I do not like the idea of exposing variables as a configuration table as
> operating system users may start doing strange things like accessing
> variables via the configuration table. Why not keep the pointer to the
> internal storage of variable hidden in your runtime code and expose the
> runtime services defined in the UEFI spec?
> 
> Please, elaborate on the benefit of your approach.

I have already commented on your reply to my patch#11.

Rather, my aim of this patch is to discuss the following points:

> > 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.

and

> > TODO's
> > ======
> > (May or 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

What functionality should be initially included in my first implementation
for capsule support.

FYI, Sughosh is going to work on
  * capsule authentication
  * recovery from update failure (or at least, A/B partition)
in the coming months.

Thanks,
-Takahiro Akashi
  


> > 
> > 
> > Changes
> > =======
> > Initial release as RFC (March 17, 2020)
> > 
> > AKASHI Takahiro (14):
> >    efi_loader: define OsIndicationsSupported flags
> >    efi_loader: define System Resource Table macros
> >    efi_loader: export a couple of protocol related functions
> >    efi_loader: correct a definition of struct efi_capsule_header
> >    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
> >    efi_loader: capsule: support variable update
> >    efi_loader: variable: export variables table for runtime access
> >    cmd: add "efidebug capsule" command
> >    tools: add mkeficapsule command for UEFI capsule update test
> >    test/py: add efi capsule test
> > 
> >   cmd/efidebug.c                                | 234 +++++
> >   include/efi_api.h                             | 214 ++++-
> >   include/efi_loader.h                          |  53 ++
> >   lib/efi_loader/Kconfig                        |  65 ++
> >   lib/efi_loader/Makefile                       |   2 +
> >   lib/efi_loader/efi_boottime.c                 |  29 +-
> >   lib/efi_loader/efi_capsule.c                  | 860 ++++++++++++++++++
> >   lib/efi_loader/efi_firmware.c                 | 191 ++++
> >   lib/efi_loader/efi_runtime.c                  | 104 ++-
> >   lib/efi_loader/efi_setup.c                    |  41 +-
> >   lib/efi_loader/efi_variable.c                 | 109 +++
> >   test/py/tests/test_efi_capsule/conftest.py    | 109 +++
> >   test/py/tests/test_efi_capsule/defs.py        |  21 +
> >   .../test_efi_capsule/test_capsule_firmware.py | 102 +++
> >   .../test_efi_capsule/test_capsule_variable.py | 141 +++
> >   test/py/tests/test_efi_capsule/uboot_env.its  |  25 +
> >   tools/Makefile                                |   3 +
> >   tools/mkeficapsule.c                          | 501 ++++++++++
> >   18 files changed, 2744 insertions(+), 60 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/test_capsule_variable.py
> >   create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its
> >   create mode 100644 tools/mkeficapsule.c
> > 
> 

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

* [RFC 07/14] efi_loader: capsule: add memory range capsule definitions
  2020-03-18  1:22     ` AKASHI Takahiro
@ 2020-03-18  7:35       ` Heinrich Schuchardt
  2020-03-18  7:57         ` AKASHI Takahiro
  0 siblings, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-18  7:35 UTC (permalink / raw)
  To: u-boot

On 3/18/20 2:22 AM, AKASHI Takahiro wrote:
> On Tue, Mar 17, 2020 at 09:11:43AM +0100, Heinrich Schuchardt wrote:
>> On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
>>> 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.
>>
>> There are multiple places where RAM availability is tested by writing
>> during board bring up. Please, describe which changes are needed in ATF
>> and U-Boot to make memory range capsules work reliably.
>
> I think that they are quite system/platform-specific
> and that it's beyond the scope of this patch.
>
> Thanks,
> -Takahiro Akashi

To me it does not make sense to merge this development if the platform
compatibility is not evaluated.

Best regards

Heinrich

>
>
>> Consider also memory encryption provided by some CPUs.
>> https://www.tomshardware.com/news/intel-mktme-amd-memory-encryption,39467.html
>>
>> Aren't file based capsules a much more reliable way for the OS to
>> communicate to the firmware?
>>
>> Best regards
>>
>> Heinrich
>>
>>>
>>> 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 ac2b38801c0c..b7bf21cac7ad 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
>>>
>>

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

* [RFC 07/14] efi_loader: capsule: add memory range capsule definitions
  2020-03-18  7:35       ` Heinrich Schuchardt
@ 2020-03-18  7:57         ` AKASHI Takahiro
  2020-04-06  7:48           ` AKASHI Takahiro
  0 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-18  7:57 UTC (permalink / raw)
  To: u-boot

On Wed, Mar 18, 2020 at 08:35:57AM +0100, Heinrich Schuchardt wrote:
> On 3/18/20 2:22 AM, AKASHI Takahiro wrote:
> > On Tue, Mar 17, 2020 at 09:11:43AM +0100, Heinrich Schuchardt wrote:
> > > On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > > > 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.
> > > 
> > > There are multiple places where RAM availability is tested by writing
> > > during board bring up. Please, describe which changes are needed in ATF
> > > and U-Boot to make memory range capsules work reliably.
> > 
> > I think that they are quite system/platform-specific
> > and that it's beyond the scope of this patch.
> > 
> > Thanks,
> > -Takahiro Akashi
> 
> To me it does not make sense to merge this development if the platform
> compatibility is not evaluated.

What do you mean by "platform compatibility?"
Please elaborate it and specific requirements, if any,
for better understandings.

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> > 
> > 
> > > Consider also memory encryption provided by some CPUs.
> > > https://www.tomshardware.com/news/intel-mktme-amd-memory-encryption,39467.html
> > > 
> > > Aren't file based capsules a much more reliable way for the OS to
> > > communicate to the firmware?
> > > 
> > > Best regards
> > > 
> > > Heinrich
> > > 
> > > > 
> > > > 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 ac2b38801c0c..b7bf21cac7ad 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
> > > > 
> > > 
> 

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

* [RFC 09/14] efi_loader: add simple firmware management protocol for FIT image
  2020-03-17  2:12 ` [RFC 09/14] efi_loader: add simple firmware management protocol for FIT image AKASHI Takahiro
@ 2020-03-18  8:04   ` Heinrich Schuchardt
  2020-03-18  8:17     ` AKASHI Takahiro
  0 siblings, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-18  8:04 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> 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_loader.h          |   3 +
>   lib/efi_loader/Kconfig        |  24 ++++-
>   lib/efi_loader/Makefile       |   1 +
>   lib/efi_loader/efi_firmware.c | 191 ++++++++++++++++++++++++++++++++++
>   4 files changed, 218 insertions(+), 1 deletion(-)
>   create mode 100644 lib/efi_loader/efi_firmware.c
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index c701672e18db..79bdf9586d24 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -134,6 +134,7 @@ extern const struct efi_hii_config_access_protocol efi_hii_config_access;
>   extern const struct efi_hii_database_protocol efi_hii_database;
>   extern const struct efi_hii_string_protocol efi_hii_string;
>   extern const struct efi_rng_protocol efi_rng_protocol;
> +extern const struct efi_firmware_management_protocol efi_fmp_fit;
>
>   uint16_t *efi_dp_str(struct efi_device_path *dp);
>
> @@ -180,6 +181,8 @@ extern const efi_guid_t efi_guid_hii_database_protocol;
>   extern const efi_guid_t efi_guid_hii_string_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;
>
>   /* GUID of RNG protocol */
>   extern const efi_guid_t efi_guid_rng_protocol;
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 43d6f75d557a..41b1e9b5543c 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -97,7 +97,6 @@ config EFI_CAPSULE_UPDATE
>   	  Select this option if you want to use capsule update feature,
>   	  including firmware updates and variable updates.
>
> -
>   if EFI_CAPSULE_UPDATE
>
>   config EFI_CAPSULE_UPDATE_FIRMWARE
> @@ -107,6 +106,29 @@ config EFI_CAPSULE_UPDATE_FIRMWARE
>   	  Select this option if you want to enable capsule-based
>   	  firmware update
>
> +config EFI_CAPSULE_FIT_SIMPLE
> +	bool "Firmware management protocol for simple FIT image"
> +	depends on EFI_CAPSULE_UPDATE_FIRMWARE
> +	depends on FIT
> +	select UPDATE_TFTP

UPDATE_TFTP is a very unsecure setting. A rogue DHCP and tFTP server can
be used to compromise a device where this is enabled.

Why should we need to enable an insecure network protocol to have
capsule updates?

Best regards

Heinrich

> +	select DFU
> +	default n
> +	help
> +	  Select this option if you want to enable firmware management protocol
> +	  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 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 for storing FIT image
> +
>   endif
>
>   config EFI_CAPSULE_ON_DISK
> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> index f19096924bef..50da10e0e3d9 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_CAPSULE_UPDATE) += 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..021c93196242
> --- /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) 2018 Linaro Limited
> + *			Author: AKASHI Takahiro
> + */
> +
> +#include <common.h>
> +#include <efi_loader.h>
> +#include <net.h>
> +
> +/*
> + * This FIMRWARE_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;
> +}
>

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

* [RFC 09/14] efi_loader: add simple firmware management protocol for FIT image
  2020-03-18  8:04   ` Heinrich Schuchardt
@ 2020-03-18  8:17     ` AKASHI Takahiro
  2020-03-18  9:06       ` Heinrich Schuchardt
  0 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-18  8:17 UTC (permalink / raw)
  To: u-boot

On Wed, Mar 18, 2020 at 09:04:44AM +0100, Heinrich Schuchardt wrote:
> On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > 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_loader.h          |   3 +
> >   lib/efi_loader/Kconfig        |  24 ++++-
> >   lib/efi_loader/Makefile       |   1 +
> >   lib/efi_loader/efi_firmware.c | 191 ++++++++++++++++++++++++++++++++++
> >   4 files changed, 218 insertions(+), 1 deletion(-)
> >   create mode 100644 lib/efi_loader/efi_firmware.c
> > 
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index c701672e18db..79bdf9586d24 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -134,6 +134,7 @@ extern const struct efi_hii_config_access_protocol efi_hii_config_access;
> >   extern const struct efi_hii_database_protocol efi_hii_database;
> >   extern const struct efi_hii_string_protocol efi_hii_string;
> >   extern const struct efi_rng_protocol efi_rng_protocol;
> > +extern const struct efi_firmware_management_protocol efi_fmp_fit;
> > 
> >   uint16_t *efi_dp_str(struct efi_device_path *dp);
> > 
> > @@ -180,6 +181,8 @@ extern const efi_guid_t efi_guid_hii_database_protocol;
> >   extern const efi_guid_t efi_guid_hii_string_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;
> > 
> >   /* GUID of RNG protocol */
> >   extern const efi_guid_t efi_guid_rng_protocol;
> > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > index 43d6f75d557a..41b1e9b5543c 100644
> > --- a/lib/efi_loader/Kconfig
> > +++ b/lib/efi_loader/Kconfig
> > @@ -97,7 +97,6 @@ config EFI_CAPSULE_UPDATE
> >   	  Select this option if you want to use capsule update feature,
> >   	  including firmware updates and variable updates.
> > 
> > -
> >   if EFI_CAPSULE_UPDATE
> > 
> >   config EFI_CAPSULE_UPDATE_FIRMWARE
> > @@ -107,6 +106,29 @@ config EFI_CAPSULE_UPDATE_FIRMWARE
> >   	  Select this option if you want to enable capsule-based
> >   	  firmware update
> > 
> > +config EFI_CAPSULE_FIT_SIMPLE
> > +	bool "Firmware management protocol for simple FIT image"
> > +	depends on EFI_CAPSULE_UPDATE_FIRMWARE
> > +	depends on FIT
> > +	select UPDATE_TFTP
> 
> UPDATE_TFTP is a very unsecure setting. A rogue DHCP and tFTP server can
> be used to compromise a device where this is enabled.
> 
> Why should we need to enable an insecure network protocol to have
> capsule updates?

1. This is a sample FMP driver to demonstrate a power of capsule
2. update_tftp() is called *only* against the interface and device
   that are specified by configuration. It's up to the developer.
3. Later on, capsule authentication support will be implemented.

So I believe that my approach here makes good sense.

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> > +	select DFU
> > +	default n
> > +	help
> > +	  Select this option if you want to enable firmware management protocol
> > +	  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 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 for storing FIT image
> > +
> >   endif
> > 
> >   config EFI_CAPSULE_ON_DISK
> > diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> > index f19096924bef..50da10e0e3d9 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_CAPSULE_UPDATE) += 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..021c93196242
> > --- /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) 2018 Linaro Limited
> > + *			Author: AKASHI Takahiro
> > + */
> > +
> > +#include <common.h>
> > +#include <efi_loader.h>
> > +#include <net.h>
> > +
> > +/*
> > + * This FIMRWARE_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;
> > +}
> > 
> 

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

* [RFC 06/14] efi_loader: capsule: add capsule_on_disk support
  2020-03-17  2:12 ` [RFC 06/14] efi_loader: capsule: add capsule_on_disk support AKASHI Takahiro
@ 2020-03-18  8:55   ` Heinrich Schuchardt
  2020-03-19 17:08     ` Heinrich Schuchardt
  0 siblings, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-18  8:55 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 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 identified only if they
> are located in a specific directory on a device that is determined
> by "BootXXXX" variables.

We have efi_set_bootdev() defining the boot device. So why do you refer
to BootXXXX?

Please, add Sphinx style comments to the functions describing
functionality and parameters.

>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/efi_loader.h          |  18 ++
>   lib/efi_loader/Kconfig        |   7 +
>   lib/efi_loader/efi_boottime.c |   3 +
>   lib/efi_loader/efi_capsule.c  | 548 ++++++++++++++++++++++++++++++++++
>   lib/efi_loader/efi_setup.c    |   6 +
>   5 files changed, 582 insertions(+)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index c3cb7735bf50..c701672e18db 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -178,6 +178,8 @@ extern const efi_guid_t efi_guid_hii_config_routing_protocol;
>   extern const efi_guid_t efi_guid_hii_config_access_protocol;
>   extern const efi_guid_t efi_guid_hii_database_protocol;
>   extern const efi_guid_t efi_guid_hii_string_protocol;
> +/* GUID of capsule update result */
> +extern const efi_guid_t efi_guid_capsule_report;
>
>   /* GUID of RNG protocol */
>   extern const efi_guid_t efi_guid_rng_protocol;
> @@ -690,6 +692,22 @@ efi_status_t EFIAPI efi_query_capsule_caps(
>   		u64 *maximum_capsule_size,
>   		u32 *reset_type);
>
> +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> +#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\"
> +
> +/* Hook at initialization */
> +efi_status_t efi_launch_capsules(void);
> +/* Notify ExitBootServices() is called */
> +void efi_capsule_boot_exit_notify(void);
> +#else
> +static inline efi_status_t efi_launch_capsules(void)
> +{
> +	return EFI_SUCCESS;
> +}
> +
> +static inline efi_capsule_boot_exit_notify(void) {}
> +#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 */
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 2ef6cb124f3a..95e10f7d981b 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -97,6 +97,13 @@ config EFI_CAPSULE_UPDATE
>   	  Select this option if you want to use capsule update feature,
>   	  including firmware updates and variable updates.
>
> +config EFI_CAPSULE_ON_DISK
> +	bool "Enable capsule-on-disk support"
> +	depends on EFI_CAPSULE_UPDATE
> +	default n
> +	help
> +	  Select this option if you want to use capsule-on-disk feature.
> +
>   config EFI_LOADER_BOUNCE_BUFFER
>   	bool "EFI Applications use bounce buffers for DMA operations"
>   	depends on ARM64
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 9860d5047502..c2a789b4f910 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -1981,6 +1981,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
>   	/* Notify variable services */
>   	efi_variables_boot_exit_notify();
>
> +	/* Notify capsule services */
> +	efi_capsule_boot_exit_notify();
> +
>   	/* Remove all events except EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
>   	list_for_each_entry_safe(evt, next_event, &efi_events, link) {
>   		if (evt->type != EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> index d3f931910d10..f3e2a555a6b9 100644
> --- a/lib/efi_loader/efi_capsule.c
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -10,8 +10,14 @@
>   #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;
> +
> +/* for file system access */
> +static struct efi_file_handle *bootdev_root;
> +
>   /*
>    * Launch a capsule
>    */
> @@ -96,3 +102,545 @@ efi_status_t EFIAPI efi_query_capsule_caps(
>   out:
>   	return EFI_EXIT(ret);
>   }
> +
> +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> +static void efi_capsule_result_variable(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 = variableame16;
> +	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)
> +		EFI_PRINT("EFI Capsule: creating %s failed\n", variable_name);

I think this is an error that should always be reported to the user.
Please, use printf().

After https://patchwork.ozlabs.org/patch/1245322/ is merged we can move
to log().

> +}
> +
> +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(struct efi_device_path *dp)
> +{
> +	efi_handle_t handle;
> +	struct efi_handler *handler;
> +	efi_status_t ret;
> +
> +	handle = efi_dp_find_obj(dp, NULL);
> +	if (!handle)
> +		return false;
> +
> +	/* check if this is a block device */
> +	ret = efi_search_protocol(handle, &efi_block_io_guid, &handler);
> +	if (ret != EFI_SUCCESS)
> +		return false;
> +
> +	return true;
> +}
> +
> +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(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(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;
> +}
> +
> +static 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;
> +}
> +
> +/*
> + * Launch all the capsules in system at boot time
> + *
> + * Called by efi init code
> + */
> +efi_status_t efi_launch_capsules(void)
> +{
> +	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;
> +
> +	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("EFI 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)
> +				EFI_PRINT("EFI Capsule update failed at %ls\n",
> +					  files[i]);

Isn't this an error that should always be presented to the user?

> +
> +			free(capsule);
> +		} else {
> +			EFI_PRINT("EFI Capsule read failed\n");

Same here.

> +		}
> +		/* create CapsuleXXXX */
> +		efi_capsule_result_variable(num, capsule, ret);
> +
> +		/* delete a capsule either in case of success or failure */
> +		ret = efi_capsule_delete_file(files[i]);
> +		if (ret != EFI_SUCCESS)
> +			EFI_PRINT("EFI Capsule deletion of capsule failed at %ls\n",
> +				  files[i]);

Same here.

> +	}
> +	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;
> +}
> +
> +/*
> + * Dummy functions after ExitBootServices()
> + */
> +efi_status_t EFIAPI efi_update_capsule_runtime(
> +		struct efi_capsule_header **capsule_header_array,
> +		efi_uintn_t capsule_count,
> +		u64 scatter_gather_list)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +efi_status_t EFIAPI efi_query_capsule_caps_runtime(
> +		struct efi_capsule_header **capsule_header_array,
> +		efi_uintn_t capsule_count,
> +		u64 *maximum_capsule_size,
> +		u32 *reset_type)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +/**
> + * efi_capsule_boot_exit_notify() - notify ExitBootServices() is called
> + */
> +void efi_capsule_boot_exit_notify(void)

Shouldn't we put this into efi_runtime_detach() to reduce code size?

The rest looks good at first sight.

Best regards

Heinrich

> +{
> +	efi_runtime_services.update_capsule = efi_update_capsule_runtime;
> +	efi_runtime_services.query_capsule_caps =
> +				efi_query_capsule_caps_runtime;
> +	efi_update_table_header_crc32(&efi_runtime_services.hdr);
> +}
> +#else
> +/*
> + * Dummy functions for runtime services
> + */
> +efi_status_t EFIAPI efi_update_capsule(
> +		struct efi_capsule_header **capsule_header_array,
> +		efi_uintn_t capsule_count,
> +		u64 scatter_gather_list)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +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)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
> diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
> index c485cad34022..309defc5e40d 100644
> --- a/lib/efi_loader/efi_setup.c
> +++ b/lib/efi_loader/efi_setup.c
> @@ -96,6 +96,10 @@ static efi_status_t efi_init_os_indications(void)
>   #ifdef CONFIG_EFI_CAPSULE_UPDATE
>   	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,
> @@ -196,6 +200,8 @@ efi_status_t efi_init_obj_list(void)
>   	if (ret != EFI_SUCCESS)
>   		goto out;
>
> +	/* Execute capsules after reboot */
> +	ret = efi_launch_capsules();
>   out:
>   	efi_obj_list_initialized = ret;
>   	return ret;
>

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

* [RFC 09/14] efi_loader: add simple firmware management protocol for FIT image
  2020-03-18  8:17     ` AKASHI Takahiro
@ 2020-03-18  9:06       ` Heinrich Schuchardt
  2020-04-06  7:59         ` AKASHI Takahiro
  0 siblings, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-18  9:06 UTC (permalink / raw)
  To: u-boot

On 3/18/20 9:17 AM, AKASHI Takahiro wrote:
> On Wed, Mar 18, 2020 at 09:04:44AM +0100, Heinrich Schuchardt wrote:
>> On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
>>> 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_loader.h          |   3 +
>>>    lib/efi_loader/Kconfig        |  24 ++++-
>>>    lib/efi_loader/Makefile       |   1 +
>>>    lib/efi_loader/efi_firmware.c | 191 ++++++++++++++++++++++++++++++++++
>>>    4 files changed, 218 insertions(+), 1 deletion(-)
>>>    create mode 100644 lib/efi_loader/efi_firmware.c
>>>
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index c701672e18db..79bdf9586d24 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -134,6 +134,7 @@ extern const struct efi_hii_config_access_protocol efi_hii_config_access;
>>>    extern const struct efi_hii_database_protocol efi_hii_database;
>>>    extern const struct efi_hii_string_protocol efi_hii_string;
>>>    extern const struct efi_rng_protocol efi_rng_protocol;
>>> +extern const struct efi_firmware_management_protocol efi_fmp_fit;
>>>
>>>    uint16_t *efi_dp_str(struct efi_device_path *dp);
>>>
>>> @@ -180,6 +181,8 @@ extern const efi_guid_t efi_guid_hii_database_protocol;
>>>    extern const efi_guid_t efi_guid_hii_string_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;
>>>
>>>    /* GUID of RNG protocol */
>>>    extern const efi_guid_t efi_guid_rng_protocol;
>>> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
>>> index 43d6f75d557a..41b1e9b5543c 100644
>>> --- a/lib/efi_loader/Kconfig
>>> +++ b/lib/efi_loader/Kconfig
>>> @@ -97,7 +97,6 @@ config EFI_CAPSULE_UPDATE
>>>    	  Select this option if you want to use capsule update feature,
>>>    	  including firmware updates and variable updates.
>>>
>>> -
>>>    if EFI_CAPSULE_UPDATE
>>>
>>>    config EFI_CAPSULE_UPDATE_FIRMWARE
>>> @@ -107,6 +106,29 @@ config EFI_CAPSULE_UPDATE_FIRMWARE
>>>    	  Select this option if you want to enable capsule-based
>>>    	  firmware update
>>>
>>> +config EFI_CAPSULE_FIT_SIMPLE
>>> +	bool "Firmware management protocol for simple FIT image"
>>> +	depends on EFI_CAPSULE_UPDATE_FIRMWARE
>>> +	depends on FIT
>>> +	select UPDATE_TFTP
>>
>> UPDATE_TFTP is a very unsecure setting. A rogue DHCP and tFTP server can
>> be used to compromise a device where this is enabled.
>>
>> Why should we need to enable an insecure network protocol to have
>> capsule updates?
>
> 1. This is a sample FMP driver to demonstrate a power of capsule
> 2. update_tftp() is called *only* against the interface and device
>     that are specified by configuration. It's up to the developer.

I am concerned about the dhcp or tftp command becoming insecure with
CONFIG_UPDATE_TFTP=y.

I would prefer a CONFIG_UPDATE controlling if common/update.c is
compiled and a separate CONFIG_UPDATE_TFTP which enables what is tFTP
specific.

Best regards

Heinrich

> 3. Later on, capsule authentication support will be implemented.
>
> So I believe that my approach here makes good sense.
>
> -Takahiro Akashi
>
>> Best regards
>>
>> Heinrich
>>
>>> +	select DFU
>>> +	default n
>>> +	help
>>> +	  Select this option if you want to enable firmware management protocol
>>> +	  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 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 for storing FIT image
>>> +
>>>    endif
>>>
>>>    config EFI_CAPSULE_ON_DISK
>>> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
>>> index f19096924bef..50da10e0e3d9 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_CAPSULE_UPDATE) += 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..021c93196242
>>> --- /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) 2018 Linaro Limited
>>> + *			Author: AKASHI Takahiro
>>> + */
>>> +
>>> +#include <common.h>
>>> +#include <efi_loader.h>
>>> +#include <net.h>
>>> +
>>> +/*
>>> + * This FIMRWARE_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;
>>> +}
>>>
>>

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

* [RFC 11/14] efi_loader: variable: export variables table for runtime access
  2020-03-17  2:12 ` [RFC 11/14] efi_loader: variable: export variables table for runtime access AKASHI Takahiro
  2020-03-17  7:37   ` Heinrich Schuchardt
@ 2020-03-18 13:54   ` Sughosh Ganu
  1 sibling, 0 replies; 49+ messages in thread
From: Sughosh Ganu @ 2020-03-18 13:54 UTC (permalink / raw)
  To: u-boot

On Tue, 17 Mar 2020 at 07:42, AKASHI Takahiro <takahiro.akashi@linaro.org>
wrote:

> There are platforms on which OS's won't be able to access UEFI variables
> at runtime (i.e. after ExitBootServices).
> With this patch, all the UEFI variables are exported as a configuration
> table so as to enable retrieving variables' values from the table, and
> later modifying them via capsule-on-disk if necessary.
>
> The idea is based on Peter's proposal[1] in boot-arch ML.
>
> [1]
> https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  include/efi_loader.h          |   7 +++
>  lib/efi_loader/Kconfig        |  10 ++++
>  lib/efi_loader/efi_boottime.c |  10 ++++
>  lib/efi_loader/efi_capsule.c  |   4 ++
>  lib/efi_loader/efi_variable.c | 109 ++++++++++++++++++++++++++++++++++
>  5 files changed, 140 insertions(+)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 79bdf9586d24..93ed5502821c 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -183,6 +183,8 @@ extern const efi_guid_t efi_guid_hii_string_protocol;
>  extern const efi_guid_t efi_guid_capsule_report;
>  /* GUID of firmware management protocol */
>  extern const efi_guid_t efi_guid_firmware_management_protocol;
> +/* GUID of runtime variable access */
> +extern const efi_guid_t efi_guid_variable_storage;
>
>  /* GUID of RNG protocol */
>  extern const efi_guid_t efi_guid_rng_protocol;
> @@ -659,6 +661,10 @@ efi_status_t EFIAPI efi_query_variable_info(
>                         u64 *remaining_variable_storage_size,
>                         u64 *maximum_variable_size);
>
> +#ifdef CONFIG_EFI_VARIABLE_EXPORT
> +efi_status_t efi_install_variables_table(void);
> +#endif
> +
>  /*
>   * See section 3.1.3 in the v2.7 UEFI spec for more details on
>   * the layout of EFI_LOAD_OPTION.  In short it is:
> @@ -683,6 +689,7 @@ struct efi_load_option {
>  void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data);
>  unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8
> **data);
>  efi_status_t efi_bootmgr_load(efi_handle_t *handle);
> +efi_status_t efi_install_variables_table(void);
>
>  /* Capsule update */
>  efi_status_t EFIAPI efi_update_capsule(
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 616e2acbe102..edb8d19059ea 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -41,6 +41,16 @@ 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_VARIABLE_EXPORT
> +       bool "Export variables table as configuration table"
> +       depends on !EFI_VARIABLE_RUNTIME_ACCESS
> +       depends on  EFI_CAPSULE_UPDATE_VARIABLE
> +       default y
> +       help
> +         Select this option if you want to export UEFI variables as
> +         configuration table so that OS can still get UEFI variable's
> +         value.
> +
>  config EFI_DEVICE_PATH_TO_TEXT
>         bool "Device path to text protocol"
>         default y
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index c2a789b4f910..406644e4da67 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -2898,6 +2898,16 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t
> image_handle,
>         if (ret != EFI_SUCCESS)
>                 return EFI_EXIT(EFI_INVALID_PARAMETER);
>
> +#ifdef CONFIG_EFI_VARIABLE_EXPORT
> +       /* Install variables database as a configuration table */
> +       ret = efi_install_variables_table();
> +       if (ret != EFI_SUCCESS)
> +               return EFI_EXIT(ret);
> +#endif
> +       /*
> +        * TODO: remove a table after image is terminated.
> +        */
> +
>         image_obj->exit_data_size = exit_data_size;
>         image_obj->exit_data = exit_data;
>
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> index 1293270aea95..6b737aec1b28 100644
> --- a/lib/efi_loader/efi_capsule.c
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -18,7 +18,11 @@ 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_VARIABLE_EXPORT
> +const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;
> +#else
>  static const efi_guid_t efi_guid_variable_storage =
> EFI_VARIABLE_STORAGE_GUID;
> +#endif
>

Need to remove the static keyword here. The build fails when
CONFIG_EFI_VARIABLE_EXPORT is not defined, with the extern declaration now
being done in efi_loader.h

-sughosh

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

* [RFC 08/14] efi_loader: capsule: support firmware update
  2020-03-17  2:12 ` [RFC 08/14] efi_loader: capsule: support firmware update AKASHI Takahiro
@ 2020-03-18 14:09   ` Sughosh Ganu
  0 siblings, 0 replies; 49+ messages in thread
From: Sughosh Ganu @ 2020-03-18 14:09 UTC (permalink / raw)
  To: u-boot

On Tue, 17 Mar 2020 at 07:42, AKASHI Takahiro <takahiro.akashi@linaro.org>
wrote:

> 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, installing drivers is not supported yet.
>
> 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            | 127 +++++++++++++++++++++++++++
>  lib/efi_loader/Kconfig       |  12 +++
>  lib/efi_loader/efi_capsule.c | 165 +++++++++++++++++++++++++++++++++++
>  lib/efi_loader/efi_setup.c   |   4 +
>  4 files changed, 308 insertions(+)
>

<snip>


> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> index f3e2a555a6b9..f3526beed681 100644
> --- a/lib/efi_loader/efi_capsule.c
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -14,10 +14,164 @@
>  #include <sort.h>
>

<snip>


>  /*
>   * Launch a capsule
>   */
> @@ -54,6 +208,17 @@ 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)) {
> +               EFI_PRINT("EFI Capsule (guid:%pUl)\n",
> &capsule->capsule_guid);
> +               if (!guidcmp(&capsule->capsule_guid,
> +                            &efi_guid_firmware_management_capsule_id))
> +                       ret  = efi_capsule_update_firmware(
> +                                       (struct
> efi_firmware_management_capsule_header *)
> +                                       ((void *)capsule +
> sizeof(*capsule)));
>

Instead of sizeof(*capsule), please use header_size member of
efi_capsule_header. The spec mentions that the size of the capsule header
might be larger than the capsule header structure. Moreover, when testing
with a capsule built from the edk2 capsule generation script, i do see that
the header_size is greater than sizeof(efi_capsule_header).

-sughosh

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

* [RFC 01/14] efi_loader: define OsIndicationsSupported flags
  2020-03-18  1:18     ` AKASHI Takahiro
@ 2020-03-18 18:01       ` Heinrich Schuchardt
  0 siblings, 0 replies; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-18 18:01 UTC (permalink / raw)
  To: u-boot

On 3/18/20 2:18 AM, AKASHI Takahiro wrote:
> On Tue, Mar 17, 2020 at 08:03:21AM +0100, Heinrich Schuchardt wrote:
>> On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
>>> These flags are expected to be set in OsIndicationsSupported variable
>>> if corresponding features are supported. See UEFI specification,
>>> section 8.5.4.
>>>
>>> In particular, capsule-related flags will be used in my capsule
>>> update patch.
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>    include/efi_api.h | 11 +++++++++++
>>>    1 file changed, 11 insertions(+)
>>>
>>> diff --git a/include/efi_api.h b/include/efi_api.h
>>> index 22396172e15f..73c0722b0168 100644
>>> --- a/include/efi_api.h
>>> +++ b/include/efi_api.h
>>> @@ -57,6 +57,17 @@ typedef u16 efi_form_id_t;
>>>
>>>    struct efi_event;
>>>
>>> +/* OsIndicationsSupported flags */
>>> +#define EFI_OS_INDICATIONS_BOOT_TO_RW_UI		0x0000000000000001
>> This is called
>> EFI_OS_INDICATIONS_BOOT_TO_FW_UI
>> in the spec.
>
> Yes, it's a typo.
>
> Thanks,
> -Takahiro Akashi

Corrected and merged into u-boot/master.

Best regards

Heinrich

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

* [RFC 02/14] efi_loader: define System Resource Table macros
  2020-03-17  7:06   ` Heinrich Schuchardt
@ 2020-03-18 18:02     ` Heinrich Schuchardt
  0 siblings, 0 replies; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-18 18:02 UTC (permalink / raw)
  To: u-boot

On 3/17/20 8:06 AM, Heinrich Schuchardt wrote:
> On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
>> Some of those values will be used in an implementation of UEFI firmware
>> management protocol as part of my capsule update patch.
>>
>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>
> Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>

Merged into u-boot master

Best regards

Heinrich

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

* [RFC 03/14] efi_loader: export a couple of protocol related functions
  2020-03-17  2:12 ` [RFC 03/14] efi_loader: export a couple of protocol related functions AKASHI Takahiro
  2020-03-17  7:19   ` Heinrich Schuchardt
@ 2020-03-18 18:03   ` Heinrich Schuchardt
  1 sibling, 0 replies; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-18 18:03 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> This is a preparatory patch.
> Those functions will be used in an implementation of UEFI firmware
> management protocol as part of my capsule update patch.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

Merged into U-Boot master

Best regards

Heinrich

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

* [RFC 04/14] efi_loader: correct a definition of struct efi_capsule_header
  2020-03-17  2:12 ` [RFC 04/14] efi_loader: correct a definition of struct efi_capsule_header AKASHI Takahiro
  2020-03-17  7:25   ` Heinrich Schuchardt
@ 2020-03-18 18:03   ` Heinrich Schuchardt
  1 sibling, 0 replies; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-18 18:03 UTC (permalink / raw)
  To: u-boot

On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> See UEFI specification, section 8.5.3.
> In addition, the structure, efi_capsule_header, should be "packed"
> as it is a serialized binary format in a capsule file.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

Merged into U-Boot master.

Best regards

Heinrich

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

* [RFC 00/14] efi_loader: add capsule update support
  2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
                   ` (14 preceding siblings ...)
  2020-03-17  7:49 ` [RFC 00/14] efi_loader: add capsule update support Heinrich Schuchardt
@ 2020-03-18 18:16 ` Sughosh Ganu
  15 siblings, 0 replies; 49+ messages in thread
From: Sughosh Ganu @ 2020-03-18 18:16 UTC (permalink / raw)
  To: u-boot

On Tue, 17 Mar 2020 at 07:42, AKASHI Takahiro <takahiro.akashi@linaro.org>
wrote:

> Summary
> =======
> 'UpdateCapsule' is one of runtime services defined in UEFI specification
> and its aim is to allow a caller to pass information to the firmware.
> This is mostly used to update firmware binary on devices by instructions
> from OS.
>
> In this patch series, all the related definitions and structures are given
> as UEFI specification describes and basic framework for capsule support is
> implemented. Currently supported types of capsule are
>  * firmware update (Firmware Management Protocol or simply FMP)
>  * variable update
>
> UpdateCapsule is a runtime services function, but is, at least initially,
> provided only before exiting boot services alike other runtime functions.
> This is because modifying storage which may be shared with OS must be
> carefully designed and there is no general assumption to do that as in
> the case of [Get/]SetVariable.
> Instead, any capsule can be handed over to the firmware as a file on
> a specific file system. In other words, we only support "capsules on disk"
> for now.
>
> Regarding firmware update, most of functionality is provided by FMP
> driver and it will be by nature system/platform-specific. So you can and
> should implement FMP drivers based on your system requirements.
> In this patch series, only a simple FMP driver based on FIT image for
> a single region is supported.  (So it is "for reference only")
> See more details in "efi_loader: capsule: add simple firmware management
> protocol."
>
> Regarding variable update, the implementation here is based on a draft
> proposal[1] by Peter in Boot-arch ML. The specification should be discussed
> and finalized first. So the code doesn't fully implement Peter's idea.
>
> [1]
> https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html
>
> Patch structure
> ===============
> Patch#1-#4: preparatory patches
> Patch#5-#11: main part of implementation
> Patch#12-#14: utilities and tests
>
>
With the changes that I suggested in a couple of patches, I am able to test
the capsule-on-disk update of the u-boot firmware image on the qemu arm64
platform. I have also tested the capsule update functionality via the
'efidebug capsule update -v <capsule address>' command. Thanks!

-sughosh

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

* [RFC 13/14] tools: add mkeficapsule command for UEFI capsule update test
  2020-03-18  1:32     ` AKASHI Takahiro
@ 2020-03-19  8:55       ` Ilias Apalodimas
  0 siblings, 0 replies; 49+ messages in thread
From: Ilias Apalodimas @ 2020-03-19  8:55 UTC (permalink / raw)
  To: u-boot

On Wed, Mar 18, 2020 at 10:32:38AM +0900, AKASHI Takahiro wrote:
> On Tue, Mar 17, 2020 at 08:58:55AM +0100, Heinrich Schuchardt wrote:
> > On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > > This is a utility mainly for test purpose.
> > >    mkeficapsule -f: create a FIT image firmware capsule
> > 
> > The UEFI spec defines a capsule format and how to invoke it by placing
> > the file in the right directory before reboot. Why do we need a FIT
> > image firmware capsule?
> 
> ??? I don't get your point.
> 
> A capsule is just a wrapper that can hold any kind (or format) of
> binary data. In fact, an associated capsule driver should know
> how the data should correctly be handled on U-Boot.
> 
> So if you provide your own driver (more specifically, "firmware
> management protocol" for firmware update), you can encapsulate
> any firmware in your proprietary format.
> 
> In this RFC, I provide a *sample* driver for a single FIT image
> in patch#9.

FWIW I think Takahiro's approach is fine here. FIT has been around for a couple
of years so why not use it? As Takahiro pointed out, this is by no means
restrictive.

Regards
/Ilias

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

* [RFC 11/14] efi_loader: variable: export variables table for runtime access
  2020-03-18  1:53     ` AKASHI Takahiro
@ 2020-03-19  9:30       ` Ilias Apalodimas
  0 siblings, 0 replies; 49+ messages in thread
From: Ilias Apalodimas @ 2020-03-19  9:30 UTC (permalink / raw)
  To: u-boot

Akashi-san,

On Wed, Mar 18, 2020 at 10:53:45AM +0900, AKASHI Takahiro wrote:
> On Tue, Mar 17, 2020 at 08:37:47AM +0100, Heinrich Schuchardt wrote:
> > On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > > There are platforms on which OS's won't be able to access UEFI variables
> > > at runtime (i.e. after ExitBootServices).
> > > With this patch, all the UEFI variables are exported as a configuration
> > > table so as to enable retrieving variables' values from the table, and
> > > later modifying them via capsule-on-disk if necessary.
> > 
> > I do not understand why we should expose our internal memory for holding
> > UEFI variables to the operating system. This might end up in users
> > trying to access the variables directly.
> 
> I think that you somehow misunderstand my code as it never exposes
> any "internal memory," although I don't know what it exactly means in
> this context.
> This configuration table is nothing but a list of data that represent
> all the UEFI variables in implementation-agnostic format.
> 
> > I do not understand why we should not keep the pointer in our private
> > memory.
> 
> Anyway, this patch naively implements Peter's proposal while I also
> submitted another patch[1] that allows HL-OS to use GetVariable
> interface directly via *caching*.

How are the two approaches going to affect existig tools (i.e efivar --list) to
read the variables?

> 
> Since how we should enable accessing UEFI variables at runtime is
> one of key issues, I'd rather discuss in boot-arch ML as I suggested
> in the cover letter.
> I have already re-activated the discussion there[2].
> Please make your comments there for wider audience.
> 
> [1] https://lists.denx.de/pipermail/u-boot/2019-June/371769.html
> [2] https://lists.linaro.org/pipermail/boot-architecture/2020-March/001149.html
> 
Will do

Regards
/Ilias

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

* [RFC 06/14] efi_loader: capsule: add capsule_on_disk support
  2020-03-18  8:55   ` Heinrich Schuchardt
@ 2020-03-19 17:08     ` Heinrich Schuchardt
  2020-03-30  7:43       ` AKASHI Takahiro
  0 siblings, 1 reply; 49+ messages in thread
From: Heinrich Schuchardt @ 2020-03-19 17:08 UTC (permalink / raw)
  To: u-boot

On 3/18/20 9:55 AM, Heinrich Schuchardt wrote:
> On 3/17/20 3:12 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 identified only if they
>> are located in a specific directory on a device that is determined
>> by "BootXXXX" variables.
> 
> We have efi_set_bootdev() defining the boot device. So why do you refer
> to BootXXXX?
> 
> Please, add Sphinx style comments to the functions describing
> functionality and parameters.
> 
>>
>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>> ---
>> ? include/efi_loader.h????????? |? 18 ++
>> ? lib/efi_loader/Kconfig??????? |?? 7 +
>> ? lib/efi_loader/efi_boottime.c |?? 3 +
>> ? lib/efi_loader/efi_capsule.c? | 548 ++++++++++++++++++++++++++++++++++
>> ? lib/efi_loader/efi_setup.c??? |?? 6 +
>> ? 5 files changed, 582 insertions(+)
>>
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index c3cb7735bf50..c701672e18db 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -178,6 +178,8 @@ extern const efi_guid_t
>> efi_guid_hii_config_routing_protocol;
>> ? extern const efi_guid_t efi_guid_hii_config_access_protocol;
>> ? extern const efi_guid_t efi_guid_hii_database_protocol;
>> ? extern const efi_guid_t efi_guid_hii_string_protocol;
>> +/* GUID of capsule update result */
>> +extern const efi_guid_t efi_guid_capsule_report;
>>
>> ? /* GUID of RNG protocol */
>> ? extern const efi_guid_t efi_guid_rng_protocol;
>> @@ -690,6 +692,22 @@ efi_status_t EFIAPI efi_query_capsule_caps(
>> ????????? u64 *maximum_capsule_size,
>> ????????? u32 *reset_type);
>>
>> +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
>> +#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\"
>> +
>> +/* Hook at initialization */
>> +efi_status_t efi_launch_capsules(void);
>> +/* Notify ExitBootServices() is called */
>> +void efi_capsule_boot_exit_notify(void);
>> +#else
>> +static inline efi_status_t efi_launch_capsules(void)
>> +{
>> +??? return EFI_SUCCESS;
>> +}
>> +
>> +static inline efi_capsule_boot_exit_notify(void) {}
>> +#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 */
>> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
>> index 2ef6cb124f3a..95e10f7d981b 100644
>> --- a/lib/efi_loader/Kconfig
>> +++ b/lib/efi_loader/Kconfig
>> @@ -97,6 +97,13 @@ config EFI_CAPSULE_UPDATE
>> ??????? Select this option if you want to use capsule update feature,
>> ??????? including firmware updates and variable updates.
>>
>> +config EFI_CAPSULE_ON_DISK
>> +??? bool "Enable capsule-on-disk support"
>> +??? depends on EFI_CAPSULE_UPDATE
>> +??? default n
>> +??? help
>> +????? Select this option if you want to use capsule-on-disk feature.
>> +
>> ? config EFI_LOADER_BOUNCE_BUFFER
>> ????? bool "EFI Applications use bounce buffers for DMA operations"
>> ????? depends on ARM64
>> diff --git a/lib/efi_loader/efi_boottime.c
>> b/lib/efi_loader/efi_boottime.c
>> index 9860d5047502..c2a789b4f910 100644
>> --- a/lib/efi_loader/efi_boottime.c
>> +++ b/lib/efi_loader/efi_boottime.c
>> @@ -1981,6 +1981,9 @@ static efi_status_t EFIAPI
>> efi_exit_boot_services(efi_handle_t image_handle,
>> ????? /* Notify variable services */
>> ????? efi_variables_boot_exit_notify();
>>
>> +??? /* Notify capsule services */
>> +??? efi_capsule_boot_exit_notify();
>> +
>> ????? /* Remove all events except EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
>> ????? list_for_each_entry_safe(evt, next_event, &efi_events, link) {
>> ????????? if (evt->type != EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)
>> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
>> index d3f931910d10..f3e2a555a6b9 100644
>> --- a/lib/efi_loader/efi_capsule.c
>> +++ b/lib/efi_loader/efi_capsule.c
>> @@ -10,8 +10,14 @@
>> ? #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;
>> +
>> +/* for file system access */
>> +static struct efi_file_handle *bootdev_root;
>> +
>> ? /*
>> ?? * Launch a capsule
>> ?? */
>> @@ -96,3 +102,545 @@ efi_status_t EFIAPI efi_query_capsule_caps(
>> ? out:
>> ????? return EFI_EXIT(ret);
>> ? }
>> +
>> +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
>> +static void efi_capsule_result_variable(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 = variableame16;
>> +??? 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)
>> +??????? EFI_PRINT("EFI Capsule: creating %s failed\n", variable_name);
> 
> I think this is an error that should always be reported to the user.
> Please, use printf().
> 
> After https://patchwork.ozlabs.org/patch/1245322/ is merged we can move
> to log().
> 
>> +}
>> +
>> +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(struct efi_device_path *dp)
>> +{
>> +??? efi_handle_t handle;
>> +??? struct efi_handler *handler;
>> +??? efi_status_t ret;
>> +
>> +??? handle = efi_dp_find_obj(dp, NULL);
>> +??? if (!handle)
>> +??????? return false;
>> +
>> +??? /* check if this is a block device */
>> +??? ret = efi_search_protocol(handle, &efi_block_io_guid, &handler);
>> +??? if (ret != EFI_SUCCESS)
>> +??????? return false;
>> +
>> +??? return true;
>> +}
>> +
>> +static efi_status_t find_boot_device(void)

Please, use the EFI system partition, cf.

[PATCH 0/2] efi_loader: detect EFI system partition
https://lists.denx.de/pipermail/u-boot/2020-March/403560.htm
https://patchwork.ozlabs.org/project/uboot/list/?series=165417

Best regards

Heinrich

>> +{
>> +??? 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(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(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;
>> +}
>> +
>> +static 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;
>> +}
>> +
>> +/*
>> + * Launch all the capsules in system at boot time
>> + *
>> + * Called by efi init code
>> + */
>> +efi_status_t efi_launch_capsules(void)
>> +{
>> +??? 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;
>> +
>> +??? 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("EFI 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)
>> +??????????????? EFI_PRINT("EFI Capsule update failed at %ls\n",
>> +????????????????????? files[i]);
> 
> Isn't this an error that should always be presented to the user?
> 
>> +
>> +??????????? free(capsule);
>> +??????? } else {
>> +??????????? EFI_PRINT("EFI Capsule read failed\n");
> 
> Same here.
> 
>> +??????? }
>> +??????? /* create CapsuleXXXX */
>> +??????? efi_capsule_result_variable(num, capsule, ret);
>> +
>> +??????? /* delete a capsule either in case of success or failure */
>> +??????? ret = efi_capsule_delete_file(files[i]);
>> +??????? if (ret != EFI_SUCCESS)
>> +??????????? EFI_PRINT("EFI Capsule deletion of capsule failed at %ls\n",
>> +????????????????? files[i]);
> 
> Same here.
> 
>> +??? }
>> +??? 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;
>> +}
>> +
>> +/*
>> + * Dummy functions after ExitBootServices()
>> + */
>> +efi_status_t EFIAPI efi_update_capsule_runtime(
>> +??????? struct efi_capsule_header **capsule_header_array,
>> +??????? efi_uintn_t capsule_count,
>> +??????? u64 scatter_gather_list)
>> +{
>> +??? return EFI_UNSUPPORTED;
>> +}
>> +
>> +efi_status_t EFIAPI efi_query_capsule_caps_runtime(
>> +??????? struct efi_capsule_header **capsule_header_array,
>> +??????? efi_uintn_t capsule_count,
>> +??????? u64 *maximum_capsule_size,
>> +??????? u32 *reset_type)
>> +{
>> +??? return EFI_UNSUPPORTED;
>> +}
>> +
>> +/**
>> + * efi_capsule_boot_exit_notify() - notify ExitBootServices() is called
>> + */
>> +void efi_capsule_boot_exit_notify(void)
> 
> Shouldn't we put this into efi_runtime_detach() to reduce code size?
> 
> The rest looks good at first sight.
> 
> Best regards
> 
> Heinrich
> 
>> +{
>> +??? efi_runtime_services.update_capsule = efi_update_capsule_runtime;
>> +??? efi_runtime_services.query_capsule_caps =
>> +??????????????? efi_query_capsule_caps_runtime;
>> +??? efi_update_table_header_crc32(&efi_runtime_services.hdr);
>> +}
>> +#else
>> +/*
>> + * Dummy functions for runtime services
>> + */
>> +efi_status_t EFIAPI efi_update_capsule(
>> +??????? struct efi_capsule_header **capsule_header_array,
>> +??????? efi_uintn_t capsule_count,
>> +??????? u64 scatter_gather_list)
>> +{
>> +??? return EFI_UNSUPPORTED;
>> +}
>> +
>> +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)
>> +{
>> +??? return EFI_UNSUPPORTED;
>> +}
>> +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
>> diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
>> index c485cad34022..309defc5e40d 100644
>> --- a/lib/efi_loader/efi_setup.c
>> +++ b/lib/efi_loader/efi_setup.c
>> @@ -96,6 +96,10 @@ static efi_status_t efi_init_os_indications(void)
>> ? #ifdef CONFIG_EFI_CAPSULE_UPDATE
>> ????? 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,
>> @@ -196,6 +200,8 @@ efi_status_t efi_init_obj_list(void)
>> ????? if (ret != EFI_SUCCESS)
>> ????????? goto out;
>>
>> +??? /* Execute capsules after reboot */
>> +??? ret = efi_launch_capsules();
>> ? out:
>> ????? efi_obj_list_initialized = ret;
>> ????? return ret;
>>
> 

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

* [RFC 06/14] efi_loader: capsule: add capsule_on_disk support
  2020-03-19 17:08     ` Heinrich Schuchardt
@ 2020-03-30  7:43       ` AKASHI Takahiro
  0 siblings, 0 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-30  7:43 UTC (permalink / raw)
  To: u-boot

On Thu, Mar 19, 2020 at 06:08:15PM +0100, Heinrich Schuchardt wrote:
> On 3/18/20 9:55 AM, Heinrich Schuchardt wrote:
> > On 3/17/20 3:12 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 identified only if they
> >> are located in a specific directory on a device that is determined
> >> by "BootXXXX" variables.
> > 
> > We have efi_set_bootdev() defining the boot device. So why do you refer
> > to BootXXXX?
> > 
> > Please, add Sphinx style comments to the functions describing
> > functionality and parameters.
> > 
> >>
> >> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >> ---
> >> ? include/efi_loader.h????????? |? 18 ++
> >> ? lib/efi_loader/Kconfig??????? |?? 7 +
> >> ? lib/efi_loader/efi_boottime.c |?? 3 +
> >> ? lib/efi_loader/efi_capsule.c? | 548 ++++++++++++++++++++++++++++++++++
> >> ? lib/efi_loader/efi_setup.c??? |?? 6 +
> >> ? 5 files changed, 582 insertions(+)
> >>
> >> diff --git a/include/efi_loader.h b/include/efi_loader.h
> >> index c3cb7735bf50..c701672e18db 100644
> >> --- a/include/efi_loader.h
> >> +++ b/include/efi_loader.h
> >> @@ -178,6 +178,8 @@ extern const efi_guid_t
> >> efi_guid_hii_config_routing_protocol;
> >> ? extern const efi_guid_t efi_guid_hii_config_access_protocol;
> >> ? extern const efi_guid_t efi_guid_hii_database_protocol;
> >> ? extern const efi_guid_t efi_guid_hii_string_protocol;
> >> +/* GUID of capsule update result */
> >> +extern const efi_guid_t efi_guid_capsule_report;
> >>
> >> ? /* GUID of RNG protocol */
> >> ? extern const efi_guid_t efi_guid_rng_protocol;
> >> @@ -690,6 +692,22 @@ efi_status_t EFIAPI efi_query_capsule_caps(
> >> ????????? u64 *maximum_capsule_size,
> >> ????????? u32 *reset_type);
> >>
> >> +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> >> +#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\"
> >> +
> >> +/* Hook at initialization */
> >> +efi_status_t efi_launch_capsules(void);
> >> +/* Notify ExitBootServices() is called */
> >> +void efi_capsule_boot_exit_notify(void);
> >> +#else
> >> +static inline efi_status_t efi_launch_capsules(void)
> >> +{
> >> +??? return EFI_SUCCESS;
> >> +}
> >> +
> >> +static inline efi_capsule_boot_exit_notify(void) {}
> >> +#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 */
> >> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> >> index 2ef6cb124f3a..95e10f7d981b 100644
> >> --- a/lib/efi_loader/Kconfig
> >> +++ b/lib/efi_loader/Kconfig
> >> @@ -97,6 +97,13 @@ config EFI_CAPSULE_UPDATE
> >> ??????? Select this option if you want to use capsule update feature,
> >> ??????? including firmware updates and variable updates.
> >>
> >> +config EFI_CAPSULE_ON_DISK
> >> +??? bool "Enable capsule-on-disk support"
> >> +??? depends on EFI_CAPSULE_UPDATE
> >> +??? default n
> >> +??? help
> >> +????? Select this option if you want to use capsule-on-disk feature.
> >> +
> >> ? config EFI_LOADER_BOUNCE_BUFFER
> >> ????? bool "EFI Applications use bounce buffers for DMA operations"
> >> ????? depends on ARM64
> >> diff --git a/lib/efi_loader/efi_boottime.c
> >> b/lib/efi_loader/efi_boottime.c
> >> index 9860d5047502..c2a789b4f910 100644
> >> --- a/lib/efi_loader/efi_boottime.c
> >> +++ b/lib/efi_loader/efi_boottime.c
> >> @@ -1981,6 +1981,9 @@ static efi_status_t EFIAPI
> >> efi_exit_boot_services(efi_handle_t image_handle,
> >> ????? /* Notify variable services */
> >> ????? efi_variables_boot_exit_notify();
> >>
> >> +??? /* Notify capsule services */
> >> +??? efi_capsule_boot_exit_notify();
> >> +
> >> ????? /* Remove all events except EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
> >> ????? list_for_each_entry_safe(evt, next_event, &efi_events, link) {
> >> ????????? if (evt->type != EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)
> >> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> >> index d3f931910d10..f3e2a555a6b9 100644
> >> --- a/lib/efi_loader/efi_capsule.c
> >> +++ b/lib/efi_loader/efi_capsule.c
> >> @@ -10,8 +10,14 @@
> >> ? #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;
> >> +
> >> +/* for file system access */
> >> +static struct efi_file_handle *bootdev_root;
> >> +
> >> ? /*
> >> ?? * Launch a capsule
> >> ?? */
> >> @@ -96,3 +102,545 @@ efi_status_t EFIAPI efi_query_capsule_caps(
> >> ? out:
> >> ????? return EFI_EXIT(ret);
> >> ? }
> >> +
> >> +#ifdef CONFIG_EFI_CAPSULE_ON_DISK
> >> +static void efi_capsule_result_variable(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 = variableame16;
> >> +??? 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)
> >> +??????? EFI_PRINT("EFI Capsule: creating %s failed\n", variable_name);
> > 
> > I think this is an error that should always be reported to the user.
> > Please, use printf().
> > 
> > After https://patchwork.ozlabs.org/patch/1245322/ is merged we can move
> > to log().
> > 
> >> +}
> >> +
> >> +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(struct efi_device_path *dp)
> >> +{
> >> +??? efi_handle_t handle;
> >> +??? struct efi_handler *handler;
> >> +??? efi_status_t ret;
> >> +
> >> +??? handle = efi_dp_find_obj(dp, NULL);
> >> +??? if (!handle)
> >> +??????? return false;
> >> +
> >> +??? /* check if this is a block device */
> >> +??? ret = efi_search_protocol(handle, &efi_block_io_guid, &handler);
> >> +??? if (ret != EFI_SUCCESS)
> >> +??????? return false;
> >> +
> >> +??? return true;
> >> +}
> >> +
> >> +static efi_status_t find_boot_device(void)
> 
> Please, use the EFI system partition, cf.
> 
> [PATCH 0/2] efi_loader: detect EFI system partition
> https://lists.denx.de/pipermail/u-boot/2020-March/403560.htm
> https://patchwork.ozlabs.org/project/uboot/list/?series=165417

No.
UEFI specification, section 8.5.5, clearly requires that a device
must be determined by BootNext/BootOrder variables.
*Additionally*, we should check if the device path points to EFI system
partition on the device.
===8<===
The device to be checked for \EFI\UpdateCapsule is identified by reference
to FilePathList field within the selected active Boot#### variable.
===>8===

-Takahiro Akashi


> 
> Best regards
> 
> Heinrich
> 
> >> +{
> >> +??? 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(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(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;
> >> +}
> >> +
> >> +static 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;
> >> +}
> >> +
> >> +/*
> >> + * Launch all the capsules in system at boot time
> >> + *
> >> + * Called by efi init code
> >> + */
> >> +efi_status_t efi_launch_capsules(void)
> >> +{
> >> +??? 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;
> >> +
> >> +??? 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("EFI 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)
> >> +??????????????? EFI_PRINT("EFI Capsule update failed at %ls\n",
> >> +????????????????????? files[i]);
> > 
> > Isn't this an error that should always be presented to the user?
> > 
> >> +
> >> +??????????? free(capsule);
> >> +??????? } else {
> >> +??????????? EFI_PRINT("EFI Capsule read failed\n");
> > 
> > Same here.
> > 
> >> +??????? }
> >> +??????? /* create CapsuleXXXX */
> >> +??????? efi_capsule_result_variable(num, capsule, ret);
> >> +
> >> +??????? /* delete a capsule either in case of success or failure */
> >> +??????? ret = efi_capsule_delete_file(files[i]);
> >> +??????? if (ret != EFI_SUCCESS)
> >> +??????????? EFI_PRINT("EFI Capsule deletion of capsule failed at %ls\n",
> >> +????????????????? files[i]);
> > 
> > Same here.
> > 
> >> +??? }
> >> +??? 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;
> >> +}
> >> +
> >> +/*
> >> + * Dummy functions after ExitBootServices()
> >> + */
> >> +efi_status_t EFIAPI efi_update_capsule_runtime(
> >> +??????? struct efi_capsule_header **capsule_header_array,
> >> +??????? efi_uintn_t capsule_count,
> >> +??????? u64 scatter_gather_list)
> >> +{
> >> +??? return EFI_UNSUPPORTED;
> >> +}
> >> +
> >> +efi_status_t EFIAPI efi_query_capsule_caps_runtime(
> >> +??????? struct efi_capsule_header **capsule_header_array,
> >> +??????? efi_uintn_t capsule_count,
> >> +??????? u64 *maximum_capsule_size,
> >> +??????? u32 *reset_type)
> >> +{
> >> +??? return EFI_UNSUPPORTED;
> >> +}
> >> +
> >> +/**
> >> + * efi_capsule_boot_exit_notify() - notify ExitBootServices() is called
> >> + */
> >> +void efi_capsule_boot_exit_notify(void)
> > 
> > Shouldn't we put this into efi_runtime_detach() to reduce code size?
> > 
> > The rest looks good at first sight.
> > 
> > Best regards
> > 
> > Heinrich
> > 
> >> +{
> >> +??? efi_runtime_services.update_capsule = efi_update_capsule_runtime;
> >> +??? efi_runtime_services.query_capsule_caps =
> >> +??????????????? efi_query_capsule_caps_runtime;
> >> +??? efi_update_table_header_crc32(&efi_runtime_services.hdr);
> >> +}
> >> +#else
> >> +/*
> >> + * Dummy functions for runtime services
> >> + */
> >> +efi_status_t EFIAPI efi_update_capsule(
> >> +??????? struct efi_capsule_header **capsule_header_array,
> >> +??????? efi_uintn_t capsule_count,
> >> +??????? u64 scatter_gather_list)
> >> +{
> >> +??? return EFI_UNSUPPORTED;
> >> +}
> >> +
> >> +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)
> >> +{
> >> +??? return EFI_UNSUPPORTED;
> >> +}
> >> +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
> >> diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
> >> index c485cad34022..309defc5e40d 100644
> >> --- a/lib/efi_loader/efi_setup.c
> >> +++ b/lib/efi_loader/efi_setup.c
> >> @@ -96,6 +96,10 @@ static efi_status_t efi_init_os_indications(void)
> >> ? #ifdef CONFIG_EFI_CAPSULE_UPDATE
> >> ????? 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,
> >> @@ -196,6 +200,8 @@ efi_status_t efi_init_obj_list(void)
> >> ????? if (ret != EFI_SUCCESS)
> >> ????????? goto out;
> >>
> >> +??? /* Execute capsules after reboot */
> >> +??? ret = efi_launch_capsules();
> >> ? out:
> >> ????? efi_obj_list_initialized = ret;
> >> ????? return ret;
> >>
> > 
> 

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

* [RFC 00/14] efi_loader: add capsule update support
  2020-03-18  2:04   ` AKASHI Takahiro
@ 2020-03-31  4:36     ` AKASHI Takahiro
  2020-04-14  4:38       ` AKASHI Takahiro
  0 siblings, 1 reply; 49+ messages in thread
From: AKASHI Takahiro @ 2020-03-31  4:36 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Wed, Mar 18, 2020 at 11:04:05AM +0900, AKASHI Takahiro wrote:
> Heinrich,
> 
> Thank you for your quick review.
> 
> On Tue, Mar 17, 2020 at 08:49:12AM +0100, Heinrich Schuchardt wrote:
> > On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > > Summary
> > > =======
> > > 'UpdateCapsule' is one of runtime services defined in UEFI specification
> > > and its aim is to allow a caller to pass information to the firmware.
> > > This is mostly used to update firmware binary on devices by instructions
> > > from OS.
> > > 
> > > In this patch series, all the related definitions and structures are given
> > > as UEFI specification describes and basic framework for capsule support is
> > > implemented. Currently supported types of capsule are
> > >   * firmware update (Firmware Management Protocol or simply FMP)
> > >   * variable update
> > > 
> > > UpdateCapsule is a runtime services function, but is, at least initially,
> > > provided only before exiting boot services alike other runtime functions.
> > > This is because modifying storage which may be shared with OS must be
> > > carefully designed and there is no general assumption to do that as in
> > > the case of [Get/]SetVariable.
> > > Instead, any capsule can be handed over to the firmware as a file on
> > > a specific file system. In other words, we only support "capsules on disk"
> > > for now.
> > > 
> > > Regarding firmware update, most of functionality is provided by FMP
> > > driver and it will be by nature system/platform-specific. So you can and
> > > should implement FMP drivers based on your system requirements.
> > > In this patch series, only a simple FMP driver based on FIT image for
> > > a single region is supported.  (So it is "for reference only")
> > > See more details in "efi_loader: capsule: add simple firmware management
> > > protocol."
> > > 
> > > Regarding variable update, the implementation here is based on a draft
> > > proposal[1] by Peter in Boot-arch ML. The specification should be discussed
> > > and finalized first. So the code doesn't fully implement Peter's idea.
> > > 
> > > [1] https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html
> > 
> > I do not like the idea of exposing variables as a configuration table as
> > operating system users may start doing strange things like accessing
> > variables via the configuration table. Why not keep the pointer to the
> > internal storage of variable hidden in your runtime code and expose the
> > runtime services defined in the UEFI spec?
> > 
> > Please, elaborate on the benefit of your approach.
> 
> I have already commented on your reply to my patch#11.
> 
> Rather, my aim of this patch is to discuss the following points:

Do you have any comments on them?

> > > 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.

We, Linaro, internally discussed this issue and agreed that the current
capsule handling should be split from UEFI initialization code,
efi_init_obj_list(), and that both of them should be called respectively
as early as possible in U-Boot initialization time, probably in board_init_r().

We may restrict this behavior only if CAPSULE is enabled.

> 
> and
> 
> > > TODO's
> > > ======
> > > (May or 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
> 
> What functionality should be initially included in my first implementation
> for capsule support.

Do you have any primary requirements so that you will be happy to
accept my patch series?

-Takahiro Akashi


> FYI, Sughosh is going to work on
>   * capsule authentication
>   * recovery from update failure (or at least, A/B partition)
> in the coming months.
> 
> Thanks,
> -Takahiro Akashi
>   
> 
> 
> > > 
> > > 
> > > Changes
> > > =======
> > > Initial release as RFC (March 17, 2020)
> > > 
> > > AKASHI Takahiro (14):
> > >    efi_loader: define OsIndicationsSupported flags
> > >    efi_loader: define System Resource Table macros
> > >    efi_loader: export a couple of protocol related functions
> > >    efi_loader: correct a definition of struct efi_capsule_header
> > >    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
> > >    efi_loader: capsule: support variable update
> > >    efi_loader: variable: export variables table for runtime access
> > >    cmd: add "efidebug capsule" command
> > >    tools: add mkeficapsule command for UEFI capsule update test
> > >    test/py: add efi capsule test
> > > 
> > >   cmd/efidebug.c                                | 234 +++++
> > >   include/efi_api.h                             | 214 ++++-
> > >   include/efi_loader.h                          |  53 ++
> > >   lib/efi_loader/Kconfig                        |  65 ++
> > >   lib/efi_loader/Makefile                       |   2 +
> > >   lib/efi_loader/efi_boottime.c                 |  29 +-
> > >   lib/efi_loader/efi_capsule.c                  | 860 ++++++++++++++++++
> > >   lib/efi_loader/efi_firmware.c                 | 191 ++++
> > >   lib/efi_loader/efi_runtime.c                  | 104 ++-
> > >   lib/efi_loader/efi_setup.c                    |  41 +-
> > >   lib/efi_loader/efi_variable.c                 | 109 +++
> > >   test/py/tests/test_efi_capsule/conftest.py    | 109 +++
> > >   test/py/tests/test_efi_capsule/defs.py        |  21 +
> > >   .../test_efi_capsule/test_capsule_firmware.py | 102 +++
> > >   .../test_efi_capsule/test_capsule_variable.py | 141 +++
> > >   test/py/tests/test_efi_capsule/uboot_env.its  |  25 +
> > >   tools/Makefile                                |   3 +
> > >   tools/mkeficapsule.c                          | 501 ++++++++++
> > >   18 files changed, 2744 insertions(+), 60 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/test_capsule_variable.py
> > >   create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its
> > >   create mode 100644 tools/mkeficapsule.c
> > > 
> > 

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

* [RFC 07/14] efi_loader: capsule: add memory range capsule definitions
  2020-03-18  7:57         ` AKASHI Takahiro
@ 2020-04-06  7:48           ` AKASHI Takahiro
  0 siblings, 0 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-04-06  7:48 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Wed, Mar 18, 2020 at 04:57:16PM +0900, AKASHI Takahiro wrote:
> On Wed, Mar 18, 2020 at 08:35:57AM +0100, Heinrich Schuchardt wrote:
> > On 3/18/20 2:22 AM, AKASHI Takahiro wrote:
> > > On Tue, Mar 17, 2020 at 09:11:43AM +0100, Heinrich Schuchardt wrote:
> > > > On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > > > > 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.
> > > > 
> > > > There are multiple places where RAM availability is tested by writing
> > > > during board bring up. Please, describe which changes are needed in ATF
> > > > and U-Boot to make memory range capsules work reliably.
> > > 
> > > I think that they are quite system/platform-specific
> > > and that it's beyond the scope of this patch.
> > > 
> > > Thanks,
> > > -Takahiro Akashi
> > 
> > To me it does not make sense to merge this development if the platform
> > compatibility is not evaluated.
> 
> What do you mean by "platform compatibility?"
> Please elaborate it and specific requirements, if any,
> for better understandings.

I need your feedback to improve my code.

-Takahiro Akashi

> -Takahiro Akashi
> 
> > Best regards
> > 
> > Heinrich
> > 
> > > 
> > > 
> > > > Consider also memory encryption provided by some CPUs.
> > > > https://www.tomshardware.com/news/intel-mktme-amd-memory-encryption,39467.html
> > > > 
> > > > Aren't file based capsules a much more reliable way for the OS to
> > > > communicate to the firmware?
> > > > 
> > > > Best regards
> > > > 
> > > > Heinrich
> > > > 
> > > > > 
> > > > > 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 ac2b38801c0c..b7bf21cac7ad 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
> > > > > 
> > > > 
> > 

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

* [RFC 09/14] efi_loader: add simple firmware management protocol for FIT image
  2020-03-18  9:06       ` Heinrich Schuchardt
@ 2020-04-06  7:59         ` AKASHI Takahiro
  0 siblings, 0 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-04-06  7:59 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Wed, Mar 18, 2020 at 10:06:38AM +0100, Heinrich Schuchardt wrote:
> On 3/18/20 9:17 AM, AKASHI Takahiro wrote:
> > On Wed, Mar 18, 2020 at 09:04:44AM +0100, Heinrich Schuchardt wrote:
> > > On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > > > 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_loader.h          |   3 +
> > > >    lib/efi_loader/Kconfig        |  24 ++++-
> > > >    lib/efi_loader/Makefile       |   1 +
> > > >    lib/efi_loader/efi_firmware.c | 191 ++++++++++++++++++++++++++++++++++
> > > >    4 files changed, 218 insertions(+), 1 deletion(-)
> > > >    create mode 100644 lib/efi_loader/efi_firmware.c
> > > > 
> > > > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > > > index c701672e18db..79bdf9586d24 100644
> > > > --- a/include/efi_loader.h
> > > > +++ b/include/efi_loader.h
> > > > @@ -134,6 +134,7 @@ extern const struct efi_hii_config_access_protocol efi_hii_config_access;
> > > >    extern const struct efi_hii_database_protocol efi_hii_database;
> > > >    extern const struct efi_hii_string_protocol efi_hii_string;
> > > >    extern const struct efi_rng_protocol efi_rng_protocol;
> > > > +extern const struct efi_firmware_management_protocol efi_fmp_fit;
> > > > 
> > > >    uint16_t *efi_dp_str(struct efi_device_path *dp);
> > > > 
> > > > @@ -180,6 +181,8 @@ extern const efi_guid_t efi_guid_hii_database_protocol;
> > > >    extern const efi_guid_t efi_guid_hii_string_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;
> > > > 
> > > >    /* GUID of RNG protocol */
> > > >    extern const efi_guid_t efi_guid_rng_protocol;
> > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > > > index 43d6f75d557a..41b1e9b5543c 100644
> > > > --- a/lib/efi_loader/Kconfig
> > > > +++ b/lib/efi_loader/Kconfig
> > > > @@ -97,7 +97,6 @@ config EFI_CAPSULE_UPDATE
> > > >    	  Select this option if you want to use capsule update feature,
> > > >    	  including firmware updates and variable updates.
> > > > 
> > > > -
> > > >    if EFI_CAPSULE_UPDATE
> > > > 
> > > >    config EFI_CAPSULE_UPDATE_FIRMWARE
> > > > @@ -107,6 +106,29 @@ config EFI_CAPSULE_UPDATE_FIRMWARE
> > > >    	  Select this option if you want to enable capsule-based
> > > >    	  firmware update
> > > > 
> > > > +config EFI_CAPSULE_FIT_SIMPLE
> > > > +	bool "Firmware management protocol for simple FIT image"
> > > > +	depends on EFI_CAPSULE_UPDATE_FIRMWARE
> > > > +	depends on FIT
> > > > +	select UPDATE_TFTP
> > > 
> > > UPDATE_TFTP is a very unsecure setting. A rogue DHCP and tFTP server can
> > > be used to compromise a device where this is enabled.
> > > 
> > > Why should we need to enable an insecure network protocol to have
> > > capsule updates?
> > 
> > 1. This is a sample FMP driver to demonstrate a power of capsule
> > 2. update_tftp() is called *only* against the interface and device
> >     that are specified by configuration. It's up to the developer.
> 
> I am concerned about the dhcp or tftp command becoming insecure with
> CONFIG_UPDATE_TFTP=y.

I don't know how CONFIG_UPDATE_TFTP can cause insecure situation here.
See my comment in [1].

> I would prefer a CONFIG_UPDATE controlling if common/update.c is
> compiled and a separate CONFIG_UPDATE_TFTP which enables what is tFTP
> specific.

It would be possible, but it also means that the meaning of CONFIG_UPDATE_TFTP
will be changed. Is it acceptable?

 [1] https://lists.denx.de/pipermail/u-boot/2020-March/401741.html

-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> > 3. Later on, capsule authentication support will be implemented.
> > 
> > So I believe that my approach here makes good sense.
> > 
> > -Takahiro Akashi
> > 
> > > Best regards
> > > 
> > > Heinrich
> > > 
> > > > +	select DFU
> > > > +	default n
> > > > +	help
> > > > +	  Select this option if you want to enable firmware management protocol
> > > > +	  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 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 for storing FIT image
> > > > +
> > > >    endif
> > > > 
> > > >    config EFI_CAPSULE_ON_DISK
> > > > diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> > > > index f19096924bef..50da10e0e3d9 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_CAPSULE_UPDATE) += 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..021c93196242
> > > > --- /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) 2018 Linaro Limited
> > > > + *			Author: AKASHI Takahiro
> > > > + */
> > > > +
> > > > +#include <common.h>
> > > > +#include <efi_loader.h>
> > > > +#include <net.h>
> > > > +
> > > > +/*
> > > > + * This FIMRWARE_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;
> > > > +}
> > > > 
> > > 
> 

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

* [RFC 00/14] efi_loader: add capsule update support
  2020-03-31  4:36     ` AKASHI Takahiro
@ 2020-04-14  4:38       ` AKASHI Takahiro
  0 siblings, 0 replies; 49+ messages in thread
From: AKASHI Takahiro @ 2020-04-14  4:38 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Tue, Mar 31, 2020 at 01:36:19PM +0900, AKASHI Takahiro wrote:
> Heinrich,
> 
> On Wed, Mar 18, 2020 at 11:04:05AM +0900, AKASHI Takahiro wrote:
> > Heinrich,
> > 
> > Thank you for your quick review.
> > 
> > On Tue, Mar 17, 2020 at 08:49:12AM +0100, Heinrich Schuchardt wrote:
> > > On 3/17/20 3:12 AM, AKASHI Takahiro wrote:
> > > > Summary
> > > > =======
> > > > 'UpdateCapsule' is one of runtime services defined in UEFI specification
> > > > and its aim is to allow a caller to pass information to the firmware.
> > > > This is mostly used to update firmware binary on devices by instructions
> > > > from OS.
> > > > 
> > > > In this patch series, all the related definitions and structures are given
> > > > as UEFI specification describes and basic framework for capsule support is
> > > > implemented. Currently supported types of capsule are
> > > >   * firmware update (Firmware Management Protocol or simply FMP)
> > > >   * variable update
> > > > 
> > > > UpdateCapsule is a runtime services function, but is, at least initially,
> > > > provided only before exiting boot services alike other runtime functions.
> > > > This is because modifying storage which may be shared with OS must be
> > > > carefully designed and there is no general assumption to do that as in
> > > > the case of [Get/]SetVariable.
> > > > Instead, any capsule can be handed over to the firmware as a file on
> > > > a specific file system. In other words, we only support "capsules on disk"
> > > > for now.
> > > > 
> > > > Regarding firmware update, most of functionality is provided by FMP
> > > > driver and it will be by nature system/platform-specific. So you can and
> > > > should implement FMP drivers based on your system requirements.
> > > > In this patch series, only a simple FMP driver based on FIT image for
> > > > a single region is supported.  (So it is "for reference only")
> > > > See more details in "efi_loader: capsule: add simple firmware management
> > > > protocol."
> > > > 
> > > > Regarding variable update, the implementation here is based on a draft
> > > > proposal[1] by Peter in Boot-arch ML. The specification should be discussed
> > > > and finalized first. So the code doesn't fully implement Peter's idea.
> > > > 
> > > > [1] https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html
> > > 
> > > I do not like the idea of exposing variables as a configuration table as
> > > operating system users may start doing strange things like accessing
> > > variables via the configuration table. Why not keep the pointer to the
> > > internal storage of variable hidden in your runtime code and expose the
> > > runtime services defined in the UEFI spec?
> > > 
> > > Please, elaborate on the benefit of your approach.
> > 
> > I have already commented on your reply to my patch#11.
> > 
> > Rather, my aim of this patch is to discuss the following points:
> 
> Do you have any comments on them?

No comment?
Do you mind my posting v1 without addressing any TODO list below?
(I'm going to add some enhancement to address "timing of executing
capsule" issue in v1.)

Thanks,
-Takahiro Akashi


> > > > 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.
> 
> We, Linaro, internally discussed this issue and agreed that the current
> capsule handling should be split from UEFI initialization code,
> efi_init_obj_list(), and that both of them should be called respectively
> as early as possible in U-Boot initialization time, probably in board_init_r().
> 
> We may restrict this behavior only if CAPSULE is enabled.
> 
> > 
> > and
> > 
> > > > TODO's
> > > > ======
> > > > (May or 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
> > 
> > What functionality should be initially included in my first implementation
> > for capsule support.
> 
> Do you have any primary requirements so that you will be happy to
> accept my patch series?
> 
> -Takahiro Akashi
> 
> 
> > FYI, Sughosh is going to work on
> >   * capsule authentication
> >   * recovery from update failure (or at least, A/B partition)
> > in the coming months.
> > 
> > Thanks,
> > -Takahiro Akashi
> >   
> > 
> > 
> > > > 
> > > > 
> > > > Changes
> > > > =======
> > > > Initial release as RFC (March 17, 2020)
> > > > 
> > > > AKASHI Takahiro (14):
> > > >    efi_loader: define OsIndicationsSupported flags
> > > >    efi_loader: define System Resource Table macros
> > > >    efi_loader: export a couple of protocol related functions
> > > >    efi_loader: correct a definition of struct efi_capsule_header
> > > >    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
> > > >    efi_loader: capsule: support variable update
> > > >    efi_loader: variable: export variables table for runtime access
> > > >    cmd: add "efidebug capsule" command
> > > >    tools: add mkeficapsule command for UEFI capsule update test
> > > >    test/py: add efi capsule test
> > > > 
> > > >   cmd/efidebug.c                                | 234 +++++
> > > >   include/efi_api.h                             | 214 ++++-
> > > >   include/efi_loader.h                          |  53 ++
> > > >   lib/efi_loader/Kconfig                        |  65 ++
> > > >   lib/efi_loader/Makefile                       |   2 +
> > > >   lib/efi_loader/efi_boottime.c                 |  29 +-
> > > >   lib/efi_loader/efi_capsule.c                  | 860 ++++++++++++++++++
> > > >   lib/efi_loader/efi_firmware.c                 | 191 ++++
> > > >   lib/efi_loader/efi_runtime.c                  | 104 ++-
> > > >   lib/efi_loader/efi_setup.c                    |  41 +-
> > > >   lib/efi_loader/efi_variable.c                 | 109 +++
> > > >   test/py/tests/test_efi_capsule/conftest.py    | 109 +++
> > > >   test/py/tests/test_efi_capsule/defs.py        |  21 +
> > > >   .../test_efi_capsule/test_capsule_firmware.py | 102 +++
> > > >   .../test_efi_capsule/test_capsule_variable.py | 141 +++
> > > >   test/py/tests/test_efi_capsule/uboot_env.its  |  25 +
> > > >   tools/Makefile                                |   3 +
> > > >   tools/mkeficapsule.c                          | 501 ++++++++++
> > > >   18 files changed, 2744 insertions(+), 60 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/test_capsule_variable.py
> > > >   create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its
> > > >   create mode 100644 tools/mkeficapsule.c
> > > > 
> > > 

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

end of thread, other threads:[~2020-04-14  4:38 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-17  2:12 [RFC 00/14] efi_loader: add capsule update support AKASHI Takahiro
2020-03-17  2:12 ` [RFC 01/14] efi_loader: define OsIndicationsSupported flags AKASHI Takahiro
2020-03-17  7:03   ` Heinrich Schuchardt
2020-03-18  1:18     ` AKASHI Takahiro
2020-03-18 18:01       ` Heinrich Schuchardt
2020-03-17  2:12 ` [RFC 02/14] efi_loader: define System Resource Table macros AKASHI Takahiro
2020-03-17  7:06   ` Heinrich Schuchardt
2020-03-18 18:02     ` Heinrich Schuchardt
2020-03-17  2:12 ` [RFC 03/14] efi_loader: export a couple of protocol related functions AKASHI Takahiro
2020-03-17  7:19   ` Heinrich Schuchardt
2020-03-18 18:03   ` Heinrich Schuchardt
2020-03-17  2:12 ` [RFC 04/14] efi_loader: correct a definition of struct efi_capsule_header AKASHI Takahiro
2020-03-17  7:25   ` Heinrich Schuchardt
2020-03-18 18:03   ` Heinrich Schuchardt
2020-03-17  2:12 ` [RFC 05/14] efi_loader: define UpdateCapsule api AKASHI Takahiro
2020-03-17  2:12 ` [RFC 06/14] efi_loader: capsule: add capsule_on_disk support AKASHI Takahiro
2020-03-18  8:55   ` Heinrich Schuchardt
2020-03-19 17:08     ` Heinrich Schuchardt
2020-03-30  7:43       ` AKASHI Takahiro
2020-03-17  2:12 ` [RFC 07/14] efi_loader: capsule: add memory range capsule definitions AKASHI Takahiro
2020-03-17  8:11   ` Heinrich Schuchardt
2020-03-18  1:22     ` AKASHI Takahiro
2020-03-18  7:35       ` Heinrich Schuchardt
2020-03-18  7:57         ` AKASHI Takahiro
2020-04-06  7:48           ` AKASHI Takahiro
2020-03-17  2:12 ` [RFC 08/14] efi_loader: capsule: support firmware update AKASHI Takahiro
2020-03-18 14:09   ` Sughosh Ganu
2020-03-17  2:12 ` [RFC 09/14] efi_loader: add simple firmware management protocol for FIT image AKASHI Takahiro
2020-03-18  8:04   ` Heinrich Schuchardt
2020-03-18  8:17     ` AKASHI Takahiro
2020-03-18  9:06       ` Heinrich Schuchardt
2020-04-06  7:59         ` AKASHI Takahiro
2020-03-17  2:12 ` [RFC 10/14] efi_loader: capsule: support variable update AKASHI Takahiro
2020-03-17  2:12 ` [RFC 11/14] efi_loader: variable: export variables table for runtime access AKASHI Takahiro
2020-03-17  7:37   ` Heinrich Schuchardt
2020-03-18  1:53     ` AKASHI Takahiro
2020-03-19  9:30       ` Ilias Apalodimas
2020-03-18 13:54   ` Sughosh Ganu
2020-03-17  2:12 ` [RFC 12/14] cmd: add "efidebug capsule" command AKASHI Takahiro
2020-03-17  2:12 ` [RFC 13/14] tools: add mkeficapsule command for UEFI capsule update test AKASHI Takahiro
2020-03-17  7:58   ` Heinrich Schuchardt
2020-03-18  1:32     ` AKASHI Takahiro
2020-03-19  8:55       ` Ilias Apalodimas
2020-03-17  2:12 ` [RFC 14/14] test/py: add efi capsule test AKASHI Takahiro
2020-03-17  7:49 ` [RFC 00/14] efi_loader: add capsule update support Heinrich Schuchardt
2020-03-18  2:04   ` AKASHI Takahiro
2020-03-31  4:36     ` AKASHI Takahiro
2020-04-14  4:38       ` AKASHI Takahiro
2020-03-18 18:16 ` Sughosh Ganu

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.