第12章 进入保护模式

news2024/12/23 18:58:48

第12章 进入保护模式

章节一开始说明了为什么要有保护模式?后续介绍了如何进入保护模式。

实模式:在实模式下,程序是可以“为所欲为”的。它想访问内存的哪一部分,都可以很轻松地通过设置段地址和偏移地址来办到。

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

代码清单12-1

该章代码的功能:程序运行后从实模式进入保护模式,并在屏幕上输出字符:Protect Mode OK.
image

全局描述符表

段描述符(Segment Descriptor):每个段都需要一个描述符,来描述段的信息,长度为8字节。段的信息包括起始地址,段的界限等各种访问属性。

描述符表(Descriptor Table):所有段描述符都集中放在一起,称为描述符表。

全局描述符表(Global Descriptor Table,GDT):该表是为整个软硬件系统服务的。

后面还有一个局部描述符表是为(Local Descriptor Table,LDT)是为单个应用服务的。

全局描述符表寄存器(GDTR):处理器内部用于跟踪全局描述符表的48位寄存器。该寄存器包含两个部分:

  • 32位的线性地址:范围0x00000000到0xFFFFFFFF,2^32次方,4GB内存的范围。
  • 16位的边界:最大2^16字节,每个描述符占8个字节,所以总共可以存储8192个描述符。

image

定义GDT:进入保护模式之前需要定义GDT,在实模式下只能访问1MB的内存,所以GDT一般定义在1MB以下的内存范围。

image

存储器的段描述符

段描述符格式:每个描述符在GDT中占8字节(64位),分为低32位和高32位。

image

  • 段基地址:32位,可以是0~4GB的任意范围;未开启分页功能,就是物理地址。
  • 段界限:20位,指定段的边界,也就是大小,范围:0x00000~0xFFFFF。
    • 偏移量从0开始:代码段、数据段和栈段,偏移量从0开始,偏移量的最大值就是段段界限。
    • 偏移量从最大值开始:专为栈段设计,段界限就为段内不可使用的最小偏移量。
  • G位:粒度(Granularity)位,用于解释段界限的含义。
    • 0:段界限以字节为单位,段的扩展范围是从1字节到1兆字节(1B~1MB)。
    • 1:界限是以4KB为单位,的扩展范围(段的大小)是从4KB到4GB)。
  • D/B位:默认操作尺寸”(Default Operation Size)或者“默认的栈指针尺寸”(Default Stack Pointer Size),又或者“上部边界”(Upper Bound)标志。主要是为了兼容16位保护模式。
  • L位:4位代码段标志(64-bit Code Segment),保留此位给64位处理器使用。
  • AVL位:软件可以使用的位(Available),通用由操作系统安排。
  • P位:段存在位(Segment Present),用于指示描述符所对应的段是否存在。
  • DPL:描述符的特权级(Descriptor Privilege Level, DPL),分别是0、1、2、3,其中0是最高特权级别,3是最低特权级别。
  • S位:指定描述符的类型(Descriptor Type)。
    • 0:系统段。
    • 1:码段或者数据段(栈段也是特殊的数据段)
  • TYPE位:用于指示描述符的子类型,有数据和代码两种。

image

数据类型的TYPE位

image

代码类型的TYPE位

书中一开始设置了堆栈段和栈指针、GDT。相关段:

  • 代码段段地址:0x0000。
  • 栈段段地址:0x0000,栈指针 0x7c00,向下扩展。
  • GDT:0x07e0,实际物理地址从 0x00007e00 开始,最大偏移地址:0x00007e00 + 0x0000FFFF = 0x00017DFF,总计64KB的空间。

代码我加了一些注释:

;设置堆栈段和栈指针
mov ax, cs         ;cs代码段为0x0000
mov ss, ax         ;ss栈段位0x0000
mov sp, 0x7c00     ;sp栈指针0x7c00,向下扩展

......
......

