Index: head/sys/dev/tpm/tpm20.c =================================================================== --- head/sys/dev/tpm/tpm20.c (revision 346258) +++ head/sys/dev/tpm/tpm20.c (revision 346259) @@ -1,352 +1,358 @@ /*- * Copyright (c) 2018 Stormshield. * Copyright (c) 2018 Semihalf. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include "tpm20.h" #define TPM_HARVEST_SIZE 16 /* * Perform a harvest every 10 seconds. * Since discrete TPMs are painfully slow * we don't want to execute this too often * as the chip is likely to be used by others too. */ #define TPM_HARVEST_INTERVAL 10000000 MALLOC_DECLARE(M_TPM20); MALLOC_DEFINE(M_TPM20, "tpm_buffer", "buffer for tpm 2.0 driver"); static void tpm20_discard_buffer(void *arg); #ifdef TPM_HARVEST static void tpm20_harvest(void *arg); #endif static int tpm20_save_state(device_t dev, bool suspend); static d_open_t tpm20_open; static d_close_t tpm20_close; static d_read_t tpm20_read; static d_write_t tpm20_write; static d_ioctl_t tpm20_ioctl; static struct cdevsw tpm20_cdevsw = { .d_version = D_VERSION, .d_open = tpm20_open, .d_close = tpm20_close, .d_read = tpm20_read, .d_write = tpm20_write, .d_ioctl = tpm20_ioctl, .d_name = "tpm20", }; int tpm20_read(struct cdev *dev, struct uio *uio, int flags) { struct tpm_sc *sc; size_t bytes_to_transfer; int result = 0; sc = (struct tpm_sc *)dev->si_drv1; callout_stop(&sc->discard_buffer_callout); sx_xlock(&sc->dev_lock); + if (sc->owner_tid != uio->uio_td->td_tid) { + sx_xunlock(&sc->dev_lock); + return (EPERM); + } bytes_to_transfer = MIN(sc->pending_data_length, uio->uio_resid); if (bytes_to_transfer > 0) { result = uiomove((caddr_t) sc->buf, bytes_to_transfer, uio); memset(sc->buf, 0, TPM_BUFSIZE); sc->pending_data_length = 0; cv_signal(&sc->buf_cv); } else { result = ETIMEDOUT; } sx_xunlock(&sc->dev_lock); return (result); } int tpm20_write(struct cdev *dev, struct uio *uio, int flags) { struct tpm_sc *sc; size_t byte_count; int result = 0; sc = (struct tpm_sc *)dev->si_drv1; byte_count = uio->uio_resid; if (byte_count < TPM_HEADER_SIZE) { device_printf(sc->dev, "Requested transfer is too small\n"); return (EINVAL); } if (byte_count > TPM_BUFSIZE) { device_printf(sc->dev, "Requested transfer is too large\n"); return (E2BIG); } sx_xlock(&sc->dev_lock); while (sc->pending_data_length != 0) cv_wait(&sc->buf_cv, &sc->dev_lock); result = uiomove(sc->buf, byte_count, uio); if (result != 0) { sx_xunlock(&sc->dev_lock); return (result); } result = sc->transmit(sc, byte_count); - if (result == 0) + if (result == 0) { callout_reset(&sc->discard_buffer_callout, TPM_READ_TIMEOUT / tick, tpm20_discard_buffer, sc); + sc->owner_tid = uio->uio_td->td_tid; + } sx_xunlock(&sc->dev_lock); return (result); } static void tpm20_discard_buffer(void *arg) { struct tpm_sc *sc; sc = (struct tpm_sc *)arg; if (callout_pending(&sc->discard_buffer_callout)) return; sx_xlock(&sc->dev_lock); memset(sc->buf, 0, TPM_BUFSIZE); sc->pending_data_length = 0; cv_signal(&sc->buf_cv); sx_xunlock(&sc->dev_lock); device_printf(sc->dev, "User failed to read buffer in time\n"); } int tpm20_open(struct cdev *dev, int flag, int mode, struct thread *td) { return (0); } int tpm20_close(struct cdev *dev, int flag, int mode, struct thread *td) { return (0); } int tpm20_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { return (ENOTTY); } int tpm20_init(struct tpm_sc *sc) { struct make_dev_args args; int result; sc->buf = malloc(TPM_BUFSIZE, M_TPM20, M_WAITOK); sx_init(&sc->dev_lock, "TPM driver lock"); cv_init(&sc->buf_cv, "TPM buffer cv"); callout_init(&sc->discard_buffer_callout, 1); #ifdef TPM_HARVEST sc->harvest_ticks = TPM_HARVEST_INTERVAL / tick; callout_init(&sc->harvest_callout, 1); callout_reset(&sc->harvest_callout, 0, tpm20_harvest, sc); #endif sc->pending_data_length = 0; make_dev_args_init(&args); args.mda_devsw = &tpm20_cdevsw; args.mda_uid = UID_ROOT; args.mda_gid = GID_WHEEL; args.mda_mode = TPM_CDEV_PERM_FLAG; args.mda_si_drv1 = sc; result = make_dev_s(&args, &sc->sc_cdev, TPM_CDEV_NAME); if (result != 0) tpm20_release(sc); return (result); } void tpm20_release(struct tpm_sc *sc) { #ifdef TPM_HARVEST callout_drain(&sc->harvest_callout); #endif if (sc->buf != NULL) free(sc->buf, M_TPM20); sx_destroy(&sc->dev_lock); cv_destroy(&sc->buf_cv); if (sc->sc_cdev != NULL) destroy_dev(sc->sc_cdev); } int tpm20_suspend(device_t dev) { return (tpm20_save_state(dev, true)); } int tpm20_shutdown(device_t dev) { return (tpm20_save_state(dev, false)); } #ifdef TPM_HARVEST /* * Get TPM_HARVEST_SIZE random bytes and add them * into system entropy pool. */ static void tpm20_harvest(void *arg) { struct tpm_sc *sc; unsigned char entropy[TPM_HARVEST_SIZE]; uint16_t entropy_size; int result; uint8_t cmd[] = { 0x80, 0x01, /* TPM_ST_NO_SESSIONS tag*/ 0x00, 0x00, 0x00, 0x0c, /* cmd length */ 0x00, 0x00, 0x01, 0x7b, /* cmd TPM_CC_GetRandom */ 0x00, TPM_HARVEST_SIZE /* number of bytes requested */ }; sc = arg; sx_xlock(&sc->dev_lock); while (sc->pending_data_length != 0) cv_wait(&sc->buf_cv, &sc->dev_lock); memcpy(sc->buf, cmd, sizeof(cmd)); result = sc->transmit(sc, sizeof(cmd)); if (result != 0) { sx_xunlock(&sc->dev_lock); return; } /* Ignore response size */ sc->pending_data_length = 0; /* The number of random bytes we got is placed right after the header */ entropy_size = (uint16_t) sc->buf[TPM_HEADER_SIZE + 1]; if (entropy_size > 0) { entropy_size = MIN(entropy_size, TPM_HARVEST_SIZE); memcpy(entropy, sc->buf + TPM_HEADER_SIZE + sizeof(uint16_t), entropy_size); } sx_xunlock(&sc->dev_lock); if (entropy_size > 0) random_harvest_queue(entropy, entropy_size, RANDOM_PURE_TPM); callout_reset(&sc->harvest_callout, sc->harvest_ticks, tpm20_harvest, sc); } #endif /* TPM_HARVEST */ static int tpm20_save_state(device_t dev, bool suspend) { struct tpm_sc *sc; uint8_t save_cmd[] = { 0x80, 0x01, /* TPM_ST_NO_SESSIONS tag*/ 0x00, 0x00, 0x00, 0x0C, /* cmd length */ 0x00, 0x00, 0x01, 0x45, 0x00, 0x00 /* cmd TPM_CC_Shutdown */ }; sc = device_get_softc(dev); /* * Inform the TPM whether we are going to suspend or reboot/shutdown. */ if (suspend) save_cmd[11] = 1; /* TPM_SU_STATE */ if (sc == NULL || sc->buf == NULL) return (0); sx_xlock(&sc->dev_lock); memcpy(sc->buf, save_cmd, sizeof(save_cmd)); sc->transmit(sc, sizeof(save_cmd)); sx_xunlock(&sc->dev_lock); return (0); } int32_t tpm20_get_timeout(uint32_t command) { int32_t timeout; switch (command) { case TPM_CC_CreatePrimary: case TPM_CC_Create: case TPM_CC_CreateLoaded: timeout = TPM_TIMEOUT_LONG; break; case TPM_CC_SequenceComplete: case TPM_CC_Startup: case TPM_CC_SequenceUpdate: case TPM_CC_GetCapability: case TPM_CC_PCR_Extend: case TPM_CC_EventSequenceComplete: case TPM_CC_HashSequenceStart: timeout = TPM_TIMEOUT_C; break; default: timeout = TPM_TIMEOUT_B; break; } return timeout; } Index: head/sys/dev/tpm/tpm20.h =================================================================== --- head/sys/dev/tpm/tpm20.h (revision 346258) +++ head/sys/dev/tpm/tpm20.h (revision 346259) @@ -1,190 +1,191 @@ /*- * Copyright (c) 2018 Stormshield. * Copyright (c) 2018 Semihalf. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _TPM20_H_ #define _TPM20_H_ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt_acpi.h" #include "opt_tpm.h" #define BIT(x) (1 << (x)) /* Timeouts in us */ #define TPM_TIMEOUT_A 750000 #define TPM_TIMEOUT_B 2000000 #define TPM_TIMEOUT_C 200000 #define TPM_TIMEOUT_D 30000 /* * Generating RSA key pair takes ~(10-20s), which is significantly longer than * any timeout defined in spec. Because of that we need a new one. */ #define TPM_TIMEOUT_LONG 40000000 /* List of commands that require TPM_TIMEOUT_LONG time to complete */ #define TPM_CC_CreatePrimary 0x00000131 #define TPM_CC_Create 0x00000153 #define TPM_CC_CreateLoaded 0x00000191 /* List of commands that require only TPM_TIMEOUT_C time to complete */ #define TPM_CC_SequenceComplete 0x0000013e #define TPM_CC_Startup 0x00000144 #define TPM_CC_SequenceUpdate 0x0000015c #define TPM_CC_GetCapability 0x0000017a #define TPM_CC_PCR_Extend 0x00000182 #define TPM_CC_EventSequenceComplete 0x00000185 #define TPM_CC_HashSequenceStart 0x00000186 /* Timeout before data in read buffer is discarded */ #define TPM_READ_TIMEOUT 500000 #define TPM_BUFSIZE 0x1000 #define TPM_HEADER_SIZE 10 #define TPM_CDEV_NAME "tpm0" #define TPM_CDEV_PERM_FLAG 0600 #define TPM2_START_METHOD_ACPI 2 #define TPM2_START_METHOD_TIS 6 #define TPM2_START_METHOD_CRB 7 #define TPM2_START_METHOD_CRB_ACPI 8 struct tpm_sc { device_t dev; struct resource *mem_res; struct resource *irq_res; int mem_rid; int irq_rid; struct cdev *sc_cdev; struct sx dev_lock; struct cv buf_cv; void *intr_cookie; int intr_type; /* Current event type */ bool interrupts; uint8_t *buf; size_t pending_data_length; + lwpid_t owner_tid; struct callout discard_buffer_callout; #ifdef TPM_HARVEST struct callout harvest_callout; int harvest_ticks; #endif int (*transmit)(struct tpm_sc *, size_t); }; int tpm20_suspend(device_t dev); int tpm20_shutdown(device_t dev); int32_t tpm20_get_timeout(uint32_t command); int tpm20_init(struct tpm_sc *sc); void tpm20_release(struct tpm_sc *sc); /* Small helper routines for io ops */ static inline uint8_t RD1(struct tpm_sc *sc, bus_size_t off) { return (bus_read_1(sc->mem_res, off)); } static inline uint32_t RD4(struct tpm_sc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off)); } #ifdef __amd64__ static inline uint64_t RD8(struct tpm_sc *sc, bus_size_t off) { return (bus_read_8(sc->mem_res, off)); } #endif static inline void WR1(struct tpm_sc *sc, bus_size_t off, uint8_t val) { bus_write_1(sc->mem_res, off, val); } static inline void WR4(struct tpm_sc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off, val); } static inline void AND4(struct tpm_sc *sc, bus_size_t off, uint32_t val) { WR4(sc, off, RD4(sc, off) & val); } static inline void OR1(struct tpm_sc *sc, bus_size_t off, uint8_t val) { WR1(sc, off, RD1(sc, off) | val); } static inline void OR4(struct tpm_sc *sc, bus_size_t off, uint32_t val) { WR4(sc, off, RD4(sc, off) | val); } #endif /* _TPM20_H_ */