💻
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
  • DisplayHIIByGuid
  • Formset: classguid
  • HIIStaticFormDriver
  • 기본 UEFI 메뉴에서 애플리케이션 확인하기
  1. UEFI 개발
  2. VFR

59. 간단한 폼 애플리케이션을 UEFI 드라이버 Form으로 변환하기

지금까지는 폼을 HII 데이터베이스에 입력한 것과 동일한 애플리케이션에 표시했다. 그러나 지금까지 한 방식은 표준 방법이 아니다. 일반적으로 UEFI 드라이버는 HII 폼을 작성하고 다른 애플리케이션은 이를 로드한다. 실제 비휘발성 설정을 가진 더 복잡한 form을 생성해야 하기 때문에 이 두 가지 기능을 분리해야 한다.

따라서 아래의 2가지를 진행한다.

  • HII Form UEFI 애플리케이션을 UEFI 드라이버 폼으로 변환

  • HII 데이터베이스에서 작성된 HII 폼을 로드할 수 있는 애플리케이션 생성

DisplayHIIByGuid

두 번째 작업부터 진행하기 위해 DisplayHIIByGuid 애플리케이션을 생성한다. UefiLessonsPkg/DisplayHIIByGuid/DisplayHIIByGuid.inf

[Defines]
  INF_VERSION                    = 1.25
  BASE_NAME                      = DisplayHIIByGuid
  FILE_GUID                      = 1597e1d0-7f62-4631-a166-703f03bd7223
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = ShellCEntryLib

[Sources]
  DisplayHIIByGuid.c

[Packages]
  MdePkg/MdePkg.dec

[LibraryClasses]
  UefiLib
  ShellCEntryLib

UefiLessonsPkg/DisplayHIIByGuid/DisplayHIIByGuid.c

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

INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  )
{
  return EFI_SUCCESS;
}

UefiLessonsPkg/UefiLessonsPkg.dsc

[Components]
  ...
  UefiLessonsPkg/DisplayHIIByGuid/DisplayHIIByGuid.inf

애플리케이션은 명령 인자에서 HII 패키지 목록 GUID를 받아 그 패키지에서 HII 폼을 표시할 것이다.

/**
  Convert a Null-terminated Unicode GUID string to a value of type
  EFI_GUID.
  ...
  If String is not aligned in a 16-bit boundary, then ASSERT().
  @param  String                   Pointer to a Null-terminated Unicode string.
  @param  Guid                     Pointer to the converted GUID.
  @retval RETURN_SUCCESS           Guid is translated from String.
  @retval RETURN_INVALID_PARAMETER If String is NULL.
                                   If Data is NULL.
  @retval RETURN_UNSUPPORTED       If String is not as the above format.
**/
RETURN_STATUS
EFIAPI
StrToGuid (
  IN  CONST CHAR16  *String,
  OUT GUID          *Guid
  );

아래와 같이 사용하며 DisplayHIIByGuid.c에 작성한다.

if (Argc != 2) {
  Print(L"Usage:\n");
  Print(L"  DisplayHIIByGuid <Package list GUID>\n");
  return EFI_INVALID_PARAMETER;
}

EFI_GUID PackageListGuid;
EFI_STATUS Status = StrToGuid(Argv[1], &PackageListGuid);
if (Status != RETURN_SUCCESS) {
  Print(L"Error! Can't convert <Package list GUID> argument to GUID\n");
  return EFI_INVALID_PARAMETER;
}
/**
  Retrieves the array of all the HII Handles or the HII handles of a specific
  package list GUID in the HII Database.
  This array is terminated with a NULL HII Handle.
  This function allocates the returned array using AllocatePool().
  The caller is responsible for freeing the array with FreePool().
  @param[in]  PackageListGuid  An optional parameter that is used to request
                               HII Handles associated with a specific
                               Package List GUID.  If this parameter is NULL,
                               then all the HII Handles in the HII Database
                               are returned.  If this parameter is not NULL,
                               then zero or more HII Handles associated with
                               PackageListGuid are returned.
  @retval NULL   No HII handles were found in the HII database
  @retval NULL   The array of HII Handles could not be retrieved
  @retval Other  A pointer to the NULL terminated array of HII Handles
**/
EFI_HII_HANDLE *
EFIAPI
HiiGetHiiHandles (
  IN CONST EFI_GUID  *PackageListGuid  OPTIONAL
  )
