💻
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
  • 다른 간단한 PCD 유형
  • 값을 초기화 할 때 사용할 수 있는 표현
  • VOID* PCD 데이터 유형
  • Byte 배열 PCD
  • GUID PCD
  • DEVICE_PATH
  • 정수 캐스트 사용
  • LABEL과 OFFSET_OF
  • 결합 배열 초기화
  • 사용자 지정 타입
  • 고정 크기의 배열
  • PcdValueInit
  1. UEFI 개발
  2. PCD

20. PCD 소개

PreviousPCDNext21. PCD 변수에 대한 Overriding

Last updated 2 years ago

PCD(Platform Configuration Database)는 드라이버 또는 애플리케이션이 액세스할 수 있는 다양한 현재 플랫폼 설정 또는 지침이 포함된 데이터베이스이다.

PCD에 대한 자세한 설명은 EDKII 스펙을 통해 확인할 수 있다. - -

PCD entry는 PCD라고도 불린다. 그래서 여기서도 그냥 PCD라고 부른다.

PCD entry는 DEC 파일에 정의된다.

<TokenSpaceGuidCName> 은 GUID 값이고, <Token>은 32bit 값이다. 이것들은 PCD를 식별하는 데 사용된다.

<TokenSpaceGuidCName>.<PcdCName>|<DefaultValue>|<DatumType>|<Token>

먼저 모든 PCD를 포함하는 토큰 공간을 선언한다. 일반적으로 g<PakageName>TokenSpaceGuid로 정의되기 때문에 UefiLessonsPkg/UefiLessonsPkg.dec에 추가해야 한다.

[Guids]
  ...
  gUefiLessonsPkgTokenSpaceGuid = {0x150cab53, 0xad47, 0x4385, {0xb5, 0xdd, 0xbc, 0xfc, 0x76, 0xba, 0xca, 0xf0}}

<Token> 값의 경우, 일반적으로 패키지 작성자는 0x00000001부터 순차적으로 쓰기 시작한다. 또한 일부 PCD가 하나의 논리 그룹에 속한다는 것을 나타내기 위해 종종 토큰 번호를 사용하여 이를 나타낼 수 있다. 예를 들어 토큰 그룹은 0x1XXXXXXX, 0x2XXXXXXX 등이 될 수 있다.

패키지가 발전함에 따라 일부 PCD가 추가되고 일부 PCD가 제거된다. 만약 순차적인 번호를 사용한다면 이것은 어려움을 줄 수 있다. 예를 들어 토큰이 0x0000000A 및 0x0000000B인 PCD가 있는 경우 어려움이 발생할 수 있으며 새 PCD를 넣는 가장 논리적인 방법은 0x0000000A인 PCD 이후이다. 물론 PCD 토큰을 0x0000000C에 할당하여 실행할 수도 있지만, 그렇다면 순차적인 번호 지정의 의미를 알아야 한다.

순차적인 번호 지정은 힘들기 때문에 아래 스크립트를 생성했다. 아래 스크립트는 임의의 4바이트 토큰 번호를 생성하는 스크립트다.

#!/bin/bash

##
#  This is a simple script that generates a random 4-byte hex value for a PCD Token
##

hexdump -vn4 -e'"0x%08X\n"' /dev/urandom

스크립트를 사용하면 아래와 같이 4바이트의 토큰 번호를 생성할 수 있다.

$ ./scripts/genToken.sh
0x3B81CDF1

이제 gUefiLessonsPkgTokenSpaceGuid를 정의하는 데 사용한 *.dec 파일에 PCD를 정의할 수 있다. PCD를 UINT8 PcdInt8 = 0x88 로 시작한다.

[PcdsFixedAtBuild]
  gUefiLessonsPkgTokenSpaceGuid.PcdInt8|0x88|UINT8|0x3B81CDF1

PCDLesson 애플리케이션을 생성하기 위해 UefiLessons/PCDLesson/PCDLesson.c 파일에 아래 코드를 추가한다.

#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/PcdLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  Print(L"PcdInt8=0x%x\n", FixedPcdGet8(PcdInt8));

  return EFI_SUCCESS;
}

FixedPcdGet8을 사용하기 위해서는 헤더 파일이 필요하다. PcdLib 헤더를 추가한다.

#include <Library/PcdLib.h>
#define FixedPcdGet8(TokenName)            _PCD_VALUE_##TokenName

아직은 빌드를 시도하면 애플리케이션이 정의되어 있지 않기 때문에 에러가 발생한다.

/home/kostr/tiano/edk2/MdePkg/Include/Library/PcdLib.h:97:45: error: ‘_PCD_VALUE_PcdInt8’ undeclared (first use in this function)
   97 | #define FixedPcdGet8(TokenName)            _PCD_VALUE_##TokenName
      |                                             ^~~~~~~~~~~

에러를 해결하기 위해서는 아직 생성하지 않았던 *.inf 파일을 생성해야 한다. 해당 파일은 HelloWorld 애플리케이션에서 사용했던 것을 복사하여 수정 해야 할 부분을 수정한 후 PCD 애플리케이션을 추가한다.

[FixedPcd]
  gUefiLessonsPkgTokenSpaceGuid.PcdInt8

