cuefignu-efi

How can I use UEFI Runtime Services?


I want to learn more about firmware developement. I already know how to write assembly programs for the old BIOS and now I wanted to start with UEFI. I managed to compile and emulate a Hello World program, and now I was trying to write a program which displays on the screen the current time using the Runtime Service GetTime(). However when I use this function the program hangs, as if it wasn't installed during PI. Here is the code:

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

//gBS: SystemTable->BootServices;
//gRS: SystemTable->RuntimeServices;

EFI_STATUS
efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE* systab)
{
    EFI_TIME* time;

    InitializeLib(image, systab);
    RT->GetTime(time, NULL);

    Print(L"Time %u\n", time->Hour);
    return EFI_SUCCESS;
}

Do you have any clue about what I've done wrong?

Here is the code I use to compile and emulate in case you need:

gcc -I/usr/include/efi -I/usr/include/efi/x86_64/  -fpic -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -maccumulate-outgoing-args -c main.c -o main.o

ld -shared -Bsymbolic -L/usr/lib -T/usr/lib/elf_x86_64_efi.lds /usr/lib/crt0-efi-x86_64.o main.o -o main.so -lgnuefi -lefi

objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym  -j .rel -j .rela -j .rel.* -j .rela.* -j .reloc --target efi-app-x86_64 --subsystem=10 main.so main.efi

uefi-run -b /usr/share/edk2-ovmf/x64/OVMF.fd -q /usr/bin/qemu-system-x86_64 main.efi

Solution

  • If you are using gnu-efi, use uefi_call_wrapper() to call UEFI functions.

    RT->GetTime(time, NULL);  // Program hangs
    uefi_call_wrapper(RT->GetTime, 2, time, NULL);  // Okay
    

    The reason is the different calling convention between UEFI (which uses Microsoft x64 calling convention) and Linux (which uses System V amd64 ABI). By default, gcc will generate the code in Linux format, so we need to explicitly tell it to generate it in UEFI format.

    You can see the difference by peforming an objdump.