一、模块背景
调试PCIe AER错误恢复代码非常困难,因为它很难触发真正的硬件错误。基于软件的错误注入可用于伪造各种PCIe错误。
首先,应该在内核中启用PCIe AER软件错误注入配置,即以下应位于.config中的项目。
CONFIG_PCIEAER_INJECT = y或CONFIG_PCIEAER_INJECT = m
使用新内核重新启动或insmod模块后,名为设备文件/ dev / aer_inject 会被创建。
然后,需要一个名为aer-inject的用户空间工具,
该工具可以从此获取:git clone https://git.kernel.org/pub/scm/linux/kernel/git/gong.chen/aer-inject.git
Tips:
使用aer-inject工具的环境不能是虚拟机环境!!!
二、驱动介绍
linux内核版本:4.9.190。
aer inject的驱动文件:aer_inject.c,位于[drivers\pci\pcie\aer]。
模块的初始化入口:aer_inject_init,直接注册了一个混杂设备驱动。
static int __init aer_inject_init(void)
return misc_register(&aer_inject_device);
然后直接找到file_operations结构体,主要增加了aer_inject_write这个写接口。
static struct miscdevice aer_inject_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "aer_inject",
.fops = &aer_inject_fops,
static const struct file_operations aer_inject_fops = {
.write = aer_inject_write,
.owner = THIS_MODULE,
.llseek = noop_llseek,
当用户调用设备节点进行写操作时,aer_inject_write接口函数将被调用,aer_inject_write里首先检查权限,只有root用户才能正常操作,然后将用户空间的程序写的内容复制到内核空间,最后调用aer_inject函数继续处理。
在函数aer_inject里,首先调用pci_get_domain_bus_and_slot寻找需要inject设备的pci_dev结构体。
dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn);
if (!dev)
return -ENODEV;
rpdev = pcie_find_root_port(dev);
if (!rpdev) {
dev_err(&dev->dev, "aer_inject: Root port not found\n");
ret = -ENODEV;
goto out_put;
然后再寻找根端口的pci_dev结构体。
接下来调用pci_find_ext_capability来确认是否支持AER功能。
pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (!pos_cap_err) {
dev_err(&dev->dev, "aer_inject: Device doesn't support AER\n");
ret = -EPROTONOSUPPORT;
goto out_put;
pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever);
pci_read_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, &cor_mask);
pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
&uncor_mask);
rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR);
if (!rp_pos_cap_err) {
dev_err(&rpdev->dev,
"aer_inject: Root port doesn't support AER\n");
ret = -EPROTONOSUPPORT;
goto out_put;
然后调用__find_aer_error_by_dev函数寻找dev设备的aer_error结构体,并对其内容进行设置。
err = __find_aer_error_by_dev(dev);
if (!err) {
err = err_alloc;
err_alloc = NULL;
aer_error_init(err, einj->domain, einj->bus, devfn,
pos_cap_err);
list_add(&err->list, &einjected);
err->uncor_status |= einj->uncor_status;
err->cor_status |= einj->cor_status;
err->header_log0 = einj->header_log0;
err->header_log1 = einj->header_log1;
err->header_log2 = einj->header_log2;
err->header_log3 = einj->header_log3;
接下来寻找根端口设备的aer_error结构体,并对可校正错误和不可校正错误的状态进行设置。
rperr = __find_aer_error_by_dev(rpdev);
if (!rperr) {
rperr = rperr_alloc;
rperr_alloc = NULL;
aer_error_init(rperr, pci_domain_nr(rpdev->bus),
rpdev->bus->number, rpdev->devfn,
rp_pos_cap_err);
list_add(&rperr->list, &einjected);
if (einj->cor_status) {
if (rperr->root_status & PCI_ERR_ROOT_COR_RCV)
rperr->root_status |= PCI_ERR_ROOT_MULTI_COR_RCV;
rperr->root_status |= PCI_ERR_ROOT_COR_RCV;
rperr->source_id &= 0xffff0000;
rperr->source_id |= (einj->bus << 8) | devfn;
if (einj->uncor_status) {
if (rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV)
rperr->root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV;
if (sever & einj->uncor_status) {
rperr->root_status |= PCI_ERR_ROOT_FATAL_RCV;
if (!(rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV))
rperr->root_status |= PCI_ERR_ROOT_FIRST_FATAL;
} else
rperr->root_status |= PCI_ERR_ROOT_NONFATAL_RCV;
rperr->root_status |= PCI_ERR_ROOT_UNCOR_RCV;
rperr->source_id &= 0x0000ffff;
rperr->source_id |= ((einj->bus << 8) | devfn) << 16;
接下来调用pci_bus_set_aer_ops来设置设备总线的,主要就是设置读写的结构体,这个地方也就是inject的精华之处。
ret = pci_bus_set_aer_ops(dev->bus);
if (ret)
goto out_put;
ret = pci_bus_set_aer_ops(rpdev->bus);
if (ret)
goto out_put;
static int pci_bus_set_aer_ops(struct pci_bus *bus)
struct pci_ops *ops;
struct pci_bus_ops *bus_ops;
unsigned long flags;
bus_ops = kmalloc(sizeof(*bus_ops), GFP_KERNEL);
if (!bus_ops)
return -ENOMEM;
ops = pci_bus_set_ops(bus, &aer_inj_pci_ops);
spin_lock_irqsave(&inject_lock, flags);
if (ops == &aer_inj_pci_ops)
goto out;
pci_bus_ops_init(bus_ops, bus, ops);
list_add(&bus_ops->list, &pci_bus_ops_list);
bus_ops = NULL;
out:
spin_unlock_irqrestore(&inject_lock, flags);
kfree(bus_ops);
return 0;
static struct pci_ops aer_inj_pci_ops = {
.read = aer_inj_read_config,
.write = aer_inj_write_config,
以aer_inj_read_config为例,在find_pci_config_dword里面实际上替换了之前设置的内容,这就使得独处的错误为用户设定的错误。
static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
u32 *sim;
struct aer_error *err;
unsigned long flags;
struct pci_ops *ops;
struct pci_ops *my_ops;
int domain;
int rv;
spin_lock_irqsave(&inject_lock, flags);
if (size != sizeof(u32))
goto out;
domain = pci_domain_nr(bus);
if (domain < 0)
goto out;
err = __find_aer_error(domain, bus->number, devfn);
if (!err)
goto out;
sim = find_pci_config_dword(err, where, NULL);
if (sim) {
*val = *sim;
spin_unlock_irqrestore(&inject_lock, flags);
return 0;
out:
ops = __find_pci_bus_ops(bus);
* pci_lock must already be held, so we can directly
* manipulate bus->ops. Many config access functions,
* including pci_generic_config_read() require the original
* bus->ops be installed to function, so temporarily put them
* back.
my_ops = bus->ops;
bus->ops = ops;
rv = ops->read(bus, devfn, where, size, val);
bus->ops = my_ops;
spin_unlock_irqrestore(&inject_lock, flags);
return rv;
static u32 *find_pci_config_dword(struct aer_error *err, int where,
int *prw1cs)
int rw1cs = 0;
u32 *target = NULL;
if (err->pos_cap_err == -1)
return NULL;
switch (where - err->pos_cap_err) {
case PCI_ERR_UNCOR_STATUS:
target = &err->uncor_status;
rw1cs = 1;
break;
case PCI_ERR_COR_STATUS:
target = &err->cor_status;
rw1cs = 1;
break;
case PCI_ERR_HEADER_LOG:
target = &err->header_log0;
break;
case PCI_ERR_HEADER_LOG+4:
target = &err->header_log1;
break;
case PCI_ERR_HEADER_LOG+8:
target = &err->header_log2;
break;
case PCI_ERR_HEADER_LOG+12:
target = &err->header_log3;
break;
case PCI_ERR_ROOT_STATUS:
target = &err->root_status;
rw1cs = 1;
break;
case PCI_ERR_ROOT_ERR_SRC:
target = &err->source_id;
break;
if (prw1cs)
*prw1cs = rw1cs;
return target;
最后再调用aer_irq(-1, edev);模拟中断产生,然后走常规的PCIe AER检测机制。
AER 即 Advanced Error Reporting高级错误报告,是PCIe高级特性,用于报告PCIe 错误信息,是PCIe RAS特性最重要的部分,本文从PCIe AER协议、固件、linux内核实现讲述PCIe AER知识。
CONFIG_CROSS_COMPILE
交叉编译工具前缀(比如"arm-linux-"相当于使用"make CROSS_COMPILE=arm-linux-"进行编译).除非你想配置后默认自动进行交叉编译,否则不要使用此选项.
Local version - append to kernel rel...
前面的文章提到过高级错误报告(Advanced Error Reporting,AER),接下来详细地介绍一下这一功能。在已有的PCIe错误报告机制上(之前文章介绍的),AER还支持以下特性:
· 在登记实际发生的错误类型时
DPC的全称是downstream port containment,是针对root port和pcie switch检测到不可恢复的错误时,就会通知下游端口的业务,以防止数据损坏的扩散.
其代码在drivers/pci/pcie/dpc.c
static struct pcie_port_service_driver dpcdriver = {
.name = "dpc",
.port_...
什么是AER
AER 英文简称 Advanced Error Reporting 翻译中文是高级错误报告,是PCIE异常信息处理机制,用于报告PCIe 错误信息
错误信息主要分为两种 Correctable Errors 和Uncorrectable errors
其中 Correctable Errors包含非致命的错误和致命的错误
a.ERR_FATAL:致命错误,此错误类型影响了PCIe link链路。
b.ERR_NONFATAL:指影响了设备功能,但是PCIe
在drivers/pci/probe.c 中的pci_init_capabilities函数中调用pci_iov_init 来进行sriov的初始化
int pci_iov_init(struct pci_dev *dev)
int pos;
if (!pci_is_pcie(dev))
return -ENODEV;
pos = p
CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址:https://hceng.cn/2019/09/05/Buildroot%E7%AC%94%E8%AE%B0/#more
整理Buildroot笔记,包含配置选项注释、目录结构分析、常用命令、构建示例、 使用技巧。
1.Buildroot基本介绍
Buildroot是Linu...
本文翻译自内核文档:linux\Documentation\PCI\pcieaer-howto.txt
《 PCI Express高级错误报告驱动程序指南》 HOWTO
T.Long Nguyen <tom.l.nguyen@intel.com>
张艳敏<yanmin.zhang@intel.com>
2006年7月29日
1.1关于本指南