首页 理论教育 操作系统实现之路:保护模式初始化

操作系统实现之路:保护模式初始化

时间:2023-10-21 理论教育 版权反馈
【摘要】:Minker.bin模块是完全运行在保护模式之下的模块。实模式下的初始化完成之后,CPU已经进入保护模式,并跳转到MINIKER.BIN模块的开始处继续执行。上述代码明确地指示编译器编译成32位指令代码,即保护模式下的指令,且偏移地址设定为1MB开始处。代码重新初始化DS和ES寄存器,在REALINIT.BIN中,转换到保护模式之后,仅仅初始化了CS寄存器,此处对DS和ES寄存器进行初始化,为代码的继续执行建立环境。

操作系统实现之路:保护模式初始化

Minker.bin模块是完全运行在保护模式之下的模块。严格来说,这个模块是一个历史遗留模块。当初在设计Hello China的时候,是希望把键盘驱动程序、字符显示驱动程序等内容放到这个模块里实现,同时在这个模块内实现一个最简单的shell,完成用户交互。通过Miniker即可独立操作计算机,无需进一步加载master等模块,这也是miniker(Mini-Kernel)名称的由来。但随着Hello China结构的转变,miniker中的大部分功能都被独立出来单独实现。比如键盘驱动程序,用C语言做了更加完善的实现。这样Miniker的功能就退化为初始化中断描述符表(IDT)和全局描述符表(GDT)的功能。这两个表项的初始化代码不多,后续会移植到realinit中实现(实际上realinit已经初始化了GDT,但是比较简单,miniker又做了更加完整的初始化),这样miniker会慢慢消失。但在V1.75版本里,这个模块还是存在的。

实模式下的初始化完成之后,CPU已经进入保护模式,并跳转到MINIKER.BIN模块的开始处继续执行。下面是MINIKER.BIN模块的开始部分代码。

978-7-111-41444-5-Chapter03-31.jpg

上述代码明确地指示编译器编译成32位指令代码,即保护模式下的指令,且偏移地址设定为1MB开始处。MINIKER.BIN模块也被编译成纯二进制可执行文件格式,没有任何特定的文件头部信息。

代码重新初始化DS和ES寄存器,在REALINIT.BIN中,转换到保护模式之后,仅仅初始化了CS寄存器(JMP指令完成),此处对DS和ES寄存器进行初始化,为代码的继续执行建立环境。需要注意的是,对于DS/ES等寄存器的初始化,使用的是全局描述符表中的第三个表项(偏移0x10处)。然后,又通过一条跳转指令跳转到标号为gl_sysredirect的指令处开始执行。在上述代码和gl_sysredirect标号之间,定义了一些全局的数据结构,包括全局描述表、中断描述表等。

下面是gl_sysredirect标号处的代码。

978-7-111-41444-5-Chapter03-32.jpg

978-7-111-41444-5-Chapter03-33.jpg

上述代码首先把MINIKER.BIN和MASTER.BIN两个模块从最初位置(加载后的位置,位于低端的1MB内存范围内)重新搬移到1MB物理内存开始处(此时CPU工作在32位模式下,可以访问32位地址空间的任何位置)。其中,con_mini_size和con_mast_size是两个预定义的宏,分别指出了MINIKER.BIN和MASTER.BIN两个模块的长度,把这两个模块的长度相加,便是要搬移的大小。然后执行movsd(转移32比特的字符串)指令,并以rep为前缀,一次性把MINIKER.BIN和MASTER.BIN搬移到指定位置。搬移完成后,内存的布局如图3-8所示。

978-7-111-41444-5-Chapter03-34.jpg

图3-8 模块搬移完成后的内存布局

