From 17d2989a9e9bb07d4b5a2b9607e584845c494b1b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alexandra=20H=C3=A1jkov=C3=A1?= Date: Fri, 31 Jul 2020 13:19:16 +0200 Subject: [PATCH] amd64: Implement RDSEED This commit implements amd64 RDSEED instruction, on hosts that have it and adds the new test case - none/tests/amd64/rdseed based on the existing rdrand support. https://bugs.kde.org/show_bug.cgi?id=424298 --- NEWS | 1 + VEX/priv/guest_amd64_defs.h | 11 ++- VEX/priv/guest_amd64_helpers.c | 46 ++++++++++-- VEX/priv/guest_amd64_toIR.c | 49 ++++++++---- VEX/priv/host_amd64_isel.c | 3 +- VEX/priv/main_main.c | 1 + VEX/pub/libvex.h | 1 + configure.ac | 20 +++++ coregrind/m_machine.c | 9 ++- none/tests/amd64/Makefile.am | 4 + none/tests/amd64/rdseed.c | 116 +++++++++++++++++++++++++++++ none/tests/amd64/rdseed.stderr.exp | 0 none/tests/amd64/rdseed.stdout.exp | 33 ++++++++ none/tests/amd64/rdseed.vgtest | 4 + tests/x86_amd64_features.c | 27 +++++-- 15 files changed, 290 insertions(+), 35 deletions(-) create mode 100644 none/tests/amd64/rdseed.c create mode 100644 none/tests/amd64/rdseed.stderr.exp create mode 100644 none/tests/amd64/rdseed.stdout.exp create mode 100644 none/tests/amd64/rdseed.vgtest diff --git a/NEWS b/NEWS index 248d90892a..ce27f392ec 100644 --- a/NEWS +++ b/NEWS @@ -47,6 +47,7 @@ n-i-bz helgrind: If hg_cli__realloc fails, return NULL. 422174 unhandled instruction bytes: 0x48 0xE9 (REX prefixed JMP instruction) 422623 epoll_ctl warns for uninitialized padding on non-amd64 64bit arches 423021 PPC: Add missing ISA 3.0 documentation link and HWCAPS test. +424298 amd64: Implement RDSEED Release 3.16.1 (?? June 2020) diff --git a/VEX/priv/guest_amd64_defs.h b/VEX/priv/guest_amd64_defs.h index 54672dc225..f9a9a90975 100644 --- a/VEX/priv/guest_amd64_defs.h +++ b/VEX/priv/guest_amd64_defs.h @@ -164,9 +164,13 @@ extern void amd64g_dirtyhelper_CPUID_sse3_and_cx16 ( VexGuestAMD64State* st ); extern void amd64g_dirtyhelper_CPUID_sse42_and_cx16 ( VexGuestAMD64State* st ); extern void amd64g_dirtyhelper_CPUID_avx_and_cx16 ( VexGuestAMD64State* st, ULong hasF16C, - ULong hasRDRAND ); -extern void amd64g_dirtyhelper_CPUID_avx2 ( VexGuestAMD64State* st, - ULong hasF16C, ULong hasRDRAND ); + ULong hasRDRAND, + ULong hasRDSEED ); + +extern void amd64g_dirtyhelper_CPUID_avx2 ( VexGuestAMD64State* st, + ULong hasF16C, ULong hasRDRAND, + ULong hasRDSEED ); + extern void amd64g_dirtyhelper_FINIT ( VexGuestAMD64State* ); @@ -194,6 +198,7 @@ extern void amd64g_dirtyhelper_SxDT ( void* address, // resulting C flag value in bit 32. extern ULong amd64g_dirtyhelper_RDRAND ( void ); +extern ULong amd64g_dirtyhelper_RDSEED ( void ); /* Helps with PCMP{I,E}STR{I,M}. diff --git a/VEX/priv/guest_amd64_helpers.c b/VEX/priv/guest_amd64_helpers.c index 182bae0bc5..ab7b64bcc1 100644 --- a/VEX/priv/guest_amd64_helpers.c +++ b/VEX/priv/guest_amd64_helpers.c @@ -3142,7 +3142,8 @@ void amd64g_dirtyhelper_CPUID_sse42_and_cx16 ( VexGuestAMD64State* st ) power management: */ void amd64g_dirtyhelper_CPUID_avx_and_cx16 ( VexGuestAMD64State* st, - ULong hasF16C, ULong hasRDRAND ) + ULong hasF16C, ULong hasRDRAND, + ULong hasRDSEED ) { vassert((hasF16C >> 1) == 0ULL); vassert((hasRDRAND >> 1) == 0ULL); @@ -3194,9 +3195,14 @@ void amd64g_dirtyhelper_CPUID_avx_and_cx16 ( VexGuestAMD64State* st, case 0x00000006: SET_ABCD(0x00000077, 0x00000002, 0x00000009, 0x00000000); break; - case 0x00000007: - SET_ABCD(0x00000000, 0x00000800, 0x00000000, 0x00000000); + case 0x00000007: { + UInt ebx_extra = 0; + if (old_ecx == 0) + ebx_extra = hasRDSEED ? (1U << 18) : 0; + SET_ABCD(0x00000000, 0x00000800 | ebx_extra, 0x00000000, + 0x00000000); break; + } case 0x00000008: SET_ABCD(0x00000000, 0x00000000, 0x00000000, 0x00000000); break; @@ -3320,7 +3326,8 @@ void amd64g_dirtyhelper_CPUID_avx_and_cx16 ( VexGuestAMD64State* st, power management: */ void amd64g_dirtyhelper_CPUID_avx2 ( VexGuestAMD64State* st, - ULong hasF16C, ULong hasRDRAND ) + ULong hasF16C, ULong hasRDRAND, + ULong hasRDSEED ) { vassert((hasF16C >> 1) == 0ULL); vassert((hasRDRAND >> 1) == 0ULL); @@ -3375,8 +3382,12 @@ void amd64g_dirtyhelper_CPUID_avx2 ( VexGuestAMD64State* st, case 0x00000007: switch (old_ecx) { /* Don't advertise FSGSBASE support, bit 0 in EBX. */ - case 0x00000000: SET_ABCD(0x00000000, 0x000027aa, - 0x00000000, 0x00000000); break; + + case 0x00000000: { + UInt ebx_extra = hasRDSEED ? (1U << 18) : 0; + SET_ABCD(0x00000000, 0x000027aa | ebx_extra, + 0x00000000, 0x00000000); break; + } default: SET_ABCD(0x00000000, 0x00000000, 0x00000000, 0x00000000); break; } @@ -3781,6 +3792,29 @@ ULong amd64g_dirtyhelper_RDRAND ( void ) { # endif } +ULong amd64g_dirtyhelper_RDSEED ( void ) { +# if defined(__x86_64__) + ULong res = 0; + ULong cflag = 0; + __asm__ __volatile__( + "movq $0, %%r11 ; " + "movq $0, %%r12 ; " + "rdseed %%r11d ; " + "setc %%r12b ; " + "movq %%r11, %0 ; " + "movq %%r12, %1" + : "=r"(res), "=r"(cflag) : : "r11", "r12" + ); + res &= 0xFFFFFFFFULL; + cflag &= 1ULL; + return (cflag << 32) | res; +# else + /* There's nothing we can sensibly do. Return a value denoting + "I succeeded, and the random bits are all zero" :-/ */ + return 1ULL << 32; +# endif +} + /*---------------------------------------------------------------*/ /*--- Helpers for MMX/SSE/SSE2. ---*/ /*---------------------------------------------------------------*/ diff --git a/VEX/priv/guest_amd64_toIR.c b/VEX/priv/guest_amd64_toIR.c index 7888132ebd..2faca7d03f 100644 --- a/VEX/priv/guest_amd64_toIR.c +++ b/VEX/priv/guest_amd64_toIR.c @@ -21989,9 +21989,11 @@ Long dis_ESC_0F ( || fAddr == &amd64g_dirtyhelper_CPUID_avx_and_cx16) { Bool hasF16C = (archinfo->hwcaps & VEX_HWCAPS_AMD64_F16C) != 0; Bool hasRDRAND = (archinfo->hwcaps & VEX_HWCAPS_AMD64_RDRAND) != 0; - args = mkIRExprVec_3(IRExpr_GSPTR(), + Bool hasRDSEED = (archinfo->hwcaps & VEX_HWCAPS_AMD64_RDSEED) != 0; + args = mkIRExprVec_4(IRExpr_GSPTR(), mkIRExpr_HWord(hasF16C ? 1 : 0), - mkIRExpr_HWord(hasRDRAND ? 1 : 0)); + mkIRExpr_HWord(hasRDRAND ? 1 : 0), + mkIRExpr_HWord(hasRDSEED ? 1 : 0)); } else { args = mkIRExprVec_1(IRExpr_GSPTR()); } @@ -22344,20 +22346,28 @@ Long dis_ESC_0F ( return delta; } // if (isValidCMPXCHG) - /* 0F C7 /6 no-F2-or-F3 = RDRAND */ - if (gregLO3ofRM(modrm) == 6/*RDRAND*/ - && (archinfo->hwcaps & VEX_HWCAPS_AMD64_RDRAND) + /* 0F C7 /6 no-F2-or-F3 = RDRAND, 0F C7 /7 = RDSEED */ + int insn = gregLO3ofRM(modrm); + if (((insn == 6 && (archinfo->hwcaps & VEX_HWCAPS_AMD64_RDRAND)) + || (insn == 7 && (archinfo->hwcaps & VEX_HWCAPS_AMD64_RDSEED))) && epartIsReg(modrm) && haveNoF2noF3(pfx) && (sz == 8 || sz == 4 || sz == 2)) { + delta++; // move past modrm IRType ty = szToITy(sz); // Pull a first 32 bits of randomness, plus C flag, out of the host. IRTemp pairLO = newTemp(Ity_I64); - IRDirty* dLO - = unsafeIRDirty_1_N(pairLO, 0/*regparms*/, - "amd64g_dirtyhelper_RDRAND", - &amd64g_dirtyhelper_RDRAND, mkIRExprVec_0()); + IRDirty* dLO; + if (insn == 6) /* RDRAND */ + dLO = unsafeIRDirty_1_N(pairLO, 0/*regparms*/, + "amd64g_dirtyhelper_RDRAND", + &amd64g_dirtyhelper_RDRAND, mkIRExprVec_0()); + else /* RDSEED */ + dLO = unsafeIRDirty_1_N(pairLO, 0/*regparms*/, + "amd64g_dirtyhelper_RDSEED", + &amd64g_dirtyhelper_RDSEED, mkIRExprVec_0()); + // There are no guest state or memory effects to declare for |dLO|. stmt( IRStmt_Dirty(dLO) ); @@ -22373,10 +22383,16 @@ Long dis_ESC_0F ( if (ty == Ity_I64) { // Pull another 32 bits of randomness out of the host. IRTemp pairHI = newTemp(Ity_I64); - IRDirty* dHI - = unsafeIRDirty_1_N(pairHI, 0/*regparms*/, - "amd64g_dirtyhelper_RDRAND", - &amd64g_dirtyhelper_RDRAND, mkIRExprVec_0()); + IRDirty* dHI; + if (insn == 6) /* RDRAND */ + dHI = unsafeIRDirty_1_N(pairHI, 0/*regparms*/, + "amd64g_dirtyhelper_RDRAND", + &amd64g_dirtyhelper_RDRAND, mkIRExprVec_0()); + else /* RDSEED */ + dHI = unsafeIRDirty_1_N(pairHI, 0/*regparms*/, + "amd64g_dirtyhelper_RDSEED", + &amd64g_dirtyhelper_RDSEED, mkIRExprVec_0()); + // There are no guest state or memory effects to declare for |dHI|. stmt( IRStmt_Dirty(dHI) ); @@ -22420,7 +22436,12 @@ Long dis_ESC_0F ( stmt( IRStmt_Put( OFFB_CC_DEP2, mkU64(0) )); stmt( IRStmt_Put( OFFB_CC_NDEP, mkU64(0) )); - DIP("rdrand %s", nameIRegE(sz, pfx, modrm)); + if (insn == 6) { + DIP("rdrand %s", nameIRegE(sz, pfx, modrm)); + } else { + DIP("rdseed %s", nameIRegE(sz, pfx, modrm)); + } + return delta; } diff --git a/VEX/priv/host_amd64_isel.c b/VEX/priv/host_amd64_isel.c index c3cd61c1f3..3299c3df91 100644 --- a/VEX/priv/host_amd64_isel.c +++ b/VEX/priv/host_amd64_isel.c @@ -5303,7 +5303,8 @@ HInstrArray* iselSB_AMD64 ( const IRSB* bb, | VEX_HWCAPS_AMD64_BMI | VEX_HWCAPS_AMD64_AVX2 | VEX_HWCAPS_AMD64_F16C - | VEX_HWCAPS_AMD64_RDRAND))); + | VEX_HWCAPS_AMD64_RDRAND + | VEX_HWCAPS_AMD64_RDSEED))); /* Check that the host's endianness is as expected. */ vassert(archinfo_host->endness == VexEndnessLE); diff --git a/VEX/priv/main_main.c b/VEX/priv/main_main.c index 7a3cb75caf..3e788d5240 100644 --- a/VEX/priv/main_main.c +++ b/VEX/priv/main_main.c @@ -1648,6 +1648,7 @@ static const HChar* show_hwcaps_amd64 ( UInt hwcaps ) { VEX_HWCAPS_AMD64_BMI, "bmi" }, { VEX_HWCAPS_AMD64_F16C, "f16c" }, { VEX_HWCAPS_AMD64_RDRAND, "rdrand" }, + { VEX_HWCAPS_AMD64_RDSEED, "rdseed" }, }; /* Allocate a large enough buffer */ static HChar buf[sizeof prefix + diff --git a/VEX/pub/libvex.h b/VEX/pub/libvex.h index 6da26dcb5f..4eb97162fa 100644 --- a/VEX/pub/libvex.h +++ b/VEX/pub/libvex.h @@ -100,6 +100,7 @@ typedef #define VEX_HWCAPS_AMD64_AVX2 (1<<11) /* AVX2 instructions */ #define VEX_HWCAPS_AMD64_RDRAND (1<<13) /* RDRAND instructions */ #define VEX_HWCAPS_AMD64_F16C (1<<14) /* F16C instructions */ +#define VEX_HWCAPS_AMD64_RDSEED (1<<15) /* RDSEED instructions */ /* ppc32: baseline capability is integer only */ #define VEX_HWCAPS_PPC32_F (1<<8) /* basic (non-optional) FP */ diff --git a/configure.ac b/configure.ac index 49f2ba83de..1a89d05e5e 100755 --- a/configure.ac +++ b/configure.ac @@ -3034,6 +3034,26 @@ AC_MSG_RESULT([no]) AM_CONDITIONAL(BUILD_RDRAND_TESTS, test x$ac_have_as_rdrand = xyes) +# does the amd64 assembler understand the RDSEED instruction? +# Note, this doesn't generate a C-level symbol. It generates a +# automake-level symbol (BUILD_RDSEED_TESTS), used in test Makefile.am's +AC_MSG_CHECKING([if amd64 assembler knows the RDSEED instruction]) + +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ + do { + asm ("rdseed %r14"); + asm ("rdseed %r14d"); + asm ("rdseed %r14w"); + } while (0) +]])], [ +ac_have_as_rdseed=yes +AC_MSG_RESULT([yes]) +], [ +ac_have_as_rdseed=no +AC_MSG_RESULT([no]) +]) + +AM_CONDITIONAL(BUILD_RDSEED_TESTS, test x$ac_have_as_rdseed = xyes) # does the amd64 assembler understand the F16C instructions (VCVTPH2PS and # VCVTPS2PH) ? diff --git a/coregrind/m_machine.c b/coregrind/m_machine.c index 0fd1d21c78..5594923fdb 100644 --- a/coregrind/m_machine.c +++ b/coregrind/m_machine.c @@ -967,7 +967,7 @@ Bool VG_(machine_get_hwcaps)( void ) #elif defined(VGA_amd64) { Bool have_sse3, have_ssse3, have_cx8, have_cx16; Bool have_lzcnt, have_avx, have_bmi, have_avx2; - Bool have_rdtscp, have_rdrand, have_f16c; + Bool have_rdtscp, have_rdrand, have_f16c, have_rdseed; UInt eax, ebx, ecx, edx, max_basic, max_extended; ULong xgetbv_0 = 0; HChar vstr[13]; @@ -975,7 +975,7 @@ Bool VG_(machine_get_hwcaps)( void ) have_sse3 = have_ssse3 = have_cx8 = have_cx16 = have_lzcnt = have_avx = have_bmi = have_avx2 - = have_rdtscp = have_rdrand = have_f16c = False; + = have_rdtscp = have_rdrand = have_f16c = have_rdseed = False; eax = ebx = ecx = edx = max_basic = max_extended = 0; @@ -1079,6 +1079,7 @@ Bool VG_(machine_get_hwcaps)( void ) VG_(cpuid)(7, 0, &eax, &ebx, &ecx, &edx); have_bmi = (ebx & (1<<3)) != 0; /* True => have BMI1 */ have_avx2 = (ebx & (1<<5)) != 0; /* True => have AVX2 */ + have_rdseed = (ebx & (1<<18)) != 0; /* True => have RDSEED insns */ } /* Sanity check for RDRAND and F16C. These don't actually *need* AVX, but @@ -1087,6 +1088,7 @@ Bool VG_(machine_get_hwcaps)( void ) if (!have_avx) { have_f16c = False; have_rdrand = False; + have_rdseed = False; } va = VexArchAMD64; @@ -1100,7 +1102,8 @@ Bool VG_(machine_get_hwcaps)( void ) | (have_avx2 ? VEX_HWCAPS_AMD64_AVX2 : 0) | (have_rdtscp ? VEX_HWCAPS_AMD64_RDTSCP : 0) | (have_f16c ? VEX_HWCAPS_AMD64_F16C : 0) - | (have_rdrand ? VEX_HWCAPS_AMD64_RDRAND : 0); + | (have_rdrand ? VEX_HWCAPS_AMD64_RDRAND : 0) + | (have_rdseed ? VEX_HWCAPS_AMD64_RDSEED : 0); VG_(machine_get_cache_info)(&vai); diff --git a/none/tests/amd64/Makefile.am b/none/tests/amd64/Makefile.am index 88dc9b4da9..79d9e4f57d 100644 --- a/none/tests/amd64/Makefile.am +++ b/none/tests/amd64/Makefile.am @@ -82,6 +82,7 @@ EXTRA_DIST = \ pcmpxstrx64w.vgtest \ rcl-amd64.vgtest rcl-amd64.stdout.exp rcl-amd64.stderr.exp \ rdrand.vgtest rdrand.stdout.exp rdrand.stderr.exp \ + rdseed.vgtest rdseed.stdout.exp rdseed.stderr.exp \ redundantRexW.vgtest redundantRexW.stdout.exp \ redundantRexW.stderr.exp \ smc1.stderr.exp smc1.stdout.exp smc1.vgtest \ @@ -168,6 +169,9 @@ endif if BUILD_RDRAND_TESTS check_PROGRAMS += rdrand endif +if BUILD_RDSEED_TESTS + check_PROGRAMS += rdseed +endif # DDD: these need to be made to work on Darwin like the x86/ ones were. diff --git a/none/tests/amd64/rdseed.c b/none/tests/amd64/rdseed.c new file mode 100644 index 0000000000..784c4e96ea --- /dev/null +++ b/none/tests/amd64/rdseed.c @@ -0,0 +1,116 @@ + +#include +#include +#include +#include "tests/malloc.h" + +typedef unsigned char UChar; +typedef unsigned int UInt; +typedef unsigned long int UWord; +typedef unsigned long long int ULong; + +// What can we actually test here? The instructions take no input and +// produce output which is by definition totally random. So apart from +// not simply failing insn decode, there's nothing much to test. + +// Get 10 values of each size, and check that they are not all the same +// (otherwise something's obviously wrong). Now, statistically, it's +// highly unlikely that they are all the same. For 10 16 bit ints, the +// probability of them being all the same is (I'd guess) (2^-16) ^ (10-1), +// that is, 2^-144. + +ULong do_rdseed64 ( void ) +{ + while (1) { + ULong res = 0; + ULong cflag = 0; + __asm__ __volatile__( + "movabsq $0x5555555555555555, %%r11 ; " + "movq $0, %%r12 ; " + "rdseed %%r11 ; " + "setc %%r12b ; " + "movq %%r11, %0 ; " + "movq %%r12, %1" + : "=r"(res), "=r"(cflag) : : "r11", "r12" + ); + if (cflag == 1) + return res; + } + /*NOTREACHED*/ +} + +ULong do_rdseed32 ( void ) +{ + while (1) { + ULong res = 0; + ULong cflag = 0; + __asm__ __volatile__( + "movabsq $0x5555555555555555, %%r11 ; " + "movq $0, %%r12 ; " + "rdseed %%r11d ; " + "setc %%r12b ; " + "movq %%r11, %0 ; " + "movq %%r12, %1" + : "=r"(res), "=r"(cflag) : : "r11", "r12" + ); + if (cflag == 1) + return res; + } + /*NOTREACHED*/ +} + +ULong do_rdseed16 ( void ) +{ + while (1) { + ULong res = 0; + ULong cflag = 0; + __asm__ __volatile__( + "movabsq $0x5555555555555555, %%r11 ; " + "movq $0, %%r12 ; " + "rdseed %%r11w ; " + "setc %%r12b ; " + "movq %%r11, %0 ; " + "movq %%r12, %1" + : "=r"(res), "=r"(cflag) : : "r11", "r12" + ); + if (cflag == 1) + return res; + } + /*NOTREACHED*/ +} + +void do_test ( ULong(*fn)(void), + ULong mask + /* with 1s indicating the random bits in the result */ ) +{ + ULong arr[10]; + for (UInt i = 0; i < 10; i++) { + arr[i] = fn(); + } + + // They really should all be different (to an extremely high probabilty. + // See comment above. + int allSame = 1/*true*/; // really, a Bool + for (UInt i = 1; i < 10; i++) { + if (arr[i] != arr[0]) { + allSame = 0/*false*/; + break; + } + } + assert(!allSame); + + // The 0/32/48 leading bits of the result should have a particular value, + // depending on the insn. So print them, with the random part masked out. + for (UInt i = 0; i < 10; i++) { + printf("0x%016llx\n", arr[i] & ~mask); + } + printf("\n"); +} + +int main ( void ) +{ + do_test( do_rdseed64, 0xFFFFFFFFFFFFFFFFULL ); + do_test( do_rdseed32, 0x00000000FFFFFFFFULL ); + do_test( do_rdseed16, 0x000000000000FFFFULL ); + return 0; +} diff --git a/none/tests/amd64/rdseed.stderr.exp b/none/tests/amd64/rdseed.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/none/tests/amd64/rdseed.stdout.exp b/none/tests/amd64/rdseed.stdout.exp new file mode 100644 index 0000000000..dccfdc9134 --- /dev/null +++ b/none/tests/amd64/rdseed.stdout.exp @@ -0,0 +1,33 @@ +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 + +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 +0x0000000000000000 + +0x5555555555550000 +0x5555555555550000 +0x5555555555550000 +0x5555555555550000 +0x5555555555550000 +0x5555555555550000 +0x5555555555550000 +0x5555555555550000 +0x5555555555550000 +0x5555555555550000 + diff --git a/none/tests/amd64/rdseed.vgtest b/none/tests/amd64/rdseed.vgtest new file mode 100644 index 0000000000..2d2deff15e --- /dev/null +++ b/none/tests/amd64/rdseed.vgtest @@ -0,0 +1,4 @@ +prog: rdseed +prereq: test -x rdseed && ../../../tests/x86_amd64_features amd64-rdseed +vgopts: -q + diff --git a/tests/x86_amd64_features.c b/tests/x86_amd64_features.c index 3f6d3a7368..488f155b64 100644 --- a/tests/x86_amd64_features.c +++ b/tests/x86_amd64_features.c @@ -25,14 +25,14 @@ typedef int Bool; #if defined(VGA_x86) || defined(VGA_amd64) -static void cpuid ( unsigned int n, +static void cpuid ( unsigned int n, unsigned int m, unsigned int* a, unsigned int* b, unsigned int* c, unsigned int* d ) { __asm__ __volatile__ ( "cpuid" : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) /* output */ - : "0" (n) /* input */ + : "0" (n), "2" (m) /* input */ ); } @@ -40,7 +40,7 @@ static Bool vendorStringEquals ( char* str ) { char vstr[13]; unsigned int a, b, c, d; - cpuid(0, &a, &b, &c, &d); + cpuid(0, 0, &a, &b, &c, &d); memcpy(&vstr[0], &b, 4); memcpy(&vstr[4], &d, 4); memcpy(&vstr[8], &c, 4); @@ -70,7 +70,9 @@ static Bool have_xgetbv ( void ) static Bool go(char* cpu) { - unsigned int level = 0, cmask = 0, dmask = 0, a, b, c, d; + unsigned int level = 0, sublevel = 0; + unsigned int amask = 0, bmask = 0, cmask = 0, dmask = 0; + unsigned int a, b, c, d; Bool require_amd = False; Bool require_xgetbv = False; if ( strcmp( cpu, "x86-fpu" ) == 0 ) { @@ -135,22 +137,31 @@ static Bool go(char* cpu) } else if (strcmp (cpu, "amd64-rdrand" ) == 0) { level = 1; cmask = 1 << 30; + } else if (strcmp (cpu, "amd64-rdseed" ) == 0) { + level = 7; + bmask = 1 << 18; #endif } else { return UNRECOGNISED_FEATURE; } - assert( !(cmask != 0 && dmask != 0) ); - assert( !(cmask == 0 && dmask == 0) ); + assert( !(cmask != 0 && dmask != 0 && bmask != 0) ); + assert( !(cmask == 0 && dmask == 0 && bmask == 0) ); if (require_amd && !vendorStringEquals("AuthenticAMD")) return FEATURE_NOT_PRESENT; // regardless of what that feature actually is - cpuid( level & 0x80000000, &a, &b, &c, &d ); + cpuid( level & 0x80000000, 0, &a, &b, &c, &d ); if ( a >= level ) { - cpuid( level, &a, &b, &c, &d ); + cpuid( level, sublevel, &a, &b, &c, &d ); + + if (amask > 0 && (a & amask) == amask) + return FEATURE_PRESENT; + + if (bmask > 0 && (b & bmask) == bmask) + return FEATURE_PRESENT; if (dmask > 0 && (d & dmask) == dmask) { if (require_xgetbv && !have_xgetbv()) -- 2.47.3