保护模式详解
在ia32下,cpu有两种工作模式:实模式和保护模式。
在实模式下,16位的寄存器用“段+偏移”的方法计算有效地址。
段寄存器始终是16位的。在实模式下,段值xxxxh表示的以xxxx0h开始的一段内存。但在保护模式下,段寄存器的值变成了一个索引(还有附加信息)这个索引指向了一个数据结构的表(gdt/ldt)项,表项(描述符)中详细定义了段的其实地址、界限、属性等内容。
保护模式需要理解:描述符,选择子
描述符包括,存储段描述符(代码段,数据段,堆栈段),系统描述符(任务状态段TSS,局部描述符表LDT),门描述符(调用门,任务门,中断门,陷阱门),下面以存储段描述符位例
我们看一下描述符的结构:
描述符共8个字节:
0,1字节是段界限(2字节)
2,3,4字节是段基址的低24位(3字节)
5,6字节是段基址的属性(2字节)
7字节是段机制的高8位(1字节)
属性:
(1) P:存在(Present)位。
P=1 表示描述符对地址转换是有效的,或者说该描述符所描述的段存在,即在内存中;
P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常。
(2) DPL: 表示描述符特权级(Descriptor Privilege level),共2位。它规定了所描述段的特权级,用于特权检查,以决定对该段能否访问。
(3) DT:说明描述符的类型。
对于存储段描述符而言,DT=1,以区别与系统段描述符和门描述符(DT=0)。
(4) TYPE: 说明存储段描述符所描述的存储段的具体属性。
数据段类型 类型值说明
----------------------------------
0只读
1只读、已访问
2读/写
3读/写、已访问
4只读、向下扩展
5只读、向下扩展、已访问
6读/写、向下扩展
7读/写、向下扩展、已访问
选择子的结构:
RPL(Requested Privilege Level): 请求特权级,用于特权检查。
TI(Table Indicator): 引用描述符表指示位
TI=0 指示从全局描述符表GDT中读取描述符;
TI=1 指示从局部描述符表LDT中读取描述符。
看一下段描述符的一个宏定义:
- ; 存储段描述符类型值说明
- ;----------------------------------------------------------------------------
- DA_DR EQU 90h ; 存在的只读数据段类型值 1001 0000
- DA_DRW EQU 92h ; 存在的可读写数据段属性值 1001 0010
- DA_DRWA EQU 93h ; 存在的已访问可读写数据段类型值 1001 0011
- DA_C EQU 98h ; 存在的只执行代码段属性值 1001 1000
- DA_CR EQU 9Ah ; 存在的可执行可读代码段属性值 1001 1010
- DA_CCO EQU 9Ch ; 存在的只执行一致代码段属性值 1001 1100
- DA_CCOR EQU 9Eh ; 存在的可执行可读一致代码段属性值 1001 1110
59.; 宏 ------------------------------------------------------------------------------------------------------ 60.; 61.; 描述符 62.; usage: Descriptor Base, Limit, Attr 63.; Base: dd 64.; Limit: dd (low 20 bits available)低二十位可用 65.; Attr: dw (lower 4 bits of higher byte are always 0)高字节的低四位始终为0 66.%macro Descriptor 3 ;段界限为低地址 1代表Base 2代表Limit 3代表属性 67. dw %2 & 0FFFFh ; 段界限 1 (2 字节) 68. dw %1 & 0FFFFh ; 段首地址 1 (2 字节) 69. db (%1 >> 16) & 0FFh ; 段首地址 2 (1 字节) 70. dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性 1 + 段界限 2 + 属性 2 (2 字节) 71. db (%1 >> 24) & 0FFh ; 段首地址 3 (1 字节) 72.%endmacro ; 共 8 字节
base传递的是段基址:dd双字,32位
limit传递的是段界限:dd双子,但只会用到低20位 ((%2>>8)& 0f00h)
Attr传递的是属性:字16位,高字节的低4位会被抹去。(%3 & 0f0ffh)
gdtr寄存器格式:
(1)准备gdt。
(2)加载gdt。
(3)关中断,保护模式下的中断处理方式是不同的,不关中断可能发生错误。
(4)打开a20地址线。
(5)置cr0的PE位。
(6)跳转进入保护模式。
实例:
; ┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓
; ┃31..24┃ (见下图) ┃ 段基址(23..0) ┃ 段界限(15..0)┃
; ┃ ┃ ┃ ┃ ┃
; ┃ 基址2┃③│② │ ①┃基址1b│ 基址1a ┃ 段界限1 ┃
; ┣━━━╋━━━┳━━━╋━━━━━━━━━━━╋━━━━━━━┫
; ┃ %6 ┃ %5 ┃ %4 ┃ %3 ┃ %2 ┃ %1 ┃
; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━┛
; │ \_________
; │ \__________________
; │ \________________________________________________
; │ \
; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
; ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃
; ┣━━╋━━╋━━╋━━╋━━┻━━┻━━┻━━╋━━╋━━┻━━╋━━╋━━┻━━┻━━┻━━┫
; ┃ G ┃ D ┃ 0 ┃ AVL┃ 段界限 2 (19..16) ┃ P ┃ DPL ┃ S ┃ TYPE ┃
; ┣━━┻━━┻━━┻━━╋━━━━━━━━━━━╋━━┻━━━━━┻━━┻━━━━━━━━━━━┫
; ┃ ③: 属性 2 ┃ ②: 段界限 2 ┃ ①: 属性1 ┃
; ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┛
; 高地址 低地址
;
;
%macro Descriptor 3 ;base(dd) limit(dd) attr(dw)
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 字节
DA_32 EQU 4000h ; 32 位段
DA_DR EQU 90h ; 存在的只读数据段类型值
DA_DRW EQU 92h ; 存在的可读写数据段属性值
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;利用Descriptor 宏 base , limit ,attr
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_32: Descriptor 0,CODE32_LEN-1,DA_32+DA_DR
LABEL_DESC_VEDIO Descriptor 0B8000h,0ffffh,DA_DRW
GDT_LEN EQU $-LABEL_GDT
GDTPTR dw GDT_LEN-1 ;gdt界限
dd 0
;GDT 选择子
SelectorCode32 equ LABEL_DESC_32 - LABEL_GDT
SelectorVedio equ LABEL_DESC_VEDIO - LABEL_GDT
[SECTION .c16]
[BITS 16]
LABEL_BEGIN:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h
;初始化gdt
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEC_CODE32
mov word [LABEL_DESC_32+2],ax
shr eax,16
mov byte [LABEL_DESC_32+4],al
mov byte [LABEL_DESC_32+7],ah
; 为加载 GDTR 作准备
xor eax,eax
mov ax,ds
shl eax,4
add eax,LABEL_GDT
mov dword [GDTPTR+2], eax
;加载gtd
lgtd [GDTPTR]
;关中断
cli
;打开a20地址线
in al,92h
or al,00000010h
out 92h,al
;cr0
mov eax,cr0
or eax,1
mov cr0,eax
;jmp
jmp dword SelectorCode32:0
LABEL_SEC_CODE32:
mov ax,SelectorCode32
mov gs,ax
mov edi, (80 * 10 + 0) * 2 ;10行0列
mov ah, 0Ch ; 0000: 黑底 1100: 红字
mov al,'P'
mov [gs:edi],ax
jmp $
CODE32_LEN EQU $-LABEL_SEC_CODE32
参考《自己动手写操作系统》