Orange‘s 一个操作系统的实现 chap3.1 & 3.2 记录

chap3

3.1 认识保护模式

  • pmtest1.asm——实现由实模式到保护模式的转换
; ==========================================
; pmtest1.asm
; 编译方法:nasm pmtest1.asm -o pmtest1.bin
; ==========================================

%include	"pm.inc"	; 常量, 宏, 以及一些说明

org	07c00h
	jmp	LABEL_BEGIN

[SECTION .gdt]
; GDT
;                              段基址,       段界限     , 属性
LABEL_GDT:	   Descriptor       0,                0, 0           ; 空描述符
LABEL_DESC_CODE32: Descriptor       0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段
LABEL_DESC_VIDEO:  Descriptor 0B8000h,           0ffffh, DA_DRW	     ; 显存首地址
; GDT 结束

GdtLen		equ	$ - LABEL_GDT	; GDT长度
GdtPtr		dw	GdtLen - 1	; GDT界限
		dd	0		; GDT基地址

; GDT 选择子
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT
; END of [SECTION .gdt]

[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, 0100h

	; 初始化 32 位代码段描述符
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4
	add	eax, LABEL_SEG_CODE32
	mov	word [LABEL_DESC_CODE32 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE32 + 4], al
	mov	byte [LABEL_DESC_CODE32 + 7], ah

	; 为加载 GDTR 作准备
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_GDT		; eax <- gdt 基地址
	mov	dword [GdtPtr + 2], eax	; [GdtPtr + 2] <- gdt 基地址

	; 加载 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	; 执行这一句会把 SelectorCode32 装入 cs,
					; 并跳转到 Code32Selector:0  处
; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS	32]

LABEL_SEG_CODE32:
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子(目的)

	mov	edi, (80 * 11 + 79) * 2	; 屏幕第 11 行, 第 79 列。
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, 'P'
	mov	[gs:edi], ax

	; 到此停止
	jmp	$

SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]
  • 编译:
nasm pmtest1.asm -o pmtest1.bin
  • 写入软盘映像:
dd if=pmtest1.asm of=a.omg bs=512 count=1 conv=notrunc

运行bochs

3.1.1 保护模式的运行环境

  前面的操作是将pmtest1.bin直接写入引导扇区,从而实现从实模式到保护模式的切换。这样操作方便,但由于引导扇区空间有限(仅512bytes),如果pmtest1.bin尺寸变得更大,甚至至超过引导扇区的大小,那前述操作就行不通了。
  书中给出了两个解决方案:1.写一个引导扇区以读取我们的程序并执行。2.借用DOS来执行我们的程序。书中最终采用方案2。

  1. 在bochs官网下载一个FreeDos。解压后将其中的a.img复制到我们的工作目录中,并改名为freedos.img

  2. 用bximage生成一个软盘映像,命名为pm.img

  3. 修改bochsrc

floppya: 1_44=../mine/freedos.img, status=inserted
floppyb: 1_44=../mine/pm.img, status=inserted
boot:a
  1. 启动bochs,待FreeDos启动完毕后格式化B:盘
format b:

format
5. 将pmtest1.asm的第8行改为org 0100h,并重新编译。

nasm pmtest1.asm -o pmtest1.com
  1. 将pmtest1.com复制到虚拟软盘pm.img上。
sudo mkdir /mnt/floppy
sudo mount -o loop pm.img /mnt/floppy
sudo cp pmtest1.com  /mnt/floppy
sudo umount /mnt/floppy
  1. 启动bochs,执行下面的命令。(右侧红色"P"出现说明执行pmtest1成功。)
b:\pmtest1.com

运行结果

3.1.2 保护模式&实模式切换

切换基本流程

  1. 准备GDT(准备GDT基地址)
  2. 用lgdt加载GDTR。
  3. 打开A20。
  4. 置CR0的PE位。
  5. 跳转,进入保护模式。

调试代码pmtest1.asm

  • 将执行至mov eax, cr0,CR0的PE位为0
    debug1

  • 将执行至jmp dword SelectorCode32:0,CR0的PE位置1,进入保护模式
    debug2

3.1.3 段描述符分析

