diff options
Diffstat (limited to 'drivers/acpi/apei/einj.c')
-rw-r--r-- | drivers/acpi/apei/einj.c | 66 |
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 | ||
289 | static 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 */ |
290 | static int __einj_error_trigger(u64 trigger_paddr) | 310 | static 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; |