爱影博客

记录生活 知识分享

如何读取PCI device Bar的type,base address,以及size

PCI标准定义了三种物理地址空间,分别是IO address, Memory address space以及Configuration address space。其中Configuration address space专门用于配置PCI设备,PCI标准规定了标准的configuration space header,每个PCI设备都必须实现,软件通过configuration address space,来访问device的configuration space header,从而可以对device进行配置。

PCI标准要求device将内部寄存器,片上内存等资源map到IO space或者Memory space,从而软件可以通过发起IO/Memory space的读写来访问device的内部资源。PCI标准在configuration space header里预留了6个32-bit的Base address (BAR),系统软件可以通过往BAR里设置IO/Memory space的基地址,来实现对device内部资源的影射。在设置好这些BAR后,一旦软件发起对这些地址的读写,都会被device截获。

PCI规定32位BAR的低4位为read-only,标识了这个device资源在IO space,32-bit的Memory space还是64-bit的Memory space,以及访问时是否prefetchable。BAR的最后一位标识了是IO space还是Memory space。

   BAR[bit:0] = 1 — IO space
                    = 0 — Memory space
《如何读取PCI device Bar的type,base address,以及size》
《如何读取PCI device Bar的type,base address,以及size》

在OS启动前,BIOS会预先program好device的各个BAR的基地址,所以一般OS不需要重新去program BAR,但是当device资源出现冲突时,系统也可以通过写BAR的值,来重新修改device的资源影射。

PCI标准规定,先往32位BAR里写0xffffffff,再读取该BAR,就可以得到该BAR的size。如果读到的size为0,说明该BAR没有被device使用。

Power-up software can determine how much address space the device requires by writing a
value of all 1’s to the register and then reading the value back. The device will return 0’s in
all don’t-care address bits, effectively specifying the address space required. Unimplemented
Base Address registers are hardwired to zero.

如果该BAR是64-bit Memory space,则下一个BAR被认为是64位的高32位,64位size的高32位,也可以通过同样的方法得到。

64-bit (memory) Base Address registers can be handled the same, except that the second
32-bit register is considered an extension of the first; i.e., bits 32-63. Software writes
0FFFFFFFFh to both registers, reads them back, and combines the result into a 64-bit value.
Size calculation is done on the 64-bit value.

读取base,size以及type的伪代码:

   u32 base, size;
   pci_resource_type type;    (io_space, mem32_space, mem64_space)
   boolean  prefetchable;
   
   //得到base address
   pci_config_read(device, BARi, &base);    

   //得到size
   pci_config_write(device, BAR, 0xffffffff);    
   pci_config_read(device, BAR, &size);

   //重新写回base address,有需要也可以是其他的地址
   pci_config_write(device, BAR, base);    

   //确定type
   if (base & 0x1)
       type = io_space;
   else if((base & 0x6) == 0x4)
       type = mem64_space;
   else
       type = mem32_space;

   prefetchable = (base & 0x8) ? true : false;

   //如果是64-bit Memory space,得到base address和size的高32位,方法相同
   if (type == mem64_space) {
       u64 base64 = base;
       u64 size64 = size;

       //BAR+4指向下一个BAR的位置,此时被认为是64位的高32位
       pci_config_read(device, BAR+4, &base);    

       base64 |= ((u64)base << 32);
       
       pci_config_write(device, BAR+4,    0xffffffff);
       pci_config_read(device, BAR+4, &size);

       size64 |= ((u64)size << 32);
   }

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注