UEFI开发实战用户交互界面使用说明UNI文件

综述

UEFI用户交互界面的实现涉及到多种不同类型的文件,这里要讲的是UNI文件,它也是其中最简单的一种。本文主要参考自《edk-ii-uni-specification.pdf》(以下简称参考文档)。它可以在EDK II Specifications · tianocore/tianocore.github.io Wiki · GitHub下载到。文本的代码示例来自EDK2017,由于版本更新等原因,示例中的代码可能跟实际GIT库中的代码有一定的差异。

作用

关于UNI文件的作用,在参考文档中做了如下的说明:

就是说,UNI提供了一系列的标记(Token)用来表示文本,这些文本可以有不同的实现(比如可以使用不同的语言,不同的编码格式),但是在代码调用中都可以使用统一的标记来表示。以之前在【UEFI实战】UEFI用户交互界面使用说明中的图示为例:

这里的Select Language就是一个在UNI文件中定义的标记(具体在FrontPageStrings.uni文件中):

可以看到,这里实际上支持两种语言,一种是英语一种是法语,所以我们的Front Page中可以进行切换:

而在我们的代码中,实际上使用的是STR_LANGUAGE_SELECT这个标记:

  HiiCreateOneOfOpCode (
    StartOpCodeHandle,
    FRONT_PAGE_KEY_LANGUAGE,
    0,
    0,
    STRING_TOKEN (STR_LANGUAGE_SELECT),
    STRING_TOKEN (STR_LANGUAGE_SELECT_HELP),
    EFI_IFR_FLAG_CALLBACK,
    EFI_IFR_NUMERIC_SIZE_1,
    OptionsOpCodeHandle,
    NULL
    );

至于具体如何切换,这里暂不深入。

语法

关于UNI文件中的语法,在参考文档中做了比较详细的说明(也有没说明白的地方)。但是因为使用了BNF的表示方法(包括BNF的扩展方法),所以初看起来还是有一些吃力,这里做简要的介绍。关于BNF以及它的扩展版本,可以通过链接查看,或者在其它的地方找到具体的使用说明。

基本定义

首先是一些需要了解的基本定义:

UNI文件中需要指明LanguageCodes,它是表示语言的标记,比如美式英语是en-US,法语是fr-FR,中文是zh-CN等等,它们在rfc4646中定义。

UNI文件中使用的字符串可以包含EscChar,全称是Escape Character,中文翻译是转义字符,基本听到中文名懂编程的都知道是什么意思了。

UNI文件中使用的标记可以包含数组,英文大小写,下划线(_)和折线(-)。UNI文件中可以包含其它的文件,使用include表达式。还有在UNI文件中会用到的基本表达式的BNF表示:

简单来说,::=左侧的是表达式,右侧是字符串定义。比如<US>这个表达式是最基本的,它就表示一个空格,它也会在::=的右侧用到,来组成更复杂的标记;比如<MS>,它的定义是<US>+,就表示多个空格;再比如<ME>,它的定义是<MS>{<EOL>},<EOL>表示的是End Of Line,所以<ME>表达式实际上就是多个空格组成的一行。

以此类推,构成了UNI(其实也是其它一切用BNF或者它的扩展版本表示的)文件的语法基础,正是上述的基本语法阐述了一个UNI文件应该如何实现。

语法说明

UNI文件的基本语法如下:

以一个实际的例子做对比:

这里就可以很明确的看到各个部分。不过上述例子中还有一部分没有包含进去,就是

/=#

它应该属于<Content>的一部分,称为ControlRefactor,不过目前不清楚它的作用,并且不是所有的UNI文件中都包含它,另外<LanguageDefs>也不是所有的UNI文件都包含,关于这些,在参考文档中并没有特别说明原因。

<CommentLine>和<LanguageDefs>两部分都比较简单,这里主要说明下<Content>,它的定义如下:

1. 它也可以包含<CommentLine>,事实上注释可以出现在任何的位置,关于注释的表达式,在之前的基本定义中已经说明过,就是以//开头的行;

2. 空白行也随时可以出现;

3. <UnicodeLines>是以#string开头的一个字符串(中间可以换行),它算是<Content>中最重要的部分,包含了真正用来定义并在其它文件中使用的标记;

4. <ControlRefactor>在前面的红色字体中已有说明,含义不明;

5. <LanguageDefs>也可以出现在<Content>中;

6. <SecurityLines>同样含义不明;

7. <IncludeLines>也在之前提到过,就是用来包含其它UNI文件的,像下面那样:

#include "MiscBaseBoardManufacturer.uni"
#include "MiscBiosVendor.uni"
#include "MiscChassisManufacturer.uni"
#include "MiscOemString.uni"
#include "MiscPortInternalConnectorDesignator.uni"
#include "MiscSystemLanguageString.uni"
#include "MiscSystemManufacturer.uni"
#include "MiscSystemOptionString.uni"
#include "MiscSystemSlotDesignation.uni"

