前面的两节里面仅仅简单的实现了从实模式进入到保护模式然后又返回实模式,而且特权等级都是在Ring0层的。保护模式之所以有保护作用,除了前面的分段机制,保证段不会越界之外。特权等级机制,也是起了很关键的作用。
关于特权等级的知识,在《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转 一文中有比较详细的解释。关于RPL、CPL、DPL的区分也做了比较详细的区分。
现在先看结果再来分析,代码如下:
;/*
;nasm boot.asm -o boot.com
;*/
;
; 描述符
; usage: Descriptor Base, Limit, Attr
; Base: dd
; Limit: dd (low 20 bits available)
; Attr: dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
dw %2 & 0FFFFh ; 段界限1
dw %1 & 0FFFFh ; 段基址1
db (%1 >> 16) & 0FFh ; 段基址2
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2
db (%1 >> 24) & 0FFh ; 段基址3
%endmacro ; 共 8 字节
;
; 门
; usage: Gate Selector, Offset, DCount, Attr
; Selector: dw
; Offset: dd
; DCount: db
; Attr: db
%macro Gate 4
dw (%2 & 0FFFFh) ; 偏移1
dw %1 ; 选择子
dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性
dw ((%2 >> 16) & 0FFFFh) ; 偏移2
%endmacro ; 共 8 字节
DA_DRW EQU 92h ; 存在的可读写数据段属性值
; 描述符类型
DA_32 EQU 4000h ; 32 位段
DA_DPL0 EQU 00h ; DPL = 0
DA_DPL1 EQU 20h ; DPL = 1
DA_DPL2 EQU 40h ; DPL = 2
DA_DPL3 EQU 60h ; DPL = 3
DA_C EQU 98h ; 存在的只执行代码段属性值
; 系统段描述符类型
DA_LDT EQU 82h ; 局部描述符表段类型值
SA_TIG EQU 0 ; ┓TI
SA_TIL EQU 4 ; ┛
DA_386TSS EQU 89h ; 可用 386 任务状态段类型值
DA_386CGate EQU 8Ch ; 386 调用门类型值
SA_RPL3 EQU 3 ;
org 0x0100
jmp LABEL_BEGIN
[section .gdt]
;GDT 段基址, 段界限, 段属性
LABEL_GDT_NULL: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_NORMAL: Descriptor 0, 0FFFFh, DA_DRW
LABEL_DSCPTR_CODE16: Descriptor 0, 0FFFFh, DA_C ;16位非一致代码段;为什么段界限为0FFFFh程序才没错
LABEL_DSCPTR_STACK: Descriptor 0, TopOfStack-1, DA_DRW + DA_32;32位堆栈
LABEL_DESCPTR_DATA: Descriptor 0, DataLen-1, DA_DRW +DA_DPL3
LABEL_DESCPTR_TEST: Descriptor 0500000h, 0FFFFh, DA_DRW
LABEL_CODE_DESC: Descriptor 0, Code32Len-1, DA_C + DA_32 ;非一致代码段
LABEL_VEDIO_DESC: Descriptor 0B8000H, 0FFFFH, DA_DRW +DA_DPL3;显存
LABEL_DESCPTR_LDT Descriptor 0, LDTLen-1, DA_LDT
LABEL_DESCPTR_CODE_GATE Descriptor 0, CodeGateLen-1, DA_C + DA_32
LABEL_DESCPTR_TSS Descriptor 0, TSSLen-1, DA_386TSS
;;;;;================= Ring3 ===================
LABEL_DESCPTR_RING3_CODE32: Descriptor 0, Ring3Code32Len-1, DA_C + DA_32 + DA_DPL3
LABEL_DESCPTR_RING3_STACK32: Descriptor 0, TopOfStackRing3-1, DA_DRW + DA_DPL3
; 门 目标选择子, 偏移, DCount, 属性
LABEL_CALL_GATE: Gate SelectorCodeGate, 0, 0, DA_386CGate + DA_DPL3
;GDT end
Gdtlen equ $ - LABEL_GDT_NULL
Gdtptr dw Gdtlen; GDTR段界限
dd 0 ;GDTR段基址
;GDT选择子
SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT_NULL
SelectorCode32 equ LABEL_CODE_DESC - LABEL_GDT_NULL
SelectorCode16 equ LABEL_DSCPTR_CODE16 - LABEL_GDT_NULL
SelectorStack equ LABEL_DSCPTR_STACK - LABEL_GDT_NULL
SelectorData equ LABEL_DESCPTR_DATA - LABEL_GDT_NULL
SelectorTest equ LABEL_DESCPTR_TEST - LABEL_GDT_NULL
SelectorVideo equ LABEL_VEDIO_DESC - LABEL_GDT_NULL
SelectorLDT equ LABEL_DESCPTR_LDT - LABEL_GDT_NULL
SelectorCodeGate equ LABEL_DESCPTR_CODE_GATE - LABEL_GDT_NULL
SelectorGate equ LABEL_CALL_GATE - LABEL_GDT_NULL
SelectorTSS equ LABEL_DESCPTR_TSS - LABEL_GDT_NULL
;;Ring3 Selector
SelectorRing3Stack32 equ LABEL_DESCPTR_RING3_STACK32 - LABEL_GDT_NULL + SA_RPL3
SelectorRing3Code32 equ LABEL_DESCPTR_RING3_CODE32 - LABEL_GDT_NULL + SA_RPL3
;end section .gdt
[section .code16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov es, ax
mov ds, ax
mov ss, ax
mov sp, 0100h
mov [LABEL_GOBACK_REAL+3] , ax ;;为跳转回实模式做准备
mov [SPValueInRealMode], sp
;初始化32位代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_CODE32
mov word [LABEL_CODE_DESC + 2], ax
shr eax, 16
mov byte [LABEL_CODE_DESC + 4], al
mov byte [LABEL_CODE_DESC + 7], ah
;初始化数据段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_DATA
mov word [LABEL_DESCPTR_DATA + 2], ax
shr eax, 16
mov byte [LABEL_DESCPTR_DATA + 4], al
mov byte [LABEL_DESCPTR_DATA + 7], ah
;;初始化堆栈段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_STACK
mov word [LABEL_DSCPTR_STACK + 2], ax
shr eax, 16
mov byte [LABEL_DSCPTR_STACK + 4], al
mov byte [LABEL_DSCPTR_STACK + 7], ah
;;初始化16位代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_CODE16
mov word [LABEL_DSCPTR_CODE16 + 2], ax
shr eax, 16
mov byte [LABEL_DSCPTR_CODE16 + 4], al
mov byte [LABEL_DSCPTR_CODE16 + 7], ah
;;初始化Ring3代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_CODE32_RING3
mov word [LABEL_DESCPTR_RING3_CODE32 + 2], ax
shr eax, 16
mov byte [LABEL_DESCPTR_RING3_CODE32 + 4], al
mov byte [LABEL_DESCPTR_RING3_CODE32 + 7], ah
;;初始化Ring3堆栈段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_STACK32_RING3
mov word [LABEL_DESCPTR_RING3_STACK32 + 2], ax
shr eax, 16
mov byte [LABEL_DESCPTR_RING3_STACK32 + 4], al
mov byte [LABEL_DESCPTR_RING3_STACK32 + 7], ah
;;初始化TSS描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_TSS
mov word [LABEL_DESCPTR_TSS + 2], ax
shr eax, 16
mov byte [LABEL_DESCPTR_TSS + 4], al
mov byte [LABEL_DESCPTR_TSS + 7], ah
;;初始化LDT 在GDT中的描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_LDT
mov word [LABEL_DESCPTR_LDT + 2], ax
shr eax, 16
mov byte [LABEL_DESCPTR_LDT + 4], al
mov byte [LABEL_DESCPTR_LDT + 7], ah
; 初始化 LDT 中的描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_LDT_Code32
mov word [LABEL_LDT_DESCPTR_CODE32 + 2], ax
shr eax, 16
mov byte [LABEL_LDT_DESCPTR_CODE32 + 4], al
; 初始化 门描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_SEG_CODE_GATE
mov word [LABEL_DESCPTR_CODE_GATE + 2], ax
shr eax, 16
mov byte [LABEL_DESCPTR_CODE_GATE + 4], al
;为加载GDTR做准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT_NULL
mov dword[Gdtptr + 2], eax
;加载gdtr
lgdt [Gdtptr]
;关中断
cli
;a20地址线打开
in al, 92h
or al, 00000010b
out 92h, al
;切换保护模式 准备
mov eax, cr0
or eax, 1
mov cr0, eax
;跳转到保护模式
jmp dword SelectorCode32:0
;;============跳转回实模式执行代码 down================
LABEL_REAL_ENTRY:
mov ax, cs
mov ds, ax
mov ss, ax
mov es, ax
mov sp, [SPValueInRealMode]
;;关闭A20地址线
in al, 92h
and al, 11111101b
out 92h, al
;;开中断
sti
;;返回dos
mov ax, 4c00h
int 21h
;;============^up^跳转回实模式执行代码================
; end of section .code16
;;========== 跳回实模式准备代码=====================
[section .code16]
ALIGN 32
[BITS 16]
LABEL_CODE16:
mov ax, SelectorNormal
mov ds, ax
mov ss, ax
mov es, ax
mov gs, ax
mov fs, ax
;;cr0 PE位清零
mov eax, cr0
and al, 11111110B
mov cr0, eax
LABEL_GOBACK_REAL:
jmp 0:LABEL_REAL_ENTRY ;跳转回实模式 段地址会在程序开始处被设置成正确的值
Code16Len equ $ - LABEL_CODE16
;;end of code16
;;=============================================================
[section .tss]
ALIGN 32
[BITS 32]
LABEL_TSS:
DD 0 ; Back
DD TopOfStack ; 0 级堆栈
DD SelectorStack ;
DD 0 ; 1 级堆栈
DD 0 ;
DD 0 ; 2 级堆栈
DD 0 ;
DD 0 ; CR3
DD 0 ; EIP
DD 0 ; EFLAGS
DD 0 ; EAX
DD 0 ; ECX
DD 0 ; EDX
DD 0 ; EBX
DD 0 ; ESP
DD 0 ; EBP
DD 0 ; ESI
DD 0 ; EDI
DD 0 ; ES
DD 0 ; CS
DD 0 ; SS
DD 0 ; DS
DD 0 ; FS
DD 0 ; GS
DD 0 ; LDT
DW 0 ; 调试陷阱标志
DW $ - LABEL_TSS + 2 ; I/O位图基址
DB 0ffh ; I/O位图结束标志
TSSLen equ $ - LABEL_TSS
;;===================== 数据段==========================
[section .data32]
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0
;字符串
Message: db "In Protect Mode Now ^-^", 0
OffsetMessage equ Message - $$
TestStr db "Now I am In Ring3",0
OffsetRing3Message equ TestStr - $$
GateMessage db "by gate into Ring0",0
OffsetGateMessage equ GateMessage - $$
LDTMessage db "I am using LDT",0
OffsetLdtMessage equ LDTMessage -$$
DataLen equ $ - LABEL_DATA
;;end of .data32
;;===========================================================
;;===========堆栈段 =================
[section .stack32_ring0]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0
TopOfStack equ $ - LABEL_STACK
[section .stack32_ring3]
ALIGN 32
[BITS 32]
LABEL_STACK32_RING3:
times 512 db 0
TopOfStackRing3 equ $ - LABEL_STACK32_RING3
;;end of .stack32
;;=====================================================
;;====================== 保护模式 全局代码段 ========================
[section .code32]
[BITS 32]
LABEL_CODE32:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorStack
mov ss, ax
mov ax, SelectorData
mov ds, ax
mov ax, SelectorTest
mov es, ax
mov esp, TopOfStack-1
;;显示一个字符串
mov ah, 0ch
xor esi, esi
xor edi, edi
mov esi, OffsetMessage
mov edi, (80*10 + 10)*2
cld
.1:
lodsb
test al, al
jz .2
mov [gs:edi], ax
add edi, 2
jmp .1
.2: ;字符串显示完毕
; Load TSS
mov ax, SelectorTSS
ltr ax ; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器 TR。
push SelectorRing3Stack32
push TopOfStackRing3-1
push SelectorRing3Code32
push 0
retf ;;ring0 --> ring3
;jmp SelectorCode16:0
;;加载ldt
;mov ax, SelectorLDT
;lldt ax
;;跳转到局部代码段
;jmp LDT_Code32Selector:0
Code32Len equ $ - LABEL_CODE32
;end of .code32
[SECTION .GateCode32]; 调用门目标段
ALIGN 32
[BITS 32]
LABEL_SEG_CODE_GATE:
mov ax, SelectorVideo
mov gs, ax ; 视频段选择子(目的)
;;显示一个字符串
mov ah, 0ch
xor esi, esi
xor edi, edi
mov esi, OffsetGateMessage
add edi, (80*12 + 10)*2
cld
.1:
lodsb
test al, al
jz .2
mov [gs:edi], ax
add edi, 2
jmp .1
.2: ;字符串显示完毕
;;加载ldt
mov ax, SelectorLDT
lldt ax
;;跳转到局部代码段
jmp LDT_Code32Selector:0
;retf
CodeGateLen equ $ - LABEL_SEG_CODE_GATE
; END of [SECTION .sdest]
;;------------------< Ring3 >-----------------
[section .code32_ring3]
ALIGN 32
[BITS 32]
LABEL_CODE32_RING3:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorData
mov ds, ax
;;显示一个字符串
mov ah, 0ch
xor esi, esi
xor edi, edi
mov esi, OffsetRing3Message
add edi, (80*11 + 10)*2
cld
.1:
lodsb
test al, al
jz .2
mov [gs:edi], ax
add edi, 2
jmp .1
.2: ;字符串显示完毕
;;call ring0 by callgate
call SelectorGate:0
jmp $
Ring3Code32Len equ $ - LABEL_CODE32_RING3
;;=================================================================
;;===================== ldt =====================
[section .ldt]
ALIGN 32
LABEL_LDT:
LABEL_LDT_DESCPTR_CODE32: Descriptor 0, LDT_Code32Len, DA_C + DA_32
LDTLen equ $ - LABEL_LDT
;LDT Descriptor
LDT_Code32Selector equ LABEL_LDT_DESCPTR_CODE32 - LABEL_LDT + SA_TIL
;end of section .ldt
;;===================== 局部代码段 =====================
[section .ldtcode32]
ALIGN 32
[BITS 32]
LABEL_LDT_Code32:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorData
mov ds, ax
mov ax, SelectorStack
mov ss, ax
;;显示一个字符串
mov ah, 0ch
xor esi, esi
xor edi, edi
mov esi, OffsetLdtMessage
add edi, (80*13 + 10)*2
cld
.1:
lodsb
test al, al
jz .2
mov [gs:edi], ax
add edi, 2
jmp .1
.2: ;字符串显示完毕
; 准备经由16位代码段跳回实模式
jmp SelectorCode16:0
LDT_Code32Len equ $ - LABEL_LDT_Code32
;end of .ldtcode32
;==========================================================================
上述代码主要实现:
1、从实模式进入保护模式("In Protect Mode Now ^-^")
2、在保护模式中由Ring0特权级到Ring3特权级的转换("Now I am in Ring3")
3、在Ring3特权级通过call调用门,访问Ring0级别的代码段("by gate into Ring0")
4、在Ring0级别的代码段中,通过ldt访问局部代码段("i am using LDT")
5、在局部代码段中返回实模式(返回dos)
没图说JB:
在编程中注意的问题:
1、各个段描述符的特权级别不能搞错,在Ring3级别的代码段中,只能访问Ring3级别的调用门,但可以通过调用门访问Ring0级别的代码段,虽然比较绕,但我觉得这是特权级别转换的一个关键点。
2、在Ring0和Ring3特权级别的转换中,涉及到不同特权级别堆栈的切换,以及TSS的应用,也算是特权级别转换时注意的一个关键点。
3、要想把程序写的更好,最基本的要求是正确的理解编程中用到的各种定义,和编程中的一些硬性要求,才会举一反三。例如:为什么TSS只有Ring0~Ring2的堆栈信息保留项。若每个进程都有个TSS,他们是如何切换的,又是如何保存TSS信息的。
参考 《一个操作系统的实现》 于渊