diff mbox series

[v5,13/23] FWU: Add FWU metadata access driver for non-GPT MTD devices

Message ID 20220609123010.1017463-14-sughosh.ganu@linaro.org
State New
Headers show
Series FWU: Add FWU Multi Bank Update feature support | expand

Commit Message

Sughosh Ganu June 9, 2022, 12:30 p.m. UTC
From: Masami Hiramatsu <masami.hiramatsu@linaro.org>

For the platform which doesn't have GPT partitions for the firmware
but on MTD devices, the FWU metadata is stored on MTD device as raw
image at specific offset. This driver gives the access methods
for the FWU metadata information on such MTD devices.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@linaro.org>
Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 drivers/fwu-mdata/Kconfig         |   8 +
 drivers/fwu-mdata/Makefile        |   1 +
 drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++++++++++++++
 3 files changed, 317 insertions(+)
 create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c

Comments

Etienne Carriere June 21, 2022, 10:56 a.m. UTC | #1
On Thu, 9 Jun 2022 at 14:31, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> From: Masami Hiramatsu <masami.hiramatsu@linaro.org>
>
> For the platform which doesn't have GPT partitions for the firmware
> but on MTD devices, the FWU metadata is stored on MTD device as raw
> image at specific offset. This driver gives the access methods
> for the FWU metadata information on such MTD devices.
>
> Signed-off-by: Masami Hiramatsu <masami.hiramatsu@linaro.org>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>  drivers/fwu-mdata/Kconfig         |   8 +
>  drivers/fwu-mdata/Makefile        |   1 +
>  drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++++++++++++++
>  3 files changed, 317 insertions(+)
>  create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c
>
> diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig
> index d5edef19d6..a8fa9ad783 100644
> --- a/drivers/fwu-mdata/Kconfig
> +++ b/drivers/fwu-mdata/Kconfig
> @@ -14,3 +14,11 @@ config FWU_MDATA_GPT_BLK
>         help
>           Enable support for accessing FWU Metadata on GPT partitioned
>           block devices.
> +
> +config FWU_MDATA_MTD
> +       bool "FWU Metadata access for non-GPT MTD devices"
> +       depends on DM_FWU_MDATA && MTD
> +       help
> +         Enable support for accessing FWU Metadata on non-partitioned
> +         (or non-GPT partitioned, e.g. partition nodes in devicetree)
> +         MTD devices.
> diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile
> index 12a5b4fe04..c574c59be2 100644
> --- a/drivers/fwu-mdata/Makefile
> +++ b/drivers/fwu-mdata/Makefile
> @@ -5,3 +5,4 @@
>
>  obj-$(CONFIG_DM_FWU_MDATA) += fwu-mdata-uclass.o
>  obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_mdata_gpt_blk.o
> +obj-$(CONFIG_FWU_MDATA_MTD) += fwu_mdata_mtd.o
> diff --git a/drivers/fwu-mdata/fwu_mdata_mtd.c b/drivers/fwu-mdata/fwu_mdata_mtd.c
> new file mode 100644
> index 0000000000..9eb471e73e
> --- /dev/null
> +++ b/drivers/fwu-mdata/fwu_mdata_mtd.c
> @@ -0,0 +1,308 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (c) 2022, Linaro Limited
> + */
> +
> +#include <efi_loader.h>
> +#include <fwu.h>
> +#include <fwu_mdata.h>
> +#include <malloc.h>
> +#include <memalign.h>
> +#include <spi.h>
> +#include <spi_flash.h>
> +#include <flash.h>
> +
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <u-boot/crc.h>
> +
> +struct fwu_mdata_mtd_priv {
> +       struct mtd_info *mtd;
> +       u32 pri_offset;
> +       u32 sec_offset;
> +};

Add an inline description of the fields.