gdt_size         dw 0                          
gdt_base         dd 0x00007e00               ;GDT的物理地址

此时内存映像:
image

为什么GDT保存在 0x00007e00 这个位置?

主引导扇区程序起始地址为0x00007c00,共512(0x200)字节。
结束地址就是:0x00007c00 + 512 = 0x00007e00

安装存储器的段描述符并加载GDTR

计算GDT所在的逻辑段地址和偏移地址:代码我加了一些注释。

;计算GDT所在的逻辑段地址
mov ax, [cs: gdt_base + 0x7c00]           ;低16位, 值为 0x7e00
mov dx, [cs: gdt_base + 0x7c00 + 0x02]    ;高16位, 值位 0x0000
mov bx, 16                                ;段地址要16位对齐
div bx                                    ;ax存储商,dx存储余数  

mov ds, ax                                ;令DS指向该GDT段以进行操作
mov bx, dx                                ;段内起始偏移地址

......
......

gdt_size         dw 0                          
gdt_base         dd 0x00007e00               ;GDT的物理地址

这里之所以要加上0x7c00,是因为目前代码段cs地址为:0x0000。

安装空描述符:处理器规定,GDT中的第一个描述符必须是空描述符,或者叫“哑描述符”、NULL描述符。

;创建0#描述符,它是空描述符,这是处理器的要求
mov dword [bx+0x00],0x00   ;低32位
mov dword [bx+0x04],0x00   ;高32位

创建1#描述符:进入保护模式要在屏幕上显示一行文本,显示文本需要访问显存,要访问显存就必须将它定义成一个段,并创建一个描述符。

;创建#1描述符,保护模式下的数据段描述符(文本模式下的显示缓冲区)
mov dword [bx+0x08],0x8000ffff     ;低32位
mov dword [bx+0x0c],0x0040920b     ;高32位

该描述符在内存中的映像:

image

根据段描述符的格式,可以分析得出:

  • 线性基地址:0x000B8000;
  • 段界限:0x0FFFF;
  • 粒度为字节(G=0),即,该段的长度为64KB;
  • 属于存储器的段(S=1);
  • 这是一个32位的段(D=1);
  • 该段目前位于内存中(P=1);
  • 段的特权级为0(DPL=00);
  • 这是一个可读可写、向上扩展的数据段(TYPE=0010);

初始化描述符表寄存器GDTR:加载描述符表的线性基地址和界限到寄存器GDTR,这要使用lgdt指令。

lgdt m ;有效地址m处,包含了GDT的32位线性地址和16位界限值,共6字节。
       ;  前(低)16位是GDT的界限值。
       ;  后(高)32位是GDT的基地址。

相关代码:

;初始化描述符表寄存器GDTR
mov word [cs: gdt_size+0x7c00],15  ;描述符表的界限(总字节数减一)

lgdt [cs: gdt_size+0x7c00]         ;将gdt_size开始的6个字节加载到gdtr

......
......

gdt_size         dw 0   
gdt_base         dd 0x00007e00               ;GDT的物理地址

关于第21条地址线A20的问题

回绕特性:8086只有20根地址线,当物理地址达到最高端0xFFFFF时,再加1,结果为0x100000。但因为它只能维持20位的地址,故进位自然丢失,地址又绕回最低地址端0x00000。

20根地址线最大地址 + 1
    = 0xFFFFF + 0x1 
    = 0x100000 (因为只有20根地址线,高位丢弃)
    = 0x000000

A20控制策略:80286有24条地址线,地址回绕就不行了。为了兼容8086,强制第21根地址线恒为0,通过键盘控制器接口进行设置。
image

改进后的A20控制策略:80486有了A20M#引脚(A20屏蔽,A20 Mask),通过输入输出控制器集中芯片ICH的端口0x92设置A20线。
image

书中涉及的代码是这三行:

