[PATCH v10 00/15] FWU: Add FWU Multi Bank Update feature support

The patchset adds support for the FWU Multi Bank Update[1] feature. Certain aspects of the Dependable Boot[2] specification have also been implemented.
The FWU multi bank update feature is used for supporting multiple sets(also called banks) of firmware image(s), allowing the platform to boot from a different bank, in case it fails to boot from the active bank. This functionality is supported by keeping the relevant information in a structure called metadata, which provides information on the images. Among other parameters, the metadata structure contains information on the currect active bank that is being used to boot image(s).
Functionality is being added to work with the UEFI capsule driver in u-boot. The metadata is read to gather information on the update bank, which is the bank to which the firmware images would be flashed to. On a successful completion of the update of all components, the active bank field in the metadata is updated, to reflect the bank from which the platform will boot on the subsequent boots.
Currently, the feature is being enabled on the STM32MP157C-DK2 and Synquacer boards. The DK2 board boots a FIP image from a uSD card partitioned with the GPT partioning scheme, while the Synquacer board boots a FIP image from a MTD partitioned SPI NOR flash device.
This feature also requires changes in a previous stage of bootloader, which parses the metadata and selects the bank to boot the image(s) from. Support has being added in tf-a(BL2 stage) for the STM32MP157C-DK2 board to boot the active bank images. These changes have been merged to the upstream tf-a repository.
The patch for adding a python test for the feature has been developed, and was sent in the version 5 of the patches[3]. However, the test script depends on adding support for the feature on MTD SPI NOR devices, and that is being done as part of the Synquacer patches. Hence these set of patches do not have the test script for the feature. That will be added through the patches for adding support for the feauture on Synquacer platform.
[1] - https://developer.arm.com/documentation/den0118/a [2] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e... [3] - https://lists.denx.de/pipermail/u-boot/2022-June/485992.html
Changes since V9: * Add a helper function fwu_get_dev_mdata() to get the FWU metadata device and read the metadata. * Rename fwu_get_image_alt_num() as fwu_get_image_image_index() based on suggestion from Takahiro. * s/formated/formatted in the commit message * Add a '\n' character in the log message * Remove the check board_is_stm32mp15x_dk2() as suggested by Etienne * Remove unneeded braces as suggested by Ilias * Change the fwu_plat_get_bootidx() function to take an uint * as the function parameter instead of the void * as suggested by Etienne. * Add a comment to indicate where the event notification will be called, as suggested by Simon. * Remove the unused variable active_idx, as suggested by Etienne * Move the global variables into local variables as suggested by Ilias. * Change fwu_get_image_alt_num() name to fwu_get_image_image_index() as suggested by Takahiro. * Allow capsule updates to be called from efi_init_obj_list() with the FWU feature enabled, as suggested by Takahiro. * Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature enabled. * Define the FWU feature related functions as __maybe_unused to allow for compilation with the FWU feature disabled. * Add a document for the command * Change the fwu_plat_get_bootidx() function to take an uint * as the function parameter instead of the void * as suggested by Etienne.
Sughosh Ganu (15): dt/bindings: Add bindings for GPT based 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: Add helper functions for accessing FWU metadata FWU: STM32MP1: Add support to read boot index from backup register event: Add an event for main_loop 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 test: dm: Add test cases for FWU Metadata uclass mkeficapsule: Add support for generating empty capsules mkeficapsule: Add support for setting OEM flags in capsule header FWU: doc: Add documentation for the FWU feature
arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi | 7 + arch/arm/mach-stm32mp/include/mach/stm32.h | 5 + arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 7 +- board/sandbox/sandbox.c | 8 + board/st/stm32mp1/stm32mp1.c | 44 ++ cmd/Kconfig | 6 + cmd/Makefile | 1 + cmd/fwu_mdata.c | 79 +++ common/board_r.c | 3 + common/event.c | 3 + configs/sandbox64_defconfig | 5 +- doc/develop/uefi/fwu_updates.rst | 165 +++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + .../firmware/fwu-mdata-gpt.yaml | 32 + doc/mkeficapsule.1 | 33 +- doc/usage/cmd/fwu_mdata.rst | 43 ++ doc/usage/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fwu-mdata/Kconfig | 16 + drivers/fwu-mdata/Makefile | 8 + drivers/fwu-mdata/fwu-mdata-uclass.c | 107 ++++ drivers/fwu-mdata/gpt_blk.c | 378 ++++++++++++ include/configs/stm32mp15_common.h | 4 + include/dm/uclass-id.h | 1 + include/event.h | 3 + include/fwu.h | 302 +++++++++ include/fwu_mdata.h | 67 ++ lib/Kconfig | 6 + lib/Makefile | 1 + lib/efi_loader/efi_capsule.c | 243 +++++++- lib/efi_loader/efi_setup.c | 1 + lib/fwu_updates/Kconfig | 33 + lib/fwu_updates/Makefile | 7 + lib/fwu_updates/fwu.c | 577 ++++++++++++++++++ lib/fwu_updates/fwu_gpt.c | 114 ++++ test/dm/Makefile | 1 + test/dm/fwu_mdata.c | 149 +++++ test/dm/fwu_mdata_disk_image.h | 112 ++++ .../test_capsule_firmware_fit.py | 1 - .../test_capsule_firmware_signed_fit.py | 1 - tools/Makefile | 2 +- tools/eficapsule.h | 8 + tools/mkeficapsule.c | 109 +++- 46 files changed, 2679 insertions(+), 26 deletions(-) create mode 100644 cmd/fwu_mdata.c create mode 100644 doc/develop/uefi/fwu_updates.rst create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-gpt.yaml create mode 100644 doc/usage/cmd/fwu_mdata.rst 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/gpt_blk.c create mode 100644 include/fwu.h create mode 100644 include/fwu_mdata.h create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile create mode 100644 lib/fwu_updates/fwu.c create mode 100644 lib/fwu_updates/fwu_gpt.c create mode 100644 test/dm/fwu_mdata.c create mode 100644 test/dm/fwu_mdata_disk_image.h

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 Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de Acked-by: Etienne Carriere etienne.carriere@linaro.org Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org --- Changes since V9: None
.../firmware/fwu-mdata-gpt.yaml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/fwu-mdata-gpt.yaml
diff --git a/doc/device-tree-bindings/firmware/fwu-mdata-gpt.yaml b/doc/device-tree-bindings/firmware/fwu-mdata-gpt.yaml new file mode 100644 index 0000000000..0735191ff1 --- /dev/null +++ b/doc/device-tree-bindings/firmware/fwu-mdata-gpt.yaml @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/firmware/fwu-mdata-gpt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: FWU metadata on device with GPT partitioned layout + +maintainers: + - Sughosh Ganu sughosh.ganu@linaro.org + +properties: + compatible: + items: + - const: u-boot,fwu-mdata-gpt + + fwu-mdata-store: + maxItems: 1 + description: Phandle of the device which contains the FWU medatata partition. + +required: + - compatible + - fwu-mdata-store + +additionalProperties: false + +examples: + - | + fwu-mdata { + compatible = "u-boot,fwu-mdata-gpt"; + fwu-mdata-store = <&sdmmc1>; + };

In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, which is stored on a dedicated partition. Add the metadata structure, and a driver model uclass which provides functions to access the metadata. These are generic API's, and implementations can be added based on parameters like how the metadata partition is accessed and what type of storage device houses the metadata.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Reviewed-by: Etienne Carriere etienne.carriere@linaro.org --- Changes since V9: * Add a helper function fwu_get_dev_mdata() to get the FWU metadata device and read the metadata. * Rename fwu_get_image_alt_num() as fwu_get_image_image_index() based on suggestion from Takahiro.
drivers/fwu-mdata/fwu-mdata-uclass.c | 107 +++++++++ include/dm/uclass-id.h | 1 + include/fwu.h | 214 +++++++++++++++++ include/fwu_mdata.h | 67 ++++++ lib/fwu_updates/fwu.c | 333 +++++++++++++++++++++++++++ 5 files changed, 722 insertions(+) create mode 100644 drivers/fwu-mdata/fwu-mdata-uclass.c create mode 100644 include/fwu.h create mode 100644 include/fwu_mdata.h create mode 100644 lib/fwu_updates/fwu.c
diff --git a/drivers/fwu-mdata/fwu-mdata-uclass.c b/drivers/fwu-mdata/fwu-mdata-uclass.c new file mode 100644 index 0000000000..65ae93c21f --- /dev/null +++ b/drivers/fwu-mdata/fwu-mdata-uclass.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, Linaro Limited + */ + +#define LOG_CATEGORY UCLASS_FWU_MDATA + +#include <common.h> +#include <dm.h> +#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> + +#include <linux/errno.h> +#include <linux/types.h> +#include <u-boot/crc.h> + +/** + * fwu_mdata_check() - Check if the FWU metadata is valid + * @dev: FWU metadata device + * + * 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(struct udevice *dev) +{ + const struct fwu_mdata_ops *ops = device_get_ops(dev); + + if (!ops->mdata_check) { + log_debug("mdata_check() method not defined\n"); + return -ENOSYS; + } + + return ops->mdata_check(dev); +} + +/** + * fwu_get_mdata() - Get a FWU metadata copy + * @dev: FWU metadata device + * @mdata: Copy of the FWU metadata + * + * Get a valid copy of the FWU metadata. + * + * Note: This function is to be called first when modifying any fields + * in the metadata. The sequence of calls to modify any field in the + * metadata would be 1) fwu_get_mdata 2) Modify metadata, followed by + * 3) fwu_update_mdata + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_get_mdata(struct udevice *dev, struct fwu_mdata *mdata) +{ + const struct fwu_mdata_ops *ops = device_get_ops(dev); + + if (!ops->get_mdata) { + log_debug("get_mdata() method not defined\n"); + return -ENOSYS; + } + + return ops->get_mdata(dev, mdata); +} + +/** + * fwu_update_mdata() - Update the FWU metadata + * @dev: FWU metadata device + * @mdata: Copy of the FWU metadata + * + * Update the FWU metadata structure by writing to the + * FWU metadata partitions. + * + * Note: This function is not to be called directly to update the + * metadata fields. The sequence of function calls should be + * 1) fwu_get_mdata() 2) Modify the medata fields 3) fwu_update_mdata() + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_update_mdata(struct udevice *dev, struct fwu_mdata *mdata) +{ + void *buf; + const struct fwu_mdata_ops *ops = device_get_ops(dev); + + if (!ops->update_mdata) { + log_debug("get_mdata() method not defined\n"); + return -ENOSYS; + } + + /* + * Calculate the crc32 for the updated FWU metadata + * and put the updated value in the FWU metadata crc32 + * field + */ + buf = &mdata->version; + mdata->crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32)); + + return ops->update_mdata(dev, mdata); +} + +UCLASS_DRIVER(fwu_mdata) = { + .id = UCLASS_FWU_MDATA, + .name = "fwu-mdata", +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..598a8c10a0 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -58,6 +58,7 @@ enum uclass_id { UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ + UCLASS_FWU_MDATA, /* FWU Metadata Access */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ UCLASS_HASH, /* Hash device */ UCLASS_HWSPINLOCK, /* Hardware semaphores */ diff --git a/include/fwu.h b/include/fwu.h new file mode 100644 index 0000000000..745f6225d0 --- /dev/null +++ b/include/fwu.h @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022, Linaro Limited + */ + +#if !defined _FWU_H_ +#define _FWU_H_ + +#include <blk.h> +#include <efi.h> + +#include <linux/types.h> + +struct fwu_mdata; +struct udevice; + +/** + * @mdata_check: check the validity of the FWU metadata partitions + * @get_mdata() - Get a FWU metadata copy + * @update_mdata() - Update the FWU metadata copy + */ +struct fwu_mdata_ops { + /** + * mdata_check() - Check if the FWU metadata is valid + * @dev: FWU device + * + * 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 (*mdata_check)(struct udevice *dev); + + /** + * get_mdata() - Get a FWU metadata copy + * @dev: FWU device + * @mdata: Pointer to FWU metadata + * + * Get a valid copy of the FWU metadata. + * + * Return: 0 if OK, -ve on error + */ + int (*get_mdata)(struct udevice *dev, struct fwu_mdata *mdata); + + /** + * fwu_update_mdata() - Update the FWU metadata + * @dev: FWU device + * @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 (*update_mdata)(struct udevice *dev, struct fwu_mdata *mdata); +}; + +#define FWU_MDATA_VERSION 0x1 + +/* +* GUID value defined in the FWU specification for identification +* of the FWU metadata partition. +*/ +#define FWU_MDATA_GUID \ + EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ + 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23) + +/** + * fwu_get_mdata() - Get a FWU metadata copy + * @dev: FWU metadata device + * @mdata: Copy of the FWU metadata + * + * Get a valid copy of the FWU metadata. + * + * Note: This function is to be called first when modifying any fields + * in the metadata. The sequence of calls to modify any field in the + * metadata would be 1) fwu_get_mdata 2) Modify metadata, followed by + * 3) fwu_update_mdata + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_get_mdata(struct udevice *dev, struct fwu_mdata *mdata); + +/** + * fwu_update_mdata() - Update the FWU metadata + * @dev: FWU metadata device + * @mdata: Copy of the FWU metadata + * + * Update the FWU metadata structure by writing to the + * FWU metadata partitions. + * + * Note: This function is not to be called directly to update the + * metadata fields. The sequence of function calls should be + * 1) fwu_get_mdata() 2) Modify the medata fields 3) fwu_update_mdata() + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_update_mdata(struct udevice *dev, struct fwu_mdata *mdata); + +/** + * fwu_get_active_index() - Get active_index from the FWU metadata + * @active_idxp: 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_idxp); + +/** + * 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(uint active_idx); + +/** + * fwu_get_image_index() - Get the Image Index 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 + * @image_index: The Image Index 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 Image Index value which will be used for the + * capsule update. + * + * Currently, the capsule update driver uses the DFU framework for + * the updates. This function gets the DFU alt number which is to + * be used as the Image Index + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_get_image_index(efi_guid_t *image_type_id, u32 update_bank, + u8 *image_index); + +/** + * fwu_mdata_check() - Check if the FWU metadata is valid + * @dev: FWU metadata device + * + * 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(struct udevice *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); + +/** + * 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); + +/** + * 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); + +/** + * 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); + +#endif /* _FWU_H_ */ diff --git a/include/fwu_mdata.h b/include/fwu_mdata.h new file mode 100644 index 0000000000..8fda4f4ac2 --- /dev/null +++ b/include/fwu_mdata.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022, Linaro Limited + */ + +#if !defined _FWU_MDATA_H_ +#define _FWU_MDATA_H_ + +#include <efi.h> + +/** + * struct fwu_image_bank_info - firmware image information + * @image_uuid: Guid value of the image in this bank + * @accepted: Acceptance status of the image + * @reserved: Reserved + * + * The structure contains image specific fields which are + * used to identify the image and to specify the image's + * acceptance status + */ +struct fwu_image_bank_info { + efi_guid_t image_uuid; + uint32_t accepted; + uint32_t reserved; +}; + +/** + * struct fwu_image_entry - information for a particular type of image + * @image_type_uuid: Guid value for identifying the image type + * @location_uuid: Guid of the storage volume where the image is located + * @img_bank_info: Array containing properties of images + * + * This structure contains information on various types of updatable + * firmware images. Each image type then contains an array of image + * information per bank. + */ +struct fwu_image_entry { + efi_guid_t image_type_uuid; + efi_guid_t location_uuid; + struct fwu_image_bank_info img_bank_info[CONFIG_FWU_NUM_BANKS]; +}; + +/** + * struct fwu_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]; +}; + +#endif /* _FWU_MDATA_H_ */ diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c new file mode 100644 index 0000000000..fb9dbca307 --- /dev/null +++ b/lib/fwu_updates/fwu.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, Linaro Limited + */ + +#include <dm.h> +#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.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_mdata(struct udevice **dev, struct fwu_mdata *mdata) + +{ + int ret; + + ret = uclass_first_device(UCLASS_FWU_MDATA, dev); + if (ret) { + log_debug("Cannot find fwu device\n"); + return ret; + } + + ret = fwu_get_mdata(*dev, mdata); + if (ret < 0) + log_debug("Unable to get valid FWU metadata\n"); + + return ret; +} + +/** + * 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_debug("crc32 check failed for %s FWU metadata partition\n", + pri_part ? "primary" : "secondary"); + return -EINVAL; + } + + 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 udevice *dev; + struct fwu_mdata mdata = { 0 }; + + ret = fwu_get_dev_mdata(&dev, &mdata); + if (ret) + return ret; + + /* + * Found the FWU metadata partition, now read the active_index + * value + */ + *active_idx = mdata.active_index; + if (*active_idx >= CONFIG_FWU_NUM_BANKS) { + log_debug("Active index value read is incorrect\n"); + ret = -EINVAL; + } + + 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(uint active_idx) +{ + int ret; + struct udevice *dev; + struct fwu_mdata mdata = { 0 }; + + if (active_idx >= CONFIG_FWU_NUM_BANKS) { + log_debug("Invalid active index value\n"); + return -EINVAL; + } + + ret = fwu_get_dev_mdata(&dev, &mdata); + if (ret) + return ret; + + /* + * 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(dev, &mdata); + if (ret < 0) { + log_debug("Failed to update FWU metadata partitions\n"); + ret = -EIO; + } + + return ret; +} + +/** + * fwu_get_image_index() - Get the Image Index 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 + * @image_index: The Image Index 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 Image Index value which will be used for the + * capsule update. + * + * Currently, the capsule update driver uses the DFU framework for + * the updates. This function gets the DFU alt number which is to + * be used as the Image Index + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_get_image_index(efi_guid_t *image_type_id, u32 update_bank, + u8 *image_index) +{ + int ret, i; + efi_guid_t *image_guid; + struct udevice *dev; + struct fwu_mdata mdata = { 0 }; + struct fwu_image_entry *img_entry; + struct fwu_image_bank_info *img_bank_info; + + ret = fwu_get_dev_mdata(&dev, &mdata); + if (ret) + return ret; + + ret = -EINVAL; + /* + * The FWU metadata has been read. Now get the image_uuid for the + * image with the update_bank. + */ + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { + if (!guidcmp(image_type_id, + &mdata.img_entry[i].image_type_uuid)) { + img_entry = &mdata.img_entry[i]; + img_bank_info = &img_entry->img_bank_info[update_bank]; + image_guid = &img_bank_info->image_uuid; + ret = fwu_plat_get_alt_num(dev, image_guid, image_index); + if (ret) { + log_debug("alt_num not found for partition with GUID %pUs\n", + image_guid); + } else { + log_debug("alt_num %d for partition %pUs\n", + *image_index, image_guid); + ++*image_index; + } + + goto out; + } + } + + log_debug("Partition with the image type %pUs not found\n", + image_type_id); + +out: + return ret; +} + +/** + * 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 udevice *dev; + struct fwu_mdata mdata = { 0 }; + + ret = fwu_get_dev_mdata(&dev, &mdata); + if (ret) + return ret; + + /* + * 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(dev, &mdata); + if (ret < 0) { + log_debug("Failed to update FWU metadata partitions\n"); + ret = -EIO; + } + + return ret; +} + +/** + * fwu_clrset_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_clrset_image_accept(efi_guid_t *img_type_id, u32 bank, u8 action) +{ + int ret, i; + struct udevice *dev; + struct fwu_mdata mdata = { 0 }; + struct fwu_image_entry *img_entry; + struct fwu_image_bank_info *img_bank_info; + + ret = fwu_get_dev_mdata(&dev, &mdata); + if (ret) + return ret; + + img_entry = &mdata.img_entry[0]; + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; 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(dev, &mdata); + goto out; + } + } + + /* Image not found */ + ret = -ENOENT; + +out: + 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_clrset_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_clrset_image_accept(img_type_id, bank, + IMAGE_ACCEPT_CLEAR); +}

