qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Christopher Covington <cov@codeaurora.org>
To: qemu-devel@nongnu.org
Cc: Christopher Covington <cov@codeaurora.org>
Subject: [Qemu-devel] [RFC 14/14] bbvec: Properly detect conditional thumb2 branching instructions
Date: Wed,  5 Aug 2015 12:51:23 -0400	[thread overview]
Message-ID: <1438793483-12721-15-git-send-email-cov@codeaurora.org> (raw)
In-Reply-To: <1438793483-12721-1-git-send-email-cov@codeaurora.org>

Add bbvec detection of thumb2 branch instructions via a stripped-down
version of the thumb2 instruction decoding logic. Unfortunately, this
code still called into disas_neon_ls_insn(), which is apparently not
free from side effects. Therefore, calling into this function twice
(once in the added bbvec thumb2 branch detection code and once in the
original decoding code) causes QEMU to report segfaults and illegal
instruction exceptions to the guest kernel (possibly among other
undesirable, still-undiscovered behavior). Because neon instructions
are not allowed to write to the PC, eliminating the call to
disas_neon_ls_insn() altogether is safe.

Written by Aaron Lindsay.

Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
 target-arm/translate.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 261 insertions(+), 3 deletions(-)

diff --git a/target-arm/translate.c b/target-arm/translate.c
index f9d69ef..8e9b97b 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -9411,6 +9411,260 @@ gen_thumb2_data_op(DisasContext *s, int op, int conds, uint32_t shifter_out,
     return 0;
 }
 
