diff options
| -rw-r--r-- | Documentation/acpi/apei/einj.txt | 10 | ||||
| -rw-r--r-- | drivers/acpi/apei/einj.c | 72 |
2 files changed, 77 insertions, 5 deletions
diff --git a/Documentation/acpi/apei/einj.txt b/Documentation/acpi/apei/einj.txt index 838b7f0d5e11..dfab71848dc8 100644 --- a/Documentation/acpi/apei/einj.txt +++ b/Documentation/acpi/apei/einj.txt | |||
| @@ -45,5 +45,15 @@ directory apei/einj. The following files are provided. | |||
| 45 | injection. Before this, please specify all necessary error | 45 | injection. Before this, please specify all necessary error |
| 46 | parameters. | 46 | parameters. |
| 47 | 47 | ||
| 48 | - param1 | ||
| 49 | This file is used to set the first error parameter value. Effect of | ||
| 50 | parameter depends on error_type specified. For memory error, this is | ||
| 51 | physical memory address. | ||
| 52 | |||
| 53 | - param2 | ||
| 54 | This file is used to set the second error parameter value. Effect of | ||
| 55 | parameter depends on error_type specified. For memory error, this is | ||
| 56 | physical memory address mask. | ||
| 57 | |||
| 48 | For more information about EINJ, please refer to ACPI specification | 58 | For more information about EINJ, please refer to ACPI specification |
| 49 | version 4.0, section 17.5. | 59 | version 4.0, section 17.5. |
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 4ccebd8f930a..465c885938ee 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | * For more information about EINJ, please refer to ACPI Specification | 7 | * For more information about EINJ, please refer to ACPI Specification |
| 8 | * version 4.0, section 17.5. | 8 | * version 4.0, section 17.5. |
| 9 | * | 9 | * |
| 10 | * Copyright 2009 Intel Corp. | 10 | * Copyright 2009-2010 Intel Corp. |
| 11 | * Author: Huang Ying <ying.huang@intel.com> | 11 | * Author: Huang Ying <ying.huang@intel.com> |
| 12 | * | 12 | * |
| 13 | * This program is free software; you can redistribute it and/or | 13 | * This program is free software; you can redistribute it and/or |
| @@ -42,6 +42,20 @@ | |||
| 42 | /* Firmware should respond within 1 miliseconds */ | 42 | /* Firmware should respond within 1 miliseconds */ |
| 43 | #define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC) | 43 | #define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC) |
| 44 | 44 | ||
| 45 | /* | ||
| 46 | * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the | ||
| 47 | * EINJ table through an unpublished extension. Use with caution as | ||
| 48 | * most will ignore the parameter and make their own choice of address | ||
| 49 | * for error injection. | ||
| 50 | */ | ||
| 51 | struct einj_parameter { | ||
| 52 | u64 type; | ||
| 53 | u64 reserved1; | ||
| 54 | u64 reserved2; | ||
| 55 | u64 param1; | ||
| 56 | u64 param2; | ||
| 57 | }; | ||
| 58 | |||
| 45 | #define EINJ_OP_BUSY 0x1 | 59 | #define EINJ_OP_BUSY 0x1 |
| 46 | #define EINJ_STATUS_SUCCESS 0x0 | 60 | #define EINJ_STATUS_SUCCESS 0x0 |
| 47 | #define EINJ_STATUS_FAIL 0x1 | 61 | #define EINJ_STATUS_FAIL 0x1 |
| @@ -85,6 +99,8 @@ static struct apei_exec_ins_type einj_ins_type[] = { | |||
| 85 | */ | 99 | */ |
| 86 | static DEFINE_MUTEX(einj_mutex); | 100 | static DEFINE_MUTEX(einj_mutex); |
| 87 | 101 | ||
| 102 | static struct einj_parameter *einj_param; | ||
| 103 | |||
| 88 | static void einj_exec_ctx_init(struct apei_exec_context *ctx) | 104 | static void einj_exec_ctx_init(struct apei_exec_context *ctx) |
| 89 | { | 105 | { |
| 90 | apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type), | 106 | apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type), |
| @@ -130,6 +146,26 @@ static int einj_timedout(u64 *t) | |||
| 130 | return 0; | 146 | return 0; |
| 131 | } | 147 | } |
| 132 | 148 | ||
| 149 | static u64 einj_get_parameter_address(void) | ||
| 150 | { | ||
| 151 | int i; | ||
| 152 | u64 paddr = 0; | ||
| 153 | struct acpi_whea_header *entry; | ||
| 154 | |||
| 155 | entry = EINJ_TAB_ENTRY(einj_tab); | ||
| 156 | for (i = 0; i < einj_tab->entries; i++) { | ||
| 157 | if (entry->action == ACPI_EINJ_SET_ERROR_TYPE && | ||
| 158 | entry->instruction == ACPI_EINJ_WRITE_REGISTER && | ||
| 159 | entry->register_region.space_id == | ||
| 160 | ACPI_ADR_SPACE_SYSTEM_MEMORY) | ||
| 161 | memcpy(&paddr, &entry->register_region.address, | ||
| 162 | sizeof(paddr)); | ||
| 163 | entry++; | ||
| 164 | } | ||
| 165 | |||
| 166 | return paddr; | ||
| 167 | } | ||
| 168 | |||
| 133 | /* do sanity check to trigger table */ | 169 | /* do sanity check to trigger table */ |
| 134 | static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) | 170 | static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) |
| 135 | { | 171 | { |
| @@ -233,7 +269,7 @@ out: | |||
| 233 | return rc; | 269 | return rc; |
| 234 | } | 270 | } |
| 235 | 271 | ||
| 236 | static int __einj_error_inject(u32 type) | 272 | static int __einj_error_inject(u32 type, u64 param1, u64 param2) |
| 237 | { | 273 | { |
| 238 | struct apei_exec_context ctx; | 274 | struct apei_exec_context ctx; |
| 239 | u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT; | 275 | u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT; |
| @@ -248,6 +284,10 @@ static int __einj_error_inject(u32 type) | |||
| 248 | rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); | 284 | rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); |
| 249 | if (rc) | 285 | if (rc) |
| 250 | return rc; | 286 | return rc; |
| 287 | if (einj_param) { | ||
| 288 | writeq(param1, &einj_param->param1); | ||
| 289 | writeq(param2, &einj_param->param2); | ||
| 290 | } | ||
| 251 | rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); | 291 | rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); |
| 252 | if (rc) | 292 | if (rc) |
| 253 | return rc; | 293 | return rc; |
| @@ -281,18 +321,20 @@ static int __einj_error_inject(u32 type) | |||
| 281 | } | 321 | } |
| 282 | 322 | ||
| 283 | /* Inject the specified hardware error */ | 323 | /* Inject the specified hardware error */ |
| 284 | static int einj_error_inject(u32 type) | 324 | static int einj_error_inject(u32 type, u64 param1, u64 param2) |
| 285 | { | 325 | { |
| 286 | int rc; | 326 | int rc; |
| 287 | 327 | ||
| 288 | mutex_lock(&einj_mutex); | 328 | mutex_lock(&einj_mutex); |
| 289 | rc = __einj_error_inject(type); | 329 | rc = __einj_error_inject(type, param1, param2); |
| 290 | mutex_unlock(&einj_mutex); | 330 | mutex_unlock(&einj_mutex); |
| 291 | 331 | ||
| 292 | return rc; | 332 | return rc; |
| 293 | } | 333 | } |
| 294 | 334 | ||
| 295 | static u32 error_type; | 335 | static u32 error_type; |
| 336 | static u64 error_param1; | ||
| 337 | static u64 error_param2; | ||
| 296 | static struct dentry *einj_debug_dir; | 338 | static struct dentry *einj_debug_dir; |
| 297 | 339 | ||
| 298 | static int available_error_type_show(struct seq_file *m, void *v) | 340 | static int available_error_type_show(struct seq_file *m, void *v) |
| @@ -376,7 +418,7 @@ static int error_inject_set(void *data, u64 val) | |||
| 376 | if (!error_type) | 418 | if (!error_type) |
| 377 | return -EINVAL; | 419 | return -EINVAL; |
| 378 | 420 | ||
| 379 | return einj_error_inject(error_type); | 421 | return einj_error_inject(error_type, error_param1, error_param2); |
| 380 | } | 422 | } |
| 381 | 423 | ||
| 382 | DEFINE_SIMPLE_ATTRIBUTE(error_inject_fops, NULL, | 424 | DEFINE_SIMPLE_ATTRIBUTE(error_inject_fops, NULL, |
| @@ -399,6 +441,7 @@ static int einj_check_table(struct acpi_table_einj *einj_tab) | |||
| 399 | static int __init einj_init(void) | 441 | static int __init einj_init(void) |
| 400 | { | 442 | { |
| 401 | int rc; | 443 | int rc; |
| 444 | u64 param_paddr; | ||
| 402 | acpi_status status; | 445 | acpi_status status; |
| 403 | struct dentry *fentry; | 446 | struct dentry *fentry; |
| 404 | struct apei_exec_context ctx; | 447 | struct apei_exec_context ctx; |
| @@ -436,6 +479,14 @@ static int __init einj_init(void) | |||
| 436 | einj_debug_dir, NULL, &error_type_fops); | 479 | einj_debug_dir, NULL, &error_type_fops); |
| 437 | if (!fentry) | 480 | if (!fentry) |
| 438 | goto err_cleanup; | 481 | goto err_cleanup; |
| 482 | fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR, | ||
| 483 | einj_debug_dir, &error_param1); | ||
| 484 | if (!fentry) | ||
| 485 | goto err_cleanup; | ||
| 486 | fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR, | ||
| 487 | einj_debug_dir, &error_param2); | ||
| 488 | if (!fentry) | ||
| 489 | goto err_cleanup; | ||
| 439 | fentry = debugfs_create_file("error_inject", S_IWUSR, | 490 | fentry = debugfs_create_file("error_inject", S_IWUSR, |
| 440 | einj_debug_dir, NULL, &error_inject_fops); | 491 | einj_debug_dir, NULL, &error_inject_fops); |
| 441 | if (!fentry) | 492 | if (!fentry) |
| @@ -452,11 +503,20 @@ static int __init einj_init(void) | |||
| 452 | rc = apei_exec_pre_map_gars(&ctx); | 503 | rc = apei_exec_pre_map_gars(&ctx); |
| 453 | if (rc) | 504 | if (rc) |
| 454 | goto err_release; | 505 | goto err_release; |
| 506 | param_paddr = einj_get_parameter_address(); | ||
| 507 | if (param_paddr) { | ||
| 508 | einj_param = ioremap(param_paddr, sizeof(*einj_param)); | ||
| 509 | rc = -ENOMEM; | ||
| 510 | if (!einj_param) | ||
| 511 | goto err_unmap; | ||
| 512 | } | ||
| 455 | 513 | ||
| 456 | pr_info(EINJ_PFX "Error INJection is initialized.\n"); | 514 | pr_info(EINJ_PFX "Error INJection is initialized.\n"); |
| 457 | 515 | ||
| 458 | return 0; | 516 | return 0; |
| 459 | 517 | ||
| 518 | err_unmap: | ||
| 519 | apei_exec_post_unmap_gars(&ctx); | ||
| 460 | err_release: | 520 | err_release: |
| 461 | apei_resources_release(&einj_resources); | 521 | apei_resources_release(&einj_resources); |
| 462 | err_fini: | 522 | err_fini: |
| @@ -471,6 +531,8 @@ static void __exit einj_exit(void) | |||
| 471 | { | 531 | { |
| 472 | struct apei_exec_context ctx; | 532 | struct apei_exec_context ctx; |
| 473 | 533 | ||
| 534 | if (einj_param) | ||
| 535 | iounmap(einj_param); | ||
| 474 | einj_exec_ctx_init(&ctx); | 536 | einj_exec_ctx_init(&ctx); |
| 475 | apei_exec_post_unmap_gars(&ctx); | 537 | apei_exec_post_unmap_gars(&ctx); |
| 476 | apei_resources_release(&einj_resources); | 538 | apei_resources_release(&einj_resources); |
