우리의 efivarstore에서는 특정 UEFI 변수를 참조한다. HII 하위 시스템은 이 변수에 대한 액세스를 관리할 수 있지만 이 변수가 시스템에 이미 존재해야한다.
그래서 드라이버 진입점에 이 변수를 생성해 보자. 재부팅 사이에 이 변수를 유지하려면 먼저 gRT->GetVariable 호출을 통해 변수가 이미 존재하는지 확인해야 한다.
변수가 없는 경우에만 기본값으로 변수를 만들자. 이 메커니즘은 EDKII 코드베이스에서 여러 번 사용된다.
#include <Library/UefiRuntimeServicesTableLib.h>#include <Library/BaseMemoryLib.h>#define FORMSET_GUID {0xef2acc91,0x7b50,0x4ab9,{0xab,0x67,0x2b,0x4,0xf8,0xbc,0x13,0x5e}}EFI_STATUSEFIAPIHIIFormCheckboxEntryPoint( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable){ EFI_STATUS Status; EFI_GUID Guid = FORMSET_GUID; UINTN BufferSize; UINT8 EfiVarstore; BufferSize =sizeof(UINT8); Status =gRT->GetVariable( L"CheckboxValue",&Guid,NULL,&BufferSize,&EfiVarstore);if(EFI_ERROR(Status)){ZeroMem(&EfiVarstore,sizeof(EfiVarstore)); Status =gRT->SetVariable( L"CheckboxValue",&Guid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,sizeof(EfiVarstore),&EfiVarstore);if(EFI_ERROR(Status)){Print(L"Error! Can't create variable! %r\n", Status);}} ...}
이제 드라이버 로드 시 변수가 어떻게 생성되는지 확인하자.
OVMF를 다시 시작하고 이제 로드된 드라이버 없이도 시스템에 변수가 있는지 확인할 수 있다.
그러나 우리의 드라이버는 부팅 프로세스에 포함되어 있지 않으며 선택적 드라이버이다. 따라서 사용자가 진정으로 드라이버에 연결된 모든 항목을 제거하려는 경우 UEFI 변수 삭제를 위해 HIIFormCheckboxUnload에 코드를 추가할 수 있다.
이제 코드를 검증해보자. 드라이버 언로드의 경우 Shell의 unload 명령을 사용할 수 있다.
도움말 메시지에서 볼 수 있듯이 언로드하려면 드라이버 핸들을 찾아야 한다. 이를 위해 dh 명령을 사용할 수 있다. 드라이버 로드 후에 실행하면 출력 끝에 다음과 같은 내용이 표시된다.
따라서 이 경우 드라이버 핸들 번호는 A9이다.
그 후 UEFI 변수가 삭제된다.
모두 잘 작동하지만 우리 폼은 어떨까? 불행하게도 checkbox가 제대로 작동하기에는 이것만으로는 충분하지 않다. 동일한 오류 메시지와 함께 폼이 제출되도록 허용하지 않는다. 한 가지를 더 해야 한다.
Device Path 추가
ShowHII.efi 애플리케이션의 출력을 보면 FORMS 패키지가 있는 모든 PackageList에 DEVICE_PATH 패키지도 있음을 알 수 있다.
예를 들어,
이것은 실수가 아니다. HII 하위 시스템이 올바르게 작동하려면 폼과 함께 Device Path를 제공해야 한다.
이것이 checkbox 폼이 올바르게 작동하기 위해 해야할 마지막 일이다.
기억하겠지만 DevicePath 노드에는 다음과 같은 몇 가지 가능한 type이 있다.
그리고 모든 type에는 여러 하위 type이 있다. type + 하위 type의 조합은 뒤에 오는 실제 데이터의 구조를 정의한다. 모든 데이터 형식은 UEFI 스펙에 의해 엄격하게 정의된다. 그러나 일부 유형은 공급업체가 장치 경로에서 자체 사용자 지정 구조를 제공할 수 있는 가능성을 남긴다. 물론 EDKII lib는 특수 경로를 출력할 수 없지만 최소한 실패하지는 않을 것이다. 라이브러리는 EFI_DEVICE_PATH_PROTOCOL.Length 필드를 사용하여 알 수 없는 DeviceNode를 건너뛸 수 있기 때문이다.
FS0:\> unload -?
Unloads a driver image that was already loaded.
UNLOAD [-n] [-v|-verbose] Handle
-n - Skips all prompts during unloading, so that it can be used
in a script file.
-v, -verbose - Dumps verbose status information before the image is unloaded.
Handle - Specifies the handle of driver to unload, always taken as hexadecimal number.
NOTES:
1. The '-n' option can be used to skip all prompts during unloading.
2. If the '-v' option is specified, verbose image information will be
displayed before the image is unloaded.
3. Only drivers that support unloading can be successfully unloaded.
4. Use the 'LOAD' command to load a driver.
EXAMPLES:
* To find the handle for the UEFI driver image to unload:
Shell> dh -b
* To unload the UEFI driver image with handle 27:
Shell> unload 27
typedef struct {
UINT8 Type; ///< 0x01 Hardware Device Path.
///< 0x02 ACPI Device Path.
///< 0x03 Messaging Device Path.
///< 0x04 Media Device Path.
///< 0x05 BIOS Boot Specification Device Path.
///< 0x7F End of Hardware Device Path.
UINT8 SubType; ///< Varies by Type
UINT8 Length[2];
} EFI_DEVICE_PATH_PROTOCOL;
#define HW_VENDOR_DP 0x04 // vendor subtype for the Hardware Device Path type
#define MSG_VENDOR_DP 0x0a // vendor subtype for the Messaging Device Path type
#define MEDIA_VENDOR_DP 0x03 // vendor subtype for the Media Device Path type
///
/// The Vendor Device Path allows the creation of vendor-defined Device Paths. A vendor must
/// allocate a Vendor GUID for a Device Path. The Vendor GUID can then be used to define the
/// contents on the n bytes that follow in the Vendor Device Path node.
///
typedef struct {
EFI_DEVICE_PATH_PROTOCOL Header;
///
/// Vendor-assigned GUID that defines the data that follows.
///
EFI_GUID Guid;
///
/// Vendor-defined variable size data.
///
} VENDOR_DEVICE_PATH;
EFI_BOOT_SERVICES.InstallMultipleProtocolInterfaces()
Summary:
Installs one or more protocol interfaces into the boot services environment.
Prototype:
typedef
EFI_STATUS
EFIAPI *EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES) (
IN OUT EFI_HANDLE *Handle,
...
);
Parameters:
Handle The pointer to a handle to install the new protocol interfaces on, or a pointer to NULL if a new handle is to be
allocated.
... A variable argument list containing pairs of protocol GUIDs and protocol interfaces.
Description:
This function installs a set of protocol interfaces into the boot services environment. It removes
arguments from the variable argument list in pairs. The first item is always a pointer to the protocol’s
GUID, and the second item is always a pointer to the protocol’s interface. These pairs are used to call the
boot service EFI_BOOT_SERVICES.InstallProtocolInterface() to add a protocol interface to
Handle. If Handle is NULL on entry, then a new handle will be allocated. The pairs of arguments are
removed in order from the variable argument list until a NULL protocol GUID value is found.
EFI_HANDLE mDriverHandle = NULL;
EFI_STATUS
EFIAPI
HIIFormCheckboxEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = gBS->InstallMultipleProtocolInterfaces(
&mDriverHandle,
&gEfiDevicePathProtocolGuid,
&mHiiVendorDevicePath,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
...
}
typedef struct _EFI_HII_DEVICE_PATH_PACKAGE {
EFI_HII_PACKAGE_HEADER Header;
//EFI_DEVICE_PATH_PROTOCOL DevicePath[];
} EFI_HII_DEVICE_PATH_PACKAGE;
Header The standard package header, where Header.Type = EFI_HII_PACKAGE_DEVICE_PATH.
DevicePath The Device Path description associated with the driver handle that provided the content sent to the HII database.
/**
Registers a list of packages in the HII Database and returns the HII Handle
associated with that registration. If an HII Handle has already been registered
with the same PackageListGuid and DeviceHandle, then NULL is returned. If there
are not enough resources to perform the registration, then NULL is returned.
If an empty list of packages is passed in, then NULL is returned. If the size of
the list of package is 0, then NULL is returned.
The variable arguments are pointers that point to package headers defined
by UEFI VFR compiler and StringGather tool.
#pragma pack (push, 1)
typedef struct {
UINT32 BinaryLength;
EFI_HII_PACKAGE_HEADER PackageHeader;
} EDKII_AUTOGEN_PACKAGES_HEADER;
#pragma pack (pop)
@param[in] PackageListGuid The GUID of the package list.
@param[in] DeviceHandle If not NULL, the Device Handle on which <--------- !!
an instance of DEVICE_PATH_PROTOCOL is installed.
This Device Handle uniquely defines the device that
the added packages are associated with.
@param[in] ... The variable argument list that contains pointers
to packages terminated by a NULL.
@retval NULL An HII Handle has already been registered in the HII Database with
the same PackageListGuid and DeviceHandle.
@retval NULL The HII Handle could not be created.
@retval NULL An empty list of packages was passed in.
@retval NULL All packages are empty.
@retval Other The HII Handle associated with the newly registered package list.
**/
EFI_HII_HANDLE
EFIAPI
HiiAddPackages (
IN CONST EFI_GUID *PackageListGuid,
IN EFI_HANDLE DeviceHandle OPTIONAL,
...
)
;