谈一谈PCI Option rom

BIOS在POST(Power-on Self Test)阶段,会扫描pci设备是否有expansion rom,有的话将其拷贝到ram中执行。在PCI规范中称为expansion rom,在BIOS术语里面称为option rom

Expansion rom base address

扩展空间桥

PCI_EXPANSION_ROM_BASE 0X30

1)PCIE枚举的时候,侦测到该PCIE设备;

(2)检查该PCIE设备是否包含有合法的Option Rom,即符合业界Option Rom标准规范,合法的关键字为0xaa55;如果有,则执行步骤(3),如果没有则执行步骤(6);

(3)从步骤(2)得知含有合法的Option Rom,再进一步分析该Option Rom:从中找到关键字“PCIR”处的地址,然后读取该地址处有关Option Rom类型的字段type,如果类型type字段为0x03,则表明为UEFI Option Rom,则执行步骤(4);如果该字段不是0x03,则执行步骤(6);

(4)拷贝UEFI Option Rom到内存中;

(5)运行拷贝到内存中的UEFI Option Rom;

PCI配置空间  Expansion rom base address 0xfffffffe

然后返回基本的rom大小信息,保存在RomSize变量里面。

当返回的地址不是0或者0xffffffffe

就认为该PCI的opronrom寄存器有配置过 返回的大小

 

PCI扫描过程中是如何确定PCI设备是支持option rom的?

InitializePciDevices->EnumerateBus->QueryPciDevice-> GetOptRomRequirements

Option Rom驱动后面是如何被处理的?

InstallPciDevice

 

 

Device List Pointer: points to the list of Device IDs supported by the Expansion ROM image

图1 UEFI Option ROM结构(From UEFI Spec 2.8 page 723)

毕竟都是脱胎于PCI/PCIE规范,和Legacy Option ROM的结构是相同的。UEFI Option ROM利用了之前保留的字节(偏移0x04处),用来表明自己的身份。

1 UEFI Option ROM的加载过程

Option ROM的执行文件不是在Flash上运行的,它会被拷贝到内存中,然后在内存中执行。Legacy BIOS的Option ROM一般加载到0xC0000~0xE0000(即0xC000段至0xE000段),而UEFI Option ROM并没有这样的约定。

仔细读读ProcessOpRomImage()的代码,可以窥见处理Option Rom的过程。

ProcessOpRomImage()的入口参数只有一个:PCI_IO_DEVICE *PciDevice。这是一个指向PCI设备的指针,包含了设备的所有属性以及其兄弟设备、父设备的信息。数据结构PCI_IO_DEVICE定义在PciBus.h中,可以对照PCI协议去理解。

函数中用do-while循环对设备进行循环处理,寻找到Option ROM的ROM signature(也即0xAA55)。而后对ROM结构进行分析,包括是否为EFI image、机器类型是否支持等,并创建其设备路径(DevicePath)。

在UEFI中,使用Device Path去描述一个设备的位置信息,总线、启动项等也常用它来描述。进入UEFI shell的时候,出现的各设备的字符串描述,就是Device Path:

 

Device Path共有六种,在Uefi Spec中有详细的描述

typedef struct {

  UINT32  Signature;    ///< "PCIR"

  UINT16  VendorId;

  UINT16  DeviceId;

  UINT16  Reservd0;

  UINT16  Length;

  UINT8   Revision;

  UINT8   ClassCode[3];

  UINT16  ImageLength;

  UINT16  CodeRevision;

  UINT8   CodeType;

  UINT8   Indicator;

  UINT16  Reserved1;

} PCI_DATA_STRUCTURE;

函数中创建的Option ROM device path类型为MEDIA_DEVICE_PATH,这是一种能够作为启动项设备的Device Path。

创建完成后,调用LoadImage()和StartImage(),执行Option ROM代码。

1.GetOpRomInfo(IN OUT PCI_IO_DEVICE  *PciIoDevice)  //获取Option rom的信息 判断PCI设备是否支持Optionrom 支持写入

0xfffffffe返回option romRomSize

2.ContainEfiImage(

 IN VOID    *RomImage,

  IN UINT64  RomSize        

)

check if the RomImage contains EFI Images. 

#define PCI_DATA_STRUCTURE_SIGNATURE             SIGNATURE_32 ('P', 'C', 'I', 'R')

