arm64: add undefined instruction handler hooks
authorAlex Van Brunt <avanbrunt@nvidia.com>
Wed, 29 Jan 2014 21:41:01 +0000 (13:41 -0800)
committerGreg Hackmann <ghackmann@google.com>
Sat, 13 Sep 2014 00:29:37 +0000 (17:29 -0700)
Add undefined instruction handler hooks similar to the system in the
arm archetecture. One difference is that hooks can only be added at
boot time and they can never be removed. This removes the need for
the spinlock in the handler.

Change-Id: I4684937f5209ca2a64ee63947bb2ab6411ae14f7
Signed-off-by: Alex Van Brunt <avanbrunt@nvidia.com>
Reviewed-on: http://git-master/r/361736
Reviewed-on: http://git-master/r/365059
Reviewed-by: Richard Wiley <rwiley@nvidia.com>
Tested-by: Oskari Jaaskelainen <oskarij@nvidia.com>
arch/arm64/include/asm/traps.h
arch/arm64/kernel/traps.c

index 10ca8ff93cc25ca99a00b0bd3ed6993daea633a1..75f51eadb9eb01bcfbc4e98fc19c5f5e5fdac4d3 100644 (file)
 #ifndef __ASM_TRAP_H
 #define __ASM_TRAP_H
 
+#include <linux/list.h>
+
+struct undef_hook {
+       struct list_head node;
+       u32 instr_mask;
+       u32 instr_val;
+       u32 pstate_mask;
+       u32 pstate_val;
+       int (*fn)(struct pt_regs *regs, unsigned int instr);
+};
+
+void register_undef_hook(struct undef_hook *hook);
+
 static inline int in_exception_text(unsigned long ptr)
 {
        extern char __exception_text_start[];
index 7ffadddb645d32cda5fd54a0c2a5a0c5456b4018..4f8bf320ec99840f86d10d4e8cc6565f76de4d96 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 1995-2009 Russell King
  * Copyright (C) 2012 ARM Ltd.
+ * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -257,15 +258,54 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
                die(str, regs, err);
 }
 
+static LIST_HEAD(undef_hook);
+
+void register_undef_hook(struct undef_hook *hook)
+{
+       list_add(&hook->node, &undef_hook);
+}
+
+static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
+{
+       struct undef_hook *hook;
+       int (*fn)(struct pt_regs *regs, unsigned int instr) = NULL;
+
+       list_for_each_entry(hook, &undef_hook, node)
+               if ((instr & hook->instr_mask) == hook->instr_val &&
+                   (regs->pstate & hook->pstate_mask) == hook->pstate_val)
+                       fn = hook->fn;
+
+       return fn ? fn(regs, instr) : 1;
+}
+
 asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
 {
+       u32 instr;
        siginfo_t info;
        void __user *pc = (void __user *)instruction_pointer(regs);
 
        /* check for AArch32 breakpoint instructions */
        if (!aarch32_break_handler(regs))
                return;
+       if (compat_thumb_mode(regs)) {
+               if (get_user(instr, (u16 __user *)pc))
+                       goto die_sig;
+               if (is_wide_instruction(instr)) {
+                       u32 instr2;
+                       if (get_user(instr2, (u16 __user *)pc+1))
+                               goto die_sig;
+                       instr <<= 16;
+                       instr |= instr2;
+               }
+
+       } else if ((get_user(instr, (u32 __user *)pc))) {
+               goto die_sig;
+       }
+
+       if (call_undef_hook(regs, instr) == 0)
+               return;
 
+die_sig:
        if (show_unhandled_signals && unhandled_signal(current, SIGILL) &&
            printk_ratelimit()) {
                pr_info("%s[%d]: undefined instruction: pc=%p\n",