[PcdsPatchableInModule] 섹션에서 DEC(UefiLessonsPkg.dec) 파일에 새 PCD를 추가한다.
Copy [PcdsPatchableInModule]
gUefiLessonsPkgTokenSpaceGuid.PcdPatchableInt32|0x31313131|UINT32|0xFCDA11B5
INF(PCDLessons.inf)에 마찬가지로 추가해준다.
Copy [PatchPcd]
gUefiLessonsPkgTokenSpaceGuid.PcdPatchableInt32
PCDLesson.c 파일에서 PCD 값을 출력해내려면, PatchPcdGet 또는 Generic PcdGet을 사용해야 한다.
두 방법 모두 테스트 해보자.
Copy Print(L"PcdPatchableInt32=0x%x\n", PatchPcdGet32(PcdPatchableInt32));
Print(L"PcdPatchableInt32=0x%x\n", PcdGet32(PcdPatchableInt32));
Copy FS0:\> PCDLesson.efi
...
PcdPatchableInt32=0x31313131
PcdPatchableInt32=0x31313131
빌드 이후 AutoGen 파일은 각각 다음과 같다.
Copy $ cat Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h
---
#define _PCD_TOKEN_PcdPatchableInt32 0U
#define _PCD_PATCHABLE_VALUE_PcdPatchableInt32 ((UINT32)0x31313131U)
extern volatile UINT32 _gPcd_BinaryPatch_PcdPatchableInt32;
#define _PCD_GET_MODE_32_PcdPatchableInt32 _gPcd_BinaryPatch_PcdPatchableInt32
#define _PCD_PATCHABLE_PcdPatchableInt32_SIZE 4
#define _PCD_GET_MODE_SIZE_PcdPatchableInt32 _gPcd_BinaryPatch_Size_PcdPatchableInt32
extern UINTN _gPcd_BinaryPatch_Size_PcdPatchableInt32;
#define _PCD_SET_MODE_32_PcdPatchableInt32(Value) (_gPcd_BinaryPatch_PcdPatchableInt32 = (Value))
#define _PCD_SET_MODE_32_S_PcdPatchableInt32(Value) ((_gPcd_BinaryPatch_PcdPatchableInt32 = (Value)), RETURN_SUCCESS)
Copy $ cat Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.c
---
volatile UINT32 _gPcd_BinaryPatch_PcdPatchableInt32 = _PCD_PATCHABLE_VALUE_PcdPatchableInt32;
GLOBAL_REMOVE_IF_UNREFERENCED UINTN _gPcd_BinaryPatch_Size_PcdPatchableInt32 = 4;
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h 따르면,
Copy #define PatchPcdGet32(TokenName) _gPcd_BinaryPatch_##TokenName
...
#define PcdGet32(TokenName) _PCD_GET_MODE_32_##TokenName
전처리기 코드를 풀면, 두 호출이 동일한 변수로 해독되는 것을 확인할 수 있다.
Copy PatchPcdGet32(PcdPatchableInt32) -> _gPcd_BinaryPatch_PcdPatchableInt32
PcdGet32(PcdPatchableInt32) -> _PCD_GET_MODE_32_PcdPatchableInt32 -> _gPcd_BinaryPatch_PcdPatchableInt32
아래는 AutoGen.c에서 할당되는 volatile 변수이다.
Copy volatile UINT32 _gPcd_BinaryPatch_PcdPatchableInt32 = _PCD_PATCHABLE_VALUE_PcdPatchableInt32 // = ((UINT32)0x31313131U)
따라서, FixedAtBuild와 FeatureFlag PCD의 주요한 차이점은 volatile로 정의된 변수와 SET 함수가 차단되지 않는다는 것이다.
런타임 시 PCD 값 수정
지금부터는 PCD를 설정해보겠다. 우리가 알고 있는 바와 같이, 두 가지 방법이 있다.
PatchPcdSet<Type> 혹은 generic PcdSet<Type>S API 중 하나를 사용할 수 있다.
자세한 사항은 PcdLib.h를 참고하자. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h
Copy #define PatchPcdSet32(TokenName, Value) (_gPcd_BinaryPatch_##TokenName = (Value))
...
#define PcdSet32S(TokenName, Value) _PCD_SET_MODE_32_S_##TokenName ((Value))
PcdSet32S는 값을 반환하는 매크로로 해독된다는 것을 기억하고 있도록 하자. 예를 들어,
Copy PcdSet32S(PcdPatchableInt32, 44) --> _PCD_SET_MODE_32_S_PcdPatchableInt32 ((44)) --> ((_gPcd_BinaryPatch_PcdPatchableInt32 = (44)), RETURN_SUCCESS)
따라서 다음과 같은 오류를 원하지 않는 경우에는,
Copy /<...>/Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h:108:106: error: right-hand operand of comma expression has no effect [-Werror=unused-value]
108 | #define _PCD_SET_MODE_32_S_PcdPatchableInt32(Value) ((_gPcd_BinaryPatch_PcdPatchableInt32 = (Value)), RETURN_SUCCESS)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~.
아래 코드와 함께 사용해야 한다.
Copy EFI_STATUS Status = PcdSet32S(PcdPatchableInt32, 44);
Print(L"Status=%r\n", Status);
PcdSet32S와 다르게 PatchPcdSet32 API 함수는 이러한 것이 필요치 않으므로, 다음처럼 간단하게 사용할 수 있다.
Copy PatchPcdSet32(PcdPatchableInt32, 43);
PCDLesson.c 애플리케이션 코드에서 두 SET 메소드를 모두 테스트한다.
Copy Print(L"PcdPatchableInt32=0x%x\n", PatchPcdGet32(PcdPatchableInt32));
Print(L"PcdPatchableInt32=0x%x\n", PcdGet32(PcdPatchableInt32));
PatchPcdSet32(PcdPatchableInt32, 43);
Print(L"PcdPatchableInt32=%d\n", PatchPcdGet32(PcdPatchableInt32));
EFI_STATUS Status = PcdSet32S(PcdPatchableInt32, 44);
Print(L"Status=%r\n", Status);
Print(L"PcdPatchableInt32=%d\n", PatchPcdGet32(PcdPatchableInt32));
위의 코드를 올바르게 빌드하고 실행하면, 다음의 결과가 표시된다.
Copy FS0:\> PCDLesson.efi
...
PcdPatchableInt32=0x31313131
PcdPatchableInt32=0x31313131
PcdPatchableInt32=43
Status=Success
PcdPatchableInt32=44
PCD 패치
이 섹션에서는 PCD 기본값에 16진수(HEX)를 할당하거나 이 PCD 유형의 이름을 PatchableInModule로 지정한 이유에 대해 언급한다.
이 PCD 타입은 바이너리 PE/COFF 이미지(즉, 최종 *.efi 파일)에서 PCD의 값이 변경될 수 있기 때문에 그렇게 이름 붙였다고 말할 수 있다. 이를 위해 두 가지 유틸리티가 사용된다.
GenPatchPcdTable - 이 툴은 맵 파일을 파싱하여 EFI 이미지에 대한 패치 가능한 PCD 오프셋을 가져오는 데 사용된다.
PatchPcdValue - 이 툴은 실제로 PCD 값을 패치하는 데 사용된다.
EDKII에서 이러한 유틸리티에 대한 매뉴얼을 제공하고 있다.
https://github.com/tianocore/edk2/blob/master/BaseTools/UserManuals/PatchPcdValue_Utility_Man_Page.rtf
GenPatchPcdTable Tool
GenPatchPcdTable부터 시작해보겠다. 먼저 이 도구에 대한 도움말을 확인해보자.
Copy $ ./BaseTools/BinWrappers/PosixLike/GenPatchPcdTable -h
Usage: GenPatchPcdTable.py -m <MapFile> -e <EfiFile> -o <OutFile>
Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-m MAPFILE, --mapfile=MAPFILE
Absolute path of module map file.
-e EFIFILE, --efifile=EFIFILE
Absolute path of EFI binary file.
-o OUTFILE, --outputfile=OUTFILE
Absolute path of output file to store the got
patchable PCD table.
이제 PatchPcdTable를 만들어보겠다.
*.efi 파일의 경우, 다음 중 하나를 사용할 수 있다.
Copy Build/UefiLessonsPkg/RELEASE_GCC5/X64/PCDLesson.efi
Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/OUTPUT/PCDLesson.efi
Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.efi
*.map 파일의 경우, 다음 중 하나를 사용할 수 있다.
Copy Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.map
Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/OUTPUT/PCDLesson.map
맵 파일에서 PCD를 어떻게 찾을 수 있을지 궁금한 경우, 다음 코드를 사용하면 PCD를 찾을 수 있다.
Copy $ grep PatchableInt32 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.map -A2
.data._gPcd_BinaryPatch_PcdPatchableInt32
0x0000000000005380 0x4 /tmp/PCDLesson.dll.sOAh1G.ltrans0.ltrans.o
.data.rel.ro 0x0000000000005384 0x0 PCDLesson.obj (symbol from plugin)
보다시피 PCD의 기본값은 0x5380 오프셋 아래에 있다.(해당 값은 0x5380 값이 아닌 다른 값이 나올 수도 있다. 원문에서는 0x55A0을 기준으로 기술하고 있다.
이제 앱 빌드DEBUG 폴더에서 GenPatchPcdTable을 실행하여 PCDLessonPatchPcdTalbe을 생성해보자.
Copy ./BaseTools/BinWrappers/PosixLike/GenPatchPcdTable \
-m Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.map \
-e Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.efi \
-o Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLessonPatchPcdTable
생성된 PCDLessonPatchPcdTable 파일을 체크하면 다음과 같다.
Copy $ cat Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLessonPatchPcdTable
PCD Name Offset Section Name
PcdPatchableInt32 0x5380 .data
보다시피, 맵 파일에서 본 것과 동일한 오프셋을 가진다.
이제 *.efl 파일의 기본 PCD 값을 마지막으로 살펴보겠다. (오프셋 값에 주의하도록 하자.)
Copy hexdump -s 0x5380 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.efi -n 4
0005380 3131 3131
00055a4
PatchPcdValue Tool
PatchPcdValue 툴에 대한 도움말은 다음과 같다.
Copy $ ./BaseTools/BinWrappers/PosixLike/PatchPcdValue -h
Usage: PatchPcdValue.py -f Offset -u Value -t Type [-s MaxSize] <input_file>
Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
Options:
-f PCDOFFSET, --offset=PCDOFFSET
Start offset to the image is used to store PCD value.
-u PCDVALUE, --value=PCDVALUE
PCD value will be updated into the image.
-t PCDTYPENAME, --type=PCDTYPENAME
The name of PCD data type may be one of VOID*,BOOLEAN,
UINT8, UINT16, UINT32, UINT64.
-s PCDMAXSIZE, --maxsize=PCDMAXSIZE
Max size of data buffer is taken by PCD value.It must
be set when PCD type is VOID*.
-v, --verbose Run verbosely
-d LOGLEVEL, --debug=LOGLEVEL
Run with debug information
-q, --quiet Run quietly
-? show this help message and exit
--version show program's version number and exit
-h, --help show this help message and exit
이제, 이를 사용하여 *.elf 파일에서 PCD를 패치해본다.
Copy ./BaseTools/BinWrappers/PosixLike/PatchPcdValue \
--offset=0x5380 \
--value=0xDEADDEAD \
--type=UINT32 \
Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.efi
hexdump 값을 다시 찾아보면,
Copy $ hexdump -s 0x55A0 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.efi -n 4
0005380 dead dead
0005384
바이너리에서 PCD를 성공적으로 변경할 수 있었다.
이제 수정된 PCDLesson.efi 파일을 UEFI 공유 디스크에 복사하여 OVMF에서 앱을 실행해보자.
Copy FS0:\> PCDLesson.efi
...
PcdPatchableInt32=0xDEADDEAD
PcdPatchableInt32=0xDEADDEAD
PcdPatchableInt32=43
Status=Success
PcdPatchableInt32=44