;

HiiGetHiiHandles를 아래와 같이 간단하게 사용할 수 있으며 DisplayHIIByGuid.c에 작성한다.

...
EFI_HII_HANDLE* HiiHandles = HiiGetHiiHandles(&PackageListGuid);

<...>

FreePool(HiiHandles);
#include <Library/MemoryAllocationLib.h>
#include <Library/HiiLib.h>

HiiLib 헤더 파일을 사용하기 때문에 INF 파일에 필요한 패키지와 라이브러리 클래스를 추가한다.

[Packages]
  ...
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  ...
  HiiLib

이제 GUID와 연결된 HII 핸들 배열에 대한 포인터가 있다. SendForm을 호출하기 전에 이 배열의 길이를 알아야 한다. 아래 코드와 같이 HiiHandles 포인터를 복사해 NULL을 만날 때까지 반복문을 돌려 배열의 길이를 알아낸다. 아래 코드를 <...>에 작성한다.

EFI_HII_HANDLE* HiiHandle = HiiHandles;
UINTN HandleCount=0;
while (*HiiHandle != NULL) {
  HiiHandle++;
  HandleCount++;
}

SendForm 함수를 사용하기 위해 EFI_FORM_BROWSER2_PROTOCOL에서 SendForm 함수를 호출해야 한다. 따라서 필요한 프로토콜과 헤더 파일을 추가한다.

UefiLessonsPkg/DisplayHIIByGuid/DisplayHIIByGuid.inf

[Protocols]
  gEfiFormBrowser2ProtocolGuid

UefiLessonsPkg/DisplayHIIByGuid/DisplayHIIByGuid.c

#include <Protocol/FormBrowser2.h>

추가로 프로토콜을 호출하기 위해 아래 코드를 작성한다.

EFI_FORM_BROWSER2_PROTOCOL* FormBrowser2;
Status = gBS->LocateProtocol(&gEfiFormBrowser2ProtocolGuid, NULL, (VOID**)&FormBrowser2);
if (EFI_ERROR(Status)) {
  Print(L"Error! Can't locate gEfiFormBrowser2Protocol\n");
  FreePool(HiiHandles);
  return Status;
}

Status = FormBrowser2->SendForm (
                         FormBrowser2,
                         HiiHandles,
                         HandleCount,
                         NULL,
                         0,
                         NULL,
                         NULL
                         );

if (EFI_ERROR(Status)) {
  Print(L"Error! SendForm returned %r\n", Status);
}

빌드 후 QEMU를 실행하여 DispalyHIIByGuid 애플리케이션을 실행하기 전에 기존에 만들었던 ShowHII 애플리케이션을 통해 HII 데이터베이스의 모든 패키지 목록 확인한다.

FS0:\> ShowHII.efi
PackageList[0]: GUID=A487A478-51EF-48AA-8794-7BEE2A0562F1; size=0x1ADC
        Package[0]: type=STRINGS; size=0x1AC4
        Package[1]: type=END; size=0x4
PackageList[1]: GUID=19618BCE-55AE-09C6-37E9-4CE04084C7A1; size=0x21E4
        Package[0]: type=STRINGS; size=0x21CC
        Package[1]: type=END; size=0x4
PackageList[2]: GUID=2F30DA26-F51B-4B6F-85C4-31873C281BCA; size=0xA93
        Package[0]: type=STRINGS; size=0xA7B
        Package[1]: type=END; size=0x4
