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.
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...
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;
}