[PATCH v5 00/23] 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.
Earlier, two separate patchsets were being sent. The patchset sent by me was adding support for the feature, and enabling the feature on the ST board. The other set of patches were being sent by Masami Hiramatsu, which were enabling the feature on the Synquacer platform. This patchset contains both set of patches, along with the associated documentation and the python test script for testing the feature.
The upstreaming effort for this feature had been put on a temporary hold to address the fixing of some issues in the capsule update code, primarily using a per platform image GUID for the updatable images. Now that the series has been merged, upstreaming effort for the FWU update feature is being resumed. Hence, this version does not have any review comments being addressed.
[1] - https://developer.arm.com/documentation/den0118/a [2] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e...
Jassi Brar (1): developerbox: synquacer: Use FIP as the updatable image
Masami Hiramatsu (9): FWU: Add FWU metadata access driver for non-GPT MTD devices dt/bindings: firmware: Add FWU metadata on MTD devices binding tools: Add mkfwumdata tool for FWU metadata image FWU: doc: Update documentation for the FWU non-GPT MTD synquacer: Update for TBBR (BL2) based new FIP layout FWU: synquacer: Add FWU Multi bank update support for DeveloperBox FWU: synquacer: Generate dfu_alt_info from devicetree partition doc: synquacer: Add how to enable FWU Multi Bank Update [TEMP]configs: synquacer: Add FWU support for DeveloperBox
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 FWU: doc: Add documentation for the FWU feature sandbox: fwu: Add support for testing FWU feature on sandbox
arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi | 7 + .../synquacer-sc2a11-developerbox-u-boot.dtsi | 40 +- arch/arm/mach-stm32mp/include/mach/stm32.h | 4 + arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 45 +- board/sandbox/sandbox.c | 49 ++ board/socionext/developerbox/Kconfig | 14 + board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 17 +- board/socionext/developerbox/fwu_plat.c | 200 ++++++++ board/st/stm32mp1/stm32mp1.c | 141 ++++++ cmd/Kconfig | 7 + cmd/Makefile | 1 + cmd/fwu_mdata.c | 74 +++ common/board_r.c | 5 + configs/sandbox64_defconfig | 12 +- configs/synquacer_developerbox_defconfig | 10 +- doc/board/socionext/developerbox.rst | 110 +++++ doc/develop/uefi/fwu_updates.rst | 200 ++++++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + .../firmware/fwu-mdata.txt | 18 + .../firmware/uboot,fwu-mdata-mtd.yaml | 38 ++ doc/mkeficapsule.1 | 29 +- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fwu-mdata/Kconfig | 24 + drivers/fwu-mdata/Makefile | 8 + drivers/fwu-mdata/fwu-mdata-uclass.c | 459 ++++++++++++++++++ drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 404 +++++++++++++++ drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++ include/configs/stm32mp15_common.h | 4 + include/configs/synquacer.h | 21 +- include/dm/uclass-id.h | 1 + include/fwu.h | 74 +++ 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 | 206 ++++++++ lib/fwu_updates/fwu_mtd.c | 173 +++++++ .../test_capsule_firmware_fit.py | 1 - .../py/tests/test_fwu_updates/capsule_defs.py | 10 + test/py/tests/test_fwu_updates/conftest.py | 78 +++ .../test_fwu_updates/test_fwu_updates.py | 367 ++++++++++++++ tools/Kconfig | 9 + tools/Makefile | 4 + tools/eficapsule.h | 8 + tools/mkeficapsule.c | 139 +++++- tools/mkfwumdata.c | 298 ++++++++++++ 53 files changed, 3903 insertions(+), 73 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c 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.txt create mode 100644 doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.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 drivers/fwu-mdata/fwu_mdata_mtd.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_mtd.c create mode 100644 test/py/tests/test_fwu_updates/capsule_defs.py create mode 100644 test/py/tests/test_fwu_updates/conftest.py create mode 100644 test/py/tests/test_fwu_updates/test_fwu_updates.py create mode 100644 tools/mkfwumdata.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 --- .../firmware/fwu-mdata.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata.txt
diff --git a/doc/device-tree-bindings/firmware/fwu-mdata.txt b/doc/device-tree-bindings/firmware/fwu-mdata.txt new file mode 100644 index 0000000000..2d8ed056a5 --- /dev/null +++ b/doc/device-tree-bindings/firmware/fwu-mdata.txt @@ -0,0 +1,18 @@ +FWU Metadata Access Devicetree Binding + +The FWU Multi Bank Update feature uses a metadata structure, stored on +a separate partition for keeping information on the set of updatable +images. The device tree node provides information on the storage +device that contains the FWU metadata. + +Required properties : + +- compatible : "u-boot,fwu-mdata-gpt"; +- fwu-mdata-store : should point to the storage device which contains + the FWU metadata partition. + +Example : + fwu-mdata { + compatible = "u-boot,fwu-mdata-gpt"; + fwu-mdata-store = <&sdmmc1>; + };

čt 9. 6. 2022 v 14:30 odesílatel Sughosh Ganu sughosh.ganu@linaro.org napsal:
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
.../firmware/fwu-mdata.txt | 18 ++++++++++++++++++
Why is this in txt file when mtd one is in yaml. doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml
Thanks, Michal

On Thu, 16 Jun 2022 at 19:04, Michal Simek monstr@monstr.eu wrote:
čt 9. 6. 2022 v 14:30 odesílatel Sughosh Ganu sughosh.ganu@linaro.org napsal:
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
.../firmware/fwu-mdata.txt | 18 ++++++++++++++++++
Why is this in txt file when mtd one is in yaml. doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml
The above binding was written by me in txt, while the mtd one is authored by Masami Hiramatsu. I will change this to yaml in the next version.
-sughosh
Thanks, Michal
-- Michal Simek, Ing. (M.Eng), OpenPGP -> KeyID: FE3D1F91 w: www.monstr.eu p: +42-0-721842854 Maintainer of Linux kernel - Xilinx Microblaze Maintainer of Linux kernel - Xilinx Zynq ARM and ZynqMP ARM64 SoCs U-Boot custodian - Xilinx Microblaze/Zynq/ZynqMP/Versal SoCs

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 --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fwu-mdata/Kconfig | 7 + drivers/fwu-mdata/Makefile | 6 + drivers/fwu-mdata/fwu-mdata-uclass.c | 459 +++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/fwu.h | 49 +++ include/fwu_mdata.h | 67 ++++ 8 files changed, 592 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 b26ca8cf70..adc6079ecf 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -42,6 +42,8 @@ source "drivers/firmware/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 67c8af7442..901150bb35 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..7fec7171f4 --- /dev/null +++ b/drivers/fwu-mdata/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# 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..1530ceb01d --- /dev/null +++ b/drivers/fwu-mdata/fwu-mdata-uclass.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022, Linaro Limited + */ + +#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_get_priv(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. + * + * 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. + * + * 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 3ba69ad9a0..7da719c048 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -57,6 +57,7 @@ enum uclass_id { UCLASS_ETH_PHY, /* Ethernet PHY device */ UCLASS_FIRMWARE, /* Firmware */ 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..f9e44e7b39 --- /dev/null +++ b/include/fwu.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * 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..701efbba03 --- /dev/null +++ b/include/fwu_mdata.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * 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_ */

Hello Sughosh,
On Thu, 9 Jun 2022 at 14:30, Sughosh Ganu sughosh.ganu@linaro.org wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, which is stored on a dedicated partition. Add the metadata structure, and 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
drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fwu-mdata/Kconfig | 7 + drivers/fwu-mdata/Makefile | 6 + drivers/fwu-mdata/fwu-mdata-uclass.c | 459 +++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/fwu.h | 49 +++ include/fwu_mdata.h | 67 ++++ 8 files changed, 592 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 b26ca8cf70..adc6079ecf 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -42,6 +42,8 @@ source "drivers/firmware/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 67c8af7442..901150bb35 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..7fec7171f4 --- /dev/null +++ b/drivers/fwu-mdata/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# 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..1530ceb01d --- /dev/null +++ b/drivers/fwu-mdata/fwu-mdata-uclass.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2022, Linaro Limited
- */
+#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_get_priv(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);
s/&image_guid/image_guid/ Ditto in trace below.
} 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.
- Return: 0 if OK, -ve on error
- */
+int fwu_get_mdata(struct fwu_mdata **mdata)
Is there a real need for this function to allocate an instance of struct mdata. I think it would be clearer if it was the caller's responsibility to allocate/free the structure.
Or maybe rename this function fwu_alloc_and_copy_mdata() to highlight that the function gives an allocated copy of the data. One should be careful when calling these API functions as some act on a local copy (retrieved from fw_get_mdata()) while other functions modify straight fwu-mdata in the storage media.
Br, etienne
+{
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.
- 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 3ba69ad9a0..7da719c048 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -57,6 +57,7 @@ enum uclass_id { UCLASS_ETH_PHY, /* Ethernet PHY device */ UCLASS_FIRMWARE, /* Firmware */ 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..f9e44e7b39 --- /dev/null +++ b/include/fwu.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- 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..701efbba03 --- /dev/null +++ b/include/fwu_mdata.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- 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_ */
2.25.1

hi Etienne,
On Tue, 21 Jun 2022 at 16:24, Etienne Carriere etienne.carriere@linaro.org wrote:
Hello Sughosh,
On Thu, 9 Jun 2022 at 14:30, Sughosh Ganu sughosh.ganu@linaro.org wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, which is stored on a dedicated partition. Add the metadata structure, and 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
drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fwu-mdata/Kconfig | 7 + drivers/fwu-mdata/Makefile | 6 + drivers/fwu-mdata/fwu-mdata-uclass.c | 459 +++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/fwu.h | 49 +++ include/fwu_mdata.h | 67 ++++ 8 files changed, 592 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
<snip>
diff --git a/drivers/fwu-mdata/fwu-mdata-uclass.c b/drivers/fwu-mdata/fwu-mdata-uclass.c new file mode 100644 index 0000000000..1530ceb01d --- /dev/null +++ b/drivers/fwu-mdata/fwu-mdata-uclass.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2022, Linaro Limited
- */
+#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_get_priv(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);
s/&image_guid/image_guid/ Ditto in trace below.
Will fix both.
} 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.
- Return: 0 if OK, -ve on error
- */
+int fwu_get_mdata(struct fwu_mdata **mdata)
Is there a real need for this function to allocate an instance of struct mdata. I think it would be clearer if it was the caller's responsibility to allocate/free the structure.
Or maybe rename this function fwu_alloc_and_copy_mdata() to highlight that the function gives an allocated copy of the data.
I guess I can put a comment in the function description saying that the function is responsible for the allocation of the metadata structure.
One should be careful when calling these API functions as some act on a local copy (retrieved from fw_get_mdata()) while other functions modify straight fwu-mdata in the storage media.
Did you find any function which is modifying the metadata on the storage device directly. The API fwu_update_mdata() is supposed to be doing that. If you have come across any function which is directly modifying the metadata on the storage media, please let me know and I will fix it.
-sughosh
Br, etienne
+{
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.
- 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 3ba69ad9a0..7da719c048 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -57,6 +57,7 @@ enum uclass_id { UCLASS_ETH_PHY, /* Ethernet PHY device */ UCLASS_FIRMWARE, /* Firmware */ 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..f9e44e7b39 --- /dev/null +++ b/include/fwu.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- 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..701efbba03 --- /dev/null +++ b/include/fwu_mdata.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- 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_ */
2.25.1

Hi Sughosh,
On Thu, 23 Jun 2022 at 08:24, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Etienne,
On Tue, 21 Jun 2022 at 16:24, Etienne Carriere etienne.carriere@linaro.org wrote:
Hello Sughosh,
On Thu, 9 Jun 2022 at 14:30, Sughosh Ganu sughosh.ganu@linaro.org wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, which is stored on a dedicated partition. Add the metadata structure, and 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
drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fwu-mdata/Kconfig | 7 + drivers/fwu-mdata/Makefile | 6 + drivers/fwu-mdata/fwu-mdata-uclass.c | 459 +++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/fwu.h | 49 +++ include/fwu_mdata.h | 67 ++++ 8 files changed, 592 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
<snip>
diff --git a/drivers/fwu-mdata/fwu-mdata-uclass.c b/drivers/fwu-mdata/fwu-mdata-uclass.c new file mode 100644 index 0000000000..1530ceb01d --- /dev/null +++ b/drivers/fwu-mdata/fwu-mdata-uclass.c
<snip>
+/**
- fwu_get_mdata() - Get a FWU metadata copy
- @mdata: Copy of the FWU metadata
- Get a valid copy of the FWU metadata.
- Return: 0 if OK, -ve on error
- */
+int fwu_get_mdata(struct fwu_mdata **mdata)
Is there a real need for this function to allocate an instance of struct mdata. I think it would be clearer if it was the caller's responsibility to allocate/free the structure.
Or maybe rename this function fwu_alloc_and_copy_mdata() to highlight that the function gives an allocated copy of the data.
I guess I can put a comment in the function description saying that the function is responsible for the allocation of the metadata structure.
I think it would be better.
One should be careful when calling these API functions as some act on a local copy (retrieved from fw_get_mdata()) while other functions modify straight fwu-mdata in the storage media.
Did you find any function which is modifying the metadata on the storage device directly. The API fwu_update_mdata() is supposed to be doing that. If you have come across any function which is directly modifying the metadata on the storage media, please let me know and I will fix it.
Many functions do so: fwu_clear_accept_image(), fwu_clear_accept_image(), fwu_resert_boot_index(), etc... Actually all generic functions do so while only fwu_get_mdata() and fwu_update_mdata() act on a RAM copy.
Maybe fwu-mdata ops should have a status field for when a RAM copy was exported and used to prevent direct updates to mdata in storage until caller releases (fw_put_mdata()?) the exposed copy. Would this scheme be overkilling...
Or maybe fwu_clear_accept_image() and other helper functions could also require a mdata RAM reference to act on, letting the caller also go through fwu_get_mdata()/fwu_update_mdata().
etienne
<snip>

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 --- drivers/fwu-mdata/Kconfig | 9 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 404 ++++++++++++++++++++++++++ include/fwu.h | 2 + 4 files changed, 416 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 7fec7171f4..12a5b4fe04 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..329bd3779b --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_gpt_blk.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022, Linaro Limited + */ + +#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; + + desc = dev_get_uclass_plat(dev_get_priv(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; + + desc = dev_get_uclass_plat(dev_get_priv(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; +} + +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); +} + +int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata **mdata) +{ + struct blk_desc *desc; + + desc = dev_get_uclass_plat(dev_get_priv(dev)); + if (!desc) { + log_err("Block device not found\n"); + return -ENODEV; + } + + return gpt_get_mdata(desc, mdata); +} + +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 = 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), + &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; + + ret = fwu_get_mdata_device(dev, &mdata_dev); + if (ret) + return ret; + + dev_set_priv(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, +}; diff --git a/include/fwu.h b/include/fwu.h index f9e44e7b39..3b1ee4e83e 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -39,6 +39,8 @@ 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_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev); +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);

Hi,
On 6/9/22 14:29, Sughosh Ganu wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, on a separate partition. Add 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
drivers/fwu-mdata/Kconfig | 9 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 404 ++++++++++++++++++++++++++ include/fwu.h | 2 + 4 files changed, 416 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 7fec7171f4..12a5b4fe04 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
It is strange to have '_' and '-' in file name for the same directory
=> to be coherent = fwu-mdata-gpt-blk.c
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..329bd3779b --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_gpt_blk.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2022, Linaro Limited
- */
+#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)
[...]
+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);
+}
+int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata **mdata) +{
- struct blk_desc *desc;
- desc = dev_get_uclass_plat(dev_get_priv(dev));
as dev = fwu_mdata_gpt_blk(UCLASS_FWU_MDATA)
dev_get_priv(dev) => get value saved in dev_set_priv(dev, mdata_dev);
even if it is OK, it not clear here.
can you add a struct to prepare addition of other elements in privdata:
struct fwu_mdata_gpt_blk_priv { struct udevice *blk_dev; }
+ struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
+ struct blk_desc *desc;
+ 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);
+}
+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 = ofnode_get_property(dev_ofnode(dev), "fwu-mdata-store",
&size);
phandle_p = dev_read_prop(dev, "fwu-mdata-store", &size);
it is more simple
- if (!phandle_p) {
log_err("fwu-mdata-store property not found\n");
return -ENOENT;
- }
- phandle = fdt32_to_cpu(*phandle_p);
or phandle can be read directly by
+ ret =dev_read_phandle_with_args(dev, "fwu-mdata-store", NULL, 0, 0, phandle_p)
+ if (ret) { + log_err("fwu-mdata-store property not found\n"); + return ret; + }
- 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;
- ret = fwu_get_mdata_device(dev, &mdata_dev);
- if (ret)
return ret;
- dev_set_priv(dev, mdata_dev);
Avoid to use dev_set_priv() in driver :
/** * dev_set_priv() - Set the private data for a device * * This is normally handled by driver model, which automatically allocates * private data when an 'auto' size if provided by the driver. * * Use this function to override normal operation for special situations, such * as needing to allocate a variable amount of data. *
...
+ struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
+ 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 f9e44e7b39..3b1ee4e83e 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -39,6 +39,8 @@ 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_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev); +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);
Regards
Patrick

Hi
On 6/21/22 11:34, Patrick DELAUNAY wrote:
Hi,
On 6/9/22 14:29, Sughosh Ganu wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, on a separate partition. Add 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
drivers/fwu-mdata/Kconfig | 9 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 404 ++++++++++++++++++++++++++ include/fwu.h | 2 + 4 files changed, 416 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 7fec7171f4..12a5b4fe04 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
It is strange to have '_' and '-' in file name for the same directory
=> to be coherent = fwu-mdata-gpt-blk.c
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..329bd3779b --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_gpt_blk.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2022, Linaro Limited
- */
+ #define LOG_CATEGORY UCLASS_FWU_MDATA
For command log filtering by uclass
+#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>
[...]
Regards
Patrick

hi Patrick, Apologies for the late reply. I had missed out replying to the review comments on this patch. There are some review comments on the Synquacer patches which need to be taken care of by another engineer. Once those review comments are taken care of, I will be sending the next version.
On Tue, 21 Jun 2022 at 15:04, Patrick DELAUNAY patrick.delaunay@foss.st.com wrote:
Hi,
On 6/9/22 14:29, Sughosh Ganu wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, on a separate partition. Add 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
drivers/fwu-mdata/Kconfig | 9 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 404 ++++++++++++++++++++++++++ include/fwu.h | 2 + 4 files changed, 416 insertions(+) create mode 100644 drivers/fwu-mdata/fwu_mdata_gpt_blk.c
<snip>
diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile index 7fec7171f4..12a5b4fe04 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
It is strange to have '_' and '-' in file name for the same directory
=> to be coherent = fwu-mdata-gpt-blk.c
I see this kind of naming style in many other directories under drivers/. The uclass file is named using the foo-uclass.c, while the other driver files are named bar_driver.c
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..329bd3779b --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_gpt_blk.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2022, Linaro Limited
- */
+#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)
[...]
+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);
+}
+int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata **mdata) +{
struct blk_desc *desc;
desc = dev_get_uclass_plat(dev_get_priv(dev));
as dev = fwu_mdata_gpt_blk(UCLASS_FWU_MDATA)
dev_get_priv(dev) => get value saved in dev_set_priv(dev, mdata_dev);
even if it is OK, it not clear here.
can you add a struct to prepare addition of other elements in privdata:
struct fwu_mdata_gpt_blk_priv { struct udevice *blk_dev; }
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
struct blk_desc *desc;
desc = dev_get_uclass_plat(priv->blk_dev);
Okay. Will add a priv structure as you suggest.
if (!desc) {
log_err("Block device not found\n");
return -ENODEV;
}
return gpt_get_mdata(desc, mdata);
+}
+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 = ofnode_get_property(dev_ofnode(dev), "fwu-mdata-store",
&size);
phandle_p = dev_read_prop(dev, "fwu-mdata-store", &size);
it is more simple
Okay
if (!phandle_p) {
log_err("fwu-mdata-store property not found\n");
return -ENOENT;
}
phandle = fdt32_to_cpu(*phandle_p);
or phandle can be read directly by
- ret =dev_read_phandle_with_args(dev, "fwu-mdata-store", NULL, 0, 0,
phandle_p)
I did not understand this review comment properly. In any case, I am using the API, dev_read_prop that you suggested above to read the phandle pointer.
if (ret) {
log_err("fwu-mdata-store property not found\n");
return ret;
}
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;
ret = fwu_get_mdata_device(dev, &mdata_dev);
if (ret)
return ret;
dev_set_priv(dev, mdata_dev);
Avoid to use dev_set_priv() in driver :
/**
- dev_set_priv() - Set the private data for a device
- This is normally handled by driver model, which automatically allocates
- private data when an 'auto' size if provided by the driver.
- Use this function to override normal operation for special
situations, such
- as needing to allocate a variable amount of data.
...
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
priv->blk_dev = mdata_dev;
Okay
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),
Okay. I have incorporated these review comments. Thanks.
-sughosh
+}; diff --git a/include/fwu.h b/include/fwu.h index f9e44e7b39..3b1ee4e83e 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -39,6 +39,8 @@ 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_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev); +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);
Regards
Patrick

Hello Sughosh,
On Thu, 9 Jun 2022 at 14:30, Sughosh Ganu sughosh.ganu@linaro.org wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, on a separate partition. Add 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
drivers/fwu-mdata/Kconfig | 9 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 404 ++++++++++++++++++++++++++ include/fwu.h | 2 + 4 files changed, 416 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 7fec7171f4..12a5b4fe04 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..329bd3779b --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_gpt_blk.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2022, Linaro Limited
- */
+#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;
desc = dev_get_uclass_plat(dev_get_priv(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)
Upon success, I think this function should also either ensure secondary_part contains a valid copy of primary part, Maybe this function should call gpt_check_mdata_validity() and then read mdata content.
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;
desc = dev_get_uclass_plat(dev_get_priv(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");
I think this function should select one of the part and copies to the other, e.g. assume primary is fine, copy primary to secondary and return upon success.
}
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;
+}
+int fwu_gpt_mdata_check(struct udevice *dev)
Function should be static.
+{
/*
* 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);
+}
+int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata **mdata)
Function should be static.
+{
struct blk_desc *desc;
desc = dev_get_uclass_plat(dev_get_priv(dev));
if (!desc) {
log_err("Block device not found\n");
return -ENODEV;
}
return gpt_get_mdata(desc, mdata);
+}
+int fwu_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev)
Function could be static (and declaration removed from fwu.h).
+{
u32 phandle;
int ret, size;
struct udevice *parent, *child;
const fdt32_t *phandle_p = NULL;
phandle_p = ofnode_get_property(dev_ofnode(dev), "fwu-mdata-store",
&size);
Should this be retrieved from driver's ::of_to_plat method?
Br, etienne
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;
ret = fwu_get_mdata_device(dev, &mdata_dev);
if (ret)
return ret;
dev_set_priv(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,
+}; diff --git a/include/fwu.h b/include/fwu.h index f9e44e7b39..3b1ee4e83e 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -39,6 +39,8 @@ 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_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev); +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); -- 2.25.1

hi Etienne,
On Tue, 21 Jun 2022 at 16:26, Etienne Carriere etienne.carriere@linaro.org wrote:
Hello Sughosh,
On Thu, 9 Jun 2022 at 14:30, Sughosh Ganu sughosh.ganu@linaro.org wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, on a separate partition. Add 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
drivers/fwu-mdata/Kconfig | 9 + drivers/fwu-mdata/Makefile | 1 + drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 404 ++++++++++++++++++++++++++ include/fwu.h | 2 + 4 files changed, 416 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 7fec7171f4..12a5b4fe04 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..329bd3779b --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_gpt_blk.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2022, Linaro Limited
- */
+#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)
<snip>
+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)
Upon success, I think this function should also either ensure secondary_part contains a valid copy of primary part, Maybe this function should call gpt_check_mdata_validity() and then read mdata content.
Okay
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;
desc = dev_get_uclass_plat(dev_get_priv(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");
I think this function should select one of the part and copies to the other, e.g. assume primary is fine, copy primary to secondary and return upon success.
In this case, I think there is some kind of an unexpected scenario which has resulted in both the metadata partitions being valid but not being the same. Should this not be flagged as an error for the user to handle? Moreover, which metadata partition do we assume to be the correct one . We are just calling one partition as primary, and another one secondary. But the spec does not give any more importance to one over the other -- the spec just says that both the copies should be the same.
}
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;
+}
+int fwu_gpt_mdata_check(struct udevice *dev)
Function should be static.
Will change all the relevant functions as per your comment.
+{
/*
* 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);
+}
+int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata **mdata)
Function should be static.
+{
struct blk_desc *desc;
desc = dev_get_uclass_plat(dev_get_priv(dev));
if (!desc) {
log_err("Block device not found\n");
return -ENODEV;
}
return gpt_get_mdata(desc, mdata);
+}
+int fwu_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev)
Function could be static (and declaration removed from fwu.h).
Okay
+{
u32 phandle;
int ret, size;
struct udevice *parent, *child;
const fdt32_t *phandle_p = NULL;
phandle_p = ofnode_get_property(dev_ofnode(dev), "fwu-mdata-store",
&size);
Should this be retrieved from driver's ::of_to_plat method?
Patrick has suggested another API for getting the phandle. Thanks.
-sughosh
Br, etienne
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;
ret = fwu_get_mdata_device(dev, &mdata_dev);
if (ret)
return ret;
dev_set_priv(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,
+}; diff --git a/include/fwu.h b/include/fwu.h index f9e44e7b39..3b1ee4e83e 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -39,6 +39,8 @@ 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_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev); +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); -- 2.25.1

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

Hi,
On 6/9/22 14:29, Sughosh Ganu 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
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>;
- };
+};
Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com
Thanks Patrick

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 --- 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 07b1a63db7..62d98ad776 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> @@ -92,6 +93,16 @@ DECLARE_GLOBAL_DATA_PTR; #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 */ @@ -675,6 +686,14 @@ int board_init(void)
setup_led(LEDST_ON);
+#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) + if (board_is_dk2()) { + efi_guid_t image_type_guid = STM32MP1_DK2_FIP_IMAGE_GUID; + guidcpy(&fw_images[0].image_type_id, &image_type_guid); + fw_images[0].fw_name = u"STM32MP1-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 6b40cdb017..6a1ae9788d 100644 --- a/include/configs/stm32mp15_common.h +++ b/include/configs/stm32mp15_common.h @@ -54,6 +54,10 @@ #define CONFIG_SYS_AUTOLOAD "no" #endif
+#define STM32MP1_DK2_FIP_IMAGE_GUID \ + EFI_GUID(0x19d5df83, 0x11b0, 0x457b, 0xbe, 0x2c, \ + 0x75, 0x59, 0xc1, 0x31, 0x42, 0xa5) + /*****************************************************************************/ #ifdef CONFIG_DISTRO_DEFAULTS /*****************************************************************************/

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 --- board/st/stm32mp1/stm32mp1.c | 115 +++++++++++++++++++++++++++++++++++ include/fwu.h | 2 + 2 files changed, 117 insertions(+)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 62d98ad776..e68bf09955 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> @@ -967,3 +971,114 @@ 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> + +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; +} + +static int gpt_plat_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid, + int *alt_num) +{ + 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_MMC) + 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; +} + +int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, + int *alt_num) +{ + struct blk_desc *desc; + + desc = dev_get_uclass_plat(dev); + if (!desc) { + log_err("Block device not found\n"); + return -ENODEV; + } + + return gpt_plat_get_alt_num(desc, image_guid, alt_num); +} + +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 3b1ee4e83e..36e58afa29 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -46,6 +46,8 @@ 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); +int fwu_plat_get_update_index(u32 *update_idx); #endif /* _FWU_H_ */

Hi Sughosh,
On Thu, Jun 09, 2022 at 05:59:53PM +0530, Sughosh Ganu 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
board/st/stm32mp1/stm32mp1.c | 115 +++++++++++++++++++++++++++++++++++ include/fwu.h | 2 + 2 files changed, 117 insertions(+)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 62d98ad776..e68bf09955 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> @@ -967,3 +971,114 @@ 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>
+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;
+}
+static int gpt_plat_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid,
int *alt_num)
Does this really need to be defined per platform?
Most of the stuff in here are generic apart from the info of were the metadata is stored. So wouldn't it better to move this in the generic API and add an argument for the dfu device type?
The platform portion would then just call this function with an extra arg e.g DFU_DEV_MMC
+{
- 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);
[...]
Regards /Ilias

hi Ilias,
On Fri, 10 Jun 2022 at 17:23, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Sughosh,
On Thu, Jun 09, 2022 at 05:59:53PM +0530, Sughosh Ganu 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
board/st/stm32mp1/stm32mp1.c | 115 +++++++++++++++++++++++++++++++++++ include/fwu.h | 2 + 2 files changed, 117 insertions(+)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 62d98ad776..e68bf09955 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> @@ -967,3 +971,114 @@ 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>
+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;
+}
+static int gpt_plat_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid,
int *alt_num)
Does this really need to be defined per platform?
Most of the stuff in here are generic apart from the info of were the metadata is stored. So wouldn't it better to move this in the generic API and add an argument for the dfu device type?
The platform portion would then just call this function with an extra arg e.g DFU_DEV_MMC
Yes, it makes sense. I will make the change that you suggest. Thanks.
-sughosh
+{
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);
[...]
Regards /Ilias

Hi,
On 6/9/22 14:29, Sughosh Ganu 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
board/st/stm32mp1/stm32mp1.c | 115 +++++++++++++++++++++++++++++++++++ include/fwu.h | 2 + 2 files changed, 117 insertions(+)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 62d98ad776..e68bf09955 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> @@ -967,3 +971,114 @@ 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>
[...]
+#endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/include/fwu.h b/include/fwu.h index 3b1ee4e83e..36e58afa29 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -46,6 +46,8 @@ 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);
Added empty line
int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, int *alt_num); +int fwu_plat_get_update_index(u32 *update_idx); #endif /* _FWU_H_ */
And I am agree with Ilias remark, should be generic
=> search on the current UCLASS_FWU_MDATA
perhaps need a new ops in u-class ? as implementation can be different for GPT and MTD.
Patrick

On Tue, 21 Jun 2022 at 15:19, Patrick DELAUNAY patrick.delaunay@foss.st.com wrote:
Hi,
On 6/9/22 14:29, Sughosh Ganu 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
board/st/stm32mp1/stm32mp1.c | 115 +++++++++++++++++++++++++++++++++++ include/fwu.h | 2 + 2 files changed, 117 insertions(+)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 62d98ad776..e68bf09955 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> @@ -967,3 +971,114 @@ 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>
[...]
+#endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/include/fwu.h b/include/fwu.h index 3b1ee4e83e..36e58afa29 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -46,6 +46,8 @@ 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);
Added empty line
Will remove
int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, int *alt_num); +int fwu_plat_get_update_index(u32 *update_idx); #endif /* _FWU_H_ */
And I am agree with Ilias remark, should be generic
=> search on the current UCLASS_FWU_MDATA
perhaps need a new ops in u-class ? as implementation can be
different for GPT and MTD.
My understanding of Ilias's comments was that the function can be generic for all GPT based platforms. But I will check if this can be reused for both GPT and MTD devices, on the lines that you mention above. Thanks.
-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 --- arch/arm/mach-stm32mp/include/mach/stm32.h | 4 ++++ board/st/stm32mp1/stm32mp1.c | 7 +++++++ include/fwu.h | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index 47e88fc3dc..40995ee142 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -100,6 +100,7 @@ enum boot_device { #define TAMP_BACKUP_REGISTER(x) (STM32_TAMP_BASE + 0x100 + 4 * x) #define TAMP_BACKUP_MAGIC_NUMBER TAMP_BACKUP_REGISTER(4) #define TAMP_BACKUP_BRANCH_ADDRESS TAMP_BACKUP_REGISTER(5) +#define TAMP_FWU_BOOT_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) @@ -118,6 +119,9 @@ enum boot_device { #define TAMP_BOOT_INSTANCE_MASK GENMASK(3, 0) #define TAMP_BOOT_FORCED_MASK GENMASK(7, 0) #define TAMP_BOOT_DEBUG_ON BIT(16) +#define TAMP_FWU_BOOT_IDX_MASK GENMASK(3, 0) + +#define TAMP_FWU_BOOT_IDX_OFFSET 0
enum forced_boot_mode { BOOT_NORMAL = 0x00, diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index e68bf09955..dff41ed6f6 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -1081,4 +1081,11 @@ 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 36e58afa29..41774ff9e2 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -46,7 +46,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_plat_get_update_index(u32 *update_idx);

That's looks ok to me, but I'd rather ST people to have a look
On Thu, 9 Jun 2022 at 15:31, 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
arch/arm/mach-stm32mp/include/mach/stm32.h | 4 ++++ board/st/stm32mp1/stm32mp1.c | 7 +++++++ include/fwu.h | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index 47e88fc3dc..40995ee142 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -100,6 +100,7 @@ enum boot_device { #define TAMP_BACKUP_REGISTER(x) (STM32_TAMP_BASE + 0x100 + 4 * x) #define TAMP_BACKUP_MAGIC_NUMBER TAMP_BACKUP_REGISTER(4) #define TAMP_BACKUP_BRANCH_ADDRESS TAMP_BACKUP_REGISTER(5) +#define TAMP_FWU_BOOT_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) @@ -118,6 +119,9 @@ enum boot_device { #define TAMP_BOOT_INSTANCE_MASK GENMASK(3, 0) #define TAMP_BOOT_FORCED_MASK GENMASK(7, 0) #define TAMP_BOOT_DEBUG_ON BIT(16) +#define TAMP_FWU_BOOT_IDX_MASK GENMASK(3, 0)
+#define TAMP_FWU_BOOT_IDX_OFFSET 0
enum forced_boot_mode { BOOT_NORMAL = 0x00, diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index e68bf09955..dff41ed6f6 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -1081,4 +1081,11 @@ 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 36e58afa29..41774ff9e2 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -46,7 +46,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_plat_get_update_index(u32 *update_idx); -- 2.25.1
Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org

Hi,
On 6/9/22 14:29, Sughosh Ganu wrote:
The FWU Multi Bank Update feature allows the platform to boot the firmware images from one of the partitions(banks). The first stage bootloader(fsbl) passes the value of the boot index, i.e. the bank from which the firmware images were booted from to U-Boot. On the STM32MP157C-DK2 board, this value is passed through one of the SoC's backup register. Add a function to read the boot index value from the backup register.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
arch/arm/mach-stm32mp/include/mach/stm32.h | 4 ++++ board/st/stm32mp1/stm32mp1.c | 7 +++++++ include/fwu.h | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index 47e88fc3dc..40995ee142 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -100,6 +100,7 @@ enum boot_device { #define TAMP_BACKUP_REGISTER(x) (STM32_TAMP_BASE + 0x100 + 4 * x) #define TAMP_BACKUP_MAGIC_NUMBER TAMP_BACKUP_REGISTER(4) #define TAMP_BACKUP_BRANCH_ADDRESS TAMP_BACKUP_REGISTER(5) +#define TAMP_FWU_BOOT_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) @@ -118,6 +119,9 @@ enum boot_device { #define TAMP_BOOT_INSTANCE_MASK GENMASK(3, 0) #define TAMP_BOOT_FORCED_MASK GENMASK(7, 0) #define TAMP_BOOT_DEBUG_ON BIT(16) +#define TAMP_FWU_BOOT_IDX_MASK GENMASK(3, 0)
+#define TAMP_FWU_BOOT_IDX_OFFSET 0
please don't mix the 2 TAMP_FWU defines with define and enum for TAMP_BOOT
=> move the 2 defines before TAMP_COPRO defines.
#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
enum forced_boot_mode { BOOT_NORMAL = 0x00, diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index e68bf09955..dff41ed6f6 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -1081,4 +1081,11 @@ 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 36e58afa29..41774ff9e2 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -46,7 +46,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_plat_get_update_index(u32 *update_idx);
With the modifications:
Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com
Thanks Patrick

On Tue, 21 Jun 2022 at 16:57, Patrick DELAUNAY patrick.delaunay@foss.st.com wrote:
Hi,
On 6/9/22 14:29, Sughosh Ganu wrote:
The FWU Multi Bank Update feature allows the platform to boot the firmware images from one of the partitions(banks). The first stage bootloader(fsbl) passes the value of the boot index, i.e. the bank from which the firmware images were booted from to U-Boot. On the STM32MP157C-DK2 board, this value is passed through one of the SoC's backup register. Add a function to read the boot index value from the backup register.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
arch/arm/mach-stm32mp/include/mach/stm32.h | 4 ++++ board/st/stm32mp1/stm32mp1.c | 7 +++++++ include/fwu.h | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index 47e88fc3dc..40995ee142 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -100,6 +100,7 @@ enum boot_device { #define TAMP_BACKUP_REGISTER(x) (STM32_TAMP_BASE + 0x100 + 4 * x) #define TAMP_BACKUP_MAGIC_NUMBER TAMP_BACKUP_REGISTER(4) #define TAMP_BACKUP_BRANCH_ADDRESS TAMP_BACKUP_REGISTER(5) +#define TAMP_FWU_BOOT_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) @@ -118,6 +119,9 @@ enum boot_device { #define TAMP_BOOT_INSTANCE_MASK GENMASK(3, 0) #define TAMP_BOOT_FORCED_MASK GENMASK(7, 0) #define TAMP_BOOT_DEBUG_ON BIT(16) +#define TAMP_FWU_BOOT_IDX_MASK GENMASK(3, 0)
+#define TAMP_FWU_BOOT_IDX_OFFSET 0
please don't mix the 2 TAMP_FWU defines with define and enum for TAMP_BOOT
=> move the 2 defines before TAMP_COPRO defines.
#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
Will change as per your suggestion. Thanks.
-sughosh
#define TAMP_COPRO_STATE_OFF 0 #define TAMP_COPRO_STATE_INIT 1
enum forced_boot_mode { BOOT_NORMAL = 0x00, diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index e68bf09955..dff41ed6f6 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -1081,4 +1081,11 @@ 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 36e58afa29..41774ff9e2 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -46,7 +46,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_plat_get_update_index(u32 *update_idx);
With the modifications:
Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com
Thanks Patrick

The FWU Multi Bank Update specification requires the Update Agent to carry out certain checks at the time of platform boot. The Update Agent is the component which is responsible for updating the firmware components and maintaining and keeping the metadata in sync.
The spec requires that the Update Agent perform the following checks at the time of boot * Sanity check of both the metadata copies maintained by the platform. * Get the boot index passed to U-Boot by the prior stage bootloader and use this value for metadata bookkeeping. * Check if the system is booting in Trial State. If the system boots in the Trial State for more than a specified number of boot counts, change the Active Bank to be booting the platform from.
Add these checks in the board initialisation sequence, invoked after relocation.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
diff --git a/common/board_r.c b/common/board_r.c index 6f4aca2077..33a600715d 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> @@ -797,6 +798,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 41774ff9e2..8fbd91b463 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -33,6 +33,9 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+int fwu_boottime_checks(void); +u8 fwu_update_checks_pass(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..af884439fb --- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 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(L"TrialStateCtr", + &efi_global_variable_guid, + &var_attributes, + &var_size, &trial_state_ctr, + NULL); + if (status != EFI_SUCCESS) { + log_err("Unable to read TrialStateCtr variable\n"); + ret = -1; + goto out; + } + + ++trial_state_ctr; + if (trial_state_ctr > CONFIG_FWU_TRIAL_STATE_CNT) { + log_info("Trial State count exceeded. Revert back to previous_active_index\n"); + active_idx = mdata->active_index; + ret = fwu_revert_boot_index(); + if (ret) { + log_err("Unable to revert active_index\n"); + goto out; + } + + trial_state_ctr = 0; + status = efi_set_variable_int(L"TrialStateCtr", + &efi_global_variable_guid, + var_attributes, + 0, + &trial_state_ctr, false); + if (status != EFI_SUCCESS) { + log_err("Unable to clear TrialStateCtr variable\n"); + ret = -1; + goto out; + } + } else { + status = efi_set_variable_int(L"TrialStateCtr", + &efi_global_variable_guid, + var_attributes, + var_size, + &trial_state_ctr, false); + if (status != EFI_SUCCESS) { + log_err("Unable to increment TrialStateCtr variable\n"); + ret = -1; + goto out; + } + } + } else { + trial_state_ctr = 0; + status = efi_set_variable_int(L"TrialStateCtr", + &efi_global_variable_guid, + 0, + 0, &trial_state_ctr, + NULL); + } + +out: + free(mdata); + return ret; +} + +u8 fwu_update_checks_pass(void) +{ + return !trial_state && boottime_check; +} + +int fwu_boottime_checks(void) +{ + int ret; + struct udevice *dev; + u32 boot_idx, active_idx; + + if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) { + log_err("FWU Metadata device not found\n"); + return 0; + } + + 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; +}

On 6/9/22 14:29, Sughosh Ganu 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
common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
diff --git a/common/board_r.c b/common/board_r.c index 6f4aca2077..33a600715d 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> @@ -797,6 +798,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 41774ff9e2..8fbd91b463 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -33,6 +33,9 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+int fwu_boottime_checks(void); +u8 fwu_update_checks_pass(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..af884439fb --- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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(L"TrialStateCtr",
&efi_global_variable_guid,
&var_attributes,
&var_size, &trial_state_ctr,
NULL);
if (status != EFI_SUCCESS) {
log_err("Unable to read TrialStateCtr variable\n");
ret = -1;
goto out;
}
++trial_state_ctr;
if (trial_state_ctr > CONFIG_FWU_TRIAL_STATE_CNT) {
log_info("Trial State count exceeded. Revert back to previous_active_index\n");
active_idx = mdata->active_index;
ret = fwu_revert_boot_index();
if (ret) {
log_err("Unable to revert active_index\n");
goto out;
}
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
0,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to clear TrialStateCtr variable\n");
ret = -1;
goto out;
}
} else {
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
var_size,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to increment TrialStateCtr variable\n");
ret = -1;
goto out;
}
}
- } else {
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
0,
0, &trial_state_ctr,
NULL);
- }
+out:
- free(mdata);
- return ret;
+}
+u8 fwu_update_checks_pass(void) +{
- return !trial_state && boottime_check;
+}
+int fwu_boottime_checks(void) +{
- int ret;
- struct udevice *dev;
- u32 boot_idx, active_idx;
- if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) {
log_err("FWU Metadata device not found\n");
return 0;
- }
- 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)
efi_init_obj_list() slows down the boot process. Why do want to invoke it if no EFI binary is launched?
See also Takahiro's hint in https://lore.kernel.org/all/20220615061616.GD58082@laputa/
Best regards
Heinrich
return 0;
- ret = fwu_trial_state_check();
- if (!ret)
boottime_check = 1;
- return 0;
+}

