11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e4021345SHuang Ying /*
3e4021345SHuang Ying * APEI Error INJection support
4e4021345SHuang Ying *
5e4021345SHuang Ying * EINJ provides a hardware error injection mechanism, this is useful
6e4021345SHuang Ying * for debugging and testing of other APEI and RAS features.
7e4021345SHuang Ying *
8e4021345SHuang Ying * For more information about EINJ, please refer to ACPI Specification
9e4021345SHuang Ying * version 4.0, section 17.5.
10e4021345SHuang Ying *
116e320ec1SHuang Ying * Copyright 2009-2010 Intel Corp.
12e4021345SHuang Ying * Author: Huang Ying <ying.huang@intel.com>
13e4021345SHuang Ying */
14e4021345SHuang Ying
15e4021345SHuang Ying #include <linux/kernel.h>
16e4021345SHuang Ying #include <linux/module.h>
17e4021345SHuang Ying #include <linux/init.h>
18e4021345SHuang Ying #include <linux/io.h>
19e4021345SHuang Ying #include <linux/debugfs.h>
20e4021345SHuang Ying #include <linux/seq_file.h>
21e4021345SHuang Ying #include <linux/nmi.h>
22e4021345SHuang Ying #include <linux/delay.h>
23c5a13032SChen Gong #include <linux/mm.h>
24d3ab3edcSChen, Gong #include <asm/unaligned.h>
25e4021345SHuang Ying
26e4021345SHuang Ying #include "apei-internal.h"
27e4021345SHuang Ying
28b2f740baSBorislav Petkov #undef pr_fmt
29b2f740baSBorislav Petkov #define pr_fmt(fmt) "EINJ: " fmt
30e4021345SHuang Ying
31bf7fc0c3SShuai Xue #define SLEEP_UNIT_MIN 1000 /* 1ms */
32bf7fc0c3SShuai Xue #define SLEEP_UNIT_MAX 5000 /* 5ms */
33bf7fc0c3SShuai Xue /* Firmware should respond within 1 seconds */
34bf7fc0c3SShuai Xue #define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC)
35c5a13032SChen Gong #define ACPI5_VENDOR_BIT BIT(31)
36c5a13032SChen Gong #define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \
37c5a13032SChen Gong ACPI_EINJ_MEMORY_UNCORRECTABLE | \
38c5a13032SChen Gong ACPI_EINJ_MEMORY_FATAL)
39e4021345SHuang Ying
406e320ec1SHuang Ying /*
41c130bd6fSTony Luck * ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action.
42c130bd6fSTony Luck */
43c130bd6fSTony Luck static int acpi5;
44c130bd6fSTony Luck
45c130bd6fSTony Luck struct set_error_type_with_address {
46c130bd6fSTony Luck u32 type;
47c130bd6fSTony Luck u32 vendor_extension;
48c130bd6fSTony Luck u32 flags;
49c130bd6fSTony Luck u32 apicid;
50c130bd6fSTony Luck u64 memory_address;
51c130bd6fSTony Luck u64 memory_address_range;
52c130bd6fSTony Luck u32 pcie_sbdf;
53c130bd6fSTony Luck };
54c130bd6fSTony Luck enum {
55c130bd6fSTony Luck SETWA_FLAGS_APICID = 1,
56c130bd6fSTony Luck SETWA_FLAGS_MEM = 2,
57c130bd6fSTony Luck SETWA_FLAGS_PCIE_SBDF = 4,
58c130bd6fSTony Luck };
59c130bd6fSTony Luck
60c130bd6fSTony Luck /*
61c130bd6fSTony Luck * Vendor extensions for platform specific operations
62c130bd6fSTony Luck */
63c130bd6fSTony Luck struct vendor_error_type_extension {
64c130bd6fSTony Luck u32 length;
65c130bd6fSTony Luck u32 pcie_sbdf;
66c130bd6fSTony Luck u16 vendor_id;
67c130bd6fSTony Luck u16 device_id;
68c130bd6fSTony Luck u8 rev_id;
69c130bd6fSTony Luck u8 reserved[3];
70c130bd6fSTony Luck };
71c130bd6fSTony Luck
72ee49089dSChen Gong static u32 notrigger;
73ee49089dSChen Gong
74c130bd6fSTony Luck static u32 vendor_flags;
75c130bd6fSTony Luck static struct debugfs_blob_wrapper vendor_blob;
76c130bd6fSTony Luck static char vendor_dev[64];
77c130bd6fSTony Luck
78c130bd6fSTony Luck /*
796e320ec1SHuang Ying * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the
806e320ec1SHuang Ying * EINJ table through an unpublished extension. Use with caution as
816e320ec1SHuang Ying * most will ignore the parameter and make their own choice of address
82c3e6088eSHuang Ying * for error injection. This extension is used only if
83c3e6088eSHuang Ying * param_extension module parameter is specified.
846e320ec1SHuang Ying */
856e320ec1SHuang Ying struct einj_parameter {
866e320ec1SHuang Ying u64 type;
876e320ec1SHuang Ying u64 reserved1;
886e320ec1SHuang Ying u64 reserved2;
896e320ec1SHuang Ying u64 param1;
906e320ec1SHuang Ying u64 param2;
916e320ec1SHuang Ying };
926e320ec1SHuang Ying
93e4021345SHuang Ying #define EINJ_OP_BUSY 0x1
94e4021345SHuang Ying #define EINJ_STATUS_SUCCESS 0x0
95e4021345SHuang Ying #define EINJ_STATUS_FAIL 0x1
96e4021345SHuang Ying #define EINJ_STATUS_INVAL 0x2
97e4021345SHuang Ying
98e4021345SHuang Ying #define EINJ_TAB_ENTRY(tab) \
99e4021345SHuang Ying ((struct acpi_whea_header *)((char *)(tab) + \
100e4021345SHuang Ying sizeof(struct acpi_table_einj)))
101e4021345SHuang Ying
102c3e6088eSHuang Ying static bool param_extension;
103c3e6088eSHuang Ying module_param(param_extension, bool, 0);
104c3e6088eSHuang Ying
105e4021345SHuang Ying static struct acpi_table_einj *einj_tab;
106e4021345SHuang Ying
107e4021345SHuang Ying static struct apei_resources einj_resources;
108e4021345SHuang Ying
109e4021345SHuang Ying static struct apei_exec_ins_type einj_ins_type[] = {
110e4021345SHuang Ying [ACPI_EINJ_READ_REGISTER] = {
111e4021345SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER,
112e4021345SHuang Ying .run = apei_exec_read_register,
113e4021345SHuang Ying },
114e4021345SHuang Ying [ACPI_EINJ_READ_REGISTER_VALUE] = {
115e4021345SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER,
116e4021345SHuang Ying .run = apei_exec_read_register_value,
117e4021345SHuang Ying },
118e4021345SHuang Ying [ACPI_EINJ_WRITE_REGISTER] = {
119e4021345SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER,
120e4021345SHuang Ying .run = apei_exec_write_register,
121e4021345SHuang Ying },
122e4021345SHuang Ying [ACPI_EINJ_WRITE_REGISTER_VALUE] = {
123e4021345SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER,
124e4021345SHuang Ying .run = apei_exec_write_register_value,
125e4021345SHuang Ying },
126e4021345SHuang Ying [ACPI_EINJ_NOOP] = {
127e4021345SHuang Ying .flags = 0,
128e4021345SHuang Ying .run = apei_exec_noop,
129e4021345SHuang Ying },
130e4021345SHuang Ying };
131e4021345SHuang Ying
132e4021345SHuang Ying /*
133e4021345SHuang Ying * Prevent EINJ interpreter to run simultaneously, because the
134e4021345SHuang Ying * corresponding firmware implementation may not work properly when
135e4021345SHuang Ying * invoked simultaneously.
136e4021345SHuang Ying */
137e4021345SHuang Ying static DEFINE_MUTEX(einj_mutex);
138e4021345SHuang Ying
139c130bd6fSTony Luck static void *einj_param;
140c130bd6fSTony Luck
einj_exec_ctx_init(struct apei_exec_context * ctx)141e4021345SHuang Ying static void einj_exec_ctx_init(struct apei_exec_context *ctx)
142e4021345SHuang Ying {
143e4021345SHuang Ying apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type),
144e4021345SHuang Ying EINJ_TAB_ENTRY(einj_tab), einj_tab->entries);
145e4021345SHuang Ying }
146e4021345SHuang Ying
__einj_get_available_error_type(u32 * type)147e4021345SHuang Ying static int __einj_get_available_error_type(u32 *type)
148e4021345SHuang Ying {
149e4021345SHuang Ying struct apei_exec_context ctx;
150e4021345SHuang Ying int rc;
151e4021345SHuang Ying
152e4021345SHuang Ying einj_exec_ctx_init(&ctx);
153e4021345SHuang Ying rc = apei_exec_run(&ctx, ACPI_EINJ_GET_ERROR_TYPE);
154e4021345SHuang Ying if (rc)
155e4021345SHuang Ying return rc;
156e4021345SHuang Ying *type = apei_exec_ctx_get_output(&ctx);
157e4021345SHuang Ying
158e4021345SHuang Ying return 0;
159e4021345SHuang Ying }
160e4021345SHuang Ying
161e4021345SHuang Ying /* Get error injection capabilities of the platform */
einj_get_available_error_type(u32 * type)162e4021345SHuang Ying static int einj_get_available_error_type(u32 *type)
163e4021345SHuang Ying {
164e4021345SHuang Ying int rc;
165e4021345SHuang Ying
166e4021345SHuang Ying mutex_lock(&einj_mutex);
167e4021345SHuang Ying rc = __einj_get_available_error_type(type);
168e4021345SHuang Ying mutex_unlock(&einj_mutex);
169e4021345SHuang Ying
170e4021345SHuang Ying return rc;
171e4021345SHuang Ying }
172e4021345SHuang Ying
einj_timedout(u64 * t)173e4021345SHuang Ying static int einj_timedout(u64 *t)
174e4021345SHuang Ying {
175bf7fc0c3SShuai Xue if ((s64)*t < SLEEP_UNIT_MIN) {
176933ca4e3SKefeng Wang pr_warn(FW_WARN "Firmware does not respond in time\n");
177e4021345SHuang Ying return 1;
178e4021345SHuang Ying }
179bf7fc0c3SShuai Xue *t -= SLEEP_UNIT_MIN;
180bf7fc0c3SShuai Xue usleep_range(SLEEP_UNIT_MIN, SLEEP_UNIT_MAX);
181bf7fc0c3SShuai Xue
182e4021345SHuang Ying return 0;
183e4021345SHuang Ying }
184e4021345SHuang Ying
check_vendor_extension(u64 paddr,struct set_error_type_with_address * v5param)185c130bd6fSTony Luck static void check_vendor_extension(u64 paddr,
186c130bd6fSTony Luck struct set_error_type_with_address *v5param)
187c130bd6fSTony Luck {
188459413dbSLuck, Tony int offset = v5param->vendor_extension;
189c130bd6fSTony Luck struct vendor_error_type_extension *v;
190c130bd6fSTony Luck u32 sbdf;
191c130bd6fSTony Luck
192c130bd6fSTony Luck if (!offset)
193c130bd6fSTony Luck return;
194a238317cSLv Zheng v = acpi_os_map_iomem(paddr + offset, sizeof(*v));
195c130bd6fSTony Luck if (!v)
196c130bd6fSTony Luck return;
197459413dbSLuck, Tony sbdf = v->pcie_sbdf;
198c130bd6fSTony Luck sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n",
199c130bd6fSTony Luck sbdf >> 24, (sbdf >> 16) & 0xff,
200c130bd6fSTony Luck (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7,
201459413dbSLuck, Tony v->vendor_id, v->device_id, v->rev_id);
202a238317cSLv Zheng acpi_os_unmap_iomem(v, sizeof(*v));
203c130bd6fSTony Luck }
204c130bd6fSTony Luck
einj_get_parameter_address(void)205c130bd6fSTony Luck static void *einj_get_parameter_address(void)
2066e320ec1SHuang Ying {
2076e320ec1SHuang Ying int i;
208d3ab3edcSChen, Gong u64 pa_v4 = 0, pa_v5 = 0;
2096e320ec1SHuang Ying struct acpi_whea_header *entry;
2106e320ec1SHuang Ying
2116e320ec1SHuang Ying entry = EINJ_TAB_ENTRY(einj_tab);
2126e320ec1SHuang Ying for (i = 0; i < einj_tab->entries; i++) {
2136e320ec1SHuang Ying if (entry->action == ACPI_EINJ_SET_ERROR_TYPE &&
2146e320ec1SHuang Ying entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
2156e320ec1SHuang Ying entry->register_region.space_id ==
2166e320ec1SHuang Ying ACPI_ADR_SPACE_SYSTEM_MEMORY)
217d3ab3edcSChen, Gong pa_v4 = get_unaligned(&entry->register_region.address);
218c130bd6fSTony Luck if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS &&
219c130bd6fSTony Luck entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
220c130bd6fSTony Luck entry->register_region.space_id ==
221c130bd6fSTony Luck ACPI_ADR_SPACE_SYSTEM_MEMORY)
222d3ab3edcSChen, Gong pa_v5 = get_unaligned(&entry->register_region.address);
2236e320ec1SHuang Ying entry++;
2246e320ec1SHuang Ying }
225d3ab3edcSChen, Gong if (pa_v5) {
226c130bd6fSTony Luck struct set_error_type_with_address *v5param;
2276e320ec1SHuang Ying
228a238317cSLv Zheng v5param = acpi_os_map_iomem(pa_v5, sizeof(*v5param));
229c130bd6fSTony Luck if (v5param) {
230c130bd6fSTony Luck acpi5 = 1;
231d3ab3edcSChen, Gong check_vendor_extension(pa_v5, v5param);
232c130bd6fSTony Luck return v5param;
233c130bd6fSTony Luck }
234c130bd6fSTony Luck }
235d3ab3edcSChen, Gong if (param_extension && pa_v4) {
236c130bd6fSTony Luck struct einj_parameter *v4param;
237c130bd6fSTony Luck
238a238317cSLv Zheng v4param = acpi_os_map_iomem(pa_v4, sizeof(*v4param));
239c130bd6fSTony Luck if (!v4param)
24029924b9fSDan Carpenter return NULL;
241459413dbSLuck, Tony if (v4param->reserved1 || v4param->reserved2) {
242a238317cSLv Zheng acpi_os_unmap_iomem(v4param, sizeof(*v4param));
24329924b9fSDan Carpenter return NULL;
244c130bd6fSTony Luck }
245c130bd6fSTony Luck return v4param;
246c130bd6fSTony Luck }
247c130bd6fSTony Luck
24829924b9fSDan Carpenter return NULL;
2496e320ec1SHuang Ying }
2506e320ec1SHuang Ying
251e4021345SHuang Ying /* do sanity check to trigger table */
einj_check_trigger_header(struct acpi_einj_trigger * trigger_tab)252e4021345SHuang Ying static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab)
253e4021345SHuang Ying {
254e4021345SHuang Ying if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger))
255e4021345SHuang Ying return -EINVAL;
256e4021345SHuang Ying if (trigger_tab->table_size > PAGE_SIZE ||
2574c40aed8SNiklas Söderlund trigger_tab->table_size < trigger_tab->header_size)
258e4021345SHuang Ying return -EINVAL;
259e4021345SHuang Ying if (trigger_tab->entry_count !=
260e4021345SHuang Ying (trigger_tab->table_size - trigger_tab->header_size) /
261e4021345SHuang Ying sizeof(struct acpi_einj_entry))
262e4021345SHuang Ying return -EINVAL;
263e4021345SHuang Ying
264e4021345SHuang Ying return 0;
265e4021345SHuang Ying }
266e4021345SHuang Ying
einj_get_trigger_parameter_region(struct acpi_einj_trigger * trigger_tab,u64 param1,u64 param2)267b4e008dcSXiao, Hui static struct acpi_generic_address *einj_get_trigger_parameter_region(
268b4e008dcSXiao, Hui struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2)
269b4e008dcSXiao, Hui {
270b4e008dcSXiao, Hui int i;
271b4e008dcSXiao, Hui struct acpi_whea_header *entry;
272b4e008dcSXiao, Hui
273b4e008dcSXiao, Hui entry = (struct acpi_whea_header *)
274b4e008dcSXiao, Hui ((char *)trigger_tab + sizeof(struct acpi_einj_trigger));
275b4e008dcSXiao, Hui for (i = 0; i < trigger_tab->entry_count; i++) {
276b4e008dcSXiao, Hui if (entry->action == ACPI_EINJ_TRIGGER_ERROR &&
2771d5d820bSYazen Ghannam entry->instruction <= ACPI_EINJ_WRITE_REGISTER_VALUE &&
278b4e008dcSXiao, Hui entry->register_region.space_id ==
279b4e008dcSXiao, Hui ACPI_ADR_SPACE_SYSTEM_MEMORY &&
280b4e008dcSXiao, Hui (entry->register_region.address & param2) == (param1 & param2))
281b4e008dcSXiao, Hui return &entry->register_region;
282b4e008dcSXiao, Hui entry++;
283b4e008dcSXiao, Hui }
284b4e008dcSXiao, Hui
285b4e008dcSXiao, Hui return NULL;
286b4e008dcSXiao, Hui }
287e4021345SHuang Ying /* Execute instructions in trigger error action table */
__einj_error_trigger(u64 trigger_paddr,u32 type,u64 param1,u64 param2)288fdea163dSHuang Ying static int __einj_error_trigger(u64 trigger_paddr, u32 type,
289fdea163dSHuang Ying u64 param1, u64 param2)
290e4021345SHuang Ying {
291e4021345SHuang Ying struct acpi_einj_trigger *trigger_tab = NULL;
292e4021345SHuang Ying struct apei_exec_context trigger_ctx;
293e4021345SHuang Ying struct apei_resources trigger_resources;
294e4021345SHuang Ying struct acpi_whea_header *trigger_entry;
295e4021345SHuang Ying struct resource *r;
296e4021345SHuang Ying u32 table_size;
297e4021345SHuang Ying int rc = -EIO;
298b4e008dcSXiao, Hui struct acpi_generic_address *trigger_param_region = NULL;
299e4021345SHuang Ying
300e4021345SHuang Ying r = request_mem_region(trigger_paddr, sizeof(*trigger_tab),
301e4021345SHuang Ying "APEI EINJ Trigger Table");
302e4021345SHuang Ying if (!r) {
303b2f740baSBorislav Petkov pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n",
304e4021345SHuang Ying (unsigned long long)trigger_paddr,
30546b91e37SBjorn Helgaas (unsigned long long)trigger_paddr +
30646b91e37SBjorn Helgaas sizeof(*trigger_tab) - 1);
307e4021345SHuang Ying goto out;
308e4021345SHuang Ying }
309e4021345SHuang Ying trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab));
310e4021345SHuang Ying if (!trigger_tab) {
311b2f740baSBorislav Petkov pr_err("Failed to map trigger table!\n");
312e4021345SHuang Ying goto out_rel_header;
313e4021345SHuang Ying }
314e4021345SHuang Ying rc = einj_check_trigger_header(trigger_tab);
315e4021345SHuang Ying if (rc) {
316933ca4e3SKefeng Wang pr_warn(FW_BUG "Invalid trigger error action table.\n");
317e4021345SHuang Ying goto out_rel_header;
318e4021345SHuang Ying }
3194c40aed8SNiklas Söderlund
3204c40aed8SNiklas Söderlund /* No action structures in the TRIGGER_ERROR table, nothing to do */
3214c40aed8SNiklas Söderlund if (!trigger_tab->entry_count)
3224c40aed8SNiklas Söderlund goto out_rel_header;
3234c40aed8SNiklas Söderlund
324e4021345SHuang Ying rc = -EIO;
325e4021345SHuang Ying table_size = trigger_tab->table_size;
326e4021345SHuang Ying r = request_mem_region(trigger_paddr + sizeof(*trigger_tab),
327e4021345SHuang Ying table_size - sizeof(*trigger_tab),
328e4021345SHuang Ying "APEI EINJ Trigger Table");
329e4021345SHuang Ying if (!r) {
330b2f740baSBorislav Petkov pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n",
331e4021345SHuang Ying (unsigned long long)trigger_paddr + sizeof(*trigger_tab),
33246b91e37SBjorn Helgaas (unsigned long long)trigger_paddr + table_size - 1);
333e4021345SHuang Ying goto out_rel_header;
334e4021345SHuang Ying }
335e4021345SHuang Ying iounmap(trigger_tab);
336e4021345SHuang Ying trigger_tab = ioremap_cache(trigger_paddr, table_size);
337e4021345SHuang Ying if (!trigger_tab) {
338b2f740baSBorislav Petkov pr_err("Failed to map trigger table!\n");
339e4021345SHuang Ying goto out_rel_entry;
340e4021345SHuang Ying }
341e4021345SHuang Ying trigger_entry = (struct acpi_whea_header *)
342e4021345SHuang Ying ((char *)trigger_tab + sizeof(struct acpi_einj_trigger));
343e4021345SHuang Ying apei_resources_init(&trigger_resources);
344e4021345SHuang Ying apei_exec_ctx_init(&trigger_ctx, einj_ins_type,
345e4021345SHuang Ying ARRAY_SIZE(einj_ins_type),
346e4021345SHuang Ying trigger_entry, trigger_tab->entry_count);
347e4021345SHuang Ying rc = apei_exec_collect_resources(&trigger_ctx, &trigger_resources);
348e4021345SHuang Ying if (rc)
349e4021345SHuang Ying goto out_fini;
350e4021345SHuang Ying rc = apei_resources_sub(&trigger_resources, &einj_resources);
351e4021345SHuang Ying if (rc)
352e4021345SHuang Ying goto out_fini;
353fdea163dSHuang Ying /*
354fdea163dSHuang Ying * Some firmware will access target address specified in
355fdea163dSHuang Ying * param1 to trigger the error when injecting memory error.
356fdea163dSHuang Ying * This will cause resource conflict with regular memory. So
357fdea163dSHuang Ying * remove it from trigger table resources.
358fdea163dSHuang Ying */
359c5a13032SChen Gong if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) {
360fdea163dSHuang Ying struct apei_resources addr_resources;
36137ea9693SJay Lu
362fdea163dSHuang Ying apei_resources_init(&addr_resources);
363b4e008dcSXiao, Hui trigger_param_region = einj_get_trigger_parameter_region(
364b4e008dcSXiao, Hui trigger_tab, param1, param2);
365b4e008dcSXiao, Hui if (trigger_param_region) {
366fdea163dSHuang Ying rc = apei_resources_add(&addr_resources,
367b4e008dcSXiao, Hui trigger_param_region->address,
368b4e008dcSXiao, Hui trigger_param_region->bit_width/8, true);
369fdea163dSHuang Ying if (rc)
370fdea163dSHuang Ying goto out_fini;
371b4e008dcSXiao, Hui rc = apei_resources_sub(&trigger_resources,
372b4e008dcSXiao, Hui &addr_resources);
373b4e008dcSXiao, Hui }
374fdea163dSHuang Ying apei_resources_fini(&addr_resources);
375fdea163dSHuang Ying if (rc)
376fdea163dSHuang Ying goto out_fini;
377fdea163dSHuang Ying }
378e4021345SHuang Ying rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger");
379e4021345SHuang Ying if (rc)
380e4021345SHuang Ying goto out_fini;
381e4021345SHuang Ying rc = apei_exec_pre_map_gars(&trigger_ctx);
382e4021345SHuang Ying if (rc)
383e4021345SHuang Ying goto out_release;
384e4021345SHuang Ying
385e4021345SHuang Ying rc = apei_exec_run(&trigger_ctx, ACPI_EINJ_TRIGGER_ERROR);
386e4021345SHuang Ying
387e4021345SHuang Ying apei_exec_post_unmap_gars(&trigger_ctx);
388e4021345SHuang Ying out_release:
389e4021345SHuang Ying apei_resources_release(&trigger_resources);
390e4021345SHuang Ying out_fini:
391e4021345SHuang Ying apei_resources_fini(&trigger_resources);
392e4021345SHuang Ying out_rel_entry:
393e4021345SHuang Ying release_mem_region(trigger_paddr + sizeof(*trigger_tab),
394e4021345SHuang Ying table_size - sizeof(*trigger_tab));
395e4021345SHuang Ying out_rel_header:
396e4021345SHuang Ying release_mem_region(trigger_paddr, sizeof(*trigger_tab));
397e4021345SHuang Ying out:
398e4021345SHuang Ying if (trigger_tab)
399e4021345SHuang Ying iounmap(trigger_tab);
400e4021345SHuang Ying
401e4021345SHuang Ying return rc;
402e4021345SHuang Ying }
403e4021345SHuang Ying
__einj_error_inject(u32 type,u32 flags,u64 param1,u64 param2,u64 param3,u64 param4)4043482fb5eSLuck, Tony static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
4053482fb5eSLuck, Tony u64 param3, u64 param4)
406e4021345SHuang Ying {
407e4021345SHuang Ying struct apei_exec_context ctx;
408e4021345SHuang Ying u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT;
409e4021345SHuang Ying int rc;
410e4021345SHuang Ying
411e4021345SHuang Ying einj_exec_ctx_init(&ctx);
412e4021345SHuang Ying
413392913deSHuang Ying rc = apei_exec_run_optional(&ctx, ACPI_EINJ_BEGIN_OPERATION);
414e4021345SHuang Ying if (rc)
415e4021345SHuang Ying return rc;
416e4021345SHuang Ying apei_exec_ctx_set_input(&ctx, type);
417c130bd6fSTony Luck if (acpi5) {
418c130bd6fSTony Luck struct set_error_type_with_address *v5param = einj_param;
419c130bd6fSTony Luck
420459413dbSLuck, Tony v5param->type = type;
421c5a13032SChen Gong if (type & ACPI5_VENDOR_BIT) {
422c130bd6fSTony Luck switch (vendor_flags) {
423c130bd6fSTony Luck case SETWA_FLAGS_APICID:
424459413dbSLuck, Tony v5param->apicid = param1;
425c130bd6fSTony Luck break;
426c130bd6fSTony Luck case SETWA_FLAGS_MEM:
427459413dbSLuck, Tony v5param->memory_address = param1;
428459413dbSLuck, Tony v5param->memory_address_range = param2;
429c130bd6fSTony Luck break;
430c130bd6fSTony Luck case SETWA_FLAGS_PCIE_SBDF:
431459413dbSLuck, Tony v5param->pcie_sbdf = param1;
432c130bd6fSTony Luck break;
433c130bd6fSTony Luck }
434459413dbSLuck, Tony v5param->flags = vendor_flags;
4353482fb5eSLuck, Tony } else if (flags) {
4363482fb5eSLuck, Tony v5param->flags = flags;
4373482fb5eSLuck, Tony v5param->memory_address = param1;
4383482fb5eSLuck, Tony v5param->memory_address_range = param2;
4393482fb5eSLuck, Tony v5param->apicid = param3;
4403482fb5eSLuck, Tony v5param->pcie_sbdf = param4;
441c130bd6fSTony Luck } else {
442c130bd6fSTony Luck switch (type) {
443c130bd6fSTony Luck case ACPI_EINJ_PROCESSOR_CORRECTABLE:
444c130bd6fSTony Luck case ACPI_EINJ_PROCESSOR_UNCORRECTABLE:
445c130bd6fSTony Luck case ACPI_EINJ_PROCESSOR_FATAL:
446459413dbSLuck, Tony v5param->apicid = param1;
447459413dbSLuck, Tony v5param->flags = SETWA_FLAGS_APICID;
448c130bd6fSTony Luck break;
449c130bd6fSTony Luck case ACPI_EINJ_MEMORY_CORRECTABLE:
450c130bd6fSTony Luck case ACPI_EINJ_MEMORY_UNCORRECTABLE:
451c130bd6fSTony Luck case ACPI_EINJ_MEMORY_FATAL:
452459413dbSLuck, Tony v5param->memory_address = param1;
453459413dbSLuck, Tony v5param->memory_address_range = param2;
454459413dbSLuck, Tony v5param->flags = SETWA_FLAGS_MEM;
455c130bd6fSTony Luck break;
456c130bd6fSTony Luck case ACPI_EINJ_PCIX_CORRECTABLE:
457c130bd6fSTony Luck case ACPI_EINJ_PCIX_UNCORRECTABLE:
458c130bd6fSTony Luck case ACPI_EINJ_PCIX_FATAL:
459459413dbSLuck, Tony v5param->pcie_sbdf = param1;
460459413dbSLuck, Tony v5param->flags = SETWA_FLAGS_PCIE_SBDF;
461c130bd6fSTony Luck break;
462c130bd6fSTony Luck }
463c130bd6fSTony Luck }
464c130bd6fSTony Luck } else {
465e4021345SHuang Ying rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE);
466e4021345SHuang Ying if (rc)
467e4021345SHuang Ying return rc;
4686e320ec1SHuang Ying if (einj_param) {
469c130bd6fSTony Luck struct einj_parameter *v4param = einj_param;
47037ea9693SJay Lu
471459413dbSLuck, Tony v4param->param1 = param1;
472459413dbSLuck, Tony v4param->param2 = param2;
473c130bd6fSTony Luck }
4746e320ec1SHuang Ying }
475e4021345SHuang Ying rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION);
476e4021345SHuang Ying if (rc)
477e4021345SHuang Ying return rc;
478e4021345SHuang Ying for (;;) {
479e4021345SHuang Ying rc = apei_exec_run(&ctx, ACPI_EINJ_CHECK_BUSY_STATUS);
480e4021345SHuang Ying if (rc)
481e4021345SHuang Ying return rc;
482e4021345SHuang Ying val = apei_exec_ctx_get_output(&ctx);
483e4021345SHuang Ying if (!(val & EINJ_OP_BUSY))
484e4021345SHuang Ying break;
485e4021345SHuang Ying if (einj_timedout(&timeout))
486e4021345SHuang Ying return -EIO;
487e4021345SHuang Ying }
488e4021345SHuang Ying rc = apei_exec_run(&ctx, ACPI_EINJ_GET_COMMAND_STATUS);
489e4021345SHuang Ying if (rc)
490e4021345SHuang Ying return rc;
491e4021345SHuang Ying val = apei_exec_ctx_get_output(&ctx);
492*f1e65718SShuai Xue if (val == EINJ_STATUS_FAIL)
493e4021345SHuang Ying return -EBUSY;
494*f1e65718SShuai Xue else if (val == EINJ_STATUS_INVAL)
495*f1e65718SShuai Xue return -EINVAL;
496e4021345SHuang Ying
497*f1e65718SShuai Xue /*
498*f1e65718SShuai Xue * The error is injected into the platform successfully, then it needs
499*f1e65718SShuai Xue * to trigger the error.
500*f1e65718SShuai Xue */
501e4021345SHuang Ying rc = apei_exec_run(&ctx, ACPI_EINJ_GET_TRIGGER_TABLE);
502e4021345SHuang Ying if (rc)
503e4021345SHuang Ying return rc;
504e4021345SHuang Ying trigger_paddr = apei_exec_ctx_get_output(&ctx);
505ee49089dSChen Gong if (notrigger == 0) {
506fdea163dSHuang Ying rc = __einj_error_trigger(trigger_paddr, type, param1, param2);
507e4021345SHuang Ying if (rc)
508e4021345SHuang Ying return rc;
509ee49089dSChen Gong }
510392913deSHuang Ying rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
511e4021345SHuang Ying
512e4021345SHuang Ying return rc;
513e4021345SHuang Ying }
514e4021345SHuang Ying
515e4021345SHuang Ying /* Inject the specified hardware error */
einj_error_inject(u32 type,u32 flags,u64 param1,u64 param2,u64 param3,u64 param4)5163482fb5eSLuck, Tony static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
5173482fb5eSLuck, Tony u64 param3, u64 param4)
518e4021345SHuang Ying {
519e4021345SHuang Ying int rc;
5204650bac1SToshi Kani u64 base_addr, size;
521e4021345SHuang Ying
5223482fb5eSLuck, Tony /* If user manually set "flags", make sure it is legal */
5233482fb5eSLuck, Tony if (flags && (flags &
5243482fb5eSLuck, Tony ~(SETWA_FLAGS_APICID|SETWA_FLAGS_MEM|SETWA_FLAGS_PCIE_SBDF)))
5253482fb5eSLuck, Tony return -EINVAL;
5263482fb5eSLuck, Tony
527c5a13032SChen Gong /*
528c5a13032SChen Gong * We need extra sanity checks for memory errors.
529c5a13032SChen Gong * Other types leap directly to injection.
530c5a13032SChen Gong */
531c5a13032SChen Gong
532c5a13032SChen Gong /* ensure param1/param2 existed */
533c5a13032SChen Gong if (!(param_extension || acpi5))
534c5a13032SChen Gong goto inject;
535c5a13032SChen Gong
536c5a13032SChen Gong /* ensure injection is memory related */
537c5a13032SChen Gong if (type & ACPI5_VENDOR_BIT) {
538c5a13032SChen Gong if (vendor_flags != SETWA_FLAGS_MEM)
539c5a13032SChen Gong goto inject;
5403482fb5eSLuck, Tony } else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM))
541c5a13032SChen Gong goto inject;
542c5a13032SChen Gong
543c5a13032SChen Gong /*
544c5a13032SChen Gong * Disallow crazy address masks that give BIOS leeway to pick
545c5a13032SChen Gong * injection address almost anywhere. Insist on page or
5464650bac1SToshi Kani * better granularity and that target address is normal RAM or
5474650bac1SToshi Kani * NVDIMM.
548c5a13032SChen Gong */
5494650bac1SToshi Kani base_addr = param1 & param2;
5504650bac1SToshi Kani size = ~param2 + 1;
5514650bac1SToshi Kani
5524650bac1SToshi Kani if (((param2 & PAGE_MASK) != PAGE_MASK) ||
5534650bac1SToshi Kani ((region_intersects(base_addr, size, IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)
5544650bac1SToshi Kani != REGION_INTERSECTS) &&
5554650bac1SToshi Kani (region_intersects(base_addr, size, IORESOURCE_MEM, IORES_DESC_PERSISTENT_MEMORY)
556c6acb1e7STony Luck != REGION_INTERSECTS) &&
557b13a3e5fSDan Williams (region_intersects(base_addr, size, IORESOURCE_MEM, IORES_DESC_SOFT_RESERVED)
558b13a3e5fSDan Williams != REGION_INTERSECTS) &&
559c6acb1e7STony Luck !arch_is_platform_page(base_addr)))
560c5a13032SChen Gong return -EINVAL;
561c5a13032SChen Gong
562ab59c893STony Luck if (is_zero_pfn(base_addr >> PAGE_SHIFT))
563ab59c893STony Luck return -EADDRINUSE;
564ab59c893STony Luck
565c5a13032SChen Gong inject:
566e4021345SHuang Ying mutex_lock(&einj_mutex);
5673482fb5eSLuck, Tony rc = __einj_error_inject(type, flags, param1, param2, param3, param4);
568e4021345SHuang Ying mutex_unlock(&einj_mutex);
569e4021345SHuang Ying
570e4021345SHuang Ying return rc;
571e4021345SHuang Ying }
572e4021345SHuang Ying
573e4021345SHuang Ying static u32 error_type;
5743482fb5eSLuck, Tony static u32 error_flags;
5756e320ec1SHuang Ying static u64 error_param1;
5766e320ec1SHuang Ying static u64 error_param2;
5773482fb5eSLuck, Tony static u64 error_param3;
5783482fb5eSLuck, Tony static u64 error_param4;
579e4021345SHuang Ying static struct dentry *einj_debug_dir;
58087386ee8SJay Lu static const char * const einj_error_type_string[] = {
58187386ee8SJay Lu "0x00000001\tProcessor Correctable\n",
58287386ee8SJay Lu "0x00000002\tProcessor Uncorrectable non-fatal\n",
58387386ee8SJay Lu "0x00000004\tProcessor Uncorrectable fatal\n",
58487386ee8SJay Lu "0x00000008\tMemory Correctable\n",
58587386ee8SJay Lu "0x00000010\tMemory Uncorrectable non-fatal\n",
58687386ee8SJay Lu "0x00000020\tMemory Uncorrectable fatal\n",
58787386ee8SJay Lu "0x00000040\tPCI Express Correctable\n",
58887386ee8SJay Lu "0x00000080\tPCI Express Uncorrectable non-fatal\n",
58987386ee8SJay Lu "0x00000100\tPCI Express Uncorrectable fatal\n",
59087386ee8SJay Lu "0x00000200\tPlatform Correctable\n",
59187386ee8SJay Lu "0x00000400\tPlatform Uncorrectable non-fatal\n",
59287386ee8SJay Lu "0x00000800\tPlatform Uncorrectable fatal\n",
593fe6603caSTony Luck "0x00001000\tCXL.cache Protocol Correctable\n",
594fe6603caSTony Luck "0x00002000\tCXL.cache Protocol Uncorrectable non-fatal\n",
595fe6603caSTony Luck "0x00004000\tCXL.cache Protocol Uncorrectable fatal\n",
596fe6603caSTony Luck "0x00008000\tCXL.mem Protocol Correctable\n",
597fe6603caSTony Luck "0x00010000\tCXL.mem Protocol Uncorrectable non-fatal\n",
598fe6603caSTony Luck "0x00020000\tCXL.mem Protocol Uncorrectable fatal\n",
59987386ee8SJay Lu };
600e4021345SHuang Ying
available_error_type_show(struct seq_file * m,void * v)601e4021345SHuang Ying static int available_error_type_show(struct seq_file *m, void *v)
602e4021345SHuang Ying {
603e4021345SHuang Ying int rc;
604e4021345SHuang Ying u32 available_error_type = 0;
605e4021345SHuang Ying
606e4021345SHuang Ying rc = einj_get_available_error_type(&available_error_type);
607e4021345SHuang Ying if (rc)
608e4021345SHuang Ying return rc;
60987386ee8SJay Lu for (int pos = 0; pos < ARRAY_SIZE(einj_error_type_string); pos++)
61087386ee8SJay Lu if (available_error_type & BIT(pos))
61187386ee8SJay Lu seq_puts(m, einj_error_type_string[pos]);
612e4021345SHuang Ying
613e4021345SHuang Ying return 0;
614e4021345SHuang Ying }
615e4021345SHuang Ying
6160c166c3dSYangtao Li DEFINE_SHOW_ATTRIBUTE(available_error_type);
617e4021345SHuang Ying
error_type_get(void * data,u64 * val)618e4021345SHuang Ying static int error_type_get(void *data, u64 *val)
619e4021345SHuang Ying {
620e4021345SHuang Ying *val = error_type;
621e4021345SHuang Ying
622e4021345SHuang Ying return 0;
623e4021345SHuang Ying }
624e4021345SHuang Ying
error_type_set(void * data,u64 val)625e4021345SHuang Ying static int error_type_set(void *data, u64 val)
626e4021345SHuang Ying {
627e4021345SHuang Ying int rc;
628e4021345SHuang Ying u32 available_error_type = 0;
629c130bd6fSTony Luck u32 tval, vendor;
630c130bd6fSTony Luck
63153fc7e80SShuai Xue /* Only low 32 bits for error type are valid */
63253fc7e80SShuai Xue if (val & GENMASK_ULL(63, 32))
63353fc7e80SShuai Xue return -EINVAL;
63453fc7e80SShuai Xue
635c130bd6fSTony Luck /*
636c130bd6fSTony Luck * Vendor defined types have 0x80000000 bit set, and
637c130bd6fSTony Luck * are not enumerated by ACPI_EINJ_GET_ERROR_TYPE
638c130bd6fSTony Luck */
639c5a13032SChen Gong vendor = val & ACPI5_VENDOR_BIT;
640c130bd6fSTony Luck tval = val & 0x7fffffff;
641e4021345SHuang Ying
642e4021345SHuang Ying /* Only one error type can be specified */
643c130bd6fSTony Luck if (tval & (tval - 1))
644e4021345SHuang Ying return -EINVAL;
645c130bd6fSTony Luck if (!vendor) {
646e4021345SHuang Ying rc = einj_get_available_error_type(&available_error_type);
647e4021345SHuang Ying if (rc)
648e4021345SHuang Ying return rc;
649e4021345SHuang Ying if (!(val & available_error_type))
650e4021345SHuang Ying return -EINVAL;
651c130bd6fSTony Luck }
652e4021345SHuang Ying error_type = val;
653e4021345SHuang Ying
654e4021345SHuang Ying return 0;
655e4021345SHuang Ying }
656e4021345SHuang Ying
657ee9fa8f3SYueHaibing DEFINE_DEBUGFS_ATTRIBUTE(error_type_fops, error_type_get, error_type_set,
658ee9fa8f3SYueHaibing "0x%llx\n");
659e4021345SHuang Ying
error_inject_set(void * data,u64 val)660e4021345SHuang Ying static int error_inject_set(void *data, u64 val)
661e4021345SHuang Ying {
662e4021345SHuang Ying if (!error_type)
663e4021345SHuang Ying return -EINVAL;
664e4021345SHuang Ying
6653482fb5eSLuck, Tony return einj_error_inject(error_type, error_flags, error_param1, error_param2,
6663482fb5eSLuck, Tony error_param3, error_param4);
667e4021345SHuang Ying }
668e4021345SHuang Ying
669ee9fa8f3SYueHaibing DEFINE_DEBUGFS_ATTRIBUTE(error_inject_fops, NULL, error_inject_set, "%llu\n");
670e4021345SHuang Ying
einj_check_table(struct acpi_table_einj * einj_tab)671e4021345SHuang Ying static int einj_check_table(struct acpi_table_einj *einj_tab)
672e4021345SHuang Ying {
6733a78f965SHuang Ying if ((einj_tab->header_length !=
6743a78f965SHuang Ying (sizeof(struct acpi_table_einj) - sizeof(einj_tab->header)))
6753a78f965SHuang Ying && (einj_tab->header_length != sizeof(struct acpi_table_einj)))
676e4021345SHuang Ying return -EINVAL;
677e4021345SHuang Ying if (einj_tab->header.length < sizeof(struct acpi_table_einj))
678e4021345SHuang Ying return -EINVAL;
679e4021345SHuang Ying if (einj_tab->entries !=
680e4021345SHuang Ying (einj_tab->header.length - sizeof(struct acpi_table_einj)) /
681e4021345SHuang Ying sizeof(struct acpi_einj_entry))
682e4021345SHuang Ying return -EINVAL;
683e4021345SHuang Ying
684e4021345SHuang Ying return 0;
685e4021345SHuang Ying }
686e4021345SHuang Ying
einj_init(void)687e4021345SHuang Ying static int __init einj_init(void)
688e4021345SHuang Ying {
689e4021345SHuang Ying int rc;
690e4021345SHuang Ying acpi_status status;
691e4021345SHuang Ying struct apei_exec_context ctx;
692e4021345SHuang Ying
693dba64830SBorislav Petkov if (acpi_disabled) {
694b7a732a7SJon Hunter pr_info("ACPI disabled.\n");
695e4021345SHuang Ying return -ENODEV;
696dba64830SBorislav Petkov }
697e4021345SHuang Ying
698e4021345SHuang Ying status = acpi_get_table(ACPI_SIG_EINJ, 0,
699e4021345SHuang Ying (struct acpi_table_header **)&einj_tab);
700dba64830SBorislav Petkov if (status == AE_NOT_FOUND) {
701dba64830SBorislav Petkov pr_warn("EINJ table not found.\n");
702e4021345SHuang Ying return -ENODEV;
70337ea9693SJay Lu } else if (ACPI_FAILURE(status)) {
704dba64830SBorislav Petkov pr_err("Failed to get EINJ table: %s\n",
705dba64830SBorislav Petkov acpi_format_exception(status));
706e4021345SHuang Ying return -EINVAL;
707e4021345SHuang Ying }
708e4021345SHuang Ying
709e4021345SHuang Ying rc = einj_check_table(einj_tab);
710e4021345SHuang Ying if (rc) {
711d2226784SColin Ian King pr_warn(FW_BUG "Invalid EINJ table.\n");
712541156a3SHanjun Guo goto err_put_table;
713e4021345SHuang Ying }
714e4021345SHuang Ying
715e4021345SHuang Ying rc = -ENOMEM;
716e4021345SHuang Ying einj_debug_dir = debugfs_create_dir("einj", apei_get_debugfs_dir());
717dba64830SBorislav Petkov
7189ec6dbfbSGreg Kroah-Hartman debugfs_create_file("available_error_type", S_IRUSR, einj_debug_dir,
7199ec6dbfbSGreg Kroah-Hartman NULL, &available_error_type_fops);
720dcaed592SRafael J. Wysocki debugfs_create_file_unsafe("error_type", 0600, einj_debug_dir,
7219ec6dbfbSGreg Kroah-Hartman NULL, &error_type_fops);
722dcaed592SRafael J. Wysocki debugfs_create_file_unsafe("error_inject", 0200, einj_debug_dir,
7239ec6dbfbSGreg Kroah-Hartman NULL, &error_inject_fops);
724e4021345SHuang Ying
725e4021345SHuang Ying apei_resources_init(&einj_resources);
726e4021345SHuang Ying einj_exec_ctx_init(&ctx);
727e4021345SHuang Ying rc = apei_exec_collect_resources(&ctx, &einj_resources);
728dba64830SBorislav Petkov if (rc) {
729dba64830SBorislav Petkov pr_err("Error collecting EINJ resources.\n");
730e4021345SHuang Ying goto err_fini;
731dba64830SBorislav Petkov }
732dba64830SBorislav Petkov
733e4021345SHuang Ying rc = apei_resources_request(&einj_resources, "APEI EINJ");
734dba64830SBorislav Petkov if (rc) {
735dba64830SBorislav Petkov pr_err("Error requesting memory/port resources.\n");
736e4021345SHuang Ying goto err_fini;
737dba64830SBorislav Petkov }
738dba64830SBorislav Petkov
739e4021345SHuang Ying rc = apei_exec_pre_map_gars(&ctx);
740dba64830SBorislav Petkov if (rc) {
741dba64830SBorislav Petkov pr_err("Error pre-mapping GARs.\n");
742e4021345SHuang Ying goto err_release;
743dba64830SBorislav Petkov }
744c130bd6fSTony Luck
745c130bd6fSTony Luck einj_param = einj_get_parameter_address();
746c130bd6fSTony Luck if ((param_extension || acpi5) && einj_param) {
7479ec6dbfbSGreg Kroah-Hartman debugfs_create_x32("flags", S_IRUSR | S_IWUSR, einj_debug_dir,
7489ec6dbfbSGreg Kroah-Hartman &error_flags);
7499ec6dbfbSGreg Kroah-Hartman debugfs_create_x64("param1", S_IRUSR | S_IWUSR, einj_debug_dir,
7509ec6dbfbSGreg Kroah-Hartman &error_param1);
7519ec6dbfbSGreg Kroah-Hartman debugfs_create_x64("param2", S_IRUSR | S_IWUSR, einj_debug_dir,
7529ec6dbfbSGreg Kroah-Hartman &error_param2);
7539ec6dbfbSGreg Kroah-Hartman debugfs_create_x64("param3", S_IRUSR | S_IWUSR, einj_debug_dir,
7549ec6dbfbSGreg Kroah-Hartman &error_param3);
7559ec6dbfbSGreg Kroah-Hartman debugfs_create_x64("param4", S_IRUSR | S_IWUSR, einj_debug_dir,
7569ec6dbfbSGreg Kroah-Hartman &error_param4);
7579ec6dbfbSGreg Kroah-Hartman debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR,
758ee49089dSChen Gong einj_debug_dir, ¬rigger);
759c130bd6fSTony Luck }
760c130bd6fSTony Luck
761c130bd6fSTony Luck if (vendor_dev[0]) {
762c130bd6fSTony Luck vendor_blob.data = vendor_dev;
763c130bd6fSTony Luck vendor_blob.size = strlen(vendor_dev);
7649ec6dbfbSGreg Kroah-Hartman debugfs_create_blob("vendor", S_IRUSR, einj_debug_dir,
7659ec6dbfbSGreg Kroah-Hartman &vendor_blob);
7669ec6dbfbSGreg Kroah-Hartman debugfs_create_x32("vendor_flags", S_IRUSR | S_IWUSR,
767c130bd6fSTony Luck einj_debug_dir, &vendor_flags);
7686e320ec1SHuang Ying }
769e4021345SHuang Ying
770b2f740baSBorislav Petkov pr_info("Error INJection is initialized.\n");
771e4021345SHuang Ying
772e4021345SHuang Ying return 0;
773e4021345SHuang Ying
774e4021345SHuang Ying err_release:
775e4021345SHuang Ying apei_resources_release(&einj_resources);
776e4021345SHuang Ying err_fini:
777e4021345SHuang Ying apei_resources_fini(&einj_resources);
778e4021345SHuang Ying debugfs_remove_recursive(einj_debug_dir);
779541156a3SHanjun Guo err_put_table:
780541156a3SHanjun Guo acpi_put_table((struct acpi_table_header *)einj_tab);
781e4021345SHuang Ying
782e4021345SHuang Ying return rc;
783e4021345SHuang Ying }
784e4021345SHuang Ying
einj_exit(void)785e4021345SHuang Ying static void __exit einj_exit(void)
786e4021345SHuang Ying {
787e4021345SHuang Ying struct apei_exec_context ctx;
788e4021345SHuang Ying
789459413dbSLuck, Tony if (einj_param) {
790459413dbSLuck, Tony acpi_size size = (acpi5) ?
791459413dbSLuck, Tony sizeof(struct set_error_type_with_address) :
792459413dbSLuck, Tony sizeof(struct einj_parameter);
793459413dbSLuck, Tony
794a238317cSLv Zheng acpi_os_unmap_iomem(einj_param, size);
795459413dbSLuck, Tony }
796e4021345SHuang Ying einj_exec_ctx_init(&ctx);
797e4021345SHuang Ying apei_exec_post_unmap_gars(&ctx);
798e4021345SHuang Ying apei_resources_release(&einj_resources);
799e4021345SHuang Ying apei_resources_fini(&einj_resources);
800e4021345SHuang Ying debugfs_remove_recursive(einj_debug_dir);
801541156a3SHanjun Guo acpi_put_table((struct acpi_table_header *)einj_tab);
802e4021345SHuang Ying }
803e4021345SHuang Ying
804e4021345SHuang Ying module_init(einj_init);
805e4021345SHuang Ying module_exit(einj_exit);
806e4021345SHuang Ying
807e4021345SHuang Ying MODULE_AUTHOR("Huang Ying");
808e4021345SHuang Ying MODULE_DESCRIPTION("APEI Error INJection support");
809e4021345SHuang Ying MODULE_LICENSE("GPL");
810