44. HII 데이터베이스 내부
먼저 HII 데이터베이스의 기본 구조를 살펴보자. https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabase.h
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;
UINTN Attribute;
EFI_GUID CurrentLayoutGuid;
EFI_HII_KEYBOARD_LAYOUT *CurrentLayout;
} HII_DATABASE_PRIVATE_DATA;
해당 구조에는 기본 HII 프로토콜에 대한 포인터가 포함되어 있다. 이러한 각 프로토콜은 HII의 다른 부분과의 상호작용을 담당한다. 예를 들어 하나는 HII 데이터베이스(EFI_HII_FONT_PROTOCOL)의 글꼴과의 상호 작용을 담당하고, 다른 하나는 이미지와의 상호 작용(EFI_HII_IMAGE_PROTOCOL/EFI_HII_IMAGE_EX_PROTOCOL)과 문자열(EFI_HII_STRING_PROTOCOL)과의 상호작용을 담당한다.
앞서 데이터베이스에서 HII 패키지를 추가/제거하는 EFI_HII_DATABASE_PROTOCOL
를 살펴보았기 때문에 다른 HII 요소들을 통해 이 프로토콜의 가능성을 더 살펴보자.
프로토콜 외에도 이 구조는 다른 요소에 대한 이중 연결 리스트를 유지한다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Base.h
///
/// LIST_ENTRY structure definition.
///
typedef struct _LIST_ENTRY LIST_ENTRY;
///
/// _LIST_ENTRY structure definition.
///
struct _LIST_ENTRY {
LIST_ENTRY *ForwardLink;
LIST_ENTRY *BackLink;
};
이러한 목록 중에는 데이터 베이스 레코드 LIST_ENTRY DatabaseList
에 대한 이중 연결 리스트가 있다.
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabase.h
#define HII_DATABASE_RECORD_SIGNATURE SIGNATURE_32 ('h','i','d','r')
typedef struct _HII_DATABASE_RECORD {
UINTN Signature;
HII_DATABASE_PACKAGE_LIST_INSTANCE *PackageList;
EFI_HANDLE DriverHandle;
EFI_HII_HANDLE Handle;
LIST_ENTRY DatabaseEntry;
} HII_DATABASE_RECORD;
LIST_ENTRY DatabaseList
는 첫번째 HII_DATABASE_RECORD
의 DatabaseEntry
필드를 가르킨다. 이 구조에서 DatabaseEntry
는 차례로 다음 HII_DATABASE_RECORD
의 DatabaseEntry
필드를 가르킨다.
각 데이터베이스 레코드에는 HII_DATABASE_PACKAGE_LIST_INSTANCE *PackageList
가 존재한다.
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabase.h
typedef struct _HII_DATABASE_PACKAGE_LIST_INSTANCE {
EFI_HII_PACKAGE_LIST_HEADER PackageListHdr;
LIST_ENTRY GuidPkgHdr;
LIST_ENTRY FormPkgHdr;
LIST_ENTRY KeyboardLayoutHdr;
LIST_ENTRY StringPkgHdr;
LIST_ENTRY FontPkgHdr;
HII_IMAGE_PACKAGE_INSTANCE *ImagePkg;
LIST_ENTRY SimpleFontPkgHdr;
UINT8 *DevicePathPkg;
} HII_DATABASE_PACKAGE_LIST_INSTANCE;
각 이중 연결 리스트에는 이 패키지 목록에 있는 해당 유형의 패키지에 대한 포인터가 포함되어 있다. 이전 학습에서는 모든 패키지 목록과 해당 패키지를 연속 데이터 배열로 받았지만 이는 EFI_HII_DATABASE_PROTOCOL
의 ExportPackageLists
함수 기능일 뿐, HII 데이터는 플랫폼 메모리 전체에 분산 될 수 있는 이중 연결 리스트로 표현되어 있다.
HII_DATABASE_RECORD
에 있는 또 다른 중요한 필드는 EFI_HII_HANDLE
핸들이다. 각 HII_DATABASE_RECORD
는 패키지 목록을 정의하고 EFI_HII_HANDLE
로 식별된다.
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiInternalFormRepresentation.h
typedef VOID* EFI_HII_HANDLE;
하지만 GenerateHiiDatabaseRecord
함수 구현을 살펴보면 HII 핸들의 실제 구현을 볼 수 있다.
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/HiiDatabaseDxe/Database.c
EFI_STATUS
GenerateHiiDatabaseRecord (
IN HII_DATABASE_PRIVATE_DATA *Private,
OUT HII_DATABASE_RECORD **DatabaseNode
)
{
...
HII_HANDLE *HiiHandle;
HII_DATABASE_RECORD *DatabaseRecord;
...
DatabaseRecord->Handle = (EFI_HII_HANDLE) HiiHandle;
...
}
HII_HANDLE
타입의 정의는 다음과 같다.
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabase.h
#define HII_HANDLE_SIGNATURE SIGNATURE_32 ('h','i','h','l')
typedef struct {
UINTN Signature;
LIST_ENTRY Handle;
UINTN Key;
} HII_HANDLE;
Key
필드는 HII_DATABASE_PRIVATE_DATA
에 있는 HiiHandleCount
의 현재 값에 해당한다.
그리고 LIST_ENTRY Handle
은 시스템의 모든 HII_HANDLE
을 함께 연결하는데 도움이 된다. 중요한 점은 HII_DATABASE_PRIVATE_DATA
에 있는 LIST_ENTRY HiiHandleList
필드와 동일한 핸들 목록이다.
다음은 방금 다룬 HII 데이터 베이스 구조의 그림이다.

