操作系统-从保护模式返回实模式

Q.从上节课可以引出一个小问题,下面的语句是否有Bug的存在操作系统-从保护模式返回实模式
在进入保护模式后,在第一处使用了栈段选择址对ss赋值,然后使用打印,觉得会出现Bug的存在的是因为使用了调用函数,而使用调用函数需要栈空间,还需要栈顶指针-esp寄存器,而在这里的代码中没有对esp寄存器进行赋值。在这里需要对esp寄存器进行查找。
A.对上节课的代码进行实验
操作系统-从保护模式返回实模式操作系统-从保护模式返回实模式操作系统-从保护模式返回实模式操作系统-从保护模式返回实模式操作系统-从保护模式返回实模式
操作系统-从保护模式返回实模式操作系统-从保护模式返回实模式
在这里进行的步骤是首先反编译找到跳转处的地址值,然后在bochs上设置断点通过一步一步的单步调试使其运行到图5中即将进行call命令处,然后实验reg命令对寄存器地址进行查看,发现esp寄存器的地址是0x00007c00,与上章的栈顶寄存器地址一致,程序的运行结果符合预期。这里会出现一个疑问,没有对esp寄存器地址进行赋值,为什么会在32保护模式下,esp寄存器的值却指向栈顶,可以再一次的进行实验
实验-对esp进行验证
操作系统-从保护模式返回实模式
操作系统-从保护模式返回实模式操作系统-从保护模式返回实模式![]
操作系统-从保护模式返回实模式
操作系统-从保护模式返回实模式
这里运行的情况是,同样首先通过反编译对图1处的地址进行查找,然后设置断点并查该断点处的esp寄存器值,然后单步调试值跳转处在对,esp寄存器值进行查看,最后进入到32位保护模式下对esp寄存器地址进行查看,发现所有的esp寄存器的值都是相同的,所以在32位保护模式下剋有不对esp寄存器进行赋值.
a.保护模式下的栈段
1.指定一段空间,并为其定义段描述符
2.根据段描述符表中的位置定义选择子
3.初始化段寄存器
4.初始化栈顶指针
B.栈段的一般性定义(保护模式下)
主要对上章的段描述符与基址进行了修改,同时重新定义了一个新的代码节,还有定义保护模式下专用的栈段

%include "inc.asm"

org 0x9000

jmp CODE16_SEGMENT

[section .gdt]
; GDT definition
;                                
GDT_ENTRY       :     Descriptor    0,            0,           0
CODE32_DESC     :     Descriptor    0,    Code32SegLen - 1,    DA_C + DA_32
VIDEO_DESC      :     Descriptor 0xB8000,     0x07FFF,         DA_DRWA + DA_32
DATA32_DESC     :     Descriptor    0,    Data32SegLen - 1,    DA_DR + DA_32
STACK32_DESC    :     Descriptor    0,     TopOfStack32,     DA_DRW + DA_32
; GDT end

GdtLen    equ   $ - GDT_ENTRY

GdtPtr:
          dw   GdtLen - 1
          dd   0

; GDT Selector

Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL0
Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL0

; end of [section .gdt]

TopOfStack16    equ 0x7c00

[section .dat]
[bits 32]
DATA32_SEGMENT:
    DTOS               db  "D.T.OS!", 0
    DTOS_OFFSET        equ DTOS - $$
    HELLO_WORLD        db  "Hello World!", 0
    HELLO_WORLD_OFFSET equ HELLO_WORLD - $$

Data32SegLen equ $ - DATA32_SEGMENT

[section .s16]
[bits 16]
CODE16_SEGMENT:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, TopOfStack16

    ; initialize GDT for 32 bits code segment
    mov esi, CODE32_SEGMENT
    mov edi, CODE32_DESC

    call InitDescItem

    mov esi, DATA32_SEGMENT
    mov edi, DATA32_DESC

    call InitDescItem

    mov esi, STACK32_SEGMENT
    mov edi, STACK32_DESC

    call InitDescItem

    ; initialize GDT pointer struct
    mov eax, 0
    mov ax, ds
    shl eax, 4
    add eax, GDT_ENTRY
    mov dword [GdtPtr + 2], eax

    ; 1. load GDT
    lgdt [GdtPtr]

    ; 2. close interrupt
    cli 

    ; 3. open A20
    in al, 0x92
    or al, 00000010b
    out 0x92, al

    ; 4. enter protect mode
    mov eax, cr0
    or eax, 0x01
    mov cr0, eax

    ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0