上述<Content>的定义中,除了两个标红的不知道作用,最重要的就是<UnicodeLines>了,它的定义如下:

下面是一个具体的例子:

对于<FontId>没有找到具体的例子。这里说明下它的定义:

然后定义的Identifier就会被用在<UnicodeLines>中。

UNI文件的使用

本文的最后介绍下UNI文件的使用。

首先在C代码中要使用UNI文件,对应模块的INF文件中需要包含该文件,以Front Page对应的UiApp.inf为例:

在模块编译的过程中,编译工具(其实是Python脚本)将它们转换成对应的Unicode字节,放到AutoGen.c文件中的某个数组中。然后在C代码中要使用的话,需要先包含该UNI,使用的函数如下:

/**
  Registers a list of packages in the HII Database and returns the HII Handle
  associated with that registration.  If an HII Handle has already been registered
  with the same PackageListGuid and DeviceHandle, then NULL is returned.  If there
  are not enough resources to perform the registration, then NULL is returned.
  If an empty list of packages is passed in, then NULL is returned.  If the size of
  the list of package is 0, then NULL is returned.
  The variable arguments are pointers which point to package header that defined
  by UEFI VFR compiler and StringGather tool.
  #pragma pack (push, 1)
  typedef struct {
    UINT32                  BinaryLength;
    EFI_HII_PACKAGE_HEADER  PackageHeader;
  } EDKII_AUTOGEN_PACKAGES_HEADER;
  #pragma pack (pop)

  @param[in]  PackageListGuid  The GUID of the package list.
  @param[in]  DeviceHandle     If not NULL, the Device Handle on which
                               an instance of DEVICE_PATH_PROTOCOL is installed.
                               This Device Handle uniquely defines the device that
                               the added packages are associated with.
  @param[in]  ...              The variable argument list that contains pointers
                               to packages terminated by a NULL.
  @retval NULL   A HII Handle has already been registered in the HII Database with
                 the same PackageListGuid and DeviceHandle.
  @retval NULL   The HII Handle could not be created.
  @retval NULL   An empty list of packages was passed in.
  @retval NULL   All packages are empty.
  @retval Other  The HII Handle associated with the newly registered package list.
**/
EFI_HII_HANDLE
EFIAPI
HiiAddPackages (
  IN CONST EFI_GUID    *PackageListGuid,
  IN       EFI_HANDLE  DeviceHandle  OPTIONAL,
  ...
  )

该函数将UNI文件提供的字符串最终放到HII数据库中,该HII数据库中还包含图像,窗体(Form),字体等内容。下面是一个具体的例子:

  //
  // Publish our HII data
  //
  gFrontPagePrivate.HiiHandle = HiiAddPackages (
                                  &mFrontPageGuid,
                                  gFrontPagePrivate.DriverHandle,
                                  FrontPageVfrBin,
                                  UiAppStrings,
                                  NULL
                                  );

这里的UiAppStrings参数对应的就是UiApp.uni,对应关系是“BASE_NAME”+Strings对应到字符数组,这里的BASE_NAME的值来自inf。同时xxxStrings也是AutoGen.c中转换后得到的数组变量名。以上述的UiAppStrings为例(这里截取部分代码):