HII 데이터베이스 초기화
이 HII 데이터베이스 구조는 HiiDatabaseDxe
에서 정적 초기화 되었다.
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseEntry.c
HII_DATABASE_PRIVATE_DATA mPrivate = {
HII_DATABASE_PRIVATE_DATA_SIGNATURE,
{
(LIST_ENTRY *) NULL,
(LIST_ENTRY *) NULL
},
{
(LIST_ENTRY *) NULL,
(LIST_ENTRY *) NULL
},
{
HiiStringToImage,
HiiStringIdToImage,
HiiGetGlyph,
HiiGetFontInfo
},
{
HiiNewImage,
HiiGetImage,
HiiSetImage,
HiiDrawImage,
HiiDrawImageId
},
{
HiiNewImageEx,
HiiGetImageEx,
HiiSetImageEx,
HiiDrawImageEx,
HiiDrawImageIdEx,
HiiGetImageInfo
},
{
HiiNewString,
HiiGetString,
HiiSetString,
HiiGetLanguages,
HiiGetSecondaryLanguages
},
{
HiiNewPackageList,
HiiRemovePackageList,
HiiUpdatePackageList,
HiiListPackageLists,
HiiExportPackageLists,
HiiRegisterPackageNotify,
HiiUnregisterPackageNotify,
HiiFindKeyboardLayouts,
HiiGetKeyboardLayout,
HiiSetKeyboardLayout,
HiiGetPackageListHandle
},
{
HiiConfigRoutingExtractConfig,
HiiConfigRoutingExportConfig,
HiiConfigRoutingRouteConfig,
HiiBlockToConfig,
HiiConfigToBlock,
HiiGetAltCfg
},
{
EfiConfigKeywordHandlerSetData,
EfiConfigKeywordHandlerGetData
},
{
(LIST_ENTRY *) NULL,
(LIST_ENTRY *) NULL
},
0,
{
(LIST_ENTRY *) NULL,
(LIST_ENTRY *) NULL
},
EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK),
{
0x00000000,
0x0000,
0x0000,
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
},
NULL
};
HiiDatabaseDxe
driver의 진입점은 linked lists(LIST_ENTRY
)를 초기화하고 HII 데이버베이스의 모든 프로토콜을 시트템에 설치한다.
EFI_STATUS
EFIAPI
InitializeHiiDatabase (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
...
InitializeListHead (&mPrivate.DatabaseList);
InitializeListHead (&mPrivate.DatabaseNotifyList);
InitializeListHead (&mPrivate.HiiHandleList);
InitializeListHead (&mPrivate.FontInfoList);
...
Handle = NULL;
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
);
}
...
}
Linked lists 포인터
여기에 해당 필드에 대한 포인터로부터 구조체 포인터를 가져오는 전처리기 방법이 있다.
바로CR
매크로의 도움으로 DatabaseEntry
필드에 대한 포인터로 HII_DATABASE_RECORD
에 대한 포인터를 쉽게 얻는 것이다.
LIST_ENTRY* Link;
HII_DATABASE_RECORD* DatabaseRecord = CR (Link, HII_DATABASE_RECORD, DatabaseEntry, HII_DATABASE_RECORD_SIGNATURE);
아래는CR
매크로에 대한 정의이다.
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DebugLib.h
@param Record The pointer to the field specified by Field within a data
structure of type TYPE.
@param TYPE The name of the data structure type to return This
data structure must contain the field specified by Field.
@param Field The name of the field in the data structure specified
by TYPE to which Record points.
@param TestSignature The 32-bit signature value to match.
**/
#if !defined(MDEPKG_NDEBUG)
#define CR(Record, TYPE, Field, TestSignature) \
(DebugAssertEnabled () && (BASE_CR (Record, TYPE, Field)->Signature != TestSignature)) ? \
(TYPE *) (_ASSERT (CR has Bad Signature), Record) : \
BASE_CR (Record, TYPE, Field)
#else
#define CR(Record, TYPE, Field, TestSignature) \
BASE_CR (Record, TYPE, Field)
#endif
그리고 BASE_CR
에 대한 정의는 다음과 같다.
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Base.h
/**
Macro that returns a pointer to the data structure that contains a specified field of
that data structure. This is a lightweight method to hide information by placing a
public data structure inside a larger private data structure and using a pointer to
the public data structure to retrieve a pointer to the private data structure.
This function computes the offset, in bytes, of field specified by Field from the beginning
of the data structure specified by TYPE. This offset is subtracted from Record, and is
used to return a pointer to a data structure of the type specified by TYPE. If the data type
specified by TYPE does not contain the field specified by Field, then the module will not compile.
@param Record Pointer to the field specified by Field within a data structure of type TYPE.
@param TYPE The name of the data structure type to return. This data structure must
contain the field specified by Field.
@param Field The name of the field in the data structure specified by TYPE to which Record points.
@return A pointer to the structure from one of it's elements.
**/
#define BASE_CR(Record, TYPE, Field) ((TYPE *) ((CHAR8 *) (Record) - OFFSET_OF (TYPE, Field)))
마지막으로로OFFSET_OF
에 대한 정의이다.
/**
The macro that returns the byte offset of a field in a data structure.
This function returns the offset, in bytes, of field specified by Field from the
beginning of the data structure specified by TYPE. If TYPE does not contain Field,
the module will not compile.
@param TYPE The name of the data structure that contains the field specified by Field.
@param Field The name of the field in the data structure.
@return Offset, in bytes, of field.
**/
#if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__)
#define OFFSET_OF(TYPE, Field) ((UINTN) __builtin_offsetof(TYPE, Field))
#endif
#ifndef OFFSET_OF
#define OFFSET_OF(TYPE, Field) ((UINTN) &(((TYPE *)0)->Field))
#endif
Last updated