If the CPU declares AVX or AVX-512 support, verify that all the
corresponding bits are set in the XCR0 register.  If any are missing,
warn and don't set the corresponding X86_FEATURE_* flags.

This eliminates the perceived need for UML-supporting AVX and AVX-512
optimized code in the kernel (that is, lib/raid/ currently) to start
checking the XCR0 bits in addition to X86_FEATURE_AVX*.

This aligns UML with the vast majority of userspace programs, which
check the XCR0 bits before considering AVX and AVX-512 to be supported,
as per the procedure documented in Intel's CPU manual.

Acked-by: Christoph Hellwig <[email protected]>
Signed-off-by: Eric Biggers <[email protected]>
---
 arch/um/kernel/um_arch.c | 78 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 77 insertions(+), 1 deletion(-)

diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c
index e4ee693961e4..9d53cc6a783d 100644
--- a/arch/um/kernel/um_arch.c
+++ b/arch/um/kernel/um_arch.c
@@ -264,12 +264,88 @@ unsigned long brk_start;
 
 #define MIN_VMALLOC (32 * 1024 * 1024)
 
+static u64 __init read_xcr0(void)
+{
+       u32 a, b, c, d;
+
+       asm volatile("cpuid"
+                    : "=a"(a), "=b"(b), "=c"(c), "=d"(d)
+                    : "a"(0), "c"(0));
+       if (a >= 1) { /* max_leaf >= 1 */
+               asm volatile("cpuid"
+                            : "=a"(a), "=b"(b), "=c"(c), "=d"(d)
+                            : "a"(1), "c"(0));
+               if (c & (1 << 27)) { /* XSAVE enabled by OS */
+                       asm volatile("xgetbv" : "=d"(d), "=a"(a) : "c"(0));
+                       return ((u64)d << 32) | a;
+               }
+       }
+       return 0;
+}
+
+static void __init validate_and_set_cpu_cap(int cap, u64 xcr0)
+{
+       /*
+        * Check for missing xstate features right away, so that there's no
+        * perceived need for all optimized code in the kernel to do so.
+        */
+       switch (cap) {
+       case X86_FEATURE_AVX:
+       case X86_FEATURE_AVX2:
+       case X86_FEATURE_AVX_VNNI:
+       case X86_FEATURE_FMA:
+       case X86_FEATURE_VAES:
+       case X86_FEATURE_VPCLMULQDQ:
+               if ((xcr0 & 0x7) != 0x7) {
+                       static bool warned;
+
+                       if (!warned) {
+                               os_warn("Disabling AVX support due to missing 
xstate features\n");
+                               warned = true;
+                       }
+                       return;
+               }
+               break;
+       case X86_FEATURE_AVX512F:
+       case X86_FEATURE_AVX512BW:
+       case X86_FEATURE_AVX512CD:
+       case X86_FEATURE_AVX512DQ:
+       case X86_FEATURE_AVX512ER:
+       case X86_FEATURE_AVX512IFMA:
+       case X86_FEATURE_AVX512PF:
+       case X86_FEATURE_AVX512VBMI:
+       case X86_FEATURE_AVX512VL:
+       case X86_FEATURE_AVX512_4FMAPS:
+       case X86_FEATURE_AVX512_4VNNIW:
+       case X86_FEATURE_AVX512_BF16:
+       case X86_FEATURE_AVX512_BITALG:
+       case X86_FEATURE_AVX512_FP16:
+       case X86_FEATURE_AVX512_VBMI2:
+       case X86_FEATURE_AVX512_VNNI:
+       case X86_FEATURE_AVX512_VP2INTERSECT:
+       case X86_FEATURE_AVX512_VPOPCNTDQ:
+               if ((xcr0 & 0xe7) != 0xe7) {
+                       static bool warned;
+
+                       if (!warned) {
+                               os_warn("Disabling AVX-512 support due to 
missing xstate features\n");
+                               warned = true;
+                       }
+                       return;
+               }
+               break;
+       }
+       set_cpu_cap(&boot_cpu_data, cap);
+}
+
 static void __init parse_host_cpu_flags(char *line)
 {
+       u64 xcr0 = read_xcr0();
        int i;
+
        for (i = 0; i < 32*NCAPINTS; i++) {
                if ((x86_cap_flags[i] != NULL) && strstr(line, 
x86_cap_flags[i]))
-                       set_cpu_cap(&boot_cpu_data, i);
+                       validate_and_set_cpu_cap(i, xcr0);
        }
 }
 
-- 
2.55.0


Reply via email to