# 45. EFI\_HII\_DATABASE\_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 문자열 목록 게시

지난 수업에서는 내부적으로 HII 데이터베이스가 패키지 목록과 해당 패키지를 연속적인 데이터 배열이 아닌 많은 이중 연결 리스트로 구현된 복잡한 데이터 구조로 저장되어 있다는 것을 발견했다.

하지만 `EFI_HII_DATABASE_PROTOCOL` 에서 `ExportPackageLists` 를 사용했을 때 패키지 목록과 해당 패키지의 연속 데이터 배열을 받을 수 있었다. 이는 HII 데이터베이스의 내부를 추상화하고 사용자 입장에서 분석하기 쉬운 형식으로 데이터를 제공하는 편리한 인터페이스라고 볼 수 있다.

`EFI_HII_DATABASE_PROTOCOL` 에서 `NewPackageList` 를 통해 패키지 목록을 데이터베이스에 추가하는 경우도 마찬가지이다. 이 함수는 동일한 형식의 연속적 데이터 배열에서 오는 패키지 목록을 예상한다.

```
EFI_HII_DATABASE_PROTOCOL.NewPackageList()

Summary:
Adds the packages in the package list to the HII database.

Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_HII_DATABASE_NEW_PACK) (
 IN CONST EFI_HII_DATABASE_PROTOCOL *This,
 IN CONST EFI_HII_PACKAGE_LIST_HEADER *PackageList,
 IN CONST EFI_HANDLE DriverHandle, OPTIONAL
 OUT EFI_HII_HANDLE *Handle
 );

Parameters:
This		A pointer to the EFI_HII_DATABASE_PROTOCOL instance
PackageList	A pointer to an EFI_HII_PACKAGE_LIST_HEADER structure
DriverHandle	Associate the package list with this EFI handle
Handle		A pointer to the EFI_HII_HANDLE instance
Description	This function adds the packages in the package list to the database and returns a handle. If there is a
		EFI_DEVICE_PATH_PROTOCOL associated with the DriverHandle, then this function will create a
		package of type EFI_PACKAGE_TYPE_DEVICE_PATH and add it to the package list.
```

`ShowHII` 애플리케이션의 출력을 다시 한번 살펴보자.

```
FS0:\> ShowHII.efi
PackageList[0]: GUID=A487A478-51EF-48AA-8794-7BEE2A0562F1; size=0x1ADC
        Package[0]: type=STRINGS; size=0x1AC4
        Package[1]: type=END; size=0x4
PackageList[1]: GUID=19618BCE-55AE-09C6-37E9-4CE04084C7A1; size=0x21E4
        Package[0]: type=STRINGS; size=0x21CC
        Package[1]: type=END; size=0x4
PackageList[2]: GUID=2F30DA26-F51B-4B6F-85C4-31873C281BCA; size=0xA93
        Package[0]: type=STRINGS; size=0xA7B
        Package[1]: type=END; size=0x4
PackageList[3]: GUID=F74D20EE-37E7-48FC-97F7-9B1047749C69; size=0x2EE9
        Package[0]: type=IMAGES; size=0x2ED1
        Package[1]: type=END; size=0x4
PackageList[4]: GUID=EBF8ED7C-0DD1-4787-84F1-F48D537DCACF; size=0x46C
        Package[0]: type=FORMS; size=0x82
        Package[1]: type=FORMS; size=0x82
        Package[2]: type=STRINGS; size=0x199
        Package[3]: type=STRINGS; size=0x19B
        Package[4]: type=DEVICE_PATH; size=0x1C
        Package[5]: type=END; size=0x4
PackageList[5]: GUID=FE561596-E6BF-41A6-8376-C72B719874D0; size=0x93F
        Package[0]: type=FORMS; size=0xF5
        Package[1]: type=STRINGS; size=0x40A
        Package[2]: type=STRINGS; size=0x40C
        Package[3]: type=DEVICE_PATH; size=0x1C
        Package[4]: type=END; size=0x4
PackageList[6]: GUID=2A46715F-3581-4A55-8E73-2B769AAA30C5; size=0x6B0
        Package[0]: type=FORMS; size=0x143
        Package[1]: type=STRINGS; size=0x539
        Package[2]: type=DEVICE_PATH; size=0x1C
        Package[3]: type=END; size=0x4
PackageList[7]: GUID=99FDC8FD-849B-4EBA-AD13-FB9699C90A4D; size=0x6FE
        Package[0]: type=STRINGS; size=0x340
        Package[1]: type=STRINGS; size=0x3A6
        Package[2]: type=END; size=0x4
PackageList[8]: GUID=E38C1029-E38F-45B9-8F0D-E2E60BC9B262; size=0x15DA
        Package[0]: type=STRINGS; size=0xA88
        Package[1]: type=STRINGS; size=0xB3A
        Package[2]: type=END; size=0x4
PackageList[9]: GUID=D9DCC5DF-4007-435E-9098-8970935504B2; size=0x855
        Package[0]: type=FORMS; size=0x1F6
        Package[1]: type=STRINGS; size=0x62B
        Package[2]: type=DEVICE_PATH; size=0x1C
        Package[3]: type=END; size=0x4
PackageList[10]: GUID=F5F219D3-7006-4648-AC8D-D61DFB7BC6AD; size=0x14EC
        Package[0]: type=SIMPLE_FONTS; size=0x14D4
        Package[1]: type=END; size=0x4
PackageList[11]: GUID=4B47D616-A8D6-4552-9D44-CCAD2E0F4CF9; size=0x6AC8
        Package[0]: type=FORMS; size=0x1030
        Package[1]: type=STRINGS; size=0x3C99
        Package[2]: type=STRINGS; size=0x1DCB
        Package[3]: type=DEVICE_PATH; size=0x1C
        Package[4]: type=END; size=0x4
PackageList[12]: GUID=F95A7CCC-4C55-4426-A7B4-DC8961950BAE; size=0x13909
        Package[0]: type=STRINGS; size=0x138F1
        Package[1]: type=END; size=0x4
PackageList[13]: GUID=DEC5DAA4-6781-4820-9C63-A7B0E4F1DB31; size=0x8677
        Package[0]: type=STRINGS; size=0x865F
        Package[1]: type=END; size=0x4
PackageList[14]: GUID=4344558D-4EF9-4725-B1E4-3376E8D6974F; size=0x83BD
        Package[0]: type=STRINGS; size=0x83A5
        Package[1]: type=END; size=0x4
PackageList[15]: GUID=0AF0B742-63EC-45BD-8DB6-71AD7F2FE8E8; size=0xCB04
        Package[0]: type=STRINGS; size=0xCAEC
        Package[1]: type=END; size=0x4
PackageList[16]: GUID=25F200AA-D3CB-470A-BF51-E7D162D22E6F; size=0x1D3D7
        Package[0]: type=STRINGS; size=0x1D3BF
        Package[1]: type=END; size=0x4
PackageList[17]: GUID=5F5F605D-1583-4A2D-A6B2-EB12DAB4A2B6; size=0x3048
        Package[0]: type=STRINGS; size=0x3030
        Package[1]: type=END; size=0x4
PackageList[18]: GUID=F3D301BB-F4A5-45A8-B0B7-FA999C6237AE; size=0x26B5
        Package[0]: type=STRINGS; size=0x269D
        Package[1]: type=END; size=0x4
PackageList[19]: GUID=7C04A583-9E3E-4F1C-AD65-E05268D0B4D1; size=0x5CB
        Package[0]: type=STRINGS; size=0x5B3
        Package[1]: type=END; size=0x4
```

