💻
UEFI 프로젝트
  • 🧑‍🏫프로젝트 개요
  • 📖UEFI 개념
    • 1. BIOS의 과거
    • 2. UEFI 개념
    • 3. BIOS vs UEFI
    • 4. UEFI 부팅 단계
  • 🖥️UEFI 개발
    • UEFI 개발 시작하기
      • 0. EDK II 빌드 환경 구성
      • 1. 간단한 EFI application 만들기
      • 2. 간단한 Pkg 만들기
      • 3. Hello World 출력하기
      • 4. 라이브러리와 Hello World
      • 5. Conf를 통한 Build 단순화
    • 핸들 및 프로토콜
      • 6. 핸들/프로토콜 데이터 베이스 구조 - Part 1
      • 7. 핸들/프로토콜 데이터 베이스 구조 - Part 2
      • 8. HandleProtocol API 함수 & ImageHandle 프로토콜을 통한 정보
      • 9. ProtocolsPerHandle API를 통한 ImageHandle 프로토콜 가져오기
      • 10. EFI_STATUS 타입 과 EFI_ERROR 매크로
    • 메모리 맵
      • 11. EFI 메모리 맵 정보 얻기
      • 12. EFI 메모리 맵을 리눅스 커널 스타일로 바꾸기
    • 명령줄 인수를 받는 간단한 앱 만들기
      • 13.ShellAppMain Entry point
      • 14.gRT->GetNextVariableName API를 사용하여 모든 변수 이름 및 GUID 가져오기
    • 부팅 옵션
      • 15. gRT->GetVariable API를 사용하여 부팅 변수 가져오기 및 구문 분석
      • 16. OVMF 이미지 내에 부팅 옵션 추가
      • 17. 부팅 옵션에 WaitForEvent 함수 추가
      • 18. ReadKeyStroke 함수로 사용자 입력 처리
      • 19. bcfg 명령어를 사용한 부팅 옵션 수정
    • PCD
      • 20. PCD 소개
      • 21. PCD 변수에 대한 Overriding
      • 22. Feature Flag PCD와 BOOLEAN FixedAtBuild PCD의 비교
      • 23. PatchableInModule PCD 및 GenPatchPcdTable/PatchPcdValue 유틸리티를 통해 PCD를 변경하는 방법
      • 24. Dynamic/DynamiEx PCDs
      • 25. PCD 더 알아보기
    • 테이블
      • 26. EFI_CONFIGURATION_TABLE에서 참조되는 테이블
      • 27. dmem/EFI_SMBIOS_PROTOCOL/smbiosview를 통해서 SMBIOS 정보 가져오기
      • 28. EFI_SHELL_PROTOCOL을 통하여 ACPI 테이블을 파일에 저장하기
      • 29. EFI_ACPI_SDT_PROTOCOL 및 ShellLib를 사용하여 ACPI BGRT 테이블에서 BMP 이미지 저장하기
    • PCI
      • 30. PCI 루트 브리지 찾은 후 시스템의 모든 PCI 기능 가져오기
      • 31. ShellLib/PrintLib 함수를 사용해 PCI Vendor/Device 정보 가져오기
      • 32. EFI_PCI_IO_PROTOCOL을 사용해 PCI Option ROM 이미지 표시
      • 33. EfiRom 유틸리티를 사용한 PCI Option ROM 이미지 파싱 및 생성
    • 드라이버 및 라이브러리
      • 34. 간단한 UEFI 드라이버 생성
      • 35. 애플리케이션에서 사용할 간단한 라이브러리 생성
      • 36. Library의 constructor와 destructor, NULL Library
      • 37. Shell에 acpiview 명령을 추가하는 방법 조사
      • 38. 사용자 지정 프로토콜을 만들고 사용하기
      • 39. RegisterKeyNotify / UnrigisterKeyNotify 함수를 사용해 단축키 기능을 추가하는 드라이버 만들기
      • 40. Key #### NVRAM 변수
    • 디버그
      • 41. DEBUG 출력문 내부 구조와 DEBUG 문 제어를 위한 PCD 분석, 그리고 OVMF 부트 로그 가져오기
      • 42. GDB를 이용한 Driver/Application 및 OVMF Debug
    • HII
      • 43. HII 데이터베이스 개념 및 출력
      • 44. HII 데이터베이스 내부
      • 45. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 문자열 목록 게시
      • 46. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 HII 패키지 목록 게시
      • 47. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 HII 패키지 목록 게시
      • 48. UNI 파일 및 HiiLib를 사용하여 HII String 패키지 게시 및 작업하기
      • 49.MODULE_UNI_FILE/PACKAGE_UNI_FILE/[UserExtensions.TianoCore."ExtraFiles"]의 도움으로 UNI 파일 선언하기
      • 50.UEFI_HII_RESOURCE_SECTION을 사용하여 문자열 패키지와 함께 HII 패키지 목록 게시하기
      • 51. UEFI APP에 메뉴얼 추가하기(shell의 -?와 help 옵션)
      • 52. Russian 글꼴 추가 - Part 1.
      • 53. Russian 글꼴 추가 - Part 2.
      • 54. EFI_HII_STRING_PROTOCOL의 NewString 및 SetString 함수를 사용하여 다른 언어에 대한 문자열 패키지를 동적으로 추가
      • 55. PlatformLangCodes EFI 변수 수정 및 다른 언어를 동적 추가하기
      • 56. 코드에서 FILE_GUID 및 BASE_NAME을 가져오기
    • VFR
      • 57. VFR을 사용해 간단한 폼 생성 및 EFI_FORM_BROWSER2_PROTOCOL.SendForm()를 통해 화면에 폼 표시하기
      • 58. VFR 요소 : subtitle 및 text
      • 59. 간단한 폼 애플리케이션을 UEFI 드라이버 Form으로 변환하기
      • 60. gRT->SetVariable() 함수를 사용한 UEFI 변수 생성, 변경 및 삭제
      • 61.dmpstore 명령을 사용하여 변수를 파일에 저장/로드하기
      • 62. UEFI Device path의 구조
      • 63. checkbox를 가진 HII 폼 만들기
      • 64. checkbox를 가진 HII폼 만들기
      • 65. VFR 추가 입력 요소 Part 1: number
      • 66. VFR 추가 입력 요소 Part 2: string
      • 67. VFR 추가 입력 요소 Part 3: date & time
      • 68. VFR 추가 입력 요소 Part 3: oneof & orderedlist
      • 69. VFR의 조건부 키워드
      • 70. VFR의 상수 및 연산자가 내장된 기본 조건문
      • 71. 기본 VFR 내장 문자열용 함수
      • 72. label 키워드를 이용하여 HII 양식에 동적 요소 추가하기
      • 73. VFR question 기본값 설정
  • 🔐UEFI 보안
    • 1. 개요
    • 2. 공격 벡터
    • 3. mitigation
    • 4. 정적 분석 방법
    • 5. 동적 분석 방법
