💻
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
  • 문자열 패키지
  • 문자열 패키지 내용
  • 문자열 패키지 만들기
  1. UEFI 개발
  2. HII

46. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 HII 패키지 목록 게시

파트 2: 문자열 패키지 데이터 생성

문자열 패키지

문자열 패키지는 한 가지 언어로 된 문자열들의 집합이다. 패키지의 각 문자열은 ID를 통해 참조할 수 있다. 이러한 문자열에 대한 ID는 어디에도 기록되지 않지만 ID에 번호를 매길 때 모든 문자열 패키지에서 ID는 1부터 시작해서 모든 문자열에서 1씩 증가한다고 가정한다.(몇 가지 예외가 존재하지만 지금은 중요하지 않다.)

일반적으로 인터페이스에 대한 번역을 지원하고 싶을 때 지원하려는 각 언어에 대해 문자열 패키지를 만든다. 이러한 모든 언어 문자열 패키지에서 동일한 단어는 동일한 ID를 가져야 한다.

예를 들어 아래와 같은 개념이다.

      String package (ENG)        String package (FR)
      ___________________         ___________________

ID=1   ...                            ...

       ...                            ...

ID=5   Hello!                        Bonjour!

       ...                            ...

패키지 목록에서 코드 문자열은 (ID, language)조합으로 수신 된다. 이는 번역을 쉽게 전환할 수 있는 UI를 만드는 데 도움이 된다.

문자열 패키지 내용

문자열 패키지는 EFI_HII_STRING_PACKAGE_HDR라는 헤더로 시작되고 type이 여러개인 String blocks를 포함하고 있다. 그리고 패키지 목록과 마찬가지로 마지막 요소(=String block)에는 특별한 END type이 있다.

가장 중요하고 가장 일반적인 String 블록 type은 UCS2 String 블록이다. 블록 데이터로 문자열 블록 type을 가리키고 있는 헤더와 CHAR16 문자열을 가지고 있다. 기본적으로 문자열 패키지는 각각 하나의 문자열을 포함하는 UCS2 String 블록의 집합이다.

문자열 패키지 만들기

문자열 패키지의 헤더를 살펴보자.

Prototype:
typedef struct _EFI_HII_STRING_PACKAGE_HDR {
 EFI_HII_PACKAGE_HEADER Header;
 UINT32 HdrSize;
 UINT32 StringInfoOffset;
 CHAR16 LanguageWindow[16];
 EFI_STRING_ID LanguageName;
 CHAR8 Language[ … ];
} EFI_HII_STRING_PACKAGE_HDR;

Members:
Header			The standard package header, where Header.Type = EFI_HII_PACKAGE_STRINGS.
HdrSize			Size of this header.
StringInfoOffset	Offset, relative to the start of this header, of the string information.
LanguageWindow		Specifies the default values placed in the static and dynamic windows
			before processing each SCSU-encoded string.
LanguageName		String identifier within the current string package of the full name of the
			language specified by Language.
Language		The null-terminated ASCII string that specifies the language of the strings in the package.

CHAR8 Language는 en-EN, en-US, en, ru-RU, ...와 같은 문자열이다. Language필드의 크기가 고정되어 있지 않기 때문에 EFI_HII_STRING_PACKAGE_HDR에는 전체 패키지 헤더 크기를 나타내는 HdrSize 필드가 있다.

이 짧은 형식 외에도 모든 문자열 패키지에는 전체 언어 이름(예: English) 문자열이 포함되며 LanguageName 필드가 이 문자열의 ID이다. 일반적으로 ID=1인 첫 번째 문자열이다.

이미 알고 있지만 문자열 패키지는 String Block으로 채워진다. 다양한 type이 있을 수 있지만 지금은 가장 중요한 type인 UCS2 String 블록과 End 블록을 조사해보려고 한다.

MdePkg/Include/Uefi/UefiInternalFormRepresentation.h

typedef struct {
  UINT8                   BlockType;
} EFI_HII_STRING_BLOCK;

//
// Value of different string information block types
//
#define EFI_HII_SIBT_END                     0x00
...
#define EFI_HII_SIBT_STRING_UCS2             0x14
...

typedef struct _EFI_HII_SIBT_STRING_UCS2_BLOCK {
  EFI_HII_STRING_BLOCK    Header;
  CHAR16                  StringText[1];                // <--- String size is not fixed, but to point a fact that this type of block has
} EFI_HII_SIBT_STRING_UCS2_BLOCK;                       //      a string in itself, the header contains one element array

