模块
HII的交互逻辑如下图所示:
其中上边是输入设备,比如键盘、鼠标等;右边是输出设备,比如显示器;左边是UEFI变量,用来存放HII会涉及到的数据。这三个部分都属于UEFI基础,不是这里介绍的对象,这里主要说明中间紫色部分的IFR Browser和HII Database,它们也是HII需要关注的重要模块,对应到代码中主要有如下的几个模块:
MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
除此之外,其它的模块(就是上图绿色部分Driver)也会提供用户交互界面需要的元素,这通常是为了完成该模块需要的操作。
在上面的三个模块中,HiiDatabaseDxe.inf
模块用来初始化并安装操作界面元素的Protocol以及配置相关的Protocol,这些界面元素包括字体,字符串,结构、图像等。SetupBrowserDxe.inf
这个模块依赖于HiiDatabaseDxe.inf
,它提供了上图中间部分的实现,实际上是整个用户交互界面的引擎,用来实现各类必要的操作(实际上也不是它来实现,它只是调用了在绿色部分Driver中的操作)。DisplayEngineDxe.inf
是配合SetupBrowserDxe.inf
一起使用的,它依赖于后者。
模块安装的Protocol有如下一些:
HiiDatabaseDxe.inf
本模块初始化了HII_DATABASE_PRIVATE_DATA
(对应变量mPrivate
):
InitializeListHead (&mPrivate.DatabaseList);
InitializeListHead (&mPrivate.DatabaseNotifyList);
InitializeListHead (&mPrivate.HiiHandleList);
InitializeListHead (&mPrivate.FontInfoList);
然后安装了一堆Protocol:
Status = gBS->InstallMultipleProtocolInterfaces (
&Handle,
&gEfiHiiFontProtocolGuid,
&mPrivate.HiiFont,
&gEfiHiiStringProtocolGuid,
&mPrivate.HiiString,
&gEfiHiiDatabaseProtocolGuid,
&mPrivate.HiiDatabase,
&gEfiHiiConfigRoutingProtocolGuid,
&mPrivate.ConfigRouting,
&gEfiConfigKeywordHandlerProtocolGuid,
&mPrivate.ConfigKeywordHandler,
NULL
);
还有一部分是可选的:
if (FeaturePcdGet (PcdSupportHiiImageProtocol)) {
Status = gBS->InstallMultipleProtocolInterfaces (
&Handle,
&gEfiHiiImageProtocolGuid, &mPrivate.HiiImage,
&gEfiHiiImageExProtocolGuid, &mPrivate.HiiImageEx,
NULL
);
}
关于各个安装的Protocol,后续会介绍。这些Protocol都安装到了前面提到的HII_DATABASE_PRIVATE_DATA
结构体:
typedef struct _HII_DATABASE_PRIVATE_DATA {
UINTN Signature;
LIST_ENTRY DatabaseList;
LIST_ENTRY DatabaseNotifyList;
EFI_HII_FONT_PROTOCOL HiiFont;
EFI_HII_IMAGE_PROTOCOL HiiImage;
EFI_HII_IMAGE_EX_PROTOCOL HiiImageEx;
EFI_HII_STRING_PROTOCOL HiiString;
EFI_HII_DATABASE_PROTOCOL HiiDatabase;
EFI_HII_CONFIG_ROUTING_PROTOCOL ConfigRouting;
EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL ConfigKeywordHandler;
LIST_ENTRY HiiHandleList;
INTN HiiHandleCount;
LIST_ENTRY FontInfoList; // global font info list
UINTN Attribute; // default system color
EFI_GUID CurrentLayoutGuid;
EFI_HII_KEYBOARD_LAYOUT *CurrentLayout;
} HII_DATABASE_PRIVATE_DATA;
这些Protocol就是用来操作UEFI交互界面中的字体、字符串、图像等元素的。
SetupBrowserDxe.inf
该模块依赖于在HiiDatabaseDxe.inf
中安装的某些Protocol,所以现有LocateProtocol()
操作:
//
// Locate required Hii relative protocols
//
Status = gBS->LocateProtocol (
&gEfiHiiDatabaseProtocolGuid,
NULL,
(VOID **) &mHiiDatabase
);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (
&gEfiHiiConfigRoutingProtocolGuid,
NULL,
(VOID **) &mHiiConfigRouting
);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (
&gEfiDevicePathFromTextProtocolGuid,
NULL,
(VOID **) &mPathFromText
);
然后安装Protocol:
//
// Install FormBrowser2 protocol
//
mPrivateData.Handle = NULL;
Status = gBS->InstallProtocolInterface (
&mPrivateData.Handle,
&gEfiFormBrowser2ProtocolGuid,
EFI_NATIVE_INTERFACE,
&mPrivateData.FormBrowser2
);
ASSERT_EFI_ERROR (Status);
Status = gBS->InstallProtocolInterface (
&mPrivateData.Handle,
&gEdkiiFormBrowserEx2ProtocolGuid,
EFI_NATIVE_INTERFACE,
&mPrivateData.FormBrowserEx2
);
ASSERT_EFI_ERROR (Status);
Status = gBS->InstallProtocolInterface (
&mPrivateData.Handle,
&gEdkiiFormBrowserExProtocolGuid,
EFI_NATIVE_INTERFACE,
&mPrivateData.FormBrowserEx
);
ASSERT_EFI_ERROR (Status);
然后初始化SETUP_DRIVER_PRIVATE_DATA
(对应变量mPrivateData
):
InitializeListHead (&mPrivateData.FormBrowserEx2.FormViewHistoryHead);
InitializeListHead (&mPrivateData.FormBrowserEx2.OverrideQestListHead);
还有初始化EDKII_FORM_DISPLAY_ENGINE_PROTOCOL
(对应mFormDisplay
,这个Protocol实际上会在DisplayEngineDxe.inf
整个模块中安装,所以利用了Callback方式):
Status = gBS->LocateProtocol (
&gEdkiiFormDisplayEngineProtocolGuid,
NULL,
(VOID **) &mFormDisplay
);
if (EFI_ERROR (Status)) {
EfiCreateProtocolNotifyEvent (
&gEdkiiFormDisplayEngineProtocolGuid,
TPL_CALLBACK,
FormDisplayCallback,
NULL,
&Registration
);
}
以及FORM_DISPLAY_ENGINE_FORM
(对应gDisplayFormData
):
/**
Initialize the Display form structure data.
**/
VOID
InitializeDisplayFormData (
VOID
)
{
EFI_STATUS Status;
gDisplayFormData.Signature = FORM_DISPLAY_ENGINE_FORM_SIGNATURE;
gDisplayFormData.Version = FORM_DISPLAY_ENGINE_VERSION_1;
gDisplayFormData.ImageId = 0;
gDisplayFormData.AnimationId = 0;
InitializeListHead (&gDisplayFormData.StatementListHead);
InitializeListHead (&gDisplayFormData.StatementListOSF);
InitializeListHead (&gDisplayFormData.HotKeyListHead);
Status = gBS->CreateEvent (
EVT_NOTIFY_WAIT,
TPL_CALLBACK,
EfiEventEmptyFunction,
NULL,
&mValueChangedEvent
);
ASSERT_EFI_ERROR (Status);
}
DisplayEngineDxe.inf
首先是安装一个Strings:
gHiiHandle = HiiAddPackages (
&gDisplayEngineGuid,
ImageHandle,
DisplayEngineStrings,
NULL
);
ASSERT (gHiiHandle != NULL);
然后是安装Protocol:
//
// Install Form Display protocol
//
Status = gBS->InstallProtocolInterface (
&mPrivateData.Handle,
&gEdkiiFormDisplayEngineProtocolGuid,
EFI_NATIVE_INTERFACE,
&mPrivateData.FromDisplayProt
);
ASSERT_EFI_ERROR (Status);
之后初始化一堆字符串:
InitializeDisplayStrings();
/**
Initialize the HII String Token to the correct values.
**/
VOID
InitializeDisplayStrings (
VOID
)
{
gReconnectConfirmChanges = GetToken (STRING_TOKEN (RECONNECT_CONFIRM_CHANGES), gHiiHandle);
mUnknownString = GetToken (STRING_TOKEN (UNKNOWN_STRING), gHiiHandle);
gSaveFailed = GetToken (STRING_TOKEN (SAVE_FAILED), gHiiHandle);
gNoSubmitIfFailed = GetToken (STRING_TOKEN (NO_SUBMIT_IF_CHECK_FAILED), gHiiHandle);
gReconnectFail = GetToken (STRING_TOKEN (RECONNECT_FAILED), gHiiHandle);
gReconnectRequired = GetToken (STRING_TOKEN (RECONNECT_REQUIRED), gHiiHandle);
gChangesOpt = GetToken (STRING_TOKEN (RECONNECT_CHANGES_OPTIONS), gHiiHandle);
gSaveProcess = GetToken (STRING_TOKEN (DISCARD_OR_JUMP), gHiiHandle);
gSaveNoSubmitProcess = GetToken (STRING_TOKEN (DISCARD_OR_CHECK), gHiiHandle);
gDiscardChange = GetToken (STRING_TOKEN (DISCARD_OR_JUMP_DISCARD), gHiiHandle);
gJumpToFormSet = GetToken (STRING_TOKEN (DISCARD_OR_JUMP_JUMP), gHiiHandle);
gCheckError = GetToken (STRING_TOKEN (DISCARD_OR_CHECK_CHECK), gHiiHandle);
gPromptForData = GetToken (STRING_TOKEN (PROMPT_FOR_DATA), gHiiHandle);
gPromptForPassword = GetToken (STRING_TOKEN (PROMPT_FOR_PASSWORD), gHiiHandle);
gPromptForNewPassword = GetToken (STRING_TOKEN (PROMPT_FOR_NEW_PASSWORD), gHiiHandle);
gConfirmPassword = GetToken (STRING_TOKEN (CONFIRM_PASSWORD), gHiiHandle);
gConfirmError = GetToken (STRING_TOKEN (CONFIRM_ERROR), gHiiHandle);
gPassowordInvalid = GetToken (STRING_TOKEN (PASSWORD_INVALID), gHiiHandle);
gPressEnter = GetToken (STRING_TOKEN (PRESS_ENTER), gHiiHandle);
gEmptyString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
gMiniString = GetToken (STRING_TOKEN (MINI_STRING), gHiiHandle);
gOptionMismatch = GetToken (STRING_TOKEN (OPTION_MISMATCH), gHiiHandle);
gFormSuppress = GetToken (STRING_TOKEN (FORM_SUPPRESSED), gHiiHandle);
gProtocolNotFound = GetToken (STRING_TOKEN (PROTOCOL_NOT_FOUND), gHiiHandle);
gFormNotFound = GetToken (STRING_TOKEN (STATUS_BROWSER_FORM_NOT_FOUND), gHiiHandle);
gNoSubmitIf = GetToken (STRING_TOKEN (STATUS_BROWSER_NO_SUBMIT_IF), gHiiHandle);
gBrowserError = GetToken (STRING_TOKEN (STATUS_BROWSER_ERROR), gHiiHandle);
gConfirmDefaultMsg = GetToken (STRING_TOKEN (CONFIRM_DEFAULT_MESSAGE), gHiiHandle);
gConfirmDiscardMsg = GetToken (STRING_TOKEN (CONFIRM_DISCARD_MESSAGE), gHiiHandle);
gConfirmSubmitMsg = GetToken (STRING_TOKEN (CONFIRM_SUBMIT_MESSAGE), gHiiHandle);
gConfirmResetMsg = GetToken (STRING_TOKEN (CONFIRM_RESET_MESSAGE), gHiiHandle);
gConfirmExitMsg = GetToken (STRING_TOKEN (CONFIRM_EXIT_MESSAGE), gHiiHandle);
gConfirmDefaultMsg2nd = GetToken (STRING_TOKEN (CONFIRM_DEFAULT_MESSAGE_2ND), gHiiHandle);
gConfirmSubmitMsg2nd = GetToken (STRING_TOKEN (CONFIRM_SUBMIT_MESSAGE_2ND), gHiiHandle);
gConfirmResetMsg2nd = GetToken (STRING_TOKEN (CONFIRM_RESET_MESSAGE_2ND), gHiiHandle);
gConfirmExitMsg2nd = GetToken (STRING_TOKEN (CONFIRM_EXIT_MESSAGE_2ND), gHiiHandle);
gConfirmOpt = GetToken (STRING_TOKEN (CONFIRM_OPTION), gHiiHandle);
gConfirmOptYes = GetToken (STRING_TOKEN (CONFIRM_OPTION_YES), gHiiHandle);
gConfirmOptNo = GetToken (STRING_TOKEN (CONFIRM_OPTION_NO), gHiiHandle);
gConfirmMsgConnect = GetToken (STRING_TOKEN (CONFIRM_OPTION_CONNECT), gHiiHandle);
gConfirmMsgEnd = GetToken (STRING_TOKEN (CONFIRM_OPTION_END), gHiiHandle);
gPasswordUnsupported = GetToken (STRING_TOKEN (PASSWORD_NOT_SUPPORTED ), gHiiHandle);
}
之后初始化几个变量:
ZeroMem (&gHighligthMenuInfo, sizeof (gHighligthMenuInfo));
ZeroMem (&gOldFormEntry, sizeof (gOldFormEntry));
最后注册几个快捷键:
//
// Use BrowserEx2 protocol to register HotKey.
//
Status = gBS->LocateProtocol (&gEdkiiFormBrowserEx2ProtocolGuid, NULL, (VOID **) &FormBrowserEx2);
if (!EFI_ERROR (Status)) {
//
// Register the default HotKey F9 and F10 again.
//
HotKey.UnicodeChar = CHAR_NULL;
HotKey.ScanCode = SCAN_F10;
NewString = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_TEN_STRING), NULL);
ASSERT (NewString != NULL);
FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString);
HotKey.ScanCode = SCAN_F9;
NewString = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_NINE_STRING), NULL);
ASSERT (NewString != NULL);
FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString);
}