x86emul: fix {,i}mul and {,i}div
authorJan Beulich <jbeulich@suse.com>
Fri, 30 Sep 2016 07:52:52 +0000 (09:52 +0200)
committerJan Beulich <jbeulich@suse.com>
Fri, 30 Sep 2016 07:52:52 +0000 (09:52 +0200)
Commit a3db233ede ("x86emul: use DstEax also for {,I}{MUL,DIV}") went
a little too far: DstEax and SrcEax weren't really meant to be used
together with ModRM - they assume modrm_reg remains zero by the time
the destination / source register pointer gets calculated. Don't fully
undo that commit though, but instead just correct the register pointer,
and don't use dst.val as input for mul and imul (div and idiv did avoid
that already).

Reported-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Tested-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
xen/arch/x86/x86_emulate/x86_emulate.c

index 53cf2dd961ecb9c87e631dd82b1cc0c13e141999..b6d3f43c682b43f1633e07bf0796ebeb47d45506 100644 (file)
@@ -3845,18 +3845,19 @@ x86_emulate(
             emulate_1op("neg", dst, _regs.eflags);
             break;
         case 4: /* mul */
+            dst.reg = (unsigned long *)&_regs.eax;
             _regs.eflags &= ~(EFLG_OF|EFLG_CF);
             switch ( dst.bytes )
             {
             case 1:
-                dst.val = (uint8_t)dst.val;
+                dst.val = (uint8_t)_regs.eax;
                 dst.val *= src.val;
                 if ( (uint8_t)dst.val != (uint16_t)dst.val )
                     _regs.eflags |= EFLG_OF|EFLG_CF;
                 dst.bytes = 2;
                 break;
             case 2:
-                dst.val = (uint16_t)dst.val;
+                dst.val = (uint16_t)_regs.eax;
                 dst.val *= src.val;
                 if ( (uint16_t)dst.val != (uint32_t)dst.val )
                     _regs.eflags |= EFLG_OF|EFLG_CF;
@@ -3864,7 +3865,7 @@ x86_emulate(
                 break;
 #ifdef __x86_64__
             case 4:
-                dst.val = (uint32_t)dst.val;
+                dst.val = _regs._eax;
                 dst.val *= src.val;
                 if ( (uint32_t)dst.val != dst.val )
                     _regs.eflags |= EFLG_OF|EFLG_CF;
@@ -3873,7 +3874,7 @@ x86_emulate(
 #endif
             default:
                 u[0] = src.val;
-                u[1] = dst.val;
+                u[1] = _regs.eax;
                 if ( mul_dbl(u) )
                     _regs.eflags |= EFLG_OF|EFLG_CF;
                 _regs.edx = u[1];
@@ -3882,12 +3883,13 @@ x86_emulate(
             }
             break;
         case 5: /* imul */
+            dst.reg = (unsigned long *)&_regs.eax;
         imul:
             _regs.eflags &= ~(EFLG_OF|EFLG_CF);
             switch ( dst.bytes )
             {
             case 1:
-                dst.val = (int8_t)src.val * (int8_t)dst.val;
+                dst.val = (int8_t)src.val * (int8_t)_regs.eax;
                 if ( (int8_t)dst.val != (int16_t)dst.val )
                     _regs.eflags |= EFLG_OF|EFLG_CF;
                 ASSERT(b > 0x6b);
@@ -3895,7 +3897,7 @@ x86_emulate(
                 break;
             case 2:
                 dst.val = ((uint32_t)(int16_t)src.val *
-                           (uint32_t)(int16_t)dst.val);
+                           (uint32_t)(int16_t)_regs.eax);
                 if ( (int16_t)dst.val != (int32_t)dst.val )
                     _regs.eflags |= EFLG_OF|EFLG_CF;
                 if ( b > 0x6b )
@@ -3904,7 +3906,7 @@ x86_emulate(
 #ifdef __x86_64__
             case 4:
                 dst.val = ((uint64_t)(int32_t)src.val *
-                           (uint64_t)(int32_t)dst.val);
+                           (uint64_t)(int32_t)_regs.eax);
                 if ( (int32_t)dst.val != dst.val )
                     _regs.eflags |= EFLG_OF|EFLG_CF;
                 if ( b > 0x6b )
@@ -3913,7 +3915,7 @@ x86_emulate(
 #endif
             default:
                 u[0] = src.val;
-                u[1] = dst.val;
+                u[1] = _regs.eax;
                 if ( imul_dbl(u) )
                     _regs.eflags |= EFLG_OF|EFLG_CF;
                 if ( b > 0x6b )
@@ -3923,6 +3925,7 @@ x86_emulate(
             }
             break;
         case 6: /* div */
+            dst.reg = (unsigned long *)&_regs.eax;
             switch ( src.bytes )
             {
             case 1:
@@ -3968,6 +3971,7 @@ x86_emulate(
             }
             break;
         case 7: /* idiv */
+            dst.reg = (unsigned long *)&_regs.eax;
             switch ( src.bytes )
             {
             case 1: