X86架构(九)——保护模式的进入

全局描述符表

全局描述符表(Global Descriptor Table,GDT)是保护模式下非常重要的一个数据结构。

在保护模式下,对内存的访问仍然使用段地址和偏移地址,在每个段能够访问之前,必须先行设置好 GDT 的地址,并加载全局描述符表寄存器(GDTR)

和一个段有关的信息需要8 个字节来描述,所以称为段描述符(Segment Descriptor),每个段都需要一个描述符。为了存放这些描述符,需要在内存中开辟出一段空间。在这段空间里,所有的描述符都是挨在一起,集中存放的,这就构成一个描述符表

最主要的描述符表是全局描述符表(Global Descriptor Table,GDT),在进入保护模式前,必须要定义全局描述符表

处理器内部有一个48位的寄存器(全局描述符表寄存器(GDTR))用于跟踪全局描述符表。该寄存器分为两部分:
GDTR
32位线性基地址:保存全局描述符表在内存中的起始线性地址
16位边界:保存全局描述符表的边界(界限)
因为GDT的界限是16位的,所以,该表最大是65536 字节且一个描述符占8 字节,故最多可以定义8192个描述符

由于在进入保护模式之后,处理器立即要按新的内存访问模式工作,所以,必须在进入保护模式之前定义GDT。但是,由于在实模式下只能访问1MB的内存,故GDT通常都定义在1MB以下的内存范围中。允许在进入保护模式之后换个位置重新定义GDT
GDT+GDTR

从BIOS理解GDT的设置

在实模式下,主引导程序的加载位置是0x0000:0x7c00.
在32位模式下,对应着物理地址0x00007c00。主引导扇区程序共512字节,GDT可设在主引导程序之后,也就是物理地址0x00007e00处
内存分配

  1. 初始化段寄存器
	; 设置堆栈段和栈指针 mov ax, cs      mov ss, ax		; 栈基地址与代码段相同mov sp, 0x7c00	; 栈指针指向0x7c00; GDT起始线性地址
gdt_base:dd 0x00007e00; 创建段描述符(段描述符格式见下图); 此时处理器处于实模式; 计算GDT所在的逻辑段地址 ; 主引导程序的实际加载位置是逻辑地址0x0000:0x7c00; 故标号gdt_base处的偏移地址是gdt_base+0x7c00mov ax, [cs:gdt_base+0x7c00]        ;16位 mov dx, [cs:gdt_base+0x7c00+0x02]   ;16位 mov bx, 16; 32位除法; 商是逻辑段地址,余数是偏移地址     div bx; 商在ax中mov ds, ax; 余数在dx中mov bx, dx

在32位保护模式下,段地址是32位的线性地址,如果未开启分页功能,该线性地址就是物理地址
段描述符

  • 段基地址可以是0~4GB范围内的任意地址
  • 20位的段界限用来限制段的扩展范围
  • G是粒度(Granularity)位,用于解释段界限的含义。当G 位是"0"时,段界限以字节为单位,此时,段的扩展范围是从1字节到1兆字节,因为描述符中的界限值是20 位的。相反,如果该位是"1",那么,段界限是以4KB为单位的,此时,段的扩展范围是从4KB到4GB
  • S用于指定描述符的类型(Descriptor Type)。当该位是"0"时,表示是一个系统段;为"1"时,表示是一个代码段或者数据段(栈段也是特殊的数据段)
  • DPL表示描述符的特权级(Descriptor Privilege Level - DPL),共有4种处理器支持的特权级别,分别是0、1、2、3,其中0 是最高特权级别,3 是最低特权级别。刚进入保护模式时执行的代码具有最高特权级0,这些代码通常都是操作系统代码,因此它的特权级别最高
    note 描述符的特权级用于指定要访问该段所必须具有的最低特权级。如果这里的数值是2,那么,只有特权级别为0、1 和2 的程序才能访问该段,而特权级为3 的程序访问该段时,处理器会予以阻止
  • P是段存在位(Segment Present)。P位用于指示描述符所对应的段是否存在
  • D/B位是"默认的操作数大小"(Default Operation Size)或者"默认的栈指针大小"(Default Stack Pointer Size),又或者"上部边界"(Upper Bound)标志
    note 设立该标志位,主要是为了能够在32位处理器上兼容运行16位保护模式的程序(对于代码段,此位称做"D"位,用于指示指令中默认的偏移地址和操作数尺寸。D=0表示指令中的偏移地址或者操作数是16位的;D=1指示32 位的偏移地址或者操作数)
  • L位是64位代码段标志(64-bit Code Segment),保留此位给64 位处理器使用
  • TYPE字段用于指示描述符的子类型,对于数据段来说,这4位分别是X、E、W、A 位;而对于代码段来说,这4位则分别是X、C、R、A 位
    type
    对于数据段来说,E位指示段的扩展方向。E=0是向上扩展的,是普通的数据段;E=1是向下扩展的,通常是栈段。W位指示段的读写属性,W=0的段是不允许写入的,否则会引发处理器异常中断;W =1的段是可以正常写入的

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

