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