本文主要简要描述下pmon中x86emu对vga初始化的步骤 1。主要思想: x86emu 实际上可以看作是执行x86指令的一台虚拟机,对其介绍分为三个部分,内存空间 映射,模拟中断机制,和指令执行模拟三个方面。 2。内存空间映射 x86emu中寻址空间空间默认是0~100000大小,映射关系如下: 0~0xa0000 此空间访问 INTPriv(pInt)->base指针所指向的空间 0xa0000~0xc0000 此空间映射 INTPriv(pInt)->vRam指针指向的空间 0xc0000~0xf0000 此空间映射 INTPriv(pInt)->base+V_BIOS开始的空间 0xf0000~0x100000 此空间映射 INTPriv(pInt)->sysMem指针指向的空间 其中INTPriv(pInt)->vRam赋值是VGA_BASE + 0xa0000即vga分配的io空间 INTPriv(pInt)->base 和INTPriv(pInt)->sysMem 是在vga_bios_init函数中malloc的空间 INTPriv(pInt)->base+V_BIOS中开始的10000空间内拷贝了 Target/Bonitoxxxx/Bonito/vgarom.c中定义的vgarom数组内容。 而模拟执行的指令,也正是vgarom数组里面。 3。模拟中断机制 由于pmon中仅支持单线程操作,因此对于中断不可能异步执行。其中断处理函数如下: 28 void xf86ExecX86int10(xf86Int10InfoPtrpInt) 29 { 30 int sig = setup_int(pInt); 31 32 if (sig < 0) 33 return; 34 35 if (int_handler(pInt)) { 36 X86EMU_exec(); 37 } 38 39 finish_int(pInt, sig); 40 } 其中setup_int 是保存现场, finish_int是恢复现场 int_handler(pInt)是为处理中断作准备。 X86EMU_exec()是执行中断处理程序。 int_handler函数内容如下: 33 int int_handler(xf86Int10InfoPtr pInt) 34 { 35 int num = pInt->num; 36 int ret = 0; 40 switch (num) { 41 #ifndef _PC 42 case 0x10: 43 case 0x42: 44 case 0x6D: 45 if (getIntVect(pInt, num) == I_S_DEFAULT_INT_VECT) { 46 printf("default int10 called,intno=%x\n", num); 47 ret = int42_handler(pInt); 48 } 49 break; 50 case 0x15: 51 ret = int15_handler(pInt); 52 break; 53 #endif 54 case 0x1A: 55 ret = int1A_handler(pInt); 56 break; 57 case 0xe6: 58 ret = intE6_handler(pInt); 59 break; 60 default: 61 break; 62 } 。。。。 可见起针对中断号pInt->num选择相应的处理函数,函数内容包括跳转虚拟的pc寄存器等等内容。 3.指令执行的模拟 x86emu将所有程序都看作是中断处理程序,因此其指令执行入口就是 X86EMU_exec()这个中断处理函数其内容如下: 92 void X86EMU_exec(void) 93 { 94 u8 op1; 95 96 M.x86.intr = 0; 97 DB(x86emu_end_instr();) 98 99 for (;;) { 100DB( if (CHECK_IP_FETCH()) 101 x86emu_check_ip_access();) 102 103 SAVE_IP_CS(M.x86.R_CS, M.x86.R_IP); 104 INC_DECODED_INST_LEN(1); 105 if (M.x86.intr) { 106 if (M.x86.intr & INTR_HALTED) { 107DB( printf("halted\n"); 108 X86EMU_trace_regs();) 109 return; 110 } 111 if (((M.x86.intr & INTR_SYNCH)&& (M.x86.intno == 0 || M.x86.intno== 2)) || 112 !ACCESS_FLAG(F_IF)) { 113 x86emu_intr_handle(); 114 } 115 } 117 //if(M.x86.debug==0x3){ 118 printf("CS:%4X,IP:%4X,AX=%4X,BX=%4X,CX=%4X,DX=%4X,SP=%4X\n",M.x86.R_CS,M.x86.R_IP, 119 M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, M.x86.R_DX,M.x86.R_SP); 120 printf("\tBP=%4X,SI=%4X,DI=%4X,DS=%4X,SS=%4X,ES=%4X\n",M.x86.R_BP,M.x86.R_SI,M.x86.R_DI, 121 M.x86.R_DS,M.x86.R_SS,M.x86.R_ES); 122 // } 123 123 124 op1 = (*sys_rdb)(((u32)M.x86.R_CS <<4) + (M.x86.R_IP++)); 127 (*x86emu_optab[op1])(op1); 128 } 129 } 如上所示,M.x86是对x86处理器的寄存器的模拟,例如M.x86.R_AX模拟cpu的%ax寄存器,M.x86.intr模拟中断寄存器。 函数是一个死循环,出口当且仅当M.x86.intr & INTR_HALTED时,即中断处理完成时. 循环过程如下: a)检测中断是否执行完,如果执行完了即:M.x86.intr & INTR_HALTED返回,否则继续 b)检测是否有中断:即((M.x86.intr & INTR_SYNCH)&& (M.x86.intno == 0 || M.x86.intno== 2)) || !ACCESS_FLAG(F_IF) 有则执行x86emu_intr_handle()函数,内容如下: 50 static void x86emu_intr_handle(void) 51 { 52 u8 intno; 53 54 if (M.x86.intr & INTR_SYNCH) { 55 intno = M.x86.intno; 56 if (_X86EMU_intrTab[intno]) { 57 (*_X86EMU_intrTab[intno])(intno); 58 } else { 59 push_word((u16)M.x86.R_FLG); 60 CLEAR_FLAG(F_IF); 61 CLEAR_FLAG(F_TF); 62 push_word(M.x86.R_CS); 63 M.x86.R_CS = mem_access_word(intno * 4 + 2); 64 push_word(M.x86.R_IP); 65 M.x86.R_IP = mem_access_word(intno * 4); 66 M.x86.intr = 0; 67 } 68 } 69 } _X86EMU_intrTab[intno]中全部是static voidx86emu_do_int(int num)函数 内容如下: 18 static void x86emu_do_int(int num) 19 { 20 Int10Current->num = num; 21printf("int10current->num=0x%x\n",num); 22 if (!int_handler(Int10Current)) { 23 printf("int_handler_int10current is error,systemhalted...\n"); 24 X86EMU_halt_sys(); 25 } 26 } 即调用int_handler跳转地址等准备工作。否则继续执行 c) 取出指令,获得操作码 即:op1 =(*sys_rdb)(((u32)M.x86.R_CS << 4) +(M.x86.R_IP++)) sys_rdb是读地址操作,M.x86.R_CS通常是0xc000 M.x86.R_IP是模拟cpu的pc寄存器,因此起获得的是0xc0000 + ip 的地址的代码即INTPriv(pInt)->base+V_BIOS开始的内容. d) 执行操作码. 即:(*x86emu_optab[op1])(op1) x86emu_optab是x86emu/int10/src/x86emu/op.c中实现的很多x86结构的指令。 如上循环不断执行直至中断处理完毕时方退出,此时便完成了对vga的初始化。