R位指示代码段是否允许读出

数据段和代码段的A位是已访问(Accessed)位,用于指示它所指向的段最近是否被访问过。在描述符创建的时候,应该清零。之后,每当该段被访问时,处理器自动将该位置1。对该位的清零是由操作系统负责的,通过定期监视该位的状态,就可以统计出该段的使用频率。当内存空间紧张时,可以把不经常使用的段退避到硬盘上,从而实现虚拟内存管理

  • AVL是软件可以使用的位(Available),通常由操作系统来用,处理器并不使用它
  1. 初始化GDT
    ;处理器规定,GDT中的第一个描述符必须是空描述符;创建0#描述符mov dword [bx+0x00], 0x00mov dword [bx+0x04], 0x00;创建#1描述符,保护模式下的代码段描述符mov dword [bx+0x08], 0x7c0001ff     mov dword [bx+0x0c], 0x00409800   

描述符1的低32位是0x7c0001ff,高32位是0x00409800
段描述符1
该描述符所指向的段是现在正在执行的主引导程序所在的区域

	;创建#2描述符,保护模式下的数据段描述符(文本模式下的显示缓冲区) mov dword [bx+0x10],0x8000ffff     mov dword [bx+0x14],0x0040920b  

描述符2用于安装一个数据段描述符(0x000b8000是显存的起始地址)
描述符2

;创建#3描述符,保护模式下的堆栈段描述符mov dword [bx+0x18],0x00007a00mov dword [bx+0x1c],0x00409600

描述符3用于安装栈段的描述符
描述符3
段界限的值0x07a00加上1,ESP寄存器所允许的最小值。当执行push、call 这样的隐式栈操作时,处理器会检查ESP 寄存器的值,一旦发现它小于等于这里指定的数值,会引发异常中断

  1. 加载描述符表的线性基地址和界限到GDTR寄存器
gdt_size:dw 0
gdt_base:dd 0x00007e00; 初始化描述符表寄存器GDTR;16位是GDT的界限值,高32位是GDT的基地址; 共有4个描述符(包括空描述符),每个描述符占8字节,一共是32字节mov word [cs:gdt_size+0x7c00], 31  ;描述符表的界限(总字节数减一); lgdt的操作数是一个48位的内存区域(GDTR48)lgdt [cs:gdt_size+0x7c00]
  1. A20遗留问题
    在即将进入保护模式之前,这里还涉及一个历史遗留问题,那就是处理器的第21根地址线 —— A20
    简单点说呢就是从808680286再到80486为解决实模式下地址回绕的问题而引申出的一种与硬件特性挂钩的解决方案
    80486处理器开始,处理器有了A20M#引脚,低电平有效的
    A20
    输入输出控制器集中芯片ICH的处理器接口部分,有一个用于兼容老式设备的端口0x92,第0位叫做INIT_NOW,用于初始化处理器,当它从0过渡到1时,ICH芯片会使处理器INIT#引脚的电平变低,导致计算机重新启动
    端口0x92的位1用于控制A20(ALT_A20_GATE),它和来自键盘控制器的A20控制线一起,通过或门连接到处理器的A20M#引脚。当INIT_NOW0过渡到1时,ALT_A20_GATE将被置"1"。这就是说,计算机启动时,A20是自动启用的
	in al, 0x92                        ;南桥芯片内的端口 or al, 0000_0010Bout 0x92, al                       ;打开A20
  1. 保护模式下的内存访问
    我们要知道:保护模式与实模式的切换是由CR0寄存器(这两种模式切换的开关原是在一个叫CR0 的寄存器)控制的(别问我怎么知道,书上说的😅)
    CR0是32位的寄存器,包含了一系列用于控制处理器操作模式和运行状态的标志位.它的位0是保护模式允许位(Protection Enable,PE),如果把该位置"1",处理器进入保护模式

