# Commit a2ed643ed783020f885035432e9c0919756921d1
# Date 2018-01-26 14:10:21 +0000
# Author Andrew Cooper <andrew.cooper3@citrix.com>
# Committer Andrew Cooper <andrew.cooper3@citrix.com>
x86/ctxt: Issue a speculation barrier between vcpu contexts

Issuing an IBPB command flushes the Branch Target Buffer, so that any poison
left by one vcpu won't remain when beginning to execute the next.

The cost of IBPB is substantial, and skipped on transition to idle, as Xen's
idle code is robust already.  All transitions into vcpu context are fully
serialising in practice (and under consideration for being retroactively
declared architecturally serialising), so a cunning attacker cannot use SP1 to
try and skip the flush.

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

--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -54,6 +54,7 @@
 #ifdef CONFIG_COMPAT
 #include <compat/vcpu.h>
 #endif
+#include <asm/spec_ctrl.h>
 
 DEFINE_PER_CPU(struct vcpu *, curr_vcpu);
 DEFINE_PER_CPU(u64, efer);
@@ -1550,6 +1551,34 @@ void context_switch(struct vcpu *prev, s
             load_LDT(next);
             load_segments(next);
         }
+
+        if ( opt_ibpb && !is_idle_domain(next->domain) )
+        {
+            static DEFINE_PER_CPU(unsigned int, last);
+            unsigned int *last_id = &this_cpu(last);
+
+            /*
+             * Squash the domid and vcpu id together for comparison
+             * efficiency.  We could in principle stash and compare the struct
+             * vcpu pointer, but this risks a false alias if a domain has died
+             * and the same 4k page gets reused for a new vcpu.
+             */
+            unsigned int next_id = (((unsigned int)next->domain->domain_id <<
+                                     16) | (uint16_t)next->vcpu_id);
+            BUILD_BUG_ON(MAX_VIRT_CPUS > 0xffff);
+
+            /*
+             * When scheduling from a vcpu, to idle, and back to the same vcpu
+             * (which might be common in a lightly loaded system, or when
+             * using vcpu pinning), there is no need to issue IBPB, as we are
+             * returning to the same security context.
+             */
+            if ( *last_id != next_id )
+            {
+                wrmsrl(MSR_PRED_CMD, PRED_CMD_IBPB);
+                *last_id = next_id;
+            }
+        }
     }
 
     context_saved(prev);
--- a/xen/arch/x86/spec_ctrl.c
+++ b/xen/arch/x86/spec_ctrl.c
@@ -32,6 +32,7 @@ enum ind_thunk {
 static int8_t __initdata opt_ibrs = -1;
 static bool_t __initdata opt_rsb_native = 1;
 static bool_t __initdata opt_rsb_vmexit = 1;
+bool_t __read_mostly opt_ibpb = 1;
 int8_t __read_mostly default_xen_ibrs = -1;
 uint8_t __read_mostly default_xen_rsb = 0;
 
@@ -47,6 +48,8 @@ static int __init parse_bti(const char *
 
         if ( (val = parse_boolean("ibrs", s, ss)) >= 0 )
             opt_ibrs = val;
+        else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 )
+            opt_ibpb = val;
         else if ( (val = parse_boolean("rsb_native", s, ss)) >= 0 )
             opt_rsb_native = val;
         else if ( (val = parse_boolean("rsb_vmexit", s, ss)) >= 0 )
@@ -88,11 +91,12 @@ static void __init print_details(enum in
 #endif
 
     printk(XENLOG_INFO
-           "BTI mitigations: Thunk %s, Others:%s%s%s\n",
+           "BTI mitigations: Thunk %s, Others:%s%s%s%s\n",
            thunk == THUNK_NONE      ? "N/A" :
            thunk == THUNK_RETPOLINE ? "RETPOLINE" : "?",
            default_xen_ibrs > 0     ? " IBRS+" :
            default_xen_ibrs == 0    ? " IBRS-" : "",
+           opt_ibpb                 ? " IBPB"  : "",
            default_xen_rsb & XEN_RSB_NATIVE ? " RSB_NATIVE" : "",
            default_xen_rsb & XEN_RSB_VMEXIT ? " RSB_VMEXIT" : "");
 }
@@ -155,6 +159,10 @@ void __init init_speculation_mitigations
     if ( opt_rsb_vmexit )
         default_xen_rsb |= XEN_RSB_VMEXIT;
 
+    /* Check we have hardware IBPB support before using it... */
+    if ( !boot_cpu_has(X86_FEATURE_IBRSB) && !boot_cpu_has(X86_FEATURE_IBPB) )
+        opt_ibpb = 0;
+
     /* (Re)init BSP state now that default_xen_* have been calculated. */
     init_shadow_spec_ctrl_state();
 
--- a/xen/include/asm-x86/spec_ctrl.h
+++ b/xen/include/asm-x86/spec_ctrl.h
@@ -24,6 +24,7 @@
 
 void init_speculation_mitigations(void);
 
+extern bool_t opt_ibpb;
 extern int8_t default_xen_ibrs;
 extern uint8_t default_xen_rsb;
 
