diff --git a/VEX/priv/guest_arm64_defs.h b/VEX/priv/guest_arm64_defs.h
index 18b08b7ee..629ae6a58 100644
--- a/VEX/priv/guest_arm64_defs.h
+++ b/VEX/priv/guest_arm64_defs.h
@@ -132,6 +132,8 @@ extern ULong arm64g_dirtyhelper_MRS_ID_AA64MMFR1_EL1 ( void );
extern ULong arm64g_dirtyhelper_MRS_ID_AA64ISAR0_EL1 ( void );
extern ULong arm64g_dirtyhelper_MRS_ID_AA64ISAR1_EL1 ( void );
+extern void arm64g_dirtyhelper_RNDR ( /*OUT*/V128* res, ULong reseed );
+
extern void arm64g_dirtyhelper_PMULLQ ( /*OUT*/V128* res,
ULong arg1, ULong arg2 );
diff --git a/VEX/priv/guest_arm64_helpers.c b/VEX/priv/guest_arm64_helpers.c
index d6a03fe36..e5260b782 100644
--- a/VEX/priv/guest_arm64_helpers.c
+++ b/VEX/priv/guest_arm64_helpers.c
@@ -924,10 +924,11 @@ ULong arm64g_dirtyhelper_MRS_ID_AA64ISAR0_EL1 ( void )
// 9 - SM3 (unsupported)
// 10 - SM4 (unsupported)
// 11 - DP
+ // 15 - RNDR
- // 10
- // 109876543210
- w &= 0xF000F0FFFFFF;
+ // 1 10
+ // 5432109876543210
+ w &= 0xF000F000F0FFFFFFULL;
return w;
# else
@@ -953,6 +954,39 @@ ULong arm64g_dirtyhelper_MRS_ID_AA64ISAR1_EL1 ( void )
# endif
}
+/* CALLED FROM GENERATED CODE */
+/* DIRTY HELPER (non-referentially-transparent) */
+/* Read RNDR (reseed == 0) or RNDRRS (reseed != 0). Writes the random value
+ to res->w64[0] and the resulting NZCV, in bits 31:28, to res->w64[1]. With
+ a deterministic RNG installed, both read the calling thread's stream and
+ always succeed. Otherwise the host's register is read. On non-arm64
+ hosts, report failure. */
+void arm64g_dirtyhelper_RNDR ( /*OUT*/V128* res, ULong reseed )
+{
+ if (vex_random32) {
+ ULong lo = vex_random32();
+ ULong hi = vex_random32();
+ res->w64[0] = (hi << 32) | lo;
+ res->w64[1] = 0;
+ return;
+ }
+# if defined(__aarch64__) && !defined(__arm__)
+ ULong value = 0;
+ ULong nzcv = 0;
+ if (reseed)
+ __asm__ __volatile__("mrs %0, s3_3_c2_c4_1 ; mrs %1, nzcv"
+ : "=r"(value), "=r"(nzcv) : : "cc");
+ else
+ __asm__ __volatile__("mrs %0, s3_3_c2_c4_0 ; mrs %1, nzcv"
+ : "=r"(value), "=r"(nzcv) : : "cc");
+ res->w64[0] = value;
+ res->w64[1] = nzcv & (0xFULL << ARM64G_CC_SHIFT_V);
+# else
+ res->w64[0] = 0;
+ res->w64[1] = 1ULL << ARM64G_CC_SHIFT_Z;
+# endif
+}
+
void arm64g_dirtyhelper_PMULLQ ( /*OUT*/V128* res, ULong arg1, ULong arg2 )
{
/* This doesn't need to be a dirty helper, except for the fact that
diff --git a/VEX/priv/guest_arm64_toIR.c b/VEX/priv/guest_arm64_toIR.c
index 62927537c..ebab85b82 100644
--- a/VEX/priv/guest_arm64_toIR.c
+++ b/VEX/priv/guest_arm64_toIR.c
@@ -7978,6 +7978,34 @@ Bool dis_ARM64_branch_etc(/*MB_OUT*/DisResult* dres, UInt insn,
DIP("mrs %s, cntfrq_el0\n", nameIReg64orZR(tt));
return True;
}
+ /* ---- Cases for RNDR and RNDRRS (FEAT_RNG) ----
+ D5 3B 24 000 Rt MRS Xt, rndr
+ D5 3B 24 001 Rt MRS Xt, rndrrs
+ Decoded when the host has FEAT_RNG, or when a deterministic RNG is
+ installed, since then the host register is never read. On success
+ NZCV is 0000; on failure Xt is 0 and NZCV is 0100.
+ */
+ if ((INSN(31,0) & 0xFFFFFFC0) == 0xD53B2400
+ && ((archinfo->hwcaps & VEX_HWCAPS_ARM64_RNG) || vex_random32)) {
+ UInt tt = INSN(4,0);
+ Bool reseed = INSN(5,5) == 1;
+ IRTemp pair = newTemp(Ity_V128);
+ IRDirty* d = unsafeIRDirty_1_N(
+ pair,
+ 0/*regparms*/,
+ "arm64g_dirtyhelper_RNDR",
+ &arm64g_dirtyhelper_RNDR,
+ mkIRExprVec_2(IRExpr_VECRET(),
+ mkU64(reseed ? 1 : 0))
+ );
+ stmt( IRStmt_Dirty(d) );
+ IRTemp nzcv = newTemp(Ity_I64);
+ assign(nzcv, unop(Iop_V128HIto64, mkexpr(pair)));
+ putIReg64orZR(tt, unop(Iop_V128to64, mkexpr(pair)));
+ setFlags_COPY(nzcv);
+ DIP("mrs %s, %s\n", nameIReg64orZR(tt), reseed ? "rndrrs" : "rndr");
+ return True;
+ }
/* ------------------ IC_IVAU ------------------ */
/* D5 0B 75 001 Rt ic ivau, rT
diff --git a/VEX/priv/main_main.c b/VEX/priv/main_main.c
index d5eb5e153..646dce97b 100644
--- a/VEX/priv/main_main.c
+++ b/VEX/priv/main_main.c
@@ -1913,6 +1913,7 @@ static const HChar* show_hwcaps_arm64 ( UInt hwcaps )
{ VEX_HWCAPS_ARM64_BF16, "bf16" },
{ VEX_HWCAPS_ARM64_FP16, "fp16" },
{ VEX_HWCAPS_ARM64_VFP16, "vfp16" },
+ { VEX_HWCAPS_ARM64_RNG, "rng" },
};
static HChar buf[sizeof prefix + // '\0'
diff --git a/VEX/pub/libvex.h b/VEX/pub/libvex.h
index f50971159..1149038d0 100644
--- a/VEX/pub/libvex.h
+++ b/VEX/pub/libvex.h
@@ -242,6 +242,7 @@ typedef
#define VEX_HWCAPS_ARM64_BF16 (1 << 13)
#define VEX_HWCAPS_ARM64_FP16 (1 << 14)
#define VEX_HWCAPS_ARM64_VFP16 (1 << 15)
+#define VEX_HWCAPS_ARM64_RNG (1 << 16)
/* MIPS baseline capability */
/* Assigned Company values for bits 23:16 of the PRId Register
diff --git a/coregrind/m_initimg/initimg-linux.c b/coregrind/m_initimg/initimg-linux.c
index ec630d917..faa0a2d51 100644
--- a/coregrind/m_initimg/initimg-linux.c
+++ b/coregrind/m_initimg/initimg-linux.c
@@ -894,6 +894,14 @@ Addr setup_client_stack( void* init_sp,
break;
# endif
+# if defined(VGP_arm64_linux)
+ case AT_HWCAP2:
+ /* Limit AT_HWCAP2 to the features VEX supports. RNDR and RNDRRS
+ are decoded whenever the host has FEAT_RNG. */
+ auxv->u.a_val &= VKI_HWCAP2_RNG;
+ break;
+# endif
+
case AT_ICACHEBSIZE:
case AT_DCACHEBSIZE:
case AT_UCACHEBSIZE:
diff --git a/coregrind/m_machine.c b/coregrind/m_machine.c
index c93eaf98a..2c21fbeb3 100644
--- a/coregrind/m_machine.c
+++ b/coregrind/m_machine.c
@@ -1856,11 +1856,11 @@ Bool VG_(machine_get_hwcaps)( void )
Bool have_fhm, have_dp, have_sm4, have_sm3, have_sha3, have_rdm;
Bool have_atomics, have_i8mm, have_bf16, have_dpbcvap, have_dpbcvadp;
- Bool have_vfp16, have_fp16;
+ Bool have_vfp16, have_fp16, have_rng;
have_fhm = have_dp = have_sm4 = have_sm3 = have_sha3 = have_rdm
= have_atomics = have_i8mm = have_bf16 = have_dpbcvap
- = have_dpbcvadp = have_vfp16 = have_fp16 = False;
+ = have_dpbcvadp = have_vfp16 = have_fp16 = have_rng = False;
/* Some baseline v8.0 kernels do not allow reads of these registers. Use
* the same SIGILL handling algorithm as other architectures for such
@@ -1938,6 +1938,7 @@ Bool VG_(machine_get_hwcaps)( void )
return True;
/* ID_AA64ISAR0_EL1 Instruction set attribute register 0 fields */
+ #define ID_AA64ISAR0_RNDR_SHIFT 60
#define ID_AA64ISAR0_FHM_SHIFT 48
#define ID_AA64ISAR0_DP_SHIFT 44
#define ID_AA64ISAR0_SM4_SHIFT 40
@@ -1946,6 +1947,7 @@ Bool VG_(machine_get_hwcaps)( void )
#define ID_AA64ISAR0_RDM_SHIFT 28
#define ID_AA64ISAR0_ATOMICS_SHIFT 20
/* Field values */
+ #define ID_AA64ISAR0_RNDR_SUPPORTED 0x1
#define ID_AA64ISAR0_FHM_SUPPORTED 0x1
#define ID_AA64ISAR0_DP_SUPPORTED 0x1
#define ID_AA64ISAR0_SM4_SUPPORTED 0x1
@@ -2032,6 +2034,12 @@ Bool VG_(machine_get_hwcaps)( void )
get_ftr(ID_AA64ISAR0_EL1, ID_AA64ISAR0_ATOMICS_SHIFT,
ID_AA64ISAR0_ATOMICS_SUPPORTED, have_atomics);
+ /* RNDR indicates support for the RNDR and RNDRRS registers.
+ * Optional for v8.5.
+ */
+ get_ftr(ID_AA64ISAR0_EL1, ID_AA64ISAR0_RNDR_SHIFT,
+ ID_AA64ISAR0_RNDR_SUPPORTED, have_rng);
+
/* Read ID_AA64ISAR1_EL1 attributes */
/* I8MM indicates support for SMMLA, SUDOT, UMMLA, USMMLA, and USDOT
@@ -2086,6 +2094,7 @@ Bool VG_(machine_get_hwcaps)( void )
if (have_bf16) vai.hwcaps |= VEX_HWCAPS_ARM64_BF16;
if (have_fp16) vai.hwcaps |= VEX_HWCAPS_ARM64_FP16;
if (have_vfp16) vai.hwcaps |= VEX_HWCAPS_ARM64_VFP16;
+ if (have_rng) vai.hwcaps |= VEX_HWCAPS_ARM64_RNG;
#undef get_cpu_ftr
#undef get_ftr
diff --git a/coregrind/m_main.c b/coregrind/m_main.c
index 1e11f998f..24ce30a43 100644
--- a/coregrind/m_main.c
+++ b/coregrind/m_main.c
@@ -108,7 +108,7 @@ static void usage_NORETURN ( int need_help )
" to run despite their file capabilities, which\n"
" they do not get under Valgrind\n"
" --deterministic-rng=no|yes serve AT_RANDOM, getrandom() and /dev/urandom\n"
-" and RDRAND/RDSEED from a fixed-seed PRNG [no]\n"
+" and hardware RNG insns from a fixed-seed PRNG [no]\n"
" --child-silent-after-fork=no|yes omit child output between fork & exec? [no]\n"
" --vgdb=no|yes|full activate gdbserver? [yes]\n"
" full is slower but provides precise watchpoint/step\n"
diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml
index ff2292256..e7b97474b 100644
--- a/docs/xml/manual-core.xml
+++ b/docs/xml/manual-core.xml
@@ -814,7 +814,13 @@ in most cases. We group the available options by rough categories.
RDRAND and
RDSEED. On hosts that support them,
programs that execute them anyway draw from the calling thread's PRNG
- stream, which always reports success.
+ stream, which always reports success. On arm64,
+ RNDR and
+ RNDRRS draw from the same stream and
+ always report success, also on hosts without FEAT_RNG.
+ HWCAP2_RNG and
+ ID_AA64ISAR0_EL1.RNDR still report
+ them only when the host supports them.
For nonempty random-device
preadv2 reads, the supported flags are
RWF_HIPRI,
diff --git a/include/vki/vki-arm64-linux.h b/include/vki/vki-arm64-linux.h
index f2de1aca0..fe08b92e2 100644
--- a/include/vki/vki-arm64-linux.h
+++ b/include/vki/vki-arm64-linux.h
@@ -730,6 +730,8 @@ struct vki_shminfo64 {
#define VKI_HWCAP_ILRCPC (1 << 26)
#define VKI_HWCAP_FLAGM (1 << 27)
+#define VKI_HWCAP2_RNG (1 << 16)
+
//----------------------------------------------------------------------
// And that's it!
//----------------------------------------------------------------------
diff --git a/none/tests/arm64/Makefile.am b/none/tests/arm64/Makefile.am
index 4fa101d77..4b0d580c4 100644
--- a/none/tests/arm64/Makefile.am
+++ b/none/tests/arm64/Makefile.am
@@ -9,6 +9,8 @@ EXTRA_DIST = \
crc32.stdout.exp crc32.stderr.exp crc32.vgtest \
cvtf_imm.stdout.exp cvtf_imm.stderr.exp cvtf_imm.vgtest \
dc_cvax.vgtest dc_cvax.stderr.exp \
+ deterministic_rng.stdout.exp deterministic_rng.stderr.exp \
+ deterministic_rng.vgtest \
fcvta_s_u.vgtest fcvta_s_u.stderr.exp \
fp_and_simd.stdout.exp fp_and_simd.stderr.exp fp_and_simd.vgtest \
frinta_frintn.stderr.exp frinta_frintn.vgtest \
@@ -33,6 +35,7 @@ check_PROGRAMS = \
bug489338 \
cvtf_imm \
dc_cvax \
+ deterministic_rng \
fp_and_simd \
integer \
memory_test \
@@ -92,6 +95,7 @@ fp_and_simd_v82_SOURCES = fp_and_simd_v82.c simd.h simd.c
sha512_v82_SOURCES = sha512_v82.c simd.h simd.c
bug489338_LDADD = -lm
+deterministic_rng_LDADD = -lpthread
fp_and_simd_LDADD = -lm
simd_v81_LDADD = -lm
fp_and_simd_v82_LDADD = -lm
diff --git a/none/tests/arm64/deterministic_rng.c b/none/tests/arm64/deterministic_rng.c
new file mode 100644
index 000000000..55dfa6980
--- /dev/null
+++ b/none/tests/arm64/deterministic_rng.c
@@ -0,0 +1,46 @@
+#include
+#include
+#include
+#include
+
+enum {
+ NZCV_ALL = 0xf0000000,
+ NZCV_SUCCESS = 0,
+ DRAW_COUNT = 2
+};
+
+#define REGISTER_SENTINEL UINT64_C(0x5555555555555555)
+
+/* Use the generic register names so the test assembles without +rng. */
+#define DRAW(name, sysreg) \
+static void* name(void* unused) \
+{ \
+ (void)unused; \
+ for (int i = 0; i < DRAW_COUNT; i++) { \
+ uint64_t value = REGISTER_SENTINEL; \
+ uint64_t nzcv; \
+ __asm__ volatile("msr nzcv, %2; mrs %0, " sysreg "; mrs %1, nzcv" \
+ : "=&r"(value), "=&r"(nzcv) \
+ : "r"((uint64_t)NZCV_ALL) : "cc"); \
+ assert((nzcv & NZCV_ALL) == NZCV_SUCCESS); \
+ printf(#name ": %016llx\n", (unsigned long long)value); \
+ } \
+ return NULL; \
+}
+
+DRAW(rndr, "s3_3_c2_c4_0")
+DRAW(rndrrs, "s3_3_c2_c4_1")
+
+int main(void)
+{
+ void* (*draws[])(void*) = { rndr, rndrrs };
+ /* Fresh threads keep expected bytes independent of libc startup draws. */
+ for (unsigned int i = 0; i < sizeof(draws) / sizeof(draws[0]); i++) {
+ pthread_t thread;
+ int err = pthread_create(&thread, NULL, draws[i], NULL);
+ assert(err == 0);
+ err = pthread_join(thread, NULL);
+ assert(err == 0);
+ }
+ return 0;
+}
diff --git a/none/tests/arm64/deterministic_rng.stderr.exp b/none/tests/arm64/deterministic_rng.stderr.exp
new file mode 100644
index 000000000..e69de29bb
diff --git a/none/tests/arm64/deterministic_rng.stdout.exp b/none/tests/arm64/deterministic_rng.stdout.exp
new file mode 100644
index 000000000..63d772c7f
--- /dev/null
+++ b/none/tests/arm64/deterministic_rng.stdout.exp
@@ -0,0 +1,4 @@
+rndr: 5ef4b5199914d78e
+rndr: 7fb4c3bf72a5486f
+rndrrs: b8e63bec131a7a6b
+rndrrs: f1aa70fe9100862e
diff --git a/none/tests/arm64/deterministic_rng.vgtest b/none/tests/arm64/deterministic_rng.vgtest
new file mode 100644
index 000000000..855fb4d9d
--- /dev/null
+++ b/none/tests/arm64/deterministic_rng.vgtest
@@ -0,0 +1,3 @@
+prog: deterministic_rng
+prereq: test -x deterministic_rng
+vgopts: -q --deterministic-rng=yes
diff --git a/none/tests/cmdline1.stdout.exp b/none/tests/cmdline1.stdout.exp
index 3a8e14d51..112905132 100644
--- a/none/tests/cmdline1.stdout.exp
+++ b/none/tests/cmdline1.stdout.exp
@@ -23,7 +23,7 @@ usage: valgrind [options] prog-and-args
to run despite their file capabilities, which
they do not get under Valgrind
--deterministic-rng=no|yes serve AT_RANDOM, getrandom() and /dev/urandom
- and RDRAND/RDSEED from a fixed-seed PRNG [no]
+ and hardware RNG insns from a fixed-seed PRNG [no]
--child-silent-after-fork=no|yes omit child output between fork & exec? [no]
--vgdb=no|yes|full activate gdbserver? [yes]
full is slower but provides precise watchpoint/step
diff --git a/none/tests/cmdline2.stdout.exp b/none/tests/cmdline2.stdout.exp
index 58d77335b..926a2513a 100644
--- a/none/tests/cmdline2.stdout.exp
+++ b/none/tests/cmdline2.stdout.exp
@@ -23,7 +23,7 @@ usage: valgrind [options] prog-and-args
to run despite their file capabilities, which
they do not get under Valgrind
--deterministic-rng=no|yes serve AT_RANDOM, getrandom() and /dev/urandom
- and RDRAND/RDSEED from a fixed-seed PRNG [no]
+ and hardware RNG insns from a fixed-seed PRNG [no]
--child-silent-after-fork=no|yes omit child output between fork & exec? [no]
--vgdb=no|yes|full activate gdbserver? [yes]
full is slower but provides precise watchpoint/step