# Commit 6be613f29b4205349275d24367bd4c82fb2960dd
# Date 2019-03-12 17:05:21 +0000
# Author Andrew Cooper <andrew.cooper3@citrix.com>
# Committer Andrew Cooper <andrew.cooper3@citrix.com>
x86/tsx: Implement controls for RTM force-abort mode

The CPUID bit and MSR are deliberately not exposed to guests, because they
won't exist on newer processors.  As vPMU isn't security supported, the
misbehaviour of PCR3 isn't expected to impact production deployments.

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
@@ -1540,7 +1540,7 @@ Use Virtual Processor ID support if avai
 flushes on VM entry and exit, increasing performance.
 
 ### vpmu
-> `= ( bts )`
+> `= ( bts | rtm-abort=<bool> )`
 
 > Default: `off`
 
@@ -1556,6 +1556,21 @@ wrong behaviour (see handle\_pmc\_quirk(
 If 'vpmu=bts' is specified the virtualisation of the Branch Trace Store (BTS)
 feature is switched on on Intel processors supporting this feature.
 
+vpmu=rtm-abort controls a trade-off between working Restricted Transactional
+Memory, and working performance counters.
+
+All processors released to date (Q1 2019) supporting Transactional Memory
+Extensions suffer an erratum which has been addressed in microcode.
+
+Processors based on the Skylake microarchitecture with up-to-date
+microcode internally use performance counter 3 to work around the erratum.
+A consequence is that the counter gets reprogrammed whenever an `XBEGIN`
+instruction is executed.
+
+An alternative mode exists where PCR3 behaves as before, at the cost of
+`XBEGIN` unconditionally aborting.  Enabling `rtm-abort` mode will
+activate this alternative mode.
+
 Note that if **watchdog** option is also specified vpmu will be turned off.
 
 *Warning:*
--- a/xen/arch/x86/cpu/intel.c
+++ b/xen/arch/x86/cpu/intel.c
@@ -210,6 +210,9 @@ static void __devinit Intel_errata_worka
 	if (c->x86 == 6 && cpu_has_clflush &&
 	    (c->x86_model == 29 || c->x86_model == 46 || c->x86_model == 47))
 		set_bit(X86_FEATURE_CLFLUSH_MONITOR, c->x86_capability);
+
+	if (cpu_has_tsx_force_abort && opt_rtm_abort)
+		wrmsrl(MSR_TSX_FORCE_ABORT, TSX_FORCE_ABORT_RTM);
 }
 
 
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -4546,6 +4546,8 @@ int hvm_msr_read_intercept(unsigned int
     case MSR_PRED_CMD:
     case MSR_FLUSH_CMD:
         /* Write-only */
+    case MSR_TSX_FORCE_ABORT:
+        /* Not offered to guests. */
         goto gp_fault;
 
     case MSR_SPEC_CTRL:
@@ -4735,6 +4737,8 @@ int hvm_msr_write_intercept(unsigned int
 
     case MSR_ARCH_CAPABILITIES:
         /* Read-only */
+    case MSR_TSX_FORCE_ABORT:
+        /* Not offered to guests. */
         goto gp_fault;
 
     case MSR_AMD64_NB_CFG:
--- a/xen/arch/x86/hvm/vpmu.c
+++ b/xen/arch/x86/hvm/vpmu.c
@@ -39,6 +39,7 @@
  * "vpmu=bts" : vpmu enabled and Intel BTS feature switched on.
  */
 static unsigned int __read_mostly opt_vpmu_enabled;
+bool_t __read_mostly opt_rtm_abort;
 static void parse_vpmu_param(char *s);
 custom_param("vpmu", parse_vpmu_param);
 
@@ -48,11 +49,19 @@ static void __init parse_vpmu_param(char
 {
     switch ( parse_bool(s) )
     {
+        int val;
+
     case 0:
         break;
     default:
         if ( !strcmp(s, "bts") )
             opt_vpmu_enabled |= VPMU_BOOT_BTS;
+        else if ( (val = parse_boolean("rtm-abort", s, s + strlen(s))) >= 0 )
+        {
+            opt_rtm_abort = val;
+            /* rtm-abort doesn't imply vpmu=1 */
+            break;
+        }
         else if ( *s )
         {
             printk("VPMU: unknown flag: %s - vpmu disabled!\n", s);
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -2674,6 +2674,8 @@ static int emulate_privileged_op(struct
         case MSR_INTEL_PLATFORM_INFO:
         case MSR_ARCH_CAPABILITIES:
             /* The MSR is read-only. */
+        case MSR_TSX_FORCE_ABORT:
+            /* Not offered to guests. */
             goto fail;
 
         case MSR_SPEC_CTRL:
@@ -2819,6 +2821,8 @@ static int emulate_privileged_op(struct
         case MSR_PRED_CMD:
         case MSR_FLUSH_CMD:
             /* Write-only */
+        case MSR_TSX_FORCE_ABORT:
+            /* Not offered to guests. */
             goto fail;
 
         case MSR_SPEC_CTRL:
--- a/xen/include/asm-x86/cpufeature.h
+++ b/xen/include/asm-x86/cpufeature.h
@@ -170,6 +170,7 @@
 #define X86_FEATURE_IBPB	(8*32+12) /* IBPB support only (no IBRS, used by AMD) */
 
 /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */
+#define X86_FEATURE_TSX_FORCE_ABORT (9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */
 #define X86_FEATURE_IBRSB	(9*32+26) /* IBRS and IBPB support (used by Intel) */
 #define X86_FEATURE_STIBP	(9*32+27) /* STIBP */
 #define X86_FEATURE_L1D_FLUSH	(9*32+28) /* MSR_FLUSH_CMD and L1D flush. */
@@ -227,7 +228,7 @@
 #define cpu_has_avx             boot_cpu_has(X86_FEATURE_AVX)
 #define cpu_has_lwp             boot_cpu_has(X86_FEATURE_LWP)
 #define cpu_has_mpx             boot_cpu_has(X86_FEATURE_MPX)
-
+#define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT)
 #define cpu_has_arch_perfmon    boot_cpu_has(X86_FEATURE_ARCH_PERFMON)
 
 #define cpu_has_rdtscp          boot_cpu_has(X86_FEATURE_RDTSCP)
--- a/xen/include/asm-x86/msr-index.h
+++ b/xen/include/asm-x86/msr-index.h
@@ -45,6 +45,9 @@
 #define MSR_FLUSH_CMD			0x0000010b
 #define FLUSH_CMD_L1D			(_AC(1, ULL) << 0)
 
+#define MSR_TSX_FORCE_ABORT             0x0000010f
+#define TSX_FORCE_ABORT_RTM             (_AC(1, ULL) <<  0)
+
 /* Intel MSRs. Some also available on other CPUs */
 #define MSR_IA32_PERFCTR0		0x000000c1
 #define MSR_IA32_A_PERFCTR0		0x000004c1
--- a/xen/include/asm-x86/hvm/vpmu.h
+++ b/xen/include/asm-x86/hvm/vpmu.h
@@ -87,6 +87,8 @@ struct vpmu_struct {
 #define vpmu_is_set(_vpmu, _x) ((_vpmu)->flags & (_x))
 #define vpmu_clear(_vpmu)      ((_vpmu)->flags = 0)
 
+extern bool_t opt_rtm_abort;
+
 int vpmu_do_wrmsr(unsigned int msr, uint64_t msr_content, uint64_t supported);
 int vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content);
 void vpmu_do_interrupt(struct cpu_user_regs *regs);