추가적으로 PCD를 정의하는 dec 파일을 포함하도록 한 후 빌드를 시도하면 잘 되는 것을 볼 수 있다.

[Packages]
  ...
  UefiLessonsPkg/UefiLessonsPkg.dec

자동 생성된 파일 AutoGen.h/AutoGen.c의 내용을 확인하면 PCD가 있는 것을 확인할 수 있다.

  • Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h

// Definition of PCDs used in this module

#define _PCD_TOKEN_PcdInt8  0U
#define _PCD_SIZE_PcdInt8 1
#define _PCD_GET_MODE_SIZE_PcdInt8  _PCD_SIZE_PcdInt8
#define _PCD_VALUE_PcdInt8  0x88U
extern const  UINT8  _gPcd_FixedAtBuild_PcdInt8;
#define _PCD_GET_MODE_8_PcdInt8  _gPcd_FixedAtBuild_PcdInt8
//#define _PCD_SET_MODE_8_PcdInt8  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD
  • Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.c

// Definition of PCDs used in this module
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdInt8 = _PCD_VALUE_PcdInt8;

따라서 프리프로세서는 아래와 같이 코드를 확장한다.

FixedPcdGet8(PcdInt8) -> _PCD_VALUE_PcdInt8 -> 0x88U

OVMF를 통해 PCDLesson 애플리케이션을 실행하면 올바른 값이 출력되는 것을 볼 수 있다.

FS0:\> PCDLesson.efi
PcdInt8=0x88

PCD에는 여러 유형이 있고 FixedAtBuild PCD는 그 중 하나이다. 코드에서는 FixedPcdGet8 호출을 사용하여 PCD 값을 얻었으며, 이 호출은 PCD가 FixedAtBuild인 경우에만 할 수 있다. FixedAtBuild와는 달리 PCD 유형에 관계없이 PCD 값을 얻는 데 사용할 수 있는 일반적인 PcdGet8 호출이 있다.

#define PcdGet8(TokenName)                 _PCD_GET_MODE_8_##TokenName

PcdGet8을 사용하면 아래와 같이 확장된다.

PcdGet8(PcdInt8) -> _PCD_GET_MODE_8_PcdInt8 -> _gPcd_FixedAtBuild_PcdInt8

AutoGen.c에서는 아래와 같이 정의되어 있다.

 GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdInt8 = _PCD_VALUE_PcdInt8   // =0x88U;

PCDLesson.c에 아래와 같이 PcdGet8과 FixedAtBuild를 비교할 수 있도록 수정한다.

Print(L"PcdInt8=%d\n", FixedPcdGet8(PcdInt8));
Print(L"PcdInt8=%d\n", PcdGet8(PcdInt8));

빌드 후 결과를 확인하면 동일한 것을 볼 수 있다. 다만, 차이점은 PcdGet8은 다른 PCD 유형과 함께 사용할 수 있다는 것이다.

FS0:\> PCDLesson.efi
PcdInt8=0x88
PcdInt8=0x88

다른 간단한 PCD 유형

위의 예제에서는 UINT8을 PCD의 <DatumType>로 사용했다. 이러한 유형과 함께 EDKII를 사용하면 다른 정수 데이터 유형 UINT16, UINT32 또는 UINT64 정수 데이터 유형 및 BOOLEAN 유형을 사용할 수 있다.

이러한 PCD를 UefiLessonsPkg/UefiLessonsPkg.dec에 추가한다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdInt16|0x1616|UINT16|0x77DFB6E6
  gUefiLessonsPkgTokenSpaceGuid.PcdInt32|0x32323232|UINT32|0xF2A48130
  gUefiLessonsPkgTokenSpaceGuid.PcdInt64|0x6464646464646464|UINT64|0x652F4E29
  gUefiLessonsPkgTokenSpaceGuid.PcdBool|TRUE|BOOLEAN|0x69E88A63

또한, UefiLessonsPkg/PCDLesson/PCDLesson.inf 모듈에 포함하여야 한다.

[FixedPcd]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdInt16
  gUefiLessonsPkgTokenSpaceGuid.PcdInt32
  gUefiLessonsPkgTokenSpaceGuid.PcdInt64
  gUefiLessonsPkgTokenSpaceGuid.PcdBool

빌드를 진행하면 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h에 채워지는 것을 볼 수 있다.

#define _PCD_TOKEN_PcdInt16  0U
#define _PCD_SIZE_PcdInt16 2
#define _PCD_GET_MODE_SIZE_PcdInt16  _PCD_SIZE_PcdInt16
#define _PCD_VALUE_PcdInt16  0x1616U
extern const  UINT16  _gPcd_FixedAtBuild_PcdInt16;
#define _PCD_GET_MODE_16_PcdInt16  _gPcd_FixedAtBuild_PcdInt16
//#define _PCD_SET_MODE_16_PcdInt16  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

#define _PCD_TOKEN_PcdInt32  0U
#define _PCD_SIZE_PcdInt32 4
#define _PCD_GET_MODE_SIZE_PcdInt32  _PCD_SIZE_PcdInt32
#define _PCD_VALUE_PcdInt32  0x32323232U
extern const  UINT32  _gPcd_FixedAtBuild_PcdInt32;
#define _PCD_GET_MODE_32_PcdInt32  _gPcd_FixedAtBuild_PcdInt32
//#define _PCD_SET_MODE_32_PcdInt32  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

