Merge tag 'armsoc-dt' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[linux-2.6-block.git] / drivers / char / tpm / tpm2-space.c
1 /*
2  * Copyright (C) 2016 Intel Corporation
3  *
4  * Authors:
5  * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
6  *
7  * Maintained by: <tpmdd-devel@lists.sourceforge.net>
8  *
9  * This file contains TPM2 protocol implementations of the commands
10  * used by the kernel internally.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; version 2
15  * of the License.
16  */
17
18 #include <linux/gfp.h>
19 #include <asm/unaligned.h>
20 #include "tpm.h"
21
22 enum tpm2_handle_types {
23         TPM2_HT_HMAC_SESSION    = 0x02000000,
24         TPM2_HT_POLICY_SESSION  = 0x03000000,
25         TPM2_HT_TRANSIENT       = 0x80000000,
26 };
27
28 struct tpm2_context {
29         __be64 sequence;
30         __be32 saved_handle;
31         __be32 hierarchy;
32         __be16 blob_size;
33 } __packed;
34
35 static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)
36 {
37         int i;
38
39         for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
40                 if (space->session_tbl[i])
41                         tpm2_flush_context_cmd(chip, space->session_tbl[i],
42                                                TPM_TRANSMIT_NESTED);
43         }
44 }
45
46 int tpm2_init_space(struct tpm_space *space)
47 {
48         space->context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
49         if (!space->context_buf)
50                 return -ENOMEM;
51
52         space->session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
53         if (space->session_buf == NULL) {
54                 kfree(space->context_buf);
55                 return -ENOMEM;
56         }
57
58         return 0;
59 }
60
61 void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
62 {
63         mutex_lock(&chip->tpm_mutex);
64         tpm2_flush_sessions(chip, space);
65         mutex_unlock(&chip->tpm_mutex);
66         kfree(space->context_buf);
67         kfree(space->session_buf);
68 }
69
70 static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
71                              unsigned int *offset, u32 *handle)
72 {
73         struct tpm_buf tbuf;
74         struct tpm2_context *ctx;
75         unsigned int body_size;
76         int rc;
77
78         rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
79         if (rc)
80                 return rc;
81
82         ctx = (struct tpm2_context *)&buf[*offset];
83         body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
84         tpm_buf_append(&tbuf, &buf[*offset], body_size);
85
86         rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4,
87                               TPM_TRANSMIT_NESTED, NULL);
88         if (rc < 0) {
89                 dev_warn(&chip->dev, "%s: failed with a system error %d\n",
90                          __func__, rc);
91                 tpm_buf_destroy(&tbuf);
92                 return -EFAULT;
93         } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
94                    rc == TPM2_RC_REFERENCE_H0) {
95                 /*
96                  * TPM_RC_HANDLE means that the session context can't
97                  * be loaded because of an internal counter mismatch
98                  * that makes the TPM think there might have been a
99                  * replay.  This might happen if the context was saved
100                  * and loaded outside the space.
101                  *
102                  * TPM_RC_REFERENCE_H0 means the session has been
103                  * flushed outside the space
104                  */
105                 *handle = 0;
106                 tpm_buf_destroy(&tbuf);
107                 return -ENOENT;
108         } else if (rc > 0) {
109                 dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
110                          __func__, rc);
111                 tpm_buf_destroy(&tbuf);
112                 return -EFAULT;
113         }
114
115         *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
116         *offset += body_size;
117
118         tpm_buf_destroy(&tbuf);
119         return 0;
120 }
121
122 static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
123                              unsigned int buf_size, unsigned int *offset)
124 {
125         struct tpm_buf tbuf;
126         unsigned int body_size;
127         int rc;
128
129         rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
130         if (rc)
131                 return rc;
132
133         tpm_buf_append_u32(&tbuf, handle);
134
135         rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0,
136                               TPM_TRANSMIT_NESTED, NULL);
137         if (rc < 0) {
138                 dev_warn(&chip->dev, "%s: failed with a system error %d\n",
139                          __func__, rc);
140                 tpm_buf_destroy(&tbuf);
141                 return -EFAULT;
142         } else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
143                 tpm_buf_destroy(&tbuf);
144                 return -ENOENT;
145         } else if (rc) {
146                 dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
147                          __func__, rc);
148                 tpm_buf_destroy(&tbuf);
149                 return -EFAULT;
150         }
151
152         body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
153         if ((*offset + body_size) > buf_size) {
154                 dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
155                 tpm_buf_destroy(&tbuf);
156                 return -ENOMEM;
157         }
158
159         memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
160         *offset += body_size;
161         tpm_buf_destroy(&tbuf);
162         return 0;
163 }
164
165 static void tpm2_flush_space(struct tpm_chip *chip)
166 {
167         struct tpm_space *space = &chip->work_space;
168         int i;
169
170         for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
171                 if (space->context_tbl[i] && ~space->context_tbl[i])
172                         tpm2_flush_context_cmd(chip, space->context_tbl[i],
173                                                TPM_TRANSMIT_NESTED);
174
175         tpm2_flush_sessions(chip, space);
176 }
177
178 static int tpm2_load_space(struct tpm_chip *chip)
179 {
180         struct tpm_space *space = &chip->work_space;
181         unsigned int offset;
182         int i;
183         int rc;
184
185         for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
186                 if (!space->context_tbl[i])
187                         continue;
188
189                 /* sanity check, should never happen */
190                 if (~space->context_tbl[i]) {
191                         dev_err(&chip->dev, "context table is inconsistent");
192                         return -EFAULT;
193                 }
194
195                 rc = tpm2_load_context(chip, space->context_buf, &offset,
196                                        &space->context_tbl[i]);
197                 if (rc)
198                         return rc;
199         }
200
201         for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
202                 u32 handle;
203
204                 if (!space->session_tbl[i])
205                         continue;
206
207                 rc = tpm2_load_context(chip, space->session_buf,
208                                        &offset, &handle);
209                 if (rc == -ENOENT) {
210                         /* load failed, just forget session */
211                         space->session_tbl[i] = 0;
212                 } else if (rc) {
213                         tpm2_flush_space(chip);
214                         return rc;
215                 }
216                 if (handle != space->session_tbl[i]) {
217                         dev_warn(&chip->dev, "session restored to wrong handle\n");
218                         tpm2_flush_space(chip);
219                         return -EFAULT;
220                 }
221         }
222
223         return 0;
224 }
225
226 static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)
227 {
228         u32 vhandle = be32_to_cpup((__be32 *)handle);
229         u32 phandle;
230         int i;
231
232         i = 0xFFFFFF - (vhandle & 0xFFFFFF);
233         if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])
234                 return false;
235
236         phandle = space->context_tbl[i];
237         *((__be32 *)handle) = cpu_to_be32(phandle);
238         return true;
239 }
240
241 static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
242 {
243         struct tpm_space *space = &chip->work_space;
244         unsigned int nr_handles;
245         u32 attrs;
246         __be32 *handle;
247         int i;
248
249         i = tpm2_find_cc(chip, cc);
250         if (i < 0)
251                 return -EINVAL;
252
253         attrs = chip->cc_attrs_tbl[i];
254         nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
255
256         handle = (__be32 *)&cmd[TPM_HEADER_SIZE];
257         for (i = 0; i < nr_handles; i++, handle++) {
258                 if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {
259                         if (!tpm2_map_to_phandle(space, handle))
260                                 return -EINVAL;
261                 }
262         }
263
264         return 0;
265 }
266
267 int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
268                        u8 *cmd)
269 {
270         int rc;
271
272         if (!space)
273                 return 0;
274
275         memcpy(&chip->work_space.context_tbl, &space->context_tbl,
276                sizeof(space->context_tbl));
277         memcpy(&chip->work_space.session_tbl, &space->session_tbl,
278                sizeof(space->session_tbl));
279         memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE);
280         memcpy(chip->work_space.session_buf, space->session_buf, PAGE_SIZE);
281
282         rc = tpm2_load_space(chip);
283         if (rc) {
284                 tpm2_flush_space(chip);
285                 return rc;
286         }
287
288         rc = tpm2_map_command(chip, cc, cmd);
289         if (rc) {
290                 tpm2_flush_space(chip);
291                 return rc;
292         }
293
294         return 0;
295 }
296
297 static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)
298 {
299         struct tpm_space *space = &chip->work_space;
300         int i;
301
302         for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)
303                 if (space->session_tbl[i] == 0)
304                         break;
305
306         if (i == ARRAY_SIZE(space->session_tbl))
307                 return false;
308
309         space->session_tbl[i] = handle;
310         return true;
311 }
312
313 static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)
314 {
315         int i;
316
317         for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
318                 if (alloc) {
319                         if (!space->context_tbl[i]) {
320                                 space->context_tbl[i] = phandle;
321                                 break;
322                         }
323                 } else if (space->context_tbl[i] == phandle)
324                         break;
325         }
326
327         if (i == ARRAY_SIZE(space->context_tbl))
328                 return 0;
329
330         return TPM2_HT_TRANSIENT | (0xFFFFFF - i);
331 }
332
333 static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
334                                     size_t len)
335 {
336         struct tpm_space *space = &chip->work_space;
337         struct tpm_output_header *header = (void *)rsp;
338         u32 phandle;
339         u32 phandle_type;
340         u32 vhandle;
341         u32 attrs;
342         int i;
343
344         if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)
345                 return 0;
346
347         i = tpm2_find_cc(chip, cc);
348         /* sanity check, should never happen */
349         if (i < 0)
350                 return -EFAULT;
351
352         attrs = chip->cc_attrs_tbl[i];
353         if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))
354                 return 0;
355
356         phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);
357         phandle_type = phandle & 0xFF000000;
358
359         switch (phandle_type) {
360         case TPM2_HT_TRANSIENT:
361                 vhandle = tpm2_map_to_vhandle(space, phandle, true);
362                 if (!vhandle)
363                         goto out_no_slots;
364
365                 *(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);
366                 break;
367         case TPM2_HT_HMAC_SESSION:
368         case TPM2_HT_POLICY_SESSION:
369                 if (!tpm2_add_session(chip, phandle))
370                         goto out_no_slots;
371                 break;
372         default:
373                 dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
374                         __func__, phandle);
375                 break;
376         };
377
378         return 0;
379 out_no_slots:
380         tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_NESTED);
381         dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
382                  phandle);
383         return -ENOMEM;
384 }
385
386 struct tpm2_cap_handles {
387         u8 more_data;
388         __be32 capability;
389         __be32 count;
390         __be32 handles[];
391 } __packed;
392
393 static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
394                                   size_t len)
395 {
396         struct tpm_space *space = &chip->work_space;
397         struct tpm_output_header *header = (void *)rsp;
398         struct tpm2_cap_handles *data;
399         u32 phandle;
400         u32 phandle_type;
401         u32 vhandle;
402         int i;
403         int j;
404
405         if (cc != TPM2_CC_GET_CAPABILITY ||
406             be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {
407                 return 0;
408         }
409
410         if (len < TPM_HEADER_SIZE + 9)
411                 return -EFAULT;
412
413         data = (void *)&rsp[TPM_HEADER_SIZE];
414         if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)
415                 return 0;
416
417         if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))
418                 return -EFAULT;
419
420         for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {
421                 phandle = be32_to_cpup((__be32 *)&data->handles[i]);
422                 phandle_type = phandle & 0xFF000000;
423
424                 switch (phandle_type) {
425                 case TPM2_HT_TRANSIENT:
426                         vhandle = tpm2_map_to_vhandle(space, phandle, false);
427                         if (!vhandle)
428                                 break;
429
430                         data->handles[j] = cpu_to_be32(vhandle);
431                         j++;
432                         break;
433
434                 default:
435                         data->handles[j] = cpu_to_be32(phandle);
436                         j++;
437                         break;
438                 }
439
440         }
441
442         header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);
443         data->count = cpu_to_be32(j);
444         return 0;
445 }
446
447 static int tpm2_save_space(struct tpm_chip *chip)
448 {
449         struct tpm_space *space = &chip->work_space;
450         unsigned int offset;
451         int i;
452         int rc;
453
454         for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
455                 if (!(space->context_tbl[i] && ~space->context_tbl[i]))
456                         continue;
457
458                 rc = tpm2_save_context(chip, space->context_tbl[i],
459                                        space->context_buf, PAGE_SIZE,
460                                        &offset);
461                 if (rc == -ENOENT) {
462                         space->context_tbl[i] = 0;
463                         continue;
464                 } else if (rc)
465                         return rc;
466
467                 tpm2_flush_context_cmd(chip, space->context_tbl[i],
468                                        TPM_TRANSMIT_NESTED);
469                 space->context_tbl[i] = ~0;
470         }
471
472         for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
473                 if (!space->session_tbl[i])
474                         continue;
475
476                 rc = tpm2_save_context(chip, space->session_tbl[i],
477                                        space->session_buf, PAGE_SIZE,
478                                        &offset);
479
480                 if (rc == -ENOENT) {
481                         /* handle error saving session, just forget it */
482                         space->session_tbl[i] = 0;
483                 } else if (rc < 0) {
484                         tpm2_flush_space(chip);
485                         return rc;
486                 }
487         }
488
489         return 0;
490 }
491
492 int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
493                       u32 cc, u8 *buf, size_t *bufsiz)
494 {
495         struct tpm_output_header *header = (void *)buf;
496         int rc;
497
498         if (!space)
499                 return 0;
500
501         rc = tpm2_map_response_header(chip, cc, buf, *bufsiz);
502         if (rc) {
503                 tpm2_flush_space(chip);
504                 return rc;
505         }
506
507         rc = tpm2_map_response_body(chip, cc, buf, *bufsiz);
508         if (rc) {
509                 tpm2_flush_space(chip);
510                 return rc;
511         }
512
513         rc = tpm2_save_space(chip);
514         if (rc) {
515                 tpm2_flush_space(chip);
516                 return rc;
517         }
518
519         *bufsiz = be32_to_cpu(header->length);
520
521         memcpy(&space->context_tbl, &chip->work_space.context_tbl,
522                sizeof(space->context_tbl));
523         memcpy(&space->session_tbl, &chip->work_space.session_tbl,
524                sizeof(space->session_tbl));
525         memcpy(space->context_buf, chip->work_space.context_buf, PAGE_SIZE);
526         memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE);
527
528         return 0;
529 }