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