OSDev.org
https://forum.osdev.org/

Far jump to higher half 64bit address from 32 bit binary
https://forum.osdev.org/viewtopic.php?f=1&t=23100
Page 1 of 1

Author:  Qeroq [ Fri Feb 04, 2011 10:49 am ]
Post subject:  Far jump to higher half 64bit address from 32 bit binary

Hello,
to circumvent the problems I had with GRUB2 loading my AMD64 kernel, I decided to write a small loader binary in 32 bits that resides in the lower half, does the long mode readying stuff, loads the kernel ELF64 binary that is attached as a multiboot1 module and jump to its entry point.

Although the last point on the list might sound simplest, its actually the one I have most of the problems with. To jump to the 64 bit higher half entry point address (somewhere near 0xFFFFFF000000000), I planned to far jump to a 64 bit realm in the loader itself, that performs the jump to the actual entry point. My problem is to embed this 64 bit realm into my elf32 kernel. I tried the following approach, but it did not work out (machine resetting):
Code:
    ; mov rax, [0x1002]
    ; Entry point address QWORD is stored at 0x1002
    db 0x48
    db 0x8b
    db 0x04
    db 0x25
    db 0x02
    db 0x10
    db 0x00
   
    ; Spacer
    db 0x0
   
    ; jmp rax
    db 0xff
    db 0xe0

It's not a really clean approach and I really don't know how to debug the code above, so is there any way of jumping to this high entry point address besides of making the binary elf64 itself (GRUB Legacy, unpatched, and I want it to stay this way) and linking the 64 bit realm to it or adding another module that contains just these few bytes of 64 bit code?

Author:  xenos [ Fri Feb 04, 2011 12:25 pm ]
Post subject:  Re: Far jump to higher half 64bit address from 32 bit binary

I don't know which assembler you are using (NASM?), but I don't think you need to hardcode the jump byte by byte. In my kernel, I use a small 64 bit trampoline which is identity mapped in the lower half and to which I can jump from protected mode using a 32 bit far jump. As soon as I am in a 64 bit code segment, I load rax with a higher half address.

Code:
.code32
   // enable paging (activate long mode), enable floating point exception
   movl %cr0, %ecx
   orl $0x8000000a, %ecx
   movl %ecx, %cr0

   lgdt (pGDT64 - KERNEL_OFFSET)

   // jump into long mode
   ljmpl $0x20, $(long_mode - KERNEL_OFFSET)

.code64
long_mode:
   movabsq $(stack + STACK_SIZE), %rsp

   // time for some C!
   movabsq $(KernelEntry), %rax
   callq *%rax // Run the boot module by calling its C++ entry point

Author:  Qeroq [ Fri Feb 04, 2011 1:47 pm ]
Post subject:  Re: Far jump to higher half 64bit address from 32 bit binary

I actually tried to do so, but as I'm linking my loader as elf32, the object files cannot be elf64 which would be required for embedding the trampoline. I try to stick with elf32 as it should be most compatible among multiboot bootloaders, especially GRUB Legacy. In the ideal case my boot.s would not something like this, but doing this I run into the mentioned linking issues:

Code:
section .text
[BITS 32]
; Code, code, code...
; ------------------------------------------------------------------------------
; Realm 64
; ------------------------------------------------------------------------------
[BITS 64]
realm64:
    mov rax, [0x1002]
    jmp rax


BTW: I'm using NASM (Intel Syntax, I could never really accommodate myself to GAS).

Edit: Typo

Author:  xenos [ Fri Feb 04, 2011 2:43 pm ]
Post subject:  Re: Far jump to higher half 64bit address from 32 bit binary

I think I get your point. I guess in that case you really need to replace the 64 bit trampoline code from my example by some hand-written byte code as in your original post. Most importantly, you need to hard-code the higher half entry address of your 64 bit kernel, since you cannot use 64 bit relocations in a 32 bit elf file.

Another possibility would be to include the trampoline in your 64 bit kernel module and to use an identity (or some other temporary lower half) mapping in the early boot phase. Then you can jump from your 32 bit loader to the lower half mapped trampoline in your 64 bit kernel. From there you jump to the higher half and clear the identity mapping.

In any case, you must be in a 64 bit code segment before you can use 64 bit addresses, so either way you will need some trampoline below 4GB.

Author:  Qeroq [ Fri Feb 04, 2011 3:50 pm ]
Post subject:  Re: Far jump to higher half 64bit address from 32 bit binary

Developing the idea of the hard coded address a bit further, the byte code could dynamically be generated, written to memory and jumped to. That enables me to use the entry point address as it is defined in the ELF64 header and still not requiring it to provide a trampoline by itself. I think I will give this a try.

Edit: Actually this C code seems to create the right opcodes for jumping to 0x1234567890ABCDEF, I hope it will work out...
Edit 2: It does work after having spend several hours debugging my paging code, so this solution seems to be a serious alternative when loading 64 bit kernels.
Code:
    uint8_t *trampoline = (uint8_t *) (BOOT_INFO_OFFSET + 0x1000 - 12);
    trampoline[0] = 0x48;
    trampoline[1] = 0xb8;
    trampoline[10] = 0xff;
    trampoline[11] = 0xe0;
    * ((uint64_t *) (BOOT_INFO_OFFSET + 0x1000 - 10)) = 0x1234567890ABCDEF;

Author:  xenos [ Sat Feb 05, 2011 2:19 am ]
Post subject:  Re: Far jump to higher half 64bit address from 32 bit binary

Nice idea, I think that should work as well. The byte coded mov / jmp looks perfectly fine to me.

Page 1 of 1 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/