diff mbox

[for-2.4] tcg/i386: ignore high bits for user mode 32-bit qemu_ld/st

Message ID 1436968536-24106-1-git-send-email-aurelien@aurel32.net
State New
Headers show

Commit Message

Aurelien Jarno July 15, 2015, 1:55 p.m. UTC
For a 64-bit host not implementing the trunc_shr_i32 op, the high bits
of a register should be ignored for 32-bit ops. This is currently not
the case of qemu_ld/st ops in user mode.

Fix that by either using the ADDR32 prefix (in case GUEST_BASE == 0 or
a segment register is in use), or by doing an explicit zero-extension.
The zero-extension can be done in place as we know the registers holds
a 32-bit value.

Reported-by: Leon Alrae <leon.alrae@imgtec.com>
Cc: Leon Alrae <leon.alrae@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Richard Henderson <rth@twiddle.net>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 tcg/i386/tcg-target.c | 44 ++++++++++++++++++++++++++++++++------------
 1 file changed, 32 insertions(+), 12 deletions(-)

Comments

Leon Alrae July 15, 2015, 3:06 p.m. UTC | #1
On 15/07/2015 14:55, Aurelien Jarno wrote:
> For a 64-bit host not implementing the trunc_shr_i32 op, the high bits
> of a register should be ignored for 32-bit ops. This is currently not
> the case of qemu_ld/st ops in user mode.
> 
> Fix that by either using the ADDR32 prefix (in case GUEST_BASE == 0 or
> a segment register is in use), or by doing an explicit zero-extension.
> The zero-extension can be done in place as we know the registers holds
> a 32-bit value.
> 
> Reported-by: Leon Alrae <leon.alrae@imgtec.com>
> Cc: Leon Alrae <leon.alrae@imgtec.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Richard Henderson <rth@twiddle.net>
> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
> ---
>  tcg/i386/tcg-target.c | 44 ++++++++++++++++++++++++++++++++------------
>  1 file changed, 32 insertions(+), 12 deletions(-)

Tested-by: Leon Alrae <leon.alrae@imgtec.com>

Thanks for looking into this!

Leon
Richard Henderson July 16, 2015, 9:21 p.m. UTC | #2
On 07/15/2015 02:55 PM, Aurelien Jarno wrote:
> Fix that by either using the ADDR32 prefix (in case GUEST_BASE == 0 or
> a segment register is in use), or by doing an explicit zero-extension.
> The zero-extension can be done in place as we know the registers holds
> a 32-bit value.

I'd prefer not to do that, even if we can show that it's true.  I have an 
alternative that I'll post shortly.


r~
diff mbox

Patch

diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c
index f952645..f77e6c1 100644
--- a/tcg/i386/tcg-target.c
+++ b/tcg/i386/tcg-target.c
@@ -1570,12 +1570,21 @@  static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64)
         int32_t offset = GUEST_BASE;
         TCGReg base = addrlo;
         int seg = 0;
+        int addr32 = 0;
+
+        /* The x86 TCG backend doesn't implement the trunc_shr_i32 op.  This
+           means the high bits of a register should be ignored for 32-bit ops.
+           For that we either use the ADDR32 prefix (in case GUEST_BASE == 0
+           or a segment register is in use), or by doing an explicit
+           zero-extension.  */
+        if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) {
+            if (GUEST_BASE == 0 || guest_base_flags) {
+                addr32 = P_ADDR32;
+            } else {
+                tcg_out_ext32u(s, base, base);
+            }
+        }
 
-        /* ??? We assume all operations have left us with register contents
-           that are zero extended.  So far this appears to be true.  If we
-           want to enforce this, we can either do an explicit zero-extension
-           here, or (if GUEST_BASE == 0, or a segment register is in use)
-           use the ADDR32 prefix.  For now, do nothing.  */
         if (GUEST_BASE && guest_base_flags) {
             seg = guest_base_flags;
             offset = 0;
@@ -1586,7 +1595,8 @@  static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64)
             offset = 0;
         }
 
-        tcg_out_qemu_ld_direct(s, datalo, datahi, base, offset, seg, opc);
+        tcg_out_qemu_ld_direct(s, datalo, datahi, base, offset,
+                               addr32 | seg, opc);
     }
 #endif
 }
@@ -1701,12 +1711,21 @@  static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64)
         int32_t offset = GUEST_BASE;
         TCGReg base = addrlo;
         int seg = 0;
+        int addr32 = 0;
+
+        /* The x86 TCG backend doesn't implement the trunc_shr_i32 op.  This
+           means the high bits of a register should be ignored for 32-bit ops.
+           For that we either use the ADDR32 prefix (in case GUEST_BASE == 0
+           or a segment register is in use), or by doing an explicit
+           zero-extension.  */
+        if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) {
+            if (GUEST_BASE == 0 || guest_base_flags) {
+                addr32 = P_ADDR32;
+            } else {
+                tcg_out_ext32u(s, base, base);
+            }
+        }
 
-        /* ??? We assume all operations have left us with register contents
-           that are zero extended.  So far this appears to be true.  If we
-           want to enforce this, we can either do an explicit zero-extension
-           here, or (if GUEST_BASE == 0, or a segment register is in use)
-           use the ADDR32 prefix.  For now, do nothing.  */
         if (GUEST_BASE && guest_base_flags) {
             seg = guest_base_flags;
             offset = 0;
@@ -1717,7 +1736,8 @@  static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64)
             offset = 0;
         }
 
-        tcg_out_qemu_st_direct(s, datalo, datahi, base, offset, seg, opc);
+        tcg_out_qemu_st_direct(s, datalo, datahi, base, offset,
+                               addr32 | seg, opc);
     }
 #endif
 }