162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PCIe AER software error injection support.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Debugging PCIe AER code is quite difficult because it is hard to
662306a36Sopenharmony_ci * trigger various real hardware errors. Software based error
762306a36Sopenharmony_ci * injection can fake almost all kinds of errors with the help of a
862306a36Sopenharmony_ci * user space helper tool aer-inject, which can be gotten from:
962306a36Sopenharmony_ci *   https://git.kernel.org/cgit/linux/kernel/git/gong.chen/aer-inject.git/
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Copyright 2009 Intel Corporation.
1262306a36Sopenharmony_ci *     Huang Ying <ying.huang@intel.com>
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define dev_fmt(fmt) "aer_inject: " fmt
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/init.h>
1962306a36Sopenharmony_ci#include <linux/interrupt.h>
2062306a36Sopenharmony_ci#include <linux/miscdevice.h>
2162306a36Sopenharmony_ci#include <linux/pci.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <linux/fs.h>
2462306a36Sopenharmony_ci#include <linux/uaccess.h>
2562306a36Sopenharmony_ci#include <linux/stddef.h>
2662306a36Sopenharmony_ci#include <linux/device.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "portdrv.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Override the existing corrected and uncorrected error masks */
3162306a36Sopenharmony_cistatic bool aer_mask_override;
3262306a36Sopenharmony_cimodule_param(aer_mask_override, bool, 0);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct aer_error_inj {
3562306a36Sopenharmony_ci	u8 bus;
3662306a36Sopenharmony_ci	u8 dev;
3762306a36Sopenharmony_ci	u8 fn;
3862306a36Sopenharmony_ci	u32 uncor_status;
3962306a36Sopenharmony_ci	u32 cor_status;
4062306a36Sopenharmony_ci	u32 header_log0;
4162306a36Sopenharmony_ci	u32 header_log1;
4262306a36Sopenharmony_ci	u32 header_log2;
4362306a36Sopenharmony_ci	u32 header_log3;
4462306a36Sopenharmony_ci	u32 domain;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct aer_error {
4862306a36Sopenharmony_ci	struct list_head list;
4962306a36Sopenharmony_ci	u32 domain;
5062306a36Sopenharmony_ci	unsigned int bus;
5162306a36Sopenharmony_ci	unsigned int devfn;
5262306a36Sopenharmony_ci	int pos_cap_err;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	u32 uncor_status;
5562306a36Sopenharmony_ci	u32 cor_status;
5662306a36Sopenharmony_ci	u32 header_log0;
5762306a36Sopenharmony_ci	u32 header_log1;
5862306a36Sopenharmony_ci	u32 header_log2;
5962306a36Sopenharmony_ci	u32 header_log3;
6062306a36Sopenharmony_ci	u32 root_status;
6162306a36Sopenharmony_ci	u32 source_id;
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistruct pci_bus_ops {
6562306a36Sopenharmony_ci	struct list_head list;
6662306a36Sopenharmony_ci	struct pci_bus *bus;
6762306a36Sopenharmony_ci	struct pci_ops *ops;
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic LIST_HEAD(einjected);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic LIST_HEAD(pci_bus_ops_list);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* Protect einjected and pci_bus_ops_list */
7562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(inject_lock);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void aer_error_init(struct aer_error *err, u32 domain,
7862306a36Sopenharmony_ci			   unsigned int bus, unsigned int devfn,
7962306a36Sopenharmony_ci			   int pos_cap_err)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	INIT_LIST_HEAD(&err->list);
8262306a36Sopenharmony_ci	err->domain = domain;
8362306a36Sopenharmony_ci	err->bus = bus;
8462306a36Sopenharmony_ci	err->devfn = devfn;
8562306a36Sopenharmony_ci	err->pos_cap_err = pos_cap_err;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* inject_lock must be held before calling */
8962306a36Sopenharmony_cistatic struct aer_error *__find_aer_error(u32 domain, unsigned int bus,
9062306a36Sopenharmony_ci					  unsigned int devfn)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct aer_error *err;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	list_for_each_entry(err, &einjected, list) {
9562306a36Sopenharmony_ci		if (domain == err->domain &&
9662306a36Sopenharmony_ci		    bus == err->bus &&
9762306a36Sopenharmony_ci		    devfn == err->devfn)
9862306a36Sopenharmony_ci			return err;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	return NULL;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* inject_lock must be held before calling */
10462306a36Sopenharmony_cistatic struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int domain = pci_domain_nr(dev->bus);
10762306a36Sopenharmony_ci	if (domain < 0)
10862306a36Sopenharmony_ci		return NULL;
10962306a36Sopenharmony_ci	return __find_aer_error(domain, dev->bus->number, dev->devfn);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/* inject_lock must be held before calling */
11362306a36Sopenharmony_cistatic struct pci_ops *__find_pci_bus_ops(struct pci_bus *bus)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct pci_bus_ops *bus_ops;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	list_for_each_entry(bus_ops, &pci_bus_ops_list, list) {
11862306a36Sopenharmony_ci		if (bus_ops->bus == bus)
11962306a36Sopenharmony_ci			return bus_ops->ops;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	return NULL;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic struct pci_bus_ops *pci_bus_ops_pop(void)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	unsigned long flags;
12762306a36Sopenharmony_ci	struct pci_bus_ops *bus_ops;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	spin_lock_irqsave(&inject_lock, flags);
13062306a36Sopenharmony_ci	bus_ops = list_first_entry_or_null(&pci_bus_ops_list,
13162306a36Sopenharmony_ci					   struct pci_bus_ops, list);
13262306a36Sopenharmony_ci	if (bus_ops)
13362306a36Sopenharmony_ci		list_del(&bus_ops->list);
13462306a36Sopenharmony_ci	spin_unlock_irqrestore(&inject_lock, flags);
13562306a36Sopenharmony_ci	return bus_ops;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic u32 *find_pci_config_dword(struct aer_error *err, int where,
13962306a36Sopenharmony_ci				  int *prw1cs)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	int rw1cs = 0;
14262306a36Sopenharmony_ci	u32 *target = NULL;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (err->pos_cap_err == -1)
14562306a36Sopenharmony_ci		return NULL;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	switch (where - err->pos_cap_err) {
14862306a36Sopenharmony_ci	case PCI_ERR_UNCOR_STATUS:
14962306a36Sopenharmony_ci		target = &err->uncor_status;
15062306a36Sopenharmony_ci		rw1cs = 1;
15162306a36Sopenharmony_ci		break;
15262306a36Sopenharmony_ci	case PCI_ERR_COR_STATUS:
15362306a36Sopenharmony_ci		target = &err->cor_status;
15462306a36Sopenharmony_ci		rw1cs = 1;
15562306a36Sopenharmony_ci		break;
15662306a36Sopenharmony_ci	case PCI_ERR_HEADER_LOG:
15762306a36Sopenharmony_ci		target = &err->header_log0;
15862306a36Sopenharmony_ci		break;
15962306a36Sopenharmony_ci	case PCI_ERR_HEADER_LOG+4:
16062306a36Sopenharmony_ci		target = &err->header_log1;
16162306a36Sopenharmony_ci		break;
16262306a36Sopenharmony_ci	case PCI_ERR_HEADER_LOG+8:
16362306a36Sopenharmony_ci		target = &err->header_log2;
16462306a36Sopenharmony_ci		break;
16562306a36Sopenharmony_ci	case PCI_ERR_HEADER_LOG+12:
16662306a36Sopenharmony_ci		target = &err->header_log3;
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci	case PCI_ERR_ROOT_STATUS:
16962306a36Sopenharmony_ci		target = &err->root_status;
17062306a36Sopenharmony_ci		rw1cs = 1;
17162306a36Sopenharmony_ci		break;
17262306a36Sopenharmony_ci	case PCI_ERR_ROOT_ERR_SRC:
17362306a36Sopenharmony_ci		target = &err->source_id;
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	if (prw1cs)
17762306a36Sopenharmony_ci		*prw1cs = rw1cs;
17862306a36Sopenharmony_ci	return target;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int aer_inj_read(struct pci_bus *bus, unsigned int devfn, int where,
18262306a36Sopenharmony_ci			int size, u32 *val)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct pci_ops *ops, *my_ops;
18562306a36Sopenharmony_ci	int rv;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	ops = __find_pci_bus_ops(bus);
18862306a36Sopenharmony_ci	if (!ops)
18962306a36Sopenharmony_ci		return -1;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	my_ops = bus->ops;
19262306a36Sopenharmony_ci	bus->ops = ops;
19362306a36Sopenharmony_ci	rv = ops->read(bus, devfn, where, size, val);
19462306a36Sopenharmony_ci	bus->ops = my_ops;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return rv;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int aer_inj_write(struct pci_bus *bus, unsigned int devfn, int where,
20062306a36Sopenharmony_ci			 int size, u32 val)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct pci_ops *ops, *my_ops;
20362306a36Sopenharmony_ci	int rv;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ops = __find_pci_bus_ops(bus);
20662306a36Sopenharmony_ci	if (!ops)
20762306a36Sopenharmony_ci		return -1;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	my_ops = bus->ops;
21062306a36Sopenharmony_ci	bus->ops = ops;
21162306a36Sopenharmony_ci	rv = ops->write(bus, devfn, where, size, val);
21262306a36Sopenharmony_ci	bus->ops = my_ops;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return rv;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
21862306a36Sopenharmony_ci			       int where, int size, u32 *val)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	u32 *sim;
22162306a36Sopenharmony_ci	struct aer_error *err;
22262306a36Sopenharmony_ci	unsigned long flags;
22362306a36Sopenharmony_ci	int domain;
22462306a36Sopenharmony_ci	int rv;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	spin_lock_irqsave(&inject_lock, flags);
22762306a36Sopenharmony_ci	if (size != sizeof(u32))
22862306a36Sopenharmony_ci		goto out;
22962306a36Sopenharmony_ci	domain = pci_domain_nr(bus);
23062306a36Sopenharmony_ci	if (domain < 0)
23162306a36Sopenharmony_ci		goto out;
23262306a36Sopenharmony_ci	err = __find_aer_error(domain, bus->number, devfn);
23362306a36Sopenharmony_ci	if (!err)
23462306a36Sopenharmony_ci		goto out;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	sim = find_pci_config_dword(err, where, NULL);
23762306a36Sopenharmony_ci	if (sim) {
23862306a36Sopenharmony_ci		*val = *sim;
23962306a36Sopenharmony_ci		spin_unlock_irqrestore(&inject_lock, flags);
24062306a36Sopenharmony_ci		return 0;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ciout:
24362306a36Sopenharmony_ci	rv = aer_inj_read(bus, devfn, where, size, val);
24462306a36Sopenharmony_ci	spin_unlock_irqrestore(&inject_lock, flags);
24562306a36Sopenharmony_ci	return rv;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
24962306a36Sopenharmony_ci				int where, int size, u32 val)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	u32 *sim;
25262306a36Sopenharmony_ci	struct aer_error *err;
25362306a36Sopenharmony_ci	unsigned long flags;
25462306a36Sopenharmony_ci	int rw1cs;
25562306a36Sopenharmony_ci	int domain;
25662306a36Sopenharmony_ci	int rv;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	spin_lock_irqsave(&inject_lock, flags);
25962306a36Sopenharmony_ci	if (size != sizeof(u32))
26062306a36Sopenharmony_ci		goto out;
26162306a36Sopenharmony_ci	domain = pci_domain_nr(bus);
26262306a36Sopenharmony_ci	if (domain < 0)
26362306a36Sopenharmony_ci		goto out;
26462306a36Sopenharmony_ci	err = __find_aer_error(domain, bus->number, devfn);
26562306a36Sopenharmony_ci	if (!err)
26662306a36Sopenharmony_ci		goto out;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	sim = find_pci_config_dword(err, where, &rw1cs);
26962306a36Sopenharmony_ci	if (sim) {
27062306a36Sopenharmony_ci		if (rw1cs)
27162306a36Sopenharmony_ci			*sim ^= val;
27262306a36Sopenharmony_ci		else
27362306a36Sopenharmony_ci			*sim = val;
27462306a36Sopenharmony_ci		spin_unlock_irqrestore(&inject_lock, flags);
27562306a36Sopenharmony_ci		return 0;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ciout:
27862306a36Sopenharmony_ci	rv = aer_inj_write(bus, devfn, where, size, val);
27962306a36Sopenharmony_ci	spin_unlock_irqrestore(&inject_lock, flags);
28062306a36Sopenharmony_ci	return rv;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic struct pci_ops aer_inj_pci_ops = {
28462306a36Sopenharmony_ci	.read = aer_inj_read_config,
28562306a36Sopenharmony_ci	.write = aer_inj_write_config,
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void pci_bus_ops_init(struct pci_bus_ops *bus_ops,
28962306a36Sopenharmony_ci			     struct pci_bus *bus,
29062306a36Sopenharmony_ci			     struct pci_ops *ops)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	INIT_LIST_HEAD(&bus_ops->list);
29362306a36Sopenharmony_ci	bus_ops->bus = bus;
29462306a36Sopenharmony_ci	bus_ops->ops = ops;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int pci_bus_set_aer_ops(struct pci_bus *bus)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct pci_ops *ops;
30062306a36Sopenharmony_ci	struct pci_bus_ops *bus_ops;
30162306a36Sopenharmony_ci	unsigned long flags;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	bus_ops = kmalloc(sizeof(*bus_ops), GFP_KERNEL);
30462306a36Sopenharmony_ci	if (!bus_ops)
30562306a36Sopenharmony_ci		return -ENOMEM;
30662306a36Sopenharmony_ci	ops = pci_bus_set_ops(bus, &aer_inj_pci_ops);
30762306a36Sopenharmony_ci	spin_lock_irqsave(&inject_lock, flags);
30862306a36Sopenharmony_ci	if (ops == &aer_inj_pci_ops)
30962306a36Sopenharmony_ci		goto out;
31062306a36Sopenharmony_ci	pci_bus_ops_init(bus_ops, bus, ops);
31162306a36Sopenharmony_ci	list_add(&bus_ops->list, &pci_bus_ops_list);
31262306a36Sopenharmony_ci	bus_ops = NULL;
31362306a36Sopenharmony_ciout:
31462306a36Sopenharmony_ci	spin_unlock_irqrestore(&inject_lock, flags);
31562306a36Sopenharmony_ci	kfree(bus_ops);
31662306a36Sopenharmony_ci	return 0;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int aer_inject(struct aer_error_inj *einj)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct aer_error *err, *rperr;
32262306a36Sopenharmony_ci	struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
32362306a36Sopenharmony_ci	struct pci_dev *dev, *rpdev;
32462306a36Sopenharmony_ci	struct pcie_device *edev;
32562306a36Sopenharmony_ci	struct device *device;
32662306a36Sopenharmony_ci	unsigned long flags;
32762306a36Sopenharmony_ci	unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn);
32862306a36Sopenharmony_ci	int pos_cap_err, rp_pos_cap_err;
32962306a36Sopenharmony_ci	u32 sever, cor_mask, uncor_mask, cor_mask_orig = 0, uncor_mask_orig = 0;
33062306a36Sopenharmony_ci	int ret = 0;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn);
33362306a36Sopenharmony_ci	if (!dev)
33462306a36Sopenharmony_ci		return -ENODEV;
33562306a36Sopenharmony_ci	rpdev = pcie_find_root_port(dev);
33662306a36Sopenharmony_ci	/* If Root Port not found, try to find an RCEC */
33762306a36Sopenharmony_ci	if (!rpdev)
33862306a36Sopenharmony_ci		rpdev = dev->rcec;
33962306a36Sopenharmony_ci	if (!rpdev) {
34062306a36Sopenharmony_ci		pci_err(dev, "Neither Root Port nor RCEC found\n");
34162306a36Sopenharmony_ci		ret = -ENODEV;
34262306a36Sopenharmony_ci		goto out_put;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	pos_cap_err = dev->aer_cap;
34662306a36Sopenharmony_ci	if (!pos_cap_err) {
34762306a36Sopenharmony_ci		pci_err(dev, "Device doesn't support AER\n");
34862306a36Sopenharmony_ci		ret = -EPROTONOSUPPORT;
34962306a36Sopenharmony_ci		goto out_put;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci	pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever);
35262306a36Sopenharmony_ci	pci_read_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, &cor_mask);
35362306a36Sopenharmony_ci	pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
35462306a36Sopenharmony_ci			      &uncor_mask);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	rp_pos_cap_err = rpdev->aer_cap;
35762306a36Sopenharmony_ci	if (!rp_pos_cap_err) {
35862306a36Sopenharmony_ci		pci_err(rpdev, "Root port doesn't support AER\n");
35962306a36Sopenharmony_ci		ret = -EPROTONOSUPPORT;
36062306a36Sopenharmony_ci		goto out_put;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	err_alloc =  kzalloc(sizeof(struct aer_error), GFP_KERNEL);
36462306a36Sopenharmony_ci	if (!err_alloc) {
36562306a36Sopenharmony_ci		ret = -ENOMEM;
36662306a36Sopenharmony_ci		goto out_put;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	rperr_alloc =  kzalloc(sizeof(struct aer_error), GFP_KERNEL);
36962306a36Sopenharmony_ci	if (!rperr_alloc) {
37062306a36Sopenharmony_ci		ret = -ENOMEM;
37162306a36Sopenharmony_ci		goto out_put;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (aer_mask_override) {
37562306a36Sopenharmony_ci		cor_mask_orig = cor_mask;
37662306a36Sopenharmony_ci		cor_mask &= !(einj->cor_status);
37762306a36Sopenharmony_ci		pci_write_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK,
37862306a36Sopenharmony_ci				       cor_mask);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		uncor_mask_orig = uncor_mask;
38162306a36Sopenharmony_ci		uncor_mask &= !(einj->uncor_status);
38262306a36Sopenharmony_ci		pci_write_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
38362306a36Sopenharmony_ci				       uncor_mask);
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	spin_lock_irqsave(&inject_lock, flags);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	err = __find_aer_error_by_dev(dev);
38962306a36Sopenharmony_ci	if (!err) {
39062306a36Sopenharmony_ci		err = err_alloc;
39162306a36Sopenharmony_ci		err_alloc = NULL;
39262306a36Sopenharmony_ci		aer_error_init(err, einj->domain, einj->bus, devfn,
39362306a36Sopenharmony_ci			       pos_cap_err);
39462306a36Sopenharmony_ci		list_add(&err->list, &einjected);
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	err->uncor_status |= einj->uncor_status;
39762306a36Sopenharmony_ci	err->cor_status |= einj->cor_status;
39862306a36Sopenharmony_ci	err->header_log0 = einj->header_log0;
39962306a36Sopenharmony_ci	err->header_log1 = einj->header_log1;
40062306a36Sopenharmony_ci	err->header_log2 = einj->header_log2;
40162306a36Sopenharmony_ci	err->header_log3 = einj->header_log3;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (!aer_mask_override && einj->cor_status &&
40462306a36Sopenharmony_ci	    !(einj->cor_status & ~cor_mask)) {
40562306a36Sopenharmony_ci		ret = -EINVAL;
40662306a36Sopenharmony_ci		pci_warn(dev, "The correctable error(s) is masked by device\n");
40762306a36Sopenharmony_ci		spin_unlock_irqrestore(&inject_lock, flags);
40862306a36Sopenharmony_ci		goto out_put;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci	if (!aer_mask_override && einj->uncor_status &&
41162306a36Sopenharmony_ci	    !(einj->uncor_status & ~uncor_mask)) {
41262306a36Sopenharmony_ci		ret = -EINVAL;
41362306a36Sopenharmony_ci		pci_warn(dev, "The uncorrectable error(s) is masked by device\n");
41462306a36Sopenharmony_ci		spin_unlock_irqrestore(&inject_lock, flags);
41562306a36Sopenharmony_ci		goto out_put;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	rperr = __find_aer_error_by_dev(rpdev);
41962306a36Sopenharmony_ci	if (!rperr) {
42062306a36Sopenharmony_ci		rperr = rperr_alloc;
42162306a36Sopenharmony_ci		rperr_alloc = NULL;
42262306a36Sopenharmony_ci		aer_error_init(rperr, pci_domain_nr(rpdev->bus),
42362306a36Sopenharmony_ci			       rpdev->bus->number, rpdev->devfn,
42462306a36Sopenharmony_ci			       rp_pos_cap_err);
42562306a36Sopenharmony_ci		list_add(&rperr->list, &einjected);
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci	if (einj->cor_status) {
42862306a36Sopenharmony_ci		if (rperr->root_status & PCI_ERR_ROOT_COR_RCV)
42962306a36Sopenharmony_ci			rperr->root_status |= PCI_ERR_ROOT_MULTI_COR_RCV;
43062306a36Sopenharmony_ci		else
43162306a36Sopenharmony_ci			rperr->root_status |= PCI_ERR_ROOT_COR_RCV;
43262306a36Sopenharmony_ci		rperr->source_id &= 0xffff0000;
43362306a36Sopenharmony_ci		rperr->source_id |= (einj->bus << 8) | devfn;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci	if (einj->uncor_status) {
43662306a36Sopenharmony_ci		if (rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV)
43762306a36Sopenharmony_ci			rperr->root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV;
43862306a36Sopenharmony_ci		if (sever & einj->uncor_status) {
43962306a36Sopenharmony_ci			rperr->root_status |= PCI_ERR_ROOT_FATAL_RCV;
44062306a36Sopenharmony_ci			if (!(rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV))
44162306a36Sopenharmony_ci				rperr->root_status |= PCI_ERR_ROOT_FIRST_FATAL;
44262306a36Sopenharmony_ci		} else
44362306a36Sopenharmony_ci			rperr->root_status |= PCI_ERR_ROOT_NONFATAL_RCV;
44462306a36Sopenharmony_ci		rperr->root_status |= PCI_ERR_ROOT_UNCOR_RCV;
44562306a36Sopenharmony_ci		rperr->source_id &= 0x0000ffff;
44662306a36Sopenharmony_ci		rperr->source_id |= ((einj->bus << 8) | devfn) << 16;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci	spin_unlock_irqrestore(&inject_lock, flags);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (aer_mask_override) {
45162306a36Sopenharmony_ci		pci_write_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK,
45262306a36Sopenharmony_ci				       cor_mask_orig);
45362306a36Sopenharmony_ci		pci_write_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
45462306a36Sopenharmony_ci				       uncor_mask_orig);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	ret = pci_bus_set_aer_ops(dev->bus);
45862306a36Sopenharmony_ci	if (ret)
45962306a36Sopenharmony_ci		goto out_put;
46062306a36Sopenharmony_ci	ret = pci_bus_set_aer_ops(rpdev->bus);
46162306a36Sopenharmony_ci	if (ret)
46262306a36Sopenharmony_ci		goto out_put;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	device = pcie_port_find_device(rpdev, PCIE_PORT_SERVICE_AER);
46562306a36Sopenharmony_ci	if (device) {
46662306a36Sopenharmony_ci		edev = to_pcie_device(device);
46762306a36Sopenharmony_ci		if (!get_service_data(edev)) {
46862306a36Sopenharmony_ci			pci_warn(edev->port, "AER service is not initialized\n");
46962306a36Sopenharmony_ci			ret = -EPROTONOSUPPORT;
47062306a36Sopenharmony_ci			goto out_put;
47162306a36Sopenharmony_ci		}
47262306a36Sopenharmony_ci		pci_info(edev->port, "Injecting errors %08x/%08x into device %s\n",
47362306a36Sopenharmony_ci			 einj->cor_status, einj->uncor_status, pci_name(dev));
47462306a36Sopenharmony_ci		ret = irq_inject_interrupt(edev->irq);
47562306a36Sopenharmony_ci	} else {
47662306a36Sopenharmony_ci		pci_err(rpdev, "AER device not found\n");
47762306a36Sopenharmony_ci		ret = -ENODEV;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ciout_put:
48062306a36Sopenharmony_ci	kfree(err_alloc);
48162306a36Sopenharmony_ci	kfree(rperr_alloc);
48262306a36Sopenharmony_ci	pci_dev_put(dev);
48362306a36Sopenharmony_ci	return ret;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic ssize_t aer_inject_write(struct file *filp, const char __user *ubuf,
48762306a36Sopenharmony_ci				size_t usize, loff_t *off)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct aer_error_inj einj;
49062306a36Sopenharmony_ci	int ret;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
49362306a36Sopenharmony_ci		return -EPERM;
49462306a36Sopenharmony_ci	if (usize < offsetof(struct aer_error_inj, domain) ||
49562306a36Sopenharmony_ci	    usize > sizeof(einj))
49662306a36Sopenharmony_ci		return -EINVAL;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	memset(&einj, 0, sizeof(einj));
49962306a36Sopenharmony_ci	if (copy_from_user(&einj, ubuf, usize))
50062306a36Sopenharmony_ci		return -EFAULT;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	ret = aer_inject(&einj);
50362306a36Sopenharmony_ci	return ret ? ret : usize;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic const struct file_operations aer_inject_fops = {
50762306a36Sopenharmony_ci	.write = aer_inject_write,
50862306a36Sopenharmony_ci	.owner = THIS_MODULE,
50962306a36Sopenharmony_ci	.llseek = noop_llseek,
51062306a36Sopenharmony_ci};
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic struct miscdevice aer_inject_device = {
51362306a36Sopenharmony_ci	.minor = MISC_DYNAMIC_MINOR,
51462306a36Sopenharmony_ci	.name = "aer_inject",
51562306a36Sopenharmony_ci	.fops = &aer_inject_fops,
51662306a36Sopenharmony_ci};
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int __init aer_inject_init(void)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	return misc_register(&aer_inject_device);
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic void __exit aer_inject_exit(void)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct aer_error *err, *err_next;
52662306a36Sopenharmony_ci	unsigned long flags;
52762306a36Sopenharmony_ci	struct pci_bus_ops *bus_ops;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	misc_deregister(&aer_inject_device);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	while ((bus_ops = pci_bus_ops_pop())) {
53262306a36Sopenharmony_ci		pci_bus_set_ops(bus_ops->bus, bus_ops->ops);
53362306a36Sopenharmony_ci		kfree(bus_ops);
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	spin_lock_irqsave(&inject_lock, flags);
53762306a36Sopenharmony_ci	list_for_each_entry_safe(err, err_next, &einjected, list) {
53862306a36Sopenharmony_ci		list_del(&err->list);
53962306a36Sopenharmony_ci		kfree(err);
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci	spin_unlock_irqrestore(&inject_lock, flags);
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cimodule_init(aer_inject_init);
54562306a36Sopenharmony_cimodule_exit(aer_inject_exit);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ciMODULE_DESCRIPTION("PCIe AER software error injector");
54862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
549