summaryrefslogtreecommitdiff
path: root/drivers/fwu-mdata/gpt_blk.c
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 /drivers/fwu-mdata/gpt_blk.c
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/fwu-mdata/gpt_blk.c')
-rw-r--r--drivers/fwu-mdata/gpt_blk.c290
1 files changed, 290 insertions, 0 deletions
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),
+};