On Wed, Jun 15, 2022 at 08:34:18AM +0200, Heinrich Schuchardt wrote:
On 6/9/22 14:29, Sughosh Ganu 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
common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
diff --git a/common/board_r.c b/common/board_r.c index 6f4aca2077..33a600715d 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> @@ -797,6 +798,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 41774ff9e2..8fbd91b463 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -33,6 +33,9 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+int fwu_boottime_checks(void); +u8 fwu_update_checks_pass(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..af884439fb --- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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(L"TrialStateCtr",
&efi_global_variable_guid,
&var_attributes,
&var_size, &trial_state_ctr,
NULL);
if (status != EFI_SUCCESS) {
log_err("Unable to read TrialStateCtr variable\n");
ret = -1;
goto out;
}
++trial_state_ctr;
if (trial_state_ctr > CONFIG_FWU_TRIAL_STATE_CNT) {
log_info("Trial State count exceeded. Revert back to previous_active_index\n");
active_idx = mdata->active_index;
ret = fwu_revert_boot_index();
if (ret) {
log_err("Unable to revert active_index\n");
goto out;
}
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
0,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to clear TrialStateCtr variable\n");
ret = -1;
goto out;
}
} else {
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
var_size,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to increment TrialStateCtr variable\n");
ret = -1;
goto out;
}
}
- } else {
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
0,
0, &trial_state_ctr,
NULL);
- }
+out:
- free(mdata);
- return ret;
+}
+u8 fwu_update_checks_pass(void) +{
- return !trial_state && boottime_check;
+}
+int fwu_boottime_checks(void) +{
- int ret;
- struct udevice *dev;
- u32 boot_idx, active_idx;
- if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) {
log_err("FWU Metadata device not found\n");
return 0;
- }
- 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)
efi_init_obj_list() slows down the boot process. Why do want to invoke it if no EFI binary is launched?
Because fwu_boottime_checks() must access UEFI variables.
-Takahiro Akashi
See also Takahiro's hint in https://lore.kernel.org/all/20220615061616.GD58082@laputa/
Best regards
Heinrich
return 0;
- ret = fwu_trial_state_check();
- if (!ret)
boottime_check = 1;
- return 0;
+}

Hi Sughosh,
On Thu, 9 Jun 2022 at 14:31, 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
common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
diff --git a/common/board_r.c b/common/board_r.c index 6f4aca2077..33a600715d 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> @@ -797,6 +798,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 41774ff9e2..8fbd91b463 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -33,6 +33,9 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+int fwu_boottime_checks(void); +u8 fwu_update_checks_pass(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..af884439fb --- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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(L"TrialStateCtr",
&efi_global_variable_guid,
&var_attributes,
&var_size, &trial_state_ctr,
NULL);
if (status != EFI_SUCCESS) {
log_err("Unable to read TrialStateCtr variable\n");
ret = -1;
goto out;
}
++trial_state_ctr;
if (trial_state_ctr > CONFIG_FWU_TRIAL_STATE_CNT) {
log_info("Trial State count exceeded. Revert back to previous_active_index\n");
active_idx = mdata->active_index;
ret = fwu_revert_boot_index();
if (ret) {
log_err("Unable to revert active_index\n");
goto out;
}
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
0,
s/0/var_size/ ? Ditto 24 lines below.
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to clear TrialStateCtr variable\n");
ret = -1;
goto out;
}
} else {
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
var_size,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to increment TrialStateCtr variable\n");
ret = -1;
goto out;
}
}
} else {
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
0,
0, &trial_state_ctr,
NULL);
check status value.
}
+out:
free(mdata);
return ret;
+}
+u8 fwu_update_checks_pass(void) +{
return !trial_state && boottime_check;
+}
+int fwu_boottime_checks(void) +{
int ret;
struct udevice *dev;
u32 boot_idx, active_idx;
if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) {
log_err("FWU Metadata device not found\n");
return 0;
}
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.25.1

hi Etienne,
On Tue, 21 Jun 2022 at 16:26, Etienne Carriere etienne.carriere@linaro.org wrote:
Hi Sughosh,
On Thu, 9 Jun 2022 at 14:31, 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
common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
<snip>
--- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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(L"TrialStateCtr",
&efi_global_variable_guid,
&var_attributes,
&var_size, &trial_state_ctr,
NULL);
if (status != EFI_SUCCESS) {
log_err("Unable to read TrialStateCtr variable\n");
ret = -1;
goto out;
}
++trial_state_ctr;
if (trial_state_ctr > CONFIG_FWU_TRIAL_STATE_CNT) {
log_info("Trial State count exceeded. Revert back to previous_active_index\n");
active_idx = mdata->active_index;
ret = fwu_revert_boot_index();
if (ret) {
log_err("Unable to revert active_index\n");
goto out;
}
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
0,
s/0/var_size/ ? Ditto 24 lines below.
The variable size is 0 since the variable is being deleted here and the other instance that you mention. Maybe I can put a comment in the two places.
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to clear TrialStateCtr variable\n");
ret = -1;
goto out;
}
} else {
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
var_size,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to increment TrialStateCtr variable\n");
ret = -1;
goto out;
}
}
} else {
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
0,
0, &trial_state_ctr,
NULL);
check status value.
Okay, I can put a log mentioning the error. There is not much use of the status apart from this.
-sughosh
}
+out:
free(mdata);
return ret;
+}
+u8 fwu_update_checks_pass(void) +{
return !trial_state && boottime_check;
+}
+int fwu_boottime_checks(void) +{
int ret;
struct udevice *dev;
u32 boot_idx, active_idx;
if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) {
log_err("FWU Metadata device not found\n");
return 0;
}
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.25.1

Hi Sughosh,
On Thu, 23 Jun 2022 at 11:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Etienne,
On Tue, 21 Jun 2022 at 16:26, Etienne Carriere etienne.carriere@linaro.org wrote:
Hi Sughosh,
On Thu, 9 Jun 2022 at 14:31, 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
common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
<snip>
--- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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(L"TrialStateCtr",
&efi_global_variable_guid,
&var_attributes,
&var_size, &trial_state_ctr,
NULL);
if (status != EFI_SUCCESS) {
log_err("Unable to read TrialStateCtr variable\n");
ret = -1;
goto out;
}
++trial_state_ctr;
if (trial_state_ctr > CONFIG_FWU_TRIAL_STATE_CNT) {
log_info("Trial State count exceeded. Revert back to previous_active_index\n");
active_idx = mdata->active_index;
ret = fwu_revert_boot_index();
if (ret) {
log_err("Unable to revert active_index\n");
goto out;
}
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
0,
s/0/var_size/ ? Ditto 24 lines below.
The variable size is 0 since the variable is being deleted here and the other instance that you mention. Maybe I can put a comment in the two places.
The goal is to delete the variable or to reset it to 0? If really deleting, this function should rather pass NULL instead of &trial_state_ctr.
Regarding adding an inline comment, i guess you don't need to if that's part of efi_set_variable_int() API. By the way, it would help if this behaviour was described in the function declaration, but that's another story.
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to clear TrialStateCtr variable\n");
ret = -1;
goto out;
}
} else {
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
var_size,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to increment TrialStateCtr variable\n");
ret = -1;
goto out;
}
}
} else {
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
0,
0, &trial_state_ctr,
NULL);
check status value.
Okay, I can put a log mentioning the error. There is not much use of the status apart from this.
Failing to reset/delete TrialStateCtr is an error case and should be reported when this function returns, no?
-sughosh
}
+out:
free(mdata);
return ret;
+}
+u8 fwu_update_checks_pass(void) +{
return !trial_state && boottime_check;
+}
+int fwu_boottime_checks(void) +{
int ret;
struct udevice *dev;
u32 boot_idx, active_idx;
if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) {
log_err("FWU Metadata device not found\n");
return 0;
}
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.25.1

hi Etienne,
On Thu, 23 Jun 2022 at 18:02, Etienne Carriere etienne.carriere@linaro.org wrote:
Hi Sughosh,
On Thu, 23 Jun 2022 at 11:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Etienne,
On Tue, 21 Jun 2022 at 16:26, Etienne Carriere etienne.carriere@linaro.org wrote:
Hi Sughosh,
On Thu, 9 Jun 2022 at 14:31, 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
common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
<snip>
--- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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(L"TrialStateCtr",
&efi_global_variable_guid,
&var_attributes,
&var_size, &trial_state_ctr,
NULL);
if (status != EFI_SUCCESS) {
log_err("Unable to read TrialStateCtr variable\n");
ret = -1;
goto out;
}
++trial_state_ctr;
if (trial_state_ctr > CONFIG_FWU_TRIAL_STATE_CNT) {
log_info("Trial State count exceeded. Revert back to previous_active_index\n");
active_idx = mdata->active_index;
ret = fwu_revert_boot_index();
if (ret) {
log_err("Unable to revert active_index\n");
goto out;
}
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
0,
s/0/var_size/ ? Ditto 24 lines below.
The variable size is 0 since the variable is being deleted here and the other instance that you mention. Maybe I can put a comment in the two places.
The goal is to delete the variable or to reset it to 0?
The goal is to delete the variable.
If really deleting, this function should rather pass NULL instead of &trial_state_ctr.
The efi_set_variable_int checks the values of the attributes and data_size parameters to decide if the variable is to be deleted. The value of the variable data is not considered. But I can see that this can be confusing to someone reading the code. I will pass this as NULL instead.
Regarding adding an inline comment, i guess you don't need to if that's part of efi_set_variable_int() API. By the way, it would help if this behaviour was described in the function declaration, but that's another story.
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to clear TrialStateCtr variable\n");
ret = -1;
goto out;
}
} else {
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
var_size,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to increment TrialStateCtr variable\n");
ret = -1;
goto out;
}
}
} else {
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
0,
0, &trial_state_ctr,
NULL);
check status value.
Okay, I can put a log mentioning the error. There is not much use of the status apart from this.
Failing to reset/delete TrialStateCtr is an error case and should be reported when this function returns, no?
I do see your point. I was thinking that the inability to delete the variable for any reason should not result in the boottime_check variable being set to 0, as in that case, an update initiated would not proceed. This may or may not be an issue since the ESP and the firmware images might be on different storage devices. Please let me know what would be your preference. Thanks.
-sughosh
-sughosh
}
+out:
free(mdata);
return ret;
+}
+u8 fwu_update_checks_pass(void) +{
return !trial_state && boottime_check;
+}
+int fwu_boottime_checks(void) +{
int ret;
struct udevice *dev;
u32 boot_idx, active_idx;
if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) {
log_err("FWU Metadata device not found\n");
return 0;
}
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.25.1

Hi,
On 6/9/22 14:29, Sughosh Ganu 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
common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
diff --git a/common/board_r.c b/common/board_r.c index 6f4aca2077..33a600715d 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> @@ -797,6 +798,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 41774ff9e2..8fbd91b463 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -33,6 +33,9 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+int fwu_boottime_checks(void); +u8 fwu_update_checks_pass(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..af884439fb --- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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(L"TrialStateCtr",
&efi_global_variable_guid,
&var_attributes,
&var_size, &trial_state_ctr,
NULL);
for 'L"TrialStateCtr"' => wide characters for unicode
L string is really needed here ?
cf= http://patchwork.ozlabs.org/project/uboot/patch/20220123195514.3152022-5-sjg...
same for all the other string L"TrialStateCtr" in the file...
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;
}
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
0,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to clear TrialStateCtr variable\n");
ret = -1;
goto out;
}
} else {
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
var_size,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to increment TrialStateCtr variable\n");
ret = -1;
goto out;
}
}
- } else {
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
0,
0, &trial_state_ctr,
NULL);
- }
+out:
- free(mdata);
- return ret;
+}
+u8 fwu_update_checks_pass(void) +{
- return !trial_state && boottime_check;
+}
+int fwu_boottime_checks(void) +{
- int ret;
- struct udevice *dev;
- u32 boot_idx, active_idx;
- if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) {
log_err("FWU Metadata device not found\n");
return 0;
- }
This call of "uclass_get_device(UCLASS_FWU_MDATA, 0, &dev)"
is not needed => it is already done in
fwu_mdata_check() => fwu_get_dev_ops()
- 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;
+}
Regards
Patrick

hi Patrick,
On Tue, 21 Jun 2022 at 17:16, Patrick DELAUNAY patrick.delaunay@foss.st.com wrote:
Hi,
On 6/9/22 14:29, Sughosh Ganu 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
common/board_r.c | 5 ++ include/fwu.h | 3 + lib/fwu_updates/fwu.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 lib/fwu_updates/fwu.c
diff --git a/common/board_r.c b/common/board_r.c index 6f4aca2077..33a600715d 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> @@ -797,6 +798,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 41774ff9e2..8fbd91b463 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -33,6 +33,9 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+int fwu_boottime_checks(void); +u8 fwu_update_checks_pass(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..af884439fb --- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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(L"TrialStateCtr",
&efi_global_variable_guid,
&var_attributes,
&var_size, &trial_state_ctr,
NULL);
for 'L"TrialStateCtr"' => wide characters for unicode
L string is really needed here ?
cf= http://patchwork.ozlabs.org/project/uboot/patch/20220123195514.3152022-5-sjg...
same for all the other string L"TrialStateCtr" in the file...
Will change.
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;
}
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
0,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to clear TrialStateCtr variable\n");
ret = -1;
goto out;
}
} else {
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
var_size,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to increment TrialStateCtr variable\n");
ret = -1;
goto out;
}
}
} else {
trial_state_ctr = 0;
status = efi_set_variable_int(L"TrialStateCtr",
&efi_global_variable_guid,
0,
0, &trial_state_ctr,
NULL);
}
+out:
free(mdata);
return ret;
+}
+u8 fwu_update_checks_pass(void) +{
return !trial_state && boottime_check;
+}
+int fwu_boottime_checks(void) +{
int ret;
struct udevice *dev;
u32 boot_idx, active_idx;
if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) {
log_err("FWU Metadata device not found\n");
return 0;
}
This call of "uclass_get_device(UCLASS_FWU_MDATA, 0, &dev)"
is not needed => it is already done in
fwu_mdata_check() => fwu_get_dev_ops()
Yes, will remove this call. Thanks.
-sughosh
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;
+}
Regards
Patrick

The FWU Multi Bank Update feature supports updation of firmware images to one of multiple sets(also called banks) of images. The firmware images are clubbed together in banks, with the system booting images from the active bank. Information on the images such as which bank they belong to is stored as part of the metadata structure, which is stored on the same storage media as the firmware images on a dedicated partition.
At the time of update, the metadata is read to identify the bank to which the images need to be flashed(update bank). On a successful update, the metadata is modified to set the updated bank as active bank to subsequently boot from.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- include/fwu.h | 12 +- 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 | 6 + lib/fwu_updates/fwu.c | 26 ++++ 8 files changed, 309 insertions(+), 7 deletions(-) create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile
diff --git a/include/fwu.h b/include/fwu.h index 8fbd91b463..9c8012407b 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -28,13 +28,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)
-int fwu_boottime_checks(void); +#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 acc0ac081a..4ca6ea226b 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -966,3 +966,9 @@ config PHANDLE_CHECK_SEQ phandles in fdtdec_get_alias_seq() function.
endmenu + +menu "FWU Multi Bank Updates" + +source lib/fwu_updates/Kconfig + +endmenu diff --git a/lib/Makefile b/lib/Makefile index d9b1811f75..0cf8527c2d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index c76a5f3570..8ca041e6a2 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: @@ -1101,8 +1287,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; @@ -1130,12 +1318,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); @@ -1143,6 +1333,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]); @@ -1150,7 +1341,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 250eeb2fcd..8658ebf56a 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -346,7 +346,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 new file mode 100644 index 0000000000..74e37014e2 --- /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_MULTI_BANK_UPDATE) += fwu.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index af884439fb..422ef58661 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -112,6 +112,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(L"TrialStateCtr", + &efi_global_variable_guid, + var_attributes, + var_size, + &trial_state_ctr, false); + if (status != EFI_SUCCESS) { + log_err("Unable to increment TrialStateCtr variable\n"); + ret = -1; + } + + return ret; +} + int fwu_boottime_checks(void) { int ret;

On Thu, 9 Jun 2022 at 14:31, Sughosh Ganu sughosh.ganu@linaro.org wrote:
The FWU Multi Bank Update feature supports updation of firmware images to one of multiple sets(also called banks) of images. The firmware images are clubbed together in banks, with the system booting images from the active bank. Information on the images such as which bank they belong to is stored as part of the metadata structure, which is stored on the same storage media as the firmware images on a dedicated partition.
At the time of update, the metadata is read to identify the bank to which the images need to be flashed(update bank). On a successful update, the metadata is modified to set the updated bank as active bank to subsequently boot from.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
include/fwu.h | 12 +- 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 | 6 + lib/fwu_updates/fwu.c | 26 ++++ 8 files changed, 309 insertions(+), 7 deletions(-) create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile
diff --git a/include/fwu.h b/include/fwu.h index 8fbd91b463..9c8012407b 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -28,13 +28,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)
-int fwu_boottime_checks(void); +#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 acc0ac081a..4ca6ea226b 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -966,3 +966,9 @@ config PHANDLE_CHECK_SEQ phandles in fdtdec_get_alias_seq() function.
endmenu
+menu "FWU Multi Bank Updates"
+source lib/fwu_updates/Kconfig
+endmenu diff --git a/lib/Makefile b/lib/Makefile index d9b1811f75..0cf8527c2d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index c76a5f3570..8ca041e6a2 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: @@ -1101,8 +1287,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;
@@ -1130,12 +1318,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);
@@ -1143,6 +1333,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]);
@@ -1150,7 +1341,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 250eeb2fcd..8658ebf56a 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -346,7 +346,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 new file mode 100644 index 0000000000..74e37014e2 --- /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_MULTI_BANK_UPDATE) += fwu.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index af884439fb..422ef58661 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -112,6 +112,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(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
var_size,
&trial_state_ctr, false);
if (status != EFI_SUCCESS) {
log_err("Unable to increment TrialStateCtr variable\n");
ret = -1;
}
return ret;
+}
int fwu_boottime_checks(void) { int ret; -- 2.25.1

Hi,
On 6/9/22 14:29, Sughosh Ganu wrote:
The FWU Multi Bank Update feature supports updation of firmware images to one of multiple sets(also called banks) of images. The firmware images are clubbed together in banks, with the system booting images from the active bank. Information on the images such as which bank they belong to is stored as part of the metadata structure, which is stored on the same storage media as the firmware images on a dedicated partition.
At the time of update, the metadata is read to identify the bank to which the images need to be flashed(update bank). On a successful update, the metadata is modified to set the updated bank as active bank to subsequently boot from.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
include/fwu.h | 12 +- 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 | 6 + lib/fwu_updates/fwu.c | 26 ++++ 8 files changed, 309 insertions(+), 7 deletions(-) create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile
[...]
diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index af884439fb..422ef58661 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -112,6 +112,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(L"TrialStateCtr",
&efi_global_variable_guid,
var_attributes,
var_size,
&trial_state_ctr, false);
u"TrialStateCtr",
if USC2 is not mandatory.
- if (status != EFI_SUCCESS) {
log_err("Unable to increment TrialStateCtr variable\n");
ret = -1;
- }
- return ret;
+}
- int fwu_boottime_checks(void) { int ret;
Regards

Add a command to read the metadata as specified in the FWU specification and print the fields of the metadata.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- cmd/Kconfig | 7 +++++ cmd/Makefile | 1 + cmd/fwu_mdata.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 cmd/fwu_mdata.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 09193b61b9..275becd837 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -144,6 +144,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 if FWU_MULTI_BANK_UPDATE + help + Command to read the metadata and dump it's contents + config CMD_LICENSE bool "license" select BUILD_BIN2C diff --git a/cmd/Makefile b/cmd/Makefile index 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..bc20ca26a3 --- /dev/null +++ b/cmd/fwu_mdata.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * 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; + u32 nimages, nbanks; + + 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); + + nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK; + nbanks = CONFIG_FWU_NUM_BANKS; + printf("\tImage Info\n"); + for (i = 0; i < nimages; 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 < nbanks; j++) { + img_info = &img_entry->img_bank_info[j]; + printf("Image Guid: %pUL\n", &img_info->image_uuid); + printf("Image Acceptance: %#x\n", img_info->accepted); + } + } +} + +int do_fwu_mdata_read(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct udevice *dev; + int ret = CMD_RET_SUCCESS; + 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; + } + + ret = fwu_get_mdata(&mdata); + if (ret < 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, Jun 09, 2022 at 05:59:57PM +0530, Sughosh Ganu 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
cmd/Kconfig | 7 +++++ cmd/Makefile | 1 + cmd/fwu_mdata.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 cmd/fwu_mdata.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 09193b61b9..275becd837 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -144,6 +144,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 if FWU_MULTI_BANK_UPDATE
- help
Command to read the metadata and dump it's contents
config CMD_LICENSE bool "license" select BUILD_BIN2C diff --git a/cmd/Makefile b/cmd/Makefile index 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..bc20ca26a3 --- /dev/null +++ b/cmd/fwu_mdata.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- 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;
- u32 nimages, nbanks;
nit but we don't really need those two. Just use the define.
- 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);
- nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
- nbanks = CONFIG_FWU_NUM_BANKS;
- printf("\tImage Info\n");
- for (i = 0; i < nimages; 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 < nbanks; j++) {
img_info = &img_entry->img_bank_info[j];
printf("Image Guid: %pUL\n", &img_info->image_uuid);
printf("Image Acceptance: %#x\n", img_info->accepted);
Can we do 'yes/no' on the image acceptance please?
}
- }
+}
+int do_fwu_mdata_read(struct cmd_tbl *cmdtp, int flag,
int argc, char * const argv[])
+{
- struct udevice *dev;
- int ret = CMD_RET_SUCCESS;
- 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;
- }
- ret = fwu_get_mdata(&mdata);
- if (ret < 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.25.1
Regards /Ilias

hi Ilias,
On Fri, 10 Jun 2022 at 17:37, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Thu, Jun 09, 2022 at 05:59:57PM +0530, Sughosh Ganu 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
cmd/Kconfig | 7 +++++ cmd/Makefile | 1 + cmd/fwu_mdata.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 cmd/fwu_mdata.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 09193b61b9..275becd837 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -144,6 +144,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 if FWU_MULTI_BANK_UPDATE
help
Command to read the metadata and dump it's contents
config CMD_LICENSE bool "license" select BUILD_BIN2C diff --git a/cmd/Makefile b/cmd/Makefile index 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..bc20ca26a3 --- /dev/null +++ b/cmd/fwu_mdata.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- 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;
u32 nimages, nbanks;
nit but we don't really need those two. Just use the define.
Okay. Will change.
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);
nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
nbanks = CONFIG_FWU_NUM_BANKS;
printf("\tImage Info\n");
for (i = 0; i < nimages; 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 < nbanks; j++) {
img_info = &img_entry->img_bank_info[j];
printf("Image Guid: %pUL\n", &img_info->image_uuid);
printf("Image Acceptance: %#x\n", img_info->accepted);
Can we do 'yes/no' on the image acceptance please?
Okay. Will change.
-sughosh
}
}
+}
+int do_fwu_mdata_read(struct cmd_tbl *cmdtp, int flag,
int argc, char * const argv[])
+{
struct udevice *dev;
int ret = CMD_RET_SUCCESS;
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;
}
ret = fwu_get_mdata(&mdata);
if (ret < 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.25.1
Regards /Ilias

On 6/9/22 14:29, Sughosh Ganu 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
cmd/Kconfig | 7 +++++ cmd/Makefile | 1 + cmd/fwu_mdata.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 cmd/fwu_mdata.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 09193b61b9..275becd837 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -144,6 +144,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 if FWU_MULTI_BANK_UPDATE
- help
Command to read the metadata and dump it's contents
- config CMD_LICENSE bool "license" select BUILD_BIN2C
diff --git a/cmd/Makefile b/cmd/Makefile index 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..bc20ca26a3 --- /dev/null +++ b/cmd/fwu_mdata.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- 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;
- u32 nimages, nbanks;
- 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);
- nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
- nbanks = CONFIG_FWU_NUM_BANKS;
- printf("\tImage Info\n");
- for (i = 0; i < nimages; 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 < nbanks; j++) {
img_info = &img_entry->img_bank_info[j];
printf("Image Guid: %pUL\n", &img_info->image_uuid);
printf("Image Acceptance: %#x\n", img_info->accepted);
}
- }
+}
+int do_fwu_mdata_read(struct cmd_tbl *cmdtp, int flag,
int argc, char * const argv[])
+{
- struct udevice *dev;
- int ret = CMD_RET_SUCCESS;
- 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;
- }
- ret = fwu_get_mdata(&mdata);
- if (ret < 0) {
log_err("Unable to get valid FWU metadata\n");
ret = CMD_RET_FAILURE;
goto out;
- }
I think here would be a need to also check data you get back. I am playing with it and if I rewrite one copy and second copy is different I don't get any error message and I think it is wrong. You should IMHO use mdata_check hooks to check that what you get is correct and aligned.
Thanks, Michal

