switching from 32-bit stub to 64-bit kernel

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
slide_rule
Posts: 16
Joined: Sat Nov 24, 2007 6:41 pm
Location: loglogdecalog

switching from 32-bit stub to 64-bit kernel

Post by slide_rule »

I'm working on a 64-bit kernel, and I'm having a little trouble wrapping my brain around how exactly to get from 32-bits (loaded by GRUB, "real" kernel as module) to 64 bits.

Here's what (I think) I know: I need to get the location of the kernel from the multiboot info structure and find the ELF64 entry point by reading the ELF headers in the kernel.

All well and good. I had planned on doing a 2-for-1 identity map like Linux does in early boot; where ram is mapped 1:1 and also into the higher-half, so I can use either kind of address. But it occurs to me that, since my kernel is loaded as a module, there's going to be some kind of delta between where its loaded and where it wants to be loaded, right?

So it seems to me like what I need to do is *find* the physical address of the kernel's entry point and page map that physical page into the virtual address the kernel will be expecting for its own entry point.

Is that all correct? If so, some pointers on how to find that entry point would be great; my plan was to try finding the file offset of the .text section (from the ELF headers) and then map that as the base virtual address of my kernel. That assumes that grub put the module up in such a way as the physical address of its .text section is page aligned; which seems a little optimistic.
Last edited by slide_rule on Fri Jun 06, 2008 11:01 am, edited 2 times in total.
zerosum
Member
Member
Posts: 63
Joined: Wed Apr 09, 2008 6:57 pm

Post by zerosum »

The ELF headers have the program entry point. Identity map enough memory from that point to hold the sections defined in the kernel executable. Load it up and jump to it :-)

Cheers,
Lee
slide_rule
Posts: 16
Joined: Sat Nov 24, 2007 6:41 pm
Location: loglogdecalog

Post by slide_rule »

Hrm, not sure I made myself clear. I know how to get the entry point out of the ELF headers; but since I linked my kernel as a higher-half kernel, the address is way up in virtual memory (0xffffffff8000000-ish). Am I looking at the wrong part of the ELF header?

So my question is, if I need to find the *physical* address of the entry point's actual code, how do I do that? If I were writing a 32-bit kernel that was loaded directly by grub, finding a physical address would just be a matter of subtracting the kernel's base virtual address from a given symbol. But since I've told grub to load my kernel as a module, the constant to be subtracted is altered, since the kernel is loaded into memory (as far as I can tell) arbitrarily.

It also seems like 1:1 page mapping isn't what I'm looking for, as I understand it, I need to map the kernel's base virtual address to the actual physical address of the kernel image, which could be anywhere.

If anyone has a link to code that does this trick, I'll happily shut up and go look at that.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Hi,

If loaded as a module, your kernel will not be pre-relocated. The module table given by GRUB will point to the position and size of the unrelocated ELF in physical memory.

You therefore need to set up 64 bit paging, page in a range for the kernel and relocate the kernel manually. You know the offset you need, because you will have chosen that location yourself. Example:

* Your kernel (unrelocated) module is loaded from 0x200000 to 0x208000. Read the ELF headers to work out relocated size and lowest virtual address.
* Supposing you need 1 MiB and there is a large enough physical range at 0x400000, you now subtract that lowest virtual address from 0x400000 and you now have your offset. You need to take this in to account when fixing-up the entire ELF kernel, to load it in to its location at 0x400000.
* Now, simply page 0x400000 to 0xFFFFFF8000000000 (or wherever) in your new PML4 before switching to long mode, and jump to the kernel entry point, using a 64 bit code descriptor (I prefer to push the parameters and do an IRETQ).

I hope this answers the question and that I have understood it correctly.

Cheers,
Adam
slide_rule
Posts: 16
Joined: Sat Nov 24, 2007 6:41 pm
Location: loglogdecalog

Post by slide_rule »

Perfect, yes, thanks very much!
User avatar
Zenith
Member
Member
Posts: 224
Joined: Tue Apr 10, 2007 4:42 pm

Post by Zenith »

Yeah, that's pretty much what you have to do. GRUB will give you the physical address of your module. It's up to you to page-map it, etc.

But just to suggest something alternative:

You can use a linker script to combine the 32-bit stub and 64-bit kernel into one elf64 file. GRUB (I think it only works with v2, but you can try) will load your kernel at 0x100000 and execute your 32-bit stub. You then set up paging, and call the first 64-bit kernel function in virtual memory, after the entire kernel has been page-mapped to your specific offset.

I know it works, because I use this method in my kernel. See http://www.osdev.org/wiki/Creating_a_64-bit_kernel (shameless plug :wink: ).

Good luck!
"Sufficiently advanced stupidity is indistinguishable from malice."
slide_rule
Posts: 16
Joined: Sat Nov 24, 2007 6:41 pm
Location: loglogdecalog

Post by slide_rule »

Actually, I've been referring to your wiki entry all along; thanks much for the page. I'm not feeling up to elf relocation in the boot part of my kernel: that's a project for implementing my executable loader later on. So I'm going to look into the 32/64 hybrid that your wiki entry talks about.

I'm looking into patching grub; I don't have a dedicated test rig that I can sacrifice to the gods of alpha software, so I'm not totally ready to put GRUB2 on it yet.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

slide_rule wrote:I don't have a dedicated test rig that I can sacrifice to the gods of alpha software, so I'm not totally ready to put GRUB2 on it yet.
Neither do a lot of us. Using an emulator is highly suggested :D
slide_rule
Posts: 16
Joined: Sat Nov 24, 2007 6:41 pm
Location: loglogdecalog

Post by slide_rule »

karekare0 wrote:You can use a linker script to combine the 32-bit stub and 64-bit kernel into one elf64 file.
And lo, I am doing it. But here's a question: I have a .bss and a .data section in my 32-bit stub (for holding a stack and a temporary GDT, respectively). When I link it, I get this "relocation truncated to fit" warning, which I assume is because I've got 32-bit code with its data & bss sections relocated on the other side of a 64-bit VMA.

I tried doing this:

Code: Select all

.setupdata: {
        *(.setupdata)
}
.setupbss: {
        *(.setupbss)
}
and put those sections on the near side of my ". = KERNEL_VMA" line, but I get warnings about "uninitialized data reserved in non-bss section." I'm hunting through the ld manual, but I can't find any way to say "treat this section as a [bss|data] section."

So then I remembered the AT(ADDR(.text) - KERNEL_VMA) cleverness in karekare's wiki entry, so I tried that and all of a sudden my sections overlapped somehow.

Clearly I'm a little new to linkers, but any help?

(ps, I do use an emulator; I just have a hard time resisting the urge to be flippant. :? )

[edit]
I did a terrible thing to my linker script: I made 2 .bss and .data sections: the lower 2 include only the stub .data and .bss, the upper everything else. This made the warnings go away, until I tried to push the address of my 64-bit entry point onto the stack for a far return. Then it's back to the "relocation truncated to fit R_X86_64_32" piece.

I also tried this:

Code: Select all

...lots of inept 32-bit code...
mov ecx, EFER_MSR
	rdmsr
	or eax, 1<<EFER_LME_BIT
	wrmsr
	;ok, now we can jump to 64-bit code
	jmp 0x10:cont32
[BITS 64]
cont32:
	call entry_point
But now I get "relocation truncated to fit R_X86_64_PC32."

/me is officially stumped.
Post Reply