Merge tag 'x86-mm-2021-04-29' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
[platform/kernel/linux-starfive.git] / arch / x86 / kernel / alternative.c
index 94649f8..6974b51 100644 (file)
@@ -28,6 +28,7 @@
 #include <asm/insn.h>
 #include <asm/io.h>
 #include <asm/fixmap.h>
+#include <asm/paravirt.h>
 
 int __read_mostly alternatives_patched;
 
@@ -74,186 +75,30 @@ do {                                                                       \
        }                                                               \
 } while (0)
 
-/*
- * Each GENERIC_NOPX is of X bytes, and defined as an array of bytes
- * that correspond to that nop. Getting from one nop to the next, we
- * add to the array the offset that is equal to the sum of all sizes of
- * nops preceding the one we are after.
- *
- * Note: The GENERIC_NOP5_ATOMIC is at the end, as it breaks the
- * nice symmetry of sizes of the previous nops.
- */
-#if defined(GENERIC_NOP1) && !defined(CONFIG_X86_64)
-static const unsigned char intelnops[] =
-{
-       GENERIC_NOP1,
-       GENERIC_NOP2,
-       GENERIC_NOP3,
-       GENERIC_NOP4,
-       GENERIC_NOP5,
-       GENERIC_NOP6,
-       GENERIC_NOP7,
-       GENERIC_NOP8,
-       GENERIC_NOP5_ATOMIC
-};
-static const unsigned char * const intel_nops[ASM_NOP_MAX+2] =
+const unsigned char x86nops[] =
 {
-       NULL,
-       intelnops,
-       intelnops + 1,
-       intelnops + 1 + 2,
-       intelnops + 1 + 2 + 3,
-       intelnops + 1 + 2 + 3 + 4,
-       intelnops + 1 + 2 + 3 + 4 + 5,
-       intelnops + 1 + 2 + 3 + 4 + 5 + 6,
-       intelnops + 1 + 2 + 3 + 4 + 5 + 6 + 7,
-       intelnops + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8,
+       BYTES_NOP1,
+       BYTES_NOP2,
+       BYTES_NOP3,
+       BYTES_NOP4,
+       BYTES_NOP5,
+       BYTES_NOP6,
+       BYTES_NOP7,
+       BYTES_NOP8,
 };
-#endif
 
-#ifdef K8_NOP1
-static const unsigned char k8nops[] =
-{
-       K8_NOP1,
-       K8_NOP2,
-       K8_NOP3,
-       K8_NOP4,
-       K8_NOP5,
-       K8_NOP6,
-       K8_NOP7,
-       K8_NOP8,
-       K8_NOP5_ATOMIC
-};
-static const unsigned char * const k8_nops[ASM_NOP_MAX+2] =
+const unsigned char * const x86_nops[ASM_NOP_MAX+1] =
 {
        NULL,
-       k8nops,
-       k8nops + 1,
-       k8nops + 1 + 2,
-       k8nops + 1 + 2 + 3,
-       k8nops + 1 + 2 + 3 + 4,
-       k8nops + 1 + 2 + 3 + 4 + 5,
-       k8nops + 1 + 2 + 3 + 4 + 5 + 6,
-       k8nops + 1 + 2 + 3 + 4 + 5 + 6 + 7,
-       k8nops + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8,
+       x86nops,
+       x86nops + 1,
+       x86nops + 1 + 2,
+       x86nops + 1 + 2 + 3,
+       x86nops + 1 + 2 + 3 + 4,
+       x86nops + 1 + 2 + 3 + 4 + 5,
+       x86nops + 1 + 2 + 3 + 4 + 5 + 6,
+       x86nops + 1 + 2 + 3 + 4 + 5 + 6 + 7,
 };
