2

I'm trying to write a hello world kind of program using gnu-efi, but without the Boot Services, because they become unavailable after ExitBootServices. Writing directly to video memory before calling ExitBootServices does not display anything.

For this reason I need to call ExitBootServices, which needs a Mapkey. The MapKey is provided by the GetMemoryMap function. But when I call it, my application crashes (I am using qemu).

This is my code:

#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");
}

Even before executing ExitBootServices qemu crashes with the error:

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

Can anyone tell what is wrong with what I am doing? Thank you.

m4tx
  • 4,139
  • 5
  • 37
  • 61
andrei-n
  • 87
  • 2
  • 5
  • How do you determine that this happens before ExitBootServices? What is your code expected to do if it succeeds with ExitBootServices? – unixsmurf Sep 09 '16 at 23:25
  • I tried to comment the code (from the ExitBootServices call) and I still get the same error. If I leave the code I get two errors (the second is caught by "EFI_ERROR(status)" of ExitBootServices)). The first is on the bash screen running qemu. If it succeeds I expect no errors with the "example" string on the top left corner. – andrei-n Sep 10 '16 at 06:58
  • So when I ask "what is your code expected to do", I mean what is it expected to do once it hits the end of the function. If you have succeeded in your call to ExitBootServices, then you have no context to return to. Is this the actual code you're building, and if so, why is your compiler not telling you off for missing out a return at the end of the function? – unixsmurf Sep 13 '16 at 09:57
  • Have you tried casting the pointer in the first argument to uefi_call_wrapper to a void *? Did this code work at some point? Or is this your first attempt to run the program? – Joshua W Sep 23 '16 at 07:54
  • Sorry, it doesn't seem to change anything... The problem is really with the GetMemoryMap call. If I return before, it's ok. But if I return just after the call, qemu crashes. – andrei-n Sep 24 '16 at 05:39

1 Answers1

3

It looks like your main issue is that you forgot to pass in the number of arguments to uefi_call_wrapper for your call to GetMemoryMap... Passing in a pointer (big number... much larger than 5) is probably trashing the UEFI firmware emulation and QEMU by extension. Your ExitBootServices call will fail for the same reason, you did not pass in the number of arguments.

Your code also makes some unnecessary, possibly incorrect assumptions...

  1. The system will have 16 or fewer entries in the memory map...
  2. The UEFI firmware is going to return whatever version of EFI_MEMORY_DESCRIPTOR that you compiled against...

The defined behavior of GetMemoryMap allows us to resolve issue 1, and we can do everything possible to ensure that our code is forward compatible with reasonable future revisions to UEFI with new versions of EFI_MEMORY_DESCRIPTOR.

Here is an example in C of getting a memory map and exit boot services:

#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;
}
m4tx
  • 4,139
  • 5
  • 37
  • 61
Joshua W
  • 1,103
  • 12
  • 29