OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 5:27 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 9 posts ] 
Author Message
 Post subject: UEFI: Getting 'EFI_NOT_FOUND' error when allocating pages.
PostPosted: Thu Aug 04, 2022 10:56 pm 
Offline

Joined: Fri Nov 08, 2019 5:35 pm
Posts: 16
Hello,
I'm writing a UEFI bootloader for a hobby kernel.
I'm getting an 'EFI_NOT_FOUND' error when using the 'AllocatePages' UEFI function to allocate pages at a specific physical address. I'm able to successfully load and parse the kernel ELF file. I'm able to allocate pages using the 'AllocateAnyPages' allocation type, then copy the kernel image into memory without any problems.
If I use the 'AllocateAddress' allocation type to allocate pages at a specific address, I get an 'EFI_NOT_FOUND' error. The spec states that this indicates "The requested pages could not be found".

The UEFI spec states:
Quote:
Allocation requests of Type AllocateAnyPages allocate any available range of pages that satisfies the
request. On input, the address pointed to by Memory is ignored.

However if I use 'AllocateAnyPages', and pass in the same `p_paddr` value from the ELF file's program header, the pages are actually allocated at the physical address specified. This confusing behavior occurs in both QEMU (using OVMF) and on real hardware. Due to UEFI identity mapping the address space, I can actually load the kernel into memory using 'AllocateAnyPages', exit boot services, jump to the kernel entry point, and successfully load the kernel.
Does anyone know why this is happening? Am I overlooking something critical in my design?

I'm also confused about where to place the kernel in physical memory. The linker script for my test kernel specifies that the kernel starts at physical address 1MB. I know that UEFI doesn't guarantee any physical address is free, however no test value I've used has yielded a valid value when using the 'AllocateAddress' allocation type. Should I perform all of the virtual memory mapping before jumping to the kernel, and not worry about the kernel's physical address at all?


Top
 Profile  
 
 Post subject: Re: UEFI: Getting 'EFI_NOT_FOUND' error when allocating page
PostPosted: Thu Aug 04, 2022 11:47 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5100
stillconfused wrote:
Does anyone know why this is happening? Am I overlooking something critical in my design?

What does your code to call AllocatePages() look like? I suspect you're calling it with the wrong parameters.

stillconfused wrote:
Should I perform all of the virtual memory mapping before jumping to the kernel, and not worry about the kernel's physical address at all?

Yes.


Top
 Profile  
 
 Post subject: Re: UEFI: Getting 'EFI_NOT_FOUND' error when allocating page
PostPosted: Fri Aug 05, 2022 1:23 am 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
stillconfused wrote:
I'm also confused about where to place the kernel in physical memory.

Yeah, so are a lot of people. With UEFI, you cannot assume any physical address to be free when loading the kernel. Not 0, not 1MB, not 16MB, not anything. Therefore, attempting to run a kernel in identity mapped mode is only possible if the kernel is position independent. That requires a lot more loader support than I would be willing to spend, so the alternative is to simply not identity map the kernel. I have a higher-half kernel that is mapped to -2GB, to take advantage of the kernel code model. You can do the same, or you can link the kernel as a completely normal userspace executable, your choice. Then the position of the kernel in physical memory can be abstracted by the virtual mapping.

This does require some work in the kernel itself: You cannot simply start to use physical addresses. If you know a physical address and want to access it, you must map it into virtual space. However, this is also very simple, since in 64-bit mode you have so much virtual space to work with. I have a linear mapping of all physical memory starting at 0xffff800000000000, so mapping a given physical address amounts to checking that the address is in range of the mapping, and then adding that constant.

As I said, the alternative is that you link the kernel as a PIE. Good luck with that. The result should be an executable with ELF type ET_DYN, that contains a relocation table. You can put it anywhere in memory, but must process the relocations.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: UEFI: Getting 'EFI_NOT_FOUND' error when allocating page
PostPosted: Fri Aug 05, 2022 1:38 am 
Offline

Joined: Fri Nov 08, 2019 5:35 pm
Posts: 16
Octocontrabass wrote:
stillconfused wrote:
Does anyone know why this is happening? Am I overlooking something critical in my design?

What does your code to call AllocatePages() look like? I suspect you're calling it with the wrong parameters.


Here's where I make the call to 'AllocatePages()'. The parameters are exactly as written:
Code:
   status = uefi_call_wrapper(gBS->AllocatePages, 4,
      AllocateAnyPages, EfiLoaderData, segment_page_count,
      (EFI_PHYSICAL_ADDRESS*)segment_physical_address);


'segment_physical_address' is the 'p_paddr' attribute from the ELF program header.
I know it would be more correct to use 'EfiLoaderCode' for the memory type. It doesn't seem to make a difference, oddly enough. I'll resolve this eventually.
Keep in mind that the code you're seeing will actually successfully load the kernel. Even on real hardware. So it's unlikely that it's too wrong. I've checked things like the page count, and it seems correct.


Last edited by stillconfused on Fri Aug 05, 2022 1:45 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: UEFI: Getting 'EFI_NOT_FOUND' error when allocating page
PostPosted: Fri Aug 05, 2022 1:43 am 
Offline

