[RFC PATCH v2 0/8] FWU: Add FWU Multi Bank Update for DeveloerBox

Hi,
Here is the 2nd version of RFC series of patches for the FWU Multi Bank Update support for the DeveloperBox platform. This series depends on Sughosh's Multi Bank Update v4 [1].
[1] https://lore.kernel.org/all/20220207182001.31270-1-sughosh.ganu@linaro.org/T...
Unlike the STM32MP board, DeveloperBox (SynQuacer) loads the firmware from SPI NOR flash. Thus it doesn't use GPT partitions to store the firmware banks and the FWU metadata. Instead, it stores those data at fixed address areas on SPI NOR flash. I carefully chose the areas which doesn't overlap the previous firmware and EDK2.
This adds FWU metadata SF driver for this FWU multi bank support on SPI flash without GPT. This is identified by "u-boot,fwu-mdata-sf". The last patch adds the DT bindings in YAML.
Since there is no GPT, the location GUID for images and the image GUID for banks are Null GUID. I will fix this with uuid property for fixed-partition devicetree afterwards.
And the SynQuacer also does not have any non-volatile register. Thus this allocates the platform defined boot index on the SPI flash too.
NOTE: To use this series, you also need to update SCP firmware[2] and TF-A[3] on the DeveloperBox. Those are under cleaning up.
[2] https://git.linaro.org/people/masami.hiramatsu/SCP-firmware.git/ [3] https://git.linaro.org/people/masami.hiramatsu/arm-trusted-firmware.git/
Thank you,
---
Masami Hiramatsu (8): FWU: Calculate CRC32 in fwu_update_mdata() FWU: Free metadata copy if gpt_get_mdata() failed synquacer: Update for TBBR based new FIP layout dt/bindings: firmware: Add FWU metadata on SPI flash binding FWU: Add FWU metadata access driver for SPI flash FWU: synquacer: Add FWU Multi bank update support for DeveloperBox FWU: synquacer: Initialize broken metadata configs: synquacer: Add FWU support for DeveloperBox
.../dts/synquacer-sc2a11-developerbox-u-boot.dtsi | 33 ++ board/socionext/developerbox/Kconfig | 24 ++ board/socionext/developerbox/Makefile | 1 board/socionext/developerbox/fwu_plat.c | 238 ++++++++++++++++ configs/synquacer_developerbox_defconfig | 12 + .../firmware/fwu-mdata-sf.yaml | 38 +++ drivers/fwu-mdata/Kconfig | 9 + drivers/fwu-mdata/Makefile | 1 drivers/fwu-mdata/fwu-mdata-uclass.c | 57 +--- drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 49 ++- drivers/fwu-mdata/fwu_mdata_sf.c | 294 ++++++++++++++++++++ include/configs/synquacer.h | 14 + include/fwu.h | 2 13 files changed, 698 insertions(+), 74 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-sf.yaml create mode 100644 drivers/fwu-mdata/fwu_mdata_sf.c
-- Masami Hiramatsu masami.hiramatsu@linaro.org

To avoid calculating crc32 in several places, do it in fwu_update_mdata(). This also ensures the mdata crc32 is always sane.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org --- drivers/fwu-mdata/fwu-mdata-uclass.c | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-)
diff --git a/drivers/fwu-mdata/fwu-mdata-uclass.c b/drivers/fwu-mdata/fwu-mdata-uclass.c index 64b3051ecf..b98eda3789 100644 --- a/drivers/fwu-mdata/fwu-mdata-uclass.c +++ b/drivers/fwu-mdata/fwu-mdata-uclass.c @@ -115,7 +115,6 @@ out: int fwu_update_active_index(u32 active_idx) { int ret; - void *buf; struct fwu_mdata *mdata = NULL;
if (active_idx > CONFIG_FWU_NUM_BANKS - 1) { @@ -136,14 +135,6 @@ int fwu_update_active_index(u32 active_idx) mdata->previous_active_index = mdata->active_index; mdata->active_index = active_idx;
- /* - * Calculate the crc32 for the updated FWU metadata - * and put the updated value in the FWU metadata crc32 - * field - */ - buf = &mdata->version; - mdata->crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32)); - /* * Now write this updated FWU metadata to both the * FWU metadata partitions @@ -233,7 +224,6 @@ int fwu_mdata_check(void) int fwu_revert_boot_index(void) { int ret; - void *buf; u32 cur_active_index; struct fwu_mdata *mdata = NULL;
@@ -251,14 +241,6 @@ int fwu_revert_boot_index(void) mdata->active_index = mdata->previous_active_index; mdata->previous_active_index = cur_active_index;
- /* - * Calculate the crc32 for the updated FWU metadata - * and put the updated value in the FWU metadata crc32 - * field - */ - buf = &mdata->version; - mdata->crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32)); - /* * Now write this updated FWU metadata to both the * FWU metadata partitions @@ -293,7 +275,6 @@ out: static int fwu_set_clear_image_accept(efi_guid_t *img_type_id, u32 bank, u8 action) { - void *buf; int ret, i; u32 nimages; struct fwu_mdata *mdata = NULL; @@ -316,10 +297,6 @@ static int fwu_set_clear_image_accept(efi_guid_t *img_type_id, else img_bank_info->accepted = 0;
- buf = &mdata->version; - mdata->crc32 = crc32(0, buf, sizeof(*mdata) - - sizeof(u32)); - ret = fwu_update_mdata(mdata); goto out; } @@ -425,6 +402,16 @@ int fwu_update_mdata(struct fwu_mdata *mdata) return -ENOSYS; }
+ if (!mdata) + return -EINVAL; + /* + * Calculate the crc32 for the updated FWU metadata + * and put the updated value in the FWU metadata crc32 + * field + */ + mdata->crc32 = crc32(0, (const unsigned char *)&mdata->version, + sizeof(*mdata) - sizeof(u32)); + return ops->update_mdata(dev, mdata); }

