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