1.3.5 head.s开始执行(2)

来源:互联网 发布:pathfinder软件价格 编辑:程序博客网 时间:2024/04/20 17:35

1.3.5 head.s开始执行(2)

小贴士

取段寄存器指令(Load Segment Instruction):该组指令的功能是把内存单元的一个“低字”传送给指令中指定的16位寄存器,把随后的一个“高字”传给相应的段寄存器(DS、ES、FS、GS和SS)。其指令格式如下:

  1. LDS/LES/LFS/LGS/LSS Reg, Mem 

指令LDS(Load Data Segment Register)和LES(Load Extra Segment Register)在8086CPU中就存在,而LFS和LGS(Load Extra Segment Register)、LSS(Load Stack Segment Register)是80386及其以后CPU中才有的指令。 如果Reg是16位寄存器,那么Mem必须是32位指针;如果Reg是32位寄存器,那么Men必须是48位指针,其低32位给指令中指定的寄存器,高16位给指令中的段寄存器。

0x10将SS的值设置为与前面4个段选择符的值相同。这样,SS与前面讲解过的4个段选择符相同,段基址都指向0x000000,段限长都是8MB,特权级都是内核特权级,后面的压栈动作就要在这里进行。

特别值得一提的是,现在刚刚从实模式转变到保护模式,段基址的使用方法和实模式差别非常大,要使用GDT产生段基址,前面讲到的那几行设置段选择符的指令本身都是要用GDT寻址的。现在就能清楚地看出,如果没有setup程序在16位实模式下模拟32位保护模式而创建的GDT,恐怕前面这几行指令都无法执行。

注意,栈顶的增长方向是从高地址向低地址的。参见图1-27的下部,注意栈段基址和ESP在图中的位置。

我们现在回忆一下图1-8中对栈指针寄存器的设置,那时是设置sp,而这时是设置esp,多了一个字母e,这是为适应保护模式而做的调整。这段内容对应的代码如下:

  1. //代码路径:boot/head.s  
  2. lss _stack_start, %esp 
 图1-27 设置栈head程序接下来对中断描述符表进行设置,代码如下所示:
  1. //代码路径:boot/head.s  
  2. call setup_idt  
  3. ……  
  4. setup_idt:  
  5.     lea ignore_int,%edx  
  6.     movl $0x00080000,%eax  
  7.     movw %dx,%ax        /* selector = 0x0008 = cs */  
  8.     movw $0x8E00,%dx    /* interrupt gate-dpl=0, present */  
  9.     lea _idt,%edi  
  10.     mov $256,%ecx  
  11. rp_sidt:  
  12.     movl %eax,(%edi)  
  13.     movl %edx,4(%edi)  
  14.     addl $8,%edi  
  15.     dec %ecx  
  16.     jne rp_sidt  
  17.     lidt idt_descr  
  18.     ret 

小贴士

一个中断描述符的结构如下:

 

中断描述符为64位,包含了其对应中断服务程序的段内偏移地址(OFFSET)、所在段选择符(SELECTOR)、段特权级(DPL)、段存在标志(P)、段描述符类型(TYPE)等信息,供CPU在程序中需要进行中断服务时找到相应的中断服务程序。其中,第0~15位和第48~63位组合成32位的中断服务程序的段内偏移地址;第16~31位为段选择符(SELECTOR),定位中断服务程序所在段;第47位为段存在标志(P),用于标识此段是否存在于内存中,为虚拟存储提供支持;第45~46位为特权级标志(DPL),特权级范围从0~3;第40~43位为段描述符类型标志(TPYE),中断描述符对应的类型标志为1110(0xE),即将此段描述符标记为“386中断门”。

这是重建保护模式下中断服务体系的开始,程序先让所有的中断描述符默认指向ignore_int这个位置(将来main函数里面还要让中断描述符对应具体的中断服务程序),之后还要对中断描述符表寄存器的值进行设置。图1-28显示了具体的操作状态。

 图1-28 设置中断描述符表

点评

构造中断描述符表,并使所有中断服务程序指向同一段只显示一行提示信息就返回的服务程序,先使中断机制的整体架构搭建起来(实际的中断服务程序挂接则在main函数中完成)。从编程技术上讲,这是一个占位的操作方式,也防止了“野指针”。

现在,head程序要废除已有的GDT,并在内核中的新位置重新创建全局描述符表,如图1-29所示。其中第二项和第三项分别为内核代码段描述符和内核数据段描述符,其段限长均被设置为16MB,并设置全局描述符表寄存器的值。

 图1-29 重新创建GDT
0 0