Signed-off-by: Masami Hiramatsu <[email protected]>
---
 include/fwu.h                  |   13 ++
 lib/fwu_updates/Kconfig        |   34 ++++++
 lib/fwu_updates/Makefile       |    5 -
 lib/fwu_updates/fwu_mdata_sf.c |  241 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 288 insertions(+), 5 deletions(-)
 create mode 100644 lib/fwu_updates/fwu_mdata_sf.c

diff --git a/include/fwu.h b/include/fwu.h
index 7af94988b7..a013170321 100644
--- a/include/fwu.h
+++ b/include/fwu.h
@@ -64,9 +64,18 @@ int fwu_accept_image(efi_guid_t *img_type_id, u32 bank);
 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_blk_desc(struct blk_desc **desc);
-int fwu_plat_get_alt_num(void *identifier);
 int fwu_plat_fill_partition_guids(efi_guid_t **part_guid_arr);
 void fwu_plat_get_bootidx(void *boot_idx);
 
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE_GPT_BLK
+int fwu_plat_get_blk_desc(struct blk_desc **desc);
+int fwu_plat_get_alt_num(void *identifier);
+#endif
+
+#ifdef CONFIG_FWU_MULTI_BANK_UPDATE_SF
+struct fwu_mdata_ops *fwu_sf_get_fwu_mdata_ops(void);
+int fwu_plat_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
+                              int *alt_no);
+#endif
+
 #endif /* _FWU_H_ */
diff --git a/lib/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig
index 0940a90747..8a369b9872 100644
--- a/lib/fwu_updates/Kconfig
+++ b/lib/fwu_updates/Kconfig
@@ -38,3 +38,37 @@ config FWU_REBOOT_AFTER_UPDATE
          Reboot the machine soon after installing a new firmware
          and start trial boot. You can disable this option for
          debugging or FWU development, but recommended to enable it.
+
+config FWU_MULTI_BANK_UPDATE_GPT_BLK
+       bool "Enable FWU Multi Bank Update for GPT on blockdev"
+       depends on FWU_MULTI_BANK_UPDATE
+       select EFI_PARTITION
+       help
+         Enable FWU Multi Bank Update for GPT on a block device. This
+         driver depends on GPT and the block device. FWU meatadata and
+         firmware will be stored on a block device with GPT.
+
+config FWU_MULTI_BANK_UPDATE_SF
+       bool "Enable FWU Multi Bank Update for SPI Flash"
+       depends on FWU_MULTI_BANK_UPDATE
+       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.
+
+config FWU_SF_PRIMARY_MDATA_OFFSET
+       hex "The offset of the primary metadata"
+       depends on FWU_MULTI_BANK_UPDATE_SF
+       help
+         The offset of the primary metadata on SPI Flash in
+         hexadecimal.
+
+config FWU_SF_SECONDARY_MDATA_OFFSET
+       hex "The offset of the secondary metadata"
+       depends on FWU_MULTI_BANK_UPDATE_SF
+       help
+         The offset of the secondary metadata on SPI Flash in
+         hexadecimal.
+         This must NOT be the same offset of the primary
+         metadata.
diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile
index 73099a30cb..80669344f2 100644
--- a/lib/fwu_updates/Makefile
+++ b/lib/fwu_updates/Makefile
@@ -6,6 +6,5 @@
 obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_mdata.o
 obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o
 
