All of lore.kernel.org
 help / color / mirror / Atom feed
* [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature
@ 2021-11-25  7:12 Sughosh Ganu
  2021-11-25  7:12 ` [RESEND RFC PATCH 01/10] GPT: Add function to get gpt header and partition entries Sughosh Ganu
                   ` (10 more replies)
  0 siblings, 11 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:12 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu



(resending to including the first paragraph which got deleted for some
reason).

The patchset adds support for the FWU Multi Bank Update[1]
feature. Certain aspects of the Dependable Boot[2] specification have
also been implemented.

The FWU multi bank update feature is used for supporting multiple
sets(also called banks) of firmware image(s), allowing the platform to
boot from a different bank, in case it fails to boot from the active
bank. This functionality is supported by keeping the relevant
information in a structure called metadata, which provides information
on the images. Among other parameters, the metadata structure contains
information on the currect active bank that is being used to boot
image(s).

Functionality is being added to work with the UEFI capsule driver in
u-boot. The metadata is read to gather information on the update bank,
which is the bank to which the firmware images would be flashed to. On
a successful completion of the update of all components, the active
bank field in the metadata is updated, to reflect the bank from which
the platform will boot on the subsequent boots.

Currently, the feature is being enabled on the STM32MP157C-DK2
board which boots a FIP image from a uSD card partitioned with the GPT
partioning scheme. This also requires changes in the previous stage of
bootloader, which parses the metadata and selects the bank to boot the
image(s) from. Support is being added in tf-a(BL2 stage) for the
STM32MP157C-DK2 board to boot the active bank images. These changes
are under review currently[3].

Todo's
------
1) Add a test(selftest) for the metadata access.
2) Add a tool for generation of the metadata. Not sure if this needs to
   be part of the u-boot repository though.
3) Add a tool for generation of the firmware accept/reject dummy
   capsule. Need to check if this can be added to the mkeficapsule
   tool in u-boot.

[1] - https://developer.arm.com/documentation/den0118/a
[2] - https://staging-git.codelinaro.org/linaro/firmware-dual-banked-updates/test
[3] - https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/12566

Sughosh Ganu (10):
  GPT: Add function to get gpt header and partition entries
  stm32mp: dfu: Move the ram partitions to the end of the dfu_alt_info
    variable
  FWU: Add metadata structure and functions for accessing metadata
  FWU: Add metadata access functions for GPT partitioned block devices
  FWU: stm32mp1: Add helper functions for accessing metadata
  FWU: STM32MP1: Add support to read boot index from backup register
  EFI: FMP: Add provision to update image's ImageTypeId in image
    descriptor
  FWU: Add boot time checks as highlighted by the FWU specification
  FWU: Add support for FWU Multi Bank Update feature
  FWU: cmd: Add a command to read metadata

 arch/arm/mach-stm32mp/include/mach/stm32.h |   1 +
 board/st/common/stm32mp_dfu.c              |  11 +-
 board/st/stm32mp1/stm32mp1.c               |  70 ++
 cmd/Kconfig                                |   6 +
 cmd/Makefile                               |   1 +
 cmd/fwu_metadata.c                         |  65 ++
 common/board_r.c                           |   6 +
 disk/part_efi.c                            |  10 +
 include/fwu_metadata.h                     | 140 ++++
 include/part.h                             |  14 +
 lib/Kconfig                                |  32 +
 lib/Makefile                               |   1 +
 lib/efi_loader/efi_capsule.c               | 190 +++++-
 lib/efi_loader/efi_firmware.c              |  76 ++-
 lib/fwu_updates/Makefile                   |  11 +
 lib/fwu_updates/fwu.c                      | 170 +++++
 lib/fwu_updates/fwu_metadata.c             | 275 ++++++++
 lib/fwu_updates/fwu_metadata_gpt_blk.c     | 716 +++++++++++++++++++++
 18 files changed, 1784 insertions(+), 11 deletions(-)
 create mode 100644 cmd/fwu_metadata.c
 create mode 100644 include/fwu_metadata.h
 create mode 100644 lib/fwu_updates/Makefile
 create mode 100644 lib/fwu_updates/fwu.c
 create mode 100644 lib/fwu_updates/fwu_metadata.c
 create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c

-- 
2.17.1


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

* [RESEND RFC PATCH 01/10] GPT: Add function to get gpt header and partition entries
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
@ 2021-11-25  7:12 ` Sughosh Ganu
  2021-11-25  7:12 ` [RESEND RFC PATCH 02/10] stm32mp: dfu: Move the ram partitions to the end of the dfu_alt_info variable Sughosh Ganu
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:12 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu,
	Sughosh Ganu

Add function to get the gpt header and partition entries filled. These
would be used subsequently for multi bank firmware update support on
devices where the images reside on GPT partitions.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 disk/part_efi.c | 10 ++++++++++
 include/part.h  | 14 ++++++++++++++
 2 files changed, 24 insertions(+)

diff --git a/disk/part_efi.c b/disk/part_efi.c
index 0ca7effc32..792b9218a9 100644
--- a/disk/part_efi.c
+++ b/disk/part_efi.c
@@ -216,6 +216,16 @@ int get_disk_guid(struct blk_desc * dev_desc, char *guid)
 	return 0;
 }
 
+int get_gpt_hdr_parts(struct blk_desc *desc, gpt_header *gpt_head,
+		      gpt_entry **gpt_pte)
+{
+	/* This function validates and fills in the GPT header and PTE's */
+	if (find_valid_gpt(desc, gpt_head, gpt_pte) != 1)
+		return -EINVAL;
+
+	return 0;
+}
+
 void part_print_efi(struct blk_desc *dev_desc)
 {
 	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz);
