保护模式小结:

  -使用选择子访问段描述符表时,索引值的合法性检测,这个检测是处理器做的

    当索引值越界时,引发异常

    判断规则:索引值*8 + 7 <= 段描述表界限值

段描述表界限值就是段描述表中的地址最大值

  

  -内存段类型合法性检测,使用选择子给相应的段寄存器赋值的时候会进行这个合法性检测

    具备可执行属性的段(代码段)才能加载到CS寄存器

    具备可写属性的段(数据段)才能加载到SS寄存器,加载到SS寄存器的段是栈段

    具备可读属性的段才能加载到DS,ES,FS,GS寄存器

 这里列出来的都是最低的条件

  -代码段和数据段的保护

    处理器每访问一个地址都要确认该地址不超过界限值

    判断规则:

      代码段:IP + 指令长度 <= 代码段界限

      数据段:访问起始地址 + 访问数据长度 <= 数据段界限

    段界限是相对于段基址而言的,而不是绝对物理地址

 注意:

  保护模式中代码中定义的界限值通常为:

    最大偏移地址值(相对于段基址)。

 

实验分析:

正常程序如下:

 

  1 %include "inc.asm"
  2 
  3 org 0x9000
  4 
  5 jmp CODE16_SEGMENT
  6 
  7 [section .gdt]
  8 ; GDT definition
  9 ;                                 段基址,       段界限,       段属性
 10 GDT_ENTRY       :     Descriptor    0,            0,           0
 11 CODE32_DESC     :     Descriptor    0,    Code32SegLen  - 1,   DA_C + DA_32
 12 VIDEO_DESC      :     Descriptor 0xB8000,       0x07FFF,       DA_DRWA + DA_32
 13 DATA32_DESC     :     Descriptor    0,    Data32SegLen  - 1,   DA_DR + DA_32
 14 STACK32_DESC    :     Descriptor    0,      TopOfStack32,      DA_DRW + DA_32        
 15 ; GDT end
 16 
 17 GdtLen    equ   $ - GDT_ENTRY
 18 
 19 GdtPtr:
 20           dw   GdtLen - 1
 21           dd   0
 22           
 23           
 24 ; GDT Selector
 25 
 26 Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0
 27 VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL0
 28 Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
 29 Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL0
 30 
 31 ; end of [section .gdt]
 32 
 33 TopOfStack16    equ  0x7c00
 34 
 35 [section .dat]
 36 [bits 32]
 37 DATA32_SEGMENT:
 38     DTOS                 db    "D.T.OS!", 0
 39     DTOS_OFFSET          equ   DTOS - $$
 40     HELLO_WORLD          db    "Hello World!", 0
 41     HELLO_WORLD_OFFSET   equ  HELLO_WORLD - $$
 42 
 43 Data32SegLen  equ $ - DATA32_SEGMENT
 44 
 45 [section .s16]
 46 [bits 16]
 47 CODE16_SEGMENT:
 48     mov ax, cs
 49     mov ds, ax
 50     mov es, ax
 51     mov ss, ax
 52     mov sp, TopOfStack16
 53     
 54     ; initialize GDT for 32 bits code segment
 55     mov esi, CODE32_SEGMENT
 56     mov edi, CODE32_DESC
 57     
 58     call InitDescItem
 59     
 60     mov esi, DATA32_SEGMENT
 61     mov edi, DATA32_DESC
 62     
 63     call InitDescItem
 64     
 65     mov esi, DATA32_SEGMENT
 66     mov edi, STACK32_DESC
 67     
 68     call InitDescItem
 69     
 70     ; initialize GDT pointer struct
 71     mov eax, 0
 72     mov ax, ds
 73     shl eax, 4
 74     add eax, GDT_ENTRY
 75     mov dword [GdtPtr + 2], eax
 76 
 77     ; 1. load GDT
 78     lgdt [GdtPtr]
 79     
 80     ; 2. close interrupt
 81     cli 
 82     
 83     ; 3. open A20
 84     in al, 0x92
 85     or al, 00000010b
 86     out 0x92, al
 87     
 88     ; 4. enter protect mode
 89     mov eax, cr0
 90     or eax, 0x01
 91     mov cr0, eax
 92     
 93     ; 5. jump to 32 bits code
 94     jmp dword Code32Selector : 0
 95 
 96     
 97 ; esi    --> code segment label
 98 ; edi    --> descriptor label
 99 InitDescItem:
