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)
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* );
// 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}.
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);
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;
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);
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;
}
# 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. ---*/
/*---------------------------------------------------------------*/
|| 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());
}
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) );
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) );
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;
}
| 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);
{ 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 +
#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 */
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) ?
#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];
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;
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
if (!have_avx) {
have_f16c = False;
have_rdrand = False;
+ have_rdseed = False;
}
va = VexArchAMD64;
| (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);
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 \
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.
--- /dev/null
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#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;
+}
--- /dev/null
+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
+
--- /dev/null
+prog: rdseed
+prereq: test -x rdseed && ../../../tests/x86_amd64_features amd64-rdseed
+vgopts: -q
+
#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 */
);
}
{
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);
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 ) {
} 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())