wjk
wjk

Reputation: 1249

Jumping to a 64-bit value in x86

[This code is for 32-bit x86 Intel assembler. Also, please try to adhere to the assembler syntax used by Xcode 5. Assembly language is not my forte, and I want to try to avoid silly mistakes caused by my not understanding the code.]

I am trying to write a boot loader. The entry point of my kernel is located above 0xFFFFFFFF (because this kernel kernel file was built in 64-bit mode). Now, I have the following snippet of assembly code, taken from my boot loader’s source code:

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// startprog(phyaddr, arg)
// Start the program on protected mode where phyaddr is the entry point.
// Passes arg to the program in %eax.
//
_startprog:
  push    %ebp
  mov     %esp, %ebp

  mov     0xc(%ebp), %eax  // argument to program
  mov     0x8(%ebp), %ecx  // entry offset 
  mov     $0x28, %ebx      // segment
  push    %ebx
  push    %ecx

  // set up %ds and %es

  mov     $0x20, %ebx
  movw    %bx, %ds
  movw    %bx, %es

  lret

This code is called from C with a pointer to the kernel’s entry point and an argument pointer. The thing is, the above code assumes that the address of the entry point fits in a 32-bit pointer. However, the entry point of my kernel won’t fit in a 32-bit pointer; it takes up all 64 bits of its natively sized pointer (in case you’re wondering, I cannot simply rebuild my kernel in 32-bit mode, or else I wouldn’t be asking this question). What is the best way to jump to a 64-bit address in 32-bit assembly?

Thanks. (Here are the sources for my kernel and the sources for my boot loader, if you need them.)

Upvotes: 0

Views: 1654

Answers (1)

mirabilos
mirabilos

Reputation: 5327

You first need to switch to Long Mode (64-bit mode) inside the bootloader, only then can you jump to the kernel. The reason for this is that the instruction pointer (IP) is only 16 bits wide in Real Mode and 32 bits wide in Virtual Protected Address Mode, and the high 32/48 bits are masked out except in Long Mode.

There is lots of “Long Mode in bootloader” code around, e.g. this OSDev article on entering it without intermediate 32-bit mode. Hope that helps.

If your kernel does not support being called in Long Mode, your only option is to load it below 4 GiB, because there cannot be any 32-bit code running in addresses above. Without looking at the source (sorry, it's 1:26 AM, and I need to work tomorrow), some kernels support being run at, say, their entry address & 0x00FFFFFF or something, before their final mode of operation is set up. (For example, the MirBSD kernel has VMA and LMA 0xD0100120 but expects to be jumped to at 0x00100120, and later sets up paging itself to map itself high.

Upvotes: 3

Related Questions