; esi    --> code segment label
; edi    --> descriptor label
InitDescItem:
    push eax

    mov eax, 0
    mov ax, cs
    shl eax, 4
    add eax, esi
    mov word [edi + 2], ax
    shr eax, 16
    mov byte [edi + 4], al
    mov byte [edi + 7], ah

    pop eax

    ret

[section .s32]
[bits 32]
CODE32_SEGMENT:
    mov ax, VideoSelector
    mov gs, ax

    mov ax, Stack32Selector
    mov ss, ax

    mov eax, TopOfStack32
    mov esp, eax

    mov ax, Data32Selector
    mov ds, ax

    mov ebp, DTOS_OFFSET
    mov bx, 0x0C
    mov dh, 12
    mov dl, 33

    call PrintString

    mov ebp, HELLO_WORLD_OFFSET
    mov bx, 0x0C
    mov dh, 13
    mov dl, 31

    call PrintString

    jmp $

; ds:ebp    --> string address
; bx        --> attribute
; dx        --> dh : row, dl : col
PrintString:
    push ebp
    push eax
    push edi
    push cx
    push dx

print:
    mov cl, [ds:ebp]
    cmp cl, 0
    je end
    mov eax, 80
    mul dh
    add al, dl
    shl eax, 1
    mov edi, eax
    mov ah, bl
    mov al, cl
    mov [gs:edi], ax
    inc ebp
    inc dl
    jmp print

end:
    pop dx
    pop cx
    pop edi
    pop eax
    pop ebp

    ret

Code32SegLen    equ    $ - CODE32_SEGMENT

[section .gs]
[bits 32]
STACK32_SEGMENT:
    times 1024 * 4 db 0

Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32  equ Stack32SegLen - 1

make之后在bochs下的运行结果
操作系统-从保护模式返回实模式
现在通过实验对esp再次进行查看,首先再次通过反编译对跳转指令查看,然后设置断点并单步查看,并单步查看的寄存器地址的变化-ss段寄存器指向了定义的栈段,最后esp指向栈空间的限制处,最后打印处字符串,说明自定义的栈段空间被使用了操作系统-从保护模式返回实模式操作系统-从保护模式返回实模式
C.是否能从保护模式返回实模式?
a.80x86中的一个神秘限制
1.无法直接从32位代码段回到实模式
2.只能从16位代码段间接返回实模式
3.在返回前必须用合适的选择子对寄存器赋值
b.处理器中的设计简介
1.80286之后的处理器都提供兼容8086的实模式
2.然而,绝大多数处理器都运行于保护模式
3.因此,保护模式的运行效率至关重要
4.那么,处理器如何高效的访问内存中的段描述符?

解决方案-高速缓存存储器
a.当使用选择子设置段寄存器时
1.根据选择子访问内存中的段描述符
2.将段描述符加载到段寄存器的高速缓冲存储器
3.需要段描述符信息时,直接从高速缓冲存储器中获得
但是会出现一个问题,当处理器运行于实模式时,段寄存器的高速缓冲存储器是否会用到?
在这里需要注意的是-在实模式下,高速缓冲存储器仍然发挥着作用,段基址是32位,其值是相应段寄存器的值乘以16,实模式下段基址有效位为20位,段界限固定为0xFFFF(64K),段属性的值不可设置,只能继续沿用保护方式下所设置的值
操作系统-从保护模式返回实模式
因此,当从保护模式返回实模式时-提供加载一个合适的描述符选择子到有关段寄存器,以使得对应段描述符高速缓冲寄存器中含有合适的段界限和属性
返回实模式的流程
操作系统-从保护模式返回实模式操作系统-从保护模式返回实模式
代码

%include "inc.asm"

org 0x9000

jmp ENTRY_SEGMENT

[section .gdt]
; GDT definition
;                                 段基址,       段界限,       段属性
GDT_ENTRY       :     Descriptor    0,            0,           0
CODE32_DESC     :     Descriptor    0,    Code32SegLen - 1,    DA_C + DA_32
VIDEO_DESC      :     Descriptor 0xB8000,     0x07FFF,         DA_DRWA + DA_32
DATA32_DESC     :     Descriptor    0,    Data32SegLen - 1,    DA_DR + DA_32
STACK32_DESC    :     Descriptor    0,     TopOfStack32,       DA_DRW + DA_32
CODE16_DESC     :     Descriptor    0,        0xFFFF,          DA_C 
UPDATE_DESC     :     Descriptor    0,        0xFFFF,          DA_DRW
; GDT end

GdtLen    equ   $ - GDT_ENTRY

GdtPtr:
          dw   GdtLen - 1
          dd   0