On Thu, 15 Sept 2022 at 03:15, Sughosh Ganu sughosh.ganu@linaro.org wrote: ....
+/**
- fwu_get_active_index() - Get active_index from the FWU metadata
- @active_idxp: 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_idxp);
Bank index is u32 here and uint in fwu_update_active_index(), so s/u32/uint ?
cheers.

On Mon, 19 Sept 2022 at 06:03, Jassi Brar jaswinder.singh@linaro.org wrote:
On Thu, 15 Sept 2022 at 03:15, Sughosh Ganu sughosh.ganu@linaro.org wrote: ....
+/**
- fwu_get_active_index() - Get active_index from the FWU metadata
- @active_idxp: 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_idxp);
Bank index is u32 here and uint in fwu_update_active_index(), so s/u32/uint ?
Will change. Thanks.
-sughosh

On Thu, Sep 15, 2022 at 3:15 AM Sughosh Ganu sughosh.ganu@linaro.org wrote: ....
+/**
- @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 {
/**
* mdata_check() - Check if the FWU metadata is valid
* @dev: FWU device
*
* 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 (*mdata_check)(struct udevice *dev);
Like get_mdata and update_mdata, maybe check_mdata too ?
.....
+/**
- fwu_get_active_index() - Get active_index from the FWU metadata
- @active_idxp: 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_idxp);
+/**
- 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(uint active_idx);
maybe fwu_set_active_index ? just like fwu_get_active_index
.....
+/**
- 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 udevice *dev;
struct fwu_mdata mdata = { 0 };
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
/*
* 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;
This may cause problems. We are reverting because active_index does not work, and here we set it to previous_active_index which is supposed to mean "last good index". Also this logic assumes a 2-banks setup, and is obviously incorrect for >2 banks where the previous_active_index should point to "boot_index minus 2" bank (but of course there is no guarantee that that bank is preserved still). So either previous_active_index be left changed OR we also copy the previous bank to active bank before the swap.
.....
+/**
- 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_clrset_image_accept(img_type_id, bank,
IMAGE_ACCEPT_SET);
+}
+/**
- fwu_clear_accept_image() - Clear the Acceptance bit for the image
Something more consistent like fwu_image_accepted_clear() and fwu_image_accepted_set() ?
cheers.

On Mon, 26 Sept 2022 at 08:28, Jassi Brar jassisinghbrar@gmail.com wrote:
On Thu, Sep 15, 2022 at 3:15 AM Sughosh Ganu sughosh.ganu@linaro.org wrote: ....
+/**
- @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 {
/**
* mdata_check() - Check if the FWU metadata is valid
* @dev: FWU device
*
* 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 (*mdata_check)(struct udevice *dev);
Like get_mdata and update_mdata, maybe check_mdata too ?
Okay
.....
+/**
- fwu_get_active_index() - Get active_index from the FWU metadata
- @active_idxp: 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_idxp);
+/**
- 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(uint active_idx);
maybe fwu_set_active_index ? just like fwu_get_active_index
Okay
.....
+/**
- 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 udevice *dev;
struct fwu_mdata mdata = { 0 };
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
/*
* 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;
This may cause problems. We are reverting because active_index does not work, and here we set it to previous_active_index which is supposed to mean "last good index". Also this logic assumes a 2-banks setup, and is obviously incorrect for >2 banks where the previous_active_index should point to "boot_index minus 2" bank (but of course there is no guarantee that that bank is preserved still). So either previous_active_index be left changed OR we also copy the previous bank to active bank before the swap.
Sorry, but I don't understand the review comment here. Even in the case of num_banks > 2, this function is simply using the previous_active_index value. It does not care what the previous_active_index value is. If you remember, the setting of the update bank is really a platform function(fwu_plat_get_update_index()). A platform can set any bank number as the update bank. So we cannot tell what the value of the previous_active_index will be. All that this function does is use the previous_active_index as the partition/bank to boot from in the subsequent boot cycle.
.....
+/**
- 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_clrset_image_accept(img_type_id, bank,
IMAGE_ACCEPT_SET);
+}
+/**
- fwu_clear_accept_image() - Clear the Acceptance bit for the image
Something more consistent like fwu_image_accepted_clear() and fwu_image_accepted_set() ?
Umm, the other related API is fwu_accept_image, and this is clearing the accept bit, hence the name. If you don't feel strongly about this, I would prefer the current name.
-sughosh

On Mon, Sep 26, 2022 at 5:00 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:28, Jassi Brar jassisinghbrar@gmail.com wrote:
.....
+/**
- 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 udevice *dev;
struct fwu_mdata mdata = { 0 };
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
/*
* 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;
This may cause problems. We are reverting because active_index does not work, and here we set it to previous_active_index which is supposed to mean "last good index". Also this logic assumes a 2-banks setup, and is obviously incorrect for >2 banks where the previous_active_index should point to "boot_index minus 2" bank (but of course there is no guarantee that that bank is preserved still). So either previous_active_index be left changed OR we also copy the previous bank to active bank before the swap.
Sorry, but I don't understand the review comment here. Even in the case of num_banks > 2, this function is simply using the previous_active_index value. It does not care what the previous_active_index value is. If you remember, the setting of the update bank is really a platform function(fwu_plat_get_update_index()). A platform can set any bank number as the update bank. So we cannot tell what the value of the previous_active_index will be.
Do you remember you pick update_bank in a circular-buffer manner in fwu_plat_get_update_index() ? But don't even bother the >2 banks.
Consider the simple 2-banks platform.... Initially: active_index = 1 previous_active_index = 0
After update and before reboot active_index = 0 <<<< updated bank 0 previous_active_index = 1
After reboot, for some reason update fails (reject bank0) and we call fwu_revert_boot_index() active_index = 1 <<< good previous_active_index = 0 <<<< points to unbootable bank
Which may be seen as inconsistency if we assume previous_bank to always contain a bootable set of images. So we also need to copy bank1 into bank0 as part of the revert (at least as a backup for reasons other than a/b update failure).
All that this function does is use the previous_active_index as the partition/bank to boot from in the subsequent boot cycle.
That is, you assume the previous_active_index bank contains working images.
.....
+/**
- 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_clrset_image_accept(img_type_id, bank,
IMAGE_ACCEPT_SET);
+}
+/**
- fwu_clear_accept_image() - Clear the Acceptance bit for the image
Something more consistent like fwu_image_accepted_clear() and fwu_image_accepted_set() ?
Umm, the other related API is fwu_accept_image, and this is clearing the accept bit, hence the name. If you don't feel strongly about this, I would prefer the current name.
fwu_accept_image() and fwu_clear_accept_image() don't seem like a pair.... is all I say.
cheers.

On Mon, 26 Sept 2022 at 20:12, Jassi Brar jassisinghbrar@gmail.com wrote:
On Mon, Sep 26, 2022 at 5:00 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:28, Jassi Brar jassisinghbrar@gmail.com wrote:
.....
+/**
- 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 udevice *dev;
struct fwu_mdata mdata = { 0 };
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
/*
* 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;
This may cause problems. We are reverting because active_index does not work, and here we set it to previous_active_index which is supposed to mean "last good index". Also this logic assumes a 2-banks setup, and is obviously incorrect for >2 banks where the previous_active_index should point to "boot_index minus 2" bank (but of course there is no guarantee that that bank is preserved still). So either previous_active_index be left changed OR we also copy the previous bank to active bank before the swap.
Sorry, but I don't understand the review comment here. Even in the case of num_banks > 2, this function is simply using the previous_active_index value. It does not care what the previous_active_index value is. If you remember, the setting of the update bank is really a platform function(fwu_plat_get_update_index()). A platform can set any bank number as the update bank. So we cannot tell what the value of the previous_active_index will be.
Do you remember you pick update_bank in a circular-buffer manner in fwu_plat_get_update_index() ? But don't even bother the >2 banks.
Consider the simple 2-banks platform.... Initially: active_index = 1 previous_active_index = 0
After update and before reboot active_index = 0 <<<< updated bank 0 previous_active_index = 1
After reboot, for some reason update fails (reject bank0) and we call fwu_revert_boot_index() active_index = 1 <<< good previous_active_index = 0 <<<< points to unbootable bank
Which may be seen as inconsistency if we assume previous_bank to always contain a bootable set of images. So we also need to copy bank1 into bank0 as part of the revert (at least as a backup for reasons other than a/b update failure).
If the platform owner wants to restore a particular bank with good images, the procedure to update that bank needs to be followed just like it was any other update. If an updated bank fails the image acceptance test, the following boot would be from the previous_active_bank. In that case, the other bank needs to be updated by explicitly putting capsules in the ESP and initiating the update.
All that this function does is use the previous_active_index as the partition/bank to boot from in the subsequent boot cycle.
That is, you assume the previous_active_index bank contains working images.
It is the responsibility of the platform owner to ensure that all partitions have valid images. For e.g. a platform which implements rollback protection would have this scenario in the case where, after updating a bank with images, the platform's rollback counter is incremented. This would mean that the previous_active_index(all other banks) no longer contains valid/bootable images. In such a scenario, the platform owner would have to ensure that all banks are updated with valid images. It is not the role to be played by the update code to identify this situation. The FWU code is simply for updating the images which are part of capsules to the update bank.
-sughosh
.....
+/**
- 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_clrset_image_accept(img_type_id, bank,
IMAGE_ACCEPT_SET);
+}
+/**
- fwu_clear_accept_image() - Clear the Acceptance bit for the image
Something more consistent like fwu_image_accepted_clear() and fwu_image_accepted_set() ?
Umm, the other related API is fwu_accept_image, and this is clearing the accept bit, hence the name. If you don't feel strongly about this, I would prefer the current name.
fwu_accept_image() and fwu_clear_accept_image() don't seem like a pair.... is all I say.
cheers.

On Tue, Sep 27, 2022 at 2:14 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 20:12, Jassi Brar jassisinghbrar@gmail.com wrote:
On Mon, Sep 26, 2022 at 5:00 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:28, Jassi Brar jassisinghbrar@gmail.com wrote:
.....
+/**
- 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 udevice *dev;
struct fwu_mdata mdata = { 0 };
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
/*
* 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;
This may cause problems. We are reverting because active_index does not work, and here we set it to previous_active_index which is supposed to mean "last good index". Also this logic assumes a 2-banks setup, and is obviously incorrect for >2 banks where the previous_active_index should point to "boot_index minus 2" bank (but of course there is no guarantee that that bank is preserved still). So either previous_active_index be left changed OR we also copy the previous bank to active bank before the swap.
Sorry, but I don't understand the review comment here. Even in the case of num_banks > 2, this function is simply using the previous_active_index value. It does not care what the previous_active_index value is. If you remember, the setting of the update bank is really a platform function(fwu_plat_get_update_index()). A platform can set any bank number as the update bank. So we cannot tell what the value of the previous_active_index will be.
Do you remember you pick update_bank in a circular-buffer manner in fwu_plat_get_update_index() ? But don't even bother the >2 banks.
Consider the simple 2-banks platform.... Initially: active_index = 1 previous_active_index = 0
After update and before reboot active_index = 0 <<<< updated bank 0 previous_active_index = 1
After reboot, for some reason update fails (reject bank0) and we call fwu_revert_boot_index() active_index = 1 <<< good previous_active_index = 0 <<<< points to unbootable bank
Which may be seen as inconsistency if we assume previous_bank to always contain a bootable set of images. So we also need to copy bank1 into bank0 as part of the revert (at least as a backup for reasons other than a/b update failure).
If the platform owner wants to restore a particular bank with good images, the procedure to update that bank needs to be followed just like it was any other update.
The banks are under FWU and the platform has (should have) no control over which bank the image goes in.
If an updated bank fails the image acceptance test, the following boot would be from the previous_active_bank. In that case, the other bank needs to be updated by explicitly putting capsules in the ESP and initiating the update.
Which capsule - the one that just failed or the previous one (which may not be available/provided)? Doesn't simply copying over the bank make more sense?
All that this function does is use the previous_active_index as the partition/bank to boot from in the subsequent boot cycle.
That is, you assume the previous_active_index bank contains working images.
It is the responsibility of the platform owner to ensure that all partitions have valid images.
I differ. The platform should not be modifying banks and meta-data beneath the fwu framework. The specification says "A Client can only read from or write to images in the update bank"
-j

On Tue, 27 Sept 2022 at 21:55, Jassi Brar jassisinghbrar@gmail.com wrote:
On Tue, Sep 27, 2022 at 2:14 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 20:12, Jassi Brar jassisinghbrar@gmail.com wrote:
On Mon, Sep 26, 2022 at 5:00 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:28, Jassi Brar jassisinghbrar@gmail.com wrote:
.....
+/**
- 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 udevice *dev;
struct fwu_mdata mdata = { 0 };
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
/*
* 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;
This may cause problems. We are reverting because active_index does not work, and here we set it to previous_active_index which is supposed to mean "last good index". Also this logic assumes a 2-banks setup, and is obviously incorrect for >2 banks where the previous_active_index should point to "boot_index minus 2" bank (but of course there is no guarantee that that bank is preserved still). So either previous_active_index be left changed OR we also copy the previous bank to active bank before the swap.
Sorry, but I don't understand the review comment here. Even in the case of num_banks > 2, this function is simply using the previous_active_index value. It does not care what the previous_active_index value is. If you remember, the setting of the update bank is really a platform function(fwu_plat_get_update_index()). A platform can set any bank number as the update bank. So we cannot tell what the value of the previous_active_index will be.
Do you remember you pick update_bank in a circular-buffer manner in fwu_plat_get_update_index() ? But don't even bother the >2 banks.
Consider the simple 2-banks platform.... Initially: active_index = 1 previous_active_index = 0
After update and before reboot active_index = 0 <<<< updated bank 0 previous_active_index = 1
After reboot, for some reason update fails (reject bank0) and we call fwu_revert_boot_index() active_index = 1 <<< good previous_active_index = 0 <<<< points to unbootable bank
Which may be seen as inconsistency if we assume previous_bank to always contain a bootable set of images. So we also need to copy bank1 into bank0 as part of the revert (at least as a backup for reasons other than a/b update failure).
If the platform owner wants to restore a particular bank with good images, the procedure to update that bank needs to be followed just like it was any other update.
The banks are under FWU and the platform has (should have) no control over which bank the image goes in.
It is the platform code that can decide on how the update banks are selected. Even in the case of num_banks > 2, the platform can have a round robin selection of the update banks through which all the other banks can be restored/updated. And currently, the default function for selection of the update bank does use round robin selection of the banks.
If an updated bank fails the image acceptance test, the following boot would be from the previous_active_bank. In that case, the other bank needs to be updated by explicitly putting capsules in the ESP and initiating the update.
Which capsule - the one that just failed or the previous one (which may not be available/provided)?
One with working images. Please note that in the normal flow, a user would have tested the images locally before getting them deployed.
Doesn't simply copying over the bank make more sense?
When we are talking about "simply copying", I hope you appreciate that 1) this(restoration of the banks) is not under the purview of the FWU spec and 2) it will need some thought on how to have a generic implementation which fetches images from the good bank and then writes them over to the other banks. The implementation of the FWU spec requires using the UEFI capsule update interface for the updates. This would require some thought on how to use the FMP functions to copy over the images to other banks in a generic way. But please note that even in case of num_banks > 2, this can be done through the normal capsule update flow if the platform implements a round robin update bank selection.
All that this function does is use the previous_active_index as the partition/bank to boot from in the subsequent boot cycle.
That is, you assume the previous_active_index bank contains working images.
It is the responsibility of the platform owner to ensure that all partitions have valid images.
I differ. The platform should not be modifying banks and meta-data beneath the fwu framework. The specification says "A Client can only read from or write to images in the update bank"
I did not say modifying the banks through a parallel mechanism. Like I mentioned above, this can be done through the normal update flow.
-sughosh

On Wed, Sep 28, 2022 at 1:00 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Tue, 27 Sept 2022 at 21:55, Jassi Brar jassisinghbrar@gmail.com wrote:
On Tue, Sep 27, 2022 at 2:14 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 20:12, Jassi Brar jassisinghbrar@gmail.com wrote:
On Mon, Sep 26, 2022 at 5:00 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:28, Jassi Brar jassisinghbrar@gmail.com wrote:
..... > +/** > + * 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 udevice *dev; > + struct fwu_mdata mdata = { 0 }; > + > + ret = fwu_get_dev_mdata(&dev, &mdata); > + if (ret) > + return ret; > + > + /* > + * 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; > This may cause problems. We are reverting because active_index does not work, and here we set it to previous_active_index which is supposed to mean "last good index". Also this logic assumes a 2-banks setup, and is obviously incorrect for >2 banks where the previous_active_index should point to "boot_index minus 2" bank (but of course there is no guarantee that that bank is preserved still). So either previous_active_index be left changed OR we also copy the previous bank to active bank before the swap.
Sorry, but I don't understand the review comment here. Even in the case of num_banks > 2, this function is simply using the previous_active_index value. It does not care what the previous_active_index value is. If you remember, the setting of the update bank is really a platform function(fwu_plat_get_update_index()). A platform can set any bank number as the update bank. So we cannot tell what the value of the previous_active_index will be.
Do you remember you pick update_bank in a circular-buffer manner in fwu_plat_get_update_index() ? But don't even bother the >2 banks.
Consider the simple 2-banks platform.... Initially: active_index = 1 previous_active_index = 0
After update and before reboot active_index = 0 <<<< updated bank 0 previous_active_index = 1
After reboot, for some reason update fails (reject bank0) and we call fwu_revert_boot_index() active_index = 1 <<< good previous_active_index = 0 <<<< points to unbootable bank
Which may be seen as inconsistency if we assume previous_bank to always contain a bootable set of images. So we also need to copy bank1 into bank0 as part of the revert (at least as a backup for reasons other than a/b update failure).
If the platform owner wants to restore a particular bank with good images, the procedure to update that bank needs to be followed just like it was any other update.
The banks are under FWU and the platform has (should have) no control over which bank the image goes in.
It is the platform code that can decide on how the update banks are selected. Even in the case of num_banks > 2, the platform can have a round robin selection of the update banks through which all the other banks can be restored/updated. And currently, the default function for selection of the update bank does use round robin selection of the banks.
Platform only reads from and writes to a bank number given by the fwu. Platform has no means to sense if a bank is accepted or rejected. Or is there?
If an updated bank fails the image acceptance test, the following boot would be from the previous_active_bank. In that case, the other bank needs to be updated by explicitly putting capsules in the ESP and initiating the update.
Which capsule - the one that just failed or the previous one (which may not be available/provided)?
One with working images. Please note that in the normal flow, a user would have tested the images locally before getting them deployed.
In the "normal flow" of the world many industries shouldn't exist - security, anti-virus, customer-support, repair etc :)
Doesn't simply copying over the bank make more sense?
When we are talking about "simply copying", I hope you appreciate that
- this(restoration of the banks) is not under the purview of the FWU
spec
I think it is. The restoration is part of the management of the banks... just like FWU makes sure metadata replicas are always in sync.
2) it will need some thought on how to have a generic
implementation which fetches images from the good bank and then writes them over to the other banks.
Not from "good bank" to "other banks". FWU only needs to copy from previous_bank to active_bank before swapping their pointers in fwu_revert_boot_index() maybe by a platform specific callback copy_bank(int from, int to) ?
The implementation of the FWU spec requires using the UEFI capsule update interface for the updates.
The spec actually expects FWU to reside in BL32, but here we are implementing it in BL33 (I know we had to). IMO, always keeping a good previous_bank is not even a violation of the spec, it is internal to the implementation.
But ok. I don't want to be the one to slow down the uboot's fwu progress. We can always implement it later when more people see the need.
-jassi

On Thu, 29 Sept 2022 at 00:59, Jassi Brar jassisinghbrar@gmail.com wrote:
On Wed, Sep 28, 2022 at 1:00 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Tue, 27 Sept 2022 at 21:55, Jassi Brar jassisinghbrar@gmail.com wrote:
On Tue, Sep 27, 2022 at 2:14 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 20:12, Jassi Brar jassisinghbrar@gmail.com wrote:
On Mon, Sep 26, 2022 at 5:00 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:28, Jassi Brar jassisinghbrar@gmail.com wrote: >
> > ..... > > +/** > > + * 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 udevice *dev; > > + struct fwu_mdata mdata = { 0 }; > > + > > + ret = fwu_get_dev_mdata(&dev, &mdata); > > + if (ret) > > + return ret; > > + > > + /* > > + * 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; > > > This may cause problems. > We are reverting because active_index does not work, and here we set > it to previous_active_index which is supposed to mean "last good > index". > Also this logic assumes a 2-banks setup, and is obviously incorrect > for >2 banks where the previous_active_index should point to > "boot_index minus 2" bank (but of course there is no guarantee that > that bank is preserved still). > So either previous_active_index be left changed OR we also copy the > previous bank to active bank before the swap.
Sorry, but I don't understand the review comment here. Even in the case of num_banks > 2, this function is simply using the previous_active_index value. It does not care what the previous_active_index value is. If you remember, the setting of the update bank is really a platform function(fwu_plat_get_update_index()). A platform can set any bank number as the update bank. So we cannot tell what the value of the previous_active_index will be.
Do you remember you pick update_bank in a circular-buffer manner in fwu_plat_get_update_index() ? But don't even bother the >2 banks.
Consider the simple 2-banks platform.... Initially: active_index = 1 previous_active_index = 0
After update and before reboot active_index = 0 <<<< updated bank 0 previous_active_index = 1
After reboot, for some reason update fails (reject bank0) and we call fwu_revert_boot_index() active_index = 1 <<< good previous_active_index = 0 <<<< points to unbootable bank
Which may be seen as inconsistency if we assume previous_bank to always contain a bootable set of images. So we also need to copy bank1 into bank0 as part of the revert (at least as a backup for reasons other than a/b update failure).
If the platform owner wants to restore a particular bank with good images, the procedure to update that bank needs to be followed just like it was any other update.
The banks are under FWU and the platform has (should have) no control over which bank the image goes in.
It is the platform code that can decide on how the update banks are selected. Even in the case of num_banks > 2, the platform can have a round robin selection of the update banks through which all the other banks can be restored/updated. And currently, the default function for selection of the update bank does use round robin selection of the banks.
Platform only reads from and writes to a bank number given by the fwu. Platform has no means to sense if a bank is accepted or rejected. Or is there?
What I meant is that the bank to which the update will be written to is governed by the function fwu_plat_get_update_index(), which can be provided by every platform. And the platform can compute the update bank in a round robin fashion. This is another way to restore a bank from which the images are not booting for any reason(like rollback protection). Unless there is a hardware issue in the storage device, which will require manual intervention. So what I am trying to say here is that there is a way the rest of the banks can be restored without having to copy the banks.
If an updated bank fails the image acceptance test, the following boot would be from the previous_active_bank. In that case, the other bank needs to be updated by explicitly putting capsules in the ESP and initiating the update.
Which capsule - the one that just failed or the previous one (which may not be available/provided)?
One with working images. Please note that in the normal flow, a user would have tested the images locally before getting them deployed.
In the "normal flow" of the world many industries shouldn't exist - security, anti-virus, customer-support, repair etc :)
Doesn't simply copying over the bank make more sense?
When we are talking about "simply copying", I hope you appreciate that
- this(restoration of the banks) is not under the purview of the FWU
spec
I think it is. The restoration is part of the management of the banks... just like FWU makes sure metadata replicas are always in sync.
Okay. I did not find mention of this in the FWU specification. Can you point me to this.
- it will need some thought on how to have a generic
implementation which fetches images from the good bank and then writes them over to the other banks.
Not from "good bank" to "other banks". FWU only needs to copy from previous_bank to active_bank before swapping their pointers in fwu_revert_boot_index() maybe by a platform specific callback copy_bank(int from, int to) ?
Yes, but you might have a situation where all the other banks need to be updated, say due to rollback protection kicking in. I was talking from that point of view.
The implementation of the FWU spec requires using the UEFI capsule update interface for the updates.
The spec actually expects FWU to reside in BL32, but here we are implementing it in BL33 (I know we had to).
Not really. The spec does actually mention this scenario of the Update Agent and Client both residing in the normal world. This is a valid scenario that is described by the FWU spec, pg 39, "Normal World controlled FW store".
IMO, always keeping a good previous_bank is not even a violation of the spec, it is internal to the implementation.
Yes, I take your point. Only thing I was trying to highlight is that it can be achieved even without the copy.
-sughosh
But ok. I don't want to be the one to slow down the uboot's fwu progress. We can always implement it later when more people see the need.
-jassi

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 formatted with GPT based partition scheme.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Reviewed-by: Etienne Carriere etienne.carriere@linaro.org --- Changes since V9: * s/formated/formatted in the commit message * Add a '\n' character in the log message
drivers/fwu-mdata/Kconfig | 16 ++ drivers/fwu-mdata/Makefile | 8 + drivers/fwu-mdata/gpt_blk.c | 378 ++++++++++++++++++++++++++++++++++++ include/fwu.h | 4 + 4 files changed, 406 insertions(+) create mode 100644 drivers/fwu-mdata/Kconfig create mode 100644 drivers/fwu-mdata/Makefile create mode 100644 drivers/fwu-mdata/gpt_blk.c
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig new file mode 100644 index 0000000000..7322da48b1 --- /dev/null +++ b/drivers/fwu-mdata/Kconfig @@ -0,0 +1,16 @@ +config 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. + +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 new file mode 100644 index 0000000000..3fee64c10c --- /dev/null +++ b/drivers/fwu-mdata/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +# + + +obj-$(CONFIG_FWU_MDATA) += fwu-mdata-uclass.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += gpt_blk.o diff --git a/drivers/fwu-mdata/gpt_blk.c b/drivers/fwu-mdata/gpt_blk.c new file mode 100644 index 0000000000..df115575e0 --- /dev/null +++ b/drivers/fwu-mdata/gpt_blk.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, Linaro Limited + */ + +#define LOG_CATEGORY UCLASS_FWU_MDATA + +#include <blk.h> +#include <dm.h> +#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <memalign.h> +#include <part.h> +#include <part_efi.h> + +#include <dm/device-internal.h> +#include <linux/errno.h> +#include <linux/types.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, + uint *primary_mpart, + uint *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_debug("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_debug("Unable to get the partition info for the FWU metadata part %d\n", + part_num); + return -ENOENT; + } + + /* 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 -ENOENT; +} + +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 -ENOENT; + } + + len = sizeof(*mdata); + blkcnt = BLOCK_CNT(len, desc); + if (blkcnt > info.size) { + log_debug("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_debug("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_debug("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; + uint primary_mpart = 0, secondary_mpart = 0; + struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev); + + desc = dev_get_uclass_plat(priv->blk_dev); + + ret = gpt_get_mdata_partitions(desc, &primary_mpart, + &secondary_mpart); + if (ret < 0) { + log_debug("Error getting the FWU metadata partitions\n"); + return -ENOENT; + } + + /* First write the primary partition */ + ret = gpt_write_mdata_partition(desc, mdata, primary_mpart); + if (ret < 0) { + log_debug("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_debug("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; + uint primary_mpart = 0, secondary_mpart = 0; + + ret = gpt_get_mdata_partitions(desc, &primary_mpart, + &secondary_mpart); + + if (ret < 0) { + log_debug("Error getting the FWU metadata partitions\n"); + return -ENOENT; + } + + ret = gpt_read_mdata(desc, mdata, primary_mpart); + if (ret < 0) { + log_debug("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_debug("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 -EIO; +} + +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; + uint primary_mpart = 0, secondary_mpart = 0; + uint valid_partitions, invalid_partitions; + struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev); + + desc = dev_get_uclass_plat(priv->blk_dev); + + /* + * 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_debug("Error getting the FWU metadata partitions\n"); + return -ENOENT; + } + + ret = gpt_read_mdata(desc, &pri_mdata, primary_mpart); + if (ret < 0) { + log_debug("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_debug("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_debug("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_debug("Restoring %s FWU metadata partition failed\n", + (invalid_partitions == PRIMARY_PART) ? + "primary" : "secondary"); + +out: + return ret; +} + +static int fwu_gpt_mdata_check(struct udevice *dev) +{ + /* + * Check if both the copies of the FWU + * metadata are valid. If one has gone + * bad, restore it from the other good + * copy. + */ + return gpt_check_mdata_validity(dev); +} + +static int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata *mdata) +{ + struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev); + + return gpt_get_mdata(dev_get_uclass_plat(priv->blk_dev), mdata); +} + +static int fwu_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev) +{ + u32 phandle; + int ret, size; + struct udevice *parent; + const fdt32_t *phandle_p = NULL; + + phandle_p = dev_read_prop(dev, "fwu-mdata-store", &size); + if (!phandle_p) { + log_debug("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; + + return blk_get_from_parent(parent, mdata_dev); +} + +static int fwu_mdata_gpt_blk_probe(struct udevice *dev) +{ + int ret; + struct udevice *mdata_dev = NULL; + struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev); + + ret = fwu_get_mdata_device(dev, &mdata_dev); + if (ret) + return ret; + + priv->blk_dev = mdata_dev; + + return 0; +} + +static const struct fwu_mdata_ops fwu_gpt_blk_ops = { + .mdata_check = fwu_gpt_mdata_check, + .get_mdata = fwu_gpt_get_mdata, + .update_mdata = fwu_gpt_update_mdata, +}; + +static const struct udevice_id fwu_mdata_ids[] = { + { .compatible = "u-boot,fwu-mdata-gpt" }, + { } +}; + +U_BOOT_DRIVER(fwu_mdata_gpt_blk) = { + .name = "fwu-mdata-gpt-blk", + .id = UCLASS_FWU_MDATA, + .of_match = fwu_mdata_ids, + .ops = &fwu_gpt_blk_ops, + .probe = fwu_mdata_gpt_blk_probe, + .priv_auto = sizeof(struct fwu_mdata_gpt_blk_priv), +}; diff --git a/include/fwu.h b/include/fwu.h index 745f6225d0..1e16253f69 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -14,6 +14,10 @@ struct fwu_mdata; struct udevice;
+struct fwu_mdata_gpt_blk_priv { + struct udevice *blk_dev; +}; + /** * @mdata_check: check the validity of the FWU metadata partitions * @get_mdata() - Get a FWU metadata copy

Hi Sughosh
On Thu, Sep 15, 2022 at 01:44:39PM +0530, Sughosh Ganu wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, on a separate partition. Add 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 formatted with GPT based partition scheme.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Reviewed-by: Etienne Carriere etienne.carriere@linaro.org
Changes since V9:
- s/formated/formatted in the commit message
- Add a '\n' character in the log message
drivers/fwu-mdata/Kconfig | 16 ++ drivers/fwu-mdata/Makefile | 8 + drivers/fwu-mdata/gpt_blk.c | 378 ++++++++++++++++++++++++++++++++++++ include/fwu.h | 4 + 4 files changed, 406 insertions(+) create mode 100644 drivers/fwu-mdata/Kconfig create mode 100644 drivers/fwu-mdata/Makefile create mode 100644 drivers/fwu-mdata/gpt_blk.c
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig new file mode 100644 index 0000000000..7322da48b1 --- /dev/null +++ b/drivers/fwu-mdata/Kconfig @@ -0,0 +1,16 @@ +config 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.
+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 new file mode 100644 index 0000000000..3fee64c10c --- /dev/null +++ b/drivers/fwu-mdata/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +#
+obj-$(CONFIG_FWU_MDATA) += fwu-mdata-uclass.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += gpt_blk.o diff --git a/drivers/fwu-mdata/gpt_blk.c b/drivers/fwu-mdata/gpt_blk.c new file mode 100644 index 0000000000..df115575e0 --- /dev/null +++ b/drivers/fwu-mdata/gpt_blk.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2022, Linaro Limited
- */
+#define LOG_CATEGORY UCLASS_FWU_MDATA
+#include <blk.h> +#include <dm.h> +#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <memalign.h> +#include <part.h> +#include <part_efi.h>
+#include <dm/device-internal.h> +#include <linux/errno.h> +#include <linux/types.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,
uint *primary_mpart,
uint *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_debug("Expect two copies of the FWU metadata instead of %d\n",
mdata_parts);
ret = -EINVAL;
- } else {
ret = 0;
- }
Can we change that a bit? There are are some assumptions in this code, e.g the user must pass the values zero'ed out. Can we instead get an array of 2 instead? In that case you don't care if any of the values are zero and you can just fill in the array. Something like:
if (!guidcmp(&fwu_mdata_guid, &part_type_guid)) { array[mdata_parts] = i; mdata_parts++; You can also move the check in that if and make the func a lot easier to read.
- 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_debug("Unable to get the partition info for the FWU metadata part %d\n",
part_num);
return -ENOENT;
- }
- /* 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 -ENOENT;
+}
+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 -ENOENT;
- }
- len = sizeof(*mdata);
- blkcnt = BLOCK_CNT(len, desc);
- if (blkcnt > info.size) {
log_debug("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_debug("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_debug("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;
- uint primary_mpart = 0, secondary_mpart = 0;
- struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
- desc = dev_get_uclass_plat(priv->blk_dev);
- ret = gpt_get_mdata_partitions(desc, &primary_mpart,
&secondary_mpart);
- if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
- }
- /* First write the primary partition */
- ret = gpt_write_mdata_partition(desc, mdata, primary_mpart);
- if (ret < 0) {
log_debug("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_debug("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;
- uint primary_mpart = 0, secondary_mpart = 0;
- ret = gpt_get_mdata_partitions(desc, &primary_mpart,
&secondary_mpart);
- if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
- }
- ret = gpt_read_mdata(desc, mdata, primary_mpart);
- if (ret < 0) {
log_debug("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_debug("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 -EIO;
+}
+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;
- uint primary_mpart = 0, secondary_mpart = 0;
- uint valid_partitions, invalid_partitions;
- struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
- desc = dev_get_uclass_plat(priv->blk_dev);
- /*
* 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_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
- }
- ret = gpt_read_mdata(desc, &pri_mdata, primary_mpart);
- if (ret < 0) {
log_debug("Failed to read the FWU metadata from the device\n");
goto secondary_read;
Can't we skip the goto here and just make the if above (if ret == 0)?
- }
- 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_debug("Failed to read the FWU metadata from the device\n");
Same here
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_debug("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_debug("Restoring %s FWU metadata partition failed\n",
(invalid_partitions == PRIMARY_PART) ?
"primary" : "secondary");
+out:
- return ret;
+}
+static int fwu_gpt_mdata_check(struct udevice *dev) +{
- /*
* Check if both the copies of the FWU
* metadata are valid. If one has gone
* bad, restore it from the other good
* copy.
*/
- return gpt_check_mdata_validity(dev);
+}
+static int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata *mdata) +{
- struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
- return gpt_get_mdata(dev_get_uclass_plat(priv->blk_dev), mdata);
+}
+static int fwu_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev) +{
- u32 phandle;
- int ret, size;
- struct udevice *parent;
- const fdt32_t *phandle_p = NULL;
- phandle_p = dev_read_prop(dev, "fwu-mdata-store", &size);
- if (!phandle_p) {
log_debug("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;
- return blk_get_from_parent(parent, mdata_dev);
+}
+static int fwu_mdata_gpt_blk_probe(struct udevice *dev) +{
- int ret;
- struct udevice *mdata_dev = NULL;
- struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
- ret = fwu_get_mdata_device(dev, &mdata_dev);
- if (ret)
return ret;
- priv->blk_dev = mdata_dev;
- return 0;
+}
+static const struct fwu_mdata_ops fwu_gpt_blk_ops = {
- .mdata_check = fwu_gpt_mdata_check,
- .get_mdata = fwu_gpt_get_mdata,
- .update_mdata = fwu_gpt_update_mdata,
+};
+static const struct udevice_id fwu_mdata_ids[] = {
- { .compatible = "u-boot,fwu-mdata-gpt" },
- { }
+};
+U_BOOT_DRIVER(fwu_mdata_gpt_blk) = {
- .name = "fwu-mdata-gpt-blk",
- .id = UCLASS_FWU_MDATA,
- .of_match = fwu_mdata_ids,
- .ops = &fwu_gpt_blk_ops,
- .probe = fwu_mdata_gpt_blk_probe,
- .priv_auto = sizeof(struct fwu_mdata_gpt_blk_priv),
+}; diff --git a/include/fwu.h b/include/fwu.h index 745f6225d0..1e16253f69 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -14,6 +14,10 @@ struct fwu_mdata; struct udevice;
+struct fwu_mdata_gpt_blk_priv {
- struct udevice *blk_dev;
+};
/**
- @mdata_check: check the validity of the FWU metadata partitions
- @get_mdata() - Get a FWU metadata copy
-- 2.34.1
Cheers /Ilias

hi Ilias,
On Thu, 22 Sept 2022 at 14:16, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Sughosh
On Thu, Sep 15, 2022 at 01:44:39PM +0530, Sughosh Ganu wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, on a separate partition. Add 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 formatted with GPT based partition scheme.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Reviewed-by: Etienne Carriere etienne.carriere@linaro.org
Changes since V9:
- s/formated/formatted in the commit message
- Add a '\n' character in the log message
drivers/fwu-mdata/Kconfig | 16 ++ drivers/fwu-mdata/Makefile | 8 + drivers/fwu-mdata/gpt_blk.c | 378 ++++++++++++++++++++++++++++++++++++ include/fwu.h | 4 + 4 files changed, 406 insertions(+) create mode 100644 drivers/fwu-mdata/Kconfig create mode 100644 drivers/fwu-mdata/Makefile create mode 100644 drivers/fwu-mdata/gpt_blk.c
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig new file mode 100644 index 0000000000..7322da48b1 --- /dev/null +++ b/drivers/fwu-mdata/Kconfig @@ -0,0 +1,16 @@ +config 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.
+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 new file mode 100644 index 0000000000..3fee64c10c --- /dev/null +++ b/drivers/fwu-mdata/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +#
+obj-$(CONFIG_FWU_MDATA) += fwu-mdata-uclass.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += gpt_blk.o diff --git a/drivers/fwu-mdata/gpt_blk.c b/drivers/fwu-mdata/gpt_blk.c new file mode 100644 index 0000000000..df115575e0 --- /dev/null +++ b/drivers/fwu-mdata/gpt_blk.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2022, Linaro Limited
- */
+#define LOG_CATEGORY UCLASS_FWU_MDATA
+#include <blk.h> +#include <dm.h> +#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <memalign.h> +#include <part.h> +#include <part_efi.h>
+#include <dm/device-internal.h> +#include <linux/errno.h> +#include <linux/types.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,
uint *primary_mpart,
uint *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_debug("Expect two copies of the FWU metadata instead of %d\n",
mdata_parts);
ret = -EINVAL;
} else {
ret = 0;
}
Can we change that a bit? There are are some assumptions in this code, e.g the user must pass the values zero'ed out. Can we instead get an array of 2 instead? In that case you don't care if any of the values are zero and you can just fill in the array. Something like:
Okay, will make use of the array as you suggest.
if (!guidcmp(&fwu_mdata_guid, &part_type_guid)) { array[mdata_parts] = i; mdata_parts++; You can also move the check in that if and make the func a lot easier to read.
Sorry, I do not get this part.
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_debug("Unable to get the partition info for the FWU metadata part %d\n",
part_num);
return -ENOENT;
}
/* 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 -ENOENT;
+}
+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 -ENOENT;
}
len = sizeof(*mdata);
blkcnt = BLOCK_CNT(len, desc);
if (blkcnt > info.size) {
log_debug("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_debug("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_debug("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;
uint primary_mpart = 0, secondary_mpart = 0;
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
desc = dev_get_uclass_plat(priv->blk_dev);
ret = gpt_get_mdata_partitions(desc, &primary_mpart,
&secondary_mpart);
if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
}
/* First write the primary partition */
ret = gpt_write_mdata_partition(desc, mdata, primary_mpart);
if (ret < 0) {
log_debug("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_debug("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;
uint primary_mpart = 0, secondary_mpart = 0;
ret = gpt_get_mdata_partitions(desc, &primary_mpart,
&secondary_mpart);
if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
}
ret = gpt_read_mdata(desc, mdata, primary_mpart);
if (ret < 0) {
log_debug("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_debug("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 -EIO;
+}
+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;
uint primary_mpart = 0, secondary_mpart = 0;
uint valid_partitions, invalid_partitions;
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
desc = dev_get_uclass_plat(priv->blk_dev);
/*
* 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_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
}
ret = gpt_read_mdata(desc, &pri_mdata, primary_mpart);
if (ret < 0) {
log_debug("Failed to read the FWU metadata from the device\n");
goto secondary_read;
Can't we skip the goto here and just make the if above (if ret == 0)?
Will change
}
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_debug("Failed to read the FWU metadata from the device\n");
Same here
Will change
-sughosh
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_debug("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_debug("Restoring %s FWU metadata partition failed\n",
(invalid_partitions == PRIMARY_PART) ?
"primary" : "secondary");
+out:
return ret;
+}
+static int fwu_gpt_mdata_check(struct udevice *dev) +{
/*
* Check if both the copies of the FWU
* metadata are valid. If one has gone
* bad, restore it from the other good
* copy.
*/
return gpt_check_mdata_validity(dev);
+}
+static int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata *mdata) +{
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
return gpt_get_mdata(dev_get_uclass_plat(priv->blk_dev), mdata);
+}
+static int fwu_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev) +{
u32 phandle;
int ret, size;
struct udevice *parent;
const fdt32_t *phandle_p = NULL;
phandle_p = dev_read_prop(dev, "fwu-mdata-store", &size);
if (!phandle_p) {
log_debug("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;
return blk_get_from_parent(parent, mdata_dev);
+}
+static int fwu_mdata_gpt_blk_probe(struct udevice *dev) +{
int ret;
struct udevice *mdata_dev = NULL;
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
ret = fwu_get_mdata_device(dev, &mdata_dev);
if (ret)
return ret;
priv->blk_dev = mdata_dev;
return 0;
+}
+static const struct fwu_mdata_ops fwu_gpt_blk_ops = {
.mdata_check = fwu_gpt_mdata_check,
.get_mdata = fwu_gpt_get_mdata,
.update_mdata = fwu_gpt_update_mdata,
+};
+static const struct udevice_id fwu_mdata_ids[] = {
{ .compatible = "u-boot,fwu-mdata-gpt" },
{ }
+};
+U_BOOT_DRIVER(fwu_mdata_gpt_blk) = {
.name = "fwu-mdata-gpt-blk",
.id = UCLASS_FWU_MDATA,
.of_match = fwu_mdata_ids,
.ops = &fwu_gpt_blk_ops,
.probe = fwu_mdata_gpt_blk_probe,
.priv_auto = sizeof(struct fwu_mdata_gpt_blk_priv),
+}; diff --git a/include/fwu.h b/include/fwu.h index 745f6225d0..1e16253f69 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -14,6 +14,10 @@ struct fwu_mdata; struct udevice;
+struct fwu_mdata_gpt_blk_priv {
struct udevice *blk_dev;
+};
/**
- @mdata_check: check the validity of the FWU metadata partitions
- @get_mdata() - Get a FWU metadata copy
-- 2.34.1
Cheers /Ilias

Hello Sughosh and all,
On Mon, 26 Sept 2022 at 10:46, Sughosh Ganu sughosh.ganu@linaro.org wrote:
hi Ilias,
On Thu, 22 Sept 2022 at 14:16, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Sughosh
On Thu, Sep 15, 2022 at 01:44:39PM +0530, Sughosh Ganu wrote:
In the FWU Multi Bank Update feature, the information about the updatable images is stored as part of the metadata, on a separate partition. Add 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 formatted with GPT based partition scheme.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Reviewed-by: Etienne Carriere etienne.carriere@linaro.org
Changes since V9:
- s/formated/formatted in the commit message
- Add a '\n' character in the log message
drivers/fwu-mdata/Kconfig | 16 ++ drivers/fwu-mdata/Makefile | 8 + drivers/fwu-mdata/gpt_blk.c | 378 ++++++++++++++++++++++++++++++++++++ include/fwu.h | 4 + 4 files changed, 406 insertions(+) create mode 100644 drivers/fwu-mdata/Kconfig create mode 100644 drivers/fwu-mdata/Makefile create mode 100644 drivers/fwu-mdata/gpt_blk.c
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig new file mode 100644 index 0000000000..7322da48b1 --- /dev/null +++ b/drivers/fwu-mdata/Kconfig @@ -0,0 +1,16 @@ +config 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.
+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.
By the way, drop HAVE_BLOCK_DEVICE that is no more needed here. See https://source.denx.de/u-boot/u-boot/-/commit/7f8967c2b82f9917987b69fbf43f8f...
diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile new file mode 100644 index 0000000000..3fee64c10c --- /dev/null +++ b/drivers/fwu-mdata/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +#
+obj-$(CONFIG_FWU_MDATA) += fwu-mdata-uclass.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += gpt_blk.o diff --git a/drivers/fwu-mdata/gpt_blk.c b/drivers/fwu-mdata/gpt_blk.c new file mode 100644 index 0000000000..df115575e0 --- /dev/null +++ b/drivers/fwu-mdata/gpt_blk.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2022, Linaro Limited
- */
+#define LOG_CATEGORY UCLASS_FWU_MDATA
+#include <blk.h> +#include <dm.h> +#include <efi_loader.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <memalign.h> +#include <part.h> +#include <part_efi.h>
+#include <dm/device-internal.h> +#include <linux/errno.h> +#include <linux/types.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,
uint *primary_mpart,
uint *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_debug("Expect two copies of the FWU metadata instead of %d\n",
mdata_parts);
ret = -EINVAL;
} else {
ret = 0;
}
Can we change that a bit? There are are some assumptions in this code, e.g the user must pass the values zero'ed out. Can we instead get an array of
I don't agree. If the function returns an error code, caller shall not use the output content. That is what callers of gpt_get_mdata_partitions() do in this patch, so returning -EINVAL if fine. (note that I don't mind this gets an array ref rather than 2 pointers).
best regards, etienne
2 instead? In that case you don't care if any of the values are zero and you can just fill in the array. Something like:
Okay, will make use of the array as you suggest.
if (!guidcmp(&fwu_mdata_guid, &part_type_guid)) { array[mdata_parts] = i; mdata_parts++; You can also move the check in that if and make the func a lot easier to read.
Sorry, I do not get this part.
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_debug("Unable to get the partition info for the FWU metadata part %d\n",
part_num);
return -ENOENT;
}
/* 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 -ENOENT;
+}
+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 -ENOENT;
}
len = sizeof(*mdata);
blkcnt = BLOCK_CNT(len, desc);
if (blkcnt > info.size) {
log_debug("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_debug("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_debug("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;
uint primary_mpart = 0, secondary_mpart = 0;
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
desc = dev_get_uclass_plat(priv->blk_dev);
ret = gpt_get_mdata_partitions(desc, &primary_mpart,
&secondary_mpart);
if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
}
/* First write the primary partition */
ret = gpt_write_mdata_partition(desc, mdata, primary_mpart);
if (ret < 0) {
log_debug("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_debug("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;
uint primary_mpart = 0, secondary_mpart = 0;
ret = gpt_get_mdata_partitions(desc, &primary_mpart,
&secondary_mpart);
if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
}
ret = gpt_read_mdata(desc, mdata, primary_mpart);
if (ret < 0) {
log_debug("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_debug("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 -EIO;
+}
+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;
uint primary_mpart = 0, secondary_mpart = 0;
uint valid_partitions, invalid_partitions;
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
desc = dev_get_uclass_plat(priv->blk_dev);
/*
* 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_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
}
ret = gpt_read_mdata(desc, &pri_mdata, primary_mpart);
if (ret < 0) {
log_debug("Failed to read the FWU metadata from the device\n");
goto secondary_read;
Can't we skip the goto here and just make the if above (if ret == 0)?
Will change
}
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_debug("Failed to read the FWU metadata from the device\n");
Same here
Will change
-sughosh
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_debug("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_debug("Restoring %s FWU metadata partition failed\n",
(invalid_partitions == PRIMARY_PART) ?
"primary" : "secondary");
+out:
return ret;
+}
+static int fwu_gpt_mdata_check(struct udevice *dev) +{
/*
* Check if both the copies of the FWU
* metadata are valid. If one has gone
* bad, restore it from the other good
* copy.
*/
return gpt_check_mdata_validity(dev);
+}
+static int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata *mdata) +{
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
return gpt_get_mdata(dev_get_uclass_plat(priv->blk_dev), mdata);
+}
+static int fwu_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev) +{
u32 phandle;
int ret, size;
struct udevice *parent;
const fdt32_t *phandle_p = NULL;
phandle_p = dev_read_prop(dev, "fwu-mdata-store", &size);
if (!phandle_p) {
log_debug("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;
return blk_get_from_parent(parent, mdata_dev);
+}
+static int fwu_mdata_gpt_blk_probe(struct udevice *dev) +{
int ret;
struct udevice *mdata_dev = NULL;
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
ret = fwu_get_mdata_device(dev, &mdata_dev);
if (ret)
return ret;
priv->blk_dev = mdata_dev;
return 0;
+}
+static const struct fwu_mdata_ops fwu_gpt_blk_ops = {
.mdata_check = fwu_gpt_mdata_check,
.get_mdata = fwu_gpt_get_mdata,
.update_mdata = fwu_gpt_update_mdata,
+};
+static const struct udevice_id fwu_mdata_ids[] = {
{ .compatible = "u-boot,fwu-mdata-gpt" },
{ }
+};
+U_BOOT_DRIVER(fwu_mdata_gpt_blk) = {
.name = "fwu-mdata-gpt-blk",
.id = UCLASS_FWU_MDATA,
.of_match = fwu_mdata_ids,
.ops = &fwu_gpt_blk_ops,
.probe = fwu_mdata_gpt_blk_probe,
.priv_auto = sizeof(struct fwu_mdata_gpt_blk_priv),
+}; diff --git a/include/fwu.h b/include/fwu.h index 745f6225d0..1e16253f69 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -14,6 +14,10 @@ struct fwu_mdata; struct udevice;
+struct fwu_mdata_gpt_blk_priv {
struct udevice *blk_dev;
+};
/**
- @mdata_check: check the validity of the FWU metadata partitions
- @get_mdata() - Get a FWU metadata copy
-- 2.34.1
Cheers /Ilias

Hey Etiennem
[...]
uint *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_debug("Expect two copies of the FWU metadata instead of %d\n",
mdata_parts);
ret = -EINVAL;
} else {
ret = 0;
}
Can we change that a bit? There are are some assumptions in this code, e.g the user must pass the values zero'ed out. Can we instead get an array of
I don't agree. If the function returns an error code, caller shall not use the output content. That is what callers of gpt_get_mdata_partitions() do in this patch, so returning -EINVAL if fine. (note that I don't mind this gets an array ref rather than 2 pointers).
Ah true. In any case I still think we should change to an array, imho it would be much easier to read
best regards, etienne
2 instead? In that case you don't care if any of the values are zero and you can just fill in the array. Something like:
Okay, will make use of the array as you suggest.
[...]
Regards /Ilias

On Thu, Sep 15, 2022 at 3:16 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig new file mode 100644 index 0000000000..7322da48b1 --- /dev/null +++ b/drivers/fwu-mdata/Kconfig @@ -0,0 +1,16 @@ +config 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.
+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
It should depend on FWU_MDATA instead of DM. Though, eventually it will be a choice between GPT and MTD.
...
+static int fwu_gpt_mdata_check(struct udevice *dev) +{
/*
* Check if both the copies of the FWU
* metadata are valid. If one has gone
* bad, restore it from the other good
* copy.
*/
This comment is repeated 3 more times in the patchset. Maybe get rid of this and some other.
cheers.

On Mon, 26 Sept 2022 at 08:22, Jassi Brar jassisinghbrar@gmail.com wrote:
On Thu, Sep 15, 2022 at 3:16 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig new file mode 100644 index 0000000000..7322da48b1 --- /dev/null +++ b/drivers/fwu-mdata/Kconfig @@ -0,0 +1,16 @@ +config 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.
+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
It should depend on FWU_MDATA instead of DM. Though, eventually it will be a choice between GPT and MTD.
Yes, this should depend on FWU_MDATA. Will change.
...
+static int fwu_gpt_mdata_check(struct udevice *dev) +{
/*
* Check if both the copies of the FWU
* metadata are valid. If one has gone
* bad, restore it from the other good
* copy.
*/
This comment is repeated 3 more times in the patchset. Maybe get rid of this and some other.
Sorry, I could not find any other comment like this in the file. Can you point me out where this comment has been repeated.
-sughosh

On Mon, Sep 26, 2022 at 3:48 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:22, Jassi Brar jassisinghbrar@gmail.com wrote:
On Thu, Sep 15, 2022 at 3:16 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig new file mode 100644 index 0000000000..7322da48b1 --- /dev/null +++ b/drivers/fwu-mdata/Kconfig @@ -0,0 +1,16 @@ +config 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.
+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
It should depend on FWU_MDATA instead of DM. Though, eventually it will be a choice between GPT and MTD.
Yes, this should depend on FWU_MDATA. Will change.
...
+static int fwu_gpt_mdata_check(struct udevice *dev) +{
/*
* Check if both the copies of the FWU
* metadata are valid. If one has gone
* bad, restore it from the other good
* copy.
*/
This comment is repeated 3 more times in the patchset. Maybe get rid of this and some other.
Sorry, I could not find any other comment like this in the file. Can you point me out where this comment has been repeated.
I said patchset, not file. grep'ing "restore it from the other" in the patches show drivers/fwu-mdata/gpt_blk.c, drivers/fwu-mdata/fwu-mdata-uclass.c and twice in include/fwu.h
Btw, the other three times it says "restore it from the other bad copy" :D
-j

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

Enabling capsule update functionality on the platform requires populating information on the images that are to be updated using the functionality. Do so for the DK2 board.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org --- Changes since V9: * Remove the check board_is_stm32mp15x_dk2() as suggested by Etienne
board/st/stm32mp1/stm32mp1.c | 21 +++++++++++++++++++++ include/configs/stm32mp15_common.h | 4 ++++ 2 files changed, 25 insertions(+)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 8c162b42a5..6562bcf95b 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -11,6 +11,7 @@ #include <clk.h> #include <config.h> #include <dm.h> +#include <efi_loader.h> #include <env.h> #include <env_internal.h> #include <fdt_simplefb.h> @@ -87,6 +88,16 @@ #define USB_START_LOW_THRESHOLD_UV 1230000 #define USB_START_HIGH_THRESHOLD_UV 2150000
+#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) +struct efi_fw_image fw_images[1]; + +struct efi_capsule_update_info update_info = { + .images = fw_images, +}; + +u8 num_image_type_guids = ARRAY_SIZE(fw_images); +#endif /* EFI_HAVE_CAPSULE_SUPPORT */ + int board_early_init_f(void) { /* nothing to do, only used in SPL */ @@ -666,6 +677,16 @@ int board_init(void)
setup_led(LEDST_ON);
+#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT) + efi_guid_t image_type_guid = STM32MP_FIP_IMAGE_GUID; + guidcpy(&fw_images[0].image_type_id, &image_type_guid); + fw_images[0].fw_name = u"STM32MP-FIP"; + /* + * For FWU multi bank update, the image + * index will be computed at runtime + */ + fw_images[0].image_index = 0; +#endif return 0; }
diff --git a/include/configs/stm32mp15_common.h b/include/configs/stm32mp15_common.h index c5412ffeb3..bb19dae945 100644 --- a/include/configs/stm32mp15_common.h +++ b/include/configs/stm32mp15_common.h @@ -34,6 +34,10 @@ #define CONFIG_SERVERIP 192.168.1.1 #endif
+#define STM32MP_FIP_IMAGE_GUID \ + EFI_GUID(0x19d5df83, 0x11b0, 0x457b, 0xbe, 0x2c, \ + 0x75, 0x59, 0xc1, 0x31, 0x42, 0xa5) + /*****************************************************************************/ #ifdef CONFIG_DISTRO_DEFAULTS /*****************************************************************************/

Add weak functions for getting the update index value and dfu alternate number needed for FWU Multi Bank update functionality.
The current implementation for getting the update index value is for platforms with 2 banks. If a platform supports more than 2 banks, it can implement it's own function. The function to get the dfu alternate number has been added for platforms with GPT partitioned storage devices. Platforms with other storage partition scheme need to implement their own function.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Acked-by: Etienne Carriere etienne.carriere@linaro.org --- Changes since V9: * Remove unneeded braces as suggested by Ilias
include/fwu.h | 29 ++++++++++ lib/fwu_updates/fwu.c | 27 +++++++++ lib/fwu_updates/fwu_gpt.c | 114 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 lib/fwu_updates/fwu_gpt.c
diff --git a/include/fwu.h b/include/fwu.h index 1e16253f69..3ff37c628b 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -215,4 +215,33 @@ 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);
+/** + * fwu_plat_get_alt_num() - Get the DFU Alt Num for the image from the platform + * @dev: FWU device + * @image_guid: Image GUID for which DFU alt number needs to be retrieved + * @alt_num: Pointer to the alt_num + * + * Get the DFU alt number from the platform for the image specified by the + * image GUID. + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, + u8 *alt_num); + +/** + * fwu_plat_get_update_index() - Get the value of the update bank + * @update_idx: Bank number to which images are to be updated + * + * Get the value of the bank(partition) to which the update needs to be + * made. + * + * Note: This is a weak function and platforms can override this with + * their own implementation for selection of the update bank. + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_plat_get_update_index(uint *update_idx); #endif /* _FWU_H_ */ diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index fb9dbca307..8e91b7aeae 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -331,3 +331,30 @@ int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank) return fwu_clrset_image_accept(img_type_id, bank, IMAGE_ACCEPT_CLEAR); } + +/** + * fwu_plat_get_update_index() - Get the value of the update bank + * @update_idx: Bank number to which images are to be updated + * + * Get the value of the bank(partition) to which the update needs to be + * made. + * + * Note: This is a weak function and platforms can override this with + * their own implementation for selection of the update bank. + * + * Return: 0 if OK, -ve on error + * + */ +__weak int fwu_plat_get_update_index(uint *update_idx) +{ + int ret; + u32 active_idx; + + ret = fwu_get_active_index(&active_idx); + if (ret < 0) + return -1; + + *update_idx = (active_idx + 1) % CONFIG_FWU_NUM_BANKS; + + return ret; +} diff --git a/lib/fwu_updates/fwu_gpt.c b/lib/fwu_updates/fwu_gpt.c new file mode 100644 index 0000000000..a1b0f3bea7 --- /dev/null +++ b/lib/fwu_updates/fwu_gpt.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, Linaro Limited + */ + +#include <blk.h> +#include <dfu.h> +#include <efi.h> +#include <efi_loader.h> +#include <fwu.h> +#include <log.h> +#include <part.h> + +#include <linux/errno.h> + +static int get_gpt_dfu_identifier(struct blk_desc *desc, efi_guid_t *image_guid) +{ + int i; + struct disk_partition info; + efi_guid_t unique_part_guid; + + for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) { + if (part_get_info(desc, i, &info)) + continue; + uuid_str_to_bin(info.uuid, unique_part_guid.b, + UUID_STR_FORMAT_GUID); + + if (!guidcmp(&unique_part_guid, image_guid)) + return i; + } + + log_err("No partition found with image_guid %pUs\n", image_guid); + return -ENOENT; +} + +static int fwu_gpt_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid, + u8 *alt_num, unsigned char dfu_dev) +{ + int ret = -1; + int i, part, dev_num; + int nalt; + struct dfu_entity *dfu; + + dev_num = desc->devnum; + part = get_gpt_dfu_identifier(desc, image_guid); + if (part < 0) + return -ENOENT; + + dfu_init_env_entities(NULL, NULL); + + nalt = 0; + list_for_each_entry(dfu, &dfu_list, list) + nalt++; + + if (!nalt) { + log_warning("No entities in dfu_alt_info\n"); + dfu_free_entities(); + return -ENOENT; + } + + for (i = 0; i < nalt; i++) { + dfu = dfu_get_entity(i); + + if (!dfu) + continue; + + /* + * Currently, Multi Bank update + * feature is being supported + * only on GPT partitioned + * MMC/SD devices. + */ + if (dfu->dev_type != dfu_dev) + continue; + + if (dfu->layout == DFU_RAW_ADDR && + dfu->data.mmc.dev_num == dev_num && + dfu->data.mmc.part == part) { + *alt_num = dfu->alt; + ret = 0; + break; + } + } + + dfu_free_entities(); + + return ret; +} + +/** + * fwu_plat_get_alt_num() - Get the DFU alt number + * @dev: FWU metadata device + * @image_guid: GUID value of the image for which the alt num is to + * be obtained + * @alt_num: The DFU alt number for the image that is to be updated + * + * Get the DFU alt number for the image that is to be updated. The + * image is identified with the image_guid parameter that is passed + * to the function. + * + * Note: This is a weak function and platforms can override this with + * their own implementation for obtaining the alt number value. + * + * Return: 0 if OK, -ve on error + * + */ +__weak int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, + u8 *alt_num) +{ + struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev); + + return fwu_gpt_get_alt_num(dev_get_uclass_plat(priv->blk_dev), + image_guid, alt_num, DFU_DEV_MMC); +}

Hi Sughosh
On Thu, Sep 15, 2022 at 01:44:42PM +0530, Sughosh Ganu wrote:
Add weak functions for getting the update index value and dfu alternate number needed for FWU Multi Bank update functionality.
The current implementation for getting the update index value is for platforms with 2 banks. If a platform supports more than 2 banks, it can implement it's own function. The function to get the dfu alternate number has been added for platforms with GPT partitioned storage devices. Platforms with other storage partition scheme need to implement their own function.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Acked-by: Etienne Carriere etienne.carriere@linaro.org
Changes since V9:
- Remove unneeded braces as suggested by Ilias
include/fwu.h | 29 ++++++++++ lib/fwu_updates/fwu.c | 27 +++++++++ lib/fwu_updates/fwu_gpt.c | 114 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 lib/fwu_updates/fwu_gpt.c
diff --git a/include/fwu.h b/include/fwu.h index 1e16253f69..3ff37c628b 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -215,4 +215,33 @@ 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);
+/**
- fwu_plat_get_alt_num() - Get the DFU Alt Num for the image from the platform
- @dev: FWU device
- @image_guid: Image GUID for which DFU alt number needs to be retrieved
- @alt_num: Pointer to the alt_num
- Get the DFU alt number from the platform for the image specified by the
- image GUID.
- Return: 0 if OK, -ve on error
- */
+int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid,
u8 *alt_num);
+/**
- fwu_plat_get_update_index() - Get the value of the update bank
- @update_idx: Bank number to which images are to be updated
- Get the value of the bank(partition) to which the update needs to be
- made.
- Note: This is a weak function and platforms can override this with
- their own implementation for selection of the update bank.
- Return: 0 if OK, -ve on error
- */
+int fwu_plat_get_update_index(uint *update_idx); #endif /* _FWU_H_ */ diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index fb9dbca307..8e91b7aeae 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -331,3 +331,30 @@ int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank) return fwu_clrset_image_accept(img_type_id, bank, IMAGE_ACCEPT_CLEAR); }
+/**
- fwu_plat_get_update_index() - Get the value of the update bank
- @update_idx: Bank number to which images are to be updated
- Get the value of the bank(partition) to which the update needs to be
- made.
- Note: This is a weak function and platforms can override this with
- their own implementation for selection of the update bank.
- Return: 0 if OK, -ve on error
- */
+__weak int fwu_plat_get_update_index(uint *update_idx) +{
- int ret;
- u32 active_idx;
- ret = fwu_get_active_index(&active_idx);
- if (ret < 0)
return -1;
- *update_idx = (active_idx + 1) % CONFIG_FWU_NUM_BANKS;
- return ret;
+} diff --git a/lib/fwu_updates/fwu_gpt.c b/lib/fwu_updates/fwu_gpt.c new file mode 100644 index 0000000000..a1b0f3bea7 --- /dev/null +++ b/lib/fwu_updates/fwu_gpt.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2022, Linaro Limited
- */
+#include <blk.h> +#include <dfu.h> +#include <efi.h> +#include <efi_loader.h> +#include <fwu.h> +#include <log.h> +#include <part.h>
+#include <linux/errno.h>
+static int get_gpt_dfu_identifier(struct blk_desc *desc, efi_guid_t *image_guid) +{
- int i;
- struct disk_partition info;
- efi_guid_t unique_part_guid;
- for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) {
if (part_get_info(desc, i, &info))
continue;
uuid_str_to_bin(info.uuid, unique_part_guid.b,
UUID_STR_FORMAT_GUID);
if (!guidcmp(&unique_part_guid, image_guid))
return i;
- }
- log_err("No partition found with image_guid %pUs\n", image_guid);
- return -ENOENT;
+}
+static int fwu_gpt_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid,
u8 *alt_num, unsigned char dfu_dev)
+{
- int ret = -1;
- int i, part, dev_num;
- int nalt;
- struct dfu_entity *dfu;
- dev_num = desc->devnum;
- part = get_gpt_dfu_identifier(desc, image_guid);
- if (part < 0)
return -ENOENT;
- dfu_init_env_entities(NULL, NULL);
- nalt = 0;
- list_for_each_entry(dfu, &dfu_list, list)
nalt++;
- if (!nalt) {
log_warning("No entities in dfu_alt_info\n");
dfu_free_entities();
return -ENOENT;
- }
- for (i = 0; i < nalt; i++) {
dfu = dfu_get_entity(i);
if (!dfu)
continue;
/*
* Currently, Multi Bank update
* feature is being supported
* only on GPT partitioned
* MMC/SD devices.
*/
if (dfu->dev_type != dfu_dev)
continue;
if (dfu->layout == DFU_RAW_ADDR &&
dfu->data.mmc.dev_num == dev_num &&
dfu->data.mmc.part == part) {
*alt_num = dfu->alt;
ret = 0;
break;
I get that we only currently support it on mmc, but the if above is not going to scale as we add devices. Is there something better we can come up with? Probably a helper in the dfu layer?
Cheers /Ilias
}
- }
- dfu_free_entities();
- return ret;
+}
+/**
- fwu_plat_get_alt_num() - Get the DFU alt number
- @dev: FWU metadata device
- @image_guid: GUID value of the image for which the alt num is to
be obtained
- @alt_num: The DFU alt number for the image that is to be updated
- Get the DFU alt number for the image that is to be updated. The
- image is identified with the image_guid parameter that is passed
- to the function.
- Note: This is a weak function and platforms can override this with
- their own implementation for obtaining the alt number value.
- Return: 0 if OK, -ve on error
- */
+__weak int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid,
u8 *alt_num)
+{
- struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
- return fwu_gpt_get_alt_num(dev_get_uclass_plat(priv->blk_dev),
image_guid, alt_num, DFU_DEV_MMC);
+}
2.34.1