in al,0x92        ;南桥芯片内的端口,读取0x92端口的数据
or al,0000_0010B  ;第2位(位1)置为1,表示打开A20线
out 0x92,al       ;写回0x92端口,打开A20

保护模式下的内存访问

CR0寄存器:CR0是处理器内部的控制寄存器(Control Register, CR)。是32位的,包含了一系列用于控制处理器操作模式和运行状态的标志位。

它的第1位(位0)是保护模式允许位(Protection Enable, PE),为1则表示进入保护模式。
image

关中断:保护模式下,原有的中断向量表不再适用。

cli    ;保护模式下中断机制尚未建立,应禁止中断

CR0寄存器位0置1

mov eax,cr0      ;将cr0读入eax
or eax,1         ;第1位(位0)置1  
mov cr0,eax      ;写回cr0,设置PE位

在Bochs中可以通过creg命令查看控制寄存器的内容。

段寄存器在实模式和保护模式下的工作方式:在32位处理器上,段寄存器CS、DS、ES、FS、GS、SS,有相应的描述符高速缓存器,用来缓存计算过后的线性地址。
image

1)实模式:段寄存器存储的是逻辑段地址,采用段地址和偏移地址的寻址方式,实际物理地址为:段地址0x10+偏移地址。段地址0x10 的结果会保留描述符高速缓存器,下次用的时候直接从这里取就可以了。

例如:

mov cx,0x2000    ;设置ds为0x2000
mov ds,cx        
mov [0xc0],al    ;寻址:0x2000*0x10+0xc0,
                 ;0x20000 这个结果会保存在描述符高速缓冲器

2)保护模式:段寄存器存储的是段选择子。段选择子包含:描述符在描述符表中的索引号、TI、RPL。

image

设置数据段描述符选择子:将描述符选择子0x0008(二进制数0000_0000_00001_0_00)传送到段选择器DS中。

mov cx,00000000000_01_000B ;加载数据段选择子(0x08)
mov ds,cx

段选择子的工作方式:每个描述符占8字节,描述符在描述符表内的偏移地址是索引号乘以8,又因为GDT的线性基地址在GDTR中,所以描述符的实际物理地址为:

描述符的实际物理地址 = GDTR的线性基地址 + 描述符索引号*8

从GDT表中获取到段描述符后,会将段的线性基地址、段界限和段的访问属性保存到描述符高速缓存器中。后续用到该段寄存器时,就会直接从缓存器中获取内容。

画个示意图:

image

段选择子的工作方式

在屏幕上打印字符:在屏幕上显示"Protect mode OK."

;以下在屏幕上显示"Protect mode OK."
mov byte [0x00],'P'    ;默认使用ds寄存器,下同
mov byte [0x02],'r'
mov byte [0x04],'o'
mov byte [0x06],'t'
mov byte [0x08],'e'
mov byte [0x0a],'c'
mov byte [0x0c],'t'
mov byte [0x0e],' '
mov byte [0x10],'m'
mov byte [0x12],'o'
mov byte [0x14],'d'
mov byte [0x16],'e'
mov byte [0x18],' '
mov byte [0x1a],'O'
mov byte [0x1c],'K'
mov byte [0x1e],'.'

数据段访存方式:每当有访问内存的指令时,就不再访问GDT中的描述符,直接用当前段寄存器描述符高速缓存器提供线性基地址。
image

代码段访存方式:不单是访问数据段,即使在处理器取指令执行时,也采用了相同的方法。一开始执行代码的时候,处理器将逻辑段地址左移4次,形成20位地址,左侧补0,补足32位后再传送到CS段描述符高速缓存器。此后,就直接使用这个32位的线性基地址访问内存(包括取指令)。
image

为什么不用更改CS段选择器:实模式和16位保护模式都是16位的工作模式,在这两种工作模式下,指令的格式和寻址方式相同,执行相同的操作,默认的数据操作尺寸都是16位的,使用16位有效地址访问内存。

