EFI ExitBootServices() function crashes in QEMU

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
anatolik
Posts: 16
Joined: Sun Jul 07, 2013 7:29 pm

EFI ExitBootServices() function crashes in QEMU

Post by anatolik »

Hello, I am implementing a simple UEFI bootloader. Most EFI functions that I tried work me - I can clear Console, print text, successfully read files from system partition, allocate memory pages.

But if I try to read memory map and then call ExitBootServices() QEMU crashes for me:
qemu-system-x86_64: Trying to execute code outside RAM or ROM at 0x00000000000b0000

Here is my test-case program that uses GNU-EFI library

Code: Select all

#include "elf.h"
#include <efi.h>
#include <efilib.h>

EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SysTab) {
    InitializeLib(ImageHandle, SysTab);

    uint8_t buffer[4900];
    UINTN msize = sizeof(buffer);
    UINTN mkey = 0, dsize = 0;
    UINT32 dversion;
    SysTab->BootServices->GetMemoryMap(&msize, (EFI_MEMORY_DESCRIPTOR*)buffer, &mkey, &dsize, &dversion);

    EFI_STATUS st = SysTab->BootServices->ExitBootServices(ImageHandle, mkey);
    // QEMU crashes here ^^^^^^^^^^^^^^^^^^^^^^^^^^
    // qemu-system-x86_64: Trying to execute code outside RAM or ROM at 0x00000000000b0000

    Print(L"Press any key: %d\n", st);

    UINTN index;
    EFI_EVENT event = SysTab->ConIn->WaitForKey;
    SysTab->BootServices->WaitForEvent(1, &event, &index);

    return EFI_SUCCESS;
}

Here is a Makefile for it

Code: Select all

ARCH            = x86_64

OBJS            = bootloader.o
TARGET          = bootloader.efi

EFIINC          = /usr/include/efi
EFIINCS         = -I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol
EFILIB          = /usr/lib
EFI_CRT_OBJS    = $(EFILIB)/crt0-efi-$(ARCH).o
EFI_LDS         = $(EFILIB)/elf_$(ARCH)_efi.lds

CFLAGS          = $(EFIINCS) -fno-stack-protector -fPIC -fshort-wchar -mno-red-zone -Wall -std=c11

ifeq ($(ARCH),x86_64)
  CFLAGS += -DHAVE_USE_MS_ABI
endif

LDFLAGS         = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L $(EFILIB) $(EFI_CRT_OBJS) 

all: $(TARGET)

bootloader.so: $(OBJS)
	ld $(LDFLAGS) $(OBJS) -o $@ -lefi -lgnuefi

%.efi: %.so
	objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym  -j .rel -j .rela -j .reloc --target=efi-app-$(ARCH) $^ $@

hda/EFI/BOOT/BOOTX64.EFI: bootloader.efi
	mkdir -p hda/EFI/BOOT/
	cp bootloader.efi hda/EFI/BOOT/BOOTX64.EFI

run: hda/EFI/BOOT/BOOTX64.EFI
	qemu-system-x86_64 --bios OVMF_CODE.fd -hda fat:rw:hda -net none


I think I tried everything: GCC vs clang, compiling/running at Arch vs Debian, tried different OVMF version (from Arch, Debian, Fedora). The crash is still here. I am staring at the code and do not see any problems with it.

Does anybody have a clue what is going here and why ExitBootServices() crashes?
anatolik
Posts: 16
Joined: Sun Jul 07, 2013 7:29 pm

Re: EFI ExitBootServices() function crashes in QEMU

Post by anatolik »

I also tried to use AllocatedPool instead of static array but result is the same:

Code: Select all

#include "elf.h"
#include <efi.h>
#include <efilib.h>

EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SysTab) {
    InitializeLib(ImageHandle, SysTab);

    UINTN mkey = 0, dsize = 0;
    UINT32 dversion = 0;
    UINTN msize = 32768;
    EFI_MEMORY_DESCRIPTOR *mmap = AllocatePool(msize);
    SysTab->BootServices->GetMemoryMap(&msize, mmap, &mkey, &dsize, &dversion);

    EFI_STATUS st = SysTab->BootServices->ExitBootServices(ImageHandle, mkey);
    // QEMU crashes here ^^^^^^^^^^^^^^^^^^^^^^^^^^
    // qemu-system-x86_64: Trying to execute code outside RAM or ROM at 0x00000000000b0000

    FreePool(mmap);
    Print(L"Press any key: %d\n", st);

    UINTN index;
    EFI_EVENT event = SysTab->ConIn->WaitForKey;
    SysTab->BootServices->WaitForEvent(1, &event, &index);

    return EFI_SUCCESS;
}
I also tried to debug this crash and enabled QEMU cpu debugging. It looks like at some point IP jumps to address 0 and start executing it. Begginning of the address space is filled with zeros and x86 emulator executes as valid instructsions ('addl' if I remember).

But the it reaches 0x00000000000b0000 that is some kind of system memory (video?) and value of this memory cell is invalid instruction that crashes QEMU.
User avatar
zaval
Member
Member
Posts: 659
Joined: Fri Feb 17, 2017 4:01 pm
Location: Ukraine, Bachmut
Contact:

Re: EFI ExitBootServices() function crashes in QEMU

Post by zaval »

You cannot use Boot Services after ExitBootServices(). Like here:

Code: Select all

   EFI_STATUS st = SysTab->BootServices->ExitBootServices(ImageHandle, mkey);
    // QEMU crashes here ^^^^^^^^^^^^^^^^^^^^^^^^^^
    // qemu-system-x86_64: Trying to execute code outside RAM or ROM at 0x00000000000b0000

    FreePool(mmap);
    Print(L"Press any key: %d\n", st);

    UINTN index;
    EFI_EVENT event = SysTab->ConIn->WaitForKey;
    SysTab->BootServices->WaitForEvent(1, &event, &index);
ANT - NT-like OS for x64 and arm64.
efify - UEFI for a couple of boards (mips and arm). suspended due to lost of all the target park boards (russians destroyed our town).
anatolik
Posts: 16
Joined: Sun Jul 07, 2013 7:29 pm

Re: EFI ExitBootServices() function crashes in QEMU

Post by anatolik »

Here is simplified version of the same code. The issue still exists

Code: Select all

#include "elf.h"
#include <efi.h>
#include <efilib.h>

EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SysTab) {
    InitializeLib(ImageHandle, SysTab);

    UINTN mkey = 0, dsize = 0;
    UINT32 dversion = 0;
    UINTN msize = 32768;
    EFI_MEMORY_DESCRIPTOR *mmap = AllocatePool(msize);
    if (!mmap) {
        Print(L"AllocatePool failed\n");
        goto error;
    }

    SysTab->BootServices->GetMemoryMap(&msize, mmap, &mkey, &dsize, &dversion);

    EFI_STATUS st = SysTab->BootServices->ExitBootServices(ImageHandle, mkey);
    // QEMU crashes here ^^^^^^^^^^^^^^^^^^^^^^^^^^
    // qemu-system-x86_64: Trying to execute code outside RAM or ROM at 0x00000000000b0000
error:
    return EFI_SUCCESS;
}
User avatar
zaval
Member
Member
Posts: 659
Joined: Fri Feb 17, 2017 4:01 pm
Location: Ukraine, Bachmut
Contact:

Re: EFI ExitBootServices() function crashes in QEMU

Post by zaval »

Does GetMemoryMap return success?
ANT - NT-like OS for x64 and arm64.
efify - UEFI for a couple of boards (mips and arm). suspended due to lost of all the target park boards (russians destroyed our town).
anatolik
Posts: 16
Joined: Sun Jul 07, 2013 7:29 pm

Re: EFI ExitBootServices() function crashes in QEMU

Post by anatolik »

zaval wrote:Does GetMemoryMap return success?
Yes it does. And the memory descriptors info seems fine. Though it does not really matter. In case if GetMemoryMap() is unsuccessfull, subsequent ExitBootServices() should return INVALID_PARAMETER error. But definitely not crash.

