一起学习x86/x64知识

本文深入探讨x86/x64体系结构中的细节问题,通过实验验证了官方手册中的一些描述与实际表现之间的差异,特别是在中断处理和优先级方面的差异。

在实际工作中接触到一些与x86/x64平台底层的问题时,往往是遇到不懂的就翻翻Intel或AMD提供的手册,这样掌握并不牢固,下次重遇问题时还要翻手册。现在让我们一起来系统地学习x86/x64体系知识,然后需要温故而知新。


实验的重要性

在学习过程中,我们以探索为目的,以实验为依托。要透彻地对一个知识点进行理解,显然经过测试实验的论证是最好的方法,也只有经过全方位的测试才是最为真实可靠的。Intel和AMD官方手册某些描述上存在着隐晦难懂的地方,甚至可能还会出现错误。因此测试实验起的作用是:验证真伪和去除疑惑。

下图是在Intel64软件开发者手册里截取到的一段有误的描述:


截图中描述的是INT指令在返回时的一部分处理流程:在IA-32e模式下(描述中包括了64位模式和compatibility模式)当operand size为32或16时,只会pop出32位或16位的CS,EIP以及EFLAGS值。然而实际上在64位模式下,处理器还会无条件pop出SS和ESP值并不依赖于operandsize的值。

这个描述与x64体系中的设计产生一些冲突(主要依据于AMD64手册的描述)。因此,在无法准确判断时,我们做出测试实验来验证,在64位模式下执行下面的代码:

mov WORD [rsp], 0

mov WORD [rsp + 2], KERNEL_CS

mov WORD [rsp + 4], 46

mov WORD [rsp + 6], 08F0h

mov WORD [rsp + 8], 0A0h

db 66h

iret

这段代码执行16位的中断返回,在栈中压入的是16位的值,实验的结果是:


这是运行在bochs上的测试结果,RSP和SS值没有改变,没有pop出SS与SP值,这是bochs按照Intel64手册上所描述的流程进行模拟了。


在vmware和真实机器上执行显示出SS值为0A0h,RSP值为08F0h,处理器已经从栈中POP出SS与SP值。

然这个实验可以写得更好些更清晰些,显然从这个实验测试里知道Intel64手册中的这一段描述与事实不符。这里阐述了实验作用之一:就是验证真伪。

下面一段话是关于中断优先级别的描述:

Each interrupt vector is an 8-bitvalue. The interrupt-priority class is the value of bits 7:4 of theinterrupt vector. The lowest interrupt-priority class is 1 and the highest is15; interrupts with vectors in the range 0–15 (with interrupt-priority class 0)are illegal and are never delivered. Because vectors 0–31 are reserved fordedicated uses by

the Intel 64 and IA-32 architectures, software should configure interruptvectors to use interrupt-priority classes in the range 2–15.

Each interrupt-priority class encompasses 16 vectors. The relativepriority of interrupts within an interrupt-priority class is determined by thevalue of bits 3:0 of the vector number. The higher the value of those bits, thehigher the priority within that interrupt-priority class. Thus, each interruptvector comprises two parts, with the high 4 bits indicating itsinterrupt-priority class and the low 4 bits indicating its ranking within theinterrupt-priority class.

这段话描述8位的中断vector被分为两部分用来管理优先级,高4位是priorityclass,低4位是priority ranking。我们可以看作:高4位管理等级,低4位是等级内的排名。vector值大优先级别就高。

然而这里的描述会让我们产生困惑:同一个class内的ranking是否越大优先级别越高,实际上不是,处理器响应中断请求是按vector[7:4]值来仲裁:只有priority class大的才能响应。


上图所显示的是另一个测试实验,当我们对TPR(Task Priority Register)设置的值为0x32时,它的中断门坎的priority class值为3(第3级),同时PPR(Processor Priority Register)也被置为0x32,PPR的值受TPR与ISR影响。只有满足下列条件时中断请求才能被响应:

vector[7:4] > PPR[7:4]

因此:vector为0x33的中断请求发生时,将会被阻塞得不到响应的,只有vector的priority class值大于3时才会响应。

