进入保护模式(二)——《x86汇编语言:从实模式到保护模式》读书笔记14
首先来段题外话:之前我发现我贴出的代码都没有行号,给讲解带来不便。所以从现在起,我要给代码加上行号。我写博客用的这个插入代码的插件,确实不支持自动插入行号。我真的没有找到什么好方法,无奈之下,只能按照网友的说法,在VIM中给每行代码加上行号,然后再贴出来。
在VIM中每一行都添加上行号的方法是:
:%s/^/\=line(".")/
对,只要执行这个命令就可以了。至于为什么这样写,可以参考我的另一篇博文
《在VIM中添加行号的方法》http://blog.csdn.net/longintchar/article/details/50569851
我们接着上篇博文 进入保护模式(一)——《x86汇编语言:从实模式到保护模式》读书笔记12 说。
(五)设置PE位
44 cli ;保护模式下中断机制尚未建立,应
45 ;禁止中断
46 mov eax,cr0
47 or eax,1
48 mov cr0,eax ;设置PE位
第44行,用于关中断。因为保护模式下的中断和实模式不同,所以原来的中断向量表不再适用,BIOS中断也不能再用,因为它们都是实模式下的代码。在重新配置保护模式下的中断环境之前,我们必须关中断。
CR0是处理器内部的一个控制寄存器,也是32位的(如下图,图片来自赵炯的《Linux内核完全剖析》)。
它的bit0是保护模式允许位(Protection Enable,PE)。当PE=1时,则处理器进入保护模式。
第46~48用于设置CR0的bit0为1.
(六)关于段寄存器
我们知道,32位模式下,段寄存器有CS,DS,ES,SS,FS,GS. 这些段寄存器每个都分为2个部分,一个是16位的可见部分,一个是隐藏部分,称为描述符高速缓存器,用来存放段的线性基地址、段界限和段属性。如下图:
1.实模式下的内存访问
在32位处理器上的实模式下,假如执行下面的代码。
mov cx,0x2000
mov ds,cx
mov [0xc0],al
CPU在把0x2000传送到DS的同时,还会把0x2000左移4位(0x20000),传送到DS描述符高速缓存寄存器(段基地址部分仅低20位有效,高12位全部是0)。此后,只要不改变DS的内容,那么每次访问内存都直接使用DS描述符高速缓存寄存器的内容作为段地址。
2.保护模式下的内存访问
在保护模式下,实模式的6个段寄存器叫做“段选择器”。尽管在访问内存的时候也要指定一个段,但是传送到段选择器的内容不是逻辑段地址,而是段选择子(也叫段选择符)。
如下图(图片来自赵炯的《Linux内核完全剖析》)所示,段选择子由三部分组成。
- 请求特权级RPL(Requested Privilege Level):提供了段保护信息,我们以后会学习。现在只需设置为00即可。
- 表指示标志TI(Table Index):TI=0时,表示描述符在GDT中;TI=1时,表示描述符在LDT(我们以后会学习)中。
- 索引值(Index):描述符在GDT或者LDT中的索引项号。
为了说明保护模式下的内存访问,我们回到代码。
56 mov cx,00000000000_10_000B ;加载数据段选择子(0x10)
57 mov ds,cx
58
59 ;以下在屏幕上显示"Protect mode OK."
60 mov byte [0x00],'P'
61 mov byte [0x02],'r'
62 mov byte [0x04],'o'
63 mov byte [0x06],'t'
64 mov byte [0x08],'e'
65 mov byte [0x0a],'c'
66 mov byte [0x0c],'t'
第56、57行,将段选择子10000b传到段选择器DS中,从段选择子可以看出,RPL=0;TI=0(表示GDT);索引号为2;
当处理器执行任何改变段选择器的指令时(比如mov、jmp far、call far、iret、retf等),就将指令中提供的索引号*8作为偏移地址,同GDTR寄存器中的线性基地址相加,然后访问GDT。如果没有什么问题(比如超过了GDT的界限),就把找到的描述符加载到不可见的描述符高速缓存寄存器。此后,每当有访问内存的指令时,就不再访问GDT中的描述符,而是直接使用段寄存器的描述符高速缓存寄存器。
结合代码来说,第57行,处理器把2*8(=16)作为偏移地址,同GDTR的内容(内容为0x00007e00)相加,得到0x0000_7e16,根据这个地址找到描述符(就是我们之前创建的#2描述符)
27 ;创建#2描述符,保护模式下的数据段描述符(文本模式下的显示缓冲区)
28 mov dword [bx+0x10],0x8000ffff
29 mov dword [bx+0x14],0x0040920b
然后,把这个描述符加载到高速缓存寄存器(包括线性基地址0x000b8000,段界限,段属性)。
第60行,执行这条指令时,处理器用DS描述符高速缓存寄存器中的线性基地址(0x000b8000,文本模式的显存起始地址)加上指令中的偏移量0x00,形成32位的物理地址0x000b8000,并将字符‘P’写入该处。
不仅仅是访问数据段,处理器访问代码段取指令的时候,也是采用相同的方法。假设CS描述符高速缓存寄存器已经装载了正确的32位线性基地址,那么处理器取指令的时候,会使用CS描述符高速缓存寄存器中的32位线性基地址加上EIP中的偏移量,构成32位的物理地址,根据这个物理地址从内存中取得指令。
(七)清空流水线并串行化处理器
正如前文所述,即使在实模式下,段寄存器的高速缓存寄存器也被用于访问内存。当处理器进入保护模式后,高速缓存寄存器的内容依然残留,但是这些内容在保护模式下是无效的。因此,比较安全的做法是尽快刷新段选择器,包括描述符高速缓存寄存器。
另外,在进入保护模式之前,很多指令已经进入了流水线。因为处理器工作在实模式下,所以它们都是按照16位操作数和地址长度进行译码的,即使是那些用bits32编译的指令,为了防止执行结果不正确,所以必须清空流水线。还用,那些通过乱序执行得到的中间结果也是无效的,所以必须清理掉,让处理器串化执行。
为了达到上述目的,我们可以采用远转移指令jmp或者远过程调用指令call。遇到这类指令,处理器一般会清空流水线并且串化执行;另一方面,远转移会重新加载CS,并刷新描述符高速缓存寄存器的内容。所以,强烈建议在设置了PE位后,立刻用jmp或者call转移到当前指令流的下一条指令上。
于是代码中有:
50 ;以下进入保护模式... ...
51 jmp dword 0x0008:flush ;16位的描述符选择子:32位偏移
52 ;清流水线并串行化处理器
53 [bits 32]
54
55 flush:
56 mov cx,00000000000_10_000B ;加载数据段选择子(0x10)
57 mov ds,cx
第51行,是一条远转移指令。如果你忘记了jmp的用法,没有关系,可以参考我的另一篇博文8086处理器的无条件转移指令——《x86汇编语言:从实模式到保护模式》读书笔记13。
这条指令和位于它前面的指令一样,是默认用[bits 16]编译的。但是因为使用了关键字dword(注意:这里的dword是修饰偏移地址flush的),所以编译后的偏移地址是32位的。
如果51行这样写:
51 jmp 0x0008:flush ;16位的描述符选择子:16位偏移
这样写是不严谨的。因为这样编译出来的目标地址是16位的。如果flush代表的地址是0x12345678,那么编译后会被截断成为0x5678,这显然是错的。所以这个跳转一定要加dword.
注意:因为设置了PE位,所以现在已经处于保护模式下了。所以处理器会把第一个操作数(0x0008)理解为段选择子,而不是是模式下的逻辑段基址。当51行的指令执行时,处理器会把选择子0x0008(索引号为1,TI=0,RPL=00)加载到CS,并把#1描述符(定义了一个代码段,基地址是0x7c00,段界限是0x1ff,长度为0x200)加载到CS描述符高速缓存寄存器中。所以程序会转移到基地址为0x0000_7c00的代码段内的某个位置执行。这个位置取决于偏移地址。偏移地址就是标号flush的汇编地址(因为指定了dword,所以编译后是32位的),处理器会用这个32位的数值来代替EIP的原有内容。于是,程序就转移到flush处了。
第53行,使用了伪指令[bits 32],从这以后,指令是按照32位编译的。因为指令执行到这里的时候,已经真真正正地进入了保护模式了。
(八)进入保护模式的主要步骤
我们总结一下进入保护模式的主要步骤:
1.安装段描述符,构造GDT
2.用lgdt指令加载GDTR
3.打开A20
4.设置CR0的PE位为1
5.跳转,真正进入保护保护模式。
(九)在屏幕上显示字符
55 flush:
56 mov cx,00000000000_10_000B ;加载数据段选择子(0x10)
57 mov ds,cx
58
59 ;以下在屏幕上显示"Protect mode OK."
60 mov byte [0x00],'P'
61 mov byte [0x02],'r'
62 mov byte [0x04],'o'
63 mov byte [0x06],'t'
64 mov byte [0x08],'e'
65 mov byte [0x0a],'c'
66 mov byte [0x0c],'t'
67 mov byte [0x0e],' '
68 mov byte [0x10],'m'
69 mov byte [0x12],'o'
70 mov byte [0x14],'d'
71 mov byte [0x16],'e'
72 mov byte [0x18],' '
73 mov byte [0x1a],'O'
74 mov byte [0x1c],'K'
56、57行,前文已经说过,令DS指向文本模式的显示缓冲区。
60~74行,就是在屏幕左上角显示"Protect mode OK." 需要说明的是:不管是实模式还是保护模式,外围设备是不受影响的。
最后注意一点:
保护模式下,不允许使用mov指令改变段寄存器CS的内容。比如
mov cs,ax
这样写是不对的。这样做会导致处理器产生一个无效操作码的异常中断。
进入保护模式(二)——《x86汇编语言:从实模式到保护模式》读书笔记14的更多相关文章
- ASM:《X86汇编语言-从实模式到保护模式》第14章:保护模式下的特权保护和任务概述
★PART1:32位保护模式下任务的隔离和特权级保护 这一章是全书的重点之一,这一张必须要理解特权级(包括CPL,RPL和DPL的含义)是什么,调用门的使用,还有LDT和TSS的工作原理(15章着重 ...
- ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构
★PART1:32位的x86处理器执行方式和架构 1. 寄存器的拓展(IA-32) 从80386开始,处理器内的寄存器从16位拓展到32位,命名其实就是在前面加上e(Extend)就好了,8个通用寄存 ...
- ASM:《X86汇编语言-从实模式到保护模式》第11章:进入保护模式
★PART1:进入保护模式 1. 全局描述符表(Global Descriptor Table,GDT) 32位保护模式下,如果要使用一个段,必须先登记,登记的信息包括段的起始地址,段的 ...
- 【weixin】微信支付---Native支付模式二(PC端支付大多采用此模式)
[模式二]:商户后台系统调用微信支付[统一下单API]生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易.注意:该模式的预付单有效期为2小时,过期后无法支付 模式二与模式一相比, ...
- ASM:《X86汇编语言-从实模式到保护模式》第15章:任务切换
15章其实应该是和14章相辅相成的(感觉应该是作者觉得14章内容太多了然后切出来了一点).任务切换和14章的某些概念是分不开的. ★PART1:任务门与任务切换的方法 1. 任务管理程序 14章的时候 ...
- ASM:《X86汇编语言-从实模式到保护模式》第8章:实模式下硬盘的访问,程序重定位和加载
第八章是一个非常重要的章节,讲述的是实模式下对硬件的访问(这一节主要讲的是硬盘),还有用户程序重定位的问题.现在整理出来刚好能和保护模式下的用户程序定位作一个对比. ★PART1:用户程序的重 ...
- ASM:《X86汇编语言-从实模式到保护模式》第9章:实模式下中断机制和实时时钟
中断是处理器一个非常重要的工作机制.第9章是讲中断在实模式下如何工作,第17章是讲中断在保护模式下如何工作. ★PART1:外部硬件中断 外部硬件中断是通过两个信号线引入处理器内部的,这两条线分别叫N ...
- ASM:《X86汇编语言-从实模式到保护模式》第16章:Intel处理器的分页机制和动态页面分配
第16章讲的是分页机制和动态页面分配的问题,说实话这个一开始接触是会把人绕晕的,但是这个的确太重要了,有了分页机制内存管理就变得很简单,而且能直接实现平坦模式. ★PART1:Intel X86基础分 ...
- ASM:《X86汇编语言-从实模式到保护模式》1-4章:处理器,内存和硬盘基础
其实很久之前就学完了实模式了,但是一直没有总结,感觉现在直接在书上做笔记的弊端就是有些知识点不能很很深刻地记下来(毕竟手写最明显的优点就是能深刻地记住知识,但是就是用太多的时间罢了).一下内容都是一些 ...
随机推荐
- OSI结构和TCP/IP模型
TCP/IP层次模型共分为五层:应用层HTTP.传输层TCP.网络层IP.数据链路层Data-link.物理层physical. 应用层—应用层是所有用户所面向的应用程序的统称.ICP/IP协议族在这 ...
- java动态代理类
很有意思的一个东西,在java.lang.reflect包下 示例代码 package com.guangshan.test.proxy; import java.lang.reflect.Invoc ...
- HTML中的内容总结
一.URL编码类型 对于Get方法,参数是直接通过URL传递的,那这个参数又是根据什么进行编码的呢?对于JSP网页,这个编码是通过第一句描述: <%@ page language="j ...
- DATEADD和DATEDIFF
DateAdd函数 返回 返回包含一个日期的 Variant (Date),这一日期还加上了一段时间间隔. 语法 DateAdd(interval, number, date) DateAdd 函数语 ...
- OI树上问题 简单学习笔记
判断链 每个点的度数不超过2 判断树 n个点,n-1条边 每两个点之间的路径唯一 多叉树转换成二叉树 第一个孩子作为左孩子,第一个孩子的兄弟作为它的右孩子. 树的重心 树上一点,满足删除该点时,树内剩 ...
- Mysql数据操作《一》数据的增删改
插入数据INSERT 1. 插入完整数据(顺序插入) 语法一: INSERT INTO 表名(字段1,字段2,字段3…字段n) VALUES(值1,值2,值3…值n); 语法二: INSERT INT ...
- django admin后台的简单使用
创建自己的model.py文件 from django.db import models from django.contrib.auth.models import ( BaseUserManage ...
- jmeter处理json(关联)
例:用户需要登录成功后才可进行充值,进行充值操作时需要获取登录成功返回的sign值,在jmeter中可以通过关联的方式进行处理. jmeter中json path插件的使用方法:http://www. ...
- scrapy框架基于CrawlSpider的全站数据爬取
引入 提问:如果想要通过爬虫程序去爬取”糗百“全站数据新闻数据的话,有几种实现方法? 方法一:基于Scrapy框架中的Spider的递归爬取进行实现(Request模块递归回调parse方法). 方法 ...
- [ActionScript 3.0] 创建倒影
package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Display ...