Fix buggy/incomplete detection of AVX/AVX2 support
[libgcrypt.git] / src / hwf-x86.c
index 2ceb04c..0591b4f 100644 (file)
@@ -95,6 +95,21 @@ get_cpuid(unsigned int in, unsigned int *eax, unsigned int *ebx,
   if (edx)
     *edx = regs[3];
 }
+
+static unsigned int
+get_xgetbv(void)
+{
+  unsigned int t_eax;
+
+  asm volatile
+    ("xgetbv\n\t"
+     : "=a" (t_eax)
+     : "c" (0)
+    );
+
+  return t_eax;
+}
+
 #endif /* i386 && GNUC */
 
 
@@ -129,6 +144,21 @@ get_cpuid(unsigned int in, unsigned int *eax, unsigned int *ebx,
   if (edx)
     *edx = regs[3];
 }
+
+static unsigned int
+get_xgetbv(void)
+{
+  unsigned int t_eax;
+
+  asm volatile
+    ("xgetbv\n\t"
+     : "=a" (t_eax)
+     : "c" (0)
+    );
+
+  return t_eax;
+}
+
 #endif /* x86-64 && GNUC */
 
 
@@ -138,9 +168,12 @@ detect_x86_gnuc (void)
 {
   char vendor_id[12+1];
   unsigned int features;
+  unsigned int os_supports_avx_avx2_registers = 0;
   unsigned int max_cpuid_level;
   unsigned int result = 0;
 
+  (void)os_supports_avx_avx2_registers;
+
   if (!is_cpuid_available())
     return 0;
 
@@ -189,6 +222,7 @@ detect_x86_gnuc (void)
   else if (!strcmp (vendor_id, "GenuineIntel"))
     {
       /* This is an Intel CPU.  */
+      result |= HWF_INTEL_CPU;
     }
   else if (!strcmp (vendor_id, "AuthenticAMD"))
     {
@@ -201,15 +235,33 @@ detect_x86_gnuc (void)
   /* Get CPU info and Intel feature flags (ECX).  */
   get_cpuid(1, NULL, NULL, &features, NULL);
 
+#ifdef ENABLE_PCLMUL_SUPPORT
+  /* Test bit 1 for PCLMUL.  */
+  if (features & 0x00000002)
+     result |= HWF_INTEL_PCLMUL;
+#endif
+  /* Test bit 9 for SSSE3.  */
+  if (features & 0x00000200)
+     result |= HWF_INTEL_SSSE3;
 #ifdef ENABLE_AESNI_SUPPORT
   /* Test bit 25 for AES-NI.  */
   if (features & 0x02000000)
      result |= HWF_INTEL_AESNI;
 #endif /*ENABLE_AESNI_SUPPORT*/
+#if defined(ENABLE_AVX_SUPPORT) || defined(ENABLE_AVX2_SUPPORT)
+  /* Test bit 27 for OSXSAVE (required for AVX/AVX2).  */
+  if (features & 0x08000000)
+    {
+      /* Check that OS has enabled both XMM and YMM state support.  */
+      if ((get_xgetbv() & 0x6) == 0x6)
+        os_supports_avx_avx2_registers = 1;
+    }
+#endif
 #ifdef ENABLE_AVX_SUPPORT
   /* Test bit 28 for AVX.  */
   if (features & 0x10000000)
-     result |= HWF_INTEL_AVX;
+    if (os_supports_avx_avx2_registers)
+      result |= HWF_INTEL_AVX;
 #endif /*ENABLE_AVX_SUPPORT*/
 #ifdef ENABLE_DRNG_SUPPORT
   /* Test bit 30 for RDRAND.  */
@@ -223,12 +275,17 @@ detect_x86_gnuc (void)
    * Source: http://www.sandpile.org/x86/cpuid.htm  */
   if (max_cpuid_level >= 7 && (features & 0x00000001))
     {
-#ifdef ENABLE_AVX2_SUPPORT
       /* Get CPUID:7 contains further Intel feature flags. */
       get_cpuid(7, NULL, &features, NULL, NULL);
 
+      /* Test bit 8 for BMI2.  */
+      if (features & 0x00000100)
+          result |= HWF_INTEL_BMI2;
+
+#ifdef ENABLE_AVX2_SUPPORT
       /* Test bit 5 for AVX2.  */
       if (features & 0x00000020)
+        if (os_supports_avx_avx2_registers)
           result |= HWF_INTEL_AVX2;
 #endif /*ENABLE_AVX_SUPPORT*/
     }