10
flush_gdt:
    lgdt [gdtr]
    jmp 0x08:complete_flush

complete_flush:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    ret

我无法理解这段代码的作用。flush_gdt是一个标签没问题,然后将指针lgdt [gdtr]加载到寄存器中,然后从.48-bitgdtrjmp 0x08:complet_flush

jmp 指令有什么作用?然后为什么我们将 0x10 移动到 ax 然后再移动到其他寄存器

4

1 回答 1

29

x86 支持两种虚拟内存方案(在此处阅读):

  • 分段,必须,使用分段表 GDT 管理。
  • 分页,可选,使用页表 PDT 管理。

大多数操作系统都希望使用分页并且不希望分段,但它必须而且不能只是禁用。

所以诀窍是禁用它的效果,因为它不存在。这通常可以通过创建 4 个大的重叠段描述符(在空段旁边)来完成:

  • 段索引 0:空段描述符
  • 段索引 1:特权(内核)模式的代码段描述符
  • 段索引 2:特权(内核)模式的数据段描述符
  • 段索引 3:非特权(用户)模式的代码段描述符
  • 段索引 4:非特权(用户)模式的数据段描述符

所有这些段都从0x00000000到开始0xffffffff,因此您最终会得到重叠的大段,即特权代码和数据,以及同时存在的非特权代码和数据。这应该打开虚拟内存并禁用分段效果。

处理器使用段选择器(段寄存器cs, ds, ss...)来找出正确的段(再一次,分段是必须的)。

每个段选择器都是 16 位大小,并具有以下布局(源代码):

在此处输入图像描述

  • 前两位表示特权级别,x86 支持 4 个级别,但实际使用的只有两个(00最高和11最低)。

  • 第三位表示应该使用该表,主要0是 GDT。

  • 其余 13 位表示段索引。

如果您解释0x08加载的cs,它将是二进制的:

0000000000001     0         00
index 1 (code)   GDT    privileged

并且0x10加载在ds, ss, ... 中:

0000000000010     0         00
index 2 (data)   GDT    privileged

如果您阅读任何用户模式程序的段选择器,您应该会看到该cs值为27( 0x1b),这意味着:

0000000000011     0         11
index 3 (code)   GDT   non-privileged

并且数据选择器ds, ss, ... 应该存储 35 ( 0x23):

0000000000100     0         11
index 4 (data)   GDT   non-privileged

数据段选择器(寄存器)可以使用简单mov的指令轻松修改,但cs不能与 一起使用mov,因此您可以使用jmp 0x08:OFFSET将段配置加载到代码段选择器中。

于 2014-06-01T11:02:41.863 回答