[U-Boot] [PATCHv2 2/4] drivers: firmware: add RSU support for Stratix10 SoC

richard.gong at linux.intel.com richard.gong at linux.intel.com
Wed Oct 30 20:34:54 UTC 2019


From: Richard Gong <richard.gong at intel.com>

The Intel Remote System Update (RSU) provides a way for users to update
the QSPI configuration bitstream of an Intel Stratix 10 SoC device with
significantly reduced risk of corrupting the bitstream storage and
bricking the system.

This patch adds RSU support which allows user to perform a complete set
of RSU operations:
        1. Provides support for creating the initial flash images for a
           system to support RSU.
        2. Allows several production images to be tried in a specific
           order until one of them is successful.
        3. Loads a factory image if no production image is available, or
           all production images failed.
        4. Provides users with the ability to add and remove production
           images.
        5. Provides user with the ability to change the order in which
           production images are loaded.
        6. Provides user with the ability to load a specific image from
           flash. The image is a production or factory image.
        7. Provides user with information on which image is currently
           running, and what errors were encountered by RSU.

Signed-off-by: Radu Bacrau <radu.bacrau at intel.com>
Signed-off-by: Richard Gong <richard.gong at intel.com>
---
 drivers/firmware/Kconfig       |   11 +
 drivers/firmware/Makefile      |    1 +
 drivers/firmware/rsu.c         |  662 +++++++++++++++++++++++++
 drivers/firmware/rsu_ll_qspi.c | 1050 ++++++++++++++++++++++++++++++++++++++++
 drivers/firmware/rsu_misc.c    |  817 +++++++++++++++++++++++++++++++
 include/intel/rsu.h            |  291 +++++++++++
 include/intel/rsu_ll.h         |   72 +++
 include/intel/rsu_misc.h       |   60 +++
 8 files changed, 2964 insertions(+)
 create mode 100644 drivers/firmware/rsu.c
 create mode 100644 drivers/firmware/rsu_ll_qspi.c
 create mode 100644 drivers/firmware/rsu_misc.c
 create mode 100644 include/intel/rsu.h
 create mode 100644 include/intel/rsu_ll.h
 create mode 100644 include/intel/rsu_misc.h

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 873bc8c..3a13405 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -26,3 +26,14 @@ config TI_SCI_PROTOCOL
 
 	  This protocol library is used by client drivers to use the features
 	  provided by the system controller.
+
+config INTEL_RSU
+	bool "Intel Stratix10/Agilex Remote System Update"
+	depends on TARGET_SOCFPGA_STRATIX10
+	help
+	  The Intel Remote System Update (RSU) provides a way for users
+	  to update the QSPI configuration bitstream of an Intel Stratix10
+	  or Agilex SoC device with significantly reduced risk of
+	  corrupting the bitstream storage and bricking the system.
+
+	  Say Y here if you want Intel RSU support.
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 6c3e129..97912ea 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_FIRMWARE)		+= firmware-uclass.o
 obj-$(CONFIG_$(SPL_)ARM_PSCI_FW)	+= psci.o
 obj-$(CONFIG_TI_SCI_PROTOCOL)	+= ti_sci.o
 obj-$(CONFIG_SANDBOX)		+= firmware-sandbox.o
