自制OS3-1到3-16==保护模式(GDT、选择子、寄存器)、多任务由来(LDT)、内核态和用户态ring0和ring3、特权级切换(TSS-CPL-DPL-RPL-门)、时钟中断、保护模式中断编程

本文主要是介绍自制OS3-1到3-16==保护模式(GDT、选择子、寄存器)、多任务由来(LDT)、内核态和用户态ring0和ring3、特权级切换(TSS-CPL-DPL-RPL-门)、时钟中断、保护模式中断编程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

保护模式

前面我们可以任意的访问内存,寄存器在段式内存中,其实没有界限,操作系统和用户程序实际上做不到内存的隔离。

用户程序所访问的逻辑地址,实际上就是物理地址。

用户程序可以随意修改段基址(mbr es=7c00 loader=b800)

保护模式将16位寄存器扩展到了32位

 向下兼容,原先得段+偏移这种编程寻址结构,不破坏这种结构。

INTEL专门设计了一个数据结构来描述这个寻址,全局描述符表GDT

由于这个一个复杂的数据结构,放在内存中

在这个GDT中有一个叫做段描述符的,用一个叫做GDTR的寄存器指向他。

将一个数据的访问分成三个部分,CPU要把他拼起来,这个过程叫做保护模式下的寻址。

可以看下INTEL 80386的手册。

验证 寄存器

以保护模式的方式访问物理地址 0B8000h


;----------------------------------------------------------------------------
DA_DR		EQU	90h	; 存在的只读数据段类型值
DA_DRW		EQU	92h	; 存在的可读写数据段属性值
DA_DRWA		EQU	93h	; 存在的已访问可读写数据段类型值
DA_C		EQU	98h	; 存在的只执行代码段属性值
DA_CR		EQU	9Ah	; 存在的可执行可读代码段属性值
DA_CCO		EQU	9Ch	; 存在的只执行一致代码段属性值
DA_CCOR		EQU	9Eh	; 存在的可执行可读一致代码段属性值
;----------------------------------------------------------------------------
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
;----------------------------------------------------------------------------
%macro Descriptor 3dw	%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 ;org 07c00hjmp	LABEL_BEGIN[SECTION .gdt]
; GDT
;                                         段基址,      段界限     , 属性
LABEL_GDT:		Descriptor	       0,                0, 0     		; 空描述符
LABEL_DESC_CODE32:	Descriptor	       0, SegCode32Len - 1, DA_C + DA_32	; 非一致代码段, 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, csmov	ds, axmov	es, axmov	ss, axmov	sp, 0100h; 初始化 32 位代码段描述符xor	eax, eax  mov	ax, csshl	eax, 4add	eax, LABEL_SEG_CODE32mov	word [LABEL_DESC_CODE32 + 2], axshr	eax, 16mov	byte [LABEL_DESC_CODE32 + 4], almov	byte [LABEL_DESC_CODE32 + 7], ah; 为加载 GDTR 作准备xor	eax, eaxmov	ax, dsshl	eax, 4add	eax, LABEL_GDT		; eax <- gdt 基地址mov	dword [GdtPtr + 2], eax	; [GdtPtr + 2] <- gdt 基地址; 加载 GDTRlgdt	[GdtPtr]; 关中断cli; 打开地址线A20in	al, 92hor	al, 00000010bout	92h, al; 准备切换到保护模式mov	eax, cr0or	eax, 1mov	cr0, eax; 真正进入保护模式jmp	dword SelectorCode32:0	; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处
; END of [SECTION .s16][SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS	32]LABEL_SEG_CODE32:mov	ax, SelectorVideomov	gs, ax			; 视频段选择子(目的)mov	edi, (80 * 3 + 0) * 2	; 屏幕第 3 行, 第 0 列。mov	ah, 0Ch			; 0000: 黑底    1100: 红字mov	al, 'D'mov	[gs:edi], ax; 到此停止jmp	$SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]
times 361	db 0
dw 0xaa55

 编写上述p1.asm,编译成p1.bin,通过dd写入虚拟磁盘,然后用虚拟机加载虚拟磁盘

E:\>nasm p1.asm -o p1.bin

E:\>dd if=p1.bin of=dingst.vhd bs=512 count=1
rawwrite dd for windows version 0.5.
Written by John Newbigin <jn@it.swin.edu.au>
This program is covered by the GPL.  See copying.txt for details
1+0 records in
1+0 records out

