UEFI простой пример использования ExitBootServices (с gnu-efi)

Я пытаюсь написать программу типа hello world с использованием gnu-efi, но без Boot Services, потому что они становятся недоступными после ExitBootServices. Запись непосредственно в видеопамять перед вызовом ExitBootServices ничего не отображает.

По этой причине мне нужно вызвать ExitBootServices, для чего нужен Mapkey. MapKey предоставляется функцией GetMemoryMap. Но когда я это называю, мое приложение вылетает (я использую qemu).

Это мой код:

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

void write_string( int color, const char *string )
{
    volatile char *video = (volatile char*)0xB8000;
    while( *string != 0 )
    {
        *video++ = *string++;
        *video++ = color;
    }
}

EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
    EFI_LOADED_IMAGE *loaded_image = NULL;
    EFI_STATUS status;
    InitializeLib(ImageHandle, SystemTable);

    status = uefi_call_wrapper(SystemTable->BootServices->HandleProtocol,
        3, ImageHandle, &LoadedImageProtocol, (void **)&loaded_image);
    if (EFI_ERROR(status)) {
        Print(L"handleprotocol: %r\n", status);
        return EFI_SUCCESS;
    }

    /* GetMemoryMap */
    UINTN MemoryMapSize = sizeof(EFI_MEMORY_DESCRIPTOR) * 0x10;
    EFI_MEMORY_DESCRIPTOR *MemoryMap = AllocatePool (MemoryMapSize);
    UINTN MapKey = 0;
    UINTN DescriptorSize = 0;
    UINT32 DescriptorVersion = 0;
    status = uefi_call_wrapper(SystemTable->BootServices->GetMemoryMap,
        &MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);
    if (EFI_ERROR(status)) {
        Print(L"GetMemoryMap: %r\n", status);
        return EFI_SUCCESS;
    }

    /* ExitBootServices */
    status = uefi_call_wrapper(SystemTable->BootServices->ExitBootServices,
        ImageHandle, MapKey);
    if (EFI_ERROR(status)) {
        Print(L"ExitBootServices: %r\n", status);
        return EFI_SUCCESS;
    }

    write_string(0x07, "example");
}

Еще до выполнения ExitBootServices qemu вылетает с ошибкой:

qemu: fatal: Trying to execute code outside RAM or ROM at 0x00000000000b0000

Может кто-нибудь сказать, что не так с тем, что я делаю? Спасибо.

1 ответ

Решение

Похоже, ваша основная проблема в том, что вы забыли передать количество аргументов в uefi_call_wrapper для вашего вызова GetMemoryMap... Передача указателя (большое число... намного больше 5), вероятно, разрушает эмуляцию прошивки UEFI и QEMU по расширению. Ваш вызов ExitBootServices завершится ошибкой по той же причине, что вы не передали количество аргументов.

Ваш код также делает некоторые ненужные, возможно, неправильные предположения...

  1. Система будет иметь 16 или менее записей в карте памяти...
  2. Прошивка UEFI вернет любую версию EFI_MEMORY_DESCRIPTOR, с которой вы скомпилировали...

Определенное поведение GetMemoryMap позволяет нам решить проблему 1, и мы можем сделать все возможное, чтобы обеспечить прямую совместимость нашего кода с разумными будущими изменениями UEFI с новыми версиями EFI_MEMORY_DESCRIPTOR.

Вот пример на C получения карты памяти и выхода из служб загрузки:

#include <efi.h>

#define ErrorCheck(actual, expected) if(actual != expected) return actual

EFI_STATUS EFIAPI efi_main(EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE *systemTable)
{
    EFI_STATUS result;


   // TODO: Load anything that would change the memory map... (ex: OS kernal executable)


    UINTN mapSize = 0, mapKey, descriptorSize;
    EFI_MEMORY_DESCRIPTOR *memoryMap = NULL;
    UINT32 descriptorVersion;
    // Get the required memory pool size for the memory map...
    result = uefi_call_wrapper((void *)systemTable->BootServices->GetMemoryMap, 5, &mapSize, &memoryMap, NULL, &descriptorSize, NULL);
    ErrorCheck(result, EFI_BUFFER_TOO_SMALL);
    // Allocating the pool creates at least one new descriptor... for the chunk of memory changed to EfiLoaderData
    // Not sure that UEFI firmware must allocate on a memory type boundry... if not, then two descriptors might be created
    mapSize += 2 * descriptorSize;
    // Get a pool of memory to hold the map...
    result = uefi_call_wrapper((void *)systemTable->BootServices->AllocatePool, 3, EfiLoaderData, mapSize, (void **)&memoryMap);
    ErrorCheck(result, EFI_SUCCESS);
    // Get the actual memory map...
    result = uefi_call_wrapper((void *)systemTable->BootServices->GetMemoryMap, 5, &mapSize, &memoryMap, &mapKey, &descriptorSize, &descriptorVersion);
    ErrorCheck(result, EFI_SUCCESS);

    result = uefi_call_wrapper((void *)systemTable->BootServices->ExitBootServices, 2, imageHandle, mapKey);
    ErrorCheck(result, EFI_SUCCESS);


    // TODO: Boot Services no longer available. Do whatever with Runtime Services... (ex: start OS kernal executable)


    return EFI_SUCCESS;
}
Другие вопросы по тегам