UEFI HII Database

UEFI Human Interface Infrastructure (HII) is UEFI specs defined database that stores BIOS menu items.

Dismantling the UEFI HII Database (Part 1)

On most systems, you can enter a system BIOS menu by a certain keystroke, interrupting the normal flow, and then the system presents a set of navigatable forms.

On UEFI based firmware, the mechanism through which this data is interacted with is part of the Human Interface Infrastructure (HII). Typically, this data is packed in .hpk extensions, and the database format is standardized by UEFI specs

String Representation in HII Database

If you look at the implementation of a driver that has to present configuration items in the BIOS menu, typically there is an associated .uni extension file. This ends up in a string package. Typically, a string package is a bunch of strings with a unique identifier local to the package entry. 

As we introduced package in previous sentence, let’s briefly identify what a package is: HII database is organized in the form of packages of various types. 

For an exhaustive list, it is recommended to go through Section 33.3 of UEFI Specs. However, in the Tianocore implementation, the most important types are identified in UEFIInternalFormRepresentation.h in Mde package in the form of pre-processor directives.

#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

Coming back to string packages, now we know a string package will be identified by EFI_HII_PACKAGE_STRINGS as identified in the above codeblock. For a generic package header, we take help from UEFIInternalFormRepresentation.h again:

typedef struct {
  UINT32    Length : 24;
  UINT32    Type   : 8;
  // UINT8  Data[...];
} EFI_HII_PACKAGE_HEADER;

Here Type identifies the type of package, and Length describes the total length of the package in bytes. Type here dictates how the following data is packed. Looking at UEFIInternalFormRepresentation.h, string header is defined as:

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;

As you would have noticed, the first entry of EFI_HII_STRING_PACKAGE_HDR is EFI_HII_PACKAGE_HEADER which we described earlier. In essence, based on Type field, our header is morphed into a specific header type, exposing more information related to the type of header. The last field of header describes the language of the following ‘unicode’ string block in ‘ASCII’.

Typically, a package contains a further classification of blocks of packed data; similarly, a string block is followed by a further classification of following string blocks in a string package. The strings are encoded in unicode format, i.e., 2 bytes per character. For now, we will look at the following two important string blocks:

#define EFI_HII_SIBT_STRING_UCS2        0x14
#define EFI_HII_SIBT_END                0x00

EFI_HII_SIBT_END is the easiest, as it signifies the end of the block. EFI_HII_SIBT_STRING_UCS2 block contains a string of arbitrary length encoded in unicode format. The corresponding block looks like the following:

typedef struct _EFI_HII_SIBT_STRING_UCS2_BLOCK {
  EFI_HII_STRING_BLOCK    Header;
  CHAR16                  StringText[1];
} EFI_HII_SIBT_STRING_UCS2_BLOCK;

where Header=EFI_HII_SIBT_STRING_UCS2.

If you refer back to my comment about string packages having a unique identifier, you will notice that there is no such identifier associated with the strings here in the header. This unique identifier is basically a simple counter that starts with a value of 1 in the header and increments by 1 for each such block. Coming back to .uni files, each such file basically ends up in string package and is referred to by its unique identifier.

Another package type is form packages as specified by pre-processor directives. Forms govern how the data is shown to the user; things such as navigation of the BIOS menu, options presented to the user, and the way they are presented (checkbox, dropdown, etc.) are all specified in the Form package.

Before we take a look into form package, let’s first understand the direct approach as to how a driver’s writer would introduce configurable items into the BIOS menu. Disclaimer: What follows below is just *a* single way of implementation, described here to provide an overall overview of the process.

How might a driver within the UEFI framework publish BIOS Setup Menu Options?

A driver’s writer might introduce non-volatile variables, the values of which are configurable by the user and influence the bootflow somehow. What I refer to as a variable here is referred to as a value in UEFI specs. A variable, according to the UEFI specification, is a contiguous memory block that might contain thousands of bytes. A value is at an offset within variable, made up of a couple of bytes. The values might influence the bootflow, especially how a driver initializes a certain functionality. In order it be user configurable, it needs to be presented somewhere in the forms. For that, a driver’s writer might add a Visual Forms Representation (VFR) file.

A.vfr file will contain information on how the options will be presented in front of the user (i.e., a checkbox, a question, a dropdown, help, a prompt), where they will appear (where in the navigation of the BIOS menu), and what will be influenced (value, restart needed or not, or callbacks). VFR is represented in its own syntax, which is defined here. VFR employs VFRCompile utility to generate Internal Forms Representation (IFR), which consists of the representation of the forms in binary format. It produces a file with .hpk extension, which can be parsed with several tools; one such tool is IfrViewer.

A.vfr file contains only the string IDs, associated varstores (variables), and variables (values). The strings are contained in a separate file with the extension “.uni”. The strings are then packed separately in .hpk (in the format we already touched upon above). These package entries (.hpk) (e.g form package and string package) are then packed to form a package list. We will come back to this detail, when we decompile an HII database. 

To put things together, a driver might introduce a configurable value (variable) that influences certain elements of bootflow. This value is then published in a varstore (variable), i.e., a contiguous space identified by a GUID. The corresponding options, associated with the value and to be presented in the BIOS menu, are then described in the form of VFR syntax and published through an HII protocol in the HII DB. The change in the configuration item can then invoke certain actions. The typical action, for instance, could be to reset the target so that upon the next installation of driver-related protocols (or later), the driver can utilize the value to influence user-configurable items in its logic.

Coming up Next

In Part 2 we will look into:

  1.  FORM packages. 
  2. Writing a UEFI shell application decompiling HII DB to retrieve BIOS menu options and associated strings