100     push eax
101     
102     mov eax, 0
103     mov ax, cs
104     shl eax, 4
105     add eax, esi
106     mov word [edi + 2], ax
107     shr eax, 16
108     mov byte [edi + 4], al
109     mov byte [edi + 7], ah
110     
111     pop eax
112     
113     ret
114     
115     
116 [section .s32]
117 [bits 32]
118 CODE32_SEGMENT:
119     mov ax, VideoSelector
120     mov gs, ax
121     
122     mov ax, Stack32Selector
123     mov ss, ax
124     
125     mov eax, TopOfStack32
126     mov esp, eax
127     
128     mov ax, Data32Selector
129     mov ds, ax
130     
131     mov ebp, DTOS_OFFSET
132     mov bx, 0x0C
133     mov dh, 12
134     mov dl, 33
135     
136     call PrintString
137     
138     mov ebp, HELLO_WORLD_OFFSET
139     mov bx, 0x0C
140     mov dh, 13
141     mov dl, 30
142     
143     call PrintString
144     
145     jmp $
146 
147 ; ds:ebp   --> string address
148 ; bx       --> attribute
149 ; dx       --> dh : row, dl : col
150 PrintString:
151     push ebp
152     push eax
153     push edi 
154     push cx
155     push dx
156     
157 print:
158     mov cl, [ds:ebp]
159     cmp cl, 0
160     je end
161     mov eax, 80
162     mul dh
163     add al, dl
164     shl eax, 1
165     mov edi, eax
166     mov ah, bl
167     mov al, cl
168     mov [gs:edi], ax
169     inc ebp
170     inc dl
171     jmp print
172     
173 end:
174     pop dx
175     pop cx
176     pop edi
177     pop eax
178     pop ebp
179     
180     ret
181 
182 Code32SegLen    equ    $ - CODE32_SEGMENT
183 
184 [section .gs]
185 [bits 32]
186 STACK32_SEGMENT:
187     times 1024 * 4 db 0
188     
189 Stack32SegLen    equ $ - STACK32_SEGMENT
190 TopOfStack32    equ Stack32SegLen - 1

 

运行结果如下:

修改程序如下:

  1 %include "inc.asm"
  2 
  3 org 0x9000
  4 
  5 jmp CODE16_SEGMENT
  6 
  7 [section .gdt]
  8 ; GDT definition
  9 ;                                 段基址,       段界限,       段属性
 10 GDT_ENTRY       :     Descriptor    0,            0,           0
 11 CODE32_DESC     :     Descriptor    0,    Code32SegLen  - 1,   DA_C + DA_32
 12 VIDEO_DESC      :     Descriptor 0xB8000,       0x07FFF,       DA_DRWA + DA_32
 13 DATA32_DESC     :     Descriptor    0,    Data32SegLen  - 1,   DA_DR + DA_32
 14 STACK32_DESC    :     Descriptor    0,      TopOfStack32,      DA_DRW + DA_32        
 15 ; GDT end
 16 
 17 GdtLen    equ   $ - GDT_ENTRY
 18 
 19 GdtPtr:
 20           dw   GdtLen - 1
 21           dd   0
 22           
 23           
 24 ; GDT Selector
 25 
 26 Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0
 27 VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL0
 28 Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
 29 Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL0
 30 ExceptionSelector   equ (0x0005 << 3) + SA_TIG + SA_RPL0
 31 
 32 ; end of [section .gdt]
 33 
 34 TopOfStack16    equ  0x7c00
 35 
 36 [section .dat]
 37 [bits 32]
 38 DATA32_SEGMENT:
 39     DTOS                 db    "D.T.OS!", 0
 40     DTOS_OFFSET          equ   DTOS - $$
 41     HELLO_WORLD          db    "Hello World!", 0
 42     HELLO_WORLD_OFFSET   equ  HELLO_WORLD - $$
 43 
 44 Data32SegLen  equ $ - DATA32_SEGMENT
 45 
 46 [section .s16]
 47 [bits 16]
 48 CODE16_SEGMENT:
 49     mov ax, cs
 50     mov ds, ax
 51     mov es, ax
 52     mov ss, ax
 53     mov sp, TopOfStack16
 54     
 55     ; initialize GDT for 32 bits code segment
 56     mov esi, CODE32_SEGMENT
 57     mov edi, CODE32_DESC
 58     
 59     call InitDescItem
 60     
 61     mov esi, DATA32_SEGMENT
 62     mov edi, DATA32_DESC
 63     
 64     call InitDescItem
 65     
 66     mov esi, DATA32_SEGMENT
 67     mov edi, STACK32_DESC
 68     
 69     call InitDescItem
 70     
 71     ; initialize GDT pointer struct
 72     mov eax, 0
 73     mov ax, ds
 74     shl eax, 4
 75     add eax, GDT_ENTRY
 76     mov dword [GdtPtr + 2], eax
 77 
 78     ; 1. load GDT
 79     lgdt [GdtPtr]
 80     
 81     ; 2. close interrupt
 82     cli 
 83     
 84     ; 3. open A20
 85     in al, 0x92
 86     or al, 00000010b
 87     out 0x92, al
 88     
 89     ; 4. enter protect mode
 90     mov eax, cr0
 91     or eax, 0x01
 92     mov cr0, eax
 93     
 94     ; 5. jump to 32 bits code
 95     jmp dword ExceptionSelector : 0
 96 
 97     
 98 ; esi    --> code segment label
 99 ; edi    --> descriptor label
