专注于操作系统17之进入ring3特权级

      现在,我们要进行特权级间的跳转,即低特权级代码,调用高特权级的代码;高特权级调用低特权级的代码。我们前面的程序都是在ring0级(特权级最高),所以,我们先实现从高特权级向低特权级的跳转,从ring0级跳到ring3级。

     从ring0级进入ring3级时,我们的堆栈是发生了变化了的,即ring0级和ring3级各有各的堆栈。所以,首先我们要建立两个堆栈段,一个ring0级的堆栈段,一个ring3级的堆栈段。接着我们要建立一个代码段(ring3级的)即我们要跳到的目标段,在GDT表中添加这个段的描述符。那要怎么从ring0级跳到ring3级呢?要跳到一个代码段,我们要知道这个段的基址,和目标程序在这个段内的偏移。段的基址由描述符(可由选择子来选择)给出,至于段的偏移,我们这里取0,即目标代码放在段的首地址处。我们要将ring3级的堆栈段的信息(堆栈段的选择子和栈顶指针)和ring3级代码段的信息(选择子和偏移),代码中有注释。特权级在描述中。

          好了,现在给出代码(这里有些代码没用到,这将在下一篇文章中用到),这里是把文件编译成.com的文件,然后在dos系统(在虚拟机中)中运行。如果没安dos系统请参看前一篇文章。   这里的代码是参考了《自己动手写操作系统》。

      

;;nasm 2.07
;;nasm toring3.asm -o toring3.com
org 0100H

