ExpansionOption ROM

必读资料

什么是Expansion/option rom?

  • Expansion rom是pci/pcie设备可选的一个外接的eprom芯片,其中用来存储相应pci设备的初始化代码或者系统启动代码(比如pxe或者pci boot)。
  • BIOS在POST(Power-on Self Test)阶段,会扫描pci设备是否有expansion_rom,有的话将其拷贝到ram中执行。
  • 在PCI规范中称为expansion rom,在BIOS术语里面称为option rom。
  • PCI Agent设备配置空间 0x30地址,4字节 为expansion rom信息

image.png

作用是什么?

在《PCIE体系结构》P46中解释:

有些PCI设备在处理器还没运行系统之前,比如在BIOS中,就需要完成基本配置,如PC的显卡,键盘,USB等。 为了实现这个”预先执行“功能,PCI设备需要提供一段ROM程序,处理器在初始化时运行这段ROM程序来初始化相关设备。 Expansion ROM 记载了ROM程序地址

总结:系统启动前的预初始化程序,存储在PCI设备EEPROM中,系统上电时执行这部分代码来初始化基础外设

注:Expansion ROM和BAR空间互斥使用,同一时间只能使能一个。

分布结构和使用

寄存器结构如上图:

  • bit[31:11]是PCIE域基地址
  • [10:1]Reserved
  • [0]使能位

怎么知道PCIE设备是否包含Expansion ROM?

并不是所有PCIe设备都包含Expansion ROM,所以就需要软件进行判定。
《PCIe Expansion Roms》中讲了:
判断长度是否为0即可。

怎么知道Expansion ROM的空间大小

在Expansion ROM的判定阶段,有一个过程是向Expansion ROM Base Address Register写入0xFFFF FFFF,然后读取它的值,利用这个值就可以判定出Expansion ROM的空间大小。

假设写入0xFFFF FFFF之后,读出的值是0xFFFE_0000/0xFFFE_0001. bit[0]代表是否使能
那么第17位就是非0值的最低位,那么Expansion ROM的大小就是2^17 Bytes= 128KB。
PCI文档规定,ROM最大可为16MB,即bit[31:25]必须是可读/可写的。

Expansion ROM的初始化过程

BIOS的POST阶段,扫描并执行pci设备的expansion的过程大概分以下几步:

  • 首先判断pci设备是否实现“Expansion rom base address”寄存器,有才可以用:
    如何实现? 和BAR一样写入0xFFFF_FFFF到这个配置空间,然后可以读取到长度,非0代表有数据;
  • 如果有实现了expansion的基址寄存器,则配置和使能expansion rom,然后查找expansion rom是否有”AA55”的标示字符,如果有则说明设备有真实的expansion rom芯片存在
  • 如果expansion rom已经存在,则扫描是否有适合本设备和本CPU架构的image代码存在;
  • 如果有适合本环境的image代码存在,则把相应的代码拷贝到ram的合适位置,并跳入header format中指定的初始化入口执行;
  • 最后关闭expansion rom的使能。

在Linux读取设备的Expansion ROM方法

  1. echo 1 > /sys/bus/pci/devices/0000:01:00.0/enable # 需要是能command的memory位才能读写。
  2. echo 1 > /sys/bus/pci/devices/0000:01:00.0/rom
  3. cat /sys/bus/pci/devices/0000:01:00.0/rom > /tmp/image.rom
  4. echo 0 > /sys/bus/pci/devices/0000:01:00.0/rom
  5. echo 0 > /sys/bus/pci/devices/0000:01:00.0/enable

Expansion rom解析工具: https://github.com/awilliam/rom-parser

Linux代码读取过程

这部分推荐参考sysfs中读取optionRom代码,没必要自己写。
参考:driver/pci/pci-sysfs.c即可

  1. // 1.使能 《PCIE体系结构》P48:command寄存器只有在使能IO/MEMORY后才能访问BAR和Expansion ROM空间pci_enable_device
  2. // 2.获取资源空间,因为这里地址是PCI域地址,所以不能直接读取配置空间
  3. // 《PCIE体系结构》P48:不能使用读取配置空间操作来访问这块地址,必须用
  4. // pci_dev->resource[6].start或者pci_resource_start 系列函数
  5. // struct pci_dev中:
  6. // struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
  7. // 由宏可知 0-5是bar空间,6是Expansion rom空间
  8. for(loop=0;loop<DEVICE_COUNT_RESOURCE; ++loop){
  9. printk("resource: start:%#llx,end:%#llx,name:%s,flags:%#lx,desc:%#lx\n",
  10. r->start,r->end,r->name,r->flags,r->desc);
  11. r++;
  12. }
  13. // 判断Expansion rom是否存在?如果存在,读取
  14. struct pci_bus *bus;
  15. struct pci_ops *ops;
  16. bus = pci_dev->bus;
  17. ops = bus->ops;
  18. printk("check Expansion ROM"); // devfn need to be ensuere
  19. ops->write(bus,0,0x30,4,0xffffffff);
  20. ops->read(bus,0,0x30,4,&tmp_val);
  21. printk("Expansion ROM len is %#x\n",tmp_val); // 可以读出长度
  22. ops->write(bus,0,0x30,4,0x00000000); // 用完之后记得释放