#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include "g10lib.h"
#include "hwf-common.h"
#if !defined (__i386__) && !defined (__x86_64__)
# error Module build for wrong CPU.
#endif
#undef HAS_X86_CPUID
#if defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4 && defined (__GNUC__)
# define HAS_X86_CPUID 1
static int
is_cpuid_available(void)
{
int has_cpuid = 0;
asm volatile
("pushf\n\t"
"popl %%eax\n\t"
"movl %%eax, %%ecx\n\t"
"xorl $0x200000, %%eax\n\t"
"pushl %%eax\n\t"
"popf\n\t"
"pushf\n\t"
"popl %%eax\n\t"
"pushl %%ecx\n\t"
"popf\n\t"
"xorl %%eax, %%ecx\n\t"
"jz .Lno_cpuid%=\n\t"
"movl $1, %0\n"
".Lno_cpuid%=:\n\t"
: "+r" (has_cpuid)
:
: "%eax", "%ecx", "cc"
);
return has_cpuid;
}
static void
get_cpuid(unsigned int in, unsigned int *eax, unsigned int *ebx,
unsigned int *ecx, unsigned int *edx)
{
unsigned int regs[4];
asm volatile
("pushl %%ebx\n\t"
"movl %1, %%ebx\n\t"
"cpuid\n\t"
"movl %%ebx, %1\n\t"
"popl %%ebx\n\t"
: "=a" (regs[0]), "=D" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
: "0" (in), "1" (0), "2" (0), "3" (0)
: "cc"
);
if (eax)
*eax = regs[0];
if (ebx)
*ebx = regs[1];
if (ecx)
*ecx = regs[2];
if (edx)
*edx = regs[3];
}
#if defined(ENABLE_AVX_SUPPORT) || defined(ENABLE_AVX2_SUPPORT)
static unsigned int
get_xgetbv(void)
{
unsigned int t_eax, t_edx;
asm volatile
("xgetbv\n\t"
: "=a" (t_eax), "=d" (t_edx)
: "c" (0)
);
return t_eax;
}
#endif
#endif
#if defined (__x86_64__) && defined (__GNUC__)
# define HAS_X86_CPUID 1
static int
is_cpuid_available(void)
{
return 1;
}
static void
get_cpuid(unsigned int in, unsigned int *eax, unsigned int *ebx,
unsigned int *ecx, unsigned int *edx)
{
unsigned int regs[4];
asm volatile
("cpuid\n\t"
: "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
: "0" (in), "1" (0), "2" (0), "3" (0)
: "cc"
);
if (eax)
*eax = regs[0];
if (ebx)
*ebx = regs[1];
if (ecx)
*ecx = regs[2];
if (edx)
*edx = regs[3];
}
#if defined(ENABLE_AVX_SUPPORT) || defined(ENABLE_AVX2_SUPPORT)
static unsigned int
get_xgetbv(void)
{
unsigned int t_eax, t_edx;
asm volatile
("xgetbv\n\t"
: "=a" (t_eax), "=d" (t_edx)
: "c" (0)
);
return t_eax;
}
#endif
#endif
#ifdef HAS_X86_CPUID
static unsigned int
detect_x86_gnuc (void)
{
union
{
char c[12+1];
unsigned int ui[3];
} vendor_id;
unsigned int features, features2;
unsigned int os_supports_avx_avx2_registers = 0;
unsigned int max_cpuid_level;
unsigned int fms, family, model;
unsigned int result = 0;
unsigned int avoid_vpgather = 0;
(void)os_supports_avx_avx2_registers;
if (!is_cpuid_available())
return 0;
get_cpuid(0, &max_cpuid_level, &vendor_id.ui[0], &vendor_id.ui[2],
&vendor_id.ui[1]);
vendor_id.c[12] = 0;
if (0)
;
#ifdef ENABLE_PADLOCK_SUPPORT
else if (!strcmp (vendor_id.c, "CentaurHauls"))
{
get_cpuid(0xC0000000, &features, NULL, NULL, NULL);
if (features > 0xC0000000)
{
get_cpuid(0xC0000001, NULL, NULL, NULL, &features);
if ((features & 0x0C) == 0x0C)
result |= HWF_PADLOCK_RNG;
if ((features & 0xC0) == 0xC0)
result |= HWF_PADLOCK_AES;
if ((features & 0xC00) == 0xC00)
result |= HWF_PADLOCK_SHA;
if ((features & 0x3000) == 0x3000)
result |= HWF_PADLOCK_MMUL;
}
}
#endif
else if (!strcmp (vendor_id.c, "GenuineIntel"))
{
result |= HWF_INTEL_CPU;
}
else if (!strcmp (vendor_id.c, "AuthenticAMD"))
{
}
get_cpuid(1, &fms, NULL, &features, &features2);
family = ((fms & 0xf00) >> 8) + ((fms & 0xff00000) >> 20);
model = ((fms & 0xf0) >> 4) + ((fms & 0xf0000) >> 12);
if ((result & HWF_INTEL_CPU) && family == 6)
{
switch (model)
{
case 0x2A:
case 0x2D:
case 0x3A:
case 0x3C:
case 0x3F:
case 0x45:
case 0x46:
case 0x3D:
case 0x4F:
case 0x56:
case 0x47:
case 0x4E:
case 0x5E:
case 0x8E:
case 0x9E:
case 0x55:
case 0x66:
result |= HWF_INTEL_FAST_SHLD;
break;
}
switch (model)
{
case 0x3C:
case 0x3F:
case 0x45:
case 0x46:
avoid_vpgather |= 1;
break;
}
}
else
{
avoid_vpgather |= 1;
}
#ifdef ENABLE_PCLMUL_SUPPORT
if (features & 0x00000002)
result |= HWF_INTEL_PCLMUL;
#endif
if (features & 0x00000200)
result |= HWF_INTEL_SSSE3;
if (features & 0x00080000)
result |= HWF_INTEL_SSE4_1;
#ifdef ENABLE_AESNI_SUPPORT
if (features & 0x02000000)
result |= HWF_INTEL_AESNI;
#endif
#if defined(ENABLE_AVX_SUPPORT) || defined(ENABLE_AVX2_SUPPORT)
if (features & 0x08000000)
{
if ((get_xgetbv() & 0x6) == 0x6)
os_supports_avx_avx2_registers = 1;
}
#endif
#ifdef ENABLE_AVX_SUPPORT
if (features & 0x10000000)
if (os_supports_avx_avx2_registers)
result |= HWF_INTEL_AVX;
#endif
#ifdef ENABLE_DRNG_SUPPORT
if (features & 0x40000000)
result |= HWF_INTEL_RDRAND;
#endif
if (features2 & 0x00000010)
result |= HWF_INTEL_RDTSC;
if (max_cpuid_level >= 7 && (features & 0x00000001))
{
get_cpuid(7, NULL, &features, NULL, NULL);
if (features & 0x00000100)
result |= HWF_INTEL_BMI2;
#ifdef ENABLE_AVX2_SUPPORT
if (features & 0x00000020)
if (os_supports_avx_avx2_registers)
result |= HWF_INTEL_AVX2;
if ((result & HWF_INTEL_AVX2) && !avoid_vpgather)
result |= HWF_INTEL_FAST_VPGATHER;
#endif
}
return result;
}
#endif
unsigned int
_gcry_hwf_detect_x86 (void)
{
#if defined (HAS_X86_CPUID)
return detect_x86_gnuc ();
#else
return 0;
#endif
}