Bochs源码分析-MEM类

因为学习需要,要看虚拟机Bochs的源代码。写随笔主要为了学习总结,其次是分享大家共同研究,大神勿喷,欢迎评论。

手头资料:bochs源代码,下于:bochs.sourceforge.net,还有喻强写的源码分析电纸书。

计算机系统最主要的两个部件是CPU和内存。CPU负责不断读取内存里面的指令进行工作。一块固定大小的内存条(2G)和能由很多ROM,RAM,SDRAM通过位扩展法、字扩展法共同组合而成,有的外部设备的驱动的各种特殊功能控制器也被编址到内存空间,也就是说:内存集成了很多功能,虽然同样是内存空间的地址访问,可能对应不同的设备,有着不同的处理办法。CPU对外界的访问是通过地址线和内存线,只管把要访问的地址交与总线,具体是有南北桥这样的设备,进行地址到各种不同设备、处理办法之间的转换。Bochs需要模拟整个内存空间,就要相应的申请相同大小的变量来模拟器状态的变化,具体上Bochs把内存分成三块主存SDRAM,256KB ROM,和另外一个附加的空间(特殊功能寄存器)。具体的代码是在memory/misc_mem.cpp:init_memorty(int memsize)来实现,调用是在main函数,bx_init_hardware()中:BX_MEM(0)->init_memory(memSize);在函数中内存变量的具体申请是:alloc_vector_aligned(memsize+ BIOSROMSZ + EXROMSIZE  + 4096, BX_MEM_VECTOR_ALIGN);BX_MEM_THIS rom = &BX_MEM_THIS vector[memsize];
  BX_MEM_THIS bogus = &BX_MEM_THIS vector[memsize + BIOSROMSZ + EXROMSIZE];可以看出变量vector指向内存初始地址(表示主内存SDRAM),rom指向memsize向后的一段地址(表示ROM),bogus指向更后面一段(为特殊功能寄存器使用),分开处理,就是为了,模拟对不同的内存空间对用的具体操作和安全限制是不一样的。对于固定的体系架构(如X86)内存空间具体每部分地址空间的用途都已经设计好了,约定熟成,这样方便操作系统的开发,当然模拟机也要遵循这样的约定管理不同的地址空间,以便不加修改就能运行x86对应的操作系统。具体表现在mem类最主要的两个函数:memory/memory.cpp writePhysicalPage(BX_CPU_C *cpu, bx_phy_address addr, unsigned len, void *data)和readPhysicalPage(BX_CPU_C *cpu, bx_phy_address addr, unsigned len, void *data)主要工作就是对要访问的地址,按约定熟成进行划分,对应到具体的某项用途,调用相应的处理方法。其x86内存约定比如:(1m之内的内存使用)

// 0x00000 - 0x7ffff    DOS area (512K)
// 0x80000 - 0x9ffff    Optional fixed memory hole (128K)
// 0xa0000 - 0xbffff    Standard PCI/ISA Video Mem / SMMRAM (128K)
// 0xc0000 - 0xdffff    Expansion Card BIOS and Buffer Area (128K)
// 0xe0000 - 0xeffff    Lower BIOS Area (64K)
// 0xf0000 - 0xfffff    Upper BIOS Area (64K)

这两个函数其实是模拟了南北桥这样设备的功能。

因为各种外部设备可能把自己控制器的特殊功能控制器,映射到内存空间上(统一地址映射),统一管理,Bochs也是这样处理的, 具体是维持很多mem_handler链表,具体数据结构是:struct  memory_handler_struct {
  struct memory_handler_struct *next;      // 下一个的pointer,  用来构成链表
  unsigned long begin;       // 受管理的内存端开始地址
  unsigned long end;        // 受管理的内存端结束地址
 memory_handler_t read_handler ;  // 读函数
 memory_handler_t write_handler;  // 写函数
 void *read_param;
 void *write_param;
};

可见,当Bochs新添加一个设备时,要让它的特殊功能控制器,在内存空间申请一段空间,注册相应的mem_handler链表,说明申请空间的地址范围,和自己想要注册的回调函数。当CPU通过上面两个函数访问内存时,会首先遍历mem_handler链表,如果地址落到其中之一,就调用里面的注册函数。

当然我上面提到的访问地址都已经是物理地址,而从软件指令使用的逻辑地址要经过逻辑地址->线性地址->物理地址,之间的转换,也就是CPU开启,分段、分页功能。主要的实现代码是在:CPU/page.cpp中,其中最主要的函数是:translate_linear(bx_address laddr, unsigned pl, unsigned rw, unsigned access_type);

主要过程是,分别获取页目录项、页表项,获取物理页的地址,其中对每个表项会有特权级等安全方面的检测,同时还会涉及TLB数据结构的查询、更新等管理。其中TLB数据项的结构(cpu.h)

typedef struct {
  bx_address lpf;     // linear page frame
  bx_phy_address ppf; // physical page frame
  Bit32u accessBits;  // Page Table Address for updating A & D bits
  bx_hostpageaddr_t hostPageAddr;
} bx_TLB_entry;

struct {    

bx_TLB_entry entry[BX_TLB_SIZE]  BX_CPP_AlignN(16);

} TLB;

posted @ 2013-11-06 09:51  安心编码  阅读(1162)  评论(0编辑  收藏  举报