aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVineet Gupta <vgupta@synopsys.com>2013-01-23 06:00:36 -0500
committerVineet Gupta <vgupta@synopsys.com>2013-02-15 12:46:06 -0500
commit2e651ea1596b0ee25af4fcdc4cd13cbb33ffc254 (patch)
tree51210f1cd80327a00e2fd4b0d20849b6329925d6
parentbf14e3b979a01cd7298d631736f965fe83c6e2bc (diff)
ARC: Unaligned access emulation
ARC700 doesn't natively support unaligned access, but can be emulated -Unaligned Access Exception -Disassembly at the Fault address to find the exact insn (long/short) Also per Arnd's comment, we runtime control it using 2 sysctl knobs: * SYSCTL_ARCH_UNALIGN_ALLOW: Runtime enable/disble * SYSCTL_ARCH_UNALIGN_NO_WARN: Warn on each emulation attempt Originally contributed by Tim Yao <tim.yao@amlogic.com> Signed-off-by: Vineet Gupta <vgupta@synopsys.com> Cc: Tim Yao <tim.yao@amlogic.com> Acked-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r--arch/arc/Kconfig11
-rw-r--r--arch/arc/include/asm/Kbuild1
-rw-r--r--arch/arc/include/asm/ptrace.h3
-rw-r--r--arch/arc/include/asm/unaligned.h29
-rw-r--r--arch/arc/kernel/Makefile1
-rw-r--r--arch/arc/kernel/disasm.c2
-rw-r--r--arch/arc/kernel/entry.S13
-rw-r--r--arch/arc/kernel/traps.c26
-rw-r--r--arch/arc/kernel/unaligned.c245
9 files changed, 329 insertions, 2 deletions
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index cde8d3fcec94..f8042835e746 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -336,6 +336,17 @@ config ARC_CURR_IN_REG
336 This reserved Register R25 to point to Current Task in 336 This reserved Register R25 to point to Current Task in
337 kernel mode. This saves memory access for each such access 337 kernel mode. This saves memory access for each such access
338 338
339
340config ARC_MISALIGN_ACCESS
341 bool "Emulate unaligned memory access (userspace only)"
342 default N
343 select SYSCTL_ARCH_UNALIGN_NO_WARN
344 select SYSCTL_ARCH_UNALIGN_ALLOW
345 help
346 This enables misaligned 16 & 32 bit memory access from user space.
347 Use ONLY-IF-ABS-NECESSARY as it will be very slow and also can hide
348 potential bugs in code
349
339config ARC_STACK_NONEXEC 350config ARC_STACK_NONEXEC
340 bool "Make stack non-executable" 351 bool "Make stack non-executable"
341 default n 352 default n
diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild
index 78e982dad537..b24089c974a9 100644
--- a/arch/arc/include/asm/Kbuild
+++ b/arch/arc/include/asm/Kbuild
@@ -52,7 +52,6 @@ generic-y += topology.h
52generic-y += trace_clock.h 52generic-y += trace_clock.h
53generic-y += types.h 53generic-y += types.h
54generic-y += ucontext.h 54generic-y += ucontext.h
55generic-y += unaligned.h
56generic-y += user.h 55generic-y += user.h
57generic-y += vga.h 56generic-y += vga.h
58generic-y += xor.h 57generic-y += xor.h
diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h
index 063ed0040ef7..df5b95213776 100644
--- a/arch/arc/include/asm/ptrace.h
+++ b/arch/arc/include/asm/ptrace.h
@@ -97,6 +97,9 @@ struct callee_regs {
97 sp; \ 97 sp; \
98}) 98})
99 99
100/* return 1 if PC in delay slot */
101#define delay_mode(regs) ((regs->status32 & STATUS_DE_MASK) == STATUS_DE_MASK)
102
100#define in_syscall(regs) (regs->event & orig_r8_IS_SCALL) 103#define in_syscall(regs) (regs->event & orig_r8_IS_SCALL)
101#define in_brkpt_trap(regs) (regs->event & orig_r8_IS_BRKPT) 104#define in_brkpt_trap(regs) (regs->event & orig_r8_IS_BRKPT)
102 105
diff --git a/arch/arc/include/asm/unaligned.h b/arch/arc/include/asm/unaligned.h
new file mode 100644
index 000000000000..5dbe63f17b66
--- /dev/null
+++ b/arch/arc/include/asm/unaligned.h
@@ -0,0 +1,29 @@
1/*
2 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#ifndef _ASM_ARC_UNALIGNED_H
10#define _ASM_ARC_UNALIGNED_H
11
12/* ARC700 can't handle unaligned Data accesses. */
13
14#include <asm-generic/unaligned.h>
15#include <asm/ptrace.h>
16
17#ifdef CONFIG_ARC_MISALIGN_ACCESS
18int misaligned_fixup(unsigned long address, struct pt_regs *regs,
19 unsigned long cause, struct callee_regs *cregs);
20#else
21static inline int
22misaligned_fixup(unsigned long address, struct pt_regs *regs,
23 unsigned long cause, struct callee_regs *cregs)
24{
25 return 0;
26}
27#endif
28
29#endif /* _ASM_ARC_UNALIGNED_H */
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index d331bd7047a1..507a33d4a255 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_MODULES) += arcksyms.o module.o
16obj-$(CONFIG_SMP) += smp.o 16obj-$(CONFIG_SMP) += smp.o
17obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o 17obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o
18obj-$(CONFIG_KPROBES) += kprobes.o 18obj-$(CONFIG_KPROBES) += kprobes.o
19obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o
19 20
20obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o 21obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o
21CFLAGS_fpu.o += -mdpfp 22CFLAGS_fpu.o += -mdpfp
diff --git a/arch/arc/kernel/disasm.c b/arch/arc/kernel/disasm.c
index 51bad8ff373b..2f390289a792 100644
--- a/arch/arc/kernel/disasm.c
+++ b/arch/arc/kernel/disasm.c
@@ -15,7 +15,7 @@
15#include <asm/disasm.h> 15#include <asm/disasm.h>
16#include <asm/uaccess.h> 16#include <asm/uaccess.h>
17 17
18#if defined(CONFIG_KGDB) || defined(CONFIG_MISALIGN_ACCESS) || \ 18#if defined(CONFIG_KGDB) || defined(CONFIG_ARC_MISALIGN_ACCESS) || \
19 defined(CONFIG_KPROBES) 19 defined(CONFIG_KPROBES)
20 20
21/* disasm_instr: Analyses instruction at addr, stores 21/* disasm_instr: Analyses instruction at addr, stores
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index 021cfa46a1dc..f8efade15368 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -7,6 +7,9 @@
7 * it under the terms of the GNU General Public License version 2 as 7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation. 8 * published by the Free Software Foundation.
9 * 9 *
10 * vineetg: May 2011
11 * -Userspace unaligned access emulation
12 *
10 * vineetg: Feb 2011 (ptrace low level code fixes) 13 * vineetg: Feb 2011 (ptrace low level code fixes)
11 * -traced syscall return code (r0) was not saved into pt_regs for restoring 14 * -traced syscall return code (r0) was not saved into pt_regs for restoring
12 * into user reg-file when traded task rets to user space. 15 * into user reg-file when traded task rets to user space.
@@ -387,7 +390,17 @@ ARC_ENTRY EV_TLBProtV
387 mov r1, r4 ; faulting address 390 mov r1, r4 ; faulting address
388 mov r2, sp ; pt_regs 391 mov r2, sp ; pt_regs
389 392
393#ifdef CONFIG_ARC_MISALIGN_ACCESS
394 SAVE_CALLEE_SAVED_USER
395 mov r3, sp ; callee_regs
396#endif
397
390 bl do_misaligned_access 398 bl do_misaligned_access
399
400#ifdef CONFIG_ARC_MISALIGN_ACCESS
401 DISCARD_CALLEE_SAVED_USER
402#endif
403
391 b ret_from_exception 404 b ret_from_exception
392 405
393ARC_EXIT EV_TLBProtV 406ARC_EXIT EV_TLBProtV
diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c
index c6396b48fcd2..ec802c52a1ca 100644
--- a/arch/arc/kernel/traps.c
+++ b/arch/arc/kernel/traps.c
@@ -7,6 +7,9 @@
7 * it under the terms of the GNU General Public License version 2 as 7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation. 8 * published by the Free Software Foundation.
9 * 9 *
10 * vineetg: May 2011
11 * -user-space unaligned access emulation
12 *
10 * Rahul Trivedi: Codito Technologies 2004 13 * Rahul Trivedi: Codito Technologies 2004
11 */ 14 */
12 15
@@ -16,6 +19,7 @@
16#include <asm/ptrace.h> 19#include <asm/ptrace.h>
17#include <asm/setup.h> 20#include <asm/setup.h>
18#include <asm/kprobes.h> 21#include <asm/kprobes.h>
22#include <asm/unaligned.h>
19 23
20void __init trap_init(void) 24void __init trap_init(void)
21{ 25{
@@ -79,7 +83,29 @@ DO_ERROR_INFO(SIGILL, "Illegal Insn (or Seq)", insterror_is_error, ILL_ILLOPC)
79DO_ERROR_INFO(SIGBUS, "Invalid Mem Access", do_memory_error, BUS_ADRERR) 83DO_ERROR_INFO(SIGBUS, "Invalid Mem Access", do_memory_error, BUS_ADRERR)
80DO_ERROR_INFO(SIGTRAP, "Breakpoint Set", trap_is_brkpt, TRAP_BRKPT) 84DO_ERROR_INFO(SIGTRAP, "Breakpoint Set", trap_is_brkpt, TRAP_BRKPT)
81 85
86#ifdef CONFIG_ARC_MISALIGN_ACCESS
87/*
88 * Entry Point for Misaligned Data access Exception, for emulating in software
89 */
90int do_misaligned_access(unsigned long cause, unsigned long address,
91 struct pt_regs *regs, struct callee_regs *cregs)
92{
93 if (misaligned_fixup(address, regs, cause, cregs) != 0) {
94 siginfo_t info;
95
96 info.si_signo = SIGBUS;
97 info.si_errno = 0;
98 info.si_code = BUS_ADRALN;
99 info.si_addr = (void __user *)address;
100 return handle_exception(cause, "Misaligned Access", regs,
101 &info);
102 }
103 return 0;
104}
105
106#else
82DO_ERROR_INFO(SIGSEGV, "Misaligned Access", do_misaligned_access, SEGV_ACCERR) 107DO_ERROR_INFO(SIGSEGV, "Misaligned Access", do_misaligned_access, SEGV_ACCERR)
108#endif
83 109
84/* 110/*
85 * Entry point for miscll errors such as Nested Exceptions 111 * Entry point for miscll errors such as Nested Exceptions
diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c
new file mode 100644
index 000000000000..4cd81633febd
--- /dev/null
+++ b/arch/arc/kernel/unaligned.c
@@ -0,0 +1,245 @@
1/*
2 * Copyright (C) 2011-2012 Synopsys (www.synopsys.com)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * vineetg : May 2011
9 * -Adapted (from .26 to .35)
10 * -original contribution by Tim.yao@amlogic.com
11 *
12 */
13
14#include <linux/types.h>
15#include <linux/ptrace.h>
16#include <linux/uaccess.h>
17#include <asm/disasm.h>
18
19#define __get8_unaligned_check(val, addr, err) \
20 __asm__( \
21 "1: ldb.ab %1, [%2, 1]\n" \
22 "2:\n" \
23 " .section .fixup,\"ax\"\n" \
24 " .align 4\n" \
25 "3: mov %0, 1\n" \
26 " b 2b\n" \
27 " .previous\n" \
28 " .section __ex_table,\"a\"\n" \
29 " .align 4\n" \
30 " .long 1b, 3b\n" \
31 " .previous\n" \
32 : "=r" (err), "=&r" (val), "=r" (addr) \
33 : "0" (err), "2" (addr))
34
35#define get16_unaligned_check(val, addr) \
36 do { \
37 unsigned int err = 0, v, a = addr; \
38 __get8_unaligned_check(v, a, err); \
39 val = v ; \
40 __get8_unaligned_check(v, a, err); \
41 val |= v << 8; \
42 if (err) \
43 goto fault; \
44 } while (0)
45
46#define get32_unaligned_check(val, addr) \
47 do { \
48 unsigned int err = 0, v, a = addr; \
49 __get8_unaligned_check(v, a, err); \
50 val = v << 0; \
51 __get8_unaligned_check(v, a, err); \
52 val |= v << 8; \
53 __get8_unaligned_check(v, a, err); \
54 val |= v << 16; \
55 __get8_unaligned_check(v, a, err); \
56 val |= v << 24; \
57 if (err) \
58 goto fault; \
59 } while (0)
60
61#define put16_unaligned_check(val, addr) \
62 do { \
63 unsigned int err = 0, v = val, a = addr;\
64 \
65 __asm__( \
66 "1: stb.ab %1, [%2, 1]\n" \
67 " lsr %1, %1, 8\n" \
68 "2: stb %1, [%2]\n" \
69 "3:\n" \
70 " .section .fixup,\"ax\"\n" \
71 " .align 4\n" \
72 "4: mov %0, 1\n" \
73 " b 3b\n" \
74 " .previous\n" \
75 " .section __ex_table,\"a\"\n" \
76 " .align 4\n" \
77 " .long 1b, 4b\n" \
78 " .long 2b, 4b\n" \
79 " .previous\n" \
80 : "=r" (err), "=&r" (v), "=&r" (a) \
81 : "0" (err), "1" (v), "2" (a)); \
82 \
83 if (err) \
84 goto fault; \
85 } while (0)
86
87#define put32_unaligned_check(val, addr) \
88 do { \
89 unsigned int err = 0, v = val, a = addr;\
90 __asm__( \
91 \
92 "1: stb.ab %1, [%2, 1]\n" \
93 " lsr %1, %1, 8\n" \
94 "2: stb.ab %1, [%2, 1]\n" \
95 " lsr %1, %1, 8\n" \
96 "3: stb.ab %1, [%2, 1]\n" \
97 " lsr %1, %1, 8\n" \
98 "4: stb %1, [%2]\n" \
99 "5:\n" \
100 " .section .fixup,\"ax\"\n" \
101 " .align 4\n" \
102 "6: mov %0, 1\n" \
103 " b 5b\n" \
104 " .previous\n" \
105 " .section __ex_table,\"a\"\n" \
106 " .align 4\n" \
107 " .long 1b, 6b\n" \
108 " .long 2b, 6b\n" \
109 " .long 3b, 6b\n" \
110 " .long 4b, 6b\n" \
111 " .previous\n" \
112 : "=r" (err), "=&r" (v), "=&r" (a) \
113 : "0" (err), "1" (v), "2" (a)); \
114 \
115 if (err) \
116 goto fault; \
117 } while (0)
118
119/* sysctl hooks */
120int unaligned_enabled __read_mostly = 1; /* Enabled by default */
121int no_unaligned_warning __read_mostly = 1; /* Only 1 warning by default */
122
123static void fixup_load(struct disasm_state *state, struct pt_regs *regs,
124 struct callee_regs *cregs)
125{
126 int val;
127
128 /* register write back */
129 if ((state->aa == 1) || (state->aa == 2)) {
130 set_reg(state->wb_reg, state->src1 + state->src2, regs, cregs);
131
132 if (state->aa == 2)
133 state->src2 = 0;
134 }
135
136 if (state->zz == 0) {
137 get32_unaligned_check(val, state->src1 + state->src2);
138 } else {
139 get16_unaligned_check(val, state->src1 + state->src2);
140
141 if (state->x)
142 val = (val << 16) >> 16;
143 }
144
145 if (state->pref == 0)
146 set_reg(state->dest, val, regs, cregs);
147
148 return;
149
150fault: state->fault = 1;
151}
152
153static void fixup_store(struct disasm_state *state, struct pt_regs *regs,
154 struct callee_regs *cregs)
155{
156 /* register write back */
157 if ((state->aa == 1) || (state->aa == 2)) {
158 set_reg(state->wb_reg, state->src2 + state->src3, regs, cregs);
159
160 if (state->aa == 3)
161 state->src3 = 0;
162 } else if (state->aa == 3) {
163 if (state->zz == 2) {
164 set_reg(state->wb_reg, state->src2 + (state->src3 << 1),
165 regs, cregs);
166 } else if (!state->zz) {
167 set_reg(state->wb_reg, state->src2 + (state->src3 << 2),
168 regs, cregs);
169 } else {
170 goto fault;
171 }
172 }
173
174 /* write fix-up */
175 if (!state->zz)
176 put32_unaligned_check(state->src1, state->src2 + state->src3);
177 else
178 put16_unaligned_check(state->src1, state->src2 + state->src3);
179
180 return;
181
182fault: state->fault = 1;
183}
184
185/*
186 * Handle an unaligned access
187 * Returns 0 if successfully handled, 1 if some error happened
188 */
189int misaligned_fixup(unsigned long address, struct pt_regs *regs,
190 unsigned long cause, struct callee_regs *cregs)
191{
192 struct disasm_state state;
193 char buf[TASK_COMM_LEN];
194
195 /* handle user mode only and only if enabled by sysadmin */
196 if (!user_mode(regs) || !unaligned_enabled)
197 return 1;
198
199 if (no_unaligned_warning) {
200 pr_warn_once("%s(%d) made unaligned access which was emulated"
201 " by kernel assist\n. This can degrade application"
202 " performance significantly\n. To enable further"
203 " logging of such instances, please \n"
204 " echo 0 > /proc/sys/kernel/ignore-unaligned-usertrap\n",
205 get_task_comm(buf, current), task_pid_nr(current));
206 } else {
207 /* Add rate limiting if it gets down to it */
208 pr_warn("%s(%d): unaligned access to/from 0x%lx by PC: 0x%lx\n",
209 get_task_comm(buf, current), task_pid_nr(current),
210 address, regs->ret);
211
212 }
213
214 disasm_instr(regs->ret, &state, 1, regs, cregs);
215
216 if (state.fault)
217 goto fault;
218
219 /* ldb/stb should not have unaligned exception */
220 if ((state.zz == 1) || (state.di))
221 goto fault;
222
223 if (!state.write)
224 fixup_load(&state, regs, cregs);
225 else
226 fixup_store(&state, regs, cregs);
227
228 if (state.fault)
229 goto fault;
230
231 if (delay_mode(regs)) {
232 regs->ret = regs->bta;
233 regs->status32 &= ~STATUS_DE_MASK;
234 } else {
235 regs->ret += state.instr_len;
236 }
237
238 return 0;
239
240fault:
241 pr_err("Alignment trap: fault in fix-up %08lx at [<%08lx>]\n",
242 state.words[0], address);
243
244 return 1;
245}