Some TPM implementations, particularly fTPM using the CRB interface, require the TPM to transition through idle and ready states for each command rather than once per session.
According to the TCG PC Client Platform TPM Profile (PTP) specification, the CRB interface includes an idle/ready handshake for power management. Software should set cmdReady to transition the TPM from idle to ready state before sending a command, and set goIdle when finished to allow the TPM to enter a low-power state. The current implementation calls cmd_ready once during tpm_chip_start() and go_idle once during tpm_chip_stop(). During the startup sequence, multiple commands are sent between these calls (self-test, get capabilities, session init). For fTPM, when subsequent commands are sent without transitioning to idle after the previous command completes, those commands timeout as the TPM is waiting for the idle transition. Fix this by calling cmd_ready before each command transmission and go_idle after receiving each response in tpm_try_transmit(). For TPM implementations that don't require per-command transitions, the callbacks can return immediately (as many already do based on the start method). This resolves timeout errors during TPM initialization on systems where BIOS has already performed TPM startup, as the kernel can now properly communicate with the fTPM during auto-startup. Signed-off-by: Mario Limonciello <[email protected]> --- drivers/char/tpm/tpm-interface.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index f745a098908b3..a98ddca876152 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -113,6 +113,15 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) return -E2BIG; } + if (chip->ops->cmd_ready) { + rc = chip->ops->cmd_ready(chip); + if (rc) { + dev_err(&chip->dev, + "%s: cmd_ready(): error %d\n", __func__, rc); + return rc; + } + } + rc = chip->ops->send(chip, buf, bufsiz, count); if (rc < 0) { if (rc != -EPIPE) @@ -181,6 +190,12 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length)) rc = -EFAULT; + if (chip->ops->go_idle) { + int idle_rc = chip->ops->go_idle(chip); + if (idle_rc && !rc) + rc = idle_rc; + } + return rc ? rc : len; } -- 2.43.0