PackageList[3]: GUID=F74D20EE-37E7-48FC-97F7-9B1047749C69; size=0x2EE9
        Package[0]: type=IMAGES; size=0x2ED1
        Package[1]: type=END; size=0x4
PackageList[4]: GUID=EBF8ED7C-0DD1-4787-84F1-F48D537DCACF; size=0x46C
        Package[0]: type=FORMS; size=0x82
        Package[1]: type=FORMS; size=0x82
        Package[2]: type=STRINGS; size=0x199
        Package[3]: type=STRINGS; size=0x19B
        Package[4]: type=DEVICE_PATH; size=0x1C
        Package[5]: type=END; size=0x4
PackageList[5]: GUID=FE561596-E6BF-41A6-8376-C72B719874D0; size=0x93F
        Package[0]: type=FORMS; size=0xF5
        Package[1]: type=STRINGS; size=0x40A
        Package[2]: type=STRINGS; size=0x40C
        Package[3]: type=DEVICE_PATH; size=0x1C
        Package[4]: type=END; size=0x4
PackageList[6]: GUID=2A46715F-3581-4A55-8E73-2B769AAA30C5; size=0x6B0
        Package[0]: type=FORMS; size=0x143
        Package[1]: type=STRINGS; size=0x539
        Package[2]: type=DEVICE_PATH; size=0x1C
        Package[3]: type=END; size=0x4
PackageList[7]: GUID=99FDC8FD-849B-4EBA-AD13-FB9699C90A4D; size=0x6FE
        Package[0]: type=STRINGS; size=0x340
        Package[1]: type=STRINGS; size=0x3A6
        Package[2]: type=END; size=0x4
PackageList[8]: GUID=E38C1029-E38F-45B9-8F0D-E2E60BC9B262; size=0x15DA
        Package[0]: type=STRINGS; size=0xA88
        Package[1]: type=STRINGS; size=0xB3A
        Package[2]: type=END; size=0x4
PackageList[9]: GUID=D9DCC5DF-4007-435E-9098-8970935504B2; size=0x855
        Package[0]: type=FORMS; size=0x1F6
        Package[1]: type=STRINGS; size=0x62B
        Package[2]: type=DEVICE_PATH; size=0x1C
        Package[3]: type=END; size=0x4
PackageList[10]: GUID=F5F219D3-7006-4648-AC8D-D61DFB7BC6AD; size=0x14EC
        Package[0]: type=SIMPLE_FONTS; size=0x14D4
        Package[1]: type=END; size=0x4
PackageList[11]: GUID=4B47D616-A8D6-4552-9D44-CCAD2E0F4CF9; size=0x6AC8
        Package[0]: type=FORMS; size=0x1030
        Package[1]: type=STRINGS; size=0x3C99
        Package[2]: type=STRINGS; size=0x1DCB
        Package[3]: type=DEVICE_PATH; size=0x1C
        Package[4]: type=END; size=0x4
PackageList[12]: GUID=F95A7CCC-4C55-4426-A7B4-DC8961950BAE; size=0x13909
        Package[0]: type=STRINGS; size=0x138F1
        Package[1]: type=END; size=0x4
PackageList[13]: GUID=DEC5DAA4-6781-4820-9C63-A7B0E4F1DB31; size=0x8677
        Package[0]: type=STRINGS; size=0x865F
        Package[1]: type=END; size=0x4
PackageList[14]: GUID=4344558D-4EF9-4725-B1E4-3376E8D6974F; size=0x83BD
        Package[0]: type=STRINGS; size=0x83A5
        Package[1]: type=END; size=0x4
PackageList[15]: GUID=0AF0B742-63EC-45BD-8DB6-71AD7F2FE8E8; size=0xCB04
        Package[0]: type=STRINGS; size=0xCAEC
        Package[1]: type=END; size=0x4
