I have problems understanding the long mode tutorials

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.
User avatar
dukedevon
Posts: 21
Joined: Thu Jul 08, 2010 10:02 am
Location: Karlsruhe, Germany
Contact:

I have problems understanding the long mode tutorials

Post by dukedevon »

I just want to ask if I understood that right:
To get to long mode, you boot a 32bit kernel-like executable which sets up everything you need for long mode.
Then they all say jump to your 64 bit code but I can't link 32bit and 64bit executables.
Do I have to load my 64bit kernel as a multiboot module and then jump to its location?

Ho do I do that?

TIA
chris
FlExOS --- Stay tuned ;-)
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: I have problems understanding the long mode tutorials

Post by bluemoon »

Have you read the X86-64?

There is explanation on how to setup long mode.

I believe 8 Linking a 64-bit ELF64 kernel against a 32-bit ELF32 bootstrap (for use with Multiboot) is what you wanted.
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: I have problems understanding the long mode tutorials

Post by xenos »

IMHO the part about linking 32 bit and 64 bit code in the Wiki looks way too complicated... There is no need to play around with things like readelf to place a 32 bit loader into a 64 bit executable. For example, in my kernel all object files are 64 bit ELF files - even though one of them contains some 32 bit startup code. This startup code is written in assembly with the .code32 directive:

http://xenos.svn.sourceforge.net/viewvc ... iew=markup

The 32 bit code sets up some basic tables - IDT, GDT and some page tables. In this case, I'm using only 4kB pages. As soon as all necessary structures exist, the code switches to long mode and jumps into 64 bit code.

Since the kernel is a 64 bit ELF file, it cannot be recognized directly by GRUB Legacy (and I don't want to rely on GRUB2's feature of loading ELF64 kernels). Thus, I use an "AOut Kludge" in my multiboot header which tells GRUB where to load my 64 bit sections.
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
Neolander
Member
Member
Posts: 228
Joined: Tue Mar 23, 2010 3:01 pm
Location: Uppsala, Sweden
Contact:

Re: I have problems understanding the long mode tutorials

Post by Neolander »

Hi,

The option which I've chosen myself was the following : I've written two binaries, one 64-bit one which was to become my kernel and one 32-bit one which is loaded and run by GRUB, sets up long mode, and loads and runs the kernel.

Both the binaries were based on the http://wiki.osdev.org/Bare_bones tutorial, one with a 32-bit toolchain and one with a 64-bit toolchain. I think that it is possible to use a 64-bit toolchain to compile a 32-bit binary, using the "-m32" parameter to gcc and ld, but I'm not 100% sure of that and in my case it wasn't relevant anyway.

First, I've set up GRUB to load the 64-bit binary as a module, and the 32-bit binary as a kernel, and checked that the 32-bit binary worked by coding text display routines on it (these are desperately needed later anyway). Then I've written some code which makes the switch to the 32-bit "compatibility" subset of long mode, which took some time. Then I've written an ELF64 loader to load the 64-bit binary, which took even more time because I didn't know where to find information for this (man elf and linux's elf.h header are your friends). And at this point switching to long mode was only a long jump away.

Does it help ?
Last edited by Neolander on Sun Jun 12, 2011 3:09 am, edited 1 time in total.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: I have problems understanding the long mode tutorials

Post by gerryg400 »

IMHO, Neolander's method provides the most flexibility. In particular, the 32bit loader can examine the hardware and choose which kernel (23bit/64bit/SMP variety) and drivers to load. It can also load an initrd when that is eventually required. As the OS becomes more mature the loader can become very complex and quite large. Because it is a separate binary will be very simple to discard its text and data once the kernel proper is running.

In a microkernel, if you're trying to keep drivers out of the kernel it makes special sense to have hardware detection in a separate program.
Neolander wrote:I think that it is possible to use a 64-bit toolchain to compile a 32-bit binary, using the "-m32" parameter to gcc and ld, but I'm not 100% sure of that and in my case it wasn't relevant anyway.
-m32 certainly works for me.
If a trainstation is where trains stop, what is a workstation ?
User avatar
Neolander
Member
Member
Posts: 228
Joined: Tue Mar 23, 2010 3:01 pm
Location: Uppsala, Sweden
Contact:

Re: I have problems understanding the long mode tutorials

Post by Neolander »

gerryg400 wrote:-m32 certainly works for me.
Does it just compile 32-bit code within a section of an ELF64 binary, or does it also generate ELF32 binaries ? :)
User avatar
Neolander
Member
Member
Posts: 228
Joined: Tue Mar 23, 2010 3:01 pm
Location: Uppsala, Sweden
Contact:

