《Orange's 一个操作系统的实现》读书手记3(1)--- [ 保护模式(Protect Mode)]

 

 

保护模式(Protect Mode) (1)

 

  到底什么是保护模式?谁又在保护谁?是通过什么方式达到保护目的的? 为什么要保护呢?带着这些疑问和几分好奇,我们开始了第三章的学习,开始随着作者一步一步地体会“保护”二字的内在含义。那个连胚胎都还不是的“操作系统” 究竟能否正常发育,这一章的“保护”很关键!

3.1 认识保护模式

在好奇心的驱使下,人总是很勤劳的,作者如是说。

接着作者马上就给我们泼来了一头雾水:

;==========================================

; 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        ; 显存首地址

; GDT 结束

 

GdtLen        equ $ - LABEL_GDT   ; GDT 长度

GdtPtr        dw  GdtLen - 1          ; GDT 界限

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]

L ABEL_BEGIN:

mov ax, cs

mov ds, ax

mov es, ax

mov ss, ax

mov sp, 0100h

 

; 初始化 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]

 

; 关中断

cli

 

; 打开地址线A20

in  al, 92h

or  al, 00000010b

out 92h, al

 

; 准备切换到保护模式

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]

---------------------------------------%include "pm.inc"-----------------------------------------------

;

; 描述符

; 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

dw  %1 & 0FFFFh                 ; 段基址1

db  (%1 >> 16) & 0FFh           ; 段基址2

dw  ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2

db  (%1 >> 24) & 0FFh           ; 段基址3

%endmacro ; 8 字节

 

  如果你是第一次读到这里,不知道你对这段代码有怎么的感觉,可能对开头的耐心到第二页就消失的差不多了,然后很快的扫视,很短时间之后,你已经到达了代码末尾。

  知我者作者也!我确确实实就是这样的感受。我只记住了一句话:这段代码实现了从实模式到保护模式的转换!

  我们看到,整个程序有很多个类似[SECTION .xxx] 的东西,这个是用来把程序大卸八块的;我们还看到,[SECTION .gdt] 后面有3 个带 Descriptor 字样的行。

  先来看看这个Descriptor 是什么东东,看字面意思,我们知道它好像描述了一些什么东西……

  是的,它是一个宏,而且还带了3 个参数,不过它代表的不是一段可执行的代码,而是一个数据结构!我们暂且把它看成是结构体好了,汇编器处理的时候会直接把这个结构体替换进来。我们知道,一个dw 两个字节,一个db 一个字节,总共有3 dw 2 db ,所以这个结构体总共有3*2+2*1 = 8 个字节,跟注释说的是一致的。

  其中,%1 表示第一个参数,%2 代表第二个参数,%3 代表第三个参数。我们又看到,这些个参数被支离破碎地填充到了Descriptor 8 个字节里面。

  怎么搞这么麻烦啊! ?你问我,呵呵,我也不知道,反正这个叫GDT 的结构体有点变态 是不是,更可恶的是还不止一个。

  相比GDT 这个变态的家伙,紧挨着它屁股后面的这个数据结构GdtPtr 就要单纯多了。

GdtPtr      dw  GdtLen - 1          ; GDT 界限

              dd  0                   ; GDT 基地址

  还真是GDT 的跟屁虫!你看到了的:里面的东西全都是跟GDT 有关的。 2 个字节描述了GDT 的身高--- 界限;后4 个字节想要透露出GDT 的藏身地--- 基地址。( 其实刚开始GdtPtr 自己也不知道,所以是0)

  然后是两行Selector 打头的常量 (equ 定义出来是常量哦,像上面的dw dd 定义的才是变量) ,这里好像是选择了GDT 中的某个符号到某个符号差值,一下子搞不明白了,我选择暂时离开。

  再往下走吧,在[SECTION .s16] 后面有个[BITS 16] ,下面应该就是一些16 位的代码了吧?这些代码做了什么事?

  它把ds (数据段)、es( 扩展段) ss( 堆栈段) 全搞成跟cs( 代码段) 一样值了( 这个值就由org 指定的0x7c00) ,还把sp( 堆栈指针) 指向0x100h (256) 处。然后eax 自身异或清零,代码段指针被传进去,左移4 位后与LABEL_SEG_CODE32 相加。 

  LABEL_SEG_CODE32 在哪里?

  LABEL_SEG_CODE32 [SECTION .s32] [BITS 32] 后面,是一段32 位代码段的头标签。根据NASM 的规则我们知道,任何不带[] 标签或变量是代表一个地址,所以LABEL_SEG_CODE32 就是那段32 位代码段的偏移地址。Cs 16 位的,左移4 位再加上一个偏移地址不就是一个20 位的物理地址吗!

  这个20 位的物理地址又被分成了3 部分,被填入变态的GDT LABEL_DESC_CODE32 的第2 4 7 字节处,果真是初始化了一个GDT

  eax 又改过自新了一次,与上面类似地,通过ds 竟然把变态的GDT 老大的地址弄到了,而且还把它存到了跟屁虫GdtPtr 的第2 字节开始处. 。这下GdtPtr 什么都没做就有了那些GDT 的藏身基地,真是踏破铁鞋无觅处,得来全不费工夫。

  Lgdt 是个探子----- 一旦GdtPrt 有了GDT 们的基地消息,就向GDTR 报告。这样一来,GDTR 就可以顺滕摸瓜把GDT 们一个一个给揪出来了。究竟GDTR 是如何找到GDT 们的,下一节会有分解。

  再看,in/out 做了一些端口操作,打通了地址线A20 ,然后又对控制器寄存器cr0 的第0 位做了些手脚,此时此刻,我们终于走到了保护模式的大门口!

  鲤鱼跳龙门,华丽转身。

  jmp 之后它就开始进入保护模式的天地了,在这片天地里,它向人们展现了一个红色的’P ,最终死在了jmp $ 上。

  百说不如一做,让我们来见证一下这段辉煌。

  第二章已经搭建好的bochs nasm 可以派上用场了:

  nasm pmtest1.asm -o pmtest1.bin

  dd   if=pmtest1.bin of=a.img bs=512 count=1 conv=notrunc

  bochs

 

 

注意:a.img 一定要用上次做好的那个,为什么?想一想。



现在,我们已经隐隐约约知道到了:

  •   程序定义了一个叫做GDT 的数据结构。
  • 后面的16 位代码进行了一些与GDT 有关的操作。
  • 程序最后跳到32 位代码做了一点操作显存的工作。

 

  下一节,让我们跟保护模式走得更近一些。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值