hi Ilias,
On Thu, 22 Sept 2022 at 14:29, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Sughosh
On Thu, Sep 15, 2022 at 01:44:42PM +0530, Sughosh Ganu wrote:
Add weak functions for getting the update index value and dfu alternate number needed for FWU Multi Bank update functionality.
The current implementation for getting the update index value is for platforms with 2 banks. If a platform supports more than 2 banks, it can implement it's own function. The function to get the dfu alternate number has been added for platforms with GPT partitioned storage devices. Platforms with other storage partition scheme need to implement their own function.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Acked-by: Etienne Carriere etienne.carriere@linaro.org
Changes since V9:
- Remove unneeded braces as suggested by Ilias
include/fwu.h | 29 ++++++++++ lib/fwu_updates/fwu.c | 27 +++++++++ lib/fwu_updates/fwu_gpt.c | 114 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 lib/fwu_updates/fwu_gpt.c
diff --git a/include/fwu.h b/include/fwu.h index 1e16253f69..3ff37c628b 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -215,4 +215,33 @@ 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);
+/**
- fwu_plat_get_alt_num() - Get the DFU Alt Num for the image from the platform
- @dev: FWU device
- @image_guid: Image GUID for which DFU alt number needs to be retrieved
- @alt_num: Pointer to the alt_num
- Get the DFU alt number from the platform for the image specified by the
- image GUID.
- Return: 0 if OK, -ve on error
- */
+int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid,
u8 *alt_num);
+/**
- fwu_plat_get_update_index() - Get the value of the update bank
- @update_idx: Bank number to which images are to be updated
- Get the value of the bank(partition) to which the update needs to be
- made.
- Note: This is a weak function and platforms can override this with
- their own implementation for selection of the update bank.
- Return: 0 if OK, -ve on error
- */
+int fwu_plat_get_update_index(uint *update_idx); #endif /* _FWU_H_ */ diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index fb9dbca307..8e91b7aeae 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -331,3 +331,30 @@ int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank) return fwu_clrset_image_accept(img_type_id, bank, IMAGE_ACCEPT_CLEAR); }
+/**
- fwu_plat_get_update_index() - Get the value of the update bank
- @update_idx: Bank number to which images are to be updated
- Get the value of the bank(partition) to which the update needs to be
- made.
- Note: This is a weak function and platforms can override this with
- their own implementation for selection of the update bank.
- Return: 0 if OK, -ve on error
- */
+__weak int fwu_plat_get_update_index(uint *update_idx) +{
int ret;
u32 active_idx;
ret = fwu_get_active_index(&active_idx);
if (ret < 0)
return -1;
*update_idx = (active_idx + 1) % CONFIG_FWU_NUM_BANKS;
return ret;
+} diff --git a/lib/fwu_updates/fwu_gpt.c b/lib/fwu_updates/fwu_gpt.c new file mode 100644 index 0000000000..a1b0f3bea7 --- /dev/null +++ b/lib/fwu_updates/fwu_gpt.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2022, Linaro Limited
- */
+#include <blk.h> +#include <dfu.h> +#include <efi.h> +#include <efi_loader.h> +#include <fwu.h> +#include <log.h> +#include <part.h>
+#include <linux/errno.h>
+static int get_gpt_dfu_identifier(struct blk_desc *desc, efi_guid_t *image_guid) +{
int i;
struct disk_partition info;
efi_guid_t unique_part_guid;
for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) {
if (part_get_info(desc, i, &info))
continue;
uuid_str_to_bin(info.uuid, unique_part_guid.b,
UUID_STR_FORMAT_GUID);
if (!guidcmp(&unique_part_guid, image_guid))
return i;
}
log_err("No partition found with image_guid %pUs\n", image_guid);
return -ENOENT;
+}
+static int fwu_gpt_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid,
u8 *alt_num, unsigned char dfu_dev)
+{
int ret = -1;
int i, part, dev_num;
int nalt;
struct dfu_entity *dfu;
dev_num = desc->devnum;
part = get_gpt_dfu_identifier(desc, image_guid);
if (part < 0)
return -ENOENT;
dfu_init_env_entities(NULL, NULL);
nalt = 0;
list_for_each_entry(dfu, &dfu_list, list)
nalt++;
if (!nalt) {
log_warning("No entities in dfu_alt_info\n");
dfu_free_entities();
return -ENOENT;
}
for (i = 0; i < nalt; i++) {
dfu = dfu_get_entity(i);
if (!dfu)
continue;
/*
* Currently, Multi Bank update
* feature is being supported
* only on GPT partitioned
* MMC/SD devices.
*/
if (dfu->dev_type != dfu_dev)
continue;
if (dfu->layout == DFU_RAW_ADDR &&
dfu->data.mmc.dev_num == dev_num &&
dfu->data.mmc.part == part) {
*alt_num = dfu->alt;
ret = 0;
break;
I get that we only currently support it on mmc, but the if above is not going to scale as we add devices. Is there something better we can come up with? Probably a helper in the dfu layer?
Currently, the DFU supports only the mmc which will be a GPT partitioned device. But yes, yours is a valid point in terms of scalability. However, I am wondering if this check should instead be in the board file. Every platform would know which is the device being used for storing the firmware images. Will this check not be more apt in a board function?
-sughosh
Cheers /Ilias
}
}
dfu_free_entities();
return ret;
+}
+/**
- fwu_plat_get_alt_num() - Get the DFU alt number
- @dev: FWU metadata device
- @image_guid: GUID value of the image for which the alt num is to
be obtained
- @alt_num: The DFU alt number for the image that is to be updated
- Get the DFU alt number for the image that is to be updated. The
- image is identified with the image_guid parameter that is passed
- to the function.
- Note: This is a weak function and platforms can override this with
- their own implementation for obtaining the alt number value.
- Return: 0 if OK, -ve on error
- */
+__weak int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid,
u8 *alt_num)
+{
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
return fwu_gpt_get_alt_num(dev_get_uclass_plat(priv->blk_dev),
image_guid, alt_num, DFU_DEV_MMC);
+}
2.34.1

[...]
+static int fwu_gpt_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid,
u8 *alt_num, unsigned char dfu_dev)
+{
int ret = -1;
int i, part, dev_num;
int nalt;
struct dfu_entity *dfu;
dev_num = desc->devnum;
part = get_gpt_dfu_identifier(desc, image_guid);
if (part < 0)
return -ENOENT;
dfu_init_env_entities(NULL, NULL);
nalt = 0;
list_for_each_entry(dfu, &dfu_list, list)
nalt++;
if (!nalt) {
log_warning("No entities in dfu_alt_info\n");
dfu_free_entities();
return -ENOENT;
}
for (i = 0; i < nalt; i++) {
dfu = dfu_get_entity(i);
if (!dfu)
continue;
/*
* Currently, Multi Bank update
* feature is being supported
* only on GPT partitioned
* MMC/SD devices.
*/
if (dfu->dev_type != dfu_dev)
continue;
if (dfu->layout == DFU_RAW_ADDR &&
dfu->data.mmc.dev_num == dev_num &&
dfu->data.mmc.part == part) {
*alt_num = dfu->alt;
ret = 0;
break;
I get that we only currently support it on mmc, but the if above is not going to scale as we add devices. Is there something better we can come up with? Probably a helper in the dfu layer?
Currently, the DFU supports only the mmc which will be a GPT partitioned device. But yes, yours is a valid point in terms of scalability. However, I am wondering if this check should instead be in the board file. Every platform would know which is the device being used for storing the firmware images. Will this check not be more apt in a board function?
Isn't this supposed to come from the dfu_alt_info in the command line? IOW What you are trying to prevent here is a user misconfiguration?
Thanks /Ilias
-sughosh
Cheers /Ilias
}
}
dfu_free_entities();
return ret;
+}
+/**
- fwu_plat_get_alt_num() - Get the DFU alt number
- @dev: FWU metadata device
- @image_guid: GUID value of the image for which the alt num is to
be obtained
- @alt_num: The DFU alt number for the image that is to be updated
- Get the DFU alt number for the image that is to be updated. The
- image is identified with the image_guid parameter that is passed
- to the function.
- Note: This is a weak function and platforms can override this with
- their own implementation for obtaining the alt number value.
- Return: 0 if OK, -ve on error
- */
+__weak int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid,
u8 *alt_num)
+{
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
return fwu_gpt_get_alt_num(dev_get_uclass_plat(priv->blk_dev),
image_guid, alt_num, DFU_DEV_MMC);
+}
2.34.1

The FWU Multi Bank Update feature allows the platform to boot the firmware images from one of the partitions(banks). The first stage bootloader(fsbl) passes the value of the boot index, i.e. the bank from which the firmware images were booted from to U-Boot. On the STM32MP157C-DK2 board, this value is passed through one of the SoC's backup register. Add a function to read the boot index value from the backup register.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org --- Changes since V9: * Change the fwu_plat_get_bootidx() function to take an uint * as the function parameter instead of the void * as suggested by Etienne.
arch/arm/mach-stm32mp/include/mach/stm32.h | 5 +++++ board/st/stm32mp1/stm32mp1.c | 23 ++++++++++++++++++++++ include/fwu.h | 12 +++++++++++ 3 files changed, 40 insertions(+)
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index c70375a723..c85ae6a34e 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -112,11 +112,16 @@ enum boot_device { #ifdef CONFIG_STM32MP15x #define TAMP_BACKUP_MAGIC_NUMBER TAMP_BACKUP_REGISTER(4) #define TAMP_BACKUP_BRANCH_ADDRESS TAMP_BACKUP_REGISTER(5) +#define TAMP_FWU_BOOT_INFO_REG TAMP_BACKUP_REGISTER(10) #define TAMP_COPRO_RSC_TBL_ADDRESS TAMP_BACKUP_REGISTER(17) #define TAMP_COPRO_STATE TAMP_BACKUP_REGISTER(18) #define TAMP_BOOT_CONTEXT TAMP_BACKUP_REGISTER(20) #define TAMP_BOOTCOUNT TAMP_BACKUP_REGISTER(21)
+#define TAMP_FWU_BOOT_IDX_MASK GENMASK(3, 0) + +#define TAMP_FWU_BOOT_IDX_OFFSET 0 + #define TAMP_COPRO_STATE_OFF 0 #define TAMP_COPRO_STATE_INIT 1 #define TAMP_COPRO_STATE_CRUN 2 diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 6562bcf95b..54b9535405 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -960,3 +960,26 @@ 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> + +/** + * fwu_plat_get_bootidx() - Get the value of the boot index + * @boot_idx: Boot index value + * + * Get the value of the bank(partition) from which the platform + * has booted. This value is passed to U-Boot from the earlier + * stage bootloader which loads and boots all the relevant + * firmware images + * + */ +void fwu_plat_get_bootidx(uint *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 3ff37c628b..484289ed4f 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -244,4 +244,16 @@ int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid, * */ int fwu_plat_get_update_index(uint *update_idx); + +/** + * fwu_plat_get_bootidx() - Get the value of the boot index + * @boot_idx: Boot index value + * + * Get the value of the bank(partition) from which the platform + * has booted. This value is passed to U-Boot from the earlier + * stage bootloader which loads and boots all the relevant + * firmware images + * + */ +void fwu_plat_get_bootidx(uint *boot_idx); #endif /* _FWU_H_ */

On Thu, 15 Sept 2022 at 10:15, Sughosh Ganu sughosh.ganu@linaro.org wrote:
The FWU Multi Bank Update feature allows the platform to boot the firmware images from one of the partitions(banks). The first stage bootloader(fsbl) passes the value of the boot index, i.e. the bank from which the firmware images were booted from to U-Boot. On the STM32MP157C-DK2 board, this value is passed through one of the SoC's backup register. Add a function to read the boot index value from the backup register.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Patrick Delaunay patrick.delaunay@foss.st.com Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org
Changes since V9:
- Change the fwu_plat_get_bootidx() function to take an uint * as the function parameter instead of the void * as suggested by Etienne.
arch/arm/mach-stm32mp/include/mach/stm32.h | 5 +++++ board/st/stm32mp1/stm32mp1.c | 23 ++++++++++++++++++++++ include/fwu.h | 12 +++++++++++ 3 files changed, 40 insertions(+)
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index c70375a723..c85ae6a34e 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -112,11 +112,16 @@ enum boot_device { #ifdef CONFIG_STM32MP15x #define TAMP_BACKUP_MAGIC_NUMBER TAMP_BACKUP_REGISTER(4) #define TAMP_BACKUP_BRANCH_ADDRESS TAMP_BACKUP_REGISTER(5) +#define TAMP_FWU_BOOT_INFO_REG TAMP_BACKUP_REGISTER(10) #define TAMP_COPRO_RSC_TBL_ADDRESS TAMP_BACKUP_REGISTER(17) #define TAMP_COPRO_STATE TAMP_BACKUP_REGISTER(18) #define TAMP_BOOT_CONTEXT TAMP_BACKUP_REGISTER(20) #define TAMP_BOOTCOUNT TAMP_BACKUP_REGISTER(21)
+#define TAMP_FWU_BOOT_IDX_MASK GENMASK(3, 0)
+#define TAMP_FWU_BOOT_IDX_OFFSET 0
#define TAMP_COPRO_STATE_OFF 0 #define TAMP_COPRO_STATE_INIT 1 #define TAMP_COPRO_STATE_CRUN 2 diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 6562bcf95b..54b9535405 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -960,3 +960,26 @@ 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>
+/**
- fwu_plat_get_bootidx() - Get the value of the boot index
- @boot_idx: Boot index value
- Get the value of the bank(partition) from which the platform
- has booted. This value is passed to U-Boot from the earlier
- stage bootloader which loads and boots all the relevant
- firmware images
- */
+void fwu_plat_get_bootidx(uint *boot_idx) +{
u32 *bootidx = boot_idx;
*bootidx = (readl(TAMP_FWU_BOOT_INFO_REG) >>
TAMP_FWU_BOOT_IDX_OFFSET) & TAMP_FWU_BOOT_IDX_MASK;
+}
I may be nitpicking but despite this is fine in this source file build only for 32bit, I think the cast is not that clean. Would prefer:
void fwu_plat_get_bootidx(uint *boot_idx) { *boot_idx = (readl(TAMP_FWU_BOOT_INFO_REG) >> TAMP_FWU_BOOT_IDX_OFFSET) & TAMP_FWU_BOOT_IDX_MASK; }
br, etienne
+#endif /* CONFIG_FWU_MULTI_BANK_UPDATE */ diff --git a/include/fwu.h b/include/fwu.h index 3ff37c628b..484289ed4f 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -244,4 +244,16 @@ int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid,
*/ int fwu_plat_get_update_index(uint *update_idx);
+/**
- fwu_plat_get_bootidx() - Get the value of the boot index
- @boot_idx: Boot index value
- Get the value of the bank(partition) from which the platform
- has booted. This value is passed to U-Boot from the earlier
- stage bootloader which loads and boots all the relevant
- firmware images
- */
+void fwu_plat_get_bootidx(uint *boot_idx);
#endif /* _FWU_H_ */
2.34.1

Add an event type EVT_MAIN_LOOP that can be used for registering events that need to be run after the platform has been initialised and before the main_loop function is called.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Simon Glass sjg@chromium.org --- Changes since V9: * Add a comment to indicate where the event notification will be called, as suggested by Simon.
common/board_r.c | 3 +++ common/event.c | 3 +++ include/event.h | 3 +++ 3 files changed, 9 insertions(+)
diff --git a/common/board_r.c b/common/board_r.c index 56eb60fa27..1db061e65c 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -576,6 +576,9 @@ static int run_main_loop(void) #ifdef CONFIG_SANDBOX sandbox_main_loop_init(); #endif + + event_notify_null(EVT_MAIN_LOOP); + /* main_loop() can return to retry autoboot, if so just run it again */ for (;;) main_loop(); diff --git a/common/event.c b/common/event.c index 3e34550978..231b9e6ffd 100644 --- a/common/event.c +++ b/common/event.c @@ -38,6 +38,9 @@ const char *const type_name[] = {
/* fdt hooks */ "ft_fixup", + + /* main loop events */ + "main_loop", };
_Static_assert(ARRAY_SIZE(type_name) == EVT_COUNT, "event type_name size"); diff --git a/include/event.h b/include/event.h index e8f2f55c63..8089b464b1 100644 --- a/include/event.h +++ b/include/event.h @@ -34,6 +34,9 @@ enum event_t { /* Device tree fixups before booting */ EVT_FT_FIXUP,
+ /* To be called once, before calling main_loop() */ + EVT_MAIN_LOOP, + EVT_COUNT };

On Thu, Sep 15, 2022 at 01:44:44PM +0530, Sughosh Ganu wrote:
Add an event type EVT_MAIN_LOOP that can be used for registering events that need to be run after the platform has been initialised and before the main_loop function is called.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Simon Glass sjg@chromium.org
Changes since V9:
- Add a comment to indicate where the event notification will be called, as suggested by Simon.
common/board_r.c | 3 +++ common/event.c | 3 +++ include/event.h | 3 +++ 3 files changed, 9 insertions(+)
diff --git a/common/board_r.c b/common/board_r.c index 56eb60fa27..1db061e65c 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -576,6 +576,9 @@ static int run_main_loop(void) #ifdef CONFIG_SANDBOX sandbox_main_loop_init(); #endif
- event_notify_null(EVT_MAIN_LOOP);
- /* main_loop() can return to retry autoboot, if so just run it again */ for (;;) main_loop();
diff --git a/common/event.c b/common/event.c index 3e34550978..231b9e6ffd 100644 --- a/common/event.c +++ b/common/event.c @@ -38,6 +38,9 @@ const char *const type_name[] = {
/* fdt hooks */ "ft_fixup",
- /* main loop events */
- "main_loop",
};
_Static_assert(ARRAY_SIZE(type_name) == EVT_COUNT, "event type_name size"); diff --git a/include/event.h b/include/event.h index e8f2f55c63..8089b464b1 100644 --- a/include/event.h +++ b/include/event.h @@ -34,6 +34,9 @@ enum event_t { /* Device tree fixups before booting */ EVT_FT_FIXUP,
- /* To be called once, before calling main_loop() */
- EVT_MAIN_LOOP,
- EVT_COUNT
};
-- 2.34.1
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org

The FWU Multi Bank Update specification requires the Update Agent to carry out certain checks at the time of platform boot. The Update Agent is the component which is responsible for updating the firmware components and maintaining and keeping the metadata in sync.
The spec requires that the Update Agent perform the following checks at the time of boot * Sanity check of both the metadata copies maintained by the platform. * Get the boot index passed to U-Boot by the prior stage bootloader and use this value for metadata bookkeeping. * Check if the system is booting in Trial State. If the system boots in the Trial State for more than a specified number of boot counts, change the Active Bank to be booting the platform from.
Call these checks through the main loop event at the time of platform boot.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Etienne Carriere etienne.carriere@linaro.org --- Changes since V9: * Remove the unused variable active_idx, as suggested by Etienne
include/fwu.h | 13 +++ lib/efi_loader/efi_setup.c | 1 + lib/fwu_updates/fwu.c | 192 ++++++++++++++++++++++++++++++++++++- 3 files changed, 204 insertions(+), 2 deletions(-)
diff --git a/include/fwu.h b/include/fwu.h index 484289ed4f..d5f77ce83c 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -256,4 +256,17 @@ int fwu_plat_get_update_index(uint *update_idx); * */ void fwu_plat_get_bootidx(uint *boot_idx); + +/** + * fwu_update_checks_pass() - Check if FWU update can be done + * + * Check if the FWU update can be executed. The updates are + * allowed only when the platform is not in Trial State and + * the boot time checks have passed + * + * Return: 1 if OK, 0 on error + * + */ +u8 fwu_update_checks_pass(void); + #endif /* _FWU_H_ */ diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index c633fcd91e..557e97de4a 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -199,6 +199,7 @@ static efi_status_t __efi_init_early(void) goto out;
ret = efi_disk_init(); + out: return ret; } diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 8e91b7aeae..32518d6f86 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -4,10 +4,19 @@ */
#include <dm.h> +#include <efi.h> #include <efi_loader.h> +#include <efi_variable.h> +#include <event.h> #include <fwu.h> #include <fwu_mdata.h> -#include <log.h> +#include <malloc.h> + +#include <linux/errno.h> +#include <linux/types.h> + +static u8 trial_state; +static u8 boottime_check;
#include <linux/errno.h> #include <linux/types.h> @@ -16,8 +25,112 @@ #define IMAGE_ACCEPT_SET BIT(0) #define IMAGE_ACCEPT_CLEAR BIT(1)
-static int fwu_get_dev_mdata(struct udevice **dev, struct fwu_mdata *mdata) +static int trial_counter_update(u16 *trial_state_ctr) +{ + bool delete; + u32 var_attr; + efi_status_t status; + efi_uintn_t var_size; + + delete = !trial_state_ctr ? true : false; + var_size = !trial_state_ctr ? 0 : (efi_uintn_t)sizeof(*trial_state_ctr); + var_attr = !trial_state_ctr ? 0 : EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS; + status = efi_set_variable_int(u"TrialStateCtr", + &efi_global_variable_guid, + var_attr, + var_size, trial_state_ctr, false); + + if ((delete && (status != EFI_NOT_FOUND && + status != EFI_SUCCESS)) || + (!delete && status != EFI_SUCCESS)) + return -1; + + return 0; +} + +static int in_trial_state(struct fwu_mdata *mdata) +{ + u32 i, active_bank; + struct fwu_image_entry *img_entry; + struct fwu_image_bank_info *img_bank_info; + + active_bank = mdata->active_index; + img_entry = &mdata->img_entry[0]; + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { + img_bank_info = &img_entry[i].img_bank_info[active_bank]; + if (!img_bank_info->accepted) { + return 1; + } + } + + return 0; +} + +static int fwu_trial_state_check(struct udevice *dev) +{ + int ret; + efi_status_t status; + efi_uintn_t var_size; + u16 trial_state_ctr; + u32 var_attributes; + struct fwu_mdata mdata = { 0 }; + + ret = fwu_get_mdata(dev, &mdata); + if (ret) + return ret; + + if ((trial_state = in_trial_state(&mdata))) { + var_size = (efi_uintn_t)sizeof(trial_state_ctr); + log_info("System booting in Trial State\n"); + var_attributes = EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS; + status = efi_get_variable_int(u"TrialStateCtr", + &efi_global_variable_guid, + &var_attributes, + &var_size, &trial_state_ctr, + NULL); + if (status != EFI_SUCCESS) { + log_err("Unable to read TrialStateCtr variable\n"); + ret = -1; + goto out; + } + + ++trial_state_ctr; + if (trial_state_ctr > CONFIG_FWU_TRIAL_STATE_CNT) { + log_info("Trial State count exceeded. Revert back to previous_active_index\n"); + ret = fwu_revert_boot_index(); + if (ret) { + log_err("Unable to revert active_index\n"); + goto out; + } + + /* Delete the TrialStateCtr variable */ + ret = trial_counter_update(NULL); + if (ret) { + log_err("Unable to delete TrialStateCtr variable\n"); + goto out; + } + } else { + ret = trial_counter_update(&trial_state_ctr); + if (ret) { + log_err("Unable to increment TrialStateCtr variable\n"); + goto out; + } + } + } else { + /* Delete the variable */ + ret = trial_counter_update(NULL); + if (ret) { + log_err("Unable to delete TrialStateCtr variable\n"); + } + } + +out: + return ret; +}
+static int fwu_get_dev_mdata(struct udevice **dev, struct fwu_mdata *mdata) { int ret;
@@ -27,6 +140,9 @@ static int fwu_get_dev_mdata(struct udevice **dev, struct fwu_mdata *mdata) return ret; }
+ if (!mdata) + return 0; + ret = fwu_get_mdata(*dev, mdata); if (ret < 0) log_debug("Unable to get valid FWU metadata\n"); @@ -358,3 +474,75 @@ __weak int fwu_plat_get_update_index(uint *update_idx)
return ret; } + +/** + * fwu_update_checks_pass() - Check if FWU update can be done + * + * Check if the FWU update can be executed. The updates are + * allowed only when the platform is not in Trial State and + * the boot time checks have passed + * + * Return: 1 if OK, 0 on error + * + */ +u8 fwu_update_checks_pass(void) +{ + return !trial_state && boottime_check; +} + +static int fwu_boottime_checks(void *ctx, struct event *event) +{ + int ret; + struct udevice *dev; + u32 boot_idx, active_idx; + + ret = fwu_get_dev_mdata(&dev, NULL); + if (ret) + return ret; + + ret = fwu_mdata_check(dev); + 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(dev); + if (!ret) + boottime_check = 1; + + return 0; +} +EVENT_SPY(EVT_MAIN_LOOP, fwu_boottime_checks);