diff --git a/include/part.h b/include/part.h
index b66b07a1f0..8e86485b97 100644
--- a/include/part.h
+++ b/include/part.h
@@ -345,6 +345,20 @@ struct part_driver {
 
 #if CONFIG_IS_ENABLED(EFI_PARTITION)
 /* disk/part_efi.c */
+
+/**
+ * get_gpt_hdr_parts() - Get information on the GPT Header and
+ *                       Partition Table Entries
+ *
+ * @param desc - block device descriptor
+ * @param gpt_h - pointer to GPT header representation
+ * @param gpt_e - pointer to GPT partition table entries
+ *
+ * @return - zero on success, otherwise error
+ */
+int get_gpt_hdr_parts(struct blk_desc *desc, gpt_header *gpt_head,
+		      gpt_entry **gpt_pte);
+
 /**
  * write_gpt_table() - Write the GUID Partition Table to disk
  *
-- 
2.17.1


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

* [RESEND RFC PATCH 02/10] stm32mp: dfu: Move the ram partitions to the end of the dfu_alt_info variable
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
  2021-11-25  7:12 ` [RESEND RFC PATCH 01/10] GPT: Add function to get gpt header and partition entries Sughosh Ganu
@ 2021-11-25  7:12 ` Sughosh Ganu
  2021-11-25  7:12 ` [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata Sughosh Ganu
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:12 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu,
	Sughosh Ganu

With the FWU multi bank update feature enabled, the dfu alt no that is
used to identify the partition to be updated is derived at runtime and
should match the partition number on the storage media. Achieve this
by moving the ram partitions to the end of the dfu_alt_info variable.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 board/st/common/stm32mp_dfu.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/board/st/common/stm32mp_dfu.c b/board/st/common/stm32mp_dfu.c
index 00d1fb8f59..394a2704ee 100644
--- a/board/st/common/stm32mp_dfu.c
+++ b/board/st/common/stm32mp_dfu.c
@@ -102,6 +102,7 @@ static void board_get_alt_info_mtd(struct mtd_info *mtd, char *buf)
 
 void set_dfu_alt_info(char *interface, char *devstr)
 {
+	int len;
 	struct udevice *dev;
 	struct mtd_info *mtd;
 
@@ -112,9 +113,6 @@ void set_dfu_alt_info(char *interface, char *devstr)
 
 	memset(buf, 0, sizeof(buf));
 
-	snprintf(buf, DFU_ALT_BUF_LEN,
-		 "ram 0=%s", CONFIG_DFU_ALT_RAM0);
-
 	if (CONFIG_IS_ENABLED(MMC)) {
 		if (!uclass_get_device(UCLASS_MMC, 0, &dev))
 			board_get_alt_info_mmc(dev, buf);
@@ -151,6 +149,13 @@ void set_dfu_alt_info(char *interface, char *devstr)
 			strncat(buf, "&virt 1=PMIC", DFU_ALT_BUF_LEN);
 	}
 
+	len = strlen(buf);
+	if (buf[0] != '\0')
+		len += snprintf(buf + len, DFU_ALT_BUF_LEN - len, "&");
+
+	snprintf(buf + len, DFU_ALT_BUF_LEN,
+		 "ram 0=%s", CONFIG_DFU_ALT_RAM0);
+
 	env_set("dfu_alt_info", buf);
 	puts("DFU alt info setting: done\n");
 }
-- 
2.17.1


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

* [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
  2021-11-25  7:12 ` [RESEND RFC PATCH 01/10] GPT: Add function to get gpt header and partition entries Sughosh Ganu
  2021-11-25  7:12 ` [RESEND RFC PATCH 02/10] stm32mp: dfu: Move the ram partitions to the end of the dfu_alt_info variable Sughosh Ganu
@ 2021-11-25  7:12 ` Sughosh Ganu
  2021-11-26 11:35   ` Ilias Apalodimas
                     ` (2 more replies)
  2021-11-25  7:12 ` [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices Sughosh Ganu
                   ` (7 subsequent siblings)
  10 siblings, 3 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:12 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu,
	Sughosh Ganu

In the FWU Multi Bank Update feature, the information about the
updatable images is stored as part of the metadata, which is stored on
a dedicated partition. Add the metadata structure, and functions to
access the metadata. These are generic API's, and implementations can
be added based on parameters like how the metadata partition is
accessed and what type of storage device houses the metadata.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 include/fwu_metadata.h         | 125 +++++++++++++++
 lib/fwu_updates/fwu_metadata.c | 275 +++++++++++++++++++++++++++++++++
 2 files changed, 400 insertions(+)
 create mode 100644 include/fwu_metadata.h
 create mode 100644 lib/fwu_updates/fwu_metadata.c

diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
new file mode 100644
index 0000000000..e692ef7506
--- /dev/null
+++ b/include/fwu_metadata.h
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#if !defined _FWU_METADATA_H_
+#define _FWU_METADATA_H_
+
+#include <blk.h>
+#include <efi.h>
+#include <uuid.h>
+
+#include <linux/types.h>
+
+/**
+ * struct fwu_image_bank_info - firmware image information
+ * @image_uuid: Guid value of the image in this bank
+ * @accepted: Acceptance status of the image
+ * @reserved: Reserved
+ *
+ * The structure contains image specific fields which are
+ * used to identify the image and to specify the image's
+ * acceptance status
+ */
+struct fwu_image_bank_info {
+	efi_guid_t  image_uuid;
+	u32 accepted;
+	u32 reserved;
+};
+
+/**
+ * struct fwu_image_entry - information for a particular type of image
+ * @image_type_uuid: Guid value for identifying the image type
+ * @location_uuid: Guid of the storage volume where the image is located
+ * @img_bank_info: Array containing properties of images
+ *
+ * This structure contains information on various types of updatable
+ * firmware images. Each image type then contains an array of image
+ * information per bank.
+ */
+struct fwu_image_entry {
+	efi_guid_t image_type_uuid;
+	efi_guid_t location_uuid;
+	struct fwu_image_bank_info img_bank_info[CONFIG_FWU_NUM_BANKS];
+};
+
+/**
+ * struct fwu_metadata - Metadata structure for multi-bank updates
+ * @crc32: crc32 value for the metadata
+ * @version: Metadata version
+ * @active_index: Index of the bank currently used for booting images
+ * @previous_active_inde: Index of the bank used before the current bank
+ *                        being used for booting
+ * @img_entry: Array of information on various firmware images that can
+ *             be updated
+ *
+ * This structure is used to store all the needed information for performing
+ * multi bank updates on the platform. This contains info on the bank being
+ * used to boot along with the information needed for identification of
+ * individual images
+ */
+struct fwu_metadata {
+	u32 crc32;
+	u32 version;
+	u32 active_index;
+	u32 previous_active_index;
+
+	struct fwu_image_entry img_entry[CONFIG_FWU_NUM_IMAGES_PER_BANK];
+};
+
+/**
+ * @get_active_index: get the current active_index value
+ * @update_active_index: update the active_index value
+ * @fill_partition_guid_array: fill the array with guid values of the
+ *                             partitions found on the storage media
+ * @get_image_alt_num: get the alt number to be used for the image
+ * @metadata_check: check the validity of the metadata partitions
+ * @revert_boot_index: set the active_index to previous_active_index
+ * @set_accept_image: set the accepted bit for the image
+ * @clear_accept_image: clear the accepted bit for the image
+ * @get_metadata() - Get a metadata copy
+ */
+struct fwu_metadata_ops {
+	int (*get_active_index)(u32 *active_idx);
+
+	int (*update_active_index)(u32 active_idx);
+
+	int (*fill_partition_guid_array)(efi_guid_t **part_guid_arr,
+					 u32 *nparts);
+
+	int (*get_image_alt_num)(efi_guid_t image_type_id, u32 update_bank,
+				 int *alt_num);
+
+	int (*metadata_check)(void);
+
+	int (*revert_boot_index)(u32 *active_idx);
+
+	int (*set_accept_image)(efi_guid_t *img_type_id);
+
+	int (*clear_accept_image)(efi_guid_t *img_type_id, u32 bank);
+
+	int (*get_metadata)(struct fwu_metadata **metadata);
+};
+
+#define FWU_METADATA_GUID \
+	EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \
+		 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+
+#define FWU_METADATA_VERSION	0x1
+
+extern struct fwu_metadata_ops fwu_gpt_blk_ops;
+
+struct fwu_metadata_ops *get_plat_fwu_metadata_ops(void);
+int fwu_get_active_index(u32 *active_idx);
+int fwu_update_active_index(u32 active_idx);
+int fwu_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32 *nparts);
+int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
+			  int *alt_num);
+int fwu_metadata_check(void);
+int fwu_revert_boot_index(u32 *active_idx);
+int fwu_accept_image(efi_guid_t *img_type_id);
+int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank);
+int fwu_get_metadata(struct fwu_metadata **metadata);
+
+#endif /* _FWU_METADATA_H_ */
diff --git a/lib/fwu_updates/fwu_metadata.c b/lib/fwu_updates/fwu_metadata.c
new file mode 100644
index 0000000000..ebc3eaa04a
--- /dev/null
+++ b/lib/fwu_updates/fwu_metadata.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#include <fwu_metadata.h>
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+static inline struct fwu_metadata_ops *get_fwu_metadata_ops(void)
+{
+	return get_plat_fwu_metadata_ops();
+}
+
+/**
+ * fwu_get_active_index() - Get active_index from the metadata
+ * @active_idx: active_index value to be read
+ *
+ * Read the active_index field from the metadata and place it in
+ * the variable pointed to be the function argument.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_get_active_index(u32 *active_idx)
+{
+	struct fwu_metadata_ops *ops;
+
+	ops = get_fwu_metadata_ops();
+	if (!ops) {
+		log_err("Unable to get fwu ops\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	if (!ops->get_active_index) {
+		log_err("get_active_index() method not defined for the platform\n");
+		return -ENOSYS;
+	}
+
+	return ops->get_active_index(active_idx);
+}
+
+/**
+ * fwu_update_active_index() - Update active_index from the metadata
+ * @active_idx: active_index value to be updated
+ *
+ * Update the active_index field in the metadata
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_update_active_index(u32 active_idx)
+{
+	struct fwu_metadata_ops *ops;
+
+	ops = get_fwu_metadata_ops();
+	if (!ops) {
+		log_err("Unable to get fwu ops\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	if (!ops->update_active_index) {
+		log_err("update_active_index() method not defined for the platform\n");
+		return -ENOSYS;
+	}
+
+	return ops->update_active_index(active_idx);
+}
+
+/**
+ * fwu_fill_partition_guid_array() - Fill the part_guid_arr array with the guid's of
+ *                                   the partitions
+ * @part_guid_arr: array of partition guid's
+ * @nparts: Number of gpt partitions on the device
+ *
+ * Get the information on the partition guid's, filling the array with the guid
+ * values and also the number of partitions.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32 *nparts)
+{
+	struct fwu_metadata_ops *ops;
+
+	ops = get_fwu_metadata_ops();
+	if (!ops) {
+		log_err("Unable to get fwu ops\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	if (!ops->fill_partition_guid_array) {
+		log_err("fill_partition_guid_array() method not defined for the platform\n");
+		return -ENOSYS;
+	}
+
+	return ops->fill_partition_guid_array(part_guid_arr, nparts);
+}
+
+/**
+ * fwu_get_image_alt_num() - Get the dfu alt number to be used for capsule update
+ * @image_type_id: image guid as passed in the capsule
+ * @update_bank: Bank to which the update is to be made
+ * @alt_num: The alt_num for the image
+ *
+ * Based on the guid value passed in the capsule, along with the bank to which the
+ * image needs to be updated, get the dfu alt number which will be used for the
+ * capsule update
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
+			  int *alt_num)
+{
+	struct fwu_metadata_ops *ops;
+
+	ops = get_fwu_metadata_ops();
+	if (!ops) {
+		log_err("Unable to get fwu ops\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	if (!ops->get_image_alt_num) {
+		log_err("get_image_alt_num() method not defined for the platform\n");
+		return -ENOSYS;
+	}
+
+	return ops->get_image_alt_num(image_type_id, update_bank, alt_num);
+}
+
+/**
+ * fwu_metadata_check() - Check if the metadata is valid
+ *
+ * Validate both copies of metadata. If one of the copies
+ * has gone bad, restore it from the other bad copy.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_metadata_check(void)
+{
+	struct fwu_metadata_ops *ops;
+
+	ops = get_fwu_metadata_ops();
+	if (!ops) {
+		log_err("Unable to get fwu ops\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	if (!ops->metadata_check) {
+		log_err("metadata_check() method not defined for the platform\n");
+		return -ENOSYS;
+	}
+
+	return ops->metadata_check();
+}
+
+/**
+ * fwu_revert_boot_index() - Revert the active index in the metadata
+ * @active_idx: Value of the updated active_index
+ *
+ * Revert the active_index value in the metadata, by swapping the values
+ * of active_index and previous_active_index in both copies of the
+ * metadata.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_revert_boot_index(u32 *active_idx)
+{
+	struct fwu_metadata_ops *ops;
+
+	ops = get_fwu_metadata_ops();
+	if (!ops) {
+		log_err("Unable to get fwu ops\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	if (!ops->revert_boot_index) {
+		log_err("revert_boot_index() method not defined for the platform\n");
+		return -ENOSYS;
+	}
+
+	return ops->revert_boot_index(active_idx);
+}
+
+/**
+ * fwu_accept_image() - Set the Acceptance bit for the image
+ * @img_type_id: Guid of the image type for which the accepted bit is to be
+ *               cleared
+ *
+ * Set the accepted bit for the image specified by the img_guid parameter. This
+ * indicates acceptance of image for subsequent boots by some governing component
+ * like OS(or firmware).
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_accept_image(efi_guid_t *img_type_id)
+{
+	struct fwu_metadata_ops *ops;
+
+	ops = get_fwu_metadata_ops();
+	if (!ops) {
+		log_err("Unable to get fwu ops\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	if (!ops->set_accept_image) {
+		log_err("set_accept_image() method not defined for the platform\n");
+		return -ENOSYS;
+	}
+
+	return ops->set_accept_image(img_type_id);
+}
+
+/**
+ * fwu_clear_accept_image() - Clear the Acceptance bit for the image
+ * @img_type_id: Guid of the image type for which the accepted bit is to be
+ *               cleared
+ *
+ * Clear the accepted bit for the image type specified by the img_type_id parameter.
+ * This function is called after the image has been updated. The accepted bit is
+ * cleared to be set subsequently after passing the image acceptance criteria, by
+ * either the OS(or firmware)
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
+{
+	struct fwu_metadata_ops *ops;
+
+	ops = get_fwu_metadata_ops();
+	if (!ops) {
+		log_err("Unable to get fwu ops\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	if (!ops->clear_accept_image) {
+		log_err("clear_accept_image() method not defined for the platform\n");
+		return -ENOSYS;
+	}
+
+	return ops->clear_accept_image(img_type_id, bank);
+}
+
+/**
+ * fwu_get_metadata() - Get a metadata copy
+ * @metadata: Copy of the metadata
+ *
+ * Get a valid copy of the metadata.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_get_metadata(struct fwu_metadata **metadata)
+{
+	struct fwu_metadata_ops *ops;
+
+	ops = get_fwu_metadata_ops();
+	if (!ops) {
+		log_err("Unable to get fwu ops\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	if (!ops->get_metadata) {
+		log_err("get_metadata() method not defined for the platform\n");
+		return -ENOSYS;
+	}
+
+	return ops->get_metadata(metadata);
+}
-- 
2.17.1


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

* [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
                   ` (2 preceding siblings ...)
  2021-11-25  7:12 ` [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata Sughosh Ganu
@ 2021-11-25  7:12 ` Sughosh Ganu
  2021-12-01 12:26   ` Ilias Apalodimas
  2021-12-01 18:02   ` Simon Glass
  2021-11-25  7:12 ` [RESEND RFC PATCH 05/10] FWU: stm32mp1: Add helper functions for accessing metadata Sughosh Ganu
                   ` (6 subsequent siblings)
  10 siblings, 2 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:12 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu,
	Sughosh Ganu

In the FWU Multi Bank Update feature, the information about the
updatable images is stored as part of the metadata, on a separate
partition. Add functions for reading from and writing to the metadata
when the updatable images and the metadata are stored on a block
device which is formated with GPT based partition scheme.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
 1 file changed, 716 insertions(+)
 create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c

diff --git a/lib/fwu_updates/fwu_metadata_gpt_blk.c b/lib/fwu_updates/fwu_metadata_gpt_blk.c
new file mode 100644
index 0000000000..98cc53f706
--- /dev/null
+++ b/lib/fwu_updates/fwu_metadata_gpt_blk.c
@@ -0,0 +1,716 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#include <blk.h>
+#include <efi_loader.h>
+#include <fwu_metadata.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <part.h>
+#include <part_efi.h>
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <u-boot/crc.h>
+
+#define PRIMARY_VALID		0x1
+#define SECONDARY_VALID		0x2
+
+#define MDATA_READ		(u8)0x1
+#define MDATA_WRITE		(u8)0x2
+
+#define IMAGE_ACCEPT_SET	(u8)0x1
+#define IMAGE_ACCEPT_CLEAR	(u8)0x2
+
+static int gpt_verify_metadata(struct fwu_metadata *metadata, bool pri_part)
+{
+	u32 calc_crc32;
+	void *buf;
+
+	buf = &metadata->version;
+	calc_crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
+
+	if (calc_crc32 != metadata->crc32) {
+		log_err("crc32 check failed for %s metadata partition\n",
+			pri_part ? "primary" : "secondary");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int gpt_get_metadata_partitions(struct blk_desc *desc,
+				       u32 *primary_mpart,
+				       u32 *secondary_mpart)
+{
+	int i, ret;
+	u32 nparts, mparts;
+	gpt_entry *gpt_pte = NULL;
+	const efi_guid_t fwu_metadata_guid = FWU_METADATA_GUID;
+
+	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
+				     desc->blksz);
+
+	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
+	if (ret < 0) {
+		log_err("Error getting GPT header and partitions\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	nparts = gpt_head->num_partition_entries;
+	for (i = 0, mparts = 0; i < nparts; i++) {
+		if (!guidcmp(&fwu_metadata_guid,
+			     &gpt_pte[i].partition_type_guid)) {
+			++mparts;
+			if (!*primary_mpart)
+				*primary_mpart = i + 1;
+			else
+				*secondary_mpart = i + 1;
+		}
+	}
+
+	if (mparts != 2) {
+		log_err("Expect two copies of the metadata instead of %d\n",
+			mparts);
+		ret = -EINVAL;
+	} else {
+		ret = 0;
+	}
+out:
+	free(gpt_pte);
+
+	return ret;
+}
+
+static int gpt_get_metadata_disk_part(struct blk_desc *desc,
+				      struct disk_partition *info,
+				      u32 part_num)
+{
+	int ret;
+	char *metadata_guid_str = "8a7a84a0-8387-40f6-ab41-a8b9a5a60d23";
+
+	ret = part_get_info(desc, part_num, info);
+	if (ret < 0) {
+		log_err("Unable to get the partition info for the metadata part %d",
+			part_num);
+		return -1;
+	}
+
+	/* Check that it is indeed the metadata partition */
+	if (!strncmp(info->type_guid, metadata_guid_str, UUID_STR_LEN)) {
+		/* Found the metadata partition */
+		return 0;
+	}
+
+	return -1;
+}
+
+static int gpt_read_write_metadata(struct blk_desc *desc,
+				   struct fwu_metadata *metadata,
+				   u8 access, u32 part_num)
+{
+	int ret;
+	u32 len, blk_start, blkcnt;
+	struct disk_partition info;
+
+	ALLOC_CACHE_ALIGN_BUFFER_PAD(struct fwu_metadata, mdata, 1, desc->blksz);
+
+	ret = gpt_get_metadata_disk_part(desc, &info, part_num);
+	if (ret < 0) {
+		printf("Unable to get the metadata partition\n");
+		return -ENODEV;
+	}
+
+	len = sizeof(*metadata);
+	blkcnt = BLOCK_CNT(len, desc);
+	if (blkcnt > info.size) {
+		log_err("Block count exceeds metadata partition size\n");
+		return -ERANGE;
+	}
+
+	blk_start = info.start;
+	if (access == MDATA_READ) {
+		if (blk_dread(desc, blk_start, blkcnt, mdata) != blkcnt) {
+			log_err("Error reading metadata from the device\n");
+			return -EIO;
+		}
+		memcpy(metadata, mdata, sizeof(struct fwu_metadata));
+	} else {
+		if (blk_dwrite(desc, blk_start, blkcnt, metadata) != blkcnt) {
+			log_err("Error writing metadata to the device\n");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+static int gpt_read_metadata(struct blk_desc *desc,
+			     struct fwu_metadata *metadata, u32 part_num)
+{
+	return gpt_read_write_metadata(desc, metadata, MDATA_READ, part_num);
+}
+
+static int gpt_write_metadata_partition(struct blk_desc *desc,
+					struct fwu_metadata *metadata,
+					u32 part_num)
+{
+	return gpt_read_write_metadata(desc, metadata, MDATA_WRITE, part_num);
+}
+
+static int gpt_update_metadata(struct fwu_metadata *metadata)
+{
+	int ret;
+	struct blk_desc *desc;
+	u32 primary_mpart, secondary_mpart;
+
+	ret = fwu_plat_get_blk_desc(&desc);
+	if (ret < 0) {
+		log_err("Block device not found\n");
+		return -ENODEV;
+	}
+
+	primary_mpart = secondary_mpart = 0;
+	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
+					  &secondary_mpart);
+
+	if (ret < 0) {
+		log_err("Error getting the metadata partitions\n");
+		return -ENODEV;
+	}
+
+	/* First write the primary partition*/
+	ret = gpt_write_metadata_partition(desc, metadata, primary_mpart);
+	if (ret < 0) {
+		log_err("Updating primary metadata partition failed\n");
+		return ret;
+	}
+
+	/* And now the replica */
+	ret = gpt_write_metadata_partition(desc, metadata, secondary_mpart);
+	if (ret < 0) {
+		log_err("Updating secondary metadata partition failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int gpt_get_valid_metadata(struct fwu_metadata **metadata)
+{
+	int ret;
+	struct blk_desc *desc;
+	u32 primary_mpart, secondary_mpart;
+
+	ret = fwu_plat_get_blk_desc(&desc);
+	if (ret < 0) {
+		log_err("Block device not found\n");
+		return -ENODEV;
+	}
+
+	primary_mpart = secondary_mpart = 0;
+	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
+					  &secondary_mpart);
+
+	if (ret < 0) {
+		log_err("Error getting the metadata partitions\n");
+		return -ENODEV;
+	}
+
+	*metadata = malloc(sizeof(struct fwu_metadata));
+	if (!*metadata) {
+		log_err("Unable to allocate memory for reading metadata\n");
+		return -ENOMEM;
+	}
+
+	ret = gpt_read_metadata(desc, *metadata, primary_mpart);
+	if (ret < 0) {
+		log_err("Failed to read the metadata from the device\n");
+		return -EIO;
+	}
+
+	ret = gpt_verify_metadata(*metadata, 1);
+	if (!ret)
+		return 0;
+
+	/*
+	 * Verification of the primary metadata copy failed.
+	 * Try to read the replica.
+	 */
+	memset(*metadata, 0, sizeof(struct fwu_metadata));
+	ret = gpt_read_metadata(desc, *metadata, secondary_mpart);
+	if (ret < 0) {
+		log_err("Failed to read the metadata from the device\n");
+		return -EIO;
+	}
+
+	ret = gpt_verify_metadata(*metadata, 0);
+	if (!ret)
+		return 0;
+
+	/* Both the metadata copies are corrupted. */
+	return -1;
+}
+
+static int gpt_check_metadata_validity(void)
+{
+	int ret;
+	struct blk_desc *desc;
+	struct fwu_metadata *pri_metadata;
+	struct fwu_metadata *secondary_metadata;
+	u32 primary_mpart, secondary_mpart;
+	u32 valid_partitions;
+
+	ret = fwu_plat_get_blk_desc(&desc);
+	if (ret < 0) {
+		log_err("Block device not found\n");
+		return -ENODEV;
+	}
+
+	/*
+	 * Two metadata partitions are expected.
+	 * If we don't have two, user needs to create
+	 * them first
+	 */
+	primary_mpart = secondary_mpart = 0;
+	valid_partitions = 0;
+	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
+					  &secondary_mpart);
+
+	if (ret < 0) {
+		log_err("Error getting the metadata partitions\n");
+		return -ENODEV;
+	}
+
+	pri_metadata = malloc(sizeof(*pri_metadata));
+	if (!pri_metadata) {
+		log_err("Unable to allocate memory for reading metadata\n");
+		return -ENOMEM;
+	}
+
+	secondary_metadata = malloc(sizeof(*secondary_metadata));
+	if (!secondary_metadata) {
+		log_err("Unable to allocate memory for reading metadata\n");
+		return -ENOMEM;
+	}
+
+	ret = gpt_read_metadata(desc, pri_metadata, primary_mpart);
+	if (ret < 0) {
+		log_err("Failed to read the metadata from the device\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	ret = gpt_verify_metadata(pri_metadata, 1);
+	if (!ret)
+		valid_partitions |= PRIMARY_VALID;
+
+	/* Now check the secondary partition */
+	ret = gpt_read_metadata(desc, secondary_metadata, secondary_mpart);
+	if (ret < 0) {
+		log_err("Failed to read the metadata from the device\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	ret = gpt_verify_metadata(secondary_metadata, 0);
+	if (!ret)
+		valid_partitions |= SECONDARY_VALID;
+
+	if (valid_partitions == (PRIMARY_VALID | SECONDARY_VALID)) {
+		ret = -1;
+		/*
+		 * Before returning, check that both the
+		 * metadata copies are the same. If not,
+		 * the metadata copies need to be
+		 * re-populated.
+		 */
+		if (!memcmp(pri_metadata, secondary_metadata,
+			    sizeof(*pri_metadata)))
+			ret = 0;
+		goto out;
+	} else if (valid_partitions == SECONDARY_VALID) {
+		ret = gpt_write_metadata_partition(desc, secondary_metadata,
+						   primary_mpart);
+		if (ret < 0) {
+			log_err("Restoring primary metadata partition failed\n");
+			goto out;
+		}
+	} else if (valid_partitions == PRIMARY_VALID) {
+		ret = gpt_write_metadata_partition(desc, pri_metadata,
+						   secondary_mpart);
+		if (ret < 0) {
+			log_err("Restoring secondary metadata partition failed\n");
+			goto out;
+		}
+	} else {
+		ret = -1;
+	}
+
+out:
+	free(pri_metadata);
+
+	return ret;
+}
+
+static int gpt_fill_partition_guid_array(struct blk_desc *desc,
+					 efi_guid_t **part_guid_arr,
+					 u32 *nparts)
+{
+	int ret, i;
+	u32 parts;
+	gpt_entry *gpt_pte = NULL;
+	const efi_guid_t null_guid = NULL_GUID;
+
+	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
+				     desc->blksz);
+
+	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
+	if (ret < 0) {
+		log_err("Error getting GPT header and partitions\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	*nparts = gpt_head->num_partition_entries;
+
+	/*
+	 * There could be a scenario where the number of partition entries
+	 * configured in the GPT header is the default value of 128. Find
+	 * the actual number of populated partitioned entries
+	 */
+	for (i = 0, parts = 0; i < *nparts; i++) {
+		if (!guidcmp(&gpt_pte[i].partition_type_guid, &null_guid))
+			continue;
+		++parts;
+	}
+
+	*nparts = parts;
+	*part_guid_arr = malloc(sizeof(efi_guid_t) * *nparts);
+	if (!part_guid_arr) {
+		log_err("Unable to allocate memory for guid array\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < *nparts; i++) {
+		guidcpy((*part_guid_arr + i),
+			&gpt_pte[i].partition_type_guid);
+	}
+
+out:
+	free(gpt_pte);
+	return ret;
+}
+
+int fwu_gpt_get_active_index(u32 *active_idx)
+{
+	int ret;
+	struct fwu_metadata *metadata;
+
+	ret = gpt_get_valid_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to get valid metadata\n");
+		goto out;
+	}
+
+	/*
+	 * Found the metadata partition, now read the active_index
+	 * value
+	 */
+	*active_idx = metadata->active_index;
+	if (*active_idx > CONFIG_FWU_NUM_BANKS - 1) {
+		printf("Active index value read is incorrect\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+out:
+	free(metadata);
+
+	return ret;
+}
+
+static int gpt_get_image_alt_num(struct blk_desc *desc,
+				 efi_guid_t image_type_id,
+				 u32 update_bank, int *alt_no)
+{
+	int ret, i;
+	u32 nparts;
+	gpt_entry *gpt_pte = NULL;
+	struct fwu_metadata *metadata;
+	struct fwu_image_entry *img_entry;
+	struct fwu_image_bank_info *img_bank_info;
+	efi_guid_t unique_part_guid;
+	efi_guid_t image_guid = NULL_GUID;
+
+	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
+				     desc->blksz);
+
+	ret = gpt_get_valid_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to read valid metadata\n");
+		goto out;
+	}
+
+	/*
+	 * The metadata has been read. Now get the image_uuid for the
+	 * image with the update_bank.
+	 */
+	for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
+		if (!guidcmp(&image_type_id,
+			     &metadata->img_entry[i].image_type_uuid)) {
+			img_entry = &metadata->img_entry[i];
+			img_bank_info = &img_entry->img_bank_info[update_bank];
+			guidcpy(&image_guid, &img_bank_info->image_uuid);
+		}
+	}
+
+	/*
+	 * Now read the GPT Partition Table Entries to find a matching
+	 * partition with UniquePartitionGuid value. We need to iterate
+	 * through all the GPT partitions since they might be in any
+	 * order
+	 */
+	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
+	if (ret < 0) {
+		log_err("Error getting GPT header and partitions\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	nparts = gpt_head->num_partition_entries;
+
+	for (i = 0; i < nparts; i++) {
+		unique_part_guid = gpt_pte[i].unique_partition_guid;
+		if (!guidcmp(&unique_part_guid, &image_guid)) {
+			/* Found the partition */
+			*alt_no = i + 1;
+			break;
+		}
+	}
+
+	if (i == nparts) {
+		log_err("Partition with the image guid not found\n");
+		ret = -EINVAL;
+	}
+
+out:
+	free(metadata);
+	free(gpt_pte);
+	return ret;
+}
+
+int fwu_gpt_update_active_index(u32 active_idx)
+{
+	int ret;
+	void *buf;
+	u32 cur_active_index;
+	struct fwu_metadata *metadata;
+
+	if (active_idx > CONFIG_FWU_NUM_BANKS) {
+		printf("Active index value to be updated is incorrect\n");
+		return -1;
+	}
+
+	ret = gpt_get_valid_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to get valid metadata\n");
+		goto out;
+	}
+
+	/*
+	 * Update the active index and previous_active_index fields
+	 * in the metadata
+	 */
+	cur_active_index = metadata->active_index;
+	metadata->active_index = active_idx;
+	metadata->previous_active_index = cur_active_index;
+
+	/*
+	 * Calculate the crc32 for the updated metadata
+	 * and put the updated value in the metadata crc32
+	 * field
+	 */
+	buf = &metadata->version;
+	metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
+
+	/*
+	 * Now write this updated metadata to both the
+	 * metadata partitions
+	 */
+	ret = gpt_update_metadata(metadata);
+	if (ret < 0) {
+		log_err("Failed to update metadata partitions\n");
+		ret = -EIO;
+	}
+
+out:
+	free(metadata);
+
+	return ret;
+}
+
+int fwu_gpt_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32 *nparts)
+{
+	int ret;
+	struct blk_desc *desc;
+
+	ret = fwu_plat_get_blk_desc(&desc);
+	if (ret < 0) {
+		log_err("Block device not found\n");
+		return -ENODEV;
+	}
+
+	return gpt_fill_partition_guid_array(desc, part_guid_arr, nparts);
+}
+
+int fwu_gpt_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
+			      int *alt_no)
+{
+	int ret;
+	struct blk_desc *desc;
+
+	ret = fwu_plat_get_blk_desc(&desc);
+	if (ret < 0) {
+		log_err("Block device not found\n");
+		return -ENODEV;
+	}
+
+	return gpt_get_image_alt_num(desc, image_type_id, update_bank, alt_no);
+}
+
+int fwu_gpt_metadata_check(void)
+{
+	/*
+	 * Check if both the copies of the metadata are valid.
+	 * If one has gone bad, restore it from the other good
+	 * copy.
+	 */
+	return gpt_check_metadata_validity();
+}
+
+int fwu_gpt_get_metadata(struct fwu_metadata **metadata)
+{
+	return gpt_get_valid_metadata(metadata);
+}
+
+int fwu_gpt_revert_boot_index(u32 *active_idx)
+{
+	int ret;
+	void *buf;
+	u32 cur_active_index;
+	struct fwu_metadata *metadata;
+
+	ret = gpt_get_valid_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to get valid metadata\n");
+		goto out;
+	}
+
+	/*
+	 * Swap the active index and previous_active_index fields
+	 * in the metadata
+	 */
+	cur_active_index = metadata->active_index;
+	metadata->active_index = metadata->previous_active_index;
+	metadata->previous_active_index = cur_active_index;
+	*active_idx = metadata->active_index;
+
+	/*
+	 * Calculate the crc32 for the updated metadata
+	 * and put the updated value in the metadata crc32
+	 * field
+	 */
+	buf = &metadata->version;
+	metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
+
+	/*
+	 * Now write this updated metadata to both the
+	 * metadata partitions
+	 */
+	ret = gpt_update_metadata(metadata);
+	if (ret < 0) {
+		log_err("Failed to update metadata partitions\n");
+		ret = -EIO;
+	}
+
+out:
+	free(metadata);
+
+	return ret;
+}
+
+static int fwu_gpt_set_clear_image_accept(efi_guid_t *img_type_id,
+					  u32 bank, u8 action)
+{
+	void *buf;
+	int ret, i;
+	u32 nimages;
+	struct fwu_metadata *metadata;
+	struct fwu_image_entry *img_entry;
+	struct fwu_image_bank_info *img_bank_info;
+
+	ret = gpt_get_valid_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to get valid metadata\n");
+		goto out;
+	}
+
+	if (action == IMAGE_ACCEPT_SET)
+		bank = metadata->active_index;
+
+	nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
+	img_entry = &metadata->img_entry[0];
+	for (i = 0; i < nimages; i++) {
+		if (!guidcmp(&img_entry[i].image_type_uuid, img_type_id)) {
+			img_bank_info = &img_entry[i].img_bank_info[bank];
+			if (action == IMAGE_ACCEPT_SET)
+				img_bank_info->accepted |= FWU_IMAGE_ACCEPTED;
+			else
+				img_bank_info->accepted = 0;
+
+			buf = &metadata->version;
+			metadata->crc32 = crc32(0, buf, sizeof(*metadata) -
+						sizeof(u32));
+
+			ret = gpt_update_metadata(metadata);
+			goto out;
+		}
+	}
+
+	/* Image not found */
+	ret = -EINVAL;
+
+out:
+	free(metadata);
+
+	return ret;
+}
+
+int fwu_gpt_accept_image(efi_guid_t *img_type_id)
+{
+	return fwu_gpt_set_clear_image_accept(img_type_id, 0,
+					      IMAGE_ACCEPT_SET);
+}
+
+int fwu_gpt_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
+{
+	return fwu_gpt_set_clear_image_accept(img_type_id, bank,
+					      IMAGE_ACCEPT_CLEAR);
+}
+
+struct fwu_metadata_ops fwu_gpt_blk_ops = {
+	.get_active_index = fwu_gpt_get_active_index,
+	.update_active_index = fwu_gpt_update_active_index,
+	.fill_partition_guid_array = fwu_gpt_fill_partition_guid_array,
+	.get_image_alt_num = fwu_gpt_get_image_alt_num,
+	.metadata_check = fwu_gpt_metadata_check,
+	.revert_boot_index = fwu_gpt_revert_boot_index,
+	.set_accept_image = fwu_gpt_accept_image,
+	.clear_accept_image = fwu_gpt_clear_accept_image,
+	.get_metadata = fwu_gpt_get_metadata,
+};
-- 
2.17.1


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

* [RESEND RFC PATCH 05/10] FWU: stm32mp1: Add helper functions for accessing metadata
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
                   ` (3 preceding siblings ...)
  2021-11-25  7:12 ` [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices Sughosh Ganu
@ 2021-11-25  7:12 ` Sughosh Ganu
  2021-11-25  7:12 ` [RESEND RFC PATCH 06/10] FWU: STM32MP1: Add support to read boot index from backup register Sughosh Ganu
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:12 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu,
	Sughosh Ganu

Add helper functions needed for accessing the metadata which contains
information on the updatable images. These functions have been added
for the STM32MP157C-DK2 board which has the updatable images on the
uSD card, formatted as GPT partitions.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 board/st/stm32mp1/stm32mp1.c | 63 ++++++++++++++++++++++++++++++++++++
 include/fwu_metadata.h       |  3 ++
 2 files changed, 66 insertions(+)

diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c
index 84592677e4..a6884d2772 100644
--- a/board/st/stm32mp1/stm32mp1.c
+++ b/board/st/stm32mp1/stm32mp1.c
@@ -7,6 +7,7 @@
 
 #include <common.h>
 #include <adc.h>
+#include <blk.h>
 #include <bootm.h>
 #include <clk.h>
 #include <config.h>
@@ -14,6 +15,7 @@
 #include <env.h>
 #include <env_internal.h>
 #include <fdt_support.h>
+#include <fwu_metadata.h>
 #include <g_dnl.h>
 #include <generic-phy.h>
 #include <hang.h>
@@ -23,6 +25,7 @@
 #include <log.h>
 #include <malloc.h>
 #include <misc.h>
+#include <mmc.h>
 #include <mtd_node.h>
 #include <net.h>
 #include <netdev.h>
@@ -938,3 +941,63 @@ static void board_copro_image_process(ulong fw_image, size_t fw_size)
 }
 
 U_BOOT_FIT_LOADABLE_HANDLER(IH_TYPE_COPRO, board_copro_image_process);
+
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE
+
+int fwu_plat_get_update_index(u32 *update_idx)
+{
+	int ret;
+	u32 active_idx;
+
+	ret = fwu_get_active_index(&active_idx);
+
+	if (ret < 0)
+		return -1;
+
+	*update_idx = active_idx ^= 0x1;
+
+	return ret;
+}
+
+int fwu_plat_get_blk_desc(struct blk_desc **desc)
+{
+	int ret;
+	struct mmc *mmc;
+	struct udevice *dev;
+
+	/*
+	 * Initial support is being added for the DK2
+	 * platform
+	 */
+	if (CONFIG_IS_ENABLED(TARGET_ST_STM32MP15x) &&
+	    (of_machine_is_compatible("st,stm32mp157c-dk2"))) {
+		ret = uclass_get_device(UCLASS_MMC, 0, &dev);
+		if (ret)
+			return -1;
+
+		mmc = mmc_get_mmc_dev(dev);
+		if (!mmc)
+			return -1;
+
+		if (mmc_init(mmc))
+			return -1;
+
+		*desc = mmc_get_blk_desc(mmc);
+		if (!*desc)
+			return -1;
+	}
+
+	return 0;
+}
+
+struct fwu_metadata_ops *get_plat_fwu_metadata_ops(void)
+{
+	if (CONFIG_IS_ENABLED(TARGET_ST_STM32MP15x) &&
+	    (of_machine_is_compatible("st,stm32mp157c-dk2"))) {
+		return &fwu_gpt_blk_ops;
+	}
+
+	return NULL;
+}
+
+#endif /* CONFIG_FWU_MULTI_BANK_UPDATE */
diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
index e692ef7506..6a5e814ab6 100644
--- a/include/fwu_metadata.h
+++ b/include/fwu_metadata.h
@@ -122,4 +122,7 @@ int fwu_accept_image(efi_guid_t *img_type_id);
 int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank);
 int fwu_get_metadata(struct fwu_metadata **metadata);
 
+int fwu_plat_get_update_index(u32 *update_idx);
+int fwu_plat_get_blk_desc(struct blk_desc **desc);
+
 #endif /* _FWU_METADATA_H_ */
-- 
2.17.1


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

* [RESEND RFC PATCH 06/10] FWU: STM32MP1: Add support to read boot index from backup register
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
                   ` (4 preceding siblings ...)
  2021-11-25  7:12 ` [RESEND RFC PATCH 05/10] FWU: stm32mp1: Add helper functions for accessing metadata Sughosh Ganu
@ 2021-11-25  7:12 ` Sughosh Ganu
  2021-11-25  7:12 ` [RESEND RFC PATCH 07/10] EFI: FMP: Add provision to update image's ImageTypeId in image descriptor Sughosh Ganu
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:12 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu,
	Sughosh Ganu

The FWU Multi Bank Update feature allows the platform to boot the
firmware images from one of the partitions(banks). The first stage
bootloader(fsbl) passes the value of the boot index, i.e. the bank
from which the firmware images were booted from to U-Boot. On the
STM32MP157C-DK2 board, this value is passed through one of the SoC's
backup register. Add a function to read the boot index value from the
backup register.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 arch/arm/mach-stm32mp/include/mach/stm32.h | 1 +
 board/st/stm32mp1/stm32mp1.c               | 7 +++++++
 include/fwu_metadata.h                     | 1 +
 3 files changed, 9 insertions(+)

diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h
index c11a9903f2..21ed9f12e4 100644
--- a/arch/arm/mach-stm32mp/include/mach/stm32.h
+++ b/arch/arm/mach-stm32mp/include/mach/stm32.h
@@ -97,6 +97,7 @@ enum boot_device {
 #define TAMP_BACKUP_REGISTER(x)		(STM32_TAMP_BASE + 0x100 + 4 * x)
 #define TAMP_BACKUP_MAGIC_NUMBER	TAMP_BACKUP_REGISTER(4)
 #define TAMP_BACKUP_BRANCH_ADDRESS	TAMP_BACKUP_REGISTER(5)
+#define TAMP_FWU_BOOT_IDX		TAMP_BACKUP_REGISTER(10)
 #define TAMP_COPRO_RSC_TBL_ADDRESS	TAMP_BACKUP_REGISTER(17)
 #define TAMP_COPRO_STATE		TAMP_BACKUP_REGISTER(18)
 #define TAMP_BOOT_CONTEXT		TAMP_BACKUP_REGISTER(20)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c
index a6884d2772..32bba71289 100644
--- a/board/st/stm32mp1/stm32mp1.c
+++ b/board/st/stm32mp1/stm32mp1.c
@@ -990,6 +990,13 @@ int fwu_plat_get_blk_desc(struct blk_desc **desc)
 	return 0;
 }
 
+void fwu_plat_get_bootidx(void *boot_idx)
+{
+	u32 *bootidx = boot_idx;
+
+	*bootidx = readl(TAMP_FWU_BOOT_IDX);
+}
+
 struct fwu_metadata_ops *get_plat_fwu_metadata_ops(void)
 {
 	if (CONFIG_IS_ENABLED(TARGET_ST_STM32MP15x) &&
diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
index 6a5e814ab6..44f06f4c6a 100644
--- a/include/fwu_metadata.h
+++ b/include/fwu_metadata.h
@@ -124,5 +124,6 @@ int fwu_get_metadata(struct fwu_metadata **metadata);
 
 int fwu_plat_get_update_index(u32 *update_idx);
 int fwu_plat_get_blk_desc(struct blk_desc **desc);
+void fwu_plat_get_bootidx(void *boot_idx);
 
 #endif /* _FWU_METADATA_H_ */
-- 
2.17.1


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

* [RESEND RFC PATCH 07/10] EFI: FMP: Add provision to update image's ImageTypeId in image descriptor
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
                   ` (5 preceding siblings ...)
  2021-11-25  7:12 ` [RESEND RFC PATCH 06/10] FWU: STM32MP1: Add support to read boot index from backup register Sughosh Ganu
@ 2021-11-25  7:12 ` Sughosh Ganu
  2021-11-26 12:43   ` Heinrich Schuchardt
  2021-11-25  7:13 ` [RESEND RFC PATCH 08/10] FWU: Add boot time checks as highlighted by the FWU specification Sughosh Ganu
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:12 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu,
	Sughosh Ganu

The FWU Multi Banks Update feature allows updating different types of
updatable firmware images on the platform. These image types are
identified using the ImageTypeId GUID value. Add support in the
GetImageInfo function of the FMP protocol to get the GUID values for
the individual images and populate these in the image descriptor for
the corresponding images.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 lib/efi_loader/efi_firmware.c | 76 ++++++++++++++++++++++++++++++++---
 1 file changed, 71 insertions(+), 5 deletions(-)

diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index a1b88dbfc2..a2b639b448 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 <fwu_metadata.h>
 #include <image.h>
 #include <signatures.h>
 
@@ -106,7 +107,8 @@ efi_status_t EFIAPI efi_firmware_set_package_info_unsupported(
  * @descriptor_size:		Pointer to descriptor size
  * package_version:		Package version
  * package_version_name:	Package version's name
- * image_type:			Image type GUID
+ * guid_array:			Image type GUID array
+ * nparts:			Number of partions on the storage device
  *
  * Return information bout the current firmware image in @image_info.
  * @image_info will consist of a number of descriptors.
@@ -122,7 +124,7 @@ static efi_status_t efi_get_dfu_info(
 	efi_uintn_t *descriptor_size,
 	u32 *package_version,
 	u16 **package_version_name,
-	const efi_guid_t *image_type)
+	const efi_guid_t *guid_array, u32 nparts)
 {
 	struct dfu_entity *dfu;
 	size_t names_len, total_size;
@@ -145,6 +147,19 @@ static efi_status_t efi_get_dfu_info(
 		return EFI_SUCCESS;
 	}
 
+	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+		/*
+		 * For FWU multi bank updates, the number of partitions
+		 * should at least be same as dfu partitions or less
+		 */
+		if (nparts > dfu_num) {
+			log_err("Number of dfu alt no's less than partitions\n");
+			dfu_free_entities();
+
+			return EFI_INVALID_PARAMETER;
+		}
+	}
+
 	total_size = sizeof(*image_info) * dfu_num + names_len;
 	/*
 	 * we will assume that sizeof(*image_info) * dfu_name
@@ -172,7 +187,11 @@ static efi_status_t efi_get_dfu_info(
 	next = name;
 	list_for_each_entry(dfu, &dfu_list, list) {
 		image_info[i].image_index = dfu->alt + 1;
-		image_info[i].image_type_id = *image_type;
+		if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE))
+			image_info[i].image_type_id = guid_array[i];
+		else
+			image_info[i].image_type_id = *guid_array;
+
 		image_info[i].image_id = dfu->alt;
 
 		/* copy the DFU entity name */
@@ -249,7 +268,9 @@ efi_status_t EFIAPI efi_firmware_fit_get_image_info(
 	u32 *package_version,
 	u16 **package_version_name)
 {
+	u32 nparts;
 	efi_status_t ret;
+	efi_guid_t *part_guid_arr;
 
 	EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
 		  image_info_size, image_info,
@@ -264,12 +285,24 @@ efi_status_t EFIAPI efi_firmware_fit_get_image_info(
 	     !descriptor_size || !package_version || !package_version_name))
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
 
+	part_guid_arr = malloc(sizeof(efi_guid_t));
+	if (!part_guid_arr) {
+		log_err("Unable to allocate memory for guid array\n");
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
+	}
+
+	guidcpy(part_guid_arr, &efi_firmware_image_type_uboot_fit);
+	nparts = 1;
+
 	ret = efi_get_dfu_info(image_info_size, image_info,
 			       descriptor_version, descriptor_count,
 			       descriptor_size,
 			       package_version, package_version_name,
-			       &efi_firmware_image_type_uboot_fit);
+			       part_guid_arr, nparts);
 
+out:
+	free(part_guid_arr);
 	return EFI_EXIT(ret);
 }
 
@@ -358,7 +391,10 @@ efi_status_t EFIAPI efi_firmware_raw_get_image_info(
 	u32 *package_version,
 	u16 **package_version_name)
 {
+	u32 nparts;
+	int status;
 	efi_status_t ret = EFI_SUCCESS;
+	efi_guid_t *part_guid_arr;
 
 	EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
 		  image_info_size, image_info,
@@ -373,12 +409,42 @@ efi_status_t EFIAPI efi_firmware_raw_get_image_info(
 	     !descriptor_size || !package_version || !package_version_name))
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
 
+	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+		/*
+		 * Read the ImageType GUID values. Populate the guid array
+		 * with thesevalues. These are the values to be used in the
+		 * capsule for ImageTypeId.
+		 */
+		status = fwu_fill_partition_guid_array(&part_guid_arr,
+						       &nparts);
+		if (status < 0) {
+			log_err("Unable to get partiion guid's\n");
+			if (status == -EIO)
+				ret = EFI_DEVICE_ERROR;
+			else if (status == -ENOMEM)
+				ret = EFI_OUT_OF_RESOURCES;
+			goto out;
+		}
+	} else {
+		part_guid_arr = malloc(sizeof(efi_guid_t));
+		if (!part_guid_arr) {
+			log_err("Unable to allocate memory for guid array\n");
+			ret = EFI_OUT_OF_RESOURCES;
+			goto out;
+		}
+
+		guidcpy(part_guid_arr, &efi_firmware_image_type_uboot_raw);
+		nparts = 1;
+	}
+
 	ret = efi_get_dfu_info(image_info_size, image_info,
 			       descriptor_version, descriptor_count,
 			       descriptor_size,
 			       package_version, package_version_name,
-			       &efi_firmware_image_type_uboot_raw);
+			       part_guid_arr, nparts);
 
+out:
+	free(part_guid_arr);
 	return EFI_EXIT(ret);
 }
 
-- 
2.17.1


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

* [RESEND RFC PATCH 08/10] FWU: Add boot time checks as highlighted by the FWU specification
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
                   ` (6 preceding siblings ...)
  2021-11-25  7:12 ` [RESEND RFC PATCH 07/10] EFI: FMP: Add provision to update image's ImageTypeId in image descriptor Sughosh Ganu
@ 2021-11-25  7:13 ` Sughosh Ganu
  2021-11-25  7:13 ` [RESEND RFC PATCH 09/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:13 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu,
	Sughosh Ganu

The FWU Multi Bank Update specification requires the Update Agent to
carry out certain checks at the time of platform boot. The Update
Agent is the component which is responsible for updating the firmware
components and maintaining and keeping the metadata in sync.

The spec requires that the Update Agent perform the following checks
at the time of boot
* Sanity check of both the metadata copies maintained by the platform.
* Get the boot index passed to U-Boot by the prior stage bootloader
  and use this value for metadata bookkeeping.
* Check if the system is booting in Trial State. If the system boots
  in the Trial State for more than a specified number of boot counts,
  change the Active Bank to be booting the platform from.

Add these checks in the board initialisation sequence, invoked after
relocation.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 common/board_r.c       |   6 ++
 include/fwu_metadata.h |   1 +
 lib/fwu_updates/fwu.c  | 143 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 150 insertions(+)
 create mode 100644 lib/fwu_updates/fwu.c

diff --git a/common/board_r.c b/common/board_r.c
index 31a59c585a..01ccce2cca 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -78,6 +78,9 @@
 #ifdef CONFIG_EFI_SETUP_EARLY
 #include <efi_loader.h>
 #endif
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE
+#include <fwu_metadata.h>
+#endif
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -805,6 +808,9 @@ static init_fnc_t init_sequence_r[] = {
 #endif
 #ifdef CONFIG_EFI_SETUP_EARLY
 	(init_fnc_t)efi_init_obj_list,
+#endif
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE
+	fwu_boottime_checks,
 #endif
 	run_main_loop,
 };
diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
index 44f06f4c6a..02897f33a8 100644
--- a/include/fwu_metadata.h
+++ b/include/fwu_metadata.h
@@ -125,5 +125,6 @@ int fwu_get_metadata(struct fwu_metadata **metadata);
 int fwu_plat_get_update_index(u32 *update_idx);
 int fwu_plat_get_blk_desc(struct blk_desc **desc);
 void fwu_plat_get_bootidx(void *boot_idx);
+int fwu_boottime_checks(void);
 
 #endif /* _FWU_METADATA_H_ */
diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c
new file mode 100644
index 0000000000..2e1904b912
--- /dev/null
+++ b/lib/fwu_updates/fwu.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#include <efi.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <fwu_metadata.h>
+#include <malloc.h>
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+static int fwu_trial_state_check(void)
+{
+	int ret, i;
+	u8 trial_state;
+	efi_status_t status;
+	efi_uintn_t var_size;
+	u16 trial_state_ctr;
+	u32 nimages, active_bank, var_attributes, active_idx;
+	struct fwu_metadata *metadata;
+	struct fwu_image_entry *img_entry;
+	struct fwu_image_bank_info *img_bank_info;
+
+	ret = fwu_get_metadata(&metadata);
+	if (ret < 0)
+		return ret;
+
+	trial_state = ret = 0;
+	nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
+	active_bank = metadata->active_index;
+	img_entry = &metadata->img_entry[0];
+	for (i = 0; i < nimages; i++) {
+		img_bank_info = &img_entry[i].img_bank_info[active_bank];
+		if (!img_bank_info->accepted) {
+			trial_state = 1;
+			break;
+		}
+	}
+
+	if (trial_state) {
+		var_size = (efi_uintn_t)sizeof(trial_state_ctr);
+		log_info("System booting in Trial State\n");
+		var_attributes = EFI_VARIABLE_NON_VOLATILE |
+			EFI_VARIABLE_BOOTSERVICE_ACCESS |
+			EFI_VARIABLE_RUNTIME_ACCESS,
+		status = efi_get_variable_int(L"TrialStateCtr",
+					      &efi_global_variable_guid,
+					      &var_attributes,
+					      &var_size, &trial_state_ctr,
+					      NULL);
+		if (status != EFI_SUCCESS) {
+			log_err("Unable to read TrialStateCtr variable\n");
+			ret = -1;
+			goto out;
+		}
+
+		++trial_state_ctr;
+		if (trial_state_ctr > CONFIG_FWU_TRIAL_STATE_CNT) {
+			log_info("Trial State count exceeded. Revert back to previous_active_index\n");
+			trial_state_ctr = 0;
+			status = efi_set_variable_int(L"TrialStateCtr",
+						      &efi_global_variable_guid,
+						      var_attributes,
+						      var_size,
+						      &trial_state_ctr, false);
+			if (status != EFI_SUCCESS) {
+				log_err("Unable to clear TrialStateCtr variable\n");
+				ret = -1;
+				goto out;
+			}
+
+			active_idx = metadata->active_index;
+			ret = fwu_revert_boot_index(&active_idx);
+			if (ret < 0) {
+				log_err("Unable to revert active_index\n");
+				goto out;
+			}
+		} else {
+			status = efi_set_variable_int(L"TrialStateCtr",
+						      &efi_global_variable_guid,
+						      var_attributes,
+						      var_size,
+						      &trial_state_ctr, false);
+			if (status != EFI_SUCCESS) {
+				log_err("Unable to increment TrialStateCtr variable\n");
+				ret = -1;
+				goto out;
+			} else {
+				ret = 0;
+			}
+		}
+	}
+
+out:
+	free(metadata);
+	return ret;
+}
+
+int fwu_boottime_checks(void)
+{
+	int ret;
+	u32 boot_idx, active_idx;
+
+	ret = fwu_metadata_check();
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Get the Boot Index, i.e. the bank from
+	 * which the platform has booted. This value
+	 * gets passed from the ealier stage bootloader
+	 * which booted u-boot, e.g. tf-a. If the
+	 * boot index is not the same as the
+	 * active_index read from the metadata,
+	 * update the active_index.
+	 */
+	fwu_plat_get_bootidx(&boot_idx);
+	if (boot_idx >= CONFIG_FWU_NUM_BANKS)
+		return -EINVAL;
+
+	ret = fwu_get_active_index(&active_idx);
+	if (ret < 0)
+		return ret;
+
+	if (boot_idx != active_idx) {
+		log_info("Boot idx %u is not matching active idx %u, changing active_idx\n",
+			 boot_idx, active_idx);
+		ret = fwu_update_active_index(boot_idx);
+		if (ret < 0)
+			return ret;
+		else
+			return 0;
+	}
+
+	ret = fwu_trial_state_check();
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
-- 
2.17.1


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

* [RESEND RFC PATCH 09/10] FWU: Add support for FWU Multi Bank Update feature
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
                   ` (7 preceding siblings ...)
  2021-11-25  7:13 ` [RESEND RFC PATCH 08/10] FWU: Add boot time checks as highlighted by the FWU specification Sughosh Ganu
@ 2021-11-25  7:13 ` Sughosh Ganu
  2021-11-26 12:55   ` Heinrich Schuchardt
  2021-11-25  7:13 ` [RESEND RFC PATCH 10/10] FWU: cmd: Add a command to read metadata Sughosh Ganu
  2021-11-26 12:29 ` [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Heinrich Schuchardt
  10 siblings, 1 reply; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:13 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu,
	Sughosh Ganu

The FWU Multi Bank Update feature supports updation of firmware images
to one of multiple sets(also called banks) of images. The firmware
images are clubbed together in banks, with the system booting images
from the active bank. Information on the images such as which bank
they belong to is stored as part of the metadata structure, which is
stored on the same storage media as the firmware images on a dedicated
partition.

At the time of update, the metadata is read to identify the bank to
which the images need to be flashed(update bank). On a successful
update, the metadata is modified to set the updated bank as active
bank to subsequently boot from.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 include/fwu_metadata.h       |  10 ++
 lib/Kconfig                  |  32 ++++++
 lib/Makefile                 |   1 +
 lib/efi_loader/efi_capsule.c | 190 ++++++++++++++++++++++++++++++++++-
 lib/fwu_updates/Makefile     |  11 ++
 lib/fwu_updates/fwu.c        |  29 +++++-
 6 files changed, 269 insertions(+), 4 deletions(-)
 create mode 100644 lib/fwu_updates/Makefile

diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
index 02897f33a8..2d276a019a 100644
--- a/include/fwu_metadata.h
+++ b/include/fwu_metadata.h
@@ -106,7 +106,16 @@ struct fwu_metadata_ops {
 	EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \
 		 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
 
+#define FWU_OS_REQUEST_FW_REVERT_GUID \
+	EFI_GUID(0xacd58b4b, 0xc0e8, 0x475f, 0x99, 0xb5, \
+		 0x6b, 0x3f, 0x7e, 0x07, 0xaa, 0xf0)
+
+#define FWU_OS_REQUEST_FW_ACCEPT_GUID \
+	EFI_GUID(0x0c996046, 0xbcc0, 0x4d04, 0x85, 0xec, \
+		 0xe1, 0xfc, 0xed, 0xf1, 0xc6, 0xf8)
+
 #define FWU_METADATA_VERSION	0x1
+#define FWU_IMAGE_ACCEPTED	0x1
 
 extern struct fwu_metadata_ops fwu_gpt_blk_ops;
 
@@ -126,5 +135,6 @@ int fwu_plat_get_update_index(u32 *update_idx);
 int fwu_plat_get_blk_desc(struct blk_desc **desc);
 void fwu_plat_get_bootidx(void *boot_idx);
 int fwu_boottime_checks(void);
+int fwu_trial_state_ctr_start(void);
 
 #endif /* _FWU_METADATA_H_ */
diff --git a/lib/Kconfig b/lib/Kconfig
index 807a4c6ade..7cb306317c 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -835,3 +835,35 @@ config PHANDLE_CHECK_SEQ
 	  When there are multiple device tree nodes with same name,
           enable this config option to distinguish them using
 	  phandles in fdtdec_get_alias_seq() function.
+
+config FWU_MULTI_BANK_UPDATE
+	bool "Enable FWU Multi Bank Update Feature"
+	depends on EFI_HAVE_CAPSULE_SUPPORT
+	select PARTITION_TYPE_GUID
+	select EFI_SETUP_EARLY
+	help
+	  Feature for updating firmware images on platforms having
+	  multiple banks(copies) of the firmware images. One of the
+	  bank is selected for updating all the firmware components
+
+config FWU_NUM_BANKS
+	int "Number of Banks defined by the platform"
+	depends on FWU_MULTI_BANK_UPDATE
+	help
+	  Define the number of banks of firmware images on a platform
+
+config FWU_NUM_IMAGES_PER_BANK
+	int "Number of firmware images per bank"
+	depends on FWU_MULTI_BANK_UPDATE
+	help
+	  Define the number of firmware images per bank. This value
+	  should be the same for all the banks.
+
+config FWU_TRIAL_STATE_CNT
+	int "Number of times system boots in Trial State"
+	depends on FWU_MULTI_BANK_UPDATE
+	default 3
+	help
+	  With FWU Multi Bank Update feature enabled, number of times
+	  the platform is allowed to boot in Trial State after an
+	  update.
diff --git a/lib/Makefile b/lib/Makefile
index 5ddbc77ed6..bc5c1e22fc 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/
 obj-$(CONFIG_EFI_LOADER) += efi_driver/
 obj-$(CONFIG_EFI_LOADER) += efi_loader/
 obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/
 obj-$(CONFIG_LZMA) += lzma/
 obj-$(CONFIG_BZIP2) += bzip2/
 obj-$(CONFIG_TIZEN) += tizen/
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index 502bcfca6e..40b9e87e92 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -14,6 +14,7 @@
 #include <env.h>
 #include <fdtdec.h>
 #include <fs.h>
+#include <fwu_metadata.h>
 #include <malloc.h>
 #include <mapmem.h>
 #include <sort.h>
@@ -30,6 +31,13 @@ static const efi_guid_t efi_guid_firmware_management_capsule_id =
 		EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
 const efi_guid_t efi_guid_firmware_management_protocol =
 		EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
+const efi_guid_t fwu_guid_os_request_fw_revert =
+		FWU_OS_REQUEST_FW_REVERT_GUID;
+const efi_guid_t fwu_guid_os_request_fw_accept =
+		FWU_OS_REQUEST_FW_ACCEPT_GUID;
+
+__maybe_unused static u32 update_index;
+__maybe_unused static bool capsule_update;
 
 #ifdef CONFIG_EFI_CAPSULE_ON_DISK
 /* for file system access */
@@ -403,10 +411,13 @@ static efi_status_t efi_capsule_update_firmware(
 	void *image_binary, *vendor_code;
 	efi_handle_t *handles;
 	efi_uintn_t no_handles;
-	int item;
+	int item, alt_no;
 	struct efi_firmware_management_protocol *fmp;
 	u16 *abort_reason;
+	efi_guid_t image_type_id;
 	efi_status_t ret = EFI_SUCCESS;
+	int status;
+	u8 image_index;
 
 	/* sanity check */
 	if (capsule_data->header_size < sizeof(*capsule) ||
@@ -481,8 +492,36 @@ static efi_status_t efi_capsule_update_firmware(
 				goto out;
 		}
 
+		if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+			/*
+			 * Based on the value of update_image_type_id,
+			 * derive the alt number value. This will be
+			 * passed as update_image_index to the
+			 * set_image function.
+			 */
+			image_type_id = image->update_image_type_id;
+			status = fwu_get_image_alt_num(image_type_id,
+						       update_index,
+						       &alt_no);
+			if (status < 0) {
+				log_err("Unable to get the alt no for the image type %pUl\n",
+					&image_type_id);
+				if (status == -ENODEV || status == -EIO)
+					ret = EFI_DEVICE_ERROR;
+				else if (status == -ENOMEM)
+					ret = EFI_OUT_OF_RESOURCES;
+				else if (status == -ERANGE || status == -EINVAL)
+					ret = EFI_INVALID_PARAMETER;
+				goto out;
+			}
+			log_debug("alt_no %u for Image Type Id %pUl\n",
+				  alt_no, &image_type_id);
+			image_index = alt_no;
+		} else {
+			image_index = image->update_image_index;
+		}
 		abort_reason = NULL;
-		ret = EFI_CALL(fmp->set_image(fmp, image->update_image_index,
+		ret = EFI_CALL(fmp->set_image(fmp, image_index,
 					      image_binary,
 					      image_binary_size,
 					      vendor_code, NULL,
@@ -493,6 +532,24 @@ static efi_status_t efi_capsule_update_firmware(
 			efi_free_pool(abort_reason);
 			goto out;
 		}
+
+		if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+			status = fwu_clear_accept_image(&image_type_id,
+							update_index);
+			if (status < 0) {
+				log_err("Unable to clear the accept bit for the image %pUl\n",
+					&image_type_id);
+				if (status == -ENODEV || status == -EIO)
+					ret = EFI_DEVICE_ERROR;
+				else if (status == -ENOMEM)
+					ret = EFI_OUT_OF_RESOURCES;
+				else if (status == -ERANGE || status == -EINVAL)
+					ret = EFI_INVALID_PARAMETER;
+				goto out;
+			}
+			log_debug("Cleared out accepted bit for Image %pUl\n", &image_type_id);
+		}
+
 	}
 
 out:
@@ -527,6 +584,9 @@ efi_status_t EFIAPI efi_update_capsule(
 		u64 scatter_gather_list)
 {
 	struct efi_capsule_header *capsule;
+	efi_guid_t *image_guid;
+	u32 active_idx;
+	int status;
 	unsigned int i;
 	efi_status_t ret;
 
@@ -538,6 +598,16 @@ efi_status_t EFIAPI efi_update_capsule(
 		goto out;
 	}
 
+	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+		/* First obtain the update_index from the platform */
+		status = fwu_plat_get_update_index(&update_index);
+		if (status < 0) {
+			log_err("Failed to get the FWU update_index value\n");
+			ret = EFI_DEVICE_ERROR;
+			goto out;
+		}
+	}
+
 	ret = EFI_SUCCESS;
 	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
 	     i++, capsule = *(++capsule_header_array)) {
@@ -553,6 +623,55 @@ efi_status_t EFIAPI efi_update_capsule(
 		if (!guidcmp(&capsule->capsule_guid,
 			     &efi_guid_firmware_management_capsule_id)) {
 			ret  = efi_capsule_update_firmware(capsule);
+			capsule_update = true;
+		} else if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+			capsule_update = false;
+			if (!guidcmp(&capsule->capsule_guid,
+				     &fwu_guid_os_request_fw_revert)) {
+				/*
+				 * One of the previously updated image has
+				 * failed the OS acceptance test. OS has
+				 * requested to revert back to the earlier
+				 * boot index
+				 */
+				status = fwu_revert_boot_index(&active_idx);
+				if (status < 0) {
+					log_err("Failed to revert the FWU boot index\n");
+					if (status == -ENODEV ||
+					    status == -ERANGE ||
+					    status == -EIO)
+						ret = EFI_DEVICE_ERROR;
+					else if (status == -EINVAL)
+						ret = EFI_INVALID_PARAMETER;
+					else if (status == -ENOMEM)
+						ret = EFI_OUT_OF_RESOURCES;
+				} else {
+					ret = EFI_SUCCESS;
+					log_err("Reverted the FWU active_index to %u. Recommend rebooting the system\n",
+						active_idx);
+				}
+			} else if (!guidcmp(&capsule->capsule_guid,
+					    &fwu_guid_os_request_fw_accept)) {
+				/*
+				 * Image accepted by the OS. Set the acceptance
+				 * status for the image.
+				 */
+				image_guid = (void *)(char *)capsule +
+					capsule->header_size;
+				status = fwu_accept_image(image_guid);
+				if (status < 0) {
+					if (status == -ENODEV ||
+					    status == -ERANGE ||
+					    status == -EIO)
+						ret = EFI_DEVICE_ERROR;
+					else if (status == -EINVAL)
+						ret = EFI_INVALID_PARAMETER;
+					else if (status == -ENOMEM)
+						ret = EFI_OUT_OF_RESOURCES;
+				} else {
+					ret = EFI_SUCCESS;
+				}
+			}
 		} else {
 			log_err("Unsupported capsule type: %pUl\n",
 				&capsule->capsule_guid);
@@ -563,6 +682,36 @@ efi_status_t EFIAPI efi_update_capsule(
 			goto out;
 	}
 
+	/*
+	 * Update the metadata once all the capsules have
+	 * been updated. This is done only for the Runtime
+	 * capsule update service.
+	 * The update_index value now gets written to the
+	 * active_index and the update_index value also
+	 * gets updated.
+	 * For the capsule-on-disk feature, the updation
+	 * of the metadata happens in efi_launch_capsules
+	 */
+	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE) &&
+	    !IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK)) {
+		status = fwu_update_active_index(update_index);
+		if (status < 0) {
+			log_err("Failed to update FWU metadata index values\n");
+			if (status == -EINVAL || status == -ERANGE)
+				ret = EFI_INVALID_PARAMETER;
+			else if (status == -ENODEV || status == -EIO)
+				ret = EFI_DEVICE_ERROR;
+			else if (status == -ENOMEM)
+				ret = EFI_OUT_OF_RESOURCES;
+		} else {
+			status = fwu_trial_state_ctr_start();
+			if (status < 0)
+				ret = EFI_DEVICE_ERROR;
+			else
+				ret = EFI_SUCCESS;
+		}
+	}
+
 	if (IS_ENABLED(CONFIG_EFI_ESRT)) {
 		/* Rebuild the ESRT to reflect any updated FW images. */
 		ret = efi_esrt_populate();
@@ -1075,8 +1224,10 @@ efi_status_t efi_launch_capsules(void)
 {
 	struct efi_capsule_header *capsule = NULL;
 	u16 **files;
+	int status;
 	unsigned int nfiles, index, i;
 	efi_status_t ret;
+	bool update_status = true;
 
 	if (!check_run_capsules())
 		return EFI_SUCCESS;
@@ -1104,9 +1255,11 @@ efi_status_t efi_launch_capsules(void)
 		ret = efi_capsule_read_file(files[i], &capsule);
 		if (ret == EFI_SUCCESS) {
 			ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0));
-			if (ret != EFI_SUCCESS)
+			if (ret != EFI_SUCCESS) {
 				log_err("Applying capsule %ls failed\n",
 					files[i]);
+				update_status = false;
+			}
 
 			/* create CapsuleXXXX */
 			set_capsule_result(index, capsule, ret);
@@ -1114,6 +1267,7 @@ efi_status_t efi_launch_capsules(void)
 			free(capsule);
 		} else {
 			log_err("Reading capsule %ls failed\n", files[i]);
+			update_status = false;
 		}
 		/* delete a capsule either in case of success or failure */
 		ret = efi_capsule_delete_file(files[i]);
@@ -1121,7 +1275,37 @@ efi_status_t efi_launch_capsules(void)
 			log_err("Deleting capsule %ls failed\n",
 				files[i]);
 	}
+
 	efi_capsule_scan_done();
+	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+		if (update_status == true && capsule_update == true) {
+			/*
+			 * All the capsules have been updated successfully,
+			 * update the FWU metadata.
+			 */
+			log_debug("Update Complete. Now updating active_index to %u\n",
+				  update_index);
+			status = fwu_update_active_index(update_index);
+			if (status < 0) {
+				log_err("Failed to update FWU metadata index values\n");
+				if (status == -EINVAL || status == -ERANGE)
+					ret = EFI_INVALID_PARAMETER;
+				else if (status == -ENODEV || status == -EIO)
+					ret = EFI_DEVICE_ERROR;
+				else if (status == -ENOMEM)
+					ret = EFI_OUT_OF_RESOURCES;
+			} else {
+				log_debug("Successfully updated the active_index\n");
+				status = fwu_trial_state_ctr_start();
+				if (status < 0)
+					ret = EFI_DEVICE_ERROR;
+				else
+					ret = EFI_SUCCESS;
+			}
+		} else if (capsule_update == true && update_status == false) {
+			log_err("All capsules were not updated. Not updating metadata\n");
+		}
+	}
 
 	for (i = 0; i < nfiles; i++)
 		free(files[i]);
diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile
new file mode 100644
index 0000000000..f6fa8290c1
--- /dev/null
+++ b/lib/fwu_updates/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2021, Linaro Limited
+#
+
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_metadata.o
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o
+
+ifdef CONFIG_EFI_PARTITION
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_metadata_gpt_blk.o
+endif
diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c
index 2e1904b912..bbd3dc249f 100644
--- a/lib/fwu_updates/fwu.c
+++ b/lib/fwu_updates/fwu.c
@@ -45,7 +45,7 @@ static int fwu_trial_state_check(void)
 		log_info("System booting in Trial State\n");
 		var_attributes = EFI_VARIABLE_NON_VOLATILE |
 			EFI_VARIABLE_BOOTSERVICE_ACCESS |
-			EFI_VARIABLE_RUNTIME_ACCESS,
+			EFI_VARIABLE_RUNTIME_ACCESS;
 		status = efi_get_variable_int(L"TrialStateCtr",
 					      &efi_global_variable_guid,
 					      &var_attributes,
@@ -99,6 +99,33 @@ out:
 	return ret;
 }
 
+int fwu_trial_state_ctr_start(void)
+{
+	int ret;
+	u32 var_attributes;
+	efi_status_t status;
+	efi_uintn_t var_size;
+	u16 trial_state_ctr;
+
+	var_size = (efi_uintn_t)sizeof(trial_state_ctr);
+	var_attributes = EFI_VARIABLE_NON_VOLATILE |
+		EFI_VARIABLE_BOOTSERVICE_ACCESS |
+		EFI_VARIABLE_RUNTIME_ACCESS;
+
+	trial_state_ctr = ret = 0;
+	status = efi_set_variable_int(L"TrialStateCtr",
+				      &efi_global_variable_guid,
+				      var_attributes,
+				      var_size,
+				      &trial_state_ctr, false);
+	if (status != EFI_SUCCESS) {
+		log_err("Unable to increment TrialStateCtr variable\n");
+		ret = -1;
+	}
+
+	return ret;
+}
+
 int fwu_boottime_checks(void)
 {
 	int ret;
-- 
2.17.1


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

* [RESEND RFC PATCH 10/10] FWU: cmd: Add a command to read metadata
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
                   ` (8 preceding siblings ...)
  2021-11-25  7:13 ` [RESEND RFC PATCH 09/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
@ 2021-11-25  7:13 ` Sughosh Ganu
  2021-11-26 12:29 ` [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Heinrich Schuchardt
  10 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-25  7:13 UTC (permalink / raw)
  To: u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu,
	Sughosh Ganu

Add a command to read the metadata as specified in the FWU
specification and print the fields of the metadata.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 cmd/Kconfig        |  6 +++++
 cmd/Makefile       |  1 +
 cmd/fwu_metadata.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+)
 create mode 100644 cmd/fwu_metadata.c

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 5b30b13e43..1bf590caf0 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -137,6 +137,12 @@ config CMD_CPU
 	  internal name) and clock frequency. Other information may be
 	  available depending on the CPU driver.
 
+config CMD_FWU_METADATA
+	bool "fwu metadata read"
+	default y if FWU_MULTI_BANK_UPDATE
+	help
+	  Command to read the metadata and dump it's contents
+
 config CMD_LICENSE
 	bool "license"
 	select BUILD_BIN2C
diff --git a/cmd/Makefile b/cmd/Makefile
index 891819ae0f..9dc05dd719 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_CMD_FPGA) += fpga.o
 obj-$(CONFIG_CMD_FPGAD) += fpgad.o
 obj-$(CONFIG_CMD_FS_GENERIC) += fs.o
 obj-$(CONFIG_CMD_FUSE) += fuse.o
+obj-$(CONFIG_CMD_FWU_METADATA) += fwu_metadata.o
 obj-$(CONFIG_CMD_GETTIME) += gettime.o
 obj-$(CONFIG_CMD_GPIO) += gpio.o
 obj-$(CONFIG_CMD_HVC) += smccc.o
diff --git a/cmd/fwu_metadata.c b/cmd/fwu_metadata.c
new file mode 100644
index 0000000000..3908b60dea
--- /dev/null
+++ b/cmd/fwu_metadata.c
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#include <command.h>
+#include <fwu_metadata.h>
+#include <stdlib.h>
+
+#include <linux/types.h>
+
+static void print_metadata(struct fwu_metadata *metadata)
+{
+	int i, j;
+	struct fwu_image_entry *img_entry;
+	struct fwu_image_bank_info *img_info;
+	u32 nimages, nbanks;
+
+	printf("\tMetadata Read\n");
+	printf("crc32: %#x\n", metadata->crc32);
+	printf("version: %#x\n", metadata->version);
+	printf("active_index: %#x\n", metadata->active_index);
+	printf("previous_active_index: %#x\n", metadata->previous_active_index);
+
+	nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
+	nbanks = CONFIG_FWU_NUM_BANKS;
+	printf("\tImage Info\n");
+	for (i = 0; i < nimages; i++) {
+		img_entry = &metadata->img_entry[i];
+		printf("\nImage Type Guid: %pUL\n", &img_entry->image_type_uuid);
+		printf("Location Guid: %pUL\n", &img_entry->location_uuid);
+		for (j = 0; j < nbanks; j++) {
+			img_info = &img_entry->img_bank_info[j];
+			printf("Image Guid:  %pUL\n", &img_info->image_uuid);
+			printf("Image Acceptance: %#x\n", img_info->accepted);
+		}
+	}
+}
+
+int do_metadata_read(struct cmd_tbl *cmdtp, int flag,
+		     int argc, char * const argv[])
+{
+	int ret = CMD_RET_SUCCESS;
+	struct fwu_metadata *metadata;
+
+	printf("Trying to get the metadata\n");
+	ret = fwu_get_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to get valid metadata\n");
+		ret = CMD_RET_FAILURE;
+		goto out;
+	}
+
+	print_metadata(metadata);
+
+out:
+	free(metadata);
+	return ret;
+}
+
+U_BOOT_CMD(
+	metadata_read,	1,	1,	do_metadata_read,
+	"Read and print FWU metadata",
+	""
+);
-- 
2.17.1


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

* Re: [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata
  2021-11-25  7:12 ` [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata Sughosh Ganu
@ 2021-11-26 11:35   ` Ilias Apalodimas
  2021-11-29  6:38     ` Sughosh Ganu
  2021-11-30 12:57   ` Heinrich Schuchardt
  2021-12-01  6:26   ` Simon Glass
  2 siblings, 1 reply; 36+ messages in thread
From: Ilias Apalodimas @ 2021-11-26 11:35 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Jose Marinho, Grant Likely, Jason Liu

Hi Sughosh, 

On Thu, Nov 25, 2021 at 12:42:55PM +0530, Sughosh Ganu wrote:
> In the FWU Multi Bank Update feature, the information about the
> updatable images is stored as part of the metadata, which is stored on
> a dedicated partition. Add the metadata structure, and functions to
> access the metadata. These are generic API's, and implementations can
> be added based on parameters like how the metadata partition is
> accessed and what type of storage device houses the metadata.
> 
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>  include/fwu_metadata.h         | 125 +++++++++++++++
>  lib/fwu_updates/fwu_metadata.c | 275 +++++++++++++++++++++++++++++++++
>  2 files changed, 400 insertions(+)
>  create mode 100644 include/fwu_metadata.h
>  create mode 100644 lib/fwu_updates/fwu_metadata.c
> 
> diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
> new file mode 100644
> index 0000000000..e692ef7506
> --- /dev/null
> +++ b/include/fwu_metadata.h
> @@ -0,0 +1,125 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (c) 2021, Linaro Limited

Please add a link here to the arm spec that describes the metadata etc

> + */
> +
> +#if !defined _FWU_METADATA_H_
> +#define _FWU_METADATA_H_
> +
> +#include <blk.h>
> +#include <efi.h>
> +#include <uuid.h>
> +
> +#include <linux/types.h>
> +
> +/**
> + * struct fwu_image_bank_info - firmware image information
> + * @image_uuid: Guid value of the image in this bank
> + * @accepted: Acceptance status of the image
> + * @reserved: Reserved
> + *
> + * The structure contains image specific fields which are
> + * used to identify the image and to specify the image's
> + * acceptance status
> + */
> +struct fwu_image_bank_info {
> +	efi_guid_t  image_uuid;
> +	u32 accepted;
> +	u32 reserved;
> +};

fwu_image_bank_info -> fwu_img_bank_info

> +
> +/**
> + * struct fwu_image_entry - information for a particular type of image
> + * @image_type_uuid: Guid value for identifying the image type
> + * @location_uuid: Guid of the storage volume where the image is located

/s/Guid/GUID

> + * @img_bank_info: Array containing properties of images
> + *
> + * This structure contains information on various types of updatable
> + * firmware images. Each image type then contains an array of image
> + * information per bank.
> + */
> +struct fwu_image_entry {
> +	efi_guid_t image_type_uuid;
> +	efi_guid_t location_uuid;
> +	struct fwu_image_bank_info img_bank_info[CONFIG_FWU_NUM_BANKS];
> +};
> +

It seems like you've followed the naming proposed in the spec,  which makes
reading spec -- code easier.  However I feel we should add a few more
comments on the naming to make reading easier or change the naming and
mention the original name in comments.

A 'bank' is supposed to contain:
bank[0]: Uboot(0), TF-A(0) etc
bank[1]: Uboot(1), TF-A(1) etc
However there's no structure that defines an entire bank.  Instead the bank
information is constructed by reading the metadata and fixing it up on
the fly.

fwu_image_bank_info -- Information for a specific image (e.g OP-TEE,
U-Boot, TF-A, whatever) but not within a *bank*.  That's amongst a
collection of images of the same type.

IOW img_bank_info looks like:
img_bank_info[0] -> U-Boot(0), U-Boot(1) etc
img_bank_info[1] -> TF-A(0), TF-A(1) etc

@Jose can we tweak the spec naming a bit to be more intuitive?
I am terrible at naming stuff but what about:
fwu_image_bank_info -> fwu_img_repo_info, fwu_img_vault_info, 
					   fwu_img_storage_info, fwu_img_array_info, 


> +/**
> + * struct fwu_metadata - Metadata structure for multi-bank updates
> + * @crc32: crc32 value for the metadata
> + * @version: Metadata version
> + * @active_index: Index of the bank currently used for booting images
> + * @previous_active_inde: Index of the bank used before the current bank
> + *                        being used for booting
> + * @img_entry: Array of information on various firmware images that can
> + *             be updated
> + *
> + * This structure is used to store all the needed information for performing
> + * multi bank updates on the platform. This contains info on the bank being
> + * used to boot along with the information needed for identification of
> + * individual images
> + */
> +struct fwu_metadata {
> +	u32 crc32;
> +	u32 version;
> +	u32 active_index;
> +	u32 previous_active_index;
> +
> +	struct fwu_image_entry img_entry[CONFIG_FWU_NUM_IMAGES_PER_BANK];
> +};
> +
> +/**
> + * @get_active_index: get the current active_index value
> + * @update_active_index: update the active_index value
> + * @fill_partition_guid_array: fill the array with guid values of the
> + *                             partitions found on the storage media
> + * @get_image_alt_num: get the alt number to be used for the image
> + * @metadata_check: check the validity of the metadata partitions
> + * @revert_boot_index: set the active_index to previous_active_index
> + * @set_accept_image: set the accepted bit for the image
> + * @clear_accept_image: clear the accepted bit for the image
> + * @get_metadata() - Get a metadata copy
> + */
> +struct fwu_metadata_ops {
> +	int (*get_active_index)(u32 *active_idx);
> +
> +	int (*update_active_index)(u32 active_idx);
> +
> +	int (*fill_partition_guid_array)(efi_guid_t **part_guid_arr,
> +					 u32 *nparts);
> +
> +	int (*get_image_alt_num)(efi_guid_t image_type_id, u32 update_bank,
> +				 int *alt_num);
> +
> +	int (*metadata_check)(void);
> +
> +	int (*revert_boot_index)(u32 *active_idx);
> +
> +	int (*set_accept_image)(efi_guid_t *img_type_id);
> +
> +	int (*clear_accept_image)(efi_guid_t *img_type_id, u32 bank);
> +
> +	int (*get_metadata)(struct fwu_metadata **metadata);
> +};
> +
> +#define FWU_METADATA_GUID \
> +	EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \
> +		 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
> +
> +#define FWU_METADATA_VERSION	0x1
> +
> +extern struct fwu_metadata_ops fwu_gpt_blk_ops;
> +
> +struct fwu_metadata_ops *get_plat_fwu_metadata_ops(void);
> +int fwu_get_active_index(u32 *active_idx);
> +int fwu_update_active_index(u32 active_idx);
> +int fwu_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32 *nparts);
> +int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> +			  int *alt_num);
> +int fwu_metadata_check(void);
> +int fwu_revert_boot_index(u32 *active_idx);
> +int fwu_accept_image(efi_guid_t *img_type_id);
> +int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank);
> +int fwu_get_metadata(struct fwu_metadata **metadata);
> +
> +#endif /* _FWU_METADATA_H_ */
> diff --git a/lib/fwu_updates/fwu_metadata.c b/lib/fwu_updates/fwu_metadata.c
> new file mode 100644
> index 0000000000..ebc3eaa04a
> --- /dev/null
> +++ b/lib/fwu_updates/fwu_metadata.c
> @@ -0,0 +1,275 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (c) 2021, Linaro Limited
> + */
> +
> +#include <fwu_metadata.h>
> +
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +
> +static inline struct fwu_metadata_ops *get_fwu_metadata_ops(void)
> +{
> +	return get_plat_fwu_metadata_ops();
> +}
> +
> +/**
> + * fwu_get_active_index() - Get active_index from the metadata
> + * @active_idx: active_index value to be read
> + *
> + * Read the active_index field from the metadata and place it in
> + * the variable pointed to be the function argument.
> + *
> + * Return: 0 if OK, -ve on error

-ve ?

> + *
> + */
> +int fwu_get_active_index(u32 *active_idx)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();

On all callsites of get_fwu_metadata_ops() do we need to be that verbose on
the ops missing?  If not we can just squeeze in the if
(!ops->XXXXXX) check in get_fwu_metadata_ops() and simply return an error
there.

> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_update_active_index(u32 active_idx)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->update_active_index) {
> +		log_err("update_active_index() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->update_active_index(active_idx);
> +}
> +
> +/**
> + * fwu_fill_partition_guid_array() - Fill the part_guid_arr array with the guid's of
> + *                                   the partitions
> + * @part_guid_arr: array of partition guid's
> + * @nparts: Number of gpt partitions on the device
> + *
> + * Get the information on the partition guid's, filling the array with the guid
> + * values and also the number of partitions.
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32 *nparts)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->fill_partition_guid_array) {
> +		log_err("fill_partition_guid_array() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->fill_partition_guid_array(part_guid_arr, nparts);
> +}
> +
> +/**
> + * fwu_get_image_alt_num() - Get the dfu alt number to be used for capsule update
> + * @image_type_id: image guid as passed in the capsule
> + * @update_bank: Bank to which the update is to be made
> + * @alt_num: The alt_num for the image
> + *
> + * Based on the guid value passed in the capsule, along with the bank to which the
> + * image needs to be updated, get the dfu alt number which will be used for the
> + * capsule update
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> +			  int *alt_num)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->get_image_alt_num) {
> +		log_err("get_image_alt_num() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->get_image_alt_num(image_type_id, update_bank, alt_num);
> +}
> +
> +/**
> + * fwu_metadata_check() - Check if the metadata is valid
> + *
> + * Validate both copies of metadata. If one of the copies
> + * has gone bad, restore it from the other bad copy.
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_metadata_check(void)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->metadata_check) {
> +		log_err("metadata_check() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->metadata_check();
> +}
> +
> +/**
> + * fwu_revert_boot_index() - Revert the active index in the metadata
> + * @active_idx: Value of the updated active_index
> + *
> + * Revert the active_index value in the metadata, by swapping the values
> + * of active_index and previous_active_index in both copies of the
> + * metadata.
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_revert_boot_index(u32 *active_idx)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->revert_boot_index) {
> +		log_err("revert_boot_index() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->revert_boot_index(active_idx);
> +}
> +
> +/**
> + * fwu_accept_image() - Set the Acceptance bit for the image
> + * @img_type_id: Guid of the image type for which the accepted bit is to be
> + *               cleared
> + *
> + * Set the accepted bit for the image specified by the img_guid parameter. This
> + * indicates acceptance of image for subsequent boots by some governing component
> + * like OS(or firmware).
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_accept_image(efi_guid_t *img_type_id)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->set_accept_image) {
> +		log_err("set_accept_image() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->set_accept_image(img_type_id);
> +}
> +
> +/**
> + * fwu_clear_accept_image() - Clear the Acceptance bit for the image
> + * @img_type_id: Guid of the image type for which the accepted bit is to be
> + *               cleared
> + *
> + * Clear the accepted bit for the image type specified by the img_type_id parameter.
> + * This function is called after the image has been updated. The accepted bit is
> + * cleared to be set subsequently after passing the image acceptance criteria, by
> + * either the OS(or firmware)
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->clear_accept_image) {
> +		log_err("clear_accept_image() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->clear_accept_image(img_type_id, bank);
> +}
> +
> +/**
> + * fwu_get_metadata() - Get a metadata copy
> + * @metadata: Copy of the metadata
> + *
> + * Get a valid copy of the metadata.
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_get_metadata(struct fwu_metadata **metadata)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->get_metadata) {
> +		log_err("get_metadata() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->get_metadata(metadata);
> +}
> -- 
> 2.17.1
> 


Cheers
/Ilias

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

* Re: [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature
  2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
                   ` (9 preceding siblings ...)
  2021-11-25  7:13 ` [RESEND RFC PATCH 10/10] FWU: cmd: Add a command to read metadata Sughosh Ganu
@ 2021-11-26 12:29 ` Heinrich Schuchardt
  2021-11-26 12:48   ` Ilias Apalodimas
  10 siblings, 1 reply; 36+ messages in thread
From: Heinrich Schuchardt @ 2021-11-26 12:29 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: Patrick Delaunay, Patrice Chotard, Alexander Graf, Simon Glass,
	Bin Meng, Peng Fan, AKASHI Takahiro, Ilias Apalodimas,
	Jose Marinho, Grant Likely, Jason Liu, u-boot

On 11/25/21 08:12, Sughosh Ganu wrote:
> (resending to including the first paragraph which got deleted for some
> reason).
>
> The patchset adds support for the FWU Multi Bank Update[1]

The patch set is lacking a documentation update
The patch set is lacking an integration test.

Please, add both to enable reviewing.

Best regards

Heinrich

> feature. Certain aspects of the Dependable Boot[2] specification have
> also been implemented.
>
> The FWU multi bank update feature is used for supporting multiple
> sets(also called banks) of firmware image(s), allowing the platform to
> boot from a different bank, in case it fails to boot from the active
> bank. This functionality is supported by keeping the relevant
> information in a structure called metadata, which provides information
> on the images. Among other parameters, the metadata structure contains
> information on the currect active bank that is being used to boot
> image(s).
>
> Functionality is being added to work with the UEFI capsule driver in
> u-boot. The metadata is read to gather information on the update bank,
> which is the bank to which the firmware images would be flashed to. On
> a successful completion of the update of all components, the active
> bank field in the metadata is updated, to reflect the bank from which
> the platform will boot on the subsequent boots.
>
> Currently, the feature is being enabled on the STM32MP157C-DK2
> board which boots a FIP image from a uSD card partitioned with the GPT
> partioning scheme. This also requires changes in the previous stage of
> bootloader, which parses the metadata and selects the bank to boot the
> image(s) from. Support is being added in tf-a(BL2 stage) for the
> STM32MP157C-DK2 board to boot the active bank images. These changes
> are under review currently[3].
>
> Todo's
> ------
> 1) Add a test(selftest) for the metadata access.
> 2) Add a tool for generation of the metadata. Not sure if this needs to
>     be part of the u-boot repository though.
> 3) Add a tool for generation of the firmware accept/reject dummy
>     capsule. Need to check if this can be added to the mkeficapsule
>     tool in u-boot.
>
> [1] - https://developer.arm.com/documentation/den0118/a
> [2] - https://staging-git.codelinaro.org/linaro/firmware-dual-banked-updates/test
> [3] - https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/12566
>
> Sughosh Ganu (10):
>    GPT: Add function to get gpt header and partition entries
>    stm32mp: dfu: Move the ram partitions to the end of the dfu_alt_info
>      variable
>    FWU: Add metadata structure and functions for accessing metadata
>    FWU: Add metadata access functions for GPT partitioned block devices
>    FWU: stm32mp1: Add helper functions for accessing metadata
>    FWU: STM32MP1: Add support to read boot index from backup register
>    EFI: FMP: Add provision to update image's ImageTypeId in image
>      descriptor
>    FWU: Add boot time checks as highlighted by the FWU specification
>    FWU: Add support for FWU Multi Bank Update feature
>    FWU: cmd: Add a command to read metadata
>
>   arch/arm/mach-stm32mp/include/mach/stm32.h |   1 +
>   board/st/common/stm32mp_dfu.c              |  11 +-
>   board/st/stm32mp1/stm32mp1.c               |  70 ++
>   cmd/Kconfig                                |   6 +
>   cmd/Makefile                               |   1 +
>   cmd/fwu_metadata.c                         |  65 ++
>   common/board_r.c                           |   6 +
>   disk/part_efi.c                            |  10 +
>   include/fwu_metadata.h                     | 140 ++++
>   include/part.h                             |  14 +
>   lib/Kconfig                                |  32 +
>   lib/Makefile                               |   1 +
>   lib/efi_loader/efi_capsule.c               | 190 +++++-
>   lib/efi_loader/efi_firmware.c              |  76 ++-
>   lib/fwu_updates/Makefile                   |  11 +
>   lib/fwu_updates/fwu.c                      | 170 +++++
>   lib/fwu_updates/fwu_metadata.c             | 275 ++++++++
>   lib/fwu_updates/fwu_metadata_gpt_blk.c     | 716 +++++++++++++++++++++
>   18 files changed, 1784 insertions(+), 11 deletions(-)
>   create mode 100644 cmd/fwu_metadata.c
>   create mode 100644 include/fwu_metadata.h
>   create mode 100644 lib/fwu_updates/Makefile
>   create mode 100644 lib/fwu_updates/fwu.c
>   create mode 100644 lib/fwu_updates/fwu_metadata.c
>   create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
>


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

* Re: [RESEND RFC PATCH 07/10] EFI: FMP: Add provision to update image's ImageTypeId in image descriptor
  2021-11-25  7:12 ` [RESEND RFC PATCH 07/10] EFI: FMP: Add provision to update image's ImageTypeId in image descriptor Sughosh Ganu
@ 2021-11-26 12:43   ` Heinrich Schuchardt
  2021-11-29 11:38     ` Sughosh Ganu
  0 siblings, 1 reply; 36+ messages in thread
From: Heinrich Schuchardt @ 2021-11-26 12:43 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: Patrick Delaunay, Patrice Chotard, Alexander Graf, Simon Glass,
	Bin Meng, Peng Fan, AKASHI Takahiro, Ilias Apalodimas,
	Jose Marinho, Grant Likely, Jason Liu, u-boot

On 11/25/21 08:12, Sughosh Ganu wrote:
> The FWU Multi Banks Update feature allows updating different types of
> updatable firmware images on the platform. These image types are
> identified using the ImageTypeId GUID value. Add support in the
> GetImageInfo function of the FMP protocol to get the GUID values for
> the individual images and populate these in the image descriptor for
> the corresponding images.
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>   lib/efi_loader/efi_firmware.c | 76 ++++++++++++++++++++++++++++++++---
>   1 file changed, 71 insertions(+), 5 deletions(-)
>
> diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
> index a1b88dbfc2..a2b639b448 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 <fwu_metadata.h>
>   #include <image.h>
>   #include <signatures.h>
>
> @@ -106,7 +107,8 @@ efi_status_t EFIAPI efi_firmware_set_package_info_unsupported(
>    * @descriptor_size:		Pointer to descriptor size
>    * package_version:		Package version
>    * package_version_name:	Package version's name
> - * image_type:			Image type GUID
> + * guid_array:			Image type GUID array
> + * nparts:			Number of partions on the storage device
>    *
>    * Return information bout the current firmware image in @image_info.
>    * @image_info will consist of a number of descriptors.
> @@ -122,7 +124,7 @@ static efi_status_t efi_get_dfu_info(
>   	efi_uintn_t *descriptor_size,
>   	u32 *package_version,
>   	u16 **package_version_name,
> -	const efi_guid_t *image_type)
> +	const efi_guid_t *guid_array, u32 nparts)
>   {
>   	struct dfu_entity *dfu;
>   	size_t names_len, total_size;
> @@ -145,6 +147,19 @@ static efi_status_t efi_get_dfu_info(
>   		return EFI_SUCCESS;
>   	}
>
> +	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {

The concept of multiple banks is not related to the concept of multiple
GUIDs. So this if statement should be removed.

> +		/*
> +		 * For FWU multi bank updates, the number of partitions
> +		 * should at least be same as dfu partitions or less
> +		 */
> +		if (nparts > dfu_num) {
> +			log_err("Number of dfu alt no's less than partitions\n");
> +			dfu_free_entities();
> +
> +			return EFI_INVALID_PARAMETER;
> +		}
> +	}
> +
>   	total_size = sizeof(*image_info) * dfu_num + names_len;
>   	/*
>   	 * we will assume that sizeof(*image_info) * dfu_name
> @@ -172,7 +187,11 @@ static efi_status_t efi_get_dfu_info(
>   	next = name;
>   	list_for_each_entry(dfu, &dfu_list, list) {
>   		image_info[i].image_index = dfu->alt + 1;
> -		image_info[i].image_type_id = *image_type;
> +		if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE))

The number of GUIDs is not related to the number of banks.
Please, remove this test.

> +			image_info[i].image_type_id = guid_array[i];

The sequence of GUIDs in a capsule should not influence the update. The
design lacks a configuration defining which GUID maps to which DFU part.

Please, get rid of all DFU environment variables and describe the update
in a configuration file that you add to the capsule.

> +		else
> +			image_info[i].image_type_id = *guid_array;

Do you want to write the same image to multiple places? Why?

Best regards

Heirnich

> +
>   		image_info[i].image_id = dfu->alt;
>
>   		/* copy the DFU entity name */
> @@ -249,7 +268,9 @@ efi_status_t EFIAPI efi_firmware_fit_get_image_info(
>   	u32 *package_version,
>   	u16 **package_version_name)
>   {
> +	u32 nparts;
>   	efi_status_t ret;
> +	efi_guid_t *part_guid_arr;
>
>   	EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
>   		  image_info_size, image_info,
> @@ -264,12 +285,24 @@ efi_status_t EFIAPI efi_firmware_fit_get_image_info(
>   	     !descriptor_size || !package_version || !package_version_name))
>   		return EFI_EXIT(EFI_INVALID_PARAMETER);
>
> +	part_guid_arr = malloc(sizeof(efi_guid_t));
> +	if (!part_guid_arr) {
> +		log_err("Unable to allocate memory for guid array\n");
> +		ret = EFI_OUT_OF_RESOURCES;
> +		goto out;
> +	}
> +
> +	guidcpy(part_guid_arr, &efi_firmware_image_type_uboot_fit);
> +	nparts = 1;
> +
>   	ret = efi_get_dfu_info(image_info_size, image_info,
>   			       descriptor_version, descriptor_count,
>   			       descriptor_size,
>   			       package_version, package_version_name,
> -			       &efi_firmware_image_type_uboot_fit);
> +			       part_guid_arr, nparts);
>
> +out:
> +	free(part_guid_arr);
>   	return EFI_EXIT(ret);
>   }
>
> @@ -358,7 +391,10 @@ efi_status_t EFIAPI efi_firmware_raw_get_image_info(
>   	u32 *package_version,
>   	u16 **package_version_name)
>   {
> +	u32 nparts;
> +	int status;
>   	efi_status_t ret = EFI_SUCCESS;
> +	efi_guid_t *part_guid_arr;
>
>   	EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
>   		  image_info_size, image_info,
> @@ -373,12 +409,42 @@ efi_status_t EFIAPI efi_firmware_raw_get_image_info(
>   	     !descriptor_size || !package_version || !package_version_name))
>   		return EFI_EXIT(EFI_INVALID_PARAMETER);
>
> +	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> +		/*
> +		 * Read the ImageType GUID values. Populate the guid array
> +		 * with thesevalues. These are the values to be used in the
> +		 * capsule for ImageTypeId.
> +		 */
> +		status = fwu_fill_partition_guid_array(&part_guid_arr,
> +						       &nparts);
> +		if (status < 0) {
> +			log_err("Unable to get partiion guid's\n");
> +			if (status == -EIO)
> +				ret = EFI_DEVICE_ERROR;
> +			else if (status == -ENOMEM)
> +				ret = EFI_OUT_OF_RESOURCES;
> +			goto out;
> +		}
> +	} else {
> +		part_guid_arr = malloc(sizeof(efi_guid_t));
> +		if (!part_guid_arr) {
> +			log_err("Unable to allocate memory for guid array\n");
> +			ret = EFI_OUT_OF_RESOURCES;
> +			goto out;
> +		}
> +
> +		guidcpy(part_guid_arr, &efi_firmware_image_type_uboot_raw);
> +		nparts = 1;
> +	}
> +
>   	ret = efi_get_dfu_info(image_info_size, image_info,
>   			       descriptor_version, descriptor_count,
>   			       descriptor_size,
>   			       package_version, package_version_name,
> -			       &efi_firmware_image_type_uboot_raw);
> +			       part_guid_arr, nparts);
>
> +out:
> +	free(part_guid_arr);
>   	return EFI_EXIT(ret);
>   }
>
>


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