Powered by GitBook
On this page
  • 애플리케이션 만들기
  • UNI 파일
  • HiiGetString
  • Best Language
  1. UEFI 개발
  2. HII

48. UNI 파일 및 HiiLib를 사용하여 HII String 패키지 게시 및 작업하기

NewPackageList 함수를 직접 사용하여 새 문자열 패키지를 추가하는 것은 꽤 어려운 작업이다. 우리는몇 개의 문자열만 추가했으며 패키지 목록에 필요한 데이터 배열을 동적으로 계산하지도 않았다는 점을 명심해야한다. 또한 글꼴/양식/이미지/등등을 추가하려면 이러한 패키지의 형식을 조사하고 필요한 함수도 작성해야한다.

EDKII가 이러한 작업을 단순화하기 위해 무엇을 제공할 수 있는지 확인해보자. 이번 장에서는 특히 문자열 패키지 생성을 단순화하는 방법에 대해 알아볼 것이다.

애플리케이션 만들기

평소처럼 스크립트를 사용해 새 애플리케이션을 만든다.

./createNewApp.sh HIIStringsUNI

DSC 패키지에 추가해준다.(UefiLessonsPkg/UefiLessonsPkg.dsc)

[Components]
  ...
  UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI.inf

지난 번과 같이 패키지 목록에 대한 GUID가 필요하며 패키지 DEC 파일 (UefiLessonsPkg/UefiLessonsPkg.dec)에서 선언한다.