然而,Intel64手册里描述的并不清晰,我们只能通过实验来测试和验证,去除我们的困惑,这是实验的第二个作用。

构造实验平台

我们可以想象到:由于OS的限制,大多数实验是并不适合在现有的OS平台里进行测试。因此我们需要选择要祼机上进行测试。

可以选择的运行环境有真实机器,bochs模拟器或者vmware虚拟机。那么我们可以选择的介质是U盘,硬盘映像文件或者软盘映像文件,并需要编写自己的boot程序来引导测试实验。根据不同的介质软件不同的格式:U盘和硬盘映像文件使用FAT32格式,软件映像文件可以直接将boot代码写入MBR中即可。

上面是启动boot程序的流程,FAT32文件格式中,我们的boot程序写入63号扇区(从0开始),BIOS将boot程序读入7C00h位置后,继续加载我们实验的后续模式。

以U盘和硬盘映像文件使用的FAT32文件格式为例,下面的映像文件组织:


我们实验中常用的模块是boot模块,setup模块,lib16模块,protected模块,lib32模块以及long模块。lib16与lib32模块是库代码,protected模块是32位保护模式执行代码,long模块是64位long模块执行代码,而boot模块引导它们,setup模块切入保护模式。

规定了这些模块在映像文件中的存放位置后,需要将这些模块代码写入映像文件中,可以使用最原始的方法,即:手工使用Hex类型软件合并写入。这种方法实在是太麻烦了。

这里使用自己编写的合并工具merge,根据它的配置文件批量写入到映像文件中,下面是配置文件的示例:

# 输入文件, 输入文件 offset, 输出文件, 输出文件 offset, 写入 block 数( 1 block = 512bytes)

#**** 每一项用逗号分隔 ****

#

#example:

#

#模块名 offset 输出文件名 offset count(1count = 512 bytes)

#-------------------------------------------------

#boot, 0, demo.img, 0, 1

#setup, 0, demo.img, 1, 2

#init, 0, demo.img, 3, 5

#

# 意思是:

#boot 模块从 block 0 开始写入 demo.img 写入位置为 block 0, 写入 1 个 block

#setup 模块从 block 0 开始写入 demo.img 写入位置为 block 1, 写入 2 个 block

#init 模块从 block 0 开始写入 demo.img 写入位置为 block 3, 写入 5 个 block

# 下面是第2 章中使用到的配置实例:

boot,0,demo.img,0,1

每一行有5个项目,以逗号分隔。分别是:源文件,源文件起始点,目标文件,目标文件起始点,写入的块数(以512字节为一块)。使用merge工具我们只需要在命令执行merge命令即可。merge会帮助我们批量地写入目标文件相应的位置,省事且不会出错。

这里,\\.\g就是U盘的设备名,当配置文件中添加写入U盘时(目标文件为U盘设备名),我们可以插入U盘到真实机器去运行。

接下来,需要编写自己的boot代码,下面是简短的示例片断:

; set BOOT_SEG environment

mov ax, cs

mov ds, ax

mov ss, ax

mov es, ax

mov sp, BOOT_SEG ; stack 底为 BOOT_SEG

call clear_screen

mov si, hello

call print_message

mov si, 20 ; setup 模块在第20 号扇区里

mov di, SETUP_SEG - 2

call load_module ; 使用 load_module() 读多个扇区

mov si, SETUP_SEG

call print_message

mov si, word [load_message_table + eax * 2]

call print_message

next:

jmp $

在这个boot示例里,主要的工作是调用load_module()函数来加载上述的某个模块,接下来,调用print_message()打印信息。


最后,每个测试实验下,都包括源代码文件,bochs配置文件,merge工具的配置文件,软盘映像文件(demo.img)以及硬盘映像文件(c.img)。




