Skip to content

Commit aee5404

Browse files
committed
Add support for RNDR/RNDRRS for aarch64 on Linux
This checks whether the ARMv8.5 extensions RNDR and RNDRRS are available and uses them for random entropy purposes. They are functionally identical to the x86 RDRAND/RDSEED extensions and are used in a similar manner.
1 parent adc41cf commit aee5404

File tree

1 file changed

+73
-0
lines changed

1 file changed

+73
-0
lines changed

src/random.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
#ifdef HAVE_SYSCTL_ARND
4141
#include <sys/sysctl.h>
4242
#endif
43+
#if defined(HAVE_STRONG_GETAUXVAL) && defined(__aarch64__)
44+
#include <sys/auxv.h>
45+
#endif
4346

4447
[[noreturn]] static void RandFailure()
4548
{
@@ -175,6 +178,62 @@ static uint64_t GetRdSeed() noexcept
175178
#endif
176179
}
177180

181+
#elif defined(__aarch64__) && defined(HWCAP2_RNG)
182+
183+
static bool g_rndr_supported = false;
184+
185+
static void InitHardwareRand()
186+
{
187+
if (getauxval(AT_HWCAP2) & HWCAP2_RNG) {
188+
g_rndr_supported = true;
189+
}
190+
}
191+
192+
static void ReportHardwareRand()
193+
{
194+
// This must be done in a separate function, as InitHardwareRand() may be indirectly called
195+
// from global constructors, before logging is initialized.
196+
if (g_rndr_supported) {
197+
LogPrintf("Using RNDR and RNDRRS as additional entropy sources\n");
198+
}
199+
}
200+
201+
/** Read 64 bits of entropy using rndr.
202+
*
203+
* Must only be called when RNDR is supported.
204+
*/
205+
static uint64_t GetRNDR() noexcept
206+
{
207+
uint8_t ok;
208+
uint64_t r1;
209+
do {
210+
// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/RNDR--Random-Number
211+
__asm__ volatile("mrs %0, s3_3_c2_c4_0; cset %w1, ne;"
212+
: "=r"(r1), "=r"(ok)::"cc");
213+
if (ok) break;
214+
__asm__ volatile("yield");
215+
} while (true);
216+
return r1;
217+
}
218+
219+
/** Read 64 bits of entropy using rndrrs.
220+
*
221+
* Must only be called when RNDRRS is supported.
222+
*/
223+
static uint64_t GetRNDRRS() noexcept
224+
{
225+
uint8_t ok;
226+
uint64_t r1;
227+
do {
228+
// https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/RNDRRS--Reseeded-Random-Number
229+
__asm__ volatile("mrs %0, s3_3_c2_c4_1; cset %w1, ne;"
230+
: "=r"(r1), "=r"(ok)::"cc");
231+
if (ok) break;
232+
__asm__ volatile("yield");
233+
} while (true);
234+
return r1;
235+
}
236+
178237
#else
179238
/* Access to other hardware random number generators could be added here later,
180239
* assuming it is sufficiently fast (in the order of a few hundred CPU cycles).
@@ -193,6 +252,12 @@ static void SeedHardwareFast(CSHA512& hasher) noexcept {
193252
hasher.Write((const unsigned char*)&out, sizeof(out));
194253
return;
195254
}
255+
#elif defined(__aarch64__) && defined(HWCAP2_RNG)
256+
if (g_rndr_supported) {
257+
uint64_t out = GetRNDR();
258+
hasher.Write((const unsigned char*)&out, sizeof(out));
259+
return;
260+
}
196261
#endif
197262
}
198263

@@ -218,6 +283,14 @@ static void SeedHardwareSlow(CSHA512& hasher) noexcept {
218283
}
219284
return;
220285
}
286+
#elif defined(__aarch64__) && defined(HWCAP2_RNG)
287+
if (g_rndr_supported) {
288+
for (int i = 0; i < 4; ++i) {
289+
uint64_t out = GetRNDRRS();
290+
hasher.Write((const unsigned char*)&out, sizeof(out));
291+
}
292+
return;
293+
}
221294
#endif
222295
}
223296

0 commit comments

Comments
 (0)