从上述代码可以发现,我们不是直接使用的物理地址0B8000h,用的另外一种方式将想要显示的数据放到了 0B8000h

16位实模式下:物理地址=逻辑地址=段首址+偏移

32位保护模式下:不是简单的16位的增强,特指CPU可以使用32位状态下的保护模式

想让每一个应用程序都可以独立的享有4G的空间。但是只有32位的地址线,理论下之后2的32次方也就是4G的物理地址,32位CPU有32根地址线,物理地址=2^32=4294967296=4GB,但是在实际应用中,PCI内存范围占用了大量的地址范围——接近750MB,导致最后系统物理地址只有3.25GB左右。所以怎么能够做到让每一个应用程序都可以独立的享有4G的空间?

        1、必须要有硬件的支持  这个硬件叫做MMU。将MMU映射到实际的物理地址中。

        2、编址的观念必须变化

由此应用程序就不用考虑使用哪些物理地址,都拥有4G的可用虚拟地址 ,所有程序都有自己的段0-4G,这就是线性地址。但是为了使用好地址翻译0-4G线性,我们必须规定,你的所有的内存的访问:

        1、在定义程序的段、段界限,要附加段的特权、段的类型,防止内存的误操作

        2、cs 、ds这些所谓的在16位实模式下的段寄存器,不再是段基址的概念,而要变成段选择子,而变成段选择子,就放弃了直接访问物理内存的能力。

只有段选择子和段描述符结合,才能真怎访问物理地址,这是硬件赋予的保护能力。

应用程序只需要调用MMU,实际的物理地址由MMU自动分配。

所以我们需要一个全新的数据结构去管理这个内容→GDT

GDT由GDTR管理。

OS管理计算机的硬件软件,他可以做任何事情。但是应用程序绝对不可以,我们希望应用程序只能访问自己的数据,及时这个程序要跳转,那么也只能在自己的各个代码段中进行跳转。

如果我们的OS是一个多用户多任务的系统,那么我们使用实模式不设防的内存访问,很有可能造成程序崩溃,因为可能这个程序使用的地址被另一个程序更改了。

=======================================================================

内存的访问

 为了能让程序在内存中自由定位那么我们设计了段,但是在保护模式下,对每一个段,必须登记。登记的段的信息,称为段的描述字descriptor。段的描述字规定为8个字节(64位),所有允许的描述字构成了全局描述符表。GDT=global descriptor table,为整个软硬件服务,所以必须在进入保护模式之前定义它。先有工商局,工商局先有商店门类规范,你才能去申请开店。

段的描述字/符组成如图:

查询因特尔的手册

在保护模式下,我们可以通过GDT来约束应用程序访问内存的权限。

在访问之前,必须先在GDT中定义你要访问的段。描述符不是用户程序自己建立的,而是在加载的时候,由OS根据你的程序结构建立。用户程序无法建立和修改GDT。

实现保护模式流程:

首先初始化GDT(填申请表并提交到特定地址)

第二将内存中的表,通过LGDT加载(OS去特定地址审核填的表)

 关闭A20地址线

打开CR0寄存器(段寻址切换到段选择子)

进入保护模式

GDT翻译后,线性地址经过MMU变成实际的物理地址 

保护模式实战:

使用虚拟机启动DOS622操作系统,用于学习保护模式。

GDT 选择子 寄存器

选择刚才创建的ISO文件,即可发现文件出现在了DOS622的R盘中。

1、准备GDT

2、用LGDT加载GDT

3、打开A20

4、打开CR0寄存器

5、jmp跳入保护模式的地址