#define PCI_CODE_TYPE_EFI_IMAGE  0x03    //EFIimage

RomPcir= (PCI_DATA_STRUCTURE *)((UINT8 *)RomHeader + RomHeader->PcirOffset);

    if (RomPcir->Signature != PCI_DATA_STRUCTURE_SIGNATURE) {

      break;

    }

    if (RomPcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) {

      return TRUE;

    }

3.InitializePciLoadFile2()->LocalLoadFile2

 

@param RomBase    Base address of ROM driver loaded from.

4.

LoadOpRomImage (

  IN PCI_IO_DEVICE  *PciDevice,

  IN UINT64         RomBase 

  )   //

5.

ProcessOpRomImage (

  IN  PCI_IO_DEVICE  *PciDevice

  )

option rom Image需要512字节对齐  循环寻找所有的Image

FV拓展 EFI OPTIONROM

 

 

2 如何生成UEFI Option ROM

UEFI Option ROM实际上是UEFI driver的一种,EDKII提供了相应的工具,将生成文件转换为Option ROM。前面已经说过了,我们现在所开发的option ROM,主要是PCI Option ROM。关于PCI Option ROM的内容可以参考《EDKII Driver Writer’s Guide for UEFI 2.3.1》,以下的介绍也主要来自于这个文档。

有两种方法可以生成PCI Option ROM镜像,使用工具EfiRom转换或直接使用EDKII的INF/FDF文件编译生成。

EfiRom提供了源代码,允许用户在任何支持EDKII的操作系统上编译。源代码位于\BaseTools\Source\C\EfiRom下,在我开发用的机器上(Win10),编译后的执行文件位于\BaseTools\Bin\Win32。

ENTRY_POINT                    = FatEntryPoint

  UNLOAD_IMAGE                   = FatUnload

  PCI_VENDOR_ID                  = 0x1d94

  PCI_DEVICE_ID                  = 0x8086

  PCI_CLASS_CODE                 = 0x03

  PCI_COMPRESS                   =TURE

Loadpcirom  xxx.rom  判断loadrom是否是option rom 的header AA55 

((Pcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE)

EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE  = 0x0EF1  

UEFI Option ROM是通过UEFI Driver转换而来的,至于如何编写UEFI driver,那是另外一个议题,这里不展开。

EfiRom会对传入的efi文件(UEFI Driver)进行验证,比如Rom头是不是0xAA55、PCI数据结构标识是不是“PCIR”等。任何一项检查不通过,则EfiRom会退出,创建Option ROM的过程将被终止。

生成命令如下:

EfiRom -f 0x9999 -i 0x8000 -e pcidriver.efi

其中,-f后指定Verdor ID,-i后指定 Device ID,-e之后给定需要转换的文件。更多的转换方法,包括如何与Legacy Option ROM一起打包转换、如何压缩等,请参考之前提到的手册《EDKII Driver Writer’s Guide for UEFI 2.3.1》第18章第7节。

另一种转换方法是使用INF/FDF,在build命令执行的时候,自动调用efirom将其转换为指定的Option ROM。这是我常用的方法,编译的同时就完成了转换过程,一个典型的INF例子如下:

Vendor ID和Device ID可以在Inf文件中指定,其他包括PCI类码、PCI版本、是否对Option ROM进行压缩(PCI_COMPRESS)都可以指定。

不管是采用EfiRom工具直接转换,还是使用Inf文件,都只能对一个UEFI Driver进行处理。如果需要同时管理多个UEFI Driver,以及生成多种类型的Option ROM(IA32、X64等),可以使用FDF文件进行处理。具体的内容就不一一讨论了,同样可以参考上述的编程手册。

成功制作optionrom 文件在EFIshell 下使用Loadpcirom file 成功

但是发现start image 是不会执行到UEFI driver的Entry point  会执行DriverBingProtocol,还存在问题不会去执行option rom的Entry point,原因怀疑为fat.inf中包含的Lib无法使用导致Entry point无法执行,使用hellowworld.rom后成功执行Entripoint 代码

UefiMain.dll文件时使用了/dll/entry:_ModuleEntryPoint。.efi文件是遵循PE32格式的二进制文件,_ModuleEntryPoint是这个二进制文件的入口。

在Shell中执行UefiMain.efi时,Shell首先用gBS->LoadImage()将UefiMain.efi文件加载到内存生成Image对象,然后调用gBS->StartImage(Image)启动这个Image

 

 

所开发的Option ROM是遵循UEFI driver module的,实际上也算是PCI Driver,所以它必须实现EFI_DRIVER_BINDING_PROTOCOL,并实例化Supported(), Start(), and Stop() 这三个服务。

前面已经探讨过如何启动Option ROM了,目前我们主要关心在哪里加上实际执行的代码。

大部分执行代码是在Start()中添加的,Start()主要的任务是启动硬件设备,在函数中最重要的事情是调用InstallProtocolInterface()或者InstallMultipleProtocolInterfaces()在ControllerHandle上安装驱动Protocol。

而Stop()函数用来卸载驱动,并停止硬件设备,基本上不需要修改BlankDrv提供的原始代码。

Supported()用来评估传递给Driver的device handle所指明的pci controller是否能被driver所驱动,主要通过Vendor ID、Device ID、Class code判定。

如前所述,具体的驱动细节可以参考其他的文档,架构搭建完后,就可以专注在Option ROM的功能实现上了。之前35篇博客中所讨论的议题,所编写的代码,完全可以移植到Option ROM中,只要硬件设备的扩展ROM大小足够。

 

 开发Option ROM

Option rom的驱动driver 需要满足PCI的规范 不能是任意的UEFI Driver

加载option rom需要判断是否为目标PCIE设备

Driver image需要从一些媒介上加载,如ROM, FLASH, harddrives, floppy drives, CD-ROM甚至是network controller,当发现了driver image,就会通过loadimage()加载到system memory中,并创建一个handle,handle上挂载Loaded Image Protocol 实例,从而这个handle称为Image

Handle,此时driver并没有started,等待startimage()调用

loadimage()

*第一步:将UefiMain.efi文件加载到内存,生成Image对象,NewHandle为其句柄

句柄:

1、特殊的智能指针(当一个应用程序要引用其他系统管理的内存块或对象时)

2、Windows编程的基础。句柄指的是使用一个唯一的4字节型整数值,来标识应用程序中

的不同对象和同类中的不同示例。应用程序能通过句柄访问相应对象的信息,但这种句柄

不是指针,应用程序不能通过句柄直接阅读文件中的信息。句柄是Windows系统用来标识

应用程序中建立的或是使用的资源的唯一整数。

使用gBS->LoadImage()函数,将加载结果返回给Status参数,同时改变NewHand的值。

使用EFI_ERROR()函数判断是否加载成功。

*/

Status = gBS->LoadImage(

FALSE,

*ParentImageHandle,

(EFI_DEVICE_PATH_PROTOCOL*)DevicePath,

NULL,

0,

&NewHandle);

ImageHandle: 选择start的image handle

imagehandle在经startimage()执行后可能结构如下,一个挂载有driver binding protocol实例的image handle 称为Driver Image Handle

Startimage()

在gBS->StartImage中,SetJump/LongJump为应用程序的执行提供了一种错误处理机制,执行流程如图所示:

就是调用各个driver image 的入口函数,入口函数必须符合UEFI Driver Model,不接触硬件,只能在自己的image handle上install protocol 实例,如Driver Binding Protocol,ComponentName Protocol可选择性install;

 

Build  efi的指令

DEBUG_VS2008x86_IA32_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG

RELEASE_VS2008x86_IA32_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4254 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /MERGE:.data=.text /MERGE:.rdata=.text

由图可以看出,连接器在生产UefiMain.dll文件时使用了/dll/entry:_ModuleEntryPoint.efi文件是遵循PE32格式的二进制文件,_ModuleEntryPoint是这个二进制文件的入口。

  • 调用CoreLoadImage,从FV中load DXE image 到memory中, 应该是code中定义的FFS(DXE module),编译生成的PE/COFF格式image,load成功生成ImageHandle
  • Option Rom内置在设备中
  • Option Rom在UEFI引导下自动加载
  • EFI Option Rom包括作为设备驱动程序的EFI图像
  • ROM报头有0x0EF1作为签名
  • PCI数据结构包括DeviceId和VendorId
  • 选项rom图像可以包含多个图像,以支持不同的ARCHs。
  • 32
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值