Page 1 of 1

Call to UEFI AllocatePages fails with an EFI_NOT_FOUND

Posted: Sun Jun 05, 2022 11:03 am
by ThatCodingGuy89
I am aware that this means that UEFI can't find the requested pages, (from page 203 of https://uefi.org/sites/default/files/re ... %202_6.pdf)
but the address I'm requesting it allocate pages from is well within my 32 GB of memory. (Specifically, address 0x1000)
The error only doesn't occur when I request it allocate address 0x0, which doesn't make any sense to me.

Here's the code:

Code: Select all

    EFI_FILE_HANDLE kernelHandle; // Kernel file handle

    // Get a handle to the kernel file
    uefi_call_wrapper(volumeHandle->Open, 5, volumeHandle, &kernelHandle, L"kernel.elf", EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
    
    // Read from the kernel file and load it into memory

    uint64_t kernelSize = FileSize(kernelHandle);

    #define PAGE_SIZE 4096
    #define PRE_ALLOC 5

    // Allocate (kernelSize / PAGE_SIZE) + PRE_ALLOC pages for the kernel. UEFI initilizes the VAS as identity mapped,
    // so I can't use AllocatePool, as the kernel address will be inconsistent otherwise.

    UINTN numPages = (kernelSize / PAGE_SIZE) + PRE_ALLOC;

    void* kernelAddress = (void*)0x1000;
    Status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAddress, EfiLoaderCode, numPages, (EFI_PHYSICAL_ADDRESS*)&kernelAddress);
    if (EFI_ERROR(Status) || kernelAddress == NULL)
    {
        Print(L"Could not allocate pages for the kernel! Stopping boot!\n");
        Print(L"Failure Reason: ");

        if (Status == EFI_OUT_OF_RESOURCES)
        {
            Print(L"EFI_OUT_OF_RESOURCES\n");
        }
        else if (Status == EFI_INVALID_PARAMETER)
        {
            Print(L"EFI_INVALID_PARAMETER\n");
        }
        else if (Status == EFI_NOT_FOUND)
        {
            Print(L"EFI_NOT_FOUND\n");
        }
        else if (Status == EFI_SUCCESS && kernelAddress == NULL)
        {
            Print(L"UNKNOWN_K_PTR_NULL\n");
        }

        Print(L"Please power off your machine. (I am too lazy to implement ACPI drivers)\n");

        while (true) { }

    }

    uint8_t *kernelBuf = kernelAddress;

    uefi_call_wrapper(kernelHandle->Read, 3, kernelHandle, &kernelSize, kernelBuf);
    

    Print(L"Loaded kernel at address %x\n", kernelBuf);

    while (true) { }
I've tested this in both QEMU and real hardware, both have the same result.

Here's the Github repo: https://github.com/ThatCodingGuy86/MimosaOS

Re: Call to UEFI AllocatePages fails with an EFI_NOT_FOUND

Posted: Sun Jun 05, 2022 12:02 pm
by Ethin
First, you might want to use EDK II instead of GNU EFI, which is just a massive set of hacks to try to get EFI working with ELF that I'm pretty sure hardly anyone understands. Second, I'm pretty sure you want to use AllocatePool instead of AllocatePages. Maybe there's a particular reason you need AllocatePages for this, but if yoru trying to load your kernel, it should be relocatable so you can just use AllocatePool and relocate it to wherever UEFI wants it to be. The page your allocating could also just not be available, as your firmware indicates; its best practice to never assume anything about your memory map when UEFI is active.

Re: Call to UEFI AllocatePages fails with an EFI_NOT_FOUND

Posted: Sun Jun 05, 2022 12:13 pm
by Octocontrabass
ThatCodingGuy89 wrote:the address I'm requesting it allocate pages from is well within my 32 GB of memory. (Specifically, address 0x1000)
Have you tried dumping the memory map to see if that address is actually available? UEFI doesn't guarantee available memory at any specific address.
ThatCodingGuy89 wrote:The error only doesn't occur when I request it allocate address 0x0, which doesn't make any sense to me.
That's the only address where you definitely should see an error in QEMU, since OVMF reserves the page at 0 to provide a compatibility hack for Windows 7.

Code: Select all

uefi_call_wrapper
This is unrelated, but I really don't like gnu-efi. Personally, I'd rather use a compiler that can directly target UEFI so no wrapper is needed. (I think you can even use the gnu-efi headers if you do that, but I just write my own UEFI headers.)

Code: Select all

AllocateAddress
You probably should use AllocateAnyPages. UEFI doesn't guarantee available memory at any specific address.

Re: Call to UEFI AllocatePages fails with an EFI_NOT_FOUND

Posted: Sun Jun 05, 2022 12:58 pm
by ThatCodingGuy89
Octocontrabass wrote:Personally, I'd rather use a compiler that can directly target UEFI so no wrapper is needed.
Such as? I haven't heard of a compiler that directly supports UEFI without the hackery of gnu-efi.
Octocontrabass wrote: Have you tried dumping the memory map to see if that address is actually available? UEFI doesn't guarantee available memory at any specific address.
I just did, I decided the reasonable first thing to check would be NumberOfPages, and it says 0. I'm not entirely sure how that make sense though, as for memory mapping to work at all in long mode (and I think in 32 bit mode as well), you have to have page tables. Or does it mean actual pages? Either way, it doesn't make any sense.
Ethin wrote:I'm pretty sure you want to use AllocatePool instead of AllocatePages. Maybe there's a particular reason you need AllocatePages for this, but if yoru trying to load your kernel, it should be relocatable so you can just use AllocatePool and relocate it to wherever UEFI wants it to be.
The reason is in the comment above the AllocatePages call. I know you can make an ELF executable relocatable, but to my knowledge, this requires loader support,
which I honestly don't want to deal with, as it's been difficult enough parsing the ELF header.

Re: Call to UEFI AllocatePages fails with an EFI_NOT_FOUND

Posted: Sun Jun 05, 2022 1:55 pm
by Octocontrabass
ThatCodingGuy89 wrote:
Octocontrabass wrote:Personally, I'd rather use a compiler that can directly target UEFI so no wrapper is needed.
Such as?
Clang or MinGW-w64 GCC. They don't actually have a preset UEFI target, but the Windows target is close enough - you just need a few extra options to create an EFI binary. With GCC, I also use a custom linker script, although I'm pretty sure that's not actually necessary.

I use something like this:

Code: Select all

clang --target=x86_64-windows -ffreestanding -nostdlib -mno-stack-arg-probe -mgeneral-regs-only -fuse-ld=lld-link -Wl,-entry:efi_main -Wl,-subsystem:efi_application -Wl,-largeaddressaware
x86_64-w64-mingw32-gcc -ffreestanding -nostdlib -mno-stack-arg-probe -mgeneral-regs-only -pie -s -Wl,--subsystem,10 -e efi_main -T uefi.ld
ThatCodingGuy89 wrote:I just did, I decided the reasonable first thing to check would be NumberOfPages, and it says 0.
Does it say that in all of the memory map entries, or just one of them? In QEMU I see around 100 entries.
ThatCodingGuy89 wrote:I know you can make an ELF executable relocatable, but to my knowledge, this requires loader support, which I honestly don't want to deal with, as it's been difficult enough parsing the ELF header.
Or you can use paging to map your ELF executable wherever it needs to be. Build your page tables, call ExitBootServices(), switch to your page tables, and jump to your kernel. This way, you're not depending on any particular physical address being available, but your kernel doesn't need to be relocatable.