summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2022-11-01 09:32:21 -0400
committerTom Rini <[email protected]>2022-11-01 09:32:21 -0400
commitc8d9ff634fc429db5acf2f5386ea937f0fef1ae7 (patch)
tree5ee0a5efa73f325b172a16db7551af87725fdb1f /drivers
parenta90afc6730e6c67ad37f4c98a02891a93b4ff971 (diff)
parent75f11c3bfdcfbadad0265eda74c372e52423ae4c (diff)
Merge branch '2022-10-31-FWU-add-FWU-multi-bank-update-feature-support'
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
Diffstat (limited to 'drivers')
-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
6 files changed, 503 insertions, 0 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 8b6fead3510..75ac149d311 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 a1700c819de..ac2d83af4e3 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 00000000000..36c4479a59e
--- /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 00000000000..3fee64c10c9
--- /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 00000000000..b477e9603fb
--- /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 00000000000..d35ce49c5c1
--- /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),
+};