[Guids]
  ...
  gHIIStringsUNIGuid = { 0x6ee19058, 0x0fe2, 0x44ed, { 0x89, 0x1c, 0xa5, 0xd7, 0xe1, 0x08, 0xee, 0x1a }}

그리고 애플리케이션 INF 파일(UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI.inf)에 이것들을 추가한다.

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

[Guids]
  gHIIStringsUNIGuid

UNI 파일

EDKII에서는 특별한 UNI 형식의 파일에서 모든 번역 문자열을 정의할 수 있다. EDKII 빌드 유틸리티는 이러한 파일의 데이터를 구문 분석하고 문자열 패키지 콘텐츠로 배열을 생성해 준다.

애플리케이션 INF 파일 UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI.inf의 Sources 섹션에 Strings.uni 파일을 추가해보자.

*.uni 파일에 아무 이름이나 사용할 수 있으며 Source 섹션에 *.uni를 원하는 만큼 포함할 수 있다는 것을 알아두자.

[Sources]
  ...
  Strings.uni

UefiLessonsPkg/HIIStringsUNI/Strings.uni파일을 만들어주고 내용을 써준다.

#langdef en-US "English"
#langdef fr-FR "Francais"

#string STR_HELLO         #language en-US  "Hello!"
                          #language fr-FR  "Bonjour!"

#string STR_BYE           #language en-US  "Bye!"
                          #language fr-FR  "Au revoir!"

이 파일은 2개의 문자열 패키지에 대한 소스가 된다.

1) 'en-US' string package
ID 1: "English"
ID 2: "Hello!"
ID 3: "Bye!"
2) 'fr-FR' string package
ID 1: "Francais"
ID 2: "Bonjour!"
ID 3: "Au revoir!"

지금 애플리케이션을 빌드하면 이 파일이 일반 빌드 파일인 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/HIIStringsUNIStrDefs.h와 함께 생성된다.

이 파일의 이름은 INF 파일의 BASE_NAME 값으로 구성되며 기본적으로 <BASE_NAME>StrDefs.h이다.

이 파일을 살펴보면,

extern unsigned char HIIStringsUNIStrings[];

이것은 필요한 문자열 패키지 데이터가 있는 배열이다. HiiLib의 HiiAddPackages 함수로 전달하여 HII 패키지를 데이터베이스에 추가하고 새 패키지 목록을 생성한다.