On Thu, Sep 15, 2022 at 3:17 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
....
diff --git a/include/fwu.h b/include/fwu.h index 484289ed4f..d5f77ce83c 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -256,4 +256,17 @@ int fwu_plat_get_update_index(uint *update_idx);
*/ void fwu_plat_get_bootidx(uint *boot_idx);
Or simply return the boot_idx instead of modifying the pointed variable
.....
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index c633fcd91e..557e97de4a 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -199,6 +199,7 @@ static efi_status_t __efi_init_early(void) goto out;
ret = efi_disk_init();
out: return ret; }
We can do without this change in this patchset :)
.....
+static int fwu_trial_state_check(struct udevice *dev) +{
int ret;
efi_status_t status;
efi_uintn_t var_size;
u16 trial_state_ctr;
u32 var_attributes;
struct fwu_mdata mdata = { 0 };
ret = fwu_get_mdata(dev, &mdata);
if (ret)
return ret;
if ((trial_state = in_trial_state(&mdata))) {
This may raise warnings on code checkers. So maybe move the assignment out of the check.
.....
+static int fwu_boottime_checks(void *ctx, struct event *event) +{
int ret;
struct udevice *dev;
u32 boot_idx, active_idx;
ret = fwu_get_dev_mdata(&dev, NULL);
if (ret)
return ret;
ret = fwu_mdata_check(dev);
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;
We may not want to do anything FWU (accept, reject, modify mdata) until we reboot, if we are recovering from last bad upgrade. So maybe not set boottime_check
cheers

On Mon, 26 Sept 2022 at 08:29, Jassi Brar jassisinghbrar@gmail.com wrote:
On Thu, Sep 15, 2022 at 3:17 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
....
diff --git a/include/fwu.h b/include/fwu.h index 484289ed4f..d5f77ce83c 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -256,4 +256,17 @@ int fwu_plat_get_update_index(uint *update_idx);
*/ void fwu_plat_get_bootidx(uint *boot_idx);
Or simply return the boot_idx instead of modifying the pointed variable
This is following the prototype that is being used for all the functions. Would prefer to have that consistency.
.....
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index c633fcd91e..557e97de4a 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -199,6 +199,7 @@ static efi_status_t __efi_init_early(void) goto out;
ret = efi_disk_init();
out: return ret; }
We can do without this change in this patchset :)
Will remove.
.....
+static int fwu_trial_state_check(struct udevice *dev) +{
int ret;
efi_status_t status;
efi_uintn_t var_size;
u16 trial_state_ctr;
u32 var_attributes;
struct fwu_mdata mdata = { 0 };
ret = fwu_get_mdata(dev, &mdata);
if (ret)
return ret;
if ((trial_state = in_trial_state(&mdata))) {
This may raise warnings on code checkers. So maybe move the assignment out of the check.
I did get a compiler warning asking me to use the parentheses around the assignment. But I can move the assignment out.
.....
+static int fwu_boottime_checks(void *ctx, struct event *event) +{
int ret;
struct udevice *dev;
u32 boot_idx, active_idx;
ret = fwu_get_dev_mdata(&dev, NULL);
if (ret)
return ret;
ret = fwu_mdata_check(dev);
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;
We may not want to do anything FWU (accept, reject, modify mdata) until we reboot, if we are recovering from last bad upgrade. So maybe not set boottime_check
Actually, the difference between the boot bank and active bank will happen when there is some kind of corruption on the media due to which the platform could not boot from the active bank(could also be due to repeated wd timeouts). What issue do you see in attempting to update the other bank in case of a mismatch.
-sughosh

On Mon, Sep 26, 2022 at 5:08 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:29, Jassi Brar jassisinghbrar@gmail.com wrote:
.....
+static int fwu_boottime_checks(void *ctx, struct event *event) +{
int ret;
struct udevice *dev;
u32 boot_idx, active_idx;
ret = fwu_get_dev_mdata(&dev, NULL);
if (ret)
return ret;
ret = fwu_mdata_check(dev);
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;
We may not want to do anything FWU (accept, reject, modify mdata) until we reboot, if we are recovering from last bad upgrade. So maybe not set boottime_check
Actually, the difference between the boot bank and active bank will happen when there is some kind of corruption on the media due to which the platform could not boot from the active bank(could also be due to repeated wd timeouts).
... which may have been caused by the last upgrade attempt, among other reasons.
fwu_trial_state_check() will never be called in this case and any subsequent fwu_update_checks_pass() will pass even if we are in trial state.
-j

On Mon, 26 Sept 2022 at 19:37, Jassi Brar jassisinghbrar@gmail.com wrote:
On Mon, Sep 26, 2022 at 5:08 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:29, Jassi Brar jassisinghbrar@gmail.com wrote:
.....
+static int fwu_boottime_checks(void *ctx, struct event *event) +{
int ret;
struct udevice *dev;
u32 boot_idx, active_idx;
ret = fwu_get_dev_mdata(&dev, NULL);
if (ret)
return ret;
ret = fwu_mdata_check(dev);
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;
We may not want to do anything FWU (accept, reject, modify mdata) until we reboot, if we are recovering from last bad upgrade. So maybe not set boottime_check
Actually, the difference between the boot bank and active bank will happen when there is some kind of corruption on the media due to which the platform could not boot from the active bank(could also be due to repeated wd timeouts).
... which may have been caused by the last upgrade attempt, among other reasons.
fwu_trial_state_check() will never be called in this case and any subsequent fwu_update_checks_pass() will pass even if we are in trial state.
If the platform is unable to boot from the updated partition, resulting in booting from a different partition, the platform is no longer in the trial state, since it has not booted from the updated partition -- determination of trial state is only based on reading the accepted bit of all images in the booted partition. I believe this would be a reason to want to change the images on the other partition from which the platform could not boot, unless that was due to some hardware error, in which case it would require manual intervention. But my point is, not allowing FWU updates in the scenario you mention does not help prevent any unwanted situation.
-sughosh

The FWU Multi Bank Update feature supports updation of firmware images to one of multiple sets(also called banks) of images. The firmware images are clubbed together in banks, with the system booting images from the active bank. Information on the images such as which bank they belong to is stored as part of the metadata structure, which is stored on the same storage media as the firmware images on a dedicated partition.
At the time of update, the metadata is read to identify the bank to which the images need to be flashed(update bank). On a successful update, the metadata is modified to set the updated bank as active bank to subsequently boot from.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V9: * Move the global variables into local variables as suggested by Ilias. * Change fwu_get_image_alt_num() name to fwu_get_image_image_index() as suggested by Takahiro. * Allow capsule updates to be called from efi_init_obj_list() with the FWU feature enabled, as suggested by Takahiro. * Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature enabled. * Define the FWU feature related functions as __maybe_unused to allow for compilation with the FWU feature disabled.
drivers/Kconfig | 2 + drivers/Makefile | 1 + include/fwu.h | 30 +++++ lib/Kconfig | 6 + lib/Makefile | 1 + lib/efi_loader/efi_capsule.c | 243 ++++++++++++++++++++++++++++++++++- lib/fwu_updates/Kconfig | 33 +++++ lib/fwu_updates/Makefile | 7 + lib/fwu_updates/fwu.c | 23 ++++ 9 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile
diff --git a/drivers/Kconfig b/drivers/Kconfig index 8b6fead351..75ac149d31 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -44,6 +44,8 @@ source "drivers/fuzz/Kconfig"
source "drivers/fpga/Kconfig"
+source "drivers/fwu-mdata/Kconfig" + source "drivers/gpio/Kconfig"
source "drivers/hwspinlock/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index eba9940231..af7ed7bdf3 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -84,6 +84,7 @@ obj-y += cache/ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ obj-$(CONFIG_FASTBOOT) += fastboot/ +obj-$(CONFIG_FWU_MDATA) += fwu-mdata/ obj-y += misc/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_NVME) += nvme/ diff --git a/include/fwu.h b/include/fwu.h index d5f77ce83c..1d15ac98da 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -60,6 +60,7 @@ struct fwu_mdata_ops { };
#define FWU_MDATA_VERSION 0x1 +#define FWU_IMAGE_ACCEPTED 0x1
/* * GUID value defined in the FWU specification for identification @@ -69,6 +70,24 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+/* +* GUID value defined in the Dependable Boot specification for +* identification of the revert capsule, used for reverting +* any image in the updated bank. +*/ +#define FWU_OS_REQUEST_FW_REVERT_GUID \ + EFI_GUID(0xacd58b4b, 0xc0e8, 0x475f, 0x99, 0xb5, \ + 0x6b, 0x3f, 0x7e, 0x07, 0xaa, 0xf0) + +/* +* GUID value defined in the Dependable Boot specification for +* identification of the accept capsule, used for accepting +* an image in the updated bank. +*/ +#define FWU_OS_REQUEST_FW_ACCEPT_GUID \ + EFI_GUID(0x0c996046, 0xbcc0, 0x4d04, 0x85, 0xec, \ + 0xe1, 0xfc, 0xed, 0xf1, 0xc6, 0xf8) + /** * fwu_get_mdata() - Get a FWU metadata copy * @dev: FWU metadata device @@ -269,4 +288,15 @@ void fwu_plat_get_bootidx(uint *boot_idx); */ u8 fwu_update_checks_pass(void);
+/** + * fwu_trial_state_ctr_start() - Start the Trial State counter + * + * Start the counter to identify the platform booting in the + * Trial State. The counter is implemented as an EFI variable. + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_trial_state_ctr_start(void); + #endif /* _FWU_H_ */ diff --git a/lib/Kconfig b/lib/Kconfig index 6121c80dc8..6abe1d0a86 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -978,3 +978,9 @@ config LMB_RESERVED_REGIONS memory blocks.
endmenu + +menu "FWU Multi Bank Updates" + +source lib/fwu_updates/Kconfig + +endmenu diff --git a/lib/Makefile b/lib/Makefile index e3deb15287..f2cfd1e428 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_FIT) += libfdt/ diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index a6b98f066a..7f431ab477 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,12 @@ 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
#ifdef CONFIG_EFI_CAPSULE_ON_DISK /* for file system access */ @@ -133,6 +140,7 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule, * @instance: Instance number * @handles: Handles of FMP drivers * @no_handles: Number of handles + * @image_index_check: Image Index check flag * * Search for Firmware Management Protocol drivers, matching the image * type, @image_type and the machine instance, @instance, from the list, @@ -144,7 +152,8 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule, */ static struct efi_firmware_management_protocol * efi_fmp_find(efi_guid_t *image_type, u8 image_index, u64 instance, - efi_handle_t *handles, efi_uintn_t no_handles) + efi_handle_t *handles, efi_uintn_t no_handles, + bool image_index_check) { efi_handle_t *handle; struct efi_firmware_management_protocol *fmp; @@ -205,7 +214,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 +398,130 @@ efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_s } #endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */
+static __maybe_unused 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 __maybe_unused efi_status_t fwu_to_efi_error(int err) +{ + efi_status_t ret; + + switch(err) { + case 0: + ret = EFI_SUCCESS; + break; + case -ENODEV: + case -ERANGE: + case -EIO: + ret = EFI_DEVICE_ERROR; + break; + case -EINVAL: + ret = EFI_INVALID_PARAMETER; + break; + default: + ret = EFI_OUT_OF_RESOURCES; + } + + return ret; +} + +static __maybe_unused 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(); + ret = fwu_to_efi_error(status); + if (ret == EFI_SUCCESS) + log_info("Reverted the FWU active_index. Recommend rebooting the system\n"); + else + log_err("Failed to revert the FWU boot index\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); + ret = fwu_to_efi_error(status); + if (ret != EFI_SUCCESS) { + log_err("Unable to get the active_index from the FWU metadata\n"); + return ret; + } + + status = fwu_accept_image(image_guid, active_idx); + ret = fwu_to_efi_error(status); + if (ret != EFI_SUCCESS) + log_err("Unable to set the Accept bit for the image %pUs\n", + image_guid); + } + + return ret; +} + +static __maybe_unused void fwu_post_update_checks( + struct efi_capsule_header *capsule, + bool *fw_accept_os, bool *capsule_update) +{ + if (fwu_empty_capsule(capsule)) + *capsule_update = false; + else + if (!*fw_accept_os) + *fw_accept_os = + capsule->flags & FW_ACCEPT_OS ? 0x1 : 0x0; +} + +static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) +{ + int status; + u32 update_index; + efi_status_t ret; + + 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; + } + + /* + * 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); + ret = fwu_to_efi_error(status); + if (ret != EFI_SUCCESS) { + log_err("Failed to update FWU metadata index values\n"); + } else { + log_debug("Successfully updated the active_index\n"); + ret = EFI_SUCCESS; + if (fw_accept_os) { + status = fwu_trial_state_ctr_start(); + if (status < 0) + ret = EFI_DEVICE_ERROR; + } + } + + return ret; +}
/** * efi_capsule_update_firmware - update firmware from capsule @@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( int item; 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; + u32 update_index; + bool fw_accept_os, image_index_check; + + 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)) + return fwu_empty_capsule_process(capsule_data); + + /* 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; + } + + image_index_check = false; + fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0; + } else { + image_index_check = true; + }
/* sanity check */ if (capsule_data->header_size < sizeof(*capsule) || @@ -455,7 +617,8 @@ static efi_status_t efi_capsule_update_firmware( fmp = efi_fmp_find(&image->update_image_type_id, image->update_image_index, image->update_hardware_instance, - handles, no_handles); + handles, no_handles, + image_index_check); if (!fmp) { log_err("FMP driver not found for firmware type %pUs, hardware instance %lld\n", &image->update_image_type_id, @@ -485,8 +648,30 @@ 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 image index 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_index(&image_type_id, + update_index, + &image_index); + ret = fwu_to_efi_error(status); + if (ret != EFI_SUCCESS) { + log_err("Unable to get the Image Index for the image type %pUs\n", + &image_type_id); + goto out; + } + log_debug("Image Index %u for Image Type Id %pUs\n", + image_index, &image_type_id); + } 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 +682,33 @@ 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); + } + ret = fwu_to_efi_error(status); + if (ret != EFI_SUCCESS) { + log_err("Unable to %s the accept bit for the image %pUs\n", + fw_accept_os ? "clear" : "set", + &image_type_id); + goto out; + } + + log_debug("%s the accepted bit for Image %pUs\n", + fw_accept_os ? "Cleared" : "Set", + &image_type_id); + } + }
out: @@ -1104,6 +1316,9 @@ efi_status_t efi_launch_capsules(void) u16 **files; unsigned int nfiles, index, i; efi_status_t ret; + bool capsule_update = true; + bool update_status = true; + bool fw_accept_os = false;
if (check_run_capsules() != EFI_SUCCESS) return EFI_SUCCESS; @@ -1131,12 +1346,19 @@ 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]); + if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) { + fwu_post_update_checks(capsule, + &fw_accept_os, + &capsule_update); + } + }
/* create CapsuleXXXX */ set_capsule_result(index, capsule, ret); @@ -1144,6 +1366,7 @@ efi_status_t efi_launch_capsules(void) free(capsule); } else { log_err("Reading capsule %ls failed\n", files[i]); + update_status = false; } /* delete a capsule either in case of success or failure */ ret = efi_capsule_delete_file(files[i]); @@ -1151,7 +1374,15 @@ 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) { + ret = fwu_post_update_process(fw_accept_os); + } 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/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig new file mode 100644 index 0000000000..78759e6618 --- /dev/null +++ b/lib/fwu_updates/Kconfig @@ -0,0 +1,33 @@ +config FWU_MULTI_BANK_UPDATE + bool "Enable FWU Multi Bank Update Feature" + depends on EFI_CAPSULE_ON_DISK + select PARTITION_TYPE_GUID + select EFI_SETUP_EARLY + imply EFI_CAPSULE_ON_DISK_EARLY + select EVENT + 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..1993088e5b --- /dev/null +++ b/lib/fwu_updates/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +# + +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 32518d6f86..7209000b56 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -490,7 +490,30 @@ u8 fwu_update_checks_pass(void) return !trial_state && boottime_check; }
+/** + * fwu_trial_state_ctr_start() - Start the Trial State counter + * + * Start the counter to identify the platform booting in the + * Trial State. The counter is implemented as an EFI variable. + * + * Return: 0 if OK, -ve on error + * + */ +int fwu_trial_state_ctr_start(void) +{ + int ret; + u16 trial_state_ctr; + + trial_state_ctr = 0; + ret = trial_counter_update(&trial_state_ctr); + if (ret) + log_err("Unable to initialise TrialStateCtr\n"); + + return ret; +} + static int fwu_boottime_checks(void *ctx, struct event *event) + { int ret; struct udevice *dev;

Hi Sughosh,
On Thu, Sep 15, 2022 at 01:44:46PM +0530, 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
Changes since V9:
- Move the global variables into local variables as suggested by Ilias.
- Change fwu_get_image_alt_num() name to fwu_get_image_image_index()
-> typo? fwu_get_image_index()?
as suggested by Takahiro.
- Allow capsule updates to be called from efi_init_obj_list() with the FWU feature enabled, as suggested by Takahiro.
- Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature enabled.
- Define the FWU feature related functions as __maybe_unused to allow for compilation with the FWU feature disabled.
drivers/Kconfig | 2 + drivers/Makefile | 1 + include/fwu.h | 30 +++++ lib/Kconfig | 6 + lib/Makefile | 1 + lib/efi_loader/efi_capsule.c | 243 ++++++++++++++++++++++++++++++++++- lib/fwu_updates/Kconfig | 33 +++++ lib/fwu_updates/Makefile | 7 + lib/fwu_updates/fwu.c | 23 ++++ 9 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile
diff --git a/drivers/Kconfig b/drivers/Kconfig index 8b6fead351..75ac149d31 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -44,6 +44,8 @@ source "drivers/fuzz/Kconfig"
source "drivers/fpga/Kconfig"
+source "drivers/fwu-mdata/Kconfig"
source "drivers/gpio/Kconfig"
source "drivers/hwspinlock/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index eba9940231..af7ed7bdf3 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -84,6 +84,7 @@ obj-y += cache/ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ obj-$(CONFIG_FASTBOOT) += fastboot/ +obj-$(CONFIG_FWU_MDATA) += fwu-mdata/ obj-y += misc/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_NVME) += nvme/ diff --git a/include/fwu.h b/include/fwu.h index d5f77ce83c..1d15ac98da 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -60,6 +60,7 @@ struct fwu_mdata_ops { };
#define FWU_MDATA_VERSION 0x1 +#define FWU_IMAGE_ACCEPTED 0x1
/*
- GUID value defined in the FWU specification for identification
@@ -69,6 +70,24 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+/* +* GUID value defined in the Dependable Boot specification for +* identification of the revert capsule, used for reverting +* any image in the updated bank. +*/ +#define FWU_OS_REQUEST_FW_REVERT_GUID \
- EFI_GUID(0xacd58b4b, 0xc0e8, 0x475f, 0x99, 0xb5, \
0x6b, 0x3f, 0x7e, 0x07, 0xaa, 0xf0)
+/* +* GUID value defined in the Dependable Boot specification for +* identification of the accept capsule, used for accepting +* an image in the updated bank. +*/ +#define FWU_OS_REQUEST_FW_ACCEPT_GUID \
- EFI_GUID(0x0c996046, 0xbcc0, 0x4d04, 0x85, 0xec, \
0xe1, 0xfc, 0xed, 0xf1, 0xc6, 0xf8)
/**
- fwu_get_mdata() - Get a FWU metadata copy
- @dev: FWU metadata device
@@ -269,4 +288,15 @@ void fwu_plat_get_bootidx(uint *boot_idx); */ u8 fwu_update_checks_pass(void);
+/**
- fwu_trial_state_ctr_start() - Start the Trial State counter
- Start the counter to identify the platform booting in the
- Trial State. The counter is implemented as an EFI variable.
- Return: 0 if OK, -ve on error
- */
+int fwu_trial_state_ctr_start(void);
#endif /* _FWU_H_ */ diff --git a/lib/Kconfig b/lib/Kconfig index 6121c80dc8..6abe1d0a86 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -978,3 +978,9 @@ config LMB_RESERVED_REGIONS memory blocks.
endmenu
+menu "FWU Multi Bank Updates"
+source lib/fwu_updates/Kconfig
+endmenu diff --git a/lib/Makefile b/lib/Makefile index e3deb15287..f2cfd1e428 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_FIT) += libfdt/ diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index a6b98f066a..7f431ab477 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,12 @@ 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
#ifdef CONFIG_EFI_CAPSULE_ON_DISK /* for file system access */ @@ -133,6 +140,7 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule,
- @instance: Instance number
- @handles: Handles of FMP drivers
- @no_handles: Number of handles
- @image_index_check: Image Index check flag
- Search for Firmware Management Protocol drivers, matching the image
- type, @image_type and the machine instance, @instance, from the list,
@@ -144,7 +152,8 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule, */ static struct efi_firmware_management_protocol * efi_fmp_find(efi_guid_t *image_type, u8 image_index, u64 instance,
efi_handle_t *handles, efi_uintn_t no_handles)
efi_handle_t *handles, efi_uintn_t no_handles,
bool image_index_check)
{ efi_handle_t *handle; struct efi_firmware_management_protocol *fmp; @@ -205,7 +214,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 +398,130 @@ efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_s } #endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */
+static __maybe_unused 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 __maybe_unused efi_status_t fwu_to_efi_error(int err) +{
- efi_status_t ret;
- switch(err) {
- case 0:
ret = EFI_SUCCESS;
break;
- case -ENODEV:
- case -ERANGE:
- case -EIO:
ret = EFI_DEVICE_ERROR;
break;
- case -EINVAL:
ret = EFI_INVALID_PARAMETER;
break;
- default:
ret = EFI_OUT_OF_RESOURCES;
- }
- return ret;
+}
+static __maybe_unused 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();
ret = fwu_to_efi_error(status);
if (ret == EFI_SUCCESS)
log_info("Reverted the FWU active_index. Recommend rebooting the system\n");
else
log_err("Failed to revert the FWU boot index\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);
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to get the active_index from the FWU metadata\n");
return ret;
}
status = fwu_accept_image(image_guid, active_idx);
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS)
log_err("Unable to set the Accept bit for the image %pUs\n",
image_guid);
- }
- return ret;
+}
+static __maybe_unused void fwu_post_update_checks(
- struct efi_capsule_header *capsule,
- bool *fw_accept_os, bool *capsule_update)
+{
- if (fwu_empty_capsule(capsule))
*capsule_update = false;
- else
if (!*fw_accept_os)
*fw_accept_os =
capsule->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
+}
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) +{
- int status;
- u32 update_index;
- efi_status_t ret;
- 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;
- }
- /*
* 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);
- ret = fwu_to_efi_error(status);
- if (ret != EFI_SUCCESS) {
log_err("Failed to update FWU metadata index values\n");
- } else {
log_debug("Successfully updated the active_index\n");
ret = EFI_SUCCESS;
if (fw_accept_os) {
status = fwu_trial_state_ctr_start();
if (status < 0)
ret = EFI_DEVICE_ERROR;
}
- }
- return ret;
+}
/**
- efi_capsule_update_firmware - update firmware from capsule
@@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( int item; 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;
u32 update_index;
bool fw_accept_os, image_index_check;
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))
return fwu_empty_capsule_process(capsule_data);
/* 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;
}
image_index_check = false;
fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
} else {
image_index_check = true;
}
/* sanity check */ if (capsule_data->header_size < sizeof(*capsule) ||
@@ -455,7 +617,8 @@ static efi_status_t efi_capsule_update_firmware( fmp = efi_fmp_find(&image->update_image_type_id, image->update_image_index, image->update_hardware_instance,
handles, no_handles);
handles, no_handles,
if (!fmp) { log_err("FMP driver not found for firmware type %pUs, hardware instance %lld\n", &image->update_image_type_id,image_index_check);
@@ -485,8 +648,30 @@ 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 image index 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_index(&image_type_id,
update_index,
&image_index);
AS I said in my comment to v9, this function should be moved in FMP driver, that is, efi_firmware.c and contained in set_image().
You try to use different image_index's to distinguish A and B banks, but this kind of usage is quite implementation-dependent since other firmware framework may use a different approach to support multiple banks.
Please remember that, from the viewpoint of API, image_index must be unique whether it is on A bank or B bank as it is used to identify a specific firmware image within a device, not a "physical" location.
Please re-think.
-Takahiro Akashi
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to get the Image Index for the image type %pUs\n",
&image_type_id);
goto out;
}
log_debug("Image Index %u for Image Type Id %pUs\n",
image_index, &image_type_id);
} 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 +682,33 @@ 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);
}
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to %s the accept bit for the image %pUs\n",
fw_accept_os ? "clear" : "set",
&image_type_id);
goto out;
}
log_debug("%s the accepted bit for Image %pUs\n",
fw_accept_os ? "Cleared" : "Set",
&image_type_id);
}
- }
out: @@ -1104,6 +1316,9 @@ efi_status_t efi_launch_capsules(void) u16 **files; unsigned int nfiles, index, i; efi_status_t ret;
bool capsule_update = true;
bool update_status = true;
bool fw_accept_os = false;
if (check_run_capsules() != EFI_SUCCESS) return EFI_SUCCESS;
@@ -1131,12 +1346,19 @@ 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]);
if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
fwu_post_update_checks(capsule,
&fw_accept_os,
&capsule_update);
}
} /* create CapsuleXXXX */ set_capsule_result(index, capsule, ret);
@@ -1144,6 +1366,7 @@ efi_status_t efi_launch_capsules(void) free(capsule); } else { log_err("Reading capsule %ls failed\n", files[i]);
} /* delete a capsule either in case of success or failure */ ret = efi_capsule_delete_file(files[i]);update_status = false;
@@ -1151,7 +1374,15 @@ 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) {
ret = fwu_post_update_process(fw_accept_os);
} 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/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig new file mode 100644 index 0000000000..78759e6618 --- /dev/null +++ b/lib/fwu_updates/Kconfig @@ -0,0 +1,33 @@ +config FWU_MULTI_BANK_UPDATE
- bool "Enable FWU Multi Bank Update Feature"
- depends on EFI_CAPSULE_ON_DISK
- select PARTITION_TYPE_GUID
- select EFI_SETUP_EARLY
- imply EFI_CAPSULE_ON_DISK_EARLY
- select EVENT
- 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..1993088e5b --- /dev/null +++ b/lib/fwu_updates/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +#
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 32518d6f86..7209000b56 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -490,7 +490,30 @@ u8 fwu_update_checks_pass(void) return !trial_state && boottime_check; }
+/**
- fwu_trial_state_ctr_start() - Start the Trial State counter
- Start the counter to identify the platform booting in the
- Trial State. The counter is implemented as an EFI variable.
- Return: 0 if OK, -ve on error
- */
+int fwu_trial_state_ctr_start(void) +{
- int ret;
- u16 trial_state_ctr;
- trial_state_ctr = 0;
- ret = trial_counter_update(&trial_state_ctr);
- if (ret)
log_err("Unable to initialise TrialStateCtr\n");
- return ret;
+}
static int fwu_boottime_checks(void *ctx, struct event *event)
{ int ret; struct udevice *dev; -- 2.34.1

() hi Takahiro,
On Fri, 16 Sept 2022 at 07:17, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Hi Sughosh,
On Thu, Sep 15, 2022 at 01:44:46PM +0530, 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
Changes since V9:
- Move the global variables into local variables as suggested by Ilias.
- Change fwu_get_image_alt_num() name to fwu_get_image_image_index()
-> typo? fwu_get_image_index()?
as suggested by Takahiro.
- Allow capsule updates to be called from efi_init_obj_list() with the FWU feature enabled, as suggested by Takahiro.
- Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature enabled.
- Define the FWU feature related functions as __maybe_unused to allow for compilation with the FWU feature disabled.
drivers/Kconfig | 2 + drivers/Makefile | 1 + include/fwu.h | 30 +++++ lib/Kconfig | 6 + lib/Makefile | 1 + lib/efi_loader/efi_capsule.c | 243 ++++++++++++++++++++++++++++++++++- lib/fwu_updates/Kconfig | 33 +++++ lib/fwu_updates/Makefile | 7 + lib/fwu_updates/fwu.c | 23 ++++ 9 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile
diff --git a/drivers/Kconfig b/drivers/Kconfig index 8b6fead351..75ac149d31 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -44,6 +44,8 @@ source "drivers/fuzz/Kconfig"
source "drivers/fpga/Kconfig"
+source "drivers/fwu-mdata/Kconfig"
source "drivers/gpio/Kconfig"
source "drivers/hwspinlock/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index eba9940231..af7ed7bdf3 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -84,6 +84,7 @@ obj-y += cache/ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ obj-$(CONFIG_FASTBOOT) += fastboot/ +obj-$(CONFIG_FWU_MDATA) += fwu-mdata/ obj-y += misc/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_NVME) += nvme/ diff --git a/include/fwu.h b/include/fwu.h index d5f77ce83c..1d15ac98da 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -60,6 +60,7 @@ struct fwu_mdata_ops { };
#define FWU_MDATA_VERSION 0x1 +#define FWU_IMAGE_ACCEPTED 0x1
/*
- GUID value defined in the FWU specification for identification
@@ -69,6 +70,24 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+/* +* GUID value defined in the Dependable Boot specification for +* identification of the revert capsule, used for reverting +* any image in the updated bank. +*/ +#define FWU_OS_REQUEST_FW_REVERT_GUID \
EFI_GUID(0xacd58b4b, 0xc0e8, 0x475f, 0x99, 0xb5, \
0x6b, 0x3f, 0x7e, 0x07, 0xaa, 0xf0)
+/* +* GUID value defined in the Dependable Boot specification for +* identification of the accept capsule, used for accepting +* an image in the updated bank. +*/ +#define FWU_OS_REQUEST_FW_ACCEPT_GUID \
EFI_GUID(0x0c996046, 0xbcc0, 0x4d04, 0x85, 0xec, \
0xe1, 0xfc, 0xed, 0xf1, 0xc6, 0xf8)
/**
- fwu_get_mdata() - Get a FWU metadata copy
- @dev: FWU metadata device
@@ -269,4 +288,15 @@ void fwu_plat_get_bootidx(uint *boot_idx); */ u8 fwu_update_checks_pass(void);
+/**
- fwu_trial_state_ctr_start() - Start the Trial State counter
- Start the counter to identify the platform booting in the
- Trial State. The counter is implemented as an EFI variable.
- Return: 0 if OK, -ve on error
- */
+int fwu_trial_state_ctr_start(void);
#endif /* _FWU_H_ */ diff --git a/lib/Kconfig b/lib/Kconfig index 6121c80dc8..6abe1d0a86 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -978,3 +978,9 @@ config LMB_RESERVED_REGIONS memory blocks.
endmenu
+menu "FWU Multi Bank Updates"
+source lib/fwu_updates/Kconfig
+endmenu diff --git a/lib/Makefile b/lib/Makefile index e3deb15287..f2cfd1e428 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_FIT) += libfdt/ diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index a6b98f066a..7f431ab477 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,12 @@ 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
#ifdef CONFIG_EFI_CAPSULE_ON_DISK /* for file system access */ @@ -133,6 +140,7 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule,
- @instance: Instance number
- @handles: Handles of FMP drivers
- @no_handles: Number of handles
- @image_index_check: Image Index check flag
- Search for Firmware Management Protocol drivers, matching the image
- type, @image_type and the machine instance, @instance, from the list,
@@ -144,7 +152,8 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule, */ static struct efi_firmware_management_protocol * efi_fmp_find(efi_guid_t *image_type, u8 image_index, u64 instance,
efi_handle_t *handles, efi_uintn_t no_handles)
efi_handle_t *handles, efi_uintn_t no_handles,
bool image_index_check)
{ efi_handle_t *handle; struct efi_firmware_management_protocol *fmp; @@ -205,7 +214,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 +398,130 @@ efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_s } #endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */
+static __maybe_unused 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 __maybe_unused efi_status_t fwu_to_efi_error(int err) +{
efi_status_t ret;
switch(err) {
case 0:
ret = EFI_SUCCESS;
break;
case -ENODEV:
case -ERANGE:
case -EIO:
ret = EFI_DEVICE_ERROR;
break;
case -EINVAL:
ret = EFI_INVALID_PARAMETER;
break;
default:
ret = EFI_OUT_OF_RESOURCES;
}
return ret;
+}
+static __maybe_unused 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();
ret = fwu_to_efi_error(status);
if (ret == EFI_SUCCESS)
log_info("Reverted the FWU active_index. Recommend rebooting the system\n");
else
log_err("Failed to revert the FWU boot index\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);
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to get the active_index from the FWU metadata\n");
return ret;
}
status = fwu_accept_image(image_guid, active_idx);
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS)
log_err("Unable to set the Accept bit for the image %pUs\n",
image_guid);
}
return ret;
+}
+static __maybe_unused void fwu_post_update_checks(
struct efi_capsule_header *capsule,
bool *fw_accept_os, bool *capsule_update)
+{
if (fwu_empty_capsule(capsule))
*capsule_update = false;
else
if (!*fw_accept_os)
*fw_accept_os =
capsule->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
+}
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) +{
int status;
u32 update_index;
efi_status_t ret;
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;
}
/*
* 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);
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Failed to update FWU metadata index values\n");
} else {
log_debug("Successfully updated the active_index\n");
ret = EFI_SUCCESS;
if (fw_accept_os) {
status = fwu_trial_state_ctr_start();
if (status < 0)
ret = EFI_DEVICE_ERROR;
}
}
return ret;
+}
/**
- efi_capsule_update_firmware - update firmware from capsule
@@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( int item; 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;
u32 update_index;
bool fw_accept_os, image_index_check;
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))
return fwu_empty_capsule_process(capsule_data);
/* 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;
}
image_index_check = false;
fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
} else {
image_index_check = true;
} /* sanity check */ if (capsule_data->header_size < sizeof(*capsule) ||
@@ -455,7 +617,8 @@ static efi_status_t efi_capsule_update_firmware( fmp = efi_fmp_find(&image->update_image_type_id, image->update_image_index, image->update_hardware_instance,
handles, no_handles);
handles, no_handles,
image_index_check); if (!fmp) { log_err("FMP driver not found for firmware type %pUs, hardware instance %lld\n", &image->update_image_type_id,
@@ -485,8 +648,30 @@ 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 image index 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_index(&image_type_id,
update_index,
&image_index);
AS I said in my comment to v9, this function should be moved in FMP driver, that is, efi_firmware.c and contained in set_image().
Okay. I had replied to your review comment and for this specific comment, I had mentioned that I would prefer keeping this in the capsule driver. Since you did not object to that, I was under the assumption that you are fine with what I had said.
I looked at moving this to the FMP's set_image function. However, there is an issue in that the fwu_get_image_index() function needs to be passed the ImageTypeId GUID value for getting the image index. However, the set_image function has not been passed this GUID. Unless we use some global variable, it would not be possible to move this function to the set_image function.
You try to use different image_index's to distinguish A and B banks, but this kind of usage is quite implementation-dependent since other firmware framework may use a different approach to support multiple banks.
True, but even with this implementation, that underlying framework can be abstracted. If, in the future, we have an option for multiple frameworks for performing the update, the fwu_get_image_index() can be extended to support those multiple framework implementations. The API is just getting the image index for the image payload, and the image index will remain irrespective of the underlying framework for doing the updates.
-sughosh
Please remember that, from the viewpoint of API, image_index must be unique whether it is on A bank or B bank as it is used to identify a specific firmware image within a device, not a "physical" location.
Please re-think.
-Takahiro Akashi
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to get the Image Index for the image type %pUs\n",
&image_type_id);
goto out;
}
log_debug("Image Index %u for Image Type Id %pUs\n",
image_index, &image_type_id);
} 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 +682,33 @@ 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);
}
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to %s the accept bit for the image %pUs\n",
fw_accept_os ? "clear" : "set",
&image_type_id);
goto out;
}
log_debug("%s the accepted bit for Image %pUs\n",
fw_accept_os ? "Cleared" : "Set",
&image_type_id);
}
}
out: @@ -1104,6 +1316,9 @@ efi_status_t efi_launch_capsules(void) u16 **files; unsigned int nfiles, index, i; efi_status_t ret;
bool capsule_update = true;
bool update_status = true;
bool fw_accept_os = false; if (check_run_capsules() != EFI_SUCCESS) return EFI_SUCCESS;
@@ -1131,12 +1346,19 @@ 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]);
if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
fwu_post_update_checks(capsule,
&fw_accept_os,
&capsule_update);
}
} /* create CapsuleXXXX */ set_capsule_result(index, capsule, ret);
@@ -1144,6 +1366,7 @@ efi_status_t efi_launch_capsules(void) free(capsule); } else { log_err("Reading capsule %ls failed\n", files[i]);
update_status = false; } /* delete a capsule either in case of success or failure */ ret = efi_capsule_delete_file(files[i]);
@@ -1151,7 +1374,15 @@ 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) {
ret = fwu_post_update_process(fw_accept_os);
} 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/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig new file mode 100644 index 0000000000..78759e6618 --- /dev/null +++ b/lib/fwu_updates/Kconfig @@ -0,0 +1,33 @@ +config FWU_MULTI_BANK_UPDATE
bool "Enable FWU Multi Bank Update Feature"
depends on EFI_CAPSULE_ON_DISK
select PARTITION_TYPE_GUID
select EFI_SETUP_EARLY
imply EFI_CAPSULE_ON_DISK_EARLY
select EVENT
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..1993088e5b --- /dev/null +++ b/lib/fwu_updates/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +#
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 32518d6f86..7209000b56 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -490,7 +490,30 @@ u8 fwu_update_checks_pass(void) return !trial_state && boottime_check; }
+/**
- fwu_trial_state_ctr_start() - Start the Trial State counter
- Start the counter to identify the platform booting in the
- Trial State. The counter is implemented as an EFI variable.
- Return: 0 if OK, -ve on error
- */
+int fwu_trial_state_ctr_start(void) +{
int ret;
u16 trial_state_ctr;
trial_state_ctr = 0;
ret = trial_counter_update(&trial_state_ctr);
if (ret)
log_err("Unable to initialise TrialStateCtr\n");
return ret;
+}
static int fwu_boottime_checks(void *ctx, struct event *event)
{ int ret; struct udevice *dev; -- 2.34.1

On Fri, Sep 16, 2022 at 10:52:11AM +0530, Sughosh Ganu wrote:
() hi Takahiro,
On Fri, 16 Sept 2022 at 07:17, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Hi Sughosh,
On Thu, Sep 15, 2022 at 01:44:46PM +0530, 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
Changes since V9:
- Move the global variables into local variables as suggested by Ilias.
- Change fwu_get_image_alt_num() name to fwu_get_image_image_index()
-> typo? fwu_get_image_index()?
as suggested by Takahiro.
- Allow capsule updates to be called from efi_init_obj_list() with the FWU feature enabled, as suggested by Takahiro.
- Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature enabled.
- Define the FWU feature related functions as __maybe_unused to allow for compilation with the FWU feature disabled.
drivers/Kconfig | 2 + drivers/Makefile | 1 + include/fwu.h | 30 +++++ lib/Kconfig | 6 + lib/Makefile | 1 + lib/efi_loader/efi_capsule.c | 243 ++++++++++++++++++++++++++++++++++- lib/fwu_updates/Kconfig | 33 +++++ lib/fwu_updates/Makefile | 7 + lib/fwu_updates/fwu.c | 23 ++++ 9 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile
diff --git a/drivers/Kconfig b/drivers/Kconfig index 8b6fead351..75ac149d31 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -44,6 +44,8 @@ source "drivers/fuzz/Kconfig"
source "drivers/fpga/Kconfig"
+source "drivers/fwu-mdata/Kconfig"
source "drivers/gpio/Kconfig"
source "drivers/hwspinlock/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index eba9940231..af7ed7bdf3 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -84,6 +84,7 @@ obj-y += cache/ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ obj-$(CONFIG_FASTBOOT) += fastboot/ +obj-$(CONFIG_FWU_MDATA) += fwu-mdata/ obj-y += misc/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_NVME) += nvme/ diff --git a/include/fwu.h b/include/fwu.h index d5f77ce83c..1d15ac98da 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -60,6 +60,7 @@ struct fwu_mdata_ops { };
#define FWU_MDATA_VERSION 0x1 +#define FWU_IMAGE_ACCEPTED 0x1
/*
- GUID value defined in the FWU specification for identification
@@ -69,6 +70,24 @@ struct fwu_mdata_ops { EFI_GUID(0x8a7a84a0, 0x8387, 0x40f6, 0xab, 0x41, \ 0xa8, 0xb9, 0xa5, 0xa6, 0x0d, 0x23)
+/* +* GUID value defined in the Dependable Boot specification for +* identification of the revert capsule, used for reverting +* any image in the updated bank. +*/ +#define FWU_OS_REQUEST_FW_REVERT_GUID \
EFI_GUID(0xacd58b4b, 0xc0e8, 0x475f, 0x99, 0xb5, \
0x6b, 0x3f, 0x7e, 0x07, 0xaa, 0xf0)
+/* +* GUID value defined in the Dependable Boot specification for +* identification of the accept capsule, used for accepting +* an image in the updated bank. +*/ +#define FWU_OS_REQUEST_FW_ACCEPT_GUID \
EFI_GUID(0x0c996046, 0xbcc0, 0x4d04, 0x85, 0xec, \
0xe1, 0xfc, 0xed, 0xf1, 0xc6, 0xf8)
/**
- fwu_get_mdata() - Get a FWU metadata copy
- @dev: FWU metadata device
@@ -269,4 +288,15 @@ void fwu_plat_get_bootidx(uint *boot_idx); */ u8 fwu_update_checks_pass(void);
+/**
- fwu_trial_state_ctr_start() - Start the Trial State counter
- Start the counter to identify the platform booting in the
- Trial State. The counter is implemented as an EFI variable.
- Return: 0 if OK, -ve on error
- */
+int fwu_trial_state_ctr_start(void);
#endif /* _FWU_H_ */ diff --git a/lib/Kconfig b/lib/Kconfig index 6121c80dc8..6abe1d0a86 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -978,3 +978,9 @@ config LMB_RESERVED_REGIONS memory blocks.
endmenu
+menu "FWU Multi Bank Updates"
+source lib/fwu_updates/Kconfig
+endmenu diff --git a/lib/Makefile b/lib/Makefile index e3deb15287..f2cfd1e428 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_FIT) += libfdt/ diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index a6b98f066a..7f431ab477 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,12 @@ 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
#ifdef CONFIG_EFI_CAPSULE_ON_DISK /* for file system access */ @@ -133,6 +140,7 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule,
- @instance: Instance number
- @handles: Handles of FMP drivers
- @no_handles: Number of handles
- @image_index_check: Image Index check flag
- Search for Firmware Management Protocol drivers, matching the image
- type, @image_type and the machine instance, @instance, from the list,
@@ -144,7 +152,8 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule, */ static struct efi_firmware_management_protocol * efi_fmp_find(efi_guid_t *image_type, u8 image_index, u64 instance,
efi_handle_t *handles, efi_uintn_t no_handles)
efi_handle_t *handles, efi_uintn_t no_handles,
bool image_index_check)
{ efi_handle_t *handle; struct efi_firmware_management_protocol *fmp; @@ -205,7 +214,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 +398,130 @@ efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_s } #endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */
+static __maybe_unused 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 __maybe_unused efi_status_t fwu_to_efi_error(int err) +{
efi_status_t ret;
switch(err) {
case 0:
ret = EFI_SUCCESS;
break;
case -ENODEV:
case -ERANGE:
case -EIO:
ret = EFI_DEVICE_ERROR;
break;
case -EINVAL:
ret = EFI_INVALID_PARAMETER;
break;
default:
ret = EFI_OUT_OF_RESOURCES;
}
return ret;
+}
+static __maybe_unused 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();
ret = fwu_to_efi_error(status);
if (ret == EFI_SUCCESS)
log_info("Reverted the FWU active_index. Recommend rebooting the system\n");
else
log_err("Failed to revert the FWU boot index\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);
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to get the active_index from the FWU metadata\n");
return ret;
}
status = fwu_accept_image(image_guid, active_idx);
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS)
log_err("Unable to set the Accept bit for the image %pUs\n",
image_guid);
}
return ret;
+}
+static __maybe_unused void fwu_post_update_checks(
struct efi_capsule_header *capsule,
bool *fw_accept_os, bool *capsule_update)
+{
if (fwu_empty_capsule(capsule))
*capsule_update = false;
else
if (!*fw_accept_os)
*fw_accept_os =
capsule->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
+}
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) +{
int status;
u32 update_index;
efi_status_t ret;
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;
}
/*
* 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);
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Failed to update FWU metadata index values\n");
} else {
log_debug("Successfully updated the active_index\n");
ret = EFI_SUCCESS;
if (fw_accept_os) {
status = fwu_trial_state_ctr_start();
if (status < 0)
ret = EFI_DEVICE_ERROR;
}
}
return ret;
+}
/**
- efi_capsule_update_firmware - update firmware from capsule
@@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( int item; 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;
u32 update_index;
bool fw_accept_os, image_index_check;
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))
return fwu_empty_capsule_process(capsule_data);
/* 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;
}
image_index_check = false;
fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
} else {
image_index_check = true;
} /* sanity check */ if (capsule_data->header_size < sizeof(*capsule) ||
@@ -455,7 +617,8 @@ static efi_status_t efi_capsule_update_firmware( fmp = efi_fmp_find(&image->update_image_type_id, image->update_image_index, image->update_hardware_instance,
handles, no_handles);
handles, no_handles,
image_index_check); if (!fmp) { log_err("FMP driver not found for firmware type %pUs, hardware instance %lld\n", &image->update_image_type_id,
@@ -485,8 +648,30 @@ 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 image index 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_index(&image_type_id,
update_index,
&image_index);
AS I said in my comment to v9, this function should be moved in FMP driver, that is, efi_firmware.c and contained in set_image().
Okay. I had replied to your review comment and for this specific comment, I had mentioned that I would prefer keeping this in the capsule driver. Since you did not object to that, I was under the assumption that you are fine with what I had said.
I looked at moving this to the FMP's set_image function. However, there is an issue in that the fwu_get_image_index() function needs to be passed the ImageTypeId GUID value for getting the image index. However, the set_image function has not been passed this GUID. Unless we use some global variable, it would not be possible to move this function to the set_image function.
I doubt it. Because FMP driver is looked for with image type id at efi_fmp_find(), it should know who it is. After you change in the past, current FMP drivers, either FIT or RAW, are bound only to a single GUID. Right?
You try to use different image_index's to distinguish A and B banks, but this kind of usage is quite implementation-dependent since other firmware framework may use a different approach to support multiple banks.
True, but even with this implementation, that underlying framework can be abstracted. If, in the future, we have an option for multiple frameworks for performing the update, the fwu_get_image_index() can be extended to support those multiple framework implementations. The API
I can't image how. My point is that a caller of set_image() can and should pass an unique (and the same) index id whether the working firmware is on A or B bank.
I think that all the visible part of A/B update in efi_capsule.c is a handling of accept/revert capsules.
-Takahiro Akashi
is just getting the image index for the image payload, and the image index will remain irrespective of the underlying framework for doing the updates.
-sughosh
Please remember that, from the viewpoint of API, image_index must be unique whether it is on A bank or B bank as it is used to identify a specific firmware image within a device, not a "physical" location.
Please re-think.
-Takahiro Akashi
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to get the Image Index for the image type %pUs\n",
&image_type_id);
goto out;
}
log_debug("Image Index %u for Image Type Id %pUs\n",
image_index, &image_type_id);
} 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 +682,33 @@ 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);
}
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to %s the accept bit for the image %pUs\n",
fw_accept_os ? "clear" : "set",
&image_type_id);
goto out;
}
log_debug("%s the accepted bit for Image %pUs\n",
fw_accept_os ? "Cleared" : "Set",
&image_type_id);
}
}
out: @@ -1104,6 +1316,9 @@ efi_status_t efi_launch_capsules(void) u16 **files; unsigned int nfiles, index, i; efi_status_t ret;
bool capsule_update = true;
bool update_status = true;
bool fw_accept_os = false; if (check_run_capsules() != EFI_SUCCESS) return EFI_SUCCESS;
@@ -1131,12 +1346,19 @@ 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]);
if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
fwu_post_update_checks(capsule,
&fw_accept_os,
&capsule_update);
}
} /* create CapsuleXXXX */ set_capsule_result(index, capsule, ret);
@@ -1144,6 +1366,7 @@ efi_status_t efi_launch_capsules(void) free(capsule); } else { log_err("Reading capsule %ls failed\n", files[i]);
update_status = false; } /* delete a capsule either in case of success or failure */ ret = efi_capsule_delete_file(files[i]);
@@ -1151,7 +1374,15 @@ 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) {
ret = fwu_post_update_process(fw_accept_os);
} 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/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig new file mode 100644 index 0000000000..78759e6618 --- /dev/null +++ b/lib/fwu_updates/Kconfig @@ -0,0 +1,33 @@ +config FWU_MULTI_BANK_UPDATE
bool "Enable FWU Multi Bank Update Feature"
depends on EFI_CAPSULE_ON_DISK
select PARTITION_TYPE_GUID
select EFI_SETUP_EARLY
imply EFI_CAPSULE_ON_DISK_EARLY
select EVENT
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..1993088e5b --- /dev/null +++ b/lib/fwu_updates/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +#
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 32518d6f86..7209000b56 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -490,7 +490,30 @@ u8 fwu_update_checks_pass(void) return !trial_state && boottime_check; }
+/**
- fwu_trial_state_ctr_start() - Start the Trial State counter
- Start the counter to identify the platform booting in the
- Trial State. The counter is implemented as an EFI variable.
- Return: 0 if OK, -ve on error
- */
+int fwu_trial_state_ctr_start(void) +{
int ret;
u16 trial_state_ctr;
trial_state_ctr = 0;
ret = trial_counter_update(&trial_state_ctr);
if (ret)
log_err("Unable to initialise TrialStateCtr\n");
return ret;
+}
static int fwu_boottime_checks(void *ctx, struct event *event)
{ int ret; struct udevice *dev; -- 2.34.1

hi Takahiro,
On Fri, 16 Sept 2022 at 12:20, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Fri, Sep 16, 2022 at 10:52:11AM +0530, Sughosh Ganu wrote:
() hi Takahiro,
On Fri, 16 Sept 2022 at 07:17, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Hi Sughosh,
On Thu, Sep 15, 2022 at 01:44:46PM +0530, 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
Changes since V9:
- Move the global variables into local variables as suggested by Ilias.
- Change fwu_get_image_alt_num() name to fwu_get_image_image_index()
-> typo? fwu_get_image_index()?
as suggested by Takahiro.
- Allow capsule updates to be called from efi_init_obj_list() with the FWU feature enabled, as suggested by Takahiro.
- Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature enabled.
- Define the FWU feature related functions as __maybe_unused to allow for compilation with the FWU feature disabled.
drivers/Kconfig | 2 + drivers/Makefile | 1 + include/fwu.h | 30 +++++ lib/Kconfig | 6 + lib/Makefile | 1 + lib/efi_loader/efi_capsule.c | 243 ++++++++++++++++++++++++++++++++++- lib/fwu_updates/Kconfig | 33 +++++ lib/fwu_updates/Makefile | 7 + lib/fwu_updates/fwu.c | 23 ++++ 9 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile
<snip>
/**
- efi_capsule_update_firmware - update firmware from capsule
@@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( int item; 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;
u32 update_index;
bool fw_accept_os, image_index_check;
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))
return fwu_empty_capsule_process(capsule_data);
/* 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;
}
image_index_check = false;
fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
} else {
image_index_check = true;
} /* sanity check */ if (capsule_data->header_size < sizeof(*capsule) ||
@@ -455,7 +617,8 @@ static efi_status_t efi_capsule_update_firmware( fmp = efi_fmp_find(&image->update_image_type_id, image->update_image_index, image->update_hardware_instance,
handles, no_handles);
handles, no_handles,
image_index_check); if (!fmp) { log_err("FMP driver not found for firmware type %pUs, hardware instance %lld\n", &image->update_image_type_id,
@@ -485,8 +648,30 @@ 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 image index 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_index(&image_type_id,
update_index,
&image_index);
AS I said in my comment to v9, this function should be moved in FMP driver, that is, efi_firmware.c and contained in set_image().
Okay. I had replied to your review comment and for this specific comment, I had mentioned that I would prefer keeping this in the capsule driver. Since you did not object to that, I was under the assumption that you are fine with what I had said.
I looked at moving this to the FMP's set_image function. However, there is an issue in that the fwu_get_image_index() function needs to be passed the ImageTypeId GUID value for getting the image index. However, the set_image function has not been passed this GUID. Unless we use some global variable, it would not be possible to move this function to the set_image function.
I doubt it. Because FMP driver is looked for with image type id at efi_fmp_find(), it should know who it is. After you change in the past, current FMP drivers, either FIT or RAW, are bound only to a single GUID. Right?
With the recent change that I had made, we do need different GUIDs for different images in the capsule, but the FMP instance will be the same for all raw images, and similarly for all FIT images. But the set_image function does not know for which image the function has been called. Multiple images of a given type(raw/FIT) can use the same set_image function.
You try to use different image_index's to distinguish A and B banks, but this kind of usage is quite implementation-dependent since other firmware framework may use a different approach to support multiple banks.
True, but even with this implementation, that underlying framework can be abstracted. If, in the future, we have an option for multiple frameworks for performing the update, the fwu_get_image_index() can be extended to support those multiple framework implementations. The API
I can't image how. My point is that a caller of set_image() can and should pass an unique (and the same) index id whether the working firmware is on A or B bank.
We have discussed this earlier as well. What you say is true for the normal capsule update. However, for the FWU(A/B) updates, the image index is going to be calculated at run-time, based on the partition(bank) to which the image needs to be written to. Which is the sole purpose of having the fwu_get_image_index() API. I could have moved the function out of the efi_capsule.c to the FMP's set_image functions, but like I mentioned earlier, the set_image function does not know the ImageTypeId of the image for which it has been called -- since the image_index is a parameter being passed to the set_image function, we need to compute it earlier, before calling the function.
-sughosh
I think that all the visible part of A/B update in efi_capsule.c is a handling of accept/revert capsules.
-Takahiro Akashi
is just getting the image index for the image payload, and the image index will remain irrespective of the underlying framework for doing the updates.
-sughosh
Please remember that, from the viewpoint of API, image_index must be unique whether it is on A bank or B bank as it is used to identify a specific firmware image within a device, not a "physical" location.
Please re-think.
-Takahiro Akashi
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to get the Image Index for the image type %pUs\n",
&image_type_id);
goto out;
}
log_debug("Image Index %u for Image Type Id %pUs\n",
image_index, &image_type_id);
} 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 +682,33 @@ 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);
}
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to %s the accept bit for the image %pUs\n",
fw_accept_os ? "clear" : "set",
&image_type_id);
goto out;
}
log_debug("%s the accepted bit for Image %pUs\n",
fw_accept_os ? "Cleared" : "Set",
&image_type_id);
}
}
out: @@ -1104,6 +1316,9 @@ efi_status_t efi_launch_capsules(void) u16 **files; unsigned int nfiles, index, i; efi_status_t ret;
bool capsule_update = true;
bool update_status = true;
bool fw_accept_os = false; if (check_run_capsules() != EFI_SUCCESS) return EFI_SUCCESS;
@@ -1131,12 +1346,19 @@ 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]);
if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
fwu_post_update_checks(capsule,
&fw_accept_os,
&capsule_update);
}
} /* create CapsuleXXXX */ set_capsule_result(index, capsule, ret);
@@ -1144,6 +1366,7 @@ efi_status_t efi_launch_capsules(void) free(capsule); } else { log_err("Reading capsule %ls failed\n", files[i]);
update_status = false; } /* delete a capsule either in case of success or failure */ ret = efi_capsule_delete_file(files[i]);
@@ -1151,7 +1374,15 @@ 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) {
ret = fwu_post_update_process(fw_accept_os);
} 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/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig new file mode 100644 index 0000000000..78759e6618 --- /dev/null +++ b/lib/fwu_updates/Kconfig @@ -0,0 +1,33 @@ +config FWU_MULTI_BANK_UPDATE
bool "Enable FWU Multi Bank Update Feature"
depends on EFI_CAPSULE_ON_DISK
select PARTITION_TYPE_GUID
select EFI_SETUP_EARLY
imply EFI_CAPSULE_ON_DISK_EARLY
select EVENT
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..1993088e5b --- /dev/null +++ b/lib/fwu_updates/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +#
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 32518d6f86..7209000b56 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -490,7 +490,30 @@ u8 fwu_update_checks_pass(void) return !trial_state && boottime_check; }
+/**
- fwu_trial_state_ctr_start() - Start the Trial State counter
- Start the counter to identify the platform booting in the
- Trial State. The counter is implemented as an EFI variable.
- Return: 0 if OK, -ve on error
- */
+int fwu_trial_state_ctr_start(void) +{
int ret;
u16 trial_state_ctr;
trial_state_ctr = 0;
ret = trial_counter_update(&trial_state_ctr);
if (ret)
log_err("Unable to initialise TrialStateCtr\n");
return ret;
+}
static int fwu_boottime_checks(void *ctx, struct event *event)
{ int ret; struct udevice *dev; -- 2.34.1

On Fri, Sep 16, 2022 at 04:24:35PM +0530, Sughosh Ganu wrote:
hi Takahiro,
On Fri, 16 Sept 2022 at 12:20, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Fri, Sep 16, 2022 at 10:52:11AM +0530, Sughosh Ganu wrote:
() hi Takahiro,
On Fri, 16 Sept 2022 at 07:17, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Hi Sughosh,
On Thu, Sep 15, 2022 at 01:44:46PM +0530, 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
Changes since V9:
- Move the global variables into local variables as suggested by Ilias.
- Change fwu_get_image_alt_num() name to fwu_get_image_image_index()
-> typo? fwu_get_image_index()?
as suggested by Takahiro.
- Allow capsule updates to be called from efi_init_obj_list() with the FWU feature enabled, as suggested by Takahiro.
- Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature enabled.
- Define the FWU feature related functions as __maybe_unused to allow for compilation with the FWU feature disabled.
drivers/Kconfig | 2 + drivers/Makefile | 1 + include/fwu.h | 30 +++++ lib/Kconfig | 6 + lib/Makefile | 1 + lib/efi_loader/efi_capsule.c | 243 ++++++++++++++++++++++++++++++++++- lib/fwu_updates/Kconfig | 33 +++++ lib/fwu_updates/Makefile | 7 + lib/fwu_updates/fwu.c | 23 ++++ 9 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile
<snip>
/**
- efi_capsule_update_firmware - update firmware from capsule
@@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( int item; 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;
u32 update_index;
bool fw_accept_os, image_index_check;
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))
return fwu_empty_capsule_process(capsule_data);
/* 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;
}
image_index_check = false;
fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
} else {
image_index_check = true;
} /* sanity check */ if (capsule_data->header_size < sizeof(*capsule) ||
@@ -455,7 +617,8 @@ static efi_status_t efi_capsule_update_firmware( fmp = efi_fmp_find(&image->update_image_type_id, image->update_image_index, image->update_hardware_instance,
handles, no_handles);
handles, no_handles,
image_index_check); if (!fmp) { log_err("FMP driver not found for firmware type %pUs, hardware instance %lld\n", &image->update_image_type_id,
@@ -485,8 +648,30 @@ 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 image index 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_index(&image_type_id,
update_index,
&image_index);
AS I said in my comment to v9, this function should be moved in FMP driver, that is, efi_firmware.c and contained in set_image().
Okay. I had replied to your review comment and for this specific comment, I had mentioned that I would prefer keeping this in the capsule driver. Since you did not object to that, I was under the assumption that you are fine with what I had said.
I looked at moving this to the FMP's set_image function. However, there is an issue in that the fwu_get_image_index() function needs to be passed the ImageTypeId GUID value for getting the image index. However, the set_image function has not been passed this GUID. Unless we use some global variable, it would not be possible to move this function to the set_image function.
I doubt it. Because FMP driver is looked for with image type id at efi_fmp_find(), it should know who it is. After you change in the past, current FMP drivers, either FIT or RAW, are bound only to a single GUID. Right?
With the recent change that I had made, we do need different GUIDs for different images in the capsule, but the FMP instance will be the same for all raw images, and similarly for all FIT images. But the set_image function does not know for which image the function has been called. Multiple images of a given type(raw/FIT) can use the same set_image function.
You try to use different image_index's to distinguish A and B banks, but this kind of usage is quite implementation-dependent since other firmware framework may use a different approach to support multiple banks.
True, but even with this implementation, that underlying framework can be abstracted. If, in the future, we have an option for multiple frameworks for performing the update, the fwu_get_image_index() can be extended to support those multiple framework implementations. The API
I can't image how. My point is that a caller of set_image() can and should pass an unique (and the same) index id whether the working firmware is on A or B bank.
We have discussed this earlier as well. What you say is true for the normal capsule update. However, for the FWU(A/B) updates, the image index is going to be calculated at run-time, based on the partition(bank) to which the image needs to be written to. Which is
It sound weird to me. If we assume what you said here, FMP driver is expected to handle a capsule image solely based on "index" but without knowing which type (id) the image belongs to. I don' think it can be universal assumption for all kind of FMP's. Why must we have different semantics of set_image() for normal (non-A/B-update) case and A/B update case?
-Takahiro Akashi
the sole purpose of having the fwu_get_image_index() API. I could have moved the function out of the efi_capsule.c to the FMP's set_image functions, but like I mentioned earlier, the set_image function does not know the ImageTypeId of the image for which it has been called -- since the image_index is a parameter being passed to the set_image function, we need to compute it earlier, before calling the function.
-sughosh
I think that all the visible part of A/B update in efi_capsule.c is a handling of accept/revert capsules.
-Takahiro Akashi
is just getting the image index for the image payload, and the image index will remain irrespective of the underlying framework for doing the updates.
-sughosh
Please remember that, from the viewpoint of API, image_index must be unique whether it is on A bank or B bank as it is used to identify a specific firmware image within a device, not a "physical" location.
Please re-think.
-Takahiro Akashi
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to get the Image Index for the image type %pUs\n",
&image_type_id);
goto out;
}
log_debug("Image Index %u for Image Type Id %pUs\n",
image_index, &image_type_id);
} 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 +682,33 @@ 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);
}
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to %s the accept bit for the image %pUs\n",
fw_accept_os ? "clear" : "set",
&image_type_id);
goto out;
}
log_debug("%s the accepted bit for Image %pUs\n",
fw_accept_os ? "Cleared" : "Set",
&image_type_id);
}
}
out: @@ -1104,6 +1316,9 @@ efi_status_t efi_launch_capsules(void) u16 **files; unsigned int nfiles, index, i; efi_status_t ret;
bool capsule_update = true;
bool update_status = true;
bool fw_accept_os = false; if (check_run_capsules() != EFI_SUCCESS) return EFI_SUCCESS;
@@ -1131,12 +1346,19 @@ 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]);
if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
fwu_post_update_checks(capsule,
&fw_accept_os,
&capsule_update);
}
} /* create CapsuleXXXX */ set_capsule_result(index, capsule, ret);
@@ -1144,6 +1366,7 @@ efi_status_t efi_launch_capsules(void) free(capsule); } else { log_err("Reading capsule %ls failed\n", files[i]);
update_status = false; } /* delete a capsule either in case of success or failure */ ret = efi_capsule_delete_file(files[i]);
@@ -1151,7 +1374,15 @@ 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) {
ret = fwu_post_update_process(fw_accept_os);
} 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/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig new file mode 100644 index 0000000000..78759e6618 --- /dev/null +++ b/lib/fwu_updates/Kconfig @@ -0,0 +1,33 @@ +config FWU_MULTI_BANK_UPDATE
bool "Enable FWU Multi Bank Update Feature"
depends on EFI_CAPSULE_ON_DISK
select PARTITION_TYPE_GUID
select EFI_SETUP_EARLY
imply EFI_CAPSULE_ON_DISK_EARLY
select EVENT
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..1993088e5b --- /dev/null +++ b/lib/fwu_updates/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +#
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 32518d6f86..7209000b56 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -490,7 +490,30 @@ u8 fwu_update_checks_pass(void) return !trial_state && boottime_check; }
+/**
- fwu_trial_state_ctr_start() - Start the Trial State counter
- Start the counter to identify the platform booting in the
- Trial State. The counter is implemented as an EFI variable.
- Return: 0 if OK, -ve on error
- */
+int fwu_trial_state_ctr_start(void) +{
int ret;
u16 trial_state_ctr;
trial_state_ctr = 0;
ret = trial_counter_update(&trial_state_ctr);
if (ret)
log_err("Unable to initialise TrialStateCtr\n");
return ret;
+}
static int fwu_boottime_checks(void *ctx, struct event *event)
{ int ret; struct udevice *dev; -- 2.34.1

On Tue, 20 Sept 2022 at 13:46, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Fri, Sep 16, 2022 at 04:24:35PM +0530, Sughosh Ganu wrote:
hi Takahiro,
On Fri, 16 Sept 2022 at 12:20, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Fri, Sep 16, 2022 at 10:52:11AM +0530, Sughosh Ganu wrote:
() hi Takahiro,
On Fri, 16 Sept 2022 at 07:17, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Hi Sughosh,
On Thu, Sep 15, 2022 at 01:44:46PM +0530, 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
Changes since V9:
- Move the global variables into local variables as suggested by Ilias.
- Change fwu_get_image_alt_num() name to fwu_get_image_image_index()
-> typo? fwu_get_image_index()?
as suggested by Takahiro.
- Allow capsule updates to be called from efi_init_obj_list() with the FWU feature enabled, as suggested by Takahiro.
- Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature enabled.
- Define the FWU feature related functions as __maybe_unused to allow for compilation with the FWU feature disabled.
drivers/Kconfig | 2 + drivers/Makefile | 1 + include/fwu.h | 30 +++++ lib/Kconfig | 6 + lib/Makefile | 1 + lib/efi_loader/efi_capsule.c | 243 ++++++++++++++++++++++++++++++++++- lib/fwu_updates/Kconfig | 33 +++++ lib/fwu_updates/Makefile | 7 + lib/fwu_updates/fwu.c | 23 ++++ 9 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 lib/fwu_updates/Kconfig create mode 100644 lib/fwu_updates/Makefile
<snip>
/**
- efi_capsule_update_firmware - update firmware from capsule
@@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( int item; 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;
u32 update_index;
bool fw_accept_os, image_index_check;
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))
return fwu_empty_capsule_process(capsule_data);
/* 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;
}
image_index_check = false;
fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
} else {
image_index_check = true;
} /* sanity check */ if (capsule_data->header_size < sizeof(*capsule) ||
@@ -455,7 +617,8 @@ static efi_status_t efi_capsule_update_firmware( fmp = efi_fmp_find(&image->update_image_type_id, image->update_image_index, image->update_hardware_instance,
handles, no_handles);
handles, no_handles,
image_index_check); if (!fmp) { log_err("FMP driver not found for firmware type %pUs, hardware instance %lld\n", &image->update_image_type_id,
@@ -485,8 +648,30 @@ 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 image index 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_index(&image_type_id,
update_index,
&image_index);
AS I said in my comment to v9, this function should be moved in FMP driver, that is, efi_firmware.c and contained in set_image().
Okay. I had replied to your review comment and for this specific comment, I had mentioned that I would prefer keeping this in the capsule driver. Since you did not object to that, I was under the assumption that you are fine with what I had said.
I looked at moving this to the FMP's set_image function. However, there is an issue in that the fwu_get_image_index() function needs to be passed the ImageTypeId GUID value for getting the image index. However, the set_image function has not been passed this GUID. Unless we use some global variable, it would not be possible to move this function to the set_image function.
I doubt it. Because FMP driver is looked for with image type id at efi_fmp_find(), it should know who it is. After you change in the past, current FMP drivers, either FIT or RAW, are bound only to a single GUID. Right?
With the recent change that I had made, we do need different GUIDs for different images in the capsule, but the FMP instance will be the same for all raw images, and similarly for all FIT images. But the set_image function does not know for which image the function has been called. Multiple images of a given type(raw/FIT) can use the same set_image function.
You try to use different image_index's to distinguish A and B banks, but this kind of usage is quite implementation-dependent since other firmware framework may use a different approach to support multiple banks.
True, but even with this implementation, that underlying framework can be abstracted. If, in the future, we have an option for multiple frameworks for performing the update, the fwu_get_image_index() can be extended to support those multiple framework implementations. The API
I can't image how. My point is that a caller of set_image() can and should pass an unique (and the same) index id whether the working firmware is on A or B bank.
We have discussed this earlier as well. What you say is true for the normal capsule update. However, for the FWU(A/B) updates, the image index is going to be calculated at run-time, based on the partition(bank) to which the image needs to be written to. Which is
It sound weird to me. If we assume what you said here, FMP driver is expected to handle a capsule image solely based on "index" but without knowing which type (id) the image belongs to.
I am referring to the set_image(SetImage) FMP function. That is indeed solely based on the image index -- there is no ImageTypeId being passed to the SetImage FMP function.
efi_status_t (EFIAPI *set_image)( struct efi_firmware_management_protocol *this, u8 image_index, const void *image, efi_uintn_t image_size, const void *vendor_code, efi_status_t (*progress)(efi_uintn_t completion), u16 **abort_reason);
I don' think it can be universal assumption for all kind of FMP's. Why must we have different semantics of set_image() for normal (non-A/B-update) case and A/B update case?
The semantics are not different for set_image() for the two cases. It's just that the image_index value being passed would be different for the A/B update case, since that value is being calculated at run-time.
-sughosh
-Takahiro Akashi
the sole purpose of having the fwu_get_image_index() API. I could have moved the function out of the efi_capsule.c to the FMP's set_image functions, but like I mentioned earlier, the set_image function does not know the ImageTypeId of the image for which it has been called -- since the image_index is a parameter being passed to the set_image function, we need to compute it earlier, before calling the function.
-sughosh
I think that all the visible part of A/B update in efi_capsule.c is a handling of accept/revert capsules.
-Takahiro Akashi
is just getting the image index for the image payload, and the image index will remain irrespective of the underlying framework for doing the updates.
-sughosh
Please remember that, from the viewpoint of API, image_index must be unique whether it is on A bank or B bank as it is used to identify a specific firmware image within a device, not a "physical" location.
Please re-think.
-Takahiro Akashi
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to get the Image Index for the image type %pUs\n",
&image_type_id);
goto out;
}
log_debug("Image Index %u for Image Type Id %pUs\n",
image_index, &image_type_id);
} 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 +682,33 @@ 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);
}
ret = fwu_to_efi_error(status);
if (ret != EFI_SUCCESS) {
log_err("Unable to %s the accept bit for the image %pUs\n",
fw_accept_os ? "clear" : "set",
&image_type_id);
goto out;
}
log_debug("%s the accepted bit for Image %pUs\n",
fw_accept_os ? "Cleared" : "Set",
&image_type_id);
}
}
out: @@ -1104,6 +1316,9 @@ efi_status_t efi_launch_capsules(void) u16 **files; unsigned int nfiles, index, i; efi_status_t ret;
bool capsule_update = true;
bool update_status = true;
bool fw_accept_os = false; if (check_run_capsules() != EFI_SUCCESS) return EFI_SUCCESS;
@@ -1131,12 +1346,19 @@ 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]);
if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
fwu_post_update_checks(capsule,
&fw_accept_os,
&capsule_update);
}
} /* create CapsuleXXXX */ set_capsule_result(index, capsule, ret);
@@ -1144,6 +1366,7 @@ efi_status_t efi_launch_capsules(void) free(capsule); } else { log_err("Reading capsule %ls failed\n", files[i]);
update_status = false; } /* delete a capsule either in case of success or failure */ ret = efi_capsule_delete_file(files[i]);
@@ -1151,7 +1374,15 @@ 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) {
ret = fwu_post_update_process(fw_accept_os);
} 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/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig new file mode 100644 index 0000000000..78759e6618 --- /dev/null +++ b/lib/fwu_updates/Kconfig @@ -0,0 +1,33 @@ +config FWU_MULTI_BANK_UPDATE
bool "Enable FWU Multi Bank Update Feature"
depends on EFI_CAPSULE_ON_DISK
select PARTITION_TYPE_GUID
select EFI_SETUP_EARLY
imply EFI_CAPSULE_ON_DISK_EARLY
select EVENT
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..1993088e5b --- /dev/null +++ b/lib/fwu_updates/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2022, Linaro Limited +#
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 32518d6f86..7209000b56 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -490,7 +490,30 @@ u8 fwu_update_checks_pass(void) return !trial_state && boottime_check; }
+/**
- fwu_trial_state_ctr_start() - Start the Trial State counter
- Start the counter to identify the platform booting in the
- Trial State. The counter is implemented as an EFI variable.
- Return: 0 if OK, -ve on error
- */
+int fwu_trial_state_ctr_start(void) +{
int ret;
u16 trial_state_ctr;
trial_state_ctr = 0;
ret = trial_counter_update(&trial_state_ctr);
if (ret)
log_err("Unable to initialise TrialStateCtr\n");
return ret;
+}
static int fwu_boottime_checks(void *ctx, struct event *event)
{ int ret; struct udevice *dev; -- 2.34.1

Sughosh,
On Tue, Sep 20, 2022 at 06:34:12PM +0530, Sughosh Ganu wrote:
On Tue, 20 Sept 2022 at 13:46, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Fri, Sep 16, 2022 at 04:24:35PM +0530, Sughosh Ganu wrote:
hi Takahiro,
On Fri, 16 Sept 2022 at 12:20, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Fri, Sep 16, 2022 at 10:52:11AM +0530, Sughosh Ganu wrote:
() hi Takahiro,
On Fri, 16 Sept 2022 at 07:17, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Hi Sughosh,
On Thu, Sep 15, 2022 at 01:44:46PM +0530, 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 > --- > Changes since V9: > * Move the global variables into local variables as suggested by > Ilias. > * Change fwu_get_image_alt_num() name to fwu_get_image_image_index()
-> typo? fwu_get_image_index()?
> as suggested by Takahiro. > * Allow capsule updates to be called from efi_init_obj_list() with the > FWU feature enabled, as suggested by Takahiro. > * Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature > enabled. > * Define the FWU feature related functions as __maybe_unused to allow > for compilation with the FWU feature disabled. > > drivers/Kconfig | 2 + > drivers/Makefile | 1 + > include/fwu.h | 30 +++++ > lib/Kconfig | 6 + > lib/Makefile | 1 + > lib/efi_loader/efi_capsule.c | 243 ++++++++++++++++++++++++++++++++++- > lib/fwu_updates/Kconfig | 33 +++++ > lib/fwu_updates/Makefile | 7 + > lib/fwu_updates/fwu.c | 23 ++++ > 9 files changed, 340 insertions(+), 6 deletions(-) > create mode 100644 lib/fwu_updates/Kconfig > create mode 100644 lib/fwu_updates/Makefile >
<snip>
> > /** > * efi_capsule_update_firmware - update firmware from capsule > @@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( > int item; > 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; > + u32 update_index; > + bool fw_accept_os, image_index_check; > + > + 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)) > + return fwu_empty_capsule_process(capsule_data); > + > + /* 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; > + } > + > + image_index_check = false; > + fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0; > + } else { > + image_index_check = true; > + } > > /* sanity check */ > if (capsule_data->header_size < sizeof(*capsule) || > @@ -455,7 +617,8 @@ static efi_status_t efi_capsule_update_firmware( > fmp = efi_fmp_find(&image->update_image_type_id, > image->update_image_index, > image->update_hardware_instance, > - handles, no_handles); > + handles, no_handles, > + image_index_check); > if (!fmp) { > log_err("FMP driver not found for firmware type %pUs, hardware instance %lld\n", > &image->update_image_type_id, > @@ -485,8 +648,30 @@ 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 image index 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_index(&image_type_id, > + update_index, > + &image_index);
AS I said in my comment to v9, this function should be moved in FMP driver, that is, efi_firmware.c and contained in set_image().
Okay. I had replied to your review comment and for this specific comment, I had mentioned that I would prefer keeping this in the capsule driver. Since you did not object to that, I was under the assumption that you are fine with what I had said.
I looked at moving this to the FMP's set_image function. However, there is an issue in that the fwu_get_image_index() function needs to be passed the ImageTypeId GUID value for getting the image index. However, the set_image function has not been passed this GUID. Unless we use some global variable, it would not be possible to move this function to the set_image function.
I doubt it. Because FMP driver is looked for with image type id at efi_fmp_find(), it should know who it is. After you change in the past, current FMP drivers, either FIT or RAW, are bound only to a single GUID. Right?
With the recent change that I had made, we do need different GUIDs for different images in the capsule, but the FMP instance will be the same for all raw images, and similarly for all FIT images. But the set_image function does not know for which image the function has been called. Multiple images of a given type(raw/FIT) can use the same set_image function.
You try to use different image_index's to distinguish A and B banks, but this kind of usage is quite implementation-dependent since other firmware framework may use a different approach to support multiple banks.
True, but even with this implementation, that underlying framework can be abstracted. If, in the future, we have an option for multiple frameworks for performing the update, the fwu_get_image_index() can be extended to support those multiple framework implementations. The API
I can't image how. My point is that a caller of set_image() can and should pass an unique (and the same) index id whether the working firmware is on A or B bank.
We have discussed this earlier as well. What you say is true for the normal capsule update. However, for the FWU(A/B) updates, the image index is going to be calculated at run-time, based on the partition(bank) to which the image needs to be written to. Which is
It sound weird to me. If we assume what you said here, FMP driver is expected to handle a capsule image solely based on "index" but without knowing which type (id) the image belongs to.
I am referring to the set_image(SetImage) FMP function. That is indeed solely based on the image index -- there is no ImageTypeId being passed to the SetImage FMP function.
Before further discussion, let me ask in another way.
What contents do you expect GetImageInfo() returns, in particular, for "ImageIndex" for a specific firmware image?
UEFI specification says, in 23.1 Firmware Management Protocol, ===(a)=== The definition of GET_IMAGE_INFO(): DescriptorCount A pointer to the location in which firmware returns the number of descriptors or firmware images within this device.
...
The definition of EFI_FIRMWARE_IMAGE_DESCRIPTOR: ImageIndex A unique number identifying the firmware image within the device. The number is between 1 and DescriptorCount. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ImageTypeId A unique GUID identifying the firmware image type. === ===
So whatever "ImageTypeId" is, ImageIndex will and can uniquely identify a specific firmware image in terms of a table of EFI_FIRMWARE_IMAGE_DESCRIPTOR's which is returned by GetImageInfo() of a FMP driver.
Furthermore,
in 23.3 Delivering Capsules Containing Updates to Firmware Management Protocol, ===(b)=== The definition of EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER: UpdateImageTypeId Used to identify device firmware targeted by this update. This guid is matched by system firmware against ImageTypeId field within a EFI_FIRMWARE_IMAGE_DESCRIPTOR returned by an instance of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo() in the system. UpdateImageIndex Passed as ImageIndex in call to EFI_FIRMWARE_MANAGEMENT_PROTOCOL.SetImage() ^^^^^^^^^^^^^ === ===
So I believe that "UpdateImageIndex" in a header of capsule file is expected to be passed *directly* to SetImage() as one of arguments, assuming that a content of capsule image header is irrelevant to which bank the firmware is to be applied at any time.
-Takahiro Akashi
efi_status_t (EFIAPI *set_image)( struct efi_firmware_management_protocol *this, u8 image_index, const void *image, efi_uintn_t image_size, const void *vendor_code, efi_status_t (*progress)(efi_uintn_t completion), u16 **abort_reason);
I don' think it can be universal assumption for all kind of FMP's. Why must we have different semantics of set_image() for normal (non-A/B-update) case and A/B update case?
The semantics are not different for set_image() for the two cases. It's just that the image_index value being passed would be different for the A/B update case, since that value is being calculated at run-time.
-sughosh
-Takahiro Akashi
the sole purpose of having the fwu_get_image_index() API. I could have moved the function out of the efi_capsule.c to the FMP's set_image functions, but like I mentioned earlier, the set_image function does not know the ImageTypeId of the image for which it has been called -- since the image_index is a parameter being passed to the set_image function, we need to compute it earlier, before calling the function.
-sughosh
I think that all the visible part of A/B update in efi_capsule.c is a handling of accept/revert capsules.
-Takahiro Akashi
is just getting the image index for the image payload, and the image index will remain irrespective of the underlying framework for doing the updates.
-sughosh
Please remember that, from the viewpoint of API, image_index must be unique whether it is on A bank or B bank as it is used to identify a specific firmware image within a device, not a "physical" location.
Please re-think.
-Takahiro Akashi
> + ret = fwu_to_efi_error(status); > + if (ret != EFI_SUCCESS) { > + log_err("Unable to get the Image Index for the image type %pUs\n", > + &image_type_id); > + goto out; > + } > + log_debug("Image Index %u for Image Type Id %pUs\n", > + image_index, &image_type_id); > + } 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 +682,33 @@ 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); > + } > + ret = fwu_to_efi_error(status); > + if (ret != EFI_SUCCESS) { > + log_err("Unable to %s the accept bit for the image %pUs\n", > + fw_accept_os ? "clear" : "set", > + &image_type_id); > + goto out; > + } > + > + log_debug("%s the accepted bit for Image %pUs\n", > + fw_accept_os ? "Cleared" : "Set", > + &image_type_id); > + } > + > } > > out: > @@ -1104,6 +1316,9 @@ efi_status_t efi_launch_capsules(void) > u16 **files; > unsigned int nfiles, index, i; > efi_status_t ret; > + bool capsule_update = true; > + bool update_status = true; > + bool fw_accept_os = false; > > if (check_run_capsules() != EFI_SUCCESS) > return EFI_SUCCESS; > @@ -1131,12 +1346,19 @@ 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]); > + if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) { > + fwu_post_update_checks(capsule, > + &fw_accept_os, > + &capsule_update); > + } > + } > > /* create CapsuleXXXX */ > set_capsule_result(index, capsule, ret); > @@ -1144,6 +1366,7 @@ efi_status_t efi_launch_capsules(void) > free(capsule); > } else { > log_err("Reading capsule %ls failed\n", files[i]); > + update_status = false; > } > /* delete a capsule either in case of success or failure */ > ret = efi_capsule_delete_file(files[i]); > @@ -1151,7 +1374,15 @@ 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) { > + ret = fwu_post_update_process(fw_accept_os); > + } 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/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig > new file mode 100644 > index 0000000000..78759e6618 > --- /dev/null > +++ b/lib/fwu_updates/Kconfig > @@ -0,0 +1,33 @@ > +config FWU_MULTI_BANK_UPDATE > + bool "Enable FWU Multi Bank Update Feature" > + depends on EFI_CAPSULE_ON_DISK > + select PARTITION_TYPE_GUID > + select EFI_SETUP_EARLY > + imply EFI_CAPSULE_ON_DISK_EARLY > + select EVENT > + 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..1993088e5b > --- /dev/null > +++ b/lib/fwu_updates/Makefile > @@ -0,0 +1,7 @@ > +# SPDX-License-Identifier: GPL-2.0-or-later > +# > +# Copyright (c) 2022, Linaro Limited > +# > + > +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o > +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o > diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c > index 32518d6f86..7209000b56 100644 > --- a/lib/fwu_updates/fwu.c > +++ b/lib/fwu_updates/fwu.c > @@ -490,7 +490,30 @@ u8 fwu_update_checks_pass(void) > return !trial_state && boottime_check; > } > > +/** > + * fwu_trial_state_ctr_start() - Start the Trial State counter > + * > + * Start the counter to identify the platform booting in the > + * Trial State. The counter is implemented as an EFI variable. > + * > + * Return: 0 if OK, -ve on error > + * > + */ > +int fwu_trial_state_ctr_start(void) > +{ > + int ret; > + u16 trial_state_ctr; > + > + trial_state_ctr = 0; > + ret = trial_counter_update(&trial_state_ctr); > + if (ret) > + log_err("Unable to initialise TrialStateCtr\n"); > + > + return ret; > +} > + > static int fwu_boottime_checks(void *ctx, struct event *event) > + > { > int ret; > struct udevice *dev; > -- > 2.34.1 >

