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