保护模式下的中断机制和实模式不同,因此,原有的中断向量表不再适用
在重新设置保护模式下的中断环境之前,必须关中断

	;保护模式下中断机制尚未建立,应禁止中断cli;设置PE位  mov eax, cr0or eax, 1mov cr0, eax                            
  1. 段寄存器
    32位处理器的段寄存器又分为两部分,前16位和8086相同,在实模式下,它们用于按传统的方式寻址1MB内存
    每个段寄存器还包括一个不可见的部分,称为描述符高速缓存器,用来存放段的线性基地址、段界限和段属性
    段寄存器
    在实模式下,每当引用一个段时,处理器自动将段地址左移4位,并传送到描述符高速缓存器。在保护模式下,尽管访问内存时也需要指定一个段,但传送到段选择器的内容不是逻辑段地址,而是段描述符在描述符表中的索引号
    在保护模式下访问一个段时,传送到段选择器的是段选择子
    段选择子- 描述符索引用来在描述符表中选择一个段描述符
  • TI是描述符表指示器(Table Indicator),TI=0时,表示描述符在GDT中;TI=1时,描述符在LDT中
  • RPL是请求特权级,表示给出当前选择子的那个程序的特权级别
flush:; 将描述符选择子0x0010传送到段选择器DS; 指定的描述符索引号是2(上方创建的),指定的描述符表是GDT,请求特权级RPL00mov cx,00000000000_10_000B         ;加载数据段选择子(0x10)mov ds,cx

GDT的线性基地址在GDTR中,每个描述符占8字节,因此,描述符在表内的偏移地址是索引号乘以8。如下图所示,当处理器在执行任何改变段选择器的指令时会将指令中提供的索引号乘以8作为偏移地址,同GDTR中提供的线性基地址相加,以访问GDT
加载过程
此后,每当有访问内存的指令时,就不再访问GDT中的描述符,直接用当前段寄存器描述符高速缓存器提供线性基地址。
保护模式内存访问

	; 如上图所示,指令没有段超越前缀,默认使用数据段寄存器DS; 执行这些指令时,处理器用DS描述符高速缓存中的线性基地址加上指令中给出的偏移量形成32位物理地址mov byte [0x00],'P'  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

不单单是访问数据段,即使是处理器取指令执行时,也采用了相同的方法。如下图所示,在32位保护模式下,处理器使用的指令指针寄存器是EIP。假设已经从描述符表中选择了一个段描述符,CS描述符高速缓存器已经装载了正确的32位线性基地址,那么,当处理器取指令时,会自动用描述符高速缓存器中的32位线性基地址加上指令指针寄存器EIP中的32位偏移量,形成32位物理地址,从内存中取得执令并加以执行
指令过程

  1. 清空流水线并串行化处理器
    在进入保护模式前,有很多指令已经进入了流水线。因为处理器工作在实模式下,所以它们都是按16位操作数和16位地址长度进行译码的。进入保护模式后,由于对操作数和默认地址大小的解释不同,指令的执行结果可能会不正确,所以必须清空流水线。
