WriteOS: 关于操作系统进入保护模式的总结

转载:http://sleepycat.org/tech/os/protectmode


目录:

  • 本文简介
  • 1 进入保护模式的代码分析
  • 2 关于选择子(selector)的总结
  • 3 mov byte [LABEL_DESC_CODE32 + 4], al
  • 4 jmp dword SelectorCode32:0
  • 5 参考资料

本文简介

  • 概要:总结关于操作系统进入保护模式的相关知识点
  • 版本:NASM version 2.09.08
  • 日期:2012-08-21
  • 永久链接:http://sleepycat.org/tech/os/protectmode

1 进入保护模式的代码分析

代码来自《自己动手写操作系统》一书. 在原有基础上加了些自己的注释.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
; ==========================================
; 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        ; 显存首地址
 
 
GdtLen        equ    $ - LABEL_GDT    ; GDT长度
 
;共6 byte, 结构与寄存器 gdtr 一致. 用于保存 GDT 表信息, 通过 lgdt 加载.
;后3-6 byte内容, 在下面的代码中会计算出并填入.
GdtPtr        dw    GdtLen - 1   ; GDT界限
               dd    0            ; GDT基地址
 
; GDT 选择子
SelectorCode32        equ    LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo         equ    LABEL_DESC_VIDEO  - LABEL_GDT
 
 
[SECTION .s16]
[BITS    16]
LABEL_BEGIN:
     mov    ax, cs
     mov    ds, ax
     mov    es, ax
     mov    ss, ax
     mov    sp, 0100h
 
 
     ;此处计算出了 LABEL_SEG_CODE32 的真实物理地址,保存于 eax 中.
     xor    eax, eax
     mov    ax, cs
     shl    eax, 4                 ;eax 中存储 cs 段地址, 并左移四位
     add    eax, LABEL_SEG_CODE32  ;段+偏移地址, 计算出其真实物理首地址
 
     ;此处将前面计算出的真实物理地址, 按规则放入GDT相应的段基址位置.
     ;注: 保护模式寻址步骤:
     ;   1). Selector 选择子 => 取得对应 GDT 的位置
     ;   2). GDT 的 段基址 => 取得 物理首地址
     ;   3). 物理首地址 + 偏移量 => 真实地址
     mov    word [LABEL_DESC_CODE32 + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_CODE32 + 4], al
     mov    byte [LABEL_DESC_CODE32 + 7], ah
 
 
     xor    eax, eax
     mov    ax, ds
     shl    eax, 4                     ; eax 中存储 ds 段地址, 并左移四位
     add    eax, LABEL_GDT             ; 段+偏移地址, 计算出真实物理首地址(即GDT开始处地址)
     mov    dword [GdtPtr + 2], eax    ; 将此物理首地址放入 GdtPtr(共6 byte) 的3-6 byte 处.
 
     ; 加载到寄存器 gdtr.
     lgdt    [GdtPtr]
 
     ; 关中断
     cli
 
     ; 打开地址线A20
     in    al, 92h
     or    al, 00000010b
     out   92h, al
 
     ; 将寄存器 cr0 的第 0 位置设置为1, 表示 CPU 要运行于保护模式
     mov   eax, cr0
     or    eax, 1
     mov   cr0, eax
 
     ; 进入保护模式
     jmp   dword SelectorCode32:0   ; 执行这一句会把 SelectorCode32 装入 cs,
                                     ; 并跳转到 Code32Selector: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]

2 关于选择子(selector)的总结

    selector 是对应的 GDT 相对于 GDT 起始位置的偏移量.

    GDT 的长度都为 8 byte, 故 selector 的值为 8 的倍数. 也因此, 其最低 3 位正常情况下应该全为零, 但实际上, 最后 3 位被用作标志符, 标志 GDT 或 LDT 等, 故在使用 LDT 时, 需要直接修改对应的值。

3 mov byte [LABEL_DESC_CODE32 + 4], al

[LABEL_DESC_CODE32 + 4]
    表示 LABEL_DESC_CODE32 所在的地址, 加上 4 个内存单位(byte)

mov byte [LABEL_DESC_CODE32 + 4], al 
    表示将 al 内容, 放置到 [LABEL_DESC_CODE32 + 4] 所在地址, 放置内容长度为 1 byte.

4 jmp dword SelectorCode32:0

    SelectorCode32 => 选择子, 16 位, 被装入 cs.
    dword => 表示后面的偏移量为 32 位. 按保护模式方式寻址.

5 参考资料

    http://www.360doc.com/content/11/0409/22/6580811_108479867.shtml

你可能感兴趣的:(操作系统)