100 InitDescItem:
101     push eax
102     
103     mov eax, 0
104     mov ax, cs
105     shl eax, 4
106     add eax, esi
107     mov word [edi + 2], ax
108     shr eax, 16
109     mov byte [edi + 4], al
110     mov byte [edi + 7], ah
111     
112     pop eax
113     
114     ret
115     
116     
117 [section .s32]
118 [bits 32]
119 CODE32_SEGMENT:
120     mov ax, VideoSelector
121     mov gs, ax
122     
123     mov ax, Stack32Selector
124     mov ss, ax
125     
126     mov eax, TopOfStack32
127     mov esp, eax
128     
129     mov ax, Data32Selector
130     mov ds, ax
131     
132     mov ebp, DTOS_OFFSET
133     mov bx, 0x0C
134     mov dh, 12
135     mov dl, 33
136     
137     call PrintString
138     
139     mov ebp, HELLO_WORLD_OFFSET
140     mov bx, 0x0C
141     mov dh, 13
142     mov dl, 30
143     
144     call PrintString
145     
146     jmp $
147 
148 ; ds:ebp   --> string address
149 ; bx       --> attribute
150 ; dx       --> dh : row, dl : col
151 PrintString:
152     push ebp
153     push eax
154     push edi 
155     push cx
156     push dx
157     
158 print:
159     mov cl, [ds:ebp]
160     cmp cl, 0
161     je end
162     mov eax, 80
163     mul dh
164     add al, dl
165     shl eax, 1
166     mov edi, eax
167     mov ah, bl
168     mov al, cl
169     mov [gs:edi], ax
170     inc ebp
171     inc dl
172     jmp print
173     
174 end:
175     pop dx
176     pop cx
177     pop edi
178     pop eax
179     pop ebp
180     
181     ret
182 
183 Code32SegLen    equ    $ - CODE32_SEGMENT
184 
185 [section .gs]
186 [bits 32]
187 STACK32_SEGMENT:
188     times 1024 * 4 db 0
189     
190 Stack32SegLen    equ $ - STACK32_SEGMENT
191 TopOfStack32    equ Stack32SegLen - 1

我们在第30行新添加了一个段选择子,选择子号是5,而在第8-14行的段描述符表中没有为5的描述符。第95行跳转时我们使用新添加的段选择子,启动bochs,实验结果如下:

 可以看出CPU复位了,因为选择子对用的描述符越界了。

 下面进行属性的实验:

在正常的程序基础上进行修改:

1、修改代码段属性

将第11行的代码段的可执行属性改成可读属性,程序运行崩溃,运行结果如下:

2、修改栈段属性,将可写属性去掉,只留下可读属性,程序崩溃,如下:

 

可以看到程序在执行第123行的mov ss,ax是崩溃的,因为这时候栈段的属性没有可写了,所以会出错。

3、修改数据段属性,将可读写属性改为可执行属性

 

 运行结果如下:

 

 可以看到在第129行出错,mov ds,ax导致程序崩溃。

 

