aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/apei/einj.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/apei/einj.c')
-rw-r--r--drivers/acpi/apei/einj.c66
1 files changed, 56 insertions, 10 deletions
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c
index 31546fd21029..5b898d4dda99 100644
--- a/drivers/acpi/apei/einj.c
+++ b/drivers/acpi/apei/einj.c
@@ -286,8 +286,29 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab)
286 return 0; 286 return 0;
287} 287}
288 288
289static struct acpi_generic_address *einj_get_trigger_parameter_region(
290 struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2)
291{
292 int i;
293 struct acpi_whea_header *entry;
294
295 entry = (struct acpi_whea_header *)
296 ((char *)trigger_tab + sizeof(struct acpi_einj_trigger));
297 for (i = 0; i < trigger_tab->entry_count; i++) {
298 if (entry->action == ACPI_EINJ_TRIGGER_ERROR &&
299 entry->instruction == ACPI_EINJ_WRITE_REGISTER_VALUE &&
300 entry->register_region.space_id ==
301 ACPI_ADR_SPACE_SYSTEM_MEMORY &&
302 (entry->register_region.address & param2) == (param1 & param2))
303 return &entry->register_region;
304 entry++;
305 }
306
307 return NULL;
308}
289/* Execute instructions in trigger error action table */ 309/* Execute instructions in trigger error action table */
290static int __einj_error_trigger(u64 trigger_paddr) 310static int __einj_error_trigger(u64 trigger_paddr, u32 type,
311 u64 param1, u64 param2)
291{ 312{
292 struct acpi_einj_trigger *trigger_tab = NULL; 313 struct acpi_einj_trigger *trigger_tab = NULL;
293 struct apei_exec_context trigger_ctx; 314 struct apei_exec_context trigger_ctx;
@@ -296,14 +317,16 @@ static int __einj_error_trigger(u64 trigger_paddr)
296 struct resource *r; 317 struct resource *r;
297 u32 table_size; 318 u32 table_size;
298 int rc = -EIO; 319 int rc = -EIO;
320 struct acpi_generic_address *trigger_param_region = NULL;
299 321
300 r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), 322 r = request_mem_region(trigger_paddr, sizeof(*trigger_tab),
301 "APEI EINJ Trigger Table"); 323 "APEI EINJ Trigger Table");
302 if (!r) { 324 if (!r) {
303 pr_err(EINJ_PFX 325 pr_err(EINJ_PFX
304 "Can not request iomem region <%016llx-%016llx> for Trigger table.\n", 326 "Can not request [mem %#010llx-%#010llx] for Trigger table\n",
305 (unsigned long long)trigger_paddr, 327 (unsigned long long)trigger_paddr,
306 (unsigned long long)trigger_paddr+sizeof(*trigger_tab)); 328 (unsigned long long)trigger_paddr +
329 sizeof(*trigger_tab) - 1);
307 goto out; 330 goto out;
308 } 331 }
309 trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); 332 trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab));
@@ -324,9 +347,9 @@ static int __einj_error_trigger(u64 trigger_paddr)
324 "APEI EINJ Trigger Table"); 347 "APEI EINJ Trigger Table");
325 if (!r) { 348 if (!r) {
326 pr_err(EINJ_PFX 349 pr_err(EINJ_PFX
327"Can not request iomem region <%016llx-%016llx> for Trigger Table Entry.\n", 350"Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n",
328 (unsigned long long)trigger_paddr+sizeof(*trigger_tab), 351 (unsigned long long)trigger_paddr + sizeof(*trigger_tab),
329 (unsigned long long)trigger_paddr + table_size); 352 (unsigned long long)trigger_paddr + table_size - 1);
330 goto out_rel_header; 353 goto out_rel_header;
331 } 354 }
332 iounmap(trigger_tab); 355 iounmap(trigger_tab);
@@ -347,6 +370,30 @@ static int __einj_error_trigger(u64 trigger_paddr)
347 rc = apei_resources_sub(&trigger_resources, &einj_resources); 370 rc = apei_resources_sub(&trigger_resources, &einj_resources);
348 if (rc) 371 if (rc)
349 goto out_fini; 372 goto out_fini;
373 /*
374 * Some firmware will access target address specified in
375 * param1 to trigger the error when injecting memory error.
376 * This will cause resource conflict with regular memory. So
377 * remove it from trigger table resources.
378 */
379 if (param_extension && (type & 0x0038) && param2) {
380 struct apei_resources addr_resources;
381 apei_resources_init(&addr_resources);
382 trigger_param_region = einj_get_trigger_parameter_region(
383 trigger_tab, param1, param2);
384 if (trigger_param_region) {
385 rc = apei_resources_add(&addr_resources,
386 trigger_param_region->address,
387 trigger_param_region->bit_width/8, true);
388 if (rc)
389 goto out_fini;
390 rc = apei_resources_sub(&trigger_resources,
391 &addr_resources);
392 }
393 apei_resources_fini(&addr_resources);
394 if (rc)
395 goto out_fini;
396 }
350 rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger"); 397 rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger");
351 if (rc) 398 if (rc)
352 goto out_fini; 399 goto out_fini;
@@ -460,7 +507,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
460 if (rc) 507 if (rc)
461 return rc; 508 return rc;
462 trigger_paddr = apei_exec_ctx_get_output(&ctx); 509 trigger_paddr = apei_exec_ctx_get_output(&ctx);
463 rc = __einj_error_trigger(trigger_paddr); 510 rc = __einj_error_trigger(trigger_paddr, type, param1, param2);
464 if (rc) 511 if (rc)
465 return rc; 512 return rc;
466 rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION); 513 rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
@@ -610,10 +657,9 @@ static int __init einj_init(void)
610 657
611 status = acpi_get_table(ACPI_SIG_EINJ, 0, 658 status = acpi_get_table(ACPI_SIG_EINJ, 0,
612 (struct acpi_table_header **)&einj_tab); 659 (struct acpi_table_header **)&einj_tab);
613 if (status == AE_NOT_FOUND) { 660 if (status == AE_NOT_FOUND)
614 pr_info(EINJ_PFX "Table is not found!\n");
615 return -ENODEV; 661 return -ENODEV;
616 } else if (ACPI_FAILURE(status)) { 662 else if (ACPI_FAILURE(status)) {
617 const char *msg = acpi_format_exception(status); 663 const char *msg = acpi_format_exception(status);
618 pr_err(EINJ_PFX "Failed to get table, %s\n", msg); 664 pr_err(EINJ_PFX "Failed to get table, %s\n", msg);
619 return -EINVAL; 665 return -EINVAL;