# Load the GDT.
mov $gdt_descriptor, %ecx
lgdt (%ecx)
mov $0x10, %cx
mov %cx, %ds
mov %cx, %es
mov %cx, %fs
mov %cx, %gs
mov %cx, %ss
ljmp $0x8, $1f
1: mov $kernel_stack, %esp
我无法理解这段代码的作用。 为什么在加载GDT后将mov $ 0x10转换为cx然后再转到其他寄存器? ljmp指令做了什么?
答案 0 :(得分:3)
它从lgdt
告诉CPU的新GDT加载段描述符缓存(在CPU内部)。
当您更改表条目或更改表格指向的位置时,CPU内部的段描述不会自动更新。
您甚至可以切换回实时模式,DS base = 0 limit = 4GiB(ES和SS相同),并在实模式下使用32位地址,直到下一个mov ds, r16
或{{1} }指令覆盖缓存的段描述。 (这被称为big / huge unreal mode,如果你也为CS做了很多,但这不太方便,因为实模式中的中断只能保存IP,而不是EIP。)
pop ds
是far jmp
,它设置CS(在这种情况下使用与数据描述符不同的描述符)。 x86不允许ljmp
或mov
设置CS,只有远程跳转。据推测,CPU不会通过此跳转来改变模式,否则asm源需要使用pop
或.code32
指令。
目标是.code16
方向的1:
标签。因此f
到mov
使用GDT索引1中的任何代码段设置进行解码/运行。(段选择器的低3位是权限位,因此%esp
是GDT索引1,$8
是GDT索引2.)
将$0x10
与mov
从设置%ss
的指令分开是有点奇怪的,因为x86会自动推迟中断,直到指令 之后> %esp
到mov
。这使您可以在不使用SS
/ cli
的情况下自动设置SS:SP,但可能此代码在已禁用中断的情况下运行。此代码可能仅在启动期间运行一次,因此只要在设置新的GDT和IDT所需的时间内禁用中断就有意义。