#define _PCD_TOKEN_PcdInt64  0U
#define _PCD_SIZE_PcdInt64 8
#define _PCD_GET_MODE_SIZE_PcdInt64  _PCD_SIZE_PcdInt64
#define _PCD_VALUE_PcdInt64  0x6464646464646464ULL
extern const  UINT64  _gPcd_FixedAtBuild_PcdInt64;
#define _PCD_GET_MODE_64_PcdInt64  _gPcd_FixedAtBuild_PcdInt64
//#define _PCD_SET_MODE_64_PcdInt64  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

#define _PCD_TOKEN_PcdBool  0U
#define _PCD_SIZE_PcdBool 1
#define _PCD_GET_MODE_SIZE_PcdBool  _PCD_SIZE_PcdBool
#define _PCD_VALUE_PcdBool  1U
extern const  BOOLEAN  _gPcd_FixedAtBuild_PcdBool;
#define _PCD_GET_MODE_BOOL_PcdBool  _gPcd_FixedAtBuild_PcdBool
//#define _PCD_SET_MODE_BOOL_PcdBool  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.c에서도 확인이 가능하다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINT16 _gPcd_FixedAtBuild_PcdInt16 = _PCD_VALUE_PcdInt16;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT32 _gPcd_FixedAtBuild_PcdInt32 = _PCD_VALUE_PcdInt32;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT64 _gPcd_FixedAtBuild_PcdInt64 = _PCD_VALUE_PcdInt64;
GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdBool = _PCD_VALUE_PcdBool;

선언된 내용 살펴보면 처음에 사용했던 UINT8와 모두 비슷한 것을 알 수 있다.

이전과 같이 FixedPcdGet과 PcdGet API를 사용하여 PCD 값을 확인한다.

Print(L"PcdInt16=0x%x\n", FixedPcdGet16(PcdInt16));
Print(L"PcdInt32=0x%x\n", FixedPcdGet32(PcdInt32));
Print(L"PcdInt64=0x%x\n", FixedPcdGet64(PcdInt64));
Print(L"PcdBool=0x%x\n", FixedPcdGetBool(PcdBool));
Print(L"PcdInt16=0x%x\n", PcdGet16(PcdInt16));
Print(L"PcdInt32=0x%x\n", PcdGet32(PcdInt32));
Print(L"PcdInt64=0x%x\n", PcdGet64(PcdInt64));
Print(L"PcdIntBool=0x%x\n", PcdGetBool(PcdBool));

출력된 결과를 보면 값이 동일한 것을 볼 수 있다.

FS0:\> PCDLesson.efi
...
PcdInt16=0x1616
PcdInt32=0x32323232
PcdInt64=0x64646464
PcdBool=0x1
PcdInt16=0x1616
PcdInt32=0x32323232
PcdInt64=0x64646464
PcdIntBool=0x1

값을 초기화 할 때 사용할 수 있는 표현

값을 초기화 할 때 식을 사용할 수 있다. 연산에서 |를 사용할 때는 괄호(...) 안에 식을 넣어야 한다. 예시는 아래와 같다. UefiLessonsPkg/UefiLessonsPkg.dec에 아래와 같이 입력한다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdExpression|0xFF000000 + 0x00FFFFFF|UINT32|0x9C405222
  gUefiLessonsPkgTokenSpaceGuid.PcdExpression_1|((0xFFFFFFFF & 0x000000FF) << 8) + 0x33|UINT32|0x5911C44B
  gUefiLessonsPkgTokenSpaceGuid.PcdExpression_2|(0x00000000 | 0x00100000)|UINT32|0xAD880207
  gUefiLessonsPkgTokenSpaceGuid.PcdExpression_3|((56 < 78) || !(23 > 44))|BOOLEAN|0x45EDE955

*.dec 파일에 추가하였으니 *.inf 파일에도 추가해야 한다. 여기서는 UefiLessonsPkg/PCDLesson/PCDLesson.inf에 추가한다.

[FixedPcd]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdExpression
  gUefiLessonsPkgTokenSpaceGuid.PcdExpression_1
  gUefiLessonsPkgTokenSpaceGuid.PcdExpression_2
  gUefiLessonsPkgTokenSpaceGuid.PcdExpression_3

빌드 후 Autogen.h 을 확인하면 값이 계산되어 들어간 것을 볼 수 있다.

...
#define _PCD_VALUE_PcdExpression  4294967295U		// =0xffffffff
...
#define _PCD_VALUE_PcdExpression_1  65331U              // =0xff33
...
#define _PCD_VALUE_PcdExpression_2  1048576U            // =0x100000
...
#define _PCD_VALUE_PcdExpression_3  1U
...

VOID* PCD 데이터 유형

EDK2에서는 UINT8, UINT16, UINT32, UINT64, BOOLEAN 외에도 VOID* 유형을 사용할 수 있다.

문자열 PCD

아래와 같은 PCD를 UefiLessonsPkg/UefiLessonsPkg.dec에 추가한다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdAsciiStr|"hello"|VOID*|0xB29914B5
  gUefiLessonsPkgTokenSpaceGuid.PcdUCS2Str|L"hello"|VOID*|0xF22124E5

