Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI/APEI: Add parameter check before error injection

When param1 is enabled in EINJ but not assigned with a valid
value, sometimes it will cause the error like below:

APEI: Can not request [mem 0x7aaa7000-0x7aaa7007] for APEI EINJ Trigger registers

It is because 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 It must be removed
from trigger table resources, but incorrect param1/param2
combination will stop this action. Add extra check to avoid
this kind of error.

Signed-off-by: Chen Gong <gong.chen@linux.intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>

authored by

Chen Gong and committed by
Tony Luck
c5a13032 b8edb641

+36 -3
+35 -3
drivers/acpi/apei/einj.c
··· 32 32 #include <linux/seq_file.h> 33 33 #include <linux/nmi.h> 34 34 #include <linux/delay.h> 35 + #include <linux/mm.h> 35 36 #include <acpi/acpi.h> 36 37 37 38 #include "apei-internal.h" ··· 42 41 #define SPIN_UNIT 100 /* 100ns */ 43 42 /* Firmware should respond within 1 milliseconds */ 44 43 #define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC) 44 + #define ACPI5_VENDOR_BIT BIT(31) 45 + #define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \ 46 + ACPI_EINJ_MEMORY_UNCORRECTABLE | \ 47 + ACPI_EINJ_MEMORY_FATAL) 45 48 46 49 /* 47 50 * ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action. ··· 372 367 * This will cause resource conflict with regular memory. So 373 368 * remove it from trigger table resources. 374 369 */ 375 - if ((param_extension || acpi5) && (type & 0x0038) && param2) { 370 + if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) { 376 371 struct apei_resources addr_resources; 377 372 apei_resources_init(&addr_resources); 378 373 trigger_param_region = einj_get_trigger_parameter_region( ··· 432 427 struct set_error_type_with_address *v5param = einj_param; 433 428 434 429 v5param->type = type; 435 - if (type & 0x80000000) { 430 + if (type & ACPI5_VENDOR_BIT) { 436 431 switch (vendor_flags) { 437 432 case SETWA_FLAGS_APICID: 438 433 v5param->apicid = param1; ··· 517 512 static int einj_error_inject(u32 type, u64 param1, u64 param2) 518 513 { 519 514 int rc; 515 + unsigned long pfn; 520 516 517 + /* 518 + * We need extra sanity checks for memory errors. 519 + * Other types leap directly to injection. 520 + */ 521 + 522 + /* ensure param1/param2 existed */ 523 + if (!(param_extension || acpi5)) 524 + goto inject; 525 + 526 + /* ensure injection is memory related */ 527 + if (type & ACPI5_VENDOR_BIT) { 528 + if (vendor_flags != SETWA_FLAGS_MEM) 529 + goto inject; 530 + } else if (!(type & MEM_ERROR_MASK)) 531 + goto inject; 532 + 533 + /* 534 + * Disallow crazy address masks that give BIOS leeway to pick 535 + * injection address almost anywhere. Insist on page or 536 + * better granularity and that target address is normal RAM. 537 + */ 538 + pfn = PFN_DOWN(param1 & param2); 539 + if (!page_is_ram(pfn) || ((param2 & PAGE_MASK) != PAGE_MASK)) 540 + return -EINVAL; 541 + 542 + inject: 521 543 mutex_lock(&einj_mutex); 522 544 rc = __einj_error_inject(type, param1, param2); 523 545 mutex_unlock(&einj_mutex); ··· 622 590 * Vendor defined types have 0x80000000 bit set, and 623 591 * are not enumerated by ACPI_EINJ_GET_ERROR_TYPE 624 592 */ 625 - vendor = val & 0x80000000; 593 + vendor = val & ACPI5_VENDOR_BIT; 626 594 tval = val & 0x7fffffff; 627 595 628 596 /* Only one error type can be specified */
+1
kernel/resource.c
··· 409 409 { 410 410 return walk_system_ram_range(pfn, 1, NULL, __is_ram) == 1; 411 411 } 412 + EXPORT_SYMBOL_GPL(page_is_ram); 412 413 413 414 void __weak arch_remove_reservations(struct resource *avail) 414 415 {