...

typedef struct _EFI_HII_SIBT_END_BLOCK {
  EFI_HII_STRING_BLOCK    Header;
} EFI_HII_SIBT_END_BLOCK;

이제 문자열에서 패키지 데이터 배열을 채울 수 있는 함수를 작성해보자.

UINT32 InitStringPackage(CHAR8* Ptr, CHAR8* Language, CHAR16** Strings, UINTN StringsLen):

// CHAR8* Ptr	 - start of the array where we want our package data
// CHAR8* Language  - short language form for the `EFI_HII_STRING_PACKAGE_HDR.Language` field
// CHAR16** Strings - array of CHAR16* Strings that would be written to the package
// UINTN StringsLen - how many string there are in the Strings array
//
// Return UINT32    - function return the result package size

다음은 함수 구현이다.

UINT32 InitStringPackage(CHAR8* Ptr, CHAR8* Language, CHAR16** Strings, UINTN StringsLen)
{
  UINT32 Size = 0;
  EFI_HII_STRING_PACKAGE_HDR* HIIStringPackageHdr = (EFI_HII_STRING_PACKAGE_HDR*)&Ptr[0];
  HIIStringPackageHdr->Header.Type = EFI_HII_PACKAGE_STRINGS;
  UINT32 HeaderSize = (UINT32) (AsciiStrSize(Language) - 1 + sizeof (EFI_HII_STRING_PACKAGE_HDR));
  HIIStringPackageHdr->HdrSize = HeaderSize;
  HIIStringPackageHdr->StringInfoOffset = HeaderSize;
  HIIStringPackageHdr->LanguageName = 1;                // <--- the String[0] should be a full language name string!
  AsciiStrCpyS (HIIStringPackageHdr->Language,
                (HeaderSize - OFFSET_OF(EFI_HII_STRING_PACKAGE_HDR,Language)) / sizeof (CHAR8),
                Language);
  Size += HeaderSize;

  for (UINTN i=0; i<StringsLen; i++)
  {
    EFI_HII_SIBT_STRING_UCS2_BLOCK* StrBlock = (EFI_HII_SIBT_STRING_UCS2_BLOCK*)&Ptr[Size];
    StrBlock->Header.BlockType = EFI_HII_SIBT_STRING_UCS2;
    StrCpyS(StrBlock->StringText, StrLen(Strings[i])+1, Strings[i]);
    Size += sizeof(EFI_HII_SIBT_STRING_UCS2_BLOCK) + StrLen(Strings[i])*2;  
  }

  EFI_HII_SIBT_END_BLOCK* EndBlock = (EFI_HII_SIBT_END_BLOCK*)&Ptr[Size];
  EndBlock->Header.BlockType = EFI_HII_SIBT_END;
  Size += sizeof(EFI_HII_SIBT_END_BLOCK);

  HIIStringPackageHdr->Header.Length = Size;

  return Size;
}

위 함수는 다음과 같은 방식으로 호출할 수 있다.

  CHAR16* EnStrings[] = {
    L"English",
    L"Hello",
  };
  CHAR8* Data = (CHAR8*) AllocateZeroPool(200);
  UINT32 PackageSize = InitStringPackage(Data, "en-US", EnStrings, sizeof(EnStrings)/sizeof(EnStrings[0]));

여기서 데이터 배열에 크기 200을 사용하여 패키지에 필요한 크기보다 훨씬 더 큰 크기를 사용하고 있다. 원래는 배열 크기에 대한 계산을 수행해야 하지만 이번 장은 현재도 충분히 어려우므로 지금은 그대로 유지한다.

이제 문자열 패키지를 생성하는 InitStringPackage 함수가 있으므로 코드에서 패키지 데이터를 채울 수 있다.

다음은 전체 패키지 목록 데이터 생성 코드이다.

  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);

  CHAR16* EnStrings[] = {
    L"English",
    L"Hello",
  };
  offset += InitStringPackage(&Data[offset], "en-US", EnStrings, sizeof(EnStrings)/sizeof(EnStrings[0]));

  CHAR16* FrStrings[] = {
    L"French",
    L"Bonjour",
  };
  offset += InitStringPackage(&Data[offset], "fr-FR", FrStrings, sizeof(FrStrings)/sizeof(FrStrings[0]));

  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);
Previous45. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 문자열 목록 게시Next47. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 HII 패키지 목록 게시

Last updated 2 years ago

🖥️
String Package 구조
UCS2 String Block 구조