Module Name:    src
Committed By:   martin
Date:           Wed Aug  3 16:00:47 UTC 2022

Modified Files:
        src/share/man/man4 [netbsd-9]: tpm.4
        src/sys/dev/acpi [netbsd-9]: tpm_acpi.c
        src/sys/dev/ic [netbsd-9]: tpm.c tpmreg.h tpmvar.h

Log Message:
Pull up following revision(s) (requested by riastradh in ticket #1495):

        share/man/man4/tpm.4: revision 1.7
        sys/dev/ic/tpm.c: revision 1.17
        sys/dev/ic/tpmvar.h: revision 1.10
        sys/dev/ic/tpm.c: revision 1.18
        sys/dev/ic/tpm.c: revision 1.19
        sys/dev/acpi/tpm_acpi.c: revision 1.14
        sys/dev/ic/tpmreg.h: revision 1.10
        sys/dev/ic/tpmreg.h: revision 1.11
        sys/dev/ic/tpm.c: revision 1.21
        sys/dev/ic/tpm.c: revision 1.22
        sys/dev/ic/tpm.c: revision 1.23
        sys/dev/ic/tpm.c: revision 1.24
        sys/dev/ic/tpm.c: revision 1.25
        sys/dev/ic/tpmreg.h: revision 1.7
        sys/dev/ic/tpmreg.h: revision 1.8
        sys/dev/ic/tpmreg.h: revision 1.9
        sys/dev/ic/tpmvar.h: revision 1.8
        sys/dev/ic/tpmvar.h: revision 1.9

dev/ic/tpm: Tidy up headers.
- Add include guards.
- Add necessary includes.
- Sort includes.
- Use _BYTE_ORDER, not BYTE_ORDER, for public header.

dev/ic/tpm: Add missing line break in attach output.

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.

Add note about enabling TPM and rnd(4) source.

tpm(4): Handle TPM 2.0 random source too, and loop on short reads.
Tested on ThinkPad T480.

tpm(4): Preserve error if any on ending commands.
This way we don't spuriously suppress an error, such as
TPM_DEACTIVATED, in a loop where we rely on it.

tpm@acpi: Require only one locality's worth of register space.
We don't actually use the registers for the other localities, and
some older TPMs only have the first locality exposed via ACPI.

tpm(4): Fix disabling of rnd source if tpm is deactivated.

Nothing prevents a second worker from being queued when the first one
is about to do rnd_detach_source.  Instead, just set a flag so future
requests don't bother running a new thread; if there's a concurrent
one that's already been scheduled on another CPU, well, too bad, we
get a couple extra log messages but that's fine.

A better way to do this would probably be to detect whether the tpm
is deactivated at attach time, but that requires reading more of the
tpm spec than I care to do when there are alternative ways to
procrastinate like scrubbing the toilet.

tpm(4): Fix suspend and rework I/O transaction lock.

Use sc->sc_lock over individual I/O transactions, not open/close of
the whole device.  This way there is a bounded time before the tpm is
unbusied even if userland is getting at it, so userland can't hold up
suspend indefinitely.  Of course, the tpm might be suspended and
resumed in the middle of the user's session this way -- tough.

This limits the response buffer to 1024 bytes -- which is already a
bit hefty to have on the stack (but it's probably not very deep on
the stack from userland so maybe not a big deal).  If it turns out we
need more, we can use kmem to allocate a buffer on the heap, with the
caveat that it might fail.  This is necessary so that suspend doesn't
block indefinitely on uiomove in tpmread.

tpm(4): Nix TPM_BE16/TPM_BE32.  Just use sys/endian.h.


To generate a diff of this commit:
cvs rdiff -u -r1.4.6.1 -r1.4.6.2 src/share/man/man4/tpm.4
cvs rdiff -u -r1.8.2.1 -r1.8.2.2 src/sys/dev/acpi/tpm_acpi.c
cvs rdiff -u -r1.13.2.1 -r1.13.2.2 src/sys/dev/ic/tpm.c
cvs rdiff -u -r1.4.2.1 -r1.4.2.2 src/sys/dev/ic/tpmreg.h \
    src/sys/dev/ic/tpmvar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/share/man/man4/tpm.4
diff -u src/share/man/man4/tpm.4:1.4.6.1 src/share/man/man4/tpm.4:1.4.6.2
--- src/share/man/man4/tpm.4:1.4.6.1	Wed Oct 16 09:52:38 2019
+++ src/share/man/man4/tpm.4	Wed Aug  3 16:00:47 2022
@@ -1,4 +1,4 @@
-.\"	$NetBSD: tpm.4,v 1.4.6.1 2019/10/16 09:52:38 martin Exp $
+.\"	$NetBSD: tpm.4,v 1.4.6.2 2022/08/03 16:00:47 martin Exp $
 .\"
 .\" Copyright (c) 2019 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -51,9 +51,19 @@ TPM 1.2 chips over ISA
 .El
 .Pp
 Note that the supported interface version is TIS1.2 in each case.
+.Pp
+The TPM may need to be enabled in the system's firmware or BIOS, which
+requires a reboot to take effect.
+This is generally beyond the control of
+.Nx .
+Enabling a TPM does not require using trusted boot \(em it can be
+enabled, for example, only for the
+.Xr rnd 4
+entropy source.
 .Sh SEE ALSO
 .Xr config 1 ,
-.Xr intro 4
+.Xr intro 4 ,
+.Xr rnd 4
 .Sh AUTHORS
 .An -nosplit
 The

Index: src/sys/dev/acpi/tpm_acpi.c
diff -u src/sys/dev/acpi/tpm_acpi.c:1.8.2.1 src/sys/dev/acpi/tpm_acpi.c:1.8.2.2
--- src/sys/dev/acpi/tpm_acpi.c:1.8.2.1	Wed Oct 16 09:52:38 2019
+++ src/sys/dev/acpi/tpm_acpi.c	Wed Aug  3 16:00:47 2022
@@ -1,4 +1,4 @@
-/* $NetBSD: tpm_acpi.c,v 1.8.2.1 2019/10/16 09:52:38 martin Exp $ */
+/* $NetBSD: tpm_acpi.c,v 1.8.2.2 2022/08/03 16:00:47 martin Exp $ */
 
 /*
  * Copyright (c) 2012, 2019 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tpm_acpi.c,v 1.8.2.1 2019/10/16 09:52:38 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tpm_acpi.c,v 1.8.2.2 2022/08/03 16:00:47 martin Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -113,8 +113,8 @@ tpm_acpi_attach(device_t parent, device_
 		aprint_error_dev(self, "cannot find mem\n");
 		goto out;
 	}
-	if (mem->ar_length != TPM_SPACE_SIZE) {
-		aprint_error_dev(self, "wrong size mem %"PRIu64" != %u\n",
+	if (mem->ar_length < TPM_SPACE_SIZE) {
+		aprint_error_dev(self, "wrong size mem %"PRIu64" < %u\n",
 		    (uint64_t)mem->ar_length, TPM_SPACE_SIZE);
 		goto out;
 	}

Index: src/sys/dev/ic/tpm.c
diff -u src/sys/dev/ic/tpm.c:1.13.2.1 src/sys/dev/ic/tpm.c:1.13.2.2
--- src/sys/dev/ic/tpm.c:1.13.2.1	Wed Oct 16 09:52:38 2019
+++ src/sys/dev/ic/tpm.c	Wed Aug  3 16:00:47 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: tpm.c,v 1.13.2.1 2019/10/16 09:52:38 martin Exp $	*/
+/*	$NetBSD: tpm.c,v 1.13.2.2 2022/08/03 16:00:47 martin Exp $	*/
 
 /*
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -48,17 +48,21 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tpm.c,v 1.13.2.1 2019/10/16 09:52:38 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tpm.c,v 1.13.2.2 2022/08/03 16:00:47 martin Exp $");
 
 #include <sys/param.h>
-#include <sys/systm.h>
+#include <sys/types.h>
+
+#include <sys/atomic.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/device.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
-#include <sys/proc.h>
-#include <sys/device.h>
-#include <sys/conf.h>
-#include <sys/bus.h>
 #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>
@@ -137,14 +141,91 @@ tpm12_suspend(struct tpm_softc *sc)
 		0x00, 0x00, 0x00, 0x98	/* TPM_ORD_SaveState */
 	};
 	struct tpm_header response;
+	size_t nread;
+	bool endwrite = false, endread = false;
+	int error;
 
-	if ((*sc->sc_intf->write)(sc, &command, sizeof(command)) != 0)
-		return false;
-	if ((*sc->sc_intf->read)(sc, &response, sizeof(response), NULL, 0) != 0)
-		return false;
-	if (TPM_BE32(response.code) != 0)
-		return false;
+	/*
+	 * Write the command.
+	 */
+	error = (*sc->sc_intf->start)(sc, UIO_WRITE);
+	if (error) {
+		device_printf(sc->sc_dev, "start write failed: %d", error);
+		goto out;
+	}
+
+	endwrite = true;
+
+	error = (*sc->sc_intf->write)(sc, &command, sizeof(command));
+	if (error) {
+		device_printf(sc->sc_dev, "write TPM_ORD_SaveState failed: %d",
+		    error);
+		goto out;
+	}
+
+	endwrite = false;
+
+	error = (*sc->sc_intf->end)(sc, UIO_WRITE, 0);
+	if (error) {
+		device_printf(sc->sc_dev, "end write failed: %d", error);
+		goto out;
+	}
+
+	/*
+	 * Read the response -- just the header; we don't expect a
+	 * payload.
+	 */
+	error = (*sc->sc_intf->start)(sc, UIO_READ);
+	if (error) {
+		device_printf(sc->sc_dev, "start read failed: %d", error);
+		goto out;
+	}
+
+	endread = true;
+
+	error = (*sc->sc_intf->read)(sc, &response, sizeof(response), &nread,
+	    0);
+	if (error) {
+		device_printf(sc->sc_dev, "read failed: %d", error);
+		goto out;
+	}
+	if (nread != sizeof(response)) {
+		device_printf(sc->sc_dev, "short header read: %zu", nread);
+		goto out;
+	}
 
+	endread = false;
+
+	error = (*sc->sc_intf->end)(sc, UIO_READ, 0);
+	if (error) {
+		device_printf(sc->sc_dev, "end read failed: %d", error);
+		goto out;
+	}
+
+	/*
+	 * Verify the response looks reasonable.
+	 */
+	if (be16toh(response.tag) != TPM_TAG_RSP_COMMAND ||
+	    be32toh(response.length) != sizeof(response) ||
+	    be32toh(response.code) != 0) {
+		device_printf(sc->sc_dev,
+		    "TPM_ORD_SaveState failed: tag=0x%x length=0x%x code=0x%x",
+		    be16toh(response.tag),
+		    be32toh(response.length),
+		    be32toh(response.code));
+		error = EIO;
+		goto out;
+	}
+
+	/* Success!  */
+	error = 0;
+
+out:	if (endwrite)
+		error = (*sc->sc_intf->end)(sc, UIO_WRITE, error);
+	if (endread)
+		error = (*sc->sc_intf->end)(sc, UIO_READ, error);
+	if (error)
+		return false;
 	return true;
 }
 