[https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Library/HiiLib.h \

https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiHiiLib/HiiLib.c](https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Library/HiiLib.hhttps://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiHiiLib/HiiLib.c)

/**
  Registers a list of packages in the HII Database and returns the HII Handle
  associated with that registration.  If an HII Handle has already been registered
  with the same PackageListGuid and DeviceHandle, then NULL is returned.  If there
  are not enough resources to perform the registration, then NULL is returned.
  If an empty list of packages is passed in, then NULL is returned.  If the size of
  the list of package is 0, then NULL is returned.
  The variable arguments are pointers that point to package headers defined
  by UEFI VFR compiler and StringGather tool.
  #pragma pack (push, 1)
  typedef struct {
    UINT32                  BinaryLength;
    EFI_HII_PACKAGE_HEADER  PackageHeader;
  } EDKII_AUTOGEN_PACKAGES_HEADER;
  #pragma pack (pop)
  @param[in]  PackageListGuid  The GUID of the package list.
  @param[in]  DeviceHandle     If not NULL, the Device Handle on which
                               an instance of DEVICE_PATH_PROTOCOL is installed.
                               This Device Handle uniquely defines the device that
                               the added packages are associated with.
  @param[in]  ...              The variable argument list that contains pointers
                               to packages terminated by a NULL.
  @retval NULL   An HII Handle has already been registered in the HII Database with
                 the same PackageListGuid and DeviceHandle.
  @retval NULL   The HII Handle could not be created.
  @retval NULL   An empty list of packages was passed in.
  @retval NULL   All packages are empty.
  @retval Other  The HII Handle associated with the newly registered package list.
**/
EFI_HII_HANDLE
EFIAPI
HiiAddPackages (
  IN CONST EFI_GUID    *PackageListGuid,
  IN       EFI_HANDLE  DeviceHandle  OPTIONAL,
  ...
  )
;

이러한 것들로 패키지 목록 생성은 다음과 같이 간단하게 할 수 있다.

...

#include <Library/HiiLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_HII_HANDLE Handle = HiiAddPackages(&gHIIStringsUNIGuid,
                                         NULL,
                                         HIIStringsUNIStrings,
                                         NULL);

  if (Handle == NULL)
  {
    Print(L"Error! Can't perform HiiAddPackages\n");
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}

우리의 애플리케이션 INF 파일의 LibraryClasses 섹션에 HiiLib를 포함하는 것을 잊지 말자.

[Packages]
  ...
  MdeModulePkg/MdeModulePkg.dec

...

[LibraryClasses]
  ...
  HiiLib

애플리케이션을 빌드하고 OVMF에서 실행하면 실제로 코드가 2개의 문자열 패키지로 새 패키지 목록을 생성하는 것을 볼 수 있다.

FS0:\> HIIStringsUNI.efi
FS0:\> ShowHII.efi
...
PackageList[20]: GUID=6EE19058-0FE2-44ED-891C-A5D7E108EE1A; size=0xA6
        Package[0]: type=STRINGS; size=0x46
        Package[1]: type=STRINGS; size=0x48
        Package[2]: type=END; size=0x4

HiiGetString

HiiLib 라이브러리가 이미 포함되어 있으므로 HiiGetString 함수를 사용하여 문자열을 인쇄해 보자.

