diff --git a/docs/opentitan/spi_device.md b/docs/opentitan/spi_device.md index 2fcdfb9f34c9..e8dc2f213b47 100644 --- a/docs/opentitan/spi_device.md +++ b/docs/opentitan/spi_device.md @@ -17,7 +17,9 @@ This mode is not yet supported. ### TPM -This mode is not supported. +This mode is partially supported, TPM commands handled by hw are not supported yet. The CharDev +protocol doesn't support a distinct chip select for TPM, therefore it is sharing the same CS with +the other modes. If CS is asserted and TPM is enabled, then it will have priority. ## Connection with a SPI Host diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index 1edc2fa6154d..c718128d46fb 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 @@ -230,12 +228,12 @@ 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 #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 +250,67 @@ 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_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_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 +#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_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) #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 @@ -340,6 +341,11 @@ typedef enum { #define SPI_DEVICE_CMD_SW_LAST (SPI_DEVICE_CMD_HW_CFG_FIRST - 1u) #define SPI_DEVICE_CMD_SW_COUNT \ (SPI_DEVICE_CMD_SW_LAST - SPI_DEVICE_CMD_SW_FIRST + 1u) +#define TPM_OPCODE_READ_BIT 7u +#define TPM_OPCODE_SIZE_MASK 0x3Fu +#define TPM_ADDR_HEADER 0xD4u +#define TPM_READY 0x01u + static_assert((SPI_DEVICE_CMD_HW_STA_COUNT + SPI_DEVICE_CMD_SW_COUNT + SPI_DEVICE_CMD_HW_CFG_COUNT) == 28u, @@ -347,6 +353,10 @@ static_assert((SPI_DEVICE_CMD_HW_STA_COUNT + SPI_DEVICE_CMD_SW_COUNT + static_assert(PARAM_NUM_CMD_INFO == SPI_DEVICE_CMD_HW_CFG_FIRST - SPI_DEVICE_CMD_HW_STA_FIRST, "Invalid command info definitions"); +static_assert(SPI_SRAM_INGRESS_OFFSET >= + (SPI_SRAM_TPM_READ_OFFSET + SPI_SRAM_TPM_READ_SIZE), + "SPI SRAM Egress buffers overflow into Ingress buffers"); +static_assert(SPI_SRAM_END_OFFSET == 0xfc0u, "Invalid SRAM definition"); typedef enum { CTRL_MODE_DISABLED, @@ -365,6 +375,7 @@ typedef enum { typedef enum { SPI_BUS_IDLE, SPI_BUS_FLASH, + SPI_BUS_TPM, SPI_BUS_DISCARD, SPI_BUS_ERROR, } OtSpiBusState; @@ -388,6 +399,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 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; @@ -408,6 +430,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; @@ -438,6 +475,7 @@ struct OtSPIDeviceState { SpiDeviceBus bus; SpiDeviceFlash flash; + SpiDeviceTpm tpm; uint32_t *spi_regs; /* Registers */ uint32_t *tpm_regs; /* Registers */ @@ -464,7 +502,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_) \ @@ -551,7 +589,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 @@ -600,6 +637,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), }; @@ -711,9 +749,19 @@ 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); + 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]; + fifo8_reset(&tpm->rdfifo); + memset(s->sram, 0u, SRAM_SIZE); } @@ -749,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); @@ -801,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)) { @@ -1588,8 +1661,12 @@ static void ot_spi_device_spi_regs_write(void *opaque, hwaddr addr, val32 &= INTR_MASK & ~(INTR_TPM_HEADER_NOT_EMPTY_MASK); s->spi_regs[reg] &= ~val32; /* 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); @@ -1733,9 +1810,16 @@ 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; + ot_spi_device_update_irqs(s); + /* 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: @@ -1745,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]; @@ -1755,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); @@ -1795,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: + val32 &= R_TPM_STATUS_WRFIFO_PENDING_MASK; + 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)); @@ -1822,27 +1910,34 @@ static MemTxResult ot_spi_device_buf_read_with_attrs( (void)attrs; uint32_t val32; - hwaddr last = addr + size - 1u; + hwaddr last = addr + (hwaddr)(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 +1965,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 = addr + (hwaddr)(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); @@ -1883,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); @@ -1925,6 +2026,13 @@ static void ot_spi_device_chr_handle_header(OtSPIDeviceState *s) return; } + /* @todo: Check that the tpm chip-select was assigned.*/ + 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); @@ -1977,11 +2085,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, /* 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: @@ -1990,6 +2245,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; @@ -2025,6 +2283,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); @@ -2211,6 +2472,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));