UEFI实战HII之FrontPage

Posted jiangwei0512

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UEFI实战HII之FrontPage相关的知识,希望对你有一定的参考价值。

写在前面

UEFI有自己的用户交互界面,它的实现基础被称为HII(Human Interface Infrastructure),本文是一系列介绍HII实现的第一篇。这里从开源EDK代码中的界面(称为Front Page)入手,介绍它的实现,并进一步说明整个HII。

之前已经写过一系列跟Setup相关的文档,内容有重复及补充。

入口说明

Front Page的实现代码可以在https://gitee.com/jiangwei0512/edk2-beni)找到,编译执行之后的结果大致如下:

它对应的是一个启动项程序UiApp,模块代码是MdeModulePkg\\Application\\UiApp\\UiApp.inf,该模块的入口函数是InitializeUserInterface()(位于MdeModulePkg\\Application\\UiApp\\FrontPage.c)。

初始化的大致流程如下:

进入UI 退出UI之后的清理 初始化图形模式参数 关闭软件看门狗 清屏 安装字体 初始化HII字符串 设置终端模式,参数就来自第一步 进入UI入口 UI入口 退出Front Page之后的清理 UI入口 如果有必要就初始化启动设备 刷新启动项 初始化Front Page 显示Front Page

上述流程中最主要的是两个部分,”初始化Front Page“和”显示Front Page“,对应到两个函数InitializeFrontPage()CallFrontPage(),它们可以连起来看,主要做的事情就是两个:一个是准备素材,这里的素材指的是uni文件、vfr文件等表示的HII数据;另一个是显示这些素材,它通过一个EFI_FORM_BROWSER2_PROTOCOL来完成,这个Protocol提供两个接口,SendForm()用来显示配置好的HII,BrowserCallback()会被回调函数调用,用来获取和设置界面元素。

界面元素

在HII中构成一个界面的元素大致有四种,分别是字符串(Strings)、结构(Forms)、字体(Fonts)和图像(Images),如下图所示(最右边的Questions等是结构的组成部分,这里可以暂时不管,后续会介绍):

字符串通过uni文件产生,结构通过vfr文件产生,字体暂不介绍,而图像则没有特别好介绍的。本例的Front Page中使用了结构、字符串和字体,图像则没有使用到。

元素更新

元素更新主要发生在InitializeFrontPage()函数中,对应的代码:

  //
  //Updata Front Page banner strings
  //
  UpdateFrontPageBannerStrings ();

  //
  // Update front page menus.
  //
  UpdateFrontPageForm();
  • 前者UpdateFrontPageBannerStrings()主要是获取标记①(Token)对应的字符串的值并设置到对应的标记②中,它初始化Front Page的上半区静态部分:

以代码为例的话就像下面的那样:

    NewString = HiiGetString (gFrontPagePrivate.HiiHandle, STRING_TOKEN (STR_FRONT_PAGE_COMPUTER_MODEL), NULL);
    UiCustomizeFrontPageBanner (1, TRUE, &NewString);
    HiiSetString (gFrontPagePrivate.HiiHandle, STRING_TOKEN (STR_FRONT_PAGE_COMPUTER_MODEL), NewString, NULL);
    FreePool (NewString);

这里的标记STR_FRONT_PAGE_COMPUTER_MODEL出现了两次,虽然名字相同,但是是来自不同的文件,分别是uni文件中的:

#string STR_FRONT_PAGE_COMPUTER_MODEL  #language en-US  ""
                                       #language fr-FR  ""

和来自vfr文件中的:

    banner
      title = STRING_TOKEN(STR_FRONT_PAGE_COMPUTER_MODEL),
      line  1,
      align left;

标记①的操作通过HiiGetString()获取字符串,标记②的操作通过HiiSetString()设置字符串。不过两者本来就需要是一致的,所以也不需要特意去区分。

  • 后者UpdateFrontPageForm()更新其它动态的部分,而动态的部分通过vfr文件查看可以看到它们位于两个操作码之间:
    label LABEL_FRANTPAGE_INFORMATION;
    //
    // This is where we will dynamically add a Action type op-code to show
    // the platform information.
    //
    label LABEL_END;

对应的代码:

