diff options
-rw-r--r-- | board/cr50/board.h | 2 | ||||
-rw-r--r-- | chip/g/alerts.c | 364 | ||||
-rw-r--r-- | chip/g/board_id.h | 5 | ||||
-rw-r--r-- | chip/g/build.mk | 1 | ||||
-rw-r--r-- | include/config.h | 10 | ||||
-rw-r--r-- | include/tpm_vendor_cmds.h | 1 |
6 files changed, 383 insertions, 0 deletions
diff --git a/board/cr50/board.h b/board/cr50/board.h index e76caed5dc..d25314170d 100644 --- a/board/cr50/board.h +++ b/board/cr50/board.h @@ -360,4 +360,6 @@ enum nvmem_users { 0xdb, 0xf4, 0x79, 0x5f, 0x8a, 0x0f, 0x28, 0x3f} #define CONFIG_RMA_AUTH_SERVER_KEY_ID 0x10 +#define CONFIG_ENABLE_H1_ALERTS + #endif /* __CROS_EC_BOARD_H */ 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 */ diff --git a/chip/g/board_id.h b/chip/g/board_id.h index 6aa106d5b0..dda2302c14 100644 --- a/chip/g/board_id.h +++ b/chip/g/board_id.h @@ -47,6 +47,11 @@ uint32_t check_board_id_vs_header(const struct board_id *id, int read_board_id(struct board_id *id); /** + * Return the image header for the current image copy + */ +const struct SignedHeader *get_current_image_header(void); + +/** * Check if board ID in the image matches board ID field in the INFO1. * * Pass the pointer to the image header to check. If the pointer is set to diff --git a/chip/g/build.mk b/chip/g/build.mk index 2b41ca42c6..c2a5bd360c 100644 --- a/chip/g/build.mk +++ b/chip/g/build.mk @@ -67,6 +67,7 @@ chip-y+= jitter.o chip-y+= pmu.o chip-y+= trng.o chip-y+= runlevel.o +chip-$(CONFIG_ENABLE_H1_ALERTS)+= alerts.o chip-$(CONFIG_USB_FW_UPDATE)+= usb_upgrade.o chip-$(CONFIG_NON_HC_FW_UPDATE)+= upgrade_fw.o post_reset.o upgrade.o chip-$(CONFIG_SPS)+= sps.o diff --git a/include/config.h b/include/config.h index c6a57bcf13..b47b1e9892 100644 --- a/include/config.h +++ b/include/config.h @@ -968,6 +968,16 @@ */ #undef CONFIG_IGNORE_G_UPDATE_CHECKS +/* + * When enabled hardware alerts statistics provided via VendorCommand extension. + */ +#undef CONFIG_ENABLE_H1_ALERTS + +/* + * Enable console shell command 'alerts' that prints chip alerts statistics. + */ +#undef CONFIG_ENABLE_H1_ALERTS_CONSOLE + /*****************************************************************************/ /* * Debugging config diff --git a/include/tpm_vendor_cmds.h b/include/tpm_vendor_cmds.h index b61ca2623c..09d578493f 100644 --- a/include/tpm_vendor_cmds.h +++ b/include/tpm_vendor_cmds.h @@ -50,6 +50,7 @@ enum vendor_cmd_cc { VENDOR_CC_DISABLE_RMA = 32, VENDOR_CC_MANAGE_CCD_PWD = 33, VENDOR_CC_CCD = 34, + VENDOR_CC_GET_ALERTS_DATA = 35, LAST_VENDOR_COMMAND = 65535, }; |