Let's create ImageHandle
app in our UefiLessonsPkg
.
First generate a random GUID.
$ uuidgen
b68d3472-70c7-4928-841b-6566032e0a23
And then create *.inf and *.c file based on the code in our HelloWorld
app:
$ cat UefiLessonsPkg/ImageHandle/ImageHandle.inf
[Defines]
INF_VERSION = 1.25
BASE_NAME = ImageHandle
FILE_GUID = b68d3472-70c7-4928-841b-6566032e0a23
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
[Sources]
ImageHandle.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
$ cat UefiLessonsPkg/ImageHandle/ImageHandle.c
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
return EFI_SUCCESS;
}
Add a newly created app to our UefiLessonsPkg/UefiLessonsPkg.dsc
file:
[Components]
...
+ UefiLessonsPkg/ImageHandle/ImageHandle.inf
Now let's get to work.
Our main function in a *.c file has 2 input parameters EFI_HANDLE ImageHandle
and EFI_SYSTEM_TABLE *SystemTable
:
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
We've already started to explore the EFI_SYSTEM_TABLE
. In this lesson let's look what ImageHandle
is about.
ImageHandle
parameter is a handle of an image for the program itself.
You can found a definition for a EFI_HANDLE
type in a https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiBaseType.h
///
/// A collection of related interfaces.
///
typedef VOID *EFI_HANDLE;
So it is simply a pointer to a void. So it could be a pointer to anything.
In a reality it is a pointer to this type of structure https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Core/Dxe/Hand/Handle.h :
#define EFI_HANDLE_SIGNATURE SIGNATURE_32('h','n','d','l')
///
/// IHANDLE - contains a list of protocol handles
///
typedef struct {
UINTN Signature;
/// All handles list of IHANDLE
LIST_ENTRY AllHandles;
/// List of PROTOCOL_INTERFACE's for this handle
LIST_ENTRY Protocols;
UINTN LocateRequest;
/// The Handle Database Key value when this handle was last created or modified
UINT64 Key;
} IHANDLE;
LIST_ENTRY is a double linked list. So with it we can traverse to any handle in the system. Sometimes it is called the Handle database.
In case you wonder how LIST_ENTRY is defined look at the https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Base.h :
///
/// LIST_ENTRY structure definition.
///
typedef struct _LIST_ENTRY LIST_ENTRY;
///
/// _LIST_ENTRY structure definition.
///
struct _LIST_ENTRY {
LIST_ENTRY *ForwardLink;
LIST_ENTRY *BackLink;
};
Each handle has a list of protocols attached to it. Protocol is like an interface - a set of functions and data fields. To track the list of protocols attached to a handle this field is used:
/// List of PROTOCOL_INTERFACE's for this handle
LIST_ENTRY Protocols;
It is another double linked list that points to these structures:
#define PROTOCOL_INTERFACE_SIGNATURE SIGNATURE_32('p','i','f','c')
///
/// PROTOCOL_INTERFACE - each protocol installed on a handle is tracked
/// with a protocol interface structure
///
typedef struct {
UINTN Signature;
/// Link on IHANDLE.Protocols
LIST_ENTRY Link;
/// Back pointer
IHANDLE *Handle;
/// Link on PROTOCOL_ENTRY.Protocols
LIST_ENTRY ByProtocol;
/// The protocol ID
PROTOCOL_ENTRY *Protocol;
/// The interface value
VOID *Interface;
/// OPEN_PROTOCOL_DATA list
LIST_ENTRY OpenList;
UINTN OpenListCount;
} PROTOCOL_INTERFACE;
Effectively PROTOCOL_INTERFACE structure is only a proxy that points to an actual PROTOCOL_ENTRY structrure which are exist in a separate protocol database.
#define PROTOCOL_ENTRY_SIGNATURE SIGNATURE_32('p','r','t','e')
///
/// PROTOCOL_ENTRY - each different protocol has 1 entry in the protocol
/// database. Each handler that supports this protocol is listed, along
/// with a list of registered notifies.
///
typedef struct {
UINTN Signature;
/// Link Entry inserted to mProtocolDatabase
LIST_ENTRY AllEntries;
/// ID of the protocol
EFI_GUID ProtocolID;
/// All protocol interfaces
LIST_ENTRY Protocols;
/// Registerd notification handlers
LIST_ENTRY Notify;
} PROTOCOL_ENTRY;
It all sounds a little bit complicated so it is better to see it in a picture: Important information from the picture:
- HANDLE 0 has PROTOCOL_INTERFACE 0 (->PROTOCOL_ENTRY 0) and PROTOCOL_INTERFACE 1 (->PROTOCOL_ENTRY 1)
- HANDLE 1 has PROTOCOL_INTERFACE 2 (->PROTOCOL_ENTRY 2) and PROTOCOL_INTERFACE 3 (->PROTOCOL_ENTRY 1)
- HANDLE 2 has PROTOCOL_INTERFACE 4 (->PROTOCOL_ENTRY 1) Besides that:
- All handles are interconnected with the help of
AllHandles
field in eachIHANDLE
structure - All protocols are interconnected with the help of
AllEntries
field in eachPROTOCOL_ENTRY
structure Red lines: - Each handle has a double linked list of all its PROTOCOL_INTERFACES, which internally connected to each other through the
Link
field Green lines: - Each protocol entry effectively knows all the users (=PROTOCOL_INTERFACES) of it.
Let's check that EFI_HANDLE ImageHandle
effectively is the IHANDLE
structure.
To check this we will cast ImageHandle
to IHANDLE
and print its signature.
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
typedef struct {
UINTN Signature;
/// All handles list of IHANDLE
LIST_ENTRY AllHandles;
/// List of PROTOCOL_INTERFACE's for this handle
LIST_ENTRY Protocols;
UINTN LocateRequest;
/// The Handle Database Key value when this handle was last created or modified
UINT64 Key;
} IHANDLE;
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
IHANDLE* MyHandle = ImageHandle;
Print(L"Signature: %c %c %c %c\n", (MyHandle->Signature >> 0) & 0xff,
(MyHandle->Signature >> 8) & 0xff,
(MyHandle->Signature >> 16) & 0xff,
(MyHandle->Signature >> 24) & 0xff);
return EFI_SUCCESS;
}
Don't blame me for redefinition of IHANDLE
structure. These structures (IHANDLE/PROTOCOL_ENTRY/PROTOCOL_INTERFACE) are private and not intended to be used outside of the MdeModulePkg
. Even https://github.com/tianocore/edk2/blob/master/StandaloneMmPkg/Core/StandaloneMmCore.h redifines IHANDLE/PROTOCOL_ENTRY/PROTOCOL_INTERFACE.
$ . edksetup.sh
$ build
$ cp Build/UefiLessonsPkg/RELEASE_GCC5/X64/ImageHandle.efi ~/UEFI_disk/
$ qemu-system-x86_64 -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \
-drive format=raw,file=fat:rw:~/UEFI_disk \
-nographic \
-net none
UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
FS0: Alias(s):HD0a1:;BLK1:
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
BLK0: Alias(s):
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
BLK2: Alias(s):
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
Press ESC in 4 seconds to skip startup.nsh or any other key to continue.
Shell> fs0:
FS0:\> ImageHandle.efi
Signature: h n d l