【第6章】改变最简单进程的特权级

      前面的最简单进程中的,内核和进程都是运行在RING0下的,照理说不是什么进程都能运行在RING0下的,那我们现在就在让进程运行在RING1下吧。众所周知,在保护模式下,特权级的切换伴随着堆栈的切换,所以就必须要有TSS这个数据结构的存在。从特权级高的代码跳转到特权级低的代码不需要从TSS中得到堆栈的地址,因为iretd指令已经把堆栈的地址pop了出去;而从从特权级低的代码跳转到特权级高的代码则需要从TSS中得到要切换的堆栈的地址。下面来看看TSS的结构:

      在我们的程序中,时钟中断发生时,控制权就从我们的进程(RING1)转换到时钟中断处理程序中(RING0),而在时钟中断处理程序结束后则不需要TSS。我们在TSS中只需要填充ss0,esp0字段即可。

      下面就来在protect.h中定义TSS:

Code:
  1. typedef struct s_tss {  
  2.     u32 backlink;  
  3.     u32 esp0;   /* stack pointer to use during interrupt */  
  4.     u32 ss0;    /*   "   segment  "  "    "        "     */  
  5.     u32 esp1;  
  6.     u32 ss1;  
  7.     u32 esp2;  
  8.     u32 ss2;  
  9.     u32 cr3;  
  10.     u32 eip;  
  11.     u32 flags;  
  12.     u32 eax;  
  13.     u32 ecx;  
  14.     u32 edx;  
  15.     u32 ebx;  
  16.     u32 esp;  
  17.     u32 ebp;  
  18.     u32 esi;  
  19.     u32 edi;  
  20.     u32 es;  
  21.     u32 cs;  
  22.     u32 ss;  
  23.     u32 ds;  
  24.     u32 fs;  
  25.     u32 gs;  
  26.     u32 ldt;  
  27.     u16 trap;  
  28.     u16 iobase; /* I/O位图基址大于或等于TSS段界限,就表示没有I/O许可位图 */  
  29. }TSS;  

   接着需要建立一个全部的TSS类型的变量,在global.h和global.c分别加入下述语句:

Code:
  1. extern TSS tss;  /*  global.h */  
Code:
  1. TSS tss; /*  global.c */  

   然后需要填充这个TSS的变量,需要在讲各字段先初始化为0,再填充RING0下的堆栈地址,esp0的填充稍微等等,还需要在GDT中添加TSS的描述符。就在start.c中的C_Start函数中填充吧

Code:
  1. Memory_Set(&tss,sizeof(TSS),0);  
  2. tss.ss0 = 8;  
  3. tss.iobase = sizeof(tss);  
  4. Fill_Desc(5,(u32)&tss,sizeof(tss) - 1,DA_386TSS);  

   注意此时GDT的个数达到了6个,则需要进行相应修改:

Code:
  1. *(u16*)(&GDT_Ptr[0]) = 6 * 8 - 1;   /*  in function C_Start */  
     既然我们的进程在RING0下运行,那就得把LDT中的描述符的DPL改为1,还要把PCB中的各段寄存器的RPL改为1,gs指向GDT中的描述符,为了不发生异常,还得把GDT中的VIDEO描述符的DPL改为1。下面是修改的部分:
     在Init_PCB中
Code:
  1. p_PCB_A->ldts[0].attr1 &= 0x9f;  
  2. p_PCB_A->ldts[0].attr1 |= DA_DPL1;  
  3. p_PCB_A->ldts[1].attr1 &= 0x9f;  
  4. p_PCB_A->ldts[1].attr1 |= DA_DPL1;  
  5.       
  6. p_PCB_A->stack_frame.ds = 0 + 4 + 1;  
  7. p_PCB_A->stack_frame.es = 0 + 4 + 1;   
  8. p_PCB_A->stack_frame.gs = 24 + 1 ;   
  9. p_PCB_A->stack_frame.fs = 0 + 4 + 1;   
  10. p_PCB_A->stack_frame.ss = 4 + 4 + 1;   
  11. p_PCB_A->stack_frame.cs = 8 + 4 + 1;   
    在Init_GDT中:
Code:
  1. Fill_Desc(3,0xb8000,0xffff,DA_DRW + DA_DPL1 );     

  

   接下来是加载TR的时候了,在kernel.asm中添加:

 mov    ax,Selector_Kernel_Flat_RW
 mov    ds,ax
 mov    es,ax
 mov    ss,ax
 mov    esp,Top_Of_Stack    
 mov    ax,Selector_Kernel_Video
 mov    gs,ax
 
 mov    ax,40
 ltr    ax

 
 mov    dword [Disp_Pos],0
 
 call  Init_PCB
    
 jmp Selector_Kernel_Flat_C:_test

  

   最后是填充TSS变量中的esp0中的值了,我们希望把esp0指向PCB表中的进程的stack_frame的最高地址处,这样就可以在时钟中断处理程序中方便的把当时的进程中的寄存器值保存在PCB表的相应位置处。

extern tss
;...
_test:
 ;jmp    100:0
 ;sti
 
 mov    esp,[p_Resume_PCB]
 mov    ax,[esp + 68]
 lldt   ax
 lea  eax,[esp + 68]
 mov    dword [tss + 4],eax

 
 pop    gs
 pop    fs
 pop    es
 pop    ds
 
 popad
 iretd

     OK,编译链接,发现画面跟上一节一样,大功告成。今天就到这。

你可能感兴趣的:(【第6章】改变最简单进程的特权级)