💻
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
  • IFR 데이터 파싱
  • 폼 보기
  • IFR data parsing
  1. UEFI 개발
  2. VFR

57. VFR을 사용해 간단한 폼 생성 및 EFI_FORM_BROWSER2_PROTOCOL.SendForm()를 통해 화면에 폼 표시하기

PreviousVFRNext58. VFR 요소 : subtitle 및 text

Last updated 2 years ago

HII의 주요 목적은 UEFI 설정을 제어하기 위해 사용자 구성 메뉴에 표시하는 것이다.

지금까지 HII 문자열과 HII 폰트에 대해서 알아보았다. 이제 모든 것을 하나로 묶는 HII 폼에 대해 알아본다.

HII 폼 패키지의 데이터는 IFR이 내부 폼 표현을 나타내는 특수 IFR 형식으로 인코딩된다. IFR은 사람이 읽을 수 있는 것이 아니기 때문에 직접 폼 패키지를 구성하는 것은 쉽지 않다. 일련의 작동 코드(opcode)로 파싱 프로세스가 매우 지루할 수 있다.

EDK2는 VFR이라는 특별한 사람이 사람 친화적인 언어로 HII 폼을 작성하는 방법을 제공한다. 여기서 VFR은 시각적 폼 표현 (내부 폼 표현의 반대)을 나타낸다. EDK2에는 IFR opcode로 VFR 코드를 C 배열로 변환하는 VfrCompile이라는 특수 유틸리티가 있다.

HiiLib을 사용해 폼을 볼 수 있는 간단한 HIISimpleForm 애플리케이션을 생성한다.

UefiLessonsPkg/HIISimpleForm/HIISimpleForm.inf

[Defines]
  INF_VERSION                    = 1.25
  BASE_NAME                      = HIISimpleForm
  FILE_GUID                      = df2f1465-2bf1-492c-af6c-232ac40bdf82
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain

[Sources]
  HIISimpleForm.c

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

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  HiiLib

UefiLessonsPkg/HIISimpleForm/HIISimpleForm.c

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

#include <Library/HiiLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  return EFI_SUCCESS;
}

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

[Components]
  ...
  UefiLessonsPkg/HIISimpleForm/HIISimpleForm.inf

이제 vfr 파일을 생성한다.

#define HIISIMPLEFORM_FORMSET_GUID  {0xef2acc91, 0x7b50, 0x4ab9, {0xab, 0x67, 0x2b, 0x4, 0xf8, 0xbc, 0x13, 0x5e}}

formset
  guid     = HIISIMPLEFORM_FORMSET_GUID,
  title    = STRING_TOKEN(HIISIMPLEFORM_FORMSET_TITLE),
  help     = STRING_TOKEN(HIISIMPLEFORM_FORMSET_HELP),
endformset;

VFR의 모든 항목은 formset 내에서 인코딩되어야 한다. 이 요소는 formset 키워드로 시작해서 endformset으로 끝나야 한다. formset을 구성할 때는 필수로 guid, title, help 총 3개의 필드가 있어야 한다.

이제 guid를 인코딩 할 수 있다.

guid     = {0xef2acc91, 0x7b50, 0x4ab9, {0xab, 0x67, 0x2b, 0x4, 0xf8, 0xbc, 0x13, 0x5e}}

또는 이전에 했던 것처럼 C 구문과 유사한 정의문으로 작성할 수 있다.

title과 help 필드에는 문자열의 문자열 ID가 포함되어야 한다. 따라서 필요한 ID를 얻기 위해 STRING_TOKEN(...)를 사용하고 문자열 자체는 UNI 파일(UefiLessonsPkg/HIISimpleForm/Strings.uni)로 인코딩되어야 한다.

#langdef en-US "English"

#string HIISIMPLEFORM_FORMSET_TITLE          #language en-US  "Simple Formset"
#string HIISIMPLEFORM_FORMSET_HELP           #language en-US  "This is a very simple formset"
formset
  guid  = OVMF_PLATFORM_CONFIG_GUID,
  title = STRING_TOKEN(STR_FORMSET_TITLE),
  help  = STRING_TOKEN(STR_FORMSET_HELP),
  ...
endformset;
#langdef en-US "English"

#string STR_FORMSET_TITLE        #language en-US "OVMF Platform Configuration"
#string STR_FORMSET_HELP         #language en-US "Change various OVMF platform settings."

vfr 파일과 uni 파일을 위와 같이 하면 아래와 같이 BIOS에서 내용을 확인할 수 있다.

VFR과 UNI 파일이 생겼으니 HII에 폼을 게시한다. UefiLessonsPkg/HIISimpleForm/HIISimpleForm.inf에 VFR과 UNI 파일을 추가한다.

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