descriptor
  在pmtest1.asm中,段描述符的定义使用了来自于pm.inc的宏。在代码中定义段描述符时,只需确定段基址、段界限和段属性的值。pm.inc中的Descriptor宏,会自动将各值重新组织,以符合段描述符的结构。

pm.inc_descroptor

  %1、%2和%3分别对应着于pmtest1.asm中定义段描述符时需要确定的3个值(段基址、段界限和段属性)。通过逻辑运算(&、|)和位移操作(>>、<<),将原始数据按段描述符的格式重新组织。

3.1.4 LDT的添加

书上给出了一个添加LDT的步骤,还算清晰。可以按部就班地完成LDT的添加。

  1. 增加一个32位的代码段(内容不妨简单)。
  2. 增加一个段,内容是一个描述符表(LDT),可以只有一个代码段描述符,也可以添加更多的段描述符以描述更多的段。注意,涉及的选择子的TI位是1。
  3. 在GDT中增加一个描述符,用以描述这个LDT,同时要定义其选择子。
  4. 增加为新添的描述符进行初始化的代码,主要针对段基址。
  5. 用新加的LDT描述的局部任务准备完毕。
  6. 使用lldt指令加载ldtr,用jmp指令跳转等方式进行跳转。

3.2 特权级

本节主要摘自下两篇文章,建议优先阅读:
ref1
ref2

3.2.1 CPL DPL RPL

  • CPL:描述当前执行程序或者任务的特权级。存储在CS和SS的bit 0,bit 1。当程序在不同特权级代码间转移,CPL会发生改变。
  • DPL:描述段或者门的特权级,存储在描述符的DPL字段中,是这个段的特权级别,用来表明访问这个段时候,所需要的特权。
  • RPL:描述选择子的特权级,段选择子(Selector)的bit0和bit1,是可以重载的。

3.2.2 直接跳转

  “有关代码的特权检查都发生在能够改变段寄存器CS和指令指针寄存器 EIP 的指令中(即这些指令要么改变 EIP,要么改变CS和 EIP,例如 call、jmp、int、ret、sysexit 等能改变程序执行流的指令。)jmp或call后跟着48位全指针(16位段选择子+32位地址偏移),且其中的段选择子指向代码段描述符,权限访问规则及CPL变化如下:”

jmp directly

3.2.3 代码段访问数据段

  在段选择子被载入段寄存器前,会进行特权检查:CPL <= 目标数据段DPL && RPL <= 目标数据段 DPL。如果符合该条件,则允许访问。

3.2.4 通过调用门跳转

  “当段间转移指令jmp和段间转移指令call后跟着的目标段选择子指向一个调用门描述符时,该跳转就是利用调用门的跳转。这时如果选择子后跟着32位的地址偏移,这个偏移不会被cpu使用。因为调用门描述符已经记录了目标代码段的偏移。使用调用门进行的跳转比普通跳转多一个步骤,即在访问调用门描述符时要将描述符当作一个数据段来检查访问权限,要求指示调用门的选择子的 RPL <= 门描述符DPL,同时当前代码段CPL <= 门描述符DPL。就如同访问数据段一样,要求访问数据段的程序的CPL <= 待访问的数据段的DPL,同时选择子的RPL <= 待访问的数据段或堆栈段的DPL。只有满足了以上条件,CPU才会进一步从调用门描述符中读取目标代码段的选择子和地址偏移,进行下一步的操作。
  从调用门中读取到目标代码的段选择子和地址偏移后,我们当前掌握的信息又回到了先前,和普通跳转站在了同一条起跑线上(普通跳转一开始就得到了目标代码的段选择子和地址偏移)。有所不同的是,此时,CPU会将读到的目标代码段选择子中的RPL清0,即忽略了调用门中代码段选择子的RPL的作用 。完成这一步后,CPU开始对当前程序的CPL,目标代码段选择子的RPL(事实上它被清0后总能满足要求)以及由目标代码选择子指示的目标代码段描述符中的DPL 进行特权级检查,并根据情况进行跳转。”
callgate

  其中,CPL为当前代码段的特权级。RPL为指向调用门的段选择子的请求特权级。DPL_G为门描述符的特权级。DPL_B是目标段的特权级。

