文章目录
1. 问题现象
在调试 Am5728 + vxworks + 外部pcie网卡(i210) 时,发现可以找到pcie设备,但是网口不通。
- 现象1.可以在vxbus上看到pcie控制器和i210网卡的相关设备和驱动:
-> vxbDevShow
...
ti,am572x-pcie-0 (refs: 1) on simpleBus0 (refs: 27) /* pcie controller */
ti,am572x-pcie-0: <TI AM572X PCI Ex driver> (0x2028674c) level(3)
pcib-0 (refs: 1) on ti,am572x-pcie0 (refs: 1) /* pcie bridge */
pcib-0: <PCI generic bridge driver> (0x20287edc) level(4)
gei-0 (refs: 1) on pcib0 (refs: 1) /* i210 pcie设备 */
gei-0: <Intel PRO/1000 VxBus END driver> (0x20288518) level(5)
genericPhy-0 (refs: 0) on gei0 (refs: 1) /* i210 phy设备 */
genericPhy-0: <Generic 10/100/1000 PHY driver> (0x202892ec) level(6)
...
- 现象2.使用pcie的调试函数,可以看到i210网卡的pcie配置信息:
-> vxbPciCtrlShow
Pci controller (ti,am572x-pcie:0) devId=0x2028f048
value = 51 = 0x33 = '3'
-> vxbPciTopoShow(0x2028f048)
[0,0,0] - (104c 8888) type=P2P BRIDGE to [1,0,0]
base/limit:
mem= 0x20000000/0x201fffff
preMem=0xfff00000/0x000fffff
I/O= 0x0000/0x0fff
status=0x0010 ( CAP DEVSEL=0 )
command=0x0007 ( IO_ENABLE MEM_ENABLE MASTER_ENABLE )
bar0 in prefetchable 32-bit mem space @ 0x0000000000000000
[1,0,0] - (8086 1533) type=NET_CNTLR
status=0x0010 ( CAP DEVSEL=0 )
command=0x0407 ( IO_ENABLE MEM_ENABLE MASTER_ENABLE )
bar0 in 32-bit mem space @ 0x0000000020080000
value = 65535 = 0xffff
-> vxbPciHeaderShow(0x2028f048,1,0,0)
vendor ID = 0x8086
device ID = 0x1533
command register = 0x0407
status register = 0x0010
revision ID = 0x03
class code = 0x02
sub class code = 0x00
programming interface = 0x00
cache line = 0x10
latency time = 0x00
header type = 0x00
BIST = 0x00
base address 0 = 0x20080000
base address 1 = 0x00000000
base address 2 = 0x00000001
base address 3 = 0x20120000
base address 4 = 0x00000000
base address 5 = 0x00000000
cardBus CIS pointer = 0x00000000
sub system vendor ID = 0x8086
sub system ID = 0x0000
expansion ROM base address = 0x00000000
interrupt line = 0x00
interrupt pin = 0x01
min Grant = 0x00
max Latency = 0x00
Capabilities - Power Management
Capabilities - Message Signaled Interrupts: 0x50 control 0x181 Enabled, 64-bit, MME: 0 MMC: 0
Address: 0000000000000000 Data: 0x0000
Per-vector Mask: Support Mask Bit: 0x0, Pending Bit: 0x0
Capabilities - MSI-X
Capabilities - PCIe: Endpoint, IRQ 0
Device: Max Payload: 512 bytes, Extended Tag: 5-bit
Acceptable Latency: L0 - <512ns, L1 - <64us
Errors Enabled: Relaxed Ordering No Snoop
Max Read Request 512 bytes
Link: MAX Speed - 2.5Gb/s, MAX Width - by 1 Port - 4 ASPM - L0s & L1
Latency: L0s - >4us, L1 - <16us
ASPM - Disabled, RCB - 64bytes
Speed - 2.5Gb/s, Width - by 1
Ext Capabilities - Advanced Error Reporting. 0x100. Version 2. AER Control: 0xa0
Uncorrectable : Mask 0x0. Severity 0x462031
Uncorrectable Status: Correctable : Mask 0x2000.
Correctable Status: - Receiver Error
- Bad DLLP
HeaderLog:
Error Source Identification: 0x0 0x0
Ext Capabilities - Device Serial Number. 0x140. Version 1
Serial Number: 0xdd 0x40 0x55 0xff 0xff 0x6a 0x4c 0x6c
Ext Capabilities - TPH. 0x1a0. Version 1
value = 0 = 0x0
->
- 现象3. 网卡配置完成后,ifconfig虽然能看到网卡,但是读出的mac地址为
00:00:00:00:00:00
正确mac为6c:4c:6a:55:40:dd
,且ping不通:
-> ipcom_drv_eth_init("gei", 0, 0)
value = 0 = 0x0
-> ifconfig("gei0 172.16.90.81 netmask 255.255.255.0 up ")
value = 0 = 0x0
-> ifconfig("gei0")
gei0 Link type:Ethernet HWaddr 00:00:00:00:00:00
capabilities: TXCSUM TX6CSUM VLAN_MTU VLAN_TXHWTAG VLAN_RXHWTAG
inet 172.16.90.81 mask 255.255.255.0 broadcast 172.16.90.255
inet6 unicast fe80::6e4c:6aff:fe55:40dd%gei0 prefixlen 64 tentative automatic
UP SIMPLEX BROADCAST MULTICAST
MTU:1500 metric:1 VR:0 ifindex:2
RX packets:0 mcast:0 errors:0 dropped:0
TX packets:0 mcast:0 errors:0
collisions:0 unsupported proto:0
RX bytes:0 TX bytes:0
value = 0 = 0x0
->
2. 分析过程
Step 1. 排查网卡驱动(vxbGei825xxEnd.c)
首先我们从i210的网卡驱动vxbGei825xxEnd.c开始分析。
实用技巧:因为修改vxbGei825xxEnd.c需要编译VSB工程非常的慢,我们直接把vxbGei825xxEnd.c拷贝到VIP目录下直接修改。后面的其他文件同理。
从i210的datasheet上可以看到,可以通过memory bar直接访问内部寄存器,或者通过io bar间接访问内部寄存器。
我们加上调试代码,目的是打印出memeory bar和io bar的映射情况:
LOCAL STATUS geiAttach
(
VXB_DEV_ID pDev
)
{
...
for (i = 0; i < VXB_MAXBARS; i++)
{
pRes = vxbResourceAlloc (pDev, VXB_RES_MEMORY, (UINT16)i);
if (pRes != NULL)
{
pResAdr = (VXB_RESOURCE_ADR *)pRes->pRes;
if (pResAdr != NULL)
{
pDrvCtrl->geiHandle = pResAdr->pHandle;
pDrvCtrl->geiBar = (void *)pResAdr->virtual;
pDrvCtrl->geiRes = pRes;
_func_kprintf("Memory bar base addr(index = %d):\n", i);
_func_kprintf("pResAdr->virtual: 0x%x.\n", pResAdr->virtual);
_func_kprintf("pResAdr->start: 0x%llx.\n", pResAdr->start);
_func_kprintf("pResAdr->size: 0x%x.\n", pResAdr->size);
break;
}
else
{
(void) vxbResourceFree (pDev, pRes);
}
}
}
...
/* Map the I/O BAR too */
for (i = 0; i < VXB_MAXBARS; i++)
{
pRes = vxbResourceAlloc (pDev, VXB_RES_IO, (UINT16)i);
if (pRes != NULL)
{
pResAdr = (VXB_RESOURCE_ADR *)pRes->pRes;
if (pResAdr != NULL)
{
pDrvCtrl->geiIoHandle = pResAdr->pHandle;
pDrvCtrl->geiIoBar = (void *)pResAdr->virtual;
pDrvCtrl->geiIoRes = pRes;
_func_kprintf("IO bar base addr(index = %d):\n", i);
_func_kprintf("pResAdr->virtual: 0x%x.\n", pResAdr->virtual);
_func_kprintf("pResAdr->start: 0x%llx.\n", pResAdr->start);
_func_kprintf("pResAdr->size: 0x%x.\n", pResAdr->size);
break;
}
else
{
(void) vxbResourceFree (pDev, pRes);
}
}
}
...
}
可以看到打印出来的memory bar和io bar的基地址情况:
Memory bar base addr(index = 0):
pResAdr->virtual: 0x222cd000. /* cpu侧虚拟地址 */
pResAdr->start: 0x0. /* cpu侧物理地址 */
pResAdr->size: 0x80000. /* bar的大小 */
IO bar base addr(index = 2):
pResAdr->virtual: 0x2234d000. /* cpu侧虚拟地址 */
pResAdr->start: 0x20090000. /* cpu侧物理地址 */
pResAdr->size: 0x20. /* bar的大小 */
这里可以看到,memory bar的cpu侧物理地址为0x0
其实已经异常,但是因为这个时候对am5728的pcie和cpu物理地址相互映射的机制还没理解透。所以还不是十分肯定。
我们通过memoey bar来直接访问内部寄存器,看看寄存器的情况:
LOCAL STATUS geiAttach
(
VXB_DEV_ID pDev
)
{
...
_func_kprintf("memory bar direct read:\n");
for (i=0; i<0x100; i+=4)
{
if (i%0x10 == 0)
{
_func_kprintf("\n 0x%x: ", i);
}
reg = CSR_READ_4(pDrvCtrl, i);
_func_kprintf("0x%x ", reg);
}
_func_kprintf("\n\n");
...
}
读出的i210内部寄存器情况:
memory bar direct read:
0x0: 0xc744375 0xc5e815b7 0x9ffcf73b 0x95ef1d23
0x10: 0xf1afab3f 0x2498399d 0x7d989bec 0xc5bcfeb4
0x20: 0xddd1b99c 0xbead6bcd 0xdb5efb5b 0xcf5eb878
0x30: 0x6fb92ecd 0xb82e2f76 0x6dbb5e1f 0x1c297df8
0x40: 0x9d9abeb9 0xee97c976 0xa9ea5b7b 0xd4aefc74
0x50: 0x27745a85 0xfe36f8da 0x867f5e57 0x3132ee53
0x60: 0xbdfff74 0x2fb5eefb 0xffdbfd91 0x97b27d90
0x70: 0x70f2af3e 0x2765e9da 0x37fef72f 0x7823e897
0x80: 0x9c8fdd49 0xab3733bf 0xbe6799d7 0xfc2c897
0x90: 0xaff68478 0xcde7314e 0x61db877a 0x3df799ce
0xa0: 0xaef2765d 0x8a0dbd1f 0xd6677e75 0x3b8256ff
0xb0: 0x68fba56f 0xaf98e07b 0x7bf85ff 0x53fe5edf
0xc0: 0x5e3d0782 0x8f5f7b70 0x4ff309fc 0x6b194a7d
0xd0: 0x96bf3e16 0xbf7ed6dc 0xffffffff 0x1f987be8
0xe0: 0xdf7f37df 0xedba4fc2 0xff6ffc3d 0xdf9778f4
0xf0: 0xecbd6a7d 0x2bfd0b36 0x88c4046 0xd9b92976
可以看到寄存器非常的不正常。例如内部寄存器在0x28
偏移处是Flow Control Address Low
寄存器,它是一个常量为0x00C28001
,可以当我们判断寄存器是否正确的锚点
。但是我们读出的寄存器完全不对。

