2012-02-06 1 views
5

Я пытаюсь переключиться в защищенный режим в Intel x86.«звонок» после переключения в защищенный режим

Я загрузил мой GDT с lgdt, установите P флаг cr0 1 и все сегменты селекторы, но когда я вернусь из вызова функции, я не могу назвать любую другую функцию, или я получаю эту ошибку

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

Вот моя switch_to_pmode функция:

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 

И мои звонки

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

Спасибо вы:

ответ

3

Вы должны убедиться, что ассемблер переводит код, следующий за переключателем защищенного режима, как 32-битный код, с директивой .code32 (или use32 в nasm).

Кроме того, ваш обратный адрес после действия режима защищенного режима более недействителен. После этого вы не можете вернуться ни к чему. Вместо этого установите esp на что-то полезное и продолжайте.

+0

Спасибо! Работает .code32! – marmottus

3

Переход к CR0, который устанавливает или удаляет PE должен быть немедленно с последующим дальним переходом, чтобы перезагрузить компьютер, а затем вы должны перезагрузить %esp, а также все сегментные регистры. Вам нужно сделать все это до, вы касаетесь стека или включаете прерывания. И, как говорит дриршш, невозможно вернуть из этой операции, даже если вы вытащите обратный адрес, прежде чем вы отмените стек реального режима, потому что обратный адрес является адресом реального режима.

Вы пытаетесь использовать lret для перезагрузки ПК и одновременного повторного включения прерываний, но это не сработает, потому что указатель стека недействителен. Правильный код будет выглядеть примерно так:

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 

Вы должны прочитать от Intel system programming guide, в частности, глава 9 (инициализация машины), особенно раздел 9.9, который подробно описывает, как сделать защищенный режим переключатель.

+0

AFAIK до тех пор, пока не выполняется большой скачок, процессор все еще работает в реальном режиме. Я прочитал 'push $ 8; push reload_segments; retl' как творческий способ выполнения даун-прыжка. Но способ, которым вы описываете, - это канонический путь, и он работает таким же образом. – hirschhornsalz

+1

Это креативна, но это не гарантированно работает - руководство по архитектуре довольно ясно: «Сразу после команды MOV CR0 выполните длинную инструкцию JMP или far CALL ... Случайные сбои могут возникать, если * существуют другие инструкции * между [эти инструкции] ». (основное внимание) ([Руководство разработчика программного обеспечения Intel® 64 и IA-32 Volume Volume 3: Руководство по системному программированию] (http://www.intel.com/content/dam/doc/manual/64-ia-32-architectures -software-development-system-programming-manual-325384.pdf), раздел 9.9) – zwol