【从头开始写操作系统系列】一致代码段与非一致代码段
上几篇文章,我们一直在讨论的都是 GDT 相关的一些问题,现在我们知道在系统在从实模式向保护模式跳转时,GDT 是必须要准备的结构。在介绍这一跳转之前,这篇文章我们来介绍两个概念:一致代码段和非一致代码段。
首先,我们先来看几个问题:
一致代码段和非一致代码段是什么?
为什么要有一致代码段和非一致代码段?
系统提供怎样的机制来使用户程序访问内核数据?
程序如何在段与段之间跳转?
接下来,我们将讨论上述这些问题。
特权级
为了更好的理解之后的问题,我们先来讨论一个概念:特权级。
特权级是一种机制来保护数据和防止恶意行为,特权级分4种:0,1,2,3。0为最高特权级,4为最低。(在 Linux 中只使用了0和3)
这些特权级是通过三个标志来表示的:CPL,DPL,RPL
- CPL 是存放于如 CS、SS 寄存器中的,表示当前特权级
- DPL 是在 GDT(全局描述符表)/LDT(局部描述符表)中的,静态的
- RPL是代码中根据不同段跳转而确定,用于刷新 CPL
CPL(Current Privilege Level)是表示当前执行程序的特权级,它被存放在CS 和 SS 的第0位和第1位。通常情况下,CPL等于代码的段的特权级。在遇到一致代码段时,一致代码段可以被相同或者更低特权级的代码段访问。当处理器访问一个与 CPL特权级不同的代码段时,CPL 不会改变。
DPL(Descriptor Privilege Level)表示段或者门的特权级,它被存储在段描述符或门描述符的 DPL 字段中。当当前代码段要访问一个段或门时,DPL 会和 CPL 以及段选择子或门选择子的 RPL 进行比较,根据段或门的不同类型,DPL 将会被区别对待
- 数据段:DPL 规定了可以访问该段的最低特权级,如果 DPL 为1,那么只有运行在CPL 为0或者1的程序才有权访问它。
- 非一致代码段(不使用调用门的情况下):DPL 规定了可以访问该段的特权级,如果 DPL 为1,那么只有运行在 CPL 为1的程序才有权访问它。
- 调用门:DPL 规定了当前运行程序可以访问调用门的最低特权级(和数据段访问规则相同)
- 一致代码段和通过调用门访问的非一致代码段:DPL 规定了有权访问该段的最高特权级。例,一个一致代码段的 DPL 为2,那么运行在 CPL 为0,1的程序无权访问此段。
RPL(Request Privilege Level):RPL 是通过选择子的第0,1位表现出来的,处理器通过检查 CPL 和 RPL来确认一个访问请求是否合法。
一致代码段与非一致代码段
一致代码段:通俗的讲,一致代码段就是系统用来共享、提供给低特权级的程序使用调用的代码。
非一致代码段:为了避免被低特权级程序访问而被系统保护起来的代码。
一致代码段限制
特权级高的程序不允许访问特权级低的数据,即核心态程序不能访问用户态数据。
特权级低的程序可以访问特权级高的程序,但是特权级不会因此而改变。
非一致代码段限制
只允许同级之间访问
不允许不同级之间访问,核心态不能访问用户态,用户态也不能访问核心态
通常低特权值代码必须通过『门』来完成对高特权值代码的调用
为什么要定义一致代码段与非一致代码段?
定义这个概念主要是为了系统安全:内核要和用户程序分开,内核一定要安全不能被用户程序干涉。但有时候用户程序也需要读取内核的某些数据。于是操作系统内核程序开辟一些可以供用用户程序访问的段,但是不允许用户程序写入数据。内核不用知道用户程序的数据,内核不用调用用户程序的数据,内核不用转移到用户程序中来。用户程序只能访问到内核的某些共享的段,我们称这些段为一致代码段。用户程序不能访问内核不共享的段。
门描述符
门描述符结构
调用门的使用方式
门描述符的实现
; 门描述符
; 4个参数:
; 1.选择子:16位
; 2.偏移量:32位
; 3.DCount
; 4.属性
%macro Gate 4 dw (%2 & 0FFFFh) ;取参数2的低16位填充一个 WORD dw %1 ;取参数1填充一个 WORD dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性 dw ((%2 >> 16) & 0FFFFh) ; 偏移2
%endmacro ; 共 8 字节
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
不同特权级代码段的转移
使用 jmp 和 call 可以实现下列4种转移:
目标操作数包含目标代码段的段选择子
目标操作数指向包含目标代码段段选择子的门描述符
目标操作数指向包含目标代码段段选择子的 TTS(Task-State Stack)
目标操作数指向一个任务门,这个任务门指向包含目标代码段段选择子对 TTS
这四种方式可以分为两类:一是,通过 call 或 jmp 的直接转移,二是,通过某个描述符的间接转移
通过 call 和 jmp 直接转移
目标代码段 | 条件 | CPL 变化 |
---|---|---|
非一致代码段 | CPL=DPLRPL<=DPL | 当转移到目标代码段时,CPL=DPL |
一致代码段 | CPL>DPL,RPL 不做检查 | 当转移到目标代码段时,CPL 会延续下来 |
通过门描述符的转移
假设我们想由代码A转移到代码B,运用一个调用门G,即调用门G中的目标选择子指向代码B的段。实际上,这个问题主要涉及这几个元素:CPL、RPL、代码B的DPL(记做DPL_B),调用门G的DPL(记做DPL_G)。
目标代码段 | call | jmp |
---|---|---|
非一致代码段 | CPL<=DPL_GRPL<=DPL_GDPL_B<=CPL | CPL<=DPL_GRPL<=DPL_GDPL_B=CPL |
一致代码段 | CPL<=DPL_GRPL<=DPL_GDPL_B<=CPL | CPL<=DPL_GRPL<=DPL_GDPL_B<=CPL |
通过调用门和 call 指令,可以实现低特权级到高特权级的转移,无论目标代码段是一致的还是非一致的
通过门调用和 jmp 指令,如果目标代码段是一致的,则可以实现低特权级到高特权级的转移;如果是非一致的,那么只能实现相同特权级的转移
版权声明:本文为博主原创文章,未经博主允许不得转载。
文章来源:http://blog.luoyuanhang.com
文章来源: blog.csdn.net,作者:冰水比水冰,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/luoyhang003/article/details/47754401
- 点赞
- 收藏
- 关注作者
评论(0)