365 lines
12 KiB
C
365 lines
12 KiB
C
/* Copyright 2018 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 "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 */
|