64. checkbox를 가진 HII폼 만들기

파트 2: efivarstore가 올바르게 작동하는 데 필요한 코드 작성하기

UEFI 변수 생성하기

우리의 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_STATUS
EFIAPI
HIIFormCheckboxEntryPoint (
  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 패키지가 있는 모든 PackageListDEVICE_PATH 패키지도 있음을 알 수 있다.

예를 들어,

이것은 실수가 아니다. HII 하위 시스템이 올바르게 작동하려면 폼과 함께 Device Path를 제공해야 한다.

이것이 checkbox 폼이 올바르게 작동하기 위해 해야할 마지막 일이다.

기억하겠지만 DevicePath 노드에는 다음과 같은 몇 가지 가능한 type이 있다.

그리고 모든 type에는 여러 하위 type이 있다. type + 하위 type의 조합은 뒤에 오는 실제 데이터의 구조를 정의한다. 모든 데이터 형식은 UEFI 스펙에 의해 엄격하게 정의된다. 그러나 일부 유형은 공급업체가 장치 경로에서 자체 사용자 지정 구조를 제공할 수 있는 가능성을 남긴다. 물론 EDKII lib는 특수 경로를 출력할 수 없지만 최소한 실패하지는 않을 것이다. 라이브러리는 EFI_DEVICE_PATH_PROTOCOL.Length 필드를 사용하여 알 수 없는 DeviceNode를 건너뛸 수 있기 때문이다.

이 모든 것은 다음과 같은 특별한 하위 type으로 인해 가능해진다.

모든 경우에 공급업체는 다음 데이터를 정의하는 특수 GUID를 제공한다.(https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/DevicePath.h).

EDKII 코드베이스의 폼에 대한 장치 경로는 이 방법으로 표시된다. 일반적으로 HARDWARE_DEVICE_PATH HW_VENDOR_DP는 해당 벤더 노드의 Type 및 SubType으로 사용된다.

이를 염두에 두고 우리의 고유한 Device Path를 만들어보자. 우리의 특수 Device Node에는 Guid = FORMSET_GUID가 있다. 이것은 필수 사항이 아니며 경로에 자체 GUID가 있을 수 있으므로 나는 불필요한 GUID를 생성하고 싶지 않다.

우리의 특별한 GUID는 중요한 데이터가 없는 장치 경로 노드를 정의하므로 다음과 같이 전체 Device Path 구조를 정적으로 정의할 수 있다.

이제 드라이버에 EFI_DEVICE_PATH_PROTOCOL을 설치해야 한다. 기억하겠지만 InstallProtocolInterface는 이제 사용되지 않는 것으로 간주되므로 InstallMultipleProtocolInterfaces를 사용하자.

따라서 드라이버에 대해 EFI_HANDLE mDriverHandle을 선언하고 Device Path를 설치해보자.

드라이버 언로드에서 프로토콜을 제거하는 것을 잊지 말자.

모든 것이 올바르게 작동하는지 확인해보자. dh 명령으로 이제 드라이버가 DevicePath 프로토콜이 설치된 다른 핸들을 생성하는 것을 볼 수 있다.

그리고 드라이버 언로드에서 두 핸들이 성공적으로 제거되는 것도 확인 가능하다.

이제 실제로 Device Path 패키지를 HII 데이터베이스에 추가해야 한다. HII 데이터베이스의 패키지 및 패키지 목록을 기억하고 있겠지만,

다행히 폼과 문자열을 등록하는 데 지속적으로 사용되었던 HiiLib의 HiiAddPackages를 Device Path 설치에도 쉽게 사용할 수 있다.

우리가 해야 할 일은 EFI_DEVICE_PATH_PROTOCOL이 설치된 EFI_HANDLE(우리가 이미 생성한 것)을 제공하는 것 뿐이다.

HiiAddPackages 설명을 다시 살펴보고 강조 표시된 인수에 주의하자.

https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Library/HiiLib.h https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiHiiLib/HiiLib.c

그 전에는 항상 DeviceHandle 인수 대신 NULL을 사용했었다. 그래서 우리가 해야 할 일은 이것을 바꾸는 것이다.

이제 폼이 올바르게 작동해야 한다. 하지만 테스트하기 전에 한 가지만 더 해보자. 우리의 정적 Device Path에는 FORMSET_GUID가 포함되어 있다. 따라서 GetVariable/SetVariable 코드를 약간 단순화할 수 있다.

EFI_GUID Guid = FORMSET_GUID 변수를 만들고 참조하는 대신 간단하게 &mHiiVendorDevicePath.VendorDevicePath.Guid를 사용할 수 있다.

이제 빌드하고 모든 것이 올바르게 작동하는지 확인할 차례이다.

기본적으로 checkbox는 체크되어 있지 않다. 이제 성공적으로 수정하고 저장할 수 있다. 또한 변경 사항이 영구적인지 확인할 수 있다. OVMF를 재부팅하고 드라이버를 다시 로드하면 이제 폼이 체크된 상태의 checkbox로 시작하는 것을 볼 수 있다.

그러나 드라이버를 수동으로 언로드하면 애플리케이션의 코드가 UEFI 변수를 삭제한다. 따라서 다시 로드하면 checkbox가 기본적으로 비어 있는 상태로 되돌아 간다.

Last updated