diff options
| author | Olivier Cochard <olivier@FreeBSD.org> | 2026-11-04 21:05:22 +0000 |
|---|---|---|
| committer | Olivier Cochard <olivier@FreeBSD.org> | 2026-11-05 19:51:20 +0000 |
| commit | bfe4be85a9354b92135e105028f2e1b3e0307ada (patch) | |
| tree | be840cbb1e5bf5161cab5a62655d45ee62d62c30 | |
| parent | 843cb77850d991c349fd384fee6402b6af5d7f74 (diff) | |
tpm_crb: wait for idle and locality release after each command
transmit only requested GO_IDLE and the locality release, so a back-to-back
command could see stale state and fail with EIO.
A post-send idle timeout no longer discards a completed response.
Reviewed by: kbowling
Approved by: kbowling
Differential Revision: https://ron-dev.freebsd.org/FreeBSD/src/pulls/93
| -rw-r--r-- | sys/dev/tpm/tpm_crb.c | 39 |
1 files changed, 32 insertions, 7 deletions
diff --git a/sys/dev/tpm/tpm_crb.c b/sys/dev/tpm/tpm_crb.c index df47f88f79fd..4e6f550e473c 100644 --- a/sys/dev/tpm/tpm_crb.c +++ b/sys/dev/tpm/tpm_crb.c @@ -552,10 +552,33 @@ tpmcrb_request_locality(struct tpmcrb_sc *crb_sc, int locality) static void tpmcrb_relinquish_locality(struct tpmcrb_sc *crb_sc) { + struct resource *res = crb_sc->base.mem_res; + uint32_t mask; + int timeout; - if (crb_sc->has_locality) - bus_write_4(crb_sc->base.mem_res, TPM_LOC_CTRL, - TPM_LOC_CTRL_RELINQUISH); + if (!crb_sc->has_locality) + return; + + /* PTP 1.07, Table 37: the other fields mean something only if valid. */ + mask = TPM_LOC_STATE_VALID | TPM_LOC_STATE_ASSIGNED; + + bus_write_4(res, TPM_LOC_CTRL, TPM_LOC_CTRL_RELINQUISH); + + /* + * Wait for the TPM to act on the request. Otherwise a following + * tpmcrb_request_locality() can see the stale assignment and proceed + * just before the locality goes away. Read once more after the last + * pause so a release during it is not reported as a failure. + */ + for (timeout = TPM_TIMEOUT_C;; timeout -= tick) { + if ((bus_read_4(res, TPM_LOC_STATE) & mask) == + TPM_LOC_STATE_VALID) + return; + if (timeout <= 0) + break; + pause("TPM locality", 1); + } + device_printf(crb_sc->base.dev, "Failed to relinquish locality\n"); } static bool @@ -783,12 +806,14 @@ out: (tpmcrb_ctrl_read_4(crb_sc, TPM_CRB_CTRL_START) & TPM_CRB_CTRL_START_CMD) != 0) (void)tpmcrb_cancel_cmd(crb_sc); - if (!tpmcrb_state_idle(crb_sc, false)) { + /* + * Let the TPM finish going idle before giving up the locality. + * The next transmit requests idle again if needed, so a timeout + * here must not discard the response of a completed command. + */ + if (!tpmcrb_state_idle(crb_sc, true)) device_printf(dev, "Failed to transition to idle state post-send\n"); - if (error == 0) - error = EIO; - } tpmcrb_relinquish_locality(crb_sc); } |
