Page 1 of 1

ljmp seems to get truncated

Posted: Wed Jan 11, 2006 12:03 am
by Searinox
Hello!
I have a strange problem with the long jump after enabling protected mode. The jump instruction seems to get truncated if I try to jump, for example to 0x10000 in a linear code segment (this is where my kernel is).
My GDT looks like this:

Code: Select all

gdt_ptr:
   .word gdt_end - gdt - 1   /* GDT Limit */
gdt_b:   .long gdt   /* GDT base, set by the code */

gdt:
   .word   0, 0
   .byte   0, 0, 0, 0

   /* Linear Code segment */
   .word   0xFFFF, 0x0000
   .byte   0x0, 0x9A, 0xCF, 0 /* Code, Ring-0, Page-granular */

   /* Linear Data segment */
   .word   0xFFFF, 0x0000
   .byte   0x0, 0x92, 0xCF, 0 /* Data, Ring-0, Page-granular */

   /* Video data */
   .word   4000, 0x8000
   .byte   0xb, 0x92, 0, 0
   
   /* Minikernel segment */
   .word   0x1000, 0x0000
   .byte   0x1, 0x9A, 0, 0
gdt_end:
The ljmp looks like this:

Code: Select all

/* Load the GDT */
   lgdt   gdt_ptr

/* Enter Protected Mode */
   movl   %cr0, %eax
   orb      $1, %al
   movl   %eax, %cr0

.code32
/* We're in! */
/* Far jump in order to flush the prefetch queue */
   ljmp   $0x8, $0x10000
Bochs outputs the following:

Code: Select all

00001154704i[CPU  ] protected mode
00001154704i[CPU  ] CS.d_b = 16 bit
00001154704i[CPU  ] SS.d_b = 16 bit
00001154704i[CPU  ] | EAX=00000011  EBX=00000000  ECX=00070002  EDX=00000000
00001154704i[CPU  ] | ESP=0000fffc  EBP=00000000  ESI=00007343  EDI=0000ffde
00001154704i[CPU  ] | IOPL=0 vm RF ac nt of df if tf sf zf af PF cf
00001154704i[CPU  ] | SEG selector     base    limit G D
00001154704i[CPU  ] | SEG sltr(index|ti|rpl)     base    limit G D
00001154704i[CPU  ] |  CS:0000( 0000| 0|  0) 00000000 0000ffff 0 0
00001154704i[CPU  ] |  DS:07c0( 0000| 0|  0) 00007c00 0000ffff 0 0
00001154704i[CPU  ] |  SS:0000( 0000| 0|  0) 00000000 0000ffff 0 0
00001154704i[CPU  ] |  ES:1000( 0000| 0|  0) 00010000 0000ffff 0 0
00001154704i[CPU  ] |  FS:0000( 0000| 0|  0) 00000000 0000ffff 0 0
00001154704i[CPU  ] |  GS:0000( 0000| 0|  0) 00000000 0000ffff 0 0
00001154704i[CPU  ] | EIP=00007c5c (00007c5c)
00001154704i[CPU  ] | CR0=0x00000011 CR1=0 CR2=0x00000000
00001154704i[CPU  ] | CR3=0x00000000 CR4=0x00000000
00001154704i[CPU  ] >> ea
00001154704i[CPU  ] >> 00
00001154704i[CPU  ] >> 00
00001154704i[CPU  ] >> 10
00001154704i[CPU  ] >> 00
00001154704i[CPU  ] >> : jmp far 0010:0000
00001154704i[SYS  ] bx_pc_system_c::Reset(SOFTWARE) called
00001154704i[     ] Ctrl-C detected in signal handler.
00001154704i[     ] dbg: Quit

Re:ljmp seems to get truncated

Posted: Wed Jan 11, 2006 2:33 am
by durand
After your "Enter Protected Mode" code bit and you've successfully enabled the bit in CR0, you're still running in a 16 bit segment ;) So you shouldn't specify .code32 yet. You need to far jump in order for the CS register to be reloaded with the new 32 bit GDT entry.

So, instead you should ljmp into a 32 bit code segment like so:

Code: Select all

/* Enter Protected Mode */
   movl   %cr0, %eax
   orb      $1, %al
   movl   %eax, %cr0

/* We're enabled! */
/* Far jump into a 32 bit code segment */
   ljmp   $0x8, $0x10000


By the way, when you specify .code16 in gas source, the compiler just prepends all 32 bit operations with the 32 bit stub thingy so that the CPU executes a 32 bit command in 16 bit mode. Once you specify the .code32 again, it doesn't prepend the 32 bit stub.

So your ljmp in a code32 section isn't being seen as 32 bit code.. just 16 bit code because of it still using a 16 bit segment. So the CPU interprets it incorrectly.

Re:ljmp seems to get truncated

Posted: Wed Jan 11, 2006 2:35 am
by kataklinger
Try this.

Code: Select all

/* Load the GDT */
   lgdt   gdt_ptr

/* Enter Protected Mode */
   movl   %cr0, %eax
   orb      $1, %al
   movl   %eax, %cr0
   ljmp   $0x8, pm_code

.code32
/* We're in! */
/* Far jump in order to flush the prefetch queue */
pm_code:
  /* some code */

Re:ljmp seems to get truncated

Posted: Wed Jan 11, 2006 10:56 am
by Searinox
Thank you both already.
durand, I tried your suggestion but as complains:

Code: Select all

boot/loader.s: Assembler messages:
boot/loader.s:95: Error: 16-bit jump out of range
I then tried kataklinger's solution with the following code:

Code: Select all

   ljmp   $32, $pmode

.code32
pmode:
   ljmp   $0x8, $0x10000
Where $32 is a gdt entry that is exactly the boot segment again.
The jump is executed correctly to the pmode label, yet in the bochs debugger, the the following jump into the flat segment looks like this:

Code: Select all

Next at t=1154705
(0) [0x00007c61] 0020:00000061 (unk. ctxt): jmp far 0001:0000         ; ea00000100
which can't be right!
I'm happy with any idea ;)

Re:ljmp seems to get truncated

Posted: Wed Jan 11, 2006 11:06 am
by Pype.Clicker
address size override prefix ? i suspect it to be O32 or A32 (respectively prefix opcode 0x66 and 0x67 iirc)...

Typically, the "jump to 32 bit code" features something like

Code: Select all

    ; cr0 := cr0 OR 1;
    0x67 0x66 ; next instruction is to be decoded as 32-bit 
            ; even if current CPU mode is 16-bit
     far-jmp-opcode (0xea did the trick for me)
            offset_value (32 bits)
            selector_value (16 bits)
put those bytes in a "db", disassemble them and see how your assembler will like you to write that.

Re:ljmp seems to get truncated

Posted: Wed Jan 11, 2006 11:25 am
by Searinox
Thanks!
This seems finally to work:

Code: Select all


.byte 0x66         /* Address size override prefix */
.byte 0xea
.int 0x10000      /* Address */
.word 0x8         /* Segment selector */