操作系统——认识保护模式

操作系统——认识保护模式

实验目的及内容

  1. 认真阅读章节资料,掌握什么是保护模式,弄清关键数据结构:GDT、descriptor、selector、GDTR, 及其之间关系,阅读pm.inc文件中数据结构以及含义,写出对宏Descriptor的分析
  2. 调试代码,/a/ 掌握从实模式到保护模式的基本方法,画出代码流程图,如果代码/a/中,第71行有dword前缀和没有前缀,编译出来的代码有区别么,为什么,请调试截图。
  3. 调试代码,/b/,掌握GDT的构造与切换,从保护模式切换回实模式方法。
  4. 调试代码,/c/,掌握LDT切换。
  5. 调试代码,/d/掌握一致代码段、非一致代码段、数据段的权限访问规则,掌握CPL、DPL、RPL之间关系,以及段间切换的基本方法。
  6. 调试代码,/e/掌握利用调用门进行特权级变换的转移。

实验器材:

VMware虚拟机+Ubuntu32位

实验步骤:

1.阅读章节资料
2.调试代码a,b,c,d,e
3.根据五组代码的区别,以及其分别对应的功能进行分析。

1.a:

程序大致框架为:
在这里插入图片描述
在此之后则进入显存打印结果P;
正常执行代码pmtest1的结果如下
在这里插入图片描述
但是
在这里插入图片描述
如果将71行的这个位置的dword去除,则会导致bochs闪退报错。
很明显,这里是不能删除的,不然人家也不会写对吧,但到底是因为什么呢?
我们来查看一下编译的二进制代码,虽然只有字节显示我并不能理解其含义,但我们会发现,删除dword之后,编译出的二进制代码相比于之前会短一些。

如果使用jmp SelectorCode32:0:
这样编译出来的只是16位的代码。如果目标地址的偏移不是0,而是一个较大的值,比如jmp SelectorCode32:0x12345678,则 编译后偏移会被截断,只剩下0x5678。

2.b:

在这里插入图片描述
在这里插入图片描述
调试方式与结果如上,这个程序讲述的是从保护模式切换回实模式的方法。在之前的实验里我们了解了怎么进行从实模式到保护模式的切换,但在实验的结尾我们是以一个死循环结束,从输出也可以看出,他没有弹出一个新的DOS,而是打印完P之后就没有反应。在这个实验里,我们添加了一段代码,使得其在执行完之后可以返回到实模式,弹出一个新的DOS。
在这里插入图片描述
从这里开始,保护模式执行完成,返回到实模式中。
在这里插入图片描述
在这里插入图片描述
跳回实模式之后,程序重新设置各个寄存器的值,关闭A20,开中断。
我们看到,程序打印出两行数字,第一行全部是零,说明开始内存地址5MB处都是0,而下一行已经变成了41 42 43…,
说明写操作成功。十六进制的41、42、43、…、48正是A、B、C、…、H。

3.c:

在这里插入图片描述
结果如上,编译、调试过程与之前相似就不做截图了。
这一部分实验主要讲的是LDT的构造与运行。其实我觉得LDT和GDT并没有什么很大区别,从代码中也可以看出:
在这里插入图片描述
在这里插入图片描述
上图显示的更是极其相似,在GDT中为lgdt,在这则是lldt,其工作也是类似的,就是加载ldtr,操作的是一个选择子。因为比较类似,所以不再多做说明。

4.d:

在这里插入图片描述
结果如上。

CPL
CPL是当前执行的程序或任务的特权级。在通常情况下,CPL等于代码所在的段的特权级。在遇到一致代码段时,情况稍稍有点特殊,一致代码段可以被相同或者更低特权级的代码访问。当处理器访问一个与CPL特权级不同的一致代码段时,CPL不会被改变。

DPL
DPL表示段或者门的特权级。它被存储在段描述符或者门描述符的DPL字段中,正如我们先前所看到的那样。当当前代码段试图访问一个段或者门时,DPL将会和CPL以及段或门选择子的RPL相比较,根据段或者门类型的不同,DPL将会被区别对待:
数据段:DPL规定了可以访问此段的最低特权级。比如,一个数据段的DPL是1,那么只有运行在CPL为0或者1的程序才有权访问它。
非一致代码段(不使用调用门的情况下):DPL规定访问此段的特权级。比如,一个非一致代码段的特权级为0,那么只有CPL为0的程序才可以访问它。
调用门:DPL规定了当前执行的程序或任务可以访问此调用门的最低特权级(这与数据段的规则是一致的)。
一致代码段和通过调用门访问的非一致代码段:DPL规定了访问此段的最高特权级。比如,一个一致代码段的DPL是2,那么CPL为0和1的程序将无法访问此段。
TSS:DPL规定了可以访问此TSS的最低特权级(这与数据段的规则是一致的)。
RPL
RPL是通过段选择子的第0位和第1位表现出来的。处理器通过检查RPL和CPL来确认一个访问请求是否合法。即便提出访问请求的段有足够的特权级,如果RPL不够也是不行的。操作系统过程往往用RPL来避免低特权级应用程序访问高特权级段内的数据。当操作系统过程(被调用过程)从一个应用程序(调用过程)接收到一个选择子时,将会把选择子的RPL设成调用者的特权级。于是,当操作系统用这个选择子去访问相应的段时,处理器将会用调用过程的特权级(已经被存到RPL中),而不是更高的操作系统过程的特权级(CPL)进行特权检验。这样,RPL就保证了操作系统不会越俎代庖地代表一个程序去访问一个段,除非这个程序本身是有权限的。

