|
@@ -194,8 +194,29 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static struct acpi_generic_address *einj_get_trigger_parameter_region(
|
|
|
+ struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct acpi_whea_header *entry;
|
|
|
+
|
|
|
+ entry = (struct acpi_whea_header *)
|
|
|
+ ((char *)trigger_tab + sizeof(struct acpi_einj_trigger));
|
|
|
+ for (i = 0; i < trigger_tab->entry_count; i++) {
|
|
|
+ if (entry->action == ACPI_EINJ_TRIGGER_ERROR &&
|
|
|
+ entry->instruction == ACPI_EINJ_WRITE_REGISTER_VALUE &&
|
|
|
+ entry->register_region.space_id ==
|
|
|
+ ACPI_ADR_SPACE_SYSTEM_MEMORY &&
|
|
|
+ (entry->register_region.address & param2) == (param1 & param2))
|
|
|
+ return &entry->register_region;
|
|
|
+ entry++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
/* Execute instructions in trigger error action table */
|
|
|
-static int __einj_error_trigger(u64 trigger_paddr)
|
|
|
+static int __einj_error_trigger(u64 trigger_paddr, u32 type,
|
|
|
+ u64 param1, u64 param2)
|
|
|
{
|
|
|
struct acpi_einj_trigger *trigger_tab = NULL;
|
|
|
struct apei_exec_context trigger_ctx;
|
|
@@ -204,14 +225,16 @@ static int __einj_error_trigger(u64 trigger_paddr)
|
|
|
struct resource *r;
|
|
|
u32 table_size;
|
|
|
int rc = -EIO;
|
|
|
+ struct acpi_generic_address *trigger_param_region = NULL;
|
|
|
|
|
|
r = request_mem_region(trigger_paddr, sizeof(*trigger_tab),
|
|
|
"APEI EINJ Trigger Table");
|
|
|
if (!r) {
|
|
|
pr_err(EINJ_PFX
|
|
|
- "Can not request iomem region <%016llx-%016llx> for Trigger table.\n",
|
|
|
+ "Can not request [mem %#010llx-%#010llx] for Trigger table\n",
|
|
|
(unsigned long long)trigger_paddr,
|
|
|
- (unsigned long long)trigger_paddr+sizeof(*trigger_tab));
|
|
|
+ (unsigned long long)trigger_paddr +
|
|
|
+ sizeof(*trigger_tab) - 1);
|
|
|
goto out;
|
|
|
}
|
|
|
trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab));
|
|
@@ -232,9 +255,9 @@ static int __einj_error_trigger(u64 trigger_paddr)
|
|
|
"APEI EINJ Trigger Table");
|
|
|
if (!r) {
|
|
|
pr_err(EINJ_PFX
|
|
|
-"Can not request iomem region <%016llx-%016llx> for Trigger Table Entry.\n",
|
|
|
- (unsigned long long)trigger_paddr+sizeof(*trigger_tab),
|
|
|
- (unsigned long long)trigger_paddr + table_size);
|
|
|
+"Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n",
|
|
|
+ (unsigned long long)trigger_paddr + sizeof(*trigger_tab),
|
|
|
+ (unsigned long long)trigger_paddr + table_size - 1);
|
|
|
goto out_rel_header;
|
|
|
}
|
|
|
iounmap(trigger_tab);
|
|
@@ -255,6 +278,30 @@ static int __einj_error_trigger(u64 trigger_paddr)
|
|
|
rc = apei_resources_sub(&trigger_resources, &einj_resources);
|
|
|
if (rc)
|
|
|
goto out_fini;
|
|
|
+ /*
|
|
|
+ * Some firmware will access target address specified in
|
|
|
+ * param1 to trigger the error when injecting memory error.
|
|
|
+ * This will cause resource conflict with regular memory. So
|
|
|
+ * remove it from trigger table resources.
|
|
|
+ */
|
|
|
+ if (param_extension && (type & 0x0038) && param2) {
|
|
|
+ struct apei_resources addr_resources;
|
|
|
+ apei_resources_init(&addr_resources);
|
|
|
+ trigger_param_region = einj_get_trigger_parameter_region(
|
|
|
+ trigger_tab, param1, param2);
|
|
|
+ if (trigger_param_region) {
|
|
|
+ rc = apei_resources_add(&addr_resources,
|
|
|
+ trigger_param_region->address,
|
|
|
+ trigger_param_region->bit_width/8, true);
|
|
|
+ if (rc)
|
|
|
+ goto out_fini;
|
|
|
+ rc = apei_resources_sub(&trigger_resources,
|
|
|
+ &addr_resources);
|
|
|
+ }
|
|
|
+ apei_resources_fini(&addr_resources);
|
|
|
+ if (rc)
|
|
|
+ goto out_fini;
|
|
|
+ }
|
|
|
rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger");
|
|
|
if (rc)
|
|
|
goto out_fini;
|
|
@@ -324,7 +371,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
trigger_paddr = apei_exec_ctx_get_output(&ctx);
|
|
|
- rc = __einj_error_trigger(trigger_paddr);
|
|
|
+ rc = __einj_error_trigger(trigger_paddr, type, param1, param2);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
|
|
@@ -465,10 +512,9 @@ static int __init einj_init(void)
|
|
|
|
|
|
status = acpi_get_table(ACPI_SIG_EINJ, 0,
|
|
|
(struct acpi_table_header **)&einj_tab);
|
|
|
- if (status == AE_NOT_FOUND) {
|
|
|
- pr_info(EINJ_PFX "Table is not found!\n");
|
|
|
+ if (status == AE_NOT_FOUND)
|
|
|
return -ENODEV;
|
|
|
- } else if (ACPI_FAILURE(status)) {
|
|
|
+ else if (ACPI_FAILURE(status)) {
|
|
|
const char *msg = acpi_format_exception(status);
|
|
|
pr_err(EINJ_PFX "Failed to get table, %s\n", msg);
|
|
|
return -EINVAL;
|