【问题标题】:UEFI simple example of using ExitBootServices (with gnu-efi)UEFI 使用 ExitBootServices 的简单示例(使用 gnu-efi)
【发布时间】:2021-03-02 02:29:25
【问题描述】:

我正在尝试使用 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

谁能告诉我做错了什么? 谢谢。

【问题讨论】:

  • 您如何确定这发生在 ExitBootServices 之前?如果 ExitBootServices 成功,您的代码会做什么?
  • 我尝试注释代码(来自 ExitBootServices 调用),但仍然收到相同的错误。如果我留下代码,我会得到两个错误(第二个被 ExitBootServices 的“EFI_ERROR(status)”捕获)。第一个是在 bash 屏幕上运行 qemu。如果成功,我希望左上角的“示例”字符串不会出错。
  • 所以当我问“你的代码应该做什么”时,我的意思是一旦它到达函数的末尾,它会做什么。如果您已成功调用 ExitBootServices,那么您将没有上下文可返回。这是你正在构建的实际代码吗?如果是,为什么你的编译器没有告诉你在函数末尾错过了一个返回?
  • 您是否尝试将 uefi_call_wrapper 的第一个参数中的指针转换为 void *?这段代码在某个时候有效吗?或者这是您第一次尝试运行该程序?
  • 对不起,它似乎没有改变任何东西......问题确实出在 GetMemoryMap 调用上。如果我之前回来,没关系。但如果我在通话后立即返回,qemu 就会崩溃。

标签: qemu uefi gnu-efi


【解决方案1】:

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

您的代码还做出了一些不必要的、可能不正确的假设...

  1. 系统在内存映射中将有 16 个或更少的条目...
  2. UEFI 固件将返回您编译时针对的任何版本的 EFI_MEMORY_DESCRIPTOR...

GetMemoryMap 的定义行为允许我们解决问题 1,并且我们可以尽一切可能确保我们的代码与未来合理的 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;
}

【讨论】:

    猜你喜欢
    • 2015-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-08
    • 2019-04-04
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    相关资源
    最近更新 更多