[PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
Russell King - ARM Linux
linux at arm.linux.org.uk
Sat Jul 31 07:29:16 EDT 2010
On Sat, Jul 31, 2010 at 11:52:25AM +0100, Russell King - ARM Linux wrote:
> On Fri, Jul 30, 2010 at 01:04:54PM +0300, Siarhei Siamashka wrote:
> > On Thursday 29 July 2010 22:28:56 ext Pavel Machek wrote:
> > > I believe emulation just to annoy users into submitting bugreports is
> > > serious overengineering.
> >
> > It's not to annoy them, but to provide the users with the information about
> > valid problems in their applications. It's up to the users to either do
> > anything with it, or ignore.
>
> What's missing is that the SWP instruction will eventually be dropped
> from the later ARM architecture revisions, so sooner or later programs
> will break without either SWP emulation support or being fixed.
>
> The SWP instruction is already deprecated from ARMv6, and in ARMv6 it
> was still present (see the note in section A3.4). In ARMv7, it defaults
> to being disabled and causing an illegal instruction fault. The next
> step will be to remove it from the hardware entirely which I suspect
> isn't that far away.
>
> So this patch makes total sense.
>
> What I do think we need to do is collect all the definitions for decoding
> instructions together - we have stuff like (in kprobes-decode.c):
>
> int rd = (insn >> 12) & 0xf;
>
> when we also have in arch/arm/mm/alignment.c:
>
> #define RD_BITS(i) ((i >> 12) & 15) /* Rd */
>
> and it seems we're going to get another version of this for the swp
> emulation support.
First stab at merging these ARM ISA decoding macros/code - I'm sure
there's more which can be done:
diff --git a/arch/arm/include/asm/arm-isa.h b/arch/arm/include/asm/arm-isa.h
index e69de29..aad7dd3 100644
--- a/arch/arm/include/asm/arm-isa.h
+++ b/arch/arm/include/asm/arm-isa.h
@@ -0,0 +1,36 @@
+#ifndef __ASM_ARM_ISA_H
+#define __ASM_ARM_ISA_H
+
+#define CODING_BITS(i) ((i) & 0x0e000000)
+
+#define LDST_I_BIT(i) ((i) & (1 << 26)) /* Immediate constant */
+#define LDST_P_BIT(i) ((i) & (1 << 24)) /* Preindex */
+#define LDST_U_BIT(i) ((i) & (1 << 23)) /* Add offset */
+#define LDST_W_BIT(i) ((i) & (1 << 21)) /* Writeback */
+#define LDST_L_BIT(i) ((i) & (1 << 20)) /* Load */
+
+#define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0)
+
+#define LDSTHD_I_BIT(i) ((i) & (1 << 22)) /* double/half-word immed */
+
+#define RN_BITS(i) (((i) >> 16) & 15) /* Rn */
+#define RD_BITS(i) (((i) >> 12) & 15) /* Rd */
+#define RM_BITS(i) ((i) & 15) /* Rm */
+
+#define OFFSET_BITS(i) ((i) & 0x0fff)
+
+#define LDM_S_BIT(i) ((i) & (1 << 22)) /* write CPSR from SPSR */
+#define LDM_REGMASK(i) ((i) & 0xffff)
+
+#define IS_SHIFT(i) (i & 0x0ff0)
+#define SHIFT_BITS(i) ((i >> 7) & 0x1f)
+#define SHIFT_TYPE(i) (i & 0x60)
+#define SHIFT_LSL 0x00
+#define SHIFT_LSR 0x20
+#define SHIFT_ASR 0x40
+#define SHIFT_RORRRX 0x60
+
+#define IS_T32(hi16) \
+ (((hi16) & 0xe000) == 0xe000 && ((hi16) & 0x1800))
+
+#endif
diff --git a/arch/arm/kernel/kprobes-decode.c b/arch/arm/kernel/kprobes-decode.c
index 8bccbfa..a11c45d 100644
--- a/arch/arm/kernel/kprobes-decode.c
+++ b/arch/arm/kernel/kprobes-decode.c
@@ -64,6 +64,8 @@
#include <linux/kernel.h>
#include <linux/kprobes.h>
+#include <asm/arm-isa.h>
+
#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
@@ -448,7 +450,7 @@ static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rm = insn & 0xf;
+ int rm = RM_BITS(insn);
long rmv = regs->uregs[rm];
if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
@@ -467,11 +469,11 @@ static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rn = (insn >> 16) & 0xf;
- int lbit = insn & (1 << 20);
- int wbit = insn & (1 << 21);
- int ubit = insn & (1 << 23);
- int pbit = insn & (1 << 24);
+ int rn = RN_BITS(insn);
+ int lbit = LDST_L_BIT(insn);
+ int wbit = LDST_W_BIT(insn);
+ int ubit = LDST_U_BIT(insn);
+ int pbit = LDST_P_BIT(insn);
long *addr = (long *)regs->uregs[rn];
int reg_bit_vector;
int reg_count;
@@ -480,7 +482,7 @@ static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
return;
reg_count = 0;
- reg_bit_vector = insn & 0xffff;
+ reg_bit_vector = LDM_REGMASK(insn);
while (reg_bit_vector) {
reg_bit_vector &= (reg_bit_vector - 1);
++reg_count;
@@ -490,7 +492,7 @@ static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
addr -= reg_count;
addr += (!pbit == !ubit);
- reg_bit_vector = insn & 0xffff;
+ reg_bit_vector = LDM_REGMASK(insn);
while (reg_bit_vector) {
int reg = __ffs(reg_bit_vector);
reg_bit_vector &= (reg_bit_vector - 1);
@@ -529,7 +531,7 @@ static void __kprobes emulate_ldcstc(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rn = (insn >> 16) & 0xf;
+ int rn = RN_BITS(insn);
long rnv = regs->uregs[rn];
/* Save Rn in case of writeback. */
@@ -540,9 +542,9 @@ static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs)
{
insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
- int rm = insn & 0xf; /* rm may be invalid, don't care. */
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn);
+ int rm = RM_BITS(insn); /* rm may be invalid, don't care. */
/* Not following the C calling convention here, so need asm(). */
__asm__ __volatile__ (
@@ -568,9 +570,9 @@ static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs)
{
insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
- int rm = insn & 0xf;
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn);
+ int rm = RM_BITS(insn);
long rnv = regs->uregs[rn];
long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
@@ -585,9 +587,9 @@ static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs)
kprobe_opcode_t insn = p->opcode;
long ppc = (long)p->addr + 8;
union reg_pair fnr;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
- int rm = insn & 0xf;
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn);
+ int rm = RM_BITS(insn);
long rdv;
long rnv = (rn == 15) ? ppc : regs->uregs[rn];
long rmv = (rm == 15) ? ppc : regs->uregs[rm];
@@ -616,9 +618,9 @@ static void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs)
insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
long iaddr = (long)p->addr;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
- int rm = insn & 0xf;
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn);
+ int rm = RM_BITS(insn);
long rdv = (rd == 15) ? iaddr + str_pc_offset : regs->uregs[rd];
long rnv = (rn == 15) ? iaddr + 8 : regs->uregs[rn];
long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
@@ -633,8 +635,8 @@ static void __kprobes emulate_mrrc(struct kprobe *p, struct pt_regs *regs)
insn_llret_0arg_fn_t *i_fn = (insn_llret_0arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
union reg_pair fnr;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn);
fnr.dr = insnslot_llret_0arg_rflags(regs->ARM_cpsr, i_fn);
regs->uregs[rn] = fnr.r0;
@@ -645,8 +647,8 @@ static void __kprobes emulate_mcrr(struct kprobe *p, struct pt_regs *regs)
{
insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn);
long rnv = regs->uregs[rn];
long rdv = regs->uregs[rd];
@@ -657,8 +659,8 @@ static void __kprobes emulate_sat(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rm = insn & 0xf;
+ int rd = RD_BITS(insn);
+ int rm = RM_BITS(insn);
long rmv = regs->uregs[rm];
/* Writes Q flag */
@@ -669,9 +671,9 @@ static void __kprobes emulate_sel(struct kprobe *p, struct pt_regs *regs)
{
insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
- int rm = insn & 0xf;
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn);
+ int rm = RM_BITS(insn);
long rnv = regs->uregs[rn];
long rmv = regs->uregs[rm];
@@ -690,7 +692,7 @@ static void __kprobes emulate_rd12(struct kprobe *p, struct pt_regs *regs)
{
insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
+ int rd = RD_BITS(insn);
regs->uregs[rd] = insnslot_0arg_rflags(regs->ARM_cpsr, i_fn);
}
@@ -699,7 +701,7 @@ static void __kprobes emulate_ird12(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int ird = (insn >> 12) & 0xf;
+ int ird = RD_BITS(insn);
insnslot_1arg_rflags(regs->uregs[ird], regs->ARM_cpsr, i_fn);
}
@@ -708,7 +710,7 @@ static void __kprobes emulate_rn16(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rn = (insn >> 16) & 0xf;
+ int rn = RN_BITS(insn);
long rnv = regs->uregs[rn];
insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
@@ -718,8 +720,8 @@ static void __kprobes emulate_rd12rm0(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rm = insn & 0xf;
+ int rd = RD_BITS(insn);
+ int rm = RM_BITS(insn);
long rmv = regs->uregs[rm];
regs->uregs[rd] = insnslot_1arg_rflags(rmv, regs->ARM_cpsr, i_fn);
@@ -730,9 +732,9 @@ emulate_rd12rn16rm0_rwflags(struct kprobe *p, struct pt_regs *regs)
{
insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
- int rm = insn & 0xf;
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn);
+ int rm = RM_BITS(insn);
long rnv = regs->uregs[rn];
long rmv = regs->uregs[rm];
@@ -745,10 +747,10 @@ emulate_rd16rn12rs8rm0_rwflags(struct kprobe *p, struct pt_regs *regs)
{
insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 16) & 0xf;
- int rn = (insn >> 12) & 0xf;
+ int rd = RN_BITS(insn); /* intentionally swapped! */
+ int rn = RD_BITS(insn); /* intentionally swapped! */
int rs = (insn >> 8) & 0xf;
- int rm = insn & 0xf;
+ int rm = RM_BITS(insn);
long rnv = regs->uregs[rn];
long rsv = regs->uregs[rs];
long rmv = regs->uregs[rm];
@@ -762,9 +764,9 @@ emulate_rd16rs8rm0_rwflags(struct kprobe *p, struct pt_regs *regs)
{
insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 16) & 0xf;
+ int rd = RN_BITS(insn);
int rs = (insn >> 8) & 0xf;
- int rm = insn & 0xf;
+ int rm = RM_BITS(insn);
long rsv = regs->uregs[rs];
long rmv = regs->uregs[rm];
@@ -778,10 +780,10 @@ emulate_rdhi16rdlo12rs8rm0_rwflags(struct kprobe *p, struct pt_regs *regs)
insn_llret_4arg_fn_t *i_fn = (insn_llret_4arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
union reg_pair fnr;
- int rdhi = (insn >> 16) & 0xf;
- int rdlo = (insn >> 12) & 0xf;
+ int rdhi = RN_BITS(insn);
+ int rdlo = RD_BITS(insn);
int rs = (insn >> 8) & 0xf;
- int rm = insn & 0xf;
+ int rm = RM_BITS(insn);
long rsv = regs->uregs[rs];
long rmv = regs->uregs[rm];
@@ -797,8 +799,8 @@ emulate_alu_imm_rflags(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn);
long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn];
regs->uregs[rd] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
@@ -809,8 +811,8 @@ emulate_alu_imm_rwflags(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn);
long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn];
regs->uregs[rd] = insnslot_1arg_rwflags(rnv, ®s->ARM_cpsr, i_fn);
@@ -822,10 +824,10 @@ emulate_alu_rflags(struct kprobe *p, struct pt_regs *regs)
insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
long ppc = (long)p->addr + 8;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn); /* rn/rnv/rs/rsv may be */
int rs = (insn >> 8) & 0xf; /* invalid, don't care. */
- int rm = insn & 0xf;
+ int rm = RM_BITS(insn);
long rnv = (rn == 15) ? ppc : regs->uregs[rn];
long rmv = (rm == 15) ? ppc : regs->uregs[rm];
long rsv = regs->uregs[rs];
@@ -840,10 +842,10 @@ emulate_alu_rwflags(struct kprobe *p, struct pt_regs *regs)
insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
long ppc = (long)p->addr + 8;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */
+ int rd = RD_BITS(insn);
+ int rn = RN_BITS(insn); /* rn/rnv/rs/rsv may be */
int rs = (insn >> 8) & 0xf; /* invalid, don't care. */
- int rm = insn & 0xf;
+ int rm = RM_BITS(insn);
long rnv = (rn == 15) ? ppc : regs->uregs[rn];
long rmv = (rm == 15) ? ppc : regs->uregs[rm];
long rsv = regs->uregs[rs];
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index 6f98c35..77ebbd3 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -22,6 +22,7 @@
#include <linux/sched.h>
#include <linux/uaccess.h>
+#include <asm/arm-isa.h>
#include <asm/unaligned.h>
#include "fault.h"
@@ -36,39 +37,9 @@
* *** NOTE ***
* This code is not portable to processors with late data abort handling.
*/
-#define CODING_BITS(i) (i & 0x0e000000)
-
-#define LDST_I_BIT(i) (i & (1 << 26)) /* Immediate constant */
-#define LDST_P_BIT(i) (i & (1 << 24)) /* Preindex */
-#define LDST_U_BIT(i) (i & (1 << 23)) /* Add offset */
-#define LDST_W_BIT(i) (i & (1 << 21)) /* Writeback */
-#define LDST_L_BIT(i) (i & (1 << 20)) /* Load */
-
-#define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0)
-
-#define LDSTHD_I_BIT(i) (i & (1 << 22)) /* double/half-word immed */
-#define LDM_S_BIT(i) (i & (1 << 22)) /* write CPSR from SPSR */
-
-#define RN_BITS(i) ((i >> 16) & 15) /* Rn */
-#define RD_BITS(i) ((i >> 12) & 15) /* Rd */
-#define RM_BITS(i) (i & 15) /* Rm */
-
-#define REGMASK_BITS(i) (i & 0xffff)
-#define OFFSET_BITS(i) (i & 0x0fff)
-
-#define IS_SHIFT(i) (i & 0x0ff0)
-#define SHIFT_BITS(i) ((i >> 7) & 0x1f)
-#define SHIFT_TYPE(i) (i & 0x60)
-#define SHIFT_LSL 0x00
-#define SHIFT_LSR 0x20
-#define SHIFT_ASR 0x40
-#define SHIFT_RORRRX 0x60
-
#define BAD_INSTR 0xdeadc0de
/* Thumb-2 32 bit format per ARMv7 DDI0406A A6.3, either f800h,e800h,f800h */
-#define IS_T32(hi16) \
- (((hi16) & 0xe000) == 0xe000 && ((hi16) & 0x1800))
static unsigned long ai_user;
static unsigned long ai_sys;
@@ -462,7 +433,7 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg
ai_multi += 1;
/* count the number of registers in the mask to be transferred */
- nr_regs = hweight16(REGMASK_BITS(instr)) * 4;
+ nr_regs = hweight16(LDM_REGMASK(instr)) * 4;
rn = RN_BITS(instr);
newaddr = eaddr = regs->uregs[rn];
@@ -497,7 +468,7 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg
#endif
if (user_mode(regs)) {
- for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
+ for (regbits = LDM_REGMASK(instr), rd = 0; regbits;
regbits >>= 1, rd += 1)
if (regbits & 1) {
if (LDST_L_BIT(instr)) {
@@ -509,7 +480,7 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg
eaddr += 4;
}
} else {
- for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
+ for (regbits = LDM_REGMASK(instr), rd = 0; regbits;
regbits >>= 1, rd += 1)
if (regbits & 1) {
if (LDST_L_BIT(instr)) {
@@ -524,7 +495,7 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg
if (LDST_W_BIT(instr))
regs->uregs[rn] = newaddr;
- if (!LDST_L_BIT(instr) || !(REGMASK_BITS(instr) & (1 << 15)))
+ if (!LDST_L_BIT(instr) || !(LDM_REGMASK(instr) & (1 << 15)))
regs->ARM_pc -= correction;
return TYPE_DONE;
More information about the linux-arm-kernel
mailing list