맞춤형 UEFI 프로토콜을 만드는 방법

21809 단어 dxeuefiedk2tutorial
DXE 드라이버의 주요 작업은 프로토콜을 생성하고 설치하는 것입니다. 이번 포스팅에서는 EDK2에서 프로토콜을 생성하는 방법과 다른 UEFI 모듈에서 해당 프로토콜을 사용하는 방법에 대해 설명하겠습니다.

UEFI 프로토콜 설명



DXE 드라이버는 프로토콜을 생성하여 장치에 대한 액세스를 추상화합니다. 예를 들어 Tcg2Protocol을 사용하여 MMIO 또는 포트 IO와 같은 것을 통해 액세스하는 대신 TPM2.0 기능에 액세스할 수 있습니다. 프로토콜은 함수 포인터를 포함하는 데이터 구조이며 실제 함수의 내용은 해당 프로토콜을 설치한 DXE 드라이버에 있습니다. 프로토콜(데이터 구조) 자체도 해당 DXE 드라이버(일반적으로 .data 섹션)에 있습니다.

(EDK2 UEFI Driver Writer's Guide 3.6.2의 이미지)

각 프로토콜에는 자체 GUID가 있으며 다른 UEFI 모듈이 이 프로토콜을 찾는 데 사용됩니다. 여러 프로토콜을 그룹화할 수 있으며 이 그룹은 핸들로 식별됩니다. 따라서 먼저 gBS->LocateHandle를 사용하여 Handle을 찾은 다음 내부의 Protocol을 찾을 수 있습니다. 그러나 GUID로 프로토콜을 직접 찾는 데 사용하는 것이 더 간단할 수 있습니다gBS->LocateProtocol.

프로토콜은 모든 UEFI 모듈을 통해 액세스할 수 있어야 하므로 단일 데이터베이스에 저장됩니다. 이 데이터베이스에 프로토콜을 저장하기 위해서는 프로토콜을 생성한 DXE Driver가 내부gBS->InstallMultipleProtocolInterfaces를 사용해야 합니다.

구현


MyDxe2.inf



inf 파일부터 시작하겠습니다. 요점은 gEfiDummyProtocol[Protocols] 를 쓰는 것입니다. 이 DXE 드라이버가 생성하고 소비하는 프로토콜을 작성해야 합니다.

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = MyDxe2
  FILE_GUID                      = 9d46dccd-ea11-4808-93e9-9b7021889b2d
  MODULE_TYPE                    = DXE_DRIVER
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = MyDxeEntry

[Sources]
    MyDxe2.h
    MyDxe2.c

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

[LibraryClasses]
    UefiDriverEntryPoint
    UefiRuntimeServicesTableLib
    UefiBootServicesTableLib
    UefiLib
    PrintLib

[Protocols]
    gEfiDummyProtocolGuid   ## PRODUCES

[Depex]
    TRUE


MyDxe2.h



이것은 헤더 파일입니다. 이것은 주로 EFI_DUMMY_PROTOCOL 또는 DUMMY_FUNC1 와 같은 유형을 정의하므로 MyDxe2.c에서 사용할 수 있습니다. gEfiDummyProtocolGuid의 정의는 별도의 곳에서 정의한다.

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiBootServicesTableLib.h>

typedef EFI_STATUS (EFIAPI *DUMMY_FUNC1)();
typedef EFI_STATUS (EFIAPI *DUMMY_FUNC2)();

// e9811f61-14bd-4637-8d17-aec540d8f508
#define EFI_DUMMY_PROTOCOL_GUID \
    { 0xe9811f61, 0x14bd, 0x4637, { 0x8d, 0x17, 0xae, 0xc5, 0x40, 0xd8, 0xf5, 0x08 } }

extern EFI_GUID gEfiDummyProtocolGuid;

typedef struct _EFI_DUMMY_PROTOCOL {
    DUMMY_FUNC1 DummyFunc1;
    DUMMY_FUNC2 DummyFunc2;
} EFI_DUMMY_PROTOCOL;


MyDxe2.c



DXE 드라이버의 내용입니다. mDummy는 이 프로토콜의 실제 데이터 구조이며 이 프로토콜은 gBS->InstallMultipleProtocolInterfaces에 의해 설치됩니다. DummyFunc1DummyFunc2의 내용은 무엇이든 가능하지만 이 예에서는 "DummyFuncN called"메시지를 MyDxeStatus라는 UEFI 변수에 저장하므로 프로토콜이 실제로 올바르게 실행되고 있는지 확인할 수 있습니다. .

#include "MyDxe2.h"

UINT32 myvarSize      = 30;
CHAR8  myvarValue[30] = {0};
CHAR16 myvarName[30]  = L"MyDxeStatus";

// eefbd379-9f5c-4a92-a157-ae4079eb1448
EFI_GUID myvarGUID = { 0xeefbd379, 0x9f5c, 0x4a92, { 0xa1, 0x57, 0xae, 0x40, 0x79, 0xeb, 0x14, 0x48 }};

EFI_HANDLE mDummyHandle = NULL;

EFI_STATUS EFIAPI DummyFunc1() {
    AsciiSPrint(myvarValue, 18, "DummyFunc1 called");
    gRT->SetVariable(
            myvarName,
            &myvarGUID,
            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
            myvarSize,
            myvarValue);
    return EFI_SUCCESS;
}

EFI_STATUS EFIAPI DummyFunc2() {
    AsciiSPrint(myvarValue, 18, "DummyFunc2 called");
    gRT->SetVariable(
            myvarName,
            &myvarGUID,
            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
            myvarSize,
            myvarValue);
    return EFI_SUCCESS;
}

EFI_DUMMY_PROTOCOL mDummy = {
    DummyFunc1,
    DummyFunc2
};

EFI_STATUS EFIAPI MyDxeEntry(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
    gBS->InstallMultipleProtocolInterfaces(&mDummyHandle, &gEfiDummyProtocolGuid, &mDummy, NULL);

    EFI_TIME time;
    gRT->GetTime(&time, NULL);
    AsciiSPrint(myvarValue, 12, "%2d/%2d %2d:%2d", time.Month, time.Day, time.Hour, time.Minute);

    gRT->SetVariable(
            myvarName,
            &myvarGUID,
            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
            myvarSize,
            myvarValue);

    return EFI_SUCCESS;
}


MdeModulePkg.dec



다른 UEFI 모듈에서 gEfiDummyProtocolGuid를 사용하려면 MdeModulePkg.dec에 gEfiDummyProtocolGuid를 정의해야 합니다. MdeModulePkg.dec 내부에 아래와 같이 정의를 추가합니다.

[Protocols]
...
  ## MyDxe/MyDxe2.h
  gEfiDummyProtocolGuid = { 0xe9811f61, 0x14bd, 0x4637, { 0x8d, 0x17, 0xae, 0xc5, 0x40, 0xd8, 0xf5, 0x08 } }


MdeModulePkg.dsc




[Components]
  MdeModulePkg/MyDxe/MyDxe2.inf
  ...


빌드


ACTIVE_PLATFORM = MdeModulePkg/MdeModulePkg.dscConf/target.txt가 있는지 확인하고 빌드해 보겠습니다. 그런 다음 에서 설명한 것처럼 FFS 파일을 구성하고 BIOS 이미지에 FFS 파일을 추가하고 해당 이미지로 SPI 플래시 칩을 플래시합니다.

DummyProtocol 사용



다음으로 이 프로토콜을 사용하는 모듈을 작성해 봅시다. 출력을 쉽게 화면에 출력할 수 있도록 BDS 단계에서 USB 메모리에서 실행되는 UEFI 모듈을 작성할 것입니다.AppPkg/ 아래에 edk2/라는 폴더를 만들고 이러한 파일을 만듭니다.

AppPkg/
├── AppPkg.dec
├── AppPkg.dsc
└── Applications/
    └── UseDummyProtocol/
        ├── UseDummyProtocol.c
        └── UseDummyProtocol.inf


AppPkg.dec


FirstPkg는 아무 이름이나 될 수 있습니다.

[Defines]
  DEC_SPECIFICATION              = 0x00010005
  PACKAGE_NAME                   = FirstPkg
  PACKAGE_GUID                   = 75281f80-8657-4cf2-837f-04d73179c590
  PACKAGE_VERSION                = 0.1


AppPkg.dsc




[Defines]
  PLATFORM_NAME                  = FirstPkg
  PLATFORM_GUID                  = 7b86cb02-9fbb-4f77-8d93-35c8c90f2488
  PLATFORM_VERSION               = 0.1
  DSC_SPECIFICATION              = 0x00010005
  OUTPUT_DIRECTORY               = Build/FirstPkg$(ARCH)
  SUPPORTED_ARCHITECTURES        = IA32|X64
  BUILD_TARGETS                  = DEBUG|RELEASE|NOOPT
  DEFINE DEBUG_ENABLE_OUTPUT     = TRUE

[LibraryClasses]
  # Entry point
  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
  ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf

  # Common Libraries
  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
  BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
  !if $(DEBUG_ENABLE_OUTPUT)
    DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
    DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
  !else   ## DEBUG_ENABLE_OUTPUT
    DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
  !endif  ## DEBUG_ENABLE_OUTPUT

  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
  UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
  DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
  RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf

[Components]
  AppPkg/Applications/UseDummyProtocol/UseDummyProtocol.inf


UseDummyProtocol.inf




[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = UseDummyProtocol
  FILE_GUID                      = 5765e8dd-c97a-4ce2-891e-9e24b94d654b
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = UefiMain

[Sources]
  UseDummyProtocol.c

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

[LibraryClasses]
  UefiLib
  ShellCEntryLib
  UefiBootServicesTableLib
  UefiRuntimeServicesTableLib

[Protocols]
  gEfiDummyProtocolGuid  ## CONSUME


UseDummyProtocol.c



이것은 MyDxeStatus(DummyProtocol의 함수)를 호출하기 전과 후에 GetVariable에 의해 UEFI 변수DummyFunc1를 확인하고 있습니다. DummyFunc1MyDxeStatus의 값을 원래 이 DXE 드라이버가 실행될 때 설정된 현재 시간인 DummyFunc1 called로 변경합니다.

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <MyDxe/MyDxe2.h>
#include <Library/BaseMemoryLib.h>

EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
    EFI_DUMMY_PROTOCOL *DummyProtocol;
    gBS->LocateProtocol(&gEfiDummyProtocolGuid, NULL, (VOID**)&DummyProtocol);

    CHAR8 myvarValue[30];
    UINT64 myvarSize = 30;
    UINT32 Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
    EFI_GUID myvarGUID = { 0xeefbd379, 0x9f5c, 0x4a92, { 0xa1, 0x57, 0xae, 0x40, 0x79, 0xeb, 0x14, 0x48 }};

    Print(L"[Before Calling Protocol] MyDxeStatus:\r\n");
    gRT->GetVariable(
            L"MyDxeStatus",
            &myvarGUID,
            &Attributes,
            &myvarSize,
            myvarValue);

    AsciiPrint("%a\n\n", myvarValue);

    DummyProtocol->DummyFunc1();

    Print(L"[After Calling Protocol] MyDxeStatus:\r\n");
    gRT->GetVariable(
            L"MyDxeStatus",
            &myvarGUID,
            &Attributes,
            &myvarSize,
            myvarValue);

    AsciiPrint("%a\n", myvarValue);

    while(1);
    return 0;
}


빌드 및 실행



빌드하기 전에 ACTIVE_PLATFORM = AppPkg/AppPkg.dsc에서 Conf/target.txt로 변경합니다. 그런 다음 빌드하십시오.
이번에는 생성된 UseDummyProtocol.efi를 USB 메모리에 저장하겠습니다. USB 메모리에 디렉토리EFI/BOOT/를 만들고 UseDummyProtocol.efi를 bootx64.efi로 이름을 바꾸고 폴더에 넣습니다.

이전에 BIOS를 플래시한 PC에 USB를 연결하고 해당 컴퓨터를 부팅합니다. 아래 출력이 보이면 성공한 것입니다.

(문자열 길이를 지정하지 못했습니다 ...)

좋은 웹페이지 즐겨찾기