* Re: [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature
  2021-11-26 12:29 ` [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Heinrich Schuchardt
@ 2021-11-26 12:48   ` Ilias Apalodimas
  0 siblings, 0 replies; 36+ messages in thread
From: Ilias Apalodimas @ 2021-11-26 12:48 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Sughosh Ganu, Patrick Delaunay, Patrice Chotard, Alexander Graf,
	Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro, Jose Marinho,
	Grant Likely, Jason Liu, u-boot

Hi Heincrich, 

On Fri, Nov 26, 2021 at 01:29:02PM +0100, Heinrich Schuchardt wrote:
> On 11/25/21 08:12, Sughosh Ganu wrote:
> > (resending to including the first paragraph which got deleted for some
> > reason).
> > 
> > The patchset adds support for the FWU Multi Bank Update[1]
> 
> The patch set is lacking a documentation update
> The patch set is lacking an integration test.
> 
> Please, add both to enable reviewing.

The entire idea, as well as the structures used is documented in [1] [2].  
I understand that many of you don't have time to go through the entire spec,
but if it helps I'll be happy to present it on a U-Boot contributors call.

Sughosh will add the relevant short documentation in U-Boot, explaining 
the basic usage and referring to the specs once the discussion has settled
down.  However this is an RFC, it's primary purpose is to discuss the general
architecture and idea of rollback protected firmware updates.
Adding selftests to an RFC to enable reviewing is a bit too much imho.

[1] https://staging-git.codelinaro.org/linaro/firmware-dual-banked-updates/test/-/releases
[2] https://developer.arm.com/documentation/den0118/a

Regards
/Ilias

> 
> Best regards
> 
> Heinrich
> 
> > feature. Certain aspects of the Dependable Boot[2] specification have
> > also been implemented.
> > 
> > The FWU multi bank update feature is used for supporting multiple
> > sets(also called banks) of firmware image(s), allowing the platform to
> > boot from a different bank, in case it fails to boot from the active
> > bank. This functionality is supported by keeping the relevant
> > information in a structure called metadata, which provides information
> > on the images. Among other parameters, the metadata structure contains
> > information on the currect active bank that is being used to boot
> > image(s).
> > 
> > Functionality is being added to work with the UEFI capsule driver in
> > u-boot. The metadata is read to gather information on the update bank,
> > which is the bank to which the firmware images would be flashed to. On
> > a successful completion of the update of all components, the active
> > bank field in the metadata is updated, to reflect the bank from which
> > the platform will boot on the subsequent boots.
> > 
> > Currently, the feature is being enabled on the STM32MP157C-DK2
> > board which boots a FIP image from a uSD card partitioned with the GPT
> > partioning scheme. This also requires changes in the previous stage of
> > bootloader, which parses the metadata and selects the bank to boot the
> > image(s) from. Support is being added in tf-a(BL2 stage) for the
> > STM32MP157C-DK2 board to boot the active bank images. These changes
> > are under review currently[3].
> > 
> > Todo's
> > ------
> > 1) Add a test(selftest) for the metadata access.
> > 2) Add a tool for generation of the metadata. Not sure if this needs to
> >     be part of the u-boot repository though.
> > 3) Add a tool for generation of the firmware accept/reject dummy
> >     capsule. Need to check if this can be added to the mkeficapsule
> >     tool in u-boot.
> > 
> > [1] - https://developer.arm.com/documentation/den0118/a
> > [2] - https://staging-git.codelinaro.org/linaro/firmware-dual-banked-updates/test
> > [3] - https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/12566
> > 
> > Sughosh Ganu (10):
> >    GPT: Add function to get gpt header and partition entries
> >    stm32mp: dfu: Move the ram partitions to the end of the dfu_alt_info
> >      variable
> >    FWU: Add metadata structure and functions for accessing metadata
> >    FWU: Add metadata access functions for GPT partitioned block devices
> >    FWU: stm32mp1: Add helper functions for accessing metadata
> >    FWU: STM32MP1: Add support to read boot index from backup register
> >    EFI: FMP: Add provision to update image's ImageTypeId in image
> >      descriptor
> >    FWU: Add boot time checks as highlighted by the FWU specification
> >    FWU: Add support for FWU Multi Bank Update feature
> >    FWU: cmd: Add a command to read metadata
> > 
> >   arch/arm/mach-stm32mp/include/mach/stm32.h |   1 +
> >   board/st/common/stm32mp_dfu.c              |  11 +-
> >   board/st/stm32mp1/stm32mp1.c               |  70 ++
> >   cmd/Kconfig                                |   6 +
> >   cmd/Makefile                               |   1 +
> >   cmd/fwu_metadata.c                         |  65 ++
> >   common/board_r.c                           |   6 +
> >   disk/part_efi.c                            |  10 +
> >   include/fwu_metadata.h                     | 140 ++++
> >   include/part.h                             |  14 +
> >   lib/Kconfig                                |  32 +
> >   lib/Makefile                               |   1 +
> >   lib/efi_loader/efi_capsule.c               | 190 +++++-
> >   lib/efi_loader/efi_firmware.c              |  76 ++-
> >   lib/fwu_updates/Makefile                   |  11 +
> >   lib/fwu_updates/fwu.c                      | 170 +++++
> >   lib/fwu_updates/fwu_metadata.c             | 275 ++++++++
> >   lib/fwu_updates/fwu_metadata_gpt_blk.c     | 716 +++++++++++++++++++++
> >   18 files changed, 1784 insertions(+), 11 deletions(-)
> >   create mode 100644 cmd/fwu_metadata.c
> >   create mode 100644 include/fwu_metadata.h
> >   create mode 100644 lib/fwu_updates/Makefile
> >   create mode 100644 lib/fwu_updates/fwu.c
> >   create mode 100644 lib/fwu_updates/fwu_metadata.c
> >   create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
> > 
> 

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