hi Takahiro,
On Wed, 21 Sept 2022 at 10:58, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Sughosh,
On Tue, Sep 20, 2022 at 06:34:12PM +0530, Sughosh Ganu wrote:
On Tue, 20 Sept 2022 at 13:46, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Fri, Sep 16, 2022 at 04:24:35PM +0530, Sughosh Ganu wrote:
hi Takahiro,
On Fri, 16 Sept 2022 at 12:20, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Fri, Sep 16, 2022 at 10:52:11AM +0530, Sughosh Ganu wrote:
() hi Takahiro,
On Fri, 16 Sept 2022 at 07:17, Takahiro Akashi takahiro.akashi@linaro.org wrote: > > Hi Sughosh, > > On Thu, Sep 15, 2022 at 01:44:46PM +0530, 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 > > --- > > Changes since V9: > > * Move the global variables into local variables as suggested by > > Ilias. > > * Change fwu_get_image_alt_num() name to fwu_get_image_image_index() > > -> typo? fwu_get_image_index()? > > > as suggested by Takahiro. > > * Allow capsule updates to be called from efi_init_obj_list() with the > > FWU feature enabled, as suggested by Takahiro. > > * Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature > > enabled. > > * Define the FWU feature related functions as __maybe_unused to allow > > for compilation with the FWU feature disabled. > > > > drivers/Kconfig | 2 + > > drivers/Makefile | 1 + > > include/fwu.h | 30 +++++ > > lib/Kconfig | 6 + > > lib/Makefile | 1 + > > lib/efi_loader/efi_capsule.c | 243 ++++++++++++++++++++++++++++++++++- > > lib/fwu_updates/Kconfig | 33 +++++ > > lib/fwu_updates/Makefile | 7 + > > lib/fwu_updates/fwu.c | 23 ++++ > > 9 files changed, 340 insertions(+), 6 deletions(-) > > create mode 100644 lib/fwu_updates/Kconfig > > create mode 100644 lib/fwu_updates/Makefile > >
<snip>
> > > > /** > > * efi_capsule_update_firmware - update firmware from capsule > > @@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( > > int item; > > 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; > > + u32 update_index; > > + bool fw_accept_os, image_index_check; > > + > > + 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)) > > + return fwu_empty_capsule_process(capsule_data); > > + > > + /* 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; > > + } > > + > > + image_index_check = false; > > + fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0; > > + } else { > > + image_index_check = true; > > + } > > > > /* sanity check */ > > if (capsule_data->header_size < sizeof(*capsule) || > > @@ -455,7 +617,8 @@ static efi_status_t efi_capsule_update_firmware( > > fmp = efi_fmp_find(&image->update_image_type_id, > > image->update_image_index, > > image->update_hardware_instance, > > - handles, no_handles); > > + handles, no_handles, > > + image_index_check); > > if (!fmp) { > > log_err("FMP driver not found for firmware type %pUs, hardware instance %lld\n", > > &image->update_image_type_id, > > @@ -485,8 +648,30 @@ 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 image index 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_index(&image_type_id, > > + update_index, > > + &image_index); > > AS I said in my comment to v9, this function should be moved in FMP driver, > that is, efi_firmware.c and contained in set_image().
Okay. I had replied to your review comment and for this specific comment, I had mentioned that I would prefer keeping this in the capsule driver. Since you did not object to that, I was under the assumption that you are fine with what I had said.
I looked at moving this to the FMP's set_image function. However, there is an issue in that the fwu_get_image_index() function needs to be passed the ImageTypeId GUID value for getting the image index. However, the set_image function has not been passed this GUID. Unless we use some global variable, it would not be possible to move this function to the set_image function.
I doubt it. Because FMP driver is looked for with image type id at efi_fmp_find(), it should know who it is. After you change in the past, current FMP drivers, either FIT or RAW, are bound only to a single GUID. Right?
With the recent change that I had made, we do need different GUIDs for different images in the capsule, but the FMP instance will be the same for all raw images, and similarly for all FIT images. But the set_image function does not know for which image the function has been called. Multiple images of a given type(raw/FIT) can use the same set_image function.
> > You try to use different image_index's to distinguish A and B banks, but > this kind of usage is quite implementation-dependent since other firmware > framework may use a different approach to support multiple banks.
True, but even with this implementation, that underlying framework can be abstracted. If, in the future, we have an option for multiple frameworks for performing the update, the fwu_get_image_index() can be extended to support those multiple framework implementations. The API
I can't image how. My point is that a caller of set_image() can and should pass an unique (and the same) index id whether the working firmware is on A or B bank.
We have discussed this earlier as well. What you say is true for the normal capsule update. However, for the FWU(A/B) updates, the image index is going to be calculated at run-time, based on the partition(bank) to which the image needs to be written to. Which is
It sound weird to me. If we assume what you said here, FMP driver is expected to handle a capsule image solely based on "index" but without knowing which type (id) the image belongs to.
I am referring to the set_image(SetImage) FMP function. That is indeed solely based on the image index -- there is no ImageTypeId being passed to the SetImage FMP function.
Before further discussion, let me ask in another way.
What contents do you expect GetImageInfo() returns, in particular, for "ImageIndex" for a specific firmware image?
UEFI specification says, in 23.1 Firmware Management Protocol, ===(a)=== The definition of GET_IMAGE_INFO(): DescriptorCount A pointer to the location in which firmware returns the number of descriptors or firmware images within this device.
...
The definition of EFI_FIRMWARE_IMAGE_DESCRIPTOR: ImageIndex A unique number identifying the firmware image within the device. The number is between 1 and DescriptorCount. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ImageTypeId A unique GUID identifying the firmware image type. === ===
So whatever "ImageTypeId" is, ImageIndex will and can uniquely identify a specific firmware image in terms of a table of EFI_FIRMWARE_IMAGE_DESCRIPTOR's which is returned by GetImageInfo() of a FMP driver.
Yes, this is true for the normal capsule update scenario. However, in the case of A/B updates, the above condition is no longer true. We will have a single ImageTypeId with multiple ImageIndex values, since we have multiple banks. And if we have to extrapolate the ImageIndex of BankB based on the value of ImageIndex in BankA, we will have to put that restriction on the order of placement of the images on the storage media -- this would mean that the order of images in all banks will have to be the same. While in case of FWU A/B updates,we do not need that restriction, since the FWU metadata does not put any restriction on placement of images. Which is why the image_index is being derived at run-time based on the values of ImageTypeId and update bank.
Actually, this is something pretty similar to the case of updating FIT images. Even in case of FIT updates, we use a single image_index(0x1) for the entire FIT image, and then the actual FIT update code is figuring out the location of the constituent images at run-time based on parsing of the FIT header. In the case of FWU(A/B) updates, we figure out the image_index at run-time based on the values of the ImageTypeId and the update bank. The way I see it, this is not affecting the native capsule update code behaviour. Just that in the case of A/B updates, the way of deriving the ImageIndex is different.
-sughosh
Furthermore,
in 23.3 Delivering Capsules Containing Updates to Firmware Management Protocol, ===(b)=== The definition of EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER: UpdateImageTypeId Used to identify device firmware targeted by this update. This guid is matched by system firmware against ImageTypeId field within a EFI_FIRMWARE_IMAGE_DESCRIPTOR returned by an instance of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo() in the system. UpdateImageIndex Passed as ImageIndex in call to EFI_FIRMWARE_MANAGEMENT_PROTOCOL.SetImage() ^^^^^^^^^^^^^ === ===
So I believe that "UpdateImageIndex" in a header of capsule file is expected to be passed *directly* to SetImage() as one of arguments, assuming that a content of capsule image header is irrelevant to which bank the firmware is to be applied at any time.
-Takahiro Akashi
efi_status_t (EFIAPI *set_image)( struct efi_firmware_management_protocol *this, u8 image_index, const void *image, efi_uintn_t image_size, const void *vendor_code, efi_status_t (*progress)(efi_uintn_t completion), u16 **abort_reason);
I don' think it can be universal assumption for all kind of FMP's. Why must we have different semantics of set_image() for normal (non-A/B-update) case and A/B update case?
The semantics are not different for set_image() for the two cases. It's just that the image_index value being passed would be different for the A/B update case, since that value is being calculated at run-time.
-sughosh
-Takahiro Akashi
the sole purpose of having the fwu_get_image_index() API. I could have moved the function out of the efi_capsule.c to the FMP's set_image functions, but like I mentioned earlier, the set_image function does not know the ImageTypeId of the image for which it has been called -- since the image_index is a parameter being passed to the set_image function, we need to compute it earlier, before calling the function.
-sughosh
I think that all the visible part of A/B update in efi_capsule.c is a handling of accept/revert capsules.
-Takahiro Akashi
is just getting the image index for the image payload, and the image index will remain irrespective of the underlying framework for doing the updates.
-sughosh
> > Please remember that, from the viewpoint of API, image_index must be unique > whether it is on A bank or B bank as it is used to identify a specific firmware image > within a device, not a "physical" location. > > Please re-think. > > -Takahiro Akashi > > > > + ret = fwu_to_efi_error(status); > > + if (ret != EFI_SUCCESS) { > > + log_err("Unable to get the Image Index for the image type %pUs\n", > > + &image_type_id); > > + goto out; > > + } > > + log_debug("Image Index %u for Image Type Id %pUs\n", > > + image_index, &image_type_id); > > + } 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 +682,33 @@ 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); > > + } > > + ret = fwu_to_efi_error(status); > > + if (ret != EFI_SUCCESS) { > > + log_err("Unable to %s the accept bit for the image %pUs\n", > > + fw_accept_os ? "clear" : "set", > > + &image_type_id); > > + goto out; > > + } > > + > > + log_debug("%s the accepted bit for Image %pUs\n", > > + fw_accept_os ? "Cleared" : "Set", > > + &image_type_id); > > + } > > + > > } > > > > out: > > @@ -1104,6 +1316,9 @@ efi_status_t efi_launch_capsules(void) > > u16 **files; > > unsigned int nfiles, index, i; > > efi_status_t ret; > > + bool capsule_update = true; > > + bool update_status = true; > > + bool fw_accept_os = false; > > > > if (check_run_capsules() != EFI_SUCCESS) > > return EFI_SUCCESS; > > @@ -1131,12 +1346,19 @@ 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]); > > + if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) { > > + fwu_post_update_checks(capsule, > > + &fw_accept_os, > > + &capsule_update); > > + } > > + } > > > > /* create CapsuleXXXX */ > > set_capsule_result(index, capsule, ret); > > @@ -1144,6 +1366,7 @@ efi_status_t efi_launch_capsules(void) > > free(capsule); > > } else { > > log_err("Reading capsule %ls failed\n", files[i]); > > + update_status = false; > > } > > /* delete a capsule either in case of success or failure */ > > ret = efi_capsule_delete_file(files[i]); > > @@ -1151,7 +1374,15 @@ 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) { > > + ret = fwu_post_update_process(fw_accept_os); > > + } 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/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig > > new file mode 100644 > > index 0000000000..78759e6618 > > --- /dev/null > > +++ b/lib/fwu_updates/Kconfig > > @@ -0,0 +1,33 @@ > > +config FWU_MULTI_BANK_UPDATE > > + bool "Enable FWU Multi Bank Update Feature" > > + depends on EFI_CAPSULE_ON_DISK > > + select PARTITION_TYPE_GUID > > + select EFI_SETUP_EARLY > > + imply EFI_CAPSULE_ON_DISK_EARLY > > + select EVENT > > + 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..1993088e5b > > --- /dev/null > > +++ b/lib/fwu_updates/Makefile > > @@ -0,0 +1,7 @@ > > +# SPDX-License-Identifier: GPL-2.0-or-later > > +# > > +# Copyright (c) 2022, Linaro Limited > > +# > > + > > +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o > > +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o > > diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c > > index 32518d6f86..7209000b56 100644 > > --- a/lib/fwu_updates/fwu.c > > +++ b/lib/fwu_updates/fwu.c > > @@ -490,7 +490,30 @@ u8 fwu_update_checks_pass(void) > > return !trial_state && boottime_check; > > } > > > > +/** > > + * fwu_trial_state_ctr_start() - Start the Trial State counter > > + * > > + * Start the counter to identify the platform booting in the > > + * Trial State. The counter is implemented as an EFI variable. > > + * > > + * Return: 0 if OK, -ve on error > > + * > > + */ > > +int fwu_trial_state_ctr_start(void) > > +{ > > + int ret; > > + u16 trial_state_ctr; > > + > > + trial_state_ctr = 0; > > + ret = trial_counter_update(&trial_state_ctr); > > + if (ret) > > + log_err("Unable to initialise TrialStateCtr\n"); > > + > > + return ret; > > +} > > + > > static int fwu_boottime_checks(void *ctx, struct event *event) > > + > > { > > int ret; > > struct udevice *dev; > > -- > > 2.34.1 > >

