[SOLVED] Switch calling convention from UEFI to kernel
Posted: Wed Apr 24, 2024 7:21 am
I have written lots of kernels but they were all for MBR.
Now I am finally working on an UEFI loader for my kernel, and it works perfectly: it sets up all the necessary data and passes it on to the kernel it loads from a dedicated partition.
Both of them are written in C, UEFI is compiled with mingw and uses the UEFI calling convention, the Kernel is compiled with gcc to an ELF binary and uses the Linux calling convention.
The call works, but the parameter is passed incorrectly, which is logical as it is passed on through register RCX instead of RDI. The call returns properly and as both conventions expect, the return value is in RAX.
I tried adding __cdecl to the typedef of the entry function in the hopes that mingw would change the calling conventions for that function, but it doesn't help:
The config is not being passed correctly.
So I improvised and came up with:
This means both RDI and RCX are passing the config data, and this works perfectly.
However, it feels like I am hacking. What if the next version of the C compiler decides that it needs to fiddle with RDI in between of those two lines for some reason? It will no longer work. I could of course also add the call to the kernel_main function in assembler to fix that.
But there must be a better way... why doesn't the __cdecl attribute help with this? Am I doing it wrong? Is there something else I need to do?
Now I am finally working on an UEFI loader for my kernel, and it works perfectly: it sets up all the necessary data and passes it on to the kernel it loads from a dedicated partition.
Both of them are written in C, UEFI is compiled with mingw and uses the UEFI calling convention, the Kernel is compiled with gcc to an ELF binary and uses the Linux calling convention.
The call works, but the parameter is passed incorrectly, which is logical as it is passed on through register RCX instead of RDI. The call returns properly and as both conventions expect, the return value is in RAX.
I tried adding __cdecl to the typedef of the entry function in the hopes that mingw would change the calling conventions for that function, but it doesn't help:
Code: Select all
typedef int __cdecl (*KERNEL_MAIN)(KernelConfig *config);
...
KERNEL_MAIN kernel_main = (KERNEL_MAIN)(kernel_location + elf->ProgramEntryOffset);
int return_value = kernel_main(&config);
So I improvised and came up with:
Code: Select all
__asm__ __volatile__ ("mov %0, %%rdi\n" : : "r"(&config) : );
int return_value = kernel_main(&config);
However, it feels like I am hacking. What if the next version of the C compiler decides that it needs to fiddle with RDI in between of those two lines for some reason? It will no longer work. I could of course also add the call to the kernel_main function in assembler to fix that.
But there must be a better way... why doesn't the __cdecl attribute help with this? Am I doing it wrong? Is there something else I need to do?