diff options
Diffstat (limited to 'arch/frv/kernel/traps.c')
-rw-r--r-- | arch/frv/kernel/traps.c | 240 |
1 files changed, 234 insertions, 6 deletions
diff --git a/arch/frv/kernel/traps.c b/arch/frv/kernel/traps.c index 2e6098c8557..a40df80b2eb 100644 --- a/arch/frv/kernel/traps.c +++ b/arch/frv/kernel/traps.c | |||
@@ -49,7 +49,7 @@ asmlinkage void insn_access_error(unsigned long esfr1, unsigned long epcr0, unsi | |||
49 | info.si_signo = SIGSEGV; | 49 | info.si_signo = SIGSEGV; |
50 | info.si_code = SEGV_ACCERR; | 50 | info.si_code = SEGV_ACCERR; |
51 | info.si_errno = 0; | 51 | info.si_errno = 0; |
52 | info.si_addr = (void *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc); | 52 | info.si_addr = (void __user *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc); |
53 | 53 | ||
54 | force_sig_info(info.si_signo, &info, current); | 54 | force_sig_info(info.si_signo, &info, current); |
55 | } /* end insn_access_error() */ | 55 | } /* end insn_access_error() */ |
@@ -73,7 +73,7 @@ asmlinkage void illegal_instruction(unsigned long esfr1, unsigned long epcr0, un | |||
73 | epcr0, esr0, esfr1); | 73 | epcr0, esr0, esfr1); |
74 | 74 | ||
75 | info.si_errno = 0; | 75 | info.si_errno = 0; |
76 | info.si_addr = (void *) ((epcr0 & EPCR0_PC) ? (epcr0 & EPCR0_PC) : __frame->pc); | 76 | info.si_addr = (void __user *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc); |
77 | 77 | ||
78 | switch (__frame->tbr & TBR_TT) { | 78 | switch (__frame->tbr & TBR_TT) { |
79 | case TBR_TT_ILLEGAL_INSTR: | 79 | case TBR_TT_ILLEGAL_INSTR: |
@@ -102,6 +102,234 @@ asmlinkage void illegal_instruction(unsigned long esfr1, unsigned long epcr0, un | |||
102 | 102 | ||
103 | /*****************************************************************************/ | 103 | /*****************************************************************************/ |
104 | /* | 104 | /* |
105 | * handle atomic operations with errors | ||
106 | * - arguments in gr8, gr9, gr10 | ||
107 | * - original memory value placed in gr5 | ||
108 | * - replacement memory value placed in gr9 | ||
109 | */ | ||
110 | asmlinkage void atomic_operation(unsigned long esfr1, unsigned long epcr0, | ||
111 | unsigned long esr0) | ||
112 | { | ||
113 | static DEFINE_SPINLOCK(atomic_op_lock); | ||
114 | unsigned long x, y, z; | ||
115 | unsigned long __user *p; | ||
116 | mm_segment_t oldfs; | ||
117 | siginfo_t info; | ||
118 | int ret; | ||
119 | |||
120 | y = 0; | ||
121 | z = 0; | ||
122 | |||
123 | oldfs = get_fs(); | ||
124 | if (!user_mode(__frame)) | ||
125 | set_fs(KERNEL_DS); | ||
126 | |||
127 | switch (__frame->tbr & TBR_TT) { | ||
128 | /* TIRA gr0,#120 | ||
129 | * u32 __atomic_user_cmpxchg32(u32 *ptr, u32 test, u32 new) | ||
130 | */ | ||
131 | case TBR_TT_ATOMIC_CMPXCHG32: | ||
132 | p = (unsigned long __user *) __frame->gr8; | ||
133 | x = __frame->gr9; | ||
134 | y = __frame->gr10; | ||
135 | |||
136 | for (;;) { | ||
137 | ret = get_user(z, p); | ||
138 | if (ret < 0) | ||
139 | goto error; | ||
140 | |||
141 | if (z != x) | ||
142 | goto done; | ||
143 | |||
144 | spin_lock_irq(&atomic_op_lock); | ||
145 | |||
146 | if (__get_user(z, p) == 0) { | ||
147 | if (z != x) | ||
148 | goto done2; | ||
149 | |||
150 | if (__put_user(y, p) == 0) | ||
151 | goto done2; | ||
152 | goto error2; | ||
153 | } | ||
154 | |||
155 | spin_unlock_irq(&atomic_op_lock); | ||
156 | } | ||
157 | |||
158 | /* TIRA gr0,#121 | ||
159 | * u32 __atomic_kernel_xchg32(void *v, u32 new) | ||
160 | */ | ||
161 | case TBR_TT_ATOMIC_XCHG32: | ||
162 | p = (unsigned long __user *) __frame->gr8; | ||
163 | y = __frame->gr9; | ||
164 | |||
165 | for (;;) { | ||
166 | ret = get_user(z, p); | ||
167 | if (ret < 0) | ||
168 | goto error; | ||
169 | |||
170 | spin_lock_irq(&atomic_op_lock); | ||
171 | |||
172 | if (__get_user(z, p) == 0) { | ||
173 | if (__put_user(y, p) == 0) | ||
174 | goto done2; | ||
175 | goto error2; | ||
176 | } | ||
177 | |||
178 | spin_unlock_irq(&atomic_op_lock); | ||
179 | } | ||
180 | |||
181 | /* TIRA gr0,#122 | ||
182 | * ulong __atomic_kernel_XOR_return(ulong i, ulong *v) | ||
183 | */ | ||
184 | case TBR_TT_ATOMIC_XOR: | ||
185 | p = (unsigned long __user *) __frame->gr8; | ||
186 | x = __frame->gr9; | ||
187 | |||
188 | for (;;) { | ||
189 | ret = get_user(z, p); | ||
190 | if (ret < 0) | ||
191 | goto error; | ||
192 | |||
193 | spin_lock_irq(&atomic_op_lock); | ||
194 | |||
195 | if (__get_user(z, p) == 0) { | ||
196 | y = x ^ z; | ||
197 | if (__put_user(y, p) == 0) | ||
198 | goto done2; | ||
199 | goto error2; | ||
200 | } | ||
201 | |||
202 | spin_unlock_irq(&atomic_op_lock); | ||
203 | } | ||
204 | |||
205 | /* TIRA gr0,#123 | ||
206 | * ulong __atomic_kernel_OR_return(ulong i, ulong *v) | ||
207 | */ | ||
208 | case TBR_TT_ATOMIC_OR: | ||
209 | p = (unsigned long __user *) __frame->gr8; | ||
210 | x = __frame->gr9; | ||
211 | |||
212 | for (;;) { | ||
213 | ret = get_user(z, p); | ||
214 | if (ret < 0) | ||
215 | goto error; | ||
216 | |||
217 | spin_lock_irq(&atomic_op_lock); | ||
218 | |||
219 | if (__get_user(z, p) == 0) { | ||
220 | y = x ^ z; | ||
221 | if (__put_user(y, p) == 0) | ||
222 | goto done2; | ||
223 | goto error2; | ||
224 | } | ||
225 | |||
226 | spin_unlock_irq(&atomic_op_lock); | ||
227 | } | ||
228 | |||
229 | /* TIRA gr0,#124 | ||
230 | * ulong __atomic_kernel_AND_return(ulong i, ulong *v) | ||
231 | */ | ||
232 | case TBR_TT_ATOMIC_AND: | ||
233 | p = (unsigned long __user *) __frame->gr8; | ||
234 | x = __frame->gr9; | ||
235 | |||
236 | for (;;) { | ||
237 | ret = get_user(z, p); | ||
238 | if (ret < 0) | ||
239 | goto error; | ||
240 | |||
241 | spin_lock_irq(&atomic_op_lock); | ||
242 | |||
243 | if (__get_user(z, p) == 0) { | ||
244 | y = x & z; | ||
245 | if (__put_user(y, p) == 0) | ||
246 | goto done2; | ||
247 | goto error2; | ||
248 | } | ||
249 | |||
250 | spin_unlock_irq(&atomic_op_lock); | ||
251 | } | ||
252 | |||
253 | /* TIRA gr0,#125 | ||
254 | * int __atomic_user_sub_return(atomic_t *v, int i) | ||
255 | */ | ||
256 | case TBR_TT_ATOMIC_SUB: | ||
257 | p = (unsigned long __user *) __frame->gr8; | ||
258 | x = __frame->gr9; | ||
259 | |||
260 | for (;;) { | ||
261 | ret = get_user(z, p); | ||
262 | if (ret < 0) | ||
263 | goto error; | ||
264 | |||
265 | spin_lock_irq(&atomic_op_lock); | ||
266 | |||
267 | if (__get_user(z, p) == 0) { | ||
268 | y = z - x; | ||
269 | if (__put_user(y, p) == 0) | ||
270 | goto done2; | ||
271 | goto error2; | ||
272 | } | ||
273 | |||
274 | spin_unlock_irq(&atomic_op_lock); | ||
275 | } | ||
276 | |||
277 | /* TIRA gr0,#126 | ||
278 | * int __atomic_user_add_return(atomic_t *v, int i) | ||
279 | */ | ||
280 | case TBR_TT_ATOMIC_ADD: | ||
281 | p = (unsigned long __user *) __frame->gr8; | ||
282 | x = __frame->gr9; | ||
283 | |||
284 | for (;;) { | ||
285 | ret = get_user(z, p); | ||
286 | if (ret < 0) | ||
287 | goto error; | ||
288 | |||
289 | spin_lock_irq(&atomic_op_lock); | ||
290 | |||
291 | if (__get_user(z, p) == 0) { | ||
292 | y = z + x; | ||
293 | if (__put_user(y, p) == 0) | ||
294 | goto done2; | ||
295 | goto error2; | ||
296 | } | ||
297 | |||
298 | spin_unlock_irq(&atomic_op_lock); | ||
299 | } | ||
300 | |||
301 | default: | ||
302 | BUG(); | ||
303 | } | ||
304 | |||
305 | done2: | ||
306 | spin_unlock_irq(&atomic_op_lock); | ||
307 | done: | ||
308 | if (!user_mode(__frame)) | ||
309 | set_fs(oldfs); | ||
310 | __frame->gr5 = z; | ||
311 | __frame->gr9 = y; | ||
312 | return; | ||
313 | |||
314 | error2: | ||
315 | spin_unlock_irq(&atomic_op_lock); | ||
316 | error: | ||
317 | if (!user_mode(__frame)) | ||
318 | set_fs(oldfs); | ||
319 | __frame->pc -= 4; | ||
320 | |||
321 | die_if_kernel("-- Atomic Op Error --\n"); | ||
322 | |||
323 | info.si_signo = SIGSEGV; | ||
324 | info.si_code = SEGV_ACCERR; | ||
325 | info.si_errno = 0; | ||
326 | info.si_addr = (void __user *) __frame->pc; | ||
327 | |||
328 | force_sig_info(info.si_signo, &info, current); | ||
329 | } | ||
330 | |||
331 | /*****************************************************************************/ | ||
332 | /* | ||
105 | * | 333 | * |
106 | */ | 334 | */ |
107 | asmlinkage void media_exception(unsigned long msr0, unsigned long msr1) | 335 | asmlinkage void media_exception(unsigned long msr0, unsigned long msr1) |
@@ -116,7 +344,7 @@ asmlinkage void media_exception(unsigned long msr0, unsigned long msr1) | |||
116 | info.si_signo = SIGFPE; | 344 | info.si_signo = SIGFPE; |
117 | info.si_code = FPE_MDAOVF; | 345 | info.si_code = FPE_MDAOVF; |
118 | info.si_errno = 0; | 346 | info.si_errno = 0; |
119 | info.si_addr = (void *) __frame->pc; | 347 | info.si_addr = (void __user *) __frame->pc; |
120 | 348 | ||
121 | force_sig_info(info.si_signo, &info, current); | 349 | force_sig_info(info.si_signo, &info, current); |
122 | } /* end media_exception() */ | 350 | } /* end media_exception() */ |
@@ -156,7 +384,7 @@ asmlinkage void memory_access_exception(unsigned long esr0, | |||
156 | info.si_addr = NULL; | 384 | info.si_addr = NULL; |
157 | 385 | ||
158 | if ((esr0 & (ESRx_VALID | ESR0_EAV)) == (ESRx_VALID | ESR0_EAV)) | 386 | if ((esr0 & (ESRx_VALID | ESR0_EAV)) == (ESRx_VALID | ESR0_EAV)) |
159 | info.si_addr = (void *) ear0; | 387 | info.si_addr = (void __user *) ear0; |
160 | 388 | ||
161 | force_sig_info(info.si_signo, &info, current); | 389 | force_sig_info(info.si_signo, &info, current); |
162 | 390 | ||
@@ -185,7 +413,7 @@ asmlinkage void data_access_error(unsigned long esfr1, unsigned long esr15, unsi | |||
185 | info.si_signo = SIGSEGV; | 413 | info.si_signo = SIGSEGV; |
186 | info.si_code = SEGV_ACCERR; | 414 | info.si_code = SEGV_ACCERR; |
187 | info.si_errno = 0; | 415 | info.si_errno = 0; |
188 | info.si_addr = (void *) | 416 | info.si_addr = (void __user *) |
189 | (((esr15 & (ESRx_VALID|ESR15_EAV)) == (ESRx_VALID|ESR15_EAV)) ? ear15 : 0); | 417 | (((esr15 & (ESRx_VALID|ESR15_EAV)) == (ESRx_VALID|ESR15_EAV)) ? ear15 : 0); |
190 | 418 | ||
191 | force_sig_info(info.si_signo, &info, current); | 419 | force_sig_info(info.si_signo, &info, current); |
@@ -219,7 +447,7 @@ asmlinkage void division_exception(unsigned long esfr1, unsigned long esr0, unsi | |||
219 | info.si_signo = SIGFPE; | 447 | info.si_signo = SIGFPE; |
220 | info.si_code = FPE_INTDIV; | 448 | info.si_code = FPE_INTDIV; |
221 | info.si_errno = 0; | 449 | info.si_errno = 0; |
222 | info.si_addr = (void *) __frame->pc; | 450 | info.si_addr = (void __user *) __frame->pc; |
223 | 451 | ||
224 | force_sig_info(info.si_signo, &info, current); | 452 | force_sig_info(info.si_signo, &info, current); |
225 | } /* end division_exception() */ | 453 | } /* end division_exception() */ |