当一台机器按某一个快捷键进setup界面的时候,如F2/F8/ESC,UiApp的生命周期就正式开始了。
UiApp,顾名思义,它就是一个app,只不过,这是一个特殊的app,我们进入setup的界面就是这个app的具体表现:
OptionNumber = PlatformRegisterFvBootOption (
&gUiAppFileGuid,
L"Enter Setup",
LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_APP
);
DEBUG((DEBUG_INFO, "setup %a(),%d,%d\n", __FUNCTION__,__LINE__,OptionNumber));
Status = EfiBootManagerAddKeyOptionVariable (
NULL, (UINT16)OptionNumber, 0, &F8, NULL
);
如上这一段,就是这个app开始的地方,通过将app进行注册,然后进入这个app,
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = InitializeUserInterface
通过这个app的inf文件,我们能够发现,这个入口就是InitializeUserInterface函数,
InitializeUserInterface函数
这个函数主要进行下面几件事:
1、判断是否有设置用户密码或者管理员密码,如果有,需要输入密码才能进入设置界面
2、打开下面两个协议,设置setup界面的显示分辨率和文本的大小:
Status = gBS->HandleProtocol (
gST->ConsoleOutHandle,
&gEfiGraphicsOutputProtocolGuid,
(VOID**)&GraphicsOutput
);
if (EFI_ERROR (Status)) {
GraphicsOutput = NULL;
}
Status = gBS->HandleProtocol (
gST->ConsoleOutHandle,
&gEfiSimpleTextOutProtocolGuid,
(VOID**)&SimpleTextOut
);
if (EFI_ERROR (Status)) {
SimpleTextOut = NULL;
}
3、我们需要关闭看门狗,防止我们进入设置界面后时间过久被看门狗判断为异常而进行系统复位,并在进入setup界面前进行清屏:
gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
gST->ConOut->ClearScreen (gST->ConOut);
4、安装字体以及字符串支持,字体使用一种称为Glyph的元素表示,它其实就是一个二进制的文件,里面包含了描述字体的元素,字符串通过UNI文件转换成,编译时在AutoGen.c中生成对应的数组,然后通过下面的函数来注册到HII数据库中。
EFI_HII_HANDLE HiiHandle;
HiiHandle = HiiAddPackages (
&mFontPackageGuid,
gImageHandle,
&mFontBin,
NULL
);
ASSERT (HiiHandle != NULL);
gStringPackHandle = HiiAddPackages (
&mBdsStringPackGuid,
gImageHandle,
BdsDxeStrings,
NULL
);
ASSERT (gStringPackHandle != NULL);
5、使设置的显示分辨率和文本大小生效,生效后进入UiEntry
UiSetConsoleMode (TRUE);
UiEntry (FALSE);
最后,进行退出相关处理:
UiSetConsoleMode (FALSE);
UninitializeStringSupport ();
HiiRemovePackages (HiiHandle);
UiEntry函数
UiEntry函数就正式进入到了我们经常修改设置界面相关内容的地方,先运行下面的代码
//
// Enter Setup page.
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_PC_USER_SETUP)
);
//
// Indicate if the connect all has been performed before.
// If has not been performed before, do here.
//
if (!ConnectAllHappened) {
EfiBootManagerConnectAll ();
}
//
// The boot option enumeration time is acceptable in Ui driver
//
EfiBootManagerRefreshAllBootOption ();
//
// Boot Logo is corrupted, report it using Boot Logo protocol.
//
Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);
if (!EFI_ERROR (Status) && (BootLogo != NULL)) {
BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);
}
这里以防万一,所以看前文UiEntry函数的参数是false,又运行了一遍EfiBootManagerConnectAll ()函数,如果这个函数本身是运行过了的,可以设置为true也未尝不可(个人判断)
运行完上述代码后接下来继续运行InitializeFrontPage ()函数、CallFrontPage ()函数、FreeFrontPage ()函数、SetupResetReminder ()函数。整个UiEntry函数就这样运行完了,现在主要讲一下上述过程中的函数分别是干什么用的
InitializeFrontPage ()函数
1、打开gEfiFormBrowser2Protocol
//
// Locate Hii relative protocols
//
Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &gFormBrowser2);
if (EFI_ERROR (Status)) {
return Status;
}
2、安装相关的protocol
gFrontPagePrivate.DriverHandle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&gFrontPagePrivate.DriverHandle,
&gEfiDevicePathProtocolGuid,
&mFrontPageHiiVendorDevicePath,
&gEfiHiiConfigAccessProtocolGuid,
&gFrontPagePrivate.ConfigAccess,
NULL
);
ASSERT_EFI_ERROR (Status);
3、安装VFR对应的二进制文件
//
// Publish our HII data
//
gFrontPagePrivate.HiiHandle = HiiAddPackages (
&mFrontPageGuid,
gFrontPagePrivate.DriverHandle,
FrontPageVfrBin,
UiAppStrings,
NULL
);
ASSERT (gFrontPagePrivate.HiiHandle != NULL);
4、更新字符串数据以及表单数据,这个字符串是banner的字符串,当然其他的字符串,完全可以在所属的C文件中更改,这个表单数据,也就是分页。
//
//Updata Front Page banner strings
//
UpdateFrontPageBannerStrings ();
//
// Update front page menus.
//
UpdateFrontPageForm();
CallFrontPage ()函数
这个函数就不细说了,一看注释便知
//
// Begin waiting for USER INPUT
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_PC_INPUT_WAIT)
);
ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
Status = gFormBrowser2->SendForm (
gFormBrowser2,
&gFrontPagePrivate.HiiHandle,
1,
&mFrontPageGuid,
0,
NULL,
&ActionRequest
);
//
// Check whether user change any option setting which needs a reset to be effective
//
if (ActionRequest == EFI_BROWSER_ACTION_REQUEST_RESET) {
EnableResetRequired ();
}
FreeFrontPage ()函数
这个函数同理:
EFI_STATUS Status;
Status = gBS->UninstallMultipleProtocolInterfaces (
gFrontPagePrivate.DriverHandle,
&gEfiDevicePathProtocolGuid,
&mFrontPageHiiVendorDevicePath,
&gEfiHiiConfigAccessProtocolGuid,
&gFrontPagePrivate.ConfigAccess,
NULL
);
ASSERT_EFI_ERROR (Status);
//
// Publish our HII data
//
HiiRemovePackages (gFrontPagePrivate.HiiHandle);
if (gFrontPagePrivate.LanguageToken != NULL) {
FreePool (gFrontPagePrivate.LanguageToken);
gFrontPagePrivate.LanguageToken = NULL;
}
SetupResetReminder ()函数
这个函数主要判断你改了某些设置后是否进行重启,因为有的需要重启才能生效
EFI_INPUT_KEY Key;
CHAR16 *StringBuffer1;
CHAR16 *StringBuffer2;
//
//check any reset required change is applied? if yes, reset system
//
if (IsResetRequired ()) {
StringBuffer1 = AllocateZeroPool (MAX_STRING_LEN * sizeof (CHAR16));
ASSERT (StringBuffer1 != NULL);
StringBuffer2 = AllocateZeroPool (MAX_STRING_LEN * sizeof (CHAR16));
ASSERT (StringBuffer2 != NULL);
StrCpyS (StringBuffer1, MAX_STRING_LEN, L"Configuration changed. Reset to apply it Now.");
StrCpyS (StringBuffer2, MAX_STRING_LEN, L"Press ENTER to reset");
//
// Popup a menu to notice user
//
do {
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, StringBuffer1, StringBuffer2, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
FreePool (StringBuffer1);
FreePool (StringBuffer2);
gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
}
写在最后,作为一个页面,下面的代码同样不能少,并且也是上述代码的前提
FRONT_PAGE_CALLBACK_DATA gFrontPagePrivate = {
FRONT_PAGE_CALLBACK_DATA_SIGNATURE,
NULL,
NULL,
NULL,
{
FakeExtractConfig,
FakeRouteConfig,
FrontPageCallback
}
};
HII_VENDOR_DEVICE_PATH mFrontPageHiiVendorDevicePath = {
{
{
HARDWARE_DEVICE_PATH,
HW_VENDOR_DP,
{
(UINT8) (sizeof (VENDOR_DEVICE_PATH)),
(UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
}
},
//
// {8E6D99EE-7531-48f8-8745-7F6144468FF2} f59887d4-28d6-4801-9bdf-d20120d0bd0e
//
{ 0xf59887d4, 0x28d6, 0x4801, { 0x9b, 0xdf, 0xd2, 0x01, 0x20, 0xd0, 0xbd, 0x0e } }
},
{
END_DEVICE_PATH_TYPE,
END_ENTIRE_DEVICE_PATH_SUBTYPE,
{
(UINT8) (END_DEVICE_PATH_LENGTH),
(UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
}
}
};