@@ -158,14 +239,91 @@ tpm20_suspend(struct tpm_softc *sc)
 		0x00, 0x01		/* TPM_SU_STATE */
 	};
 	struct tpm_header response;
+	size_t nread;
+	bool endwrite = false, endread = false;
+	int error;
 
-	if ((*sc->sc_intf->write)(sc, &command, sizeof(command)) != 0)
-		return false;
-	if ((*sc->sc_intf->read)(sc, &response, sizeof(response), NULL, 0) != 0)
-		return false;
-	if (TPM_BE32(response.code) != 0)
-		return false;
+	/*
+	 * Write the command.
+	 */
+	error = (*sc->sc_intf->start)(sc, UIO_WRITE);
+	if (error) {
+		device_printf(sc->sc_dev, "start write failed: %d", error);
+		goto out;
+	}
+
+	endwrite = true;
+
+	error = (*sc->sc_intf->write)(sc, &command, sizeof(command));
+	if (error) {
+		device_printf(sc->sc_dev, "write TPM_ORD_SaveState failed: %d",
+		    error);
+		goto out;
+	}
+
+	endwrite = false;
+
+	error = (*sc->sc_intf->end)(sc, UIO_WRITE, 0);
+	if (error) {
+		device_printf(sc->sc_dev, "end write failed: %d", error);
+		goto out;
+	}
 
