tpm20: fix suspend/resume and entropy harvesting

There were a few problem here:
  - TPM2_Shutdown results in a response that we need to either process
    or ignore, otherwise any tpm20_write or tpm20_harvest call will
    trivially hang on an `sc->pending_data_length != 0`
  - We should have a matching TPM2_Startup upon resume to restore any
    state that should have persisted
  - We must drain the harvest task before we suspend to avoid problems
    there

This commit is sufficient to avoid breaking suspend/resume.

PR:		291067
Fixes:	a2d5ed9442 ("Introduce driver for TPM 2.0 in CRB and [...]")
Fixes:	4ee7d3b011 ("Allow using TPM as entropy source.")
Co-authored-by:	markj (D53835)
Tested by:	garga
Differential Revision:	https://reviews.freebsd.org/D55074
This commit is contained in:
Kyle Evans
2026-02-04 21:35:01 -06:00
parent 5c9d988d86
commit 38a4995eb5
4 changed files with 68 additions and 0 deletions
+65
View File
@@ -45,6 +45,7 @@ static void tpm20_discard_buffer(void *arg);
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
static void tpm20_harvest(void *arg, int unused);
#endif
static int tpm20_restart(device_t dev, bool clear);
static int tpm20_save_state(device_t dev, bool suspend);
static d_open_t tpm20_open;
@@ -242,9 +243,31 @@ tpm20_release(struct tpm_sc *sc)
destroy_dev(sc->sc_cdev);
}
int
tpm20_resume(device_t dev)
{
tpm20_restart(dev, false);
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
struct tpm_sc *sc;
sc = device_get_softc(dev);
taskqueue_enqueue_timeout(taskqueue_thread, &sc->harvest_task,
hz * TPM_HARVEST_INTERVAL);
#endif
return (0);
}
int
tpm20_suspend(device_t dev)
{
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
struct tpm_sc *sc;
sc = device_get_softc(dev);
taskqueue_drain_timeout(taskqueue_thread, &sc->harvest_task);
#endif
return (tpm20_save_state(dev, true));
}
@@ -307,6 +330,43 @@ tpm20_harvest(void *arg, int unused)
}
#endif /* TPM_HARVEST */
static int
tpm20_restart(device_t dev, bool clear)
{
struct tpm_sc *sc;
uint8_t startup_cmd[] = {
0x80, 0x01, /* TPM_ST_NO_SESSIONS tag*/
0x00, 0x00, 0x00, 0x0C, /* cmd length */
0x00, 0x00, 0x01, 0x44, /* cmd TPM_CC_Startup */
0x00, 0x01 /* TPM_SU_STATE */
};
sc = device_get_softc(dev);
/*
* Inform the TPM whether we are resetting or resuming.
*/
if (clear)
startup_cmd[11] = 0; /* TPM_SU_CLEAR */
if (sc == NULL || sc->buf == NULL)
return (0);
sx_xlock(&sc->dev_lock);
MPASS(sc->pending_data_length == 0);
memcpy(sc->buf, startup_cmd, sizeof(startup_cmd));
/* XXX Ignoring both TPM_TRANSMIT return and tpm's response */
TPM_TRANSMIT(sc->dev, sizeof(startup_cmd));
sc->pending_data_length = 0;
sc->total_length = 0;
sx_xunlock(&sc->dev_lock);
return (0);
}
static int
tpm20_save_state(device_t dev, bool suspend)
{
@@ -331,8 +391,13 @@ tpm20_save_state(device_t dev, bool suspend)
sx_xlock(&sc->dev_lock);
MPASS(sc->pending_data_length == 0);
memcpy(sc->buf, save_cmd, sizeof(save_cmd));
/* XXX Ignoring both TPM_TRANSMIT return and tpm's response */
TPM_TRANSMIT(sc->dev, sizeof(save_cmd));
sc->pending_data_length = 0;
sc->total_length = 0;
sx_xunlock(&sc->dev_lock);
+1
View File
@@ -136,6 +136,7 @@ struct tpm_sc {
};
int tpm20_suspend(device_t dev);
int tpm20_resume(device_t dev);
int tpm20_shutdown(device_t dev);
int32_t tpm20_get_timeout(uint32_t command);
int tpm20_init(struct tpm_sc *sc);
+1
View File
@@ -651,6 +651,7 @@ static device_method_t tpmcrb_methods[] = {
DEVMETHOD(device_detach, tpmcrb_detach),
DEVMETHOD(device_shutdown, tpm20_shutdown),
DEVMETHOD(device_suspend, tpm20_suspend),
DEVMETHOD(device_resume, tpm20_resume),
DEVMETHOD(tpm_transmit, tpmcrb_transmit),
{0, 0}
};
+1
View File
@@ -487,6 +487,7 @@ static device_method_t tpmtis_methods[] = {
DEVMETHOD(device_detach, tpmtis_detach),
DEVMETHOD(device_shutdown, tpm20_shutdown),
DEVMETHOD(device_suspend, tpm20_suspend),
DEVMETHOD(device_resume, tpm20_resume),
DEVMETHOD(tpm_transmit, tpmtis_transmit),
DEVMETHOD_END
};