【UEFI实战】UEFI用户交互界面使用说明之C代码实现(HII Package)

说明

之前的文章中已经对用户交互界面使用到的UNI、VFR等文件做了介绍。

本文将介绍如何在C代码中使用UNI、VFR等文件(其实是根据这些文件生成的二进制数据),并最终显示界面。

本文以EDK2017源代码为例,在vUDK2017: https://github.com/tianocore/edk2.git Tag vUDK2017.中可以下载到。

本文主要介绍在C代码中会使用到的数据结构部分内容,这部分的内容就是从UNI、VFR等文件到C语言代码的桥梁。

数据结构

首先介绍一下C代码中操作界面需要使用到的主要的数据结构。

HII Package

首先是EFI_HII_PACKAGE_HEADER

///
/// The header found at the start of each package.
///
typedef struct {
  UINT32  Length:24;
  UINT32  Type:8;
  // UINT8  Data[...];
} EFI_HII_PACKAGE_HEADER;

它是一系列的Package中最开始部分的内容,而这些Package包含的最重要的就是一个个的二进制(即上图中被注释掉的Data,而这部分Data会在具体的Package类型中表现出来),这些二进制表示的就是Strings、Fonts、ImagesForms等(上述标红的四个是构成显示框体的基础部分)的数据,一个具体的Package的样子大概是这样的:

Package有如下的类型(以代码中的宏表示):

//
// Value of HII package type
// 
#define EFI_HII_PACKAGE_TYPE_ALL             0x00
#define EFI_HII_PACKAGE_TYPE_GUID            0x01
#define EFI_HII_PACKAGE_FORMS                0x02
#define EFI_HII_PACKAGE_STRINGS              0x04
#define EFI_HII_PACKAGE_FONTS                0x05
#define EFI_HII_PACKAGE_IMAGES               0x06
#define EFI_HII_PACKAGE_SIMPLE_FONTS         0x07
#define EFI_HII_PACKAGE_DEVICE_PATH          0x08
#define EFI_HII_PACKAGE_KEYBOARD_LAYOUT      0x09
#define EFI_HII_PACKAGE_ANIMATIONS           0x0A
#define EFI_HII_PACKAGE_END                  0xDF
#define EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN    0xE0
#define EFI_HII_PACKAGE_TYPE_SYSTEM_END      0xFF

对应的说明如下(文档中少说明了Keyboard Layout Package):

下面会简单说明各个Package的用法。

除了各种类型的Package之外,还有一个需要关注的结构体EFI_HII_PACKAGE_LIST_HEADER

///
/// The header found at the start of each package list.
///
typedef struct {
  EFI_GUID               PackageListGuid;
  UINT32                 PackageLength;
} EFI_HII_PACKAGE_LIST_HEADER;

这个Header放在前面介绍的Package的开头,它有一个GUID可以标记整个Package链。

对应上面的EFI_HII_PACKAGE_LIST_HEADER,当然也有结尾的Package,这在前面的宏里面也有体现,其实现大概就是下面的样子:

//
// Template used to mark the end of a list of packages 
//
GLOBAL_REMOVE_IF_UNREFERENCED CONST EFI_HII_PACKAGE_HEADER  mEndOfPakageList = {
  sizeof (EFI_HII_PACKAGE_HEADER),
  EFI_HII_PACKAGE_END
};

在我们使用HiiAddPackages()来注册Package的时候,都会用到这里的EFI_HII_PACKAGE_LIST_HEADER和mEndOfPakageList,前者在整个Pacakge链的开头,而后者在结尾,中间就是其它的Package,而作为参数传入到HiiAddPackages()中的GUID将作为EFI_HII_PACKAGE_LIST_HEADER中的PackageListGuid成员的值来标记整个Package链。而整个链将会以为EFI_HII_PACKAGE_LIST_HEADER的PackageListGuid作为标志通过EFI_HII_DATABASE_PROTOCOL的NewPackageList加入到HII数据库中,对应的数据在内存中大概是这个样子:

这里说明的结构体都可以在UefiInternalFormRepresentation.h中找到。

Forms Package

Forms Package的Hearder如下所示:

///
/// The Form package is used to carry form-based encoding data.
///
typedef struct _EFI_HII_FORM_PACKAGE_HDR {
  EFI_HII_PACKAGE_HEADER       Header;
  // EFI_IFR_OP_HEADER         OpCodeHeader;
  // More op-codes follow
} EFI_HII_FORM_PACKAGE_HDR;

