1. 程式人生 > >作業系統實現之保護模式

作業系統實現之保護模式

保護模式主要是為了防止使用者程式故意使壞.而且使用者所引用的地址都是指向真實的實體地址.跟核心屬於同特權級.不利於安全.另外保護模式將是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這個字元