保护模式启动内核

保护模式启动内核我们已经从实时模式进入保护模式,现在我们马上就要启动OS Kernel了

OS Kernel运行在32-bit段模式,而当前我们却仍然处于16-bit段模式

这是怎么回事?为了了解这个问题,我们需要仔细探讨一下IA-32的段模式的实现方法

IA-32共提供了6个16-bit段寄存器:CS,DS,SS,ES,FS,GS

但事实上,这16-bit只是对程序员可见的部分,但每个寄存器仍然包括64-bit的不可见部分

可见部分是为了供程序员装载段寄存器,但一旦装载完成,CPU真正使用的就只是不可见部分,可见部分就完全没有用了

不可见部分存放的内容是什么?具体格式我没有看到相关资料,但可以确定的是隐藏部分的内容和段描述符的内容是一致的(请参考段描述的格式),只不过格式可能不完全相同

但格式对我们理解这一点并不重要,因为程序员不可能能够直接操作它

我们以CS寄存器为例,对于其它寄存器也是一样的:在实时模式下,当我们执行一个装载CS寄存器的指令的时候(jmp,call,ret等),相关的值会被装入CS寄存器的可见部分,但同时CPU也会根据可见部分的内容来设置不可见部分

比如我们执行"ljmp x1234, $go "之后,CS寄存器的可见部分的内容就是1234h,同时,不可见部分的32-bit Base Address域被设置为00001234h,20-bit的Limit域被设置为固定值10000h,也就是64 KB,Access Information部分的其它值我们不去考虑,只考虑其D/B位,由于执行此指令时处于Real Mode模式,所以D/B被设置为0,表示此段是一个16-bit段

当对CS寄存器的可见部分和不可见部分的内容都被设置之后,CS寄存器的装载工作完成

随后当CPU需要通过CS的内容进行地址运算的时候,则仅仅引用不可见部分

在保护模式下,当我们执行一个装载CS寄存器的指令的时候,段选择子(Segment Selector)被装入CS寄存器的可见部分,同时CPU根据此选择子到相应的描述符表中(GDT或LDT)找到相应的段描述符并将其内容装载入CS寄存器的不可见部分

随后CPU当需要通过CS的内容进行地址运算的时候,也仅仅引用不可见部分

从上面的描述可以看出,事实上CPU在引用段寄存器的内容进行地址运算时,实时模式和保护模式是一致的

另外,也明白了为什么我们在实时模式下设置的段寄存器的内容到了保护模式下仍然引用的是16-bit段

那么我们如何将CS设置为引用32-bit段?方法就像我们前面所讨论的,使用jmp或call指令,引用一个段选择子,到GDT中装载一个引用32-bit段的段描述符

需要注意的是,如果CS寄存器的内容指出当前是一个16-bit段,那么当前的地址模式也就是16-bit地址模式,这与你当前是出于实时模式还是保护模式无关

而我们装载32-bit段的jmp指令或call指令必须使用的是32-bit地址模式

而我们当前的boot部分代码是16-bit代码,所以我们必须在此jmp/call指令前加上地址转换前缀代码66h

下面的例子就是使用jmp指令装入32-bit段

Jmpi指令的含义是段间跳转,其Opcode为Eah,其格式为:jmpi Offset, Segment Selector

# 由于当前的代码是16-bit代码,而我们要执行32-bit地址模式的指令,指令前# 需要有地址模式切换前缀66h,如果我们直接写jmp指令,由编译器来生成代码# 的话,是无法作到这一点的,所以我们直接写相关数据

.byte 0x66, 0xea # prefix + jmpi-opcode.long 0x1000 # Offset.word __KERNEL_CS # CS segment selector上面的代码相当于32-bit指令:jmpi 0x1000,__KERNEL_CS如果__KERNEL_CS段选择子所引用的段描述符设置的段空间为线形地址[0,4 GB],而我们将OS Kernel放在物理地址1000h,那么此jmpi指令就跳转到OS Kernel的入口处,并开始执行它

以上内容由大学时代综合整理自互联网,实际情况请以官方资料为准。

相关