It is better if a function which returns an error then release all allocated memory resources. This simplifies the mind model and less chance to forgot to free memory and double free.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org --- drivers/fwu-mdata/fwu-mdata-uclass.c | 24 ++++++---------- drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 49 ++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 37 deletions(-)
diff --git a/drivers/fwu-mdata/fwu-mdata-uclass.c b/drivers/fwu-mdata/fwu-mdata-uclass.c index b98eda3789..c1cd77243f 100644 --- a/drivers/fwu-mdata/fwu-mdata-uclass.c +++ b/drivers/fwu-mdata/fwu-mdata-uclass.c @@ -79,12 +79,12 @@ int fwu_verify_mdata(struct fwu_mdata *mdata, bool pri_part) int fwu_get_active_index(u32 *active_idx) { int ret; - struct fwu_mdata *mdata = NULL; + struct fwu_mdata *mdata;
ret = fwu_get_mdata(&mdata); if (ret < 0) { log_err("Unable to get valid FWU metadata\n"); - goto out; + return ret; }
/* @@ -96,8 +96,6 @@ int fwu_get_active_index(u32 *active_idx) log_err("Active index value read is incorrect\n"); ret = -EINVAL; } - -out: free(mdata);
return ret; @@ -115,17 +113,17 @@ out: int fwu_update_active_index(u32 active_idx) { int ret; - struct fwu_mdata *mdata = NULL; + struct fwu_mdata *mdata;
if (active_idx > CONFIG_FWU_NUM_BANKS - 1) { log_err("Active index value to be updated is incorrect\n"); - return -1; + return -EINVAL; }
ret = fwu_get_mdata(&mdata); if (ret < 0) { log_err("Unable to get valid FWU metadata\n"); - goto out; + return ret; }
/* @@ -144,8 +142,6 @@ int fwu_update_active_index(u32 active_idx) log_err("Failed to update FWU metadata partitions\n"); ret = -EIO; } - -out: free(mdata);
return ret; @@ -225,12 +221,12 @@ int fwu_revert_boot_index(void) { int ret; u32 cur_active_index; - struct fwu_mdata *mdata = NULL; + struct fwu_mdata *mdata;
ret = fwu_get_mdata(&mdata); if (ret < 0) { log_err("Unable to get valid FWU metadata\n"); - goto out; + return ret; }
/* @@ -250,8 +246,6 @@ int fwu_revert_boot_index(void) log_err("Failed to update FWU metadata partitions\n"); ret = -EIO; } - -out: free(mdata);
return ret; @@ -277,14 +271,14 @@ static int fwu_set_clear_image_accept(efi_guid_t *img_type_id, { int ret, i; u32 nimages; - struct fwu_mdata *mdata = NULL; + struct fwu_mdata *mdata; struct fwu_image_entry *img_entry; struct fwu_image_bank_info *img_bank_info;
ret = fwu_get_mdata(&mdata); if (ret < 0) { log_err("Unable to get valid FWU metadata\n"); - goto out; + return ret; }
nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK; diff --git a/drivers/fwu-mdata/fwu_mdata_gpt_blk.c b/drivers/fwu-mdata/fwu_mdata_gpt_blk.c index 9170c3f6af..a32195db2e 100644 --- a/drivers/fwu-mdata/fwu_mdata_gpt_blk.c +++ b/drivers/fwu-mdata/fwu_mdata_gpt_blk.c @@ -177,18 +177,9 @@ static int fwu_gpt_update_mdata(struct udevice * dev, struct fwu_mdata *mdata) return 0; }
-static int gpt_get_mdata(struct blk_desc *desc, struct fwu_mdata **mdata) +static int gpt_get_mdata_part(struct blk_desc *desc, struct fwu_mdata **mdata, u16 part) { int ret; - u16 primary_mpart = 0, secondary_mpart = 0; - - ret = gpt_get_mdata_partitions(desc, &primary_mpart, - &secondary_mpart); - - if (ret < 0) { - log_err("Error getting the FWU metadata partitions\n"); - return -ENODEV; - }
*mdata = malloc(sizeof(struct fwu_mdata)); if (!*mdata) { @@ -196,28 +187,42 @@ static int gpt_get_mdata(struct blk_desc *desc, struct fwu_mdata **mdata) return -ENOMEM; }
- ret = gpt_read_mdata(desc, *mdata, primary_mpart); + ret = gpt_read_mdata(desc, *mdata, part); if (ret < 0) { log_err("Failed to read the FWU metadata from the device\n"); - return -EIO; + ret = -EIO; + } else { + ret = fwu_verify_mdata(*mdata, 1); + if (!ret) + return 0; } + free(*mdata); + + return ret; +}
- ret = fwu_verify_mdata(*mdata, 1); +static int gpt_get_mdata(struct blk_desc *desc, struct fwu_mdata **mdata) +{ + int ret; + u16 primary_mpart = 0, secondary_mpart = 0; + + ret = gpt_get_mdata_partitions(desc, &primary_mpart, + &secondary_mpart); + + if (ret < 0) { + log_err("Error getting the FWU metadata partitions\n"); + return -ENODEV; + } + + ret = gpt_get_mdata_part(desc, mdata, primary_mpart); if (!ret) return 0;
/* - * Verification of the primary FWU metadata copy failed. + * Reading of the primary FWU metadata copy failed. * Try to read the replica. */ - memset(*mdata, 0, sizeof(struct fwu_mdata)); - ret = gpt_read_mdata(desc, *mdata, secondary_mpart); - if (ret < 0) { - log_err("Failed to read the FWU metadata from the device\n"); - return -EIO; - } - - ret = fwu_verify_mdata(*mdata, 0); + ret = gpt_get_mdata_part(desc, mdata, secondary_mpart); if (!ret) return 0;

This changes SPI NOR flash partition layout for TBBR and also make the U-Boot as position independent executable again because BL33 is loaded on the memory.
With enabling TBBR, TF-A BL2 loads all BL3x images from FIP image, and the U-Boot image is added to the FIP image as BL33, and loaded to memory when boot instead of XIP on SPI NOR flash. To avoid mixing up with the legacy images, this new FIP image is stored on unused area (0x600000-) and the U-Boot env vars are also stored at 0x580000 so that it will not break existing EDK2 area.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org --- .../dts/synquacer-sc2a11-developerbox-u-boot.dtsi | 26 ++++++++++++++------ configs/synquacer_developerbox_defconfig | 5 ++-- include/configs/synquacer.h | 4 +-- 3 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi index 7a56116d6f..095727e03c 100644 --- a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi +++ b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi @@ -56,7 +56,7 @@ };
partition@180000 { - label = "FIP-TFA"; + label = "LegacyFIP"; reg = <0x180000 0x78000>; };
@@ -66,18 +66,28 @@ };
partition@200000 { - label = "U-Boot"; - reg = <0x200000 0x100000>; + label = "EDK2"; + reg = <0x200000 0x200000>; };
- partition@300000 { - label = "UBoot-Env"; - reg = <0x300000 0x100000>; + partition@400000 { + label = "EDK2-Env"; + reg = <0x400000 0x100000>; };
partition@500000 { - label = "Ex-OPTEE"; - reg = <0x500000 0x200000>; + label = "Metadata"; + reg = <0x500000 0x80000>; + }; + + partition@580000 { + label = "UBoot-Env"; + reg = <0x580000 0x80000>; + }; + + partition@600000 { + label = "FIP"; + reg = <0x600000 0x400000>; }; }; }; diff --git a/configs/synquacer_developerbox_defconfig b/configs/synquacer_developerbox_defconfig index 4fb0fba441..692919e1f5 100644 --- a/configs/synquacer_developerbox_defconfig +++ b/configs/synquacer_developerbox_defconfig @@ -1,9 +1,10 @@ CONFIG_ARM=y CONFIG_ARCH_SYNQUACER=y -CONFIG_SYS_TEXT_BASE=0x08200000 +CONFIG_POSITION_INDEPENDENT=y +CONFIG_SYS_TEXT_BASE=0 CONFIG_SYS_MALLOC_LEN=0x1000000 CONFIG_ENV_SIZE=0x30000 -CONFIG_ENV_OFFSET=0x300000 +CONFIG_ENV_OFFSET=0x580000 CONFIG_ENV_SECT_SIZE=0x10000 CONFIG_DM_GPIO=y CONFIG_DEFAULT_DEVICE_TREE="synquacer-sc2a11-developerbox" diff --git a/include/configs/synquacer.h b/include/configs/synquacer.h index 3d099b4f11..1b6e6d011e 100644 --- a/include/configs/synquacer.h +++ b/include/configs/synquacer.h @@ -52,9 +52,7 @@ /* #define CONFIG_SYS_PCI_64BIT 1 */
#define DEFAULT_DFU_ALT_INFO "dfu_alt_info=" \ - "mtd nor1=u-boot.bin raw 200000 100000;" \ - "fip.bin raw 180000 78000;" \ - "optee.bin raw 500000 100000\0" + "mtd nor1=fip.bin raw 600000 400000\0"
/* Distro boot settings */ #ifndef CONFIG_SPL_BUILD

Add a devicetree-binding YAML file for the FWU metadata on SPI Flash without GPT.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org --- .../firmware/fwu-mdata-sf.yaml | 38 ++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-sf.yaml
diff --git a/doc/device-tree-bindings/firmware/fwu-mdata-sf.yaml b/doc/device-tree-bindings/firmware/fwu-mdata-sf.yaml new file mode 100644 index 0000000000..3d8726231f --- /dev/null +++ b/doc/device-tree-bindings/firmware/fwu-mdata-sf.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/firmware/u-boot,fwu-mdata-sf.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: FWU metadata on SPI Flash without GPT + +maintainers: + - Masami Hiramatsu masami.hiramatsu@linaro.org + +properties: + compatible: + items: + - const: u-boot,fwu-mdata-sf + + fwu-mdata-store: + maxItems: 1 + description: Phandle of the SPI NOR flash device which contains the FWU medatata. + + mdata-offsets: + minItems: 2 + description: Offsets of the primary and secondary FWU metadata in the NOR flash. + +required: + - compatible + - fwu-mdata-store + - mdata-offsets + +additionalProperties: false + +examples: + - | + fwu-mdata { + compatible = "u-boot,fwu-mdata-sf"; + fwu-mdata-store = <&spi-flash>; + mdata-offsets = <0x500000 0x530000>; + };