因为io bar也可以访问寄存器,这个时候想到可以通过io bar来间接访问内部寄存器,看看寄存器的情况:
LOCAL STATUS geiAttach
(
VXB_DEV_ID pDev
)
{
...
_func_kprintf("io bar indirect read:\n");
ioBar = pDrvCtrl->geiIoBar;
ioHandle = pDrvCtrl->geiIoHandle;
for (i=0; i<0x100; i+=4)
{
if (i%0x10 == 0)
{
_func_kprintf("\n 0x%x: ", i);
}
vxbWrite32 (ioHandle, ioBar, i);
reg = vxbRead32 (ioHandle, ioBar + 1);
_func_kprintf("0x%x ", reg);
}
_func_kprintf("\n\n");
...
}
读出的i210内部寄存器情况:
io bar indirect read:
0x0: 0x81c0241 0x81c0241 0x280780 0x0
0x10: 0x6082b40 0x2 0x1400c0 0x8000a
0x20: 0x18001400 0x0 0xc28001 0x100
0x30: 0x8808 0x200 0x81008100 0x0
0x40: 0x556a4c6c 0x8000dd40 0x0 0x0
0x50: 0x0 0x0 0x0 0x0
0x60: 0x0 0x0 0x0 0x0
0x70: 0x0 0x0 0x0 0x0
0x80: 0x0 0x0 0x0 0x0
0x90: 0x0 0x0 0x0 0x0
0xa0: 0x0 0x0 0x0 0x0
0xb0: 0x0 0x0 0x0 0x0
0xc0: 0x40004 0x0 0x0 0x0
0xd0: 0x0 0x0 0x0 0x0
0xe0: 0x0 0x0 0x0 0x0
0xf0: 0x0 0x0 0x0 0x0
可以看到读出的寄存器看起来比较正常,锚点
0x28寄存器值为0xc28001
,非常吻合。
总结:从上面的操作可以看到,通过memory bar访问寄存器异常,通过io bar访问寄存器正常。初步怀疑是memory bar的地址映射过程中出了问题。
Step 2. 综合分析(相关知识)
- pcie bar地址的映射
上一步我们怀疑memory bar地址在映射过程中出错,在系统中关于这部分的映射一共有3种地址:cpu侧虚拟地址、cpu侧物理地址、pcie侧物理地址。
关于memory bar的这几个地址,通过vxbPciHeaderShow()命令我们看到pcie侧物理地址
为0x20080000,通过上一步我们加的打印可以看到cpu侧虚拟地址
为0x222cd000、cpu侧物理地址
为0x0。
cpu侧虚拟地址 | cpu侧物理地址 | pcie侧物理地址 |
---|---|---|
0x222cd000 | 0x0 | 0x20080000 |
可以看到cpu侧物理地址
为0x0出错的嫌疑非常大,我们进一步追查其中的映射过程。
- pcie 初始化过程
pcie的初始化首先会来到pcie控制器的驱动,在vxbFdtTiAM572xPcie.c:
LOCAL VXB_DRV_METHOD pciExMethodList[] =
{
{ VXB_DEVMETHOD_CALL(vxbDevProbe), (FUNCPTR)tiAm572xPcieProbe},
{ VXB_DEVMETHOD_CALL(vxbDevAttach), (FUNCPTR)tiAm572xPcieAttach},
{ VXB_DEVMETHOD_CALL(vxbDevIoctl), (FUNCPTR)vxbPciBusIoctl},
{ VXB_DEVMETHOD_CALL(vxbPciIntAssign), (FUNCPTR)tiAm572xPcieIntAssign},
{ VXB_DEVMETHOD_CALL(vxbPciCfgRead), (FUNCPTR)tiAm572xPcieCfgRead},
{ VXB_DEVMETHOD_CALL(vxbPciCfgWrite), (FUNCPTR)tiAm572xPcieCfgWrite},
{ VXB_DEVMETHOD_CALL(vxbResourceAlloc), (FUNCPTR)tiAm572xPcieResAlloc},
{ VXB_DEVMETHOD_CALL(vxbResourceFree), (FUNCPTR)tiAm572xPcieResFree},
{ VXB_DEVMETHOD_CALL(vxbResourceListGet), (FUNCPTR)tiAm572xPcieResListGet},
{ VXB_DEVMETHOD_CALL(vxbIntEnable), (FUNCPTR)tiAm572xPcieIntEnable},
{ VXB_DEVMETHOD_CALL(vxbIntDisable), (FUNCPTR)tiAm572xPcieIntDisable},
{ VXB_DEVMETHOD_CALL(vxbIntBind), (FUNCPTR)tiAm572xPcieIntBind},
{ VXB_DEVMETHOD_CALL(vxbIntAlloc), (FUNCPTR)tiAm572xPcieIntAlloc},
{ VXB_DEVMETHOD_CALL(vxbIntFree), (FUNCPTR)tiAm572xPcieIntFree},
{ 0, NULL }
};
LOCAL VXB_FDT_DEV_MATCH_ENTRY pciExMatch[] =
{
{
TI_AM572X_DRV_NAME, /* compatible */
NULL
},
{} /* empty terminated list */
};
/* globals */
VXB_DRV vxbFdtTiAm572xPcieDrv =
{
{NULL} ,
TI_AM572X_DRV_NAME, /* Name */
"TI AM572X PCI Ex driver", /* Description */
VXB_BUSID_FDT, /* Class */
0, /* Flags */
0, /* Reference count */
pciExMethodList /* Method table */
};
VXB_DRV_DEF(vxbFdtTiAm572xPcieDrv)
在attach函数tiAm572xPcieAttach()中开始pcie控制器的初始化工作:
LOCAL STATUS tiAm572xPcieAttach
(
VXB_DEV_ID pDev
)
{
...
/* (1) 从DTS中读取pcie控制器的相关配置 */
/* (2) 初始化pcie控制器 */
/* (3) 自动扫描并配置PCIE总线上的设备 */
/* PCI autoconfiguration and announce device */
if (OK != vxbPciAutoConfig (pDev))
{
DEBUG_MSG (AM572X_DBG_ERR, "<tiAm572xPcieAttach>: vxbPciAutoConfig"
" failed\n");
goto errOut;
}
...
}
tiAm572xPcieAttach()最后会调用vxbPciAutoConfig()来遍历pcie总线上的设备,并且调用vxbPciBusAddDev()函数把扫描到的pcie设备转成VxBus Device来添加。在vxbPci.c:
STATUS vxbPciAutoConfig
(
VXB_DEV_ID pDev
)
{
...
/* (3.1) 扫描并配置pcie总线上的设备,
_func_vxbPciAutoConfig = vxbPciAutoConfigFunc
*/
if (pSoftc->autoConfig == TRUE)
{
if (_func_vxbPciAutoConfig != NULL)
_func_vxbPciAutoConfig (pDev);
else
return (ERROR);
}
/* (3.2) 把扫描并配置好的pcie设备,当成vxbus device注册 */
return (vxbPciBusAddDev (pDev, pRes->baseBusNumber));
}
Step 3. 排查PCIE物理地址和CPU物理地址转换(vxbPci.c)
我们继续怀疑是不是
pcie侧物理地址
到cpu侧物理地址
转换的过程中出错了?
转换函数在:vxbPciBusAddDev() -> vxbPciResourceInit() -> vxbPciAddr2Cpu()。
LOCAL PHYS_ADDR vxbPciAddr2Cpu
(
VXB_DEV_ID pDev, /* device info */
UINT64 pciAddr /* PCI address */
)
{
UINT64 phyAdjust = 0;
...
for (i = 0; i < pSoftc->segCount; i++)
{
pciRootRes = (PCI_ROOT_RES *)&pSoftc->pRootRes[i];
for (j = 0; j < PCI_ROOT_RES_MAX_IDX; j++)
{
if ((pciAddr >= pciRootRes->barRes[j].start) &&
(pciAddr < pciRootRes->barRes[j].end))
{
phyAdjust = pciAddr - pciRootRes->barRes[j].start;
phyAdjust += pciRootRes->barRes[j].cpuBase;
break;
}
}
}
return ((PHYS_ADDR)phyAdjust);
}
am572x_idk_ca15.dts中定义了两组pcie侧物理地址
到cpu侧物理地址
转换:
ranges = <0x2000000 0 0x200A0000 0x200A0000 0 0x0FF50000
0x1000000 0 0x00000000 0x20090000 0 0x00010000>;
窗口类型 | pcie侧物理地址 | cpu侧物理地址 | 窗口大小 |
---|---|---|---|
memory窗口 | 0x200A0000 | 0x200A0000 | 0x0FF50000 |
io窗口 | 0x00000000 | 0x20090000 | 0x00010000 |
从上述函数可以看到,我们memory bar0读出的pcie侧物理地址
为0x20080000,那么上述的两个窗口我们都不能命中。我们只能得到默认值0。符合step 1中我们读出的cpu侧物理地址
0x0。
总结:从上述分析看,vxbPciAddr2Cpu()这部分的地址转换的逻辑是没有问题的。出问题可能性最大的还是0x20080000这个地址有问题。
Step 4. 排查PCIE物理地址的配置(vxbPciAutoCfg.c)
我们深入映射的核心vxbPciAutoCfg.c来分析pcie侧物理地址
具体配置过程。:
LOCAL STATUS vxbPciAutoConfigFunc
(
VXB_DEV_ID busCtrlID
)
{
...
(void) vxbPciDeviceScan (busCtrlID, pciRootRes, pciRootRes->baseBusNumber, pPciDev);
if (!DLL_EMPTY(&pPciDev->children))
{
vxbPciResAdjust (busCtrlID, pciRootRes, pPciDev);
vxbPciResourceSort (busCtrlID, pPciDev, 0);
vxbPciResourceAssign (busCtrlID, pciRootRes, pPciDev);
vxbPciProgramBar(busCtrlID, pPciDev);
}
...
}
首先我们打开文件的调试开关:
/* Debug macro */
#undef PCI_DEBUG
#define PCI_DEBUG
#ifdef PCI_DEBUG
#include <private/kwriteLibP.h>
LOCAL int debugLevel = 1000;
默认打印出了一些信息:
Insert resource1 (1:0:0) BAR (0) to (0:0:0) type (0) size (0x80000) align(0x80000) preBar (0x6)
Insert resource1 (1:0:0) BAR (2) to (0:0:0) type (1) size (0x20) align(0x20) preBar (0x7)
Insert resource1 (1:0:0) BAR (3) to (0:0:0) type (0) size (0x4000) align(0x4000) preBar (0x6)
Insert resource2 (0:0:0) BAR (6) to (255:255:255) type (0) size (0x100000) align(0x100000) preBar (0x2028f328)
Insert resource2 (0:0:0) BAR (7) to (255:255:255) type (1) size (0x1000) align(0x1000) preBar (0x2028f328)
(255:255:255) bar(6) request (0x0:0x0) size(0x100000) align(0x100000) addr(0x200a0000)
(255:255:255) bar(7) request (0x0:0x0) size(0x1000) align(0x1000) addr(0x0)
(0:0:0) bar(6) request (0x200a0000:0x100000) size(0x80000) align(0x80000) addr(0x200a0000)
(0:0:0) bar(6) request (0x200a0000:0x100000) size(0x4000) align(0x4000) addr(0x20120000)
(0:0:0) bar(7) request (0x0:0x1000) size(0x20) align(0x20) addr(0x0)
ProgramBar bridge(000): BAR6:[0x200a0000-0x2019ffff] align 0x100000 size 0x100000
ProgramBar bridge(000): BAR7:[0x0-0xfff] align 0x1000 size 0x1000
ProgramBar device(100): BAR0:[0x200a0000-0x2011ffff] align 0x80000 size 0x80000
ProgramBar device(100): BAR2:[0x0-0x1f] align 0x20 size 0x20
ProgramBar device(100): BAR3:[0x20120000-0x20123fff] align 0x4000 size 0x4000
可以看到我们关注的i210的memory bar0,分配到的pcie侧物理地址
为0x200a0000,大小为0x80000,对齐为0x80000。
ProgramBar device(100): BAR0:[0x200a0000-0x2011ffff] align 0x80000 size 0x80000
这里就非常奇怪了,为什么这里分配到的
pcie侧物理地址
为0x200a0000,但是我们通过vxbPciHeaderShow()命令读出来的值为0x20080000?
我们首先排查寄存器的配置过程,在具体的配置函数vxbPciProgramBar()中加入调试信息:
LOCAL void vxbPciProgramBar
(
VXB_DEV_ID busCtrlID,
PCI_DEVICE * pPciDev
)
{
...
PCI_LOG_MSG(600, "bar %d: write val = 0x%x .\n", i, (UINT32)pPciDev->resource[i].start);
(void) VXB_PCI_CFG_WRITE(busCtrlID, &hardInfo,
PCI_CFG_BASE_ADDRESS_0 + i*4, 4,
(UINT32)pPciDev->resource[i].start);
(void) VXB_PCI_CFG_READ(busCtrlID, &hardInfo,
PCI_CFG_BASE_ADDRESS_0 + i*4, 4,
®);
PCI_LOG_MSG(600, "bar %d: read val = 0x%x .\n", i, reg);
...
}
打印出信息如下:
bar 0: write val = 0x200a0000 .
bar 0: read val = 0x20080000 .
可以看到bar 0 base address寄存器,写进去的值为
0x200a0000
,但是读出来的值为0x20080000
。这就非常奇怪了,为什么会这样?
后来想到是不是pci bar alignment的问题,因为bar0大小为0x80000,它分配的地址也要求0x80000对齐的。但是0x200a0000这个地址是不符合0x80000对齐的,这说明pcie侧物理地址
的分配策略有问题。
我们查看具体的分配函数vxbPciBarAlloc():
LOCAL STATUS vxbPciBarAlloc
(
PCI_RES * pRes, /* parent resource*/
PCI_MMIO_DESC * pDesc
)
{
PCI_BAR_ADR_POOL * pPool = &pRes->pool;
/* (1) 情况1:在pRes->pool->list中分配符合对齐和大小条件的地址空间 */
for (pCur = (PCI_ADDR_RESOURCE *) DLL_FIRST (&pPool->list);
pCur != NULL;
pCur = pNext)
{
nodeIndex++;
alignStart =
ROUND_UP (pCur->start + pCur->size, pDesc->align);
pNext = (PCI_ADDR_RESOURCE *) DLL_NEXT (&pCur->node);
if ((pNext != NULL) &&
(pNext->start > alignStart) &&
((pNext->start - alignStart) >= pDesc->size))
break;
}
/* (2) 情况2:如果pRes->pool->list为空,直接使用pPool->base */
if (nodeIndex == 0)
alignStart = pPool->base;
...
}
可以看到我们刚好是情况2
,所以我们获得的地址是没有做地址对齐的。
3. 结论
综上分析,问题的结论是:
pcie控制器驱动在自动配置pci设备的过程中,会探测pci设备bar空间的大小,并根据bar空间的大小和对齐分配一个合适的pcie侧物理地址
给它。但是vxworks的地址空间分配函数vxbPciBarAlloc(),在pRes->pool->list为空的情况下不会做地址对齐操作。
bar0大小为0x80000对齐为0x80000,但是分配了一个没有0x80000对齐的地址0x200a0000。
地址0x200a0000写入bar0,低位会被强制清零,变成了0x20080000。这样就造成了后续的一系列映射出错。
解决补丁:
--- a/vxbPciAutoCfg.c
+++ b/vxbPciAutoCfg.c
@@ -180,7 +180,7 @@ LOCAL STATUS vxbPciBarAlloc
}
if (nodeIndex == 0)
- alignStart = pPool->base;
+ alignStart = ROUND_UP(pPool->base, pDesc->align);
pNew = (PCI_ADDR_RESOURCE *) vxbMemAlloc (sizeof (PCI_ADDR_RESOURCE));