+#ifdef CONFIG_BBVEC
+/* Call gen_store_is_jmp(1) if the instruction is a thumb2 branch instruction.
+ * This is called by disas_thumb_insn. The switch/case/if structures are a
+ * simplified copy of the logic in disas_thumb2_insn */
+static void thumb2_branch_detection(CPUARMState *env, DisasContext *s, uint16_t insn_hw1)
+{
+    uint32_t insn, shift, rd, rn, rs;
+    int op;
+
+    if (!(arm_dc_feature(s, ARM_FEATURE_THUMB2)
+          || arm_dc_feature(s, ARM_FEATURE_M))) {
+        /* Thumb-1 cores may need to treat bl and blx as a pair of
+           16-bit instructions to get correct prefetch abort behavior.  */
+        insn = insn_hw1;
+        if ((insn & (1 << 12)) == 0) {
+            ARCH(5);
+            gen_store_is_jmp(1);
+            return;
+        }
+        if (insn & (1 << 11)) {
+            gen_store_is_jmp(1);
+            return;
+        }
+        if ((s->pc & ~TARGET_PAGE_MASK) == 0) {
+            return;
+        }
+        /* Fall through to 32-bit decode.  */
+    }
+
+    insn = arm_lduw_code(env, s->pc, s->bswap_code);
+    insn |= (uint32_t)insn_hw1 << 16;
+
+    if ((insn & 0xf800e800) != 0xf000e800) {
+        ARCH(6T2);
+    }
+
+    rn = (insn >> 16) & 0xf;
+    rs = (insn >> 12) & 0xf;
+    rd = (insn >> 8) & 0xf;
+    switch ((insn >> 25) & 0xf) {
+    case 0: case 1: case 2: case 3:
+        /* 16-bit instructions.  Should never happen.  */
+        return;
+    case 4:
+        if (insn & (1 << 22)) {
+            /* Other load/store, table branch.  */
+            if (insn & 0x01200000) {
+                /* Load/store doubleword.  */
+                if (insn & (1 << 20)) {
+                    /* ldrd */
+                    if (rs == 15 || rd == 15)
+                        gen_store_is_jmp(1);
+                }
+            } else if ((insn & (1 << 23)) == 0) {
+                /* Load/store exclusive word.  */
+            } else if ((insn & (7 << 5)) == 0) {
+                /* Table Branch.  */
+                gen_store_is_jmp(1);
+            } else {
+                int op2 = (insn >> 6) & 0x3;
+                op = (insn >> 4) & 0x3;
+                switch (op2) {
+                case 0:
+                    return;
+                case 1:
+                    /* Load/store exclusive byte/halfword/doubleword */
+                    if (op == 2) {
+                        return;
+                    }
+                    ARCH(7);
+                    break;
+                case 2:
+                    /* Load-acquire/store-release */
+                    if (op == 3) {
+                        return;
+                    }
+                    /* Fall through */
+                case 3:
+                    /* Load-acquire/store-release exclusive */
+                    ARCH(8);
+                    break;
+                }
+                if (!(op2 & 1)) {
+                    if (insn & (1 << 20)) {
+                        if (op >= 0 && op <= 2 && rs == 15)
+                            gen_store_is_jmp(1);
+                    }
+                } else if (insn & (1 << 20)) {
+                    if (rs == 15 || rd == 15)
+                        gen_store_is_jmp(1);
+                }
+            }
+        } else {
+            /* Load/store multiple, RFE, SRS.  */
+            if (((insn >> 23) & 1) == ((insn >> 24) & 1)) {
+                /* RFE, SRS: not available in user mode or on M profile */
+                if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_M)) {
+                    goto illegal_op;
+                }
+                if (insn & (1 << 20)) {
+                    /* rfe */
+                    if (insn & (1 << 21)) {
+                        /* Base writeback.  */
+                        if (rn == 15)
+                            gen_store_is_jmp(1);
+                    }
+                }
+            } else {
+                int i, loaded_base = 0;
+                /* Load/store multiple.  */
+                for (i = 0; i < 16; i++) {
+                    if ((insn & (1 << i)) == 0)
+                        continue;
+                    if (insn & (1 << 20)) {
+                        /* Load.  */
+                        if (i == 15) {
+                            gen_store_is_jmp(1);
+                        } else if (i == rn) {
+                            loaded_base = 1;
+                        } else {
+                            if (i == 15)
+                                gen_store_is_jmp(1);
+                        }
+                    }
+                }
+                if (loaded_base) {
+                    if (rn == 15)
+                        gen_store_is_jmp(1);
+                }
+                if (insn & (1 << 21)) {
+                    /* Base register writeback.  */
+                    /* Fault if writeback register is in register list.  */
+                    if (insn & (1 << rn))
+                        goto illegal_op;
+                    if (rn == 15)
+                        gen_store_is_jmp(1);
+                }
+            }
+        }
+        break;
+    case 5:
+        op = (insn >> 21) & 0xf;
+        if (op == 6) {
+            if (rd == 15)
+                gen_store_is_jmp(1);
+        }
+        break;
+    case 13: /* Misc data processing.  */
+        op = ((insn >> 22) & 6) | ((insn >> 7) & 1);
+        if (op < 4 && (insn & 0xf000) != 0xf000)
+            goto illegal_op;
+        switch (op) {
+        case 0: /* Register controlled shift.  */
+            if ((insn & 0x70) != 0)
+                goto illegal_op;
+            if (rd == 15)
+                gen_store_is_jmp(1);
+            break;
+        case 1: /* Sign/zero extend.  */
+            op = (insn >> 20) & 7;
+            if (op < 0 || op > 5)
+                return;
+            if (rd == 15)
+                gen_store_is_jmp(1);
+            break;
+        case 2: /* SIMD add/subtract.  */
+            op = (insn >> 20) & 7;
+            shift = (insn >> 4) & 7;
+            if ((op & 3) == 3 || (shift & 3) == 3)
+                goto illegal_op;
+        case 3: /* Other data processing.  */
+        case 4: case 5: /* 32-bit multiply.  Sum of absolute differences.  */
+            if (rd == 15)
+                gen_store_is_jmp(1);
+            break;
+        case 6: case 7: /* 64-bit multiply, Divide.  */
+            op = ((insn >> 4) & 0xf) | ((insn >> 16) & 0x70);
+            if ((op & 0x50) == 0x10) {
+                /* sdiv, udiv */
+                if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DIV)) {
+                    goto illegal_op;
+                }
+
+                if (rd == 15)
+                    gen_store_is_jmp(1);
+            }
+            break;
+        }
+        break;
+    case 8: case 9: case 10: case 11:
+        if (insn & (1 << 15)) {
+            /* Branches, misc control.  */
+            if (insn & 0x5000) {
+                /* Unconditional branch.  */
+                gen_store_is_jmp(1);
+            } else if (((insn >> 23) & 7) == 7) {
+                /* Misc control */
+                if (insn & (1 << 13))
+                    goto illegal_op;
+
+                if ((insn & (1 << 26)) == 0) {
+                    op = (insn >> 20) & 7;
+                    if (op == 4) /* bxj */
+                        gen_store_is_jmp(1);
+                }
+            } else {
+                /* Conditional branch.  */
+                gen_store_is_jmp(1);
+            }
+        } else {
+            /* Data processing immediate.  */
+            if (insn & (1 << 25)) {
+                if (insn & (1 << 24)) {
+                    if (insn & (1 << 20))
+                        goto illegal_op;
+                    /* Bitfield/Saturate.  */
+                    if (rd == 15)
+                        gen_store_is_jmp(1);
+                } else {
+                    if (rd == 15)
+                        gen_store_is_jmp(1);
+                }
+            }
+        }
+        break;
+    case 12: /* Load/store single data item.  */
+        {
+        if ((insn & 0x01100000) == 0x01000000)
+            break; // Ignore neon instructions, they should not update the PC
+        op = ((insn >> 21) & 3) | ((insn >> 22) & 4);
+        if (insn & (1 << 20)) {
+            /* Load.  */
+            switch (op) {
+            case 0:
+            case 4:
+            case 1:
+            case 5:
+            case 2:
+                break;
+            default:
+                goto illegal_op;
+            }
+            if (rs == 15) {
+                gen_store_is_jmp(1);
+            }
+        }
+        }
+        break;
+    }
+illegal_op:
+    return;
+}
+#endif // CONFIG_BBVEC
+
 /* Translate a 32-bit thumb instruction.  Returns nonzero if the instruction
    is not legal.  */
 static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw1)
