0

问题是关于在英特尔 i386 上从实模式切换到保护模式时代码段选择器的持久有效性。切换代码如下(摘自bootasm.Sxv6 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 兼容。

4

1 回答 1

1

现代参考是英特尔软件开发人员手册,第 3A 卷,第 9.9.1 节,“切换到保护模式”。

英特尔并不热衷于解释魔法在内部是如何运作的。它所说的以及您需要知道的所有内容是,如果您movl %eax, %cr0紧随其后的是远跳或远呼叫,那么一切都会奏效。如果您将任何其他指令放在那里,那么“可能会发生随机故障”(他们的措辞)。

正如它所说,%cs继续保持其先前的值,并且大概是如果您将远调用作为movl %eax, %cr0. (堆栈在哪里是另一个有趣的问题——我认为每个人都使用跳转,所以它很少出现。)但是对于这条指令,它显然没有以通常的方式用作选择器。

关于它如何工作的一个猜测:我们知道在保护模式下,有隐藏的寄存器存储段属性,并在加载段寄存器时从描述符表中重新加载。因此,movl %eax, %cr0可能会导致对应的隐藏寄存器%cs加载一个段的属性,该段的基地址是当前 16 位段的线性地址:例如,如果%cs包含0x1234,则它可能是一个基地址为 的段0x12340。但是%cs寄存器本身可以单独放置,暂时不匹配其隐藏的对应物。然后,如果 的高位%eip为零,则将从正确的位置获取下一条指令。该指令必须是将重新加载的长跳转%cs以及隐藏段属性寄存器。

也有可能它只是设置了一些内部标志,上面写着“即使在保护模式下,根据实模式地址转换获取下一条指令”。然后,当发生远跳转时,或者在获取一条指令之后,或者类似的事情时,这个标志就会被清除。

于 2021-04-07T04:37:50.843 回答