git: 4e0f283fb97a - main - tpm: Bound TPM 1.2 locality ownership
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 01 Sep 2026 18:24:25 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=4e0f283fb97a7b0903adc7bc3ef2f6e07167c73e
commit 4e0f283fb97a7b0903adc7bc3ef2f6e07167c73e
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-27 13:42:49 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-09-01 18:23:36 +0000
tpm: Bound TPM 1.2 locality ownership
A TIS locality must remain active while a command is in flight, but
should be relinquished once the command completes or is abandoned. The
driver retained locality zero after probe, initialization, and resume,
and several transaction error paths returned without releasing it.
Closing the device after writing a command without reading its response
had the same effect.
Track locality ownership and whether a command is awaiting its response.
Release locality after probe, initialization, and resume; retain it only
across a successful command write and its matching response read. Abort
and release on errors, replacement commands, close, and detach.
Wait for locality during ISA probe instead of assuming an immediate
grant, release locality acquired by the probe, and stop treating the
command-style TPM_ACCESS register as restorable state.
On a ThinkPad T440p with an STMicro TPM 1.2, the old driver left
TPM_ACCESS at 0xa1 immediately after attach. The new driver left it at
0x81 after attach, completed PCR reads, and closing with an unread
response. PCR reads also survived an unload and reload without TPM or
locking diagnostics.
Reviewed by: kevants
MFC after: 2 weeks
Sponsored by: BBOX.io
Differential Revision: https://reviews.freebsd.org/D59237
---
sys/dev/tpm/tpm.c | 171 +++++++++++++++++++++++++++++++++++++++------------
sys/dev/tpm/tpmvar.h | 2 +
2 files changed, 134 insertions(+), 39 deletions(-)
diff --git a/sys/dev/tpm/tpm.c b/sys/dev/tpm/tpm.c
index 8c12abb39d1a..f37ce6955f17 100644
--- a/sys/dev/tpm/tpm.c
+++ b/sys/dev/tpm/tpm.c
@@ -194,7 +194,9 @@ int tpm_legacy_write(struct tpm_softc *, void *, int);
int tpm_legacy_end(struct tpm_softc *, int, int);
static int tpm_transmit_header(struct tpm_softc *, uint32_t, uint32_t *);
+static void tpm_tis12_abort(struct tpm_softc *);
static int tpm_tis12_devid_index(uint32_t);
+static void tpm_tis12_relinquish_locality(struct tpm_softc *);
static int tpm_tis12_resume(struct tpm_softc *);
@@ -225,6 +227,8 @@ tpm_attach(device_t dev)
sc->sc_flags = 0;
sc->sc_suspend = 0;
sc->sc_dying = false;
+ sc->sc_locality = false;
+ sc->sc_command_pending = false;
sc->mem_rid = 0;
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
@@ -245,9 +249,6 @@ tpm_attach(device_t dev)
else
irq = IRQUNK;
- /* In case PnP probe this may contain some initialization. */
- tpm_tis12_probe(sc->sc_bt, sc->sc_bh);
-
if (tpm_legacy_probe(sc->sc_bt, sc->sc_bh)) {
sc->sc_init = tpm_legacy_init;
sc->sc_start = tpm_legacy_start;
@@ -315,6 +316,10 @@ tpm_detach(device_t dev)
destroy_dev(sc->sc_cdev);
sc->sc_cdev = NULL;
}
+ sx_xlock(&sc->sc_lock);
+ if (sc->mem_res != NULL && sc->sc_init == tpm_tis12_init)
+ tpm_tis12_abort(sc);
+ sx_xunlock(&sc->sc_lock);
if (sc->intr_cookie != NULL) {
bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
sc->intr_cookie = NULL;
@@ -341,7 +346,9 @@ int
tpm_tis12_probe(bus_space_tag_t bt, bus_space_handle_t bh)
{
u_int32_t r;
- u_int8_t save, reg;
+ u_int8_t reg;
+ bool acquired;
+ int to;
r = bus_space_read_4(bt, bh, TPM_INTF_CAPABILITIES);
if (r == 0xffffffff)
@@ -358,15 +365,34 @@ tpm_tis12_probe(bus_space_tag_t bt, bus_space_handle_t bh)
return 0;
}
- save = bus_space_read_1(bt, bh, TPM_ACCESS);
- bus_space_write_1(bt, bh, TPM_ACCESS, TPM_ACCESS_REQUEST_USE);
reg = bus_space_read_1(bt, bh, TPM_ACCESS);
- if ((reg & TPM_ACCESS_VALID) && (reg & TPM_ACCESS_ACTIVE_LOCALITY) &&
- bus_space_read_4(bt, bh, TPM_ID) != 0xffffffff)
- return 1;
+ acquired = false;
+ if ((reg & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) !=
+ (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) {
+ bus_space_write_1(bt, bh, TPM_ACCESS,
+ TPM_ACCESS_REQUEST_USE);
+ to = TPM_ACCESS_TMO; /* Steps of one millisecond. */
+ do {
+ reg = bus_space_read_1(bt, bh, TPM_ACCESS);
+ if ((reg & (TPM_ACCESS_VALID |
+ TPM_ACCESS_ACTIVE_LOCALITY)) ==
+ (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) {
+ acquired = true;
+ break;
+ }
+ DELAY(1000);
+ } while (--to != 0);
+ }
- bus_space_write_1(bt, bh, TPM_ACCESS, save);
- return 0;
+ if ((reg & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) ==
+ (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY))
+ r = bus_space_read_4(bt, bh, TPM_ID);
+ else
+ r = UINT32_MAX;
+ if (acquired)
+ bus_space_write_1(bt, bh, TPM_ACCESS,
+ TPM_ACCESS_ACTIVE_LOCALITY);
+ return (r != UINT32_MAX);
}
/*
@@ -427,7 +453,7 @@ int
tpm_tis12_init(struct tpm_softc *sc, int irq, const char *name)
{
u_int32_t r;
- int i;
+ int error, i;
sx_assert(&sc->sc_lock, SA_XLOCKED);
r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTF_CAPABILITIES);
@@ -451,16 +477,22 @@ tpm_tis12_init(struct tpm_softc *sc, int irq, const char *name)
else
printf(": device 0x%08x rev 0x%x\n", sc->sc_devid, sc->sc_rev);
- if (tpm_request_locality(sc, 0))
+ error = tpm_request_locality(sc, 0);
+ if (error != 0)
return 1;
- if (tpm_tis12_irqinit(sc, irq, i))
- return 1;
+ error = tpm_tis12_irqinit(sc, irq, i);
+ if (error != 0)
+ goto out;
/* Abort whatever it thought it was doing. */
bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, TPM_STS_CMD_READY);
+ error = tpm_waitfor(sc, TPM_STS_CMD_READY, TPM_READY_TMO,
+ sc->sc_write);
- return 0;
+out:
+ tpm_tis12_relinquish_locality(sc);
+ return (error != 0);
}
/* Restore TIS state which is not guaranteed to survive S3. */
@@ -489,13 +521,16 @@ tpm_tis12_resume(struct tpm_softc *sc)
return (error);
error = tpm_tis12_irqinit(sc, irq, i);
if (error != 0)
- return (error);
+ goto out;
/* Abort firmware residue and leave the command FIFO ready. */
bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS,
TPM_STS_CMD_READY);
- return (tpm_waitfor(sc, TPM_STS_CMD_READY, TPM_READY_TMO,
- sc->sc_write));
+ error = tpm_waitfor(sc, TPM_STS_CMD_READY, TPM_READY_TMO,
+ sc->sc_write);
+out:
+ tpm_tis12_relinquish_locality(sc);
+ return (error);
}
int
@@ -507,11 +542,15 @@ tpm_request_locality(struct tpm_softc *sc, int l)
sx_assert(&sc->sc_lock, SA_XLOCKED);
if (l != 0)
return EINVAL;
+ KASSERT(!sc->sc_locality, ("%s: locality already owned", __func__));
if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) &
(TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) ==
- (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY))
+ (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) {
+ sc->sc_locality = true;
return 0;
+ }
+ sc->sc_locality = false;
bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS,
TPM_ACCESS_REQUEST_USE);
@@ -526,6 +565,14 @@ tpm_request_locality(struct tpm_softc *sc, int l)
#ifdef TPM_DEBUG
printf("tpm_request_locality: interrupted %d\n", rv);
#endif
+ r = bus_space_read_1(sc->sc_bt, sc->sc_bh,
+ TPM_ACCESS);
+ if ((r & (TPM_ACCESS_VALID |
+ TPM_ACCESS_ACTIVE_LOCALITY)) ==
+ (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) {
+ sc->sc_locality = true;
+ tpm_tis12_relinquish_locality(sc);
+ }
return rv;
}
}
@@ -538,9 +585,34 @@ tpm_request_locality(struct tpm_softc *sc, int l)
return EBUSY;
}
+ sc->sc_locality = true;
return 0;
}
+static void
+tpm_tis12_relinquish_locality(struct tpm_softc *sc)
+{
+
+ sx_assert(&sc->sc_lock, SA_XLOCKED);
+ sc->sc_command_pending = false;
+ if (!sc->sc_locality)
+ return;
+ bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS,
+ TPM_ACCESS_ACTIVE_LOCALITY);
+ sc->sc_locality = false;
+}
+
+static void
+tpm_tis12_abort(struct tpm_softc *sc)
+{
+
+ sx_assert(&sc->sc_lock, SA_XLOCKED);
+ if (sc->sc_locality)
+ bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS,
+ TPM_STS_CMD_READY);
+ tpm_tis12_relinquish_locality(sc);
+}
+
int
tpm_getburst(struct tpm_softc *sc)
{
@@ -921,14 +993,23 @@ tpm_tis12_start(struct tpm_softc *sc, int flag)
sx_assert(&sc->sc_lock, SA_XLOCKED);
if (flag == UIO_READ) {
+ if (!sc->sc_locality || !sc->sc_command_pending) {
+ tpm_tis12_abort(sc);
+ return (EIO);
+ }
rv = tpm_waitfor(sc, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
TPM_READ_TMO, sc->sc_read);
- return rv;
+ if (rv != 0)
+ tpm_tis12_abort(sc);
+ return (rv);
}
+ /* Abort an incomplete command before starting another one. */
+ tpm_tis12_abort(sc);
+
/* Own our (0th) locality. */
if ((rv = tpm_request_locality(sc, 0)) != 0)
- return rv;
+ return (rv);
sc->sc_stat = tpm_status(sc);
if (sc->sc_stat & TPM_STS_CMD_READY) {
@@ -950,7 +1031,8 @@ tpm_tis12_start(struct tpm_softc *sc, int flag)
#ifdef TPM_DEBUG
printf("tpm_tis12_start: UIO_WRITE readying failed %d\n", rv);
#endif
- return rv;
+ tpm_tis12_abort(sc);
+ return (rv);
}
#ifdef TPM_DEBUG
@@ -1013,8 +1095,8 @@ tpm_tis12_write(struct tpm_softc *sc, void *buf, int len)
#endif
sx_assert(&sc->sc_lock, SA_XLOCKED);
- if ((rv = tpm_request_locality(sc, 0)) != 0)
- return rv;
+ if (!sc->sc_locality)
+ return (EIO);
cnt = 0;
while (cnt < len - 1) {
@@ -1070,27 +1152,31 @@ tpm_tis12_end(struct tpm_softc *sc, int flag, int err)
sx_assert(&sc->sc_lock, SA_XLOCKED);
if (flag == UIO_READ) {
- if ((rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO,
- sc->sc_read)))
- return rv;
+ if (!sc->sc_locality) {
+ tpm_tis12_abort(sc);
+ return (err != 0 ? 0 : EIO);
+ }
+ if (err == 0)
+ rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO,
+ sc->sc_read);
/* Still more data? */
- sc->sc_stat = tpm_status(sc);
- if (!err && ((sc->sc_stat & TPM_STS_DATA_AVAIL) == TPM_STS_DATA_AVAIL)) {
+ if (err == 0 && rv == 0)
+ sc->sc_stat = tpm_status(sc);
+ if (err == 0 && rv == 0 &&
+ (sc->sc_stat & TPM_STS_DATA_AVAIL) != 0) {
#ifdef TPM_DEBUG
printf("tpm_tis12_end: read failed stat=%b\n",
sc->sc_stat, TPM_STS_BITS);
#endif
rv = EIO;
}
-
- bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS,
- TPM_STS_CMD_READY);
-
- /* Release our (0th) locality. */
- bus_space_write_1(sc->sc_bt, sc->sc_bh,TPM_ACCESS,
- TPM_ACCESS_ACTIVE_LOCALITY);
+ tpm_tis12_abort(sc);
} else {
+ if (!sc->sc_locality) {
+ tpm_tis12_abort(sc);
+ return (err != 0 ? 0 : EIO);
+ }
/* Hungry for more? */
sc->sc_stat = tpm_status(sc);
if (!err && (sc->sc_stat & TPM_STS_DATA_EXPECT)) {
@@ -1101,8 +1187,13 @@ tpm_tis12_end(struct tpm_softc *sc, int flag, int err)
rv = EIO;
}
- bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS,
- err ? TPM_STS_CMD_READY : TPM_STS_GO);
+ if (err != 0 || rv != 0)
+ tpm_tis12_abort(sc);
+ else {
+ bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS,
+ TPM_STS_GO);
+ sc->sc_command_pending = true;
+ }
}
return rv;
@@ -1368,6 +1459,8 @@ tpmclose(struct cdev *dev, int flag, int mode, struct thread *td)
if ((sc->sc_flags & TPM_OPEN) == 0)
error = EINVAL;
else {
+ if (sc->sc_init == tpm_tis12_init)
+ tpm_tis12_abort(sc);
sc->sc_flags &= ~TPM_OPEN;
error = 0;
}
diff --git a/sys/dev/tpm/tpmvar.h b/sys/dev/tpm/tpmvar.h
index fc11d08fa4c1..61e5c17a8ffb 100644
--- a/sys/dev/tpm/tpmvar.h
+++ b/sys/dev/tpm/tpmvar.h
@@ -60,6 +60,8 @@ struct tpm_softc {
struct mtx sc_intr_lock;
struct cv sc_intr_cv;
bool sc_dying;
+ bool sc_locality;
+ bool sc_command_pending;
#endif
#ifndef __FreeBSD__