在bootloader中远程跳转gdt

abk*_*kds 8 x86 assembly bootloader gdt

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
Run Code Online (Sandbox Code Playgroud)

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

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

小智 17

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

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

大多数操作系统都希望使用分页而不希望分段,但必须禁用它.

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

  • 段索引0:空段描述符
  • 段索引1:特权(内核)模式的代码段描述符
  • segment index 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
Run Code Online (Sandbox Code Playgroud)

0x10被加载在ds,ss...:

0000000000010     0         00
index 2 (data)   GDT    privileged
Run Code Online (Sandbox Code Playgroud)

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

0000000000011     0         11
index 3 (code)   GDT   non-privileged
Run Code Online (Sandbox Code Playgroud)

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

0000000000100     0         11
index 4 (data)   GDT   non-privileged
Run Code Online (Sandbox Code Playgroud)

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

  • 毫无疑问,这是对现有的一些复制粘贴代码最简洁的解释之一。这值得存档。 (3认同)