diff options
author | Neil DI FOLCO <neil.difolco@qorvo.com> | 2022-12-08 16:19:46 +0100 |
---|---|---|
committer | Neil DI FOLCO <neil.difolco@qorvo.com> | 2022-12-14 19:12:30 +0100 |
commit | d900fbab259e458be76aa401473496574b121991 (patch) | |
tree | 746d2b15520acc93079ed42a68320612ea94ea20 | |
parent | 7489e25a9675becbb01e95794e44bd47402fb7cc (diff) | |
download | uwb-d900fbab259e458be76aa401473496574b121991.tar.gz |
[R-5.2] uwb: update qm35 driver to release R5.2
Add C0 support
Change-Id: Ic1f43522c04df3877695a48cedfb1a22a1b2eacc
Signed-off-by: Neil DI FOLCO <neil.difolco@qorvo.com>
-rw-r--r-- | Kbuild | 6 | ||||
-rw-r--r-- | debug.c | 156 | ||||
-rw-r--r-- | debug.h | 9 | ||||
-rw-r--r-- | hsspi_log.c | 10 | ||||
-rw-r--r-- | libqmrom/CMakeLists.txt | 6 | ||||
-rw-r--r-- | libqmrom/include/byteswap.h | 10 | ||||
-rw-r--r-- | libqmrom/include/qmrom.h | 152 | ||||
-rw-r--r-- | libqmrom/include/qmrom_spi.h | 6 | ||||
-rw-r--r-- | libqmrom/include/qmrom_utils.h | 7 | ||||
-rw-r--r-- | libqmrom/include/spi_rom_protocol.h | 140 | ||||
-rw-r--r-- | libqmrom/src/qmrom.c | 879 | ||||
-rw-r--r-- | libqmrom/src/qmrom_a0.c | 240 | ||||
-rw-r--r-- | libqmrom/src/qmrom_b0.c | 384 | ||||
-rw-r--r-- | libqmrom/src/qmrom_c0.c | 442 | ||||
-rw-r--r-- | libqmrom/src/qmrom_common.c | 421 | ||||
-rw-r--r-- | libqmrom/src/spi_rom_protocol.c | 166 | ||||
-rw-r--r-- | qm35-spi.c | 83 | ||||
-rw-r--r-- | qm35.h | 1 | ||||
-rw-r--r-- | qmrom_spi.c | 28 |
19 files changed, 1794 insertions, 1352 deletions
@@ -6,9 +6,11 @@ qm35-y := \ qm35-spi.o \ qm35_rb.o \ qmrom_spi.o \ - libqmrom/src/qmrom.o \ + libqmrom/src/qmrom_common.o \ + libqmrom/src/qmrom_a0.o \ + libqmrom/src/qmrom_b0.o \ + libqmrom/src/qmrom_c0.o \ libqmrom/src/qmrom_log.o \ - libqmrom/src/spi_rom_protocol.o \ hsspi.o \ hsspi_uci.o \ hsspi_log.o \ @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 - /* * This file is part of the QM35 UCI stack for linux. * @@ -268,78 +267,6 @@ static ssize_t debug_coredump_write(struct file *filp, const char __user *buff, return count; } -static int debug_debug_certificate_open(struct inode *inodep, struct file *filep) -{ - struct debug *debug = priv_from_file(filep); - - if (debug->certificate != NULL) - return -EBUSY; - - debug->certificate = kmalloc(sizeof(*debug->certificate) + DEBUG_CERTIFICATE_SIZE, - GFP_KERNEL); - if (debug->certificate == NULL) - return -ENOMEM; - - return 0; -} - -static int debug_debug_certificate_close(struct inode *inodep, struct file *filep) -{ - struct debug *debug = priv_from_file(filep); - struct qm35_ctx *qm35_hdl; - int ret; - const char *operation = filep->f_pos ? "flashing" : "erasing"; - - qm35_hdl = container_of(debug, struct qm35_ctx, debug); - - qm35_hsspi_stop(qm35_hdl); - - dev_dbg(&qm35_hdl->spi->dev, "%s debug certificate (%lld bytes)\n", operation, - filep->f_pos); - - if (filep->f_pos) { - - debug->certificate->size = filep->f_pos; - - } else { - - /* WA: qmrom_erase_dbg_cert is not working, waiting to find the root - * cause, workaround is to write a zeroed certificate - * ret = qmrom_erase_dbg_cert(qm35_hdl->spi, qmrom_spi_reset_device, qm35_hdl); - */ - debug->certificate->size = DEBUG_CERTIFICATE_SIZE; - memset(debug->certificate->data, 0, DEBUG_CERTIFICATE_SIZE); - } - - ret = qmrom_flash_dbg_cert(qm35_hdl->spi, debug->certificate, - qmrom_spi_reset_device, qm35_hdl); - - if (ret) - dev_err(&qm35_hdl->spi->dev, "%s debug certificate fails: %d\n", operation, ret); - else - dev_info(&qm35_hdl->spi->dev, "%s debug certificate success\n", operation); - - msleep(QM_BEFORE_RESET_MS); - qm35_reset(qm35_hdl, QM_RESET_LOW_MS); - msleep(QM_BOOT_MS); - - qm35_hsspi_start(qm35_hdl); - - kfree(debug->certificate); - debug->certificate = NULL; - - return 0; -} - -static ssize_t debug_debug_certificate_write(struct file *filp, - const char __user *buff, size_t count, loff_t *off) -{ - struct debug *debug = priv_from_file(filp); - - return simple_write_to_buffer(debug->certificate->data, - DEBUG_CERTIFICATE_SIZE, off, buff, count); -} - static const struct file_operations debug_enable_fops = { .owner = THIS_MODULE, .write = debug_enable_write, @@ -372,13 +299,6 @@ static const struct file_operations debug_coredump_fops = { .write = debug_coredump_write, }; -static const struct file_operations debug_debug_certificate_fops = { - .owner = THIS_MODULE, - .open = debug_debug_certificate_open, - .write = debug_debug_certificate_write, - .release = debug_debug_certificate_close, -}; - int debug_create_module_entry(struct debug *debug, struct log_module *log_module) { @@ -417,6 +337,21 @@ void debug_new_trace_available(struct debug *debug) static int debug_devid_show(struct seq_file *s, void *unused) { struct debug *debug = (struct debug *)s->private; + uint16_t dev_id; + int rc; + + if (debug->trace_ops && debug->trace_ops->get_dev_id) { + rc = debug->trace_ops->get_dev_id(debug, &dev_id); + if (rc < 0) + return -EIO; + seq_printf(s, "deca%04x\n", dev_id); + } + return 0; +} + +static int debug_socid_show(struct seq_file *s, void *unused) +{ + struct debug *debug = (struct debug *)s->private; uint8_t soc_id[ROM_SOC_ID_LEN]; int rc; @@ -430,21 +365,57 @@ static int debug_devid_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(debug_devid); +DEFINE_SHOW_ATTRIBUTE(debug_socid); -int debug_init(struct debug *debug, struct dentry *root) +void debug_soc_info_available(struct debug *debug) { struct dentry *file; - init_waitqueue_head(&debug->wq); - mutex_init(&debug->pv_filp_lock); - debug->pv_filp = NULL; + debug->chip_dir = debugfs_create_dir("chip", debug->root_dir); + if (!debug->chip_dir) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/chip\n"); + goto unregister; + } + + file = debugfs_create_file("dev_id", 0444, debug->chip_dir, debug, + &debug_devid_fops); + if (!file) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/dev_id\n"); + goto unregister; + } + + file = debugfs_create_file("soc_id", 0444, debug->chip_dir, debug, + &debug_socid_fops); + if (!file) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/soc_id\n"); + goto unregister; + } + return; + +unregister: + debugfs_remove_recursive(debug->chip_dir); +} + +int debug_init_root(struct debug *debug, struct dentry *root) +{ debug->root_dir = debugfs_create_dir("uwb0", root); if (!debug->root_dir) { pr_err("qm35: failed to create /sys/kernel/debug/uwb0\n"); - goto unregister; + return -1; } + return 0; +} + +int debug_init(struct debug *debug) +{ + struct dentry *file; + + init_waitqueue_head(&debug->wq); + mutex_init(&debug->pv_filp_lock); + debug->pv_filp = NULL; + debug->fw_dir = debugfs_create_dir("fw", debug->root_dir); if (!debug->fw_dir) { pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw\n"); @@ -472,21 +443,6 @@ int debug_init(struct debug *debug, struct dentry *root) goto unregister; } - file = debugfs_create_file("devid", 0444, debug->fw_dir, debug, - &debug_devid_fops); - if (!file) { - pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/devid\n"); - goto unregister; - } - - debug->certificate = NULL; - file = debugfs_create_file("debug_certificate", 0200, debug->fw_dir, debug, - &debug_debug_certificate_fops); - if (!file) { - pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/debug_certificate\n"); - goto unregister; - } - file = debugfs_create_file("test_sleep_hsspi_ms", 0200, debug->fw_dir, debug, &debug_test_hsspi_sleep_fops); if (!file) { @@ -46,6 +46,7 @@ struct debug_trace_ops { rb_entry_size_t (*trace_get_next_size)(struct debug *dbg); bool (*trace_next_avail)(struct debug *dbg); void (*trace_reset)(struct debug *dbg); + int (*get_dev_id)(struct debug *dbg, uint16_t *dev_id); int (*get_soc_id)(struct debug *dbg, uint8_t *soc_id); }; @@ -57,12 +58,13 @@ struct debug_coredump_ops { struct debug { struct dentry *root_dir; struct dentry *fw_dir; + struct dentry *chip_dir; const struct debug_trace_ops *trace_ops; const struct debug_coredump_ops *coredump_ops; struct wait_queue_head wq; struct file *pv_filp; struct mutex pv_filp_lock; - struct binary_chunk *certificate; + struct firmware *certificate; }; // TODO move this from here to a commom place for both log layer and debug @@ -74,7 +76,8 @@ struct log_module { struct debug *debug; }; -int debug_init(struct debug *debug, struct dentry *root); +int debug_init_root(struct debug *debug, struct dentry *root); +int debug_init(struct debug *debug); void debug_deinit(struct debug *debug); int debug_create_module_entry(struct debug *debug, @@ -82,4 +85,6 @@ int debug_create_module_entry(struct debug *debug, void debug_new_trace_available(struct debug *debug); +void debug_soc_info_available(struct debug *debug); + #endif // __DEBUG_H__ diff --git a/hsspi_log.c b/hsspi_log.c index e9575f7..9a4541d 100644 --- a/hsspi_log.c +++ b/hsspi_log.c @@ -404,6 +404,15 @@ static void log_trace_reset(struct debug *dbg) rb_reset(&qm35_hdl->log_layer.rb); } +static int get_dev_id(struct debug *dbg, uint16_t *dev_id) +{ + struct qm35_ctx *qm35_hdl; + + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + return qm_get_dev_id(qm35_hdl, dev_id); +} + static int get_soc_id(struct debug *dbg, uint8_t *soc_id) { struct qm35_ctx *qm35_hdl; @@ -422,6 +431,7 @@ static const struct debug_trace_ops debug_trace_ops = { .trace_get_next_size = log_trace_get_next_size, .trace_next_avail = log_trace_next_avail, .trace_reset = log_trace_reset, + .get_dev_id = get_dev_id, .get_soc_id = get_soc_id, }; diff --git a/libqmrom/CMakeLists.txt b/libqmrom/CMakeLists.txt index 6500a52..3e11781 100644 --- a/libqmrom/CMakeLists.txt +++ b/libqmrom/CMakeLists.txt @@ -1,7 +1,9 @@ set(SOURCES - src/qmrom.c + src/qmrom_common.c + src/qmrom_a0.c + src/qmrom_b0.c + src/qmrom_c0.c src/qmrom_log.c - src/spi_rom_protocol.c ) add_library(qmrom SHARED ${SOURCES}) diff --git a/libqmrom/include/byteswap.h b/libqmrom/include/byteswap.h new file mode 100644 index 0000000..6852736 --- /dev/null +++ b/libqmrom/include/byteswap.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#ifndef BYTESWAP_H +#define BYTESWAP_H +#define bswap_16 __builtin_bswap16 +#endif diff --git a/libqmrom/include/qmrom.h b/libqmrom/include/qmrom.h index 8ae60f2..cbc9b28 100644 --- a/libqmrom/include/qmrom.h +++ b/libqmrom/include/qmrom.h @@ -1,23 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 /* * Copyright 2021 Qorvo US, Inc. * - * SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 - * - * This file is provided under the Apache License 2.0, or the - * GNU General Public License v2.0. - * */ + #ifndef __QMROM_H__ #define __QMROM_H__ #ifndef __KERNEL__ +#include <errno.h> +#include <stdint.h> +#include <string.h> #include <stdint.h> +#include <stdbool.h> +#include <byteswap.h> +#include <inttypes.h> #else +#include <linux/kernel.h> +#include <linux/errno.h> #include <linux/types.h> +#include <linux/string.h> +#define bswap_16 be16_to_cpu +#define PRIu32 "u" #endif #include <qmrom_error.h> +#undef CHECK_STCS + #define PEG_ERR_TIMEOUT PEG_ERR_BASE - 1 #define PEG_ERR_ROM_NOT_READY PEG_ERR_BASE - 2 #define PEG_ERR_SEND_CERT_WRITE PEG_ERR_BASE - 3 @@ -26,11 +36,20 @@ enum chip_revision_e { CHIP_REVISION_A0 = 0xA0, CHIP_REVISION_B0 = 0xB0, + CHIP_REVISION_C0 = 0xC0, CHIP_REVISION_UNKNOWN = 0xFF }; +#define HBK_LOC 12 +typedef enum { + HBK_2E_ICV = 0, + HBK_2E_OEM = 1, + HBK_1E_ICV_OEM = 2, +} hbk_t; + #define ROM_VERSION_A0 0x01a0 #define ROM_VERSION_B0 0xb000 + #define ROM_SOC_ID_LEN 0x20 #define ROM_UUID_LEN 0x10 @@ -45,38 +64,107 @@ enum chip_revision_e { /*! Defines the RMA life-cycle state value. */ #define CC_BSV_RMA_LCS 0x7 -/* This function toggles the GPIO driving the RSTn pin of the device - * It shall be implemented per the user of the library +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +struct qmrom_handle; + +struct unstitched_firmware { + struct firmware *fw_img; + struct firmware *fw_crt; + struct firmware *key1_crt; + struct firmware *key2_crt; +}; + +#define SSTC2UINT32(handle, offset) ({\ + uint32_t tmp = 0xbeefdeed; \ + if ((handle)->sstc->len >= (offset) + sizeof(tmp)) \ + memcpy(&tmp, &(handle)->sstc->payload[(offset)], sizeof(tmp)); \ + tmp; \ + }) + +#define SSTC2UINT16(handle, offset) ({\ + uint16_t tmp = 0xbeed; \ + if ((handle)->sstc->len >= (offset) + sizeof(tmp)) \ + memcpy(&tmp, &(handle)->sstc->payload[(offset)], sizeof(tmp)); \ + tmp; \ + }) + +/* Those functions allow the libqmrom to call + * revision specific functions */ -typedef int (*qmrom_reset_device)(void *reset_handle); +typedef int (*flash_fw_fn)(struct qmrom_handle *handle, + const struct firmware *fw); +typedef int (*flash_unstitched_fw_fn)(struct qmrom_handle *handle, + const struct unstitched_firmware *fw); +typedef int (*flash_debug_cert_fn)(struct qmrom_handle *handle, + struct firmware *dbg_cert); +typedef int (*erase_debug_cert_fn)(struct qmrom_handle *handle); -typedef const struct firmware* (*qmrom_get_firmware)( - void *handle, enum chip_revision_e revision, int lcs_state); -typedef void (*qmrom_release_firmware)(const struct firmware* fw); +struct rom_code_ops { + flash_fw_fn flash_fw; + flash_unstitched_fw_fn flash_unstitched_fw; + flash_debug_cert_fn flash_debug_cert; + erase_debug_cert_fn erase_debug_cert; +}; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" +/* Those functions allow the libqmrom to call + * device specific functions + */ +typedef int (*reset_device_fn)(void *handle); -struct binary_chunk { - uint32_t size; - uint8_t data[0]; + +struct device_ops { + reset_device_fn reset; +}; + +struct qmrom_handle { + void *spi_handle; + void *reset_handle; + void *ss_rdy_handle; + int comms_retries; + enum chip_revision_e chip_rev; + uint16_t device_version; + struct device_ops dev_ops; + struct rom_code_ops rom_ops; + uint32_t lcs_state; + struct stc *hstc; + struct stc *sstc; + uint8_t soc_id[ROM_SOC_ID_LEN]; + uint8_t uuid[ROM_UUID_LEN]; + bool is_be; }; -int qmrom_download_fw(void *spi_handle, - qmrom_get_firmware get_fw_fn, - qmrom_release_firmware release_fw_fn, - qmrom_reset_device reset_fn, - void *reset_handle, - int is_sram, - int lcs_state); -int qmrom_erase_dbg_cert(void *spi_handle, - qmrom_reset_device reset_fn, void *reset_handle); -int qmrom_flash_dbg_cert(void *spi_handle, struct binary_chunk *dbg_cert, - qmrom_reset_device reset_fn, void *reset_handle); -int qmrom_get_soc_info(void *spi_handle, - qmrom_reset_device reset_fn, void *reset_handle, - uint8_t soc_id[ROM_SOC_ID_LEN], - uint8_t uuid[ROM_UUID_LEN], - uint8_t *lcs_state); +int qmrom_unstitch_fw(const struct firmware *fw, + struct unstitched_firmware *unstitched_fw, + enum chip_revision_e revision); +struct qmrom_handle *qmrom_init(void *spi_handle, + void *reset_handle, + void *ss_rdy_handle, + int comms_retries, + reset_device_fn reset); +void qmrom_deinit(struct qmrom_handle *handle); +int qmrom_reboot_bootloader(struct qmrom_handle *handle); +int qmrom_flash_dbg_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert); +int qmrom_erase_dbg_cert(struct qmrom_handle *handle); +int qmrom_flash_fw(struct qmrom_handle *handle, + const struct firmware *fw); +int qmrom_flash_unstitched_fw(struct qmrom_handle *handle, + const struct unstitched_firmware *fw); +int qmrom_pre_read(struct qmrom_handle *handle); +int qmrom_read(struct qmrom_handle *handle); +int qmrom_write_cmd(struct qmrom_handle *handle, uint8_t cmd); +int qmrom_write_cmd32(struct qmrom_handle *handle, uint32_t cmd); +int qmrom_write_size_cmd(struct qmrom_handle *handle, uint8_t cmd, + uint16_t data_size, const char *data); +int qmrom_write_size_cmd32(struct qmrom_handle *handle, uint32_t cmd, + uint16_t data_size, const char *data); + +#ifdef CHECK_STCS +void check_stcs(const char *func, int line, struct qmrom_handle *h); +#else +#define check_stcs(f, l, h) +#endif #endif /* __QMROM_H__ */ diff --git a/libqmrom/include/qmrom_spi.h b/libqmrom/include/qmrom_spi.h index 2266149..42bebb6 100644 --- a/libqmrom/include/qmrom_spi.h +++ b/libqmrom/include/qmrom_spi.h @@ -16,15 +16,13 @@ #ifndef __KERNEL__ struct firmware { - uint32_t size; - uint8_t data[0]; + size_t size; + const uint8_t *data; }; #else #include <linux/firmware.h> #endif -#define DEFAULT_SPI_CLOCKRATE_A0 750000 -#define DEFAULT_SPI_CLOCKRATE 3000000 #define DEFAULT_SPI_LATENCY_MS 2 #define SPI_ERR_NOCHAN SPI_ERR_BASE - 1 diff --git a/libqmrom/include/qmrom_utils.h b/libqmrom/include/qmrom_utils.h index ceb113c..47bdd1f 100644 --- a/libqmrom/include/qmrom_utils.h +++ b/libqmrom/include/qmrom_utils.h @@ -11,9 +11,14 @@ #define __QMROM_UTILS_H__ #ifndef __KERNEL__ +#include <stdlib.h> +#include <stdint.h> +#ifndef __linux__ +extern void usleep(unsigned int us); +#else #include <unistd.h> -#include <stdlib.h> +#endif #define qmrom_msleep(ms) \ do { \ diff --git a/libqmrom/include/spi_rom_protocol.h b/libqmrom/include/spi_rom_protocol.h index 4881222..f17575e 100644 --- a/libqmrom/include/spi_rom_protocol.h +++ b/libqmrom/include/spi_rom_protocol.h @@ -15,7 +15,6 @@ #ifndef __KERNEL__ #include <stdint.h> -#include <unistd.h> #else #include <linux/types.h> #endif @@ -41,6 +40,7 @@ struct stc { uint8_t out_active : 1; // Output active indication: Set to one by the SOC SPI driver to tell the HOST that it is outputting data on MISO line and expecting that the host is doing a read transaction at this time. This is set to zero for all other transactions. uint8_t out_waiting : 1; // Output data waiting indication: Set to one by the SOC SPI driver to tell the HOST there is data awaiting reading. This is set to zero when there is no data pending output. } soc_flags; + uint8_t raw_flags; }; uint8_t ul; uint16_t len; @@ -63,13 +63,13 @@ struct stc { #define SPI_DEVICE_READY_FLAGS SPI_SH_ODW_BIT_MASK /* Communication parameters */ -#define MAX_STC_FRAME_LEN 1024 +#define MAX_STC_FRAME_LEN 2048 #define MAX_STC_PAYLOAD_LEN (MAX_STC_FRAME_LEN - sizeof(struct stc)) #define SPI_NUM_READS_FOR_READY 1 #define SPI_NUM_FAILS_RETRY 4 #define SPI_ET_PROTOCOL 5 #define SPI_RST_LOW_DELAY_MS 20 -#define SPI_INTERCMD_DELAY_MS 10 +#define SPI_INTERCMD_DELAY_MS 1 #define SPI_DEVICE_POLL_RETRY 10 #define SPI_READY_TIMEOUT_MS 50 #define SPI_ET_VERSION_LOCATION 0x601f0000 @@ -89,138 +89,4 @@ struct stc { #define SPI_ROM_WRITE_IMAGE_SIZE_SIZE 4 #define SPI_ROM_DBG_CERT_SIZE_SIZE 5 -_Static_assert(MAX_STC_PAYLOAD_LEN == 1020, - "MAX_STC_PAYLOAD_LEN should be 1020..."); - -enum spi_rsp_e { - SPI_RSP_WAIT_DOWNLOAD_MODE = 1, /*Waiting for download command*/ - SPI_RSP_WAIT_FOR_KEY1_CERT_A0, - SPI_RSP_WAIT_FOR_KEY2_CERT_A0, - SPI_RSP_WAIT_FOR_IMAGE_CERT_A0, - SPI_RSP_WAIT_IMAGE_SIZE, - SPI_RSP_WAIT_FOR_IMAGE, - SPI_RSP_DOWNLOAD_OK, - SPI_RSP_BOOT_OK, - SPI_RSP_ERROR_CS, /*Checksum/CRC error*/ - SPI_RSP_ERROR_CERTIFICATE, /*Got error certificate RSA/FW ver/Didn't get all the data before switching to image/...*/ - SPI_RSP_CMD_TOO_SHORT, /*Got command smaller than SPI_HEADER_SIZE. Each command must be at least this size.*/ - SPI_RSP_ERROR_LOADING_IN_DOWNLOAD, /*Error checking certificates or image, going to download mode*/ - SPI_RSP_WAIT_FOR_KEY1_CERT_B0 = 0x11, - SPI_RSP_WAIT_FOR_KEY2_CERT_B0 = 0x12, - SPI_RSP_WAIT_FOR_IMAGE_CERT_B0 = 0x13, - SPI_RSP_WAIT_DBG_CERT_B0 = 0x14, - SPI_RSP_WAIT_DBG_CERT_SIZE_B0 = 0x1e, -}; - -enum spi_cmd_dnld_e { - SPI_BOOT_DOWNLOAD_RRAM_CMD_A0 = 0x40, - SPI_BOOT_JUMP_RRAM_CMD, - SPI_BOOT_DOWNLOAD_SRAM_CMD, - SPI_BOOT_JUMP_SRAM_CMD, - - SPI_BOOT_DOWNLOAD_SEC_ICV_CMD_B0 = 0, - SPI_BOOT_DOWNLOAD_SEC_OEM_CMD_B0 = 1, - SPI_BOOT_GET_CHIP_VER_B0 = 2, - SPI_BOOT_GET_CHIP_INFO_B0 = 3, - SPI_BOOT_ERASE_DEBUG_CERT_B0 = 4, - SPI_BOOT_DOWNLOAD_DEBUG_CERT_B0 = 5, - SPI_BOOT_DOWNLOAD_SEC_IMAGE_DATA_B0 = 0xf, - SPI_BOOT_DOWNLOAD_CERT_DATA_B0 = 0x10, - SPI_BOOT_DEBUG_CERT_SIZE_B0 = 0x11, -}; - -enum spi_ul_e { - SPI_UL_NO_PROTOCOL = 0, - SPI_UL_ROM_BOOT_PROTO = 1, - SPI_UL_EMB_TEST = 5, -}; - -int spi_proto_prepare_write_cmd(void *spi_handle, int do_exp_resp, - uint8_t exp_resp, int read_len, - struct stc *sstc, struct stc *hstc, - enum chip_revision_e revision); -int spi_proto_send_stc(void *spi_handle, struct stc *sstc, - const struct stc *hstc, enum chip_revision_e revision); -int spi_proto_wait_for_device_flag(void *spi_handle, uint8_t flag_mask, - struct stc *sstc, struct stc *hstc, - int retries); - -#define dump_full_hstc(log_lvl, s, ...) \ - do { \ - log_lvl(__VA_ARGS__); \ - log_lvl(" # host stc: {.rvd 0x%x, .rd %d, .prd %d, .wr %d, .ul 0x%x, .len %u} -- ", \ - s->host_flags.reserved, s->host_flags.read, \ - s->host_flags.pre_read, s->host_flags.write, s->ul, \ - s->len); \ - hexdump(log_lvl, (unsigned char *)s, \ - sizeof(struct stc) + s->len); \ - } while (0) - -#define dump_full_sstc(log_lvl, s, ...) \ - do { \ - uint16_t l = s->len; \ - log_lvl(__VA_ARGS__); \ - log_lvl(" # soc stc: {.rvd 0x%x, .err %d, .rdy %d, .oact %d, .owait %d, .ul 0x%x, .len %u} -- ", \ - s->soc_flags.reserved, s->soc_flags.err, \ - s->soc_flags.ready, s->soc_flags.out_active, \ - s->soc_flags.out_waiting, s->ul, l); \ - hexdump(log_lvl, (unsigned char *)s, sizeof(struct stc) + l); \ - } while (0) - -#define dump_decoded_hstc(log_lvl, s, ...) \ - do { \ - log_lvl(__VA_ARGS__); \ - log_lvl(" # host stc: {.rvd 0x%x, .rd %d, .prd %d, .wr %d, .ul 0x%x, .len %u}\n", \ - s->host_flags.reserved, s->host_flags.read, \ - s->host_flags.pre_read, s->host_flags.write, s->ul, \ - s->len); \ - } while (0) - -#define dump_decoded_sstc(log_lvl, s, ...) \ - do { \ - log_lvl(__VA_ARGS__); \ - log_lvl(" # soc stc: {.rvd 0x%x, .err %d, .rdy %d, .oact %d, .owait %d, .ul 0x%x, .len %u}\n", \ - s->soc_flags.reserved, s->soc_flags.err, \ - s->soc_flags.ready, s->soc_flags.out_active, \ - s->soc_flags.out_waiting, s->ul, s->len); \ - } while (0) - -#define dump_raw_hstc(log_lvl, s, ...) \ - do { \ - log_lvl(__VA_ARGS__); \ - log_lvl(" # host stc %d data bytes -- ", s->len); \ - hexdump(log_lvl, (unsigned char *)s, \ - sizeof(struct stc) + s->len); \ - } while (0) - -#define dump_raw_sstc(log_lvl, s, ...) \ - do { \ - log_lvl(__VA_ARGS__); \ - log_lvl(" # soc stc %d data bytes -- ", s->len); \ - hexdump(log_lvl, (unsigned char *)s, \ - sizeof(struct stc) + s->len); \ - } while (0) - -#define dump_raw_hsstc(log_lvl, h, s, ...) \ - do { \ - log_lvl(__VA_ARGS__); \ - log_lvl("%04x:", (uint32_t)(h->len + sizeof(struct stc))); \ - hexrawdump(log_lvl, (unsigned char *)h, \ - sizeof(struct stc) + h->len); \ - log_lvl(":"); \ - hexrawdump(log_lvl, (unsigned char *)s, \ - sizeof(struct stc) + h->len); \ - log_lvl("\n"); \ - } while (0) - -#define dump_raw_buffer(log_lvl, t, r, l, ...) \ - do { \ - log_lvl(__VA_ARGS__); \ - log_lvl("%04x:", l); \ - hexrawdump(log_lvl, t, l); \ - log_lvl(":"); \ - hexrawdump(log_lvl, r, l); \ - log_lvl("\n"); \ - } while (0) - #endif /* __SPI_ROM_PROTOCOL_H__ */ diff --git a/libqmrom/src/qmrom.c b/libqmrom/src/qmrom.c deleted file mode 100644 index c76baae..0000000 --- a/libqmrom/src/qmrom.c +++ /dev/null @@ -1,879 +0,0 @@ -/* - * Copyright 2021 Qorvo US, Inc. - * - * SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 - * - * This file is provided under the Apache License 2.0, or the - * GNU General Public License v2.0. - * - */ -#include <qmrom_utils.h> -#include <qmrom_log.h> -#include <qmrom_spi.h> -#include <qmrom.h> -#include <spi_rom_protocol.h> - -#ifndef __KERNEL__ -#include <errno.h> -#include <stdint.h> -#include <string.h> -#else -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/string.h> -#endif - -#define CHUNK_SIZE_A0 1016 -#define CHUNK_SIZE_B0 1008 - -#define HBK_LOC 12 -typedef enum { - HBK_2E_ICV = 0, - HBK_2E_OEM = 1, - HBK_1E_ICV_OEM = 2, -} hbk_t; - -struct unstitched_firmware { - struct binary_chunk *fw_img; - struct binary_chunk *fw_crt; - struct binary_chunk *key1_crt; - struct binary_chunk *key2_crt; -}; - -static int qmrom_chip_revision(void *spi_handle, - struct stc *sstc, - struct stc *hstc, - enum chip_revision_e *revision); -static int qmrom_unstitch_fw(const struct firmware *fw, - struct unstitched_firmware *unstitched_fw, - enum chip_revision_e revision); -static int qmrom_start_bootloader_download(void *spi_handle, struct stc *sstc, - struct stc *hstc, int is_sram, - enum chip_revision_e revision, uint8_t is_oem); -static int qmrom_get_bootloader_rom_version(void *spi_handle, struct stc *sstc, - struct stc *hstc, uint16_t *version); -static int qmrom_send_cert(void *spi_handle, struct binary_chunk *fcert, - uint16_t exp_read, uint16_t read_size, - uint16_t cert_size, struct stc *sstc, - struct stc *hstc, enum chip_revision_e revision); -static int qmrom_send_image_size(void *spi_handle, struct binary_chunk *fw, - struct stc *sstc, struct stc *hstc, - enum chip_revision_e revision); -static int qmrom_reboot_bootloader(void *spi_handle, - qmrom_reset_device reset_fn, - void *reset_handle); -static int qmrom_send_multichunk_bytes(void *spi_handle, - struct binary_chunk *fw, - struct stc *sstc, struct stc *hstc, - enum chip_revision_e revision, - int rsp_expected, int cmd_code); - - -static void qmrom_free_stcs(struct stc *hstc, struct stc *sstc) -{ - if (hstc) qmrom_free(hstc); - if (sstc) qmrom_free(sstc); -} - -static int qmrom_allocate_stcs(struct stc **hstc, struct stc **sstc) -{ - int rc = 0; - uint8_t *tx_buf = NULL, *rx_buf = NULL; - - qmrom_alloc(tx_buf, MAX_STC_FRAME_LEN); - if (tx_buf == NULL) { - rc = -ENOMEM; - goto out; - } - - qmrom_alloc(rx_buf, MAX_STC_FRAME_LEN); - if (rx_buf == NULL) { - qmrom_free(tx_buf); - rc = -ENOMEM; - goto out; - } - - *hstc = (struct stc *)tx_buf; - *sstc = (struct stc *)rx_buf; - return rc; -out: - qmrom_free_stcs((struct stc *)tx_buf, (struct stc *)rx_buf); - *hstc = NULL; - *sstc = NULL; - return rc; -} - -static int qmrom_send_dbg_cert_size(void *spi_handle, struct binary_chunk *fw, - struct stc *sstc, struct stc *hstc) -{ - int rc; - memset(hstc->payload, 0, SPI_BOOT_DEBUG_CERT_SIZE_B0); - rc = spi_proto_prepare_write_cmd(spi_handle, 1, - SPI_RSP_WAIT_DBG_CERT_SIZE_B0, - SPI_ROM_DBG_CERT_SIZE_SIZE, sstc, - hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: prepare write failed with %d\n", __func__, rc); - return rc; - } - - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.write = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - hstc->len = sizeof(uint32_t) + 1; - hstc->payload[0] = SPI_BOOT_DEBUG_CERT_SIZE_B0; - hstc->payload[1] = fw->size & 0xff; - hstc->payload[2] = (fw->size >> 8) & 0xff; - hstc->payload[3] = (fw->size >> 16) & 0xff; - hstc->payload[4] = (fw->size >> 24) & 0xff; - rc = spi_proto_send_stc(spi_handle, sstc, hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: write failed with %d\n", __func__, rc); - return rc; - } - dump_raw_hsstc(LOG_DBG, hstc, sstc, "debug_cert_size write\n"); - return 0; -} - -int qmrom_flash_dbg_cert(void *spi_handle, struct binary_chunk *dbg_cert, - qmrom_reset_device reset_fn, void *reset_handle) -{ - int rc = 0; - struct stc *hstc, *sstc; - - rc = qmrom_allocate_stcs(&hstc, &sstc); - if (rc) - return rc; - - LOG_INFO("Rebooting the board\n"); - rc = qmrom_reboot_bootloader(spi_handle, reset_fn, reset_handle); - if (rc) - goto out; - - rc = spi_proto_prepare_write_cmd(spi_handle, 1, - 0, SPI_ROM_DBG_CERT_SIZE_SIZE, sstc, - hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: prepare write failed with %d\n", __func__, rc); - goto out; - } - - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.write = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - hstc->len = 1; - hstc->payload[0] = SPI_BOOT_DOWNLOAD_DEBUG_CERT_B0; - rc = spi_proto_send_stc(spi_handle, sstc, hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: write failed with %d\n", __func__, rc); - goto out; - } - dump_raw_hsstc(LOG_DBG, hstc, sstc, "download debug cert\n"); - - LOG_INFO("Sending the dbg cert size cmd\n"); - rc = qmrom_send_dbg_cert_size(spi_handle, dbg_cert, sstc, hstc); - if (rc) - goto out; - - rc = qmrom_send_multichunk_bytes(spi_handle, dbg_cert, sstc, hstc, CHIP_REVISION_B0, - SPI_RSP_WAIT_DBG_CERT_B0, SPI_BOOT_DOWNLOAD_CERT_DATA_B0); - -out: - qmrom_free_stcs(hstc, sstc); - return rc; -} - -int qmrom_erase_dbg_cert(void *spi_handle, - qmrom_reset_device reset_fn, void *reset_handle) -{ - int rc = 0; - struct stc *hstc, *sstc; - - rc = qmrom_allocate_stcs(&hstc, &sstc); - if (rc) - return rc; - - LOG_INFO("Rebooting the board\n"); - rc = qmrom_reboot_bootloader(spi_handle, reset_fn, reset_handle); - if (rc) - goto out; - - /* Erasing... */ - hstc->payload[0] = 0; - rc = spi_proto_prepare_write_cmd(spi_handle, 1, - 0, 1, sstc, hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: prepare write failed with %d\n", __func__, rc); - goto out; - } - /* Send the erase cmd */ - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.write = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - hstc->len = 1; - hstc->payload[0] = SPI_BOOT_ERASE_DEBUG_CERT_B0; - rc = spi_proto_send_stc(spi_handle, sstc, hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: write failed with %d\n", __func__, rc); - goto out; - } - dump_raw_hsstc(LOG_DBG, hstc, sstc, "erase debug cert\n"); - -out: - qmrom_free_stcs(hstc, sstc); - return rc; -} - -int qmrom_get_soc_info(void *spi_handle, - qmrom_reset_device reset_fn, void *reset_handle, - uint8_t soc_id[ROM_SOC_ID_LEN], - uint8_t uuid[ROM_UUID_LEN], - uint8_t *lcs_state) -{ - int rc = 0; - struct stc *hstc, *sstc; - uint8_t *payload; - int i; - enum chip_revision_e revision; - - rc = qmrom_allocate_stcs(&hstc, &sstc); - if (rc) - return rc; - - LOG_INFO("Rebooting the board\n"); - rc = qmrom_reboot_bootloader(spi_handle, reset_fn, reset_handle); - if (rc) - goto out; - - LOG_INFO("Getting the chip revision\n"); - rc = qmrom_chip_revision(spi_handle, sstc, hstc, &revision); - if (rc) - goto out; - - if (revision == CHIP_REVISION_A0) { - LOG_WARN("%s: SoC info not supported on chip revision A0\n", __func__); - rc = -1; - goto out; - } - - LOG_INFO("Rebooting the board\n"); - rc = qmrom_reboot_bootloader(spi_handle, reset_fn, reset_handle); - if (rc) - goto out; - - /* Sending command... */ - hstc->payload[0] = 3; - rc = spi_proto_prepare_write_cmd(spi_handle, 1, - 0, 1, sstc, hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: prepare write failed with %d\n", __func__, rc); - goto out; - } - /* Send the erase cmd */ - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.read = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - hstc->len = 0x32; - hstc->payload[0] = 0; - rc = spi_proto_send_stc(spi_handle, sstc, hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: write failed with %d\n", __func__, rc); - goto out; - } - dump_raw_hsstc(LOG_DBG, hstc, sstc, "erase debug cert\n"); - - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.write = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - hstc->len = 1; - hstc->payload[0] = SPI_BOOT_GET_CHIP_INFO_B0; - rc = spi_proto_send_stc(spi_handle, sstc, hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: write failed with %d\n", __func__, rc); - return rc; - } - hstc->all = 0; - sstc->all = 0; - memset(hstc->payload, 0, SPI_ROM_READ_INFO_SIZE_B0); - rc = spi_proto_prepare_write_cmd(spi_handle, 0, - SPI_RSP_WAIT_DOWNLOAD_MODE, - SPI_ROM_READ_INFO_SIZE_B0, sstc, - hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: prepare write failed with %d\n", __func__, rc); - return rc; - } - if (sstc->len != SPI_ROM_READ_INFO_SIZE_B0) { - LOG_ERR("%s: wrong device info size 0x%x\n", __func__, sstc->len); - return -1; - } - - /* skip the first byte */ - payload = &sstc->payload[1]; - for (i = 0 ; i < ROM_SOC_ID_LEN ; i++) - soc_id[i] = payload[ROM_SOC_ID_LEN - i - 1]; - payload += ROM_SOC_ID_LEN; - *lcs_state = *payload; - payload += 1; - for (i = 0 ; i < ROM_UUID_LEN ; i++) - uuid[i] = payload[ROM_UUID_LEN - i - 1]; - -out: - qmrom_free_stcs(hstc, sstc); - return rc; -} - -int qmrom_download_fw(void *spi_handle, - qmrom_get_firmware get_fw_fn, - qmrom_release_firmware release_fw_fn, - qmrom_reset_device reset_fn, - void *reset_handle, - int is_sram, - int lcs_state) -{ - int rc = 0; - struct stc *hstc, *sstc; - uint16_t spi_rom_read_image_cert_size = 0; - const struct firmware *stitched_firmware; - struct unstitched_firmware unstitched_fw = { 0 }; - enum chip_revision_e revision; - int is_oem; - - rc = qmrom_allocate_stcs(&hstc, &sstc); - if (rc) - return rc; - - LOG_INFO("Rebooting the board\n"); - rc = qmrom_reboot_bootloader(spi_handle, reset_fn, reset_handle); - if (rc) - goto fail; - - LOG_INFO("Getting the chip revision\n"); - rc = qmrom_chip_revision(spi_handle, sstc, hstc, &revision); - if (rc) - goto fail; - - if (revision == CHIP_REVISION_A0) { - spi_rom_read_image_cert_size = SPI_ROM_READ_IMAGE_CERT_SIZE_A0; - } else if (revision == CHIP_REVISION_B0) { - spi_rom_read_image_cert_size = SPI_ROM_READ_IMAGE_CERT_SIZE_B0; - } else { - LOG_ERR("%s: unknown chip: %d\n", __func__, revision); - goto fail; - } - - - stitched_firmware = get_fw_fn(spi_handle, revision, lcs_state); - if (stitched_firmware == NULL) { - LOG_ERR("%s: failed getting the binary firmware\n", __func__); - rc = -1; - goto fail; - } - - LOG_INFO("Unstitching the fw\n"); - rc = qmrom_unstitch_fw(stitched_firmware, &unstitched_fw, revision); - if (rc) - goto fail; - - release_fw_fn(stitched_firmware); - - is_oem = unstitched_fw.key1_crt->data[HBK_LOC] != HBK_2E_ICV; - LOG_INFO("Starting the download fw sequence (%s)\n", is_oem ? "OEM" : "ICV"); - rc = qmrom_start_bootloader_download(spi_handle, sstc, hstc, - is_sram, revision, is_oem); - if (rc) - goto fail; - - LOG_INFO("Sending the key1 certificate\n"); - rc = qmrom_send_cert(spi_handle, unstitched_fw.key1_crt, - revision == CHIP_REVISION_A0 ? - SPI_RSP_WAIT_FOR_KEY1_CERT_A0 : - SPI_RSP_WAIT_FOR_KEY1_CERT_B0, - spi_rom_read_image_cert_size, - unstitched_fw.key1_crt->size, - sstc, hstc, revision); - if (rc) - goto fail; - - LOG_INFO("Sending the key2 certificate\n"); - rc = qmrom_send_cert(spi_handle, unstitched_fw.key2_crt, - revision == CHIP_REVISION_A0 ? - SPI_RSP_WAIT_FOR_KEY2_CERT_A0 : - SPI_RSP_WAIT_FOR_KEY2_CERT_B0, - spi_rom_read_image_cert_size, - unstitched_fw.key2_crt->size, - sstc, hstc, revision); - if (rc) - goto fail; - - LOG_INFO("Sending the content certificate\n"); - rc = qmrom_send_cert(spi_handle, unstitched_fw.fw_crt, - revision == CHIP_REVISION_A0 ? - SPI_RSP_WAIT_FOR_IMAGE_CERT_A0 : - SPI_RSP_WAIT_FOR_IMAGE_CERT_B0, - spi_rom_read_image_cert_size, - unstitched_fw.fw_crt->size, - sstc, hstc, revision); - if (rc) - goto fail; - - if (revision == CHIP_REVISION_A0) { - LOG_INFO("Sending the image size\n"); - rc = qmrom_send_image_size(spi_handle, unstitched_fw.fw_img, - sstc, hstc, revision); - if (rc) - goto fail; - } - - LOG_INFO("Sending the image\n"); - rc = qmrom_send_multichunk_bytes(spi_handle, unstitched_fw.fw_img, - sstc, hstc, revision, - SPI_RSP_WAIT_FOR_IMAGE, - SPI_BOOT_DOWNLOAD_SEC_IMAGE_DATA_B0); - if (rc) - goto fail; - -fail: - qmrom_free_stcs(hstc, sstc); - if (unstitched_fw.fw_img) { - qmrom_free(unstitched_fw.fw_img); - qmrom_free(unstitched_fw.fw_crt); - qmrom_free(unstitched_fw.key1_crt); - qmrom_free(unstitched_fw.key2_crt); - } - return rc; -} - -static int qmrom_chip_revision(void *spi_handle, - struct stc *sstc, - struct stc *hstc, - enum chip_revision_e *revision) { - uint16_t rom_version; - int rc = 0; - - LOG_INFO("Getting the bootloader ROM version\n"); - rc = qmrom_get_bootloader_rom_version(spi_handle, sstc, hstc, - &rom_version); - if (rc) - goto out; - - if (rom_version == ROM_VERSION_A0) - *revision = CHIP_REVISION_A0; - else if (rom_version == ROM_VERSION_B0) - *revision = CHIP_REVISION_B0; - else { - LOG_ERR("%s: unknown chip revision, received id: 0x%x\n", - __func__, rom_version); - *revision = CHIP_REVISION_UNKNOWN; - rc = 1; - } - - LOG_INFO("detected chip revision: %02x\n", *revision); -out: - return rc; -} - -static int qmrom_unstitch_fw(const struct firmware *fw, - struct unstitched_firmware *unstitched_fw, - enum chip_revision_e revision) { - uint32_t tot_len = 0; - uint32_t fw_img_sz = 0; - uint32_t fw_crt_sz = 0; - uint32_t key1_crt_sz = 0; - uint32_t key2_crt_sz = 0; - uint8_t *p_key1; - uint8_t *p_key2; - uint8_t *p_crt; - uint8_t *p_fw; - int ret = 0; - - if (revision == CHIP_REVISION_A0) { - fw_img_sz = fw->size; - fw_crt_sz = SPI_ROM_WRITE_IMAGE_CERT_SIZE; - key1_crt_sz = SPI_ROM_WRITE_KEY_CERT_SIZE; - key2_crt_sz = SPI_ROM_WRITE_KEY_CERT_SIZE; - - p_fw = (uint8_t *) fw->data; - p_key1 = NULL; - p_key2 = NULL; - p_crt = NULL; - tot_len = 0; - } else if (revision == CHIP_REVISION_B0) { - // key1 - key1_crt_sz = *(uint32_t * ) & fw->data[tot_len]; - tot_len += sizeof(key1_crt_sz); - p_key1 = (uint8_t * ) & fw->data[tot_len]; - tot_len += key1_crt_sz; - if (tot_len >= fw->size) { - LOG_ERR("%s: Invalid key1_len %u -- stitched len %zu\n", - __func__, key1_crt_sz, (size_t)fw->size); - ret = -EINVAL; - goto out; - } - - // key2 - key2_crt_sz = *(uint32_t * ) & fw->data[tot_len]; - tot_len += sizeof(key2_crt_sz); - p_key2 = (uint8_t * ) & fw->data[tot_len]; - tot_len += key2_crt_sz; - if (tot_len >= fw->size) { - LOG_ERR("%s: Invalid key1_len %u and key2_len %u -- stitched len %zu\n", - __func__, key1_crt_sz, key2_crt_sz, (size_t)fw->size); - ret = -EINVAL; - goto out; - } - - // cert - fw_crt_sz = *(uint32_t * ) & fw->data[tot_len]; - tot_len += sizeof(fw_crt_sz); - p_crt = (uint8_t * ) & fw->data[tot_len]; - tot_len += fw_crt_sz; - if (tot_len >= fw->size) { - LOG_ERR("%s: Invalid key1_len %u, key2_len %u and content len %u -- stitched len %zu\n", - __func__, key1_crt_sz, key2_crt_sz, fw_crt_sz, - (size_t)fw->size); - ret = -EINVAL; - goto out; - } - - // fw - fw_img_sz = *(uint32_t * ) & fw->data[tot_len]; - tot_len += sizeof(fw_img_sz); - p_fw = (uint8_t * ) & fw->data[tot_len]; - tot_len += fw_img_sz; - if (tot_len != fw->size) { - LOG_ERR("%s: Invalid key1_len %u, key2_len %u, content len %u and fw_len %u -- stitched len %zu\n", - __func__, key1_crt_sz, key2_crt_sz, fw_crt_sz, - fw_img_sz, (size_t)fw->size); - ret = -EINVAL; - goto out; - } - } else { - LOG_ERR("%s: unknown chip: %d\n", __func__, revision); - goto out; - } - - qmrom_alloc(unstitched_fw->fw_img, - fw_img_sz + sizeof(unstitched_fw->fw_img)); - if (unstitched_fw->fw_img == NULL) { - ret = -ENOMEM; - goto out; - } - - qmrom_alloc(unstitched_fw->fw_crt, - fw_crt_sz + sizeof(unstitched_fw->fw_crt)); - if (unstitched_fw->fw_crt == NULL) { - ret = -ENOMEM; - goto err; - } - - qmrom_alloc(unstitched_fw->key1_crt, - key1_crt_sz + sizeof(unstitched_fw->key1_crt)); - if (unstitched_fw->key1_crt == NULL) { - ret = -ENOMEM; - goto err; - } - - qmrom_alloc(unstitched_fw->key2_crt, - key2_crt_sz + sizeof(unstitched_fw->key2_crt)); - if (unstitched_fw->key2_crt == NULL) { - ret = -ENOMEM; - goto err; - } - - unstitched_fw->key1_crt->size = key1_crt_sz; - unstitched_fw->key2_crt->size = key2_crt_sz; - unstitched_fw->fw_crt->size = fw_crt_sz; - unstitched_fw->fw_img->size = fw_img_sz; - - if (revision == CHIP_REVISION_B0) { - memcpy(unstitched_fw->key1_crt->data, p_key1, key1_crt_sz); - memcpy(unstitched_fw->key2_crt->data, p_key2, key2_crt_sz); - memcpy(unstitched_fw->fw_crt->data, p_crt, fw_crt_sz); - } - - memcpy(unstitched_fw->fw_img->data, p_fw, fw_img_sz); - - return 0; - -err: - qmrom_free(unstitched_fw->fw_img); - qmrom_free(unstitched_fw->fw_crt); - qmrom_free(unstitched_fw->key1_crt); - qmrom_free(unstitched_fw->key2_crt); - -out: - return ret; -} - -static int qmrom_start_bootloader_download(void *spi_handle, struct stc *sstc, - struct stc *hstc, int is_sram, enum chip_revision_e revision, - uint8_t is_oem) -{ - int rc; - - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.write = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - - if (revision == CHIP_REVISION_A0) { - hstc->payload[0] = is_sram ? SPI_BOOT_DOWNLOAD_SRAM_CMD : - SPI_BOOT_DOWNLOAD_RRAM_CMD_A0; - } else if (revision == CHIP_REVISION_B0) { - hstc->payload[0] = is_oem ? SPI_BOOT_DOWNLOAD_SEC_OEM_CMD_B0 : - SPI_BOOT_DOWNLOAD_SEC_ICV_CMD_B0; - } else { - LOG_ERR("%s: unknown chip: %d\n", __func__, revision); - return -1; - } - - hstc->len = 1; - rc = spi_proto_send_stc(spi_handle, sstc, hstc, revision); - if (rc) { - LOG_ERR("%s: write failed with %d\n", __func__, rc); - return rc; - } - dump_raw_hsstc(LOG_DBG, hstc, sstc, "download write\n"); - return 0; -} - -static int qmrom_get_bootloader_rom_version(void *spi_handle, struct stc *sstc, - struct stc *hstc, uint16_t *version) { - int rc; - uint16_t dev_id = 0; - - memset(hstc->payload, 0, SPI_ROM_READ_VERSION_SIZE_A0); - rc = spi_proto_prepare_write_cmd(spi_handle, 1, - SPI_RSP_WAIT_DOWNLOAD_MODE, - SPI_ROM_READ_VERSION_SIZE_A0, sstc, - hstc, CHIP_REVISION_A0); - // FIXME: we have to ignore the rc here because for B0, the write cmd - // function will fail because the SPI_RSP_WAIT_DOWNLOAD_MODE won't match - // with the expected one as this flag is different between A0 and B0 - // and at this point we don't know the existing revision. -// if (rc) { -// LOG_ERR("%s: prepare write failed with %d\n", __func__, rc); -// return rc; -// } - - *version = sstc->payload[1] << 8 | sstc->payload[2]; - if (*version == ROM_VERSION_A0) { - LOG_INFO("%s: ROM bootloader version: 0x%04x, Device ID 0x%04x\n", - __func__, *version, dev_id); - - return 0; - } - - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.write = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - hstc->len = 1; - hstc->payload[0] = SPI_BOOT_GET_CHIP_VER_B0; - rc = spi_proto_send_stc(spi_handle, sstc, hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: write failed with %d\n", __func__, rc); - return rc; - } - hstc->all = 0; - sstc->all = 0; - memset(hstc->payload, 0, SPI_ROM_READ_VERSION_SIZE_B0); - rc = spi_proto_prepare_write_cmd(spi_handle, 0, - SPI_RSP_WAIT_DOWNLOAD_MODE, - SPI_ROM_READ_VERSION_SIZE_B0, sstc, - hstc, CHIP_REVISION_B0); - if (rc) { - LOG_ERR("%s: prepare write failed with %d\n", __func__, rc); - return rc; - } - *version = sstc->payload[1] << 8 | sstc->payload[2]; - dev_id = sstc->payload[3] << 8 | sstc->payload[4]; - LOG_INFO("%s: ROM bootloader version: 0x%04x, Device ID 0x%04x\n", __func__, *version, dev_id); - - return 0; -} - -static int qmrom_send_cert(void *spi_handle, struct binary_chunk *fcert, - uint16_t exp_read, uint16_t read_size, - uint16_t cert_size, struct stc *sstc, - struct stc *hstc, enum chip_revision_e revision) -{ - int rc; - memset(hstc->payload, 0, read_size); - rc = spi_proto_prepare_write_cmd(spi_handle, 1, exp_read, read_size, - sstc, hstc, revision); - if (rc) { - LOG_ERR("%s: prepare write failed with %d\n", __func__, rc); - return rc; - } - if (fcert->size < cert_size) { - LOG_ERR("%s: could not read the full certificate (%d bytes asked, %d read)\n", - __func__, cert_size, fcert->size); - return PEG_ERR_SEND_CERT_WRITE; - } - - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.write = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - - switch (revision) { - case CHIP_REVISION_A0: - hstc->len = cert_size; - memcpy(hstc->payload, fcert->data, cert_size); - break; - case CHIP_REVISION_B0: - hstc->len = cert_size + 1; - hstc->payload[0] = SPI_BOOT_DOWNLOAD_CERT_DATA_B0; - memcpy(hstc->payload + 1, fcert->data, cert_size); - break; - default: - LOG_ERR("%s: Wrong revision %d\n", __func__, revision); - return PEG_ERR_WRONG_REVISION; - } - - rc = spi_proto_send_stc(spi_handle, sstc, hstc, revision); - if (rc) { - LOG_ERR("%s: write failed with %d\n", __func__, rc); - return rc; - } - dump_raw_hsstc(LOG_DBG, hstc, sstc, "certificate write\n"); - return 0; -} - -static int qmrom_send_image_size(void *spi_handle, struct binary_chunk *fw, - struct stc *sstc, struct stc *hstc, enum chip_revision_e revision) -{ - int rc; - uint16_t spi_rom_read_image_size_size = 0; - - if (revision == CHIP_REVISION_A0) { - spi_rom_read_image_size_size = SPI_ROM_READ_IMAGE_SIZE_SIZE_A0; - } else if (revision == CHIP_REVISION_B0) { - spi_rom_read_image_size_size = SPI_ROM_READ_IMAGE_SIZE_SIZE_B0; - } else { - LOG_ERR("%s: unknown chip: %d\n", __func__, revision); - return -1; - } - - memset(hstc->payload, 0, spi_rom_read_image_size_size); - rc = spi_proto_prepare_write_cmd(spi_handle, 1, - SPI_RSP_WAIT_IMAGE_SIZE, - spi_rom_read_image_size_size, sstc, - hstc, revision); - - if (rc) { - LOG_ERR("%s: prepare write failed with %d\n", __func__, rc); - return rc; - } - - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.write = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - hstc->len = sizeof(uint32_t); - hstc->payload[0] = fw->size & 0xff; - hstc->payload[1] = (fw->size >> 8) & 0xff; - hstc->payload[2] = (fw->size >> 16) & 0xff; - hstc->payload[3] = (fw->size >> 24) & 0xff; - rc = spi_proto_send_stc(spi_handle, sstc, hstc, revision); - if (rc) { - LOG_ERR("%s: write failed with %d\n", __func__, rc); - return rc; - } - dump_raw_hsstc(LOG_DBG, hstc, sstc, "image_size write\n"); - return 0; -} - -static int qmrom_send_multichunk_bytes(void *spi_handle, struct binary_chunk *fw, - struct stc *sstc, struct stc *hstc, enum chip_revision_e revision, - int rsp_expected, int cmd_code) -{ - size_t total_sent = 0, current_size; - uint8_t *payload = fw->data; - uint16_t chunk_size; - - if (revision == CHIP_REVISION_A0) { - chunk_size = CHUNK_SIZE_A0; - } else if (revision == CHIP_REVISION_B0) { - chunk_size = CHUNK_SIZE_B0; - } else { - LOG_ERR("%s: unknown chip: %d\n", __func__, revision); - return -1; - } - - do { - int rc; - hstc->len = 0; - hstc->payload[0] = 0; - rc = spi_proto_prepare_write_cmd( - spi_handle, 1, rsp_expected, 1, sstc, hstc, revision); - if (rc) { - LOG_ERR("%s: spi_proto_prepare_write_cmd failed with %d\n", - __func__, rc); - return rc; - } - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.write = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - current_size = fw->size - total_sent; - if (current_size > chunk_size) - current_size = chunk_size; - - if (revision == CHIP_REVISION_A0) { - memcpy(hstc->payload, payload, current_size); - hstc->len = current_size; - } else if (revision == CHIP_REVISION_B0) { - hstc->payload[0] = cmd_code; - memcpy(&hstc->payload[1], payload, current_size); - hstc->len = current_size + 1; - } else { - LOG_ERR("%s: unknown chip: %d\n", __func__, revision); - return -1; - } - - rc = spi_proto_send_stc(spi_handle, sstc, hstc, revision); - if (rc) { - LOG_ERR("%s: write failed with %d\n", __func__, rc); - return rc; - } - dump_raw_hsstc(LOG_DBG, hstc, sstc, "image write\n"); - - total_sent += current_size; - payload += current_size; - } while (total_sent < fw->size); - return 0; -} - -int qmrom_reboot_bootloader(void *spi_handle, - qmrom_reset_device reset_fn, - void *reset_handle) -{ - int rc; - rc = qmrom_spi_set_cs_level(spi_handle, 0); - if (rc) { - LOG_ERR("%s: spi_set_cs_level(0) failed with %d\n", __func__, - rc); - return rc; - } - qmrom_msleep(SPI_RST_LOW_DELAY_MS); - - reset_fn(reset_handle); - - qmrom_msleep(SPI_RST_LOW_DELAY_MS); - - rc = qmrom_spi_set_cs_level(spi_handle, 1); - if (rc) { - LOG_ERR("%s: spi_set_cs_level(1) failed with %d\n", __func__, - rc); - return rc; - } - return 0; -} diff --git a/libqmrom/src/qmrom_a0.c b/libqmrom/src/qmrom_a0.c new file mode 100644 index 0000000..aa8bca6 --- /dev/null +++ b/libqmrom/src/qmrom_a0.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#include <qmrom.h> +#include <qmrom_spi.h> +#include <qmrom_log.h> +#include <qmrom_utils.h> +#include <spi_rom_protocol.h> + +#define DEFAULT_SPI_CLOCKRATE 750000 +#define CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET 1 +#define CHUNK_SIZE_A0 1016 + +enum A0_CMD { + ROM_CMD_A0_GET_CHIP_VER = 0x0, + ROM_CMD_A0_DOWNLOAD_RRAM_CMD = 0x40, +}; + +enum A0_RESP { + /* Waiting for download command */ + SPI_RSP_WAIT_DOWNLOAD_MODE = 1, + SPI_RSP_WAIT_FOR_KEY1_CERT, + SPI_RSP_WAIT_FOR_KEY2_CERT, + SPI_RSP_WAIT_FOR_IMAGE_CERT, + SPI_RSP_WAIT_IMAGE_SIZE, + SPI_RSP_WAIT_FOR_IMAGE, + SPI_RSP_DOWNLOAD_OK, + SPI_RSP_BOOT_OK, + /* Checksum/CRC error */ + SPI_RSP_ERROR_CS, + /* Got error certificate RSA/FW ver. Didn't get + * all the data before switching to image... + */ + SPI_RSP_ERROR_CERTIFICATE, + /* Got command smaller than SPI_HEADER_SIZE. + * Each command must be at least this size. + */ + SPI_RSP_CMD_TOO_SHORT, + /* Error checking certificates or image, going + * to download mode. + */ + SPI_RSP_ERROR_LOADING_IN_DOWNLOAD, +}; + +static int qmrom_a0_flash_fw(struct qmrom_handle *handle, + const struct firmware *fw); + +void qmrom_a0_poll_soc(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + handle->hstc->all = 0; + qmrom_msleep(SPI_READY_TIMEOUT_MS); + do { + qmrom_spi_transfer(handle->spi_handle, + (char *)handle->sstc, (const char *)handle->hstc, + 1); + } while (retries-- && handle->sstc->raw_flags == 0); +} + +int qmrom_a0_wait_ready(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + qmrom_a0_poll_soc(handle); + + while (retries-- && + !handle->sstc->soc_flags.out_waiting) + { + qmrom_a0_poll_soc(handle); + } + + return retries > 0 ? 0 : -1; +} + +int qmrom_a0_probe_device(struct qmrom_handle *handle) +{ + int rc; + LOG_DBG("%s: enters...\n", __func__); + handle->is_be = true; + + qmrom_spi_set_freq(DEFAULT_SPI_CLOCKRATE); + + rc = qmrom_reboot_bootloader(handle); + if (rc) { + LOG_ERR("%s: cannot reset the device...\n", __func__); + return rc; + } + + rc = qmrom_a0_wait_ready(handle); + if (rc) { + LOG_INFO("%s: maybe not a A0 device\n", __func__); + return rc; + } + qmrom_pre_read(handle); + handle->sstc->len = bswap_16(handle->sstc->len); + if (handle->sstc->len > 0xff) { + /* likely the wrong endianness, B0 or C0? */ + return -1; + } + qmrom_read(handle); + + LOG_DBG("%s: Set the chip_rev/device_version\n", __func__); + handle->chip_rev = bswap_16(SSTC2UINT16(handle, + CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET)) & 0xFF; + + if (handle->chip_rev != CHIP_REVISION_A0) { + LOG_ERR("%s: wrong chip revision %#x\n", __func__, handle->chip_rev); + handle->chip_rev = -1; + return -1; + } + + /* Set rom ops */ + handle->rom_ops.flash_fw = qmrom_a0_flash_fw; + handle->rom_ops.flash_debug_cert = NULL; + handle->rom_ops.erase_debug_cert = NULL; + return 0; +} + +int qmrom_a0_write_data(struct qmrom_handle *handle, + uint16_t data_size, const char *data) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = data_size; + memcpy(handle->hstc->payload, data, data_size); + + return qmrom_spi_transfer(handle->spi_handle, + (char *)handle->sstc, (const char *)handle->hstc, + sizeof(struct stc) + data_size); +} + +static int qmrom_a0_write_chunks(struct qmrom_handle *handle, + const struct firmware *fw) +{ + int rc, sent = 0; + const char *bin_data = (const char *)fw->data; + + check_stcs(__func__, __LINE__, handle); + while (sent < fw->size) { + uint32_t tx_bytes = fw->size - sent; + if (tx_bytes > CHUNK_SIZE_A0) tx_bytes = CHUNK_SIZE_A0; + + LOG_DBG("%s: poll soc...\n", __func__); + check_stcs(__func__, __LINE__, handle); + qmrom_a0_poll_soc(handle); + qmrom_pre_read(handle); + handle->sstc->len = bswap_16(handle->sstc->len); + qmrom_read(handle); + if (handle->sstc->payload[0] != SPI_RSP_WAIT_FOR_IMAGE) + { + LOG_ERR("%s: wrong data result (%#x vs %#x)!!!\n", + __func__, handle->sstc->payload[0] & 0xff, + SPI_RSP_WAIT_FOR_IMAGE); + return SPI_PROTO_WRONG_RESP; + } + + LOG_DBG("%s: sending %"PRIu32" bytes of data\n", __func__, tx_bytes); + rc = qmrom_a0_write_data(handle, tx_bytes, bin_data); + if (rc) + return rc; + sent += tx_bytes; + bin_data += tx_bytes; + check_stcs(__func__, __LINE__, handle); + } + return 0; +} + +static int qmrom_a0_flash_fw(struct qmrom_handle *handle, + const struct firmware *fw) +{ + int rc = 0, resp; + + LOG_DBG("%s: starting...\n", __func__); + + /* Reboot since the rom code on A0 seems + * to have issues when starting flashing + * after some prior interaction (like GET_CHIP_VERSION) + */ + rc = qmrom_reboot_bootloader(handle); + if (rc) { + LOG_ERR("%s: cannot reset the device...\n", __func__); + return rc; + } + + rc = qmrom_a0_wait_ready(handle); + if (rc) { + LOG_ERR("%s: timedout waiting for the device to be ready\n", __func__); + return rc; + } + qmrom_pre_read(handle); + handle->sstc->len = bswap_16(handle->sstc->len); + qmrom_read(handle); + if (handle->sstc->payload[0] != SPI_RSP_WAIT_DOWNLOAD_MODE) + { + LOG_ERR("%s: wrong data result (%#x vs %#x)!!!\n", + __func__, handle->sstc->payload[0] & 0xff, + SPI_RSP_WAIT_DOWNLOAD_MODE); + return SPI_PROTO_WRONG_RESP; + } + + check_stcs(__func__, __LINE__, handle); + LOG_DBG("%s: sending ROM_CMD_A0_DOWNLOAD_RRAM_CMD command\n", __func__); + rc = qmrom_write_cmd(handle, ROM_CMD_A0_DOWNLOAD_RRAM_CMD); + if (rc) + return rc; + + for (resp = SPI_RSP_WAIT_FOR_KEY1_CERT ; resp < SPI_RSP_WAIT_FOR_IMAGE ; resp++) + { + qmrom_a0_poll_soc(handle); + qmrom_pre_read(handle); + handle->sstc->len = bswap_16(handle->sstc->len); + qmrom_read(handle); + if (handle->sstc->payload[0] != resp) + { + LOG_ERR("%s: wrong data result (%#x vs %#x)!!!\n", + __func__, handle->sstc->payload[0] & 0xff, + resp); + return SPI_PROTO_WRONG_RESP; + } + if (resp < SPI_RSP_WAIT_IMAGE_SIZE) { + rc = qmrom_write_cmd(handle, 0); + if (rc) + return rc; + } + } + + LOG_DBG("%s: sending fw size\n", __func__); + rc = qmrom_a0_write_data(handle, sizeof(uint32_t), + (const char *)&fw->size); + if (rc) + return rc; + + check_stcs(__func__, __LINE__, handle); + rc = qmrom_a0_write_chunks(handle, fw); + check_stcs(__func__, __LINE__, handle); + return rc; +} diff --git a/libqmrom/src/qmrom_b0.c b/libqmrom/src/qmrom_b0.c new file mode 100644 index 0000000..0dc3921 --- /dev/null +++ b/libqmrom/src/qmrom_b0.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#include <qmrom.h> +#include <qmrom_spi.h> +#include <qmrom_log.h> +#include <qmrom_utils.h> +#include <spi_rom_protocol.h> + +#define DEFAULT_SPI_CLOCKRATE 3000000 +#define CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET 1 +#define CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET 3 +#define CHUNK_SIZE_B0 1008 + +enum B0_CMD { + ROM_CMD_B0_SEC_LOAD_ICV_IMG_TO_RRAM = 0x0, + ROM_CMD_B0_SEC_LOAD_OEM_IMG_TO_RRAM = 0x1, + ROM_CMD_B0_GET_CHIP_VER = 0x2, + ROM_CMD_B0_GET_SOC_INFO = 0x3, + ROM_CMD_B0_ERASE_DBG_CERT = 0x4, + ROM_CMD_B0_WRITE_DBG_CERT = 0x5, + ROM_CMD_B0_SEC_IMAGE_DATA = 0xf, + ROM_CMD_B0_CERT_DATA = 0x10, + ROM_CMD_B0_DEBUG_CERT_SIZE = 0x11, +}; + +enum B0_RESP { + READY_FOR_CS_LOW_CMD = 0x00, + WRONG_CS_LOW_CMD = 0x01, + WAITING_FOR_NS_RRAM_FILE_SIZE = 0x02, + WAITING_FOR_NS_SRAM_FILE_SIZE = 0x03, + WAITING_FOR_NS_RRAM_FILE_DATA = 0x04, + WAITING_FOR_NS_SRAM_FILE_DATA = 0x05, + WAITING_FOR_SEC_FILE_DATA = 0x06, + ERR_NS_SRAM_OR_RRAM_SIZE_CMD = 0x07, + ERR_SEC_RRAM_SIZE_CMD = 0x08, + ERR_WAITING_FOR_NS_IMAGE_DATA_CMD = 0x09, + ERR_WAITING_FOR_SEC_IMAGE_DATA_CMD = 0x0A, + ERR_IMAGE_SIZE_IS_ZERO = 0x0B, + /* Got more data than expected size */ + ERR_IMAGE_SIZE_TOO_BIG = 0x0C, + /* Image must divide in 16 without remainder */ + ERR_IMAGE_IS_NOT_16BYTES_MUL = 0x0D, + ERR_GOT_DATA_MORE_THAN_ALLOWED = 0x0E, + /* Remainder is allowed only for last packet */ + ERR_RRAM_DATA_REMAINDER_NOT_ALLOWED = 0x0F, + ERR_WAITING_FOR_CERT_DATA_CMD = 0x10, + WAITING_FOR_FIRST_KEY_CERT = 0x11, + WAITING_FOR_SECOND_KEY_CERT = 0x12, + WAITING_FOR_CONTENT_CERT = 0x13, + WAITING_FOR_DEBUG_CERT_DATA = 0x14, + ERR_FIRST_KEY_CERT_OR_FW_VER = 0x15, + ERR_SECOND_KEY_CERT = 0x16, + ERR_CONTENT_CERT_DOWNLOAD_ADDR = 0x17, + /* If the content certificate contains to much images */ + ERR_TOO_MANY_IMAGES_IN_CONTENT_CERT = 0x18, + ERR_ADDRESS_NOT_DIVIDED_BY_8 = 0x19, + ERR_IMAGE_BOUNDARIES = 0x1A, + /* Expected ICV type and got OEM */ + ERR_CERT_TYPE = 0x1B, + ERR_PRODUCT_ID = 0x1C, + ERR_RRAM_RANGE_OR_WRITE = 0x1D, + WAITING_TO_DEBUG_CERTIFICATE_SIZE = 0x1E, + ERR_DEBUG_CERT_SIZE = 0x1F, +}; + +static int qmrom_b0_flash_fw(struct qmrom_handle *handle, + const struct firmware *fw); +static int qmrom_b0_flash_debug_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert); +static int qmrom_b0_erase_debug_cert(struct qmrom_handle *handle); +static int qmrom_b0_flash_unstitched_fw(struct qmrom_handle *handle, + const struct unstitched_firmware *all_fws); + +static void qmrom_b0_poll_soc(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + memset(handle->hstc, 0, sizeof(struct stc)); + qmrom_msleep(SPI_READY_TIMEOUT_MS); + do { + qmrom_spi_transfer(handle->spi_handle, + (char *)handle->sstc, (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); + } while (retries-- && handle->sstc->raw_flags == 0); +} + +static int qmrom_b0_wait_ready(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + qmrom_b0_poll_soc(handle); + + /* handle->sstc has been updated */ + while (retries-- && + handle->sstc->raw_flags != SPI_SH_READY_CMD_BIT_MASK) + { + if (handle->sstc->soc_flags.out_waiting) { + qmrom_pre_read(handle); + } else if (handle->sstc->soc_flags.out_active) { + if (handle->sstc->len > 0xff) { + /* likely the wrong endianness, A0? */ + return -1; + } + qmrom_read(handle); + } else { + /* error? */ + qmrom_b0_poll_soc(handle); + } + } + + return retries > 0 ? 0 : -1; +} + +static int qmrom_b0_poll_cmd_resp(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + + qmrom_b0_poll_soc(handle); + do { + if (handle->sstc->soc_flags.out_waiting) { + qmrom_pre_read(handle); + if (handle->sstc->len > 0xff) { + /* likely the wrong endianness, A0? */ + return -1; + } + qmrom_read(handle); + break; + } else qmrom_b0_poll_soc(handle); + } while (retries--); + + return retries > 0 ? 0 : -1; +} + +int qmrom_b0_probe_device(struct qmrom_handle *handle) +{ + int rc, i; + uint8_t *soc_lcs_uuid; + handle->is_be = false; + check_stcs(__func__, __LINE__, handle); + + qmrom_spi_set_freq(DEFAULT_SPI_CLOCKRATE); + + rc = qmrom_reboot_bootloader(handle); + if (rc) { + LOG_ERR("%s: cannot reset the device...\n", __func__); + return rc; + } + + rc = qmrom_b0_wait_ready(handle); + if (rc) { + LOG_INFO("%s: maybe not a B0 device\n", __func__); + return rc; + } + + rc = qmrom_write_cmd(handle, ROM_CMD_B0_GET_CHIP_VER); + if (rc) + return rc; + + rc = qmrom_b0_poll_cmd_resp(handle); + if (rc) + return rc; + + handle->chip_rev = SSTC2UINT16(handle, CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET) & 0xFF; + handle->device_version = bswap_16(SSTC2UINT16(handle, CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET)); + if (handle->chip_rev != CHIP_REVISION_B0) { + LOG_ERR("%s: wrong chip revision 0x%x\n", __func__, handle->chip_rev); + handle->chip_rev = -1; + return -1; + } + + rc = qmrom_b0_wait_ready(handle); + if (rc) { + LOG_ERR("%s: hmm something went wrong!!!\n", __func__); + return rc; + } + + rc = qmrom_write_cmd(handle, ROM_CMD_B0_GET_SOC_INFO); + if (rc) + return rc; + + rc = qmrom_b0_poll_cmd_resp(handle); + if (rc) + return rc; + + /* skip the first byte */ + soc_lcs_uuid = &(handle->sstc->payload[1]); + for (i = 0 ; i < ROM_SOC_ID_LEN ; i++) + handle->soc_id[i] = soc_lcs_uuid[ROM_SOC_ID_LEN - i - 1]; + soc_lcs_uuid += ROM_SOC_ID_LEN; + handle->lcs_state = soc_lcs_uuid[0]; + soc_lcs_uuid += 1; + for (i = 0 ; i < ROM_UUID_LEN ; i++) + handle->uuid[i] = soc_lcs_uuid[ROM_UUID_LEN - i - 1]; + + /* Set rom ops */ + handle->rom_ops.flash_fw = qmrom_b0_flash_fw; + handle->rom_ops.flash_unstitched_fw = qmrom_b0_flash_unstitched_fw; + handle->rom_ops.flash_debug_cert = qmrom_b0_flash_debug_cert; + handle->rom_ops.erase_debug_cert = qmrom_b0_erase_debug_cert; + + check_stcs(__func__, __LINE__, handle); + return 0; +} + +static int qmrom_b0_flash_data(struct qmrom_handle *handle, + struct firmware *fw, uint8_t cmd, uint8_t resp) +{ + int rc, sent = 0; + const char *bin_data = (const char *)fw->data; + + check_stcs(__func__, __LINE__, handle); + while (sent < fw->size) { + uint32_t tx_bytes = fw->size - sent; + if (tx_bytes > CHUNK_SIZE_B0) + tx_bytes = CHUNK_SIZE_B0; + + LOG_DBG("%s: poll soc...\n", __func__); + check_stcs(__func__, __LINE__, handle); + qmrom_b0_poll_soc(handle); + qmrom_pre_read(handle); + qmrom_read(handle); + + LOG_DBG("%s: sending %d command with %"PRIu32" bytes\n", __func__, + cmd, tx_bytes); + rc = qmrom_write_size_cmd(handle, cmd, tx_bytes, bin_data); + if (handle->sstc->payload[0] != resp) { + LOG_ERR("%s: wrong data result (%#x vs %#x)!!!\n", + __func__, handle->sstc->payload[0] & 0xff, + resp); + return SPI_PROTO_WRONG_RESP; + } + if (rc) + return rc; + sent += tx_bytes; + bin_data += tx_bytes; + check_stcs(__func__, __LINE__, handle); + } + return 0; +} + +static int qmrom_b0_flash_fw(struct qmrom_handle *handle, + const struct firmware *fw) +{ + int rc = 0; + struct unstitched_firmware all_fws = { 0 }; + + rc = qmrom_unstitch_fw(fw, &all_fws, handle->chip_rev); + if (rc) { + LOG_ERR("%s: Unstitched fw flashing not supported yet\n", __func__); + return rc; + } + rc = qmrom_b0_flash_unstitched_fw(handle, &all_fws); + return rc; +} + +static int qmrom_b0_flash_unstitched_fw(struct qmrom_handle *handle, + const struct unstitched_firmware *all_fws) +{ + int rc = 0; + uint8_t flash_cmd = handle->lcs_state == CC_BSV_SECURE_LCS ? + ROM_CMD_B0_SEC_LOAD_OEM_IMG_TO_RRAM : ROM_CMD_B0_SEC_LOAD_ICV_IMG_TO_RRAM; + + if (all_fws->key1_crt->data[HBK_LOC] == HBK_2E_ICV && + handle->lcs_state != CC_BSV_CHIP_MANUFACTURE_LCS) { + LOG_ERR("%s: Trying to flash an ICV fw on a non ICV platform\n", + __func__); + rc = -EINVAL; + goto end; + } + + if (all_fws->key1_crt->data[HBK_LOC] == HBK_2E_OEM && + handle->lcs_state != CC_BSV_SECURE_LCS) { + LOG_ERR("%s: Trying to flash an OEM fw on a non OEM platform\n", + __func__); + rc = -EINVAL; + goto end; + } + + LOG_DBG("%s: starting...\n", __func__); + check_stcs(__func__, __LINE__, handle); + + rc = qmrom_b0_wait_ready(handle); + if (rc) goto end; + + check_stcs(__func__, __LINE__, handle); + LOG_DBG("%s: sending flash_cmd %u command\n", __func__, flash_cmd); + rc = qmrom_write_cmd(handle, flash_cmd); + if (rc) goto end; + + check_stcs(__func__, __LINE__, handle); + rc = qmrom_b0_flash_data(handle, all_fws->key1_crt, + ROM_CMD_B0_CERT_DATA, WAITING_FOR_FIRST_KEY_CERT); + if (rc) goto end; + + check_stcs(__func__, __LINE__, handle); + rc = qmrom_b0_flash_data(handle, all_fws->key2_crt, + ROM_CMD_B0_CERT_DATA, WAITING_FOR_SECOND_KEY_CERT); + if (rc) goto end; + + check_stcs(__func__, __LINE__, handle); + rc = qmrom_b0_flash_data(handle, all_fws->fw_crt, + ROM_CMD_B0_CERT_DATA, WAITING_FOR_CONTENT_CERT); + if (rc) goto end; + + check_stcs(__func__, __LINE__, handle); + rc = qmrom_b0_flash_data(handle, all_fws->fw_img, + ROM_CMD_B0_SEC_IMAGE_DATA, WAITING_FOR_SEC_FILE_DATA); + + if (!rc) qmrom_msleep(SPI_READY_TIMEOUT_MS); + +end: + check_stcs(__func__, __LINE__, handle); + qmrom_free(all_fws->fw_img); + qmrom_free(all_fws->fw_crt); + qmrom_free(all_fws->key1_crt); + qmrom_free(all_fws->key2_crt); + return rc; +} + +static int qmrom_b0_flash_debug_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + check_stcs(__func__, __LINE__, handle); + + rc = qmrom_b0_wait_ready(handle); + if (rc) + return rc; + + check_stcs(__func__, __LINE__, handle); + LOG_DBG("%s: sending ROM_CMD_B0_WRITE_DBG_CERT command\n", __func__); + rc = qmrom_write_cmd(handle, ROM_CMD_B0_WRITE_DBG_CERT); + if (rc) + return rc; + + check_stcs(__func__, __LINE__, handle); + LOG_DBG("%s: poll soc...\n", __func__); + qmrom_b0_poll_soc(handle); + qmrom_pre_read(handle); + qmrom_read(handle); + + check_stcs(__func__, __LINE__, handle); + LOG_DBG("%s: sending ROM_CMD_B0_DEBUG_CERT_SIZE command\n", __func__); + rc = qmrom_write_size_cmd(handle, ROM_CMD_B0_DEBUG_CERT_SIZE, + sizeof(uint32_t), (const char *)&dbg_cert->size); + if (handle->sstc->payload[0] != WAITING_TO_DEBUG_CERTIFICATE_SIZE) { + LOG_ERR("%s: wrong debug cert size result (0x%x vs 0x%x)!!!\n", + __func__, handle->sstc->payload[0] & 0xff, + WAITING_TO_DEBUG_CERTIFICATE_SIZE); + return SPI_PROTO_WRONG_RESP; + } + if (rc) + return rc; + + rc = qmrom_b0_flash_data(handle, dbg_cert, + ROM_CMD_B0_CERT_DATA, WAITING_FOR_DEBUG_CERT_DATA); + check_stcs(__func__, __LINE__, handle); + return 0; +} + +static int qmrom_b0_erase_debug_cert(struct qmrom_handle *handle) +{ + int rc; + + LOG_INFO("%s: starting...\n", __func__); + check_stcs(__func__, __LINE__, handle); + + rc = qmrom_b0_wait_ready(handle); + if (!rc) + return rc; + + LOG_DBG("%s: sending ROM_CMD_B0_ERASE_DBG_CERT command\n", __func__); + rc = qmrom_write_cmd(handle, ROM_CMD_B0_ERASE_DBG_CERT); + if (rc) + return rc; + + qmrom_msleep(SPI_READY_TIMEOUT_MS); + check_stcs(__func__, __LINE__, handle); + return 0; +} diff --git a/libqmrom/src/qmrom_c0.c b/libqmrom/src/qmrom_c0.c new file mode 100644 index 0000000..540c591 --- /dev/null +++ b/libqmrom/src/qmrom_c0.c @@ -0,0 +1,442 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#include <qmrom.h> +#include <qmrom_spi.h> +#include <qmrom_log.h> +#include <qmrom_utils.h> +#include <spi_rom_protocol.h> + +#define DEFAULT_SPI_CLOCKRATE 5000000 +#define CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET 4 +#define CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET 6 +#define CHUNK_SIZE_C0 2040 + +#define SPI_SH_READY_CMD_BIT_MASK_C0 (SPI_SH_READY_CMD_BIT_MASK >> 4 | SPI_SH_READY_CMD_BIT_MASK) + +enum C0_CMD { + ROM_CMD_C0_SEC_LOAD_ICV_IMG_TO_RRAM = 0x0, + ROM_CMD_C0_SEC_LOAD_OEM_IMG_TO_RRAM = 0x1, + ROM_CMD_C0_GET_CHIP_VER = 0x2, + ROM_CMD_C0_GET_SOC_INFO = 0x3, + ROM_CMD_C0_ERASE_DBG_CERT = 0x4, + ROM_CMD_C0_USE_DIRECT_RRAM_WR = 0X5, + ROM_CMD_C0_USE_INDIRECT_RRAM_WR = 0x6, + ROM_CMD_C0_WRITE_DBG_CERT = 0x7, + ROM_CMD_C0_SEC_IMAGE_DATA = 0x12, + ROM_CMD_C0_CERT_DATA = 0x13, + ROM_CMD_C0_DEBUG_CERT_SIZE = 0x14, +}; + +enum C0_RESP { + READY_FOR_CS_LOW_CMD = 0x00, + WRONG_CS_LOW_CMD = 0x01, + WAITING_FOR_NS_RRAM_FILE_SIZE = 0x02, + WAITING_FOR_NS_SRAM_FILE_SIZE = 0x03, + WAITING_FOR_NS_RRAM_FILE_DATA = 0x04, + WAITING_FOR_NS_SRAM_FILE_DATA = 0x05, + WAITING_FOR_SEC_FILE_DATA = 0x06, + ERR_NS_SRAM_OR_RRAM_SIZE_CMD = 0x07, + ERR_SEC_RRAM_SIZE_CMD = 0x08, + ERR_WAITING_FOR_NS_IMAGE_DATA_CMD = 0x09, + ERR_WAITING_FOR_SEC_IMAGE_DATA_CMD = 0x0A, + ERR_IMAGE_SIZE_IS_ZERO = 0x0B, + /* Got more data than expected size */ + ERR_IMAGE_SIZE_TOO_BIG = 0x0C, + /* Image must divide in 16 without remainder */ + ERR_IMAGE_IS_NOT_16BYTES_MUL = 0x0D, + ERR_GOT_DATA_MORE_THAN_ALLOWED = 0x0E, + /* Remainder is allowed only for last packet */ + ERR_RRAM_DATA_REMAINDER_NOT_ALLOWED = 0x0F, + ERR_WAITING_FOR_CERT_DATA_CMD = 0x10, + WAITING_FOR_FIRST_KEY_CERT = 0x11, + WAITING_FOR_SECOND_KEY_CERT = 0x12, + WAITING_FOR_CONTENT_CERT = 0x13, + WAITING_FOR_DEBUG_CERT_DATA = 0x14, + ERR_FIRST_KEY_CERT_OR_FW_VER = 0x15, + ERR_SECOND_KEY_CERT = 0x16, + ERR_CONTENT_CERT_DOWNLOAD_ADDR = 0x17, + /* If the content certificate contains to much images */ + ERR_TOO_MANY_IMAGES_IN_CONTENT_CERT = 0x18, + ERR_ADDRESS_NOT_DIVIDED_BY_8 = 0x19, + ERR_IMAGE_BOUNDARIES = 0x1A, + /* Expected ICV type and got OEM */ + ERR_CERT_TYPE = 0x1B, + ERR_PRODUCT_ID = 0x1C, + ERR_RRAM_RANGE_OR_WRITE = 0x1D, + WAITING_TO_DEBUG_CERTIFICATE_SIZE = 0x1E, + ERR_DEBUG_CERT_SIZE = 0x1F, +}; + +static int qmrom_c0_flash_fw(struct qmrom_handle *handle, + const struct firmware *fw); +static int qmrom_c0_flash_debug_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert); +static int qmrom_c0_erase_debug_cert(struct qmrom_handle *handle); + +#define qmrom_pre_read_c0(h) \ + ({ \ + int rc = qmrom_spi_wait_for_ready_line( \ + (h)->ss_rdy_handle, SPI_READY_TIMEOUT_MS);\ + if (!rc) rc = qmrom_pre_read((h)); \ + rc; \ + }) +#define qmrom_read_c0(h) \ + ({ \ + int rc = qmrom_spi_wait_for_ready_line( \ + (h)->ss_rdy_handle, SPI_READY_TIMEOUT_MS);\ + if (!rc) rc = qmrom_read((h)); \ + rc; \ + }) +#define qmrom_write_cmd_c0(h, cmd) \ + ({ \ + int rc = qmrom_spi_wait_for_ready_line( \ + (h)->ss_rdy_handle, SPI_READY_TIMEOUT_MS);\ + if (!rc) rc = qmrom_write_cmd((h), (cmd)); \ + rc; \ + }) +#define qmrom_write_cmd32_c0(h, cmd) \ + ({ \ + int rc = qmrom_spi_wait_for_ready_line( \ + (h)->ss_rdy_handle, SPI_READY_TIMEOUT_MS);\ + if (!rc) rc = qmrom_write_cmd32((h), (cmd)); \ + rc; \ + }) +#define qmrom_write_size_cmd_c0(h, cmd, ds, d) \ + ({ \ + int rc = qmrom_spi_wait_for_ready_line( \ + (h)->ss_rdy_handle, SPI_READY_TIMEOUT_MS);\ + if (!rc) rc = qmrom_write_size_cmd((h), (cmd), (ds), (d)); \ + rc; \ + }) +#define qmrom_write_size_cmd32_c0(h, cmd, ds, d) \ + ({ \ + int rc = qmrom_spi_wait_for_ready_line( \ + (h)->ss_rdy_handle, SPI_READY_TIMEOUT_MS);\ + if (!rc) rc = qmrom_write_size_cmd32((h), (cmd), (ds), (d)); \ + rc; \ + }) + +static void qmrom_c0_poll_soc(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + memset(handle->hstc, 0, sizeof(struct stc)); + do { + int rc = qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle, SPI_READY_TIMEOUT_MS); + if (rc) { + LOG_ERR("%s qmrom_spi_wait_for_ready_line failed\n", __func__); + continue; + } + qmrom_spi_transfer(handle->spi_handle, + (char *)handle->sstc, (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); + } while (retries-- && handle->sstc->raw_flags == 0); +} + +static int qmrom_c0_wait_ready(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + qmrom_c0_poll_soc(handle); + + /* handle->sstc has been updated */ + while (retries-- && + handle->sstc->raw_flags != SPI_SH_READY_CMD_BIT_MASK_C0) + { + if (handle->sstc->soc_flags.out_waiting) { + qmrom_pre_read_c0(handle); + qmrom_read_c0(handle); + } else if (handle->sstc->soc_flags.out_active) { + if (handle->sstc->len > 0xff) { + /* likely the wrong endianness, A0? */ + return -1; + } + qmrom_read_c0(handle); + } else qmrom_c0_poll_soc(handle); + } + if (retries <= 0) + LOG_ERR("%s failed after %d replies\n", __func__, handle->comms_retries); + + return retries > 0 ? 0 : -1; +} + +static int qmrom_c0_poll_cmd_resp(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + + qmrom_c0_poll_soc(handle); + do { + if (handle->sstc->soc_flags.out_waiting) { + qmrom_pre_read_c0(handle); + if (handle->sstc->len > 0xff) { + /* likely the wrong endianness, A0? */ + return -1; + } + qmrom_read_c0(handle); + break; + } else qmrom_c0_poll_soc(handle); + } + while (retries--); + if (retries <= 0) + LOG_ERR("%s failed after %d replies\n", __func__, handle->comms_retries); + + return retries > 0 ? 0 : -1; +} + +int qmrom_c0_probe_device(struct qmrom_handle *handle) +{ + int rc, i; + uint8_t *soc_lcs_uuid; + + handle->is_be = false; + + qmrom_spi_set_freq(DEFAULT_SPI_CLOCKRATE); + + rc = qmrom_reboot_bootloader(handle); + if (rc) { + LOG_ERR("%s: cannot reset the device...\n", __func__); + return rc; + } + + rc = qmrom_c0_wait_ready(handle); + if (rc) { + LOG_INFO("%s: maybe not a C0 device\n", __func__); + return rc; + } + + rc = qmrom_write_cmd32_c0(handle, ROM_CMD_C0_GET_CHIP_VER); + if (rc) + return rc; + + rc = qmrom_c0_poll_cmd_resp(handle); + if (rc) + return rc; + + handle->chip_rev = SSTC2UINT16(handle, CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET) & 0xFF; + handle->device_version = bswap_16(SSTC2UINT16(handle, CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET)); + if (handle->chip_rev != CHIP_REVISION_C0) { + LOG_ERR("%s: wrong chip revision %#x\n", __func__, handle->chip_rev); + handle->chip_rev = -1; + return -1; + } + + rc = qmrom_c0_wait_ready(handle); + if (rc) { + LOG_ERR("%s: hmm something went wrong!!!\n", __func__); + return rc; + } + + rc = qmrom_write_cmd32_c0(handle, ROM_CMD_C0_GET_SOC_INFO); + if (rc) + return rc; + + rc = qmrom_c0_poll_cmd_resp(handle); + if (rc) + return rc; + + /* skip the first 4 bytes */ + soc_lcs_uuid = &(handle->sstc->payload[4]); + for (i = 0 ; i < ROM_SOC_ID_LEN ; i++) + handle->soc_id[i] = soc_lcs_uuid[ROM_SOC_ID_LEN - i - 1]; + soc_lcs_uuid += ROM_SOC_ID_LEN; + memcpy(&handle->lcs_state, soc_lcs_uuid, sizeof(uint32_t)); + soc_lcs_uuid += 4; + for (i = 0 ; i < ROM_UUID_LEN ; i++) + handle->uuid[i] = soc_lcs_uuid[ROM_UUID_LEN - i - 1]; + + /* Set rom ops */ + handle->rom_ops.flash_fw = qmrom_c0_flash_fw; + handle->rom_ops.flash_debug_cert = qmrom_c0_flash_debug_cert; + handle->rom_ops.erase_debug_cert = qmrom_c0_erase_debug_cert; + + return 0; +} + +static int qmrom_c0_flash_data(struct qmrom_handle *handle, + struct firmware *fw, uint8_t cmd, uint8_t resp, bool skip_last_check) +{ + int rc, sent = 0; + const char *bin_data = (const char *)fw->data; + + while (sent < fw->size) { + uint32_t tx_bytes = fw->size - sent; + if (tx_bytes > CHUNK_SIZE_C0) tx_bytes = CHUNK_SIZE_C0; + + LOG_DBG("%s: sending command %#x with %"PRIu32" bytes\n", __func__, + cmd, tx_bytes); + rc = qmrom_write_size_cmd32_c0(handle, cmd, tx_bytes, bin_data); + if (rc) + return rc; + sent += tx_bytes; + bin_data += tx_bytes; + if (skip_last_check && sent == fw->size) { + LOG_INFO("%s: flashing done, quitting now\n", __func__); + break; + } + qmrom_c0_poll_soc(handle); + qmrom_pre_read_c0(handle); + qmrom_read_c0(handle); + if (handle->sstc->payload[0] != resp) { + LOG_ERR("%s: wrong data result (%#x vs %#x)!!!\n", + __func__, handle->sstc->payload[0] & 0xff, + resp); + return SPI_PROTO_WRONG_RESP; + } + } + qmrom_msleep(SPI_READY_TIMEOUT_MS); + return 0; +} + +static int qmrom_c0_flash_fw(struct qmrom_handle *handle, + const struct firmware *fw) +{ + int rc = 0; + struct unstitched_firmware all_fws = { 0 }; + uint8_t flash_cmd = handle->lcs_state == CC_BSV_SECURE_LCS ? + ROM_CMD_C0_SEC_LOAD_OEM_IMG_TO_RRAM : ROM_CMD_C0_SEC_LOAD_ICV_IMG_TO_RRAM; + + LOG_INFO("Unstitching the fw %p->data %p\n", (void*)fw, (void*)fw->data); + rc = qmrom_unstitch_fw(fw, &all_fws, handle->chip_rev); + if (rc) { + LOG_ERR("%s: Unstitched fw flashing not supported yet\n", __func__); + return rc; + } + + if (all_fws.key1_crt->data[HBK_LOC] == HBK_2E_ICV && + handle->lcs_state != CC_BSV_CHIP_MANUFACTURE_LCS) { + LOG_ERR("%s: Trying to flash an ICV fw on a non ICV platform\n", + __func__); + rc = -EINVAL; + goto end; + } + + if (all_fws.key1_crt->data[HBK_LOC] == HBK_2E_OEM && + handle->lcs_state != CC_BSV_SECURE_LCS) { + LOG_ERR("%s: Trying to flash an OEM fw on a non OEM platform\n", + __func__); + rc = -EINVAL; + goto end; + } + + LOG_DBG("%s: starting...\n", __func__); + + /* Set RRAM write mode */ + rc = qmrom_c0_wait_ready(handle); + if (rc) + goto end; + + rc = qmrom_write_cmd32_c0(handle, ROM_CMD_C0_USE_INDIRECT_RRAM_WR); + if (rc) + goto end; + + qmrom_c0_poll_soc(handle); + qmrom_pre_read_c0(handle); + qmrom_read_c0(handle); + qmrom_c0_poll_soc(handle); + + LOG_DBG("%s: sending flash_cmd %u command\n", __func__, flash_cmd); + rc = qmrom_write_cmd32_c0(handle, flash_cmd); + if (rc) + goto end; + + qmrom_c0_poll_cmd_resp(handle); + if (handle->sstc->payload[0] != WAITING_FOR_FIRST_KEY_CERT) { + LOG_ERR("%s: Waiting for WAITING_FOR_FIRST_KEY_CERT(%#x) but got %#x\n", + __func__, WAITING_FOR_FIRST_KEY_CERT, handle->sstc->payload[0]); + goto end; + } + + qmrom_c0_poll_soc(handle); + + rc = qmrom_c0_flash_data(handle, all_fws.key1_crt, + ROM_CMD_C0_CERT_DATA, WAITING_FOR_SECOND_KEY_CERT, false); + if (rc) + goto end; + + rc = qmrom_c0_flash_data(handle, all_fws.key2_crt, + ROM_CMD_C0_CERT_DATA, WAITING_FOR_CONTENT_CERT, false); + if (rc) + goto end; + + rc = qmrom_c0_flash_data(handle, all_fws.fw_crt, + ROM_CMD_C0_CERT_DATA, WAITING_FOR_SEC_FILE_DATA, false); + if (rc) + goto end; + + rc = qmrom_c0_flash_data(handle, all_fws.fw_img, + ROM_CMD_C0_SEC_IMAGE_DATA, WAITING_FOR_SEC_FILE_DATA, true); + +end: + qmrom_free(all_fws.fw_img); + qmrom_free(all_fws.fw_crt); + qmrom_free(all_fws.key1_crt); + qmrom_free(all_fws.key2_crt); + return rc; +} + +static int qmrom_c0_flash_debug_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + rc = qmrom_c0_wait_ready(handle); + if (rc) + return rc; + + LOG_DBG("%s: sending ROM_CMD_C0_USE_DIRECT_RRAM_WR command\n", __func__); + rc = qmrom_write_cmd32_c0(handle, ROM_CMD_C0_USE_INDIRECT_RRAM_WR); + if (rc) + return rc; + + qmrom_c0_poll_soc(handle); + qmrom_pre_read_c0(handle); + qmrom_read_c0(handle); + qmrom_c0_poll_soc(handle); + + LOG_DBG("%s: sending ROM_CMD_C0_WRITE_DBG_CERT command\n", __func__); + rc = qmrom_write_cmd32_c0(handle, ROM_CMD_C0_WRITE_DBG_CERT); + if (rc) + return rc; + qmrom_c0_poll_cmd_resp(handle); + if (handle->sstc->payload[0] != WAITING_TO_DEBUG_CERTIFICATE_SIZE) { + LOG_ERR("%s: Waiting for WAITING_TO_DEBUG_CERTIFICATE_SIZE(%#x) but got %#x\n", + __func__, WAITING_TO_DEBUG_CERTIFICATE_SIZE, handle->sstc->payload[0]); + return rc; + } + + LOG_DBG("%s: sending ROM_CMD_C0_DEBUG_CERT_SIZE command\n", __func__); + rc = qmrom_write_size_cmd32_c0(handle, ROM_CMD_C0_DEBUG_CERT_SIZE, + sizeof(uint32_t), (const char *)&dbg_cert->size); + qmrom_c0_poll_cmd_resp(handle); + if (handle->sstc->payload[0] != WAITING_FOR_DEBUG_CERT_DATA) { + LOG_ERR("%s: Waiting for WAITING_FOR_DEBUG_CERT_DATA(%#x) but got %#x\n", + __func__, WAITING_FOR_DEBUG_CERT_DATA, handle->sstc->payload[0]); + return rc; + } + + rc = qmrom_c0_flash_data(handle, dbg_cert, + ROM_CMD_C0_CERT_DATA, WAITING_FOR_DEBUG_CERT_DATA, true); + return 0; +} + +static int qmrom_c0_erase_debug_cert(struct qmrom_handle *handle) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + + rc = qmrom_c0_wait_ready(handle); + if (rc) + return rc; + + LOG_DBG("%s: sending ROM_CMD_C0_ERASE_DBG_CERT command\n", __func__); + rc = qmrom_write_cmd32_c0(handle, ROM_CMD_C0_ERASE_DBG_CERT); + if (rc) + return rc; + + qmrom_msleep(SPI_READY_TIMEOUT_MS); + return 0; +} diff --git a/libqmrom/src/qmrom_common.c b/libqmrom/src/qmrom_common.c new file mode 100644 index 0000000..d5bac77 --- /dev/null +++ b/libqmrom/src/qmrom_common.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#include <qmrom_utils.h> +#include <qmrom_log.h> +#include <qmrom_spi.h> +#include <qmrom.h> +#include <spi_rom_protocol.h> + +int qmrom_a0_probe_device(struct qmrom_handle *handle); +int qmrom_b0_probe_device(struct qmrom_handle *handle); +int qmrom_c0_probe_device(struct qmrom_handle *handle); + +static void qmrom_free_stcs(struct qmrom_handle *h) +{ + if (h->hstc) + qmrom_free(h->hstc); + if (h->sstc) + qmrom_free(h->sstc); +} + +#ifdef CHECK_STCS +void check_stcs(const char *func, int line, struct qmrom_handle *h) +{ + uint32_t *buff = (uint32_t *)h->hstc; + if (buff[MAX_STC_FRAME_LEN / sizeof(uint32_t)] != 0xfeeddeef) { + LOG_ERR("%s:%d - hstc %pK corrupted\n", func, line, (void*)h->hstc); + } else { + LOG_ERR("%s:%d - hstc %pK safe\n", func, line, (void*)h->hstc); + } + buff = (uint32_t *)h->sstc; + if (buff[MAX_STC_FRAME_LEN / sizeof(uint32_t)] != 0xfeeddeef) { + LOG_ERR("%s:%d - sstc %pK corrupted\n", func, line, (void*)h->sstc); + } else { + LOG_ERR("%s:%d - sstc %pK safe\n", func, line, (void*)h->sstc); + } +} +#endif + +static int qmrom_allocate_stcs(struct qmrom_handle *h) +{ + int rc = 0; + uint8_t *tx_buf = NULL, *rx_buf = NULL; + + qmrom_alloc(tx_buf, MAX_STC_FRAME_LEN + sizeof(uint32_t)); + if (tx_buf == NULL) { + rc = -ENOMEM; + goto out; + } + + qmrom_alloc(rx_buf, MAX_STC_FRAME_LEN + sizeof(uint32_t)); + if (rx_buf == NULL) { + qmrom_free(tx_buf); + rc = -ENOMEM; + goto out; + } + +#ifdef CHECK_STCS + ((uint32_t*)tx_buf)[MAX_STC_FRAME_LEN / sizeof(uint32_t)] = 0xfeeddeef; + ((uint32_t*)rx_buf)[MAX_STC_FRAME_LEN / sizeof(uint32_t)] = 0xfeeddeef; +#endif + h->hstc = (struct stc *)tx_buf; + h->sstc = (struct stc *)rx_buf; + return rc; +out: + qmrom_free_stcs(h); + return rc; +} + +int qmrom_pre_read(struct qmrom_handle *handle) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.pre_read = 1; + handle->hstc->ul = 1; + handle->hstc->len = 0; + handle->hstc->payload[0] = 0; + return qmrom_spi_transfer(handle->spi_handle, + (char *)handle->sstc, (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +int qmrom_read(struct qmrom_handle *handle) +{ + size_t rd_size = handle->sstc->len; + LOG_DBG("%s: reading %zu bytes...\n", __func__, rd_size); + memset(handle->hstc, 0, sizeof(struct stc) + rd_size); + handle->hstc->host_flags.read = 1; + handle->hstc->ul = 1; + handle->hstc->len = handle->sstc->len; + + return qmrom_spi_transfer(handle->spi_handle, + (char *)handle->sstc, (const char *)handle->hstc, + sizeof(struct stc) + rd_size); +} + +int qmrom_write_cmd(struct qmrom_handle *handle, uint8_t cmd) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = 1; + handle->hstc->payload[0] = cmd; + + return qmrom_spi_transfer(handle->spi_handle, + (char *)handle->sstc, (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +int qmrom_write_cmd32(struct qmrom_handle *handle, uint32_t cmd) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = sizeof(cmd); + memcpy(handle->hstc->payload, &cmd, sizeof(cmd)); + + return qmrom_spi_transfer(handle->spi_handle, + (char *)handle->sstc, (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +int qmrom_write_size_cmd(struct qmrom_handle *handle, uint8_t cmd, + uint16_t data_size, const char *data) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = data_size + 1; + handle->hstc->payload[0] = cmd; + memcpy(&handle->hstc->payload[1], data, data_size); + + return qmrom_spi_transfer(handle->spi_handle, + (char *)handle->sstc, (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +int qmrom_write_size_cmd32(struct qmrom_handle *handle, uint32_t cmd, + uint16_t data_size, const char *data) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = data_size + sizeof(cmd); + memcpy(handle->hstc->payload, &cmd, sizeof(cmd)); + memcpy(&handle->hstc->payload[sizeof(cmd)], data, data_size); + + return qmrom_spi_transfer(handle->spi_handle, + (char *)handle->sstc, (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +/* + * Unfortunately, A0, B0 and C0 have different + * APIs to get the chip version... + * + */ +int qmrom_probe_device(struct qmrom_handle *handle) +{ + int rc; + + /* Test B0 first */ + rc = qmrom_b0_probe_device(handle); + if (!rc) + return rc; + + /* Test C0 next */ + rc = qmrom_c0_probe_device(handle); + if (!rc) + return rc; + + /* Finally try A0 */ + rc = qmrom_a0_probe_device(handle); + if (!rc) + return rc; + + /* None matched!!! */ + return -1; +} + +struct qmrom_handle *qmrom_init(void *spi_handle, + void *reset_handle, + void *ss_rdy_handle, + int comms_retries, + reset_device_fn reset) +{ + struct qmrom_handle *handle; + int rc; + + qmrom_alloc(handle, sizeof(struct qmrom_handle)); + if (!handle) { + LOG_ERR("%s: Couldn't allocate %zu bytes...\n", __func__, + sizeof(struct qmrom_handle)); + return NULL; + } + rc = qmrom_allocate_stcs(handle); + if (rc) { + LOG_ERR("%s: Couldn't allocate stcs...\n", __func__); + qmrom_free(handle); + return NULL; + } + + handle->spi_handle = spi_handle; + handle->reset_handle = reset_handle; + handle->ss_rdy_handle = ss_rdy_handle; + handle->comms_retries = comms_retries; + handle->chip_rev = CHIP_REVISION_UNKNOWN; + handle->device_version = -1; + handle->lcs_state = -1; + + handle->dev_ops.reset = reset; + + rc = qmrom_probe_device(handle); + if (rc) { + LOG_ERR("%s: qmrom_probe_device returned %d!\n", __func__, rc); + qmrom_free_stcs(handle); + qmrom_free(handle); + return NULL; + } + + check_stcs(__func__, __LINE__, handle); + return handle; +} + +void qmrom_deinit(struct qmrom_handle *handle) +{ + LOG_DBG("Deinitializing %pK\n", (void*)handle); + qmrom_free_stcs(handle); + qmrom_free(handle); +} + +int qmrom_flash_dbg_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert) +{ + if (!handle->rom_ops.flash_debug_cert) { + LOG_ERR("%s: flash debug certificate not support on this device\n", + __func__); + return -EINVAL; + } + return handle->rom_ops.flash_debug_cert(handle, dbg_cert); +} + +int qmrom_erase_dbg_cert(struct qmrom_handle *handle) +{ + if (!handle->rom_ops.erase_debug_cert) { + LOG_ERR("%s: erase debug certificate not support on this device\n", + __func__); + return -EINVAL; + } + return handle->rom_ops.erase_debug_cert(handle); +} + +int qmrom_flash_fw(struct qmrom_handle *handle, + const struct firmware *fw) +{ + return handle->rom_ops.flash_fw(handle, fw); +} + +int qmrom_flash_unstitched_fw(struct qmrom_handle *handle, + const struct unstitched_firmware *fw) +{ + return handle->rom_ops.flash_unstitched_fw(handle, fw); +} + +int qmrom_unstitch_fw(const struct firmware *fw, + struct unstitched_firmware *unstitched_fw, + enum chip_revision_e revision) { + uint32_t tot_len = 0; + uint32_t fw_img_sz = 0; + uint32_t fw_crt_sz = 0; + uint32_t key1_crt_sz = 0; + uint32_t key2_crt_sz = 0; + uint8_t *p_key1; + uint8_t *p_key2; + uint8_t *p_crt; + uint8_t *p_fw; + int ret = 0; + + if (revision == CHIP_REVISION_A0) { + LOG_ERR("%s: A0, no unstitching!!!\n", __func__); + return -EINVAL; + } + if (fw->size < 2 * sizeof(key1_crt_sz)) { + LOG_ERR("%s: Not enough data (%zu) to unstitch\n", __func__, fw->size); + return -EINVAL; + } + LOG_INFO("%s: Unstitching %zu bytes\n", __func__, fw->size); + + /* key1 */ + key1_crt_sz = *(uint32_t * ) & fw->data[tot_len]; + if (tot_len + key1_crt_sz + sizeof(key1_crt_sz) > fw->size) { + LOG_ERR("%s: Invalid or corrupted stitched file at offset \ + %"PRIu32" (key1)\n", __func__, tot_len); + ret = -EINVAL; + goto out; + } + tot_len += sizeof(key1_crt_sz); + p_key1 = (uint8_t * ) & fw->data[tot_len]; + tot_len += key1_crt_sz; + + /* key2 */ + key2_crt_sz = *(uint32_t * ) & fw->data[tot_len]; + if (tot_len + key2_crt_sz + sizeof(key2_crt_sz) > fw->size) { + LOG_ERR("%s: Invalid or corrupted stitched file at offset \ + %"PRIu32" (key2)\n", __func__, tot_len); + ret = -EINVAL; + goto out; + } + tot_len += sizeof(key2_crt_sz); + p_key2 = (uint8_t * ) & fw->data[tot_len]; + tot_len += key2_crt_sz; + + /* cert */ + fw_crt_sz = *(uint32_t * ) & fw->data[tot_len]; + if (tot_len + fw_crt_sz + sizeof(fw_crt_sz) > fw->size) { + LOG_ERR("%s: Invalid or corrupted stitched file at offset \ + %"PRIu32" (content cert)\n", __func__, tot_len); + ret = -EINVAL; + goto out; + } + tot_len += sizeof(fw_crt_sz); + p_crt = (uint8_t * ) & fw->data[tot_len]; + tot_len += fw_crt_sz; + + /* fw */ + fw_img_sz = *(uint32_t * ) & fw->data[tot_len]; + if (tot_len + fw_img_sz + sizeof(fw_img_sz) != fw->size) { + LOG_ERR("%s: Invalid or corrupted stitched file at offset \ + %"PRIu32" (firmnware)\n", __func__, tot_len); + ret = -EINVAL; + goto out; + } + tot_len += sizeof(fw_img_sz); + p_fw = (uint8_t * ) & fw->data[tot_len]; + + qmrom_alloc(unstitched_fw->fw_img, + fw_img_sz + sizeof(struct firmware)); + if (unstitched_fw->fw_img == NULL) { + ret = -ENOMEM; + goto out; + } + + qmrom_alloc(unstitched_fw->fw_crt, + fw_crt_sz + sizeof(struct firmware)); + if (unstitched_fw->fw_crt == NULL) { + ret = -ENOMEM; + goto err; + } + + qmrom_alloc(unstitched_fw->key1_crt, + key1_crt_sz + sizeof(struct firmware)); + if (unstitched_fw->key1_crt == NULL) { + ret = -ENOMEM; + goto err; + } + + qmrom_alloc(unstitched_fw->key2_crt, + key2_crt_sz + sizeof(struct firmware)); + if (unstitched_fw->key2_crt == NULL) { + ret = -ENOMEM; + goto err; + } + + unstitched_fw->key1_crt->data = (const uint8_t *)(unstitched_fw->key1_crt + 1); + unstitched_fw->key2_crt->data = (const uint8_t *)(unstitched_fw->key2_crt + 1); + unstitched_fw->fw_crt->data = (const uint8_t *)(unstitched_fw->fw_crt + 1); + unstitched_fw->fw_img->data = (const uint8_t *)(unstitched_fw->fw_img + 1); + unstitched_fw->key1_crt->size = key1_crt_sz; + unstitched_fw->key2_crt->size = key2_crt_sz; + unstitched_fw->fw_crt->size = fw_crt_sz; + unstitched_fw->fw_img->size = fw_img_sz; + + memcpy((void *)unstitched_fw->key1_crt->data, p_key1, key1_crt_sz); + memcpy((void *)unstitched_fw->key2_crt->data, p_key2, key2_crt_sz); + memcpy((void *)unstitched_fw->fw_crt->data, p_crt, fw_crt_sz); + memcpy((void *)unstitched_fw->fw_img->data, p_fw, fw_img_sz); + return 0; + +err: + if (unstitched_fw->fw_img) + qmrom_free(unstitched_fw->fw_img); + if (unstitched_fw->fw_crt) + qmrom_free(unstitched_fw->fw_crt); + if (unstitched_fw->key1_crt) + qmrom_free(unstitched_fw->key1_crt); + if (unstitched_fw->key2_crt) + qmrom_free(unstitched_fw->key2_crt); + +out: + return ret; +} + +int qmrom_reboot_bootloader(struct qmrom_handle *handle) +{ + int rc; + + rc = qmrom_spi_set_cs_level(handle->spi_handle, 0); + if (rc) { + LOG_ERR("%s: spi_set_cs_level(0) failed with %d\n", __func__, + rc); + return rc; + } + qmrom_msleep(SPI_RST_LOW_DELAY_MS); + + handle->dev_ops.reset(handle->reset_handle); + + qmrom_msleep(SPI_RST_LOW_DELAY_MS); + + rc = qmrom_spi_set_cs_level(handle->spi_handle, 1); + if (rc) { + LOG_ERR("%s: spi_set_cs_level(1) failed with %d\n", __func__, + rc); + return rc; + } + + qmrom_msleep(SPI_RST_LOW_DELAY_MS); + + return 0; +} diff --git a/libqmrom/src/spi_rom_protocol.c b/libqmrom/src/spi_rom_protocol.c deleted file mode 100644 index a627eaa..0000000 --- a/libqmrom/src/spi_rom_protocol.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2021 Qorvo US, Inc. - * - * SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 - * - * This file is provided under the Apache License 2.0, or the - * GNU General Public License v2.0. - * - */ -#include <spi_rom_protocol.h> -#include <qmrom_utils.h> -#include <qmrom_log.h> -#include <qmrom_spi.h> -#include <qmrom.h> - -#ifdef __KERNEL__ -#include <linux/kernel.h> -#define bswap_16 cpu_to_be16 -#else -#include <byteswap.h> -#endif - -static const char *rom_code_b0_responses_str[] = { - "res_READY_FOR_CS_LOW_CMD", - "res_WRONG_CS_LOW_CMD", - "res_WAITING_FOR_NS_RRAM_FILE_SIZE", - "res_WAITING_FOR_NS_SRAM_FILE_SIZE", - "res_WAITING_FOR_NS_RRAM_FILE_DATA", - "res_WAITING_FOR_NS_SRAM_FILE_DATA", // 5 - "res_WAITING_FOR_SEC_FILE_DATA", - "res_ERR_NS_SRAM_OR_RRAM_SIZE_CMD", - "res_ERR_SEC_RRAM_SIZE_CMD", - "res_ERR_WAITING_FOR_NS_IMAGE_DATA_CMD", - "res_ERR_WAITING_FOR_SEC_IMAGE_DATA_CMD", // 10 - "res_ERR_IMAGE_SIZE_IS_ZERO", - "res_ERR_IMAGE_SIZE_TOO_BIG", /*Got more data than expected size*/ - "res_ERR_IMAGE_IS_NOT_16BYTES_MUL", /*Image must divide in 16 without remainder*/ - "res_ERR_GOT_DATA_MORE_THAN_ALLOWED", - "res_ERR_RRAM_DATA_REMAINDER_NOT_ALLOWED", // 15 /*Remainder is allowed only for last packet*/ - "res_ERR_WAITING_FOR_CERT_DATA_CMD", - "res_WAITING_FOR_FIRST_KEY_CERT", - "res_WAITING_FOR_SECOND_KEY_CERT", - "res_WAITING_FOR_CONTENT_CERT", - "res_WAITING_FOR_DEBUG_CERT_DATA", // 20 - "res_ERR_FIRST_KEY_CERT_OR_FW_VER", - "res_ERR_SECOND_KEY_CERT", - "res_ERR_CONTENT_CERT_DOWNLOAD_ADDR", - "res_ERR_TOO_MANY_IMAGES_IN_CONTENT_CERT",/*If the content certificate contains to much images*/ - "res_ERR_ADDRESS_NOT_DIVIDED_BY_8", //25 - "res_ERR_IMAGE_BOUNDARIES", - "res_ERR_CERT_TYPE", /* Expected ICV type and got OEM */ - "res_ERR_PRODUCT_ID", - "res_ERR_RRAM_RANGE_OR_WRITE", - "res_WAITING_TO_DEBUG_CERTIFICATE_SIZE", // 30 - "res_ERR_DEBUG_CERT_SIZE" -}; -static const int rom_code_b0_responses_nb = - sizeof(rom_code_b0_responses_str) / sizeof(char *); -_Static_assert(sizeof(rom_code_b0_responses_str) / sizeof(char *) == 32, "Wrong array size"); - - -int spi_proto_prepare_write_cmd(void *spi_handle, int do_exp_resp, - uint8_t exp_resp, int read_len, - struct stc *sstc, struct stc *hstc, - enum chip_revision_e revision) -{ - uint8_t dev_ready_flag = SPI_DEVICE_READY_FLAGS; - int rc; - - if (revision == CHIP_REVISION_A0) { - qmrom_spi_set_freq(DEFAULT_SPI_CLOCKRATE_A0); - } - else { - qmrom_spi_set_freq(DEFAULT_SPI_CLOCKRATE); - } - - rc = spi_proto_wait_for_device_flag(spi_handle, - dev_ready_flag, - sstc, hstc, - SPI_DEVICE_POLL_RETRY); - if (rc) { - LOG_ERR("%s: spi_proto_wait_for_device_flag(%x) failed with %d\n", - __func__, dev_ready_flag, rc); - return rc; - } - - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.pre_read = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - hstc->len = 0; - rc = spi_proto_send_stc(spi_handle, sstc, hstc, revision); - if (rc) { - LOG_ERR("%s: pre-read failed with %d\n", __func__, rc); - return rc; - } - dump_raw_hsstc(LOG_DBG, hstc, sstc, "pre-read\n"); - - hstc->all = 0; - sstc->all = 0; - hstc->host_flags.read = 1; - hstc->ul = SPI_UL_ROM_BOOT_PROTO; - hstc->len = read_len; - /* hstc paylaod shall be set by the caller */ - rc = spi_proto_send_stc(spi_handle, sstc, hstc, revision); - if (rc) { - LOG_ERR("%s: read failed with %d\n", __func__, rc); - return rc; - } - dump_raw_hsstc(LOG_DBG, hstc, sstc, "read\n"); - if (do_exp_resp && sstc->payload[0] != exp_resp) { - if ((revision == CHIP_REVISION_B0) && - (exp_resp < rom_code_b0_responses_nb) && - (sstc->payload[0] < rom_code_b0_responses_nb)) - { - LOG_ERR("%s: rom code answered %s(0x%02x) vs %s(0x%02x) expected\n", - __func__, rom_code_b0_responses_str[sstc->payload[0]], - sstc->payload[0], - rom_code_b0_responses_str[exp_resp], exp_resp); - } else { - LOG_ERR("%s: rom code answered 0x%02x vs 0x%02x expected -- 0x%x 0x%x\n", - __func__, sstc->payload[0], exp_resp, rom_code_b0_responses_nb, revision); - } - return SPI_PROTO_WRONG_RESP; - } - return 0; -} - -int spi_proto_wait_for_device_flag(void *spi_handle, uint8_t flag_mask, - struct stc *sstc, struct stc *hstc, - int retries) -{ - uint8_t tx_buffer[16], rx_buffer[16]; - int rc; - - tx_buffer[0] = 0; - rc = qmrom_spi_wait_for_ready_line(spi_handle, SPI_READY_TIMEOUT_MS); - if (rc) - return rc; - do { - rc = qmrom_spi_transfer(spi_handle, (char *)rx_buffer, - (char *)tx_buffer, 1); - if (rc) - return rc; - dump_raw_buffer(LOG_DBG, tx_buffer, rx_buffer, 1, "Device ready\n"); - if ((rx_buffer[0] & flag_mask) == flag_mask) - return 0; - qmrom_msleep(SPI_INTERCMD_DELAY_MS); - } while (--retries > 0); - return PEG_ERR_TIMEOUT; -} - -int spi_proto_send_stc(void *spi_handle, struct stc *sstc, - const struct stc *hstc, enum chip_revision_e revision) -{ - int rc = qmrom_spi_transfer(spi_handle, (char *)sstc, (const char *)hstc, - sizeof(struct stc) + hstc->len); - if (rc) { - LOG_ERR("%s: qmrom_spi_transfer failed: error %d\n", __func__, rc); - return rc; - } - if (revision == CHIP_REVISION_A0) { - sstc->len = bswap_16(sstc->len); - } - return 0; -} @@ -56,6 +56,7 @@ #include "hsspi_test.h" #define QM35_REGULATOR_DELAY_US 1000 +#define QMROM_RETRIES 10 static int qm_firmware_load(struct qm35_ctx *qm35_hdl); static void qm35_regulators_set(struct qm35_ctx *qm35_hdl, bool on); @@ -88,7 +89,16 @@ static bool wake_use_csn = false; module_param(wake_use_csn, bool, 0444); MODULE_PARM_DESC(wake_use_csn, "Use HSSPI CSn pin to wake up QM35"); +int trace_spi_xfers; +module_param(trace_spi_xfers, int, 0444); +MODULE_PARM_DESC(trace_spi_xfers, "Trace all the SPI transfers"); + +int qmrom_retries = QMROM_RETRIES; +module_param(qmrom_retries, int, 0444); +MODULE_PARM_DESC(qmrom_retries, "QMROM retries"); + static uint8_t qm_soc_id[ROM_SOC_ID_LEN]; +static uint16_t qm_dev_id; /* * uci_open() : open operation for uci device @@ -398,38 +408,65 @@ static int qm_firmware_load(struct qm35_ctx *qm35_hdl) { struct spi_device *spi = qm35_hdl->spi; unsigned int state = qm35_get_state(qm35_hdl); + const struct firmware *fw; + struct qmrom_handle *h; int ret; - uint8_t uuid[ROM_UUID_LEN]; - uint8_t lcs_state = CC_BSV_SECURE_LCS; qm35_set_state(qm35_hdl, QM35_CTRL_STATE_FW_DOWNLOADING); qmrom_set_log_device(&spi->dev, LOG_WARN); - dev_info(&spi->dev, "Get device info\n"); - ret = qmrom_get_soc_info(&spi->dev, qmrom_spi_reset_device, qm35_hdl, - qm_soc_id, uuid, &lcs_state); - if (!ret) { - dev_info(&spi->dev, " devid: %*phN\n", ROM_SOC_ID_LEN, qm_soc_id); - dev_info(&spi->dev, " uuid: %*phN\n", ROM_UUID_LEN, uuid); - dev_info(&spi->dev, " lcs_state: %hhu\n", lcs_state); + h = qmrom_init(&spi->dev, + qm35_hdl, + qm35_hdl->gpio_ss_rdy, + qmrom_retries, + qmrom_spi_reset_device); + if (!h) { + pr_err("qmrom_init failed\n"); + ret = -1; + goto out; + } + + dev_info(&spi->dev, " chip_ver: %x\n", h->chip_rev); + dev_info(&spi->dev, " dev_id: deca%04x\n", h->device_version); + + if (h->chip_rev != CHIP_REVISION_A0) { + dev_info(&spi->dev, " soc_id: %*phN\n", ROM_SOC_ID_LEN, h->soc_id); + dev_info(&spi->dev, " uuid: %*phN\n", ROM_UUID_LEN, h->uuid); + dev_info(&spi->dev, " lcs_state: %hhu\n", h->lcs_state); + + memcpy(&qm_dev_id, &h->device_version, sizeof(qm_dev_id)); + memcpy(qm_soc_id, h->soc_id, ROM_SOC_ID_LEN); + + debug_soc_info_available(&qm35_hdl->debug); + } else { + dev_dbg(&spi->dev, "SoC info not supported on chip revision A0\n"); } - dev_info(&spi->dev, "Starting device flashing!\n"); - ret = qmrom_download_fw(spi, qmrom_spi_get_firmware, - qmrom_spi_release_firmware, - qmrom_spi_reset_device, qm35_hdl, 0, lcs_state); + dev_dbg(&spi->dev, "Starting device flashing!\n"); + fw = qmrom_spi_get_firmware(&spi->dev, + h->chip_rev, h->lcs_state); + ret = qmrom_flash_fw(h, fw); + qmrom_spi_release_firmware(fw); if (ret) dev_err(&spi->dev, "Firmware download failed!\n"); else dev_info(&spi->dev, "Device flashing completed!\n"); +out: qm35_set_state(qm35_hdl, state); return ret; } +int qm_get_dev_id(struct qm35_ctx *qm35_hdl, uint16_t *dev_id) +{ + memcpy(dev_id, &qm_dev_id, sizeof(qm_dev_id)); + + return 0; +} + int qm_get_soc_id(struct qm35_ctx *qm35_hdl, uint8_t *soc_id) { memcpy(soc_id, qm_soc_id, ROM_SOC_ID_LEN); @@ -651,10 +688,14 @@ static int qm35_probe(struct spi_device *spi) qm35_ctx->state = QM35_CTRL_STATE_UNKNOWN; - if (flash_on_probe) { - ret = qm_firmware_load(qm35_ctx); - if (ret) - goto poweroff; + /* we need the debugfs root initialized here to be able + * to display the soc info populated if flash_on_probe + * is set for chips different than A0 + */ + ret = debug_init_root(&qm35_ctx->debug, NULL); + if (ret) { + debug_deinit(&qm35_ctx->debug); + goto poweroff; } ret = hsspi_init(&qm35_ctx->hsspi, spi); @@ -665,7 +706,7 @@ static int qm35_probe(struct spi_device *spi) if (ret) goto hsspi_deinit; - ret = debug_init(&qm35_ctx->debug, NULL); + ret = debug_init(&qm35_ctx->debug); if (ret) goto uci_layer_deinit; @@ -695,6 +736,12 @@ static int qm35_probe(struct spi_device *spi) if (ret) goto log_layer_unregister; + if (flash_on_probe) { + ret = qm_firmware_load(qm35_ctx); + if (ret) + goto log_layer_unregister; + } + hsspi_start(&qm35_ctx->hsspi); /* we can have missed an edge */ @@ -83,6 +83,7 @@ static inline int qm35_reset(struct qm35_ctx *qm35_hdl, int timeout_ms) return -ENODEV; } +int qm_get_dev_id(struct qm35_ctx *qm35_hdl, uint16_t *dev_id); int qm_get_soc_id(struct qm35_ctx *qm35_hdl, uint8_t *soc_id); void qm35_hsspi_start(struct qm35_ctx *qm35_hdl); diff --git a/qmrom_spi.c b/qmrom_spi.c index 123c534..8f3f2e7 100644 --- a/qmrom_spi.c +++ b/qmrom_spi.c @@ -34,8 +34,8 @@ #include "qm35.h" static const char *fwname = NULL; -// Initialize to the lowest default frequency -static unsigned int speed_hz = DEFAULT_SPI_CLOCKRATE_A0; +static unsigned int speed_hz; +extern int trace_spi_xfers; void qmrom_set_fwname(const char *name) { @@ -45,6 +45,7 @@ void qmrom_set_fwname(const char *name) int qmrom_spi_transfer(void *handle, char *rbuf, const char *wbuf, size_t size) { struct spi_device *spi = (struct spi_device *)handle; + int rc; struct spi_transfer xfer[] = { { @@ -55,7 +56,16 @@ int qmrom_spi_transfer(void *handle, char *rbuf, const char *wbuf, size_t size) }, }; - return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); + rc = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); + + if (trace_spi_xfers) { + print_hex_dump(KERN_DEBUG, "tx:", DUMP_PREFIX_NONE, + 16, 1, wbuf, size, false); + print_hex_dump(KERN_DEBUG, "rx:", DUMP_PREFIX_NONE, + 16, 1, rbuf, size, false); + } + + return rc; } int qmrom_spi_set_cs_level(void *handle, int level) @@ -96,8 +106,7 @@ const struct firmware *qmrom_spi_get_firmware(void *handle, if (revision == CHIP_REVISION_A0) snprintf(_fw_name, sizeof(_fw_name), "qm35_%02x.bin", revision); else - snprintf(_fw_name, sizeof(_fw_name), "qm35_%02x_%.3s.bin", - revision, + snprintf(_fw_name, sizeof(_fw_name), "qm35_b0_%.3s.bin", lcs_state == CC_BSV_SECURE_LCS ? "oem" : "icv"); } else { fw_name = fwname; @@ -123,12 +132,13 @@ void qmrom_spi_release_firmware(const struct firmware *fw) release_firmware(fw); } -// FIXME: wait for ss ready mock int qmrom_spi_wait_for_ready_line(void *handle, unsigned int timeout_ms) { - usleep_range(timeout_ms * 1000, timeout_ms * 1000); - - return 0; + int count_down = (int)timeout_ms; + while (!gpiod_get_value(handle) && (--count_down >= 0)) { + usleep_range(1000, 1100); + } + return gpiod_get_value(handle) ? 0 : -1; } void qmrom_spi_set_freq(unsigned int freq) |