💻
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 값 수정
  • PCD 패치
  • GenPatchPcdTable Tool
  • PatchPcdValue Tool
  1. UEFI 개발
  2. PCD

23. PatchableInModule PCD 및 GenPatchPcdTable/PatchPcdValue 유틸리티를 통해 PCD를 변경하는 방법

PatchableInModule PCD class에 대해서 알아보자.

[PcdsPatchableInModule] 섹션에서 DEC(UefiLessonsPkg.dec) 파일에 새 PCD를 추가한다.

[PcdsPatchableInModule]
  gUefiLessonsPkgTokenSpaceGuid.PcdPatchableInt32|0x31313131|UINT32|0xFCDA11B5

INF(PCDLessons.inf)에 마찬가지로 추가해준다.

[PatchPcd]
  gUefiLessonsPkgTokenSpaceGuid.PcdPatchableInt32

PCDLesson.c 파일에서 PCD 값을 출력해내려면, PatchPcdGet 또는 Generic PcdGet을 사용해야 한다.

두 방법 모두 테스트 해보자.

Print(L"PcdPatchableInt32=0x%x\n", PatchPcdGet32(PcdPatchableInt32));
Print(L"PcdPatchableInt32=0x%x\n", PcdGet32(PcdPatchableInt32));
FS0:\> PCDLesson.efi
...
PcdPatchableInt32=0x31313131
PcdPatchableInt32=0x31313131

빌드 이후 AutoGen 파일은 각각 다음과 같다.

$ 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)
$ 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;
#define PatchPcdGet32(TokenName)  _gPcd_BinaryPatch_##TokenName
...
#define PcdGet32(TokenName)  _PCD_GET_MODE_32_##TokenName

전처리기 코드를 풀면, 두 호출이 동일한 변수로 해독되는 것을 확인할 수 있다.

PatchPcdGet32(PcdPatchableInt32) -> _gPcd_BinaryPatch_PcdPatchableInt32

PcdGet32(PcdPatchableInt32) -> _PCD_GET_MODE_32_PcdPatchableInt32 ->  _gPcd_BinaryPatch_PcdPatchableInt32

아래는 AutoGen.c에서 할당되는 volatile 변수이다.

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 중 하나를 사용할 수 있다.

#define PatchPcdSet32(TokenName, Value)  (_gPcd_BinaryPatch_##TokenName = (Value))
...
#define PcdSet32S(TokenName, Value)         _PCD_SET_MODE_32_S_##TokenName    ((Value))

PcdSet32S는 값을 반환하는 매크로로 해독된다는 것을 기억하고 있도록 하자. 예를 들어,

PcdSet32S(PcdPatchableInt32, 44) -->  _PCD_SET_MODE_32_S_PcdPatchableInt32 ((44)) --> ((_gPcd_BinaryPatch_PcdPatchableInt32 = (44)), RETURN_SUCCESS)

따라서 다음과 같은 오류를 원하지 않는 경우에는,

/<...>/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)
      |                                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~.

아래 코드와 함께 사용해야 한다.

EFI_STATUS Status = PcdSet32S(PcdPatchableInt32, 44);
Print(L"Status=%r\n", Status);

PcdSet32S와 다르게 PatchPcdSet32 API 함수는 이러한 것이 필요치 않으므로, 다음처럼 간단하게 사용할 수 있다.

PatchPcdSet32(PcdPatchableInt32, 43);

PCDLesson.c 애플리케이션 코드에서 두 SET 메소드를 모두 테스트한다.

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

위의 코드를 올바르게 빌드하고 실행하면, 다음의 결과가 표시된다.

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 값을 패치하는 데 사용된다.

GenPatchPcdTable Tool

GenPatchPcdTable부터 시작해보겠다. 먼저 이 도구에 대한 도움말을 확인해보자.

$ ./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 파일의 경우, 다음 중 하나를 사용할 수 있다.

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 파일의 경우, 다음 중 하나를 사용할 수 있다.

Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.map
Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/OUTPUT/PCDLesson.map

맵 파일에서 PCD를 어떻게 찾을 수 있을지 궁금한 경우, 다음 코드를 사용하면 PCD를 찾을 수 있다.

$ 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을 생성해보자.

./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 파일을 체크하면 다음과 같다.

$ cat Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLessonPatchPcdTable
PCD Name                       Offset    Section Name
PcdPatchableInt32              0x5380     .data

보다시피, 맵 파일에서 본 것과 동일한 오프셋을 가진다.

이제 *.efl 파일의 기본 PCD 값을 마지막으로 살펴보겠다. (오프셋 값에 주의하도록 하자.)

hexdump -s 0x5380 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.efi -n 4
0005380 3131 3131
00055a4

PatchPcdValue Tool

PatchPcdValue 툴에 대한 도움말은 다음과 같다.

$ ./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를 패치해본다.

./BaseTools/BinWrappers/PosixLike/PatchPcdValue \
  --offset=0x5380 \
  --value=0xDEADDEAD \
  --type=UINT32 \
  Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/PCDLesson.efi

hexdump 값을 다시 찾아보면,

$ 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에서 앱을 실행해보자.

FS0:\> PCDLesson.efi
...
PcdPatchableInt32=0xDEADDEAD
PcdPatchableInt32=0xDEADDEAD
PcdPatchableInt32=43
Status=Success
PcdPatchableInt32=44
Previous22. Feature Flag PCD와 BOOLEAN FixedAtBuild PCD의 비교Next24. Dynamic/DynamiEx PCDs

Last updated 2 years ago

따르면,

자세한 사항은 PcdLib.h를 참고하자.

EDKII에서 이러한 유틸리티에 대한 매뉴얼을 제공하고 있다.

🖥️
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h
https://github.com/tianocore/edk2/blob/master/BaseTools/UserManuals/PatchPcdValue_Utility_Man_Page.rtf