如果后面要更改CS段选择器,就要用选择子的方式了。

程序的运行和调试

运行程序并观察结果

程序运行后从实模式进入保护模式,并在屏幕上输出字符:Protect Mode OK.
image

处理器刚加电时的段寄存器状态

自测试(Build-In Self-Test, BIST):在x86处理器加电后,它的固件会对自身进行初始化,还可以选择执行一个内置的自测试。

预置值:当处理器初始化完成后,它内部的各个寄存器,包括通用寄存器、段寄存器、控制寄存器、指令指针寄存器EIP、栈指针寄存器ESP等都会有一个预置的值。

预置值查询

  1. 查阅手册:NTEL公司的手册Intel® 64 and IA-32 Architectures Software Developer’s Manual;
  2. Bochs用软件来模拟处理器的工作。

使用sreg查看段寄存器状态:
image

  • "dh"和"dl"的内容是Bochs根据段寄存器描述符高速缓存器的内容构造的。dh是段描述符的高32位;dl是段描述符的低32位。
  • “Data segment” 表示该段是数据段;
  • “base” 指示段的基地址;
  • “limit” 指示段的界限;
  • “Read/Write” 表示段可读可写;
  • “Accessed” 指示段曾经被访问过。

CS预置值含义:CS段描述符高速缓存器中的基地址被预置为0xFFFF0000,EIP的预置内容是0x0000FFF0,所以处理器第一次取指令时发出的地址是0xFFFFFFF0。

处理器的设计者希望把ROM-BIOS放到4GB可寻址内存范围的最高端,4GB以下,连同传统的低端1MB都是连续的RAM区,连续的、不间断的RAM能为操作系统管理内存带来方便。

兼容低端1MB的ROM-BIOS:执行的第一条指令: jmp far f000:e05b(jmp 0xf000:0xe05b),CS段描述符高速缓存器中的基地址就变成了 0x000F0000 ,就转到低地址端的BIOS执行了。

设置PE位后的段寄存器状态

刚设置好PE位时,尽管进入了保护模式,但显示的依然是实模式。

image

代码段寄存器CS描述符高速缓存器的dh为0x00009300,即,G=0,D=0,L=0,P=1,DPL=00,S=1,TYPE=0011。通俗地说,这是一个粒度为字节的数据段。

刚进入保护模式后,CS描述符高速缓存器中的D位是0,所以是工作在16位保护模式下。只要处理器执行的是16位代码,就不会有问题。

加载段寄存器DS之后的状态

进入保护模式后,用段选择子0x0008来加载段寄存器DS,处理器用这个选择子从全局描述符表GDT中取出对应的描述符,并刷新DS描述符高速缓存器。

image

为什么ds的dh是0040930b?正常应该是0040920b?

前面都还正常,执行了 mov ds,cx 之后就变成 0040930b 了。
image

在操作过程中,发现这个值变成了0040930b,前面设置的时候是0040920b,怎么变了呢?后面查询段描述符结构才发现是描述符TYPE位的Access标志被置为1了,所以加1就变成3了。
image

查看全局描述符表GDT

查看指令:info gdt
image

  • 第一行:GDT的线性地址基地址(0x7e00)及界限值(15),
  • 第二行开始:Bochs给出了每个描述符的索引和相关信息。
    • 第一个表项的索引号是0,它是空描述符;
    • 第二个表项的索引号是0x0008,在中括号里给出了。在这一行里,“Data segment”表明这是数据段的描述符;“base”表示段的32位基地址;“limit”表示段界限值;“Read/Write”表示段是可读可写的;“Accessed”表示段被访问过。

查看控制寄存器的内容

查看指令:creg
image

CR0的内容:0x60000011,为了方便起见,Bochs以大小写的形式给出了各个控制位的状态,小写表示该位是“0”,大写表示该位是“1”,即处于置位状态。图中显示了大写的“PE”,表明此位是“1”,处于保护模式。