Joined: Fri Nov 08, 2019 5:35 pm
Posts: 16
nullplan wrote:
Yeah, so are a lot of people. With UEFI, you cannot assume any physical address to be free when loading the kernel....


Thank you for your very detailed response. I'll keep this in mind!
I intend to have a higher half kernel in the long run, however for the sake of developing the bootloader first I just developed the minimum viable product, or so I thought.


Top
 Profile  
 
 Post subject: Re: UEFI: Getting 'EFI_NOT_FOUND' error when allocating page
PostPosted: Fri Aug 05, 2022 9:57 am 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
stillconfused wrote:
I intend to have a higher half kernel in the long run, however for the sake of developing the bootloader first I just developed the minimum viable product, or so I thought.
You still can. Minimum viable kernel is something like:
Code:
kernel.S:
.text
_start:
  cli
  hlt
  jmp _start

link.ld:
SECTIONS {
  . = 0xffffffff80000000 + SIZEOF_HEADERS;
  .text: {
    *(.text)
    *(.text.*)
  }
  .rodata: {
    *(.rodata)
    *(.rodata.*)
  }
  . = ALIGN(4096);
  .data: {
    *(.data)
    *(.data.*)
  }
  .bss: {
    *(.bss)
    *(.bss.*)
    *(COMMON)
  }

Makefile:
kernel.elf: kernel.o link.ld
  x86_64-elf-gcc -ffreestanding -mgeneral-regs-only -O3 -mcmodel=kernel -mno-red-zone -nostdlib -Wl,-z,max-page-size=0x1000 -Wl,-T,link.ld -o kernel.elf kernel.o

kernel.o: kernel.S
  x86_64-elf-gcc -ffreestanding -mgeneral-regs-only -O3 -mcmodel=kernel -mno-red-zone -o kernel.o kernel.S


There. Kernel is essentially a normal ELF executable, except for the high load address and the alignment hole in the middle. It does nothing but halt the system. With UEFI, bootloader needs to set up the initial environment. Kernel can still additionally load itself afterwards, in order to discard the bootloader mappings and take control of the PML4 completely (for example, in my case, it is the kernel that decides to use global pages and map the kernel globally), but the initial mapping has to be done by the bootloader.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: UEFI: Getting 'EFI_NOT_FOUND' error when allocating page
PostPosted: Fri Aug 05, 2022 11:02 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5100
stillconfused wrote:
Code:
uefi_call_wrapper

GNU-EFI is full of ugly hacks like this, but I thought you didn't actually need uefi_call_wrapper anymore. Recent enough versions of GCC should be able to directly emit function calls using the correct ABI.

stillconfused wrote:
Code:
(EFI_PHYSICAL_ADDRESS*)segment_physical_address

That looks like a problem. Why do you have this cast here? I suspect it's not doing what you want it to do. Did you actually want to use the address-of operator?


Top
 Profile  
 
 Post subject: Re: UEFI: Getting 'EFI_NOT_FOUND' error when allocating page
PostPosted: Fri Aug 05, 2022 11:09 am 
Offline
Member
Member

Joined: Mon Feb 02, 2015 7:11 pm
Posts: 898
An alternative that I use is simply to have a relocatable trampoline to jump to your kernel.

1) Allocate a page of memory for the trampoline under the kernel's starting address. For example if your kernel is at 0xF0000000, you can use that with UEFi's `maxAddress` allocation type. This will ensure that the memory page allocated is under 0xF0000000 and won't conflict with your kernel's virtual memory range.
2) Copy relocatable trampoline code to the memory allocated in #1.
3) ExitBootServices()
4) Update virtual memory map (CR3) with mappings for kernel. Make sure the anything under the kernel space is identity-mapped.
5) Jump to the relocated trampoline with the address of the kernel's entry point.

For example (note that I do it slightly differently from described above, I exit boot services before allocating the trampoline memory because I don't rely on UEFI at that point, I also identity-map the first 4GB of memory as my kernel's starting virtual address is far above that):

trampoline itself:
https://github.com/kiznit/rainbow-os/bl ... line.S#L35

code to copy and jump to the kernel using the relocated trampoline
https://github.com/kiznit/rainbow-os/bl ... ne.cpp#L40

_________________
https://github.com/kiznit/rainbow-os


Top
 Profile  
 
 Post subject: Re: UEFI: Getting 'EFI_NOT_FOUND' error when allocating page
PostPosted: Fri Aug 05, 2022 9:41 pm 
Offline

Joined: Fri Nov 08, 2019 5:35 pm
Posts: 16
Octocontrabass wrote:
Why do you have this cast here? I suspect it's not doing what you want it to do. Did you actually want to use the address-of operator?


Oh wow... This is very embarrassing. You're totally right. Thank you for pointing this out! I was missing the address-of operator! I put the cast there because I compile with strict settings, and I would get a warning otherwise. Ironically in this case it actually suppressed the warning that would have given away the problem.
The call actually works as expected now.

The topic has been very valuable for other reasons too. I'll take the other suggestions on board as well!


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 85 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group