从32位保护模式返回实模式:是否可省略远跳转指令?
从32位保护模式返回实模式:能否省略远跳转指令?
我正在从32位保护模式返回实模式,参考的代码里包含一条远跳转指令,我的当前代码如下:
; At this point we are still in protected mode with CS pointing ; to a 16-bit descriptor. bits 16 cli ; Just to make sure. mov eax, cr0 and eax, 0x7FFFFFFe ; Disable paging bit & disable 16-bit pmode. mov cr0, eax f1: jmp 0:GoRMode ; ?? Is this far jump needed ?? GoRMode: mov sp, 0x8000 mov ax, 0 ; Reset segment registers to 0. mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax pushf pop ax or ax, strict word 0x200 ; IF := 1 (sti) by the iret below. push ax push word my_segment push word my_offset iret ; Pops IP first, then CS, then FLAGS.
我的核心问题是:能否省略这条远跳转指令而不引发异常?
我的思路是最终的iret指令会负责设置CS寄存器。我已在QEMU(含/不含KVM)、VirtualBox、v86及86Box(Intel 430VX芯片组、Pentium-S P54C 90MHz CPU)环境中测试,有无该远跳转指令代码均可正常运行,且已验证此场景下iret不会弹出SS和SP寄存器。
通过使用iret,我可避免中断过早触发的问题:仅当iret执行时才会启用中断,此时系统已完全进入实模式(即所有段寄存器均使用实模式描述符)。另外我知道可将末尾几条指令替换为sti+jmp my_segment:my_offset,但这可能因两条指令间触发中断而失效。
回答
可以省略这条远跳转指令,不会引发异常,原因如下:
- 执行
mov cr0, eax关闭保护模式位后,CPU进入实模式,但此时CS仍保留着之前的16位保护模式选择子。不过实模式下段寄存器的作用是直接作为段基址的高16位(左移4位),即便CS此时的数值是保护模式选择子,后续的iret指令会原子性地将CS设置为my_segment,直接覆盖原有值,因此中间的远跳转并非必需。 - 你的多环境测试结果已经验证了这一点:无论是虚拟机还是硬件模拟器,省略远跳转后代码都能正常运行,说明这种做法具备兼容性。
- 用
iret来完成中断启用和跳转的方案确实更安全:iret会一次性设置FLAGS(包括IF位)和CS:IP,不存在sti和远跳转之间的中断触发窗口,避免了中断过早触发导致的问题。
内容的提问来源于stack exchange,提问作者pts
相关产品推荐
相关产品推荐

