Page 1 of 1

UEFI GetMemoryMap crashes

Posted: Sat Nov 05, 2016 5:33 pm
by TenormanChilli
Hi everyone :)
I want to obtain the uefi memory map, so I can implement paging for my OS(for x86 architecture):

Code: Select all

#include <efi.h>
void waitForUser(EFI_SYSTEM_TABLE *st);

EFI_STATUS
efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
    EFI_SYSTEM_TABLE *st = SystemTable;
    EFI_STATUS result; 
    UINTN mapSize = 0, mapKey, descriptorSize;
    EFI_MEMORY_DESCRIPTOR memoryMap;
    EFI_MEMORY_TYPE memType = EfiLoaderData;
    UINT32 descriptorVersion = 1;

    // FIRST CALL: Get the required memory pool size for the memory map
    result = st->BootServices->GetMemoryMap(&mapSize, 
      &memoryMap, NULL, &descriptorSize, NULL);
    if(result != EFI_BUFFER_TOO_SMALL)
    {
      st->ConOut->OutputString(st->ConOut, L"Getting memory map size failure\n\r");
      return result;
    }
    // Allocating the pool creates at least one new descriptor
    mapSize += 2 * descriptorSize;
    memoryMap.PhysicalStart = 0;
    VOID *physStr;
    result = st->BootServices->AllocatePool(memType, mapSize, &(physStr));
    if(result != EFI_SUCCESS)
    {
      st->ConOut->OutputString(st->ConOut, L"Allocating pool failure\n\r");
      return result;
    }

    //SECOND CALL: get actual memory map
    st->ConOut->OutputString(st->ConOut, L"Getting memory map:\n\r");
    waitForUser(st);
    result = st->BootServices->GetMemoryMap(&mapSize, 
      &memoryMap, &mapKey, &descriptorSize, &descriptorVersion);
    if(result != EFI_SUCCESS)
    {
      st->ConOut->OutputString(st->ConOut, L"obtaining map failure\n\r");
      return result;
    }
    else
      st->ConOut->OutputString(st->ConOut, L"obtaining map success\n\r");
    return result;
}

void waitForUser(EFI_SYSTEM_TABLE *st)
{
  EFI_STATUS Status;
  EFI_INPUT_KEY Key;
  //emty console input buffer to flush any keystrokes entered
  //before this point
  Status = st->ConIn->Reset(st->ConIn, FALSE);
    if (EFI_ERROR(Status))
        return;
  //wait for key to become available
  while ((Status = st->ConIn->ReadKeyStroke(st->ConIn, &Key)) == EFI_NOT_READY);
}
While the first call of GetMemoryMap() works fine, the second one crashes whole qemu and I get the following message:
qemu: fatal: Trying to execute code outside RAM or ROM at 0x000b0000

EAX=00000000 EBX=3ffc10f0 ECX=3ffafd98 EDX=00000000
ESI=3efb0d90 EDI=3ffafeac EBP=3ffafdac ESP=3ffafd50
EIP=000afff8 EFL=00000246 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0008 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0010 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0008 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0008 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0008 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0008 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 3ff7ed90 0000003f
IDT= 3fc5d010 000007ff
CR0=40000033 CR2=00000000 CR3=00000000 CR4=00000648
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000000 CCD=00000000 CCO=ADDB
EFER=0000000000000000
FCW=027f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
As far as I've checked no processor exception is raised during this execution.
I'm using gnu-efi, ovmf and qemu. I'll be really grateful for any tip how to fix this.

Re: UEFI GetMemoryMap crashes

Posted: Sat Nov 05, 2016 8:26 pm
by thepowersgang
At a guess, your error is not setting the `mapKey` variable before passing it to the second GetMemoryMap call. That's the most major difference between your code and mine.

Re: UEFI GetMemoryMap crashes

Posted: Sat Nov 05, 2016 8:35 pm
by Brendan
Hi,
TenormanChilli wrote:qemu: fatal: Trying to execute code outside RAM or ROM at 0x000b0000
That's usually caused by something messing up your stack, the CPU returning from a function to "dodgy address", and then the CPU executing garbage/data/zeros starting from "dodgy address" and going up to "1st byte that happens to be outside RAM or ROM".

I'd assume that you're supposed to be using:

Code: Select all

EFI_MEMORY_DESCRIPTOR *memoryMapPtr;
..and:

Code: Select all

result = st->BootServices->AllocatePool(memType, mapSize, &memoryMapPtr);
..and:

Code: Select all

result = st->BootServices->GetMemoryMap(&mapSize, memoryMapPtr, &mapKey, &descriptorSize, &descriptorVersion);
..and because you're not using a pointer to the memory map you're telling UEFI to copy the memory map into a few bytes on your stack that isn't large enough, so it overwrites your stack.

Also note that every UEFI function can allocate memory; including the UEFI functions you're using to do console output; so your "mapSize" may be too small by the time you try to get the memory map. Also UEFI can merge areas of the same type; so allocating a pool for the memory map might not increase the size of the memory map and your "mapSize" may be larger than necessary when you actually get the memory map. Finally; UEFI functions (including those for console output) can change the memory map after you get the memory map but before you call "ExitBootServices", so if/when you do get a memory map it will be mostly useless anyway.

The correct way to avoid all those problems is to use a loop that retries (and frees the old memory and allocates new memory) until UEFI stops saying "EFI_BUFFER_TOO_SMALL"; and then call "ExitBootServices" as soon as you successfully get a memory map (making sure that no UEFI functions are called between getting the memory map and calling "ExitBootServices") and then (just to make sure) retrying the whole thing again if "ExitBootServices" returns "EFI_INVALID_PARAMETER" (if the "mapKey" is wrong).

Of course after that you can't use most UEFI functions (and nothing beyond your control can modify the memory map you got).


Cheers,

Brendan

Re: UEFI GetMemoryMap crashes

Posted: Sun Nov 06, 2016 8:24 am
by TenormanChilli
Thank you Brendan! It's all working now