git: 2fe8510b0a06 - main - tpm20: Release transport state after command failures
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 03 Sep 2026 04:01:52 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=2fe8510b0a06ad577789ece7be8c9319c8ce3d06
commit 2fe8510b0a06ad577789ece7be8c9319c8ce3d06
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-27 12:48:05 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-09-03 04:00:45 +0000
tpm20: Release transport state after command failures
Once a transport acquires locality, several TIS and CRB error paths
return without relinquishing it. They can also leave a partial FIFO
transaction or an active CRB command for the next operation to inherit.
Route post-locality exits through common cleanup. Reset the TIS command
state on every attempt. For CRB, cancel an active failed command when
necessary, request the idle state, and relinquish locality even when the
state transition itself fails.
Successful command handling is unchanged apart from sharing the same
cleanup path.
Reviewed by: kevans
MFC after: 2 weeks
Sponsored by: BBOX.io
Differential Revision: https://reviews.freebsd.org/D59243
---
sys/dev/tpm/tpm_crb.c | 57 ++++++++++++++++++++++++++---------------
sys/dev/tpm/tpm_tis_core.c | 63 +++++++++++++++++++++++++++++-----------------
2 files changed, 77 insertions(+), 43 deletions(-)
diff --git a/sys/dev/tpm/tpm_crb.c b/sys/dev/tpm/tpm_crb.c
index ac093c3857ba..3e567e215313 100644
--- a/sys/dev/tpm/tpm_crb.c
+++ b/sys/dev/tpm/tpm_crb.c
@@ -484,12 +484,15 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
struct tpmcrb_sc *crb_sc;
struct tpm_sc *sc;
uint32_t mask, curr_cmd;
- int timeout, bytes_available;
+ int bytes_available, error, timeout;
+ bool command_started, locality;
crb_sc = device_get_softc(dev);
sc = &crb_sc->base;
sx_assert(&sc->dev_lock, SA_XLOCKED);
+ command_started = false;
+ locality = false;
if (length > crb_sc->cmd_buf_size) {
device_printf(dev,
@@ -507,6 +510,7 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
"Failed to obtain locality\n");
return (EIO);
}
+ locality = true;
/* Clear cancellation bit */
TPM_WRITE_4(dev, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CLEAR);
@@ -515,7 +519,8 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
if (!tpmcrb_state_idle(crb_sc, true)) {
device_printf(dev,
"Failed to transition to idle state\n");
- return (EIO);
+ error = EIO;
+ goto out;
}
}
@@ -523,7 +528,8 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
if (!tpmcrb_state_ready(crb_sc, true)) {
device_printf(dev,
"Failed to transition to ready state\n");
- return (EIO);
+ error = EIO;
+ goto out;
}
/*
@@ -540,20 +546,25 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
TPM_WRITE_4(dev, TPM_CRB_CTRL_START, TPM_CRB_CTRL_START_CMD);
TPM_WRITE_BARRIER(dev, TPM_CRB_CTRL_START, 4);
+ command_started = true;
if (!tpmcrb_notify_cmdready(crb_sc, timeout)) {
device_printf(dev,
"Timeout while waiting for device to ready\n");
- if (!tpmcrb_cancel_cmd(sc))
- return (EIO);
+ if (!tpmcrb_cancel_cmd(sc)) {
+ error = EIO;
+ goto out;
+ }
}
mask = ~0;
if (!tpm_wait_for_u32(sc, TPM_CRB_CTRL_START, mask, ~mask, timeout)) {
device_printf(dev,
"Timeout while waiting for device to process cmd\n");
- if (!tpmcrb_cancel_cmd(sc))
- return (EIO);
+ if (!tpmcrb_cancel_cmd(sc)) {
+ error = EIO;
+ goto out;
+ }
}
/* Read response header. Length is passed in bytes 2 - 6. */
@@ -565,27 +576,33 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
device_printf(dev,
"Incorrect response size: %d\n",
bytes_available);
- return (EIO);
+ error = EIO;
+ goto out;
}
bus_read_region_stream_1(sc->mem_res, crb_sc->rsp_off + TPM_HEADER_SIZE,
&priv->buf[TPM_HEADER_SIZE], bytes_available - TPM_HEADER_SIZE);
- /*
- * No need to wait for the transition to idle on the way out, we can
- * relinquish locality right away.
- */
- if (!tpmcrb_state_idle(crb_sc, false)) {
- device_printf(dev,
- "Failed to transition to idle state post-send\n");
- return (EIO);
- }
-
- tpmcrb_relinquish_locality(sc);
priv->offset = 0;
priv->len = bytes_available;
+ error = 0;
+
+out:
+ if (locality) {
+ if (error != 0 && command_started &&
+ (TPM_READ_4(dev, TPM_CRB_CTRL_START) &
+ TPM_CRB_CTRL_START_CMD) != 0)
+ (void)tpmcrb_cancel_cmd(sc);
+ if (!tpmcrb_state_idle(crb_sc, false)) {
+ device_printf(dev,
+ "Failed to transition to idle state post-send\n");
+ if (error == 0)
+ error = EIO;
+ }
+ tpmcrb_relinquish_locality(sc);
+ }
- return (0);
+ return (error);
}
/* StartMethod Implementation Details */
diff --git a/sys/dev/tpm/tpm_tis_core.c b/sys/dev/tpm/tpm_tis_core.c
index 08d37ebcfe19..785c887e58cf 100644
--- a/sys/dev/tpm/tpm_tis_core.c
+++ b/sys/dev/tpm/tpm_tis_core.c
@@ -487,25 +487,30 @@ tpmtis_transmit(device_t dev, struct tpm_priv *priv, size_t length)
struct tpm_sc *sc;
size_t bytes_available;
uint32_t mask, curr_cmd;
- int timeout;
+ int error, timeout;
+ bool locality;
sc = device_get_softc(dev);
sx_assert(&sc->dev_lock, SA_XLOCKED);
+ locality = false;
if (!tpmtis_request_locality(sc, 0)) {
device_printf(dev,
"Failed to obtain locality\n");
return (EIO);
}
+ locality = true;
if (!tpmtis_go_ready(sc)) {
device_printf(dev,
"Failed to switch to ready state\n");
- return (EIO);
+ error = EIO;
+ goto out;
}
if (!tpmtis_write_bytes(sc, length, priv->buf)) {
device_printf(dev,
"Failed to write cmd to device\n");
- return (EIO);
+ error = EIO;
+ goto out;
}
mask = TPM_STS_VALID;
@@ -513,13 +518,15 @@ tpmtis_transmit(device_t dev, struct tpm_priv *priv, size_t length)
TPM_INT_STS_VALID, true)) {
device_printf(dev,
"Timeout while waiting for valid bit\n");
- return (EIO);
+ error = EIO;
+ goto out;
}
if (TPM_READ_4(dev, TPM_STS) & TPM_STS_DATA_EXPECTED) {
device_printf(dev,
"Device expects more data even though we already"
" sent everything we had\n");
- return (EIO);
+ error = EIO;
+ goto out;
}
/*
@@ -541,22 +548,27 @@ tpmtis_transmit(device_t dev, struct tpm_priv *priv, size_t length)
* Switching to ready state also cancels processing
* current command
*/
- if (!tpmtis_go_ready(sc))
- return (EIO);
+ if (!tpmtis_go_ready(sc)) {
+ error = EIO;
+ goto out;
+ }
/*
* After canceling a command we should get a response,
* check if there is one.
*/
if (!tpm_wait_for_reg(sc, TPM_STS, mask, mask, TPM_TIMEOUT_C,
- TPM_INT_STS_DATA_AVAIL, true))
- return (EIO);
+ TPM_INT_STS_DATA_AVAIL, true)) {
+ error = EIO;
+ goto out;
+ }
}
/* Read response header. Length is passed in bytes 2 - 6. */
if (!tpmtis_read_bytes(sc, TPM_HEADER_SIZE, priv->buf)) {
device_printf(dev,
"Failed to read response header\n");
- return (EIO);
+ error = EIO;
+ goto out;
}
bytes_available = be32toh(*(uint32_t *) (&priv->buf[2]));
@@ -564,29 +576,34 @@ tpmtis_transmit(device_t dev, struct tpm_priv *priv, size_t length)
device_printf(dev,
"Incorrect response size: %zu\n",
bytes_available);
- return (EIO);
+ error = EIO;
+ goto out;
}
if (!tpmtis_read_bytes(sc, bytes_available - TPM_HEADER_SIZE,
&priv->buf[TPM_HEADER_SIZE])) {
device_printf(dev,
"Failed to read response\n");
- return (EIO);
+ error = EIO;
+ goto out;
}
+ priv->offset = 0;
+ priv->len = bytes_available;
+ error = 0;
+out:
/*
- * Per TIS 1.3 section 5.6.12, write commandReady after reading the
- * response so the TPM can free the ReadFIFO and other internal
- * resources. The next tpmtis_go_ready() provides the second
- * write the spec mentions, and waits for the state transition,
- * so no wait is needed here.
+ * Per TIS 1.3 section 5.6.12, write commandReady after every command
+ * attempt so the TPM can discard a partial FIFO transaction and free
+ * its internal resources. The next tpmtis_go_ready() provides the
+ * second write the spec mentions and waits for the state transition.
*/
- TPM_WRITE_4(sc->dev, TPM_STS, TPM_STS_CMD_RDY);
- TPM_WRITE_BARRIER(sc->dev, TPM_STS, 4);
- tpmtis_relinquish_locality(sc);
- priv->offset = 0;
- priv->len = bytes_available;
+ if (locality) {
+ TPM_WRITE_4(sc->dev, TPM_STS, TPM_STS_CMD_RDY);
+ TPM_WRITE_BARRIER(sc->dev, TPM_STS, 4);
+ tpmtis_relinquish_locality(sc);
+ }
- return (0);
+ return (error);
}
/* ACPI Driver */