本章习题

如果一个数据段描述符的G位是1,E位是0,段的基地址为0,段界限为1,该段一共有多少字节?段的线性地址范围是什么?

答:根据段描述符格式可以得出:

  • G=1表示界限是以4KB为单位。
  • E=0这里应该是D位?

该段一共有 1*4KB = 4KB,段的线性地址范围是:0x00000000 ~ 0x00001000 ,4KB的空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2128013.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Plasma FX

Plasma FX是使用自定义着色器的等离子效果的集合。 通过调整粒子系统参数和颜色,可以轻松自定义效果。 特征 50+效果 定制化 等离子粒子着色器 演示场景 移动预制件 下载:​​Unity资源商店链接资源下载链接 效果图:

nodejs基础教程之-异步编程promise/async/generator

1. 异步 所谓"异步",简单说就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段,比如,有一个任务是读取文件进行处理,异步的执行过程就是下面…

C#获取计算机信息

目录 效果 项目 代码 下载 效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Management; n…

基于SSM的二手物品交易管理系统的设计与实现 (含源码+sql+视频导入教程+文档+PPT)

👉文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的二手物品交易管理系统7拥有两种角色 管理员:用户管理、分类管理、商品管理、订单管理、系统管理等 用户:登录注册、充值、收货、评价、收藏、购物车、订…

vue + Element UI table动态合并单元格