+	/*
+	 * Read the response -- just the header; we don't expect a
+	 * payload.
+	 */
+	error = (*sc->sc_intf->start)(sc, UIO_READ);
+	if (error) {
+		device_printf(sc->sc_dev, "start read failed: %d", error);
+		goto out;
+	}
+
+	endread = true;
+
+	error = (*sc->sc_intf->read)(sc, &response, sizeof(response), &nread,
+	    0);
+	if (error) {
+		device_printf(sc->sc_dev, "read failed: %d", error);
+		goto out;
+	}
+	if (nread != sizeof(response)) {
+		device_printf(sc->sc_dev, "short header read: %zu", nread);
+		goto out;
+	}
+
+	endread = false;
+
+	error = (*sc->sc_intf->end)(sc, UIO_READ, 0);
+	if (error) {
+		device_printf(sc->sc_dev, "end read failed: %d", error);
+		goto out;
+	}
+
+	/*
+	 * Verify the response looks reasonable.
+	 */
+	if (be16toh(response.tag) != TPM2_ST_NO_SESSIONS ||
+	    be32toh(response.length) != sizeof(response) ||
+	    be32toh(response.code) != TPM2_RC_SUCCESS) {
+		device_printf(sc->sc_dev,
+		    "TPM_CC_Shutdown failed: tag=0x%x length=0x%x code=0x%x",
+		    be16toh(response.tag),
+		    be32toh(response.length),
+		    be32toh(response.code));
+		error = EIO;
+		goto out;
+	}
+
+	/* Success!  */
+	error = 0;
+
+out:	if (endwrite)
+		error = (*sc->sc_intf->end)(sc, UIO_WRITE, error);
+	if (endread)
+		error = (*sc->sc_intf->end)(sc, UIO_READ, error);
+	if (error)
+		return false;
 	return true;
 }
 
