aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/kernel/swp_emulate.c267
-rw-r--r--arch/arm/mm/Kconfig27
-rw-r--r--arch/arm/mm/proc-v7.S4
4 files changed, 299 insertions, 0 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 5b9b268f4fbb..119a6bb59bde 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_KGDB) += kgdb.o
42obj-$(CONFIG_ARM_UNWIND) += unwind.o 42obj-$(CONFIG_ARM_UNWIND) += unwind.o
43obj-$(CONFIG_HAVE_TCM) += tcm.o 43obj-$(CONFIG_HAVE_TCM) += tcm.o
44obj-$(CONFIG_CRASH_DUMP) += crash_dump.o 44obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
45obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o
45obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o 46obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
46 47
47obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o 48obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c
new file mode 100644
index 000000000000..7a5760922914
--- /dev/null
+++ b/arch/arm/kernel/swp_emulate.c
@@ -0,0 +1,267 @@
1/*
2 * linux/arch/arm/kernel/swp_emulate.c
3 *
4 * Copyright (C) 2009 ARM Limited
5 * __user_* functions adapted from include/asm/uaccess.h
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Implements emulation of the SWP/SWPB instructions using load-exclusive and
12 * store-exclusive for processors that have them disabled (or future ones that
13 * might not implement them).
14 *
15 * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>]
16 * Where: Rt = destination
17 * Rt2 = source
18 * Rn = address
19 */
20
21#include <linux/init.h>
22#include <linux/kernel.h>
23#include <linux/proc_fs.h>
24#include <linux/sched.h>
25#include <linux/syscalls.h>
26#include <linux/perf_event.h>
27
28#include <asm/traps.h>
29#include <asm/uaccess.h>
30
31/*
32 * Error-checking SWP macros implemented using ldrex{b}/strex{b}
33 */
34#define __user_swpX_asm(data, addr, res, temp, B) \
35 __asm__ __volatile__( \
36 " mov %2, %1\n" \
37 "0: ldrex"B" %1, [%3]\n" \
38 "1: strex"B" %0, %2, [%3]\n" \
39 " cmp %0, #0\n" \
40 " movne %0, %4\n" \
41 "2:\n" \
42 " .section .fixup,\"ax\"\n" \
43 " .align 2\n" \
44 "3: mov %0, %5\n" \
45 " b 2b\n" \
46 " .previous\n" \
47 " .section __ex_table,\"a\"\n" \
48 " .align 3\n" \
49 " .long 0b, 3b\n" \
50 " .long 1b, 3b\n" \
51 " .previous" \
52 : "=&r" (res), "+r" (data), "=&r" (temp) \
53 : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \
54 : "cc", "memory")
55
56#define __user_swp_asm(data, addr, res, temp) \
57 __user_swpX_asm(data, addr, res, temp, "")
58#define __user_swpb_asm(data, addr, res, temp) \
59 __user_swpX_asm(data, addr, res, temp, "b")
60
61/*
62 * Macros/defines for extracting register numbers from instruction.
63 */
64#define EXTRACT_REG_NUM(instruction, offset) \
65 (((instruction) & (0xf << (offset))) >> (offset))
66#define RN_OFFSET 16
67#define RT_OFFSET 12
68#define RT2_OFFSET 0
69/*
70 * Bit 22 of the instruction encoding distinguishes between
71 * the SWP and SWPB variants (bit set means SWPB).
72 */
73#define TYPE_SWPB (1 << 22)
74
75static unsigned long swpcounter;
76static unsigned long swpbcounter;
77static unsigned long abtcounter;
78static pid_t previous_pid;
79
80#ifdef CONFIG_PROC_FS
81static int proc_read_status(char *page, char **start, off_t off, int count,
82 int *eof, void *data)
83{
84 char *p = page;
85 int len;
86
87 p += sprintf(p, "Emulated SWP:\t\t%lu\n", swpcounter);
88 p += sprintf(p, "Emulated SWPB:\t\t%lu\n", swpbcounter);
89 p += sprintf(p, "Aborted SWP{B}:\t\t%lu\n", abtcounter);
90 if (previous_pid != 0)
91 p += sprintf(p, "Last process:\t\t%d\n", previous_pid);
92
93 len = (p - page) - off;
94 if (len < 0)
95 len = 0;
96
97 *eof = (len <= count) ? 1 : 0;
98 *start = page + off;
99
100 return len;
101}
102#endif
103
104/*
105 * Set up process info to signal segmentation fault - called on access error.
106 */
107static void set_segfault(struct pt_regs *regs, unsigned long addr)
108{
109 siginfo_t info;
110
111 if (find_vma(current->mm, addr) == NULL)
112 info.si_code = SEGV_MAPERR;
113 else
114 info.si_code = SEGV_ACCERR;
115
116 info.si_signo = SIGSEGV;
117 info.si_errno = 0;
118 info.si_addr = (void *) instruction_pointer(regs);
119
120 pr_debug("SWP{B} emulation: access caused memory abort!\n");
121 arm_notify_die("Illegal memory access", regs, &info, 0, 0);
122
123 abtcounter++;
124}
125
126static int emulate_swpX(unsigned int address, unsigned int *data,
127 unsigned int type)
128{
129 unsigned int res = 0;
130
131 if ((type != TYPE_SWPB) && (address & 0x3)) {
132 /* SWP to unaligned address not permitted */
133 pr_debug("SWP instruction on unaligned pointer!\n");
134 return -EFAULT;
135 }
136
137 while (1) {
138 unsigned long temp;
139
140 /*
141 * Barrier required between accessing protected resource and
142 * releasing a lock for it. Legacy code might not have done
143 * this, and we cannot determine that this is not the case
144 * being emulated, so insert always.
145 */
146 smp_mb();
147
148 if (type == TYPE_SWPB)
149 __user_swpb_asm(*data, address, res, temp);
150 else
151 __user_swp_asm(*data, address, res, temp);
152
153 if (likely(res != -EAGAIN) || signal_pending(current))
154 break;
155
156 cond_resched();
157 }
158
159 if (res == 0) {
160 /*
161 * Barrier also required between aquiring a lock for a
162 * protected resource and accessing the resource. Inserted for
163 * same reason as above.
164 */
165 smp_mb();
166
167 if (type == TYPE_SWPB)
168 swpbcounter++;
169 else
170 swpcounter++;
171 }
172
173 return res;
174}
175
176/*
177 * swp_handler logs the id of calling process, dissects the instruction, sanity
178 * checks the memory location, calls emulate_swpX for the actual operation and
179 * deals with fixup/error handling before returning
180 */
181static int swp_handler(struct pt_regs *regs, unsigned int instr)
182{
183 unsigned int address, destreg, data, type;
184 unsigned int res = 0;
185
186 perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, regs->ARM_pc);
187
188 if (current->pid != previous_pid) {
189 pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n",
190 current->comm, (unsigned long)current->pid);
191 previous_pid = current->pid;
192 }
193
194 address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)];
195 data = regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)];
196 destreg = EXTRACT_REG_NUM(instr, RT_OFFSET);
197
198 type = instr & TYPE_SWPB;
199
200 pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n",
201 EXTRACT_REG_NUM(instr, RN_OFFSET), address,
202 destreg, EXTRACT_REG_NUM(instr, RT2_OFFSET), data);
203
204 /* Check access in reasonable access range for both SWP and SWPB */
205 if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) {
206 pr_debug("SWP{B} emulation: access to %p not allowed!\n",
207 (void *)address);
208 res = -EFAULT;
209 } else {
210 res = emulate_swpX(address, &data, type);
211 }
212
213 if (res == 0) {
214 /*
215 * On successful emulation, revert the adjustment to the PC
216 * made in kernel/traps.c in order to resume execution at the
217 * instruction following the SWP{B}.
218 */
219 regs->ARM_pc += 4;
220 regs->uregs[destreg] = data;
221 } else if (res == -EFAULT) {
222 /*
223 * Memory errors do not mean emulation failed.
224 * Set up signal info to return SEGV, then return OK
225 */
226 set_segfault(regs, address);
227 }
228
229 return 0;
230}
231
232/*
233 * Only emulate SWP/SWPB executed in ARM state/User mode.
234 * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE.
235 */
236static struct undef_hook swp_hook = {
237 .instr_mask = 0x0fb00ff0,
238 .instr_val = 0x01000090,
239 .cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT,
240 .cpsr_val = USR_MODE,
241 .fn = swp_handler
242};
243
244/*
245 * Register handler and create status file in /proc/cpu
246 * Invoked as late_initcall, since not needed before init spawned.
247 */
248static int __init swp_emulation_init(void)
249{
250#ifdef CONFIG_PROC_FS
251 struct proc_dir_entry *res;
252
253 res = create_proc_entry("cpu/swp_emulation", S_IRUGO, NULL);
254
255 if (!res)
256 return -ENOMEM;
257
258 res->read_proc = proc_read_status;
259#endif /* CONFIG_PROC_FS */
260
261 printk(KERN_NOTICE "Registering SWP/SWPB emulation handler\n");
262 register_undef_hook(&swp_hook);
263
264 return 0;
265}
266
267late_initcall(swp_emulation_init);
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 6d05f79a8cd2..8493ed04797a 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -636,6 +636,33 @@ config ARM_THUMBEE
636 Say Y here if you have a CPU with the ThumbEE extension and code to 636 Say Y here if you have a CPU with the ThumbEE extension and code to
637 make use of it. Say N for code that can run on CPUs without ThumbEE. 637 make use of it. Say N for code that can run on CPUs without ThumbEE.
638 638
639config SWP_EMULATE
640 bool "Emulate SWP/SWPB instructions"
641 depends on CPU_V7
642 select HAVE_PROC_CPU if PROC_FS
643 default y if SMP
644 help
645 ARMv6 architecture deprecates use of the SWP/SWPB instructions.
646 ARMv7 multiprocessing extensions introduce the ability to disable
647 these instructions, triggering an undefined instruction exception
648 when executed. Say Y here to enable software emulation of these
649 instructions for userspace (not kernel) using LDREX/STREX.
650 Also creates /proc/cpu/swp_emulation for statistics.
651
652 In some older versions of glibc [<=2.8] SWP is used during futex
653 trylock() operations with the assumption that the code will not
654 be preempted. This invalid assumption may be more likely to fail
655 with SWP emulation enabled, leading to deadlock of the user
656 application.
657
658 NOTE: when accessing uncached shared regions, LDREX/STREX rely
659 on an external transaction monitoring block called a global
660 monitor to maintain update atomicity. If your system does not
661 implement a global monitor, this option can cause programs that
662 perform SWP operations to uncached memory to deadlock.
663
664 If unsure, say Y.
665
639config CPU_BIG_ENDIAN 666config CPU_BIG_ENDIAN
640 bool "Build big-endian kernel" 667 bool "Build big-endian kernel"
641 depends on ARCH_SUPPORTS_BIG_ENDIAN 668 depends on ARCH_SUPPORTS_BIG_ENDIAN
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index cfc11afab1fb..2b5b20baf80d 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -314,6 +314,10 @@ __v7_setup:
314#ifdef CONFIG_CPU_ENDIAN_BE8 314#ifdef CONFIG_CPU_ENDIAN_BE8
315 orr r6, r6, #1 << 25 @ big-endian page tables 315 orr r6, r6, #1 << 25 @ big-endian page tables
316#endif 316#endif
317#ifdef CONFIG_SWP_EMULATE
318 orr r5, r5, #(1 << 10) @ set SW bit in "clear"
319 bic r6, r6, #(1 << 10) @ clear it in "mmuset"
320#endif
317 mrc p15, 0, r0, c1, c0, 0 @ read control register 321 mrc p15, 0, r0, c1, c0, 0 @ read control register
318 bic r0, r0, r5 @ clear bits them 322 bic r0, r0, r5 @ clear bits them
319 orr r0, r0, r6 @ set them 323 orr r0, r0, r6 @ set them