On 6/9/22 14:29, Sughosh Ganu 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
cmd/Kconfig | 7 +++++ cmd/Makefile | 1 + cmd/fwu_mdata.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 cmd/fwu_mdata.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 09193b61b9..275becd837 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -144,6 +144,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 if FWU_MULTI_BANK_UPDATE
+ default y
it is enough with the depends on
- 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..bc20ca26a3 --- /dev/null +++ b/cmd/fwu_mdata.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- 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;
- u32 nimages, nbanks;
- 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);
- nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
- nbanks = CONFIG_FWU_NUM_BANKS;
- printf("\tImage Info\n");
- for (i = 0; i < nimages; 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 < nbanks; j++) {
img_info = &img_entry->img_bank_info[j];
printf("Image Guid: %pUL\n", &img_info->image_uuid);
printf("Image Acceptance: %#x\n", img_info->accepted);
}
- }
+}
+int do_fwu_mdata_read(struct cmd_tbl *cmdtp, int flag,
int argc, char * const argv[])
+{
- struct udevice *dev;
- int ret = CMD_RET_SUCCESS;
- 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;
- }
ret is reused here ?
so the default value is overwrite
I think it is better to have 2 variables
ret => cmd result
res => result of fwu_get_mdata() ?
- ret = fwu_get_mdata(&mdata);
- if (ret < 0) {
log_err("Unable to get valid FWU metadata\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",
- ""
+);
regards
Patrick

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 --- doc/mkeficapsule.1 | 29 ++++++--- tools/eficapsule.h | 8 +++ tools/mkeficapsule.c | 139 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 25 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..e8eb6b070d 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,16 @@ 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"; + +static bool empty_capsule; +static unsigned char capsule; + +enum { + CAPSULE_NORMAL_BLOB = 0, + CAPSULE_ACCEPT, + CAPSULE_REVERT, +} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,24 +48,47 @@ 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}, };
static void print_usage(void) { - fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n" - "Options:\n" - - "\t-g, --guid <guid string> guid for image blob type\n" - "\t-i, --index <index> update image index\n" - "\t-I, --instance <instance> update hardware instance\n" - "\t-p, --private-key <privkey file> private key file\n" - "\t-c, --certificate <cert file> signer's certificate file\n" - "\t-m, --monotonic-count <count> monotonic count\n" - "\t-d, --dump_sig dump signature (*.p7)\n" - "\t-h, --help print a help message\n", - tool_name); + if (empty_capsule) { + if (capsule == CAPSULE_ACCEPT) { + fprintf(stderr, "Usage: %s [options] <output file>\n", + tool_name); + fprintf(stderr, "Options:\n" + "\t-A, --fw-accept firmware accept capsule\n" + "\t-g, --guid <guid string> guid for image blob type\n" + "\t-h, --help print a help message\n" + ); + } else { + fprintf(stderr, "Usage: %s [options] <output file>\n", + tool_name); + fprintf(stderr, "Options:\n" + "\t-R, --fw-revert firmware revert capsule\n" + "\t-h, --help print a help message\n" + ); + } + } else { + fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n" + "Options:\n" + + "\t-g, --guid <guid string> guid for image blob type\n" + "\t-i, --index <index> update image index\n" + "\t-I, --instance <instance> update hardware instance\n" + "\t-p, --private-key <privkey file> private key file\n" + "\t-c, --certificate <cert file> signer's certificate file\n" + "\t-m, --monotonic-count <count> monotonic count\n" + "\t-d, --dump_sig dump signature (*.p7)\n" + "\t-A, --fw-accept firmware accept capsule\n" + "\t-R, --fw-revert firmware revert capsule\n" + "\t-h, --help print a help message\n", + tool_name); + } }
/** @@ -564,6 +596,50 @@ 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; + 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 payload, 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) { + memcpy(&payload, guid, sizeof(efi_guid_t)); + if (write_capsule_file(f, &payload, sizeof(payload), + "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 @@ -639,22 +715,49 @@ int main(int argc, char **argv) case 'd': dump_sig = 1; break; + case 'A': + capsule |= CAPSULE_ACCEPT; + break; + case 'R': + capsule |= CAPSULE_REVERT; + break; case 'h': print_usage(); exit(EXIT_SUCCESS); } }
+ if (capsule == (CAPSULE_ACCEPT | CAPSULE_REVERT)) { + fprintf(stderr, + "Select either of Accept or Revert capsule generation\n"); + exit(EXIT_FAILURE); + } + + empty_capsule = (capsule == CAPSULE_ACCEPT || + capsule == CAPSULE_REVERT); + /* check necessary parameters */ - if ((argc != optind + 2) || !guid || - ((privkey_file && !cert_file) || - (!privkey_file && cert_file))) { + if ((!empty_capsule && + ((argc != optind + 2) || !guid || + ((privkey_file && !cert_file) || + (!privkey_file && cert_file)))) || + (empty_capsule && + ((argc != optind + 1) || + ((capsule == CAPSULE_ACCEPT) && !guid) || + ((capsule == 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 (empty_capsule) { + if (create_empty_capsule(argv[argc - 1], guid, + capsule == 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); }

On 6/9/22 14:29, Sughosh Ganu 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
doc/mkeficapsule.1 | 29 ++++++--- tools/eficapsule.h | 8 +++ tools/mkeficapsule.c | 139 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 25 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..e8eb6b070d 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,16 @@ 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";
+static bool empty_capsule; +static unsigned char capsule;
+enum {
- CAPSULE_NORMAL_BLOB = 0,
- CAPSULE_ACCEPT,
- CAPSULE_REVERT,
+} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,24 +48,47 @@ 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}, };
static void print_usage(void) {
- fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-h, --help print a help message\n",
tool_name);
- if (empty_capsule) {
if (capsule == CAPSULE_ACCEPT) {
fprintf(stderr, "Usage: %s [options] <output file>\n",
My expectation is that this function always provides the same output.
If different scenarios allow only specific combinations of arguments you may describe it here.
tool_name);
fprintf(stderr, "Options:\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-h, --help print a help message\n"
);
} else {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n"
);
}
- } else {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-A, --fw-accept firmware accept capsule, requires GUID\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-R, --fw-revert firmware revert capsule, takes no GUID\n"
"\t-h, --help print a help message\n",
tool_name);
} }
/**
@@ -564,6 +596,50 @@ 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;
- 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 payload, 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) {
memcpy(&payload, guid, sizeof(efi_guid_t));
if (write_capsule_file(f, &payload, sizeof(payload),
"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
@@ -639,22 +715,49 @@ int main(int argc, char **argv)
case 'd': dump_sig = 1; break;
case 'A':
capsule |= CAPSULE_ACCEPT;
break;
case 'R':
capsule |= CAPSULE_REVERT;
case 'h':break;
You must handle '?' and ':' here too. Just use
default:
print_usage(); exit(EXIT_SUCCESS); }
}
- if (capsule == (CAPSULE_ACCEPT | CAPSULE_REVERT)) {
fprintf(stderr,
"Select either of Accept or Revert capsule generation\n");
exit(EXIT_FAILURE);
- }
- empty_capsule = (capsule == CAPSULE_ACCEPT ||
capsule == CAPSULE_REVERT);
- /* check necessary parameters */
- if ((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file))) {
- if ((!empty_capsule &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(empty_capsule &&
((argc != optind + 1) ||
((capsule == CAPSULE_ACCEPT) && !guid) ||
((capsule == CAPSULE_REVERT) && guid)))) {
Please, write a message explaining to the user what was wrong, e.g.
"Option -A does not take a GUID" or "Option -R requires a 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 (empty_capsule) {
if (create_empty_capsule(argv[argc - 1], guid,
capsule == CAPSULE_ACCEPT) < 0) {
fprintf(stderr, "Creating empty capsule failed\n");
The called function should provide a message that describes what went wrong.
Best regards
Heinrich
exit(EXIT_FAILURE);
}
- } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
index, instance, mcount, privkey_file,
fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }cert_file) < 0) {

On Thu, 9 Jun 2022 at 21:58, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 6/9/22 14:29, Sughosh Ganu 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
doc/mkeficapsule.1 | 29 ++++++--- tools/eficapsule.h | 8 +++ tools/mkeficapsule.c | 139 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 25 deletions(-)
<snip>
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index 5f74d23b9e..e8eb6b070d 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,16 @@ 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";
+static bool empty_capsule; +static unsigned char capsule;
+enum {
CAPSULE_NORMAL_BLOB = 0,
CAPSULE_ACCEPT,
CAPSULE_REVERT,
+} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,24 +48,47 @@ 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},
};
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-h, --help print a help message\n",
tool_name);
if (empty_capsule) {
if (capsule == CAPSULE_ACCEPT) {
fprintf(stderr, "Usage: %s [options] <output file>\n",
My expectation is that this function always provides the same output.
If different scenarios allow only specific combinations of arguments you may describe it here.
Okay
tool_name);
fprintf(stderr, "Options:\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-h, --help print a help message\n"
);
} else {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n"
);
}
} else {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-A, --fw-accept firmware accept capsule, requires GUID\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-R, --fw-revert firmware revert capsule, takes no GUID\n"
"\t-h, --help print a help message\n",
tool_name);
}
}
/**
@@ -564,6 +596,50 @@ 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;
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 payload, 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) {
memcpy(&payload, guid, sizeof(efi_guid_t));
if (write_capsule_file(f, &payload, sizeof(payload),
"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
@@ -639,22 +715,49 @@ int main(int argc, char **argv)
case 'd': dump_sig = 1; break;
case 'A':
capsule |= CAPSULE_ACCEPT;
break;
case 'R':
capsule |= CAPSULE_REVERT;
break; case 'h':
You must handle '?' and ':' here too. Just use
default:
Okay
print_usage(); exit(EXIT_SUCCESS); } }
if (capsule == (CAPSULE_ACCEPT | CAPSULE_REVERT)) {
fprintf(stderr,
"Select either of Accept or Revert capsule generation\n");
exit(EXIT_FAILURE);
}
empty_capsule = (capsule == CAPSULE_ACCEPT ||
capsule == CAPSULE_REVERT);
/* check necessary parameters */
if ((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file))) {
if ((!empty_capsule &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(empty_capsule &&
((argc != optind + 1) ||
((capsule == CAPSULE_ACCEPT) && !guid) ||
((capsule == CAPSULE_REVERT) && guid)))) {
Please, write a message explaining to the user what was wrong, e.g.
"Option -A does not take a GUID" or "Option -R requires a GUID"
We are calling the print_usage function where I am adding the additional information that you suggested above. I think that would suffice. Else we are basically repeating the same information twice.
print_usage(); exit(EXIT_FAILURE); }
if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, index, instance,
mcount, privkey_file, cert_file) < 0) {
if (empty_capsule) {
if (create_empty_capsule(argv[argc - 1], guid,
capsule == CAPSULE_ACCEPT) < 0) {
fprintf(stderr, "Creating empty capsule failed\n");
The called function should provide a message that describes what went wrong.
The create_empty_capsule function calls the write_capsule_file function for creation of the capsule file, and passes the string that is to be printed in case of an error condition.
-sughosh
Best regards
Heinrich
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); }

On Thu, Jun 09, 2022 at 05:59:58PM +0530, Sughosh Ganu 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
doc/mkeficapsule.1 | 29 ++++++--- tools/eficapsule.h | 8 +++ tools/mkeficapsule.c | 139 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 25 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
"image acceptance" -> "firmware acceptance"
I don't still understand why we need a guid for acceptance while revert doesn't require it. I believe that firmware update is "all or nothing", isn't it?
If there is a good reason, please describe a possible/expected scenario.
.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..e8eb6b070d 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,16 @@ 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";
+static bool empty_capsule; +static unsigned char capsule;
+enum {
- CAPSULE_NORMAL_BLOB = 0,
- CAPSULE_ACCEPT,
- CAPSULE_REVERT,
+} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,24 +48,47 @@ 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},
};
static void print_usage(void) {
- fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-h, --help print a help message\n",
tool_name);
- if (empty_capsule) {
if (capsule == CAPSULE_ACCEPT) {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-h, --help print a help message\n"
);
} else {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n"
);
}
- } else {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n",
tool_name);
- }
}
/** @@ -564,6 +596,50 @@ 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;
- 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 payload, 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));
-> guidcpy()
- 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) {
memcpy(&payload, guid, sizeof(efi_guid_t));
ditto
if (write_capsule_file(f, &payload, sizeof(payload),
"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
@@ -639,22 +715,49 @@ int main(int argc, char **argv) case 'd': dump_sig = 1; break;
case 'A':
capsule |= CAPSULE_ACCEPT;
break;
case 'R':
capsule |= CAPSULE_REVERT;
break;
case 'h': print_usage(); exit(EXIT_SUCCESS); } }
if (capsule == (CAPSULE_ACCEPT | CAPSULE_REVERT)) {
fprintf(stderr,
"Select either of Accept or Revert capsule generation\n");
exit(EXIT_FAILURE);
}
empty_capsule = (capsule == CAPSULE_ACCEPT ||
capsule == CAPSULE_REVERT);
So empty_capsule is redundant as empty_capsule is equivalent with "capsule == CAPSULE_NORMAL_BLOB". I think that a single variable, say capsule_type, is enough.
/* check necessary parameters */
- if ((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file))) {
- if ((!empty_capsule &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(empty_capsule &&
((argc != optind + 1) ||
((capsule == CAPSULE_ACCEPT) && !guid) ||
print_usage(); exit(EXIT_FAILURE); }((capsule == CAPSULE_REVERT) && guid)))) {
- if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, index, instance,
mcount, privkey_file, cert_file) < 0) {
- if (empty_capsule) {
if (create_empty_capsule(argv[argc - 1], guid,
capsule == CAPSULE_ACCEPT) < 0) {
if (capsule_type != CAPSULE_NORMAL_BLOB) create_empty_capsule(..., capsule_type == CAPSULE_ACCEPT);
Simple is the best :)
-Takahiro Akashi
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,
fprintf(stderr, "Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }cert_file) < 0) {
-- 2.25.1

On Wed, 15 Jun 2022 at 10:41, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Thu, Jun 09, 2022 at 05:59:58PM +0530, Sughosh Ganu 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
doc/mkeficapsule.1 | 29 ++++++--- tools/eficapsule.h | 8 +++ tools/mkeficapsule.c | 139 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 25 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
"image acceptance" -> "firmware acceptance"
Okay
I don't still understand why we need a guid for acceptance while revert doesn't require it. I believe that firmware update is "all or nothing", isn't it?
I believe this gives more flexibility in that different components might be required to accept the various firmware images. So, one component might accept the optee_os, while another might be responsible for accepting u-boot. In any case, we do check that all the components have their accepted bit set, and only if so, does the bank boot in the regular state. In case of a firmware revert, it would not matter which firmware component is being reverted -- the platform would simply need to boot from the other bank. Do you see any issue with the current method that we have?
If there is a good reason, please describe a possible/expected scenario.
Where do you want me to explain this, in the feature documentation? Or do you think this can be elaborated in greater detail in the spec.
.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..e8eb6b070d 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,16 @@ 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";
+static bool empty_capsule; +static unsigned char capsule;
+enum {
CAPSULE_NORMAL_BLOB = 0,
CAPSULE_ACCEPT,
CAPSULE_REVERT,
+} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,24 +48,47 @@ 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},
};
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-h, --help print a help message\n",
tool_name);
if (empty_capsule) {
if (capsule == CAPSULE_ACCEPT) {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-h, --help print a help message\n"
);
} else {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n"
);
}
} else {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n",
tool_name);
}
}
/** @@ -564,6 +596,50 @@ 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;
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 payload, 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));
-> guidcpy()
This being a host tool, guidcpy cannot be used. You have used memcpy in the create_fwbin() for the same reason I guess.
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) {
memcpy(&payload, guid, sizeof(efi_guid_t));
ditto
Same as above.
if (write_capsule_file(f, &payload, sizeof(payload),
"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
@@ -639,22 +715,49 @@ int main(int argc, char **argv) case 'd': dump_sig = 1; break;
case 'A':
capsule |= CAPSULE_ACCEPT;
break;
case 'R':
capsule |= CAPSULE_REVERT;
break; case 'h': print_usage(); exit(EXIT_SUCCESS); } }
if (capsule == (CAPSULE_ACCEPT | CAPSULE_REVERT)) {
fprintf(stderr,
"Select either of Accept or Revert capsule generation\n");
exit(EXIT_FAILURE);
}
empty_capsule = (capsule == CAPSULE_ACCEPT ||
capsule == CAPSULE_REVERT);
So empty_capsule is redundant as empty_capsule is equivalent with "capsule == CAPSULE_NORMAL_BLOB". I think that a single variable, say capsule_type, is enough.
I was using empty_capsule primarily to make the check done below look more succinct and readable. But I can change that to capsule != CAPSULE_NORMAL_BLOB.
/* check necessary parameters */
if ((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file))) {
if ((!empty_capsule &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(empty_capsule &&
((argc != optind + 1) ||
((capsule == CAPSULE_ACCEPT) && !guid) ||
((capsule == 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 (empty_capsule) {
if (create_empty_capsule(argv[argc - 1], guid,
capsule == CAPSULE_ACCEPT) < 0) {
if (capsule_type != CAPSULE_NORMAL_BLOB) create_empty_capsule(..., capsule_type == CAPSULE_ACCEPT);
Simple is the best :)
Common, please don't tell me that the code above is complicated. Just that we can do without the empty_capsule variable, yes.
-sughosh
-Takahiro Akashi
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); }
-- 2.25.1

Sughosh,
On Wed, Jun 15, 2022 at 04:19:56PM +0530, Sughosh Ganu wrote:
On Wed, 15 Jun 2022 at 10:41, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Thu, Jun 09, 2022 at 05:59:58PM +0530, Sughosh Ganu 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
doc/mkeficapsule.1 | 29 ++++++--- tools/eficapsule.h | 8 +++ tools/mkeficapsule.c | 139 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 25 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
"image acceptance" -> "firmware acceptance"
Okay
I don't still understand why we need a guid for acceptance while revert doesn't require it. I believe that firmware update is "all or nothing", isn't it?
I believe this gives more flexibility in that different components might be required to accept the various firmware images. So, one component might accept the optee_os, while another might be responsible for accepting u-boot. In any case, we do check that all the components have their accepted bit set, and only if so, does the bank boot in the regular state.
Probably I don't understand the behavior. Let's assume that we have firmware A and firmware B and then update both. When the firmware A is accepted and B is not (not yet issuing acceptance capsule) and I try to reboot the system, what happens?
From which bank does the system boot, old one or new one?
In case of a firmware revert, it would not matter which firmware component is being reverted -- the platform would simply need to boot from the other bank. Do you see any issue with the current method that we have?
If there is a good reason, please describe a possible/expected scenario.
Where do you want me to explain this, in the feature documentation? Or do you think this can be elaborated in greater detail in the spec.
I prefer some explanation in U-Boot doc.
.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..e8eb6b070d 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,16 @@ 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";
+static bool empty_capsule; +static unsigned char capsule;
+enum {
CAPSULE_NORMAL_BLOB = 0,
CAPSULE_ACCEPT,
CAPSULE_REVERT,
+} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,24 +48,47 @@ 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},
};
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-h, --help print a help message\n",
tool_name);
if (empty_capsule) {
if (capsule == CAPSULE_ACCEPT) {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-h, --help print a help message\n"
);
} else {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n"
);
}
} else {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n",
tool_name);
}
}
/** @@ -564,6 +596,50 @@ 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;
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 payload, 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));
-> guidcpy()
This being a host tool, guidcpy cannot be used. You have used memcpy in the create_fwbin() for the same reason I guess.
Ah, right.
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) {
memcpy(&payload, guid, sizeof(efi_guid_t));
ditto
Same as above.
if (write_capsule_file(f, &payload, sizeof(payload),
"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
@@ -639,22 +715,49 @@ int main(int argc, char **argv) case 'd': dump_sig = 1; break;
case 'A':
capsule |= CAPSULE_ACCEPT;
break;
case 'R':
capsule |= CAPSULE_REVERT;
break; case 'h': print_usage(); exit(EXIT_SUCCESS); } }
if (capsule == (CAPSULE_ACCEPT | CAPSULE_REVERT)) {
fprintf(stderr,
"Select either of Accept or Revert capsule generation\n");
exit(EXIT_FAILURE);
}
empty_capsule = (capsule == CAPSULE_ACCEPT ||
capsule == CAPSULE_REVERT);
So empty_capsule is redundant as empty_capsule is equivalent with "capsule == CAPSULE_NORMAL_BLOB". I think that a single variable, say capsule_type, is enough.
I was using empty_capsule primarily to make the check done below look more succinct and readable. But I can change that to capsule != CAPSULE_NORMAL_BLOB.
/* check necessary parameters */
if ((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file))) {
if ((!empty_capsule &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(empty_capsule &&
((argc != optind + 1) ||
((capsule == CAPSULE_ACCEPT) && !guid) ||
((capsule == 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 (empty_capsule) {
if (create_empty_capsule(argv[argc - 1], guid,
capsule == CAPSULE_ACCEPT) < 0) {
if (capsule_type != CAPSULE_NORMAL_BLOB) create_empty_capsule(..., capsule_type == CAPSULE_ACCEPT);
Simple is the best :)
Common, please don't tell me that the code above is complicated. Just that we can do without the empty_capsule variable, yes.
Sorry, not complicated, but having two variables make little sense.
-Takahiro Akashi
-sughosh
-Takahiro Akashi
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); }
-- 2.25.1

hi Takahiro,
On Thu, 16 Jun 2022 at 06:31, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Sughosh,
On Wed, Jun 15, 2022 at 04:19:56PM +0530, Sughosh Ganu wrote:
On Wed, 15 Jun 2022 at 10:41, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Thu, Jun 09, 2022 at 05:59:58PM +0530, Sughosh Ganu 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
doc/mkeficapsule.1 | 29 ++++++--- tools/eficapsule.h | 8 +++ tools/mkeficapsule.c | 139 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 25 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
"image acceptance" -> "firmware acceptance"
Okay
I don't still understand why we need a guid for acceptance while revert doesn't require it. I believe that firmware update is "all or nothing", isn't it?
I believe this gives more flexibility in that different components might be required to accept the various firmware images. So, one component might accept the optee_os, while another might be responsible for accepting u-boot. In any case, we do check that all the components have their accepted bit set, and only if so, does the bank boot in the regular state.
Probably I don't understand the behavior. Let's assume that we have firmware A and firmware B and then update both. When the firmware A is accepted and B is not (not yet issuing acceptance capsule) and I try to reboot the system, what happens? From which bank does the system boot, old one or new one?
Once any/all of the images have been updated, on subsequent reboot, the platform would boot in Trial State from the updated bank. I have introduced an EFI variable, TrialeStateCtr for counting the number of times the system is booting in the trial state. The system remains in trial state as long as all the images from the updated bank have not been accepted. The platform boots in trial state for a particular number of iterations(configurable), and once that count has exceeded, the active bank value gets changed to the previous_active_index, and the platform subsequently boots from the other bank. If all the images do get accepted while the platform is in trial state, the platform transitions to the regular state, and continues booting from that bank.
-sughosh
In case of a firmware revert, it would not matter which firmware component is being reverted -- the platform would simply need to boot from the other bank. Do you see any issue with the current method that we have?
If there is a good reason, please describe a possible/expected scenario.
Where do you want me to explain this, in the feature documentation? Or do you think this can be elaborated in greater detail in the spec.
I prefer some explanation in U-Boot doc.
.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..e8eb6b070d 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,16 @@ 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";
+static bool empty_capsule; +static unsigned char capsule;
+enum {
CAPSULE_NORMAL_BLOB = 0,
CAPSULE_ACCEPT,
CAPSULE_REVERT,
+} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,24 +48,47 @@ 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},
};
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-h, --help print a help message\n",
tool_name);
if (empty_capsule) {
if (capsule == CAPSULE_ACCEPT) {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-h, --help print a help message\n"
);
} else {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n"
);
}
} else {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n",
tool_name);
}
}
/** @@ -564,6 +596,50 @@ 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;
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 payload, 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));
-> guidcpy()
This being a host tool, guidcpy cannot be used. You have used memcpy in the create_fwbin() for the same reason I guess.
Ah, right.
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) {
memcpy(&payload, guid, sizeof(efi_guid_t));
ditto
Same as above.
if (write_capsule_file(f, &payload, sizeof(payload),
"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
@@ -639,22 +715,49 @@ int main(int argc, char **argv) case 'd': dump_sig = 1; break;
case 'A':
capsule |= CAPSULE_ACCEPT;
break;
case 'R':
capsule |= CAPSULE_REVERT;
break; case 'h': print_usage(); exit(EXIT_SUCCESS); } }
if (capsule == (CAPSULE_ACCEPT | CAPSULE_REVERT)) {
fprintf(stderr,
"Select either of Accept or Revert capsule generation\n");
exit(EXIT_FAILURE);
}
empty_capsule = (capsule == CAPSULE_ACCEPT ||
capsule == CAPSULE_REVERT);
So empty_capsule is redundant as empty_capsule is equivalent with "capsule == CAPSULE_NORMAL_BLOB". I think that a single variable, say capsule_type, is enough.
I was using empty_capsule primarily to make the check done below look more succinct and readable. But I can change that to capsule != CAPSULE_NORMAL_BLOB.
/* check necessary parameters */
if ((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file))) {
if ((!empty_capsule &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(empty_capsule &&
((argc != optind + 1) ||
((capsule == CAPSULE_ACCEPT) && !guid) ||
((capsule == 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 (empty_capsule) {
if (create_empty_capsule(argv[argc - 1], guid,
capsule == CAPSULE_ACCEPT) < 0) {
if (capsule_type != CAPSULE_NORMAL_BLOB) create_empty_capsule(..., capsule_type == CAPSULE_ACCEPT);
Simple is the best :)
Common, please don't tell me that the code above is complicated. Just that we can do without the empty_capsule variable, yes.
Sorry, not complicated, but having two variables make little sense.
-Takahiro Akashi
-sughosh
-Takahiro Akashi
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); }
-- 2.25.1

Sughosh,
On Thu, Jun 16, 2022 at 12:42:08PM +0530, Sughosh Ganu wrote:
hi Takahiro,
On Thu, 16 Jun 2022 at 06:31, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Sughosh,
On Wed, Jun 15, 2022 at 04:19:56PM +0530, Sughosh Ganu wrote:
On Wed, 15 Jun 2022 at 10:41, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Thu, Jun 09, 2022 at 05:59:58PM +0530, Sughosh Ganu 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
doc/mkeficapsule.1 | 29 ++++++--- tools/eficapsule.h | 8 +++ tools/mkeficapsule.c | 139 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 25 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
"image acceptance" -> "firmware acceptance"
Okay
I don't still understand why we need a guid for acceptance while revert doesn't require it. I believe that firmware update is "all or nothing", isn't it?
I believe this gives more flexibility in that different components might be required to accept the various firmware images. So, one component might accept the optee_os, while another might be responsible for accepting u-boot. In any case, we do check that all the components have their accepted bit set, and only if so, does the bank boot in the regular state.
Probably I don't understand the behavior. Let's assume that we have firmware A and firmware B and then update both. When the firmware A is accepted and B is not (not yet issuing acceptance capsule) and I try to reboot the system, what happens? From which bank does the system boot, old one or new one?
Once any/all of the images have been updated, on subsequent reboot, the platform would boot in Trial State from the updated bank. I have introduced an EFI variable, TrialeStateCtr for counting the number of times the system is booting in the trial state. The system remains in trial state as long as all the images from the updated bank have not been accepted. The platform boots in trial state for a particular number of iterations(configurable), and once that count has exceeded, the active bank value gets changed to the previous_active_index, and the platform subsequently boots from the other bank. If all the images do get accepted while the platform is in trial state, the platform transitions to the regular state, and continues booting from that bank.
Thank you for the details. But if I understand correctly, why do we need a partially-accepted status? When some are accepted but others not, the system boots from an update bank in any way. Right? So at the end, all that we should do is to accept the boot "from a new bank" permanently (and transit to, what you say, regular state), or to let the system boot from an old bank (again, in a regular state).
I don't see any good reason for having a partial acceptance.
We should continue this discussion, but anyhow, you should think of adding such a description above in U-Boot doc.
-Takahiro Akashi
-sughosh
In case of a firmware revert, it would not matter which firmware component is being reverted -- the platform would simply need to boot from the other bank. Do you see any issue with the current method that we have?
If there is a good reason, please describe a possible/expected scenario.
Where do you want me to explain this, in the feature documentation? Or do you think this can be elaborated in greater detail in the spec.
I prefer some explanation in U-Boot doc.
.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..e8eb6b070d 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,16 @@ 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";
+static bool empty_capsule; +static unsigned char capsule;
+enum {
CAPSULE_NORMAL_BLOB = 0,
CAPSULE_ACCEPT,
CAPSULE_REVERT,
+} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,24 +48,47 @@ 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},
};
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-h, --help print a help message\n",
tool_name);
if (empty_capsule) {
if (capsule == CAPSULE_ACCEPT) {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-h, --help print a help message\n"
);
} else {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n"
);
}
} else {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n",
tool_name);
}
}
/** @@ -564,6 +596,50 @@ 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;
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 payload, 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));
-> guidcpy()
This being a host tool, guidcpy cannot be used. You have used memcpy in the create_fwbin() for the same reason I guess.
Ah, right.
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) {
memcpy(&payload, guid, sizeof(efi_guid_t));
ditto
Same as above.
if (write_capsule_file(f, &payload, sizeof(payload),
"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
@@ -639,22 +715,49 @@ int main(int argc, char **argv) case 'd': dump_sig = 1; break;
case 'A':
capsule |= CAPSULE_ACCEPT;
break;
case 'R':
capsule |= CAPSULE_REVERT;
break; case 'h': print_usage(); exit(EXIT_SUCCESS); } }
if (capsule == (CAPSULE_ACCEPT | CAPSULE_REVERT)) {
fprintf(stderr,
"Select either of Accept or Revert capsule generation\n");
exit(EXIT_FAILURE);
}
empty_capsule = (capsule == CAPSULE_ACCEPT ||
capsule == CAPSULE_REVERT);
So empty_capsule is redundant as empty_capsule is equivalent with "capsule == CAPSULE_NORMAL_BLOB". I think that a single variable, say capsule_type, is enough.
I was using empty_capsule primarily to make the check done below look more succinct and readable. But I can change that to capsule != CAPSULE_NORMAL_BLOB.
/* check necessary parameters */
if ((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file))) {
if ((!empty_capsule &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(empty_capsule &&
((argc != optind + 1) ||
((capsule == CAPSULE_ACCEPT) && !guid) ||
((capsule == 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 (empty_capsule) {
if (create_empty_capsule(argv[argc - 1], guid,
capsule == CAPSULE_ACCEPT) < 0) {
if (capsule_type != CAPSULE_NORMAL_BLOB) create_empty_capsule(..., capsule_type == CAPSULE_ACCEPT);
Simple is the best :)
Common, please don't tell me that the code above is complicated. Just that we can do without the empty_capsule variable, yes.
Sorry, not complicated, but having two variables make little sense.
-Takahiro Akashi
-sughosh
-Takahiro Akashi
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); }
-- 2.25.1

Takahiro,
On Fri, 17 Jun 2022 at 06:16, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Sughosh,
On Thu, Jun 16, 2022 at 12:42:08PM +0530, Sughosh Ganu wrote:
hi Takahiro,
On Thu, 16 Jun 2022 at 06:31, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Sughosh,
On Wed, Jun 15, 2022 at 04:19:56PM +0530, Sughosh Ganu wrote:
On Wed, 15 Jun 2022 at 10:41, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Thu, Jun 09, 2022 at 05:59:58PM +0530, Sughosh Ganu 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
doc/mkeficapsule.1 | 29 ++++++--- tools/eficapsule.h | 8 +++ tools/mkeficapsule.c | 139 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 25 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
"image acceptance" -> "firmware acceptance"
Okay
I don't still understand why we need a guid for acceptance while revert doesn't require it. I believe that firmware update is "all or nothing", isn't it?
I believe this gives more flexibility in that different components might be required to accept the various firmware images. So, one component might accept the optee_os, while another might be responsible for accepting u-boot. In any case, we do check that all the components have their accepted bit set, and only if so, does the bank boot in the regular state.
Probably I don't understand the behavior. Let's assume that we have firmware A and firmware B and then update both. When the firmware A is accepted and B is not (not yet issuing acceptance capsule) and I try to reboot the system, what happens? From which bank does the system boot, old one or new one?
Once any/all of the images have been updated, on subsequent reboot, the platform would boot in Trial State from the updated bank. I have introduced an EFI variable, TrialeStateCtr for counting the number of times the system is booting in the trial state. The system remains in trial state as long as all the images from the updated bank have not been accepted. The platform boots in trial state for a particular number of iterations(configurable), and once that count has exceeded, the active bank value gets changed to the previous_active_index, and the platform subsequently boots from the other bank. If all the images do get accepted while the platform is in trial state, the platform transitions to the regular state, and continues booting from that bank.
Thank you for the details. But if I understand correctly, why do we need a partially-accepted status?
It's not so much about needing a partial update, but allowing for a scenario where multiple components might be tasked with accepting different firmware images. I see this as a kind of flexibility given. I do appreciate your point of view to have a common format for accept and reject capsules, but this format does not hurt IMO as long as it is documented and explained.
When some are accepted but others not, the system boots from an update bank in any way. Right? So at the end, all that we should do is to accept the boot "from a new bank" permanently (and transit to, what you say, regular state), or to let the system boot from an old bank (again, in a regular state).
I don't see any good reason for having a partial acceptance.
We should continue this discussion, but anyhow, you should think of adding such a description above in U-Boot doc.
Okay. Will do.
-sughosh
-Takahiro Akashi
-sughosh
In case of a firmware revert, it would not matter which firmware component is being reverted -- the platform would simply need to boot from the other bank. Do you see any issue with the current method that we have?
If there is a good reason, please describe a possible/expected scenario.
Where do you want me to explain this, in the feature documentation? Or do you think this can be elaborated in greater detail in the spec.
I prefer some explanation in U-Boot doc.
.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..e8eb6b070d 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,16 @@ 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";
+static bool empty_capsule; +static unsigned char capsule;
+enum {
CAPSULE_NORMAL_BLOB = 0,
CAPSULE_ACCEPT,
CAPSULE_REVERT,
+} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,24 +48,47 @@ 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},
};
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-h, --help print a help message\n",
tool_name);
if (empty_capsule) {
if (capsule == CAPSULE_ACCEPT) {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-h, --help print a help message\n"
);
} else {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n"
);
}
} else {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n",
tool_name);
}
}
/** @@ -564,6 +596,50 @@ 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;
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 payload, 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));
-> guidcpy()
This being a host tool, guidcpy cannot be used. You have used memcpy in the create_fwbin() for the same reason I guess.
Ah, right.
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) {
memcpy(&payload, guid, sizeof(efi_guid_t));
ditto
Same as above.
if (write_capsule_file(f, &payload, sizeof(payload),
"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
@@ -639,22 +715,49 @@ int main(int argc, char **argv) case 'd': dump_sig = 1; break;
case 'A':
capsule |= CAPSULE_ACCEPT;
break;
case 'R':
capsule |= CAPSULE_REVERT;
break; case 'h': print_usage(); exit(EXIT_SUCCESS); } }
if (capsule == (CAPSULE_ACCEPT | CAPSULE_REVERT)) {
fprintf(stderr,
"Select either of Accept or Revert capsule generation\n");
exit(EXIT_FAILURE);
}
empty_capsule = (capsule == CAPSULE_ACCEPT ||
capsule == CAPSULE_REVERT);
So empty_capsule is redundant as empty_capsule is equivalent with "capsule == CAPSULE_NORMAL_BLOB". I think that a single variable, say capsule_type, is enough.
I was using empty_capsule primarily to make the check done below look more succinct and readable. But I can change that to capsule != CAPSULE_NORMAL_BLOB.
/* check necessary parameters */
if ((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file))) {
if ((!empty_capsule &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(empty_capsule &&
((argc != optind + 1) ||
((capsule == CAPSULE_ACCEPT) && !guid) ||
((capsule == 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 (empty_capsule) {
if (create_empty_capsule(argv[argc - 1], guid,
capsule == CAPSULE_ACCEPT) < 0) {
if (capsule_type != CAPSULE_NORMAL_BLOB) create_empty_capsule(..., capsule_type == CAPSULE_ACCEPT);
Simple is the best :)
Common, please don't tell me that the code above is complicated. Just that we can do without the empty_capsule variable, yes.
Sorry, not complicated, but having two variables make little sense.
-Takahiro Akashi
-sughosh
-Takahiro Akashi
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); }
-- 2.25.1

On Thu, 9 Jun 2022 at 14:31, 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
doc/mkeficapsule.1 | 29 ++++++--- tools/eficapsule.h | 8 +++ tools/mkeficapsule.c | 139 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 25 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..e8eb6b070d 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -29,7 +29,16 @@ 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";
+static bool empty_capsule; +static unsigned char capsule;
Local function create_fwbin() defines a local variable with that same label. Should be ok to remove this 'capsule' variable if only using 'capsule_type' as suggested by Takahiro-san.
+enum {
CAPSULE_NORMAL_BLOB = 0,
CAPSULE_ACCEPT,
CAPSULE_REVERT,
+} capsule_type;
static struct option options[] = { {"guid", required_argument, NULL, 'g'}, @@ -39,24 +48,47 @@ 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},
};
static void print_usage(void) {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-h, --help print a help message\n",
tool_name);
if (empty_capsule) {
if (capsule == CAPSULE_ACCEPT) {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-h, --help print a help message\n"
);
} else {
fprintf(stderr, "Usage: %s [options] <output file>\n",
tool_name);
fprintf(stderr, "Options:\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n"
);
}
} else {
fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
"Options:\n"
"\t-g, --guid <guid string> guid for image blob type\n"
"\t-i, --index <index> update image index\n"
"\t-I, --instance <instance> update hardware instance\n"
"\t-p, --private-key <privkey file> private key file\n"
"\t-c, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
"\t-A, --fw-accept firmware accept capsule\n"
"\t-R, --fw-revert firmware revert capsule\n"
"\t-h, --help print a help message\n",
tool_name);
}
}
/** @@ -564,6 +596,50 @@ 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;
It would be better to initialize the header to known value, e.g. = { }; here.
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 payload, 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) {
memcpy(&payload, guid, sizeof(efi_guid_t));
if (write_capsule_file(f, &payload, sizeof(payload),
"FW Accept Capsule Payload"))
nitpicking: is payload temp variable needed? could pass guid as argument.
goto err;
}
ret = 0;
+err:
if (f)
fclose(f);
return ret;
+}
/**
- main - main entry function of mkeficapsule
- @argc: Number of arguments
@@ -639,22 +715,49 @@ int main(int argc, char **argv) case 'd': dump_sig = 1; break;
case 'A':
capsule |= CAPSULE_ACCEPT;
break;
case 'R':
capsule |= CAPSULE_REVERT;
break; case 'h': print_usage(); exit(EXIT_SUCCESS); } }
if (capsule == (CAPSULE_ACCEPT | CAPSULE_REVERT)) {
fprintf(stderr,
"Select either of Accept or Revert capsule generation\n");
exit(EXIT_FAILURE);
}
empty_capsule = (capsule == CAPSULE_ACCEPT ||
capsule == CAPSULE_REVERT);
/* check necessary parameters */
if ((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file))) {
if ((!empty_capsule &&
((argc != optind + 2) || !guid ||
((privkey_file && !cert_file) ||
(!privkey_file && cert_file)))) ||
(empty_capsule &&
((argc != optind + 1) ||
((capsule == CAPSULE_ACCEPT) && !guid) ||
((capsule == 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 (empty_capsule) {
if (create_empty_capsule(argv[argc - 1], guid,
capsule == 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); }
-- 2.25.1

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 --- doc/develop/uefi/fwu_updates.rst | 142 +++++++++++++++++++++++++++++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + 3 files changed, 145 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..1c34beb7d5 --- /dev/null +++ b/doc/develop/uefi/fwu_updates.rst @@ -0,0 +1,142 @@ +.. 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. + +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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Hi,
Two minor issues.
On 6/9/22 14:29, Sughosh Ganu wrote:
Add documentattion for the FWU Multi Bank Update feature. The document
s/documentattio/documentation
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
doc/develop/uefi/fwu_updates.rst | 142 +++++++++++++++++++++++++++++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + 3 files changed, 145 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..1c34beb7d5 --- /dev/null +++ b/doc/develop/uefi/fwu_updates.rst @@ -0,0 +1,142 @@ +.. 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
s/parititions/partitions/
+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.
+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
Regards
Patrick

From: Masami Hiramatsu masami.hiramatsu@linaro.org
For the platform which doesn't have GPT partitions for the firmware but on MTD devices, the FWU metadata is stored on MTD device as raw image at specific offset. This driver gives the access methods for the FWU metadata information on such MTD devices.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@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 12a5b4fe04..c574c59be2 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..9eb471e73e --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_mtd.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 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 Thu, 9 Jun 2022 at 14:31, Sughosh Ganu sughosh.ganu@linaro.org wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
For the platform which doesn't have GPT partitions for the firmware but on MTD devices, the FWU metadata is stored on MTD device as raw image at specific offset. This driver gives the access methods for the FWU metadata information on such MTD devices.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@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 12a5b4fe04..c574c59be2 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..9eb471e73e --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_mtd.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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;
+};
Add an inline description of the fields.
+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;
Is it possible this can fail knowing that content of both images were checked at probe time? If it can fail, then, as for the GPT case, i think this function should ensure the primary and secondary images, in the boot media, are valid and consistent (sae data). For example calling back fwu_mtd_mdata_check() for that purpose. Otherwise, if fwu_mtd_update_mdata() is interrupted (e.g. power loss), it would leave no valid fwu_mdata in the storage, preventing the system from rebooting.
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),
+};
2.25.1

Hi,
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
For the platform which doesn't have GPT partitions for the firmware but on MTD devices, the FWU metadata is stored on MTD device as raw image at specific offset. This driver gives the access methods for the FWU metadata information on such MTD devices.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@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 12a5b4fe04..c574c59be2 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..9eb471e73e --- /dev/null +++ b/drivers/fwu-mdata/fwu_mdata_mtd.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2022, Linaro Limited
- */
+ #define LOG_CATEGORY UCLASS_FWU_MDATA
it is requested for log command to filter by uclass
the next include is not needed ?
#include <common.h>
+#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <malloc.h> +#include <memalign.h> +#include <spi.h> +#include <spi_flash.h> +#include <flash.h>
+#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h>
+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 ={};
missing space : ' = {};'
- 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);
dev_err(dev,
- ret = fwu_mtd_load_secondary_mdata(mtd_priv, &secondary);
- if (ret < 0)
log_err("Failed to read the secondary mdata: %d\n", ret);
dev_err(dev,
- if (primary && secondary) {
if (memcmp(primary, secondary, sizeof(struct fwu_mdata))) {
log_err("The primary and the secondary mdata are different\n");
dev_err(dev,
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");
dev_err(dev,
- } else if (secondary) {
ret = fwu_mtd_save_primary_mdata(mtd_priv, secondary);
if (ret < 0)
log_err("Restoring primary mdata partition failed\n");
dev_err(dev,
- }
- 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);
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);
or directly use dev_read_phandle_with_args() ?
ret = dev_read_phandle_with_args(dev, "fwu-mdata-store", NULL, 0, 0, &phandle_args);
and using = phandle_args.node
- 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);
can be replaced by dev_read_u32_index()
ret = dev_read_u32_index(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);
ret = dev_read_u32_index(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),
+};
regards
Patrick

From: Masami Hiramatsu masami.hiramatsu@linaro.org
Add a devicetree-binding YAML file for the FWU metadata on MTD devices without GPT.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- .../firmware/uboot,fwu-mdata-mtd.yaml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml
diff --git a/doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml b/doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml new file mode 100644 index 0000000000..4f5404f999 --- /dev/null +++ b/doc/device-tree-bindings/firmware/uboot,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 Thu, 9 Jun 2022 at 14:31, Sughosh Ganu sughosh.ganu@linaro.org wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
Add a devicetree-binding YAML file for the FWU metadata on MTD devices without GPT.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
.../firmware/uboot,fwu-mdata-mtd.yaml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml
diff --git a/doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml b/doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml new file mode 100644 index 0000000000..4f5404f999 --- /dev/null +++ b/doc/device-tree-bindings/firmware/uboot,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>;
Is a single 32bit value sufficient to define a block offset in an MTD device? I was thinking of NAND with a density > 4GByte.
- };
-- 2.25.1

Hi,
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
Add a devicetree-binding YAML file for the FWU metadata on MTD devices without GPT.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
.../firmware/uboot,fwu-mdata-mtd.yaml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml
diff --git a/doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml b/doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml new file mode 100644 index 0000000000..4f5404f999 --- /dev/null +++ b/doc/device-tree-bindings/firmware/uboot,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>;
- };
Do you need to have offset her => normally it is already managed by mtd partition
spi-flash@0 { #address-cells = <1>; #size-cells = <1>; compatible = "spi-flash", "jedec,spi-nor"; .... metadata1: partition@metadata1 { reg = <0x500000 0x30000>; label = "metadata1"; }; metadata2: partition@metadata2 { reg = <0x530000 0x30000>; label = "metadata2"; }; .... };
So the needed offset information can be found with 2 handles
to the MTD partitions in the device tree !
+ fwu-mdata { + compatible = "u-boot,fwu-mdata-mtd"; + fwu-mdata-store = <&metadata1>, <&metadata2>; + };
This proposal can simplify the binding
+ fwu-mdata-store: + maxItems: 2 + description: Phandle of the two MTD partitions which contains the FWU medatata.
+required: + - compatible + - fwu-mdata-store
Patrick

From: Masami Hiramatsu masami.hiramatsu@linaro.org
Add 'mkfwumdata' tool which can generate an image of the FWU metadata which is required for initializing the platform.
Usage: mkfwumdata -i NR_IMAGES -b NR_BANKS [--guid] \ LOCATION_UUID0,IMAGE_TYPE_UUID0,BANK0_IMAGE_UUID[,BANK1_IMAGE_UUID[,...]] \ LOCATION_UUID1,... \ IMAGE_FILE
'-i' takes the number of images and '-b' takes the number of banks. This takes lists of uuids for the images on arguments, and the last argument must be the output image file name.
'--guid' (or '-g' in short) allows user to specify the location UUID and image IDs in GUID instead of UUID. This option is useful if the platform uses GPT partiotion. In this case, the UUID list (for an image) becomes;
DiskGUID,ParitionTypeGUID,UniquePartitionGUID,...
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- tools/Kconfig | 9 ++ tools/Makefile | 4 + tools/mkfwumdata.c | 298 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 311 insertions(+) create mode 100644 tools/mkfwumdata.c
diff --git a/tools/Kconfig b/tools/Kconfig index 117c921da3..3484be99d0 100644 --- a/tools/Kconfig +++ b/tools/Kconfig @@ -98,4 +98,13 @@ config TOOLS_MKEFICAPSULE optionally sign that file. If you want to enable UEFI capsule update feature on your target, you certainly need this.
+config TOOLS_MKFWUMDATA + bool "Build mkfwumdata command" + default y if FWU_MULTI_BANK_UPDATE + help + This command allows users to create a raw image of the FWU + metadata for initial installation of the FWU multi bank + update on the board. The installation method depends on + the platform. + endmenu diff --git a/tools/Makefile b/tools/Makefile index 9f2339666a..cd39e5ff6f 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -245,6 +245,10 @@ HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include HOSTLDLIBS_mkeficapsule += -lgnutls -luuid hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
+mkfwumdata-objs := mkfwumdata.o lib/crc32.o +HOSTLDLIBS_mkfwumdata += -luuid +hostprogs-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata + # We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of # exceptions for files that aren't complaint. diff --git a/tools/mkfwumdata.c b/tools/mkfwumdata.c new file mode 100644 index 0000000000..4eb304cae3 --- /dev/null +++ b/tools/mkfwumdata.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <u-boot/crc.h> +#include <unistd.h> +#include <uuid/uuid.h> + +/* This will dynamically allocate the fwu_mdata */ +#define CONFIG_FWU_NUM_BANKS 0 +#define CONFIG_FWU_NUM_IMAGES_PER_BANK 0 + +/* Since we can not include fwu.h, redefine version here. */ +#define FWU_MDATA_VERSION 1 + +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +#include <fwu_mdata.h> + +/* TODO: Endianess conversion may be required for some arch. */ + +static const char *opts_short = "b:i:a:gh"; + +static struct option options[] = { + {"banks", required_argument, NULL, 'b'}, + {"images", required_argument, NULL, 'i'}, + {"guid", required_argument, NULL, 'g'}, + {"active-bank", required_argument, NULL, 'a'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0}, +}; + +static void print_usage(void) +{ + fprintf(stderr, "Usage: mkfwumdata [options] <UUIDs list> [<UUIDs list>...] <output file>\n"); + fprintf(stderr, "Options:\n" + "\t-i, --images <num> Number of images\n" + "\t-b, --banks <num> Number of banks\n" + "\t-a, --active-bank <num> Active bank\n" + "\t-g, --guid Use GUID instead of UUID\n" + "\t-h, --help print a help message\n" + ); + fprintf(stderr, "UUIDs list syntax:\n" + "\t<location uuid>,<image type uuid>,<image uuid>[,<image uuid>]\n" + "\n\tYou must specify # of banks of image-uuid and # of images of the lists.\n" + "\tIf the location uuid and image uuid are '0', those are filled with null uuid.\n" + ); +} + +static bool __use_guid; +static u32 active_bank; + +struct fwu_mdata_object { + size_t images; + size_t banks; + size_t size; + struct fwu_mdata *mdata; +}; + +struct fwu_mdata_object *fwu_alloc_mdata(size_t images, size_t banks) +{ + struct fwu_mdata_object *mobj; + + mobj = malloc(sizeof(*mobj)); + if (!mobj) + return NULL; + mobj->size = sizeof(struct fwu_mdata) + + (sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * banks) * images; + mobj->images = images; + mobj->banks = banks; + mobj->mdata = malloc(mobj->size); + if (!mobj->mdata) { + free(mobj); + return NULL; + } + memset(mobj->mdata, 0, mobj->size); + + return mobj; +} + +struct fwu_image_entry *fwu_get_image(struct fwu_mdata_object *mobj, size_t idx) +{ + size_t offset; + + offset = sizeof(struct fwu_mdata) + + (sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * mobj->banks) * idx; + + return (struct fwu_image_entry *)((char *)mobj->mdata + offset); +} + +struct fwu_image_bank_info *fwu_get_bank(struct fwu_mdata_object *mobj, + size_t img_idx, size_t bnk_idx) +{ + size_t offset; + + offset = sizeof(struct fwu_mdata) + + (sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * mobj->banks) * img_idx + + sizeof(struct fwu_image_entry) + + sizeof(struct fwu_image_bank_info) * bnk_idx; + + return (struct fwu_image_bank_info *)((char *)mobj->mdata + offset); +} + +/** + * convert_uuid_to_guid() - convert UUID to GUID + * @buf: UUID binary + * + * UUID and GUID have the same data structure, but their binary + * formats are different due to the endianness. See lib/uuid.c. + * Since uuid_parse() can handle only UUID, this function must + * be called to get correct data for GUID when parsing a string. + * + * The correct data will be returned in @buf. + */ +void convert_uuid_to_guid(unsigned char *buf) +{ + unsigned char c; + + c = buf[0]; + buf[0] = buf[3]; + buf[3] = c; + c = buf[1]; + buf[1] = buf[2]; + buf[2] = c; + + c = buf[4]; + buf[4] = buf[5]; + buf[5] = c; + + c = buf[6]; + buf[6] = buf[7]; + buf[7] = c; +} + +int uuid_guid_parse(char *uuidstr, unsigned char *uuid) +{ + int ret; + + ret = uuid_parse(uuidstr, uuid); + if (ret < 0) + return ret; + + if (__use_guid) + convert_uuid_to_guid(uuid); + + return ret; +} + +int fwu_parse_fill_image_uuid(struct fwu_mdata_object *mobj, + size_t idx, char *uuids) +{ + struct fwu_image_entry *image = fwu_get_image(mobj, idx); + struct fwu_image_bank_info *bank; + char *p = uuids, *uuid; + int i; + + if (!image) + return -ENOENT; + + /* Image location UUID */ + uuid = strsep(&p, ","); + if (!uuid) + return -EINVAL; + + if (strcmp(uuid, "0") && + uuid_guid_parse(uuid, (unsigned char *)&image->location_uuid) < 0) + return -EINVAL; + + /* Image type UUID */ + uuid = strsep(&p, ","); + if (!uuid) + return -EINVAL; + + if (uuid_guid_parse(uuid, (unsigned char *)&image->image_type_uuid) < 0) + return -EINVAL; + + /* Fill bank image-UUID */ + for (i = 0; i < mobj->banks; i++) { + bank = fwu_get_bank(mobj, idx, i); + if (!bank) + return -ENOENT; + bank->accepted = 1; + uuid = strsep(&p, ","); + if (!uuid) + return -EINVAL; + + if (strcmp(uuid, "0") && + uuid_guid_parse(uuid, (unsigned char *)&bank->image_uuid) < 0) + return -EINVAL; + } + return 0; +} + +/* Caller must ensure that @uuids[] has @mobj->images entries. */ +int fwu_parse_fill_uuids(struct fwu_mdata_object *mobj, char *uuids[]) +{ + struct fwu_mdata *mdata = mobj->mdata; + int i, ret; + + mdata->version = FWU_MDATA_VERSION; + mdata->active_index = active_bank; + mdata->previous_active_index = active_bank ? 0 : + (uint32_t)(mobj->banks - 1); + + for (i = 0; i < mobj->images; i++) { + ret = fwu_parse_fill_image_uuid(mobj, i, uuids[i]); + if (ret < 0) + return ret; + } + + mdata->crc32 = crc32(0, (const unsigned char *)&mdata->version, + mobj->size - sizeof(uint32_t)); + + return 0; +} + +int fwu_make_mdata(size_t images, size_t banks, char *uuids[], char *output) +{ + struct fwu_mdata_object *mobj; + FILE *file; + int ret; + + mobj = fwu_alloc_mdata(images, banks); + if (!mobj) + return -ENOMEM; + + ret = fwu_parse_fill_uuids(mobj, uuids); + if (ret < 0) + return ret; + + file = fopen(output, "w"); + if (!file) + return -errno; + + ret = fwrite(mobj->mdata, mobj->size, 1, file); + if (ret != mobj->size) + ret = -errno; + else + ret = 0; + + fclose(file); + return ret; +} + +int main(int argc, char *argv[]) +{ + unsigned long banks = 0, images = 0; + int c, ret; + + do { + c = getopt_long(argc, argv, opts_short, options, NULL); + switch (c) { + case 'h': + print_usage(); + return 0; + case 'b': + banks = strtoul(optarg, NULL, 0); + break; + case 'i': + images = strtoul(optarg, NULL, 0); + break; + case 'g': + __use_guid = true; + break; + case 'a': + active_bank = strtoul(optarg, NULL, 0); + break; + } + } while (c != -1); + + if (!banks || !images) { + fprintf(stderr, "Error: The number of banks and images must not be 0.\n"); + return -EINVAL; + } + + /* This command takes UUIDs * images and output file. */ + if (optind + images + 1 != argc) { + fprintf(stderr, "Error: UUID list or output file is not specified or too much.\n"); + print_usage(); + return -ERANGE; + } + ret = fwu_make_mdata(images, banks, argv + optind, argv[argc - 1]); + if (ret < 0) + fprintf(stderr, "Error: Failed to parse and write image: %s\n", + strerror(-ret)); + return ret; +}

On Thu, 9 Jun 2022 at 14:31, Sughosh Ganu sughosh.ganu@linaro.org wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
Add 'mkfwumdata' tool which can generate an image of the FWU metadata which is required for initializing the platform.
Usage: mkfwumdata -i NR_IMAGES -b NR_BANKS [--guid] \ LOCATION_UUID0,IMAGE_TYPE_UUID0,BANK0_IMAGE_UUID[,BANK1_IMAGE_UUID[,...]] \ LOCATION_UUID1,... \ IMAGE_FILE
'-i' takes the number of images and '-b' takes the number of banks. This takes lists of uuids for the images on arguments, and the last argument must be the output image file name.
'--guid' (or '-g' in short) allows user to specify the location UUID and image IDs in GUID instead of UUID. This option is useful if the platform uses GPT partiotion. In this case, the UUID list (for an image) becomes;
DiskGUID,ParitionTypeGUID,UniquePartitionGUID,...
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
tools/Kconfig | 9 ++ tools/Makefile | 4 + tools/mkfwumdata.c | 298 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 311 insertions(+) create mode 100644 tools/mkfwumdata.c
diff --git a/tools/Kconfig b/tools/Kconfig index 117c921da3..3484be99d0 100644 --- a/tools/Kconfig +++ b/tools/Kconfig @@ -98,4 +98,13 @@ config TOOLS_MKEFICAPSULE optionally sign that file. If you want to enable UEFI capsule update feature on your target, you certainly need this.
+config TOOLS_MKFWUMDATA
bool "Build mkfwumdata command"
default y if FWU_MULTI_BANK_UPDATE
help
This command allows users to create a raw image of the FWU
metadata for initial installation of the FWU multi bank
update on the board. The installation method depends on
the platform.
endmenu diff --git a/tools/Makefile b/tools/Makefile index 9f2339666a..cd39e5ff6f 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -245,6 +245,10 @@ HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include HOSTLDLIBS_mkeficapsule += -lgnutls -luuid hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
+mkfwumdata-objs := mkfwumdata.o lib/crc32.o +HOSTLDLIBS_mkfwumdata += -luuid +hostprogs-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata
# We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of # exceptions for files that aren't complaint. diff --git a/tools/mkfwumdata.c b/tools/mkfwumdata.c new file mode 100644 index 0000000000..4eb304cae3 --- /dev/null +++ b/tools/mkfwumdata.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0+
+#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <u-boot/crc.h> +#include <unistd.h> +#include <uuid/uuid.h>
+/* This will dynamically allocate the fwu_mdata */ +#define CONFIG_FWU_NUM_BANKS 0 +#define CONFIG_FWU_NUM_IMAGES_PER_BANK 0
+/* Since we can not include fwu.h, redefine version here. */ +#define FWU_MDATA_VERSION 1
+typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64;
+#include <fwu_mdata.h>
+/* TODO: Endianess conversion may be required for some arch. */
+static const char *opts_short = "b:i:a:gh";
+static struct option options[] = {
{"banks", required_argument, NULL, 'b'},
{"images", required_argument, NULL, 'i'},
{"guid", required_argument, NULL, 'g'},
{"active-bank", required_argument, NULL, 'a'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0},
+};
+static void print_usage(void) +{
fprintf(stderr, "Usage: mkfwumdata [options] <UUIDs list> [<UUIDs list>...] <output file>\n");
fprintf(stderr, "Options:\n"
"\t-i, --images <num> Number of images\n"
"\t-b, --banks <num> Number of banks\n"
"\t-a, --active-bank <num> Active bank\n"
"\t-g, --guid Use GUID instead of UUID\n"
"\t-h, --help print a help message\n"
);
fprintf(stderr, "UUIDs list syntax:\n"
"\t<location uuid>,<image type uuid>,<image uuid>[,<image uuid>]\n"
"\n\tYou must specify # of banks of image-uuid and # of images of the lists.\n"
It's not really explicit how many ',<image uuid>' occurrences are needed. Maybe: In a <UUIDs list> item, there must be as many <image uuid> occurrences as the given number of banks. There must be as many <UUIDs list> items as the given number of images.
"\tIf the location uuid and image uuid are '0', those are filled with null uuid.\n"
);
+}
+static bool __use_guid; +static u32 active_bank;
+struct fwu_mdata_object {
size_t images;
size_t banks;
size_t size;
struct fwu_mdata *mdata;
+};
+struct fwu_mdata_object *fwu_alloc_mdata(size_t images, size_t banks) +{
struct fwu_mdata_object *mobj;
mobj = malloc(sizeof(*mobj));
calloc()?
if (!mobj)
return NULL;
mobj->size = sizeof(struct fwu_mdata) +
(sizeof(struct fwu_image_entry) +
sizeof(struct fwu_image_bank_info) * banks) * images;
mobj->images = images;
mobj->banks = banks;
mobj->mdata = malloc(mobj->size);
calloc()?
if (!mobj->mdata) {
free(mobj);
return NULL;
}
memset(mobj->mdata, 0, mobj->size);
return mobj;
+}
+struct fwu_image_entry *fwu_get_image(struct fwu_mdata_object *mobj, size_t idx) +{
size_t offset;
offset = sizeof(struct fwu_mdata) +
(sizeof(struct fwu_image_entry) +
sizeof(struct fwu_image_bank_info) * mobj->banks) * idx;
return (struct fwu_image_entry *)((char *)mobj->mdata + offset);
+}
+struct fwu_image_bank_info *fwu_get_bank(struct fwu_mdata_object *mobj,
size_t img_idx, size_t bnk_idx)
+{
size_t offset;
offset = sizeof(struct fwu_mdata) +
(sizeof(struct fwu_image_entry) +
sizeof(struct fwu_image_bank_info) * mobj->banks) * img_idx +
sizeof(struct fwu_image_entry) +
sizeof(struct fwu_image_bank_info) * bnk_idx;
return (struct fwu_image_bank_info *)((char *)mobj->mdata + offset);
+}
+/**
- convert_uuid_to_guid() - convert UUID to GUID
- @buf: UUID binary
- UUID and GUID have the same data structure, but their binary
- formats are different due to the endianness. See lib/uuid.c.
- Since uuid_parse() can handle only UUID, this function must
- be called to get correct data for GUID when parsing a string.
- The correct data will be returned in @buf.
- */
+void convert_uuid_to_guid(unsigned char *buf) +{
unsigned char c;
c = buf[0];
buf[0] = buf[3];
buf[3] = c;
c = buf[1];
buf[1] = buf[2];
buf[2] = c;
c = buf[4];
buf[4] = buf[5];
buf[5] = c;
c = buf[6];
buf[6] = buf[7];
buf[7] = c;
+}
+int uuid_guid_parse(char *uuidstr, unsigned char *uuid) +{
int ret;
ret = uuid_parse(uuidstr, uuid);
if (ret < 0)
return ret;
if (__use_guid)
convert_uuid_to_guid(uuid);
return ret;
+}
+int fwu_parse_fill_image_uuid(struct fwu_mdata_object *mobj,
size_t idx, char *uuids)
+{
struct fwu_image_entry *image = fwu_get_image(mobj, idx);
struct fwu_image_bank_info *bank;
char *p = uuids, *uuid;
int i;
if (!image)
return -ENOENT;
/* Image location UUID */
uuid = strsep(&p, ",");
if (!uuid)
return -EINVAL;
if (strcmp(uuid, "0") &&
uuid_guid_parse(uuid, (unsigned char *)&image->location_uuid) < 0)
return -EINVAL;
/* Image type UUID */
uuid = strsep(&p, ",");
if (!uuid)
return -EINVAL;
if (uuid_guid_parse(uuid, (unsigned char *)&image->image_type_uuid) < 0)
return -EINVAL;
/* Fill bank image-UUID */
for (i = 0; i < mobj->banks; i++) {
bank = fwu_get_bank(mobj, idx, i);
if (!bank)
return -ENOENT;
bank->accepted = 1;
uuid = strsep(&p, ",");
if (!uuid)
return -EINVAL;
if (strcmp(uuid, "0") &&
uuid_guid_parse(uuid, (unsigned char *)&bank->image_uuid) < 0)
return -EINVAL;
}
return 0;
+}
+/* Caller must ensure that @uuids[] has @mobj->images entries. */ +int fwu_parse_fill_uuids(struct fwu_mdata_object *mobj, char *uuids[]) +{
struct fwu_mdata *mdata = mobj->mdata;
int i, ret;
mdata->version = FWU_MDATA_VERSION;
mdata->active_index = active_bank;
mdata->previous_active_index = active_bank ? 0 :
(uint32_t)(mobj->banks - 1);
This looks platform specific (see fwu_plat_get_update_index() is platform specific). Maybe a dedicated argument to this tool should define the alternate/previous active index.
for (i = 0; i < mobj->images; i++) {
ret = fwu_parse_fill_image_uuid(mobj, i, uuids[i]);
if (ret < 0)
return ret;
}
mdata->crc32 = crc32(0, (const unsigned char *)&mdata->version,
mobj->size - sizeof(uint32_t));
return 0;
+}
+int fwu_make_mdata(size_t images, size_t banks, char *uuids[], char *output) +{
struct fwu_mdata_object *mobj;
FILE *file;
int ret;
mobj = fwu_alloc_mdata(images, banks);
if (!mobj)
return -ENOMEM;
ret = fwu_parse_fill_uuids(mobj, uuids);
if (ret < 0)
I think this error case and those below should also free the memory allocated by fwu_alloc_mdata().
return ret;
file = fopen(output, "w");
if (!file)
return -errno;
ret = fwrite(mobj->mdata, mobj->size, 1, file);
if (ret != mobj->size)
ret = -errno;
else
ret = 0;
fclose(file);
return ret;
+}
+int main(int argc, char *argv[]) +{
unsigned long banks = 0, images = 0;
int c, ret;
do {
c = getopt_long(argc, argv, opts_short, options, NULL);
switch (c) {
case 'h':
print_usage();
return 0;
case 'b':
banks = strtoul(optarg, NULL, 0);
break;
case 'i':
images = strtoul(optarg, NULL, 0);
break;
case 'g':
__use_guid = true;
break;
case 'a':
active_bank = strtoul(optarg, NULL, 0);
break;
}
} while (c != -1);
if (!banks || !images) {
fprintf(stderr, "Error: The number of banks and images must not be 0.\n");
return -EINVAL;
}
/* This command takes UUIDs * images and output file. */
if (optind + images + 1 != argc) {
fprintf(stderr, "Error: UUID list or output file is not specified or too much.\n");
print_usage();
return -ERANGE;
}
ret = fwu_make_mdata(images, banks, argv + optind, argv[argc - 1]);
if (ret < 0)
fprintf(stderr, "Error: Failed to parse and write image: %s\n",
strerror(-ret));
return ret;
+}
2.25.1

On 6/21/22 12:57, Etienne Carriere wrote:
On Thu, 9 Jun 2022 at 14:31, Sughosh Ganu sughosh.ganu@linaro.org wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
Add 'mkfwumdata' tool which can generate an image of the FWU metadata which is required for initializing the platform.
Usage: mkfwumdata -i NR_IMAGES -b NR_BANKS [--guid] \ LOCATION_UUID0,IMAGE_TYPE_UUID0,BANK0_IMAGE_UUID[,BANK1_IMAGE_UUID[,...]] \ LOCATION_UUID1,... \ IMAGE_FILE
'-i' takes the number of images and '-b' takes the number of banks. This takes lists of uuids for the images on arguments, and the last argument must be the output image file name.
'--guid' (or '-g' in short) allows user to specify the location UUID and image IDs in GUID instead of UUID. This option is useful if the platform uses GPT partiotion. In this case, the UUID list (for an image) becomes;
DiskGUID,ParitionTypeGUID,UniquePartitionGUID,...
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
tools/Kconfig | 9 ++ tools/Makefile | 4 + tools/mkfwumdata.c | 298 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 311 insertions(+) create mode 100644 tools/mkfwumdata.c
diff --git a/tools/Kconfig b/tools/Kconfig index 117c921da3..3484be99d0 100644 --- a/tools/Kconfig +++ b/tools/Kconfig @@ -98,4 +98,13 @@ config TOOLS_MKEFICAPSULE optionally sign that file. If you want to enable UEFI capsule update feature on your target, you certainly need this.
+config TOOLS_MKFWUMDATA
bool "Build mkfwumdata command"
default y if FWU_MULTI_BANK_UPDATE
help
This command allows users to create a raw image of the FWU
metadata for initial installation of the FWU multi bank
update on the board. The installation method depends on
the platform.
- endmenu
diff --git a/tools/Makefile b/tools/Makefile index 9f2339666a..cd39e5ff6f 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -245,6 +245,10 @@ HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include HOSTLDLIBS_mkeficapsule += -lgnutls -luuid hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
+mkfwumdata-objs := mkfwumdata.o lib/crc32.o +HOSTLDLIBS_mkfwumdata += -luuid +hostprogs-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata
- # We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of # exceptions for files that aren't complaint.
diff --git a/tools/mkfwumdata.c b/tools/mkfwumdata.c new file mode 100644 index 0000000000..4eb304cae3 --- /dev/null +++ b/tools/mkfwumdata.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0+
+#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <u-boot/crc.h> +#include <unistd.h> +#include <uuid/uuid.h>
+/* This will dynamically allocate the fwu_mdata */ +#define CONFIG_FWU_NUM_BANKS 0 +#define CONFIG_FWU_NUM_IMAGES_PER_BANK 0
+/* Since we can not include fwu.h, redefine version here. */ +#define FWU_MDATA_VERSION 1
+typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64;
+#include <fwu_mdata.h>
+/* TODO: Endianess conversion may be required for some arch. */
+static const char *opts_short = "b:i:a:gh";
+static struct option options[] = {
{"banks", required_argument, NULL, 'b'},
{"images", required_argument, NULL, 'i'},
{"guid", required_argument, NULL, 'g'},
{"active-bank", required_argument, NULL, 'a'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0},
+};
+static void print_usage(void) +{
fprintf(stderr, "Usage: mkfwumdata [options] <UUIDs list> [<UUIDs list>...] <output file>\n");
fprintf(stderr, "Options:\n"
"\t-i, --images <num> Number of images\n"
"\t-b, --banks <num> Number of banks\n"
"\t-a, --active-bank <num> Active bank\n"
"\t-g, --guid Use GUID instead of UUID\n"
"\t-h, --help print a help message\n"
);
fprintf(stderr, "UUIDs list syntax:\n"
"\t<location uuid>,<image type uuid>,<image uuid>[,<image uuid>]\n"
"\n\tYou must specify # of banks of image-uuid and # of images of the lists.\n"
It's not really explicit how many ',<image uuid>' occurrences are needed. Maybe: In a <UUIDs list> item, there must be as many <image uuid> occurrences as the given number of banks. There must be as many <UUIDs list> items as the given number of images.
+1 on this. I was trying 2 bank 2 image configuration and it is not clear how it should be specified. Better description would be good.
Thanks, Michal

Hi,
one minor remark.
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
Add 'mkfwumdata' tool which can generate an image of the FWU metadata which is required for initializing the platform.
Usage: mkfwumdata -i NR_IMAGES -b NR_BANKS [--guid] \ LOCATION_UUID0,IMAGE_TYPE_UUID0,BANK0_IMAGE_UUID[,BANK1_IMAGE_UUID[,...]] \ LOCATION_UUID1,... \ IMAGE_FILE
'-i' takes the number of images and '-b' takes the number of banks. This takes lists of uuids for the images on arguments, and the last argument must be the output image file name.
'--guid' (or '-g' in short) allows user to specify the location UUID and image IDs in GUID instead of UUID. This option is useful if the platform uses GPT partiotion. In this case, the UUID list (for an image) becomes;
DiskGUID,ParitionTypeGUID,UniquePartitionGUID,...
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
tools/Kconfig | 9 ++ tools/Makefile | 4 + tools/mkfwumdata.c | 298 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 311 insertions(+) create mode 100644 tools/mkfwumdata.c
diff --git a/tools/Kconfig b/tools/Kconfig index 117c921da3..3484be99d0 100644 --- a/tools/Kconfig +++ b/tools/Kconfig @@ -98,4 +98,13 @@ config TOOLS_MKEFICAPSULE optionally sign that file. If you want to enable UEFI capsule update feature on your target, you certainly need this.
+config TOOLS_MKFWUMDATA
- bool "Build mkfwumdata command"
- default y if FWU_MULTI_BANK_UPDATE
- help
This command allows users to create a raw image of the FWU
metadata for initial installation of the FWU multi bank
update on the board. The installation method depends on
the platform.
- endmenu
diff --git a/tools/Makefile b/tools/Makefile index 9f2339666a..cd39e5ff6f 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -245,6 +245,10 @@ HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include HOSTLDLIBS_mkeficapsule += -lgnutls -luuid hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
+mkfwumdata-objs := mkfwumdata.o lib/crc32.o +HOSTLDLIBS_mkfwumdata += -luuid +hostprogs-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata
- # We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of # exceptions for files that aren't complaint.
diff --git a/tools/mkfwumdata.c b/tools/mkfwumdata.c new file mode 100644 index 0000000000..4eb304cae3 --- /dev/null +++ b/tools/mkfwumdata.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0+
+#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <u-boot/crc.h> +#include <unistd.h> +#include <uuid/uuid.h>
+/* This will dynamically allocate the fwu_mdata */ +#define CONFIG_FWU_NUM_BANKS 0 +#define CONFIG_FWU_NUM_IMAGES_PER_BANK 0
+/* Since we can not include fwu.h, redefine version here. */ +#define FWU_MDATA_VERSION 1
adding typedef should be avoided
can you include "asm/types.h" or "linux/types.h" to use the generic define
+typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64;
+#include <fwu_mdata.h>
+/* TODO: Endianess conversion may be required for some arch. */
+static const char *opts_short = "b:i:a:gh";
+static struct option options[] = {
- {"banks", required_argument, NULL, 'b'},
- {"images", required_argument, NULL, 'i'},
- {"guid", required_argument, NULL, 'g'},
- {"active-bank", required_argument, NULL, 'a'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0},
+};
[...]
Regards
Patrick

From: Masami Hiramatsu masami.hiramatsu@linaro.org
Update documentation for the FWU non-GPT MTD device and mkfwumdata command.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- doc/develop/uefi/fwu_updates.rst | 82 +++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 12 deletions(-)
diff --git a/doc/develop/uefi/fwu_updates.rst b/doc/develop/uefi/fwu_updates.rst index 1c34beb7d5..1ea54328d1 100644 --- a/doc/develop/uefi/fwu_updates.rst +++ b/doc/develop/uefi/fwu_updates.rst @@ -15,10 +15,11 @@ 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 +called FWU 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. +media. If the firmware images are stored on the flash device which +has no GPT, the platform driver can provide the image identification +feature.
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 @@ -43,21 +44,31 @@ The feature can be enabled by specifying the following configs:: 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> + CONFIG_TOOLS_MKFWUMDATA=y + + CONFIG_FWU_MDATA_GPT_BLK=y + CONFIG_FWU_MDATA_SF=y
-in the .config file +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. +Update functionality. And the third group of configs are FWU Metadata +drivers. You can enable either one of ``CONFIG_FWU_MDATA_GPT_BLK`` and +``CONFIG_FWU_MDATA_SF`` or both of them, according to the platform +support. Anyway, a correct driver will be probed by devicetree node. + +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 -------------------------------------------------
+If your platform stores the firmware on GPT partitioned storage +device (e.g. eMMC/SD), please follow this section. + 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 @@ -74,7 +85,14 @@ 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:: +of. The GPT itself has one GUID:: + + *DiskGUID* + +This DiskGUID value should correspond to the *location_uuid* field +of the FWU metadata. + +And each GPT partition entry in the GPT header has two GUIDs::
*PartitionTypeGUID* *UniquePartitionGUID* @@ -93,9 +111,49 @@ 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. +Setting up the device for non-GPT partitioned MTD device +-------------------------------------------------------- + +If your platform stores the firmware on non-GPT partitioned MTD +device, please follow this section. + +Before enabling the functionality in U-Boot, please confirm that +your platform correctly define (or generate) `dfu_alt_info`, which +has to have all *banks* as the dfu entries. Also, the devicetree's +`fwu_mdata` node must be "u-boot,fwu-mdata-mtd" compatible node +and has FWU metadata offsets on `mdata-offsets` property. +Please refer to U-Boot +`doc <doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.yaml>`__ for +the device tree bindings. + +Similar to the GPT, you can also define the DiskGUID, and the +UniquePartitionGUID in the devicetree as additional properties of +the "fixed-partitions" compatible partition nodes, and the platform +code can generate the `dfu_alt_info` from that. In this case, +*image_type_uuid* field of the FWU mdata is used instead of the +PartitionTypeGUID. + +Generate the FWU metadata image +------------------------------- + +To generate the FWU metadata raw image, you can use `tools/mkfwumdata` +command. + + tools/mkfwumdata -i <#images> -b <#banks> \ + <location_uuid0,image_type_uuid0,image_uuid0_0,image_uuid0_1,...> \ + [location_uuid1,image_type_uuid1,image_uuid1_0,image_uuid1_1,...] \ + <output-file> + +Or, if you know GUID instead of UUID, you can use --guid option. + + tools/mkfwumdata -i <#images> -b <#banks> --guid \ + <DiskGUID0,PartitionTypeGUID0,UniquePartitionGUID0_0,UniquePartitionGUID0_1,...> \ + [DiskGUID1,PartitionTypeGUID1,UniquePartitionGUID1_0,UniquePartitionGUID1_1,...] \ + <output-file> + +When generating the metadata, the *location_uuid*, the *image_type_uuid* +and the *image_uuid* values should match the *DiskGUID*, the +*PartitionTypeGUID* and the *UniquePartitionGUID* values respectively.
Performing the Update ---------------------

From: Masami Hiramatsu masami.hiramatsu@linaro.org
This changes SPI NOR flash partition layout for TBBR and also make the U-Boot as position independent executable again because BL33 is loaded on the memory.
With enabling TBBR, TF-A BL2 loads all BL3x images from FIP image, and the U-Boot image is added to the FIP image as BL33, and loaded to memory when boot instead of XIP on SPI NOR flash. To avoid mixing up with the legacy images, this new FIP image is stored on unused area (0x600000-) and the U-Boot env vars are also stored at 0x580000 so that it will not break existing EDK2 area.
NOTE: This introduces an incombatible change to the synquacer_developerbox_defconfig. If you want to build U-Boot for booting from legacy FIP image, you need to specify previous configuration.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- .../synquacer-sc2a11-developerbox-u-boot.dtsi | 26 +++++++++++++------ configs/synquacer_developerbox_defconfig | 5 ++-- include/configs/synquacer.h | 4 +-- 3 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi index 7a56116d6f..095727e03c 100644 --- a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi +++ b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi @@ -56,7 +56,7 @@ };
partition@180000 { - label = "FIP-TFA"; + label = "LegacyFIP"; reg = <0x180000 0x78000>; };
@@ -66,18 +66,28 @@ };
partition@200000 { - label = "U-Boot"; - reg = <0x200000 0x100000>; + label = "EDK2"; + reg = <0x200000 0x200000>; };
- partition@300000 { - label = "UBoot-Env"; - reg = <0x300000 0x100000>; + partition@400000 { + label = "EDK2-Env"; + reg = <0x400000 0x100000>; };
partition@500000 { - label = "Ex-OPTEE"; - reg = <0x500000 0x200000>; + label = "Metadata"; + reg = <0x500000 0x80000>; + }; + + partition@580000 { + label = "UBoot-Env"; + reg = <0x580000 0x80000>; + }; + + partition@600000 { + label = "FIP"; + reg = <0x600000 0x400000>; }; }; }; diff --git a/configs/synquacer_developerbox_defconfig b/configs/synquacer_developerbox_defconfig index add6041e27..29b1e11401 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" diff --git a/include/configs/synquacer.h b/include/configs/synquacer.h index 5686a5b910..572f0a42ac 100644 --- a/include/configs/synquacer.h +++ b/include/configs/synquacer.h @@ -47,9 +47,7 @@ /* Since U-Boot 64bit PCIe support is limited, disable 64bit MMIO support */
#define DEFAULT_DFU_ALT_INFO "dfu_alt_info=" \ - "mtd nor1=u-boot.bin raw 200000 100000;" \ - "fip.bin raw 180000 78000;" \ - "optee.bin raw 500000 100000\0" + "mtd nor1=fip.bin raw 600000 400000\0"
/* GUIDs for capsule updatable firmware images */ #define DEVELOPERBOX_UBOOT_IMAGE_GUID \

From: Jassi Brar jaswinder.singh@linaro.org
The Synquacer board is migrating to using the FIP as the only updatable image on the platform with the u-boot and op-tee images packaged as part of the FIP image. Make changes to the structures used for capsule updates to reflect this change.
Signed-off-by: Jassi Brar jaswinder.singh@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- board/socionext/developerbox/developerbox.c | 17 +++-------------- include/configs/synquacer.h | 13 ++----------- 2 files changed, 5 insertions(+), 25 deletions(-)
diff --git a/board/socionext/developerbox/developerbox.c b/board/socionext/developerbox/developerbox.c index f5a5fe0121..b946428ddb 100644 --- a/board/socionext/developerbox/developerbox.c +++ b/board/socionext/developerbox/developerbox.c @@ -20,27 +20,16 @@
#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) struct efi_fw_image fw_images[] = { - { - .image_type_id = DEVELOPERBOX_UBOOT_IMAGE_GUID, - .fw_name = u"DEVELOPERBOX-UBOOT", - .image_index = 1, - }, { .image_type_id = DEVELOPERBOX_FIP_IMAGE_GUID, .fw_name = u"DEVELOPERBOX-FIP", - .image_index = 2, - }, - { - .image_type_id = DEVELOPERBOX_OPTEE_IMAGE_GUID, - .fw_name = u"DEVELOPERBOX-OPTEE", - .image_index = 3, + .image_index = 1, }, };
struct efi_capsule_update_info update_info = { - .dfu_string = "mtd nor1=u-boot.bin raw 200000 100000;" - "fip.bin raw 180000 78000;" - "optee.bin raw 500000 100000", + .dfu_string = "mtd nor1=bank0 raw 600000 400000;" + "bank1 raw a00000 400000;", .images = fw_images, };
diff --git a/include/configs/synquacer.h b/include/configs/synquacer.h index 572f0a42ac..eafcc69e12 100644 --- a/include/configs/synquacer.h +++ b/include/configs/synquacer.h @@ -50,18 +50,9 @@ "mtd nor1=fip.bin raw 600000 400000\0"
/* GUIDs for capsule updatable firmware images */ -#define DEVELOPERBOX_UBOOT_IMAGE_GUID \ - EFI_GUID(0x53a92e83, 0x4ef4, 0x473a, 0x8b, 0x0d, \ - 0xb5, 0xd8, 0xc7, 0xb2, 0xd6, 0x00) - #define DEVELOPERBOX_FIP_IMAGE_GUID \ - EFI_GUID(0x880866e9, 0x84ba, 0x4793, 0xa9, 0x08, \ - 0x33, 0xe0, 0xb9, 0x16, 0xf3, 0x98) - -#define DEVELOPERBOX_OPTEE_IMAGE_GUID \ - EFI_GUID(0xc1b629f1, 0xce0e, 0x4894, 0x82, 0xbf, \ - 0xf0, 0xa3, 0x83, 0x87, 0xe6, 0x30) - + EFI_GUID(0x7d6dc310, 0x52ca, 0x43b8, 0xb7, 0xb9, \ + 0xf9, 0xd6, 0xc5, 0x01, 0xd1, 0x08) /* Distro boot settings */ #ifndef CONFIG_SPL_BUILD #ifdef CONFIG_CMD_USB

From: Masami Hiramatsu masami.hiramatsu@linaro.org
The DeveloperBox platform can support the FWU Multi bank update. SCP firmware will switch the boot mode by DSW3-4 and load the Multi bank update supported TF-A BL2 from 0x600000 offset on the SPI flash. Thus it can co-exist with the legacy boot mode (legacy U-Boot or EDK2).
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org --- Changes in v3: - Change devicetree to add partitions. - Update fwu_plat_get_alt_num() to find the alt number from the bank index. - Use only 2 partitions for AB update. - Clear platform-mdata's boot_count to finish platform trial boot.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- .../synquacer-sc2a11-developerbox-u-boot.dtsi | 15 +- board/socionext/developerbox/Kconfig | 13 ++ board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/fwu_plat.c | 207 ++++++++++++++++++ include/configs/synquacer.h | 8 + 5 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c
diff --git a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi index 095727e03c..ab4e3d1c2b 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"; @@ -84,11 +84,15 @@ label = "UBoot-Env"; reg = <0x580000 0x80000>; }; - + /* FWU Multi bank update partitions */ partition@600000 { - label = "FIP"; + label = "FIP-Bank0"; reg = <0x600000 0x400000>; }; + partition@a00000 { + label = "FIP-Bank1"; + reg = <0xa00000 0x400000>; + }; }; }; }; @@ -114,6 +118,11 @@ optee { status = "okay"; }; + fwu-mdata { + compatible = "u-boot,fwu-mdata-mtd"; + fwu-mdata-store = <&spi_flash>; + mdata-offsets = <0x500000 0x530000>; + }; }; };
diff --git a/board/socionext/developerbox/Kconfig b/board/socionext/developerbox/Kconfig index c181d26a44..7df6750baf 100644 --- a/board/socionext/developerbox/Kconfig +++ b/board/socionext/developerbox/Kconfig @@ -32,4 +32,17 @@ config SYS_CONFIG_NAME default "synquacer"
endif + +config FWU_MULTI_BANK_UPDATE + select FWU_MDATA_MTD + select DM_SPI_FLASH + select DM_FWU_MDATA + select BOARD_LATE_INIT + +config FWU_NUM_BANKS + default 2 + +config FWU_NUM_IMAGES_PER_BANK + default 1 + endif diff --git a/board/socionext/developerbox/Makefile b/board/socionext/developerbox/Makefile index 4a46de995a..9b80ee38e7 100644 --- a/board/socionext/developerbox/Makefile +++ b/board/socionext/developerbox/Makefile @@ -7,3 +7,4 @@ #
obj-y := developerbox.o +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_plat.o diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c new file mode 100644 index 0000000000..fd6d0e3659 --- /dev/null +++ b/board/socionext/developerbox/fwu_plat.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 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 <spi.h> +#include <spi_flash.h> + +#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h> + +/* SPI Flash accessors */ +static struct spi_flash *plat_spi_flash; + +static int __plat_sf_get_flash(void) +{ + /* TODO: define platform spi-flash somewhere. */ + plat_spi_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, + CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, + CONFIG_SF_DEFAULT_MODE); + + return 0; +} + +static int plat_sf_get_flash(struct spi_flash **flash) +{ + int ret = 0; + + if (!plat_spi_flash) + ret = __plat_sf_get_flash(); + + *flash = plat_spi_flash; + + return ret; +} + +static int sf_load_data(u32 offs, u32 size, void **data) +{ + struct spi_flash *flash; + int ret; + + ret = plat_sf_get_flash(&flash); + if (ret < 0) + return ret; + + *data = memalign(ARCH_DMA_MINALIGN, size); + if (!*data) + return -ENOMEM; + + ret = spi_flash_read(flash, offs, size, *data); + if (ret < 0) { + free(*data); + *data = NULL; + } + + return ret; +} + +static int sf_save_data(u32 offs, u32 size, void *data) +{ + struct spi_flash *flash; + u32 sect_size, nsect; + void *buf; + int ret; + + ret = plat_sf_get_flash(&flash); + if (ret < 0) + return ret; + + sect_size = flash->mtd.erasesize; + nsect = DIV_ROUND_UP(size, sect_size); + ret = spi_flash_erase(flash, offs, nsect * sect_size); + if (ret < 0) + return ret; + + buf = memalign(ARCH_DMA_MINALIGN, size); + if (!buf) + return -ENOMEM; + memcpy(buf, data, size); + + ret = spi_flash_write(flash, offs, size, buf); + + free(buf); + + return ret; +} + +#define PLAT_METADATA_OFFSET 0x510000 +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) + +struct __packed devbox_metadata { + u32 boot_index; + u32 boot_count; +} *devbox_plat_metadata; + +int fwu_plat_get_alt_num(struct udevice __always_unused *dev, + efi_guid_t *image_id, int *alt_num) +{ + struct fwu_image_bank_info *bank; + struct fwu_mdata *mdata; + int i, ret; + + ret = fwu_get_mdata(&mdata); + if (ret < 0) + return ret; + + /* + * DeveloperBox FWU expects Bank:Image = 1:1, and the dfu_alt_info + * only has the entries for banks. Thus the alt_no should be equal + * to the bank index number. + */ + ret = -ENOENT; + for (i = 0; i < CONFIG_FWU_NUM_BANKS; i++) { + bank = &mdata->img_entry[0].img_bank_info[i]; + if (guidcmp(image_id, &bank->image_uuid) == 0) { + *alt_num = i; + ret = 0; + break; + } + } + + free(mdata); + + return ret; +} + +/* This assumes that user doesn't change system default dfu_alt_info */ +efi_status_t fill_image_type_guid_array(const efi_guid_t __always_unused + *default_guid, + efi_guid_t **part_guid_arr) +{ + int i; + + *part_guid_arr = malloc(sizeof(efi_guid_t) * DEFAULT_DFU_ALT_NUM); + if (!*part_guid_arr) + return EFI_OUT_OF_RESOURCES; + + for (i = 0; i < DEFAULT_DFU_ALT_NUM; i++) + guidcpy((*part_guid_arr + i), &devbox_fip_image_type_guid); + + return EFI_SUCCESS; +} + +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 = (active_idx + 1) % CONFIG_FWU_NUM_BANKS; + + return ret; +} + +static int devbox_load_plat_metadata(void) +{ + if (devbox_plat_metadata) + return 0; + + return sf_load_data(PLAT_METADATA_OFFSET, PLAT_METADATA_SIZE, + (void **)&devbox_plat_metadata); +} + +void fwu_plat_get_bootidx(void *boot_idx) +{ + u32 *bootidx = boot_idx; + + if (devbox_load_plat_metadata() < 0) + *bootidx = 0; + else + *bootidx = devbox_plat_metadata->boot_index; +} + +int board_late_init(void) +{ + int ret; + + ret = devbox_load_plat_metadata(); + if (ret < 0) + return ret; + + if (devbox_plat_metadata->boot_count) { + /* We are in the platform trial boot. Finish it. */ + devbox_plat_metadata->boot_count = 0; + ret = sf_save_data(PLAT_METADATA_OFFSET, PLAT_METADATA_SIZE, + (void *)devbox_plat_metadata); + if (ret < 0) + return ret; + + pr_debug("FWU: Finish platform trial boot safely.\n"); + } + + return 0; +} diff --git a/include/configs/synquacer.h b/include/configs/synquacer.h index eafcc69e12..14eeb3f57e 100644 --- a/include/configs/synquacer.h +++ b/include/configs/synquacer.h @@ -46,8 +46,16 @@
/* Since U-Boot 64bit PCIe support is limited, disable 64bit MMIO support */
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE +#define DEFAULT_DFU_ALT_NUM 2 +#define DEFAULT_DFU_ALT_INFO "dfu_alt_info=" \ + "mtd nor1=bank0 raw 600000 400000;" \ + "bank1 raw a00000 400000\0" +#else +#define DEFAULT_DFU_ALT_NUM 1 #define DEFAULT_DFU_ALT_INFO "dfu_alt_info=" \ "mtd nor1=fip.bin raw 600000 400000\0" +#endif
/* GUIDs for capsule updatable firmware images */ #define DEVELOPERBOX_FIP_IMAGE_GUID \

On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
The DeveloperBox platform can support the FWU Multi bank update. SCP firmware will switch the boot mode by DSW3-4 and load the Multi bank update supported TF-A BL2 from 0x600000 offset on the SPI flash. Thus it can co-exist with the legacy boot mode (legacy U-Boot or EDK2).
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org
Changes in v3:
- Change devicetree to add partitions.
- Update fwu_plat_get_alt_num() to find the alt number from the bank index.
- Use only 2 partitions for AB update.
- Clear platform-mdata's boot_count to finish platform trial boot.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
.../synquacer-sc2a11-developerbox-u-boot.dtsi | 15 +- board/socionext/developerbox/Kconfig | 13 ++ board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/fwu_plat.c | 207 ++++++++++++++++++ include/configs/synquacer.h | 8 + 5 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c
diff --git a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi index 095727e03c..ab4e3d1c2b 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";
@@ -84,11 +84,15 @@ label = "UBoot-Env"; reg = <0x580000 0x80000>; };
/* FWU Multi bank update partitions */ partition@600000 {
label = "FIP";
label = "FIP-Bank0"; reg = <0x600000 0x400000>; };
partition@a00000 {
label = "FIP-Bank1";
reg = <0xa00000 0x400000>;
}; };}; };
@@ -114,6 +118,11 @@ optee { status = "okay"; };
fwu-mdata {
compatible = "u-boot,fwu-mdata-mtd";
fwu-mdata-store = <&spi_flash>;
mdata-offsets = <0x500000 0x530000>;
}; };};
diff --git a/board/socionext/developerbox/Kconfig b/board/socionext/developerbox/Kconfig index c181d26a44..7df6750baf 100644 --- a/board/socionext/developerbox/Kconfig +++ b/board/socionext/developerbox/Kconfig @@ -32,4 +32,17 @@ config SYS_CONFIG_NAME default "synquacer"
endif
+config FWU_MULTI_BANK_UPDATE
- select FWU_MDATA_MTD
- select DM_SPI_FLASH
- select DM_FWU_MDATA
- select BOARD_LATE_INIT
+config FWU_NUM_BANKS
- default 2
+config FWU_NUM_IMAGES_PER_BANK
- default 1
- endif
diff --git a/board/socionext/developerbox/Makefile b/board/socionext/developerbox/Makefile index 4a46de995a..9b80ee38e7 100644 --- a/board/socionext/developerbox/Makefile +++ b/board/socionext/developerbox/Makefile @@ -7,3 +7,4 @@ #
obj-y := developerbox.o +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_plat.o diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c new file mode 100644 index 0000000000..fd6d0e3659 --- /dev/null +++ b/board/socionext/developerbox/fwu_plat.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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 <spi.h> +#include <spi_flash.h>
+#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h>
+/* SPI Flash accessors */ +static struct spi_flash *plat_spi_flash;
+static int __plat_sf_get_flash(void) +{
- /* TODO: define platform spi-flash somewhere. */
- plat_spi_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
CONFIG_SF_DEFAULT_CS,
CONFIG_SF_DEFAULT_SPEED,
CONFIG_SF_DEFAULT_MODE);
- return 0;
+}
+static int plat_sf_get_flash(struct spi_flash **flash) +{
- int ret = 0;
- if (!plat_spi_flash)
ret = __plat_sf_get_flash();
- *flash = plat_spi_flash;
- return ret;
+}
+static int sf_load_data(u32 offs, u32 size, void **data) +{
- struct spi_flash *flash;
- int ret;
- ret = plat_sf_get_flash(&flash);
- if (ret < 0)
return ret;
- *data = memalign(ARCH_DMA_MINALIGN, size);
- if (!*data)
return -ENOMEM;
- ret = spi_flash_read(flash, offs, size, *data);
- if (ret < 0) {
free(*data);
*data = NULL;
- }
- return ret;
+}
+static int sf_save_data(u32 offs, u32 size, void *data) +{
- struct spi_flash *flash;
- u32 sect_size, nsect;
- void *buf;
- int ret;
- ret = plat_sf_get_flash(&flash);
- if (ret < 0)
return ret;
- sect_size = flash->mtd.erasesize;
- nsect = DIV_ROUND_UP(size, sect_size);
- ret = spi_flash_erase(flash, offs, nsect * sect_size);
- if (ret < 0)
return ret;
- buf = memalign(ARCH_DMA_MINALIGN, size);
- if (!buf)
return -ENOMEM;
- memcpy(buf, data, size);
- ret = spi_flash_write(flash, offs, size, buf);
- free(buf);
- return ret;
+}
+#define PLAT_METADATA_OFFSET 0x510000
Meta data offsets based on DT is somewhere else. I would expect that this is read from DT instead of hardcoding it here.
124 fwu-mdata { 125 compatible = "u-boot,fwu-mdata-mtd"; 126 fwu-mdata-store = <&spi_flash>; 127 mdata-offsets = <0x500000 0x530000>; 128 };
Thanks, Michal