3.2.5 call retf jmp

  直接转移只需要进行特权级检查,符合条件,即可通过选择子找到对应的段描述符。最后根据段描述符中的段基址并结合偏移,找到目标段,但CPL不会发生变化。(2)通过调用门的转移,也需要进行特权级检查。如果通过特权级检查,就可以根据门描述符中的段选择子找到目标段的段描述符,然后找到目标段。当使用call指令,通过调用门方式访问非一致代码段时,如果CPL > 目标段DPL,则会发生特权级变化,CPL最终等于目标段DPL。除此情况外,只要通过特权级检查,特权级都不会变化。
  call可以通过调用门实现由低特权级向高特权级的切换。retf可以实现由高特权级向低特权级的切换。jmp不能实现特权级(这里指CPL)的变换。
  call和retf在进行跳转时,还涉及堆栈操作。在保护模式下,对应每一个特权级,存在一个堆栈空间(共4中特权级,因此共4块堆栈空间。)。在从低特权级向高特权级跳转时(call gate),会从TSS中读出目标特权级堆栈的SS和SP并切换(还有对当前CS、EIP的保存和参数复制。返回时,retf会完成一个非逆操作流,但能实现堆栈切换与返回。此处不过度展开)。

3.2.6 尝试

  • 自定义添加1个GDT代码段、1个LDT代码段,GDT段内要对一个内存数据结构写入一段字符串,然后LDT段内代码段功能为读取并打印该GDT的内容。

  首先,分别添加GDT代码段、LDT代码段的段选择子与段描述符,并在段描述符初始化中,添加对两者段描述符的初始化。
  然后,编写两处代码段:
  GDT中,将字符“12345”写入data段中Empty处(Empty是实现开辟好的一段空间);LDT中,将字符“12345”读出,并在屏幕上显示。最终,系统从LDT代码段跳转回实模式。

;基于pmtest3.asm修改
; ==========================================
; pmtest3.asm
; 编译方法:nasm pmtest3.asm -o pmtest3.com
; ==========================================

%include	"pm.inc"	; 常量, 宏, 以及一些说明

org	0100h
	jmp	LABEL_BEGIN

[SECTION .gdt]
; GDT
;                                         段基址,       段界限     , 属性
LABEL_GDT:         Descriptor       0,                 0, 0     	; 空描述符
LABEL_DESC_NORMAL: Descriptor       0,            0ffffh, DA_DRW	; Normal 描述符
LABEL_DESC_CODE32: Descriptor       0,  SegCode32Len - 1, DA_C + DA_32	; 非一致代码段, 32
LABEL_DESC_CODE:   Descriptor       0,    SegCodeLen - 1, DA_C + DA_32  ; my code segment  
LABEL_DESC_CODE16: Descriptor       0,            0ffffh, DA_C		; 非一致代码段, 16
LABEL_DESC_DATA:   Descriptor       0,       DataLen - 1, DA_DRW+DA_DPL1	; Data
LABEL_DESC_STACK:  Descriptor       0,        TopOfStack, DA_DRWA + DA_32; Stack, 32 位
LABEL_DESC_LDT:    Descriptor       0,        LDTLen - 1, DA_LDT	; LDT
LABEL_DESC_VIDEO:  Descriptor 0B8000h,            0ffffh, DA_DRW	; 显存首地址
; GDT 结束

GdtLen		equ	$ - LABEL_GDT	; GDT长度
GdtPtr		dw	GdtLen - 1	; GDT界限
		dd	0		; GDT基地址

; GDT 选择子
SelectorNormal		equ	LABEL_DESC_NORMAL	- LABEL_GDT
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorCode16		equ	LABEL_DESC_CODE16	- LABEL_GDT
SelectorData		equ	LABEL_DESC_DATA		- LABEL_GDT
SelectorStack		equ	LABEL_DESC_STACK	- LABEL_GDT
SelectorLDT		equ	LABEL_DESC_LDT		- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT
SelectorCode		equ	LABEL_DESC_CODE		- LABEL_GDT

; END of [SECTION .gdt]

