48. UNI 파일 및 HiiLib를 사용하여 HII String 패키지 게시 및 작업하기
NewPackageList 함수를 직접 사용하여 새 문자열 패키지를 추가하는 것은 꽤 어려운 작업이다. 우리는몇 개의 문자열만 추가했으며 패키지 목록에 필요한 데이터 배열을 동적으로 계산하지도 않았다는 점을 명심해야한다. 또한 글꼴/양식/이미지/등등을 추가하려면 이러한 패키지의 형식을 조사하고 필요한 함수도 작성해야한다.
EDKII가 이러한 작업을 단순화하기 위해 무엇을 제공할 수 있는지 확인해보자. 이번 장에서는 특히 문자열 패키지 생성을 단순화하는 방법에 대해 알아볼 것이다.
애플리케이션 만들기
평소처럼 스크립트를 사용해 새 애플리케이션을 만든다.
./createNewApp.sh HIIStringsUNIDSC 패키지에 추가해준다.(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]
gHIIStringsUNIGuidUNI 파일
EDKII에서는 특별한 UNI 형식의 파일에서 모든 번역 문자열을 정의할 수 있다. EDKII 빌드 유틸리티는 이러한 파일의 데이터를 구문 분석하고 문자열 패키지 콘텐츠로 배열을 생성해 준다.
애플리케이션 INF 파일 UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI.inf의 Sources 섹션에 Strings.uni 파일을 추가해보자.
*.uni 파일에 아무 이름이나 사용할 수 있으며 Source 섹션에 *.uni를 원하는 만큼 포함할 수 있다는 것을 알아두자.
UefiLessonsPkg/HIIStringsUNI/Strings.uni파일을 만들어주고 내용을 써준다.
이 파일은 2개의 문자열 패키지에 대한 소스가 된다.
UNI 파일 형식 스펙문서에서 UNI 파일 형식에 대한 자세한 내용을 읽을 수 있다. (https://edk2-docs.gitbook.io/edk-ii-uni-specification/)
지금 애플리케이션을 빌드하면 이 파일이 일반 빌드 파일인 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/HIIStringsUNIStrDefs.h와 함께 생성된다.
이 파일의 이름은 INF 파일의 BASE_NAME 값으로 구성되며 기본적으로 <BASE_NAME>StrDefs.h이다.
이 파일을 살펴보면,
이것은 필요한 문자열 패키지 데이터가 있는 배열이다. 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)
이러한 것들로 패키지 목록 생성은 다음과 같이 간단하게 할 수 있다.
우리의 애플리케이션 INF 파일의 LibraryClasses 섹션에 HiiLib를 포함하는 것을 잊지 말자.
애플리케이션을 빌드하고 OVMF에서 실행하면 실제로 코드가 2개의 문자열 패키지로 새 패키지 목록을 생성하는 것을 볼 수 있다.
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)
애플리케이션에 다음 코드를 추가해보자.
빌드 후에 실행해보면 다음과 같은 결과를 볼 수 있다.
뭔가 잘못된 것을 알 수 있다. String 패키지에 언어 문자열(ID=1)만 채워진 것을 볼 수 있다.
Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/AutoGen.c 파일에 있는 실제 HIIStringsUNIStrings 배열 데이터를 살펴보자.
몇 가지 주목해야 할 사항들이 있다.
배열에는 문자열 데이터 패키지만 포함되며 패키지 목록 헤더나 End 패키지는 포함되지 않는다.
배열에는 특별한 4바이트 헤더 STRGATHER_OUTPUT_HEADER가 있다. - 이 헤더를 포함하는 배열의 크기를 포함한다.
배열에는 실제로 언어 이름 문자열만 있다.
어떻게 STRGATHER_OUTPUT_HEADER가 패키지 목록을 구성하고 적절한 데이터로 NewPackageList를 호출하는 데 사용되는지 알아보기 위해 함수 구현을 살펴볼 수 있다.
이제 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/HIIStringsUNISTrDefs.h를 한 번 더 살펴보자.
이것이 우리 문제의 원인이다. 단순히 토큰이참조되지 않았기 때문에 문자열이 배열로 이동하지 않았던 것이다.
배열 데이터 생성을 담당하는 빌드 도구 StrGather.py (https://github.com/tianocore/edk2/blob/master/BaseTools/Source/Python/AutoGen/StrGather.py)는 애플리케이션 코드에서 STRING_TOKEN(...) 매크로를 확인하고 코드에서 참조되는 문자열만 채운다.
그러나 전체 언어 이름은 필수 필드이므로 항상 채워진다. 이것이 첫 번째 애플리케이션 실행에서 그 필드만 보인 이유이다.
출력코드를 다음과 같이 변경해보자.
애플리케이션을 빌드하고 생성된 파일을 확인해보자.
Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/HIIStringsUNIStrDefs.h
Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/AutoGen.c
이번에는 문자열이 HIIStringsUNIStrings 배열에 들어간 것을 볼 수 있다.
이제 OVMF에서 애플리케이션을 실행해보면 올바른 출력을 얻을 수 있다.
STRING_TOKEN 매크로를 사용하여 문자열을 참조하는 것이 얼마나 중요한지 알 수 있지만 실제로 이 매크로는 그 값에 아무런 영향을 미치지 않는다는 점을 잘 기억해야 한다. (https://github.com/tianocore/edk2/blob/master/BaseTools/Source /C/Include/Common/UefiInternalFormRepresentation.h)
Best Language
HiiGetString 라이브러리 기능은 직접 프로토콜 사용보다 간단할 뿐만 아니라 또 다른 유용한 기능이 존재한다. 바로 대상 언어를 제공하지 않고 HiiGetString을 호출할 수 있다는 점이다. 이 방법을 쓰면 함수는 어떤 언어를 사용하는 것이 더 나은지 스스로 결정한다. 이렇게 하면 PlatformLang 런타임 변수의 값에 따라 최상의 언어가 선택된다. gRT->GetNextVariableName/gRT->GetVariable 을 통해 런타임 변수 작업을 했던 것이 기억날 것이다. PlatformLang도 그 중 하나였다. ListVariables.efi 애플리케이션을 통해 이 옵션이 gEfiGlobalVariableGuid 아래에 있음을 발견했었다.
8BE4DF61-93CA-11D2-AA0D-00E098032B8C - gEfiGlobalVariableGuid https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Guid/GlobalVariable.h
https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec
애플리케이션 끝에 다음 문자열 출력문을 추가해보자.
애플리케이션을 빌드 후 OVMF에서 실행해보면 다음과 같은 결과를 볼 수 있다.
이제 UEFI Shell에서 exit을 실행하여 BIOS 설정으로 이동한 후 언어를 프랑스어로 변경한다. QEMU를 닫고 다시 실행해보면 이제 최상의 언어에 대한 출력은 fr-FR String 패키지에서 나오는 것을 볼 수 있다.
또한 보다 일반적인 언어 이름으로 HiiGetString을 호출할 수 있는 기능이 있다. 예를 들어 fr-FR 대신 fr을 써도 여전히 올바른 문자열 패키지를 선택한다.
위 코드는 다음과 같이 출력된다.
이 기능들은 모두 HiiGetString 구현에서 온 것임을 명심해야한다.
EFI_HII_STRING_PROTOCOL.GetString()에 "fr-FR" 대신 "fr"을 넣으면 오류가 발생한다.
Last updated