Problem with UEFI bootloader loading PE

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
heemogoblin
Posts: 13
Joined: Sat Jun 27, 2020 8:00 am
Libera.chat IRC: heemogoblin

Problem with UEFI bootloader loading PE

Post by heemogoblin »

Been working on this for a day or 2 now, but my kernel does not run. It is loaded by my efi bootloader, code is here: https://pastebin.com/BefcLQbN. My code jumps the the kernel (code here: https://pastebin.com/ZTSWA1TK) all good, but then the kernel does nothing. I have tried having it do a reset via the efi runtime services, does nothing. In the struct I pass to the entry point there is an address to the linear frame buffer for the graphics, my kernel now is meant to draw with that but nothing happens.

In terms of what I've done, firstly I tried loading the sections one by one into memory. Then from looking at the chaiOS loader I realised I had to load the header in too. Then after following this thread viewtopic.php?f=1&t=29818&start=30 I realised I could just dump the file in memory at imageBase. Then I stopped using AllocatePages to get memory for my kernel (as I had been doing previously) and now in this last iteration I have simply asked efi to read from the file and dump the contents at imageBase. All of the times, I have the same result.
In terms of jumping to the entry point, I have tried firstly some asm which I link in which changes the rsp and rbp so that the kernel can run. That did yield the same result as I have now, but at the previous blog link I have found it easier to cast the entry address to a function pointer and call that.

All of this is compiled under visual studio, all runs fine, kernel exe has the section alignment equal to the file alignment. The bootloader and kernel are tested in virtualbox.

Could anyone please explain why the kernel does not run and what I could do to get it running? I shall be very grateful, thanks in advance.
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: Problem with UEFI bootloader loading PE

Post by nexos »

I doesn't appear you have copied the image to its base address. You must do that. Copy the whole file to the base address in the optional header. Once you do that, it should work.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
heemogoblin
Posts: 13
Joined: Sat Jun 27, 2020 8:00 am
Libera.chat IRC: heemogoblin

Re: Problem with UEFI bootloader loading PE

Post by heemogoblin »

nexos wrote:I doesn't appear you have copied the image to its base address. You must do that. Copy the whole file to the base address in the optional header. Once you do that, it should work.
I am stuck on how to do this. I suppose the easiest way to do it is with kernel->Read(kernel, &optHdr.sizeOfImage, optHdr.ImageBase);
but that does not work. Do you have any suggestions on how to do this please? Thank you very much.
User avatar
bellezzasolo
Member
Member
Posts: 110
Joined: Sun Feb 20, 2011 2:01 pm

Re: Problem with UEFI bootloader loading PE

Post by bellezzasolo »

heemogoblin wrote:
nexos wrote:I doesn't appear you have copied the image to its base address. You must do that. Copy the whole file to the base address in the optional header. Once you do that, it should work.
I am stuck on how to do this. I suppose the easiest way to do it is with kernel->Read(kernel, &optHdr.sizeOfImage, optHdr.ImageBase);
but that does not work. Do you have any suggestions on how to do this please? Thank you very much.
Feel free to take a look at my UEFI bootloader.
Github - imageloader
Whoever said you can't do OS development on Windows?
https://github.com/ChaiSoft/ChaiOS
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Problem with UEFI bootloader loading PE

Post by bzt »

heemogoblin wrote:I am stuck on how to do this. I suppose the easiest way to do it is with kernel->Read(kernel, &optHdr.sizeOfImage, optHdr.ImageBase);
but that does not work. Do you have any suggestions on how to do this please? Thank you very much.
Well, this could work with a firmware that does not allocate memory dynamically (such as BIOS). But since UEFI has dynamic memory allocation, there's no guarantee that the memory at ImageBase is going to be free to use.

You have two options to circumvent this. Both has approx. the equal complexity, the second might be a little simpler, but only possible under special circumstances.

1. tell your compiler to generate position independent code (-fpic)
This way you don't have to care about ImageBase, because the code will contain only RIP relative addresses. There's a downside, most programs need absolute addresses at some point. To solve the issue, the compiler generates a table with memory addresses which your loader must fill in according to the load address (so called relocation table). That's why UEFI mandates and accepts only relocatable PE.

