【X86汇编语言 从实模式到保护模式】07 进入保护模式

本文详细介绍了80x86处理器在保护模式下如何使用全局描述符表(GDT)进行内存管理,包括段描述符的结构、内存访问规则、堆栈操作以及如何初始化GDT和加载GDTR。保护模式旨在隔离多用户、多任务环境下各程序的内存空间,通过段选择子和描述符实现权限控制和内存访问限制。文章还涉及了A20地址线的问题,确保处理器能寻址超过1MB的内存,并讨论了从实模式切换到保护模式的细节,如清空流水线和堆栈初始化。

1 为何要使用保护模式

一般来说,操作系统负责整个计算机软、硬件的管理,它做任何事情都是可以的。但是,用户程序却应当有所限制,只允许它访问属于自己的数据,即使是转移,也只允许在自己的各个代码段之间进行。

但是在实模式下,用户程序对内存的访问非常自由,没有任何限制,随随便便就可以修改任何一个内存单元。

在多用户、多任务时代,内存中会有多个用户(应用)程序在同时运行。为了使它们彼此隔离,防止因某个程序的编写错误或者崩溃而影响到操作系统和其他用户程序,使用保护模式是必要的。

2. 全局描述符表

一般来说,为了让程序在内存中能自由浮动而又不影响它的正常执行,处理器将内存划分成逻辑上的段,并在指令中使用段内偏移地址。在保护模式下,对内存的访问仍然使用段地址和偏移地址,但是,在每个段能够访问之前,必须先进行登记。

和一个段有关的信息需要 8 个字节,64 位来描述,称为段描述符(Segment Descriptor),每个段都需要一个描述符。而存放这些描述符内存区域构成一个描述符表。

描述符表分**全局描述符表(Global Descriptor Table,GDT)**和局部描述符表(Local Descripto Table,LDT)。全局描述符表是为整个软硬件系统服务的。因此在进入保护模式前,必须要定义全局描述符表。

如图 1 所示,处理器内部有一个 48 位的寄存器,用来访问全局描述符表,称为全局描述符表寄存器(GDTR)
在这里插入图片描述

该寄存器分成 32 位的线性地址和 16 位的边界。GDTR 的 32 位线性基地址部分保存的是全局描述符表在内存中的起始线性地址,16 位边界部分保存的是全局描述符表的边界(界限),其在数值上等于表的大小(总字节数)减一。

GDT 的界限是 16 位的,所以,该表最大是 216 字节,也就是 65536 字节(64KB)。又因为一个描述符占 8 字节,故最多可以定义 8192 个描述符。

理论上,全局描述符表可以位于内存中的任何地方。但是,在进入保护模式之后,处理器要按新的内存访问模式工作,所以必须在进入保护模式之前定义 GDT。而在实模式下只能访问 1MB 的内存,故 GDT 通常都定义在 1MB 以下的内存范围中,如图 2 所示。允许在进入保护模式之后换个位置重新定义 GDT。
在这里插入图片描述

3 存储器的段描述符

第 7~9 行用于初始化堆栈,使堆栈段的逻辑段地址和代码段相同,并使堆栈指针寄存器 SP 指向 0x7c00。

后面开始定义主引导扇区代码所使用的数据段、代码段和堆栈段。在保护模式下,内存的访问必须通过描述符来进行。因此这些段必须重新在 GDT 中定义。