DA_32 EQU	4000h;32位
DA_C EQU 98h; 只执行代码段的属性
DA_DRW	EQU 92h;可读写的数据段
DA_DRWA EQU 93h;存在的已访问的可读写的%macro Descriptor 3dw %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
%endmacroorg 0100h 	;因为我们dos下调试程序,那么0100是可用区域jmp PM_BEGIN	;跳入到标号为PM_BEGIN的代码段开始推进[SECTION .gdt]
;GDT
;								段基址,段界限,属性
PM_GDT:				Descriptor		0,		0,		0
PM_DESC_CODE32:	Descriptor		0,		SegCode32Len -1,DA_C+DA_32
PM_DESC_DATA:		Descriptor		0,		DATALen-1, DA_DRW	
PM_DESC_STACK:		Descriptor		0,		TopOfStack,	DA_DRWA+DA_32
PM_DESC_TEST:		Descriptor		0200000h,0ffffh,	DA_DRW
PM_DESC_VIDEO:		Descriptor		0B8000h,	0ffffh, DA_DRW
;end of definiton gdt
GdtLen equ $ - PM_GDT
GdtPtr dw GdtLen - 1
dd  0 ; GDT 基地址;GDT 选择子
SelectoerCode32	equ PM_DESC_CODE32 - PM_GDT	
SelectoerDATA	equ PM_DESC_DATA - PM_GDT
SelectoerSTACK	equ PM_DESC_STACK - PM_GDT	
SelectoerTEST	equ PM_DESC_TEST - PM_GDT
SelectoerVideo	equ PM_DESC_VIDEO - PM_GDT				
;END of [SECTION .gdt][SECTION .data1]
ALIGN 32
[BITS 32]
PM_DATA:
PMMessage : db "Potect Mode", 0;
OffsetPMessage equ PMMessage - $$
DATALen equ $- PM_DATA
;END of [SECTION .data];全局的堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
PM_STACK:times 512 db 0
TopOfStack equ $ - PM_STACK -1
;END of STACK	[SECTION .s16]
[BITS 16]
PM_BEGIN:mov ax,csmov ds,axmov es,axmov ss,axmov sp,0100h;初始化32位的代码段xor eax,eaxmov ax,csshl eax,4add eax,PM_SEG_CODE32mov word[PM_DESC_CODE32+2],axshr eax,16mov byte [PM_DESC_CODE32+4],almov byte [PM_DESC_CODE32+7],ah;初始化32位的数据段xor eax,eaxmov ax,dsshl eax,4add eax,PM_DATAmov word[PM_DESC_DATA+2],axshr eax,16mov byte [PM_DESC_DATA+4],almov byte [PM_DESC_DATA+7],ah;初始化32位的stack段xor eax,eaxmov ax,dsshl eax,4add eax,PM_STACKmov word[PM_DESC_STACK+2],axshr eax,16mov byte [PM_DESC_STACK+4],almov byte [PM_DESC_STACK+7],ah;加载GDTRxor eax,eaxmov ax,dsshl eax,4add eax,PM_GDTmov dword [GdtPtr +2 ],eaxlgdt [GdtPtr];A20cliin al,92hor al,00000010bout 92h,al;切换到保护模式mov eax,cr0or eax,1mov cr0,eaxjmp dword SelectoerCode32:0[SECTION .s32]	;32位的代码段
[BITS 32]
PM_SEG_CODE32 :mov ax,SelectoerDATA	;通过数据段的选择子放入ds寄存器,就可以用段+偏移进行寻址mov ds,axmov ax,SelectoerTEST	;通过测试段的选择子放入es寄存器,就可以用段+偏移进行寻址mov es,axmov ax,SelectoerVideomov gs,axmov ax,SelectoerSTACKmov ss,axmov esp,TopOfStackmov ah,0Chxor esi,esixor edi,edimov esi,OffsetPMessagemov edi,(80*10 +0) *2cld.1:lodsbtest al,aljz .2mov [gs:edi],axadd edi,2jmp .1.2: ;显示完毕;测试段的寻址mov ax, 'A'mov [es:0],axmov ax,SelectoerVideomov gs,ax mov edi,(80*15 +0) *2mov ah,0Chmov al,[es:0]mov [gs:edi],axjmp $SegCode32Len equ $ - PM_SEG_CODE32

nasm pm.asm -o pm.exe

生成ISO文件后挂载到DOS622

然后执行

从上面可知已经突破BIOS 1M的寻址,实现了之前MBR+LOADER的相同功能,只是使用的不再是实模式,而是使用的保护模式了。

多任务的由来:LDT=>GDT

LDT局部描述符表 TSS