> +
> +enum fwu_mtd_op {
> +       FWU_MTD_READ,
> +       FWU_MTD_WRITE,
> +};
> +
> +static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
> +{
> +       return !do_div(size, mtd->erasesize);
> +}
> +
> +static int mtd_io_data(struct mtd_info *mtd, u32 offs, u32 size, void *data,
> +                      enum fwu_mtd_op op)
> +{
> +       struct mtd_oob_ops io_op ={};
> +       u64 lock_offs, lock_len;
> +       size_t len;
> +       void *buf;
> +       int ret;
> +
> +       if (!mtd_is_aligned_with_block_size(mtd, offs))
> +               return -EINVAL;
> +       lock_offs = offs;
> +       lock_len = round_up(size, mtd->erasesize);
> +
> +       ret = mtd_unlock(mtd, lock_offs, lock_len);
> +       if (ret && ret != -EOPNOTSUPP)
> +               return ret;
> +
> +       if (op == FWU_MTD_WRITE) {
> +               struct erase_info erase_op = {};
> +
> +               /* This will expand erase size to align with the block size */
> +               erase_op.mtd = mtd;
> +               erase_op.addr = lock_offs;
> +               erase_op.len = lock_len;
> +               erase_op.scrub = 0;
> +
> +               ret = mtd_erase(mtd, &erase_op);
> +               if (ret)
> +                       goto lock_out;
> +       }
> +
> +       /* Also, expand the write size to align with the write size */
> +       len = round_up(size, mtd->writesize);
> +
> +       buf = memalign(ARCH_DMA_MINALIGN, len);
> +       if (!buf) {
> +               ret = -ENOMEM;
> +               goto lock_out;
> +       }
> +       io_op.mode = MTD_OPS_AUTO_OOB;
> +       io_op.len = len;
> +       io_op.ooblen = 0;
> +       io_op.datbuf = buf;
> +       io_op.oobbuf = NULL;
> +
> +       if (op == FWU_MTD_WRITE) {
> +               memcpy(buf, data, size);
> +               ret = mtd_write_oob(mtd, offs, &io_op);
> +       } else {
> +               ret = mtd_read_oob(mtd, offs, &io_op);
> +               if (!ret)
> +                       memcpy(data, buf, size);
> +       }
> +       free(buf);
> +
> +lock_out:
> +       mtd_lock(mtd, lock_offs, lock_len);
> +
> +       return ret;
> +}
> +
> +static int fwu_mtd_load_mdata(struct mtd_info *mtd, struct fwu_mdata **mdata,
> +                             u32 offs, bool primary)
> +{
> +       size_t size = sizeof(struct fwu_mdata);
> +       int ret;
> +
> +       *mdata = malloc(size);
> +       if (!*mdata)
> +               return -ENOMEM;
> +
> +       ret = mtd_io_data(mtd, offs, size, (void *)*mdata, FWU_MTD_READ);
> +       if (ret >= 0) {
> +               ret = fwu_verify_mdata(*mdata, primary);
> +               if (ret < 0) {
> +                       free(*mdata);
> +                       *mdata = NULL;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +static int fwu_mtd_load_primary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
> +                                    struct fwu_mdata **mdata)
> +{
> +       return fwu_mtd_load_mdata(mtd_priv->mtd, mdata, mtd_priv->pri_offset, true);
> +}
> +
> +static int fwu_mtd_load_secondary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
> +                                      struct fwu_mdata **mdata)
> +{
> +       return fwu_mtd_load_mdata(mtd_priv->mtd, mdata, mtd_priv->sec_offset, false);
> +}
> +
> +static int fwu_mtd_save_primary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
> +                                    struct fwu_mdata *mdata)
> +{
> +       return mtd_io_data(mtd_priv->mtd, mtd_priv->pri_offset,
> +                          sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE);
> +}
> +
> +static int fwu_mtd_save_secondary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
> +                                      struct fwu_mdata *mdata)
> +{
> +       return mtd_io_data(mtd_priv->mtd, mtd_priv->sec_offset,
> +                          sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE);
> +}
> +
> +static int fwu_mtd_get_valid_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
> +                                 struct fwu_mdata **mdata)
> +{
> +       if (fwu_mtd_load_primary_mdata(mtd_priv, mdata) == 0)
> +               return 0;

Is it possible  this can fail knowing that content of both images were
checked at probe time?
If it can fail, then, as for the GPT case, i think this function
should ensure the primary and secondary images, in the boot media, are
valid and consistent (sae data).
For example calling back fwu_mtd_mdata_check() for that purpose.
Otherwise, if fwu_mtd_update_mdata() is interrupted (e.g. power loss),
it would leave no valid fwu_mdata in the storage, preventing the
system from rebooting.


> +
> +       log_err("Failed to load/verify primary mdata. Try secondary.\n");
> +
> +       if (fwu_mtd_load_secondary_mdata(mtd_priv, mdata) == 0)
> +               return 0;
> +
> +       log_err("Failed to load/verify secondary mdata.\n");
> +
> +       return -1;
> +}
> +
> +static int fwu_mtd_update_mdata(struct udevice *dev, struct fwu_mdata *mdata)
> +{
> +       struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
> +       int ret;
> +
> +       /* Update mdata crc32 field */
> +       mdata->crc32 = crc32(0, (void *)&mdata->version,
> +                            sizeof(*mdata) - sizeof(u32));
> +
> +       /* First write the primary mdata */
> +       ret = fwu_mtd_save_primary_mdata(mtd_priv, mdata);
> +       if (ret < 0) {
> +               log_err("Failed to update the primary mdata.\n");
> +               return ret;
> +       }
> +
> +       /* And now the replica */
> +       ret = fwu_mtd_save_secondary_mdata(mtd_priv, mdata);
> +       if (ret < 0) {
> +               log_err("Failed to update the secondary mdata.\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int fwu_mtd_mdata_check(struct udevice *dev)
> +{
> +       struct fwu_mdata *primary = NULL, *secondary = NULL;
> +       struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
> +       int ret;
> +
> +       ret = fwu_mtd_load_primary_mdata(mtd_priv, &primary);
> +       if (ret < 0)
> +               log_err("Failed to read the primary mdata: %d\n", ret);
> +
> +       ret = fwu_mtd_load_secondary_mdata(mtd_priv, &secondary);
> +       if (ret < 0)
> +               log_err("Failed to read the secondary mdata: %d\n", ret);
> +
> +       if (primary && secondary) {
> +               if (memcmp(primary, secondary, sizeof(struct fwu_mdata))) {
> +                       log_err("The primary and the secondary mdata are different\n");
> +                       ret = -1;
> +               }
> +       } else if (primary) {
> +               ret = fwu_mtd_save_secondary_mdata(mtd_priv, primary);
> +               if (ret < 0)
> +                       log_err("Restoring secondary mdata partition failed\n");
> +       } else if (secondary) {
> +               ret = fwu_mtd_save_primary_mdata(mtd_priv, secondary);
> +               if (ret < 0)
> +                       log_err("Restoring primary mdata partition failed\n");
> +       }
> +
> +       free(primary);
> +       free(secondary);
> +       return ret;
> +}
> +
> +static int fwu_mtd_get_mdata(struct udevice *dev, struct fwu_mdata **mdata)
> +{
> +       struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
> +
> +       return fwu_mtd_get_valid_mdata(mtd_priv, mdata);
> +}
> +
> +/**
> + * fwu_mdata_mtd_of_to_plat() - Translate from DT to fwu mdata device
> + */
> +static int fwu_mdata_mtd_of_to_plat(struct udevice *dev)
> +{
> +       struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
> +       const fdt32_t *phandle_p = NULL;
> +       struct udevice *mtd_dev;
> +       struct mtd_info *mtd;
> +       int ret, size;
> +       u32 phandle;
> +
> +       /* Find the FWU mdata storage device */
> +       phandle_p = ofnode_get_property(dev_ofnode(dev),
> +                                       "fwu-mdata-store", &size);
> +       if (!phandle_p) {
> +               log_err("fwu-mdata-store property not found\n");
> +               return -ENOENT;
> +       }
> +
> +       phandle = fdt32_to_cpu(*phandle_p);
> +
> +       ret = device_get_global_by_ofnode(
> +               ofnode_get_by_phandle(phandle),
> +               &mtd_dev);
> +       if (ret)
> +               return ret;
> +
> +       mtd_probe_devices();
> +
> +       mtd_for_each_device(mtd) {
> +               if (mtd->dev == mtd_dev) {
> +                       mtd_priv->mtd = mtd;
> +                       log_debug("Found the FWU mdata mtd device %s\n", mtd->name);
> +                       break;
> +               }
> +       }
> +       if (!mtd_priv->mtd) {
> +               log_err("Failed to find mtd device by fwu-mdata-store\n");
> +               return -ENOENT;
> +       }
> +
> +       /* Get the offset of primary and seconday mdata */
> +       ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 0,
> +                                   &mtd_priv->pri_offset);
> +       if (ret)
> +               return ret;
> +       ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 1,
> +                                   &mtd_priv->sec_offset);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +static int fwu_mdata_mtd_probe(struct udevice *dev)
> +{
> +       /* Ensure the metadata can be read. */
> +       return fwu_mtd_mdata_check(dev);
> +}
> +
> +static struct fwu_mdata_ops fwu_mtd_ops = {
> +       .mdata_check = fwu_mtd_mdata_check,
> +       .get_mdata = fwu_mtd_get_mdata,
> +       .update_mdata = fwu_mtd_update_mdata,
> +};
> +
> +static const struct udevice_id fwu_mdata_ids[] = {
> +       { .compatible = "u-boot,fwu-mdata-mtd" },
> +       { }
> +};
> +
> +U_BOOT_DRIVER(fwu_mdata_mtd) = {
> +       .name           = "fwu-mdata-mtd",
> +       .id             = UCLASS_FWU_MDATA,
> +       .of_match       = fwu_mdata_ids,
> +       .ops            = &fwu_mtd_ops,
> +       .probe          = fwu_mdata_mtd_probe,
> +       .of_to_plat     = fwu_mdata_mtd_of_to_plat,
> +       .priv_auto      = sizeof(struct fwu_mdata_mtd_priv),
> +};
> --
> 2.25.1
>
Patrick Delaunay June 21, 2022, 12:39 p.m. UTC | #2
Hi,

On 6/9/22 14:30, Sughosh Ganu wrote:
> From: Masami Hiramatsu <masami.hiramatsu@linaro.org>
>
> For the platform which doesn't have GPT partitions for the firmware
> but on MTD devices, the FWU metadata is stored on MTD device as raw
> image at specific offset. This driver gives the access methods
> for the FWU metadata information on such MTD devices.
>
> Signed-off-by: Masami Hiramatsu <masami.hiramatsu@linaro.org>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>   drivers/fwu-mdata/Kconfig         |   8 +
>   drivers/fwu-mdata/Makefile        |   1 +
>   drivers/fwu-mdata/fwu_mdata_mtd.c | 308 ++++++++++++++++++++++++++++++
>   3 files changed, 317 insertions(+)
>   create mode 100644 drivers/fwu-mdata/fwu_mdata_mtd.c
>
> diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig
> index d5edef19d6..a8fa9ad783 100644
> --- a/drivers/fwu-mdata/Kconfig
> +++ b/drivers/fwu-mdata/Kconfig
> @@ -14,3 +14,11 @@ config FWU_MDATA_GPT_BLK
>   	help
>   	  Enable support for accessing FWU Metadata on GPT partitioned
>   	  block devices.
> +
> +config FWU_MDATA_MTD
> +	bool "FWU Metadata access for non-GPT MTD devices"
> +	depends on DM_FWU_MDATA && MTD
> +	help
> +	  Enable support for accessing FWU Metadata on non-partitioned
> +	  (or non-GPT partitioned, e.g. partition nodes in devicetree)
> +	  MTD devices.
> diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile
> index 12a5b4fe04..c574c59be2 100644
> --- a/drivers/fwu-mdata/Makefile
> +++ b/drivers/fwu-mdata/Makefile
> @@ -5,3 +5,4 @@
>   
>   obj-$(CONFIG_DM_FWU_MDATA) += fwu-mdata-uclass.o
>   obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_mdata_gpt_blk.o
> +obj-$(CONFIG_FWU_MDATA_MTD) += fwu_mdata_mtd.o
> diff --git a/drivers/fwu-mdata/fwu_mdata_mtd.c b/drivers/fwu-mdata/fwu_mdata_mtd.c
> new file mode 100644
> index 0000000000..9eb471e73e
> --- /dev/null
> +++ b/drivers/fwu-mdata/fwu_mdata_mtd.c
> @@ -0,0 +1,308 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (c) 2022, Linaro Limited
> + */
> +

+ #define LOG_CATEGORY UCLASS_FWU_MDATA

it is requested for log command to filter by uclass


the next include is not needed ?

#include <common.h>

> +#include <efi_loader.h>
> +#include <fwu.h>
> +#include <fwu_mdata.h>
> +#include <malloc.h>
> +#include <memalign.h>
> +#include <spi.h>
> +#include <spi_flash.h>
> +#include <flash.h>
> +
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <u-boot/crc.h>
> +
> +struct fwu_mdata_mtd_priv {
> +	struct mtd_info *mtd;
> +	u32 pri_offset;
> +	u32 sec_offset;
> +};
> +
> +enum fwu_mtd_op {
> +	FWU_MTD_READ,
> +	FWU_MTD_WRITE,
> +};
> +
> +static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
> +{
> +	return !do_div(size, mtd->erasesize);
> +}
> +
> +static int mtd_io_data(struct mtd_info *mtd, u32 offs, u32 size, void *data,
> +		       enum fwu_mtd_op op)
> +{
> +	struct mtd_oob_ops io_op ={};


missing space : ' = {};'

> +	u64 lock_offs, lock_len;
> +	size_t len;
> +	void *buf;
> +	int ret;
> +
> +	if (!mtd_is_aligned_with_block_size(mtd, offs))
> +		return -EINVAL;
> +	lock_offs = offs;
> +	lock_len = round_up(size, mtd->erasesize);
> +
> +	ret = mtd_unlock(mtd, lock_offs, lock_len);
> +	if (ret && ret != -EOPNOTSUPP)
> +		return ret;
> +
> +	if (op == FWU_MTD_WRITE) {
> +		struct erase_info erase_op = {};
> +
> +		/* This will expand erase size to align with the block size */
> +		erase_op.mtd = mtd;
> +		erase_op.addr = lock_offs;
> +		erase_op.len = lock_len;
> +		erase_op.scrub = 0;
> +
> +		ret = mtd_erase(mtd, &erase_op);
> +		if (ret)
> +			goto lock_out;
> +	}
> +
> +	/* Also, expand the write size to align with the write size */
> +	len = round_up(size, mtd->writesize);
> +
> +	buf = memalign(ARCH_DMA_MINALIGN, len);
> +	if (!buf) {
> +		ret = -ENOMEM;
> +		goto lock_out;
> +	}
> +	io_op.mode = MTD_OPS_AUTO_OOB;
> +	io_op.len = len;
> +	io_op.ooblen = 0;
> +	io_op.datbuf = buf;
> +	io_op.oobbuf = NULL;
> +
> +	if (op == FWU_MTD_WRITE) {
> +		memcpy(buf, data, size);
> +		ret = mtd_write_oob(mtd, offs, &io_op);
> +	} else {
> +		ret = mtd_read_oob(mtd, offs, &io_op);
> +		if (!ret)
> +			memcpy(data, buf, size);
> +	}
> +	free(buf);
> +
> +lock_out:
> +	mtd_lock(mtd, lock_offs, lock_len);
> +
> +	return ret;
> +}
> +
> +static int fwu_mtd_load_mdata(struct mtd_info *mtd, struct fwu_mdata **mdata,
> +			      u32 offs, bool primary)
> +{
> +	size_t size = sizeof(struct fwu_mdata);
> +	int ret;
> +
> +	*mdata = malloc(size);
> +	if (!*mdata)
> +		return -ENOMEM;
> +
> +	ret = mtd_io_data(mtd, offs, size, (void *)*mdata, FWU_MTD_READ);
> +	if (ret >= 0) {
> +		ret = fwu_verify_mdata(*mdata, primary);
> +		if (ret < 0) {
> +			free(*mdata);
> +			*mdata = NULL;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int fwu_mtd_load_primary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
> +				     struct fwu_mdata **mdata)
> +{
> +	return fwu_mtd_load_mdata(mtd_priv->mtd, mdata, mtd_priv->pri_offset, true);
> +}
> +
> +static int fwu_mtd_load_secondary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
> +				       struct fwu_mdata **mdata)
> +{
> +	return fwu_mtd_load_mdata(mtd_priv->mtd, mdata, mtd_priv->sec_offset, false);
> +}
> +
> +static int fwu_mtd_save_primary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
> +				     struct fwu_mdata *mdata)
> +{
> +	return mtd_io_data(mtd_priv->mtd, mtd_priv->pri_offset,
> +			   sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE);
> +}
> +
> +static int fwu_mtd_save_secondary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
> +				       struct fwu_mdata *mdata)
> +{
> +	return mtd_io_data(mtd_priv->mtd, mtd_priv->sec_offset,
> +			   sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE);
> +}
> +
> +static int fwu_mtd_get_valid_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
> +				  struct fwu_mdata **mdata)
> +{
> +	if (fwu_mtd_load_primary_mdata(mtd_priv, mdata) == 0)
> +		return 0;
> +
> +	log_err("Failed to load/verify primary mdata. Try secondary.\n");
> +
> +	if (fwu_mtd_load_secondary_mdata(mtd_priv, mdata) == 0)
> +		return 0;
> +
> +	log_err("Failed to load/verify secondary mdata.\n");
> +
> +	return -1;
> +}
> +
> +static int fwu_mtd_update_mdata(struct udevice *dev, struct fwu_mdata *mdata)
> +{
> +	struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
> +	int ret;
> +
> +	/* Update mdata crc32 field */
> +	mdata->crc32 = crc32(0, (void *)&mdata->version,
> +			     sizeof(*mdata) - sizeof(u32));
> +
> +	/* First write the primary mdata */
> +	ret = fwu_mtd_save_primary_mdata(mtd_priv, mdata);
> +	if (ret < 0) {
> +		log_err("Failed to update the primary mdata.\n");
> +		return ret;
> +	}
> +
> +	/* And now the replica */
> +	ret = fwu_mtd_save_secondary_mdata(mtd_priv, mdata);
> +	if (ret < 0) {
> +		log_err("Failed to update the secondary mdata.\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int fwu_mtd_mdata_check(struct udevice *dev)
> +{
> +	struct fwu_mdata *primary = NULL, *secondary = NULL;
> +	struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
> +	int ret;
> +
> +	ret = fwu_mtd_load_primary_mdata(mtd_priv, &primary);
> +	if (ret < 0)
> +		log_err("Failed to read the primary mdata: %d\n", ret);
dev_err(dev,
> +
> +	ret = fwu_mtd_load_secondary_mdata(mtd_priv, &secondary);
> +	if (ret < 0)
> +		log_err("Failed to read the secondary mdata: %d\n", ret);
dev_err(dev,
> +
> +	if (primary && secondary) {
> +		if (memcmp(primary, secondary, sizeof(struct fwu_mdata))) {
> +			log_err("The primary and the secondary mdata are different\n");
dev_err(dev,
> +			ret = -1;
> +		}
> +	} else if (primary) {
> +		ret = fwu_mtd_save_secondary_mdata(mtd_priv, primary);
> +		if (ret < 0)
> +			log_err("Restoring secondary mdata partition failed\n");
dev_err(dev,
> +	} else if (secondary) {
> +		ret = fwu_mtd_save_primary_mdata(mtd_priv, secondary);
> +		if (ret < 0)
> +			log_err("Restoring primary mdata partition failed\n");
dev_err(dev,
> +	}
> +
> +	free(primary);
> +	free(secondary);
> +	return ret;
> +}
> +
> +static int fwu_mtd_get_mdata(struct udevice *dev, struct fwu_mdata **mdata)
> +{
> +	struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
> +
> +	return fwu_mtd_get_valid_mdata(mtd_priv, mdata);
> +}
> +
> +/**
> + * fwu_mdata_mtd_of_to_plat() - Translate from DT to fwu mdata device
> + */
> +static int fwu_mdata_mtd_of_to_plat(struct udevice *dev)
> +{
> +	struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
> +	const fdt32_t *phandle_p = NULL;
> +	struct udevice *mtd_dev;
> +	struct mtd_info *mtd;
> +	int ret, size;
> +	u32 phandle;
> +
> +	/* Find the FWU mdata storage device */
> +	phandle_p = ofnode_get_property(dev_ofnode(dev),
> +					"fwu-mdata-store", &size);

phandle_p = dev_read_prop(dev, "fwu-mdata-store", &size);


> +	if (!phandle_p) {
> +		log_err("fwu-mdata-store property not found\n");
> +		return -ENOENT;
> +	}
> +
> +	phandle = fdt32_to_cpu(*phandle_p);
> +

or directly use dev_read_phandle_with_args() ?

ret = dev_read_phandle_with_args(dev, "fwu-mdata-store", NULL, 0, 0, 
&phandle_args);

and using = phandle_args.node


> +	ret = device_get_global_by_ofnode(
> +		ofnode_get_by_phandle(phandle),
> +		&mtd_dev);
> +	if (ret)
> +		return ret;
> +
> +	mtd_probe_devices();
> +
> +	mtd_for_each_device(mtd) {
> +		if (mtd->dev == mtd_dev) {
> +			mtd_priv->mtd = mtd;
> +			log_debug("Found the FWU mdata mtd device %s\n", mtd->name);
> +			break;
> +		}
> +	}
> +	if (!mtd_priv->mtd) {
> +		log_err("Failed to find mtd device by fwu-mdata-store\n");
> +		return -ENOENT;
> +	}
> +
> +	/* Get the offset of primary and seconday mdata */
> +	ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 0,
> +				    &mtd_priv->pri_offset);


can be replaced by dev_read_u32_index()

ret = dev_read_u32_index(dev, "mdata-offsets", 0, &mtd_priv->pri_offset);


> +	if (ret)
> +		return ret;
> +	ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 1,
> +				    &mtd_priv->sec_offset);


ret = dev_read_u32_index(dev, "mdata-offsets", 1, &mtd_priv->sec_offset);


> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int fwu_mdata_mtd_probe(struct udevice *dev)
> +{
> +	/* Ensure the metadata can be read. */
> +	return fwu_mtd_mdata_check(dev);
> +}
> +
> +static struct fwu_mdata_ops fwu_mtd_ops = {
> +	.mdata_check = fwu_mtd_mdata_check,
> +	.get_mdata = fwu_mtd_get_mdata,
> +	.update_mdata = fwu_mtd_update_mdata,
> +};
> +
> +static const struct udevice_id fwu_mdata_ids[] = {
> +	{ .compatible = "u-boot,fwu-mdata-mtd" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(fwu_mdata_mtd) = {
> +	.name		= "fwu-mdata-mtd",
> +	.id		= UCLASS_FWU_MDATA,
> +	.of_match	= fwu_mdata_ids,
> +	.ops		= &fwu_mtd_ops,
> +	.probe		= fwu_mdata_mtd_probe,
> +	.of_to_plat	= fwu_mdata_mtd_of_to_plat,
> +	.priv_auto	= sizeof(struct fwu_mdata_mtd_priv),
> +};

regards

Patrick
diff mbox series

Patch

diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig
index d5edef19d6..a8fa9ad783 100644
--- a/drivers/fwu-mdata/Kconfig
+++ b/drivers/fwu-mdata/Kconfig
@@ -14,3 +14,11 @@  config FWU_MDATA_GPT_BLK
 	help
 	  Enable support for accessing FWU Metadata on GPT partitioned
 	  block devices.
+
+config FWU_MDATA_MTD
+	bool "FWU Metadata access for non-GPT MTD devices"
+	depends on DM_FWU_MDATA && MTD
+	help
+	  Enable support for accessing FWU Metadata on non-partitioned
+	  (or non-GPT partitioned, e.g. partition nodes in devicetree)
+	  MTD devices.
diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile
index 12a5b4fe04..c574c59be2 100644
--- a/drivers/fwu-mdata/Makefile
+++ b/drivers/fwu-mdata/Makefile
@@ -5,3 +5,4 @@ 
 
 obj-$(CONFIG_DM_FWU_MDATA) += fwu-mdata-uclass.o
 obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_mdata_gpt_blk.o
+obj-$(CONFIG_FWU_MDATA_MTD) += fwu_mdata_mtd.o
diff --git a/drivers/fwu-mdata/fwu_mdata_mtd.c b/drivers/fwu-mdata/fwu_mdata_mtd.c
new file mode 100644
index 0000000000..9eb471e73e
--- /dev/null
+++ b/drivers/fwu-mdata/fwu_mdata_mtd.c
@@ -0,0 +1,308 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2022, Linaro Limited
+ */
+
+#include <efi_loader.h>
+#include <fwu.h>
+#include <fwu_mdata.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <spi.h>
+#include <spi_flash.h>
+#include <flash.h>
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <u-boot/crc.h>
+
+struct fwu_mdata_mtd_priv {
+	struct mtd_info *mtd;
+	u32 pri_offset;
+	u32 sec_offset;
+};
+
+enum fwu_mtd_op {
+	FWU_MTD_READ,
+	FWU_MTD_WRITE,
+};
+
+static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
+{
+	return !do_div(size, mtd->erasesize);
+}
+
+static int mtd_io_data(struct mtd_info *mtd, u32 offs, u32 size, void *data,
+		       enum fwu_mtd_op op)
+{
+	struct mtd_oob_ops io_op ={};
+	u64 lock_offs, lock_len;
+	size_t len;
+	void *buf;
+	int ret;
+
+	if (!mtd_is_aligned_with_block_size(mtd, offs))
+		return -EINVAL;
+	lock_offs = offs;
+	lock_len = round_up(size, mtd->erasesize);
+
+	ret = mtd_unlock(mtd, lock_offs, lock_len);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+
+	if (op == FWU_MTD_WRITE) {
+		struct erase_info erase_op = {};
+
+		/* This will expand erase size to align with the block size */
+		erase_op.mtd = mtd;
+		erase_op.addr = lock_offs;
+		erase_op.len = lock_len;
+		erase_op.scrub = 0;
+
+		ret = mtd_erase(mtd, &erase_op);
+		if (ret)
+			goto lock_out;
+	}
+
+	/* Also, expand the write size to align with the write size */
+	len = round_up(size, mtd->writesize);
+
+	buf = memalign(ARCH_DMA_MINALIGN, len);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto lock_out;
+	}
+	io_op.mode = MTD_OPS_AUTO_OOB;
+	io_op.len = len;
+	io_op.ooblen = 0;
+	io_op.datbuf = buf;
+	io_op.oobbuf = NULL;
+
+	if (op == FWU_MTD_WRITE) {
+		memcpy(buf, data, size);
+		ret = mtd_write_oob(mtd, offs, &io_op);
+	} else {
+		ret = mtd_read_oob(mtd, offs, &io_op);
+		if (!ret)
+			memcpy(data, buf, size);
+	}
+	free(buf);
+
+lock_out:
+	mtd_lock(mtd, lock_offs, lock_len);
+
+	return ret;
+}
+
+static int fwu_mtd_load_mdata(struct mtd_info *mtd, struct fwu_mdata **mdata,
+			      u32 offs, bool primary)
+{
+	size_t size = sizeof(struct fwu_mdata);
+	int ret;
+
+	*mdata = malloc(size);
+	if (!*mdata)
+		return -ENOMEM;
+
+	ret = mtd_io_data(mtd, offs, size, (void *)*mdata, FWU_MTD_READ);
+	if (ret >= 0) {
+		ret = fwu_verify_mdata(*mdata, primary);
+		if (ret < 0) {
+			free(*mdata);
+			*mdata = NULL;
+		}
+	}
+
+	return ret;
+}
+
+static int fwu_mtd_load_primary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
+				     struct fwu_mdata **mdata)
+{
+	return fwu_mtd_load_mdata(mtd_priv->mtd, mdata, mtd_priv->pri_offset, true);
+}
+
+static int fwu_mtd_load_secondary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
+				       struct fwu_mdata **mdata)
+{
+	return fwu_mtd_load_mdata(mtd_priv->mtd, mdata, mtd_priv->sec_offset, false);
+}
+
+static int fwu_mtd_save_primary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
+				     struct fwu_mdata *mdata)
+{
+	return mtd_io_data(mtd_priv->mtd, mtd_priv->pri_offset,
+			   sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE);
+}
+
+static int fwu_mtd_save_secondary_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
+				       struct fwu_mdata *mdata)
+{
+	return mtd_io_data(mtd_priv->mtd, mtd_priv->sec_offset,
+			   sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE);
+}
+
+static int fwu_mtd_get_valid_mdata(struct fwu_mdata_mtd_priv *mtd_priv,
+				  struct fwu_mdata **mdata)
+{
+	if (fwu_mtd_load_primary_mdata(mtd_priv, mdata) == 0)
+		return 0;
+
+	log_err("Failed to load/verify primary mdata. Try secondary.\n");
+
+	if (fwu_mtd_load_secondary_mdata(mtd_priv, mdata) == 0)
+		return 0;
+
+	log_err("Failed to load/verify secondary mdata.\n");
+
+	return -1;
+}
+
+static int fwu_mtd_update_mdata(struct udevice *dev, struct fwu_mdata *mdata)
+{
+	struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
+	int ret;
+
+	/* Update mdata crc32 field */
+	mdata->crc32 = crc32(0, (void *)&mdata->version,
+			     sizeof(*mdata) - sizeof(u32));
+
+	/* First write the primary mdata */
+	ret = fwu_mtd_save_primary_mdata(mtd_priv, mdata);
+	if (ret < 0) {
+		log_err("Failed to update the primary mdata.\n");
+		return ret;
+	}
+
+	/* And now the replica */
+	ret = fwu_mtd_save_secondary_mdata(mtd_priv, mdata);
+	if (ret < 0) {
+		log_err("Failed to update the secondary mdata.\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fwu_mtd_mdata_check(struct udevice *dev)
+{
+	struct fwu_mdata *primary = NULL, *secondary = NULL;
+	struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
+	int ret;
+
+	ret = fwu_mtd_load_primary_mdata(mtd_priv, &primary);
+	if (ret < 0)
+		log_err("Failed to read the primary mdata: %d\n", ret);
+
+	ret = fwu_mtd_load_secondary_mdata(mtd_priv, &secondary);
+	if (ret < 0)
+		log_err("Failed to read the secondary mdata: %d\n", ret);
+
+	if (primary && secondary) {
+		if (memcmp(primary, secondary, sizeof(struct fwu_mdata))) {
+			log_err("The primary and the secondary mdata are different\n");
+			ret = -1;
+		}
+	} else if (primary) {
+		ret = fwu_mtd_save_secondary_mdata(mtd_priv, primary);
+		if (ret < 0)
+			log_err("Restoring secondary mdata partition failed\n");
+	} else if (secondary) {
+		ret = fwu_mtd_save_primary_mdata(mtd_priv, secondary);
+		if (ret < 0)
+			log_err("Restoring primary mdata partition failed\n");
+	}
+
+	free(primary);
+	free(secondary);
+	return ret;
+}
+
+static int fwu_mtd_get_mdata(struct udevice *dev, struct fwu_mdata **mdata)
+{
+	struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
+
+	return fwu_mtd_get_valid_mdata(mtd_priv, mdata);
+}
+
+/**
+ * fwu_mdata_mtd_of_to_plat() - Translate from DT to fwu mdata device
+ */
+static int fwu_mdata_mtd_of_to_plat(struct udevice *dev)
+{
+	struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
+	const fdt32_t *phandle_p = NULL;
+	struct udevice *mtd_dev;
+	struct mtd_info *mtd;
+	int ret, size;
+	u32 phandle;
+
+	/* Find the FWU mdata storage device */
+	phandle_p = ofnode_get_property(dev_ofnode(dev),
+					"fwu-mdata-store", &size);
+	if (!phandle_p) {
+		log_err("fwu-mdata-store property not found\n");
+		return -ENOENT;
+	}
+
+	phandle = fdt32_to_cpu(*phandle_p);
+
+	ret = device_get_global_by_ofnode(
+		ofnode_get_by_phandle(phandle),
+		&mtd_dev);
+	if (ret)
+		return ret;
+
+	mtd_probe_devices();
+
+	mtd_for_each_device(mtd) {
+		if (mtd->dev == mtd_dev) {
+			mtd_priv->mtd = mtd;
+			log_debug("Found the FWU mdata mtd device %s\n", mtd->name);
+			break;
+		}
+	}
+	if (!mtd_priv->mtd) {
+		log_err("Failed to find mtd device by fwu-mdata-store\n");
+		return -ENOENT;
+	}
+
+	/* Get the offset of primary and seconday mdata */
+	ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 0,
+				    &mtd_priv->pri_offset);
+	if (ret)
+		return ret;
+	ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 1,
+				    &mtd_priv->sec_offset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int fwu_mdata_mtd_probe(struct udevice *dev)
+{
+	/* Ensure the metadata can be read. */
+	return fwu_mtd_mdata_check(dev);
+}
+
+static struct fwu_mdata_ops fwu_mtd_ops = {
+	.mdata_check = fwu_mtd_mdata_check,
+	.get_mdata = fwu_mtd_get_mdata,
+	.update_mdata = fwu_mtd_update_mdata,
+};
+
+static const struct udevice_id fwu_mdata_ids[] = {
+	{ .compatible = "u-boot,fwu-mdata-mtd" },
+	{ }
+};
+
+U_BOOT_DRIVER(fwu_mdata_mtd) = {
+	.name		= "fwu-mdata-mtd",
+	.id		= UCLASS_FWU_MDATA,
+	.of_match	= fwu_mdata_ids,
+	.ops		= &fwu_mtd_ops,
+	.probe		= fwu_mdata_mtd_probe,
+	.of_to_plat	= fwu_mdata_mtd_of_to_plat,
+	.priv_auto	= sizeof(struct fwu_mdata_mtd_priv),
+};