Fix scsi sector size confusion (Blue Swirl).
[qemu] / target-arm / translate.c
index 089fbf2..c582d4b 100644 (file)
@@ -526,6 +526,17 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn)
     uint32_t rd, rn, rm, op, i, n, offset, delta_d, delta_m, bank_mask;
     int dp, veclen;
 
+    if (!arm_feature(env, ARM_FEATURE_VFP))
+        return 1;
+
+    if ((env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) == 0) {
+        /* VFP disabled.  Only allow fmxr/fmrx to/from fpexc and fpsid.  */
+        if ((insn & 0x0fe00fff) != 0x0ee00a10)
+            return 1;
+        rn = (insn >> 16) & 0xf;
+        if (rn != 0 && rn != 8)
+            return 1;
+    }
     dp = ((insn & 0xf00) == 0xb00);
     switch ((insn >> 24) & 0xf) {
     case 0xe:
@@ -563,11 +574,15 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn)
                     /* vfp->arm */
                     if (insn & (1 << 21)) {
                         /* system register */
+                        rn >>= 1;
                         switch (rn) {
-                        case 0: /* fpsid */
-                            n = 0x0091A0000;
+                        case ARM_VFP_FPSID:
+                        case ARM_VFP_FPEXC:
+                        case ARM_VFP_FPINST:
+                        case ARM_VFP_FPINST2:
+                            gen_op_vfp_movl_T0_xreg(rn);
                             break;
-                        case 2: /* fpscr */
+                        case ARM_VFP_FPSCR:
                            if (rd == 15)
                                gen_op_vfp_movl_T0_fpscr_flags();
                            else
@@ -589,17 +604,24 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn)
                     /* arm->vfp */
                     gen_movl_T0_reg(s, rd);
                     if (insn & (1 << 21)) {
+                        rn >>= 1;
                         /* system register */
                         switch (rn) {
-                        case 0: /* fpsid */
+                        case ARM_VFP_FPSID:
                             /* Writes are ignored.  */
                             break;
-                        case 2: /* fpscr */
+                        case ARM_VFP_FPSCR:
                             gen_op_vfp_movl_fpscr_T0();
-                            /* This could change vector settings, so jump to
-                               the next instuction.  */
                             gen_lookup_tb(s);
                             break;
+                        case ARM_VFP_FPEXC:
+                            gen_op_vfp_movl_xreg_T0(rn);
+                            gen_lookup_tb(s);
+                            break;
+                        case ARM_VFP_FPINST:
+                        case ARM_VFP_FPINST2:
+                            gen_op_vfp_movl_xreg_T0(rn);
+                            break;
                         default:
                             return 1;
                         }
@@ -1033,7 +1055,7 @@ static inline void gen_mulxy(int x, int y)
 }
 
 /* Return the mask of PSR bits set by a MSR instruction.  */
-static uint32_t msr_mask(DisasContext *s, int flags) {
+static uint32_t msr_mask(DisasContext *s, int flags, int spsr) {
     uint32_t mask;
 
     mask = 0;
@@ -1045,8 +1067,11 @@ static uint32_t msr_mask(DisasContext *s, int flags) {
         mask |= 0xff0000;
     if (flags & (1 << 3))
         mask |= 0xff000000;
-    /* Mask out undefined bits and state bits.  */
-    mask &= 0xf89f03df;
+    /* Mask out undefined bits.  */
+    mask &= 0xf90f03ff;
+    /* Mask out state bits.  */
+    if (!spsr)
+        mask &= ~0x01000020;
     /* Mask out privileged bits.  */
     if (IS_USER(s))
         mask &= 0xf80f0200;
@@ -1138,8 +1163,8 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
         if (shift)
             val = (val >> shift) | (val << (32 - shift));
         gen_op_movl_T0_im(val);
-        if (gen_set_psr_T0(s, msr_mask(s, (insn >> 16) & 0xf),
-                           (insn & (1 << 22)) != 0))
+        i = ((insn & (1 << 22)) != 0);
+        if (gen_set_psr_T0(s, msr_mask(s, (insn >> 16) & 0xf, i), i))
             goto illegal_op;
     } else if ((insn & 0x0f900000) == 0x01000000
                && (insn & 0x00000090) != 0x00000090) {
@@ -1152,11 +1177,11 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
             if (op1 & 1) {
                 /* PSR = reg */
                 gen_movl_T0_reg(s, rm);
-                if (gen_set_psr_T0(s, msr_mask(s, (insn >> 16) & 0xf),
-                                   (op1 & 2) != 0))
+                i = ((op1 & 2) != 0);
+                if (gen_set_psr_T0(s, msr_mask(s, (insn >> 16) & 0xf, i), i))
                     goto illegal_op;
             } else {
-                /* reg = CPSR */
+                /* reg = PSR */
                 rd = (insn >> 12) & 0xf;
                 if (op1 & 2) {
                     if (IS_USER(s))
@@ -1217,6 +1242,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
                 gen_op_addl_T0_T1_saturate();
             gen_movl_reg_T0(s, rd);
             break;
+        case 7: /* bkpt */
+            gen_op_movl_T0_im((long)s->pc - 4);
+            gen_op_movl_reg_TN[0][15]();
+            gen_op_bkpt();
+            s->is_jmp = DISAS_JUMP;
+            break;
         case 0x8: /* signed multiply */
         case 0xa:
         case 0xc:
@@ -1558,6 +1589,15 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
         case 0x5:
         case 0x6:
         case 0x7:
+            /* Check for undefined extension instructions
+             * per the ARM Bible IE:
+             * xxxx 0111 1111 xxxx  xxxx xxxx 1111 xxxx
+             */
+            sh = (0xf << 20) | (0xf << 4);
+            if (op1 == 0x7 && ((insn & sh) == sh))
+            {
+                goto illegal_op;
+            }
             /* load/store byte/word */
             rn = (insn >> 16) & 0xf;
             rd = (insn >> 12) & 0xf;
@@ -1621,7 +1661,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
         case 0x08:
         case 0x09:
             {
-                int j, n, user;
+                int j, n, user, loaded_base;
                 /* load/store multiple words */
                 /* XXX: store correct base if write back */
                 user = 0;
@@ -1636,6 +1676,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
                 gen_movl_T1_reg(s, rn);
                 
                 /* compute total size */
+                loaded_base = 0;
                 n = 0;
                 for(i=0;i<16;i++) {
                     if (insn & (1 << i))
@@ -1669,6 +1710,9 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
                                 gen_bx(s);
                             } else if (user) {
                                 gen_op_movl_user_T0(i);
+                            } else if (i == rn) {
+                                gen_op_movl_T2_T0();
+                                loaded_base = 1;
                             } else {
                                 gen_movl_reg_T0(s, i);
                             }
@@ -1712,6 +1756,10 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
                     }
                     gen_movl_reg_T1(s, rn);
                 }
+                if (loaded_base) {
+                    gen_op_movl_T0_T2();
+                    gen_movl_reg_T0(s, rn);
+                }
                 if ((insn & (1 << 22)) && !user) {
                     /* Restore CPSR from SPSR.  */
                     gen_op_movl_T0_spsr();
@@ -1916,12 +1964,15 @@ static void disas_thumb_insn(DisasContext *s)
             break;
         case 0x2: /* lsl */
             gen_op_shll_T1_T0_cc();
+            gen_op_logic_T1_cc();
             break;
         case 0x3: /* lsr */
             gen_op_shrl_T1_T0_cc();
+            gen_op_logic_T1_cc();
             break;
         case 0x4: /* asr */
             gen_op_sarl_T1_T0_cc();
+            gen_op_logic_T1_cc();
             break;
         case 0x5: /* adc */
             gen_op_adcl_T0_T1_cc();
@@ -1931,6 +1982,7 @@ static void disas_thumb_insn(DisasContext *s)
             break;
         case 0x7: /* ror */
             gen_op_rorl_T1_T0_cc();
+            gen_op_logic_T1_cc();
             break;
         case 0x8: /* tst */
             gen_op_andl_T0_T1();
@@ -2183,6 +2235,13 @@ static void disas_thumb_insn(DisasContext *s)
                 gen_bx(s);
             break;
 
+        case 0xe: /* bkpt */
+            gen_op_movl_T0_im((long)s->pc - 2);
+            gen_op_movl_reg_TN[0][15]();
+            gen_op_bkpt();
+            s->is_jmp = DISAS_JUMP;
+            break;
+
         default:
             goto undef;
         }
@@ -2245,8 +2304,21 @@ static void disas_thumb_insn(DisasContext *s)
 
     case 14:
         /* unconditional branch */
-        if (insn & (1 << 11))
-            goto undef; /* Second half of a blx */
+        if (insn & (1 << 11)) {
+            /* Second half of blx.  */
+            offset = ((insn & 0x7ff) << 1);
+            gen_movl_T0_reg(s, 14);
+            gen_op_movl_T1_im(offset);
+            gen_op_addl_T0_T1();
+            gen_op_movl_T1_im(0xfffffffc);
+            gen_op_andl_T0_T1();
+
+            val = (uint32_t)s->pc;
+            gen_op_movl_T1_im(val | 1);
+            gen_movl_reg_T1(s, 14);
+            gen_bx(s);
+            break;
+        }
         val = (uint32_t)s->pc;
         offset = ((int32_t)insn << 21) >> 21;
         val += (offset << 1) + 2;
@@ -2255,6 +2327,29 @@ static void disas_thumb_insn(DisasContext *s)
 
     case 15:
         /* branch and link [and switch to arm] */
+        if ((s->pc & ~TARGET_PAGE_MASK) == 0) {
+            /* Instruction spans a page boundary.  Implement it as two
+               16-bit instructions in case the second half causes an
+               prefetch abort.  */
+            offset = ((int32_t)insn << 21) >> 9;
+            val = s->pc + 2 + offset;
+            gen_op_movl_T0_im(val);
+            gen_movl_reg_T0(s, 14);
+            break;
+        }
+        if (insn & (1 << 11)) {
+            /* Second half of bl.  */
+            offset = ((insn & 0x7ff) << 1) | 1;
+            gen_movl_T0_reg(s, 14);
+            gen_op_movl_T1_im(offset);
+            gen_op_addl_T0_T1();
+
+            val = (uint32_t)s->pc;
+            gen_op_movl_T1_im(val | 1);
+            gen_movl_reg_T1(s, 14);
+            gen_bx(s);
+            break;
+        }
         offset = ((int32_t)insn << 21) >> 10;
         insn = lduw_code(s->pc);
         offset |= insn & 0x7ff;
@@ -2428,35 +2523,6 @@ int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
     return gen_intermediate_code_internal(env, tb, 1);
 }
 
-void cpu_reset(CPUARMState *env)
-{
-#if defined (CONFIG_USER_ONLY)
-    env->uncached_cpsr = ARM_CPU_MODE_USR;
-#else
-    /* SVC mode with interrupts disabled.  */
-    env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
-#endif
-    env->regs[15] = 0;
-}
-
-CPUARMState *cpu_arm_init(void)
-{
-    CPUARMState *env;
-
-    env = qemu_mallocz(sizeof(CPUARMState));
-    if (!env)
-        return NULL;
-    cpu_exec_init(env);
-    cpu_reset(env);
-    tlb_flush(env, 1);
-    return env;
-}
-
-void cpu_arm_close(CPUARMState *env)
-{
-    free(env);
-}
-
 static const char *cpu_mode_names[16] = {
   "usr", "fiq", "irq", "svc", "???", "???", "???", "abt",
   "???", "???", "???", "und", "???", "???", "???", "sys"
@@ -2500,6 +2566,6 @@ void cpu_dump_state(CPUState *env, FILE *f,
                     i, (int)(uint32_t)d.l.upper, (int)(uint32_t)d.l.lower,
                     d.d);
     }
-    cpu_fprintf(f, "FPSCR: %08x\n", (int)env->vfp.fpscr);
+    cpu_fprintf(f, "FPSCR: %08x\n", (int)env->vfp.xregs[ARM_VFP_FPSCR]);
 }