[PATCH v7 00/13] FWU: Add FWU Multi Bank Update feature support

The patchset adds support for the FWU Multi Bank Update[1] feature. Certain aspects of the Dependable Boot[2] specification have also been implemented.
The FWU multi bank update feature is used for supporting multiple sets(also called banks) of firmware image(s), allowing the platform to boot from a different bank, in case it fails to boot from the active bank. This functionality is supported by keeping the relevant information in a structure called metadata, which provides information on the images. Among other parameters, the metadata structure contains information on the currect active bank that is being used to boot image(s).
Functionality is being added to work with the UEFI capsule driver in u-boot. The metadata is read to gather information on the update bank, which is the bank to which the firmware images would be flashed to. On a successful completion of the update of all components, the active bank field in the metadata is updated, to reflect the bank from which the platform will boot on the subsequent boots.
Currently, the feature is being enabled on the STM32MP157C-DK2 and Synquacer boards. The DK2 board boots a FIP image from a uSD card partitioned with the GPT partioning scheme, while the Synquacer board boots a FIP image from a MTD partitioned SPI NOR flash device.
This feature also requires changes in a previous stage of bootloader, which parses the metadata and selects the bank to boot the image(s) from. Support has being added in tf-a(BL2 stage) for the STM32MP157C-DK2 board to boot the active bank images. These changes have been merged to the upstream tf-a repository.
The earlier patchset contained patches for both the DK2 and the Synquacer platforms. The handling of review comments for the Synquacer platform is to be taken up by a different engineer, and has not been done yet. After discussion with Tom Rini and Heinrich, it was decided to send the patches for the DK2 platform separately for review. The patch for adding a python test for the feature has been developed, and was sent in the version 5 of the patches[3]. However, the test script depends on adding support for the feature on MTD SPI NOR devices, and that is being done as part of the Synquacer patches. Hence these set of patches do not have the test script for the feature. That will be added through the patches for adding support for the feauture on Synquacer platform.
[1] - https://developer.arm.com/documentation/den0118/a [2] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e... [3] - https://lists.denx.de/pipermail/u-boot/2022-June/485992.html
Changes since V6: * Define the LOG_CATEGORY macro as suggested by Patrick * Add some documentation in the fwu_get_mdata and fwu_update_mdata functions to describe the sequence of calls to be made for modifying the metadata, as suggested by Etienn * Define the LOG_CATEGORY macro as suggested by Patrick * s/STM32MP1/STM32MP15/ as suggested by Patrick
Sughosh Ganu (13): dt/bindings: Add bindings for FWU Metadata storage device FWU: Add FWU metadata structure and driver for accessing metadata FWU: Add FWU metadata access driver for GPT partitioned block devices stm32mp1: dk2: Add a node for the FWU metadata device stm32mp1: dk2: Add image information for capsule updates FWU: stm32mp1: Add helper functions for accessing FWU metadata FWU: STM32MP1: Add support to read boot index from backup register FWU: Add boot time checks as highlighted by the FWU specification FWU: Add support for the FWU Multi Bank Update feature FWU: cmd: Add a command to read FWU metadata mkeficapsule: Add support for generating empty capsules mkeficapsule: Add support for setting OEM flags in capsule header FWU: doc: Add documentation for the FWU feature
arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi | 7 + arch/arm/mach-stm32mp/include/mach/stm32.h | 5 + board/st/stm32mp1/stm32mp1.c | 67 +++ cmd/Kconfig | 7 + cmd/Makefile | 1 + cmd/fwu_mdata.c | 80 +++ common/board_r.c | 5 + doc/develop/uefi/fwu_updates.rst | 156 ++++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + .../firmware/fwu-mdata.yaml | 32 ++ doc/mkeficapsule.1 | 33 +- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fwu-mdata/Kconfig | 16 + drivers/fwu-mdata/Makefile | 7 + drivers/fwu-mdata/fwu-mdata-uclass.c | 469 ++++++++++++++++++ drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 410 +++++++++++++++ include/configs/stm32mp15_common.h | 4 + include/dm/uclass-id.h | 1 + include/fwu.h | 71 +++ include/fwu_mdata.h | 67 +++ lib/Kconfig | 6 + lib/Makefile | 1 + lib/efi_loader/efi_capsule.c | 231 ++++++++- lib/efi_loader/efi_setup.c | 3 +- lib/fwu_updates/Kconfig | 31 ++ lib/fwu_updates/Makefile | 7 + lib/fwu_updates/fwu.c | 191 +++++++ lib/fwu_updates/fwu_gpt.c | 88 ++++ tools/eficapsule.h | 8 + tools/mkeficapsule.c | 105 +++- 32 files changed, 2094 insertions(+), 21 deletions(-) create mode 100644 cmd/fwu_mdata.c create mode 100644 doc/develop/uefi/fwu_updates.rst create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata.yaml create mode 100644 drivers/fwu-mdata/Kconfig create mode 100644 drivers/fwu-mdata/Makefile create mode 100644 drivers/fwu-mdata/fwu-mdata-uclass.c create mode 100644 drivers/fwu-mdata/fwu_mdata_gpt_blk.c create mode 100644 include/fwu.h create mode 100644 include/fwu_mdata.h create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile create mode 100644 lib/fwu_updates/fwu.c create mode 100644 lib/fwu_updates/fwu_gpt.c

Add bindings needed for accessing the FWU metadata partitions. These include the compatible string which point to the access method and the actual device which stores the FWU metadata.
The current patch adds basic bindings needed for accessing the metadata structure on GPT partitioned block devices.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V6: None
.../firmware/fwu-mdata.yaml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata.yaml
diff --git a/doc/device-tree-bindings/firmware/fwu-mdata.yaml b/doc/device-tree-bindings/firmware/fwu-mdata.yaml new file mode 100644 index 0000000000..97d30bd1c1 --- /dev/null +++ b/doc/device-tree-bindings/firmware/fwu-mdata.yaml @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/firmware/fwu-mdata.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: FWU metadata on device with GPT partitioned layout + +maintainers: + - Sughosh Ganu sughosh.ganu@linaro.org + +properties: + compatible: + items: + - const: u-boot,fwu-mdata-gpt + + fwu-mdata-store: + maxItems: 1 + description: Phandle of the device which contains the FWU medatata partition. + +required: + - compatible + - fwu-mdata-store + +additionalProperties: false + +examples: + - | + fwu-mdata { + compatible = "u-boot,fwu-mdata-gpt"; + fwu-mdata-store = <&sdmmc1>; + };

On 7/14/22 20:39, Sughosh Ganu wrote:
Add bindings needed for accessing the FWU metadata partitions. These include the compatible string which point to the access method and the actual device which stores the FWU metadata.
The current patch adds basic bindings needed for accessing the metadata structure on GPT partitioned block devices.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V6: None
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de

On Thu, 14 Jul 2022 at 13:39, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add bindings needed for accessing the FWU metadata partitions. These include the compatible string which point to the access method and the actual device which stores the FWU metadata.
The current patch adds basic bindings needed for accessing the metadata structure on GPT partitioned block devices.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V6: None
.../firmware/fwu-mdata.yaml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata.yaml
diff --git a/doc/device-tree-bindings/firmware/fwu-mdata.yaml b/doc/device-tree-bindings/firmware/fwu-mdata.yaml new file mode 100644 index 0000000000..97d30bd1c1 --- /dev/null +++ b/doc/device-tree-bindings/firmware/fwu-mdata.yaml
I think we want to call it fwu-mdata-gpt.yaml ?
thanks.

On Sun, 17 Jul 2022 at 02:43, Jassi Brar jaswinder.singh@linaro.org wrote:
On Thu, 14 Jul 2022 at 13:39, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add bindings needed for accessing the FWU metadata partitions. These include the compatible string which point to the access method and the actual device which stores the FWU metadata.
The current patch adds basic bindings needed for accessing the metadata structure on GPT partitioned block devices.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V6: None
.../firmware/fwu-mdata.yaml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata.yaml
diff --git a/doc/device-tree-bindings/firmware/fwu-mdata.yaml b/doc/device-tree-bindings/firmware/fwu-mdata.yaml new file mode 100644 index 0000000000..97d30bd1c1 --- /dev/null +++ b/doc/device-tree-bindings/firmware/fwu-mdata.yaml
I think we want to call it fwu-mdata-gpt.yaml ?
Yes, makes sense. Will rename it. Thanks.
-sughosh

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 a driver model uclass which provides 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 Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com --- Changes since V6: * Define the LOG_CATEGORY macro as suggested by Patrick * Add some documentation in the fwu_get_mdata and fwu_update_mdata functions to describe the sequence of calls to be made for modifying the metadata, as suggested by Etienne
drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fwu-mdata/Kconfig | 7 + drivers/fwu-mdata/Makefile | 6 + drivers/fwu-mdata/fwu-mdata-uclass.c | 469 +++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/fwu.h | 49 +++ include/fwu_mdata.h | 67 ++++ 8 files changed, 602 insertions(+) create mode 100644 drivers/fwu-mdata/Kconfig create mode 100644 drivers/fwu-mdata/Makefile create mode 100644 drivers/fwu-mdata/fwu-mdata-uclass.c create mode 100644 include/fwu.h create mode 100644 include/fwu_mdata.h
diff --git a/drivers/Kconfig b/drivers/Kconfig index 8b6fead351..75ac149d31 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -44,6 +44,8 @@ source "drivers/fuzz/Kconfig"
source "drivers/fpga/Kconfig"
+source "drivers/fwu-mdata/Kconfig" + source "drivers/gpio/Kconfig"
source "drivers/hwspinlock/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index d63fd1c04d..7710f18236 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -83,6 +83,7 @@ obj-y += cache/ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ obj-$(CONFIG_FASTBOOT) += fastboot/ +obj-$(CONFIG_DM_FWU_MDATA) += fwu-mdata/ obj-y += misc/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_NVME) += nvme/ diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig new file mode 100644 index 0000000000..d6a21c8e19 --- /dev/null +++ b/drivers/fwu-mdata/Kconfig @@ -0,0 +1,7 @@ +config DM_FWU_MDATA + bool "Driver support for accessing FWU Metadata" + depends on DM + help + Enable support for accessing FWU Metadata partitions. The + FWU Metadata partitions reside on the same storage device + which contains the other FWU updatable firmware images. diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile new file mode 100644 index 0000000000..e53a8c9983 --- /dev/null +++ b/drivers/fwu-mdata/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +# + +obj-$(CONFIG_DM_FWU_MDATA) += fwu-mdata-uclass.o diff --git a/drivers/fwu-mdata/fwu-mdata-uclass.c b/drivers/fwu-mdata/fwu-mdata-uclass.c new file mode 100644 index 0000000000..4ba102ff81 --- /dev/null +++ b/drivers/fwu-mdata/fwu-mdata-uclass.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, Linaro Limited + */ + +#define LOG_CATEGORY UCLASS_FWU_MDATA + +#include <common.h> +#include <dm.h> +#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <malloc.h> + +#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h> + +#define IMAGE_ACCEPT_SET BIT(0) +#define IMAGE_ACCEPT_CLEAR BIT(1) + +static int fwu_get_dev_ops(struct udevice **dev, + const struct fwu_mdata_ops **ops) +{ + int ret; + + ret = uclass_get_device(UCLASS_FWU_MDATA, 0, dev); + if (ret) { + log_debug("Cannot find fwu device\n"); + return ret; + } + + if ((*ops = device_get_ops(*dev)) == NULL) { + log_debug("Cannot get fwu device ops\n"); + return -ENOSYS; + } + + return 0; +} + +/** + * fwu_verify_mdata() - Verify the FWU metadata + * @mdata: FWU metadata structure + * @pri_part: FWU metadata partition is primary or secondary + * + * Verify the FWU metadata by computing the CRC32 for the metadata + * structure and comparing it against the CRC32 value stored as part + * of the structure. + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_verify_mdata(struct fwu_mdata *mdata, bool pri_part) +{ + u32 calc_crc32; + void *buf; + + buf = &mdata->version; + calc_crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32)); + + if (calc_crc32 != mdata->crc32) { + log_err("crc32 check failed for %s FWU metadata partition\n", + pri_part ? "primary" : "secondary"); + return -1; + } + + return 0; +} + +/** + * fwu_get_active_index() - Get active_index from the FWU metadata + * @active_idx: active_index value to be read + * + * Read the active_index field from the FWU 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) +{ + int ret; + struct fwu_mdata *mdata = NULL; + + ret = fwu_get_mdata(&mdata); + if (ret < 0) { + log_err("Unable to get valid FWU metadata\n"); + goto out; + } + + /* + * Found the FWU metadata partition, now read the active_index + * value + */ + *active_idx = mdata->active_index; + if (*active_idx > CONFIG_FWU_NUM_BANKS - 1) { + log_err("Active index value read is incorrect\n"); + ret = -EINVAL; + } + +out: + free(mdata); + + return ret; +} + +/** + * fwu_update_active_index() - Update active_index from the FWU metadata + * @active_idx: active_index value to be updated + * + * Update the active_index field in the FWU metadata + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_update_active_index(u32 active_idx) +{ + int ret; + struct fwu_mdata *mdata = NULL; + + if (active_idx > CONFIG_FWU_NUM_BANKS - 1) { + log_err("Active index value to be updated is incorrect\n"); + return -1; + } + + ret = fwu_get_mdata(&mdata); + if (ret < 0) { + log_err("Unable to get valid FWU metadata\n"); + goto out; + } + + /* + * Update the active index and previous_active_index fields + * in the FWU metadata + */ + mdata->previous_active_index = mdata->active_index; + mdata->active_index = active_idx; + + /* + * Now write this updated FWU metadata to both the + * FWU metadata partitions + */ + ret = fwu_update_mdata(mdata); + if (ret < 0) { + log_err("Failed to update FWU metadata partitions\n"); + ret = -EIO; + } + +out: + free(mdata); + + return ret; +} + +/** + * fwu_get_image_alt_num() - Get the dfu alt number to be used for capsule update + * @image_type_id: pointer to the 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) +{ + int ret, i; + efi_guid_t *image_guid; + struct udevice *dev = NULL; + struct fwu_mdata *mdata = NULL; + struct fwu_image_entry *img_entry; + const struct fwu_mdata_ops *ops = NULL; + struct fwu_image_bank_info *img_bank_info; + + ret = fwu_get_dev_ops(&dev, &ops); + if (ret) + return ret; + + ret = fwu_get_mdata(&mdata); + if (ret) { + log_err("Unable to get valid FWU metadata\n"); + goto out; + } + + /* + * The FWU 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, + &mdata->img_entry[i].image_type_uuid)) { + img_entry = &mdata->img_entry[i]; + img_bank_info = &img_entry->img_bank_info[update_bank]; + image_guid = &img_bank_info->image_uuid; + ret = fwu_plat_get_alt_num(dev, image_guid, alt_num); + break; + } + } + + if (i == CONFIG_FWU_NUM_IMAGES_PER_BANK) { + log_err("Partition with the image type %pUs not found\n", + image_type_id); + ret = -EINVAL; + goto out; + } + + if (!ret) { + log_debug("alt_num %d for partition %pUs\n", + *alt_num, image_guid); + } else { + log_err("alt_num not found for partition with GUID %pUs\n", + image_guid); + ret = -EINVAL; + } + +out: + free(mdata); + + return ret; +} + +/** + * fwu_mdata_check() - Check if the FWU metadata is valid + * + * Validate both copies of the FWU 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_mdata_check(void) +{ + int ret; + struct udevice *dev = NULL; + const struct fwu_mdata_ops *ops = NULL; + + ret = fwu_get_dev_ops(&dev, &ops); + if (ret) + return ret; + + if (!ops->mdata_check) { + log_err("mdata_check() method not defined\n"); + return -ENOSYS; + } + + return ops->mdata_check(dev); +} + +/** + * fwu_revert_boot_index() - Revert the active index in the FWU metadata + * + * Revert the active_index value in the FWU metadata, by swapping the values + * of active_index and previous_active_index in both copies of the + * FWU metadata. + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_revert_boot_index(void) +{ + int ret; + u32 cur_active_index; + struct fwu_mdata *mdata = NULL; + + ret = fwu_get_mdata(&mdata); + if (ret < 0) { + log_err("Unable to get valid FWU metadata\n"); + goto out; + } + + /* + * Swap the active index and previous_active_index fields + * in the FWU metadata + */ + cur_active_index = mdata->active_index; + mdata->active_index = mdata->previous_active_index; + mdata->previous_active_index = cur_active_index; + + /* + * Now write this updated FWU metadata to both the + * FWU metadata partitions + */ + ret = fwu_update_mdata(mdata); + if (ret < 0) { + log_err("Failed to update FWU metadata partitions\n"); + ret = -EIO; + } + +out: + free(mdata); + + return ret; +} + +/** + * fwu_set_clear_image_accept() - Set or Clear the Acceptance bit for the image + * @img_type_id: Guid of the image type for which the accepted bit is to be + * set or cleared + * @bank: Bank of which the image's Accept bit is to be set or cleared + * @action: Action which specifies whether image's Accept bit is to be set or + * cleared + * + * Set/Clear the accepted bit for the image specified by the img_guid parameter. + * This indicates acceptance or rejection of image for subsequent boots by some + * governing component like OS(or firmware). + * + * Return: 0 if OK, -ve on error + * + */ +static int fwu_set_clear_image_accept(efi_guid_t *img_type_id, + u32 bank, u8 action) +{ + int ret, i; + u32 nimages; + struct fwu_mdata *mdata = NULL; + 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; + } + + nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK; + img_entry = &mdata->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; + + ret = fwu_update_mdata(mdata); + goto out; + } + } + + /* Image not found */ + ret = -EINVAL; + +out: + free(mdata); + + return ret; +} + +/** + * 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 + * @bank: Bank of which the image's Accept bit is to be set + * + * 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, u32 bank) +{ + return fwu_set_clear_image_accept(img_type_id, bank, + IMAGE_ACCEPT_SET); +} + +/** + * 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 + * @bank: Bank of which the image's Accept 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) +{ + return fwu_set_clear_image_accept(img_type_id, bank, + IMAGE_ACCEPT_CLEAR); +} + +/** + * fwu_get_mdata() - Get a FWU metadata copy + * @mdata: Copy of the FWU metadata + * + * Get a valid copy of the FWU metadata. + * + * Note: This function is to be called first when modifying any fields + * in the metadata. The sequence of calls to modify any field in the + * metadata would be 1) fwu_get_mdata 2) Modify metadata, followed by + * 3) fwu_update_mdata + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_get_mdata(struct fwu_mdata **mdata) +{ + int ret; + struct udevice *dev = NULL; + const struct fwu_mdata_ops *ops = NULL; + + ret = fwu_get_dev_ops(&dev, &ops); + if (ret) + return ret; + + if (!ops->get_mdata) { + log_err("get_mdata() method not defined\n"); + return -ENOSYS; + } + + return ops->get_mdata(dev, mdata); +} + +/** + * fwu_update_mdata() - Update the FWU metadata + * @mdata: Copy of the FWU metadata + * + * Update the FWU metadata structure by writing to the + * FWU metadata partitions. + * + * Note: This function is not to be called directly to update the + * metadata fields. The sequence of function calls should be + * 1) fwu_get_mdata() 2) Modify the medata fields 3) fwu_update_mdata() + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_update_mdata(struct fwu_mdata *mdata) +{ + int ret; + void *buf; + struct udevice *dev = NULL; + const struct fwu_mdata_ops *ops = NULL; + + ret = fwu_get_dev_ops(&dev, &ops); + if (ret) + return ret; + + if (!ops->update_mdata) { + log_err("get_mdata() method not defined\n"); + return -ENOSYS; + } + + /* + * 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)); + + return ops->update_mdata(dev, mdata); +} + +UCLASS_DRIVER(fwu_mdata) = { + .id = UCLASS_FWU_MDATA, + .name = "fwu-mdata", +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..598a8c10a0 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -58,6 +58,7 @@ enum uclass_id { UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ + UCLASS_FWU_MDATA, /* FWU Metadata Access */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ UCLASS_HASH, /* Hash device */ UCLASS_HWSPINLOCK, /* Hardware semaphores */ diff --git a/include/fwu.h b/include/fwu.h new file mode 100644 index 0000000000..e03cfff800 --- /dev/null +++ b/include/fwu.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022, Linaro Limited + */ + +#if !defined _FWU_H_ +#define _FWU_H_ + +#include <blk.h> +#include <efi.h> + +#include <linux/types.h> + +struct fwu_mdata; +struct udevice; + +/** + * @mdata_check: check the validity of the FWU metadata partitions + * @get_mdata() - Get a FWU metadata copy + * @update_mdata() - Update the FWU metadata copy + */ +struct fwu_mdata_ops { + int (*mdata_check)(struct udevice *dev); + + int (*get_mdata)(struct udevice *dev, struct fwu_mdata **mdata); + + int (*update_mdata)(struct udevice *dev, struct fwu_mdata *mdata); +}; + +#define FWU_MDATA_VERSION 0x1 + +#define FWU_MDATA_GUID \ + EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ + 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23) + +int fwu_get_mdata(struct fwu_mdata **mdata); +int fwu_update_mdata(struct fwu_mdata *mdata); +int fwu_get_active_index(u32 *active_idx); +int fwu_update_active_index(u32 active_idx); +int fwu_get_image_alt_num(efi_guid_t *image_type_id, u32 update_bank, + int *alt_num); +int fwu_mdata_check(void); +int fwu_revert_boot_index(void); +int fwu_accept_image(efi_guid_t *img_type_id, u32 bank); +int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank); + +int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, + int *alt_num); +#endif /* _FWU_H_ */ diff --git a/include/fwu_mdata.h b/include/fwu_mdata.h new file mode 100644 index 0000000000..72e3edab43 --- /dev/null +++ b/include/fwu_mdata.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022, Linaro Limited + */ + +#if !defined _FWU_MDATA_H_ +#define _FWU_MDATA_H_ + +#include <efi.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; + uint32_t accepted; + uint32_t reserved; +} __attribute__((__packed__)); + +/** + * 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]; +} __attribute__((__packed__)); + +/** + * struct fwu_mdata - FWU metadata structure for multi-bank updates + * @crc32: crc32 value for the FWU metadata + * @version: FWU 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_mdata { + uint32_t crc32; + uint32_t version; + uint32_t active_index; + uint32_t previous_active_index; + + struct fwu_image_entry img_entry[CONFIG_FWU_NUM_IMAGES_PER_BANK]; +} __attribute__((__packed__)); + +#endif /* _FWU_MDATA_H_ */