上述搬移完成之后,紧接着又通过一条跳转指令(绝对位置跳转),跳转到标号为gl_initgdt位置开始运行。需要注意的是,这时候MINIKER.BIN已经被搬移到1MB开始处,因此,后续的执行也必须相应地调整到新的MINIKER.BIN所在的位置。若通过通常的跳转指令,直接以标号为参数,则不能正常工作。因为标号为参数,只能是一个相对位置的跳转,即以当前位置为基础,在EIP寄存器上加上当前位置到标号的偏移量,而不是跳转到标号的绝对位置。因此,必须采用绝对跳转,即直接跳转到标号所在位置。这样,采用寄存器作为参数,来执行JMP指令,可以实现绝对跳转。详细信息,可参考Intel CPU的指令手册。

顾名思义,gl_initgdt标号开始的代码,应该是用来完成初始化GDT的,如下所示:(www.xing528.com)

978-7-111-41444-5-Chapter03-35.jpg

978-7-111-41444-5-Chapter03-36.jpg

这段代码重新初始化了GDT寄存器,这时候的初始化,不但对DS、ES等寄存器做了初始化,而且还初始化了SS寄存器和ESP寄存器,这样后续代码就可以执行函数调用(CALL)指令了。需要注意的是,上述对各段寄存器的初始化,虽然采用了不同的段描述符,但所有段描述符的基址和长度都是相同的,因为目前Hello China的实现采用了平展模式,每个段都可以完整覆盖整个线性地址空间。详细信息可参考第5章。

对于堆栈寄存器(ESP)的初始化,是直接把一个预先定义的值DEF_INIT_ESP装入ESP寄存器,这个值目前定义为0x13FFFFFF,即物理内存20MB地址处。

完成GDT的初始化,以及相关寄存器的初始化后,又通过一条远跳转指令,跳转到gl_sysinit处继续执行。这条指令不但完成了执行路径的转移,而且更新了CS寄存器,并更新了CPU的内部上下文,包括指令预取队列、CACHE等。下面是gl_sysinit的实现。

978-7-111-41444-5-Chapter03-37.jpg

上述代码调用一个本地过程np_fill_idt,用来完成中断描述符表的初始化,然后初始化idtr寄存器(lidt指令),并重新初始化8259中断控制寄存器。这样做是因为在实模式下,中断控制器的中断向量是从0开始的,而一旦转移到保护模式下,外部中断却是从32开始,因此,必须对中断控制器进行重新编程,以产生新的中断向量。对于中断描述符表(IDT)的初始化,在第8章中有详细介绍,在此不作赘述。

完成上述功能后,该过程使能中断(sti指令),并采用一个绝对跳转指令跳转到con_mast_start处开始执行。con_mast_start是一个预定义的宏,指明了MASTER.BIN模块所在的内存位置(物理内存位置)。至此,MINIKER.BIN模块执行完毕,控制转移到MASTER.BIN模块。

至此,采用汇编语言部分实现的功能已经执行完毕,这部分也是与特定硬件平台相关的。后续所有功能,都是采用C语言实现的与机器无关部分功能。

嵌入式开发领域,往往把整个软件分成两部分:BSP和应用代码部分,其中BSP是单板支撑包的缩写。一般情况下,在BSP中,实现了特定硬件相关的初始化代码,以及特定硬件的驱动程序,这样是为了提升整个系统的可移植性。因为在把代码移植到不同的硬件平台的时候,从理论上说,只需要修改BSP部分就可以了(实际上远没有这么方便)。在Hello China的实现中,可以把REALINIT.BIN和MINIKER.BIN两个模块看作BSP,因为在这两个模块中,完成了对特定硬件平台(PC)的硬件初始化功能,而且在MINIKER.BIN中,还实现了针对标准PC显示器和PC键盘的驱动程序,这样在MASTER.BIN模块中,就不用考虑这些硬件的差异,而直接通过特定的接口,调用硬件提供的服务。

MASTER.BIN模块是Hello China的核心模块。该模块也完成一些初始化工作,但这些初始化工作不是特定硬件的初始化,而是操作系统正常工作的核心数据结构和核心数据对象的初始化。在完成这些初始化工作后,操作系统启动一个shell线程,用来完成与用户的交互,到达这一步后,操作系统才算成功启动完毕。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