@@ -324,10 +482,348 @@ tpm_tis12_probe(bus_space_tag_t bt, bus_
 }
 
 static int
+tpm12_rng(struct tpm_softc *sc, unsigned *entropybitsp)
+{
+	/*
+	 * 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 endwrite = false, endread = false;
+	size_t nread;
+	uint16_t tag;
+	uint32_t pktlen, code, nbytes, entropybits = 0;
+	int rv;
+
+	/* 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.
+	 */
+	CTASSERT(sizeof(response.bytes) <= UINT_MAX/(NBBY/2));
+	entropybits = (NBBY/2)*nbytes;
+	rnd_add_data(&sc->sc_rnd, response.bytes, nbytes, entropybits);
+
+out:	/* 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);
+
+	*entropybitsp = entropybits;
+	return rv;
+}
+
+static int
+tpm20_rng(struct tpm_softc *sc, unsigned *entropybitsp)
+{
+	/*
+	 * Trusted Platform Module Library, Family "2.0", Level 00
+	 * Revision 01.38, Part 3: Commands, Sec. 16.1 `TPM2_GetRandom'
+	 *
+	 * https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-3-Commands-01.38.pdf#page=133
+	 */
+	struct {
+		struct tpm_header hdr;
+		uint16_t bytesRequested;
+	} __packed command;
+	struct response {
+		struct tpm_header hdr;
+		uint16_t randomBytesSize;
+		uint8_t bytes[64];
+	} __packed response;
+	bool endwrite = false, endread = false;
+	size_t nread;
+	uint16_t tag;
+	uint32_t pktlen, code, nbytes, entropybits = 0;
+	int rv;
+
+	/* Encode the command.  */
+	memset(&command, 0, sizeof(command));
+	command.hdr.tag = htobe16(TPM2_ST_NO_SESSIONS);
+	command.hdr.length = htobe32(sizeof(command));
+	command.hdr.code = htobe32(TPM2_CC_GetRandom);
+	command.bytesRequested = htobe16(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 != TPM2_ST_NO_SESSIONS ||
+	    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 & TPM2_RC_WARN) {
+			aprint_debug_dev(sc->sc_dev, "%s: tpm busy,"
+			    " code=TPM_RC_WARN+0x%x\n",
+			    __func__, code & ~TPM2_RC_WARN);
+			rv = 0;
+		} else {
+			device_printf(sc->sc_dev, "bad tpm response:"
+			    " tag=%u len=%u code=0x%x\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 = be16toh(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.
+	 */
+	CTASSERT(sizeof(response.bytes) <= UINT_MAX/(NBBY/2));
+	entropybits = (NBBY/2)*nbytes;
+	rnd_add_data(&sc->sc_rnd, response.bytes, nbytes, entropybits);
+
+out:	/* 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);
+
+	*entropybitsp = entropybits;
+	return rv;
+}
+
+static void
+tpm_rng_work(struct work *wk, void *cookie)
+{
+	struct tpm_softc *sc = cookie;
+	unsigned nbytes, entropybits;
+	int rv;
+
+	/* Acknowledge the request.  */
+	nbytes = atomic_swap_uint(&sc->sc_rndpending, 0);
+
+	/* Lock the tpm while we do I/O transactions with it.  */
+	mutex_enter(&sc->sc_lock);
+
+	/*
+	 * Issue as many commands as needed to fulfill the request, but
+	 * stop if anything fails.
+	 */
+	for (; nbytes; nbytes -= MIN(nbytes, MAX(1, entropybits/NBBY))) {
+		switch (sc->sc_ver) {
+		case TPM_1_2:
+			rv = tpm12_rng(sc, &entropybits);
+			break;
+		case TPM_2_0:
+			rv = tpm20_rng(sc, &entropybits);
+			break;
+		default:
+			panic("bad tpm version: %d", sc->sc_ver);
+		}
+		if (rv)
+			break;
+	}
+
+	/*
+	 * 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");
+		atomic_store_relaxed(&sc->sc_rnddisabled, true);
+		/* XXX worker thread can't workqueue_destroy its own queue */
+	}
+
+	/* Relinquish the tpm.  */
+	mutex_exit(&sc->sc_lock);
+}
+
+static void
+tpm_rng_get(size_t nbytes, void *cookie)
+{
+	struct tpm_softc *sc = cookie;
+
+	if (atomic_load_relaxed(&sc->sc_rnddisabled))
+		return;		/* tough */
+	if (atomic_swap_uint(&sc->sc_rndpending, MIN(nbytes, UINT_MAX/NBBY))
+	    == 0)
+		workqueue_enqueue(sc->sc_rndwq, &sc->sc_rndwk, NULL);
+}
+
+static int
 tpm_tis12_init(struct tpm_softc *sc)
 {
 	int rv;
 
+	aprint_naive("\n");
+	aprint_normal("\n");
+
 	sc->sc_caps = bus_space_read_4(sc->sc_bt, sc->sc_bh,
 	    TPM_INTF_CAPABILITY);
 	sc->sc_devid = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_ID);
@@ -342,6 +838,15 @@ tpm_tis12_init(struct tpm_softc *sc)
 	/* 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_rng_work, sc, PRI_NONE, IPL_VM, WQ_MPSAFE)) != 0)
+		return rv;
+	rndsource_setcb(&sc->sc_rnd, tpm_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;
 }
 
@@ -454,7 +959,7 @@ tpm_tis12_end(struct tpm_softc *sc, int 
 	if (rw == UIO_READ) {
 		rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO, sc->sc_intf->read);
 		if (rv)
-			return rv;
+			goto out;
 
 		/* Still more data? */
 		sc->sc_status = tpm_status(sc);
@@ -479,7 +984,7 @@ tpm_tis12_end(struct tpm_softc *sc, int 
 		    err ? TPM_STS_CMD_READY : TPM_STS_GO);
 	}
 