첫 번째와 두번째는 데이터를 표현에 차이가 있다. 첫 번째는 "..."로 되어있고 두 번째는 L"..."로 되어있다.

이제 PCD를 UefiLessonsPkg/PCDLesson/PCDLesson.inf에 추가한다.

[FixedPcd]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdAsciiStr
  gUefiLessonsPkgTokenSpaceGuid.PcdUCS2Str

빌드 후 AutoGen.h 파일을 살펴보면 아래와 같다.

#define _PCD_TOKEN_PcdAsciiStr  0U
#define _PCD_VALUE_PcdAsciiStr  _gPcd_FixedAtBuild_PcdAsciiStr
extern const UINT8 _gPcd_FixedAtBuild_PcdAsciiStr[6];
#define _PCD_GET_MODE_PTR_PcdAsciiStr  _gPcd_FixedAtBuild_PcdAsciiStr
#define _PCD_SIZE_PcdAsciiStr 6
#define _PCD_GET_MODE_SIZE_PcdAsciiStr  _PCD_SIZE_PcdAsciiStr
//#define _PCD_SET_MODE_PTR_PcdAsciiStr  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

#define _PCD_TOKEN_PcdUCS2Str  0U
#define _PCD_VALUE_PcdUCS2Str  _gPcd_FixedAtBuild_PcdUCS2Str
extern const UINT16 _gPcd_FixedAtBuild_PcdUCS2Str[6];
#define _PCD_GET_MODE_PTR_PcdUCS2Str  _gPcd_FixedAtBuild_PcdUCS2Str
#define _PCD_SIZE_PcdUCS2Str 12
#define _PCD_GET_MODE_SIZE_PcdUCS2Str  _PCD_SIZE_PcdUCS2Str
//#define _PCD_SET_MODE_PTR_PcdUCS2Str  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

Autogen.c파일은 아래와 같다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdAsciiStr[6] = {104, 101, 108, 108, 111, 0 };
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdAsciiStr = 6;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT16 _gPcd_FixedAtBuild_PcdUCS2Str[6] = {104, 101, 108, 108, 111, 0 };
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdUCS2Str = 12;

위 파일들을 보면 알 수 있듯이 PcdAsciiStr 배열은 UINT8로 구성됐지만 PcdUCS2Str 배열은 UINT16으로 구성되어 있다.

또 다른 차이점은 문자열 크기에 대한 상수(sizeof()의 값)가 생성된다는 것이다. 이러한 값은 빌드 시스템에 의해 동적으로 계산된다.

VOID*타입인 PCD 값을 가져오려면 FixedPcdGetPtr이나 PcdGetPtrAPI를 사용하고 바이트 단위로 가져오려면 FixedPcdGetSize나 PcdGetSize API를 사용한다.

Print(L"PcdAsciiStr=%a\n", FixedPcdGetPtr(PcdAsciiStr));
Print(L"PcdAsciiStrSize=%d\n", FixedPcdGetSize(PcdAsciiStr));
Print(L"PcdUCS2Str=%s\n", PcdGetPtr(PcdUCS2Str));
Print(L"PcdUCS2StrSize=%d\n", PcdGetSize(PcdUCS2Str));

빌드 후 PCDLesson.efi를 실행하면 아래와 같이 문자열과 크기를 확인할 수 있다.

FS0:\> PCDLesson.efi
...
PcdAsciiStr=hello
PcdAsciiStrSize=6
PcdUCS2Str=hello
PcdUCS2StrSize=12
#define FixedPcdGetPtr(TokenName)  ((VOID *)_PCD_VALUE_##TokenName)
#define FixedPcdGetSize(TokenName)  _PCD_SIZE_##TokenName
#define PcdGetPtr(TokenName)  _PCD_GET_MODE_PTR_##TokenName
#define PcdGetSize(TokenName)  _PCD_GET_MODE_SIZE_##TokenName

이 예제에서는 문자열을 초기화 할 때 "..."과 L"..."을 사용했지만 '...'이나 L'...'을 사용해도 된다.

Byte 배열 PCD

VOID*는 일반적으로 바이트 배열에 사용된다.

예시로 보기 위해 PCD를 UefiLessonsPkg/UefiLessonsPkg.dec에 추가한다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdArray|{0xA5, 0xA6, 0xA7}|VOID*|0xD5DB9A27

UefiLessonsPkg/PCDLesson/PCDLesson.inf에도 추가한다.

[FixedPcd]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdArray

AutoGen 파일을 보면 PCD 배열이 추가된 것을 볼 수 있다.

  • Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h

#define _PCD_TOKEN_PcdArray  0U
#define _PCD_VALUE_PcdArray  (VOID *)_gPcd_FixedAtBuild_PcdArray
extern const UINT8 _gPcd_FixedAtBuild_PcdArray[3];
#define _PCD_GET_MODE_PTR_PcdArray  (VOID *)_gPcd_FixedAtBuild_PcdArray
#define _PCD_SIZE_PcdArray 3
#define _PCD_GET_MODE_SIZE_PcdArray  _PCD_SIZE_PcdArray
//#define _PCD_SET_MODE_PTR_PcdArray  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD
  • Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.c

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArray[3] = {0xA5, 0xA6, 0xA7};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArray = 3;

