戏说BIOS之PCI SCAN

戏说BIOSPCI SCAN 

1. Introduction

 

     PCIintel公司在1990年前后开发的,后续经过若干年的发展以及标准化,它已然成为server&pc上的标准总线。PCI以其出色的设计以及不错的通信速率在计算机领域攻城掠地,不断的取代诸如MCA,ISA,EISA,VESA,NuBus等传统总线。PCI相对于传统总线有非常多的优点,如:1.它是数据总线和地址总线是分时复用的,这样减少了pin脚节省了空间,而且这样也可以方便实现突发式数据传输。2.它是即插即用的(plug & play),当device插入系统时,系统会自动对device进行资源分配并加载对应driver,而传统的ISA device则需要做复杂的手工配置。3.中断共享,传统的总线有一个致命的缺陷就是它们是中断独占的,本来系统的中断就非常紧缺所以增加新的device会出现中断不够使用的麻烦,而pci irq routing机制使得不同的device irq共用成为现实。可是技术的发展总是长江后浪推前浪,前浪死在沙滩上J! PCI又逐渐被更好的总线PCIE所取代渐渐退出PC的历史舞台,后续我会再去研究一下PCIE

 

2. PCI Arch

 

       可能是软体背景的原因,因此我看PCI spec也会习惯性的使用软件设计的视角去理解PCI的设计(我觉得有关设计、架构的理论应该是相通的,正如软件中经典的design pattern的思想来源于建筑学一样)。我的视角里PCI同经典的接口编程或者插件式设计非常接近。接口本质上是一组规则的集合它是对同类事物行为上的表示,它的主要目标是实现相同类别的不同对象行为上的多态性。面向接口的编程是OO思想的精髓所在。它的好处体现在哪里呢?首先它增强了系统的灵活性,只要遵循接口定义的规则,系统的底层实现部分就可以灵活的替换、扩充如: PCI总线定出了设备的统一的硬件接口,这样遵循该接口pci device就可以方便的扩展入系统;另外相同的接口可以接入不同厂家的设备就像同样的sata接口可以接三星的光驱也可以接LG的。其次规则给出以后,实现该接口的部件就会有共通的接口但是不同的实现,如此系统端就可以通过接口灵活实现对部件的操作配置。PCI定义出了三种规格的配置空间,根据配置空间提供的信息系统端可以方便的识别设备的种类,功能甚至于厂商和版本号,获得非常丰富的系统端知识;而且该功能也使得设备可以动态的配置资源进而能够做到plug & play

 

3. PCI Scan

 

     PCI Configuration Space是大小为256字节的一块空间,它由headerdevice specific两个部分组成,其中header部分是固定的而device specific部分则是与device相关的,不同的device会有不同的layout。配置空间被用于配置,初始化以及灾难性错误处理的功能。下图1type 00h Configura-tion Space Header

 

                                  1

PCI Scan的重要任务就是读出该256bytes 配置空间,那么如何读取这部分的信息呢? 有下述两种方法:

1.使用0CF8-0CFB, 0CFC这两组IO port存取PCI Configuration Space总线号、设备号、功能号和寄存器号组合成一个双字送到配置地址端口 (CF8H-CFBH)然后读写配置数据端口 (CFCH)即可获得配置空间的数据,下图2是配置地址寄存器的格式定义:

 

                                           2

所以我们先要build一个config-address然后再去透过端口存取配置空间。下述代码用于build config-address

;----------------------------------------------------------------------------  

;   build _pci_cfg_add:        

;    build pci config address    

;    used registers:       eax,ebx

;-----------------------------------------------------------------------------

build_pci_cfg_add      proc   near

 

              push   eax

              push   ebx

 

              xor     eax,eax

              xor     ebx,ebx

              mov    PCI_CFG_ADDRESS,80000000h

 

              mov    al,PCI_BUS_NUM

              shl      ax,08h

              mov    bl,PCI_DEV_NUM

              shl      bx,03h

              or      ax,bx

              or      al,PCI_FUN_NUM

 

              shl      eax,08h

              or      PCI_CFG_ADDRESS,eax

             

              pop    ebx

              pop    eax    

              ret

