120_IA32逻辑功能仿真实现
1.无需注册登录,支付后按照提示操作即可获取该资料.
2.资料以网页介绍的为准,下载后不会有水印.资料仅供学习参考之用.
密 惠 保
3.4 bochs介绍
bochs是一个X86的模拟器,它可以模拟几乎所有类型的X86 CPU,包括16位,32位和64位(X86_64)。一个64位的X86可以看作是32位和16位X86的超集。X86_64的工作方式包括以下两类:
IA-32模式:包括实模式,保护模式和虚拟8086模式三个子模式。
IA-32e模式:包括长模式和兼容模式。
bochs中包含多个工程:
⑴bochs主工程,它的任务是生成一个bochs虚拟机框架。
⑵bx_debug,这是处理debug命令的工程。
⑶cpu,这是完成CPU时序与指令系统的工程。
⑷disasm ,它是反汇编工程,可以方便用户对代码的理解。
⑸fpu,它专门用来处理浮点数运算。
⑹gui,这是图形处理工程,包含了虚拟机在桌面显示的相关操作。
⑺iodev,io操作的工程,完成并协调io操作。
⑻memory,主存操作的工程。
⑼stubs:插槽库,用来扩展CPU功能以及提供与外界的接口功能。
其中cpu和stubs是我们设计中需要的2个库,cpu包含了一系列的功能实现,stubs是我们调用cpu库的接口,通过修改它才能是我们的DLL正确的调用CPU库的方法。 【买计算机毕业论文就到计算机毕业论文网】 [版权所有:http://think58.com]
在bochs中,用BX_CPU_C类来模拟CPU,这个类正是我移植需要的。
3.4.1 BX_CPU_C的数据成员
char name[64],代表CPU的名字。
unsigned bx_cpuid,CPU的ID号,用于SMP(对称多处理)的机器。
bx_gen_reg_t gen_reg[BX_GENERAL_REGISTERS],bx_gen_reg_t是一个复杂的结构体,它表示一个64位的通用寄存器,其主要成员有:rrx:表示一个完整的64位寄存器。hrx、erx:分别表示64位寄存器的高32位与低32位,这时候64位的寄存器被分割成2个32位寄存器使用。
rx:表示erx的低16位,此时寄存器被当作16位寄存器使用。
rh、rl:分别表示rx的高8位与低8位,此时rx被分割成2个8位寄存器。
gen_reg就是一个代表通用寄存器的数组,BX_GENERAL_REGISTERS是寄存器的个数,如果模拟的是64位处理器,那么BX_GENERAL_REGISTERS值为16,否则值为8。64位时通用寄存器分别是RAX、RCX、RDX、RBX、RSP、RBP、RSI、RDI、R8~R15;32位时没有R8~R15 。
Bit64u rip; Bit32u eip;
分别是64位模式和32位模式下的指令指示器。
bx_segment_reg_t sregs[6];
bx_segment_reg_t是表示段寄存器的结构,sregs[6]是表示了6个段寄存器的数组。分别是ES、CS、SS、DS、FS和GS。 think58 [来源:http://www.think58.com]
BX_MEM_C *mem;
mem是指向这个CPU所使用的内存的指针 。
bx_local_apic_c local_apic;
bx_local_apic是模拟本地APIC的类,用于SMP系统。
unsigned cpu_mode;
就是前面提到的CPU的工作模式,主要有5种,分别是实模式(16位模式)、8086虚拟模式(32位模式下的虚拟16位模式)、保护模式(32位模式)、兼容模式(64位模式下的虚拟32位,16位模式)和长模式(64位模式)。分别使用宏BX_MODE_IA32_REAL、BX_MODE_IA32_V8086、BX_MODE_IA32_PROTECTED、BX_MODE_LONG_COMPAT和BX_MODE_LONG_64表示。
void cpu_loop(Bit32s max_instr_count)
cpu_loop是一个非常重要的函数,所有的指令函数都在这里执行。参数max_instr_count表示cpu_loop执行的最大指令数。 think58好,好think58 [资料来源:www.THINK58.com]
void prefetch(void)预取指令函数,计算指令的物理地址和其他相关信息,为后面的指令译码作准备。
unsigned fetchDecode(Bit8u *iptr, bxInstruction_c *instruction,unsigned remain)
unsigned fetchDecode64(Bit8u *iptr, bxInstruction_c *instruction,unsigned remain)
这两个函数用于对指令进行译码,前者按指令长度32位译码,后者按64位译码,参数意义分别是指令的物理地址,空白指令结构和指令长度的最大字节数 think58.com [来源:http://think58.com]
void boundaryFetch(Bit8u *fetchPtr, unsigned remainingInPage, bxInstruction_c *i)
X86的最长指令可以达到15个字节,因此指令可能出现跨页,此时prefetch()+ fetchDecode()/fetchDecode64()失败,必须调用boundaryFetch()函数进行跨页取指译码。
3.4.2 BX_CPU_C 类对CPU工作流程的简易描述
函数cpu_loop()的流程图: [来源:http://think58.com]
本文来自think58
[资料来源:http://www.THINK58.com]
think58
[版权所有:http://think58.com]
copyright think58
[来源:http://www.think58.com]
think58.com [资料来源:http://THINK58.com]
[来源:http://www.think58.com]
[来源:http://www.think58.com]
[资料来源:http://think58.com]
本文来自think58 [版权所有:http://think58.com]
图1 cpu_loop流程图 think58 [来源:http://www.think58.com]
prefetch()函数(指令预取)
void BX_CPU_C::prefetch(void)
{
计算laddr //指令所在的线性地址
计算paddr //经过地址翻译得到的指令的物理地址
计算laddrPageOffset0 //指令所在的线性页(虚页)的基地址
计算eipPageOffset0
//RIP - (laddr - laddrPageOffset0),指令所在页基 地址相对于CS的偏移
计算eipPageBias // - eipPageOffset0
计算eipPageWindowSize //页大小,值为4096;
计算pAddrA20Page //pAddr & 0xfffff000,指令所在的物理页的基地址
计算eipFetchPtr
//通过getHostMemAddr()计算得到的指令所在页在主机(真实机器)上的基地址
}
所谓指令预取,就是计算指令的物理地址和其他相关信息,为后面的指令译码作准备。图2是其功能流程: [来源:http://www.think58.com]
本文来自think58 [版权所有:http://think58.com]
copyright think58
[资料来源:http://think58.com]
think58好,好think58 [版权所有:http://think58.com]
内容来自think58
[资料来源:www.THINK58.com]
内容来自think58 [资料来源:http://think58.com]
[资料来源:http://think58.com]
think58好,好think58 [资料来源:http://THINK58.com]
[版权所有:http://think58.com]think58好,好think58
think58 [资料来源:THINK58.com]
think58 [资料来源:http://think58.com]
本文来自think58 [资料来源:http://think58.com]
think58 [来源:http://think58.com]
本文来自think58
[资料来源:http://think58.com]
图2 功能图 think58.com
fetchDecode()函数(指令译码):
指令的结构用一个类指令结构bxInstruction_c 来表示,它的两个主要成员函数下:
void (BX_CPU_C::*ResolveModrm) (bxInstruction_c *) BX_CPP_AttrRegparmN(1);
//获取指令的类型,类型是由bochs定义的
void (BX_CPU_C::*execute)(bxInstruction_c *);
//指令对应的执行函数指针
基本指令放在一个数组中:
static BxOpcodeInfo_t BxOpcodeInfo[512*2]
第0~511项: 16bit mode
第512~1023项: 32bit mode
(其中包括了fpu,x86-64,3DNOW,SSE等指令入口) think58好,好think58
[资料来源:http://think58.com]
boundaryFetch()函数(跨页边界取指)
void boundaryFetch(Bit8u *fetchPtr, unsigned remainingInPage, bxInstruction_c *i)因为指令跨页,不能将指令的首地址作为参数传给fetchDecode()/fetchDecode64(),因此设立临时变量fetchBuffer,用以保存在两页中取出的共15个字节,并将fetchBuffer作为译码的参数。指令的最大长度为15字节,但具体每条指令的长度是不定的,在本次取指过程中为了实现跨页移动了RIP,因此返回之前必须将其复原,函数返回以后,在cpu_loop()里面,会根据实际的指令长度移动RIP。
copyright think58 [资料来源:THINK58.com]