// SPDX-License-Identifier: GPL-2.0-only /* * APEI Error INJection support * * EINJ provides a hardware error injection mechanism, this is useful * for debugging and testing of other APEI and RAS features. * * For more information about EINJ, please refer to ACPI Specification * version 4.0, section 17.5. * * Copyright 2009-2010 Intel Corp. * Author: Huang Ying <ying.huang@intel.com>
*/
/* * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the * EINJ table through an unpublished extension. Use with caution as * most will ignore the parameter and make their own choice of address * for error injection. This extension is used only if * param_extension module parameter is specified.
*/ struct einj_parameter {
u64 type;
u64 reserved1;
u64 reserved2;
u64 param1;
u64 param2;
};
/* * Prevent EINJ interpreter to run simultaneously, because the * corresponding firmware implementation may not work properly when * invoked simultaneously.
*/ static DEFINE_MUTEX(einj_mutex);
/* * Exported APIs use this flag to exit early if einj_probe() failed.
*/ bool einj_initialized __ro_after_init;
entry = EINJ_TAB_ENTRY(einj_tab); for (i = 0; i < einj_tab->entries; i++) { if (entry->action == ACPI_EINJ_SET_ERROR_TYPE &&
entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
entry->register_region.space_id ==
ACPI_ADR_SPACE_SYSTEM_MEMORY)
pa_v4 = get_unaligned(&entry->register_region.address); if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS &&
entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
entry->register_region.space_id ==
ACPI_ADR_SPACE_SYSTEM_MEMORY)
pa_v5 = get_unaligned(&entry->register_region.address);
entry++;
} if (pa_v5) { struct set_error_type_with_address v5param; struct set_error_type_with_address __iomem *p;
v5param_size = sizeof(v5param);
p = acpi_os_map_iomem(pa_v5, sizeof(*p)); if (p) { int offset, len;
memcpy_fromio(&v5param, p, v5param_size);
acpi5 = 1;
check_vendor_extension(pa_v5, &v5param); if (is_v2 && available_error_type & ACPI65_EINJV2_SUPP) {
len = v5param.einjv2_struct.length;
offset = offsetof(struct einjv2_extension_struct, component_arr);
max_nr_components = (len - offset) / sizeof(v5param.einjv2_struct.component_arr[0]); /* * The first call to acpi_os_map_iomem above does not include the * component array, instead it is used to read and calculate maximum * number of components supported by the system. Below, the mapping * is expanded to include the component array.
*/
acpi_os_unmap_iomem(p, v5param_size);
offset = offsetof(struct set_error_type_with_address, einjv2_struct);
v5param_size = offset + struct_size(&v5param.einjv2_struct,
component_arr, max_nr_components);
p = acpi_os_map_iomem(pa_v5, v5param_size);
} return p;
}
} if (param_extension && pa_v4) { struct einj_parameter v4param; struct einj_parameter __iomem *p;
p = acpi_os_map_iomem(pa_v4, sizeof(*p)); if (!p) return NULL;
memcpy_fromio(&v4param, p, sizeof(v4param)); if (v4param.reserved1 || v4param.reserved2) {
acpi_os_unmap_iomem(p, sizeof(v4param)); return NULL;
} return p;
}
return NULL;
}
/* do sanity check to trigger table */ staticint einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab)
{ if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger)) return -EINVAL; if (trigger_tab->table_size > PAGE_SIZE ||
trigger_tab->table_size < trigger_tab->header_size) return -EINVAL; if (trigger_tab->entry_count !=
(trigger_tab->table_size - trigger_tab->header_size) / sizeof(struct acpi_einj_entry)) return -EINVAL;
if (is_v2) { for (i = 0; i < max_nr_components; i++) { if (is_end_of_list(syndrome_data[i].comp_id.acpi_id)) break;
v5param->einjv2_struct.component_arr[i].comp_id =
syndrome_data[i].comp_id;
v5param->einjv2_struct.component_arr[i].comp_synd =
syndrome_data[i].comp_synd;
}
v5param->einjv2_struct.component_arr_count = i;
} else {
v5param->apicid = param3;
v5param->pcie_sbdf = param4;
}
} else { switch (type) { case ACPI_EINJ_PROCESSOR_CORRECTABLE: case ACPI_EINJ_PROCESSOR_UNCORRECTABLE: case ACPI_EINJ_PROCESSOR_FATAL:
v5param->apicid = param1;
v5param->flags = SETWA_FLAGS_APICID; break; case ACPI_EINJ_MEMORY_CORRECTABLE: case ACPI_EINJ_MEMORY_UNCORRECTABLE: case ACPI_EINJ_MEMORY_FATAL:
v5param->memory_address = param1;
v5param->memory_address_range = param2;
v5param->flags = SETWA_FLAGS_MEM; break; case ACPI_EINJ_PCIX_CORRECTABLE: case ACPI_EINJ_PCIX_UNCORRECTABLE: case ACPI_EINJ_PCIX_FATAL:
v5param->pcie_sbdf = param1;
v5param->flags = SETWA_FLAGS_PCIE_SBDF; break;
}
}
memcpy_toio(einj_param, v5param, v5param_size);
kfree(v5param);
} else {
rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); if (rc) return rc; if (einj_param) { struct einj_parameter v4param;
memcpy_fromio(&v4param, einj_param, sizeof(v4param));
v4param.param1 = param1;
v4param.param2 = param2;
memcpy_toio(einj_param, &v4param, sizeof(v4param));
}
}
rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); if (rc) return rc; for (;;) {
rc = apei_exec_run(&ctx, ACPI_EINJ_CHECK_BUSY_STATUS); if (rc) return rc;
val = apei_exec_ctx_get_output(&ctx); if (!(val & EINJ_OP_BUSY)) break; if (einj_timedout(&timeout)) return -EIO;
}
rc = apei_exec_run(&ctx, ACPI_EINJ_GET_COMMAND_STATUS); if (rc) return rc;
val = apei_exec_ctx_get_output(&ctx); if (val == EINJ_STATUS_FAIL) return -EBUSY; elseif (val == EINJ_STATUS_INVAL) return -EINVAL;
/* * The error is injected into the platform successfully, then it needs * to trigger the error.
*/
rc = apei_exec_run(&ctx, ACPI_EINJ_GET_TRIGGER_TABLE); if (rc) return rc;
trigger_paddr = apei_exec_ctx_get_output(&ctx); if (notrigger == 0) {
rc = __einj_error_trigger(trigger_paddr, type, param1, param2); if (rc) return rc;
}
rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
return rc;
}
/* Inject the specified hardware error */ int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
u64 param4)
{ int rc;
u64 base_addr, size;
/* If user manually set "flags", make sure it is legal */ if (flags && (flags & ~(SETWA_FLAGS_APICID | SETWA_FLAGS_MEM |
SETWA_FLAGS_PCIE_SBDF | SETWA_FLAGS_EINJV2))) return -EINVAL;
/* check if type is a valid EINJv2 error type */ if (is_v2) { if (!(type & available_error_type_v2)) return -EINVAL;
} /* * We need extra sanity checks for memory errors. * Other types leap directly to injection.
*/
/* ensure injection is memory related */ if (type & ACPI5_VENDOR_BIT) { if (vendor_flags != SETWA_FLAGS_MEM) goto inject;
} elseif (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM)) { goto inject;
}
/* * Injections targeting a CXL 1.0/1.1 port have to be injected * via the einj_cxl_rch_error_inject() path as that does the proper * validation of the given RCRB base (MMIO) address.
*/ if (einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM)) return -EINVAL;
/* * Disallow crazy address masks that give BIOS leeway to pick * injection address almost anywhere. Insist on page or * better granularity and that target address is normal RAM or * NVDIMM.
*/
base_addr = param1 & param2;
size = ~param2 + 1;
int einj_validate_error_type(u64 type)
{
u32 tval, vendor;
/* Only low 32 bits for error type are valid */ if (type & GENMASK_ULL(63, 32)) return -EINVAL;
/* * Vendor defined types have 0x80000000 bit set, and * are not enumerated by ACPI_EINJ_GET_ERROR_TYPE
*/
vendor = type & ACPI5_VENDOR_BIT;
tval = type & GENMASK(30, 0);
/* Only one error type can be specified */ if (tval & (tval - 1)) return -EINVAL; if (!vendor) if (!(type & (available_error_type | available_error_type_v2))) return -EINVAL;
/* Empty line means invalidate this entry */ if (c == 1) {
memset(save, 0xff, COMPONENT_LEN); return c;
}
if (input[0] == '0' && (input[1] == 'x' || input[1] == 'X'))
s = input + 2; else
s = input;
e = input + c - 1;
for (i = 0; i < COMPONENT_LEN; i++) {
byte[1] = *--e;
byte[0] = e > s ? *--e : '0'; if (kstrtol(byte, 16, &val)) return -EINVAL;
tmp[i] = val; if (e <= s) break;
} while (++i < COMPONENT_LEN)
tmp[i] = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.