For the platform which doesn't have GPT partitions for the firmware but on SPI flash, the FWU metadata is stored on SPI flash as raw image at specific offset. This driver gives the access methods for those metadata information on the SPI flash.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org --- drivers/fwu-mdata/Kconfig | 9 + drivers/fwu-mdata/Makefile | 1 drivers/fwu-mdata/fwu_mdata_sf.c | 294 ++++++++++++++++++++++++++++++++++++++ include/fwu.h | 2 4 files changed, 306 insertions(+) create mode 100644 drivers/fwu-mdata/fwu_mdata_sf.c
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig index d5edef19d6..9bed3e9c1e 100644 --- a/drivers/fwu-mdata/Kconfig +++ b/drivers/fwu-mdata/Kconfig @@ -14,3 +14,12 @@ config FWU_MDATA_GPT_BLK help Enable support for accessing FWU Metadata on GPT partitioned block devices. + +config FWU_MDATA_SF + bool "Enable FWU Multi Bank Update for SPI Flash" + depends on DM_FWU_MDATA + help + Enable FWU Multi Bank Update for SPI flash driver. This + driver does not depend on GPT. Instead, the platform must + provide some APIs and define the offset of the primary and + the secondary metadata. diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile index 12a5b4fe04..f8db25ab69 100644 --- a/drivers/fwu-mdata/Makefile +++ b/drivers/fwu-mdata/Makefile @@ -5,3 +5,4 @@
obj-$(CONFIG_DM_FWU_MDATA) += fwu-mdata-uclass.o obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_mdata_gpt_blk.o +obj-$(CONFIG_FWU_MDATA_SF) += fwu_mdata_sf.o diff --git a/drivers/fwu-mdata/fwu_mdata_sf.c b/drivers/fwu-mdata/fwu_mdata_sf.c new file mode 100644 index 0000000000..010528b91a --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_sf.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021, Linaro Limited + */ + +#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <malloc.h> +#include <memalign.h> +#include <spi.h> +#include <spi_flash.h> +#include <flash.h> + +#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h> + +struct fwu_mdata_sf_priv { + struct spi_flash *sf; + u32 pri_offset; + u32 sec_offset; +}; + +static int sf_load_data(struct spi_flash *sf, u32 offs, u32 size, void **data) +{ + int ret; + + *data = memalign(ARCH_DMA_MINALIGN, size); + if (!*data) + return -ENOMEM; + + ret = spi_flash_read(sf, offs, size, *data); + if (ret < 0) { + free(*data); + *data = NULL; + } + + return ret; +} + +static int sf_save_data(struct spi_flash *sf, u32 offs, u32 size, void *data) +{ + u32 sect_size, nsect; + void *buf; + int ret; + + sect_size = sf->mtd.erasesize; + nsect = DIV_ROUND_UP(size, sect_size); + ret = spi_flash_erase(sf, offs, nsect * sect_size); + if (ret < 0) + return ret; + + buf = memalign(ARCH_DMA_MINALIGN, size); + if (!buf) + return -ENOMEM; + memcpy(buf, data, size); + + ret = spi_flash_write(sf, offs, size, buf); + + free(buf); + + return ret; +} + +static int fwu_sf_load_mdata(struct spi_flash *sf, struct fwu_mdata **mdata, u32 offs, bool primary) +{ + int ret; + + ret = sf_load_data(sf, offs, sizeof(struct fwu_mdata), (void **)mdata); + + if (ret >= 0) { + ret = fwu_verify_mdata(*mdata, primary); + if (ret < 0) { + free(*mdata); + *mdata = NULL; + } + } + + return ret; +} + +static int fwu_sf_load_primary_mdata(struct fwu_mdata_sf_priv *sf_priv, + struct fwu_mdata **mdata) +{ + return fwu_sf_load_mdata(sf_priv->sf, mdata, sf_priv->pri_offset, true); +} + +static int fwu_sf_load_secondary_mdata(struct fwu_mdata_sf_priv *sf_priv, + struct fwu_mdata **mdata) +{ + return fwu_sf_load_mdata(sf_priv->sf, mdata, sf_priv->sec_offset, false); +} + +static int fwu_sf_save_primary_mdata(struct fwu_mdata_sf_priv *sf_priv, + struct fwu_mdata *mdata) +{ + return sf_save_data(sf_priv->sf, sf_priv->pri_offset, + sizeof(struct fwu_mdata), mdata); +} + +static int fwu_sf_save_secondary_mdata(struct fwu_mdata_sf_priv *sf_priv, + struct fwu_mdata *mdata) +{ + return sf_save_data(sf_priv->sf, sf_priv->sec_offset, + sizeof(struct fwu_mdata), mdata); +} + +static int fwu_sf_get_valid_mdata(struct fwu_mdata_sf_priv *sf_priv, + struct fwu_mdata **mdata) +{ + if (fwu_sf_load_primary_mdata(sf_priv, mdata) == 0) + return 0; + + log_err("Failed to load/verify primary mdata. Try secondary.\n"); + + if (fwu_sf_load_secondary_mdata(sf_priv, mdata) == 0) + return 0; + + log_err("Failed to load/verify secondary mdata.\n"); + + return -1; +} + +static int fwu_sf_update_mdata(struct udevice *dev, struct fwu_mdata *mdata) +{ + struct fwu_mdata_sf_priv *sf_priv = dev_get_priv(dev); + int ret; + + /* Update mdata crc32 field */ + mdata->crc32 = crc32(0, (void *)&mdata->version, + sizeof(*mdata) - sizeof(u32)); + + /* First write the primary mdata */ + ret = fwu_sf_save_primary_mdata(sf_priv, mdata); + if (ret < 0) { + log_err("Failed to update the primary mdata.\n"); + return ret; + } + + /* And now the replica */ + ret = fwu_sf_save_secondary_mdata(sf_priv, mdata); + if (ret < 0) { + log_err("Failed to update the secondary mdata.\n"); + return ret; + } + + return 0; +} + +static int fwu_sf_mdata_check(struct udevice *dev) +{ + struct fwu_mdata *primary = NULL, *secondary = NULL; + struct fwu_mdata_sf_priv *sf_priv = dev_get_priv(dev); + int ret; + + ret = fwu_sf_load_primary_mdata(sf_priv, &primary); + if (ret < 0) + log_err("Failed to read the primary mdata: %d\n", ret); + + ret = fwu_sf_load_secondary_mdata(sf_priv, &secondary); + if (ret < 0) + log_err("Failed to read the secondary mdata: %d\n", ret); + + if (primary && secondary) { + if (memcmp(primary, secondary, sizeof(struct fwu_mdata))) { + log_err("The primary and the secondary mdata are different\n"); + ret = -1; + } + } else if (primary) { + ret = fwu_sf_save_secondary_mdata(sf_priv, primary); + if (ret < 0) + log_err("Restoring secondary mdata partition failed\n"); + } else if (secondary) { + ret = fwu_sf_save_primary_mdata(sf_priv, secondary); + if (ret < 0) + log_err("Restoring primary mdata partition failed\n"); + } + + free(primary); + free(secondary); + return ret; +} + +static int fwu_sf_get_mdata(struct udevice *dev, struct fwu_mdata **mdata) +{ + struct fwu_mdata_sf_priv *sf_priv = dev_get_priv(dev); + + return fwu_sf_get_valid_mdata(sf_priv, mdata); +} + +/* + * By default, this expects that dfu_alt_info is defined as the following order + * image0.bank0, image0.bank1, image1.bank0, ... + * Thus the alt_no is (the index of image) * #banks + update_bank. + */ +int __weak fwu_plat_get_image_alt_num(efi_guid_t image_type_id, + u32 update_bank, int *alt_no) +{ + struct fwu_mdata *mdata; + int i, ret; + + ret = fwu_get_mdata(&mdata); + if (ret < 0) + return ret; + + ret = -ENOENT; + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) + if (!guidcmp(&image_type_id, &mdata->img_entry[i].image_type_uuid)) { + *alt_no = i * CONFIG_FWU_NUM_BANKS + update_bank; + ret = 0; + break; + } + + free(mdata); + return ret; +} + +/* Since the dfu_alt_info is defined by the platform, ask platform about alt-number. */ +static int fwu_sf_get_image_alt_num(struct udevice *dev, efi_guid_t image_type_id, + u32 update_bank, int *alt_no) +{ + return fwu_plat_get_image_alt_num(image_type_id, update_bank, alt_no); +} + +/** + * fwu_mdata_sf_of_to_plat() - Translate from DT to fwu mdata device + */ +static int fwu_mdata_sf_of_to_plat(struct udevice *dev) +{ + struct fwu_mdata_sf_priv *sf_priv = dev_get_priv(dev); + const fdt32_t *phandle_p = NULL; + struct udevice *sf_dev; + int ret, size; + u32 phandle; + + /* Find the FWU mdata storage device */ + phandle_p = ofnode_get_property(dev_ofnode(dev), + "fwu-mdata-store", &size); + if (!phandle_p) { + log_err("fwu-mdata-store property not found\n"); + return -ENOENT; + } + + phandle = fdt32_to_cpu(*phandle_p); + + ret = device_get_global_by_ofnode( + ofnode_get_by_phandle(phandle), + &sf_dev); + if (ret) + return ret; + + sf_priv->sf = dev_get_uclass_priv(sf_dev); + + /* Get the offset of primary and seconday mdata */ + ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 0, + &sf_priv->pri_offset); + if (ret) + return ret; + ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 1, + &sf_priv->sec_offset); + if (ret) + return ret; + + return 0; +} + +static int fwu_mdata_sf_probe(struct udevice *dev) +{ + /* Ensure the metadata can be read. */ + return fwu_sf_mdata_check(dev); +} + +static struct fwu_mdata_ops fwu_sf_ops = { + .get_image_alt_num = fwu_sf_get_image_alt_num, + .mdata_check = fwu_sf_mdata_check, + .get_mdata = fwu_sf_get_mdata, + .update_mdata = fwu_sf_update_mdata, +}; + +static const struct udevice_id fwu_mdata_ids[] = { + { .compatible = "u-boot,fwu-mdata-sf" }, + { } +}; + +U_BOOT_DRIVER(fwu_mdata_sf) = { + .name = "fwu-mdata-sf", + .id = UCLASS_FWU_MDATA, + .of_match = fwu_mdata_ids, + .ops = &fwu_sf_ops, + .probe = fwu_mdata_sf_probe, + .of_to_plat = fwu_mdata_sf_of_to_plat, + .priv_auto = sizeof(struct fwu_mdata_sf_priv), +}; diff --git a/include/fwu.h b/include/fwu.h index 88dc4a4b9a..f5ac3f6d2e 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -66,5 +66,7 @@ int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank); int fwu_plat_get_update_index(u32 *update_idx); int fwu_plat_get_alt_num(struct udevice *dev, void *identifier); void fwu_plat_get_bootidx(void *boot_idx); +int fwu_plat_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank, + int *alt_no);
#endif /* _FWU_H_ */

