以前在实验从保护模式进入实模式进行系统调用的时候,一点儿代码调试了长达一周时间。后来发现确实一些细小的问题,例如某个寄存器高16位有数据,在虚拟机下没问题,在真机实模式下就无法正常运行了。
同样,这次试验的是vm86,在bochs和qemu都通过了,在vpc和物理机却发生各种奇妙的错误。。。
莫非。。。
下面给出VM86的主要代码:
// VM86 General Protection Fault ...
int gpf_handler( int err_code, I386_REGISTERS* r)
{
t_8* ip;
t_16* stack, *ivt, *stack32;
THREAD* thr;
ARCH_THREAD* arch;
int is_data32, is_addr32;
thr = current_thread();
// X86相关线程信息
arch = &thr->arch;
// 非vm86模式下的异常不进行处理
if( !arch->in_vm86 )
return 0;
// 得到ip的线性地址
ip = (t_8*)FARPTR_TO_LINEAR(r->cs, r->eip);
// 得到堆栈的线性地址
stack = (t_16*)FARPTR_TO_LINEAR(r->ss, r->esp);
stack32 = stack;
// 默认的bios中断向量在地址0处
ivt = (t_16*)0;
// 是否32位操作
is_data32 = is_addr32 = 0;
for(;;){
// Check the Opcode
switch( ip[0] ){
case 0x66: //32bit data
is_data32 = 1;
ip ++;
r->eip = (t_16) (r->eip + 1);
break;
case 0x67: //32bit address
is_addr32 = 1;
ip ++;
r->eip = (t_16) (r->eip + 1);
PERROR("##not supported not.");
break;
case 0xCD: //int n
switch( ip[1] ){
case 0xFB:
case 0x03: //debug
PERROR("[%d]VM86 thread exit with code:0x%X", thr->tid, r->eax);
thread_terminate( thr, r->eax );
return 0;
//never return here
case 0xFC: //delay 5 seconds
thread_wait(r->eax);
r->eip = (t_16) (r->eip + 1);
return 1;
default:
// kprintf("vm86 interrupt 0x%x at 0x%X\n", ip[1], r->eip );
stack -= 3;
r->esp = ((r->esp & 0xffff) - 6) & 0xffff;
stack[0] = (t_16) (r->eip + 2);
stack[1] = r->cs;
stack[2] = (t_16) r->eflags;
//!!check the IF if necessary
if( arch->vmflag_if )
stack[2] |= EFLAG_IF;
else
stack[2] &= ~EFLAG_IF;
r->cs = ivt[ip[1] * 2 + 1];
r->eip = ivt[ip[1] * 2];
return 1;
}
case 0x9c: //pushf
if( is_data32 ){
r->esp = ((r->esp & 0xffff) - 4) & 0xffff;
stack32--;
stack32[0] = r->eflags;
if( arch->vmflag_if )
stack32[0] |= EFLAG_IF;
else
stack32[0] &= ~EFLAG_IF;
}else{
r->esp = ((r->esp & 0xffff) - 2) & 0xffff;
stack--;
stack[0] = (t_16) r->eflags;
if( arch->vmflag_if )
stack[0] |= EFLAG_IF;
else
stack[0] &= ~EFLAG_IF;
}
r->eip = (t_16) (r->eip + 1);
return 1;
case 0x9d: //popf
if( is_data32 ){
r->eflags = stack32[0]|EFLAG_VM|EFLAG_IF;
r->esp = ((r->esp & 0xffff) + 4) & 0xffff;
arch->vmflag_if = (stack32[0]&EFLAG_IF)!=0;
}else{
r->eflags = stack[0]|EFLAG_VM|EFLAG_IF;
r->esp = ((r->esp & 0xffff) + 2) & 0xffff;
arch->vmflag_if = (stack[0]&EFLAG_IF)!=0;
}
r->eip = (t_16) (r->eip + 1);
return 1;
case 0xEE: //out byte
out_byte(r->edx, r->eax&0xFF);
r->eip = (t_16) (r->eip + 1);
return 1;
case 0xEF: //out word
/* 我看的资料上竟然没有说在0xEF前加0x66要输出32位值,
* 在VMware上无法改变分辨率。后来发现此处最可疑,想不到
* 改成dword后竟然成功了。我真聪明:-)
*/
if( is_data32 ){
out_dword(r->edx, r->eax);
}else{
out_word(r->edx, r->eax&0xFFFF);
}
r->eip = (t_16) (r->eip + 1);
return 1;
case 0xEC: //in byte
r->eax = (r->eax&0xFFFFFF00)|in_byte(r->edx);
r->eip = (t_16) (r->eip + 1);
return 1;
case 0xED: //in word
if( is_data32 ){
r->eax = in_dword(r->edx);
}else{
r->eax = (r->eax&0xFFFF0000)|in_word(r->edx);
}
r->eip = (t_16) (r->eip + 1);
return 1;
case 0xCF: //iret
if( is_data32 ){
r->eip = stack32[0];
r->cs = stack32[1];
r->eflags = EFLAG_VM|EFLAG_IF|stack32[2];
arch->vmflag_if = (stack32[2]&EFLAG_IF)!=0;
r->esp = ((r->esp & 0xffff) + 12) & 0xffff;
}else{
r->eip = stack[0];
r->cs = stack[1];
r->eflags = EFLAG_VM|EFLAG_IF|stack[2];
arch->vmflag_if = (stack[2]&EFLAG_IF)!=0;
r->esp = ((r->esp & 0xffff) + 6) & 0xffff;
}
return 1;
case 0xFA: //cli
arch->vmflag_if = 0;
r->eip = (t_16) (r->eip + 1);
return 1;
case 0xFB: //sti
arch->vmflag_if = 1;
r->eip = (t_16) (r->eip + 1);
return 1;
default:
PERROR("Unhandled Opcode: 0x%X at 0x%X", ip[0], ip );
return 0;
}
}
return 0; //kill the thread
}
If you are smart enough, you will understand that I'm not a simple boy!
2009年8月29日星期六
自旋一奇怪问题!!
订阅:
博文评论 (Atom)
没有评论:
发表评论