xref: /freebsd/sys/dev/tpm/tpm20.c (revision c25976f0a9a3a102ce47b45c19b2c93e8069433b)
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/random.h>
29 #include <dev/random/randomdev.h>
30 
31 #include "tpm20.h"
32 
33 #define TPM_HARVEST_SIZE     16
34 /*
35  * Perform a harvest every 10 seconds.
36  * Since discrete TPMs are painfully slow
37  * we don't want to execute this too often
38  * as the chip is likely to be used by others too.
39  */
40 #define TPM_HARVEST_INTERVAL 10
41 
42 MALLOC_DEFINE(M_TPM20, "tpm_buffer", "buffer for tpm 2.0 driver");
43 
44 #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
45 static void tpm20_harvest(void *arg, int unused);
46 #endif
47 static int  tpm20_restart(device_t dev, bool clear);
48 static int  tpm20_save_state(device_t dev, bool suspend);
49 
50 static d_open_t		tpm20_open;
51 static d_close_t	tpm20_close;
52 static d_read_t		tpm20_read;
53 static d_write_t	tpm20_write;
54 static d_ioctl_t	tpm20_ioctl;
55 
56 static struct cdevsw tpm20_cdevsw = {
57 	.d_version = D_VERSION,
58 	.d_open = tpm20_open,
59 	.d_close = tpm20_close,
60 	.d_read = tpm20_read,
61 	.d_write = tpm20_write,
62 	.d_ioctl = tpm20_ioctl,
63 	.d_name = "tpm20",
64 };
65 
66 int
67 tpm20_read(struct cdev *dev, struct uio *uio, int flags)
68 {
69 	struct tpm_sc *sc;
70 	struct tpm_priv *priv;
71 	size_t bytes_to_transfer;
72 	size_t offset;
73 	int result = 0;
74 
75 	sc = (struct tpm_sc *)dev->si_drv1;
76 	devfs_get_cdevpriv((void **)&priv);
77 
78 	sx_xlock(&sc->dev_lock);
79 	offset = priv->offset;
80 	bytes_to_transfer = MIN(priv->len, uio->uio_resid);
81 	if (bytes_to_transfer > 0) {
82 		result = uiomove((caddr_t) priv->buf + offset, bytes_to_transfer, uio);
83 		priv->offset += bytes_to_transfer;
84 		priv->len -= bytes_to_transfer;
85 	} else {
86 		result = 0;
87 	}
88 
89 	sx_xunlock(&sc->dev_lock);
90 
91 	return (result);
92 }
93 
94 int
95 tpm20_write(struct cdev *dev, struct uio *uio, int flags)
96 {
97 	struct tpm_sc *sc;
98 	struct tpm_priv *priv;
99 	size_t byte_count;
100 	int result = 0;
101 
102 	sc = (struct tpm_sc *)dev->si_drv1;
103 	devfs_get_cdevpriv((void **)&priv);
104 
105 	byte_count = uio->uio_resid;
106 	if (byte_count < TPM_HEADER_SIZE) {
107 		device_printf(sc->dev,
108 		    "Requested transfer is too small\n");
109 		return (EINVAL);
110 	}
111 
112 	if (byte_count > TPM_BUFSIZE) {
113 		device_printf(sc->dev,
114 		    "Requested transfer is too large\n");
115 		return (E2BIG);
116 	}
117 
118 	sx_xlock(&sc->dev_lock);
119 
120 	result = uiomove(priv->buf, byte_count, uio);
121 	if (result != 0) {
122 		sx_xunlock(&sc->dev_lock);
123 		return (result);
124 	}
125 
126 	result = TPM_TRANSMIT(sc->dev, priv, byte_count);
127 
128 	sx_xunlock(&sc->dev_lock);
129 	return (result);
130 }
131 
132 static struct tpm_priv *
133 tpm20_priv_alloc(void)
134 {
135 	struct tpm_priv *priv;
136 
137 	priv = malloc(sizeof (*priv), M_TPM20, M_WAITOK | M_ZERO);
138 	return (priv);
139 }
140 
141 static void
142 tpm20_priv_dtor(void *data)
143 {
144 	struct tpm_priv *priv = data;
145 
146 	free(priv->buf, M_TPM20);
147 }
148 
149 int
150 tpm20_open(struct cdev *dev, int flag, int mode, struct thread *td)
151 {
152 	struct tpm_priv *priv;
153 
154 	priv = tpm20_priv_alloc();
155 	devfs_set_cdevpriv(priv, tpm20_priv_dtor);
156 
157 	return (0);
158 }
159 
160 int
161 tpm20_close(struct cdev *dev, int flag, int mode, struct thread *td)
162 {
163 
164 	return (0);
165 }
166 
167 int
168 tpm20_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
169     int flags, struct thread *td)
170 {
171 
172 	return (ENOTTY);
173 }
174 
175 #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
176 static const struct random_source random_tpm = {
177 	.rs_ident = "TPM",
178 	.rs_source = RANDOM_PURE_TPM,
179 };
180 #endif
181 
182 int
183 tpm20_init(struct tpm_sc *sc)
184 {
185 	struct make_dev_args args;
186 	int result;
187 
188 	sc->internal_priv = tpm20_priv_alloc();
189 
190 	make_dev_args_init(&args);
191 	args.mda_devsw = &tpm20_cdevsw;
192 	args.mda_uid = UID_ROOT;
193 	args.mda_gid = GID_WHEEL;
194 	args.mda_mode = TPM_CDEV_PERM_FLAG;
195 	args.mda_si_drv1 = sc;
196 	result = make_dev_s(&args, &sc->sc_cdev, TPM_CDEV_NAME);
197 	if (result != 0)
198 		tpm20_release(sc);
199 
200 #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
201 	random_source_register(&random_tpm);
202 	TIMEOUT_TASK_INIT(taskqueue_thread, &sc->harvest_task, 0,
203 	    tpm20_harvest, sc);
204 	taskqueue_enqueue_timeout(taskqueue_thread, &sc->harvest_task, 0);
205 #endif
206 
207 	return (result);
208 
209 }
210 
211 void
212 tpm20_release(struct tpm_sc *sc)
213 {
214 
215 #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
216 	if (device_is_attached(sc->dev))
217 		taskqueue_drain_timeout(taskqueue_thread, &sc->harvest_task);
218 	random_source_deregister(&random_tpm);
219 #endif
220 
221 	tpm20_priv_dtor(sc->internal_priv);
222 	sx_destroy(&sc->dev_lock);
223 	if (sc->sc_cdev != NULL)
224 		destroy_dev(sc->sc_cdev);
225 }
226 
227 int
228 tpm20_resume(device_t dev)
229 {
230 
231 	tpm20_restart(dev, false);
232 
233 #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
234 	struct tpm_sc *sc;
235 
236 	sc = device_get_softc(dev);
237 	taskqueue_enqueue_timeout(taskqueue_thread, &sc->harvest_task,
238 	    hz * TPM_HARVEST_INTERVAL);
239 #endif
240 	return (0);
241 }
242 
243 int
244 tpm20_suspend(device_t dev)
245 {
246 #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
247 	struct tpm_sc *sc;
248 
249 	sc = device_get_softc(dev);
250 	taskqueue_drain_timeout(taskqueue_thread, &sc->harvest_task);
251 #endif
252 	return (tpm20_save_state(dev, true));
253 }
254 
255 int
256 tpm20_shutdown(device_t dev)
257 {
258 	return (tpm20_save_state(dev, false));
259 }
260 
261 #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
262 /*
263  * Get TPM_HARVEST_SIZE random bytes and add them
264  * into system entropy pool.
265  */
266 static void
267 tpm20_harvest(void *arg, int unused)
268 {
269 	struct tpm_sc *sc;
270 	struct tpm_priv *priv;
271 	unsigned char entropy[TPM_HARVEST_SIZE];
272 	uint16_t entropy_size;
273 	int result;
274 	uint8_t cmd[] = {
275 		0x80, 0x01,		/* TPM_ST_NO_SESSIONS tag*/
276 		0x00, 0x00, 0x00, 0x0c,	/* cmd length */
277 		0x00, 0x00, 0x01, 0x7b,	/* cmd TPM_CC_GetRandom */
278 		0x00, TPM_HARVEST_SIZE 	/* number of bytes requested */
279 	};
280 
281 	sc = arg;
282 	sx_xlock(&sc->dev_lock);
283 
284 	priv = sc->internal_priv;
285 	memcpy(priv->buf, cmd, sizeof(cmd));
286 
287 	result = TPM_TRANSMIT(sc->dev, priv, sizeof(cmd));
288 	if (result != 0) {
289 		sx_xunlock(&sc->dev_lock);
290 		return;
291 	}
292 
293 	/* The number of random bytes we got is placed right after the header */
294 	entropy_size = (uint16_t) priv->buf[TPM_HEADER_SIZE + 1];
295 	if (entropy_size > 0) {
296 		entropy_size = MIN(entropy_size, TPM_HARVEST_SIZE);
297 		memcpy(entropy,
298 			priv->buf + TPM_HEADER_SIZE + sizeof(uint16_t),
299 			entropy_size);
300 	}
301 
302 	sx_xunlock(&sc->dev_lock);
303 	if (entropy_size > 0)
304 		random_harvest_queue(entropy, entropy_size, RANDOM_PURE_TPM);
305 
306 	taskqueue_enqueue_timeout(taskqueue_thread, &sc->harvest_task,
307 	    hz * TPM_HARVEST_INTERVAL);
308 }
309 #endif	/* TPM_HARVEST */
310 
311 static int
312 tpm20_restart(device_t dev, bool clear)
313 {
314 	struct tpm_sc *sc;
315 	struct tpm_priv *priv;
316 	uint8_t startup_cmd[] = {
317 		0x80, 0x01,             /* TPM_ST_NO_SESSIONS tag*/
318 		0x00, 0x00, 0x00, 0x0C, /* cmd length */
319 		0x00, 0x00, 0x01, 0x44, /* cmd TPM_CC_Startup */
320 		0x00, 0x01              /* TPM_SU_STATE */
321 	};
322 
323 	sc = device_get_softc(dev);
324 
325 	/*
326 	 * Inform the TPM whether we are resetting or resuming.
327 	 */
328 	if (clear)
329 		startup_cmd[11] = 0; /* TPM_SU_CLEAR */
330 
331 	if (sc == NULL)
332 		return (0);
333 
334 	sx_xlock(&sc->dev_lock);
335 
336 	priv = sc->internal_priv;
337 	memcpy(priv->buf, startup_cmd, sizeof(startup_cmd));
338 
339 	/* XXX Ignoring both TPM_TRANSMIT return and tpm's response */
340 	TPM_TRANSMIT(sc->dev, priv, sizeof(startup_cmd));
341 
342 	sx_xunlock(&sc->dev_lock);
343 
344 	return (0);
345 }
346 
347 static int
348 tpm20_save_state(device_t dev, bool suspend)
349 {
350 	struct tpm_sc *sc;
351 	struct tpm_priv *priv;
352 	uint8_t save_cmd[] = {
353 		0x80, 0x01,             /* TPM_ST_NO_SESSIONS tag*/
354 		0x00, 0x00, 0x00, 0x0C, /* cmd length */
355 		0x00, 0x00, 0x01, 0x45, /* cmd TPM_CC_Shutdown */
356 		0x00, 0x00              /* TPM_SU_STATE */
357 	};
358 
359 	sc = device_get_softc(dev);
360 
361 	/*
362 	 * Inform the TPM whether we are going to suspend or reboot/shutdown.
363 	 */
364 	if (suspend)
365 		save_cmd[11] = 1; /* TPM_SU_STATE */
366 
367 	if (sc == NULL)
368 		return (0);
369 
370 	sx_xlock(&sc->dev_lock);
371 
372 	priv = sc->internal_priv;
373 	memcpy(priv->buf, save_cmd, sizeof(save_cmd));
374 
375 	/* XXX Ignoring both TPM_TRANSMIT return and tpm's response */
376 	TPM_TRANSMIT(sc->dev, priv, sizeof(save_cmd));
377 
378 	sx_xunlock(&sc->dev_lock);
379 
380 	return (0);
381 }
382 
383 int32_t
384 tpm20_get_timeout(uint32_t command)
385 {
386 	int32_t timeout;
387 
388 	switch (command) {
389 		case TPM_CC_CreatePrimary:
390 		case TPM_CC_Create:
391 		case TPM_CC_CreateLoaded:
392 			timeout = TPM_TIMEOUT_LONG;
393 			break;
394 		case TPM_CC_SequenceComplete:
395 		case TPM_CC_Startup:
396 		case TPM_CC_SequenceUpdate:
397 		case TPM_CC_GetCapability:
398 		case TPM_CC_PCR_Extend:
399 		case TPM_CC_EventSequenceComplete:
400 		case TPM_CC_HashSequenceStart:
401 			timeout = TPM_TIMEOUT_C;
402 			break;
403 		default:
404 			timeout = TPM_TIMEOUT_B;
405 			break;
406 	}
407 	return timeout;
408 }
409