[RFC PATCH 00/10] FWU: Add support for FWU Multi Bank Update feature

The FWU multi bank update feature is used for supporting multiple sets(also called banks) of firmware images, 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 images.
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.
This also requires changes in the previous stage of bootloader, which parses the metadata and selects the bank to boot the images from.
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. 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].
The following config's need to be selected for enabling the feature CONFIG_FWU_MULTI_BANK_UPDATE CONFIG_EFI_CAPSULE_ON_DISK CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT CONFIG_EFI_CAPSULE_FIRMWARE CONFIG_EFI_CAPSULE_FIRMWARE_RAW CONFIG_EFI_CAPSULE_FMP_HEADER -- if capsule is being generated using EDKII GenerateCapsule script CONFIG_CMD_FWU_METADATA
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

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 *

Hi Sugosh
On 11/25/21 8:01 AM, Sughosh Ganu wrote:
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 mailto:sughosh.ganu@linaro.org 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
gpt_h need to be allocated / DMA alligned by caller
+ * @param gpt_e - pointer to GPT partition table entries
gpt_e is allocated in this fucntion need to freed by caller
+ * + * @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 *
no need to export the low level function "find_valid_gpt"
the same services are already provided by part_get_info() ?
ST Restricted
2 information are used in the serie
=> gpt_head->num_partition_entries => &gpt_pte[i].partition_type_guid
type_guid is available in disk_partition, and num_partition_entries is not needed (replaced by MAX_SEARCH_PARTITIONS)
something like:
struct disk_partition info;
for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { if (part_get_info(desc, p, &info)) continue; => info.type_guid }
Regards
Patrick

hi Patrick,
On Tue, 7 Dec 2021 at 21:05, Patrick DELAUNAY patrick.delaunay@foss.st.com wrote:
Hi Sugosh
On 11/25/21 8:01 AM, Sughosh Ganu wrote:
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 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
gpt_h need to be allocated / DMA alligned by caller
Will add this comment to the description.
- @param gpt_e - pointer to GPT partition table entries
gpt_e is allocated in this fucntion need to freed by caller
Will add this comment to the description.
- @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
no need to export the low level function "find_valid_gpt"
find_valid_gpt is not being exported. It is defined statically in part_efi.c. Do i misunderstand your comment?
the same services are already provided by part_get_info() ?
ST Restricted
2 information are used in the serie
=> gpt_head->num_partition_entries
=> &gpt_pte[i].partition_type_guid
type_guid is available in disk_partition,
and num_partition_entries is not needed (replaced by MAX_SEARCH_PARTITIONS)
something like:
struct disk_partition info; for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { if (part_get_info(desc, p, &info)) continue; => info.type_guid }
I had actually explored using part_get_info. But the type_guid field is actually a char string. While I need the guid value to compare it using guidcmp. Which is the reason I introduced get_gpt_hdr_parts.
-sughosh
Regards
Patrick

On Wed, Dec 08, 2021 at 01:10:51PM +0530, Sughosh Ganu wrote:
hi Patrick,
On Tue, 7 Dec 2021 at 21:05, Patrick DELAUNAY patrick.delaunay@foss.st.com wrote:
Hi Sugosh
On 11/25/21 8:01 AM, Sughosh Ganu wrote:
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 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
gpt_h need to be allocated / DMA alligned by caller
Will add this comment to the description.
- @param gpt_e - pointer to GPT partition table entries
gpt_e is allocated in this fucntion need to freed by caller
Will add this comment to the description.
- @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
no need to export the low level function "find_valid_gpt"
find_valid_gpt is not being exported. It is defined statically in part_efi.c. Do i misunderstand your comment?
the same services are already provided by part_get_info() ?
ST Restricted
2 information are used in the serie
=> gpt_head->num_partition_entries
=> &gpt_pte[i].partition_type_guid
type_guid is available in disk_partition,
and num_partition_entries is not needed (replaced by MAX_SEARCH_PARTITIONS)
something like:
struct disk_partition info; for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { if (part_get_info(desc, p, &info)) continue; => info.type_guid }
I had actually explored using part_get_info. But the type_guid field is actually a char string. While I need the guid value to compare it using guidcmp. Which is the reason I introduced get_gpt_hdr_parts.
Think of using uuid_str_to_bin() if this is the only reason.
-Takahiro Akashi
-sughosh
Regards
Patrick

