# Commit b76ec3946bf6caca2c3950b857c008bc8db6723f
# Date 2018-08-14 16:56:47 +0100
# Author Juergen Gross <jgross@suse.com>
# Committer Andrew Cooper <andrew.cooper3@citrix.com>
x86/shadow: Infrastructure to force a PV guest into shadow mode

To mitigate L1TF, we cannot alter an architecturally-legitimate PTE a PV guest
chooses to write, but we can force the PV domain into shadow mode so Xen
controls the PTEs which are reachable by the CPU pagewalk.

Introduce new shadow mode, PG_SH_forced, and a tasklet to perform the
transition.  Later patches will introduce the logic to enable this mode at the
appropriate time.

To simplify vcpu cleanup, make tasklet_kill() idempotent with respect to
tasklet_init(), which involves adding a helper to check for an uninitialised
list head.

This is part of XSA-273 / CVE-2018-3620.

Signed-off-by: Juergen Gross <jgross@suse.com>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Tim Deegan <tim@xen.org>
Reviewed-by: Jan Beulich <jbeulich@suse.com>

--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -56,6 +56,7 @@
 #include <compat/vcpu.h>
 #endif
 #include <asm/spec_ctrl.h>
+#include <asm/shadow.h>
 
 DEFINE_PER_CPU(struct vcpu *, curr_vcpu);
 DEFINE_PER_CPU(u64, efer);