-	return rv;
+out:	return err ? err : rv;
 }
 
 const struct tpm_intf tpm_intf_tis12 = {
@@ -561,46 +1066,60 @@ tpmread(dev_t dev, struct uio *uio, int 
 	struct tpm_softc *sc = device_lookup_private(&tpm_cd, minor(dev));
 	struct tpm_header hdr;
 	uint8_t buf[TPM_BUFSIZ];
-	size_t cnt, len, n;
+	size_t cnt, len = 0/*XXXGCC*/;
+	bool end = false;
 	int rv;
 
 	if (sc == NULL)
 		return ENXIO;
 
+	mutex_enter(&sc->sc_lock);
+
 	if ((rv = (*sc->sc_intf->start)(sc, UIO_READ)))
-		return rv;
+		goto out;
+	end = true;
 
 	/* Get the header. */
 	if ((rv = (*sc->sc_intf->read)(sc, &hdr, sizeof(hdr), &cnt, 0))) {
 		goto out;
 	}
-	len = TPM_BE32(hdr.length);
-	if (len > uio->uio_resid || len < cnt) {
+	if (cnt != sizeof(hdr)) {
+		rv = EIO;
+		goto out;
+	}
+	len = be32toh(hdr.length);
+	if (len > MIN(sizeof(buf), uio->uio_resid) || len < sizeof(hdr)) {
 		rv = EIO;
 		goto out;
 	}
 
-	/* Copy out the header. */
-	if ((rv = uiomove(&hdr, cnt, uio))) {
+	/* Get the payload. */
+	len -= sizeof(hdr);
+	if ((rv = (*sc->sc_intf->read)(sc, buf, len, NULL, TPM_PARAM_SIZE))) {
 		goto out;
 	}
 
-	/* Process the rest. */
-	len -= cnt;
-	while (len > 0) {
-		n = MIN(sizeof(buf), len);
-		if ((rv = (*sc->sc_intf->read)(sc, buf, n, NULL, TPM_PARAM_SIZE))) {
-			goto out;
-		}
-		if ((rv = uiomove(buf, n, uio))) {
-			goto out;
-		}
-		len -= n;
+out:	if (end)
+		rv = (*sc->sc_intf->end)(sc, UIO_READ, rv);
+
+	mutex_exit(&sc->sc_lock);
+
+	/* If anything went wrong, stop here -- nothing to copy out. */
+	if (rv)
+		return rv;
+
+	/* Copy out the header. */
+	if ((rv = uiomove(&hdr, sizeof(hdr), uio))) {
+		return rv;
 	}
 
-out:
-	rv = (*sc->sc_intf->end)(sc, UIO_READ, rv);
-	return rv;
+	/* Copy out the payload.  */
+	if ((rv = uiomove(buf, len, uio))) {
+		return rv;
+	}
+
+	/* Success! */
+	return 0;
 }
 
 static int
@@ -608,6 +1127,7 @@ tpmwrite(dev_t dev, struct uio *uio, int
 {
 	struct tpm_softc *sc = device_lookup_private(&tpm_cd, minor(dev));
 	uint8_t buf[TPM_BUFSIZ];
+	bool end = false;
 	int n, rv;
 
 	if (sc == NULL)
@@ -615,17 +1135,24 @@ tpmwrite(dev_t dev, struct uio *uio, int
 
 	n = MIN(sizeof(buf), uio->uio_resid);
 	if ((rv = uiomove(buf, n, uio))) {
-		goto out;
+		return rv;
 	}
+
+	mutex_enter(&sc->sc_lock);
+
 	if ((rv = (*sc->sc_intf->start)(sc, UIO_WRITE))) {
 		goto out;
 	}
+	end = true;
+
 	if ((rv = (*sc->sc_intf->write)(sc, buf, n))) {
 		goto out;
 	}
 
-	rv = (*sc->sc_intf->end)(sc, UIO_WRITE, rv);
-out:
+out:	if (end)
+		rv = (*sc->sc_intf->end)(sc, UIO_WRITE, rv);
+
+	mutex_exit(&sc->sc_lock);
 	return rv;
 }
 

Index: src/sys/dev/ic/tpmreg.h
diff -u src/sys/dev/ic/tpmreg.h:1.4.2.1 src/sys/dev/ic/tpmreg.h:1.4.2.2
--- src/sys/dev/ic/tpmreg.h:1.4.2.1	Wed Oct 16 09:52:38 2019
+++ src/sys/dev/ic/tpmreg.h	Wed Aug  3 16:00:47 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: tpmreg.h,v 1.4.2.1 2019/10/16 09:52:38 martin Exp $	*/
+/*	$NetBSD: tpmreg.h,v 1.4.2.2 2022/08/03 16:00:47 martin Exp $	*/
 
 /*
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -29,13 +29,13 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#if (BYTE_ORDER == LITTLE_ENDIAN)
-#define TPM_BE16(a)	bswap16(a)
-#define TPM_BE32(a)	bswap32(a)
-#else
-#define TPM_BE16(a)	(a)
-#define TPM_BE32(a)	(a)
-#endif
+#ifndef	DEV_IC_TPMREG_H
+#define	DEV_IC_TPMREG_H
+
+#include <sys/types.h>
+
+#include <sys/cdefs.h>
+#include <sys/endian.h>
 
 struct tpm_header {
 	uint16_t tag;
@@ -102,6 +102,63 @@ struct tpm_header {
 #define	TPM_REV				0x0f04	/* 8bit register */
 
 /*
- * Five localities, 4K per locality.
+ * Five localities, 4K per locality.  But we only use the registers for
+ * the first locality, so this is 0x1000 rather than 0x5000.
+ */
+#define	TPM_SPACE_SIZE	0x1000
+
+#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
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Trusted Platform Module Library Specification, Family "2.0",
+ * Level 00, Revision 01.59 -- November 2019
+ *
+ * https://trustedcomputinggroup.org/resource/tpm-library-specification/
+ *
+ * Where this spec names things TPM_* that don't obviously coincide
+ * with the 1.2 things, we name them TPM2_*.
  */
-#define	TPM_SPACE_SIZE	0x5000
+
+/* https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-4-Supporting-Routines-01.38-code.pdf#page=172 */
+#define	TPM2_ST_RSP_COMMAND		0x00c4
+#define	TPM2_ST_NULL			0x8000
+#define	TPM2_ST_NO_SESSIONS		0x8001
+#define	TPM2_ST_SESSIONS		0x8002
+/* ... */
+
+/* https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf#page=45 */
+#define	TPM2_CC_GetRandom		0x0000017b
+
+/* https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf#page=53 */
+#define	TPM2_RC_SUCCESS			0x000
+#define	TPM2_RC_BAD_TAG			0x01e
+
+#define	TPM2_RC_VER1			0x100
+
+#define	TPM2_RC_FMT1			0x080
+
+#define	TPM2_RC_WARN			0x900
+#define	TPM2_RC_TESTING			(TPM2_RC_WARN + 0x00a)
+#define	TPM2_RC_RETRY			(TPM2_RC_WARN + 0x022)
+
+#endif	/* DEV_IC_TPMREG_H */
Index: src/sys/dev/ic/tpmvar.h
diff -u src/sys/dev/ic/tpmvar.h:1.4.2.1 src/sys/dev/ic/tpmvar.h:1.4.2.2
--- src/sys/dev/ic/tpmvar.h:1.4.2.1	Wed Oct 16 09:52:38 2019
+++ src/sys/dev/ic/tpmvar.h	Wed Aug  3 16:00:47 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: tpmvar.h,v 1.4.2.1 2019/10/16 09:52:38 martin Exp $	*/
+/*	$NetBSD: tpmvar.h,v 1.4.2.2 2022/08/03 16:00:47 martin Exp $	*/
 
 /*
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -29,6 +29,11 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifndef	DEV_IC_TPMVAR_H
+#define	DEV_IC_TPMVAR_H
+
+#include <sys/types.h>
+
 #define TPM_API_VERSION		1
 
 enum tpm_version {
@@ -54,6 +59,12 @@ struct tpm_ioc_getinfo {
 
 #ifdef _KERNEL
 
+#include <sys/bus.h>
+#include <sys/device_if.h>
+#include <sys/mutex.h>
+#include <sys/rndsource.h>
+#include <sys/workqueue.h>
+
 struct tpm_softc;
 
 struct tpm_intf {
@@ -82,9 +93,17 @@ struct tpm_softc {
 	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 sc_rnddisabled;
 };
 
 bool tpm_suspend(device_t, const pmf_qual_t *);
 bool tpm_resume(device_t, const pmf_qual_t *);
 
 #endif
+
+#endif	/* DEV_IC_TPMVAR_H */

Reply via email to