本文主要简要描述下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的初始化。