FWU: Add FWU metadata access driver for MTD storage regions
In the FWU Multi Bank Update feature, the information about the
updatable images is stored as part of the metadata, on a separate
region. Add a driver for reading from and writing to the metadata
when the updatable images and the metadata are stored on a raw
MTD region.
The code is divided into core under drivers/fwu-mdata/ and some helper
functions clubbed together under lib/fwu_updates/
Signed-off-by: Masami Hiramatsu <masami.hiramatsu@linaro.org>
Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile
index 1993088..c9e3c06 100644
--- a/lib/fwu_updates/Makefile
+++ b/lib/fwu_updates/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o
obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o
+obj-$(CONFIG_FWU_MDATA_MTD) += fwu_mtd.o
diff --git a/lib/fwu_updates/fwu_mtd.c b/lib/fwu_updates/fwu_mtd.c
new file mode 100644
index 0000000..b73111a
--- /dev/null
+++ b/lib/fwu_updates/fwu_mtd.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023, Linaro Limited
+ */
+
+#include <dm.h>
+#include <dfu.h>
+#include <fwu.h>
+#include <fwu_mdata.h>
+#include <log.h>
+#include <malloc.h>
+#include <mtd.h>
+#include <uuid.h>
+#include <vsprintf.h>
+
+#include <dm/ofnode.h>
+
+struct fwu_mtd_image_info
+fwu_mtd_images[CONFIG_FWU_NUM_BANKS * CONFIG_FWU_NUM_IMAGES_PER_BANK];
+
+static struct fwu_mtd_image_info *mtd_img_by_uuid(const char *uuidbuf)
+{
+ int num_images = ARRAY_SIZE(fwu_mtd_images);
+
+ for (int i = 0; i < num_images; i++)
+ if (!strcmp(uuidbuf, fwu_mtd_images[i].uuidbuf))
+ return &fwu_mtd_images[i];
+
+ return NULL;
+}
+
+int fwu_mtd_get_alt_num(efi_guid_t *image_id, u8 *alt_num,
+ const char *mtd_dev)
+{
+ struct fwu_mtd_image_info *mtd_img_info;
+ char uuidbuf[UUID_STR_LEN + 1];
+ fdt_addr_t offset, size = 0;
+ struct dfu_entity *dfu;
+ int i, nalt, ret;
+
+ mtd_probe_devices();
+
+ uuid_bin_to_str(image_id->b, uuidbuf, UUID_STR_FORMAT_STD);
+
+ mtd_img_info = mtd_img_by_uuid(uuidbuf);
+ if (!mtd_img_info) {
+ log_err("%s: Not found partition for image %s\n", __func__, uuidbuf);
+ return -ENOENT;
+ }
+
+ offset = mtd_img_info->start;
+ size = mtd_img_info->size;
+
+ ret = dfu_init_env_entities(NULL, NULL);
+ if (ret)
+ return -ENOENT;
+
+ nalt = 0;
+ list_for_each_entry(dfu, &dfu_list, list)
+ nalt++;
+
+ if (!nalt) {
+ log_warning("No entities in dfu_alt_info\n");
+ dfu_free_entities();
+ return -ENOENT;
+ }
+
+ ret = -ENOENT;
+ for (i = 0; i < nalt; i++) {
+ dfu = dfu_get_entity(i);
+
+ /* Only MTD RAW access */
+ if (!dfu || dfu->dev_type != DFU_DEV_MTD ||
+ dfu->layout != DFU_RAW_ADDR ||
+ dfu->data.mtd.start != offset ||
+ dfu->data.mtd.size != size)
+ continue;
+
+ *alt_num = dfu->alt;
+ ret = 0;
+ break;
+ }
+
+ dfu_free_entities();
+
+ log_debug("%s: %s -> %d\n", __func__, uuidbuf, *alt_num);
+ return ret;
+}
+
+/**
+ * fwu_plat_get_alt_num() - Get the DFU Alt Num for the image from the platform
+ * @dev: FWU device
+ * @image_id: 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.
+ *
+ * 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_id,
+ u8 *alt_num)
+{
+ return fwu_mtd_get_alt_num(image_id, alt_num, "nor1");
+}
+
+static int gen_image_alt_info(char *buf, size_t len, int sidx,
+ struct fwu_image_entry *img, struct mtd_info *mtd)
+{
+ char *p = buf, *end = buf + len;
+ int i;
+
+ p += snprintf(p, end - p, "mtd %s", mtd->name);
+ if (end < p) {
+ log_err("%s:%d Run out of buffer\n", __func__, __LINE__);
+ return -E2BIG;
+ }
+
+ /*
+ * List the image banks in the FWU mdata and search the corresponding
+ * partition based on partition's uuid.
+ */
+ for (i = 0; i < CONFIG_FWU_NUM_BANKS; i++) {
+ struct fwu_mtd_image_info *mtd_img_info;
+ struct fwu_image_bank_info *bank;
+ char uuidbuf[UUID_STR_LEN + 1];
+ u32 offset, size;
+
+ /* Query a partition by image UUID */
+ bank = &img->img_bank_info[i];
+ uuid_bin_to_str(bank->image_uuid.b, uuidbuf, UUID_STR_FORMAT_STD);
+
+ mtd_img_info = mtd_img_by_uuid(uuidbuf);
+ if (!mtd_img_info) {
+ log_err("%s: Not found partition for image %s\n", __func__, uuidbuf);
+ break;
+ }
+
+ offset = mtd_img_info->start;
+ size = mtd_img_info->size;
+
+ p += snprintf(p, end - p, "%sbank%d raw %x %x",
+ i == 0 ? "=" : ";", i, offset, size);
+ if (end < p) {
+ log_err("%s:%d Run out of buffer\n", __func__, __LINE__);
+ return -E2BIG;
+ }
+ }
+
+ if (i == CONFIG_FWU_NUM_BANKS)
+ return 0;
+
+ return -ENOENT;
+}
+
+int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd)
+{
+ struct fwu_mdata mdata;
+ int i, l, ret;
+
+ ret = fwu_get_mdata(&mdata);
+ if (ret < 0) {
+ log_err("Failed to get the FWU mdata.\n");
+ return ret;
+ }
+
+ for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
+ ret = gen_image_alt_info(buf, len, i * CONFIG_FWU_NUM_BANKS,
+ &mdata.img_entry[i], mtd);
+ if (ret)
+ break;
+
+ l = strlen(buf);
+ /* Replace the last ';' with '&' if there is another image. */
+ if (i != CONFIG_FWU_NUM_IMAGES_PER_BANK - 1 && l)
+ buf[l - 1] = '&';
+ len -= l;
+ buf += l;
+ }
+
+ return ret;
+}