一、功能需求 1、根据名称相同的合并工作阶段和主要任务合并这两列,但主要任务内容一样,但要考虑主要任务一样,但工作阶段不一样的情况。(枞向合并) 2、落实情况里的定量内容和定性内容值一样则合并。(横向…

【机器学习】马尔可夫随机场的基本概念、和贝叶斯网络的联系与对比以及在python中的实例

引言 马尔可夫随机场(Markov Random Field,简称MRF)是一种用于描述变量之间依赖关系的概率模型,它在机器学习和图像处理等领域有着广泛的应用 文章目录 引言一、马尔科夫随机场1.1 定义1.2 特点1.3 应用1.4 学习算法1.5 总结 二、…

使用 Apache Spark 和 Deequ 分析大数据集

在当今的数据驱动环境中,掌握使用 Apache Spark 和 Deequ 对大型数据集进行分析对于任何处理数据分析、SEO 优化或需要深入研究数字内容的类似领域的专业人士来说都至关重要。 Apache Spark 提供处理大量数据所需的计算能力,而 Deequ 提供质量保证层&am…

杨氏矩阵中查找某个数字是否存在(不能使用遍历)

杨氏矩阵&#xff1a; 有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的 如图所示&#xff1a; i为行&#xff0c;j为列 如果要找9&#xff0c;先从arr【0】【2】处开始找&#xff0c;3<9,i,排除第一行&#xff0c;6<9,i,排除第…

基于SSM和VUE的药品管理系统(含源码+sql+视频导入教程+文档)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM和VUE的药品管理系统2拥有两种角色 管理员&#xff1a;药品管理、出库管理、入库管理、销售员管理、报损管理等 销售员&#xff1a;登录注册、入库、出库、销售、报损等 1.1 背景…

c++习题31-阶乘和

目录 一&#xff0c;题目 二&#xff0c;思路 三&#xff0c;代码 一&#xff0c;题目 描述 用高精度计算出 S1!2!3!…n!&#xff08;n≤50&#xff09;,其中“!”表示阶乘&#xff0c;例如&#xff1a;5!54321。 输入正整数n&#xff0c;输出计算结果S。 输入描述 …

微信公众号管理系统-需求整理

微信公众号管理系统-需求整理 文章目录 一、拟开发功能1.公众账号管理(必须)2.菜单管理(必须)2.1 跳转网页(view)2.2 发送消息(media_id)2.3 永久素材管理2.4 打开指定小程序(miniprogram)2.5 自定义点击事件(click)3.权限控制(必须)二、参与人员三、开发周期四、环境及…

二叉树的层序遍历-广度优先遍历

正常来讲二叉树的层序遍历 我们 使用递归 &#xff0c;来进行 就可以得到正确答案&#xff0c;但是有时候递归比较难以理解&#xff0c;我们今天用队列的形式 来进行二叉树的层序遍历 我们使用队列对二叉树进行层序遍历的核心思想有两个 1. 我们用队列 记录二叉树每一层的…

240908-结合DBGPT与Ollama实现RAG本地知识检索增强

A. 最终效果 B. 背景说明 DBGPT在0.5.6版本中开始支持Ollama&#xff1a;v0.5.6 版本更新 网友对其Web端及界面端的设置进行了分享&#xff1a; feat(model): support ollama as an optional llm & embedding proxy by GITHUBear Pull Request #1475 eosphoros-ai/DB-G…

【新手必看】Windows 上安装 Minio及简单使用

一&#xff0c;minio是什么&#xff1f; MinIO 是一个高性能、分布式的对象存储系统&#xff0c;专门用于存储大量非结构化数据&#xff0c;例如图片、视频、日志文件、备份等&#xff0c;且一个对象文件可以是任意大小&#xff0c;从几kb到最大5T不等。Minio是一个非常轻量的服…

【数学建模】2024年国赛成绩公布时间预测

经过三天三夜的奋战&#xff0c;2024年数学建模国赛也宣告结束&#xff0c;相信这几天超高强度的比赛也让大家精疲力竭了&#xff0c;所以经过几天的调整&#xff0c;大家都恢复过来了吧。能够在高强度的压力下坚持这么久&#xff0c;大家都超级厉害的&#xff01; 当然完整提交…

如何更好地解释你的想法并说服他人

这篇内容讨论了如何更有效地解释你的思想和想法&#xff0c;并说服他人。提出了几个原则和策略&#xff0c;如意想不到、简单性、具体化、故事性和情感。 中文 1 我最近读了一本关于有效沟通的书&#xff0c;名为《让你的想法更具说服力》&#xff08;《创意黏力学》&#x…

僵尸网络开发了新的攻击技术和基础设施

臭名昭著的 Quad7 僵尸网络&#xff08;也称为 7777 僵尸网络&#xff09;不断发展其运营&#xff0c;最近的发现表明其目标和攻击方法都发生了重大变化。 根据 Sekoia.io 的最新报告&#xff0c;Quad7 的运营商正在开发新的后门和基础设施&#xff0c;以增强僵尸网络的弹性&a…

ISAC: Toward Dual-Functional Wireless Networks for 6G and Beyond【论文阅读笔记】

此系列是本人阅读论文过程中的简单笔记&#xff0c;比较随意且具有严重的偏向性&#xff08;偏向自己研究方向和感兴趣的&#xff09;&#xff0c;随缘分享&#xff0c;共同进步~ Integrated Sensing and Communications: Toward Dual-Functional Wireless Networks for 6G and…

让人物照片跳舞vigen追影

本质上是一种视频换脸技术的升级版&#xff0c;视频换身。 项目体验地址&#xff1a;》》》魔搭社区《《《 选择“创作视频” 1.上传一张全身照片 2.选择一个包含动作的视频 3.点击“开始生成” 看效果 dreamoving-v1-2024-09-08-1000017-888139-result 动作的表现力还是很惊…

通过Docker实现openGauss的快速容器化安装

容器安装 本章节主要介绍通过 Docker 安装 openGauss&#xff0c;方便 DevOps 用户的安装、配置和环境设置。 支持的架构和操作系统版本 x86-64 CentOS 7.6 ARM64 openEuler 20.03 LTS 配置准备 使用 buildDockerImage.sh 脚本构建 docker 镜像&#xff0c;buildDockerIm…