위 출력에서 각 패키지의 목록들이 하나 이상의 데이터 패키지를 포함하고 `END` 타입의 패키지로 끝나는 것을 볼 수 있다.

일반적인 패키지는 `EFI_HII_PACKAGE_HEADER` 와 데이터 컨텐츠가 포함된다. 하지만 `END` 타입의 패키지에는 `EFI_HII_PACKAGE_HEADER` 필드가 `EFI_HII_PACKAGE_END` 로 설정되어 있을 뿐이다.

```
typedef struct {
 EFI_GUID PackageListGuid;
 UINT32 PackagLength;
} EFI_HII_PACKAGE_LIST_HEADER;

typedef struct {
 UINT32 Length:24;
 UINT32 Type:8;
 UINT8 Data[ … ];
} EFI_HII_PACKAGE_HEADER;
```

따라서 기본적으로 패키지 목록 데이터의 모습은 다음과 같다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2Fgit-blob-c6fd9bd9a8e030b553aab95f05865455bdda244b%2Fimage%20(21).png?alt=media" alt=""><figcaption></figcaption></figure>

일반적인 패키지에 다른 유형이 존재할 수 있다. 이 `EFI_HII_PACKAGE_HEADER.type` 필드에 존재하는 정의를 살펴보자.

```
//
// Value of HII package type
//
#define EFI_HII_PACKAGE_TYPE_ALL             0x00
#define EFI_HII_PACKAGE_TYPE_GUID            0x01
#define EFI_HII_PACKAGE_FORMS                0x02
#define EFI_HII_PACKAGE_STRINGS              0x04
#define EFI_HII_PACKAGE_FONTS                0x05
#define EFI_HII_PACKAGE_IMAGES               0x06
#define EFI_HII_PACKAGE_SIMPLE_FONTS         0x07
#define EFI_HII_PACKAGE_DEVICE_PATH          0x08
#define EFI_HII_PACKAGE_KEYBOARD_LAYOUT      0x09
#define EFI_HII_PACKAGE_ANIMATIONS           0x0A
#define EFI_HII_PACKAGE_END                  0xDF
#define EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN    0xE0
#define EFI_HII_PACKAGE_TYPE_SYSTEM_END      0xFF
```

다음 수업에서는 문자열 패키지가 포함된 패키지 목록을 추가할 것이다.

```
EFI_HII_PACKAGE_HEADER.type = EFI_HII_PACKAGE_STRINGS
```

## 템플릿을 통해 application 만들기

이전 스크립트를 이용해 편리하게 애플리케이션을 제작하자.

```
$ ./createNewApp.sh HIIStringsC
```

