]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/tpm/tpm20.c
Merge lld trunk r366426, and resolve conflicts.
[FreeBSD/FreeBSD.git] / sys / dev / tpm / tpm20.c
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         if (sc->owner_tid != uio->uio_td->td_tid) {
81                 sx_xunlock(&sc->dev_lock);
82                 return (EPERM);
83         }
84
85         bytes_to_transfer = MIN(sc->pending_data_length, uio->uio_resid);
86         if (bytes_to_transfer > 0) {
87                 result = uiomove((caddr_t) sc->buf, bytes_to_transfer, uio);
88                 memset(sc->buf, 0, TPM_BUFSIZE);
89                 sc->pending_data_length = 0;
90                 cv_signal(&sc->buf_cv);
91         } else {
92                 result = ETIMEDOUT;
93         }
94
95         sx_xunlock(&sc->dev_lock);
96
97         return (result);
98 }
99
100 int
101 tpm20_write(struct cdev *dev, struct uio *uio, int flags)
102 {
103         struct tpm_sc *sc;
104         size_t byte_count;
105         int result = 0;
106
107         sc = (struct tpm_sc *)dev->si_drv1;
108
109         byte_count = uio->uio_resid;
110         if (byte_count < TPM_HEADER_SIZE) {
111                 device_printf(sc->dev,
112                     "Requested transfer is too small\n");
113                 return (EINVAL);
114         }
115
116         if (byte_count > TPM_BUFSIZE) {
117                 device_printf(sc->dev,
118                     "Requested transfer is too large\n");
119                 return (E2BIG);
120         }
121
122         sx_xlock(&sc->dev_lock);
123
124         while (sc->pending_data_length != 0)
125                 cv_wait(&sc->buf_cv, &sc->dev_lock);
126
127         result = uiomove(sc->buf, byte_count, uio);
128         if (result != 0) {
129                 sx_xunlock(&sc->dev_lock);
130                 return (result);
131         }
132
133         result = sc->transmit(sc, byte_count);
134
135         if (result == 0) {
136                 callout_reset(&sc->discard_buffer_callout,
137                     TPM_READ_TIMEOUT / tick, tpm20_discard_buffer, sc);
138                 sc->owner_tid = uio->uio_td->td_tid;
139         }
140
141         sx_xunlock(&sc->dev_lock);
142         return (result);
143 }
144
145 static void
146 tpm20_discard_buffer(void *arg)
147 {
148         struct tpm_sc *sc;
149
150         sc = (struct tpm_sc *)arg;
151         if (callout_pending(&sc->discard_buffer_callout))
152                 return;
153
154         sx_xlock(&sc->dev_lock);
155
156         memset(sc->buf, 0, TPM_BUFSIZE);
157         sc->pending_data_length = 0;
158
159         cv_signal(&sc->buf_cv);
160         sx_xunlock(&sc->dev_lock);
161
162         device_printf(sc->dev,
163             "User failed to read buffer in time\n");
164 }
165
166 int
167 tpm20_open(struct cdev *dev, int flag, int mode, struct thread *td)
168 {
169
170         return (0);
171 }
172
173 int
174 tpm20_close(struct cdev *dev, int flag, int mode, struct thread *td)
175 {
176
177         return (0);
178 }
179
180
181 int
182 tpm20_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
183     int flags, struct thread *td)
184 {
185
186         return (ENOTTY);
187 }
188
189 int
190 tpm20_init(struct tpm_sc *sc)
191 {
192         struct make_dev_args args;
193         int result;
194
195         sc->buf = malloc(TPM_BUFSIZE, M_TPM20, M_WAITOK);
196         sx_init(&sc->dev_lock, "TPM driver lock");
197         cv_init(&sc->buf_cv, "TPM buffer cv");
198         callout_init(&sc->discard_buffer_callout, 1);
199 #ifdef TPM_HARVEST
200         sc->harvest_ticks = TPM_HARVEST_INTERVAL / tick;
201         callout_init(&sc->harvest_callout, 1);
202         callout_reset(&sc->harvest_callout, 0, tpm20_harvest, sc);
203 #endif
204         sc->pending_data_length = 0;
205
206         make_dev_args_init(&args);
207         args.mda_devsw = &tpm20_cdevsw;
208         args.mda_uid = UID_ROOT;
209         args.mda_gid = GID_WHEEL;
210         args.mda_mode = TPM_CDEV_PERM_FLAG;
211         args.mda_si_drv1 = sc;
212         result = make_dev_s(&args, &sc->sc_cdev, TPM_CDEV_NAME);
213         if (result != 0)
214                 tpm20_release(sc);
215
216         return (result);
217
218 }
219
220 void
221 tpm20_release(struct tpm_sc *sc)
222 {
223
224 #ifdef TPM_HARVEST
225         callout_drain(&sc->harvest_callout);
226 #endif
227
228         if (sc->buf != NULL)
229                 free(sc->buf, M_TPM20);
230
231         sx_destroy(&sc->dev_lock);
232         cv_destroy(&sc->buf_cv);
233         if (sc->sc_cdev != NULL)
234                 destroy_dev(sc->sc_cdev);
235 }
236
237
238 int
239 tpm20_suspend(device_t dev)
240 {
241         return (tpm20_save_state(dev, true));
242 }
243
244 int
245 tpm20_shutdown(device_t dev)
246 {
247         return (tpm20_save_state(dev, false));
248 }
249
250 #ifdef TPM_HARVEST
251
252 /*
253  * Get TPM_HARVEST_SIZE random bytes and add them
254  * into system entropy pool.
255  */
256 static void
257 tpm20_harvest(void *arg)
258 {
259         struct tpm_sc *sc;
260         unsigned char entropy[TPM_HARVEST_SIZE];
261         uint16_t entropy_size;
262         int result;
263         uint8_t cmd[] = {
264                 0x80, 0x01,             /* TPM_ST_NO_SESSIONS tag*/
265                 0x00, 0x00, 0x00, 0x0c, /* cmd length */
266                 0x00, 0x00, 0x01, 0x7b, /* cmd TPM_CC_GetRandom */
267                 0x00, TPM_HARVEST_SIZE  /* number of bytes requested */
268         };
269
270
271         sc = arg;
272         sx_xlock(&sc->dev_lock);
273         while (sc->pending_data_length != 0)
274                 cv_wait(&sc->buf_cv, &sc->dev_lock);
275
276         memcpy(sc->buf, cmd, sizeof(cmd));
277         result = sc->transmit(sc, sizeof(cmd));
278         if (result != 0) {
279                 sx_xunlock(&sc->dev_lock);
280                 return;
281         }
282
283         /* Ignore response size */
284         sc->pending_data_length = 0;
285
286         /* The number of random bytes we got is placed right after the header */
287         entropy_size = (uint16_t) sc->buf[TPM_HEADER_SIZE + 1];
288         if (entropy_size > 0) {
289                 entropy_size = MIN(entropy_size, TPM_HARVEST_SIZE);
290                 memcpy(entropy,
291                         sc->buf + TPM_HEADER_SIZE + sizeof(uint16_t),
292                         entropy_size);
293         }
294
295         sx_xunlock(&sc->dev_lock);
296         if (entropy_size > 0)
297                 random_harvest_queue(entropy, entropy_size, RANDOM_PURE_TPM);
298
299         callout_reset(&sc->harvest_callout, sc->harvest_ticks, tpm20_harvest, sc);
300 }
301 #endif  /* TPM_HARVEST */
302
303 static int
304 tpm20_save_state(device_t dev, bool suspend)
305 {
306         struct tpm_sc *sc;
307         uint8_t save_cmd[] = {
308                 0x80, 0x01,             /* TPM_ST_NO_SESSIONS tag*/
309                 0x00, 0x00, 0x00, 0x0C, /* cmd length */
310                 0x00, 0x00, 0x01, 0x45, /* cmd TPM_CC_Shutdown */
311                 0x00, 0x00              /* TPM_SU_STATE */
312         };
313
314         sc = device_get_softc(dev);
315
316         /*
317          * Inform the TPM whether we are going to suspend or reboot/shutdown.
318          */
319         if (suspend)
320                 save_cmd[11] = 1; /* TPM_SU_STATE */
321
322         if (sc == NULL || sc->buf == NULL)
323                 return (0);
324
325         sx_xlock(&sc->dev_lock);
326
327         memcpy(sc->buf, save_cmd, sizeof(save_cmd));
328         sc->transmit(sc, sizeof(save_cmd));
329
330         sx_xunlock(&sc->dev_lock);
331
332         return (0);
333 }
334
335 int32_t
336 tpm20_get_timeout(uint32_t command)
337 {
338         int32_t timeout;
339
340         switch (command) {
341                 case TPM_CC_CreatePrimary:
342                 case TPM_CC_Create:
343                 case TPM_CC_CreateLoaded:
344                         timeout = TPM_TIMEOUT_LONG;
345                         break;
346                 case TPM_CC_SequenceComplete:
347                 case TPM_CC_Startup:
348                 case TPM_CC_SequenceUpdate:
349                 case TPM_CC_GetCapability:
350                 case TPM_CC_PCR_Extend:
351                 case TPM_CC_EventSequenceComplete:
352                 case TPM_CC_HashSequenceStart:
353                         timeout = TPM_TIMEOUT_C;
354                         break;
355                 default:
356                         timeout = TPM_TIMEOUT_B;
357                         break;
358         }
359         return timeout;
360 }