DA_32 EQU	4000h;32位
DA_C EQU 98h; 只执行代码段的属性
DA_DRW	EQU 92h;可读写的数据段
DA_DRWA EQU 93h;存在的已访问的可读写的
DA_LDT EQU 82h;省局SA_TIL EQU	4 ;具体的任务%macro Descriptor 3dw %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
%endmacroorg 0100h 	;因为我们dos下调试程序,那么0100是可用区域jmp PM_BEGIN	;跳入到标号为PM_BEGIN的代码段开始推进[SECTION .gdt]
;GDT
;								段基址,段界限,属性
PM_GDT:				Descriptor		0,		0,		0
PM_DESC_CODE32:	Descriptor		0,		SegCode32Len -1,DA_C+DA_32
PM_DESC_DATA:		Descriptor		0,		DATALen-1, DA_DRW	
PM_DESC_STACK:		Descriptor		0,		TopOfStack,	DA_DRWA+DA_32
PM_DESC_TEST:		Descriptor		0200000h,0ffffh,	DA_DRW
PM_DESC_VIDEO:		Descriptor		0B8000h,	0ffffh, DA_DRWLABEL_DESC_LDT:		Descriptor		0,	LDTLen -1, DA_LDT
;end of definiton gdt
GdtLen equ $ - PM_GDT
GdtPtr dw GdtLen - 1
dd  0 ; GDT 基地址;GDT 选择子
SelectoerCode32	equ PM_DESC_CODE32 - PM_GDT	
SelectoerDATA	equ PM_DESC_DATA - PM_GDT
SelectoerSTACK	equ PM_DESC_STACK - PM_GDT	
SelectoerTEST	equ PM_DESC_TEST - PM_GDT
SelectoerVideo	equ PM_DESC_VIDEO - PM_GDT	
SelectoerLDT	equ LABEL_DESC_LDT - PM_GDT			
;END of [SECTION .gdt][SECTION .data1]
ALIGN 32
[BITS 32]
PM_DATA:
PMMessage : db "Potect Mode", 0;
OffsetPMessage equ PMMessage - $$
DATALen equ $- PM_DATA
;END of [SECTION .data];全局的堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
PM_STACK:times 512 db 0
TopOfStack equ $ - PM_STACK -1
;END of STACK	[SECTION .s16]
[BITS 16]
PM_BEGIN:mov ax,csmov ds,axmov es,axmov ss,axmov sp,0100h;初始化32位的代码段xor eax,eaxmov ax,csshl eax,4add eax,PM_SEG_CODE32mov word[PM_DESC_CODE32+2],axshr eax,16mov byte [PM_DESC_CODE32+4],almov byte [PM_DESC_CODE32+7],ah;初始化32位的数据段xor eax,eaxmov ax,dsshl eax,4add eax,PM_DATAmov word[PM_DESC_DATA+2],axshr eax,16mov byte [PM_DESC_DATA+4],almov byte [PM_DESC_DATA+7],ah;初始化32位的stack段xor eax,eaxmov ax,dsshl eax,4add eax,PM_STACKmov word[PM_DESC_STACK+2],axshr eax,16mov byte [PM_DESC_STACK+4],almov byte [PM_DESC_STACK+7],ah;初始化32位的LDT,得把省局注册到全国xor eax,eaxmov ax,dsshl eax,4add eax,LABEL_LDT					;//to do mov word[LABEL_DESC_LDT+2],axshr eax,16mov byte [LABEL_DESC_LDT+4],almov byte [LABEL_DESC_LDT+7],ah;根据GDT,把LDT管理的具体公司初始化,得把省局注册到全国xor eax,eaxmov ax,dsshl eax,4add eax, LABEL_CODE_A					;//to do mov word[LABEL_LDT_DESC_CODEA+2],axshr eax,16mov byte [LABEL_LDT_DESC_CODEA +4],almov byte [LABEL_LDT_DESC_CODEA +7],ah;加载GDTRxor eax,eaxmov ax,dsshl eax,4add eax,PM_GDTmov dword [GdtPtr +2 ],eaxlgdt [GdtPtr];A20cliin al,92hor al,00000010bout 92h,al;切换到保护模式mov eax,cr0or eax,1mov cr0,eaxjmp dword SelectoerCode32:0[SECTION .s32]	;32位的代码段
[BITS 32]
PM_SEG_CODE32 :mov ax,SelectoerDATA	;通过数据段的选择子放入ds寄存器,就可以用段+偏移进行寻址mov ds,axmov ax,SelectoerTEST	;通过测试段的选择子放入es寄存器,就可以用段+偏移进行寻址mov es,axmov ax,SelectoerVideomov gs,axmov ax,SelectoerSTACKmov ss,axmov esp,TopOfStackmov ah,0Chxor esi,esixor edi,edimov esi,OffsetPMessagemov edi,(80*10 +0) *2cld.1:lodsbtest al,aljz .2mov [gs:edi],axadd edi,2jmp .1.2: ;显示完毕;Load LDTmov ax,SelectoerLDT	;	SelectoerLDT=> GDTlldt axjmp SelectoerLDTCodeA:0SegCode32Len equ $ - PM_SEG_CODE32;LDT
[SECTION .ldt]
ALIGN 32
LABEL_LDT:
;									段基址,段界限,		属性
LABEL_LDT_DESC_CODEA:	Descriptor		0,		CodeALen-1,		DA_C+DA_32LDTLen	equ $ - LABEL_LDT
;选择子
SelectoerLDTCodeA	equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL[SECTION .la]
ALIGN 32
[BITS 32]
LABEL_CODE_A:mov  ax,SelectoerVideomov gs,axmov edi, (80*5 +0) *2mov ah, 0Chmov al, 'D'mov [gs:edi],axjmp $
CodeALen  equ $ - LABEL_CODE_A
;END of 任务段