VOID
UpdateFrontPageForm (
  VOID
  )

  VOID                        *StartOpCodeHandle;
  VOID                        *EndOpCodeHandle;
  EFI_IFR_GUID_LABEL          *StartGuidLabel;
  EFI_IFR_GUID_LABEL          *EndGuidLabel;

  //
  // Allocate space for creation of UpdateData Buffer
  //
  StartOpCodeHandle = HiiAllocateOpCodeHandle ();
  ASSERT (StartOpCodeHandle != NULL);

  EndOpCodeHandle = HiiAllocateOpCodeHandle ();
  ASSERT (EndOpCodeHandle != NULL);
  //
  // Create Hii Extend Label OpCode as the start opcode
  //
  StartGuidLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
  StartGuidLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
  StartGuidLabel->Number       = LABEL_FRANTPAGE_INFORMATION;  // 对应vfr中的LABEL_FRANTPAGE_INFORMATION
  //
  // Create Hii Extend Label OpCode as the end opcode
  //
  EndGuidLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
  EndGuidLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
  EndGuidLabel->Number       = LABEL_END;	// 对应vfr中的LABEL_END

  //
  //Updata Front Page form
  //
  UiCustomizeFrontPage (
    gFrontPagePrivate.HiiHandle,
    StartOpCodeHandle
    );

  HiiUpdateForm (
    gFrontPagePrivate.HiiHandle,
    &mFrontPageGuid,
    FRONT_PAGE_FORM_ID,
    StartOpCodeHandle,
    EndOpCodeHandle
    );

  HiiFreeOpCodeHandle (StartOpCodeHandle);
  HiiFreeOpCodeHandle (EndOpCodeHandle);

这里的两个部分(StartOpCodeHandleEndOpCodeHandle)组合构成了Front Page剩余的部分,其过程如下:

  1. 通过函数HiiAllocateOpCodeHandle()创建两个OpCodeHandle,它们对应的是同一个结构体:
typedef struct 
  UINT8  *Buffer;
  UINTN  BufferSize;
  UINTN  Position;
 HII_LIB_OPCODE_BUFFER;

创建好之后的结构体中Buffer是一个大小为0x200字节的空间;BufferSize就是0x200;Position初始化为0,它相当于存放其它操作码的容器。

  1. 创建两个OpCode,会使用到前面创建的OpCodeHandle:
StartGuidLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
StartGuidLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
StartGuidLabel->Number       = LABEL_FRANTPAGE_INFORMATION;

HiiCreateGuidOpCode()的第一个参数就是OpCodeHandle;第二个参数是一个GUID;第三个参数是可选的,可以是NULL;第四个参数是组件元素结构体的大小。本例创建了两个EFI_IFR_GUID_LABEL结构体(跟vfr文件中的label对应),它也是HiiCreateGuidOpCode()的返回值,其结构如下:

///
/// Label opcode.
///
typedef struct _EFI_IFR_GUID_LABEL 
  EFI_IFR_OP_HEADER   Header;
  ///
  /// EFI_IFR_TIANO_GUID.
  ///
  EFI_GUID            Guid;
  ///
  /// EFI_IFR_EXTEND_OP_LABEL.
  ///
  UINT8               ExtendOpCode;
  ///
  /// Label Number.
  ///
  UINT16              Number;
 EFI_IFR_GUID_LABEL;

后面的两句代码用来初始化EFI_IFR_GUID_LABEL结构体的后两个参数,其中Number就对应到vfr文件中的label名,本例中分别是LABEL_FRANTPAGE_INFORMATIONLABEL_END

  1. OpCode的自定义,其实就是在StartOpCodeHandleEndOpCodeHandle之间创建自定义的元素。
  2. 更新Front Page结构,更新的部分就是前面代码中增加的自定义元素。
  3. 释放资源。

这里最重要的是第3步,对应如下的函数:

VOID
UiCustomizeFrontPage (
  IN EFI_HII_HANDLE  HiiHandle,
  IN VOID            *StartOpCodeHandle
  )

  //
  // Create "Select Language" menu with Oneof opcode.
  //
  UiCreateLanguageMenu (HiiHandle, StartOpCodeHandle);

  //
  // Create empty line.
  //
  UiCreateEmptyLine(HiiHandle, StartOpCodeHandle);

  //
  // Find third party drivers which need to be shown in the front page.
  //
  UiListThirdPartyDrivers (HiiHandle, &gEfiIfrFrontPageGuid, NULL, StartOpCodeHandle);

  //
  // Create empty line.
  //
  UiCreateEmptyLine(HiiHandle, StartOpCodeHandle);

  //
  // Create "Continue" menu.
  //
  UiCreateContinueMenu(HiiHandle, StartOpCodeHandle);

  //
  // Create reset menu.
  //
  UiCreateResetMenu(HiiHandle, StartOpCodeHandle);

基本上图中显示的部分都有对应的函数,但是底部黑色部分并没有,它们是根据特定情况而产生的,比如<Enter>= Select Entry是因为UiCreateLanguageMenu()而显示的,而↑↓=Move Highlight只要有创建上述的元素就会有显示。动作显示的组件有一个包含的关系,如下图所示:

以上是关于UEFI实战HII之FrontPage的主要内容,如果未能解决你的问题,请参考以下文章

UEFI实战HII之配置

UEFI实战HII之常用函数

UEFI实战HII之常用函数

UEFI实战HII之HII数据库和Form浏览器

UEFI实战HII之涉及模块

UEFI实战HII之涉及模块