The DeveloperBox platform can support the FWU Multi bank update. SCP firmware will switch the boot mode by DSW3-4 and load the Multi bank update supported TF-A BL2 from 0x600000 offset on the SPI flash. Thus it can co-exist with the legacy boot mode (legacy U-Boot or EDK2).
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org --- .../dts/synquacer-sc2a11-developerbox-u-boot.dtsi | 7 + board/socionext/developerbox/Kconfig | 12 + board/socionext/developerbox/Makefile | 1 board/socionext/developerbox/fwu_plat.c | 178 ++++++++++++++++++++ include/configs/synquacer.h | 10 + 5 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 board/socionext/developerbox/fwu_plat.c
diff --git a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi index 095727e03c..d2078da8b8 100644 --- a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi +++ b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi @@ -23,7 +23,7 @@ active_clk_edges; chipselect_num = <1>;
- spi-flash@0 { + spi_flash: spi-flash@0 { #address-cells = <1>; #size-cells = <1>; compatible = "jedec,spi-nor"; @@ -114,6 +114,11 @@ optee { status = "okay"; }; + fwu-mdata { + compatible = "u-boot,fwu-mdata-sf"; + fwu-mdata-store = <&spi_flash>; + mdata-offsets = <0x500000 0x530000>; + }; }; };
diff --git a/board/socionext/developerbox/Kconfig b/board/socionext/developerbox/Kconfig index c181d26a44..4120098cab 100644 --- a/board/socionext/developerbox/Kconfig +++ b/board/socionext/developerbox/Kconfig @@ -32,4 +32,16 @@ config SYS_CONFIG_NAME default "synquacer"
endif + +config FWU_MULTI_BANK_UPDATE + select FWU_MDATA_SF + select DM_SPI_FLASH + select DM_FWU_MDATA + +config FWU_NUM_BANKS + default 6 + +config FWU_NUM_IMAGES_PER_BANK + default 1 + endif diff --git a/board/socionext/developerbox/Makefile b/board/socionext/developerbox/Makefile index 4a46de995a..9b80ee38e7 100644 --- a/board/socionext/developerbox/Makefile +++ b/board/socionext/developerbox/Makefile @@ -7,3 +7,4 @@ #
obj-y := developerbox.o +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_plat.o diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c new file mode 100644 index 0000000000..cbbbd58bc0 --- /dev/null +++ b/board/socionext/developerbox/fwu_plat.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021, Linaro Limited + */ + +#include <dfu.h> +#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <malloc.h> +#include <memalign.h> +#include <spi.h> +#include <spi_flash.h> +#include <flash.h> + +#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h> + +/* SPI Flash accessors */ +static struct spi_flash *plat_spi_flash; + +static int __plat_sf_get_flash(void) +{ + struct udevice *new; + int ret; + + //TODO: define platform spi-flash somewhere. + ret = spi_flash_probe_bus_cs(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE, + &new); + if (ret) + return ret; + + plat_spi_flash = dev_get_uclass_priv(new); + return 0; +} + +static int plat_sf_get_flash(struct spi_flash **flash) +{ + int ret = 0; + + if (!plat_spi_flash) + ret = __plat_sf_get_flash(); + + *flash = plat_spi_flash; + + return ret; +} + +static int sf_load_data(u32 offs, u32 size, void **data) +{ + struct spi_flash *flash; + int ret; + + ret = plat_sf_get_flash(&flash); + if (ret < 0) + return ret; + + *data = memalign(ARCH_DMA_MINALIGN, size); + if (!*data) + return -ENOMEM; + + ret = spi_flash_read(flash, offs, size, *data); + if (ret < 0) { + free(*data); + *data = NULL; + } + + return ret; +} + +/* Platform dependent GUIDs */ + +/* The GUID for the SNI FIP image type GUID */ +#define FWU_IMAGE_TYPE_DEVBOX_FIP_GUID \ + EFI_GUID(0x7d6dc310, 0x52ca, 0x43b8, 0xb7, 0xb9, \ + 0xf9, 0xd6, 0xc5, 0x01, 0xd1, 0x08) + +#define PLAT_METADATA_OFFSET 0x510000 +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) + +struct __packed devbox_metadata { + u32 boot_index; + u32 boot_count; +} *devbox_plat_metadata; + +static const efi_guid_t devbox_fip_image_type_guid = FWU_IMAGE_TYPE_DEVBOX_FIP_GUID; + +int fwu_plat_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank, + int *alt_no) +{ + /* DeveloperBox FWU Multi bank only supports FIP image. */ + if (guidcmp(&image_type_id, &devbox_fip_image_type_guid)) + return -EOPNOTSUPP; + + /* + * DeveloperBox FWU expects Bank:Image = 1:1, and the dfu_alt_info + * only has the entries for banks. Thus the alt_no should be equal + * to the update_bank. + */ + update_bank %= CONFIG_FWU_NUM_BANKS; + *alt_no = update_bank; + + return 0; +} + +efi_status_t fill_image_type_guid_array(const efi_guid_t __always_unused + *default_guid, + efi_guid_t **part_guid_arr) +{ + int i; + int alt_num; + struct dfu_entity *dfu; + efi_status_t ret = EFI_SUCCESS; + + dfu_init_env_entities(NULL, NULL); + + /* TODO: generate DFU and guid array from metadata. */ + alt_num = 0; + list_for_each_entry(dfu, &dfu_list, list) { + ++alt_num; + } + + if (!alt_num) { + log_warning("Probably dfu_alt_info not defined\n"); + ret = EFI_NOT_READY; + goto out; + } + + *part_guid_arr = malloc(sizeof(efi_guid_t) * alt_num); + if (!*part_guid_arr) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + for (i = 0; i < alt_num; i++) + guidcpy((*part_guid_arr + i), &devbox_fip_image_type_guid); +out: + dfu_free_entities(); + + return ret; +} + +/* TBD: add a usage counter for wear leveling */ +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 + 1) % CONFIG_FWU_NUM_BANKS; + + return ret; +} + +static int devbox_load_plat_metadata(void) +{ + if (devbox_plat_metadata) + return 0; + + return sf_load_data(PLAT_METADATA_OFFSET, PLAT_METADATA_SIZE, + (void **)&devbox_plat_metadata); +} + +void fwu_plat_get_bootidx(void *boot_idx) +{ + u32 *bootidx = boot_idx; + + if (devbox_load_plat_metadata() < 0) + *bootidx = 0; + else + *bootidx = devbox_plat_metadata->boot_index; +} diff --git a/include/configs/synquacer.h b/include/configs/synquacer.h index 1b6e6d011e..f00c5bb8cc 100644 --- a/include/configs/synquacer.h +++ b/include/configs/synquacer.h @@ -51,8 +51,18 @@ /* Since U-Boot 64bit PCIe support is limited, disable 64bit MMIO support */ /* #define CONFIG_SYS_PCI_64BIT 1 */
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE +#define DEFAULT_DFU_ALT_INFO "dfu_alt_info=" \ + "mtd nor1=bank0 raw 600000 400000;" \ + "bank1 raw a00000 400000;" \ + "bank2 raw e00000 400000;" \ + "bank3 raw 1200000 400000;" \ + "bank4 raw 1600000 400000;" \ + "bank5 raw 1a00000 400000\0" +#else #define DEFAULT_DFU_ALT_INFO "dfu_alt_info=" \ "mtd nor1=fip.bin raw 600000 400000\0" +#endif
/* Distro boot settings */ #ifndef CONFIG_SPL_BUILD

Since the FWU metadata is not initialized at the installation, if it is broken, it should be initialized. Usually, the FWU metadata is not covered by capsule update, so it is safe to initialize the metadata portion if it seems broken.
But for the production device, usually firmware will be installed with initialized metadata, and the broken metadata means the device can be compromized. In that case, build U-Boot without this option.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org --- board/socionext/developerbox/Kconfig | 12 ++++++ board/socionext/developerbox/fwu_plat.c | 60 +++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+)
diff --git a/board/socionext/developerbox/Kconfig b/board/socionext/developerbox/Kconfig index 4120098cab..9fbe8d1e74 100644 --- a/board/socionext/developerbox/Kconfig +++ b/board/socionext/developerbox/Kconfig @@ -44,4 +44,16 @@ config FWU_NUM_BANKS config FWU_NUM_IMAGES_PER_BANK default 1
+config FWU_INIT_BROKEN_METADATA + bool "Initialize FWU metadata if broken" + select BOARD_LATE_INIT + default n + help + Initialize FWU metadata if the metadata is broken. + This option is only for the development environment, since if the + metadata is broken, it means someone may compromize it. In that case + the production device must be bricked. + But for the development environment, or initial installation of the + FWU multi-bank update firmware, this will be useful. + endif diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c index cbbbd58bc0..1892f79660 100644 --- a/board/socionext/developerbox/fwu_plat.c +++ b/board/socionext/developerbox/fwu_plat.c @@ -176,3 +176,63 @@ void fwu_plat_get_bootidx(void *boot_idx) else *bootidx = devbox_plat_metadata->boot_index; } + +#ifdef CONFIG_FWU_INIT_BROKEN_METADATA + +static void devbox_init_fwu_mdata(void) +{ + const efi_guid_t null_guid = NULL_GUID; + struct fwu_image_bank_info *bank; + struct fwu_mdata *metadata; + int i, j, ret; + + metadata = memalign(ARCH_DMA_MINALIGN, sizeof(*metadata)); + if (!metadata) { + log_err("Failed to allocate initial metadata.\n"); + return; + } + + metadata->version = 1; + metadata->active_index = 0; + metadata->previous_active_index = 0; + + /* + * Since the DeveloperBox doesn't use GPT, both of + * fwu_image_entry::location_uuid and + * fwu_img_bank_info::image_uuid are null GUID. + */ + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { + guidcpy(&metadata->img_entry[i].image_type_uuid, + &devbox_fip_image_type_guid); + guidcpy(&metadata->img_entry[i].location_uuid, + &null_guid); + bank = metadata->img_entry[i].img_bank_info; + + for (j = 0; j < CONFIG_FWU_NUM_BANKS; j++) { + guidcpy(&bank[j].image_uuid, &null_guid); + bank[j].accepted = (j == 0) ? 1 : 0; + bank[j].reserved = 0; + } + } + + ret = fwu_update_mdata(metadata); + if (ret < 0) + log_err("Failed to initialize FWU metadata\n"); + else + log_err("Initialized FWU metadata\n"); + free(metadata); +} + +int board_late_init(void) +{ + struct fwu_mdata *metadata; + + if (fwu_get_mdata(&metadata) < 0) { + // Initialize FWU metadata if broken + log_err("Unable to get a valid metadata. Initialize it.\n"); + devbox_init_fwu_mdata(); + } + return 0; +} + +#endif

