tpm: Harvest entropy from TPM 1.2 devices

The TPM 2.0 driver periodically contributes TPM-generated random bytes
to the kernel entropy pool, but the TPM 1.2 driver does not.

Issue TPM_GetRandom every ten seconds and submit successful responses
as RANDOM_PURE_TPM.  Keep each exchange under the TPM transaction lock.
Defer harvesting while the character device is open or a command is
pending so the task cannot consume a user response.  Drain the task for
suspend and detach, rearming it after resume or a failed suspend.

Document the expanded scope of RANDOM_ENABLE_TPM and update the kernel
configuration comments that described it as TPM 2.0-only.

Validated on a STMicroelectronics TPM 1.2 TIS device.  DTrace observed
periodic entropy submissions; a raw GetRandom response remained intact
while the device was held open across a harvest interval; and module
unload/reload deregistered and restored the entropy source cleanly.

Tested by:	zarychtam_plan-b.pwste.edu.pl
MFC after:	2 weeks
Sponsored by:	BBOX.io
Differential Revision:	https://reviews.freebsd.org/D59239
This commit is contained in:
Kevin Bowling committed 2026-10-30 15:24:16 -07:00
1 parent f0ac0f73ae
commit 4bcd10f7c0
6 files changed
+216 -5

No files matched your search