; GDT Selector

Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL0
Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL0
Code16Selector    equ (0x0005 << 3) + SA_TIG + SA_RPL0
UpdateSelector    equ (0x0006 << 3) + SA_TIG + SA_RPL0
; end of [section .gdt]

TopOfStack16    equ 0x7c00

[section .dat]
[bits 32]
DATA32_SEGMENT:
    DTOS               db  "D.T.OS!", 0
    DTOS_OFFSET        equ DTOS - $$
    HELLO_WORLD        db  "Hello World!", 0
    HELLO_WORLD_OFFSET equ HELLO_WORLD - $$

Data32SegLen equ $ - DATA32_SEGMENT

[section .s16]
[bits 16]
ENTRY_SEGMENT:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, TopOfStack16

    mov [BACK_TO_REAL_MODE + 3], ax

    ; initialize GDT for 32 bits code segment
    mov esi, CODE32_SEGMENT
    mov edi, CODE32_DESC

    call InitDescItem

    mov esi, DATA32_SEGMENT
    mov edi, DATA32_DESC

    call InitDescItem

    mov esi, STACK32_SEGMENT
    mov edi, STACK32_DESC

    call InitDescItem

    mov esi, CODE16_SEGMENT
    mov edi, CODE16_DESC

    call InitDescItem

    ; initialize GDT pointer struct
    mov eax, 0
    mov ax, ds
    shl eax, 4
    add eax, GDT_ENTRY
    mov dword [GdtPtr + 2], eax

    ; 1. load GDT
    lgdt [GdtPtr]

    ; 2. close interrupt
    cli 

    ; 3. open A20
    in al, 0x92
    or al, 00000010b
    out 0x92, al

    ; 4. enter protect mode
    mov eax, cr0
    or eax, 0x01
    mov cr0, eax

    ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0

BACK_ENTRY_SEGMENT:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, TopOfStack16

    in al, 0x92
    and al, 11111101b
    out 0x92, al

    sti

    mov bp, HELLO_WORLD
    mov cx, 12
    mov dx, 0
    mov ax, 0x1301
    mov bx, 0x0007
    int 0x10

    jmp $

; esi    --> code segment label
; edi    --> descriptor label
InitDescItem:
    push eax

    mov eax, 0
    mov ax, cs
    shl eax, 4
    add eax, esi
    mov word [edi + 2], ax
    shr eax, 16
    mov byte [edi + 4], al
    mov byte [edi + 7], ah

    pop eax

    ret

[section .s16]
[bits 16]
CODE16_SEGMENT:
    mov ax, UpdateSelector
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov eax, cr0
    and al, 11111110b
    mov cr0, eax

BACK_TO_REAL_MODE:    
    jmp 0 : BACK_ENTRY_SEGMENT

Code16SegLen    equ    $ - CODE16_SEGMENT

[section .s32]
[bits 32]
CODE32_SEGMENT:
    mov ax, VideoSelector
    mov gs, ax

    mov ax, Stack32Selector
    mov ss, ax

    mov eax, TopOfStack32
    mov esp, eax

    mov ax, Data32Selector
    mov ds, ax

    mov ebp, DTOS_OFFSET
    mov bx, 0x0C
    mov dh, 12
    mov dl, 33

    call PrintString

    mov ebp, HELLO_WORLD_OFFSET
    mov bx, 0x0C
    mov dh, 13
    mov dl, 31

    call PrintString

    jmp Code16Selector : 0

; ds:ebp    --> string address
; bx        --> attribute
; dx        --> dh : row, dl : col
PrintString:
    push ebp
    push eax
    push edi
    push cx
    push dx

print:
    mov cl, [ds:ebp]
    cmp cl, 0
    je end
    mov eax, 80
    mul dh
    add al, dl
    shl eax, 1
    mov edi, eax
    mov ah, bl
    mov al, cl
    mov [gs:edi], ax
    inc ebp
    inc dl
    jmp print

end:
    pop dx
    pop cx
    pop edi
    pop eax
    pop ebp

    ret

Code32SegLen    equ    $ - CODE32_SEGMENT

[section .gs]
[bits 32]
STACK32_SEGMENT:
    times 1024 * 4 db 0

Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32  equ Stack32SegLen - 1

运行结果
操作系统-从保护模式返回实模式
小结
1.定义保护模式的栈段时,必须设置段选择子和栈顶指针
2.从保护模式能够间接跳转返回实模式
3.在实模式下,依然使用高速缓冲存储器中数据做有效性判断
4.通过运行时修改指令中的数据能够动态决定代码的行为

猜你喜欢

转载自blog.51cto.com/13475106/2456581
今日推荐