Label들은 EFI_IFR_GUID_OP opcode로 인코딩 되어졌다. 해당 opcode는 vendor가 UEFI 스펙에 정의되지 않은 새로운 IFR opcode를 추가할 수 있도록 돕는다. 이는 UEFI 스펙의 확장을 위한 수단이다.
EFI_IFR_GUID
Summary:
A GUIDed operation. This op-code serves as an extensible op-code which can be defined by the Guid value to have various functionality. It should be noted that IFR browsers or scripts which cannot interpret the meaning of this GUIDed op-code will skip it.
Prototype:
#define EFI_IFR_GUID_OP 0x5F
typedef struct _EFI_IFR_GUID {
EFI_IFR_OP_HEADER Header;
EFI_GUID Guid;
//Optional Data Follows
} EFI_IFR_GUID;
Parameters:
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_GUID_OP
Guid The GUID value for this op-code. This field is intended to define a particular type of special-purpose function,
and the format of the data which immediately follows the Guid field (if any) is defined by that particular GUID.
만약 binary의 데이터를 분석하면 이 경우 Guid가 다음과 같다는 것을 알 수 있다.
35 17 0B 0F A0 87 93 41 B2 66 53 8C 38 AF 48 CE
이 값은 opcode의 끝(Header.Length 에 의해 결정된다) 다음에 오는 나머지 모든 opcode 데이터의 모든 형식을 정의한다. 이 경우 00 11 11 또는 00 22 22 이다.\
IFR에 존재하는 GUID는 ./MdeModulePkg/MdeModulePkg.dec 에 정의되었다.
/**
This function updates a form that has previously been registered with the HII
Database. This function will perform at most one update operation.
The form to update is specified by Handle, FormSetGuid, and FormId. Binary
comparisons of IFR opcodes are performed from the beginning of the form being
updated until an IFR opcode is found that exactly matches the first IFR opcode
specified by StartOpCodeHandle. The following rules are used to determine if
an insert, replace, or delete operation is performed:
1) If no matches are found, then NULL is returned.
2) If a match is found, and EndOpCodeHandle is NULL, then all of the IFR opcodes
from StartOpCodeHandle except the first opcode are inserted immediately after
the matching IFR opcode in the form to be updated.
3) If a match is found, and EndOpCodeHandle is not NULL, then a search is made
from the matching IFR opcode until an IFR opcode exactly matches the first
IFR opcode specified by EndOpCodeHandle. If no match is found for the first
IFR opcode specified by EndOpCodeHandle, then NULL is returned. If a match
is found, then all of the IFR opcodes between the start match and the end
match are deleted from the form being updated and all of the IFR opcodes
from StartOpCodeHandle except the first opcode are inserted immediately after
the matching start IFR opcode. If StartOpCcodeHandle only contains one
IFR instruction, then the result of this operation will delete all of the IFR
opcodes between the start end matches.
If HiiHandle is NULL, then ASSERT().
If StartOpCodeHandle is NULL, then ASSERT().
@param[in] HiiHandle The HII Handle of the form to update.
@param[in] FormSetGuid The Formset GUID of the form to update. This
is an optional parameter that may be NULL.
If it is NULL, all FormSet will be updated.
@param[in] FormId The ID of the form to update.
@param[in] StartOpCodeHandle An OpCode Handle that contains the set of IFR
opcodes to be inserted or replaced in the form.
The first IFR instruction in StartOpCodeHandle
is used to find matching IFR opcode in the
form.
@param[in] EndOpCodeHandle An OpCcode Handle that contains the IFR opcode
that marks the end of a replace operation in
the form. This is an optional parameter that
may be NULL. If it is NULL, then the IFR
opcodes specified by StartOpCodeHandle are
inserted into the form.
@retval EFI_OUT_OF_RESOURCES Not enough memory resources are allocated.
@retval EFI_NOT_FOUND The following cases will return EFI_NOT_FOUND:
1) The form specified by HiiHandle, FormSetGuid,
and FormId could not be found in the HII Database.
2) No IFR opcodes in the target form match the first
IFR opcode in StartOpCodeHandle.
3) EndOpCOde is not NULL, and no IFR opcodes in the
target form following a matching start opcode match
the first IFR opcode in EndOpCodeHandle.
@retval EFI_SUCCESS The matched form is updated by StartOpcode.
**/
EFI_STATUS
EFIAPI
HiiUpdateForm (
IN EFI_HII_HANDLE HiiHandle,
IN EFI_GUID *FormSetGuid OPTIONAL,
IN EFI_FORM_ID FormId,
IN VOID *StartOpCodeHandle,
IN VOID *EndOpCodeHandle OPTIONAL
);
보다시피 이 함수에는 두 개의 opcode 핸들이 필요하다.
StartOpCodeHandle - 대체 section의 시작을 표시하는 IFR과 넣으려고 하는 모든 opcode를 포함하는 opcode 버퍼에 대한 핸들,
EndOpCodeHandle - 대체 section의 끝을 표시하는 IFR을 포함하는 opcode 버퍼에 대한 핸들
이러한 OpCode 핸들을 생성하기 위해 HiiLib 의 특별한 함수를 살펴보자.
/**
Allocates and returns a new OpCode Handle. OpCode Handles must be freed with
HiiFreeOpCodeHandle().
@retval NULL There are not enough resources to allocate a new OpCode Handle.
@retval Other A new OpCode handle.
**/
VOID *
EFIAPI
HiiAllocateOpCodeHandle (
VOID
);
여기서 중요한 점은 이 함수는 어떠한 크기도 허용하지 않는다는 점이다. 왜냐하면 이 작업에서 해당 buffer는 항상 고정이기 때문이다. HiiAllocateOpCodeHandle 은 다음과 같이 정의된 새로 할당되는 구조인 HII_LIB_OPCODE_BUFFER 에 대한 포인터를 반환한다.
/**
Frees an OpCode Handle that was previously allocated with HiiAllocateOpCodeHandle().
When an OpCode Handle is freed, all of the opcodes associated with the OpCode
Handle are also freed.
If OpCodeHandle is NULL, then ASSERT().
@param[in] OpCodeHandle The handle to the buffer of opcodes.
**/
VOID
EFIAPI
HiiFreeOpCodeHandle (
VOID *OpCodeHandle
);
이제 StartOpCodeHandle 과 EndOpCodeHandle 에 올바른 데이터를 만들어야 한다. 첫번째로 우리는 lable IFR을 추가해야 한다. 이 작업은 HiiCreateGuidOpCode 함를 활용한다.
/**
Create EFI_IFR_GUID opcode.
If OpCodeHandle is NULL, then ASSERT().
If Guid is NULL, then ASSERT().
If OpCodeSize < sizeof (EFI_IFR_GUID), then ASSERT().
@param[in] OpCodeHandle Handle to the buffer of opcodes.
@param[in] Guid Pointer to EFI_GUID of this guided opcode.
@param[in] GuidOpCode Pointer to an EFI_IFR_GUID opcode. This is an
optional parameter that may be NULL. If this
parameter is NULL, then the GUID extension
region of the created opcode is filled with zeros.
If this parameter is not NULL, then the GUID
extension region of GuidData will be copied to
the GUID extension region of the created opcode.
@param[in] OpCodeSize The size, in bytes, of created opcode. This value
must be >= sizeof(EFI_IFR_GUID).
@retval NULL There is not enough space left in Buffer to add the opcode.
@retval Other A pointer to the created opcode.
**/
UINT8 *
EFIAPI
HiiCreateGuidOpCode (
IN VOID *OpCodeHandle,
IN CONST EFI_GUID *Guid,
IN CONST VOID *GuidOpCode OPTIONAL,
IN UINTN OpCodeSize
)
이 함수는 opcode 버퍼에 대한 포인터를 반환하는데 우리의 경우에는 EFI_IFR_GUID_LABEL opcode로 시작하는 버퍼가 된다. 따라서 버퍼를 EFI_IFR_GUID_LABEL 구조로 캐스팅하고 올바른 데이터로 채워야 한다. 만약 HiiCreateGuidOpCode 가 NULL 을 반환한다면 오류가 발생한 것이다.
이 코드에서 gEfiIfrTianoGuid 를 사용하므로 코드에 적절한 헤더 파일 정보(#include <Guid/MdeModuleHii.h>)와 *.inf 파일의 Guid 섹션에 정보를 추가해야 한다.
[Guids]
gEfiIfrTianoGuid
마지막 작업은 IFR 코드에 새 코드를 포함하는 것이다. 따라서 우리는 Label 외에도 StartOpCodeHandle 이 참조하는 버퍼에 실제 새로운 IFR 코드를 추가해야 한다. HiiCreateTextOpcode 함수를 사용해 VFR text 요소를 추가해보자.
/**
Create EFI_IFR_TEXT_OP opcode.
If OpCodeHandle is NULL, then ASSERT().
@param[in] OpCodeHandle Handle to the buffer of opcodes.
@param[in] Prompt String ID for Prompt.
@param[in] Help String ID for Help.
@param[in] TextTwo String ID for TextTwo.
@retval NULL There is not enough space left in Buffer to add the opcode.
@retval Other A pointer to the created opcode.
**/
UINT8 *
EFIAPI
HiiCreateTextOpCode (
IN VOID *OpCodeHandle,
IN EFI_STRING_ID Prompt,
IN EFI_STRING_ID Help,
IN EFI_STRING_ID TextTwo
);
보다시피 이 함수는 새 text 요소에 대해 EFI_STRING_ID 가 필요하다. 우리는 이 요소를 동적으로 생성하므로 HII 데이터베이스에 새 문자열도 동적으로 추가해보자.
/**
This function create a new string in String Package or updates an existing
string in a String Package. If StringId is 0, then a new string is added to
a String Package. If StringId is not zero, then a string in String Package is
updated. If SupportedLanguages is NULL, then the string is added or updated
for all the languages that the String Package supports. If SupportedLanguages
is not NULL, then the string is added or updated for the set of languages
specified by SupportedLanguages.
If HiiHandle is NULL, then ASSERT().
If String is NULL, then ASSERT().
@param[in] HiiHandle A handle that was previously registered in the
HII Database.
@param[in] StringId If zero, then a new string is created in the
String Package associated with HiiHandle. If
non-zero, then the string specified by StringId
is updated in the String Package associated
with HiiHandle.
@param[in] String A pointer to the Null-terminated Unicode string
to add or update in the String Package associated
with HiiHandle.
@param[in] SupportedLanguages A pointer to a Null-terminated ASCII string of
language codes. If this parameter is NULL, then
String is added or updated in the String Package
associated with HiiHandle for all the languages
that the String Package supports. If this
parameter is not NULL, then then String is added
or updated in the String Package associated with
HiiHandle for the set oflanguages specified by
SupportedLanguages. The format of
SupportedLanguages must follow the language
format assumed the HII Database.
@retval 0 The string could not be added or updated in the String Package.
@retval Other The EFI_STRING_ID of the newly added or updated string.
**/
EFI_STRING_ID
EFIAPI
HiiSetString (
IN EFI_HII_HANDLE HiiHandle,
IN EFI_STRING_ID StringId OPTIONAL,
IN CONST EFI_STRING String,
IN CONST CHAR8 *SupportedLanguages OPTIONAL
)
/**
Append raw opcodes to an OpCodeHandle.
If OpCodeHandle is NULL, then ASSERT().
If RawBuffer is NULL, then ASSERT();
@param[in] OpCodeHandle Handle to the buffer of opcodes.
@param[in] RawBuffer Buffer of opcodes to append.
@param[in] RawBufferSize The size, in bytes, of Buffer.
@retval NULL There is not enough space left in Buffer to add the opcode.
@retval Other A pointer to the appended opcodes.
**/
UINT8 *
EFIAPI
HiiCreateRawOpCodes (
IN VOID *OpCodeHandle,
IN UINT8 *RawBuffer,
IN UINTN RawBufferSize
)