summaryrefslogtreecommitdiff
path: root/src/acpi/acpi.c
diff options
context:
space:
mode:
authorRocky Phagura <rphagura@fb.com>2021-01-10 15:42:50 -0800
committerDavid Hendricks <david.hendricks@gmail.com>2021-04-30 01:19:30 +0000
commiteff0713dfcbc2a32a20946c3a69ccb85775de4a0 (patch)
tree85f57e48d6eb3b8eff4b9033f03c48787529a12a /src/acpi/acpi.c
parent7da1c1732a45f8e426772b99946e6718b9b4df03 (diff)
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 <rphagura@fb.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/49286 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org> Reviewed-by: Rocky Phagura
Diffstat (limited to 'src/acpi/acpi.c')
-rw-r--r--src/acpi/acpi.c148
1 files changed, 148 insertions, 0 deletions
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: