2-UEFI的PCI设备扫描以及optionRom加载

1. 含有optionRom信息的PCI配置空间

1.1 Pci设备的配置空间

        PCI/PCIE总线时处理器系统的一部分,主要功能时连接外部设备。PCI总线有独立的地址空间,它与处理器的地址空间是隔离的。PCI下面的设备是以类似于桥的方式互相连接的,PCI树的根节点被叫做PCI Root Bridge,在它之下就是BUS(0)。BUS(0)下面可以连接两种设备,分别是PCI Bridge和PCI Agent。其中PCI Bridge用来扩展原来的PCI总线,在PCI Bridge之下可以延伸出新的总线,以此连接更多的设备。而PCI Agent就是真正的PCI设备(比如网卡)了,是PCI树的最末端。我们RAID/HBA卡属于PCI设备,下面介绍的PCIE配置空间,也是以Agent设备为例子来说明的。

        PCI设备的配置空间是256个字节;PCIE设备的配置空间是4K。目前我们知道配置空间的前面的一些字节就可以了。Agent的配置空间被成为Type 00h,主要的显示如下所示:

图1-1 agent设备的配置空间

        对于我们目前经常需要使用到的字段主要是0-31:Vendor ID,Device ID:标记了一个设备的生产厂商和具体的设备。

        0x30的位置开始存储着expansion rom的基础地址。

        备注:对于Bridge设备,存储expansion rom信息的寄存器在0x38这个位置,具体显示如下: 

图1-2 bridge设备的配置空间

        BISO若是想知道存储的optionRom需要多大的空间,只需要想0x30或者0x38寄存器的位置全部写上0,再读回来,就可以知道这个optionRom的大小了。

        有关详细的说明,可以参考:https://blog.csdn.net/jiangwei0512/article/details/51603525

1.2  optionRom的标记

       上一小节记录了,optionRom信息在PCI的配置空间上存储的位置。本小节分析一下optionRom的头是什么样子的。

       optionRom的头由UEFI的spec来规定的,具体的内容如下所示:

图1-3 optionRom的镜像和头

        如上图所示,PCI Option ROM由image header, PCI data structure和Image 三个部分组成。

        首先,在Option ROM的头两个字节是magic number: "0x55“,“0xAA"。由此可以验证是否是合法的option rom.

        其次,在0x18-0x19这个位置可以获取到PCI Data structure pointer, 这其实是一个地址,由这个地址,我们可以得到PCI Data structure所存放的位置。通过解析PCI Data structure,我们可以获得ROM的version, class type, size等等信息。

        Image是我们真正的可执行代码,是由我们写的代码生成的。

        这里的详细信息,可以参见:https://blog.csdn.net/huangkangying/article/details/8932463

2. optionRom的加载过程

2.1 镜像大小确认的流程

        在optionRom正式从flash加载到内存中之前,需要进行optionRom基本信息的获取,例如获取optionRom的大小信息。这里的函数调用关系如下所示:

图2-1 optionRom基本信息获取流程

        在这个流程里面,最终会调用函数GetOpRomInfo这个函数。

        这个函数的主要作用就是获取optionRom的大小信息,函数主要流程如下所示:

        这个函数里面会向寄存器0x30或者0x38(区分叶节点和桥节点)的高11-31位置(32位的寄存器)写入全是1的数值。再将该寄存器的值读回来,就会获得基本的rom大小信息,保存在RomSize变量里面。

2.2 镜像加载流程

2.2.1 ProcessOptionRom

        设备进行PCI设备扫描的大体流程如下:

图2-2 optionRom加载的流程

       加载root桥下面每一个子节点的optionRom

       枚举一个root桥下面所有设备的函数是:

        判断当前的root桥下面,是否有一个子节点。在确保有子节点且子节点不是指向自己本身,可以执行optionRom的加载。若是root下面的节点仍然有子成员,那么继续执行这个函数,一层套一层,否则执行函数LoadOpRomImage。

        这里按照pci的拓扑结构进行划分,一层又一层。直到根节点。

2.2.2 进LoadOpRomImage

        将存在optionRom设备的optionRom加载到内存中

        前面已经说过,存放optionRom位置信息的寄存器在PCI设备的0x30或者0x38的位置,主要是区分两种类型的PCI设备。下面有着代码的证据:

 

       如果设备时PCI的桥设备,这个寄存器所在的位置就是0x38,否则这个寄存器的位置就是0x30。

         为Rom的头结构与PCIR结构申请空间,并且是能rom相关的寄存器

        从指定的rombar的偏移地址读取数据,若是读取的数据头不是0x55aa,将会向下面偏移接下来的512字节。若是找到了,那就停止向下偏移寻址。这里的RomSize就是根据3.1小节的内容读取出来的数据。

       若是找到了optionRom,将FirstCheck标记设置为FALSE,若是Pcir寄存器的偏移是0,偏移不是4字节对齐,或者偏移过大,都会停止该流程。

         读取PCIr寄存器的值,标记位不对,结束流程;image大小过大结束流程;代码类型若是legacy的,那就为LegacyImageLength赋值。

         Legacy类型的代码,Rom的size是最大值的。若是读取的rom的大小是大于0的,将rom读取到内存中。

         将读取到的rom大小与子内存中的位置,为PciDevice->PciIo赋值。将optionRom的大小与位置信息,赋值给内部的数据结构。

2.2.3 流程性总结

        在PCI设备枚举阶段,会根据当前的pci root节点下面是否有设备,进行optionRom的扫描。扫描过程中区分节点设备和桥设备。节点设备存放optionRom信息的寄存器在PCI配置寄存器偏移0x30的位置,桥设备存放optionRom信息的寄存器在0x38的偏移位置上。以下的内容以节点设备为例子。

        若是想把optionRom拷贝到内存,先把0x30位置的内容读取回来,存放在PCI_EXPANSION_ROM_HEADER数据类型变量里面。这个数据类型是:

        PciHostBridgeResourceAllocator

        这个数据两个比较重要的信息,Signture中存放了optionRom的头信息,确保头部是0xaa55。

       pcirOffset存放了在那个位置可以获取PCI_DATA_STRUCTURE数据结构。这个数据结构显示如下:

 

       这个结构里面会保存,标记位,VendorId、DeviceID、版本号以及image大小等众多的重要信息。

在spec文档上,记录了标准optionRom的结构信息:

         对于UEFI下面optionRom的结构,显示如下:

         在PCI_DATA_STRUCTURE数据结构中保存着image的大小。在验证PCI_DATA_STRUCTURE数据符合要求后,会将image镜像拷贝到系统的内存中。

posted @ 2022-08-21 22:48  free-锻炼身体  阅读(2445)  评论(0编辑  收藏  举报