Hi Sughosh,
- */
+#define LOG_CATEGORY UCLASS_FWU_MDATA
[...]
+/**
- fwu_verify_mdata() - Verify the FWU metadata
- @mdata: FWU metadata structure
- @pri_part: FWU metadata partition is primary or secondary
- Verify the FWU metadata by computing the CRC32 for the metadata
- structure and comparing it against the CRC32 value stored as part
- of the structure.
+/**
- fwu_update_active_index() - Update active_index from the FWU metadata
- @active_idx: active_index value to be updated
+int fwu_update_active_index(u32 active_idx) +{
int ret;
struct fwu_mdata *mdata = NULL;
if (active_idx > CONFIG_FWU_NUM_BANKS - 1) {
log_err("Active index value to be updated is incorrect\n");
Nit but "Invalid active index value" is makes more sense
return -1;
}
[...]
+}
+/**
- fwu_get_image_alt_num() - Get the dfu alt number to be used for capsule update
- @image_type_id: pointer to the 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)
+{
int ret, i;
efi_guid_t *image_guid;
struct udevice *dev = NULL;
struct fwu_mdata *mdata = NULL;
struct fwu_image_entry *img_entry;
const struct fwu_mdata_ops *ops = NULL;
struct fwu_image_bank_info *img_bank_info;
ret = fwu_get_dev_ops(&dev, &ops);
if (ret)
return ret;
ret = fwu_get_mdata(&mdata);
if (ret) {
log_err("Unable to get valid FWU metadata\n");
goto out;
}
/*
* The FWU 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,
&mdata->img_entry[i].image_type_uuid)) {
img_entry = &mdata->img_entry[i];
img_bank_info = &img_entry->img_bank_info[update_bank];
image_guid = &img_bank_info->image_uuid;
ret = fwu_plat_get_alt_num(dev, image_guid, alt_num);
break;
}
}
I think if you set ret = -EINVAL on top of the for loop you can mostly get rid of the code below. ret = -EINVAL for () { ..... ret = fwu_plat_get_alt_num(dev, image_guid, alt_num); if (ret) log_err("alt_num not found for partition with GUID %pUs\n", image_guid); goto out; //on success instead of break } log_err("Partition with the image type %pUs not found\n", image_type_id);
out: free(mdata);
return ret;
Isn't that more readable?
if (i == CONFIG_FWU_NUM_IMAGES_PER_BANK) {
log_err("Partition with the image type %pUs not found\n",
image_type_id);
ret = -EINVAL;
goto out;
}
if (!ret) {
log_debug("alt_num %d for partition %pUs\n",
*alt_num, image_guid);
} else {
log_err("alt_num not found for partition with GUID %pUs\n",
image_guid);
ret = -EINVAL;
}
+out:
free(mdata);
return ret;
+}
+/**
[...]
u32 bank, u8 action)
+{
int ret, i;
u32 nimages;
Get rid of nimages when they are only used as CONFIG_FWU_NUM_IMAGES_PER_BANK (there's more than one)
[...]
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..598a8c10a0 100644 @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/*
- Copyright (c) 2022, Linaro Limited
- */
+#if !defined _FWU_MDATA_H_ +#define _FWU_MDATA_H_
+#include <efi.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;
uint32_t accepted;
uint32_t reserved;
+} __attribute__((__packed__));
The spec doesn't require those to be packed. If there's a reason we should have it in comments, otherwise just drop the packed attribute
+/**
- 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];
+} __attribute__((__packed__));
+/**
- struct fwu_mdata - FWU metadata structure for multi-bank updates
- @crc32: crc32 value for the FWU metadata
- @version: FWU 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_mdata {
uint32_t crc32;
uint32_t version;
uint32_t active_index;
uint32_t previous_active_index;
struct fwu_image_entry img_entry[CONFIG_FWU_NUM_IMAGES_PER_BANK];
+} __attribute__((__packed__));
+#endif /* _FWU_MDATA_H_ */
2.34.1
Thanks! /Ilias

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 a driver 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 Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com --- Changes since V6: * Define the LOG_CATEGORY macro as suggested by Patrick
drivers/fwu-mdata/Kconfig | 9 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 410 ++++++++++++++++++++++++++ include/fwu.h | 5 + 4 files changed, 425 insertions(+) create mode 100644 drivers/fwu-mdata/fwu_mdata_gpt_blk.c
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig index d6a21c8e19..d5edef19d6 100644 --- a/drivers/fwu-mdata/Kconfig +++ b/drivers/fwu-mdata/Kconfig @@ -5,3 +5,12 @@ config DM_FWU_MDATA Enable support for accessing FWU Metadata partitions. The FWU Metadata partitions reside on the same storage device which contains the other FWU updatable firmware images. + +config FWU_MDATA_GPT_BLK + bool "FWU Metadata access for GPT partitioned Block devices" + select PARTITION_TYPE_GUID + select PARTITION_UUIDS + depends on DM && HAVE_BLOCK_DEVICE && EFI_PARTITION + help + Enable support for accessing FWU Metadata on GPT partitioned + block devices. diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile index e53a8c9983..313049f67a 100644 --- a/drivers/fwu-mdata/Makefile +++ b/drivers/fwu-mdata/Makefile @@ -4,3 +4,4 @@ #
obj-$(CONFIG_DM_FWU_MDATA) += fwu-mdata-uclass.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_mdata_gpt_blk.o diff --git a/drivers/fwu-mdata/fwu_mdata_gpt_blk.c b/drivers/fwu-mdata/fwu_mdata_gpt_blk.c new file mode 100644 index 0000000000..f694c4369b --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_gpt_blk.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, Linaro Limited + */ + +#define LOG_CATEGORY UCLASS_FWU_MDATA + +#include <blk.h> +#include <dm.h> +#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <malloc.h> +#include <memalign.h> +#include <part.h> +#include <part_efi.h> + +#include <dm/device-internal.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h> + +#define PRIMARY_PART BIT(0) +#define SECONDARY_PART BIT(1) +#define BOTH_PARTS (PRIMARY_PART | SECONDARY_PART) + +#define MDATA_READ BIT(0) +#define MDATA_WRITE BIT(1) + +static int gpt_get_mdata_partitions(struct blk_desc *desc, + u16 *primary_mpart, + u16 *secondary_mpart) +{ + int i, ret; + u32 mdata_parts; + efi_guid_t part_type_guid; + struct disk_partition info; + const efi_guid_t fwu_mdata_guid = FWU_MDATA_GUID; + + mdata_parts = 0; + for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) { + if (part_get_info(desc, i, &info)) + continue; + uuid_str_to_bin(info.type_guid, part_type_guid.b, + UUID_STR_FORMAT_GUID); + + if (!guidcmp(&fwu_mdata_guid, &part_type_guid)) { + ++mdata_parts; + if (!*primary_mpart) + *primary_mpart = i; + else + *secondary_mpart = i; + } + } + + if (mdata_parts != 2) { + log_err("Expect two copies of the FWU metadata instead of %d\n", + mdata_parts); + ret = -EINVAL; + } else { + ret = 0; + } + + return ret; +} + +static int gpt_get_mdata_disk_part(struct blk_desc *desc, + struct disk_partition *info, + u32 part_num) +{ + int ret; + char *mdata_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 FWU metadata part %d", + part_num); + return -1; + } + + /* Check that it is indeed the FWU metadata partition */ + if (!strncmp(info->type_guid, mdata_guid_str, UUID_STR_LEN)) { + /* Found the FWU metadata partition */ + return 0; + } + + return -1; +} + +static int gpt_read_write_mdata(struct blk_desc *desc, + struct fwu_mdata *mdata, + u8 access, u32 part_num) +{ + int ret; + u32 len, blk_start, blkcnt; + struct disk_partition info; + + ALLOC_CACHE_ALIGN_BUFFER_PAD(struct fwu_mdata, mdata_aligned, 1, + desc->blksz); + + ret = gpt_get_mdata_disk_part(desc, &info, part_num); + if (ret < 0) { + printf("Unable to get the FWU metadata partition\n"); + return -ENODEV; + } + + len = sizeof(*mdata); + blkcnt = BLOCK_CNT(len, desc); + if (blkcnt > info.size) { + log_err("Block count exceeds FWU metadata partition size\n"); + return -ERANGE; + } + + blk_start = info.start; + if (access == MDATA_READ) { + if (blk_dread(desc, blk_start, blkcnt, mdata_aligned) != blkcnt) { + log_err("Error reading FWU metadata from the device\n"); + return -EIO; + } + memcpy(mdata, mdata_aligned, sizeof(struct fwu_mdata)); + } else { + if (blk_dwrite(desc, blk_start, blkcnt, mdata) != blkcnt) { + log_err("Error writing FWU metadata to the device\n"); + return -EIO; + } + } + + return 0; +} + +static int gpt_read_mdata(struct blk_desc *desc, + struct fwu_mdata *mdata, u32 part_num) +{ + return gpt_read_write_mdata(desc, mdata, MDATA_READ, part_num); +} + +static int gpt_write_mdata_partition(struct blk_desc *desc, + struct fwu_mdata *mdata, + u32 part_num) +{ + return gpt_read_write_mdata(desc, mdata, MDATA_WRITE, part_num); +} + +static int fwu_gpt_update_mdata(struct udevice *dev, struct fwu_mdata *mdata) +{ + int ret; + struct blk_desc *desc; + u16 primary_mpart = 0, secondary_mpart = 0; + struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev); + + desc = dev_get_uclass_plat(priv->blk_dev); + if (!desc) { + log_err("Block device not found\n"); + return -ENODEV; + } + + ret = gpt_get_mdata_partitions(desc, &primary_mpart, + &secondary_mpart); + + if (ret < 0) { + log_err("Error getting the FWU metadata partitions\n"); + return -ENODEV; + } + + /* First write the primary partition*/ + ret = gpt_write_mdata_partition(desc, mdata, primary_mpart); + if (ret < 0) { + log_err("Updating primary FWU metadata partition failed\n"); + return ret; + } + + /* And now the replica */ + ret = gpt_write_mdata_partition(desc, mdata, secondary_mpart); + if (ret < 0) { + log_err("Updating secondary FWU metadata partition failed\n"); + return ret; + } + + return 0; +} + +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; + } + + *mdata = malloc(sizeof(struct fwu_mdata)); + if (!*mdata) { + log_err("Unable to allocate memory for reading FWU metadata\n"); + return -ENOMEM; + } + + ret = gpt_read_mdata(desc, *mdata, primary_mpart); + if (ret < 0) { + log_err("Failed to read the FWU metadata from the device\n"); + return -EIO; + } + + ret = fwu_verify_mdata(*mdata, 1); + if (!ret) + return 0; + + /* + * Verification 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); + if (!ret) + return 0; + + /* Both the FWU metadata copies are corrupted. */ + return -1; +} + +static int gpt_check_mdata_validity(struct udevice *dev) +{ + int ret; + struct blk_desc *desc; + struct fwu_mdata pri_mdata; + struct fwu_mdata secondary_mdata; + u16 primary_mpart = 0, secondary_mpart = 0; + u16 valid_partitions, invalid_partitions; + struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev); + + desc = dev_get_uclass_plat(priv->blk_dev); + if (!desc) { + log_err("Block device not found\n"); + return -ENODEV; + } + + /* + * Two FWU metadata partitions are expected. + * If we don't have two, user needs to create + * them first + */ + valid_partitions = 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_read_mdata(desc, &pri_mdata, primary_mpart); + if (ret < 0) { + log_err("Failed to read the FWU metadata from the device\n"); + goto secondary_read; + } + + ret = fwu_verify_mdata(&pri_mdata, 1); + if (!ret) + valid_partitions |= PRIMARY_PART; + +secondary_read: + /* Now check the secondary partition */ + ret = gpt_read_mdata(desc, &secondary_mdata, secondary_mpart); + if (ret < 0) { + log_err("Failed to read the FWU metadata from the device\n"); + goto mdata_restore; + } + + ret = fwu_verify_mdata(&secondary_mdata, 0); + if (!ret) + valid_partitions |= SECONDARY_PART; + +mdata_restore: + if (valid_partitions == (PRIMARY_PART | SECONDARY_PART)) { + ret = -1; + /* + * Before returning, check that both the + * FWU metadata copies are the same. If not, + * the FWU metadata copies need to be + * re-populated. + */ + if (!memcmp(&pri_mdata, &secondary_mdata, + sizeof(struct fwu_mdata))) { + ret = 0; + } else { + log_err("Both FWU metadata copies are valid but do not match. Please check!\n"); + } + goto out; + } + + ret = -1; + if (!(valid_partitions & BOTH_PARTS)) + goto out; + + invalid_partitions = valid_partitions ^ BOTH_PARTS; + ret = gpt_write_mdata_partition(desc, + (invalid_partitions == PRIMARY_PART) ? + &secondary_mdata : &pri_mdata, + (invalid_partitions == PRIMARY_PART) ? + primary_mpart : secondary_mpart); + + if (ret < 0) + log_err("Restoring %s FWU metadata partition failed\n", + (invalid_partitions == PRIMARY_PART) ? + "primary" : "secondary"); + +out: + return ret; +} + +static int fwu_gpt_mdata_check(struct udevice *dev) +{ + /* + * Check if both the copies of the FWU metadata are + * valid. If one has gone bad, restore it from the + * other good copy. + */ + return gpt_check_mdata_validity(dev); +} + +static int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata **mdata) +{ + struct blk_desc *desc; + struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev); + + desc = dev_get_uclass_plat(priv->blk_dev); + if (!desc) { + log_err("Block device not found\n"); + return -ENODEV; + } + + return gpt_get_mdata(desc, mdata); +} + +static int fwu_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev) +{ + u32 phandle; + int ret, size; + struct udevice *parent, *child; + const fdt32_t *phandle_p = NULL; + + phandle_p = dev_read_prop(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), + &parent); + if (ret) + return ret; + + ret = -ENODEV; + for (device_find_first_child(parent, &child); child; + device_find_next_child(&child)) { + if (device_get_uclass_id(child) == UCLASS_BLK) { + *mdata_dev = child; + ret = 0; + } + } + + return ret; +} + +static int fwu_mdata_gpt_blk_probe(struct udevice *dev) +{ + int ret; + struct udevice *mdata_dev = NULL; + struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev); + + ret = fwu_get_mdata_device(dev, &mdata_dev); + if (ret) + return ret; + + priv->blk_dev = mdata_dev; + + return 0; +} + +static const struct fwu_mdata_ops fwu_gpt_blk_ops = { + .mdata_check = fwu_gpt_mdata_check, + .get_mdata = fwu_gpt_get_mdata, + .update_mdata = fwu_gpt_update_mdata, +}; + +static const struct udevice_id fwu_mdata_ids[] = { + { .compatible = "u-boot,fwu-mdata-gpt" }, + { } +}; + +U_BOOT_DRIVER(fwu_mdata_gpt_blk) = { + .name = "fwu-mdata-gpt-blk", + .id = UCLASS_FWU_MDATA, + .of_match = fwu_mdata_ids, + .ops = &fwu_gpt_blk_ops, + .probe = fwu_mdata_gpt_blk_probe, + .priv_auto = sizeof(struct fwu_mdata_gpt_blk_priv), +}; diff --git a/include/fwu.h b/include/fwu.h index e03cfff800..8259c75d12 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -14,6 +14,10 @@ struct fwu_mdata; struct udevice;
+struct fwu_mdata_gpt_blk_priv { + struct udevice *blk_dev; +}; + /** * @mdata_check: check the validity of the FWU metadata partitions * @get_mdata() - Get a FWU metadata copy @@ -39,6 +43,7 @@ int fwu_get_active_index(u32 *active_idx); int fwu_update_active_index(u32 active_idx); int fwu_get_image_alt_num(efi_guid_t *image_type_id, u32 update_bank, int *alt_num); +int fwu_verify_mdata(struct fwu_mdata *mdata, bool pri_part); int fwu_mdata_check(void); int fwu_revert_boot_index(void); int fwu_accept_image(efi_guid_t *img_type_id, u32 bank);

The FWU metadata structure is accessed through the driver model interface. On the stm32mp157c-dk2 board, the FWU metadata is stored on the uSD card. Add the fwu-mdata node on the u-boot specifc dtsi file for accessing the metadata structure.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com --- Changes since V6: None
arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi b/arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi index 06ef3a4095..24f86209db 100644 --- a/arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi +++ b/arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi @@ -4,3 +4,10 @@ */
#include "stm32mp157a-dk1-u-boot.dtsi" + +/ { + fwu-mdata { + compatible = "u-boot,fwu-mdata-gpt"; + fwu-mdata-store = <&sdmmc1>; + }; +};

On Thu, 14 Jul 2022 at 21:39, Sughosh Ganu sughosh.ganu@linaro.org wrote:
The FWU metadata structure is accessed through the driver model interface. On the stm32mp157c-dk2 board, the FWU metadata is stored on the uSD card. Add the fwu-mdata node on the u-boot specifc dtsi file for accessing the metadata structure.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com
Changes since V6: None
arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi b/arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi index 06ef3a4095..24f86209db 100644 --- a/arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi +++ b/arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi @@ -4,3 +4,10 @@ */
#include "stm32mp157a-dk1-u-boot.dtsi"
+/ {
fwu-mdata {
compatible = "u-boot,fwu-mdata-gpt";
fwu-mdata-store = <&sdmmc1>;
};
+};
2.34.1
Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org

Enabling capsule update functionality on the platform requires populating information on the images that are to be updated using the functionality. Do so for the DK2 board.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com --- Changes since V6: * s/STM32MP1/STM32MP15/ as suggested by Patrick
board/st/stm32mp1/stm32mp1.c | 19 +++++++++++++++++++ include/configs/stm32mp15_common.h | 4 ++++ 2 files changed, 23 insertions(+)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 9496890d16..e3a04f8d8a 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -11,6 +11,7 @@ #include <clk.h> #include <config.h> #include <dm.h> +#include <efi_loader.h> #include <env.h> #include <env_internal.h> #include <fdt_simplefb.h> @@ -87,6 +88,16 @@ #define USB_START_LOW_THRESHOLD_UV 1230000 #define USB_START_HIGH_THRESHOLD_UV 2150000
+#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) +struct efi_fw_image fw_images[1]; + +struct efi_capsule_update_info update_info = { + .images = fw_images, +}; + +u8 num_image_type_guids = ARRAY_SIZE(fw_images); +#endif /* EFI_HAVE_CAPSULE_SUPPORT */ + int board_early_init_f(void) { /* nothing to do, only used in SPL */ @@ -670,6 +681,14 @@ int board_init(void)
setup_led(LEDST_ON);
+#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) + if (board_is_stm32mp15x_dk2()) { + efi_guid_t image_type_guid = STM32MP15_DK2_FIP_IMAGE_GUID; + guidcpy(&fw_images[0].image_type_id, &image_type_guid); + fw_images[0].fw_name = u"STM32MP15-DK2-FIP"; + fw_images[0].image_index = 5; + } +#endif return 0; }
diff --git a/include/configs/stm32mp15_common.h b/include/configs/stm32mp15_common.h index c5412ffeb3..6ab10d8ce5 100644 --- a/include/configs/stm32mp15_common.h +++ b/include/configs/stm32mp15_common.h @@ -34,6 +34,10 @@ #define CONFIG_SERVERIP 192.168.1.1 #endif
+#define STM32MP15_DK2_FIP_IMAGE_GUID \ + EFI_GUID(0x19d5df83, 0x11b0, 0x457b, 0xbe, 0x2c, \ + 0x75, 0x59, 0xc1, 0x31, 0x42, 0xa5) + /*****************************************************************************/ #ifdef CONFIG_DISTRO_DEFAULTS /*****************************************************************************/

On Thu, 14 Jul 2022 at 21:40, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Enabling capsule update functionality on the platform requires populating information on the images that are to be updated using the functionality. Do so for the DK2 board.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com
Changes since V6:
- s/STM32MP1/STM32MP15/ as suggested by Patrick
board/st/stm32mp1/stm32mp1.c | 19 +++++++++++++++++++ include/configs/stm32mp15_common.h | 4 ++++ 2 files changed, 23 insertions(+)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 9496890d16..e3a04f8d8a 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -11,6 +11,7 @@ #include <clk.h> #include <config.h> #include <dm.h> +#include <efi_loader.h> #include <env.h> #include <env_internal.h> #include <fdt_simplefb.h> @@ -87,6 +88,16 @@ #define USB_START_LOW_THRESHOLD_UV 1230000 #define USB_START_HIGH_THRESHOLD_UV 2150000
+#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) +struct efi_fw_image fw_images[1];
+struct efi_capsule_update_info update_info = {
.images = fw_images,
+};
+u8 num_image_type_guids = ARRAY_SIZE(fw_images); +#endif /* EFI_HAVE_CAPSULE_SUPPORT */
int board_early_init_f(void) { /* nothing to do, only used in SPL */ @@ -670,6 +681,14 @@ int board_init(void)
setup_led(LEDST_ON);
+#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT)
if (board_is_stm32mp15x_dk2()) {
efi_guid_t image_type_guid = STM32MP15_DK2_FIP_IMAGE_GUID;
guidcpy(&fw_images[0].image_type_id, &image_type_guid);
fw_images[0].fw_name = u"STM32MP15-DK2-FIP";
fw_images[0].image_index = 5;
}
+#endif return 0; }
diff --git a/include/configs/stm32mp15_common.h b/include/configs/stm32mp15_common.h index c5412ffeb3..6ab10d8ce5 100644 --- a/include/configs/stm32mp15_common.h +++ b/include/configs/stm32mp15_common.h @@ -34,6 +34,10 @@ #define CONFIG_SERVERIP 192.168.1.1 #endif
+#define STM32MP15_DK2_FIP_IMAGE_GUID \
EFI_GUID(0x19d5df83, 0x11b0, 0x457b, 0xbe, 0x2c, \
0x75, 0x59, 0xc1, 0x31, 0x42, 0xa5)
/*****************************************************************************/ #ifdef CONFIG_DISTRO_DEFAULTS /*****************************************************************************/ -- 2.34.1
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org

Add helper functions needed for accessing the FWU 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 Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com --- Changes since V6: None
board/st/stm32mp1/stm32mp1.c | 40 ++++++++++++++++ include/fwu.h | 3 ++ lib/fwu_updates/Makefile | 6 +++ lib/fwu_updates/fwu_gpt.c | 88 ++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 lib/fwu_updates/Makefile create mode 100644 lib/fwu_updates/fwu_gpt.c
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index e3a04f8d8a..44c7943f1d 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -7,9 +7,11 @@
#include <common.h> #include <adc.h> +#include <blk.h> #include <bootm.h> #include <clk.h> #include <config.h> +#include <dfu.h> #include <dm.h> #include <efi_loader.h> #include <env.h> @@ -25,9 +27,11 @@ #include <log.h> #include <malloc.h> #include <misc.h> +#include <mmc.h> #include <mtd_node.h> #include <net.h> #include <netdev.h> +#include <part.h> #include <phy.h> #include <remoteproc.h> #include <reset.h> @@ -962,3 +966,39 @@ 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); + +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) + +#include <fwu.h> +#include <fwu_mdata.h> + +int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, + int *alt_num) +{ + struct blk_desc *desc; + struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev); + + desc = dev_get_uclass_plat(priv->blk_dev); + if (!desc) { + log_err("Block device not found\n"); + return -ENODEV; + } + + return fwu_gpt_get_alt_num(desc, image_guid, alt_num, DFU_DEV_MMC); +} + +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; +} +#endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/include/fwu.h b/include/fwu.h index 8259c75d12..38dceca9c5 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -51,4 +51,7 @@ int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank);
int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, int *alt_num); +int fwu_gpt_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid, + int *alt_num, unsigned char dfu_dev); +int fwu_plat_get_update_index(u32 *update_idx); #endif /* _FWU_H_ */ diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile new file mode 100644 index 0000000000..5a59e4a833 --- /dev/null +++ b/lib/fwu_updates/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2022, Linaro Limited +# + +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o diff --git a/lib/fwu_updates/fwu_gpt.c b/lib/fwu_updates/fwu_gpt.c new file mode 100644 index 0000000000..434ec76bde --- /dev/null +++ b/lib/fwu_updates/fwu_gpt.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022, Linaro Limited + */ + +#include <blk.h> +#include <dfu.h> +#include <efi.h> +#include <efi_loader.h> +#include <log.h> +#include <part.h> + +#include <linux/errno.h> + +static int get_gpt_dfu_identifier(struct blk_desc *desc, efi_guid_t *image_guid) +{ + int i; + struct disk_partition info; + efi_guid_t unique_part_guid; + + for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) { + if (part_get_info(desc, i, &info)) + continue; + uuid_str_to_bin(info.uuid, unique_part_guid.b, + UUID_STR_FORMAT_GUID); + + if (!guidcmp(&unique_part_guid, image_guid)) + return i; + } + + log_err("No partition found with image_guid %pUs\n", image_guid); + return -ENOENT; +} + +int fwu_gpt_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid, + int *alt_num, unsigned char dfu_dev) +{ + int ret = -1; + int i, part, dev_num; + int nalt; + struct dfu_entity *dfu; + + dev_num = desc->devnum; + part = get_gpt_dfu_identifier(desc, image_guid); + if (part < 0) + return -ENOENT; + + dfu_init_env_entities(NULL, NULL); + + nalt = 0; + list_for_each_entry(dfu, &dfu_list, list) { + nalt++; + } + + if (!nalt) { + log_warning("No entities in dfu_alt_info\n"); + dfu_free_entities(); + return -ENOENT; + } + + for (i = 0; i < nalt; i++) { + dfu = dfu_get_entity(i); + + if (!dfu) + continue; + + /* + * Currently, Multi Bank update + * feature is being supported + * only on GPT partitioned + * MMC/SD devices. + */ + if (dfu->dev_type != dfu_dev) + continue; + + if (dfu->layout == DFU_RAW_ADDR && + dfu->data.mmc.dev_num == dev_num && + dfu->data.mmc.part == part) { + *alt_num = dfu->alt; + ret = 0; + break; + } + } + + dfu_free_entities(); + + return ret; +}