* Re: [RESEND RFC PATCH 09/10] FWU: Add support for FWU Multi Bank Update feature
  2021-11-25  7:13 ` [RESEND RFC PATCH 09/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
@ 2021-11-26 12:55   ` Heinrich Schuchardt
  2021-11-29 11:44     ` Sughosh Ganu
  0 siblings, 1 reply; 36+ messages in thread
From: Heinrich Schuchardt @ 2021-11-26 12:55 UTC (permalink / raw)
  To: Sughosh Ganu, u-boot
  Cc: Patrick Delaunay, Patrice Chotard, Alexander Graf, Simon Glass,
	Bin Meng, Peng Fan, AKASHI Takahiro, Ilias Apalodimas,
	Jose Marinho, Grant Likely, Jason Liu

On 11/25/21 08:13, Sughosh Ganu wrote:
> The FWU Multi Bank Update feature supports updation of firmware images
> to one of multiple sets(also called banks) of images. The firmware
> images are clubbed together in banks, with the system booting images
> from the active bank. Information on the images such as which bank
> they belong to is stored as part of the metadata structure, which is
> stored on the same storage media as the firmware images on a dedicated
> partition.
>
> At the time of update, the metadata is read to identify the bank to
> which the images need to be flashed(update bank). On a successful
> update, the metadata is modified to set the updated bank as active
> bank to subsequently boot from.
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>   include/fwu_metadata.h       |  10 ++
>   lib/Kconfig                  |  32 ++++++
>   lib/Makefile                 |   1 +
>   lib/efi_loader/efi_capsule.c | 190 ++++++++++++++++++++++++++++++++++-
>   lib/fwu_updates/Makefile     |  11 ++
>   lib/fwu_updates/fwu.c        |  29 +++++-
>   6 files changed, 269 insertions(+), 4 deletions(-)
>   create mode 100644 lib/fwu_updates/Makefile
>
> diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
> index 02897f33a8..2d276a019a 100644
> --- a/include/fwu_metadata.h
> +++ b/include/fwu_metadata.h
> @@ -106,7 +106,16 @@ struct fwu_metadata_ops {
>   	EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \
>   		 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
>
> +#define FWU_OS_REQUEST_FW_REVERT_GUID \
> +	EFI_GUID(0xacd58b4b, 0xc0e8, 0x475f, 0x99, 0xb5, \
> +		 0x6b, 0x3f, 0x7e, 0x07, 0xaa, 0xf0)
> +
> +#define FWU_OS_REQUEST_FW_ACCEPT_GUID \
> +	EFI_GUID(0x0c996046, 0xbcc0, 0x4d04, 0x85, 0xec, \
> +		 0xe1, 0xfc, 0xed, 0xf1, 0xc6, 0xf8)
> +
>   #define FWU_METADATA_VERSION	0x1
> +#define FWU_IMAGE_ACCEPTED	0x1
>
>   extern struct fwu_metadata_ops fwu_gpt_blk_ops;
>
> @@ -126,5 +135,6 @@ int fwu_plat_get_update_index(u32 *update_idx);
>   int fwu_plat_get_blk_desc(struct blk_desc **desc);
>   void fwu_plat_get_bootidx(void *boot_idx);
>   int fwu_boottime_checks(void);
> +int fwu_trial_state_ctr_start(void);
>
>   #endif /* _FWU_METADATA_H_ */
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 807a4c6ade..7cb306317c 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -835,3 +835,35 @@ config PHANDLE_CHECK_SEQ
>   	  When there are multiple device tree nodes with same name,
>             enable this config option to distinguish them using
>   	  phandles in fdtdec_get_alias_seq() function.
> +
> +config FWU_MULTI_BANK_UPDATE
> +	bool "Enable FWU Multi Bank Update Feature"

Why do we need a configuration variable? Having 1 bank is just a special
case which should not use separate code.

> +	depends on EFI_HAVE_CAPSULE_SUPPORT
> +	select PARTITION_TYPE_GUID
> +	select EFI_SETUP_EARLY
> +	help
> +	  Feature for updating firmware images on platforms having
> +	  multiple banks(copies) of the firmware images. One of the
> +	  bank is selected for updating all the firmware components
> +
> +config FWU_NUM_BANKS
> +	int "Number of Banks defined by the platform"
> +	depends on FWU_MULTI_BANK_UPDATE

This should default to 1.

> +	help
> +	  Define the number of banks of firmware images on a platform
> +
> +config FWU_NUM_IMAGES_PER_BANK
> +	int "Number of firmware images per bank"
> +	depends on FWU_MULTI_BANK_UPDATE

This dependency makes no sense. Even if I have one bank I can have
multiple images.

Why should this configuration variable be needed?
The dfu variables and the capsule define how many images are updated.

> +	help
> +	  Define the number of firmware images per bank. This value
> +	  should be the same for all the banks.
> +
> +config FWU_TRIAL_STATE_CNT
> +	int "Number of times system boots in Trial State"
> +	depends on FWU_MULTI_BANK_UPDATE
> +	default 3
> +	help
> +	  With FWU Multi Bank Update feature enabled, number of times
> +	  the platform is allowed to boot in Trial State after an
> +	  update.

Do you mean:

"This is the number of boots in trial state before falling back to the
last successfully used bank."

Best regards

Heinrich

> diff --git a/lib/Makefile b/lib/Makefile
> index 5ddbc77ed6..bc5c1e22fc 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/
>   obj-$(CONFIG_EFI_LOADER) += efi_driver/
>   obj-$(CONFIG_EFI_LOADER) += efi_loader/
>   obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/
> +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/
>   obj-$(CONFIG_LZMA) += lzma/
>   obj-$(CONFIG_BZIP2) += bzip2/
>   obj-$(CONFIG_TIZEN) += tizen/
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> index 502bcfca6e..40b9e87e92 100644
> --- a/lib/efi_loader/efi_capsule.c
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -14,6 +14,7 @@
>   #include <env.h>
>   #include <fdtdec.h>
>   #include <fs.h>
> +#include <fwu_metadata.h>
>   #include <malloc.h>
>   #include <mapmem.h>
>   #include <sort.h>
> @@ -30,6 +31,13 @@ static const efi_guid_t efi_guid_firmware_management_capsule_id =
>   		EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
>   const efi_guid_t efi_guid_firmware_management_protocol =
>   		EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
> +const efi_guid_t fwu_guid_os_request_fw_revert =
> +		FWU_OS_REQUEST_FW_REVERT_GUID;
> +const efi_guid_t fwu_guid_os_request_fw_accept =
> +		FWU_OS_REQUEST_FW_ACCEPT_GUID;
> +
> +__maybe_unused static u32 update_index;
> +__maybe_unused static bool capsule_update;
>
>   #ifdef CONFIG_EFI_CAPSULE_ON_DISK
>   /* for file system access */
> @@ -403,10 +411,13 @@ static efi_status_t efi_capsule_update_firmware(
>   	void *image_binary, *vendor_code;
>   	efi_handle_t *handles;
>   	efi_uintn_t no_handles;
> -	int item;
> +	int item, alt_no;
>   	struct efi_firmware_management_protocol *fmp;
>   	u16 *abort_reason;
> +	efi_guid_t image_type_id;
>   	efi_status_t ret = EFI_SUCCESS;
> +	int status;
> +	u8 image_index;
>
>   	/* sanity check */
>   	if (capsule_data->header_size < sizeof(*capsule) ||
> @@ -481,8 +492,36 @@ static efi_status_t efi_capsule_update_firmware(
>   				goto out;
>   		}
>
> +		if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> +			/*
> +			 * Based on the value of update_image_type_id,
> +			 * derive the alt number value. This will be
> +			 * passed as update_image_index to the
> +			 * set_image function.
> +			 */
> +			image_type_id = image->update_image_type_id;
> +			status = fwu_get_image_alt_num(image_type_id,
> +						       update_index,
> +						       &alt_no);
> +			if (status < 0) {
> +				log_err("Unable to get the alt no for the image type %pUl\n",
> +					&image_type_id);
> +				if (status == -ENODEV || status == -EIO)
> +					ret = EFI_DEVICE_ERROR;
> +				else if (status == -ENOMEM)
> +					ret = EFI_OUT_OF_RESOURCES;
> +				else if (status == -ERANGE || status == -EINVAL)
> +					ret = EFI_INVALID_PARAMETER;
> +				goto out;
> +			}
> +			log_debug("alt_no %u for Image Type Id %pUl\n",
> +				  alt_no, &image_type_id);
> +			image_index = alt_no;
> +		} else {
> +			image_index = image->update_image_index;
> +		}
>   		abort_reason = NULL;
> -		ret = EFI_CALL(fmp->set_image(fmp, image->update_image_index,
> +		ret = EFI_CALL(fmp->set_image(fmp, image_index,
>   					      image_binary,
>   					      image_binary_size,
>   					      vendor_code, NULL,
> @@ -493,6 +532,24 @@ static efi_status_t efi_capsule_update_firmware(
>   			efi_free_pool(abort_reason);
>   			goto out;
>   		}
> +
> +		if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> +			status = fwu_clear_accept_image(&image_type_id,
> +							update_index);
> +			if (status < 0) {
> +				log_err("Unable to clear the accept bit for the image %pUl\n",
> +					&image_type_id);
> +				if (status == -ENODEV || status == -EIO)
> +					ret = EFI_DEVICE_ERROR;
> +				else if (status == -ENOMEM)
> +					ret = EFI_OUT_OF_RESOURCES;
> +				else if (status == -ERANGE || status == -EINVAL)
> +					ret = EFI_INVALID_PARAMETER;
> +				goto out;
> +			}
> +			log_debug("Cleared out accepted bit for Image %pUl\n", &image_type_id);
> +		}
> +
>   	}
>
>   out:
> @@ -527,6 +584,9 @@ efi_status_t EFIAPI efi_update_capsule(
>   		u64 scatter_gather_list)
>   {
>   	struct efi_capsule_header *capsule;
> +	efi_guid_t *image_guid;
> +	u32 active_idx;
> +	int status;
>   	unsigned int i;
>   	efi_status_t ret;
>
> @@ -538,6 +598,16 @@ efi_status_t EFIAPI efi_update_capsule(
>   		goto out;
>   	}
>
> +	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> +		/* First obtain the update_index from the platform */
> +		status = fwu_plat_get_update_index(&update_index);
> +		if (status < 0) {
> +			log_err("Failed to get the FWU update_index value\n");
> +			ret = EFI_DEVICE_ERROR;
> +			goto out;
> +		}
> +	}
> +
>   	ret = EFI_SUCCESS;
>   	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
>   	     i++, capsule = *(++capsule_header_array)) {
> @@ -553,6 +623,55 @@ efi_status_t EFIAPI efi_update_capsule(
>   		if (!guidcmp(&capsule->capsule_guid,
>   			     &efi_guid_firmware_management_capsule_id)) {
>   			ret  = efi_capsule_update_firmware(capsule);
> +			capsule_update = true;
> +		} else if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> +			capsule_update = false;
> +			if (!guidcmp(&capsule->capsule_guid,
> +				     &fwu_guid_os_request_fw_revert)) {
> +				/*
> +				 * One of the previously updated image has
> +				 * failed the OS acceptance test. OS has
> +				 * requested to revert back to the earlier
> +				 * boot index
> +				 */
> +				status = fwu_revert_boot_index(&active_idx);
> +				if (status < 0) {
> +					log_err("Failed to revert the FWU boot index\n");
> +					if (status == -ENODEV ||
> +					    status == -ERANGE ||
> +					    status == -EIO)
> +						ret = EFI_DEVICE_ERROR;
> +					else if (status == -EINVAL)
> +						ret = EFI_INVALID_PARAMETER;
> +					else if (status == -ENOMEM)
> +						ret = EFI_OUT_OF_RESOURCES;
> +				} else {
> +					ret = EFI_SUCCESS;
> +					log_err("Reverted the FWU active_index to %u. Recommend rebooting the system\n",
> +						active_idx);
> +				}
> +			} else if (!guidcmp(&capsule->capsule_guid,
> +					    &fwu_guid_os_request_fw_accept)) {
> +				/*
> +				 * Image accepted by the OS. Set the acceptance
> +				 * status for the image.
> +				 */
> +				image_guid = (void *)(char *)capsule +
> +					capsule->header_size;
> +				status = fwu_accept_image(image_guid);
> +				if (status < 0) {
> +					if (status == -ENODEV ||
> +					    status == -ERANGE ||
> +					    status == -EIO)
> +						ret = EFI_DEVICE_ERROR;
> +					else if (status == -EINVAL)
> +						ret = EFI_INVALID_PARAMETER;
> +					else if (status == -ENOMEM)
> +						ret = EFI_OUT_OF_RESOURCES;
> +				} else {
> +					ret = EFI_SUCCESS;
> +				}
> +			}
>   		} else {
>   			log_err("Unsupported capsule type: %pUl\n",
>   				&capsule->capsule_guid);
> @@ -563,6 +682,36 @@ efi_status_t EFIAPI efi_update_capsule(
>   			goto out;
>   	}
>
> +	/*
> +	 * Update the metadata once all the capsules have
> +	 * been updated. This is done only for the Runtime
> +	 * capsule update service.
> +	 * The update_index value now gets written to the
> +	 * active_index and the update_index value also
> +	 * gets updated.
> +	 * For the capsule-on-disk feature, the updation
> +	 * of the metadata happens in efi_launch_capsules
> +	 */
> +	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE) &&
> +	    !IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK)) {
> +		status = fwu_update_active_index(update_index);
> +		if (status < 0) {
> +			log_err("Failed to update FWU metadata index values\n");
> +			if (status == -EINVAL || status == -ERANGE)
> +				ret = EFI_INVALID_PARAMETER;
> +			else if (status == -ENODEV || status == -EIO)
> +				ret = EFI_DEVICE_ERROR;
> +			else if (status == -ENOMEM)
> +				ret = EFI_OUT_OF_RESOURCES;
> +		} else {
> +			status = fwu_trial_state_ctr_start();
> +			if (status < 0)
> +				ret = EFI_DEVICE_ERROR;
> +			else
> +				ret = EFI_SUCCESS;
> +		}
> +	}
> +
>   	if (IS_ENABLED(CONFIG_EFI_ESRT)) {
>   		/* Rebuild the ESRT to reflect any updated FW images. */
>   		ret = efi_esrt_populate();
> @@ -1075,8 +1224,10 @@ efi_status_t efi_launch_capsules(void)
>   {
>   	struct efi_capsule_header *capsule = NULL;
>   	u16 **files;
> +	int status;
>   	unsigned int nfiles, index, i;
>   	efi_status_t ret;
> +	bool update_status = true;
>
>   	if (!check_run_capsules())
>   		return EFI_SUCCESS;
> @@ -1104,9 +1255,11 @@ efi_status_t efi_launch_capsules(void)
>   		ret = efi_capsule_read_file(files[i], &capsule);
>   		if (ret == EFI_SUCCESS) {
>   			ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0));
> -			if (ret != EFI_SUCCESS)
> +			if (ret != EFI_SUCCESS) {
>   				log_err("Applying capsule %ls failed\n",
>   					files[i]);
> +				update_status = false;
> +			}
>
>   			/* create CapsuleXXXX */
>   			set_capsule_result(index, capsule, ret);
> @@ -1114,6 +1267,7 @@ efi_status_t efi_launch_capsules(void)
>   			free(capsule);
>   		} else {
>   			log_err("Reading capsule %ls failed\n", files[i]);
> +			update_status = false;
>   		}
>   		/* delete a capsule either in case of success or failure */
>   		ret = efi_capsule_delete_file(files[i]);
> @@ -1121,7 +1275,37 @@ efi_status_t efi_launch_capsules(void)
>   			log_err("Deleting capsule %ls failed\n",
>   				files[i]);
>   	}
> +
>   	efi_capsule_scan_done();
> +	if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> +		if (update_status == true && capsule_update == true) {
> +			/*
> +			 * All the capsules have been updated successfully,
> +			 * update the FWU metadata.
> +			 */
> +			log_debug("Update Complete. Now updating active_index to %u\n",
> +				  update_index);
> +			status = fwu_update_active_index(update_index);
> +			if (status < 0) {
> +				log_err("Failed to update FWU metadata index values\n");
> +				if (status == -EINVAL || status == -ERANGE)
> +					ret = EFI_INVALID_PARAMETER;
> +				else if (status == -ENODEV || status == -EIO)
> +					ret = EFI_DEVICE_ERROR;
> +				else if (status == -ENOMEM)
> +					ret = EFI_OUT_OF_RESOURCES;
> +			} else {
> +				log_debug("Successfully updated the active_index\n");
> +				status = fwu_trial_state_ctr_start();
> +				if (status < 0)
> +					ret = EFI_DEVICE_ERROR;
> +				else
> +					ret = EFI_SUCCESS;
> +			}
> +		} else if (capsule_update == true && update_status == false) {
> +			log_err("All capsules were not updated. Not updating metadata\n");
> +		}
> +	}
>
>   	for (i = 0; i < nfiles; i++)
>   		free(files[i]);
> diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile
> new file mode 100644
> index 0000000000..f6fa8290c1
> --- /dev/null
> +++ b/lib/fwu_updates/Makefile
> @@ -0,0 +1,11 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +#
> +# Copyright (c) 2021, Linaro Limited
> +#
> +
> +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_metadata.o
> +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o
> +
> +ifdef CONFIG_EFI_PARTITION
> +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_metadata_gpt_blk.o
> +endif
> diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c
> index 2e1904b912..bbd3dc249f 100644
> --- a/lib/fwu_updates/fwu.c
> +++ b/lib/fwu_updates/fwu.c
> @@ -45,7 +45,7 @@ static int fwu_trial_state_check(void)
>   		log_info("System booting in Trial State\n");
>   		var_attributes = EFI_VARIABLE_NON_VOLATILE |
>   			EFI_VARIABLE_BOOTSERVICE_ACCESS |
> -			EFI_VARIABLE_RUNTIME_ACCESS,
> +			EFI_VARIABLE_RUNTIME_ACCESS;
>   		status = efi_get_variable_int(L"TrialStateCtr",
>   					      &efi_global_variable_guid,
>   					      &var_attributes,
> @@ -99,6 +99,33 @@ out:
>   	return ret;
>   }
>
> +int fwu_trial_state_ctr_start(void)
> +{
> +	int ret;
> +	u32 var_attributes;
> +	efi_status_t status;
> +	efi_uintn_t var_size;
> +	u16 trial_state_ctr;
> +
> +	var_size = (efi_uintn_t)sizeof(trial_state_ctr);
> +	var_attributes = EFI_VARIABLE_NON_VOLATILE |
> +		EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +		EFI_VARIABLE_RUNTIME_ACCESS;
> +
> +	trial_state_ctr = ret = 0;
> +	status = efi_set_variable_int(L"TrialStateCtr",
> +				      &efi_global_variable_guid,
> +				      var_attributes,
> +				      var_size,
> +				      &trial_state_ctr, false);
> +	if (status != EFI_SUCCESS) {
> +		log_err("Unable to increment TrialStateCtr variable\n");
> +		ret = -1;
> +	}
> +
> +	return ret;
> +}
> +
>   int fwu_boottime_checks(void)
>   {
>   	int ret;
>


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

* Re: [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata
  2021-11-26 11:35   ` Ilias Apalodimas
@ 2021-11-29  6:38     ` Sughosh Ganu
  0 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-29  6:38 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Jose Marinho, Grant Likely, Jason Liu

hi Ilias,
Thanks for the review.

On Fri, 26 Nov 2021 at 17:05, Ilias Apalodimas <ilias.apalodimas@linaro.org>
wrote:

> Hi Sughosh,
>
> On Thu, Nov 25, 2021 at 12:42:55PM +0530, Sughosh Ganu wrote:
> > In the FWU Multi Bank Update feature, the information about the
> > updatable images is stored as part of the metadata, which is stored on
> > a dedicated partition. Add the metadata structure, and functions to
> > access the metadata. These are generic API's, and implementations can
> > be added based on parameters like how the metadata partition is
> > accessed and what type of storage device houses the metadata.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >  include/fwu_metadata.h         | 125 +++++++++++++++
> >  lib/fwu_updates/fwu_metadata.c | 275 +++++++++++++++++++++++++++++++++
> >  2 files changed, 400 insertions(+)
> >  create mode 100644 include/fwu_metadata.h
> >  create mode 100644 lib/fwu_updates/fwu_metadata.c
> >
> > diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
> > new file mode 100644
> > index 0000000000..e692ef7506
> > --- /dev/null
> > +++ b/include/fwu_metadata.h
> > @@ -0,0 +1,125 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + * Copyright (c) 2021, Linaro Limited
>
> Please add a link here to the arm spec that describes the metadata etc
>

Will add.


>
> > + */
> > +
> > +#if !defined _FWU_METADATA_H_
> > +#define _FWU_METADATA_H_
> > +
> > +#include <blk.h>
> > +#include <efi.h>
> > +#include <uuid.h>
> > +
> > +#include <linux/types.h>
> > +
> > +/**
> > + * struct fwu_image_bank_info - firmware image information
> > + * @image_uuid: Guid value of the image in this bank
> > + * @accepted: Acceptance status of the image
> > + * @reserved: Reserved
> > + *
> > + * The structure contains image specific fields which are
> > + * used to identify the image and to specify the image's
> > + * acceptance status
> > + */
> > +struct fwu_image_bank_info {
> > +     efi_guid_t  image_uuid;
> > +     u32 accepted;
> > +     u32 reserved;
> > +};
>
> fwu_image_bank_info -> fwu_img_bank_info
>

Okay.


>
> > +
> > +/**
> > + * struct fwu_image_entry - information for a particular type of image
> > + * @image_type_uuid: Guid value for identifying the image type
> > + * @location_uuid: Guid of the storage volume where the image is located
>
> /s/Guid/GUID
>

Will change.


>
> > + * @img_bank_info: Array containing properties of images
> > + *
> > + * This structure contains information on various types of updatable
> > + * firmware images. Each image type then contains an array of image
> > + * information per bank.
> > + */
> > +struct fwu_image_entry {
> > +     efi_guid_t image_type_uuid;
> > +     efi_guid_t location_uuid;
> > +     struct fwu_image_bank_info img_bank_info[CONFIG_FWU_NUM_BANKS];
> > +};
> > +
>
> It seems like you've followed the naming proposed in the spec,  which makes
> reading spec -- code easier.  However I feel we should add a few more
> comments on the naming to make reading easier or change the naming and
> mention the original name in comments.
>
> A 'bank' is supposed to contain:
> bank[0]: Uboot(0), TF-A(0) etc
> bank[1]: Uboot(1), TF-A(1) etc
> However there's no structure that defines an entire bank.  Instead the bank
> information is constructed by reading the metadata and fixing it up on
> the fly.
>
> fwu_image_bank_info -- Information for a specific image (e.g OP-TEE,
> U-Boot, TF-A, whatever) but not within a *bank*.  That's amongst a
> collection of images of the same type.
>
> IOW img_bank_info looks like:
> img_bank_info[0] -> U-Boot(0), U-Boot(1) etc
> img_bank_info[1] -> TF-A(0), TF-A(1) etc
>
> @Jose can we tweak the spec naming a bit to be more intuitive?
> I am terrible at naming stuff but what about:
> fwu_image_bank_info -> fwu_img_repo_info, fwu_img_vault_info,
>                                            fwu_img_storage_info,
> fwu_img_array_info,


>
>
> > +/**
> > + * struct fwu_metadata - Metadata structure for multi-bank updates
> > + * @crc32: crc32 value for the metadata
> > + * @version: Metadata version
> > + * @active_index: Index of the bank currently used for booting images
> > + * @previous_active_inde: Index of the bank used before the current bank
> > + *                        being used for booting
> > + * @img_entry: Array of information on various firmware images that can
> > + *             be updated
> > + *
> > + * This structure is used to store all the needed information for
> performing
> > + * multi bank updates on the platform. This contains info on the bank
> being
> > + * used to boot along with the information needed for identification of
> > + * individual images
> > + */
> > +struct fwu_metadata {
> > +     u32 crc32;
> > +     u32 version;
> > +     u32 active_index;
> > +     u32 previous_active_index;
> > +
> > +     struct fwu_image_entry img_entry[CONFIG_FWU_NUM_IMAGES_PER_BANK];
> > +};
> > +
> > +/**
> > + * @get_active_index: get the current active_index value
> > + * @update_active_index: update the active_index value
> > + * @fill_partition_guid_array: fill the array with guid values of the
> > + *                             partitions found on the storage media
> > + * @get_image_alt_num: get the alt number to be used for the image
> > + * @metadata_check: check the validity of the metadata partitions
> > + * @revert_boot_index: set the active_index to previous_active_index
> > + * @set_accept_image: set the accepted bit for the image
> > + * @clear_accept_image: clear the accepted bit for the image
> > + * @get_metadata() - Get a metadata copy
> > + */
> > +struct fwu_metadata_ops {
> > +     int (*get_active_index)(u32 *active_idx);
> > +
> > +     int (*update_active_index)(u32 active_idx);
> > +
> > +     int (*fill_partition_guid_array)(efi_guid_t **part_guid_arr,
> > +                                      u32 *nparts);
> > +
> > +     int (*get_image_alt_num)(efi_guid_t image_type_id, u32 update_bank,
> > +                              int *alt_num);
> > +
> > +     int (*metadata_check)(void);
> > +
> > +     int (*revert_boot_index)(u32 *active_idx);
> > +
> > +     int (*set_accept_image)(efi_guid_t *img_type_id);
> > +
> > +     int (*clear_accept_image)(efi_guid_t *img_type_id, u32 bank);
> > +
> > +     int (*get_metadata)(struct fwu_metadata **metadata);
> > +};
> > +
> > +#define FWU_METADATA_GUID \
> > +     EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \
> > +              0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
> > +
> > +#define FWU_METADATA_VERSION 0x1
> > +
> > +extern struct fwu_metadata_ops fwu_gpt_blk_ops;
> > +
> > +struct fwu_metadata_ops *get_plat_fwu_metadata_ops(void);
> > +int fwu_get_active_index(u32 *active_idx);
> > +int fwu_update_active_index(u32 active_idx);
> > +int fwu_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32
> *nparts);
> > +int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> > +                       int *alt_num);
> > +int fwu_metadata_check(void);
> > +int fwu_revert_boot_index(u32 *active_idx);
> > +int fwu_accept_image(efi_guid_t *img_type_id);
> > +int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank);
> > +int fwu_get_metadata(struct fwu_metadata **metadata);
> > +
> > +#endif /* _FWU_METADATA_H_ */
> > diff --git a/lib/fwu_updates/fwu_metadata.c
> b/lib/fwu_updates/fwu_metadata.c
> > new file mode 100644
> > index 0000000000..ebc3eaa04a
> > --- /dev/null
> > +++ b/lib/fwu_updates/fwu_metadata.c
> > @@ -0,0 +1,275 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (c) 2021, Linaro Limited
> > + */
> > +
> > +#include <fwu_metadata.h>
> > +
> > +#include <linux/errno.h>
> > +#include <linux/types.h>
> > +
> > +static inline struct fwu_metadata_ops *get_fwu_metadata_ops(void)
> > +{
> > +     return get_plat_fwu_metadata_ops();
> > +}
> > +
> > +/**
> > + * fwu_get_active_index() - Get active_index from the metadata
> > + * @active_idx: active_index value to be read
> > + *
> > + * Read the active_index field from the metadata and place it in
> > + * the variable pointed to be the function argument.
> > + *
> > + * Return: 0 if OK, -ve on error
>
> -ve ?
>

Sorry, I did not get this review comment. The active index is returned back
through the function parameter. The return value indicates whether the
function is returning the active_index value successfully(0), or if there
was an error(-ve) in getting the active_index.


> > + *
> > + */
> > +int fwu_get_active_index(u32 *active_idx)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
>
> On all callsites of get_fwu_metadata_ops() do we need to be that verbose on
> the ops missing?  If not we can just squeeze in the if
> (!ops->XXXXXX) check in get_fwu_metadata_ops() and simply return an error
> there.
>

Okay. Will change.


>
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_update_active_index(u32 active_idx)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->update_active_index) {
> > +             log_err("update_active_index() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->update_active_index(active_idx);
> > +}
> > +
> > +/**
> > + * fwu_fill_partition_guid_array() - Fill the part_guid_arr array with
> the guid's of
> > + *                                   the partitions
> > + * @part_guid_arr: array of partition guid's
> > + * @nparts: Number of gpt partitions on the device
> > + *
> > + * Get the information on the partition guid's, filling the array with
> the guid
> > + * values and also the number of partitions.
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32
> *nparts)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->fill_partition_guid_array) {
> > +             log_err("fill_partition_guid_array() method not defined
> for the platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->fill_partition_guid_array(part_guid_arr, nparts);
> > +}
> > +
> > +/**
> > + * fwu_get_image_alt_num() - Get the dfu alt number to be used for
> capsule update
> > + * @image_type_id: image guid as passed in the capsule
> > + * @update_bank: Bank to which the update is to be made
> > + * @alt_num: The alt_num for the image
> > + *
> > + * Based on the guid value passed in the capsule, along with the bank
> to which the
> > + * image needs to be updated, get the dfu alt number which will be used
> for the
> > + * capsule update
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> > +                       int *alt_num)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->get_image_alt_num) {
> > +             log_err("get_image_alt_num() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->get_image_alt_num(image_type_id, update_bank, alt_num);
> > +}
> > +
> > +/**
> > + * fwu_metadata_check() - Check if the metadata is valid
> > + *
> > + * Validate both copies of metadata. If one of the copies
> > + * has gone bad, restore it from the other bad copy.
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_metadata_check(void)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->metadata_check) {
> > +             log_err("metadata_check() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->metadata_check();
> > +}
> > +
> > +/**
> > + * fwu_revert_boot_index() - Revert the active index in the metadata
> > + * @active_idx: Value of the updated active_index
> > + *
> > + * Revert the active_index value in the metadata, by swapping the values
> > + * of active_index and previous_active_index in both copies of the
> > + * metadata.
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_revert_boot_index(u32 *active_idx)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->revert_boot_index) {
> > +             log_err("revert_boot_index() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->revert_boot_index(active_idx);
> > +}
> > +
> > +/**
> > + * fwu_accept_image() - Set the Acceptance bit for the image
> > + * @img_type_id: Guid of the image type for which the accepted bit is
> to be
> > + *               cleared
> > + *
> > + * Set the accepted bit for the image specified by the img_guid
> parameter. This
> > + * indicates acceptance of image for subsequent boots by some governing
> component
> > + * like OS(or firmware).
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_accept_image(efi_guid_t *img_type_id)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->set_accept_image) {
> > +             log_err("set_accept_image() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->set_accept_image(img_type_id);
> > +}
> > +
> > +/**
> > + * fwu_clear_accept_image() - Clear the Acceptance bit for the image
> > + * @img_type_id: Guid of the image type for which the accepted bit is
> to be
> > + *               cleared
> > + *
> > + * Clear the accepted bit for the image type specified by the
> img_type_id parameter.
> > + * This function is called after the image has been updated. The
> accepted bit is
> > + * cleared to be set subsequently after passing the image acceptance
> criteria, by
> > + * either the OS(or firmware)
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->clear_accept_image) {
> > +             log_err("clear_accept_image() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->clear_accept_image(img_type_id, bank);
> > +}
> > +
> > +/**
> > + * fwu_get_metadata() - Get a metadata copy
> > + * @metadata: Copy of the metadata
> > + *
> > + * Get a valid copy of the metadata.
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_get_metadata(struct fwu_metadata **metadata)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->get_metadata) {
> > +             log_err("get_metadata() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->get_metadata(metadata);
> > +}
> > --
> > 2.17.1
> >
>
>
> Cheers
> /Ilias
>

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

* Re: [RESEND RFC PATCH 07/10] EFI: FMP: Add provision to update image's ImageTypeId in image descriptor
  2021-11-26 12:43   ` Heinrich Schuchardt
@ 2021-11-29 11:38     ` Sughosh Ganu
  0 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-29 11:38 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Patrick Delaunay, Patrice Chotard, Alexander Graf, Simon Glass,
	Bin Meng, Peng Fan, AKASHI Takahiro, Ilias Apalodimas,
	Jose Marinho, Grant Likely, Jason Liu, u-boot

hi Heinrich,
Thanks for taking up the review.

On Fri, 26 Nov 2021 at 18:18, Heinrich Schuchardt <xypron.glpk@gmx.de>
wrote:

> On 11/25/21 08:12, Sughosh Ganu wrote:
> > The FWU Multi Banks Update feature allows updating different types of
> > updatable firmware images on the platform. These image types are
> > identified using the ImageTypeId GUID value. Add support in the
> > GetImageInfo function of the FMP protocol to get the GUID values for
> > the individual images and populate these in the image descriptor for
> > the corresponding images.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >   lib/efi_loader/efi_firmware.c | 76 ++++++++++++++++++++++++++++++++---
> >   1 file changed, 71 insertions(+), 5 deletions(-)
> >
> > diff --git a/lib/efi_loader/efi_firmware.c
> b/lib/efi_loader/efi_firmware.c
> > index a1b88dbfc2..a2b639b448 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 <fwu_metadata.h>
> >   #include <image.h>
> >   #include <signatures.h>
> >
> > @@ -106,7 +107,8 @@ efi_status_t EFIAPI
> efi_firmware_set_package_info_unsupported(
> >    * @descriptor_size:                Pointer to descriptor size
> >    * package_version:         Package version
> >    * package_version_name:    Package version's name
> > - * image_type:                       Image type GUID
> > + * guid_array:                       Image type GUID array
> > + * nparts:                   Number of partions on the storage device
> >    *
> >    * Return information bout the current firmware image in @image_info.
> >    * @image_info will consist of a number of descriptors.
> > @@ -122,7 +124,7 @@ static efi_status_t efi_get_dfu_info(
> >       efi_uintn_t *descriptor_size,
> >       u32 *package_version,
> >       u16 **package_version_name,
> > -     const efi_guid_t *image_type)
> > +     const efi_guid_t *guid_array, u32 nparts)
> >   {
> >       struct dfu_entity *dfu;
> >       size_t names_len, total_size;
> > @@ -145,6 +147,19 @@ static efi_status_t efi_get_dfu_info(
> >               return EFI_SUCCESS;
> >       }
> >
> > +     if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
>
> The concept of multiple banks is not related to the concept of multiple
> GUIDs. So this if statement should be removed.


Okay. Will make the change.


>


> > +             /*
> > +              * For FWU multi bank updates, the number of partitions
> > +              * should at least be same as dfu partitions or less
> > +              */
> > +             if (nparts > dfu_num) {
> > +                     log_err("Number of dfu alt no's less than
> partitions\n");
> > +                     dfu_free_entities();
> > +
> > +                     return EFI_INVALID_PARAMETER;
> > +             }
> > +     }
> > +
> >       total_size = sizeof(*image_info) * dfu_num + names_len;
> >       /*
> >        * we will assume that sizeof(*image_info) * dfu_name
> > @@ -172,7 +187,11 @@ static efi_status_t efi_get_dfu_info(
> >       next = name;
> >       list_for_each_entry(dfu, &dfu_list, list) {
> >               image_info[i].image_index = dfu->alt + 1;
> > -             image_info[i].image_type_id = *image_type;
> > +             if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE))
>
> The number of GUIDs is not related to the number of banks.
> Please, remove this test.
>

Okay, will make it common, as you suggest.


>
> > +                     image_info[i].image_type_id = guid_array[i];
>
> The sequence of GUIDs in a capsule should not influence the update. The
> design lacks a configuration defining which GUID maps to which DFU part.
>
> Please, get rid of all DFU environment variables and describe the update
> in a configuration file that you add to the capsule.
>

Like we discussed, the information on the update, like the device,
partition number etc cannot be stored as part of the capsule header
currently. So, for now, we have to make do with using the dfu variables for
this.


> > +             else
> > +                     image_info[i].image_type_id = *guid_array;
>
> Do you want to write the same image to multiple places? Why?
>

I was keeping the logic consistent with the way it is currently. This will
go away as the explicit check for CONFIG_FWU_MULTI_BANK_UPDATE is removed
based on your comment above.

-sughosh


> Best regards
>
> Heirnich
>
> > +
> >               image_info[i].image_id = dfu->alt;
> >
> >               /* copy the DFU entity name */
> > @@ -249,7 +268,9 @@ efi_status_t EFIAPI efi_firmware_fit_get_image_info(
> >       u32 *package_version,
> >       u16 **package_version_name)
> >   {
> > +     u32 nparts;
> >       efi_status_t ret;
> > +     efi_guid_t *part_guid_arr;
> >
> >       EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
> >                 image_info_size, image_info,
> > @@ -264,12 +285,24 @@ efi_status_t EFIAPI
> efi_firmware_fit_get_image_info(
> >            !descriptor_size || !package_version ||
> !package_version_name))
> >               return EFI_EXIT(EFI_INVALID_PARAMETER);
> >
> > +     part_guid_arr = malloc(sizeof(efi_guid_t));
> > +     if (!part_guid_arr) {
> > +             log_err("Unable to allocate memory for guid array\n");
> > +             ret = EFI_OUT_OF_RESOURCES;
> > +             goto out;
> > +     }
> > +
> > +     guidcpy(part_guid_arr, &efi_firmware_image_type_uboot_fit);
> > +     nparts = 1;
> > +
> >       ret = efi_get_dfu_info(image_info_size, image_info,
> >                              descriptor_version, descriptor_count,
> >                              descriptor_size,
> >                              package_version, package_version_name,
> > -                            &efi_firmware_image_type_uboot_fit);
> > +                            part_guid_arr, nparts);
> >
> > +out:
> > +     free(part_guid_arr);
> >       return EFI_EXIT(ret);
> >   }
> >
> > @@ -358,7 +391,10 @@ efi_status_t EFIAPI efi_firmware_raw_get_image_info(
> >       u32 *package_version,
> >       u16 **package_version_name)
> >   {
> > +     u32 nparts;
> > +     int status;
> >       efi_status_t ret = EFI_SUCCESS;
> > +     efi_guid_t *part_guid_arr;
> >
> >       EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
> >                 image_info_size, image_info,
> > @@ -373,12 +409,42 @@ efi_status_t EFIAPI
> efi_firmware_raw_get_image_info(
> >            !descriptor_size || !package_version ||
> !package_version_name))
> >               return EFI_EXIT(EFI_INVALID_PARAMETER);
> >
> > +     if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> > +             /*
> > +              * Read the ImageType GUID values. Populate the guid array
> > +              * with thesevalues. These are the values to be used in the
> > +              * capsule for ImageTypeId.
> > +              */
> > +             status = fwu_fill_partition_guid_array(&part_guid_arr,
> > +                                                    &nparts);
> > +             if (status < 0) {
> > +                     log_err("Unable to get partiion guid's\n");
> > +                     if (status == -EIO)
> > +                             ret = EFI_DEVICE_ERROR;
> > +                     else if (status == -ENOMEM)
> > +                             ret = EFI_OUT_OF_RESOURCES;
> > +                     goto out;
> > +             }
> > +     } else {
> > +             part_guid_arr = malloc(sizeof(efi_guid_t));
> > +             if (!part_guid_arr) {
> > +                     log_err("Unable to allocate memory for guid
> array\n");
> > +                     ret = EFI_OUT_OF_RESOURCES;
> > +                     goto out;
> > +             }
> > +
> > +             guidcpy(part_guid_arr, &efi_firmware_image_type_uboot_raw);
> > +             nparts = 1;
> > +     }
> > +
> >       ret = efi_get_dfu_info(image_info_size, image_info,
> >                              descriptor_version, descriptor_count,
> >                              descriptor_size,
> >                              package_version, package_version_name,
> > -                            &efi_firmware_image_type_uboot_raw);
> > +                            part_guid_arr, nparts);
> >
> > +out:
> > +     free(part_guid_arr);
> >       return EFI_EXIT(ret);
> >   }
> >
> >
>
>

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

* Re: [RESEND RFC PATCH 09/10] FWU: Add support for FWU Multi Bank Update feature
  2021-11-26 12:55   ` Heinrich Schuchardt
