From patchwork Wed Oct 30 20:34:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Gong X-Patchwork-Id: 1186933 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.intel.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 473KbT5VZpz9sPT for ; Thu, 31 Oct 2019 07:21:09 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 72852C21E52; Wed, 30 Oct 2019 20:20:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 50A9BC21ECF; Wed, 30 Oct 2019 20:20:44 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 09C9AC21EF0; Wed, 30 Oct 2019 20:20:36 +0000 (UTC) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by lists.denx.de (Postfix) with ESMTPS id 53579C21DFA for ; Wed, 30 Oct 2019 20:20:33 +0000 (UTC) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 30 Oct 2019 13:20:31 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.68,248,1569308400"; d="scan'208";a="230582062" Received: from marshy.an.intel.com ([10.122.105.159]) by fmsmga002.fm.intel.com with ESMTP; 30 Oct 2019 13:20:30 -0700 From: richard.gong@linux.intel.com To: marex@denx.de, simon.k.r.goldschmidt@gmail.com, albert.u.boot@aribaud.net, u-boot@lists.denx.de Date: Wed, 30 Oct 2019 15:34:53 -0500 Message-Id: <1572467696-15358-2-git-send-email-richard.gong@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1572467696-15358-1-git-send-email-richard.gong@linux.intel.com> References: <1572467696-15358-1-git-send-email-richard.gong@linux.intel.com> Cc: chee.hong.ang@intel.com, Chin Liang See , radu.bacrau@intel.com, richard.gong@intel.com Subject: [U-Boot] [PATCHv2 1/4] arm: socfpga: stratix10: add RSU mailbox support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Richard Gong Add Remote System Update (RSU) related mailbox support. This includes RSU_STATUS which reports status of bitstream loaded by Configuration Management Firmware (CMF), RSU_UPDATE which will invokes CMF to load new bitstream, GET_SUBPARTITION_TABLE which will query CMF on the start address of sub-partition table, HPS_STAGE_NOTIFY which notifies firmware the stage of HPS code execution and related functions to support Secure Monitor Call (SMC). Signed-off-by: Ley Foon Tan Signed-off-by: Chin Liang See Signed-off-by: Radu Bacrau Signed-off-by: Richard Gong --- arch/arm/mach-socfpga/include/mach/mailbox_s10.h | 36 ++++++++--- arch/arm/mach-socfpga/mailbox_s10.c | 79 ++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-socfpga/include/mach/mailbox_s10.h b/arch/arm/mach-socfpga/include/mach/mailbox_s10.h index ae728a5..979fd06 100644 --- a/arch/arm/mach-socfpga/include/mach/mailbox_s10.h +++ b/arch/arm/mach-socfpga/include/mach/mailbox_s10.h @@ -77,16 +77,20 @@ enum ALT_SDM_MBOX_RESP_CODE { }; /* Mailbox command list */ -#define MBOX_RESTART 2 -#define MBOX_CONFIG_STATUS 4 -#define MBOX_RECONFIG 6 -#define MBOX_RECONFIG_MSEL 7 -#define MBOX_RECONFIG_DATA 8 -#define MBOX_RECONFIG_STATUS 9 -#define MBOX_QSPI_OPEN 50 -#define MBOX_QSPI_CLOSE 51 -#define MBOX_QSPI_DIRECT 59 -#define MBOX_REBOOT_HPS 71 +#define MBOX_RESTART 2 +#define MBOX_CONFIG_STATUS 4 +#define MBOX_RECONFIG 6 +#define MBOX_RECONFIG_MSEL 7 +#define MBOX_RECONFIG_DATA 8 +#define MBOX_RECONFIG_STATUS 9 +#define MBOX_QSPI_OPEN 50 +#define MBOX_QSPI_CLOSE 51 +#define MBOX_QSPI_DIRECT 59 +#define MBOX_REBOOT_HPS 71 +#define MBOX_GET_SUBPARTITION_TABLE 90 +#define MBOX_RSU_STATUS 91 +#define MBOX_RSU_UPDATE 92 +#define MBOX_HPS_STAGE_NOTIFY 93 /* Mailbox registers */ #define MBOX_CIN 0 /* command valid offset */ @@ -130,6 +134,11 @@ enum ALT_SDM_MBOX_RESP_CODE { #define RCF_SOFTFUNC_STATUS_SEU_ERROR BIT(3) #define RCF_PIN_STATUS_NSTATUS BIT(31) +/* Defines for HPS_STAGE_NOTIFY */ +#define HPS_EXECUTION_STATE_FSBL 0 +#define HPS_EXECUTION_STATE_SSBL 1 +#define HPS_EXECUTION_STATE_OS 2 + int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg, u8 urgent, u32 *resp_buf_len, u32 *resp_buf); int mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg, @@ -146,6 +155,13 @@ int mbox_qspi_open(void); #endif int mbox_reset_cold(void); +int mbox_rsu_get_spt_offset(u32 *resp_buf, u32 resp_buf_len); +int mbox_rsu_status(u32 *resp_buf, u32 resp_buf_len); +int mbox_rsu_status_psci(u32 *resp_buf, u32 resp_buf_len); +int mbox_rsu_update(u32 *flash_offset); +int mbox_rsu_update_psci(u32 *flash_offset); +int mbox_hps_stage_notify(u32 execution_stage); +int mbox_hps_stage_notify_psci(u32 execution_stage); int mbox_get_fpga_config_status(u32 cmd); int mbox_get_fpga_config_status_psci(u32 cmd); #endif /* _MAILBOX_S10_H_ */ diff --git a/arch/arm/mach-socfpga/mailbox_s10.c b/arch/arm/mach-socfpga/mailbox_s10.c index 4498ab5..f7c0f1b 100644 --- a/arch/arm/mach-socfpga/mailbox_s10.c +++ b/arch/arm/mach-socfpga/mailbox_s10.c @@ -9,6 +9,7 @@ #include #include #include +#include #include DECLARE_GLOBAL_DATA_PTR; @@ -342,6 +343,72 @@ int mbox_reset_cold(void) return 0; } +int mbox_rsu_get_spt_offset(u32 *resp_buf, u32 resp_buf_len) +{ + return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_GET_SUBPARTITION_TABLE, + MBOX_CMD_DIRECT, 0, NULL, 0, (u32 *)&resp_buf_len, + (u32 *)resp_buf); +} + +int mbox_rsu_status(u32 *resp_buf, u32 resp_buf_len) +{ + int ret; + struct rsu_status_info *info = (struct rsu_status_info *)resp_buf; + + info->retry_counter = -1; + + ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RSU_STATUS, MBOX_CMD_DIRECT, 0, + NULL, 0, (u32 *)&resp_buf_len, (u32 *)resp_buf); + + if (ret) + return ret; + + if (info->retry_counter != -1) + if (!RSU_VERSION_ACMF_VERSION(info->version)) + info->version |= FIELD_PREP(RSU_VERSION_ACMF_MASK, 1); + + return ret; +} + +int __secure mbox_rsu_status_psci(u32 *resp_buf, u32 resp_buf_len) +{ + int ret; + struct rsu_status_info *info = (struct rsu_status_info *)resp_buf; + int adjust = (resp_buf_len >= 9); + + if (adjust) + info->retry_counter = -1; + + ret = mbox_send_cmd_psci(MBOX_ID_UBOOT, MBOX_RSU_STATUS, + MBOX_CMD_DIRECT, 0, NULL, 0, + (u32 *)&resp_buf_len, (u32 *)resp_buf); + + if (ret) + return ret; + + if (!adjust) + return ret; + + if (info->retry_counter != -1) + if (!RSU_VERSION_ACMF_VERSION(info->version)) + info->version |= FIELD_PREP(RSU_VERSION_ACMF_MASK, 1); + + return ret; +} + +int mbox_rsu_update(u32 *flash_offset) +{ + return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RSU_UPDATE, MBOX_CMD_DIRECT, 2, + (u32 *)flash_offset, 0, 0, NULL); +} + +int __secure mbox_rsu_update_psci(u32 *flash_offset) +{ + return mbox_send_cmd_psci(MBOX_ID_UBOOT, MBOX_RSU_UPDATE, + MBOX_CMD_DIRECT, 2, (u32 *)flash_offset, + 0, 0, NULL); +} + /* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */ static __always_inline int mbox_get_fpga_config_status_common(u32 cmd) { @@ -380,6 +447,12 @@ static __always_inline int mbox_get_fpga_config_status_common(u32 cmd) return MBOX_CFGSTAT_STATE_CONFIG; } +int __secure mbox_hps_stage_notify_psci(u32 execution_stage) +{ + return mbox_send_cmd_psci(MBOX_ID_UBOOT, MBOX_HPS_STAGE_NOTIFY, + MBOX_CMD_DIRECT, 1, &execution_stage, 0, 0, NULL); +} + int mbox_get_fpga_config_status(u32 cmd) { return mbox_get_fpga_config_status_common(cmd); @@ -405,6 +478,12 @@ int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len, resp_buf_len, resp_buf); } +int mbox_hps_stage_notify(u32 execution_stage) +{ + return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_HPS_STAGE_NOTIFY, + MBOX_CMD_DIRECT, 1, &execution_stage, 0, 0, NULL); +} + int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg) { return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg); From patchwork Wed Oct 30 20:34:54 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Gong X-Patchwork-Id: 1186935 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.intel.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 473Kcf2pNNz9sPT for ; Thu, 31 Oct 2019 07:22:10 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 19B42C21EBF; Wed, 30 Oct 2019 20:22:08 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 6B772C21EC2; Wed, 30 Oct 2019 20:21:05 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id B7E9AC21EB9; Wed, 30 Oct 2019 20:20:41 +0000 (UTC) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by lists.denx.de (Postfix) with ESMTPS id AAB60C21E1E for ; Wed, 30 Oct 2019 20:20:36 +0000 (UTC) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 30 Oct 2019 13:20:36 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.68,248,1569308400"; d="scan'208";a="230582088" Received: from marshy.an.intel.com ([10.122.105.159]) by fmsmga002.fm.intel.com with ESMTP; 30 Oct 2019 13:20:35 -0700 From: richard.gong@linux.intel.com To: marex@denx.de, simon.k.r.goldschmidt@gmail.com, albert.u.boot@aribaud.net, u-boot@lists.denx.de Date: Wed, 30 Oct 2019 15:34:54 -0500 Message-Id: <1572467696-15358-3-git-send-email-richard.gong@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1572467696-15358-1-git-send-email-richard.gong@linux.intel.com> References: <1572467696-15358-1-git-send-email-richard.gong@linux.intel.com> Cc: chee.hong.ang@intel.com, radu.bacrau@intel.com, richard.gong@intel.com Subject: [U-Boot] [PATCHv2 2/4] drivers: firmware: add RSU support for Stratix10 SoC X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Richard Gong 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 Signed-off-by: Richard Gong --- 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 +#include +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include + +/* 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 +#include + +/** + * 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 + +/* + * 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 From patchwork Wed Oct 30 20:34:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Gong X-Patchwork-Id: 1186934 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.intel.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 473Kbf3mlyz9sPT for ; Thu, 31 Oct 2019 07:21:18 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 38673C21F05; Wed, 30 Oct 2019 20:21:04 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id DD9F1C21EBD; Wed, 30 Oct 2019 20:20:47 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 171CBC21E77; Wed, 30 Oct 2019 20:20:41 +0000 (UTC) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by lists.denx.de (Postfix) with ESMTPS id DF91EC21EA7 for ; Wed, 30 Oct 2019 20:20:38 +0000 (UTC) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 30 Oct 2019 13:20:38 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.68,248,1569308400"; d="scan'208";a="230582102" Received: from marshy.an.intel.com ([10.122.105.159]) by fmsmga002.fm.intel.com with ESMTP; 30 Oct 2019 13:20:37 -0700 From: richard.gong@linux.intel.com To: marex@denx.de, simon.k.r.goldschmidt@gmail.com, albert.u.boot@aribaud.net, u-boot@lists.denx.de Date: Wed, 30 Oct 2019 15:34:55 -0500 Message-Id: <1572467696-15358-4-git-send-email-richard.gong@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1572467696-15358-1-git-send-email-richard.gong@linux.intel.com> References: <1572467696-15358-1-git-send-email-richard.gong@linux.intel.com> Cc: chee.hong.ang@intel.com, Chin Liang See , radu.bacrau@intel.com, richard.gong@intel.com Subject: [U-Boot] [PATCHv2 3/4] dirvers: firmware: add console commands for RSU support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Richard Gong The Intel Remote System Update (RSU) provides a way for users to update the QSPI configuration bitstream of a Intel Stratix 10 SoC device with significantly reduced risk of corrupting the bitstream storage and bricking the system. This patch provides console commands to exercises the RSU APIs at Intel Stratix10 SoC. The commands use the term "slot" to refer to a sub-partition which is intended to contain a production image, and the term "priority" to refer to the fact that the images are loaded by firmware in the defined order. The provided console commands are below, you can also refer to the header file rsu.h for the details of RSU APIs: slot_count slot_by_name slot_get_info slot_size slot_priority slot_erase slot_program_buf slot_program_buf_raw slot_verify_buf slot_verify_buf_raw slot_enable slot_disable slot_load slot_load_factory slot_rename status_log list dtb update notify clear_error_status reset_retry_counter Signed-off-by: Chin Liang See Signed-off-by: Radu Bacrau Signed-off-by: Richard Gong --- drivers/firmware/Makefile | 2 +- drivers/firmware/rsu_s10.c | 874 +++++++++++++++++++++++++++++++++++++++++++++ include/intel/rsu_s10.h | 46 +++ 3 files changed, 921 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/rsu_s10.c create mode 100644 include/intel/rsu_s10.h diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 97912ea..0b915c5 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -2,4 +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 +obj-$(CONFIG_INTEL_RSU) += rsu.o rsu_ll_qspi.o rsu_misc.o rsu_s10.o diff --git a/drivers/firmware/rsu_s10.c b/drivers/firmware/rsu_s10.c new file mode 100644 index 0000000..2e508c1 --- /dev/null +++ b/drivers/firmware/rsu_s10.c @@ -0,0 +1,874 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Intel Corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct socfpga_rsu_s10_cpb rsu_cpb = {0}; +struct socfpga_rsu_s10_spt rsu_spt = {0}; +u32 rsu_spt0_offset = 0, rsu_spt1_offset = 0; + +static int initialized; + +static int rsu_print_status(void) +{ + struct rsu_status_info status_info; + + if (mbox_rsu_status((u32 *)&status_info, sizeof(status_info) / 4)) { + puts("RSU: Firmware or flash content not supporting RSU\n"); + return -ENOTSUPP; + } + puts("RSU: Remote System Update Status\n"); + printf("Current Image\t: 0x%08llx\n", status_info.current_image); + printf("Last Fail Image\t: 0x%08llx\n", status_info.fail_image); + printf("State\t\t: 0x%08x\n", status_info.state); + printf("Version\t\t: 0x%08x\n", status_info.version); + printf("Error location\t: 0x%08x\n", status_info.error_location); + printf("Error details\t: 0x%08x\n", status_info.error_details); + if (RSU_VERSION_ACMF_VERSION(status_info.version) && + RSU_VERSION_DCMF_VERSION(status_info.version)) + printf("Retry counter\t: 0x%08x\n", status_info.retry_counter); + + return 0; +} + +static void rsu_print_spt_slot(void) +{ + int i; + + puts("RSU: Sub-partition table content\n"); + for (i = 0; i < rsu_spt.entries; i++) { + printf("%16s\tOffset:0x%08x%08x\tLength:0x%08x\tFlag:0x%08x\n", + rsu_spt.spt_slot[i].name, + rsu_spt.spt_slot[i].offset[1], + rsu_spt.spt_slot[i].offset[0], + rsu_spt.spt_slot[i].length, + rsu_spt.spt_slot[i].flag); + } +} + +static void rsu_print_cpb_slot(void) +{ + int i, j = 1; + + puts("RSU: CMF pointer block's image pointer list\n"); + for (i = rsu_cpb.nslots - 1; i >= 0; i--) { + if (rsu_cpb.pointer_slot[i] != ~0 && + rsu_cpb.pointer_slot[i] != 0) { + printf("Priority %d Offset: 0x%016llx nslot: %d\n", + j, rsu_cpb.pointer_slot[i], i); + j++; + } + } +} + +static u32 rsu_spt_slot_find_cpb(void) +{ + int i; + + for (i = 0; i < rsu_spt.entries; i++) { + if (strstr(rsu_spt.spt_slot[i].name, "CPB0")) + return rsu_spt.spt_slot[i].offset[0]; + } + puts("RSU: Cannot find SPT0 entry from sub-partition table\n"); + return 0; +} + +static u32 rsu_get_boot_part_len(void) +{ + int i; + u32 offset = 0, len = 0; + + /* look for last entry that has largest offset */ + for (i = 0; i < rsu_spt.entries; i++) { + if (rsu_spt.spt_slot[i].offset[0] > offset) { + offset = rsu_spt.spt_slot[i].offset[0]; + len = rsu_spt.spt_slot[i].length; + } + } + + /* With the len, we shall know the boot partition size */ + len += offset; + return roundup(len, 64 << 10); /* align to 64kB, flash sector size */ +} + +int rsu_spt_cpb_list(int argc, char * const argv[]) +{ + u32 spt_offset[4]; + u32 cpb_offset; + int err; + struct spi_flash *flash; + + if (argc != 1) + return CMD_RET_USAGE; + + /* print the RSU status */ + err = rsu_print_status(); + if (err) + return err; + + /* retrieve the sub-partition table (spt) offset from firmware */ + if (mbox_rsu_get_spt_offset(spt_offset, 4)) { + puts("RSU: Error from mbox_rsu_get_spt_offset\n"); + return -ECOMM; + } + rsu_spt0_offset = spt_offset[SPT0_INDEX]; + rsu_spt1_offset = spt_offset[SPT1_INDEX]; + + /* update into U-Boot env so we can update into DTS later */ + env_set_hex("rsu_sbt0", rsu_spt0_offset); + env_set_hex("rsu_sbt1", rsu_spt1_offset); + printf("RSU: Sub-partition table 0 offset 0x%08x\n", rsu_spt0_offset); + printf("RSU: Sub-partition table 1 offset 0x%08x\n", rsu_spt1_offset); + + /* retrieve sub-partition table (spt) from flash */ + flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, + CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, + CONFIG_SF_DEFAULT_MODE); + if (!flash) { + puts("RSU: SPI probe failed.\n"); + return -ENODEV; + } + if (spi_flash_read(flash, rsu_spt0_offset, sizeof(rsu_spt), &rsu_spt)) { + puts("RSU: spi_flash_read failed\n"); + return -EIO; + } + + /* valid the sub-partition table (spt) magic number */ + if (rsu_spt.magic_number != RSU_S10_SPT_MAGIC_NUMBER) { + printf("RSU: SPT magic number not match 0x%08x\n", + rsu_spt.magic_number); + return -EFAULT; + } + + /* list the sub-partition table (spt) content */ + rsu_print_spt_slot(); + + /* locate where is CMF pointer block (cpb) */ + cpb_offset = rsu_spt_slot_find_cpb(); + if (!cpb_offset) + return -ENXIO; + printf("RSU: CMF pointer block offset 0x%08x\n", cpb_offset); + + /* retrieve CMF pointer block (cpb) from flash */ + if (spi_flash_read(flash, cpb_offset, sizeof(rsu_cpb), &rsu_cpb)) { + puts("RSU: spi_flash_read failed\n"); + return -EIO; + } + + /* valid the CMF pointer block (cpb) magic number */ + if (rsu_cpb.magic_number != RSU_S10_CPB_MAGIC_NUMBER) { + printf("RSU: CMF pointer block magic number not match 0x%08x\n", + rsu_cpb.magic_number); + return -EFAULT; + } + + /* list the CMF pointer block (cpb) content */ + rsu_print_cpb_slot(); + + return 0; +} + +int rsu_update(int argc, char * const argv[]) +{ + u32 flash_offset[2]; + u64 addr; + char *endp; + + if (argc != 2) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[1], &endp, 16); + + flash_offset[0] = lower_32_bits(addr); + flash_offset[1] = upper_32_bits(addr); + + printf("RSU: RSU update to 0x%08x%08x\n", + flash_offset[1], flash_offset[0]); + mbox_rsu_update(flash_offset); + return 0; +} + +int rsu_dtb(int argc, char * const argv[]) +{ + char flash0_string[100]; + const char *fdt_flash0; + int nodeoffset, len; + u32 reg[2]; + int err; + + /* Extracting RSU info from bitstream */ + err = rsu_spt_cpb_list(argc, argv); + if (err == -ENOTSUPP) + return 0; + else if (err) + return err; + + /* Extract the flash0's reg from Linux DTB */ + nodeoffset = fdt_path_offset(working_fdt, "/__symbols__"); + if (nodeoffset < 0) { + puts("DTB: __symbols__ node not found."); + puts(" Ensure you load kernel dtb and fdt addr\n"); + return -ENODEV; + } + fdt_flash0 = fdt_getprop(working_fdt, nodeoffset, "qspi_boot", &len); + if (!fdt_flash0) { + puts("DTB: qspi_boot alias node not found. Check your dts\n"); + return -ENODEV; + } + strcpy(flash0_string, fdt_flash0); + printf("DTB: qspi_boot node at %s\n", flash0_string); + + /* assemble new reg value for boot partition */ + len = rsu_get_boot_part_len(); + reg[0] = cpu_to_fdt32(rsu_spt0_offset); + reg[1] = cpu_to_fdt32(len - rsu_spt0_offset); + + /* update back to Linux DTB */ + nodeoffset = fdt_path_offset(working_fdt, flash0_string); + if (nodeoffset < 0) { + printf("DTB: %s node not found\n", flash0_string); + return -ENODEV; + } + return fdt_setprop(working_fdt, nodeoffset, "reg", reg, sizeof(reg)); +} + +static int slot_count(int argc, char * const argv[]) +{ + int count; + + if (argc != 1) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + count = rsu_slot_count(); + if (count < 0) + return CMD_RET_FAILURE; + + printf("Number of slots = %d.\n", count); + + return CMD_RET_SUCCESS; +} + +static int slot_by_name(int argc, char * const argv[]) +{ + char *name = argv[1]; + int slot; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = rsu_slot_by_name(name); + if (slot < 0) + return CMD_RET_FAILURE; + + printf("Slot name '%s' is %d.\n", name, slot); + return CMD_RET_SUCCESS; +} + +static int slot_get_info(int argc, char * const argv[]) +{ + int slot; + char *endp; + struct rsu_slot_info info; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + ret = rsu_slot_get_info(slot, &info); + if (ret) + return CMD_RET_FAILURE; + + printf("NAME: %s\n", info.name); + printf("OFFSET: 0x%016llX\n", info.offset); + printf("SIZE: 0x%08X\n", info.size); + if (info.priority) + printf("PRIORITY: %i\n", info.priority); + else + printf("PRIORITY: [disabled]\n"); + + return CMD_RET_SUCCESS; +} + +static int slot_size(int argc, char * const argv[]) +{ + int slot; + char *endp; + int size; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + size = rsu_slot_size(slot); + if (size < 0) + return CMD_RET_FAILURE; + + printf("Slot %d size = %d.\n", slot, size); + return CMD_RET_SUCCESS; +} + +static int slot_priority(int argc, char * const argv[]) +{ + int slot; + char *endp; + int priority; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + priority = rsu_slot_priority(slot); + if (priority < 0) + return CMD_RET_FAILURE; + + printf("Slot %d priority = %d.\n", slot, priority); + return CMD_RET_SUCCESS; +} + +static int slot_erase(int argc, char * const argv[]) +{ + int slot; + char *endp; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + ret = rsu_slot_erase(slot); + if (ret) + return CMD_RET_FAILURE; + + printf("Slot %d erased.\n", slot); + return CMD_RET_SUCCESS; +} + +static int slot_program_buf(int argc, char * const argv[]) +{ + int slot; + char *endp; + u64 address; + int size; + int ret; + int addr_lo; + int addr_hi; + + if (argc != 4) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + address = simple_strtoul(argv[2], &endp, 16); + size = simple_strtoul(argv[3], &endp, 16); + + ret = rsu_slot_program_buf(slot, (void *)address, size); + if (ret) + return CMD_RET_FAILURE; + + addr_hi = upper_32_bits(address); + addr_lo = lower_32_bits(address); + printf("Slot %d was programmed with buffer=0x%08x%08x size=%d.\n", + slot, addr_hi, addr_lo, size); + + return CMD_RET_SUCCESS; +} + +static int slot_program_factory_update_buf(int argc, char * const argv[]) +{ + int slot; + char *endp; + u64 address; + int size; + int ret; + int addr_lo; + int addr_hi; + + if (argc != 4) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + address = simple_strtoul(argv[2], &endp, 16); + size = simple_strtoul(argv[3], &endp, 16); + + ret = rsu_slot_program_factory_update_buf(slot, (void *)address, size); + if (ret) + return CMD_RET_FAILURE; + + addr_hi = upper_32_bits(address); + addr_lo = lower_32_bits(address); + printf("Slot %d was programmed with buffer=0x%08x%08x size=%d.\n", + slot, addr_hi, addr_lo, size); + + return CMD_RET_SUCCESS; +} + +static int slot_program_buf_raw(int argc, char * const argv[]) +{ + int slot; + char *endp; + u64 address; + int size; + int ret; + int addr_lo; + int addr_hi; + + if (argc != 4) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + address = simple_strtoul(argv[2], &endp, 16); + size = simple_strtoul(argv[3], &endp, 16); + + ret = rsu_slot_program_buf_raw(slot, (void *)address, size); + if (ret) + return CMD_RET_FAILURE; + + addr_hi = upper_32_bits(address); + addr_lo = lower_32_bits(address); + printf("Slot %d was programmed with raw buffer=0x%08x%08x size=%d.\n", + slot, addr_hi, addr_lo, size); + + return CMD_RET_SUCCESS; +} + +static int slot_verify_buf(int argc, char * const argv[]) +{ + int slot; + char *endp; + u64 address; + int size; + int ret; + int addr_lo; + int addr_hi; + + if (argc != 4) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + address = simple_strtoul(argv[2], &endp, 16); + size = simple_strtoul(argv[3], &endp, 16); + + ret = rsu_slot_verify_buf(slot, (void *)address, size); + if (ret) + return CMD_RET_FAILURE; + + addr_hi = upper_32_bits(address); + addr_lo = lower_32_bits(address); + printf("Slot %d was verified with buffer=0x%08x%08x size=%d.\n", + slot, addr_hi, addr_lo, size); + + return CMD_RET_SUCCESS; +} + +static int slot_verify_buf_raw(int argc, char * const argv[]) +{ + int slot; + char *endp; + u64 address; + int size; + int ret; + int addr_lo; + int addr_hi; + + if (argc != 4) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + address = simple_strtoul(argv[2], &endp, 16); + size = simple_strtoul(argv[3], &endp, 16); + + ret = rsu_slot_verify_buf_raw(slot, (void *)address, size); + if (ret) + return CMD_RET_FAILURE; + + addr_hi = upper_32_bits(address); + addr_lo = lower_32_bits(address); + printf("Slot %d was verified with raw buffer=0x%08x%08x size=%d.\n", + slot, addr_hi, addr_lo, size); + + return CMD_RET_SUCCESS; +} + +static int slot_enable(int argc, char * const argv[]) +{ + int slot; + char *endp; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + ret = rsu_slot_enable(slot); + if (ret < 0) + return CMD_RET_FAILURE; + + printf("Slot %d enabled.\n", slot); + return CMD_RET_SUCCESS; +} + +static int slot_disable(int argc, char * const argv[]) +{ + int slot; + char *endp; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + ret = rsu_slot_disable(slot); + if (ret < 0) + return CMD_RET_FAILURE; + + printf("Slot %d disabled.\n", slot); + return CMD_RET_SUCCESS; +} + +static int slot_load(int argc, char * const argv[]) +{ + int slot; + char *endp; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + ret = rsu_slot_load(slot); + if (ret < 0) + return CMD_RET_FAILURE; + + printf("Slot %d loading.\n", slot); + return CMD_RET_SUCCESS; +} + +static int slot_load_factory(int argc, char * const argv[]) +{ + int ret; + + if (argc != 1) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + ret = rsu_slot_load_factory(); + if (ret < 0) + return CMD_RET_FAILURE; + + printf("Factory loading.\n"); + return CMD_RET_SUCCESS; +} + +static int slot_rename(int argc, char * const argv[]) +{ + int slot; + char *endp; + char *name; + int ret; + + if (argc != 3) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + slot = simple_strtoul(argv[1], &endp, 16); + name = argv[2]; + + ret = rsu_slot_rename(slot, name); + if (ret < 0) + return CMD_RET_FAILURE; + + printf("Slot %d renamed to %s.\n", slot, name); + return CMD_RET_SUCCESS; +} + +static int status_log(int argc, char * const argv[]) +{ + struct rsu_status_info info; + int ret; + + if (argc != 1) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + ret = rsu_status_log(&info); + if (ret < 0) + return CMD_RET_FAILURE; + + printf("Current Image\t: 0x%08llx\n", info.current_image); + printf("Last Fail Image\t: 0x%08llx\n", info.fail_image); + printf("State\t\t: 0x%08x\n", info.state); + printf("Version\t\t: 0x%08x\n", info.version); + printf("Error location\t: 0x%08x\n", info.error_location); + printf("Error details\t: 0x%08x\n", info.error_details); + if (info.version) + printf("Retry counter\t: 0x%08x\n", info.retry_counter); + + return CMD_RET_SUCCESS; +} + +static int notify(int argc, char * const argv[]) +{ + u32 stage; + char *endp; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + stage = simple_strtoul(argv[1], &endp, 16); + ret = rsu_notify(stage); + if (ret) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +static int clear_error_status(int argc, char * const argv[]) +{ + int ret; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + ret = rsu_clear_error_status(); + if (ret) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +static int reset_retry_counter(int argc, char * const argv[]) +{ + int ret; + + if (!initialized) { + if (rsu_init(NULL)) + return CMD_RET_FAILURE; + + initialized = 1; + } + + ret = rsu_reset_retry_counter(); + if (ret) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +struct func_t { + const char *cmd_string; + int (*func_ptr)(int cmd_argc, char * const cmd_argv[]); +}; + +static const struct func_t rsu_func_t[] = { + {"dtb", rsu_dtb}, + {"list", rsu_spt_cpb_list}, + {"slot_by_name", slot_by_name}, + {"slot_count", slot_count}, + {"slot_disable", slot_disable}, + {"slot_enable", slot_enable}, + {"slot_erase", slot_erase}, + {"slot_get_info", slot_get_info}, + {"slot_load", slot_load}, + {"slot_load_factory", slot_load_factory}, + {"slot_priority", slot_priority}, + {"slot_program_buf", slot_program_buf}, + {"slot_program_buf_raw", slot_program_buf_raw}, + {"slot_program_factory_update_buf", slot_program_factory_update_buf}, + {"slot_rename", slot_rename}, + {"slot_size", slot_size}, + {"slot_verify_buf", slot_verify_buf}, + {"slot_verify_buf_raw", slot_verify_buf_raw}, + {"status_log", status_log}, + {"update", rsu_update}, + {"notify", notify}, + {"clear_error_status", clear_error_status}, + {"reset_retry_counter", reset_retry_counter} +}; + +int do_rsu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const char *cmd; + int i; + + if (argc < 2) + return CMD_RET_USAGE; + + cmd = argv[1]; + --argc; + ++argv; + + for (i = 0; i < ARRAY_SIZE(rsu_func_t); i++) { + if (!strcmp(cmd, rsu_func_t[i].cmd_string)) + return rsu_func_t[i].func_ptr(argc, argv); + } + + return CMD_RET_USAGE; +} + +U_BOOT_CMD(rsu, 5, 1, do_rsu, + "SoCFPGA Stratix10 SoC Remote System Update", + "dtb - Update Linux DTB qspi-boot parition offset with spt0 value\n" + "list - List down the available bitstreams in flash\n" + "slot_by_name - find slot by name and display the slot number\n" + "slot_count - display the slot count\n" + "slot_disable - remove slot from CPB\n" + "slot_enable - make slot the highest priority\n" + "slot_erase - erase slot\n" + "slot_get_info - display slot information\n" + "slot_load - load slot immediately\n" + "slot_load_factory - load factory immediately\n" + "slot_priority - display slot priority\n" + "slot_program_buf - program buffer into slot, and make it highest priority\n" + "slot_program_buf_raw - program raw buffer into slot\n" + "slot_program_factory_update_buf - program factory update buffer into slot, and make it highest priority\n" + "slot_rename - rename slot\n" + "slot_size - display slot size\n" + "slot_verify_buf - verify slot contents against buffer\n" + "slot_verify_buf_raw - verify slot contents against raw buffer\n" + "status_log - display RSU status\n" + "update - Initiate firmware to load bitstream as specified by flash_offset\n" + "notify - Let SDM know the current state of HPS software\n" + "clear_error_status - clear the RSU error status\n" + "reset_retry_counter - reset the RSU retry counter\n" + "" +); diff --git a/include/intel/rsu_s10.h b/include/intel/rsu_s10.h new file mode 100644 index 0000000..cd50b65 --- /dev/null +++ b/include/intel/rsu_s10.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 Intel Corporation + * + */ +#ifndef _RSU_S10_H_ +#define _RSU_S10_H_ + +extern u32 smc_rsu_update_address; + +#define RSU_S10_CPB_MAGIC_NUMBER 0x57789609 +#define RSU_S10_SPT_MAGIC_NUMBER 0x57713427 + +#define SPT0_INDEX 1 +#define SPT1_INDEX 3 + +/* CMF pointer block */ +struct socfpga_rsu_s10_cpb { + u32 magic_number; + u32 header_size; + u32 total_size; + u32 reserved1; + u32 iptab_offset; + u32 nslots; + u32 reserved2; + u64 pointer_slot[508]; +}; + +/* sub partition slot */ +struct socfpga_rsu_s10_spt_slot { + char name[16]; + u32 offset[2]; + u32 length; + u32 flag; +}; + +/* sub partition table */ +struct socfpga_rsu_s10_spt { + u32 magic_number; + u32 version; + u32 entries; + u32 reserved[5]; + struct socfpga_rsu_s10_spt_slot spt_slot[127]; +}; + +#endif /* _RSU_S10_H_ */ From patchwork Wed Oct 30 20:34:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Richard Gong X-Patchwork-Id: 1186936 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.intel.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 473KfH0RPhz9sPT for ; Thu, 31 Oct 2019 07:23:34 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id AA165C21EB9; Wed, 30 Oct 2019 20:23:30 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 675FCC21E7F; Wed, 30 Oct 2019 20:21:13 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 7B0ECC21E52; Wed, 30 Oct 2019 20:20:41 +0000 (UTC) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by lists.denx.de (Postfix) with ESMTPS id 4023DC21EB9 for ; Wed, 30 Oct 2019 20:20:41 +0000 (UTC) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 30 Oct 2019 13:20:40 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.68,248,1569308400"; d="scan'208";a="230582116" Received: from marshy.an.intel.com ([10.122.105.159]) by fmsmga002.fm.intel.com with ESMTP; 30 Oct 2019 13:20:40 -0700 From: richard.gong@linux.intel.com To: marex@denx.de, simon.k.r.goldschmidt@gmail.com, albert.u.boot@aribaud.net, u-boot@lists.denx.de Date: Wed, 30 Oct 2019 15:34:56 -0500 Message-Id: <1572467696-15358-5-git-send-email-richard.gong@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1572467696-15358-1-git-send-email-richard.gong@linux.intel.com> References: <1572467696-15358-1-git-send-email-richard.gong@linux.intel.com> MIME-Version: 1.0 Cc: chee.hong.ang@intel.com, radu.bacrau@intel.com, richard.gong@intel.com Subject: [U-Boot] [PATCHv2 4/4] arm: socfpga: stratix10: add environment variables for RSU support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Richard Gong Add two RSU environment variables: 1. rsu_log_level the variable is unsigned integer and its default value is RSU_DEBUG (7), which only show log with RSU_INFO,RSU_WARNING and RSU_ERR. To enable all logs (RSU_ERR, RSU_WARNING, RSU_INFO and RSU_DEBUG), you need set log level to 8 or above via “setenv rsu_log_level 8”. To disable all logs, you need set log level to 3 or below. 2. rsu_protected_slot by default there is no protected RSU slot, you need run "setenv rsu_protected_slot ” to set a slot protected, and “setenv rsu_protected_slot ” to unset a protected slot. Signed-off-by: Richard Gong --- arch/arm/mach-socfpga/misc_s10.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/mach-socfpga/misc_s10.c b/arch/arm/mach-socfpga/misc_s10.c index 0a5fab1..c9a6f00 100644 --- a/arch/arm/mach-socfpga/misc_s10.c +++ b/arch/arm/mach-socfpga/misc_s10.c @@ -21,6 +21,8 @@ #include +#define RSU_DEFAULT_LOG_LEVEL 7 + DECLARE_GLOBAL_DATA_PTR; static struct socfpga_system_manager *sysmgr_regs = @@ -136,10 +138,17 @@ int print_cpuinfo(void) int arch_misc_init(void) { char qspi_string[13]; + char level[4]; + + snprintf(level, sizeof(level), "%u", RSU_DEFAULT_LOG_LEVEL); sprintf(qspi_string, "<0x%08x>", cm_get_qspi_controller_clk_hz()); env_set("qspi_clock", qspi_string); + /* setup for RSU */ + env_set("rsu_protected_slot", ""); + env_set("rsu_log_level", level); + socfpga_set_phymode(); return 0; }