Hi Sughosh,
On Thu, 14 Jul 2022 at 21:40, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add helper functions needed for accessing the FWU 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 Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com
Changes since V6: None
board/st/stm32mp1/stm32mp1.c | 40 ++++++++++++++++ include/fwu.h | 3 ++ lib/fwu_updates/Makefile | 6 +++ lib/fwu_updates/fwu_gpt.c | 88 ++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 lib/fwu_updates/Makefile create mode 100644 lib/fwu_updates/fwu_gpt.c
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index e3a04f8d8a..44c7943f1d 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -7,9 +7,11 @@
#include <common.h> #include <adc.h> +#include <blk.h> #include <bootm.h> #include <clk.h> #include <config.h> +#include <dfu.h> #include <dm.h> #include <efi_loader.h> #include <env.h> @@ -25,9 +27,11 @@ #include <log.h> #include <malloc.h> #include <misc.h> +#include <mmc.h> #include <mtd_node.h> #include <net.h> #include <netdev.h> +#include <part.h> #include <phy.h> #include <remoteproc.h> #include <reset.h> @@ -962,3 +966,39 @@ 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);
+#if defined(CONFIG_FWU_MULTI_BANK_UPDATE)
+#include <fwu.h> +#include <fwu_mdata.h>
+int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid,
int *alt_num)
+{
struct blk_desc *desc;
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
desc = dev_get_uclass_plat(priv->blk_dev);
if (!desc) {
log_err("Block device not found\n");
return -ENODEV;
}
return fwu_gpt_get_alt_num(desc, image_guid, alt_num, DFU_DEV_MMC);
+}
+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;
+}
I'dl ike to move those 2 out to the generic API as well. Sure a device might be paranoid and have more than 2 banks for firmware redundancy, however I believe 2 will be the main case. So we can use these 2 as generic weak functions and special devices can override that
Regards /Ilias
+#endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/include/fwu.h b/include/fwu.h index 8259c75d12..38dceca9c5 100644 --- a/include/fwu.h +++ b/include/fwu.h
[...]

On Thu, 14 Jul 2022 at 13:40, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add helper functions needed for accessing the FWU 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 Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com
Changes since V6: None
board/st/stm32mp1/stm32mp1.c | 40 ++++++++++++++++ include/fwu.h | 3 ++ lib/fwu_updates/Makefile | 6 +++ lib/fwu_updates/fwu_gpt.c | 88 ++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 lib/fwu_updates/Makefile create mode 100644 lib/fwu_updates/fwu_gpt.c
fwu_gpt is to be used by all platforms that have the gpt-scheme, not just STM.
So do you want to break this patch into two ? One generic that introduces the helper functions and another for stm using that api
Also, you may want to specify GPL-2.0-or-later everywhere.
thanks.

On Fri, 22 Jul 2022 at 09:09, Jassi Brar jaswinder.singh@linaro.org wrote:
On Thu, 14 Jul 2022 at 13:40, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add helper functions needed for accessing the FWU 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 Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com
Changes since V6: None
board/st/stm32mp1/stm32mp1.c | 40 ++++++++++++++++ include/fwu.h | 3 ++ lib/fwu_updates/Makefile | 6 +++ lib/fwu_updates/fwu_gpt.c | 88 ++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 lib/fwu_updates/Makefile create mode 100644 lib/fwu_updates/fwu_gpt.c
fwu_gpt is to be used by all platforms that have the gpt-scheme, not just STM.
So do you want to break this patch into two ? One generic that introduces the helper functions and another for stm using that api
Ilias has suggested moving the two API functions to common files and having them defined as weak functions. I will be making this change in the next version.
Also, you may want to specify GPL-2.0-or-later everywhere.
I thought I had changed that in this version of the patchset, but unfortunately these two files have not been changed. I will change this in the next version.
-sughosh

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 Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com --- Changes since V6: None
arch/arm/mach-stm32mp/include/mach/stm32.h | 5 +++++ board/st/stm32mp1/stm32mp1.c | 8 ++++++++ include/fwu.h | 1 + 3 files changed, 14 insertions(+)
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index c70375a723..c85ae6a34e 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -112,11 +112,16 @@ enum boot_device { #ifdef CONFIG_STM32MP15x #define TAMP_BACKUP_MAGIC_NUMBER TAMP_BACKUP_REGISTER(4) #define TAMP_BACKUP_BRANCH_ADDRESS TAMP_BACKUP_REGISTER(5) +#define TAMP_FWU_BOOT_INFO_REG 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) #define TAMP_BOOTCOUNT TAMP_BACKUP_REGISTER(21)
+#define TAMP_FWU_BOOT_IDX_MASK GENMASK(3, 0) + +#define TAMP_FWU_BOOT_IDX_OFFSET 0 + #define TAMP_COPRO_STATE_OFF 0 #define TAMP_COPRO_STATE_INIT 1 #define TAMP_COPRO_STATE_CRUN 2 diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 44c7943f1d..ddf5053601 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -1001,4 +1001,12 @@ int fwu_plat_get_update_index(u32 *update_idx)
return ret; } + +void fwu_plat_get_bootidx(void *boot_idx) +{ + u32 *bootidx = boot_idx; + + *bootidx = (readl(TAMP_FWU_BOOT_INFO_REG) >> + TAMP_FWU_BOOT_IDX_OFFSET) & TAMP_FWU_BOOT_IDX_MASK; +} #endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/include/fwu.h b/include/fwu.h index 38dceca9c5..edb28c9659 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -49,6 +49,7 @@ int fwu_revert_boot_index(void); int fwu_accept_image(efi_guid_t *img_type_id, u32 bank); int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank);
+void fwu_plat_get_bootidx(void *boot_idx); int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, int *alt_num); int fwu_gpt_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid,

On Thu, 14 Jul 2022 at 21:40, Sughosh Ganu sughosh.ganu@linaro.org 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 Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com
Changes since V6: None
arch/arm/mach-stm32mp/include/mach/stm32.h | 5 +++++ board/st/stm32mp1/stm32mp1.c | 8 ++++++++ include/fwu.h | 1 + 3 files changed, 14 insertions(+)
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index c70375a723..c85ae6a34e 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -112,11 +112,16 @@ enum boot_device { #ifdef CONFIG_STM32MP15x #define TAMP_BACKUP_MAGIC_NUMBER TAMP_BACKUP_REGISTER(4) #define TAMP_BACKUP_BRANCH_ADDRESS TAMP_BACKUP_REGISTER(5) +#define TAMP_FWU_BOOT_INFO_REG 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) #define TAMP_BOOTCOUNT TAMP_BACKUP_REGISTER(21)
+#define TAMP_FWU_BOOT_IDX_MASK GENMASK(3, 0)
+#define TAMP_FWU_BOOT_IDX_OFFSET 0
#define TAMP_COPRO_STATE_OFF 0 #define TAMP_COPRO_STATE_INIT 1 #define TAMP_COPRO_STATE_CRUN 2 diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 44c7943f1d..ddf5053601 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -1001,4 +1001,12 @@ int fwu_plat_get_update_index(u32 *update_idx)
return ret;
}
+void fwu_plat_get_bootidx(void *boot_idx) +{
u32 *bootidx = boot_idx;
*bootidx = (readl(TAMP_FWU_BOOT_INFO_REG) >>
TAMP_FWU_BOOT_IDX_OFFSET) & TAMP_FWU_BOOT_IDX_MASK;
+} #endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/include/fwu.h b/include/fwu.h index 38dceca9c5..edb28c9659 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -49,6 +49,7 @@ int fwu_revert_boot_index(void); int fwu_accept_image(efi_guid_t *img_type_id, u32 bank); int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank);
+void fwu_plat_get_bootidx(void *boot_idx); int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, int *alt_num); int fwu_gpt_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid, -- 2.34.1
Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org

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 --- Changes since V6: None
common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 165 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
diff --git a/common/board_r.c b/common/board_r.c index ed29069d2d..5210ed6f32 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -15,6 +15,7 @@ #include <cpu_func.h> #include <exports.h> #include <flash.h> +#include <fwu.h> #include <hang.h> #include <image.h> #include <irq_func.h> @@ -785,6 +786,10 @@ static init_fnc_t init_sequence_r[] = { #if defined(CONFIG_PRAM) initr_mem, #endif + +#ifdef CONFIG_FWU_MULTI_BANK_UPDATE + fwu_boottime_checks, +#endif run_main_loop, };
diff --git a/include/fwu.h b/include/fwu.h index edb28c9659..b374fd1179 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -37,6 +37,9 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+u8 fwu_update_checks_pass(void); +int fwu_boottime_checks(void); + int fwu_get_mdata(struct fwu_mdata **mdata); int fwu_update_mdata(struct fwu_mdata *mdata); int fwu_get_active_index(u32 *active_idx); diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c new file mode 100644 index 0000000000..10a0522333 --- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, Linaro Limited + */ + +#include <dm.h> +#include <efi.h> +#include <efi_loader.h> +#include <efi_variable.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <malloc.h> + +#include <linux/errno.h> +#include <linux/types.h> + +static u8 trial_state; +static u8 boottime_check; + +static int fwu_trial_state_check(void) +{ + int ret, i; + efi_status_t status; + efi_uintn_t var_size; + u16 trial_state_ctr; + u32 nimages, active_bank, var_attributes, active_idx; + struct fwu_mdata *mdata = NULL; + struct fwu_image_entry *img_entry; + struct fwu_image_bank_info *img_bank_info; + + ret = fwu_get_mdata(&mdata); + if (ret) + return ret; + + ret = 0; + nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK; + active_bank = mdata->active_index; + img_entry = &mdata->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; + status = efi_get_variable_int(u"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"); + active_idx = mdata->active_index; + ret = fwu_revert_boot_index(); + if (ret) { + log_err("Unable to revert active_index\n"); + goto out; + } + + /* Delete the TrialStateCtr variable */ + status = efi_set_variable_int(u"TrialStateCtr", + &efi_global_variable_guid, + var_attributes, + 0, NULL, false); + if (status != EFI_SUCCESS) { + log_err("Unable to delete TrialStateCtr variable\n"); + ret = -1; + goto out; + } + } else { + status = efi_set_variable_int(u"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 { + /* Delete the variable */ + status = efi_set_variable_int(u"TrialStateCtr", + &efi_global_variable_guid, + 0, 0, NULL, NULL); + if (status != EFI_SUCCESS && status != EFI_NOT_FOUND) { + ret = -1; + log_err("Unable to delete TrialStateCtr variable\n"); + } + } + +out: + free(mdata); + return ret; +} + +u8 fwu_update_checks_pass(void) +{ + return !trial_state && boottime_check; +} + +int fwu_boottime_checks(void) +{ + int ret; + u32 boot_idx, active_idx; + + ret = fwu_mdata_check(); + if (ret) { + return 0; + } + + /* + * 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 FWU metadata, + * update the active_index. + */ + fwu_plat_get_bootidx(&boot_idx); + if (boot_idx >= CONFIG_FWU_NUM_BANKS) { + log_err("Received incorrect value of boot_index\n"); + return 0; + } + + ret = fwu_get_active_index(&active_idx); + if (ret) { + log_err("Unable to read active_index\n"); + return 0; + } + + 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) + boottime_check = 1; + + return 0; + } + + if (efi_init_obj_list() != EFI_SUCCESS) + return 0; + + ret = fwu_trial_state_check(); + if (!ret) + boottime_check = 1; + + return 0; +}

Hi Sughosh,
On Thu, 14 Jul 2022 at 21:40, Sughosh Ganu sughosh.ganu@linaro.org wrote:
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
Changes since V6: None
common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 165 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
diff --git a/common/board_r.c b/common/board_r.c index ed29069d2d..5210ed6f32 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -15,6 +15,7 @@ #include <cpu_func.h> #include <exports.h> #include <flash.h> +#include <fwu.h> #include <hang.h> #include <image.h> #include <irq_func.h> @@ -785,6 +786,10 @@ static init_fnc_t init_sequence_r[] = { #if defined(CONFIG_PRAM) initr_mem, #endif
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE
fwu_boottime_checks,
+#endif run_main_loop, };
diff --git a/include/fwu.h b/include/fwu.h index edb28c9659..b374fd1179 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -37,6 +37,9 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+u8 fwu_update_checks_pass(void); +int fwu_boottime_checks(void);
int fwu_get_mdata(struct fwu_mdata **mdata); int fwu_update_mdata(struct fwu_mdata *mdata); int fwu_get_active_index(u32 *active_idx); diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c new file mode 100644 index 0000000000..10a0522333 --- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2022, Linaro Limited
- */
+#include <dm.h> +#include <efi.h> +#include <efi_loader.h> +#include <efi_variable.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <malloc.h>
+#include <linux/errno.h> +#include <linux/types.h>
+static u8 trial_state; +static u8 boottime_check;
+static int fwu_trial_state_check(void) +{
int ret, i;
efi_status_t status;
efi_uintn_t var_size;
u16 trial_state_ctr;
u32 nimages, active_bank, var_attributes, active_idx;
struct fwu_mdata *mdata = NULL;
struct fwu_image_entry *img_entry;
struct fwu_image_bank_info *img_bank_info;
ret = fwu_get_mdata(&mdata);
if (ret)
return ret;
ret = 0;
Is ret = 0 needed here? Isn't it 0 already ?
nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
active_bank = mdata->active_index;
img_entry = &mdata->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;
}
}
Is this used elsewhere in the patchset? The function is starting to be big, so perhaps moving this in a static bool "in_trial_state()" or similar would make it more readable.
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;
status = efi_get_variable_int(u"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");
active_idx = mdata->active_index;
ret = fwu_revert_boot_index();
if (ret) {
log_err("Unable to revert active_index\n");
goto out;
}
/* Delete the TrialStateCtr variable */
status = efi_set_variable_int(u"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
0, NULL, false);
if (status != EFI_SUCCESS) {
log_err("Unable to delete TrialStateCtr variable\n");
ret = -1;
goto out;
}
} else {
status = efi_set_variable_int(u"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 {
/* Delete the variable */
status = efi_set_variable_int(u"TrialStateCtr",
&efi_global_variable_guid,
0, 0, NULL, NULL);
the last argument should be false
if (status != EFI_SUCCESS && status != EFI_NOT_FOUND) {
Is there a chance the code will try to delete the variable when it's not there?
ret = -1;
log_err("Unable to delete TrialStateCtr variable\n");
}
}
Can we please move get/set variable into functions? Something like - trial_counter_inc - trial_counter_reset
Regards /Ilias
+out:
free(mdata);
return ret;
+}
+u8 fwu_update_checks_pass(void) +{
return !trial_state && boottime_check;
+}
+int fwu_boottime_checks(void) +{
int ret;
u32 boot_idx, active_idx;
ret = fwu_mdata_check();
if (ret) {
return 0;
}
/*
* 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 FWU metadata,
* update the active_index.
*/
fwu_plat_get_bootidx(&boot_idx);
if (boot_idx >= CONFIG_FWU_NUM_BANKS) {
log_err("Received incorrect value of boot_index\n");
return 0;
}
ret = fwu_get_active_index(&active_idx);
if (ret) {
log_err("Unable to read active_index\n");
return 0;
}
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)
boottime_check = 1;
return 0;
}
if (efi_init_obj_list() != EFI_SUCCESS)
return 0;
ret = fwu_trial_state_check();
if (!ret)
boottime_check = 1;
return 0;
+}
2.34.1

Hi Sughosh,
nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
active_bank = mdata->active_index;
img_entry = &mdata->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;
}
}
Is this used elsewhere in the patchset? The function is starting to be big, so perhaps moving this in a static bool "in_trial_state()" or similar would make it more readable.
There was a discussion about this on the synquacer thread for A/B updates. Once you split those in a function, it's better to extend the bootcount API with an EFI backed storage. The reasoning that a user might disable editing env variables for security reasons and that device might not be able to preserve RAM or store the counter in CPU registers across reboots.
If we extend the bootcount API with this code we can plug in the functionality seamlessly based on the hardware capabilities.
[...]
Regards
/Ilias

hi Ilias,
On Wed, 20 Jul 2022 at 13:06, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Sughosh,
nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
active_bank = mdata->active_index;
img_entry = &mdata->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;
}
}
Is this used elsewhere in the patchset? The function is starting to be big, so perhaps moving this in a static bool "in_trial_state()" or similar would make it more readable.
There was a discussion about this on the synquacer thread for A/B updates. Once you split those in a function, it's better to extend the bootcount API with an EFI backed storage. The reasoning that a user might disable editing env variables for security reasons and that device might not be able to preserve RAM or store the counter in CPU registers across reboots.
If we extend the bootcount API with this code we can plug in the functionality seamlessly based on the hardware capabilities.
I have incorporated the rest of your comments on this patch. I moved the access to the TrialStateCtr EFI variable to the bootcount API like you suggested. However, I face an issue, in that on every boot, the bootdelay_process function gets called which increments the bootcount variable. This does not map with the requirement of the FWU feature since the TrialStateCtr variable needs to be updated only when the platform transitions to the Trial State, after a successful update. Once the platform transitions from the Trial State to the Regular State, the variable gets deleted. Hence I think it would be better to use the current logic of the handling of the TrialStateCtr variable. Thanks.
-sughosh
[...]
Regards
/Ilias

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 --- Changes since V6: None
include/fwu.h | 10 ++ lib/Kconfig | 6 + lib/Makefile | 1 + lib/efi_loader/efi_capsule.c | 231 ++++++++++++++++++++++++++++++++++- lib/efi_loader/efi_setup.c | 3 +- lib/fwu_updates/Kconfig | 31 +++++ lib/fwu_updates/Makefile | 3 +- lib/fwu_updates/fwu.c | 26 ++++ 8 files changed, 304 insertions(+), 7 deletions(-) create mode 100644 lib/fwu_updates/Kconfig
diff --git a/include/fwu.h b/include/fwu.h index b374fd1179..7ff1cd75d3 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -32,13 +32,23 @@ struct fwu_mdata_ops { };
#define FWU_MDATA_VERSION 0x1 +#define FWU_IMAGE_ACCEPTED 0x1
#define FWU_MDATA_GUID \ 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) + u8 fwu_update_checks_pass(void); int fwu_boottime_checks(void); +int fwu_trial_state_ctr_start(void);
int fwu_get_mdata(struct fwu_mdata **mdata); int fwu_update_mdata(struct fwu_mdata *mdata); diff --git a/lib/Kconfig b/lib/Kconfig index 7dd777b56a..0e5390597c 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -967,3 +967,9 @@ config LMB_RESERVED_REGIONS memory blocks.
endmenu + +menu "FWU Multi Bank Updates" + +source lib/fwu_updates/Kconfig + +endmenu diff --git a/lib/Makefile b/lib/Makefile index e3deb15287..f2cfd1e428 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_FIT) += libfdt/ diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index a6b98f066a..18a356ba08 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.h> #include <hang.h> #include <malloc.h> #include <mapmem.h> @@ -32,6 +33,17 @@ 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; + +#define FW_ACCEPT_OS (u32)0x8000 + +__maybe_unused static u32 update_index; +__maybe_unused static bool capsule_update; +__maybe_unused static bool fw_accept_os; +static bool image_index_check = true;
#ifdef CONFIG_EFI_CAPSULE_ON_DISK /* for file system access */ @@ -205,7 +217,8 @@ efi_fmp_find(efi_guid_t *image_type, u8 image_index, u64 instance, log_debug("+++ desc[%d] index: %d, name: %ls\n", j, desc->image_index, desc->image_id_name); if (!guidcmp(&desc->image_type_id, image_type) && - (desc->image_index == image_index) && + (!image_index_check || + desc->image_index == image_index) && (!instance || !desc->hardware_instance || desc->hardware_instance == instance)) @@ -388,6 +401,87 @@ efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_s } #endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */
+static bool fwu_empty_capsule(struct efi_capsule_header *capsule) +{ + return !guidcmp(&capsule->capsule_guid, + &fwu_guid_os_request_fw_revert) || + !guidcmp(&capsule->capsule_guid, + &fwu_guid_os_request_fw_accept); +} + +static efi_status_t fwu_empty_capsule_process( + struct efi_capsule_header *capsule) +{ + int status; + u32 active_idx; + efi_status_t ret; + efi_guid_t *image_guid; + + 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(); + 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 + ret = EFI_OUT_OF_RESOURCES; + } else { + ret = EFI_SUCCESS; + log_err("Reverted the FWU active_index. Recommend rebooting the system\n"); + } + } else { + /* + * Image accepted by the OS. Set the acceptance + * status for the image. + */ + image_guid = (void *)(char *)capsule + + capsule->header_size; + + status = fwu_get_active_index(&active_idx); + if (status < 0) { + log_err("Unable to get the active_index from the FWU metadata\n"); + if (status == -ENODEV || + status == -ERANGE || + status == -EIO) + ret = EFI_DEVICE_ERROR; + else if (status == -EINVAL) + ret = EFI_INVALID_PARAMETER; + else + ret = EFI_OUT_OF_RESOURCES; + + return ret; + } + + status = fwu_accept_image(image_guid, active_idx); + if (status < 0) { + log_err("Unable to set the Accept bit for the image %pUs\n", + image_guid); + if (status == -ENODEV || + status == -ERANGE || + status == -EIO) + ret = EFI_DEVICE_ERROR; + else if (status == -EINVAL) + ret = EFI_INVALID_PARAMETER; + else + ret = EFI_OUT_OF_RESOURCES; + } else { + ret = EFI_SUCCESS; + } + } + + return ret; +}
/** * efi_capsule_update_firmware - update firmware from capsule @@ -407,10 +501,42 @@ 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; + + if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) { + if (!fwu_empty_capsule(capsule_data) && + !fwu_update_checks_pass()) { + log_err("FWU checks failed. Cannot start update\n"); + return EFI_INVALID_PARAMETER; + } + + if (fwu_empty_capsule(capsule_data)) { + capsule_update = false; + return fwu_empty_capsule_process(capsule_data); + } else { + capsule_update = true; + } + + /* 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"); + return EFI_DEVICE_ERROR; + } + + fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0; + /* + * For Multi Bank updates, the image index is determined at + * runtime based on the value of the update bank. + */ + image_index_check = false; + }
/* sanity check */ if (capsule_data->header_size < sizeof(*capsule) || @@ -485,8 +611,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 %pUs\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 %pUs\n", + alt_no, &image_type_id); + image_index = alt_no + 1; + } 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, @@ -497,6 +651,38 @@ static efi_status_t efi_capsule_update_firmware( efi_free_pool(abort_reason); goto out; } + + if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) { + if (!fw_accept_os) { + /* + * The OS will not be accepting the firmware + * images. Set the accept bit of all the + * images contained in this capsule. + */ + status = fwu_accept_image(&image_type_id, + update_index); + } else { + status = fwu_clear_accept_image(&image_type_id, + update_index); + } + + if (status < 0) { + log_err("Unable to %s the accept bit for the image %pUs\n", + fw_accept_os ? "clear" : "set", + &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("%s the accepted bit for Image %pUs\n", + fw_accept_os ? "Cleared" : "Set", + &image_type_id); + } + }
out: @@ -1102,8 +1288,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() != EFI_SUCCESS) return EFI_SUCCESS; @@ -1131,12 +1319,14 @@ efi_status_t efi_launch_capsules(void) ret = efi_capsule_read_file(files[i], &capsule); if (ret == EFI_SUCCESS) { ret = efi_capsule_update_firmware(capsule); - if (ret != EFI_SUCCESS) + if (ret != EFI_SUCCESS) { log_err("Applying capsule %ls failed.\n", files[i]); - else + update_status = false; + } else { log_info("Applying capsule %ls succeeded.\n", files[i]); + }
/* create CapsuleXXXX */ set_capsule_result(index, capsule, ret); @@ -1144,6 +1334,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]); @@ -1151,7 +1342,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 FWU metadata\n"); + } + }
for (i = 0; i < nfiles; i++) free(files[i]); diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 492ecf4cb1..5010a0836e 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -350,7 +350,8 @@ efi_status_t efi_init_obj_list(void) goto out;
/* Execute capsules after reboot */ - if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK) && + if (!IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE) && + IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK) && !IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)) ret = efi_launch_capsules(); out: diff --git a/lib/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig new file mode 100644 index 0000000000..6de28e0c9c --- /dev/null +++ b/lib/fwu_updates/Kconfig @@ -0,0 +1,31 @@ +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/fwu_updates/Makefile b/lib/fwu_updates/Makefile index 5a59e4a833..1993088e5b 100644 --- a/lib/fwu_updates/Makefile +++ b/lib/fwu_updates/Makefile @@ -1,6 +1,7 @@ -# SPDX-License-Identifier: GPL-2.0+ +# SPDX-License-Identifier: GPL-2.0-or-later # # Copyright (c) 2022, Linaro Limited #
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 10a0522333..390afe5f6f 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -113,6 +113,32 @@ u8 fwu_update_checks_pass(void) return !trial_state && boottime_check; }
+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; + + trial_state_ctr = ret = 0; + status = efi_set_variable_int(u"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;

Hi Sughosh,
[...]
#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */
+static bool fwu_empty_capsule(struct efi_capsule_header *capsule) +{
return !guidcmp(&capsule->capsule_guid,
&fwu_guid_os_request_fw_revert) ||
!guidcmp(&capsule->capsule_guid,
&fwu_guid_os_request_fw_accept);
+}
+static efi_status_t fwu_empty_capsule_process(
struct efi_capsule_header *capsule)
+{
int status;
u32 active_idx;
efi_status_t ret;
efi_guid_t *image_guid;
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();
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
ret = EFI_OUT_OF_RESOURCES;
In all the case you carry those if statements, define a function like
static efi_status_t fwu_to_efi_error (int err) { switch(err) { case -ENODEV: case -ERANGE: case -EIO: return EFI_DEVICE_ERROR; } ..... } and use it in the error handling below. That should make the weird looking error handling go away and adding more cases in the future a lot easier.
} else {
ret = EFI_SUCCESS;
log_err("Reverted the FWU active_index. Recommend rebooting the system\n");
Does it really need log_err?
[...]
Regards /Ilias

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 --- Changes since V6: None
cmd/Kconfig | 7 +++++ cmd/Makefile | 1 + cmd/fwu_mdata.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 cmd/fwu_mdata.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index d5f842136c..2110f59f6a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -161,6 +161,13 @@ 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" + depends on FWU_MULTI_BANK_UPDATE + default y + 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 5e43a1e022..259a93bc65 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -76,6 +76,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_mdata.o obj-$(CONFIG_CMD_GETTIME) += gettime.o obj-$(CONFIG_CMD_GPIO) += gpio.o obj-$(CONFIG_CMD_HVC) += smccc.o diff --git a/cmd/fwu_mdata.c b/cmd/fwu_mdata.c new file mode 100644 index 0000000000..ee9d035374 --- /dev/null +++ b/cmd/fwu_mdata.c @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022, Linaro Limited + */ + +#include <command.h> +#include <dm.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <stdio.h> +#include <stdlib.h> + +#include <linux/types.h> + +static void print_mdata(struct fwu_mdata *mdata) +{ + int i, j; + struct fwu_image_entry *img_entry; + struct fwu_image_bank_info *img_info; + + printf("\tFWU Metadata\n"); + printf("crc32: %#x\n", mdata->crc32); + printf("version: %#x\n", mdata->version); + printf("active_index: %#x\n", mdata->active_index); + printf("previous_active_index: %#x\n", mdata->previous_active_index); + + printf("\tImage Info\n"); + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { + img_entry = &mdata->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 < CONFIG_FWU_NUM_BANKS; j++) { + img_info = &img_entry->img_bank_info[j]; + printf("Image Guid: %pUL\n", &img_info->image_uuid); + printf("Image Acceptance: %s\n", + img_info->accepted == 0x1 ? "yes" : "no"); + } + } +} + +int do_fwu_mdata_read(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct udevice *dev; + int ret = CMD_RET_SUCCESS, res; + struct fwu_mdata *mdata = NULL; + + if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) { + log_err("Unable to get FWU metadata device\n"); + return CMD_RET_FAILURE; + } + + res = fwu_mdata_check(); + if (res < 0) { + log_err("FWU Metadata check failed\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + res = fwu_get_mdata(&mdata); + if (res < 0) { + log_err("Unable to get valid FWU metadata\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + print_mdata(mdata); + +out: + free(mdata); + return ret; +} + +U_BOOT_CMD( + fwu_mdata_read, 1, 1, do_fwu_mdata_read, + "Read and print FWU metadata", + "" +);

On Thu, 14 Jul 2022 at 21:40, Sughosh Ganu sughosh.ganu@linaro.org wrote:
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
Changes since V6: None
cmd/Kconfig | 7 +++++ cmd/Makefile | 1 + cmd/fwu_mdata.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 cmd/fwu_mdata.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index d5f842136c..2110f59f6a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -161,6 +161,13 @@ 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"
depends on FWU_MULTI_BANK_UPDATE
default y
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 5e43a1e022..259a93bc65 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -76,6 +76,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_mdata.o obj-$(CONFIG_CMD_GETTIME) += gettime.o obj-$(CONFIG_CMD_GPIO) += gpio.o obj-$(CONFIG_CMD_HVC) += smccc.o diff --git a/cmd/fwu_mdata.c b/cmd/fwu_mdata.c new file mode 100644 index 0000000000..ee9d035374 --- /dev/null +++ b/cmd/fwu_mdata.c @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/*
- Copyright (c) 2022, Linaro Limited
- */
+#include <command.h> +#include <dm.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <stdio.h> +#include <stdlib.h>
+#include <linux/types.h>
+static void print_mdata(struct fwu_mdata *mdata) +{
int i, j;
struct fwu_image_entry *img_entry;
struct fwu_image_bank_info *img_info;
printf("\tFWU Metadata\n");
printf("crc32: %#x\n", mdata->crc32);
printf("version: %#x\n", mdata->version);
printf("active_index: %#x\n", mdata->active_index);
printf("previous_active_index: %#x\n", mdata->previous_active_index);
printf("\tImage Info\n");
for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
img_entry = &mdata->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 < CONFIG_FWU_NUM_BANKS; j++) {
img_info = &img_entry->img_bank_info[j];
printf("Image Guid: %pUL\n", &img_info->image_uuid);
printf("Image Acceptance: %s\n",
img_info->accepted == 0x1 ? "yes" : "no");
}
}
+}
+int do_fwu_mdata_read(struct cmd_tbl *cmdtp, int flag,
int argc, char * const argv[])
+{
struct udevice *dev;
int ret = CMD_RET_SUCCESS, res;
struct fwu_mdata *mdata = NULL;
if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) {
log_err("Unable to get FWU metadata device\n");
return CMD_RET_FAILURE;
}
res = fwu_mdata_check();
if (res < 0) {
log_err("FWU Metadata check failed\n");
ret = CMD_RET_FAILURE;
goto out;
}
res = fwu_get_mdata(&mdata);
if (res < 0) {
log_err("Unable to get valid FWU metadata\n");
ret = CMD_RET_FAILURE;
goto out;
}
print_mdata(mdata);
+out:
free(mdata);
return ret;
+}
+U_BOOT_CMD(
fwu_mdata_read, 1, 1, do_fwu_mdata_read,
"Read and print FWU metadata",
""
+);
2.34.1
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org