+9 -1
View File
@@ -13,7 +13,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd October 31, 2018
.Dd August 27, 2026
.Dt TPM 4
.Os
.Sh NAME
@@ -21,6 +21,7 @@
.Nd Trusted Platform Module
.Sh SYNOPSIS
.Cd device tpm
.Cd options RANDOM_ENABLE_TPM
.Pp
In
.Xr device.hints 5 :
@@ -43,6 +44,12 @@ store cryptographic keys.
The driver can be configured to use an IRQ by providing a free ISA
interrupt vector in
.Pa /boot/device.hints .
.Pp
When the kernel is configured with
.Cd "options RANDOM_ENABLE_TPM" ,
the driver periodically obtains random bytes from the TPM and supplies them
to
.Xr random 4 .
.Sh HARDWARE
The
.Nm
@@ -66,6 +73,7 @@ Winbond WEC WPCT200
.El
.Sh SEE ALSO
.Xr intro 4 ,
.Xr random 4 ,
.Xr device.hints 5 ,
.Xr config 8
.Pp
+1 -1
View File
@@ -218,7 +218,7 @@ device smsc # SMSC LAN91C111
# random(4)
device tpm # Trusted Platform Module
options RANDOM_ENABLE_TPM # enable entropy from TPM 2.0
options RANDOM_ENABLE_TPM # enable entropy from TPM devices
options RANDOM_ENABLE_KBD
options RANDOM_ENABLE_MOUSE
+1 -1
View File
@@ -111,6 +111,6 @@ device smbios
# random(4)
device tpm # Trusted Platform Module
options RANDOM_ENABLE_TPM # enable entropy from TPM 2.0
options RANDOM_ENABLE_TPM # enable entropy from TPM devices
options RANDOM_ENABLE_KBD
options RANDOM_ENABLE_MOUSE
+198 -1
View File
@@ -24,6 +24,7 @@
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/random.h>
#include <sys/module.h>
#include <sys/conf.h>
@@ -43,8 +44,20 @@
#include "tpm_if.h"
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
#include <dev/random/randomdev.h>
#include <dev/random/random_harvestq.h>
#endif
MALLOC_DEFINE(M_TPM, "tpm12_buffer", "buffer for TPM 1.2 responses");
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
static const struct random_source random_tpm12 = {
.rs_ident = "TPM",
.rs_source = RANDOM_PURE_TPM,
};
#endif
#define TPM_BUFSIZ 1024
#define TPM_HDRSIZE 10
@@ -54,11 +67,15 @@ MALLOC_DEFINE(M_TPM, "tpm12_buffer", "buffer for TPM 1.2 responses");
#define TPM_TAG_RQU_COMMAND 0x00c1
#define TPM_TAG_RSP_COMMAND 0x00c4
#define TPM_ORD_SAVESTATE 0x00000098
#define TPM_ORD_GETRANDOM 0x00000046
#define TPM_WARN_RETRY 0x00000800
#define TPM_SAVESTATE_RETRIES 50
#define TPM_SAVESTATE_RETRY_MS 100
#define TPM_HARVEST_SIZE 16
#define TPM_HARVEST_INTERVAL 10
#define TPM_ACCESS 0x0000 /* access register */
#define TPM_ACCESS_ESTABLISHMENT 0x01 /* establishment */
#define TPM_ACCESS_REQUEST_USE 0x02 /* request using locality */
@@ -196,6 +213,11 @@ int tpm_legacy_end(struct tpm_softc *, int, int);
static int tpm_attach_finish(struct tpm_softc *);
static void tpm_delayed_attach(void *);
static int tpm_transmit_header(struct tpm_softc *, uint32_t, uint32_t *);
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
static int tpm_get_random(struct tpm_softc *, uint8_t *, size_t *);
static void tpm_harvest(void *, int);
static void tpm_harvest_enqueue(const uint8_t *, size_t);
#endif
static void tpm_tis12_abort(struct tpm_softc *);
static int tpm_tis12_devid_index(uint32_t);
static int tpm_tis12_quiesce(struct tpm_softc *);
@@ -254,6 +276,9 @@ tpm_attach(device_t dev)
sc->sc_intr_received = false;
sc->sc_irq_config.valid = false;
TASK_INIT(&sc->sc_intr_task, 0, tpm_tis12_intr_task, sc);
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
sc->sc_harvest_registered = false;
#endif
sc->mem_rid = 0;
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
@@ -366,7 +391,19 @@ tpm_attach_finish(struct tpm_softc *sc)
args.mda_gid = GID_WHEEL;
args.mda_mode = 0600;
args.mda_si_drv1 = sc;
return (make_dev_s(&args, &sc->sc_cdev, "tpm"));
error = make_dev_s(&args, &sc->sc_cdev, "tpm");
if (error != 0)
return (error);
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
TIMEOUT_TASK_INIT(taskqueue_thread, &sc->sc_harvest_task, 0,
tpm_harvest, sc);
random_source_register(&random_tpm12);
sc->sc_harvest_registered = true;
taskqueue_enqueue_timeout(taskqueue_thread, &sc->sc_harvest_task, 0);
#endif
return (0);
}
static void
@@ -401,6 +438,15 @@ tpm_detach(device_t dev)
sx_xunlock(&sc->sc_lock);
taskqueue_drain(taskqueue_thread, &sc->sc_intr_task);
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
if (sc->sc_harvest_registered) {
taskqueue_drain_timeout(taskqueue_thread,
&sc->sc_harvest_task);
random_source_deregister(&random_tpm12);
sc->sc_harvest_registered = false;
}
#endif
/*
* Prevent new methods from touching the transport. Do not hold the
* lock while destroy_dev() drains methods and cdevpriv destructors
@@ -1039,6 +1085,142 @@ tpm_transmit_header(struct tpm_softc *sc, uint32_t ordinal, uint32_t *tpm_rc)
return (0);
}
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
/*
* Fetch random bytes as one command/response transaction. The caller owns
* sc_lock so a character-device request cannot be interposed between the
* write and read phases.
*/
static int
tpm_get_random(struct tpm_softc *sc, uint8_t *entropy, size_t *entropy_size)
{
uint8_t command[TPM_HDRSIZE + sizeof(uint32_t)];
uint8_t response[TPM_HDRSIZE + sizeof(uint32_t) + TPM_HARVEST_SIZE];
size_t count, response_size;
uint32_t random_size, tpm_rc;
int end_error, error;
sx_assert(&sc->sc_lock, SA_XLOCKED);
*entropy_size = 0;
random_size = 0;
be16enc(command, TPM_TAG_RQU_COMMAND);
be32enc(command + 2, sizeof(command));
be32enc(command + 6, TPM_ORD_GETRANDOM);
be32enc(command + TPM_HDRSIZE, TPM_HARVEST_SIZE);
error = sc->sc_start(sc, UIO_WRITE);
if (error != 0)
return (error);
error = sc->sc_write(sc, command, sizeof(command));
end_error = sc->sc_end(sc, UIO_WRITE, error);
if (error == 0)
error = end_error;
if (error != 0)
return (error);
error = sc->sc_start(sc, UIO_READ);
if (error != 0)
return (error);
count = 0;
error = sc->sc_read(sc, response, TPM_HDRSIZE, &count,
TPM_PARAM_SIZE);
if (error != 0)
goto end;
if (count != TPM_HDRSIZE ||
be16dec(response) != TPM_TAG_RSP_COMMAND) {
error = EPROTO;
goto end;
}
response_size = be32dec(response + 2);
if (response_size < TPM_HDRSIZE || response_size > sizeof(response)) {
error = EPROTO;
goto end;
}
if (response_size > TPM_HDRSIZE) {
count = 0;
error = sc->sc_read(sc, response + TPM_HDRSIZE,
(int)(response_size - TPM_HDRSIZE), &count,
TPM_PARAM_SIZE);
if (error != 0)
goto end;
if (count != response_size - TPM_HDRSIZE) {
error = EIO;
goto end;
}
}
tpm_rc = be32dec(response + 6);
if (tpm_rc != 0) {
error = EIO;
goto end;
}
if (response_size < TPM_HDRSIZE + sizeof(uint32_t)) {
error = EPROTO;
goto end;
}
random_size = be32dec(response + TPM_HDRSIZE);
if (random_size > response_size - TPM_HDRSIZE - sizeof(uint32_t)) {
error = EPROTO;
goto end;
}
random_size = MIN(random_size, TPM_HARVEST_SIZE);
end:
end_error = sc->sc_end(sc, UIO_READ, error);
if (error == 0)
error = end_error;
if (error == 0) {
memcpy(entropy, response + TPM_HDRSIZE + sizeof(uint32_t),
random_size);
*entropy_size = random_size;
}
return (error);
}
static void
tpm_harvest_enqueue(const uint8_t *entropy, size_t entropy_size)
{
size_t chunk_size;
/* Preserve all RNG bytes rather than squashing an oversized event. */
chunk_size = sizeof(((struct harvest_event *)0)->he_entropy);
while (entropy_size != 0) {
size_t size;
size = MIN(entropy_size, chunk_size);
random_harvest_queue(entropy, (u_int)size, RANDOM_PURE_TPM);
entropy += size;
entropy_size -= size;
}
}
static void
tpm_harvest(void *arg, int pending)
{
struct tpm_softc *sc;
uint8_t entropy[TPM_HARVEST_SIZE];
size_t entropy_size;
sc = arg;
entropy_size = 0;
sx_xlock(&sc->sc_lock);
if (sc->sc_dying || sc->sc_suspend != 0)
goto out;
/* Do not interpose between a user's command and response. */
if ((sc->sc_flags & TPM_OPEN) == 0 && !sc->sc_command_pending)
(void)tpm_get_random(sc, entropy, &entropy_size);
taskqueue_enqueue_timeout(taskqueue_thread, &sc->sc_harvest_task,
hz * TPM_HARVEST_INTERVAL);
out:
sx_xunlock(&sc->sc_lock);
if (entropy_size != 0)
tpm_harvest_enqueue(entropy, entropy_size);
}
#endif
/* Save TPM state on suspend. */
int
tpm_suspend(device_t dev)
@@ -1053,6 +1235,11 @@ tpm_suspend(device_t dev)
* invalidate that saved state, so retry SaveState before entering S3.
*/
sc = device_get_softc(dev);
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
if (sc->sc_harvest_registered)
taskqueue_drain_timeout(taskqueue_thread,
&sc->sc_harvest_task);
#endif
sx_xlock(&sc->sc_lock);
if (sc->sc_dying) {
error = ENXIO;
@@ -1088,6 +1275,11 @@ tpm_suspend(device_t dev)
out:
sx_xunlock(&sc->sc_lock);
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
if (error != 0 && sc->sc_harvest_registered)
taskqueue_enqueue_timeout(taskqueue_thread,
&sc->sc_harvest_task, hz * TPM_HARVEST_INTERVAL);
#endif
return (error);
}
@@ -1117,6 +1309,11 @@ tpm_resume(device_t dev)
out:
sx_xunlock(&sc->sc_lock);
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
if (error == 0 && sc->sc_harvest_registered)
taskqueue_enqueue_timeout(taskqueue_thread,
&sc->sc_harvest_task, hz * TPM_HARVEST_INTERVAL);
#endif
return (error);
}
+6
View File
@@ -20,6 +20,8 @@
#define _TPMVAR_H
#ifdef __FreeBSD__
#include "opt_tpm.h"
#include <sys/condvar.h>
#include <sys/kernel.h>
#include <sys/lock.h>
@@ -77,6 +79,10 @@ struct tpm_softc {
bool sc_dying;
bool sc_locality;
bool sc_command_pending;
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
struct timeout_task sc_harvest_task;
bool sc_harvest_registered;
#endif
#endif
#ifndef __FreeBSD__
+1 -1
View File
@@ -218,7 +218,7 @@ device iicoc # OpenCores I2C controller support
# random(4)
device tpm # Trusted Platform Module
options RANDOM_ENABLE_TPM # enable entropy from TPM 2.0
options RANDOM_ENABLE_TPM # enable entropy from TPM devices
# Include SoC specific configuration
include "std.allwinner"