PackageList[16]: GUID=25F200AA-D3CB-470A-BF51-E7D162D22E6F; size=0x1D3D7
        Package[0]: type=STRINGS; size=0x1D3BF
        Package[1]: type=END; size=0x4
PackageList[17]: GUID=5F5F605D-1583-4A2D-A6B2-EB12DAB4A2B6; size=0x3048
        Package[0]: type=STRINGS; size=0x3030
        Package[1]: type=END; size=0x4
PackageList[18]: GUID=F3D301BB-F4A5-45A8-B0B7-FA999C6237AE; size=0x26B5
        Package[0]: type=STRINGS; size=0x269D
        Package[1]: type=END; size=0x4
PackageList[19]: GUID=7C04A583-9E3E-4F1C-AD65-E05268D0B4D1; size=0x5CB
        Package[0]: type=STRINGS; size=0x5B3
        Package[1]: type=END; size=0x4

ShowHII 애플리케이션을 통해 GUID도 확인할 수 있으므로 이제 DisplayHIIByGuid를 실행할 수 있다.

FS0:\> DisplayHIIByGuid.efi EBF8ED7C-0DD1-4787-84F1-F48D537DCACF
FS0:\> DisplayHIIByGuid.efi FE561596-E6BF-41A6-8376-C72B719874D0
FS0:\> DisplayHIIByGuid.efi 2A46715F-3581-4A55-8E73-2B769AAA30C5
FS0:\> DisplayHIIByGuid.efi D9DCC5DF-4007-435E-9098-8970935504B2
FS0:\> DisplayHIIByGuid.efi 4B47D616-A8D6-4552-9D44-CCAD2E0F4CF9

여기서 확인한 GUID은 아래와 같다.

DisplayHIIByGuid.efi D9DCC5DF-4007-435E-9098-8970935504B2 명령어를 통해서는 아래와 같은 화면을 확인할 수 있다.

Formset: classguid

DisplayHIIByGuid.efi를 사용해 보았을 때 하나의 GUID가 작동하지 않는 다는 것을 알 수 있었다.

FS0:\> DisplayHIIByGuid.efi FE561596-E6BF-41A6-8376-C72B719874D0
Error! SendForm returned Not found

해당 GUID에 대한 내용은 아래 링크에서 확인할 수 있다.

EFI_FORM_BROWSER2_PROTOCOL.SendForm()을 다시 살펴본다.

EFI_FORM_BROWSER2_PROTOCOL.SendForm()

Summary:
Initialize the browser to display the specified configuration forms.

Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_SEND_FORM2) (
 IN CONST EFI_FORM_BROWSER2_PROTOCOL *This,
 IN EFI_HII_HANDLE *Handles,
 IN UINTN HandleCount,
 IN CONST EFI_GUID *FormsetGuid, OPTIONAL
 IN EFI_FORM_ID FormId, OPTIONAL
 IN CONST EFI_SCREEN_DESCRIPTOR *ScreenDimensions, OPTIONAL
 OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest OPTIONAL
 );

Parameters:
This			A pointer to the EFI_FORM_BROWSER2_PROTOCOL instance.
Handles			A pointer to an array of HII handles to display.
HandleCount		The number of handles in the array specified by Handle.
FormsetGuid		This field points to the EFI_GUID which must match the Guid field or one of the
                	elements of the ClassId field in the EFI_IFR_FORM_SET op-code. If FormsetGuid
                	is NULL, then this function will display the form set class
                	EFI_HII_PLATFORM_SETUP_FORMSET_GUID.
FormId			This field specifies the identifier of the form within the form set to render as the first
			displayable page. If this field has a value of 0x0000, then the Forms Browser will
			render the first enabled form in the form set.
ScreenDimensions	Points to recommended form dimensions, including any non-content area, in characters.
ActionRequested		Points to the action recommended by the form.

Description:
This function is the primary interface to the Forms Browser. The Forms Browser displays the forms specified by FormsetGuid and FormId from all of HII handles specified by Handles. If more than one form can be displayed, the Forms Browser will provide some means for the user to navigate between the
forms in addition to that provided by cross-references in the forms themselves.

