UEFI Driver
UEFI Driver Model
UEFI Driver主要用于管理设备,在DXE阶段被加载,在BDS阶段被启用,DXE负责把当前系统中所有的Driver,不管DXE Driver,DXE RunTime Driver,还是UEFI Driver 全部给加载起来,把entrypoint都执行完,DXE Driver是不负责加载Application的,Application是到BDS加载。在BDS阶段要把所有的Device值都初始化好,启动加载的Driver,让相应的Driver对Device进行管理。此时就可以提供相应的输入输出设备,启动设备。
为什么要引入UEFI Driver?
UEFI Driver的引入,更好的实现了模块化,模块化可以理解为这些UEFI Driver就是用来管理设备的,如果说Driver用来管理显卡,那么显卡的厂商就可以独立撰写一个UEFI Driver,这个Driver可以运行在所有的UEFI环境下面,而不必根据不同的环境进行调整。
所以对于UEFI Driver的模块化,可以做bindary形式的发布,可以build in 到option rom里面去,对外的入口是非常清晰的。这就是为什么说UEFI Driver把整个firmware扩展了,提高了固件整体的扩展性,可能说之前的固件只能跑跑Application,现在还可以加载很多的Driver,第三方写的一些Device Driver,可能不需要build in到flash上,可以在shell阶段重新加载或在BDS阶段加载。
对于跨平台性,同样一个Driver按照二进制形式进行发布后,就可以在所有follow UEFI规范的平台跑起来,这样的方式大家可以并行开发,平台开发和设备开发分别进行,加快进程,最后协同到一起。
上图明确展现了UEFI Driver的的概念,PEIM为PEI阶段的module,APP属于OS下面的Driver,UEFI Driver就可以泛指DXE和BDS之间的Driver,也可以理解为在PEI之后OS之前的所有Driver的泛称。包括EFI Driver Model Driver(协议比较相关,无论平台是Intel 还是Arch都要按照这个标准来)以及非该类型的Non-Driver Model Driver (和平台相关)。
Service Driver:如常见的BootService、RuntimeService等等服务驱动
Initializing Driver:初始化服务、CPU 芯片 设备等的初始化驱动 FchDxe / PchDxe / CpuDxe
Root Bridge Drivers:PCIE的根桥设备等 根桥驱动 (x86架构使用的是PCI Bus) PciHostBridge
EFI 1.02 Drivers: 符合EFI 1.02规范的非Non-Driver Model Driver
Bus Drivers: 总线驱动, 用于驱动总线上的设备 PciBus / UsbBus / AtaBus / SMBus /…
Device Drivers: 设备驱动, 用于驱动设备 SATA / NVME / GOP/ Keyboard / Mouse /…
Hybrid Drivers: 既有总线驱动又包括设备驱动 (不太常用)
如何定义UEFI Driver?
UEFI Driver与标准的DXE Driver是有不同的,DXE Driver主要做硬件 平台的初始化,比如对外设寄存器做一些配置,GPIO做一些修改。UEFI Driver主要是对一些PCI Device进行管理,有一个启动识别过程,确认目标是需要管理的Device后再去设备上install 相关的 protocol和service,再由上层进一步调用。对于UEFI Driver来说:
1 :管理的是PCI Device
2 :分层的,有一个总线和架构
UEFI Protocol:由GUID命名的函数和数据结构组成的接口,存储在Handle数据库中 ,是模块化的Driver之间的通讯工具。Handle类似VOID*的Point(PEI阶段的PPI没有Handle概念,DXE阶段才出现),指平台系统中的所有内容都有的一个句柄、驱动程序、设备、图像等。在这个Handle上安装一个Protocol对接一个GUID。GUID指的是UEFI平台只知道句柄数据库中的GUID,作为惟一的身份象征。
UEFI Driver加载流程:
在DXE phase加载,加载的时候会去执行UEFI的entry point,执行完以后会去做driver的初始化,install这个driver的protocol,如果是UEFI Driver 那么install的就是Driver binding Protocol和component name Protocol,这样就算是执行完了所有的Driver初始化,entry point就退出了,退出再把控制权返回给UEFI Loader。
Application是到上面的阶段就结束了,什么痕迹都不存在了,但是对于UEFI Driver来说还没有结束,它的Protocol都已经install完了,这些Protocol都已经在Handle database里面,整个UEFI系统已经开始管理了,虽然entry point已经执行完了,但是protocol还在,仍能被其他模块使用,所以Driver是一直常驻在内存中的,直到控制权由BIOS通过exitbootservice移交到OS Loader,才会被全部释放。或者在存在过程中人为将某个模块的Driver卸载掉。
Handle Database 用于Protocol的管理,假设某UEFI Driver拥有两个protocol,分别有两个不同的名字,提供不同的Service,那么在entrypoint里面就会installprotocol ,install到handle database里面去,这个Protocol还有可能depend到其他的已经install好的Protocol,并不见得这个Protocol体现的就是从头到尾完整的实现,之所以将BIOS启动系统模块化,目的就是通过模块之间的配合加载完成启动。几乎DXE阶段所有的Driver都会调用protocol。上图表示的是,UEFI Driver在加载的时候depend到handle database里面已加载好的Protocol,随后成功install后又会被其他protocol调用,最终管理Device。
UEFI Driver Binding Protocol
对于UEFI Driver 来说,一定要install Driver Binding Protocol,这是UEFI spec定义好的Protocol,里面包含三个API:Supported()、Start()、Stop()。
Supported():Check DeviceID 和Vendor ID 来辨别某个Device是否由这个Driver管理,如果是则会启动UEFI的Start(),轮询执行很多次,会影响资源占用和启动时间所以除了check并没有其他安排。
Start():由Supported()确定后被启动,在硬件的controller上install一些子的handle,然后在handle上安装一些所提供的服务(Protocol)和Device path,以供给其他人使用。
Stop():通常情况下不会去调用,因为同样花费时间和空间,但从UEFI架构考虑到在某种情况下,设备不需要再使用时,想要释放Driver,调用Stop()后会将所有install的Protocol进行uninstall,这个设备就不会再被管理同时allocate的资源也会被free。但是,Driver以及安装好的Driver Binding Protocol还在,下次仍然可以重新管理使用。