git: 7c375af72ef3 - main - tpm: crb: check the error bit only after taking locality

From: Olivier Cochard <olivier_at_FreeBSD.org>
Date: Mon, 14 Sep 2026 14:28:44 UTC
The branch main has been updated by olivier:

URL: https://cgit.FreeBSD.org/src/commit/?id=7c375af72ef3f631e89431ae32a855d806e6ec98

commit 7c375af72ef3f631e89431ae32a855d806e6ec98
Author:     Olivier Cochard <olivier@FreeBSD.org>
AuthorDate: 2026-09-14 14:21:11 +0000
Commit:     Olivier Cochard <olivier@FreeBSD.org>
CommitDate: 2026-09-14 14:25:47 +0000

    tpm: crb: check the error bit only after taking locality
    
    tpmcrb_transmit() read CRB_CTRL_STS before requesting locality 0.
    An AMD Pluton fTPM (like FrameWork Desktop) using the plain CRB start method
    reads the control area as all-ones until locality is assigned, so bit 0 looks
    like a stuck tpmSts and every command failed with EIO.
    
    With RANDOM_ENABLE_TPM the harvester retries every 10 seconds, so this printed
    "Device has Error bit set" forever.
    
    Reviewed by:    kbowling
    Approved by:    kbowling
    MFC after:      2 weeks
    Sponsored by:   Netflix
    Differential Revision:  https://reviews.freebsd.org/D59661
---
 sys/dev/tpm/tpm_crb.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/sys/dev/tpm/tpm_crb.c b/sys/dev/tpm/tpm_crb.c
index e5bbec5d46da..f62d350948c8 100644
--- a/sys/dev/tpm/tpm_crb.c
+++ b/sys/dev/tpm/tpm_crb.c
@@ -514,17 +514,25 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
 		return (E2BIG);
 	}
 
-	if (TPM_READ_4(dev, TPM_CRB_CTRL_STS) & TPM_CRB_CTRL_STS_ERR_BIT) {
-		device_printf(dev,
-		    "Device has Error bit set\n");
-		return (EIO);
-	}
 	if (!tpmcrb_request_locality(sc, 0)) {
 		device_printf(dev,
 		    "Failed to obtain locality\n");
 		return (EIO);
 	}
 	locality = true;
+
+	/*
+	 * Only check for the error once we own the locality: some
+	 * implementations do not return meaningful values in the control area
+	 * before then.  An AMD Pluton fTPM using the plain CRB start method
+	 * reads all-ones, which looks like a permanently stuck tpmSts.
+	 */
+	if (TPM_READ_4(dev, TPM_CRB_CTRL_STS) & TPM_CRB_CTRL_STS_ERR_BIT) {
+		device_printf(dev,
+		    "Device has Error bit set\n");
+		error = EIO;
+		goto out;
+	}
 	/* Clear cancellation bit */
 	TPM_WRITE_4(dev, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CLEAR);