aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2022-11-01 09:32:21 -0400
committerTom Rini <trini@konsulko.com>2022-11-01 09:32:21 -0400
commitc8d9ff634fc429db5acf2f5386ea937f0fef1ae7 (patch)
tree5ee0a5efa73f325b172a16db7551af87725fdb1f
parenta90afc6730e6c67ad37f4c98a02891a93b4ff971 (diff)
parent75f11c3bfdcfbadad0265eda74c372e52423ae4c (diff)
downloadu-boot-WIP/01Nov2022.zip
u-boot-WIP/01Nov2022.tar.gz
u-boot-WIP/01Nov2022.tar.bz2
Merge branch '2022-10-31-FWU-add-FWU-multi-bank-update-feature-support'WIP/01Nov2022
To quote the author: 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/6f7ddfe3be24e18d4319e108a758d02e/mbfw.pdf [3] - https://lists.denx.de/pipermail/u-boot/2022-June/485992.html
-rw-r--r--arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi7
-rw-r--r--arch/arm/dts/stm32mp157c-ev1-u-boot.dtsi5
-rw-r--r--arch/arm/mach-stm32mp/include/mach/stm32.h5
-rw-r--r--arch/sandbox/Kconfig6
-rw-r--r--arch/sandbox/dts/test.dts7
-rw-r--r--board/sandbox/sandbox.c8
-rw-r--r--board/st/stm32mp1/stm32mp1.c39
-rw-r--r--cmd/Kconfig6
-rw-r--r--cmd/Makefile1
-rw-r--r--cmd/fwu_mdata.c79
-rw-r--r--common/board_r.c3
-rw-r--r--common/event.c3
-rw-r--r--configs/sandbox64_defconfig5
-rw-r--r--doc/develop/uefi/fwu_updates.rst184
-rw-r--r--doc/develop/uefi/index.rst1
-rw-r--r--doc/develop/uefi/uefi.rst12
-rw-r--r--doc/device-tree-bindings/firmware/fwu-mdata-gpt.yaml32
-rw-r--r--doc/mkeficapsule.133
-rw-r--r--doc/usage/cmd/fwu_mdata.rst43
-rw-r--r--doc/usage/index.rst1
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/fwu-mdata/Kconfig16
-rw-r--r--drivers/fwu-mdata/Makefile8
-rw-r--r--drivers/fwu-mdata/fwu-mdata-uclass.c186
-rw-r--r--drivers/fwu-mdata/gpt_blk.c290
-rw-r--r--include/configs/stm32mp15_common.h4
-rw-r--r--include/dm/uclass-id.h1
-rw-r--r--include/event.h3
-rw-r--r--include/fwu.h412
-rw-r--r--include/fwu_mdata.h67
-rw-r--r--lib/Kconfig6
-rw-r--r--lib/Makefile1
-rw-r--r--lib/efi_loader/efi_capsule.c210
-rw-r--r--lib/efi_loader/efi_firmware.c14
-rw-r--r--lib/fwu_updates/Kconfig33
-rw-r--r--lib/fwu_updates/Makefile7
-rw-r--r--lib/fwu_updates/fwu.c719
-rw-r--r--lib/fwu_updates/fwu_gpt.c123
-rw-r--r--test/dm/Makefile1
-rw-r--r--test/dm/fwu_mdata.c147
-rw-r--r--test/dm/fwu_mdata_disk_image.h112
-rw-r--r--test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py1
-rw-r--r--test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py1
-rw-r--r--tools/Makefile4
-rw-r--r--tools/eficapsule.h8
-rw-r--r--tools/mkeficapsule.c114
47 files changed, 2949 insertions, 22 deletions
diff --git a/arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi b/arch/arm/dts/stm32mp157c-dk2-u-boot.dtsi
index 06ef3a4..24f8620 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>;
+ };
+};
diff --git a/arch/arm/dts/stm32mp157c-ev1-u-boot.dtsi b/arch/arm/dts/stm32mp157c-ev1-u-boot.dtsi
index ec60486..7bf08be 100644
--- a/arch/arm/dts/stm32mp157c-ev1-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp157c-ev1-u-boot.dtsi
@@ -14,6 +14,11 @@
spi0 = &qspi;
usb0 = &usbotg_hs;
};
+
+ fwu-mdata {
+ compatible = "u-boot,fwu-mdata-gpt";
+ fwu-mdata-store = <&sdmmc1>;
+ };
};
&flash0 {
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h
index c70375a..c85ae6a 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/arch/sandbox/Kconfig b/arch/sandbox/Kconfig
index 35508c6..96b3402 100644
--- a/arch/sandbox/Kconfig
+++ b/arch/sandbox/Kconfig
@@ -72,3 +72,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 25fd2bc..dffe10a 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -1001,7 +1001,7 @@
};
/* This is used for the fastboot tests */
- mmc0 {
+ mmc0: mmc0 {
compatible = "sandbox,mmc";
};
@@ -1740,6 +1740,11 @@
thermal {
compatible = "sandbox,thermal";
};
+
+ 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 38cfa08..4d89f9b 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/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c
index 2d98ff4..47b3d1b 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,13 @@ 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";
+ fw_images[0].image_index = 1;
+#endif
return 0;
}
@@ -939,3 +957,24 @@ 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)
+{
+ *boot_idx = (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/cmd/Kconfig b/cmd/Kconfig
index 3f6bc70..1054064 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -162,6 +162,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 ca9ed10..2444d11 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -80,6 +80,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 0000000..f04af27
--- /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_check_mdata_validity();
+ 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/common/board_r.c b/common/board_r.c
index 98653de..828ad44 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -579,6 +579,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 3e34550..231b9e6 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/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index af2ce34..d43390b 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -250,9 +250,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/doc/develop/uefi/fwu_updates.rst b/doc/develop/uefi/fwu_updates.rst
new file mode 100644
index 0000000..72c850a
--- /dev/null
+++ b/doc/develop/uefi/fwu_updates.rst
@@ -0,0 +1,184 @@
+.. 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 its 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_RAW=y
+
+ CONFIG_FWU_MULTI_BANK_UPDATE=y
+ CONFIG_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
+
+By enabling the CONFIG_CMD_FWU_METADATA config option, the
+fwu_mdata_read command can be used to check the current state of the
+FWU metadata structure.
+
+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 a separate partition. In addition, the two copies
+of the FWU metadata will be stored on two separate partitions. These
+partitions need to be created at the time of the platform's
+provisioning.
+
+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.
+
+The FWU specification defines the GUID value to be used for the
+metadata partitions. This would be the PartitionTypeGUID for the
+metadata partitions. Similarly, the UEFI specification defines the ESP
+GUID to be be used.
+
+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 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.
+
+In the scenario that bit15 of the Flags member of the capsule header
+has not been set, the images being updated are accepted by the
+U-Boot's UEFI firmware implementation by default, on successful
+update of the image.
+
+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>
+
+Some examples of using the mkeficapsule tool for generation of the
+empty capsule would be::
+
+.. code-block:: bash
+
+ $ ./tools/mkeficapsule --fw-accept --guid <image guid> \
+ <accept_capsule_name>
+ $ ./tools/mkeficapsule --fw-revert <revert_capsule_name>
+
+Links
+-----
+
+* [1] https://developer.arm.com/documentation/den0118/a/ - FWU Specification
+* [2] https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e18d4319e108a758d02e/mbfw.pdf - Dependable Boot Specification
diff --git a/doc/develop/uefi/index.rst b/doc/develop/uefi/index.rst
index 7e65dbc..e26b1fb 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 cd84706..e0835be 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -377,6 +379,16 @@ following command::
dfu list
+When the FWU Multi Bank Update feature is enabled on the platform, the
+image index is used only to identify the image index with the image
+GUID. The image index would not correspond to the dfu alt number. This
+is because the FWU feature supports multiple partitions(banks) of
+updatable images, and the actual dfu alt number to which the image is
+to be written to is determined at runtime, based on the value of the
+update bank to which the image is to be written. For more information
+on the FWU Multi Bank Update feature, please refer `doc
+<doc/develop/uefi/fwu_updates.rst>`__.
+
When using the FMP for FIT images, the image index value needs to be
set to 1.
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 0000000..0735191
--- /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>;
+ };
diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1
index 09bdc24..6fb2dd0 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,22 @@ 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
+.BI "-o\fR,\fB --capoemflag "
+Capsule OEM flag, value between 0x0000 to 0xffff
+
.TP
.BR -h ", " --help
Print a help message
diff --git a/doc/usage/cmd/fwu_mdata.rst b/doc/usage/cmd/fwu_mdata.rst
new file mode 100644
index 0000000..ea3c227
--- /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 c601d35..df50746 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -50,6 +50,7 @@ Shell commands
cmd/fdt
cmd/font
cmd/for
+ cmd/fwu_mdata
cmd/gpio
cmd/load
cmd/loadm
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 8b6fead..75ac149 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 a1700c8..ac2d83a 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -85,6 +85,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/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig
new file mode 100644
index 0000000..36c4479
--- /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 FWU_MDATA && BLK && 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 0000000..3fee64c
--- /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/fwu-mdata-uclass.c b/drivers/fwu-mdata/fwu-mdata-uclass.c
new file mode 100644
index 0000000..b477e96
--- /dev/null
+++ b/drivers/fwu-mdata/fwu-mdata-uclass.c
@@ -0,0 +1,186 @@
+// 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_get_mdata_part_num() - Get the FWU metadata partition numbers
+ * @dev: FWU metadata device
+ * @mdata_parts: array for storing the metadata partition numbers
+ *
+ * Get the partition numbers on the storage device on which the
+ * FWU metadata is stored. Two partition numbers will be returned.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_get_mdata_part_num(struct udevice *dev, uint *mdata_parts)
+{
+ const struct fwu_mdata_ops *ops = device_get_ops(dev);
+
+ if (!ops->get_mdata_part_num) {
+ log_debug("get_mdata_part_num() method not defined\n");
+ return -ENOSYS;
+ }
+
+ return ops->get_mdata_part_num(dev, mdata_parts);
+}
+
+/**
+ * fwu_read_mdata_partition() - Read the FWU metadata from a partition
+ * @dev: FWU metadata device
+ * @mdata: Copy of the FWU metadata
+ * @part_num: Partition number from which FWU metadata is to be read
+ *
+ * Read the FWU metadata from the specified partition number
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_read_mdata_partition(struct udevice *dev, struct fwu_mdata *mdata,
+ uint part_num)
+{
+ const struct fwu_mdata_ops *ops = device_get_ops(dev);
+
+ if (!ops->read_mdata_partition) {
+ log_debug("read_mdata_partition() method not defined\n");
+ return -ENOSYS;
+ }
+
+ return ops->read_mdata_partition(dev, mdata, part_num);
+}
+
+/**
+ * fwu_write_mdata_partition() - Write the FWU metadata to a partition
+ * @dev: FWU metadata device
+ * @mdata: Copy of the FWU metadata
+ * @part_num: Partition number to which FWU metadata is to be written
+ *
+ * Write the FWU metadata to the specified partition number
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_write_mdata_partition(struct udevice *dev, struct fwu_mdata *mdata,
+ uint part_num)
+{
+ const struct fwu_mdata_ops *ops = device_get_ops(dev);
+
+ if (!ops->write_mdata_partition) {
+ log_debug("write_mdata_partition() method not defined\n");
+ return -ENOSYS;
+ }
+
+ return ops->write_mdata_partition(dev, mdata, part_num);
+}
+
+/**
+ * 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 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->check_mdata) {
+ log_debug("check_mdata() method not defined\n");
+ return -ENOSYS;
+ }
+
+ return ops->check_mdata(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()
+ *
+ * The sequence of updating the partitions should be, update the
+ * primary metadata partition (first partition encountered), followed
+ * by updating the secondary partition. With this update sequence, in
+ * the rare scenario that the two metadata partitions are valid but do
+ * not match, maybe due to power outage at the time of updating the
+ * metadata copies, the secondary partition can be updated from the
+ * primary.
+ *
+ * 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/drivers/fwu-mdata/gpt_blk.c b/drivers/fwu-mdata/gpt_blk.c
new file mode 100644
index 0000000..d35ce49
--- /dev/null
+++ b/drivers/fwu-mdata/gpt_blk.c
@@ -0,0 +1,290 @@
+// 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>
+
+enum {
+ MDATA_READ = 1,
+ MDATA_WRITE,
+};
+
+static int gpt_get_mdata_partitions(struct blk_desc *desc,
+ uint mdata_parts[2])
+{
+ int i, ret;
+ u32 nparts;
+ efi_guid_t part_type_guid;
+ struct disk_partition info;
+ const efi_guid_t fwu_mdata_guid = FWU_MDATA_GUID;
+
+ nparts = 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)) {
+ if (nparts < 2)
+ mdata_parts[nparts] = i;
+ ++nparts;
+ }
+ }
+
+ if (nparts != 2) {
+ log_debug("Expect two copies of the FWU metadata instead of %d\n",
+ nparts);
+ 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))
+ 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);
+
+ if (!mdata)
+ return -ENOMEM;
+
+ 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 fwu_gpt_update_mdata(struct udevice *dev, struct fwu_mdata *mdata)
+{
+ int ret;
+ struct blk_desc *desc;
+ uint mdata_parts[2];
+ 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, mdata_parts);
+ if (ret < 0) {
+ log_debug("Error getting the FWU metadata partitions\n");
+ return -ENOENT;
+ }
+
+ /* First write the primary partition */
+ ret = gpt_read_write_mdata(desc, mdata, MDATA_WRITE, mdata_parts[0]);
+ if (ret < 0) {
+ log_debug("Updating primary FWU metadata partition failed\n");
+ return ret;
+ }
+
+ /* And now the replica */
+ ret = gpt_read_write_mdata(desc, mdata, MDATA_WRITE, mdata_parts[1]);
+ 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 mdata_parts[2];
+
+ ret = gpt_get_mdata_partitions(desc, mdata_parts);
+
+ if (ret < 0) {
+ log_debug("Error getting the FWU metadata partitions\n");
+ return -ENOENT;
+ }
+
+ ret = gpt_read_write_mdata(desc, mdata, MDATA_READ, mdata_parts[0]);
+ 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_write_mdata(desc, mdata, MDATA_READ, mdata_parts[1]);
+ 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 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_gpt_get_mdata_partitions(struct udevice *dev, uint *mdata_parts)
+{
+ struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
+
+ return gpt_get_mdata_partitions(dev_get_uclass_plat(priv->blk_dev),
+ mdata_parts);
+}
+
+static int fwu_gpt_read_mdata_partition(struct udevice *dev,
+ struct fwu_mdata *mdata, uint part_num)
+{
+ struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
+
+ return gpt_read_write_mdata(dev_get_uclass_plat(priv->blk_dev),
+ mdata, MDATA_READ, part_num);
+}
+
+static int fwu_gpt_write_mdata_partition(struct udevice *dev,
+ struct fwu_mdata *mdata, uint part_num)
+{
+ struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
+
+ return gpt_read_write_mdata(dev_get_uclass_plat(priv->blk_dev),
+ mdata, MDATA_WRITE, part_num);
+}
+
+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 = {
+ .get_mdata = fwu_gpt_get_mdata,
+ .update_mdata = fwu_gpt_update_mdata,
+ .get_mdata_part_num = fwu_gpt_get_mdata_partitions,
+ .read_mdata_partition = fwu_gpt_read_mdata_partition,
+ .write_mdata_partition = fwu_gpt_write_mdata_partition,
+};
+
+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/configs/stm32mp15_common.h b/include/configs/stm32mp15_common.h
index bd8e16b..214901c 100644
--- a/include/configs/stm32mp15_common.h
+++ b/include/configs/stm32mp15_common.h
@@ -32,6 +32,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
/*****************************************************************************/
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 4b2c323..1f3cf8c 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -59,6 +59,7 @@ enum uclass_id {
UCLASS_FPGA, /* FPGA device */
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/event.h b/include/event.h
index 3e6dcbc..e4580b6 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
};
diff --git a/include/fwu.h b/include/fwu.h
new file mode 100644
index 0000000..0919ced
--- /dev/null
+++ b/include/fwu.h
@@ -0,0 +1,412 @@
+/* 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;
+
+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
+ * @update_mdata() - Update the FWU metadata copy
+ */
+struct fwu_mdata_ops {
+ /**
+ * check_mdata() - 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 copy.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+ int (*check_mdata)(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);
+
+ /**
+ * 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);
+
+ /**
+ * get_mdata_part_num() - Get the FWU metadata partition numbers
+ * @dev: FWU metadata device
+ * @mdata_parts: array for storing the metadata partition numbers
+ *
+ * Get the partition numbers on the storage device on which the
+ * FWU metadata is stored. Two partition numbers will be returned.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+ int (*get_mdata_part_num)(struct udevice *dev, uint *mdata_parts);
+
+ /**
+ * read_mdata_partition() - Read the FWU metadata from a partition
+ * @dev: FWU metadata device
+ * @mdata: Copy of the FWU metadata
+ * @part_num: Partition number from which FWU metadata is to be read
+ *
+ * Read the FWU metadata from the specified partition number
+ *
+ * Return: 0 if OK, -ve on error
+ */
+ int (*read_mdata_partition)(struct udevice *dev,
+ struct fwu_mdata *mdata, uint part_num);
+
+ /**
+ * write_mdata_partition() - Write the FWU metadata to a partition
+ * @dev: FWU metadata device
+ * @mdata: Copy of the FWU metadata
+ * @part_num: Partition number to which FWU metadata is to be written
+ *
+ * Write the FWU metadata to the specified partition number
+ *
+ * Return: 0 if OK, -ve on error
+ */
+ int (*write_mdata_partition)(struct udevice *dev,
+ struct fwu_mdata *mdata, uint part_num);
+};
+
+#define FWU_MDATA_VERSION 0x1
+#define FWU_IMAGE_ACCEPTED 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)
+
+/*
+* 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_check_mdata_validity() - Check for validity of the FWU metadata copies
+ *
+ * Read both the metadata copies from the storage media, verify their
+ * checksum, and ascertain that both copies match. If one of the copies
+ * has gone bad, restore it from the good copy.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_check_mdata_validity(void);
+
+/**
+ * fwu_get_mdata_part_num() - Get the FWU metadata partition numbers
+ * @dev: FWU metadata device
+ * @mdata_parts: array for storing the metadata partition numbers
+ *
+ * Get the partition numbers on the storage device on which the
+ * FWU metadata is stored. Two partition numbers will be returned
+ * through the array.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_get_mdata_part_num(struct udevice *dev, uint *mdata_parts);
+
+/**
+ * fwu_read_mdata_partition() - Read the FWU metadata from a partition
+ * @dev: FWU metadata device
+ * @mdata: Copy of the FWU metadata
+ * @part_num: Partition number from which FWU metadata is to be read
+ *
+ * Read the FWU metadata from the specified partition number
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_read_mdata_partition(struct udevice *dev, struct fwu_mdata *mdata,
+ uint part_num);
+
+/**
+ * fwu_write_mdata_partition() - Write the FWU metadata to a partition
+ * @dev: FWU metadata device
+ * @mdata: Copy of the FWU metadata
+ * @part_num: Partition number to which FWU metadata is to be written
+ *
+ * Write the FWU metadata to the specified partition number
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_write_mdata_partition(struct udevice *dev, struct fwu_mdata *mdata,
+ uint part_num);
+
+/**
+ * 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()
+ *
+ * The sequence of updating the partitions should be, update the
+ * primary metadata partition (first partition encountered), followed
+ * by updating the secondary partition. With this update sequence, in
+ * the rare scenario that the two metadata partitions are valid but do
+ * not match, maybe due to power outage at the time of updating the
+ * metadata copies, the secondary partition can be updated from the
+ * primary.
+ *
+ * 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(uint *active_idxp);
+
+/**
+ * fwu_set_active_index() - Set active_index in the FWU metadata
+ * @active_idx: active_index value to be set
+ *
+ * Update the active_index field in the FWU metadata
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_set_active_index(uint active_idx);
+
+/**
+ * fwu_get_image_index() - Get the Image Index to be used for capsule update
+ * @image_index: The Image Index for the image
+ *
+ * The FWU multi bank update feature computes the value of image_index at
+ * runtime, based on the bank to which the image needs to be written to.
+ * Derive the image_index value for the image.
+ *
+ * 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(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 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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 if checks do not pass
+ *
+ */
+u8 fwu_update_checks_pass(void);
+
+/**
+ * fwu_empty_capsule_checks_pass() - Check if empty capsule can be processed
+ *
+ * Check if the empty capsule can be processed to either accept or revert
+ * an earlier executed update. The empty capsules need to be processed
+ * only when the platform is in Trial State and the boot time checks have
+ * passed
+ *
+ * Return: 1 if OK, 0 if not to be allowed
+ *
+ */
+u8 fwu_empty_capsule_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/include/fwu_mdata.h b/include/fwu_mdata.h
new file mode 100644
index 0000000..8fda4f4
--- /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/Kconfig b/lib/Kconfig
index 6121c80..6abe1d0 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 e3deb15..f2cfd1e 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 397e393..1163a2e 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 */
@@ -386,6 +393,132 @@ 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 -ERANGE:
+ case -EIO:
+ ret = EFI_DEVICE_ERROR;
+ break;
+ case -EINVAL:
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case -ENODEV:
+ ret = EFI_NOT_FOUND;
+ 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_guid_t *image_guid;
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+
+ 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_debug("Reverted the FWU active_index. Recommend rebooting the system\n");
+ else
+ log_err("Failed to revert the FWU boot index\n");
+ } else if (!guidcmp(&capsule->capsule_guid,
+ &fwu_guid_os_request_fw_accept)) {
+ /*
+ * Image accepted by the OS. Set the acceptance
+ * status for the image.
+ */
+ image_guid = (void *)(char *)capsule +
+ capsule->header_size;
+
+ status = fwu_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 ? true : false;
+}
+
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os)
+{
+ int status;
+ uint 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_set_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");
+ 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
@@ -408,7 +541,32 @@ 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;
+ uint update_index;
+ bool fw_accept_os;
+
+ if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+ if (fwu_empty_capsule_checks_pass() &&
+ 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;
+ }
+
+
+ /* Obtain the update_index from the platform */
+ status = fwu_plat_get_update_index(&update_index);
+ if (status < 0) {
+ log_err("Failed to get the FWU update_index value\n");
+ return EFI_DEVICE_ERROR;
+ }
+
+ fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
+ }
/* sanity check */
if (capsule_data->header_size < sizeof(*capsule) ||
@@ -495,6 +653,34 @@ static efi_status_t efi_capsule_update_firmware(
efi_free_pool(abort_reason);
goto out;
}
+
+ if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+ image_type_id = &image->update_image_type_id;
+ 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:
@@ -1103,6 +1289,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;
@@ -1130,12 +1319,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);
@@ -1143,6 +1339,7 @@ efi_status_t efi_launch_capsules(void)
free(capsule);
} else {
log_err("Reading capsule %ls failed\n", files[i]);
+ update_status = false;
}
/* delete a capsule either in case of success or failure */
ret = efi_capsule_delete_file(files[i]);
@@ -1150,8 +1347,17 @@ 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 (capsule_update == true && update_status == 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]);
free(files);
diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index 30cafd1..93e2b01 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -10,6 +10,7 @@
#include <charset.h>
#include <dfu.h>
#include <efi_loader.h>
+#include <fwu.h>
#include <image.h>
#include <signatures.h>
@@ -389,6 +390,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
efi_status_t (*progress)(efi_uintn_t completion),
u16 **abort_reason)
{
+ int ret;
efi_status_t status;
EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
@@ -401,6 +403,18 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
if (status != EFI_SUCCESS)
return EFI_EXIT(status);
+ if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+ /*
+ * Based on the value of update bank, derive the
+ * image index value.
+ */
+ ret = fwu_get_image_index(&image_index);
+ if (ret) {
+ log_debug("Unable to get FWU image_index\n");
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+ }
+ }
+
if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
NULL, NULL))
return EFI_EXIT(EFI_DEVICE_ERROR);
diff --git a/lib/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig
new file mode 100644
index 0000000..78759e6
--- /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 0000000..1993088
--- /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
new file mode 100644
index 0000000..5313d07
--- /dev/null
+++ b/lib/fwu_updates/fwu.c
@@ -0,0 +1,719 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022, Linaro Limited
+ */
+
+#include <dm.h>
+#include <efi.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <event.h>
+#include <fwu.h>
+#include <fwu_mdata.h>
+#include <malloc.h>
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+static u8 in_trial;
+static u8 boottime_check;
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <u-boot/crc.h>
+
+enum {
+ IMAGE_ACCEPT_SET = 1,
+ IMAGE_ACCEPT_CLEAR,
+};
+
+enum {
+ PRIMARY_PART = 1,
+ SECONDARY_PART,
+ BOTH_PARTS,
+};
+
+static int fwu_get_dev_mdata(struct udevice **dev, struct fwu_mdata *mdata)
+{
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_FWU_MDATA, dev);
+ if (ret) {
+ log_debug("Cannot find fwu device\n");
+ return ret;
+ }
+
+ if (!mdata)
+ return 0;
+
+ ret = fwu_get_mdata(*dev, mdata);
+ if (ret < 0)
+ log_debug("Unable to get valid FWU metadata\n");
+
+ return ret;
+}
+
+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 trial_counter_read(u16 *trial_state_ctr)
+{
+ efi_status_t status;
+ efi_uintn_t var_size;
+
+ var_size = (efi_uintn_t)sizeof(trial_state_ctr);
+ status = efi_get_variable_int(u"TrialStateCtr",
+ &efi_global_variable_guid,
+ NULL,
+ &var_size, trial_state_ctr,
+ NULL);
+ if (status != EFI_SUCCESS) {
+ log_err("Unable to read TrialStateCtr variable\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int fwu_trial_count_update(void)
+{
+ int ret;
+ u16 trial_state_ctr;
+
+ ret = trial_counter_read(&trial_state_ctr);
+ if (ret) {
+ log_debug("Unable to read trial_state_ctr\n");
+ 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");
+ ret = 1;
+ } else {
+ ret = trial_counter_update(&trial_state_ctr);
+ if (ret)
+ log_err("Unable to increment TrialStateCtr variable\n");
+ }
+
+out:
+ return ret;
+}
+
+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) {
+ log_info("System booting in Trial State\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int fwu_get_image_type_id(u8 *image_index, efi_guid_t *image_type_id)
+{
+ u8 index;
+ int i;
+ struct efi_fw_image *image;
+
+ index = *image_index;
+ image = update_info.images;
+ for (i = 0; i < num_image_type_guids; i++) {
+ if (index == image[i].image_index) {
+ guidcpy(image_type_id, &image[i].image_type_id);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * 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_check_mdata_validity() - Check for validity of the FWU metadata copies
+ *
+ * Read both the metadata copies from the storage media, verify their checksum,
+ * and ascertain that both copies match. If one of the copies has gone bad,
+ * restore it from the good copy.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_check_mdata_validity(void)
+{
+ int ret;
+ struct udevice *dev;
+ struct fwu_mdata pri_mdata;
+ struct fwu_mdata secondary_mdata;
+ uint mdata_parts[2];
+ uint valid_partitions, invalid_partitions;
+
+ ret = fwu_get_dev_mdata(&dev, NULL);
+ if (ret)
+ return ret;
+
+ /*
+ * Check if the platform has defined its own
+ * function to check the metadata partitions'
+ * validity. If so, that takes precedence.
+ */
+ ret = fwu_mdata_check(dev);
+ if (!ret || ret != -ENOSYS)
+ return ret;
+
+ /*
+ * Two FWU metadata partitions are expected.
+ * If we don't have two, user needs to create
+ * them first
+ */
+ valid_partitions = 0;
+ ret = fwu_get_mdata_part_num(dev, mdata_parts);
+ if (ret < 0) {
+ log_debug("Error getting the FWU metadata partitions\n");
+ return -ENOENT;
+ }
+
+ ret = fwu_read_mdata_partition(dev, &pri_mdata, mdata_parts[0]);
+ if (!ret) {
+ ret = fwu_verify_mdata(&pri_mdata, 1);
+ if (!ret)
+ valid_partitions |= PRIMARY_PART;
+ }
+
+ ret = fwu_read_mdata_partition(dev, &secondary_mdata, mdata_parts[1]);
+ if (!ret) {
+ ret = fwu_verify_mdata(&secondary_mdata, 0);
+ if (!ret)
+ valid_partitions |= SECONDARY_PART;
+ }
+
+ if (valid_partitions == (PRIMARY_PART | SECONDARY_PART)) {
+ /*
+ * Before returning, check that both the
+ * FWU metadata copies are the same. If not,
+ * populate the secondary partition from the
+ * primary partition copy.
+ */
+ if (!memcmp(&pri_mdata, &secondary_mdata,
+ sizeof(struct fwu_mdata))) {
+ ret = 0;
+ } else {
+ log_info("Both FWU metadata copies are valid but do not match.");
+ log_info(" Restoring the secondary partition from the primary\n");
+ ret = fwu_write_mdata_partition(dev, &pri_mdata,
+ mdata_parts[1]);
+ if (ret)
+ log_debug("Restoring secondary FWU metadata partition failed\n");
+ }
+ goto out;
+ }
+
+ if (!(valid_partitions & BOTH_PARTS)) {
+ log_info("Both FWU metadata partitions invalid\n");
+ ret = -EBADMSG;
+ goto out;
+ }
+
+ invalid_partitions = valid_partitions ^ BOTH_PARTS;
+ ret = fwu_write_mdata_partition(dev,
+ (invalid_partitions == PRIMARY_PART) ?
+ &secondary_mdata : &pri_mdata,
+ (invalid_partitions == PRIMARY_PART) ?
+ mdata_parts[0] : mdata_parts[1]);
+
+ if (ret)
+ log_debug("Restoring %s FWU metadata partition failed\n",
+ (invalid_partitions == PRIMARY_PART) ?
+ "primary" : "secondary");
+
+out:
+ return ret;
+}
+
+/**
+ * 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(uint *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_set_active_index() - Set active_index in the FWU metadata
+ * @active_idx: active_index value to be set
+ *
+ * Update the active_index field in the FWU metadata
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_set_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) {
+ 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_index: The Image Index for the image
+ *
+ * The FWU multi bank update feature computes the value of image_index at
+ * runtime, based on the bank to which the image needs to be written to.
+ * Derive the image_index value for the image.
+ *
+ * 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(u8 *image_index)
+{
+ int ret, i;
+ u8 alt_num;
+ uint update_bank;
+ efi_guid_t *image_guid, image_type_id;
+ 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 = fwu_plat_get_update_index(&update_bank);
+ if (ret) {
+ log_debug("Failed to get the FWU update bank\n");
+ goto out;
+ }
+
+ ret = fwu_get_image_type_id(image_index, &image_type_id);
+ if (ret) {
+ log_debug("Unable to get image_type_id for image_index %u\n",
+ *image_index);
+ goto out;
+ }
+
+ 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, &alt_num);
+ 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",
+ alt_num, image_guid);
+ *image_index = alt_num + 1;
+ }
+
+ 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) {
+ 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);
+}
+
+/**
+ * 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;
+}
+
+/**
+ * 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 if checks do not pass
+ *
+ */
+u8 fwu_update_checks_pass(void)
+{
+ return !in_trial && boottime_check;
+}
+
+/**
+ * fwu_empty_capsule_checks_pass() - Check if empty capsule can be processed
+ *
+ * Check if the empty capsule can be processed to either accept or revert
+ * an earlier executed update. The empty capsules need to be processed
+ * only when the platform is in Trial State and the boot time checks have
+ * passed
+ *
+ * Return: 1 if OK, 0 if not to be allowed
+ *
+ */
+u8 fwu_empty_capsule_checks_pass(void)
+{
+ return in_trial && 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;
+ u32 boot_idx, active_idx;
+ struct udevice *dev;
+ struct fwu_mdata mdata = { 0 };
+
+ /* Don't have boot time checks on sandbox */
+ if (IS_ENABLED(CONFIG_SANDBOX)) {
+ boottime_check = 1;
+ return 0;
+ }
+
+ ret = fwu_check_mdata_validity();
+ 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_set_active_index(boot_idx);
+ if (!ret)
+ boottime_check = 1;
+
+ return 0;
+ }
+
+ if (efi_init_obj_list() != EFI_SUCCESS)
+ return 0;
+
+ ret = fwu_get_dev_mdata(&dev, &mdata);
+ if (ret)
+ return ret;
+
+ in_trial = in_trial_state(&mdata);
+ if (!in_trial || (ret = fwu_trial_count_update()) > 0)
+ ret = trial_counter_update(NULL);
+
+ if (!ret)
+ boottime_check = 1;
+
+ return 0;
+}
+EVENT_SPY(EVT_MAIN_LOOP, fwu_boottime_checks);
diff --git a/lib/fwu_updates/fwu_gpt.c b/lib/fwu_updates/fwu_gpt.c
new file mode 100644
index 0000000..21a573c
--- /dev/null
+++ b/lib/fwu_updates/fwu_gpt.c
@@ -0,0 +1,123 @@
+// 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_alt_num_for_dfu_dev(struct dfu_entity *dfu, int dev_num,
+ int part, unsigned char dfu_dev,
+ u8 *alt_num)
+{
+ int ret;
+
+ switch(dfu_dev) {
+ case DFU_DEV_MMC:
+ if (dfu->layout == DFU_RAW_ADDR &&
+ dfu->data.mmc.dev_num == dev_num &&
+ dfu->data.mmc.part == part) {
+ *alt_num = dfu->alt;
+ ret = 0;
+ } else {
+ ret = -ENOENT;
+ }
+ break;
+ default:
+ ret = -ENOENT;
+ }
+
+ return ret;
+}
+
+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;
+ struct dfu_entity *dfu;
+
+ dev_num = desc->devnum;
+ part = get_gpt_dfu_identifier(desc, image_guid);
+ if (part < 0)
+ return -ENOENT;
+
+ ret = dfu_init_env_entities(NULL, NULL);
+ if (ret)
+ goto out;
+
+ i = 0;
+ while (true) {
+ dfu = dfu_get_entity(i++);
+ if (!dfu) {
+ ret = -ENOENT;
+ break;
+ }
+
+ if (dfu->dev_type != dfu_dev)
+ continue;
+
+ ret = fwu_alt_num_for_dfu_dev(dfu, dev_num, part, dfu_dev,
+ alt_num);
+ if (!ret)
+ break;
+ }
+
+out:
+ 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);
+}
diff --git a/test/dm/Makefile b/test/dm/Makefile
index cc3cc45..1901f22 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fastboot.o
endif
obj-$(CONFIG_FIRMWARE) += firmware.o
obj-$(CONFIG_DM_FPGA) += fpga.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 0000000..b179a65
--- /dev/null
+++ b/test/dm/fwu_mdata.c
@@ -0,0 +1,147 @@
+// 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_err(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_err(UCLASS_FWU_MDATA, &dev));
+
+ ut_assertok(fwu_get_mdata(dev, &mdata));
+
+ active_idx = (mdata.active_index + 1) % CONFIG_FWU_NUM_BANKS;
+ ut_assertok(fwu_set_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_err(UCLASS_FWU_MDATA, &dev));
+
+ ut_assertok(fwu_check_mdata_validity());
+
+ 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 0000000..b980341
--- /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 8f75b55..d28b53a 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 d6ca9b1..8c2d616 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 af6a710..26be0a7 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -72,7 +72,9 @@ 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
+ifneq ($(CONFIG_CMD_BOOTEFI_SELFTEST)$(CONFIG_FWU_MDATA_GPT_BLK),)
+hostprogs-y += file2include
+endif
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
diff --git a/tools/eficapsule.h b/tools/eficapsule.h
index d63b831..072a4b5 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 5f74d23..b71537b 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:o:dhAR";
+
+enum {
+ CAPSULE_NORMAL_BLOB = 0,
+ CAPSULE_ACCEPT,
+ CAPSULE_REVERT,
+} capsule_type;
static struct option options[] = {
{"guid", required_argument, NULL, 'g'},
@@ -39,6 +45,9 @@ 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'},
+ {"capoemflag", required_argument, NULL, 'o'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0},
};
@@ -55,6 +64,9 @@ 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-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n"
"\t-h, --help print a help message\n",
tool_name);
}
@@ -377,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.
@@ -389,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;
@@ -454,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)
@@ -564,6 +580,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
@@ -582,6 +641,7 @@ int main(int argc, char **argv)
unsigned char uuid_buf[16];
unsigned long index, instance;
uint64_t mcount;
+ unsigned long oemflags;
char *privkey_file, *cert_file;
int c, idx;
@@ -592,6 +652,8 @@ int main(int argc, char **argv)
privkey_file = NULL;
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)
@@ -639,22 +701,58 @@ 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;
+ case 'o':
+ oemflags = strtoul(optarg, NULL, 0);
+ if (oemflags > 0xffff) {
+ fprintf(stderr,
+ "oemflags must be between 0x0 and 0xffff\n");
+ exit(1);
+ }
+ 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, (uint16_t)oemflags) < 0) {
fprintf(stderr, "Creating firmware capsule failed\n");
exit(EXIT_FAILURE);
}