前面的最简单进程中的,内核和进程都是运行在RING0下的,照理说不是什么进程都能运行在RING0下的,那我们现在就在让进程运行在RING1下吧。众所周知,在保护模式下,特权级的切换伴随着堆栈的切换,所以就必须要有TSS这个数据结构的存在。从特权级高的代码跳转到特权级低的代码不需要从TSS中得到堆栈的地址,因为iretd指令已经把堆栈的地址pop了出去;而从从特权级低的代码跳转到特权级高的代码则需要从TSS中得到要切换的堆栈的地址。下面来看看TSS的结构:
在我们的程序中,时钟中断发生时,控制权就从我们的进程(RING1)转换到时钟中断处理程序中(RING0),而在时钟中断处理程序结束后则不需要TSS。我们在TSS中只需要填充ss0,esp0字段即可。
下面就来在protect.h中定义TSS:
- typedef struct s_tss {
- u32 backlink;
- u32 esp0; /* stack pointer to use during interrupt */
- u32 ss0; /* " segment " " " " */
- u32 esp1;
- u32 ss1;
- u32 esp2;
- u32 ss2;
- u32 cr3;
- u32 eip;
- u32 flags;
- u32 eax;
- u32 ecx;
- u32 edx;
- u32 ebx;
- u32 esp;
- u32 ebp;
- u32 esi;
- u32 edi;
- u32 es;
- u32 cs;
- u32 ss;
- u32 ds;
- u32 fs;
- u32 gs;
- u32 ldt;
- u16 trap;
- u16 iobase; /* I/O位图基址大于或等于TSS段界限,就表示没有I/O许可位图 */
- }TSS;
接着需要建立一个全部的TSS类型的变量,在global.h和global.c分别加入下述语句:
- extern TSS tss; /* global.h */
- TSS tss; /* global.c */
然后需要填充这个TSS的变量,需要在讲各字段先初始化为0,再填充RING0下的堆栈地址,esp0的填充稍微等等,还需要在GDT中添加TSS的描述符。就在start.c中的C_Start函数中填充吧:
- Memory_Set(&tss,sizeof(TSS),0);
- tss.ss0 = 8;
- tss.iobase = sizeof(tss);
- Fill_Desc(5,(u32)&tss,sizeof(tss) - 1,DA_386TSS);
注意此时GDT的个数达到了6个,则需要进行相应修改:
- *(u16*)(&GDT_Ptr[0]) = 6 * 8 - 1; /* in function C_Start */
- p_PCB_A->ldts[0].attr1 &= 0x9f;
- p_PCB_A->ldts[0].attr1 |= DA_DPL1;
- p_PCB_A->ldts[1].attr1 &= 0x9f;
- p_PCB_A->ldts[1].attr1 |= DA_DPL1;
- p_PCB_A->stack_frame.ds = 0 + 4 + 1;
- p_PCB_A->stack_frame.es = 0 + 4 + 1;
- p_PCB_A->stack_frame.gs = 24 + 1 ;
- p_PCB_A->stack_frame.fs = 0 + 4 + 1;
- p_PCB_A->stack_frame.ss = 4 + 4 + 1;
- p_PCB_A->stack_frame.cs = 8 + 4 + 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,编译链接,发现画面跟上一节一样,大功告成。今天就到这。