s32 *s;
  
        for (s = start; s < end; s++) {
-               u32 endbr, poison = gen_endbr_poison();
                void *addr = (void *)s + *s;
  
-               if (WARN_ON_ONCE(get_kernel_nofault(endbr, addr)))
+               poison_endbr(addr, true);
+               if (IS_ENABLED(CONFIG_FINEIBT))
+                       poison_endbr(addr - 16, false);
+       }
+ }
+ 
+ #else
+ 
 -void __init_or_module noinline apply_ibt_endbr(s32 *start, s32 *end) { }
++void __init_or_module apply_ibt_endbr(s32 *start, s32 *end) { }
+ 
+ #endif /* CONFIG_X86_KERNEL_IBT */
+ 
+ #ifdef CONFIG_FINEIBT
+ 
+ enum cfi_mode {
+       CFI_DEFAULT,
+       CFI_OFF,
+       CFI_KCFI,
+       CFI_FINEIBT,
+ };
+ 
+ static enum cfi_mode cfi_mode __ro_after_init = CFI_DEFAULT;
+ static bool cfi_rand __ro_after_init = true;
+ static u32  cfi_seed __ro_after_init;
+ 
+ /*
+  * Re-hash the CFI hash with a boot-time seed while making sure the result is
+  * not a valid ENDBR instruction.
+  */
+ static u32 cfi_rehash(u32 hash)
+ {
+       hash ^= cfi_seed;
+       while (unlikely(is_endbr(hash) || is_endbr(-hash))) {
+               bool lsb = hash & 1;
+               hash >>= 1;
+               if (lsb)
+                       hash ^= 0x80200003;
+       }
+       return hash;
+ }
+ 
+ static __init int cfi_parse_cmdline(char *str)
+ {
+       if (!str)
+               return -EINVAL;
+ 
+       while (str) {
+               char *next = strchr(str, ',');
+               if (next) {
+                       *next = 0;
+                       next++;
+               }
+ 
+               if (!strcmp(str, "auto")) {
+                       cfi_mode = CFI_DEFAULT;
+               } else if (!strcmp(str, "off")) {
+                       cfi_mode = CFI_OFF;
+                       cfi_rand = false;
+               } else if (!strcmp(str, "kcfi")) {
+                       cfi_mode = CFI_KCFI;
+               } else if (!strcmp(str, "fineibt")) {
+                       cfi_mode = CFI_FINEIBT;
+               } else if (!strcmp(str, "norand")) {
+                       cfi_rand = false;
+               } else {
+                       pr_err("Ignoring unknown cfi option (%s).", str);
+               }
+ 
+               str = next;
+       }
+ 
+       return 0;
+ }
+ early_param("cfi", cfi_parse_cmdline);
+ 
+ /*
+  * kCFI                                               FineIBT
+  *
+  * __cfi_\func:                                       __cfi_\func:
+  *    movl   $0x12345678,%eax         // 5         endbr64                    // 4
+  *    nop                                          subl   $0x12345678,%r10d   // 7
+  *    nop                                          jz     1f                  // 2
+  *    nop                                          ud2                        // 2
+  *    nop                                     1:   nop                        // 1
+  *    nop
+  *    nop
+  *    nop
+  *    nop
+  *    nop
+  *    nop
+  *    nop
+  *
+  *
+  * caller:                                    caller:
+  *    movl    $(-0x12345678),%r10d     // 6        movl   $0x12345678,%r10d   // 6
+  *    addl    $-15(%r11),%r10d         // 4        sub    $16,%r11            // 4
+  *    je      1f                       // 2        nop4                       // 4
+  *    ud2                              // 2
+  * 1: call    __x86_indirect_thunk_r11 // 5        call   *%r11; nop2;        // 5
+  *
+  */
+ 
+ asm(  ".pushsection .rodata                   \n"
+       "fineibt_preamble_start:                \n"
+       "       endbr64                         \n"
+       "       subl    $0x12345678, %r10d      \n"
+       "       je      fineibt_preamble_end    \n"
+       "       ud2                             \n"
+       "       nop                             \n"
+       "fineibt_preamble_end:                  \n"
+       ".popsection\n"
+ );
+ 
+ extern u8 fineibt_preamble_start[];
+ extern u8 fineibt_preamble_end[];
+ 
+ #define fineibt_preamble_size (fineibt_preamble_end - fineibt_preamble_start)
+ #define fineibt_preamble_hash 7
+ 
+ asm(  ".pushsection .rodata                   \n"
+       "fineibt_caller_start:                  \n"
+       "       movl    $0x12345678, %r10d      \n"
+       "       sub     $16, %r11               \n"
+       ASM_NOP4
+       "fineibt_caller_end:                    \n"
+       ".popsection                            \n"
+ );
+ 
+ extern u8 fineibt_caller_start[];
+ extern u8 fineibt_caller_end[];
+ 
+ #define fineibt_caller_size (fineibt_caller_end - fineibt_caller_start)
+ #define fineibt_caller_hash 2
+ 
+ #define fineibt_caller_jmp (fineibt_caller_size - 2)
+ 
+ static u32 decode_preamble_hash(void *addr)
+ {
+       u8 *p = addr;
+ 
+       /* b8 78 56 34 12          mov    $0x12345678,%eax */
+       if (p[0] == 0xb8)
+               return *(u32 *)(addr + 1);
+ 
+       return 0; /* invalid hash value */
+ }
+ 
+ static u32 decode_caller_hash(void *addr)
+ {
+       u8 *p = addr;
+ 
+       /* 41 ba 78 56 34 12       mov    $0x12345678,%r10d */
+       if (p[0] == 0x41 && p[1] == 0xba)
+               return -*(u32 *)(addr + 2);
+ 
+       /* e8 0c 78 56 34 12       jmp.d8  +12 */
+       if (p[0] == JMP8_INSN_OPCODE && p[1] == fineibt_caller_jmp)
+               return -*(u32 *)(addr + 2);
+ 
+       return 0; /* invalid hash value */
+ }
+ 
+ /* .retpoline_sites */
+ static int cfi_disable_callers(s32 *start, s32 *end)
+ {
+       /*
+        * Disable kCFI by patching in a JMP.d8, this leaves the hash immediate
+        * in tact for later usage. Also see decode_caller_hash() and
+        * cfi_rewrite_callers().
+        */
+       const u8 jmp[] = { JMP8_INSN_OPCODE, fineibt_caller_jmp };
+       s32 *s;
+ 
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+ 
+               addr -= fineibt_caller_size;
+               hash = decode_caller_hash(addr);
+               if (!hash) /* nocfi callers */
                        continue;
  
-               if (WARN_ON_ONCE(!is_endbr(endbr)))
+               text_poke_early(addr, jmp, 2);
+       }
+ 
+       return 0;
+ }
+ 
+ static int cfi_enable_callers(s32 *start, s32 *end)
+ {
+       /*
+        * Re-enable kCFI, undo what cfi_disable_callers() did.
+        */
+       const u8 mov[] = { 0x41, 0xba };
+       s32 *s;
+ 
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+ 
+               addr -= fineibt_caller_size;
+               hash = decode_caller_hash(addr);
+               if (!hash) /* nocfi callers */
                        continue;
  
-               DPRINTK("ENDBR at: %pS (%px)", addr, addr);
+               text_poke_early(addr, mov, 2);
+       }
  
