summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/acpi/Kconfig6
-rw-r--r--src/acpi/acpi.c148
-rw-r--r--src/include/acpi/acpi.h130
3 files changed, 283 insertions, 1 deletions
diff --git a/src/acpi/Kconfig b/src/acpi/Kconfig
index 11e703c606..9ffd7a93ad 100644
--- a/src/acpi/Kconfig
+++ b/src/acpi/Kconfig
@@ -38,6 +38,12 @@ config ACPI_NO_PCAT_8259
help
Selected by platforms that don't expose a PC/AT 8259 PIC pair.
+config ACPI_EINJ
+ def_bool n
+ depends on HAVE_ACPI_TABLES
+ help
+ This variable provides control for ACPI error injection table (EINJ)
+
config HAVE_ACPI_TABLES
bool
help
diff --git a/src/acpi/acpi.c b/src/acpi/acpi.c
index 64af505941..5c3fe13614 100644
--- a/src/acpi/acpi.c
+++ b/src/acpi/acpi.c
@@ -785,6 +785,152 @@ void acpi_create_hpet(acpi_hpet_t *hpet)
header->checksum = acpi_checksum((void *)hpet, sizeof(acpi_hpet_t));
}
+/*
+ * This method adds the ACPI error injection capability. It fills the default information.
+ * HW dependent code (caller) can modify the defaults upon return. If no changes are necessary
+ * and the defaults are acceptable then caller can simply add the table (acpi_add_table).
+ * INPUTS:
+ * einj - ptr to the starting location of EINJ table
+ * actions - number of actions to trigger an error (HW dependent)
+ * addr - address of trigger action table. This should be ACPI reserved memory and it will be
+ * shared between OS and FW.
+ */
+void acpi_create_einj(acpi_einj_t *einj, uintptr_t addr, u8 actions)
+{
+ int i;
+ acpi_header_t *header = &(einj->header);
+ acpi_injection_header_t *inj_header = &(einj->inj_header);
+ acpi_einj_smi_t *einj_smi = (acpi_einj_smi_t *)addr;
+ acpi_einj_trigger_table_t *tat;
+ if (!header)
+ return;
+
+ printk(BIOS_DEBUG, "%s einj_smi = %p\n", __func__, einj_smi);
+ memset(einj_smi, 0, sizeof(acpi_einj_smi_t));
+ tat = (acpi_einj_trigger_table_t *)(einj_smi + sizeof(acpi_einj_smi_t));
+ tat->header_size = 16;
+ tat->revision = 0;
+ tat->table_size = sizeof(acpi_einj_trigger_table_t) +
+ sizeof(acpi_einj_action_table_t) * actions - 1;
+ tat->entry_count = actions;
+ printk(BIOS_DEBUG, "%s trigger_action_table = %p\n", __func__, tat);
+
+ for (i = 0; i < actions; i++) {
+ tat->trigger_action[i].action = TRIGGER_ERROR;
+ tat->trigger_action[i].instruction = NO_OP;
+ tat->trigger_action[i].flags = FLAG_IGNORE;
+ tat->trigger_action[i].reg.space_id = ACPI_ADDRESS_SPACE_MEMORY;
+ tat->trigger_action[i].reg.bit_width = 64;
+ tat->trigger_action[i].reg.bit_offset = 0;
+ tat->trigger_action[i].reg.access_size = ACPI_ACCESS_SIZE_QWORD_ACCESS;
+ tat->trigger_action[i].reg.addr = 0;
+ tat->trigger_action[i].value = 0;
+ tat->trigger_action[i].mask = 0xFFFFFFFF;
+ }
+
+ acpi_einj_action_table_t default_actions[ACTION_COUNT] = {
+ [0] = {
+ .action = BEGIN_INJECT_OP,
+ .instruction = WRITE_REGISTER_VALUE,
+ .flags = FLAG_PRESERVE,
+ .reg = EINJ_REG_MEMORY((u64)(uintptr_t)&einj_smi->op_state),
+ .value = 0,
+ .mask = 0xFFFFFFFF
+ },
+ [1] = {
+ .action = GET_TRIGGER_ACTION_TABLE,
+ .instruction = READ_REGISTER,
+ .flags = FLAG_IGNORE,
+ .reg = EINJ_REG_MEMORY((u64)(uintptr_t)&einj_smi->trigger_action_table),
+ .value = 0,
+ .mask = 0xFFFFFFFFFFFFFFFF
+ },
+ [2] = {
+ .action = SET_ERROR_TYPE,
+ .instruction = WRITE_REGISTER,
+ .flags = FLAG_PRESERVE,
+ .reg = EINJ_REG_MEMORY((u64)(uintptr_t)&einj_smi->err_inject[0]),
+ .value = 0,
+ .mask = 0xFFFFFFFF
+ },
+ [3] = {
+ .action = GET_ERROR_TYPE,
+ .instruction = READ_REGISTER,
+ .flags = FLAG_IGNORE,
+ .reg = EINJ_REG_MEMORY((u64)(uintptr_t)&einj_smi->err_inj_cap),
+ .value = 0,
+ .mask = 0xFFFFFFFF
+ },
+ [4] = {
+ .action = END_INJECT_OP,
+ .instruction = WRITE_REGISTER_VALUE,
+ .flags = FLAG_PRESERVE,
+ .reg = EINJ_REG_MEMORY((u64)(uintptr_t)&einj_smi->op_state),
+ .value = 0,
+ .mask = 0xFFFFFFFF
+
+ },
+ [5] = {
+ .action = EXECUTE_INJECT_OP,
+ .instruction = WRITE_REGISTER_VALUE,
+ .flags = FLAG_PRESERVE,
+ .reg = EINJ_REG_IO(),
+ .value = 0x9a,
+ .mask = 0xFFFF,
+ },
+ [6] = {
+ .action = CHECK_BUSY_STATUS,
+ .instruction = READ_REGISTER_VALUE,
+ .flags = FLAG_IGNORE,
+ .reg = EINJ_REG_MEMORY((u64)(uintptr_t)&einj_smi->op_status),
+ .value = 1,
+ .mask = 1,
+ },
+ [7] = {
+ .action = GET_CMD_STATUS,
+ .instruction = READ_REGISTER,
+ .flags = FLAG_PRESERVE,
+ .reg = EINJ_REG_MEMORY((u64)(uintptr_t)&einj_smi->cmd_sts),
+ .value = 0,
+ .mask = 0x1fe,
+ },
+ [8] = {
+ .action = SET_ERROR_TYPE_WITH_ADDRESS,
+ .instruction = WRITE_REGISTER,
+ .flags = FLAG_PRESERVE,
+ .reg = EINJ_REG_MEMORY((u64)(uintptr_t)&einj_smi->setaddrtable),
+ .value = 1,
+ .mask = 0xffffffff
+ }
+ };
+
+ einj_smi->err_inj_cap = ACPI_EINJ_DEFAULT_CAP;
+ einj_smi->trigger_action_table = (u64) (uintptr_t)tat;
+
+ for (i = 0; i < ACTION_COUNT; i++)
+ printk(BIOS_DEBUG, "default_actions[%d].reg.addr is %llx\n", i,
+ default_actions[i].reg.addr);
+
+ memset((void *)einj, 0, sizeof(einj));
+
+ /* Fill out header fields. */
+ memcpy(header->signature, "EINJ", 4);
+ memcpy(header->oem_id, OEM_ID, 6);
+ memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
+ memcpy(header->asl_compiler_id, ASLC, 4);
+
+ header->asl_compiler_revision = asl_revision;
+ header->length = sizeof(acpi_einj_t);
+ header->revision = 1;
+ inj_header->einj_header_size = sizeof(acpi_injection_header_t);
+ inj_header->entry_count = ACTION_COUNT;
+
+ printk(BIOS_DEBUG, "%s einj->action_table = %p\n",
+ __func__, einj->action_table);
+ memcpy((void *)einj->action_table, (void *)default_actions, sizeof(einj->action_table));
+ header->checksum = acpi_checksum((void *)einj, sizeof(einj));
+}
+
void acpi_create_vfct(const struct device *device,
acpi_vfct_t *vfct,
unsigned long (*acpi_fill_vfct)(const struct device *device,
@@ -1756,6 +1902,8 @@ int get_acpi_table_revision(enum acpi_tables table)
return 1;
case RSDP: /* ACPI 2.0 upto 6.3: 2 */
return 2;
+ case EINJ:
+ return 1;
case HEST:
return 1;
case NHLT:
diff --git a/src/include/acpi/acpi.h b/src/include/acpi/acpi.h
index f2d2ad93cf..b266acf1e2 100644
--- a/src/include/acpi/acpi.h
+++ b/src/include/acpi/acpi.h
@@ -71,7 +71,7 @@ enum coreboot_acpi_ids {
enum acpi_tables {
/* Tables defined by ACPI and used by coreboot */
- BERT, DBG2, DMAR, DSDT, FACS, FADT, HEST, HPET, IVRS, MADT, MCFG,
+ BERT, DBG2, DMAR, DSDT, EINJ, FACS, FADT, HEST, HPET, IVRS, MADT, MCFG,
RSDP, RSDT, SLIT, SRAT, SSDT, TCPA, TPM2, XSDT, ECDT, LPIT,
/* Additional proprietary tables used by coreboot */
VFCT, NHLT, SPMI, CRAT
@@ -980,6 +980,134 @@ struct acpi_spmi {
u8 reserved3;
} __packed;
+/* EINJ APEI Standard Definitions */
+/* EINJ Error Types
+ Refer to the ACPI spec, EINJ section, for more info on bit definitions
+*/
+#define ACPI_EINJ_CPU_CE (1 << 0)
+#define ACPI_EINJ_CPU_UCE (1 << 1)
+#define ACPI_EINJ_CPU_UCE_FATAL (1 << 2)
+#define ACPI_EINJ_MEM_CE (1 << 3)
+#define ACPI_EINJ_MEM_UCE (1 << 4)
+#define ACPI_EINJ_MEM_UCE_FATAL (1 << 5)
+#define ACPI_EINJ_PCIE_CE (1 << 6)
+#define ACPI_EINJ_PCIE_UCE_NON_FATAL (1 << 7)
+#define ACPI_EINJ_PCIE_UCE_FATAL (1 << 8)
+#define ACPI_EINJ_PLATFORM_CE (1 << 9)
+#define ACPI_EINJ_PLATFORM_UCE (1 << 10)
+#define ACPI_EINJ_PLATFORM_UCE_FATAL (1 << 11)
+#define ACPI_EINJ_VENDOR_DEFINED (1 << 31)
+#define ACPI_EINJ_DEFAULT_CAP (ACPI_EINJ_MEM_CE | ACPI_EINJ_MEM_UCE | \
+ ACPI_EINJ_PCIE_CE | ACPI_EINJ_PCIE_UCE_FATAL)
+
+/* EINJ actions */
+#define ACTION_COUNT 9
+#define BEGIN_INJECT_OP 0x00
+#define GET_TRIGGER_ACTION_TABLE 0x01
+#define SET_ERROR_TYPE 0x02
+#define GET_ERROR_TYPE 0x03
+#define END_INJECT_OP 0x04
+#define EXECUTE_INJECT_OP 0x05
+#define CHECK_BUSY_STATUS 0x06
+#define GET_CMD_STATUS 0x07
+#define SET_ERROR_TYPE_WITH_ADDRESS 0x08
+#define TRIGGER_ERROR 0xFF
+
+/* EINJ Instructions */
+#define READ_REGISTER 0x00
+#define READ_REGISTER_VALUE 0x01
+#define WRITE_REGISTER 0x02
+#define WRITE_REGISTER_VALUE 0x03
+#define NO_OP 0x04
+
+/* EINJ (Error Injection Table) */
+typedef struct acpi_gen_regaddr1 {
+ u8 space_id; /* Address space ID */
+ u8 bit_width; /* Register size in bits */
+ u8 bit_offset; /* Register bit offset */
+ u8 access_size; /* Access size since ACPI 2.0c */
+ u64 addr; /* Register address */
+} __packed acpi_addr64_t;
+
+/* Instruction entry */
+typedef struct acpi_einj_action_table {
+ u8 action;
+ u8 instruction;
+ u16 flags;
+ acpi_addr64_t reg;
+ u64 value;
+ u64 mask;
+} __packed acpi_einj_action_table_t;
+
+typedef struct acpi_injection_header {
+ u32 einj_header_size;
+ u32 flags;
+ u32 entry_count;
+} __packed acpi_injection_header_t;
+
+typedef struct acpi_einj_trigger_table {
+ u32 header_size;
+ u32 revision;
+ u32 table_size;
+ u32 entry_count;
+ acpi_einj_action_table_t trigger_action[1];
+} __packed acpi_einj_trigger_table_t;
+
+typedef struct set_error_type {
+ u32 errtype;
+ u32 vendorerrortype;
+ u32 flags;
+ u32 apicid;
+ u64 memaddr;
+ u64 memrange;
+ u32 pciesbdf;
+} __packed set_error_type_t;
+
+#define EINJ_PARAM_NUM 6
+typedef struct acpi_einj_smi {
+ u64 op_state;
+ u64 err_inject[EINJ_PARAM_NUM];
+ u64 trigger_action_table;
+ u64 err_inj_cap;
+ u64 op_status;
+ u64 cmd_sts;
+ u64 einj_addr;
+ u64 einj_addr_msk;
+ set_error_type_t setaddrtable;
+ u64 reserved[50];
+} __packed acpi_einj_smi_t;
+
+/* EINJ Flags */
+#define EINJ_DEF_TRIGGER_PORT 0xb2
+#define FLAG_PRESERVE 0x01
+#define FLAG_IGNORE 0x00
+
+/* EINJ Registers */
+#define EINJ_REG_MEMORY(address) \
+ { \
+ .space_id = ACPI_ADDRESS_SPACE_MEMORY, \
+ .bit_width = 64, \
+ .bit_offset = 0, \
+ .access_size = ACPI_ACCESS_SIZE_QWORD_ACCESS, \
+ .addr = address}
+
+#define EINJ_REG_IO() \
+ { \
+ .space_id = ACPI_ADDRESS_SPACE_IO, \
+ .bit_width = 0x10, \
+ .bit_offset = 0, \
+ .access_size = ACPI_ACCESS_SIZE_WORD_ACCESS, \
+ .addr = EINJ_DEF_TRIGGER_PORT} /* HW dependent code can override this also */
+
+typedef struct acpi_einj {
+ acpi_header_t header;
+ acpi_injection_header_t inj_header;
+ acpi_einj_action_table_t action_table[ACTION_COUNT];
+} __packed acpi_einj_t;
+
+
+void acpi_create_einj(acpi_einj_t *einj, uintptr_t addr, u8 actions);
+
unsigned long fw_cfg_acpi_tables(unsigned long start);
/* These are implemented by the target port or north/southbridge. */