5

我正在尝试切换到 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!

谢谢

4

2 回答 2

3

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

您似乎正在尝试使用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

您应该阅读 Intel 的系统编程指南,特别是第 9 章(机器初始化),尤其是第 9.9 节,其中详细描述了如何进行保护模式切换。

于 2012-02-06T18:19:51.037 回答
3

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.

于 2012-02-06T17:49:19.527 回答