2012-02-06 26 views
5

Estoy intentando cambiar al modo protegido en intel x86."llamar" después de cambiar al modo protegido

He cargado mi GDT con lgdt, establezca el indicador P de cr0 a 1 y todos los selectores de segmentos, pero cuando regreso de la llamada a la función, no puedo llamar a cualquier otra función o me sale este error

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

Aquí es mi función 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 

Y mis llamadas

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

Gracias

Respuesta

3

Debe asegurarse de que el ensamblador traduce el código que sigue al interruptor de modo protegido como código de 32 bits, con una directiva .code32 (o use32 en nasm).

Además su dirección de devolución después de su rutina de modo protegido ya no es válida. No puedes volver a nada después de eso. En cambio, establezca esp algo útil y continúe.

+0

¡Gracias! ¡El .code32 funciona! – marmottus

3

Un movimiento a CR0 que establece o borra PE debe ser inmediatamente seguido por un salto lejos para recargar la PC, y luego debe volver a cargar %esp, así como todos los registros de segmento. Necesita hacer todo esto antes de toca la pila o habilita las interrupciones. Y (como dice drhirsch) es imposible devolver desde esta operación, incluso si apaga la dirección de retorno antes de invalidar la pila en modo real, porque la dirección de retorno es una dirección en modo real.

Parece que está intentando usar lret para volver a cargar la PC y simultáneamente volver a habilitar las interrupciones, pero eso no funcionará, porque el puntero de la pila no es válido. código correcto sería algo como esto:

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 

usted debe leer de system programming guide especialmente la sección 9.9, en particular el capítulo 9 (inicialización de la máquina) de Intel, que describe en detalle cómo hacer un interruptor de modo protegido.

+0

AFAIK siempre que no se ejecute ningún salto lejano, la CPU todavía funciona en modo real. Leí el 'push $ 8; push reload_segments; retl' como una forma creativa de ejecutar un salto lejano. Pero la forma en que describes es la manera canónica y funciona confiable de esa manera. – hirschhornsalz

+1

Es creativo, pero no está garantizado que funcione: el manual de arquitectura es bastante claro: "Inmediatamente después de la instrucción MOV CR0, ejecute una instrucción JMP o lejos LLAMADA ... Pueden ocurrir fallas aleatorias si * existen otras instrucciones * entre [estas instrucciones] ". (énfasis mío) ([Intel® 64 y IA-32 Architectures Software Manual de desarrollador Volumen 3: Guía de programación del sistema] (http://www.intel.com/content/dam/doc/manual/64-ia-32-architectures -software-desarrollador-sistema-programación-manual-325384.pdf), sección 9.9) – zwol

Cuestiones relacionadas