进程的初步实现(下)

问题

如何通过进程上下文数据恢复进程执行?

如何使得进程运行于3特权级?

恢复上下文数据

通过任务数据结构中的寄存器值恢复上下文

借助esp寄存器以及pop指令恢复通用寄存器

 

注意

启动一个新任务可以看作特殊的任务切换,切换的目标任务上下文信息中通用寄存器的值为0

 

特权级转移 (高 => 低)

将esp指向目标内存位置 (eip,cs,eflags,esp,ss)

借助iret指令降特权级执行

 

再论中断与中断返回

中断发生时,可从低特权级转移到高特权级执行 (3 => 0)

中断返回时,从高特权级转移到低特权级执行 (0 => 3)

中断服务程序返回时的栈变化

任务代码执行方案

eip 指向任务代码入口

cs 指向 LDT 中的代码段描述符 (DPL = 3)

eflags 指定关键状态 (IOPL,IF,等)

esp 指向任务使用的私有栈

ss 指向 LDT 中的数据段描述符

iret 启动任务 (从任务代码入口处执行)

 

进程启动函数

 进程的初步实现

loader.asm


%include "blfunc.asm"
%include "common.asm"

org BaseOfLoader

interface:
    BaseOfStack     equ    BaseOfLoader	
	BaseOfTarget    equ    BaseOfKernel
	Target  db "KERNEL     "
    TarLen          equ   ($-Target)

[section .gdt]
; GDT definition
;                                     段基址,       段界限,       段属性
GDT_ENTRY		    : Descriptor		0, 			  0,		   0
CODE32_DESC		    : Descriptor		0, 	   Code32SegLen - 1,  DA_32 + DA_C + DA_DPL0
VIDEO_DESC		    : Descriptor	 0xB8000, 	  0x07FFF,        DA_32 + DA_DRWA + DA_DPL0
CODE32_FLAT_DESC	: Descriptor		0, 	      0xFFFFF,        DA_32 + DA_C + DA_DPL0
DATA32_FLAT_DESC	: Descriptor		0, 	      0xFFFFF,        DA_32 + DA_DRW + DA_DPL0
TASK_LDT_DESC       : Descriptor		0, 			  0,		   0
TASK_TSS_DESC       : Descriptor		0, 			  0,		   0
; GDT end  

GdtLen	  equ	   $ - GDT_ENTRY
GdtPtr:    
		dw	GdtLen - 1
		dd	0

