"调用"切换到保护模式后

时间:2012-02-06 17:08:45

标签: assembly kernel att

我正在尝试切换到intel x86中的保护模式。

我已经用lgdt加载了我的gdt,将cr0的P标志设置为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!

谢谢

2 个答案:

答案 0 :(得分:3)

您需要确保汇编程序将受保护模式开关后的代码转换为32位代码,并使用.code32(或者use32 in nasm)指令。

此外,您的保护模式例程后的返回地址不再有效。在那之后你真的无法回归。而是将esp设置为有用的东西并继续。

答案 1 :(得分:3)

设置或清除PE的CR0移动必须立即,然后跳远重新加载PC,然后必须重新加载%esp以及所有段寄存器。您需要在触摸堆栈或启用中断之前执行所有。并且(正如drhirsch所说),即使在使实模式堆栈无效之前弹出返回地址,也不可能从此操作返回,因为返回地址是实模式地址。 / p>

您似乎正在尝试使用lret重新加载PC并同时重新启用中断,但这不起作用,因为堆栈指针无效。正确的代码看起来像这样:

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

您应该阅读英特尔的system programming guide,特别是第9章(机器初始化),尤其是第9.9节,其中详细介绍了如何进行保护模式切换。