韩德强,马 骏,张 强
(北京工业大学 计算机学院,北京100124)
统一可扩展固件接口UEFI(Unified Extensible Firmware Interface)是 Intel推出的新一代 BIOS技术,旨在定义一套操作系统与平台固件之间完整的接口规范,为操作系统的引导提供标准环境[1]。相比于使用汇编语言编写的传统BIOS,UEFI采用模块化的设计、C语言风格的参数堆栈传递方式,借由动态链接的形式所构建出来的系统,更易于实现[2]。 同时,UEFI是以 32 bit或 64 bit CPU保护模式运行,突破了传统16 bit实模式代码的寻址能力,可达到CPU的最大寻址空间。得益于这些优点,UEFI经过近10年的推广,已经成为主流,将使用传统BIOS的微型计算机系统逐渐淘汰出市场[3]。
UEFI中引入了UEFI驱动程序模型的概念,采用驱动/协议的结构开发驱动程序,驱动程序和硬件完全独立,具有很强的扩展性,从而使得UEFI平台下添加新的特性变得简单[4]。
UEFI主要由引导管理器、固件内核、协议、驱动模型等组件构成[4]。其中固件内核为UEFI的基础,它将底层硬件功能抽象化,为上层的引导管理器提供两种服务:引导服务和运行时服务。引导管理器是一个策略引擎,它使用固件内核提供的服务加载UEFI驱动程序和应用程序,并最终加载操作系统。
图1所示为UEFI的系统框架图。固件内核运行在PEI和DXE阶段,UEFI在PEI阶段直接与硬件层打交道,启动必需的硬件资源,譬如完成CPU和芯片组的初始化,进而满足DXE的执行启动条件;其后,系统在DXE阶段完成其他所有硬件的初始化,并为上层接口实现引导服务和运行时服务。引导管理器运行在DXE和BDS阶段,它通过加载框架驱动和平台驱动向上层提供UEFI服务和接口,通过加载应用程序扩展系统功能,并在BDS阶段提供一个引导菜单,供用户选择引导设备。最终引导加载器在ROM上加载操作系统加载器,将控制权移交给操作系统,完成操作系统的引导。
CC2531是TI公司推出的一款用于IEEE 802.15.4或ZigBee应用的片上系统解决方案,它能够以非常低的成本建立网络节点,支持低功耗的无线通信,主要用于远程控制、家庭控制等领域[5]。CC2531集成了USB2.0功能模块,可以更加方便地与主机进行通信。
本文将CC2531 ZigBee模块(以下简称 CC2531模块)通过USB接口与无线POS机的主机相连,CC2531模块通过ZigBee无线网络与POS机的外设(如打印机、客显、键盘等)进行信息交互。此方案实现了在启动操作系统之前对POS机及其外设进行相应远程检测和诊断的功能。图2为无线POS机系统的结构框图。
图1 UEFI系统结构框架图
图2 无线POS机系统结构框图
UEFI驱动程序模型使用句柄代表设备,每个设备对应有自己的句柄,句柄由一个或多个协议组成。协议是一个以128 bit的全局唯一标识符GUID(Globally Unique Identifier)命名的结构体,是一些指针和数据结构体或者规范定义的接口函数指针的集合,协议代表设备提供的一类服务,服务的具体功能在设备驱动程序(以下简称驱动)中实现。开发者首先找到指定设备句柄上挂载的指定协议,再通过协议提供的接口访问设备驱动中实现服务的功能函数,对设备进行操作。图3所示为设备句柄和协议的结构图。
图3 设备句柄和协议的结构图
UEFI驱动程序模型是一种用于简化设备驱动设计和执行的机制,遵循驱动程序模型规范的UEFI驱动的可执行镜像大小会得到有效的减小[6]。UEFI驱动程序模型的执行流程图如图4所示。
图4 UEFI驱动程序模型执行流程图
驱动程序模型采用UEFI驱动载入、连接的形式来进行硬件的辨识、控制及系统资源掌控。在DXE阶段,系统调用引导服务的LoadImage()函数将驱动镜像文件加载到内存中,调用StartImage()函数执行驱动的入口函数来启动驱动。遵循模型规范的设备驱动在入口函数的初始化中不涉及任何硬件操作,仅仅实现驱动绑定协议(Driver Binding Protocol),协议包含 3个接口函数:Support()、Start()和 Stop()。Support()函数用来验证驱动程序与给定的设备句柄是否匹配;Start()函数负责驱动与句柄的连接,即将抽象 I/O功能的协议安装到设备句柄上;相对应的,Stop()函数则会强制停止驱动对一个设备句柄的管理和控制,并卸载设备句柄在Start()中安装的所有协议。
DXE阶段执行完成后,虽然加载和启动了驱动,但还在等待着连接设备句柄。因此,在BDS阶段引导管理器将会调用引导服务的ConnectController()函数执行驱动的连接过程。该函数执行所有驱动绑定协议的Support()进行设备句柄的验证,若验证正确,则会继续调用Start()连接驱动和设备句柄,并在设备句柄上安装抽象I/O功能的协议。
图5所示是由一系列驱动组成的多层次的、完整的USB驱动协议栈。处于上层的驱动会使用下层驱动提供的服务,并为更高层的驱动提供服务。首先,PCI总线驱动枚举设备时发现USB主机控制器,并在控制器句柄上安装EFI_PCI_IO_PROTOCOL协议,USB主机控制器驱动则使用EFI_PCI_IO_PROTOCOL协议提供的接口实现EFI_USB_HC_PROTOCOL协议。然后,USB总线驱动使用EFI_USB_HC_PROTOCOL协议服务实现对USB设备的枚举,生成CC2531设备句柄,并在设备句柄上安装 EFI_USB_IO_PROTOCOL协议。最后,CC2531模块设备驱动使用EFI_USB_IO_PROTOCOL协议服务,在CC2531模块设备句柄上安装EFI_USB_CCCONTROL_PROTOCOL协议,完成CC2531模块驱动协议栈的加载。
图5 CC2531模块驱动协议栈结构图
CC2531模块驱动作为设备驱动,遵循UEFI驱动程序模型规范,使用驱动绑定协议的方式实现设备驱动的加载和连接。同时,驱动还提供通信协议EFI_USB_CCCONTROL_PROTOCOL用于系统与CC2531模块间的通信,并通过ZigBee网络与无线POS机外设进行通信。
(1)Support():图 6所示为 Support函数实现流程图,函数首先检查给定的目标设备句柄是否安装有 EFI_USB_IO_PROTOCOL协议,若有,则说明USB总线驱动已经辨认出该USB设备。其次,使用EFI_USB_IO_PROTOCOL协议提供的功能接口函数获取目标USB设备的设备描述符,描述符中的接口号、接口类型、子类型和协议等字段指定了的USB设备的类型,检查这些字段就可得知驱动是否为CC2531模块设备驱动,如果匹配则说明加载的驱动正确,就会执行Start()函数。以下是部分实现代码:
图6 Support实现流程图
(2)Start():Start()函数的目标是使用USB总线驱动提供的协议服务在CC2531模块设备驱动中安装通信协议EFI_USB_CCCONTROL_PROTOCOL。函数首先打开设备句柄上挂载的EFI_USB_IO_PROTOCOL协议,使用该协议提供的接口函数UsbGetInterfaceDescriptor()和UsbGetEndpointDescriptor()得到USB设备的接口描述符和端点描述符,分析设备描述符中的字段以确定USB设备信息和接口类型等参数。其次,为驱动私有数据结构体分配内存,并使用设备描述符中的字段信息进行初始化。最后,安装EFI_USB_CCCONTROL_PROTOCOL协议,完成驱动与设备的连接。图7所示为Start()函数实现流程图。
图7 Start()函数实现流程图
(3)Stop():Stop()是 Start执行流程的逆过程,该函数用于卸载EFI_USB_CCCONTROL_PROTOCOL协议,释放驱动私有数据结构体占用的内存资源,关闭EFI_USB_IO_PROTOCOL协议,断开CC2531模块驱动与设备句柄的连接。
为了实现UEFI系统与CC2531模块之间的数据通信,本文设计了EFI_USB_CCCONTROL_PROTOCOL作为驱动的通信协议。该协议中包括了UEFI系统与CC2531模块间数据发送和接收的接口函数,还有部分与POS机系统外设交互的命令函数,如系统外设启动和复位命令的接口函数。协议的部分成员函数定义如下:
CC2531模块的USB接口固件代码是基于USB通信设备类CDC(Communication Device Class)协议实现的。CDC由通信接口类和数据接口类组成,通信接口类主要负责设备的管理和控制,数据接口类则负责数据的传输。CC2531模块的USB接口采用端点0作为通信接口类中的控制端点来管理设备的枚举和命令控制,数据接口类使用块传输输入(IN)端点和块传输输出(OUT)端点实现数据的双向传输。因此,UEFI系统对CC2531模块的枚举和识别采用控制传输方式,数据通信则采用批量传输方式。
协议的发送接口函数为 UsbCCDataTransmit(),该函数调用EFI_USB_IO_PROTOCOL协议提供的服务函数UsbBulkTransfer(),通过将数据写入输出端口完成数据从主机到CC2531模块的批量传输。设备驱动发送数据接口函数的部分代码实现如下所示:
CC2531模块设备驱动的数据接收操作亦通过调用函数UsbBulkTransfer()检测输入端口的缓冲队列完成。由于数据接收操作的被动性,驱动需要建立一套读操作的响应触发机制。本方案使用UEFI的事件(EVENT)机制实现对数据接收操作的响应:首先在驱动的Start()创建一个定时器事件,并在定时器的响应函数USBCC2531Timer-Handler()中周期性调用UsbCCDataRecevice()执行对输入端口缓冲队列的读操作,如果成功读到数据,则将读回的数据存入私有数据结构体的循环队列UsbDataQueue中。其次,创建一个等待事件WaitForInputString,并在等待事件的触发函数中检查循环队列,如有数据则读取,否则循环等待。部分数据读取操作的代码如下:
UEFI采用的模块化设计和驱动程序模型都为整体系统提供了良好的兼容性和扩展性,简化了UEFI驱动和应用程序的开发难度,提高了可维护性,有助于计算机固件的进一步发展。相对于传统BIOS,UEFI具有明显的优越性,越来越得到业界和市场的认可。本文从UEFI功能扩展的角度考虑,分析了UEFI的架构和驱动程序模型,设计并实现了CC2531模块的设备驱动程序,最终实现了在启动操作系统之前通过CC2531模块与无线POS机外设进行通信的目标。本方案的下一目标是扩展UEFI下的网络应用,实现对POS机系统的远程管理和诊断。
[1]Unified EFI,Inc.Unified extensible firmware interface specification[S].Version2.3.1 Errata C.2012:1-22.
[2]朱贺新.基于 UEFI的可信 BIOS平台研究与应用[D].西安:西安科技大学,2008:11-15.
[3]万象.基于UEFI系统的LINUX通用应用平台的设计与实现[D].上海:上海交通大学,2012.
[4]潘登,刘光明.EFI结构分析及Driver开发[J].计算机工程与科学,2006,28(2):115-117.
[5]Texas Instruments.CC2531 SOC solution for IEEE 802.15.4 and ZigBee applications(Rev.A)[Z].2011.
[6]ZIMMER V,ROTHMAN M,MARISETTY S.Beyond BIOS:developing with the unified extensible firmware interface(2 Edition)[M].Intel Press,2010.