UEFI開發(fā)實戰(zhàn)用戶交互界面基礎說明
前言
本文以vUDK2017: https://github.com/tianocore/edk2.git Tag vUDK2017.中的代碼為例說明UEFI用戶交互界面的實現(xiàn)。
這里UEFI用戶交互界面的實現(xiàn)載體是OVMF(使用QEMU啟動),其形式如下:

它一般被叫做Front Page(后面將以該名稱來稱呼上述的界面),其下還包括Setup,Boot Manager,Device Manager等選項。
相比Legacy BIOS,UEFI的交互界面要豐富得多,比如支持多語言,支持圖片等,不過EDK默認帶的還是最原始的,跟Legacy BIOS類似的界面。
本文討論的就是該界面的實現(xiàn)。
啟動
在EDK2017的OVMF代碼中,F(xiàn)ront Page被做成一個獨立的APP(跟Shell一樣),然后注冊,可以通過在啟動過程中按F2來進入,具體的注冊代碼如下:
VOID
PlatformRegisterOptionsAndKeys (
VOID
)
{
EFI_STATUS Status;
EFI_INPUT_KEY Enter;
EFI_INPUT_KEY F2;
EFI_INPUT_KEY Esc;
EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
//
// Register ENTER as CONTINUE key
//
Enter.ScanCode = SCAN_NULL;
Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
ASSERT_EFI_ERROR (Status);
//
// Map F2 to Boot Manager Menu
//
F2.ScanCode = SCAN_F2;
F2.UnicodeChar = CHAR_NULL;
Esc.ScanCode = SCAN_ESC;
Esc.UnicodeChar = CHAR_NULL;
Status = EfiBootManagerGetBootManagerMenu (&BootOption);
ASSERT_EFI_ERROR (Status);
Status = EfiBootManagerAddKeyOptionVariable (
NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL
);
ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
Status = EfiBootManagerAddKeyOptionVariable (
NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL
);
ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
}而Front Page對應APP的驅(qū)動是UiApp.inf,它對應的GUID是:
# Point to the MdeModulePkg/Application/UiApp/UiApp.inf
gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile|{ 0x21, 0xaa, 0x2c, 0x46, 0x14, 0x76, 0x03, 0x45, 0x83, 0x6e, 0x8a, 0xb6, 0xf4, 0x66, 0x23, 0x31 }在EfiBootManagerGetBootManagerMenu()函數(shù)中會根據(jù)上述的GUID尋找UiApp模塊,并生成對應的啟動項。
最終的結(jié)果就是啟動過程中按F2就可以進入UiApp模塊,其入口是InitializeUserInterface(),將在后續(xù)的內(nèi)容中介紹。
UiApp模塊
InitializeUserInterface()模塊的大致流程如下:

其中綠色部分涉及到交互相關的操作,后續(xù)會重點說明。
字體
字體使用一種稱為Glyph的元素表示,它其實就是一個二進制的文件,里面包含了描述字體的元素,但是具體是怎么樣表示的,目前還不是很清楚,這個也不是我們需要關注的重點。
這個二進制在代碼中有下述的數(shù)組表示:
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;
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, // 后面的省略這個數(shù)組通過一個通過HiiAddPackages()導入,如下所示:
/**
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
);
}字符串
字符串通過UNI文件轉(zhuǎn)換成,編譯時在AutoGen.c中生成對應的數(shù)組,然后通過下面的函數(shù)來注冊到HII數(shù)據(jù)庫中:
/**
Initialize HII global accessor for string support.
**/
VOID
InitializeStringSupport (
VOID
)
{
gStringPackHandle = HiiAddPackages (
&mUiStringPackGuid,
gImageHandle,
UiAppStrings,
NULL
);
ASSERT (gStringPackHandle != NULL);
}這里的UiAppStrings就是通過.uni文件生成的字符串表示。
可以看到,導入字體和字符串使用的是相同的函數(shù)。
UI Entry
進入UI界面是通過UiEntry()來實現(xiàn)的,其大致流程如下:

這里的重點也主要在綠色部分,它包含了Front Page的初始化和調(diào)用。
上述的綠色部分大致流程如下所示:

這里最重要的是兩個部分,一個是更新Front Page的部分,另一個是SendForm()的部分。
更新Front Page部分主要由UpdateFrontPageBannerStrings()、UpdateFrontPageForm()等函數(shù)組成,它們使用了各類HII操作來更新界面,比如說UiCustomizeFrontPageBanner()構(gòu)成了Front Page界面中的一條條的字符串顯示(就是開頭圖片中的藍字部分),另外還有UiCustomizeFrontPage()、HiiUpdateForm()等函數(shù),都更新了界面。
SendForm()部分,它其實是整個UEFI界面顯示的引擎,這部分實現(xiàn)在顯示界面(比如圖形輸出界面,或者串口)上顯示前面更新的內(nèi)容,后續(xù)會詳細介紹。
本文只是簡單的介紹,以上就是UEFI開發(fā)實戰(zhàn)用戶交互界面基礎說明的詳細內(nèi)容,更多關于UEFI用戶交互界面的資料請關注腳本之家其它相關文章!

