diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2013-01-23 06:00:36 -0500 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2013-02-15 12:46:06 -0500 |
commit | 2e651ea1596b0ee25af4fcdc4cd13cbb33ffc254 (patch) | |
tree | 51210f1cd80327a00e2fd4b0d20849b6329925d6 /arch/arc/kernel | |
parent | bf14e3b979a01cd7298d631736f965fe83c6e2bc (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>
Diffstat (limited to 'arch/arc/kernel')
-rw-r--r-- | arch/arc/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arc/kernel/disasm.c | 2 | ||||
-rw-r--r-- | arch/arc/kernel/entry.S | 13 | ||||
-rw-r--r-- | arch/arc/kernel/traps.c | 26 | ||||
-rw-r--r-- | arch/arc/kernel/unaligned.c | 245 |
5 files changed, 286 insertions, 1 deletions
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 | |||
16 | obj-$(CONFIG_SMP) += smp.o | 16 | obj-$(CONFIG_SMP) += smp.o |
17 | obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o | 17 | obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o |
18 | obj-$(CONFIG_KPROBES) += kprobes.o | 18 | obj-$(CONFIG_KPROBES) += kprobes.o |
19 | obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o | ||
19 | 20 | ||
20 | obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o | 21 | obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o |
21 | CFLAGS_fpu.o += -mdpfp | 22 | CFLAGS_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 | ||
393 | ARC_EXIT EV_TLBProtV | 406 | ARC_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 | ||
20 | void __init trap_init(void) | 24 | void __init trap_init(void) |
21 | { | 25 | { |
@@ -79,7 +83,29 @@ DO_ERROR_INFO(SIGILL, "Illegal Insn (or Seq)", insterror_is_error, ILL_ILLOPC) | |||
79 | DO_ERROR_INFO(SIGBUS, "Invalid Mem Access", do_memory_error, BUS_ADRERR) | 83 | DO_ERROR_INFO(SIGBUS, "Invalid Mem Access", do_memory_error, BUS_ADRERR) |
80 | DO_ERROR_INFO(SIGTRAP, "Breakpoint Set", trap_is_brkpt, TRAP_BRKPT) | 84 | DO_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 | */ | ||
90 | int 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 | ||
82 | DO_ERROR_INFO(SIGSEGV, "Misaligned Access", do_misaligned_access, SEGV_ACCERR) | 107 | DO_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 */ | ||
120 | int unaligned_enabled __read_mostly = 1; /* Enabled by default */ | ||
121 | int no_unaligned_warning __read_mostly = 1; /* Only 1 warning by default */ | ||
122 | |||
123 | static 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 | |||
150 | fault: state->fault = 1; | ||
151 | } | ||
152 | |||
153 | static 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 | |||
182 | fault: state->fault = 1; | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * Handle an unaligned access | ||
187 | * Returns 0 if successfully handled, 1 if some error happened | ||
188 | */ | ||
189 | int 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 | |||
240 | fault: | ||
241 | pr_err("Alignment trap: fault in fix-up %08lx at [<%08lx>]\n", | ||
242 | state.words[0], address); | ||
243 | |||
244 | return 1; | ||
245 | } | ||