HIISimpleForm.c 파일에도 HII 데이터베이스에 폼과 문자열을 채우는 코드를 작성한다.

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

#include <Library/HiiLib.h>

extern UINT8 FormBin[];

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_HII_HANDLE Handle = HiiAddPackages(
                             &gEfiCallerIdGuid,
                             NULL,
                             HIISimpleFormStrings,
                             FormBin,
                             NULL
                             );
  if (Handle == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  return EFI_SUCCESS;
}

코드에서 볼 수 있듯이 FormBin 배열을 사용하여 폼 데이터를 저장한다. EDKII 빌드 시스템은 모든 VFR 파일에서 HII 폼 패키지를 생성하고 데이터를 <VFR name>Bin 배열에 넣는다. UNI 파일과 마찬가지로 이 배열에는 4바이트 패키지 크기 헤더가 추가된다. 따라서 HiiAddPackages 라이브러리 함수를 그대 사용할 수 있다. 또한 이 배열은 *.h 파일이 아닌 자동 생성된 *.c 파일에 선언되므로 파일에 extern으로 선언해야 한다.

모듈을 빌드하고 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIISimpleForm/HIISimpleForm/DEBUG/Form.c를 확인하면 아래와 같이 생성된 배열을 볼 수 있다.

unsigned char FormBin[] = {
  // ARRAY LENGTH

  0x3D,  0x00,  0x00,  0x00,

  // PACKAGE HEADER

  0x39,  0x00,  0x00,  0x02,

  // PACKAGE DATA

  0x0E,  0xA7,  0x91,  0xCC,  0x2A,  0xEF,  0x50,  0x7B,  0xB9,  0x4A,  0xAB,  0x67,  0x2B,  0x04,  0xF8,  0xBC,
  0x13,  0x5E,  0x02,  0x00,  0x03,  0x00,  0x01,  0x71,  0x99,  0x03,  0x93,  0x45,  0x85,  0x04,  0x4B,  0xB4,
  0x5E,  0x32,  0xEB,  0x83,  0x26,  0x04,  0x0E,  0x5C,  0x06,  0x00,  0x00,  0x00,  0x00,  0x5C,  0x06,  0x00,
  0x00,  0x01,  0x00,  0x29,  0x02

};

EDKII의 빌드 시스템은 *.c 파일 말고 다른 파일을 추가로 생성한다. Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIISimpleForm/HIISimpleForm/DEBUG/Form.lst

//
//  VFR compiler version  2.01 (UEFI 2.4) Developer Build based on Revision: Unknown
//
extern unsigned char HIISimpleFormStrings[];
formset
>00000000: 0E A7 91 CC 2A EF 50 7B B9 4A AB 67 2B 04 F8 BC 13 5E 02 00 03 00 01 71 99 03 93 45 85 04 4B B4 5E 32 EB 83 26 04 0E
>00000027: 5C 06 00 00 00 00
>0000002D: 5C 06 00 00 01 00
  guid = {0xef2acc91, 0x7b50, 0x4ab9, {0xab, 0x67, 0x2b, 0x4, 0xf8, 0xbc, 0x13, 0x5e}},
  title = STRING_TOKEN(0x0002),
  help = STRING_TOKEN(0x0003),
endformset;
>00000033: 29 02

//
// All Opcode Record List
//
>00000000: 0E A7 91 CC 2A EF 50 7B B9 4A AB 67 2B 04 F8 BC 13 5E 02 00 03 00 01 71 99 03 93 45 85 04 4B B4 5E 32 EB 83 26 04 0E
>00000027: 5C 06 00 00 00 00
>0000002D: 5C 06 00 00 01 00
>00000033: 29 02

Total Size of all record is 0x00000035

여기서 FormBin 배열의 PACKAGE DATA 부분이 IFR opcode에서 어떻게 구성되는지 확인할 수 있으며, 담당 VFR 코드와의 관계에 대한 몇 가지 설명을 확인할 수 있다.

IFR 데이터 파싱

데이터는 4개의 요소 EFI_IFR_FORM_SET, 2개의 EFI_IFR_DEFAULTSTORE과 EFI_IFR_END로 구성되어 있다. 이 요소들은 Form.lst에서 하나의 데이터 문자열을 가진다.

문자열 패키지와 마찬가지로 가능한 모든 구성 요소에는 공통 헤더가 있다.

EFI_IFR_OP_HEADER:

Summary:
Standard opcode header

Prototype:
typedef struct _EFI_IFR_OP_HEADER {
 UINT8 OpCode;
 UINT8 Length:7;
 UINT8 Scope:1;
} EFI_IFR_OP_HEADER;

Members:
OpCode 		Defines which type of operation is being described by this header.
Length 		Defines the number of bytes in the opcode, including this header.
Scope   	If this bit is set, the opcode begins a new scope, which is ended by an EFI_IFR_END opcode.

