summaryrefslogtreecommitdiff
path: root/chip/g/alerts.c
blob: 7396248fc02d152e7d672f5af9b77f8cb3c25c01 (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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
/* Copyright 2018 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "board_id.h"
#include "common.h"
#include "console.h"
#include "endian.h"
#include "extension.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
#include "signed_header.h"
#include "task.h"
#include "tpm_vendor_cmds.h"

#define BROM_FWBIT_APPLYSEC_SC300       0
#define BROM_FWBIT_APPLYSEC_CAMO        1
#define BROM_FWBIT_APPLYSEC_BUSERR      2
#define BROM_FWBIT_APPLYSEC_BUSOBF      3
#define BROM_FWBIT_APPLYSEC_HEARTBEAT   4
#define BROM_FWBIT_APPLYSEC_BATMON      5
#define BROM_FWBIT_APPLYSEC_RTCCHECK    6
#define BROM_FWBIT_APPLYSEC_JITTERY     7
#define BROM_FWBIT_APPLYSEC_TRNG        8
#define BROM_FWBIT_APPLYSEC_VOLT        9
#define BROM_FWBIT_APPLYSEC_NOB5        10
#define BROM_FWBIT_APPLYSEC_UNKNOWN     11

struct alert_desc {
	const char *name;
	const uint8_t fuse; // BROM_FWBIT_APPLYSEC_* fuse that gates the alert
};

// These numbers correspond to index at 'alert_counters/alert_descs' arrays
#define ALERT_NUM_CAMO0_BREACH 0
#define ALERT_NUM_CRYPTO0_DMEM_PARITY 1
#define ALERT_NUM_CRYPTO0_DRF_PARITY 2
#define ALERT_NUM_CRYPTO0_IMEM_PARITY 3
#define ALERT_NUM_CRYPTO0_PGM_FAULT 4
#define ALERT_NUM_DBCTRL_CPU0_D_IF_BUS_ERR 5
#define ALERT_NUM_DBCTRL_CPU0_D_IF_UPDATE_WATCHDOG 6
#define ALERT_NUM_DBCTRL_CPU0_I_IF_BUS_ERR 7
#define ALERT_NUM_DBCTRL_CPU0_I_IF_UPDATE_WATCHDOG 8
#define ALERT_NUM_DBCTRL_CPU0_S_IF_BUS_ERR 9
#define ALERT_NUM_DBCTRL_CPU0_S_IF_UPDATE_WATCHDOG 10
#define ALERT_NUM_DBCTRL_DDMA0_IF_BUS_ERR 11
#define ALERT_NUM_DBCTRL_DDMA0_IF_UPDATE_WATCHDOG 12
#define ALERT_NUM_DBCTRL_DSPS0_IF_BUS_ERR 13
#define ALERT_NUM_DBCTRL_DSPS0_IF_UPDATE_WATCHDOG 14
#define ALERT_NUM_DBCTRL_DUSB0_IF_BUS_ERR 15
#define ALERT_NUM_DBCTRL_DUSB0_IF_UPDATE_WATCHDOG 16
#define ALERT_NUM_FUSE0_FUSE_DEFAULTS 17
#define ALERT_NUM_GLOBALSEC_DIFF_FAIL 18
#define ALERT_NUM_GLOBALSEC_FW0 19
#define ALERT_NUM_GLOBALSEC_FW1 20
#define ALERT_NUM_GLOBALSEC_FW2 21
#define ALERT_NUM_GLOBALSEC_FW3 22
#define ALERT_NUM_GLOBALSEC_HEARTBEAT_FAIL 23
#define ALERT_NUM_GLOBALSEC_PROC_OPCODE_HASH 24
#define ALERT_NUM_GLOBALSEC_SRAM_PARITY_SCRUB 25
#define ALERT_NUM_KEYMGR0_AES_EXEC_CTR_MAX 26
#define ALERT_NUM_KEYMGR0_AES_HKEY 27
#define ALERT_NUM_KEYMGR0_CERT_LOOKUP 28
#define ALERT_NUM_KEYMGR0_FLASH_ENTRY 29
#define ALERT_NUM_KEYMGR0_PW 30
#define ALERT_NUM_KEYMGR0_SHA_EXEC_CTR_MAX 31
#define ALERT_NUM_KEYMGR0_SHA_FAULT 32
#define ALERT_NUM_KEYMGR0_SHA_HKEY 33
#define ALERT_NUM_PMU_BATTERY_MON 34
#define ALERT_NUM_PMU_PMU_WDOG 35
#define ALERT_NUM_RTC0_RTC_DEAD 36
#define ALERT_NUM_TEMP0_MAX_TEMP 37
#define ALERT_NUM_TEMP0_MAX_TEMP_DIFF 38
#define ALERT_NUM_TEMP0_MIN_TEMP 39
#define ALERT_NUM_TRNG0_OUT_OF_SPEC 40
#define ALERT_NUM_TRNG0_TIMEOUT 41
#define ALERT_NUM_VOLT0_VOLT_ERR 42
#define ALERT_NUM_XO0_JITTERY_TRIM_DIS 43

#define ALERTS_NUM 44

uint16_t alert_counters[ALERTS_NUM];

static void alerts_init(void)
{
	int irq;

	// enable every single IRQ for globalsec alerts
	for (irq = GC_IRQNUM_GLOBALSEC_CAMO0_BREACH_ALERT_INT;
		irq <= GC_IRQNUM_GLOBALSEC_XO0_JITTERY_TRIM_DIS_ALERT_INT;
		irq++) {
		task_enable_irq(irq);
	}
}
DECLARE_HOOK(HOOK_INIT, alerts_init, HOOK_PRIO_DEFAULT);

volatile uint32_t *INTR_STATUS_ADDR[] = {
	GREG32_ADDR(GLOBALSEC, ALERT_INTR_STS0),
	GREG32_ADDR(GLOBALSEC, ALERT_INTR_STS1),
};
BUILD_ASSERT(ARRAY_SIZE(INTR_STATUS_ADDR) * 32 >= ALERTS_NUM);

static void alert_intr_clear(int alert)
{
	int reg = alert / 32;
	int offset = alert % 32;

	*INTR_STATUS_ADDR[reg] = 1 << offset;
}

static void alert_interrupt_process(int alert)
{
	alert_counters[alert]++;
	alert_intr_clear(alert);
}

#define GLOBALSEC_ALERT_COUNTER(name) \
	DECLARE_IRQ(GC_IRQNUM_GLOBALSEC_##name##_ALERT_INT, handler_##name, 1); \
	void handler_##name(void) \
	{ \
		alert_interrupt_process(ALERT_NUM_##name); \
	}

GLOBALSEC_ALERT_COUNTER(CAMO0_BREACH);
GLOBALSEC_ALERT_COUNTER(CRYPTO0_DMEM_PARITY);
GLOBALSEC_ALERT_COUNTER(CRYPTO0_DRF_PARITY);
GLOBALSEC_ALERT_COUNTER(CRYPTO0_IMEM_PARITY);
GLOBALSEC_ALERT_COUNTER(CRYPTO0_PGM_FAULT);
GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_D_IF_BUS_ERR);
GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_D_IF_UPDATE_WATCHDOG);
GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_I_IF_BUS_ERR);
GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_I_IF_UPDATE_WATCHDOG);
GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_S_IF_BUS_ERR);
GLOBALSEC_ALERT_COUNTER(DBCTRL_CPU0_S_IF_UPDATE_WATCHDOG);
GLOBALSEC_ALERT_COUNTER(DBCTRL_DDMA0_IF_BUS_ERR);
GLOBALSEC_ALERT_COUNTER(DBCTRL_DDMA0_IF_UPDATE_WATCHDOG);
GLOBALSEC_ALERT_COUNTER(DBCTRL_DSPS0_IF_BUS_ERR);
GLOBALSEC_ALERT_COUNTER(DBCTRL_DSPS0_IF_UPDATE_WATCHDOG);
GLOBALSEC_ALERT_COUNTER(DBCTRL_DUSB0_IF_BUS_ERR);
GLOBALSEC_ALERT_COUNTER(DBCTRL_DUSB0_IF_UPDATE_WATCHDOG);
GLOBALSEC_ALERT_COUNTER(FUSE0_FUSE_DEFAULTS);
GLOBALSEC_ALERT_COUNTER(GLOBALSEC_DIFF_FAIL);
GLOBALSEC_ALERT_COUNTER(GLOBALSEC_FW0);
GLOBALSEC_ALERT_COUNTER(GLOBALSEC_FW1);
GLOBALSEC_ALERT_COUNTER(GLOBALSEC_FW2);
GLOBALSEC_ALERT_COUNTER(GLOBALSEC_FW3);
GLOBALSEC_ALERT_COUNTER(GLOBALSEC_HEARTBEAT_FAIL);
GLOBALSEC_ALERT_COUNTER(GLOBALSEC_PROC_OPCODE_HASH);
GLOBALSEC_ALERT_COUNTER(GLOBALSEC_SRAM_PARITY_SCRUB);
GLOBALSEC_ALERT_COUNTER(KEYMGR0_AES_EXEC_CTR_MAX);
GLOBALSEC_ALERT_COUNTER(KEYMGR0_AES_HKEY);
GLOBALSEC_ALERT_COUNTER(KEYMGR0_CERT_LOOKUP);
GLOBALSEC_ALERT_COUNTER(KEYMGR0_FLASH_ENTRY);
GLOBALSEC_ALERT_COUNTER(KEYMGR0_PW);
GLOBALSEC_ALERT_COUNTER(KEYMGR0_SHA_EXEC_CTR_MAX);
GLOBALSEC_ALERT_COUNTER(KEYMGR0_SHA_FAULT);
GLOBALSEC_ALERT_COUNTER(KEYMGR0_SHA_HKEY);
GLOBALSEC_ALERT_COUNTER(PMU_BATTERY_MON);
GLOBALSEC_ALERT_COUNTER(PMU_PMU_WDOG);
GLOBALSEC_ALERT_COUNTER(RTC0_RTC_DEAD);
GLOBALSEC_ALERT_COUNTER(TEMP0_MAX_TEMP);
GLOBALSEC_ALERT_COUNTER(TEMP0_MAX_TEMP_DIFF);
GLOBALSEC_ALERT_COUNTER(TEMP0_MIN_TEMP);
GLOBALSEC_ALERT_COUNTER(TRNG0_OUT_OF_SPEC);
GLOBALSEC_ALERT_COUNTER(TRNG0_TIMEOUT);
GLOBALSEC_ALERT_COUNTER(VOLT0_VOLT_ERR);
GLOBALSEC_ALERT_COUNTER(XO0_JITTERY_TRIM_DIS);

#define ALERTS_FORMAT_HAVEN 1

struct vc_alerts_data {
	uint16_t version_id;
	uint16_t alerts_num;
	uint16_t counters[ALERTS_NUM];
} __packed;

static enum vendor_cmd_rc vc_get_alerts_data(enum vendor_cmd_cc code,
	void *buf, size_t input_size, size_t *response_size)
{
	int i;
	struct vc_alerts_data *resp = buf;

	if (sizeof(struct vc_alerts_data) > *response_size)
		return VENDOR_RC_RESPONSE_TOO_BIG;

	memset(resp, 0, sizeof(struct vc_alerts_data));
	resp->version_id = htobe16(ALERTS_FORMAT_HAVEN);
	resp->alerts_num = htobe16(ALERTS_NUM);
	for (i = 0; i < ALERTS_NUM; i++) {
		// Most of alert_counters[i] will be zero. We want to avoid
		// disabling IRQ thus check counters with IRQ enabled.
		if (alert_counters[i]) {
			interrupt_disable();
			resp->counters[i] = htobe16(alert_counters[i]);
			alert_counters[i] = 0;
			interrupt_enable();
		}
	}

	*response_size = sizeof(struct vc_alerts_data);

	return VENDOR_RC_SUCCESS;
}
DECLARE_VENDOR_COMMAND(VENDOR_CC_GET_ALERTS_DATA, vc_get_alerts_data);

#ifdef CONFIG_ENABLE_H1_ALERTS_CONSOLE

const struct alert_desc alert_descs[] = {
	{ "camo0/breach", BROM_FWBIT_APPLYSEC_CAMO },
	{ "crypto0/dmem_parity", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "crypto0/drf_parity", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "crypto0/imem_parity", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "crypto0/pgm_fault", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "dbctrl_cpu0_D_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
	{ "dbctrl_cpu0_D_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
	{ "dbctrl_cpu0_I_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
	{ "dbctrl_cpu0_I_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
	{ "dbctrl_cpu0_S_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
	{ "dbctrl_cpu0_S_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
	{ "dbctrl_ddma0_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
	{ "dbctrl_ddma0_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
	{ "dbctrl_dsps0_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
	{ "dbctrl_dsps0_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
	{ "dbctrl_dusb0_if/bus_err", BROM_FWBIT_APPLYSEC_BUSERR },
	{ "dbctrl_dusb0_if/update_watchdog", BROM_FWBIT_APPLYSEC_BUSOBF },
	{ "fuse0/fuse_defaults", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "globalsec/diff_fail", BROM_FWBIT_APPLYSEC_HEARTBEAT },
	{ "globalsec/fw0", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "globalsec/fw1", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "globalsec/fw2", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "globalsec/fw3", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "globalsec/heartbeat_fail", BROM_FWBIT_APPLYSEC_HEARTBEAT },
	{ "globalsec/proc_opcode_hash", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "globalsec/sram_parity_scrub", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "keymgr0/aes_exec_ctr_max", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "keymgr0/aes_hkey", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "keymgr0/cert_lookup", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "keymgr0/flash_entry", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "keymgr0/pw", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "keymgr0/sha_exec_ctr_max", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "keymgr0/sha_fault", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "keymgr0/sha_hkey", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "pmu/battery_mon", BROM_FWBIT_APPLYSEC_BATMON },
	{ "pmu/pmu_wdog", BROM_FWBIT_APPLYSEC_HEARTBEAT },
	{ "rtc0/rtc_dead", BROM_FWBIT_APPLYSEC_RTCCHECK },
	{ "temp0/max_temp", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "temp0/max_temp_diff", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "temp0/min_temp", BROM_FWBIT_APPLYSEC_UNKNOWN },
	{ "trng0/out_of_spec", BROM_FWBIT_APPLYSEC_TRNG },
	{ "trng0/timeout", BROM_FWBIT_APPLYSEC_TRNG },
	{ "volt0/volt_err", BROM_FWBIT_APPLYSEC_VOLT },
	{ "xo0/jittery_trim_dis", BROM_FWBIT_APPLYSEC_JITTERY },
};
BUILD_ASSERT(ARRAY_SIZE(alert_descs) == ALERTS_NUM);

static int alert_intr_status(int alert)
{
	int reg = alert / 32;
	int offset = alert % 32;

	return !!(*INTR_STATUS_ADDR[reg] & BIT(offset));
}

#ifdef CONFIG_BOARD_ID_SUPPORT
static uint32_t fuse_enabled(void)
{
	uint32_t fuses = GR_FUSE(FW_DEFINED_BROM_APPLYSEC);
	// get_current_image_header() is defined in board_id.c and available
	// only when CONFIG_BOARD_ID_SUPPORT is enabled
	const struct SignedHeader *hdr = get_current_image_header();

	return fuses & hdr->applysec_;
}
#else /* CONFIG_BOARD_ID_SUPPORT */
static uint32_t fuse_enabled(void)
{
	return GR_FUSE(FW_DEFINED_BROM_APPLYSEC);
}
#endif /* CONFIG_BOARD_ID_SUPPORT */

static void command_alerts_list(void)
{
	int i;
	uint32_t fuses = fuse_enabled();

	ccprintf("Globalsec alerts status\nColumns:\n"
		" * name\n"
		" * fuse state: '?' - not defined, '#' disabled, '+' enabled\n"
		" * interrupt state\n"
		" * alert counter\n");

	for (i = 0; i < ALERTS_NUM; i++) {
		const char *name = alert_descs[i].name;
		char fuse_status;

		int status = alert_intr_status(i);
		int8_t fuse = alert_descs[i].fuse;

		if (fuse == BROM_FWBIT_APPLYSEC_UNKNOWN)
			fuse_status = '?';
		else if (fuses & BIT(fuse))
			fuse_status = '+';
		else
			fuse_status = '#';

		ccprintf("%32s %c %d %d\n", name, fuse_status, status,
			alert_counters[i]);
		cflush();
	}
}

/* Fire a software enabled alert */
static void command_alerts_fire(int interrupt)
{
	int i = 0;
	int value = 0;

	for (i = 3; i >= 0; i--) {
		/* Trigger register consists of four 2-bit fields.
		 * pair 01 triggers the alerts, pair 10 does not trigger
		 */
		value <<= 2;
		value |= (i == interrupt) ? 1 : 2;
	}
	GWRITE(GLOBALSEC, ALERT_FW_TRIGGER, value); // firing FW-N irq
	GWRITE(GLOBALSEC, ALERT_FW_TRIGGER, 0xaa); // back to normal
}

static int command_alerts(int argc, char **argv)
{
	char *e;

	if (argc == 1) {
		command_alerts_list();
		return EC_SUCCESS;
	}

	if (argc == 3) {
		if (!strcasecmp(argv[1], "fire")) {
			int alert = strtoi(argv[2], &e, 10);

			if (*e || alert < 0 || alert > 3) {
				ccprintf("interrupt number must be in range "
					"[0..3]\n");
				return EC_ERROR_PARAM2;
			}

			command_alerts_fire(alert);
			return EC_SUCCESS;
		}

		return EC_ERROR_PARAM1;
	}

	return EC_ERROR_PARAM_COUNT;
}

DECLARE_CONSOLE_COMMAND(alerts, command_alerts,
	"<|fire [INT]>",
	"View/change alerts status");

#endif /* CONFIG_ENABLE_H1_ALERTS_CONSOLE */