The Dependable Boot specification[1] describes the structure of the firmware accept and revert capsules. These are empty capsules which are used for signalling the acceptance or rejection of the updated firmware by the OS. Add support for generating these empty capsules.
[1] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e...
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V6: None
doc/mkeficapsule.1 | 29 ++++++++++---- tools/eficapsule.h | 8 ++++ tools/mkeficapsule.c | 92 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 115 insertions(+), 14 deletions(-)
diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1 index 09bdc24295..77ca061efd 100644 --- a/doc/mkeficapsule.1 +++ b/doc/mkeficapsule.1 @@ -8,7 +8,7 @@ mkeficapsule - Generate EFI capsule file for U-Boot
.SH SYNOPSIS .B mkeficapsule -.RI [ options "] " image-blob " " capsule-file +.RI [ options ] " " [ image-blob ] " " capsule-file
.SH "DESCRIPTION" .B mkeficapsule @@ -23,8 +23,13 @@ Optionally, a capsule file can be signed with a given private key. In this case, the update will be authenticated by verifying the signature before applying.
+Additionally, an empty capsule file can be generated for acceptance or +rejection of firmware images by a governing component like an Operating +System. The empty capsules do not require an image-blob input file. + + .B mkeficapsule -takes any type of image files, including: +takes any type of image files when generating non empty capsules, including: .TP .I raw image format is a single binary blob of any type of firmware. @@ -36,18 +41,16 @@ multiple binary blobs in a single capsule file. This type of image file can be generated by .BR mkimage .
-.PP -If you want to use other types than above two, you should explicitly -specify a guid for the FMP driver. - .SH "OPTIONS" + .TP .BI "-g\fR,\fB --guid " guid-string Specify guid for image blob type. The format is: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
The first three elements are in little endian, while the rest -is in big endian. +is in big endian. The option must be specified for all non empty and +image acceptance capsules
.TP .BI "-i\fR,\fB --index " index @@ -57,6 +60,18 @@ Specify an image index .BI "-I\fR,\fB --instance " instance Specify a hardware instance
+.PP +For generation of firmware accept empty capsule +.BR --guid +is mandatory +.TP +.BI "-A\fR,\fB --fw-accept " +Generate a firmware acceptance empty capsule + +.TP +.BI "-R\fR,\fB --fw-revert " +Generate a firmware revert empty capsule + .TP .BR -h ", " --help Print a help message diff --git a/tools/eficapsule.h b/tools/eficapsule.h index d63b831443..072a4b5598 100644 --- a/tools/eficapsule.h +++ b/tools/eficapsule.h @@ -41,6 +41,14 @@ typedef struct { EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \ 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
+#define FW_ACCEPT_OS_GUID \ + EFI_GUID(0x0c996046, 0xbcc0, 0x4d04, 0x85, 0xec, \ + 0xe1, 0xfc, 0xed, 0xf1, 0xc6, 0xf8) + +#define FW_REVERT_OS_GUID \ + EFI_GUID(0xacd58b4b, 0xc0e8, 0x475f, 0x99, 0xb5, \ + 0x6b, 0x3f, 0x7e, 0x07, 0xaa, 0xf0) + /* flags */ #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index 5f74d23b9e..244c80e1f7 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,13 @@ static const char *tool_name = "mkeficapsule"; efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
-static const char *opts_short = "g:i:I:v:p:c:m:dh"; +static const char *opts_short = "g:i:I:v:p:c:m:dhAR"; + +enum { + CAPSULE_NORMAL_BLOB = 0, + CAPSULE_ACCEPT, + CAPSULE_REVERT, +} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,6 +45,8 @@ static struct option options[] = { {"certificate", required_argument, NULL, 'c'}, {"monotonic-count", required_argument, NULL, 'm'}, {"dump-sig", no_argument, NULL, 'd'}, + {"fw-accept", no_argument, NULL, 'A'}, + {"fw-revert", no_argument, NULL, 'R'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, }; @@ -55,6 +63,8 @@ static void print_usage(void) "\t-c, --certificate <cert file> signer's certificate file\n" "\t-m, --monotonic-count <count> monotonic count\n" "\t-d, --dump_sig dump signature (*.p7)\n" + "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n" + "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n" "\t-h, --help print a help message\n", tool_name); } @@ -564,6 +574,49 @@ void convert_uuid_to_guid(unsigned char *buf) buf[7] = c; }
+static int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept) +{ + struct efi_capsule_header header = { 0 }; + FILE *f = NULL; + int ret = -1; + efi_guid_t fw_accept_guid = FW_ACCEPT_OS_GUID; + efi_guid_t fw_revert_guid = FW_REVERT_OS_GUID; + efi_guid_t capsule_guid; + + f = fopen(path, "w"); + if (!f) { + fprintf(stderr, "cannot open %s\n", path); + goto err; + } + + capsule_guid = fw_accept ? fw_accept_guid : fw_revert_guid; + + memcpy(&header.capsule_guid, &capsule_guid, sizeof(efi_guid_t)); + header.header_size = sizeof(header); + header.flags = 0; + + header.capsule_image_size = fw_accept ? + sizeof(header) + sizeof(efi_guid_t) : sizeof(header); + + if (write_capsule_file(f, &header, sizeof(header), + "Capsule header")) + goto err; + + if (fw_accept) { + if (write_capsule_file(f, guid, sizeof(*guid), + "FW Accept Capsule Payload")) + goto err; + } + + ret = 0; + +err: + if (f) + fclose(f); + + return ret; +} + /** * main - main entry function of mkeficapsule * @argc: Number of arguments @@ -592,6 +645,7 @@ int main(int argc, char **argv) privkey_file = NULL; cert_file = NULL; dump_sig = 0; + capsule_type = CAPSULE_NORMAL_BLOB; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) @@ -639,22 +693,46 @@ int main(int argc, char **argv) case 'd': dump_sig = 1; break; - case 'h': + case 'A': + capsule_type |= CAPSULE_ACCEPT; + break; + case 'R': + capsule_type |= CAPSULE_REVERT; + break; + default: print_usage(); exit(EXIT_SUCCESS); } }
+ if (capsule_type == (CAPSULE_ACCEPT | CAPSULE_REVERT)) { + fprintf(stderr, + "Select either of Accept or Revert capsule generation\n"); + exit(EXIT_FAILURE); + } + /* check necessary parameters */ - if ((argc != optind + 2) || !guid || - ((privkey_file && !cert_file) || - (!privkey_file && cert_file))) { + if ((capsule_type == CAPSULE_NORMAL_BLOB && + ((argc != optind + 2) || !guid || + ((privkey_file && !cert_file) || + (!privkey_file && cert_file)))) || + (capsule_type != CAPSULE_NORMAL_BLOB && + ((argc != optind + 1) || + ((capsule_type == CAPSULE_ACCEPT) && !guid) || + ((capsule_type == CAPSULE_REVERT) && guid)))) { print_usage(); exit(EXIT_FAILURE); }
- if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, index, instance, - mcount, privkey_file, cert_file) < 0) { + if (capsule_type != CAPSULE_NORMAL_BLOB) { + if (create_empty_capsule(argv[argc - 1], guid, + capsule_type == CAPSULE_ACCEPT) < 0) { + fprintf(stderr, "Creating empty capsule failed\n"); + exit(EXIT_FAILURE); + } + } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, + index, instance, mcount, privkey_file, + cert_file) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }

Hi Sughosh,
On Thu, 14 Jul 2022 at 21:40, Sughosh Ganu sughosh.ganu@linaro.org wrote:
The Dependable Boot specification[1] describes the structure of the firmware accept and revert capsules. These are empty capsules which are used for signalling the acceptance or rejection of the updated firmware by the OS. Add support for generating these empty capsules.
[1] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e...
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
[...]
capsule_type = CAPSULE_NORMAL_BLOB; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1)
@@ -639,22 +693,46 @@ int main(int argc, char **argv) case 'd': dump_sig = 1; break;
case 'h':
case 'A':
capsule_type |= CAPSULE_ACCEPT;
break;
case 'R':
capsule_type |= CAPSULE_REVERT;
break;
capsule_type will be set to CAPSULE_NORMAL_BLOB. I think it's better to have case 'A': if (capsule_type) //error out mentioned A and R are mutually exclusive capsule_type = CAPSULE_ACCEPT; break; case 'R': if (capsule_type) //error out mentioned A and R are mutually exclusive capsule_type |= CAPSULE_REVERT; break;
default: print_usage(); exit(EXIT_SUCCESS); } }
if (capsule_type == (CAPSULE_ACCEPT | CAPSULE_REVERT)) {
fprintf(stderr,
"Select either of Accept or Revert capsule generation\n");
exit(EXIT_FAILURE);
}
And get rid of this if
[...]
Regards /Ilias

Add support for setting OEM flags in the capsule header. As per the UEFI specification, bits 0-15 of the flags member of the capsule header can be defined per capsule GUID.
The oemflags will be used for the FWU Multi Bank update feature, as specified by the Dependable Boot specification[1]. Bit 15 of the flags member will be used to determine if the acceptance/rejection of the updated images is to be done by the firmware or an external component like the OS.
[1] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e...
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V6: None
doc/mkeficapsule.1 | 4 ++++ tools/mkeficapsule.c | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1 index 77ca061efd..6fb2dd0810 100644 --- a/doc/mkeficapsule.1 +++ b/doc/mkeficapsule.1 @@ -72,6 +72,10 @@ Generate a firmware acceptance empty capsule .BI "-R\fR,\fB --fw-revert " Generate a firmware revert empty capsule
+.TP +.BI "-o\fR,\fB --capoemflag " +Capsule OEM flag, value between 0x0000 to 0xffff + .TP .BR -h ", " --help Print a help message diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index 244c80e1f7..237c1218fd 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ static const char *tool_name = "mkeficapsule"; efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
-static const char *opts_short = "g:i:I:v:p:c:m:dhAR"; +static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -47,6 +47,7 @@ static struct option options[] = { {"dump-sig", no_argument, NULL, 'd'}, {"fw-accept", no_argument, NULL, 'A'}, {"fw-revert", no_argument, NULL, 'R'}, + {"capoemflag", required_argument, NULL, 'o'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, }; @@ -65,6 +66,7 @@ static void print_usage(void) "\t-d, --dump_sig dump signature (*.p7)\n" "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n" "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n" + "\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n" "\t-h, --help print a help message\n", tool_name); } @@ -387,6 +389,7 @@ static void free_sig_data(struct auth_context *ctx) * @mcount: Monotonic count in authentication information * @private_file: Path to a private key file * @cert_file: Path to a certificate file + * @oemflags: Capsule OEM Flags, bits 0-15 * * This function actually does the job of creating an uefi capsule file. * All the arguments must be supplied. @@ -399,7 +402,8 @@ static void free_sig_data(struct auth_context *ctx) */ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, unsigned long index, unsigned long instance, - uint64_t mcount, char *privkey_file, char *cert_file) + uint64_t mcount, char *privkey_file, char *cert_file, + uint16_t oemflags) { struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; @@ -464,6 +468,8 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */ header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; + if (oemflags) + header.flags |= oemflags; header.capsule_image_size = sizeof(header) + sizeof(capsule) + sizeof(uint64_t) + sizeof(image) @@ -635,6 +641,7 @@ int main(int argc, char **argv) unsigned char uuid_buf[16]; unsigned long index, instance; uint64_t mcount; + uint16_t oemflags; char *privkey_file, *cert_file; int c, idx;
@@ -646,6 +653,7 @@ int main(int argc, char **argv) cert_file = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB; + oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) @@ -699,6 +707,9 @@ int main(int argc, char **argv) case 'R': capsule_type |= CAPSULE_REVERT; break; + case 'o': + oemflags = strtoul(optarg, NULL, 0); + break; default: print_usage(); exit(EXIT_SUCCESS); @@ -732,7 +743,7 @@ int main(int argc, char **argv) } } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, index, instance, mcount, privkey_file, - cert_file) < 0) { + cert_file, oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }

Hi Sughosh,
On Thu, 14 Jul 2022 at 21:40, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add support for setting OEM flags in the capsule header. As per the UEFI specification, bits 0-15 of the flags member of the capsule header can be defined per capsule GUID.
The oemflags will be used for the FWU Multi Bank update feature, as specified by the Dependable Boot specification[1]. Bit 15 of the flags member will be used to determine if the acceptance/rejection of the updated images is to be done by the firmware or an external component like the OS.
Have we documented bit15 in the documentation? If not please add it.
[1] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e...
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V6: None
doc/mkeficapsule.1 | 4 ++++ tools/mkeficapsule.c | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1 index 77ca061efd..6fb2dd0810 100644 --- a/doc/mkeficapsule.1 +++ b/doc/mkeficapsule.1 @@ -72,6 +72,10 @@ Generate a firmware acceptance empty capsule .BI "-R\fR,\fB --fw-revert " Generate a firmware revert empty capsule
+.TP +.BI "-o\fR,\fB --capoemflag " +Capsule OEM flag, value between 0x0000 to 0xffff
.TP .BR -h ", " --help Print a help message diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index 244c80e1f7..237c1218fd 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ static const char *tool_name = "mkeficapsule"; efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
-static const char *opts_short = "g:i:I:v:p:c:m:dhAR"; +static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -47,6 +47,7 @@ static struct option options[] = { {"dump-sig", no_argument, NULL, 'd'}, {"fw-accept", no_argument, NULL, 'A'}, {"fw-revert", no_argument, NULL, 'R'},
{"capoemflag", required_argument, NULL, 'o'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0},
}; @@ -65,6 +66,7 @@ static void print_usage(void) "\t-d, --dump_sig dump signature (*.p7)\n" "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n" "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n"
"\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n" "\t-h, --help print a help message\n", tool_name);
} @@ -387,6 +389,7 @@ static void free_sig_data(struct auth_context *ctx)
- @mcount: Monotonic count in authentication information
- @private_file: Path to a private key file
- @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- This function actually does the job of creating an uefi capsule file.
- All the arguments must be supplied.
@@ -399,7 +402,8 @@ static void free_sig_data(struct auth_context *ctx) */ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, unsigned long index, unsigned long instance,
uint64_t mcount, char *privkey_file, char *cert_file)
uint64_t mcount, char *privkey_file, char *cert_file,
uint16_t oemflags)
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; @@ -464,6 +468,8 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */ header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
if (oemflags)
header.flags |= oemflags; header.capsule_image_size = sizeof(header) + sizeof(capsule) + sizeof(uint64_t) + sizeof(image)
@@ -635,6 +641,7 @@ int main(int argc, char **argv) unsigned char uuid_buf[16]; unsigned long index, instance; uint64_t mcount;
uint16_t oemflags; char *privkey_file, *cert_file; int c, idx;
@@ -646,6 +653,7 @@ int main(int argc, char **argv) cert_file = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB;
oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1)
@@ -699,6 +707,9 @@ int main(int argc, char **argv) case 'R': capsule_type |= CAPSULE_REVERT; break;
case 'o':
oemflags = strtoul(optarg, NULL, 0);
break; default: print_usage(); exit(EXIT_SUCCESS);
@@ -732,7 +743,7 @@ int main(int argc, char **argv) } } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, index, instance, mcount, privkey_file,
cert_file) < 0) {
cert_file, oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }
-- 2.34.1
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org

hi Ilias,
On Fri, 15 Jul 2022 at 22:11, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Sughosh,
On Thu, 14 Jul 2022 at 21:40, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add support for setting OEM flags in the capsule header. As per the UEFI specification, bits 0-15 of the flags member of the capsule header can be defined per capsule GUID.
The oemflags will be used for the FWU Multi Bank update feature, as specified by the Dependable Boot specification[1]. Bit 15 of the flags member will be used to determine if the acceptance/rejection of the updated images is to be done by the firmware or an external component like the OS.
Have we documented bit15 in the documentation? If not please add it.
Yes, the use of bit15 has been added in the documentation for this feature. I will incorporate all your review comments, and in case I hit any issues while incorporating any review comment, I will respond to that. Thanks for the review.
-sughosh
[1] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e...
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V6: None
doc/mkeficapsule.1 | 4 ++++ tools/mkeficapsule.c | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1 index 77ca061efd..6fb2dd0810 100644 --- a/doc/mkeficapsule.1 +++ b/doc/mkeficapsule.1 @@ -72,6 +72,10 @@ Generate a firmware acceptance empty capsule .BI "-R\fR,\fB --fw-revert " Generate a firmware revert empty capsule
+.TP +.BI "-o\fR,\fB --capoemflag " +Capsule OEM flag, value between 0x0000 to 0xffff
.TP .BR -h ", " --help Print a help message diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index 244c80e1f7..237c1218fd 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,7 @@ static const char *tool_name = "mkeficapsule"; efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
-static const char *opts_short = "g:i:I:v:p:c:m:dhAR"; +static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR";
enum { CAPSULE_NORMAL_BLOB = 0, @@ -47,6 +47,7 @@ static struct option options[] = { {"dump-sig", no_argument, NULL, 'd'}, {"fw-accept", no_argument, NULL, 'A'}, {"fw-revert", no_argument, NULL, 'R'},
{"capoemflag", required_argument, NULL, 'o'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0},
}; @@ -65,6 +66,7 @@ static void print_usage(void) "\t-d, --dump_sig dump signature (*.p7)\n" "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n" "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n"
"\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n" "\t-h, --help print a help message\n", tool_name);
} @@ -387,6 +389,7 @@ static void free_sig_data(struct auth_context *ctx)
- @mcount: Monotonic count in authentication information
- @private_file: Path to a private key file
- @cert_file: Path to a certificate file
- @oemflags: Capsule OEM Flags, bits 0-15
- This function actually does the job of creating an uefi capsule file.
- All the arguments must be supplied.
@@ -399,7 +402,8 @@ static void free_sig_data(struct auth_context *ctx) */ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, unsigned long index, unsigned long instance,
uint64_t mcount, char *privkey_file, char *cert_file)
uint64_t mcount, char *privkey_file, char *cert_file,
uint16_t oemflags)
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; @@ -464,6 +468,8 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */ header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
if (oemflags)
header.flags |= oemflags; header.capsule_image_size = sizeof(header) + sizeof(capsule) + sizeof(uint64_t) + sizeof(image)
@@ -635,6 +641,7 @@ int main(int argc, char **argv) unsigned char uuid_buf[16]; unsigned long index, instance; uint64_t mcount;
uint16_t oemflags; char *privkey_file, *cert_file; int c, idx;
@@ -646,6 +653,7 @@ int main(int argc, char **argv) cert_file = NULL; dump_sig = 0; capsule_type = CAPSULE_NORMAL_BLOB;
oemflags = 0; for (;;) { c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1)
@@ -699,6 +707,9 @@ int main(int argc, char **argv) case 'R': capsule_type |= CAPSULE_REVERT; break;
case 'o':
oemflags = strtoul(optarg, NULL, 0);
break; default: print_usage(); exit(EXIT_SUCCESS);
@@ -732,7 +743,7 @@ int main(int argc, char **argv) } } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, index, instance, mcount, privkey_file,
cert_file) < 0) {
cert_file, oemflags) < 0) { fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }
-- 2.34.1
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org