;以下进入保护模式... ...; 清流水线并串行化处理器 ; dword修饰偏移量为32; 保护模式下,处理器都将把第一个操作数0x0008视为段选择子; 段选择子0x0008(索引号为1TI 位是0RPL00)对应着前方定义的描述符1jmp dword 0x0008:flush             ;16位的描述符选择子:32位偏移; 当指令执行时,处理器加载段选择器CS,从GDT中取出相应的描述符加载到CS描述符高速缓存,同时,把指令中给出的32位偏移量传送到指令指针寄存器EIP
  1. 保护模式下的栈
    如上方定义的描述符3,栈的32位线性基地址是0x00000000,段界限为0x07a00,粒度为字节,属于可读可写、向下扩展的数据段
	mov cx,00000000000_11_000B         ;加载堆栈段选择子mov ss,cx; ESP作为栈指针,段界限是和段粒度一起,决定ESP寄存器所能具有的最小值; 对于描述符中G位是“0”的段来说,粒度值是1byte;而对于G位是"1"的段来说,粒度值是4KBmov esp,0x7c00

栈段

	mov ebp,esp                        ;保存堆栈指针 ; 关键字“byte”仅仅是给编译器用的,告诉它,该指令对应的格式为push imm8,必须使用操作码0x6A,而不是用来在编译后的机器指令前添加指令前缀; 故压入栈中的是一个双字push byte '.'                      ;压入立即数sub ebp,4cmp ebp,esp                        ;判断压入立即数时,ESP是否减4 jnz ghalt                          pop eaxmov [0x1e],al                      ;显示句点 ghalt:     hlt                                ;已经禁止中断,将不会被唤醒

完整程序

;设置堆栈段和栈指针 mov ax,cs      mov ss,axmov sp,0x7c00;计算GDT所在的逻辑段地址 mov ax,[cs:gdt_base+0x7c00]        ;16位 mov dx,[cs:gdt_base+0x7c00+0x02]   ;16位 mov bx,16        div bx            mov ds,ax                          ;DS指向该段以进行操作mov bx,dx                          ;段内起始偏移地址 ;创建0#描述符,它是空描述符,这是处理器的要求mov dword [bx+0x00],0x00mov dword [bx+0x04],0x00  ;创建#1描述符,保护模式下的代码段描述符mov dword [bx+0x08],0x7c0001ff     mov dword [bx+0x0c],0x00409800     ;创建#2描述符,保护模式下的数据段描述符(文本模式下的显示缓冲区) mov dword [bx+0x10],0x8000ffff     mov dword [bx+0x14],0x0040920b     ;创建#3描述符,保护模式下的堆栈段描述符mov dword [bx+0x18],0x00007a00mov dword [bx+0x1c],0x00409600;初始化描述符表寄存器GDTRmov word [cs: gdt_size+0x7c00],31  ;描述符表的界限(总字节数减一)   lgdt [cs: gdt_size+0x7c00]in al,0x92                         ;南桥芯片内的端口 or al,0000_0010Bout 0x92,al                        ;打开A20cli                                ;保护模式下中断机制尚未建立,应 ;禁止中断 mov eax,cr0or eax,1mov cr0,eax                        ;设置PE;以下进入保护模式... ...jmp dword 0x0008:flush             ;16位的描述符选择子:32位偏移;清流水线并串行化处理器 [bits 32] flush:mov cx,00000000000_10_000B         ;加载数据段选择子(0x10)mov ds,cx;以下在屏幕上显示"Protect mode OK." mov byte [0x00],'P'  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';以下用简单的示例来帮助阐述32位保护模式下的堆栈操作 mov cx,00000000000_11_000B         ;加载堆栈段选择子mov ss,cxmov esp,0x7c00mov ebp,esp                        ;保存堆栈指针 push byte '.'                      ;压入立即数sub ebp,4cmp ebp,esp                        ;判断压入立即数时,ESP是否减4 jnz ghalt                          pop eaxmov [0x1e],al                      ;显示句点 ghalt:     hlt                                ;已经禁止中断,将不会被唤醒 ;-----------------------------------------------------------------------gdt_size         dw 0gdt_base         dd 0x00007e00     ;GDT的物理地址 times 510-($-$$) db 0db 0x55,0xaa

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

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

