HII, 全称Human Interface Infrastructure, 定义了一套管理用户输入的基础架构,支持多种类型的用户
输入。在这个架构中HII数据库处于底层位置,负责提供用户安装、卸载及使用各种字符串、字体及图片
等资源的接口。
HII 数据库主要包括以下功能模块:
1 HII 数据库协议 以包列表的为单位管理HII相关的数据结构。
2 HII字体协议 从HII数据库获取字体信息的协议。
3 HII 映像协议 操作HII数据库中的映像文件的协议。
4 HII字符串协议 管理HII数据库中字符串的协议。
5 HII 配置路由协议 负责在驱动程序和配置应用程序之间传送配置数据结构的协议。
下面的章节我们将一一介绍这些模块。
HII数据库协议的实现
本节介绍HiiDatabase驱动程序中EFI_HII_DATABASE_PROTOCOL协议的实现。在HII数据库中所有
的表项均采用包列表的方式存放。包列表由guid 标识其唯一性,每个包列表包含标准头部,含一个
或多个guid包,表格包,字符串包,字体包等等 。
typedef struct _hii_database_package_list_instance{
EFI_HII_PACKAGE_LIST_HEADER PACKAGELISTHDR;
LIST_ENTRY GUIDPKGHDR;
LIST_ENTRY FORMPKGHDR;
LIST_ENTRY KEYBOARDLAYOUHDR;
LIST_ENTRY STRINGPKGHDR;
LIST_ENTRY FONTPKGHDR;
LIST_ENTRY *IMAGEPKG;
LIST_ENTRY Simplefontpkghdr;
UNIT8 *DEVICEPATHPKG;
} HII_DATABASE_PACKAGE_LIST_INSTANCE;
packagelisthdr: 包列表头部,由UEFI 规范定义。
guidPkgHdr: guid 包链表,链表 每个节点指向hii_guid_package_instance.
formpkghdr: 表格包链表,链表的每个节点指向hii_ifr_package_instance.
keyboardlayouthdr, 键盘布局链表,链表的每个节点指向HII_keyboard_layout_package_instance.
stringPkgHdr: 字符串包链表,链表的每个节点指向HII string package instance.
imagepkghdr: 指向image包
simplefontpkghdr : 简单字体包链表,链表的每个节点指向HII simple font package instance.
devicepathpkg: 指向设备路径节点。
hii_database_notify 数据结构
每个hii_database_notify 对应一个通知函数,每次调用函数hiiregisterpackagenotify() 生成一个
hii_database_notify 节点。
typedef struct _hii_database_notify {
uintn signature;
efi_handle notifyhandle;
uint8 packagetype;
efi_guid *packageguid;
efi_hii_database_notify packagenotifyfn;
efi_hii_database_notity_type notifytype;
list_entry databasenotifyentry;
} hii_database_notify;
signature: hii_database_notify 数据结构签名,值为hii_database_notify_signature.
Notifyhandle: 挂靠通知函数的handle.
packagetype:
packagenotifyfn: 满足通知条件时调用的通知函数。
notifytype: 通知类型关注的通知条件, 目前支持四种,分别是efi_hii_database_notify_new_pack,
efi_hii_database_notify_remove_pack, efi_hii_database_notify_export_pack, efi_hii_database_notify_add_pack.
databasenotifyentry: list_entry 类型, 方便将该节点加入链表。
函数
addpackages 安装一个或多个包到hii 数据库中,且调用相应的通知函数通知用户。
ExportPackagelist 导出一个包列表的内容
HiiNewPackageList() 函数
输入参数Packagelist 参数类型为EFI_HII_PACKAGE_LIST_HEADER*, 该指针指向UEFI规范定义的包
列表头部,随后的缓冲区内容是一个或多个包,目前支持的类型有:
字体包
简单字体包
字符串包
映像包
设备路径包
键盘布局包
GUID包
表格包
UEFI 规范要求系统中每个包列表由GUID标识, for 循环轮询输入参数driverhandle上已经安装的包
列表,若其guid 与输入参数packagelist指向的packagelistguid相同,则出错退出。
//
// check the package list guid to guarantee this guid in unique in database
//
for (link = private->databaselist.forwardlink; link != &private->databaselist; link = link->forwardlink) {
Databaserecord = cr(link, hii_database_record, databaseentry, hii_database_record_signature);
if (compareguid( &(databaserecord->packagelist->packagelisthdr.packagelistguid),
&packagelistguid) &&
databaserecord->Driverhandle == Driverhandle) {
return EFI_INVALID_PARAMETER;
}
}
调用generatehiidatabaserecord() 函数初始化databaserecord, 该变量类型为hii_database_record.
调用addpackages() 将packagelist指向包列表头部及各种包安装到hii 数据库中。调用Addpackage()
将packagelist指向的包列表头部及各种包安装到hii数据库中, 将输入参数driverhandle 存入databaserecored.
//
// build a packagelist node
//
status = generatehiiDatabaseRecord(priavate, &DatabaseRecord);
if (EFI_ERROR(status)) {
return Status;
}
//
// Fill in information of the created package list node
// according to incoming package list.
//
Status = addPackages(Private, EFI_HII_DATABASE_NOTIFY_NEW_PACK,
PackageList, databaseRecord);
if (efi_error(status )) {
return status;
}
DatabaseRecord->DriverHandle = DriverHandle;
检查输入参数DriverHandle上是否装有DeviePath协议,若有则组建设备路径包安装到hii数据库中,
将generatehiidatabaserecord() 函数返回的handle存入输出参数handle, 之后返回成功退出函数。
//
// Create a Device path package and add into the package list if exists.
//
Status = gBS->HandleProtocol(
DriverHandle,
&gEfiDevicePathProtocolGuid,
(VOID **) &DevicePath
);
if (!EFI_ERROR(Status) ) {
Status = AddDevicePathPackage(Private,
EFI_HII_DATABASE_NOTIFY_NEW_PACK, DEVICEPATH, DATABASERECODE);
ASSERT_EFI_ERROR(STATUS);
}
*Handle = DatabaseRecode->Handle;
return EFI_SUCCESS;
注意输出参数 handle 的类型为EFI_HII_HANDLE, HII 数据库中实际维护的handle类型为hii_handle,
如下图所示.
typdef struct {
UINTN Signature;
list_entry handle;
uintn key;
} hii_handle;
addpackages() 函数
Addpackages() 函数负责完成两件任务,之一是将packagelist指向的包列不头部及各种包安装到hii
数据库中,之二是调用之前已经存入系统的通知函数,告知安装通知函数的程序目前hii数据库有更新。
UEFI规范定义了四种通知类型。在这个函数中,输入参数notifyType支持EFI_HII_DATABASE_NOTIFY_NEW_PACKT
EFI_HII_DATABASE_NOTIFY_ADD_PACK两种。
hiiremovepackagelist() 函数
该函数将挂靠在输入参数handle 上的包列表从hii数据库中删除。 for 循环查询hii_database_private_dabaselist 链表,如果链表上的节点handle与输入参数handle相同,则该节点就是用户希望删除的包列表。接下来根据包列表类型删除包列表包含的一个或多个包,以guid包为例说明:
调用removeguidpackages() 函数将hii_database_package_list_instance.
guidpkghdr 链表上的所有guid 包一一删除,删除前调用invokeRegisterdfunction() 通知用户HII数据库中目前有哪些guid包正被移除。
//
// Get the packagelist to be removed.
//
for (Link = private->databaselist.forwardlink; link != &private->Databaselist; link = link->forwardLink) {
node = CR(link, hii_database_record, databaseentry,
hii_database_record_signature);
if (node->Handle == handle) {
Packagelist = (hii_database_package_list_instance *) (node->packagelist);
Assert (packagelist != NULL);
//
// call registered functions with REMOVE_PACK_ before removing packages then remove them
//
Status = RemoveGuidPackages(Private, Handle, PackageList);
if (Efi_error(status)) {}
return status;
}
hiiupdatePackagelist() 函数
函数参数handle标识系统需要更新的包列表,参数packagelist指向新的包列表。hiiupdatepackagelist() 函数将新的包列表中的每一一个包安装到handle标识的名列表中。
for 循环根据packagelist指向的包列表中每个包的类型,分别调用不同的处理函数删除handle标识的包列表中该类型的包。比如,packagelist 包含了guid, 那么调用Removeguidpackages() 将handle 标识的需要更新的包列表中所有的guid包删除。
//
// get original packagelist to be updated
//
for (Link = private->databaselist.ForwardLink; link != &Private->Databaselist; Link = link->forwardLink) {
node = cr(link , hii_Database_record, databaseentry, hii_database_record_signature);
if (node->handle == handle) {
oldpackagelist = node->packagelist;
//
// remove the package if its type matchs one of the package types which is contained in the new package list .
//
copymem(&packageHeader, packagehdrptr, sizeof(efi_hii_package_header));
while (packageheadr.type != efi_hii_package_end) {
switch(packageheader.type) {
case efi_hii_package_type_guid:
status = removeguidpackages(private, handle, oldpackagelist);
break;
case EFI_HII_PACKAGES_FORMS:
STATUS = REMOVEFORMPACKAGES(PRIVATE, HANDLE, OLDPACKAGELIST);
BREAK;
CASE EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
STATUS = REMOVEKYBOARDLAYOUTPACKAGES (PRIVAE, HANDLE, OLDPACKAGELIST);
BREAK;
CASE EFI_HII_PACKAGE_STRINGS:
STATUS = RemoveStringPackages(Private, Handle, OldPackageList);
break;
调用AddPackages() 将packageList 所包含的所有包安装到HII数据库中,并通知注册了通知函数的用户目前有哪些匹配通知类型的包正被加载到系统中。
//
// Add all of the packages within the new package list
//
return AddPackages(Private, EFI_HII_DATABASE_NOTIFY_ADD_PACK, PackageList, Node);
}
HiiListPackageLists() 函数
该函数的功能是列出当前系统中包含参数packagetype指定的包列表挂靠的handle. 若packageType为efi_hii_package_type_all, 则该函数必须列出当前系统中所有的包列表挂靠的handle. 若packagetype为efi_package_type_guid, 参数Packageguid需要指定具体的guid值。
当packagetype为其他类型的包,只要包列表中存在这种类型的包,则将该包列表相应的handle输出,
Exportpackagelist()函数
ExportPackageLists() 函数依照包列表格式将包列表的内容一项项依次输出,分别是包列表头,各种包,包列表尾. 输出的是包列表头,
调用exportguidpackages()将packagelist.guidpkghdr链表上的所有guid 包输出,
调用exportguidpackages() 将packagelist.guidpkghdr 链表上的所有guid 包输出。
且输出前调用invokeregisteredfunction() 函数通知注册了通知类型为efi_hii_database_notify_export_pack的用户目前有匹配类型的包正被导出。
// copy the package list header
// Resultsize indicates the length of the exported bytes of this packages list
//
ResultSize = sizeof(EFI_HII_PACKAGE_LIST_HEADER);
if (ResultSize + *UserSize <= BufferSize) {
copymem((void *) buffer, packagelist, resultsize);
}
//
// copy the packages and invoke export_pack notify functions if exists
//
Status = Exportguidpackages(
private,
handle,
packagelist,
*buffersize,
(void *)((uint8 *) buffer + Resultsize),
&ResultSize
);
if (efi_error(status)) {
return status;
}
导出包列表中所有包之后,创建了一个类型为efi_hii_package_end 的包并导入输出缓冲区。这个包用于标识包列表的结束。
//
// Append the package list end.
//
EndofPackagelist.lenght = sizeof(efi_hii_package_header);
Endofpackagelist.type = efi_hii_package_end;
if (resultsize + *usersize + sizeof(efi_hii_package_header) <= buffersize) {
copymem(
(void *) ((uint8 *)buffer + resultsize),
(void *)&endofpackagelist,
sizeof(efi_hii_package_header)
);
}
hiiunregisterpackagenotify() 函数
该函数将输入参数notificationhandle关联的hii_database_notify节点从HII_database_private_data.Databasenotifylist链表上移除,并释放该节点占用的资源。依据Notificationhandle上是否安装mHiidatabasenotifyguid判断notificationhandle是否
e