1 /*- 2 * Copyright (c) 2018 Stormshield. 3 * Copyright (c) 2018 Semihalf. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include "tpm20.h" 32 33 MALLOC_DECLARE(M_TPM20); 34 MALLOC_DEFINE(M_TPM20, "tpm_buffer", "buffer for tpm 2.0 driver"); 35 36 static void tpm20_discard_buffer(void *arg); 37 static int tpm20_save_state(device_t dev, bool suspend); 38 39 static d_open_t tpm20_open; 40 static d_close_t tpm20_close; 41 static d_read_t tpm20_read; 42 static d_write_t tpm20_write; 43 static d_ioctl_t tpm20_ioctl; 44 45 static struct cdevsw tpm20_cdevsw = { 46 .d_version = D_VERSION, 47 .d_open = tpm20_open, 48 .d_close = tpm20_close, 49 .d_read = tpm20_read, 50 .d_write = tpm20_write, 51 .d_ioctl = tpm20_ioctl, 52 .d_name = "tpm20", 53 }; 54 55 int 56 tpm20_read(struct cdev *dev, struct uio *uio, int flags) 57 { 58 struct tpm_sc *sc; 59 size_t bytes_to_transfer; 60 int result = 0; 61 62 sc = (struct tpm_sc *)dev->si_drv1; 63 64 callout_stop(&sc->discard_buffer_callout); 65 sx_xlock(&sc->dev_lock); 66 67 bytes_to_transfer = MIN(sc->pending_data_length, uio->uio_resid); 68 if (bytes_to_transfer > 0) { 69 result = uiomove((caddr_t) sc->buf, bytes_to_transfer, uio); 70 memset(sc->buf, 0, TPM_BUFSIZE); 71 sc->pending_data_length = 0; 72 cv_signal(&sc->buf_cv); 73 } else { 74 result = ETIMEDOUT; 75 } 76 77 sx_xunlock(&sc->dev_lock); 78 79 return (result); 80 } 81 82 int 83 tpm20_write(struct cdev *dev, struct uio *uio, int flags) 84 { 85 struct tpm_sc *sc; 86 size_t byte_count; 87 int result = 0; 88 89 sc = (struct tpm_sc *)dev->si_drv1; 90 91 byte_count = uio->uio_resid; 92 if (byte_count < TPM_HEADER_SIZE) { 93 device_printf(sc->dev, 94 "Requested transfer is too small\n"); 95 return (EINVAL); 96 } 97 98 if (byte_count > TPM_BUFSIZE) { 99 device_printf(sc->dev, 100 "Requested transfer is too large\n"); 101 return (E2BIG); 102 } 103 104 sx_xlock(&sc->dev_lock); 105 106 while (sc->pending_data_length != 0) 107 cv_wait(&sc->buf_cv, &sc->dev_lock); 108 109 result = uiomove(sc->buf, byte_count, uio); 110 if (result != 0) { 111 sx_xunlock(&sc->dev_lock); 112 return (result); 113 } 114 115 result = sc->transmit(sc, byte_count); 116 117 if (result == 0) 118 callout_reset(&sc->discard_buffer_callout, 119 TPM_READ_TIMEOUT / tick, tpm20_discard_buffer, sc); 120 121 sx_xunlock(&sc->dev_lock); 122 return (result); 123 } 124 125 static void tpm20_discard_buffer(void *arg) 126 { 127 struct tpm_sc *sc; 128 129 sc = (struct tpm_sc *)arg; 130 if (callout_pending(&sc->discard_buffer_callout)) 131 return; 132 133 sx_xlock(&sc->dev_lock); 134 135 memset(sc->buf, 0, TPM_BUFSIZE); 136 sc->pending_data_length = 0; 137 138 cv_signal(&sc->buf_cv); 139 sx_xunlock(&sc->dev_lock); 140 141 device_printf(sc->dev, 142 "User failed to read buffer in time\n"); 143 } 144 145 int 146 tpm20_open(struct cdev *dev, int flag, int mode, struct thread *td) 147 { 148 149 return (0); 150 } 151 152 int 153 tpm20_close(struct cdev *dev, int flag, int mode, struct thread *td) 154 { 155 156 return (0); 157 } 158 159 160 int 161 tpm20_ioctl(struct cdev *dev, u_long cmd, caddr_t data, 162 int flags, struct thread *td) 163 { 164 165 return (ENOTTY); 166 } 167 168 int 169 tpm20_init(struct tpm_sc *sc) 170 { 171 struct make_dev_args args; 172 int result; 173 174 sc->buf = malloc(TPM_BUFSIZE, M_TPM20, M_WAITOK); 175 sx_init(&sc->dev_lock, "TPM driver lock"); 176 cv_init(&sc->buf_cv, "TPM buffer cv"); 177 callout_init(&sc->discard_buffer_callout, 1); 178 sc->pending_data_length = 0; 179 180 make_dev_args_init(&args); 181 args.mda_devsw = &tpm20_cdevsw; 182 args.mda_uid = UID_ROOT; 183 args.mda_gid = GID_WHEEL; 184 args.mda_mode = TPM_CDEV_PERM_FLAG; 185 args.mda_si_drv1 = sc; 186 result = make_dev_s(&args, &sc->sc_cdev, TPM_CDEV_NAME); 187 if (result != 0) 188 tpm20_release(sc); 189 190 return (result); 191 192 } 193 194 void 195 tpm20_release(struct tpm_sc *sc) 196 { 197 198 if (sc->buf != NULL) 199 free(sc->buf, M_TPM20); 200 201 sx_destroy(&sc->dev_lock); 202 cv_destroy(&sc->buf_cv); 203 if (sc->sc_cdev != NULL) 204 destroy_dev(sc->sc_cdev); 205 } 206 207 208 int 209 tpm20_suspend(device_t dev) 210 { 211 return (tpm20_save_state(dev, true)); 212 } 213 214 int 215 tpm20_shutdown(device_t dev) 216 { 217 return (tpm20_save_state(dev, false)); 218 } 219 220 static int 221 tpm20_save_state(device_t dev, bool suspend) 222 { 223 struct tpm_sc *sc; 224 uint8_t save_cmd[] = { 225 0x80, 0x01, /* TPM_ST_NO_SESSIONS tag*/ 226 0x00, 0x00, 0x00, 0x0C, /* cmd length */ 227 0x00, 0x00, 0x01, 0x45, 0x00, 0x00 /* cmd TPM_CC_Shutdown */ 228 }; 229 230 sc = device_get_softc(dev); 231 232 /* 233 * Inform the TPM whether we are going to suspend or reboot/shutdown. 234 */ 235 if (suspend) 236 save_cmd[11] = 1; /* TPM_SU_STATE */ 237 238 if (sc == NULL || sc->buf == NULL) 239 return (0); 240 241 sx_xlock(&sc->dev_lock); 242 243 memcpy(sc->buf, save_cmd, sizeof(save_cmd)); 244 sc->transmit(sc, sizeof(save_cmd)); 245 246 sx_xunlock(&sc->dev_lock); 247 248 return (0); 249 } 250 251 int32_t 252 tpm20_get_timeout(uint32_t command) 253 { 254 int32_t timeout; 255 256 switch (command) { 257 case TPM_CC_CreatePrimary: 258 case TPM_CC_Create: 259 case TPM_CC_CreateLoaded: 260 timeout = TPM_TIMEOUT_LONG; 261 break; 262 case TPM_CC_SequenceComplete: 263 case TPM_CC_Startup: 264 case TPM_CC_SequenceUpdate: 265 case TPM_CC_GetCapability: 266 case TPM_CC_PCR_Extend: 267 case TPM_CC_EventSequenceComplete: 268 case TPM_CC_HashSequenceStart: 269 timeout = TPM_TIMEOUT_C; 270 break; 271 default: 272 timeout = TPM_TIMEOUT_B; 273 break; 274 } 275 return timeout; 276 } 277