PCIe设备漫游记----BIOS篇

调试个人经验之谈:

当主板在BIOS阶段枚举PCIE时,如果PCIE没有初始化完成,就使设备无法被枚举成功,主板会关闭slot时钟输出。一定要注意这个时间差,设备一定要初始化完成。正常枚举后一定可以访问设备寄存器空间如BAR。如果不能被正常配置,则设备没正常工作。


 初步了解完PCI总线标准之后,我们接下来正式开始PCIe设备的漫游之旅。从我们按下PC的电源按钮开始,BIOS就接管系统控制权开始工作,它会先进行一些内存和设备的初始化工作(当然,也包括我们的PCI设备),由于商业上的原因,Phoenix等厂商的BIOS代码需要授权协议,在此,我们以另外一个款开源BIOS(openbios)为例,来剖析BIOS中,我们的PCIe设备是如何被找到以及初始化的。






PCI设备的扫描是基于深度优先搜索算法(DFS:Depth First Search),也就是说,下级分支最多的PCI桥将最先完成其子设备的扫描。下面我们以图片来具体说明,BIOS是如何一步步完成PCI 设备扫描的。


第一步:

PCI Host 主桥扫描Bus 0上的设备(在一个处理器系统中,一般将与HOST主桥直接相连的PCI总线被命名为PCI Bus 0),系统首先会忽略Bus 0上的D1,D2等不会挂接PCI桥的设备,主桥发现Bridge 1后,将Bridge1 下面的PCI Bus定为 Bus 1,系统将初始化Bridge 1的配置空间,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成0和1,以表明Bridge1 的上游总线是0,下游总线是1,由于还无法确定Bridge1下挂载设备的具体情况,系统先暂时将Subordinate Bus Number设为0xFF。如下图所示:


第二步:

系统开始扫描Bus 1,将会发现Bridge 2。系统将Bridge 2下面的PCI Bus定为Bus 2,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成1和2,和上一步一样暂时把Bridge 2 的Subordinate Bus Number设为0xFF。如下图所示:



第三步:

系统继续扫描Bus 2,将会发现Bridge 4。系统将Bridge 4下面的PCI Bus定为Bus 3,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成2和3,此后

系统继续扫描后发现Bus 3 下面已经没有任何Bridge了,意味着该PCI总线下已经没有任何挂载下游总线了,因此Bridge 4的Subordinate Bus Number的值已经可以确定为3了。

如下图所示:


第四步:

完成Bus 3的扫描后,系统返回到Bus 2继续扫描,发现Bus 2下面已经没有其他Bridge了。此时Bridge 2的Subordinate Bus Number的值也已经可以确定为3了。如下图所示:



第五步:

完成Bus 2的扫描后,系统返回到Bus1继续扫描,会发现Bridge 3,系统将Bridge 3下面的PCI Bus定为Bus 4。并将Bridge 4的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成1和4,此后系统继续扫描后发现Bus 4 下面已经没有任何Bridge了,意味着该PCI总线下已经没有挂载任何下游总线了,因此Bridge 3 的Subordinate Bus Number的值已经可以确定为4了。如下图所示:



第六步:

完成Bus 4的扫描后,系统返回到Bus 1继续扫描, 发现Bus 1下面已经没有其他Bridge了。此时Bridge 1的Subordinate Bus Number的值已经可以确定为4,系统返回Bus 0继续扫描(Bus 0下如果有其他它Bridge,将重复上述的步骤进行扫描)。至此,本例中的整个PCI的设备扫描已经完成了。最终的设备和总线的扫描结果如下图所示。



了解了上面PCI设备扫描的大概流程,我们接下来看看Bios代码中具体是如何实现这些扫描的。

一般来说,我们可以通过两个寄存器来访问PCI的配置空间(寄存器CONFIG_ADDRESS与CONFIG_DATA),在x86体系下,这两个寄存器分别对应0xCF8和0xCFC端口,对配置空间的访问都是通过对这两个寄存器的读写来实现先。CONFIG_ADDRESS寄存器的具体位组成如下图所示:



Bus Number : 总线号(8 bit),范围0--255。

Device Number: 设备号(5 bit),范围0--31。

Function Number: 功能号(3 bit),范围0--7。

Register Number: 寄存器号(6 bit),范围0--63 (配置空间一共256个字节,分割成64个4字节的寄存器,从0--63编号)。

每个PCI设备可根据上图所示的四个信息:Bus Number, Device Number, Function Number,Register Number 来进行具体设备的定位并对其配置空间访问。当我们要访问PCI设备的配置空间时,先根据以上格式设置CONFIG_ADDRESS寄存器,然后再读取CONFIG_DATA寄存器即可得到相应的配置空间寄存器的值。

