183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
260083261SDirk Eibach /*
360083261SDirk Eibach * (C) Copyright 2013
460083261SDirk Eibach * Reinhard Pfau, Guntermann & Drunck GmbH, reinhard.pfau@gdsys.cc
560083261SDirk Eibach */
660083261SDirk Eibach
760083261SDirk Eibach #include <common.h>
860083261SDirk Eibach #include <malloc.h>
960083261SDirk Eibach #include <fs.h>
1060083261SDirk Eibach #include <i2c.h>
1160083261SDirk Eibach #include <mmc.h>
12d677bfe2SMiquel Raynal #include <tpm-v1.h>
1360083261SDirk Eibach #include <u-boot/sha1.h>
1460083261SDirk Eibach #include <asm/byteorder.h>
1560083261SDirk Eibach #include <asm/unaligned.h>
1660083261SDirk Eibach #include <pca9698.h>
1760083261SDirk Eibach
1860083261SDirk Eibach #include "hre.h"
1960083261SDirk Eibach
2060083261SDirk Eibach /* other constants */
2160083261SDirk Eibach enum {
2260083261SDirk Eibach ESDHC_BOOT_IMAGE_SIG_OFS = 0x40,
2360083261SDirk Eibach ESDHC_BOOT_IMAGE_SIZE_OFS = 0x48,
2460083261SDirk Eibach ESDHC_BOOT_IMAGE_ADDR_OFS = 0x50,
2560083261SDirk Eibach ESDHC_BOOT_IMAGE_TARGET_OFS = 0x58,
2660083261SDirk Eibach ESDHC_BOOT_IMAGE_ENTRY_OFS = 0x60,
2760083261SDirk Eibach };
2860083261SDirk Eibach
2960083261SDirk Eibach enum {
3060083261SDirk Eibach I2C_SOC_0 = 0,
3160083261SDirk Eibach I2C_SOC_1 = 1,
3260083261SDirk Eibach };
3360083261SDirk Eibach
3460083261SDirk Eibach enum access_mode {
3560083261SDirk Eibach HREG_NONE = 0,
3660083261SDirk Eibach HREG_RD = 1,
3760083261SDirk Eibach HREG_WR = 2,
3860083261SDirk Eibach HREG_RDWR = 3,
3960083261SDirk Eibach };
4060083261SDirk Eibach
4160083261SDirk Eibach /* register constants */
4260083261SDirk Eibach enum {
4360083261SDirk Eibach FIX_HREG_DEVICE_ID_HASH = 0,
4460083261SDirk Eibach FIX_HREG_UNUSED1 = 1,
4560083261SDirk Eibach FIX_HREG_UNUSED2 = 2,
4660083261SDirk Eibach FIX_HREG_VENDOR = 3,
4760083261SDirk Eibach COUNT_FIX_HREGS
4860083261SDirk Eibach };
4960083261SDirk Eibach
5060083261SDirk Eibach static struct h_reg pcr_hregs[24];
5160083261SDirk Eibach static struct h_reg fix_hregs[COUNT_FIX_HREGS];
5260083261SDirk Eibach static struct h_reg var_hregs[8];
5360083261SDirk Eibach
5460083261SDirk Eibach /* hre opcodes */
5560083261SDirk Eibach enum {
5660083261SDirk Eibach /* opcodes w/o data */
5760083261SDirk Eibach HRE_NOP = 0x00,
5860083261SDirk Eibach HRE_SYNC = HRE_NOP,
5960083261SDirk Eibach HRE_CHECK0 = 0x01,
6060083261SDirk Eibach /* opcodes w/o data, w/ sync dst */
6160083261SDirk Eibach /* opcodes w/ data */
6260083261SDirk Eibach HRE_LOAD = 0x81,
6360083261SDirk Eibach /* opcodes w/data, w/sync dst */
6460083261SDirk Eibach HRE_XOR = 0xC1,
6560083261SDirk Eibach HRE_AND = 0xC2,
6660083261SDirk Eibach HRE_OR = 0xC3,
6760083261SDirk Eibach HRE_EXTEND = 0xC4,
6860083261SDirk Eibach HRE_LOADKEY = 0xC5,
6960083261SDirk Eibach };
7060083261SDirk Eibach
7160083261SDirk Eibach /* hre errors */
7260083261SDirk Eibach enum {
7360083261SDirk Eibach HRE_E_OK = 0,
7460083261SDirk Eibach HRE_E_TPM_FAILURE,
7560083261SDirk Eibach HRE_E_INVALID_HREG,
7660083261SDirk Eibach };
7760083261SDirk Eibach
7860083261SDirk Eibach static uint64_t device_id;
7960083261SDirk Eibach static uint64_t device_cl;
8060083261SDirk Eibach static uint64_t device_type;
8160083261SDirk Eibach
8260083261SDirk Eibach static uint32_t platform_key_handle;
8360083261SDirk Eibach
8460083261SDirk Eibach static uint32_t hre_tpm_err;
8560083261SDirk Eibach static int hre_err = HRE_E_OK;
8660083261SDirk Eibach
8760083261SDirk Eibach #define IS_PCR_HREG(spec) ((spec) & 0x20)
8860083261SDirk Eibach #define IS_FIX_HREG(spec) (((spec) & 0x38) == 0x08)
8960083261SDirk Eibach #define IS_VAR_HREG(spec) (((spec) & 0x38) == 0x10)
9060083261SDirk Eibach #define HREG_IDX(spec) ((spec) & (IS_PCR_HREG(spec) ? 0x1f : 0x7))
9160083261SDirk Eibach
9260083261SDirk Eibach static const uint8_t vendor[] = "Guntermann & Drunck";
9360083261SDirk Eibach
9460083261SDirk Eibach /**
9560083261SDirk Eibach * @brief get the size of a given (TPM) NV area
96*abdc7b8aSSimon Glass * @param tpm TPM device
9760083261SDirk Eibach * @param index NV index of the area to get size for
9860083261SDirk Eibach * @param size pointer to the size
9960083261SDirk Eibach * @return 0 on success, != 0 on error
10060083261SDirk Eibach */
get_tpm_nv_size(struct udevice * tpm,uint32_t index,uint32_t * size)101*abdc7b8aSSimon Glass static int get_tpm_nv_size(struct udevice *tpm, uint32_t index, uint32_t *size)
10260083261SDirk Eibach {
10360083261SDirk Eibach uint32_t err;
10460083261SDirk Eibach uint8_t info[72];
10560083261SDirk Eibach uint8_t *ptr;
10660083261SDirk Eibach uint16_t v16;
10760083261SDirk Eibach
108*abdc7b8aSSimon Glass err = tpm_get_capability(tpm, TPM_CAP_NV_INDEX, index,
10960083261SDirk Eibach info, sizeof(info));
11060083261SDirk Eibach if (err) {
11160083261SDirk Eibach printf("tpm_get_capability(CAP_NV_INDEX, %08x) failed: %u\n",
11260083261SDirk Eibach index, err);
11360083261SDirk Eibach return 1;
11460083261SDirk Eibach }
11560083261SDirk Eibach
11660083261SDirk Eibach /* skip tag and nvIndex */
11760083261SDirk Eibach ptr = info + 6;
11860083261SDirk Eibach /* skip 2 pcr info fields */
11960083261SDirk Eibach v16 = get_unaligned_be16(ptr);
12060083261SDirk Eibach ptr += 2 + v16 + 1 + 20;
12160083261SDirk Eibach v16 = get_unaligned_be16(ptr);
12260083261SDirk Eibach ptr += 2 + v16 + 1 + 20;
12360083261SDirk Eibach /* skip permission and flags */
12460083261SDirk Eibach ptr += 6 + 3;
12560083261SDirk Eibach
12660083261SDirk Eibach *size = get_unaligned_be32(ptr);
12760083261SDirk Eibach return 0;
12860083261SDirk Eibach }
12960083261SDirk Eibach
13060083261SDirk Eibach /**
13160083261SDirk Eibach * @brief search for a key by usage auth and pub key hash.
132*abdc7b8aSSimon Glass * @param tpm TPM device
13360083261SDirk Eibach * @param auth usage auth of the key to search for
13460083261SDirk Eibach * @param pubkey_digest (SHA1) hash of the pub key structure of the key
13560083261SDirk Eibach * @param[out] handle the handle of the key iff found
13660083261SDirk Eibach * @return 0 if key was found in TPM; != 0 if not.
13760083261SDirk Eibach */
find_key(struct udevice * tpm,const uint8_t auth[20],const uint8_t pubkey_digest[20],uint32_t * handle)138*abdc7b8aSSimon Glass static int find_key(struct udevice *tpm, const uint8_t auth[20],
139*abdc7b8aSSimon Glass const uint8_t pubkey_digest[20], uint32_t *handle)
14060083261SDirk Eibach {
14160083261SDirk Eibach uint16_t key_count;
14260083261SDirk Eibach uint32_t key_handles[10];
14360083261SDirk Eibach uint8_t buf[288];
14460083261SDirk Eibach uint8_t *ptr;
14560083261SDirk Eibach uint32_t err;
14660083261SDirk Eibach uint8_t digest[20];
14760083261SDirk Eibach size_t buf_len;
14860083261SDirk Eibach unsigned int i;
14960083261SDirk Eibach
15060083261SDirk Eibach /* fetch list of already loaded keys in the TPM */
151*abdc7b8aSSimon Glass err = tpm_get_capability(tpm, TPM_CAP_HANDLE, TPM_RT_KEY, buf,
152*abdc7b8aSSimon Glass sizeof(buf));
15360083261SDirk Eibach if (err)
15460083261SDirk Eibach return -1;
15560083261SDirk Eibach key_count = get_unaligned_be16(buf);
15660083261SDirk Eibach ptr = buf + 2;
15760083261SDirk Eibach for (i = 0; i < key_count; ++i, ptr += 4)
15860083261SDirk Eibach key_handles[i] = get_unaligned_be32(ptr);
15960083261SDirk Eibach
16060083261SDirk Eibach /* now search a(/ the) key which we can access with the given auth */
16160083261SDirk Eibach for (i = 0; i < key_count; ++i) {
16260083261SDirk Eibach buf_len = sizeof(buf);
163*abdc7b8aSSimon Glass err = tpm_get_pub_key_oiap(tpm, key_handles[i], auth, buf,
164*abdc7b8aSSimon Glass &buf_len);
16560083261SDirk Eibach if (err && err != TPM_AUTHFAIL)
16660083261SDirk Eibach return -1;
16760083261SDirk Eibach if (err)
16860083261SDirk Eibach continue;
16960083261SDirk Eibach sha1_csum(buf, buf_len, digest);
17060083261SDirk Eibach if (!memcmp(digest, pubkey_digest, 20)) {
17160083261SDirk Eibach *handle = key_handles[i];
17260083261SDirk Eibach return 0;
17360083261SDirk Eibach }
17460083261SDirk Eibach }
17560083261SDirk Eibach return 1;
17660083261SDirk Eibach }
17760083261SDirk Eibach
17860083261SDirk Eibach /**
17960083261SDirk Eibach * @brief read CCDM common data from TPM NV
180*abdc7b8aSSimon Glass * @param tpm TPM device
18160083261SDirk Eibach * @return 0 if CCDM common data was found and read, !=0 if something failed.
18260083261SDirk Eibach */
read_common_data(struct udevice * tpm)183*abdc7b8aSSimon Glass static int read_common_data(struct udevice *tpm)
18460083261SDirk Eibach {
18560083261SDirk Eibach uint32_t size = 0;
18660083261SDirk Eibach uint32_t err;
18760083261SDirk Eibach uint8_t buf[256];
18860083261SDirk Eibach sha1_context ctx;
18960083261SDirk Eibach
190*abdc7b8aSSimon Glass if (get_tpm_nv_size(tpm, NV_COMMON_DATA_INDEX, &size) ||
19160083261SDirk Eibach size < NV_COMMON_DATA_MIN_SIZE)
19260083261SDirk Eibach return 1;
193*abdc7b8aSSimon Glass err = tpm_nv_read_value(tpm, NV_COMMON_DATA_INDEX,
19460083261SDirk Eibach buf, min(sizeof(buf), size));
19560083261SDirk Eibach if (err) {
19660083261SDirk Eibach printf("tpm_nv_read_value() failed: %u\n", err);
19760083261SDirk Eibach return 1;
19860083261SDirk Eibach }
19960083261SDirk Eibach
20060083261SDirk Eibach device_id = get_unaligned_be64(buf);
20160083261SDirk Eibach device_cl = get_unaligned_be64(buf + 8);
20260083261SDirk Eibach device_type = get_unaligned_be64(buf + 16);
20360083261SDirk Eibach
20460083261SDirk Eibach sha1_starts(&ctx);
20560083261SDirk Eibach sha1_update(&ctx, buf, 24);
20660083261SDirk Eibach sha1_finish(&ctx, fix_hregs[FIX_HREG_DEVICE_ID_HASH].digest);
20760083261SDirk Eibach fix_hregs[FIX_HREG_DEVICE_ID_HASH].valid = true;
20860083261SDirk Eibach
20960083261SDirk Eibach platform_key_handle = get_unaligned_be32(buf + 24);
21060083261SDirk Eibach
21160083261SDirk Eibach return 0;
21260083261SDirk Eibach }
21360083261SDirk Eibach
21460083261SDirk Eibach /**
21560083261SDirk Eibach * @brief get pointer to hash register by specification
21660083261SDirk Eibach * @param spec specification of a hash register
21760083261SDirk Eibach * @return pointer to hash register or NULL if @a spec does not qualify a
21860083261SDirk Eibach * valid hash register; NULL else.
21960083261SDirk Eibach */
get_hreg(uint8_t spec)22060083261SDirk Eibach static struct h_reg *get_hreg(uint8_t spec)
22160083261SDirk Eibach {
22260083261SDirk Eibach uint8_t idx;
22360083261SDirk Eibach
22460083261SDirk Eibach idx = HREG_IDX(spec);
22560083261SDirk Eibach if (IS_FIX_HREG(spec)) {
22660083261SDirk Eibach if (idx < ARRAY_SIZE(fix_hregs))
22760083261SDirk Eibach return fix_hregs + idx;
22860083261SDirk Eibach hre_err = HRE_E_INVALID_HREG;
22960083261SDirk Eibach } else if (IS_PCR_HREG(spec)) {
23060083261SDirk Eibach if (idx < ARRAY_SIZE(pcr_hregs))
23160083261SDirk Eibach return pcr_hregs + idx;
23260083261SDirk Eibach hre_err = HRE_E_INVALID_HREG;
23360083261SDirk Eibach } else if (IS_VAR_HREG(spec)) {
23460083261SDirk Eibach if (idx < ARRAY_SIZE(var_hregs))
23560083261SDirk Eibach return var_hregs + idx;
23660083261SDirk Eibach hre_err = HRE_E_INVALID_HREG;
23760083261SDirk Eibach }
23860083261SDirk Eibach return NULL;
23960083261SDirk Eibach }
24060083261SDirk Eibach
24160083261SDirk Eibach /**
24260083261SDirk Eibach * @brief get pointer of a hash register by specification and usage.
243*abdc7b8aSSimon Glass * @param tpm TPM device
24460083261SDirk Eibach * @param spec specification of a hash register
24560083261SDirk Eibach * @param mode access mode (read or write or read/write)
24660083261SDirk Eibach * @return pointer to hash register if found and valid; NULL else.
24760083261SDirk Eibach *
24860083261SDirk Eibach * This func uses @a get_reg() to determine the hash register for a given spec.
24960083261SDirk Eibach * If a register is found it is validated according to the desired access mode.
25060083261SDirk Eibach * The value of automatic registers (PCR register and fixed registers) is
25160083261SDirk Eibach * loaded or computed on read access.
25260083261SDirk Eibach */
access_hreg(struct udevice * tpm,uint8_t spec,enum access_mode mode)253*abdc7b8aSSimon Glass static struct h_reg *access_hreg(struct udevice *tpm, uint8_t spec,
254*abdc7b8aSSimon Glass enum access_mode mode)
25560083261SDirk Eibach {
25660083261SDirk Eibach struct h_reg *result;
25760083261SDirk Eibach
25860083261SDirk Eibach result = get_hreg(spec);
25960083261SDirk Eibach if (!result)
26060083261SDirk Eibach return NULL;
26160083261SDirk Eibach
26260083261SDirk Eibach if (mode & HREG_WR) {
26360083261SDirk Eibach if (IS_FIX_HREG(spec)) {
26460083261SDirk Eibach hre_err = HRE_E_INVALID_HREG;
26560083261SDirk Eibach return NULL;
26660083261SDirk Eibach }
26760083261SDirk Eibach }
26860083261SDirk Eibach if (mode & HREG_RD) {
26960083261SDirk Eibach if (!result->valid) {
27060083261SDirk Eibach if (IS_PCR_HREG(spec)) {
271*abdc7b8aSSimon Glass hre_tpm_err = tpm_pcr_read(tpm, HREG_IDX(spec),
27260083261SDirk Eibach result->digest, 20);
27360083261SDirk Eibach result->valid = (hre_tpm_err == TPM_SUCCESS);
27460083261SDirk Eibach } else if (IS_FIX_HREG(spec)) {
27560083261SDirk Eibach switch (HREG_IDX(spec)) {
27660083261SDirk Eibach case FIX_HREG_DEVICE_ID_HASH:
277*abdc7b8aSSimon Glass read_common_data(tpm);
27860083261SDirk Eibach break;
27960083261SDirk Eibach case FIX_HREG_VENDOR:
28060083261SDirk Eibach memcpy(result->digest, vendor, 20);
28160083261SDirk Eibach result->valid = true;
28260083261SDirk Eibach break;
28360083261SDirk Eibach }
28460083261SDirk Eibach } else {
28560083261SDirk Eibach result->valid = true;
28660083261SDirk Eibach }
28760083261SDirk Eibach }
28860083261SDirk Eibach if (!result->valid) {
28960083261SDirk Eibach hre_err = HRE_E_INVALID_HREG;
29060083261SDirk Eibach return NULL;
29160083261SDirk Eibach }
29260083261SDirk Eibach }
29360083261SDirk Eibach
29460083261SDirk Eibach return result;
29560083261SDirk Eibach }
29660083261SDirk Eibach
compute_and(void * _dst,const void * _src,size_t n)29760083261SDirk Eibach static void *compute_and(void *_dst, const void *_src, size_t n)
29860083261SDirk Eibach {
29960083261SDirk Eibach uint8_t *dst = _dst;
30060083261SDirk Eibach const uint8_t *src = _src;
30160083261SDirk Eibach size_t i;
30260083261SDirk Eibach
30360083261SDirk Eibach for (i = n; i-- > 0; )
30460083261SDirk Eibach *dst++ &= *src++;
30560083261SDirk Eibach
30660083261SDirk Eibach return _dst;
30760083261SDirk Eibach }
30860083261SDirk Eibach
compute_or(void * _dst,const void * _src,size_t n)30960083261SDirk Eibach static void *compute_or(void *_dst, const void *_src, size_t n)
31060083261SDirk Eibach {
31160083261SDirk Eibach uint8_t *dst = _dst;
31260083261SDirk Eibach const uint8_t *src = _src;
31360083261SDirk Eibach size_t i;
31460083261SDirk Eibach
31560083261SDirk Eibach for (i = n; i-- > 0; )
31660083261SDirk Eibach *dst++ |= *src++;
31760083261SDirk Eibach
31860083261SDirk Eibach return _dst;
31960083261SDirk Eibach }
32060083261SDirk Eibach
compute_xor(void * _dst,const void * _src,size_t n)32160083261SDirk Eibach static void *compute_xor(void *_dst, const void *_src, size_t n)
32260083261SDirk Eibach {
32360083261SDirk Eibach uint8_t *dst = _dst;
32460083261SDirk Eibach const uint8_t *src = _src;
32560083261SDirk Eibach size_t i;
32660083261SDirk Eibach
32760083261SDirk Eibach for (i = n; i-- > 0; )
32860083261SDirk Eibach *dst++ ^= *src++;
32960083261SDirk Eibach
33060083261SDirk Eibach return _dst;
33160083261SDirk Eibach }
33260083261SDirk Eibach
compute_extend(void * _dst,const void * _src,size_t n)33360083261SDirk Eibach static void *compute_extend(void *_dst, const void *_src, size_t n)
33460083261SDirk Eibach {
33560083261SDirk Eibach uint8_t digest[20];
33660083261SDirk Eibach sha1_context ctx;
33760083261SDirk Eibach
33860083261SDirk Eibach sha1_starts(&ctx);
33960083261SDirk Eibach sha1_update(&ctx, _dst, n);
34060083261SDirk Eibach sha1_update(&ctx, _src, n);
34160083261SDirk Eibach sha1_finish(&ctx, digest);
34260083261SDirk Eibach memcpy(_dst, digest, min(n, sizeof(digest)));
34360083261SDirk Eibach
34460083261SDirk Eibach return _dst;
34560083261SDirk Eibach }
34660083261SDirk Eibach
hre_op_loadkey(struct udevice * tpm,struct h_reg * src_reg,struct h_reg * dst_reg,const void * key,size_t key_size)347*abdc7b8aSSimon Glass static int hre_op_loadkey(struct udevice *tpm, struct h_reg *src_reg,
348*abdc7b8aSSimon Glass struct h_reg *dst_reg, const void *key,
349*abdc7b8aSSimon Glass size_t key_size)
35060083261SDirk Eibach {
35160083261SDirk Eibach uint32_t parent_handle;
35260083261SDirk Eibach uint32_t key_handle;
35360083261SDirk Eibach
35460083261SDirk Eibach if (!src_reg || !dst_reg || !src_reg->valid || !dst_reg->valid)
35560083261SDirk Eibach return -1;
356*abdc7b8aSSimon Glass if (find_key(tpm, src_reg->digest, dst_reg->digest, &parent_handle))
35760083261SDirk Eibach return -1;
358*abdc7b8aSSimon Glass hre_tpm_err = tpm_load_key2_oiap(tpm, parent_handle, key, key_size,
35960083261SDirk Eibach src_reg->digest, &key_handle);
36060083261SDirk Eibach if (hre_tpm_err) {
36160083261SDirk Eibach hre_err = HRE_E_TPM_FAILURE;
36260083261SDirk Eibach return -1;
36360083261SDirk Eibach }
36460083261SDirk Eibach
36560083261SDirk Eibach return 0;
36660083261SDirk Eibach }
36760083261SDirk Eibach
36860083261SDirk Eibach /**
36960083261SDirk Eibach * @brief executes the next opcode on the hash register engine.
370*abdc7b8aSSimon Glass * @param tpm TPM device
37160083261SDirk Eibach * @param[in,out] ip pointer to the opcode (instruction pointer)
37260083261SDirk Eibach * @param[in,out] code_size (remaining) size of the code
37360083261SDirk Eibach * @return new instruction pointer on success, NULL on error.
37460083261SDirk Eibach */
hre_execute_op(struct udevice * tpm,const uint8_t ** ip,size_t * code_size)375*abdc7b8aSSimon Glass static const uint8_t *hre_execute_op(struct udevice *tpm, const uint8_t **ip,
376*abdc7b8aSSimon Glass size_t *code_size)
37760083261SDirk Eibach {
37860083261SDirk Eibach bool dst_modified = false;
37960083261SDirk Eibach uint32_t ins;
38060083261SDirk Eibach uint8_t opcode;
38160083261SDirk Eibach uint8_t src_spec;
38260083261SDirk Eibach uint8_t dst_spec;
38360083261SDirk Eibach uint16_t data_size;
38460083261SDirk Eibach struct h_reg *src_reg, *dst_reg;
38560083261SDirk Eibach uint8_t buf[20];
38660083261SDirk Eibach const uint8_t *src_buf, *data;
38760083261SDirk Eibach uint8_t *ptr;
38860083261SDirk Eibach int i;
38960083261SDirk Eibach void * (*bin_func)(void *, const void *, size_t);
39060083261SDirk Eibach
39160083261SDirk Eibach if (*code_size < 4)
39260083261SDirk Eibach return NULL;
39360083261SDirk Eibach
39460083261SDirk Eibach ins = get_unaligned_be32(*ip);
39560083261SDirk Eibach opcode = **ip;
39660083261SDirk Eibach data = *ip + 4;
39760083261SDirk Eibach src_spec = (ins >> 18) & 0x3f;
39860083261SDirk Eibach dst_spec = (ins >> 12) & 0x3f;
39960083261SDirk Eibach data_size = (ins & 0x7ff);
40060083261SDirk Eibach
40160083261SDirk Eibach debug("HRE: ins=%08x (op=%02x, s=%02x, d=%02x, L=%d)\n", ins,
40260083261SDirk Eibach opcode, src_spec, dst_spec, data_size);
40360083261SDirk Eibach
40460083261SDirk Eibach if ((opcode & 0x80) && (data_size + 4) > *code_size)
40560083261SDirk Eibach return NULL;
40660083261SDirk Eibach
407*abdc7b8aSSimon Glass src_reg = access_hreg(tpm, src_spec, HREG_RD);
40860083261SDirk Eibach if (hre_err || hre_tpm_err)
40960083261SDirk Eibach return NULL;
410*abdc7b8aSSimon Glass dst_reg = access_hreg(tpm, dst_spec,
411*abdc7b8aSSimon Glass (opcode & 0x40) ? HREG_RDWR : HREG_WR);
41260083261SDirk Eibach if (hre_err || hre_tpm_err)
41360083261SDirk Eibach return NULL;
41460083261SDirk Eibach
41560083261SDirk Eibach switch (opcode) {
41660083261SDirk Eibach case HRE_NOP:
41760083261SDirk Eibach goto end;
41860083261SDirk Eibach case HRE_CHECK0:
41960083261SDirk Eibach if (src_reg) {
42060083261SDirk Eibach for (i = 0; i < 20; ++i) {
42160083261SDirk Eibach if (src_reg->digest[i])
42260083261SDirk Eibach return NULL;
42360083261SDirk Eibach }
42460083261SDirk Eibach }
42560083261SDirk Eibach break;
42660083261SDirk Eibach case HRE_LOAD:
42760083261SDirk Eibach bin_func = memcpy;
42860083261SDirk Eibach goto do_bin_func;
42960083261SDirk Eibach case HRE_XOR:
43060083261SDirk Eibach bin_func = compute_xor;
43160083261SDirk Eibach goto do_bin_func;
43260083261SDirk Eibach case HRE_AND:
43360083261SDirk Eibach bin_func = compute_and;
43460083261SDirk Eibach goto do_bin_func;
43560083261SDirk Eibach case HRE_OR:
43660083261SDirk Eibach bin_func = compute_or;
43760083261SDirk Eibach goto do_bin_func;
43860083261SDirk Eibach case HRE_EXTEND:
43960083261SDirk Eibach bin_func = compute_extend;
44060083261SDirk Eibach do_bin_func:
44160083261SDirk Eibach if (!dst_reg)
44260083261SDirk Eibach return NULL;
44360083261SDirk Eibach if (src_reg) {
44460083261SDirk Eibach src_buf = src_reg->digest;
44560083261SDirk Eibach } else {
44660083261SDirk Eibach if (!data_size) {
44760083261SDirk Eibach memset(buf, 0, 20);
44860083261SDirk Eibach src_buf = buf;
44960083261SDirk Eibach } else if (data_size == 1) {
45060083261SDirk Eibach memset(buf, *data, 20);
45160083261SDirk Eibach src_buf = buf;
45260083261SDirk Eibach } else if (data_size >= 20) {
45360083261SDirk Eibach src_buf = data;
45460083261SDirk Eibach } else {
45560083261SDirk Eibach src_buf = buf;
45660083261SDirk Eibach for (ptr = (uint8_t *)src_buf, i = 20; i > 0;
45760083261SDirk Eibach i -= data_size, ptr += data_size)
45860083261SDirk Eibach memcpy(ptr, data,
45960083261SDirk Eibach min_t(size_t, i, data_size));
46060083261SDirk Eibach }
46160083261SDirk Eibach }
46260083261SDirk Eibach bin_func(dst_reg->digest, src_buf, 20);
46360083261SDirk Eibach dst_reg->valid = true;
46460083261SDirk Eibach dst_modified = true;
46560083261SDirk Eibach break;
46660083261SDirk Eibach case HRE_LOADKEY:
467*abdc7b8aSSimon Glass if (hre_op_loadkey(tpm, src_reg, dst_reg, data, data_size))
46860083261SDirk Eibach return NULL;
46960083261SDirk Eibach break;
47060083261SDirk Eibach default:
47160083261SDirk Eibach return NULL;
47260083261SDirk Eibach }
47360083261SDirk Eibach
47460083261SDirk Eibach if (dst_reg && dst_modified && IS_PCR_HREG(dst_spec)) {
475*abdc7b8aSSimon Glass hre_tpm_err = tpm_extend(tpm, HREG_IDX(dst_spec),
476*abdc7b8aSSimon Glass dst_reg->digest, dst_reg->digest);
47760083261SDirk Eibach if (hre_tpm_err) {
47860083261SDirk Eibach hre_err = HRE_E_TPM_FAILURE;
47960083261SDirk Eibach return NULL;
48060083261SDirk Eibach }
48160083261SDirk Eibach }
48260083261SDirk Eibach end:
48360083261SDirk Eibach *ip += 4;
48460083261SDirk Eibach *code_size -= 4;
48560083261SDirk Eibach if (opcode & 0x80) {
48660083261SDirk Eibach *ip += data_size;
48760083261SDirk Eibach *code_size -= data_size;
48860083261SDirk Eibach }
48960083261SDirk Eibach
49060083261SDirk Eibach return *ip;
49160083261SDirk Eibach }
49260083261SDirk Eibach
49360083261SDirk Eibach /**
49460083261SDirk Eibach * @brief runs a program on the hash register engine.
495*abdc7b8aSSimon Glass * @param tpm TPM device
49660083261SDirk Eibach * @param code pointer to the (HRE) code.
49760083261SDirk Eibach * @param code_size size of the code (in bytes).
49860083261SDirk Eibach * @return 0 on success, != 0 on failure.
49960083261SDirk Eibach */
hre_run_program(struct udevice * tpm,const uint8_t * code,size_t code_size)500*abdc7b8aSSimon Glass int hre_run_program(struct udevice *tpm, const uint8_t *code, size_t code_size)
50160083261SDirk Eibach {
50260083261SDirk Eibach size_t code_left;
50360083261SDirk Eibach const uint8_t *ip = code;
50460083261SDirk Eibach
50560083261SDirk Eibach code_left = code_size;
50660083261SDirk Eibach hre_tpm_err = 0;
50760083261SDirk Eibach hre_err = HRE_E_OK;
50860083261SDirk Eibach while (code_left > 0)
509*abdc7b8aSSimon Glass if (!hre_execute_op(tpm, &ip, &code_left))
51060083261SDirk Eibach return -1;
51160083261SDirk Eibach
51260083261SDirk Eibach return hre_err;
51360083261SDirk Eibach }
51460083261SDirk Eibach
hre_verify_program(struct key_program * prg)51560083261SDirk Eibach int hre_verify_program(struct key_program *prg)
51660083261SDirk Eibach {
51760083261SDirk Eibach uint32_t crc;
51860083261SDirk Eibach
51960083261SDirk Eibach crc = crc32(0, prg->code, prg->code_size);
52060083261SDirk Eibach
52160083261SDirk Eibach if (crc != prg->code_crc) {
52260083261SDirk Eibach printf("HRC crc mismatch: %08x != %08x\n",
52360083261SDirk Eibach crc, prg->code_crc);
52460083261SDirk Eibach return 1;
52560083261SDirk Eibach }
52660083261SDirk Eibach return 0;
52760083261SDirk Eibach }
528