18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * APEI Error INJection support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * EINJ provides a hardware error injection mechanism, this is useful 68c2ecf20Sopenharmony_ci * for debugging and testing of other APEI and RAS features. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * For more information about EINJ, please refer to ACPI Specification 98c2ecf20Sopenharmony_ci * version 4.0, section 17.5. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright 2009-2010 Intel Corp. 128c2ecf20Sopenharmony_ci * Author: Huang Ying <ying.huang@intel.com> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 208c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 218c2ecf20Sopenharmony_ci#include <linux/nmi.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci#include <linux/mm.h> 248c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "apei-internal.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#undef pr_fmt 298c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "EINJ: " fmt 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define SPIN_UNIT 100 /* 100ns */ 328c2ecf20Sopenharmony_ci/* Firmware should respond within 1 milliseconds */ 338c2ecf20Sopenharmony_ci#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC) 348c2ecf20Sopenharmony_ci#define ACPI5_VENDOR_BIT BIT(31) 358c2ecf20Sopenharmony_ci#define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \ 368c2ecf20Sopenharmony_ci ACPI_EINJ_MEMORY_UNCORRECTABLE | \ 378c2ecf20Sopenharmony_ci ACPI_EINJ_MEMORY_FATAL) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistatic int acpi5; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct set_error_type_with_address { 458c2ecf20Sopenharmony_ci u32 type; 468c2ecf20Sopenharmony_ci u32 vendor_extension; 478c2ecf20Sopenharmony_ci u32 flags; 488c2ecf20Sopenharmony_ci u32 apicid; 498c2ecf20Sopenharmony_ci u64 memory_address; 508c2ecf20Sopenharmony_ci u64 memory_address_range; 518c2ecf20Sopenharmony_ci u32 pcie_sbdf; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_cienum { 548c2ecf20Sopenharmony_ci SETWA_FLAGS_APICID = 1, 558c2ecf20Sopenharmony_ci SETWA_FLAGS_MEM = 2, 568c2ecf20Sopenharmony_ci SETWA_FLAGS_PCIE_SBDF = 4, 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * Vendor extensions for platform specific operations 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistruct vendor_error_type_extension { 638c2ecf20Sopenharmony_ci u32 length; 648c2ecf20Sopenharmony_ci u32 pcie_sbdf; 658c2ecf20Sopenharmony_ci u16 vendor_id; 668c2ecf20Sopenharmony_ci u16 device_id; 678c2ecf20Sopenharmony_ci u8 rev_id; 688c2ecf20Sopenharmony_ci u8 reserved[3]; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic u32 notrigger; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic u32 vendor_flags; 748c2ecf20Sopenharmony_cistatic struct debugfs_blob_wrapper vendor_blob; 758c2ecf20Sopenharmony_cistatic char vendor_dev[64]; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the 798c2ecf20Sopenharmony_ci * EINJ table through an unpublished extension. Use with caution as 808c2ecf20Sopenharmony_ci * most will ignore the parameter and make their own choice of address 818c2ecf20Sopenharmony_ci * for error injection. This extension is used only if 828c2ecf20Sopenharmony_ci * param_extension module parameter is specified. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistruct einj_parameter { 858c2ecf20Sopenharmony_ci u64 type; 868c2ecf20Sopenharmony_ci u64 reserved1; 878c2ecf20Sopenharmony_ci u64 reserved2; 888c2ecf20Sopenharmony_ci u64 param1; 898c2ecf20Sopenharmony_ci u64 param2; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define EINJ_OP_BUSY 0x1 938c2ecf20Sopenharmony_ci#define EINJ_STATUS_SUCCESS 0x0 948c2ecf20Sopenharmony_ci#define EINJ_STATUS_FAIL 0x1 958c2ecf20Sopenharmony_ci#define EINJ_STATUS_INVAL 0x2 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define EINJ_TAB_ENTRY(tab) \ 988c2ecf20Sopenharmony_ci ((struct acpi_whea_header *)((char *)(tab) + \ 998c2ecf20Sopenharmony_ci sizeof(struct acpi_table_einj))) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic bool param_extension; 1028c2ecf20Sopenharmony_cimodule_param(param_extension, bool, 0); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic struct acpi_table_einj *einj_tab; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic struct apei_resources einj_resources; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic struct apei_exec_ins_type einj_ins_type[] = { 1098c2ecf20Sopenharmony_ci [ACPI_EINJ_READ_REGISTER] = { 1108c2ecf20Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 1118c2ecf20Sopenharmony_ci .run = apei_exec_read_register, 1128c2ecf20Sopenharmony_ci }, 1138c2ecf20Sopenharmony_ci [ACPI_EINJ_READ_REGISTER_VALUE] = { 1148c2ecf20Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 1158c2ecf20Sopenharmony_ci .run = apei_exec_read_register_value, 1168c2ecf20Sopenharmony_ci }, 1178c2ecf20Sopenharmony_ci [ACPI_EINJ_WRITE_REGISTER] = { 1188c2ecf20Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 1198c2ecf20Sopenharmony_ci .run = apei_exec_write_register, 1208c2ecf20Sopenharmony_ci }, 1218c2ecf20Sopenharmony_ci [ACPI_EINJ_WRITE_REGISTER_VALUE] = { 1228c2ecf20Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 1238c2ecf20Sopenharmony_ci .run = apei_exec_write_register_value, 1248c2ecf20Sopenharmony_ci }, 1258c2ecf20Sopenharmony_ci [ACPI_EINJ_NOOP] = { 1268c2ecf20Sopenharmony_ci .flags = 0, 1278c2ecf20Sopenharmony_ci .run = apei_exec_noop, 1288c2ecf20Sopenharmony_ci }, 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * Prevent EINJ interpreter to run simultaneously, because the 1338c2ecf20Sopenharmony_ci * corresponding firmware implementation may not work properly when 1348c2ecf20Sopenharmony_ci * invoked simultaneously. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(einj_mutex); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void *einj_param; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void einj_exec_ctx_init(struct apei_exec_context *ctx) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type), 1438c2ecf20Sopenharmony_ci EINJ_TAB_ENTRY(einj_tab), einj_tab->entries); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int __einj_get_available_error_type(u32 *type) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct apei_exec_context ctx; 1498c2ecf20Sopenharmony_ci int rc; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci einj_exec_ctx_init(&ctx); 1528c2ecf20Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_EINJ_GET_ERROR_TYPE); 1538c2ecf20Sopenharmony_ci if (rc) 1548c2ecf20Sopenharmony_ci return rc; 1558c2ecf20Sopenharmony_ci *type = apei_exec_ctx_get_output(&ctx); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* Get error injection capabilities of the platform */ 1618c2ecf20Sopenharmony_cistatic int einj_get_available_error_type(u32 *type) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int rc; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mutex_lock(&einj_mutex); 1668c2ecf20Sopenharmony_ci rc = __einj_get_available_error_type(type); 1678c2ecf20Sopenharmony_ci mutex_unlock(&einj_mutex); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return rc; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int einj_timedout(u64 *t) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci if ((s64)*t < SPIN_UNIT) { 1758c2ecf20Sopenharmony_ci pr_warn(FW_WARN "Firmware does not respond in time\n"); 1768c2ecf20Sopenharmony_ci return 1; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci *t -= SPIN_UNIT; 1798c2ecf20Sopenharmony_ci ndelay(SPIN_UNIT); 1808c2ecf20Sopenharmony_ci touch_nmi_watchdog(); 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void check_vendor_extension(u64 paddr, 1858c2ecf20Sopenharmony_ci struct set_error_type_with_address *v5param) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci int offset = v5param->vendor_extension; 1888c2ecf20Sopenharmony_ci struct vendor_error_type_extension *v; 1898c2ecf20Sopenharmony_ci u32 sbdf; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (!offset) 1928c2ecf20Sopenharmony_ci return; 1938c2ecf20Sopenharmony_ci v = acpi_os_map_iomem(paddr + offset, sizeof(*v)); 1948c2ecf20Sopenharmony_ci if (!v) 1958c2ecf20Sopenharmony_ci return; 1968c2ecf20Sopenharmony_ci sbdf = v->pcie_sbdf; 1978c2ecf20Sopenharmony_ci sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n", 1988c2ecf20Sopenharmony_ci sbdf >> 24, (sbdf >> 16) & 0xff, 1998c2ecf20Sopenharmony_ci (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7, 2008c2ecf20Sopenharmony_ci v->vendor_id, v->device_id, v->rev_id); 2018c2ecf20Sopenharmony_ci acpi_os_unmap_iomem(v, sizeof(*v)); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void *einj_get_parameter_address(void) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci int i; 2078c2ecf20Sopenharmony_ci u64 pa_v4 = 0, pa_v5 = 0; 2088c2ecf20Sopenharmony_ci struct acpi_whea_header *entry; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci entry = EINJ_TAB_ENTRY(einj_tab); 2118c2ecf20Sopenharmony_ci for (i = 0; i < einj_tab->entries; i++) { 2128c2ecf20Sopenharmony_ci if (entry->action == ACPI_EINJ_SET_ERROR_TYPE && 2138c2ecf20Sopenharmony_ci entry->instruction == ACPI_EINJ_WRITE_REGISTER && 2148c2ecf20Sopenharmony_ci entry->register_region.space_id == 2158c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_SYSTEM_MEMORY) 2168c2ecf20Sopenharmony_ci pa_v4 = get_unaligned(&entry->register_region.address); 2178c2ecf20Sopenharmony_ci if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS && 2188c2ecf20Sopenharmony_ci entry->instruction == ACPI_EINJ_WRITE_REGISTER && 2198c2ecf20Sopenharmony_ci entry->register_region.space_id == 2208c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_SYSTEM_MEMORY) 2218c2ecf20Sopenharmony_ci pa_v5 = get_unaligned(&entry->register_region.address); 2228c2ecf20Sopenharmony_ci entry++; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci if (pa_v5) { 2258c2ecf20Sopenharmony_ci struct set_error_type_with_address *v5param; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci v5param = acpi_os_map_iomem(pa_v5, sizeof(*v5param)); 2288c2ecf20Sopenharmony_ci if (v5param) { 2298c2ecf20Sopenharmony_ci acpi5 = 1; 2308c2ecf20Sopenharmony_ci check_vendor_extension(pa_v5, v5param); 2318c2ecf20Sopenharmony_ci return v5param; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci if (param_extension && pa_v4) { 2358c2ecf20Sopenharmony_ci struct einj_parameter *v4param; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci v4param = acpi_os_map_iomem(pa_v4, sizeof(*v4param)); 2388c2ecf20Sopenharmony_ci if (!v4param) 2398c2ecf20Sopenharmony_ci return NULL; 2408c2ecf20Sopenharmony_ci if (v4param->reserved1 || v4param->reserved2) { 2418c2ecf20Sopenharmony_ci acpi_os_unmap_iomem(v4param, sizeof(*v4param)); 2428c2ecf20Sopenharmony_ci return NULL; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci return v4param; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return NULL; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* do sanity check to trigger table */ 2518c2ecf20Sopenharmony_cistatic int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger)) 2548c2ecf20Sopenharmony_ci return -EINVAL; 2558c2ecf20Sopenharmony_ci if (trigger_tab->table_size > PAGE_SIZE || 2568c2ecf20Sopenharmony_ci trigger_tab->table_size < trigger_tab->header_size) 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci if (trigger_tab->entry_count != 2598c2ecf20Sopenharmony_ci (trigger_tab->table_size - trigger_tab->header_size) / 2608c2ecf20Sopenharmony_ci sizeof(struct acpi_einj_entry)) 2618c2ecf20Sopenharmony_ci return -EINVAL; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic struct acpi_generic_address *einj_get_trigger_parameter_region( 2678c2ecf20Sopenharmony_ci struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci int i; 2708c2ecf20Sopenharmony_ci struct acpi_whea_header *entry; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci entry = (struct acpi_whea_header *) 2738c2ecf20Sopenharmony_ci ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); 2748c2ecf20Sopenharmony_ci for (i = 0; i < trigger_tab->entry_count; i++) { 2758c2ecf20Sopenharmony_ci if (entry->action == ACPI_EINJ_TRIGGER_ERROR && 2768c2ecf20Sopenharmony_ci entry->instruction <= ACPI_EINJ_WRITE_REGISTER_VALUE && 2778c2ecf20Sopenharmony_ci entry->register_region.space_id == 2788c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_SYSTEM_MEMORY && 2798c2ecf20Sopenharmony_ci (entry->register_region.address & param2) == (param1 & param2)) 2808c2ecf20Sopenharmony_ci return &entry->register_region; 2818c2ecf20Sopenharmony_ci entry++; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return NULL; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci/* Execute instructions in trigger error action table */ 2878c2ecf20Sopenharmony_cistatic int __einj_error_trigger(u64 trigger_paddr, u32 type, 2888c2ecf20Sopenharmony_ci u64 param1, u64 param2) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct acpi_einj_trigger *trigger_tab = NULL; 2918c2ecf20Sopenharmony_ci struct apei_exec_context trigger_ctx; 2928c2ecf20Sopenharmony_ci struct apei_resources trigger_resources; 2938c2ecf20Sopenharmony_ci struct acpi_whea_header *trigger_entry; 2948c2ecf20Sopenharmony_ci struct resource *r; 2958c2ecf20Sopenharmony_ci u32 table_size; 2968c2ecf20Sopenharmony_ci int rc = -EIO; 2978c2ecf20Sopenharmony_ci struct acpi_generic_address *trigger_param_region = NULL; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), 3008c2ecf20Sopenharmony_ci "APEI EINJ Trigger Table"); 3018c2ecf20Sopenharmony_ci if (!r) { 3028c2ecf20Sopenharmony_ci pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n", 3038c2ecf20Sopenharmony_ci (unsigned long long)trigger_paddr, 3048c2ecf20Sopenharmony_ci (unsigned long long)trigger_paddr + 3058c2ecf20Sopenharmony_ci sizeof(*trigger_tab) - 1); 3068c2ecf20Sopenharmony_ci goto out; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); 3098c2ecf20Sopenharmony_ci if (!trigger_tab) { 3108c2ecf20Sopenharmony_ci pr_err("Failed to map trigger table!\n"); 3118c2ecf20Sopenharmony_ci goto out_rel_header; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci rc = einj_check_trigger_header(trigger_tab); 3148c2ecf20Sopenharmony_ci if (rc) { 3158c2ecf20Sopenharmony_ci pr_warn(FW_BUG "Invalid trigger error action table.\n"); 3168c2ecf20Sopenharmony_ci goto out_rel_header; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* No action structures in the TRIGGER_ERROR table, nothing to do */ 3208c2ecf20Sopenharmony_ci if (!trigger_tab->entry_count) 3218c2ecf20Sopenharmony_ci goto out_rel_header; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci rc = -EIO; 3248c2ecf20Sopenharmony_ci table_size = trigger_tab->table_size; 3258c2ecf20Sopenharmony_ci r = request_mem_region(trigger_paddr + sizeof(*trigger_tab), 3268c2ecf20Sopenharmony_ci table_size - sizeof(*trigger_tab), 3278c2ecf20Sopenharmony_ci "APEI EINJ Trigger Table"); 3288c2ecf20Sopenharmony_ci if (!r) { 3298c2ecf20Sopenharmony_ci pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", 3308c2ecf20Sopenharmony_ci (unsigned long long)trigger_paddr + sizeof(*trigger_tab), 3318c2ecf20Sopenharmony_ci (unsigned long long)trigger_paddr + table_size - 1); 3328c2ecf20Sopenharmony_ci goto out_rel_header; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci iounmap(trigger_tab); 3358c2ecf20Sopenharmony_ci trigger_tab = ioremap_cache(trigger_paddr, table_size); 3368c2ecf20Sopenharmony_ci if (!trigger_tab) { 3378c2ecf20Sopenharmony_ci pr_err("Failed to map trigger table!\n"); 3388c2ecf20Sopenharmony_ci goto out_rel_entry; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci trigger_entry = (struct acpi_whea_header *) 3418c2ecf20Sopenharmony_ci ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); 3428c2ecf20Sopenharmony_ci apei_resources_init(&trigger_resources); 3438c2ecf20Sopenharmony_ci apei_exec_ctx_init(&trigger_ctx, einj_ins_type, 3448c2ecf20Sopenharmony_ci ARRAY_SIZE(einj_ins_type), 3458c2ecf20Sopenharmony_ci trigger_entry, trigger_tab->entry_count); 3468c2ecf20Sopenharmony_ci rc = apei_exec_collect_resources(&trigger_ctx, &trigger_resources); 3478c2ecf20Sopenharmony_ci if (rc) 3488c2ecf20Sopenharmony_ci goto out_fini; 3498c2ecf20Sopenharmony_ci rc = apei_resources_sub(&trigger_resources, &einj_resources); 3508c2ecf20Sopenharmony_ci if (rc) 3518c2ecf20Sopenharmony_ci goto out_fini; 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * Some firmware will access target address specified in 3548c2ecf20Sopenharmony_ci * param1 to trigger the error when injecting memory error. 3558c2ecf20Sopenharmony_ci * This will cause resource conflict with regular memory. So 3568c2ecf20Sopenharmony_ci * remove it from trigger table resources. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) { 3598c2ecf20Sopenharmony_ci struct apei_resources addr_resources; 3608c2ecf20Sopenharmony_ci apei_resources_init(&addr_resources); 3618c2ecf20Sopenharmony_ci trigger_param_region = einj_get_trigger_parameter_region( 3628c2ecf20Sopenharmony_ci trigger_tab, param1, param2); 3638c2ecf20Sopenharmony_ci if (trigger_param_region) { 3648c2ecf20Sopenharmony_ci rc = apei_resources_add(&addr_resources, 3658c2ecf20Sopenharmony_ci trigger_param_region->address, 3668c2ecf20Sopenharmony_ci trigger_param_region->bit_width/8, true); 3678c2ecf20Sopenharmony_ci if (rc) 3688c2ecf20Sopenharmony_ci goto out_fini; 3698c2ecf20Sopenharmony_ci rc = apei_resources_sub(&trigger_resources, 3708c2ecf20Sopenharmony_ci &addr_resources); 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci apei_resources_fini(&addr_resources); 3738c2ecf20Sopenharmony_ci if (rc) 3748c2ecf20Sopenharmony_ci goto out_fini; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger"); 3778c2ecf20Sopenharmony_ci if (rc) 3788c2ecf20Sopenharmony_ci goto out_fini; 3798c2ecf20Sopenharmony_ci rc = apei_exec_pre_map_gars(&trigger_ctx); 3808c2ecf20Sopenharmony_ci if (rc) 3818c2ecf20Sopenharmony_ci goto out_release; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci rc = apei_exec_run(&trigger_ctx, ACPI_EINJ_TRIGGER_ERROR); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci apei_exec_post_unmap_gars(&trigger_ctx); 3868c2ecf20Sopenharmony_ciout_release: 3878c2ecf20Sopenharmony_ci apei_resources_release(&trigger_resources); 3888c2ecf20Sopenharmony_ciout_fini: 3898c2ecf20Sopenharmony_ci apei_resources_fini(&trigger_resources); 3908c2ecf20Sopenharmony_ciout_rel_entry: 3918c2ecf20Sopenharmony_ci release_mem_region(trigger_paddr + sizeof(*trigger_tab), 3928c2ecf20Sopenharmony_ci table_size - sizeof(*trigger_tab)); 3938c2ecf20Sopenharmony_ciout_rel_header: 3948c2ecf20Sopenharmony_ci release_mem_region(trigger_paddr, sizeof(*trigger_tab)); 3958c2ecf20Sopenharmony_ciout: 3968c2ecf20Sopenharmony_ci if (trigger_tab) 3978c2ecf20Sopenharmony_ci iounmap(trigger_tab); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return rc; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, 4038c2ecf20Sopenharmony_ci u64 param3, u64 param4) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct apei_exec_context ctx; 4068c2ecf20Sopenharmony_ci u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT; 4078c2ecf20Sopenharmony_ci int rc; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci einj_exec_ctx_init(&ctx); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci rc = apei_exec_run_optional(&ctx, ACPI_EINJ_BEGIN_OPERATION); 4128c2ecf20Sopenharmony_ci if (rc) 4138c2ecf20Sopenharmony_ci return rc; 4148c2ecf20Sopenharmony_ci apei_exec_ctx_set_input(&ctx, type); 4158c2ecf20Sopenharmony_ci if (acpi5) { 4168c2ecf20Sopenharmony_ci struct set_error_type_with_address *v5param = einj_param; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci v5param->type = type; 4198c2ecf20Sopenharmony_ci if (type & ACPI5_VENDOR_BIT) { 4208c2ecf20Sopenharmony_ci switch (vendor_flags) { 4218c2ecf20Sopenharmony_ci case SETWA_FLAGS_APICID: 4228c2ecf20Sopenharmony_ci v5param->apicid = param1; 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci case SETWA_FLAGS_MEM: 4258c2ecf20Sopenharmony_ci v5param->memory_address = param1; 4268c2ecf20Sopenharmony_ci v5param->memory_address_range = param2; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci case SETWA_FLAGS_PCIE_SBDF: 4298c2ecf20Sopenharmony_ci v5param->pcie_sbdf = param1; 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci v5param->flags = vendor_flags; 4338c2ecf20Sopenharmony_ci } else if (flags) { 4348c2ecf20Sopenharmony_ci v5param->flags = flags; 4358c2ecf20Sopenharmony_ci v5param->memory_address = param1; 4368c2ecf20Sopenharmony_ci v5param->memory_address_range = param2; 4378c2ecf20Sopenharmony_ci v5param->apicid = param3; 4388c2ecf20Sopenharmony_ci v5param->pcie_sbdf = param4; 4398c2ecf20Sopenharmony_ci } else { 4408c2ecf20Sopenharmony_ci switch (type) { 4418c2ecf20Sopenharmony_ci case ACPI_EINJ_PROCESSOR_CORRECTABLE: 4428c2ecf20Sopenharmony_ci case ACPI_EINJ_PROCESSOR_UNCORRECTABLE: 4438c2ecf20Sopenharmony_ci case ACPI_EINJ_PROCESSOR_FATAL: 4448c2ecf20Sopenharmony_ci v5param->apicid = param1; 4458c2ecf20Sopenharmony_ci v5param->flags = SETWA_FLAGS_APICID; 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci case ACPI_EINJ_MEMORY_CORRECTABLE: 4488c2ecf20Sopenharmony_ci case ACPI_EINJ_MEMORY_UNCORRECTABLE: 4498c2ecf20Sopenharmony_ci case ACPI_EINJ_MEMORY_FATAL: 4508c2ecf20Sopenharmony_ci v5param->memory_address = param1; 4518c2ecf20Sopenharmony_ci v5param->memory_address_range = param2; 4528c2ecf20Sopenharmony_ci v5param->flags = SETWA_FLAGS_MEM; 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci case ACPI_EINJ_PCIX_CORRECTABLE: 4558c2ecf20Sopenharmony_ci case ACPI_EINJ_PCIX_UNCORRECTABLE: 4568c2ecf20Sopenharmony_ci case ACPI_EINJ_PCIX_FATAL: 4578c2ecf20Sopenharmony_ci v5param->pcie_sbdf = param1; 4588c2ecf20Sopenharmony_ci v5param->flags = SETWA_FLAGS_PCIE_SBDF; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci } else { 4638c2ecf20Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); 4648c2ecf20Sopenharmony_ci if (rc) 4658c2ecf20Sopenharmony_ci return rc; 4668c2ecf20Sopenharmony_ci if (einj_param) { 4678c2ecf20Sopenharmony_ci struct einj_parameter *v4param = einj_param; 4688c2ecf20Sopenharmony_ci v4param->param1 = param1; 4698c2ecf20Sopenharmony_ci v4param->param2 = param2; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); 4738c2ecf20Sopenharmony_ci if (rc) 4748c2ecf20Sopenharmony_ci return rc; 4758c2ecf20Sopenharmony_ci for (;;) { 4768c2ecf20Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_EINJ_CHECK_BUSY_STATUS); 4778c2ecf20Sopenharmony_ci if (rc) 4788c2ecf20Sopenharmony_ci return rc; 4798c2ecf20Sopenharmony_ci val = apei_exec_ctx_get_output(&ctx); 4808c2ecf20Sopenharmony_ci if (!(val & EINJ_OP_BUSY)) 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci if (einj_timedout(&timeout)) 4838c2ecf20Sopenharmony_ci return -EIO; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_EINJ_GET_COMMAND_STATUS); 4868c2ecf20Sopenharmony_ci if (rc) 4878c2ecf20Sopenharmony_ci return rc; 4888c2ecf20Sopenharmony_ci val = apei_exec_ctx_get_output(&ctx); 4898c2ecf20Sopenharmony_ci if (val != EINJ_STATUS_SUCCESS) 4908c2ecf20Sopenharmony_ci return -EBUSY; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_EINJ_GET_TRIGGER_TABLE); 4938c2ecf20Sopenharmony_ci if (rc) 4948c2ecf20Sopenharmony_ci return rc; 4958c2ecf20Sopenharmony_ci trigger_paddr = apei_exec_ctx_get_output(&ctx); 4968c2ecf20Sopenharmony_ci if (notrigger == 0) { 4978c2ecf20Sopenharmony_ci rc = __einj_error_trigger(trigger_paddr, type, param1, param2); 4988c2ecf20Sopenharmony_ci if (rc) 4998c2ecf20Sopenharmony_ci return rc; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return rc; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/* Inject the specified hardware error */ 5078c2ecf20Sopenharmony_cistatic int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, 5088c2ecf20Sopenharmony_ci u64 param3, u64 param4) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci int rc; 5118c2ecf20Sopenharmony_ci u64 base_addr, size; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* If user manually set "flags", make sure it is legal */ 5148c2ecf20Sopenharmony_ci if (flags && (flags & 5158c2ecf20Sopenharmony_ci ~(SETWA_FLAGS_APICID|SETWA_FLAGS_MEM|SETWA_FLAGS_PCIE_SBDF))) 5168c2ecf20Sopenharmony_ci return -EINVAL; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* 5198c2ecf20Sopenharmony_ci * We need extra sanity checks for memory errors. 5208c2ecf20Sopenharmony_ci * Other types leap directly to injection. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* ensure param1/param2 existed */ 5248c2ecf20Sopenharmony_ci if (!(param_extension || acpi5)) 5258c2ecf20Sopenharmony_ci goto inject; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* ensure injection is memory related */ 5288c2ecf20Sopenharmony_ci if (type & ACPI5_VENDOR_BIT) { 5298c2ecf20Sopenharmony_ci if (vendor_flags != SETWA_FLAGS_MEM) 5308c2ecf20Sopenharmony_ci goto inject; 5318c2ecf20Sopenharmony_ci } else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM)) 5328c2ecf20Sopenharmony_ci goto inject; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* 5358c2ecf20Sopenharmony_ci * Disallow crazy address masks that give BIOS leeway to pick 5368c2ecf20Sopenharmony_ci * injection address almost anywhere. Insist on page or 5378c2ecf20Sopenharmony_ci * better granularity and that target address is normal RAM or 5388c2ecf20Sopenharmony_ci * NVDIMM. 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_ci base_addr = param1 & param2; 5418c2ecf20Sopenharmony_ci size = ~param2 + 1; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (((param2 & PAGE_MASK) != PAGE_MASK) || 5448c2ecf20Sopenharmony_ci ((region_intersects(base_addr, size, IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) 5458c2ecf20Sopenharmony_ci != REGION_INTERSECTS) && 5468c2ecf20Sopenharmony_ci (region_intersects(base_addr, size, IORESOURCE_MEM, IORES_DESC_PERSISTENT_MEMORY) 5478c2ecf20Sopenharmony_ci != REGION_INTERSECTS) && 5488c2ecf20Sopenharmony_ci (region_intersects(base_addr, size, IORESOURCE_MEM, IORES_DESC_SOFT_RESERVED) 5498c2ecf20Sopenharmony_ci != REGION_INTERSECTS))) 5508c2ecf20Sopenharmony_ci return -EINVAL; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ciinject: 5538c2ecf20Sopenharmony_ci mutex_lock(&einj_mutex); 5548c2ecf20Sopenharmony_ci rc = __einj_error_inject(type, flags, param1, param2, param3, param4); 5558c2ecf20Sopenharmony_ci mutex_unlock(&einj_mutex); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci return rc; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic u32 error_type; 5618c2ecf20Sopenharmony_cistatic u32 error_flags; 5628c2ecf20Sopenharmony_cistatic u64 error_param1; 5638c2ecf20Sopenharmony_cistatic u64 error_param2; 5648c2ecf20Sopenharmony_cistatic u64 error_param3; 5658c2ecf20Sopenharmony_cistatic u64 error_param4; 5668c2ecf20Sopenharmony_cistatic struct dentry *einj_debug_dir; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic int available_error_type_show(struct seq_file *m, void *v) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci int rc; 5718c2ecf20Sopenharmony_ci u32 available_error_type = 0; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci rc = einj_get_available_error_type(&available_error_type); 5748c2ecf20Sopenharmony_ci if (rc) 5758c2ecf20Sopenharmony_ci return rc; 5768c2ecf20Sopenharmony_ci if (available_error_type & 0x0001) 5778c2ecf20Sopenharmony_ci seq_printf(m, "0x00000001\tProcessor Correctable\n"); 5788c2ecf20Sopenharmony_ci if (available_error_type & 0x0002) 5798c2ecf20Sopenharmony_ci seq_printf(m, "0x00000002\tProcessor Uncorrectable non-fatal\n"); 5808c2ecf20Sopenharmony_ci if (available_error_type & 0x0004) 5818c2ecf20Sopenharmony_ci seq_printf(m, "0x00000004\tProcessor Uncorrectable fatal\n"); 5828c2ecf20Sopenharmony_ci if (available_error_type & 0x0008) 5838c2ecf20Sopenharmony_ci seq_printf(m, "0x00000008\tMemory Correctable\n"); 5848c2ecf20Sopenharmony_ci if (available_error_type & 0x0010) 5858c2ecf20Sopenharmony_ci seq_printf(m, "0x00000010\tMemory Uncorrectable non-fatal\n"); 5868c2ecf20Sopenharmony_ci if (available_error_type & 0x0020) 5878c2ecf20Sopenharmony_ci seq_printf(m, "0x00000020\tMemory Uncorrectable fatal\n"); 5888c2ecf20Sopenharmony_ci if (available_error_type & 0x0040) 5898c2ecf20Sopenharmony_ci seq_printf(m, "0x00000040\tPCI Express Correctable\n"); 5908c2ecf20Sopenharmony_ci if (available_error_type & 0x0080) 5918c2ecf20Sopenharmony_ci seq_printf(m, "0x00000080\tPCI Express Uncorrectable non-fatal\n"); 5928c2ecf20Sopenharmony_ci if (available_error_type & 0x0100) 5938c2ecf20Sopenharmony_ci seq_printf(m, "0x00000100\tPCI Express Uncorrectable fatal\n"); 5948c2ecf20Sopenharmony_ci if (available_error_type & 0x0200) 5958c2ecf20Sopenharmony_ci seq_printf(m, "0x00000200\tPlatform Correctable\n"); 5968c2ecf20Sopenharmony_ci if (available_error_type & 0x0400) 5978c2ecf20Sopenharmony_ci seq_printf(m, "0x00000400\tPlatform Uncorrectable non-fatal\n"); 5988c2ecf20Sopenharmony_ci if (available_error_type & 0x0800) 5998c2ecf20Sopenharmony_ci seq_printf(m, "0x00000800\tPlatform Uncorrectable fatal\n"); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci return 0; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(available_error_type); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic int error_type_get(void *data, u64 *val) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci *val = error_type; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic int error_type_set(void *data, u64 val) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci int rc; 6168c2ecf20Sopenharmony_ci u32 available_error_type = 0; 6178c2ecf20Sopenharmony_ci u32 tval, vendor; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* 6208c2ecf20Sopenharmony_ci * Vendor defined types have 0x80000000 bit set, and 6218c2ecf20Sopenharmony_ci * are not enumerated by ACPI_EINJ_GET_ERROR_TYPE 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_ci vendor = val & ACPI5_VENDOR_BIT; 6248c2ecf20Sopenharmony_ci tval = val & 0x7fffffff; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* Only one error type can be specified */ 6278c2ecf20Sopenharmony_ci if (tval & (tval - 1)) 6288c2ecf20Sopenharmony_ci return -EINVAL; 6298c2ecf20Sopenharmony_ci if (!vendor) { 6308c2ecf20Sopenharmony_ci rc = einj_get_available_error_type(&available_error_type); 6318c2ecf20Sopenharmony_ci if (rc) 6328c2ecf20Sopenharmony_ci return rc; 6338c2ecf20Sopenharmony_ci if (!(val & available_error_type)) 6348c2ecf20Sopenharmony_ci return -EINVAL; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci error_type = val; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(error_type_fops, error_type_get, error_type_set, 6428c2ecf20Sopenharmony_ci "0x%llx\n"); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic int error_inject_set(void *data, u64 val) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci if (!error_type) 6478c2ecf20Sopenharmony_ci return -EINVAL; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return einj_error_inject(error_type, error_flags, error_param1, error_param2, 6508c2ecf20Sopenharmony_ci error_param3, error_param4); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(error_inject_fops, NULL, error_inject_set, "%llu\n"); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic int einj_check_table(struct acpi_table_einj *einj_tab) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci if ((einj_tab->header_length != 6588c2ecf20Sopenharmony_ci (sizeof(struct acpi_table_einj) - sizeof(einj_tab->header))) 6598c2ecf20Sopenharmony_ci && (einj_tab->header_length != sizeof(struct acpi_table_einj))) 6608c2ecf20Sopenharmony_ci return -EINVAL; 6618c2ecf20Sopenharmony_ci if (einj_tab->header.length < sizeof(struct acpi_table_einj)) 6628c2ecf20Sopenharmony_ci return -EINVAL; 6638c2ecf20Sopenharmony_ci if (einj_tab->entries != 6648c2ecf20Sopenharmony_ci (einj_tab->header.length - sizeof(struct acpi_table_einj)) / 6658c2ecf20Sopenharmony_ci sizeof(struct acpi_einj_entry)) 6668c2ecf20Sopenharmony_ci return -EINVAL; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic int __init einj_init(void) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci int rc; 6748c2ecf20Sopenharmony_ci acpi_status status; 6758c2ecf20Sopenharmony_ci struct apei_exec_context ctx; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (acpi_disabled) { 6788c2ecf20Sopenharmony_ci pr_warn("ACPI disabled.\n"); 6798c2ecf20Sopenharmony_ci return -ENODEV; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci status = acpi_get_table(ACPI_SIG_EINJ, 0, 6838c2ecf20Sopenharmony_ci (struct acpi_table_header **)&einj_tab); 6848c2ecf20Sopenharmony_ci if (status == AE_NOT_FOUND) { 6858c2ecf20Sopenharmony_ci pr_warn("EINJ table not found.\n"); 6868c2ecf20Sopenharmony_ci return -ENODEV; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci else if (ACPI_FAILURE(status)) { 6898c2ecf20Sopenharmony_ci pr_err("Failed to get EINJ table: %s\n", 6908c2ecf20Sopenharmony_ci acpi_format_exception(status)); 6918c2ecf20Sopenharmony_ci return -EINVAL; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci rc = einj_check_table(einj_tab); 6958c2ecf20Sopenharmony_ci if (rc) { 6968c2ecf20Sopenharmony_ci pr_warn(FW_BUG "Invalid EINJ table.\n"); 6978c2ecf20Sopenharmony_ci goto err_put_table; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci rc = -ENOMEM; 7018c2ecf20Sopenharmony_ci einj_debug_dir = debugfs_create_dir("einj", apei_get_debugfs_dir()); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci debugfs_create_file("available_error_type", S_IRUSR, einj_debug_dir, 7048c2ecf20Sopenharmony_ci NULL, &available_error_type_fops); 7058c2ecf20Sopenharmony_ci debugfs_create_file_unsafe("error_type", 0600, einj_debug_dir, 7068c2ecf20Sopenharmony_ci NULL, &error_type_fops); 7078c2ecf20Sopenharmony_ci debugfs_create_file_unsafe("error_inject", 0200, einj_debug_dir, 7088c2ecf20Sopenharmony_ci NULL, &error_inject_fops); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci apei_resources_init(&einj_resources); 7118c2ecf20Sopenharmony_ci einj_exec_ctx_init(&ctx); 7128c2ecf20Sopenharmony_ci rc = apei_exec_collect_resources(&ctx, &einj_resources); 7138c2ecf20Sopenharmony_ci if (rc) { 7148c2ecf20Sopenharmony_ci pr_err("Error collecting EINJ resources.\n"); 7158c2ecf20Sopenharmony_ci goto err_fini; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci rc = apei_resources_request(&einj_resources, "APEI EINJ"); 7198c2ecf20Sopenharmony_ci if (rc) { 7208c2ecf20Sopenharmony_ci pr_err("Error requesting memory/port resources.\n"); 7218c2ecf20Sopenharmony_ci goto err_fini; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci rc = apei_exec_pre_map_gars(&ctx); 7258c2ecf20Sopenharmony_ci if (rc) { 7268c2ecf20Sopenharmony_ci pr_err("Error pre-mapping GARs.\n"); 7278c2ecf20Sopenharmony_ci goto err_release; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci rc = -ENOMEM; 7318c2ecf20Sopenharmony_ci einj_param = einj_get_parameter_address(); 7328c2ecf20Sopenharmony_ci if ((param_extension || acpi5) && einj_param) { 7338c2ecf20Sopenharmony_ci debugfs_create_x32("flags", S_IRUSR | S_IWUSR, einj_debug_dir, 7348c2ecf20Sopenharmony_ci &error_flags); 7358c2ecf20Sopenharmony_ci debugfs_create_x64("param1", S_IRUSR | S_IWUSR, einj_debug_dir, 7368c2ecf20Sopenharmony_ci &error_param1); 7378c2ecf20Sopenharmony_ci debugfs_create_x64("param2", S_IRUSR | S_IWUSR, einj_debug_dir, 7388c2ecf20Sopenharmony_ci &error_param2); 7398c2ecf20Sopenharmony_ci debugfs_create_x64("param3", S_IRUSR | S_IWUSR, einj_debug_dir, 7408c2ecf20Sopenharmony_ci &error_param3); 7418c2ecf20Sopenharmony_ci debugfs_create_x64("param4", S_IRUSR | S_IWUSR, einj_debug_dir, 7428c2ecf20Sopenharmony_ci &error_param4); 7438c2ecf20Sopenharmony_ci debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR, 7448c2ecf20Sopenharmony_ci einj_debug_dir, ¬rigger); 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (vendor_dev[0]) { 7488c2ecf20Sopenharmony_ci vendor_blob.data = vendor_dev; 7498c2ecf20Sopenharmony_ci vendor_blob.size = strlen(vendor_dev); 7508c2ecf20Sopenharmony_ci debugfs_create_blob("vendor", S_IRUSR, einj_debug_dir, 7518c2ecf20Sopenharmony_ci &vendor_blob); 7528c2ecf20Sopenharmony_ci debugfs_create_x32("vendor_flags", S_IRUSR | S_IWUSR, 7538c2ecf20Sopenharmony_ci einj_debug_dir, &vendor_flags); 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci pr_info("Error INJection is initialized.\n"); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci return 0; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cierr_release: 7618c2ecf20Sopenharmony_ci apei_resources_release(&einj_resources); 7628c2ecf20Sopenharmony_cierr_fini: 7638c2ecf20Sopenharmony_ci apei_resources_fini(&einj_resources); 7648c2ecf20Sopenharmony_ci debugfs_remove_recursive(einj_debug_dir); 7658c2ecf20Sopenharmony_cierr_put_table: 7668c2ecf20Sopenharmony_ci acpi_put_table((struct acpi_table_header *)einj_tab); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci return rc; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic void __exit einj_exit(void) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct apei_exec_context ctx; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (einj_param) { 7768c2ecf20Sopenharmony_ci acpi_size size = (acpi5) ? 7778c2ecf20Sopenharmony_ci sizeof(struct set_error_type_with_address) : 7788c2ecf20Sopenharmony_ci sizeof(struct einj_parameter); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci acpi_os_unmap_iomem(einj_param, size); 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci einj_exec_ctx_init(&ctx); 7838c2ecf20Sopenharmony_ci apei_exec_post_unmap_gars(&ctx); 7848c2ecf20Sopenharmony_ci apei_resources_release(&einj_resources); 7858c2ecf20Sopenharmony_ci apei_resources_fini(&einj_resources); 7868c2ecf20Sopenharmony_ci debugfs_remove_recursive(einj_debug_dir); 7878c2ecf20Sopenharmony_ci acpi_put_table((struct acpi_table_header *)einj_tab); 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cimodule_init(einj_init); 7918c2ecf20Sopenharmony_cimodule_exit(einj_exit); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Huang Ying"); 7948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("APEI Error INJection support"); 7958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 796