This is the first of two chapters about adding x86_64 support to the open-source coreboot project on Intel platforms. You can find the second article here..
It started in 2018 when I was wondering why it's still using x86_32 on modern x86 platforms.
It's an abbreviation for the execution mode the processor runs in. Almost all x86 processors since the Pentium 4 are capable of running in "long mode", that is where it makes use of 64bit registers and 64bit addressing modes. To maintain compatibility, all processors still support x86_32, where it only makes use of 32bit registers and up to 4GiB of RAM. Currently, coreboot only uses x86_32 mode.
Looking at the code in 2018
Looking at coreboot 4.8 there was only support for x86_64 on ancient AMD boards, for which most of them have been removed today due to the lack of maintenance.
coreboot thus already had x86_64 support, it just wasn't used on that many platforms. But why not ?
Simply booting an OS is possible with x86_32, too. There was simply no need to use x86_64 when coreboot was widely adapted by google in 2013. And thus all Intel platforms still use x86_32 until today.
It turns out to be easy to compile coreboot's C code for x86_64. Just select Kconfig ARCH_RAMSTAGE_X86_64 instead of ARCH_RAMSTAGE_X86_32 and you are done. But ...
It turns out the code doesn't compile due to int to void conversions, which needed a few casts and better code not assuming pointer size.
It compiled successfully, linked successfully, but didn't run successfully...
The relocations couldn't be emitted, as the support for x86_64 was missing. That would only allow us to run a program at a specific point (known at compile time) in memory, but on x86 we have relocatable stages, where the place in memory is only known at runtime.
Note: The ancient AMD boards supporting x86_64 used non-relocatable ram stage. One reason why they were removed.
Loading x86_64 code modules
In coreboot code can be relocated at runtime using relocatable modules (rmodules).
After adding the x86_64 relocation support to rmodules I tried running the program. Of course, it didn't work as the relocation symbols were incorrectly applied.
It turns out a stage can only load a relocatable stage of the same ARCH, which is x86_32 can only load x86_32. coreboot simply misses the support for it. The solution was simple: Compile the loader stage (the romstage) in x86_64 mode too and enter long mode even earlier!
Entering x86_64 mode early
To enter x86_64 mode in romstage (the stage that set's up DRAM), it's required to store the page tables somewhere, that is not DRAM, as it's not running yet.
I installed the pagetables in CAR (L2 Cache-As-RAM), enabled paging and jumped to long mode, and it worked!
It turned out that this approach was still not good enough as:
- It needs two toolchains, one x86_32 for early stages and one x86_64 for later stages
- Page-tables needs to be set up again in every stage (as they are stored in stages' heap)
- Page-tables require a lot of space in CAR, which is quite limited
- Page-tables needs to be aligned on 4K boundary
- There wasn't enough space in SMM A-seg to also host page tables
A better solution was required.
A better solution was required but I’ll leave that for another post.
If you’re like what you’re reading feel free to follow us on twitter.
If you have any questions about coreboot development ping us.