-               /*
-                * When we have IBT, the lack of ENDBR will trigger #CP
-                */
-               DUMP_BYTES(((u8*)addr), 4, "%px: orig: ", addr);
-               DUMP_BYTES(((u8*)&poison), 4, "%px: repl: ", addr);
-               text_poke_early(addr, &poison, 4);
+       return 0;
+ }
+ 
+ /* .cfi_sites */
+ static int cfi_rand_preamble(s32 *start, s32 *end)
+ {
+       s32 *s;
+ 
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+ 
+               hash = decode_preamble_hash(addr);
+               if (WARN(!hash, "no CFI hash found at: %pS %px %*ph\n",
+                        addr, addr, 5, addr))
+                       return -EINVAL;
+ 
+               hash = cfi_rehash(hash);
+               text_poke_early(addr + 1, &hash, 4);
+       }
+ 
+       return 0;
+ }
+ 
+ static int cfi_rewrite_preamble(s32 *start, s32 *end)
+ {
+       s32 *s;
+ 
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+ 
+               hash = decode_preamble_hash(addr);
+               if (WARN(!hash, "no CFI hash found at: %pS %px %*ph\n",
+                        addr, addr, 5, addr))
+                       return -EINVAL;
+ 
+               text_poke_early(addr, fineibt_preamble_start, fineibt_preamble_size);
+               WARN_ON(*(u32 *)(addr + fineibt_preamble_hash) != 0x12345678);
+               text_poke_early(addr + fineibt_preamble_hash, &hash, 4);
+       }
+ 
+       return 0;
+ }
+ 
+ /* .retpoline_sites */
+ static int cfi_rand_callers(s32 *start, s32 *end)
+ {
+       s32 *s;
+ 
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+ 
+               addr -= fineibt_caller_size;
+               hash = decode_caller_hash(addr);
+               if (hash) {
+                       hash = -cfi_rehash(hash);
+                       text_poke_early(addr + 2, &hash, 4);
+               }
+       }
+ 
+       return 0;
+ }
+ 
+ static int cfi_rewrite_callers(s32 *start, s32 *end)
+ {
+       s32 *s;
+ 
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+ 
+               addr -= fineibt_caller_size;
+               hash = decode_caller_hash(addr);
+               if (hash) {
+                       text_poke_early(addr, fineibt_caller_start, fineibt_caller_size);
+                       WARN_ON(*(u32 *)(addr + fineibt_caller_hash) != 0x12345678);
+                       text_poke_early(addr + fineibt_caller_hash, &hash, 4);
+               }
+               /* rely on apply_retpolines() */
+       }
+ 
+       return 0;
+ }
+ 
+ static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
+                           s32 *start_cfi, s32 *end_cfi, bool builtin)
+ {
+       int ret;
+ 
+       if (WARN_ONCE(fineibt_preamble_size != 16,
+                     "FineIBT preamble wrong size: %ld", fineibt_preamble_size))
+               return;
+ 
+       if (cfi_mode == CFI_DEFAULT) {
+               cfi_mode = CFI_KCFI;
+               if (HAS_KERNEL_IBT && cpu_feature_enabled(X86_FEATURE_IBT))
+                       cfi_mode = CFI_FINEIBT;
+       }
+ 
+       /*
+        * Rewrite the callers to not use the __cfi_ stubs, such that we might
+        * rewrite them. This disables all CFI. If this succeeds but any of the
+        * later stages fails, we're without CFI.
+        */
+       ret = cfi_disable_callers(start_retpoline, end_retpoline);
+       if (ret)
+               goto err;
+ 
+       if (cfi_rand) {
+               if (builtin)
+                       cfi_seed = get_random_u32();
+ 
+               ret = cfi_rand_preamble(start_cfi, end_cfi);
+               if (ret)
+                       goto err;
+ 
+               ret = cfi_rand_callers(start_retpoline, end_retpoline);
+               if (ret)
+                       goto err;
        }
+ 
+       switch (cfi_mode) {
+       case CFI_OFF:
+               if (builtin)
+                       pr_info("Disabling CFI\n");
+               return;
+ 
+       case CFI_KCFI:
+               ret = cfi_enable_callers(start_retpoline, end_retpoline);
+               if (ret)
+                       goto err;
+ 
+               if (builtin)
+                       pr_info("Using kCFI\n");
+               return;
+ 
+       case CFI_FINEIBT:
+               ret = cfi_rewrite_preamble(start_cfi, end_cfi);
+               if (ret)
+                       goto err;
+ 
+               ret = cfi_rewrite_callers(start_retpoline, end_retpoline);
+               if (ret)
+                       goto err;
+ 
+               if (builtin)
+                       pr_info("Using FineIBT CFI\n");
+               return;
+ 
+       default:
+               break;
+       }
+ 
+ err:
+       pr_err("Something went horribly wrong trying to rewrite the CFI implementation.\n");
  }
  
  #else