Re: I have problems understanding the long mode tutorials

Post by Neolander »

Thanks for the clarification. Then I can indeed think about that when building the next version of my toolchain, though again now it's too late. :P
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

Re: I have problems understanding the long mode tutorials

Post by shikhin »

Neolander wrote:Hi,

The option which I've chosen myself was the following : I've written two binaries, one 64-bit one which was to become my kernel and one 32-bit one which is loaded and run by GRUB, sets up long mode, and loads and runs the kernel.

Both the binaries were based on the http://wiki.osdev.org/Bare_bones tutorial, one with a 32-bit toolchain and one with a 64-bit toolchain. I think that it is possible to use a 64-bit toolchain to compile a 32-bit binary, using the "-m32" parameter to gcc and ld, but I'm not 100% sure of that and in my case it wasn't relevant anyway.

First, I've set up GRUB to load the 64-bit binary as a module, and the 32-bit binary as a kernel, and checked that the 32-bit binary worked by coding text display routines on it (these are desperately needed later anyway). Then I've written some code which makes the switch to the 32-bit "compatibility" subset of long mode, which took some time. Then I've written an ELF64 loader to load the 64-bit binary, which took even more time because I didn't know where to find information for this (man elf and linux's elf.h header are your friends). And at this point switching to long mode was only a long jump away.

Does it help ?
Now, I am feeling like a big idiot, who is trying to ask a stupid question, and that too by barging on to some one else' thread... :?

Anyway, here I shoot:

My design is similar to Neolander's, if not somewhat more complex. The initialization happens like:
  • Intermediate Loader is run by GRUB, with Kernel, Long mode Kernel, and the Modules image passed as modules.
  • Intermediate Loader initializes a trivial PMM, and does some basic CPU detection.
  • Basic screen handling is put up.
  • Intermediate Loader decides whether to run the Long mode, PAE, or Legacy Kernel (where PAE and Legacy Kernel are the same, but the VMM differs)
  • The relative module is set up, with the memory marked in the VMM.
  • Intermediate Loader sets up a VMM, which initializes all tables, sets up the CR4, CR3 registers, but doesn't set the PG bit in CR0. This helps in avoiding code duplication.
  • The ACPI, MPS and SMBIOS tables are relocated to a address, with information about them passed by a Draumr Header.
  • Finally the ELF are parsed, and at the last moment PG bit is set, and the jump occurs. If the long mode kernel is to be run, first the EFER.LME flag is set, then PG flag is set, and then a Long Mode GDT is lgdt'ed.
While some may be find this ugly, this design seems quite sophisticate to me. Moreover, not setting the PG bit also means that I don't get TLB flushes. Now comes the problem.

For the 32-bit kernel, the last step, is a piece of cake, and I do it by inline assembly for readability. However, for the Long Mode kernel, things get a bit more complex. Thus, I do it all in pure assembly. I have got to the point where I can do something like:

Code: Select all

jmp 0x08:long_mode

long_mode:
    hlt
And enter pure Long Mode. Woot. However, I can't get it around on how to call the x86-64 ELF's entry point. Since the entry point is 0xFFFF800000001000, if I enter a bits 64 in there, I get the probably not so famous message:
nasm wrote: error: elf32 output format does not support 64-bit code
If I just put a CALL 0xFFFF800000001000, 0x1000 gets called. I don't want to make this hacky, and want to do it neatly.

Now, I should really move out of here. Hope this question helps the OP though, and he can probably learn from both my mistakes and my design, and do something good. :|

Regards,
Shikhin 8)
http://shikhin.in/

Current status: Gandr.
User avatar
Neolander
Member
Member
Posts: 228
Joined: Tue Mar 23, 2010 3:01 pm
Location: Uppsala, Sweden
Contact:

Re: I have problems understanding the long mode tutorials

