Subject: x86/alternative: Support replacements when a feature is not present
From: Andrew Cooper <[email-masked]>

Use the top bit of a->cpuid to express inverted polarity.  This requires
stripping the top bit back out when performing the sanity checks.

Despite only being used once, create a replace boolean to express the decision
more clearly in _apply_alternatives().

Signed-off-by: Andrew Cooper <[email-masked]>
Reviewed-by: Jan Beulich <[email-masked]>

diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c
index 1ba35cb9ede9..88c90044c20d 100644
--- a/xen/arch/x86/alternative.c
+++ b/xen/arch/x86/alternative.c
@@ -197,6 +197,8 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start,
         uint8_t *repl = ALT_REPL_PTR(a);
         uint8_t buf[MAX_PATCH_LEN];
         unsigned int total_len = a->orig_len + a->pad_len;
+        unsigned int feat = a->cpuid & ~ALT_FLAG_NOT;
+        bool inv = a->cpuid & ALT_FLAG_NOT, replace;
 
         if ( a->repl_len > total_len )
         {
@@ -214,11 +216,11 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start,
             return -ENOSPC;
         }
 
-        if ( a->cpuid >= NCAPINTS * 32 )
+        if ( feat >= NCAPINTS * 32 )
         {
              printk(XENLOG_ERR
                    "Alt for %ps, feature %#x outside of featureset range %#x\n",
-                   ALT_ORIG_PTR(a), a->cpuid, NCAPINTS * 32);
+                   ALT_ORIG_PTR(a), feat, NCAPINTS * 32);
             return -ERANGE;
         }
 
@@ -243,8 +245,14 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start,
             continue;
         }
 
+        /*
+         * Should a replacement be performed?  Most replacements have positive
+         * polarity, but we support negative polarity too.
+         */
+        replace = boot_cpu_has(feat) ^ inv;
+
         /* If there is no replacement to make, see about optimising the nops. */
-        if ( !boot_cpu_has(a->cpuid) )
+        if ( !replace )
         {
             /* Origin site site already touched?  Don't nop anything. */
             if ( base->priv )
diff --git a/xen/arch/x86/include/asm/alternative-asm.h b/xen/arch/x86/include/asm/alternative-asm.h
index 83e8594f0eaf..ed3f4cb055e8 100644
--- a/xen/arch/x86/include/asm/alternative-asm.h
+++ b/xen/arch/x86/include/asm/alternative-asm.h
@@ -12,7 +12,7 @@
  * instruction. See apply_alternatives().
  */
 .macro altinstruction_entry orig, repl, feature, orig_len, repl_len, pad_len
-    .if \feature >= NCAPINTS * 32
+    .if ((\feature) & ~ALT_FLAG_NOT) >= NCAPINTS * 32
         .error "alternative feature outside of featureset range"
     .endif
     .long \orig - .
diff --git a/xen/arch/x86/include/asm/alternative.h b/xen/arch/x86/include/asm/alternative.h
index 38472fb58e2d..ef63e257ad78 100644
--- a/xen/arch/x86/include/asm/alternative.h
+++ b/xen/arch/x86/include/asm/alternative.h
@@ -1,6 +1,13 @@
 #ifndef __X86_ALTERNATIVE_H__
 #define __X86_ALTERNATIVE_H__
 
+/*
+ * Common to both C and ASM.  Express a replacement when a feature is not
+ * available.
+ */
+#define ALT_FLAG_NOT (1 << 15)
+#define ALT_NOT(x) (ALT_FLAG_NOT | (x))
+
 #ifdef __ASSEMBLY__
 #include <asm/alternative-asm.h>
 #else
@@ -12,7 +19,7 @@
 struct __packed alt_instr {
     int32_t  orig_offset;   /* original instruction */
     int32_t  repl_offset;   /* offset to replacement instruction */
-    uint16_t cpuid;         /* cpuid bit set for replacement */
+    uint16_t cpuid;         /* cpuid bit set for replacement (top bit is polarity) */
     uint8_t  orig_len;      /* length of original instruction */
     uint8_t  repl_len;      /* length of new instruction */
     uint8_t  pad_len;       /* length of build-time padding */
@@ -60,7 +67,7 @@ extern void alternative_branches(void);
                     alt_repl_len(n2)) "-" alt_orig_len)
 
 #define ALTINSTR_ENTRY(feature, num)                                    \
-        " .if " STR(feature) " >= " STR(NCAPINTS * 32) "\n"             \
+        " .if (" STR(feature & ~ALT_FLAG_NOT) ") >= " STR(NCAPINTS * 32) "\n" \
         " .error \"alternative feature outside of featureset range\"\n" \
         " .endif\n"                                                     \
         " .long .LXEN%=_orig_s - .\n"             /* label           */ \