@ 2021-11-29 11:44     ` Sughosh Ganu
  0 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-11-29 11:44 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Alexander Graf,
	Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu

On Fri, 26 Nov 2021 at 18:25, Heinrich Schuchardt <xypron.glpk@gmx.de>
wrote:

> On 11/25/21 08:13, Sughosh Ganu wrote:
> > The FWU Multi Bank Update feature supports updation of firmware images
> > to one of multiple sets(also called banks) of images. The firmware
> > images are clubbed together in banks, with the system booting images
> > from the active bank. Information on the images such as which bank
> > they belong to is stored as part of the metadata structure, which is
> > stored on the same storage media as the firmware images on a dedicated
> > partition.
> >
> > At the time of update, the metadata is read to identify the bank to
> > which the images need to be flashed(update bank). On a successful
> > update, the metadata is modified to set the updated bank as active
> > bank to subsequently boot from.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >   include/fwu_metadata.h       |  10 ++
> >   lib/Kconfig                  |  32 ++++++
> >   lib/Makefile                 |   1 +
> >   lib/efi_loader/efi_capsule.c | 190 ++++++++++++++++++++++++++++++++++-
> >   lib/fwu_updates/Makefile     |  11 ++
> >   lib/fwu_updates/fwu.c        |  29 +++++-
> >   6 files changed, 269 insertions(+), 4 deletions(-)
> >   create mode 100644 lib/fwu_updates/Makefile
> >
> > diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
> > index 02897f33a8..2d276a019a 100644
> > --- a/include/fwu_metadata.h
> > +++ b/include/fwu_metadata.h
> > @@ -106,7 +106,16 @@ struct fwu_metadata_ops {
> >       EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \
> >                0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
> >
> > +#define FWU_OS_REQUEST_FW_REVERT_GUID \
> > +     EFI_GUID(0xacd58b4b, 0xc0e8, 0x475f, 0x99, 0xb5, \
> > +              0x6b, 0x3f, 0x7e, 0x07, 0xaa, 0xf0)
> > +
> > +#define FWU_OS_REQUEST_FW_ACCEPT_GUID \
> > +     EFI_GUID(0x0c996046, 0xbcc0, 0x4d04, 0x85, 0xec, \
> > +              0xe1, 0xfc, 0xed, 0xf1, 0xc6, 0xf8)
> > +
> >   #define FWU_METADATA_VERSION        0x1
> > +#define FWU_IMAGE_ACCEPTED   0x1
> >
> >   extern struct fwu_metadata_ops fwu_gpt_blk_ops;
> >
> > @@ -126,5 +135,6 @@ int fwu_plat_get_update_index(u32 *update_idx);
> >   int fwu_plat_get_blk_desc(struct blk_desc **desc);
> >   void fwu_plat_get_bootidx(void *boot_idx);
> >   int fwu_boottime_checks(void);
> > +int fwu_trial_state_ctr_start(void);
> >
> >   #endif /* _FWU_METADATA_H_ */
> > diff --git a/lib/Kconfig b/lib/Kconfig
> > index 807a4c6ade..7cb306317c 100644
> > --- a/lib/Kconfig
> > +++ b/lib/Kconfig
> > @@ -835,3 +835,35 @@ config PHANDLE_CHECK_SEQ
> >         When there are multiple device tree nodes with same name,
> >             enable this config option to distinguish them using
> >         phandles in fdtdec_get_alias_seq() function.
> > +
> > +config FWU_MULTI_BANK_UPDATE
> > +     bool "Enable FWU Multi Bank Update Feature"
>
> Why do we need a configuration variable? Having 1 bank is just a special
> case which should not use separate code.
>

This is needed to distinguish between using the update along with
maintaining the metadata, as described by the specification. With the case
of a single bank, I don't see any reason for having the metadata -- the
firmware update can be carried out using the capsule driver.


> > +     depends on EFI_HAVE_CAPSULE_SUPPORT
> > +     select PARTITION_TYPE_GUID
> > +     select EFI_SETUP_EARLY
> > +     help
> > +       Feature for updating firmware images on platforms having
> > +       multiple banks(copies) of the firmware images. One of the
> > +       bank is selected for updating all the firmware components
> > +
> > +config FWU_NUM_BANKS
> > +     int "Number of Banks defined by the platform"
> > +     depends on FWU_MULTI_BANK_UPDATE
>
> This should default to 1.
>

Like we discussed, I will check if this value and the #images_per_bank can
be incorporated in the metadata structure. If it gets added to the
metadata, the corresponding configs can be removed.


> > +     help
> > +       Define the number of banks of firmware images on a platform
> > +
> > +config FWU_NUM_IMAGES_PER_BANK
> > +     int "Number of firmware images per bank"
> > +     depends on FWU_MULTI_BANK_UPDATE
>
> This dependency makes no sense. Even if I have one bank I can have
> multiple images.
>
> Why should this configuration variable be needed?
> The dfu variables and the capsule define how many images are updated.
>
> > +     help
> > +       Define the number of firmware images per bank. This value
> > +       should be the same for all the banks.
> > +
> > +config FWU_TRIAL_STATE_CNT
> > +     int "Number of times system boots in Trial State"
> > +     depends on FWU_MULTI_BANK_UPDATE
> > +     default 3
> > +     help
> > +       With FWU Multi Bank Update feature enabled, number of times
> > +       the platform is allowed to boot in Trial State after an
> > +       update.
>
> Do you mean:
>
> "This is the number of boots in trial state before falling back to the
> last successfully used bank."
>

Yes, this is more clear. Will change the wording. Thanks.

-sughosh


>
> Best regards
>
> Heinrich
>
> > diff --git a/lib/Makefile b/lib/Makefile
> > index 5ddbc77ed6..bc5c1e22fc 100644
> > --- a/lib/Makefile
> > +++ b/lib/Makefile
> > @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/
> >   obj-$(CONFIG_EFI_LOADER) += efi_driver/
> >   obj-$(CONFIG_EFI_LOADER) += efi_loader/
> >   obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/
> > +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/
> >   obj-$(CONFIG_LZMA) += lzma/
> >   obj-$(CONFIG_BZIP2) += bzip2/
> >   obj-$(CONFIG_TIZEN) += tizen/
> > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> > index 502bcfca6e..40b9e87e92 100644
> > --- a/lib/efi_loader/efi_capsule.c
> > +++ b/lib/efi_loader/efi_capsule.c
> > @@ -14,6 +14,7 @@
> >   #include <env.h>
> >   #include <fdtdec.h>
> >   #include <fs.h>
> > +#include <fwu_metadata.h>
> >   #include <malloc.h>
> >   #include <mapmem.h>
> >   #include <sort.h>
> > @@ -30,6 +31,13 @@ static const efi_guid_t
> efi_guid_firmware_management_capsule_id =
> >               EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> >   const efi_guid_t efi_guid_firmware_management_protocol =
> >               EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
> > +const efi_guid_t fwu_guid_os_request_fw_revert =
> > +             FWU_OS_REQUEST_FW_REVERT_GUID;
> > +const efi_guid_t fwu_guid_os_request_fw_accept =
> > +             FWU_OS_REQUEST_FW_ACCEPT_GUID;
> > +
> > +__maybe_unused static u32 update_index;
> > +__maybe_unused static bool capsule_update;
> >
> >   #ifdef CONFIG_EFI_CAPSULE_ON_DISK
> >   /* for file system access */
> > @@ -403,10 +411,13 @@ static efi_status_t efi_capsule_update_firmware(
> >       void *image_binary, *vendor_code;
> >       efi_handle_t *handles;
> >       efi_uintn_t no_handles;
> > -     int item;
> > +     int item, alt_no;
> >       struct efi_firmware_management_protocol *fmp;
> >       u16 *abort_reason;
> > +     efi_guid_t image_type_id;
> >       efi_status_t ret = EFI_SUCCESS;
> > +     int status;
> > +     u8 image_index;
> >
> >       /* sanity check */
> >       if (capsule_data->header_size < sizeof(*capsule) ||
> > @@ -481,8 +492,36 @@ static efi_status_t efi_capsule_update_firmware(
> >                               goto out;
> >               }
> >
> > +             if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> > +                     /*
> > +                      * Based on the value of update_image_type_id,
> > +                      * derive the alt number value. This will be
> > +                      * passed as update_image_index to the
> > +                      * set_image function.
> > +                      */
> > +                     image_type_id = image->update_image_type_id;
> > +                     status = fwu_get_image_alt_num(image_type_id,
> > +                                                    update_index,
> > +                                                    &alt_no);
> > +                     if (status < 0) {
> > +                             log_err("Unable to get the alt no for the
> image type %pUl\n",
> > +                                     &image_type_id);
> > +                             if (status == -ENODEV || status == -EIO)
> > +                                     ret = EFI_DEVICE_ERROR;
> > +                             else if (status == -ENOMEM)
> > +                                     ret = EFI_OUT_OF_RESOURCES;
> > +                             else if (status == -ERANGE || status ==
> -EINVAL)
> > +                                     ret = EFI_INVALID_PARAMETER;
> > +                             goto out;
> > +                     }
> > +                     log_debug("alt_no %u for Image Type Id %pUl\n",
> > +                               alt_no, &image_type_id);
> > +                     image_index = alt_no;
> > +             } else {
> > +                     image_index = image->update_image_index;
> > +             }
> >               abort_reason = NULL;
> > -             ret = EFI_CALL(fmp->set_image(fmp,
> image->update_image_index,
> > +             ret = EFI_CALL(fmp->set_image(fmp, image_index,
> >                                             image_binary,
> >                                             image_binary_size,
> >                                             vendor_code, NULL,
> > @@ -493,6 +532,24 @@ static efi_status_t efi_capsule_update_firmware(
> >                       efi_free_pool(abort_reason);
> >                       goto out;
> >               }
> > +
> > +             if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> > +                     status = fwu_clear_accept_image(&image_type_id,
> > +                                                     update_index);
> > +                     if (status < 0) {
> > +                             log_err("Unable to clear the accept bit
> for the image %pUl\n",
> > +                                     &image_type_id);
> > +                             if (status == -ENODEV || status == -EIO)
> > +                                     ret = EFI_DEVICE_ERROR;
> > +                             else if (status == -ENOMEM)
> > +                                     ret = EFI_OUT_OF_RESOURCES;
> > +                             else if (status == -ERANGE || status ==
> -EINVAL)
> > +                                     ret = EFI_INVALID_PARAMETER;
> > +                             goto out;
> > +                     }
> > +                     log_debug("Cleared out accepted bit for Image
> %pUl\n", &image_type_id);
> > +             }
> > +
> >       }
> >
> >   out:
> > @@ -527,6 +584,9 @@ efi_status_t EFIAPI efi_update_capsule(
> >               u64 scatter_gather_list)
> >   {
> >       struct efi_capsule_header *capsule;
> > +     efi_guid_t *image_guid;
> > +     u32 active_idx;
> > +     int status;
> >       unsigned int i;
> >       efi_status_t ret;
> >
> > @@ -538,6 +598,16 @@ efi_status_t EFIAPI efi_update_capsule(
> >               goto out;
> >       }
> >
> > +     if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> > +             /* First obtain the update_index from the platform */
> > +             status = fwu_plat_get_update_index(&update_index);
> > +             if (status < 0) {
> > +                     log_err("Failed to get the FWU update_index
> value\n");
> > +                     ret = EFI_DEVICE_ERROR;
> > +                     goto out;
> > +             }
> > +     }
> > +
> >       ret = EFI_SUCCESS;
> >       for (i = 0, capsule = *capsule_header_array; i < capsule_count;
> >            i++, capsule = *(++capsule_header_array)) {
> > @@ -553,6 +623,55 @@ efi_status_t EFIAPI efi_update_capsule(
> >               if (!guidcmp(&capsule->capsule_guid,
> >                            &efi_guid_firmware_management_capsule_id)) {
> >                       ret  = efi_capsule_update_firmware(capsule);
> > +                     capsule_update = true;
> > +             } else if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> > +                     capsule_update = false;
> > +                     if (!guidcmp(&capsule->capsule_guid,
> > +                                  &fwu_guid_os_request_fw_revert)) {
> > +                             /*
> > +                              * One of the previously updated image has
> > +                              * failed the OS acceptance test. OS has
> > +                              * requested to revert back to the earlier
> > +                              * boot index
> > +                              */
> > +                             status =
> fwu_revert_boot_index(&active_idx);
> > +                             if (status < 0) {
> > +                                     log_err("Failed to revert the FWU
> boot index\n");
> > +                                     if (status == -ENODEV ||
> > +                                         status == -ERANGE ||
> > +                                         status == -EIO)
> > +                                             ret = EFI_DEVICE_ERROR;
> > +                                     else if (status == -EINVAL)
> > +                                             ret =
> EFI_INVALID_PARAMETER;
> > +                                     else if (status == -ENOMEM)
> > +                                             ret = EFI_OUT_OF_RESOURCES;
> > +                             } else {
> > +                                     ret = EFI_SUCCESS;
> > +                                     log_err("Reverted the FWU
> active_index to %u. Recommend rebooting the system\n",
> > +                                             active_idx);
> > +                             }
> > +                     } else if (!guidcmp(&capsule->capsule_guid,
> > +
>  &fwu_guid_os_request_fw_accept)) {
> > +                             /*
> > +                              * Image accepted by the OS. Set the
> acceptance
> > +                              * status for the image.
> > +                              */
> > +                             image_guid = (void *)(char *)capsule +
> > +                                     capsule->header_size;
> > +                             status = fwu_accept_image(image_guid);
> > +                             if (status < 0) {
> > +                                     if (status == -ENODEV ||
> > +                                         status == -ERANGE ||
> > +                                         status == -EIO)
> > +                                             ret = EFI_DEVICE_ERROR;
> > +                                     else if (status == -EINVAL)
> > +                                             ret =
> EFI_INVALID_PARAMETER;
> > +                                     else if (status == -ENOMEM)
> > +                                             ret = EFI_OUT_OF_RESOURCES;
> > +                             } else {
> > +                                     ret = EFI_SUCCESS;
> > +                             }
> > +                     }
> >               } else {
> >                       log_err("Unsupported capsule type: %pUl\n",
> >                               &capsule->capsule_guid);
> > @@ -563,6 +682,36 @@ efi_status_t EFIAPI efi_update_capsule(
> >                       goto out;
> >       }
> >
> > +     /*
> > +      * Update the metadata once all the capsules have
> > +      * been updated. This is done only for the Runtime
> > +      * capsule update service.
> > +      * The update_index value now gets written to the
> > +      * active_index and the update_index value also
> > +      * gets updated.
> > +      * For the capsule-on-disk feature, the updation
> > +      * of the metadata happens in efi_launch_capsules
> > +      */
> > +     if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE) &&
> > +         !IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK)) {
> > +             status = fwu_update_active_index(update_index);
> > +             if (status < 0) {
> > +                     log_err("Failed to update FWU metadata index
> values\n");
> > +                     if (status == -EINVAL || status == -ERANGE)
> > +                             ret = EFI_INVALID_PARAMETER;
> > +                     else if (status == -ENODEV || status == -EIO)
> > +                             ret = EFI_DEVICE_ERROR;
> > +                     else if (status == -ENOMEM)
> > +                             ret = EFI_OUT_OF_RESOURCES;
> > +             } else {
> > +                     status = fwu_trial_state_ctr_start();
> > +                     if (status < 0)
> > +                             ret = EFI_DEVICE_ERROR;
> > +                     else
> > +                             ret = EFI_SUCCESS;
> > +             }
> > +     }
> > +
> >       if (IS_ENABLED(CONFIG_EFI_ESRT)) {
> >               /* Rebuild the ESRT to reflect any updated FW images. */
> >               ret = efi_esrt_populate();
> > @@ -1075,8 +1224,10 @@ efi_status_t efi_launch_capsules(void)
> >   {
> >       struct efi_capsule_header *capsule = NULL;
> >       u16 **files;
> > +     int status;
> >       unsigned int nfiles, index, i;
> >       efi_status_t ret;
> > +     bool update_status = true;
> >
> >       if (!check_run_capsules())
> >               return EFI_SUCCESS;
> > @@ -1104,9 +1255,11 @@ efi_status_t efi_launch_capsules(void)
> >               ret = efi_capsule_read_file(files[i], &capsule);
> >               if (ret == EFI_SUCCESS) {
> >                       ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0));
> > -                     if (ret != EFI_SUCCESS)
> > +                     if (ret != EFI_SUCCESS) {
> >                               log_err("Applying capsule %ls failed\n",
> >                                       files[i]);
> > +                             update_status = false;
> > +                     }
> >
> >                       /* create CapsuleXXXX */
> >                       set_capsule_result(index, capsule, ret);
> > @@ -1114,6 +1267,7 @@ efi_status_t efi_launch_capsules(void)
> >                       free(capsule);
> >               } else {
> >                       log_err("Reading capsule %ls failed\n", files[i]);
> > +                     update_status = false;
> >               }
> >               /* delete a capsule either in case of success or failure */
> >               ret = efi_capsule_delete_file(files[i]);
> > @@ -1121,7 +1275,37 @@ efi_status_t efi_launch_capsules(void)
> >                       log_err("Deleting capsule %ls failed\n",
> >                               files[i]);
> >       }
> > +
> >       efi_capsule_scan_done();
> > +     if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
> > +             if (update_status == true && capsule_update == true) {
> > +                     /*
> > +                      * All the capsules have been updated successfully,
> > +                      * update the FWU metadata.
> > +                      */
> > +                     log_debug("Update Complete. Now updating
> active_index to %u\n",
> > +                               update_index);
> > +                     status = fwu_update_active_index(update_index);
> > +                     if (status < 0) {
> > +                             log_err("Failed to update FWU metadata
> index values\n");
> > +                             if (status == -EINVAL || status == -ERANGE)
> > +                                     ret = EFI_INVALID_PARAMETER;
> > +                             else if (status == -ENODEV || status ==
> -EIO)
> > +                                     ret = EFI_DEVICE_ERROR;
> > +                             else if (status == -ENOMEM)
> > +                                     ret = EFI_OUT_OF_RESOURCES;
> > +                     } else {
> > +                             log_debug("Successfully updated the
> active_index\n");
> > +                             status = fwu_trial_state_ctr_start();
> > +                             if (status < 0)
> > +                                     ret = EFI_DEVICE_ERROR;
> > +                             else
> > +                                     ret = EFI_SUCCESS;
> > +                     }
> > +             } else if (capsule_update == true && update_status ==
> false) {
> > +                     log_err("All capsules were not updated. Not
> updating metadata\n");
> > +             }
> > +     }
> >
> >       for (i = 0; i < nfiles; i++)
> >               free(files[i]);
> > diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile
> > new file mode 100644
> > index 0000000000..f6fa8290c1
> > --- /dev/null
> > +++ b/lib/fwu_updates/Makefile
> > @@ -0,0 +1,11 @@
> > +# SPDX-License-Identifier: GPL-2.0+
> > +#
> > +# Copyright (c) 2021, Linaro Limited
> > +#
> > +
> > +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_metadata.o
> > +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o
> > +
> > +ifdef CONFIG_EFI_PARTITION
> > +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_metadata_gpt_blk.o
> > +endif
> > diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c
> > index 2e1904b912..bbd3dc249f 100644
> > --- a/lib/fwu_updates/fwu.c
> > +++ b/lib/fwu_updates/fwu.c
> > @@ -45,7 +45,7 @@ static int fwu_trial_state_check(void)
> >               log_info("System booting in Trial State\n");
> >               var_attributes = EFI_VARIABLE_NON_VOLATILE |
> >                       EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > -                     EFI_VARIABLE_RUNTIME_ACCESS,
> > +                     EFI_VARIABLE_RUNTIME_ACCESS;
> >               status = efi_get_variable_int(L"TrialStateCtr",
> >                                             &efi_global_variable_guid,
> >                                             &var_attributes,
> > @@ -99,6 +99,33 @@ out:
> >       return ret;
> >   }
> >
> > +int fwu_trial_state_ctr_start(void)
> > +{
> > +     int ret;
> > +     u32 var_attributes;
> > +     efi_status_t status;
> > +     efi_uintn_t var_size;
> > +     u16 trial_state_ctr;
> > +
> > +     var_size = (efi_uintn_t)sizeof(trial_state_ctr);
> > +     var_attributes = EFI_VARIABLE_NON_VOLATILE |
> > +             EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +             EFI_VARIABLE_RUNTIME_ACCESS;
> > +
> > +     trial_state_ctr = ret = 0;
> > +     status = efi_set_variable_int(L"TrialStateCtr",
> > +                                   &efi_global_variable_guid,
> > +                                   var_attributes,
> > +                                   var_size,
> > +                                   &trial_state_ctr, false);
> > +     if (status != EFI_SUCCESS) {
> > +             log_err("Unable to increment TrialStateCtr variable\n");
> > +             ret = -1;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> >   int fwu_boottime_checks(void)
> >   {
> >       int ret;
> >
>
>

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

* Re: [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata
  2021-11-25  7:12 ` [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata Sughosh Ganu
  2021-11-26 11:35   ` Ilias Apalodimas
@ 2021-11-30 12:57   ` Heinrich Schuchardt
  2021-12-01  5:36     ` Sughosh Ganu
  2021-12-01  7:46     ` Ilias Apalodimas
  2021-12-01  6:26   ` Simon Glass
  2 siblings, 2 replies; 36+ messages in thread
From: Heinrich Schuchardt @ 2021-11-30 12:57 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: Patrick Delaunay, Patrice Chotard, Alexander Graf, Simon Glass,
	Bin Meng, Peng Fan, AKASHI Takahiro, Ilias Apalodimas,
	Jose Marinho, Grant Likely, Jason Liu, u-boot

On 11/25/21 08:12, Sughosh Ganu wrote:
> In the FWU Multi Bank Update feature, the information about the
> updatable images is stored as part of the metadata, which is stored on
> a dedicated partition. Add the metadata structure, and functions to
> access the metadata. These are generic API's, and implementations can
> be added based on parameters like how the metadata partition is
> accessed and what type of storage device houses the metadata.
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>   include/fwu_metadata.h         | 125 +++++++++++++++
>   lib/fwu_updates/fwu_metadata.c | 275 +++++++++++++++++++++++++++++++++
>   2 files changed, 400 insertions(+)
>   create mode 100644 include/fwu_metadata.h
>   create mode 100644 lib/fwu_updates/fwu_metadata.c
>
> diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
> new file mode 100644
> index 0000000000..e692ef7506
> --- /dev/null
> +++ b/include/fwu_metadata.h
> @@ -0,0 +1,125 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (c) 2021, Linaro Limited
> + */
> +
> +#if !defined _FWU_METADATA_H_
> +#define _FWU_METADATA_H_
> +
> +#include <blk.h>
> +#include <efi.h>
> +#include <uuid.h>
> +
> +#include <linux/types.h>
> +
> +/**
> + * struct fwu_image_bank_info - firmware image information
> + * @image_uuid: Guid value of the image in this bank
> + * @accepted: Acceptance status of the image
> + * @reserved: Reserved
> + *
> + * The structure contains image specific fields which are
> + * used to identify the image and to specify the image's
> + * acceptance status
> + */
> +struct fwu_image_bank_info {
> +	efi_guid_t  image_uuid;
> +	u32 accepted;
> +	u32 reserved;
> +};
> +
> +/**
> + * struct fwu_image_entry - information for a particular type of image
> + * @image_type_uuid: Guid value for identifying the image type
> + * @location_uuid: Guid of the storage volume where the image is located
> + * @img_bank_info: Array containing properties of images
> + *
> + * This structure contains information on various types of updatable
> + * firmware images. Each image type then contains an array of image
> + * information per bank.
> + */
> +struct fwu_image_entry {
> +	efi_guid_t image_type_uuid;
> +	efi_guid_t location_uuid;
> +	struct fwu_image_bank_info img_bank_info[CONFIG_FWU_NUM_BANKS];
> +};
> +
> +/**
> + * struct fwu_metadata - Metadata structure for multi-bank updates
> + * @crc32: crc32 value for the metadata
> + * @version: Metadata version
> + * @active_index: Index of the bank currently used for booting images
> + * @previous_active_inde: Index of the bank used before the current bank
> + *                        being used for booting
> + * @img_entry: Array of information on various firmware images that can
> + *             be updated
> + *
> + * This structure is used to store all the needed information for performing
> + * multi bank updates on the platform. This contains info on the bank being
> + * used to boot along with the information needed for identification of
> + * individual images
> + */
> +struct fwu_metadata {
> +	u32 crc32;
> +	u32 version;
> +	u32 active_index;
> +	u32 previous_active_index;
> +
> +	struct fwu_image_entry img_entry[CONFIG_FWU_NUM_IMAGES_PER_BANK];
> +};
> +
> +/**
> + * @get_active_index: get the current active_index value
> + * @update_active_index: update the active_index value
> + * @fill_partition_guid_array: fill the array with guid values of the
> + *                             partitions found on the storage media
> + * @get_image_alt_num: get the alt number to be used for the image
> + * @metadata_check: check the validity of the metadata partitions
> + * @revert_boot_index: set the active_index to previous_active_index
> + * @set_accept_image: set the accepted bit for the image
> + * @clear_accept_image: clear the accepted bit for the image
> + * @get_metadata() - Get a metadata copy
> + */
> +struct fwu_metadata_ops {
> +	int (*get_active_index)(u32 *active_idx);
> +
> +	int (*update_active_index)(u32 active_idx);
> +
> +	int (*fill_partition_guid_array)(efi_guid_t **part_guid_arr,
> +					 u32 *nparts);
> +
> +	int (*get_image_alt_num)(efi_guid_t image_type_id, u32 update_bank,
> +				 int *alt_num);
> +
> +	int (*metadata_check)(void);
> +
> +	int (*revert_boot_index)(u32 *active_idx);
> +
> +	int (*set_accept_image)(efi_guid_t *img_type_id);
> +
> +	int (*clear_accept_image)(efi_guid_t *img_type_id, u32 bank);
> +
> +	int (*get_metadata)(struct fwu_metadata **metadata);
> +};
> +
> +#define FWU_METADATA_GUID \
> +	EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \
> +		 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
> +
> +#define FWU_METADATA_VERSION	0x1
> +
> +extern struct fwu_metadata_ops fwu_gpt_blk_ops;
> +
> +struct fwu_metadata_ops *get_plat_fwu_metadata_ops(void);
> +int fwu_get_active_index(u32 *active_idx);
> +int fwu_update_active_index(u32 active_idx);
> +int fwu_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32 *nparts);
> +int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> +			  int *alt_num);
> +int fwu_metadata_check(void);
> +int fwu_revert_boot_index(u32 *active_idx);
> +int fwu_accept_image(efi_guid_t *img_type_id);
> +int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank);
> +int fwu_get_metadata(struct fwu_metadata **metadata);
> +
> +#endif /* _FWU_METADATA_H_ */
> diff --git a/lib/fwu_updates/fwu_metadata.c b/lib/fwu_updates/fwu_metadata.c
> new file mode 100644
> index 0000000000..ebc3eaa04a
> --- /dev/null
> +++ b/lib/fwu_updates/fwu_metadata.c
> @@ -0,0 +1,275 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (c) 2021, Linaro Limited
> + */
> +
> +#include <fwu_metadata.h>
> +
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +
> +static inline struct fwu_metadata_ops *get_fwu_metadata_ops(void)
> +{
> +	return get_plat_fwu_metadata_ops();
> +}
> +
> +/**
> + * fwu_get_active_index() - Get active_index from the metadata
> + * @active_idx: active_index value to be read
> + *
> + * Read the active_index field from the metadata and place it in
> + * the variable pointed to be the function argument.
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_get_active_index(u32 *active_idx)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->get_active_index) {
> +		log_err("get_active_index() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->get_active_index(active_idx);
> +}
> +
> +/**
> + * fwu_update_active_index() - Update active_index from the metadata
> + * @active_idx: active_index value to be updated
> + *
> + * Update the active_index field in the metadata
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_update_active_index(u32 active_idx)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->update_active_index) {
> +		log_err("update_active_index() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->update_active_index(active_idx);
> +}
> +
> +/**
> + * fwu_fill_partition_guid_array() - Fill the part_guid_arr array with the guid's of
> + *                                   the partitions
> + * @part_guid_arr: array of partition guid's
> + * @nparts: Number of gpt partitions on the device
> + *
> + * Get the information on the partition guid's, filling the array with the guid
> + * values and also the number of partitions.
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32 *nparts)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->fill_partition_guid_array) {
> +		log_err("fill_partition_guid_array() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->fill_partition_guid_array(part_guid_arr, nparts);
> +}
> +
> +/**
> + * fwu_get_image_alt_num() - Get the dfu alt number to be used for capsule update
> + * @image_type_id: image guid as passed in the capsule
> + * @update_bank: Bank to which the update is to be made
> + * @alt_num: The alt_num for the image
> + *
> + * Based on the guid value passed in the capsule, along with the bank to which the
> + * image needs to be updated, get the dfu alt number which will be used for the
> + * capsule update
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> +			  int *alt_num)
> +{
> +	struct fwu_metadata_ops *ops;

The metadata is an untrusted information source and hence MUST NOT be
used to map the image_type_id to the DFU alt_number. Don't invite for an
denial of service attack.

The signed capsule would be a good place for storing the DFU mapping.

Best regards

Heinrich


> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->get_image_alt_num) {
> +		log_err("get_image_alt_num() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->get_image_alt_num(image_type_id, update_bank, alt_num);
> +}
> +
> +/**
> + * fwu_metadata_check() - Check if the metadata is valid
> + *
> + * Validate both copies of metadata. If one of the copies
> + * has gone bad, restore it from the other bad copy.
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_metadata_check(void)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->metadata_check) {
> +		log_err("metadata_check() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->metadata_check();
> +}
> +
> +/**
> + * fwu_revert_boot_index() - Revert the active index in the metadata
> + * @active_idx: Value of the updated active_index
> + *
> + * Revert the active_index value in the metadata, by swapping the values
> + * of active_index and previous_active_index in both copies of the
> + * metadata.
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_revert_boot_index(u32 *active_idx)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->revert_boot_index) {
> +		log_err("revert_boot_index() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->revert_boot_index(active_idx);
> +}
> +
> +/**
> + * fwu_accept_image() - Set the Acceptance bit for the image
> + * @img_type_id: Guid of the image type for which the accepted bit is to be
> + *               cleared
> + *
> + * Set the accepted bit for the image specified by the img_guid parameter. This
> + * indicates acceptance of image for subsequent boots by some governing component
> + * like OS(or firmware).
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_accept_image(efi_guid_t *img_type_id)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->set_accept_image) {
> +		log_err("set_accept_image() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->set_accept_image(img_type_id);
> +}
> +
> +/**
> + * fwu_clear_accept_image() - Clear the Acceptance bit for the image
> + * @img_type_id: Guid of the image type for which the accepted bit is to be
> + *               cleared
> + *
> + * Clear the accepted bit for the image type specified by the img_type_id parameter.
> + * This function is called after the image has been updated. The accepted bit is
> + * cleared to be set subsequently after passing the image acceptance criteria, by
> + * either the OS(or firmware)
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->clear_accept_image) {
> +		log_err("clear_accept_image() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->clear_accept_image(img_type_id, bank);
> +}
> +
> +/**
> + * fwu_get_metadata() - Get a metadata copy
> + * @metadata: Copy of the metadata
> + *
> + * Get a valid copy of the metadata.
> + *
> + * Return: 0 if OK, -ve on error
> + *
> + */
> +int fwu_get_metadata(struct fwu_metadata **metadata)
> +{
> +	struct fwu_metadata_ops *ops;
> +
> +	ops = get_fwu_metadata_ops();
> +	if (!ops) {
> +		log_err("Unable to get fwu ops\n");
> +		return -EPROTONOSUPPORT;
> +	}
> +
> +	if (!ops->get_metadata) {
> +		log_err("get_metadata() method not defined for the platform\n");
> +		return -ENOSYS;
> +	}
> +
> +	return ops->get_metadata(metadata);
> +}
>


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

* Re: [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata
  2021-11-30 12:57   ` Heinrich Schuchardt
@ 2021-12-01  5:36     ` Sughosh Ganu
  2021-12-01  7:50       ` Ilias Apalodimas
  2021-12-01  7:46     ` Ilias Apalodimas
  1 sibling, 1 reply; 36+ messages in thread
From: Sughosh Ganu @ 2021-12-01  5:36 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Patrick Delaunay, Patrice Chotard, Alexander Graf, Simon Glass,
	Bin Meng, Peng Fan, AKASHI Takahiro, Ilias Apalodimas,
	Jose Marinho, Grant Likely, Jason Liu, u-boot

On Tue, 30 Nov 2021 at 18:33, Heinrich Schuchardt <xypron.glpk@gmx.de>
wrote:

> On 11/25/21 08:12, Sughosh Ganu wrote:
> > In the FWU Multi Bank Update feature, the information about the
> > updatable images is stored as part of the metadata, which is stored on
> > a dedicated partition. Add the metadata structure, and functions to
> > access the metadata. These are generic API's, and implementations can
> > be added based on parameters like how the metadata partition is
> > accessed and what type of storage device houses the metadata.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >   include/fwu_metadata.h         | 125 +++++++++++++++
> >   lib/fwu_updates/fwu_metadata.c | 275 +++++++++++++++++++++++++++++++++
> >   2 files changed, 400 insertions(+)
> >   create mode 100644 include/fwu_metadata.h
> >   create mode 100644 lib/fwu_updates/fwu_metadata.c
> >
> > diff --git a/include/fwu_metadata.h b/include/fwu_metadata.h
> > new file mode 100644
> > index 0000000000..e692ef7506
> > --- /dev/null
> > +++ b/include/fwu_metadata.h
> > @@ -0,0 +1,125 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + * Copyright (c) 2021, Linaro Limited
> > + */
> > +
> > +#if !defined _FWU_METADATA_H_
> > +#define _FWU_METADATA_H_
> > +
> > +#include <blk.h>
> > +#include <efi.h>
> > +#include <uuid.h>
> > +
> > +#include <linux/types.h>
> > +
> > +/**
> > + * struct fwu_image_bank_info - firmware image information
> > + * @image_uuid: Guid value of the image in this bank
> > + * @accepted: Acceptance status of the image
> > + * @reserved: Reserved
> > + *
> > + * The structure contains image specific fields which are
> > + * used to identify the image and to specify the image's
> > + * acceptance status
> > + */
> > +struct fwu_image_bank_info {
> > +     efi_guid_t  image_uuid;
> > +     u32 accepted;
> > +     u32 reserved;
> > +};
> > +
> > +/**
> > + * struct fwu_image_entry - information for a particular type of image
> > + * @image_type_uuid: Guid value for identifying the image type
> > + * @location_uuid: Guid of the storage volume where the image is located
> > + * @img_bank_info: Array containing properties of images
> > + *
> > + * This structure contains information on various types of updatable
> > + * firmware images. Each image type then contains an array of image
> > + * information per bank.
> > + */
> > +struct fwu_image_entry {
> > +     efi_guid_t image_type_uuid;
> > +     efi_guid_t location_uuid;
> > +     struct fwu_image_bank_info img_bank_info[CONFIG_FWU_NUM_BANKS];
> > +};
> > +
> > +/**
> > + * struct fwu_metadata - Metadata structure for multi-bank updates
> > + * @crc32: crc32 value for the metadata
> > + * @version: Metadata version
> > + * @active_index: Index of the bank currently used for booting images
> > + * @previous_active_inde: Index of the bank used before the current bank
> > + *                        being used for booting
> > + * @img_entry: Array of information on various firmware images that can
> > + *             be updated
> > + *
> > + * This structure is used to store all the needed information for
> performing
> > + * multi bank updates on the platform. This contains info on the bank
> being
> > + * used to boot along with the information needed for identification of
> > + * individual images
> > + */
> > +struct fwu_metadata {
> > +     u32 crc32;
> > +     u32 version;
> > +     u32 active_index;
> > +     u32 previous_active_index;
> > +
> > +     struct fwu_image_entry img_entry[CONFIG_FWU_NUM_IMAGES_PER_BANK];
> > +};
> > +
> > +/**
> > + * @get_active_index: get the current active_index value
> > + * @update_active_index: update the active_index value
> > + * @fill_partition_guid_array: fill the array with guid values of the
> > + *                             partitions found on the storage media
> > + * @get_image_alt_num: get the alt number to be used for the image
> > + * @metadata_check: check the validity of the metadata partitions
> > + * @revert_boot_index: set the active_index to previous_active_index
> > + * @set_accept_image: set the accepted bit for the image
> > + * @clear_accept_image: clear the accepted bit for the image
> > + * @get_metadata() - Get a metadata copy
> > + */
> > +struct fwu_metadata_ops {
> > +     int (*get_active_index)(u32 *active_idx);
> > +
> > +     int (*update_active_index)(u32 active_idx);
> > +
> > +     int (*fill_partition_guid_array)(efi_guid_t **part_guid_arr,
> > +                                      u32 *nparts);
> > +
> > +     int (*get_image_alt_num)(efi_guid_t image_type_id, u32 update_bank,
> > +                              int *alt_num);
> > +
> > +     int (*metadata_check)(void);
> > +
> > +     int (*revert_boot_index)(u32 *active_idx);
> > +
> > +     int (*set_accept_image)(efi_guid_t *img_type_id);
> > +
> > +     int (*clear_accept_image)(efi_guid_t *img_type_id, u32 bank);
> > +
> > +     int (*get_metadata)(struct fwu_metadata **metadata);
> > +};
> > +
> > +#define FWU_METADATA_GUID \
> > +     EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \
> > +              0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
> > +
> > +#define FWU_METADATA_VERSION 0x1
> > +
> > +extern struct fwu_metadata_ops fwu_gpt_blk_ops;
> > +
> > +struct fwu_metadata_ops *get_plat_fwu_metadata_ops(void);
> > +int fwu_get_active_index(u32 *active_idx);
> > +int fwu_update_active_index(u32 active_idx);
> > +int fwu_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32
> *nparts);
> > +int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> > +                       int *alt_num);
> > +int fwu_metadata_check(void);
> > +int fwu_revert_boot_index(u32 *active_idx);
> > +int fwu_accept_image(efi_guid_t *img_type_id);
> > +int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank);
> > +int fwu_get_metadata(struct fwu_metadata **metadata);
> > +
> > +#endif /* _FWU_METADATA_H_ */
> > diff --git a/lib/fwu_updates/fwu_metadata.c
> b/lib/fwu_updates/fwu_metadata.c
> > new file mode 100644
> > index 0000000000..ebc3eaa04a
> > --- /dev/null
> > +++ b/lib/fwu_updates/fwu_metadata.c
> > @@ -0,0 +1,275 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (c) 2021, Linaro Limited
> > + */
> > +
> > +#include <fwu_metadata.h>
> > +
> > +#include <linux/errno.h>
> > +#include <linux/types.h>
> > +
> > +static inline struct fwu_metadata_ops *get_fwu_metadata_ops(void)
> > +{
> > +     return get_plat_fwu_metadata_ops();
> > +}
> > +
> > +/**
> > + * fwu_get_active_index() - Get active_index from the metadata
> > + * @active_idx: active_index value to be read
> > + *
> > + * Read the active_index field from the metadata and place it in
> > + * the variable pointed to be the function argument.
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_get_active_index(u32 *active_idx)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->get_active_index) {
> > +             log_err("get_active_index() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->get_active_index(active_idx);
> > +}
> > +
> > +/**
> > + * fwu_update_active_index() - Update active_index from the metadata
> > + * @active_idx: active_index value to be updated
> > + *
> > + * Update the active_index field in the metadata
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_update_active_index(u32 active_idx)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->update_active_index) {
> > +             log_err("update_active_index() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->update_active_index(active_idx);
> > +}
> > +
> > +/**
> > + * fwu_fill_partition_guid_array() - Fill the part_guid_arr array with
> the guid's of
> > + *                                   the partitions
> > + * @part_guid_arr: array of partition guid's
> > + * @nparts: Number of gpt partitions on the device
> > + *
> > + * Get the information on the partition guid's, filling the array with
> the guid
> > + * values and also the number of partitions.
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32
> *nparts)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->fill_partition_guid_array) {
> > +             log_err("fill_partition_guid_array() method not defined
> for the platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->fill_partition_guid_array(part_guid_arr, nparts);
> > +}
> > +
> > +/**
> > + * fwu_get_image_alt_num() - Get the dfu alt number to be used for
> capsule update
> > + * @image_type_id: image guid as passed in the capsule
> > + * @update_bank: Bank to which the update is to be made
> > + * @alt_num: The alt_num for the image
> > + *
> > + * Based on the guid value passed in the capsule, along with the bank
> to which the
> > + * image needs to be updated, get the dfu alt number which will be used
> for the
> > + * capsule update
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> > +                       int *alt_num)
> > +{
> > +     struct fwu_metadata_ops *ops;
>
> The metadata is an untrusted information source and hence MUST NOT be
> used to map the image_type_id to the DFU alt_number. Don't invite for an
> denial of service attack.
>
> The signed capsule would be a good place for storing the DFU mapping.
>

I understand your concern with using dfu_alt_info for storing the
information needed for writing the capsule payload. However, putting the
information currently stored on the dfu_alt_info on a capsule should
require a spec change IMO. This should first be discussed and brought in as
part of the UEFI spec. Also, when you say signed capsule, please note not
the entire capsule gets signed -- it is only the capsule payloads that are
signed, not the headers. So putting the information currently stored in dfu
env var to the capsule would mean adding a header to the payload, which
would contain this information, and then the header plus payload would be
signed. However this is implemented, this would mean changes to the current
capsule format, and making this change without changing the spec would also
mean that we will also not be able to use the GenerateCapsule tool for
capsule generation. This is not a small change which can be included as a
patch in the FWU A/B update series, but should be taken up as a separate
exercise.

-sughosh


> Best regards
>
> Heinrich
>
>
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->get_image_alt_num) {
> > +             log_err("get_image_alt_num() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->get_image_alt_num(image_type_id, update_bank, alt_num);
> > +}
> > +
> > +/**
> > + * fwu_metadata_check() - Check if the metadata is valid
> > + *
> > + * Validate both copies of metadata. If one of the copies
> > + * has gone bad, restore it from the other bad copy.
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_metadata_check(void)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->metadata_check) {
> > +             log_err("metadata_check() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->metadata_check();
> > +}
> > +
> > +/**
> > + * fwu_revert_boot_index() - Revert the active index in the metadata
> > + * @active_idx: Value of the updated active_index
> > + *
> > + * Revert the active_index value in the metadata, by swapping the values
> > + * of active_index and previous_active_index in both copies of the
> > + * metadata.
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_revert_boot_index(u32 *active_idx)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->revert_boot_index) {
> > +             log_err("revert_boot_index() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->revert_boot_index(active_idx);
> > +}
> > +
> > +/**
> > + * fwu_accept_image() - Set the Acceptance bit for the image
> > + * @img_type_id: Guid of the image type for which the accepted bit is
> to be
> > + *               cleared
> > + *
> > + * Set the accepted bit for the image specified by the img_guid
> parameter. This
> > + * indicates acceptance of image for subsequent boots by some governing
> component
> > + * like OS(or firmware).
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_accept_image(efi_guid_t *img_type_id)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->set_accept_image) {
> > +             log_err("set_accept_image() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->set_accept_image(img_type_id);
> > +}
> > +
> > +/**
> > + * fwu_clear_accept_image() - Clear the Acceptance bit for the image
> > + * @img_type_id: Guid of the image type for which the accepted bit is
> to be
> > + *               cleared
> > + *
> > + * Clear the accepted bit for the image type specified by the
> img_type_id parameter.
> > + * This function is called after the image has been updated. The
> accepted bit is
> > + * cleared to be set subsequently after passing the image acceptance
> criteria, by
> > + * either the OS(or firmware)
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->clear_accept_image) {
> > +             log_err("clear_accept_image() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->clear_accept_image(img_type_id, bank);
> > +}
> > +
> > +/**
> > + * fwu_get_metadata() - Get a metadata copy
> > + * @metadata: Copy of the metadata
> > + *
> > + * Get a valid copy of the metadata.
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_get_metadata(struct fwu_metadata **metadata)
> > +{
> > +     struct fwu_metadata_ops *ops;
> > +
> > +     ops = get_fwu_metadata_ops();
> > +     if (!ops) {
> > +             log_err("Unable to get fwu ops\n");
> > +             return -EPROTONOSUPPORT;
> > +     }
> > +
> > +     if (!ops->get_metadata) {
> > +             log_err("get_metadata() method not defined for the
> platform\n");
> > +             return -ENOSYS;
> > +     }
> > +
> > +     return ops->get_metadata(metadata);
> > +}
> >
>
>

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

* Re: [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata
  2021-11-25  7:12 ` [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata Sughosh Ganu
  2021-11-26 11:35   ` Ilias Apalodimas
  2021-11-30 12:57   ` Heinrich Schuchardt
@ 2021-12-01  6:26   ` Simon Glass
  2021-12-01  6:42     ` Sughosh Ganu
  2 siblings, 1 reply; 36+ messages in thread
From: Simon Glass @ 2021-12-01  6:26 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu

Hi,

On Thu, 25 Nov 2021 at 00:13, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> In the FWU Multi Bank Update feature, the information about the
> updatable images is stored as part of the metadata, which is stored on
> a dedicated partition. Add the metadata structure, and functions to
> access the metadata. These are generic API's, and implementations can
> be added based on parameters like how the metadata partition is
> accessed and what type of storage device houses the metadata.
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>  include/fwu_metadata.h         | 125 +++++++++++++++
>  lib/fwu_updates/fwu_metadata.c | 275 +++++++++++++++++++++++++++++++++
>  2 files changed, 400 insertions(+)
>  create mode 100644 include/fwu_metadata.h
>  create mode 100644 lib/fwu_updates/fwu_metadata.c

What is this, please? Could you add a link to the docs in the commit
message and something in doc/ as well?

Regards,
Simon

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

* Re: [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata
  2021-12-01  6:26   ` Simon Glass
@ 2021-12-01  6:42     ` Sughosh Ganu
  0 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-12-01  6:42 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu

hi Simon,

On Wed, 1 Dec 2021 at 11:56, Simon Glass <sjg@chromium.org> wrote:

> Hi,
>
> On Thu, 25 Nov 2021 at 00:13, Sughosh Ganu <sughosh.ganu@linaro.org>
> wrote:
> >
> > In the FWU Multi Bank Update feature, the information about the
> > updatable images is stored as part of the metadata, which is stored on
> > a dedicated partition. Add the metadata structure, and functions to
> > access the metadata. These are generic API's, and implementations can
> > be added based on parameters like how the metadata partition is
> > accessed and what type of storage device houses the metadata.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >  include/fwu_metadata.h         | 125 +++++++++++++++
> >  lib/fwu_updates/fwu_metadata.c | 275 +++++++++++++++++++++++++++++++++
> >  2 files changed, 400 insertions(+)
> >  create mode 100644 include/fwu_metadata.h
> >  create mode 100644 lib/fwu_updates/fwu_metadata.c
>
> What is this, please? Could you add a link to the docs in the commit
> message and something in doc/ as well?
>

This is implementation of the FWU[1] specification from Arm. Some aspects
of the DependableBoot[2] specification are also implemented. I had put
pointers to the document in the cover letter[3]. I have not added any
documentation yet, since this is a rfc. Will do so once the initial reviews
are completed. Hope that is fine.

-sughosh

[1] - https://developer.arm.com/documentation/den0118/a
[2] -
https://staging-git.codelinaro.org/linaro/firmware-dual-banked-updates/test
[3] - https://lists.denx.de/pipermail/u-boot/2021-November/468232.html

>
> Regards,
> Simon
>

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

* Re: [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata
  2021-11-30 12:57   ` Heinrich Schuchardt
  2021-12-01  5:36     ` Sughosh Ganu
@ 2021-12-01  7:46     ` Ilias Apalodimas
  1 sibling, 0 replies; 36+ messages in thread
From: Ilias Apalodimas @ 2021-12-01  7:46 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Sughosh Ganu, Patrick Delaunay, Patrice Chotard, Alexander Graf,
	Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro, Jose Marinho,
	Grant Likely, Jason Liu, u-boot

Hi Heinrich,

[...]

> > +/**
> > + * fwu_get_image_alt_num() - Get the dfu alt number to be used for capsule update
> > + * @image_type_id: image guid as passed in the capsule
> > + * @update_bank: Bank to which the update is to be made
> > + * @alt_num: The alt_num for the image
> > + *
> > + * Based on the guid value passed in the capsule, along with the bank to which the
> > + * image needs to be updated, get the dfu alt number which will be used for the
> > + * capsule update
> > + *
> > + * Return: 0 if OK, -ve on error
> > + *
> > + */
> > +int fwu_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> > +                       int *alt_num)
> > +{
> > +     struct fwu_metadata_ops *ops;
>
> The metadata is an untrusted information source and hence MUST NOT be
> used to map the image_type_id to the DFU alt_number. Don't invite for an
> denial of service attack.

You are assuming here an attacker can manipulate the metada to trigger
a DoS by overwriting wrong parts of the flash.  However there's an
easier way to trigger that.  If he already has access,  he can
completely erase the metadata and their backup GPT.  You then get the
same result a device that cant boot.  Is there any scenario you have
in mind that storing those as part fo the capsule would help?  The way
I see it unless we have the metadata and the firmware stored in a
flash in the secure world, I don't see a sensible way to protect
against those kind of attacks.

>
> The signed capsule would be a good place for storing the DFU mapping.
>


[...]

Thanks for taking the time with this!
Regards
/Ilias

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

* Re: [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata
  2021-12-01  5:36     ` Sughosh Ganu
@ 2021-12-01  7:50       ` Ilias Apalodimas
  2021-12-01  8:31         ` Sughosh Ganu
  0 siblings, 1 reply; 36+ messages in thread
From: Ilias Apalodimas @ 2021-12-01  7:50 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: Heinrich Schuchardt, Patrick Delaunay, Patrice Chotard,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Jose Marinho, Grant Likely, Jason Liu, u-boot

Hi Sughosh,

[...]
>> > +{
>> > +     struct fwu_metadata_ops *ops;
>>
>> The metadata is an untrusted information source and hence MUST NOT be
>> used to map the image_type_id to the DFU alt_number. Don't invite for an
>> denial of service attack.
>>
>> The signed capsule would be a good place for storing the DFU mapping.
>
>
> I understand your concern with using dfu_alt_info for storing the information needed for writing the capsule payload. However, putting the information currently stored on the dfu_alt_info on a capsule should require a spec change IMO. This should first be discussed and brought in as part of the UEFI spec.

Well not the UEFI spec.  You got the FMP driver which is abstract
enough to handle that.  However as I already replied to Heinrich and
attacker can just erase the entire GPT,  instead of bothering altering
it.  So what I've been trying to think based on Heinrich's suggestion
is if an attacker can manipulate the metadata in such a way to force
the device boot something it shouldn't.  But since BL1 will go ahead
and verify signatures before booting them anyway,  I can't think of
something valid.

> Also, when you say signed capsule, please note not the entire capsule gets signed -- it is only the capsule payloads that are signed, not the headers. So putting the information currently stored in dfu env var to the capsule would mean adding a header to the payload, which would contain this information, and then the header plus payload would be signed. However this is > implemented, this would mean changes to the current capsule format, and making this change without changing the spec would also mean that we will also not be able to use the GenerateCapsule tool for capsule generation. This is not a small change which can be included as a patch in the FWU A/B update series, but should be taken up as a separate exercise.
>

[...]


Cheers
/Ilias

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

* Re: [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata
  2021-12-01  7:50       ` Ilias Apalodimas
@ 2021-12-01  8:31         ` Sughosh Ganu
  0 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-12-01  8:31 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Heinrich Schuchardt, Patrick Delaunay, Patrice Chotard,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Jose Marinho, Grant Likely, Jason Liu, u-boot

hi Ilias,

On Wed, 1 Dec 2021 at 13:20, Ilias Apalodimas <ilias.apalodimas@linaro.org>
wrote:

> Hi Sughosh,
>
> [...]
> >> > +{
> >> > +     struct fwu_metadata_ops *ops;
> >>
> >> The metadata is an untrusted information source and hence MUST NOT be
> >> used to map the image_type_id to the DFU alt_number. Don't invite for an
> >> denial of service attack.
> >>
> >> The signed capsule would be a good place for storing the DFU mapping.
> >
> >
> > I understand your concern with using dfu_alt_info for storing the
> information needed for writing the capsule payload. However, putting the
> information currently stored on the dfu_alt_info on a capsule should
> require a spec change IMO. This should first be discussed and brought in as
> part of the UEFI spec.
>
> Well not the UEFI spec.  You got the FMP driver which is abstract
> enough to handle that.  However as I already replied to Heinrich and
> attacker can just erase the entire GPT,  instead of bothering altering
> it.  So what I've been trying to think based on Heinrich's suggestion
> is if an attacker can manipulate the metadata in such a way to force
> the device boot something it shouldn't.  But since BL1 will go ahead
> and verify signatures before booting them anyway,  I can't think of
> something valid.
>

Sorry, I misinterpreted the comment from Heinrich. I was replying to the
comment from Heinrich about not using the dfu_alt_info env variable for the
updates. I think Heinrich is also suggesting putting the metadata
equivalent information on the capsule. This would also mean adding a header
to each payload where the header stores the metadata information. But as
you say, having the firmware and the metadata on a device that can be
accessed from the non-secure world, we cannot avoid DoS attacks even with
the metadata on the capsule. Also, this would mean having multiple copies
of the metadata, since the earlier stage bootloader(BL2/spl) shall still
need the metadata on a storage device partition to identify which bank to
boot from.

-sughosh


>
> > Also, when you say signed capsule, please note not the entire capsule
> gets signed -- it is only the capsule payloads that are signed, not the
> headers. So putting the information currently stored in dfu env var to the
> capsule would mean adding a header to the payload, which would contain this
> information, and then the header plus payload would be signed. However this
> is > implemented, this would mean changes to the current capsule format,
> and making this change without changing the spec would also mean that we
> will also not be able to use the GenerateCapsule tool for capsule
> generation. This is not a small change which can be included as a patch in
> the FWU A/B update series, but should be taken up as a separate exercise.
> >
>
> [...]
>
>
> Cheers
> /Ilias
>

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

* Re: [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-11-25  7:12 ` [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices Sughosh Ganu
@ 2021-12-01 12:26   ` Ilias Apalodimas
  2021-12-02  7:43     ` Sughosh Ganu
  2021-12-01 18:02   ` Simon Glass
  1 sibling, 1 reply; 36+ messages in thread
From: Ilias Apalodimas @ 2021-12-01 12:26 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Jose Marinho, Grant Likely, Jason Liu

Hi Sughosh, 

On Thu, Nov 25, 2021 at 12:42:56PM +0530, Sughosh Ganu wrote:
> In the FWU Multi Bank Update feature, the information about the
> updatable images is stored as part of the metadata, on a separate
> partition. Add functions for reading from and writing to the metadata
> when the updatable images and the metadata are stored on a block
> device which is formated with GPT based partition scheme.
> 
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
>  1 file changed, 716 insertions(+)
>  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
> 
> +#define SECONDARY_VALID		0x2


Change those to BIT(0), BIT(1) etc please

> +
> +#define MDATA_READ		(u8)0x1
> +#define MDATA_WRITE		(u8)0x2
> +
> +#define IMAGE_ACCEPT_SET	(u8)0x1
> +#define IMAGE_ACCEPT_CLEAR	(u8)0x2
> +
> +static int gpt_verify_metadata(struct fwu_metadata *metadata, bool pri_part)
> +{
> +	u32 calc_crc32;
> +	void *buf;
> +
> +	buf = &metadata->version;
> +	calc_crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> +
> +	if (calc_crc32 != metadata->crc32) {
> +		log_err("crc32 check failed for %s metadata partition\n",
> +			pri_part ? "primary" : "secondary");

I think we can rid of the 'bool pri_part'.  It's only used for printing and
you can have more that 2 partitions anyway right?

> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int gpt_get_metadata_partitions(struct blk_desc *desc,


Please add a function descriptions (on following functions as well)

> +				       u32 *primary_mpart,
> +				       u32 *secondary_mpart)

u16 maybe? This is the number of gpt partitions with metadata right? 

> +{
> +	int i, ret;
> +	u32 nparts, mparts;
> +	gpt_entry *gpt_pte = NULL;
> +	const efi_guid_t fwu_metadata_guid = FWU_METADATA_GUID;
> +
> +	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> +				     desc->blksz);
> +
> +	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> +	if (ret < 0) {
> +		log_err("Error getting GPT header and partitions\n");
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	nparts = gpt_head->num_partition_entries;
> +	for (i = 0, mparts = 0; i < nparts; i++) {
> +		if (!guidcmp(&fwu_metadata_guid,
> +			     &gpt_pte[i].partition_type_guid)) {
> +			++mparts;
> +			if (!*primary_mpart)
> +				*primary_mpart = i + 1;
> +			else
> +				*secondary_mpart = i + 1;
> +		}
> +	}
> +
> +	if (mparts != 2) {
> +		log_err("Expect two copies of the metadata instead of %d\n",
> +			mparts);
> +		ret = -EINVAL;
> +	} else {
> +		ret = 0;
> +	}
> +out:
> +	free(gpt_pte);
> +
> +	return ret;
> +}
> +
> +static int gpt_get_metadata_disk_part(struct blk_desc *desc,
> +				      struct disk_partition *info,
> +				      u32 part_num)
> +{
> +	int ret;
> +	char *metadata_guid_str = "8a7a84a0-8387-40f6-ab41-a8b9a5a60d23";
> +
> +	ret = part_get_info(desc, part_num, info);
> +	if (ret < 0) {
> +		log_err("Unable to get the partition info for the metadata part %d",
> +			part_num);
> +		return -1;
> +	}
> +
> +	/* Check that it is indeed the metadata partition */
> +	if (!strncmp(info->type_guid, metadata_guid_str, UUID_STR_LEN)) {
> +		/* Found the metadata partition */
> +		return 0;
> +	}
> +
> +	return -1;
> +}
> +
> +static int gpt_read_write_metadata(struct blk_desc *desc,
> +				   struct fwu_metadata *metadata,
> +				   u8 access, u32 part_num)
> +{
> +	int ret;
> +	u32 len, blk_start, blkcnt;
> +	struct disk_partition info;
> +
> +	ALLOC_CACHE_ALIGN_BUFFER_PAD(struct fwu_metadata, mdata, 1, desc->blksz);
> +
> +	ret = gpt_get_metadata_disk_part(desc, &info, part_num);
> +	if (ret < 0) {
> +		printf("Unable to get the metadata partition\n");
> +		return -ENODEV;
> +	}
> +
> +	len = sizeof(*metadata);
> +	blkcnt = BLOCK_CNT(len, desc);
> +	if (blkcnt > info.size) {
> +		log_err("Block count exceeds metadata partition size\n");
> +		return -ERANGE;
> +	}
> +
> +	blk_start = info.start;
> +	if (access == MDATA_READ) {
> +		if (blk_dread(desc, blk_start, blkcnt, mdata) != blkcnt) {
> +			log_err("Error reading metadata from the device\n");
> +			return -EIO;
> +		}
> +		memcpy(metadata, mdata, sizeof(struct fwu_metadata));
> +	} else {
> +		if (blk_dwrite(desc, blk_start, blkcnt, metadata) != blkcnt) {
> +			log_err("Error writing metadata to the device\n");
> +			return -EIO;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int gpt_read_metadata(struct blk_desc *desc,
> +			     struct fwu_metadata *metadata, u32 part_num)
> +{
> +	return gpt_read_write_metadata(desc, metadata, MDATA_READ, part_num);
> +}
> +
> +static int gpt_write_metadata_partition(struct blk_desc *desc,
> +					struct fwu_metadata *metadata,
> +					u32 part_num)
> +{
> +	return gpt_read_write_metadata(desc, metadata, MDATA_WRITE, part_num);
> +}
> +
> +static int gpt_update_metadata(struct fwu_metadata *metadata)
> +{
> +	int ret;
> +	struct blk_desc *desc;
> +	u32 primary_mpart, secondary_mpart;
> +
> +	ret = fwu_plat_get_blk_desc(&desc);
> +	if (ret < 0) {
> +		log_err("Block device not found\n");
> +		return -ENODEV;
> +	}
> +
> +	primary_mpart = secondary_mpart = 0;
> +	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> +					  &secondary_mpart);
> +
> +	if (ret < 0) {
> +		log_err("Error getting the metadata partitions\n");
> +		return -ENODEV;
> +	}
> +
> +	/* First write the primary partition*/
> +	ret = gpt_write_metadata_partition(desc, metadata, primary_mpart);
> +	if (ret < 0) {
> +		log_err("Updating primary metadata partition failed\n");
> +		return ret;
> +	}
> +
> +	/* And now the replica */
> +	ret = gpt_write_metadata_partition(desc, metadata, secondary_mpart);
> +	if (ret < 0) {
> +		log_err("Updating secondary metadata partition failed\n");
> +		return ret;
> +	}

So shouldn't we do something about this case?  The first partition was
correctly written and the second failed.  Now if the primary GPT somehow
gets corrupted the device is now unusable. 
I assume that depending on what happened to the firmware rollback counter,
we could either try to rewrite this, or revert the first one as well
(assuming rollback counters allow that).

> +
> +	return 0;
> +}
> +
> +static int gpt_get_valid_metadata(struct fwu_metadata **metadata)
> +{
> +	int ret;
> +	struct blk_desc *desc;
> +	u32 primary_mpart, secondary_mpart;
> +
> +	ret = fwu_plat_get_blk_desc(&desc);
> +	if (ret < 0) {
> +		log_err("Block device not found\n");
> +		return -ENODEV;
> +	}
> +
> +	primary_mpart = secondary_mpart = 0;
> +	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> +					  &secondary_mpart);
> +
> +	if (ret < 0) {
> +		log_err("Error getting the metadata partitions\n");
> +		return -ENODEV;
> +	}
> +
> +	*metadata = malloc(sizeof(struct fwu_metadata));
> +	if (!*metadata) {
> +		log_err("Unable to allocate memory for reading metadata\n");
> +		return -ENOMEM;
> +	}
> +
> +	ret = gpt_read_metadata(desc, *metadata, primary_mpart);
> +	if (ret < 0) {
> +		log_err("Failed to read the metadata from the device\n");
> +		return -EIO;
> +	}
> +
> +	ret = gpt_verify_metadata(*metadata, 1);
> +	if (!ret)
> +		return 0;
> +
> +	/*
> +	 * Verification of the primary metadata copy failed.
> +	 * Try to read the replica.
> +	 */
> +	memset(*metadata, 0, sizeof(struct fwu_metadata));
> +	ret = gpt_read_metadata(desc, *metadata, secondary_mpart);
> +	if (ret < 0) {
> +		log_err("Failed to read the metadata from the device\n");
> +		return -EIO;
> +	}
> +
> +	ret = gpt_verify_metadata(*metadata, 0);
> +	if (!ret)
> +		return 0;
> +
> +	/* Both the metadata copies are corrupted. */
> +	return -1;
> +}
> +
> +static int gpt_check_metadata_validity(void)
> +{
> +	int ret;
> +	struct blk_desc *desc;
> +	struct fwu_metadata *pri_metadata;
> +	struct fwu_metadata *secondary_metadata;

init those to NULL so you can goto out and free
pri_metadata/secondary_metadata unconditionally
But do you really need a pointer here? Can't this just be 
struct fwu_metadata pri_metadata, secondary_metadata;?

> +	u32 primary_mpart, secondary_mpart;
> +	u32 valid_partitions;

u16 for both I guess?

> +
> +	ret = fwu_plat_get_blk_desc(&desc);
> +	if (ret < 0) {
> +		log_err("Block device not found\n");
> +		return -ENODEV;
> +	}
> +
> +	/*
> +	 * Two metadata partitions are expected.
> +	 * If we don't have two, user needs to create
> +	 * them first
> +	 */
> +	primary_mpart = secondary_mpart = 0;
> +	valid_partitions = 0;
> +	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> +					  &secondary_mpart);
> +
> +	if (ret < 0) {
> +		log_err("Error getting the metadata partitions\n");
> +		return -ENODEV;
> +	}
> +
> +	pri_metadata = malloc(sizeof(*pri_metadata));
> +	if (!pri_metadata) {
> +		log_err("Unable to allocate memory for reading metadata\n");
> +		return -ENOMEM;
> +	}
> +
> +	secondary_metadata = malloc(sizeof(*secondary_metadata));
> +	if (!secondary_metadata) {
> +		log_err("Unable to allocate memory for reading metadata\n");
> +		return -ENOMEM;
> +	}
> +
> +	ret = gpt_read_metadata(desc, pri_metadata, primary_mpart);
> +	if (ret < 0) {
> +		log_err("Failed to read the metadata from the device\n");
> +		ret = -EIO;
> +		goto out;

It doesn't make sense to exit here without checking the secondary
partition.

> +	}
> +
> +	ret = gpt_verify_metadata(pri_metadata, 1);
> +	if (!ret)
> +		valid_partitions |= PRIMARY_VALID;
> +
> +	/* Now check the secondary partition */
> +	ret = gpt_read_metadata(desc, secondary_metadata, secondary_mpart);
> +	if (ret < 0) {
> +		log_err("Failed to read the metadata from the device\n");
> +		ret = -EIO;

Ditto, if the first is valid we can still rescue that.

> +		goto out;
> +	}
> +
> +	ret = gpt_verify_metadata(secondary_metadata, 0);
> +	if (!ret)
> +		valid_partitions |= SECONDARY_VALID;
> +
> +	if (valid_partitions == (PRIMARY_VALID | SECONDARY_VALID)) {
> +		ret = -1;
> +		/*
> +		 * Before returning, check that both the
> +		 * metadata copies are the same. If not,
> +		 * the metadata copies need to be
> +		 * re-populated.
> +		 */
> +		if (!memcmp(pri_metadata, secondary_metadata,
> +			    sizeof(*pri_metadata)))
> +			ret = 0;

Is anyone else copying the metadata if this fails? In that case would it
make sense to just copy pri_metadata-> secondary_metadata here and sync
them up?

> +		goto out;
> +	} else if (valid_partitions == SECONDARY_VALID) {
> +		ret = gpt_write_metadata_partition(desc, secondary_metadata,
> +						   primary_mpart);
> +		if (ret < 0) {
> +			log_err("Restoring primary metadata partition failed\n");
> +			goto out;
> +		}
> +	} else if (valid_partitions == PRIMARY_VALID) {
> +		ret = gpt_write_metadata_partition(desc, pri_metadata,
> +						   secondary_mpart);
> +		if (ret < 0) {
> +			log_err("Restoring secondary metadata partition failed\n");
> +			goto out;
> +		}
> +	} else {
> +		ret = -1;
> +	}

I would write this whole if a bit differently. Since you have the valid
partitions in a bitmap.  
redefine your original definitions like

#define PRIMARY_VALID   BIT(0)
#define SECONDARY_VALID BIT(1)
#define BOTH_VALID      (PRIMARY_VALID | SECONDARY_VALID)

if (!(valid_partitions & BOTH_VALID))
	goto out;

wrong = valid_partitions ^ BOTH_VALID;
if (!out)
    <both valid code>
else 
    <'wrong' is the number of invalid partition now>
	gpt_write_metadata_partition(desc, 
								 (wrong == PRIMARY_VALID) ? secondary_metadata :  pri_metadata),
								 (wrong == PRIMARY_VALID) ? primary_mpart :  secondary_mpart)

> +
> +out:
> +	free(pri_metadata);

secondary_metadata needs freeing as well if you keep the ptrs

> +
> +	return ret;
> +}
> +
> +static int gpt_fill_partition_guid_array(struct blk_desc *desc,
> +					 efi_guid_t **part_guid_arr,
> +					 u32 *nparts)
> +{
> +	int ret, i;
> +	u32 parts;
> +	gpt_entry *gpt_pte = NULL;
> +	const efi_guid_t null_guid = NULL_GUID;
> +
> +	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> +				     desc->blksz);
> +
> +	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> +	if (ret < 0) {
> +		log_err("Error getting GPT header and partitions\n");
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	*nparts = gpt_head->num_partition_entries;
> +
> +	/*
> +	 * There could be a scenario where the number of partition entries
> +	 * configured in the GPT header is the default value of 128. Find
> +	 * the actual number of populated partitioned entries
> +	 */
> +	for (i = 0, parts = 0; i < *nparts; i++) {

Just init 'parts' on the declaration

> +		if (!guidcmp(&gpt_pte[i].partition_type_guid, &null_guid))
> +			continue;
> +		++parts;
> +	}
> +
> +	*nparts = parts;
> +	*part_guid_arr = malloc(sizeof(efi_guid_t) * *nparts);
> +	if (!part_guid_arr) {
> +		log_err("Unable to allocate memory for guid array\n");
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	for (i = 0; i < *nparts; i++) {
> +		guidcpy((*part_guid_arr + i),
> +			&gpt_pte[i].partition_type_guid);
> +	}
> +
> +out:
> +	free(gpt_pte);
> +	return ret;
> +}
> +
> +int fwu_gpt_get_active_index(u32 *active_idx)
> +{
> +	int ret;
> +	struct fwu_metadata *metadata;
> +
> +	ret = gpt_get_valid_metadata(&metadata);
> +	if (ret < 0) {
> +		log_err("Unable to get valid metadata\n");
> +		goto out;
> +	}
> +
> +	/*
> +	 * Found the metadata partition, now read the active_index
> +	 * value
> +	 */
> +	*active_idx = metadata->active_index;
> +	if (*active_idx > CONFIG_FWU_NUM_BANKS - 1) {
> +		printf("Active index value read is incorrect\n");
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +out:
> +	free(metadata);
> +
> +	return ret;
> +}
> +
> +static int gpt_get_image_alt_num(struct blk_desc *desc,
> +				 efi_guid_t image_type_id,
> +				 u32 update_bank, int *alt_no)
> +{
> +	int ret, i;
> +	u32 nparts;
> +	gpt_entry *gpt_pte = NULL;
> +	struct fwu_metadata *metadata;
> +	struct fwu_image_entry *img_entry;
> +	struct fwu_image_bank_info *img_bank_info;
> +	efi_guid_t unique_part_guid;
> +	efi_guid_t image_guid = NULL_GUID;
> +
> +	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> +				     desc->blksz);
> +
> +	ret = gpt_get_valid_metadata(&metadata);
> +	if (ret < 0) {
> +		log_err("Unable to read valid metadata\n");
> +		goto out;
> +	}
> +
> +	/*
> +	 * The metadata has been read. Now get the image_uuid for the
> +	 * image with the update_bank.
> +	 */
> +	for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
> +		if (!guidcmp(&image_type_id,
> +			     &metadata->img_entry[i].image_type_uuid)) {
> +			img_entry = &metadata->img_entry[i];
> +			img_bank_info = &img_entry->img_bank_info[update_bank];
> +			guidcpy(&image_guid, &img_bank_info->image_uuid);

break;?

> +		}
> +	}
> +
> +	/*
> +	 * Now read the GPT Partition Table Entries to find a matching
> +	 * partition with UniquePartitionGuid value. We need to iterate
> +	 * through all the GPT partitions since they might be in any
> +	 * order
> +	 */
> +	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> +	if (ret < 0) {
> +		log_err("Error getting GPT header and partitions\n");
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	nparts = gpt_head->num_partition_entries;
> +
> +	for (i = 0; i < nparts; i++) {
> +		unique_part_guid = gpt_pte[i].unique_partition_guid;
> +		if (!guidcmp(&unique_part_guid, &image_guid)) {
> +			/* Found the partition */
> +			*alt_no = i + 1;
> +			break;
> +		}
> +	}
> +
> +	if (i == nparts) {
> +		log_err("Partition with the image guid not found\n");
> +		ret = -EINVAL;
> +	}
> +
> +out:
> +	free(metadata);
> +	free(gpt_pte);
> +	return ret;
> +}
> +
> +int fwu_gpt_update_active_index(u32 active_idx)
> +{
> +	int ret;
> +	void *buf;
> +	u32 cur_active_index;
> +	struct fwu_metadata *metadata;
> +
> +	if (active_idx > CONFIG_FWU_NUM_BANKS) {
> +		printf("Active index value to be updated is incorrect\n");
> +		return -1;
> +	}
> +
> +	ret = gpt_get_valid_metadata(&metadata);
> +	if (ret < 0) {
> +		log_err("Unable to get valid metadata\n");
> +		goto out;
> +	}
> +
> +	/*
> +	 * Update the active index and previous_active_index fields
> +	 * in the metadata
> +	 */
> +	cur_active_index = metadata->active_index;
> +	metadata->active_index = active_idx;
> +	metadata->previous_active_index = cur_active_index;

You don't need the cur_active_index, just swap the 2 lines above.
metadata->previous_active_index = metadata->active_index;
metadata->active_index = active_idx;

> +
> +	/*
> +	 * Calculate the crc32 for the updated metadata
> +	 * and put the updated value in the metadata crc32
> +	 * field
> +	 */
> +	buf = &metadata->version;
> +	metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> +
> +	/*
> +	 * Now write this updated metadata to both the
> +	 * metadata partitions
> +	 */
> +	ret = gpt_update_metadata(metadata);
> +	if (ret < 0) {
> +		log_err("Failed to update metadata partitions\n");
> +		ret = -EIO;
> +	}
> +
> +out:
> +	free(metadata);
> +
> +	return ret;
> +}
> +
> +int fwu_gpt_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32 *nparts)
> +{
> +	int ret;
> +	struct blk_desc *desc;
> +
> +	ret = fwu_plat_get_blk_desc(&desc);
> +	if (ret < 0) {
> +		log_err("Block device not found\n");
> +		return -ENODEV;
> +	}
> +
> +	return gpt_fill_partition_guid_array(desc, part_guid_arr, nparts);
> +}
> +
> +int fwu_gpt_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> +			      int *alt_no)
> +{
> +	int ret;
> +	struct blk_desc *desc;
> +
> +	ret = fwu_plat_get_blk_desc(&desc);
> +	if (ret < 0) {
> +		log_err("Block device not found\n");
> +		return -ENODEV;
> +	}
> +
> +	return gpt_get_image_alt_num(desc, image_type_id, update_bank, alt_no);
> +}
> +
> +int fwu_gpt_metadata_check(void)
> +{
> +	/*
> +	 * Check if both the copies of the metadata are valid.
> +	 * If one has gone bad, restore it from the other good
> +	 * copy.
> +	 */
> +	return gpt_check_metadata_validity();
> +}
> +
> +int fwu_gpt_get_metadata(struct fwu_metadata **metadata)
> +{
> +	return gpt_get_valid_metadata(metadata);
> +}
> +
> +int fwu_gpt_revert_boot_index(u32 *active_idx)
> +{
> +	int ret;
> +	void *buf;
> +	u32 cur_active_index;
> +	struct fwu_metadata *metadata;
> +
> +	ret = gpt_get_valid_metadata(&metadata);
> +	if (ret < 0) {
> +		log_err("Unable to get valid metadata\n");
> +		goto out;
> +	}
> +
> +	/*
> +	 * Swap the active index and previous_active_index fields
> +	 * in the metadata
> +	 */
> +	cur_active_index = metadata->active_index;
> +	metadata->active_index = metadata->previous_active_index;
> +	metadata->previous_active_index = cur_active_index;

Ditto, you don't need cur_active_index;

> +	*active_idx = metadata->active_index;
> +
> +	/*
> +	 * Calculate the crc32 for the updated metadata
> +	 * and put the updated value in the metadata crc32
> +	 * field
> +	 */
> +	buf = &metadata->version;
> +	metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> +
> +	/*
> +	 * Now write this updated metadata to both the
> +	 * metadata partitions
> +	 */
> +	ret = gpt_update_metadata(metadata);
> +	if (ret < 0) {
> +		log_err("Failed to update metadata partitions\n");
> +		ret = -EIO;
> +	}
> +
> +out:
> +	free(metadata);
> +
> +	return ret;
> +}
> +
> +static int fwu_gpt_set_clear_image_accept(efi_guid_t *img_type_id,
> +					  u32 bank, u8 action)
> +{
> +	void *buf;
> +	int ret, i;
> +	u32 nimages;
> +	struct fwu_metadata *metadata;
> +	struct fwu_image_entry *img_entry;
> +	struct fwu_image_bank_info *img_bank_info;
> +
> +	ret = gpt_get_valid_metadata(&metadata);
> +	if (ret < 0) {
> +		log_err("Unable to get valid metadata\n");
> +		goto out;
> +	}
> +
> +	if (action == IMAGE_ACCEPT_SET)
> +		bank = metadata->active_index;

I think it's clearer if fwu_gpt_accept_image() /
fwu_gpt_clear_accept_image() read the metadata themselves and pass them as
a ptr.  That would mean you also have the right bank number and you wont be
needing this anymore.

> +
> +	nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
> +	img_entry = &metadata->img_entry[0];
> +	for (i = 0; i < nimages; i++) {
> +		if (!guidcmp(&img_entry[i].image_type_uuid, img_type_id)) {
> +			img_bank_info = &img_entry[i].img_bank_info[bank];
> +			if (action == IMAGE_ACCEPT_SET)
> +				img_bank_info->accepted |= FWU_IMAGE_ACCEPTED;

Do you need to preserve existing bits on 'accepted' here?

> +			else
> +				img_bank_info->accepted = 0;
> +
> +			buf = &metadata->version;
> +			metadata->crc32 = crc32(0, buf, sizeof(*metadata) -
> +						sizeof(u32));
> +
> +			ret = gpt_update_metadata(metadata);
> +			goto out;
> +		}
> +	}
> +
> +	/* Image not found */
> +	ret = -EINVAL;
> +
> +out:
> +	free(metadata);
> +
> +	return ret;
> +}
> +
> +int fwu_gpt_accept_image(efi_guid_t *img_type_id)
> +{
> +	return fwu_gpt_set_clear_image_accept(img_type_id, 0,
> +					      IMAGE_ACCEPT_SET);
> +}
> +
> +int fwu_gpt_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
> +{
> +	return fwu_gpt_set_clear_image_accept(img_type_id, bank,
> +					      IMAGE_ACCEPT_CLEAR);
> +}
> +
> +struct fwu_metadata_ops fwu_gpt_blk_ops = {
> +	.get_active_index = fwu_gpt_get_active_index,
> +	.update_active_index = fwu_gpt_update_active_index,
> +	.fill_partition_guid_array = fwu_gpt_fill_partition_guid_array,
> +	.get_image_alt_num = fwu_gpt_get_image_alt_num,
> +	.metadata_check = fwu_gpt_metadata_check,
> +	.revert_boot_index = fwu_gpt_revert_boot_index,
> +	.set_accept_image = fwu_gpt_accept_image,
> +	.clear_accept_image = fwu_gpt_clear_accept_image,
> +	.get_metadata = fwu_gpt_get_metadata,
> +};
> -- 
> 2.17.1
> 

Cheers
/Ilias

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

* Re: [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-11-25  7:12 ` [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices Sughosh Ganu
  2021-12-01 12:26   ` Ilias Apalodimas
@ 2021-12-01 18:02   ` Simon Glass
  2021-12-02  8:05     ` Sughosh Ganu
  1 sibling, 1 reply; 36+ messages in thread
From: Simon Glass @ 2021-12-01 18:02 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu

Hi Sughosh,

On Thu, 25 Nov 2021 at 00:13, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> In the FWU Multi Bank Update feature, the information about the
> updatable images is stored as part of the metadata, on a separate
> partition. Add functions for reading from and writing to the metadata
> when the updatable images and the metadata are stored on a block
> device which is formated with GPT based partition scheme.
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
>  1 file changed, 716 insertions(+)
>  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c

Can we pick a better name than metadata? It is just so
vague...everything is metadata.

Regards,
Simon

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

* Re: [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-12-01 12:26   ` Ilias Apalodimas
@ 2021-12-02  7:43     ` Sughosh Ganu
  2021-12-08 14:17       ` Etienne Carriere
  0 siblings, 1 reply; 36+ messages in thread
From: Sughosh Ganu @ 2021-12-02  7:43 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Simon Glass, Bin Meng, Peng Fan, AKASHI Takahiro,
	Jose Marinho, Grant Likely, Jason Liu

hi Ilias,

On Wed, 1 Dec 2021 at 17:56, Ilias Apalodimas <ilias.apalodimas@linaro.org>
wrote:

> Hi Sughosh,
>
> On Thu, Nov 25, 2021 at 12:42:56PM +0530, Sughosh Ganu wrote:
> > In the FWU Multi Bank Update feature, the information about the
> > updatable images is stored as part of the metadata, on a separate
> > partition. Add functions for reading from and writing to the metadata
> > when the updatable images and the metadata are stored on a block
> > device which is formated with GPT based partition scheme.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
> >  1 file changed, 716 insertions(+)
> >  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
> >
> > +#define SECONDARY_VALID              0x2
>
>
> Change those to BIT(0), BIT(1) etc please
>

Will change.


>
> > +
> > +#define MDATA_READ           (u8)0x1
> > +#define MDATA_WRITE          (u8)0x2
> > +
> > +#define IMAGE_ACCEPT_SET     (u8)0x1
> > +#define IMAGE_ACCEPT_CLEAR   (u8)0x2
> > +
> > +static int gpt_verify_metadata(struct fwu_metadata *metadata, bool
> pri_part)
> > +{
> > +     u32 calc_crc32;
> > +     void *buf;
> > +
> > +     buf = &metadata->version;
> > +     calc_crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > +
> > +     if (calc_crc32 != metadata->crc32) {
> > +             log_err("crc32 check failed for %s metadata partition\n",
> > +                     pri_part ? "primary" : "secondary");
>
> I think we can rid of the 'bool pri_part'.  It's only used for printing and
> you can have more that 2 partitions anyway right?
>

We will have only two partitions for the metadata. But i think looking at
it now, it is a bit fuzzy on which is the primary metadata partition and
which is the secondary one. Can we instead print the UniquePartitionGUID of
the metadata partition instead. That will help in identifying for which
metadata copy the verification failed. Let me know your thoughts on this.


> > +             return -1;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int gpt_get_metadata_partitions(struct blk_desc *desc,
>
>
> Please add a function descriptions (on following functions as well)
>

I have added the function descriptions in the fwu_metadata.c, where the
api's are being defined. Do we need to add the descriptions for the
functions in this file as well?


>
> > +                                    u32 *primary_mpart,
> > +                                    u32 *secondary_mpart)
>
> u16 maybe? This is the number of gpt partitions with metadata right?


Okay.


>
>
> > +{
> > +     int i, ret;
> > +     u32 nparts, mparts;
> > +     gpt_entry *gpt_pte = NULL;
> > +     const efi_guid_t fwu_metadata_guid = FWU_METADATA_GUID;
> > +
> > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > +                                  desc->blksz);
> > +
> > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > +     if (ret < 0) {
> > +             log_err("Error getting GPT header and partitions\n");
> > +             ret = -EIO;
> > +             goto out;
> > +     }
> > +
> > +     nparts = gpt_head->num_partition_entries;
> > +     for (i = 0, mparts = 0; i < nparts; i++) {
> > +             if (!guidcmp(&fwu_metadata_guid,
> > +                          &gpt_pte[i].partition_type_guid)) {
> > +                     ++mparts;
> > +                     if (!*primary_mpart)
> > +                             *primary_mpart = i + 1;
> > +                     else
> > +                             *secondary_mpart = i + 1;
> > +             }
> > +     }
> > +
> > +     if (mparts != 2) {
> > +             log_err("Expect two copies of the metadata instead of
> %d\n",
> > +                     mparts);
> > +             ret = -EINVAL;
> > +     } else {
> > +             ret = 0;
> > +     }
> > +out:
> > +     free(gpt_pte);
> > +
> > +     return ret;
> > +}
> > +
> > +static int gpt_get_metadata_disk_part(struct blk_desc *desc,
> > +                                   struct disk_partition *info,
> > +                                   u32 part_num)
> > +{
> > +     int ret;
> > +     char *metadata_guid_str = "8a7a84a0-8387-40f6-ab41-a8b9a5a60d23";
> > +
> > +     ret = part_get_info(desc, part_num, info);
> > +     if (ret < 0) {
> > +             log_err("Unable to get the partition info for the metadata
> part %d",
> > +                     part_num);
> > +             return -1;
> > +     }
> > +
> > +     /* Check that it is indeed the metadata partition */
> > +     if (!strncmp(info->type_guid, metadata_guid_str, UUID_STR_LEN)) {
> > +             /* Found the metadata partition */
> > +             return 0;
> > +     }
> > +
> > +     return -1;
> > +}
> > +
> > +static int gpt_read_write_metadata(struct blk_desc *desc,
> > +                                struct fwu_metadata *metadata,
> > +                                u8 access, u32 part_num)
> > +{
> > +     int ret;
> > +     u32 len, blk_start, blkcnt;
> > +     struct disk_partition info;
> > +
> > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(struct fwu_metadata, mdata, 1,
> desc->blksz);
> > +
> > +     ret = gpt_get_metadata_disk_part(desc, &info, part_num);
> > +     if (ret < 0) {
> > +             printf("Unable to get the metadata partition\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     len = sizeof(*metadata);
> > +     blkcnt = BLOCK_CNT(len, desc);
> > +     if (blkcnt > info.size) {
> > +             log_err("Block count exceeds metadata partition size\n");
> > +             return -ERANGE;
> > +     }
> > +
> > +     blk_start = info.start;
> > +     if (access == MDATA_READ) {
> > +             if (blk_dread(desc, blk_start, blkcnt, mdata) != blkcnt) {
> > +                     log_err("Error reading metadata from the
> device\n");
> > +                     return -EIO;
> > +             }
> > +             memcpy(metadata, mdata, sizeof(struct fwu_metadata));
> > +     } else {
> > +             if (blk_dwrite(desc, blk_start, blkcnt, metadata) !=
> blkcnt) {
> > +                     log_err("Error writing metadata to the device\n");
> > +                     return -EIO;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int gpt_read_metadata(struct blk_desc *desc,
> > +                          struct fwu_metadata *metadata, u32 part_num)
> > +{
> > +     return gpt_read_write_metadata(desc, metadata, MDATA_READ,
> part_num);
> > +}
> > +
> > +static int gpt_write_metadata_partition(struct blk_desc *desc,
> > +                                     struct fwu_metadata *metadata,
> > +                                     u32 part_num)
> > +{
> > +     return gpt_read_write_metadata(desc, metadata, MDATA_WRITE,
> part_num);
> > +}
> > +
> > +static int gpt_update_metadata(struct fwu_metadata *metadata)
> > +{
> > +     int ret;
> > +     struct blk_desc *desc;
> > +     u32 primary_mpart, secondary_mpart;
> > +
> > +     ret = fwu_plat_get_blk_desc(&desc);
> > +     if (ret < 0) {
> > +             log_err("Block device not found\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     primary_mpart = secondary_mpart = 0;
> > +     ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> > +                                       &secondary_mpart);
> > +
> > +     if (ret < 0) {
> > +             log_err("Error getting the metadata partitions\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     /* First write the primary partition*/
> > +     ret = gpt_write_metadata_partition(desc, metadata, primary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Updating primary metadata partition failed\n");
> > +             return ret;
> > +     }
> > +
> > +     /* And now the replica */
> > +     ret = gpt_write_metadata_partition(desc, metadata,
> secondary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Updating secondary metadata partition failed\n");
> > +             return ret;
> > +     }
>
> So shouldn't we do something about this case?  The first partition was
> correctly written and the second failed.  Now if the primary GPT somehow
> gets corrupted the device is now unusable.
> I assume that depending on what happened to the firmware rollback counter,
> we could either try to rewrite this, or revert the first one as well
> (assuming rollback counters allow that).
>

Okay, although this might be indicative of some underlying hardware issue
with the device, else this scenario should not play out.


> > +
> > +     return 0;
> > +}
> > +
> > +static int gpt_get_valid_metadata(struct fwu_metadata **metadata)
> > +{
> > +     int ret;
> > +     struct blk_desc *desc;
> > +     u32 primary_mpart, secondary_mpart;
> > +
> > +     ret = fwu_plat_get_blk_desc(&desc);
> > +     if (ret < 0) {
> > +             log_err("Block device not found\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     primary_mpart = secondary_mpart = 0;
> > +     ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> > +                                       &secondary_mpart);
> > +
> > +     if (ret < 0) {
> > +             log_err("Error getting the metadata partitions\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     *metadata = malloc(sizeof(struct fwu_metadata));
> > +     if (!*metadata) {
> > +             log_err("Unable to allocate memory for reading
> metadata\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     ret = gpt_read_metadata(desc, *metadata, primary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Failed to read the metadata from the device\n");
> > +             return -EIO;
> > +     }
> > +
> > +     ret = gpt_verify_metadata(*metadata, 1);
> > +     if (!ret)
> > +             return 0;
> > +
> > +     /*
> > +      * Verification of the primary metadata copy failed.
> > +      * Try to read the replica.
> > +      */
> > +     memset(*metadata, 0, sizeof(struct fwu_metadata));
> > +     ret = gpt_read_metadata(desc, *metadata, secondary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Failed to read the metadata from the device\n");
> > +             return -EIO;
> > +     }
> > +
> > +     ret = gpt_verify_metadata(*metadata, 0);
> > +     if (!ret)
> > +             return 0;
> > +
> > +     /* Both the metadata copies are corrupted. */
> > +     return -1;
> > +}
> > +
> > +static int gpt_check_metadata_validity(void)
> > +{
> > +     int ret;
> > +     struct blk_desc *desc;
> > +     struct fwu_metadata *pri_metadata;
> > +     struct fwu_metadata *secondary_metadata;
>
> init those to NULL so you can goto out and free
> pri_metadata/secondary_metadata unconditionally
> But do you really need a pointer here? Can't this just be
> struct fwu_metadata pri_metadata, secondary_metadata;?
>

Yes, these can be declared as local variables, and that will get rid of the
malloc. Will change.


> > +     u32 primary_mpart, secondary_mpart;
> > +     u32 valid_partitions;
>
> u16 for both I guess?
>

Okay.


>
> > +
> > +     ret = fwu_plat_get_blk_desc(&desc);
> > +     if (ret < 0) {
> > +             log_err("Block device not found\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     /*
> > +      * Two metadata partitions are expected.
> > +      * If we don't have two, user needs to create
> > +      * them first
> > +      */
> > +     primary_mpart = secondary_mpart = 0;
> > +     valid_partitions = 0;
> > +     ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> > +                                       &secondary_mpart);
> > +
> > +     if (ret < 0) {
> > +             log_err("Error getting the metadata partitions\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     pri_metadata = malloc(sizeof(*pri_metadata));
> > +     if (!pri_metadata) {
> > +             log_err("Unable to allocate memory for reading
> metadata\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     secondary_metadata = malloc(sizeof(*secondary_metadata));
> > +     if (!secondary_metadata) {
> > +             log_err("Unable to allocate memory for reading
> metadata\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     ret = gpt_read_metadata(desc, pri_metadata, primary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Failed to read the metadata from the device\n");
> > +             ret = -EIO;
> > +             goto out;
>
> It doesn't make sense to exit here without checking the secondary
> partition.
>

Okay. Will revisit this logic. Although, this scenario should not play out
unless there is some underlying issue with the device. But you are correct
that if one of the metadata copies is valid, we can try restoring the other
one.


>
> > +     }
> > +
> > +     ret = gpt_verify_metadata(pri_metadata, 1);
> > +     if (!ret)
> > +             valid_partitions |= PRIMARY_VALID;
> > +
> > +     /* Now check the secondary partition */
> > +     ret = gpt_read_metadata(desc, secondary_metadata, secondary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Failed to read the metadata from the device\n");
> > +             ret = -EIO;
>
> Ditto, if the first is valid we can still rescue that.
>

Okay.


>
> > +             goto out;
> > +     }
> > +
> > +     ret = gpt_verify_metadata(secondary_metadata, 0);
> > +     if (!ret)
> > +             valid_partitions |= SECONDARY_VALID;
> > +
> > +     if (valid_partitions == (PRIMARY_VALID | SECONDARY_VALID)) {
> > +             ret = -1;
> > +             /*
> > +              * Before returning, check that both the
> > +              * metadata copies are the same. If not,
> > +              * the metadata copies need to be
> > +              * re-populated.
> > +              */
> > +             if (!memcmp(pri_metadata, secondary_metadata,
> > +                         sizeof(*pri_metadata)))
> > +                     ret = 0;
>
> Is anyone else copying the metadata if this fails? In that case would it
> make sense to just copy pri_metadata-> secondary_metadata here and sync
> them up?
>

So this is a pretty fundamental error scenario where both metadata copies
are valid, but are out of sync -- this should never happen. Will it be
better instead to return an error and let the user check why this happened.


> > +             goto out;
> > +     } else if (valid_partitions == SECONDARY_VALID) {
> > +             ret = gpt_write_metadata_partition(desc,
> secondary_metadata,
> > +                                                primary_mpart);
> > +             if (ret < 0) {
> > +                     log_err("Restoring primary metadata partition
> failed\n");
> > +                     goto out;
> > +             }
> > +     } else if (valid_partitions == PRIMARY_VALID) {
> > +             ret = gpt_write_metadata_partition(desc, pri_metadata,
> > +                                                secondary_mpart);
> > +             if (ret < 0) {
> > +                     log_err("Restoring secondary metadata partition
> failed\n");
> > +                     goto out;
> > +             }
> > +     } else {
> > +             ret = -1;
> > +     }
>
> I would write this whole if a bit differently. Since you have the valid
> partitions in a bitmap.
> redefine your original definitions like
>
> #define PRIMARY_VALID   BIT(0)
> #define SECONDARY_VALID BIT(1)
> #define BOTH_VALID      (PRIMARY_VALID | SECONDARY_VALID)
>
> if (!(valid_partitions & BOTH_VALID))
>         goto out;
>
> wrong = valid_partitions ^ BOTH_VALID;
> if (!out)
>     <both valid code>
> else
>     <'wrong' is the number of invalid partition now>
>         gpt_write_metadata_partition(desc,
>                                                                  (wrong ==
> PRIMARY_VALID) ? secondary_metadata :  pri_metadata),
>                                                                  (wrong ==
> PRIMARY_VALID) ? primary_mpart :  secondary_mpart)
>

I will check with you on this offline. Am a little confused here :)


>
> > +
> > +out:
> > +     free(pri_metadata);
>
> secondary_metadata needs freeing as well if you keep the ptrs
>

Yes, this is a remnant from my earlier implementation where i was
allocating memory for both copies of metadata through a single call to
malloc. But this will go away with declaration of local variables instead
of malloc.


>
> > +
> > +     return ret;
> > +}
> > +
> > +static int gpt_fill_partition_guid_array(struct blk_desc *desc,
> > +                                      efi_guid_t **part_guid_arr,
> > +                                      u32 *nparts)
> > +{
> > +     int ret, i;
> > +     u32 parts;
> > +     gpt_entry *gpt_pte = NULL;
> > +     const efi_guid_t null_guid = NULL_GUID;
> > +
> > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > +                                  desc->blksz);
> > +
> > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > +     if (ret < 0) {
> > +             log_err("Error getting GPT header and partitions\n");
> > +             ret = -EIO;
> > +             goto out;
> > +     }
> > +
> > +     *nparts = gpt_head->num_partition_entries;
> > +
> > +     /*
> > +      * There could be a scenario where the number of partition entries
> > +      * configured in the GPT header is the default value of 128. Find
> > +      * the actual number of populated partitioned entries
> > +      */
> > +     for (i = 0, parts = 0; i < *nparts; i++) {
>
> Just init 'parts' on the declaration
>

Okay.


>
> > +             if (!guidcmp(&gpt_pte[i].partition_type_guid, &null_guid))
> > +                     continue;
> > +             ++parts;
> > +     }
> > +
> > +     *nparts = parts;
> > +     *part_guid_arr = malloc(sizeof(efi_guid_t) * *nparts);
> > +     if (!part_guid_arr) {
> > +             log_err("Unable to allocate memory for guid array\n");
> > +             ret = -ENOMEM;
> > +             goto out;
> > +     }
> > +
> > +     for (i = 0; i < *nparts; i++) {
> > +             guidcpy((*part_guid_arr + i),
> > +                     &gpt_pte[i].partition_type_guid);
> > +     }
> > +
> > +out:
> > +     free(gpt_pte);
> > +     return ret;
> > +}
> > +
> > +int fwu_gpt_get_active_index(u32 *active_idx)
> > +{
> > +     int ret;
> > +     struct fwu_metadata *metadata;
> > +
> > +     ret = gpt_get_valid_metadata(&metadata);
> > +     if (ret < 0) {
> > +             log_err("Unable to get valid metadata\n");
> > +             goto out;
> > +     }
> > +
> > +     /*
> > +      * Found the metadata partition, now read the active_index
> > +      * value
> > +      */
> > +     *active_idx = metadata->active_index;
> > +     if (*active_idx > CONFIG_FWU_NUM_BANKS - 1) {
> > +             printf("Active index value read is incorrect\n");
> > +             ret = -EINVAL;
> > +             goto out;
> > +     }
> > +
> > +out:
> > +     free(metadata);
> > +
> > +     return ret;
> > +}
> > +
> > +static int gpt_get_image_alt_num(struct blk_desc *desc,
> > +                              efi_guid_t image_type_id,
> > +                              u32 update_bank, int *alt_no)
> > +{
> > +     int ret, i;
> > +     u32 nparts;
> > +     gpt_entry *gpt_pte = NULL;
> > +     struct fwu_metadata *metadata;
> > +     struct fwu_image_entry *img_entry;
> > +     struct fwu_image_bank_info *img_bank_info;
> > +     efi_guid_t unique_part_guid;
> > +     efi_guid_t image_guid = NULL_GUID;
> > +
> > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > +                                  desc->blksz);
> > +
> > +     ret = gpt_get_valid_metadata(&metadata);
> > +     if (ret < 0) {
> > +             log_err("Unable to read valid metadata\n");
> > +             goto out;
> > +     }
> > +
> > +     /*
> > +      * The metadata has been read. Now get the image_uuid for the
> > +      * image with the update_bank.
> > +      */
> > +     for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
> > +             if (!guidcmp(&image_type_id,
> > +                          &metadata->img_entry[i].image_type_uuid)) {
> > +                     img_entry = &metadata->img_entry[i];
> > +                     img_bank_info =
> &img_entry->img_bank_info[update_bank];
> > +                     guidcpy(&image_guid, &img_bank_info->image_uuid);
>
> break;?
>

Okay.


>
> > +             }
> > +     }
> > +
> > +     /*
> > +      * Now read the GPT Partition Table Entries to find a matching
> > +      * partition with UniquePartitionGuid value. We need to iterate
> > +      * through all the GPT partitions since they might be in any
> > +      * order
> > +      */
> > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > +     if (ret < 0) {
> > +             log_err("Error getting GPT header and partitions\n");
> > +             ret = -EIO;
> > +             goto out;
> > +     }
> > +
> > +     nparts = gpt_head->num_partition_entries;
> > +
> > +     for (i = 0; i < nparts; i++) {
> > +             unique_part_guid = gpt_pte[i].unique_partition_guid;
> > +             if (!guidcmp(&unique_part_guid, &image_guid)) {
> > +                     /* Found the partition */
> > +                     *alt_no = i + 1;
> > +                     break;
> > +             }
> > +     }
> > +
> > +     if (i == nparts) {
> > +             log_err("Partition with the image guid not found\n");
> > +             ret = -EINVAL;
> > +     }
> > +
> > +out:
> > +     free(metadata);
> > +     free(gpt_pte);
> > +     return ret;
> > +}
> > +
> > +int fwu_gpt_update_active_index(u32 active_idx)
> > +{
> > +     int ret;
> > +     void *buf;
> > +     u32 cur_active_index;
> > +     struct fwu_metadata *metadata;
> > +
> > +     if (active_idx > CONFIG_FWU_NUM_BANKS) {
> > +             printf("Active index value to be updated is incorrect\n");
> > +             return -1;
> > +     }
> > +
> > +     ret = gpt_get_valid_metadata(&metadata);
> > +     if (ret < 0) {
> > +             log_err("Unable to get valid metadata\n");
> > +             goto out;
> > +     }
> > +
> > +     /*
> > +      * Update the active index and previous_active_index fields
> > +      * in the metadata
> > +      */
> > +     cur_active_index = metadata->active_index;
> > +     metadata->active_index = active_idx;
> > +     metadata->previous_active_index = cur_active_index;
>
> You don't need the cur_active_index, just swap the 2 lines above.
> metadata->previous_active_index = metadata->active_index;
> metadata->active_index = active_idx;
>

Will change.


>
> > +
> > +     /*
> > +      * Calculate the crc32 for the updated metadata
> > +      * and put the updated value in the metadata crc32
> > +      * field
> > +      */
> > +     buf = &metadata->version;
> > +     metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > +
> > +     /*
> > +      * Now write this updated metadata to both the
> > +      * metadata partitions
> > +      */
> > +     ret = gpt_update_metadata(metadata);
> > +     if (ret < 0) {
> > +             log_err("Failed to update metadata partitions\n");
> > +             ret = -EIO;
> > +     }
> > +
> > +out:
> > +     free(metadata);
> > +
> > +     return ret;
> > +}
> > +
> > +int fwu_gpt_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32
> *nparts)
> > +{
> > +     int ret;
> > +     struct blk_desc *desc;
> > +
> > +     ret = fwu_plat_get_blk_desc(&desc);
> > +     if (ret < 0) {
> > +             log_err("Block device not found\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     return gpt_fill_partition_guid_array(desc, part_guid_arr, nparts);
> > +}
> > +
> > +int fwu_gpt_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> > +                           int *alt_no)
> > +{
> > +     int ret;
> > +     struct blk_desc *desc;
> > +
> > +     ret = fwu_plat_get_blk_desc(&desc);
> > +     if (ret < 0) {
> > +             log_err("Block device not found\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     return gpt_get_image_alt_num(desc, image_type_id, update_bank,
> alt_no);
> > +}
> > +
> > +int fwu_gpt_metadata_check(void)
> > +{
> > +     /*
> > +      * Check if both the copies of the metadata are valid.
> > +      * If one has gone bad, restore it from the other good
> > +      * copy.
> > +      */
> > +     return gpt_check_metadata_validity();
> > +}
> > +
> > +int fwu_gpt_get_metadata(struct fwu_metadata **metadata)
> > +{
> > +     return gpt_get_valid_metadata(metadata);
> > +}
> > +
> > +int fwu_gpt_revert_boot_index(u32 *active_idx)
> > +{
> > +     int ret;
> > +     void *buf;
> > +     u32 cur_active_index;
> > +     struct fwu_metadata *metadata;
> > +
> > +     ret = gpt_get_valid_metadata(&metadata);
> > +     if (ret < 0) {
> > +             log_err("Unable to get valid metadata\n");
> > +             goto out;
> > +     }
> > +
> > +     /*
> > +      * Swap the active index and previous_active_index fields
> > +      * in the metadata
> > +      */
> > +     cur_active_index = metadata->active_index;
> > +     metadata->active_index = metadata->previous_active_index;
> > +     metadata->previous_active_index = cur_active_index;
>
> Ditto, you don't need cur_active_index;
>

Will change.


>
> > +     *active_idx = metadata->active_index;
> > +
> > +     /*
> > +      * Calculate the crc32 for the updated metadata
> > +      * and put the updated value in the metadata crc32
> > +      * field
> > +      */
> > +     buf = &metadata->version;
> > +     metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > +
> > +     /*
> > +      * Now write this updated metadata to both the
> > +      * metadata partitions
> > +      */
> > +     ret = gpt_update_metadata(metadata);
> > +     if (ret < 0) {
> > +             log_err("Failed to update metadata partitions\n");
> > +             ret = -EIO;
> > +     }
> > +
> > +out:
> > +     free(metadata);
> > +
> > +     return ret;
> > +}
> > +
> > +static int fwu_gpt_set_clear_image_accept(efi_guid_t *img_type_id,
> > +                                       u32 bank, u8 action)
> > +{
> > +     void *buf;
> > +     int ret, i;
> > +     u32 nimages;
> > +     struct fwu_metadata *metadata;
> > +     struct fwu_image_entry *img_entry;
> > +     struct fwu_image_bank_info *img_bank_info;
> > +
> > +     ret = gpt_get_valid_metadata(&metadata);
> > +     if (ret < 0) {
> > +             log_err("Unable to get valid metadata\n");
> > +             goto out;
> > +     }
> > +
> > +     if (action == IMAGE_ACCEPT_SET)
> > +             bank = metadata->active_index;
>
> I think it's clearer if fwu_gpt_accept_image() /
> fwu_gpt_clear_accept_image() read the metadata themselves and pass them as
> a ptr.  That would mean you also have the right bank number and you wont be
> needing this anymore.
>

For clearing the accepted bit, the fwu_clear_accept_image function passes
the updated bank as a parameter. We thus need to pass the bank as a
parameter in any case. This would not be needed if the platform only has
two banks, but would be needed if the number of banks is more than two.


>
> > +
> > +     nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
> > +     img_entry = &metadata->img_entry[0];
> > +     for (i = 0; i < nimages; i++) {
> > +             if (!guidcmp(&img_entry[i].image_type_uuid, img_type_id)) {
> > +                     img_bank_info = &img_entry[i].img_bank_info[bank];
> > +                     if (action == IMAGE_ACCEPT_SET)
> > +                             img_bank_info->accepted |=
> FWU_IMAGE_ACCEPTED;
>
> Do you need to preserve existing bits on 'accepted' here?
>

The spec says that the Accepted field either should be 0 or 1. So this
should be fine.

-sughosh


> > +                     else
> > +                             img_bank_info->accepted = 0;
> > +
> > +                     buf = &metadata->version;
> > +                     metadata->crc32 = crc32(0, buf, sizeof(*metadata) -
> > +                                             sizeof(u32));
> > +
> > +                     ret = gpt_update_metadata(metadata);
> > +                     goto out;
> > +             }
> > +     }
> > +
> > +     /* Image not found */
> > +     ret = -EINVAL;
> > +
> > +out:
> > +     free(metadata);
> > +
> > +     return ret;
> > +}
> > +
> > +int fwu_gpt_accept_image(efi_guid_t *img_type_id)
> > +{
> > +     return fwu_gpt_set_clear_image_accept(img_type_id, 0,
> > +                                           IMAGE_ACCEPT_SET);
> > +}
> > +
> > +int fwu_gpt_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
> > +{
> > +     return fwu_gpt_set_clear_image_accept(img_type_id, bank,
> > +                                           IMAGE_ACCEPT_CLEAR);
> > +}
> > +
> > +struct fwu_metadata_ops fwu_gpt_blk_ops = {
> > +     .get_active_index = fwu_gpt_get_active_index,
> > +     .update_active_index = fwu_gpt_update_active_index,
> > +     .fill_partition_guid_array = fwu_gpt_fill_partition_guid_array,
> > +     .get_image_alt_num = fwu_gpt_get_image_alt_num,
> > +     .metadata_check = fwu_gpt_metadata_check,
> > +     .revert_boot_index = fwu_gpt_revert_boot_index,
> > +     .set_accept_image = fwu_gpt_accept_image,
> > +     .clear_accept_image = fwu_gpt_clear_accept_image,
> > +     .get_metadata = fwu_gpt_get_metadata,
> > +};
> > --
> > 2.17.1
> >
>
> Cheers
> /Ilias
>

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

* Re: [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-12-01 18:02   ` Simon Glass
@ 2021-12-02  8:05     ` Sughosh Ganu
  2021-12-02 13:34       ` Simon Glass
  0 siblings, 1 reply; 36+ messages in thread
From: Sughosh Ganu @ 2021-12-02  8:05 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu

hi Simon,

On Wed, 1 Dec 2021 at 23:32, Simon Glass <sjg@chromium.org> wrote:

> Hi Sughosh,
>
> On Thu, 25 Nov 2021 at 00:13, Sughosh Ganu <sughosh.ganu@linaro.org>
> wrote:
> >
> > In the FWU Multi Bank Update feature, the information about the
> > updatable images is stored as part of the metadata, on a separate
> > partition. Add functions for reading from and writing to the metadata
> > when the updatable images and the metadata are stored on a block
> > device which is formated with GPT based partition scheme.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
> >  1 file changed, 716 insertions(+)
> >  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
>
> Can we pick a better name than metadata? It is just so
> vague...everything is metadata.
>

The specification calls it metadata. And the functions in this file are for
accessing the metadata on a GPT partitioned block device. Do you have any
alternate name for this?

-sughosh

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

* Re: [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-12-02  8:05     ` Sughosh Ganu
@ 2021-12-02 13:34       ` Simon Glass
  2021-12-03  5:43         ` Sughosh Ganu
  0 siblings, 1 reply; 36+ messages in thread
From: Simon Glass @ 2021-12-02 13:34 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu

Hi Sughosh,

On Thu, 2 Dec 2021 at 01:05, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Simon,
>
> On Wed, 1 Dec 2021 at 23:32, Simon Glass <sjg@chromium.org> wrote:
>>
>> Hi Sughosh,
>>
>> On Thu, 25 Nov 2021 at 00:13, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>> >
>> > In the FWU Multi Bank Update feature, the information about the
>> > updatable images is stored as part of the metadata, on a separate
>> > partition. Add functions for reading from and writing to the metadata
>> > when the updatable images and the metadata are stored on a block
>> > device which is formated with GPT based partition scheme.
>> >
>> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
>> > ---
>> >  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
>> >  1 file changed, 716 insertions(+)
>> >  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
>>
>> Can we pick a better name than metadata? It is just so
>> vague...everything is metadata.
>
>
> The specification calls it metadata. And the functions in this file are for accessing the metadata on a GPT partitioned block device. Do you have any alternate name for this?

Well lots of things call their metadata "metadata". That doesn't mean
it is a good name for the code base. Which specification? Do you have
a link?

Regards,
Simon

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

* Re: [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-12-02 13:34       ` Simon Glass
@ 2021-12-03  5:43         ` Sughosh Ganu
  0 siblings, 0 replies; 36+ messages in thread
From: Sughosh Ganu @ 2021-12-03  5:43 UTC (permalink / raw)
  To: Simon Glass
  Cc: u-boot, Patrick Delaunay, Patrice Chotard, Heinrich Schuchardt,
	Alexander Graf, Bin Meng, Peng Fan, AKASHI Takahiro,
	Ilias Apalodimas, Jose Marinho, Grant Likely, Jason Liu

hi Simon,

On Thu, 2 Dec 2021 at 19:04, Simon Glass <sjg@chromium.org> wrote:

> Hi Sughosh,
>
> On Thu, 2 Dec 2021 at 01:05, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > hi Simon,
> >
> > On Wed, 1 Dec 2021 at 23:32, Simon Glass <sjg@chromium.org> wrote:
> >>
> >> Hi Sughosh,
> >>
> >> On Thu, 25 Nov 2021 at 00:13, Sughosh Ganu <sughosh.ganu@linaro.org>
> wrote:
> >> >
> >> > In the FWU Multi Bank Update feature, the information about the
> >> > updatable images is stored as part of the metadata, on a separate
> >> > partition. Add functions for reading from and writing to the metadata
> >> > when the updatable images and the metadata are stored on a block
> >> > device which is formated with GPT based partition scheme.
> >> >
> >> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> >> > ---
> >> >  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716
> +++++++++++++++++++++++++
> >> >  1 file changed, 716 insertions(+)
> >> >  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
> >>
> >> Can we pick a better name than metadata? It is just so
> >> vague...everything is metadata.
> >
> >
> > The specification calls it metadata. And the functions in this file are
> for accessing the metadata on a GPT partitioned block device. Do you have
> any alternate name for this?
>
> Well lots of things call their metadata "metadata". That doesn't mean
> it is a good name for the code base. Which specification? Do you have
> a link?
>

I am referring to the FWU specification[1]. Please refer to section 4 in
the document.

-sughosh

[1] - https://developer.arm.com/documentation/den0118/a

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

* Re: [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-12-02  7:43     ` Sughosh Ganu
@ 2021-12-08 14:17       ` Etienne Carriere
  2021-12-09  2:32         ` Simon Glass
  2021-12-09  7:37         ` Ilias Apalodimas
  0 siblings, 2 replies; 36+ messages in thread
From: Etienne Carriere @ 2021-12-08 14:17 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: Ilias Apalodimas, u-boot, Patrick Delaunay, Patrice Chotard,
	Heinrich Schuchardt, Alexander Graf, Simon Glass, Bin Meng,
	Peng Fan, AKASHI Takahiro, Jose Marinho, Grant Likely, Jason Liu

Hi Sughosh, Ilias (and all),

On Thu, 2 Dec 2021 at 08:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> hi Ilias,
>
> On Wed, 1 Dec 2021 at 17:56, Ilias Apalodimas <ilias.apalodimas@linaro.org>
> wrote:
>
> > Hi Sughosh,
> >
> > On Thu, Nov 25, 2021 at 12:42:56PM +0530, Sughosh Ganu wrote:
> > > In the FWU Multi Bank Update feature, the information about the
> > > updatable images is stored as part of the metadata, on a separate
> > > partition. Add functions for reading from and writing to the metadata
> > > when the updatable images and the metadata are stored on a block
> > > device which is formated with GPT based partition scheme.
> > >
> > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > ---
> > >  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
> > >  1 file changed, 716 insertions(+)
> > >  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
> > >
> > > +#define SECONDARY_VALID              0x2
> >
> >
> > Change those to BIT(0), BIT(1) etc please
> >
>
> Will change.
>
>
> >
> > > +
> > > +#define MDATA_READ           (u8)0x1
> > > +#define MDATA_WRITE          (u8)0x2
> > > +
> > > +#define IMAGE_ACCEPT_SET     (u8)0x1
> > > +#define IMAGE_ACCEPT_CLEAR   (u8)0x2
> > > +
> > > +static int gpt_verify_metadata(struct fwu_metadata *metadata, bool
> > pri_part)
> > > +{
> > > +     u32 calc_crc32;
> > > +     void *buf;
> > > +
> > > +     buf = &metadata->version;
> > > +     calc_crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > > +
> > > +     if (calc_crc32 != metadata->crc32) {
> > > +             log_err("crc32 check failed for %s metadata partition\n",
> > > +                     pri_part ? "primary" : "secondary");
> >
> > I think we can rid of the 'bool pri_part'.  It's only used for printing and
> > you can have more that 2 partitions anyway right?
> >
>
> We will have only two partitions for the metadata. But i think looking at
> it now, it is a bit fuzzy on which is the primary metadata partition and
> which is the secondary one. Can we instead print the UniquePartitionGUID of
> the metadata partition instead. That will help in identifying for which
> metadata copy the verification failed. Let me know your thoughts on this.
>
>
> > > +             return -1;
> > > +     }
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static int gpt_get_metadata_partitions(struct blk_desc *desc,
> >
> >
> > Please add a function descriptions (on following functions as well)
> >
>
> I have added the function descriptions in the fwu_metadata.c, where the
> api's are being defined. Do we need to add the descriptions for the
> functions in this file as well?
>
>
> >
> > > +                                    u32 *primary_mpart,
> > > +                                    u32 *secondary_mpart)
> >
> > u16 maybe? This is the number of gpt partitions with metadata right?
>
>
> Okay.
>
>
> >
> >
> > > +{
> > > +     int i, ret;
> > > +     u32 nparts, mparts;

I think the 2 variables labels are too similar, it's a source of confusion.
One is a number of entries, the second is a counter. It would be nice
it's a bit more explicit.

> > > +     gpt_entry *gpt_pte = NULL;
> > > +     const efi_guid_t fwu_metadata_guid = FWU_METADATA_GUID;
> > > +
> > > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > > +                                  desc->blksz);
> > > +
> > > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > > +     if (ret < 0) {
> > > +             log_err("Error getting GPT header and partitions\n");
> > > +             ret = -EIO;
> > > +             goto out;
> > > +     }
> > > +
> > > +     nparts = gpt_head->num_partition_entries;
> > > +     for (i = 0, mparts = 0; i < nparts; i++) {
> > > +             if (!guidcmp(&fwu_metadata_guid,
> > > +                          &gpt_pte[i].partition_type_guid)) {
> > > +                     ++mparts;
> > > +                     if (!*primary_mpart)
> > > +                             *primary_mpart = i + 1;
> > > +                     else
> > > +                             *secondary_mpart = i + 1;
> > > +             }
> > > +     }
> > > +
> > > +     if (mparts != 2) {
> > > +             log_err("Expect two copies of the metadata instead of
> > %d\n",
> > > +                     mparts);
> > > +             ret = -EINVAL;
> > > +     } else {
> > > +             ret = 0;
> > > +     }
> > > +out:
> > > +     free(gpt_pte);
> > > +
> > > +     return ret;
> > > +}
> > > +
> > > +static int gpt_get_metadata_disk_part(struct blk_desc *desc,
> > > +                                   struct disk_partition *info,
> > > +                                   u32 part_num)
> > > +{
> > > +     int ret;
> > > +     char *metadata_guid_str = "8a7a84a0-8387-40f6-ab41-a8b9a5a60d23";
> > > +
> > > +     ret = part_get_info(desc, part_num, info);
> > > +     if (ret < 0) {
> > > +             log_err("Unable to get the partition info for the metadata
> > part %d",
> > > +                     part_num);
> > > +             return -1;
> > > +     }
> > > +
> > > +     /* Check that it is indeed the metadata partition */
> > > +     if (!strncmp(info->type_guid, metadata_guid_str, UUID_STR_LEN)) {
> > > +             /* Found the metadata partition */
> > > +             return 0;
> > > +     }
> > > +
> > > +     return -1;
> > > +}
> > > +
> > > +static int gpt_read_write_metadata(struct blk_desc *desc,
> > > +                                struct fwu_metadata *metadata,
> > > +                                u8 access, u32 part_num)
> > > +{
> > > +     int ret;
> > > +     u32 len, blk_start, blkcnt;
> > > +     struct disk_partition info;
> > > +
> > > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(struct fwu_metadata, mdata, 1,
> > desc->blksz);
> > > +
> > > +     ret = gpt_get_metadata_disk_part(desc, &info, part_num);
> > > +     if (ret < 0) {
> > > +             printf("Unable to get the metadata partition\n");
> > > +             return -ENODEV;
> > > +     }
> > > +
> > > +     len = sizeof(*metadata);
> > > +     blkcnt = BLOCK_CNT(len, desc);
> > > +     if (blkcnt > info.size) {
> > > +             log_err("Block count exceeds metadata partition size\n");
> > > +             return -ERANGE;
> > > +     }
> > > +
> > > +     blk_start = info.start;
> > > +     if (access == MDATA_READ) {
> > > +             if (blk_dread(desc, blk_start, blkcnt, mdata) != blkcnt) {
> > > +                     log_err("Error reading metadata from the
> > device\n");
> > > +                     return -EIO;
> > > +             }
> > > +             memcpy(metadata, mdata, sizeof(struct fwu_metadata));
> > > +     } else {
> > > +             if (blk_dwrite(desc, blk_start, blkcnt, metadata) !=
> > blkcnt) {
> > > +                     log_err("Error writing metadata to the device\n");
> > > +                     return -EIO;
> > > +             }
> > > +     }
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static int gpt_read_metadata(struct blk_desc *desc,
> > > +                          struct fwu_metadata *metadata, u32 part_num)
> > > +{
> > > +     return gpt_read_write_metadata(desc, metadata, MDATA_READ,
> > part_num);
> > > +}
> > > +
> > > +static int gpt_write_metadata_partition(struct blk_desc *desc,
> > > +                                     struct fwu_metadata *metadata,
> > > +                                     u32 part_num)
> > > +{
> > > +     return gpt_read_write_metadata(desc, metadata, MDATA_WRITE,
> > part_num);
> > > +}
> > > +
> > > +static int gpt_update_metadata(struct fwu_metadata *metadata)
> > > +{
> > > +     int ret;
> > > +     struct blk_desc *desc;
> > > +     u32 primary_mpart, secondary_mpart;
> > > +
> > > +     ret = fwu_plat_get_blk_desc(&desc);
> > > +     if (ret < 0) {
> > > +             log_err("Block device not found\n");
> > > +             return -ENODEV;
> > > +     }
> > > +
> > > +     primary_mpart = secondary_mpart = 0;
> > > +     ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> > > +                                       &secondary_mpart);
> > > +
> > > +     if (ret < 0) {
> > > +             log_err("Error getting the metadata partitions\n");
> > > +             return -ENODEV;
> > > +     }
> > > +
> > > +     /* First write the primary partition*/
> > > +     ret = gpt_write_metadata_partition(desc, metadata, primary_mpart);
> > > +     if (ret < 0) {
> > > +             log_err("Updating primary metadata partition failed\n");
> > > +             return ret;
> > > +     }
> > > +
> > > +     /* And now the replica */
> > > +     ret = gpt_write_metadata_partition(desc, metadata,
> > secondary_mpart);
> > > +     if (ret < 0) {
> > > +             log_err("Updating secondary metadata partition failed\n");
> > > +             return ret;
> > > +     }
> >
> > So shouldn't we do something about this case?  The first partition was
> > correctly written and the second failed.  Now if the primary GPT somehow
> > gets corrupted the device is now unusable.

The replica is not there to overcome bitflips of the storage media.
It's here to allow updates while reliable info a still available in
the counter part.
The scheme could be to rely on only 1 instance of the fwu-metadata
(sorry Simon) image is valid.
A first load: load 1st instance, crap the second.
At update: find the crapped one: write it with new data. Upon success
crapped the alternate one.
This is a suggestion. There are many ways to handle that.

For sure, the scheme should be well defined so that the boot stage
that read fwu-data complies with the scheme used to write them.



> > I assume that depending on what happened to the firmware rollback counter,
> > we could either try to rewrite this, or revert the first one as well
> > (assuming rollback counters allow that).

Rollback counters should protect image version management, not
metadata updates (imho).

> >
>
> Okay, although this might be indicative of some underlying hardware issue
> with the device, else this scenario should not play out.
>
>
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static int gpt_get_valid_metadata(struct fwu_metadata **metadata)

Could be renamed gpt_get_metadata(), we don't expect to get invalid data :)


> > > +{
> > > +     int ret;
> > > +     struct blk_desc *desc;
> > > +     u32 primary_mpart, secondary_mpart;
> > > +
> > > +     ret = fwu_plat_get_blk_desc(&desc);
> > > +     if (ret < 0) {
> > > +             log_err("Block device not found\n");
> > > +             return -ENODEV;
> > > +     }
> > > +
> > > +     primary_mpart = secondary_mpart = 0;

Wouldn't it be better to have such default initialization values where
those variables are defined?

> > > +     ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> > > +                                       &secondary_mpart);
> > > +
> > > +     if (ret < 0) {
> > > +             log_err("Error getting the metadata partitions\n");
> > > +             return -ENODEV;
> > > +     }
> > > +
> > > +     *metadata = malloc(sizeof(struct fwu_metadata));
> > > +     if (!*metadata) {
> > > +             log_err("Unable to allocate memory for reading
> > metadata\n");
> > > +             return -ENOMEM;
> > > +     }
> > > +
> > > +     ret = gpt_read_metadata(desc, *metadata, primary_mpart);
> > > +     if (ret < 0) {
> > > +             log_err("Failed to read the metadata from the device\n");
> > > +             return -EIO;
> > > +     }
> > > +
> > > +     ret = gpt_verify_metadata(*metadata, 1);
> > > +     if (!ret)
> > > +             return 0;
> > > +
> > > +     /*
> > > +      * Verification of the primary metadata copy failed.
> > > +      * Try to read the replica.
> > > +      */
> > > +     memset(*metadata, 0, sizeof(struct fwu_metadata));
> > > +     ret = gpt_read_metadata(desc, *metadata, secondary_mpart);
> > > +     if (ret < 0) {
> > > +             log_err("Failed to read the metadata from the device\n");
> > > +             return -EIO;
> > > +     }
> > > +
> > > +     ret = gpt_verify_metadata(*metadata, 0);
> > > +     if (!ret)
> > > +             return 0;
> > > +
> > > +     /* Both the metadata copies are corrupted. */
> > > +     return -1;
> > > +}
> > > +
> > > +static int gpt_check_metadata_validity(void)
> > > +{
> > > +     int ret;
> > > +     struct blk_desc *desc;
> > > +     struct fwu_metadata *pri_metadata;
> > > +     struct fwu_metadata *secondary_metadata;
> >
> > init those to NULL so you can goto out and free
> > pri_metadata/secondary_metadata unconditionally
> > But do you really need a pointer here? Can't this just be
> > struct fwu_metadata pri_metadata, secondary_metadata;?
> >
>
> Yes, these can be declared as local variables, and that will get rid of the
> malloc. Will change.
>
>
> > > +     u32 primary_mpart, secondary_mpart;
> > > +     u32 valid_partitions;
> >
> > u16 for both I guess?
> >
>
> Okay.

unsigned int would do the work. No need for explicit byte-size type
here, it doesn't add value.

>
>
> >
> > > +
> > > +     ret = fwu_plat_get_blk_desc(&desc);
> > > +     if (ret < 0) {
> > > +             log_err("Block device not found\n");
> > > +             return -ENODEV;
> > > +     }
> > > +
> > > +     /*
> > > +      * Two metadata partitions are expected.
> > > +      * If we don't have two, user needs to create
> > > +      * them first
> > > +      */
> > > +     primary_mpart = secondary_mpart = 0;
> > > +     valid_partitions = 0;
> > > +     ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> > > +                                       &secondary_mpart);
> > > +
> > > +     if (ret < 0) {
> > > +             log_err("Error getting the metadata partitions\n");
> > > +             return -ENODEV;
> > > +     }
> > > +
> > > +     pri_metadata = malloc(sizeof(*pri_metadata));
> > > +     if (!pri_metadata) {
> > > +             log_err("Unable to allocate memory for reading
> > metadata\n");
> > > +             return -ENOMEM;
> > > +     }
> > > +
> > > +     secondary_metadata = malloc(sizeof(*secondary_metadata));
> > > +     if (!secondary_metadata) {
> > > +             log_err("Unable to allocate memory for reading
> > metadata\n");
> > > +             return -ENOMEM;
> > > +     }
> > > +
> > > +     ret = gpt_read_metadata(desc, pri_metadata, primary_mpart);
> > > +     if (ret < 0) {
> > > +             log_err("Failed to read the metadata from the device\n");
> > > +             ret = -EIO;
> > > +             goto out;
> >
> > It doesn't make sense to exit here without checking the secondary
> > partition.
> >
>
> Okay. Will revisit this logic. Although, this scenario should not play out
> unless there is some underlying issue with the device. But you are correct
> that if one of the metadata copies is valid, we can try restoring the other
> one.
>
>
> >
> > > +     }
> > > +
> > > +     ret = gpt_verify_metadata(pri_metadata, 1);
> > > +     if (!ret)
> > > +             valid_partitions |= PRIMARY_VALID;
> > > +
> > > +     /* Now check the secondary partition */
> > > +     ret = gpt_read_metadata(desc, secondary_metadata, secondary_mpart);
> > > +     if (ret < 0) {
> > > +             log_err("Failed to read the metadata from the device\n");
> > > +             ret = -EIO;
> >
> > Ditto, if the first is valid we can still rescue that.
> >
>
> Okay.
>
>
> >
> > > +             goto out;
> > > +     }
> > > +
> > > +     ret = gpt_verify_metadata(secondary_metadata, 0);
> > > +     if (!ret)
> > > +             valid_partitions |= SECONDARY_VALID;
> > > +
> > > +     if (valid_partitions == (PRIMARY_VALID | SECONDARY_VALID)) {
> > > +             ret = -1;
> > > +             /*
> > > +              * Before returning, check that both the
> > > +              * metadata copies are the same. If not,
> > > +              * the metadata copies need to be
> > > +              * re-populated.
> > > +              */
> > > +             if (!memcmp(pri_metadata, secondary_metadata,
> > > +                         sizeof(*pri_metadata)))
> > > +                     ret = 0;
> >
> > Is anyone else copying the metadata if this fails? In that case would it
> > make sense to just copy pri_metadata-> secondary_metadata here and sync
> > them up?
> >
>
> So this is a pretty fundamental error scenario where both metadata copies
> are valid, but are out of sync -- this should never happen. Will it be
> better instead to return an error and let the user check why this happened.
>
>
> > > +             goto out;
> > > +     } else if (valid_partitions == SECONDARY_VALID) {
> > > +             ret = gpt_write_metadata_partition(desc,
> > secondary_metadata,
> > > +                                                primary_mpart);
> > > +             if (ret < 0) {
> > > +                     log_err("Restoring primary metadata partition
> > failed\n");
> > > +                     goto out;
> > > +             }
> > > +     } else if (valid_partitions == PRIMARY_VALID) {
> > > +             ret = gpt_write_metadata_partition(desc, pri_metadata,
> > > +                                                secondary_mpart);
> > > +             if (ret < 0) {
> > > +                     log_err("Restoring secondary metadata partition
> > failed\n");
> > > +                     goto out;
> > > +             }
> > > +     } else {
> > > +             ret = -1;
> > > +     }
> >
> > I would write this whole if a bit differently. Since you have the valid
> > partitions in a bitmap.
> > redefine your original definitions like
> >
> > #define PRIMARY_VALID   BIT(0)
> > #define SECONDARY_VALID BIT(1)
> > #define BOTH_VALID      (PRIMARY_VALID | SECONDARY_VALID)
> >
> > if (!(valid_partitions & BOTH_VALID))
> >         goto out;
> >
> > wrong = valid_partitions ^ BOTH_VALID;
> > if (!out)
> >     <both valid code>
> > else
> >     <'wrong' is the number of invalid partition now>
> >         gpt_write_metadata_partition(desc,
> >                                                                  (wrong ==
> > PRIMARY_VALID) ? secondary_metadata :  pri_metadata),
> >                                                                  (wrong ==
> > PRIMARY_VALID) ? primary_mpart :  secondary_mpart)
> >
>
> I will check with you on this offline. Am a little confused here :)
>
>
> >
> > > +
> > > +out:
> > > +     free(pri_metadata);
> >
> > secondary_metadata needs freeing as well if you keep the ptrs
> >
>
> Yes, this is a remnant from my earlier implementation where i was
> allocating memory for both copies of metadata through a single call to
> malloc. But this will go away with declaration of local variables instead
> of malloc.
>
>
> >
> > > +
> > > +     return ret;
> > > +}
> > > +
> > > +static int gpt_fill_partition_guid_array(struct blk_desc *desc,
> > > +                                      efi_guid_t **part_guid_arr,
> > > +                                      u32 *nparts)
> > > +{
> > > +     int ret, i;
> > > +     u32 parts;
> > > +     gpt_entry *gpt_pte = NULL;
> > > +     const efi_guid_t null_guid = NULL_GUID;
> > > +
> > > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > > +                                  desc->blksz);
> > > +
> > > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > > +     if (ret < 0) {
> > > +             log_err("Error getting GPT header and partitions\n");
> > > +             ret = -EIO;
> > > +             goto out;
> > > +     }
> > > +
> > > +     *nparts = gpt_head->num_partition_entries;
> > > +
> > > +     /*
> > > +      * There could be a scenario where the number of partition entries
> > > +      * configured in the GPT header is the default value of 128. Find
> > > +      * the actual number of populated partitioned entries
> > > +      */
> > > +     for (i = 0, parts = 0; i < *nparts; i++) {
> >
> > Just init 'parts' on the declaration
> >
>
> Okay.
>
>
> >
> > > +             if (!guidcmp(&gpt_pte[i].partition_type_guid, &null_guid))
> > > +                     continue;
> > > +             ++parts;
> > > +     }
> > > +
> > > +     *nparts = parts;
> > > +     *part_guid_arr = malloc(sizeof(efi_guid_t) * *nparts);
> > > +     if (!part_guid_arr) {
> > > +             log_err("Unable to allocate memory for guid array\n");
> > > +             ret = -ENOMEM;
> > > +             goto out;
> > > +     }
> > > +
> > > +     for (i = 0; i < *nparts; i++) {
> > > +             guidcpy((*part_guid_arr + i),
> > > +                     &gpt_pte[i].partition_type_guid);
> > > +     }
> > > +
> > > +out:
> > > +     free(gpt_pte);
> > > +     return ret;
> > > +}
> > > +
> > > +int fwu_gpt_get_active_index(u32 *active_idx)
> > > +{
> > > +     int ret;
> > > +     struct fwu_metadata *metadata;
> > > +
> > > +     ret = gpt_get_valid_metadata(&metadata);
> > > +     if (ret < 0) {
> > > +             log_err("Unable to get valid metadata\n");
> > > +             goto out;
> > > +     }
> > > +
> > > +     /*
> > > +      * Found the metadata partition, now read the active_index
> > > +      * value
> > > +      */
> > > +     *active_idx = metadata->active_index;
> > > +     if (*active_idx > CONFIG_FWU_NUM_BANKS - 1) {
> > > +             printf("Active index value read is incorrect\n");
> > > +             ret = -EINVAL;
> > > +             goto out;
> > > +     }
> > > +
> > > +out:
> > > +     free(metadata);
> > > +
> > > +     return ret;
> > > +}
> > > +
> > > +static int gpt_get_image_alt_num(struct blk_desc *desc,
> > > +                              efi_guid_t image_type_id,
> > > +                              u32 update_bank, int *alt_no)
> > > +{
> > > +     int ret, i;
> > > +     u32 nparts;
> > > +     gpt_entry *gpt_pte = NULL;
> > > +     struct fwu_metadata *metadata;
> > > +     struct fwu_image_entry *img_entry;
> > > +     struct fwu_image_bank_info *img_bank_info;
> > > +     efi_guid_t unique_part_guid;
> > > +     efi_guid_t image_guid = NULL_GUID;
> > > +
> > > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > > +                                  desc->blksz);
> > > +
> > > +     ret = gpt_get_valid_metadata(&metadata);
> > > +     if (ret < 0) {
> > > +             log_err("Unable to read valid metadata\n");
> > > +             goto out;
> > > +     }
> > > +
> > > +     /*
> > > +      * The metadata has been read. Now get the image_uuid for the
> > > +      * image with the update_bank.
> > > +      */
> > > +     for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
> > > +             if (!guidcmp(&image_type_id,
> > > +                          &metadata->img_entry[i].image_type_uuid)) {
> > > +                     img_entry = &metadata->img_entry[i];
> > > +                     img_bank_info =
> > &img_entry->img_bank_info[update_bank];
> > > +                     guidcpy(&image_guid, &img_bank_info->image_uuid);
> >
> > break;?
> >
>
> Okay.
>
>
> >
> > > +             }
> > > +     }
> > > +
> > > +     /*
> > > +      * Now read the GPT Partition Table Entries to find a matching
> > > +      * partition with UniquePartitionGuid value. We need to iterate
> > > +      * through all the GPT partitions since they might be in any
> > > +      * order
> > > +      */
> > > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > > +     if (ret < 0) {
> > > +             log_err("Error getting GPT header and partitions\n");
> > > +             ret = -EIO;
> > > +             goto out;
> > > +     }
> > > +
> > > +     nparts = gpt_head->num_partition_entries;
> > > +
> > > +     for (i = 0; i < nparts; i++) {
> > > +             unique_part_guid = gpt_pte[i].unique_partition_guid;
> > > +             if (!guidcmp(&unique_part_guid, &image_guid)) {
> > > +                     /* Found the partition */
> > > +                     *alt_no = i + 1;
> > > +                     break;
> > > +             }
> > > +     }
> > > +
> > > +     if (i == nparts) {
> > > +             log_err("Partition with the image guid not found\n");
> > > +             ret = -EINVAL;
> > > +     }
> > > +
> > > +out:
> > > +     free(metadata);
> > > +     free(gpt_pte);
> > > +     return ret;
> > > +}
> > > +
> > > +int fwu_gpt_update_active_index(u32 active_idx)
> > > +{
> > > +     int ret;
> > > +     void *buf;
> > > +     u32 cur_active_index;
> > > +     struct fwu_metadata *metadata;
> > > +
> > > +     if (active_idx > CONFIG_FWU_NUM_BANKS) {
> > > +             printf("Active index value to be updated is incorrect\n");
> > > +             return -1;
> > > +     }
> > > +
> > > +     ret = gpt_get_valid_metadata(&metadata);
> > > +     if (ret < 0) {
> > > +             log_err("Unable to get valid metadata\n");
> > > +             goto out;
> > > +     }
> > > +
> > > +     /*
> > > +      * Update the active index and previous_active_index fields
> > > +      * in the metadata
> > > +      */
> > > +     cur_active_index = metadata->active_index;
> > > +     metadata->active_index = active_idx;
> > > +     metadata->previous_active_index = cur_active_index;
> >
> > You don't need the cur_active_index, just swap the 2 lines above.
> > metadata->previous_active_index = metadata->active_index;
> > metadata->active_index = active_idx;
> >
>
> Will change.
>
>
> >
> > > +
> > > +     /*
> > > +      * Calculate the crc32 for the updated metadata
> > > +      * and put the updated value in the metadata crc32
> > > +      * field
> > > +      */
> > > +     buf = &metadata->version;
> > > +     metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > > +
> > > +     /*
> > > +      * Now write this updated metadata to both the
> > > +      * metadata partitions
> > > +      */
> > > +     ret = gpt_update_metadata(metadata);
> > > +     if (ret < 0) {
> > > +             log_err("Failed to update metadata partitions\n");
> > > +             ret = -EIO;
> > > +     }
> > > +
> > > +out:
> > > +     free(metadata);
> > > +
> > > +     return ret;
> > > +}
> > > +
> > > +int fwu_gpt_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32
> > *nparts)
> > > +{
> > > +     int ret;
> > > +     struct blk_desc *desc;
> > > +
> > > +     ret = fwu_plat_get_blk_desc(&desc);
> > > +     if (ret < 0) {
> > > +             log_err("Block device not found\n");
> > > +             return -ENODEV;
> > > +     }
> > > +
> > > +     return gpt_fill_partition_guid_array(desc, part_guid_arr, nparts);
> > > +}
> > > +
> > > +int fwu_gpt_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> > > +                           int *alt_no)
> > > +{
> > > +     int ret;
> > > +     struct blk_desc *desc;
> > > +
> > > +     ret = fwu_plat_get_blk_desc(&desc);
> > > +     if (ret < 0) {
> > > +             log_err("Block device not found\n");
> > > +             return -ENODEV;
> > > +     }
> > > +
> > > +     return gpt_get_image_alt_num(desc, image_type_id, update_bank,
> > alt_no);
> > > +}
> > > +
> > > +int fwu_gpt_metadata_check(void)
> > > +{
> > > +     /*
> > > +      * Check if both the copies of the metadata are valid.
> > > +      * If one has gone bad, restore it from the other good
> > > +      * copy.
> > > +      */
> > > +     return gpt_check_metadata_validity();
> > > +}
> > > +
> > > +int fwu_gpt_get_metadata(struct fwu_metadata **metadata)
> > > +{
> > > +     return gpt_get_valid_metadata(metadata);
> > > +}
> > > +
> > > +int fwu_gpt_revert_boot_index(u32 *active_idx)
> > > +{
> > > +     int ret;
> > > +     void *buf;
> > > +     u32 cur_active_index;
> > > +     struct fwu_metadata *metadata;
> > > +
> > > +     ret = gpt_get_valid_metadata(&metadata);
> > > +     if (ret < 0) {
> > > +             log_err("Unable to get valid metadata\n");
> > > +             goto out;
> > > +     }
> > > +
> > > +     /*
> > > +      * Swap the active index and previous_active_index fields
> > > +      * in the metadata
> > > +      */
> > > +     cur_active_index = metadata->active_index;
> > > +     metadata->active_index = metadata->previous_active_index;
> > > +     metadata->previous_active_index = cur_active_index;
> >
> > Ditto, you don't need cur_active_index;
> >
>
> Will change.
>
>
> >
> > > +     *active_idx = metadata->active_index;
> > > +
> > > +     /*
> > > +      * Calculate the crc32 for the updated metadata
> > > +      * and put the updated value in the metadata crc32
> > > +      * field
> > > +      */
> > > +     buf = &metadata->version;
> > > +     metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > > +
> > > +     /*
> > > +      * Now write this updated metadata to both the
> > > +      * metadata partitions
> > > +      */
> > > +     ret = gpt_update_metadata(metadata);
> > > +     if (ret < 0) {
> > > +             log_err("Failed to update metadata partitions\n");
> > > +             ret = -EIO;
> > > +     }
> > > +
> > > +out:
> > > +     free(metadata);
> > > +
> > > +     return ret;
> > > +}
> > > +
> > > +static int fwu_gpt_set_clear_image_accept(efi_guid_t *img_type_id,
> > > +                                       u32 bank, u8 action)
> > > +{
> > > +     void *buf;
> > > +     int ret, i;
> > > +     u32 nimages;
> > > +     struct fwu_metadata *metadata;
> > > +     struct fwu_image_entry *img_entry;
> > > +     struct fwu_image_bank_info *img_bank_info;
> > > +
> > > +     ret = gpt_get_valid_metadata(&metadata);
> > > +     if (ret < 0) {
> > > +             log_err("Unable to get valid metadata\n");
> > > +             goto out;
> > > +     }
> > > +
> > > +     if (action == IMAGE_ACCEPT_SET)
> > > +             bank = metadata->active_index;
> >
> > I think it's clearer if fwu_gpt_accept_image() /
> > fwu_gpt_clear_accept_image() read the metadata themselves and pass them as
> > a ptr.  That would mean you also have the right bank number and you wont be
> > needing this anymore.
> >
>
> For clearing the accepted bit, the fwu_clear_accept_image function passes
> the updated bank as a parameter. We thus need to pass the bank as a
> parameter in any case. This would not be needed if the platform only has
> two banks, but would be needed if the number of banks is more than two.
>
>
> >
> > > +
> > > +     nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
> > > +     img_entry = &metadata->img_entry[0];
> > > +     for (i = 0; i < nimages; i++) {
> > > +             if (!guidcmp(&img_entry[i].image_type_uuid, img_type_id)) {
> > > +                     img_bank_info = &img_entry[i].img_bank_info[bank];
> > > +                     if (action == IMAGE_ACCEPT_SET)
> > > +                             img_bank_info->accepted |=
> > FWU_IMAGE_ACCEPTED;
> >
> > Do you need to preserve existing bits on 'accepted' here?
> >
>
> The spec says that the Accepted field either should be 0 or 1. So this
> should be fine.
>
> -sughosh
>
>
> > > +                     else
> > > +                             img_bank_info->accepted = 0;
> > > +
> > > +                     buf = &metadata->version;
> > > +                     metadata->crc32 = crc32(0, buf, sizeof(*metadata) -
> > > +                                             sizeof(u32));
> > > +
> > > +                     ret = gpt_update_metadata(metadata);
> > > +                     goto out;
> > > +             }
> > > +     }
> > > +
> > > +     /* Image not found */
> > > +     ret = -EINVAL;
> > > +
> > > +out:
> > > +     free(metadata);
> > > +
> > > +     return ret;
> > > +}
> > > +
> > > +int fwu_gpt_accept_image(efi_guid_t *img_type_id)
> > > +{
> > > +     return fwu_gpt_set_clear_image_accept(img_type_id, 0,
> > > +                                           IMAGE_ACCEPT_SET);
> > > +}
> > > +
> > > +int fwu_gpt_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
> > > +{
> > > +     return fwu_gpt_set_clear_image_accept(img_type_id, bank,
> > > +                                           IMAGE_ACCEPT_CLEAR);
> > > +}
> > > +
> > > +struct fwu_metadata_ops fwu_gpt_blk_ops = {
> > > +     .get_active_index = fwu_gpt_get_active_index,
> > > +     .update_active_index = fwu_gpt_update_active_index,
> > > +     .fill_partition_guid_array = fwu_gpt_fill_partition_guid_array,
> > > +     .get_image_alt_num = fwu_gpt_get_image_alt_num,
> > > +     .metadata_check = fwu_gpt_metadata_check,
> > > +     .revert_boot_index = fwu_gpt_revert_boot_index,
> > > +     .set_accept_image = fwu_gpt_accept_image,
> > > +     .clear_accept_image = fwu_gpt_clear_accept_image,
> > > +     .get_metadata = fwu_gpt_get_metadata,
> > > +};
> > > --
> > > 2.17.1
> > >
> >
> > Cheers
> > /Ilias
> >

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

* Re: [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-12-08 14:17       ` Etienne Carriere
@ 2021-12-09  2:32         ` Simon Glass
  2021-12-09  7:37         ` Ilias Apalodimas
  1 sibling, 0 replies; 36+ messages in thread
From: Simon Glass @ 2021-12-09  2:32 UTC (permalink / raw)
  To: Etienne Carriere
  Cc: Sughosh Ganu, Ilias Apalodimas, u-boot, Patrick Delaunay,
	Patrice Chotard, Heinrich Schuchardt, Alexander Graf, Bin Meng,
	Peng Fan, AKASHI Takahiro, Jose Marinho, Grant Likely, Jason Liu

Hi Etienne,

On Wed, 8 Dec 2021 at 07:18, Etienne Carriere
<etienne.carriere@linaro.org> wrote:
>
> Hi Sughosh, Ilias (and all),
>
> On Thu, 2 Dec 2021 at 08:43, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
> >
> > hi Ilias,
> >
> > On Wed, 1 Dec 2021 at 17:56, Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > wrote:
> >
> > > Hi Sughosh,
> > >
> > > On Thu, Nov 25, 2021 at 12:42:56PM +0530, Sughosh Ganu wrote:
> > > > In the FWU Multi Bank Update feature, the information about the
> > > > updatable images is stored as part of the metadata, on a separate
> > > > partition. Add functions for reading from and writing to the metadata
> > > > when the updatable images and the metadata are stored on a block
> > > > device which is formated with GPT based partition scheme.
> > > >
> > > > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > > > ---
> > > >  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
> > > >  1 file changed, 716 insertions(+)
> > > >  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
> > > >
> > > > +#define SECONDARY_VALID              0x2
> > >
> > >
> > > Change those to BIT(0), BIT(1) etc please
> > >
> >
> > Will change.
> >
> >
> > >
> > > > +
> > > > +#define MDATA_READ           (u8)0x1
> > > > +#define MDATA_WRITE          (u8)0x2
> > > > +
> > > > +#define IMAGE_ACCEPT_SET     (u8)0x1
> > > > +#define IMAGE_ACCEPT_CLEAR   (u8)0x2
> > > > +
> > > > +static int gpt_verify_metadata(struct fwu_metadata *metadata, bool
> > > pri_part)
> > > > +{
> > > > +     u32 calc_crc32;
> > > > +     void *buf;
> > > > +
> > > > +     buf = &metadata->version;
> > > > +     calc_crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > > > +
> > > > +     if (calc_crc32 != metadata->crc32) {
> > > > +             log_err("crc32 check failed for %s metadata partition\n",
> > > > +                     pri_part ? "primary" : "secondary");
> > >
> > > I think we can rid of the 'bool pri_part'.  It's only used for printing and
> > > you can have more that 2 partitions anyway right?
> > >
> >
> > We will have only two partitions for the metadata. But i think looking at
> > it now, it is a bit fuzzy on which is the primary metadata partition and
> > which is the secondary one. Can we instead print the UniquePartitionGUID of
> > the metadata partition instead. That will help in identifying for which
> > metadata copy the verification failed. Let me know your thoughts on this.
> >
> >
> > > > +             return -1;
> > > > +     }
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static int gpt_get_metadata_partitions(struct blk_desc *desc,
> > >
> > >
> > > Please add a function descriptions (on following functions as well)
> > >
> >
> > I have added the function descriptions in the fwu_metadata.c, where the
> > api's are being defined. Do we need to add the descriptions for the
> > functions in this file as well?
> >
> >
> > >
> > > > +                                    u32 *primary_mpart,
> > > > +                                    u32 *secondary_mpart)
> > >
> > > u16 maybe? This is the number of gpt partitions with metadata right?
> >
> >
> > Okay.
> >
> >
> > >
> > >
> > > > +{
> > > > +     int i, ret;
> > > > +     u32 nparts, mparts;
>
> I think the 2 variables labels are too similar, it's a source of confusion.
> One is a number of entries, the second is a counter. It would be nice
> it's a bit more explicit.
>
> > > > +     gpt_entry *gpt_pte = NULL;
> > > > +     const efi_guid_t fwu_metadata_guid = FWU_METADATA_GUID;
> > > > +
> > > > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > > > +                                  desc->blksz);
> > > > +
> > > > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > > > +     if (ret < 0) {
> > > > +             log_err("Error getting GPT header and partitions\n");
> > > > +             ret = -EIO;
> > > > +             goto out;
> > > > +     }
> > > > +
> > > > +     nparts = gpt_head->num_partition_entries;
> > > > +     for (i = 0, mparts = 0; i < nparts; i++) {
> > > > +             if (!guidcmp(&fwu_metadata_guid,
> > > > +                          &gpt_pte[i].partition_type_guid)) {
> > > > +                     ++mparts;
> > > > +                     if (!*primary_mpart)
> > > > +                             *primary_mpart = i + 1;
> > > > +                     else
> > > > +                             *secondary_mpart = i + 1;
> > > > +             }
> > > > +     }
> > > > +
> > > > +     if (mparts != 2) {
> > > > +             log_err("Expect two copies of the metadata instead of
> > > %d\n",
> > > > +                     mparts);
> > > > +             ret = -EINVAL;
> > > > +     } else {
> > > > +             ret = 0;
> > > > +     }
> > > > +out:
> > > > +     free(gpt_pte);
> > > > +
> > > > +     return ret;
> > > > +}
> > > > +
> > > > +static int gpt_get_metadata_disk_part(struct blk_desc *desc,
> > > > +                                   struct disk_partition *info,
> > > > +                                   u32 part_num)
> > > > +{
> > > > +     int ret;
> > > > +     char *metadata_guid_str = "8a7a84a0-8387-40f6-ab41-a8b9a5a60d23";
> > > > +
> > > > +     ret = part_get_info(desc, part_num, info);
> > > > +     if (ret < 0) {
> > > > +             log_err("Unable to get the partition info for the metadata
> > > part %d",
> > > > +                     part_num);
> > > > +             return -1;
> > > > +     }
> > > > +
> > > > +     /* Check that it is indeed the metadata partition */
> > > > +     if (!strncmp(info->type_guid, metadata_guid_str, UUID_STR_LEN)) {
> > > > +             /* Found the metadata partition */
> > > > +             return 0;
> > > > +     }
> > > > +
> > > > +     return -1;
> > > > +}
> > > > +
> > > > +static int gpt_read_write_metadata(struct blk_desc *desc,
> > > > +                                struct fwu_metadata *metadata,
> > > > +                                u8 access, u32 part_num)
> > > > +{
> > > > +     int ret;
> > > > +     u32 len, blk_start, blkcnt;
> > > > +     struct disk_partition info;
> > > > +
> > > > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(struct fwu_metadata, mdata, 1,
> > > desc->blksz);
> > > > +
> > > > +     ret = gpt_get_metadata_disk_part(desc, &info, part_num);
> > > > +     if (ret < 0) {
> > > > +             printf("Unable to get the metadata partition\n");
> > > > +             return -ENODEV;
> > > > +     }
> > > > +
> > > > +     len = sizeof(*metadata);
> > > > +     blkcnt = BLOCK_CNT(len, desc);
> > > > +     if (blkcnt > info.size) {
> > > > +             log_err("Block count exceeds metadata partition size\n");
> > > > +             return -ERANGE;
> > > > +     }
> > > > +
> > > > +     blk_start = info.start;
> > > > +     if (access == MDATA_READ) {
> > > > +             if (blk_dread(desc, blk_start, blkcnt, mdata) != blkcnt) {
> > > > +                     log_err("Error reading metadata from the
> > > device\n");
> > > > +                     return -EIO;
> > > > +             }
> > > > +             memcpy(metadata, mdata, sizeof(struct fwu_metadata));
> > > > +     } else {
> > > > +             if (blk_dwrite(desc, blk_start, blkcnt, metadata) !=
> > > blkcnt) {
> > > > +                     log_err("Error writing metadata to the device\n");
> > > > +                     return -EIO;
> > > > +             }
> > > > +     }
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static int gpt_read_metadata(struct blk_desc *desc,
> > > > +                          struct fwu_metadata *metadata, u32 part_num)
> > > > +{
> > > > +     return gpt_read_write_metadata(desc, metadata, MDATA_READ,
> > > part_num);
> > > > +}
> > > > +
> > > > +static int gpt_write_metadata_partition(struct blk_desc *desc,
> > > > +                                     struct fwu_metadata *metadata,
> > > > +                                     u32 part_num)
> > > > +{
> > > > +     return gpt_read_write_metadata(desc, metadata, MDATA_WRITE,
> > > part_num);
> > > > +}
> > > > +
> > > > +static int gpt_update_metadata(struct fwu_metadata *metadata)
> > > > +{
> > > > +     int ret;
> > > > +     struct blk_desc *desc;
> > > > +     u32 primary_mpart, secondary_mpart;
> > > > +
> > > > +     ret = fwu_plat_get_blk_desc(&desc);
> > > > +     if (ret < 0) {
> > > > +             log_err("Block device not found\n");
> > > > +             return -ENODEV;
> > > > +     }
> > > > +
> > > > +     primary_mpart = secondary_mpart = 0;
> > > > +     ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> > > > +                                       &secondary_mpart);
> > > > +
> > > > +     if (ret < 0) {
> > > > +             log_err("Error getting the metadata partitions\n");
> > > > +             return -ENODEV;
> > > > +     }
> > > > +
> > > > +     /* First write the primary partition*/
> > > > +     ret = gpt_write_metadata_partition(desc, metadata, primary_mpart);
> > > > +     if (ret < 0) {
> > > > +             log_err("Updating primary metadata partition failed\n");
> > > > +             return ret;
> > > > +     }
> > > > +
> > > > +     /* And now the replica */
> > > > +     ret = gpt_write_metadata_partition(desc, metadata,
> > > secondary_mpart);
> > > > +     if (ret < 0) {
> > > > +             log_err("Updating secondary metadata partition failed\n");
> > > > +             return ret;
> > > > +     }
> > >
> > > So shouldn't we do something about this case?  The first partition was
> > > correctly written and the second failed.  Now if the primary GPT somehow
> > > gets corrupted the device is now unusable.
>
> The replica is not there to overcome bitflips of the storage media.
> It's here to allow updates while reliable info a still available in
> the counter part.
> The scheme could be to rely on only 1 instance of the fwu-metadata
> (sorry Simon) image is valid.
> A first load: load 1st instance, crap the second.
> At update: find the crapped one: write it with new data. Upon success
> crapped the alternate one.
> This is a suggestion. There are many ways to handle that.
>
> For sure, the scheme should be well defined so that the boot stage
> that read fwu-data complies with the scheme used to write them.
>
>
>
> > > I assume that depending on what happened to the firmware rollback counter,
> > > we could either try to rewrite this, or revert the first one as well
> > > (assuming rollback counters allow that).
>
> Rollback counters should protect image version management, not
> metadata updates (imho).
>
> > >
> >
> > Okay, although this might be indicative of some underlying hardware issue
> > with the device, else this scenario should not play out.
> >
> >
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static int gpt_get_valid_metadata(struct fwu_metadata **metadata)
>
> Could be renamed gpt_get_metadata(), we don't expect to get invalid data :)

How about gpt_get_mdata() ?

When I read this I thought it was getting the GPT table...'metadata'
is just too generic.

Regards,
Simon

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

* Re: [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-12-08 14:17       ` Etienne Carriere
  2021-12-09  2:32         ` Simon Glass
@ 2021-12-09  7:37         ` Ilias Apalodimas
  2021-12-13  9:29           ` Etienne Carriere
  1 sibling, 1 reply; 36+ messages in thread
From: Ilias Apalodimas @ 2021-12-09  7:37 UTC (permalink / raw)
  To: Etienne Carriere
  Cc: Sughosh Ganu, u-boot, Patrick Delaunay, Patrice Chotard,
	Heinrich Schuchardt, Alexander Graf, Simon Glass, Bin Meng,
	Peng Fan, AKASHI Takahiro, Jose Marinho, Grant Likely, Jason Liu

Hi Etienne,


[...]
> > > > +
> > > > +     /* And now the replica */
> > > > +     ret = gpt_write_metadata_partition(desc, metadata,
> > > secondary_mpart);
> > > > +     if (ret < 0) {
> > > > +             log_err("Updating secondary metadata partition failed\n");
> > > > +             return ret;
> > > > +     }
> > >
> > > So shouldn't we do something about this case?  The first partition was
> > > correctly written and the second failed.  Now if the primary GPT somehow
> > > gets corrupted the device is now unusable.
>
> The replica is not there to overcome bitflips of the storage media.
> It's here to allow updates while reliable info a still available in
> the counter part.

Sure but with this piece of code this assumption is broken.  At the
point the secondary partition fails to write, you loose that
reliability. When the next update happens you are left with one valid
and one invalid partition of metadata.

> The scheme could be to rely on only 1 instance of the fwu-metadata
> (sorry Simon) image is valid.
> A first load: load 1st instance, crap the second.
> At update: find the crapped one: write it with new data. Upon success
> crapped the alternate one.
> This is a suggestion. There are many ways to handle that.

We could change to something like that, however this is not what's
currently happening.  gpt_check_metadata_validity() is trying to check
and make sure both of the partitions are sane.  If they aren't it
tries to recover those looking at a sane partition. So the question
for really is, should we do something *here* or rely on the fact that
the next update will try to fix the broken metadata.

Cheers
/Ilias

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

* Re: [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
  2021-12-09  7:37         ` Ilias Apalodimas
@ 2021-12-13  9:29           ` Etienne Carriere
  0 siblings, 0 replies; 36+ messages in thread
From: Etienne Carriere @ 2021-12-13  9:29 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Sughosh Ganu, u-boot, Patrick Delaunay, Patrice Chotard,
	Heinrich Schuchardt, Alexander Graf, Simon Glass, Bin Meng,
	Peng Fan, AKASHI Takahiro, Jose Marinho, Grant Likely, Jason Liu

Hi Ilias,

On Thu, 9 Dec 2021 at 08:37, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> Hi Etienne,
>
>
> [...]
> > > > > +
> > > > > +     /* And now the replica */
> > > > > +     ret = gpt_write_metadata_partition(desc, metadata,
> > > > secondary_mpart);
> > > > > +     if (ret < 0) {
> > > > > +             log_err("Updating secondary metadata partition failed\n");
> > > > > +             return ret;
> > > > > +     }
> > > >
> > > > So shouldn't we do something about this case?  The first partition was
> > > > correctly written and the second failed.  Now if the primary GPT somehow
> > > > gets corrupted the device is now unusable.
> >
> > The replica is not there to overcome bitflips of the storage media.
> > It's here to allow updates while reliable info a still available in
> > the counter part.
>
> Sure but with this piece of code this assumption is broken.  At the
> point the secondary partition fails to write, you loose that
> reliability. When the next update happens you are left with one valid
> and one invalid partition of metadata.
>
> > The scheme could be to rely on only 1 instance of the fwu-metadata
> > (sorry Simon) image is valid.
> > A first load: load 1st instance, crap the second.
> > At update: find the crapped one: write it with new data. Upon success
> > crapped the alternate one.
> > This is a suggestion. There are many ways to handle that.
>
> We could change to something like that, however this is not what's
> currently happening.  gpt_check_metadata_validity() is trying to check
> and make sure both of the partitions are sane.  If they aren't it
> tries to recover those looking at a sane partition. So the question
> for really is, should we do something *here* or rely on the fact that
> the next update will try to fix the broken metadata.
>
> Cheers
> /Ilias

I think the right sequence would be to check if 1 of the 2 mdat
partitions is broken,
update that first and return an error on failure,
then update the one sane and emit a warning on failure.

Cheers,
etienne

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

end of thread, other threads:[~2021-12-13  9:30 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-25  7:12 [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
2021-11-25  7:12 ` [RESEND RFC PATCH 01/10] GPT: Add function to get gpt header and partition entries Sughosh Ganu
2021-11-25  7:12 ` [RESEND RFC PATCH 02/10] stm32mp: dfu: Move the ram partitions to the end of the dfu_alt_info variable Sughosh Ganu
2021-11-25  7:12 ` [RESEND RFC PATCH 03/10] FWU: Add metadata structure and functions for accessing metadata Sughosh Ganu
2021-11-26 11:35   ` Ilias Apalodimas
2021-11-29  6:38     ` Sughosh Ganu
2021-11-30 12:57   ` Heinrich Schuchardt
2021-12-01  5:36     ` Sughosh Ganu
2021-12-01  7:50       ` Ilias Apalodimas
2021-12-01  8:31         ` Sughosh Ganu
2021-12-01  7:46     ` Ilias Apalodimas
2021-12-01  6:26   ` Simon Glass
2021-12-01  6:42     ` Sughosh Ganu
2021-11-25  7:12 ` [RESEND RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices Sughosh Ganu
2021-12-01 12:26   ` Ilias Apalodimas
2021-12-02  7:43     ` Sughosh Ganu
2021-12-08 14:17       ` Etienne Carriere
2021-12-09  2:32         ` Simon Glass
2021-12-09  7:37         ` Ilias Apalodimas
2021-12-13  9:29           ` Etienne Carriere
2021-12-01 18:02   ` Simon Glass
2021-12-02  8:05     ` Sughosh Ganu
2021-12-02 13:34       ` Simon Glass
2021-12-03  5:43         ` Sughosh Ganu
2021-11-25  7:12 ` [RESEND RFC PATCH 05/10] FWU: stm32mp1: Add helper functions for accessing metadata Sughosh Ganu
2021-11-25  7:12 ` [RESEND RFC PATCH 06/10] FWU: STM32MP1: Add support to read boot index from backup register Sughosh Ganu
2021-11-25  7:12 ` [RESEND RFC PATCH 07/10] EFI: FMP: Add provision to update image's ImageTypeId in image descriptor Sughosh Ganu
2021-11-26 12:43   ` Heinrich Schuchardt
2021-11-29 11:38     ` Sughosh Ganu
2021-11-25  7:13 ` [RESEND RFC PATCH 08/10] FWU: Add boot time checks as highlighted by the FWU specification Sughosh Ganu
2021-11-25  7:13 ` [RESEND RFC PATCH 09/10] FWU: Add support for FWU Multi Bank Update feature Sughosh Ganu
2021-11-26 12:55   ` Heinrich Schuchardt
2021-11-29 11:44     ` Sughosh Ganu
2021-11-25  7:13 ` [RESEND RFC PATCH 10/10] FWU: cmd: Add a command to read metadata Sughosh Ganu
2021-11-26 12:29 ` [RESEND RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature Heinrich Schuchardt
2021-11-26 12:48   ` 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.