@@ -424,6 +425,9 @@ int arch_domain_create(struct domain *d,
         (domcr_flags & DOMCRF_hap);
     d->arch.hvm_domain.mem_sharing_enabled = 0;
 
+    if ( !is_hvm_domain(d) )
+        pv_l1tf_domain_init(d);
+
     d->arch.s3_integrity = !!(domcr_flags & DOMCRF_s3_integrity);
 
     INIT_LIST_HEAD(&d->arch.pdev_list);
@@ -562,6 +566,8 @@ int arch_domain_create(struct domain *d,
 #else
     free_xenheap_pages(d->arch.mm_perdomain_pt, pdpt_order);
 #endif
+    if ( !is_hvm_domain(d) )
+        pv_l1tf_domain_destroy(d);
     return rc;
 }
 
@@ -573,6 +579,8 @@ void arch_domain_destroy(struct domain *
 
     if ( is_hvm_domain(d) )
         hvm_domain_destroy(d);
+    else
+        pv_l1tf_domain_destroy(d);
 
     free_domain_pirqs(d);
     if ( !is_idle_domain(d) )
--- a/xen/arch/x86/mm/paging.c
+++ b/xen/arch/x86/mm/paging.c
@@ -806,6 +806,8 @@ void paging_dump_domain_info(struct doma
         printk("    paging assistance: ");
         if ( paging_mode_shadow(d) )
             printk("shadow ");
+        if ( paging_mode_sh_forced(d) )
+            printk("forced ");
         if ( paging_mode_hap(d) )
             printk("hap ");
         if ( paging_mode_refcounts(d) )
--- a/xen/arch/x86/mm/shadow/common.c
+++ b/xen/arch/x86/mm/shadow/common.c
@@ -3138,6 +3138,16 @@ static void sh_new_mode(struct domain *d
 
     ASSERT(shadow_locked_by_me(d));
     ASSERT(d != current->domain);
+
+    /*
+     * If PG_SH_forced has previously been activated because of writing an
+     * L1TF-vulnerable PTE, it must remain active for the remaining lifetime
+     * of the domain, even if the logdirty mode needs to be controlled for
+     * migration purposes.
+     */
+    if ( paging_mode_sh_forced(d) )
+        new_mode |= PG_SH_forced | PG_SH_enable;
+
     d->arch.paging.mode = new_mode;
     for_each_vcpu(d, v)
         sh_update_paging_modes(v);
@@ -4015,6 +4025,29 @@ void shadow_audit_tables(struct vcpu *v)
 
 #endif /* Shadow audit */
 
+void pv_l1tf_tasklet(unsigned long data)
+{
+    struct domain *d = (void *)data;
+
+    domain_pause(d);
+    shadow_lock(d);
+
+    if ( !paging_mode_sh_forced(d) && !d->is_dying )
+    {
+        int ret = shadow_one_bit_enable(d, PG_SH_forced);
+
+        if ( ret )
+        {
+            printk(XENLOG_G_ERR "d%d Failed to enable PG_SH_forced: %d\n",
+                   d->domain_id, ret);
+            domain_crash(d);
+        }
+    }
+
+    shadow_unlock(d);
+    domain_unpause(d);
+}
+
 /*
  * Local variables:
  * mode: C
--- a/xen/common/softirq.c
+++ b/xen/common/softirq.c
@@ -167,6 +167,10 @@ void tasklet_kill(struct tasklet *t)
 
     spin_lock_irqsave(&tasklet_lock, flags);
 
+    /* Cope with uninitialised tasklets. */
+    if ( list_head_is_null(&t->list) )
+        goto unlock;
+
     if ( !list_empty(&t->list) )
     {
         BUG_ON(t->is_dead || t->is_running || !t->is_scheduled);
@@ -182,6 +186,7 @@ void tasklet_kill(struct tasklet *t)
         spin_lock_irqsave(&tasklet_lock, flags);
     }
 
+ unlock:
     spin_unlock_irqrestore(&tasklet_lock, flags);
 }
 
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -122,6 +122,9 @@ struct shadow_domain {
     int oos_off;
 
     int pagetable_dying_op;
+
+    /* PV L1 Terminal Fault mitigation. */
+    struct tasklet pv_l1tf_tasklet;
 };
 
 struct shadow_vcpu {
@@ -307,6 +310,9 @@ struct arch_domain
     /* Domain cannot handle spurious page faults? */
     bool_t suppress_spurious_page_faults;
 
+    /* Mitigate L1TF with shadow/crashing? */
+    bool_t check_l1tf;
+
     /* Continuable domain_relinquish_resources(). */
     enum {
         RELMEM_not_started,
--- a/xen/include/asm-x86/paging.h
+++ b/xen/include/asm-x86/paging.h
@@ -38,8 +38,10 @@
 
 #define PG_SH_shift    20
 #define PG_HAP_shift   21
+#define PG_SHF_shift   22
 /* We're in one of the shadow modes */
 #define PG_SH_enable   (1U << PG_SH_shift)
+#define PG_SH_forced   (1U << PG_SHF_shift)
 #define PG_HAP_enable  (1U << PG_HAP_shift)
 
 /* common paging mode bits */
@@ -56,6 +58,7 @@
 
 #define paging_mode_enabled(_d)   ((_d)->arch.paging.mode)
 #define paging_mode_shadow(_d)    ((_d)->arch.paging.mode & PG_SH_enable)
+#define paging_mode_sh_forced(_d) ((_d)->arch.paging.mode & PG_SH_forced)
 #define paging_mode_hap(_d)       ((_d)->arch.paging.mode & PG_HAP_enable)
 
 #define paging_mode_refcounts(_d) ((_d)->arch.paging.mode & PG_refcounts)
--- a/xen/include/asm-x86/shadow.h
+++ b/xen/include/asm-x86/shadow.h
@@ -30,6 +30,7 @@
 #include <asm/flushtlb.h>
 #include <asm/paging.h>
 #include <asm/p2m.h>
+#include <asm/spec_ctrl.h>
 
 /*****************************************************************************
  * Macros to tell which shadow paging mode a domain is in*/
@@ -107,6 +108,33 @@ shadow_drop_references(struct domain *d,
         sh_remove_all_mappings(d->vcpu[0], _mfn(page_to_mfn(p)));
 }
 
+/*
+ * Mitigations for L1TF / CVE-2018-3620 for PV guests.
+ *
+ * We cannot alter an architecturally-legitimate PTE which a PV guest has
+ * chosen to write, as traditional paged-out metadata is L1TF-vulnerable.
+ * What we can do is force a PV guest which writes a vulnerable PTE into
+ * shadow mode, so Xen controls the pagetables which are reachable by the CPU
+ * pagewalk.
+ */
+
+void pv_l1tf_tasklet(unsigned long data);
+
+static inline void pv_l1tf_domain_init(struct domain *d)
+{
+    d->arch.check_l1tf =
+        opt_pv_l1tf & (d->domain_id == 0 /* ->is_privileged not usable yet */
+                       ? OPT_PV_L1TF_DOM0 : OPT_PV_L1TF_DOMU);
+
+    tasklet_init(&d->arch.paging.shadow.pv_l1tf_tasklet,
+                 pv_l1tf_tasklet, (unsigned long)d);
+}
+
+static inline void pv_l1tf_domain_destroy(struct domain *d)
+{
+    tasklet_kill(&d->arch.paging.shadow.pv_l1tf_tasklet);
+}
+
 /* Remove all shadows of the guest mfn. */
 void sh_remove_shadows(struct vcpu *v, mfn_t gmfn, int fast, int all);
 static inline void shadow_remove_all_shadows(struct vcpu *v, mfn_t gmfn)
--- a/xen/include/xen/list.h
+++ b/xen/include/xen/list.h
@@ -42,6 +42,11 @@ static inline void INIT_LIST_HEAD(struct
     list->prev = list;
 }
 
+static inline bool_t list_head_is_null(const struct list_head *list)
+{
+    return !list->next && !list->prev;
+}
+
 /*
  * Insert a new entry between two known consecutive entries. 
  *