-ifdef CONFIG_EFI_PARTITION
-obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_mdata_gpt_blk.o
-endif
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE_GPT_BLK) += fwu_mdata_gpt_blk.o
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE_SF) += fwu_mdata_sf.o
diff --git a/lib/fwu_updates/fwu_mdata_sf.c b/lib/fwu_updates/fwu_mdata_sf.c
new file mode 100644
index 0000000000..9e3cae7b68
--- /dev/null
+++ b/lib/fwu_updates/fwu_mdata_sf.c
@@ -0,0 +1,241 @@
+// 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>
+
+/*
+ * For the FWU SPI flash driver, the platform must define below functions
+ * according to its dfu_alt_info.
+ */
+extern int fwu_plat_get_image_alt_num(efi_guid_t image_type_id,
+                                     u32 update_bank, int *alt_no);
+
+static struct spi_flash *fwu_spi_flash;
+
+static int __fwu_sf_get_flash(void)
+{
+       if (IS_ENABLED(CONFIG_DM_SPI_FLASH)) {
+               struct udevice *new;
+               int     ret;
+
+               ret = spi_flash_probe_bus_cs(CONFIG_SF_DEFAULT_BUS, 
CONFIG_SF_DEFAULT_CS,
+                                            CONFIG_SF_DEFAULT_SPEED, 
CONFIG_SF_DEFAULT_MODE,
+                                            &new);
+               if (ret)
+                       return ret;
+
+               fwu_spi_flash = dev_get_uclass_priv(new);
+       } else {
+               fwu_spi_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, 
CONFIG_SF_DEFAULT_CS,
+                                               CONFIG_SF_DEFAULT_SPEED, 
CONFIG_SF_DEFAULT_MODE);
+               if (!fwu_spi_flash)
+                       return -EIO;
+       }
+       return 0;
+}
+
+static int fwu_sf_get_flash(struct spi_flash **flash)
+{
+       int ret = 0;
+
+       if (!fwu_spi_flash)
+               ret = __fwu_sf_get_flash();
+
+       *flash = fwu_spi_flash;
+
+       return ret;
+}
+
+static int sf_load_data(u32 offs, u32 size, void **data)
+{
+       struct spi_flash *flash;
+       int ret;
+
+       ret = fwu_sf_get_flash(&flash);
+       if (ret < 0)
+               return ret;
+
+       *data = memalign(ARCH_DMA_MINALIGN, size);
+       if (!*data)
+               return -ENOMEM;
+
+       ret = spi_flash_read(flash, offs, size, *data);
+       if (ret < 0) {
+               free(*data);
+               *data = NULL;
+       }
+
+       return ret;
+}
+
+static int sf_save_data(u32 offs, u32 size, void *data)
+{
+       struct spi_flash *flash;
+       u32 sect_size, nsect;
+       void *buf;
+       int ret;
+
+       ret = fwu_sf_get_flash(&flash);
+       if (ret < 0)
+               return ret;
+
+       sect_size = flash->mtd.erasesize;
+       nsect = DIV_ROUND_UP(size, sect_size);
+       ret = spi_flash_erase(flash, 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(flash, offs, size, buf);
+
+       free(buf);
+
+       return ret;
+}
+
+static int fwu_sf_load_mdata(struct fwu_mdata **mdata, u32 offs)
+{
+       int ret;
+
+       ret = sf_load_data(offs, sizeof(struct fwu_mdata), (void **)mdata);
+
+       if (ret >= 0) {
+               ret = fwu_verify_mdata(*mdata,
+                               offs == CONFIG_FWU_SF_PRIMARY_MDATA_OFFSET);
+               if (ret < 0) {
+                       free(*mdata);
+                       *mdata = NULL;
+               }
+       }
+
+       return ret;
+}
+
+static int fwu_sf_load_primary_mdata(struct fwu_mdata **mdata)
+{
+       return fwu_sf_load_mdata(mdata, CONFIG_FWU_SF_PRIMARY_MDATA_OFFSET);
+}
+
+static int fwu_sf_load_secondary_mdata(struct fwu_mdata **mdata)
+{
+       return fwu_sf_load_mdata(mdata, CONFIG_FWU_SF_SECONDARY_MDATA_OFFSET);
+}
+
+static int fwu_sf_save_primary_mdata(struct fwu_mdata *mdata)
+{
+       return sf_save_data(CONFIG_FWU_SF_PRIMARY_MDATA_OFFSET,
+                           sizeof(struct fwu_mdata), mdata);
+}
+
+static int fwu_sf_save_secondary_mdata(struct fwu_mdata *mdata)
+{
+       return sf_save_data(CONFIG_FWU_SF_SECONDARY_MDATA_OFFSET,
+                           sizeof(struct fwu_mdata), mdata);
+}
+
+static int fwu_sf_get_valid_mdata(struct fwu_mdata **mdata)
+{
+       if (fwu_sf_load_primary_mdata(mdata) == 0)
+               return 0;
+
+       log_err("Failed to load/verify primary mdata. Try secondary.\n");
+
+       if (fwu_sf_load_secondary_mdata(mdata) == 0)
+               return 0;
+
+       log_err("Failed to load/verify secondary mdata.\n");
+
+       return -1;
+}
+
+static int fwu_sf_update_mdata(struct fwu_mdata *mdata)
+{
+       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(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(mdata);
+       if (ret < 0) {
+               log_err("Failed to update the secondary mdata.\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int fwu_sf_mdata_check(void)
+{
+       struct fwu_mdata *primary = NULL, *secondary = NULL;
+       int ret;
+
+       ret = fwu_sf_load_primary_mdata(&primary);
+       if (ret < 0)
+               log_err("Failed to read the primary mdata: %d\n", ret);
+
+       ret = fwu_sf_load_secondary_mdata(&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(primary);
+               if (ret < 0)
+                       log_err("Restoring secondary mdata partition failed\n");
+       } else if (secondary) {
+               ret = fwu_sf_save_primary_mdata(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 fwu_mdata **mdata)
+{
+       return fwu_sf_get_valid_mdata(mdata);
+}
+
+static struct fwu_mdata_ops fwu_sf_mdata_ops = {
+       .get_image_alt_num = fwu_plat_get_image_alt_num,
+       .mdata_check = fwu_sf_mdata_check,
+       .get_mdata = fwu_sf_get_mdata,
+       .update_mdata = fwu_sf_update_mdata,
+};
+
+struct fwu_mdata_ops *fwu_sf_get_fwu_mdata_ops(void)
+{
+       return &fwu_sf_mdata_ops;
+}

Reply via email to