2. load your PE anywhere in physical memory, then map it at ImageBase virtual memory
This method can be only used if you have a single executable to run, like a kernel for example. You load the PE anywhere into memory (load address), and then you create a paging table that translates address space in a way that physical load address maps to virtual ImageBase. You activate the new paging table before you jump to the PE's entry point, and presto. This solution works with both relocatable and non-relocatable PEs, but limited to one PE only (in lack of a scheduler and taskswitcher, there can be only one active paging table at any given time).

Cheers,
bzt
User avatar
zaval
Member
Member
Posts: 657
Joined: Fri Feb 17, 2017 4:01 pm
Location: Ukraine, Bachmut
Contact:

Re: Problem with UEFI bootloader loading PE

Post by zaval »

heemogoblin wrote:Been working on this for a day or 2 now, but my kernel does not run. It is loaded by my efi bootloader, code is here: https://pastebin.com/BefcLQbN. My code jumps the the kernel (code here: https://pastebin.com/ZTSWA1TK) all good, but then the kernel does nothing. I have tried having it do a reset via the efi runtime services, does nothing. In the struct I pass to the entry point there is an address to the linear frame buffer for the graphics, my kernel now is meant to draw with that but nothing happens.

In terms of what I've done, firstly I tried loading the sections one by one into memory. Then from looking at the chaiOS loader I realised I had to load the header in too. Then after following this thread viewtopic.php?f=1&t=29818&start=30 I realised I could just dump the file in memory at imageBase. Then I stopped using AllocatePages to get memory for my kernel (as I had been doing previously) and now in this last iteration I have simply asked efi to read from the file and dump the contents at imageBase. All of the times, I have the same result.
In terms of jumping to the entry point, I have tried firstly some asm which I link in which changes the rsp and rbp so that the kernel can run. That did yield the same result as I have now, but at the previous blog link I have found it easier to cast the entry address to a function pointer and call that.

All of this is compiled under visual studio, all runs fine, kernel exe has the section alignment equal to the file alignment. The bootloader and kernel are tested in virtualbox.

Could anyone please explain why the kernel does not run and what I could do to get it running? I shall be very grateful, thanks in advance.
First of all, your loader must switch Virtual Memory on, before jumping to the kernel. that is, you have to build up page tables and fill them in with proper mapping information for at least that region of the Virtual address space, where your kernel images are going to be placed. also for the stack. and then, at the very end, or, more generally - at the well defined point - switch VM on. second, since it's a kernel file, you won't get problems with its Base Address - no need to do relocations at all, you can even strip them. You need to set up stack, before jumping into the kernel.

This all is way more complex, than this silly identity mapped jumping, but it's what you ultimately will need to have, so no need to distract yourself on what is totally different. it's tempting to do this simple jump for drawing framebuffer, I know, :D but it's not like you want your kernel work. in future, surely. :)

As of loading, loading section by section with building the mapping for each is the way. use AllocatePages(), remember, UEFI uses identiy mapping, so addresses returned by this function, is system addresses as well. you cannot just read a PE file into memory and jump there, because sections in the file are placed more tightly, than in memory. you need something like this, for each kernel PE image:
0. read the PE headers, parse info in it.
1. look at the size of a section, calculate the number of pages it will need.
2. allocate pages for this section with AllocatePages()
3. read the section from the file into the previously allocated buffer
4. map the pages in page tables, and also, you need to keep the list of these pages (PFN, physical addresses) for the next stages of the initialization to know, these pages aren't free anymore. using loader specific flags in the UEFI memory map, that you will own soon, would be a nice idea, but be aware, some emulators, unfortunately, have bugs with this. -_-
I'd rather build up PFN database by the loader (that's why I say loaders are very OS specific), and marked these pages allocated directly there. that means, the loader needs to allocate PFN as well, before loading PEs. still, in the UEFI Memory Map, these pages need to be distinguished. again, the best solution, it was actually made for that, is allocating memory (by AllocatePool() or AllocatePages()) with your loader specific flags.
5. more sections? yes - goto 1, no - exit loop.

of course, it's very simplified. but this is how it should look like.
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