x86/tsx: Introduce tsx= to use MSR_TSX_CTRL when available

To protect against the TSX Async Abort speculative vulnerability, Intel have
released new microcode for affected parts which introduce the MSR_TSX_CTRL
control, which allows TSX to be turned off.  This will be architectural on
future parts.

Introduce tsx= to provide a global on/off for TSX, including its enumeration
via CPUID.  Provide stub virtualisation of this MSR, as it is not exposed to
guests at the moment.

VMs may have booted before microcode is loaded, or before hosts have rebooted,
and they still want to migrate freely.  A VM which booted seeing TSX can
migrate safely to hosts with TSX disabled - TSX will start unconditionally
aborting, but still behave in a manner compatible with the ABI.

The guest-visible behaviour is equivalent to late loading the microcode and
setting the RTM_DISABLE bit in the course of live patching.

This is part of XSA-305 / CVE-2019-11135

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>

--- a/docs/misc/xen-command-line.markdown
+++ b/docs/misc/xen-command-line.markdown
@@ -1491,6 +1491,20 @@ pages) must also be specified via the tb
 ### tsc
 > `= unstable | skewed`
 
+### tsx
+    = <bool>
+
+    Applicability: x86
+    Default: true
+
+Controls for the use of Transactional Synchronization eXtensions.
+
+On Intel parts released in Q3 2019 (with updated microcode), and future parts,
+a control has been introduced which allows TSX to be turned off.
+
+On systems with the ability to turn TSX off, this boolean offers system wide
+control of whether TSX is enabled or disabled.
+
 ### ucode
 > `= [<integer> | scan]`
 
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -54,6 +54,7 @@ obj-y += sysctl.o
 obj-y += time.o
 obj-y += trace.o
 obj-y += traps.o
+obj-y += tsx.o
 obj-y += usercopy.o
 obj-y += x86_emulate.o
 obj-y += machine_kexec.o
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -4401,6 +4401,19 @@ void hvm_cpuid(unsigned int input, unsig
                 *edx |= cpufeat_mask(X86_FEATURE_STIBP);
             else
                 *edx &= ~cpufeat_mask(X86_FEATURE_STIBP);
+
+            /*
+             * On hardware with MSR_TSX_CTRL, the admin may have elected to
+             * disable TSX and hide the feature bits.  Migrating-in VMs may
+             * have been booted pre-mitigation when the TSX features were
+             * visible.
+             *
+             * This situation is compatible (albeit with a perf hit to any
+             * TSX code in the guest), so allow the feature bits to remain set.
+             */
+            if ( cpu_has_tsx_ctrl )
+                *ebx |= cpufeat_mask(X86_FEATURE_HLE) |
+                        cpufeat_mask(X86_FEATURE_RTM);
         }
         break;
     case 0xb:
@@ -4566,6 +4579,7 @@ int hvm_msr_read_intercept(unsigned int
     case MSR_FLUSH_CMD:
         /* Write-only */
     case MSR_TSX_FORCE_ABORT:
+    case MSR_TSX_CTRL:
         /* Not offered to guests. */
         goto gp_fault;
 
@@ -4758,6 +4772,7 @@ int hvm_msr_write_intercept(unsigned int
     case MSR_ARCH_CAPABILITIES:
         /* Read-only */
     case MSR_TSX_FORCE_ABORT:
+    case MSR_TSX_CTRL:
         /* Not offered to guests. */
         goto gp_fault;
 
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -1362,6 +1362,8 @@ void __init noreturn __start_xen(unsigne
 
     early_microcode_init();
 
+    tsx_init(); /* Needs microcode.  May change HLE/RTM feature bits. */
+
     identify_cpu(&boot_cpu_data);
 
     if ( cpu_has_fxsr )
--- a/xen/arch/x86/smpboot.c
+++ b/xen/arch/x86/smpboot.c
@@ -381,6 +381,8 @@ void start_secondary(void *unused)
     if ( boot_cpu_has(X86_FEATURE_IBRSB) )
         wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl);
 
+    tsx_init(); /* Needs microcode.  May change HLE/RTM feature bits. */
+
     smp_callin();
 
     init_percpu_time();
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -825,6 +825,20 @@ void pv_cpuid(uint32_t leaf, uint32_t su
                     d |= cpufeat_mask(X86_FEATURE_STIBP);
                 else
                     d &= ~cpufeat_mask(X86_FEATURE_STIBP);
+
+                /*
+                 * On hardware with MSR_TSX_CTRL, the admin may have elected to
+                 * disable TSX and hide the feature bits.  Migrating-in VMs may
+                 * have been booted pre-mitigation when the TSX features were
+                 * visible.
+                 *
+                 * This situation is compatible (albeit with a perf hit to any
+                 * TSX code in the guest), so allow the feature bits to remain
+                 * set.
+                 */
+                if ( cpu_has_tsx_ctrl )
+                    b |= cpufeat_mask(X86_FEATURE_HLE) |
+                         cpufeat_mask(X86_FEATURE_RTM);
             }
             break;
 
@@ -2689,6 +2703,7 @@ static int emulate_privileged_op(struct
         case MSR_ARCH_CAPABILITIES:
             /* The MSR is read-only. */
         case MSR_TSX_FORCE_ABORT:
+        case MSR_TSX_CTRL:
             /* Not offered to guests. */
             goto fail;
 
@@ -2836,6 +2851,7 @@ static int emulate_privileged_op(struct
         case MSR_FLUSH_CMD:
             /* Write-only */
         case MSR_TSX_FORCE_ABORT:
+        case MSR_TSX_CTRL:
             /* Not offered to guests. */
             goto fail;
 
--- /dev/null
+++ b/xen/arch/x86/tsx.c
@@ -0,0 +1,79 @@
+#include <xen/init.h>
+#include <asm/msr.h>
+
+/*
+ * Valid values:
+ *   1 => Explicit tsx=1
+ *   0 => Explicit tsx=0
+ *  -1 => Default, implicit tsx=1
+ *
+ * This is arranged such that the bottom bit encodes whether TSX is actually
+ * disabled, while identifying various explicit (>=0) and implicit (<0)
+ * conditions.
+ */
+int8_t __read_mostly opt_tsx = -1;
+int8_t __read_mostly cpu_has_tsx_ctrl = -1;
+
+static int __init parse_tsx(const char *s)
+{
+    int rc = 0, val = parse_bool(s);
+
+    if ( val >= 0 )
+        opt_tsx = val;
+    else
+        rc = -EINVAL;
+
+    return rc;
+}
+custom_param("tsx", parse_tsx);
+
+void tsx_init(void)
+{
+    static bool_t __read_mostly logged;
+
+    /*
+     * This function is first called between microcode being loaded, and CPUID
+     * being scanned generally.  Calculate from raw data whether MSR_TSX_CTRL
+     * is available.
+     */
+    if ( unlikely(cpu_has_tsx_ctrl < 0) )
+    {
+        uint64_t caps = 0;
+
+        if ( boot_cpu_data.cpuid_level >= 7 &&
+             (cpuid_count_edx(7, 0) & (1 << 29)) )
+            rdmsrl(MSR_ARCH_CAPABILITIES, caps);
+
+        cpu_has_tsx_ctrl = !!(caps & (1 << 7));
+    }
+
+    if ( cpu_has_tsx_ctrl )
+    {
+        uint64_t val;
+
+        rdmsrl(MSR_TSX_CTRL, val);
+
+        val &= ~(TSX_CTRL_RTM_DISABLE | TSX_CTRL_CPUID_CLEAR);
+        /* Check bottom bit only.  Higher bits are various sentinals. */
+        if ( !(opt_tsx & 1) )
+            val |= TSX_CTRL_RTM_DISABLE | TSX_CTRL_CPUID_CLEAR;
+
+        wrmsrl(MSR_TSX_CTRL, val);
+    }
+    else if ( opt_tsx >= 0 && !logged )
+    {
+        logged = 1;
+        printk(XENLOG_WARNING
+               "MSR_TSX_CTRL not available - Ignoring tsx= setting\n");
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- a/xen/include/asm-x86/msr-index.h
+++ b/xen/include/asm-x86/msr-index.h
@@ -52,6 +52,10 @@
 #define MSR_TSX_FORCE_ABORT             0x0000010f
 #define TSX_FORCE_ABORT_RTM             (_AC(1, ULL) <<  0)
 
+#define MSR_TSX_CTRL                    0x00000122
+#define TSX_CTRL_RTM_DISABLE            (_AC(1, ULL) <<  0)
+#define TSX_CTRL_CPUID_CLEAR            (_AC(1, ULL) <<  1)
+
 /* Intel MSRs. Some also available on other CPUs */
 #define MSR_IA32_PERFCTR0		0x000000c1
 #define MSR_IA32_A_PERFCTR0		0x000004c1
--- a/xen/include/asm-x86/processor.h
+++ b/xen/include/asm-x86/processor.h
@@ -299,6 +299,16 @@ static always_inline unsigned int cpuid_
     return edx;
 }
 
+static always_inline unsigned int cpuid_count_edx(
+    unsigned int leaf, unsigned int subleaf)
+{
+    unsigned int edx, tmp;
+
+    cpuid_count(leaf, subleaf, &tmp, &tmp, &tmp, &edx);
+
+    return edx;
+}
+
 static inline unsigned long read_cr0(void)
 {
     unsigned long cr0;
@@ -579,6 +589,9 @@ static inline void pv_cpuid_regs(struct
              &regs->_eax, &regs->_ebx, &regs->_ecx, &regs->_edx);
 }
 
+extern int8_t opt_tsx, cpu_has_tsx_ctrl;
+void tsx_init(void);
+
 #endif /* !__ASSEMBLY__ */
 
 #endif /* __ASM_X86_PROCESSOR_H */
