mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
svm: Improve emulation of SMSW instruction for memory operands.
From: Trolle Selander <trolle.selander@gmail.com> Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
d049abba4d
commit
1064d675ae
1 file changed
+58
-6
@@ -1866,11 +1866,13 @@ static int svm_cr_access(struct vcpu *v, unsigned int cr, unsigned int type,
|
||||
{
|
||||
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};
|
||||
@@ -1931,9 +1933,59 @@ static int svm_cr_access(struct vcpu *v, unsigned int cr, unsigned int type,
|
||||
break;
|
||||
|
||||
case INSTR_SMSW:
|
||||
value = v->arch.hvm_svm.cpu_shadow_cr0;
|
||||
gpreg = decode_src_reg(prefix, buffer[index+2]);
|
||||
set_reg(gpreg, value, regs, vmcb);
|
||||
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);
|
||||
}
|
||||
/*
|
||||
* 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)) )
|
||||
{
|
||||
gdprintk(XENLOG_ERR, "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);
|
||||
addr += offset;
|
||||
hvm_copy_to_guest_virt(addr,&value,2);
|
||||
}
|
||||
else
|
||||
{
|
||||
gdprintk(XENLOG_ERR, "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;
|
||||
|
||||
default:
|
||||
|
||||