* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
@ 2010-07-19 18:02 Leif Lindholm
2010-07-19 19:49 ` Kirill A. Shutemov
2010-07-20 9:12 ` Catalin Marinas
0 siblings, 2 replies; 13+ messages in thread
From: Leif Lindholm @ 2010-07-19 18:02 UTC (permalink / raw)
To: linux-arm-kernel
The SWP instruction was deprecated in the ARMv6 architecture, superseded
by the LDREX/STREX family of instructions for
load-linked/store-conditional operations. The ARMv7 multiprocessing
extensions mandate that SWP/SWPB instructions are treated as undefined
from reset, with the ability to enable them through the System Control
Register SW bit.
This patch adds the alternative solution to emulate the SWP and SWPB
instructions using LDREX/STREX sequences, and log statistics to
/proc/cpu/swp_emulation.
Signed-off-by: Leif Lindholm <leif.lindholm@arm.com>
CC: Catalin Marinas <catalin.marinas@arm.com>
---
This patch depends on Catalin Marinas' "Remove the domain switching on
ARMv6k/v7 CPUs" patch for setting access privileges to kernel
read-only when user read-only.
Documentation/arm/00-INDEX | 2
Documentation/arm/swp_emulation | 27 ++++
arch/arm/kernel/Makefile | 1
arch/arm/kernel/swp_emulate.c | 260 +++++++++++++++++++++++++++++++++++++++
arch/arm/mm/Kconfig | 27 ++++
arch/arm/mm/proc-v7.S | 4 +
6 files changed, 321 insertions(+), 0 deletions(-)
create mode 100644 Documentation/arm/swp_emulation
create mode 100644 arch/arm/kernel/swp_emulate.c
diff --git a/Documentation/arm/00-INDEX b/Documentation/arm/00-INDEX
index 7f5fc3b..2d02564 100644
--- a/Documentation/arm/00-INDEX
+++ b/Documentation/arm/00-INDEX
@@ -32,3 +32,5 @@ memory.txt
- description of the virtual memory layout
nwfpe/
- NWFPE floating point emulator documentation
+swp_emulation
+ - SWP/SWPB emulation handler/logging description
diff --git a/Documentation/arm/swp_emulation b/Documentation/arm/swp_emulation
new file mode 100644
index 0000000..af903d2
--- /dev/null
+++ b/Documentation/arm/swp_emulation
@@ -0,0 +1,27 @@
+Software emulation of deprecated SWP instruction (CONFIG_SWP_EMULATE)
+---------------------------------------------------------------------
+
+ARMv6 architecture deprecates use of the SWP/SWPB instructions, and recommeds
+moving to the load-locked/store-conditional instructions LDREX and STREX.
+
+ARMv7 multiprocessing extensions introduce the ability to disable these
+instructions, triggering an undefined instruction exception when executed.
+Trapped instructions are emulated using an LDREX/STREX or LDREXB/STREXB
+sequence. If a memory access fault (an abort) occurs, a segmentation fault is
+signalled to the triggering process.
+
+/proc/cpu/swp_emulation holds some statistics/information, including the PID of
+the last process to trigger the emulation to be invocated. For example:
+---
+Emulated SWP: 12
+Emulated SWPB: 0
+Aborted SWP{B}: 1
+Last process: 314
+---
+
+NOTE: when accessing uncached shared regions, LDREX/STREX rely on an external
+transaction monitoring block called a global monitor to maintain update
+atomicity. If your system does not implement a global monitor, this option can
+cause programs that perform SWP operations to uncached memory to deadlock, as
+the STREX operation will always fail.
+
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 26d302c..d022b76 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_ARM_UNWIND) += unwind.o
obj-$(CONFIG_HAVE_TCM) += tcm.o
+obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o
obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312
diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c
new file mode 100644
index 0000000..c254d1d
--- /dev/null
+++ b/arch/arm/kernel/swp_emulate.c
@@ -0,0 +1,260 @@
+/*
+ * linux/arch/arm/kernel/swp_emulate.c
+ *
+ * Copyright (C) 2009 ARM Limited
+ * __user_* functions adapted from include/asm/uaccess.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Implements emulation of the SWP/SWPB instructions using load-exclusive and
+ * store-exclusive for processors that have them disabled (or future ones that
+ * might not implement them).
+ *
+ * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>]
+ * Where: Rt = destination
+ * Rt2 = source
+ * Rn = address
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+
+#include <asm/traps.h>
+#include <asm/uaccess.h>
+
+/*
+ * Error-checking SWP macros implemented using ldrex{b}/strex{b}
+ */
+#define __user_swpX_asm(data, addr, res, B) \
+ __asm__ __volatile__( \
+ " mov r3, %1\n" \
+ "0: ldrex"B" %1, [%2]\n" \
+ "1: strex"B" %0, r3, [%2]\n" \
+ " cmp %0, #0\n" \
+ " movne %0, %3\n" \
+ "2:\n" \
+ " .section .fixup,\"ax\"\n" \
+ " .align 2\n" \
+ "3: mov %0, %4\n" \
+ " b 2b\n" \
+ " .previous\n" \
+ " .section __ex_table,\"a\"\n" \
+ " .align 3\n" \
+ " .long 0b, 3b\n" \
+ " .long 1b, 3b\n" \
+ " .previous" \
+ : "=&r" (res), "+r" (data) \
+ : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \
+ : "cc", "r3")
+
+#define __user_swp_asm(data, addr, res) __user_swpX_asm(data, addr, res, "")
+#define __user_swpb_asm(data, addr, res) __user_swpX_asm(data, addr, res, "b")
+
+/*
+ * Macros/defines for extracting register numbers from instruction.
+ */
+#define EXTRACT_REG_NUM(instruction, offset) \
+ (((instruction) & (0xf << (offset))) >> (offset))
+#define RN_OFFSET 16
+#define RT_OFFSET 12
+#define RT2_OFFSET 0
+/*
+ * Bit 22 of the instruction encoding distinguishes between
+ * the SWP and SWPB variants (bit set means SWPB).
+ */
+#define TYPE_SWPB (1 << 22)
+
+static unsigned long long swpcounter;
+static unsigned long long swpbcounter;
+static unsigned long long abtcounter;
+static long previous_pid;
+
+#ifdef CONFIG_PROC_FS
+static int proc_read_status(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ char *p = page;
+ int len;
+
+ p += sprintf(p, "Emulated SWP:\t\t%llu\n", swpcounter);
+ p += sprintf(p, "Emulated SWPB:\t\t%llu\n", swpbcounter);
+ p += sprintf(p, "Aborted SWP{B}:\t\t%llu\n", abtcounter);
+ if (previous_pid != 0)
+ p += sprintf(p, "Last process:\t\t%ld\n", previous_pid);
+
+ len = (p - page) - off;
+ if (len < 0)
+ len = 0;
+
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+
+ return len;
+}
+#endif
+
+/*
+ * Set up process info to signal segmentation fault - called on access error.
+ */
+static void set_segfault(struct pt_regs *regs, unsigned long addr)
+{
+ siginfo_t info;
+
+ if (find_vma(current->mm, addr) == NULL)
+ info.si_code = SEGV_MAPERR;
+ else
+ info.si_code = SEGV_ACCERR;
+
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_addr = (void *) instruction_pointer(regs);
+
+ pr_debug("SWP{B} emulation: access caused memory abort!\n");
+ arm_notify_die("Illegal memory access", regs, &info, 0, 0);
+
+ abtcounter++;
+}
+
+static int emulate_swpX(unsigned int address, unsigned int *data,
+ unsigned int type)
+{
+ unsigned int res = 0;
+
+ if ((type != TYPE_SWPB) && (address & 0x3)) {
+ /* SWP to unaligned address not permitted */
+ pr_debug("SWP instruction on unaligned pointer!\n");
+ return -EFAULT;
+ }
+
+ while (1) {
+ /*
+ * Barrier required between accessing protected resource and
+ * releasing a lock for it. Legacy code might not have done
+ * this, and we cannot determine that this is not the case
+ * being emulated, so insert always.
+ */
+ smp_mb();
+
+ if (type == TYPE_SWPB)
+ __user_swpb_asm(*data, address, res);
+ else
+ __user_swp_asm(*data, address, res);
+
+ if (likely(res != -EAGAIN) || signal_pending(current))
+ break;
+
+ cond_resched();
+ }
+
+ if (res == 0) {
+ /*
+ * Barrier also required between aquiring a lock for a
+ * protected resource and accessing the resource. Inserted for
+ * same reason as above.
+ */
+ smp_mb();
+
+ if (type == TYPE_SWPB)
+ swpbcounter++;
+ else
+ swpcounter++;
+ }
+
+ return res;
+}
+
+/*
+ * swp_handler logs the id of calling process, dissects the instruction, sanity
+ * checks the memory location, calls emulate_swpX for the actual operation and
+ * deals with fixup/error handling before returning
+ */
+static int swp_handler(struct pt_regs *regs, unsigned int instr)
+{
+ unsigned int address, destreg, data, type;
+ unsigned int res = 0;
+
+ if (current->pid != previous_pid) {
+ pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n",
+ current->comm, (unsigned long)current->pid);
+ previous_pid = current->pid;
+ }
+
+ address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)];
+ data = regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)];
+ destreg = EXTRACT_REG_NUM(instr, RT_OFFSET);
+
+ type = instr & TYPE_SWPB;
+
+ pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n",
+ EXTRACT_REG_NUM(instr, RN_OFFSET), address,
+ destreg, EXTRACT_REG_NUM(instr, RT2_OFFSET), data);
+
+ /* Check access in reasonable access range for both SWP and SWPB */
+ if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) {
+ pr_debug("SWP{B} emulation: access to %p not allowed!\n",
+ (void *)address);
+ res = -EFAULT;
+ } else {
+ res = emulate_swpX(address, &data, type);
+ }
+
+ if (res == 0) {
+ /*
+ * On successful emulation, revert the adjustment to the PC
+ * made in kernel/traps.c in order to resume execution at the
+ * instruction following the SWP{B}.
+ */
+ regs->ARM_pc += 4;
+ regs->uregs[destreg] = data;
+ } else if (res == -EFAULT) {
+ /*
+ * Memory errors do not mean emulation failed.
+ * Set up signal info to return SEGV, then return OK
+ */
+ set_segfault(regs, address);
+ }
+
+ return 0;
+}
+
+/*
+ * Only emulate SWP/SWPB executed in ARM state/User mode.
+ * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE.
+ */
+static struct undef_hook swp_hook = {
+ .instr_mask = 0x0fb00ff0,
+ .instr_val = 0x01000090,
+ .cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT,
+ .cpsr_val = USR_MODE,
+ .fn = swp_handler
+};
+
+/*
+ * Register handler and create status file in /proc/cpu
+ * Invoked as late_initcall, since not needed before init spawned.
+ */
+static int __init swp_emulation_init(void)
+{
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *res;
+
+ res = create_proc_entry("cpu/swp_emulation", S_IRUGO, NULL);
+
+ if (!res)
+ return -ENOMEM;
+
+ res->read_proc = proc_read_status;
+#endif /* CONFIG_PROC_FS */
+
+ printk(KERN_NOTICE "Registering SWP/SWPB emulation handler\n");
+ register_undef_hook(&swp_hook);
+
+ return 0;
+}
+
+late_initcall(swp_emulation_init);
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 294a57d..0b57d73 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -636,6 +636,33 @@ config ARM_THUMBEE
Say Y here if you have a CPU with the ThumbEE extension and code to
make use of it. Say N for code that can run on CPUs without ThumbEE.
+config SWP_EMULATE
+ bool "Emulate SWP/SWPB instructions"
+ depends on CPU_V7
+ select HAVE_PROC_CPU if PROC_FS
+ default y if SMP
+ help
+ ARMv6 architecture deprecates use of the SWP/SWPB instructions.
+ ARMv7 multiprocessing extensions introduce the ability to disable
+ these instructions, triggering an undefined instruction exception
+ when executed. Say Y here to enable software emulation of these
+ instructions for userspace (not kernel) using LDREX/STREX.
+ Also creates /proc/cpu/swp_emulation for statistics.
+
+ In some older versions of glibc [<=2.8] SWP is used during futex
+ trylock() operations with the assumption that the code will not
+ be preempted. This invalid assumption may be more likely to fail
+ with SWP emulation enabled, leading to deadlock of the user
+ application.
+
+ NOTE: when accessing uncached shared regions, LDREX/STREX rely
+ on an external transaction monitoring block called a global
+ monitor to maintain update atomicity. If your system does not
+ implement a global monitor, this option can cause programs that
+ perform SWP operations to uncached memory to deadlock.
+
+ If unsure, say Y.
+
config CPU_BIG_ENDIAN
bool "Build big-endian kernel"
depends on ARCH_SUPPORTS_BIG_ENDIAN
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index c1c3fe0..656548a 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -281,6 +281,10 @@ __v7_setup:
#ifdef CONFIG_CPU_ENDIAN_BE8
orr r6, r6, #1 << 25 @ big-endian page tables
#endif
+#ifdef CONFIG_SWP_EMULATE
+ orr r5, r5, #(1 << 10) @ set SW bit in "clear"
+ bic r6, r6, #(1 << 10) @ clear it in "mmuset"
+#endif
mrc p15, 0, r0, c1, c0, 0 @ read control register
bic r0, r0, r5 @ clear bits them
orr r0, r0, r6 @ set them
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-19 18:02 [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6) Leif Lindholm
@ 2010-07-19 19:49 ` Kirill A. Shutemov
2010-07-19 20:02 ` Linus Walleij
2010-07-20 9:12 ` Catalin Marinas
1 sibling, 1 reply; 13+ messages in thread
From: Kirill A. Shutemov @ 2010-07-19 19:49 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jul 19, 2010 at 07:02:00PM +0100, Leif Lindholm wrote:
> The SWP instruction was deprecated in the ARMv6 architecture, superseded
> by the LDREX/STREX family of instructions for
> load-linked/store-conditional operations. The ARMv7 multiprocessing
> extensions mandate that SWP/SWPB instructions are treated as undefined
> from reset, with the ability to enable them through the System Control
> Register SW bit.
>
> This patch adds the alternative solution to emulate the SWP and SWPB
> instructions using LDREX/STREX sequences, and log statistics to
> /proc/cpu/swp_emulation.
Why do we need this? Why do not just fix broken programs?
> Signed-off-by: Leif Lindholm <leif.lindholm@arm.com>
> CC: Catalin Marinas <catalin.marinas@arm.com>
> ---
>
> This patch depends on Catalin Marinas' "Remove the domain switching on
> ARMv6k/v7 CPUs" patch for setting access privileges to kernel
> read-only when user read-only.
>
> Documentation/arm/00-INDEX | 2
> Documentation/arm/swp_emulation | 27 ++++
> arch/arm/kernel/Makefile | 1
> arch/arm/kernel/swp_emulate.c | 260 +++++++++++++++++++++++++++++++++++++++
> arch/arm/mm/Kconfig | 27 ++++
> arch/arm/mm/proc-v7.S | 4 +
> 6 files changed, 321 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/arm/swp_emulation
> create mode 100644 arch/arm/kernel/swp_emulate.c
>
> diff --git a/Documentation/arm/00-INDEX b/Documentation/arm/00-INDEX
> index 7f5fc3b..2d02564 100644
> --- a/Documentation/arm/00-INDEX
> +++ b/Documentation/arm/00-INDEX
> @@ -32,3 +32,5 @@ memory.txt
> - description of the virtual memory layout
> nwfpe/
> - NWFPE floating point emulator documentation
> +swp_emulation
> + - SWP/SWPB emulation handler/logging description
> diff --git a/Documentation/arm/swp_emulation b/Documentation/arm/swp_emulation
> new file mode 100644
> index 0000000..af903d2
> --- /dev/null
> +++ b/Documentation/arm/swp_emulation
> @@ -0,0 +1,27 @@
> +Software emulation of deprecated SWP instruction (CONFIG_SWP_EMULATE)
> +---------------------------------------------------------------------
> +
> +ARMv6 architecture deprecates use of the SWP/SWPB instructions, and recommeds
> +moving to the load-locked/store-conditional instructions LDREX and STREX.
> +
> +ARMv7 multiprocessing extensions introduce the ability to disable these
> +instructions, triggering an undefined instruction exception when executed.
> +Trapped instructions are emulated using an LDREX/STREX or LDREXB/STREXB
> +sequence. If a memory access fault (an abort) occurs, a segmentation fault is
> +signalled to the triggering process.
> +
> +/proc/cpu/swp_emulation holds some statistics/information, including the PID of
> +the last process to trigger the emulation to be invocated. For example:
> +---
> +Emulated SWP: 12
> +Emulated SWPB: 0
> +Aborted SWP{B}: 1
> +Last process: 314
> +---
> +
> +NOTE: when accessing uncached shared regions, LDREX/STREX rely on an external
> +transaction monitoring block called a global monitor to maintain update
> +atomicity. If your system does not implement a global monitor, this option can
> +cause programs that perform SWP operations to uncached memory to deadlock, as
> +the STREX operation will always fail.
> +
> diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
> index 26d302c..d022b76 100644
> --- a/arch/arm/kernel/Makefile
> +++ b/arch/arm/kernel/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
> obj-$(CONFIG_KGDB) += kgdb.o
> obj-$(CONFIG_ARM_UNWIND) += unwind.o
> obj-$(CONFIG_HAVE_TCM) += tcm.o
> +obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o
>
> obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
> AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312
> diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c
> new file mode 100644
> index 0000000..c254d1d
> --- /dev/null
> +++ b/arch/arm/kernel/swp_emulate.c
> @@ -0,0 +1,260 @@
> +/*
> + * linux/arch/arm/kernel/swp_emulate.c
> + *
> + * Copyright (C) 2009 ARM Limited
> + * __user_* functions adapted from include/asm/uaccess.h
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Implements emulation of the SWP/SWPB instructions using load-exclusive and
> + * store-exclusive for processors that have them disabled (or future ones that
> + * might not implement them).
> + *
> + * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>]
> + * Where: Rt = destination
> + * Rt2 = source
> + * Rn = address
> + */
> +
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/proc_fs.h>
> +#include <linux/sched.h>
> +#include <linux/syscalls.h>
> +
> +#include <asm/traps.h>
> +#include <asm/uaccess.h>
> +
> +/*
> + * Error-checking SWP macros implemented using ldrex{b}/strex{b}
> + */
> +#define __user_swpX_asm(data, addr, res, B) \
> + __asm__ __volatile__( \
> + " mov r3, %1\n" \
> + "0: ldrex"B" %1, [%2]\n" \
> + "1: strex"B" %0, r3, [%2]\n" \
> + " cmp %0, #0\n" \
> + " movne %0, %3\n" \
> + "2:\n" \
> + " .section .fixup,\"ax\"\n" \
> + " .align 2\n" \
> + "3: mov %0, %4\n" \
> + " b 2b\n" \
> + " .previous\n" \
> + " .section __ex_table,\"a\"\n" \
> + " .align 3\n" \
> + " .long 0b, 3b\n" \
> + " .long 1b, 3b\n" \
> + " .previous" \
> + : "=&r" (res), "+r" (data) \
> + : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \
> + : "cc", "r3")
> +
> +#define __user_swp_asm(data, addr, res) __user_swpX_asm(data, addr, res, "")
> +#define __user_swpb_asm(data, addr, res) __user_swpX_asm(data, addr, res, "b")
> +
> +/*
> + * Macros/defines for extracting register numbers from instruction.
> + */
> +#define EXTRACT_REG_NUM(instruction, offset) \
> + (((instruction) & (0xf << (offset))) >> (offset))
> +#define RN_OFFSET 16
> +#define RT_OFFSET 12
> +#define RT2_OFFSET 0
> +/*
> + * Bit 22 of the instruction encoding distinguishes between
> + * the SWP and SWPB variants (bit set means SWPB).
> + */
> +#define TYPE_SWPB (1 << 22)
> +
> +static unsigned long long swpcounter;
> +static unsigned long long swpbcounter;
> +static unsigned long long abtcounter;
> +static long previous_pid;
> +
> +#ifdef CONFIG_PROC_FS
> +static int proc_read_status(char *page, char **start, off_t off, int count,
> + int *eof, void *data)
> +{
> + char *p = page;
> + int len;
> +
> + p += sprintf(p, "Emulated SWP:\t\t%llu\n", swpcounter);
> + p += sprintf(p, "Emulated SWPB:\t\t%llu\n", swpbcounter);
> + p += sprintf(p, "Aborted SWP{B}:\t\t%llu\n", abtcounter);
> + if (previous_pid != 0)
> + p += sprintf(p, "Last process:\t\t%ld\n", previous_pid);
> +
> + len = (p - page) - off;
> + if (len < 0)
> + len = 0;
> +
> + *eof = (len <= count) ? 1 : 0;
> + *start = page + off;
> +
> + return len;
> +}
> +#endif
> +
> +/*
> + * Set up process info to signal segmentation fault - called on access error.
> + */
> +static void set_segfault(struct pt_regs *regs, unsigned long addr)
> +{
> + siginfo_t info;
> +
> + if (find_vma(current->mm, addr) == NULL)
> + info.si_code = SEGV_MAPERR;
> + else
> + info.si_code = SEGV_ACCERR;
> +
> + info.si_signo = SIGSEGV;
> + info.si_errno = 0;
> + info.si_addr = (void *) instruction_pointer(regs);
> +
> + pr_debug("SWP{B} emulation: access caused memory abort!\n");
> + arm_notify_die("Illegal memory access", regs, &info, 0, 0);
> +
> + abtcounter++;
> +}
> +
> +static int emulate_swpX(unsigned int address, unsigned int *data,
> + unsigned int type)
> +{
> + unsigned int res = 0;
> +
> + if ((type != TYPE_SWPB) && (address & 0x3)) {
> + /* SWP to unaligned address not permitted */
> + pr_debug("SWP instruction on unaligned pointer!\n");
> + return -EFAULT;
> + }
> +
> + while (1) {
> + /*
> + * Barrier required between accessing protected resource and
> + * releasing a lock for it. Legacy code might not have done
> + * this, and we cannot determine that this is not the case
> + * being emulated, so insert always.
> + */
> + smp_mb();
> +
> + if (type == TYPE_SWPB)
> + __user_swpb_asm(*data, address, res);
> + else
> + __user_swp_asm(*data, address, res);
> +
> + if (likely(res != -EAGAIN) || signal_pending(current))
> + break;
> +
> + cond_resched();
> + }
> +
> + if (res == 0) {
> + /*
> + * Barrier also required between aquiring a lock for a
> + * protected resource and accessing the resource. Inserted for
> + * same reason as above.
> + */
> + smp_mb();
> +
> + if (type == TYPE_SWPB)
> + swpbcounter++;
> + else
> + swpcounter++;
> + }
> +
> + return res;
> +}
> +
> +/*
> + * swp_handler logs the id of calling process, dissects the instruction, sanity
> + * checks the memory location, calls emulate_swpX for the actual operation and
> + * deals with fixup/error handling before returning
> + */
> +static int swp_handler(struct pt_regs *regs, unsigned int instr)
> +{
> + unsigned int address, destreg, data, type;
> + unsigned int res = 0;
> +
> + if (current->pid != previous_pid) {
> + pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n",
> + current->comm, (unsigned long)current->pid);
> + previous_pid = current->pid;
> + }
> +
> + address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)];
> + data = regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)];
> + destreg = EXTRACT_REG_NUM(instr, RT_OFFSET);
> +
> + type = instr & TYPE_SWPB;
> +
> + pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n",
> + EXTRACT_REG_NUM(instr, RN_OFFSET), address,
> + destreg, EXTRACT_REG_NUM(instr, RT2_OFFSET), data);
> +
> + /* Check access in reasonable access range for both SWP and SWPB */
> + if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) {
> + pr_debug("SWP{B} emulation: access to %p not allowed!\n",
> + (void *)address);
> + res = -EFAULT;
> + } else {
> + res = emulate_swpX(address, &data, type);
> + }
> +
> + if (res == 0) {
> + /*
> + * On successful emulation, revert the adjustment to the PC
> + * made in kernel/traps.c in order to resume execution at the
> + * instruction following the SWP{B}.
> + */
> + regs->ARM_pc += 4;
> + regs->uregs[destreg] = data;
> + } else if (res == -EFAULT) {
> + /*
> + * Memory errors do not mean emulation failed.
> + * Set up signal info to return SEGV, then return OK
> + */
> + set_segfault(regs, address);
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Only emulate SWP/SWPB executed in ARM state/User mode.
> + * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE.
> + */
> +static struct undef_hook swp_hook = {
> + .instr_mask = 0x0fb00ff0,
> + .instr_val = 0x01000090,
> + .cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT,
> + .cpsr_val = USR_MODE,
> + .fn = swp_handler
> +};
> +
> +/*
> + * Register handler and create status file in /proc/cpu
> + * Invoked as late_initcall, since not needed before init spawned.
> + */
> +static int __init swp_emulation_init(void)
> +{
> +#ifdef CONFIG_PROC_FS
> + struct proc_dir_entry *res;
> +
> + res = create_proc_entry("cpu/swp_emulation", S_IRUGO, NULL);
> +
> + if (!res)
> + return -ENOMEM;
> +
> + res->read_proc = proc_read_status;
> +#endif /* CONFIG_PROC_FS */
> +
> + printk(KERN_NOTICE "Registering SWP/SWPB emulation handler\n");
> + register_undef_hook(&swp_hook);
> +
> + return 0;
> +}
> +
> +late_initcall(swp_emulation_init);
> diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
> index 294a57d..0b57d73 100644
> --- a/arch/arm/mm/Kconfig
> +++ b/arch/arm/mm/Kconfig
> @@ -636,6 +636,33 @@ config ARM_THUMBEE
> Say Y here if you have a CPU with the ThumbEE extension and code to
> make use of it. Say N for code that can run on CPUs without ThumbEE.
>
> +config SWP_EMULATE
> + bool "Emulate SWP/SWPB instructions"
> + depends on CPU_V7
> + select HAVE_PROC_CPU if PROC_FS
> + default y if SMP
> + help
> + ARMv6 architecture deprecates use of the SWP/SWPB instructions.
> + ARMv7 multiprocessing extensions introduce the ability to disable
> + these instructions, triggering an undefined instruction exception
> + when executed. Say Y here to enable software emulation of these
> + instructions for userspace (not kernel) using LDREX/STREX.
> + Also creates /proc/cpu/swp_emulation for statistics.
> +
> + In some older versions of glibc [<=2.8] SWP is used during futex
> + trylock() operations with the assumption that the code will not
> + be preempted. This invalid assumption may be more likely to fail
> + with SWP emulation enabled, leading to deadlock of the user
> + application.
> +
> + NOTE: when accessing uncached shared regions, LDREX/STREX rely
> + on an external transaction monitoring block called a global
> + monitor to maintain update atomicity. If your system does not
> + implement a global monitor, this option can cause programs that
> + perform SWP operations to uncached memory to deadlock.
> +
> + If unsure, say Y.
> +
> config CPU_BIG_ENDIAN
> bool "Build big-endian kernel"
> depends on ARCH_SUPPORTS_BIG_ENDIAN
> diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
> index c1c3fe0..656548a 100644
> --- a/arch/arm/mm/proc-v7.S
> +++ b/arch/arm/mm/proc-v7.S
> @@ -281,6 +281,10 @@ __v7_setup:
> #ifdef CONFIG_CPU_ENDIAN_BE8
> orr r6, r6, #1 << 25 @ big-endian page tables
> #endif
> +#ifdef CONFIG_SWP_EMULATE
> + orr r5, r5, #(1 << 10) @ set SW bit in "clear"
> + bic r6, r6, #(1 << 10) @ clear it in "mmuset"
> +#endif
> mrc p15, 0, r0, c1, c0, 0 @ read control register
> bic r0, r0, r5 @ clear bits them
> orr r0, r0, r6 @ set them
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
Kirill A. Shutemov
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-19 19:49 ` Kirill A. Shutemov
@ 2010-07-19 20:02 ` Linus Walleij
2010-07-19 21:26 ` Kirill A. Shutemov
` (2 more replies)
0 siblings, 3 replies; 13+ messages in thread
From: Linus Walleij @ 2010-07-19 20:02 UTC (permalink / raw)
To: linux-arm-kernel
2010/7/19 Kirill A. Shutemov <kirill@shutemov.name>:
> Why do we need this? Why do not just fix broken programs?
People tend to use some third-party precompiled binaries, some of
them compiled with a compiler using SWP.
A typical example is the CDC Java runtime, JRE.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-19 20:02 ` Linus Walleij
@ 2010-07-19 21:26 ` Kirill A. Shutemov
2010-07-28 6:06 ` Pavel Machek
2010-08-03 19:42 ` Jamie Lokier
2 siblings, 0 replies; 13+ messages in thread
From: Kirill A. Shutemov @ 2010-07-19 21:26 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jul 19, 2010 at 10:02:12PM +0200, Linus Walleij wrote:
> 2010/7/19 Kirill A. Shutemov <kirill@shutemov.name>:
>
> > Why do we need this? Why do not just fix broken programs?
>
> People tend to use some third-party precompiled binaries, some of
> them compiled with a compiler using SWP.
>
> A typical example is the CDC Java runtime, JRE.
I think beter to describe it in commit message.
--
Kirill A. Shutemov
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-19 18:02 [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6) Leif Lindholm
2010-07-19 19:49 ` Kirill A. Shutemov
@ 2010-07-20 9:12 ` Catalin Marinas
1 sibling, 0 replies; 13+ messages in thread
From: Catalin Marinas @ 2010-07-20 9:12 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, 2010-07-19 at 19:02 +0100, Leif Lindholm wrote:
> The SWP instruction was deprecated in the ARMv6 architecture, superseded
> by the LDREX/STREX family of instructions for
> load-linked/store-conditional operations. The ARMv7 multiprocessing
> extensions mandate that SWP/SWPB instructions are treated as undefined
> from reset, with the ability to enable them through the System Control
> Register SW bit.
>
> This patch adds the alternative solution to emulate the SWP and SWPB
> instructions using LDREX/STREX sequences, and log statistics to
> /proc/cpu/swp_emulation.
>
> Signed-off-by: Leif Lindholm <leif.lindholm@arm.com>
> CC: Catalin Marinas <catalin.marinas@arm.com>
> ---
>
> This patch depends on Catalin Marinas' "Remove the domain switching on
> ARMv6k/v7 CPUs" patch for setting access privileges to kernel
> read-only when user read-only.
Since this patch depends on my domains one, I'll also merge it in my
public tree until the domains patch is accepted.
Just for clarification - the reason it depends on my domains patch is
because of the permission change for read-only user mappings (from
kernel read/write to kernel read-only so that we get COW during SWP
emulation).
An alternative would be to extract the permission settings part from my
domains patch and merge it separately but I really don't think it's
worth the effort as we need the domains patch in the kernel.
In the meantime:
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-19 20:02 ` Linus Walleij
2010-07-19 21:26 ` Kirill A. Shutemov
@ 2010-07-28 6:06 ` Pavel Machek
2010-07-29 10:13 ` Siarhei Siamashka
2010-08-03 19:42 ` Jamie Lokier
2 siblings, 1 reply; 13+ messages in thread
From: Pavel Machek @ 2010-07-28 6:06 UTC (permalink / raw)
To: linux-arm-kernel
On Mon 2010-07-19 22:02:12, Linus Walleij wrote:
> 2010/7/19 Kirill A. Shutemov <kirill@shutemov.name>:
>
> > Why do we need this? Why do not just fix broken programs?
>
> People tend to use some third-party precompiled binaries, some of
> them compiled with a compiler using SWP.
Why not just let the cpu emulate it? It surely will be faster...?
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-28 6:06 ` Pavel Machek
@ 2010-07-29 10:13 ` Siarhei Siamashka
2010-07-29 19:28 ` Pavel Machek
0 siblings, 1 reply; 13+ messages in thread
From: Siarhei Siamashka @ 2010-07-29 10:13 UTC (permalink / raw)
To: linux-arm-kernel
On Wednesday 28 July 2010 09:06:49 ext Pavel Machek wrote:
> On Mon 2010-07-19 22:02:12, Linus Walleij wrote:
> > 2010/7/19 Kirill A. Shutemov <kirill@shutemov.name>:
> > > Why do we need this? Why do not just fix broken programs?
> >
> > People tend to use some third-party precompiled binaries, some of
> > them compiled with a compiler using SWP.
>
> Why not just let the cpu emulate it? It surely will be faster...?
IMHO, being able to identify the usage of SWP instructions and fix bad
programs eventually (by spamming the said third-party programs bugtrackers) is
a good enough reason.
That's somewhat similar to userspace unaligned memory accesses handling,
except that the kernel being totally silent about them by default is not
helping much in practice.
--
Best regards,
Siarhei Siamashka
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-29 10:13 ` Siarhei Siamashka
@ 2010-07-29 19:28 ` Pavel Machek
2010-07-30 10:04 ` Siarhei Siamashka
0 siblings, 1 reply; 13+ messages in thread
From: Pavel Machek @ 2010-07-29 19:28 UTC (permalink / raw)
To: linux-arm-kernel
On Thu 2010-07-29 13:13:29, Siarhei Siamashka wrote:
> On Wednesday 28 July 2010 09:06:49 ext Pavel Machek wrote:
> > On Mon 2010-07-19 22:02:12, Linus Walleij wrote:
> > > 2010/7/19 Kirill A. Shutemov <kirill@shutemov.name>:
> > > > Why do we need this? Why do not just fix broken programs?
> > >
> > > People tend to use some third-party precompiled binaries, some of
> > > them compiled with a compiler using SWP.
> >
> > Why not just let the cpu emulate it? It surely will be faster...?
>
> IMHO, being able to identify the usage of SWP instructions and fix bad
> programs eventually (by spamming the said third-party programs bugtrackers) is
> a good enough reason.
Why do we need _emulation_ for that?
Either you care about compatibility, then you let the CPU emulate SWP,
or you want to fix your userspace, so you turn off SWP emulation and
see what breaks.
I believe emulation just to annoy users into submitting bugreports is
serious overengineering.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-29 19:28 ` Pavel Machek
@ 2010-07-30 10:04 ` Siarhei Siamashka
2010-07-31 10:52 ` Russell King - ARM Linux
0 siblings, 1 reply; 13+ messages in thread
From: Siarhei Siamashka @ 2010-07-30 10:04 UTC (permalink / raw)
To: linux-arm-kernel
On Thursday 29 July 2010 22:28:56 ext Pavel Machek wrote:
> On Thu 2010-07-29 13:13:29, Siarhei Siamashka wrote:
> > On Wednesday 28 July 2010 09:06:49 ext Pavel Machek wrote:
> > > On Mon 2010-07-19 22:02:12, Linus Walleij wrote:
> > > > 2010/7/19 Kirill A. Shutemov <kirill@shutemov.name>:
> > > > > Why do we need this? Why do not just fix broken programs?
> > > >
> > > > People tend to use some third-party precompiled binaries, some of
> > > > them compiled with a compiler using SWP.
> > >
> > > Why not just let the cpu emulate it? It surely will be faster...?
> >
> > IMHO, being able to identify the usage of SWP instructions and fix bad
> > programs eventually (by spamming the said third-party programs
> > bugtrackers) is a good enough reason.
>
> Why do we need _emulation_ for that?
In order to have it trapped and recorded in log, while still not causing
disasterous effects for the users (their applications still work).
> Either you care about compatibility, then you let the CPU emulate SWP,
> or you want to fix your userspace, so you turn off SWP emulation and
> see what breaks.
In practice, nobody would turn off SWP emulation ever in such setup, and the
problems will remain unresolved for ages.
> 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.
--
Best regards,
Siarhei Siamashka
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-30 10:04 ` Siarhei Siamashka
@ 2010-07-31 10:52 ` Russell King - ARM Linux
2010-07-31 11:29 ` Russell King - ARM Linux
0 siblings, 1 reply; 13+ messages in thread
From: Russell King - ARM Linux @ 2010-07-31 10:52 UTC (permalink / raw)
To: linux-arm-kernel
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.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-31 10:52 ` Russell King - ARM Linux
@ 2010-07-31 11:29 ` Russell King - ARM Linux
2010-08-02 18:45 ` Leif Lindholm
0 siblings, 1 reply; 13+ messages in thread
From: Russell King - ARM Linux @ 2010-07-31 11:29 UTC (permalink / raw)
To: linux-arm-kernel
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;
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-31 11:29 ` Russell King - ARM Linux
@ 2010-08-02 18:45 ` Leif Lindholm
0 siblings, 0 replies; 13+ messages in thread
From: Leif Lindholm @ 2010-08-02 18:45 UTC (permalink / raw)
To: linux-arm-kernel
> -----Original Message-----
> From: Russell King - ARM Linux [mailto:linux at arm.linux.org.uk]
> Sent: 31 July 2010 12:29
> 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
> +#define RN_BITS(i) (((i) >> 16) & 15) /* Rn */
> +#define RD_BITS(i) (((i) >> 12) & 15) /* Rd */
> +#define RM_BITS(i) ((i) & 15) /* Rm
*/
While I could bring the SWP emulation patch in line by just adding a
couple of defines here for RT_BITS and RT2_BITS, unfortunately, the
ARM instruction formats aren't 100% consistent about these offsets:
STREX* have their Rn and Rd fields at the same offsets as above, with
an Rt field at offset 0.
| | | | Rn | Rd | | | Rt |
SWP/SWPB have Rt at offset 12 with an Rt2 at offset 0.
| | | | Rn | Rt | | | Rt2|
STRD likewise has Rt at offset 12, but with an Rm at 0.
| | | | Rn | Rt | | | Rm |
ASR has Rd at offset 12, but with Rm at 8 and Rn at 0.
| | | | | Rd | Rm | | Rn |
MLA has Rd at 16, with Ra at 12, Rm at 8 and Rn at 0.
| | | | Rd | Ra | Rm | | Rn |
So in the long run we would either need to implement an instruction
decoder to extract the offsets of fields, or separate the least common
ones out into separate defines. The former having potential negative
impact on code size and performance.
If you'd like, I could update the SWP emulation patch to set up
separate SWP_RT_BITS and SWP_RT2_BITS in arm-isa.h for now, along with
a couple of other things I would need. At least that would gather the
encoding stuff into one file.
/
Leif
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6)
2010-07-19 20:02 ` Linus Walleij
2010-07-19 21:26 ` Kirill A. Shutemov
2010-07-28 6:06 ` Pavel Machek
@ 2010-08-03 19:42 ` Jamie Lokier
2 siblings, 0 replies; 13+ messages in thread
From: Jamie Lokier @ 2010-08-03 19:42 UTC (permalink / raw)
To: linux-arm-kernel
Linus Walleij wrote:
> 2010/7/19 Kirill A. Shutemov <kirill@shutemov.name>:
>
> > Why do we need this? Why do not just fix broken programs?
>
> People tend to use some third-party precompiled binaries, some of
> them compiled with a compiler using SWP.
>
> A typical example is the CDC Java runtime, JRE.
Also people like to run the same binaries on different ARMs.
E.g. Debian, Ubuntu.
System pthread libraries can have multiple implementations, but
not every user of SWP or LDREX/STREX is in the pthread libraries.
-- Jamie
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2010-08-03 19:42 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-07-19 18:02 [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6) Leif Lindholm
2010-07-19 19:49 ` Kirill A. Shutemov
2010-07-19 20:02 ` Linus Walleij
2010-07-19 21:26 ` Kirill A. Shutemov
2010-07-28 6:06 ` Pavel Machek
2010-07-29 10:13 ` Siarhei Siamashka
2010-07-29 19:28 ` Pavel Machek
2010-07-30 10:04 ` Siarhei Siamashka
2010-07-31 10:52 ` Russell King - ARM Linux
2010-07-31 11:29 ` Russell King - ARM Linux
2010-08-02 18:45 ` Leif Lindholm
2010-08-03 19:42 ` Jamie Lokier
2010-07-20 9:12 ` Catalin Marinas
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).