Hi,
I decided to add tools/mkfwumdata tool to make a raw image of fwu_mdata for initialization. So this patch will be dropped from next version.
Thank you,
2022年2月18日(金) 0:12 Masami Hiramatsu masami.hiramatsu@linaro.org:
Since the FWU metadata is not initialized at the installation, if it is broken, it should be initialized. Usually, the FWU metadata is not covered by capsule update, so it is safe to initialize the metadata portion if it seems broken.
But for the production device, usually firmware will be installed with initialized metadata, and the broken metadata means the device can be compromized. In that case, build U-Boot without this option.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org
board/socionext/developerbox/Kconfig | 12 ++++++ board/socionext/developerbox/fwu_plat.c | 60 +++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+)
diff --git a/board/socionext/developerbox/Kconfig b/board/socionext/developerbox/Kconfig index 4120098cab..9fbe8d1e74 100644 --- a/board/socionext/developerbox/Kconfig +++ b/board/socionext/developerbox/Kconfig @@ -44,4 +44,16 @@ config FWU_NUM_BANKS config FWU_NUM_IMAGES_PER_BANK default 1
+config FWU_INIT_BROKEN_METADATA
bool "Initialize FWU metadata if broken"
select BOARD_LATE_INIT
default n
help
Initialize FWU metadata if the metadata is broken.
This option is only for the development environment, since if the
metadata is broken, it means someone may compromize it. In that case
the production device must be bricked.
But for the development environment, or initial installation of the
FWU multi-bank update firmware, this will be useful.
endif diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c index cbbbd58bc0..1892f79660 100644 --- a/board/socionext/developerbox/fwu_plat.c +++ b/board/socionext/developerbox/fwu_plat.c @@ -176,3 +176,63 @@ void fwu_plat_get_bootidx(void *boot_idx) else *bootidx = devbox_plat_metadata->boot_index; }
+#ifdef CONFIG_FWU_INIT_BROKEN_METADATA
+static void devbox_init_fwu_mdata(void) +{
const efi_guid_t null_guid = NULL_GUID;
struct fwu_image_bank_info *bank;
struct fwu_mdata *metadata;
int i, j, ret;
metadata = memalign(ARCH_DMA_MINALIGN, sizeof(*metadata));
if (!metadata) {
log_err("Failed to allocate initial metadata.\n");
return;
}
metadata->version = 1;
metadata->active_index = 0;
metadata->previous_active_index = 0;
/*
* Since the DeveloperBox doesn't use GPT, both of
* fwu_image_entry::location_uuid and
* fwu_img_bank_info::image_uuid are null GUID.
*/
for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
guidcpy(&metadata->img_entry[i].image_type_uuid,
&devbox_fip_image_type_guid);
guidcpy(&metadata->img_entry[i].location_uuid,
&null_guid);
bank = metadata->img_entry[i].img_bank_info;
for (j = 0; j < CONFIG_FWU_NUM_BANKS; j++) {
guidcpy(&bank[j].image_uuid, &null_guid);
bank[j].accepted = (j == 0) ? 1 : 0;
bank[j].reserved = 0;
}
}
ret = fwu_update_mdata(metadata);
if (ret < 0)
log_err("Failed to initialize FWU metadata\n");
else
log_err("Initialized FWU metadata\n");
free(metadata);
+}
+int board_late_init(void) +{
struct fwu_mdata *metadata;
if (fwu_get_mdata(&metadata) < 0) {
// Initialize FWU metadata if broken
log_err("Unable to get a valid metadata. Initialize it.\n");
devbox_init_fwu_mdata();
}
return 0;
+}
+#endif

Enable FWU Multi-Bank support for DeveloperBox SynQuacer platform. This also enables fwu_metadata_read command and "reboot soon after update" option.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org --- configs/synquacer_developerbox_defconfig | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/configs/synquacer_developerbox_defconfig b/configs/synquacer_developerbox_defconfig index 692919e1f5..3bd875c797 100644 --- a/configs/synquacer_developerbox_defconfig +++ b/configs/synquacer_developerbox_defconfig @@ -95,3 +95,10 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y +CONFIG_EFI_SECURE_BOOT=y +CONFIG_FWU_MULTI_BANK_UPDATE=y +CONFIG_FWU_MULTI_BANK_UPDATE_SF=y +CONFIG_FWU_MULTI_BANK_UPDATE_GPT_BLK=n +CONFIG_CMD_FWU_METADATA=y +CONFIG_FWU_REBOOT_AFTER_UPDATE=y +CONFIG_FWU_INIT_BROKEN_METADATA=y
participants (1)
-
Masami Hiramatsu