diff mbox series

[RFC,v2,5/8] FWU: Add FWU metadata access driver for SPI flash

Message ID 164511072688.43219.11381169900703679729.stgit@localhost
State New
Headers show
Series FWU: Add FWU Multi Bank Update for DeveloerBox | expand

Commit Message

Masami Hiramatsu Feb. 17, 2022, 3:12 p.m. UTC
For the platform which doesn't have GPT partitions for the firmware
but on SPI flash, the FWU metadata is stored on SPI flash as raw
image at specific offset. This driver gives the access methods
for those metadata information on the SPI flash.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@linaro.org>
---
 drivers/fwu-mdata/Kconfig        |    9 +
 drivers/fwu-mdata/Makefile       |    1 
 drivers/fwu-mdata/fwu_mdata_sf.c |  294 ++++++++++++++++++++++++++++++++++++++
 include/fwu.h                    |    2 
 4 files changed, 306 insertions(+)
 create mode 100644 drivers/fwu-mdata/fwu_mdata_sf.c
diff mbox series

Patch

diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig
index d5edef19d6..9bed3e9c1e 100644
--- a/drivers/fwu-mdata/Kconfig
+++ b/drivers/fwu-mdata/Kconfig
@@ -14,3 +14,12 @@  config FWU_MDATA_GPT_BLK
 	help
 	  Enable support for accessing FWU Metadata on GPT partitioned
 	  block devices.
