]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/x86/kernel/jump_label.c
x86/jump-label: Show where and what was wrong on errors
[karo-tx-linux.git] / arch / x86 / kernel / jump_label.c
index 2889b3d438823ea58d6796355e08fb081c1896f7..912a528129168c909ae1940de3d718dbbe6a9913 100644 (file)
@@ -24,18 +24,57 @@ union jump_code_union {
        } __attribute__((packed));
 };
 
+static void bug_at(unsigned char *ip, int line)
+{
+       /*
+        * The location is not an op that we were expecting.
+        * Something went wrong. Crash the box, as something could be
+        * corrupting the kernel.
+        */
+       pr_warning("Unexpected op at %pS [%p] (%02x %02x %02x %02x %02x) %s:%d\n",
+              ip, ip, ip[0], ip[1], ip[2], ip[3], ip[4], __FILE__, line);
+       BUG();
+}
+
 static void __jump_label_transform(struct jump_entry *entry,
                                   enum jump_label_type type,
-                                  void *(*poker)(void *, const void *, size_t))
+                                  void *(*poker)(void *, const void *, size_t),
+                                  int init)
 {
        union jump_code_union code;
+       const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5];
 
        if (type == JUMP_LABEL_ENABLE) {
+               /*
+                * We are enabling this jump label. If it is not a nop
+                * then something must have gone wrong.
+                */
+               if (unlikely(memcmp((void *)entry->code, ideal_nop, 5) != 0))
+                       bug_at((void *)entry->code, __LINE__);
+
                code.jump = 0xe9;
                code.offset = entry->target -
                                (entry->code + JUMP_LABEL_NOP_SIZE);
-       } else
+       } else {
+               /*
+                * We are disabling this jump label. If it is not what
+                * we think it is, then something must have gone wrong.
+                * If this is the first initialization call, then we
+                * are converting the default nop to the ideal nop.
+                */
+               if (init) {
+                       const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP };
+                       if (unlikely(memcmp((void *)entry->code, default_nop, 5) != 0))
+                               bug_at((void *)entry->code, __LINE__);
+               } else {
+                       code.jump = 0xe9;
+                       code.offset = entry->target -
+                               (entry->code + JUMP_LABEL_NOP_SIZE);
+                       if (unlikely(memcmp((void *)entry->code, &code, 5) != 0))
+                               bug_at((void *)entry->code, __LINE__);
+               }
                memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE);
+       }
 
        (*poker)((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE);
 }
@@ -45,15 +84,38 @@ void arch_jump_label_transform(struct jump_entry *entry,
 {
        get_online_cpus();
        mutex_lock(&text_mutex);
-       __jump_label_transform(entry, type, text_poke_smp);
+       __jump_label_transform(entry, type, text_poke_smp, 0);
        mutex_unlock(&text_mutex);
        put_online_cpus();
 }
 
+static enum {
+       JL_STATE_START,
+       JL_STATE_NO_UPDATE,
+       JL_STATE_UPDATE,
+} jlstate __initdata_or_module = JL_STATE_START;
+
 __init_or_module void arch_jump_label_transform_static(struct jump_entry *entry,
                                      enum jump_label_type type)
 {
-       __jump_label_transform(entry, type, text_poke_early);
+       /*
+        * This function is called at boot up and when modules are
+        * first loaded. Check if the default nop, the one that is
+        * inserted at compile time, is the ideal nop. If it is, then
+        * we do not need to update the nop, and we can leave it as is.
+        * If it is not, then we need to update the nop to the ideal nop.
+        */
+       if (jlstate == JL_STATE_START) {
+               const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP };
+               const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5];
+
+               if (memcmp(ideal_nop, default_nop, 5) != 0)
+                       jlstate = JL_STATE_UPDATE;
+               else
+                       jlstate = JL_STATE_NO_UPDATE;
+       }
+       if (jlstate == JL_STATE_UPDATE)
+               __jump_label_transform(entry, type, text_poke_early, 1);
 }
 
 #endif