切换到保护模式后“呼叫”
Posted
技术标签:
【中文标题】切换到保护模式后“呼叫”【英文标题】:"call" after switching to Protected Mode 【发布时间】:2012-02-06 17:08:45 【问题描述】:我正在尝试在 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!
谢谢
【问题讨论】:
【参考方案1】:您需要确保汇编器使用.code32
(或nasm 中的use32
)指令将受保护模式开关后的代码转换为32 位代码。
此外,您在受保护模式例程之后的返回地址不再有效。在那之后你不能真正回到任何东西。而是将 esp 设置为有用的东西并继续。
【讨论】:
【参考方案2】:必须立即移动到设置或清除 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 的 system programming guide,尤其是第 9 章(机器初始化),尤其是第 9.9 节,其中详细描述了如何进行保护模式切换。
【讨论】:
AFAIK 只要不执行远跳转,CPU 仍然在实模式下工作。我读了push $8; push reload_segments; retl
作为执行远跳的创造性方式。但是您描述的方式是规范的方式,并且以这种方式工作可靠。
很有创意,但不保证能正常工作——架构手册上说的很清楚:“紧跟MOV CR0指令,执行far JMP或far CALL指令……可能会出现随机故障如果在 [这些指令] 之间存在其他指令。” (强调我的)(Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3: System Programming Guide,第 9.9 节)以上是关于切换到保护模式后“呼叫”的主要内容,如果未能解决你的问题,请参考以下文章