Linux驱动bootloader之UEFI系统二——再看UEFI


一、再识UEFI

之前把UEFI 简单的理解为是一种Bootloader,其功能与LK类似,就是引导加载内核镜像程序,将整个系统launch起来,随着与UEFI 更多的接触,以及在网上查找资料,UEFI是由BIOS 演变而来,将会替代掉BIOS近 二十年的统治地位。

UEFI: 可扩展固件接口(Extensible FirmwareInterface,EFI)是 Intel 为 PC 固件的体系结构、接口和服务提出的建议标准。其主要目的是为了提供一组在 OS 加载之前(启动前)在所有平台上一致的、正确指定的启动服务,被看做是有近20多年历史的 BIOS 的继任者。并且security有所加强

UEFI相比BIOS而言,其优势在于:简单易读,可移植性高,并由高安全性。

BIOS是汇编实现的,采用的是16bit 实模式寻址方式,最大支持的内存只有1M,代码易读性以及实现的功能都受到限制,而且移植起来不方便。BIOS支持的最大磁盘空间不超过2TB。

UEFI克服了上述的所有缺点,采用C语言实现,分层,模块化设计,实现了CPU以及驱动的无关性。UEFI可以理解为一个完整的系统,包含了上电时序,驱动实现,os环境建立(这个os可以理解为UEFI运行独有的os,非linux,windows),应用程序。其中应用程序支持网络配置,类shell环境,fastboot,linux loader等。


二、Linux加载入口函数LinuxLoaderEntry()

下面来看bootloader 的程序,拿到代码之后,面对成千上万行的代码,该从何看起呢?

是main函数?还是打印log 之后,查找log中出现的关键字?

以上都是手段。

首先直接上来撸代码是很懵懂的,会接触到很多专业术语以及相关的知识应用,这些也是需要慢慢了解和积累的。否则怎么可能别人学不会,你就能学会呢??!!

其一、看搜索main函数,为什么可查看如下链接的文章:

https://blog.csdn.net/clam_zxf/article/details/79772508

其二、分析串口log——最终走的方法,比较简单明了。

截取我的部分串口log如下==>最终定位到函数LinuxLoaderEntry

UEFI Total : 763 ms
POST Time      [ 2106] OS Loader
Loader Build Info: Mar 26 2018 09:02:46
VB: RWDeviceState: Succeed using devinfo!
KeyPressed = (0x0)
XblBootMode = 0
Error locating the reset reason protocol
Reboot reason: 0
Recovery command: 32 ffbm-02
E7S Platform Version : 0x10000
LoaderBuild Info: Mar 26 2018 09:02:46

__DATE__ = Mar 26 2018__TIME__ = 09:02:46

Loader被编译的时间信息是2018年的3月26号 9点02分

EFI_STATUS EFIAPI LinuxLoaderEntry(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)

{

         EFI_STATUSStatus;

         UINT32BootReason = NORMAL_MODE;

         UINT32KeyPressed;

         /*MultiSlot Boot */

         BOOLEANMultiSlotBoot;

         DEBUG((EFI_D_INFO, "LoaderBuild Info: %a %a\n", __DATE__, __TIME__));

下面整理大致的流程图:


在此处就有一个按键读取的程序KeyPress()


用sourceInsight去搜索函数ReadKeyStrokeEx();

///
/// The EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL is used on the ConsoleIn
/// device. It is an extension to the Simple Text Input protocol
/// which allows a variety of extended shift state information to be
/// returned.
///
struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL{
  EFI_INPUT_RESET_EX              Reset;
  EFI_INPUT_READ_KEY_EX           ReadKeyStrokeEx;//读取输入设备按键
  ///
  /// Event to use with WaitForEvent() to wait for a key to be available.
  ///
  EFI_EVENT                       WaitForKeyEx;
  EFI_SET_STATE                   SetState;
  EFI_REGISTER_KEYSTROKE_NOTIFY   RegisterKeyNotify;
  EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify;
};
最终找到协议接口
EFI_INPUT_READ_KEY_EX
EFI_STATUS
(EFIAPI *EFI_INPUT_READ_KEY_EX) (
  IN  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
  OUT EFI_KEY_DATA                      *KeyData
  )

很明显,使用sourceInsight翻了遍也没有找到协议的EFI_INPUT_READ_KEY_EX()实现的代码,

程序中是直接调用了UEFI 的API接口去读取输入设备。具体的API实现暂时未找到,但是找到了UEFI spec

由UEFI 的spec说明书中找到了对应的说明:

EFI_SIMPLE_TEXT_INPUT_PROTOCOL: ReadKeyStroke() /ReadKeyStrokeEx()
     非阻塞的输入方式,执行完后立即返回,不会等待键盘的输入。输入的数据需要进行转换成EFI_INPUT_KEY 、EFI_KEY_DATA
附上UEFI Spec的下载链接:

https://download.csdn.net/download/clam_zxf/10327908点击打开链接

具体UEFI 中该API接口的实现,后续继续分析。

Simple Text Input Protocol:
     如下是数据结构的定义,里面我们需要特别注意到的一个是EFI_EVENT WatiForKey,在SimpleTextInput.c中有参考的实例。

EFI_SIMPLE_TEXT_INPUT_PROTOCOL:Reset()   
    reset 输入设备,比如说键盘

EFI_SIMPLE_TEXT_INPUT_PROTOCOL: ReadKeyStroke() /ReadKeyStrokeEx()
     非阻塞的输入方式,执行完后立即返回,不会等待键盘的输入。输入的数据需要进行转换成EFI_INPUT_KEY 、EFI_KEY_DATA

EFI_SIMPLE_TEXT_INPUT_PROTOCOL: WaitForKey/ WaitForKeyEx:
    一旦EVENT被创建,他们必须要与EVENT notification function链接起来,一旦输入设备的缓冲中有我们需要的数据的时候,那么这两        种EVENT就会被UEFI BS服务 SignalEvent()设置为signaled状态。

EFI_SIMPLE_TEXT_INPUT_PROTOCOL: SetState()
    设置输入设备的状态,比如键盘的大小写指示灯,数字键盘锁定灯等等。

EFI_SIMPLE_TEXT_INPUT_PROTOCOL: RegisterKeyNotify()
     用来注册一个notification 函数,当按键被按下的时候,执行该函数。

EFI_SIMPLE_TEXT_INPUT_PROTOCOL: UnregisterKeyNotify()
    执行RegisterKeyNotify()相反的动作。

本文参考了部分UEFI大神的文章:https://blog.csdn.net/CStyle_0x007/article/details/8893038

猜你喜欢

转载自blog.csdn.net/clam_zxf/article/details/79819139
今日推荐