[https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Library/HiiLib.h \

https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiHiiLib/HiiString.c](https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Library/HiiLib.hhttps://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiHiiLib/HiiString.c)

/**
  Retrieves a string from a string package in a specific language specified in Language
  or in the best lanaguage. See HiiGetStringEx () for the details.
  @param[in]  HiiHandle  A handle that was previously registered in the HII Database.
  @param[in]  StringId   The identifier of the string to retrieved from the string
                         package associated with HiiHandle.
  @param[in]  Language   The language of the string to retrieve.  If this parameter
                         is NULL, then the current platform language is used.  The
                         format of Language must follow the language format assumed in
                         the HII Database.
  @retval NULL   The string specified by StringId is not present in the string package.
  @retval Other  The string was returned.
**/
EFI_STRING
EFIAPI
HiiGetString (
  IN EFI_HII_HANDLE  HiiHandle,
  IN EFI_STRING_ID   StringId,
  IN CONST CHAR8     *Language  OPTIONAL
  );

애플리케이션에 다음 코드를 추가해보자.

Print(L"en-US ID=1: %s\n", HiiGetString(Handle, 1, "en-US"));
Print(L"en-US ID=2: %s\n", HiiGetString(Handle, 2, "en-US"));
Print(L"en-US ID=3: %s\n", HiiGetString(Handle, 3, "en-US"));
Print(L"fr-FR ID=1: %s\n", HiiGetString(Handle, 1, "fr-FR"));
Print(L"fr-FR ID=2: %s\n", HiiGetString(Handle, 2, "fr-FR"));
Print(L"fr-FR ID=3: %s\n", HiiGetString(Handle, 3, "fr-FR"));

빌드 후에 실행해보면 다음과 같은 결과를 볼 수 있다.

FS0:\> HIIStringsUNI.efi
en-US ID=1: English
en-US ID=2: <null string>
en-US ID=3: <null string>
fr-FR ID=1: Francais
fr-FR ID=2: <null string>
fr-FR ID=3: <null string>

뭔가 잘못된 것을 알 수 있다. String 패키지에 언어 문자열(ID=1)만 채워진 것을 볼 수 있다.

Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/AutoGen.c 파일에 있는 실제 HIIStringsUNIStrings 배열 데이터를 살펴보자.

//
//Unicode String Pack Definition
//
unsigned char HIIStringsUNIStrings[] = {

// STRGATHER_OUTPUT_HEADER
  0x92,  0x00,  0x00,  0x00,

// PACKAGE HEADER

  0x46,  0x00,  0x00,  0x04,  0x34,  0x00,  0x00,  0x00,  0x34,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x01,  0x00,  0x65,  0x6E,
  0x2D,  0x55,  0x53,  0x00,

// PACKAGE DATA

// 0x0001: $PRINTABLE_LANGUAGE_NAME:0x0001
  0x14,  0x45,  0x00,  0x6E,  0x00,  0x67,  0x00,  0x6C,  0x00,  0x69,  0x00,  0x73,  0x00,  0x68,  0x00,  0x00,
  0x00,
  0x00,
// PACKAGE HEADER

  0x48,  0x00,  0x00,  0x04,  0x34,  0x00,  0x00,  0x00,  0x34,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x01,  0x00,  0x66,  0x72,
  0x2D,  0x46,  0x52,  0x00,

// PACKAGE DATA

// 0x0001: $PRINTABLE_LANGUAGE_NAME:0x0001
  0x14,  0x46,  0x00,  0x72,  0x00,  0x61,  0x00,  0x6E,  0x00,  0x63,  0x00,  0x61,  0x00,  0x69,  0x00,  0x73,
  0x00,  0x00,  0x00,
  0x00,

};

몇 가지 주목해야 할 사항들이 있다.

  • 배열에는 문자열 데이터 패키지만 포함되며 패키지 목록 헤더나 End 패키지는 포함되지 않는다.

  • 배열에는 특별한 4바이트 헤더 STRGATHER_OUTPUT_HEADER가 있다. - 이 헤더를 포함하는 배열의 크기를 포함한다.

  • 배열에는 실제로 언어 이름 문자열만 있다.

어떻게 STRGATHER_OUTPUT_HEADER가 패키지 목록을 구성하고 적절한 데이터로 NewPackageList를 호출하는 데 사용되는지 알아보기 위해 함수 구현을 살펴볼 수 있다.

이제 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/HIIStringsUNISTrDefs.h를 한 번 더 살펴보자.

//
//Unicode String ID
//
// #define $LANGUAGE_NAME                                       0x0000 // not referenced
// #define $PRINTABLE_LANGUAGE_NAME                             0x0001 // not referenced
// #define STR_HELLO                                            0x0002 // not referenced
// #define STR_BYE                                              0x0003 // not referenced

이것이 우리 문제의 원인이다. 단순히 토큰이참조되지 않았기 때문에 문자열이 배열로 이동하지 않았던 것이다.

배열 데이터 생성을 담당하는 빌드 도구 StrGather.py (https://github.com/tianocore/edk2/blob/master/BaseTools/Source/Python/AutoGen/StrGather.py)는 애플리케이션 코드에서 STRING_TOKEN(...) 매크로를 확인하고 코드에서 참조되는 문자열만 채운다.

STRING_TOKEN = re.compile('STRING_TOKEN *\(([A-Z0-9_]+) *\)', re.MULTILINE | re.UNICODE)

그러나 전체 언어 이름은 필수 필드이므로 항상 채워진다. 이것이 첫 번째 애플리케이션 실행에서 그 필드만 보인 이유이다.

출력코드를 다음과 같이 변경해보자.

Print(L"en-US ID=1: %s\n", HiiGetString(Handle, 1, "en-US"));
Print(L"en-US ID=2: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_HELLO), "en-US"));
Print(L"en-US ID=3: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_BYE), "en-US"));
Print(L"fr-FR ID=1: %s\n", HiiGetString(Handle, 1, "fr-FR"));
Print(L"fr-FR ID=2: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_HELLO), "fr-FR"));
Print(L"fr-FR ID=3: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_BYE), "fr-FR"));

애플리케이션을 빌드하고 생성된 파일을 확인해보자.

Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/HIIStringsUNIStrDefs.h

//
//Unicode String ID
//
// #define $LANGUAGE_NAME                                       0x0000 // not referenced
// #define $PRINTABLE_LANGUAGE_NAME                             0x0001 // not referenced
#define STR_HELLO                                            0x0002
#define STR_BYE                                              0x0003

Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/AutoGen.c

//
//Unicode String Pack Definition
//
unsigned char HIIStringsUNIStrings[] = {

// STRGATHER_OUTPUT_HEADER
  0xD6,  0x00,  0x00,  0x00,

// PACKAGE HEADER

  0x60,  0x00,  0x00,  0x04,  0x34,  0x00,  0x00,  0x00,  0x34,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x01,  0x00,  0x65,  0x6E,
  0x2D,  0x55,  0x53,  0x00,

// PACKAGE DATA

// 0x0001: $PRINTABLE_LANGUAGE_NAME:0x0001
  0x14,  0x45,  0x00,  0x6E,  0x00,  0x67,  0x00,  0x6C,  0x00,  0x69,  0x00,  0x73,  0x00,  0x68,  0x00,  0x00,
  0x00,
// 0x0002: STR_HELLO:0x0002
  0x14,  0x48,  0x00,  0x65,  0x00,  0x6C,  0x00,  0x6C,  0x00,  0x6F,  0x00,  0x21,  0x00,  0x00,  0x00,
// 0x0003: STR_BYE:0x0003
  0x14,  0x42,  0x00,  0x79,  0x00,  0x65,  0x00,  0x21,  0x00,  0x00,  0x00,
  0x00,
// PACKAGE HEADER

  0x72,  0x00,  0x00,  0x04,  0x34,  0x00,  0x00,  0x00,  0x34,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x01,  0x00,  0x66,  0x72,
  0x2D,  0x46,  0x52,  0x00,

// PACKAGE DATA

// 0x0001: $PRINTABLE_LANGUAGE_NAME:0x0001
  0x14,  0x46,  0x00,  0x72,  0x00,  0x61,  0x00,  0x6E,  0x00,  0x63,  0x00,  0x61,  0x00,  0x69,  0x00,  0x73,
  0x00,  0x00,  0x00,
// 0x0002: STR_HELLO:0x0002
  0x14,  0x42,  0x00,  0x6F,  0x00,  0x6E,  0x00,  0x6A,  0x00,  0x6F,  0x00,  0x75,  0x00,  0x72,  0x00,  0x21,
  0x00,  0x00,  0x00,
// 0x0003: STR_BYE:0x0003
  0x14,  0x41,  0x00,  0x75,  0x00,  0x20,  0x00,  0x72,  0x00,  0x65,  0x00,  0x76,  0x00,  0x6F,  0x00,  0x69,
  0x00,  0x72,  0x00,  0x21,  0x00,  0x00,  0x00,
  0x00,

};

이번에는 문자열이 HIIStringsUNIStrings 배열에 들어간 것을 볼 수 있다.

이제 OVMF에서 애플리케이션을 실행해보면 올바른 출력을 얻을 수 있다.

FS0:\> HIIStringsUNI.efi
en-US ID=1: English
en-US ID=2: Hello!
en-US ID=3: Bye!
fr-FR ID=1: Francais
fr-FR ID=2: Bonjour!
fr-FR ID=3: Au revoir!

STRING_TOKEN 매크로를 사용하여 문자열을 참조하는 것이 얼마나 중요한지 알 수 있지만 실제로 이 매크로는 그 값에 아무런 영향을 미치지 않는다는 점을 잘 기억해야 한다. (https://github.com/tianocore/edk2/blob/master/BaseTools/Source /C/Include/Common/UefiInternalFormRepresentation.h)

//
// References to string tokens must use this macro to enable scanning for
// token usages.
//
//
// STRING_TOKEN is not defined in UEFI specification. But it is placed
// here for the easy access by C files and VFR source files.
//
#define STRING_TOKEN(t) t

Best Language

HiiGetString 라이브러리 기능은 직접 프로토콜 사용보다 간단할 뿐만 아니라 또 다른 유용한 기능이 존재한다. 바로 대상 언어를 제공하지 않고 HiiGetString을 호출할 수 있다는 점이다. 이 방법을 쓰면 함수는 어떤 언어를 사용하는 것이 더 나은지 스스로 결정한다. 이렇게 하면 PlatformLang 런타임 변수의 값에 따라 최상의 언어가 선택된다. gRT->GetNextVariableName/gRT->GetVariable 을 통해 런타임 변수 작업을 했던 것이 기억날 것이다. PlatformLang도 그 중 하나였다. ListVariables.efi 애플리케이션을 통해 이 옵션이 gEfiGlobalVariableGuid 아래에 있음을 발견했었다.

애플리케이션 끝에 다음 문자열 출력문을 추가해보자.

Print(L"Best language ID=1: %s\n", HiiGetString(Handle, 1, NULL));
Print(L"Best language ID=2: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_HELLO), NULL));
Print(L"Best langiage ID=3: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_BYE), NULL));

애플리케이션을 빌드 후 OVMF에서 실행해보면 다음과 같은 결과를 볼 수 있다.

FS0:\> HIIStringsUNI.efi
en-US ID=1: English
en-US ID=2: Hello!
en-US ID=3: Bye!
fr-FR ID=1: Francais
fr-FR ID=2: Bonjour!
fr-FR ID=3: Au revoir!
Best language ID=1: English
Best language ID=2: Hello!
Best language ID=3: Bye!

이제 UEFI Shell에서 exit을 실행하여 BIOS 설정으로 이동한 후 언어를 프랑스어로 변경한다. QEMU를 닫고 다시 실행해보면 이제 최상의 언어에 대한 출력은 fr-FR String 패키지에서 나오는 것을 볼 수 있다.

FS0:\> HIIStringsUNI.efi
en-US ID=1: English
en-US ID=2: Hello!
en-US ID=3: Bye!
fr-FR ID=1: Francais
fr-FR ID=2: Bonjour!
fr-FR ID=3: Au revoir!
Best language ID=1: Francais
Best language ID=2: Bonjour!
Best language ID=3: Au revoir!

또한 보다 일반적인 언어 이름으로 HiiGetString을 호출할 수 있는 기능이 있다. 예를 들어 fr-FR 대신 fr을 써도 여전히 ​​올바른 문자열 패키지를 선택한다.

 Print(L"fr ID=3: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_BYE), "fr"));

위 코드는 다음과 같이 출력된다.

fr ID=3: Au revoir!

이 기능들은 모두 HiiGetString 구현에서 온 것임을 명심해야한다.

EFI_HII_STRING_PROTOCOL.GetString()에 "fr-FR" 대신 "fr"을 넣으면 오류가 발생한다.

Previous47. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 HII 패키지 목록 게시Next49.MODULE_UNI_FILE/PACKAGE_UNI_FILE/[UserExtensions.TianoCore."ExtraFiles"]의 도움으로 UNI 파일 선언하기

Last updated 2 years ago

UNI 파일 형식 스펙문서에서 UNI 파일 형식에 대한 자세한 내용을 읽을 수 있다. (

8BE4DF61-93CA-11D2-AA0D-00E098032B8C - gEfiGlobalVariableGuid

🖥️
https://edk2-docs.gitbook.io/edk-ii-uni-specification/)
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Guid/GlobalVariable.h
https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec