问题是关于在英特尔 i386 上从实模式切换到保护模式时代码段选择器的持久有效性。切换代码如下(摘自bootasm.S
xv6 x86版本):
9138 # Switch from real to protected mode. Use a bootstrap GDT that makes 9139 # virtual addresses map directly to physical addresses so that the 9140 # effective memory map doesn’t change during the transition. 9141 lgdt gdtdesc 9142 movl %cr0, %eax 9143 orl $CR0_PE, %eax 9144 movl %eax, %cr0 9150 # Complete the transition to 32−bit protected mode by using a long jmp 9151 # to reload %cs and %eip. The segment descriptors are set up with no 9152 # translation, so that the mapping is still the identity mapping. 9153 ljmp $(SEG_KCODE<<3), $start32
GDT 布局如下:
9182 gdt: 9183 SEG_NULLASM # null seg 9184 SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg 9185 SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
在执行第 9144 行之后,处理器切换到保护模式,其中仅启用段内存管理(但尚未启用分页)。我的理解是,既然已经启用了segment MM,那么接下来的指令的取指应该符合segment MM的规则。然而,此时(就在第 9153 行之前),代码选择器保持为 0,在我的理解中,这意味着代码段应该选择了 GDT 中的第零个描述符,即为空。但是我的问题自然而然地出现了,这样的空描述符如何加载假定的ljmp
指令?我试图通过谷歌搜索来回答我的问题,一份文件给出了如下解释:http ://www.logix.cz/michal/doc/i386/chp10-03.htm#10-03
段寄存器继续指向与实地址模式相同的线性地址
这句话似乎回答了我的问题:如果段寄存器继续指向同一个线性地址,那么下一条指令应该和实模式下一样,即ljmp
. 但是我马上有一系列新问题:为什么段选择器可以“继续指向相同的线性地址”?处理器不是改成保护模式了吗?0 in 的值不是%cs
指向第零个描述符,而不是第一个(在第 9184 行设置),它是假定的要获取ljmp
指令的描述符吗?x86 CPU 是如何神奇地知道ljmp
它应该执行的下一条指令是什么?任何描述这种魔法的手册中的描述在哪里?我试图说服自己ljmp
已在处理器的指令队列中预取,但同一网页的第二段告诉我预取的ljmp
(如果有的话)已失效,因此 CPU 应重新获取下一条指令。您能否解释一下“段寄存器如何继续指向与实地址模式相同的线性地址”?谢谢你。
PS,我正在使用的 CPU 与 intel i386 兼容。