-#endif
-
-#if defined(K7_NOP1) && !defined(CONFIG_X86_64)
-static const unsigned char k7nops[] =
-{
-       K7_NOP1,
-       K7_NOP2,
-       K7_NOP3,
-       K7_NOP4,
-       K7_NOP5,
-       K7_NOP6,
-       K7_NOP7,
-       K7_NOP8,
-       K7_NOP5_ATOMIC
-};
-static const unsigned char * const k7_nops[ASM_NOP_MAX+2] =
-{
-       NULL,
-       k7nops,
-       k7nops + 1,
-       k7nops + 1 + 2,
-       k7nops + 1 + 2 + 3,
-       k7nops + 1 + 2 + 3 + 4,
-       k7nops + 1 + 2 + 3 + 4 + 5,
-       k7nops + 1 + 2 + 3 + 4 + 5 + 6,
-       k7nops + 1 + 2 + 3 + 4 + 5 + 6 + 7,
-       k7nops + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8,
-};
-#endif
-
-#ifdef P6_NOP1
-static const unsigned char p6nops[] =
-{
-       P6_NOP1,
-       P6_NOP2,
-       P6_NOP3,
-       P6_NOP4,
-       P6_NOP5,
-       P6_NOP6,
-       P6_NOP7,
-       P6_NOP8,
-       P6_NOP5_ATOMIC
-};
-static const unsigned char * const p6_nops[ASM_NOP_MAX+2] =
-{
-       NULL,
-       p6nops,
-       p6nops + 1,
-       p6nops + 1 + 2,
-       p6nops + 1 + 2 + 3,
-       p6nops + 1 + 2 + 3 + 4,
-       p6nops + 1 + 2 + 3 + 4 + 5,
-       p6nops + 1 + 2 + 3 + 4 + 5 + 6,
-       p6nops + 1 + 2 + 3 + 4 + 5 + 6 + 7,
-       p6nops + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8,
-};
-#endif
-
-/* Initialize these to a safe default */
-#ifdef CONFIG_X86_64
-const unsigned char * const *ideal_nops = p6_nops;
-#else
-const unsigned char * const *ideal_nops = intel_nops;
-#endif
-
-void __init arch_init_ideal_nops(void)
-{
-       switch (boot_cpu_data.x86_vendor) {
-       case X86_VENDOR_INTEL:
-               /*
-                * Due to a decoder implementation quirk, some
-                * specific Intel CPUs actually perform better with
-                * the "k8_nops" than with the SDM-recommended NOPs.
-                */
-               if (boot_cpu_data.x86 == 6 &&
-                   boot_cpu_data.x86_model >= 0x0f &&
-                   boot_cpu_data.x86_model != 0x1c &&
-                   boot_cpu_data.x86_model != 0x26 &&
-                   boot_cpu_data.x86_model != 0x27 &&
-                   boot_cpu_data.x86_model < 0x30) {
-                       ideal_nops = k8_nops;
-               } else if (boot_cpu_has(X86_FEATURE_NOPL)) {
-                          ideal_nops = p6_nops;
-               } else {
-#ifdef CONFIG_X86_64
-                       ideal_nops = k8_nops;
-#else
-                       ideal_nops = intel_nops;
-#endif
-               }
-               break;
-
-       case X86_VENDOR_HYGON:
-               ideal_nops = p6_nops;
-               return;
-
-       case X86_VENDOR_AMD:
-               if (boot_cpu_data.x86 > 0xf) {
-                       ideal_nops = p6_nops;
-                       return;
-               }
-
-               fallthrough;
-
-       default:
-#ifdef CONFIG_X86_64
-               ideal_nops = k8_nops;
-#else
-               if (boot_cpu_has(X86_FEATURE_K8))
-                       ideal_nops = k8_nops;
-               else if (boot_cpu_has(X86_FEATURE_K7))
-                       ideal_nops = k7_nops;
-               else
-                       ideal_nops = intel_nops;
-#endif
-       }
-}
 
 /* Use this to add nops to a buffer, then text_poke the whole buffer. */
 static void __init_or_module add_nops(void *insns, unsigned int len)
@@ -262,7 +107,7 @@ static void __init_or_module add_nops(void *insns, unsigned int len)
                unsigned int noplen = len;
                if (noplen > ASM_NOP_MAX)
                        noplen = ASM_NOP_MAX;