On Thu, 9 Dec 2021 at 07:02, AKASHI Takahiro takahiro.akashi@linaro.org wrote:
On Wed, Dec 08, 2021 at 01:10:51PM +0530, Sughosh Ganu wrote:
hi Patrick,
On Tue, 7 Dec 2021 at 21:05, Patrick DELAUNAY <
patrick.delaunay@foss.st.com>
wrote:
Hi Sugosh
On 11/25/21 8:01 AM, Sughosh Ganu wrote:
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 <
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
gpt_h need to be allocated / DMA alligned by caller
Will add this comment to the description.
- @param gpt_e - pointer to GPT partition table entries
gpt_e is allocated in this fucntion need to freed by caller
Will add this comment to the description.
- @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
no need to export the low level function "find_valid_gpt"
find_valid_gpt is not being exported. It is defined statically in part_efi.c. Do i misunderstand your comment?
the same services are already provided by part_get_info() ?
ST Restricted
2 information are used in the serie
=> gpt_head->num_partition_entries
=> &gpt_pte[i].partition_type_guid
type_guid is available in disk_partition,
and num_partition_entries is not needed (replaced by
MAX_SEARCH_PARTITIONS)
something like:
struct disk_partition info; for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { if (part_get_info(desc, p, &info)) continue; => info.type_guid }
I had actually explored using part_get_info. But the type_guid field is actually a char string. While I need the guid value to compare it using guidcmp. Which is the reason I introduced get_gpt_hdr_parts.
Think of using uuid_str_to_bin() if this is the only reason.
Will check if i can use this function. Thanks.
-sughosh
-Takahiro Akashi
-sughosh
Regards
Patrick

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"); }

On Thu, 25 Nov 2021 at 08:03, Sughosh Ganu sughosh.ganu@linaro.org wrote:
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);
s/DFU_ALT_BUF_LEN/DFU_ALT_BUF_LEN - len/
Should check snprintf() return value.
env_set("dfu_alt_info", buf); puts("DFU alt info setting: done\n");
}
2.17.1

hi Etienne,
On Wed, 8 Dec 2021 at 18:44, Etienne Carriere etienne.carriere@linaro.org wrote:
On Thu, 25 Nov 2021 at 08:03, Sughosh Ganu sughosh.ganu@linaro.org wrote:
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);
s/DFU_ALT_BUF_LEN/DFU_ALT_BUF_LEN - len/
Should check snprintf() return value.
This patch would not be needed, if what Patrick suggested[1] works.
-sughosh
[1] - https://lists.denx.de/pipermail/u-boot/2021-December/469256.html
env_set("dfu_alt_info", buf); puts("DFU alt info setting: done\n");
}
2.17.1

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); +}

Hi Sughosh,
On Thu, 25 Nov 2021 at 08:03, 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
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_
nitpicking: prefer #ifndef ?
+#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);
revert_active_index seems a better name (imho)
Is active_idx really needed as an output argument? One requiring this value could fetch it calling get_active_index ops afterward.
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)
static inline in a C-source file?
+{
return get_plat_fwu_metadata_ops();
Can't we use fwu_gpt_blk_ops reference straight in the below functions?
Regards, Etienne
+}
+/**
- 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

On Wed, 8 Dec 2021 at 19:23, Etienne Carriere etienne.carriere@linaro.org wrote:
Hi Sughosh,
On Thu, 25 Nov 2021 at 08:03, 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
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_
nitpicking: prefer #ifndef ?
Actually I prefer !defined :). Will keep it as is if you don't have a strong opinion on this.
+#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);
revert_active_index seems a better name (imho)
Okay.
Is active_idx really needed as an output argument? One requiring this value could fetch it calling get_active_index ops afterward.
You are right. Will change.
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)
static inline in a C-source file?
+{
return get_plat_fwu_metadata_ops();
Can't we use fwu_gpt_blk_ops reference straight in the below functions?
This would be needed to have the flexibility to add metadata access functions for some other types of devices, maybe ones which do not use GPT based partition scheme.
-sughosh
Regards, Etienne
+}
+/**
- 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

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, +};

Hi Sughsoh,
On 11/25/21 8:01 AM, 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
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;
here we found the partition = <i>
but I am not sure of the hard-coded mapping here <alt> = <part_id> + 1
at least I think it should be configured for each board
for example with a weak function ?
*alt_no = board_get_dfu_alt_fwu(i);
with:
/* default => dfu alternate use the same order than parttion */ weak int board_get_dfu_alt_fwu(int part) { return part + 1; )
But I think we can make something more dynamic here by parsing DFU information to found the alternate associated to the requested partition
by example by using dfu_get_entity() => search on the "mmc" dev / part
int mmc_get_dfu_alt_fwu(int dev, int part) {
for (i = 0; i < nb_alt; i++) { dfu = dfu_get_entity(i); /* only SDCard / eMMC suport with GPT partitioning */ if (dfu->dev_type != DFU_DEV_MMC) continue; if (dfu->layout = DFU_RAW_ADDR && dfu->data.mmc.dev == dev && dfu->data.mmc.part == part) return  }
this request can support boards with several MMC instances as the partitions are not the necessary the first one
for example trying to update partition in mmc 1 when partition on mmc 0 are the first one in dfu alternate.
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,
+};
Regards
Patrick

