xref: /openbmc/linux/drivers/acpi/apei/einj.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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, &notrigger);
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