[SECTION .data1]	 ; 数据段
ALIGN	32
[BITS	32]
LABEL_DATA:
SPValueInRealMode	dw	0
; 字符串
PMMessage:		db	"In Protect Mode now. ^-^", 0	; 进入保护模式后显示此字符串
OffsetPMMessage		equ	PMMessage - $$
StrTest:		db	"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0		
OffsetStrTest		equ	StrTest - $$
Mystr:			db	"12345", 0
OffsetMystr:		db	Mystr - $$
Empty:			db	0, 0, 0, 0, 0, 0
OffsetEmpty:		equ	Empty - $$
DataLen			equ	$ - LABEL_DATA
; END of [SECTION .data1]


; 全局堆栈段
[SECTION .gs]
ALIGN	32
[BITS	32]
LABEL_STACK:
	times 512 db 0

TopOfStack	equ	$ - LABEL_STACK - 1

; END of [SECTION .gs]


[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, 0100h

	mov	[LABEL_GO_BACK_TO_REAL+3], ax
	mov	[SPValueInRealMode], sp

	; 初始化 16 位代码段描述符
	mov	ax, cs
	movzx	eax, ax
	shl	eax, 4
	add	eax, LABEL_SEG_CODE16
	mov	word [LABEL_DESC_CODE16 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE16 + 4], al
	mov	byte [LABEL_DESC_CODE16 + 7], ah

	; 初始化 32 位代码段描述符
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4
	add	eax, LABEL_SEG_CODE32
	mov	word [LABEL_DESC_CODE32 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE32 + 4], al
	mov	byte [LABEL_DESC_CODE32 + 7], ah
	
	;initialization mycodesegment descriptor
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4
	add	eax, LABEL_SEG_CODE
	mov	word [LABEL_DESC_CODE + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE + 4], al
	mov	byte [LABEL_DESC_CODE + 7], ah
	
	; 初始化数据段描述符
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_DATA
	mov	word [LABEL_DESC_DATA + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_DATA + 4], al
	mov	byte [LABEL_DESC_DATA + 7], ah

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

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

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

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

	; 为加载 GDTR 作准备
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_GDT		; eax <- gdt 基地址
	mov	dword [GdtPtr + 2], eax	; [GdtPtr + 2] <- gdt 基地址

	; 加载 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 SelectorCode:0	; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:		; 从保护模式跳回到实模式就到了这里
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax

	mov	sp, [SPValueInRealMode]

	in	al, 92h		; ┓
	and	al, 11111101b	; ┣ 关闭 A20 地址线
	out	92h, al		; ┛

	sti			; 开中断

	mov	ax, 4c00h	; ┓
	int	21h		; ┛回到 DOS
; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS	32]
LABEL_SEG_CODE:
	xor	eax, eax
	mov	ax, SelectorData
	mov	es, ax
	mov	edi, OffsetEmpty
	mov	ax, SelectorStack
	mov	ss, ax
	
	mov	esp,TopOfStack
	xchg	bx, bx
	mov	al, 49	; 1
	xor	ecx, ecx
	mov	cx, 5
fill:	
	xchg	bx, bx
	mov	[es:edi], al
	inc	ax
	inc	edi
	loop 	fill

	xchg	bx, bx
	mov	ax, SelectorLDT
	lldt	ax

	jmp	SelectorLDTCodeB:0

SegCodeLen	equ	$ - LABEL_SEG_CODE
;--------------------------------------------------------
LABEL_SEG_CODE32:
	mov	ax, SelectorData
	mov	ds, ax			; 数据段选择子
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子

	mov	ax, SelectorStack
	mov	ss, ax			; 堆栈段选择子

	mov	esp, TopOfStack


	; 下面显示一个字符串
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	xor	esi, esi
	xor	edi, edi
	mov	esi, OffsetPMMessage	; 源数据偏移
	mov	edi, (80 * 10 + 0) * 2	; 目的数据偏移。屏幕第 10 行, 第 0 列。
	cld
.1:
	lodsb
	test	al, al
	jz	.2
	mov	[gs:edi], ax
	add	edi, 2
	jmp	.1
.2:	; 显示完毕

	call	DispReturn
	;xchg	bx, bx
	; Load LDT
	mov	ax, SelectorLDT
	lldt	ax
	;xchg	bx, bx
	jmp	SelectorLDTCodeA:0	; 跳入局部任务

