diff options
Diffstat (limited to 'chip/g/alerts.c')
-rw-r--r-- | chip/g/alerts.c | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/chip/g/alerts.c b/chip/g/alerts.c new file mode 100644 index 0000000000..6cfb936b9c --- /dev/null +++ b/chip/g/alerts.c @@ -0,0 +1,364 @@ +/* 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] & (1 << 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 & (1 << 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 */ |