diff -uNr xen-unstable.hg-pristine/xen/arch/x86/hvm/svm/svm.c xen-unstable.hg/xen/arch/x86/hvm/svm/svm.c --- xen-unstable.hg-pristine/xen/arch/x86/hvm/svm/svm.c 2007-03-13 02:17:37.000000000 +0100 +++ xen-unstable.hg/xen/arch/x86/hvm/svm/svm.c 2007-03-13 12:11:02.000000000 +0100 @@ -2156,11 +2156,13 @@ { struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; int inst_len = 0; - int index; - unsigned int gpreg; - unsigned long value; + int index,addr_size,i; + unsigned int gpreg,offset; + unsigned long value,addr; u8 buffer[MAX_INST_LEN]; u8 prefix = 0; + u8 modrm; + enum x86_segment seg; int result = 1; enum instruction_index list_a[] = {INSTR_MOV2CR, INSTR_CLTS, INSTR_LMSW}; enum instruction_index list_b[] = {INSTR_MOVCR2, INSTR_SMSW}; @@ -2244,16 +2246,61 @@ break; case INSTR_SMSW: - if (svm_dbg_on) - svm_dump_inst(svm_rip2pointer(v)); - value = v->arch.hvm_svm.cpu_shadow_cr0; - gpreg = decode_src_reg(prefix, buffer[index+2]); - set_reg(gpreg, value, regs, vmcb); + if (svm_dbg_on) + svm_dump_inst(svm_rip2pointer(v)); + value = v->arch.hvm_svm.cpu_shadow_cr0 & 0xFFFF; + modrm = buffer[index+2]; + addr_size = svm_guest_x86_mode( v ); + if ( likely( ( modrm & 0xC0 ) >> 6 == 3 ) ) + { + gpreg = decode_src_reg(prefix, modrm); + set_reg(gpreg, value, regs, vmcb); + if ( svm_dbg_on ) + printk("CR0-SMSW value=%lx, reg=%d, inst_len=%d\n", value, gpreg, inst_len); + } + /* For now, only implement decode of the offset mode, since that's the only mode observed in a real-world OS. + This code is also making the assumption that we'll never hit this code in long mode... */ + else if ( modrm == 0x26 || modrm == 0x25 ) + { + seg = x86_seg_ds; + i = index; + /* Segment or address size overrides? */ + while ( i-- ) { + switch ( buffer[i] ) + { + case 0x26: seg = x86_seg_es;break; + case 0x2e: seg = x86_seg_cs;break; + case 0x36: seg = x86_seg_ss;break; + case 0x64: seg = x86_seg_fs;break; + case 0x65: seg = x86_seg_gs;break; + case 0x67: addr_size ^= 6 ;break; + } + } + /* Bail unless this really is a seg_base + offset case */ + if ( ( modrm == 0x26 && addr_size == 4 ) || ( modrm == 0x25 && addr_size == 2 ) ) { + printk("SMSW emulation at guest address: %lx failed due to unhandled addressing mode." + "ModRM byte was: %x \n", svm_rip2pointer(v), modrm); + domain_crash(v->domain); + } + inst_len += addr_size; + offset = *(( unsigned int *) ( void *) &buffer[index + 3]); + offset = ( addr_size == 4 ) ? offset : ( offset & 0xFFFF ); + addr = hvm_get_segment_base(v, seg); + if ( svm_dbg_on ) { + printk("CR0-SMSW->mem value=%lx, base addr = %lx," + " offset = %i, inst_len=%d \n", value, addr, offset, inst_len); + } + addr += offset; + hvm_copy_to_guest_virt(addr,&value,2); + } + else + { + printk("SMSW emulation at guest address: %lx failed due to unhandled addressing mode!" + "ModRM byte was: %x \n", svm_rip2pointer(v), modrm); + domain_crash(v->domain); + } + break; - if (svm_dbg_on) - printk("CR0-SMSW value=%lx, reg=%d, inst_len=%d\n", value, gpreg, - inst_len); - break; default: BUG();