xref: /freebsd/sys/dev/tpm/tpm20.c (revision 9cdd5c07ad924249d620437d00458c3b6434dbe5)
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 <sys/random.h>
32 
33 #include "tpm20.h"
34 
35 #define TPM_HARVEST_SIZE     16
36 /*
37  * Perform a harvest every 10 seconds.
38  * Since discrete TPMs are painfully slow
39  * we don't want to execute this too often
40  * as the chip is likely to be used by others too.
41  */
42 #define TPM_HARVEST_INTERVAL 10000000
43 
44 MALLOC_DECLARE(M_TPM20);
45 MALLOC_DEFINE(M_TPM20, "tpm_buffer", "buffer for tpm 2.0 driver");
46 
47 static void tpm20_discard_buffer(void *arg);
48 #ifdef TPM_HARVEST
49 static void tpm20_harvest(void *arg);
50 #endif
51 static int  tpm20_save_state(device_t dev, bool suspend);
52 
53 static d_open_t		tpm20_open;
54 static d_close_t	tpm20_close;
55 static d_read_t		tpm20_read;
56 static d_write_t	tpm20_write;
57 static d_ioctl_t	tpm20_ioctl;
58 
59 static struct cdevsw tpm20_cdevsw = {
60 	.d_version = D_VERSION,
61 	.d_open = tpm20_open,
62 	.d_close = tpm20_close,
63 	.d_read = tpm20_read,
64 	.d_write = tpm20_write,
65 	.d_ioctl = tpm20_ioctl,
66 	.d_name = "tpm20",
67 };
68 
69 int
70 tpm20_read(struct cdev *dev, struct uio *uio, int flags)
71 {
72 	struct tpm_sc *sc;
73 	size_t bytes_to_transfer;
74 	int result = 0;
75 
76 	sc = (struct tpm_sc *)dev->si_drv1;
77 
78 	callout_stop(&sc->discard_buffer_callout);
79 	sx_xlock(&sc->dev_lock);
80 
81 	bytes_to_transfer = MIN(sc->pending_data_length, uio->uio_resid);
82 	if (bytes_to_transfer > 0) {
83 		result = uiomove((caddr_t) sc->buf, bytes_to_transfer, uio);
84 		memset(sc->buf, 0, TPM_BUFSIZE);
85 		sc->pending_data_length = 0;
86 		cv_signal(&sc->buf_cv);
87 	} else {
88 		result = ETIMEDOUT;
89 	}
90 
91 	sx_xunlock(&sc->dev_lock);
92 
93 	return (result);
94 }
95 
96 int
97 tpm20_write(struct cdev *dev, struct uio *uio, int flags)
98 {
99 	struct tpm_sc *sc;
100 	size_t byte_count;
101 	int result = 0;
102 
103 	sc = (struct tpm_sc *)dev->si_drv1;
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 	while (sc->pending_data_length != 0)
121 		cv_wait(&sc->buf_cv, &sc->dev_lock);
122 
123 	result = uiomove(sc->buf, byte_count, uio);
124 	if (result != 0) {
125 		sx_xunlock(&sc->dev_lock);
126 		return (result);
127 	}
128 
129 	result = sc->transmit(sc, byte_count);
130 
131 	if (result == 0)
132 		callout_reset(&sc->discard_buffer_callout,
133 		    TPM_READ_TIMEOUT / tick, tpm20_discard_buffer, sc);
134 
135 	sx_xunlock(&sc->dev_lock);
136 	return (result);
137 }
138 
139 static void tpm20_discard_buffer(void *arg)
140 {
141 	struct tpm_sc *sc;
142 
143 	sc = (struct tpm_sc *)arg;
144 	if (callout_pending(&sc->discard_buffer_callout))
145 		return;
146 
147 	sx_xlock(&sc->dev_lock);
148 
149 	memset(sc->buf, 0, TPM_BUFSIZE);
150 	sc->pending_data_length = 0;
151 
152 	cv_signal(&sc->buf_cv);
153 	sx_xunlock(&sc->dev_lock);
154 
155 	device_printf(sc->dev,
156 	    "User failed to read buffer in time\n");
157 }
158 
159 int
160 tpm20_open(struct cdev *dev, int flag, int mode, struct thread *td)
161 {
162 
163 	return (0);
164 }
165 
166 int
167 tpm20_close(struct cdev *dev, int flag, int mode, struct thread *td)
168 {
169 
170 	return (0);
171 }
172 
173 
174 int
175 tpm20_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
176     int flags, struct thread *td)
177 {
178 
179 	return (ENOTTY);
180 }
181 
182 int
183 tpm20_init(struct tpm_sc *sc)
184 {
185 	struct make_dev_args args;
186 	int result;
187 
188 	sc->buf = malloc(TPM_BUFSIZE, M_TPM20, M_WAITOK);
189 	sx_init(&sc->dev_lock, "TPM driver lock");
190 	cv_init(&sc->buf_cv, "TPM buffer cv");
191 	callout_init(&sc->discard_buffer_callout, 1);
192 #ifdef TPM_HARVEST
193 	sc->harvest_ticks = TPM_HARVEST_INTERVAL / tick;
194 	callout_init(&sc->harvest_callout, 1);
195 	callout_reset(&sc->harvest_callout, 0, tpm20_harvest, sc);
196 #endif
197 	sc->pending_data_length = 0;
198 
199 	make_dev_args_init(&args);
200 	args.mda_devsw = &tpm20_cdevsw;
201 	args.mda_uid = UID_ROOT;
202 	args.mda_gid = GID_WHEEL;
203 	args.mda_mode = TPM_CDEV_PERM_FLAG;
204 	args.mda_si_drv1 = sc;
205 	result = make_dev_s(&args, &sc->sc_cdev, TPM_CDEV_NAME);
206 	if (result != 0)
207 		tpm20_release(sc);
208 
209 	return (result);
210 
211 }
212 
213 void
214 tpm20_release(struct tpm_sc *sc)
215 {
216 
217 #ifdef TPM_HARVEST
218 	callout_drain(&sc->harvest_callout);
219 #endif
220 
221 	if (sc->buf != NULL)
222 		free(sc->buf, M_TPM20);
223 
224 	sx_destroy(&sc->dev_lock);
225 	cv_destroy(&sc->buf_cv);
226 	if (sc->sc_cdev != NULL)
227 		destroy_dev(sc->sc_cdev);
228 }
229 
230 
231 int
232 tpm20_suspend(device_t dev)
233 {
234 	return (tpm20_save_state(dev, true));
235 }
236 
237 int
238 tpm20_shutdown(device_t dev)
239 {
240 	return (tpm20_save_state(dev, false));
241 }
242 
243 #ifdef TPM_HARVEST
244 
245 /*
246  * Get TPM_HARVEST_SIZE random bytes and add them
247  * into system entropy pool.
248  */
249 static void
250 tpm20_harvest(void *arg)
251 {
252 	struct tpm_sc *sc;
253 	unsigned char entropy[TPM_HARVEST_SIZE];
254 	uint16_t entropy_size;
255 	int result;
256 	uint8_t cmd[] = {
257 		0x80, 0x01,		/* TPM_ST_NO_SESSIONS tag*/
258 		0x00, 0x00, 0x00, 0x0c,	/* cmd length */
259 		0x00, 0x00, 0x01, 0x7b,	/* cmd TPM_CC_GetRandom */
260 		0x00, TPM_HARVEST_SIZE 	/* number of bytes requested */
261 	};
262 
263 
264 	sc = arg;
265 	sx_xlock(&sc->dev_lock);
266 	while (sc->pending_data_length != 0)
267 		cv_wait(&sc->buf_cv, &sc->dev_lock);
268 
269 	memcpy(sc->buf, cmd, sizeof(cmd));
270 	result = sc->transmit(sc, sizeof(cmd));
271 	if (result != 0) {
272 		sx_xunlock(&sc->dev_lock);
273 		return;
274 	}
275 
276 	/* Ignore response size */
277 	sc->pending_data_length = 0;
278 
279 	/* The number of random bytes we got is placed right after the header */
280 	entropy_size = (uint16_t) sc->buf[TPM_HEADER_SIZE + 1];
281 	if (entropy_size > 0) {
282 		entropy_size = MIN(entropy_size, TPM_HARVEST_SIZE);
283 		memcpy(entropy,
284 			sc->buf + TPM_HEADER_SIZE + sizeof(uint16_t),
285 			entropy_size);
286 	}
287 
288 	sx_xunlock(&sc->dev_lock);
289 	if (entropy_size > 0)
290 		random_harvest_queue(entropy, entropy_size, RANDOM_PURE_TPM);
291 
292 	callout_reset(&sc->harvest_callout, sc->harvest_ticks, tpm20_harvest, sc);
293 }
294 #endif	/* TPM_HARVEST */
295 
296 static int
297 tpm20_save_state(device_t dev, bool suspend)
298 {
299 	struct tpm_sc *sc;
300 	uint8_t save_cmd[] = {
301 		0x80, 0x01, /* TPM_ST_NO_SESSIONS tag*/
302 		0x00, 0x00, 0x00, 0x0C, /* cmd length */
303 		0x00, 0x00, 0x01, 0x45, 0x00, 0x00 /* cmd TPM_CC_Shutdown */
304 	};
305 
306 	sc = device_get_softc(dev);
307 
308 	/*
309 	 * Inform the TPM whether we are going to suspend or reboot/shutdown.
310 	 */
311 	if (suspend)
312 		save_cmd[11] = 1;	/* TPM_SU_STATE */
313 
314 	if (sc == NULL || sc->buf == NULL)
315 		return (0);
316 
317 	sx_xlock(&sc->dev_lock);
318 
319 	memcpy(sc->buf, save_cmd, sizeof(save_cmd));
320 	sc->transmit(sc, sizeof(save_cmd));
321 
322 	sx_xunlock(&sc->dev_lock);
323 
324 	return (0);
325 }
326 
327 int32_t
328 tpm20_get_timeout(uint32_t command)
329 {
330 	int32_t timeout;
331 
332 	switch (command) {
333 		case TPM_CC_CreatePrimary:
334 		case TPM_CC_Create:
335 		case TPM_CC_CreateLoaded:
336 			timeout = TPM_TIMEOUT_LONG;
337 			break;
338 		case TPM_CC_SequenceComplete:
339 		case TPM_CC_Startup:
340 		case TPM_CC_SequenceUpdate:
341 		case TPM_CC_GetCapability:
342 		case TPM_CC_PCR_Extend:
343 		case TPM_CC_EventSequenceComplete:
344 		case TPM_CC_HashSequenceStart:
345 			timeout = TPM_TIMEOUT_C;
346 			break;
347 		default:
348 			timeout = TPM_TIMEOUT_B;
349 			break;
350 	}
351 	return timeout;
352 }
353