From 991077f86f19233a8d64e76e3e7fd16260dd4671 Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Fri, 17 Oct 2025 10:47:46 +0100 Subject: [PATCH 1/2] [ot] hw/opentitan: ot_spi_device: Update the SRAM definition Based on the table in the spi_device/doc/programmers_guide Signed-off-by: Douglas Reis --- hw/opentitan/ot_spi_device.c | 150 +++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 68 deletions(-) diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index 1edc2fa6154d..bbacbd23d27b 100644 --- a/hw/opentitan/ot_spi_device.c +++ b/hw/opentitan/ot_spi_device.c @@ -44,14 +44,12 @@ #include "trace.h" #define PARAM_SRAM_DEPTH 1024u -#define PARAM_SRAM_OFFSET 4096u -#define PARAM_SRAM_EGRESS_DEPTH 832u -#define PARAM_SRAM_INGRESS_DEPTH 104u +#define PARAM_SRAM_EGRESS_DEPTH 848u +#define PARAM_SRAM_INGRESS_DEPTH 112u #define PARAM_NUM_CMD_INFO 24u #define PARAM_NUM_LOCALITY 5u -#define PARAM_TPM_WR_FIFO_PTR_W 7u -#define PARAM_TPM_RD_FIFO_PTR_W 5u -#define PARAM_TPM_RD_FIFO_WIDTH 32u +#define PARAM_TPM_RD_FIFO_DEPTH 16u +#define PARAM_TPM_WR_FIFO_DEPTH 16u #define PARAM_NUM_IRQS 8u #define PARAM_NUM_ALERTS 1u #define PARAM_REG_WIDTH 32u @@ -236,6 +234,8 @@ REG32(TPM_WRITE_FIFO, 0x38u) #define SPI_BUS_PROTO_VER 0 #define SPI_BUS_HEADER_SIZE (2u * sizeof(uint32_t)) +#define SPI_TPM_READ_FIFO_SIZE_BYTES \ + (PARAM_TPM_RD_FIFO_DEPTH * sizeof(uint32_t)) /** * Delay for handling non-aligned generic data transfer and flush the FIFO. * Generic mode is deprecated anyway. Arbitrarily set to 1 ms. @@ -252,64 +252,72 @@ REG32(TPM_WRITE_FIFO, 0x38u) */ #define SPI_BUS_FLASH_READ_DELAY_NS 100000000u -/* - * New scheme (Egress + Ingress) Old Scheme (DPSRAM) - * +-----------------------------+ +-----------------------+ - * | Flash / Passthru modes | | Flash / Passthru modes| - * 0x000 -+----------------+------+-----+ -+----------------+------+ - * | Read Command 0 | 1KiB | Out | | Read Command 0 | 1KiB | - * 0x400 -+----------------+------+-----+ -+----------------+------+ - * | Read Command 1 | 1KiB | Out | | Read Command 1 | 1KiB | - * 0x800 -+----------------+------+-----+ -+----------------+------+ - * | Mailbox | 1KiB | Out | | Mailbox | 1KiB | - * 0xc00 -+----------------+------+-----+ -+----------------+------+ - * | SFDP | 256B | Out | | SFDP | 256B | - * 0xd00 -+----------------+------+-----+ -+----------------+------+ - * | | | Payload FIFO | 256B | - * 0xe00 -+----------------+------+-----+ -+----------------+------+ - * | Payload FIFO | 256B | In | | Command FIFO | 64B | - * 0xe40 -+----------------+------+-----+ -+----------------+------+ - * | Command FIFO | 64B | In | | Address FIFO | 64B | - * 0xe80 -+----------------+------+-----+ -+----------------+------+ - * | Address FIFO | 64B | In | - * 0xe80 -+----------------+------+-----+ +/* Memory layout extracted from the documentation: + * opentitan.org/book/hw/ip/spi_device/doc/programmers_guide.html#dual-port-sram-layout * + * New scheme (Egress + Ingress) Old Scheme (DPSRAM) + * +--------------------------------+ +-----------------------+ + * | Flash / Passthru modes | | Flash / Passthru modes| + * 0x000 -+-------------------+------+-----+ -+----------------+------+ + * | Read Command 0 | 1KiB | Out | | Read Command 0 | 1KiB | + * 0x400 -+-------------------+------+-----+ -+----------------+------+ + * | Read Command 1 | 1KiB | Out | | Read Command 1 | 1KiB | + * 0x800 -+-------------------+------+-----+ -+----------------+------+ + * | Mailbox | 1KiB | Out | | Mailbox | 1KiB | + * 0xc00 -+-------------------+------+-----+ -+----------------+------+ + * | SFDP | 256B | Out | | SFDP | 256B | + * 0xd00 -+-------------------+------+-----+ -+----------------+------+ + * | TPM Read Buffer | 64B | Out | | Payload FIFO | 256B | + * 0xd40 -+-------------------+------+-----+ -+----------------+------+ + * | | | | | Command FIFO | 64B | + * 0xe00 -+-------------------+------+-----+ -+----------------+------+ + * | Payload FIFO | 256B | In | | Address FIFO | 64B | + * 0xf00 -+-------------------+------+-----+ -+----------------+------+ + * | Command FIFO | 64B | In | + * 0xf40 -+-------------------+------+-----+ + * | Address FIFO | 64B | In | + * 0xf80 -+-------------------+------+-----+ + * | TPM Write Buffer | 64B | In | + * 0xfc0 -+-------------------+------+-----+ * */ -#define SPI_SRAM_READ0_OFFSET 0x0 -#define SPI_SRAM_READ_SIZE 0x400u -#define SPI_SRAM_READ1_OFFSET (SPI_SRAM_READ0_OFFSET + SPI_SRAM_READ_SIZE) -#define SPI_SRAM_READ1_SIZE 0x400u -#define SPI_SRAM_MBX_OFFSET (SPI_SRAM_READ1_OFFSET + SPI_SRAM_READ_SIZE) -#define SPI_SRAM_MBX_SIZE 0x400u -#define SPI_SRAM_SFDP_OFFSET (SPI_SRAM_MBX_OFFSET + SPI_SRAM_MBX_SIZE) -#define SPI_SRAM_SFDP_SIZE 0x100u -/* with new scheme (no dual part SRAM, the following offsets are shifted...) */ -#define SPI_SRAM_INGRESS_OFFSET 0x100u -#define SPI_SRAM_PAYLOAD_OFFSET (SPI_SRAM_SFDP_OFFSET + SPI_SRAM_SFDP_SIZE) +#define SPI_SRAM_READ0_OFFSET 0x0 +#define SPI_SRAM_READ_SIZE 0x400u +#define SPI_SRAM_READ1_OFFSET (SPI_SRAM_READ0_OFFSET + SPI_SRAM_READ_SIZE) +#define SPI_SRAM_READ1_SIZE 0x400u +#define SPI_SRAM_MBX_OFFSET (SPI_SRAM_READ1_OFFSET + SPI_SRAM_READ_SIZE) +#define SPI_SRAM_MBX_SIZE 0x400u +#define SPI_SRAM_SFDP_OFFSET (SPI_SRAM_MBX_OFFSET + SPI_SRAM_MBX_SIZE) +#define SPI_SRAM_SFDP_SIZE 0x100u +#define SPI_SRAM_TPM_READ_OFFSET (SPI_SRAM_SFDP_OFFSET + SPI_SRAM_SFDP_SIZE) +#define SPI_SRAM_TPM_READ_SIZE 0x40u +#define SPI_SRAM_INGRESS_OFFSET 0xE00u +static_assert(SPI_SRAM_INGRESS_OFFSET >= + (SPI_SRAM_TPM_READ_OFFSET + SPI_SRAM_TPM_READ_SIZE), + "SPI SRAM Egress buffers overflow into Ingress buffers"); +#define SPI_SRAM_PAYLOAD_OFFSET SPI_SRAM_INGRESS_OFFSET #define SPI_SRAM_PAYLOAD_SIZE 0x100u -#define SPI_SRAM_CMD_OFFSET (SPI_SRAM_PAYLOAD_OFFSET + SPI_SRAM_PAYLOAD_SIZE) -#define SPI_SRAM_CMD_SIZE 0x40u -#define SPI_SRAM_ADDR_OFFSET (SPI_SRAM_CMD_OFFSET + SPI_SRAM_CMD_SIZE) -#define SPI_SRAM_ADDR_SIZE 0x40u -#define SPI_SRAM_ADDR_END (SPI_SRAM_ADDR_OFFSET + SPI_SRAM_ADDR_SIZE) -#define SPI_SRAM_END_OFFSET (SPI_SRAM_ADDR_END) -static_assert(SPI_SRAM_END_OFFSET == 0xe80u, "Invalid SRAM definition"); - +#define SPI_SRAM_CMD_OFFSET (SPI_SRAM_PAYLOAD_OFFSET + SPI_SRAM_PAYLOAD_SIZE) +#define SPI_SRAM_CMD_SIZE 0x40u +#define SPI_SRAM_ADDR_OFFSET (SPI_SRAM_CMD_OFFSET + SPI_SRAM_CMD_SIZE) +#define SPI_SRAM_ADDR_SIZE 0x40u +#define SPI_SRAM_TPM_WRITE_OFFSET (SPI_SRAM_ADDR_OFFSET + SPI_SRAM_ADDR_SIZE) +#define SPI_SRAM_TPM_WRITE_SIZE 0x40u +#define SPI_SRAM_ADDR_END (SPI_SRAM_TPM_WRITE_OFFSET + SPI_SRAM_TPM_WRITE_SIZE) +#define SPI_SRAM_END_OFFSET (SPI_SRAM_ADDR_END) +static_assert(SPI_SRAM_END_OFFSET == 0xfc0u, "Invalid SRAM definition"); #define SPI_DEVICE_SIZE 0x2000u #define SPI_DEVICE_SPI_REGS_OFFSET 0u #define SPI_DEVICE_TPM_REGS_OFFSET 0x800u #define SPI_DEVICE_SRAM_OFFSET 0x1000u -#define SRAM_SIZE PARAM_SRAM_OFFSET -#define EGRESS_BUFFER_SIZE_BYTES \ - (SPI_SRAM_PAYLOAD_OFFSET - SPI_SRAM_READ0_OFFSET) -#define EGRESS_BUFFER_SIZE_WORDS (EGRESS_BUFFER_SIZE_BYTES / sizeof(uint32_t)) -#define INGRESS_BUFFER_SIZE_BYTES \ - (SPI_SRAM_END_OFFSET - SPI_SRAM_PAYLOAD_OFFSET) -#define INGRESS_BUFFER_SIZE_WORDS (INGRESS_BUFFER_SIZE_BYTES / sizeof(uint32_t)) -#define FLASH_READ_BUFFER_SIZE (2u * SPI_SRAM_READ_SIZE) +#define SRAM_SIZE (PARAM_SRAM_DEPTH * sizeof(uint32_t)) +#define EGRESS_BUFFER_SIZE_BYTES (PARAM_SRAM_EGRESS_DEPTH * sizeof(uint32_t)) +#define EGRESS_BUFFER_SIZE_WORDS PARAM_SRAM_EGRESS_DEPTH +#define INGRESS_BUFFER_SIZE_BYTES (PARAM_SRAM_INGRESS_DEPTH * sizeof(uint32_t)) +#define INGRESS_BUFFER_SIZE_WORDS PARAM_SRAM_INGRESS_DEPTH +#define FLASH_READ_BUFFER_SIZE (2u * SPI_SRAM_READ_SIZE) #define SPI_DEFAULT_TX_VALUE ((uint8_t)0xffu) #define SPI_FLASH_BUFFER_SIZE 256u @@ -711,7 +719,6 @@ static void ot_spi_device_clear_modes(OtSPIDeviceState *s) f->type = SPI_FLASH_CMD_NONE; g_assert(s->sram); f->payload = &((uint8_t *)s->sram)[SPI_SRAM_PAYLOAD_OFFSET]; - f->payload += SPI_SRAM_INGRESS_OFFSET; memset(f->buffer, 0u, SPI_FLASH_BUFFER_SIZE); memset(s->sram, 0u, SRAM_SIZE); @@ -1822,27 +1829,34 @@ static MemTxResult ot_spi_device_buf_read_with_attrs( (void)attrs; uint32_t val32; - hwaddr last = addr + size - 1u; + hwaddr last = (hwaddr)((uint32_t)addr + size - 1u); - if (last < SPI_SRAM_PAYLOAD_OFFSET + SPI_SRAM_INGRESS_OFFSET) { + if (addr < SPI_SRAM_INGRESS_OFFSET) { qemu_log_mask(LOG_GUEST_ERROR, "%s: cannot read egress buffer 0x%" HWADDR_PRIx "\n", __func__, addr); return MEMTX_DECODE_ERROR; } - if (last < SPI_SRAM_CMD_OFFSET + SPI_SRAM_INGRESS_OFFSET) { - /* payload buffer */ + + if ((addr >= SPI_SRAM_PAYLOAD_OFFSET && + last < (SPI_SRAM_PAYLOAD_OFFSET + SPI_SRAM_PAYLOAD_SIZE)) || + (addr >= SPI_SRAM_TPM_WRITE_OFFSET && + last < (SPI_SRAM_TPM_WRITE_OFFSET + SPI_SRAM_TPM_WRITE_SIZE))) { + /* flash payload and tpm write buffers */ val32 = s->sram[addr >> 2u]; - } else if (last < SPI_SRAM_ADDR_OFFSET + SPI_SRAM_INGRESS_OFFSET) { - /* command FIFO */ + } else if (addr >= SPI_SRAM_CMD_OFFSET && + last < (SPI_SRAM_CMD_OFFSET + SPI_SRAM_CMD_SIZE)) { + /* flash command FIFO */ val32 = ((const uint32_t *)s->flash.cmd_fifo.data)[addr >> 2u]; - } else if (last < SPI_SRAM_ADDR_END + SPI_SRAM_INGRESS_OFFSET) { - /* address FIFO */ + } else if (addr >= SPI_SRAM_ADDR_OFFSET && + last < (SPI_SRAM_ADDR_OFFSET + SPI_SRAM_ADDR_SIZE)) { + /* flash address FIFO */ val32 = s->flash.address_fifo.data[addr >> 2u]; } else { - /* TPM or not used area */ - qemu_log_mask(LOG_UNIMP, "%s: TPM not supported 0x%" HWADDR_PRIx "\n", - __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid ingress buffer access to 0x%" HWADDR_PRIx + "-0x%" HWADDR_PRIx "\n", + __func__, addr, last); val32 = 0; } @@ -1870,9 +1884,9 @@ static MemTxResult ot_spi_device_buf_write_with_attrs( uint32_t pc = ibex_get_current_pc(); trace_ot_spi_device_buf_write_in(s->ot_id, (uint32_t)addr, size, val32, pc); - hwaddr last = addr + size - 1u; + hwaddr last = (hwaddr)((uint32_t)addr + size - 1u); - if (last >= SPI_SRAM_PAYLOAD_OFFSET) { + if (last >= SPI_SRAM_INGRESS_OFFSET) { qemu_log_mask(LOG_GUEST_ERROR, "%s: cannot write ingress buffer 0x%" HWADDR_PRIx "\n", __func__, addr); From 86a8fb5ef480b4356f394e6f9e1c90b6b798f3a2 Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Wed, 15 Oct 2025 11:54:07 +0100 Subject: [PATCH 2/2] [ot] hw/opentitan: ot_spi_device: Implement the tpm mode Signed-off-by: Douglas Reis --- hw/opentitan/ot_spi_device.c | 301 +++++++++++++++++++++++++++++++---- 1 file changed, 274 insertions(+), 27 deletions(-) diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index bbacbd23d27b..87dd794a8639 100644 --- a/hw/opentitan/ot_spi_device.c +++ b/hw/opentitan/ot_spi_device.c @@ -228,8 +228,6 @@ REG32(TPM_CMD_ADDR, 0x30u) FIELD(TPM_CMD_ADDR, ADDR, 0u, 24u) FIELD(TPM_CMD_ADDR, CMD, 24u, 8u) REG32(TPM_READ_FIFO, 0x34u) -REG32(TPM_WRITE_FIFO, 0x38u) - FIELD(TPM_WRITE_FIFO, VALUE, 0u, 8u) /* clang-format on */ #define SPI_BUS_PROTO_VER 0 @@ -295,8 +293,8 @@ REG32(TPM_WRITE_FIFO, 0x38u) static_assert(SPI_SRAM_INGRESS_OFFSET >= (SPI_SRAM_TPM_READ_OFFSET + SPI_SRAM_TPM_READ_SIZE), "SPI SRAM Egress buffers overflow into Ingress buffers"); -#define SPI_SRAM_PAYLOAD_OFFSET SPI_SRAM_INGRESS_OFFSET -#define SPI_SRAM_PAYLOAD_SIZE 0x100u +#define SPI_SRAM_PAYLOAD_OFFSET SPI_SRAM_INGRESS_OFFSET +#define SPI_SRAM_PAYLOAD_SIZE 0x100u #define SPI_SRAM_CMD_OFFSET (SPI_SRAM_PAYLOAD_OFFSET + SPI_SRAM_PAYLOAD_SIZE) #define SPI_SRAM_CMD_SIZE 0x40u #define SPI_SRAM_ADDR_OFFSET (SPI_SRAM_CMD_OFFSET + SPI_SRAM_CMD_SIZE) @@ -356,6 +354,11 @@ static_assert(PARAM_NUM_CMD_INFO == SPI_DEVICE_CMD_HW_CFG_FIRST - SPI_DEVICE_CMD_HW_STA_FIRST, "Invalid command info definitions"); +#define TPM_OPCODE_READ_BIT 7u +#define TPM_OPCODE_SIZE_MASK 0x3Fu +#define TPM_ADDR_HEADER 0xD4u +#define TPM_READY 0x01u + typedef enum { CTRL_MODE_DISABLED, CTRL_MODE_FLASH, @@ -373,6 +376,7 @@ typedef enum { typedef enum { SPI_BUS_IDLE, SPI_BUS_FLASH, + SPI_BUS_TPM, SPI_BUS_DISCARD, SPI_BUS_ERROR, } OtSpiBusState; @@ -396,6 +400,17 @@ typedef enum { SPI_FLASH_ERROR, /* On error */ } OtSpiFlashState; +typedef enum { + SPI_TPM_IDLE, /* Wait for the fist byte of the header.*/ + SPI_TPM_ADDR, /* Wait for the the following 3 bytes of the header.*/ + SPI_TPM_WAIT, /* Not ready for the host to read.*/ + SPI_TPM_START_BYTE, /* Now ready for the host to read.*/ + SPI_TPM_READ, /* The host requested a read handled by Software.*/ + SPI_TPM_READ_HW_REG, /* Host requested a read handled by Hardware.*/ + SPI_TPM_WRITE, /* Host is writing.*/ + SPI_TPM_END, /* Finished the spi transaction.*/ +} OtSpiTpmState; + typedef struct { OtSpiFlashState state; OtSpiFlashCommand type; @@ -416,6 +431,21 @@ typedef struct { bool new_cmd; /* New command has been pushed in current SPI transaction */ } SpiDeviceFlash; +typedef struct { + OtSpiTpmState state; + uint8_t *write_buffer; + uint32_t opcode; + unsigned write_pos; + unsigned len; + Fifo8 rdfifo; + unsigned can_receive; + unsigned transfer_size; + unsigned reg; + unsigned locality; + bool read; + bool should_sw_handle; +} SpiDeviceTpm; + typedef struct { uint32_t *buf; uint32_t *ptr; @@ -446,6 +476,7 @@ struct OtSPIDeviceState { SpiDeviceBus bus; SpiDeviceFlash flash; + SpiDeviceTpm tpm; uint32_t *spi_regs; /* Registers */ uint32_t *tpm_regs; /* Registers */ @@ -472,7 +503,7 @@ struct OtSPIDeviceClass { SPI_REG_NAMES[_reg_] : \ "?") -#define R_TPM_LAST_REG (R_TPM_WRITE_FIFO) +#define R_TPM_LAST_REG (R_TPM_READ_FIFO) #define TPM_REGS_COUNT (R_TPM_LAST_REG + 1u) #define TPM_REGS_SIZE (TPM_REGS_COUNT * sizeof(uint32_t)) #define TPM_REG_NAME(_reg_) \ @@ -559,7 +590,6 @@ static const char *TPM_REG_NAMES[TPM_REGS_COUNT] = { REG_NAME_ENTRY(TPM_RID), REG_NAME_ENTRY(TPM_CMD_ADDR), REG_NAME_ENTRY(TPM_READ_FIFO), - REG_NAME_ENTRY(TPM_WRITE_FIFO), }; /* clang-format on */ #undef REG_NAME_ENTRY @@ -608,6 +638,7 @@ static const char *TPM_REG_NAMES[TPM_REGS_COUNT] = { static const char *BUS_STATE_NAMES[] = { STATE_NAME_ENTRY(SPI_BUS_IDLE), STATE_NAME_ENTRY(SPI_BUS_FLASH), + STATE_NAME_ENTRY(SPI_BUS_TPM), STATE_NAME_ENTRY(SPI_BUS_DISCARD), STATE_NAME_ENTRY(SPI_BUS_ERROR), }; @@ -721,6 +752,16 @@ static void ot_spi_device_clear_modes(OtSPIDeviceState *s) f->payload = &((uint8_t *)s->sram)[SPI_SRAM_PAYLOAD_OFFSET]; memset(f->buffer, 0u, SPI_FLASH_BUFFER_SIZE); + SpiDeviceTpm *tpm = &s->tpm; + tpm->state = SPI_TPM_IDLE; + tpm->can_receive = 1u; + tpm->transfer_size = 0u; + tpm->read = false; + tpm->reg = 0u; + tpm->opcode = 0u; + tpm->locality = 0u; + tpm->write_buffer = &((uint8_t *)s->sram)[SPI_SRAM_TPM_WRITE_OFFSET]; + memset(s->sram, 0u, SRAM_SIZE); } @@ -756,6 +797,31 @@ static void ot_spi_device_update_alerts(OtSPIDeviceState *s) } } +static bool ot_spi_device_is_tpm_enabled(const OtSPIDeviceState *s) +{ + return (bool)FIELD_EX32(s->spi_regs[R_TPM_CFG], TPM_CFG, EN); +} + +/* + * if the SW set this field to 1, the HW logic always pushes the command/addr + * and write data to buffers. The logic does not compare the incoming address to + * the list of managed-by-HW register addresses. + */ +static bool ot_spi_device_is_tpm_mode_crb(const OtSPIDeviceState *s) +{ + return (bool)FIELD_EX32(s->spi_regs[R_TPM_CFG], TPM_CFG, TPM_MODE); +} + +/* + * If 0, TPM submodule directly returns the return-by-HW registers for the read + * requests. If 1, TPM submodule uploads the TPM command regardless of the + * address, and the SW may return the value through the read FIFO. + */ +static bool ot_spi_device_tpm_disable_hw_regs(const OtSPIDeviceState *s) +{ + return (bool)FIELD_EX32(s->spi_regs[R_TPM_CFG], TPM_CFG, HW_REG_DIS); +} + static OtSpiDeviceMode ot_spi_device_get_mode(const OtSPIDeviceState *s) { return (OtSpiDeviceMode)FIELD_EX32(s->spi_regs[R_CONTROL], CONTROL, MODE); @@ -808,7 +874,7 @@ static void ot_spi_device_release(OtSPIDeviceState *s) trace_ot_spi_device_release(s->ot_id); BUS_CHANGE_STATE(s, IDLE); - bus->byte_count = 0; + bus->byte_count = 0u; bool update_irq = false; switch (ot_spi_device_get_mode(s)) { @@ -1592,11 +1658,16 @@ static void ot_spi_device_spi_regs_write(void *opaque, hwaddr addr, switch (reg) { case R_INTR_STATE: - val32 &= INTR_MASK & ~(INTR_TPM_HEADER_NOT_EMPTY_MASK); - s->spi_regs[reg] &= ~val32; /* RW1C */ + val32 &= INTR_MASK; + s->spi_regs[reg] &= + ~val32 & ~(INTR_TPM_HEADER_NOT_EMPTY_MASK); /* RW1C */ ot_spi_device_update_irqs(s); - if (!ot_spi_device_flash_is_readbuf_irq(s)) { - /* no need to trigger the timer if readbuf IRQs have been cleared */ + if (!ot_spi_device_is_tpm_enabled(s) && + !ot_spi_device_flash_is_readbuf_irq(s)) { + /* + * no need to trigger the timer if in tpm mode or readbuf IRQs have + * been cleared. + */ trace_ot_spi_device_flash_pace(s->ot_id, "clear", timer_pending(s->flash.irq_timer)); timer_del(s->flash.irq_timer); @@ -1627,8 +1698,8 @@ static void ot_spi_device_spi_regs_write(void *opaque, hwaddr addr, s->spi_regs[reg] = val32; switch (ot_spi_device_get_mode(s)) { case CTRL_MODE_FLASH: - break; case CTRL_MODE_DISABLED: + break; case CTRL_MODE_PASSTHROUGH: default: qemu_log_mask(LOG_UNIMP, "%s: unsupported mode\n", __func__); @@ -1740,9 +1811,15 @@ ot_spi_device_tpm_regs_read(void *opaque, hwaddr addr, unsigned size) hwaddr reg = R32_OFF(addr); switch (reg) { - case R_TPM_CAP: - case R_TPM_CFG: + case R_TPM_CMD_ADDR: + s->tpm_regs[R_TPM_STATUS] &= ~R_TPM_STATUS_CMDADDR_NOTEMPTY_MASK; + s->spi_regs[R_INTR_STATE] &= ~INTR_TPM_HEADER_NOT_EMPTY_MASK; + /* fall through*/ case R_TPM_STATUS: + case R_TPM_CFG: + val32 = s->tpm_regs[reg]; + break; + case R_TPM_CAP: case R_TPM_ACCESS_0: case R_TPM_ACCESS_1: case R_TPM_STS: @@ -1752,8 +1829,6 @@ ot_spi_device_tpm_regs_read(void *opaque, hwaddr addr, unsigned size) case R_TPM_INT_STATUS: case R_TPM_DID_VID: case R_TPM_RID: - case R_TPM_CMD_ADDR: - case R_TPM_WRITE_FIFO: qemu_log_mask(LOG_UNIMP, "%s: %s: not supported\n", __func__, TPM_REG_NAME(reg)); val32 = s->tpm_regs[reg]; @@ -1762,15 +1837,16 @@ ot_spi_device_tpm_regs_read(void *opaque, hwaddr addr, unsigned size) qemu_log_mask(LOG_GUEST_ERROR, "%s: W/O register 0x%02" HWADDR_PRIx " (%s)\n", __func__, addr, SPI_REG_NAME(reg)); - val32 = 0; + val32 = 0u; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); - val32 = 0; + val32 = 0u; break; } + uint32_t pc = ibex_get_current_pc(); trace_ot_spi_device_io_tpm_read_out(s->ot_id, (uint32_t)addr, TPM_REG_NAME(reg), val32, pc); @@ -1802,15 +1878,20 @@ static void ot_spi_device_tpm_regs_write(void *opaque, hwaddr addr, case R_TPM_INT_STATUS: case R_TPM_DID_VID: case R_TPM_RID: - case R_TPM_READ_FIFO: - qemu_log_mask(LOG_UNIMP, "%s: %s: not supported\n", __func__, - TPM_REG_NAME(reg)); s->tpm_regs[reg] = val32; break; - case R_TPM_CAP: case R_TPM_STATUS: + s->tpm_regs[reg] &= + ~(val32 & R_TPM_STATUS_WRFIFO_PENDING_MASK); /*RW0C*/ + break; + case R_TPM_READ_FIFO: + fifo8_push(&s->tpm.rdfifo, (uint8_t)(val32 >> 0) & 0xffu); + fifo8_push(&s->tpm.rdfifo, (uint8_t)(val32 >> 8u) & 0xffu); + fifo8_push(&s->tpm.rdfifo, (uint8_t)(val32 >> 16u) & 0xffu); + fifo8_push(&s->tpm.rdfifo, (uint8_t)(val32 >> 24u) & 0xffu); + break; + case R_TPM_CAP: case R_TPM_CMD_ADDR: - case R_TPM_WRITE_FIFO: qemu_log_mask(LOG_GUEST_ERROR, "%s: R/O register 0x%02" HWADDR_PRIx " (%s)\n", __func__, addr, TPM_REG_NAME(reg)); @@ -1829,7 +1910,7 @@ static MemTxResult ot_spi_device_buf_read_with_attrs( (void)attrs; uint32_t val32; - hwaddr last = (hwaddr)((uint32_t)addr + size - 1u); + hwaddr last = addr + (hwaddr)(size - 1u); if (addr < SPI_SRAM_INGRESS_OFFSET) { qemu_log_mask(LOG_GUEST_ERROR, @@ -1884,7 +1965,7 @@ static MemTxResult ot_spi_device_buf_write_with_attrs( uint32_t pc = ibex_get_current_pc(); trace_ot_spi_device_buf_write_in(s->ot_id, (uint32_t)addr, size, val32, pc); - hwaddr last = (hwaddr)((uint32_t)addr + size - 1u); + hwaddr last = addr + (hwaddr)(size - 1u); if (last >= SPI_SRAM_INGRESS_OFFSET) { qemu_log_mask(LOG_GUEST_ERROR, @@ -1897,11 +1978,17 @@ static MemTxResult ot_spi_device_buf_write_with_attrs( return MEMTX_OK; } +static void ot_spi_device_tpm_init_buffers(OtSPIDeviceState *s) +{ + s->tpm.write_pos = 0u; + s->tpm.len = SPI_SRAM_TPM_WRITE_SIZE; +} + static void ot_spi_device_chr_handle_header(OtSPIDeviceState *s) { SpiDeviceBus *bus = &s->bus; - uint32_t size = 0; + uint32_t size = 0u; const uint8_t *hdr = fifo8_pop_bufptr(&bus->chr_fifo, SPI_BUS_HEADER_SIZE, &size); @@ -1939,6 +2026,12 @@ static void ot_spi_device_chr_handle_header(OtSPIDeviceState *s) return; } + if (ot_spi_device_is_tpm_enabled(s)) { + ot_spi_device_tpm_init_buffers(s); + BUS_CHANGE_STATE(s, TPM); + return; + } + switch (ot_spi_device_get_mode(s)) { case CTRL_MODE_FLASH: BUS_CHANGE_STATE(s, FLASH); @@ -1991,11 +2084,158 @@ static void ot_spi_device_chr_recv_flash(OtSPIDeviceState *s, } } +static bool ot_spi_device_tpm_is_hw_register(uint32_t addr) +{ + static const uint32_t TPM_HW_ADDR[] = { + 0x000u, /* 000 Access_x*/ + 0x008u, /* 00B:008 Interrupt Enable*/ + 0x00Cu, /* 00C Interrupt Vector*/ + 0x010u, /* 013:010 Interrupt Status*/ + 0x014u, /* 017:014 Interface Capability*/ + 0x018u, /* 01B:018 Status_x*/ + 0x028u, /* 02B:028 Hash Start*/ + 0xF00u, /* F03:F00 DID_VID*/ + 0xF04u /* F04:F04 RID*/ + }; + for (uint32_t idx = 0u; idx < ARRAY_SIZE(TPM_HW_ADDR); idx++) { + if (TPM_HW_ADDR[idx] == (addr & ~0x3)) { + return true; + } + } + return false; +} + +static void ot_spi_device_tpm_idle_state(OtSPIDeviceState *s, + const uint8_t *buf, unsigned size) +{ + g_assert(size == 1u); + SpiDeviceTpm *tpm = &s->tpm; + fifo8_reset(&tpm->rdfifo); + tpm->opcode = buf[0u]; + tpm->read = tpm->opcode >> TPM_OPCODE_READ_BIT; + tpm->transfer_size = (tpm->opcode & TPM_OPCODE_SIZE_MASK) + 1u; + tpm->state = SPI_TPM_ADDR; + tpm->can_receive = 3u; +} + +static void ot_spi_device_tpm_addr_state(OtSPIDeviceState *s, + const uint8_t *buf, unsigned size) +{ + g_assert(size == 3u); + SpiDeviceTpm *tpm = &s->tpm; + tpm->reg = buf[1u] << 8u | buf[2u]; + tpm->locality = tpm->reg >> 12u; + s->tpm_regs[R_TPM_CMD_ADDR] = + (tpm->opcode << R_TPM_CMD_ADDR_CMD_SHIFT) | tpm->reg; + + /* When read, we immediately signal the software to fill the FIFO */ + if (tpm->read) { + s->tpm_regs[R_TPM_STATUS] |= R_TPM_STATUS_CMDADDR_NOTEMPTY_MASK; + s->spi_regs[R_INTR_STATE] |= INTR_TPM_HEADER_NOT_EMPTY_MASK; + ot_spi_device_update_irqs(s); + } + + tpm->can_receive = 1u; + tpm->should_sw_handle = + ot_spi_device_is_tpm_mode_crb(s) || + ot_spi_device_tpm_disable_hw_regs(s) || buf[0u] != TPM_ADDR_HEADER || + !ot_spi_device_tpm_is_hw_register(tpm->reg); + + tpm->state = tpm->should_sw_handle ? SPI_TPM_WAIT : SPI_TPM_START_BYTE; +} + +static void ot_spi_device_tpm_write_state(OtSPIDeviceState *s, + const uint8_t *buf, unsigned size) +{ + SpiDeviceTpm *tpm = &s->tpm; + memcpy(&tpm->write_buffer[tpm->write_pos], buf, size); + tpm->write_pos += size; + tpm->state = + tpm->write_pos >= tpm->transfer_size ? SPI_TPM_IDLE : tpm->state; + + s->tpm_regs[R_TPM_STATUS] |= R_TPM_STATUS_CMDADDR_NOTEMPTY_MASK; + s->spi_regs[R_INTR_STATE] |= INTR_TPM_HEADER_NOT_EMPTY_MASK; + ot_spi_device_update_irqs(s); +} + +static void ot_spi_device_tpm_read_state(OtSPIDeviceState *s, unsigned size, + uint8_t *tx_buf) +{ + SpiDeviceTpm *tpm = &s->tpm; + g_assert(fifo8_pop_buf(&tpm->rdfifo, tx_buf, size) == size); + tpm->state = SPI_TPM_IDLE; +} + +static void ot_spi_device_tpm_wait_state(OtSPIDeviceState *s) +{ + SpiDeviceTpm *tpm = &s->tpm; + if (!tpm->read || (fifo8_num_used(&tpm->rdfifo) >= tpm->transfer_size)) { + tpm->state = SPI_TPM_START_BYTE; + } +} + +static void ot_spi_device_tpm_start_byte_state(OtSPIDeviceState *s, + unsigned size, uint8_t *tx_buf) +{ + SpiDeviceTpm *tpm = &s->tpm; + tpm->state = SPI_TPM_WRITE; + if (tpm->read) { + tpm->state = tpm->should_sw_handle ? SPI_TPM_READ : SPI_TPM_READ_HW_REG; + } + tx_buf[size - 1u] = TPM_READY; + tpm->can_receive = tpm->transfer_size; +} + +static void ot_spi_device_tpm_state_machine(OtSPIDeviceState *s, + const uint8_t *buf, unsigned size) +{ + g_assert(size <= SPI_TPM_READ_FIFO_SIZE_BYTES); + uint8_t tx_buf[SPI_TPM_READ_FIFO_SIZE_BYTES] = { 0u }; + + SpiDeviceTpm *tpm = &s->tpm; + switch (tpm->state) { + case SPI_TPM_IDLE: + ot_spi_device_tpm_idle_state(s, buf, size); + break; + case SPI_TPM_ADDR: + ot_spi_device_tpm_addr_state(s, buf, size); + break; + case SPI_TPM_WRITE: + ot_spi_device_tpm_write_state(s, buf, size); + break; + case SPI_TPM_READ: + ot_spi_device_tpm_read_state(s, size, tx_buf); + break; + case SPI_TPM_READ_HW_REG: + /* @todo: implement hw register.*/ + tpm->state = SPI_TPM_IDLE; + break; + case SPI_TPM_WAIT: + ot_spi_device_tpm_wait_state(s); + break; + case SPI_TPM_START_BYTE: + ot_spi_device_tpm_start_byte_state(s, size, tx_buf); + break; + case SPI_TPM_END: + /* Wait for the cs de-assertion.*/ + s->bus.byte_count = 0u; + tpm->state = SPI_TPM_IDLE; + return; + default: + break; + }; + if (qemu_chr_fe_backend_connected(&s->chr)) { + qemu_chr_fe_write(&s->chr, tx_buf, (int)size); + } + s->bus.byte_count -= size; + tpm->can_receive = tpm->state == SPI_TPM_IDLE ? 1u : tpm->can_receive; +} + static int ot_spi_device_chr_can_receive(void *opaque) { OtSPIDeviceState *s = opaque; SpiDeviceBus *bus = &s->bus; - unsigned length; + unsigned length = 0u; switch (bus->state) { case SPI_BUS_IDLE: @@ -2004,6 +2244,9 @@ static int ot_spi_device_chr_can_receive(void *opaque) case SPI_BUS_FLASH: length = timer_pending(s->flash.irq_timer) ? 0 : 1u; break; + case SPI_BUS_TPM: + length = s->tpm.can_receive; + break; case SPI_BUS_DISCARD: length = 1u; break; @@ -2039,6 +2282,9 @@ static void ot_spi_device_chr_receive(void *opaque, const uint8_t *buf, case SPI_BUS_FLASH: ot_spi_device_chr_recv_flash(s, buf, (unsigned)size); break; + case SPI_BUS_TPM: + ot_spi_device_tpm_state_machine(s, buf, (unsigned)size); + break; case SPI_BUS_DISCARD: case SPI_BUS_ERROR: ot_spi_device_chr_recv_discard(s, buf, (unsigned)size); @@ -2225,6 +2471,7 @@ static void ot_spi_device_init(Object *obj) fifo8_create(&bus->chr_fifo, SPI_BUS_HEADER_SIZE); fifo8_create(&f->cmd_fifo, SPI_SRAM_CMD_SIZE / sizeof(uint32_t)); + fifo8_create(&s->tpm.rdfifo, SPI_TPM_READ_FIFO_SIZE_BYTES); ot_fifo32_create(&f->address_fifo, SPI_SRAM_ADDR_SIZE / sizeof(uint32_t)); f->buffer = (uint8_t *)g_new0(uint32_t, SPI_FLASH_BUFFER_SIZE / sizeof(uint32_t));