x86: Introduce int3 (breakpoint)-based instruction patching
[pandora-kernel.git] / arch / x86 / kernel / alternative.c
index c15cf9a..0ab4936 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/memory.h>
 #include <linux/stop_machine.h>
 #include <linux/slab.h>
+#include <linux/kdebug.h>
 #include <asm/alternative.h>
 #include <asm/sections.h>
 #include <asm/pgtable.h>
@@ -596,6 +597,111 @@ void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
        return addr;
 }
 
+static void do_sync_core(void *info)
+{
+       sync_core();
+}
+
+static bool bp_patching_in_progress;
+static void *bp_int3_handler, *bp_int3_addr;
+
+static int int3_notify(struct notifier_block *self, unsigned long val, void *data)
+{
+       struct die_args *args = data;
+
+       /* bp_patching_in_progress */
+       smp_rmb();
+
+       if (likely(!bp_patching_in_progress))
+               return NOTIFY_DONE;
+
+       /* we are not interested in non-int3 faults and ring > 0 faults */
+       if (val != DIE_INT3 || !args->regs || user_mode_vm(args->regs)
+                           || args->regs->ip != (unsigned long)bp_int3_addr)
+               return NOTIFY_DONE;
+
+       /* set up the specified breakpoint handler */
+       args->regs->ip = (unsigned long) bp_int3_handler;
+
+       return NOTIFY_STOP;
+}
+/**
+ * text_poke_bp() -- update instructions on live kernel on SMP
+ * @addr:      address to patch
+ * @opcode:    opcode of new instruction
+ * @len:       length to copy
+ * @handler:   address to jump to when the temporary breakpoint is hit
+ *
+ * Modify multi-byte instruction by using int3 breakpoint on SMP.
+ * In contrary to text_poke_smp(), we completely avoid stop_machine() here,
+ * and achieve the synchronization using int3 breakpoint.
+ *
+ * The way it is done:
+ *     - add a int3 trap to the address that will be patched
+ *     - sync cores
+ *     - update all but the first byte of the patched range
+ *     - sync cores
+ *     - replace the first byte (int3) by the first byte of
+ *       replacing opcode
+ *     - sync cores
+ *
+ * Note: must be called under text_mutex.
+ */
+void *text_poke_bp(void *addr, const void *opcode, size_t len, void *handler)
+{
+       unsigned char int3 = 0xcc;
+
+       bp_int3_handler = handler;
+       bp_int3_addr = (u8 *)addr + sizeof(int3);
+       bp_patching_in_progress = true;
+       /*
+        * Corresponding read barrier in int3 notifier for
+        * making sure the in_progress flags is correctly ordered wrt.
+        * patching
+        */
+       smp_wmb();
+
+       text_poke(addr, &int3, sizeof(int3));
+
+       on_each_cpu(do_sync_core, NULL, 1);
+
+       if (len - sizeof(int3) > 0) {
+               /* patch all but the first byte */
+               text_poke((char *)addr + sizeof(int3),
+                         (const char *) opcode + sizeof(int3),
+                         len - sizeof(int3));
+               /*
+                * According to Intel, this core syncing is very likely
+                * not necessary and we'd be safe even without it. But
+                * better safe than sorry (plus there's not only Intel).
+                */
+               on_each_cpu(do_sync_core, NULL, 1);
+       }
+
+       /* patch the first byte */
+       text_poke(addr, opcode, sizeof(int3));
+
+       on_each_cpu(do_sync_core, NULL, 1);
+
+       bp_patching_in_progress = false;
+       smp_wmb();
+
+       return addr;
+}
+
+/* this one needs to run before anything else handles it as a
+ * regular exception */
+static struct notifier_block int3_nb = {
+       .priority = 0x7fffffff,
+       .notifier_call = int3_notify
+};
+
+static int __init int3_init(void)
+{
+       return register_die_notifier(&int3_nb);
+}
+
+arch_initcall(int3_init);
 /*
  * Cross-modifying kernel text with stop_machine().
  * This code originally comes from immediate value.