操作系统实现之保护模式

Posted 隐无影

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了操作系统实现之保护模式相关的知识,希望对你有一定的参考价值。

保护模式主要是为了防止用户程序故意使坏.而且用户所引用的地址都是指向真实的物理地址.跟内核属于同特权级.不利于安全.另外保护模式将是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


-----------------mbr.S的定义(改动只有就是从扇区读取4个扇区.而不是1个.因为loader.bin扩充了)----------------------------

%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 $
显示效果如图


<bochs:3> creg
CR0=0x60000011: pg CD NW ac wp ne ET ts em mp PE

从上可以看出 PE位已经置为1了


最后loader.S定义了 

mov byte [gs:160],'P'

最终显示效果.如果成功进入了保护模式.就会出现P这个字符





以上是关于操作系统实现之保护模式的主要内容,如果未能解决你的问题,请参考以下文章

内核保护模式之分段机制

第十一课 实模式到保护模式 中

由表及里学 ProjectReactor 之上卷

学习逆向知识之用于游戏外挂的实现.第三讲,通过游戏外挂.分析红色警戒金钱基址.以及确定基址小技巧.

[保护模式]段寄存器

我是如何学习写一个操作系统:操作系统的启动之保护模式