%macro Descriptor 3             ;定义Descriptor结构体宏
	dw	%2 & 0FFFFh				; 段界限 1				(2 字节)
	dw	%1 & 0FFFFh				; 段基址 1				(2 字节)
	db	(%1 >> 16) & 0FFh			; 段基址 2				(1 字节)
	dw	((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)	; 属性 1 + 段界限 2 + 属性 2		(2 字节)
	db	(%1 >> 24) & 0FFh			; 段基址 3				(1 字节)
%endmacro ; 共 8 字节
                                 ;定义Gate结构体宏
%macro Gate 4
	dw	(%2 & 0FFFFh)				; 偏移 1				(2 字节)
	dw	%1					; 选择子				(2 字节)
	dw	(%3 & 1Fh) | ((%4 << 8) & 0FF00h)	; 属性					(2 字节)
	dw	((%2 >> 16) & 0FFFFh)			; 偏移 2				(2 字节)
%endmacro ; 共 8 字节


DA_C		EQU	98h	; 只读代码段的属性值
DA_DRW		EQU	92h	; 允许读写的数据段的属性值
DA_DRWA		EQU	93h	; 存在的已访问可读写数据段类型值
DA_32		EQU	4000h	;32位段的属性值
DA_LDT		EQU	  82h	; 局部描述符表段类型值
SA_TIL		EQU	4	;  用于指明是LDT的描述符,即选择符中TI=1
DA_386CGate	EQU	  8Ch	; 386 调用门类型值
DA_DPL0		EQU	  00h	; DPL = 0
DA_DPL1		EQU	  20h	; DPL = 1
DA_DPL2		EQU	  40h	; DPL = 2
DA_DPL3		EQU	  60h	; DPL = 3
SA_RPL0		EQU	0	; ┓
SA_RPL1		EQU	1	; ┣ RPL
SA_RPL2		EQU	2	; ┃
SA_RPL3		EQU	3	; ┛


	jmp	LABEL_BEGIN         
	               ;现在进行第一步:建立GDT表
[SECTION .gdt]     ;用于放GDT表的段
;GDT
LABEL_GDT: Descriptor 0,0,0 ;空描述符,当一个任务没有LDT时,会把LDTR清空,这时,LDTR便指向空描述符。

LABEL_CODE32: Descriptor 0,SegCode32Len-1,DA_C + DA_32 ; 代码段的描述符,特权级为0

LABEL_VIDEO: Descriptor 0B8000h,0ffffh,DA_DRW  +DA_DPL3  ;显存的描述符,0b8000h是显存的首地址,特权级为3
;GDT 结束
LABEL_NORMAL: Descriptor 0,0ffffh,DA_DRW      ;normal描述符,用于将段寄存器的属性规范化(即属性为读写,大小为0ffffh)

LABEL_GOALCODE: Descriptor 0,GoalCodeLen -1 ,DA_C + DA_32  ;调用门目标段的描述符,特权级为0

LABEL_LDT:   Descriptor 0,LdtLen - 1,DA_LDT     ;用于描述LDT表的描述符

LABEL_CALL_GATE: Gate SelectorGoalCode,0,0,DA_386CGate + DA_DPL3      ;门描述符
LABEL_CODERING3: Descriptor 0,CodeRing3Len -1 , DA_C + DA_32 + DA_DPL3   ;特权级为3的代码段的描述符
LABEL_STACKRING3: Descriptor 0, StackRing3Len , DA_32 + DA_DRWA  + DA_DPL3  ;特权级为3的代码段对应的堆栈段的描述符
LABEL_GSTACK: Descriptor 0, GStackLen , DA_32 + DA_DRWA      ;全局堆栈段的描述符,特权级为0
GdtLenth equ $ - LABEL_GDT   ;计算GDT表的长度

GdtPtr dw GdtLenth - 1 ; 准备存入GDTR的信息
       dd 0    ;    用于存放GDT表的物理地址,后面会计算


;GDT选择子
SelectorLdt equ LABEL_LDT - LABEL_GDT     ;ldt表的选择子
SelectorCode32 equ LABEL_CODE32 - LABEL_GDT ;代码段的选择子
SelectorVideo  equ LABEL_VIDEO - LABEL_GDT  ;显存所在段的选择子
SelectorNormal equ LABEL_NORMAL - LABEL_GDT  ;normal描述符的选择子

SelectorGoalCode equ LABEL_GOALCODE - LABEL_GDT   ;调用门所调用的目标段的描述符的选择子
SelectorCallGate  equ LABEL_CALL_GATE - LABEL_GDT   ;调用门的描述符的选择子
SelectorCodeRing3  equ LABEL_CODERING3 - LABEL_GDT   + SA_RPL3  ;特权级为3的代码段的描述符的选择子
SelectorStackRing3 equ LABEL_STACKRING3 - LABEL_GDT +SA_RPL3   ;堆栈段的描述符的选择子
SelectorGStack equ	LABEL_GSTACK - LABEL_GDT       ;全局堆栈段的选择子

;[SECTION .gdt]结束


[SECTION .ldt] ;建立LDT表
;LDT
ALIGN 32
LABEL_TABLE_LDT:
LABEL_CODEA: Descriptor 0,CodeALen - 1 ,DA_C + DA_32  ; ldt表中CODEA段的描述符

LdtLen equ $ - LABEL_TABLE_LDT 

SelectorCodeA equ LABEL_CODEA - LABEL_TABLE_LDT + SA_TIL ;这里的SA_TIL表示是LDT中的选择符

;[SECTION .ldt]结束

;全局堆栈段  ring0级的堆栈段
[SECTION .gstack]
ALIGN	32
[BITS	32]
LABEL_SEG_GSTACK:
times 512  db 0
GStackLen equ $ - LABEL_SEG_GSTACK - 1
;[SECTION .gstack]结束


[SECTION .stackring3]  ;ring3级的堆栈段
ALIGN	32
[BITS	32]
LABEL_SEG_STACKRING3:
times 512 db 0

StackRing3Len equ $ - LABEL_SEG_STACKRING3 - 1

;[SECTION .stackring3]结束



[SECTION .codering3]   ;ring3级的代码段,即我们要跳往的目标段,在屏幕中输出‘3’表示已跳到了ring3级的代码
ALIGN	32
[BITS	32]
LABEL_SEG_CODERING3:

mov ax,SelectorVideo ;视频选择子,用于找到显存段的描述符
mov gs,ax 

mov edi,(80 * 10 + 3)*2  ;屏幕的第10行,第3列
mov ah,0Ch
mov al,'3'
mov [gs:edi],ax ;将字符‘3’输出到屏幕
 
jmp $                       ;最后程序将停在这里

CodeRing3Len equ $ - LABEL_SEG_CODERING3

;[SECTION .codering3]结束



[SECTION .s16]
[BITS 16]	;目标处理器模式
LABEL_BEGIN:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h


;初始化全区堆栈段的描述符
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_GSTACK
mov	word [LABEL_GSTACK + 2], ax
shr	eax, 16
mov	byte [LABEL_GSTACK + 4], al
mov	byte [LABEL_GSTACK + 7], ah

;初始化特权级为3的代码段的描述符
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_CODERING3
mov	word [LABEL_CODERING3 + 2], ax
shr	eax, 16
mov	byte [LABEL_CODERING3 + 4], al
mov	byte [LABEL_CODERING3 + 7], ah

;初始化堆栈段的描述符
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_STACKRING3
mov	word [LABEL_STACKRING3 + 2], ax
shr	eax, 16
mov	byte [LABEL_STACKRING3 + 4], al
mov	byte [LABEL_STACKRING3 + 7], ah



;初始化调用门目标段的描述符
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_GOALCODE
mov	word [LABEL_GOALCODE + 2], ax
shr	eax, 16
mov	byte [LABEL_GOALCODE + 4], al
mov	byte [LABEL_GOALCODE + 7], ah



;初始化LDT在GDT中的描述符
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_TABLE_LDT
mov	word [LABEL_LDT + 2], ax
shr	eax, 16
mov	byte [LABEL_LDT + 4], al
mov	byte [LABEL_LDT + 7], ah


;初始化LDT中的描述符
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_CODEA
mov	word [LABEL_CODEA + 2], ax
shr	eax, 16
mov	byte [LABEL_CODEA + 4], al
mov	byte [LABEL_CODEA + 7], ah





;初始化32位段的描述符

xor eax,eax   ; 同或运算,这里是将eax清零

mov ax,cs
shl eax,4     ;左移四位,相当于乘以16,实模式下计算物理地址
add eax,LABEL_SEG_CODE32    ;eax中为32位段的物理地址

mov word [LABEL_CODE32 + 2],ax   ;将32位段的物理地址存入段的描述符中

shr eax,16
mov byte [LABEL_CODE32 + 4],al
mov byte [LABEL_CODE32 + 7],ah

;计算GDT表的物理地址,将GDT表的物理地址信息存入GdtPtr中
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_GDT
mov dword [GdtPtr + 2],eax

;现在进行第二步:将GDT表的信息加载到GDTR中
lgdt [GdtPtr]  ;装载GDTR

;关中断
cli

;现在进行第三步:打开地址线
in al,92h
or al,00000010b
out 92h,al

;现在进行第四步:置PE为1,表示CPU处于保护模式下
mov eax,cr0
or eax,1
mov cr0,eax
;现在进入第五步:跳到保护模式的代码

jmp dword SelectorCode32:0  ;这里将SelectorCode32存入CS中作为选择子
;[SECTION .s16] 结束

[SECTION .s32]
[BITS 32]  

LABEL_SEG_CODE32:
mov ax,SelectorVideo ;视频选择子,用于找到显存段的描述符
mov gs,ax 

mov ax,SelectorGStack
mov ss,ax
mov esp,GStackLen


mov edi,(80 * 10 + 0)*2  ;屏幕的第10行,第0列
mov ah,0Ch
mov al,'p'
mov [gs:edi],ax ;将字符‘p’输出到屏幕
 
push SelectorStackRing3    ;ring3级的堆栈段的选择子
                     
push StackRing3Len         ;ring3级的堆栈段的栈顶指针 
push SelectorCodeRing3    ; ring3级的代码段的选择子
push 0                      ;ring3级的代码段的段内偏移

retf                 ;从这跳到ring3级的代码段,ring3级堆栈段的栈底地址,栈顶指针, 代码段的选择子,偏移已由前面的三
                     ;个push进入了ring0级的堆栈段


 mov ax,SelectorLdt
 lldt ax            ;将描述ldt表的描述符的选择子装入LDTR寄存器

call SelectorCallGate:0   ;通过调用门,调用代码段

jmp SelectorCodeA:0    ;调到局部任务

SegCode32Len equ $ - LABEL_SEG_CODE32  ;计算32位代码段的长度


;[SECTION .s32]结束


;ldt表中描述符所指向的CODEA代码段
[SECTION .la]
[BITS 32]  

LABEL_SEG_CODEA:
mov ax,SelectorVideo ;视频选择子,用于找到显存段的描述符
mov gs,ax 

mov edi,(80 * 11 + 0)*2  ;屏幕的第11行,第0列
mov ah,0Ch
mov al,'l'
mov [gs:edi],ax ;将字符‘l’输出到屏幕

jmp $

CodeALen equ $ - LABEL_SEG_CODEA  ;计算32位代码段的长度
;[SECTION .la]结束

[SECTION .goalcode]
LABEL_SEG_GOALCODE:
mov ax,SelectorVideo ;视频选择子,用于找到显存段的描述符
mov gs,ax 

mov edi,(80 * 12 + 0)*2  ;屏幕的第12行,第0列
mov ah,0Ch
mov al,'c'
mov [gs:edi],ax ;将字符‘l’输出到屏幕
retf
GoalCodeLen equ $ - LABEL_SEG_GOALCODE  
;[SECTION .goalcode]结束




下面是运行的结果图:



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值