+
+config FWU_MDATA_SF
+	bool "Enable FWU Multi Bank Update for SPI Flash"
+	depends on DM_FWU_MDATA
+	help
+	  Enable FWU Multi Bank Update for SPI flash driver. This
+	  driver does not depend on GPT. Instead, the platform must
+	  provide some APIs and define the offset of the primary and
+	  the secondary metadata.
diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile
index 12a5b4fe04..f8db25ab69 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_SF) += fwu_mdata_sf.o
diff --git a/drivers/fwu-mdata/fwu_mdata_sf.c b/drivers/fwu-mdata/fwu_mdata_sf.c
new file mode 100644
index 0000000000..010528b91a
--- /dev/null
+++ b/drivers/fwu-mdata/fwu_mdata_sf.c
@@ -0,0 +1,294 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021, 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_sf_priv {
+	struct spi_flash *sf;
+	u32 pri_offset;
+	u32 sec_offset;
+};
+
+static int sf_load_data(struct spi_flash *sf, u32 offs, u32 size, void **data)
+{
+	int ret;
+
+	*data = memalign(ARCH_DMA_MINALIGN, size);
+	if (!*data)
+		return -ENOMEM;
+
+	ret = spi_flash_read(sf, offs, size, *data);
+	if (ret < 0) {
+		free(*data);
+		*data = NULL;
+	}
+
+	return ret;
+}
+
+static int sf_save_data(struct spi_flash *sf, u32 offs, u32 size, void *data)
+{
+	u32 sect_size, nsect;
+	void *buf;
+	int ret;
+
+	sect_size = sf->mtd.erasesize;
+	nsect = DIV_ROUND_UP(size, sect_size);
+	ret = spi_flash_erase(sf, offs, nsect * sect_size);
+	if (ret < 0)
+		return ret;
+
+	buf = memalign(ARCH_DMA_MINALIGN, size);
+	if (!buf)
+		return -ENOMEM;
+	memcpy(buf, data, size);
+
+	ret = spi_flash_write(sf, offs, size, buf);
+
+	free(buf);
+
+	return ret;
+}
+
+static int fwu_sf_load_mdata(struct spi_flash *sf, struct fwu_mdata **mdata, u32 offs, bool primary)
+{
+	int ret;
+
+	ret = sf_load_data(sf, offs, sizeof(struct fwu_mdata), (void **)mdata);
+
+	if (ret >= 0) {
+		ret = fwu_verify_mdata(*mdata, primary);
+		if (ret < 0) {
+			free(*mdata);
+			*mdata = NULL;
+		}
+	}
+
+	return ret;
+}
+
+static int fwu_sf_load_primary_mdata(struct fwu_mdata_sf_priv *sf_priv,
+				     struct fwu_mdata **mdata)
+{
+	return fwu_sf_load_mdata(sf_priv->sf, mdata, sf_priv->pri_offset, true);
+}
+
+static int fwu_sf_load_secondary_mdata(struct fwu_mdata_sf_priv *sf_priv,
+				       struct fwu_mdata **mdata)
+{
+	return fwu_sf_load_mdata(sf_priv->sf, mdata, sf_priv->sec_offset, false);
+}
+
+static int fwu_sf_save_primary_mdata(struct fwu_mdata_sf_priv *sf_priv,
+				     struct fwu_mdata *mdata)
+{
+	return sf_save_data(sf_priv->sf, sf_priv->pri_offset,
+			    sizeof(struct fwu_mdata), mdata);
+}
+
+static int fwu_sf_save_secondary_mdata(struct fwu_mdata_sf_priv *sf_priv,
+				       struct fwu_mdata *mdata)
+{
+	return sf_save_data(sf_priv->sf, sf_priv->sec_offset,
+			    sizeof(struct fwu_mdata), mdata);
+}
+
+static int fwu_sf_get_valid_mdata(struct fwu_mdata_sf_priv *sf_priv,
+				  struct fwu_mdata **mdata)
+{
+	if (fwu_sf_load_primary_mdata(sf_priv, mdata) == 0)
+		return 0;
+
+	log_err("Failed to load/verify primary mdata. Try secondary.\n");
+
+	if (fwu_sf_load_secondary_mdata(sf_priv, mdata) == 0)
+		return 0;
+
+	log_err("Failed to load/verify secondary mdata.\n");
+
+	return -1;
+}
+
+static int fwu_sf_update_mdata(struct udevice *dev, struct fwu_mdata *mdata)
+{
+	struct fwu_mdata_sf_priv *sf_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_sf_save_primary_mdata(sf_priv, mdata);
+	if (ret < 0) {
+		log_err("Failed to update the primary mdata.\n");
+		return ret;
+	}
+
+	/* And now the replica */
+	ret = fwu_sf_save_secondary_mdata(sf_priv, mdata);
+	if (ret < 0) {
+		log_err("Failed to update the secondary mdata.\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fwu_sf_mdata_check(struct udevice *dev)
+{
+	struct fwu_mdata *primary = NULL, *secondary = NULL;
+	struct fwu_mdata_sf_priv *sf_priv = dev_get_priv(dev);
+	int ret;
+
+	ret = fwu_sf_load_primary_mdata(sf_priv, &primary);
+	if (ret < 0)
+		log_err("Failed to read the primary mdata: %d\n", ret);
+
+	ret = fwu_sf_load_secondary_mdata(sf_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_sf_save_secondary_mdata(sf_priv, primary);
+		if (ret < 0)
+			log_err("Restoring secondary mdata partition failed\n");
+	} else if (secondary) {
+		ret = fwu_sf_save_primary_mdata(sf_priv, secondary);
+		if (ret < 0)
+			log_err("Restoring primary mdata partition failed\n");
+	}
+
+	free(primary);
+	free(secondary);
+	return ret;
+}
+
+static int fwu_sf_get_mdata(struct udevice *dev, struct fwu_mdata **mdata)
+{
+	struct fwu_mdata_sf_priv *sf_priv = dev_get_priv(dev);
+
+	return fwu_sf_get_valid_mdata(sf_priv, mdata);
+}
+
+/*
+ * By default, this expects that dfu_alt_info is defined as the following order
+ * image0.bank0, image0.bank1, image1.bank0, ...
+ * Thus the alt_no is (the index of image) * #banks + update_bank.
+ */
+int __weak fwu_plat_get_image_alt_num(efi_guid_t image_type_id,
+				      u32 update_bank, int *alt_no)
+{
+	struct fwu_mdata *mdata;
+	int i, ret;
+
+	ret = fwu_get_mdata(&mdata);
+	if (ret < 0)
+		return ret;
+
+	ret = -ENOENT;
+	for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++)
+		if (!guidcmp(&image_type_id, &mdata->img_entry[i].image_type_uuid)) {
+			*alt_no = i * CONFIG_FWU_NUM_BANKS + update_bank;
+			ret = 0;
+			break;
+		}
+
+	free(mdata);
+	return ret;
+}
+
+/* Since the dfu_alt_info is defined by the platform, ask platform about alt-number. */
+static int fwu_sf_get_image_alt_num(struct udevice *dev, efi_guid_t image_type_id,
+				    u32 update_bank, int *alt_no)
+{
+	return fwu_plat_get_image_alt_num(image_type_id, update_bank, alt_no);
+}
+
+/**
+ * fwu_mdata_sf_of_to_plat() - Translate from DT to fwu mdata device
+ */
+static int fwu_mdata_sf_of_to_plat(struct udevice *dev)
+{
+	struct fwu_mdata_sf_priv *sf_priv = dev_get_priv(dev);
+	const fdt32_t *phandle_p = NULL;
+	struct udevice *sf_dev;
+	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),
+		&sf_dev);
+	if (ret)
+		return ret;
+
+	sf_priv->sf = dev_get_uclass_priv(sf_dev);
+
+	/* Get the offset of primary and seconday mdata */
+	ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 0,
+				    &sf_priv->pri_offset);
+	if (ret)
+		return ret;
+	ret = ofnode_read_u32_index(dev_ofnode(dev), "mdata-offsets", 1,
+				    &sf_priv->sec_offset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int fwu_mdata_sf_probe(struct udevice *dev)
+{
+	/* Ensure the metadata can be read. */
+	return fwu_sf_mdata_check(dev);
+}
+
+static struct fwu_mdata_ops fwu_sf_ops = {
+	.get_image_alt_num = fwu_sf_get_image_alt_num,
+	.mdata_check = fwu_sf_mdata_check,
+	.get_mdata = fwu_sf_get_mdata,
+	.update_mdata = fwu_sf_update_mdata,
+};
+
+static const struct udevice_id fwu_mdata_ids[] = {
+	{ .compatible = "u-boot,fwu-mdata-sf" },
+	{ }
+};
+
+U_BOOT_DRIVER(fwu_mdata_sf) = {
+	.name		= "fwu-mdata-sf",
+	.id		= UCLASS_FWU_MDATA,
+	.of_match	= fwu_mdata_ids,
+	.ops		= &fwu_sf_ops,
+	.probe		= fwu_mdata_sf_probe,
+	.of_to_plat	= fwu_mdata_sf_of_to_plat,
+	.priv_auto	= sizeof(struct fwu_mdata_sf_priv),
+};
diff --git a/include/fwu.h b/include/fwu.h
index 88dc4a4b9a..f5ac3f6d2e 100644
--- a/include/fwu.h
+++ b/include/fwu.h
@@ -66,5 +66,7 @@  int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank);
 int fwu_plat_get_update_index(u32 *update_idx);
 int fwu_plat_get_alt_num(struct udevice *dev, void *identifier);
 void fwu_plat_get_bootidx(void *boot_idx);
+int fwu_plat_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
+			       int *alt_no);
 
 #endif /* _FWU_H_ */