Add documentattion for the FWU Multi Bank Update feature. The document describes the steps needed for setting up the platform for the feature, as well as steps for enabling the feature on the platform.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V6: None
doc/develop/uefi/fwu_updates.rst | 156 +++++++++++++++++++++++++++++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + 3 files changed, 159 insertions(+) create mode 100644 doc/develop/uefi/fwu_updates.rst
diff --git a/doc/develop/uefi/fwu_updates.rst b/doc/develop/uefi/fwu_updates.rst new file mode 100644 index 0000000000..57d1b5b703 --- /dev/null +++ b/doc/develop/uefi/fwu_updates.rst @@ -0,0 +1,156 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (c) 2022 Linaro Limited + +FWU Multi Bank Updates in U-Boot +================================ + +The FWU Multi Bank Update feature implements the firmware update +mechanism described in the PSA Firmware Update for A-profile Arm +Architecture specification[1]. Certain aspects of the Dependable +Boot specification[2] are also implemented. The feature provides a +mechanism to have multiple banks of updatable firmware images and for +updating the firmware images on the non-booted bank. On a successful +update, the platform boots from the updated bank on subsequent +boot. The UEFI capsule-on-disk update feature is used for performing +the actual updates of the updatable firmware images. + +The bookkeeping of the updatable images is done through a structure +called metadata. Currently, the FWU metadata supports identification +of images based on image GUIDs stored on a GPT partitioned storage +media. There are plans to extend the metadata structure for non GPT +partitioned devices as well. + +Accessing the FWU metadata is done through generic API's which are +defined in a driver which complies with the u-boot's driver model. A +new uclass UCLASS_FWU_MDATA has been added for accessing the FWU +metadata. Individual drivers can be added based on the type of storage +media, and it's partitioning method. Details of the storage device +containing the FWU metadata partitions are specified through a U-Boot +specific device tree property `fwu-mdata-store`. Please refer to +U-Boot `doc <doc/device-tree-bindings/firmware/fwu-mdata.txt>`__ for +the device tree bindings. + +Enabling the FWU Multi Bank Update feature +------------------------------------------ + +The feature can be enabled by specifying the following configs:: + + CONFIG_EFI_CAPSULE_ON_DISK=y + CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT=y + CONFIG_EFI_CAPSULE_FIRMWARE=y + CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y + + CONFIG_FWU_MULTI_BANK_UPDATE=y + CONFIG_CMD_FWU_METADATA=y + CONFIG_DM_FWU_MDATA=y + CONFIG_FWU_MDATA_GPT_BLK=y + CONFIG_FWU_NUM_BANKS=<val> + CONFIG_FWU_NUM_IMAGES_PER_BANK=<val> + +in the .config file + +The first group of configs enable the UEFI capsule-on-disk update +functionality. The second group of configs enable the FWU Multi Bank +Update functionality. Please refer to the section +:ref:`uefi_capsule_update_ref` for more details on generation of the +UEFI capsule. + +Setting up the device for GPT partitioned storage +------------------------------------------------- + +Before enabling the functionality in U-Boot, certain changes are +required to be done on the storage device. Assuming a GPT partitioned +storage device, the storage media needs to be partitioned with the +correct number of partitions, given the number of banks and number of +images per bank that the platform is going to support. Each updatable +firmware image will be stored on an separate partition. In addition, +the two copies of the FWU metadata will be stored on two separate +partitions. + +As an example, a platform supporting two banks with each bank +containing three images would need to have 2 * 3 = 6 parititions plus +the two metadata partitions, or 8 partitions. In addition the storage +media can have additional partitions of non-updatable images, like the +EFI System Partition(ESP), a partition for the root file system etc. + +When generating the partitions, a few aspects need to be taken care +of. Each GPT partition entry in the GPT header has two GUIDs:: + + *PartitionTypeGUID* + *UniquePartitionGUID* + +The PartitionTypeGUID value should correspond to the *image_type_uuid* +field of the FWU metadata. This field is used to identify a given type +of updatable firmware image, e.g. u-boot, op-tee, FIP etc. This GUID +should also be used for specifying the `--guid` parameter when +generating the capsule. + +The UniquePartitionGUID value should correspond to the *image_uuid* +field in the FWU metadata. This GUID is used to identify images of a +given image type in different banks. + +Similarly, the FWU specifications defines the GUID value to be used +for the metadata partitions. This would be the PartitionTypeGUID for +the metadata partitions. + +When generating the metadata, the *image_type_uuid* and the +*image_uuid* values should match the *PartitionTypeGUID* and the +*UniquePartitionGUID* values respectively. + +Performing the Update +--------------------- + +Once the storage media has been partitioned and populated with the +metadata partitions, the UEFI capsule-on-disk update functionality can +be used for performing the update. Refer to the section +:ref:`uefi_capsule_update_ref` for details on how the update can be +invoked. + +On a successful update, the FWU metadata gets updated to reflect the +bank from which the platform would be booting on subsequent boot. + +Based on the value of bit15 of the Flags member of the capsule header, +the updated images would either be accepted by the u-boot's UEFI +implementation, or by the Operating System. If the Operating System is +accepting the firmware images, it does so by generating an empty +*accept* capsule. The Operating System can also reject the updated +firmware by generating a *revert* capsule. The empty capsule can be +applied by using the exact same procedure used for performing the +capsule-on-disk update. + +The task of accepting the different firmware images, post an update +may be done by multiple, separate components in the Operating +System. To help identify the firmware image that is being accepted, +the accept capsule passes the image GUID of the firmware image being +accepted. The relevant code in u-boot then sets the Accept bit of the +corresponding firmware image for which the accept capsule was +found. Only when all the firmware components in a bank have been +accepted does the platform transition to the regular state from trial +state. + +The revert capsule on the other hand does not pass any image GUID, +since reverting any image of the bank has the same result of the +platform booting from the other bank on subsequent boot. + +Generating an empty capsule +--------------------------- + +The empty capsule can be generated using the mkeficapsule utility. To +build the tool, enable:: + + CONFIG_TOOLS_MKEFICAPSULE=y + +Run the following commands to generate the accept/revert capsules:: + +.. code-block:: bash + + $ ./tools/mkeficapsule \ + [--fw-accept --guid <image guid>] | \ + [--fw-revert] \ + <capsule_file_name> + +Links +----- + +* [1] https://developer.arm.com/documentation/den0118/a/ - FWU Specification +* [2] https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e... - Dependable Boot Specification diff --git a/doc/develop/uefi/index.rst b/doc/develop/uefi/index.rst index 7e65dbc5d5..e26b1fbe05 100644 --- a/doc/develop/uefi/index.rst +++ b/doc/develop/uefi/index.rst @@ -13,3 +13,4 @@ can be run an UEFI payload. uefi.rst u-boot_on_efi.rst iscsi.rst + fwu_updates.rst diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index 941e427093..536b278dd9 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -277,6 +277,8 @@ Enable ``CONFIG_OPTEE``, ``CONFIG_CMD_OPTEE_RPMB`` and ``CONFIG_EFI_MM_COMM_TEE`
[1] https://optee.readthedocs.io/en/latest/building/efi_vars/stmm.html
+.. _uefi_capsule_update_ref: + Enabling UEFI Capsule Update feature ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