@@ -10763,11 +11017,15 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
                         gen_store_is_jmp(1);
                     break;
                 case 14:
-                    /* branch instruction */
-                    if (!(insn & (1 << 11)))
+                    if (insn & (1 << 11)) {
+                        thumb2_branch_detection(env, s, insn);
+                    } else {
+                        /* branch instruction */
                         gen_store_is_jmp(1);
+                    }
                     break;
-                default:
+                case 15:
+                    thumb2_branch_detection(env, s, insn);
                     break;
                 }
             }
-- 
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

  parent reply	other threads:[~2015-08-05 16:52 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 01/14] Make unknown semihosting calls non-fatal Christopher Covington
2015-08-06  9:11   ` Alex Bennée
2015-08-06 17:59     ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 02/14] Added semihosting support for A64 in full-system mode Christopher Covington
2015-08-11 18:16   ` Peter Maydell
2015-08-05 16:51 ` [Qemu-devel] [RFC 03/14] Fix makefile Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 04/14] Modify load exclusive/store exclusive to use physical addresses with the monitor Christopher Covington
2015-09-23 17:19   ` [Qemu-devel] [PATCHv2] target-arm: Use physical addresses for ldrex/strex Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 05/14] Fixed TLB invalidate ops Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 06/14] Added support for block profiling for AArch32 and Aarch64 Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 07/14] Add PMU to ARM virt platform Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 08/14] Add instruction-counting infrastructure to target-arm Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality Christopher Covington
2016-02-02 21:22   ` Alistair Francis
2016-02-02 23:01     ` Christopher Covington
2016-02-02 23:22       ` Alistair Francis
2016-02-03 18:37         ` Peter Maydell
2016-02-04  0:37           ` Alistair Francis
2015-08-05 16:51 ` [Qemu-devel] [RFC 10/14] bbvec: Move mode/PID change detection to register writes Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 11/14] Print bbvec stats on 'magic' exceptions Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 12/14] bbvec: Detect mode changes after uncached_cpsr update Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 13/14] Enable negative icount values for QEMU Christopher Covington
2015-08-05 16:51 ` Christopher Covington [this message]
2015-08-11 15:27 ` [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1438793483-12721-15-git-send-email-cov@codeaurora.org \
    --to=cov@codeaurora.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).