2009年3月31日 星期二

●中斷、保護模式、分頁‧‧真是夠ㄌ___PART2


以下ㄉCODE是截取"自己動手寫作業系統",其中還有其他範例您就自己捨得花錢買一本吧。買這種書不會讓你看ㄌ一兩年後會過時ㄉ感覺 ,所以買ㄅ。 用其他顏色是我ㄉ註解。
; ==========================================
; pmtest1.asm
; 编译方法:nasm pmtest1.asm -o pmtest1.com
; ==========================================
%include "pm.inc" ; 常量, 宏, 以及一些说明
org 0100h
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 结束
;以上3個描述子請參閱pm.inc你才能真的懂。第一ㄍ為空描述子,一定要ㄉ,什麼?誰規定ㄉ,當然是intel
GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - 1 ; GDT界限;由這ㄍ界定的定義dw就知道gdt表最多可以容納8192個描述子:dw=2ㄉ16次方=65536/8=8192
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;因為是com檔所以是100h

; 初始化 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]
;lgdt作ㄌ什麼事ㄚ(就是將GdtPtr這個標籤所指ㄉ6ㄍbytesㄉ資料載入到gdtr暫存器;待進入保護模式,段式保護機制就生效ㄌ,從此過這和以前一樣ㄉ日子,只要和ds es fs gs打交道就可以ㄌ。
; 关中断
cli
; 打开地址线A20;a20位址線問題我已經有另闢文書說明請自行參閱
in al, 92h or al, 00000010b
out 92h, al
; 准备切换到保护模式;短短3行你就進入保護模式。簡單ㄅ
mov eax, cr0 or eax, 1 mov cr0, eax
; 真正进入保护模式
jmp dword SelectorCode32:0
; 执行以上這一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0處;請仔細品嘗這ㄍ註解1000遍,直到你真的懂為止。
; END of [SECTION .s16]
[SECTION .s32]
; 32 位代码段. 由实模式跳入.
[BITS 32] LABEL_SEG_CODE32: mov ax, SelectorVideo mov gs, ax
; 视频段选择子(目的)
mov edi, (80 * 10 + 0) * 2
; 屏幕第 10 行, 第 0 列。 mov ah, 0Ch ; 0000: 黑底 1100: 红字 mov al, 'P' mov [gs:edi], ax
; 到此停止
jmp $
SegCode32Len equ $ - LABEL_SEG_CODE32 ;
END of [SECTION .s32]
;==========================================
; pm.inc 有些描述必須參考上圖
;==========================================
(1) P: 存在(Present)位。 ; P=1 表示描述符对地址转换是有效的,或者说该描述符所描述的段存在,即在内存中; ; P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常。 ; ; (2) DPL: 表示描述符特权级(Descriptor Privilege level),共2位。它规定了所描述段的特权级,用于特权检查,以决定对该段能否访问。 ; ; (3) S: 说明描述符的类型。 ; 对于存储段描述符而言,S=1,以区别与系统段描述符和门描述符(S=0)。 ; ; (4) TYPE: 说明存储段描述符所描述的存储段的具体属性。 ; ; ; 数据段类型 类型值 说明 ;
; 0 只读 ; 1 只读、已访问 ; 2 读/写 ; 3 读/写、已访问 ; 4 只读、向下扩展 ; 5 只读、向下扩展、已访问 ; 6 读/写、向下扩展 ; 7 读/写、向下扩展、已访问 ; ; ; 类型值 说明 ; 代码段类型 ; 8 只执行 ; 9 只执行、已访问 ; A 执行/读 ; B 执行/读、已访问 ; C 只执行、一致码段 ; D 只执行、一致码段、已访问 ; E 执行/读、一致码段 ; F 执行/读、一致码段、已访问 ; ; ; 系统段类型 类型编码 说明 ;; 0 <未定义>
; 1 可用286TSS
; 2 LDT
; 3 忙的286TSS
; 4 286调用门
; 5 任务门
; 6 286中断门
; 7 286陷阱门
; 8 未定义
; 9 可用386TSS
; A <未定义>
; B 忙的386TSS
; C 386调用门
; D <未定义>
; E 386中断门
; F 386陷阱门
;
; (5) G: 段界限粒度(Granularity)位。
; G=0 表示界限粒度为字节;
; G=1 表示界限粒度为4K 字节。
; 注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。
;
; (6) D: D位是一个很特殊的位,在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同。
; ⑴ 在描述可执行段的描述符中,D位决定了指令使用的地址及操作数所默认的大小。
; ① D=1表示默认情况下指令使用32位地址及32位或8位操作数,这样的代码段也称为32位代码段;
; ② D=0 表示默认情况下,使用16位地址及16位或8位操作数,这样的代码段也称为16位代码段,它与80286兼容。可以使用地址大小前缀和操作数大小前缀分别改变默认的地址或操作数的大小。
; ⑵ 在向下扩展数据段的描述符中,D位决定段的上部边界。
; ① D=1表示段的上部界限为4G;
; ② D=0表示段的上部界限为64K,这是为了与80286兼容。
; ⑶ 在描述由SS寄存器寻址的段描述符中,D位决定隐式的堆栈访问指令(如PUSH和POP指令)使用何种堆栈指针寄存器。
; ① D=1表示使用32位堆栈指针寄存器ESP;
; ② D=0表示使用16位堆栈指针寄存器SP,这与80286兼容。
;
; (7) AVL: 软件可利用位。80386对该位的使用未左规定,Intel公司也保证今后开发生产的处理器只要与80386兼容,就不会对该位的使用做任何定义或规定。
;----------------------------------------------------------------------------
; 描述符类型值说明
; 其中:
; DA_ : Descriptor Attribute
; D : 数据段
; C : 代码段
; S : 系统段
; R : 只读
; RW : 读写
; A : 已访问
; 其它 : 可按照字面意思理解
;----------------------------------------------------------------------------
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
;----------------------------------------------------------------------------
; 存储段描述符类型值说明
;----------------------------------------------------------------------------
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_LDT EQU 82h ; 局部描述符表段类型值
DA_TaskGate EQU 85h ; 任务门类型值
DA_386TSS EQU 89h ; 可用 386 任务状态段类型值
DA_386CGate EQU 8Ch ; 386 调用门类型值
DA_386IGate EQU 8Eh ; 386 中断门类型值
DA_386TGate EQU 8Fh ; 386 陷阱门类型值

; RPL(Requested Privilege Level): 请求特权级,用于特权检查。
;
; TI(Table Indicator): 引用描述符表指示位
; TI=0 指示从全局描述符表GDT中读取描述符;
; TI=1 指示从局部描述符表LDT中读取描述符。
;----------------------------------------------------------------------------
; 选择子类型值说明
; 其中:
; SA_ : Selector Attribute

SA_RPL0 EQU 0 ; ┓
SA_RPL1 EQU 1 ; ┣ RPL
SA_RPL2 EQU 2 ; ┃
SA_RPL3 EQU 3 ; ┛

SA_TIG EQU 0 ; ┓TI
SA_TIL EQU 4 ; ┛

; 描述符
; usage: Descriptor Base, Limit, Attr
; Base: dd
; Limit: dd (low 20 bits available)
; Attr: dw (lower 4 bits of higher byte are always 0)
%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 ; 共 8 字节

; usage: Gate Selector, Offset, DCount, Attr
; Selector: dw
; Offset: dd
; DCount: db
; Attr: db
%macro Gate 4
dw (%2 & 0FFFFh) ; 偏移 1 (2 字节)
dw %1 ; 选择子 (2 字节)
dw (%3 & 1Fh) | ((%4 <<>> 16) & 0FFFFh) ; 偏移 2 (2 字节)
%endmacro ; 共 8 字节
/*****************************************************/
以下我們來看memtest86+如何進入保護模式:請自行參閱head.S,用/****/隔開表示代碼為不連續性。
#define RSTART startup_32
/* Fixup the gdt_descr */
leal gdt@GOTOFF(%ebx), %eax
movl %eax, 2 + gdt_descr@GOTOFF(%ebx)
/*****************************************************/
/* Load the global descriptor table */
addr32 lgdt gdt_descr - RSTART //
addr32這ㄍ標紀是什麼意思我還沒查到,但可以確定ㄉ一點是現在早已在保護模式,因為setup.S時就切進去ㄌ。
/*****************************************************/
gdt:
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x0000000000000000 /* not used */
.quad 0x00cf9a000000ffff /* 0x10 main 4gb code at 0x000000 */
.quad 0x00cf92000000ffff /* 0x18 main 4gb data at 0x000000 */

.word 0xFFFF # 16bit 64KB - (0x10000*1 = 64KB)
.word 0 # base address = SETUPSEG
.byte 0x00, 0x9b # code read/exec/accessed
.byte 0x00, 0x00 # granularity = bytes


.word 0xFFFF # 16bit 64KB - (0x10000*1 = 64KB)
.word 0 # base address = SETUPSEG
.byte 0x00, 0x93 # data read/write/accessed
.byte 0x00, 0x00 # granularity = bytes

gdt_end:

沒有留言:

張貼留言