배열 요소를 출력하기 위해 아래 코드를 PCDLesson.c에 추가한다. AutoGen.h에서 데이터가 (VOID *)로 캐스트 될 때 (UINT8*) 캐스트가 필요하다.

for (UINTN i=0; i<FixedPcdGetSize(PcdArray); i++) {
  Print(L"PcdArray[%d]=0x%02x\n", i, ((UINT8*)FixedPcdGetPtr(PcdArray))[i]);
}

빌드 후 PCDLesson.efi를 실행하면 아래와 같이 배열이 출력되는 것을 볼 수 있다.

FS0:\> PCDLesson.efi
...
PcdArray[0]=0xA5
PcdArray[1]=0xA6
PcdArray[2]=0xA7

GUID PCD

바이트 배열 초기화 구문을 사용하면 사용자 지정 구조의 유형을 이해하는 즉시 모든 사용자 지정 구조를 초기화할 수 있다. EFI_GUID(=GUID) 구조를 살펴본다.

typedef struct {
  UINT32  Data1;
  UINT16  Data2;
  UINT16  Data3;
  UINT8   Data4[8];
} GUID;

typedef GUID EFI_GUID;

만약 GUID가 "f1740707-691d-4203-bfab-99e132fa4166"인 것을 인코딩 하려면 아래와 같이 하게 된다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdGuidInBytes|{0x07, 0x07, 0x74, 0xF1, 0x1D, 0x69, 0x03, 0x42, 0xBF, 0xAB, 0x99, 0xE1, 0x32, 0xFA, 0x41, 0x66}|VOID*|0xB9E0CDC0

따라서 정확한 값을 얻기 위해서는 일부 바이트를 재구성해야 한다. x86은 little-endian이기 때문에 필요하며, 이러한 시스템에서 숫자의 바이트는 상위에서 하위로 메모리에 배치되기 때문이다.

그렇기 때문에 사용자 지정 구조에 대해 바이트 배열 초기화를 인코딩할 때 이점을 염두해야 한다.

UEFI 코드에서는 GUID가 과도하게 사용되기 때문에 EDKII는 GUID PCD 초기화를 위한 도우미 구문을 가지고 있다. 도우미 구문을 사용하는 방법은 아래와 같다. 도우미 구문을 사용하여 PCD를 정의한다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdGuid|{GUID("f1740707-691d-4203-bfab-99e132fa4166")}|VOID*|0x7F2066F7

도우미를 사용하지 않을 때와 사용할 때를 비교하기 위해 UefiLessonsPkg/UefiLessonsPkg.dec에 아래 두 줄을 입력한다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdGuidInBytes|{0x07, 0x07, 0x74, 0xF1, 0x1D, 0x69, 0x03, 0x42, 0xBF, 0xAB, 0x99, 0xE1, 0x32, 0xFA, 0x41, 0x66}|VOID*|0xB9E0CDC0
  gUefiLessonsPkgTokenSpaceGuid.PcdGuid|{GUID("f1740707-691d-4203-bfab-99e132fa4166")}|VOID*|0x7F2066F7

그리고 UefiLessonsPkg/PCDLesson/PCDLesson.inf에도 추가한다.

[FixedPcd]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdGuidInBytes
  gUefiLessonsPkgTokenSpaceGuid.PcdGuid

빌드하고 AutoGen 파일들을 살펴보면 아래와 같이 추가된 것을 확인할 수 있다.

  • Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h

#define _PCD_TOKEN_PcdGuidInBytes  0U
#define _PCD_VALUE_PcdGuidInBytes  (VOID *)_gPcd_FixedAtBuild_PcdGuidInBytes
extern const UINT8 _gPcd_FixedAtBuild_PcdGuidInBytes[16];
#define _PCD_GET_MODE_PTR_PcdGuidInBytes  (VOID *)_gPcd_FixedAtBuild_PcdGuidInBytes
#define _PCD_SIZE_PcdGuidInBytes 16
#define _PCD_GET_MODE_SIZE_PcdGuidInBytes  _PCD_SIZE_PcdGuidInBytes
//#define _PCD_SET_MODE_PTR_PcdGuidInBytes  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

#define _PCD_TOKEN_PcdGuid  0U
#define _PCD_VALUE_PcdGuid  (VOID *)_gPcd_FixedAtBuild_PcdGuid
extern const UINT8 _gPcd_FixedAtBuild_PcdGuid[16];
#define _PCD_GET_MODE_PTR_PcdGuid  (VOID *)_gPcd_FixedAtBuild_PcdGuid
#define _PCD_SIZE_PcdGuid 16
#define _PCD_GET_MODE_SIZE_PcdGuid  _PCD_SIZE_PcdGuid
//#define _PCD_SET_MODE_PTR_PcdGuid  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD
  • Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.c

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdGuidInBytes[16] = {0x07, 0x07, 0x74, 0xF1, 0x1D, 0x69, 0x03, 0x42, 0xBF, 0xAB, 0x99, 0xE1, 0x32, 0xFA, 0x41, 0x66};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdGuidInBytes = 16;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdGuid[16] = {0x07, 0x07, 0x74, 0xF1, 0x1D, 0x69, 0x03, 0x42, 0xBF, 0xAB, 0x99, 0xE1, 0x32, 0xFA, 0x41, 0x66};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdGuid = 16;

