코드에서 볼 수 있듯이 FormBin 배열을 사용하여 폼 데이터를 저장한다. EDKII 빌드 시스템은 모든 VFR 파일에서 HII 폼 패키지를 생성하고 데이터를 <VFR name>Bin 배열에 넣는다. UNI 파일과 마찬가지로 이 배열에는 4바이트 패키지 크기 헤더가 추가된다. 따라서 HiiAddPackages 라이브러리 함수를 그대 사용할 수 있다. 또한 이 배열은 *.h 파일이 아닌 자동 생성된 *.c 파일에 선언되므로 파일에 extern으로 선언해야 한다.
모듈을 빌드하고 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIISimpleForm/HIISimpleForm/DEBUG/Form.c를 확인하면 아래와 같이 생성된 배열을 볼 수 있다.
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.
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
);
애플리케이션을 빌드하고 실행하면 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에 새 문자열을 작성한다.
다시 빌드 후 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.