The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=96ed6005082fc63d633408ba3ad697e8757e80f9
commit 96ed6005082fc63d633408ba3ad697e8757e80f9 Author: Kevin Bowling <[email protected]> AuthorDate: 2026-08-26 10:33:38 +0000 Commit: Kevin Bowling <[email protected]> CommitDate: 2026-09-01 18:19:50 +0000 tpm: Restore TPM 1.2 TIS state after resume Firmware restores the state saved by TPM_ORD_SaveState, but the TIS interrupt, locality, and command FIFO state are not guaranteed to survive S3. The legacy driver previously treated resume as a no-op. Revalidate the interface and device identity, disable and acknowledge stale interrupts, restore the configured interrupt vector, reacquire locality zero, and return the FIFO to command-ready state. Also disable TIS interrupts during initial setup when the device uses polling so firmware settings cannot leave an unhandled interrupt enabled. TIS 1.3 Table 22 makes the interrupt control registers locality protected. Acquire locality before disabling or programming them during initial setup and resume rather than relying on probe retaining locality. Keep TPM self-test outside the resume critical path. It can take minutes on some TPM 1.2 devices and is not required to restore the transport state. The two-commit suspend and resume series completed two consecutive S3 cycles on a ThinkPad T440p with its STMicro TPM 1.2 Security Chip enabled. PCR 0 was readable with the same value before and after each cycle, and no SaveState or TIS restoration errors were logged. The locality ordering completed another two consecutive S3 cycles on a ThinkPad T430 with the same STMicro TPM in polling mode. PCR 0 again remained stable, and TPM access recovered without errors after each resume. PR: 291067 Reviewed by: kevans Sponsored by: BBOX.io MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D59193 --- sys/dev/tpm/tpm.c | 91 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 21 deletions(-) diff --git a/sys/dev/tpm/tpm.c b/sys/dev/tpm/tpm.c index 855dad8eee85..c877f617fdad 100644 --- a/sys/dev/tpm/tpm.c +++ b/sys/dev/tpm/tpm.c @@ -195,6 +195,8 @@ 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 int tpm_tis12_devid_index(uint32_t); +static int tpm_tis12_resume(struct tpm_softc *); /* @@ -329,19 +331,14 @@ tpm_tis12_probe(bus_space_tag_t bt, bus_space_handle_t bh) } /* - * Setup interrupt vector if one is provided and interrupts are know to - * work on that particular chip. + * Setup the interrupt vector if one is provided and interrupts are known + * to work on that particular chip. The caller must hold locality zero. */ int tpm_tis12_irqinit(struct tpm_softc *sc, int irq, int idx) { u_int32_t r; - if ((irq == IRQUNK) || (tpm_devs[idx].flags & TPM_DEV_NOINTS)) { - sc->sc_vector = IRQUNK; - return 0; - } - /* Ack and disable all interrupts. */ bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) & @@ -349,6 +346,11 @@ tpm_tis12_irqinit(struct tpm_softc *sc, int irq, int idx) bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS, bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS)); + if ((irq == IRQUNK) || (tpm_devs[idx].flags & TPM_DEV_NOINTS)) { + sc->sc_vector = IRQUNK; + return 0; + } + /* Program interrupt vector. */ bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_INT_VECTOR, irq); sc->sc_vector = irq; @@ -365,6 +367,17 @@ tpm_tis12_irqinit(struct tpm_softc *sc, int irq, int idx) return 0; } +static int +tpm_tis12_devid_index(uint32_t devid) +{ + int i; + + for (i = 0; tpm_devs[i].devid != 0; i++) + if (tpm_devs[i].devid == devid) + break; + return (i); +} + /* Setup TPM using TIS 1.2 interface. */ int tpm_tis12_init(struct tpm_softc *sc, int irq, const char *name) @@ -386,19 +399,17 @@ tpm_tis12_init(struct tpm_softc *sc, int irq, const char *name) sc->sc_devid = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_ID); sc->sc_rev = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_REV); - for (i = 0; tpm_devs[i].devid; i++) - if (tpm_devs[i].devid == sc->sc_devid) - break; + i = tpm_tis12_devid_index(sc->sc_devid); if (tpm_devs[i].devid) printf(": %s rev 0x%x\n", tpm_devs[i].name, sc->sc_rev); else printf(": device 0x%08x rev 0x%x\n", sc->sc_devid, sc->sc_rev); - if (tpm_tis12_irqinit(sc, irq, i)) + if (tpm_request_locality(sc, 0)) return 1; - if (tpm_request_locality(sc, 0)) + if (tpm_tis12_irqinit(sc, irq, i)) return 1; /* Abort whatever it thought it was doing. */ @@ -407,6 +418,40 @@ tpm_tis12_init(struct tpm_softc *sc, int irq, const char *name) return 0; } +/* Restore TIS state which is not guaranteed to survive S3. */ +static int +tpm_tis12_resume(struct tpm_softc *sc) +{ + uint32_t capabilities, devid; + int error, i, irq; + + capabilities = bus_space_read_4(sc->sc_bt, sc->sc_bh, + TPM_INTF_CAPABILITIES); + if ((capabilities & TPM_CAPSREQ) != TPM_CAPSREQ || + (capabilities & (TPM_INTF_INT_EDGE_RISING | + TPM_INTF_INT_LEVEL_LOW)) == 0) + return (ENXIO); + devid = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_ID); + if (devid == UINT32_MAX || devid != sc->sc_devid) + return (ENXIO); + + sc->sc_capabilities = capabilities; + i = tpm_tis12_devid_index(devid); + irq = sc->sc_vector; + error = tpm_request_locality(sc, 0); + if (error != 0) + return (error); + error = tpm_tis12_irqinit(sc, irq, i); + if (error != 0) + return (error); + + /* 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)); +} + int tpm_request_locality(struct tpm_softc *sc, int l) { @@ -584,21 +629,25 @@ tpm_suspend(device_t dev) return (0); } -/* - * Handle resume event. Actually nothing to do as the BIOS is supposed - * to restore the previously saved state. - */ +/* Handle resume after firmware has restored the saved TPM state. */ int tpm_resume(device_t dev) { - struct tpm_softc *sc = device_get_softc(dev); - int why = 0; + struct tpm_softc *sc; + int error; + + sc = device_get_softc(dev); + error = 0; + if (sc->sc_init == tpm_tis12_init) + error = tpm_tis12_resume(sc); #ifdef TPM_DEBUG - printf("tpm_resume: resume: %d -> %d\n", sc->sc_suspend, why); + device_printf(dev, "resume: %d -> 0\n", sc->sc_suspend); #endif - sc->sc_suspend = why; + sc->sc_suspend = 0; + if (error != 0) + device_printf(dev, "failed to restore TIS state: %d\n", error); - return 0; + return (error); } /* Dispatch suspend and resume events. */