On Wed, Sep 21, 2022 at 04:56:20PM +0530, Sughosh Ganu wrote:
hi Takahiro,
On Wed, 21 Sept 2022 at 10:58, Takahiro Akashi takahiro.akashi@linaro.org wrote:
Sughosh,
On Tue, Sep 20, 2022 at 06:34:12PM +0530, Sughosh Ganu wrote:
On Tue, 20 Sept 2022 at 13:46, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Fri, Sep 16, 2022 at 04:24:35PM +0530, Sughosh Ganu wrote:
hi Takahiro,
On Fri, 16 Sept 2022 at 12:20, Takahiro Akashi takahiro.akashi@linaro.org wrote:
On Fri, Sep 16, 2022 at 10:52:11AM +0530, Sughosh Ganu wrote: > () hi Takahiro, > > On Fri, 16 Sept 2022 at 07:17, Takahiro Akashi > takahiro.akashi@linaro.org wrote: > > > > Hi Sughosh, > > > > On Thu, Sep 15, 2022 at 01:44:46PM +0530, 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 > > > --- > > > Changes since V9: > > > * Move the global variables into local variables as suggested by > > > Ilias. > > > * Change fwu_get_image_alt_num() name to fwu_get_image_image_index() > > > > -> typo? fwu_get_image_index()? > > > > > as suggested by Takahiro. > > > * Allow capsule updates to be called from efi_init_obj_list() with the > > > FWU feature enabled, as suggested by Takahiro. > > > * Enable EFI_CAPSULE_ON_DISK_EARLY as an imply with the FWU feature > > > enabled. > > > * Define the FWU feature related functions as __maybe_unused to allow > > > for compilation with the FWU feature disabled. > > > > > > drivers/Kconfig | 2 + > > > drivers/Makefile | 1 + > > > include/fwu.h | 30 +++++ > > > lib/Kconfig | 6 + > > > lib/Makefile | 1 + > > > lib/efi_loader/efi_capsule.c | 243 ++++++++++++++++++++++++++++++++++- > > > lib/fwu_updates/Kconfig | 33 +++++ > > > lib/fwu_updates/Makefile | 7 + > > > lib/fwu_updates/fwu.c | 23 ++++ > > > 9 files changed, 340 insertions(+), 6 deletions(-) > > > create mode 100644 lib/fwu_updates/Kconfig > > > create mode 100644 lib/fwu_updates/Makefile > > >
<snip>
> > > > > > /** > > > * efi_capsule_update_firmware - update firmware from capsule > > > @@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( > > > int item; > > > 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; > > > + u32 update_index; > > > + bool fw_accept_os, image_index_check; > > > + > > > + 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)) > > > + return fwu_empty_capsule_process(capsule_data); > > > + > > > + /* 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; > > > + } > > > + > > > + image_index_check = false; > > > + fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0; > > > + } else { > > > + image_index_check = true; > > > + } > > > > > > /* sanity check */ > > > if (capsule_data->header_size < sizeof(*capsule) || > > > @@ -455,7 +617,8 @@ static efi_status_t efi_capsule_update_firmware( > > > fmp = efi_fmp_find(&image->update_image_type_id, > > > image->update_image_index, > > > image->update_hardware_instance, > > > - handles, no_handles); > > > + handles, no_handles, > > > + image_index_check); > > > if (!fmp) { > > > log_err("FMP driver not found for firmware type %pUs, hardware instance %lld\n", > > > &image->update_image_type_id, > > > @@ -485,8 +648,30 @@ 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 image index 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_index(&image_type_id, > > > + update_index, > > > + &image_index); > > > > AS I said in my comment to v9, this function should be moved in FMP driver, > > that is, efi_firmware.c and contained in set_image(). > > Okay. I had replied to your review comment and for this specific > comment, I had mentioned that I would prefer keeping this in the > capsule driver. Since you did not object to that, I was under the > assumption that you are fine with what I had said. > > I looked at moving this to the FMP's set_image function. However, > there is an issue in that the fwu_get_image_index() function needs to > be passed the ImageTypeId GUID value for getting the image index. > However, the set_image function has not been passed this GUID. Unless > we use some global variable, it would not be possible to move this > function to the set_image function.
I doubt it. Because FMP driver is looked for with image type id at efi_fmp_find(), it should know who it is. After you change in the past, current FMP drivers, either FIT or RAW, are bound only to a single GUID. Right?
With the recent change that I had made, we do need different GUIDs for different images in the capsule, but the FMP instance will be the same for all raw images, and similarly for all FIT images. But the set_image function does not know for which image the function has been called. Multiple images of a given type(raw/FIT) can use the same set_image function.
> > > > You try to use different image_index's to distinguish A and B banks, but > > this kind of usage is quite implementation-dependent since other firmware > > framework may use a different approach to support multiple banks. > > True, but even with this implementation, that underlying framework can > be abstracted. If, in the future, we have an option for multiple > frameworks for performing the update, the fwu_get_image_index() can be > extended to support those multiple framework implementations. The API
I can't image how. My point is that a caller of set_image() can and should pass an unique (and the same) index id whether the working firmware is on A or B bank.
We have discussed this earlier as well. What you say is true for the normal capsule update. However, for the FWU(A/B) updates, the image index is going to be calculated at run-time, based on the partition(bank) to which the image needs to be written to. Which is
It sound weird to me. If we assume what you said here, FMP driver is expected to handle a capsule image solely based on "index" but without knowing which type (id) the image belongs to.
I am referring to the set_image(SetImage) FMP function. That is indeed solely based on the image index -- there is no ImageTypeId being passed to the SetImage FMP function.
Before further discussion, let me ask in another way.
What contents do you expect GetImageInfo() returns, in particular, for "ImageIndex" for a specific firmware image?
UEFI specification says, in 23.1 Firmware Management Protocol, ===(a)=== The definition of GET_IMAGE_INFO(): DescriptorCount A pointer to the location in which firmware returns the number of descriptors or firmware images within this device.
...
The definition of EFI_FIRMWARE_IMAGE_DESCRIPTOR: ImageIndex A unique number identifying the firmware image within the device. The number is between 1 and DescriptorCount. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ImageTypeId A unique GUID identifying the firmware image type. === ===
So whatever "ImageTypeId" is, ImageIndex will and can uniquely identify a specific firmware image in terms of a table of EFI_FIRMWARE_IMAGE_DESCRIPTOR's which is returned by GetImageInfo() of a FMP driver.
Yes, this is true for the normal capsule update scenario. However, in the case of A/B updates, the above condition is no longer true. We will have a single ImageTypeId with multiple ImageIndex values, since we have multiple banks. And if we have to extrapolate the ImageIndex of BankB based on the value of ImageIndex in BankA, we will have to put that restriction on the order of placement of the images on the storage media -- this would mean that the order of images in all banks will have to be the same.
I don't think you fully understand what I meant to say.
As I said in my previous reply, ImageIndex is quite unique enough to distinguish all the firmware images, which is also items in a list of ImageInfo returned by GetImageInfo(), that a given FMP driver can handle.
This means that SetImage() doesn't have to take a ImageTypeId as one of arguments, and so there is no reason that might prevent us from calling fwu_get_image_index() in FMP's SetImage(). (i.e. we can drop ImageTypeId from arguments of fwu_get_image_index().)
I think that the issue is the only one that you mentioned as your concern.
While in case of FWU A/B updates,we do not need that restriction, since the FWU metadata does not put any restriction on placement of images. Which is why the image_index is being derived at run-time based on the values of ImageTypeId and update bank.
Actually, this is something pretty similar to the case of updating FIT images. Even in case of FIT updates, we use a single image_index(0x1) for the entire FIT image, and then the actual FIT update code is figuring out the location of the constituent images at run-time based on parsing of the FIT header.
This is not a fair comparison. As I repeatedly said, FIT FMP driver recognizes a FIT file as a single image in respect of UEFI APIs. How it will handle a capsule is totally up to the driver itself. We don't have to care how it will manage multiple banks internally.
-Takahiro Akashi
In the case of FWU(A/B) updates, we figure out the image_index at run-time based on the values of the ImageTypeId and the update bank. The way I see it, this is not affecting the native capsule update code behaviour. Just that in the case of A/B updates, the way of deriving the ImageIndex is different.
-sughosh
Furthermore,
in 23.3 Delivering Capsules Containing Updates to Firmware Management Protocol, ===(b)=== The definition of EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER: UpdateImageTypeId Used to identify device firmware targeted by this update. This guid is matched by system firmware against ImageTypeId field within a EFI_FIRMWARE_IMAGE_DESCRIPTOR returned by an instance of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo() in the system. UpdateImageIndex Passed as ImageIndex in call to EFI_FIRMWARE_MANAGEMENT_PROTOCOL.SetImage() ^^^^^^^^^^^^^ === ===
So I believe that "UpdateImageIndex" in a header of capsule file is expected to be passed *directly* to SetImage() as one of arguments, assuming that a content of capsule image header is irrelevant to which bank the firmware is to be applied at any time.
-Takahiro Akashi
efi_status_t (EFIAPI *set_image)( struct efi_firmware_management_protocol *this, u8 image_index, const void *image, efi_uintn_t image_size, const void *vendor_code, efi_status_t (*progress)(efi_uintn_t completion), u16 **abort_reason);
I don' think it can be universal assumption for all kind of FMP's. Why must we have different semantics of set_image() for normal (non-A/B-update) case and A/B update case?
The semantics are not different for set_image() for the two cases. It's just that the image_index value being passed would be different for the A/B update case, since that value is being calculated at run-time.
-sughosh
-Takahiro Akashi
the sole purpose of having the fwu_get_image_index() API. I could have moved the function out of the efi_capsule.c to the FMP's set_image functions, but like I mentioned earlier, the set_image function does not know the ImageTypeId of the image for which it has been called -- since the image_index is a parameter being passed to the set_image function, we need to compute it earlier, before calling the function.
-sughosh
I think that all the visible part of A/B update in efi_capsule.c is a handling of accept/revert capsules.
-Takahiro Akashi
> is just getting the image index for the image payload, and the image > index will remain irrespective of the underlying framework for doing > the updates. > > -sughosh > > > > > Please remember that, from the viewpoint of API, image_index must be unique > > whether it is on A bank or B bank as it is used to identify a specific firmware image > > within a device, not a "physical" location. > > > > Please re-think. > > > > -Takahiro Akashi > > > > > > > + ret = fwu_to_efi_error(status); > > > + if (ret != EFI_SUCCESS) { > > > + log_err("Unable to get the Image Index for the image type %pUs\n", > > > + &image_type_id); > > > + goto out; > > > + } > > > + log_debug("Image Index %u for Image Type Id %pUs\n", > > > + image_index, &image_type_id); > > > + } 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 +682,33 @@ 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); > > > + } > > > + ret = fwu_to_efi_error(status); > > > + if (ret != EFI_SUCCESS) { > > > + log_err("Unable to %s the accept bit for the image %pUs\n", > > > + fw_accept_os ? "clear" : "set", > > > + &image_type_id); > > > + goto out; > > > + } > > > + > > > + log_debug("%s the accepted bit for Image %pUs\n", > > > + fw_accept_os ? "Cleared" : "Set", > > > + &image_type_id); > > > + } > > > + > > > } > > > > > > out: > > > @@ -1104,6 +1316,9 @@ efi_status_t efi_launch_capsules(void) > > > u16 **files; > > > unsigned int nfiles, index, i; > > > efi_status_t ret; > > > + bool capsule_update = true; > > > + bool update_status = true; > > > + bool fw_accept_os = false; > > > > > > if (check_run_capsules() != EFI_SUCCESS) > > > return EFI_SUCCESS; > > > @@ -1131,12 +1346,19 @@ 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]); > > > + if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) { > > > + fwu_post_update_checks(capsule, > > > + &fw_accept_os, > > > + &capsule_update); > > > + } > > > + } > > > > > > /* create CapsuleXXXX */ > > > set_capsule_result(index, capsule, ret); > > > @@ -1144,6 +1366,7 @@ efi_status_t efi_launch_capsules(void) > > > free(capsule); > > > } else { > > > log_err("Reading capsule %ls failed\n", files[i]); > > > + update_status = false; > > > } > > > /* delete a capsule either in case of success or failure */ > > > ret = efi_capsule_delete_file(files[i]); > > > @@ -1151,7 +1374,15 @@ 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) { > > > + ret = fwu_post_update_process(fw_accept_os); > > > + } 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/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig > > > new file mode 100644 > > > index 0000000000..78759e6618 > > > --- /dev/null > > > +++ b/lib/fwu_updates/Kconfig > > > @@ -0,0 +1,33 @@ > > > +config FWU_MULTI_BANK_UPDATE > > > + bool "Enable FWU Multi Bank Update Feature" > > > + depends on EFI_CAPSULE_ON_DISK > > > + select PARTITION_TYPE_GUID > > > + select EFI_SETUP_EARLY > > > + imply EFI_CAPSULE_ON_DISK_EARLY > > > + select EVENT > > > + 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..1993088e5b > > > --- /dev/null > > > +++ b/lib/fwu_updates/Makefile > > > @@ -0,0 +1,7 @@ > > > +# SPDX-License-Identifier: GPL-2.0-or-later > > > +# > > > +# Copyright (c) 2022, Linaro Limited > > > +# > > > + > > > +obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o > > > +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o > > > diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c > > > index 32518d6f86..7209000b56 100644 > > > --- a/lib/fwu_updates/fwu.c > > > +++ b/lib/fwu_updates/fwu.c > > > @@ -490,7 +490,30 @@ u8 fwu_update_checks_pass(void) > > > return !trial_state && boottime_check; > > > } > > > > > > +/** > > > + * fwu_trial_state_ctr_start() - Start the Trial State counter > > > + * > > > + * Start the counter to identify the platform booting in the > > > + * Trial State. The counter is implemented as an EFI variable. > > > + * > > > + * Return: 0 if OK, -ve on error > > > + * > > > + */ > > > +int fwu_trial_state_ctr_start(void) > > > +{ > > > + int ret; > > > + u16 trial_state_ctr; > > > + > > > + trial_state_ctr = 0; > > > + ret = trial_counter_update(&trial_state_ctr); > > > + if (ret) > > > + log_err("Unable to initialise TrialStateCtr\n"); > > > + > > > + return ret; > > > +} > > > + > > > static int fwu_boottime_checks(void *ctx, struct event *event) > > > + > > > { > > > int ret; > > > struct udevice *dev; > > > -- > > > 2.34.1 > > >