; ------------------------------------------------------------------------
DispReturn:
	push	eax
	push	ebx
	mov	eax, edi
	mov	bl, 160
	div	bl
	and	eax, 0FFh
	inc	eax
	mov	bl, 160
	mul	bl
	mov	edi, eax
	pop	ebx
	pop	eax

	ret
; DispReturn 结束---------------------------------------------------------

SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]


; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN	32
[BITS	16]
LABEL_SEG_CODE16:
	; 跳回实模式:
	mov	ax, SelectorNormal
	mov	ds, ax
	mov	es, ax
	mov	fs, ax
	mov	gs, ax
	mov	ss, ax

	mov	eax, cr0
	and	al, 11111110b
	mov	cr0, eax

LABEL_GO_BACK_TO_REAL:
	jmp	0:LABEL_REAL_ENTRY	; 段地址会在程序开始处被设置成正确的值

Code16Len	equ	$ - LABEL_SEG_CODE16

; END of [SECTION .s16code]


; LDT
[SECTION .ldt]
ALIGN	32
LABEL_LDT:
;                            段基址       段界限      属性
LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位
LABEL_LDT_DESC_CODEB:  Descriptor 0, CodeBLen - 1, DA_C +DA_32  ; my local code
LDTLen		equ	$ - LABEL_LDT

; LDT 选择子
SelectorLDTCodeA	equ	LABEL_LDT_DESC_CODEA	- LABEL_LDT + SA_TIL
SelectorLDTCodeB	equ	LABEL_LDT_DESC_CODEB	- LABEL_LDT + SA_TIL
; END of [SECTION .ldt]


; CodeA (LDT, 32 位代码段)
[SECTION .la]
ALIGN	32
[BITS	32]
LABEL_CODE_B:
	mov	ax, SelectorVideo
	mov	gs, ax
	mov	ax, SelectorData
	mov 	ds, ax
	mov	esi, OffsetEmpty

	mov	edi, (80 * 12 + 0) * 2
	mov	ah, 0Ch

	xor	ecx, ecx
	mov	cx, 5
output:
	mov 	al, [ds:esi]
	mov	[gs:edi], ax
	inc	esi
	inc	edi
	inc	edi
	loop	output
	
	jmp	SelectorCode16:0	
CodeBLen	equ	$ - LABEL_CODE_B
LABEL_CODE_A:
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子(目的)

	mov	edi, (80 * 12 + 0) * 2	; 屏幕第 10 行, 第 0 列。
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, 'L'
	mov	[gs:edi], ax
	;xchg	bx, bx
	; 准备经由16位代码段跳回实模式
	jmp	SelectorCode16:0
CodeALen	equ	$ - LABEL_CODE_A
; END of [SECTION .la]

try1

  • 自定义2个GDT代码段A、B,分属于不同特权级,功能自定义,要求实现A–>B的跳转,以及B–>A的跳转。
      在pmtest5.asm的基础上,修改程序,实现从ring0->ring3->ring1->ring3->ring0的特权级转换。
;基于pmtest5.asm修改
; ==========================================
; pmtest5.asm
; 编译方法:nasm pmtest5.asm -o pmtest5.com
; ==========================================

%include	"pm.inc"	; 常量, 宏, 以及一些说明

org	0100h
	jmp	LABEL_BEGIN

[SECTION .gdt]
; GDT
;                            段基址,           段界限     , 属性
LABEL_GDT:             Descriptor 0,                 0, 0		   ;空描述符
LABEL_DESC_NORMAL:     Descriptor 0,            0ffffh, DA_DRW		   ;Normal描述符
LABEL_DESC_CODE32:     Descriptor 0,    SegCode32Len-1, DA_C+DA_32	   ;非一致,32
LABEL_DESC_CODE16:     Descriptor 0,            0ffffh, DA_C		   ;非一致,16
LABEL_DESC_CODE_DEST:  Descriptor 0,  SegCodeDestLen-1, DA_C+DA_32	   ;非一致,32
LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3
LABEL_DESC_CODE_RING1: Descriptor 0, SegCodeRing1Len-1, DA_C+DA_32+DA_DPL1
LABEL_DESC_DATA:       Descriptor 0,	     DataLen-1, DA_DRW             ;Data
LABEL_DESC_STACK:      Descriptor 0,        TopOfStack, DA_DRWA+DA_32	   ;Stack,32
LABEL_DESC_STACK1:     Descriptor 0,       TopOfStack1, DA_DRWA+DA_32+DA_DPL1
LABEL_DESC_STACK3:     Descriptor 0,       TopOfStack3, DA_DRWA+DA_32+DA_DPL3
LABEL_DESC_LDT:        Descriptor 0,          LDTLen-1, DA_LDT		   ;LDT
LABEL_DESC_TSS:        Descriptor 0,          TSSLen-1, DA_386TSS	   ;TSS
LABEL_DESC_VIDEO:      Descriptor 0B8000h,      0ffffh, DA_DRW+DA_DPL3

