summaryrefslogtreecommitdiff
path: root/chip/g/loader/launch.c
blob: 077961fc673ee6ac461fb4603c83a28b4375cec7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/* Copyright 2015 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "dcrypto.h"
#include "debug_printf.h"
#include "key_ladder.h"
#include "registers.h"
#include "rom_flash.h"
#include "setup.h"
#include "signed_header.h"
#include "uart.h"
#include "verify.h"

static int unlockedForExecution(void)
{
	return GREAD_FIELD(GLOBALSEC, SB_COMP_STATUS, SB_BL_SIG_MATCH);
}

void _jump_to_address(const void *addr)
{
	REG32(GC_M3_VTOR_ADDR) = (unsigned)addr;  /* Set vector base. */

	__asm__ volatile("ldr sp, [%0]; \
			ldr pc, [%0, #4];"
			 : : "r"(addr)
			 : "memory");

	__builtin_unreachable();
}

void tryLaunch(uint32_t adr, size_t max_size)
{
	static struct {
		uint32_t img_hash[SHA256_DIGEST_WORDS];
		uint32_t fuses_hash[SHA256_DIGEST_WORDS];
		uint32_t info_hash[SHA256_DIGEST_WORDS];
	} hashes;
	static uint32_t hash[SHA256_DIGEST_WORDS];
	static uint32_t fuses[FUSE_MAX];
	static uint32_t info[INFO_MAX];
	int i;
	uint32_t major;
	const uint32_t FAKE_rom_hash[8] = {1, 2, 3, 4, 5, 6, 7, 8};
	const struct SignedHeader *hdr = (const struct SignedHeader *)(adr);

	memset(&hashes, 0, sizeof(hashes));

	/* Sanity check image header. */
	if (hdr->magic != -1)
		return;
	if (hdr->image_size > max_size)
		return;

	/* Sanity checks that image belongs at adr. */
	if (hdr->ro_base < adr)
		return;
	if (hdr->ro_max > adr + max_size)
		return;
	if (hdr->rx_base < adr)
		return;
	if (hdr->rx_max > adr + max_size)
		return;

	VERBOSE("considering image at 0x%8x\n", hdr);
	VERBOSE("image size 0x%8x\n", hdr->image_size);
	VERBOSE("hashing from 0x%8x to 0x%8x\n",
		&hdr->tag, adr + hdr->image_size);

	/* Setup candidate execution region 1 based on header information. */
	/* TODO: harden against glitching: multi readback, check? */
	GREG32(GLOBALSEC, CPU0_I_STAGING_REGION1_BASE_ADDR) = hdr->rx_base;
	GREG32(GLOBALSEC, CPU0_I_STAGING_REGION1_SIZE) =
		hdr->rx_max - hdr->rx_base - 1;
	GWRITE_FIELD(GLOBALSEC, CPU0_I_STAGING_REGION1_CTRL, EN, 1);
	GWRITE_FIELD(GLOBALSEC, CPU0_I_STAGING_REGION1_CTRL, RD_EN, 1);
	DCRYPTO_SHA256_hash((uint8_t *) &hdr->tag,
			hdr->image_size - offsetof(struct SignedHeader, tag),
			(uint8_t *) hashes.img_hash);

	VERBOSE("img_hash  : %ph\n", HEX_BUF(hashes.img_hash, 32));

	/* Sense fuses into RAM array; hash array. */
	/* TODO: is this glitch resistant enough? Certainly is simple.. */
	for (i = 0; i < FUSE_MAX; ++i)
		fuses[i] = FUSE_IGNORE;

	for (i = 0; i < FUSE_MAX; ++i) {
		/*
		 * For the fuses the header cares about, read their values
		 * into the map.
		 */
		if (hdr->fusemap[i>>5] & (1 << (i&31))) {
			/*
			 * BNK0_INTG_CHKSUM is the first fuse and as such the
			 * best reference to the base address of the fuse
			 * memory map.
			 */
			fuses[i] = GREG32_ADDR(FUSE, BNK0_INTG_CHKSUM)[i];
		}
	}

	DCRYPTO_SHA256_hash((uint8_t *) fuses, sizeof(fuses),
			(uint8_t *) hashes.fuses_hash);

	VERBOSE("fuses_hash: %ph\n", HEX_BUF(hashes.fuses_hash, 32));

	/* Sense info into RAM array; hash array. */
	for (i = 0; i < INFO_MAX; ++i)
		info[i] = INFO_IGNORE;

	for (i = 0; i < INFO_MAX; ++i) {
		if (hdr->infomap[i>>5] & (1 << (i&31))) {
			uint32_t val = 0;
			/* read 2nd bank of info */
			int retval = flash_info_read(i + INFO_MAX, &val);

			info[i] ^= val ^ retval;
		}
	}

	DCRYPTO_SHA256_hash((uint8_t *) info, sizeof(info),
			(uint8_t *) hashes.info_hash);
	VERBOSE("info_hash : %ph\n", HEX_BUF(hashes.info_hash, 32));

	/* Hash our set of hashes to get final hash. */
	DCRYPTO_SHA256_hash((uint8_t *) &hashes, sizeof(hashes),
			(uint8_t *) hash);

	/*
	 * Write measured hash to unlock register to try and unlock execution.
	 * This would match when doing warm-boot from suspend, so we can avoid
	 * the slow RSA verify.
	 */
	for (i = 0; i < SHA256_DIGEST_WORDS; ++i)
		GREG32_ADDR(GLOBALSEC, SB_BL_SIG0)[i] = hash[i];

	/*
	 * Unlock attempt. Value written is irrelevant, as long as something
	 * is written.
	 */
	GREG32(GLOBALSEC, SIG_UNLOCK) = 1;

	if (!unlockedForExecution()) {
		/* Assume warm-boot failed; do full RSA verify. */
		LOADERKEY_verify(&hdr->keyid, hdr->signature, hash);
		/*
		 * PWRDN_SCRATCH* should be write-locked, tied to successful
		 * SIG_MATCH. Thus ARM is only able to write this hash if
		 * signature was correct.
		 */
		for (i = 0; i < SHA256_DIGEST_WORDS; ++i)
			/* TODO: verify written values as glitch protection? */
			GREG32_ADDR(PMU, PWRDN_SCRATCH8)[i] = hash[i];
	}


	if (!unlockedForExecution()) {
		debug_printf("Failed to unlock for execution image at 0x%08x\n",
			     adr);
		return;
	}

	/*
	 * Write PMU_PWRDN_SCRATCH_LOCK1_OFFSET to lock against rewrites.
	 * TODO: glitch resist
	 */
	GREG32(PMU, PWRDN_SCRATCH_LOCK1) = 1;

	/*
	 * Drop software level to stop SIG_MATCH from future write-unlocks.
	 * TODO: glitch detect / verify?
	 */
	GREG32(GLOBALSEC, SOFTWARE_LVL) = 0x33;

	/* Write hdr->tag, hdr->epoch_ to KDF engine FWR[0..7] */
	for (i = 0; i < ARRAY_SIZE(hdr->tag); ++i)
		GREG32_ADDR(KEYMGR, HKEY_FWR0)[i] = hdr->tag[i];

	GREG32(KEYMGR, HKEY_FWR7) = hdr->epoch_;

	/* Crank keyladder */
	if (!(GREAD(FUSE, FLASH_PERSO_PAGE_LOCK) &
	      (GC_FUSE_HIK_CREATE_LOCK_VAL_MASK <<
	       GC_FUSE_HIK_CREATE_LOCK_VAL_LSB))) {
		VERBOSE("Re-reading INFO0\n");
		/*
		 * Needed because FUSE_FLASH_PERSO_PAGE_LOCK_OFFSET isn't
		 * blown) wipe out the flash secrets saved in keymgr and
		 * re-read info0
		 */
		GREG32(KEYMGR, FLASH_RCV_WIPE) = 1;
		GREG32(FLASH, FSH_ENABLE_INFO0_SHADOW_READ) = 1;
	}

	/* Turn up random stalls for SHA */
	GREG32(KEYMGR, SHA_RAND_STALL_CTL_FREQ) = 0;  /* 0:50% */

	major = hdr->major_;
	GREG32(KEYMGR, FW_MAJOR_VERSION) = major;

	/*
	 * Lock FWR (NOTE: needs to happen after writing major!) TODO: glitch
	 * protect?
	 */
	GREG32(KEYMGR, FWR_VLD) = 2;
	GREG32(KEYMGR, FWR_LOCK) = 1;

	key_ladder_step(40, FAKE_rom_hash);

	/* TODO: do cert #40 and lock in ROM? */
	GREG32(GLOBALSEC, HIDE_ROM) = 1;

	/* TODO: bump runlevel(s) according to signature header */
	/*
	 * Flash write protect entire image area (to guard signed blob)
	 * REGION0 protects boot_loader, use REGION1 to protect app
	 */
	GREG32(GLOBALSEC, FLASH_REGION1_BASE_ADDR) = adr;
	GREG32(GLOBALSEC, FLASH_REGION1_SIZE) = hdr->image_size - 1;
	GWRITE_FIELD(GLOBALSEC, FLASH_REGION1_CTRL, EN, 1);
	GWRITE_FIELD(GLOBALSEC, FLASH_REGION1_CTRL, RD_EN, 1);
	GWRITE_FIELD(GLOBALSEC, FLASH_REGION1_CTRL, WR_EN, 0);

	/* TODO: lock FLASH_REGION 1? */
	disarmRAMGuards();

	debug_printf("Valid image found at 0x%pP, jumping", hdr);
	uart_tx_flush();

	_jump_to_address(&hdr[1]);
}