保护模式主要是为了防止用户程序故意使坏.而且用户所引用的地址都是指向真实的物理地址.跟内核属于同特权级.不利于安全.另外保护模式将是32/64位.大家所说的实模式一般指的是32位的cpu在16位模式下的状态.不是指的是16位cpu.本来没有实模式这个说法.不过后来出现保护模式.于是就出现了实模式说法
保护模式下的改变
通用寄存器扩展为32位
段寄存器保存的不再是段基址,而是段选择子(段描述符的索引)
段基址保存在段描述符中,而段描述符保存在全局描述符表(GDT)中. 而全局描述符表保存在专门的寄存器gdtr,加载时用lgdt指令进行加载
全局描述符表(本简单kernel没用局部描述符表)所存储的描述符格式
之所以如此..混乱.完全是由于历史兼容原因
再来看看段选择子(保存在段寄存器中)的定义
保护模式下的选址方式发生了改变
一开始从段寄存器获取段选择子.通过段选择子的索引位*8(乘以8是因为.一个段描述符是8个字节)+gdtr保存的全局描述符表基址. 得到段基址
然后加上偏移寄存器.得到实际地址
如何进入保护模式:
1.打开A20(历史遗留问题.第21根地址线)
2.加载dgt
3.将cr0的pe位定义为1(表示进入保护模式)
-------------------------配置文件boot.inc的定义----------------
;------------- LOADER和KERNEL ----------
loader_base_addr equ 0X900 ;加载位置
loader_stack_top equ loader_base_addr ;栈指向的位置
loader_start_sector equ 0X2 ;loader存储到第二个扇区
;-------------- GDT描述符属性 -----------
desc_g_4k equ 1_00000000000000000000000B ;描述符的g位为4k粒度
desc_d_32 equ 1_0000000000000000000000B ;表示是32位还是16位
desc_l equ 0_000000000000000000000B ; 64位代码标记,此处标记为0便可。
desc_avl equ 0_00000000000000000000B ; CPU不用此位,暂置为0
desc_limit_code2 equ 1111_0000000000000000B ;段的第二部分界限
desc_limit_data2 equ desc_limit_code2
desc_limit_video2 equ 0000_000000000000000B ;
desc_p equ 1_000000000000000B
desc_dpl_0 equ 00_0000000000000B ;段描述符特权级
desc_dpl_1 equ 01_0000000000000B
desc_dpl_2 equ 10_0000000000000B
desc_dpl_3 equ 11_0000000000000B
desc_s_code equ 1_000000000000B
desc_s_data equ desc_s_code
desc_s_SYS equ 0_000000000000B
desc_type_code equ 1000_00000000B ;X=1,C=0,R=0,A=0 代码段是可执行的,非依从的,不可读的,已访问位A清0.
desc_type_data equ 0010_00000000B ;X=0,E=0,W=1,A=0 数据段是不可执行的,向上扩展的,可写的,已访问位A清0.
;段的24位
desc_code_high4 equ (0X00 << 24) + desc_g_4k + desc_d_32 + desc_l + desc_avl + desc_limit_code2 + desc_p + desc_dpl_0 + desc_s_code + desc_type_code + 0X00
desc_data_high4 equ (0X00 << 24) + desc_g_4k + desc_d_32 + desc_l + desc_avl + desc_limit_data2 + desc_p + desc_dpl_0 + desc_s_data + desc_type_data + 0X00
desc_video_high4 equ (0X00 << 24) + desc_g_4k + desc_d_32 + desc_l + desc_avl + desc_limit_video2 + desc_p + desc_dpl_0 + desc_s_data + desc_type_data + 0X0B
;-------------- 选择子属性 ---------------
rpl0 equ 00B
rpl1 equ 01B
rpl2 equ 10B
rpl3 equ 11B
ti_gdt equ 000B
ti_ldt equ 100B
;------------- PROGRAM TYPE 定义 --------------
pt_null equ 0
%include "boot.inc" ;保存有关加载程序位置.源跟目的
section mbr vstart=0x7c00
jmp near start
message db 'ZYW_OS start'
start:
;ds:si指向数据源.es:di指向显存.
mov sp,0x7c00 ;栈顶,栈成长的方向向上
mov ax,0xb800
mov es,ax ;es指向显存位置
; 清屏(摘自百度)
;利用0x06号功能,上卷全部行,则可清屏。
; -----------------------------------------------------------
;INT 0x10 功能号:0x06 功能描述:上卷窗口
;------------------------------------------------------
;输入:
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
mov ax,0x0600
mov bx,0x0700
mov cx,0 ; 左上角: (0, 0)
mov dx,0x184f ; 右下角(80,25),
int 10h
mov si,message
mov di,0
mov cx,start-message
zyw:
movsb
mov byte [es:di],0xA4 ;控制背景跟颜色
inc di
loop zyw
mov eax,loader_start_sector ;eax保存加载程序的位置
mov bx,loader_base_addr ;bx保存加载到哪个区域
mov cx,4 ;cx保存加载几个扇区(从eax开始)
call read_disk
jmp loader_base_addr
read_disk:
push eax
mov dx,0x1f2 ;硬盘端口0x1f2 ,设置要读取多少数据
mov al,cl ;这里表示读取一个
out dx,al
pop eax
;将lba地址存入0x1f3-0x1f6处
mov dx,0x1f3
out dx,al
push cx
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
shr eax,cl
mov dx,0x1f5
out dx,al
shr eax,cl
and al,0x0f
or al,0xe0
mov dx,0x1f6
out dx,al
pop cx
;向0x1f7写入命令
mov dx,0x1f7
mov al,0x20
out dx,al
noready:
nop
in al,dx
and al,0x88
cmp al,0x08
jnz noready
;开始从0x1f0端口读取数据
mov ax,cx
mov dx,256
mul dx
mov cx,ax
mov dx,0x1f0
readdata:
in ax,dx
mov [bx],ax
add bx,2
loop readdata
ret
times 510-($-$$) db 0
db 0x55,0xaa
-----------------loader的定义-------------------------------
%include "boot.inc"
section loader vstart=loader_base_addr
jmp start
;------------全局描述符表的定义
gdt_base:
dd 0x00000000 ;全局描述表.第一个描述符要为空
dd 0x00000000
code_base:
dd 0x0000FFFF
dd desc_code_high4
data_stack_desc:
dd 0x0000ffff
dd desc_data_high4
video_desc:
dd 0x80000007 ;段界限 limit=0x7fff.基址位于0xb8000
dd desc_video_high4
;---------GDT的属性
gdt_size equ $-gdt_base ;GDT大小
gdt_limit equ gdt_size ;GDT限制
time 60 dq 0 ;预留60个描述符的空位置
;----------定义段选择子-------------
selector_code equ (0x0001<<3)+ti_gdt+rpl0
selector_data equ (0x0002<<3)+ti_gdt+rpl0
selector_video eque(0x0003<<3)+ti_gdt+rpl0
;定义gdt的指针,前2个字节为gdt界限,后4个字节为gdt起始地址
gdt_ptr dw gdt_limit
dd gdt_base
;-----进入保护模式----------
in al,0x92
or al,0000_0010B
out 0x92,al
;----------加载GDT----------
lgdt [gdt_ptr]
;---------cr0第0位置为1,表示打开保护模式-----------
mov eax,cr0
or eax,0x00000001
mov cr0,eax
jmp dword selector_code:p_mode_start:
[bits 32]
p_mode_start:
mov ax,selector_data
mov ds,ax
mov es,ax
mov ss,ax
mov esp,loader_stack_top
mov ax,selector_video
mov gs,ax
mov byte [gs:160],'P'
jmp $
显示效果如图
CR0=0x60000011: pg CD NW ac wp ne ET ts em mp PE
从上可以看出 PE位已经置为1了
最后loader.S定义了
mov byte [gs:160],'P'
最终显示效果.如果成功进入了保护模式.就会出现P这个字符