因此,BIOS中PCI配置空间的读写可以封装成下面的函数:

[cpp]  view plain copy
  1. static inline uint32_t pci_config_read32(pci_addr dev, uint8_t reg)  
  2. {  
  3.     outl(dev | reg, 0xcf8);  
  4.     return inl(0xcfc | reg);  
  5. }  
  6.   
  7. static inline void pci_config_write32(pci_addr dev, uint8_t reg, uint32_t val)  
  8. {  
  9.     outl(dev | reg, 0xcf8);  
  10.     outl(val, 0xcfc);  
  11. }  

总体来说。该BIOS扫描过程中调用如下几个主要的函数:

ob_pci_init  ----> ob_scan_pci_bus ----> pci_find_device ----> ob_pci_configure


下面我们来具体看看代码,首先BIOS执行ob_pci_init(void)函数

[cpp]  view plain copy
  1. int ob_pci_init(void)  
  2. {  
  3.         int bus;  
  4.         unsigned long mem_base, io_base;  
  5.     char *path;  
  6.   
  7. #ifdef CONFIG_DEBUG_PCI  
  8.     printk("Initializing PCI devices...\n");  
  9. #endif  
  10.   
  11.     /* brute force bus scan */  
  12.   
  13.     /* Find all PCI bridges */  
  14.   
  15.         //获取系统指定的memeory与I/O空间的范围,分配给PCIe设备。  
  16.     mem_base = arch->mem_base;  
  17.         /* I/O ports under 0x400 are used by devices mapped at fixed 
  18.            location. */  
  19.         io_base = arch->io_base + 0x400;  
  20.     path = strdup("");  
  21.   
  22.         /*遍历256条总线*/  
  23.         for (bus = 0; bus<0x100; bus++) {  
  24.         ob_scan_pci_bus(bus, &mem_base, &io_base, &path);  
  25.     }  
  26.     free(path);  
  27.     return 0;  
  28. }  


总线扫描具体实现:

[cpp]  view plain copy
  1. static void ob_scan_pci_bus(int bus, unsigned long *mem_base,  
  2.                             unsigned long *io_base, char **path)  
  3. {  
  4.     int devnum, fn, is_multi, vid, did;  
  5.     unsigned int htype;  
  6.     pci_addr addr;  
  7.     pci_config_t config;  
  8.         const pci_dev_t *pci_dev;  
  9.     uint32_t ccode;  
  10.     uint8_t class, subclass, iface, rev;  
  11.   
  12.     activate_device("/");  
  13.     for (devnum = 0; devnum < 32; devnum++) {  
  14.         is_multi = 0;  
  15.         for (fn = 0; fn==0 || (is_multi && fn<8); fn++) {  
  16. #ifdef CONFIG_XBOX  
  17.             if (pci_xbox_blacklisted (bus, devnum, fn))  
  18.                 continue;  
  19. #endif  
  20.             addr = PCI_ADDR(bus, devnum, fn);       /*获取设备配置空间地址*/  
  21.             vid = pci_config_read16(addr, PCI_VENDOR_ID);  /*获取Vendor ID*/  
  22.             did = pci_config_read16(addr, PCI_DEVICE_ID);  /*获取Device ID*/  
  23.   
  24.             if (vid==0xffff || vid==0)  
  25.                 continue;  
  26.   
  27.             ccode = pci_config_read16(addr, PCI_CLASS_DEVICE);  
  28.             class = ccode >> 8;  
  29.             subclass = ccode;  
  30.             iface = pci_config_read8(addr, PCI_CLASS_PROG);  
  31.             rev = pci_config_read8(addr, PCI_REVISION_ID);  
  32.   
  33.             pci_dev = pci_find_device(class, subclass, iface,/*具体设备查找以及初始化*/  
  34.                           vid, did);  
  35.   
  36. #ifdef CONFIG_DEBUG_PCI  
  37.             printk("%x:%x.%x - %x:%x - ", bus, devnum, fn,  
  38.                     vid, did);  
  39. #endif  
  40.             htype = pci_config_read8(addr, PCI_HEADER_TYPE);  
  41.             if (fn == 0)  
  42.                 is_multi = htype & 0x80;  
  43.   
  44.             if (pci_dev == NULL || pci_dev->name == NULL)  
  45.                             snprintf(config.path, sizeof(config.path),  
  46.                      "%s/pci%x,%x", *path, vid, did);  
  47.             else  
  48.                             snprintf(config.path, sizeof(config.path),  
  49.                      "%s/%s", *path, pci_dev->name);  
  50. #ifdef CONFIG_DEBUG_PCI  
  51.             printk("%s - ", config.path);  
  52. #endif  
  53.             config.dev = addr & 0x00FFFFFF;  
  54.   
  55.             REGISTER_NAMED_NODE(ob_pci_node, config.path);  
  56.   
  57.             activate_device(config.path);  
  58.   
  59.                         ob_pci_configure(addr, &config, mem_base, io_base); /*配置设备的配置空间*/  
  60.             ob_pci_add_properties(addr, pci_dev, &config);  
  61.   
  62.                         if (class == PCI_BASE_CLASS_BRIDGE &&  
  63.                             (subclass == PCI_SUBCLASS_BRIDGE_HOST ||  
  64.                              subclass == PCI_SUBCLASS_BRIDGE_PCI)) {  
  65.                 /* host or bridge */  
  66.                 free(*path);  
  67.                 *path = strdup(config.path);  
  68.             }  
  69.   
  70.         }  
  71.     }  
  72.     device_end();  
  73. }  