相关文章

emp.dll丢失怎么解决,快来试试这个几个解决方法

在日常使用电脑玩游戏的过程中,我们可能会遇到一些错误提示,其中最常见的就是“emp.dll丢失”。那么,emp.dll到底是什么?它为什么会丢失?丢失后会对我们的电脑产生什么影响?本文将为您详细解析emp.dll的概念…

[BUUCTF从零单排] Web方向 03.Web入门篇之sql注入-1(手工注入详解)

这是作者新开的一个专栏《BUUCTF从零单排》,旨在从零学习CTF知识,方便更多初学者了解各种类型的安全题目,后续分享一定程度会对不同类型的题目进行总结,并结合CTF书籍和真实案例实践,希望对您有所帮助。当然&#xff0…

打造高业绩朋友圈:策略与实践

在数字化时代,朋友圈不仅是个人生活的展示窗口,更是商业变现的有力平台。许多人通过精心经营朋友圈,实现了财富的增长,甚至达到了年入百万的惊人业绩。朋友圈已成为普通人实现逆袭的重要战场。 要打造一个业绩过万的朋友圈&#…

关于武汉芯景科技有限公司的IIC电平转换芯片XJ9509开发指南(兼容PCa9509)

一、芯片引脚介绍 1.芯片引脚 2.引脚描述 二、系统结构图 三、功能描述 1.VCCA1.35V,VCCB5V,A1输入,B1输出 2.VCCA1.35V,VCCB5V,B1输入,A1输出 3.VCCA1.35V,VCCB5V,A2输入,B2输出 4.VCCA1.35V,VCCB5V,B2输入,A2输出

升级 Windows 后如何恢复丢失的文件

升级到 Windows 11 后可以恢复丢失的文件!阅读帖子直到最后,了解如何做到这一点。 为了获得安全更新并使用最新的操作系统,人们会升级到最新版本的 Windows。然而,在这样做的过程中,许多人丢失了他们的重要文件&#…

基于SpringBoot+Vue+MySQL的体育商城系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着互联网的飞速发展,电子商务已成为人们日常生活中不可或缺的一部分。体育用品市场作为其中的一个重要分支,也逐渐向线上转移。基于SpringBootVueMySQL的体育商城系统应运而生,旨在通过构建…

优秀的拆分C++