Post by Neolander »

I have asked myself a similar question once, and my conclusion was that the kernel's entry point had to be at a 32-bit address, though at that address there could just be a simple assembly snippet that jumps to the real kernel high above and can be quickly overwritten after that.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: I have problems understanding the long mode tutorials

Post by Owen »

Placing your kernel at 0xFFFF800000001000 is going to require the huge memory model, which generates some truly awful code. I strongly suggest putting the kernel at 0xFFFFFFFF80000000, i.e. -2GB.

Build your 64-bit code into an ELF64 and then link this into your ELF32 binary. This is fully supported.

x86(_64) only does 32-bit relative jumps. If you want an absolute jump, you need to load a register first.
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

Re: I have problems understanding the long mode tutorials

Post by shikhin »

Hi,
Neolander wrote:I have asked myself a similar question once, and my conclusion was that the kernel's entry point had to be at a 32-bit address, though at that address there could just be a simple assembly snippet that jumps to the real kernel high above and can be quickly overwritten after that.
Could you ignore my ignorance, and give me a pointer (pun intended) on how to do this? Probably show your linker script and/or the place where you do this. Am feeling extremely embarrassed.

Regards,
Shikhin (yep, no Cool face for getting embarrassed today)
http://shikhin.in/

Current status: Gandr.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: I have problems understanding the long mode tutorials

Post by gerryg400 »

Can't you just do this ?

Code: Select all

.code64
code_seg_64:
        /* Jump to the kernel */
        movq    $0xffffffff80000000, %rax
        jmpq    *%rax
If a trainstation is where trains stop, what is a workstation ?
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

Re: I have problems understanding the long mode tutorials

Post by shikhin »

gerryg400 wrote:Can't you just do this ?

Code: Select all

.code64
code_seg_64:
/* Jump to the kernel */
movq $0xffffffff80000000, %rax
jmpq *%rax
In the Loader? No, I can't due to aforementioned reasons. Going with Neolander's advice. Thank you all for you help. I'll try doing what he suggested as soon as possible.

Regards,
Shikhin
http://shikhin.in/

Current status: Gandr.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: I have problems understanding the long mode tutorials

Post by gerryg400 »

Hmmm, not sure I understand your reason but that's okay.
If a trainstation is where trains stop, what is a workstation ?
User avatar
Neolander
Member
Member
Posts: 228
Joined: Tue Mar 23, 2010 3:01 pm
Location: Uppsala, Sweden
Contact:

Re: I have problems understanding the long mode tutorials

Post by Neolander »

Shikhin wrote:Hi,
Neolander wrote:I have asked myself a similar question once, and my conclusion was that the kernel's entry point had to be at a 32-bit address, though at that address there could just be a simple assembly snippet that jumps to the real kernel high above and can be quickly overwritten after that.
Could you ignore my ignorance, and give me a pointer (pun intended) on how to do this? Probably show your linker script and/or the place where you do this. Am feeling extremely embarrassed.

Regards,
Shikhin (yep, no Cool face for getting embarrassed today)
I'd do it pretty much the same way as gerryg's, linking a code snippet like this into the kernel (not the loader)...

Code: Select all

jumper:
        movq  $kernel_entry, %rax
        jmp %rax
...using a kernel linker script like that :

Code: Select all

ENTRY(jumper) /* Entry point is the "jumper" code */

SECTIONS {
  . = 0x40000000; /* Jumper code loaded at 1GB */

  .text_jumper : {
    jumper.o (.text) /* Replace jumper.o with the name of the object file associated with your assembly snippet */
  }

  /* I don't think such a simple assembly snippet has data sections, so I don't include them. Check ! */

  . = 0xffffffff80000000 /* Rest of the kernel loaded at regular address */
  .text : {
    *(.text)
  }

  .rodata ALIGN (0x1000) : {
    *(.rodata)
  }

  .data ALIGN (0x1000) : {
    *(.data)
  }

  .bss ALIGN (0x1000) : {
    *(COMMON)
    *(.bss)
  }
}
Provided that your loader has mapped the kernel at its virtual address properly, it should work.
Last edited by Neolander on Mon Jun 13, 2011 3:15 am, edited 1 time in total.
Post Reply