sf: winbond: Add support for W25Q80BW
[platform/kernel/u-boot.git] / lib / tpm.c
1 /*
2  * Copyright (c) 2013 The Chromium OS Authors.
3  *
4  * See file CREDITS for list of people who contributed to this
5  * project.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307 USA
21  */
22
23 #include <common.h>
24 #include <stdarg.h>
25 #include <tpm.h>
26 #include <asm/unaligned.h>
27
28 /* Internal error of TPM command library */
29 #define TPM_LIB_ERROR   ((uint32_t)~0u)
30
31 /* Useful constants */
32 enum {
33         COMMAND_BUFFER_SIZE             = 256,
34         TPM_PUBEK_SIZE                  = 256,
35         TPM_REQUEST_HEADER_LENGTH       = 10,
36         TPM_RESPONSE_HEADER_LENGTH      = 10,
37         PCR_DIGEST_LENGTH               = 20,
38 };
39
40 /**
41  * Pack data into a byte string.  The data types are specified in
42  * the format string: 'b' means unsigned byte, 'w' unsigned word,
43  * 'd' unsigned double word, and 's' byte string.  The data are a
44  * series of offsets and values (for type byte string there are also
45  * lengths).  The data values are packed into the byte string
46  * sequentially, and so a latter value could over-write a former
47  * value.
48  *
49  * @param str           output string
50  * @param size          size of output string
51  * @param format        format string
52  * @param ...           data points
53  * @return 0 on success, non-0 on error
54  */
55 int pack_byte_string(uint8_t *str, size_t size, const char *format, ...)
56 {
57         va_list args;
58         size_t offset = 0, length = 0;
59         uint8_t *data = NULL;
60         uint32_t value = 0;
61
62         va_start(args, format);
63         for (; *format; format++) {
64                 switch (*format) {
65                 case 'b':
66                         offset = va_arg(args, size_t);
67                         value = va_arg(args, int);
68                         length = 1;
69                         break;
70                 case 'w':
71                         offset = va_arg(args, size_t);
72                         value = va_arg(args, int);
73                         length = 2;
74                         break;
75                 case 'd':
76                         offset = va_arg(args, size_t);
77                         value = va_arg(args, uint32_t);
78                         length = 4;
79                         break;
80                 case 's':
81                         offset = va_arg(args, size_t);
82                         data = va_arg(args, uint8_t *);
83                         length = va_arg(args, uint32_t);
84                         break;
85                 default:
86                         debug("Couldn't recognize format string\n");
87                         return -1;
88                 }
89
90                 if (offset + length > size)
91                         return -1;
92
93                 switch (*format) {
94                 case 'b':
95                         str[offset] = value;
96                         break;
97                 case 'w':
98                         put_unaligned_be16(value, str + offset);
99                         break;
100                 case 'd':
101                         put_unaligned_be32(value, str + offset);
102                         break;
103                 case 's':
104                         memcpy(str + offset, data, length);
105                         break;
106                 }
107         }
108         va_end(args);
109
110         return 0;
111 }
112
113 /**
114  * Unpack data from a byte string.  The data types are specified in
115  * the format string: 'b' means unsigned byte, 'w' unsigned word,
116  * 'd' unsigned double word, and 's' byte string.  The data are a
117  * series of offsets and pointers (for type byte string there are also
118  * lengths).
119  *
120  * @param str           output string
121  * @param size          size of output string
122  * @param format        format string
123  * @param ...           data points
124  * @return 0 on success, non-0 on error
125  */
126 int unpack_byte_string(const uint8_t *str, size_t size, const char *format, ...)
127 {
128         va_list args;
129         size_t offset = 0, length = 0;
130         uint8_t *ptr8 = NULL;
131         uint16_t *ptr16 = NULL;
132         uint32_t *ptr32 = NULL;
133
134         va_start(args, format);
135         for (; *format; format++) {
136                 switch (*format) {
137                 case 'b':
138                         offset = va_arg(args, size_t);
139                         ptr8 = va_arg(args, uint8_t *);
140                         length = 1;
141                         break;
142                 case 'w':
143                         offset = va_arg(args, size_t);
144                         ptr16 = va_arg(args, uint16_t *);
145                         length = 2;
146                         break;
147                 case 'd':
148                         offset = va_arg(args, size_t);
149                         ptr32 = va_arg(args, uint32_t *);
150                         length = 4;
151                         break;
152                 case 's':
153                         offset = va_arg(args, size_t);
154                         ptr8 = va_arg(args, uint8_t *);
155                         length = va_arg(args, uint32_t);
156                         break;
157                 default:
158                         debug("Couldn't recognize format string\n");
159                         return -1;
160                 }
161
162                 if (offset + length > size)
163                         return -1;
164
165                 switch (*format) {
166                 case 'b':
167                         *ptr8 = str[offset];
168                         break;
169                 case 'w':
170                         *ptr16 = get_unaligned_be16(str + offset);
171                         break;
172                 case 'd':
173                         *ptr32 = get_unaligned_be32(str + offset);
174                         break;
175                 case 's':
176                         memcpy(ptr8, str + offset, length);
177                         break;
178                 }
179         }
180         va_end(args);
181
182         return 0;
183 }
184
185 /**
186  * Get TPM command size.
187  *
188  * @param command       byte string of TPM command
189  * @return command size of the TPM command
190  */
191 static uint32_t tpm_command_size(const void *command)
192 {
193         const size_t command_size_offset = 2;
194         return get_unaligned_be32(command + command_size_offset);
195 }
196
197 /**
198  * Get TPM response return code, which is one of TPM_RESULT values.
199  *
200  * @param response      byte string of TPM response
201  * @return return code of the TPM response
202  */
203 static uint32_t tpm_return_code(const void *response)
204 {
205         const size_t return_code_offset = 6;
206         return get_unaligned_be32(response + return_code_offset);
207 }
208
209 /**
210  * Send a TPM command and return response's return code, and optionally
211  * return response to caller.
212  *
213  * @param command       byte string of TPM command
214  * @param response      output buffer for TPM response, or NULL if the
215  *                      caller does not care about it
216  * @param size_ptr      output buffer size (input parameter) and TPM
217  *                      response length (output parameter); this parameter
218  *                      is a bidirectional
219  * @return return code of the TPM response
220  */
221 static uint32_t tpm_sendrecv_command(const void *command,
222                 void *response, size_t *size_ptr)
223 {
224         uint8_t response_buffer[COMMAND_BUFFER_SIZE];
225         size_t response_length;
226         uint32_t err;
227
228         if (response) {
229                 response_length = *size_ptr;
230         } else {
231                 response = response_buffer;
232                 response_length = sizeof(response_buffer);
233         }
234         err = tis_sendrecv(command, tpm_command_size(command),
235                         response, &response_length);
236         if (err)
237                 return TPM_LIB_ERROR;
238         if (response)
239                 *size_ptr = response_length;
240
241         return tpm_return_code(response);
242 }
243
244 uint32_t tpm_init(void)
245 {
246         uint32_t err;
247
248         err = tis_init();
249         if (err)
250                 return err;
251
252         return tis_open();
253 }
254
255 uint32_t tpm_startup(enum tpm_startup_type mode)
256 {
257         const uint8_t command[12] = {
258                 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x0,
259         };
260         const size_t mode_offset = 10;
261         uint8_t buf[COMMAND_BUFFER_SIZE];
262
263         if (pack_byte_string(buf, sizeof(buf), "sw",
264                                 0, command, sizeof(command),
265                                 mode_offset, mode))
266                 return TPM_LIB_ERROR;
267
268         return tpm_sendrecv_command(buf, NULL, NULL);
269 }
270
271 uint32_t tpm_self_test_full(void)
272 {
273         const uint8_t command[10] = {
274                 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50,
275         };
276         return tpm_sendrecv_command(command, NULL, NULL);
277 }
278
279 uint32_t tpm_continue_self_test(void)
280 {
281         const uint8_t command[10] = {
282                 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x53,
283         };
284         return tpm_sendrecv_command(command, NULL, NULL);
285 }
286
287 uint32_t tpm_nv_define_space(uint32_t index, uint32_t perm, uint32_t size)
288 {
289         const uint8_t command[101] = {
290                 0x0, 0xc1,              /* TPM_TAG */
291                 0x0, 0x0, 0x0, 0x65,    /* parameter size */
292                 0x0, 0x0, 0x0, 0xcc,    /* TPM_COMMAND_CODE */
293                 /* TPM_NV_DATA_PUBLIC->... */
294                 0x0, 0x18,              /* ...->TPM_STRUCTURE_TAG */
295                 0, 0, 0, 0,             /* ...->TPM_NV_INDEX */
296                 /* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */
297                 0x0, 0x3,
298                 0, 0, 0,
299                 0x1f,
300                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
301                 /* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */
302                 0x0, 0x3,
303                 0, 0, 0,
304                 0x1f,
305                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
306                 /* TPM_NV_ATTRIBUTES->... */
307                 0x0, 0x17,              /* ...->TPM_STRUCTURE_TAG */
308                 0, 0, 0, 0,             /* ...->attributes */
309                 /* End of TPM_NV_ATTRIBUTES */
310                 0,                      /* bReadSTClear */
311                 0,                      /* bWriteSTClear */
312                 0,                      /* bWriteDefine */
313                 0, 0, 0, 0,             /* size */
314         };
315         const size_t index_offset = 12;
316         const size_t perm_offset = 70;
317         const size_t size_offset = 77;
318         uint8_t buf[COMMAND_BUFFER_SIZE];
319
320         if (pack_byte_string(buf, sizeof(buf), "sddd",
321                                 0, command, sizeof(command),
322                                 index_offset, index,
323                                 perm_offset, perm,
324                                 size_offset, size))
325                 return TPM_LIB_ERROR;
326
327         return tpm_sendrecv_command(buf, NULL, NULL);
328 }
329
330 uint32_t tpm_nv_read_value(uint32_t index, void *data, uint32_t count)
331 {
332         const uint8_t command[22] = {
333                 0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0xcf,
334         };
335         const size_t index_offset = 10;
336         const size_t length_offset = 18;
337         const size_t data_size_offset = 10;
338         const size_t data_offset = 14;
339         uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
340         size_t response_length = sizeof(response);
341         uint32_t data_size;
342         uint32_t err;
343
344         if (pack_byte_string(buf, sizeof(buf), "sdd",
345                                 0, command, sizeof(command),
346                                 index_offset, index,
347                                 length_offset, count))
348                 return TPM_LIB_ERROR;
349         err = tpm_sendrecv_command(buf, response, &response_length);
350         if (err)
351                 return err;
352         if (unpack_byte_string(response, response_length, "d",
353                                 data_size_offset, &data_size))
354                 return TPM_LIB_ERROR;
355         if (data_size > count)
356                 return TPM_LIB_ERROR;
357         if (unpack_byte_string(response, response_length, "s",
358                                 data_offset, data, data_size))
359                 return TPM_LIB_ERROR;
360
361         return 0;
362 }
363
364 uint32_t tpm_nv_write_value(uint32_t index, const void *data, uint32_t length)
365 {
366         const uint8_t command[256] = {
367                 0x0, 0xc1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd,
368         };
369         const size_t command_size_offset = 2;
370         const size_t index_offset = 10;
371         const size_t length_offset = 18;
372         const size_t data_offset = 22;
373         const size_t write_info_size = 12;
374         const uint32_t total_length =
375                 TPM_REQUEST_HEADER_LENGTH + write_info_size + length;
376         uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
377         size_t response_length = sizeof(response);
378         uint32_t err;
379
380         if (pack_byte_string(buf, sizeof(buf), "sddds",
381                                 0, command, sizeof(command),
382                                 command_size_offset, total_length,
383                                 index_offset, index,
384                                 length_offset, length,
385                                 data_offset, data, length))
386                 return TPM_LIB_ERROR;
387         err = tpm_sendrecv_command(buf, response, &response_length);
388         if (err)
389                 return err;
390
391         return 0;
392 }
393
394 uint32_t tpm_extend(uint32_t index, const void *in_digest, void *out_digest)
395 {
396         const uint8_t command[34] = {
397                 0x0, 0xc1, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x14,
398         };
399         const size_t index_offset = 10;
400         const size_t in_digest_offset = 14;
401         const size_t out_digest_offset = 10;
402         uint8_t buf[COMMAND_BUFFER_SIZE];
403         uint8_t response[TPM_RESPONSE_HEADER_LENGTH + PCR_DIGEST_LENGTH];
404         size_t response_length = sizeof(response);
405         uint32_t err;
406
407         if (pack_byte_string(buf, sizeof(buf), "sds",
408                                 0, command, sizeof(command),
409                                 index_offset, index,
410                                 in_digest_offset, in_digest,
411                                 PCR_DIGEST_LENGTH))
412                 return TPM_LIB_ERROR;
413         err = tpm_sendrecv_command(buf, response, &response_length);
414         if (err)
415                 return err;
416
417         if (unpack_byte_string(response, response_length, "s",
418                                 out_digest_offset, out_digest,
419                                 PCR_DIGEST_LENGTH))
420                 return TPM_LIB_ERROR;
421
422         return 0;
423 }
424
425 uint32_t tpm_pcr_read(uint32_t index, void *data, size_t count)
426 {
427         const uint8_t command[14] = {
428                 0x0, 0xc1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x15,
429         };
430         const size_t index_offset = 10;
431         const size_t out_digest_offset = 10;
432         uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
433         size_t response_length = sizeof(response);
434         uint32_t err;
435
436         if (count < PCR_DIGEST_LENGTH)
437                 return TPM_LIB_ERROR;
438
439         if (pack_byte_string(buf, sizeof(buf), "sd",
440                                 0, command, sizeof(command),
441                                 index_offset, index))
442                 return TPM_LIB_ERROR;
443         err = tpm_sendrecv_command(buf, response, &response_length);
444         if (err)
445                 return err;
446         if (unpack_byte_string(response, response_length, "s",
447                                 out_digest_offset, data, PCR_DIGEST_LENGTH))
448                 return TPM_LIB_ERROR;
449
450         return 0;
451 }
452
453 uint32_t tpm_tsc_physical_presence(uint16_t presence)
454 {
455         const uint8_t command[12] = {
456                 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x0,
457         };
458         const size_t presence_offset = 10;
459         uint8_t buf[COMMAND_BUFFER_SIZE];
460
461         if (pack_byte_string(buf, sizeof(buf), "sw",
462                                 0, command, sizeof(command),
463                                 presence_offset, presence))
464                 return TPM_LIB_ERROR;
465
466         return tpm_sendrecv_command(buf, NULL, NULL);
467 }
468
469 uint32_t tpm_read_pubek(void *data, size_t count)
470 {
471         const uint8_t command[30] = {
472                 0x0, 0xc1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x7c,
473         };
474         const size_t response_size_offset = 2;
475         const size_t data_offset = 10;
476         const size_t header_and_checksum_size = TPM_RESPONSE_HEADER_LENGTH + 20;
477         uint8_t response[COMMAND_BUFFER_SIZE + TPM_PUBEK_SIZE];
478         size_t response_length = sizeof(response);
479         uint32_t data_size;
480         uint32_t err;
481
482         err = tpm_sendrecv_command(command, response, &response_length);
483         if (err)
484                 return err;
485         if (unpack_byte_string(response, response_length, "d",
486                                 response_size_offset, &data_size))
487                 return TPM_LIB_ERROR;
488         if (data_size < header_and_checksum_size)
489                 return TPM_LIB_ERROR;
490         data_size -= header_and_checksum_size;
491         if (data_size > count)
492                 return TPM_LIB_ERROR;
493         if (unpack_byte_string(response, response_length, "s",
494                                 data_offset, data, data_size))
495                 return TPM_LIB_ERROR;
496
497         return 0;
498 }
499
500 uint32_t tpm_force_clear(void)
501 {
502         const uint8_t command[10] = {
503                 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x5d,
504         };
505
506         return tpm_sendrecv_command(command, NULL, NULL);
507 }
508
509 uint32_t tpm_physical_enable(void)
510 {
511         const uint8_t command[10] = {
512                 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x6f,
513         };
514
515         return tpm_sendrecv_command(command, NULL, NULL);
516 }
517
518 uint32_t tpm_physical_disable(void)
519 {
520         const uint8_t command[10] = {
521                 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x70,
522         };
523
524         return tpm_sendrecv_command(command, NULL, NULL);
525 }
526
527 uint32_t tpm_physical_set_deactivated(uint8_t state)
528 {
529         const uint8_t command[11] = {
530                 0x0, 0xc1, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x72,
531         };
532         const size_t state_offset = 10;
533         uint8_t buf[COMMAND_BUFFER_SIZE];
534
535         if (pack_byte_string(buf, sizeof(buf), "sb",
536                                 0, command, sizeof(command),
537                                 state_offset, state))
538                 return TPM_LIB_ERROR;
539
540         return tpm_sendrecv_command(buf, NULL, NULL);
541 }
542
543 uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap,
544                 void *cap, size_t count)
545 {
546         const uint8_t command[22] = {
547                 0x0, 0xc1,              /* TPM_TAG */
548                 0x0, 0x0, 0x0, 0x16,    /* parameter size */
549                 0x0, 0x0, 0x0, 0x65,    /* TPM_COMMAND_CODE */
550                 0x0, 0x0, 0x0, 0x0,     /* TPM_CAPABILITY_AREA */
551                 0x0, 0x0, 0x0, 0x4,     /* subcap size */
552                 0x0, 0x0, 0x0, 0x0,     /* subcap value */
553         };
554         const size_t cap_area_offset = 10;
555         const size_t sub_cap_offset = 18;
556         const size_t cap_offset = 14;
557         const size_t cap_size_offset = 10;
558         uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
559         size_t response_length = sizeof(response);
560         uint32_t cap_size;
561         uint32_t err;
562
563         if (pack_byte_string(buf, sizeof(buf), "sdd",
564                                 0, command, sizeof(command),
565                                 cap_area_offset, cap_area,
566                                 sub_cap_offset, sub_cap))
567                 return TPM_LIB_ERROR;
568         err = tpm_sendrecv_command(buf, response, &response_length);
569         if (err)
570                 return err;
571         if (unpack_byte_string(response, response_length, "d",
572                                 cap_size_offset, &cap_size))
573                 return TPM_LIB_ERROR;
574         if (cap_size > response_length || cap_size > count)
575                 return TPM_LIB_ERROR;
576         if (unpack_byte_string(response, response_length, "s",
577                                 cap_offset, cap, cap_size))
578                 return TPM_LIB_ERROR;
579
580         return 0;
581 }