对于Forms Package的来源,就是之前文章中讨论过的VFR文件,该文件通过VfrCompiler来编译,最终得到二进制数据。

以FrontPage为例,它在编译过程中会生成一个FrontPageVfr.c(根据FrontPageVfr.vfr生成),在这个编译生成的中间文件中有一个数据FrontPageVfrBin,它就是对应的Form Package的数据(其实并不是完全一样,这里多了一个头部ARRAY LENGTH,但是在HiiAddPackages()中,这部分内容不会被计算在内),如下所示:

unsigned char FrontPageVfrBin[] = {
  // ARRAY LENGTH

  0x6F,  0x00,  0x00,  0x00,  

  // PACKAGE HEADER

  0x6B,  0x00,  0x00,  0x02,  

  // PACKAGE DATA

  0x0E,  0xA7,  0xBC,  0x30,  0x0C,  0x9E,  0x06,  0x3F,  0xA6,  0x4B,  0x82,  0x88,  0x09,  0x17,  0x9B,  0x85,  
  0x5D,  0xBE,  0x02,  0x00,  0x0C,  0x00,  0x01,  0xBC,  0x30,  0x0C,  0x9E,  0x06,  0x3F,  0xA6,  0x4B,  0x82,  
  0x88,  0x09,  0x17,  0x9B,  0x85,  0x5D,  0xBE,  0x5C,  0x06,  0x00,  0x00,  0x00,  0x00,  0x5C,  0x06,  0x00,  
  0x00,  0x01,  0x00,  0x01,  0x86,  0x00,  0x10,  0x02,  0x00,  0x5F,  0x15,  0x35,  0x17,  0x0B,  0x0F,  0xA0,  
  0x87,  0x93,  0x41,  0xB2,  0x66,  0x53,  0x8C,  0x38,  0xAF,  0x48,  0xCE,  0x00,  0x00,  0x10,  0x5F,  0x15,  
  0x35,  0x17,  0x0B,  0x0F,  0xA0,  0x87,  0x93,  0x41,  0xB2,  0x66,  0x53,  0x8C,  0x38,  0xAF,  0x48,  0xCE,  
  0x00,  0xFF,  0xFF,  0x29,  0x02,  0x29,  0x02

};

这个数据就在初始化FrontPage的时候被注册到HII数据库中,位置是在InitializeFrontPage()函数中:

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

这样,一个表示Forms的数据就被注册到了HII数据库中,可以在后续使用它。

Strings Package

Strings Package的Header如下所示:

///
/// The fixed header consists of a standard record header and then the string identifiers
/// contained in this section and the offsets of the string and language information.
///
typedef struct _EFI_HII_STRING_PACKAGE_HDR {
  EFI_HII_PACKAGE_HEADER  Header;
  UINT32                  HdrSize;
  UINT32                  StringInfoOffset;
  CHAR16                  LanguageWindow[16];
  EFI_STRING_ID           LanguageName;
  CHAR8                   Language[1];
} EFI_HII_STRING_PACKAGE_HDR;

关于Strings,在之前介绍UNI的时候有说过,在编译时生成的AutoGen.c(还是以FrongPage.c为例)中会生成一个数据:

//
//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,

这个数据就是Strings Package(其实并不是完全一样,这里多了一个头部SIRGATHER_OUTPUT_HEADER,但是在HiiAddPackages()中,这部分内容不会被计算在内)。

最终这个Strings Package也会注册到HII数据库中:

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

Fonts Package

Fonts Package有两种类型,分别是Simple Fonts和Fonts,其中Simple Fonts Package的结构体的Header如下所示:

///
/// A simplified font package consists of a font header
/// followed by a series of glyph structures.
///
typedef struct _EFI_HII_SIMPLE_FONT_PACKAGE_HDR {
  EFI_HII_PACKAGE_HEADER Header;
  UINT16                 NumberOfNarrowGlyphs;
  UINT16                 NumberOfWideGlyphs;
  // EFI_NARROW_GLYPH       NarrowGlyphs[];
  // EFI_WIDE_GLYPH         WideGlyphs[];
} EFI_HII_SIMPLE_FONT_PACKAGE_HDR;

