diff options
| -rw-r--r-- | share/man/man4/tpm.4 | 14 | ||||
| -rw-r--r-- | sys/dev/acpi/tpm_acpi.c | 8 | ||||
| -rw-r--r-- | sys/dev/ic/tpm.c | 615 | ||||
| -rw-r--r-- | sys/dev/ic/tpmreg.h | 77 | ||||
| -rw-r--r-- | sys/dev/ic/tpmvar.h | 21 |
5 files changed, 674 insertions, 61 deletions
diff --git a/share/man/man4/tpm.4 b/share/man/man4/tpm.4 index 4e565e1e6b5..0d28de8fc54 100644 --- a/share/man/man4/tpm.4 +++ b/share/man/man4/tpm.4 @@ -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 diff --git a/sys/dev/acpi/tpm_acpi.c b/sys/dev/acpi/tpm_acpi.c index a639e20cc9a..9e8ddaaed58 100644 --- a/sys/dev/acpi/tpm_acpi.c +++ b/sys/dev/acpi/tpm_acpi.c @@ -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_t self, void *aux) 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; } diff --git a/sys/dev/ic/tpm.c b/sys/dev/ic/tpm.c index 66f82291010..74577977b01 100644 --- a/sys/dev/ic/tpm.c +++ b/sys/dev/ic/tpm.c @@ -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_space_handle_t bh) } 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 rw, int err) 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 rw, int err) 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 flags) 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 flags) { 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 flags) 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; } diff --git a/sys/dev/ic/tpmreg.h b/sys/dev/ic/tpmreg.h index 0cc988c195b..218c9c1dc1c 100644 --- a/sys/dev/ic/tpmreg.h +++ b/sys/dev/ic/tpmreg.h @@ -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 */ diff --git a/sys/dev/ic/tpmvar.h b/sys/dev/ic/tpmvar.h index c8df058e05b..f655efe48ea 100644 --- a/sys/dev/ic/tpmvar.h +++ b/sys/dev/ic/tpmvar.h @@ -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 */ |