+obj-$(CONFIG_INTEL_RSU)         += rsu.o rsu_ll_qspi.o rsu_misc.o
diff --git a/drivers/firmware/rsu.c b/drivers/firmware/rsu.c
new file mode 100644
index 0000000..bcae9dc
--- /dev/null
+++ b/drivers/firmware/rsu.c
@@ -0,0 +1,662 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ */
+
+#include <common.h>
+#include <linux/errno.h>
+#include <intel/rsu.h>
+#include <intel/rsu_misc.h>
+
+/* RSU Notify Bitmasks */
+#define RSU_NOTIFY_IGNORE_STAGE         BIT(18)
+#define RSU_NOTIFY_CLEAR_ERROR_STATUS   BIT(17)
+#define RSU_NOTIFY_RESET_RETRY_COUNTER  BIT(16)
+
+struct rsu_ll_intf *ll_intf;
+
+/**
+ * rsu_init() - initialize flash driver, SPT and CPB data
+ * @filename: NULL for qspi
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_init(char *filename)
+{
+	int ret;
+
+	if (ll_intf) {
+		rsu_log(RSU_ERR, "ll_intf initialized\n");
+		return -EINTF;
+	}
+
+	ret = rsu_ll_qspi_init(&ll_intf);
+	if (ret) {
+		rsu_exit();
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * rsu_exit() - free flash driver, clean SPT and CPB data
+ */
+void rsu_exit(void)
+{
+	if (ll_intf && ll_intf->exit)
+		ll_intf->exit();
+
+	ll_intf = NULL;
+}
+
+/**
+ * rsu_slot_count() - get the number of slots defined
+ *
+ * Returns: the number of defined slots
+ */
+int rsu_slot_count(void)
+{
+	int partitions;
+	int cnt = 0;
+	int x;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	partitions = ll_intf->partition.count();
+
+	for (x = 0; x < partitions; x++) {
+		if (rsu_misc_is_slot(ll_intf, x))
+			cnt++;
+	}
+
+	return cnt;
+}
+
+/**
+ * rsu_slot_by_name() - get slot number based on name
+ * @name: name of slot
+ *
+ * Return: slot number on success, or error code
+ */
+int rsu_slot_by_name(char *name)
+{
+	int partitions;
+	int cnt = 0;
+	int x;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (!name)
+		return -EARGS;
+
+	partitions = ll_intf->partition.count();
+
+	for (x = 0; x < partitions; x++) {
+		if (rsu_misc_is_slot(ll_intf, x)) {
+			if (!strcmp(name, ll_intf->partition.name(x)))
+				return cnt;
+			cnt++;
+		}
+	}
+
+	return -ENAME;
+}
+
+/**
+ * rsu_slot_get_info() - get the attributes of a slot
+ * @slot: slot number
+ * @info: pointer to info structure to be filled in
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_get_info(int slot, struct rsu_slot_info *info)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (!info)
+		return -EARGS;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -EINVAL;
+
+	rsu_misc_safe_strcpy(info->name, sizeof(info->name),
+			     ll_intf->partition.name(part_num),
+			     sizeof(info->name));
+
+	info->offset = ll_intf->partition.offset(part_num);
+	info->size = ll_intf->partition.size(part_num);
+	info->priority = ll_intf->priority.get(part_num);
+
+	return 0;
+}
+
+/**
+ * rsu_slot_size() - get the size of a slot
+ * @slot: slot number
+ *
+ * Returns: the size of the slot in bytes, or error code
+ */
+int rsu_slot_size(int slot)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	return ll_intf->partition.size(part_num);
+}
+
+/**
+ * rsu_slot_priority() - get the Decision CMF load priority of a slot
+ * @slot: slot number
+ *
+ * Priority of zero means the slot has no priority and is disabled.
+ * The slot with priority of one has the highest priority.
+ *
+ * Returns: the priority of the slot, or error code
+ */
+int rsu_slot_priority(int slot)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	return ll_intf->priority.get(part_num);
+}
+
+/**
+ * rsu_slot_erase() - erase all data in a slot
+ * @slot: slot number
+ *
+ * Erase all data in a slot to prepare for programming. Remove the slot
+ * if it is in the CMF pointer block.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_erase(int slot)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (rsu_misc_writeprotected(slot)) {
+		rsu_log(RSU_ERR, "Trying to erase a write protected slot\n");
+		return -EWRPROT;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (ll_intf->priority.remove(part_num))
+		return -ELOWLEVEL;
+
+	if (ll_intf->data.erase(part_num))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+/**
+ * rsu_slot_program_buf() - program a slot from FPGA buffer data
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * This function is used to program a slot using FPGA config data from
+ * a buffer and then enter the slot into CPB.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_program_buf(int slot, void *buf, int size)
+{
+	int ret;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (rsu_cb_buf_init(buf, size)) {
+		rsu_log(RSU_ERR, "Bad buf/size arguments\n");
+		return -EARGS;
+	}
+
+	ret = rsu_cb_program_common(ll_intf, slot, rsu_cb_buf, 0);
+	if (ret) {
+		rsu_log(RSU_ERR, "fail to program buf data\n");
+		return ret;
+	}
+
+	rsu_cb_buf_exit();
+	return ret;
+}
+
+/**
+ * rsu_slot_program_factory_update_buf() - program a slot using factory update
+ *                                         data from a buffer
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer
+ *
+ * This function is used to program a slot using factory update data from a
+ * buffer and then enter the slot into CPB.
+ *
+ * Returns 0 on success, or error code
+ */
+int rsu_slot_program_factory_update_buf(int slot, void *buf, int size)
+{
+	return rsu_slot_program_buf(slot, buf, size);
+}
+
+/**
+ * rsu_slot_program_buf_raw() - program a slot from raw buffer data
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * This function is used to program a slot using raw data from a buffer,
+ * the slot is not entered into CPB.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_program_buf_raw(int slot, void *buf, int size)
+{
+	int ret;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (rsu_cb_buf_init(buf, size)) {
+		rsu_log(RSU_ERR, "Bad buf/size arguments\n");
+		return -EARGS;
+	}
+
+	ret = rsu_cb_program_common(ll_intf, slot, rsu_cb_buf, 1);
+	if (ret) {
+		rsu_log(RSU_ERR, "fail to program raw data\n");
+		return ret;
+	}
+
+	rsu_cb_buf_exit();
+	return ret;
+}
+
+/**
+ * rsu_slot_verify_buf() - verify FPGA config data in a slot
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * This function is used to verify FPGA configuration data in a selected
+ * slot against the provided buffer.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_verify_buf(int slot, void *buf, int size)
+{
+	int ret;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (rsu_cb_buf_init(buf, size)) {
+		rsu_log(RSU_ERR, "Bad buf/size arguments\n");
+		return -EARGS;
+	}
+
+	ret = rsu_cb_verify_common(ll_intf, slot, rsu_cb_buf, 0);
+	if (ret) {
+		rsu_log(RSU_ERR, "fail to verify buffer data\n");
+		return ret;
+	}
+
+	rsu_cb_buf_exit();
+
+	return ret;
+}
+
+/**
+ * rsu_slot_verify_buf_raw() - verify raw data in a slot
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * This function is used to verify raw data in a selected slot against
+ * the provided buffer.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_verify_buf_raw(int slot, void *buf, int size)
+{
+	int ret;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (rsu_cb_buf_init(buf, size)) {
+		rsu_log(RSU_ERR, "Bad buf/size arguments\n");
+		return -EARGS;
+	}
+
+	ret = rsu_cb_verify_common(ll_intf, slot, rsu_cb_buf, 1);
+	if (ret) {
+		rsu_log(RSU_ERR, "fail to verify raw data\n");
+		return ret;
+	}
+
+	rsu_cb_buf_exit();
+	return ret;
+}
+
+/**
+ * rsu_slot_enable() - enable the selected slot
+ * @slot: slot number
+ *
+ * Set the selected slot as the highest priority. It will be the first
+ * slot to be tried after a power-on reset.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_enable(int slot)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (ll_intf->priority.remove(part_num))
+		return -ELOWLEVEL;
+
+	if (ll_intf->priority.add(part_num))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+/**
+ * rsu_slot_disable() - disable the selected slot
+ * @slot: slot number
+ *
+ * Remove the selected slot from the priority scheme, but don't erase the
+ * slot data so that it can be re-enabled.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_disable(int slot)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (ll_intf->priority.remove(part_num))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+/**
+ * rsu_slot_load() - load the selected slot
+ * @slot: slot number
+ *
+ * This function is used to request the selected slot to be loaded
+ * immediately. On success the system is rebooted after a short delay.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_load(int slot)
+{
+	int part_num;
+	u64 offset;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	offset = ll_intf->partition.offset(part_num);
+
+	if (ll_intf->priority.get(part_num) <= 0) {
+		rsu_log(RSU_ERR, "Try to reboot from an erased slot\n");
+		return -EERASE;
+	}
+
+	return ll_intf->fw_ops.load(offset);
+}
+
+/**
+ * rsu_slot_load_factory() - load the factory image
+ *
+ * This function is used to request the factory image to be loaded
+ * immediately. On success, the system is rebooted after a short delay.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_load_factory(void)
+{
+	int part_num;
+	int partitions;
+	u64 offset;
+	char name[] = "FACTORY_IMAGE";
+
+	if (!ll_intf)
+		return -EINTF;
+
+	partitions = ll_intf->partition.count();
+	for (part_num = 0; part_num < partitions; part_num++) {
+		if (!strcmp(name, ll_intf->partition.name(part_num)))
+			break;
+	}
+
+	if (part_num >= partitions) {
+		rsu_log(RSU_ERR, "No FACTORY_IMAGE partition defined\n");
+		return -EFORMAT;
+	}
+
+	offset = ll_intf->partition.offset(part_num);
+	return ll_intf->fw_ops.load(offset);
+}
+
+/**
+ * rsu_slot_rename() - Rename the selected slot.
+ * @slot: slot number
+ * @name: new name for slot
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_rename(int slot, char *name)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (!name)
+		return -EARGS;
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (rsu_misc_is_rsvd_name(name)) {
+		rsu_log(RSU_ERR, "Partition rename uses a reserved name\n");
+		return -ENAME;
+	}
+
+	if (ll_intf->partition.rename(part_num, name))
+		return -ENAME;
+
+	return 0;
+}
+
+/**
+ * rsu_status_log() - Copy firmware status log to info struct
+ * @info: pointer to info struct to fill in
+ *
+ * Return 0 on success, or error code
+ */
+int rsu_status_log(struct rsu_status_info *info)
+{
+	if (!ll_intf)
+		return -EINTF;
+
+	return ll_intf->fw_ops.status(info);
+}
+
+/**
+ * rsu_notify() - report HPS software execution stage as a 16bit number
+ * @stage: software execution stage
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_notify(int stage)
+{
+	u32 arg;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	arg = stage & GENMASK(15, 0);
+	return ll_intf->fw_ops.notify(arg);
+}
+
+/**
+ * rsu_clear_error_status() - clear errors from the current RSU status log
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_clear_error_status(void)
+{
+	struct rsu_status_info info;
+	u32 arg;
+	int ret;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	ret = rsu_status_log(&info);
+	if (ret < 0)
+		return ret;
+
+	if (!RSU_VERSION_ACMF_VERSION(info.version))
+		return -ELOWLEVEL;
+
+	arg = RSU_NOTIFY_IGNORE_STAGE | RSU_NOTIFY_CLEAR_ERROR_STATUS;
+	return ll_intf->fw_ops.notify(arg);
+}
+
+/**
+ * rsu_reset_retry_counter() - reset the retry counter
+ *
+ * This function is used to request the retry counter to be reset, so that the
+ * currently running image may be tried again after the next watchdog timeout.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_reset_retry_counter(void)
+{
+	struct rsu_status_info info;
+	u32 arg;
+	int ret;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	ret = rsu_status_log(&info);
+	if (ret < 0)
+		return ret;
+
+	if (!RSU_VERSION_ACMF_VERSION(info.version) ||
+	    !RSU_VERSION_DCMF_VERSION(info.version))
+		return -ELOWLEVEL;
+
+	arg = RSU_NOTIFY_IGNORE_STAGE | RSU_NOTIFY_RESET_RETRY_COUNTER;
+	return ll_intf->fw_ops.notify(arg);
+}
diff --git a/drivers/firmware/rsu_ll_qspi.c b/drivers/firmware/rsu_ll_qspi.c
new file mode 100644
index 0000000..dbe29fe
--- /dev/null
+++ b/drivers/firmware/rsu_ll_qspi.c
@@ -0,0 +1,1050 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ */
+
+#include <common.h>
+#include <linux/errno.h>
+#include <asm/arch/mailbox_s10.h>
+#include <intel/rsu.h>
+#include <intel/rsu_misc.h>
+#include <spi.h>
+#include <spi_flash.h>
+
+#define SPT_MAGIC_NUMBER	0x57713427
+#define SPT_FLAG_RESERVED	1
+#define SPT_FLAG_READONLY	2
+
+#define CPB_MAGIC_NUMBER	0x57789609
+#define CPB_HEADER_SIZE		24
+
+#define ERASED_ENTRY		((u64)-1)
+#define SPENT_ENTRY		((u64)0)
+
+#define SPT_VERSION		0
+#define LIBRSU_VER		0
+
+/**
+ * struct sub_partition_table_partition - SPT partition structure
+ * @name: sub-partition name
+ * @offset: sub-partition start offset
+ * @length: sub-partition length
+ * @flags: sub-partition flags
+ */
+struct sub_partition_table_partition {
+	char name[16];
+	u64 offset;
+	u32 length;
+	u32 flags;
+};
+
+/**
+ * struct sub_partition_table - sub partition table structure
+ * @magic_number: the magic number
+ * @version: version number
+ * @partitions: number of entries
+ * @revd: reserved
+ * @sub_partition_table_partition.partition: SPT partition array
+ */
+struct sub_partition_table {
+	u32 magic_number;
+	u32 version;
+	u32 partitions;
+	u32 rsvd[5];
+	struct sub_partition_table_partition partition[127];
+};
+
+/**
+ * union cmf_pointer_block - CMF pointer block
+ * @header.magic_number: CMF pointer block magic number
+ * @header.header_size: size of CMF pointer block header
+ * @header.cpb_size: size of CMF pointer block
+ * @header.cpb_backup_offset: offset of CMF pointer block
+ * @header.image_ptr_offset: offset of image pointers
+ * @header.image_ptr_slots: number of image pointer slots
+ * @data: image pointer slot array
+ */
+union cmf_pointer_block {
+	struct {
+		u32 magic_number;
+		u32 header_size;
+		u32 cpb_size;
+		u32 cpb_backup_offset;
+		u32 image_ptr_offset;
+		u32 image_ptr_slots;
+	} header;
+	char data[4 * 1024];
+};
+
+static union cmf_pointer_block cpb;
+static struct sub_partition_table spt;
+static u64 *cpb_slots;
+struct spi_flash *flash;
+static u32 spt0_offset;
+static u32 spt1_offset;
+
+/**
+ * get_part_offset() - get a selected partition offset
+ * @part_num: the selected partition number
+ * @offset: the partition offset
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int get_part_offset(int part_num, u64 *offset)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	*offset = spt.partition[part_num].offset;
+
+	return 0;
+}
+
+/**
+ * read_dev() - read data from flash
+ * @offset: the offset which read from flash
+ * @buf: buffer for read data
+ * @len: the size of data which read from flash
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int read_dev(u64 offset, void *buf, int len)
+{
+	int ret;
+
+	ret = spi_flash_read(flash, (u32)offset, len, buf);
+	if (ret) {
+		rsu_log(RSU_ERR, "read flash error=%i\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * write_dev() - write data to flash
+ * @offset: the offset which data will written to
+ * @buf: the written data
+ * @len: the size of data which write to flash
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int write_dev(u64 offset, void *buf, int len)
+{
+	int ret;
+
+	ret = spi_flash_write(flash, (u32)offset, len, buf);
+	if (ret) {
+		rsu_log(RSU_ERR, "write flash error=%i\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * erase_dev() - erase data at flash
+ * @offset: the offset from which data will be erased
+ * @len: the size of data to be erased
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int erase_dev(u64 offset, int len)
+{
+	int ret;
+
+	ret = spi_flash_erase(flash, (u32)offset, len);
+	if (ret) {
+		rsu_log(RSU_ERR, "erase flash error=%i\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * read_part() - read a selected partition data
+ * @part_num: the selected partition number
+ * @offset: the offset from which data will be read
+ * @buf: buffer contains the read data
+ * @len: the size of data to be read
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int read_part(int part_num, u64 offset, void *buf, int len)
+{
+	u64 part_offset;
+
+	if (get_part_offset(part_num, &part_offset))
+		return -1;
+
+	if (offset < 0 || len < 0 ||
+	    (offset + len) > spt.partition[part_num].length)
+		return -1;
+
+	return read_dev(part_offset + offset, buf, len);
+}
+
+/**
+ * write_part() - write a selected partition data
+ * @part_num: the selected partition number
+ * @offset: the offset to which data will be written
+ * @buf: data to be written to
+ * @len: the size of data to be written
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int write_part(int part_num, u64 offset, void *buf, int len)
+{
+	u64 part_offset;
+
+	if (get_part_offset(part_num, &part_offset))
+		return -1;
+
+	if (offset < 0 || len < 0 ||
+	    (offset + len) > spt.partition[part_num].length)
+		return -1;
+
+	return write_dev(part_offset + offset, buf, len);
+}
+
+/**
+ * erase_part() - erase a selected partition data
+ * @part_num: the selected partition number
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int erase_part(int part_num)
+{
+	u64 part_offset;
+
+	if (get_part_offset(part_num, &part_offset))
+		return -1;
+
+	return erase_dev(part_offset, spt.partition[part_num].length);
+}
+
+/**
+ * writeback_spt() - write back SPT
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int writeback_spt(void)
+{
+	int x;
+	int updates = 0;
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strcmp(spt.partition[x].name, "SPT0") &&
+		    strcmp(spt.partition[x].name, "SPT1"))
+			continue;
+
+		if (erase_part(x)) {
+			rsu_log(RSU_ERR, "failed to erase SPTx");
+			return -1;
+		}
+
+		spt.magic_number = (u32)0xFFFFFFFF;
+		if (write_part(x, 0, &spt, sizeof(spt))) {
+			rsu_log(RSU_ERR, "failed to write SPTx table");
+			return -1;
+		}
+
+		spt.magic_number = (u32)SPT_MAGIC_NUMBER;
+		if (write_part(x, 0, &spt.magic_number,
+			       sizeof(spt.magic_number))) {
+			rsu_log(RSU_ERR, "failed to write SPTx magic #");
+			return -1;
+		}
+
+		updates++;
+	}
+
+	if (updates != 2) {
+		rsu_log(RSU_ERR, "didn't find two SPTs");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * check_spt() - check if SPT is valid
+ *
+ * Return: 0 for valid SPT, or -1 for not
+ */
+static int check_spt(void)
+{
+	int x;
+	int max_len = sizeof(spt.partition[0].name);
+	int spt0_found = 0;
+	int spt1_found = 0;
+	int cpb0_found = 0;
+	int cpb1_found = 0;
+
+	/*
+	 * Make sure the SPT names are '\0' terminated. Truncate last byte
+	 * if the name uses all available bytes.  Perform validity check on
+	 * entries.
+	 */
+
+	rsu_log(RSU_DEBUG,
+		"MAX length of a name = %i bytes\n", max_len - 1);
+
+	if (spt.version > SPT_VERSION) {
+		rsu_log(RSU_WARNING, "SPT version %i is greater than %i\n",
+			spt.version, SPT_VERSION);
+		rsu_log(RSU_WARNING,
+			"RSU Version %i - update to for newer features\n",
+			LIBRSU_VER);
+	}
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strnlen(spt.partition[x].name, max_len) >= max_len)
+			spt.partition[x].name[max_len - 1] = '\0';
+
+		rsu_log(RSU_DEBUG, "RSU %-16s %016llX - %016llX (%X)\n",
+			spt.partition[x].name, spt.partition[x].offset,
+			(spt.partition[x].offset +
+			spt.partition[x].length - 1),
+			spt.partition[x].flags);
+
+		if (strcmp(spt.partition[x].name, "SPT0") == 0)
+			spt0_found = 1;
+		else if (strcmp(spt.partition[x].name, "SPT1") == 0)
+			spt1_found = 1;
+		else if (strcmp(spt.partition[x].name, "CPB0") == 0)
+			cpb0_found = 1;
+		else if (strcmp(spt.partition[x].name, "CPB1") == 0)
+			cpb1_found = 1;
+	}
+
+	if (!spt0_found || !spt1_found || !cpb0_found || !cpb1_found) {
+		rsu_log(RSU_ERR, "Missing a critical entry in the SPT\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * load_spt() - retrieve SPT from flash
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int load_spt(void)
+{
+	int spt0_good = 0;
+	int spt1_good = 0;
+
+	rsu_log(RSU_DEBUG, "reading SPT1\n");
+	if (read_dev(spt1_offset, &spt, sizeof(spt)) == 0 &&
+	    spt.magic_number == SPT_MAGIC_NUMBER) {
+		if (check_spt() == 0)
+			spt1_good = 1;
+		else
+			rsu_log(RSU_ERR, "SPT1 validity check failed\n");
+	} else {
+		rsu_log(RSU_ERR, "Bad SPT1 magic number 0x%08X\n",
+			spt.magic_number);
+	}
+
+	rsu_log(RSU_DEBUG, "reading SPT0\n");
+	if (read_dev(spt0_offset, &spt, sizeof(spt)) == 0 &&
+	    spt.magic_number == SPT_MAGIC_NUMBER) {
+		if (check_spt() == 0)
+			spt0_good = 1;
+		else
+			rsu_log(RSU_ERR, "SPT0 validity check failed\n");
+	} else {
+		rsu_log(RSU_ERR, "Bad SPT0 magic number 0x%08X\n",
+			spt.magic_number);
+	}
+
+	if (spt0_good && spt1_good) {
+		rsu_log(RSU_INFO, "SPTs are GOOD!!!\n");
+		return 0;
+	}
+
+	if (spt0_good) {
+		rsu_log(RSU_WARNING, "warning: Restoring SPT1\n");
+		if (erase_dev(spt1_offset, 32 * 1024)) {
+			rsu_log(RSU_ERR, "Erase SPT1 region failed\n");
+			return -1;
+		}
+
+		spt.magic_number = (u32)0xFFFFFFFF;
+		if (write_dev(spt1_offset, &spt, sizeof(spt))) {
+			rsu_log(RSU_ERR, "Unable to write SPT1 table\n");
+			return -1;
+		}
+
+		spt.magic_number = (u32)SPT_MAGIC_NUMBER;
+		if (write_dev(spt1_offset, &spt.magic_number,
+			      sizeof(spt.magic_number))) {
+			rsu_log(RSU_ERR, "Unable to wr SPT1 magic #\n");
+			return -1;
+		}
+
+		return 0;
+	}
+
+	if (spt1_good) {
+		if (read_dev(spt1_offset, &spt, sizeof(spt)) ||
+		    spt.magic_number != SPT_MAGIC_NUMBER || check_spt()) {
+			rsu_log(RSU_ERR, "Failed to load SPT1\n");
+			return -1;
+		}
+
+		rsu_log(RSU_WARNING, "Restoring SPT0");
+
+		if (erase_dev(spt0_offset, 32 * 1024)) {
+			rsu_log(RSU_ERR, "Erase SPT0 region failed\n");
+			return -1;
+		}
+
+		spt.magic_number = (u32)0xFFFFFFFF;
+		if (write_dev(spt1_offset, &spt, sizeof(spt))) {
+			rsu_log(RSU_ERR, "Unable to write SPT0 table\n");
+			return -1;
+		}
+
+		spt.magic_number = (u32)SPT_MAGIC_NUMBER;
+		if (write_dev(spt1_offset, &spt.magic_number,
+			      sizeof(spt.magic_number))) {
+			rsu_log(RSU_ERR, "Unable to wr SPT0 magic #\n");
+			return -1;
+		}
+
+		return 0;
+	}
+
+	rsu_log(RSU_ERR, "no valid SPT0 and SPT1 found\n");
+	return -1;
+}
+
+/**
+ * check_cpb() - check if CPB is valid
+ *
+ * Return: 0 for the valid CPB, or -1 for not
+ */
+static int check_cpb(void)
+{
+	int x, y;
+
+	if (cpb.header.header_size > CPB_HEADER_SIZE) {
+		rsu_log(RSU_WARNING,
+			"CPB header is larger than expected\n");
+		return -1;
+	}
+
+	for (x = 0; x < cpb.header.image_ptr_slots; x++) {
+		if (cpb_slots[x] != ERASED_ENTRY &&
+		    cpb_slots[x] != SPENT_ENTRY) {
+			for (y = 0; y < spt.partitions; y++) {
+				if (cpb_slots[x] ==
+				    spt.partition[y].offset) {
+					rsu_log(RSU_DEBUG,
+						"cpb_slots[%i] = %s\n",
+						x, spt.partition[y].name);
+					break;
+				}
+			}
+
+			if (y >= spt.partitions)
+				rsu_log(RSU_DEBUG,
+					"cpb_slots[%i] = %016llX ???",
+					x, cpb_slots[x]);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * load_cpb() - retrieve CPB from flash
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int load_cpb(void)
+{
+	int x;
+	int cpb0_part = -1;
+	int cpb0_good = 0;
+	int cpb1_part = -1;
+	int cpb1_good = 0;
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strcmp(spt.partition[x].name, "CPB0") == 0)
+			cpb0_part = x;
+		else if (strcmp(spt.partition[x].name, "CPB1") == 0)
+			cpb1_part = x;
+
+		if (cpb0_part >= 0 && cpb1_part >= 0)
+			break;
+	}
+
+	if (cpb0_part < 0 || cpb1_part < 0) {
+		rsu_log(RSU_ERR, "Missing CPB0/1 partition\n");
+		return -1;
+	}
+
+	rsu_log(RSU_DEBUG, "Reading CPB1\n");
+	if (read_part(cpb1_part, 0, &cpb, sizeof(cpb)) == 0 &&
+	    cpb.header.magic_number == CPB_MAGIC_NUMBER) {
+		cpb_slots = (u64 *)
+			     &cpb.data[cpb.header.image_ptr_offset];
+		if (check_cpb() == 0)
+			cpb1_good = 1;
+	} else {
+		rsu_log(RSU_ERR, "Bad CPB1 is bad\n");
+	}
+
+	rsu_log(RSU_DEBUG, "Reading CPB0\n");
+	if (read_part(cpb0_part, 0, &cpb, sizeof(cpb)) == 0 &&
+	    cpb.header.magic_number == CPB_MAGIC_NUMBER) {
+		cpb_slots = (u64 *)
+			     &cpb.data[cpb.header.image_ptr_offset];
+		if (check_cpb() == 0)
+			cpb0_good = 1;
+	} else {
+		rsu_log(RSU_ERR, "Bad CPB0 is bad\n");
+	}
+
+	if (cpb0_good && cpb1_good) {
+		rsu_log(RSU_INFO, "CPBs are GOOD!!!\n");
+		cpb_slots = (u64 *)
+			     &cpb.data[cpb.header.image_ptr_offset];
+		return 0;
+	}
+
+	if (cpb0_good) {
+		rsu_log(RSU_WARNING, "Restoring CPB1\n");
+		if (erase_part(cpb1_part)) {
+			rsu_log(RSU_ERR, "Failed erase CPB1\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)0xFFFFFFFF;
+		if (write_part(cpb1_part, 0, &cpb, sizeof(cpb))) {
+			rsu_log(RSU_ERR, "Unable to write CPB1 table\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)CPB_MAGIC_NUMBER;
+		if (write_part(cpb1_part, 0, &cpb.header.magic_number,
+			       sizeof(cpb.header.magic_number))) {
+			rsu_log(RSU_ERR, "Unable to write CPB1 magic number\n");
+			return -1;
+		}
+
+		cpb_slots = (u64 *)&cpb.data[cpb.header.image_ptr_offset];
+		return 0;
+	}
+
+	if (cpb1_good) {
+		if (read_part(cpb1_part, 0, &cpb, sizeof(cpb)) ||
+		    cpb.header.magic_number != CPB_MAGIC_NUMBER) {
+			rsu_log(RSU_ERR, "Unable to load CPB1\n");
+			return -1;
+		}
+
+		rsu_log(RSU_WARNING, "Restoring CPB0\n");
+		if (erase_part(cpb0_part)) {
+			rsu_log(RSU_ERR, "Failed erase CPB0\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)0xFFFFFFFF;
+		if (write_part(cpb0_part, 0, &cpb, sizeof(cpb))) {
+			rsu_log(RSU_ERR, "Unable to write CPB0 table\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)CPB_MAGIC_NUMBER;
+		if (write_part(cpb0_part, 0, &cpb.header.magic_number,
+			       sizeof(cpb.header.magic_number))) {
+			rsu_log(RSU_ERR, "Unable to write CPB0 magic number\n");
+			return -1;
+		}
+
+		cpb_slots = (u64 *)&cpb.data[cpb.header.image_ptr_offset];
+		return 0;
+	}
+
+	rsu_log(RSU_ERR, "No valid CPB0 or CPB1 found\n");
+	return -1;
+}
+
+/**
+ * update_cpb() - update CPB at flash
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int update_cpb(int slot, u64 ptr)
+{
+	int x;
+	int updates = 0;
+
+	if (slot < 0 || slot > cpb.header.image_ptr_slots)
+		return -1;
+
+	if ((cpb_slots[slot] & ptr) != ptr)
+		return -1;
+
+	cpb_slots[slot] = ptr;
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strcmp(spt.partition[x].name, "CPB0") &&
+		    strcmp(spt.partition[x].name, "CPB1"))
+			continue;
+
+		if (write_part(x, 0, &cpb, sizeof(cpb)))
+			return -1;
+
+		updates++;
+	}
+
+	if (updates != 2) {
+		rsu_log(RSU_ERR, "Did not find two CPBs\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * writeback_cpb() - write CPB back to flash
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int writeback_cpb(void)
+{
+	int x;
+	int updates = 0;
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strcmp(spt.partition[x].name, "CPB0") &&
+		    strcmp(spt.partition[x].name, "CPB1"))
+			continue;
+
+		if (erase_part(x)) {
+			rsu_log(RSU_ERR, "Unable to ease CPBx\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)0xFFFFFFFF;
+		if (write_part(x, 0, &cpb, sizeof(cpb))) {
+			rsu_log(RSU_ERR, "Unable to write CPBx table\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)CPB_MAGIC_NUMBER;
+		if (write_part(x, 0, &cpb.header.magic_number,
+			       sizeof(cpb.header.magic_number))) {
+			rsu_log(RSU_ERR,
+				"Unable to write CPBx magic number\n");
+			return -1;
+		}
+
+		updates++;
+	}
+
+	if (updates != 2) {
+		rsu_log(RSU_ERR, "Did not find two CPBs\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * partition_count() - get the partition count
+ *
+ * Return: the number of partition at flash
+ */
+static int partition_count(void)
+{
+	return spt.partitions;
+}
+
+/**
+ * partition_name() - get a selected partition name
+ * @part_num: the selected partition number
+ *
+ * Return: partition name on success, or "BAD" on error
+ */
+static char *partition_name(int part_num)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return "BAD";
+
+	return spt.partition[part_num].name;
+}
+
+/**
+ * partition_offset() - get a selected partition offset
+ * @part_num: the selected partition number
+ *
+ * Return: offset on success, or -1 on error
+ */
+static u64 partition_offset(int part_num)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	return spt.partition[part_num].offset;
+}
+
+/**
+ * partition_size() - get a selected partition size
+ * @part_num: the selected partition number
+ *
+ * Return: the partition size for success, or -1 for error
+ */
+static u32 partition_size(int part_num)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	return spt.partition[part_num].length;
+}
+
+/**
+ * partition_reserved() - check if a selected partition is reserved
+ * @part_num: the selected partition number
+ *
+ * Return: 1 for reserved partition, or 0 for not
+ */
+static int partition_reserved(int part_num)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return 0;
+
+	return (spt.partition[part_num].flags & SPT_FLAG_RESERVED) ? 1 : 0;
+}
+
+/**
+ * partition_readonly() - check if a selected partition is read only
+ * @part_num: the selected partition number
+ *
+ * Return: 1 for read only partition, or 0 for not
+ */
+static int partition_readonly(int part_num)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return 0;
+
+	return (spt.partition[part_num].flags & SPT_FLAG_READONLY) ? 1 : 0;
+}
+
+/**
+ * partition_rename() - rename the selected partition name
+ * @part_num: the selected partition
+ * @name: the new name
+ *
+ * Return: 0 for success, or -1 on error
+ */
+static int partition_rename(int part_num, char *name)
+{
+	int x;
+
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	if (strnlen(name, sizeof(spt.partition[0].name)) >=
+	    sizeof(spt.partition[0].name)) {
+		rsu_log(RSU_ERR,
+			"Partition name is too long - limited to %li",
+			sizeof(spt.partition[0].name) - 1);
+		return -1;
+	}
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strncmp(spt.partition[x].name, name,
+			    sizeof(spt.partition[0].name) - 1) == 0) {
+			rsu_log(RSU_ERR,
+				"Partition rename already in use\n");
+			return -1;
+		}
+	}
+
+	rsu_misc_safe_strcpy(spt.partition[part_num].name,
+			     sizeof(spt.partition[0].name),
+			     name, sizeof(spt.partition[0].name));
+
+	if (writeback_spt())
+		return -1;
+
+	if (load_spt())
+		return -1;
+
+	return 0;
+}
+
+/**
+ * priority_get() - get the selected partition's priority
+ * @part_num: the selected partition number
+ *
+ * Return: 0 for success, or -1 on error
+ */
+static int priority_get(int part_num)
+{
+	int x;
+	int priority = 0;
+
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	for (x = cpb.header.image_ptr_slots; x > 0; x--) {
+		if (cpb_slots[x - 1] != ERASED_ENTRY &&
+		    cpb_slots[x - 1] != SPENT_ENTRY) {
+			priority++;
+			if (cpb_slots[x - 1] ==
+			    spt.partition[part_num].offset)
+				return priority;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * priority_add() - enable the selected partition's priority
+ * @part_num: the selected partition number
+ *
+ * Return: 0 for success, or  -1 on error
+ */
+static int priority_add(int part_num)
+{
+	int x;
+	int y;
+
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	for (x = 0; x < cpb.header.image_ptr_slots; x++) {
+		if (cpb_slots[x] == ERASED_ENTRY) {
+			if (update_cpb(x,
+				       spt.partition[part_num].offset)) {
+				load_cpb();
+				return -1;
+			}
+			return load_cpb();
+		}
+	}
+
+	rsu_log(RSU_DEBUG, "Compressing CPB\n");
+
+	for (x = 0, y = 0; x < cpb.header.image_ptr_slots; x++) {
+		if (cpb_slots[x] != ERASED_ENTRY &&
+		    cpb_slots[x] != SPENT_ENTRY) {
+			cpb_slots[y++] = cpb_slots[x];
+		}
+	}
+
+	if (y < cpb.header.image_ptr_slots)
+		cpb_slots[y++] = spt.partition[part_num].offset;
+	else
+		return -1;
+
+	while (y < cpb.header.image_ptr_slots)
+		cpb_slots[y++] = ERASED_ENTRY;
+
+	if (writeback_cpb() || load_cpb())
+		return -1;
+
+	return 0;
+}
+
+/**
+ * priority_remove() - remove the selected partition's priority
+ * @part_num: the selected partition number
+ *
+ * Return: 0 for success, or -1 on error
+ */
+static int priority_remove(int part_num)
+{
+	int x;
+
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	for (x = 0; x < cpb.header.image_ptr_slots; x++) {
+		if (cpb_slots[x] == spt.partition[part_num].offset)
+			if (update_cpb(x, SPENT_ENTRY)) {
+				load_cpb();
+				return -1;
+			}
+	}
+
+	return load_cpb();
+}
+
+/**
+ * data_read() - read data from flash
+ * @part_num: partition number
+ * @offset: offset which data will be read from
+ * @bytes: data size in byte which will be read
+ * @buf: pointer to buffer contains to be read data
+ *
+ * Return: 0 for success, or error code
+ */
+static int data_read(int part_num, int offset, int bytes, void *buf)
+{
+	return read_part(part_num, offset, buf, bytes);
+}
+
+/**
+ * data_write() - write data to flash
+ * @part_num: partition number
+ * @part_num: offset which data will be written to
+ * @bytes: data size in bytes which will be written
+ * @buf: pointer to buffer contains to be written data
+ *
+ * Return: 0 for success, or error code
+ */
+static int data_write(int part_num, int offset, int bytes, void *buf)
+{
+	return write_part(part_num, offset, buf, bytes);
+}
+
+/**
+ * data_erase() - erase flash data
+ * @part_num: partition number
+ *
+ * Return: 0 for success, or error code
+ */
+static int data_erase(int part_num)
+{
+	return erase_part(part_num);
+}
+
+/**
+ * image_load() - load production or factory image
+ * @offset: the image offset
+ *
+ * Return: 0 for success, or error code
+ */
+static int image_load(u64 offset)
+{
+	u32 flash_offset[2];
+
+	flash_offset[0] = lower_32_bits(offset);
+	flash_offset[1] = upper_32_bits(offset);
+
+	rsu_log(RSU_DEBUG, "RSU_DEBUG: RSU updated to 0x%08x%08x\n",
+		flash_offset[1], flash_offset[0]);
+
+	if (mbox_rsu_update(flash_offset))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+/**
+ * status_log() - get firmware status info
+ * @info: pointer to rsu_status_info
+ *
+ * Return: 0 for success, or error code
+ */
+static int status_log(struct rsu_status_info *info)
+{
+	if (mbox_rsu_status((u32 *)info,
+			    sizeof(struct rsu_status_info) / 4)) {
+		rsu_log(RSU_ERR,
+			"RSU: Firmware or flash content not supporting RSU\n");
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+/**
+ * notify_fw() - call the firmware notify command
+ * @value: the notification value
+ *
+ * Return: 0 for success, or error code
+ */
+static int notify_fw(u32 value)
+{
+	rsu_log(RSU_DEBUG, "RSU_DEBUG: notified with 0x%08x.\n", value);
+
+	if (mbox_hps_stage_notify(value))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+static void ll_exit(void)
+{
+	if (flash) {
+		spi_flash_free(flash);
+		flash = NULL;
+	}
+}
+
+static struct rsu_ll_intf qspi_ll_intf = {
+	.exit = ll_exit,
+
+	.partition.count = partition_count,
+	.partition.name = partition_name,
+	.partition.offset = partition_offset,
+	.partition.size = partition_size,
+	.partition.reserved = partition_reserved,
+	.partition.readonly = partition_readonly,
+	.partition.rename = partition_rename,
+
+	.priority.get = priority_get,
+	.priority.add = priority_add,
+	.priority.remove = priority_remove,
+
+	.data.read = data_read,
+	.data.write = data_write,
+	.data.erase = data_erase,
+
+	.fw_ops.load = image_load,
+	.fw_ops.status = status_log,
+	.fw_ops.notify = notify_fw
+};
+
+int rsu_ll_qspi_init(struct rsu_ll_intf **intf)
+{
+	u32 spt_offset[4];
+
+	/* retrieve data from flash */
+	flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
+				CONFIG_SF_DEFAULT_CS,
+				CONFIG_SF_DEFAULT_SPEED,
+				CONFIG_SF_DEFAULT_MODE);
+	if (!flash) {
+		rsu_log(RSU_ERR, "SPI probe failed.\n");
+		return -ENODEV;
+	}
+
+	/* get the offset from firmware */
+	if (mbox_rsu_get_spt_offset(spt_offset, 4)) {
+		rsu_log(RSU_ERR, "Error from mbox_rsu_get_spt_offset\n");
+		return -ECOMM;
+	}
+
+	spt0_offset = spt_offset[1];
+	spt1_offset = spt_offset[3];
+	rsu_log(RSU_DEBUG, "SPT0 offset 0x%08x\n", spt0_offset);
+	rsu_log(RSU_DEBUG, "SPT1 offset 0x%08x\n", spt1_offset);
+
+	if (load_spt()) {
+		rsu_log(RSU_ERR, "Bad SPT\n");
+		return -1;
+	}
+
+	if (load_cpb()) {
+		rsu_log(RSU_ERR, "Bad CPB\n");
+		return -1;
+	}
+
+	*intf = &qspi_ll_intf;
+
+	return 0;
+}
diff --git a/drivers/firmware/rsu_misc.c b/drivers/firmware/rsu_misc.c
new file mode 100644
index 0000000..e787bb5
--- /dev/null
+++ b/drivers/firmware/rsu_misc.c
@@ -0,0 +1,817 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ */
+
+#include <common.h>
+#include <linux/compat.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <intel/rsu.h>
+#include <intel/rsu_ll.h>
+#include <intel/rsu_misc.h>
+#include <asm/types.h>
+#include <u-boot/zlib.h>
+#include <errno.h>
+
+#define LOG_BUF_SIZE	1024
+
+static char *cb_buffer;
+static int cb_buffer_togo;
+
+static char *reserved_names[] = {
+	"BOOT_INFO",
+	"FACTORY_IMAGE",
+	"SPT",
+	"SPT0",
+	"SPT1",
+	"CPB",
+	"CPB0",
+	"CPB1",
+	""
+};
+
+struct pointer_block {
+	u32 num_ptrs;
+	u32 RSVD0;
+	u64 ptrs[4];
+	u8 RSVD1[0xd4];
+	u32 crc;
+};
+
+/**
+ * enum rsu_block_type - enumeration for image block types
+ * @SECTION_BLOCK: section block
+ * @SIGNATURE_BLOCK: signature block
+ * @REGULAR_BLOCK: all other block types
+ */
+enum rsu_block_type {
+	SECTION_BLOCK = 0,
+	SIGNATURE_BLOCK,
+	REGULAR_BLOCK
+};
+
+/* maximum number of sections supported for an image */
+#define MAX_SECTIONS 64
+
+/**
+ * struct rsu_image_state - structure for stated of image processing
+ * @offset: current block offset in bytes
+ * @block_type: current block type
+ * @sections: identified section offsets
+ * @no_sections: number of identified sections
+ * @absolute: current image is an absolute image
+ *
+ * This structure is used to maintain the state of image parsing, both for
+ * relocating images to final destination in flash, and also for verifying
+ * images already stored in flash.
+ */
+struct rsu_image_state {
+	int offset;
+	enum rsu_block_type block_type;
+	u64 sections[MAX_SECTIONS];
+	int no_sections;
+	int absolute;
+};
+
+/**
+ * find_section() - search section in the current list of identified sections
+ * @state: current state machine state
+ * @section: section to be searched
+ *
+ * Return: 1 if section is found, 0 if section is not found
+ */
+static int find_section(struct rsu_image_state *state, u64 section)
+{
+	int x;
+
+	for (x = 0; x < state->no_sections; x++)
+		if (section == state->sections[x])
+			return 1;
+
+	return 0;
+}
+
+/**
+ * add_section() - add section to the current list of identified sections
+ * @state: current state machine state
+ * @section: section to be added
+ *
+ * Return: zero value for success, -1 on error
+ */
+static int add_section(struct rsu_image_state *state, u64 section)
+{
+	if (find_section(state, section))
+		return 0;
+
+	if (state->no_sections >= MAX_SECTIONS)
+		return -1;
+
+	state->sections[state->no_sections++] = section;
+
+	return 0;
+}
+
+/**
+ * swap_bits() - swap bits
+ * @data: pointer point to data
+ * @len: data length
+ */
+static void swap_bits(char *data, int len)
+{
+	int x, y;
+	char tmp;
+
+	for (x = 0; x < len; x++) {
+		tmp = 0;
+		for (y = 0; y < 8; y++) {
+			tmp <<= 1;
+			if (data[x] & 1)
+				tmp |= 1;
+			data[x] >>= 1;
+		}
+		data[x] = tmp;
+	}
+}
+
+/**
+ * rsu_misc_is_rsvd_name() - check if a reserved name
+ *
+ * @name: name to check
+ *
+ * Returns 1 if a reserved name, or 0 for not
+ */
+int rsu_misc_is_rsvd_name(char *name)
+{
+	int x;
+
+	for (x = 0; reserved_names[x][0] != '\0'; x++)
+		if (strcmp(name, reserved_names[x]) == 0)
+			return 1;
+
+	return 0;
+}
+
+/**
+ * rsu_misc_is_slot() - check if a read only or reserved partition
+ * @ll_intf: pointer to ll_intf
+ * @part_num: partition number
+ *
+ * Return 1 if not read only or reserved, or 0 for is
+ */
+int rsu_misc_is_slot(struct rsu_ll_intf *ll_intf, int part_num)
+{
+	if (ll_intf->partition.readonly(part_num) ||
+	    ll_intf->partition.reserved(part_num))
+		return 0;
+
+	if (rsu_misc_is_rsvd_name(ll_intf->partition.name(part_num)))
+		return 0;
+
+	return 1;
+}
+
+/**
+ * rsu_misc_slot2part() - get partition number from the slot
+ * @ll_intf: pointer to ll_intf
+ * @slot: slot number
+ *
+ * Return 0 if success, or -1 for error
+ */
+int rsu_misc_slot2part(struct rsu_ll_intf *ll_intf, int slot)
+{
+	int partitions;
+	int cnt = 0;
+
+	partitions = ll_intf->partition.count();
+
+	for (int x = 0; x < partitions; x++) {
+		if (rsu_misc_is_slot(ll_intf, x)) {
+			if (slot == cnt)
+				return x;
+			cnt++;
+		}
+	}
+
+	return -1;
+}
+
+/**
+ * rsu_misc_writeprotected() - check if a slot is protected
+ * @slot: the number of slot to be checked
+ *
+ * Return 1 if a slot is protected, 0 for not
+ */
+int rsu_misc_writeprotected(int slot)
+{
+	char *protected;
+	int protected_slot_numb;
+
+	/* protect works only for slot 0-31 */
+	if (slot > 31)
+		return 0;
+
+	protected = env_get("rsu_protected_slot");
+	if (!strcmp(protected, "")) {
+		/* user doesn't set protected slot */
+		return 0;
+	}
+
+	protected_slot_numb = (int)simple_strtol(protected, NULL, 0);
+	if (protected_slot_numb < 0 || protected_slot_numb > 31) {
+		rsu_log(RSU_WARNING,
+			"protected slot works only on the first 32 slots\n");
+		return 0;
+	}
+
+	if (protected_slot_numb == slot)
+		return 1;
+	else
+		return 0;
+}
+
+/**
+ * rsu_misc_safe_strcpy() - buffer copy
+ * @dst: pointer to dst
+ * @dsz: dst buffer size
+ * @src: pointer to src
+ * @ssz: src buffer size
+ */
+void rsu_misc_safe_strcpy(char *dst, int dsz, char *src, int ssz)
+{
+	int len;
+
+	if (!dst || dsz <= 0)
+		return;
+
+	if (!src || ssz <= 0) {
+		dst[0] = '\0';
+		return;
+	}
+
+	len = strnlen(src, ssz);
+	if (len >= dsz)
+		len = dsz - 1;
+
+	memcpy(dst, src, len);
+	dst[len] = '\0';
+}
+
+/**
+ * rsu_cb_buf_init() - initialize buffer parameters
+ * @buf: pointer to buf
+ * @size: size of buffer
+ *
+ * Return 0 if success, or -1 for error
+ */
+int rsu_cb_buf_init(void *buf, int size)
+{
+	if (!buf || size <= 0)
+		return -1;
+
+	cb_buffer = (char *)buf;
+	cb_buffer_togo = size;
+
+	return 0;
+}
+
+/**
+ * rsu_cb_buf_exit() - reset buffer parameters
+ */
+void rsu_cb_buf_exit(void)
+{
+	cb_buffer = NULL;
+	cb_buffer_togo = -1;
+}
+
+/**
+ * rsu_cb_buf() - copy data to buffer
+ * @buf: pointer to data buffer
+ * @len: size of data buffer
+ *
+ * Return the buffer data size
+ */
+int rsu_cb_buf(void *buf, int len)
+{
+	int read_len;
+
+	if (!cb_buffer_togo)
+		return 0;
+
+	if (!cb_buffer || cb_buffer_togo < 0 || !buf || len < 0)
+		return -1;
+
+	if (cb_buffer_togo < len)
+		read_len = cb_buffer_togo;
+	else
+		read_len = len;
+
+	memcpy(buf, cb_buffer, read_len);
+
+	cb_buffer += read_len;
+	cb_buffer_togo -= read_len;
+
+	if (!cb_buffer_togo)
+		cb_buffer = NULL;
+
+	return read_len;
+}
+
+/**
+ * sig_block_process() - process signature block
+ * @state: current state machine state
+ * @block: signature block
+ * @info: slot where the data will be written
+ *
+ * Determine if the signature block is part of an absolute image, and add its
+ * section pointers to the list of identified sections.
+ *
+ * Return: zero value for success, or negative value on error
+ */
+static int sig_block_process(struct rsu_image_state *state,	void *block,
+			     struct rsu_slot_info *info)
+{
+	char *data = (char *)block;
+	struct pointer_block *ptr_blk = (struct pointer_block *)(data
+					+ SIG_BLOCK_PTR_OFFS);
+	int x;
+
+	/* Determine if absolute image - only done for 2nd block in an image
+	 * which is always a signature block
+	 */
+	if (state->offset == IMAGE_BLOCK_SZ)
+		for (x = 0; x < 4; x++)
+			if (ptr_blk->ptrs[x] > (__u64)info->size) {
+				state->absolute = 1;
+				rsu_log(RSU_DEBUG, "Found absolute image\n");
+				break;
+			}
+
+	/* Add pointers to list of identified sections */
+	for (x = 0; x < 4; x++)
+		if (ptr_blk->ptrs[x]) {
+			if (state->absolute)
+				add_section(state, ptr_blk->ptrs[x] -
+					    info->offset);
+			else
+				add_section(state, ptr_blk->ptrs[x]);
+		}
+
+	return 0;
+}
+
+/**
+ * sig_block_adjust() - adjust signature block pointers before writing to flash
+ * @state: current state machine state
+ * @block: signature block
+ * @info: slot where the data will be written
+ *
+ * This function checks that the section pointers are consistent, and for non-
+ * absolute images it updates them to match the destination slot, also re-
+ * computing the CRC.
+ *
+ * Return: zero value for success, -1 on error
+ */
+static int sig_block_adjust(struct rsu_image_state *state, void *block,
+			    struct rsu_slot_info *info)
+{
+	u32 calc_crc;
+	int x;
+	char *data = (char *)block;
+	struct pointer_block *ptr_blk = (struct pointer_block *)(data
+					+ SIG_BLOCK_PTR_OFFS);
+
+	/*
+	 * Check CRC on 4kB block before proceeding.  All bytes must be
+	 * bit-swapped before they can used in zlib CRC32 library function.
+	 * The CRC value is stored in big endian in the bitstream.
+	 */
+	swap_bits(block, IMAGE_BLOCK_SZ);
+	calc_crc = crc32(0, (uchar *)block, SIG_BLOCK_CRC_OFFS);
+	if (be32_to_cpu(ptr_blk->crc) != calc_crc) {
+		rsu_log(RSU_ERR,
+			"Error: Bad CRC32. Calc = %08X / From Block = %08x\n",
+			calc_crc, be32_to_cpu(ptr_blk->crc));
+		return -1;
+	}
+	swap_bits(block, IMAGE_BLOCK_SZ);
+
+	/* Check pointers */
+	for (x = 0; x < 4; x++) {
+		u64 ptr = ptr_blk->ptrs[x];
+
+		if (!ptr)
+			continue;
+
+		if (state->absolute)
+			ptr -= info->offset;
+
+		if (ptr > info->size) {
+			rsu_log(RSU_ERR,
+				"Error: A pointer not within the slot\n");
+			return -1;
+		}
+	}
+
+	/* Absolute images do not require pointer updates */
+	if (state->absolute)
+		return 0;
+
+	/* Update pointers */
+	for (x = 0; x < 4; x++) {
+		if (ptr_blk->ptrs[x]) {
+			u64 old =  ptr_blk->ptrs[x];
+
+			ptr_blk->ptrs[x] += info->offset;
+			rsu_log(RSU_DEBUG,
+				"Adjusting pointer 0x%llx -> 0x%llx\n",
+				old, ptr_blk->ptrs[x]);
+		}
+	}
+
+	/* Update CRC in block */
+	swap_bits(block, IMAGE_BLOCK_SZ);
+	calc_crc = crc32(0, (uchar *)block, SIG_BLOCK_CRC_OFFS);
+	ptr_blk->crc = be32_to_cpu(calc_crc);
+	swap_bits(block, IMAGE_BLOCK_SZ);
+
+	return 0;
+}
+
+/**
+ * block_compare() - compare two image blocks
+ * @state: current state machine state
+ * @block: input data provided by user
+ * @vblock: verification data read from flash
+ *
+ * Return: non-negative value for successful comparisor, or negative value on
+ * failure or comparison difference found.
+ */
+static int block_compare(struct rsu_image_state *state, void *block,
+			 void *vblock)
+{
+	char *buf = (char *)block;
+	char *vbuf = (char *)vblock;
+	int x;
+
+	for (x = 0; x < IMAGE_BLOCK_SZ; x++)
+		if (vbuf[x] != buf[x]) {
+			rsu_log(RSU_ERR, "Expect %02X, got %02X @0x%08X\n",
+				buf[x], vbuf[x], state->offset + x);
+			return -ECMP;
+		}
+
+	return 0;
+}
+
+/**
+ * sig_block_compare() - compare two signature blocks
+ * @state: current state machine state
+ * @ublock: input data provided by user
+ * @vblock: verification data read from flash
+ * @info: slot where the verification data was read from
+ *
+ * Absolute images are compared directly, while for non-absolute images the
+ * pointers and associated CRC are re-computed to see if they match.
+ *
+ * Return: zero for success, or negative value on erorr or finding differences.
+ */
+static int sig_block_compare(struct rsu_image_state *state, void *ublock,
+			     void *vblock, struct rsu_slot_info *info)
+{
+	u32 calc_crc;
+	int x;
+	char block[IMAGE_BLOCK_SZ];
+	struct pointer_block *ptr_blk = (struct pointer_block *)(block +
+		SIG_BLOCK_PTR_OFFS);
+
+	rsu_log(RSU_DEBUG, "Compare signature block @0x%08x\n", state->offset);
+
+	/* Make a copy of the data provided by the user */
+	memcpy(block, ublock, IMAGE_BLOCK_SZ);
+
+	/* Update signature block to match what we expect in flash */
+	if (!state->absolute) {
+		/* Update pointers */
+		for (x = 0; x < 4; x++)
+			if (ptr_blk->ptrs[x])
+				ptr_blk->ptrs[x] += info->offset;
+
+		/* Update CRC in block */
+		swap_bits(block, IMAGE_BLOCK_SZ);
+		calc_crc = crc32(0, (uchar *)block, SIG_BLOCK_CRC_OFFS);
+		ptr_blk->crc = be32_to_cpu(calc_crc);
+		swap_bits(block, IMAGE_BLOCK_SZ);
+	}
+
+	return block_compare(state, block, vblock);
+}
+
+/**
+ * rsu_misc_image_block_init() - initialize state machine for processing blocks
+ * @state: current state machine state
+ *
+ * Function is called before processing images either for writing to flash or
+ * for comparison with verification data.
+ *
+ * Returns 0 on success, or -1 on error
+ */
+static int rsu_misc_image_block_init(struct rsu_image_state *state)
+{
+	rsu_log(RSU_DEBUG, "Resetting image block state machine\n");
+
+	state->no_sections = 1;
+	add_section(state, 0);
+	state->block_type = REGULAR_BLOCK;
+	state->absolute = 0;
+	state->offset = -IMAGE_BLOCK_SZ;
+
+	return 0;
+}
+
+/**
+ * rsu_misc_image_block_process() - process image blocks
+ *
+ * @state: current state machine state
+ * @block: pointer to current 4KB image block
+ * @vblock: pointer to current 4KB image verification block
+ * @info: rsu_slot_info structure for target slot
+ *
+ * Image blocks are processed either for updating before writing to flash
+ * (when vblock==NULL) or for comparison with verification data
+ * (when vblock!=NULL)
+ *
+ * Returns 0 on success and -1 on error
+ */
+static int rsu_misc_image_block_process(struct rsu_image_state *state,
+					void *block, void *vblock,
+					struct rsu_slot_info *info)
+{
+	u32 magic;
+
+	state->offset += IMAGE_BLOCK_SZ;
+
+	if (find_section(state, state->offset))
+		state->block_type = SECTION_BLOCK;
+
+	switch (state->block_type) {
+	case SECTION_BLOCK:
+		magic = *(__u32 *)block;
+		if (magic == CMF_MAGIC) {
+			rsu_log(RSU_DEBUG, "Found CMF sect @0x%08x\n",
+				state->offset);
+			state->block_type = SIGNATURE_BLOCK;
+		} else {
+			state->block_type = REGULAR_BLOCK;
+		}
+
+		if (vblock)
+			return block_compare(state, block, vblock);
+		break;
+
+	case SIGNATURE_BLOCK:
+		rsu_log(RSU_DEBUG, "Found signature block @0x%08x\n",
+			state->offset);
+
+		if (sig_block_process(state, block, info))
+			return -1;
+
+		state->block_type = REGULAR_BLOCK;
+
+		if (vblock)
+			return sig_block_compare(state, block, vblock, info);
+
+		if (sig_block_adjust(state, block, info))
+			return -1;
+
+		break;
+
+	case REGULAR_BLOCK:
+		break;
+	}
+
+	if (vblock)
+		return block_compare(state, block, vblock);
+
+	return 0;
+}
+
+/**
+ * rsu_cb_program_common - callback to program flash
+ * @ll_intf: pointer to ll_intf
+ * @slot: slot number
+ * @callback: callback function pointer
+ * @rawdata: flag (raw data or not)
+ *
+ * Return 0 if success, or error code
+ */
+int rsu_cb_program_common(struct rsu_ll_intf *ll_intf, int slot,
+			  rsu_data_callback callback, int rawdata)
+{
+	int part_num;
+	int offset;
+	unsigned char buf[IMAGE_BLOCK_SZ];
+	unsigned char vbuf[IMAGE_BLOCK_SZ];
+	int cnt, c, done;
+	int x;
+	struct rsu_slot_info info;
+	struct rsu_image_state state;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0)
+		return -ESLOTNUM;
+
+	if (rsu_misc_writeprotected(slot)) {
+		rsu_log(RSU_ERR,
+			"Trying to program a write protected slot\n");
+		return -EWRPROT;
+	}
+
+	if (rsu_slot_get_info(slot, &info)) {
+		rsu_log(RSU_ERR, "Unable to read slot info\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (ll_intf->priority.get(part_num) > 0) {
+		rsu_log(RSU_ERR,
+			"Trying to program a slot already in use\n");
+		return -EPROGRAM;
+	}
+
+	if (!callback)
+		return -EARGS;
+
+	offset = 0;
+	done = 0;
+
+	if (rsu_misc_image_block_init(&state))
+		return -EPROGRAM;
+
+	while (!done) {
+		cnt = 0;
+		while (cnt < IMAGE_BLOCK_SZ) {
+			c = callback(buf + cnt, IMAGE_BLOCK_SZ - cnt);
+			if (c == 0) {
+				done = 1;
+				break;
+			} else if (c < 0) {
+				return -ECALLBACK;
+			}
+			cnt += c;
+		}
+
+		if (cnt == 0)
+			break;
+
+		if (!rawdata)
+			if (rsu_misc_image_block_process(&state, buf, NULL,
+							 &info))
+				return -EPROGRAM;
+
+		if ((offset + cnt) > ll_intf->partition.size(part_num)) {
+			rsu_log(RSU_ERR,
+				"Trying to program too much data into slot\n");
+			return -ESIZE;
+		}
+
+		if (ll_intf->data.write(part_num, offset, cnt, buf))
+			return -ELOWLEVEL;
+
+		if (ll_intf->data.read(part_num, offset, cnt, vbuf))
+			return -ELOWLEVEL;
+
+		for (x = 0; x < cnt; x++)
+			if (vbuf[x] != buf[x]) {
+				rsu_log(RSU_DEBUG,
+					"Expect %02X, got %02X @ 0x%08X\n",
+					buf[x], vbuf[x], offset + x);
+				return -ECMP;
+			}
+
+		offset += cnt;
+	}
+
+	if (!rawdata && ll_intf->priority.add(part_num))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+/**
+ * rsu_cb_verify_common() - callback for data verification
+ * @ll_intf: pointer to ll_intf
+ * @slot: slot number
+ * @callback: callback function pointer
+ * @rawdata: flag (raw data or not)
+ *
+ * Return 0 if success, or error code
+ */
+int rsu_cb_verify_common(struct rsu_ll_intf *ll_intf, int slot,
+			 rsu_data_callback callback, int rawdata)
+{
+	int part_num;
+	int offset;
+	unsigned char buf[IMAGE_BLOCK_SZ];
+	unsigned char vbuf[IMAGE_BLOCK_SZ];
+	int cnt, c, done;
+	int x;
+	struct rsu_slot_info info;
+	struct rsu_image_state state;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (rsu_slot_get_info(slot, &info)) {
+		rsu_log(RSU_ERR, "Unable to read slot info\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (!rawdata && ll_intf->priority.get(part_num) <= 0) {
+		rsu_log(RSU_ERR, "Trying to verify a slot not in use\n");
+		return -EERASE;
+	}
+
+	if (!callback)
+		return -EARGS;
+
+	offset = 0;
+	done = 0;
+
+	if (rsu_misc_image_block_init(&state))
+		return -ECMP;
+
+	while (!done) {
+		cnt = 0;
+		while (cnt < IMAGE_BLOCK_SZ) {
+			c = callback(buf + cnt, IMAGE_BLOCK_SZ - cnt);
+			if (c == 0) {
+				done = 1;
+				break;
+			} else if (c < 0) {
+				return -ECALLBACK;
+			}
+
+			cnt += c;
+		}
+
+		if (cnt == 0)
+			break;
+
+		if (ll_intf->data.read(part_num, offset, cnt, vbuf))
+			return -ELOWLEVEL;
+
+		if (!rawdata) {
+			if (rsu_misc_image_block_process(&state, buf, vbuf,
+							 &info))
+				return -ECMP;
+
+			offset += cnt;
+			continue;
+		}
+
+		for (x = 0; x < cnt; x++)
+			if (vbuf[x] != buf[x]) {
+				rsu_log(RSU_ERR,
+					"Expect %02X, got %02X @ 0x%08X\n",
+					buf[x], vbuf[x], offset + x);
+				return -ECMP;
+			}
+
+		offset += cnt;
+	}
+
+	return 0;
+}
+
+/*
+ * rsu_log() - display rsu log message
+ * @level: log level
+ * @format: log message format
+ */
+void rsu_log(const enum rsu_log_level level, const char *format, ...)
+{
+	va_list args;
+	int log_level;
+	char printbuffer[LOG_BUF_SIZE];
+
+	log_level = (int)simple_strtol(env_get("rsu_log_level"), NULL, 0);
+
+	if (level >= log_level)
+		return;
+
+	va_start(args, format);
+	vscnprintf(printbuffer, sizeof(printbuffer), format, args);
+	va_end(args);
+	puts(printbuffer);
+}
diff --git a/include/intel/rsu.h b/include/intel/rsu.h
new file mode 100644
index 0000000..4a55ae2
--- /dev/null
+++ b/include/intel/rsu.h
@@ -0,0 +1,291 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Intel Corporation
+ */
+
+#ifndef __RSU_H__
+#define __RSU_H__
+
+#include <asm/types.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+
+/* RSU Error Codes */
+#define EINTF		1
+#define ECFG		2
+#define ESLOTNUM	3
+#define EFORMAT		4
+#define EERASE		5
+#define EPROGRAM	6
+#define ECMP		7
+#define ESIZE		8
+#define ENAME		9
+#define EFILEIO		10
+#define ECALLBACK	11
+#define ELOWLEVEL	12
+#define EWRPROT		13
+#define EARGS		14
+
+/* RSU Version Bitmasks */
+#define RSU_VERSION_ERR_MASK		GENMASK(31, 16)
+#define RSU_VERSION_DCMF_MASK		GENMASK(7, 0)
+#define RSU_VERSION_ACMF_MASK		GENMASK(15, 8)
+
+/* Macros for extracting RSU version fields */
+#define RSU_VERSION_ERROR_SOURCE(v)	FIELD_GET(RSU_VERSION_ERR_MASK, (v))
+#define RSU_VERSION_ACMF_VERSION(v)	FIELD_GET(RSU_VERSION_ACMF_MASK, (v))
+#define RSU_VERSION_DCMF_VERSION(v)	FIELD_GET(RSU_VERSION_DCMF_MASK, (v))
+
+/**
+ * struct rsu_status_info - firmware status log info structure
+ * @current_image:address of image currently running in flash
+ * @fail_image: address of failed image in flash
+ * @state: the state of RSU system
+ * @version: the version number of RSU firmware
+ * @error_location: the error offset inside the failed image
+ * @error_details: error code
+ * @retry_counter: current image retry counter
+ *
+ * This structure is used to capture firmware status log information
+ */
+struct rsu_status_info {
+	u64 current_image;
+	u64 fail_image;
+	u32 state;
+	u32 version;
+	u32 error_location;
+	u32 error_details;
+	u32 retry_counter;
+};
+
+/**
+ * struct rsu_slot_info - slot information structure
+ * @name: a slot name
+ * @offset: a slot offset
+ * @size: the size of a slot
+ * @priority: the priority of a slot
+ *
+ * This structure is used to capture the slot information details
+ */
+struct rsu_slot_info {
+	char name[16];
+	u64 offset;
+	u32 size;
+	int priority;
+};
+
+/**
+ * rsu_init() - initialize flash driver, SPT and CPB data
+ * @filename: NULL for qspi
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_init(char *filename);
+
+/**
+ * rsu_exit() - free flash driver, clean SPT and CPB data
+ */
+void rsu_exit(void);
+
+/**
+ * rsu_slot_count() - get the number of slots defined
+ *
+ * Returns: the number of defined slots
+ */
+int rsu_slot_count(void);
+
+/**
+ * rsu_slot_by_name() - get slot number based on name
+ * @name: name of slot
+ *
+ * Return:slot number on success, or error code
+ */
+int rsu_slot_by_name(char *name);
+
+/**
+ * rsu_slot_get_info() - get the attributes of a slot
+ * @slot: slot number
+ * @info: pointer to info structure to be filled in
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_get_info(int slot, struct rsu_slot_info *info);
+
+/**
+ * rsu_slot_size() - get the size of a slot
+ * @slot: slot number
+ *
+ * Returns: the size of the slot in bytes, or error code
+ */
+int rsu_slot_size(int slot);
+
+/**
+ * rsu_slot_priority() - get the Decision CMF load priority of a slot
+ * @slot: slot number
+ *
+ * Priority of zero means the slot has no priority and is disabled.
+ * The slot with priority of one has the highest priority.
+ *
+ * Returns: the priority of the slot, or error code
+ */
+int rsu_slot_priority(int slot);
+
+/**
+ * rsu_slot_erase() - erase all data in a slot
+ * @slot: slot number
+ *
+ * Erase all data in a slot to prepare for programming. Remove the slot
+ * if it is in the CMF pointer block.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_erase(int slot);
+
+/**
+ * rsu_slot_program_buf() - program a slot from FPGA buffer data
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * This function is used to program a slot using FPGA config data from a
+ * buffer and then enter the slot into CPB.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_program_buf(int slot, void *buf, int size);
+
+/**
+ * rsu_slot_program_factory_update_buf() - program a slot using factory update
+ *                                         data from a buffer
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer
+ *
+ * This function is used to program a slot using factory update data from a
+ * buffer and then enter the slot into CPB.
+ *
+ * Returns 0 on success, or error code
+ */
+int rsu_slot_program_factory_update_buf(int slot, void *buf, int size);
+
+/**
+ * rsu_slot_program_buf_raw() - program a slot from raw buffer data
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * This function is used to program a slot using raw data from a buffer,
+ * and then enter this slot into CPB.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_program_buf_raw(int slot, void *buf, int size);
+
+/**
+ * rsu_slot_verify_buf() - verify FPGA config data in a slot against a
+ * buffer
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_verify_buf(int slot, void *buf, int size);
+
+/**
+ * rsu_slot_verify_buf_raw() - verify raw data in a slot against a buffer
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_verify_buf_raw(int slot, void *buf, int size);
+
+/**
+ * rsu_slot_enable() - enable the selected slot
+ * @slot: slot number
+ *
+ * Set the selected slot as the highest priority. It will be the first
+ * slot to be tried after a power-on reset.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_enable(int slot);
+
+/**
+ * rsu_slot_disable() - disable the selected slot
+ * @slot: slot number
+ *
+ * Remove the selected slot from the priority scheme, but don't erase the
+ * slot data so that it can be re-enabled.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_disable(int slot);
+
+/**
+ * rsu_slot_load() - load the selected slot
+ * @slot: slot number
+ *
+ * This function is used to request the selected slot to be loaded
+ * immediately. On success, after a small delay, the system is rebooted.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_load(int slot);
+
+/**
+ * rsu_slot_load_factory() - load the factory image
+ *
+ * This function is used to request the factory image to be loaded
+ * immediately. On success, after a small delay, the system is rebooted.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_load_factory(void);
+
+/**
+ * rsu_slot_rename() - Rename the selected slot.
+ * @slot: slot number
+ * @name: new name for slot
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_rename(int slot, char *name);
+
+/**
+ * rsu_status_log() - Copy firmware status log to info struct
+ * @info: pointer to info struct to fill in
+ *
+ * Return 0 on success, or error code
+ */
+int rsu_status_log(struct rsu_status_info *info);
+
+/**
+ * rsu_notify() - report HPS software execution stage as a 16bit number
+ * @stage: software execution stage
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_notify(int stage);
+
+/**
+ * rsu_clear_error_status() - clear errors from the current RSU status log
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_clear_error_status(void);
+
+/**
+ * rsu_reset_retry_counter() - reset the retry counter
+ *
+ * This function is used to request the retry counter to be set to zero, so that
+ * the currently running image may be tried again after the next watchdog
+ * timeout.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_reset_retry_counter(void);
+
+#endif
diff --git a/include/intel/rsu_ll.h b/include/intel/rsu_ll.h
new file mode 100644
index 0000000..0ead8c3
--- /dev/null
+++ b/include/intel/rsu_ll.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Intel Corporation
+ */
+
+#ifndef __RSU_LL_H__
+#define __RSU_LL_H__
+
+#include <intel/rsu.h>
+#include <asm/types.h>
+
+/**
+ * struct rsu_ll_intf - RSU low level interface
+ * @exit: exit low level interface
+ * @partition.count: get number of slots
+ * @@partition.name: get name of a slot
+ * @partition.offset: get offset of a slot
+ * @partition.size: get size of a slot
+ * @partition.reserved: check if a slot is reserved
+ * @partition.readonly: check if a slot is read only
+ * @partition.rename: rename a slot
+ * @priority.get: get priority
+ * @priority.add: add priority
+ * @priority.remove: remove priority
+ * @data.read: read data from flash device
+ * @data.write: write date to flash device
+ * @data.erase: erase date from flash device
+ * @fw_ops.load: inform firmware to load image
+ * @fw_ops.status: get status from firmware
+ * @fw_ops.notify: send notify command to firmware
+ */
+struct rsu_ll_intf {
+	void (*exit)(void);
+
+	struct {
+		int (*count)(void);
+		char* (*name)(int part_num);
+		u64 (*offset)(int part_num);
+		u32 (*size)(int part_num);
+		int (*reserved)(int part_num);
+		int (*readonly)(int part_num);
+		int (*rename)(int part_num, char *name);
+	} partition;
+
+	struct {
+		int (*get)(int part_num);
+		int (*add)(int part_num);
+		int (*remove)(int part_num);
+	} priority;
+
+	struct {
+		int (*read)(int part_num, int offset, int bytes, void *buf);
+		int (*write)(int part_num, int offset, int bytes, void *buf);
+		int (*erase)(int part_num);
+	} data;
+
+	struct {
+		int (*load)(u64 offset);
+		int (*status)(struct rsu_status_info *info);
+		int (*notify)(u32 value);
+	} fw_ops;
+};
+
+/**
+ * rsu_ll_qspi_init() - low level qspi interface init
+ * @intf: pointer to pointer of low level interface
+ *
+ * Return: 0 on success, or error code
+ */
+int rsu_ll_qspi_init(struct rsu_ll_intf **intf);
+#endif
+
diff --git a/include/intel/rsu_misc.h b/include/intel/rsu_misc.h
new file mode 100644
index 0000000..ea29e8d
--- /dev/null
+++ b/include/intel/rsu_misc.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Intel Corporation
+ */
+
+#ifndef __RSU_MISC_H__
+#define __RSU_MISC_H__
+
+#include <intel/rsu_ll.h>
+
+/*
+ * A bitstream is a recurrent data structure composed of sections. Each section
+ * consists of 4KB blocks. The first block in a section is called the main
+ * descriptor and the first 32bit value in that descriptor identifies the
+ * section typpe, with 0x62294895 denoting a CMF section.
+ * The second block in a section is called a signature block. The last 256 bytes
+ * of the signature block are called the main image pointer, and contains up to
+ * four pointers to other sections in the bitstream. The entire signature block,
+ * including the main pointer area is protected by a 32-bit CRC.
+ *
+ * The slot size is used to determine if the bitstream was generated using a
+ * slot offset address of zero. The main image pointers of all the CMF sections
+ * identified in the bitstream are updated when programming into a slot if all
+ * of the pointers are less than the slot size.
+ */
+
+#define IMAGE_BLOCK_SZ      0x1000      /* Bitstream block size */
+#define SIG_BLOCK_PTR_OFFS  0x0F00      /* Signature block pointer offset */
+#define SIG_BLOCK_CRC_OFFS  0x0FFC      /* Signature block CRC offset */
+#define CMF_MAGIC           0x62294895  /* Magic identifier for CMF sections */
+
+/* log level */
+enum rsu_log_level {
+	RSU_EMERG = 0,
+	RSU_ALERT,
+	RSU_CRIT,
+	RSU_ERR,
+	RSU_WARNING,
+	RSU_NOTICE,
+	RSU_INFO,
+	RSU_DEBUG
+};
+
+typedef int (*rsu_data_callback)(void *buf, int size);
+int rsu_cb_buf_init(void *buf, int size);
+void rsu_cb_buf_exit(void);
+int rsu_cb_buf(void *buf, int len);
+int rsu_cb_program_common(struct rsu_ll_intf *ll_intf, int slot,
+			  rsu_data_callback callback, int rawdata);
+int rsu_cb_verify_common(struct rsu_ll_intf *ll_intf, int slot,
+			 rsu_data_callback callback, int rawdata);
+
+int rsu_misc_is_rsvd_name(char *name);
+int rsu_misc_is_slot(struct rsu_ll_intf *ll_intf, int part_num);
+int rsu_misc_slot2part(struct rsu_ll_intf *ll_intf, int slot);
+int rsu_misc_writeprotected(int slot);
+void rsu_misc_safe_strcpy(char *dst, int dsz, char *src, int ssz);
+
+void rsu_log(const enum rsu_log_level level, const char *format, ...);
+#endif
-- 
2.7.4



More information about the U-Boot mailing list