上一篇文章 setup.s 解读——Linux-0.11 剖析笔记(三) 详细地解释了 setup.s 的代码,整个代码完成的任务有:
- 获取一些参数保存在 0x90000 处
- 保存光标的位置
- 获取从 1M 处开始的扩展内存大小
- 获取显示模式
- 检查显示方式(EGA/VGA)并获取参数
- 复制硬盘参数表(包括检查系统是否有第2个硬盘)
- 关中断
- 移动 system 模块到 0x00000
- 加载 IDT 和 GDT
- 开启 A20
- 设置 8259
- 进入保护模式(使 CR0 的 PE 位 = 1)
- 跳转到 0 地址执行
但是,要进入保护模式,上面的有些步骤不是必须的。
进入保护模式的主要步骤有:
- 准备 GDT
- 加载 GDT
- 打开 A20
- 使 CR0 的 PE 位 = 1
- 跳转,进入保护模式
需要强调的是,在 setup.s 程序执行结束后,系统模块 system 被移动到物理地址 0 开始处,从位置 0x90000 开始处则存放内核将会使用的一些参数,示意图如下图:
此时临时全局表中有三个描述符,第一个是 NULL,不使用;另外两个分别是代码段描述符和数据段描述符,它们都指向系统模块的起始处,即物理地址 0 处。这样当 setup . s 中执行最后一条指令
jmp 0,8
就会跳到 head.s 程序的起始位置,这条指令中的 ‘8’ 是段选择符,即 GDT 中的代码段描述符。‘0’ 是代码段中的偏移值。
这里的全局描述符表,是一个临时表,仅仅是为了进入保护模式。进入保护模式后,会执行 head.s 中的代码,head.s 会设置真正的全局描述符表。
参考资料
1《Linux内核完全剖析》(赵炯,机械工业出版社,2006)
2《Orange’s:一个操作系统的实现》(于渊)