; 门                                            目标选择子,       偏移, DCount, 属性
LABEL_CALL_GATE_TEST:	Gate		  SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL3
LABEL_CALL_GATE_31:	Gate		 SelectorCodeRing1,	     0,      0, DA_386CGate + DA_DPL3
; GDT 结束

GdtLen		equ	$ - LABEL_GDT	; GDT长度
GdtPtr		dw	GdtLen - 1	; GDT界限
		dd	0		; GDT基地址

; GDT 选择子
SelectorNormal		equ	LABEL_DESC_NORMAL	- LABEL_GDT
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorCode16		equ	LABEL_DESC_CODE16	- LABEL_GDT
SelectorCodeDest	equ	LABEL_DESC_CODE_DEST	- LABEL_GDT
SelectorCodeRing1	equ	LABEL_DESC_CODE_RING1	- LABEL_GDT + SA_RPL1
SelectorCodeRing3	equ	LABEL_DESC_CODE_RING3	- LABEL_GDT + SA_RPL3
SelectorData		equ	LABEL_DESC_DATA		- LABEL_GDT
SelectorStack		equ	LABEL_DESC_STACK	- LABEL_GDT
SelectorStack1		equ	LABEL_DESC_STACK1	- LABEL_GDT + SA_RPL1
SelectorStack3		equ	LABEL_DESC_STACK3	- LABEL_GDT + SA_RPL3
SelectorLDT		equ	LABEL_DESC_LDT		- LABEL_GDT
SelectorTSS		equ	LABEL_DESC_TSS		- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT


SelectorCallGateTest	equ	LABEL_CALL_GATE_TEST	- LABEL_GDT + SA_RPL3
SelectorCallGate31	equ	LABEL_CALL_GATE_31	- LABEL_GDT + SA_RPL3
; END of [SECTION .gdt]

[SECTION .data1]	 ; 数据段
ALIGN	32
[BITS	32]
LABEL_DATA:
SPValueInRealMode	dw	0
; 字符串
PMMessage:		db	"In Protect Mode now. ^-^", 0	; 进入保护模式后显示此字符串
OffsetPMMessage		equ	PMMessage - $$
StrTest:		db	"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest		equ	StrTest - $$
DataLen			equ	$ - LABEL_DATA
; END of [SECTION .data1]


; 全局堆栈段
[SECTION .gs]
ALIGN	32
[BITS	32]
LABEL_STACK:
	times 512 db 0
TopOfStack	equ	$ - LABEL_STACK - 1
; END of [SECTION .gs]

; 堆栈段ring1
[SECTION .s1]
ALIGN	32
[BITS	32]
LABEL_STACK1:
	times 512 db 0
TopOfStack1	equ	$ - LABEL_STACK1 - 1
; END of [SECTION .s1]

; 堆栈段ring3
[SECTION .s3]
ALIGN	32
[BITS	32]
LABEL_STACK3:
	times 512 db 0
TopOfStack3	equ	$ - LABEL_STACK3 - 1
; END of [SECTION .s3]

; TSS ---------------------------------------------------------------------------------------------
[SECTION .tss]
ALIGN	32
[BITS	32]
LABEL_TSS:
		DD	0			; Back
		DD	TopOfStack		; 0 级堆栈
		DD	SelectorStack		; 
		DD	TopOfStack1		; 1 级堆栈
		DD	SelectorStack1			; 
		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