그리고 `UefiLessonsPkg/UefiLessonsPkg.dsc` 에 application 정보를 추가한다.

```
[Components]
  ...
  UefiLessonsPkg/HIIStringsC/HIIStringsC.inf
```

## 패키지 목록 GUID

모든 패키지 목록에는 자체 GUID가 있어야기 때문에 `UefiLessonsPkg/UefiLessonsPkg.dec` 파일에 추가해주어야 한다.

```
[Guids]
  ...
  gHIIStringsCGuid = { 0x8e0b8ed3, 0x14f7, 0x499d, { 0xa2, 0x24, 0xae, 0xe8, 0x9d, 0xc9, 0x7f, 0xa3 }}
```

애플리케이션 소스 코드에서 참조하기 위해 `UefiLessonsPkg/HIIStringsC/HIIStringsC.inf` 파일에도 선언한다.

```
[Guids]
  gHIIStringsCGuid
```

마지막으로 `Packages` 섹션에 다음 `UefiLessonsPkg/UefiLessonsPkg.dec` 정보도 포함시킨다.

```
[Packages]
  ...
  UefiLessonsPkg/UefiLessonsPkg.dec
```

## UefiHiiServicesLib

각 HII 프로토콜은 시스템에서 하나의 인스턴스만을 가지기 때문에 생성자의 모든 `LocateProtocol` 논리를 추상화하고 프로토콜의 전역 변수를 채우는 라이브러리가 있다.\
<https://github.com/tianocore/edk2/tree/master/MdeModulePkg/Library/UefiHiiServicesLib>

```
EFI_HII_STRING_PROTOCOL  		*gHiiString		// UEFI HII String Protocol
EFI_HII_DATABASE_PROTOCOL  		*gHiiDatabase		// UEFI HII Database Protocol
EFI_HII_CONFIG_ROUTING_PROTOCOL 	*gHiiConfigRouting	// UEFI HII Config Routing Protocol
EFI_HII_FONT_PROTOCOL 			*gHiiFont		// UEFI HII Font Protocol
EFI_HII_IMAGE_PROTOCOL  		*gHiiImage		// UEFI HII Image Protocol
```

따라서 마지막 애플리케이션에서 아래 코드를 사용하는 대신에

```
  EFI_STATUS Status;
  EFI_HII_DATABASE_PROTOCOL* HiiDbProtocol;
  Status = gBS->LocateProtocol(&gEfiHiiDatabaseProtocolGuid,
                               NULL,
                               (VOID**)&HiiDbProtocol);
  if (EFI_ERROR(Status)) {
    Print(L"ERROR: Could not find HII Database protocol: %r\n", Status);
    return Status;
  }
```

application의 `*.inf` 파일에 `UefiHiiServicesLib` 을 포함하고 `gHiiDatabase` 를 `EFI_HII_DATABASE_PROTOCOL*` 로 사용할 수 있다.

따라서 현재 application의 `*.inf` 파일에 `UefiHiiServicesLib` 를 Library classes section에 추가한다.

```
[LibraryClasses]
  ...
  UefiHiiServicesLib
```

또한 application의 `*.inf` 에 이 라이브러리 패키지 `*.dec` 파일을 포함해야 한다.

```
[Packages]
  ...
  MdeModulePkg/MdeModulePkg.dec
```

최종적으로 소스 코드에서 해당 헤더를 선언하여 사용한다.

```
#include <Library/UefiHiiServicesLib.h>
```

## Application 코드 설명

아래 코드에서 패키지 목록의 크기를 계산하지 않기 때문에 약간의 트릭을 사용한다. 그리고 예시에서는 실제로 필요한 것보다 더 큰 숫자를 사용하고 있다.

이 장은 여러 파트로 분할되어 있으며 충분히 어렵기 때문에 이 이상의 어려움을 만들지 않고자 함을 미리 설명한다.

```
#include <Library/MemoryAllocationLib.h>
...
  CHAR8* Data = (CHAR8*) AllocateZeroPool(200);          // CHEAT! NEEDS CORRECTION FOR YOUR OWN PACKAGES!
  UINT32 offset = 0;
  EFI_HII_PACKAGE_LIST_HEADER* PackageListHdr = (EFI_HII_PACKAGE_LIST_HEADER*)&Data[offset];
  PackageListHdr->PackageListGuid = gHIIStringsCGuid;
  offset += sizeof(EFI_HII_PACKAGE_LIST_HEADER);

  <...> 		// Fill String Packages in the memory starting from &Data[offset]
  offset += <...>	// Add packages size to the 'offset' variable

  EFI_HII_PACKAGE_HEADER* HIIEndPackageHdr = (EFI_HII_PACKAGE_HEADER*)&Data[offset];
  HIIEndPackageHdr->Type = EFI_HII_PACKAGE_END;
  HIIEndPackageHdr->Length = sizeof(EFI_HII_PACKAGE_HEADER);
  offset += sizeof(EFI_HII_PACKAGE_HEADER);

  PackageListHdr->PackageLength = offset;

  <...>			// Add new package to the HII Database

  FreePool(Data);
```