On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
The DeveloperBox platform can support the FWU Multi bank update. SCP firmware will switch the boot mode by DSW3-4 and load the Multi bank update supported TF-A BL2 from 0x600000 offset on the SPI flash. Thus it can co-exist with the legacy boot mode (legacy U-Boot or EDK2).
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org
I am looking at this code again while trying on xilinx HW.
Changes in v3:
- Change devicetree to add partitions.
- Update fwu_plat_get_alt_num() to find the alt number from the bank index.
- Use only 2 partitions for AB update.
- Clear platform-mdata's boot_count to finish platform trial boot.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
.../synquacer-sc2a11-developerbox-u-boot.dtsi | 15 +- board/socionext/developerbox/Kconfig | 13 ++ board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/fwu_plat.c | 207 ++++++++++++++++++ include/configs/synquacer.h | 8 + 5 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c
diff --git a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi index 095727e03c..ab4e3d1c2b 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";
@@ -84,11 +84,15 @@ label = "UBoot-Env"; reg = <0x580000 0x80000>; };
/* FWU Multi bank update partitions */ partition@600000 {
label = "FIP";
label = "FIP-Bank0"; reg = <0x600000 0x400000>; };
partition@a00000 {
label = "FIP-Bank1";
reg = <0xa00000 0x400000>;
}; };}; };
@@ -114,6 +118,11 @@ optee { status = "okay"; };
fwu-mdata {
compatible = "u-boot,fwu-mdata-mtd";
fwu-mdata-store = <&spi_flash>;
mdata-offsets = <0x500000 0x530000>;
}; };};
diff --git a/board/socionext/developerbox/Kconfig b/board/socionext/developerbox/Kconfig index c181d26a44..7df6750baf 100644 --- a/board/socionext/developerbox/Kconfig +++ b/board/socionext/developerbox/Kconfig @@ -32,4 +32,17 @@ config SYS_CONFIG_NAME default "synquacer"
endif
+config FWU_MULTI_BANK_UPDATE
- select FWU_MDATA_MTD
- select DM_SPI_FLASH
- select DM_FWU_MDATA
- select BOARD_LATE_INIT
+config FWU_NUM_BANKS
- default 2
+config FWU_NUM_IMAGES_PER_BANK
- default 1
- endif
diff --git a/board/socionext/developerbox/Makefile b/board/socionext/developerbox/Makefile index 4a46de995a..9b80ee38e7 100644 --- a/board/socionext/developerbox/Makefile +++ b/board/socionext/developerbox/Makefile @@ -7,3 +7,4 @@ #
obj-y := developerbox.o +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_plat.o diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c new file mode 100644 index 0000000000..fd6d0e3659 --- /dev/null +++ b/board/socionext/developerbox/fwu_plat.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- 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 <spi.h> +#include <spi_flash.h>
+#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h>
+/* SPI Flash accessors */ +static struct spi_flash *plat_spi_flash;
+static int __plat_sf_get_flash(void) +{
- /* TODO: define platform spi-flash somewhere. */
- plat_spi_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
CONFIG_SF_DEFAULT_CS,
CONFIG_SF_DEFAULT_SPEED,
CONFIG_SF_DEFAULT_MODE);
- return 0;
What about if spi_flash_probe() fails?
You are returning 0 here all the time and below you are propagating it that everything is fine.
+}
+static int plat_sf_get_flash(struct spi_flash **flash) +{
- int ret = 0;
- if (!plat_spi_flash)
ret = __plat_sf_get_flash();
- *flash = plat_spi_flash;
- return ret;
+}
+static int sf_load_data(u32 offs, u32 size, void **data) +{
- struct spi_flash *flash;
- int ret;
- ret = plat_sf_get_flash(&flash);
- if (ret < 0)
return ret;
- *data = memalign(ARCH_DMA_MINALIGN, size);
- if (!*data)
return -ENOMEM;
- ret = spi_flash_read(flash, offs, size, *data);
- if (ret < 0) {
free(*data);
*data = NULL;
- }
- return ret;
+}
+static int sf_save_data(u32 offs, u32 size, void *data) +{
- struct spi_flash *flash;
- u32 sect_size, nsect;
- void *buf;
- int ret;
- ret = plat_sf_get_flash(&flash);
- if (ret < 0)
return ret;
- sect_size = flash->mtd.erasesize;
- nsect = DIV_ROUND_UP(size, sect_size);
- ret = spi_flash_erase(flash, offs, nsect * sect_size);
What it is interesting here that framework itself is using mtd infrastructure but this platform driver is calling spi functions directly. It looks a little bit nonstandard way. What's the reason for it?
- if (ret < 0)
return ret;
- buf = memalign(ARCH_DMA_MINALIGN, size);
- if (!buf)
return -ENOMEM;
- memcpy(buf, data, size);
- ret = spi_flash_write(flash, offs, size, buf);
- free(buf);
- return ret;
+}
+#define PLAT_METADATA_OFFSET 0x510000 +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata))
+struct __packed devbox_metadata {
- u32 boot_index;
- u32 boot_count;
There is the whole bootcount infrastructure for this. I think it would be much better to use that framework instead of creating parallel one.
Thanks, Michal

On Mon, 20 Jun 2022 at 03:23, Michal Simek monstr@monstr.eu wrote:
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
....
+}
+static int plat_sf_get_flash(struct spi_flash **flash) +{
int ret = 0;
if (!plat_spi_flash)
ret = __plat_sf_get_flash();
*flash = plat_spi_flash;
return ret;
+}
+static int sf_load_data(u32 offs, u32 size, void **data) +{
struct spi_flash *flash;
int ret;
ret = plat_sf_get_flash(&flash);
if (ret < 0)
return ret;
*data = memalign(ARCH_DMA_MINALIGN, size);
if (!*data)
return -ENOMEM;
ret = spi_flash_read(flash, offs, size, *data);
if (ret < 0) {
free(*data);
*data = NULL;
}
return ret;
+}
+static int sf_save_data(u32 offs, u32 size, void *data) +{
struct spi_flash *flash;
u32 sect_size, nsect;
void *buf;
int ret;
ret = plat_sf_get_flash(&flash);
if (ret < 0)
return ret;
sect_size = flash->mtd.erasesize;
nsect = DIV_ROUND_UP(size, sect_size);
ret = spi_flash_erase(flash, offs, nsect * sect_size);
What it is interesting here that framework itself is using mtd infrastructure but this platform driver is calling spi functions directly. It looks a little bit nonstandard way. What's the reason for it?
Yup, this whole sf shebang is unnecessary, and removed for next revision.
+#define PLAT_METADATA_OFFSET 0x510000 +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata))
+struct __packed devbox_metadata {
u32 boot_index;
u32 boot_count;
There is the whole bootcount infrastructure for this. I think it would be much better to use that framework instead of creating parallel one.
Yes, this goes too.
Thanks.

Hi all,
On Mon, 18 Jul 2022 at 17:43, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 20 Jun 2022 at 03:23, Michal Simek monstr@monstr.eu wrote:
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
....
+}
+static int plat_sf_get_flash(struct spi_flash **flash) +{
int ret = 0;
if (!plat_spi_flash)
ret = __plat_sf_get_flash();
*flash = plat_spi_flash;
return ret;
+}
+static int sf_load_data(u32 offs, u32 size, void **data) +{
struct spi_flash *flash;
int ret;
ret = plat_sf_get_flash(&flash);
if (ret < 0)
return ret;
*data = memalign(ARCH_DMA_MINALIGN, size);
if (!*data)
return -ENOMEM;
ret = spi_flash_read(flash, offs, size, *data);
if (ret < 0) {
free(*data);
*data = NULL;
}
return ret;
+}
+static int sf_save_data(u32 offs, u32 size, void *data) +{
struct spi_flash *flash;
u32 sect_size, nsect;
void *buf;
int ret;
ret = plat_sf_get_flash(&flash);
if (ret < 0)
return ret;
sect_size = flash->mtd.erasesize;
nsect = DIV_ROUND_UP(size, sect_size);
ret = spi_flash_erase(flash, offs, nsect * sect_size);
What it is interesting here that framework itself is using mtd infrastructure but this platform driver is calling spi functions directly. It looks a little bit nonstandard way. What's the reason for it?
Yup, this whole sf shebang is unnecessary, and removed for next revision.
+#define PLAT_METADATA_OFFSET 0x510000 +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata))
+struct __packed devbox_metadata {
u32 boot_index;
u32 boot_count;
There is the whole bootcount infrastructure for this. I think it would be much better to use that framework instead of creating parallel one.
Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Thanks /Ilias
Thanks.

On Mon, 18 Jul 2022 at 09:47, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi all,
On Mon, 18 Jul 2022 at 17:43, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 20 Jun 2022 at 03:23, Michal Simek monstr@monstr.eu wrote:
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
....
+}
+static int plat_sf_get_flash(struct spi_flash **flash) +{
int ret = 0;
if (!plat_spi_flash)
ret = __plat_sf_get_flash();
*flash = plat_spi_flash;
return ret;
+}
+static int sf_load_data(u32 offs, u32 size, void **data) +{
struct spi_flash *flash;
int ret;
ret = plat_sf_get_flash(&flash);
if (ret < 0)
return ret;
*data = memalign(ARCH_DMA_MINALIGN, size);
if (!*data)
return -ENOMEM;
ret = spi_flash_read(flash, offs, size, *data);
if (ret < 0) {
free(*data);
*data = NULL;
}
return ret;
+}
+static int sf_save_data(u32 offs, u32 size, void *data) +{
struct spi_flash *flash;
u32 sect_size, nsect;
void *buf;
int ret;
ret = plat_sf_get_flash(&flash);
if (ret < 0)
return ret;
sect_size = flash->mtd.erasesize;
nsect = DIV_ROUND_UP(size, sect_size);
ret = spi_flash_erase(flash, offs, nsect * sect_size);
What it is interesting here that framework itself is using mtd infrastructure but this platform driver is calling spi functions directly. It looks a little bit nonstandard way. What's the reason for it?
Yup, this whole sf shebang is unnecessary, and removed for next revision.
+#define PLAT_METADATA_OFFSET 0x510000 +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata))
+struct __packed devbox_metadata {
u32 boot_index;
u32 boot_count;
There is the whole bootcount infrastructure for this. I think it would be much better to use that framework instead of creating parallel one.
Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
thnx

Hi Jassi
On Mon, 18 Jul 2022 at 18:08, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 09:47, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi all,
On Mon, 18 Jul 2022 at 17:43, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 20 Jun 2022 at 03:23, Michal Simek monstr@monstr.eu wrote:
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
....
+}
+static int plat_sf_get_flash(struct spi_flash **flash) +{
int ret = 0;
if (!plat_spi_flash)
ret = __plat_sf_get_flash();
*flash = plat_spi_flash;
return ret;
+}
+static int sf_load_data(u32 offs, u32 size, void **data) +{
struct spi_flash *flash;
int ret;
ret = plat_sf_get_flash(&flash);
if (ret < 0)
return ret;
*data = memalign(ARCH_DMA_MINALIGN, size);
if (!*data)
return -ENOMEM;
ret = spi_flash_read(flash, offs, size, *data);
if (ret < 0) {
free(*data);
*data = NULL;
}
return ret;
+}
+static int sf_save_data(u32 offs, u32 size, void *data) +{
struct spi_flash *flash;
u32 sect_size, nsect;
void *buf;
int ret;
ret = plat_sf_get_flash(&flash);
if (ret < 0)
return ret;
sect_size = flash->mtd.erasesize;
nsect = DIV_ROUND_UP(size, sect_size);
ret = spi_flash_erase(flash, offs, nsect * sect_size);
What it is interesting here that framework itself is using mtd infrastructure but this platform driver is calling spi functions directly. It looks a little bit nonstandard way. What's the reason for it?
Yup, this whole sf shebang is unnecessary, and removed for next revision.
+#define PLAT_METADATA_OFFSET 0x510000 +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata))
+struct __packed devbox_metadata {
u32 boot_index;
u32 boot_count;
There is the whole bootcount infrastructure for this. I think it would be much better to use that framework instead of creating parallel one.
Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Regards /Ilias
thnx

Hi Ilias,
On Mon, 18 Jul 2022 at 10:16, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Jassi
On Mon, 18 Jul 2022 at 18:08, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 09:47, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi all,
On Mon, 18 Jul 2022 at 17:43, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 20 Jun 2022 at 03:23, Michal Simek monstr@monstr.eu wrote:
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
....
+}
+static int plat_sf_get_flash(struct spi_flash **flash) +{
int ret = 0;
if (!plat_spi_flash)
ret = __plat_sf_get_flash();
*flash = plat_spi_flash;
return ret;
+}
+static int sf_load_data(u32 offs, u32 size, void **data) +{
struct spi_flash *flash;
int ret;
ret = plat_sf_get_flash(&flash);
if (ret < 0)
return ret;
*data = memalign(ARCH_DMA_MINALIGN, size);
if (!*data)
return -ENOMEM;
ret = spi_flash_read(flash, offs, size, *data);
if (ret < 0) {
free(*data);
*data = NULL;
}
return ret;
+}
+static int sf_save_data(u32 offs, u32 size, void *data) +{
struct spi_flash *flash;
u32 sect_size, nsect;
void *buf;
int ret;
ret = plat_sf_get_flash(&flash);
if (ret < 0)
return ret;
sect_size = flash->mtd.erasesize;
nsect = DIV_ROUND_UP(size, sect_size);
ret = spi_flash_erase(flash, offs, nsect * sect_size);
What it is interesting here that framework itself is using mtd infrastructure but this platform driver is calling spi functions directly. It looks a little bit nonstandard way. What's the reason for it?
Yup, this whole sf shebang is unnecessary, and removed for next revision.
+#define PLAT_METADATA_OFFSET 0x510000 +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata))
+struct __packed devbox_metadata {
u32 boot_index;
u32 boot_count;
There is the whole bootcount infrastructure for this. I think it would be much better to use that framework instead of creating parallel one.
Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
Of the three options - registers, efi-env and mdata, I think the last one is more robust. For ex, if BL33 isn't reached after an update. We want BL2 (which may not have access to efi variables) to be able to revert the active index.
thanks.

Hi Jassi,
On Mon, 18 Jul 2022 at 18:32, Jassi Brar jaswinder.singh@linaro.org wrote:
Hi Ilias,
On Mon, 18 Jul 2022 at 10:16, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Jassi
On Mon, 18 Jul 2022 at 18:08, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 09:47, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi all,
On Mon, 18 Jul 2022 at 17:43, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 20 Jun 2022 at 03:23, Michal Simek monstr@monstr.eu wrote:
On 6/9/22 14:30, Sughosh Ganu wrote: > From: Masami Hiramatsu masami.hiramatsu@linaro.org >
....
> +} > + > +static int plat_sf_get_flash(struct spi_flash **flash) > +{ > + int ret = 0; > + > + if (!plat_spi_flash) > + ret = __plat_sf_get_flash(); > + > + *flash = plat_spi_flash; > + > + return ret; > +} > + > +static int sf_load_data(u32 offs, u32 size, void **data) > +{ > + struct spi_flash *flash; > + int ret; > + > + ret = plat_sf_get_flash(&flash); > + if (ret < 0) > + return ret; > + > + *data = memalign(ARCH_DMA_MINALIGN, size); > + if (!*data) > + return -ENOMEM; > + > + ret = spi_flash_read(flash, offs, size, *data); > + if (ret < 0) { > + free(*data); > + *data = NULL; > + } > + > + return ret; > +} > + > +static int sf_save_data(u32 offs, u32 size, void *data) > +{ > + struct spi_flash *flash; > + u32 sect_size, nsect; > + void *buf; > + int ret; > + > + ret = plat_sf_get_flash(&flash); > + if (ret < 0) > + return ret; > + > + sect_size = flash->mtd.erasesize; > + nsect = DIV_ROUND_UP(size, sect_size); > + ret = spi_flash_erase(flash, offs, nsect * sect_size);
What it is interesting here that framework itself is using mtd infrastructure but this platform driver is calling spi functions directly. It looks a little bit nonstandard way. What's the reason for it?
Yup, this whole sf shebang is unnecessary, and removed for next revision.
> + > +#define PLAT_METADATA_OFFSET 0x510000 > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > + > +struct __packed devbox_metadata { > + u32 boot_index; > + u32 boot_count;
There is the whole bootcount infrastructure for this. I think it would be much better to use that framework instead of creating parallel one.
Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
Of the three options - registers, efi-env and mdata, I think the last one is more robust. For ex, if BL33 isn't reached after an update. We want BL2 (which may not have access to efi variables) to be able to revert the active index.
I think BL2 has it's own set of internal counters for the number of reboots already (and I think on the stmp32mp1 is based on a cpu scratch register) This is supposed with BL33 reboots only. Sughosh do I remember this wrong?
Regards /Ilias
thanks.

On Mon, 18 Jul 2022 at 10:31, Jassi Brar jaswinder.singh@linaro.org wrote:
Hi Ilias,
On Mon, 18 Jul 2022 at 10:16, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Jassi
On Mon, 18 Jul 2022 at 18:08, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 09:47, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi all,
On Mon, 18 Jul 2022 at 17:43, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 20 Jun 2022 at 03:23, Michal Simek monstr@monstr.eu wrote:
On 6/9/22 14:30, Sughosh Ganu wrote: > From: Masami Hiramatsu masami.hiramatsu@linaro.org >
....
> +} > + > +static int plat_sf_get_flash(struct spi_flash **flash) > +{ > + int ret = 0; > + > + if (!plat_spi_flash) > + ret = __plat_sf_get_flash(); > + > + *flash = plat_spi_flash; > + > + return ret; > +} > + > +static int sf_load_data(u32 offs, u32 size, void **data) > +{ > + struct spi_flash *flash; > + int ret; > + > + ret = plat_sf_get_flash(&flash); > + if (ret < 0) > + return ret; > + > + *data = memalign(ARCH_DMA_MINALIGN, size); > + if (!*data) > + return -ENOMEM; > + > + ret = spi_flash_read(flash, offs, size, *data); > + if (ret < 0) { > + free(*data); > + *data = NULL; > + } > + > + return ret; > +} > + > +static int sf_save_data(u32 offs, u32 size, void *data) > +{ > + struct spi_flash *flash; > + u32 sect_size, nsect; > + void *buf; > + int ret; > + > + ret = plat_sf_get_flash(&flash); > + if (ret < 0) > + return ret; > + > + sect_size = flash->mtd.erasesize; > + nsect = DIV_ROUND_UP(size, sect_size); > + ret = spi_flash_erase(flash, offs, nsect * sect_size);
What it is interesting here that framework itself is using mtd infrastructure but this platform driver is calling spi functions directly. It looks a little bit nonstandard way. What's the reason for it?
Yup, this whole sf shebang is unnecessary, and removed for next revision.
> + > +#define PLAT_METADATA_OFFSET 0x510000 > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > + > +struct __packed devbox_metadata { > + u32 boot_index; > + u32 boot_count;
There is the whole bootcount infrastructure for this. I think it would be much better to use that framework instead of creating parallel one.
Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
Of the three options - registers, efi-env and mdata, I think the last one is more robust. For ex, if BL33 isn't reached after an update. We want BL2 (which may not have access to efi variables) to be able to revert the active index.
and which requires a bootcount for each stage. hmm... probably I am overlooking something.

Hi Jassi
On Mon, 18 Jul 2022 at 18:34, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 10:31, Jassi Brar jaswinder.singh@linaro.org wrote:
Hi Ilias,
On Mon, 18 Jul 2022 at 10:16, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Jassi
On Mon, 18 Jul 2022 at 18:08, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 09:47, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi all,
On Mon, 18 Jul 2022 at 17:43, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 20 Jun 2022 at 03:23, Michal Simek monstr@monstr.eu wrote: > On 6/9/22 14:30, Sughosh Ganu wrote: > > From: Masami Hiramatsu masami.hiramatsu@linaro.org > > ....
> > +} > > + > > +static int plat_sf_get_flash(struct spi_flash **flash) > > +{ > > + int ret = 0; > > + > > + if (!plat_spi_flash) > > + ret = __plat_sf_get_flash(); > > + > > + *flash = plat_spi_flash; > > + > > + return ret; > > +} > > + > > +static int sf_load_data(u32 offs, u32 size, void **data) > > +{ > > + struct spi_flash *flash; > > + int ret; > > + > > + ret = plat_sf_get_flash(&flash); > > + if (ret < 0) > > + return ret; > > + > > + *data = memalign(ARCH_DMA_MINALIGN, size); > > + if (!*data) > > + return -ENOMEM; > > + > > + ret = spi_flash_read(flash, offs, size, *data); > > + if (ret < 0) { > > + free(*data); > > + *data = NULL; > > + } > > + > > + return ret; > > +} > > + > > +static int sf_save_data(u32 offs, u32 size, void *data) > > +{ > > + struct spi_flash *flash; > > + u32 sect_size, nsect; > > + void *buf; > > + int ret; > > + > > + ret = plat_sf_get_flash(&flash); > > + if (ret < 0) > > + return ret; > > + > > + sect_size = flash->mtd.erasesize; > > + nsect = DIV_ROUND_UP(size, sect_size); > > + ret = spi_flash_erase(flash, offs, nsect * sect_size); > > What it is interesting here that framework itself is using mtd infrastructure > but this platform driver is calling spi functions directly. > It looks a little bit nonstandard way. What's the reason for it? > Yup, this whole sf shebang is unnecessary, and removed for next revision.
> > + > > +#define PLAT_METADATA_OFFSET 0x510000 > > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > > + > > +struct __packed devbox_metadata { > > + u32 boot_index; > > + u32 boot_count; > > There is the whole bootcount infrastructure for this. I think it would be much > better to use that framework instead of creating parallel one. > Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
Of the three options - registers, efi-env and mdata, I think the last one is more robust. For ex, if BL33 isn't reached after an update. We want BL2 (which may not have access to efi variables) to be able to revert the active index.
and which requires a bootcount for each stage. hmm... probably I am overlooking something.
Well it's indeed more complicated, but the reasoning was something along the lines of - What if BL2 crashes really early, before it can access storage? - BL2 doesn't have code to write that data only read it (in some cases, depends on how the data is stored)
So the solution was to have individual counters
Cheers /Ilias

On Mon, Jul 18, 2022 at 10:31:56AM -0500, Jassi Brar wrote:
Hi Ilias,
On Mon, 18 Jul 2022 at 10:16, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Jassi
On Mon, 18 Jul 2022 at 18:08, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 09:47, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi all,
On Mon, 18 Jul 2022 at 17:43, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 20 Jun 2022 at 03:23, Michal Simek monstr@monstr.eu wrote:
On 6/9/22 14:30, Sughosh Ganu wrote: > From: Masami Hiramatsu masami.hiramatsu@linaro.org >
....
> +} > + > +static int plat_sf_get_flash(struct spi_flash **flash) > +{ > + int ret = 0; > + > + if (!plat_spi_flash) > + ret = __plat_sf_get_flash(); > + > + *flash = plat_spi_flash; > + > + return ret; > +} > + > +static int sf_load_data(u32 offs, u32 size, void **data) > +{ > + struct spi_flash *flash; > + int ret; > + > + ret = plat_sf_get_flash(&flash); > + if (ret < 0) > + return ret; > + > + *data = memalign(ARCH_DMA_MINALIGN, size); > + if (!*data) > + return -ENOMEM; > + > + ret = spi_flash_read(flash, offs, size, *data); > + if (ret < 0) { > + free(*data); > + *data = NULL; > + } > + > + return ret; > +} > + > +static int sf_save_data(u32 offs, u32 size, void *data) > +{ > + struct spi_flash *flash; > + u32 sect_size, nsect; > + void *buf; > + int ret; > + > + ret = plat_sf_get_flash(&flash); > + if (ret < 0) > + return ret; > + > + sect_size = flash->mtd.erasesize; > + nsect = DIV_ROUND_UP(size, sect_size); > + ret = spi_flash_erase(flash, offs, nsect * sect_size);
What it is interesting here that framework itself is using mtd infrastructure but this platform driver is calling spi functions directly. It looks a little bit nonstandard way. What's the reason for it?
Yup, this whole sf shebang is unnecessary, and removed for next revision.
> + > +#define PLAT_METADATA_OFFSET 0x510000 > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > + > +struct __packed devbox_metadata { > + u32 boot_index; > + u32 boot_count;
There is the whole bootcount infrastructure for this. I think it would be much better to use that framework instead of creating parallel one.
Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
No, there just has to be "somewhere" to do the counting. We've got a DDR backed driver, for example. So yes, I think we should try and use the bootcount framework here.

On Mon, Jul 18, 2022 at 4:00 PM Tom Rini trini@konsulko.com wrote:
On Mon, Jul 18, 2022 at 10:31:56AM -0500, Jassi Brar wrote:
> > + > > +#define PLAT_METADATA_OFFSET 0x510000 > > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > > + > > +struct __packed devbox_metadata { > > + u32 boot_index; > > + u32 boot_count; > > There is the whole bootcount infrastructure for this. I think it would be much > better to use that framework instead of creating parallel one. > Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
No, there just has to be "somewhere" to do the counting. We've got a DDR backed driver, for example. So yes, I think we should try and use the bootcount framework here.
OK, for platforms that can preserve ram across reboot, using non-persistent storage can work. My platform neither preserves ram, nor has any warmreset-proof registers. So I have to choose between saving the bootcount in efi-env or in vendor specific structure next to the metadata. I prefer metadata because it is common to all stages of boot. Any corrections to this approach?
Thanks

On Tue, Jul 19, 2022 at 10:23:08AM -0500, Jassi Brar wrote:
On Mon, Jul 18, 2022 at 4:00 PM Tom Rini trini@konsulko.com wrote:
On Mon, Jul 18, 2022 at 10:31:56AM -0500, Jassi Brar wrote:
> > > + > > > +#define PLAT_METADATA_OFFSET 0x510000 > > > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > > > + > > > +struct __packed devbox_metadata { > > > + u32 boot_index; > > > + u32 boot_count; > > > > There is the whole bootcount infrastructure for this. I think it would be much > > better to use that framework instead of creating parallel one. > > > Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
No, there just has to be "somewhere" to do the counting. We've got a DDR backed driver, for example. So yes, I think we should try and use the bootcount framework here.
OK, for platforms that can preserve ram across reboot, using non-persistent storage can work. My platform neither preserves ram, nor has any warmreset-proof registers. So I have to choose between saving the bootcount in efi-env or in vendor specific structure next to the metadata. I prefer metadata because it is common to all stages of boot. Any corrections to this approach?
What I'm trying to say is that we have an abstraction for counting the number of times the system has booted since something reset the counter to zero, to signal the system is up and functional. I'll leave the details of how it's used here, and how / what backend is used or created for it up to everyone else on the thread.

On Mon, 18 Jul 2022 at 16:00, Tom Rini trini@konsulko.com wrote:
On Mon, Jul 18, 2022 at 10:31:56AM -0500, Jassi Brar wrote:
> > + > > +#define PLAT_METADATA_OFFSET 0x510000 > > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > > + > > +struct __packed devbox_metadata { > > + u32 boot_index; > > + u32 boot_count; > > There is the whole bootcount infrastructure for this. I think it would be much > better to use that framework instead of creating parallel one. > Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
No, there just has to be "somewhere" to do the counting. We've got a DDR backed driver, for example. So yes, I think we should try and use the bootcount framework here.
OK, for platforms that can preserve ram across reboot, using non-persistent storage can work. My platform neither preserves ram, nor has any warmreset-proof registers. So I have to choose between saving the bootcount in efi-env or in vendor specific structure next to the metadata. I prefer metadata because it is common to all stages of boot. Any corrections to this approach?
Thanks

Hi Jassi,
On Tue, 19 Jul 2022 at 18:27, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 16:00, Tom Rini trini@konsulko.com wrote:
On Mon, Jul 18, 2022 at 10:31:56AM -0500, Jassi Brar wrote:
> > > > + > > > +#define PLAT_METADATA_OFFSET 0x510000 > > > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > > > + > > > +struct __packed devbox_metadata { > > > + u32 boot_index; > > > + u32 boot_count; > > > > There is the whole bootcount infrastructure for this. I think it would be much > > better to use that framework instead of creating parallel one. > > > Yes, this goes too.
Is bootcount really suited for this case? AFAIK bootcount either requires device specific registers (which won't reset on reboots), or an environment you can write data to. But what if a user wants to disable writing the env variables and the device doesn't have a set of registers we can use?
Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
No, there just has to be "somewhere" to do the counting. We've got a DDR backed driver, for example. So yes, I think we should try and use the bootcount framework here.
OK, for platforms that can preserve ram across reboot, using non-persistent storage can work. My platform neither preserves ram, nor has any warmreset-proof registers. So I have to choose between saving the bootcount in efi-env or in vendor specific structure next to the metadata. I prefer metadata because it is common to all stages of boot. Any corrections to this approach?
The metadata is defined by a spec and they don't have a field for bootcounting. Once Sughosh resends his patches he'll include a bootcount backend that reuses EFI variables. Can't we just use that?
Thanks

On Wed, Jul 20, 2022 at 2:54 AM Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Jassi,
On Tue, 19 Jul 2022 at 18:27, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 16:00, Tom Rini trini@konsulko.com wrote:
On Mon, Jul 18, 2022 at 10:31:56AM -0500, Jassi Brar wrote:
> > > > > > + > > > > +#define PLAT_METADATA_OFFSET 0x510000 > > > > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > > > > + > > > > +struct __packed devbox_metadata { > > > > + u32 boot_index; > > > > + u32 boot_count; > > > > > > There is the whole bootcount infrastructure for this. I think it would be much > > > better to use that framework instead of creating parallel one. > > > > > Yes, this goes too. > > Is bootcount really suited for this case? > AFAIK bootcount either requires device specific registers (which won't > reset on reboots), or an environment you can write data to. > But what if a user wants to disable writing the env variables and the > device doesn't have a set of registers we can use? > Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
No, there just has to be "somewhere" to do the counting. We've got a DDR backed driver, for example. So yes, I think we should try and use the bootcount framework here.
OK, for platforms that can preserve ram across reboot, using non-persistent storage can work. My platform neither preserves ram, nor has any warmreset-proof registers. So I have to choose between saving the bootcount in efi-env or in vendor specific structure next to the metadata. I prefer metadata because it is common to all stages of boot. Any corrections to this approach?
The metadata is defined by a spec and they don't have a field for bootcounting. Once Sughosh resends his patches he'll include a bootcount backend that reuses EFI variables. Can't we just use that?
Yes, I am aware metadata spec has no provision of vendor data. But there is nothing illegal in appending vendor-data to metadata and that is trivial to implement ... basically use sizeof(struct fwu_mdata) + sizeof(struct sni_vendor_mdata) while read/write meta-data. That will also be zero extra-overhead.
fwu-mdata { compatible = "u-boot,fwu-mdata-mtd"; fwu-mdata-store = <&spi_flash>; mdata-offsets = <0x500000 0x530000>; vendor-data-size = <0x100>; // optional };
Sure we can use an efi variable, but I see more uses of vendor-data :- shared among BL1/BL2/BL3x/OS so we can emulate reset-syndrome, crash-logging, per-image bootcount etc when the h/w doesn't support these features.
Ofcourse, please feel free to implement efi-variables still.
thanks.

Hi Jassi
On Wed, 20 Jul 2022 at 17:30, Jassi Brar jassisinghbrar@gmail.com wrote:
On Wed, Jul 20, 2022 at 2:54 AM Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Jassi,
On Tue, 19 Jul 2022 at 18:27, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 16:00, Tom Rini trini@konsulko.com wrote:
On Mon, Jul 18, 2022 at 10:31:56AM -0500, Jassi Brar wrote:
> > > > > > > > + > > > > > +#define PLAT_METADATA_OFFSET 0x510000 > > > > > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > > > > > + > > > > > +struct __packed devbox_metadata { > > > > > + u32 boot_index; > > > > > + u32 boot_count; > > > > > > > > There is the whole bootcount infrastructure for this. I think it would be much > > > > better to use that framework instead of creating parallel one. > > > > > > > Yes, this goes too. > > > > Is bootcount really suited for this case? > > AFAIK bootcount either requires device specific registers (which won't > > reset on reboots), or an environment you can write data to. > > But what if a user wants to disable writing the env variables and the > > device doesn't have a set of registers we can use? > > > Maybe it should be moved in 'struct fwu_mdata' ?
I was mostly thinking on moving this count as another 'bootcount' method. So in case the user has disabled writing evn variables but he is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
No, there just has to be "somewhere" to do the counting. We've got a DDR backed driver, for example. So yes, I think we should try and use the bootcount framework here.
OK, for platforms that can preserve ram across reboot, using non-persistent storage can work. My platform neither preserves ram, nor has any warmreset-proof registers. So I have to choose between saving the bootcount in efi-env or in vendor specific structure next to the metadata. I prefer metadata because it is common to all stages of boot. Any corrections to this approach?
The metadata is defined by a spec and they don't have a field for bootcounting. Once Sughosh resends his patches he'll include a bootcount backend that reuses EFI variables. Can't we just use that?
Yes, I am aware metadata spec has no provision of vendor data. But there is nothing illegal in appending vendor-data to metadata and that is trivial to implement ... basically use sizeof(struct fwu_mdata) + sizeof(struct sni_vendor_mdata) while read/write meta-data. That will also be zero extra-overhead.
fwu-mdata { compatible = "u-boot,fwu-mdata-mtd"; fwu-mdata-store = <&spi_flash>; mdata-offsets = <0x500000 0x530000>; vendor-data-size = <0x100>; // optional };
Sure we can use an efi variable, but I see more uses of vendor-data :- shared among BL1/BL2/BL3x/OS so we can emulate reset-syndrome, crash-logging, per-image bootcount etc when the h/w doesn't support these features.
Ofcourse, please feel free to implement efi-variables still.
Ok, in that case, you'll still have to implement this as a 'special' bootcount method since the A/B updates code will use that API to get/set the values.
Thanks /Ilias
thanks.

On Fri, Jul 22, 2022 at 3:37 AM Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Jassi
On Wed, 20 Jul 2022 at 17:30, Jassi Brar jassisinghbrar@gmail.com wrote:
On Wed, Jul 20, 2022 at 2:54 AM Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Jassi,
On Tue, 19 Jul 2022 at 18:27, Jassi Brar jaswinder.singh@linaro.org wrote:
On Mon, 18 Jul 2022 at 16:00, Tom Rini trini@konsulko.com wrote:
On Mon, Jul 18, 2022 at 10:31:56AM -0500, Jassi Brar wrote:
> > > > > > > > > > + > > > > > > +#define PLAT_METADATA_OFFSET 0x510000 > > > > > > +#define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata)) > > > > > > + > > > > > > +struct __packed devbox_metadata { > > > > > > + u32 boot_index; > > > > > > + u32 boot_count; > > > > > > > > > > There is the whole bootcount infrastructure for this. I think it would be much > > > > > better to use that framework instead of creating parallel one. > > > > > > > > > Yes, this goes too. > > > > > > Is bootcount really suited for this case? > > > AFAIK bootcount either requires device specific registers (which won't > > > reset on reboots), or an environment you can write data to. > > > But what if a user wants to disable writing the env variables and the > > > device doesn't have a set of registers we can use? > > > > > Maybe it should be moved in 'struct fwu_mdata' ? > > I was mostly thinking on moving this count as another 'bootcount' > method. So in case the user has disabled writing evn variables but he > is booting with EFI he can use that.
Sorry, not sure I understand.... IIUIC there has to be some persistent storage.
No, there just has to be "somewhere" to do the counting. We've got a DDR backed driver, for example. So yes, I think we should try and use the bootcount framework here.
OK, for platforms that can preserve ram across reboot, using non-persistent storage can work. My platform neither preserves ram, nor has any warmreset-proof registers. So I have to choose between saving the bootcount in efi-env or in vendor specific structure next to the metadata. I prefer metadata because it is common to all stages of boot. Any corrections to this approach?
The metadata is defined by a spec and they don't have a field for bootcounting. Once Sughosh resends his patches he'll include a bootcount backend that reuses EFI variables. Can't we just use that?
Yes, I am aware metadata spec has no provision of vendor data. But there is nothing illegal in appending vendor-data to metadata and that is trivial to implement ... basically use sizeof(struct fwu_mdata) + sizeof(struct sni_vendor_mdata) while read/write meta-data. That will also be zero extra-overhead.
fwu-mdata { compatible = "u-boot,fwu-mdata-mtd"; fwu-mdata-store = <&spi_flash>; mdata-offsets = <0x500000 0x530000>; vendor-data-size = <0x100>; // optional };
Sure we can use an efi variable, but I see more uses of vendor-data :- shared among BL1/BL2/BL3x/OS so we can emulate reset-syndrome, crash-logging, per-image bootcount etc when the h/w doesn't support these features.
Ofcourse, please feel free to implement efi-variables still.
Ok, in that case, you'll still have to implement this as a 'special' bootcount method since the A/B updates code will use that API to get/set the values.
I thought the bootcount mechanism would always be platform specific? But ok.
thanks.

From: Masami Hiramatsu masami.hiramatsu@linaro.org
Generate dfu_alt_info from the partition uuid information in the devicetree, and record the mapping of partition uuid and the index of dfu_alt_num.
This could be a reference implementation of the automatic DFU generation for FWU multi-bank update for non GPT firmware platforms.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- .../synquacer-sc2a11-developerbox-u-boot.dtsi | 3 + board/socionext/developerbox/Kconfig | 1 + board/socionext/developerbox/fwu_plat.c | 79 ++++---- include/configs/synquacer.h | 6 +- include/fwu.h | 6 + lib/fwu_updates/Makefile | 1 + lib/fwu_updates/fwu_mtd.c | 173 ++++++++++++++++++ 7 files changed, 221 insertions(+), 48 deletions(-) create mode 100644 lib/fwu_updates/fwu_mtd.c
diff --git a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi index ab4e3d1c2b..c7ec8a0321 100644 --- a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi +++ b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi @@ -36,6 +36,7 @@ compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; + uuid = "17e86d77-41f9-4fd7-87ec-a55df9842de5";
partition@0 { label = "BootStrap-BL1"; @@ -88,10 +89,12 @@ 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"; }; }; }; diff --git a/board/socionext/developerbox/Kconfig b/board/socionext/developerbox/Kconfig index 7df6750baf..ad2a284f13 100644 --- a/board/socionext/developerbox/Kconfig +++ b/board/socionext/developerbox/Kconfig @@ -38,6 +38,7 @@ config FWU_MULTI_BANK_UPDATE select DM_SPI_FLASH select DM_FWU_MDATA select BOARD_LATE_INIT + select SET_DFU_ALT_INFO
config FWU_NUM_BANKS default 2 diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c index fd6d0e3659..ff06eade7d 100644 --- a/board/socionext/developerbox/fwu_plat.c +++ b/board/socionext/developerbox/fwu_plat.c @@ -10,8 +10,10 @@ #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> @@ -94,6 +96,36 @@ static int sf_save_data(u32 offs, u32 size, void *data) return ret; }
+#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); +} + #define PLAT_METADATA_OFFSET 0x510000 #define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata))
@@ -105,49 +137,7 @@ struct __packed devbox_metadata { int fwu_plat_get_alt_num(struct udevice __always_unused *dev, efi_guid_t *image_id, int *alt_num) { - struct fwu_image_bank_info *bank; - struct fwu_mdata *mdata; - int i, ret; - - ret = fwu_get_mdata(&mdata); - if (ret < 0) - return ret; - - /* - * DeveloperBox FWU expects Bank:Image = 1:1, and the dfu_alt_info - * only has the entries for banks. Thus the alt_no should be equal - * to the bank index number. - */ - ret = -ENOENT; - for (i = 0; i < CONFIG_FWU_NUM_BANKS; i++) { - bank = &mdata->img_entry[0].img_bank_info[i]; - if (guidcmp(image_id, &bank->image_uuid) == 0) { - *alt_num = i; - ret = 0; - break; - } - } - - free(mdata); - - return ret; -} - -/* This assumes that user doesn't change system default dfu_alt_info */ -efi_status_t fill_image_type_guid_array(const efi_guid_t __always_unused - *default_guid, - efi_guid_t **part_guid_arr) -{ - int i; - - *part_guid_arr = malloc(sizeof(efi_guid_t) * DEFAULT_DFU_ALT_NUM); - if (!*part_guid_arr) - return EFI_OUT_OF_RESOURCES; - - for (i = 0; i < DEFAULT_DFU_ALT_NUM; i++) - guidcpy((*part_guid_arr + i), &devbox_fip_image_type_guid); - - return EFI_SUCCESS; + return fwu_get_mtd_alt_num(image_id, alt_num, "nor1", 0); }
int fwu_plat_get_update_index(u32 *update_idx) @@ -188,6 +178,9 @@ int board_late_init(void) { int ret;
+ /* Make mmc available for EFI */ + run_command("mmc dev 0", 0); + ret = devbox_load_plat_metadata(); if (ret < 0) return ret; diff --git a/include/configs/synquacer.h b/include/configs/synquacer.h index 14eeb3f57e..103277661b 100644 --- a/include/configs/synquacer.h +++ b/include/configs/synquacer.h @@ -47,12 +47,8 @@ /* Since U-Boot 64bit PCIe support is limited, disable 64bit MMIO support */
#ifdef CONFIG_FWU_MULTI_BANK_UPDATE -#define DEFAULT_DFU_ALT_NUM 2 -#define DEFAULT_DFU_ALT_INFO "dfu_alt_info=" \ - "mtd nor1=bank0 raw 600000 400000;" \ - "bank1 raw a00000 400000\0" +#define DEFAULT_DFU_ALT_INFO #else -#define DEFAULT_DFU_ALT_NUM 1 #define DEFAULT_DFU_ALT_INFO "dfu_alt_info=" \ "mtd nor1=fip.bin raw 600000 400000\0" #endif diff --git a/include/fwu.h b/include/fwu.h index 9c8012407b..fadbfedd07 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -63,4 +63,10 @@ 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_plat_get_update_index(u32 *update_idx); + +int fwu_get_mtd_alt_num(efi_guid_t *image_id, int *alt_num, + const char *mtd_dev, bool guid); +int gen_image_alt_info(char *buf, size_t len, int sidx, + struct fwu_image_entry *img, struct mtd_info *mtd); +int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd); #endif /* _FWU_H_ */ diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile index 74e37014e2..a135fd3f4a 100644 --- a/lib/fwu_updates/Makefile +++ b/lib/fwu_updates/Makefile @@ -4,3 +4,4 @@ #
obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.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..3137f8635c --- /dev/null +++ b/lib/fwu_updates/fwu_mtd.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 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 fwu_get_mtd_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) +{ + char *p = buf, *end = buf + len; + char uuidbuf[UUID_STR_LEN + 1]; + ofnode node, parts_node; + const char *suuid; + int i; + + /* 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; + 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 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
Generate dfu_alt_info from the partition uuid information in the devicetree, and record the mapping of partition uuid and the index of dfu_alt_num.
This could be a reference implementation of the automatic DFU generation for FWU multi-bank update for non GPT firmware platforms.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
.../synquacer-sc2a11-developerbox-u-boot.dtsi | 3 + board/socionext/developerbox/Kconfig | 1 + board/socionext/developerbox/fwu_plat.c | 79 ++++---- include/configs/synquacer.h | 6 +- include/fwu.h | 6 + lib/fwu_updates/Makefile | 1 + lib/fwu_updates/fwu_mtd.c | 173 ++++++++++++++++++ 7 files changed, 221 insertions(+), 48 deletions(-) create mode 100644 lib/fwu_updates/fwu_mtd.c
diff --git a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi index ab4e3d1c2b..c7ec8a0321 100644 --- a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi +++ b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi @@ -36,6 +36,7 @@ compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>;
uuid = "17e86d77-41f9-4fd7-87ec-a55df9842de5"; partition@0 { label = "BootStrap-BL1";
@@ -88,10 +89,12 @@ 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"; }; };
diff --git a/board/socionext/developerbox/Kconfig b/board/socionext/developerbox/Kconfig index 7df6750baf..ad2a284f13 100644 --- a/board/socionext/developerbox/Kconfig +++ b/board/socionext/developerbox/Kconfig @@ -38,6 +38,7 @@ config FWU_MULTI_BANK_UPDATE select DM_SPI_FLASH select DM_FWU_MDATA select BOARD_LATE_INIT
select SET_DFU_ALT_INFO
config FWU_NUM_BANKS default 2
diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c index fd6d0e3659..ff06eade7d 100644 --- a/board/socionext/developerbox/fwu_plat.c +++ b/board/socionext/developerbox/fwu_plat.c @@ -10,8 +10,10 @@ #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> @@ -94,6 +96,36 @@ static int sf_save_data(u32 offs, u32 size, void *data) return ret; }
+#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);
+}
- #define PLAT_METADATA_OFFSET 0x510000 #define PLAT_METADATA_SIZE (sizeof(struct devbox_metadata))
@@ -105,49 +137,7 @@ struct __packed devbox_metadata { int fwu_plat_get_alt_num(struct udevice __always_unused *dev, efi_guid_t *image_id, int *alt_num) {
- struct fwu_image_bank_info *bank;
- struct fwu_mdata *mdata;
- int i, ret;
- ret = fwu_get_mdata(&mdata);
- if (ret < 0)
return ret;
- /*
* DeveloperBox FWU expects Bank:Image = 1:1, and the dfu_alt_info
* only has the entries for banks. Thus the alt_no should be equal
* to the bank index number.
*/
- ret = -ENOENT;
- for (i = 0; i < CONFIG_FWU_NUM_BANKS; i++) {
bank = &mdata->img_entry[0].img_bank_info[i];
if (guidcmp(image_id, &bank->image_uuid) == 0) {
*alt_num = i;
ret = 0;
break;
}
- }
- free(mdata);
- return ret;
-}
-/* This assumes that user doesn't change system default dfu_alt_info */ -efi_status_t fill_image_type_guid_array(const efi_guid_t __always_unused
*default_guid,
efi_guid_t **part_guid_arr)
-{
- int i;
- *part_guid_arr = malloc(sizeof(efi_guid_t) * DEFAULT_DFU_ALT_NUM);
- if (!*part_guid_arr)
return EFI_OUT_OF_RESOURCES;
- for (i = 0; i < DEFAULT_DFU_ALT_NUM; i++)
guidcpy((*part_guid_arr + i), &devbox_fip_image_type_guid);
- return EFI_SUCCESS;
return fwu_get_mtd_alt_num(image_id, alt_num, "nor1", 0); }
int fwu_plat_get_update_index(u32 *update_idx)
@@ -188,6 +178,9 @@ int board_late_init(void) { int ret;
- /* Make mmc available for EFI */
- run_command("mmc dev 0", 0);
What is this for?
And I can't see any single note about in commit message.
Thanks, Michal

On Fri, 17 Jun 2022 at 09:02, Michal Simek michal.simek@amd.com wrote:
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
.....
@@ -188,6 +178,9 @@ int board_late_init(void) { int ret;
/* Make mmc available for EFI */
run_command("mmc dev 0", 0);
What is this for?
And I can't see any single note about in commit message.
For some reason, we get "No EFI system partition" during bootup and the mmc does not show up in 'efidebug devices' unless we manually run this (or mmc part) command. Though not elegant, I found similar being done by some other platforms.... grep run_command -rw board/* I am happy to learn the proper way of doing it.
Thanks.

On Mon, Jul 18, 2022 at 09:49:56AM -0500, Jassi Brar wrote:
On Fri, 17 Jun 2022 at 09:02, Michal Simek michal.simek@amd.com wrote:
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
.....
@@ -188,6 +178,9 @@ int board_late_init(void) { int ret;
/* Make mmc available for EFI */
run_command("mmc dev 0", 0);
What is this for?
And I can't see any single note about in commit message.
For some reason, we get "No EFI system partition" during bootup and the mmc does not show up in 'efidebug devices' unless we manually run this (or mmc part) command.
As far as UEFI is concerned, any U-Boot block device will be recognized as a UEFI disk(block_io) object *only* after device_probe() is called.
-Takahiro Akashi
Though not elegant, I found similar being done by some other
platforms.... grep run_command -rw board/* I am happy to learn the proper way of doing it.
Thanks.

On Tue, 19 Jul 2022 at 20:13, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Mon, Jul 18, 2022 at 09:49:56AM -0500, Jassi Brar wrote:
On Fri, 17 Jun 2022 at 09:02, Michal Simek michal.simek@amd.com wrote:
On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
.....
@@ -188,6 +178,9 @@ int board_late_init(void) { int ret;
/* Make mmc available for EFI */
run_command("mmc dev 0", 0);
What is this for?
And I can't see any single note about in commit message.
For some reason, we get "No EFI system partition" during bootup and the mmc does not show up in 'efidebug devices' unless we manually run this (or mmc part) command.
As far as UEFI is concerned, any U-Boot block device will be recognized as a UEFI disk(block_io) object *only* after device_probe() is called.
OK, thanks for the clarification. And I shouldn't feel too bad about the hack then :)
thanks.

From: Masami Hiramatsu masami.hiramatsu@linaro.org
Add a section for the instruction of building the FWU Multi Bank Update supported U-Boot and installation.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- doc/board/socionext/developerbox.rst | 110 +++++++++++++++++++++++++++ 1 file changed, 110 insertions(+)
diff --git a/doc/board/socionext/developerbox.rst b/doc/board/socionext/developerbox.rst index 2d943c23be..d9f38a3897 100644 --- a/doc/board/socionext/developerbox.rst +++ b/doc/board/socionext/developerbox.rst @@ -85,3 +85,113 @@ 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_FWU_MDATA_MTD=y + CONFIG_CMD_FWU_METADATA=y + CONFIG_TOOLS_MKFWUMDATA=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. + +Generate FWU metadata image +--------------------------- + +Before installation, you need to generate a FWU metadata image file by mkfwumdata command as below:: + + tools/mkfwumdata -i 1 -b 2 "17e86d77-41f9-4fd7-87ec-a55df9842de5,10c36d7d-ca52-b843-b7b9-f9d6c501d108,5a66a702-99fd-4fef-a392-c26e261a2828,a8f868a1-6e5c-4757-878d-ce63375ef2c0" fwu-mdata.img + +Then, you can get the `fwu-mdata.img` image file. + +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. At first, please install FWU metadata at 0x500000 (primary) and 0x530000 (secondary).:: + + flash rawwrite 500000 60 + >> Send fwu-mdata.img via XMODEM (Control-A S in minicom) << + + flash rawwrite 530000 60 + >> Send fwu-mdata.img via XMODEM (Control-A S in minicom) << + +And 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.)

On 6/9/22 14:30, Sughosh Ganu wrote:
From: Masami Hiramatsu masami.hiramatsu@linaro.org
Add a section for the instruction of building the FWU Multi Bank Update supported U-Boot and installation.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
doc/board/socionext/developerbox.rst | 110 +++++++++++++++++++++++++++ 1 file changed, 110 insertions(+)
diff --git a/doc/board/socionext/developerbox.rst b/doc/board/socionext/developerbox.rst index 2d943c23be..d9f38a3897 100644 --- a/doc/board/socionext/developerbox.rst +++ b/doc/board/socionext/developerbox.rst @@ -85,3 +85,113 @@ 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_FWU_MDATA_MTD=y
- CONFIG_CMD_FWU_METADATA=y
- CONFIG_TOOLS_MKFWUMDATA=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
typo CONFIG_
Thanks, Michal

From: Masami Hiramatsu masami.hiramatsu@linaro.org
Enable FWU Multi-Bank support for DeveloperBox SynQuacer platform. This also enables fwu_metadata_read command and "reboot soon after update" option.
Signed-off-by: Masami Hiramatsu masami.hiramatsu@linaro.org --- Changes in v3: - Use CONFIG_FWU_MDATA_MTD. - Remove unused CONFIG_FWU_INIT_BROKEN_METADATA and add CONFIG_TOOLS_MKFWUMDATA.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- configs/synquacer_developerbox_defconfig | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/configs/synquacer_developerbox_defconfig b/configs/synquacer_developerbox_defconfig index 29b1e11401..f2c8c3cc4e 100644 --- a/configs/synquacer_developerbox_defconfig +++ b/configs/synquacer_developerbox_defconfig @@ -94,3 +94,8 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y +CONFIG_EFI_SECURE_BOOT=y +CONFIG_FWU_MULTI_BANK_UPDATE=y +CONFIG_FWU_MDATA_MTD=y +CONFIG_CMD_FWU_METADATA=y +CONFIG_TOOLS_MKFWUMDATA=y

Add a python test script for testing the FWU Multi Bank Update functionality on the sandbox platform. The script has test cases for updation of the u-boot binary and the u-boot environment image to the non active bank.
The FWU metadata is being stored on the SPI NOR flash, along with the updatable images, and the FWU metadata driver for MTD devices is being used for accessing the metadata. Certain FWU boottime checks are bypassed due to the unavailability of the EFI variable access very early in the boot on the sandbox platform -- the variable access is only available once the block disk image has been bound through the host interface.
The FWU Multi Bank feature being enabled on the sandbox64 platform is enabling the RAW Firmware Management Protocol(FMP) instance, therefore the FIT FMP instance is being removed -- the FIT FMP is already being tested on the sandbox flattree variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 45 ++- board/sandbox/sandbox.c | 49 +++ configs/sandbox64_defconfig | 12 +- include/fwu.h | 2 + lib/fwu_updates/Kconfig | 2 +- lib/fwu_updates/fwu.c | 18 +- lib/fwu_updates/fwu_mtd.c | 10 +- .../test_capsule_firmware_fit.py | 1 - .../py/tests/test_fwu_updates/capsule_defs.py | 10 + test/py/tests/test_fwu_updates/conftest.py | 78 ++++ .../test_fwu_updates/test_fwu_updates.py | 367 ++++++++++++++++++ 12 files changed, 587 insertions(+), 13 deletions(-) create mode 100644 test/py/tests/test_fwu_updates/capsule_defs.py create mode 100644 test/py/tests/test_fwu_updates/conftest.py create mode 100644 test/py/tests/test_fwu_updates/test_fwu_updates.py
diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 5f55c7f28e..2985572083 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -84,3 +84,9 @@ config SYS_FDT_LOAD_ADDR See `doc/arch/sandbox.rst` for more information.
endmenu + +config FWU_NUM_BANKS + default 2 + +config FWU_NUM_IMAGES_PER_BANK + default 2 diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 8f93775ff4..f11fa8733f 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1145,11 +1145,48 @@ pinctrl-names = "default"; pinctrl-0 = <&pinmux_spi0_pins>;
- spi.bin@0 { + spi0: spi.bin@0 { reg = <0>; compatible = "spansion,m25p16", "jedec,spi-nor"; spi-max-frequency = <40000000>; sandbox,filename = "spi.bin"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + uuid = "af9e8c96-bec5-48be-9dab-3491c04b1366"; + + partition@0 { + label = "Metadata"; + reg = <0x0 0x20000>; + }; + + /* FWU Multi bank update partitions */ + partition@100000 { + label = "U-Boot-Bank0"; + reg = <0x100000 0x10000>; + uuid = "a8f61787-5d68-4c9d-9e4a-37bb0df99da7"; + }; + + partition@120000 { + label = "U-Boot-ENV-Bank0"; + reg = <0x120000 0x10000>; + uuid = "ea9d59af-e0e8-4ef5-9b16-4c80ff67524c"; + }; + + partition@140000 { + label = "U-Boot-Bank1"; + reg = <0x140000 0x10000>; + uuid = "52377abf-c4e4-4d0b-aafd-ba081a500847"; + }; + + partition@160000 { + label = "U-Boot-ENV-Bank1"; + reg = <0x160000 0x10000>; + uuid = "4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3"; + }; + }; }; spi.bin@1 { reg = <1>; @@ -1633,6 +1670,12 @@ compatible = "sandbox,regmap_test"; }; }; + + fwu-mdata { + compatible = "u-boot,fwu-mdata-mtd"; + fwu-mdata-store = <&spi0>; + mdata-offsets = <0x0 0x10000>; + }; };
#include "sandbox_pmic.dtsi" diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index e054f300c4..222c36d301 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -6,13 +6,18 @@ #include <common.h> #include <cpu_func.h> #include <cros_ec.h> +#include <dfu.h> #include <dm.h> #include <efi.h> #include <efi_loader.h> #include <env_internal.h> +#include <fwu.h> +#include <fwu_mdata.h> #include <init.h> #include <led.h> +#include <mtd.h> #include <os.h> +#include <uuid.h> #include <asm/global_data.h> #include <asm/test.h> #include <asm/u-boot-sandbox.h> @@ -51,8 +56,15 @@ struct efi_fw_image fw_images[] = { };
struct efi_capsule_update_info update_info = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) + .dfu_string = "mtd nor0=u-boot-bin-a raw 0x100000 0x10000;" + "u-boot-env-a raw 0x120000 0x10000;" + "u-boot-bin-b raw 0x140000 0x10000;" + "u-boot-env-b raw 0x160000 0x10000", +#else .dfu_string = "sf 0:0=u-boot-bin raw 0x100000 0x50000;" "u-boot-env raw 0x150000 0x200000", +#endif .images = fw_images, };
@@ -155,3 +167,40 @@ int board_late_init(void) return 0; } #endif + +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) +int fwu_plat_get_alt_num(struct udevice __always_unused *dev, + efi_guid_t *image_id, int *alt_num) +{ + return fwu_get_mtd_alt_num(image_id, alt_num, "nor0", 1); +} + +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; +} + +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; +} +#endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index d7f22b39ae..7a813b46a5 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -244,9 +244,19 @@ CONFIG_LZ4=y CONFIG_ERRNO_STR=y CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y CONFIG_EFI_CAPSULE_ON_DISK=y -CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y +CONFIG_DM_FWU_MDATA=y +CONFIG_FWU_MULTI_BANK_UPDATE=y +CONFIG_FWU_MDATA_MTD=y +CONFIG_CMD_FWU_METADATA=y +CONFIG_TOOLS_MKFWUMDATA=y +CONFIG_DM_SPI_FLASH=y +CONFIG_CMD_MTD=y +CONFIG_DFU_MTD=y +CONFIG_DM_MTD=y +CONFIG_SPI_FLASH_MTD=y diff --git a/include/fwu.h b/include/fwu.h index fadbfedd07..9e550182eb 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -8,6 +8,8 @@
#include <blk.h> #include <efi.h> +#include <fwu_mdata.h> +#include <mtd.h>
#include <linux/types.h>
diff --git a/lib/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig index 6de28e0c9c..43bed7729c 100644 --- a/lib/fwu_updates/Kconfig +++ b/lib/fwu_updates/Kconfig @@ -2,7 +2,7 @@ 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 + select EFI_SETUP_EARLY if !SANDBOX help Feature for updating firmware images on platforms having multiple banks(copies) of the firmware images. One of the diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 422ef58661..f2c10d836b 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -185,12 +185,22 @@ int fwu_boottime_checks(void) return 0; }
- if (efi_init_obj_list() != EFI_SUCCESS) - return 0; + /* + * On the sandbox platform, the EFI variable + * access is available only after binding the + * disk image with the host interface. Skip + * the Trial State check on sandbox. + */ + if (!IS_ENABLED(CONFIG_SANDBOX)) { + if (efi_init_obj_list() != EFI_SUCCESS) + return 0;
- ret = fwu_trial_state_check(); - if (!ret) + ret = fwu_trial_state_check(); + if (!ret) + boottime_check = 1; + } else { boottime_check = 1; + }
return 0; } diff --git a/lib/fwu_updates/fwu_mtd.c b/lib/fwu_updates/fwu_mtd.c index 3137f8635c..3a9ad70203 100644 --- a/lib/fwu_updates/fwu_mtd.c +++ b/lib/fwu_updates/fwu_mtd.c @@ -84,11 +84,11 @@ int fwu_get_mtd_alt_num(efi_guid_t *image_id, int *alt_num, int gen_image_alt_info(char *buf, size_t len, int sidx, struct fwu_image_entry *img, struct mtd_info *mtd) { - char *p = buf, *end = buf + len; - char uuidbuf[UUID_STR_LEN + 1]; - ofnode node, parts_node; - const char *suuid; 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), @@ -145,7 +145,7 @@ int gen_image_alt_info(char *buf, size_t len, int sidx,
int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd) { - struct fwu_mdata *mdata; + struct fwu_mdata *mdata = NULL; int i, l, ret;
ret = fwu_get_mdata(&mdata); diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py index 5bef84958b..93bc5ed44b 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py @@ -13,7 +13,6 @@ import pytest from capsule_defs import *
-@pytest.mark.boardspec('sandbox64') @pytest.mark.boardspec('sandbox_flattree') @pytest.mark.buildconfigspec('efi_capsule_firmware_fit') @pytest.mark.buildconfigspec('efi_capsule_on_disk') diff --git a/test/py/tests/test_fwu_updates/capsule_defs.py b/test/py/tests/test_fwu_updates/capsule_defs.py new file mode 100644 index 0000000000..59b40f11bd --- /dev/null +++ b/test/py/tests/test_fwu_updates/capsule_defs.py @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ + +# Directories +CAPSULE_DATA_DIR = '/EFI/CapsuleTestData' +CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule' + +# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and +# you need build a newer version on your own. +# The path must terminate with '/' if it is not null. +EFITOOLS_PATH = '' diff --git a/test/py/tests/test_fwu_updates/conftest.py b/test/py/tests/test_fwu_updates/conftest.py new file mode 100644 index 0000000000..cdf824c3be --- /dev/null +++ b/test/py/tests/test_fwu_updates/conftest.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org + +import os +import os.path +import re +from subprocess import call, check_call, check_output, CalledProcessError +import pytest +from capsule_defs import * + +# +# Fixture for UEFI capsule test +# + +@pytest.fixture(scope='session') +def efi_capsule_data(request, u_boot_config): + """Set up a file system to be used in UEFI capsule and + authentication test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A path to disk image to be used for testing + """ + global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR + + mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule' + data_dir = mnt_point + CAPSULE_DATA_DIR + install_dir = mnt_point + CAPSULE_INSTALL_DIR + image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img' + + try: + # Create a target device + check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True) + + check_call('rm -rf %s' % mnt_point, shell=True) + check_call('mkdir -p %s' % data_dir, shell=True) + check_call('mkdir -p %s' % install_dir, shell=True) + + # Create capsule files + # two regions: one for u-boot.bin and the other for u-boot.env + check_call('cd %s; echo -n u-boot-a:Old > u-boot-a.bin.old; echo -n u-boot-env-a:Old > u-boot-env-a.old; echo -n u-boot-a:New > u-boot-a.bin.new; echo -n u-boot-env-a:New > u-boot-env-a.new; echo -n u-boot-b:Old > u-boot-b.bin.old; echo -n u-boot-env-b:Old > u-boot-env-b.old; echo -n u-boot-b:New > u-boot-b.bin.new; echo -n u-boot-env-b:New > u-boot-env-b.new;' % data_dir, + shell=True) + check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 0 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank0.bin' %(data_dir, u_boot_config.build_dir), shell=True) + check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 1 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank1.bin' %(data_dir, u_boot_config.build_dir), shell=True) + + check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-b.bin.new Test01' % + (data_dir, u_boot_config.build_dir), + shell=True) + check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-b.new Test02' % + (data_dir, u_boot_config.build_dir), + shell=True) + + check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-a.bin.new Test03' % + (data_dir, u_boot_config.build_dir), + shell=True) + check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-a.new Test04' % + (data_dir, u_boot_config.build_dir), + shell=True) + + # Create a disk image with EFI system partition + check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' % + (mnt_point, image_path), shell=True) + check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' % + image_path, shell=True) + + except CalledProcessError as exception: + pytest.skip('Setup failed: %s' % exception.cmd) + return + else: + yield image_path + finally: + call('rm -rf %s' % mnt_point, shell=True) + call('rm -f %s' % image_path, shell=True) + call('rm -f ./spi.bin', shell=True) diff --git a/test/py/tests/test_fwu_updates/test_fwu_updates.py b/test/py/tests/test_fwu_updates/test_fwu_updates.py new file mode 100644 index 0000000000..d9dff3afaf --- /dev/null +++ b/test/py/tests/test_fwu_updates/test_fwu_updates.py @@ -0,0 +1,367 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022, Linaro Limited +# +# FWU Multi Bank Firmware Update Test + +""" +This test verifies FWU Multi Bank firmware update for raw images +""" + +from subprocess import check_call, check_output, CalledProcessError +import pytest +from capsule_defs import * + + +@pytest.mark.boardspec('sandbox64') +@pytest.mark.buildconfigspec('efi_capsule_firmware_raw') +@pytest.mark.buildconfigspec('efi_capsule_on_disk') +@pytest.mark.buildconfigspec('dfu') +@pytest.mark.buildconfigspec('dfu_sf') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('fwu_multi_bank_update') +@pytest.mark.buildconfigspec('dm_fwu_mdata') +@pytest.mark.buildconfigspec('fwu_mdata_mtd') +@pytest.mark.slow +class TestEfiCapsuleFirmwareRaw(object): + def test_fwu_updates_fw1( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 1 - Update U-Boot Bank 1 binary on SPI Flash + 0x100000-0x150000: U-Boot binary Bank 0 (but dummy) + 0x150000-0x200000: U-Boot binary Bank 1 (but dummy) + """ + + # other tests might have run and the + # system might not be in a clean state. + # Restart before starting the tests. + u_boot_console.restart_uboot() + + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 1-a, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""', + 'efidebug boot order 1', + 'env set -e -nv -bs -rt OsIndications =0x0000000000000004', + 'env save']) + + # initialize contents + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 100000 10', + 'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 140000 10']) + + output = u_boot_console.run_command_list([ + 'sf read 5000000 100000 10', + 'md.b 5000000 10' + ]) + assert 'u-boot-a:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 5100000 140000 10', + 'md.b 5100000 10' + ]) + assert 'u-boot-b:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR, + 'sf write 4000000 0 $filesize', + 'sf write 4000000 10000 $filesize']) + + output = u_boot_console.run_command( + 'fwu_mdata_read') + assert 'active_index: 0x0' in ''.join(output) + assert 'previous_active_index: 0x1' in ''.join(output) + + # place a capsule file + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test01' in ''.join(output) + + # reboot + u_boot_console.restart_uboot() + + with u_boot_console.log.section('Test Case 1-b, after reboot'): + # make sure that dfu_alt_info exists even persistent variables + # are not available. + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test01' in ''.join(output) + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'efidebug capsule disk-update', wait_for_reboot = True) + + output = u_boot_console.run_command( + 'fwu_mdata_read') + assert 'active_index: 0x1' in ''.join(output) + assert 'previous_active_index: 0x0' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 100000 10', + 'md.b 4000000 10']) + assert 'u-boot-a:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 4000000 140000 10', + 'md.b 4000000 10']) + assert 'u-boot-b:New' in ''.join(output) + + def test_fwu_updates_fw2( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 2 - Update U-Boot and U-Boot Env Bank 1 binary on SPI Flash + 0x100000-0x110000: U-Boot binary Bank 0 (but dummy) + 0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy) + 0x140000-0x150000: U-Boot binary Bank 1 (but dummy) + 0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy) + """ + + # other tests might have run and the + # system might not be in a clean state. + # Restart before starting the tests. + u_boot_console.restart_uboot() + + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 2-a, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""', + 'efidebug boot order 1', + 'env set -e -nv -bs -rt OsIndications =0x0000000000000004', + 'env save']) + + # initialize contents + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 100000 10', + 'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 120000 10', + 'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 140000 10', + 'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 160000 10']) + + output = u_boot_console.run_command_list([ + 'sf read 5000000 100000 10', + 'md.b 5000000 10' + ]) + assert 'u-boot-a:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 5000000 120000 10', + 'md.b 5000000 10' + ]) + assert 'u-boot-env-a:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 5100000 140000 10', + 'md.b 5100000 10' + ]) + assert 'u-boot-b:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 5000000 160000 10', + 'md.b 5000000 10' + ]) + assert 'u-boot-env-b:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR, + 'sf write 4000000 0 100', + 'sf write 4000000 10000 100']) + + output = u_boot_console.run_command( + 'fwu_mdata_read') + assert 'active_index: 0x0' in ''.join(output) + assert 'previous_active_index: 0x1' in ''.join(output) + + # place a capsule file + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR, + 'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test01' in ''.join(output) + assert 'Test02' in ''.join(output) + + # reboot + u_boot_console.restart_uboot() + + with u_boot_console.log.section('Test Case 2-b, after reboot'): + # make sure that dfu_alt_info exists even persistent variables + # are not available. + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test01' in ''.join(output) + assert 'Test02' in ''.join(output) + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'efidebug capsule disk-update', wait_for_reboot = True) + + output = u_boot_console.run_command( + 'fwu_mdata_read') + assert 'active_index: 0x1' in ''.join(output) + assert 'previous_active_index: 0x0' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 100000 10', + 'md.b 4000000 10']) + assert 'u-boot-a:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 120000 10', + 'md.b 4000000 10']) + assert 'u-boot-env-a:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 4000000 140000 10', + 'md.b 4000000 10']) + assert 'u-boot-b:New' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 4000000 160000 10', + 'md.b 4000000 10']) + assert 'u-boot-env-b:New' in ''.join(output) + + def test_fwu_updates_fw3( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 3 - Update U-Boot and U-Boot Env Bank 0 binary on SPI Flash + 0x100000-0x110000: U-Boot binary Bank 0 (but dummy) + 0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy) + 0x140000-0x150000: U-Boot binary Bank 1 (but dummy) + 0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy) + """ + + # other tests might have run and the + # system might not be in a clean state. + # Restart before starting the tests. + u_boot_console.restart_uboot() + + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 3-a, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""', + 'efidebug boot order 1', + 'env set -e -nv -bs -rt OsIndications =0x0000000000000004', + 'env save']) + + # initialize contents + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 100000 10', + 'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 120000 10', + 'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 140000 10', + 'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 160000 10']) + + output = u_boot_console.run_command_list([ + 'sf read 5000000 100000 10', + 'md.b 5000000 10' + ]) + assert 'u-boot-a:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 5000000 120000 10', + 'md.b 5000000 10' + ]) + assert 'u-boot-env-a:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 5100000 140000 10', + 'md.b 5100000 10' + ]) + assert 'u-boot-b:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 5000000 160000 10', + 'md.b 5000000 10' + ]) + assert 'u-boot-env-b:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/metadata_bank1.bin' % CAPSULE_DATA_DIR, + 'sf write 4000000 0 100', + 'sf write 4000000 10000 100']) + + output = u_boot_console.run_command( + 'fwu_mdata_read') + assert 'active_index: 0x1' in ''.join(output) + assert 'previous_active_index: 0x0' in ''.join(output) + + # place a capsule file + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 %s/Test03' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test03 $filesize' % CAPSULE_INSTALL_DIR, + 'fatload host 0:1 4000000 %s/Test04' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test04 $filesize' % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test03' in ''.join(output) + assert 'Test04' in ''.join(output) + + # reboot + u_boot_console.restart_uboot() + + with u_boot_console.log.section('Test Case 3-b, after reboot'): + # make sure that dfu_alt_info exists even persistent variables + # are not available. + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test03' in ''.join(output) + assert 'Test04' in ''.join(output) + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'efidebug capsule disk-update', wait_for_reboot = True) + + output = u_boot_console.run_command( + 'fwu_mdata_read') + assert 'active_index: 0x0' in ''.join(output) + assert 'previous_active_index: 0x1' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 100000 10', + 'md.b 4000000 10']) + assert 'u-boot-a:New' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 120000 10', + 'md.b 4000000 10']) + assert 'u-boot-env-a:New' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 4000000 140000 10', + 'md.b 4000000 10']) + assert 'u-boot-b:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 4000000 160000 10', + 'md.b 4000000 10']) + assert 'u-boot-env-b:Old' in ''.join(output)

On Thu, Jun 09, 2022 at 06:00:10PM +0530, Sughosh Ganu wrote:
Add a python test script for testing the FWU Multi Bank Update functionality on the sandbox platform. The script has test cases for updation of the u-boot binary and the u-boot environment image to the non active bank.
The FWU metadata is being stored on the SPI NOR flash, along with the updatable images, and the FWU metadata driver for MTD devices is being used for accessing the metadata. Certain FWU boottime checks are bypassed due to the unavailability of the EFI variable access very early in the boot on the sandbox platform -- the variable access is only available once the block disk image has been bound through the host interface.
The FWU Multi Bank feature being enabled on the sandbox64 platform is enabling the RAW Firmware Management Protocol(FMP) instance, therefore the FIT FMP instance is being removed -- the FIT FMP is already being tested on the sandbox flattree variant.
IMO, Thinking of the importance of this feature, FIT FMP should also be tested *with FWU*.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 45 ++- board/sandbox/sandbox.c | 49 +++ configs/sandbox64_defconfig | 12 +- include/fwu.h | 2 + lib/fwu_updates/Kconfig | 2 +- lib/fwu_updates/fwu.c | 18 +- lib/fwu_updates/fwu_mtd.c | 10 +- .../test_capsule_firmware_fit.py | 1 - .../py/tests/test_fwu_updates/capsule_defs.py | 10 + test/py/tests/test_fwu_updates/conftest.py | 78 ++++ .../test_fwu_updates/test_fwu_updates.py | 367 ++++++++++++++++++ 12 files changed, 587 insertions(+), 13 deletions(-) create mode 100644 test/py/tests/test_fwu_updates/capsule_defs.py create mode 100644 test/py/tests/test_fwu_updates/conftest.py create mode 100644 test/py/tests/test_fwu_updates/test_fwu_updates.py
diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 5f55c7f28e..2985572083 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -84,3 +84,9 @@ config SYS_FDT_LOAD_ADDR See `doc/arch/sandbox.rst` for more information.
endmenu
+config FWU_NUM_BANKS
default 2
+config FWU_NUM_IMAGES_PER_BANK
- default 2
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 8f93775ff4..f11fa8733f 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1145,11 +1145,48 @@ pinctrl-names = "default"; pinctrl-0 = <&pinmux_spi0_pins>;
spi.bin@0 {
spi0: spi.bin@0 { reg = <0>; compatible = "spansion,m25p16", "jedec,spi-nor"; spi-max-frequency = <40000000>; sandbox,filename = "spi.bin";
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
uuid = "af9e8c96-bec5-48be-9dab-3491c04b1366";
partition@0 {
label = "Metadata";
reg = <0x0 0x20000>;
};
/* FWU Multi bank update partitions */
partition@100000 {
label = "U-Boot-Bank0";
reg = <0x100000 0x10000>;
uuid = "a8f61787-5d68-4c9d-9e4a-37bb0df99da7";
};
partition@120000 {
label = "U-Boot-ENV-Bank0";
reg = <0x120000 0x10000>;
uuid = "ea9d59af-e0e8-4ef5-9b16-4c80ff67524c";
};
partition@140000 {
label = "U-Boot-Bank1";
reg = <0x140000 0x10000>;
uuid = "52377abf-c4e4-4d0b-aafd-ba081a500847";
};
partition@160000 {
label = "U-Boot-ENV-Bank1";
reg = <0x160000 0x10000>;
uuid = "4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3";
};
}; spi.bin@1 { reg = <1>;};
@@ -1633,6 +1670,12 @@ compatible = "sandbox,regmap_test"; }; };
- fwu-mdata {
compatible = "u-boot,fwu-mdata-mtd";
fwu-mdata-store = <&spi0>;
mdata-offsets = <0x0 0x10000>;
- };
};
#include "sandbox_pmic.dtsi" diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index e054f300c4..222c36d301 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -6,13 +6,18 @@ #include <common.h> #include <cpu_func.h> #include <cros_ec.h> +#include <dfu.h> #include <dm.h> #include <efi.h> #include <efi_loader.h> #include <env_internal.h> +#include <fwu.h> +#include <fwu_mdata.h> #include <init.h> #include <led.h> +#include <mtd.h> #include <os.h> +#include <uuid.h> #include <asm/global_data.h> #include <asm/test.h> #include <asm/u-boot-sandbox.h> @@ -51,8 +56,15 @@ struct efi_fw_image fw_images[] = { };
struct efi_capsule_update_info update_info = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE)
- .dfu_string = "mtd nor0=u-boot-bin-a raw 0x100000 0x10000;"
"u-boot-env-a raw 0x120000 0x10000;"
"u-boot-bin-b raw 0x140000 0x10000;"
"u-boot-env-b raw 0x160000 0x10000",
+#else .dfu_string = "sf 0:0=u-boot-bin raw 0x100000 0x50000;" "u-boot-env raw 0x150000 0x200000", +#endif .images = fw_images, };
@@ -155,3 +167,40 @@ int board_late_init(void) return 0; } #endif
+#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) +int fwu_plat_get_alt_num(struct udevice __always_unused *dev,
efi_guid_t *image_id, int *alt_num)
+{
- return fwu_get_mtd_alt_num(image_id, alt_num, "nor0", 1);
+}
+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;
+}
+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;
+} +#endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index d7f22b39ae..7a813b46a5 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -244,9 +244,19 @@ CONFIG_LZ4=y CONFIG_ERRNO_STR=y CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y CONFIG_EFI_CAPSULE_ON_DISK=y -CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y +CONFIG_DM_FWU_MDATA=y +CONFIG_FWU_MULTI_BANK_UPDATE=y +CONFIG_FWU_MDATA_MTD=y +CONFIG_CMD_FWU_METADATA=y +CONFIG_TOOLS_MKFWUMDATA=y +CONFIG_DM_SPI_FLASH=y +CONFIG_CMD_MTD=y +CONFIG_DFU_MTD=y +CONFIG_DM_MTD=y +CONFIG_SPI_FLASH_MTD=y diff --git a/include/fwu.h b/include/fwu.h index fadbfedd07..9e550182eb 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -8,6 +8,8 @@
#include <blk.h> #include <efi.h> +#include <fwu_mdata.h> +#include <mtd.h>
#include <linux/types.h>
diff --git a/lib/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig index 6de28e0c9c..43bed7729c 100644 --- a/lib/fwu_updates/Kconfig +++ b/lib/fwu_updates/Kconfig @@ -2,7 +2,7 @@ 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
- select EFI_SETUP_EARLY if !SANDBOX help Feature for updating firmware images on platforms having multiple banks(copies) of the firmware images. One of the
diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 422ef58661..f2c10d836b 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -185,12 +185,22 @@ int fwu_boottime_checks(void) return 0; }
- if (efi_init_obj_list() != EFI_SUCCESS)
return 0;
- /*
* On the sandbox platform, the EFI variable
* access is available only after binding the
* disk image with the host interface. Skip
* the Trial State check on sandbox.
*/
- if (!IS_ENABLED(CONFIG_SANDBOX)) {
if (efi_init_obj_list() != EFI_SUCCESS)
return 0;
- ret = fwu_trial_state_check();
- if (!ret)
ret = fwu_trial_state_check();
if (!ret)
boottime_check = 1;
} else { boottime_check = 1;
}
return 0;
} diff --git a/lib/fwu_updates/fwu_mtd.c b/lib/fwu_updates/fwu_mtd.c index 3137f8635c..3a9ad70203 100644 --- a/lib/fwu_updates/fwu_mtd.c +++ b/lib/fwu_updates/fwu_mtd.c @@ -84,11 +84,11 @@ int fwu_get_mtd_alt_num(efi_guid_t *image_id, int *alt_num, int gen_image_alt_info(char *buf, size_t len, int sidx, struct fwu_image_entry *img, struct mtd_info *mtd) {
- char *p = buf, *end = buf + len;
- char uuidbuf[UUID_STR_LEN + 1];
- ofnode node, parts_node;
- const char *suuid; 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),
@@ -145,7 +145,7 @@ int gen_image_alt_info(char *buf, size_t len, int sidx,
int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd) {
- struct fwu_mdata *mdata;
struct fwu_mdata *mdata = NULL; int i, l, ret;
ret = fwu_get_mdata(&mdata);
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py index 5bef84958b..93bc5ed44b 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py @@ -13,7 +13,6 @@ import pytest from capsule_defs import *
-@pytest.mark.boardspec('sandbox64') @pytest.mark.boardspec('sandbox_flattree') @pytest.mark.buildconfigspec('efi_capsule_firmware_fit') @pytest.mark.buildconfigspec('efi_capsule_on_disk') diff --git a/test/py/tests/test_fwu_updates/capsule_defs.py b/test/py/tests/test_fwu_updates/capsule_defs.py new file mode 100644 index 0000000000..59b40f11bd --- /dev/null +++ b/test/py/tests/test_fwu_updates/capsule_defs.py @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+
+# Directories +CAPSULE_DATA_DIR = '/EFI/CapsuleTestData' +CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule'
+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and +# you need build a newer version on your own. +# The path must terminate with '/' if it is not null. +EFITOOLS_PATH = '' diff --git a/test/py/tests/test_fwu_updates/conftest.py b/test/py/tests/test_fwu_updates/conftest.py new file mode 100644 index 0000000000..cdf824c3be --- /dev/null +++ b/test/py/tests/test_fwu_updates/conftest.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org
If this file is exactly same as test_efi_capsule/conftest.py, why not move all the tests (test_fwu_updates.py) to test_efi_capsule?
The basic scenario of updating firmware, u-boot.bin and u-boot.env, is also the same, isn't it? The only difference is whether FWU_MULTI_BANK_UPDATE is enabled or not.
-Takahiro Akashi
+import os +import os.path +import re +from subprocess import call, check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+# +# Fixture for UEFI capsule test +#
+@pytest.fixture(scope='session') +def efi_capsule_data(request, u_boot_config):
- """Set up a file system to be used in UEFI capsule and
authentication test.
- Args:
request: Pytest request object.
u_boot_config: U-boot configuration.
- Return:
A path to disk image to be used for testing
- """
- global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
- mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
- data_dir = mnt_point + CAPSULE_DATA_DIR
- install_dir = mnt_point + CAPSULE_INSTALL_DIR
- image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
- try:
# Create a target device
check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
check_call('rm -rf %s' % mnt_point, shell=True)
check_call('mkdir -p %s' % data_dir, shell=True)
check_call('mkdir -p %s' % install_dir, shell=True)
# Create capsule files
# two regions: one for u-boot.bin and the other for u-boot.env
check_call('cd %s; echo -n u-boot-a:Old > u-boot-a.bin.old; echo -n u-boot-env-a:Old > u-boot-env-a.old; echo -n u-boot-a:New > u-boot-a.bin.new; echo -n u-boot-env-a:New > u-boot-env-a.new; echo -n u-boot-b:Old > u-boot-b.bin.old; echo -n u-boot-env-b:Old > u-boot-env-b.old; echo -n u-boot-b:New > u-boot-b.bin.new; echo -n u-boot-env-b:New > u-boot-env-b.new;' % data_dir,
shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 0 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank0.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 1 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank1.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-b.bin.new Test01' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-b.new Test02' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-a.bin.new Test03' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-a.new Test04' %
(data_dir, u_boot_config.build_dir),
shell=True)
# Create a disk image with EFI system partition
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
(mnt_point, image_path), shell=True)
check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' %
image_path, shell=True)
- except CalledProcessError as exception:
pytest.skip('Setup failed: %s' % exception.cmd)
return
- else:
yield image_path
- finally:
call('rm -rf %s' % mnt_point, shell=True)
call('rm -f %s' % image_path, shell=True)
call('rm -f ./spi.bin', shell=True)
diff --git a/test/py/tests/test_fwu_updates/test_fwu_updates.py b/test/py/tests/test_fwu_updates/test_fwu_updates.py new file mode 100644 index 0000000000..d9dff3afaf --- /dev/null +++ b/test/py/tests/test_fwu_updates/test_fwu_updates.py @@ -0,0 +1,367 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022, Linaro Limited +# +# FWU Multi Bank Firmware Update Test
+""" +This test verifies FWU Multi Bank firmware update for raw images +"""
+from subprocess import check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+@pytest.mark.boardspec('sandbox64') +@pytest.mark.buildconfigspec('efi_capsule_firmware_raw') +@pytest.mark.buildconfigspec('efi_capsule_on_disk') +@pytest.mark.buildconfigspec('dfu') +@pytest.mark.buildconfigspec('dfu_sf') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('fwu_multi_bank_update') +@pytest.mark.buildconfigspec('dm_fwu_mdata') +@pytest.mark.buildconfigspec('fwu_mdata_mtd') +@pytest.mark.slow +class TestEfiCapsuleFirmwareRaw(object):
- def test_fwu_updates_fw1(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 1 - Update U-Boot Bank 1 binary on SPI Flash
0x100000-0x150000: U-Boot binary Bank 0 (but dummy)
0x150000-0x200000: U-Boot binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 1-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 $filesize',
'sf write 4000000 10000 $filesize'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 1-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
- def test_fwu_updates_fw2(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 2 - Update U-Boot and U-Boot Env Bank 1 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 2-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 2-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:New' in ''.join(output)
- def test_fwu_updates_fw3(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 3 - Update U-Boot and U-Boot Env Bank 0 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 3-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank1.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test03' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test03 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test04' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test04 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 3-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:Old' in ''.join(output)
-- 2.25.1

On Wed, 15 Jun 2022 at 11:07, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Thu, Jun 09, 2022 at 06:00:10PM +0530, Sughosh Ganu wrote:
Add a python test script for testing the FWU Multi Bank Update functionality on the sandbox platform. The script has test cases for updation of the u-boot binary and the u-boot environment image to the non active bank.
The FWU metadata is being stored on the SPI NOR flash, along with the updatable images, and the FWU metadata driver for MTD devices is being used for accessing the metadata. Certain FWU boottime checks are bypassed due to the unavailability of the EFI variable access very early in the boot on the sandbox platform -- the variable access is only available once the block disk image has been bound through the host interface.
The FWU Multi Bank feature being enabled on the sandbox64 platform is enabling the RAW Firmware Management Protocol(FMP) instance, therefore the FIT FMP instance is being removed -- the FIT FMP is already being tested on the sandbox flattree variant.
IMO, Thinking of the importance of this feature, FIT FMP should also be tested *with FWU*.
How will the FWU update feature work for FIT images? As I understand FIT, it is a way of packaging different firmware images into a single package. At the time of writing the images, the FIT image parser would check the image configuration, and write the images to their respective locations. As you are aware, for the FWU feature, the information about the images, and the update bank is obtained from the structure called metadata. How does the FIT update mechanism map with the FWU metadata which is used to identify which bank needs to be updated. The bank to which the image is to be written translates into the DFU alt_num value for that image, and this gets computed at runtime. In the case of the FIT image, as per my understanding, the alt_num value is irrelevant. So my question is, how do we map the information obtained from the FWU metadata to tell the FIT image writing function(fit_update) which locations do the images need to be written to. I think this needs some more thought.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 45 ++- board/sandbox/sandbox.c | 49 +++ configs/sandbox64_defconfig | 12 +- include/fwu.h | 2 + lib/fwu_updates/Kconfig | 2 +- lib/fwu_updates/fwu.c | 18 +- lib/fwu_updates/fwu_mtd.c | 10 +- .../test_capsule_firmware_fit.py | 1 - .../py/tests/test_fwu_updates/capsule_defs.py | 10 + test/py/tests/test_fwu_updates/conftest.py | 78 ++++ .../test_fwu_updates/test_fwu_updates.py | 367 ++++++++++++++++++ 12 files changed, 587 insertions(+), 13 deletions(-) create mode 100644 test/py/tests/test_fwu_updates/capsule_defs.py create mode 100644 test/py/tests/test_fwu_updates/conftest.py create mode 100644 test/py/tests/test_fwu_updates/test_fwu_updates.py
<snip>
diff --git a/test/py/tests/test_fwu_updates/conftest.py b/test/py/tests/test_fwu_updates/conftest.py new file mode 100644 index 0000000000..cdf824c3be --- /dev/null +++ b/test/py/tests/test_fwu_updates/conftest.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org
If this file is exactly same as test_efi_capsule/conftest.py, why not move all the tests (test_fwu_updates.py) to test_efi_capsule?
The files are not exactly the same. There is use of the mkfwumdata utility used for FWU tests, along with the capsule files that are being generated. I had tried putting the code under the test_efi_capsule directory, but the result was getting cluttered. Which is why I decided to put the changes separately under the test_fwu_updates directory.
The basic scenario of updating firmware, u-boot.bin and u-boot.env, is also the same, isn't it? The only difference is whether FWU_MULTI_BANK_UPDATE is enabled or not.
There are two capsule files per image, one per bank. Also, the FWU metadata is being written to the SPI NOR device, which is being formatted as a MTD partitioned device. The underlying update mechanism is the same, yes.
-sughosh
-Takahiro Akashi
+import os +import os.path +import re +from subprocess import call, check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+# +# Fixture for UEFI capsule test +#
+@pytest.fixture(scope='session') +def efi_capsule_data(request, u_boot_config):
- """Set up a file system to be used in UEFI capsule and
authentication test.
- Args:
request: Pytest request object.
u_boot_config: U-boot configuration.
- Return:
A path to disk image to be used for testing
- """
- global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
- mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
- data_dir = mnt_point + CAPSULE_DATA_DIR
- install_dir = mnt_point + CAPSULE_INSTALL_DIR
- image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
- try:
# Create a target device
check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
check_call('rm -rf %s' % mnt_point, shell=True)
check_call('mkdir -p %s' % data_dir, shell=True)
check_call('mkdir -p %s' % install_dir, shell=True)
# Create capsule files
# two regions: one for u-boot.bin and the other for u-boot.env
check_call('cd %s; echo -n u-boot-a:Old > u-boot-a.bin.old; echo -n u-boot-env-a:Old > u-boot-env-a.old; echo -n u-boot-a:New > u-boot-a.bin.new; echo -n u-boot-env-a:New > u-boot-env-a.new; echo -n u-boot-b:Old > u-boot-b.bin.old; echo -n u-boot-env-b:Old > u-boot-env-b.old; echo -n u-boot-b:New > u-boot-b.bin.new; echo -n u-boot-env-b:New > u-boot-env-b.new;' % data_dir,
shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 0 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank0.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 1 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank1.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-b.bin.new Test01' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-b.new Test02' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-a.bin.new Test03' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-a.new Test04' %
(data_dir, u_boot_config.build_dir),
shell=True)
# Create a disk image with EFI system partition
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
(mnt_point, image_path), shell=True)
check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' %
image_path, shell=True)
- except CalledProcessError as exception:
pytest.skip('Setup failed: %s' % exception.cmd)
return
- else:
yield image_path
- finally:
call('rm -rf %s' % mnt_point, shell=True)
call('rm -f %s' % image_path, shell=True)
call('rm -f ./spi.bin', shell=True)
diff --git a/test/py/tests/test_fwu_updates/test_fwu_updates.py b/test/py/tests/test_fwu_updates/test_fwu_updates.py new file mode 100644 index 0000000000..d9dff3afaf --- /dev/null +++ b/test/py/tests/test_fwu_updates/test_fwu_updates.py @@ -0,0 +1,367 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022, Linaro Limited +# +# FWU Multi Bank Firmware Update Test
+""" +This test verifies FWU Multi Bank firmware update for raw images +"""
+from subprocess import check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+@pytest.mark.boardspec('sandbox64') +@pytest.mark.buildconfigspec('efi_capsule_firmware_raw') +@pytest.mark.buildconfigspec('efi_capsule_on_disk') +@pytest.mark.buildconfigspec('dfu') +@pytest.mark.buildconfigspec('dfu_sf') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('fwu_multi_bank_update') +@pytest.mark.buildconfigspec('dm_fwu_mdata') +@pytest.mark.buildconfigspec('fwu_mdata_mtd') +@pytest.mark.slow +class TestEfiCapsuleFirmwareRaw(object):
- def test_fwu_updates_fw1(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 1 - Update U-Boot Bank 1 binary on SPI Flash
0x100000-0x150000: U-Boot binary Bank 0 (but dummy)
0x150000-0x200000: U-Boot binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 1-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 $filesize',
'sf write 4000000 10000 $filesize'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 1-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
- def test_fwu_updates_fw2(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 2 - Update U-Boot and U-Boot Env Bank 1 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 2-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 2-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:New' in ''.join(output)
- def test_fwu_updates_fw3(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 3 - Update U-Boot and U-Boot Env Bank 0 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 3-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank1.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test03' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test03 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test04' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test04 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 3-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:Old' in ''.join(output)
-- 2.25.1

Sughosh,
On Wed, Jun 15, 2022 at 05:40:12PM +0530, Sughosh Ganu wrote:
On Wed, 15 Jun 2022 at 11:07, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Thu, Jun 09, 2022 at 06:00:10PM +0530, Sughosh Ganu wrote:
Add a python test script for testing the FWU Multi Bank Update functionality on the sandbox platform. The script has test cases for updation of the u-boot binary and the u-boot environment image to the non active bank.
The FWU metadata is being stored on the SPI NOR flash, along with the updatable images, and the FWU metadata driver for MTD devices is being used for accessing the metadata. Certain FWU boottime checks are bypassed due to the unavailability of the EFI variable access very early in the boot on the sandbox platform -- the variable access is only available once the block disk image has been bound through the host interface.
The FWU Multi Bank feature being enabled on the sandbox64 platform is enabling the RAW Firmware Management Protocol(FMP) instance, therefore the FIT FMP instance is being removed -- the FIT FMP is already being tested on the sandbox flattree variant.
IMO, Thinking of the importance of this feature, FIT FMP should also be tested *with FWU*.
How will the FWU update feature work for FIT images? As I understand
So are you deliberately designing and proposing a solution that is incompatible with (or not applicable to) an existing interface (FIT FMP)?
FIT, it is a way of packaging different firmware images into a single package. At the time of writing the images, the FIT image parser would check the image configuration, and write the images to their respective locations. As you are aware, for the FWU feature, the information about the images, and the update bank is obtained from the structure called metadata. How does the FIT update mechanism map with the FWU metadata which is used to identify which bank needs to be updated. The bank to which the image is to be written translates into the DFU alt_num value for that image, and this gets computed at runtime. In the case of the FIT image, as per my understanding, the alt_num value is irrelevant. So my question is, how do we map the information obtained from the FWU metadata to tell the FIT image writing function(fit_update) which locations do the images need to be written to. I think this needs some more thought.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 45 ++- board/sandbox/sandbox.c | 49 +++ configs/sandbox64_defconfig | 12 +- include/fwu.h | 2 + lib/fwu_updates/Kconfig | 2 +- lib/fwu_updates/fwu.c | 18 +- lib/fwu_updates/fwu_mtd.c | 10 +- .../test_capsule_firmware_fit.py | 1 - .../py/tests/test_fwu_updates/capsule_defs.py | 10 + test/py/tests/test_fwu_updates/conftest.py | 78 ++++ .../test_fwu_updates/test_fwu_updates.py | 367 ++++++++++++++++++ 12 files changed, 587 insertions(+), 13 deletions(-) create mode 100644 test/py/tests/test_fwu_updates/capsule_defs.py create mode 100644 test/py/tests/test_fwu_updates/conftest.py create mode 100644 test/py/tests/test_fwu_updates/test_fwu_updates.py
<snip>
diff --git a/test/py/tests/test_fwu_updates/conftest.py b/test/py/tests/test_fwu_updates/conftest.py new file mode 100644 index 0000000000..cdf824c3be --- /dev/null +++ b/test/py/tests/test_fwu_updates/conftest.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org
If this file is exactly same as test_efi_capsule/conftest.py, why not move all the tests (test_fwu_updates.py) to test_efi_capsule?
The files are not exactly the same. There is use of the mkfwumdata utility used for FWU tests, along with the capsule files that are being generated. I had tried putting the code under the test_efi_capsule directory, but the result was getting cluttered.
Okay, but from my curiosity, how cluttered was it? It seems to me that you are simply adding extra setup steps.
My simple concern is that, if there is a fair amount of common code between two tests, it should be unified for maintainability.
-Takahiro Akashi
Which is why I decided to put the changes separately under the test_fwu_updates directory.
The basic scenario of updating firmware, u-boot.bin and u-boot.env, is also the same, isn't it? The only difference is whether FWU_MULTI_BANK_UPDATE is enabled or not.
There are two capsule files per image, one per bank. Also, the FWU metadata is being written to the SPI NOR device, which is being formatted as a MTD partitioned device. The underlying update mechanism is the same, yes.
-sughosh
-Takahiro Akashi
+import os +import os.path +import re +from subprocess import call, check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+# +# Fixture for UEFI capsule test +#
+@pytest.fixture(scope='session') +def efi_capsule_data(request, u_boot_config):
- """Set up a file system to be used in UEFI capsule and
authentication test.
- Args:
request: Pytest request object.
u_boot_config: U-boot configuration.
- Return:
A path to disk image to be used for testing
- """
- global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
- mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
- data_dir = mnt_point + CAPSULE_DATA_DIR
- install_dir = mnt_point + CAPSULE_INSTALL_DIR
- image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
- try:
# Create a target device
check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
check_call('rm -rf %s' % mnt_point, shell=True)
check_call('mkdir -p %s' % data_dir, shell=True)
check_call('mkdir -p %s' % install_dir, shell=True)
# Create capsule files
# two regions: one for u-boot.bin and the other for u-boot.env
check_call('cd %s; echo -n u-boot-a:Old > u-boot-a.bin.old; echo -n u-boot-env-a:Old > u-boot-env-a.old; echo -n u-boot-a:New > u-boot-a.bin.new; echo -n u-boot-env-a:New > u-boot-env-a.new; echo -n u-boot-b:Old > u-boot-b.bin.old; echo -n u-boot-env-b:Old > u-boot-env-b.old; echo -n u-boot-b:New > u-boot-b.bin.new; echo -n u-boot-env-b:New > u-boot-env-b.new;' % data_dir,
shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 0 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank0.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 1 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank1.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-b.bin.new Test01' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-b.new Test02' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-a.bin.new Test03' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-a.new Test04' %
(data_dir, u_boot_config.build_dir),
shell=True)
# Create a disk image with EFI system partition
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
(mnt_point, image_path), shell=True)
check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' %
image_path, shell=True)
- except CalledProcessError as exception:
pytest.skip('Setup failed: %s' % exception.cmd)
return
- else:
yield image_path
- finally:
call('rm -rf %s' % mnt_point, shell=True)
call('rm -f %s' % image_path, shell=True)
call('rm -f ./spi.bin', shell=True)
diff --git a/test/py/tests/test_fwu_updates/test_fwu_updates.py b/test/py/tests/test_fwu_updates/test_fwu_updates.py new file mode 100644 index 0000000000..d9dff3afaf --- /dev/null +++ b/test/py/tests/test_fwu_updates/test_fwu_updates.py @@ -0,0 +1,367 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022, Linaro Limited +# +# FWU Multi Bank Firmware Update Test
+""" +This test verifies FWU Multi Bank firmware update for raw images +"""
+from subprocess import check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+@pytest.mark.boardspec('sandbox64') +@pytest.mark.buildconfigspec('efi_capsule_firmware_raw') +@pytest.mark.buildconfigspec('efi_capsule_on_disk') +@pytest.mark.buildconfigspec('dfu') +@pytest.mark.buildconfigspec('dfu_sf') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('fwu_multi_bank_update') +@pytest.mark.buildconfigspec('dm_fwu_mdata') +@pytest.mark.buildconfigspec('fwu_mdata_mtd') +@pytest.mark.slow +class TestEfiCapsuleFirmwareRaw(object):
- def test_fwu_updates_fw1(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 1 - Update U-Boot Bank 1 binary on SPI Flash
0x100000-0x150000: U-Boot binary Bank 0 (but dummy)
0x150000-0x200000: U-Boot binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 1-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 $filesize',
'sf write 4000000 10000 $filesize'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 1-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
- def test_fwu_updates_fw2(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 2 - Update U-Boot and U-Boot Env Bank 1 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 2-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 2-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:New' in ''.join(output)
- def test_fwu_updates_fw3(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 3 - Update U-Boot and U-Boot Env Bank 0 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 3-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank1.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test03' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test03 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test04' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test04 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 3-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:Old' in ''.join(output)
-- 2.25.1

On Fri, 17 Jun 2022 at 06:38, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Sughosh,
On Wed, Jun 15, 2022 at 05:40:12PM +0530, Sughosh Ganu wrote:
On Wed, 15 Jun 2022 at 11:07, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Thu, Jun 09, 2022 at 06:00:10PM +0530, Sughosh Ganu wrote:
Add a python test script for testing the FWU Multi Bank Update functionality on the sandbox platform. The script has test cases for updation of the u-boot binary and the u-boot environment image to the non active bank.
The FWU metadata is being stored on the SPI NOR flash, along with the updatable images, and the FWU metadata driver for MTD devices is being used for accessing the metadata. Certain FWU boottime checks are bypassed due to the unavailability of the EFI variable access very early in the boot on the sandbox platform -- the variable access is only available once the block disk image has been bound through the host interface.
The FWU Multi Bank feature being enabled on the sandbox64 platform is enabling the RAW Firmware Management Protocol(FMP) instance, therefore the FIT FMP instance is being removed -- the FIT FMP is already being tested on the sandbox flattree variant.
IMO, Thinking of the importance of this feature, FIT FMP should also be tested *with FWU*.
How will the FWU update feature work for FIT images? As I understand
So are you deliberately designing and proposing a solution that is incompatible with (or not applicable to) an existing interface (FIT FMP)?
Heh, why would anyone do that? The very fact that we are introducing support on two platforms, one with a GPT layout, and another with MTD is because the idea is to encourage adoption on multiple platforms. Secondly, this is a specification from Arm which was driven by Jose Marinho, and I believe he has incorporated feedback from various engineers for drafting the specification -- this specification is not limited only to platforms booting with u-boot. The main issue, as I see it, is that the FWU metadata is doing the bookkeeping for images that are present on the platform's storage device, and this just does not map with FIT which is a packaging mechanism. However, this being software, you can make changes to the fit_update function and incorporate the information from the metadata to write to the relevant locations. However, this needs to be thought through.
FIT, it is a way of packaging different firmware images into a single package. At the time of writing the images, the FIT image parser would check the image configuration, and write the images to their respective locations. As you are aware, for the FWU feature, the information about the images, and the update bank is obtained from the structure called metadata. How does the FIT update mechanism map with the FWU metadata which is used to identify which bank needs to be updated. The bank to which the image is to be written translates into the DFU alt_num value for that image, and this gets computed at runtime. In the case of the FIT image, as per my understanding, the alt_num value is irrelevant. So my question is, how do we map the information obtained from the FWU metadata to tell the FIT image writing function(fit_update) which locations do the images need to be written to. I think this needs some more thought.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 45 ++- board/sandbox/sandbox.c | 49 +++ configs/sandbox64_defconfig | 12 +- include/fwu.h | 2 + lib/fwu_updates/Kconfig | 2 +- lib/fwu_updates/fwu.c | 18 +- lib/fwu_updates/fwu_mtd.c | 10 +- .../test_capsule_firmware_fit.py | 1 - .../py/tests/test_fwu_updates/capsule_defs.py | 10 + test/py/tests/test_fwu_updates/conftest.py | 78 ++++ .../test_fwu_updates/test_fwu_updates.py | 367 ++++++++++++++++++ 12 files changed, 587 insertions(+), 13 deletions(-) create mode 100644 test/py/tests/test_fwu_updates/capsule_defs.py create mode 100644 test/py/tests/test_fwu_updates/conftest.py create mode 100644 test/py/tests/test_fwu_updates/test_fwu_updates.py
<snip>
diff --git a/test/py/tests/test_fwu_updates/conftest.py b/test/py/tests/test_fwu_updates/conftest.py new file mode 100644 index 0000000000..cdf824c3be --- /dev/null +++ b/test/py/tests/test_fwu_updates/conftest.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org
If this file is exactly same as test_efi_capsule/conftest.py, why not move all the tests (test_fwu_updates.py) to test_efi_capsule?
The files are not exactly the same. There is use of the mkfwumdata utility used for FWU tests, along with the capsule files that are being generated. I had tried putting the code under the test_efi_capsule directory, but the result was getting cluttered.
Okay, but from my curiosity, how cluttered was it? It seems to me that you are simply adding extra setup steps.
Yes, I am adding extra commands to be called, but I found the multiple commands for creation of the u-boot, u-boot-env and the same images per bank subsequently to be adding some confusion. Same for the capsule file creation as well.
My simple concern is that, if there is a fair amount of common code between two tests, it should be unified for maintainability.
Yes, I understand, which is why I had even started with putting these commands under the capsule test's conftest.py file.
-sughosh
-Takahiro Akashi
Which is why I decided to put the changes separately under the test_fwu_updates directory.
The basic scenario of updating firmware, u-boot.bin and u-boot.env, is also the same, isn't it? The only difference is whether FWU_MULTI_BANK_UPDATE is enabled or not.
There are two capsule files per image, one per bank. Also, the FWU metadata is being written to the SPI NOR device, which is being formatted as a MTD partitioned device. The underlying update mechanism is the same, yes.
-sughosh
-Takahiro Akashi
+import os +import os.path +import re +from subprocess import call, check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+# +# Fixture for UEFI capsule test +#
+@pytest.fixture(scope='session') +def efi_capsule_data(request, u_boot_config):
- """Set up a file system to be used in UEFI capsule and
authentication test.
- Args:
request: Pytest request object.
u_boot_config: U-boot configuration.
- Return:
A path to disk image to be used for testing
- """
- global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
- mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
- data_dir = mnt_point + CAPSULE_DATA_DIR
- install_dir = mnt_point + CAPSULE_INSTALL_DIR
- image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
- try:
# Create a target device
check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
check_call('rm -rf %s' % mnt_point, shell=True)
check_call('mkdir -p %s' % data_dir, shell=True)
check_call('mkdir -p %s' % install_dir, shell=True)
# Create capsule files
# two regions: one for u-boot.bin and the other for u-boot.env
check_call('cd %s; echo -n u-boot-a:Old > u-boot-a.bin.old; echo -n u-boot-env-a:Old > u-boot-env-a.old; echo -n u-boot-a:New > u-boot-a.bin.new; echo -n u-boot-env-a:New > u-boot-env-a.new; echo -n u-boot-b:Old > u-boot-b.bin.old; echo -n u-boot-env-b:Old > u-boot-env-b.old; echo -n u-boot-b:New > u-boot-b.bin.new; echo -n u-boot-env-b:New > u-boot-env-b.new;' % data_dir,
shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 0 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank0.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 1 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank1.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-b.bin.new Test01' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-b.new Test02' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-a.bin.new Test03' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-a.new Test04' %
(data_dir, u_boot_config.build_dir),
shell=True)
# Create a disk image with EFI system partition
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
(mnt_point, image_path), shell=True)
check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' %
image_path, shell=True)
- except CalledProcessError as exception:
pytest.skip('Setup failed: %s' % exception.cmd)
return
- else:
yield image_path
- finally:
call('rm -rf %s' % mnt_point, shell=True)
call('rm -f %s' % image_path, shell=True)
call('rm -f ./spi.bin', shell=True)
diff --git a/test/py/tests/test_fwu_updates/test_fwu_updates.py b/test/py/tests/test_fwu_updates/test_fwu_updates.py new file mode 100644 index 0000000000..d9dff3afaf --- /dev/null +++ b/test/py/tests/test_fwu_updates/test_fwu_updates.py @@ -0,0 +1,367 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022, Linaro Limited +# +# FWU Multi Bank Firmware Update Test
+""" +This test verifies FWU Multi Bank firmware update for raw images +"""
+from subprocess import check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+@pytest.mark.boardspec('sandbox64') +@pytest.mark.buildconfigspec('efi_capsule_firmware_raw') +@pytest.mark.buildconfigspec('efi_capsule_on_disk') +@pytest.mark.buildconfigspec('dfu') +@pytest.mark.buildconfigspec('dfu_sf') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('fwu_multi_bank_update') +@pytest.mark.buildconfigspec('dm_fwu_mdata') +@pytest.mark.buildconfigspec('fwu_mdata_mtd') +@pytest.mark.slow +class TestEfiCapsuleFirmwareRaw(object):
- def test_fwu_updates_fw1(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 1 - Update U-Boot Bank 1 binary on SPI Flash
0x100000-0x150000: U-Boot binary Bank 0 (but dummy)
0x150000-0x200000: U-Boot binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 1-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 $filesize',
'sf write 4000000 10000 $filesize'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 1-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
- def test_fwu_updates_fw2(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 2 - Update U-Boot and U-Boot Env Bank 1 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 2-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 2-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:New' in ''.join(output)
- def test_fwu_updates_fw3(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 3 - Update U-Boot and U-Boot Env Bank 0 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 3-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank1.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test03' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test03 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test04' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test04 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 3-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:Old' in ''.join(output)
-- 2.25.1

On Thu, Jun 09, 2022 at 06:00:10PM +0530, Sughosh Ganu wrote:
Add a python test script for testing the FWU Multi Bank Update functionality on the sandbox platform. The script has test cases for updation of the u-boot binary and the u-boot environment image to the non active bank.
IIUC, your test doesn't not exercise neither accept-capsule nor revert capsule. I think those tests are crucial for verifying the code.
-Takahiro Akashi
The FWU metadata is being stored on the SPI NOR flash, along with the updatable images, and the FWU metadata driver for MTD devices is being used for accessing the metadata. Certain FWU boottime checks are bypassed due to the unavailability of the EFI variable access very early in the boot on the sandbox platform -- the variable access is only available once the block disk image has been bound through the host interface.
The FWU Multi Bank feature being enabled on the sandbox64 platform is enabling the RAW Firmware Management Protocol(FMP) instance, therefore the FIT FMP instance is being removed -- the FIT FMP is already being tested on the sandbox flattree variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 45 ++- board/sandbox/sandbox.c | 49 +++ configs/sandbox64_defconfig | 12 +- include/fwu.h | 2 + lib/fwu_updates/Kconfig | 2 +- lib/fwu_updates/fwu.c | 18 +- lib/fwu_updates/fwu_mtd.c | 10 +- .../test_capsule_firmware_fit.py | 1 - .../py/tests/test_fwu_updates/capsule_defs.py | 10 + test/py/tests/test_fwu_updates/conftest.py | 78 ++++ .../test_fwu_updates/test_fwu_updates.py | 367 ++++++++++++++++++ 12 files changed, 587 insertions(+), 13 deletions(-) create mode 100644 test/py/tests/test_fwu_updates/capsule_defs.py create mode 100644 test/py/tests/test_fwu_updates/conftest.py create mode 100644 test/py/tests/test_fwu_updates/test_fwu_updates.py
diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 5f55c7f28e..2985572083 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -84,3 +84,9 @@ config SYS_FDT_LOAD_ADDR See `doc/arch/sandbox.rst` for more information.
endmenu
+config FWU_NUM_BANKS
default 2
+config FWU_NUM_IMAGES_PER_BANK
- default 2
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 8f93775ff4..f11fa8733f 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1145,11 +1145,48 @@ pinctrl-names = "default"; pinctrl-0 = <&pinmux_spi0_pins>;
spi.bin@0 {
spi0: spi.bin@0 { reg = <0>; compatible = "spansion,m25p16", "jedec,spi-nor"; spi-max-frequency = <40000000>; sandbox,filename = "spi.bin";
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
uuid = "af9e8c96-bec5-48be-9dab-3491c04b1366";
partition@0 {
label = "Metadata";
reg = <0x0 0x20000>;
};
/* FWU Multi bank update partitions */
partition@100000 {
label = "U-Boot-Bank0";
reg = <0x100000 0x10000>;
uuid = "a8f61787-5d68-4c9d-9e4a-37bb0df99da7";
};
partition@120000 {
label = "U-Boot-ENV-Bank0";
reg = <0x120000 0x10000>;
uuid = "ea9d59af-e0e8-4ef5-9b16-4c80ff67524c";
};
partition@140000 {
label = "U-Boot-Bank1";
reg = <0x140000 0x10000>;
uuid = "52377abf-c4e4-4d0b-aafd-ba081a500847";
};
partition@160000 {
label = "U-Boot-ENV-Bank1";
reg = <0x160000 0x10000>;
uuid = "4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3";
};
}; spi.bin@1 { reg = <1>;};
@@ -1633,6 +1670,12 @@ compatible = "sandbox,regmap_test"; }; };
- fwu-mdata {
compatible = "u-boot,fwu-mdata-mtd";
fwu-mdata-store = <&spi0>;
mdata-offsets = <0x0 0x10000>;
- };
};
#include "sandbox_pmic.dtsi" diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index e054f300c4..222c36d301 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -6,13 +6,18 @@ #include <common.h> #include <cpu_func.h> #include <cros_ec.h> +#include <dfu.h> #include <dm.h> #include <efi.h> #include <efi_loader.h> #include <env_internal.h> +#include <fwu.h> +#include <fwu_mdata.h> #include <init.h> #include <led.h> +#include <mtd.h> #include <os.h> +#include <uuid.h> #include <asm/global_data.h> #include <asm/test.h> #include <asm/u-boot-sandbox.h> @@ -51,8 +56,15 @@ struct efi_fw_image fw_images[] = { };
struct efi_capsule_update_info update_info = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE)
- .dfu_string = "mtd nor0=u-boot-bin-a raw 0x100000 0x10000;"
"u-boot-env-a raw 0x120000 0x10000;"
"u-boot-bin-b raw 0x140000 0x10000;"
"u-boot-env-b raw 0x160000 0x10000",
+#else .dfu_string = "sf 0:0=u-boot-bin raw 0x100000 0x50000;" "u-boot-env raw 0x150000 0x200000", +#endif .images = fw_images, };
@@ -155,3 +167,40 @@ int board_late_init(void) return 0; } #endif
+#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) +int fwu_plat_get_alt_num(struct udevice __always_unused *dev,
efi_guid_t *image_id, int *alt_num)
+{
- return fwu_get_mtd_alt_num(image_id, alt_num, "nor0", 1);
+}
+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;
+}
+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;
+} +#endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index d7f22b39ae..7a813b46a5 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -244,9 +244,19 @@ CONFIG_LZ4=y CONFIG_ERRNO_STR=y CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y CONFIG_EFI_CAPSULE_ON_DISK=y -CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y +CONFIG_DM_FWU_MDATA=y +CONFIG_FWU_MULTI_BANK_UPDATE=y +CONFIG_FWU_MDATA_MTD=y +CONFIG_CMD_FWU_METADATA=y +CONFIG_TOOLS_MKFWUMDATA=y +CONFIG_DM_SPI_FLASH=y +CONFIG_CMD_MTD=y +CONFIG_DFU_MTD=y +CONFIG_DM_MTD=y +CONFIG_SPI_FLASH_MTD=y diff --git a/include/fwu.h b/include/fwu.h index fadbfedd07..9e550182eb 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -8,6 +8,8 @@
#include <blk.h> #include <efi.h> +#include <fwu_mdata.h> +#include <mtd.h>
#include <linux/types.h>
diff --git a/lib/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig index 6de28e0c9c..43bed7729c 100644 --- a/lib/fwu_updates/Kconfig +++ b/lib/fwu_updates/Kconfig @@ -2,7 +2,7 @@ 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
- select EFI_SETUP_EARLY if !SANDBOX help Feature for updating firmware images on platforms having multiple banks(copies) of the firmware images. One of the
diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 422ef58661..f2c10d836b 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -185,12 +185,22 @@ int fwu_boottime_checks(void) return 0; }
- if (efi_init_obj_list() != EFI_SUCCESS)
return 0;
- /*
* On the sandbox platform, the EFI variable
* access is available only after binding the
* disk image with the host interface. Skip
* the Trial State check on sandbox.
*/
- if (!IS_ENABLED(CONFIG_SANDBOX)) {
if (efi_init_obj_list() != EFI_SUCCESS)
return 0;
- ret = fwu_trial_state_check();
- if (!ret)
ret = fwu_trial_state_check();
if (!ret)
boottime_check = 1;
} else { boottime_check = 1;
}
return 0;
} diff --git a/lib/fwu_updates/fwu_mtd.c b/lib/fwu_updates/fwu_mtd.c index 3137f8635c..3a9ad70203 100644 --- a/lib/fwu_updates/fwu_mtd.c +++ b/lib/fwu_updates/fwu_mtd.c @@ -84,11 +84,11 @@ int fwu_get_mtd_alt_num(efi_guid_t *image_id, int *alt_num, int gen_image_alt_info(char *buf, size_t len, int sidx, struct fwu_image_entry *img, struct mtd_info *mtd) {
- char *p = buf, *end = buf + len;
- char uuidbuf[UUID_STR_LEN + 1];
- ofnode node, parts_node;
- const char *suuid; 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),
@@ -145,7 +145,7 @@ int gen_image_alt_info(char *buf, size_t len, int sidx,
int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd) {
- struct fwu_mdata *mdata;
struct fwu_mdata *mdata = NULL; int i, l, ret;
ret = fwu_get_mdata(&mdata);
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py index 5bef84958b..93bc5ed44b 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py @@ -13,7 +13,6 @@ import pytest from capsule_defs import *
-@pytest.mark.boardspec('sandbox64') @pytest.mark.boardspec('sandbox_flattree') @pytest.mark.buildconfigspec('efi_capsule_firmware_fit') @pytest.mark.buildconfigspec('efi_capsule_on_disk') diff --git a/test/py/tests/test_fwu_updates/capsule_defs.py b/test/py/tests/test_fwu_updates/capsule_defs.py new file mode 100644 index 0000000000..59b40f11bd --- /dev/null +++ b/test/py/tests/test_fwu_updates/capsule_defs.py @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+
+# Directories +CAPSULE_DATA_DIR = '/EFI/CapsuleTestData' +CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule'
+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and +# you need build a newer version on your own. +# The path must terminate with '/' if it is not null. +EFITOOLS_PATH = '' diff --git a/test/py/tests/test_fwu_updates/conftest.py b/test/py/tests/test_fwu_updates/conftest.py new file mode 100644 index 0000000000..cdf824c3be --- /dev/null +++ b/test/py/tests/test_fwu_updates/conftest.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org
+import os +import os.path +import re +from subprocess import call, check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+# +# Fixture for UEFI capsule test +#
+@pytest.fixture(scope='session') +def efi_capsule_data(request, u_boot_config):
- """Set up a file system to be used in UEFI capsule and
authentication test.
- Args:
request: Pytest request object.
u_boot_config: U-boot configuration.
- Return:
A path to disk image to be used for testing
- """
- global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
- mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
- data_dir = mnt_point + CAPSULE_DATA_DIR
- install_dir = mnt_point + CAPSULE_INSTALL_DIR
- image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
- try:
# Create a target device
check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
check_call('rm -rf %s' % mnt_point, shell=True)
check_call('mkdir -p %s' % data_dir, shell=True)
check_call('mkdir -p %s' % install_dir, shell=True)
# Create capsule files
# two regions: one for u-boot.bin and the other for u-boot.env
check_call('cd %s; echo -n u-boot-a:Old > u-boot-a.bin.old; echo -n u-boot-env-a:Old > u-boot-env-a.old; echo -n u-boot-a:New > u-boot-a.bin.new; echo -n u-boot-env-a:New > u-boot-env-a.new; echo -n u-boot-b:Old > u-boot-b.bin.old; echo -n u-boot-env-b:Old > u-boot-env-b.old; echo -n u-boot-b:New > u-boot-b.bin.new; echo -n u-boot-env-b:New > u-boot-env-b.new;' % data_dir,
shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 0 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank0.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 1 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank1.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-b.bin.new Test01' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-b.new Test02' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-a.bin.new Test03' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-a.new Test04' %
(data_dir, u_boot_config.build_dir),
shell=True)
# Create a disk image with EFI system partition
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
(mnt_point, image_path), shell=True)
check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' %
image_path, shell=True)
- except CalledProcessError as exception:
pytest.skip('Setup failed: %s' % exception.cmd)
return
- else:
yield image_path
- finally:
call('rm -rf %s' % mnt_point, shell=True)
call('rm -f %s' % image_path, shell=True)
call('rm -f ./spi.bin', shell=True)
diff --git a/test/py/tests/test_fwu_updates/test_fwu_updates.py b/test/py/tests/test_fwu_updates/test_fwu_updates.py new file mode 100644 index 0000000000..d9dff3afaf --- /dev/null +++ b/test/py/tests/test_fwu_updates/test_fwu_updates.py @@ -0,0 +1,367 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022, Linaro Limited +# +# FWU Multi Bank Firmware Update Test
+""" +This test verifies FWU Multi Bank firmware update for raw images +"""
+from subprocess import check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+@pytest.mark.boardspec('sandbox64') +@pytest.mark.buildconfigspec('efi_capsule_firmware_raw') +@pytest.mark.buildconfigspec('efi_capsule_on_disk') +@pytest.mark.buildconfigspec('dfu') +@pytest.mark.buildconfigspec('dfu_sf') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('fwu_multi_bank_update') +@pytest.mark.buildconfigspec('dm_fwu_mdata') +@pytest.mark.buildconfigspec('fwu_mdata_mtd') +@pytest.mark.slow +class TestEfiCapsuleFirmwareRaw(object):
- def test_fwu_updates_fw1(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 1 - Update U-Boot Bank 1 binary on SPI Flash
0x100000-0x150000: U-Boot binary Bank 0 (but dummy)
0x150000-0x200000: U-Boot binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 1-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 $filesize',
'sf write 4000000 10000 $filesize'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 1-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
- def test_fwu_updates_fw2(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 2 - Update U-Boot and U-Boot Env Bank 1 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 2-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 2-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:New' in ''.join(output)
- def test_fwu_updates_fw3(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 3 - Update U-Boot and U-Boot Env Bank 0 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 3-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank1.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test03' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test03 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test04' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test04 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 3-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:Old' in ''.join(output)
-- 2.25.1

On Wed, 15 Jun 2022 at 12:00, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Thu, Jun 09, 2022 at 06:00:10PM +0530, Sughosh Ganu wrote:
Add a python test script for testing the FWU Multi Bank Update functionality on the sandbox platform. The script has test cases for updation of the u-boot binary and the u-boot environment image to the non active bank.
IIUC, your test doesn't not exercise neither accept-capsule nor revert capsule. I think those tests are crucial for verifying the code.
Yes, this is on my todo list. By default, all the images get accepted by the firmware itself, which is being done in u-boot. In case the oemflag bit 15 is set to 1 in the capsule header, the image acceptance is done through the accept capsule. I will need to add support for passing the oemflag parameter to the mkeficapsule, and then I can test this. Will keep this on my todo list, and try to put it in the upcoming versions.
-sughosh
-Takahiro Akashi
The FWU metadata is being stored on the SPI NOR flash, along with the updatable images, and the FWU metadata driver for MTD devices is being used for accessing the metadata. Certain FWU boottime checks are bypassed due to the unavailability of the EFI variable access very early in the boot on the sandbox platform -- the variable access is only available once the block disk image has been bound through the host interface.
The FWU Multi Bank feature being enabled on the sandbox64 platform is enabling the RAW Firmware Management Protocol(FMP) instance, therefore the FIT FMP instance is being removed -- the FIT FMP is already being tested on the sandbox flattree variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 45 ++- board/sandbox/sandbox.c | 49 +++ configs/sandbox64_defconfig | 12 +- include/fwu.h | 2 + lib/fwu_updates/Kconfig | 2 +- lib/fwu_updates/fwu.c | 18 +- lib/fwu_updates/fwu_mtd.c | 10 +- .../test_capsule_firmware_fit.py | 1 - .../py/tests/test_fwu_updates/capsule_defs.py | 10 + test/py/tests/test_fwu_updates/conftest.py | 78 ++++ .../test_fwu_updates/test_fwu_updates.py | 367 ++++++++++++++++++ 12 files changed, 587 insertions(+), 13 deletions(-) create mode 100644 test/py/tests/test_fwu_updates/capsule_defs.py create mode 100644 test/py/tests/test_fwu_updates/conftest.py create mode 100644 test/py/tests/test_fwu_updates/test_fwu_updates.py
diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 5f55c7f28e..2985572083 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -84,3 +84,9 @@ config SYS_FDT_LOAD_ADDR See `doc/arch/sandbox.rst` for more information.
endmenu
+config FWU_NUM_BANKS
default 2
+config FWU_NUM_IMAGES_PER_BANK
default 2
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 8f93775ff4..f11fa8733f 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1145,11 +1145,48 @@ pinctrl-names = "default"; pinctrl-0 = <&pinmux_spi0_pins>;
spi.bin@0 {
spi0: spi.bin@0 { reg = <0>; compatible = "spansion,m25p16", "jedec,spi-nor"; spi-max-frequency = <40000000>; sandbox,filename = "spi.bin";
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
uuid = "af9e8c96-bec5-48be-9dab-3491c04b1366";
partition@0 {
label = "Metadata";
reg = <0x0 0x20000>;
};
/* FWU Multi bank update partitions */
partition@100000 {
label = "U-Boot-Bank0";
reg = <0x100000 0x10000>;
uuid = "a8f61787-5d68-4c9d-9e4a-37bb0df99da7";
};
partition@120000 {
label = "U-Boot-ENV-Bank0";
reg = <0x120000 0x10000>;
uuid = "ea9d59af-e0e8-4ef5-9b16-4c80ff67524c";
};
partition@140000 {
label = "U-Boot-Bank1";
reg = <0x140000 0x10000>;
uuid = "52377abf-c4e4-4d0b-aafd-ba081a500847";
};
partition@160000 {
label = "U-Boot-ENV-Bank1";
reg = <0x160000 0x10000>;
uuid = "4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3";
};
}; }; spi.bin@1 { reg = <1>;
@@ -1633,6 +1670,12 @@ compatible = "sandbox,regmap_test"; }; };
fwu-mdata {
compatible = "u-boot,fwu-mdata-mtd";
fwu-mdata-store = <&spi0>;
mdata-offsets = <0x0 0x10000>;
};
};
#include "sandbox_pmic.dtsi" diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index e054f300c4..222c36d301 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -6,13 +6,18 @@ #include <common.h> #include <cpu_func.h> #include <cros_ec.h> +#include <dfu.h> #include <dm.h> #include <efi.h> #include <efi_loader.h> #include <env_internal.h> +#include <fwu.h> +#include <fwu_mdata.h> #include <init.h> #include <led.h> +#include <mtd.h> #include <os.h> +#include <uuid.h> #include <asm/global_data.h> #include <asm/test.h> #include <asm/u-boot-sandbox.h> @@ -51,8 +56,15 @@ struct efi_fw_image fw_images[] = { };
struct efi_capsule_update_info update_info = { +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE)
.dfu_string = "mtd nor0=u-boot-bin-a raw 0x100000 0x10000;"
"u-boot-env-a raw 0x120000 0x10000;"
"u-boot-bin-b raw 0x140000 0x10000;"
"u-boot-env-b raw 0x160000 0x10000",
+#else .dfu_string = "sf 0:0=u-boot-bin raw 0x100000 0x50000;" "u-boot-env raw 0x150000 0x200000", +#endif .images = fw_images, };
@@ -155,3 +167,40 @@ int board_late_init(void) return 0; } #endif
+#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) +int fwu_plat_get_alt_num(struct udevice __always_unused *dev,
efi_guid_t *image_id, int *alt_num)
+{
return fwu_get_mtd_alt_num(image_id, alt_num, "nor0", 1);
+}
+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;
+}
+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;
+} +#endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index d7f22b39ae..7a813b46a5 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -244,9 +244,19 @@ CONFIG_LZ4=y CONFIG_ERRNO_STR=y CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y CONFIG_EFI_CAPSULE_ON_DISK=y -CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y +CONFIG_DM_FWU_MDATA=y +CONFIG_FWU_MULTI_BANK_UPDATE=y +CONFIG_FWU_MDATA_MTD=y +CONFIG_CMD_FWU_METADATA=y +CONFIG_TOOLS_MKFWUMDATA=y +CONFIG_DM_SPI_FLASH=y +CONFIG_CMD_MTD=y +CONFIG_DFU_MTD=y +CONFIG_DM_MTD=y +CONFIG_SPI_FLASH_MTD=y diff --git a/include/fwu.h b/include/fwu.h index fadbfedd07..9e550182eb 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -8,6 +8,8 @@
#include <blk.h> #include <efi.h> +#include <fwu_mdata.h> +#include <mtd.h>
#include <linux/types.h>
diff --git a/lib/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig index 6de28e0c9c..43bed7729c 100644 --- a/lib/fwu_updates/Kconfig +++ b/lib/fwu_updates/Kconfig @@ -2,7 +2,7 @@ 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
select EFI_SETUP_EARLY if !SANDBOX help Feature for updating firmware images on platforms having multiple banks(copies) of the firmware images. One of the
diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 422ef58661..f2c10d836b 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -185,12 +185,22 @@ int fwu_boottime_checks(void) return 0; }
if (efi_init_obj_list() != EFI_SUCCESS)
return 0;
/*
* On the sandbox platform, the EFI variable
* access is available only after binding the
* disk image with the host interface. Skip
* the Trial State check on sandbox.
*/
if (!IS_ENABLED(CONFIG_SANDBOX)) {
if (efi_init_obj_list() != EFI_SUCCESS)
return 0;
ret = fwu_trial_state_check();
if (!ret)
ret = fwu_trial_state_check();
if (!ret)
boottime_check = 1;
} else { boottime_check = 1;
} return 0;
} diff --git a/lib/fwu_updates/fwu_mtd.c b/lib/fwu_updates/fwu_mtd.c index 3137f8635c..3a9ad70203 100644 --- a/lib/fwu_updates/fwu_mtd.c +++ b/lib/fwu_updates/fwu_mtd.c @@ -84,11 +84,11 @@ int fwu_get_mtd_alt_num(efi_guid_t *image_id, int *alt_num, int gen_image_alt_info(char *buf, size_t len, int sidx, struct fwu_image_entry *img, struct mtd_info *mtd) {
char *p = buf, *end = buf + len;
char uuidbuf[UUID_STR_LEN + 1];
ofnode node, parts_node;
const char *suuid; 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),
@@ -145,7 +145,7 @@ int gen_image_alt_info(char *buf, size_t len, int sidx,
int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd) {
struct fwu_mdata *mdata;
struct fwu_mdata *mdata = NULL; int i, l, ret; ret = fwu_get_mdata(&mdata);
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py index 5bef84958b..93bc5ed44b 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py @@ -13,7 +13,6 @@ import pytest from capsule_defs import *
-@pytest.mark.boardspec('sandbox64') @pytest.mark.boardspec('sandbox_flattree') @pytest.mark.buildconfigspec('efi_capsule_firmware_fit') @pytest.mark.buildconfigspec('efi_capsule_on_disk') diff --git a/test/py/tests/test_fwu_updates/capsule_defs.py b/test/py/tests/test_fwu_updates/capsule_defs.py new file mode 100644 index 0000000000..59b40f11bd --- /dev/null +++ b/test/py/tests/test_fwu_updates/capsule_defs.py @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+
+# Directories +CAPSULE_DATA_DIR = '/EFI/CapsuleTestData' +CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule'
+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and +# you need build a newer version on your own. +# The path must terminate with '/' if it is not null. +EFITOOLS_PATH = '' diff --git a/test/py/tests/test_fwu_updates/conftest.py b/test/py/tests/test_fwu_updates/conftest.py new file mode 100644 index 0000000000..cdf824c3be --- /dev/null +++ b/test/py/tests/test_fwu_updates/conftest.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org
+import os +import os.path +import re +from subprocess import call, check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+# +# Fixture for UEFI capsule test +#
+@pytest.fixture(scope='session') +def efi_capsule_data(request, u_boot_config):
- """Set up a file system to be used in UEFI capsule and
authentication test.
- Args:
request: Pytest request object.
u_boot_config: U-boot configuration.
- Return:
A path to disk image to be used for testing
- """
- global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
- mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
- data_dir = mnt_point + CAPSULE_DATA_DIR
- install_dir = mnt_point + CAPSULE_INSTALL_DIR
- image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
- try:
# Create a target device
check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
check_call('rm -rf %s' % mnt_point, shell=True)
check_call('mkdir -p %s' % data_dir, shell=True)
check_call('mkdir -p %s' % install_dir, shell=True)
# Create capsule files
# two regions: one for u-boot.bin and the other for u-boot.env
check_call('cd %s; echo -n u-boot-a:Old > u-boot-a.bin.old; echo -n u-boot-env-a:Old > u-boot-env-a.old; echo -n u-boot-a:New > u-boot-a.bin.new; echo -n u-boot-env-a:New > u-boot-env-a.new; echo -n u-boot-b:Old > u-boot-b.bin.old; echo -n u-boot-env-b:Old > u-boot-env-b.old; echo -n u-boot-b:New > u-boot-b.bin.new; echo -n u-boot-env-b:New > u-boot-env-b.new;' % data_dir,
shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 0 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank0.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkfwumdata -i 2 -b 2 -a 1 -g af9e8c96-bec5-48be-9dab-3491c04b1366,09D7CF52-0720-4710-91D1-08469B7FE9C8,a8f61787-5d68-4c9d-9e4a-37bb0df99da7,52377abf-c4e4-4d0b-aafd-ba081a500847 af9e8c96-bec5-48be-9dab-3491c04b1366,5A7021F5-FEF2-48B4-AABA-832E777418C0,ea9d59af-e0e8-4ef5-9b16-4c80ff67524c,4e01d1fa-eebb-437e-9cfe-e7dfbcd04bb3 metadata_bank1.bin' %(data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-b.bin.new Test01' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-b.new Test02' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot-a.bin.new Test03' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot-env-a.new Test04' %
(data_dir, u_boot_config.build_dir),
shell=True)
# Create a disk image with EFI system partition
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
(mnt_point, image_path), shell=True)
check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' %
image_path, shell=True)
- except CalledProcessError as exception:
pytest.skip('Setup failed: %s' % exception.cmd)
return
- else:
yield image_path
- finally:
call('rm -rf %s' % mnt_point, shell=True)
call('rm -f %s' % image_path, shell=True)
call('rm -f ./spi.bin', shell=True)
diff --git a/test/py/tests/test_fwu_updates/test_fwu_updates.py b/test/py/tests/test_fwu_updates/test_fwu_updates.py new file mode 100644 index 0000000000..d9dff3afaf --- /dev/null +++ b/test/py/tests/test_fwu_updates/test_fwu_updates.py @@ -0,0 +1,367 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022, Linaro Limited +# +# FWU Multi Bank Firmware Update Test
+""" +This test verifies FWU Multi Bank firmware update for raw images +"""
+from subprocess import check_call, check_output, CalledProcessError +import pytest +from capsule_defs import *
+@pytest.mark.boardspec('sandbox64') +@pytest.mark.buildconfigspec('efi_capsule_firmware_raw') +@pytest.mark.buildconfigspec('efi_capsule_on_disk') +@pytest.mark.buildconfigspec('dfu') +@pytest.mark.buildconfigspec('dfu_sf') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('fwu_multi_bank_update') +@pytest.mark.buildconfigspec('dm_fwu_mdata') +@pytest.mark.buildconfigspec('fwu_mdata_mtd') +@pytest.mark.slow +class TestEfiCapsuleFirmwareRaw(object):
- def test_fwu_updates_fw1(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 1 - Update U-Boot Bank 1 binary on SPI Flash
0x100000-0x150000: U-Boot binary Bank 0 (but dummy)
0x150000-0x200000: U-Boot binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 1-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 $filesize',
'sf write 4000000 10000 $filesize'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 1-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
- def test_fwu_updates_fw2(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 2 - Update U-Boot and U-Boot Env Bank 1 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 2-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank0.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 2-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test01' in ''.join(output)
assert 'Test02' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:New' in ''.join(output)
- def test_fwu_updates_fw3(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
Test Case 3 - Update U-Boot and U-Boot Env Bank 0 binary on SPI Flash
0x100000-0x110000: U-Boot binary Bank 0 (but dummy)
0x120000-0x130000: U-Boot Env binary Bank 0 (but dummy)
0x140000-0x150000: U-Boot binary Bank 1 (but dummy)
0x160000-0x170000: U-Boot Env binary Bank 1 (but dummy)
"""
# other tests might have run and the
# system might not be in a clean state.
# Restart before starting the tests.
u_boot_console.restart_uboot()
disk_img = efi_capsule_data
with u_boot_console.log.section('Test Case 3-a, before reboot'):
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
'efidebug boot order 1',
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
'env save'])
# initialize contents
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/u-boot-a.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 100000 10',
'fatload host 0:1 4000000 %s/u-boot-env-a.old' % CAPSULE_DATA_DIR,
'sf write 4000000 120000 10',
'fatload host 0:1 4000000 %s/u-boot-b.bin.old' % CAPSULE_DATA_DIR,
'sf write 4000000 140000 10',
'fatload host 0:1 4000000 %s/u-boot-env-b.old' % CAPSULE_DATA_DIR,
'sf write 4000000 160000 10'])
output = u_boot_console.run_command_list([
'sf read 5000000 100000 10',
'md.b 5000000 10'
])
assert 'u-boot-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 120000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-a:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5100000 140000 10',
'md.b 5100000 10'
])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 5000000 160000 10',
'md.b 5000000 10'
])
assert 'u-boot-env-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'fatload host 0:1 4000000 %s/metadata_bank1.bin' % CAPSULE_DATA_DIR,
'sf write 4000000 0 100',
'sf write 4000000 10000 100'])
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x1' in ''.join(output)
assert 'previous_active_index: 0x0' in ''.join(output)
# place a capsule file
output = u_boot_console.run_command_list([
'fatload host 0:1 4000000 %s/Test03' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test03 $filesize' % CAPSULE_INSTALL_DIR,
'fatload host 0:1 4000000 %s/Test04' % CAPSULE_DATA_DIR,
'fatwrite host 0:1 4000000 %s/Test04 $filesize' % CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# reboot
u_boot_console.restart_uboot()
with u_boot_console.log.section('Test Case 3-b, after reboot'):
# make sure that dfu_alt_info exists even persistent variables
# are not available.
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
assert 'Test03' in ''.join(output)
assert 'Test04' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
'efidebug capsule disk-update', wait_for_reboot = True)
output = u_boot_console.run_command(
'fwu_mdata_read')
assert 'active_index: 0x0' in ''.join(output)
assert 'previous_active_index: 0x1' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 100000 10',
'md.b 4000000 10'])
assert 'u-boot-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
'sf read 4000000 120000 10',
'md.b 4000000 10'])
assert 'u-boot-env-a:New' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 140000 10',
'md.b 4000000 10'])
assert 'u-boot-b:Old' in ''.join(output)
output = u_boot_console.run_command_list([
'sf read 4000000 160000 10',
'md.b 4000000 10'])
assert 'u-boot-env-b:Old' in ''.join(output)
-- 2.25.1

Hi,
On 6/9/22 14:29, Sughosh Ganu wrote:
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.
Earlier, two separate patchsets were being sent. The patchset sent by me was adding support for the feature, and enabling the feature on the ST board. The other set of patches were being sent by Masami Hiramatsu, which were enabling the feature on the Synquacer platform. This patchset contains both set of patches, along with the associated documentation and the python test script for testing the feature.
The upstreaming effort for this feature had been put on a temporary hold to address the fixing of some issues in the capsule update code, primarily using a per platform image GUID for the updatable images. Now that the series has been merged, upstreaming effort for the FWU update feature is being resumed. Hence, this version does not have any review comments being addressed.
[1] - https://developer.arm.com/documentation/den0118/a [2] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e...
Jassi Brar (1): developerbox: synquacer: Use FIP as the updatable image
Masami Hiramatsu (9): FWU: Add FWU metadata access driver for non-GPT MTD devices dt/bindings: firmware: Add FWU metadata on MTD devices binding tools: Add mkfwumdata tool for FWU metadata image FWU: doc: Update documentation for the FWU non-GPT MTD synquacer: Update for TBBR (BL2) based new FIP layout FWU: synquacer: Add FWU Multi bank update support for DeveloperBox FWU: synquacer: Generate dfu_alt_info from devicetree partition doc: synquacer: Add how to enable FWU Multi Bank Update [TEMP]configs: synquacer: Add FWU support for DeveloperBox
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 FWU: doc: Add documentation for the FWU feature sandbox: fwu: Add support for testing FWU feature on sandbox
arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi | 7 + .../synquacer-sc2a11-developerbox-u-boot.dtsi | 40 +- arch/arm/mach-stm32mp/include/mach/stm32.h | 4 + arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 45 +- board/sandbox/sandbox.c | 49 ++ board/socionext/developerbox/Kconfig | 14 + board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 17 +- board/socionext/developerbox/fwu_plat.c | 200 ++++++++ board/st/stm32mp1/stm32mp1.c | 141 ++++++ cmd/Kconfig | 7 + cmd/Makefile | 1 + cmd/fwu_mdata.c | 74 +++ common/board_r.c | 5 + configs/sandbox64_defconfig | 12 +- configs/synquacer_developerbox_defconfig | 10 +- doc/board/socionext/developerbox.rst | 110 +++++ doc/develop/uefi/fwu_updates.rst | 200 ++++++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + .../firmware/fwu-mdata.txt | 18 + .../firmware/uboot,fwu-mdata-mtd.yaml | 38 ++ doc/mkeficapsule.1 | 29 +- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fwu-mdata/Kconfig | 24 + drivers/fwu-mdata/Makefile | 8 + drivers/fwu-mdata/fwu-mdata-uclass.c | 459 ++++++++++++++++++ drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 404 +++++++++++++++ drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++ include/configs/stm32mp15_common.h | 4 + include/configs/synquacer.h | 21 +- include/dm/uclass-id.h | 1 + include/fwu.h | 74 +++ 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 | 206 ++++++++ lib/fwu_updates/fwu_mtd.c | 173 +++++++ .../test_capsule_firmware_fit.py | 1 - .../py/tests/test_fwu_updates/capsule_defs.py | 10 + test/py/tests/test_fwu_updates/conftest.py | 78 +++ .../test_fwu_updates/test_fwu_updates.py | 367 ++++++++++++++ tools/Kconfig | 9 + tools/Makefile | 4 + tools/eficapsule.h | 8 + tools/mkeficapsule.c | 139 +++++- tools/mkfwumdata.c | 298 ++++++++++++ 53 files changed, 3903 insertions(+), 73 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c 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.txt create mode 100644 doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.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 drivers/fwu-mdata/fwu_mdata_mtd.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_mtd.c create mode 100644 test/py/tests/test_fwu_updates/capsule_defs.py create mode 100644 test/py/tests/test_fwu_updates/conftest.py create mode 100644 test/py/tests/test_fwu_updates/test_fwu_updates.py create mode 100644 tools/mkfwumdata.c
Minor remark for all the added files with SPDX-License-Identifier
"GPL-2.0+" is obsolete are replaced by "GPL-2.0-or-later"
Reference: _https://spdx.dev/ids/_ https://spdx.dev/ids/: *_GNU licenses_* For GNU licenses, do not use just the bare license ID, such as “GPL-2.0”. Instead, always use either the suffix “-only“ or the suffix “-or-later“ with GNU licenses.
regards
Patrick

hi,
On Mon, 20 Jun 2022 at 23:42, Patrick DELAUNAY patrick.delaunay@foss.st.com wrote:
Hi,
On 6/9/22 14:29, Sughosh Ganu wrote:
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.
Earlier, two separate patchsets were being sent. The patchset sent by me was adding support for the feature, and enabling the feature on the ST board. The other set of patches were being sent by Masami Hiramatsu, which were enabling the feature on the Synquacer platform. This patchset contains both set of patches, along with the associated documentation and the python test script for testing the feature.
The upstreaming effort for this feature had been put on a temporary hold to address the fixing of some issues in the capsule update code, primarily using a per platform image GUID for the updatable images. Now that the series has been merged, upstreaming effort for the FWU update feature is being resumed. Hence, this version does not have any review comments being addressed.
[1] - https://developer.arm.com/documentation/den0118/a [2] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e...
Jassi Brar (1): developerbox: synquacer: Use FIP as the updatable image
Masami Hiramatsu (9): FWU: Add FWU metadata access driver for non-GPT MTD devices dt/bindings: firmware: Add FWU metadata on MTD devices binding tools: Add mkfwumdata tool for FWU metadata image FWU: doc: Update documentation for the FWU non-GPT MTD synquacer: Update for TBBR (BL2) based new FIP layout FWU: synquacer: Add FWU Multi bank update support for DeveloperBox FWU: synquacer: Generate dfu_alt_info from devicetree partition doc: synquacer: Add how to enable FWU Multi Bank Update [TEMP]configs: synquacer: Add FWU support for DeveloperBox
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 FWU: doc: Add documentation for the FWU feature sandbox: fwu: Add support for testing FWU feature on sandbox
arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi | 7 + .../synquacer-sc2a11-developerbox-u-boot.dtsi | 40 +- arch/arm/mach-stm32mp/include/mach/stm32.h | 4 + arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 45 +- board/sandbox/sandbox.c | 49 ++ board/socionext/developerbox/Kconfig | 14 + board/socionext/developerbox/Makefile | 1 + board/socionext/developerbox/developerbox.c | 17 +- board/socionext/developerbox/fwu_plat.c | 200 ++++++++ board/st/stm32mp1/stm32mp1.c | 141 ++++++ cmd/Kconfig | 7 + cmd/Makefile | 1 + cmd/fwu_mdata.c | 74 +++ common/board_r.c | 5 + configs/sandbox64_defconfig | 12 +- configs/synquacer_developerbox_defconfig | 10 +- doc/board/socionext/developerbox.rst | 110 +++++ doc/develop/uefi/fwu_updates.rst | 200 ++++++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + .../firmware/fwu-mdata.txt | 18 + .../firmware/uboot,fwu-mdata-mtd.yaml | 38 ++ doc/mkeficapsule.1 | 29 +- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fwu-mdata/Kconfig | 24 + drivers/fwu-mdata/Makefile | 8 + drivers/fwu-mdata/fwu-mdata-uclass.c | 459 ++++++++++++++++++ drivers/fwu-mdata/fwu_mdata_gpt_blk.c | 404 +++++++++++++++ drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++ include/configs/stm32mp15_common.h | 4 + include/configs/synquacer.h | 21 +- include/dm/uclass-id.h | 1 + include/fwu.h | 74 +++ 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 | 206 ++++++++ lib/fwu_updates/fwu_mtd.c | 173 +++++++ .../test_capsule_firmware_fit.py | 1 - .../py/tests/test_fwu_updates/capsule_defs.py | 10 + test/py/tests/test_fwu_updates/conftest.py | 78 +++ .../test_fwu_updates/test_fwu_updates.py | 367 ++++++++++++++ tools/Kconfig | 9 + tools/Makefile | 4 + tools/eficapsule.h | 8 + tools/mkeficapsule.c | 139 +++++- tools/mkfwumdata.c | 298 ++++++++++++ 53 files changed, 3903 insertions(+), 73 deletions(-) create mode 100644 board/socionext/developerbox/fwu_plat.c 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.txt create mode 100644 doc/device-tree-bindings/firmware/uboot,fwu-mdata-mtd.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 drivers/fwu-mdata/fwu_mdata_mtd.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_mtd.c create mode 100644 test/py/tests/test_fwu_updates/capsule_defs.py create mode 100644 test/py/tests/test_fwu_updates/conftest.py create mode 100644 test/py/tests/test_fwu_updates/test_fwu_updates.py create mode 100644 tools/mkfwumdata.c
Minor remark for all the added files with SPDX-License-Identifier
"GPL-2.0+" is obsolete are replaced by "GPL-2.0-or-later"
Reference: _https://spdx.dev/ids/_ https://spdx.dev/ids/: *_GNU licenses_* For GNU licenses, do not use just the bare license ID, such as “GPL-2.0”. Instead, always use either the suffix “-only“ or the suffix “-or-later“ with GNU licenses.
Will change in the next version. Thanks.
-sughosh
regards
Patrick
participants (11)
-
Etienne Carriere
-
Heinrich Schuchardt
-
Ilias Apalodimas
-
Jassi Brar
-
Jassi Brar
-
Michal Simek
-
Michal Simek
-
Patrick DELAUNAY
-
Sughosh Ganu
-
Takahiro Akashi
-
Tom Rini