-               memcpy(insns, ideal_nops[noplen], noplen);
+               memcpy(insns, x86_nops[noplen], noplen);
                insns += noplen;
                len -= noplen;
        }
@@ -344,19 +189,35 @@ done:
 static void __init_or_module noinline optimize_nops(struct alt_instr *a, u8 *instr)
 {
        unsigned long flags;
-       int i;
+       struct insn insn;
+       int nop, i = 0;
+
+       /*
+        * Jump over the non-NOP insns, the remaining bytes must be single-byte
+        * NOPs, optimize them.
+        */
+       for (;;) {
+               if (insn_decode_kernel(&insn, &instr[i]))
+                       return;
+
+               if (insn.length == 1 && insn.opcode.bytes[0] == 0x90)
+                       break;
+
+               if ((i += insn.length) >= a->instrlen)
+                       return;
+       }
 
-       for (i = 0; i < a->padlen; i++) {
-               if (instr[i] != 0x90)
+       for (nop = i; i < a->instrlen; i++) {
+               if (WARN_ONCE(instr[i] != 0x90, "Not a NOP at 0x%px\n", &instr[i]))
                        return;
        }
 
        local_irq_save(flags);
-       add_nops(instr + (a->instrlen - a->padlen), a->padlen);
+       add_nops(instr + nop, i - nop);
        local_irq_restore(flags);
 
        DUMP_BYTES(instr, a->instrlen, "%px: [%d:%d) optimized NOPs: ",
-                  instr, a->instrlen - a->padlen, a->padlen);
+                  instr, nop, a->instrlen);
 }
 
 /*
@@ -388,23 +249,29 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
         */
        for (a = start; a < end; a++) {
                int insn_buff_sz = 0;
+               /* Mask away "NOT" flag bit for feature to test. */
+               u16 feature = a->cpuid & ~ALTINSTR_FLAG_INV;
 
                instr = (u8 *)&a->instr_offset + a->instr_offset;
                replacement = (u8 *)&a->repl_offset + a->repl_offset;
                BUG_ON(a->instrlen > sizeof(insn_buff));
-               BUG_ON(a->cpuid >= (NCAPINTS + NBUGINTS) * 32);
-               if (!boot_cpu_has(a->cpuid)) {
-                       if (a->padlen > 1)
-                               optimize_nops(a, instr);
+               BUG_ON(feature >= (NCAPINTS + NBUGINTS) * 32);
 
-                       continue;
-               }
+               /*
+                * Patch if either:
+                * - feature is present
+                * - feature not present but ALTINSTR_FLAG_INV is set to mean,
+                *   patch if feature is *NOT* present.
+                */
+               if (!boot_cpu_has(feature) == !(a->cpuid & ALTINSTR_FLAG_INV))
+                       goto next;
 
-               DPRINTK("feat: %d*32+%d, old: (%pS (%px) len: %d), repl: (%px, len: %d), pad: %d",
-                       a->cpuid >> 5,
-                       a->cpuid & 0x1f,
+               DPRINTK("feat: %s%d*32+%d, old: (%pS (%px) len: %d), repl: (%px, len: %d)",
+                       (a->cpuid & ALTINSTR_FLAG_INV) ? "!" : "",
+                       feature >> 5,
+                       feature & 0x1f,
                        instr, instr, a->instrlen,
-                       replacement, a->replacementlen, a->padlen);
+                       replacement, a->replacementlen);
 
                DUMP_BYTES(instr, a->instrlen, "%px: old_insn: ", instr);
                DUMP_BYTES(replacement, a->replacementlen, "%px: rpl_insn: ", replacement);
@@ -428,14 +295,15 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
                if (a->replacementlen && is_jmp(replacement[0]))
                        recompute_jump(a, instr, replacement, insn_buff);
 
-               if (a->instrlen > a->replacementlen) {
-                       add_nops(insn_buff + a->replacementlen,
-                                a->instrlen - a->replacementlen);
-                       insn_buff_sz += a->instrlen - a->replacementlen;
-               }
+               for (; insn_buff_sz < a->instrlen; insn_buff_sz++)
+                       insn_buff[insn_buff_sz] = 0x90;
+
                DUMP_BYTES(insn_buff, insn_buff_sz, "%px: final_insn: ", instr);
 
                text_poke_early(instr, insn_buff, insn_buff_sz);
+
+next:
+               optimize_nops(a, instr);
        }
 }
 