On 7/14/22 20:39, Sughosh Ganu wrote:
Add documentattion for the FWU Multi Bank Update feature. The document describes the steps needed for setting up the platform for the feature, as well as steps for enabling the feature on the platform.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
Changes since V6: None
doc/develop/uefi/fwu_updates.rst | 156 +++++++++++++++++++++++++++++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + 3 files changed, 159 insertions(+) create mode 100644 doc/develop/uefi/fwu_updates.rst
diff --git a/doc/develop/uefi/fwu_updates.rst b/doc/develop/uefi/fwu_updates.rst new file mode 100644 index 0000000000..57d1b5b703 --- /dev/null +++ b/doc/develop/uefi/fwu_updates.rst @@ -0,0 +1,156 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (c) 2022 Linaro Limited
+FWU Multi Bank Updates in U-Boot +================================
+The FWU Multi Bank Update feature implements the firmware update +mechanism described in the PSA Firmware Update for A-profile Arm +Architecture specification[1]. Certain aspects of the Dependable
Thanks a lot for the extensive documentation.
%s/[1]/ [1]/
+Boot specification[2] are also implemented. The feature provides a
ditto
+mechanism to have multiple banks of updatable firmware images and for +updating the firmware images on the non-booted bank. On a successful +update, the platform boots from the updated bank on subsequent +boot. The UEFI capsule-on-disk update feature is used for performing +the actual updates of the updatable firmware images.
+The bookkeeping of the updatable images is done through a structure +called metadata. Currently, the FWU metadata supports identification +of images based on image GUIDs stored on a GPT partitioned storage +media. There are plans to extend the metadata structure for non GPT +partitioned devices as well.
+Accessing the FWU metadata is done through generic API's which are +defined in a driver which complies with the u-boot's driver model. A +new uclass UCLASS_FWU_MDATA has been added for accessing the FWU +metadata. Individual drivers can be added based on the type of storage +media, and it's partitioning method. Details of the storage device +containing the FWU metadata partitions are specified through a U-Boot +specific device tree property `fwu-mdata-store`. Please refer to +U-Boot `doc <doc/device-tree-bindings/firmware/fwu-mdata.txt>`__ for +the device tree bindings.
+Enabling the FWU Multi Bank Update feature +------------------------------------------
+The feature can be enabled by specifying the following configs::
- CONFIG_EFI_CAPSULE_ON_DISK=y
- CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT=y
- CONFIG_EFI_CAPSULE_FIRMWARE=y
- CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
- CONFIG_FWU_MULTI_BANK_UPDATE=y
- CONFIG_CMD_FWU_METADATA=y
- CONFIG_DM_FWU_MDATA=y
- CONFIG_FWU_MDATA_GPT_BLK=y
- CONFIG_FWU_NUM_BANKS=<val>
- CONFIG_FWU_NUM_IMAGES_PER_BANK=<val>
+in the .config file
+The first group of configs enable the UEFI capsule-on-disk update
%s/configs/configuration settings/
+functionality. The second group of configs enable the FWU Multi Bank +Update functionality. Please refer to the section +:ref:`uefi_capsule_update_ref` for more details on generation of the +UEFI capsule.
+Setting up the device for GPT partitioned storage +-------------------------------------------------
+Before enabling the functionality in U-Boot, certain changes are +required to be done on the storage device. Assuming a GPT partitioned
A GPT partitioned storage device is required.
+storage device, the storage media needs to be partitioned with the +correct number of partitions, given the number of banks and number of +images per bank that the platform is going to support. Each updatable +firmware image will be stored on an separate partition. In addition, +the two copies of the FWU metadata will be stored on two separate +partitions.
+As an example, a platform supporting two banks with each bank +containing three images would need to have 2 * 3 = 6 parititions plus
%s/parititions/partitions/
Please, provide an example:
* meta data 1 * U-Boot 1 * ESP 1 * Operating system image 1 * meta data 2 * U-Boot 2 * ESP 2 * Operating system image 2
+the two metadata partitions, or 8 partitions. In addition the storage +media can have additional partitions of non-updatable images, like the +EFI System Partition(ESP), a partition for the root file system etc.
Couldn't the ESP and the root file system also be updated by FWU for instance to switch to a new GRUB version or a new kernel?
+When generating the partitions, a few aspects need to be taken care +of. Each GPT partition entry in the GPT header has two GUIDs::
- *PartitionTypeGUID*
- *UniquePartitionGUID*
Please, use a bullet list here.
+The PartitionTypeGUID value should correspond to the *image_type_uuid*
You could use double backquotes to show variable names as code.
E.g. this is a ``variable``.
+field of the FWU metadata. This field is used to identify a given type +of updatable firmware image, e.g. u-boot, op-tee, FIP etc. This GUID
%s/u-boot/U-Boot/ $s/op-tee/OP-TEE/
Best regards
Heinrich
+should also be used for specifying the `--guid` parameter when +generating the capsule.
+The UniquePartitionGUID value should correspond to the *image_uuid* +field in the FWU metadata. This GUID is used to identify images of a +given image type in different banks.
+Similarly, the FWU specifications defines the GUID value to be used +for the metadata partitions. This would be the PartitionTypeGUID for +the metadata partitions.
+When generating the metadata, the *image_type_uuid* and the +*image_uuid* values should match the *PartitionTypeGUID* and the +*UniquePartitionGUID* values respectively.
+Performing the Update +---------------------
+Once the storage media has been partitioned and populated with the +metadata partitions, the UEFI capsule-on-disk update functionality can +be used for performing the update. Refer to the section +:ref:`uefi_capsule_update_ref` for details on how the update can be +invoked.
+On a successful update, the FWU metadata gets updated to reflect the +bank from which the platform would be booting on subsequent boot.
+Based on the value of bit15 of the Flags member of the capsule header, +the updated images would either be accepted by the u-boot's UEFI +implementation, or by the Operating System. If the Operating System is +accepting the firmware images, it does so by generating an empty +*accept* capsule. The Operating System can also reject the updated +firmware by generating a *revert* capsule. The empty capsule can be +applied by using the exact same procedure used for performing the +capsule-on-disk update.
+The task of accepting the different firmware images, post an update +may be done by multiple, separate components in the Operating +System. To help identify the firmware image that is being accepted, +the accept capsule passes the image GUID of the firmware image being +accepted. The relevant code in u-boot then sets the Accept bit of the +corresponding firmware image for which the accept capsule was +found. Only when all the firmware components in a bank have been +accepted does the platform transition to the regular state from trial +state.
+The revert capsule on the other hand does not pass any image GUID, +since reverting any image of the bank has the same result of the +platform booting from the other bank on subsequent boot.
+Generating an empty capsule +---------------------------
+The empty capsule can be generated using the mkeficapsule utility. To +build the tool, enable::
- CONFIG_TOOLS_MKEFICAPSULE=y
+Run the following commands to generate the accept/revert capsules::
+.. code-block:: bash
- $ ./tools/mkeficapsule \
[--fw-accept --guid <image guid>] | \
[--fw-revert] \
<capsule_file_name>
+Links +-----
+* [1] https://developer.arm.com/documentation/den0118/a/ - FWU Specification +* [2] https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e... - Dependable Boot Specification diff --git a/doc/develop/uefi/index.rst b/doc/develop/uefi/index.rst index 7e65dbc5d5..e26b1fbe05 100644 --- a/doc/develop/uefi/index.rst +++ b/doc/develop/uefi/index.rst @@ -13,3 +13,4 @@ can be run an UEFI payload. uefi.rst u-boot_on_efi.rst iscsi.rst
- fwu_updates.rst
diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index 941e427093..536b278dd9 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -277,6 +277,8 @@ Enable ``CONFIG_OPTEE``, ``CONFIG_CMD_OPTEE_RPMB`` and ``CONFIG_EFI_MM_COMM_TEE`
[1] https://optee.readthedocs.io/en/latest/building/efi_vars/stmm.html
+.. _uefi_capsule_update_ref:
- Enabling UEFI Capsule Update feature

From: Jassi Brar jaswinder.singh@linaro.org
The mtd and synquacer (developerbox) support was dropped from v6[1] This patchset re-introduces the support over last v7[2] submission of the patchset.
All the comments on this code over v5 submission have been addressed. Moving forward a changelog will be maintained.
[1] https://lore.kernel.org/all/20220704051658.1085442-1-sughosh.ganu@linaro.org... [2] https://lore.kernel.org/all/20220714183913.118505-1-sughosh.ganu@linaro.org/
Jassi Brar (2): dt: fwu: developerbox: enable fwu banks and mdata regions fwu: DeveloperBox: add support for FWU
Sughosh Ganu (3): dt/bindings: Add bindings for FWU Metadata mtd storage FWU: Add FWU metadata access driver for MTD storage regions FWU: mtd: Add helper functions for accessing FWU metadata
.../synquacer-sc2a11-developerbox-u-boot.dtsi | 22 +- board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 13 + board/socionext/developerbox/fwu_plat.c | 95 ++++++ configs/synquacer_developerbox_defconfig | 13 +- doc/board/socionext/developerbox.rst | 96 ++++++ .../firmware/fwu-mdata-mtd.yaml | 38 +++ drivers/fwu-mdata/Kconfig | 8 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++ include/configs/synquacer.h | 10 + lib/fwu_updates/Makefile | 1 + lib/fwu_updates/fwu_mtd.c | 173 ++++++++++ 13 files changed, 776 insertions(+), 3 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c create mode 100644 lib/fwu_updates/fwu_mtd.c

From: Jassi Brar jaswinder.singh@linaro.org
The mtd and synquacer (developerbox) support was dropped from v6[1] This patchset re-introduces the support over last v7[2] submission of the patchset.
All the comments on this code over v5 submission have been addressed. Moving forward a changelog will be maintained.
[1] https://lore.kernel.org/all/20220704051658.1085442-1-sughosh.ganu@linaro.org... [2] https://lore.kernel.org/all/20220714183913.118505-1-sughosh.ganu@linaro.org/
Jassi Brar (2): dt: fwu: developerbox: enable fwu banks and mdata regions fwu: DeveloperBox: add support for FWU
Sughosh Ganu (3): dt/bindings: Add bindings for FWU Metadata mtd storage FWU: Add FWU metadata access driver for MTD storage regions FWU: mtd: Add helper functions for accessing FWU metadata
.../synquacer-sc2a11-developerbox-u-boot.dtsi | 22 +- board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 13 + board/socionext/developerbox/fwu_plat.c | 95 ++++++ configs/synquacer_developerbox_defconfig | 13 +- doc/board/socionext/developerbox.rst | 96 ++++++ .../firmware/fwu-mdata-mtd.yaml | 38 +++ drivers/fwu-mdata/Kconfig | 8 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++ include/configs/synquacer.h | 10 + lib/fwu_updates/Makefile | 1 + lib/fwu_updates/fwu_mtd.c | 173 ++++++++++ 13 files changed, 776 insertions(+), 3 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c create mode 100644 lib/fwu_updates/fwu_mtd.c

From: Sughosh Ganu sughosh.ganu@linaro.org
Add bindings needed for accessing the FWU metadata regions. These include the compatible string which point to the access method, the actual device which stores the FWU metadata and the offsets for both metadata regions.
The current patch adds basic bindings needed for accessing the metadata structure on non-GPT mtd regions.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Jassi Brar jaswinder.singh@linaro.org --- .../firmware/fwu-mdata-mtd.yaml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml
diff --git a/doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml b/doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml new file mode 100644 index 0000000000..4f5404f999 --- /dev/null +++ b/doc/device-tree-bindings/firmware/fwu-mdata-mtd.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 MTD device without GPT + +maintainers: + - Masami Hiramatsu masami.hiramatsu@linaro.org + +properties: + compatible: + items: + - const: u-boot,fwu-mdata-mtd + + fwu-mdata-store: + maxItems: 1 + description: Phandle of the MTD 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-mtd"; + fwu-mdata-store = <&spi-flash>; + mdata-offsets = <0x500000 0x530000>; + };

On Fri, 22 Jul 2022 at 23:13, jassisinghbrar@gmail.com wrote:
From: Sughosh Ganu sughosh.ganu@linaro.org
Add bindings needed for accessing the FWU metadata regions. These include the compatible string which point to the access method, the actual device which stores the FWU metadata and the offsets for both metadata regions.
The current patch adds basic bindings needed for accessing the metadata structure on non-GPT mtd regions.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Jassi Brar jaswinder.singh@linaro.org
This patch is authored by Masami Hiramatsu, and should reflect that.
-sughosh
.../firmware/fwu-mdata-mtd.yaml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml
diff --git a/doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml b/doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml new file mode 100644 index 0000000000..4f5404f999 --- /dev/null +++ b/doc/device-tree-bindings/firmware/fwu-mdata-mtd.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 MTD device without GPT
+maintainers:
- Masami Hiramatsu masami.hiramatsu@linaro.org
+properties:
- compatible:
- items:
- const: u-boot,fwu-mdata-mtd
- fwu-mdata-store:
- maxItems: 1
- description: Phandle of the MTD 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-mtd";
fwu-mdata-store = <&spi-flash>;
mdata-offsets = <0x500000 0x530000>;
- };
-- 2.25.1

From: Sughosh Ganu sughosh.ganu@linaro.org
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, on a separate region. Add a driver for reading from and writing to the metadata when the updatable images and the metadata are stored on a raw MTD region.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Signed-off-by: Jassi Brar jaswinder.singh@linaro.org --- drivers/fwu-mdata/Kconfig | 8 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig index d5edef19d6..a8fa9ad783 100644 --- a/drivers/fwu-mdata/Kconfig +++ b/drivers/fwu-mdata/Kconfig @@ -14,3 +14,11 @@ config FWU_MDATA_GPT_BLK help Enable support for accessing FWU Metadata on GPT partitioned block devices. + +config FWU_MDATA_MTD + bool "FWU Metadata access for non-GPT MTD devices" + depends on DM_FWU_MDATA && MTD + help + Enable support for accessing FWU Metadata on non-partitioned + (or non-GPT partitioned, e.g. partition nodes in devicetree) + MTD devices. diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile index 313049f67a..58f8023f16 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_MTD) += fwu_mdata_mtd.o diff --git a/drivers/fwu-mdata/fwu_mdata_mtd.c b/drivers/fwu-mdata/fwu_mdata_mtd.c new file mode 100644 index 0000000000..d543a419fd --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_mtd.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, 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_mtd_priv { + struct mtd_info *mtd; + u32 pri_offset; + u32 sec_offset; +}; + +enum fwu_mtd_op { + FWU_MTD_READ, + FWU_MTD_WRITE, +}; + +static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size) +{ + return !do_div(size, mtd->erasesize); +} + +static int mtd_io_data(struct mtd_info *mtd, u32 offs, u32 size, void *data, + enum fwu_mtd_op op) +{ + struct mtd_oob_ops io_op ={}; + u64 lock_offs, lock_len; + size_t len; + void *buf; + int ret; + + if (!mtd_is_aligned_with_block_size(mtd, offs)) + return -EINVAL; + lock_offs = offs; + lock_len = round_up(size, mtd->erasesize); + + ret = mtd_unlock(mtd, lock_offs, lock_len); + if (ret && ret != -EOPNOTSUPP) + return ret; + + if (op == FWU_MTD_WRITE) { + struct erase_info erase_op = {}; + + /* This will expand erase size to align with the block size */ + erase_op.mtd = mtd; + erase_op.addr = lock_offs; + erase_op.len = lock_len; + erase_op.scrub = 0; + + ret = mtd_erase(mtd, &erase_op); + if (ret) + goto lock_out; + } + + /* Also, expand the write size to align with the write size */ + len = round_up(size, mtd->writesize); + + buf = memalign(ARCH_DMA_MINALIGN, len); + if (!buf) { + ret = -ENOMEM; + goto lock_out; + } + io_op.mode = MTD_OPS_AUTO_OOB; + io_op.len = len; + io_op.ooblen = 0; + io_op.datbuf = buf; + io_op.oobbuf = NULL; + + if (op == FWU_MTD_WRITE) { + memcpy(buf, data, size); + ret = mtd_write_oob(mtd, offs, &io_op); + } else { + ret = mtd_read_oob(mtd, offs, &io_op); + if (!ret) + memcpy(data, buf, size); + } + free(buf); + +lock_out: + mtd_lock(mtd, lock_offs, lock_len); + + return ret; +} + +static int fwu_mtd_load_mdata(struct mtd_info *mtd, struct fwu_mdata **mdata, + u32 offs, bool primary) +{ + size_t size = sizeof(struct fwu_mdata); + int ret; + + *mdata = malloc(size); + if (!*mdata) + return -ENOMEM; + + ret = mtd_io_data(mtd, offs, size, (void *)*mdata, FWU_MTD_READ); + if (ret >= 0) { + ret = fwu_verify_mdata(*mdata, primary); + if (ret < 0) { + free(*mdata); + *mdata = NULL; + } + } + + return ret; +} + +static int fwu_mtd_load_primary_mdata(struct fwu_mdata_mtd_priv *mtd_priv, + struct fwu_mdata **mdata) +{ + return fwu_mtd_load_mdata(mtd_priv->mtd, mdata, mtd_priv->pri_offset, true); +} + +static int fwu_mtd_load_secondary_mdata(struct fwu_mdata_mtd_priv *mtd_priv, + struct fwu_mdata **mdata) +{ + return fwu_mtd_load_mdata(mtd_priv->mtd, mdata, mtd_priv->sec_offset, false); +} + +static int fwu_mtd_save_primary_mdata(struct fwu_mdata_mtd_priv *mtd_priv, + struct fwu_mdata *mdata) +{ + return mtd_io_data(mtd_priv->mtd, mtd_priv->pri_offset, + sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE); +} + +static int fwu_mtd_save_secondary_mdata(struct fwu_mdata_mtd_priv *mtd_priv, + struct fwu_mdata *mdata) +{ + return mtd_io_data(mtd_priv->mtd, mtd_priv->sec_offset, + sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE); +} + +static int fwu_mtd_get_valid_mdata(struct fwu_mdata_mtd_priv *mtd_priv, + struct fwu_mdata **mdata) +{ + if (fwu_mtd_load_primary_mdata(mtd_priv, mdata) == 0) + return 0; + + log_err("Failed to load/verify primary mdata. Try secondary.\n"); + + if (fwu_mtd_load_secondary_mdata(mtd_priv, mdata) == 0) + return 0; + + log_err("Failed to load/verify secondary mdata.\n"); + + return -1; +} + +static int fwu_mtd_update_mdata(struct udevice *dev, struct fwu_mdata *mdata) +{ + struct fwu_mdata_mtd_priv *mtd_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_mtd_save_primary_mdata(mtd_priv, mdata); + if (ret < 0) { + log_err("Failed to update the primary mdata.\n"); + return ret; + } + + /* And now the replica */ + ret = fwu_mtd_save_secondary_mdata(mtd_priv, mdata); + if (ret < 0) { + log_err("Failed to update the secondary mdata.\n"); + return ret; + } + + return 0; +} + +static int fwu_mtd_mdata_check(struct udevice *dev) +{ + struct fwu_mdata *primary = NULL, *secondary = NULL; + struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev); + int ret; + + ret = fwu_mtd_load_primary_mdata(mtd_priv, &primary); + if (ret < 0) + log_err("Failed to read the primary mdata: %d\n", ret); + + ret = fwu_mtd_load_secondary_mdata(mtd_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_mtd_save_secondary_mdata(mtd_priv, primary); + if (ret < 0) + log_err("Restoring secondary mdata partition failed\n"); + } else if (secondary) { + ret = fwu_mtd_save_primary_mdata(mtd_priv, secondary); + if (ret < 0) + log_err("Restoring primary mdata partition failed\n"); + } + + free(primary); + free(secondary); + return ret; +} + +static int fwu_mtd_get_mdata(struct udevice *dev, struct fwu_mdata **mdata) +{ + struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev); + + return fwu_mtd_get_valid_mdata(mtd_priv, mdata); +} + +/** + * fwu_mdata_mtd_of_to_plat() - Translate from DT to fwu mdata device + */ +static int fwu_mdata_mtd_of_to_plat(struct udevice *dev) +{ + struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev); + const fdt32_t *phandle_p = NULL; + struct udevice *mtd_dev; + struct mtd_info *mtd; + 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), + &mtd_dev); + if (ret) + return ret; + + mtd_probe_devices(); + + mtd_for_each_device(mtd) { + if (mtd->dev == mtd_dev) { + mtd_priv->mtd = mtd; + log_debug("Found the FWU mdata mtd device %s\n", mtd->name); + break; + } + } + if (!mtd_priv->mtd) { + log_err("Failed to find mtd device by fwu-mdata-store\n"); + return -ENOENT; + } + + /* Get the offset of primary and seconday mdata */ + ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 0, + &mtd_priv->pri_offset); + if (ret) + return ret; + ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 1, + &mtd_priv->sec_offset); + if (ret) + return ret; + + return 0; +} + +static int fwu_mdata_mtd_probe(struct udevice *dev) +{ + /* Ensure the metadata can be read. */ + return fwu_mtd_mdata_check(dev); +} + +static struct fwu_mdata_ops fwu_mtd_ops = { + .mdata_check = fwu_mtd_mdata_check, + .get_mdata = fwu_mtd_get_mdata, + .update_mdata = fwu_mtd_update_mdata, +}; + +static const struct udevice_id fwu_mdata_ids[] = { + { .compatible = "u-boot,fwu-mdata-mtd" }, + { } +}; + +U_BOOT_DRIVER(fwu_mdata_mtd) = { + .name = "fwu-mdata-mtd", + .id = UCLASS_FWU_MDATA, + .of_match = fwu_mdata_ids, + .ops = &fwu_mtd_ops, + .probe = fwu_mdata_mtd_probe, + .of_to_plat = fwu_mdata_mtd_of_to_plat, + .priv_auto = sizeof(struct fwu_mdata_mtd_priv), +};

On 7/22/22 19:43, jassisinghbrar@gmail.com wrote:
From: Sughosh Ganu sughosh.ganu@linaro.org
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, on a separate region. Add a driver for reading from and writing to the metadata when the updatable images and the metadata are stored on a raw MTD region.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Signed-off-by: Jassi Brar jaswinder.singh@linaro.org
drivers/fwu-mdata/Kconfig | 8 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig index d5edef19d6..a8fa9ad783 100644 --- a/drivers/fwu-mdata/Kconfig +++ b/drivers/fwu-mdata/Kconfig @@ -14,3 +14,11 @@ config FWU_MDATA_GPT_BLK help Enable support for accessing FWU Metadata on GPT partitioned block devices.
+config FWU_MDATA_MTD
- bool "FWU Metadata access for non-GPT MTD devices"
- depends on DM_FWU_MDATA && MTD
- help
Enable support for accessing FWU Metadata on non-partitioned
(or non-GPT partitioned, e.g. partition nodes in devicetree)
MTD devices.
diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile index 313049f67a..58f8023f16 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_MTD) += fwu_mdata_mtd.o diff --git a/drivers/fwu-mdata/fwu_mdata_mtd.c b/drivers/fwu-mdata/fwu_mdata_mtd.c new file mode 100644 index 0000000000..d543a419fd --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_mtd.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2022, 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>
How should a reader know what pri_offset and sec_offset refer to? Please, provide Sphix style comments describing the structure.
+struct fwu_mdata_mtd_priv {
- struct mtd_info *mtd;
- u32 pri_offset;
- u32 sec_offset;
+};
+enum fwu_mtd_op {
- FWU_MTD_READ,
- FWU_MTD_WRITE,
+};
Please, document all functions.
+static bool /(struct mtd_info *mtd, u64 size) +{
- return !do_div(size, mtd->erasesize);
+}
+static int mtd_io_data(struct mtd_info *mtd, u32 offs, u32 size, void *data,
enum fwu_mtd_op op)
Most of the functionality of this function should be in the mtd uclass. It is not FWU specific.
+{
- struct mtd_oob_ops io_op ={};
- u64 lock_offs, lock_len;
- size_t len;
- void *buf;
- int ret;
- if (!mtd_is_aligned_with_block_size(mtd, offs))
return -EINVAL;
I think this is the only place where you could write an error message indicating that misplacement is the reason for the error.
- lock_offs = offs;
- lock_len = round_up(size, mtd->erasesize);
- ret = mtd_unlock(mtd, lock_offs, lock_len);
- if (ret && ret != -EOPNOTSUPP)
return ret;
- if (op == FWU_MTD_WRITE) {
struct erase_info erase_op = {};
/* This will expand erase size to align with the block size */
This comment is misplaced. It should be above the round_up() call.
erase_op.mtd = mtd;
erase_op.addr = lock_offs;
erase_op.len = lock_len;
erase_op.scrub = 0;
ret = mtd_erase(mtd, &erase_op);
if (ret)
goto lock_out;
- }
- /* Also, expand the write size to align with the write size */
- len = round_up(size, mtd->writesize);
- buf = memalign(ARCH_DMA_MINALIGN, len);
- if (!buf) {
ret = -ENOMEM;
goto lock_out;
- }
- io_op.mode = MTD_OPS_AUTO_OOB;
- io_op.len = len;
- io_op.ooblen = 0;
- io_op.datbuf = buf;
- io_op.oobbuf = NULL;
- if (op == FWU_MTD_WRITE) {
memcpy(buf, data, size);
Don't copy random bytes to the flash. You have to zero out the padding bytes.
ret = mtd_write_oob(mtd, offs, &io_op);
- } else {
ret = mtd_read_oob(mtd, offs, &io_op);
if (!ret)
memcpy(data, buf, size);
- }
- free(buf);
+lock_out:
This label sound like you want to lock something out. Please use lock: instead.
- mtd_lock(mtd, lock_offs, lock_len);
- return ret;
+}
+static int fwu_mtd_load_mdata(struct mtd_info *mtd, struct fwu_mdata **mdata,
u32 offs, bool primary)
+{
- size_t size = sizeof(struct fwu_mdata);
- int ret;
- *mdata = malloc(size);
- if (!*mdata)
return -ENOMEM;
- ret = mtd_io_data(mtd, offs, size, (void *)*mdata, FWU_MTD_READ);
The conversion to (void *) is superfluous as the expected parameter is of type void *.
- if (ret >= 0) {
ret = fwu_verify_mdata(*mdata, primary);
if (ret < 0) {
free(*mdata);
*mdata = NULL;
}
- }
- return ret;
+}
+static int fwu_mtd_load_primary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
struct fwu_mdata **mdata)
+{
- return fwu_mtd_load_mdata(mtd_priv->mtd, mdata, mtd_priv->pri_offset, true);
Instead of true and false use an enum like
enum fwu_sec_prim { FWU_META_PRIMARY, FWU_META_SECONDARY, }
and get rid of these functions. That will give you the same readability with less of complexity.
+}
+static int fwu_mtd_load_secondary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
struct fwu_mdata **mdata)
+{
- return fwu_mtd_load_mdata(mtd_priv->mtd, mdata, mtd_priv->sec_offset, false);
+}
+static int fwu_mtd_save_primary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
struct fwu_mdata *mdata)
+{
- return mtd_io_data(mtd_priv->mtd, mtd_priv->pri_offset,
sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE);
+}
+static int fwu_mtd_save_secondary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
struct fwu_mdata *mdata)
+{
- return mtd_io_data(mtd_priv->mtd, mtd_priv->sec_offset,
sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE);
+}
+static int fwu_mtd_get_valid_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
struct fwu_mdata **mdata)
+{
- if (fwu_mtd_load_primary_mdata(mtd_priv, mdata) == 0)
Please, don't use == 0. But "if (!".
return 0;
- log_err("Failed to load/verify primary mdata. Try secondary.\n");
- if (fwu_mtd_load_secondary_mdata(mtd_priv, mdata) == 0)
ditto
return 0;
- log_err("Failed to load/verify secondary mdata.\n");
- return -1;
+}
+static int fwu_mtd_update_mdata(struct udevice *dev, struct fwu_mdata *mdata) +{
- struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
- int ret;
- /* Update mdata crc32 field */
- mdata->crc32 = crc32(0, (void *)&mdata->version,
Avoid superfluous conversions. There is already a conversion in the defintion of the crc32 macro.
sizeof(*mdata) - sizeof(u32));
- /* First write the primary mdata */
- ret = fwu_mtd_save_primary_mdata(mtd_priv, mdata);
- if (ret < 0) {
log_err("Failed to update the primary mdata.\n");
return ret;
- }
- /* And now the replica */
- ret = fwu_mtd_save_secondary_mdata(mtd_priv, mdata);
- if (ret < 0) {
log_err("Failed to update the secondary mdata.\n");
return ret;
- }
- return 0;
+}
+static int fwu_mtd_mdata_check(struct udevice *dev) +{
- struct fwu_mdata *primary = NULL, *secondary = NULL;
- struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
- int ret;
- ret = fwu_mtd_load_primary_mdata(mtd_priv, &primary);
- if (ret < 0)
log_err("Failed to read the primary mdata: %d\n", ret);
- ret = fwu_mtd_load_secondary_mdata(mtd_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_mtd_save_secondary_mdata(mtd_priv, primary);
if (ret < 0)
log_err("Restoring secondary mdata partition failed\n");
- } else if (secondary) {
ret = fwu_mtd_save_primary_mdata(mtd_priv, secondary);
if (ret < 0)
log_err("Restoring primary mdata partition failed\n");
- }
If neither primary nor secondary data is available, you are happy???
- free(primary);
- free(secondary);
- return ret;
+}
+static int fwu_mtd_get_mdata(struct udevice *dev, struct fwu_mdata **mdata) +{
- struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
- return fwu_mtd_get_valid_mdata(mtd_priv, mdata);
+}
+/**
- fwu_mdata_mtd_of_to_plat() - Translate from DT to fwu mdata device
- */
+static int fwu_mdata_mtd_of_to_plat(struct udevice *dev) +{
- struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
- const fdt32_t *phandle_p = NULL;
- struct udevice *mtd_dev;
- struct mtd_info *mtd;
- 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");
A user needs to know that the problem is in the device-tree, e.g. "FWU meta data store not defined in device-tree".
return -ENOENT;
- }
- phandle = fdt32_to_cpu(*phandle_p);
- ret = device_get_global_by_ofnode(
ofnode_get_by_phandle(phandle),
&mtd_dev);
- if (ret)
return ret;
No log message?
- mtd_probe_devices();
- mtd_for_each_device(mtd) {
if (mtd->dev == mtd_dev) {
mtd_priv->mtd = mtd;
log_debug("Found the FWU mdata mtd device %s\n", mtd->name);
break;
}
- }
- if (!mtd_priv->mtd) {
log_err("Failed to find mtd device by fwu-mdata-store\n");
return -ENOENT;
- }
- /* Get the offset of primary and seconday mdata */
- ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 0,
&mtd_priv->pri_offset);
- if (ret)
return ret;
- ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 1,
&mtd_priv->sec_offset);
- if (ret)
return ret;
- return 0;
+}
+static int fwu_mdata_mtd_probe(struct udevice *dev) +{
- /* Ensure the metadata can be read. */
- return fwu_mtd_mdata_check(dev);
If you don't like the name of fwu_mtd_mdata_check(), change it instead of wrapping it in another function.
Best regards
Heinrich
+}
+static struct fwu_mdata_ops fwu_mtd_ops = {
- .mdata_check = fwu_mtd_mdata_check,
- .get_mdata = fwu_mtd_get_mdata,
- .update_mdata = fwu_mtd_update_mdata,
+};
+static const struct udevice_id fwu_mdata_ids[] = {
- { .compatible = "u-boot,fwu-mdata-mtd" },
- { }
+};
+U_BOOT_DRIVER(fwu_mdata_mtd) = {
- .name = "fwu-mdata-mtd",
- .id = UCLASS_FWU_MDATA,
- .of_match = fwu_mdata_ids,
- .ops = &fwu_mtd_ops,
- .probe = fwu_mdata_mtd_probe,
- .of_to_plat = fwu_mdata_mtd_of_to_plat,
- .priv_auto = sizeof(struct fwu_mdata_mtd_priv),
+};

From: Sughosh Ganu sughosh.ganu@linaro.org
Add helper functions needed for accessing the FWU metadata which contains information on the updatable images.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Signed-off-by: Jassi Brar jaswinder.singh@linaro.org --- lib/fwu_updates/Makefile | 1 + lib/fwu_updates/fwu_mtd.c | 173 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 lib/fwu_updates/fwu_mtd.c
diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile index 1993088e5b..c9e3c06b48 100644 --- a/lib/fwu_updates/Makefile +++ b/lib/fwu_updates/Makefile @@ -5,3 +5,4 @@
obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o +obj-$(CONFIG_FWU_MDATA_MTD) += fwu_mtd.o diff --git a/lib/fwu_updates/fwu_mtd.c b/lib/fwu_updates/fwu_mtd.c new file mode 100644 index 0000000000..f13cb149a2 --- /dev/null +++ b/lib/fwu_updates/fwu_mtd.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, Linaro Limited + */ + +#include <dfu.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <malloc.h> +#include <mtd.h> +#include <uuid.h> +#include <vsprintf.h> + +#include <dm/ofnode.h> + +int mtd_plat_get_alt_num(efi_guid_t *image_id, int *alt_num, + const char *mtd_dev, bool guid) +{ + int i, nalt; + int ret = -1; + struct mtd_info *mtd; + struct dfu_entity *dfu; + ofnode node, parts_node; + fdt_addr_t offset, size; + char uuidbuf[UUID_STR_LEN + 1]; + + mtd_probe_devices(); + mtd = get_mtd_device_nm(mtd_dev); + + /* Find partition node under given MTD device. */ + parts_node = ofnode_by_compatible(mtd_get_ofnode(mtd), + "fixed-partitions"); + + uuid_bin_to_str(image_id->b, uuidbuf, + guid ? UUID_STR_FORMAT_GUID : UUID_STR_FORMAT_STD); + node = ofnode_by_prop_value(parts_node, "uuid", uuidbuf, + sizeof(uuidbuf)); + if (!ofnode_valid(node)) { + log_warning("Warning: Failed to find partition by image UUID\n"); + return -ENOENT; + } + + offset = ofnode_get_addr_size_index_notrans(node, 0, &size); + if (offset == FDT_ADDR_T_NONE || !size) + return -ENOENT; + + dfu_init_env_entities(NULL, NULL); + + nalt = 0; + list_for_each_entry(dfu, &dfu_list, list) { + nalt++; + } + + if (!nalt) { + log_warning("No entities in dfu_alt_info\n"); + dfu_free_entities(); + return -ENOENT; + } + + for (i = 0; i < nalt; i++) { + dfu = dfu_get_entity(i); + + if (!dfu) + continue; + + if (dfu->dev_type != DFU_DEV_MTD) + continue; + + if (dfu->layout == DFU_RAW_ADDR && + dfu->data.mtd.start == offset && + dfu->data.mtd.size == size) { + *alt_num = dfu->alt; + ret = 0; + break; + } + } + + dfu_free_entities(); + + return ret; +} + +int gen_image_alt_info(char *buf, size_t len, int sidx, + struct fwu_image_entry *img, struct mtd_info *mtd) +{ + int i; + const char *suuid; + ofnode node, parts_node; + char uuidbuf[UUID_STR_LEN + 1]; + char *p = buf, *end = buf + len; + + /* Find partition node under given MTD device. */ + parts_node = ofnode_by_compatible(mtd_get_ofnode(mtd), + "fixed-partitions"); + if (!ofnode_valid(parts_node)) + return -ENOENT; + + /* Check the media UUID if exist. */ + suuid = ofnode_read_string(parts_node, "uuid"); + if (suuid) { + log_debug("Get location uuid %s\n", suuid); + uuid_bin_to_str(img->location_uuid.b, uuidbuf, + UUID_STR_FORMAT_STD); + if (strcasecmp(suuid, uuidbuf)) + log_warning("Warning: Location UUID does not match!\n"); + } + + p += snprintf(p, end - p, "mtd %s", mtd->name); + if (end < p) + return -E2BIG; + + /* + * List up the image banks in the FWU mdata and search the corresponding + * partition based on partition's uuid. + */ + for (i = 0; i < CONFIG_FWU_NUM_BANKS; i++) { + struct fwu_image_bank_info *bank; + fdt_addr_t offset, size; + + /* Query a partition by image UUID */ + bank = &img->img_bank_info[i]; + uuid_bin_to_str(bank->image_uuid.b, uuidbuf, + UUID_STR_FORMAT_STD); + node = ofnode_by_prop_value(parts_node, "uuid", uuidbuf, + sizeof(uuidbuf)); + if (!ofnode_valid(node)) { + log_warning("Warning: Failed to find partition by image UUID\n"); + break; + } + + offset = ofnode_get_addr_size_index_notrans(node, 0, &size); + if (offset == FDT_ADDR_T_NONE || !size) + break; + + p += snprintf(p, end - p, "%sbank%d raw %lx %lx", + i == 0 ? "=" : ";", i, (unsigned long)offset, + (unsigned long)size); + if (end < p) + return -E2BIG; + } + + return i != CONFIG_FWU_NUM_BANKS ? -ENOENT : 0; +} + +int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd) +{ + struct fwu_mdata *mdata = NULL; + int i, l, ret; + + ret = fwu_get_mdata(&mdata); + if (ret < 0) { + log_debug("Failed to get the FWU mdata.\n"); + return ret; + } + + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { + ret = gen_image_alt_info(buf, len, i * CONFIG_FWU_NUM_BANKS, + &mdata->img_entry[i], mtd); + if (ret) + break; + l = strlen(buf); + /* Replace the last ';' with '&' if there is another image. */ + if (i != CONFIG_FWU_NUM_IMAGES_PER_BANK - 1 && l) + buf[l - 1] = '&'; + len -= l; + buf += l; + } + + free(mdata); + + return ret; +}

On 7/22/22 19:43, jassisinghbrar@gmail.com wrote:
From: Sughosh Ganu sughosh.ganu@linaro.org
Add helper functions needed for accessing the FWU metadata which contains information on the updatable images.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Signed-off-by: Jassi Brar jaswinder.singh@linaro.org
lib/fwu_updates/Makefile | 1 + lib/fwu_updates/fwu_mtd.c | 173 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 lib/fwu_updates/fwu_mtd.c
diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile index 1993088e5b..c9e3c06b48 100644 --- a/lib/fwu_updates/Makefile +++ b/lib/fwu_updates/Makefile @@ -5,3 +5,4 @@
obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o +obj-$(CONFIG_FWU_MDATA_MTD) += fwu_mtd.o diff --git a/lib/fwu_updates/fwu_mtd.c b/lib/fwu_updates/fwu_mtd.c new file mode 100644 index 0000000000..f13cb149a2 --- /dev/null +++ b/lib/fwu_updates/fwu_mtd.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2022, Linaro Limited
- */
+#include <dfu.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <malloc.h> +#include <mtd.h> +#include <uuid.h> +#include <vsprintf.h>
+#include <dm/ofnode.h>
Please, document functions.
You missed adding the function to an include in the series. The include changes should be in the same patch as the library functions.
+int mtd_plat_get_alt_num(efi_guid_t *image_id, int *alt_num,
const char *mtd_dev, bool guid)
+{
- int i, nalt;
- int ret = -1;
- struct mtd_info *mtd;
- struct dfu_entity *dfu;
- ofnode node, parts_node;
- fdt_addr_t offset, size;
- char uuidbuf[UUID_STR_LEN + 1];
- mtd_probe_devices();
- mtd = get_mtd_device_nm(mtd_dev);
- /* Find partition node under given MTD device. */
- parts_node = ofnode_by_compatible(mtd_get_ofnode(mtd),
"fixed-partitions");
- uuid_bin_to_str(image_id->b, uuidbuf,
guid ? UUID_STR_FORMAT_GUID : UUID_STR_FORMAT_STD);
- node = ofnode_by_prop_value(parts_node, "uuid", uuidbuf,
sizeof(uuidbuf));
- if (!ofnode_valid(node)) {
log_warning("Warning: Failed to find partition by image UUID\n");
return -ENOENT;
- }
Isn't finding an mtd partition by UUID something that should be a function in the mtd uclass because it may find usages outside FWU?
- offset = ofnode_get_addr_size_index_notrans(node, 0, &size);
- if (offset == FDT_ADDR_T_NONE || !size)
return -ENOENT;
- dfu_init_env_entities(NULL, NULL);
- nalt = 0;
- list_for_each_entry(dfu, &dfu_list, list) {
nalt++;
- }
- if (!nalt) {
log_warning("No entities in dfu_alt_info\n");
dfu_free_entities();
return -ENOENT;
- }
- for (i = 0; i < nalt; i++) {
dfu = dfu_get_entity(i);
if (!dfu)
continue;
if (dfu->dev_type != DFU_DEV_MTD)
continue;
if (dfu->layout == DFU_RAW_ADDR &&
dfu->data.mtd.start == offset &&
dfu->data.mtd.size == size) {
*alt_num = dfu->alt;
ret = 0;
break;
}
- }
- dfu_free_entities();
- return ret;
+}
+int gen_image_alt_info(char *buf, size_t len, int sidx,
struct fwu_image_entry *img, struct mtd_info *mtd)
+{
- int i;
- const char *suuid;
- ofnode node, parts_node;
- char uuidbuf[UUID_STR_LEN + 1];
- char *p = buf, *end = buf + len;
- /* Find partition node under given MTD device. */
- parts_node = ofnode_by_compatible(mtd_get_ofnode(mtd),
"fixed-partitions");
- if (!ofnode_valid(parts_node))
return -ENOENT;
- /* Check the media UUID if exist. */
- suuid = ofnode_read_string(parts_node, "uuid");
- if (suuid) {
log_debug("Get location uuid %s\n", suuid);
uuid_bin_to_str(img->location_uuid.b, uuidbuf,
UUID_STR_FORMAT_STD);
if (strcasecmp(suuid, uuidbuf))
log_warning("Warning: Location UUID does not match!\n");
- }
- p += snprintf(p, end - p, "mtd %s", mtd->name);
- if (end < p)
err_log() needed?
return -E2BIG;
- /*
* List up the image banks in the FWU mdata and search the corresponding
%s/up //
* partition based on partition's uuid.
*/
- for (i = 0; i < CONFIG_FWU_NUM_BANKS; i++) {
struct fwu_image_bank_info *bank;
fdt_addr_t offset, size;
/* Query a partition by image UUID */
bank = &img->img_bank_info[i];
uuid_bin_to_str(bank->image_uuid.b, uuidbuf,
UUID_STR_FORMAT_STD);
node = ofnode_by_prop_value(parts_node, "uuid", uuidbuf,
sizeof(uuidbuf));
if (!ofnode_valid(node)) {
log_warning("Warning: Failed to find partition by image UUID\n");
break;
}
offset = ofnode_get_addr_size_index_notrans(node, 0, &size);
if (offset == FDT_ADDR_T_NONE || !size)
break;
p += snprintf(p, end - p, "%sbank%d raw %lx %lx",
i == 0 ? "=" : ";", i, (unsigned long)offset,
(unsigned long)size);
if (end < p)
return -E2BIG;
- }
- return i != CONFIG_FWU_NUM_BANKS ? -ENOENT : 0;
This is easier to read:
if (i != CONFIG_FWU_NUM_BANKS) return -ENOENT; return 0;
Best regards
Heinrich
+}
+int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd) +{
- struct fwu_mdata *mdata = NULL;
- int i, l, ret;
- ret = fwu_get_mdata(&mdata);
- if (ret < 0) {
log_debug("Failed to get the FWU mdata.\n");
return ret;
- }
- for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
ret = gen_image_alt_info(buf, len, i * CONFIG_FWU_NUM_BANKS,
&mdata->img_entry[i], mtd);
if (ret)
break;
l = strlen(buf);
/* Replace the last ';' with '&' if there is another image. */
if (i != CONFIG_FWU_NUM_IMAGES_PER_BANK - 1 && l)
buf[l - 1] = '&';
len -= l;
buf += l;
- }
- free(mdata);
- return ret;
+}

From: Jassi Brar jaswinder.singh@linaro.org
specify Bank-0/1 and fwu metadata mtd regions.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Jassi Brar jaswinder.singh@linaro.org --- .../synquacer-sc2a11-developerbox-u-boot.dtsi | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi index 7a56116d6f..62eee0aaf0 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"; @@ -36,6 +36,7 @@ compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; + uuid = "17e86d77-41f9-4fd7-87ec-a55df9842de5";
partition@0 { label = "BootStrap-BL1"; @@ -79,6 +80,19 @@ label = "Ex-OPTEE"; reg = <0x500000 0x200000>; }; + + /* FWU Multi bank update partitions */ + partition@600000 { + label = "FIP-Bank0"; + reg = <0x600000 0x400000>; + uuid = "5a66a702-99fd-4fef-a392-c26e261a2828"; + }; + + partition@a00000 { + label = "FIP-Bank1"; + reg = <0xa00000 0x400000>; + uuid = "a8f868a1-6e5c-4757-878d-ce63375ef2c0"; + }; }; }; }; @@ -104,6 +118,12 @@ optee { status = "okay"; }; + + fwu-mdata { + compatible = "u-boot,fwu-mdata-mtd"; + fwu-mdata-store = <&spi_flash>; + mdata-offsets = <0x500000 0x530000>; + }; }; };

From: Jassi Brar jaswinder.singh@linaro.org
Add code to support FWU_MULTI_BANK_UPDATE. The platform does not have gpt-partition storage for Banks and MetaData, rather it used SPI-NOR backed mtd regions for the purpose.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Jassi Brar jaswinder.singh@linaro.org --- board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 13 +++ board/socionext/developerbox/fwu_plat.c | 95 ++++++++++++++++++++ configs/synquacer_developerbox_defconfig | 13 ++- doc/board/socionext/developerbox.rst | 96 +++++++++++++++++++++ include/configs/synquacer.h | 10 +++ 6 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c
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/developerbox.c b/board/socionext/developerbox/developerbox.c index f5a5fe0121..ad2260e3d7 100644 --- a/board/socionext/developerbox/developerbox.c +++ b/board/socionext/developerbox/developerbox.c @@ -20,6 +20,13 @@
#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) struct efi_fw_image fw_images[] = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) + { + .image_type_id = DEVELOPERBOX_FIP_IMAGE_GUID, + .fw_name = u"DEVELOPERBOX-FIP", + .image_index = 1, + }, +#else { .image_type_id = DEVELOPERBOX_UBOOT_IMAGE_GUID, .fw_name = u"DEVELOPERBOX-UBOOT", @@ -35,12 +42,18 @@ struct efi_fw_image fw_images[] = { .fw_name = u"DEVELOPERBOX-OPTEE", .image_index = 3, }, +#endif };
struct efi_capsule_update_info update_info = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) + .dfu_string = "mtd nor1=bank0 raw 600000 400000;" + "bank1 raw a00000 400000;", +#else .dfu_string = "mtd nor1=u-boot.bin raw 200000 100000;" "fip.bin raw 180000 78000;" "optee.bin raw 500000 100000", +#endif .images = fw_images, };
diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c new file mode 100644 index 0000000000..9fb5cb28b3 --- /dev/null +++ b/board/socionext/developerbox/fwu_plat.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2021, Linaro Limited + */ + +#include <dfu.h> +#include <efi_loader.h> +#include <flash.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <malloc.h> +#include <memalign.h> +#include <mtd.h> +#include <spi.h> +#include <spi_flash.h> +#include <uuid.h> + +#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h> + +#define DFU_ALT_BUF_LEN 256 +#define DFU_ALT_NUM_MAX (CONFIG_FWU_NUM_IMAGES_PER_BANK * CONFIG_FWU_NUM_BANKS) + +/* Generate dfu_alt_info from partitions */ +void set_dfu_alt_info(char *interface, char *devstr) +{ + int ret; + struct mtd_info *mtd; + static char *buf = NULL; + + if (!buf) { + buf = malloc_cache_aligned(DFU_ALT_BUF_LEN); + memset(buf, 0, DFU_ALT_BUF_LEN); + + mtd_probe_devices(); + + mtd = get_mtd_device_nm("nor1"); + if (IS_ERR_OR_NULL(mtd)) + return; + + ret = fwu_gen_alt_info_from_mtd(buf, DFU_ALT_BUF_LEN, mtd); + if (ret < 0) { + log_err("Error: Failed to generate dfu_alt_info. (%d)\n", ret); + return; + } + log_debug("Make dfu_alt_info: '%s'\n", buf); + } + env_set("dfu_alt_info", buf); +} + +int fwu_plat_get_alt_num(struct udevice __always_unused *dev, + efi_guid_t *image_id, int *alt_num) +{ + return mtd_plat_get_alt_num(image_id, alt_num, "nor1", 0); +} + +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 ret; + + *update_idx = 1 - active_idx; + + return ret; +} + +void fwu_plat_get_bootidx(void *boot_idx) +{ + int ret; + u32 active_idx; + u32 *bootidx = boot_idx; + + ret = fwu_get_active_index(&active_idx); + + if (ret < 0) + *bootidx = -1; + + *bootidx = active_idx; +} + +int board_late_init(void) +{ + /* Make mmc available for EFI, otherwise efi subsystem + * complains "No EFI system partition" during bootup. + */ + run_command("mmc dev 0", 0); + + return 0; +} diff --git a/configs/synquacer_developerbox_defconfig b/configs/synquacer_developerbox_defconfig index add6041e27..ded31ada6e 100644 --- a/configs/synquacer_developerbox_defconfig +++ b/configs/synquacer_developerbox_defconfig @@ -1,10 +1,11 @@ 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_SYS_MALLOC_F_LEN=0x400 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" @@ -93,3 +94,11 @@ 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_BOARD_LATE_INIT=y +CONFIG_FWU_MULTI_BANK_UPDATE=y +CONFIG_DM_FWU_MDATA=y +CONFIG_FWU_MDATA_MTD=y +CONFIG_FWU_NUM_BANKS=2 +CONFIG_FWU_NUM_IMAGES_PER_BANK=1 +CONFIG_CMD_FWU_METADATA=y diff --git a/doc/board/socionext/developerbox.rst b/doc/board/socionext/developerbox.rst index 2d943c23be..f52820c2b0 100644 --- a/doc/board/socionext/developerbox.rst +++ b/doc/board/socionext/developerbox.rst @@ -85,3 +85,99 @@ Once the flasher tool is running we are ready flash the UEFI image::
After transferring the SPI_NOR_UBOOT.fd, turn off the DSW2-7 and reset the board.
+ +Enable FWU Multi Bank Update +============================ + +DeveloperBox supports the FWU Multi Bank Update. You *MUST* update both *SCP firmware* and *TF-A* for this feature. This will change the layout and the boot process but you can switch back to the normal one by changing the DSW 1-4 off. + +Configure U-Boot +---------------- + +To enable the FWU Multi Bank Update on the DeveloperBox, you need to add following configurations to configs/synquacer_developerbox_defconfig :: + + CONFIG_FWU_MULTI_BANK_UPDATE=y + CONFIG_DM_FWU_MDATA=y + CONFIG_FWU_MDATA_MTD=y + CONFIG_FWU_NUM_BANKS=2 + CONFIG_FWU_NUM_IMAGES_PER_BANK=1 + CONFIG_CMD_FWU_METADATA=y + +And build it:: + + cd u-boot/ + export ARCH=arm64 + export CROSS_COMPILE=aarch64-linux-gnu- + make synqucer_developerbox_defconfig + make -j `noproc` + cd ../ + +By default, the CONFIG_FWU_NUM_BANKS and COFNIG_FWU_NUM_IMAGES_PER_BANKS are set to 2 and 1 respectively. This uses FIP (Firmware Image Package) type image which contains TF-A, U-Boot and OP-TEE (the OP-TEE is optional.) +You can use fiptool to compose the FIP image from those firmware images. + +Rebuild SCP firmware +-------------------- + +Rebuild SCP firmware which supports FWU Multi Bank Update as below:: + + cd SCP-firmware/ + OUT=./build/product/synquacer + ROMFW_FILE=$OUT/scp_romfw/$SCP_BUILD_MODE/bin/scp_romfw.bin + RAMFW_FILE=$OUT/scp_ramfw/$SCP_BUILD_MODE/bin/scp_ramfw.bin + ROMRAMFW_FILE=scp_romramfw_release.bin + + make CC=$ARM_EMB_GCC PRODUCT=synquacer MODE=release + tr "\000" "\377" < /dev/zero | dd of=${ROMRAMFW_FILE} bs=1 count=196608 + dd if=${ROMFW_FILE} of=${ROMRAMFW_FILE} bs=1 conv=notrunc seek=0 + dd if=${RAMFW_FILE} of=${ROMRAMFW_FILE} bs=1 seek=65536 + cd ../ + +And you can get the `scp_romramfw_release.bin` file + +Rebuild TF-A and FIP +-------------------- + +Rebuild TF-A which supports FWU Multi Bank Update as below:: + + cd arm-trusted-firmware/ + make CROSS_COMPILE=aarch64-linux-gnu- -j`nproc` PLAT=synquacer \ + SPD=opteed SQ_RESET_TO_BL2=1 GENERATE_COT=1 MBEDTLS_DIR=../mbedtls \ + BL33=../u-boot/u-boot.bin all fip fiptool + +And make a FIP image.:: + + cp build/synquacer/release/fip.bin SPI_NOR_NEWFIP.fd + tools/fiptool/fiptool update --tb-fw build/synquacer/release/bl2.bin SPI_NOR_NEWFIP.fd + + +UUIDs for the FWU Multi Bank Update +----------------------------------- + +FWU multi-bank update requires some UUIDs. The DeveloperBox platform uses following UUIDs. + + - Location UUID for the FIP image: 17e86d77-41f9-4fd7-87ec-a55df9842de5 + - Image type UUID for the FIP image: 10c36d7d-ca52-b843-b7b9-f9d6c501d108 + - Image UUID for Bank0 : 5a66a702-99fd-4fef-a392-c26e261a2828 + - Image UUID for Bank1 : a8f868a1-6e5c-4757-878d-ce63375ef2c0 + +These UUIDs are used for making a FWU metadata image. + +Install via flash writer +------------------------ + +As explained in above section, the new FIP image and the FWU metadata image can be installed via NOR flash writer. Note that the installation offsets for the FWU multi bank update supported firmware. + +Once the flasher tool is running we are ready flash the images.:: +Write the FIP image to the 0x600000 offset.:: + + flash rawwrite 600000 180000 + >> Send SPI_NOR_NEWFIP.fd via XMODEM (Control-A S in minicom) << + +And write the new SCP firmware.:: + + flash write cm3 + >> Send scp_romramfw_release.bin via XMODEM (Control-A S in minicom) << + +At last, turn on the DSW 3-4 on the board, and reboot. +Note that if DSW 3-4 is turned off, the DeveloperBox will boot from +the original EDK2 firmware (or non-FWU U-Boot if you already installed.) diff --git a/include/configs/synquacer.h b/include/configs/synquacer.h index 5686a5b910..7995be852d 100644 --- a/include/configs/synquacer.h +++ b/include/configs/synquacer.h @@ -46,19 +46,29 @@
/* Since U-Boot 64bit PCIe support is limited, disable 64bit MMIO support */
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE +#define DEFAULT_DFU_ALT_INFO +#else #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" +#endif
/* GUIDs for capsule updatable firmware images */ #define DEVELOPERBOX_UBOOT_IMAGE_GUID \ EFI_GUID(0x53a92e83, 0x4ef4, 0x473a, 0x8b, 0x0d, \ 0xb5, 0xd8, 0xc7, 0xb2, 0xd6, 0x00)
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE +#define DEVELOPERBOX_FIP_IMAGE_GUID \ + EFI_GUID(0x7d6dc310, 0x52ca, 0x43b8, 0xb7, 0xb9, \ + 0xf9, 0xd6, 0xc5, 0x01, 0xd1, 0x08) +#else #define DEVELOPERBOX_FIP_IMAGE_GUID \ EFI_GUID(0x880866e9, 0x84ba, 0x4793, 0xa9, 0x08, \ 0x33, 0xe0, 0xb9, 0x16, 0xf3, 0x98) +#endif
#define DEVELOPERBOX_OPTEE_IMAGE_GUID \ EFI_GUID(0xc1b629f1, 0xce0e, 0x4894, 0x82, 0xbf, \

On 7/22/22 19:43, jassisinghbrar@gmail.com wrote:
From: Jassi Brar jaswinder.singh@linaro.org
Add code to support FWU_MULTI_BANK_UPDATE. The platform does not have gpt-partition storage for Banks and MetaData, rather it used SPI-NOR backed mtd regions for the purpose.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Jassi Brar jaswinder.singh@linaro.org
board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 13 +++ board/socionext/developerbox/fwu_plat.c | 95 ++++++++++++++++++++ configs/synquacer_developerbox_defconfig | 13 ++- doc/board/socionext/developerbox.rst | 96 +++++++++++++++++++++ include/configs/synquacer.h | 10 +++ 6 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c
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/developerbox.c b/board/socionext/developerbox/developerbox.c index f5a5fe0121..ad2260e3d7 100644 --- a/board/socionext/developerbox/developerbox.c +++ b/board/socionext/developerbox/developerbox.c @@ -20,6 +20,13 @@
#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) struct efi_fw_image fw_images[] = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE)
- {
.image_type_id = DEVELOPERBOX_FIP_IMAGE_GUID,
.fw_name = u"DEVELOPERBOX-FIP",
The design is flawed. These fields should be moved to the device-tree.
Best regards
Heinrich
.image_index = 1,
- },
+#else { .image_type_id = DEVELOPERBOX_UBOOT_IMAGE_GUID, .fw_name = u"DEVELOPERBOX-UBOOT", @@ -35,12 +42,18 @@ struct efi_fw_image fw_images[] = { .fw_name = u"DEVELOPERBOX-OPTEE", .image_index = 3, }, +#endif };
struct efi_capsule_update_info update_info = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE)
- .dfu_string = "mtd nor1=bank0 raw 600000 400000;"
"bank1 raw a00000 400000;",
+#else .dfu_string = "mtd nor1=u-boot.bin raw 200000 100000;" "fip.bin raw 180000 78000;" "optee.bin raw 500000 100000", +#endif .images = fw_images, };
diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c new file mode 100644 index 0000000000..9fb5cb28b3 --- /dev/null +++ b/board/socionext/developerbox/fwu_plat.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2021, Linaro Limited
- */
+#include <dfu.h> +#include <efi_loader.h> +#include <flash.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <malloc.h> +#include <memalign.h> +#include <mtd.h> +#include <spi.h> +#include <spi_flash.h> +#include <uuid.h>
+#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h>
+#define DFU_ALT_BUF_LEN 256 +#define DFU_ALT_NUM_MAX (CONFIG_FWU_NUM_IMAGES_PER_BANK * CONFIG_FWU_NUM_BANKS)
+/* Generate dfu_alt_info from partitions */ +void set_dfu_alt_info(char *interface, char *devstr) +{
- int ret;
- struct mtd_info *mtd;
- static char *buf = NULL;
- if (!buf) {
buf = malloc_cache_aligned(DFU_ALT_BUF_LEN);
memset(buf, 0, DFU_ALT_BUF_LEN);
mtd_probe_devices();
mtd = get_mtd_device_nm("nor1");
if (IS_ERR_OR_NULL(mtd))
return;
ret = fwu_gen_alt_info_from_mtd(buf, DFU_ALT_BUF_LEN, mtd);
if (ret < 0) {
log_err("Error: Failed to generate dfu_alt_info. (%d)\n", ret);
return;
}
log_debug("Make dfu_alt_info: '%s'\n", buf);
- }
- env_set("dfu_alt_info", buf);
+}
+int fwu_plat_get_alt_num(struct udevice __always_unused *dev,
efi_guid_t *image_id, int *alt_num)
+{
- return mtd_plat_get_alt_num(image_id, alt_num, "nor1", 0);
+}
+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 ret;
- *update_idx = 1 - active_idx;
- return ret;
+}
+void fwu_plat_get_bootidx(void *boot_idx) +{
- int ret;
- u32 active_idx;
- u32 *bootidx = boot_idx;
- ret = fwu_get_active_index(&active_idx);
- if (ret < 0)
*bootidx = -1;
- *bootidx = active_idx;
+}
+int board_late_init(void) +{
- /* Make mmc available for EFI, otherwise efi subsystem
* complains "No EFI system partition" during bootup.
*/
- run_command("mmc dev 0", 0);
- return 0;
+} diff --git a/configs/synquacer_developerbox_defconfig b/configs/synquacer_developerbox_defconfig index add6041e27..ded31ada6e 100644 --- a/configs/synquacer_developerbox_defconfig +++ b/configs/synquacer_developerbox_defconfig @@ -1,10 +1,11 @@ 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_SYS_MALLOC_F_LEN=0x400 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" @@ -93,3 +94,11 @@ 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_BOARD_LATE_INIT=y +CONFIG_FWU_MULTI_BANK_UPDATE=y +CONFIG_DM_FWU_MDATA=y +CONFIG_FWU_MDATA_MTD=y +CONFIG_FWU_NUM_BANKS=2 +CONFIG_FWU_NUM_IMAGES_PER_BANK=1 +CONFIG_CMD_FWU_METADATA=y diff --git a/doc/board/socionext/developerbox.rst b/doc/board/socionext/developerbox.rst index 2d943c23be..f52820c2b0 100644 --- a/doc/board/socionext/developerbox.rst +++ b/doc/board/socionext/developerbox.rst @@ -85,3 +85,99 @@ Once the flasher tool is running we are ready flash the UEFI image::
After transferring the SPI_NOR_UBOOT.fd, turn off the DSW2-7 and reset the board.
+Enable FWU Multi Bank Update +============================
+DeveloperBox supports the FWU Multi Bank Update. You *MUST* update both *SCP firmware* and *TF-A* for this feature. This will change the layout and the boot process but you can switch back to the normal one by changing the DSW 1-4 off.
+Configure U-Boot +----------------
+To enable the FWU Multi Bank Update on the DeveloperBox, you need to add following configurations to configs/synquacer_developerbox_defconfig ::
- CONFIG_FWU_MULTI_BANK_UPDATE=y
- CONFIG_DM_FWU_MDATA=y
- CONFIG_FWU_MDATA_MTD=y
- CONFIG_FWU_NUM_BANKS=2
- CONFIG_FWU_NUM_IMAGES_PER_BANK=1
- CONFIG_CMD_FWU_METADATA=y
+And build it::
- cd u-boot/
- export ARCH=arm64
- export CROSS_COMPILE=aarch64-linux-gnu-
- make synqucer_developerbox_defconfig
- make -j `noproc`
- cd ../
+By default, the CONFIG_FWU_NUM_BANKS and COFNIG_FWU_NUM_IMAGES_PER_BANKS are set to 2 and 1 respectively. This uses FIP (Firmware Image Package) type image which contains TF-A, U-Boot and OP-TEE (the OP-TEE is optional.) +You can use fiptool to compose the FIP image from those firmware images.
+Rebuild SCP firmware +--------------------
+Rebuild SCP firmware which supports FWU Multi Bank Update as below::
- cd SCP-firmware/
- OUT=./build/product/synquacer
- ROMFW_FILE=$OUT/scp_romfw/$SCP_BUILD_MODE/bin/scp_romfw.bin
- RAMFW_FILE=$OUT/scp_ramfw/$SCP_BUILD_MODE/bin/scp_ramfw.bin
- ROMRAMFW_FILE=scp_romramfw_release.bin
- make CC=$ARM_EMB_GCC PRODUCT=synquacer MODE=release
- tr "\000" "\377" < /dev/zero | dd of=${ROMRAMFW_FILE} bs=1 count=196608
- dd if=${ROMFW_FILE} of=${ROMRAMFW_FILE} bs=1 conv=notrunc seek=0
- dd if=${RAMFW_FILE} of=${ROMRAMFW_FILE} bs=1 seek=65536
- cd ../
+And you can get the `scp_romramfw_release.bin` file
+Rebuild TF-A and FIP +--------------------
+Rebuild TF-A which supports FWU Multi Bank Update as below::
- cd arm-trusted-firmware/
- make CROSS_COMPILE=aarch64-linux-gnu- -j`nproc` PLAT=synquacer \
SPD=opteed SQ_RESET_TO_BL2=1 GENERATE_COT=1 MBEDTLS_DIR=../mbedtls \
BL33=../u-boot/u-boot.bin all fip fiptool
+And make a FIP image.::
- cp build/synquacer/release/fip.bin SPI_NOR_NEWFIP.fd
- tools/fiptool/fiptool update --tb-fw build/synquacer/release/bl2.bin SPI_NOR_NEWFIP.fd
+UUIDs for the FWU Multi Bank Update +-----------------------------------
+FWU multi-bank update requires some UUIDs. The DeveloperBox platform uses following UUIDs.
- Location UUID for the FIP image: 17e86d77-41f9-4fd7-87ec-a55df9842de5
- Image type UUID for the FIP image: 10c36d7d-ca52-b843-b7b9-f9d6c501d108
- Image UUID for Bank0 : 5a66a702-99fd-4fef-a392-c26e261a2828
- Image UUID for Bank1 : a8f868a1-6e5c-4757-878d-ce63375ef2c0
+These UUIDs are used for making a FWU metadata image.
+Install via flash writer +------------------------
+As explained in above section, the new FIP image and the FWU metadata image can be installed via NOR flash writer. Note that the installation offsets for the FWU multi bank update supported firmware.
+Once the flasher tool is running we are ready flash the images.:: +Write the FIP image to the 0x600000 offset.::
- flash rawwrite 600000 180000
Send SPI_NOR_NEWFIP.fd via XMODEM (Control-A S in minicom) <<+And write the new SCP firmware.::
- flash write cm3
Send scp_romramfw_release.bin via XMODEM (Control-A S in minicom) <<+At last, turn on the DSW 3-4 on the board, and reboot. +Note that if DSW 3-4 is turned off, the DeveloperBox will boot from +the original EDK2 firmware (or non-FWU U-Boot if you already installed.) diff --git a/include/configs/synquacer.h b/include/configs/synquacer.h index 5686a5b910..7995be852d 100644 --- a/include/configs/synquacer.h +++ b/include/configs/synquacer.h @@ -46,19 +46,29 @@
/* Since U-Boot 64bit PCIe support is limited, disable 64bit MMIO support */
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE +#define DEFAULT_DFU_ALT_INFO +#else #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" +#endif
/* GUIDs for capsule updatable firmware images */ #define DEVELOPERBOX_UBOOT_IMAGE_GUID \ EFI_GUID(0x53a92e83, 0x4ef4, 0x473a, 0x8b, 0x0d, \ 0xb5, 0xd8, 0xc7, 0xb2, 0xd6, 0x00)
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE +#define DEVELOPERBOX_FIP_IMAGE_GUID \
- EFI_GUID(0x7d6dc310, 0x52ca, 0x43b8, 0xb7, 0xb9, \
0xf9, 0xd6, 0xc5, 0x01, 0xd1, 0x08)
+#else #define DEVELOPERBOX_FIP_IMAGE_GUID \ EFI_GUID(0x880866e9, 0x84ba, 0x4793, 0xa9, 0x08, \ 0x33, 0xe0, 0xb9, 0x16, 0xf3, 0x98) +#endif
#define DEVELOPERBOX_OPTEE_IMAGE_GUID \ EFI_GUID(0xc1b629f1, 0xce0e, 0x4894, 0x82, 0xbf, \

On 9/1/22 09:07, Heinrich Schuchardt wrote:
On 7/22/22 19:43, jassisinghbrar@gmail.com wrote:
From: Jassi Brar jaswinder.singh@linaro.org
Add code to support FWU_MULTI_BANK_UPDATE. The platform does not have gpt-partition storage for Banks and MetaData, rather it used SPI-NOR backed mtd regions for the purpose.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Jassi Brar jaswinder.singh@linaro.org
board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 13 +++ board/socionext/developerbox/fwu_plat.c | 95 ++++++++++++++++++++ configs/synquacer_developerbox_defconfig | 13 ++- doc/board/socionext/developerbox.rst | 96 +++++++++++++++++++++ include/configs/synquacer.h | 10 +++ 6 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c
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/developerbox.c b/board/socionext/developerbox/developerbox.c index f5a5fe0121..ad2260e3d7 100644 --- a/board/socionext/developerbox/developerbox.c +++ b/board/socionext/developerbox/developerbox.c @@ -20,6 +20,13 @@
#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) struct efi_fw_image fw_images[] = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) + { + .image_type_id = DEVELOPERBOX_FIP_IMAGE_GUID, + .fw_name = u"DEVELOPERBOX-FIP",
The design is flawed. These fields should be moved to the device-tree.
Currently we are changing C files for each board were we enable firmware updates. Probably an even better place then the device-tree would be a Kconfig file. The only problem with Kconfig is that it does not easily allow to edit arrays. But we could use a string like:
GUID,name,index,GUID,name,index,...
Best regards
Heinrich>
+ .image_index = 1, + }, +#else { .image_type_id = DEVELOPERBOX_UBOOT_IMAGE_GUID, .fw_name = u"DEVELOPERBOX-UBOOT", @@ -35,12 +42,18 @@ struct efi_fw_image fw_images[] = { .fw_name = u"DEVELOPERBOX-OPTEE", .image_index = 3, }, +#endif };
struct efi_capsule_update_info update_info = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) + .dfu_string = "mtd nor1=bank0 raw 600000 400000;" + "bank1 raw a00000 400000;", +#else .dfu_string = "mtd nor1=u-boot.bin raw 200000 100000;" "fip.bin raw 180000 78000;" "optee.bin raw 500000 100000", +#endif .images = fw_images, };
diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c new file mode 100644 index 0000000000..9fb5cb28b3 --- /dev/null +++ b/board/socionext/developerbox/fwu_plat.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2021, Linaro Limited
- */
+#include <dfu.h> +#include <efi_loader.h> +#include <flash.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <malloc.h> +#include <memalign.h> +#include <mtd.h> +#include <spi.h> +#include <spi_flash.h> +#include <uuid.h>
+#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h>
+#define DFU_ALT_BUF_LEN 256 +#define DFU_ALT_NUM_MAX (CONFIG_FWU_NUM_IMAGES_PER_BANK * CONFIG_FWU_NUM_BANKS)
+/* Generate dfu_alt_info from partitions */ +void set_dfu_alt_info(char *interface, char *devstr) +{ + int ret; + struct mtd_info *mtd; + static char *buf = NULL;
+ if (!buf) { + buf = malloc_cache_aligned(DFU_ALT_BUF_LEN); + memset(buf, 0, DFU_ALT_BUF_LEN);
+ mtd_probe_devices();
+ mtd = get_mtd_device_nm("nor1"); + if (IS_ERR_OR_NULL(mtd)) + return;
+ ret = fwu_gen_alt_info_from_mtd(buf, DFU_ALT_BUF_LEN, mtd); + if (ret < 0) { + log_err("Error: Failed to generate dfu_alt_info. (%d)\n", ret); + return; + } + log_debug("Make dfu_alt_info: '%s'\n", buf); + } + env_set("dfu_alt_info", buf); +}
+int fwu_plat_get_alt_num(struct udevice __always_unused *dev, + efi_guid_t *image_id, int *alt_num) +{ + return mtd_plat_get_alt_num(image_id, alt_num, "nor1", 0); +}
+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 ret;
+ *update_idx = 1 - active_idx;
+ return ret; +}
+void fwu_plat_get_bootidx(void *boot_idx) +{ + int ret; + u32 active_idx; + u32 *bootidx = boot_idx;
+ ret = fwu_get_active_index(&active_idx);
+ if (ret < 0) + *bootidx = -1;
+ *bootidx = active_idx; +}
+int board_late_init(void) +{ + /* Make mmc available for EFI, otherwise efi subsystem + * complains "No EFI system partition" during bootup. + */ + run_command("mmc dev 0", 0);
+ return 0; +} diff --git a/configs/synquacer_developerbox_defconfig b/configs/synquacer_developerbox_defconfig index add6041e27..ded31ada6e 100644 --- a/configs/synquacer_developerbox_defconfig +++ b/configs/synquacer_developerbox_defconfig @@ -1,10 +1,11 @@ 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_SYS_MALLOC_F_LEN=0x400 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" @@ -93,3 +94,11 @@ 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_BOARD_LATE_INIT=y +CONFIG_FWU_MULTI_BANK_UPDATE=y +CONFIG_DM_FWU_MDATA=y +CONFIG_FWU_MDATA_MTD=y +CONFIG_FWU_NUM_BANKS=2 +CONFIG_FWU_NUM_IMAGES_PER_BANK=1 +CONFIG_CMD_FWU_METADATA=y diff --git a/doc/board/socionext/developerbox.rst b/doc/board/socionext/developerbox.rst index 2d943c23be..f52820c2b0 100644 --- a/doc/board/socionext/developerbox.rst +++ b/doc/board/socionext/developerbox.rst @@ -85,3 +85,99 @@ Once the flasher tool is running we are ready flash the UEFI image::
After transferring the SPI_NOR_UBOOT.fd, turn off the DSW2-7 and reset the board.
+Enable FWU Multi Bank Update +============================
+DeveloperBox supports the FWU Multi Bank Update. You *MUST* update both *SCP firmware* and *TF-A* for this feature. This will change the layout and the boot process but you can switch back to the normal one by changing the DSW 1-4 off.
+Configure U-Boot +----------------
+To enable the FWU Multi Bank Update on the DeveloperBox, you need to add following configurations to configs/synquacer_developerbox_defconfig ::
- CONFIG_FWU_MULTI_BANK_UPDATE=y
- CONFIG_DM_FWU_MDATA=y
- CONFIG_FWU_MDATA_MTD=y
- CONFIG_FWU_NUM_BANKS=2
- CONFIG_FWU_NUM_IMAGES_PER_BANK=1
- CONFIG_CMD_FWU_METADATA=y
+And build it::
+ cd u-boot/ + export ARCH=arm64 + export CROSS_COMPILE=aarch64-linux-gnu- + make synqucer_developerbox_defconfig + make -j `noproc` + cd ../
+By default, the CONFIG_FWU_NUM_BANKS and COFNIG_FWU_NUM_IMAGES_PER_BANKS are set to 2 and 1 respectively. This uses FIP (Firmware Image Package) type image which contains TF-A, U-Boot and OP-TEE (the OP-TEE is optional.) +You can use fiptool to compose the FIP image from those firmware images.
+Rebuild SCP firmware +--------------------
+Rebuild SCP firmware which supports FWU Multi Bank Update as below::
+ cd SCP-firmware/ + OUT=./build/product/synquacer + ROMFW_FILE=$OUT/scp_romfw/$SCP_BUILD_MODE/bin/scp_romfw.bin + RAMFW_FILE=$OUT/scp_ramfw/$SCP_BUILD_MODE/bin/scp_ramfw.bin + ROMRAMFW_FILE=scp_romramfw_release.bin
+ make CC=$ARM_EMB_GCC PRODUCT=synquacer MODE=release + tr "\000" "\377" < /dev/zero | dd of=${ROMRAMFW_FILE} bs=1 count=196608 + dd if=${ROMFW_FILE} of=${ROMRAMFW_FILE} bs=1 conv=notrunc seek=0 + dd if=${RAMFW_FILE} of=${ROMRAMFW_FILE} bs=1 seek=65536 + cd ../
+And you can get the `scp_romramfw_release.bin` file
+Rebuild TF-A and FIP +--------------------
+Rebuild TF-A which supports FWU Multi Bank Update as below::
+ cd arm-trusted-firmware/ + make CROSS_COMPILE=aarch64-linux-gnu- -j`nproc` PLAT=synquacer \ + SPD=opteed SQ_RESET_TO_BL2=1 GENERATE_COT=1 MBEDTLS_DIR=../mbedtls \ + BL33=../u-boot/u-boot.bin all fip fiptool
+And make a FIP image.::
+ cp build/synquacer/release/fip.bin SPI_NOR_NEWFIP.fd + tools/fiptool/fiptool update --tb-fw build/synquacer/release/bl2.bin SPI_NOR_NEWFIP.fd
+UUIDs for the FWU Multi Bank Update +-----------------------------------
+FWU multi-bank update requires some UUIDs. The DeveloperBox platform uses following UUIDs.
- Location UUID for the FIP image: 17e86d77-41f9-4fd7-87ec-a55df9842de5
- Image type UUID for the FIP image:
10c36d7d-ca52-b843-b7b9-f9d6c501d108
- Image UUID for Bank0 : 5a66a702-99fd-4fef-a392-c26e261a2828
- Image UUID for Bank1 : a8f868a1-6e5c-4757-878d-ce63375ef2c0
+These UUIDs are used for making a FWU metadata image.
+Install via flash writer +------------------------
+As explained in above section, the new FIP image and the FWU metadata image can be installed via NOR flash writer. Note that the installation offsets for the FWU multi bank update supported firmware.
+Once the flasher tool is running we are ready flash the images.:: +Write the FIP image to the 0x600000 offset.::
+ flash rawwrite 600000 180000 + >> Send SPI_NOR_NEWFIP.fd via XMODEM (Control-A S in minicom) <<
+And write the new SCP firmware.::
+ flash write cm3 + >> Send scp_romramfw_release.bin via XMODEM (Control-A S in minicom) <<
+At last, turn on the DSW 3-4 on the board, and reboot. +Note that if DSW 3-4 is turned off, the DeveloperBox will boot from +the original EDK2 firmware (or non-FWU U-Boot if you already installed.) diff --git a/include/configs/synquacer.h b/include/configs/synquacer.h index 5686a5b910..7995be852d 100644 --- a/include/configs/synquacer.h +++ b/include/configs/synquacer.h @@ -46,19 +46,29 @@
/* Since U-Boot 64bit PCIe support is limited, disable 64bit MMIO support */
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE +#define DEFAULT_DFU_ALT_INFO +#else #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" +#endif
/* GUIDs for capsule updatable firmware images */ #define DEVELOPERBOX_UBOOT_IMAGE_GUID \ EFI_GUID(0x53a92e83, 0x4ef4, 0x473a, 0x8b, 0x0d, \ 0xb5, 0xd8, 0xc7, 0xb2, 0xd6, 0x00)
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE +#define DEVELOPERBOX_FIP_IMAGE_GUID \ + EFI_GUID(0x7d6dc310, 0x52ca, 0x43b8, 0xb7, 0xb9, \ + 0xf9, 0xd6, 0xc5, 0x01, 0xd1, 0x08) +#else #define DEVELOPERBOX_FIP_IMAGE_GUID \ EFI_GUID(0x880866e9, 0x84ba, 0x4793, 0xa9, 0x08, \ 0x33, 0xe0, 0xb9, 0x16, 0xf3, 0x98) +#endif
#define DEVELOPERBOX_OPTEE_IMAGE_GUID \ EFI_GUID(0xc1b629f1, 0xce0e, 0x4894, 0x82, 0xbf, \

On Thu, 1 Sept 2022 at 02:28, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 7/22/22 19:43, jassisinghbrar@gmail.com wrote:
diff --git a/board/socionext/developerbox/developerbox.c b/board/socionext/developerbox/developerbox.c index f5a5fe0121..ad2260e3d7 100644 --- a/board/socionext/developerbox/developerbox.c +++ b/board/socionext/developerbox/developerbox.c @@ -20,6 +20,13 @@
#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) struct efi_fw_image fw_images[] = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE)
- {
.image_type_id = DEVELOPERBOX_FIP_IMAGE_GUID,
.fw_name = u"DEVELOPERBOX-FIP",
The design is flawed. These fields should be moved to the device-tree.
Currently we are changing C files for each board were we enable firmware updates. Probably an even better place then the device-tree would be a Kconfig file. The only problem with Kconfig is that it does not easily allow to edit arrays. But we could use a string like:
GUID,name,index,GUID,name,index,...
Probably. But there already exists the structure that this patch only adds an entry to. Moving that structure into dt or kconfig should be a separate task of different context. Also right now I don't want to diverge from gpt based STM's implementation which does the same thing.
thanks

hi Jassi,
On Fri, 22 Jul 2022 at 23:12, jassisinghbrar@gmail.com wrote:
From: Jassi Brar jaswinder.singh@linaro.org
The mtd and synquacer (developerbox) support was dropped from v6[1] This patchset re-introduces the support over last v7[2] submission of the patchset.
All the comments on this code over v5 submission have been addressed. Moving forward a changelog will be maintained.
[1] https://lore.kernel.org/all/20220704051658.1085442-1-sughosh.ganu@linaro.org... [2] https://lore.kernel.org/all/20220714183913.118505-1-sughosh.ganu@linaro.org/
Jassi Brar (2): dt: fwu: developerbox: enable fwu banks and mdata regions fwu: DeveloperBox: add support for FWU
Sughosh Ganu (3): dt/bindings: Add bindings for FWU Metadata mtd storage FWU: Add FWU metadata access driver for MTD storage regions FWU: mtd: Add helper functions for accessing FWU metadata
All the above patches have been authored by Masami Hiramatsu. I think you should change the author for these patches. Also, there were a few more patches from Masami, like the documentation for the feature on the Synquacer platform, and addition of the tool, mkfwumdata, for generating the FWU metadata which I do not see in this series. Do you plan to send those separately?
-sughosh
.../synquacer-sc2a11-developerbox-u-boot.dtsi | 22 +- board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 13 + board/socionext/developerbox/fwu_plat.c | 95 ++++++ configs/synquacer_developerbox_defconfig | 13 +- doc/board/socionext/developerbox.rst | 96 ++++++ .../firmware/fwu-mdata-mtd.yaml | 38 +++ drivers/fwu-mdata/Kconfig | 8 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++ include/configs/synquacer.h | 10 + lib/fwu_updates/Makefile | 1 + lib/fwu_updates/fwu_mtd.c | 173 ++++++++++ 13 files changed, 776 insertions(+), 3 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c create mode 100644 lib/fwu_updates/fwu_mtd.c
-- 2.25.1

Hi Sughosh,
On Mon, 25 Jul 2022 at 02:18, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Jassi,
On Fri, 22 Jul 2022 at 23:12, jassisinghbrar@gmail.com wrote:
From: Jassi Brar jaswinder.singh@linaro.org
The mtd and synquacer (developerbox) support was dropped from v6[1] This patchset re-introduces the support over last v7[2] submission of the patchset.
All the comments on this code over v5 submission have been addressed. Moving forward a changelog will be maintained.
[1] https://lore.kernel.org/all/20220704051658.1085442-1-sughosh.ganu@linaro.org... [2] https://lore.kernel.org/all/20220714183913.118505-1-sughosh.ganu@linaro.org/
Jassi Brar (2): dt: fwu: developerbox: enable fwu banks and mdata regions fwu: DeveloperBox: add support for FWU
Sughosh Ganu (3): dt/bindings: Add bindings for FWU Metadata mtd storage FWU: Add FWU metadata access driver for MTD storage regions FWU: mtd: Add helper functions for accessing FWU metadata
All the above patches have been authored by Masami Hiramatsu. I think you should change the author for these patches.
oops... I used the '-c' option to carry over the log from your patches and forgot to update the author. Will do. thanks.
Also, there were a few more patches from Masami, like the documentation for the feature on the Synquacer platform, and addition of the tool, mkfwumdata, for generating the FWU metadata which I do not see in this series. Do you plan to send those separately?
yes.

On 7/22/22 19:42, jassisinghbrar@gmail.com wrote:
From: Jassi Brar jaswinder.singh@linaro.org
The mtd and synquacer (developerbox) support was dropped from v6[1] This patchset re-introduces the support over last v7[2] submission of the patchset.
All the comments on this code over v5 submission have been addressed. Moving forward a changelog will be maintained.
[1] https://lore.kernel.org/all/20220704051658.1085442-1-sughosh.ganu@linaro.org... [2] https://lore.kernel.org/all/20220714183913.118505-1-sughosh.ganu@linaro.org/
This new feature deserves a test that can executed in Gitlab.
Preferably we would test on the sandbox. But testing on QEMU using an emulated flash would be another option.
Best regards
Heinrich
Jassi Brar (2): dt: fwu: developerbox: enable fwu banks and mdata regions fwu: DeveloperBox: add support for FWU
Sughosh Ganu (3): dt/bindings: Add bindings for FWU Metadata mtd storage FWU: Add FWU metadata access driver for MTD storage regions FWU: mtd: Add helper functions for accessing FWU metadata
.../synquacer-sc2a11-developerbox-u-boot.dtsi | 22 +- board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 13 + board/socionext/developerbox/fwu_plat.c | 95 ++++++ configs/synquacer_developerbox_defconfig | 13 +- doc/board/socionext/developerbox.rst | 96 ++++++ .../firmware/fwu-mdata-mtd.yaml | 38 +++ drivers/fwu-mdata/Kconfig | 8 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++ include/configs/synquacer.h | 10 + lib/fwu_updates/Makefile | 1 + lib/fwu_updates/fwu_mtd.c | 173 ++++++++++ 13 files changed, 776 insertions(+), 3 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c create mode 100644 lib/fwu_updates/fwu_mtd.c

On 7/22/22 19:42, jassisinghbrar@gmail.com wrote:
From: Jassi Brar jaswinder.singh@linaro.org
The mtd and synquacer (developerbox) support was dropped from v6[1] This patchset re-introduces the support over last v7[2] submission of the patchset.
So this series should have been marked v8?
All the comments on this code over v5 submission have been addressed. Moving forward a changelog will be maintained.
[1] https://lore.kernel.org/all/20220704051658.1085442-1-sughosh.ganu@linaro.org... [2] https://lore.kernel.org/all/20220714183913.118505-1-sughosh.ganu@linaro.org/
How does this series relate to
[PATCH v8 00/13] FWU: Add FWU Multi Bank Update feature support https://lore.kernel.org/all/20220817124323.375968-1-sughosh.ganu@linaro.org/...
Is it superseded by that series?
This series has "FWU metadata on MTD device without GPT" while the other has "FWU metadata on device with GPT partitioned layout".
Do we need both?
Best regards
Heinrich
Jassi Brar (2): dt: fwu: developerbox: enable fwu banks and mdata regions fwu: DeveloperBox: add support for FWU
Sughosh Ganu (3): dt/bindings: Add bindings for FWU Metadata mtd storage FWU: Add FWU metadata access driver for MTD storage regions FWU: mtd: Add helper functions for accessing FWU metadata
.../synquacer-sc2a11-developerbox-u-boot.dtsi | 22 +- board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 13 + board/socionext/developerbox/fwu_plat.c | 95 ++++++ configs/synquacer_developerbox_defconfig | 13 +- doc/board/socionext/developerbox.rst | 96 ++++++ .../firmware/fwu-mdata-mtd.yaml | 38 +++ drivers/fwu-mdata/Kconfig | 8 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++ include/configs/synquacer.h | 10 + lib/fwu_updates/Makefile | 1 + lib/fwu_updates/fwu_mtd.c | 173 ++++++++++ 13 files changed, 776 insertions(+), 3 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c create mode 100644 lib/fwu_updates/fwu_mtd.c

hello Heinrich,
On Sun, 21 Aug 2022 at 12:46, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 7/22/22 19:42, jassisinghbrar@gmail.com wrote:
From: Jassi Brar jaswinder.singh@linaro.org
The mtd and synquacer (developerbox) support was dropped from v6[1] This patchset re-introduces the support over last v7[2] submission of the patchset.
So this series should have been marked v8?
All the comments on this code over v5 submission have been addressed. Moving forward a changelog will be maintained.
[1] https://lore.kernel.org/all/20220704051658.1085442-1-sughosh.ganu@linaro.org... [2] https://lore.kernel.org/all/20220714183913.118505-1-sughosh.ganu@linaro.org/
How does this series relate to
[PATCH v8 00/13] FWU: Add FWU Multi Bank Update feature support https://lore.kernel.org/all/20220817124323.375968-1-sughosh.ganu@linaro.org/...
Is it superseded by that series?
This series has "FWU metadata on MTD device without GPT" while the other has "FWU metadata on device with GPT partitioned layout".
Do we need both?
Yes, we need both the patch series. The first series([PATCH v8 00/13] FWU: Add FWU Multi Bank Update feature support) adds support for the feature and for platforms with GPT partitioning layout, and is enabling the feature on the ST DK2 board. The second series is adding support for the feature on MTD partition based devices and enabling the feature on the Synquacer board.
-sughosh
Best regards
Heinrich
Jassi Brar (2): dt: fwu: developerbox: enable fwu banks and mdata regions fwu: DeveloperBox: add support for FWU
Sughosh Ganu (3): dt/bindings: Add bindings for FWU Metadata mtd storage FWU: Add FWU metadata access driver for MTD storage regions FWU: mtd: Add helper functions for accessing FWU metadata
.../synquacer-sc2a11-developerbox-u-boot.dtsi | 22 +- board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 13 + board/socionext/developerbox/fwu_plat.c | 95 ++++++ configs/synquacer_developerbox_defconfig | 13 +- doc/board/socionext/developerbox.rst | 96 ++++++ .../firmware/fwu-mdata-mtd.yaml | 38 +++ drivers/fwu-mdata/Kconfig | 8 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++ include/configs/synquacer.h | 10 + lib/fwu_updates/Makefile | 1 + lib/fwu_updates/fwu_mtd.c | 173 ++++++++++ 13 files changed, 776 insertions(+), 3 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c create mode 100644 lib/fwu_updates/fwu_mtd.c
participants (5)
-
Heinrich Schuchardt
-
Ilias Apalodimas
-
Jassi Brar
-
jassisinghbrar@gmail.com
-
Sughosh Ganu