linux0.11 内核启动代码分析(一)
阅读原文时间:2021年04月20日阅读:1

80x86编程
1、 内存管理寄存器 GDTR 、LDTR、IDER、TR  (这四个寄存器的原理图详见 《linux内核完全注释》84页)                  
    GDTR 和 IDTR 和 LDTR 存放 描述符表的段   TR用于寻址一个特殊的任务状态TSS (该段包含着当前执行任务的重要信息)

2、 全局描述符表寄存器 GDTR
    GDTR寄存器用于存放全局描述符表GDT的32位线性基地址和16位表长度值。(表长度表示GDT表的字节长度值)。
指令LGDT和SGDT分别用来加载和保存GDTR寄存器的内容。

! 在及其加电或者处理器复位后, 基地址被默认设置为0,而长度值被设置成0XFFFF 。在保护模式初始化过程中必须给
GDTR加载一个新值

3、 中断描述符表 IDTR
与GDTR的作用类似,IDTR用来存放中断描述符IDT的32位线性基地址和16位的表长度值。指令 LIDT和SIDT 分别用来加载
保护IDTR寄存器的内容 。 加电或者复位后,和GDTR的值一样。

4、 局部描述符表寄存器 LDTR
LDTR 寄存器中用于存放局部描述符LDT的32位线性基地址、16位段限长 和描述符属性值。指令LLDT 和 SLDT分别用于
加载和保存LDTR 寄存器的段描述符部分。
<
包含LDT标的段必须在GDT表中有一个段描述符
>

<
使用LLDT指令将含有LDT表段的选择符加载进LDTR时,LDT段描述符基地址、段限长、和描述符属性会被自动加载到LDTR中
进行任务切换时,处理器会把新任务LDT的段选择符 和段描述符 自动加载进 LDTR中
>

5、 任务寄存器 TR
TR寄存器用于存放当前任务 TSS段的16位段选择符、32位基地址、16位段长度和描述符属性值。
它引用 GDT表中的一个TTS类型的描述符。 LTR 和STR 用于加载和保存TR寄存器的段选择符。

6、 控制寄存器
控制寄存器(CR0、CR1、CR2、CR3), 一般编程中,我们只会使用CR0这个寄存器。(图详见linux 内核完全注释 86页)
下面只是我在编程中使用到的一些寄存器加以解释说明:
PE 位: CR0的PE位用来 是否启用保护模式
PG 位: 是否启用分页机制。 (分页机制是在 保护模式下的才能使用)
<
《linux 内核完全注释》 86页中有一段说明我觉得有问题, PE位只是决定是否进入保护模式。并不影响段机制的使用
段机制即使是在 实模式下也是存在的, 否则以 16的地址线如何寻位到1M, 肯定使用了段基址
,由此也引入了 保护模式和  实模式的 异同比较, 篇幅较多,在此不加以详解, 百度百科 "实模式"
会有详细的解释。
>

7、 当改变PE 和PG 位时,我们必须小心, 只有当执行程序 至少有部分代码和数据在线性地址空间和物理地址空间
    具有相同的地址时,我们才能改变PG位的设置。(此时无论比是否分页,最终的地址都是一样的,目前还不知道实际意义是什么)

8、 修改 PE位后,程序必须立即使用跳转指令,以刷新处理器指令管道中已经得到的不同指令。
保护模式下寻址采用32位的段和偏移量。必须赋予系统段寄存器新的值即初始化控制寄存器和系统段。在 后面的例子代码中会 体现出来。

                                                 

                                        linux 0.11内核代码研究
           Head.s 代码研究:
1、 对于 movl $0x10, %eax;mov %ax,%ds ;指令的解释
此处的0x10 转化为二进制是 10000B, 注意在保护模式下。DS段寄存器 保存的是 段选择符
低3位有特殊的含义。故DS寄存器的是GDT 的第二个段描述符数据结构。

2、 在head.s代码中,没有使用在 boot.s中出现的jmpi 跳转指令。
原因: 在 boot.s代码中, 设置了cpu为保护模式后, 执行了一条 jmpi 0 8;指令,该指令跳到了
GDT 中的第二个 段描述符。8是段选择符, 一个段描述符暂用8个字节,第一个不能用,那么就取到了
第二个。 就跳转到了 绝对的物理地址0x0处,而偏移量也是0 。 

3、 从12到14行代码,为什么只是初始化 数据段寄存器DS、 堆栈段寄存器SS 和堆栈指针 ESP
原因:  jmpi 指令在保护模式下, 第二个操作数是段选择符, 执行该命令后,会默认将 段选择符 对应的段的基址
   写到CS 代码寄存器中。所以不需要再初始化 CS, 初始化的原因是  切换到保护模式下之后,这些寄存器的
值,必须在新的 保护模式下使用,定位到相应的 段。 
   那么也就是说,此时 cs 的值为 0x08

4、 lss  init_stack,%esp 

init_stack:
.long init_stack
.word 0x10  #堆栈段同内核数据段      这句话该什么理解?
这几条指令的作用是初始化堆栈寄存器 ss 为 0x10,因为都是段选择符,所以此处的ss和上面的ds一样,都是指向数据段寄存器
esp指向标号init_stack地址。ss存放堆栈的段地址,ESP存放栈的偏移地址。 也就是说这个堆栈是在 数据段内,由于
偏移地址的关系,隔开了
                                                              
 
                                     2015.12.1 shawn 
---------- --------------------------------------------------------------------------------------------------------------------