Subject: x86/spec-ctrl: Synthesise ITS_NO to guests on unaffected hardware
From: Andrew Cooper <[email-masked]>

It is easier to express feature word 17 in terms of word 16 + [32, 64) as
that's how the layout is given in documentation.

This is part of XSA-469 / CVE-2024-28956

Signed-off-by: Andrew Cooper <[email-masked]>
Reviewed-by: Roger Pau Monné <[email-masked]>

diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h
index 3a06b6f297b9..24b021eeffc8 100644
--- a/xen/arch/x86/include/asm/cpufeature.h
+++ b/xen/arch/x86/include/asm/cpufeature.h
@@ -218,6 +218,7 @@ static inline bool boot_cpu_has(unsigned int feat)
 #define cpu_has_gds_no          boot_cpu_has(X86_FEATURE_GDS_NO)
 #define cpu_has_rfds_no         boot_cpu_has(X86_FEATURE_RFDS_NO)
 #define cpu_has_rfds_clear      boot_cpu_has(X86_FEATURE_RFDS_CLEAR)
+#define cpu_has_its_no          boot_cpu_has(X86_FEATURE_ITS_NO)
 
 /* Synthesized. */
 #define cpu_has_arch_perfmon    boot_cpu_has(X86_FEATURE_ARCH_PERFMON)
diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c
index fe27e82a4707..0a635025e488 100644
--- a/xen/arch/x86/spec_ctrl.c
+++ b/xen/arch/x86/spec_ctrl.c
@@ -1760,6 +1760,90 @@ static void __init bhi_calculations(void)
     }
 }
 
+/*
+ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/indirect-target-selection.html
+ */
+static void __init its_calculations(void)
+{
+    /*
+     * Indirect Target Selection is a Branch Prediction bug whereby certain
+     * indirect branches (including RETs) get predicted using a direct branch
+     * target, rather than a suitable indirect target, bypassing hardware
+     * isolation protections.
+     *
+     * ITS affects Core (but not Atom) processors starting from the
+     * introduction of eIBRS, up to but not including Golden Cove cores
+     * (checked here with BHI_CTRL).
+     *
+     * The ITS_NO feature is not expected to be enumerated by hardware, and is
+     * only for VMMs to synthesise for guests.
+     *
+     * ITS comes in 3 flavours:
+     *
+     *   1) Across-IBPB.  Indirect branches after the IBPB can be controlled
+     *      by direct targets which existed prior to the IBPB.  This is
+     *      addressed in the IPU 2025.1 microcode drop, and has no other
+     *      software interaction.
+     *
+     *   2) Guest/Host.  Indirect branches in the VMM can be controlled by
+     *      direct targets from the guest.  This applies equally to PV guests
+     *      (Ring3) and HVM guests (VMX), and applies to all Skylake-uarch
+     *      cores with eIBRS.
+     *
+     *   3) Intra-mode.  Indirect branches in the VMM can be controlled by
+     *      other execution in the same mode.
+     */
+
+    /*
+     * If we can see ITS_NO, or we're virtualised, do nothing.  We are or may
+     * migrate somewhere unsafe.
+     */
+    if ( cpu_has_its_no || cpu_has_hypervisor )
+        return;
+
+    /* ITS is only known to affect Intel processors at this time. */
+    if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL )
+        return;
+
+    /*
+     * ITS does not exist on:
+     *  - non-Family 6 CPUs
+     *  - those without eIBRS
+     *  - those with BHI_CTRL
+     * but we still need to synthesise ITS_NO.
+     */
+    if ( boot_cpu_data.x86 != 6 || !cpu_has_eibrs ||
+         boot_cpu_has(X86_FEATURE_BHI_CTRL) )
+        goto synthesise;
+
+    switch ( boot_cpu_data.x86_model )
+    {
+        /* These Skylake-uarch cores suffer cases #2 and #3. */
+    case INTEL_FAM6_SKYLAKE_X:
+    case INTEL_FAM6_KABYLAKE_L:
+    case INTEL_FAM6_KABYLAKE:
+    case INTEL_FAM6_COMETLAKE:
+    case INTEL_FAM6_COMETLAKE_L:
+        return;
+
+        /* These Sunny/Willow/Cypress Cove cores suffer case #3. */
+    case INTEL_FAM6_ICELAKE_X:
+    case INTEL_FAM6_ICELAKE_D:
+    case INTEL_FAM6_ICELAKE_L:
+    case INTEL_FAM6_TIGERLAKE_L:
+    case INTEL_FAM6_TIGERLAKE:
+    case INTEL_FAM6_ROCKETLAKE:
+        return;
+
+    default:
+        break;
+    }
+
+    /* Platforms remaining are not believed to be vulnerable to ITS. */
+ synthesise:
+    setup_force_cpu_cap(X86_FEATURE_ITS_NO);
+}
+
 void spec_ctrl_init_domain(struct domain *d)
 {
     bool pv = is_pv_domain(d);
@@ -2316,6 +2400,8 @@ void __init init_speculation_mitigations(void)
 
     bhi_calculations();
 
+    its_calculations();
+
     print_details(thunk);
 
     /*
diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h
index 16207e3817bb..1e0e870c5c7d 100644
--- a/xen/include/public/arch-x86/cpufeatureset.h
+++ b/xen/include/public/arch-x86/cpufeatureset.h
@@ -379,7 +379,8 @@ XEN_CPUFEATURE(GDS_NO,             16*32+26) /*A  No Gather Data Sampling */
 XEN_CPUFEATURE(RFDS_NO,            16*32+27) /*A  No Register File Data Sampling */
 XEN_CPUFEATURE(RFDS_CLEAR,         16*32+28) /*!A| Register File(s) cleared by VERW */
 
-/* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.edx, word 17 */
+/* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.edx, word 17 (express in terms of word 16) */
+XEN_CPUFEATURE(ITS_NO,             16*32+62) /*!A No Indirect Target Selection */
 
 #endif /* XEN_CPUFEATURE */
 
diff --git a/xen/tools/gen-cpuid.py b/xen/tools/gen-cpuid.py
index a77cb30bdb4f..163b105bc6a7 100755
--- a/xen/tools/gen-cpuid.py
+++ b/xen/tools/gen-cpuid.py
@@ -51,7 +51,7 @@ def parse_definitions(state):
         r"\s+/\*([\w!|]*) .*$")
 
     word_regex = re.compile(
-        r"^/\* .* word (\d*) \*/$")
+        r"^/\* .* word (\d*) .*\*/$")
     last_word = -1
 
     this = sys.modules[__name__]