下面做界限值的实验:

1、代码段界限修改

将第11行的代码段界限改小一个字节,运行结果如下:

程序崩溃了,是在执行180行的ret时崩溃的,因为这时候的代码已经超出了界限。

2、修改数据段界限

将第13行的数据段界限改小1个字节,运行结果如下:

 

 

 这是在执行第158行的打印时崩溃了,当我们打印字符串的最后一个0时,超出了数据段的界限。

 

问题:

保护模式除了利用段界限对内存访问进行保护,是否还提供其他的保护机制?

保护模式中的特权级:

  - X86架构中的保护模式提供了四个特权级(0,1,2,3)

  - 特权级从高到低分别是0,1,2,3(数字越大特权级越低)

linux和window只用到0和3级,即内核态和用户态。

 

特权级的表现形式:

  CPL(Current Privilege Level)

    当前可执行代码段的特权级,由CS寄存器最低2位定义,CS保存的段选择子

  DPL(Descriptor Privilege Level)

    内存段的特权级,在段描述符表中定义

  RPL(Request Privilege Level)

    选择子的特权级,由选择子最低2位定义

 段描述符中的DPL用于标识内存段的特权级,可执行代码访问内存段时必须满足一定的特权级(CPL),否则,处理器将产生异常

可执行代码访问数据段的情形:

  应用程序访问内核的数据,会崩溃,因为内核的数据的DPL是0,应用程序的CPL是3。CPL应该小于等于DPL才可以访问。

 

CPL和DPL的关系:代码段之间的跳转

  保护模式中每一个代码段都定义了一个DPL

  当处理器从A代码段成功跳转到B代码段执行

    跳转之前:CPL = DPLa

    跳转之后:CPL = DPLb

   

保护模式中,每一个数据段都定义了一个DPL

当处理器执行过程中需要访问数据段时:

  CPL <= DPLdata

 

CPL和DPL实验:

首先在inc.asm文件中定义特权级

 

1、修改代码段的DPL

11行将代码段的DPL设置为3,执行结果如下:

 

程序崩溃了,提示DPL不等于CPL。

程序进入保护模式后默认的CPL为0,而我们的32位代码段DPL为3。

第88-91行的程序执行完就进入了保护模式,这时的CPL默认为0,在第94行跳转时,从CPL为0向DPL为3跳转就发生了异常。

运行结果告诉我们从CPL为0的代码段不能跳转到DPL位3的代码段。

将DPL改为0,运行结果如下:

 

 

程序正常运行了,这时候CPL等于DPL。从CPL为0的代码段可以跳转到DPL位0的代码段。

2、修改数据段的DPL

 

 

程序依然能正常运行,这说明程序在访问数据时,只要CPL小于等于相应数据段的DPL即可。

 

RPL实验:

1、修改代码段选择子的RPL

运行结果如下:

提示我们RPL > CPL,可见在代码段跳转时,RPL也参与了合法性判断。

将代码段RPL改回0即可正常运行。

2、修改数据段的RPL

 

28行将RPL改为3,运行结果:

再次修改28行的RPL:

运行结果如下:

可见访问数据段时RPL并没有参与合法性判断。

 

 3、栈段的RPL实验

第14行的栈的DPL改为3,第29行栈的RPL改为3,运行结果如下:

将上述两个特权级都改为0,如下:

结果如下:

都改为0后程序就可以正常运行了。

可见,栈段和数据段的特权级判断使用的规则并不一样。

 

 实验结论:

  处理器进入保护模式后CPL = 0(最高特权级)

  处理器不能从高特权级转换到低特权级执行

  选择子RPL大于对应段描述符的DPL时,产生异常

 

处理器进入保护模式后默认特权级为0,我们必须将它降下来,这样才能从高特权级跳转到低特权级代码执行。从低到高跳转也需要改变特权级。

 

小结:

  保护模式对内存的访问范围有严格定义

  保护模式定义了内存段的特权级(0,1,2,3)

    每个内存段都有固定的特权级(DPL)

    不同代码段之间成功跳转后CPL可能发生改变

    CPL小于或等于数据段DPL才能成功访问数据

 

 

 

 

 

 

 

 

  

posted on 2018-09-09 18:57  周伯通789  阅读(883)  评论(0编辑  收藏  举报