From eff0713dfcbc2a32a20946c3a69ccb85775de4a0 Mon Sep 17 00:00:00 2001 From: Rocky Phagura Date: Sun, 10 Jan 2021 15:42:50 -0800 Subject: src/acpi: Add APEI EINJ support This adds full EINJ support with trigger action tables. The actual error injection functionality is HW specific. Therefore, HW specific code should call acpi_create_einj with an address where action table resides. The default params of the action table are filled out by the common code. Control is then returned back to the caller to modify or override default parameters. If no changes are needed, caller can simply add the acpi table. At runtime, FW is responsible for filling out the action table with the proper entries. The action table memory is shared between FW and OS. This memory should be marked as reserved in E820 table. Tested on Deltalake mainboard. Boot to OS, load the EINJ driver ( modprobe EINJ) and verify EINJ memory entries are in /proc/iomem. Further tested by injecting errors via the APEI file nodes. More information on error injection can be referenced in the latest ACPI spec. Change-Id: I29c6a861c564ec104f2c097f3e49b3e6d38b040e Signed-off-by: Rocky Phagura Reviewed-on: https://review.coreboot.org/c/coreboot/+/49286 Tested-by: build bot (Jenkins) Reviewed-by: Tim Wawrzynczak Reviewed-by: Rocky Phagura --- src/acpi/Kconfig | 6 +++ src/acpi/acpi.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) (limited to 'src/acpi') 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: -- cgit v1.2.3