UEFI使用ExitBootServices的简单示例(使用gnu-efi)

时间:2016-09-09 08:37:27

标签: qemu uefi gnu-efi

我正在尝试使用gnu-efi编写一个hello world类型的程序,但没有引导服务,因为它们在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 个答案:

答案 0 :(得分:1)

看起来您的主要问题是您忘记将参数的数量传递给uefi_call_wrapper以调用GetMemoryMap ...传入指针(大数字...远大于5)可能会破坏UEFI固件仿真和QEMU扩展。您的ExitBootServices调用将因同样的原因而失败,您没有传入参数的数量。

您的代码也会做出一些不必要的,可能是错误的假设...

  1. 系统在内存映射中有16个或更少的条目......
  2. UEFI固件将返回您编译的任何版本的EFI_MEMORY_DESCRIPTOR ...
  3. GetMemoryMap的已定义行为允许我们解决问题1,并且我们可以尽一切可能确保我们的代码与新版EFI_MEMORY_DESCRIPTOR的UEFI的未来合理修订版向前兼容。

    以下是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;
    }