파일들을 통해 알 수 있듯이 PCD 인코딩과 초기화는 두 경우 모두 동일하다.

코드에서 GUID를 사용하려면 (VOID *)에서 (EFI_GUID*)로 GUID를 캐스팅 해야 한다.

Print(L"PcdGuidInBytes=%g\n", *(EFI_GUID*)FixedPcdGetPtr(PcdGuidInBytes));
Print(L"PcdGuid=%g\n", *(EFI_GUID*)FixedPcdGetPtr(PcdGuid));

빌드 후 PCDLesson.efi를 실행하면 출력된 결과도 동일한 것을 확인할 수 있다.

FS0:\> PCDLesson.efi
...
PcdGuidInBytes=F1740707-691D-4203-BFAB-99E132FA4166
PcdGuid=F1740707-691D-4203-BFAB-99E132FA4166

GUID가 포함된 PCD를 초기화하는 방법에는 두 가지가 더 존재한다. 다른 GUID를 초기화에 사용하거나 표준 C 구문을 EFI_GUID 구조 초기화에 사용할 수 있다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdGuidByPCD|{GUID(gUefiLessonsPkgTokenSpaceGuid)}|VOID*|0x0860CCD5
  gUefiLessonsPkgTokenSpaceGuid.PcdGuidByEfiGuid|{GUID({0x150cab53, 0xad47, 0x4385, {0xb5, 0xdd, 0xbc, 0xfc, 0x76, 0xba, 0xca, 0xf0}})}|VOID*|0x613506D5

새롭게 빌드하고 AutoGen.c 파일을 통해 확인할 수 있다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdGuidByPCD[16] = {0x53, 0xAB, 0x0C, 0x15, 0x47, 0xAD, 0x85, 0x43, 0xB5, 0xDD, 0xBC, 0xFC, 0x76, 0xBA, 0xCA, 0xF0};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdGuidByPCD = 16;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdGuidByEfiGuid[16] = {0x53, 0xAB, 0x0C, 0x15, 0x47, 0xAD, 0x85, 0x43, 0xB5, 0xDD, 0xBC, 0xFC, 0x76, 0xBA, 0xCA, 0xF0};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdGuidByEfiGuid = 16;

DEVICE_PATH

GUID(...) 도우미 외에도 EDK2에는 DEVICE_PATH(...) 도우미가 있어 다른 특별한 UEFI 구조인 Device Path를 초기화할 수 있다.

예를 들어, UEFI Shell이 시작될 때 출력되는 경로는 UEFI Device Path이다.

UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
      FS0: Alias(s):HD0a1:;BLK1:
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
     BLK0: Alias(s):
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
     BLK2: Alias(s):
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
Press ESC in 5 seconds to skip startup.nsh or any other key to continue.

그 중 하나를 PCD 초기화에 사용한다. UefiLessonsPkg/UefiLessonsPkg.dec에 추가한다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdDevicePath|{DEVICE_PATH("PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)")}|VOID*|0xC56EE1E2

UefiLessonsPkg/PCDLesson/PCDLesson.inf에도 생성한 PCD를 추가한다.

[FixedPcd]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdDevicePath

빌드 후 AutoGen.c를 살펴보면 아래와 같이 배열이 생성된 것을 볼 수 있다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdDevicePath[30] = {0x02,0x01,0x0c,0x00,0xd0,0x41,0x03,0x0a,0x00,0x00,0x00,0x00,0x01,0x01,0x06,0x00,0x01,0x01,0x03,0x01,0x08,0x00,0x00,0x00,0x00,0x00,0x7f,0xff,0x04,0x00};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdDevicePath = 30;

배열이 올바른지 확인하기 위해 프로그램에서 생성된 Device Path를 출력한다. 이를 위해 DevicePathLib.h 헤더 파일을 추가해야 한다.

#include <Library/DevicePathLib.h>

그리고 ConvertDevicePathToText 함수를 사용한다.

Print(L"PcdDevicePath: %s\n", ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*) FixedPcdGetPtr(PcdDevicePath), FALSE, FALSE));

빌드 후 PCDLesson.efi를 실행하면 아래와 같은 출력을 볼 수 있다.

FS0:\> PCDLesson.efi
...
PcdDevicePath: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)

정수 캐스트 사용

UINT8(...), UINT16(...), UINT32(...), UINT64(...) 캐스트 도우미를 사용하여 VOID* 배열을 초기화할 수 있다.

UefiLessonsPkg/UefiLessonsPkg.dec에 아래와 같이 추가한다. 앞으로 inf에 추가하는 것은 더이상 얘기하지 않을 것이니 dec에 PCD가 추가된다면 *.inf에도 추가해야 한다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdIntCasts|{UINT16(0x1122), UINT32(0x33445566), UINT8(0x77), UINT64(0x8899aabbccddeeff)}|VOID*|0x647456A6