题目: 样例解释: 样例1解释 6422221 是一个优秀的拆分。注意,6222 不是一个优秀的拆分,因为拆分成的 3 个数不满足每个数互不相同。 思路: 题目大致意思是说给定一个正整数NN,让你用二进制表示(…

PDCA优化任务流程

这里写目录标题 一、背景二、PDCA重要性与必要性概念PDCA循环的重要性 三、PDCA分析这次任务的执行任务描述分析原因:结合PDCA分析:提高办法: 四、总结 一、背景 汇报任务完成情况,未提交实际成果。 本次总结旨在通过PDCA循环的视…

鸿蒙开发(NEXT/API 12)【硬件(振动开发1)】振动

通过最大化开放马达器件能力,振动器模块服务拓展了原生马达服务,实现了振动与交互融合设计,从而打造出细腻精致的一体化振动体验和差异化体验,提升用户交互效率、易用性以及用户体验,并增强品牌竞争力。 运作机制 Vi…

学习docker第二弹------基本命令[帮助启动类命令、镜像命令、容器命令]

docker目录 前言基本命令帮助启动类命令停止docker服务查看docker状态启动docker重启docker开机启动docker查看概要信息查看总体帮助文档查看命令帮助文档 镜像命令查看所有的镜像 -a查看镜像ID -q在仓库里面查找redis拉取镜像查看容器/镜像/数据卷所占内存删除一个镜像删除多个…

机器学习:opencv--背景建模

目录 一、背景建模是什么? 二、背景建模的目的 三、背景建模的方法及原理 四、代码实现 1.创建卷积核 2.创建混合高斯模型 3.处理图像 4.绘制人形轮廓 5.条件退出 一、背景建模是什么? 指在计算机视觉中,从视频序列中提取出静态背景…

Anaconda虚拟环境默认路径在C盘怎么更改

笔者已经新建好了虚拟环境并且安装了对应库,输入conda env list查询发现虚拟环境竟然安装到了C盘(。•́︿•̀。),为避免下一次创建虚拟环境出错,笔者现在修改默认路径置D盘(软件安装盘) 参考两…

ROS学习笔记(四):使用 `ros2 run usb_cam usb_cam_node_exe` 启动 USB 摄像头

文章目录 前言1 安装 usb_cam 包2 启动 USB 摄像头3 订阅相机发布的节点信息并进行可视化3.1 使用 rqt_image_view3.2 使用 image_view3.3 使用 rviz 4 常见问题与解决方案4.1 摄像头未被识别4.2 相机显示异常4.3 如何指定不同的相机4.4 摄像头参数调整 5. 调试信息 5. 结论 前…

pyboard405意外故障,micropython OLED例程无法运行,折腾了大半天。

thonny报告&#xff1a; Traceback (most recent call last): File "<stdin>", line 3, in <module> RuntimeError: name too mode # main.py -- put your code here! from machine import I2C,Pin #从machine模块导入I2C、Pin子模块 from ss…

SpringBoot项目请求不中断动态更新代码

在开发中&#xff0c;有时候不停机动态更新代码热部署是一项至关重要的功能&#xff0c;它可以在请求不中断的情况下下更新代码。这种方式不仅提高了开发效率&#xff0c;还能加速测试和调试过程。本文将详细介绍如何在 Spring Boot 项目在Linux系统中实现热部署&#xff0c;特…

MySQL - 运维篇

一、日志 1. 错误日志 2. 二进制日志 3. 查询日志 记录了所有的增删改查语句以及DDL语句 4. 慢查询日志 二、主从复制 1. 概述 2. 原理 3. 搭建 三、分库分表 1. 介绍 2. Mycat概述 3. Mycat入门 4. Mycat配置 5. Mycat分片 6. Mycat管理及监控 四、读写分离 1. 介绍 2. 一…

PostgreSQL的安装与使用指南

PostgreSQL,简称Postgres,是一个功能强大的开源对象-关系数据库系统。它以其稳定性、可靠性、数据完整性以及对SQL标准的严格遵循而著称,广泛应用于各种规模的企业级应用、Web应用和数据仓库中。本文将详细介绍PostgreSQL在不同操作系统上的安装、配置及基本使用方法。 一、…

html+css+js实现Tabs标签页

实现效果 HTML部分 <div class"tab"><ul class"tab-nav"><li><a href"#" class"active" >用户管理</a></li><li><a href"#" >配置管理</a></li><li>&l…

毕业论文设计javaweb+VUE高校教师信息管理系统

目录 一、系统概述 二、功能详解 1. 教师管理 2. 部门管理 3. 奖惩管理 4. 业绩管理 5. 培训管理 6. 报表查询 三、总结 四、示例代码 1 前端VUE 2 后端SpringBootjava 3 数据库表 随着教育信息化的发展&#xff0c;传统的手工管理方式已经不能满足现代学校对教师…

程序员的自我修养(链接、装载与库)--摘录与汇总(一)

指令和地址分开原因&#xff1a;&#xff08;P59&#xff09; 1、独立权限&#xff0c;防止程序指令非预期改写 程序装载后&#xff0c;数据和指令分别被映射到两个虚存区域。由于数据区域对于进程来说是可读写的&#xff0c;而指令区域对于进程来说是只读的&#xff0c;所以…