我们可以看到它包含了一个EFI_HII_PACKAGE_HEADER,之后接的数据构成了字体需要的所有元素。

关于Glyph的说明,它有两种类型,一种对应宽字符,一种对应窄字符,这里使用了长度和具体实现(具体实现也是一大堆的二进制数据)两个成员来分别表示宽字符和窄字符,至于它们的具体含义,这里不做说明。但是关于字体的完整Package的一个实例结构体可以在这里列出来,如下所示:

typedef struct {
  ///
  /// This 4-bytes total array length is required by HiiAddPackages()
  ///
  UINT32                 Length;

  //
  // This is the Font package definition
  //
  EFI_HII_PACKAGE_HEADER Header;
  UINT16                 NumberOfNarrowGlyphs;
  UINT16                 NumberOfWideGlyphs;
  EFI_NARROW_GLYPH       NarrowArray[NARROW_GLYPH_NUMBER];
  EFI_WIDE_GLYPH         WideArray[WIDE_GLYPH_NUMBER];
} FONT_PACK_BIN;

它在UiApp.inf中有使用到。具体的数据如下(未显示全部):

FONT_PACK_BIN mFontBin = {
  sizeof (FONT_PACK_BIN),
  {
    sizeof (FONT_PACK_BIN) - sizeof (UINT32),
    EFI_HII_PACKAGE_SIMPLE_FONTS,
  },
  NARROW_GLYPH_NUMBER,
  0,
  {     // Narrow Glyphs
    {
      0x05d0,
      0x00,
      {
        0x00,

最终通过HiiAddPackages(),上述的数据也被注册到了HII数据库中:

/**
  Routine to export glyphs to the HII database.
  This is in addition to whatever is defined in the Graphics Console driver.

**/
EFI_HII_HANDLE
ExportFonts (
  VOID
  )
{
  return HiiAddPackages (
           &mFontPackageGuid,
           gImageHandle,
           &mFontBin,
           NULL
           );
}

另外一种Fonts(不带Simple)的Package的Header如下所示:

///
/// The fixed header consists of a standard record header,
/// then the character values in this section, the flags
/// (including the encoding method) and the offsets of the glyph
/// information, the glyph bitmaps and the character map.
///
typedef struct _EFI_HII_FONT_PACKAGE_HDR {
  EFI_HII_PACKAGE_HEADER Header;
  UINT32                 HdrSize;
  UINT32                 GlyphBlockOffset;
  EFI_HII_GLYPH_INFO     Cell;
  EFI_HII_FONT_STYLE     FontStyle;
  CHAR16                 FontFamily[1];
} EFI_HII_FONT_PACKAGE_HDR;

对这种Fonts Package不做深入说明。

Images Package

Images Package的Header如下所示:

typedef struct _EFI_HII_IMAGE_PACKAGE_HDR {
  EFI_HII_PACKAGE_HEADER  Header;
  UINT32                  ImageInfoOffset;
  UINT32                  PaletteInfoOffset;
} EFI_HII_IMAGE_PACKAGE_HDR;

关于这种Package,由于目前还没有在EDK代码中找到使用位置,目前不清楚用法。

Device Path Package

Device Path Package的Header如下所示:

///
/// The device path package is used to carry a device path
/// associated with the package list.
///
typedef struct _EFI_HII_DEVICE_PATH_PACKAGE_HDR {
  EFI_HII_PACKAGE_HEADER   Header;
  // EFI_DEVICE_PATH_PROTOCOL DevicePath[];
} EFI_HII_DEVICE_PATH_PACKAGE_HDR;

关于这种Package,由于目前还没有在EDK代码中找到使用位置,目前不清楚用法。

GUID Package

GUID Package的Header如下所示:

///
/// The GUID package is used to carry data where the format is defined by a GUID.
///
typedef struct _EFI_HII_GUID_PACKAGE_HDR {
  EFI_HII_PACKAGE_HEADER  Header;
  EFI_GUID                Guid;
  // Data per GUID definition may follow
} EFI_HII_GUID_PACKAGE_HDR;

关于这种Package,由于目前还没有在EDK代码中找到使用位置,目前不清楚用法。

Keyboard Layout Package

Keyboard Layout Package的Header如下所示:

typedef struct {
  EFI_HII_PACKAGE_HEADER  Header;
  UINT16                  LayoutCount;
  // EFI_HII_KEYBOARD_LAYOUT Layout[];
} EFI_HII_KEYBOARD_PACKAGE_HDR;

该种类型的Package是为了使键盘按键与实际的输入能够对应起来,以USB键盘为例,它对应如下的结构体:

typedef struct {
  //
  // This 4-bytes total array length is required by PreparePackageList()
  //
  UINT32                 Length;

  //
  // Keyboard Layout package definition
  //
  EFI_HII_PACKAGE_HEADER PackageHeader;
  UINT16                 LayoutCount;

  //
  // EFI_HII_KEYBOARD_LAYOUT
  //
  UINT16                 LayoutLength;
  EFI_GUID               Guid;
  UINT32                 LayoutDescriptorStringOffset;
  UINT8                  DescriptorCount;
  EFI_KEY_DESCRIPTOR     KeyDescriptor[USB_KEYBOARD_KEY_COUNT];
  UINT16                 DescriptionCount;
  CHAR16                 Language[USB_KEYBOARD_LANGUAGE_STR_LEN];
  CHAR16                 Space;
  CHAR16                 DescriptionString[USB_KEYBOARD_DESCRIPTION_STR_LEN];
} USB_KEYBOARD_LAYOUT_PACK_BIN;

具体的实例可以在Keyboard.c文件中找到:

USB_KEYBOARD_LAYOUT_PACK_BIN  mUsbKeyboardLayoutBin = {
  sizeof (USB_KEYBOARD_LAYOUT_PACK_BIN),   // Binary size

  //
  // EFI_HII_PACKAGE_HEADER
  //
  {
    sizeof (USB_KEYBOARD_LAYOUT_PACK_BIN) - sizeof (UINT32),
    EFI_HII_PACKAGE_KEYBOARD_LAYOUT
  },
  1,  // LayoutCount
  sizeof (USB_KEYBOARD_LAYOUT_PACK_BIN) - sizeof (UINT32) - sizeof (EFI_HII_PACKAGE_HEADER) - sizeof (UINT16), // LayoutLength
  USB_KEYBOARD_LAYOUT_KEY_GUID,  // KeyGuid
  sizeof (UINT16) + sizeof (EFI_GUID) + sizeof (UINT32) + sizeof (UINT8) + (USB_KEYBOARD_KEY_COUNT * sizeof (EFI_KEY_DESCRIPTOR)), // LayoutDescriptorStringOffset
  USB_KEYBOARD_KEY_COUNT, // DescriptorCount
  {
    //
    // EFI_KEY_DESCRIPTOR (total number is USB_KEYBOARD_KEY_COUNT)
    //
    {EfiKeyC1,         'a',      'A',  0, 0,  EFI_NULL_MODIFIER,   EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK},
    {EfiKeyB5,         'b',      'B',  0, 0,  EFI_NULL_MODIFIER,   EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK},
    {EfiKeyB3,         'c',      'C',  0, 0,  EFI_NULL_MODIFIER,   EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK},
    {EfiKeyC3,         'd',      'D',  0, 0,  EFI_NULL_MODIFIER,   EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK},
    {EfiKeyD3,         'e',      'E',  0, 0,  EFI_NULL_MODIFIER,   EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK},
    {EfiKeyC4,         'f',      'F',  0, 0,  EFI_NULL_MODIFIER,   EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK},

最终该结构体也注册到了HII数据库中:

  //
  // Install Keyboard Layout package to HII database
  //
  HiiHandle = HiiAddPackages (
                &gUsbKeyboardLayoutPackageGuid,
                UsbKeyboardDevice->ControllerHandle,
                &mUsbKeyboardLayoutBin,
                NULL
                );

Animations Package

Animations Package的Header如下所示:

///
/// HII animation package header.
///
typedef struct _EFI_HII_ANIMATION_PACKAGE_HDR {
  ///
  /// Standard package header, where Header.Type = EFI_HII_PACKAGE_ANIMATIONS.
  ///
  EFI_HII_PACKAGE_HEADER  Header;
  ///
  /// Offset, relative to this header, of the animation information. If 
  /// this is zero, then there are no animation sequences in the package.
  ///
  UINT32                  AnimationInfoOffset;
} EFI_HII_ANIMATION_PACKAGE_HDR;

关于这种Package,由于目前还没有在EDK代码中找到使用位置,目前不清楚用法。

以上是关于HII Package的介绍。

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值