hi Patrick,
On Tue, 7 Dec 2021 at 19:54, Patrick DELAUNAY patrick.delaunay@foss.st.com wrote:
Hi Sughsoh,
On 11/25/21 8:01 AM, 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
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;
here we found the partition = <i>
but I am not sure of the hard-coded mapping here <alt> = <part_id> + 1
at least I think it should be configured for each board
for example with a weak function ?
*alt_no = board_get_dfu_alt_fwu(i);
with:
/* default => dfu alternate use the same order than parttion */ weak int board_get_dfu_alt_fwu(int part) { return part + 1; )
But I think we can make something more dynamic here by parsing DFU information to found the alternate associated to the requested partition
by example by using dfu_get_entity() => search on the "mmc" dev / part
int mmc_get_dfu_alt_fwu(int dev, int part) {
for (i = 0; i < nb_alt; i++) { dfu = dfu_get_entity(i); /* only SDCard / eMMC suport with GPT partitioning */ if (dfu->dev_type != DFU_DEV_MMC) continue; if (dfu->layout = DFU_RAW_ADDR && dfu->data.mmc.dev == dev && dfu->data.mmc.part == part) return }
this request can support boards with several MMC instances as the partitions are not the necessary the first one
for example trying to update partition in mmc 1 when partition on mmc 0 are the first one in dfu alternate.
Thanks for this suggestion. I will try it out on my side and incorporate it in my V2. This should also get rid of patch 2, Move the ram partitions to the end of the dfu_alt_info variable.
-sughosh
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,
+};
Regards
Patrick

Hi Sughosh,
On Thu, 25 Nov 2021 at 00:03, 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
Given that you use mdata in some places, could you use it everywhere (identifiers and filenames)? I think mdata is a much better name than the generic word 'metadata'.
Regards, Simon

hi Simon,
On Thu, 9 Dec 2021 at 08:02, Simon Glass sjg@chromium.org wrote:
Hi Sughosh,
On Thu, 25 Nov 2021 at 00:03, 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
Given that you use mdata in some places, could you use it everywhere (identifiers and filenames)? I think mdata is a much better name than the generic word 'metadata'.
Okay. Will make the change as per your suggestion. Thanks.
-sughosh