//
//Unicode String Pack Definition
//
unsigned char UiAppStrings[] = {

// STRGATHER_OUTPUT_HEADER
  0xB2,  0x03,  0x00,  0x00,

// PACKAGE HEADER

  0x7B,  0x01,  0x00,  0x04,  0x34,  0x00,  0x00,  0x00,  0x34,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x01,  0x00,  0x65,  0x6E,
  0x2D,  0x55,  0x53,  0x00,

// PACKAGE DATA

// 0x0001: $PRINTABLE_LANGUAGE_NAME:0x0001
  0x14,  0x45,  0x00,  0x6E,  0x00,  0x67,  0x00,  0x6C,  0x00,  0x69,  0x00,  0x73,  0x00,  0x68,  0x00,  0x00,
  0x00,

在UiApp.uni中定义的第一个标记是:

在上述的字节中(PACKAGE DATA部分),第一个0x14有其它的含义(见EFI_HII_SIBT_STRING_UCS2),后面的每两个字节就表示一个Unicode字符的英文或者法文(上图是英文,法文在数组的后面,这里没有列出),可以看到0x0045表示的是Unicode的E,0x006E表示的是Unicode的n(具体的转换可以看这里),以此类推。

之后就可以通过对应的GUID使用到定义在UNI中的标记了,使用的方式是

STRING_TOKEN (标记)

这里的标记最终会被定义成一个宏,放到跟AutoGen.c同一层目录下的xxxStrDefs.h文件中,这里的xxx就是模块的名称。这个STRING_TOKEN(标记)会被当成一个宏,对应到一个整数,通过这个整数可以找到上述数组中对应Unicode字符串的位置。这个宏最终会被EFI_HII_STRING_PROTOCOL中的GetString()函数使用并解析成字符串:

/**
  This function retrieves the string specified by StringId which is associated
  with the specified PackageList in the language Language and copies it into
  the buffer specified by String.
  @param  This                   A pointer to the EFI_HII_STRING_PROTOCOL instance.
  @param  Language               Points to the language for the retrieved string.
  @param  PackageList            The package list in the HII database to search for
                                 the  specified string.
  @param  StringId               The string's id, which is unique within
                                 PackageList.
  @param  String                 Points to the new null-terminated string.
  @param  StringSize             On entry, points to the size of the buffer pointed
                                 to by  String, in bytes. On return, points to the
                                 length of the string, in bytes.
  @param  StringFontInfo         If not NULL, points to the string's font
                                 information.  It's caller's responsibility to free
                                 this buffer.
  @retval EFI_SUCCESS            The string was returned successfully.
  @retval EFI_NOT_FOUND          The string specified by StringId is not available.
                                 The specified PackageList is not in the database.
  @retval EFI_INVALID_LANGUAGE    The string specified by StringId is available but
                                  not in the specified language.
  @retval EFI_BUFFER_TOO_SMALL   The buffer specified by StringSize is too small to
                                 hold the string.
  @retval EFI_INVALID_PARAMETER   The Language or StringSize was NULL.
  @retval EFI_INVALID_PARAMETER   The value referenced by StringSize was not zero and
                                  String was NULL.
  @retval EFI_OUT_OF_RESOURCES    There were insufficient resources to complete the
                                  request.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_HII_GET_STRING)(
  IN CONST  EFI_HII_STRING_PROTOCOL *This,
  IN CONST  CHAR8                   *Language,
  IN        EFI_HII_HANDLE          PackageList,
  IN        EFI_STRING_ID           StringId,
  OUT       EFI_STRING              String,
  IN OUT    UINTN                   *StringSize,
  OUT       EFI_FONT_INFO           **StringFontInfo OPTIONAL
);

该Protocol在《UEFI Spec》中也有定义。它的初始化位于HiiDatabaseDxe.inf模块中,具体的实现不在这里深入了。

以上就是UNI文件说明的全部内容,更多关于UEFI用户交互界面UNI文件的资料请关注我们其它相关文章!

(0)

相关推荐

  • UEFI开发基础汇编代码的使用

    UEFI中使用汇编代码 EDK代码中包含一部分汇编代码,目前支持的有.S..asm和.nasm格式的汇编(第一个是AT&T汇编,后两个是Intel汇编,只是使用的汇编样式稍有不同,.nasm是开源和免费的,更加的通用),如果是在Windows下编译,一般使用的是NASM编译器,所以会使用.nasm格式的汇编文件,而编译工具也是免费的,可以在https://www.nasm.us/下载,并放到C:\Nasm目录,之后就可以在EDK代码中使用. 下面是一个示例,它是一个库模块: 首先创建inf文件:

  • UEFI开发实战用户交互界面基础说明

    目录 前言 启动 UiApp模块 字体 字符串 UI Entry 前言 本文以vUDK2017: https://github.com/tianocore/edk2.git Tag vUDK2017.中的代码为例说明UEFI用户交互界面的实现. 这里UEFI用户交互界面的实现载体是OVMF(使用QEMU启动),其形式如下: 它一般被叫做Front Page(后面将以该名称来称呼上述的界面),其下还包括Setup,Boot Manager,Device Manager等选项. 相比Legacy B

  • UEFI开发基础HII代码示例

    目录 代码示例 模块 formset form subtitle oneof string numeric text checkbox goto label 代码示例 代码 https://gitee.com/jiangwei0512/edk2-beni 模块 BeniPkg\DynamicCommand\SetupDynamicCommand\SetupDynamicCommand.inf. 这里通过一个命令setup来打开图形界面.图形界面的form在Page.vfr中,还有若干的uni文件

  • UEFI开发实战用户交互界面使用说明VFR文件

    目录 1. 综述 2. 作用 3. 语法 3.1 注释 3.2 预定义 3.3 数据结构 3.4 Forms相关表达式总览 3.4.1 formset 3.4.2 formset list 3.5 Form Set List 3.5.1 变量定义 3.5.2 控制语句 3.5.3 Question语句 3.5.4 form语句 3.6 Forms表达式 3.6.1 vfrStatementImage 3.6.2 vfrStatementLocked 3.6.3 vfrStatementRules

  • UEFI开发实战用户交互界面使用说明UNI文件

    综述 UEFI用户交互界面的实现涉及到多种不同类型的文件,这里要讲的是UNI文件,它也是其中最简单的一种.本文主要参考自<edk-ii-uni-specification.pdf>(以下简称参考文档).它可以在EDK II Specifications · tianocore/tianocore.github.io Wiki · GitHub下载到.文本的代码示例来自EDK2017,由于版本更新等原因,示例中的代码可能跟实际GIT库中的代码有一定的差异. 作用 关于UNI文件的作用,在参考文档

  • UEFI开发实战SlimBootloader中调用FSP

    目录 综述 编译 PostBuild Build PostBuild FSP二进制组成分析 使用 Stage1B Stage2 综述 FSP的全称是Firmware Support Package.FSP有以下的特性: FSP提供了Intel重要组件(包括处理器.内存控制器.芯片组等)的初始化: FSP被编译成独立的二进制,并可以集成到Bootloader中,这里说的Bootloader可以是Slim Bootloader,coreboot,UEFI等等: FSP的优点有免费.方便集成.可减少开

  • 微信公众平台开发实战Java版之微信获取用户基本信息

    在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的.对于不同公众号,同一用户的openid不同). 公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称.头像.性别.所在城市.语言和关注时间. 开发者可通过OpenID来获取用户基本信息.请使用https协议. 我们可以看看官方的文档:获取用户的基本信息. 接口调用请求说明 http请求方式: GET https://api.weixin.qq.com/cgi-b

  • JavaScript使用DeviceOne开发实战(二) 生成调试安装包

    在上篇文章给大家介绍了JavaScript使用DeviceOne开发实战(一) 配置和起步,本篇文章继续给大家介绍关于javascript实战相关内容,一起学习吧. 生成调试安装包 首先需要说明的是,这个步骤并不是每次调试App都必须的,大部分情况生成一次调试安装包,安装到手机上之后就可以忽略整个这个步骤.因为调试安装包包含了很多原生组件,都是可以定制勾选的,如果你需要额外增加一些原生组件,则需要勾选更多的组件并要重新生成调试安装包. 点击调试程序的菜单里的"Build Debug Versio

  • JavaScript使用DeviceOne开发实战(四)仿优酷视频应用

    大家没有进行开发之前首先需要考虑系统的差异性,比如说IOS手机有没有回退键,所以在开发时一定要考虑二级解密需要有回退键,否则ios的手机就会陷入到这个页面回不去. 安卓系统有回退键,针对这个情况需要要求用户在3秒钟之内连续按回退键两次才退出系统,以此防止用户误按回退键,具体代码实现如下: [mw_shl_code=javascript,true]page.on("back", function(){ if (canBack) { global.exit(); } else { nf.t

  • JavaScript使用DeviceOne开发实战(一) 配置和起步

    2015 年 9 月 底,DeviceOne Release发布.至此,DeviceOne 基本完成了对多端的支持.基于 DeviceOne 可以: HTML5.Android.iOS.Windows 多端代码一次编写,各处复用: 实时简单部署. 本地化UI 在接下来的时间,我会通过一系列文章来介绍 DeviceOne.本文介绍环境配置以及如何建立一个简单的项目.(注:本篇文章 iOS 和 Android和Windows 开发都适用.) 目前使用 DeviceOne 开发可以在Windows 或

  • C#实现微信跳一跳小游戏的自动跳跃助手开发实战

    一.前言: 前段时间微信更新了新版本后,带来的一款H5小游戏"跳一跳"在各朋友圈里又火了起来,类似以前的"打飞机"游戏,这游戏玩法简单,但加上了积分排名功能后,却成了"装逼"的地方,于是很多人花钱花时间的刷积分抢排名.后来越来越多的聪明的"程序哥们"弄出了不同方式不同花样的跳一跳助手(外挂?),有用JS实现的.有JAVA实现的.有Python实现的,有直接物理模式的.有机械化的.有量尺子的等等,简直是百花齐放啊-- 赶一下潮流

  • Python3网络爬虫开发实战之极验滑动验证码的识别

    上节我们了解了图形验证码的识别,简单的图形验证码我们可以直接利用 Tesserocr 来识别,但是近几年又出现了一些新型验证码,如滑动验证码,比较有代表性的就是极验验证码,它需要拖动拼合滑块才可以完成验证,相对图形验证码来说识别难度上升了几个等级,本节来讲解下极验验证码的识别过程. 1. 本节目标 本节我们的目标是用程序来识别并通过极验验证码的验证,其步骤有分析识别思路.识别缺口位置.生成滑块拖动路径,最后模拟实现滑块拼合通过验证. 2. 准备工作 本次我们使用的 Python 库是 Selen

随机推荐