保护模式
前面我们可以任意的访问内存,寄存器在段式内存中,其实没有界限,操作系统和用户程序实际上做不到内存的隔离。
用户程序所访问的逻辑地址,实际上就是物理地址。
用户程序可以随意修改段基址(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 3
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 ;
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 ; 非一致代码段, 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 * 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 3
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
%endmacro
org 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,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,PM_SEG_CODE32
mov word[PM_DESC_CODE32+2],ax
shr eax,16
mov byte [PM_DESC_CODE32+4],al
mov byte [PM_DESC_CODE32+7],ah
;初始化32位的数据段
xor eax,eax
mov ax,ds
shl eax,4
add eax,PM_DATA
mov word[PM_DESC_DATA+2],ax
shr eax,16
mov byte [PM_DESC_DATA+4],al
mov byte [PM_DESC_DATA+7],ah
;初始化32位的stack段
xor eax,eax
mov ax,ds
shl eax,4
add eax,PM_STACK
mov word[PM_DESC_STACK+2],ax
shr eax,16
mov byte [PM_DESC_STACK+4],al
mov byte [PM_DESC_STACK+7],ah
;加载GDTR
xor eax,eax
mov ax,ds
shl eax,4
add eax,PM_GDT
mov dword [GdtPtr +2 ],eax
lgdt [GdtPtr]
;A20
cli
in al,92h
or al,00000010b
out 92h,al
;切换到保护模式
mov eax,cr0
or eax,1
mov cr0,eax
jmp dword SelectoerCode32:0
[SECTION .s32] ;32位的代码段
[BITS 32]
PM_SEG_CODE32 :
mov ax,SelectoerDATA ;通过数据段的选择子放入ds寄存器,就可以用段+偏移进行寻址
mov ds,ax
mov ax,SelectoerTEST ;通过测试段的选择子放入es寄存器,就可以用段+偏移进行寻址
mov es,ax
mov ax,SelectoerVideo
mov gs,ax
mov ax,SelectoerSTACK
mov ss,ax
mov esp,TopOfStack
mov ah,0Ch
xor esi,esi
xor edi,edi
mov esi,OffsetPMessage
mov edi,(80*10 +0) *2
cld
.1:
lodsb
test al,al
jz .2
mov [gs:edi],ax
add edi,2
jmp .1
.2: ;显示完毕
;测试段的寻址
mov ax, 'A'
mov [es:0],ax
mov ax,SelectoerVideo
mov gs,ax
mov edi,(80*15 +0) *2
mov ah,0Ch
mov al,[es:0]
mov [gs:edi],ax
jmp $
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 3
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
%endmacro
org 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
LABEL_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,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,PM_SEG_CODE32
mov word[PM_DESC_CODE32+2],ax
shr eax,16
mov byte [PM_DESC_CODE32+4],al
mov byte [PM_DESC_CODE32+7],ah
;初始化32位的数据段
xor eax,eax
mov ax,ds
shl eax,4
add eax,PM_DATA
mov word[PM_DESC_DATA+2],ax
shr eax,16
mov byte [PM_DESC_DATA+4],al
mov byte [PM_DESC_DATA+7],ah
;初始化32位的stack段
xor eax,eax
mov ax,ds
shl eax,4
add eax,PM_STACK
mov word[PM_DESC_STACK+2],ax
shr eax,16
mov byte [PM_DESC_STACK+4],al
mov byte [PM_DESC_STACK+7],ah
;初始化32位的LDT,得把省局注册到全国
xor eax,eax
mov ax,ds
shl eax,4
add eax,LABEL_LDT ;//to do
mov word[LABEL_DESC_LDT+2],ax
shr eax,16
mov byte [LABEL_DESC_LDT+4],al
mov byte [LABEL_DESC_LDT+7],ah
;根据GDT,把LDT管理的具体公司初始化,得把省局注册到全国
xor eax,eax
mov ax,ds
shl eax,4
add eax, LABEL_CODE_A ;//to do
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
;加载GDTR
xor eax,eax
mov ax,ds
shl eax,4
add eax,PM_GDT
mov dword [GdtPtr +2 ],eax
lgdt [GdtPtr]
;A20
cli
in al,92h
or al,00000010b
out 92h,al
;切换到保护模式
mov eax,cr0
or eax,1
mov cr0,eax
jmp dword SelectoerCode32:0
[SECTION .s32] ;32位的代码段
[BITS 32]
PM_SEG_CODE32 :
mov ax,SelectoerDATA ;通过数据段的选择子放入ds寄存器,就可以用段+偏移进行寻址
mov ds,ax
mov ax,SelectoerTEST ;通过测试段的选择子放入es寄存器,就可以用段+偏移进行寻址
mov es,ax
mov ax,SelectoerVideo
mov gs,ax
mov ax,SelectoerSTACK
mov ss,ax
mov esp,TopOfStack
mov ah,0Ch
xor esi,esi
xor edi,edi
mov esi,OffsetPMessage
mov edi,(80*10 +0) *2
cld
.1:
lodsb
test al,al
jz .2
mov [gs:edi],ax
add edi,2
jmp .1
.2: ;显示完毕
;Load LDT
mov ax,SelectoerLDT ; SelectoerLDT=> GDT
lldt ax
jmp SelectoerLDTCodeA:0
SegCode32Len equ $ - PM_SEG_CODE32
;LDT
[SECTION .ldt]
ALIGN 32
LABEL_LDT:
; 段基址,段界限, 属性
LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen-1, DA_C+DA_32
LDTLen equ $ - LABEL_LDT
;选择子
SelectoerLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL
[SECTION .la]
ALIGN 32
[BITS 32]
LABEL_CODE_A:
mov ax,SelectoerVideo
mov gs,ax
mov edi, (80*5 +0) *2
mov ah, 0Ch
mov al, 'D'
mov [gs:edi],ax
jmp $
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 3
DA_DPL3 EQU 60h
DA_386TSS EQU 89h
%macro Descriptor 3
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
%endmacro
org 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_DPL3
LABEL_DESC_LDT: Descriptor 0, LDTLen -1, DA_LDT
PM_DESC_CODE_DEST: Descriptor 0, SegCodeDestLen -1,DA_C+DA_32
PM_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_386TSS
PM_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_RPL3
SelctorCodeRing3 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 ; Back
DD TopOfStack ; 0 级堆栈
DD SelectoerSTACK ;
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 $- 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,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,PM_SEG_CODE32
mov word[PM_DESC_CODE32+2],ax
shr eax,16
mov byte [PM_DESC_CODE32+4],al
mov byte [PM_DESC_CODE32+7],ah
;初始化32位的数据段
xor eax,eax
mov ax,ds
shl eax,4
add eax,PM_DATA
mov word[PM_DESC_DATA+2],ax
shr eax,16
mov byte [PM_DESC_DATA+4],al
mov byte [PM_DESC_DATA+7],ah
;初始化32位的stack段
xor eax,eax
mov ax,ds
shl eax,4
add eax,PM_STACK
mov word[PM_DESC_STACK+2],ax
shr eax,16
mov byte [PM_DESC_STACK+4],al
mov byte [PM_DESC_STACK+7],ah
;初始化32位的LDT,得把省局注册到全国
xor eax,eax
mov ax,ds
shl eax,4
add eax,LABEL_LDT ;//to do
mov word[LABEL_DESC_LDT+2],ax
shr eax,16
mov byte [LABEL_DESC_LDT+4],al
mov byte [LABEL_DESC_LDT+7],ah
;根据GDT,把LDT管理的具体公司初始化,得把省局注册到全国
xor eax,eax
mov ax,ds
shl eax,4
add eax, LABEL_CODE_A ;//to do
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
;为调用门的运行,我们将目标代码段跳转
xor eax,eax
mov ax,cs
shl eax,4
add eax, PM_SEG_CODE_DEST ;//to do
mov word[PM_DESC_CODE_DEST+2],ax
shr eax,16
mov byte [PM_DESC_CODE_DEST+4],al
mov byte [PM_DESC_CODE_DEST+7],ah
;初始化Ring3
xor eax,eax
mov ax,ds
shl eax,4
add eax, PM_CODE_RING3 ;//to do
mov word[PM_DESC_CODE_RING3+2],ax
shr eax,16
mov byte [PM_DESC_CODE_RING3+4],al
mov byte [PM_DESC_CODE_RING3+7],ah
;初始化TSS
xor eax,eax
mov ax,ds
shl eax,4
add eax, PM_TSS ;//to do
mov word[PM_DESC_TSS+2],ax
shr eax,16
mov byte [PM_DESC_TSS+4],al
mov byte [PM_DESC_TSS+7],ah
;加载GDTR
xor eax,eax
mov ax,ds
shl eax,4
add eax,PM_GDT
mov dword [GdtPtr +2 ],eax
lgdt [GdtPtr]
;A20
cli
in al,92h
or al,00000010b
out 92h,al
;切换到保护模式
mov eax,cr0
or eax,1
mov cr0,eax
jmp dword SelectoerCode32:0
[SECTION .s32] ;32位的代码段
[BITS 32]
PM_SEG_CODE32 :
mov ax,SelectoerDATA ;通过数据段的选择子放入ds寄存器,就可以用段+偏移进行寻址
mov ds,ax
mov ax,SelectoerTEST ;通过测试段的选择子放入es寄存器,就可以用段+偏移进行寻址
mov es,ax
mov ax,SelectoerVideo
mov gs,ax
mov ax,SelectoerSTACK
mov ss,ax
mov esp,TopOfStack
mov ah,0Ch
xor esi,esi
xor edi,edi
mov esi,OffsetPMessage
mov edi,(80*10 +0) *2
cld
.1:
lodsb
test al,al
jz .2
mov [gs:edi],ax
add edi,2
jmp .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 TSS
mov ax, SelctorTSS
ltr ax
push SelctorStack3
push TopOfStack3
push SelctorCodeRing3
push 0
retf
SegCode32Len equ $ - PM_SEG_CODE32
;LDT
[SECTION .ldt]
ALIGN 32
LABEL_LDT:
; 段基址,段界限, 属性
LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen-1, DA_C+DA_32
LDTLen equ $ - LABEL_LDT
;选择子
SelectoerLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL
[SECTION .la]
ALIGN 32
[BITS 32]
LABEL_CODE_A:
mov ax,SelectoerVideo
mov gs,ax
mov edi, (80*5 +0) *2
mov ah, 0Ch
mov al, 'D'
mov [gs:edi],ax
jmp $
CodeALen equ $ - LABEL_CODE_A
;END of 任务段
[SECTION .sdest]
ALIGN 32
[BITS 32]
PM_SEG_CODE_DEST:
mov ax,SelectoerVideo
mov gs,ax
mov edi, (80*18 +0) *2
mov ah, 0Ch
mov al, 'G'
mov [gs:edi],ax
;Load LDT
mov ax, SelectoerLDT
lldt ax
jmp SelectoerLDTCodeA:0
;retf
SegCodeDestLen equ $ - PM_SEG_CODE_DEST
;END of 调用门
;ring 3
[SECTION .ring3]
ALIGN 32
[BITS 32]
PM_CODE_RING3:
mov ax,SelectoerVideo
mov gs,ax
mov edi, (80*12 +0) *2
mov ah, 0Ch
mov al, '3'
mov [gs:edi],ax
call SelectorCallGateTest:0 ;用调用门完成特权级切换
jmp $
SegCodeRing3Len equ $ - PM_CODE_RING3
;END of 调用门
从ring0到ring3特权级切换实战
linux内核只用了ring0和riing3
时钟中断:进程时间片轮转的基础
任务门 陷阱门 调用门 中断门
学习1:如何操作硬件
学习2:保护模式下如何访问IO