빌드 후 AutoGen.c를 살펴보면 little-endian 아키텍처와 관련하여 배열이 초기화되어 있음을 알 수 있다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdIntCasts[15] = {0x22, 0x11, 0x66, 0x55, 0x44, 0x33, 0x77, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdIntCasts = 15;

LABEL과 OFFSET_OF

일부 요소에 레이블을 할당하고 LABEL(...)과 OFFSET_OF(...) 구문을 통해 요소의 오프셋을 가져올 수 있다.

UefiLessonsPkg/UefiLessonsPkg.dec에 추가한다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdWithLabels|{ 0x0A, 0x0B, OFFSET_OF(End), 0x0C, LABEL(Start) 0x0D, LABEL(End) 0x0E, 0x0F, OFFSET_OF(Start) }|VOID*|0xD91A8BF6

빌드 후 AutoGen.c를 확인하면 아래와 같이 생성된다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdWithLabels[8] = {0x0A, 0x0B, 0x05, 0x0C, 0x0D, 0x0E, 0x0F, 0x04};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdWithLabels = 8;

결합 배열 초기화

다양한 바이트 배열 초기화 방법을 결합할 수 있다. UefiLessonsPkg/UefiLessonsPkg.dec에 추가한다.

gUefiLessonsPkgTokenSpaceGuid.PcdArrayExt|{0x11, UINT16(0x2233), UINT32(0x44556677), L"hello", "world!", GUID("09b9b358-70bd-421e-bafb-4f97e2ac7d44")}|VOID*|0x7200C5DF

빌드 후 AutoGen.c를 통해 확인할 수 있다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArrayExt[42] = {0x11, 0x33, 0x22, 0x77, 0x66, 0x55, 0x44, 0x68, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0    0, 0x58, 0xB3, 0xB9, 0x09, 0xBD, 0x70, 0x1E, 0x42, 0xBA, 0xFB, 0x4F, 0x97, 0xE2, 0xAC, 0x7D, 0x44};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayExt = 42;

사용자 지정 타입

PCD에 대한 사용자 지정 타입을 만들 수 있다.

예를 들어 아래 코드로 UefiLessonsPkg/Include/CustomPcdTypes.h 만들었다.

#ifndef CUSTOM_PCD_TYPES_H
#define CUSTOM_PCD_TYPES_H

typedef struct {
  EFI_GUID Guid;
  CHAR16 Name[6];
} InnerCustomStruct;

typedef struct {
  UINT8 Val8;
  UINT32 Val32[2];
  InnerCustomStruct ValStruct;
  union {
    struct {
      UINT8 Field1:1;
      UINT8 Field2:4;
      UINT8 Filed3:3;
    } BitFields;
    UINT8 Byte;
  } ValUnion;
} CustomStruct;

#endif

UefiLessonsPkg/UefiLessonsPkg.dec에 해당 파일을 포함하도록 한다.

[Includes]
  Include

생성된 CustomStructure 타입을 사용하고 이 구문을 통해 값을 초기화할 수 있다.

gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct|{0}|CustomStruct|0x535D4CB5 {
  <Packages>
    UefiLessonsPkg/UefiLessonsPkg.dec
  <HeaderFiles>
    CustomPcdTypes.h
}
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.Val8|0x11
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.Val32[0]|0x22334455
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.Val32[1]|0x66778899
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.ValStruct.Guid|{GUID("f1740707-691d-4203-bfab-99e132fa4166")}
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.ValStruct.Name|L'Hello'
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.ValUnion.BitFields.Field2|0xF

빌드 후 AutoGen.c를 확인하면 데이터가 생성된 것을 볼 수 있다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdCustomStruct[44] = {0x11,0x00,0x00,0x00,0x55,0x44,0x33,0x22,0x99,0x88,0x77,0x66,0x07,0x07,0x74,0xf1,0x1d,0x69,0x03,0x42,0xbf,0xab,0x99,0xe1,0x32,0xfa,0x41,0x66,0x48,0x00,0x65,0x00,0x6c,0x00,0x6c,0x00,0x6f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdCustomStruct = 44;

필드를 그룹화하면 모든 필드가 의도한 대로 초기화되었음을 알 수 있다.

{
  0x11,                                                                            // UINT8 Val8
  0x00,0x00,0x00,                                                                  // alignment
  0x55,0x44,0x33,0x22,                                                             // UINT32 Val32[0]
  0x99,0x88,0x77,0x66,                                                             // UINT32 Val32[1]
  0x07,0x07,0x74,0xf1,0x1d,0x69,0x03,0x42,0xbf,0xab,0x99,0xe1,0x32,0xfa,0x41,0x66, // InnerCustomStruct.Guid
  0x48,0x00,0x65,0x00,0x6c,0x00,0x6c,0x00,0x6f,0x00,0x00,0x00,                     // InnerCustomStruct.Name
  0x1e,                                                                            // ValUnion
  0x00,0x00,0x00                                                                   // alignment
}

필드별 파일 초기화가 너무 길어 보이면 특수 CODE(...) 구문을 사용하여 C 스타일 배열 초기화를 사용할 수 있다. 그러나 이 경우 GUID(...) 또는 L'...' 초기화와 같은 도우미를 사용할 수 없다. 따라서 구조에 대한 동일한 초기화는 아래와 같다.

gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct_1|{CODE(
  {
    0x11,
    {0x22334455, 0x66778899},
    {
      {0xf1740707, 0x691d, 0x4203, {0xbf, 0xab, 0x99, 0xe1, 0x32, 0xfa, 0x41, 0x66}},
      {0x0048, 0x0065, 0x006c, 0x006c, 0x006f, 0x0000}
    },
    {{0x0, 0xf, 0x0}}
  }
 )}|CustomStruct|0xC1D6B9A7 {
  <Packages>
    UefiLessonsPkg/UefiLessonsPkg.dec
  <HeaderFiles>
    CustomPcdTypes.h
}

빌드 후 AutoGen.c 파일을 통해 결과는 같은 것을 볼 수 있다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdCustomStruct_1[44] = {0x11,0x00,0x00,0x00,0x55,0x44,0x33,0x22,0x99,0x88,0x77,0x66,0x07,0x07,0x74,0xf1,0x1d,0x69,0x03,0x42,0xbf,0xab,0x99,0xe1,0x32,0xfa,0x41,0x66,0x48,0x00,0x65,0x00,0x6c,0x00,0x6c,0x00,0x6f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdCustomStruct_1 = 44;

고정 크기의 배열

PCD 배열의 크기를 구할 수 있다. UINT32[3]과 같이 크기가 1바이트를 넘는 유형의 배열을 사용하는 경우 모든 초기화 값을 유형에 캐스팅 해야 한다. 이 문제를 방지하기 위해 특수 CODE(...)를 사용할 수 있다.

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.PcdArrayWithFixedSize|{0x0}|UINT8[12]|0x4C4CB9A3
  gUefiLessonsPkgTokenSpaceGuid.PcdArrayWithFixedSize_1|{0x0}|UINT32[3]|0x285DAD21
  gUefiLessonsPkgTokenSpaceGuid.PcdArrayWithFixedSize_2|{UINT32(0x11223344), UINT32(0x55667788), UINT32(0x99aabbcc)}|UINT32[3]|0x25D6ED26
  gUefiLessonsPkgTokenSpaceGuid.PcdArrayWithFixedSize_3|{CODE({0x11223344, 0x55667788, 0x99aabbcc})}|UINT32[3]|0xE5BC424D

AutoGen.c를 확인하면 아래와 같이 추가된 것을 볼 수 있다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArrayWithFixedSize[8] = {0xee,0xff,0x00,0x00,0x00,0x00,0x00,0x00};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayWithFixedSize = 8;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArrayWithFixedSize_1[12] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayWithFixedSize_1 = 12;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArrayWithFixedSize_2[12] = {0x44,0x33,0x22,0x11,0x88,0x77,0x66,0x55,0xcc,0xbb,0xaa,0x99};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayWithFixedSize_2 = 12;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArrayWithFixedSize_3[12] = {0x44,0x33,0x22,0x11,0x88,0x77,0x66,0x55,0xcc,0xbb,0xaa,0x99};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayWithFixedSize_3 = 12;

사용자 정의 유형에 맞는 고정 크기 배열을 생성할 수도 있다.

gUefiLessonsPkgTokenSpaceGuid.PcdArrayWithFixedSize_4|{0x0}|CustomStruct[2]|0x0D00EE44 {
  <Packages>
    UefiLessonsPkg/UefiLessonsPkg.dec
  <HeaderFiles>
    CustomPcdTypes.h
}

여기서는 필드 초기화를 수행하지 않지만 구문 증명을 위해 AutoGen.c를 보고 최종 데이터 배열의 크기가 일반적인 구조의 두 배인지 확인할 수 있다.

GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayWithFixedSize_4 = 88;

PcdValueInit

사용자 지정 유형 또는 고정 크기 배열과 함께 PCD를 사용할 경우 빌드 시스템은 빌드 폴더에 PcdValueInit 폴더를 생성한다. 이 폴더에는 빌드 시스템이 AutoGen.c 파일의 데이터를 가져오는 데 사용할 특별한 C 프로그램 PcdValueInit이 포함된다.

$ find ./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueCommon.c
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueInit.o
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueInit.c
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueInit
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/Input.txt
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueInit.d
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueCommon.d
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/Output.txt
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueCommon.o
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/Makefile

프로그램의 도움말 메시지를 확인할 수 있다.

$ ./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueInit -h
Usage: -i <input_file> -o <output_file>

optional arguments:
  -h, --help            Show this help message and exit
  -i INPUT_FILENAME, --input INPUT_FILENAME
                        PCD Database Input file name
  -o OUTPUT_FILENAME, --output OUTPUT_FILENAME
                        PCD Database Output file name

이를 통해 이 프로그램은 동일한 폴더의 input.txt 파일에서 output.txt 파일을 생성한다고 추측할 수 있다. 빌드 시스템 내부에 대해 궁금한 점이 있으면 *.txt 파일을 확인하거나 프로그램 소스를 확인한다.

이 링크()를 통해 FixedPcdGet8을 살펴보면 단순히 정의되어 있다는 것을 알 수 있다.

만약 프리프로세서를 대체하고 싶다면 PcdLib.h()를 사용하기 전에 했던 것처럼 할 수 있다.

🖥️
https://edk2-docs.gitbook.io/edk-ii-pcd-specification/
https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/edkii-platform-config-database-entries-paper.pdf
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h