【27】pciutil关于cap扫描的一段代码

这里写自定义目录标题

pcieuitl中关于cap扫描的有一段代码很有意思,咋一看,数组可能越界,仔细品味,发现写的挺巧妙的

static void
pci_scan_trad_caps(struct pci_dev *d)
{
  word status = pci_read_word(d, PCI_STATUS);--------(1)
  byte been_there[256];-------------------------------------------(2)
  int where;
 
  if (!(status & PCI_STATUS_CAP_LIST))-------------------(3)
    return;
  memset(been_there, 0, 256);----------------------------------(4)
  where = pci_read_byte(d, PCI_CAPABILITY_LIST) & ~3;------(5)
  while (where)
  {
      byte id = pci_read_byte(d, where + PCI_CAP_LIST_ID);  ------(6)
      byte next = pci_read_byte(d, where + PCI_CAP_LIST_NEXT) & ~3;-------(7)
      if (been_there[where]++)---------------------------------------------------------(8)
	    break;
      if (id == 0xff)-------------------------------------------------------------------------(9)
	    break;
      pci_add_cap(d, where, id, PCI_CAP_NORMAL);------------------------------(10)
      where = next;----------------------------------------------------------------------------(11)
  }
}

第(1)步,读取status reg
第(2)步,在栈上申请了一个256Byte的数组been_there[256]。
第(3)步,判断是否支持PCIe cap,如果设备支持PCIe cap则继续。
第(4)步,把256Byte的数组been_there[256]对应的内存清零。
第(5)步,读取PCI_CAPABILITY_LIST(0x34)获取第一个PCIe的cap的偏移
第(6)步,读取改cap对应的capid
第(7)步,读取next cap对应的偏移
第(8)步,实现两个功能,这里是先判断再++。
功能1.判断改cap是否为1,如果为1就退出,就是防止重复添加cap
功能2.cap对应flag–》been_there[where]++变成1
这里其实有个小问题,应该把8/9步骤 调换位置更好,防止芯片异常导致的id为ff
第(9)步,判断id是否为ff,ff就是链路异常或者completion timeout返回的全1的值
第(10)添加改cap到caplist
第(11)步next cap的偏移赋值给where,然后循环读
那么问题来了,1、什么时候while退出,2、怎么可以保证数组不越界?
在这里插入图片描述
在这里插入图片描述
仔细看上面两个图,然后品味下协议中的下面一段话
在这里插入图片描述
问题1、什么时候while退出
-----找到last cap时,next 就是00,然后赋值为where,也就退出了
问题2、为什么数组不会越界?
------协议规定了,PCI配置空间是256Byte,PCIe extend 配置空间4K,只要芯片没有异常,where是无论如何都不会超过0xFF的。那么芯片异常时呢,按照协议,芯片异常时,可能返回全1,读取8bit的接口,返回值可能是0xFF,也不会越界。

那pciutils的这段代码有没有问题呢,很不幸,即使是大神写的代码,异常考虑也有瑕疵
问题1
第(8)和(9)最好是调换一下位置,防止flag置1了,但是cap实际上没有加入list
问题2
由于next没有是否为0xFF,当芯片抽风时,next为0xFF,id不为0xFF,会死循环,不知道读取到哪里去了。
问题3
where没有判断是0xFF,可能导致第(6)步,已经读偏了
下面是修改后的代码,稍微显得啰嗦,但是在芯片异常时,这段代码也不会出问题。为啥会想到写的这么啰嗦呢,真的是拜某厂所赐。不得不说业界有一个“奇葩”(褒义词)一样存在的设备厂商,其中一个环节叫做FIT测试,会对设备上所有的电源时钟等各种各样你能想到的芯片和你想不到的芯片注入故障,来看看软件是否能cover的住。这种方式有好的一方面,也有不好的一方面。
好处就是可以在实验室有限的几百台设备最大可能模拟出客户现场的各种故障(PS:请相信一个经验:当你的设备发货量在10万+时,任何在实验室出现过的,那么就出现过一次,再也不复现的问题,理论分析可能性再低的问题,在客户现场都会出现)。
坏处就是导致驱动代码写的异常啰嗦。

static int
pci_scan_trad_caps(struct pci_dev *d)
{
  word status = pci_read_word(d, PCI_STATUS);
  if(status == 0xff)
    return -1;
  byte been_there[256];
  int where;

  if (!(status & PCI_STATUS_CAP_LIST))
    return 0; /*not support Extended Capability list item*/

  memset(been_there, 0, 256);
  where = pci_read_byte(d, PCI_CAPABILITY_LIST) & ~3;
  if (where == 0xff)
    return -1;
  while (where)
  {
      byte id = pci_read_byte(d, where + PCI_CAP_LIST_ID);
      if (id == 0xff)
	    return -1;
      byte next = pci_read_byte(d, where + PCI_CAP_LIST_NEXT) & ~3;
      if (next == 0xff)
	    return -1;
      if (been_there[where]++)
	    return -1;/*重复的cap,一定读取reg出了问题*/
      pci_add_cap(d, where, id, PCI_CAP_NORMAL);
      where = next;
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/linjiasen/article/details/95313260