调试 intel 82580网卡在intel 8632 switch下扫不到的问题的收获

最近在调试intel 82580这款网卡在8632 switch下扫不到的问题。耗时了三个星期的时间,在这个过程中真的是学到了很多,所以还是决定将这些都记录下来吧。因为漫漫长河中,我们终将会将一些事遗忘。

问题现象

硬件环境:

           G1控制器(00:11:00)
                |
           8632桥(03:00:00)
                |
   --------------------------------------------------------------------------------------------
   |  (04:00:00)       |(04:04:00)           |(04:05:00)                      |(04:06:00)
8632桥                   8632桥                    8632桥                                8632桥
                                                                   |
                                                               82580网卡(07:00:00)

在上面的硬件连接方式下,82580这个设备在pci 扫描枚举设备的时候,就是扫不到它。读出来的Vendorid和deviced都是0。我们换了一个插槽,将82580这个卡插到我们pcie控制器直接相连的插槽就没有问题了。同样我们在(04:05:00)这个插槽的位置插一个其他的网卡或者其他设备都是可以扫到的。所以这个问题的现象真的很诡异。

注意:上面的G1控制器是我们桥片内部集成的pcie控制器,它在我们的系统上的设备id是(bus:0,device:17,Func:0),它的下面接了一个Intel 8632的透明桥。这个桥下面又扩展出了4个8632的透明桥,每一个桥外接出一个pcie插槽。方便我们来接设备,其实在上面的现象中,82580接到哪一个8632的桥转接出来的插槽上都是扫不到的。上面每一个设备的设备id都是pci在枚举设备过程中为其分配的bus号,device号和function号。pci总线在枚举设备过程中,是以深度优先算法遍历设备的,这里不再详细的介绍遍历设备的过程,相信调试过pci设备的人都熟知他的过程。

尝试的解决办法

首先复现这个问题之后,我的第一直觉就是我们的pcie控制器也就是上面的G1控制器的配置哪里不对导致,所以就按照这个方向去追查。幸好我们还有另外一套软件pmon,在pmon上面是没有问题的。无论插到上面的哪一个插槽都是可以扫到的。因此就对比G1控制器相关的phy寄存器,配置寄存器以及其他的相关的寄存器的配置在uefi和pmon中配置的差异。在pmon中,所有的初始化都是通过汇编代码来实现的。然后就开始了对比寄存器的过程。

在BIOS下和内核下依次读取G1控制器的配置在pmon和uefi下的区别

用ejtag在pmon和uefi在进内核之前读取G1控制器相关的寄存器,看看有什么差异,然后对比差异寄存器,将uefi中配置的值修改成pmon的值,但是没有效果。都修改成一样的之后,内核下还是扫不到。为了确保万无一失又将所有的pcie控制器的寄存器都对比了一下(F0-G1)都对比了,将不一致的地方修改为一致后,还是没有效果。这个时候开始怀疑是pci扫描部分代码差异导致,因为在pci扫描过程中,会对pci设备的标准配置头中的寄存器进行配置,有可能是这里的差异。然后就开始对比配置空间的差异。

对比G1控制器及其下面的所有子设备的配置空间的值

pci设备的标准配置头是64字节,pcie的是256字节。这里我只对比了前64字节寄存器的值也就是从偏移0x0到0x40内的值,这个时候我发现所有的桥的command寄存器的第6和8bit不一致,查看pci规范,却发现无关紧要。这里设置的不一样是由于pmon和uefi在pci扫描部分的代码差异,对command寄存器的初始化不一致。
同时还发现(04:05:00)这个桥上面偏移0x20-0x23MemoryBase/MemoryLimit这两个寄存器的值不一致。在pmon上面是分配了地址的,但是在uefi下面没有。现在知道,这个寄存器分配的地址就是为下游设备使用的,下游设备分配的pci memory空间就是从这个地址开始的。如果没有发现下游设备,当然这里不会有值。这里需要注意,桥和普通pci设备的差异。普通pci设备,它自己的寄存器如果映射到memory空间,那么映射的起始地址就是bar空间中的一个,具体使用的哪一个bar不一定。而桥的bar空间是映射的自己寄存器的值。如果是透明桥,bar0和BAR1是不需要写地址的,不需要为自己的寄存器分配映射的地址空间,一般透明桥是不需要配置什么的。对比了这条链路上所有设备的标准配置头,并将不一致的修改为一致之后,还是没有效果。现在标准配置空间修改为一致后还是灭有效果,寄存器也都修改为一致。实现没有别的怀疑的地方了。后面就开始怀疑硬件信号的问题,但是是什么导致硬件信号还不清楚。然后开始读取pcie扩展配置空间的信息,看看是否在链路上就出错了。
我们从扩展配置空间的信息中,看出了虚通道协商出错了。这个错误是非常致命的错误。根据这个现象就知道为什么没有扫到它下面插的82580网卡了。根据上面的状态,猜测是pcie信号不好导致硬件链路协商的时候出现了问题,所以想通过将8632桥复位来看看是否有效果。

复位8632桥

根据8632使用手册,可以通过桥的配置空间的Bridge COntrol寄存器的第22bit来复位这个桥以及其下游的设备,将这个bit写1.硬件会自动完成复位操作,并且属于hot reset。配置空间的寄存器不需要重新初始化。
这里在uefi下添加这部分代码,但是还是没有效果。在扫描到这个桥以及 其下游设备的时候,如果扫不到,那么就复位这个桥,然后继续去扫这个桥下游的设备。添加了这么个逻辑后,代码就死循环在里面了。一直处于复位扫描复位扫描的循环状态。到这里真的就束手无策了,不知道还能做什么操作。开始怀疑我们这样只操作这一个寄存器,可能并没有真的复位,但是从手册上却找不到其他的复位的方法。所以准备开始从内核下手,看看内核中pci扫描部分时候有关于设备复位的函数,但是猜测有的可能性也不带,因为大多数复位操作都在具体的设备的驱动中完成的。pci bus层是不会有的。怀着侥幸的心里去阅读内核的代码。
最后从内核的代码中,也没有找到为什么会扫不到这个设备。这时突发奇想可以将pmon的汇编移植到uefi中看看是否就会有效果呢?然后就开始将pmon的代码往uefi中移植。

将pmon中的汇编移植到uefi中

由于pmon中这部分配置都是汇编实现的,而uefi是用C实现的,所以只能将这部分移植到uefi的start.s中,但是在初始化pci之前还有一部分7a初始化的代码以及ht相关的代码都一起移植过去了。移植过程中解决了很多编译错误的问题,也学到了很多东西。移植完之后,发现真的就可以扫到了。所以现在确定这部分代码汇编和C肯定是有差异的。然后就一点点的翻译汇编代码,又将这部分汇编从新翻译成了C,但是发现还是不行。这种现象真的就很奇怪,反复的对比代码修改代码还是不行。然后又只将C中的pci初始化的代码替换为汇编。发现这样也是可以的。所以肯定就是这部分代码的问题。然后就不断的将汇编代码才分成单独的函数,然后一个函数一个函数的替换,到底是那个函数的问题。最后定位到了最简短的函数。然后一点点的对比,发现汇编的延时和C中的延时不一致,最后将C中的延时函数加长就可以扫到了。最后定位为延时不够,导致初始化的状态不稳所致。这种问题真的是很难猜到。延时不够居然会影响这么大。所以以后和硬件相交互的时候,一定要确保时间上要满足硬件的要求,否则出现什么奇怪的现象都不好说。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章