; GDT Selector
Code32Selector	    equ    (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector	    equ    (0x0002 << 3) + SA_TIG + SA_RPL0
Code32FlatSelector	equ    (0x0003 << 3) + SA_TIG + SA_RPL0
Data32FlatSelector	equ    (0x0004 << 3) + SA_TIG + SA_RPL0
; end of [section .gdt]


[section .s16]
[bits 16]
BLMain:
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, SPInitValue

	; initialize GDT for 32 bits code segment
	mov esi, CODE32_SEGMENT
	mov edi, CODE32_DESC
	call InitDescItem

	; initalize GDT pointer struct
	mov eax, 0
	mov ax, ds
	shl eax, 4
	add eax, GDT_ENTRY
	mov dword [GdtPtr + 2], eax
	
	call LoadTarget
	cmp dx, 0
	jz Output

	call StoreGlobal

	; 1. load GDT
    lgdt [GdtPtr]
    
    ; 2. close interrupt
    ;    set IOPL to 3
    cli 
    
    pushf
    pop eax
    or eax, 0x3000
    push eax
    popf
    
    ; 3. open A20
    in al, 0x92
    or al, 00000010b
    out 0x92, al
    
    ; 4. enter protect mode
    mov eax, cr0
    or eax, 0x01
    mov cr0, eax
    
    ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0


Output:
	mov bp, ErrStr
	mov cx, ErrLen
	call Print
	
	jmp $

; esi    --> code segment labelBACK_ENTRY_SEGMENT
; edi    --> descriptor label
InitDescItem:
	push eax

	mov eax, 0
	mov ax, cs
	shl eax, 4
	add eax, esi
	mov word [edi + 2], ax
	shr eax, 16
	mov byte [edi + 4], al
	mov byte [edi + 7], ah
	
	pop eax

	ret

;
;
StoreGlobal:
	push eax	
	
	mov dword [RunTaskEntry], RunTask
	
	mov eax, dword [GdtPtr + 2]
	mov dword [GdtEntry], eax
	
	mov dword [GdtSize], GdtLen / 8
	
	pop eax
	
	ret


[section .gfunc]
[bits 32]
;
; parameter  ===> Task* pt
RunTask:
	push ebp
	mov ebp, esp
	
	mov esp, [ebp + 8]
	
	lldt word [esp + 200]
	ltr  word [esp + 202] 
	
	pop gs
	pop fs
	pop es
	pop ds
	
	popad
	
	add esp, 4
	
	iret
	

[section .s32]
[bits 32]
CODE32_SEGMENT:
	mov ax, VideoSelector
	mov gs, ax
	
	mov ax, Data32FlatSelector
	mov ds, ax
	mov es, ax
	mov fs, ax
	
	mov ax, Data32FlatSelector
	mov ss, ax
	mov esp, BaseOfLoader
	
	jmp dword Code32FlatSelector : BaseOfKernel

Code32SegLen	equ    $ - CODE32_SEGMENT


ErrStr db  "No Kernel" 
ErrLen equ ($-ErrStr)

Buffer:
    db  0x00

RunTask函数让esp指向pt的RegValue的gs地址处,设置好任务相对应的寄存器,这里使用pop指令来设置相应的寄存器是为了不改变eflags寄存器的值;加载LDT和TSS;然后又iret指令从内核跳转到任务代码段执行。

StoreGlobal函数里将RunTask函数入口地址加载到了共享地址RunTaskEntry处。

新增了LDT和TSS段,暂时先把它们的段基址和段界限,段属性设置为0,后续再初始化。

global.c

#include "global.h"

GdtInfo gGdtInfo = {0};
void (* const RunTask) (Task* pt) = NULL;

global.h

#ifndef GLOBAL_H
#define GLOBAL_H

#include "kernel.h"
#include "const.h"

extern GdtInfo gGdtInfo;
extern void (* const RunTask) (Task* pt);

#endif

新增了一个函数指针,我们要将这个指针指向RunTask函数的入口地址,来开启一个任务。

kentry.c


%include "common.asm"

global _start

extern KMain
extern ClearScreen
extern gGdtInfo
extern RunTask

[section .text]
[bits 32]
_start:
    mov ebp, 0
    
    call InitGlobal
    call ClearScreen
    
    call KMain
    
    
    jmp $
    
;
;
InitGlobal:
	push ebp
	mov ebp, esp
	
	mov eax, dword [GdtEntry]
	mov dword [gGdtInfo], eax
	
	mov eax, dword [GdtSize]
	mov dword [gGdtInfo + 4], eax
	
	mov eax, dword [RunTaskEntry]
	mov dword [RunTask], eax
	
	leave
	
	ret

在InitGlobal函数里,将global.h里定义的函数指针指向RunTask函数入口地址。

const.h



#ifndef CONST_H

#define CONST_H



#define NULL  ((void*)0)



#define	DA_DPL0			0x00

#define	DA_DPL1			0x20

#define	DA_DPL2			0x40

#define	DA_DPL3			0x60



#define	SA_RPL_MASK	0xFFFC



#define	SA_RPL0		0

#define	SA_RPL1		1

#define	SA_RPL2		2

#define	SA_RPL3		3



#define	SA_TI_MASK	0xFFFB



#define	SA_TIG		0

#define	SA_TIL		4





#define	DA_32			0x4000

#define	DA_LIMIT_4K		0x8000



#define	DA_DR			0x90

#define	DA_DRW			0x92

#define	DA_DRWA			0x93

#define	DA_C			0x98

#define	DA_CR			0x9A

#define	DA_CCO			0x9C

#define	DA_CCOR			0x9E



#define	DA_LDT			0x82

#define	DA_TaskGate		0x85

#define	DA_386TSS		0x89

#define	DA_386CGate		0x8C

#define	DA_386IGate		0x8E

#define	DA_386TGate		0x8F



#define	GDT_DUMMY_INDEX         0	

#define	GDT_CODE32_INDEX        1

#define	GDT_VIDEO_INDEX         2

#define	GDT_CODE32_FLAT_INDEX   3

#define	GDT_DATA32_FLAT_INDEX   4

#define GDT_TASK_LDT_INDEX      5

#define GDT_TASK_TSS_INDEX      6



#define	GDT_DUMMY_SELECTOR         ((GDT_DUMMY_INDEX << 3) + SA_TIG + SA_RPL0)

#define	GDT_CODE32_SELECTOR        ((GDT_CODE32_INDEX << 3) + SA_TIG + SA_RPL0)	

#define	GDT_VIDEO_SELECTOR         ((GDT_VIDEO_INDEX << 3) + SA_TIG + SA_RPL0)	

#define	GDT_CODE32_FLAT_SELECTOR   ((GDT_CODE32_FLAT_INDEX << 3) + SA_TIG + SA_RPL0)

#define	GDT_DATA32_FLAT_SELECTOR   ((GDT_DATA32_FLAT_INDEX << 3) + SA_TIG + SA_RPL0)

#define	GDT_TASK_LDT_SELECTOR      ((GDT_TASK_LDT_INDEX << 3) + SA_TIG + SA_RPL0)	

#define	GDT_TASK_TSS_SELECTOR      ((GDT_TASK_TSS_INDEX << 3) + SA_TIG + SA_RPL0)



#define	LDT_VIDEO_INDEX         0

#define	LDT_CODE32_INDEX        1	

#define	LDT_DATA32_INDEX        2



#define	LDT_VIDEO_SELECTOR     ((LDT_VIDEO_INDEX << 3) + SA_TIL + SA_RPL3)	

#define	LDT_CODE32_SELECTOR    ((LDT_CODE32_INDEX << 3) + SA_TIL + SA_RPL3)	

#define	LDT_DATA32_SELECTOR    ((LDT_DATA32_INDEX << 3) + SA_TIL + SA_RPL3)	



#endif

const.h定义了我们需要的全局段描述符和局部段描述符的选择子和选择子下标。

kmain.c

#include "kernel.h"
#include "screen.h"
#include "global.h"

Task t = {0};

Delay(int n)
{
	while(n > 0)
	{
		int i = 0, j = 0;
		
		for(i = 0; i < 1000; i++)
		{
			for(j = 0; j < 1000; j++)
			{
				asm volatile("nop\n");
			}
		}
		
		n--;
	}
}

void TaskA()
{
	int i = 0;
	
	SetPrintPos(0, 12);
	PrintString("TaskA: ");
	
	while(1)
	{
		SetPrintPos(8, 12);
		PrintChar('A' + i);
		i = (i + 1) % 26;
		Delay(1);
	}
}

void KMain()
{
	PrintString("D.T.OS\n");
	
	uint base = 0;
	uint limit = 0;
	uint attr = 0;
	int i = 0;
	
	PrintString("Gdt Entry: ");
	PrintIntHex((uint)gGdtInfo.entry);
	PrintChar('\n');
	
	for(i = 0; i < gGdtInfo.size; i++)
	{
		GetDescValue(gGdtInfo.entry + i, &base, &limit, &attr);
		PrintIntHex(base);
		PrintString("    ");
		
		PrintIntHex(limit);
		PrintString("    ");
		
		PrintIntHex(attr);
		PrintChar('\n');
	}

	PrintString("RunTask Entry: ");
	PrintIntHex((uint)RunTask);
	PrintChar('\n');
	
	t.rv.gs = LDT_VIDEO_SELECTOR;
	t.rv.cs = LDT_CODE32_SELECTOR;
	t.rv.ds = LDT_DATA32_SELECTOR;
	t.rv.es = LDT_DATA32_SELECTOR;
	t.rv.fs = LDT_DATA32_SELECTOR;
	t.rv.ss = LDT_DATA32_SELECTOR;
	
	t.rv.esp = (uint)(t.stack + sizeof(t.stack));
	t.rv.eip = (uint)TaskA;
	t.rv.eflags = 0x3002;

	t.tss.ss0 = GDT_DATA32_FLAT_SELECTOR;
	t.tss.esp0 = 0;
	t.tss.iomb = sizeof(t.tss);
	
	SetDescValue(t.ldt + LDT_VIDEO_INDEX,  0xB8000, 0X07FFF, DA_DRWA + DA_32 + DA_DPL3);
	SetDescValue(t.ldt + LDT_CODE32_INDEX, 0x00,    0XFFFFF, DA_C + DA_32 + DA_DPL3);
	SetDescValue(t.ldt + LDT_DATA32_INDEX, 0x00,    0XFFFFF, DA_DRW + DA_32 + DA_DPL3);

	t.ldtSelector = GDT_TASK_LDT_SELECTOR;
	t.tssSelector = GDT_TASK_TSS_SELECTOR;
	
	SetDescValue(&gGdtInfo.entry[GDT_TASK_LDT_INDEX], (uint)&t.ldt, sizeof(t.ldt) - 1, DA_LDT + DA_DPL0);
	SetDescValue(&gGdtInfo.entry[GDT_TASK_TSS_INDEX], (uint)&t.tss, sizeof(t.tss) - 1, DA_386TSS + DA_DPL0);
	
	PrintString("Stack Bottom: ");
    PrintIntHex((uint)t.stack);
    PrintString("    Stack Top: ");
    PrintIntHex((uint)t.stack + sizeof(t.stack));
	
	RunTask(&t);
}

我们首先需要定义一个全局的Task对象t,用t来表示一个任务。注意:这个对象必须要初始化为0,否则这个结构体的其他缺省值就不为0,任务就无法正常执行。

TaskA函数用来作为一个任务函数使用,作用是在屏幕上循环打印字母A - Z。

71行 - 76行我们把局部段描述符选择子加载到对应的t的寄存器结构体中,此时对应的局部段描述符的段基址,段界限和段属性还没设置;

78行 - 80行,我们设置esp为任务的私有栈顶,eip为TaskA (我们要运行的任务入口地址),eflags设置为0x3002 (IF 为0,关闭了处理器的中断响应;IOPL 为3),

82 行- 84行,我们把ss0设置为特权级为0栈段选择子,esp设置为0是因为我们这个程序并没有从低特权级跳转到高特权级去执行,所以暂时将它设置为0。

86行 - 88行,我们初始化了这3个局部段描述符。

93行 -94行,我们设置全局段描述符LDT和TSS。

96行 - 99行,我们打印t任务的栈底和栈顶。

101行,我们开始了任务。

程序运行成功!循环在屏幕上打印字母A - Z,打印t任务的栈的范围是0xBA20 - 0xBC20。

 可以看出cs后2位为11,说明当前CPL为3,程序当前运行在3特权级。

我们查看到esp寄存器的值为0xBBDC,在我们定义的私有栈的内存范围内。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值