On Thu, Sep 15, 2022 at 3:17 AM Sughosh Ganu sughosh.ganu@linaro.org wrote: ....
+static __maybe_unused void fwu_post_update_checks(
struct efi_capsule_header *capsule,
bool *fw_accept_os, bool *capsule_update)
+{
if (fwu_empty_capsule(capsule))
*capsule_update = false;
else
if (!*fw_accept_os)
*fw_accept_os =
capsule->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
True and False, instead of 1 and 0 for consistency.
.....
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) +{
int status;
u32 update_index;
efi_status_t ret;
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;
}
/*
* 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);
Do we want to check if all images in the bank are updated via capsules before switching the bank?
A developer will make sure all images are provided in one go, so that the switch is successful. But a malicious user may force some old vulnerable image back into use by updating all but that image.
....
@@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( int item; 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;
u32 update_index;
bool fw_accept_os, image_index_check;
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))
return fwu_empty_capsule_process(capsule_data);
This could be simplified as if (fwu_empty_capsule(capsule_data)) return fwu_empty_capsule_process(capsule_data);
if (!fwu_update_checks_pass()) { log_err("FWU checks failed. Cannot start update\n"); return EFI_INVALID_PARAMETER; }
....
@@ -1151,7 +1374,15 @@ 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)) {
missing newline before the if
if (update_status == true && capsule_update == true) {
ret = fwu_post_update_process(fw_accept_os);
} else if (capsule_update == true && update_status == false) {
log_err("All capsules were not updated. Not updating FWU metadata\n");
}
nit: maybe keep the order of capsule_update and update_status same in both clauses.
cheers.

On Mon, 26 Sept 2022 at 08:25, Jassi Brar jassisinghbrar@gmail.com wrote:
On Thu, Sep 15, 2022 at 3:17 AM Sughosh Ganu sughosh.ganu@linaro.org wrote: ....
+static __maybe_unused void fwu_post_update_checks(
struct efi_capsule_header *capsule,
bool *fw_accept_os, bool *capsule_update)
+{
if (fwu_empty_capsule(capsule))
*capsule_update = false;
else
if (!*fw_accept_os)
*fw_accept_os =
capsule->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
True and False, instead of 1 and 0 for consistency.
Okay
.....
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) +{
int status;
u32 update_index;
efi_status_t ret;
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;
}
/*
* 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);
Do we want to check if all images in the bank are updated via capsules before switching the bank?
This function does get called only when the update status for every capsule is a success. Even if one of the capsules does not get updated, the active index will not get updated.
A developer will make sure all images are provided in one go, so that the switch is successful. But a malicious user may force some old vulnerable image back into use by updating all but that image.
That I believe is to be handled through a combination of implementing a rollback protection mechanism, along with capsule authentication. These are separate to the implementation of the multi bank updates that these patches are aiming for.
....
@@ -410,7 +544,35 @@ static efi_status_t efi_capsule_update_firmware( int item; 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;
u32 update_index;
bool fw_accept_os, image_index_check;
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))
return fwu_empty_capsule_process(capsule_data);
This could be simplified as if (fwu_empty_capsule(capsule_data)) return fwu_empty_capsule_process(capsule_data);
if (!fwu_update_checks_pass()) { log_err("FWU checks failed. Cannot start update\n"); return EFI_INVALID_PARAMETER; }
Yes, will change.
....
@@ -1151,7 +1374,15 @@ 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)) {
missing newline before the if
Will add.
-sughosh
if (update_status == true && capsule_update == true) {
ret = fwu_post_update_process(fw_accept_os);
} else if (capsule_update == true && update_status == false) {
log_err("All capsules were not updated. Not updating FWU metadata\n");
}
nit: maybe keep the order of capsule_update and update_status same in both clauses.
cheers.

On Mon, Sep 26, 2022 at 4:01 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:25, Jassi Brar jassisinghbrar@gmail.com wrote:
.....
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) +{
int status;
u32 update_index;
efi_status_t ret;
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;
}
/*
* 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);
Do we want to check if all images in the bank are updated via capsules before switching the bank?
This function does get called only when the update status for every capsule is a success. Even if one of the capsules does not get updated, the active index will not get updated.
.... but we don't check if the capsule for each image in the bank is provided for update.
A developer will make sure all images are provided in one go, so that the switch is successful. But a malicious user may force some old vulnerable image back into use by updating all but that image.
That I believe is to be handled through a combination of implementing a rollback protection mechanism, along with capsule authentication. These are separate to the implementation of the multi bank updates that these patches are aiming for.
This sounds like : we don't worry about buffer-overflow vulnerabilities because the system will be secured and hardened by other mechanisms.
A/B update does not _require_ rollback-protection or capsure-authentication. A platform may rely on some other technology for tamper-proofing.
-j

On Mon, 26 Sept 2022 at 20:24, Jassi Brar jassisinghbrar@gmail.com wrote:
On Mon, Sep 26, 2022 at 4:01 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:25, Jassi Brar jassisinghbrar@gmail.com wrote:
.....
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) +{
int status;
u32 update_index;
efi_status_t ret;
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;
}
/*
* 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);
Do we want to check if all images in the bank are updated via capsules before switching the bank?
This function does get called only when the update status for every capsule is a success. Even if one of the capsules does not get updated, the active index will not get updated.
.... but we don't check if the capsule for each image in the bank is provided for update.
Yes, we have had this discussion earlier. Neither the FWU spec, nor the capsule update spec in UEFI puts that restriction that all images on the platform need to be updated. If a user wants to ensure such a behaviour, they may choose some kind of image packaging like FIP or FIT which would mean that all the images on the platform are being updated. But this is not something to be ensured by the FWU update code.
A developer will make sure all images are provided in one go, so that the switch is successful. But a malicious user may force some old vulnerable image back into use by updating all but that image.
That I believe is to be handled through a combination of implementing a rollback protection mechanism, along with capsule authentication. These are separate to the implementation of the multi bank updates that these patches are aiming for.
This sounds like : we don't worry about buffer-overflow vulnerabilities because the system will be secured and hardened by other mechanisms.
Not sure how this is related. The aim of the FWU spec is for providing a means for a platform to maintain multiple partitions of images and to specify the metadata structure for the bookkeeping of the different partitions and images on those partitions. If we need a more secure and hardened system, we do have the Dependable Boot spec, which is talking precisely about these things. Those are indeed separate features or aspects from what the FWU spec is talking about. And this patchset is implementing the FWU spec.
-sughosh
A/B update does not _require_ rollback-protection or capsure-authentication. A platform may rely on some other technology for tamper-proofing.
-j

On Tue, Sep 27, 2022 at 2:22 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 20:24, Jassi Brar jassisinghbrar@gmail.com wrote:
On Mon, Sep 26, 2022 at 4:01 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:25, Jassi Brar jassisinghbrar@gmail.com wrote:
.....
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) +{
int status;
u32 update_index;
efi_status_t ret;
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;
}
/*
* 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);
Do we want to check if all images in the bank are updated via capsules before switching the bank?
This function does get called only when the update status for every capsule is a success. Even if one of the capsules does not get updated, the active index will not get updated.
.... but we don't check if the capsule for each image in the bank is provided for update.
Yes, we have had this discussion earlier. Neither the FWU spec, nor the capsule update spec in UEFI puts that restriction that all images on the platform need to be updated. If a user wants to ensure such a behaviour, they may choose some kind of image packaging like FIP or FIT which would mean that all the images on the platform are being updated. But this is not something to be ensured by the FWU update code.
A developer will make sure all images are provided in one go, so that the switch is successful. But a malicious user may force some old vulnerable image back into use by updating all but that image.
That I believe is to be handled through a combination of implementing a rollback protection mechanism, along with capsule authentication. These are separate to the implementation of the multi bank updates that these patches are aiming for.
This sounds like : we don't worry about buffer-overflow vulnerabilities because the system will be secured and hardened by other mechanisms.
Not sure how this is related. The aim of the FWU spec is for providing a means for a platform to maintain multiple partitions of images and to specify the metadata structure for the bookkeeping of the different partitions and images on those partitions. If we need a more secure and hardened system, we do have the Dependable Boot spec, which is talking precisely about these things. Those are indeed separate features or aspects from what the FWU spec is talking about. And this patchset is implementing the FWU spec.
I am out of ways to explain. Best of luck.

On Tue, 27 Sept 2022 at 22:19, Jassi Brar jassisinghbrar@gmail.com wrote:
On Tue, Sep 27, 2022 at 2:22 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 20:24, Jassi Brar jassisinghbrar@gmail.com wrote:
On Mon, Sep 26, 2022 at 4:01 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:25, Jassi Brar jassisinghbrar@gmail.com wrote:
.....
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) +{
int status;
u32 update_index;
efi_status_t ret;
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;
}
/*
* 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);
Do we want to check if all images in the bank are updated via capsules before switching the bank?
This function does get called only when the update status for every capsule is a success. Even if one of the capsules does not get updated, the active index will not get updated.
.... but we don't check if the capsule for each image in the bank is provided for update.
Yes, we have had this discussion earlier. Neither the FWU spec, nor the capsule update spec in UEFI puts that restriction that all images on the platform need to be updated. If a user wants to ensure such a behaviour, they may choose some kind of image packaging like FIP or FIT which would mean that all the images on the platform are being updated. But this is not something to be ensured by the FWU update code.
A developer will make sure all images are provided in one go, so that the switch is successful. But a malicious user may force some old vulnerable image back into use by updating all but that image.
That I believe is to be handled through a combination of implementing a rollback protection mechanism, along with capsule authentication. These are separate to the implementation of the multi bank updates that these patches are aiming for.
This sounds like : we don't worry about buffer-overflow vulnerabilities because the system will be secured and hardened by other mechanisms.
Not sure how this is related. The aim of the FWU spec is for providing a means for a platform to maintain multiple partitions of images and to specify the metadata structure for the bookkeeping of the different partitions and images on those partitions. If we need a more secure and hardened system, we do have the Dependable Boot spec, which is talking precisely about these things. Those are indeed separate features or aspects from what the FWU spec is talking about. And this patchset is implementing the FWU spec.
I am out of ways to explain. Best of luck.
Let me try one last time. If you still do not agree with what I say, let us agree to disagree :)
You mentioned earlier that a malicious user may force some old vulnerable image back into use by updating all but that image. Now a few points to consider here. The FWU spec recommends that platforms follow the Platform Security Boot Guide [1] which specifies the trusted boot flow for platforms where the firmware images being booted on the platform, including the NWd bootloader(BL33 in tf-a jargon) are verified before getting booted. So, if the platform is booting with the trusted boot flow, the above scenario should not occur. Secondly, at the time of the update, the capsule can be authenticated before getting written to the storage. This again should be able to identify a malicious image getting written to the device. So this hypothesis of a malicious image being present should not occur with authenticated capsules and a trusted boot flow. And lastly, if a supposedly good image is found to have some vulnerability because of which an image with a fix should be installed and the vulnerable image not allowed to boot, this is precisely what we have rollback protection for.
You also mentioned that a platform might not support these functionalities. Well, if these are not being supported, we don't have much of a safety net here. Moreover, one more point to think here is that if it is possible for the user to install and boot a vulnerable image on the platform in the first place, it will not be protected by updating all the images -- the malicious user can still put a corrupt image as part of the update image bundle. The protection against booting a corrupt image or a buggy image has to come through mechanisms like trusted boot and rollback protection.
-sughosh
[1] - https://developer.arm.com/documentation/den0072/0101/?lang=en

Hello Jassi, Sughosh and all,
On Wed, 28 Sept 2022 at 08:23, Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Tue, 27 Sept 2022 at 22:19, Jassi Brar jassisinghbrar@gmail.com wrote:
On Tue, Sep 27, 2022 at 2:22 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 20:24, Jassi Brar jassisinghbrar@gmail.com wrote:
On Mon, Sep 26, 2022 at 4:01 AM Sughosh Ganu sughosh.ganu@linaro.org wrote:
On Mon, 26 Sept 2022 at 08:25, Jassi Brar jassisinghbrar@gmail.com wrote:
..... > > +static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os) > +{ > + int status; > + u32 update_index; > + efi_status_t ret; > + > + 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; > + } > + > + /* > + * 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); > Do we want to check if all images in the bank are updated via capsules before switching the bank?
This function does get called only when the update status for every capsule is a success. Even if one of the capsules does not get updated, the active index will not get updated.
.... but we don't check if the capsule for each image in the bank is provided for update.
Yes, we have had this discussion earlier. Neither the FWU spec, nor the capsule update spec in UEFI puts that restriction that all images on the platform need to be updated. If a user wants to ensure such a behaviour, they may choose some kind of image packaging like FIP or FIT which would mean that all the images on the platform are being updated. But this is not something to be ensured by the FWU update code.
A developer will make sure all images are provided in one go, so that the switch is successful. But a malicious user may force some old vulnerable image back into use by updating all but that image.
That I believe is to be handled through a combination of implementing a rollback protection mechanism, along with capsule authentication. These are separate to the implementation of the multi bank updates that these patches are aiming for.
This sounds like : we don't worry about buffer-overflow vulnerabilities because the system will be secured and hardened by other mechanisms.
Not sure how this is related. The aim of the FWU spec is for providing a means for a platform to maintain multiple partitions of images and to specify the metadata structure for the bookkeeping of the different partitions and images on those partitions. If we need a more secure and hardened system, we do have the Dependable Boot spec, which is talking precisely about these things. Those are indeed separate features or aspects from what the FWU spec is talking about. And this patchset is implementing the FWU spec.
I am out of ways to explain. Best of luck.
Let me try one last time. If you still do not agree with what I say, let us agree to disagree :)
You mentioned earlier that a malicious user may force some old vulnerable image back into use by updating all but that image. Now a few points to consider here. The FWU spec recommends that platforms follow the Platform Security Boot Guide [1] which specifies the trusted boot flow for platforms where the firmware images being booted on the platform, including the NWd bootloader(BL33 in tf-a jargon) are verified before getting booted.
Let me add that in tf-a we can bind booted images (actually booted certificates) version id to a platform non-volatile monotonic counter. This adds rollback protection on booted images upgrades.
Enabling firmware update, we need this rollback protection to prevent cases like you mentioned above:
But a malicious user may force some old vulnerable image back into use by updating all but that image.
When the system boots with accepted images (referring to fwu-mdata regular/trial state), the platform monotonic counter is updated against booted image version number if needed, preventing older images to be booted when an accepted image has been deployed. @Jassi, does this answer your question?
So, if the platform is booting with the trusted boot flow, the above scenario should not occur. Secondly, at the time of the update, the capsule can be authenticated before getting written to the storage. This again should be able to identify a malicious image getting written to the device. So this hypothesis of a malicious image being present should not occur with authenticated capsules and a trusted boot flow.
Authenticating capsules rather relates to reducing the attack surface IMHO. I don't mean it is useless, far from it. In our system, authenticated boot is the boot guard in the chain of trust. With firmware update, the guard must prevent firmware version rollback.
And lastly, if a supposedly good image is found to have some vulnerability because of which an image with a fix should be installed and the vulnerable image not allowed to boot, this is precisely what we have rollback protection for.
You also mentioned that a platform might not support these functionalities. Well, if these are not being supported, we don't have much of a safety net here. Moreover, one more point to think here is that if it is possible for the user to install and boot a vulnerable image on the platform in the first place, it will not be protected by updating all the images -- the malicious user can still put a corrupt image as part of the update image bundle. The protection against booting a corrupt image or a buggy image has to come through mechanisms like trusted boot and rollback protection.
"rollback protection" here should also cover tracking of updatable images version ID.
Best regards, Etienne
-sughosh
[1] - https://developer.arm.com/documentation/den0072/0101/?lang=en

Hi Etienne,
On Wed, Sep 28, 2022 at 2:30 AM Etienne Carriere etienne.carriere@linaro.org wrote:
Hello Jassi, Sughosh and all,
But a malicious user may force some old vulnerable image back into use by updating all but that image.
When the system boots with accepted images (referring to fwu-mdata regular/trial state), the platform monotonic counter is updated against booted image version number if needed, preventing older images to be booted when an accepted image has been deployed. @Jassi, does this answer your question?
As I said in my earlier post, I know we can employ security+integrity techniques to prevent such misuse. My point is FWU should still be implemented assuming no such technique might be available due to any reason, and we do the best we can. Just as we don't say lets not care about buffer-overflow vulnerabilities because the system can implement secure boot and other such techniques.
For example, the spec warns : "The metadata can be maliciously crafted, it should be treated as an insecure information source." So clearly the spec doesn't count on rollback and authentication mechanisms to be always available - and that is how it should be.
cheers.

Hello Jassi,
On Wed, 28 Sept 2022 at 17:17, Jassi Brar jassisinghbrar@gmail.com wrote:
Hi Etienne,
On Wed, Sep 28, 2022 at 2:30 AM Etienne Carriere etienne.carriere@linaro.org wrote:
Hello Jassi, Sughosh and all,
But a malicious user may force some old vulnerable image back into use by updating all but that image.
When the system boots with accepted images (referring to fwu-mdata regular/trial state), the platform monotonic counter is updated against booted image version number if needed, preventing older images to be booted when an accepted image has been deployed. @Jassi, does this answer your question?
As I said in my earlier post, I know we can employ security+integrity techniques to prevent such misuse. My point is FWU should still be implemented assuming no such technique might be available due to any reason, and we do the best we can. Just as we don't say lets not care about buffer-overflow vulnerabilities because the system can implement secure boot and other such techniques.
For example, the spec warns : "The metadata can be maliciously crafted, it should be treated as an insecure information source." So clearly the spec doesn't count on rollback and authentication mechanisms to be always available - and that is how it should be.
It is true fwu metadata content are not secure, as the GPT content itself. We cannot prevent OS from corrupting fwu-mdata partitions or the device GPT despite the boot sequence heavily relies on their information. When fwu mdata and GPT are not secure, FWU only allows updating boot image, it cannot secure them. Early boot stage (tf-a) is in charge of verifying booted images (authent. and rollback counter), whatever the booted images are.
Best regards, etienne
cheers.

Hi Jassi,
On Wed, Sep 28, 2022 at 10:16:53AM -0500, Jassi Brar wrote:
Hi Etienne,
On Wed, Sep 28, 2022 at 2:30 AM Etienne Carriere etienne.carriere@linaro.org wrote:
Hello Jassi, Sughosh and all,
But a malicious user may force some old vulnerable image back into use by updating all but that image.
When the system boots with accepted images (referring to fwu-mdata regular/trial state), the platform monotonic counter is updated against booted image version number if needed, preventing older images to be booted when an accepted image has been deployed. @Jassi, does this answer your question?
As I said in my earlier post, I know we can employ security+integrity techniques to prevent such misuse. My point is FWU should still be implemented assuming no such technique might be available due to any reason, and we do the best we can. Just as we don't say lets not care about buffer-overflow vulnerabilities because the system can implement secure boot and other such techniques.
For example, the spec warns : "The metadata can be maliciously crafted, it should be treated as an insecure information source." So clearly the spec doesn't count on rollback and authentication mechanisms to be always available - and that is how it should be.
We've discussed this extensively during drafting the spec. You are right that we would be better off trying to protect the fwu metadata somehow. In fact Heinrich had similar concerns when the original RFC was posted. i
But can you think of such a reliable mechanism? The only thing we could come up without overcomplicating the entire spec was a device that boots from the secure world and stores the metadata either in a flash there or a device with such protection mechanisms (e.g an RPMB).
Cheers /Ilias
cheers.

Hi Ilias,
On Mon, Oct 3, 2022 at 7:21 AM Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Jassi,
On Wed, Sep 28, 2022 at 10:16:53AM -0500, Jassi Brar wrote:
Hi Etienne,
On Wed, Sep 28, 2022 at 2:30 AM Etienne Carriere etienne.carriere@linaro.org wrote:
Hello Jassi, Sughosh and all,
But a malicious user may force some old vulnerable image back into use by updating all but that image.
When the system boots with accepted images (referring to fwu-mdata regular/trial state), the platform monotonic counter is updated against booted image version number if needed, preventing older images to be booted when an accepted image has been deployed. @Jassi, does this answer your question?
As I said in my earlier post, I know we can employ security+integrity techniques to prevent such misuse. My point is FWU should still be implemented assuming no such technique might be available due to any reason, and we do the best we can. Just as we don't say lets not care about buffer-overflow vulnerabilities because the system can implement secure boot and other such techniques.
For example, the spec warns : "The metadata can be maliciously crafted, it should be treated as an insecure information source." So clearly the spec doesn't count on rollback and authentication mechanisms to be always available - and that is how it should be.
We've discussed this extensively during drafting the spec. You are right that we would be better off trying to protect the fwu metadata somehow. In fact Heinrich had similar concerns when the original RFC was posted. i
Actually I never said we should protect the metadata. If you read the whole thread, the point was that we should try to protect against partial bank updates - accidental or malicious. We can not assume a user updating only partially, knows what they are doing.
cheers.

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 Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org Reviewed-by: Etienne Carriere etienne.carriere@linaro.org --- Changes since V9: * Add a document for the command
cmd/Kconfig | 6 +++ cmd/Makefile | 1 + cmd/fwu_mdata.c | 79 +++++++++++++++++++++++++++++++++++++ doc/usage/cmd/fwu_mdata.rst | 43 ++++++++++++++++++++ doc/usage/index.rst | 1 + 5 files changed, 130 insertions(+) create mode 100644 cmd/fwu_mdata.c create mode 100644 doc/usage/cmd/fwu_mdata.rst
diff --git a/cmd/Kconfig b/cmd/Kconfig index 0e0be94f41..af070b5459 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -161,6 +161,12 @@ config CMD_CPU internal name) and clock frequency. Other information may be available depending on the CPU driver.
+config CMD_FWU_METADATA + bool "fwu metadata read" + depends on 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 6e87522b62..ff6e160f4a 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..725b4c17d3 --- /dev/null +++ b/cmd/fwu_mdata.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022, Linaro Limited + */ + +#include <command.h> +#include <dm.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <stdio.h> +#include <stdlib.h> + +#include <linux/types.h> + +static void print_mdata(struct fwu_mdata *mdata) +{ + int i, j; + struct fwu_image_entry *img_entry; + struct fwu_image_bank_info *img_info; + + printf("\tFWU Metadata\n"); + printf("crc32: %#x\n", mdata->crc32); + printf("version: %#x\n", mdata->version); + printf("active_index: %#x\n", mdata->active_index); + printf("previous_active_index: %#x\n", mdata->previous_active_index); + + printf("\tImage Info\n"); + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { + img_entry = &mdata->img_entry[i]; + printf("\nImage Type Guid: %pUL\n", + &img_entry->image_type_uuid); + printf("Location Guid: %pUL\n", &img_entry->location_uuid); + for (j = 0; j < CONFIG_FWU_NUM_BANKS; j++) { + img_info = &img_entry->img_bank_info[j]; + printf("Image Guid: %pUL\n", &img_info->image_uuid); + printf("Image Acceptance: %s\n", + img_info->accepted == 0x1 ? "yes" : "no"); + } + } +} + +int do_fwu_mdata_read(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct udevice *dev; + int ret = CMD_RET_SUCCESS, res; + struct fwu_mdata mdata = { 0 }; + + if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) { + log_err("Unable to get FWU metadata device\n"); + return CMD_RET_FAILURE; + } + + res = fwu_mdata_check(dev); + if (res < 0) { + log_err("FWU Metadata check failed\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + res = fwu_get_mdata(dev, &mdata); + if (res < 0) { + log_err("Unable to get valid FWU metadata\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + print_mdata(&mdata); + +out: + return ret; +} + +U_BOOT_CMD( + fwu_mdata_read, 1, 1, do_fwu_mdata_read, + "Read and print FWU metadata", + "" +); diff --git a/doc/usage/cmd/fwu_mdata.rst b/doc/usage/cmd/fwu_mdata.rst new file mode 100644 index 0000000000..ea3c22724a --- /dev/null +++ b/doc/usage/cmd/fwu_mdata.rst @@ -0,0 +1,43 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +fwu_mdata_read command +====================== + +Synopsis +-------- + +:: + + fwu_mdata_read + +Description +----------- + +The fwu_mdata_read command is used to read the FWU metadata +structure. The command prints out information about the current active +bank, the previous active bank, image GUIDs, image acceptance etc. + +The output may look like: + +:: + + => fwu_mdata_read + FWU Metadata + crc32: 0xec4fb997 + version: 0x1 + active_index: 0x0 + previous_active_index: 0x1 + Image Info + + Image Type Guid: 19D5DF83-11B0-457B-BE2C-7559C13142A5 + Location Guid: 49272BEB-8DD8-46DF-8D75-356C65EFF417 + Image Guid: D57428CC-BB9A-42E0-AA36-3F5A132059C7 + Image Acceptance: yes + Image Guid: 2BE37D6D-8281-4938-BD7B-9A5BBF80869F + Image Acceptance: yes + +Configuration +------------- + +To use the fwu_mdata_read command, CONFIG_CMD_FWU_METADATA needs to be +enabled. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index f1beeec59c..5b21451f6a 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -45,6 +45,7 @@ Shell commands cmd/fatload cmd/fdt cmd/for + cmd/fwu_mdata cmd/gpio cmd/load cmd/loadm

