diff options
Diffstat (limited to 'libqmrom/src/qmrom_b0.c')
-rw-r--r-- | libqmrom/src/qmrom_b0.c | 384 |
1 files changed, 384 insertions, 0 deletions
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; +} |