@@ -605,7 +473,7 @@ void __init_or_module apply_paravirt(struct paravirt_patch_site *start,
                BUG_ON(p->len > MAX_PATCH_LEN);
                /* prep the buffer with the original instructions */
                memcpy(insn_buff, p->instr, p->len);
-               used = pv_ops.init.patch(p->type, insn_buff, (unsigned long)p->instr, p->len);
+               used = paravirt_patch(p->type, insn_buff, (unsigned long)p->instr, p->len);
 
                BUG_ON(used > p->len);
 
@@ -723,6 +591,33 @@ void __init alternative_instructions(void)
         * patching.
         */
 
+       /*
+        * Paravirt patching and alternative patching can be combined to
+        * replace a function call with a short direct code sequence (e.g.
+        * by setting a constant return value instead of doing that in an
+        * external function).
+        * In order to make this work the following sequence is required:
+        * 1. set (artificial) features depending on used paravirt
+        *    functions which can later influence alternative patching
+        * 2. apply paravirt patching (generally replacing an indirect
+        *    function call with a direct one)
+        * 3. apply alternative patching (e.g. replacing a direct function
+        *    call with a custom code sequence)
+        * Doing paravirt patching after alternative patching would clobber
+        * the optimization of the custom code with a function call again.
+        */
+       paravirt_set_cap();
+
+       /*
+        * First patch paravirt functions, such that we overwrite the indirect
+        * call with the direct call.
+        */
+       apply_paravirt(__parainstructions, __parainstructions_end);
+
+       /*
+        * Then patch alternatives, such that those paravirt calls that are in
+        * alternatives can be overwritten by their immediate fragments.
+        */
        apply_alternatives(__alt_instructions, __alt_instructions_end);
 
 #ifdef CONFIG_SMP
@@ -741,8 +636,6 @@ void __init alternative_instructions(void)
        }
 #endif
 
-       apply_paravirt(__parainstructions, __parainstructions_end);
-
        restart_nmi();
        alternatives_patched = 1;
 }
@@ -1274,15 +1167,15 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
                               const void *opcode, size_t len, const void *emulate)
 {
        struct insn insn;
+       int ret;
 
        memcpy((void *)tp->text, opcode, len);
        if (!emulate)
                emulate = opcode;
 
-       kernel_insn_init(&insn, emulate, MAX_INSN_SIZE);
-       insn_get_length(&insn);
+       ret = insn_decode_kernel(&insn, emulate);
 
-       BUG_ON(!insn_complete(&insn));
+       BUG_ON(ret < 0);
        BUG_ON(len != insn.length);
 
        tp->rel_addr = addr - (void *)_stext;
@@ -1302,13 +1195,13 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
        default: /* assume NOP */
                switch (len) {
                case 2: /* NOP2 -- emulate as JMP8+0 */
-                       BUG_ON(memcmp(emulate, ideal_nops[len], len));
+                       BUG_ON(memcmp(emulate, x86_nops[len], len));
                        tp->opcode = JMP8_INSN_OPCODE;
                        tp->rel32 = 0;
                        break;
 
                case 5: /* NOP5 -- emulate as JMP32+0 */
-                       BUG_ON(memcmp(emulate, ideal_nops[NOP_ATOMIC5], len));
+                       BUG_ON(memcmp(emulate, x86_nops[len], len));
                        tp->opcode = JMP32_INSN_OPCODE;
                        tp->rel32 = 0;
                        break;