• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/pci/pcie/aer/
1/*
2 * PCIe AER software error injection support.
3 *
4 * Debuging PCIe AER code is quite difficult because it is hard to
5 * trigger various real hardware errors. Software based error
6 * injection can fake almost all kinds of errors with the help of a
7 * user space helper tool aer-inject, which can be gotten from:
8 *   http://www.kernel.org/pub/linux/utils/pci/aer-inject/
9 *
10 * Copyright 2009 Intel Corporation.
11 *     Huang Ying <ying.huang@intel.com>
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; version 2
16 * of the License.
17 *
18 */
19
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/miscdevice.h>
23#include <linux/pci.h>
24#include <linux/slab.h>
25#include <linux/fs.h>
26#include <linux/uaccess.h>
27#include <linux/stddef.h>
28#include "aerdrv.h"
29
30struct aer_error_inj {
31	u8 bus;
32	u8 dev;
33	u8 fn;
34	u32 uncor_status;
35	u32 cor_status;
36	u32 header_log0;
37	u32 header_log1;
38	u32 header_log2;
39	u32 header_log3;
40	u16 domain;
41};
42
43struct aer_error {
44	struct list_head list;
45	u16 domain;
46	unsigned int bus;
47	unsigned int devfn;
48	int pos_cap_err;
49
50	u32 uncor_status;
51	u32 cor_status;
52	u32 header_log0;
53	u32 header_log1;
54	u32 header_log2;
55	u32 header_log3;
56	u32 root_status;
57	u32 source_id;
58};
59
60struct pci_bus_ops {
61	struct list_head list;
62	struct pci_bus *bus;
63	struct pci_ops *ops;
64};
65
66static LIST_HEAD(einjected);
67
68static LIST_HEAD(pci_bus_ops_list);
69
70/* Protect einjected and pci_bus_ops_list */
71static DEFINE_SPINLOCK(inject_lock);
72
73static void aer_error_init(struct aer_error *err, u16 domain,
74			   unsigned int bus, unsigned int devfn,
75			   int pos_cap_err)
76{
77	INIT_LIST_HEAD(&err->list);
78	err->domain = domain;
79	err->bus = bus;
80	err->devfn = devfn;
81	err->pos_cap_err = pos_cap_err;
82}
83
84/* inject_lock must be held before calling */
85static struct aer_error *__find_aer_error(u16 domain, unsigned int bus,
86					  unsigned int devfn)
87{
88	struct aer_error *err;
89
90	list_for_each_entry(err, &einjected, list) {
91		if (domain == err->domain &&
92		    bus == err->bus &&
93		    devfn == err->devfn)
94			return err;
95	}
96	return NULL;
97}
98
99/* inject_lock must be held before calling */
100static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev)
101{
102	int domain = pci_domain_nr(dev->bus);
103	if (domain < 0)
104		return NULL;
105	return __find_aer_error((u16)domain, dev->bus->number, dev->devfn);
106}
107
108/* inject_lock must be held before calling */
109static struct pci_ops *__find_pci_bus_ops(struct pci_bus *bus)
110{
111	struct pci_bus_ops *bus_ops;
112
113	list_for_each_entry(bus_ops, &pci_bus_ops_list, list) {
114		if (bus_ops->bus == bus)
115			return bus_ops->ops;
116	}
117	return NULL;
118}
119
120static struct pci_bus_ops *pci_bus_ops_pop(void)
121{
122	unsigned long flags;
123	struct pci_bus_ops *bus_ops = NULL;
124
125	spin_lock_irqsave(&inject_lock, flags);
126	if (list_empty(&pci_bus_ops_list))
127		bus_ops = NULL;
128	else {
129		struct list_head *lh = pci_bus_ops_list.next;
130		list_del(lh);
131		bus_ops = list_entry(lh, struct pci_bus_ops, list);
132	}
133	spin_unlock_irqrestore(&inject_lock, flags);
134	return bus_ops;
135}
136
137static u32 *find_pci_config_dword(struct aer_error *err, int where,
138				  int *prw1cs)
139{
140	int rw1cs = 0;
141	u32 *target = NULL;
142
143	if (err->pos_cap_err == -1)
144		return NULL;
145
146	switch (where - err->pos_cap_err) {
147	case PCI_ERR_UNCOR_STATUS:
148		target = &err->uncor_status;
149		rw1cs = 1;
150		break;
151	case PCI_ERR_COR_STATUS:
152		target = &err->cor_status;
153		rw1cs = 1;
154		break;
155	case PCI_ERR_HEADER_LOG:
156		target = &err->header_log0;
157		break;
158	case PCI_ERR_HEADER_LOG+4:
159		target = &err->header_log1;
160		break;
161	case PCI_ERR_HEADER_LOG+8:
162		target = &err->header_log2;
163		break;
164	case PCI_ERR_HEADER_LOG+12:
165		target = &err->header_log3;
166		break;
167	case PCI_ERR_ROOT_STATUS:
168		target = &err->root_status;
169		rw1cs = 1;
170		break;
171	case PCI_ERR_ROOT_ERR_SRC:
172		target = &err->source_id;
173		break;
174	}
175	if (prw1cs)
176		*prw1cs = rw1cs;
177	return target;
178}
179
180static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where,
181			int size, u32 *val)
182{
183	u32 *sim;
184	struct aer_error *err;
185	unsigned long flags;
186	struct pci_ops *ops;
187	int domain;
188
189	spin_lock_irqsave(&inject_lock, flags);
190	if (size != sizeof(u32))
191		goto out;
192	domain = pci_domain_nr(bus);
193	if (domain < 0)
194		goto out;
195	err = __find_aer_error((u16)domain, bus->number, devfn);
196	if (!err)
197		goto out;
198
199	sim = find_pci_config_dword(err, where, NULL);
200	if (sim) {
201		*val = *sim;
202		spin_unlock_irqrestore(&inject_lock, flags);
203		return 0;
204	}
205out:
206	ops = __find_pci_bus_ops(bus);
207	spin_unlock_irqrestore(&inject_lock, flags);
208	return ops->read(bus, devfn, where, size, val);
209}
210
211int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where, int size,
212		  u32 val)
213{
214	u32 *sim;
215	struct aer_error *err;
216	unsigned long flags;
217	int rw1cs;
218	struct pci_ops *ops;
219	int domain;
220
221	spin_lock_irqsave(&inject_lock, flags);
222	if (size != sizeof(u32))
223		goto out;
224	domain = pci_domain_nr(bus);
225	if (domain < 0)
226		goto out;
227	err = __find_aer_error((u16)domain, bus->number, devfn);
228	if (!err)
229		goto out;
230
231	sim = find_pci_config_dword(err, where, &rw1cs);
232	if (sim) {
233		if (rw1cs)
234			*sim ^= val;
235		else
236			*sim = val;
237		spin_unlock_irqrestore(&inject_lock, flags);
238		return 0;
239	}
240out:
241	ops = __find_pci_bus_ops(bus);
242	spin_unlock_irqrestore(&inject_lock, flags);
243	return ops->write(bus, devfn, where, size, val);
244}
245
246static struct pci_ops pci_ops_aer = {
247	.read = pci_read_aer,
248	.write = pci_write_aer,
249};
250
251static void pci_bus_ops_init(struct pci_bus_ops *bus_ops,
252			     struct pci_bus *bus,
253			     struct pci_ops *ops)
254{
255	INIT_LIST_HEAD(&bus_ops->list);
256	bus_ops->bus = bus;
257	bus_ops->ops = ops;
258}
259
260static int pci_bus_set_aer_ops(struct pci_bus *bus)
261{
262	struct pci_ops *ops;
263	struct pci_bus_ops *bus_ops;
264	unsigned long flags;
265
266	bus_ops = kmalloc(sizeof(*bus_ops), GFP_KERNEL);
267	if (!bus_ops)
268		return -ENOMEM;
269	ops = pci_bus_set_ops(bus, &pci_ops_aer);
270	spin_lock_irqsave(&inject_lock, flags);
271	if (ops == &pci_ops_aer)
272		goto out;
273	pci_bus_ops_init(bus_ops, bus, ops);
274	list_add(&bus_ops->list, &pci_bus_ops_list);
275	bus_ops = NULL;
276out:
277	spin_unlock_irqrestore(&inject_lock, flags);
278	kfree(bus_ops);
279	return 0;
280}
281
282static struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
283{
284	while (1) {
285		if (!pci_is_pcie(dev))
286			break;
287		if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
288			return dev;
289		if (!dev->bus->self)
290			break;
291		dev = dev->bus->self;
292	}
293	return NULL;
294}
295
296static int find_aer_device_iter(struct device *device, void *data)
297{
298	struct pcie_device **result = data;
299	struct pcie_device *pcie_dev;
300
301	if (device->bus == &pcie_port_bus_type) {
302		pcie_dev = to_pcie_device(device);
303		if (pcie_dev->service & PCIE_PORT_SERVICE_AER) {
304			*result = pcie_dev;
305			return 1;
306		}
307	}
308	return 0;
309}
310
311static int find_aer_device(struct pci_dev *dev, struct pcie_device **result)
312{
313	return device_for_each_child(&dev->dev, result, find_aer_device_iter);
314}
315
316static int aer_inject(struct aer_error_inj *einj)
317{
318	struct aer_error *err, *rperr;
319	struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
320	struct pci_dev *dev, *rpdev;
321	struct pcie_device *edev;
322	unsigned long flags;
323	unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn);
324	int pos_cap_err, rp_pos_cap_err;
325	u32 sever, cor_mask, uncor_mask;
326	int ret = 0;
327
328	dev = pci_get_domain_bus_and_slot((int)einj->domain, einj->bus, devfn);
329	if (!dev)
330		return -ENODEV;
331	rpdev = pcie_find_root_port(dev);
332	if (!rpdev) {
333		ret = -ENOTTY;
334		goto out_put;
335	}
336
337	pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
338	if (!pos_cap_err) {
339		ret = -ENOTTY;
340		goto out_put;
341	}
342	pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever);
343	pci_read_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, &cor_mask);
344	pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
345			      &uncor_mask);
346
347	rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR);
348	if (!rp_pos_cap_err) {
349		ret = -ENOTTY;
350		goto out_put;
351	}
352
353	err_alloc =  kzalloc(sizeof(struct aer_error), GFP_KERNEL);
354	if (!err_alloc) {
355		ret = -ENOMEM;
356		goto out_put;
357	}
358	rperr_alloc =  kzalloc(sizeof(struct aer_error), GFP_KERNEL);
359	if (!rperr_alloc) {
360		ret = -ENOMEM;
361		goto out_put;
362	}
363
364	spin_lock_irqsave(&inject_lock, flags);
365
366	err = __find_aer_error_by_dev(dev);
367	if (!err) {
368		err = err_alloc;
369		err_alloc = NULL;
370		aer_error_init(err, einj->domain, einj->bus, devfn,
371			       pos_cap_err);
372		list_add(&err->list, &einjected);
373	}
374	err->uncor_status |= einj->uncor_status;
375	err->cor_status |= einj->cor_status;
376	err->header_log0 = einj->header_log0;
377	err->header_log1 = einj->header_log1;
378	err->header_log2 = einj->header_log2;
379	err->header_log3 = einj->header_log3;
380
381	if (einj->cor_status && !(einj->cor_status & ~cor_mask)) {
382		ret = -EINVAL;
383		printk(KERN_WARNING "The correctable error(s) is masked "
384				"by device\n");
385		spin_unlock_irqrestore(&inject_lock, flags);
386		goto out_put;
387	}
388	if (einj->uncor_status && !(einj->uncor_status & ~uncor_mask)) {
389		ret = -EINVAL;
390		printk(KERN_WARNING "The uncorrectable error(s) is masked "
391				"by device\n");
392		spin_unlock_irqrestore(&inject_lock, flags);
393		goto out_put;
394	}
395
396	rperr = __find_aer_error_by_dev(rpdev);
397	if (!rperr) {
398		rperr = rperr_alloc;
399		rperr_alloc = NULL;
400		aer_error_init(rperr, pci_domain_nr(rpdev->bus),
401			       rpdev->bus->number, rpdev->devfn,
402			       rp_pos_cap_err);
403		list_add(&rperr->list, &einjected);
404	}
405	if (einj->cor_status) {
406		if (rperr->root_status & PCI_ERR_ROOT_COR_RCV)
407			rperr->root_status |= PCI_ERR_ROOT_MULTI_COR_RCV;
408		else
409			rperr->root_status |= PCI_ERR_ROOT_COR_RCV;
410		rperr->source_id &= 0xffff0000;
411		rperr->source_id |= (einj->bus << 8) | devfn;
412	}
413	if (einj->uncor_status) {
414		if (rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV)
415			rperr->root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV;
416		if (sever & einj->uncor_status) {
417			rperr->root_status |= PCI_ERR_ROOT_FATAL_RCV;
418			if (!(rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV))
419				rperr->root_status |= PCI_ERR_ROOT_FIRST_FATAL;
420		} else
421			rperr->root_status |= PCI_ERR_ROOT_NONFATAL_RCV;
422		rperr->root_status |= PCI_ERR_ROOT_UNCOR_RCV;
423		rperr->source_id &= 0x0000ffff;
424		rperr->source_id |= ((einj->bus << 8) | devfn) << 16;
425	}
426	spin_unlock_irqrestore(&inject_lock, flags);
427
428	ret = pci_bus_set_aer_ops(dev->bus);
429	if (ret)
430		goto out_put;
431	ret = pci_bus_set_aer_ops(rpdev->bus);
432	if (ret)
433		goto out_put;
434
435	if (find_aer_device(rpdev, &edev)) {
436		if (!get_service_data(edev)) {
437			printk(KERN_WARNING "AER service is not initialized\n");
438			ret = -EINVAL;
439			goto out_put;
440		}
441		aer_irq(-1, edev);
442	}
443	else
444		ret = -EINVAL;
445out_put:
446	kfree(err_alloc);
447	kfree(rperr_alloc);
448	pci_dev_put(dev);
449	return ret;
450}
451
452static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf,
453				size_t usize, loff_t *off)
454{
455	struct aer_error_inj einj;
456	int ret;
457
458	if (!capable(CAP_SYS_ADMIN))
459		return -EPERM;
460	if (usize < offsetof(struct aer_error_inj, domain) ||
461	    usize > sizeof(einj))
462		return -EINVAL;
463
464	memset(&einj, 0, sizeof(einj));
465	if (copy_from_user(&einj, ubuf, usize))
466		return -EFAULT;
467
468	ret = aer_inject(&einj);
469	return ret ? ret : usize;
470}
471
472static const struct file_operations aer_inject_fops = {
473	.write = aer_inject_write,
474	.owner = THIS_MODULE,
475};
476
477static struct miscdevice aer_inject_device = {
478	.minor = MISC_DYNAMIC_MINOR,
479	.name = "aer_inject",
480	.fops = &aer_inject_fops,
481};
482
483static int __init aer_inject_init(void)
484{
485	return misc_register(&aer_inject_device);
486}
487
488static void __exit aer_inject_exit(void)
489{
490	struct aer_error *err, *err_next;
491	unsigned long flags;
492	struct pci_bus_ops *bus_ops;
493
494	misc_deregister(&aer_inject_device);
495
496	while ((bus_ops = pci_bus_ops_pop())) {
497		pci_bus_set_ops(bus_ops->bus, bus_ops->ops);
498		kfree(bus_ops);
499	}
500
501	spin_lock_irqsave(&inject_lock, flags);
502	list_for_each_entry_safe(err, err_next, &einjected, list) {
503		list_del(&err->list);
504		kfree(err);
505	}
506	spin_unlock_irqrestore(&inject_lock, flags);
507}
508
509module_init(aer_inject_init);
510module_exit(aer_inject_exit);
511
512MODULE_DESCRIPTION("PCIe AER software error injector");
513MODULE_LICENSE("GPL");
514