; TSS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, 0100h

	mov	[LABEL_GO_BACK_TO_REAL+3], ax
	mov	[SPValueInRealMode], sp

	; 初始化 16 位代码段描述符
	mov	ax, cs
	movzx	eax, ax
	shl	eax, 4
	add	eax, LABEL_SEG_CODE16
	mov	word [LABEL_DESC_CODE16 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE16 + 4], al
	mov	byte [LABEL_DESC_CODE16 + 7], ah

	; 初始化 32 位代码段描述符
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4
	add	eax, LABEL_SEG_CODE32
	mov	word [LABEL_DESC_CODE32 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE32 + 4], al
	mov	byte [LABEL_DESC_CODE32 + 7], ah

	; 初始化测试调用门的代码段描述符
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4
	add	eax, LABEL_SEG_CODE_DEST
	mov	word [LABEL_DESC_CODE_DEST + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE_DEST + 4], al
	mov	byte [LABEL_DESC_CODE_DEST + 7], ah

	; 初始化数据段描述符
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_DATA
	mov	word [LABEL_DESC_DATA + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_DATA + 4], al
	mov	byte [LABEL_DESC_DATA + 7], ah

	; 初始化堆栈段描述符
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_STACK
	mov	word [LABEL_DESC_STACK + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_STACK + 4], al
	mov	byte [LABEL_DESC_STACK + 7], ah
	
	;stack desc r1
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_STACK1
	mov	word [LABEL_DESC_STACK1 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_STACK1 + 4], al
	mov	byte [LABEL_DESC_STACK1 + 7], ah

	; 初始化堆栈段描述符(ring3)
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_STACK3
	mov	word [LABEL_DESC_STACK3 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_STACK3 + 4], al
	mov	byte [LABEL_DESC_STACK3 + 7], ah

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

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

	; 初始化Ring3描述符
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_CODE_RING1
	mov	word [LABEL_DESC_CODE_RING1 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE_RING1 + 4], al
	mov	byte [LABEL_DESC_CODE_RING1 + 7], ah

	; 初始化Ring3描述符
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_CODE_RING3
	mov	word [LABEL_DESC_CODE_RING3 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE_RING3 + 4], al
	mov	byte [LABEL_DESC_CODE_RING3 + 7], ah

	; 初始化 TSS 描述符
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_TSS
	mov	word [LABEL_DESC_TSS + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_TSS + 4], al
	mov	byte [LABEL_DESC_TSS + 7], ah

	; 为加载 GDTR 作准备
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_GDT		; eax <- gdt 基地址
	mov	dword [GdtPtr + 2], eax	; [GdtPtr + 2] <- gdt 基地址

	; 加载 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	; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:		; 从保护模式跳回到实模式就到了这里
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax

	mov	sp, [SPValueInRealMode]

	in	al, 92h		; ┓
	and	al, 11111101b	; ┣ 关闭 A20 地址线
	out	92h, al		; ┛

	sti			; 开中断

	mov	ax, 4c00h	; ┓
	int	21h		; ┛回到 DOS
; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS	32]

LABEL_SEG_CODE32:
	mov	ax, SelectorData
	mov	ds, ax			; 数据段选择子
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子

	mov	ax, SelectorStack
	mov	ss, ax			; 堆栈段选择子

	mov	esp, TopOfStack


	; 下面显示一个字符串
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	xor	esi, esi
	xor	edi, edi
	mov	esi, OffsetPMMessage	; 源数据偏移
	mov	edi, (80 * 10 + 0) * 2	; 目的数据偏移。屏幕第 10 行, 第 0 列。
	cld
.1:
	lodsb
	test	al, al
	jz	.2
	mov	[gs:edi], ax
	add	edi, 2
	jmp	.1
.2:	; 显示完毕

	call	DispReturn
	xchg	bx, bx
	; Load TSS
	mov	ax, SelectorTSS
	ltr	ax	; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器 TR。
	xchg	bx, bx
	push	SelectorStack3
	push	TopOfStack3
	push	SelectorCodeRing3
	push	0
	xchg	bx, bx
	retf		; Ring0 -> Ring3,历史性转移!将打印数字 '3'。

