说明
之前的文章中已经对用户交互界面使用到的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、Images和Forms等(上述标红的四个是构成显示框体的基础部分)的数据,一个具体的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的介绍。