第 96 行,声明了标号 gdt_base 并初始化了一个双字 0x00007e00,我们决定从这个地方开始创建全局描述符表(GDT)。因为在实模式下,主引导程序的加载位置是 0x0000:0x7c00,32 位下对应着物理地址 0x00007c00,而主引导扇区大小 512 字节,所以把 GDT 设在主引导程序之后,也就是物理地址 0x00007e00 处。其内存分布如图 3 所示。
在这里插入图片描述
一旦确定了 GDT 在内存中的起始位置,下一步的工作就是确定要访问的段,并在 GDT 中为这
些段创建各自的描述符。如图 4 所示,每个描述符在 GDT 中占 8 字节,即 64 位。图中,下面是低 32 位,上面是高 32 位。
在这里插入图片描述
由上图可知,段描述符中:

  1. 段起始地址 32 位,段边界 20 位。在 32 位保护模式下,段地址是 32 位的线性地址,如果未开启分页功能,该线性地址就是物理地址。段基地址可以是 0~4GB 范围内的任意地址。20 位的段界限用来限制段的扩展范围。因为访问内存的方法是用段基地址加上偏移量。对于向上扩展的段,如代码段和数据段来说,偏移量是从 0 开始递增,段界限决定了偏移量的最大值;对于向下扩展的段,如堆栈段来说,段界限决定了偏移量的最小值。
  2. G 位是粒度(Granularity)位:用于解释段界限的含义。为 0 时,段界限以字节为单位,段的扩展范围是从 1 字节到 1 兆字节(1B~1MB)。为 1 时,段界限是以 4KB 为单位,段的扩展范围是从 4KB 到 4GB。
  3. D/B 位是默认的操作数大小(Default Operation Size)或者默认的堆栈指针大小(Default
    Stack Pointer Size),又或者上部边界(Upper Bound)标志。
    1. 对于代码段,此位称做 D 位,用于指示指令中默认的偏移地址和操作数尺寸。D=0 表示指令中的偏移地址或者操作数是 16 位的;D=1,指示 32 位的偏移地址或者操作数。
    2. 对于堆栈段来说,该位被叫做 B 位,用于在进行隐式的堆栈操作时,是使用 SP 寄存器还是 ESP 寄存器。隐式的堆栈操作指令包括 push、pop 和 call 等。如果该位是 0 ,在访问那个段时,使用 SP 寄存器,否则就是使用 ESP 寄存器。同时,B 位的值也决定了堆栈的上部边界。如果 B=0,那么堆栈段的上部边界(也就是 SP 寄存器的最大值)为 0xFFFF;如果 B=1,那么堆栈段的上部边界(也就是 ESP 寄存器的最大值)为 0xFFFFFFFF。
  4. L 位是 64 位代码段标志(64-bit Code Segment)位:保留此位给 64 位处理器使用。
  5. AVL 是软件可以使用的位(Available),通常由操作系统来用,处理器并不使用它。
  6. P 位是段存在位(Segment Present):用于指示描述符所对应的段是否存在。为 0 时,表示段不存在。P 位是由处理器负责检查的。每当通过描述符访问内存中的段时,如果 P 位是 0,处理器就会产生一个异常中断。通常,该中断处理过程是由操作系统提供的,该处理过程的任务是负责将该段从硬盘换回内存,并将 P 位置 1 。在多用户、多任务的系统中,这是一种常用的虚拟内存调度策略。
  7. DPL 表示描述符的特权级(Descriptor Privilege Level,DPL)。这两位用于指定段的特权级。共有 4 种处理器支持的特权级别,分别是 0、1、2、3,其中 0 是最高特权级别,3 是最低特权级别。一般是操作系统代码是 0,而普通的用户程序是 3。
  8. S 位是描述符类型(Descriptor Type)位:为 0 时,表示是一个系统段。为 1 时,表示是一个代码段或数据段。
  9. Type:用于指示描述符的子类型,或者说是类别。对于数据段来说,这 4 位分别是 X、E、W、A 位;而对于代码段来说,这 4 位则分别是 X、C、R、A 位。其关系如下表所示。

数据段:

X E W A 含义
0 0 0 x 只读
0 0 1 x 读、写
0 1 0 x 只读,向下扩展
0 1 1 x 读、写,向下扩展

E 位指示段的扩展方向。E=0 是向上扩展的,也就是向高地址方向扩展的,是普通的数据段;E=1 是向下扩展的,也就是向低地址方向扩展的,通常是堆栈段。

W 位指示段的读写属性,或者说段是否可写,W=0 的段是不允许写入的,否则会引发处理器异常中断;W=1 的段是可以正常写入的。

代码段:

X C R A 含义
1 0 0 x 只执行
1 0 1 x 执行、读
1 1 0 x 只执行,依从的代码段
1 1 1 x 执行、读,依从的代码段

C 位指示段是否为特权级依从的(Conforming)。C=0 表示非依从的代码段,这样的代码段可以从与它特权级相同的代码段调用,或者通过门调用;C=1 表示允许从低特权级的程序转移到该段执行。

R 位指示代码段是否允许读出。代码段总是可以执行的,但是,为了防止程序被破坏,它是不能写入的。至于是否有读出的可能,由 R 位指定。R=0 表示不能读出,如果企图去读一个 R=0 的代码段,会引发处理器异常中断;

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值