; ------------------------------------------------------------------------
DispReturn:
	push	eax
	push	ebx
	mov	eax, edi
	mov	bl, 160
	div	bl
	and	eax, 0FFh
	inc	eax
	mov	bl, 160
	mul	bl
	mov	edi, eax
	pop	ebx
	pop	eax

	ret
; DispReturn 结束---------------------------------------------------------

SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]


[SECTION .sdest]; 调用门目标段
[BITS	32]

LABEL_SEG_CODE_DEST:
	xchg	bx, bx
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子(目的)

	mov	edi, (80 * 12 + 0) * 2	; 屏幕第 12 行, 第 0 列。
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, 'C'
	mov	[gs:edi], ax

	; Load LDT
	mov	ax, SelectorLDT
	lldt	ax
	xchg	bx, bx
	jmp	SelectorLDTCodeA:0	; 跳入局部任务,将打印字母 'L'。

	;retf

SegCodeDestLen	equ	$ - LABEL_SEG_CODE_DEST
; END of [SECTION .sdest]


; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN	32
[BITS	16]
LABEL_SEG_CODE16:
	; 跳回实模式:
	mov	ax, SelectorNormal
	mov	ds, ax
	mov	es, ax
	mov	fs, ax
	mov	gs, ax
	mov	ss, ax

	mov	eax, cr0
	and	al, 11111110b
	mov	cr0, eax
	xchg	bx, bx
LABEL_GO_BACK_TO_REAL:
	jmp	0:LABEL_REAL_ENTRY	; 段地址会在程序开始处被设置成正确的值

Code16Len	equ	$ - LABEL_SEG_CODE16

; END of [SECTION .s16code]


; LDT
[SECTION .ldt]
ALIGN	32
LABEL_LDT:
;                                         段基址       段界限     ,   属性
LABEL_LDT_DESC_CODEA:	Descriptor	       0,     CodeALen - 1,   DA_C + DA_32	; Code, 32 位

LDTLen		equ	$ - LABEL_LDT

; LDT 选择子
SelectorLDTCodeA	equ	LABEL_LDT_DESC_CODEA	- LABEL_LDT + SA_TIL
; END of [SECTION .ldt]


; CodeA (LDT, 32 位代码段)
[SECTION .la]
ALIGN	32
[BITS	32]
LABEL_CODE_A:
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子(目的)

	mov	edi, (80 * 13 + 0) * 2	; 屏幕第 13 行, 第 0 列。
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, 'L'
	mov	[gs:edi], ax

	; 准备经由16位代码段跳回实模式
	xchg	bx, bx
	jmp	SelectorCode16:0
CodeALen	equ	$ - LABEL_CODE_A
; END of [SECTION .la]

; CodeRing1
[SECTION .ring1]
ALIGN	32
[BITS	32]
LABEL_CODE_RING1:

	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子(目的)

	mov	edi, (80 * 14 + 1) * 2	; 屏幕第 14 行, 第 1 列。
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, '1'
	mov	[gs:edi], ax
	
	xchg	bx, bx
	;push	SelectorStack3
	;push	TopOfStack3
	;push	SelectorCodeRing3
	;push	0
	retf
SegCodeRing1Len	equ	$ - LABEL_CODE_RING1
; END of [SECTION .ring1]

; CodeRing3
[SECTION .ring3]
ALIGN	32
[BITS	32]
LABEL_CODE_RING3:
	xchg	bx, bx
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子(目的)

	mov	edi, (80 * 14 + 0) * 2	; 屏幕第 14 行, 第 0 列。
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, '3'
	mov	[gs:edi], ax
	xchg	bx, bx
	call	SelectorCallGate31:0	
	call	SelectorCallGateTest:0	; 测试调用门(有特权级变换),将打印字母 'C'。
	jmp	$
SegCodeRing3Len	equ	$ - LABEL_CODE_RING3
; END of [SECTION .ring3]

try2

3.2.7 对保护模式的“保护”的理解

  一方面,段描述符中的段基址和段界限定义了一个段的范围,对超越段界限的地址的访问是禁止的,这无疑是对段的一种保护。段属性作为对段各个方面的定义,规定和限制了段的行为和性质。另一方面,在段间切换的过程中,会进行特权级检查,动态地对访问权限进行限制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值