지금까지 함수를 호출할 때 FormsetGuid = NULL을 사용했다. 설명에서는 EFI_HII_PLATFORM_SETUP_FORMSET_GUID가 사용되었음을 의미한다.

또한 이전에 FormSet에 대해 classguid를 명시적으로 선언하지 않았을 때, IFR은 기본값으로 EFI_HII_PLATFORM_SETUP_FORMSET_GUID를 가진다는 것을 알아봤었다. 그래서 SendForm도 정상적으로 호출됐었던 것이다.

formset
  guid = EFI_FILE_EXPLORE_FORMSET_GUID,
  title = STRING_TOKEN(STR_FILE_EXPLORER_TITLE),
  help = STRING_TOKEN(STR_NULL_STRING),
  classguid = EFI_FILE_EXPLORE_FORMSET_GUID,

패키지 목록에서 확인한 GUID와 EFI_FILE_EXPLORE_FORMSET_GUID는 같다.

문제를 해결하기 위해 *.c 코드를 수정해야 한다.

-  if (Argc != 2) {
+  if ((Argc < 2) || (Argc > 3)) {
     Print(L"Usage:\n");
-    Print(L"  DisplayHIIByGuid <Package list GUID>\n");
+    Print(L"  DisplayHIIByGuid <Package list GUID> [<Formset classguid>]\n");
     return EFI_INVALID_PARAMETER;
   }

새로운 인자를 처리할 수 있는 코드를 추가한다.

EFI_GUID FormsetClassGuid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID;
if (Argc == 3) {
  Status = StrToGuid(Argv[2], &FormsetClassGuid);
  if (Status != RETURN_SUCCESS) {
    Print(L"Error! Can't convert <Formset classguid> argument to GUID\n");
    return EFI_INVALID_PARAMETER;
  }
}

SendForm을 호출하는 부분에서 FormsetClassGuid를 추가한다.

   Status = FormBrowser2->SendForm (
                            FormBrowser2,
                            HiiHandles,
                            HandleCount,
-                           NULL,
+                           &FormsetClassGuid,
                            0,
                            NULL,
                            NULL
                            );

이제 아래 명령을 통해 정상 동작하는 것을 확인할 수 있다.

FS0:\> DisplayHIIByGuid.efi FE561596-E6BF-41A6-8376-C72B719874D0 FE561596-E6BF-41A6-8376-C72B719874D0

HIIStaticFormDriver

이전에 생성한 HIIStaticForm UEFI 애플리케이션의 복사본이 될 HIIStaticFormDriver Uefi 드라이버를 생성한다.

아래 스크립트를 코드를 이용해 생성한다.

#!/bin/bash
##
# Copyright (c) 2021, Konstantin Aladyshev <aladyshev22@gmail.com>
#
# SPDX-License-Identifier: MIT
##

# This is a simple script that creates a basic structure for your new UEFI driver
# Put this script in your edk2 folder and run it with 1 argument - your new driver name

DRIVER_NAME=${1}

UUID=$(uuidgen)

mkdir -p UefiLessonsPkg/${DRIVER_NAME}

cat << EOF > UefiLessonsPkg/${DRIVER_NAME}/${DRIVER_NAME}.inf
[Defines]
  INF_VERSION                    = 1.25
  BASE_NAME                      = ${DRIVER_NAME}
  FILE_GUID                      = ${UUID}
  MODULE_TYPE                    = UEFI_DRIVER
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = ${DRIVER_NAME}EntryPoint
  UNLOAD_IMAGE                   = ${DRIVER_NAME}Unload

[Sources]
  ${DRIVER_NAME}.c

[Packages]
  MdePkg/MdePkg.dec

[LibraryClasses]
  UefiDriverEntryPoint
  UefiLib
EOF

cat << EOF > UefiLessonsPkg/${DRIVER_NAME}/${DRIVER_NAME}.c
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>


EFI_STATUS
EFIAPI
${DRIVER_NAME}Unload (
  EFI_HANDLE ImageHandle
  )
{
  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
${DRIVER_NAME}EntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  return EFI_SUCCESS;
}
EOF
$ ./createNewDriver.sh HIIStaticFormDriver
$ cp UefiLessonsPkg/HIIStaticForm/Form.vfr UefiLessonsPkg/HIIStaticFormDriver/
$ cp UefiLessonsPkg/HIIStaticForm/Strings.uni UefiLessonsPkg/HIIStaticFormDriver/

아래는 HIIStaticFormDriver의 *.c와 *.inf 파일이다.

UefiLessonsPkg/HIIStaticFormDriver/HIIStaticFormDriver.inf

[Defines]
  INF_VERSION                    = 1.25
  BASE_NAME                      = HIIStaticFormDriver
  FILE_GUID                      = 22514099-ad3b-45ec-b14b-112eb6446db2
  MODULE_TYPE                    = UEFI_DRIVER
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = HIIStaticFormDriverEntryPoint
  UNLOAD_IMAGE                   = HIIStaticFormDriverUnload

[Sources]
  HIIStaticFormDriver.c
  Strings.uni
  Form.vfr

[Packages]
  MdePkg/MdePkg.dec
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  UefiDriverEntryPoint
  UefiLib
  HiiLib

UefiLessonsPkg/HIIStaticFormDriver/HIIStaticFormDriver.c

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


extern UINT8 FormBin[];

EFI_HII_HANDLE Handle;


EFI_STATUS
EFIAPI
HIIStaticFormDriverUnload (
  EFI_HANDLE ImageHandle
  )
{
  if (Handle != NULL)
    HiiRemovePackages(Handle);

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
HIIStaticFormDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  Handle = HiiAddPackages(
             &gEfiCallerIdGuid,
             NULL,
             HIIStaticFormDriverStrings,
             FormBin,
             NULL
           );
  if (Handle == NULL)
    return EFI_OUT_OF_RESOURCES;

  return EFI_SUCCESS;
}

UefiLessonsPkg/UefiLessonsPkg.dsc에 INF 파일을 추가한다.

[Components]
  UefiLessonsPkg/HIIStaticFormDriver/HIIStaticFormDriver.inf

HIIStaticFormDriver를 빌드하고 로드한다.

FS0:\> load HIIStaticFormDriver.efi
Image 'FS0:\HIIStaticFormDriver.efi' loaded at 688A000 - Success

드라이버를 로드하면 드라이버가 HII 데이터베이스에 데이터를 저장한 것을 볼 수 있다.

FS0:\> ShowHII.efi
...
PackageList[20]: GUID=22514099-AD3B-45EC-B14B-112EB6446DB2; size=0x28B
        Package[0]: type=FORMS; size=0x98
        Package[1]: type=STRINGS; size=0x1DB
        Package[2]: type=END; size=0x4

DisplayHIIByGuid.efi를 사용해 HII 데이터를 확인할 수 있다.

FS0:\> DisplayHIIByGuid.efi 22514099-AD3B-45EC-B14B-112EB6446DB2

기본 UEFI 메뉴에서 애플리케이션 확인하기

exit 명령을 통해 UEFI 메뉴로 나간다.

FS0:\> exit

Device Manager로 이동하면 Device List에 Static Formset이 있는 것을 확인할 수 있다.

해당 메뉴를 선택하면 만들었던 드라이버 폼인 것을 볼 수 있다.

classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID가 있는 모든 폼이 Device Manager에 추가된다.

따라서 폼에 다른 classguid를 사용하지 않으면 DisplayHIIByGuid.efi 애플리케이션을 사용하는 것보다 메뉴에서 폼을 확인하는 방법이 더 쉽다.

아래 코드는 CompareGuid를 사용하여 모든 formset classguid를 EFI_HII_PLATFORM_SETUP_FORMSET_GUID와 비교하고 CompareGuid가 True를 반환하는 경우에만 진행되는 snippet 폼이다.

  //
  // Get all the Hii handles
  //
  HiiHandles = HiiGetHiiHandles (NULL);

  ...

  //
  // Search for formset of each class type
  //
  for (Index = 0; HiiHandles[Index] != NULL; Index++) {
    Status = HiiGetFormSetFromHiiHandle (HiiHandles[Index], &Buffer, &BufferSize);
    if (EFI_ERROR (Status)) {
      continue;
    }

    Ptr = (UINT8 *)Buffer;
    while (TempSize < BufferSize) {
      TempSize += ((EFI_IFR_OP_HEADER *)Ptr)->Length;
      if (((EFI_IFR_OP_HEADER *)Ptr)->Length <= OFFSET_OF (EFI_IFR_FORM_SET, Flags)) {
        Ptr += ((EFI_IFR_OP_HEADER *)Ptr)->Length;
        continue;
      }

      ClassGuidNum = (UINT8)(((EFI_IFR_FORM_SET *)Ptr)->Flags & 0x3);
      ClassGuid    = (EFI_GUID *)(VOID *)(Ptr + sizeof (EFI_IFR_FORM_SET));
      while (ClassGuidNum-- > 0) {
        if (CompareGuid (&gEfiHiiPlatformSetupFormsetGuid, ClassGuid) == 0) {      <------- Add only formsets with
          ClassGuid++;                                                                      EFI_HII_PLATFORM_SETUP_FORMSET_GUID
          continue;
        }
    ...
Previous58. VFR 요소 : subtitle 및 textNext60. gRT->SetVariable() 함수를 사용한 UEFI 변수 생성, 변경 및 삭제

Last updated 2 years ago

먼저 인자로 들어오는 문자열을 GUID 값으로 변경해야 한다. 이를 위해StrToGuid 함수를 사용할 수 있다.

이제 패키지 목록 GUID가 있으면 HiiGetHiiHandles 함수를 사용해 GUID에 대한 HII 핸들을 가져올 수 있다.

EBF8ED7C-0DD1-4787-84F1-F48D537DCACF

FE561596-E6BF-41A6-8376-C72B719874D0 EFI_FILE_EXPLORE_FORMSET_GUID

2A46715F-3581-4A55-8E73-2B769AAA30C5 RAM_DISK_FORM_SET_GUID gRamDiskFormSetGuid

D9DCC5DF-4007-435E-9098-8970935504B2

4B47D616-A8D6-4552-9D44-CCAD2E0F4CF9 gIScsiConfigGuid ISCSI_CONFIG_GUID

FE561596-E6BF-41A6-8376-C72B719874D0 EFI_FILE_EXPLORE_FORMSET_GUID

그러나 문제가 있는 패키지 목록에서 폼의 VFR 코드를 보면 다른 classguid를 사용한다는 것을 알 수 있다.

새 폼이 Device Manager에 동적으로 추가되는 방법이 궁금한 경우 아래 링크를 통해 확인한다.

🖥️
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/BaseLib.h
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Library/HiiLib.h
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiHiiLib/HiiLib.c
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/FileExplorerLib/FormGuid.h
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/RamDiskHii.h
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/MdeModulePkg.dec
https://github.com/tianocore/edk2/blob/master/OvmfPkg/PlatformDxe/Platform.inf
https://github.com/tianocore/edk2/blob/master/NetworkPkg/Include/Guid/IScsiConfigHii.h
https://github.com/tianocore/edk2/blob/master/NetworkPkg/NetworkPkg.dec
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/FileExplorerLib/FormGuid.h
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/FileExplorerLib/FileExplorerVfr.vfr
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/DeviceManagerUiLib/DeviceManager.c
DisplayHIIByGuid.efi : File Explorer