Search code examples
cuefignu-efiedk2

What is the correct way to load a UEFI protocol?


Apologies if this doesn't fit the StackOverflow format. I'm currently learning about writing UEFI applications. I've been reading the UEFI standard as well as a wealth of tutorials online and I can't seem to figure out what is the correct method for loading a UEFI protocol. All the tutorials seem to differ in the method that they use.

In many cases the tutorials follow the method of locating the handle and then iterating through the handle buffer to open the protocol. Example shown below:

EFI_HANDLE *handle_buffer;
UINTN handle_count;
EFI_GRAPHICS_OUTPUT_PROTOCOL *protocol;

// GNU-EFI wrapper.
status = uefi_call_wrapper(gBS->LocateHandleBuffer,
    5,
    ByProtocol,
    &gEfiGraphicsOutputProtocolGuid,
    NULL,
    &handle_count,
    &handle_buffer);

UINTN i = 0;
for(i = 0; i < handle_count; i++) {
    status = uefi_call_wrapper(gBS->OpenProtocol, 6,
        handle_buffer[i],
        &gEfiGraphicsOutputProtocolGuid,
        (VOID **)&protocol,
        ImageHandle,                        // from `efi_main`
        NULL,
        EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);

    if(status == EFI_SUCCESS) {
        break;
    }
}

Alternatively, it appears that you can just load the protocol directly using the LocateProtocol function. Example:

status = uefi_call_wrapper(gBS->LocateProtocol,
    3,
    &gEfiGraphicsOutputProtocolGuid,
    NULL,
    &graphics_service.protocol);

Both above examples work. I'm not sure I understand the significance of fetching the handle buffer prior to loading the protocol, as seen in some tutorials and online material. From searching through examples on Github, it looks like both are used interchangeably and so far I have found both work just as well. I understand that in the former method I will need to free the buffer since it is allocated from a pool, whereas in the latter there is no responsibility for this.

Can anyone point out which is the ideal method to load a UEFI protocol? Are there any issues with either method? Any help here would be appreciated.


Solution

  • For this specific case (trying to find "EFI_GRAPHICS_OUTPUT_PROTOCOL") if there is only one, or you only want one and don't care which one; then gBS->LocateProtocol is easier (and possibly faster?) but you could use gBS->LocateHandleBuffer too if you want.

    However; what if there's 5 video cards with 2 monitors each, and there's 10 different instances of "EFI_GRAPHICS_OUTPUT_PROTOCOL"? In this case maybe you want all of them (so you can set up a nice video mode on all 10 monitors), and maybe you want to search for the right one (e.g. maybe literally the one on the right-hand side of the user) and skip/avoid the other 9 wrong ones. For this you have to use gBS->LocateHandleBuffer.