build_pci_cfg_add      endp  

 

config-address准备好以后接下来就是透过IO port读取pci configuration space了,下述代码演示读取的过程:

;----------------------------------------------------------------------------  

;   read_cfg_space

;    read pci config space use io port    

;    Called with:    NULL

;    used registers:       eax,edx

;    returned regs:        eax

;-----------------------------------------------------------------------------

read_cfg_space proc   near

 

     mov    eax,PCI_CFG_ADDRESS

     or      eax,edx

     mov    dx,PCI_CFG_APORT

     out     dx,eax

     mov    dx,PCI_CFG_DPORT

     in       eax,dx

     ret    

                       

read_cfg_space endp  

 

理论上PCI bus支持256条总线,每条总线支持32device,每个device又支持8function,所以我们组合出上面所有的可能就可以遍历出所有的PCI 设备了。可是实际上PC上面PCI 总线通常只有1条,最多也不会超过四条所以我们只扫4条总线就可以了,不用做太多的无用功。有了前面的准备,我们就来实现一个类似RU中的PCI scan吧,下图3就是我写的pciscan运行的状况了:

 

                                                     3

在该界面下按下esc就会退出该程序;移动↑↓键就可以选中device,然后敲enter就会看到该deviceConfiguration space 如下图4所示

 

 

 

                                                     4

当前界面下如果想返回到上一个界面只需要按下F6就会回到图3的界面了。

 

2. Call PCI BIOS int1A同样也可以获取PCI device的信息。其中AH=B1hAL=function id所有的function id如下所示:

 

01h: INSTALLATION CHECK

02h: FIND PCI DEVICE

03h: FIND PCI CLASS CODE

06h: PCI BUS-SPECIFIC OPERATIONS

08h: READ CONFIGURATION BYTE

09h: READ CONFIGURATION WORD

0Ah: READ CONFIGURATION DWORD

0Bh: WRITE CONFIGURATION BYTE

0Ch: WRITE CONFIGURATION WORD

0Dh: WRITE CONFIGURATION DWORD

0Eh: GET IRQ ROUTING INFORMATION

0Fh: SET PCI IRQ

81h: INSTALLATION CHECK (32-bit)

82h: FIND PCI DEVICE (32-bit)

83h: FIND PCI CLASS CODE (32-bit)

86h: PCI BUS-SPECIFIC OPERATIONS (32-bit)

88h: READ CONFIGURATION BYTE (32-bit)

89h: READ CONFIGURATION WORD (32-bit)

8Ah: READ CONFIGURATION DWORD (32-bit)

8Bh: WRITE CONFIGURATION BYTE (32-bit)

8Ch: WRITE CONFIGURATION WORD (32-bit)

8Dh: WRITE CONFIGURATION DWORD (32-bit)

8Eh: GET IRQ ROUTING INFORMATION (32-bit)

8Fh: SET PCI IRQ (32-bit)

我们使用function id 09h就可以从configuration space 中读取出一个字,这样的操作明显简单的多了,只需call 一次int1a中断即可。下述c代码演示了读取Vendor id的过程,如需读取其它部分只要算出具体config-address即可。

 

#include

#include

#include

 

int main(int argc,char** argv)

{

 

     union REGS reg;

     argc = argc;

     argv = argv;

     reg.x.ax = 0xB109;

     reg.x.bx = 0x80000000;

    reg.x.di = 0; 

     int86(0x1A, ®, ®);

 

     if(reg.x.cx != 0xffff)

     {

       printf("Vendor : %4.4X/n", reg.x.cx);

     }

 

     return 0;

}

 

程序运行结果如下图5所示:

 

                                                     5

 

最后依旧是开放完整的source code和可执行文件供有兴趣的朋友下载。        

 

      

Enjoy it

 

 

That’s all!

 

Peter

你可能感兴趣的:(BIOS,Internals)