# 20. PCD 소개

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

PCD에 대한 자세한 설명은 EDKII 스펙을 통해 확인할 수 있다.\
\- <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>

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` 파일에 아래 코드를 추가한다.

```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` 헤더를 추가한다.

```c
#include <Library/PcdLib.h>
```

이 링크(<https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h>)를 통해 `FixedPcdGet8`을 살펴보면 단순히 정의되어 있다는 것을 알 수 있다.

```c
#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`이나 `PcdGetPtr`API를 사용하고 바이트 단위로 가져오려면 `FixedPcdGetSize`나 `PcdGetSize` API를 사용한다.

```c
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
```

만약 프리프로세서를 대체하고 싶다면 PcdLib.h(<https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h>)를 사용하기 전에 했던 것처럼 할 수 있다.

```
#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를 캐스팅 해야 한다.

```c
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` 만들었다.

```c
#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` 파일을 확인하거나 프로그램 소스를 확인한다.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bob-mcd-team.gitbook.io/uefi/uefi-development/pcd/20.intro-pcd.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
