UEFI开发探索71- YIE001PCIe开发板(07 OptionROM框架)


请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

正在编辑新书《UEFI编程实践》(暂定名),现在介绍的内容,都整理到了第8章中了。

写书还是比较费神的,博客中可以随意行文,书稿则完全不能这么做。为了做到准确,有时为了确定一个数据,需要花费整个上午查找资料。期待今年上半年能够出版吧。

关于Option ROM,在第34、35和36篇中,已经描述过其历史和原理了。现在有了开发板,就可以在上面直接进行开发了。

1 开发Option ROM

代码是基于BlankDrv中的架构进行开发的,主要修改Supported()函数和Start()函数。Supported()函数中,判断是否为目标PCIE设备(也即是否为YIE001开发板);Start()函数中,添加界面代码以及访问硬件设备的代码。

示例1中给出了修改后的Supported()代码。

【示例1】Supported()函数

EFI_STATUS EFIAPI BlankDrvDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   Controller,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath   OPTIONAL)
{
  EFI_STATUS Status;
  UINT16 MyVendorID, MyDeviceID;
EFI_PCI_IO_PROTOCOL  *PciIo;
  Status = gBS->OpenProtocol (Controller, &gEfiPciIoProtocolGuid,
                  (VOID **) &PciIo,This->DriverBindingHandle,
                  Controller,EFI_OPEN_PROTOCOL_BY_DRIVER);
  if (EFI_ERROR (Status)) return Status;
  Status = PciIo->Pci.Read(PciIo,EfiPciIoWidthUint16,0,1,&MyVendorID);
  if (EFI_ERROR (Status)) goto Done; //获取厂商ID
  Status = PciIo->Pci.Read(PciIo,EfiPciIoWidthUint16,2,1,&MyDeviceID);
  if (EFI_ERROR (Status)) goto Done; //获取设备ID
  Status = EFI_SUCCESS;
  //通过厂商ID和设备ID,判断是否为目标设备
  if (MyVendorID != CH366_VENDOR_ID || MyDeviceID != CH366_DEVICE_ID)
    Status = EFI_UNSUPPORTED;
  Done:  //关闭所用的Protocol
  gBS->CloseProtocol(Controller,&gEfiPciIoProtocolGuid,
         This->DriverBindingHandle,Controller);
  return Status;
}

在Supported()函数中,首先使用OpenProtocol()打开PCI I/O Protocol,如果打开失败,则返回EFI_UNSUPPORTED。然后使用得到的PCI I/O Protocol实例,从PCI设备的配置空间,获得厂商ID和设备ID。如果所得到的厂商ID及设备ID,与目标设备的一致,则返回EFI_SUCCESS,否则返回EFI_UNSUPPORTED。

示例2中给出了修改后的Start()函数。

【示例2】Start()函数

EFI_STATUS EFIAPI BlankDrvDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   Controller,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath   OPTIONAL  )
{
  ……  //代码略
  Status = PciIo->Pci.Read(PciIo,EfiPciIoWidthUint16,16,1,&MyIoBaseAddr);     
  if (EFI_ERROR (Status)) goto Done;
  MyIoBaseAddr=(MyIoBaseAddr&0x0fffe);  //得到I/O基地址
HelloUEFI();                              //Oprom的演示函数
  Status = EFI_SUCCESS;
Done: ……  //代码略
return Status;
}

Start()函数在函数起始,通过PCI I/O Protocol的实例,在设备的PCI空间中将此基地址取到。其后调用HelloUEFI()函数,实现需要的功能。

也就是说,完成这些框架后,可以把精力集中在HelloUEFI()函数的编写上。UEFI开发探索的系列博客中,使用各种Protocol,实现了各种功能的示例程序。这些程序略作修改,都可以移植到这个地方。

本篇中,直接在HelloUEFI()中,添加打印字符串,并等待用户输入,实现简单的演示功能就可以了。如下所示:

VOID HelloUEFI(VOID)
{
  gST->ConOut->OutputString(gST->ConOut,L”Hello, I am YIE001!\n\r”)
  WaitKey();  //等待用户按键
}

2 编译及测试Option ROM

所编写的UEFI驱动,需要按照一定的方法编译,才能生成Option ROM。在第36篇中,已经比较详细地介绍过如何编译了。

本篇采用修改INF文件的方式,对UEFI驱动进行编译。示例3中给出了INF文件的修改样本。

【示例3】INF文件

[Defines]
  ……                        //其他变量,略去
  PCI_VENDOR_ID = 0x1C00     //厂商ID
  PCI_DEVICE_ID = 0x4349     //设备ID
  PCI_CLASS_CODE = 0x020000 //设备分类号
  PCI_REVISION = 0x0003      //代码版本
  PCI_COMPRESS = TRUE        //是否压缩,TRUE为压缩

假设自己编写的示例工程为MyOprom,则编译命令如下:

C:\UEFIWorkspace>build -t VS2015x86 -p RobinPkg\RobinPkg.dsc \
-m RobinPkg\Drivers\MyOprom\ MyOprom.inf -a X64

注意使用X64目标架构进行编译,大部分的机器基本上都是64位的UEFI BIOS了。

编译完成之后,将在输出目录中生成MyOprom.efi和MyOprom.rom两个文件。使用EfiRom工具,运行“-d”命令,可以查看MyOprom.rom的信息。可以看到MyOprom.rom已经是个有效的UEFI Option ROM文件,其厂商ID、设备ID等信息,与INF文件中设定的一致。

测试的方法有两种,一是在UEFI Shell下,使用load命令或者loadpcirom命令,直接将驱动挂载在设备控制器的句柄上,显示MyOprom中实现的功能;二是将二进制ROM文件,按照CH366的要求,以二进制的形式写入YIE001上的Flash ROM中,进行测试。

这两种测试方法,都要求YIE001开发板插在实际机器上运行。

这里先介绍下前一种测试方法,后一种方法,在YIE001的下一篇博客中再讨论。

将编译好的64位efi程序和rom文件,即MyOprom.efi和MyOprom.rom复制到UEFI启动U盘中。

把开发板YIE001插在主板的PCIe槽上,然后启动测试的计算机,进入UEFI Shell环境。在

UEFI Shell环境下,可以使用Shell命令“pci”列举出所有PCI设备,查看开发板YIE001是否被系统识别出来。YIE001开发板上设定的厂商ID是0x1C00,设备ID是0x4349,可在列举出的PCI设备中寻找是否存在。

使用如下命令进行测试:

load MyOprom.efi

或者

loadpcirom MyOprom.rom

加载驱动(或者Option ROM)后,屏幕上会显示示例工程中HelloUEFI()函数打印的字符串。

1,222 total views, 2 views today

发表评论

电子邮件地址不会被公开。