[PATCH v4 05/23] x86/tpm.c: add CRB interface support

Sergii Dmytruk <[email protected]> Sun, 2 Aug 2026 16:09:21 +0300
Newsgroups gmane.comp.emulators.xen.devel
Message-ID <5d71b306b8b6162cc434e8bfe16ed5dd1da068e8.1785668458.git.sergii.dmytruk@3mdeb.com>
From: Szymon Acedański <[email protected]>

TIS is an older and slower byte-oriented TPM interface that gets
replaced by CRB on modern systems.

Signed-off-by: Szymon Acedański <[email protected]>
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Sergii Dmytruk <[email protected]>
---

Notes:
    v4: new commit to support CRB interface of TPM2.0

 xen/arch/x86/tpm.c | 134 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 131 insertions(+), 3 deletions(-)

diff --git a/xen/arch/x86/tpm.c b/xen/arch/x86/tpm.c
index 59bb1ff2c4..a3d9a0d3e5 100644
--- a/xen/arch/x86/tpm.c
+++ b/xen/arch/x86/tpm.c
@@ -72,6 +72,11 @@ static uint8_t tpm_read8(unsigned int reg)
     return *(volatile uint8_t *)__va(TPM_MMIO_BASE + reg);
 }
 
+static void tpm_write32(unsigned int reg, uint32_t val)
+{
+    *(volatile uint32_t *)__va(TPM_MMIO_BASE + reg) = val;
+}
+
 static void tpm_write8(unsigned int reg, uint8_t val)
 {
     *(volatile uint8_t *)__va(TPM_MMIO_BASE + reg) = val;
@@ -110,6 +115,31 @@ static bool tpm_is_crb(void)
 #define TIS_BURST_COUNT_(x)     TPM_LOC_REG(x, 0x19)  /* the middle of STS */
 #define TIS_DATA_FIFO_(x)       TPM_LOC_REG(x, 0x24)
 
+/************************** CRB register definitions **************************/
+
+#define CRB_LOC_STATE_(x)       TPM_LOC_REG(x, 0x00)
+#define CRB_LOC_STATE_LOC_ASSIGNED   (1 << 1)
+#define CRB_LOC_STATE_REG_VALID_STS  (1 << 7)
+#define CRB_LOC_CTRL_(x)        TPM_LOC_REG(x, 0x08)
+#define CRB_LOC_CTRL_REQUEST_ACCESS  (1 << 0)
+#define CRB_LOC_CTRL_RELINQUISH      (1 << 1)
+#define CRB_CTRL_REQ_(x)        TPM_LOC_REG(x, 0x40)
+#define CRB_CTRL_REQ_CMD_READY       (1 << 0)
+#define CRB_CTRL_REQ_GO_IDLE         (1 << 1)
+#define CRB_CTRL_STS_(x)        TPM_LOC_REG(x, 0x44)
+#define CRB_CTRL_STS_ERROR           (1 << 0)
+#define CRB_CTRL_CANCEL_(x)     TPM_LOC_REG(x, 0x48)
+#define CRB_CTRL_CANCEL_INVOKE       (1 << 0)
+#define CRB_CTRL_START_(x)      TPM_LOC_REG(x, 0x4C)
+#define CRB_CTRL_START_INVOKE        (1 << 0)
+#define CRB_CTRL_CMD_SIZE_(x)   TPM_LOC_REG(x, 0x58)
+#define CRB_CTRL_CMD_LADDR_(x)  TPM_LOC_REG(x, 0x5C)
+#define CRB_CTRL_CMD_HADDR_(x)  TPM_LOC_REG(x, 0x60)
+#define CRB_CTRL_RSP_SIZE_(x)   TPM_LOC_REG(x, 0x64)
+#define CRB_CTRL_RSP_ADDR_(x)   TPM_LOC_REG(x, 0x68)
+#define CRB_DATA_BUFFER_(x)     TPM_LOC_REG(x, 0x80)
+#define CRB_DATA_BUFFER_SIZE    0x0F80
+
 /************************** TIS locality & command ****************************/
 
 static void tis_request_locality(unsigned int loc)
@@ -195,12 +225,110 @@ static void tis_send_cmd(unsigned int loc, uint8_t *buf, unsigned int i_size,
     tpm_write8(TIS_STS_(loc), STS_COMMAND_READY);
 }
 
+/************************** CRB locality & command ****************************/
+
+static void crb_request_locality(unsigned int loc)
+{
+    const uint32_t mask = CRB_LOC_STATE_LOC_ASSIGNED |
+                          CRB_LOC_STATE_REG_VALID_STS;
+
+    tpm_write32(CRB_LOC_CTRL_(loc), CRB_LOC_CTRL_REQUEST_ACCESS);
+    while ( (tpm_read32(CRB_LOC_STATE_(loc)) & mask) != mask )
+        ;
+}
+
+static void crb_relinquish_locality(unsigned int loc)
+{
+    tpm_write32(CRB_LOC_CTRL_(loc), CRB_LOC_CTRL_RELINQUISH);
+    while ( tpm_read32(CRB_LOC_STATE_(loc)) & CRB_LOC_STATE_LOC_ASSIGNED )
+        ;
+}
+
+static void crb_cmd_ready(unsigned int loc)
+{
+    tpm_write32(CRB_CTRL_REQ_(loc), CRB_CTRL_REQ_CMD_READY);
+    while ( tpm_read32(CRB_CTRL_REQ_(loc)) & CRB_CTRL_REQ_CMD_READY )
+        ;
+}
+
+static void crb_go_idle(unsigned int loc)
+{
+    tpm_write32(CRB_CTRL_REQ_(loc), CRB_CTRL_REQ_GO_IDLE);
+    while ( tpm_read32(CRB_CTRL_REQ_(loc)) & CRB_CTRL_REQ_GO_IDLE )
+        ;
+}
+
+static void crb_send_cmd(unsigned int loc, uint8_t *buf, unsigned int i_size,
+                         unsigned int *o_size)
+{
+    paddr_t data_buf_pa = TPM_MMIO_BASE + CRB_DATA_BUFFER_(loc);
+    unsigned int expected;
+
+    if ( i_size > CRB_DATA_BUFFER_SIZE || *o_size < sizeof(struct tpm_rsp_hdr) )
+    {
+        *o_size = 0;
+        return;
+    }
+
+    /* Out of caution, make sure no previous command is still executing. */
+    while ( tpm_read32(CRB_CTRL_START_(loc)) & CRB_CTRL_START_INVOKE )
+        ;
+
+    crb_cmd_ready(loc);
+
+    /* In an unlikely event that TPM signals irrecoverable error here,
+     * better bail out than hang in infinite loop waiting for the
+     * start condition later. */
+    if ( tpm_read32(CRB_CTRL_STS_(loc)) & CRB_CTRL_STS_ERROR )
+    {
+        *o_size = 0;
+        crb_go_idle(loc);
+        return;
+    }
+
+    tpm_write32(CRB_CTRL_CANCEL_(loc), 0);
+
+    tpm_write32(CRB_CTRL_CMD_LADDR_(loc), data_buf_pa);
+    tpm_write32(CRB_CTRL_CMD_HADDR_(loc), 0);
+    tpm_write32(CRB_CTRL_CMD_SIZE_(loc), CRB_DATA_BUFFER_SIZE);
+    tpm_write32(CRB_CTRL_RSP_SIZE_(loc), CRB_DATA_BUFFER_SIZE);
+    /* RSP_ADDR is 64-bit. */
+    tpm_write32(CRB_CTRL_RSP_ADDR_(loc), data_buf_pa);
+    tpm_write32(CRB_CTRL_RSP_ADDR_(loc) + 4, 0);
+
+    memcpy(__va(data_buf_pa), buf, i_size);
+
+    tpm_write32(CRB_CTRL_START_(loc), CRB_CTRL_START_INVOKE);
+    while ( tpm_read32(CRB_CTRL_START_(loc)) & CRB_CTRL_START_INVOKE )
+        ;
+
+    if ( tpm_read32(CRB_CTRL_STS_(loc)) & CRB_CTRL_STS_ERROR )
+    {
+        *o_size = 0;
+        crb_go_idle(loc);
+        return;
+    }
+
+    /* Read header to learn the response length. */
+    memcpy(buf, __va(data_buf_pa), sizeof(struct tpm_rsp_hdr));
+    expected = be32_to_cpu(((struct tpm_rsp_hdr *)buf)->paramSize);
+    if ( expected > *o_size )
+        expected = *o_size;
+    if ( expected > CRB_DATA_BUFFER_SIZE )
+        expected = CRB_DATA_BUFFER_SIZE;
+
+    memcpy(buf, __va(data_buf_pa), expected);
+
+    *o_size = expected;
+    crb_go_idle(loc);
+}
+
 /************************** Interface dispatch ********************************/
 
 static void request_locality(unsigned int loc)
 {
     if ( tpm_is_crb() )
-        return;
+        crb_request_locality(loc);
     else
         tis_request_locality(loc);
 }
@@ -208,7 +336,7 @@ static void request_locality(unsigned int loc)
 static void relinquish_locality(unsigned int loc)
 {
     if ( tpm_is_crb() )
-        return;
+        crb_relinquish_locality(loc);
     else
         tis_relinquish_locality(loc);
 }
@@ -217,7 +345,7 @@ static void send_cmd(unsigned int loc, uint8_t *buf, unsigned int i_size,
                      unsigned int *o_size)
 {
     if ( tpm_is_crb() )
-        *o_size = 0;
+        crb_send_cmd(loc, buf, i_size, o_size);
     else
         tis_send_cmd(loc, buf, i_size, o_size);
 }
-- 
2.55.0