ring0和ring3的由来:内核态和用户态的切换

CPL:当前权限级别

DPL :descriptor privilege level 段( 门)的特权级。  当当前的代码段去访问对应的真实地址,DPL会检查程序间的允许级别是否一致。低权限的访问高权限的数据直接程序崩溃。

RPL:请求权限级别

TSS Task State Segment:任务状态段。任务和进程

        如果没有OS,那么任务就等价于进程。任务实际上是一段运行在处理器上的程序。分为系统程序和用户程序。所有的程序,在有OS,写的是一个半成品,我们只负责用户态下面的,内核部分的就是OS提供的。任务是在CPU上推进,权限的转化。

        TSS维护一个栈的结构。TSS是CPU这种硬件原生的系统级别的数据结构。必须有TSS才能实现特权转换。

GDT:在内存里面,有GDTR寄存器加载

TSS:由TR寄存器加载。

这种硬件级别的数据结构,由软件填写内容,由硬件使用。

调用门与特权切换

通过调用门的方式实现两个程序的切换

DA_32 EQU	4000h;32位
DA_C EQU 98h; 只执行代码段的属性
DA_DRW	EQU 92h;可读写的数据段
DA_DRWA EQU 93h;存在的已访问的可读写的
DA_LDT EQU 82h;省局SA_TIL EQU	4 ;具体的任务
SA_RPL3 EQU 3DA_DPL3 EQU 60hDA_386TSS EQU 89h%macro Descriptor 3dw %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
%endmacroorg 0100h 	;因为我们dos下调试程序,那么0100是可用区域jmp PM_BEGIN	;跳入到标号为PM_BEGIN的代码段开始推进[SECTION .gdt]
;GDT
;								段基址,段界限,属性
PM_GDT:				Descriptor		0,		0,		0
PM_DESC_CODE32:	Descriptor		0,		SegCode32Len -1,DA_C+DA_32
PM_DESC_DATA:		Descriptor		0,		DATALen-1, DA_DRW	
PM_DESC_STACK:		Descriptor		0,		TopOfStack,	DA_DRWA+DA_32
PM_DESC_TEST:		Descriptor		0200000h,0ffffh,	DA_DRW
PM_DESC_VIDEO:		Descriptor		0B8000h,	0ffffh, DA_DRW + DA_DPL3LABEL_DESC_LDT:		Descriptor		0,	LDTLen -1, DA_LDTPM_DESC_CODE_DEST:	Descriptor		0,	SegCodeDestLen -1,DA_C+DA_32PM_DESC_CODE_RING3: Descriptor		0,	SegCodeRing3Len-1,DA_C+DA_32 + DA_DPL3
PM_DESC_STACK3:		Descriptor		0,		TopOfStack3,	DA_DRWA+DA_32+DA_DPL3
PM_DESC_TSS:		Descriptor      0, TSSLen -1,DA_386TSSPM_CALL_GATE_TEST:
dw 00000h
dw SelectoerCodeDest
dw 0ec00h
dw 00000h
;end of definiton gdt
GdtLen equ $ - PM_GDT
GdtPtr dw GdtLen - 1
dd  0 ; GDT 基地址;GDT 选择子
SelectoerCode32	equ PM_DESC_CODE32 - PM_GDT	
SelectoerDATA	equ PM_DESC_DATA - PM_GDT
SelectoerSTACK	equ PM_DESC_STACK - PM_GDT	
SelectoerTEST	equ PM_DESC_TEST - PM_GDT
SelectoerVideo	equ PM_DESC_VIDEO - PM_GDT	
SelectoerLDT	equ LABEL_DESC_LDT - PM_GDT	SelectoerCodeDest equ PM_DESC_CODE_DEST - PM_GDT
SelectorCallGateTest equ PM_CALL_GATE_TEST - PM_GDT + SA_RPL3SelctorCodeRing3 equ PM_DESC_CODE_RING3 - PM_GDT + SA_RPL3
SelctorStack3 equ PM_DESC_STACK3 - PM_GDT + SA_RPL3
SelctorTSS equ 		PM_DESC_TSS - PM_GDT;END of [SECTION .gdt][SECTION .data1]
ALIGN 32
[BITS 32]
PM_DATA:
PMMessage : db "Potect Mode", 0;
OffsetPMessage equ PMMessage - $$
DATALen equ $- PM_DATA
;END of [SECTION .data];全局的堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
PM_STACK:times 512 db 0
TopOfStack equ $ - PM_STACK -1
;END of STACK	;ring3的堆栈段
[SECTION .s3]
ALIGN 32
[BITS 32]
PM_STACK3:times 512 db 0
TopOfStack3 equ $ - PM_STACK3 -1
;END of STACK	;全局的堆栈段
[SECTION .tss]
ALIGN 32
[BITS 32]
PM_TSS:DD	0			; BackDD	TopOfStack 		; 0 级堆栈DD	SelectoerSTACK		; DD	0			; 1 级堆栈DD	0			; DD	0			; 2 级堆栈DD	0			; DD	0			; CR3DD	0			; EIPDD	0			; EFLAGSDD	0			; EAXDD	0			; ECXDD	0			; EDXDD	0			; EBXDD	0			; ESPDD	0			; EBPDD	0			; ESIDD	0			; EDIDD	0			; ESDD	0			; CSDD	0			; SSDD	0			; DSDD	0			; FSDD	0			; GSDD	0			; LDTDW	0			; 调试陷阱标志DW	$- PM_TSS+2 	; I/O位图基址DB	0ffh			; I/O位图结束标志
TSSLen equ $ - PM_TSS -1
;END of STACK	[SECTION .s16]
[BITS 16]
PM_BEGIN:mov ax,csmov ds,axmov es,axmov ss,axmov sp,0100h;初始化32位的代码段xor eax,eaxmov ax,csshl eax,4add eax,PM_SEG_CODE32mov word[PM_DESC_CODE32+2],axshr eax,16mov byte [PM_DESC_CODE32+4],almov byte [PM_DESC_CODE32+7],ah;初始化32位的数据段xor eax,eaxmov ax,dsshl eax,4add eax,PM_DATAmov word[PM_DESC_DATA+2],axshr eax,16mov byte [PM_DESC_DATA+4],almov byte [PM_DESC_DATA+7],ah;初始化32位的stack段xor eax,eaxmov ax,dsshl eax,4add eax,PM_STACKmov word[PM_DESC_STACK+2],axshr eax,16mov byte [PM_DESC_STACK+4],almov byte [PM_DESC_STACK+7],ah;初始化32位的LDT,得把省局注册到全国xor eax,eaxmov ax,dsshl eax,4add eax,LABEL_LDT					;//to do mov word[LABEL_DESC_LDT+2],axshr eax,16mov byte [LABEL_DESC_LDT+4],almov byte [LABEL_DESC_LDT+7],ah;根据GDT,把LDT管理的具体公司初始化,得把省局注册到全国xor eax,eaxmov ax,dsshl eax,4add eax, LABEL_CODE_A					;//to do mov word[LABEL_LDT_DESC_CODEA+2],axshr eax,16mov byte [LABEL_LDT_DESC_CODEA +4],almov byte [LABEL_LDT_DESC_CODEA +7],ah;为调用门的运行,我们将目标代码段跳转xor eax,eaxmov ax,csshl eax,4add eax, PM_SEG_CODE_DEST					;//to do mov word[PM_DESC_CODE_DEST+2],axshr eax,16mov byte [PM_DESC_CODE_DEST+4],almov byte [PM_DESC_CODE_DEST+7],ah;初始化Ring3xor eax,eaxmov ax,dsshl eax,4add eax, PM_CODE_RING3				;//to do mov word[PM_DESC_CODE_RING3+2],axshr eax,16mov byte [PM_DESC_CODE_RING3+4],almov byte [PM_DESC_CODE_RING3+7],ah;初始化TSSxor eax,eaxmov ax,dsshl eax,4add eax, PM_TSS			;//to do mov word[PM_DESC_TSS+2],axshr eax,16mov byte [PM_DESC_TSS+4],almov byte [PM_DESC_TSS+7],ah;加载GDTRxor eax,eaxmov ax,dsshl eax,4add eax,PM_GDTmov dword [GdtPtr +2 ],eaxlgdt [GdtPtr];A20cliin al,92hor al,00000010bout 92h,al;切换到保护模式mov eax,cr0or eax,1mov cr0,eaxjmp dword SelectoerCode32:0[SECTION .s32]	;32位的代码段
[BITS 32]
PM_SEG_CODE32 :mov ax,SelectoerDATA	;通过数据段的选择子放入ds寄存器,就可以用段+偏移进行寻址mov ds,axmov ax,SelectoerTEST	;通过测试段的选择子放入es寄存器,就可以用段+偏移进行寻址mov es,axmov ax,SelectoerVideomov gs,axmov ax,SelectoerSTACKmov ss,axmov esp,TopOfStackmov ah,0Chxor esi,esixor edi,edimov esi,OffsetPMessagemov edi,(80*10 +0) *2cld.1:lodsbtest al,aljz .2mov [gs:edi],axadd edi,2jmp .1.2: ;显示完毕;Load LDT;mov ax,SelectoerLDT	;	SelectoerLDT=> GDT;lldt ax;jmp SelectoerLDTCodeA:0;------------gate 1;call SelectorCallGateTest:0;jmp $;------------end of gate 1;beging gate 2:有级别转换;load TSSmov ax, SelctorTSSltr axpush SelctorStack3push TopOfStack3push SelctorCodeRing3push 0retfSegCode32Len equ $ - PM_SEG_CODE32;LDT
[SECTION .ldt]
ALIGN 32
LABEL_LDT:
;									段基址,段界限,		属性
LABEL_LDT_DESC_CODEA:	Descriptor		0,		CodeALen-1,		DA_C+DA_32LDTLen	equ $ - LABEL_LDT
;选择子
SelectoerLDTCodeA	equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL[SECTION .la]
ALIGN 32
[BITS 32]
LABEL_CODE_A:mov  ax,SelectoerVideomov gs,axmov edi, (80*5 +0) *2mov ah, 0Chmov al, 'D'mov [gs:edi],axjmp $
CodeALen  equ $ - LABEL_CODE_A
;END of 任务段[SECTION .sdest]
ALIGN 32
[BITS 32]
PM_SEG_CODE_DEST:mov  ax,SelectoerVideomov gs,axmov edi, (80*18 +0) *2mov ah, 0Chmov al, 'G'mov [gs:edi],ax;Load LDTmov ax, SelectoerLDTlldt axjmp SelectoerLDTCodeA:0;retfSegCodeDestLen equ $ - PM_SEG_CODE_DEST
;END of 调用门;ring 3
[SECTION .ring3]
ALIGN 32
[BITS 32]
PM_CODE_RING3:mov  ax,SelectoerVideomov gs,axmov edi, (80*12 +0) *2mov ah, 0Chmov al, '3'mov [gs:edi],axcall SelectorCallGateTest:0 ;用调用门完成特权级切换jmp $SegCodeRing3Len equ $ - PM_CODE_RING3
;END of 调用门

 从ring0到ring3特权级切换实战

linux内核只用了ring0和riing3

时钟中断:进程时间片轮转的基础

任务门 陷阱门 调用门 中断门

学习1:如何操作硬件

学习2:保护模式下如何访问IO

这篇关于自制OS3-1到3-16==保护模式(GDT、选择子、寄存器)、多任务由来(LDT)、内核态和用户态ring0和ring3、特权级切换(TSS-CPL-DPL-RPL-门)、时钟中断、保护模式中断编程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/401435

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

Go Playground 在线编程环境

For all examples in this and the next chapter, we will use Go Playground. Go Playground represents a web service that can run programs written in Go. It can be opened in a web browser using the follow

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试