C 언어에는 클래스와 같은 키워드가 없지만, 비슷하게는 만들 수 있다. struct를 이용하여 class 메소드를 함수에 대한 포인터 필드로 사용한다.
이제 SIMPLE_CLASS_PROTOCOL 유형의 정의를 포함하는 헤더 파일 만들어야 한다.
일반적으로 패키지에는 폴더의 프로토콜에 대한 헤더가 포함된다.
따라서 UefiLessonsPkg/Include/Protocol/SimpleClass.h 헤더 파일을 생성한다.
SIMPLE_CLASS_PROTOCOL 유형을 *.c 파일에 포함시킨다.
다른 모듈에서 프로토콜을 이용할 수 있게 하려면, 시스템(Protocol database)에 프로토콜을 설치해 주어야 한다.
이를 위해서 InstallProtocolInterface 함수를 사용한다.
EDKII 코드베이스 전체에서 InstallProtocolInterface를 찾을 수 있지만, 동일한 UEFI 스펙에 따르면, 사실 이건 조금 구식 API이다. 여기서 제안하는 API는 다음과 같다.
우리 예제에서 드라이버를 작성할 때, 드라이버 INF 파일에서 집입점으로 선언할 SimpleClassProtocolDriverEntryPoint 함수에서 InstallMultipleProtocolInterfaces를 사용하겠다.
여기에 mSimpleClasshandle의 주소를 출력하도록 Print문도 추가해준다.
mSimpleClassHandle 및 mSimpleClass는 동일한 *.c 파일(따라서, 앞에 m이 접두사로 쓰인)에서 선언되며, gSimpleClassProtocolGuid(g는 전역을 의미) 패키지 DEC 파일에서 선언하므로, 다른 모듈이 이 파일을 포함하고 프로토콜을 사용할 수 있다.
최종적으로, 이를 INF 파일에 넣어준다.
SimpleClassUser
지금부터는 우리가 프로토콜을 사용할 애플리케이션을 작성할 것이다. 이 프로토콜은 앱 이미지 핸들에설치되지 않았으므로, 먼저 LocateHandleBuffer API로 해당 프로토콜이 있는 모든 핸들을 찾은 다음, 각 핸들에서 OpenProtocol을 호출해야 한다.
이는 다른 EDKII 패키지의 다른 프로토콜과 다르지 않다.
프로토콜을 다음과 같이 사용한다.
현재 숫자 값을 가져와 출력
숫자 값에 5를 더하고 세팅
다시 현재 값을 가져와 출력
프로토콜 유저의 앱 INF 파일은 매우 간단하다.
패키지의 [Components] 섹션의 SimpleClassProtocol과 SimpleClassUser를 모두 추가한다.
Testing
이제 빌드하고 결과를 QEMU 공유 폴더에 복사하여 OVMF를 실행한다.
SimpleClassUser를 먼저 실행하면, 오류가 발생할 것이다.
에러 코드에서도 보이듯이, 아직 우리가 만든 프로토콜이 시스템이 설치되지 않았다. 이를 설치하고 애플리케이션을 실행시키면 된다.
dh를 사용하여 드라이버 및 해당 프로토콜에 대한 핸들을 볼 수 있다.
(핸들 번호는 본인 환경에 맞게 바꿔 생각해주면 된다.)
SimpleClassUser.efi를 다시 실행하면, 숫자가 5에서 10으로 증가한다. 변수가 SimpleClassUser.efi 외부에 저장된다는 것을 의미한다. 예상해보면, 이는 핸들 C7이 있는 프로토콜에 저장된다는 것을 짐작할 수 있다.
load 명령어를 다시 사용하면, SimpleClassProtocol.efi 드라이버의 사본을 로드할 수 있다.
이제 시스템 C7 및 C9에는 두 개의 프로토콜 핸들이 존재한다.
SimpleClassUser.efi 애플리케이션을 다시 실행하면, 시스템 2개의 또 다른 버전이 존재하는 것을 볼 수 있다.
아직 이 드라이버에는 unload 기능을 구현하지 않았으므로 언로드할 수는 없다.
여기까지의 완성 코드
IMAGE_UNLOAD
이제 프로토콜 드라이브에서 언로드 기능을 구현하도록 하자.
SimpleClassProtocol.c에는 다음 함수를 추가해준다.
테스트 해보자. 다시 드라이버를 빌드하여 로드하고 앱을 실행한다.
프로토콜 핸들에 대한 언로드를 수행할 수 없었지만, 드라이버에 대한 unload 기능을 구현해주었으니 언로드를 시도한다.
하지만, 아직 핸들(C7)이 남아있을 것이다.
이제 애플리케이션을 실행하려하면 예외가 발생한다. OpenProtocol 호출은 프로토콜에 대해 동일한 주소를 제공하나, 이제 이 메모리는 해제되고 SimpleClass -> GetNumber와 같은 프로토콜 함수에 대한 호출은 시스템에서 충돌이 발생한다.
이 문제를 해결하려면 드라이버 언로드시 시스템에서 프로토콜을 제거해야 한다.
Protocol Interface 제거하기
프로토콜 인터페이스 설치와 마찬가지로 UEFI API에는 프로토콜 제거를 위한 두 가지 함수가 존재한다. 한 가지는 더 이상 사용되지 않고(UninstallProtocolInterface), 하나는 새로운 함수(UninstallMultipleProtocolInterface)이다.
EFI_BOOT_SERVICES.InstallProtocolInterface()
Summary
Installs a protocol interface on a device handle. If the handle does not exist, it is created and added to the
list of handles in the system.
Prototype
typedef
EFI_STATUS
(EFIAPI *EFI_INSTALL_PROTOCOL_INTERFACE) (
IN OUT EFI_HANDLE *Handle,
IN EFI_GUID *Protocol,
IN EFI_INTERFACE_TYPE InterfaceType,
IN VOID *Interface
);
Parameters
Handle A pointer to the EFI_HANDLE on which the interface is to be installed. If *Handle is NULL on input,
a new handle is created and returned on output. If *Handle is not NULL on input, the protocol is
added to the handle, and the handle is returned unmodified.
If *Handle is not a valid handle, then EFI_INVALID_PARAMETER is returned.
Protocol The numeric ID of the protocol interface.
InterfaceType Indicates whether Interface is supplied in native form.
Interface A pointer to the protocol interface. The Interface must adhere to the structure defined by Protocol.
NULL can be used if a structure is not associated with Protocol.
Description
The InstallProtocolInterface() function installs a protocol interface (a GUID/Protocol Interface structure pair) on a device handle.
The same GUID cannot be installed more than once onto the same handle.
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 mSimpleClassHandle = NULL;
EFI_STATUS
EFIAPI
SimpleClassProtocolDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
Print(L"Hello from SimpleClassProtocol driver");
EFI_STATUS Status = gBS->InstallMultipleProtocolInterfaces(
&mSimpleClassHandle,
&gSimpleClassProtocolGuid,
&mSimpleClass,
NULL
);
if (!EFI_ERROR(Status))
Print(L", handle=%p\n", mSimpleClassHandle);
else
Print(L"\n", mSimpleClassHandle);
return Status;
}
$ vi UefiLessonsPkg/SimpleClassProtocol/SimpleClassProtocol.inf
---
[Defines]
INF_VERSION = 1.25
BASE_NAME = SimpleClassProtocol
FILE_GUID = 51d6a90a-c021-4472-b2c1-5fdd1b7f2196
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = SimpleClassProtocolDriverEntryPoint <---- entry point that actually calls 'InstallMultipleProtocolInterfaces'
[Sources]
SimpleClassProtocol.c
[Packages]
MdePkg/MdePkg.dec
UefiLessonsPkg/UefiLessonsPkg.dec <---- we need to add this to get access to the GUID value 'gSimpleClassProtocolGuid'
[Protocols]
gSimpleClassProtocolGuid <---- protocols that are used in module
[LibraryClasses]
UefiDriverEntryPoint
UefiLib
$ cat UefiLessonsPkg/SimpleClassUser/SimpleClassUser.c
---
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/SimpleClass.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINTN HandleCount;
EFI_HANDLE* HandleBuffer;
UINTN Index;
SIMPLE_CLASS_PROTOCOL* SimpleClass;
EFI_STATUS Status = gBS->LocateHandleBuffer (
ByProtocol,
&gSimpleClassProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
Print(L"Error! Can't find any handle with gSimpleClassProtocolGuid: %r\n", Status);
return Status;
}
for (Index = 0; Index < HandleCount; Index++) {
Print(L"Handle = %p\n", HandleBuffer[Index]);
Status = gBS->OpenProtocol(
HandleBuffer[Index],
&gSimpleClassProtocolGuid,
(VOID **)&SimpleClass,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR(Status)) {
<...>
} else {
Print(L"Error! Can't open SimpleClass protocol: %r\n", Status);
}
}
return Status;
}
UINTN Number;
Status = SimpleClass->GetNumber(&Number);
if (!EFI_ERROR(Status)) {
Print(L"Number before=%d\n", Number);
} else {
Print(L"Error! Can't get number: %r\n", Status);
}
Status = SimpleClass->SetNumber(Number+5);
if (EFI_ERROR(Status))
Print(L"Error! Can't set number: %r\n", Status);
Status = SimpleClass->GetNumber(&Number);
if (!EFI_ERROR(Status)) {
Print(L"Number after=%d\n", Number);
} else {
Print(L"Error! Can't get number: %r\n", Status);
}
$ vi UefiLessonsPkg/SimpleClassUser/SimpleClassUser.inf
---
[Defines]
INF_VERSION = 1.25
BASE_NAME = SimpleClassUser
FILE_GUID = 466eed70-8def-44ea-9fb4-9012b266ec8c
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
[Sources]
SimpleClassUser.c
[Packages]
MdePkg/MdePkg.dec
UefiLessonsPkg/UefiLessonsPkg.dec <---- we need to add it to get access to the 'gSimpleClassProtocolGuid'
[Protocols]
gSimpleClassProtocolGuid <----- add used protocol guids
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
EFI_BOOT_SERVICES.UninstallProtocolInterface()
Summary:
Removes a protocol interface from a device handle. It is recommended that UninstallMultipleProtocolInterfaces() be used in place of
UninstallProtocolInterface().
Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_UNINSTALL_PROTOCOL_INTERFACE) (
IN EFI_HANDLE Handle,
IN EFI_GUID *Protocol,
IN VOID *Interface
);
Parameters:
Handle The handle on which the interface was installed. If Handle is not a
valid handle, then EFI_INVALID_PARAMETER is returned.
Protocol The numeric ID of the interface.
Interface A pointer to the interface. NULL can be used if a structure is not associated with Protocol.
Description:
The UninstallProtocolInterface() function removes a protocol interface from the handle on
which it was previously installed. The Protocol and Interface values define the protocol interface to
remove from the handle.
If the last protocol interface is removed from a handle, the handle is freed and is no longer valid.
EFI_BOOT_SERVICES.UninstallMultipleProtocolInterfaces()
Summary:
Removes one or more protocol interfaces into the boot services environment.
Prototype:
typedef
EFI_STATUS
EFIAPI *EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES) (
IN EFI_HANDLE Handle,
...
);
Parameters:
Handle The handle to remove the protocol interfaces from.
... A variable argument list containing pairs of protocol GUIDs and protocol interfaces.
Description:
This function removes a set of protocol interfaces from 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.UninstallProtocolInterface() to remove a protocol
interface from Handle. The pairs of arguments are removed in order from the variable argument list until
a NULL protocol GUID value is found
EFI_STATUS
EFIAPI
SimpleClassProtocolDriverUnload (
IN EFI_HANDLE ImageHandle
)
{
Print(L"Bye-bye from SimpleClassProtocol driver, handle=%p\n", mSimpleClassHandle);
EFI_STATUS Status = gBS->UninstallMultipleProtocolInterfaces(
mSimpleClassHandle,
&gSimpleClassProtocolGuid,
&mSimpleClass,
NULL
);
return Status;
}
FS0:\> load SimpleClassProtocol.efi
Hello from SimpleClassProtocol driver, handle=665F118
Image 'FS0:\SimpleClassProtocol.efi' loaded at 6645000 - Success
FS0:\> SimpleClassUser.efi
Handle = 665F118
Number before=0
Number after=5
FS0:\> dh
...
C6: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C7: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
FS0:\> load SimpleClassProtocol.efi
Hello from SimpleClassProtocol driver, handle=6636898
Image 'FS0:\SimpleClassProtocol.efi' loaded at 6630000 - Success
FS0:\> SimpleClassUser.efi
Handle = 665F118
Number before=5
Number after=10
Handle = 6636898
Number before=0
Number after=5
FS0:\> dh
...
C6: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C7: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
C8: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C9: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
FS0:\> unload c6
Unload - Handle [665F018]. [y/n]?
y
Bye-bye from SimpleClassProtocol driver, handle=665F118
Unload - Handle [665F018] Result Success.
FS0:\> dh
C8: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C9: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
FS0:\> SimpleClassUser.efi
Handle = 6636898
Number before=5
Number after=10