Add test cases for accessing the FWU Metadata on the sandbox platform. The sandbox platform also uses the metadata access driver for GPT partitioned block devices.
The FWU feature will be tested on the sandbox64 variant with a raw capsule. Remove the FIT capsule testing from sandbox64 defconfig -- the FIT capsule test will be run on the sandbox_flattree variant.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Suggested-by: Heinrich Schuchardt xypron.glpk@gmx.de --- Changes since V9: * Change the fwu_plat_get_bootidx() function to take an uint * as the function parameter instead of the void * as suggested by Etienne.
arch/sandbox/Kconfig | 6 + arch/sandbox/dts/test.dts | 7 +- board/sandbox/sandbox.c | 8 + configs/sandbox64_defconfig | 5 +- lib/fwu_updates/fwu.c | 6 + test/dm/Makefile | 1 + test/dm/fwu_mdata.c | 149 ++++++++++++++++++ test/dm/fwu_mdata_disk_image.h | 112 +++++++++++++ .../test_capsule_firmware_fit.py | 1 - .../test_capsule_firmware_signed_fit.py | 1 - tools/Makefile | 2 +- 11 files changed, 293 insertions(+), 5 deletions(-) create mode 100644 test/dm/fwu_mdata.c create mode 100644 test/dm/fwu_mdata_disk_image.h
diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 852a7c8bf2..40cdea7d46 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 2761588f0d..2cba13e98d 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -948,7 +948,7 @@ };
/* This is used for the fastboot tests */ - mmc0 { + mmc0: mmc0 { compatible = "sandbox,mmc"; };
@@ -1683,6 +1683,11 @@ compatible = "sandbox,regmap_test"; }; }; + + fwu-mdata { + compatible = "u-boot,fwu-mdata-gpt"; + fwu-mdata-store = <&mmc0>; + }; };
#include "sandbox_pmic.dtsi" diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index ca9a2ca5b1..c3d0c35444 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -164,3 +164,11 @@ int init_addr_map(void)
return 0; } + +#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) +void fwu_plat_get_bootidx(uint *boot_idx) +{ + /* Dummy value */ + *boot_idx = 0; +} +#endif diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 290d1506c2..8d456e33d9 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -242,9 +242,12 @@ 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_CAPSULE_FIRMWARE_RAW=y CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_FWU_MDATA=y +CONFIG_FWU_MDATA_GPT_BLK=y +CONFIG_FWU_MULTI_BANK_UPDATE=y diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 7209000b56..5caf44d102 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -519,6 +519,12 @@ static int fwu_boottime_checks(void *ctx, struct event *event) struct udevice *dev; u32 boot_idx, active_idx;
+ /* Don't have boot time checks on sandbox */ + if (IS_ENABLED(CONFIG_SANDBOX)) { + boottime_check = 1; + return 0; + } + ret = fwu_get_dev_mdata(&dev, NULL); if (ret) return ret; diff --git a/test/dm/Makefile b/test/dm/Makefile index 7543df8823..40c2287b56 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -47,6 +47,7 @@ ifneq ($(CONFIG_EFI_PARTITION),) obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fastboot.o endif obj-$(CONFIG_FIRMWARE) += firmware.o +obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_mdata.o obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock.o obj-$(CONFIG_DM_I2C) += i2c.o obj-$(CONFIG_SOUND) += i2s.o diff --git a/test/dm/fwu_mdata.c b/test/dm/fwu_mdata.c new file mode 100644 index 0000000000..8120af334e --- /dev/null +++ b/test/dm/fwu_mdata.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, Linaro Limited + * Copyright (c) 2022, Heinrich Schuchardt xypron.glpk@gmx.de + */ + +#include <blk.h> +#include <common.h> +#include <dm.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <malloc.h> +#include <memalign.h> +#include <part.h> + +#include <dm/test.h> +#include <test/ut.h> + +#include "fwu_mdata_disk_image.h" + +/* Block size of compressed disk image */ +#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8 + +static struct udevice *mmc_dev; +static struct blk_desc *dev_desc; + +/* One 8 byte block of the compressed disk image */ +struct line { + size_t addr; + char *line; +}; + +/* Compressed disk image */ +struct compressed_disk_image { + size_t length; + struct line lines[]; +}; + +static const struct compressed_disk_image img = FWU_MDATA_DISK_IMG; + +/* Decompressed disk image */ +static u8 *image; + +static int setup_blk_device(struct unit_test_state *uts) +{ + ut_assertok(uclass_get_device(UCLASS_MMC, 0, &mmc_dev)); + ut_assertok(blk_get_device_by_str("mmc", "0", &dev_desc)); + + return 0; +} + +static int populate_mmc_disk_image(struct unit_test_state *uts) +{ + u8 *buf; + size_t i; + size_t addr; + size_t len; + + buf = malloc(img.length); + if (!buf) + return -ENOMEM; + + memset(buf, 0, img.length); + + for (i = 0; ; i++) { + if (!img.lines[i].line) + break; + addr = img.lines[i].addr; + len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE; + if (addr + len > img.length) + len = img.length - addr; + memcpy(buf + addr, img.lines[i].line, len); + } + image = buf; + + return 0; +} + +static int write_mmc_blk_device(struct unit_test_state *uts) +{ + lbaint_t blkcnt; + + blkcnt = BLOCK_CNT(img.length, dev_desc); + + ut_asserteq(blkcnt, blk_dwrite(dev_desc, 0, blkcnt, image)); + + return 0; +} + +static int dm_test_fwu_mdata_read(struct unit_test_state *uts) +{ + struct udevice *dev; + struct fwu_mdata mdata = { 0 }; + + ut_assertok(uclass_first_device(UCLASS_FWU_MDATA, &dev)); + ut_assertok(setup_blk_device(uts)); + ut_assertok(populate_mmc_disk_image(uts)); + ut_assertok(write_mmc_blk_device(uts)); + + ut_assertok(fwu_get_mdata(dev, &mdata)); + + ut_asserteq(mdata.version, 0x1); + + return 0; +} +DM_TEST(dm_test_fwu_mdata_read, UT_TESTF_SCAN_FDT); + +static int dm_test_fwu_mdata_write(struct unit_test_state *uts) +{ + u32 active_idx; + struct udevice *dev; + struct fwu_mdata mdata = { 0 }; + + ut_assertok(setup_blk_device(uts)); + ut_assertok(populate_mmc_disk_image(uts)); + ut_assertok(write_mmc_blk_device(uts)); + + ut_assertok(uclass_first_device(UCLASS_FWU_MDATA, &dev)); + ut_assertnonnull(dev); + + ut_assertok(fwu_get_mdata(dev, &mdata)); + + active_idx = (mdata.active_index + 1) % CONFIG_FWU_NUM_BANKS; + ut_assertok(fwu_update_active_index(active_idx)); + + ut_assertok(fwu_get_mdata(dev, &mdata)); + ut_asserteq(mdata.active_index, active_idx); + + return 0; +} +DM_TEST(dm_test_fwu_mdata_write, UT_TESTF_SCAN_FDT); + +static int dm_test_fwu_mdata_check(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(setup_blk_device(uts)); + ut_assertok(populate_mmc_disk_image(uts)); + ut_assertok(write_mmc_blk_device(uts)); + + ut_assertok(uclass_first_device(UCLASS_FWU_MDATA, &dev)); + ut_assertnonnull(dev); + + ut_assertok(fwu_mdata_check(dev)); + + return 0; +} +DM_TEST(dm_test_fwu_mdata_check, UT_TESTF_SCAN_FDT); diff --git a/test/dm/fwu_mdata_disk_image.h b/test/dm/fwu_mdata_disk_image.h new file mode 100644 index 0000000000..b9803417c8 --- /dev/null +++ b/test/dm/fwu_mdata_disk_image.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Non-zero 8 byte strings of a disk image + * + * Generated with tools/file2include + */ + +#define FWU_MDATA_DISK_IMG { 0x00010000, { \ + {0x000001c0, "\x02\x00\xee\x02\x02\x00\x01\x00"}, /* ........ */ \ + {0x000001c8, "\x00\x00\x7f\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x000001f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \ + {0x00000200, "\x45\x46\x49\x20\x50\x41\x52\x54"}, /* EFI PART */ \ + {0x00000208, "\x00\x00\x01\x00\x5c\x00\x00\x00"}, /* ....... */ \ + {0x00000210, "\xa6\xf6\x92\x20\x00\x00\x00\x00"}, /* ... .... */ \ + {0x00000218, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x00000220, "\x7f\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x00000228, "\x22\x00\x00\x00\x00\x00\x00\x00"}, /* "....... */ \ + {0x00000230, "\x5e\x00\x00\x00\x00\x00\x00\x00"}, /* ^....... */ \ + {0x00000238, "\xde\x99\xa2\x7e\x46\x34\xeb\x47"}, /* ...~F4.G */ \ + {0x00000240, "\x87\xf6\x4f\x75\xe8\xd5\x7d\xc7"}, /* ..Ou..}. */ \ + {0x00000248, "\x02\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x00000250, "\x80\x00\x00\x00\x80\x00\x00\x00"}, /* ........ */ \ + {0x00000258, "\x2a\x64\x03\x83\x00\x00\x00\x00"}, /* .d...... */ \ + {0x00000400, "\xa0\x84\x7a\x8a\x87\x83\xf6\x40"}, /* ..z....@ */ \ + {0x00000408, "\xab\x41\xa8\xb9\xa5\xa6\x0d\x23"}, /* .A.....# */ \ + {0x00000410, "\x3d\x6c\xb9\xaa\x20\xb2\x18\x4c"}, /* =l.. ..L */ \ + {0x00000418, "\xbc\x87\x1c\x9f\xe0\x35\x9b\x73"}, /* .....5.s */ \ + {0x00000420, "\x22\x00\x00\x00\x00\x00\x00\x00"}, /* "....... */ \ + {0x00000428, "\x31\x00\x00\x00\x00\x00\x00\x00"}, /* 1....... */ \ + {0x00000438, "\x55\x00\x6e\x00\x6b\x00\x6e\x00"}, /* U.n.k.n. */ \ + {0x00000440, "\x6f\x00\x77\x00\x6e\x00\x00\x00"}, /* o.w.n... */ \ + {0x00000480, "\xa0\x84\x7a\x8a\x87\x83\xf6\x40"}, /* ..z....@ */ \ + {0x00000488, "\xab\x41\xa8\xb9\xa5\xa6\x0d\x23"}, /* .A.....# */ \ + {0x00000490, "\x57\x24\xf6\xe6\x0b\x6f\x66\x4e"}, /* W$...ofN */ \ + {0x00000498, "\xb3\xd5\x99\x50\xa5\xc6\x4e\xc1"}, /* ...P..N. */ \ + {0x000004a0, "\x32\x00\x00\x00\x00\x00\x00\x00"}, /* 2....... */ \ + {0x000004a8, "\x41\x00\x00\x00\x00\x00\x00\x00"}, /* A....... */ \ + {0x000004b8, "\x55\x00\x6e\x00\x6b\x00\x6e\x00"}, /* U.n.k.n. */ \ + {0x000004c0, "\x6f\x00\x77\x00\x6e\x00\x00\x00"}, /* o.w.n... */ \ + {0x00004400, "\x4e\xd5\x3f\x43\x01\x00\x00\x00"}, /* N.?C.... */ \ + {0x00004408, "\x00\x00\x00\x00\x01\x00\x00\x00"}, /* ........ */ \ + {0x00004410, "\x52\xcf\xd7\x09\x20\x07\x10\x47"}, /* R... ..G */ \ + {0x00004418, "\x91\xd1\x08\x46\x9b\x7f\xe9\xc8"}, /* ...F.... */ \ + {0x00004420, "\xeb\x2b\x27\x49\xd8\x8d\xdf\x46"}, /* .+'I...F */ \ + {0x00004428, "\x8d\x75\x35\x6c\x65\xef\xf4\x17"}, /* .u5le... */ \ + {0x00004430, "\x86\x7a\x05\x10\xf1\xda\x93\x4f"}, /* .z.....O */ \ + {0x00004438, "\xba\x7f\xb1\x95\xf7\xfa\x41\x70"}, /* ......Ap */ \ + {0x00004440, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x00004448, "\x3e\xed\x62\xdb\x37\x62\xb4\x4f"}, /* >.b.7b.O */ \ + {0x00004450, "\x80\xc4\x1b\x74\xd8\x46\xa8\xe7"}, /* ...t.F.. */ \ + {0x00004458, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x00004460, "\xf5\x21\x70\x5a\xf2\xfe\xb4\x48"}, /* .!pZ...H */ \ + {0x00004468, "\xaa\xba\x83\x2e\x77\x74\x18\xc0"}, /* ....wt.. */ \ + {0x00004470, "\xeb\x2b\x27\x49\xd8\x8d\xdf\x46"}, /* .+'I...F */ \ + {0x00004478, "\x8d\x75\x35\x6c\x65\xef\xf4\x17"}, /* .u5le... */ \ + {0x00004480, "\x3b\x0e\xd2\x0b\x9f\xab\x86\x49"}, /* ;......I */ \ + {0x00004488, "\xb7\x90\x8d\xf3\x9c\x9c\xa3\x82"}, /* ........ */ \ + {0x00004490, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x00004498, "\x6d\xe4\x25\x0e\x15\xb6\xd3\x4c"}, /* m.%....L */ \ + {0x000044a0, "\x94\xda\x51\x79\x8f\xb1\x9e\xb1"}, /* ..Qy.... */ \ + {0x000044a8, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x00006400, "\x4e\xd5\x3f\x43\x01\x00\x00\x00"}, /* N.?C.... */ \ + {0x00006408, "\x00\x00\x00\x00\x01\x00\x00\x00"}, /* ........ */ \ + {0x00006410, "\x52\xcf\xd7\x09\x20\x07\x10\x47"}, /* R... ..G */ \ + {0x00006418, "\x91\xd1\x08\x46\x9b\x7f\xe9\xc8"}, /* ...F.... */ \ + {0x00006420, "\xeb\x2b\x27\x49\xd8\x8d\xdf\x46"}, /* .+'I...F */ \ + {0x00006428, "\x8d\x75\x35\x6c\x65\xef\xf4\x17"}, /* .u5le... */ \ + {0x00006430, "\x86\x7a\x05\x10\xf1\xda\x93\x4f"}, /* .z.....O */ \ + {0x00006438, "\xba\x7f\xb1\x95\xf7\xfa\x41\x70"}, /* ......Ap */ \ + {0x00006440, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x00006448, "\x3e\xed\x62\xdb\x37\x62\xb4\x4f"}, /* >.b.7b.O */ \ + {0x00006450, "\x80\xc4\x1b\x74\xd8\x46\xa8\xe7"}, /* ...t.F.. */ \ + {0x00006458, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x00006460, "\xf5\x21\x70\x5a\xf2\xfe\xb4\x48"}, /* .!pZ...H */ \ + {0x00006468, "\xaa\xba\x83\x2e\x77\x74\x18\xc0"}, /* ....wt.. */ \ + {0x00006470, "\xeb\x2b\x27\x49\xd8\x8d\xdf\x46"}, /* .+'I...F */ \ + {0x00006478, "\x8d\x75\x35\x6c\x65\xef\xf4\x17"}, /* .u5le... */ \ + {0x00006480, "\x3b\x0e\xd2\x0b\x9f\xab\x86\x49"}, /* ;......I */ \ + {0x00006488, "\xb7\x90\x8d\xf3\x9c\x9c\xa3\x82"}, /* ........ */ \ + {0x00006490, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x00006498, "\x6d\xe4\x25\x0e\x15\xb6\xd3\x4c"}, /* m.%....L */ \ + {0x000064a0, "\x94\xda\x51\x79\x8f\xb1\x9e\xb1"}, /* ..Qy.... */ \ + {0x000064a8, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x0000be00, "\xa0\x84\x7a\x8a\x87\x83\xf6\x40"}, /* ..z....@ */ \ + {0x0000be08, "\xab\x41\xa8\xb9\xa5\xa6\x0d\x23"}, /* .A.....# */ \ + {0x0000be10, "\x3d\x6c\xb9\xaa\x20\xb2\x18\x4c"}, /* =l.. ..L */ \ + {0x0000be18, "\xbc\x87\x1c\x9f\xe0\x35\x9b\x73"}, /* .....5.s */ \ + {0x0000be20, "\x22\x00\x00\x00\x00\x00\x00\x00"}, /* "....... */ \ + {0x0000be28, "\x31\x00\x00\x00\x00\x00\x00\x00"}, /* 1....... */ \ + {0x0000be38, "\x55\x00\x6e\x00\x6b\x00\x6e\x00"}, /* U.n.k.n. */ \ + {0x0000be40, "\x6f\x00\x77\x00\x6e\x00\x00\x00"}, /* o.w.n... */ \ + {0x0000be80, "\xa0\x84\x7a\x8a\x87\x83\xf6\x40"}, /* ..z....@ */ \ + {0x0000be88, "\xab\x41\xa8\xb9\xa5\xa6\x0d\x23"}, /* .A.....# */ \ + {0x0000be90, "\x57\x24\xf6\xe6\x0b\x6f\x66\x4e"}, /* W$...ofN */ \ + {0x0000be98, "\xb3\xd5\x99\x50\xa5\xc6\x4e\xc1"}, /* ...P..N. */ \ + {0x0000bea0, "\x32\x00\x00\x00\x00\x00\x00\x00"}, /* 2....... */ \ + {0x0000bea8, "\x41\x00\x00\x00\x00\x00\x00\x00"}, /* A....... */ \ + {0x0000beb8, "\x55\x00\x6e\x00\x6b\x00\x6e\x00"}, /* U.n.k.n. */ \ + {0x0000bec0, "\x6f\x00\x77\x00\x6e\x00\x00\x00"}, /* o.w.n... */ \ + {0x0000fe00, "\x45\x46\x49\x20\x50\x41\x52\x54"}, /* EFI PART */ \ + {0x0000fe08, "\x00\x00\x01\x00\x5c\x00\x00\x00"}, /* ....... */ \ + {0x0000fe10, "\xa2\xce\x23\xfc\x00\x00\x00\x00"}, /* ..#..... */ \ + {0x0000fe18, "\x7f\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x0000fe20, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ + {0x0000fe28, "\x22\x00\x00\x00\x00\x00\x00\x00"}, /* "....... */ \ + {0x0000fe30, "\x5e\x00\x00\x00\x00\x00\x00\x00"}, /* ^....... */ \ + {0x0000fe38, "\xde\x99\xa2\x7e\x46\x34\xeb\x47"}, /* ...~F4.G */ \ + {0x0000fe40, "\x87\xf6\x4f\x75\xe8\xd5\x7d\xc7"}, /* ..Ou..}. */ \ + {0x0000fe48, "\x5f\x00\x00\x00\x00\x00\x00\x00"}, /* _....... */ \ + {0x0000fe50, "\x80\x00\x00\x00\x80\x00\x00\x00"}, /* ........ */ \ + {0x0000fe58, "\x2a\x64\x03\x83\x00\x00\x00\x00"}, /* .d...... */ \ + {0, NULL} } } 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 8f75b554ad..d28b53a1a1 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_efi_capsule/test_capsule_firmware_signed_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py index d6ca9b1674..8c2d616fd0 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py @@ -14,7 +14,6 @@ with signed capsule files containing FIT images import pytest from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
-@pytest.mark.boardspec('sandbox64') @pytest.mark.boardspec('sandbox_flattree') @pytest.mark.buildconfigspec('efi_capsule_firmware_fit') @pytest.mark.buildconfigspec('efi_capsule_authenticate') diff --git a/tools/Makefile b/tools/Makefile index 3626919633..c479d3cb4c 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -74,7 +74,7 @@ mkenvimage-objs := mkenvimage.o os_support.o lib/crc32.o hostprogs-y += dumpimage mkimage hostprogs-$(CONFIG_TOOLS_LIBCRYPTO) += fit_info fit_check_sign
-hostprogs-$(CONFIG_CMD_BOOTEFI_SELFTEST) += file2include +hostprogs-$(CONFIG_CMD_BOOTEFI_SELFTEST)$(CONFIG_FWU_MDATA_GPT_BLK) += file2include
FIT_OBJS-y := fit_common.o fit_image.o image-host.o boot/image-fit.o FIT_SIG_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := image-sig-host.o boot/image-fit-sig.o

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

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

Add documentattion for the FWU Multi Bank Update feature. The document describes the steps needed for setting up the platform for the feature, as well as steps for enabling the feature on the platform.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V9: None
doc/develop/uefi/fwu_updates.rst | 165 +++++++++++++++++++++++++++++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + 3 files changed, 168 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..fad3fbb3a8 --- /dev/null +++ b/doc/develop/uefi/fwu_updates.rst @@ -0,0 +1,165 @@ +.. 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-gpt.yaml>`__ +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 configuration settings 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, a GPT partitioned storage +device is required. 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 partitions 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. An example list of images on the storage medium would be + +* FWU metadata 1 +* U-Boot 1 +* OP-TEE 1 +* FWU metadata 2 +* OP-TEE 2 +* U-Boot 2 +* ESP +* rootfs + +When generating the partitions, a few aspects need to be taken care +of. Each GPT partition entry in the GPT header has two GUIDs:: + +* PartitionTypeGUID +* UniquePartitionGUID + +The PartitionTypeGUID value should correspond to the +``image_type_uuid`` field of the FWU metadata. This field is used to +identify a given type of updatable firmware image, e.g. U-Boot, +OP-TEE, FIP etc. This GUID should also be used for specifying the +`--guid` parameter when generating the capsule. + +The UniquePartitionGUID value should correspond to the ``image_uuid`` +field in the FWU metadata. This GUID is used to identify images of a +given image type in different banks. + +Similarly, the FWU specifications defines the GUID value to be used +for the metadata partitions. This would be the PartitionTypeGUID for +the metadata partitions. + +When generating the metadata, the ``image_type_uuid`` and the +``image_uuid`` values should match the *PartitionTypeGUID* and the +*UniquePartitionGUID* values respectively. + +Performing the Update +--------------------- + +Once the storage media has been partitioned and populated with the +metadata partitions, the UEFI capsule-on-disk update functionality can +be used for performing the update. Refer to the section +:ref:`uefi_capsule_update_ref` for details on how the update can be +invoked. + +On a successful update, the FWU metadata gets updated to reflect the +bank from which the platform would be booting on subsequent boot. + +Based on the value of bit15 of the Flags member of the capsule header, +the updated images would either be accepted by the U-Boot's UEFI +implementation, or by the Operating System. If the Operating System is +accepting the firmware images, it does so by generating an empty +*accept* capsule. The Operating System can also reject the updated +firmware by generating a *revert* capsule. The empty capsule can be +applied by using the exact same procedure used for performing the +capsule-on-disk update. + +The task of accepting the different firmware images, post an update +may be done by multiple, separate components in the Operating +System. To help identify the firmware image that is being accepted, +the accept capsule passes the image GUID of the firmware image being +accepted. The relevant code in U-Boot then sets the Accept bit of the +corresponding firmware image for which the accept capsule was +found. Only when all the firmware components in a bank have been +accepted does the platform transition to the regular state from trial +state. + +The revert capsule on the other hand does not pass any image GUID, +since reverting any image of the bank has the same result of the +platform booting from the other bank on subsequent boot. + +Generating an empty capsule +--------------------------- + +The empty capsule can be generated using the mkeficapsule utility. To +build the tool, enable:: + + CONFIG_TOOLS_MKEFICAPSULE=y + +Run the following commands to generate the accept/revert capsules:: + +.. code-block:: bash + + $ ./tools/mkeficapsule \ + [--fw-accept --guid <image guid>] | \ + [--fw-revert] \ + <capsule_file_name> + +Links +----- + +* [1] https://developer.arm.com/documentation/den0118/a/ - FWU Specification +* [2] https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e... - Dependable Boot Specification diff --git a/doc/develop/uefi/index.rst b/doc/develop/uefi/index.rst index 7e65dbc5d5..e26b1fbe05 100644 --- a/doc/develop/uefi/index.rst +++ b/doc/develop/uefi/index.rst @@ -13,3 +13,4 @@ can be run an UEFI payload. uefi.rst u-boot_on_efi.rst iscsi.rst + fwu_updates.rst diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index 941e427093..536b278dd9 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -277,6 +277,8 @@ Enable ``CONFIG_OPTEE``, ``CONFIG_CMD_OPTEE_RPMB`` and ``CONFIG_EFI_MM_COMM_TEE`
[1] https://optee.readthedocs.io/en/latest/building/efi_vars/stmm.html
+.. _uefi_capsule_update_ref: + Enabling UEFI Capsule Update feature ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

On Thu, 15 Sept 2022 at 03:16, Sughosh Ganu sughosh.ganu@linaro.org wrote:
diff --git a/doc/develop/uefi/fwu_updates.rst b/doc/develop/uefi/fwu_updates.rst new file mode 100644 index 0000000000..fad3fbb3a8 --- /dev/null +++ b/doc/develop/uefi/fwu_updates.rst @@ -0,0 +1,165 @@ +.. 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-gpt.yaml>`__ +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
s/CONFIG_DM_FWU_MDATA/CONFIG_FWU_MDATA

Hello Sughosh,
Thanks for the documentation. See comments on few typos found (with help of my editor) and suggestions.
Best regards, Etienne
On Thu, 15 Sept 2022 at 10:16, Sughosh Ganu sughosh.ganu@linaro.org wrote:
Add documentattion for the FWU Multi Bank Update feature. The document
s/documentattion/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
Changes since V9: None
doc/develop/uefi/fwu_updates.rst | 165 +++++++++++++++++++++++++++++++ doc/develop/uefi/index.rst | 1 + doc/develop/uefi/uefi.rst | 2 + 3 files changed, 168 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..fad3fbb3a8 --- /dev/null +++ b/doc/develop/uefi/fwu_updates.rst @@ -0,0 +1,165 @@ +.. 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
s/it's/its/
+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-gpt.yaml>`__ +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 is not needed. Selected by the above.
- CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
- CONFIG_FWU_MULTI_BANK_UPDATE=y
- CONFIG_CMD_FWU_METADATA=y
- CONFIG_DM_FWU_MDATA=y
Note also in efi_setup.c, CONFIG_FWU_MULTI_BANK_UPDATE enforces capsules are not checked at 1st EFI command. This means we need CONFIG_EFI_CAPSULE_ON_DISK_EARLY=y, if I'm right.
- 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 configuration settings 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, a GPT partitioned storage +device is required. 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
s/an/a/
+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 partitions 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. An example list of images on the storage medium would be
+* FWU metadata 1 +* U-Boot 1 +* OP-TEE 1 +* FWU metadata 2 +* OP-TEE 2 +* U-Boot 2 +* ESP +* rootfs
+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
'FWU specifications define' or 'FWU specifications define'.
+for the metadata partitions. This would be the PartitionTypeGUID for +the metadata partitions.
+When generating the metadata, the ``image_type_uuid`` and the +``image_uuid`` values should match the *PartitionTypeGUID* and the +*UniquePartitionGUID* values respectively.
+Performing the Update +---------------------
+Once the storage media has been partitioned and populated with the +metadata partitions, the UEFI capsule-on-disk update functionality can +be used for performing the update. Refer to the section +:ref:`uefi_capsule_update_ref` for details on how the update can be +invoked.
+On a successful update, the FWU metadata gets updated to reflect the +bank from which the platform would be booting on subsequent boot.
+Based on the value of bit15 of the Flags member of the capsule header, +the updated images would either be accepted by the U-Boot's UEFI +implementation, or by the Operating System. If the Operating System is +accepting the firmware images, it does so by generating an empty +*accept* capsule. The Operating System can also reject the updated +firmware by generating a *revert* capsule. The empty capsule can be +applied by using the exact same procedure used for performing the +capsule-on-disk update.
+The task of accepting the different firmware images, post an update +may be done by multiple, separate components in the Operating +System. To help identify the firmware image that is being accepted, +the accept capsule passes the image GUID of the firmware image being +accepted. The relevant code in U-Boot then sets the Accept bit of the +corresponding firmware image for which the accept capsule was +found. Only when all the firmware components in a bank have been +accepted does the platform transition to the regular state from trial +state.
nit: for clarity, I found you should rephrase as "from ... to ...":
'... does the platform transition from trial state to regular state.'
+The revert capsule on the other hand does not pass any image GUID, +since reverting any image of the bank has the same result of the +platform booting from the other bank on subsequent boot.
+Generating an empty capsule +---------------------------
+The empty capsule can be generated using the mkeficapsule utility. To +build the tool, enable::
- CONFIG_TOOLS_MKEFICAPSULE=y
+Run the following commands to generate the accept/revert capsules::
+.. code-block:: bash
- $ ./tools/mkeficapsule \
[--fw-accept --guid <image guid>] | \
[--fw-revert] \
<capsule_file_name>
Maybe examples as: + $ ./tools/mkeficapsule --guid <image guid> <image> <capsule_file_name> + $ ./tools/mkeficapsule --fw-accept --guid <image guid> + $ ./tools/mkeficapsule --fw-revert
+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
-- 2.34.1
participants (6)
-
Etienne Carriere
-
Ilias Apalodimas
-
Jassi Brar
-
Jassi Brar
-
Sughosh Ganu
-
Takahiro Akashi