Как я могу использовать службы времени выполнения UEFI?

#c #uefi #gnu-efi

#c #uefi #gnu-efi

Вопрос:

Я хочу узнать больше о разработке встроенного ПО. Я уже знаю, как писать программы на ассемблере для старого BIOS, и теперь я хотел начать с UEFI. Мне удалось скомпилировать и эмулировать программу Hello World, и теперь я пытался написать программу, которая отображает на экране текущее время, используя службу времени выполнения getTime() . Однако, когда я использую эту функцию, программа зависает, как будто она не была установлена во время PI. Вот код:

 #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 %un", time->Hour);
    return EFI_SUCCESS;
}
 

У вас есть какие-либо подсказки о том, что я сделал не так?

Вот код, который я использую для компиляции и эмуляции на случай, если вам нужно:

 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
 

Ответ №1:

Если вы используете gnu-efi, используйте uefi_call_wrapper() для вызова функций UEFI.

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

Причина заключается в различном соглашении о вызовах между UEFI (который использует соглашение о вызовах Microsoft x64) и Linux (который использует System V amd64 ABI). По умолчанию gcc будет генерировать код в формате Linux, поэтому нам нужно явно указать ему, чтобы он генерировал его в формате UEFI.

Вы можете увидеть разницу, изменив objdump .

Ответ №2:

Я думаю, вы пропустили инициализацию RT.

 RT = SystemTable->RuntimeServices;
 

Ваш код очень похож на один из примеров (приведенный в разделе 4.7.1) Спецификации интерфейса унифицированного расширяемого микропрограммного обеспечения 2.6. Я сомневаюсь, что вы его не читали, но на всякий случай.

https://www.uefi.org/sites/default/files/resources/UEFI Spec 2_6.pdf

Комментарии:

1. Я думал, что InitializeLib() выполнила работу по инициализации RT. Я все равно попробую.