aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64/kernel/traps.c
diff options
context:
space:
mode:
authorPunit Agrawal <punit.agrawal@arm.com>2014-11-18 06:41:22 -0500
committerWill Deacon <will.deacon@arm.com>2014-11-20 11:33:43 -0500
commit9b79f52d1a702dd5b160f9d2ee0199c3122809bb (patch)
tree497967506563c89d76c9853408b7e271c7d400f5 /arch/arm64/kernel/traps.c
parentf97fc810798c261b2790c2a1660461a508a479e0 (diff)
arm64: Add support for hooks to handle undefined instructions
Add support to register hooks for undefined instructions. The handlers will be called when the undefined instruction and the processor state (as contained in pstate) match criteria used at registration. Signed-off-by: Punit Agrawal <punit.agrawal@arm.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm64/kernel/traps.c')
-rw-r--r--arch/arm64/kernel/traps.c66
1 files changed, 66 insertions, 0 deletions
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index de1b085e7963..0a801e3743d5 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -259,6 +259,69 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
259 } 259 }
260} 260}
261 261
262static LIST_HEAD(undef_hook);
263static DEFINE_RAW_SPINLOCK(undef_lock);
264
265void register_undef_hook(struct undef_hook *hook)
266{
267 unsigned long flags;
268
269 raw_spin_lock_irqsave(&undef_lock, flags);
270 list_add(&hook->node, &undef_hook);
271 raw_spin_unlock_irqrestore(&undef_lock, flags);
272}
273
274void unregister_undef_hook(struct undef_hook *hook)
275{
276 unsigned long flags;
277
278 raw_spin_lock_irqsave(&undef_lock, flags);
279 list_del(&hook->node);
280 raw_spin_unlock_irqrestore(&undef_lock, flags);
281}
282
283static int call_undef_hook(struct pt_regs *regs)
284{
285 struct undef_hook *hook;
286 unsigned long flags;
287 u32 instr;
288 int (*fn)(struct pt_regs *regs, u32 instr) = NULL;
289 void __user *pc = (void __user *)instruction_pointer(regs);
290
291 if (!user_mode(regs))
292 return 1;
293
294 if (compat_thumb_mode(regs)) {
295 /* 16-bit Thumb instruction */
296 if (get_user(instr, (u16 __user *)pc))
297 goto exit;
298 instr = le16_to_cpu(instr);
299 if (aarch32_insn_is_wide(instr)) {
300 u32 instr2;
301
302 if (get_user(instr2, (u16 __user *)(pc + 2)))
303 goto exit;
304 instr2 = le16_to_cpu(instr2);
305 instr = (instr << 16) | instr2;
306 }
307 } else {
308 /* 32-bit ARM instruction */
309 if (get_user(instr, (u32 __user *)pc))
310 goto exit;
311 instr = le32_to_cpu(instr);
312 }
313
314 raw_spin_lock_irqsave(&undef_lock, flags);
315 list_for_each_entry(hook, &undef_hook, node)
316 if ((instr & hook->instr_mask) == hook->instr_val &&
317 (regs->pstate & hook->pstate_mask) == hook->pstate_val)
318 fn = hook->fn;
319
320 raw_spin_unlock_irqrestore(&undef_lock, flags);
321exit:
322 return fn ? fn(regs, instr) : 1;
323}
324
262asmlinkage void __exception do_undefinstr(struct pt_regs *regs) 325asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
263{ 326{
264 siginfo_t info; 327 siginfo_t info;
@@ -268,6 +331,9 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
268 if (!aarch32_break_handler(regs)) 331 if (!aarch32_break_handler(regs))
269 return; 332 return;
270 333
334 if (call_undef_hook(regs) == 0)
335 return;
336
271 if (show_unhandled_signals && unhandled_signal(current, SIGILL) && 337 if (show_unhandled_signals && unhandled_signal(current, SIGILL) &&
272 printk_ratelimit()) { 338 printk_ratelimit()) {
273 pr_info("%s[%d]: undefined instruction: pc=%p\n", 339 pr_info("%s[%d]: undefined instruction: pc=%p\n",