Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/ic dev/ic/tpm: Take advantage of entropy source if a...
details: https://anonhg.NetBSD.org/src/rev/32be51e104b6
branches: trunk
changeset: 949198:32be51e104b6
user: riastradh <riastradh%NetBSD.org@localhost>
date: Mon Jan 04 18:26:59 2021 +0000
description:
dev/ic/tpm: Take advantage of entropy source if available.
If the tpm is deactivated, though, detach the entropy source so we
don't continue to try polling it -- it can't be activated without a
reboot anyway.
diffstat:
sys/dev/ic/tpm.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++-
sys/dev/ic/tpmreg.h | 22 +++++-
sys/dev/ic/tpmvar.h | 9 ++-
3 files changed, 215 insertions(+), 4 deletions(-)
diffs (291 lines):
diff -r c170ddbb9be0 -r 32be51e104b6 sys/dev/ic/tpm.c
--- a/sys/dev/ic/tpm.c Mon Jan 04 18:26:08 2021 +0000
+++ b/sys/dev/ic/tpm.c Mon Jan 04 18:26:59 2021 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: tpm.c,v 1.18 2021/01/04 18:26:08 riastradh Exp $ */
+/* $NetBSD: tpm.c,v 1.19 2021/01/04 18:26:59 riastradh Exp $ */
/*
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -48,11 +48,12 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tpm.c,v 1.18 2021/01/04 18:26:08 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tpm.c,v 1.19 2021/01/04 18:26:59 riastradh Exp $");
#include <sys/param.h>
#include <sys/types.h>
+#include <sys/atomic.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/device.h>
@@ -61,6 +62,7 @@
#include <sys/pmf.h>
#include <sys/proc.h>
#include <sys/systm.h>
+#include <sys/workqueue.h>
#include <dev/ic/tpmreg.h>
#include <dev/ic/tpmvar.h>
@@ -325,6 +327,179 @@
return 0;
}
+static void
+tpm_tis12_rng_work(struct work *wk, void *cookie)
+{
+ struct tpm_softc *sc = cookie;
+ /*
+ * TPM Specification Version 1.2, Main Part 3: Commands,
+ * Sec. 13.6 TPM_GetRandom
+ */
+ struct {
+ struct tpm_header hdr;
+ uint32_t bytesRequested;
+ } __packed command;
+ struct response {
+ struct tpm_header hdr;
+ uint32_t randomBytesSize;
+ uint8_t bytes[64];
+ } __packed response;
+ bool busy, endwrite = false, endread = false;
+ size_t nread;
+ uint16_t tag;
+ uint32_t pktlen, code, nbytes;
+ int rv;
+
+ /* Acknowledge the request. */
+ sc->sc_rndpending = 0;
+
+ /* Lock userland out of the tpm, or fail if it's already open. */
+ mutex_enter(&sc->sc_lock);
+ busy = sc->sc_busy;
+ sc->sc_busy = true;
+ mutex_exit(&sc->sc_lock);
+ if (busy) { /* tough */
+ aprint_debug_dev(sc->sc_dev, "%s: device in use\n", __func__);
+ return;
+ }
+
+ /* Encode the command. */
+ memset(&command, 0, sizeof(command));
+ command.hdr.tag = htobe16(TPM_TAG_RQU_COMMAND);
+ command.hdr.length = htobe32(sizeof(command));
+ command.hdr.code = htobe32(TPM_ORD_GetRandom);
+ command.bytesRequested = htobe32(sizeof(response.bytes));
+
+ /* Write the command. */
+ if ((rv = (*sc->sc_intf->start)(sc, UIO_WRITE)) != 0) {
+ device_printf(sc->sc_dev, "start write failed, error=%d\n",
+ rv);
+ goto out;
+ }
+ endwrite = true;
+ if ((rv = (*sc->sc_intf->write)(sc, &command, sizeof(command))) != 0) {
+ device_printf(sc->sc_dev, "write failed, error=%d\n", rv);
+ goto out;
+ }
+ rv = (*sc->sc_intf->end)(sc, UIO_WRITE, 0);
+ endwrite = false;
+ if (rv) {
+ device_printf(sc->sc_dev, "end write failed, error=%d\n", rv);
+ goto out;
+ }
+
+ /* Read the response header. */
+ if ((rv = (*sc->sc_intf->start)(sc, UIO_READ)) != 0) {
+ device_printf(sc->sc_dev, "start write failed, error=%d\n",
+ rv);
+ goto out;
+ }
+ endread = true;
+ if ((rv = (*sc->sc_intf->read)(sc, &response.hdr, sizeof(response.hdr),
+ &nread, 0)) != 0) {
+ device_printf(sc->sc_dev, "read failed, error=%d\n", rv);
+ goto out;
+ }
+
+ /* Verify the response header looks sensible. */
+ if (nread != sizeof(response.hdr)) {
+ device_printf(sc->sc_dev, "read %zu bytes, expected %zu",
+ nread, sizeof(response.hdr));
+ goto out;
+ }
+ tag = be16toh(response.hdr.tag);
+ pktlen = be32toh(response.hdr.length);
+ code = be32toh(response.hdr.code);
+ if (tag != TPM_TAG_RSP_COMMAND ||
+ pktlen < offsetof(struct response, bytes) ||
+ pktlen > sizeof(response) ||
+ code != 0) {
+ /*
+ * If the tpm itself is busy (e.g., it has yet to run a
+ * self-test, or it's in a timeout period to defend
+ * against brute force attacks), then we can try again
+ * later. Otherwise, give up.
+ */
+ if (code & TPM_NON_FATAL) {
+ aprint_debug_dev(sc->sc_dev, "%s: tpm busy, code=%u\n",
+ __func__, code & ~TPM_NON_FATAL);
+ rv = 0;
+ } else if (code == TPM_DEACTIVATED) {
+ device_printf(sc->sc_dev, "tpm is deactivated\n");
+ rv = ENXIO;
+ } else {
+ device_printf(sc->sc_dev, "bad tpm response:"
+ " tag=%u len=%u code=%u\n", tag, pktlen, code);
+ hexdump(aprint_debug, "tpm response header",
+ (const void *)&response.hdr,
+ sizeof(response.hdr));
+ rv = EIO;
+ }
+ goto out;
+ }
+
+ /* Read the response payload. */
+ if ((rv = (*sc->sc_intf->read)(sc,
+ (char *)&response + nread, pktlen - nread,
+ NULL, TPM_PARAM_SIZE)) != 0) {
+ device_printf(sc->sc_dev, "read failed, error=%d\n", rv);
+ goto out;
+ }
+ endread = false;
+ if ((rv = (*sc->sc_intf->end)(sc, UIO_READ, 0)) != 0) {
+ device_printf(sc->sc_dev, "end read failed, error=%d\n", rv);
+ goto out;
+ }
+
+ /* Verify the number of bytes read looks sensible. */
+ nbytes = be32toh(response.randomBytesSize);
+ if (nbytes > pktlen - offsetof(struct response, bytes)) {
+ device_printf(sc->sc_dev, "overlong GetRandom length:"
+ " %u, max %zu\n",
+ nbytes, pktlen - offsetof(struct response, bytes));
+ nbytes = pktlen - offsetof(struct response, bytes);
+ }
+
+ /*
+ * Enter the data into the entropy pool. Conservatively (or,
+ * perhaps, cargocultily) estimate half a bit of entropy per
+ * bit of data.
+ */
+ rnd_add_data(&sc->sc_rnd, response.bytes, nbytes, (NBBY/2)*nbytes);
+
+out: /*
+ * If the tpm is busted, no sense in trying again -- most
+ * likely, it is deactivated, and by the spec it cannot be
+ * reactivated until after a reboot.
+ */
+ if (rv) {
+ device_printf(sc->sc_dev, "deactivating entropy source\n");
+ rnd_detach_source(&sc->sc_rnd);
+ /* XXX worker thread can't workqueue_destroy its own queue */
+ }
+
+ /* End the read or write if still ongoing. */
+ if (endread)
+ rv = (*sc->sc_intf->end)(sc, UIO_READ, rv);
+ if (endwrite)
+ rv = (*sc->sc_intf->end)(sc, UIO_WRITE, rv);
+
+ /* Relinquish the tpm back to userland. */
+ mutex_enter(&sc->sc_lock);
+ KASSERT(sc->sc_busy);
+ sc->sc_busy = false;
+ mutex_exit(&sc->sc_lock);
+}
+
+static void
+tpm_tis12_rng_get(size_t nbytes, void *cookie)
+{
+ struct tpm_softc *sc = cookie;
+
+ if (atomic_swap_uint(&sc->sc_rndpending, 1) == 0)
+ workqueue_enqueue(sc->sc_rndwq, &sc->sc_rndwk, NULL);
+}
+
static int
tpm_tis12_init(struct tpm_softc *sc)
{
@@ -347,6 +522,15 @@
/* Abort whatever it thought it was doing. */
bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, TPM_STS_CMD_READY);
+ /* XXX Run this at higher priority? */
+ if ((rv = workqueue_create(&sc->sc_rndwq, device_xname(sc->sc_dev),
+ tpm_tis12_rng_work, sc, PRI_NONE, IPL_VM, WQ_MPSAFE)) != 0)
+ return rv;
+ rndsource_setcb(&sc->sc_rnd, tpm_tis12_rng_get, sc);
+ rnd_attach_source(&sc->sc_rnd, device_xname(sc->sc_dev),
+ RND_TYPE_RNG,
+ RND_FLAG_COLLECT_VALUE|RND_FLAG_ESTIMATE_VALUE|RND_FLAG_HASCB);
+
return 0;
}
diff -r c170ddbb9be0 -r 32be51e104b6 sys/dev/ic/tpmreg.h
--- a/sys/dev/ic/tpmreg.h Mon Jan 04 18:26:08 2021 +0000
+++ b/sys/dev/ic/tpmreg.h Mon Jan 04 18:26:59 2021 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: tpmreg.h,v 1.7 2021/01/04 18:22:19 riastradh Exp $ */
+/* $NetBSD: tpmreg.h,v 1.8 2021/01/04 18:26:59 riastradh Exp $ */
/*
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -114,4 +114,24 @@
*/
#define TPM_SPACE_SIZE 0x5000
+#define TPM_TAG_RQU_COMMAND 0x00c1
+#define TPM_TAG_RSP_COMMAND 0x00c4
+
+#define TPM_ORD_GetRandom 0x00000046
+
+/* TPM_RESULT return codes */
+#define TPM_AUTHFAIL 1
+#define TPM_BADINDEX 2
+#define TPM_BAD_PARAMETER 3
+#define TPM_AUDITFAILURE 4
+#define TPM_CLEAR_DISABLED 5
+#define TPM_DEACTIVATED 6
+#define TPM_DISABLED 7
+#define TPM_DISABLED_CMD 8
+#define TPM_FAIL 9
+#define TPM_BAD_ORDINAL 10
+/* ... */
+
+#define TPM_NON_FATAL 0x800
+
#endif /* DEV_IC_TPMREG_H */
diff -r c170ddbb9be0 -r 32be51e104b6 sys/dev/ic/tpmvar.h
--- a/sys/dev/ic/tpmvar.h Mon Jan 04 18:26:08 2021 +0000
+++ b/sys/dev/ic/tpmvar.h Mon Jan 04 18:26:59 2021 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: tpmvar.h,v 1.8 2021/01/04 18:22:19 riastradh Exp $ */
+/* $NetBSD: tpmvar.h,v 1.9 2021/01/04 18:26:59 riastradh Exp $ */
/*
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -62,6 +62,8 @@
#include <sys/bus.h>
#include <sys/device_if.h>
#include <sys/mutex.h>
+#include <sys/rndsource.h>
+#include <sys/workqueue.h>
struct tpm_softc;
@@ -91,6 +93,11 @@
uint32_t sc_rev;
uint32_t sc_status;
uint32_t sc_caps;
+
+ struct krndsource sc_rnd;
+ struct workqueue *sc_rndwq;
+ struct work sc_rndwk;
+ volatile unsigned sc_rndpending;
};
bool tpm_suspend(device_t, const pmf_qual_t *);
Home |
Main Index |
Thread Index |
Old Index