All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/5] FMP versioning support
@ 2023-03-23 11:09 Masahisa Kojima
  2023-03-23 11:09 ` [PATCH v4 1/5] efi_loader: store firmware version into FmpState variable Masahisa Kojima
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Masahisa Kojima @ 2023-03-23 11:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Takahiro Akashi,
	Masahisa Kojima

Firmware version management is not implemented in the current
FMP implementation. This series aims to add the versioning support
in FMP.

EDK II reference implementation utilizes the FMP Payload Header
inserted right before the capsule payload. With this series,
U-Boot also follows the EDK II implementation.

Currently, there is no way to know the current running firmware
version through the EFI interface. FMP->GetImageInfo() returns
always 0 for the version number. So a user can not know that
expected firmware is running after the capsule update.

With this series applied, version number can be specified
in the capsule file generation with mkeficapsule tool, then
user can know the running firmware version through
FMP->GetImageInfo() and ESRT.

Note that this series does not mandate the FMP Payload Header,
compatible with boards that are already using the existing
U-Boot FMP implementation.
If no FMP Payload Header is found in the capsule file, fw_version,
lowest supported version, last attempt version and last attempt
status is set to 0 and this is the same behavior as existing FMP
implementation.

Major Changes in v4:
- add python-based test

Major Changes in v3:
- exclude CONFIG_FWU_MULTI_BANK_UPDATE case

Masahisa Kojima (5):
  efi_loader: store firmware version into FmpState variable
  efi_loader: versioning support in GetImageInfo
  efi_loader: check lowest supported version in capsule update
  mkeficapsule: add FMP Payload Header
  test/py: efi_capsule: test for FMP versioning

 doc/mkeficapsule.1                            |  16 +
 lib/efi_loader/efi_firmware.c                 | 328 ++++++++++++++++--
 test/py/tests/test_efi_capsule/conftest.py    |  58 ++++
 .../test_capsule_firmware_fit.py              | 187 ++++++++++
 .../test_capsule_firmware_raw.py              | 199 +++++++++++
 .../test_capsule_firmware_signed_fit.py       | 159 +++++++++
 .../test_capsule_firmware_signed_raw.py       | 169 +++++++++
 tools/eficapsule.h                            |  31 ++
 tools/mkeficapsule.c                          |  51 ++-
 9 files changed, 1161 insertions(+), 37 deletions(-)

-- 
2.17.1


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

* [PATCH v4 1/5] efi_loader: store firmware version into FmpState variable
  2023-03-23 11:09 [PATCH v4 0/5] FMP versioning support Masahisa Kojima
@ 2023-03-23 11:09 ` Masahisa Kojima
  2023-03-28 11:53   ` Ilias Apalodimas
  2023-03-23 11:09 ` [PATCH v4 2/5] efi_loader: versioning support in GetImageInfo Masahisa Kojima
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Masahisa Kojima @ 2023-03-23 11:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Takahiro Akashi,
	Masahisa Kojima

Firmware version management is not implemented in the current
FMP protocol.
EDK II reference implementation capsule generation script inserts
the FMP Payload Header right before the payload, it contains the
firmware version and lowest supported version.

This commit utilizes the FMP Payload Header, reads the header and
stores the firmware version, lowest supported version,
last attempt version and last attempt status into "FmpStateXXXX"
EFI non-volatile variable. XXXX indicates the image index,
since FMP protocol handles multiple image indexes.

This change is compatible with the existing FMP implementation.
This change does not mandate the FMP Payload Header.
If no FMP Payload Header is found in the capsule file, fw_version,
lowest supported version, last attempt version and last attempt
status is 0 and this is the same behavior as existing FMP
implementation.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v4:
- move lines that are the same in both branches out of the if statement
- s/EDK2/EDK II/
- create print result function
- set last_attempt_version when capsule authentication failed
- use log_err() instead of printf()

Changes in v3:
- exclude CONFIG_FWU_MULTI_BANK_UPDATE case
- set image_type_id as a vendor field of FmpStateXXXX variable
- set READ_ONLY flag for FmpStateXXXX variable
- add error code for FIT image case

Changes in v2:
- modify indent

 lib/efi_loader/efi_firmware.c | 250 ++++++++++++++++++++++++++++++----
 1 file changed, 222 insertions(+), 28 deletions(-)

diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index 93e2b01c07..fb5f7906d3 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -10,6 +10,7 @@
 #include <charset.h>
 #include <dfu.h>
 #include <efi_loader.h>
+#include <efi_variable.h>
 #include <fwu.h>
 #include <image.h>
 #include <signatures.h>
@@ -36,6 +37,24 @@ struct fmp_payload_header {
 	u32 lowest_supported_version;
 };
 
+/**
+ * struct fmp_state - fmp firmware update state
+ *
+ * This structure describes the state of the firmware update
+ * through FMP protocol.
+ *
+ * @fw_version:			Firmware versions used
+ * @lowest_supported_version:	Lowest supported version
+ * @last_attempt_version:	Last attempt version
+ * @last_attempt_status:	Last attempt status
+ */
+struct fmp_state {
+	u32 fw_version;
+	u32 lowest_supported_version;
+	u32 last_attempt_version;
+	u32 last_attempt_status;
+};
+
 __weak void set_dfu_alt_info(char *interface, char *devstr)
 {
 	env_set("dfu_alt_info", update_info.dfu_string);
@@ -102,6 +121,29 @@ efi_status_t EFIAPI efi_firmware_set_package_info_unsupported(
 	return EFI_EXIT(EFI_UNSUPPORTED);
 }
 
+/**
+ * efi_firmware_get_image_type_id - get image_type_id
+ * @image_index:	image index
+ *
+ * Return the image_type_id identified by the image index.
+ *
+ * Return:		pointer to the image_type_id, NULL if image_index is invalid
+ */
+static
+efi_guid_t *efi_firmware_get_image_type_id(u8 image_index)
+{
+	int i;
+	struct efi_fw_image *fw_array;
+
+	fw_array = update_info.images;
+	for (i = 0; i < num_image_type_guids; i++) {
+		if (fw_array[i].image_index == image_index)
+			return &fw_array[i].image_type_id;
+	}
+
+	return NULL;
+}
+
 /**
  * efi_fill_image_desc_array - populate image descriptor array
  * @image_info_size:		Size of @image_info
@@ -182,6 +224,7 @@ static efi_status_t efi_fill_image_desc_array(
  * efi_firmware_capsule_authenticate - authenticate the capsule if enabled
  * @p_image:		Pointer to new image
  * @p_image_size:	Pointer to size of new image
+ * @state		Pointer to fmp state
  *
  * Authenticate the capsule if authentication is enabled.
  * The image pointer and the image size are updated in case of success.
@@ -190,14 +233,13 @@ static efi_status_t efi_fill_image_desc_array(
  */
 static
 efi_status_t efi_firmware_capsule_authenticate(const void **p_image,
-					       efi_uintn_t *p_image_size)
+					       efi_uintn_t *p_image_size,
+					       struct fmp_state *state)
 {
 	const void *image = *p_image;
 	efi_uintn_t image_size = *p_image_size;
-	u32 fmp_hdr_signature;
-	struct fmp_payload_header *header;
 	void *capsule_payload;
-	efi_status_t status;
+	efi_status_t status = EFI_SUCCESS;
 	efi_uintn_t capsule_payload_size;
 
 	if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)) {
@@ -208,13 +250,14 @@ efi_status_t efi_firmware_capsule_authenticate(const void **p_image,
 						  &capsule_payload_size);
 
 		if (status == EFI_SECURITY_VIOLATION) {
-			printf("Capsule authentication check failed. Aborting update\n");
-			return status;
+			log_err("Capsule authentication check failed. Aborting update\n");
+			state->last_attempt_status =
+				LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR;
 		} else if (status != EFI_SUCCESS) {
-			return status;
+			state->last_attempt_status =
+				LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
 		}
 
-		debug("Capsule authentication successful\n");
 		image = capsule_payload;
 		image_size = capsule_payload_size;
 	} else {
@@ -222,24 +265,145 @@ efi_status_t efi_firmware_capsule_authenticate(const void **p_image,
 		debug("Updating capsule without authenticating.\n");
 	}
 
-	fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
-	header = (void *)image;
+	*p_image = image;
+	*p_image_size = image_size;
+
+	return status;
+}
+
+/**
+ * efi_firmware_set_fmp_state_var - set FmpStateXXXX variable
+ * @state:		Pointer to fmp state
+ * @image_index:	image index
+ * @updated:		flag to indicate firmware update is successful
+ *
+ * Update the FmpStateXXXX variable with the firmware update state.
+ *
+ * Return:		status code
+ */
+static
+efi_status_t efi_firmware_set_fmp_state_var(struct fmp_state *state, u8 image_index,
+					    bool updated)
+{
+	u16 varname[13]; /* u"FmpStateXXXX" */
+	efi_status_t ret;
+	efi_uintn_t size;
+	efi_guid_t *image_type_id;
+	struct fmp_state var_state = { 0 };
+
+	image_type_id = efi_firmware_get_image_type_id(image_index);
+	if (!image_type_id)
+		return EFI_INVALID_PARAMETER;
+
+	efi_create_indexed_name(varname, sizeof(varname), "FmpState",
+				image_index);
+	size = sizeof(var_state);
+	ret = efi_get_variable_int(varname, image_type_id, NULL, &size,
+				   &var_state, NULL);
+	if (ret != EFI_SUCCESS && ret != EFI_NOT_FOUND)
+		return ret;
+
+	/*
+	 * When the capsule update is successful, FmpStateXXXX variable is set
+	 * according to the fmp payload header information. If there is no fmp payload
+	 * header in the capsule file, all values are set to 0.
+	 * When the capsule update fails, only last attempt information of FmpStateXXXX
+	 * variable is updated, fw_version and lowest_supported_version keep original
+	 * value or 0(in case no FmpStateXXXX variable found).
+	 */
+	if (updated) {
+		var_state.fw_version = state->fw_version;
+		var_state.lowest_supported_version = state->lowest_supported_version;
+	}
+	var_state.last_attempt_version = state->last_attempt_version;
+	var_state.last_attempt_status = state->last_attempt_status;
+
+	ret = efi_set_variable_int(varname, image_type_id,
+				   EFI_VARIABLE_READ_ONLY |
+				   EFI_VARIABLE_NON_VOLATILE |
+				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				   EFI_VARIABLE_RUNTIME_ACCESS,
+				   sizeof(var_state), &var_state, false);
+
+	return ret;
+}
 
+/**
+ * efi_firmware_parse_payload_header - parse FMP payload header
+ * @p_image:		Pointer to new image
+ * @p_image_size:	Pointer to size of new image
+ * @state		Pointer to fmp state
+ *
+ * Parse the FMP payload header and fill the fmp_state structure.
+ * If no FMP payload header is found, fmp_state structure is not updated.
+ *
+ */
+static
+void efi_firmware_parse_payload_header(const void **p_image,
+				       efi_uintn_t *p_image_size,
+				       struct fmp_state *state)
+{
+	const void *image = *p_image;
+	efi_uintn_t image_size = *p_image_size;
+	const struct fmp_payload_header *header;
+	u32 fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
+
+	header = image;
 	if (!memcmp(&header->signature, &fmp_hdr_signature,
 		    sizeof(fmp_hdr_signature))) {
-		/*
-		 * When building the capsule with the scripts in
-		 * edk2, a FMP header is inserted above the capsule
-		 * payload. Compensate for this header to get the
-		 * actual payload that is to be updated.
-		 */
+		/* FMP header is inserted above the capsule payload */
+		state->fw_version = header->fw_version;
+		state->lowest_supported_version = header->lowest_supported_version;
+		state->last_attempt_version = header->fw_version;
 		image += header->header_size;
 		image_size -= header->header_size;
 	}
 
 	*p_image = image;
 	*p_image_size = image_size;
-	return EFI_SUCCESS;
+}
+
+/**
+ * efi_firmware_verify_image - verify image
+ * @p_image:		Pointer to new image
+ * @p_image_size:	Pointer to size of new image
+ * @image_index		Image index
+ * @state		Pointer to fmp state
+ *
+ * Verify the capsule file
+ *
+ * Return:		status code
+ */
+static
+efi_status_t efi_firmware_verify_image(const void **p_image,
+				       efi_uintn_t *p_image_size,
+				       u8 image_index,
+				       struct fmp_state *state)
+{
+	efi_status_t ret;
+
+	ret = efi_firmware_capsule_authenticate(p_image, p_image_size, state);
+	efi_firmware_parse_payload_header(p_image, p_image_size, state);
+
+	return ret;
+}
+
+/**
+ * efi_firmware_print_result - print the firmware update result
+ * @status:	status code
+ * @state	Pointer to fmp state
+ *
+ * Print the firmware update result
+ */
+void efi_firmware_print_result(efi_status_t status, struct fmp_state *state)
+{
+	if (status == EFI_SUCCESS) {
+		if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE))
+			log_info("Firmware successfully written\n");
+		else
+			log_info("Firmware updated to version %u\n",
+				 state->fw_version);
+	}
 }
 
 /**
@@ -330,7 +494,9 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
 	efi_status_t (*progress)(efi_uintn_t completion),
 	u16 **abort_reason)
 {
+	bool updated;
 	efi_status_t status;
+	struct fmp_state state = { 0 };
 
 	EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
 		  image_size, vendor_code, progress, abort_reason);
@@ -338,14 +504,25 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
 	if (!image || image_index != 1)
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
 
-	status = efi_firmware_capsule_authenticate(&image, &image_size);
+	status = efi_firmware_verify_image(&image, &image_size, image_index,
+					   &state);
 	if (status != EFI_SUCCESS)
-		return EFI_EXIT(status);
+		goto err;
+
+	if (fit_update(image)) {
+		status = EFI_DEVICE_ERROR;
+		state.last_attempt_status = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
+		goto err;
+	}
 
-	if (fit_update(image))
-		return EFI_EXIT(EFI_DEVICE_ERROR);
+	state.last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
+err:
+	updated = (status == EFI_SUCCESS) ? true : false;
+	efi_firmware_set_fmp_state_var(&state, image_index, updated);
 
-	return EFI_EXIT(EFI_SUCCESS);
+	efi_firmware_print_result(status, &state);
+
+	return EFI_EXIT(status);
 }
 
 const struct efi_firmware_management_protocol efi_fmp_fit = {
@@ -391,7 +568,9 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
 	u16 **abort_reason)
 {
 	int ret;
+	bool updated;
 	efi_status_t status;
+	struct fmp_state state = { 0 };
 
 	EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
 		  image_size, vendor_code, progress, abort_reason);
@@ -399,9 +578,10 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
 	if (!image)
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
 
-	status = efi_firmware_capsule_authenticate(&image, &image_size);
+	status = efi_firmware_verify_image(&image, &image_size, image_index,
+					   &state);
 	if (status != EFI_SUCCESS)
-		return EFI_EXIT(status);
+		goto err;
 
 	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
 		/*
@@ -411,15 +591,29 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
 		ret = fwu_get_image_index(&image_index);
 		if (ret) {
 			log_debug("Unable to get FWU image_index\n");
-			return EFI_EXIT(EFI_DEVICE_ERROR);
+			status = EFI_DEVICE_ERROR;
+			goto err;
 		}
 	}
 
 	if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
-			     NULL, NULL))
-		return EFI_EXIT(EFI_DEVICE_ERROR);
+			     NULL, NULL)) {
+		status = EFI_DEVICE_ERROR;
+		state.last_attempt_status = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
+		goto err;
+	}
+
+	state.last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
+err:
+	updated = (status == EFI_SUCCESS) ? true : false;
+
+	/* TODO: implement versioning for FWU multi bank update */
+	if (!IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE))
+		efi_firmware_set_fmp_state_var(&state, image_index, updated);
+
+	efi_firmware_print_result(status, &state);
 
