Search code examples
uefignu-efi

Strange Invalid Parameter error returned in UEFI LocateProtocol and HandleProtocol


I recently started learning UEFI application programming, and I am using GNU-EFI and QEMU as the development platform, following https://wiki.osdev.org/GNU-EFI. However, I encounter some strange issue when trying to access the Simple Text Output Protocol. My code is the following:

#include <efi.h>
#include <efilib.h>

#define CHK_EFI_ERROR(status) \
    do { \
        if (EFI_ERROR(status)) { \
            Print(L"CHK_EFI_ERROR at %d failed with %r\n", __LINE__, status); \
        } \
    } while(0)

EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
    InitializeLib(ImageHandle, SystemTable);
    Print(L"Hello, world!\n");

    /* EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID */
    EFI_GUID *Protocol = &gEfiSimpleTextOutProtocolGuid;

    /* Use LocateProtocol */
    {
        EFI_STATUS status;
        EFI_SERIAL_IO_PROTOCOL *Interface = NULL;
        status = uefi_call_wrapper(ST->BootServices->LocateProtocol, 3
                                   Protocol, NULL, (void **)&Interface);
        CHK_EFI_ERROR(status);
    }

    /* Use HandleProtocol */
    {
        EFI_STATUS status;
        UINTN NoHandles;
        EFI_HANDLE *Buffer;
        status = uefi_call_wrapper(ST->BootServices->LocateHandleBuffer, 4,
                                   ByProtocol, Protocol, NULL, &NoHandles,
                                   &Buffer);
        CHK_EFI_ERROR(status);
        Print(L"NoHandles: %ld\n", NoHandles);
        Print(L"Buffer: %p\n", Buffer);

        for (UINTN i = 0; i < NoHandles; i++) {
            Print(L"Buffer[%ld]: %p\n", i, Buffer[i]);
            {
                EFI_STATUS status;
                EFI_SERIAL_IO_PROTOCOL *Interface = NULL;
                status = uefi_call_wrapper(ST->BootServices->HandleProtocol, 3
                                           Buffer[i], Protocol,
                                           (void **)&Interface);
                CHK_EFI_ERROR(status);
            }
        }
    }

    return EFI_SUCCESS;
}

The output is:

Hello, world!
CHK_EFI_ERROR at 27 failed with Invalid Parameter
NoHandles: 4
Buffer: 0x6A33C98
Buffer[0]: 0x6EB6798
CHK_EFI_ERROR at 50 failed with Invalid Parameter
Buffer[1]: 0x6EB5F18
CHK_EFI_ERROR at 50 failed with Invalid Parameter
Buffer[2]: 0x6AF2F98
CHK_EFI_ERROR at 50 failed with Invalid Parameter
Buffer[3]: 0x6A95C98
CHK_EFI_ERROR at 50 failed with Invalid Parameter

First, my program calls LocateProtocol to try to find a handle that supports the protocol. However, LocateProtocol returns "Invalid Parameter" error. Then, my program finds all handles that support the protocol, and call HandleProtocol on each handle. HandleProtocol returns "Invalid Parameter" error for every call.

The UEFI spec says "Invalid Parameter" error can only be returned when Interface is NULL or Protocol is NULL, but I am confident that neither is NULL.

Why is this happening? Is OVMF not implementing the specification correctly? Or did I forget to do something? One possibility I can think of is that maybe I need to load some drivers, but I don't know how to do so.


Solution

  • You are missing a comma behind the numarg parameter in

    status = uefi_call_wrapper(ST->BootServices->LocateProtocol, 3 //,
                                       Protocol, NULL, (void **)&Interface);
    

    and

    status = uefi_call_wrapper(ST->BootServices->HandleProtocol, 3 //,
                                               Buffer[i], Protocol,
                                               (void **)&Interface);
    

    One more thing you are searching for gEfiSimpleTextOutProtocolGuid (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) but you are using EFI_SERIAL_IO_PROTOCOL.