上述是课本上对CPL,DPL,RPL的解释,可以说是清晰的不能再清晰了。刚开始看的时候我不是很明白什么是一致代码段,什么是非一致代码段。简单来说,一致代码段就是操作系统可以被低层访问的代码,非一致代码段就是不能拿出来访问调用的代码。理解了这个之后,我们来看看我们的实验做了什么。
在这里插入图片描述

首先我们要知道,这个小实验是想使用调用门。我们来找一找这个调用门的定义。

扫描二维码关注公众号,回复: 11373887 查看本文章

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面的图分别对应选择子和门的定义。
初始化之后,我们就直接回到他最终打印结果的部分:
在这里插入图片描述
首先,call一个门,查看这个门后可以发现,他的最后一句为retf,也就是说在call之后,会返回这个位置继续执行下一部分的,即第二个call。所以我们的实验结果是有一个C一个L的!

5.e:

在这里插入图片描述
这个阶段的实验目的为,利用门进行有特权级变换的转移。在分析代码之后我们发现出现了一个新的段ring3。初始化选择子我们跳过。
在这里插入图片描述
先来看看ring3的定义,高亮部分的代码很明显是打印一个’3’字符,并使得程序停止。
在这里插入图片描述
定义过后,依次将ss,esp,cs,eip压栈,执行retf指令。
我们看到了一个红色的3,说明我们成功进入了ring3!
成功进入ring3之后,我们来试验一下调用门的使用。
但在此之前我们别忘了TSS!
在这里插入图片描述
准备完成后,在进行特权级变换之前加载TSS。
运行之后,我们可以看到在字符’3’之前是存在其他字符的,这就意味着在ring3下对门的调用也是成功的。
最后,我们将调用局部任务的代码加入到调用门的目标代码,使得我们的程序能成功返回实模式,这也就是最终结果上显示的3个字符的原因。

6.【Descriptor结构体】有8个字节。共包含Base, Limit, Attr三个参数。

%1表示第一个参数Base(段基址)
后同。

【第1、2字节】组合(word) 表示该段的[段界限①], dw %2 & 0FFFFh ;引用第二个参数去掉高16位

【第3、4、5字节】组合表示该段的[段基址①],dw %1 & 0FFFFh ;先得到第一个参数(段基址)低WORD。接着把第5个字节赋值,db (%1 >> 16) & 0FFh 去掉第3第4个字节的内容.再把剩下的字节赋值

【第6个字节】的内容:
在这里插入图片描述
0-3位表示:[段属性]、说明存储段描述符所描述的存储段的具体属性。

4位表示:说明描述符的类型, 对于存储段描述符而言,S=1表示是系统段描述符。

5-6位表示:DPL 该段的特权级别也就是Ring 0-3;

7位表示:P: 存在位。
; P=1 表示描述符对地址转换是有效的,即描述的段在内存当中.
; P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常

【第7个字节】的内容:
在这里插入图片描述

0-3位表示:[段界限②]

4位表示:软件可利用位。80386对该位的使用未做规定,Intel公司也保证今后开发生产的处理器只要与80386兼容,就不会对该位的使用做任何定义或规定。

5位表示:0 ;Intel资料也没表示

6位表示:是一个很特殊的位,在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同,通常置1

7位表示: 段界限粒度(Granularity)位。
G=0 表示界限粒度为字节;
G=1 表示界限粒度为4K 字节。
注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。

那么这段宏dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)表示:

取[段界限]参数除去低16位取 高4位,得到【段界限②】

取[段属性]参数的低8位 12-15位(AVL属性等)

属性 1 + 段界限 2 + 属性 2

【第8个字节】的内容:

[段基址②] 、db (%1 >> 24) & 0FFh 取基地址参数的最高8位

课后思考

添加GDT段和LDT段分别进行写入和读、打印字符的功能

我选择了在原有的GDT段和LDT段进行修改,这样省去了初始化和定义选择子的步骤。
新增的GDT段
在这里插入图片描述

下面是新增的LDT用来读出之前写入的字符串
在这里插入图片描述

自定义2个GDT代码段A、B,分属于不同特权级,功能自定义,要求实现A–>B的跳转,以及B–>A的跳转
先load TSS,布置好栈,然后利用retf指令从高特权级转移到低特权级。
在这里插入图片描述
利用调用门从低特权级跳转至高特权级的代码进行运行。
在这里插入图片描述
最后结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/HizT_1999/article/details/106973865