-----Original Message----- From: Sughosh Ganu sughosh.ganu@linaro.org Sent: 2021年11月25日 15:02 To: u-boot@lists.denx.de Cc: Patrick Delaunay patrick.delaunay@foss.st.com; Patrice Chotard patrice.chotard@foss.st.com; Heinrich Schuchardt xypron.glpk@gmx.de; Alexander Graf agraf@csgraf.de; Simon Glass sjg@chromium.org; Bin Meng bmeng.cn@gmail.com; Peng Fan peng.fan@nxp.com; AKASHI Takahiro takahiro.akashi@linaro.org; Ilias Apalodimas ilias.apalodimas@linaro.org; Jose Marinho jose.marinho@arm.com; Grant Likely grant.likely@arm.com; Jason Liu jason.hui.liu@nxp.com; Sughosh Ganu sughosh.ganu@linaro.org Subject: [RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
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
Better to define as the followings:
#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";
Is this hard-code guid_string intentioned?
- ret = part_get_info(desc, part_num, info);
Jason Liu

On Thu, 9 Dec 2021 at 15:06, Jason Liu jason.hui.liu@nxp.com wrote:
-----Original Message----- From: Sughosh Ganu sughosh.ganu@linaro.org Sent: 2021年11月25日 15:02 To: u-boot@lists.denx.de Cc: Patrick Delaunay patrick.delaunay@foss.st.com; Patrice Chotard patrice.chotard@foss.st.com; Heinrich Schuchardt xypron.glpk@gmx.de; Alexander Graf agraf@csgraf.de; Simon Glass sjg@chromium.org; Bin Meng bmeng.cn@gmail.com; Peng Fan peng.fan@nxp.com; AKASHI Takahiro takahiro.akashi@linaro.org; Ilias Apalodimas ilias.apalodimas@linaro.org; Jose Marinho jose.marinho@arm.com;
Grant
Likely grant.likely@arm.com; Jason Liu jason.hui.liu@nxp.com;
Sughosh
Ganu sughosh.ganu@linaro.org Subject: [RFC PATCH 04/10] FWU: Add metadata access functions for GPT partitioned block devices
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
Better to define as the followings:
#define MDATA_READ ((u8)0x1) #define MDATA_WRITE ((u8)0x2)
#define IMAGE_ACCEPT_SET ((u8)0x1) #define IMAGE_ACCEPT_CLEAR ((u8)0x2)
Ilias had suggested using the BIT macros for this. I have made that change. Hope that is fine.
+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";
Is this hard-code guid_string intentioned?
Yes, this is mentioned in the FWU spec[1], Table 3, pg 14.
-sughosh
[1] - https://developer.arm.com/documentation/den0118/a
ret = part_get_info(desc, part_num, info);
Jason Liu

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_ */

Hi Sanghosh
On 11/25/21 8:01 AM, Sughosh Ganu wrote:
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_ */
One general question:
How we can handle the EV1 board with 2 MMC devices => eMMC (mmc1) / SDCard (mmc0)
and how to managed HW partition of eMMC boot1 / boot2
=> for eMMC boot, the TF-A BL2 is located in these eMMC hw partition.
in the serie, I see only support for partition > 0 => partition in GPT but not the update of eMMC boot partition.
Regards
Patrick

hi Patrick,
On Tue, 7 Dec 2021 at 20:03, Patrick DELAUNAY patrick.delaunay@foss.st.com wrote:
Hi Sanghosh
On 11/25/21 8:01 AM, Sughosh Ganu wrote:
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_ */
One general question:
How we can handle the EV1 board with 2 MMC devices => eMMC (mmc1) / SDCard (mmc0)
The current metadata structure has a location_uuid, which can be used to identify the device which will contain the banks of firmware images. The current code for DK2 board does not have this implemented, but adding this logic should not be difficult. Based on the location_uuid, we can identify the device and return the blk_desc of that device.
and how to managed HW partition of eMMC boot1 / boot2
=> for eMMC boot, the TF-A BL2 is located in these eMMC hw partition.
in the serie, I see only support for partition > 0 => partition in GPT but not the update of eMMC boot partition.
I will get back to you on this. The current specification is dealing with GPT formatted devices. Need to check how the boot partitions will be supported for multiple banks of images.
-sughosh
Regards
Patrick

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_ */

Hi Sughosh,
On 11/25/21 8:01 AM, Sughosh Ganu wrote:
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)
The used TAMP register need to be aligned with TF-A BL2, offset = 10 to be confirmed
#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_ */
Regards
Patrick

hi Patrick,
On Tue, 7 Dec 2021 at 19:57, Patrick DELAUNAY patrick.delaunay@foss.st.com wrote:
Hi Sughosh,
On 11/25/21 8:01 AM, Sughosh Ganu wrote:
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)
The used TAMP register need to be aligned with TF-A BL2, offset = 10 to be confirmed
Yes, the same register is being used in tf-a BL2 to pass the boot index. The patch for this support is being reviewed in tf-a[1].
-sughosh
[1] - https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/12577
#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_ */
Regards
Patrick

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); }

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; +}

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;

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", + "" +);
participants (6)
-
AKASHI Takahiro
-
Etienne Carriere
-
Jason Liu
-
Patrick DELAUNAY
-
Simon Glass
-
Sughosh Ganu