coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/g/alerts.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 */