具体某条总线上的设备扫描由以下函数实现:

[cpp]  view plain copy
  1. <pre name="code" class="cpp">const pci_dev_t *pci_find_device (uint8_t class, uint8_t subclass,  
  2.                                   uint8_t iface, uint16_t vendor,  
  3.                                   uint16_t product)  
  4. {  
  5.     int (*config_cb)(const pci_config_t *config);  
  6.     const pci_class_t *pclass;  
  7.     const pci_subclass_t *psubclass;  
  8.     const pci_iface_t *piface;  
  9.     const pci_dev_t *dev;  
  10.     const void *private;  
  11.     pci_dev_t *new;  
  12.     const char *name, *type;  
  13.   
  14.     name = "unknown";  
  15.     type = "unknown";  
  16.     config_cb = NULL;  
  17.     private = NULL;  
  18.   
  19.     if (class == 0x00 && subclass == 0x01) {  
  20.         /* Special hack for old style VGA devices */  
  21.         class = 0x03;  
  22.         subclass = 0x00;  
  23.     } else if (class == 0xFF) {  
  24.         /* Special case for misc devices */  
  25.         dev = misc_pci;  
  26.         goto find_device;  
  27.     }  
  28.     if (class > (sizeof(pci_classes) / sizeof(pci_class_t))) {  
  29.         name = "invalid PCI device";  
  30.         type = "invalid";  
  31.         goto bad_device;  
  32.     }  
  33.     pclass = &pci_classes[class];  
  34.     name = pclass->name;  
  35.     type = pclass->type;  
  36.     for (psubclass = pclass->subc; ; psubclass++) {  
  37.         if (psubclass->subclass == 0xFF)  
  38.             goto bad_device;  
  39.         if (psubclass->subclass == subclass) {  
  40.             if (psubclass->name != NULL)  
  41.                 name = psubclass->name;  
  42.             if (psubclass->type != NULL)  
  43.                 type = psubclass->type;  
  44.             if (psubclass->config_cb != NULL) {  
  45.                 config_cb = psubclass->config_cb;  
  46.             }  
  47.             if (psubclass->private != NULL)  
  48.                 private = psubclass->private;  
  49.             if (psubclass->iface != NULL)  
  50.                 break;  
  51.             dev = psubclass->devices;  
  52.             goto find_device;  
  53.         }  
  54.     }  
  55.     for (piface = psubclass->iface; ; piface++) {  
  56.         if (piface->iface == 0xFF) {  
  57.             dev = psubclass->devices;  
  58.             break;  
  59.         }  
  60.         if (piface->iface == iface) {  
  61.             if (piface->name != NULL)  
  62.                 name = piface->name;  
  63.             if (piface->type != NULL)  
  64.                 type = piface->type;  
  65.             if (piface->config_cb != NULL) {  
  66.                 config_cb = piface->config_cb;  
  67.             }  
  68.             if (piface->private != NULL)  
  69.                 private = piface->private;  
  70.             dev = piface->devices;  
  71.             break;  
  72.         }  
  73.     }  
  74. find_device:  
  75.     if (dev == NULL)  
  76.     goto bad_device;  
  77.     for (;; dev++) {  
  78.         if (dev->vendor == 0xFFFF && dev->product == 0xFFFF) {  
  79.             goto bad_device;  
  80.         }  
  81.         if (dev->vendor == vendor && dev->product == product) {  
  82.             if (dev->name != NULL)  
  83.                 name = dev->name;  
  84.             if (dev->type != NULL)  
  85.                 type = dev->type;  
  86.             if (dev->config_cb != NULL) {  
  87.                 config_cb = dev->config_cb;  
  88.             }  
  89.             if (dev->private != NULL)  
  90.                 private = dev->private;  
  91.             new = malloc(sizeof(pci_dev_t));  
  92.             if (new == NULL)  
  93.                 return NULL;  
  94.             new->vendor = vendor;  
  95.             new->product = product;  
  96.             new->type = type;  
  97.             new->name = name;  
  98.             new->model = dev->model;  
  99.             new->compat = dev->compat;  
  100.             new->acells = dev->acells;  
  101.             new->scells = dev->scells;  
  102.             new->icells = dev->icells;  
  103.             new->config_cb = config_cb;  
  104.             new->private = private;  
  105.   
  106.             return new;  
  107.         }  
  108.     }  
  109. bad_device:  
  110.     printk("Cannot manage '%s' PCI device type '%s':\n %x %x (%x %x %x)\n",  
  111.            name, type, vendor, product, class, subclass, iface);  
  112.   
  113.     return NULL;  
  114. }  
  115.   
  116.   
  117.   
  118. 配置具体设备的配置空间  
  119.   
  120. static void ob_pci_configure(pci_addr addr, pci_config_t *config, unsigned long *mem_base,  
  121.                  unsigned long *io_base)  
  122.   
  123. {  
  124.     uint32_t smask, omask, amask, size, reloc, min_align;  
  125.         unsigned long base;  
  126.     pci_addr config_addr;  
  127.     int reg;  
  128.     uint8_t irq_pin, irq_line;  
  129.           
  130.         /*配置中断引脚与中断编号*/  
  131.     irq_pin =  pci_config_read8(addr, PCI_INTERRUPT_PIN);  
  132.     if (irq_pin) {  
  133.         config->irq_pin = irq_pin;  
  134.         irq_pin = (((config->dev >> 11) & 0x1F) + irq_pin - 1) & 3;  
  135.         irq_line = arch->irqs[irq_pin];  
  136.         pci_config_write8(addr, PCI_INTERRUPT_LINE, irq_line);  
  137.         config->irq_line = irq_line;  
  138.     } else  
  139.         config->irq_line = -1;  
  140.   
  141.         /*配置memory空间和I/O空间*/  
  142.     omask = 0x00000000;  
  143.     for (reg = 0; reg < 7; reg++) {  
  144.   
  145.         config->assigned[reg] = 0x00000000;  
  146.         config->sizes[reg] = 0x00000000;  
  147.   
  148.         if ((omask & 0x0000000f) == 0x4) {  
  149.             /* 64 bits memory mapping */  
  150.             continue;  
  151.         }  
  152.   
  153.         if (reg == 6)  
  154.             config_addr = PCI_ROM_ADDRESS;  
  155.         else  
  156.             config_addr = PCI_BASE_ADDR_0 + reg * 4;  
  157.   
  158.         config->regions[reg] = pci_config_read32(addr, config_addr);  
  159.   
  160.         /* get region size */  
  161.   
  162.         pci_config_write32(addr, config_addr, 0xffffffff);  
  163.         smask = pci_config_read32(addr, config_addr);  
  164.         if (smask == 0x00000000 || smask == 0xffffffff)  
  165.             continue;  
  166.   
  167.         if (smask & 0x00000001 && reg != 6) {  
  168.             /* I/O space */  
  169.             base = *io_base;  
  170.             min_align = 1 << 7;  
  171.             amask = 0x00000001;  
  172.             pci_config_write16(addr, PCI_COMMAND,  
  173.                        pci_config_read16(addr,  
  174.                                  PCI_COMMAND) |  
  175.                                  PCI_COMMAND_IO);  
  176.         } else {  
  177.             /* Memory Space */  
  178.             base = *mem_base;  
  179.             min_align = 1 << 16;  
  180.             amask = 0x0000000F;  
  181.             if (reg == 6) {  
  182.                 smask |= 1; /* ROM */  
  183.             }  
  184.             pci_config_write16(addr, PCI_COMMAND,  
  185.                        pci_config_read16(addr,  
  186.                                 PCI_COMMAND) |  
  187.                                 PCI_COMMAND_MEMORY);  
  188.         }  
  189.         omask = smask & amask;  
  190.         smask &= ~amask;  
  191.         size = (~smask) + 1;  
  192.         config->sizes[reg] = size;  
  193.         reloc = base;  
  194.         if (size < min_align)  
  195.             size = min_align;  
  196.         reloc = (reloc + size -1) & ~(size - 1);  
  197.         if (*io_base == base) {  
  198.             *io_base = reloc + size;  
  199.             reloc -= arch->io_base;  
  200.         } else {  
  201.             *mem_base = reloc + size;  
  202.         }  
  203.         pci_config_write32(addr, config_addr, reloc | omask);  
  204.         config->assigned[reg] = reloc | omask;  
  205.     }  
  206. }  
  207.   
  208. 通过以上这些步骤,Bios就完成了所有PCI设备的扫描,并且为每个设备分配好了系统资源。<br>  
  209. </pre>  
  210. <pre></pre>  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值