-	return EFI_EXIT(EFI_SUCCESS);
+	return EFI_EXIT(status);
 }
 
 const struct efi_firmware_management_protocol efi_fmp_raw = {
-- 
2.17.1


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

* [PATCH v4 2/5] efi_loader: versioning support in GetImageInfo
  2023-03-23 11:09 [PATCH v4 0/5] FMP versioning support Masahisa Kojima
  2023-03-23 11:09 ` [PATCH v4 1/5] efi_loader: store firmware version into FmpState variable Masahisa Kojima
@ 2023-03-23 11:09 ` Masahisa Kojima
  2023-03-28  6:52   ` Ilias Apalodimas
  2023-03-23 11:09 ` [PATCH v4 3/5] efi_loader: check lowest supported version in capsule update Masahisa Kojima
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Masahisa Kojima @ 2023-03-23 11:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Takahiro Akashi,
	Masahisa Kojima

Current FMP->GetImageInfo() always return 0 for the firmware
version, user can not identify which firmware version is currently
running through the EFI interface.

This commit reads the "FmpStateXXXX" EFI variable, then fills the
firmware version, lowest supported version, last attempt version
and last attempt status in FMP->GetImageInfo().

Now FMP->GetImageInfo() and ESRT have the meaningful version number.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
No update since v1

 lib/efi_loader/efi_firmware.c | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index fb5f7906d3..7536fcfa55 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -190,13 +190,38 @@ static efi_status_t efi_fill_image_desc_array(
 	*package_version_name = NULL; /* not supported */
 
 	for (i = 0; i < num_image_type_guids; i++) {
+		u16 varname[13]; /* u"FmpStateXXXX" */
+		efi_status_t ret;
+		efi_uintn_t size;
+		struct fmp_state var_state = { 0 };
+
 		image_info[i].image_index = fw_array[i].image_index;
 		image_info[i].image_type_id = fw_array[i].image_type_id;
 		image_info[i].image_id = fw_array[i].image_index;
 
 		image_info[i].image_id_name = fw_array[i].fw_name;
 
-		image_info[i].version = 0; /* not supported */
+		efi_create_indexed_name(varname, sizeof(varname), "FmpState",
+					fw_array[i].image_index);
+		size = sizeof(var_state);
+		ret = efi_get_variable_int(varname, &fw_array[i].image_type_id,
+					   NULL, &size, &var_state, NULL);
+		if (ret == EFI_SUCCESS) {
+			image_info[i].version = var_state.fw_version;
+			image_info[i].lowest_supported_image_version =
+				var_state.lowest_supported_version;
+			image_info[i].last_attempt_version =
+				var_state.last_attempt_version;
+			image_info[i].last_attempt_status =
+				var_state.last_attempt_status;
+		} else {
+			image_info[i].version = 0;
+			image_info[i].lowest_supported_image_version = 0;
+			image_info[i].last_attempt_version = 0;
+			image_info[i].last_attempt_status =
+				LAST_ATTEMPT_STATUS_SUCCESS;
+		}
+
 		image_info[i].version_name = NULL; /* not supported */
 		image_info[i].size = 0;
 		image_info[i].attributes_supported =
@@ -210,9 +235,6 @@ static efi_status_t efi_fill_image_desc_array(
 			image_info[0].attributes_setting |=
 				IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
 
-		image_info[i].lowest_supported_image_version = 0;
-		image_info[i].last_attempt_version = 0;
-		image_info[i].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
 		image_info[i].hardware_instance = 1;
 		image_info[i].dependencies = NULL;
 	}
-- 
2.17.1


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

* [PATCH v4 3/5] efi_loader: check lowest supported version in capsule update
  2023-03-23 11:09 [PATCH v4 0/5] FMP versioning support Masahisa Kojima
  2023-03-23 11:09 ` [PATCH v4 1/5] efi_loader: store firmware version into FmpState variable Masahisa Kojima
  2023-03-23 11:09 ` [PATCH v4 2/5] efi_loader: versioning support in GetImageInfo Masahisa Kojima
@ 2023-03-23 11:09 ` Masahisa Kojima
  2023-03-28  6:54   ` Ilias Apalodimas
  2023-03-23 11:09 ` [PATCH v4 4/5] mkeficapsule: add FMP Payload Header Masahisa Kojima
  2023-03-23 11:09 ` [PATCH v4 5/5] test/py: efi_capsule: test for FMP versioning Masahisa Kojima
  4 siblings, 1 reply; 14+ messages in thread
From: Masahisa Kojima @ 2023-03-23 11:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Takahiro Akashi,
	Masahisa Kojima

The FMP Payload Header which EDK II capsule generation scripts
insert contains lowest supported version.
This commit reads the lowest supported version stored in the
"FmpStateXXXX" EFI non-volatile variable, then check if the
firmware version of ongoing capsule is equal or greater than
the lowest supported version.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v4:
- use log_err() instead of printf()

Changes in v2:
- add error message when the firmware version is lower than
  lowest supported version

 lib/efi_loader/efi_firmware.c | 50 ++++++++++++++++++++++++++++++++++-
 1 file changed, 49 insertions(+), 1 deletion(-)

diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index 7536fcfa55..9c25b5bb38 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -385,6 +385,39 @@ void efi_firmware_parse_payload_header(const void **p_image,
 	*p_image_size = image_size;
 }
 
+/**
+ * efi_firmware_get_lowest_supported_version - get the lowest supported version
+ * @image_index:	image_index
+ *
+ * Get the lowest supported version from FmpStateXXXX variable.
+ *
+ * Return:		lowest supported version, return 0 if reading FmpStateXXXX
+ *			variable failed
+ */
+static
+u32 efi_firmware_get_lowest_supported_version(u8 image_index)
+{
+	u16 varname[13]; /* u"FmpStateXXXX" */
+	efi_status_t ret;
+	efi_uintn_t size;
+	efi_guid_t *image_type_id;
+	struct fmp_state var_state = { 0 };
+
+	image_type_id = efi_firmware_get_image_type_id(image_index);
+	if (!image_type_id)
+		return 0;
+
+	efi_create_indexed_name(varname, sizeof(varname), "FmpState",
+				image_index);
+	size = sizeof(var_state);
+	ret = efi_get_variable_int(varname, image_type_id, NULL, &size,
+				   &var_state, NULL);
+	if (ret != EFI_SUCCESS)
+		return 0;
+
+	return var_state.lowest_supported_version;
+}
+
 /**
  * efi_firmware_verify_image - verify image
  * @p_image:		Pointer to new image
@@ -392,7 +425,8 @@ void efi_firmware_parse_payload_header(const void **p_image,
  * @image_index		Image index
  * @state		Pointer to fmp state
  *
- * Verify the capsule file
+ * Verify the capsule authentication and check if the fw_version
+ * is equal or greater than the lowest supported version.
  *
  * Return:		status code
  */
@@ -403,10 +437,24 @@ efi_status_t efi_firmware_verify_image(const void **p_image,
 				       struct fmp_state *state)
 {
 	efi_status_t ret;
+	u32 lowest_supported_version;
 
 	ret = efi_firmware_capsule_authenticate(p_image, p_image_size, state);
 	efi_firmware_parse_payload_header(p_image, p_image_size, state);
 
+	/* check lowest_supported_version if capsule authentication passes */
+	if (ret == EFI_SUCCESS) {
+		lowest_supported_version =
+			efi_firmware_get_lowest_supported_version(image_index);
+		if (lowest_supported_version > state->fw_version) {
+			log_err("Firmware version %u too low. Expecting >= %u. Aborting update\n",
+				state->fw_version, lowest_supported_version);
+			state->last_attempt_status =
+				LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
+			ret = EFI_INVALID_PARAMETER;
+		}
+	}
+
 	return ret;
 }
 
-- 
2.17.1


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

* [PATCH v4 4/5] mkeficapsule: add FMP Payload Header
  2023-03-23 11:09 [PATCH v4 0/5] FMP versioning support Masahisa Kojima
                   ` (2 preceding siblings ...)
  2023-03-23 11:09 ` [PATCH v4 3/5] efi_loader: check lowest supported version in capsule update Masahisa Kojima
@ 2023-03-23 11:09 ` Masahisa Kojima
  2023-03-28  6:55   ` Ilias Apalodimas
  2023-03-23 11:09 ` [PATCH v4 5/5] test/py: efi_capsule: test for FMP versioning Masahisa Kojima
  4 siblings, 1 reply; 14+ messages in thread
From: Masahisa Kojima @ 2023-03-23 11:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Takahiro Akashi,
	Masahisa Kojima, Sughosh Ganu, Etienne Carriere

Current mkeficapsule tool does not provide firmware
version management. EDK II reference implementation inserts
the FMP Payload Header right before the payload.
It coutains the fw_version and lowest supported version.

This commit adds two new parameters required to generate
the FMP Payload Header for mkeficapsule tool.
 '-v' indicates the firmware version.
 '-l' indicates the lowest supported version.

When mkeficapsule tool is invoked with neither '-v' nor '-l' option,
FMP Payload Header is not inserted, the behavior is same as
current implementation.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
No update since v3

Changes in v3:
- remove '-f' option
- move some definitions into tools/eficapsule.h
- add dependency check of fw_version and lowest_supported_version
- remove unexpected modification of existing fprintf() call
- add documentation

Newly created in v2

 doc/mkeficapsule.1   | 16 ++++++++++++++
 tools/eficapsule.h   | 31 +++++++++++++++++++++++++++
 tools/mkeficapsule.c | 51 +++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 93 insertions(+), 5 deletions(-)

diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1
index 1ca245a10f..7c62b03c73 100644
--- a/doc/mkeficapsule.1
+++ b/doc/mkeficapsule.1
@@ -61,6 +61,22 @@ Specify an image index
 .BI "-I\fR,\fB --instance " instance
 Specify a hardware instance
 
+.PP
+FMP Payload Header is inserted right before the payload if
+.BR --fw-version
+or
+.BR --lsv
+are specified
+
+
+.TP
+.BI "-v\fR,\fB --fw-version " firmware-version
+Specify a firmware version, 0 if omitted
+
+.TP
+.BI "-l\fR,\fB --lsv " lowest-supported-version
+Specify a lowest supported version, 0 if omitted
+
 .PP
 For generation of firmware accept empty capsule
 .BR --guid
diff --git a/tools/eficapsule.h b/tools/eficapsule.h
index 072a4b5598..e5fa3398b6 100644
--- a/tools/eficapsule.h
+++ b/tools/eficapsule.h
@@ -113,4 +113,35 @@ struct efi_firmware_image_authentication {
 	struct win_certificate_uefi_guid auth_info;
 } __packed;
 
+/* fmp payload header */
+#define SIGNATURE_16(A, B)	((A) | ((B) << 8))
+#define SIGNATURE_32(A, B, C, D)	\
+	(SIGNATURE_16(A, B) | (SIGNATURE_16(C, D) << 16))
+
+#define FMP_PAYLOAD_HDR_SIGNATURE	SIGNATURE_32('M', 'S', 'S', '1')
+
+/**
+ * struct fmp_payload_header - EDK2 header for the FMP payload
+ *
+ * This structure describes the header which is preprended to the
+ * FMP payload by the edk2 capsule generation scripts.
+ *
+ * @signature:			Header signature used to identify the header
+ * @header_size:		Size of the structure
+ * @fw_version:			Firmware versions used
+ * @lowest_supported_version:	Lowest supported version
+ */
+struct fmp_payload_header {
+	uint32_t signature;
+	uint32_t header_size;
+	uint32_t fw_version;
+	uint32_t lowest_supported_version;
+};
+
+struct fmp_payload_header_params {
+	bool have_header;
+	uint32_t fw_version;
+	uint32_t lowest_supported_version;
+};
+
 #endif /* _EFI_CAPSULE_H */
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
index b71537beee..e50e6a8ed7 100644
--- a/tools/mkeficapsule.c
+++ b/tools/mkeficapsule.c
@@ -29,7 +29,7 @@ static const char *tool_name = "mkeficapsule";
 efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
 efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
 
-static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR";
+static const char *opts_short = "g:i:I:v:l:p:c:m:o:dhAR";
 
 enum {
 	CAPSULE_NORMAL_BLOB = 0,
@@ -41,6 +41,8 @@ static struct option options[] = {
 	{"guid", required_argument, NULL, 'g'},
 	{"index", required_argument, NULL, 'i'},
 	{"instance", required_argument, NULL, 'I'},
+	{"fw-version", required_argument, NULL, 'v'},
+	{"lsv", required_argument, NULL, 'l'},
 	{"private-key", required_argument, NULL, 'p'},
 	{"certificate", required_argument, NULL, 'c'},
 	{"monotonic-count", required_argument, NULL, 'm'},
@@ -60,6 +62,8 @@ static void print_usage(void)
 		"\t-g, --guid <guid string>    guid for image blob type\n"
 		"\t-i, --index <index>         update image index\n"
 		"\t-I, --instance <instance>   update hardware instance\n"
+		"\t-v, --fw-version <version>  firmware version\n"
+		"\t-l, --lsv <version>         lowest supported version\n"
 		"\t-p, --private-key <privkey file>  private key file\n"
 		"\t-c, --certificate <cert file>     signer's certificate file\n"
 		"\t-m, --monotonic-count <count>     monotonic count\n"
@@ -402,6 +406,7 @@ static void free_sig_data(struct auth_context *ctx)
  */
 static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 			unsigned long index, unsigned long instance,
+			struct fmp_payload_header_params *fmp_ph_params,
 			uint64_t mcount, char *privkey_file, char *cert_file,
 			uint16_t oemflags)
 {
@@ -410,10 +415,11 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 	struct efi_firmware_management_capsule_image_header image;
 	struct auth_context auth_context;
 	FILE *f;
-	uint8_t *data;
+	uint8_t *data, *new_data, *buf;
 	off_t bin_size;
 	uint64_t offset;
 	int ret;
+	struct fmp_payload_header payload_header;
 
 #ifdef DEBUG
 	fprintf(stderr, "For output: %s\n", path);
@@ -423,6 +429,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 	auth_context.sig_size = 0;
 	f = NULL;
 	data = NULL;
+	new_data = NULL;
 	ret = -1;
 
 	/*
@@ -431,12 +438,31 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 	if (read_bin_file(bin, &data, &bin_size))
 		goto err;
 
+	buf = data;
+
+	/* insert fmp payload header right before the payload */
+	if (fmp_ph_params->have_header) {
+		new_data = malloc(bin_size + sizeof(payload_header));
+		if (!new_data)
+			goto err;
+
+		payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
+		payload_header.header_size = sizeof(payload_header);
+		payload_header.fw_version = fmp_ph_params->fw_version;
+		payload_header.lowest_supported_version =
+			fmp_ph_params->lowest_supported_version;
+		memcpy(new_data, &payload_header, sizeof(payload_header));
+		memcpy(new_data + sizeof(payload_header), data, bin_size);
+		buf = new_data;
+		bin_size += sizeof(payload_header);
+	}
+
 	/* first, calculate signature to determine its size */
 	if (privkey_file && cert_file) {
 		auth_context.key_file = privkey_file;
 		auth_context.cert_file = cert_file;
 		auth_context.auth.monotonic_count = mcount;
-		auth_context.image_data = data;
+		auth_context.image_data = buf;
 		auth_context.image_size = bin_size;
 
 		if (create_auth_data(&auth_context)) {
@@ -536,7 +562,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 	/*
 	 * firmware binary
 	 */
-	if (write_capsule_file(f, data, bin_size, "Firmware binary"))
+	if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
 		goto err;
 
 	ret = 0;
@@ -545,6 +571,7 @@ err:
 		fclose(f);
 	free_sig_data(&auth_context);
 	free(data);
+	free(new_data);
 
 	return ret;
 }
@@ -644,6 +671,7 @@ int main(int argc, char **argv)
 	unsigned long oemflags;
 	char *privkey_file, *cert_file;
 	int c, idx;
+	struct fmp_payload_header_params fmp_ph_params = { 0 };
 
 	guid = NULL;
 	index = 0;
@@ -679,6 +707,14 @@ int main(int argc, char **argv)
 		case 'I':
 			instance = strtoul(optarg, NULL, 0);
 			break;
+		case 'v':
+			fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
+			fmp_ph_params.have_header = true;
+			break;
+		case 'l':
+			fmp_ph_params.lowest_supported_version = strtoul(optarg, NULL, 0);
+			fmp_ph_params.have_header = true;
+			break;
 		case 'p':
 			if (privkey_file) {
 				fprintf(stderr,
@@ -744,6 +780,11 @@ int main(int argc, char **argv)
 		exit(EXIT_FAILURE);
 	}
 
+	if (fmp_ph_params.fw_version < fmp_ph_params.lowest_supported_version) {
+		fprintf(stderr, "fw_version is lower than lowest_supported_version\n");
+		exit(EXIT_FAILURE);
+	}
+
 	if (capsule_type != CAPSULE_NORMAL_BLOB) {
 		if (create_empty_capsule(argv[argc - 1], guid,
 					 capsule_type == CAPSULE_ACCEPT) < 0) {
@@ -751,7 +792,7 @@ int main(int argc, char **argv)
 			exit(EXIT_FAILURE);
 		}
 	} else 	if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
-				 index, instance, mcount, privkey_file,
+				 index, instance, &fmp_ph_params, mcount, privkey_file,
 				 cert_file, (uint16_t)oemflags) < 0) {
 		fprintf(stderr, "Creating firmware capsule failed\n");
 		exit(EXIT_FAILURE);
-- 
2.17.1


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

* [PATCH v4 5/5] test/py: efi_capsule: test for FMP versioning
  2023-03-23 11:09 [PATCH v4 0/5] FMP versioning support Masahisa Kojima
                   ` (3 preceding siblings ...)
  2023-03-23 11:09 ` [PATCH v4 4/5] mkeficapsule: add FMP Payload Header Masahisa Kojima
@ 2023-03-23 11:09 ` Masahisa Kojima
  2023-03-28  6:55   ` Ilias Apalodimas
  4 siblings, 1 reply; 14+ messages in thread
From: Masahisa Kojima @ 2023-03-23 11:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Takahiro Akashi,
	Masahisa Kojima

This test covers FMP versioning for both raw and FIT image,
and both signed and non-signed capsule update.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Newly created in v4

 test/py/tests/test_efi_capsule/conftest.py    |  58 +++++
 .../test_capsule_firmware_fit.py              | 187 ++++++++++++++++
 .../test_capsule_firmware_raw.py              | 199 ++++++++++++++++++
 .../test_capsule_firmware_signed_fit.py       | 159 ++++++++++++++
 .../test_capsule_firmware_signed_raw.py       | 169 +++++++++++++++
 5 files changed, 772 insertions(+)

diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py
index 4879f2b5c2..b7332de844 100644
--- a/test/py/tests/test_efi_capsule/conftest.py
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -95,6 +95,26 @@ def efi_capsule_data(request, u_boot_config):
         check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid  058B7D83-50D5-4C47-A195-60D86AD341C4 uboot_bin_env.itb Test05' %
                    (data_dir, u_boot_config.build_dir),
                    shell=True)
+        check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 --lsv 3 '
+                        '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test101' %
+                   (data_dir, u_boot_config.build_dir),
+                   shell=True)
+        check_call('cd %s; %s/tools/mkeficapsule --index 2 --fw-version 10 --lsv 7 '
+                        '--guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test102' %
+                   (data_dir, u_boot_config.build_dir),
+                   shell=True)
+        check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 --lsv 1 '
+                        '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test103' %
+                   (data_dir, u_boot_config.build_dir),
+                   shell=True)
+        check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 --lsv 3 '
+                        '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test104' %
+                   (data_dir, u_boot_config.build_dir),
+                   shell=True)
+        check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 --lsv 1 '
+                        '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test105' %
+                   (data_dir, u_boot_config.build_dir),
+                   shell=True)
 
         if capsule_auth_enabled:
             # raw firmware signed with proper key
@@ -131,6 +151,44 @@ def efi_capsule_data(request, u_boot_config):
                             'uboot_bin_env.itb Test14'
                        % (data_dir, u_boot_config.build_dir),
                        shell=True)
+            # raw firmware signed with proper key with version information
+            check_call('cd %s; '
+                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
+                            '--fw-version 5 --lsv 3 '
+                            '--private-key SIGNER.key --certificate SIGNER.crt '
+                            '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
+                            'u-boot.bin.new Test111'
+                       % (data_dir, u_boot_config.build_dir),
+                       shell=True)
+            # raw firmware signed with *mal* key with version information
+            check_call('cd %s; '
+                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
+                            '--fw-version 8 --lsv 3 '
+                            '--private-key SIGNER2.key '
+                            '--certificate SIGNER2.crt '
+                            '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
+                            'u-boot.bin.new Test112'
+                       % (data_dir, u_boot_config.build_dir),
+                       shell=True)
+            # FIT firmware signed with proper key with version information
+            check_call('cd %s; '
+                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
+                            '--fw-version 5 --lsv 3 '
+                            '--private-key SIGNER.key --certificate SIGNER.crt '
+                            '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
+                            'uboot_bin_env.itb Test113'
+                       % (data_dir, u_boot_config.build_dir),
+                       shell=True)
+            # FIT firmware signed with *mal* key with version information
+            check_call('cd %s; '
+                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
+                            '--fw-version 8 --lsv 3 '
+                            '--private-key SIGNER2.key '
+                            '--certificate SIGNER2.crt '
+                            '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
+                            'uboot_bin_env.itb Test114'
+                       % (data_dir, u_boot_config.build_dir),
+                       shell=True)
 
         # Create a disk image with EFI system partition
         check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py
index d28b53a1a1..881fe233ec 100644
--- a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py
@@ -189,3 +189,190 @@ class TestEfiCapsuleFirmwareFit(object):
                 assert 'u-boot-env:Old' in ''.join(output)
             else:
                 assert 'u-boot-env:New' in ''.join(output)
+
+    def test_efi_capsule_fw3(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 3 - Update U-Boot and U-Boot environment on SPI Flash with version information
+                      0x100000-0x150000: U-Boot binary (but dummy)
+                      0x150000-0x200000: U-Boot environment (but dummy)
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 3-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e PlatformLangCodes', # workaround for terminal size determination
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize contents
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 150000 10',
+                'sf read 5000000 150000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test104' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test104 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test104' in ''.join(output)
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        capsule_auth = u_boot_config.buildconfig.get(
+            'config_efi_capsule_authenticate')
+
+        # reboot
+        u_boot_console.restart_uboot(expect_reset = capsule_early)
+
+        with u_boot_console.log.section('Test Case 3-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test104' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test104' not in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            if capsule_auth:
+                assert 'u-boot:Old' in ''.join(output)
+            else:
+                assert 'u-boot:New' in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf read 4000000 150000 10',
+                'md.b 4000000 10'])
+            if capsule_auth:
+                assert 'u-boot-env:Old' in ''.join(output)
+            else:
+                assert 'u-boot-env:New' in ''.join(output)
+
+            # make sure the dfu_alt_info exists because it is required for making ESRT.
+            output = u_boot_console.run_command_list([
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'efidebug capsule esrt'])
+
+            if capsule_auth:
+                assert 'ESRT: fw_version=5' not in ''.join(output)
+                assert 'ESRT: lowest_supported_fw_version=3' not in ''.join(output)
+            else:
+                assert 'ESRT: fw_version=5' in ''.join(output)
+                assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
+
+    def test_efi_capsule_fw4(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 4 - Update U-Boot and U-Boot environment on SPI Flash with version information
+                      but fw_version is lower than lowest_supported_version
+                      No update should happen
+                      0x100000-0x150000: U-Boot binary (but dummy)
+                      0x150000-0x200000: U-Boot environment (but dummy)
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 4-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e PlatformLangCodes', # workaround for terminal size determination
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize contents
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 150000 10',
+                'sf read 5000000 150000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test105' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test105 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test105' in ''.join(output)
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        capsule_auth = u_boot_config.buildconfig.get(
+            'config_efi_capsule_authenticate')
+
+        # reboot
+        u_boot_console.restart_uboot(expect_reset = capsule_early)
+
+        with u_boot_console.log.section('Test Case 4-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test105' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test105' not in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:Old' in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf read 4000000 150000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot-env:Old' in ''.join(output)
+
+            # make sure the dfu_alt_info exists because it is required for making ESRT.
+            output = u_boot_console.run_command_list([
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'efidebug capsule esrt'])
+
+            if not capsule_auth:
+                assert 'ESRT: last_attempt_version=2' in ''.join(output)
+                assert 'ESRT: last_attempt_status=incorrect version' in ''.join(output)
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py
index 92bfb14932..bb60065343 100644
--- a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py
@@ -292,3 +292,202 @@ class TestEfiCapsuleFirmwareRaw:
                 assert 'u-boot-env:Old' in ''.join(output)
             else:
                 assert 'u-boot-env:New' in ''.join(output)
+
+    def test_efi_capsule_fw4(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """ Test Case 4
+        Update U-Boot on SPI Flash, raw image format with fw_version and lowest_supported_version
+        0x100000-0x150000: U-Boot binary (but dummy)
+        0x150000-0x200000: U-Boot environment (but dummy)
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 4-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e PlatformLangCodes', # workaround for terminal size determination
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize contents
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 150000 10',
+                'sf read 5000000 150000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place the capsule files
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test101' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test101 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test101' in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test102' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test102 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test102' in ''.join(output)
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        capsule_auth = u_boot_config.buildconfig.get(
+            'config_efi_capsule_authenticate')
+
+        # reboot
+        u_boot_console.restart_uboot(expect_reset = capsule_early)
+
+        with u_boot_console.log.section('Test Case 4-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test101' in ''.join(output)
+                assert 'Test102' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test101' not in ''.join(output)
+            assert 'Test102' not in ''.join(output)
+
+            # make sure the dfu_alt_info exists because it is required for making ESRT.
+            output = u_boot_console.run_command_list([
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'efidebug capsule esrt'])
+
+            if capsule_auth:
+                # ensure that SANDBOX_UBOOT_IMAGE_GUID is in the ESRT.
+                assert '09D7CF52-0720-4710-91D1-08469B7FE9C8' in ''.join(output)
+                assert 'ESRT: fw_version=5' not in ''.join(output)
+                assert 'ESRT: lowest_supported_fw_version=3' not in ''.join(output)
+
+                # ensure that SANDBOX_UBOOT_ENV_IMAGE_GUID is in the ESRT.
+                assert '5A7021F5-FEF2-48B4-AABA-832E777418C0' in ''.join(output)
+                assert 'ESRT: fw_version=10' not in ''.join(output)
+                assert 'ESRT: lowest_supported_fw_version=7' not in ''.join(output)
+            else:
+                # ensure that SANDBOX_UBOOT_IMAGE_GUID is in the ESRT.
+                assert '09D7CF52-0720-4710-91D1-08469B7FE9C8' in ''.join(output)
+                assert 'ESRT: fw_version=5' in ''.join(output)
+                assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
+
+                # ensure that SANDBOX_UBOOT_ENV_IMAGE_GUID is in the ESRT.
+                assert '5A7021F5-FEF2-48B4-AABA-832E777418C0' in ''.join(output)
+                assert 'ESRT: fw_version=10' in ''.join(output)
+                assert 'ESRT: lowest_supported_fw_version=7' in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            if capsule_auth:
+                assert 'u-boot:Old' in ''.join(output)
+            else:
+                assert 'u-boot:New' in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 150000 10',
+                'md.b 4000000 10'])
+            if capsule_auth:
+                assert 'u-boot-env:Old' in ''.join(output)
+            else:
+                assert 'u-boot-env:New' in ''.join(output)
+
+    def test_efi_capsule_fw5(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """ Test Case 5
+        Update U-Boot on SPI Flash, raw image format with fw_version and lowest_supported_version
+        but fw_version is lower than lowest_supported_version
+        No update should happen
+        0x100000-0x150000: U-Boot binary (but dummy)
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 5-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e PlatformLangCodes', # workaround for terminal size determination
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize contents
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place the capsule files
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test103' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test103 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test103' in ''.join(output)
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        capsule_auth = u_boot_config.buildconfig.get(
+            'config_efi_capsule_authenticate')
+
+        # reboot
+        u_boot_console.restart_uboot(expect_reset = capsule_early)
+
+        with u_boot_console.log.section('Test Case 5-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test103' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test103' not in ''.join(output)
+
+            # make sure the dfu_alt_info exists because it is required for making ESRT.
+            output = u_boot_console.run_command_list([
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'efidebug capsule esrt'])
+
+            if not capsule_auth:
+                assert 'ESRT: last_attempt_version=2' in ''.join(output)
+                assert 'ESRT: last_attempt_status=incorrect version' in ''.join(output)
+
+            # make sure capsule update failed
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:Old' in ''.join(output)
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
index 8c2d616fd0..cf2a2c7ba6 100644
--- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
@@ -257,3 +257,162 @@ class TestEfiCapsuleFirmwareSignedFit(object):
                 'sf read 4000000 100000 10',
                 'md.b 4000000 10'])
             assert 'u-boot:Old' in ''.join(output)
+
+    def test_efi_capsule_auth4(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 4 - Update U-Boot on SPI Flash, FIT image format
+                      0x100000-0x150000: U-Boot binary (but dummy)
+
+                      If the capsule is properly signed with version information,
+                      the authentication should pass and the firmware be updated.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 4-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e PlatformLangCodes', # workaround for terminal size determination
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info '
+                        '"sf 0:0=u-boot-bin raw 0x100000 '
+                        '0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old'
+                        % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test113' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test113 $filesize'
+                        % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test113' in ''.join(output)
+
+        # reboot
+        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
+                                    + '/test_sig.dtb'
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 4-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info '
+                            '"sf 0:0=u-boot-bin raw 0x100000 '
+                            '0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test113' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test113' not in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:New' in ''.join(output)
+
+            output = u_boot_console.run_command('efidebug capsule esrt')
+            assert 'ESRT: fw_version=5' in ''.join(output)
+            assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
+
+    def test_efi_capsule_auth5(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 5 - Update U-Boot on SPI Flash, FIT image format
+                      0x100000-0x150000: U-Boot binary (but dummy)
+
+                      If the capsule is properly signed with version information,
+                      the authentication should pass and the firmware be updated.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 5-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e PlatformLangCodes', # workaround for terminal size determination
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info '
+                        '"sf 0:0=u-boot-bin raw 0x100000 '
+                        '0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old'
+                        % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test114' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test114 $filesize'
+                        % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test114' in ''.join(output)
+
+        # reboot
+        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
+                                    + '/test_sig.dtb'
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 5-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info '
+                            '"sf 0:0=u-boot-bin raw 0x100000 '
+                            '0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test114' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test114' not in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:Old' in ''.join(output)
+
+            output = u_boot_console.run_command('efidebug capsule esrt')
+            assert 'ESRT: fw_version=5' in ''.join(output)
+            assert 'ESRT: last_attempt_version=8' in ''.join(output)
+            assert 'ESRT: last_attempt_status=auth error' in ''.join(output)
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
index 2bbaa9cc55..631e78b1f1 100644
--- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
@@ -254,3 +254,172 @@ class TestEfiCapsuleFirmwareSignedRaw(object):
                 'sf read 4000000 100000 10',
                 'md.b 4000000 10'])
             assert 'u-boot:Old' in ''.join(output)
+
+    def test_efi_capsule_auth4(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 4 - Update U-Boot on SPI Flash, raw image format with version information
+                      0x100000-0x150000: U-Boot binary (but dummy)
+
+                      If the capsule is properly signed, the authentication
+                      should pass and the firmware be updated.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 4-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e PlatformLangCodes', # workaround for terminal size determination
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info '
+                        '"sf 0:0=u-boot-bin raw 0x100000 '
+                        '0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+
+            output = u_boot_console.run_command('efidebug capsule esrt')
+
+
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old'
+                        % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test111' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test111 $filesize'
+                            % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test111' in ''.join(output)
+
+        # reboot
+        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
+                                    + '/test_sig.dtb'
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 4-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info '
+                            '"sf 0:0=u-boot-bin raw 0x100000 '
+                            '0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test111' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test111' not in ''.join(output)
+
+            # TODO: check CapsuleStatus in CapsuleXXXX
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:New' in ''.join(output)
+
+            output = u_boot_console.run_command('efidebug capsule esrt')
+            assert 'ESRT: fw_version=5' in ''.join(output)
+            assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
+
+    def test_efi_capsule_auth5(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 5 - Update U-Boot on SPI Flash, raw image format with version information
+                      0x100000-0x150000: U-Boot binary (but dummy)
+
+                      If the capsule is signed but with an invalid key,
+                      the authentication should fail and the firmware
+                      not be updated.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 5-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'printenv -e PlatformLangCodes', # workaround for terminal size determination
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info '
+                        '"sf 0:0=u-boot-bin raw 0x100000 '
+                        '0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old'
+                        % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test112' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test112 $filesize'
+                            % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test112' in ''.join(output)
+
+        # reboot
+        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
+                                    + '/test_sig.dtb'
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 5-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info '
+                            '"sf 0:0=u-boot-bin raw 0x100000 '
+                            '0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test112' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test112' not in ''.join(output)
+
+            # TODO: check CapsuleStatus in CapsuleXXXX
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:Old' in ''.join(output)
+
+            output = u_boot_console.run_command('efidebug capsule esrt')
+            assert 'ESRT: fw_version=5' in ''.join(output)
+            assert 'ESRT: last_attempt_version=8' in ''.join(output)
+            assert 'ESRT: last_attempt_status=auth error' in ''.join(output)
-- 
2.17.1


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

* Re: [PATCH v4 2/5] efi_loader: versioning support in GetImageInfo
  2023-03-23 11:09 ` [PATCH v4 2/5] efi_loader: versioning support in GetImageInfo Masahisa Kojima
@ 2023-03-28  6:52   ` Ilias Apalodimas
  0 siblings, 0 replies; 14+ messages in thread
From: Ilias Apalodimas @ 2023-03-28  6:52 UTC (permalink / raw)
  To: Masahisa Kojima; +Cc: u-boot, Heinrich Schuchardt, Takahiro Akashi

On Thu, 23 Mar 2023 at 13:09, Masahisa Kojima
<masahisa.kojima@linaro.org> wrote:
>
> Current FMP->GetImageInfo() always return 0 for the firmware
> version, user can not identify which firmware version is currently
> running through the EFI interface.
>
> This commit reads the "FmpStateXXXX" EFI variable, then fills the
> firmware version, lowest supported version, last attempt version
> and last attempt status in FMP->GetImageInfo().
>
> Now FMP->GetImageInfo() and ESRT have the meaningful version number.
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> No update since v1
>
>  lib/efi_loader/efi_firmware.c | 30 ++++++++++++++++++++++++++----
>  1 file changed, 26 insertions(+), 4 deletions(-)
>
> diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
> index fb5f7906d3..7536fcfa55 100644
> --- a/lib/efi_loader/efi_firmware.c
> +++ b/lib/efi_loader/efi_firmware.c
> @@ -190,13 +190,38 @@ static efi_status_t efi_fill_image_desc_array(
>         *package_version_name = NULL; /* not supported */
>
>         for (i = 0; i < num_image_type_guids; i++) {
> +               u16 varname[13]; /* u"FmpStateXXXX" */
> +               efi_status_t ret;
> +               efi_uintn_t size;
> +               struct fmp_state var_state = { 0 };
> +
>                 image_info[i].image_index = fw_array[i].image_index;
>                 image_info[i].image_type_id = fw_array[i].image_type_id;
>                 image_info[i].image_id = fw_array[i].image_index;
>
>                 image_info[i].image_id_name = fw_array[i].fw_name;
>
> -               image_info[i].version = 0; /* not supported */
> +               efi_create_indexed_name(varname, sizeof(varname), "FmpState",
> +                                       fw_array[i].image_index);
> +               size = sizeof(var_state);
> +               ret = efi_get_variable_int(varname, &fw_array[i].image_type_id,
> +                                          NULL, &size, &var_state, NULL);
> +               if (ret == EFI_SUCCESS) {
> +                       image_info[i].version = var_state.fw_version;
> +                       image_info[i].lowest_supported_image_version =
> +                               var_state.lowest_supported_version;
> +                       image_info[i].last_attempt_version =
> +                               var_state.last_attempt_version;
> +                       image_info[i].last_attempt_status =
> +                               var_state.last_attempt_status;
> +               } else {
> +                       image_info[i].version = 0;
> +                       image_info[i].lowest_supported_image_version = 0;
> +                       image_info[i].last_attempt_version = 0;
> +                       image_info[i].last_attempt_status =
> +                               LAST_ATTEMPT_STATUS_SUCCESS;
> +               }
> +
>                 image_info[i].version_name = NULL; /* not supported */
>                 image_info[i].size = 0;
>                 image_info[i].attributes_supported =
> @@ -210,9 +235,6 @@ static efi_status_t efi_fill_image_desc_array(
>                         image_info[0].attributes_setting |=
>                                 IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
>
> -               image_info[i].lowest_supported_image_version = 0;
> -               image_info[i].last_attempt_version = 0;
> -               image_info[i].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
>                 image_info[i].hardware_instance = 1;
>                 image_info[i].dependencies = NULL;
>         }
> --
> 2.17.1
>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

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

* Re: [PATCH v4 3/5] efi_loader: check lowest supported version in capsule update
  2023-03-23 11:09 ` [PATCH v4 3/5] efi_loader: check lowest supported version in capsule update Masahisa Kojima
@ 2023-03-28  6:54   ` Ilias Apalodimas
  0 siblings, 0 replies; 14+ messages in thread
From: Ilias Apalodimas @ 2023-03-28  6:54 UTC (permalink / raw)
  To: Masahisa Kojima; +Cc: u-boot, Heinrich Schuchardt, Takahiro Akashi

On Thu, 23 Mar 2023 at 13:09, Masahisa Kojima
<masahisa.kojima@linaro.org> wrote:
>
> The FMP Payload Header which EDK II capsule generation scripts
> insert contains lowest supported version.
> This commit reads the lowest supported version stored in the
> "FmpStateXXXX" EFI non-volatile variable, then check if the
> firmware version of ongoing capsule is equal or greater than
> the lowest supported version.
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Changes in v4:
> - use log_err() instead of printf()
>
> Changes in v2:
> - add error message when the firmware version is lower than
>   lowest supported version
>
>  lib/efi_loader/efi_firmware.c | 50 ++++++++++++++++++++++++++++++++++-
>  1 file changed, 49 insertions(+), 1 deletion(-)
>
> diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
> index 7536fcfa55..9c25b5bb38 100644
> --- a/lib/efi_loader/efi_firmware.c
> +++ b/lib/efi_loader/efi_firmware.c
> @@ -385,6 +385,39 @@ void efi_firmware_parse_payload_header(const void **p_image,
>         *p_image_size = image_size;
>  }
>
> +/**
> + * efi_firmware_get_lowest_supported_version - get the lowest supported version
> + * @image_index:       image_index
> + *
> + * Get the lowest supported version from FmpStateXXXX variable.
> + *
> + * Return:             lowest supported version, return 0 if reading FmpStateXXXX
> + *                     variable failed
> + */
> +static
> +u32 efi_firmware_get_lowest_supported_version(u8 image_index)
> +{
> +       u16 varname[13]; /* u"FmpStateXXXX" */
> +       efi_status_t ret;
> +       efi_uintn_t size;
> +       efi_guid_t *image_type_id;
> +       struct fmp_state var_state = { 0 };
> +
> +       image_type_id = efi_firmware_get_image_type_id(image_index);
> +       if (!image_type_id)
> +               return 0;
> +
> +       efi_create_indexed_name(varname, sizeof(varname), "FmpState",
> +                               image_index);
> +       size = sizeof(var_state);
> +       ret = efi_get_variable_int(varname, image_type_id, NULL, &size,
> +                                  &var_state, NULL);
> +       if (ret != EFI_SUCCESS)
> +               return 0;
> +
> +       return var_state.lowest_supported_version;
> +}
> +
>  /**
>   * efi_firmware_verify_image - verify image
>   * @p_image:           Pointer to new image
> @@ -392,7 +425,8 @@ void efi_firmware_parse_payload_header(const void **p_image,
>   * @image_index                Image index
>   * @state              Pointer to fmp state
>   *
> - * Verify the capsule file
> + * Verify the capsule authentication and check if the fw_version
> + * is equal or greater than the lowest supported version.
>   *
>   * Return:             status code
>   */
> @@ -403,10 +437,24 @@ efi_status_t efi_firmware_verify_image(const void **p_image,
>                                        struct fmp_state *state)
>  {
>         efi_status_t ret;
> +       u32 lowest_supported_version;
>
>         ret = efi_firmware_capsule_authenticate(p_image, p_image_size, state);
>         efi_firmware_parse_payload_header(p_image, p_image_size, state);
>
> +       /* check lowest_supported_version if capsule authentication passes */
> +       if (ret == EFI_SUCCESS) {
> +               lowest_supported_version =
> +                       efi_firmware_get_lowest_supported_version(image_index);
> +               if (lowest_supported_version > state->fw_version) {
> +                       log_err("Firmware version %u too low. Expecting >= %u. Aborting update\n",
> +                               state->fw_version, lowest_supported_version);
> +                       state->last_attempt_status =
> +                               LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
> +                       ret = EFI_INVALID_PARAMETER;
> +               }
> +       }
> +
>         return ret;
>  }
>
> --
> 2.17.1
>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

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

* Re: [PATCH v4 4/5] mkeficapsule: add FMP Payload Header
  2023-03-23 11:09 ` [PATCH v4 4/5] mkeficapsule: add FMP Payload Header Masahisa Kojima
@ 2023-03-28  6:55   ` Ilias Apalodimas
  0 siblings, 0 replies; 14+ messages in thread
From: Ilias Apalodimas @ 2023-03-28  6:55 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: u-boot, Heinrich Schuchardt, Takahiro Akashi, Sughosh Ganu,
	Etienne Carriere

On Thu, 23 Mar 2023 at 13:09, Masahisa Kojima
<masahisa.kojima@linaro.org> wrote:
>
> Current mkeficapsule tool does not provide firmware
> version management. EDK II reference implementation inserts
> the FMP Payload Header right before the payload.
> It coutains the fw_version and lowest supported version.
>
> This commit adds two new parameters required to generate
> the FMP Payload Header for mkeficapsule tool.
>  '-v' indicates the firmware version.
>  '-l' indicates the lowest supported version.
>
> When mkeficapsule tool is invoked with neither '-v' nor '-l' option,
> FMP Payload Header is not inserted, the behavior is same as
> current implementation.
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> No update since v3
>
> Changes in v3:
> - remove '-f' option
> - move some definitions into tools/eficapsule.h
> - add dependency check of fw_version and lowest_supported_version
> - remove unexpected modification of existing fprintf() call
> - add documentation
>
> Newly created in v2
>
>  doc/mkeficapsule.1   | 16 ++++++++++++++
>  tools/eficapsule.h   | 31 +++++++++++++++++++++++++++
>  tools/mkeficapsule.c | 51 +++++++++++++++++++++++++++++++++++++++-----
>  3 files changed, 93 insertions(+), 5 deletions(-)
>
> diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1
> index 1ca245a10f..7c62b03c73 100644
> --- a/doc/mkeficapsule.1
> +++ b/doc/mkeficapsule.1
> @@ -61,6 +61,22 @@ Specify an image index
>  .BI "-I\fR,\fB --instance " instance
>  Specify a hardware instance
>
> +.PP
> +FMP Payload Header is inserted right before the payload if
> +.BR --fw-version
> +or
> +.BR --lsv
> +are specified
> +
> +
> +.TP
> +.BI "-v\fR,\fB --fw-version " firmware-version
> +Specify a firmware version, 0 if omitted
> +
> +.TP
> +.BI "-l\fR,\fB --lsv " lowest-supported-version
> +Specify a lowest supported version, 0 if omitted
> +
>  .PP
>  For generation of firmware accept empty capsule
>  .BR --guid
> diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> index 072a4b5598..e5fa3398b6 100644
> --- a/tools/eficapsule.h
> +++ b/tools/eficapsule.h
> @@ -113,4 +113,35 @@ struct efi_firmware_image_authentication {
>         struct win_certificate_uefi_guid auth_info;
>  } __packed;
>
> +/* fmp payload header */
> +#define SIGNATURE_16(A, B)     ((A) | ((B) << 8))
> +#define SIGNATURE_32(A, B, C, D)       \
> +       (SIGNATURE_16(A, B) | (SIGNATURE_16(C, D) << 16))
> +
> +#define FMP_PAYLOAD_HDR_SIGNATURE      SIGNATURE_32('M', 'S', 'S', '1')
> +
> +/**
> + * struct fmp_payload_header - EDK2 header for the FMP payload
> + *
> + * This structure describes the header which is preprended to the
> + * FMP payload by the edk2 capsule generation scripts.
> + *
> + * @signature:                 Header signature used to identify the header
> + * @header_size:               Size of the structure
> + * @fw_version:                        Firmware versions used
> + * @lowest_supported_version:  Lowest supported version
> + */
> +struct fmp_payload_header {
> +       uint32_t signature;
> +       uint32_t header_size;
> +       uint32_t fw_version;
> +       uint32_t lowest_supported_version;
> +};
> +
> +struct fmp_payload_header_params {
> +       bool have_header;
> +       uint32_t fw_version;
> +       uint32_t lowest_supported_version;
> +};
> +
>  #endif /* _EFI_CAPSULE_H */
> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> index b71537beee..e50e6a8ed7 100644
> --- a/tools/mkeficapsule.c
> +++ b/tools/mkeficapsule.c
> @@ -29,7 +29,7 @@ static const char *tool_name = "mkeficapsule";
>  efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
>  efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
>
> -static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR";
> +static const char *opts_short = "g:i:I:v:l:p:c:m:o:dhAR";
>
>  enum {
>         CAPSULE_NORMAL_BLOB = 0,
> @@ -41,6 +41,8 @@ static struct option options[] = {
>         {"guid", required_argument, NULL, 'g'},
>         {"index", required_argument, NULL, 'i'},
>         {"instance", required_argument, NULL, 'I'},
> +       {"fw-version", required_argument, NULL, 'v'},
> +       {"lsv", required_argument, NULL, 'l'},
>         {"private-key", required_argument, NULL, 'p'},
>         {"certificate", required_argument, NULL, 'c'},
>         {"monotonic-count", required_argument, NULL, 'm'},
> @@ -60,6 +62,8 @@ static void print_usage(void)
>                 "\t-g, --guid <guid string>    guid for image blob type\n"
>                 "\t-i, --index <index>         update image index\n"
>                 "\t-I, --instance <instance>   update hardware instance\n"
> +               "\t-v, --fw-version <version>  firmware version\n"
> +               "\t-l, --lsv <version>         lowest supported version\n"
>                 "\t-p, --private-key <privkey file>  private key file\n"
>                 "\t-c, --certificate <cert file>     signer's certificate file\n"
>                 "\t-m, --monotonic-count <count>     monotonic count\n"
> @@ -402,6 +406,7 @@ static void free_sig_data(struct auth_context *ctx)
>   */
>  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>                         unsigned long index, unsigned long instance,
> +                       struct fmp_payload_header_params *fmp_ph_params,
>                         uint64_t mcount, char *privkey_file, char *cert_file,
>                         uint16_t oemflags)
>  {
> @@ -410,10 +415,11 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>         struct efi_firmware_management_capsule_image_header image;
>         struct auth_context auth_context;
>         FILE *f;
> -       uint8_t *data;
> +       uint8_t *data, *new_data, *buf;
>         off_t bin_size;
>         uint64_t offset;
>         int ret;
> +       struct fmp_payload_header payload_header;
>
>  #ifdef DEBUG
>         fprintf(stderr, "For output: %s\n", path);
> @@ -423,6 +429,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>         auth_context.sig_size = 0;
>         f = NULL;
>         data = NULL;
> +       new_data = NULL;
>         ret = -1;
>
>         /*
> @@ -431,12 +438,31 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>         if (read_bin_file(bin, &data, &bin_size))
>                 goto err;
>
> +       buf = data;
> +
> +       /* insert fmp payload header right before the payload */
> +       if (fmp_ph_params->have_header) {
> +               new_data = malloc(bin_size + sizeof(payload_header));
> +               if (!new_data)
> +                       goto err;
> +
> +               payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
> +               payload_header.header_size = sizeof(payload_header);
> +               payload_header.fw_version = fmp_ph_params->fw_version;
> +               payload_header.lowest_supported_version =
> +                       fmp_ph_params->lowest_supported_version;
> +               memcpy(new_data, &payload_header, sizeof(payload_header));
> +               memcpy(new_data + sizeof(payload_header), data, bin_size);
> +               buf = new_data;
> +               bin_size += sizeof(payload_header);
> +       }
> +
>         /* first, calculate signature to determine its size */
>         if (privkey_file && cert_file) {
>                 auth_context.key_file = privkey_file;
>                 auth_context.cert_file = cert_file;
>                 auth_context.auth.monotonic_count = mcount;
> -               auth_context.image_data = data;
> +               auth_context.image_data = buf;
>                 auth_context.image_size = bin_size;
>
>                 if (create_auth_data(&auth_context)) {
> @@ -536,7 +562,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>         /*
>          * firmware binary
>          */
> -       if (write_capsule_file(f, data, bin_size, "Firmware binary"))
> +       if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
>                 goto err;
>
>         ret = 0;
> @@ -545,6 +571,7 @@ err:
>                 fclose(f);
>         free_sig_data(&auth_context);
>         free(data);
> +       free(new_data);
>
>         return ret;
>  }
> @@ -644,6 +671,7 @@ int main(int argc, char **argv)
>         unsigned long oemflags;
>         char *privkey_file, *cert_file;
>         int c, idx;
> +       struct fmp_payload_header_params fmp_ph_params = { 0 };
>
>         guid = NULL;
>         index = 0;
> @@ -679,6 +707,14 @@ int main(int argc, char **argv)
>                 case 'I':
>                         instance = strtoul(optarg, NULL, 0);
>                         break;
> +               case 'v':
> +                       fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
> +                       fmp_ph_params.have_header = true;
> +                       break;
> +               case 'l':
> +                       fmp_ph_params.lowest_supported_version = strtoul(optarg, NULL, 0);
> +                       fmp_ph_params.have_header = true;
> +                       break;
>                 case 'p':
>                         if (privkey_file) {
>                                 fprintf(stderr,
> @@ -744,6 +780,11 @@ int main(int argc, char **argv)
>                 exit(EXIT_FAILURE);
>         }
>
> +       if (fmp_ph_params.fw_version < fmp_ph_params.lowest_supported_version) {
> +               fprintf(stderr, "fw_version is lower than lowest_supported_version\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
>         if (capsule_type != CAPSULE_NORMAL_BLOB) {
>                 if (create_empty_capsule(argv[argc - 1], guid,
>                                          capsule_type == CAPSULE_ACCEPT) < 0) {
> @@ -751,7 +792,7 @@ int main(int argc, char **argv)
>                         exit(EXIT_FAILURE);
>                 }
>         } else  if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
> -                                index, instance, mcount, privkey_file,
> +                                index, instance, &fmp_ph_params, mcount, privkey_file,
>                                  cert_file, (uint16_t)oemflags) < 0) {
>                 fprintf(stderr, "Creating firmware capsule failed\n");
>                 exit(EXIT_FAILURE);
> --
> 2.17.1
>

Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

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

* Re: [PATCH v4 5/5] test/py: efi_capsule: test for FMP versioning
  2023-03-23 11:09 ` [PATCH v4 5/5] test/py: efi_capsule: test for FMP versioning Masahisa Kojima
@ 2023-03-28  6:55   ` Ilias Apalodimas
  0 siblings, 0 replies; 14+ messages in thread
From: Ilias Apalodimas @ 2023-03-28  6:55 UTC (permalink / raw)
  To: Masahisa Kojima; +Cc: u-boot, Heinrich Schuchardt, Takahiro Akashi

On Thu, 23 Mar 2023 at 13:09, Masahisa Kojima
<masahisa.kojima@linaro.org> wrote:
>
> This test covers FMP versioning for both raw and FIT image,
> and both signed and non-signed capsule update.
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Newly created in v4
>
>  test/py/tests/test_efi_capsule/conftest.py    |  58 +++++
>  .../test_capsule_firmware_fit.py              | 187 ++++++++++++++++
>  .../test_capsule_firmware_raw.py              | 199 ++++++++++++++++++
>  .../test_capsule_firmware_signed_fit.py       | 159 ++++++++++++++
>  .../test_capsule_firmware_signed_raw.py       | 169 +++++++++++++++
>  5 files changed, 772 insertions(+)
>
> diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py
> index 4879f2b5c2..b7332de844 100644
> --- a/test/py/tests/test_efi_capsule/conftest.py
> +++ b/test/py/tests/test_efi_capsule/conftest.py
> @@ -95,6 +95,26 @@ def efi_capsule_data(request, u_boot_config):
>          check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid  058B7D83-50D5-4C47-A195-60D86AD341C4 uboot_bin_env.itb Test05' %
>                     (data_dir, u_boot_config.build_dir),
>                     shell=True)
> +        check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 --lsv 3 '
> +                        '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test101' %
> +                   (data_dir, u_boot_config.build_dir),
> +                   shell=True)
> +        check_call('cd %s; %s/tools/mkeficapsule --index 2 --fw-version 10 --lsv 7 '
> +                        '--guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test102' %
> +                   (data_dir, u_boot_config.build_dir),
> +                   shell=True)
> +        check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 --lsv 1 '
> +                        '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test103' %
> +                   (data_dir, u_boot_config.build_dir),
> +                   shell=True)
> +        check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 --lsv 3 '
> +                        '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test104' %
> +                   (data_dir, u_boot_config.build_dir),
> +                   shell=True)
> +        check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 --lsv 1 '
> +                        '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test105' %
> +                   (data_dir, u_boot_config.build_dir),
> +                   shell=True)
>
>          if capsule_auth_enabled:
>              # raw firmware signed with proper key
> @@ -131,6 +151,44 @@ def efi_capsule_data(request, u_boot_config):
>                              'uboot_bin_env.itb Test14'
>                         % (data_dir, u_boot_config.build_dir),
>                         shell=True)
> +            # raw firmware signed with proper key with version information
> +            check_call('cd %s; '
> +                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
> +                            '--fw-version 5 --lsv 3 '
> +                            '--private-key SIGNER.key --certificate SIGNER.crt '
> +                            '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
> +                            'u-boot.bin.new Test111'
> +                       % (data_dir, u_boot_config.build_dir),
> +                       shell=True)
> +            # raw firmware signed with *mal* key with version information
> +            check_call('cd %s; '
> +                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
> +                            '--fw-version 8 --lsv 3 '
> +                            '--private-key SIGNER2.key '
> +                            '--certificate SIGNER2.crt '
> +                            '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
> +                            'u-boot.bin.new Test112'
> +                       % (data_dir, u_boot_config.build_dir),
> +                       shell=True)
> +            # FIT firmware signed with proper key with version information
> +            check_call('cd %s; '
> +                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
> +                            '--fw-version 5 --lsv 3 '
> +                            '--private-key SIGNER.key --certificate SIGNER.crt '
> +                            '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
> +                            'uboot_bin_env.itb Test113'
> +                       % (data_dir, u_boot_config.build_dir),
> +                       shell=True)
> +            # FIT firmware signed with *mal* key with version information
> +            check_call('cd %s; '
> +                       '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
> +                            '--fw-version 8 --lsv 3 '
> +                            '--private-key SIGNER2.key '
> +                            '--certificate SIGNER2.crt '
> +                            '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
> +                            'uboot_bin_env.itb Test114'
> +                       % (data_dir, u_boot_config.build_dir),
> +                       shell=True)
>
>          # Create a disk image with EFI system partition
>          check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
> diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py
> index d28b53a1a1..881fe233ec 100644
> --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py
> +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py
> @@ -189,3 +189,190 @@ class TestEfiCapsuleFirmwareFit(object):
>                  assert 'u-boot-env:Old' in ''.join(output)
>              else:
>                  assert 'u-boot-env:New' in ''.join(output)
> +
> +    def test_efi_capsule_fw3(
> +            self, u_boot_config, u_boot_console, efi_capsule_data):
> +        """
> +        Test Case 3 - Update U-Boot and U-Boot environment on SPI Flash with version information
> +                      0x100000-0x150000: U-Boot binary (but dummy)
> +                      0x150000-0x200000: U-Boot environment (but dummy)
> +        """
> +        disk_img = efi_capsule_data
> +        with u_boot_console.log.section('Test Case 3-a, before reboot'):
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'printenv -e PlatformLangCodes', # workaround for terminal size determination
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
> +                'efidebug boot order 1',
> +                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> +                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'env save'])
> +
> +            # initialize contents
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
> +                'sf write 4000000 100000 10',
> +                'sf read 5000000 100000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR,
> +                'sf write 4000000 150000 10',
> +                'sf read 5000000 150000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +
> +            # place a capsule file
> +            output = u_boot_console.run_command_list([
> +                'fatload host 0:1 4000000 %s/Test104' % CAPSULE_DATA_DIR,
> +                'fatwrite host 0:1 4000000 %s/Test104 $filesize' % CAPSULE_INSTALL_DIR,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test104' in ''.join(output)
> +
> +        capsule_early = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_on_disk_early')
> +        capsule_auth = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_authenticate')
> +
> +        # reboot
> +        u_boot_console.restart_uboot(expect_reset = capsule_early)
> +
> +        with u_boot_console.log.section('Test Case 3-b, after reboot'):
> +            if not capsule_early:
> +                # make sure that dfu_alt_info exists even persistent variables
> +                # are not available.
> +                output = u_boot_console.run_command_list([
> +                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                    'host bind 0 %s' % disk_img,
> +                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +                assert 'Test104' in ''.join(output)
> +
> +                # need to run uefi command to initiate capsule handling
> +                output = u_boot_console.run_command(
> +                    'env print -e Capsule0000', wait_for_reboot = True)
> +
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test104' not in ''.join(output)
> +
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'sf read 4000000 100000 10',
> +                'md.b 4000000 10'])
> +            if capsule_auth:
> +                assert 'u-boot:Old' in ''.join(output)
> +            else:
> +                assert 'u-boot:New' in ''.join(output)
> +
> +            output = u_boot_console.run_command_list([
> +                'sf read 4000000 150000 10',
> +                'md.b 4000000 10'])
> +            if capsule_auth:
> +                assert 'u-boot-env:Old' in ''.join(output)
> +            else:
> +                assert 'u-boot-env:New' in ''.join(output)
> +
> +            # make sure the dfu_alt_info exists because it is required for making ESRT.
> +            output = u_boot_console.run_command_list([
> +                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'efidebug capsule esrt'])
> +
> +            if capsule_auth:
> +                assert 'ESRT: fw_version=5' not in ''.join(output)
> +                assert 'ESRT: lowest_supported_fw_version=3' not in ''.join(output)
> +            else:
> +                assert 'ESRT: fw_version=5' in ''.join(output)
> +                assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
> +
> +    def test_efi_capsule_fw4(
> +            self, u_boot_config, u_boot_console, efi_capsule_data):
> +        """
> +        Test Case 4 - Update U-Boot and U-Boot environment on SPI Flash with version information
> +                      but fw_version is lower than lowest_supported_version
> +                      No update should happen
> +                      0x100000-0x150000: U-Boot binary (but dummy)
> +                      0x150000-0x200000: U-Boot environment (but dummy)
> +        """
> +        disk_img = efi_capsule_data
> +        with u_boot_console.log.section('Test Case 4-a, before reboot'):
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'printenv -e PlatformLangCodes', # workaround for terminal size determination
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
> +                'efidebug boot order 1',
> +                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> +                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'env save'])
> +
> +            # initialize contents
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
> +                'sf write 4000000 100000 10',
> +                'sf read 5000000 100000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR,
> +                'sf write 4000000 150000 10',
> +                'sf read 5000000 150000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +
> +            # place a capsule file
> +            output = u_boot_console.run_command_list([
> +                'fatload host 0:1 4000000 %s/Test105' % CAPSULE_DATA_DIR,
> +                'fatwrite host 0:1 4000000 %s/Test105 $filesize' % CAPSULE_INSTALL_DIR,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test105' in ''.join(output)
> +
> +        capsule_early = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_on_disk_early')
> +        capsule_auth = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_authenticate')
> +
> +        # reboot
> +        u_boot_console.restart_uboot(expect_reset = capsule_early)
> +
> +        with u_boot_console.log.section('Test Case 4-b, after reboot'):
> +            if not capsule_early:
> +                # make sure that dfu_alt_info exists even persistent variables
> +                # are not available.
> +                output = u_boot_console.run_command_list([
> +                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                    'host bind 0 %s' % disk_img,
> +                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +                assert 'Test105' in ''.join(output)
> +
> +                # need to run uefi command to initiate capsule handling
> +                output = u_boot_console.run_command(
> +                    'env print -e Capsule0000', wait_for_reboot = True)
> +
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test105' not in ''.join(output)
> +
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'sf read 4000000 100000 10',
> +                'md.b 4000000 10'])
> +            assert 'u-boot:Old' in ''.join(output)
> +
> +            output = u_boot_console.run_command_list([
> +                'sf read 4000000 150000 10',
> +                'md.b 4000000 10'])
> +            assert 'u-boot-env:Old' in ''.join(output)
> +
> +            # make sure the dfu_alt_info exists because it is required for making ESRT.
> +            output = u_boot_console.run_command_list([
> +                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'efidebug capsule esrt'])
> +
> +            if not capsule_auth:
> +                assert 'ESRT: last_attempt_version=2' in ''.join(output)
> +                assert 'ESRT: last_attempt_status=incorrect version' in ''.join(output)
> diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py
> index 92bfb14932..bb60065343 100644
> --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py
> +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py
> @@ -292,3 +292,202 @@ class TestEfiCapsuleFirmwareRaw:
>                  assert 'u-boot-env:Old' in ''.join(output)
>              else:
>                  assert 'u-boot-env:New' in ''.join(output)
> +
> +    def test_efi_capsule_fw4(
> +            self, u_boot_config, u_boot_console, efi_capsule_data):
> +        """ Test Case 4
> +        Update U-Boot on SPI Flash, raw image format with fw_version and lowest_supported_version
> +        0x100000-0x150000: U-Boot binary (but dummy)
> +        0x150000-0x200000: U-Boot environment (but dummy)
> +        """
> +        disk_img = efi_capsule_data
> +        with u_boot_console.log.section('Test Case 4-a, before reboot'):
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'printenv -e PlatformLangCodes', # workaround for terminal size determination
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
> +                'efidebug boot order 1',
> +                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> +                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'env save'])
> +
> +            # initialize contents
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
> +                'sf write 4000000 100000 10',
> +                'sf read 5000000 100000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR,
> +                'sf write 4000000 150000 10',
> +                'sf read 5000000 150000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +
> +            # place the capsule files
> +            output = u_boot_console.run_command_list([
> +                'fatload host 0:1 4000000 %s/Test101' % CAPSULE_DATA_DIR,
> +                'fatwrite host 0:1 4000000 %s/Test101 $filesize' % CAPSULE_INSTALL_DIR,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test101' in ''.join(output)
> +
> +            output = u_boot_console.run_command_list([
> +                'fatload host 0:1 4000000 %s/Test102' % CAPSULE_DATA_DIR,
> +                'fatwrite host 0:1 4000000 %s/Test102 $filesize' % CAPSULE_INSTALL_DIR,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test102' in ''.join(output)
> +
> +        capsule_early = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_on_disk_early')
> +        capsule_auth = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_authenticate')
> +
> +        # reboot
> +        u_boot_console.restart_uboot(expect_reset = capsule_early)
> +
> +        with u_boot_console.log.section('Test Case 4-b, after reboot'):
> +            if not capsule_early:
> +                # make sure that dfu_alt_info exists even persistent variables
> +                # are not available.
> +                output = u_boot_console.run_command_list([
> +                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                    'host bind 0 %s' % disk_img,
> +                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +                assert 'Test101' in ''.join(output)
> +                assert 'Test102' in ''.join(output)
> +
> +                # need to run uefi command to initiate capsule handling
> +                output = u_boot_console.run_command(
> +                    'env print -e Capsule0000', wait_for_reboot = True)
> +
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test101' not in ''.join(output)
> +            assert 'Test102' not in ''.join(output)
> +
> +            # make sure the dfu_alt_info exists because it is required for making ESRT.
> +            output = u_boot_console.run_command_list([
> +                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'efidebug capsule esrt'])
> +
> +            if capsule_auth:
> +                # ensure that SANDBOX_UBOOT_IMAGE_GUID is in the ESRT.
> +                assert '09D7CF52-0720-4710-91D1-08469B7FE9C8' in ''.join(output)
> +                assert 'ESRT: fw_version=5' not in ''.join(output)
> +                assert 'ESRT: lowest_supported_fw_version=3' not in ''.join(output)
> +
> +                # ensure that SANDBOX_UBOOT_ENV_IMAGE_GUID is in the ESRT.
> +                assert '5A7021F5-FEF2-48B4-AABA-832E777418C0' in ''.join(output)
> +                assert 'ESRT: fw_version=10' not in ''.join(output)
> +                assert 'ESRT: lowest_supported_fw_version=7' not in ''.join(output)
> +            else:
> +                # ensure that SANDBOX_UBOOT_IMAGE_GUID is in the ESRT.
> +                assert '09D7CF52-0720-4710-91D1-08469B7FE9C8' in ''.join(output)
> +                assert 'ESRT: fw_version=5' in ''.join(output)
> +                assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
> +
> +                # ensure that SANDBOX_UBOOT_ENV_IMAGE_GUID is in the ESRT.
> +                assert '5A7021F5-FEF2-48B4-AABA-832E777418C0' in ''.join(output)
> +                assert 'ESRT: fw_version=10' in ''.join(output)
> +                assert 'ESRT: lowest_supported_fw_version=7' in ''.join(output)
> +
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'sf read 4000000 100000 10',
> +                'md.b 4000000 10'])
> +            if capsule_auth:
> +                assert 'u-boot:Old' in ''.join(output)
> +            else:
> +                assert 'u-boot:New' in ''.join(output)
> +
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'sf read 4000000 150000 10',
> +                'md.b 4000000 10'])
> +            if capsule_auth:
> +                assert 'u-boot-env:Old' in ''.join(output)
> +            else:
> +                assert 'u-boot-env:New' in ''.join(output)
> +
> +    def test_efi_capsule_fw5(
> +            self, u_boot_config, u_boot_console, efi_capsule_data):
> +        """ Test Case 5
> +        Update U-Boot on SPI Flash, raw image format with fw_version and lowest_supported_version
> +        but fw_version is lower than lowest_supported_version
> +        No update should happen
> +        0x100000-0x150000: U-Boot binary (but dummy)
> +        """
> +        disk_img = efi_capsule_data
> +        with u_boot_console.log.section('Test Case 5-a, before reboot'):
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'printenv -e PlatformLangCodes', # workaround for terminal size determination
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
> +                'efidebug boot order 1',
> +                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> +                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'env save'])
> +
> +            # initialize contents
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
> +                'sf write 4000000 100000 10',
> +                'sf read 5000000 100000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +
> +            # place the capsule files
> +            output = u_boot_console.run_command_list([
> +                'fatload host 0:1 4000000 %s/Test103' % CAPSULE_DATA_DIR,
> +                'fatwrite host 0:1 4000000 %s/Test103 $filesize' % CAPSULE_INSTALL_DIR,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test103' in ''.join(output)
> +
> +        capsule_early = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_on_disk_early')
> +        capsule_auth = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_authenticate')
> +
> +        # reboot
> +        u_boot_console.restart_uboot(expect_reset = capsule_early)
> +
> +        with u_boot_console.log.section('Test Case 5-b, after reboot'):
> +            if not capsule_early:
> +                # make sure that dfu_alt_info exists even persistent variables
> +                # are not available.
> +                output = u_boot_console.run_command_list([
> +                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                    'host bind 0 %s' % disk_img,
> +                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +                assert 'Test103' in ''.join(output)
> +
> +                # need to run uefi command to initiate capsule handling
> +                output = u_boot_console.run_command(
> +                    'env print -e Capsule0000', wait_for_reboot = True)
> +
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test103' not in ''.join(output)
> +
> +            # make sure the dfu_alt_info exists because it is required for making ESRT.
> +            output = u_boot_console.run_command_list([
> +                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'efidebug capsule esrt'])
> +
> +            if not capsule_auth:
> +                assert 'ESRT: last_attempt_version=2' in ''.join(output)
> +                assert 'ESRT: last_attempt_status=incorrect version' in ''.join(output)
> +
> +            # make sure capsule update failed
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'sf read 4000000 100000 10',
> +                'md.b 4000000 10'])
> +            assert 'u-boot:Old' in ''.join(output)
> diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
> index 8c2d616fd0..cf2a2c7ba6 100644
> --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
> +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
> @@ -257,3 +257,162 @@ class TestEfiCapsuleFirmwareSignedFit(object):
>                  'sf read 4000000 100000 10',
>                  'md.b 4000000 10'])
>              assert 'u-boot:Old' in ''.join(output)
> +
> +    def test_efi_capsule_auth4(
> +            self, u_boot_config, u_boot_console, efi_capsule_data):
> +        """
> +        Test Case 4 - Update U-Boot on SPI Flash, FIT image format
> +                      0x100000-0x150000: U-Boot binary (but dummy)
> +
> +                      If the capsule is properly signed with version information,
> +                      the authentication should pass and the firmware be updated.
> +        """
> +        disk_img = efi_capsule_data
> +        with u_boot_console.log.section('Test Case 4-a, before reboot'):
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'printenv -e PlatformLangCodes', # workaround for terminal size determination
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
> +                'efidebug boot order 1',
> +                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> +                'env set dfu_alt_info '
> +                        '"sf 0:0=u-boot-bin raw 0x100000 '
> +                        '0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'env save'])
> +
> +            # initialize content
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.bin.old'
> +                        % CAPSULE_DATA_DIR,
> +                'sf write 4000000 100000 10',
> +                'sf read 5000000 100000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +
> +            # place a capsule file
> +            output = u_boot_console.run_command_list([
> +                'fatload host 0:1 4000000 %s/Test113' % CAPSULE_DATA_DIR,
> +                'fatwrite host 0:1 4000000 %s/Test113 $filesize'
> +                        % CAPSULE_INSTALL_DIR,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test113' in ''.join(output)
> +
> +        # reboot
> +        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
> +        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
> +                                    + '/test_sig.dtb'
> +        u_boot_console.restart_uboot()
> +
> +        capsule_early = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_on_disk_early')
> +        with u_boot_console.log.section('Test Case 4-b, after reboot'):
> +            if not capsule_early:
> +                # make sure that dfu_alt_info exists even persistent variables
> +                # are not available.
> +                output = u_boot_console.run_command_list([
> +                    'env set dfu_alt_info '
> +                            '"sf 0:0=u-boot-bin raw 0x100000 '
> +                            '0x50000;u-boot-env raw 0x150000 0x200000"',
> +                    'host bind 0 %s' % disk_img,
> +                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +                assert 'Test113' in ''.join(output)
> +
> +                # need to run uefi command to initiate capsule handling
> +                output = u_boot_console.run_command(
> +                    'env print -e Capsule0000', wait_for_reboot = True)
> +
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test113' not in ''.join(output)
> +
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'sf read 4000000 100000 10',
> +                'md.b 4000000 10'])
> +            assert 'u-boot:New' in ''.join(output)
> +
> +            output = u_boot_console.run_command('efidebug capsule esrt')
> +            assert 'ESRT: fw_version=5' in ''.join(output)
> +            assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
> +
> +    def test_efi_capsule_auth5(
> +            self, u_boot_config, u_boot_console, efi_capsule_data):
> +        """
> +        Test Case 5 - Update U-Boot on SPI Flash, FIT image format
> +                      0x100000-0x150000: U-Boot binary (but dummy)
> +
> +                      If the capsule is properly signed with version information,
> +                      the authentication should pass and the firmware be updated.
> +        """
> +        disk_img = efi_capsule_data
> +        with u_boot_console.log.section('Test Case 5-a, before reboot'):
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'printenv -e PlatformLangCodes', # workaround for terminal size determination
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
> +                'efidebug boot order 1',
> +                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> +                'env set dfu_alt_info '
> +                        '"sf 0:0=u-boot-bin raw 0x100000 '
> +                        '0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'env save'])
> +
> +            # initialize content
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.bin.old'
> +                        % CAPSULE_DATA_DIR,
> +                'sf write 4000000 100000 10',
> +                'sf read 5000000 100000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +
> +            # place a capsule file
> +            output = u_boot_console.run_command_list([
> +                'fatload host 0:1 4000000 %s/Test114' % CAPSULE_DATA_DIR,
> +                'fatwrite host 0:1 4000000 %s/Test114 $filesize'
> +                        % CAPSULE_INSTALL_DIR,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test114' in ''.join(output)
> +
> +        # reboot
> +        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
> +        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
> +                                    + '/test_sig.dtb'
> +        u_boot_console.restart_uboot()
> +
> +        capsule_early = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_on_disk_early')
> +        with u_boot_console.log.section('Test Case 5-b, after reboot'):
> +            if not capsule_early:
> +                # make sure that dfu_alt_info exists even persistent variables
> +                # are not available.
> +                output = u_boot_console.run_command_list([
> +                    'env set dfu_alt_info '
> +                            '"sf 0:0=u-boot-bin raw 0x100000 '
> +                            '0x50000;u-boot-env raw 0x150000 0x200000"',
> +                    'host bind 0 %s' % disk_img,
> +                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +                assert 'Test114' in ''.join(output)
> +
> +                # need to run uefi command to initiate capsule handling
> +                output = u_boot_console.run_command(
> +                    'env print -e Capsule0000', wait_for_reboot = True)
> +
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test114' not in ''.join(output)
> +
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'sf read 4000000 100000 10',
> +                'md.b 4000000 10'])
> +            assert 'u-boot:Old' in ''.join(output)
> +
> +            output = u_boot_console.run_command('efidebug capsule esrt')
> +            assert 'ESRT: fw_version=5' in ''.join(output)
> +            assert 'ESRT: last_attempt_version=8' in ''.join(output)
> +            assert 'ESRT: last_attempt_status=auth error' in ''.join(output)
> diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
> index 2bbaa9cc55..631e78b1f1 100644
> --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
> +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
> @@ -254,3 +254,172 @@ class TestEfiCapsuleFirmwareSignedRaw(object):
>                  'sf read 4000000 100000 10',
>                  'md.b 4000000 10'])
>              assert 'u-boot:Old' in ''.join(output)
> +
> +    def test_efi_capsule_auth4(
> +            self, u_boot_config, u_boot_console, efi_capsule_data):
> +        """
> +        Test Case 4 - Update U-Boot on SPI Flash, raw image format with version information
> +                      0x100000-0x150000: U-Boot binary (but dummy)
> +
> +                      If the capsule is properly signed, the authentication
> +                      should pass and the firmware be updated.
> +        """
> +        disk_img = efi_capsule_data
> +        with u_boot_console.log.section('Test Case 4-a, before reboot'):
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'printenv -e PlatformLangCodes', # workaround for terminal size determination
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
> +                'efidebug boot order 1',
> +                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> +                'env set dfu_alt_info '
> +                        '"sf 0:0=u-boot-bin raw 0x100000 '
> +                        '0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'env save'])
> +
> +
> +            output = u_boot_console.run_command('efidebug capsule esrt')
> +
> +
> +
> +            # initialize content
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.bin.old'
> +                        % CAPSULE_DATA_DIR,
> +                'sf write 4000000 100000 10',
> +                'sf read 5000000 100000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +
> +            # place a capsule file
> +            output = u_boot_console.run_command_list([
> +                'fatload host 0:1 4000000 %s/Test111' % CAPSULE_DATA_DIR,
> +                'fatwrite host 0:1 4000000 %s/Test111 $filesize'
> +                            % CAPSULE_INSTALL_DIR,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test111' in ''.join(output)
> +
> +        # reboot
> +        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
> +        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
> +                                    + '/test_sig.dtb'
> +        u_boot_console.restart_uboot()
> +
> +        capsule_early = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_on_disk_early')
> +        with u_boot_console.log.section('Test Case 4-b, after reboot'):
> +            if not capsule_early:
> +                # make sure that dfu_alt_info exists even persistent variables
> +                # are not available.
> +                output = u_boot_console.run_command_list([
> +                    'env set dfu_alt_info '
> +                            '"sf 0:0=u-boot-bin raw 0x100000 '
> +                            '0x50000;u-boot-env raw 0x150000 0x200000"',
> +                    'host bind 0 %s' % disk_img,
> +                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +                assert 'Test111' in ''.join(output)
> +
> +                # need to run uefi command to initiate capsule handling
> +                output = u_boot_console.run_command(
> +                    'env print -e Capsule0000', wait_for_reboot = True)
> +
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test111' not in ''.join(output)
> +
> +            # TODO: check CapsuleStatus in CapsuleXXXX
> +
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'sf read 4000000 100000 10',
> +                'md.b 4000000 10'])
> +            assert 'u-boot:New' in ''.join(output)
> +
> +            output = u_boot_console.run_command('efidebug capsule esrt')
> +            assert 'ESRT: fw_version=5' in ''.join(output)
> +            assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
> +
> +    def test_efi_capsule_auth5(
> +            self, u_boot_config, u_boot_console, efi_capsule_data):
> +        """
> +        Test Case 5 - Update U-Boot on SPI Flash, raw image format with version information
> +                      0x100000-0x150000: U-Boot binary (but dummy)
> +
> +                      If the capsule is signed but with an invalid key,
> +                      the authentication should fail and the firmware
> +                      not be updated.
> +        """
> +        disk_img = efi_capsule_data
> +        with u_boot_console.log.section('Test Case 5-a, before reboot'):
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'printenv -e PlatformLangCodes', # workaround for terminal size determination
> +                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
> +                'efidebug boot order 1',
> +                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
> +                'env set dfu_alt_info '
> +                        '"sf 0:0=u-boot-bin raw 0x100000 '
> +                        '0x50000;u-boot-env raw 0x150000 0x200000"',
> +                'env save'])
> +
> +            # initialize content
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'fatload host 0:1 4000000 %s/u-boot.bin.old'
> +                        % CAPSULE_DATA_DIR,
> +                'sf write 4000000 100000 10',
> +                'sf read 5000000 100000 10',
> +                'md.b 5000000 10'])
> +            assert 'Old' in ''.join(output)
> +
> +            # place a capsule file
> +            output = u_boot_console.run_command_list([
> +                'fatload host 0:1 4000000 %s/Test112' % CAPSULE_DATA_DIR,
> +                'fatwrite host 0:1 4000000 %s/Test112 $filesize'
> +                            % CAPSULE_INSTALL_DIR,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test112' in ''.join(output)
> +
> +        # reboot
> +        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
> +        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
> +                                    + '/test_sig.dtb'
> +        u_boot_console.restart_uboot()
> +
> +        capsule_early = u_boot_config.buildconfig.get(
> +            'config_efi_capsule_on_disk_early')
> +        with u_boot_console.log.section('Test Case 5-b, after reboot'):
> +            if not capsule_early:
> +                # make sure that dfu_alt_info exists even persistent variables
> +                # are not available.
> +                output = u_boot_console.run_command_list([
> +                    'env set dfu_alt_info '
> +                            '"sf 0:0=u-boot-bin raw 0x100000 '
> +                            '0x50000;u-boot-env raw 0x150000 0x200000"',
> +                    'host bind 0 %s' % disk_img,
> +                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +                assert 'Test112' in ''.join(output)
> +
> +                # need to run uefi command to initiate capsule handling
> +                output = u_boot_console.run_command(
> +                    'env print -e Capsule0000', wait_for_reboot = True)
> +
> +            output = u_boot_console.run_command_list([
> +                'host bind 0 %s' % disk_img,
> +                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
> +            assert 'Test112' not in ''.join(output)
> +
> +            # TODO: check CapsuleStatus in CapsuleXXXX
> +
> +            output = u_boot_console.run_command_list([
> +                'sf probe 0:0',
> +                'sf read 4000000 100000 10',
> +                'md.b 4000000 10'])
> +            assert 'u-boot:Old' in ''.join(output)
> +
> +            output = u_boot_console.run_command('efidebug capsule esrt')
> +            assert 'ESRT: fw_version=5' in ''.join(output)
> +            assert 'ESRT: last_attempt_version=8' in ''.join(output)
> +            assert 'ESRT: last_attempt_status=auth error' in ''.join(output)
> --
> 2.17.1
>

Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

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

* Re: [PATCH v4 1/5] efi_loader: store firmware version into FmpState variable
  2023-03-23 11:09 ` [PATCH v4 1/5] efi_loader: store firmware version into FmpState variable Masahisa Kojima
@ 2023-03-28 11:53   ` Ilias Apalodimas
  2023-03-28 13:02     ` Ilias Apalodimas
  0 siblings, 1 reply; 14+ messages in thread
From: Ilias Apalodimas @ 2023-03-28 11:53 UTC (permalink / raw)
  To: Masahisa Kojima; +Cc: u-boot, Heinrich Schuchardt, Takahiro Akashi

Kojima-san,

On Thu, Mar 23, 2023 at 08:09:01PM +0900, Masahisa Kojima wrote:
> Firmware version management is not implemented in the current
> FMP protocol.
> EDK II reference implementation capsule generation script inserts
> the FMP Payload Header right before the payload, it contains the
> firmware version and lowest supported version.
> 
> This commit utilizes the FMP Payload Header, reads the header and
> stores the firmware version, lowest supported version,
> last attempt version and last attempt status into "FmpStateXXXX"
> EFI non-volatile variable. XXXX indicates the image index,
> since FMP protocol handles multiple image indexes.
> 
> This change is compatible with the existing FMP implementation.
> This change does not mandate the FMP Payload Header.
> If no FMP Payload Header is found in the capsule file, fw_version,
> lowest supported version, last attempt version and last attempt
> status is 0 and this is the same behavior as existing FMP
> implementation.
> 
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Changes in v4:
> - move lines that are the same in both branches out of the if statement
> - s/EDK2/EDK II/
> - create print result function
> - set last_attempt_version when capsule authentication failed
> - use log_err() instead of printf()
> 
> Changes in v3:
> - exclude CONFIG_FWU_MULTI_BANK_UPDATE case
> - set image_type_id as a vendor field of FmpStateXXXX variable
> - set READ_ONLY flag for FmpStateXXXX variable
> - add error code for FIT image case
> 
> Changes in v2:
> - modify indent
> 
>  lib/efi_loader/efi_firmware.c | 250 ++++++++++++++++++++++++++++++----
>  1 file changed, 222 insertions(+), 28 deletions(-)
> 
> diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
> index 93e2b01c07..fb5f7906d3 100644
> --- a/lib/efi_loader/efi_firmware.c
> +++ b/lib/efi_loader/efi_firmware.c
> @@ -10,6 +10,7 @@
>  #include <charset.h>
>  #include <dfu.h>
>  #include <efi_loader.h>
> +#include <efi_variable.h>

[...]

> +void efi_firmware_parse_payload_header(const void **p_image,
> +				       efi_uintn_t *p_image_size,
> +				       struct fmp_state *state)
> +{
> +	const void *image = *p_image;
> +	efi_uintn_t image_size = *p_image_size;
> +	const struct fmp_payload_header *header;
> +	u32 fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
> +
> +	header = image;
>  	if (!memcmp(&header->signature, &fmp_hdr_signature,
>  		    sizeof(fmp_hdr_signature))) {

Why is this a memcmp? if (header->signature == FMP_PAYLOAD_HDR_SIGNATURE) ?

> -		/*
> -		 * When building the capsule with the scripts in
> -		 * edk2, a FMP header is inserted above the capsule
> -		 * payload. Compensate for this header to get the
> -		 * actual payload that is to be updated.
> -		 */
> +		/* FMP header is inserted above the capsule payload */
> +		state->fw_version = header->fw_version;
> +		state->lowest_supported_version = header->lowest_supported_version;
> +		state->last_attempt_version = header->fw_version;
>  		image += header->header_size;
>  		image_size -= header->header_size;
>  	}
>  
>  	*p_image = image;
>  	*p_image_size = image_size;
> -	return EFI_SUCCESS;
> +}
> +
> +/**
> + * efi_firmware_verify_image - verify image
> + * @p_image:		Pointer to new image
> + * @p_image_size:	Pointer to size of new image
> + * @image_index		Image index
> + * @state		Pointer to fmp state
> + *
> + * Verify the capsule file
> + *
> + * Return:		status code
> + */
> +static
> +efi_status_t efi_firmware_verify_image(const void **p_image,
> +				       efi_uintn_t *p_image_size,
> +				       u8 image_index,
> +				       struct fmp_state *state)
> +{
> +	efi_status_t ret;
> +
> +	ret = efi_firmware_capsule_authenticate(p_image, p_image_size, state);
> +	efi_firmware_parse_payload_header(p_image, p_image_size, state);

I guess this needs to run regardless of the result to set fw_version etc?
/efi_firmware_parse_payload_header() is a bit misleading though.  It
doesn't parse the fmp payload header.  Instead it sets some values that are
contained in the header and adjusts the image size accordingly.  Can we
come up with a better name?

> +
> +	return ret;
> +}
> +
> +/**
> + * efi_firmware_print_result - print the firmware update result
> + * @status:	status code
> + * @state	Pointer to fmp state
> + *
> + * Print the firmware update result
> + */
> +void efi_firmware_print_result(efi_status_t status, struct fmp_state *state)
> +{
> +	if (status == EFI_SUCCESS) {
> +		if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE))
> +			log_info("Firmware successfully written\n");
> +		else
> +			log_info("Firmware updated to version %u\n",
> +				 state->fw_version);
> +	}
>  }

This is an overkill tbh. Even if A/B updates are supported the firmware is
updated to a newer version.  Whether or not the new version will be
*accepted* on a subsequent reboot is irrelevant.  Moreover if we define a
function it should be static,  but I would prefer just getting rid of it.

>  
>  /**
> @@ -330,7 +494,9 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
>  	efi_status_t (*progress)(efi_uintn_t completion),
>  	u16 **abort_reason)
>  {
> +	bool updated;
>  	efi_status_t status;
> +	struct fmp_state state = { 0 };
>  
>  	EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
>  		  image_size, vendor_code, progress, abort_reason);
> @@ -338,14 +504,25 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
>  	if (!image || image_index != 1)
>  		return EFI_EXIT(EFI_INVALID_PARAMETER);
>  
> -	status = efi_firmware_capsule_authenticate(&image, &image_size);
> +	status = efi_firmware_verify_image(&image, &image_size, image_index,
> +					   &state);
>  	if (status != EFI_SUCCESS)
> -		return EFI_EXIT(status);
> +		goto err;
> +
> +	if (fit_update(image)) {
> +		status = EFI_DEVICE_ERROR;
> +		state.last_attempt_status = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
> +		goto err;
> +	}
>  
> -	if (fit_update(image))
> -		return EFI_EXIT(EFI_DEVICE_ERROR);
> +	state.last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
> +err:
> +	updated = (status == EFI_SUCCESS) ? true : false;
> +	efi_firmware_set_fmp_state_var(&state, image_index, updated);
>  
> -	return EFI_EXIT(EFI_SUCCESS);
> +	efi_firmware_print_result(status, &state);
> +
> +	return EFI_EXIT(status);
>  }
>  
>  const struct efi_firmware_management_protocol efi_fmp_fit = {
> @@ -391,7 +568,9 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
>  	u16 **abort_reason)
>  {
>  	int ret;
> +	bool updated;
>  	efi_status_t status;
> +	struct fmp_state state = { 0 };
>  
>  	EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
>  		  image_size, vendor_code, progress, abort_reason);
> @@ -399,9 +578,10 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
>  	if (!image)
>  		return EFI_EXIT(EFI_INVALID_PARAMETER);
>  
> -	status = efi_firmware_capsule_authenticate(&image, &image_size);
> +	status = efi_firmware_verify_image(&image, &image_size, image_index,
> +					   &state);
>  	if (status != EFI_SUCCESS)
> -		return EFI_EXIT(status);
> +		goto err;
>  
>  	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
>  		/*
> @@ -411,15 +591,29 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
>  		ret = fwu_get_image_index(&image_index);
>  		if (ret) {
>  			log_debug("Unable to get FWU image_index\n");
> -			return EFI_EXIT(EFI_DEVICE_ERROR);
> +			status = EFI_DEVICE_ERROR;
> +			goto err;
>  		}
>  	}
>  
>  	if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
> -			     NULL, NULL))
> -		return EFI_EXIT(EFI_DEVICE_ERROR);
> +			     NULL, NULL)) {
> +		status = EFI_DEVICE_ERROR;
> +		state.last_attempt_status = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
> +		goto err;
> +	}
> +
> +	state.last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
> +err:
> +	updated = (status == EFI_SUCCESS) ? true : false;
> +
> +	/* TODO: implement versioning for FWU multi bank update */
> +	if (!IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE))
> +		efi_firmware_set_fmp_state_var(&state, image_index, updated);
> +
> +	efi_firmware_print_result(status, &state);
>  
> -	return EFI_EXIT(EFI_SUCCESS);
> +	return EFI_EXIT(status);
>  }
>  
>  const struct efi_firmware_management_protocol efi_fmp_raw = {
> -- 
> 2.17.1
> 

Thanks
/Ilias

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

* Re: [PATCH v4 1/5] efi_loader: store firmware version into FmpState variable
  2023-03-28 11:53   ` Ilias Apalodimas
@ 2023-03-28 13:02     ` Ilias Apalodimas
  2023-03-29  7:33       ` Masahisa Kojima
  0 siblings, 1 reply; 14+ messages in thread
From: Ilias Apalodimas @ 2023-03-28 13:02 UTC (permalink / raw)
  To: Masahisa Kojima; +Cc: u-boot, Heinrich Schuchardt, Takahiro Akashi

Kojima-san
Apologies for the late review comments but I have some concerns about
the design and storing the info as EFI variables.  If the backing
storage is a file we can't trust any of that information since anyone
can tamper with the file, although the variables are defined as RO.

The new struct defines
struct fmp_state {
       u32 fw_version;
       u32 lowest_supported_version;
       u32 last_attempt_version;
       u32 last_attempt_status;
}

I think we should change the approach a bit.
fw_version -> this should go into the signature.dts we use to store
the EFI capsule certificates
lowest_supported_version -> same here, put it into the .dts file
last_attempt_version -> This is essentially the fw_version *after* the
upgrade is successful
last_attempt_status -> CapsuleXXXX holds that result for us and we
already have code to parse it.

This shields further against someone trying to manipulate
lowest_supported_version, in case someone uses this for rollback
protection checks.

Can we modify this patch accordingly?

Regards
/Ilias




On Tue, 28 Mar 2023 at 14:53, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> Kojima-san,
>
> On Thu, Mar 23, 2023 at 08:09:01PM +0900, Masahisa Kojima wrote:
> > Firmware version management is not implemented in the current
> > FMP protocol.
> > EDK II reference implementation capsule generation script inserts
> > the FMP Payload Header right before the payload, it contains the
> > firmware version and lowest supported version.
> >
> > This commit utilizes the FMP Payload Header, reads the header and
> > stores the firmware version, lowest supported version,
> > last attempt version and last attempt status into "FmpStateXXXX"
> > EFI non-volatile variable. XXXX indicates the image index,
> > since FMP protocol handles multiple image indexes.
> >
> > This change is compatible with the existing FMP implementation.
> > This change does not mandate the FMP Payload Header.
> > If no FMP Payload Header is found in the capsule file, fw_version,
> > lowest supported version, last attempt version and last attempt
> > status is 0 and this is the same behavior as existing FMP
> > implementation.
> >
> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > ---

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

* Re: [PATCH v4 1/5] efi_loader: store firmware version into FmpState variable
  2023-03-28 13:02     ` Ilias Apalodimas
@ 2023-03-29  7:33       ` Masahisa Kojima
  2023-03-29  7:45         ` Ilias Apalodimas
  0 siblings, 1 reply; 14+ messages in thread
From: Masahisa Kojima @ 2023-03-29  7:33 UTC (permalink / raw)
  To: Ilias Apalodimas; +Cc: u-boot, Heinrich Schuchardt, Takahiro Akashi

Hi Ilias,

On Tue, 28 Mar 2023 at 22:02, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> Kojima-san
> Apologies for the late review comments but I have some concerns about
> the design and storing the info as EFI variables.  If the backing
> storage is a file we can't trust any of that information since anyone
> can tamper with the file, although the variables are defined as RO.
>
> The new struct defines
> struct fmp_state {
>        u32 fw_version;
>        u32 lowest_supported_version;
>        u32 last_attempt_version;
>        u32 last_attempt_status;
> }
>
> I think we should change the approach a bit.
> fw_version -> this should go into the signature.dts we use to store
> the EFI capsule certificates
> lowest_supported_version -> same here, put it into the .dts file

OK, I will add these two values into .dts file.

> last_attempt_version -> This is essentially the fw_version *after* the
> upgrade is successful
> last_attempt_status -> CapsuleXXXX holds that result for us and we
> already have code to parse it.

To fill ESRT, I think these two values are not very important.
I will omit these values in my next series.

>
> This shields further against someone trying to manipulate
> lowest_supported_version, in case someone uses this for rollback
> protection checks.
>
> Can we modify this patch accordingly?

Thank you for your comment.
I will revise this series.

Regards,
Masahisa Kojima

>
> Regards
> /Ilias
>
>
>
>
> On Tue, 28 Mar 2023 at 14:53, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > Kojima-san,
> >
> > On Thu, Mar 23, 2023 at 08:09:01PM +0900, Masahisa Kojima wrote:
> > > Firmware version management is not implemented in the current
> > > FMP protocol.
> > > EDK II reference implementation capsule generation script inserts
> > > the FMP Payload Header right before the payload, it contains the
> > > firmware version and lowest supported version.
> > >
> > > This commit utilizes the FMP Payload Header, reads the header and
> > > stores the firmware version, lowest supported version,
> > > last attempt version and last attempt status into "FmpStateXXXX"
> > > EFI non-volatile variable. XXXX indicates the image index,
> > > since FMP protocol handles multiple image indexes.
> > >
> > > This change is compatible with the existing FMP implementation.
> > > This change does not mandate the FMP Payload Header.
> > > If no FMP Payload Header is found in the capsule file, fw_version,
> > > lowest supported version, last attempt version and last attempt
> > > status is 0 and this is the same behavior as existing FMP
> > > implementation.
> > >
> > > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > > ---

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

* Re: [PATCH v4 1/5] efi_loader: store firmware version into FmpState variable
  2023-03-29  7:33       ` Masahisa Kojima
@ 2023-03-29  7:45         ` Ilias Apalodimas
  0 siblings, 0 replies; 14+ messages in thread
From: Ilias Apalodimas @ 2023-03-29  7:45 UTC (permalink / raw)
  To: Masahisa Kojima; +Cc: u-boot, Heinrich Schuchardt, Takahiro Akashi

Kojima-san

On Wed, 29 Mar 2023 at 10:34, Masahisa Kojima
<masahisa.kojima@linaro.org> wrote:
>
> Hi Ilias,
>
> On Tue, 28 Mar 2023 at 22:02, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > Kojima-san
> > Apologies for the late review comments but I have some concerns about
> > the design and storing the info as EFI variables.  If the backing
> > storage is a file we can't trust any of that information since anyone
> > can tamper with the file, although the variables are defined as RO.
> >
> > The new struct defines
> > struct fmp_state {
> >        u32 fw_version;
> >        u32 lowest_supported_version;
> >        u32 last_attempt_version;
> >        u32 last_attempt_status;
> > }
> >
> > I think we should change the approach a bit.
> > fw_version -> this should go into the signature.dts we use to store
> > the EFI capsule certificates
> > lowest_supported_version -> same here, put it into the .dts file
>
> OK, I will add these two values into .dts file.

I think you can keep most of the code as-is.  IOW keep the struct and
just fill in the values from the .dts.  I think that would make the
abstraction easier when we try to add similar functionality to A/B
updates.

Regards
/Ilias
>
> > last_attempt_version -> This is essentially the fw_version *after* the
> > upgrade is successful
> > last_attempt_status -> CapsuleXXXX holds that result for us and we
> > already have code to parse it.
>
> To fill ESRT, I think these two values are not very important.
> I will omit these values in my next series.
>
> >
> > This shields further against someone trying to manipulate
> > lowest_supported_version, in case someone uses this for rollback
> > protection checks.
> >
> > Can we modify this patch accordingly?
>
> Thank you for your comment.
> I will revise this series.
>
> Regards,
> Masahisa Kojima
>
> >
> > Regards
> > /Ilias
> >
> >
> >
> >
> > On Tue, 28 Mar 2023 at 14:53, Ilias Apalodimas
> > <ilias.apalodimas@linaro.org> wrote:
> > >
> > > Kojima-san,
> > >
> > > On Thu, Mar 23, 2023 at 08:09:01PM +0900, Masahisa Kojima wrote:
> > > > Firmware version management is not implemented in the current
> > > > FMP protocol.
> > > > EDK II reference implementation capsule generation script inserts
> > > > the FMP Payload Header right before the payload, it contains the
> > > > firmware version and lowest supported version.
> > > >
> > > > This commit utilizes the FMP Payload Header, reads the header and
> > > > stores the firmware version, lowest supported version,
> > > > last attempt version and last attempt status into "FmpStateXXXX"
> > > > EFI non-volatile variable. XXXX indicates the image index,
> > > > since FMP protocol handles multiple image indexes.
> > > >
> > > > This change is compatible with the existing FMP implementation.
> > > > This change does not mandate the FMP Payload Header.
> > > > If no FMP Payload Header is found in the capsule file, fw_version,
> > > > lowest supported version, last attempt version and last attempt
> > > > status is 0 and this is the same behavior as existing FMP
> > > > implementation.
> > > >
> > > > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > > > ---

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

end of thread, other threads:[~2023-03-29  7:46 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-03-23 11:09 [PATCH v4 0/5] FMP versioning support Masahisa Kojima
2023-03-23 11:09 ` [PATCH v4 1/5] efi_loader: store firmware version into FmpState variable Masahisa Kojima
2023-03-28 11:53   ` Ilias Apalodimas
2023-03-28 13:02     ` Ilias Apalodimas
2023-03-29  7:33       ` Masahisa Kojima
2023-03-29  7:45         ` Ilias Apalodimas
2023-03-23 11:09 ` [PATCH v4 2/5] efi_loader: versioning support in GetImageInfo Masahisa Kojima
2023-03-28  6:52   ` Ilias Apalodimas
2023-03-23 11:09 ` [PATCH v4 3/5] efi_loader: check lowest supported version in capsule update Masahisa Kojima
2023-03-28  6:54   ` Ilias Apalodimas
2023-03-23 11:09 ` [PATCH v4 4/5] mkeficapsule: add FMP Payload Header Masahisa Kojima
2023-03-28  6:55   ` Ilias Apalodimas
2023-03-23 11:09 ` [PATCH v4 5/5] test/py: efi_capsule: test for FMP versioning Masahisa Kojima
2023-03-28  6:55   ` Ilias Apalodimas

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