|
@@ -384,3 +384,150 @@ do_alignment_ldrdstrd(unsigned long addr, unsigned long instr,
|
|
unsigned int rd2;
|
|
unsigned int rd2;
|
|
int load;
|
|
int load;
|
|
|
|
|
|
|
|
+ if ((instr & 0xfe000000) == 0xe8000000) {
|
|
|
|
+ /* ARMv7 Thumb-2 32-bit LDRD/STRD */
|
|
|
|
+ rd2 = (instr >> 8) & 0xf;
|
|
|
|
+ load = !!(LDST_L_BIT(instr));
|
|
|
|
+ } else if (((rd & 1) == 1) || (rd == 14))
|
|
|
|
+ goto bad;
|
|
|
|
+ else {
|
|
|
|
+ load = ((instr & 0xf0) == 0xd0);
|
|
|
|
+ rd2 = rd + 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ai_dword += 1;
|
|
|
|
+
|
|
|
|
+ if (user_mode(regs))
|
|
|
|
+ goto user;
|
|
|
|
+
|
|
|
|
+ if (load) {
|
|
|
|
+ unsigned long val;
|
|
|
|
+ get32_unaligned_check(val, addr);
|
|
|
|
+ regs->uregs[rd] = val;
|
|
|
|
+ get32_unaligned_check(val, addr + 4);
|
|
|
|
+ regs->uregs[rd2] = val;
|
|
|
|
+ } else {
|
|
|
|
+ put32_unaligned_check(regs->uregs[rd], addr);
|
|
|
|
+ put32_unaligned_check(regs->uregs[rd2], addr + 4);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return TYPE_LDST;
|
|
|
|
+
|
|
|
|
+ user:
|
|
|
|
+ if (load) {
|
|
|
|
+ unsigned long val;
|
|
|
|
+ get32t_unaligned_check(val, addr);
|
|
|
|
+ regs->uregs[rd] = val;
|
|
|
|
+ get32t_unaligned_check(val, addr + 4);
|
|
|
|
+ regs->uregs[rd2] = val;
|
|
|
|
+ } else {
|
|
|
|
+ put32t_unaligned_check(regs->uregs[rd], addr);
|
|
|
|
+ put32t_unaligned_check(regs->uregs[rd2], addr + 4);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return TYPE_LDST;
|
|
|
|
+ bad:
|
|
|
|
+ return TYPE_ERROR;
|
|
|
|
+ fault:
|
|
|
|
+ return TYPE_FAULT;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+do_alignment_ldrstr(unsigned long addr, unsigned long instr, struct pt_regs *regs)
|
|
|
|
+{
|
|
|
|
+ unsigned int rd = RD_BITS(instr);
|
|
|
|
+
|
|
|
|
+ ai_word += 1;
|
|
|
|
+
|
|
|
|
+ if ((!LDST_P_BIT(instr) && LDST_W_BIT(instr)) || user_mode(regs))
|
|
|
|
+ goto trans;
|
|
|
|
+
|
|
|
|
+ if (LDST_L_BIT(instr)) {
|
|
|
|
+ unsigned int val;
|
|
|
|
+ get32_unaligned_check(val, addr);
|
|
|
|
+ regs->uregs[rd] = val;
|
|
|
|
+ } else
|
|
|
|
+ put32_unaligned_check(regs->uregs[rd], addr);
|
|
|
|
+ return TYPE_LDST;
|
|
|
|
+
|
|
|
|
+ trans:
|
|
|
|
+ if (LDST_L_BIT(instr)) {
|
|
|
|
+ unsigned int val;
|
|
|
|
+ get32t_unaligned_check(val, addr);
|
|
|
|
+ regs->uregs[rd] = val;
|
|
|
|
+ } else
|
|
|
|
+ put32t_unaligned_check(regs->uregs[rd], addr);
|
|
|
|
+ return TYPE_LDST;
|
|
|
|
+
|
|
|
|
+ fault:
|
|
|
|
+ return TYPE_FAULT;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * LDM/STM alignment handler.
|
|
|
|
+ *
|
|
|
|
+ * There are 4 variants of this instruction:
|
|
|
|
+ *
|
|
|
|
+ * B = rn pointer before instruction, A = rn pointer after instruction
|
|
|
|
+ * ------ increasing address ----->
|
|
|
|
+ * | | r0 | r1 | ... | rx | |
|
|
|
|
+ * PU = 01 B A
|
|
|
|
+ * PU = 11 B A
|
|
|
|
+ * PU = 00 A B
|
|
|
|
+ * PU = 10 A B
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *regs)
|
|
|
|
+{
|
|
|
|
+ unsigned int rd, rn, correction, nr_regs, regbits;
|
|
|
|
+ unsigned long eaddr, newaddr;
|
|
|
|
+
|
|
|
|
+ if (LDM_S_BIT(instr))
|
|
|
|
+ goto bad;
|
|
|
|
+
|
|
|
|
+ correction = 4; /* processor implementation defined */
|
|
|
|
+ regs->ARM_pc += correction;
|
|
|
|
+
|
|
|
|
+ ai_multi += 1;
|
|
|
|
+
|
|
|
|
+ /* count the number of registers in the mask to be transferred */
|
|
|
|
+ nr_regs = hweight16(REGMASK_BITS(instr)) * 4;
|
|
|
|
+
|
|
|
|
+ rn = RN_BITS(instr);
|
|
|
|
+ newaddr = eaddr = regs->uregs[rn];
|
|
|
|
+
|
|
|
|
+ if (!LDST_U_BIT(instr))
|
|
|
|
+ nr_regs = -nr_regs;
|
|
|
|
+ newaddr += nr_regs;
|
|
|
|
+ if (!LDST_U_BIT(instr))
|
|
|
|
+ eaddr = newaddr;
|
|
|
|
+
|
|
|
|
+ if (LDST_P_EQ_U(instr)) /* U = P */
|
|
|
|
+ eaddr += 4;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * For alignment faults on the ARM922T/ARM920T the MMU makes
|
|
|
|
+ * the FSR (and hence addr) equal to the updated base address
|
|
|
|
+ * of the multiple access rather than the restored value.
|
|
|
|
+ * Switch this message off if we've got a ARM92[02], otherwise
|
|
|
|
+ * [ls]dm alignment faults are noisy!
|
|
|
|
+ */
|
|
|
|
+#if !(defined CONFIG_CPU_ARM922T) && !(defined CONFIG_CPU_ARM920T)
|
|
|
|
+ /*
|
|
|
|
+ * This is a "hint" - we already have eaddr worked out by the
|
|
|
|
+ * processor for us.
|
|
|
|
+ */
|
|
|
|
+ if (addr != eaddr) {
|
|
|
|
+ printk(KERN_ERR "LDMSTM: PC = %08lx, instr = %08lx, "
|
|
|
|
+ "addr = %08lx, eaddr = %08lx\n",
|
|
|
|
+ instruction_pointer(regs), instr, addr, eaddr);
|
|
|
|
+ show_regs(regs);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ if (user_mode(regs)) {
|
|
|
|
+ for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
|
|
|
|
+ regbits >>= 1, rd += 1)
|
|
|
|
+ if (regbits & 1) {
|
|
|
|
+ if (LDST_L_BIT(instr)) {
|
|
|
|
+ unsigned int val;
|