699241b3e62a1e250299d8535c5f16539d624055
[platform/kernel/u-boot.git] / board / gdsys / a38x / hre.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2013
4  * Reinhard Pfau, Guntermann & Drunck GmbH, reinhard.pfau@gdsys.cc
5  */
6
7 #include <common.h>
8 #include <log.h>
9 #include <malloc.h>
10 #include <fs.h>
11 #include <i2c.h>
12 #include <mmc.h>
13 #include <tpm-v1.h>
14 #include <u-boot/crc.h>
15 #include <u-boot/sha1.h>
16 #include <asm/byteorder.h>
17 #include <asm/unaligned.h>
18 #include <pca9698.h>
19
20 #include "hre.h"
21
22 /* other constants */
23 enum {
24         ESDHC_BOOT_IMAGE_SIG_OFS        = 0x40,
25         ESDHC_BOOT_IMAGE_SIZE_OFS       = 0x48,
26         ESDHC_BOOT_IMAGE_ADDR_OFS       = 0x50,
27         ESDHC_BOOT_IMAGE_TARGET_OFS     = 0x58,
28         ESDHC_BOOT_IMAGE_ENTRY_OFS      = 0x60,
29 };
30
31 enum {
32         I2C_SOC_0 = 0,
33         I2C_SOC_1 = 1,
34 };
35
36 enum access_mode {
37         HREG_NONE       = 0,
38         HREG_RD         = 1,
39         HREG_WR         = 2,
40         HREG_RDWR       = 3,
41 };
42
43 /* register constants */
44 enum {
45         FIX_HREG_DEVICE_ID_HASH = 0,
46         FIX_HREG_UNUSED1        = 1,
47         FIX_HREG_UNUSED2        = 2,
48         FIX_HREG_VENDOR         = 3,
49         COUNT_FIX_HREGS
50 };
51
52 static struct h_reg pcr_hregs[24];
53 static struct h_reg fix_hregs[COUNT_FIX_HREGS];
54 static struct h_reg var_hregs[8];
55
56 /* hre opcodes */
57 enum {
58         /* opcodes w/o data */
59         HRE_NOP         = 0x00,
60         HRE_SYNC        = HRE_NOP,
61         HRE_CHECK0      = 0x01,
62         /* opcodes w/o data, w/ sync dst */
63         /* opcodes w/ data */
64         HRE_LOAD        = 0x81,
65         /* opcodes w/data, w/sync dst */
66         HRE_XOR         = 0xC1,
67         HRE_AND         = 0xC2,
68         HRE_OR          = 0xC3,
69         HRE_EXTEND      = 0xC4,
70         HRE_LOADKEY     = 0xC5,
71 };
72
73 /* hre errors */
74 enum {
75         HRE_E_OK        = 0,
76         HRE_E_TPM_FAILURE,
77         HRE_E_INVALID_HREG,
78 };
79
80 static uint64_t device_id;
81 static uint64_t device_cl;
82 static uint64_t device_type;
83
84 static uint32_t platform_key_handle;
85
86 static uint32_t hre_tpm_err;
87 static int hre_err = HRE_E_OK;
88
89 #define IS_PCR_HREG(spec) ((spec) & 0x20)
90 #define IS_FIX_HREG(spec) (((spec) & 0x38) == 0x08)
91 #define IS_VAR_HREG(spec) (((spec) & 0x38) == 0x10)
92 #define HREG_IDX(spec) ((spec) & (IS_PCR_HREG(spec) ? 0x1f : 0x7))
93
94 static const uint8_t vendor[] = "Guntermann & Drunck";
95
96 /**
97  * @brief get the size of a given (TPM) NV area
98  * @param tpm           TPM device
99  * @param index NV index of the area to get size for
100  * @param size  pointer to the size
101  * @return 0 on success, != 0 on error
102  */
103 static int get_tpm_nv_size(struct udevice *tpm, uint32_t index, uint32_t *size)
104 {
105         uint32_t err;
106         uint8_t info[72];
107         uint8_t *ptr;
108         uint16_t v16;
109
110         err = tpm_get_capability(tpm, TPM_CAP_NV_INDEX, index,
111                                  info, sizeof(info));
112         if (err) {
113                 printf("tpm_get_capability(CAP_NV_INDEX, %08x) failed: %u\n",
114                        index, err);
115                 return 1;
116         }
117
118         /* skip tag and nvIndex */
119         ptr = info + 6;
120         /* skip 2 pcr info fields */
121         v16 = get_unaligned_be16(ptr);
122         ptr += 2 + v16 + 1 + 20;
123         v16 = get_unaligned_be16(ptr);
124         ptr += 2 + v16 + 1 + 20;
125         /* skip permission and flags */
126         ptr += 6 + 3;
127
128         *size = get_unaligned_be32(ptr);
129         return 0;
130 }
131
132 /**
133  * @brief search for a key by usage auth and pub key hash.
134  * @param tpm           TPM device
135  * @param auth  usage auth of the key to search for
136  * @param pubkey_digest (SHA1) hash of the pub key structure of the key
137  * @param[out] handle   the handle of the key iff found
138  * @return 0 if key was found in TPM; != 0 if not.
139  */
140 static int find_key(struct udevice *tpm, const uint8_t auth[20],
141                     const uint8_t pubkey_digest[20], uint32_t *handle)
142 {
143         uint16_t key_count;
144         uint32_t key_handles[10];
145         uint8_t buf[288];
146         uint8_t *ptr;
147         uint32_t err;
148         uint8_t digest[20];
149         size_t buf_len;
150         unsigned int i;
151
152         /* fetch list of already loaded keys in the TPM */
153         err = tpm_get_capability(tpm, TPM_CAP_HANDLE, TPM_RT_KEY, buf,
154                                  sizeof(buf));
155         if (err)
156                 return -1;
157         key_count = get_unaligned_be16(buf);
158         ptr = buf + 2;
159         for (i = 0; i < key_count; ++i, ptr += 4)
160                 key_handles[i] = get_unaligned_be32(ptr);
161
162         /* now search a(/ the) key which we can access with the given auth */
163         for (i = 0; i < key_count; ++i) {
164                 buf_len = sizeof(buf);
165                 err = tpm_get_pub_key_oiap(tpm, key_handles[i], auth, buf,
166                                            &buf_len);
167                 if (err && err != TPM_AUTHFAIL)
168                         return -1;
169                 if (err)
170                         continue;
171                 sha1_csum(buf, buf_len, digest);
172                 if (!memcmp(digest, pubkey_digest, 20)) {
173                         *handle = key_handles[i];
174                         return 0;
175                 }
176         }
177         return 1;
178 }
179
180 /**
181  * @brief read CCDM common data from TPM NV
182  * @param tpm           TPM device
183  * @return 0 if CCDM common data was found and read, !=0 if something failed.
184  */
185 static int read_common_data(struct udevice *tpm)
186 {
187         uint32_t size = 0;
188         uint32_t err;
189         uint8_t buf[256];
190         sha1_context ctx;
191
192         if (get_tpm_nv_size(tpm, NV_COMMON_DATA_INDEX, &size) ||
193             size < NV_COMMON_DATA_MIN_SIZE)
194                 return 1;
195         err = tpm_nv_read_value(tpm, NV_COMMON_DATA_INDEX,
196                                 buf, min(sizeof(buf), size));
197         if (err) {
198                 printf("tpm_nv_read_value() failed: %u\n", err);
199                 return 1;
200         }
201
202         device_id = get_unaligned_be64(buf);
203         device_cl = get_unaligned_be64(buf + 8);
204         device_type = get_unaligned_be64(buf + 16);
205
206         sha1_starts(&ctx);
207         sha1_update(&ctx, buf, 24);
208         sha1_finish(&ctx, fix_hregs[FIX_HREG_DEVICE_ID_HASH].digest);
209         fix_hregs[FIX_HREG_DEVICE_ID_HASH].valid = true;
210
211         platform_key_handle = get_unaligned_be32(buf + 24);
212
213         return 0;
214 }
215
216 /**
217  * @brief get pointer to  hash register by specification
218  * @param spec  specification of a hash register
219  * @return pointer to hash register or NULL if @a spec does not qualify a
220  * valid hash register; NULL else.
221  */
222 static struct h_reg *get_hreg(uint8_t spec)
223 {
224         uint8_t idx;
225
226         idx = HREG_IDX(spec);
227         if (IS_FIX_HREG(spec)) {
228                 if (idx < ARRAY_SIZE(fix_hregs))
229                         return fix_hregs + idx;
230                 hre_err = HRE_E_INVALID_HREG;
231         } else if (IS_PCR_HREG(spec)) {
232                 if (idx < ARRAY_SIZE(pcr_hregs))
233                         return pcr_hregs + idx;
234                 hre_err = HRE_E_INVALID_HREG;
235         } else if (IS_VAR_HREG(spec)) {
236                 if (idx < ARRAY_SIZE(var_hregs))
237                         return var_hregs + idx;
238                 hre_err = HRE_E_INVALID_HREG;
239         }
240         return NULL;
241 }
242
243 /**
244  * @brief get pointer of a hash register by specification and usage.
245  * @param tpm           TPM device
246  * @param spec  specification of a hash register
247  * @param mode  access mode (read or write or read/write)
248  * @return pointer to hash register if found and valid; NULL else.
249  *
250  * This func uses @a get_reg() to determine the hash register for a given spec.
251  * If a register is found it is validated according to the desired access mode.
252  * The value of automatic registers (PCR register and fixed registers) is
253  * loaded or computed on read access.
254  */
255 static struct h_reg *access_hreg(struct udevice *tpm, uint8_t spec,
256                                  enum access_mode mode)
257 {
258         struct h_reg *result;
259
260         result = get_hreg(spec);
261         if (!result)
262                 return NULL;
263
264         if (mode & HREG_WR) {
265                 if (IS_FIX_HREG(spec)) {
266                         hre_err = HRE_E_INVALID_HREG;
267                         return NULL;
268                 }
269         }
270         if (mode & HREG_RD) {
271                 if (!result->valid) {
272                         if (IS_PCR_HREG(spec)) {
273                                 hre_tpm_err = tpm_pcr_read(tpm, HREG_IDX(spec),
274                                         result->digest, 20);
275                                 result->valid = (hre_tpm_err == TPM_SUCCESS);
276                         } else if (IS_FIX_HREG(spec)) {
277                                 switch (HREG_IDX(spec)) {
278                                 case FIX_HREG_DEVICE_ID_HASH:
279                                         read_common_data(tpm);
280                                         break;
281                                 case FIX_HREG_VENDOR:
282                                         memcpy(result->digest, vendor, 20);
283                                         result->valid = true;
284                                         break;
285                                 }
286                         } else {
287                                 result->valid = true;
288                         }
289                 }
290                 if (!result->valid) {
291                         hre_err = HRE_E_INVALID_HREG;
292                         return NULL;
293                 }
294         }
295
296         return result;
297 }
298
299 static void *compute_and(void *_dst, const void *_src, size_t n)
300 {
301         uint8_t *dst = _dst;
302         const uint8_t *src = _src;
303         size_t i;
304
305         for (i = n; i-- > 0; )
306                 *dst++ &= *src++;
307
308         return _dst;
309 }
310
311 static void *compute_or(void *_dst, const void *_src, size_t n)
312 {
313         uint8_t *dst = _dst;
314         const uint8_t *src = _src;
315         size_t i;
316
317         for (i = n; i-- > 0; )
318                 *dst++ |= *src++;
319
320         return _dst;
321 }
322
323 static void *compute_xor(void *_dst, const void *_src, size_t n)
324 {
325         uint8_t *dst = _dst;
326         const uint8_t *src = _src;
327         size_t i;
328
329         for (i = n; i-- > 0; )
330                 *dst++ ^= *src++;
331
332         return _dst;
333 }
334
335 static void *compute_extend(void *_dst, const void *_src, size_t n)
336 {
337         uint8_t digest[20];
338         sha1_context ctx;
339
340         sha1_starts(&ctx);
341         sha1_update(&ctx, _dst, n);
342         sha1_update(&ctx, _src, n);
343         sha1_finish(&ctx, digest);
344         memcpy(_dst, digest, min(n, sizeof(digest)));
345
346         return _dst;
347 }
348
349 static int hre_op_loadkey(struct udevice *tpm, struct h_reg *src_reg,
350                           struct h_reg *dst_reg, const void *key,
351                           size_t key_size)
352 {
353         uint32_t parent_handle;
354         uint32_t key_handle;
355
356         if (!src_reg || !dst_reg || !src_reg->valid || !dst_reg->valid)
357                 return -1;
358         if (find_key(tpm, src_reg->digest, dst_reg->digest, &parent_handle))
359                 return -1;
360         hre_tpm_err = tpm_load_key2_oiap(tpm, parent_handle, key, key_size,
361                                          src_reg->digest, &key_handle);
362         if (hre_tpm_err) {
363                 hre_err = HRE_E_TPM_FAILURE;
364                 return -1;
365         }
366
367         return 0;
368 }
369
370 /**
371  * @brief executes the next opcode on the hash register engine.
372  * @param tpm           TPM device
373  * @param[in,out] ip    pointer to the opcode (instruction pointer)
374  * @param[in,out] code_size     (remaining) size of the code
375  * @return new instruction pointer on success, NULL on error.
376  */
377 static const uint8_t *hre_execute_op(struct udevice *tpm, const uint8_t **ip,
378                                      size_t *code_size)
379 {
380         bool dst_modified = false;
381         uint32_t ins;
382         uint8_t opcode;
383         uint8_t src_spec;
384         uint8_t dst_spec;
385         uint16_t data_size;
386         struct h_reg *src_reg, *dst_reg;
387         uint8_t buf[20];
388         const uint8_t *src_buf, *data;
389         uint8_t *ptr;
390         int i;
391         void * (*bin_func)(void *, const void *, size_t);
392
393         if (*code_size < 4)
394                 return NULL;
395
396         ins = get_unaligned_be32(*ip);
397         opcode = **ip;
398         data = *ip + 4;
399         src_spec = (ins >> 18) & 0x3f;
400         dst_spec = (ins >> 12) & 0x3f;
401         data_size = (ins & 0x7ff);
402
403         debug("HRE: ins=%08x (op=%02x, s=%02x, d=%02x, L=%d)\n", ins,
404               opcode, src_spec, dst_spec, data_size);
405
406         if ((opcode & 0x80) && (data_size + 4) > *code_size)
407                 return NULL;
408
409         src_reg = access_hreg(tpm, src_spec, HREG_RD);
410         if (hre_err || hre_tpm_err)
411                 return NULL;
412         dst_reg = access_hreg(tpm, dst_spec,
413                               (opcode & 0x40) ? HREG_RDWR : HREG_WR);
414         if (hre_err || hre_tpm_err)
415                 return NULL;
416
417         switch (opcode) {
418         case HRE_NOP:
419                 goto end;
420         case HRE_CHECK0:
421                 if (src_reg) {
422                         for (i = 0; i < 20; ++i) {
423                                 if (src_reg->digest[i])
424                                         return NULL;
425                         }
426                 }
427                 break;
428         case HRE_LOAD:
429                 bin_func = memcpy;
430                 goto do_bin_func;
431         case HRE_XOR:
432                 bin_func = compute_xor;
433                 goto do_bin_func;
434         case HRE_AND:
435                 bin_func = compute_and;
436                 goto do_bin_func;
437         case HRE_OR:
438                 bin_func = compute_or;
439                 goto do_bin_func;
440         case HRE_EXTEND:
441                 bin_func = compute_extend;
442 do_bin_func:
443                 if (!dst_reg)
444                         return NULL;
445                 if (src_reg) {
446                         src_buf = src_reg->digest;
447                 } else {
448                         if (!data_size) {
449                                 memset(buf, 0, 20);
450                                 src_buf = buf;
451                         } else if (data_size == 1) {
452                                 memset(buf, *data, 20);
453                                 src_buf = buf;
454                         } else if (data_size >= 20) {
455                                 src_buf = data;
456                         } else {
457                                 src_buf = buf;
458                                 for (ptr = (uint8_t *)src_buf, i = 20; i > 0;
459                                         i -= data_size, ptr += data_size)
460                                         memcpy(ptr, data,
461                                                min_t(size_t, i, data_size));
462                         }
463                 }
464                 bin_func(dst_reg->digest, src_buf, 20);
465                 dst_reg->valid = true;
466                 dst_modified = true;
467                 break;
468         case HRE_LOADKEY:
469                 if (hre_op_loadkey(tpm, src_reg, dst_reg, data, data_size))
470                         return NULL;
471                 break;
472         default:
473                 return NULL;
474         }
475
476         if (dst_reg && dst_modified && IS_PCR_HREG(dst_spec)) {
477                 hre_tpm_err = tpm_extend(tpm, HREG_IDX(dst_spec),
478                                          dst_reg->digest, dst_reg->digest);
479                 if (hre_tpm_err) {
480                         hre_err = HRE_E_TPM_FAILURE;
481                         return NULL;
482                 }
483         }
484 end:
485         *ip += 4;
486         *code_size -= 4;
487         if (opcode & 0x80) {
488                 *ip += data_size;
489                 *code_size -= data_size;
490         }
491
492         return *ip;
493 }
494
495 /**
496  * @brief runs a program on the hash register engine.
497  * @param tpm           TPM device
498  * @param code          pointer to the (HRE) code.
499  * @param code_size     size of the code (in bytes).
500  * @return 0 on success, != 0 on failure.
501  */
502 int hre_run_program(struct udevice *tpm, const uint8_t *code, size_t code_size)
503 {
504         size_t code_left;
505         const uint8_t *ip = code;
506
507         code_left = code_size;
508         hre_tpm_err = 0;
509         hre_err = HRE_E_OK;
510         while (code_left > 0)
511                 if (!hre_execute_op(tpm, &ip, &code_left))
512                         return -1;
513
514         return hre_err;
515 }
516
517 int hre_verify_program(struct key_program *prg)
518 {
519         uint32_t crc;
520
521         crc = crc32(0, prg->code, prg->code_size);
522
523         if (crc != prg->code_crc) {
524                 printf("HRC crc mismatch: %08x != %08x\n",
525                        crc, prg->code_crc);
526                 return 1;
527         }
528         return 0;
529 }