目录 第一篇 x86 基础 第1 章数与数据类型2 1.1 数 2 1.1.1 数字 2 1.1.2 二进制数 3 1.1.3 二进制数的排列 3 1.1.4 十六进制数 5 1.1.5 八进制数与十进制数 5 1.2 数据类型 6 1.2.1 integer 数 6 1.2.2 floating-point 数. 9 1.2.3 real number(实数)与NaN(not a number) . 11 1.2.4 unsupported 编码值 14 1.2.5 浮点数精度的转换 15 1.2.6 浮点数的溢出 17 1.2.7 BCD 码 20 1.2.8 SIMD 数据 21 第2 章 x86/x64 编程基础 23 2.1 选择编译器 23 2.2 机器语言 24 2.3 Hello world 25 2.3.1 使用寄存器传递参数 26 2.3.2 调用过程 27 2.3.3 定义变量 27 2.4 16 位编程、32 位编程,以及64 位编程 28 2.4.1 通用寄存器 28 2.4.2 操作数大小 30 2.4.2 64 位模式下的内存地址 30 2.4.4 内存寻址模式 31 2.4.5 内存寻址范围 34 2.4.6 使用的指令限制 34 2.5 编程基础 34 2.5.1 操作数寻址 35 2.5.2 传送数据指令 39 2.5.3 位操作指令 45 2.5.4 算术指令 47 2.5.5 CALL 与RET 指令 48 2.5.6 跳转指令 48 2.6 编辑与编译、运行 48 第 3 章编写本书的实验例子 50 3.1 实验的运行环境 50 3.2 生成空白的映像文件 52 3.2.1 使用nasm 编译器生成 52 3.2.2 使用bximage 工具 52 3.3 设置bochs 配置文件. 53 3.4 源代码的基本结构 54 3.5 编译源代码55 3.6 映像文件内的组织 55 3.7 使用merge 工具 56 3.7.1 merge 的配置文件 57 3.7.2 执行merge 命令 57 3.8 使用U 盘启动真实机器 58 3.8.1 使用merge 工具写U 盘 58 3.8.2 使用hex 编辑软件写U 盘 59 3.9 编写boot 代码 60 3.9.1 LBA 转换为CHS 62 3.9.2 测试是否支持int 13h 扩展功能 63 3.9.3 使用int 13h 扩展读磁盘 64 3.9.4 最后看看load_module() 64 3.10 总结 66 第4 章处理器的身份 67 4.1 测试是否支持CPUID 指令 67 4.2 CPUID 指令的术语及表达 68 4.3 基本信息与扩展信息 68 4.4 处理器的型号(family,model 与stepping) 72 4.5 最大的物理地址和线性地址 73 4.6 处理器扩展状态信息74 4.6.1 探测Processor Extended State 子叶 75 4.6.2 Processor Extended State 子叶所需内存size 76 4.6.3 Processor Extended State 的保存 77 4.6.4 Processor Extended State 的恢复 78 4.7 处理器的特性 78 4.8 处理器的Cache 与TLB 信息 80 4.9 MONITOR/MWAIT 信息 83 4.10 处理器的long mode 84 第 5 章了解 Flags 85 5.1 Eflags 中的状态标志位 86 5.1.1 signed 数的运算 86 5.1.2 unsigned 数的运算 89 5.2 IOPL 标志位 90 5.3 TF 标志与RF 标志 93 5.4 NT 标志 95 5.5 AC 标志 96 5.6 VM 标志 98 5.7 eflags 寄存器的其他事项 99 第 6 章处理器的控制寄存器 101 6.1 CR8 102 6.2 CR3 103 6.3 CR0 104 6.3.1 保护模式位PE 104 6.3.2 x87 FPU 单元的执行环境 104 6.3.3 CR0.PG 控制位 108 6.3.4 CR0.CD 与CR0.NW 控制位 108 6.3.5 CR0.WP 控制位 110 6.3.6 CR0.AM 控制位 110 6.4 CR4 110 6.4.1 CR4.TSD 与CR4.PCE 控制位 110 6.4.2 CR4.DE 与CR4.MCD 控制位 111 6.4.3 CR4.OSFXSR 控制位 111 6.4.4 CR4.VMXE 与CR4.SMXE 控制位 111 6.4.5 CR4.PCIDE 与CR4.SMEP 控制位 112 6.4.6 CR4.OSXSAVE 控制位 113 6.4.7 CR4 中关于页的控制位 113 6.5 EFER 扩展功能寄存器 114 第 7 章 MSR. 116 7.1 MSR 的使用 116 7.2 MTRR 117 7.2.1 Fixed-range 区域的映射 118 7.2.2 MTRR 的功能寄存器 120 7.3 MSR 中对特殊指令的支持 124 7.3.1 支持sysenter/sysexit 指令的MSR 125 7.3.2 支持syscall/sysret 指令的MSR 126 7.3.3 支持swapgs 指令的MSR 127 7.3.4 支持monitor/mwait 指令的MSR 128 7.4 提供processor feature 管理 129 7.5 其他未列出来的MSR 129 7.6 关于MSR 一些后续说明 129 第二篇 处理器的工作模式 第8 章实地址模式 132 8.1 真实的地址 132 8.2 real mode 的编址 132 8.3 real mode 的状态 133 8.4 段基址的计算 134 8.5 第1 条执行的指令 134 8.6 实模式下的执行环境 135 8.7 实模式下的IVT 135 8.8 突破64K 段限 136 8.9 A20 地址线 137 第 9 章 SMM系统管理模式探索 138 9.1 进入SMM 138 9.2 SMM 的运行环境 141 9.2.1 SMRAM 区域 141 9.2.2 SMM 执行环境的初始化 143 9.2.3 SMM 下的operand 与address 144 9.2.4 SMM 下的CS 与EIP 144 9.2.5 SMM 下的SS 与ESP 145 9.3 SMM 里的中断 145 9.4 SMI 的Back-to-Back 响应 147 9.5 SMM 里开启保护模式 147 9.6 SMM 的版本 148 9.7 I/O 指令的重启及Halt 重启 151 9.8 SMM 的退出 152 9.9 SMBASE 的重定位. 153 9.10 SMI 处理程序的初始化 154 9.11 SMM 的安全 156 9.11.1 芯片组的控制 156 9.11.2 处理器对SMRAM 空间的限制 158 9.11.3 cache 的限制 160 9.12 测试SMI 处理程序 161 第 10 章 x86/x64 保护模式体系(上) 163 10.1 x86/x64 的权限 164 10.2 保护模式下的环境 164 10.2.1 段式管理所使用的资源 165 10.2.2 paging 分页机制所使用的资源 165 10.3 物理地址的产生 166 10.4 段式管理机制 167 10.4.1 段式内存管理 168 10.4.2 段式的保护措施 168 10.5 段式管理的数据结构 169 10.5.1 Segment Selector(段选择子) 169 10.5.2 Descriptor Table(描述符表) 172 10.5.3 Segment Selector Register(段寄存器) 174 10.5.4 Segment Descriptor(段描述符) 175 10.5.5 LDT 描述符与LDT 258 10.6 开启保护模式 259 10.6.1 初始化GDT 260 10.6.2 初始化IDT. 262 10.6.3 切换到保护模式 263 第11 章 x86/x64 保护模式体系(下) 265 11.1 物理页面 265 11.1.1 处理器的最高物理地址(MAXPHYADDR) 266 11.1.2 物理页面的大小 267 11.1.3 页转换模式(Paging Mode) 268 11.2 paging 机制下使用的资源 270 11.2.1 寄存器 270 11.2.2 CPUID 查询leaf 270 11.2.3 寄存器的控制位 271 11.2.4 页转换表资源 272 11.3 32 位paging 模式(non-PAE 模式) 273 11.3.1 CR3 结构 274 11.3.2 32 位paging 模式下的PDE 结构 275 11.3.3 使用32 位paging 279 11.4 PAE paging 模式. 282 11.4.1 在Intel64 下的CR3 与PDPTE 寄存器 283 11.4.2 在AMD64 下的CR3 285 11.4.3 PAE paging 模式里的PDPTE 结构 286 11.4.4 PAE paging 模式里的PDE 结构 286 11.4.5 PAE paging 模式里的PTE 结构 288 11.4.6 使用和测试PAE paging 模式 288 11.4.7 使用和测试Execution Disable 功能 292 11.5 IA-32e pagi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值