歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

Linux內核系列—操作系統開發之進入32位保護模式

源碼如下:

; ==========================================
; pmtest1.asm
; 編譯方法:nasm pmtest1.asm -o pmtest1.bin
; ==========================================

%include	"pm.inc"	; 常量, 宏, 以及一些說明

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   ; 非一致代碼段
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,
					; 並跳轉到 SelectorCode32:0  處
; END of [SECTION .s16]


[SECTION .s32]; 32 位代碼段. 由實模式跳入.
[BITS	32]

LABEL_SEG_CODE32:
	mov	ax, SelectorVideo
	mov	gs, ax			; 視頻段選擇子(目的)

	mov	edi, (80 * 11 + 79) * 2	; 屏幕第 11 行, 第 79 列。
	mov	ah, 0Ch			; 0000: 黑底    1100: 紅字
	mov	al, 'P'
	mov	[gs:edi], ax

	; 到此停止
	jmp	$

SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]

運行結果如下,在屏幕最右邊有一個紅色的P:

源碼解析:

1.首先程序跳轉至LABEL_BEGIN處,jmp LABEL_BEGIN。將ds、es、ss段寄存器全部初始化為當前代碼段。

2.初始化32位代碼段描述符

在實模式下,也就是8086的16位的CPU的尋址方式是段x16+偏移,而在保護模式下,段寄存器值變成了一個索引,這個索引指向段描述符,也就是GDT中對應的那個描述符,描述符中包含了最終的基地址。

 38-41行表示將當前程序代碼段左移4位也就是x16,再加上LABEL_SEG_CODE32的偏移地址,最終形成LABEL_SEG_CODE32的物理地址,42-45表示把此物理地址加載到段描述符對應的段基址位置,段描述符格式如下圖所示

段基地址0-15位也就是LABEL_DESC_CODE32 + 2地址處,將16位的ax傳入,第43行向右移動16位表示已經傳入的16位消除,然後將剩余的兩個8位高低寄存器值傳入對應的段基地址處。

Descriptor是在pm.inc中定義的宏,如下圖所示,表示的就是段描述符的格式:

3.加載GDTR,GDRT的結構圖如下所示

GDTR是唯一的一個指向段描述符表的寄存器,48-55行就是把段描述符表的基地址加載到GDTR寄存器當中。48行清除eax值,49-52是把LABEL_GDT的物理地址加載到20行的GdtPtr的雙字處,也就是GDT基地址。55行把GdtPtr地址的內容加載到GDTR,雙字節dw對應的是16位界限,雙字dd對應的是32位基地址。

4.關中斷和打開A20

保護模式下中斷處理機制和實模式不同,所以先關閉中斷,指令為cli。打開A20為歷史原因防止偏移超出最大值時回滾。58-63行

5.切換到保護模式

只要把寄存器cr0的第0位置為1就行了。66-68行

6.真正進入保護模式

jmp dword SelectorCode32:0,這一行是真正進入保護模式的代碼,SelectorCode32是個段選擇子,段選擇子的作用是找到對應的段描述符從而找到對應的段基地址。段選擇子的格式如下圖所示

從位3開始表示為段描述符表的索引,這句代碼執行完之後就會把描述符LABEL_DESC_CODE32中的段基址也就是LABEL_SEG_CODE32加載到cs。

dword的作用是因為當前還是16位的代碼,如果沒有dword,SelectorCode32:0的偏移地址如果超出16位那麼只會截取16位。

 

到此已經完全進入32位保護模式了。

Copyright © Linux教程網 All Rights Reserved