I think I tried every possible combination with the code. And now I think the problem either in my CFLAGS/LDFLAGS or something seriously wrong with OVMF firmware. Are there any alternative UEFI firmware blobs that I can try instead of OVMF?
User avatar
zaval
Member
Member
Posts: 659
Joined: Fri Feb 17, 2017 4:01 pm
Location: Ukraine, Bachmut
Contact:

Re: EFI ExitBootServices() function crashes in QEMU

Post by zaval »

anatolik wrote:
zaval wrote:Does GetMemoryMap return success?
Yes it does. And the memory descriptors info seems fine. Though it does not really matter. In case if GetMemoryMap() is unsuccessfull, subsequent ExitBootServices() should return INVALID_PARAMETER error. But definitely not crash.

I think I tried every possible combination with the code. And now I think the problem either in my CFLAGS/LDFLAGS or something seriously wrong with OVMF firmware. Are there any alternative UEFI firmware blobs that I can try instead of OVMF?
Here, I only can suggest you to report a problem on their mailing list ([email protected]). They answer.
ANT - NT-like OS for x64 and arm64.
efify - UEFI for a couple of boards (mips and arm). suspended due to lost of all the target park boards (russians destroyed our town).
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: EFI ExitBootServices() function crashes in QEMU

Post by Brendan »

Hi,
anatolik wrote:Yes it does. And the memory descriptors info seems fine. Though it does not really matter. In case if GetMemoryMap() is unsuccessfull, subsequent ExitBootServices() should return INVALID_PARAMETER error. But definitely not crash.
Would you mind compiling without "-fno-stack-protector" or "-mno-red-zone" to see if that works?

I'm thinking that maybe something (e.g. the "-fno-stack-protector" or "-mno-red-zone") causes some kind of quirk in GNU's tools that results in the code using calling conventions that are different to what UEFI expects. Note that UEFI expects "Microsoft x64" calling conventions while GNU prefers "System V AMD64 ABI", and I'd assume that the "$(EFIINCS)" takes care of setting up the calling conventions to suit UEFI/Microsoft.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
anatolik
Posts: 16
Joined: Sun Jul 07, 2013 7:29 pm

Re: EFI ExitBootServices() function crashes in QEMU

Post by anatolik »

Alright I think I figured out the problem
You cannot use Boot Services after ExitBootServices()
I had to think on it one more time. I also checked the function docs and I see it say
On success, several fields of the EFI System Table should be set to NULL.
Setting NULL to a function pointer and later trying to call it sounds exactly like my issue.

But in my simplified example above I did not call any UEFI function after ExitBootServices(), I just returned exit code. Oh wait, what happens *after* I return from efi_main()? What if UEFI Shell tries to use BootServices?

So I added an infinite loop (I cannot use printing for debug purposes) and then attached to the app via GDB and indeed, my app called ExitBootServices() successfully without any crash. Aha, so it is UEFI Shell that crashes. It means once I called ExitBootServices() I cannot do print, I cannot return to shell, I cannot do pretty much anything except just jumping to my OS code. So I added "jmp" and now I see that execution flow reaches my OS startup code correctly.
User avatar
zaval
Member
Member
Posts: 659
Joined: Fri Feb 17, 2017 4:01 pm
Location: Ukraine, Bachmut
Contact:

Re: EFI ExitBootServices() function crashes in QEMU

Post by zaval »

It means once I called ExitBootServices() I cannot do print, I cannot return to shell, I cannot do pretty much anything except just jumping to my OS code. So I added "jmp" and now I see that execution flow reaches my OS startup code correctly.
Yes, it is supposed to be that way - ExitBootServices() is a no return to FW and jump into your OS loader/OS realm. Good to hear you resolved the problem.

Online resources are good, but don't forget - your main source is the freely available UEFI specification. Not that it's the best reading all over the world, but it is the ultimate documentation and is helpful. And it's yet not as bloated as could be! :lol:
ANT - NT-like OS for x64 and arm64.
efify - UEFI for a couple of boards (mips and arm). suspended due to lost of all the target park boards (russians destroyed our town).
Post Reply