|
本帖最后由 不点 于 2017-4-21 17:32 编辑
看这段 “全局描述符表” 的定义:
- MyGDT:
- //gdt:
- /* 0*/ .word 0 /* NULL entry. Can be used at will. */
- gdtdesc: /* 6 bytes used for gdt descriptor. */
- .word MyGDTEnd - MyGDT - 1 /* GDT limit */
- /* The gdt linear base address will be adjusted by set_int13_handler,
- * but not touched outside int13_handler. */
- .long ABS(MyGDT) /* GDT linear address */
- /* 8*/ PM_DS16 = (. - MyGDT) /* adjusted base = int13_handler */
- .long 0x0000FFFF, 0x00009300 /* 16-bit data 64K limit */
- /*16*/ PM_DS32 = (. - MyGDT)
- .long 0x0000FFFF, 0x00CF9300 /* 32-bit data 4GB limit */
- /* The 16-bit code segment base will be adjusted by set_int13_handler,
- * but not touched outside int13_handler. */
- /*24*/ PM_CS16 = (. - MyGDT) /* adjusted base = int13_handler */
- .long 0x0000FFFF, 0x00009B00 /* 16-bit code 64K limit */
- /*32*/ LM_CS64 = (. - MyGDT)
- .long 0x0000FFFF, 0x00AF9B00 /* 64-bit code 4GB limit */
- /* This gdt entry is not used by int13_handler, but used outside. */
- /*40*/ PM_CS32 = (. - MyGDT)
- .long 0x0000FFFF, 0x00CF9B00 /* 32-bit code 4GB limit */
- MyGDTEnd:
复制代码
其中的 /*32*/ 处有 LM_CS64 = (. - MyGDT),就是关于 64 位 long_mode 的代码段的描述。此处的 32,就是“段选择器”(segment selector),它就是 CS、DS、ES、SS、FS、或 GS 之类的段寄存器可以取的值。或者说,此处 32 就是“段值”,不过,保护模式的段值,其含义不同于实模式下的段值。
当 ljmp $32, $ABS(1f) 这条指令执行的时候,CPU 仍旧处于 32 位保护模式。它执行的结果,就是实实在在地切换到 64 位 long mode(长模式)。那么接下来就是 64 位的段了,不能是 32 位了,因此用 .code64 来通知编译器,接下来的汇编语言代码需要按照 64 位的指令格式来编译。
我猜,如果你将 $32 改成 $LM_CS64,可能也行(如果真行的话,那肯定更好,因为可读性更高了)。这些 PM_DS16, PM_CS16, PM_DS32,PM_CS32,LM_CS64 之类的定义,好像都是 karyonix 做的,我之所以没有采用,那是因为我的代码先写好了,karyonix 后来才增加的这几个定义(我先写的 64 位函数,karyonix 后来添加的 PAE 功能)。我对汇编语法并不熟悉,而 karyonix 比较专业。
再看结尾的片段:
- /* XXX: Is this actually needed? */
- ljmp $40, $ABS(1f) # ljmp to enter 32-bit protected mode
- 1:
- xchgl %eax, %ebx # EAX=return value
- popl %ebx
- popl %edi
- popl %esi
- popl %ebp
- ret
复制代码
这个 $40 不就是 $PM_CS32 嘛(见前面关于 PM_CS32 的定义),即 32 位保护模式代码段。那就是,实实在在地进入常规的 32 位模式。
不知道这是谁注释的:/* XXX: Is this actually needed? */ 有可能是我注释的,也有可能是 karyonix 注释的,也有可能是别的开发者注释的。反正我没什么记性了。
究竟需要不需要这个跳转,我也不能肯定。你也可以试试去掉(或者注释掉)这条 ljmp $40, $ABS(1f),看看有没有什么异常现象发生?
多执行这条指令,肯定是更安全一点(没什么坏处),但会浪费 CPU 执行这么一条指令的时间。
如果确实能够证明去掉这条指令也行的话,那还是去掉了更好,毕竟少执行一条指令、少让 cpu 走弯路,那是更优化的。如果要证明的话,需要在 AMD 和 Intel 都验证通过,那才算是一个有效的证明。
|
|