diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/acpi/Kconfig | 6 | ||||
-rw-r--r-- | src/acpi/acpi.c | 148 | ||||
-rw-r--r-- | src/include/acpi/acpi.h | 130 |
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. */ |