Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"call" after switching to Protected Mode

I'm trying to switch to the protected mode in intel x86.

I've loaded my gdt with lgdt, set the P flag of cr0 to 1 and all the segments selectors but when I return from the function call, I can't call any other function or I get this error

qemu: fatal: Trying to execute code outside RAM or ROM at 0xfeeb7c5b

Here is my switch_to_pmode function:

gdtr:
.short      23  // limit
gdtr_base:
.long       0   // base

switch_to_pmode:
    movl $null_segment, %eax        // Address of the first byte of the GDT
    movl %eax, gdtr_base

    cli             // disable interrupts

    lgdt (gdtr)

    movl %cr0, %eax
    or $0x1, %eax
    movl %eax, %cr0         // Set the PE flag

    push $0x8
    push $reload_segments
    lret

reload_segments:
    movl $0x10, %eax
    movl %eax, %ds
    movl %eax, %ss
    movl %eax, %es
    movl %eax, %fs
    movl %eax, %gs

    ret

foo:
    ret

And my calls

_start:
    call switch_to_pmode
    call foo // <----- Ouch!

Thank you

like image 891
marmottus Avatar asked Feb 06 '12 17:02

marmottus


2 Answers

You need to make sure the assembler translates the code following the protected mode switch as 32 bit code, with a .code32 (or use32 in nasm) directive.

Additionally your return address after your protected mode routine is no longer valid. You cant really return to anything after that. Instead set esp to something useful and go on.

like image 186
Gunther Piez Avatar answered Nov 12 '22 02:11

Gunther Piez


A move to CR0 that sets or clears PE must be immediately followed by a far jump to reload the PC, and then you must reload %esp as well as all the segment registers. You need to do all of this before you touch the stack or enable interrupts. And (as drhirsch says) it's impossible to return from this operation, even if you pop the return address off before you invalidate the real-mode stack, because the return address is a real-mode address.

You appear to be trying to use lret to reload the PC and simultaneously re-enable interrupts, but that won't work, because the stack pointer is invalid. Correct code would look something like this:

switch_to_pmode:
    # ... what you have ...

    movl %eax, %cr0
.code32
    ljmpl reload_segments

reload_segments:
    # ... what you have ...
    movl $pm_stack, %esp
    sti # perhaps

    # and then just go on with your startup code here
    call foo

You should read Intel's system programming guide, particularly chapter 9 (machine initialization), especially section 9.9, which describes in detail how to do a protected mode switch.

like image 39
zwol Avatar answered Nov 12 '22 03:11

zwol