git: 4bcd10f7c049 - main - tpm: Harvest entropy from TPM 1.2 devices
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 30 Oct 2026 22:26:06 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=4bcd10f7c04988056c7b28c4cbe65804d7f26d93
commit 4bcd10f7c04988056c7b28c4cbe65804d7f26d93
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-27 14:27:45 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-10-30 22:24:16 +0000
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
---
share/man/man4/tpm.4 | 10 ++-
sys/arm/conf/GENERIC | 2 +-
sys/arm64/conf/std.arm64 | 2 +-
sys/dev/tpm/tpm.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++-
sys/dev/tpm/tpmvar.h | 6 ++
sys/riscv/conf/GENERIC | 2 +-
6 files changed, 216 insertions(+), 5 deletions(-)
diff --git a/share/man/man4/tpm.4 b/share/man/man4/tpm.4
index 408b1f30022b..4a37fa2f8458 100644
--- a/share/man/man4/tpm.4
+++ b/share/man/man4/tpm.4
@@ -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
diff --git a/sys/arm/conf/GENERIC b/sys/arm/conf/GENERIC
index b0a2eb92554b..c74c8d1f56a7 100644
--- a/sys/arm/conf/GENERIC
+++ b/sys/arm/conf/GENERIC
@@ -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
diff --git a/sys/arm64/conf/std.arm64 b/sys/arm64/conf/std.arm64
index 47d803a7c37e..28d62b1657f9 100644
--- a/sys/arm64/conf/std.arm64
+++ b/sys/arm64/conf/std.arm64
@@ -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
diff --git a/sys/dev/tpm/tpm.c b/sys/dev/tpm/tpm.c
index 42c31ba75518..41f75aaa3047 100644
--- a/sys/dev/tpm/tpm.c
+++ b/sys/dev/tpm/tpm.c
@@ -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);
}
diff --git a/sys/dev/tpm/tpmvar.h b/sys/dev/tpm/tpmvar.h
index 12cee8e92c90..24e928c25f8d 100644
--- a/sys/dev/tpm/tpmvar.h
+++ b/sys/dev/tpm/tpmvar.h
@@ -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__
diff --git a/sys/riscv/conf/GENERIC b/sys/riscv/conf/GENERIC
index 7b47bd1ac407..55449f779b69 100644
--- a/sys/riscv/conf/GENERIC
+++ b/sys/riscv/conf/GENERIC
@@ -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"