Description:
Forms are represented in a binary format roughly similar to processor instructions. Each header contains an opcode, a length and a scope indicator.
If Scope indicator is set, the scope exists until it reaches a corresponding EFI_IFR_END opcode. Scopes may be nested within other scopes.

아래는 EFI_IFR_FORM_SET의 정의다.

EFI_IFR_FORM_SET

Summary:
The form set is a collection of forms that are intended to describe the pages that will be displayed to the user.

Prototype:

#define EFI_IFR_FORM_SET_OP 0x0E

typedef struct _EFI_IFR_FORM_SET {
 EFI_IFR_OP_HEADER Header;
 EFI_GUID Guid;
 EFI_STRING_ID FormSetTitle;
 EFI_STRING_ID Help;
 UINT8 Flags;
//EFI_GUID ClassGuid[…];
} EFI_IFR_FORM_SET;

Members:
Header 		The sequence that defines the type of opcode as well as the length of the opcode being defined. Header.OpCode = EFI_IFR_FORM_SET_OP.
Guid 		The unique GUID value associated with this particular form set.
FormSetTitle    The string token reference to the title of this particular form set.
Help            The string token reference to the help of this particular form set.
Flags           Flags which describe additional features of the form set. Bits 0:1 = number of members in ClassGuid. Bits 2:7 = Reserved. Should be set to zero.
ClassGuid       Zero to four class identifiers.

Description
The form set consists of a header and zero or more forms.
#define EFI_HII_PLATFORM_SETUP_FORMSET_GUID \
  { 0x93039971, 0x8545, 0x4b04, { 0xb4, 0x5e, 0x32, 0xeb, 0x83, 0x26, 0x4, 0xe } }

클래스에 대한 다른 GUID를 선언하려면 아래와 같은 VFR 구문을 사용한다.

classguid = <...>

다음으로 EFI_IFR_DEFAULTSTORE 요소를 살펴본다.

EFI_IFR_DEFAULTSTORE

Summary:
Provides a declaration for the type of default values that a question can be associated with

Prototype:
#define EFI_IFR_DEFAULTSTORE_OP 0x5c

typedef struct _EFI_IFR_DEFAULTSTORE {
 EFI_IFR_OP_HEADER Header;
 EFI_STRING_ID DefaultName;
 UINT16 DefaultId;
} EFI_IFR_DEFAULTSTORE;

Members
Header 		The sequence that defines the type of opcode as well as the length of the opcode being defined.
		For this tag, Header.OpCode = EFI_IFR_DEFAULTSTORE_OP
DefaultName 	A string token reference for the human readable string associated with the type of default being declared.
DefaultId 	The default identifier, which is unique within the current form set. The default identifier creates a group of defaults

Description:
Declares a class of default which can then have question default values associated with. An EFI_IFR_DEFAULTSTORE with a specified DefaultId must appear in the IFR before it can be referenced by an EFI_IFR_DEFAULT.

EFI_IFR_FORM_SET으로 시작했으므로 EFI_IFR_END로 끝내야 한다.

EFI_IFR_END

Summary:
End of the current scope.

Prototype:

#define EFI_IFR_END_OP 0x29

typedef struct _EFI_IFR_END {
 EFI_IFR_OP_HEADER Header;
} EFI_IFR_END;

Members:
Header Standard opcode header, where OpCode is EFI_IFR_END_OP.

Description:
Marks the end of the current scope.

폼 보기

실제로 폼을 보이게 하려면 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.

보다시피 대부분의 매개 변수는 선택 사항이다. 그래서 이 함수를 아래와 같이 단순하게 사용할 수 있다. HIISimpleFomr.c에 추가한다.

EFI_STATUS Status;
EFI_FORM_BROWSER2_PROTOCOL* FormBrowser2;
Status = gBS->LocateProtocol(&gEfiFormBrowser2ProtocolGuid, NULL, (VOID**)&FormBrowser2);
if (EFI_ERROR(Status)) {
  return Status;
}

Status = FormBrowser2->SendForm (
                         FormBrowser2,
                         &Handle,
                         1,
                         NULL,
                         0,
                         NULL,
                         NULL
                         );

HIISimpleForm.inf 파일에 gEfiFormBrowser2ProtocolGuid를 추가한다.

[Protocols]
  gEfiFormBrowser2ProtocolGuid

FormBrowser2를 사용하기 위해 헤더 파일도 추가한다.

#include <Protocol/FormBrowser2.h>

애플리케이션이 끝나면 HII 패키지가 더이상 필요하지 않으므로 제거해야 한다.

HiiRemovePackages(Handle);

