Let's print GUID for all the protocols that are exist in our IMAGE_HANDLE
.
First understand what EFI_GUID
internally means in the edk2 codebase:
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiBaseType.h
///
/// 128-bit buffer containing a unique identifier value.
///
typedef GUID EFI_GUID;
GUID
structure is defined in https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Base.h :
///
/// 128 bit buffer containing a unique identifier value.
/// Unless otherwise specified, aligned on a 64 bit boundary.
///
typedef struct {
UINT32 Data1;
UINT16 Data2;
UINT16 Data3;
UINT8 Data4[8];
} GUID;
Fortunately we don't have to manually print all these fields by hand. Print
function has a format option %g
to print GUIDs, so we could simply print GUIDs with a code like this:
Print("GUID=%g\n", myGUID);
More information about Print
formating options can be found at https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PrintLib.h
We want to print EFI_GUID
field from the PROTOCOL_ENTRY
structure which are referred from the PROTOCOL_INTERFACE
structures. So we need to define both of these structures in our file for the same reason we've defined IHANDLE
earlier.
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;
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;
PROTOCOL_INTERFACE
structures that are connected to any HANDLE are interlinked with each other with a help of a LIST_ENTRY Link
field that connects them to a double linked list.
As you may remember LIST_ENTRY
is defined like this:
///
/// LIST_ENTRY structure definition.
///
typedef struct _LIST_ENTRY LIST_ENTRY;
///
/// _LIST_ENTRY structure definition.
///
struct _LIST_ENTRY {
LIST_ENTRY *ForwardLink;
LIST_ENTRY *BackLink;
};
Each of these fields inside these structure points to another LIST_ENTRY
structure that is placed in another PROTOCOL_INTERFACE
.
So this connection looks like this:
typedef struct { typedef struct {
UINTN Signature; UINTN Signature;
struct LIST_ENTRY { |---------> struct LIST_ENTRY {
LIST_ENTRY *ForwardLink; -----------------| LIST_ENTRY *ForwardLink;
LIST_ENTRY *BackLink; LIST_ENTRY *BackLink;
} Link; } Link;
IHANDLE *Handle; IHANDLE *Handle;
LIST_ENTRY ByProtocol; LIST_ENTRY ByProtocol;
PROTOCOL_ENTRY *Protocol; PROTOCOL_ENTRY *Protocol;
VOID *Interface; VOID *Interface;
LIST_ENTRY OpenList; LIST_ENTRY OpenList;
UINTN OpenListCount; UINTN OpenListCount;
} PROTOCOL_INTERFACE; } PROTOCOL_INTERFACE;
But in reality we want a pointer not to Link
field of another PROTOCOL_INTERFACE
structure, we want a pointer to another PROTCOL_INTERFACE
structure inself.
Therefore one more thing that we would need is a couple of macros:
#define offsetof(a,b) ((INTN)(&(((a*)(0))->b)))
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
These macros can be familiar to you if you've investigated linux kernel programming. This is where I got them from anyway.
contianer_of
macro helps to get a pointer to a structure if you have a pointer to one of its fields.
If you want to understand how it works internally in C language I suggest you to look at the https://stackoverflow.com/questions/15832301/understanding-container-of-macro-in-the-linux-kernel
With all of this information final code for our UefiMain
function would look like this:
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);
Print(L"Back Protocol Interface Link: %p\n", MyHandle->Protocols.BackLink);
Print(L"Forward Protocol Interface Link: %p\n", MyHandle->Protocols.ForwardLink);
LIST_ENTRY *FirstLink = MyHandle->Protocols.ForwardLink;
LIST_ENTRY *CurrentLink = FirstLink;
do {
PROTOCOL_INTERFACE* MyProtocolInterface = container_of(CurrentLink, PROTOCOL_INTERFACE, Link);
Print(L"\n");
Print(L"Current Link: %p\n", CurrentLink);
Print(L"Signature: %c %c %c %c\n", (MyProtocolInterface->Signature >> 0) & 0xff,
(MyProtocolInterface->Signature >> 8) & 0xff,
(MyProtocolInterface->Signature >> 16) & 0xff,
(MyProtocolInterface->Signature >> 24) & 0xff);
Print(L"Back Link: %p\n", MyProtocolInterface->Link.BackLink);
Print(L"Forward Link: %p\n", MyProtocolInterface->Link.ForwardLink);
Print(L"GUID=%g\n", MyProtocolInterface->Protocol->ProtocolID);
CurrentLink = MyProtocolInterface->Link.ForwardLink;
} while (CurrentLink != FirstLink);
return EFI_SUCCESS;
}
If we compile and run our app in OVMF (I hope at this time I don't need to repeat how to do it):
FS0:\> ImageHandle.efi
h n d l
Back Protocol Interface Link: 68D4320
Forward Protocol Interface Link: 6891520
Current Link: 6891520
p i f c
Back Link: 6891430
Forward Link: 6891B20
GUID=752F3136-4E16-4FDC-A22A-E5F46812F4CA
Current Link: 6891B20
p i f c
Back Link: 6891520
Forward Link: 68D4320
GUID=BC62157E-3E33-4FEC-9920-2D3B36D750DF
Current Link: 68D4320
p i f c
Back Link: 6891B20
Forward Link: 6891430
GUID=5B1B31A1-9562-11D2-8E3F-00A0C969723B
Current Link: 6891430
? ? ?
Back Link: 68D4320
Forward Link: 6891520
GUID=00000000-0000-0000-0000-000000000000
Let's find first GUID by executing grep on the edk2 source:
$ grep -i 752F3136 -r ./ --exclude-dir=Build
./MdePkg/Include/Protocol/ShellParameters.h: 0x752f3136, 0x4e16, 0x4fdc, { 0xa2, 0x2a, 0xe5, 0xf4, 0x68, 0x12, 0xf4, 0xca } \
./MdePkg/MdePkg.dec: gEfiShellParametersProtocolGuid = { 0x752f3136, 0x4e16, 0x4fdc, {0xa2, 0x2a, 0xe5, 0xf4, 0x68, 0x12, 0xf4, 0xca }}
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/ShellParameters.h
#define EFI_SHELL_PARAMETERS_PROTOCOL_GUID \
{ \
0x752f3136, 0x4e16, 0x4fdc, { 0xa2, 0x2a, 0xe5, 0xf4, 0x68, 0x12, 0xf4, 0xca } \
}
You can also see in in a https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec file:
## Include/Protocol/ShellParameters.h
gEfiShellParametersProtocolGuid = { 0x752f3136, 0x4e16, 0x4fdc, {0xa2, 0x2a, 0xe5, 0xf4, 0x68, 0x12, 0xf4, 0xca }}
The next two GUIDs you can find in UEFI the specification.
EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL
- When installed, the Loaded Image Device Path Protocol specifies the device path that was used when a PE/COFF image was loaded through the EFI Boot Service LoadImage().
#define EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID \
{0xbc62157e,0x3e33,0x4fec,\
{0x99,0x20,0x2d,0x3b,0x36,0xd7,0x50,0xdf}}
EFI_LOADED_IMAGE_PROTOCOL
- Can be used on any image handle to obtain information about the loaded image.
#define EFI_LOADED_IMAGE_PROTOCOL_GUID\
{0x5B1B31A1,0x9562,0x11d2,\
{0x8E,0x3F,0x00,0xA0,0xC9,0x69,0x72,0x3B}}
The last PROTOCOL_INTERFACE
structure doesn't have a valid "p i f c" signature (in my case it is 0x20 0x0E 0xED 0x06), so we don't need to look at its GUID.