애플리케이션을 빌드하고 실행하면 Form Browser에 아무것도 표시되지 않고 즉시 Shell로 제어 권한을 반환한다. formset에 표시할 항목이 없기 때문이다. formset의 핵심 요소는 폼이므로 UefiLessonsPkg/HIISimpleForm/Form.vfr에 간단한 폼을 formset에 추가한다.

formset
  guid     = HIISIMPLEFORM_FORMSET_GUID,
  title    = STRING_TOKEN(HIISIMPLEFORM_FORMSET_TITLE),
  help     = STRING_TOKEN(HIISIMPLEFORM_FORMSET_HELP),
  form formid = 1,
    title = STRING_TOKEN(HIISIMPLEFORM_FORMID1_TITLE);
  endform;
endformset;

각 폼에는 최소 formid 및 title이 있어야 한다. 이것들은 필수 필드라고 할 수 있고 title은 폼이 표시될 때 페이지 제목에 사용되며, formid는 다른 코드에서 폼을 참조하는데 사용된다.

또한 다른 모든 form 속성과 마찬가지로 별도의 문자열에 formid 속성을 작성할 수 있다.

form
  formid = 1,
  title = STRING_TOKEN(HIISIMPLEFORM_FORMID1_TITLE);
endform;

그러나 일반적으로 formid는 폼 키워드와 동일한 문자열에 작성된다. 두 구문 모두 VFR에서 동일하다.

UefiLessonsPkg/HIISimpleForm/Strings.uni에 새 문자열을 작성한다.

...
#string HIISIMPLEFORM_FORMID1_TITLE          #language en-US  "Simple Form"

다시 빌드 후 HIISimpleForm.efi를 실행하면 아래와 같은 결과를 확인할 수 있다.

IFR data parsing

Form.lst를 다시 확인하면 아래와 같다.

//
//  VFR compiler version  2.01 (UEFI 2.4) Developer Build based on Revision: Unknown
//
extern unsigned char HIISimpleFormStrings[];
formset
>00000000: 0E A7 91 CC 2A EF 50 7B B9 4A AB 67 2B 04 F8 BC 13 5E 02 00 03 00 01 71 99 03 93 45 85 04 4B B4 5E 32 EB 83 26 04 0E
>00000027: 5C 06 00 00 00 00
>0000002D: 5C 06 00 00 01 00
  guid = {0xef2acc91, 0x7b50, 0x4ab9, {0xab, 0x67, 0x2b, 0x4, 0xf8, 0xbc, 0x13, 0x5e}},
  title = STRING_TOKEN(0x0002),
  help = STRING_TOKEN(0x0003),
  form
>00000033: 01 86 01 00 04 00
    formid = 1,
    title = STRING_TOKEN(0x0004);
  endform;
>00000039: 29 02
endformset;
>0000003B: 29 02

두 개의 IFR이 추가되었다. 먼저 EFI_IFR_FORM이다.

EFI_IFR_FORM

Summary:
Creates a form.

Prototype:
#define EFI_IFR_FORM_OP 0x01

typedef struct _EFI_IFR_FORM {
 EFI_IFR_OP_HEADER Header;
 EFI_FORM_ID FormId;
 EFI_STRING_ID FormTitle;
} EFI_IFR_FORM;

Members:
Header 		The sequence that defines the type of opcode as well as the length of the opcode being defined. Header.OpCode = EFI_IFR_FORM_OP.
FormId 		The form identifier, which uniquely identifies the form within the form set. The form identifier, along with the device path
		and form set GUID, uniquely identifies a form within a system.
FormTitle 	The string token reference to the title of this particular form.

Description:
A form is the encapsulation of what amounts to a browser page. The header defines a FormId, which is referenced by the form set, among others. It also defines a FormTitle, which is a string to be used as the title for the form.

그러나 이 opcode가 다른 범위를 열면 EFI_IFR_END opcode가 더 있다.

formset이 다른 formset에 포함되면 title 및 help 필드가 표시된다. 예를 들면 아래와 같다.

위에서 VFR에 클래스 guid를 선언하지 않았었다. 그러나 opcode 출력에서 IFR에 93039971-8545-4b04-b45e-32eb8326040e과 동일한 ClassGuid가 있는 것을 확인할 수 있었다. 기본적으로 클래스에 다른 GUID를 선언하지 않은 경우 기본적으로 빌드 시스템에 의해 할당된다.

🖥️
https://edk2-docs.gitbook.io/edk-ii-vfr-specification/
https://github.com/tianocore/edk2/tree/master/BaseTools/Source/C/VfrCompile
https://github.com/tianocore/edk2/blob/master/OvmfPkg/PlatformDxe/PlatformForms.vfr
https://github.com/tianocore/edk2/blob/master/OvmfPkg/PlatformDxe/Platform.uni
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Guid/HiiPlatformSetupFormset.h
HIISimpleForm.efi