summaryrefslogtreecommitdiff
path: root/darn.cpp
diff options
context:
space:
mode:
authorJeffrey Walton <noloader@gmail.com>2018-11-27 02:54:26 -0500
committerGitHub <noreply@github.com>2018-11-27 02:54:26 -0500
commit3db34abf2f9ec9c25b1140955fced60c8d40394c (patch)
tree96fb730bea5aeb85c6040c367fb5a5c800c4585b /darn.cpp
parent1966d13617b30e63068b056be90c75f37e549339 (diff)
downloadcryptopp-git-3db34abf2f9ec9c25b1140955fced60c8d40394c.tar.gz
Add Power9 Random Number Generator support (GH #747, PR #748)
Diffstat (limited to 'darn.cpp')
-rw-r--r--darn.cpp214
1 files changed, 214 insertions, 0 deletions
diff --git a/darn.cpp b/darn.cpp
new file mode 100644
index 00000000..5c40ba78
--- /dev/null
+++ b/darn.cpp
@@ -0,0 +1,214 @@
+// darn.cpp - written and placed in public domain by Jeffrey Walton
+
+#include "pch.h"
+#include "config.h"
+#include "cryptlib.h"
+#include "secblock.h"
+#include "darn.h"
+#include "cpu.h"
+
+// At the moment only GCC 7.0 (and above) seems to support __builtin_darn()
+// and __builtin_darn_32(). Clang 7.0 does not provide them. XLC is unknown,
+// but there are no hits when searching IBM's site. To cover more platforms
+// we provide GCC inline assembly like we do with RDRAND and RDSEED.
+// Platforms that don't support GCC inline assembly or the builtin will fail
+// the compile.
+
+#if defined(__GNUC__) || defined(__IBM_GCC_ASM)
+# define GCC_DARN_ASM_AVAILABLE 1
+#endif
+
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+
+NAMESPACE_BEGIN(CryptoPP)
+
+#if (CRYPTOPP_BOOL_PPC32 || CRYPTOPP_BOOL_PPC64)
+
+// *************************** 32-bit *************************** //
+
+#if (CRYPTOPP_BOOL_PPC32)
+
+// Fills 4 bytes, buffer must be aligned
+inline void DARN32(void* output)
+{
+ CRYPTOPP_ASSERT(IsAlignedOn(output, GetAlignmentOf<word32>()));
+ word32* ptr = reinterpret_cast<word32*>(output);
+
+#if defined(GCC_DARN_ASM_AVAILABLE)
+ // This is "darn r3, 0". When L=0 a 32-bit conditioned word
+ // is returned. On failure 0xffffffffffffffff is returned.
+ // The Power manual recommends only checking the low 32-bit
+ // word for this case. See Power ISA 3.0 specification, p. 78.
+ do
+ {
+ __asm__ __volatile__ (
+ #if (CRYPTOPP_BIG_ENDIAN)
+ ".byte 0x7c, 0x60, 0x05, 0xe6 \n\t" // r3 = darn 3, 0
+ "mr %0, 3 \n\t" // val = r3
+ #else
+ ".byte 0xe6, 0x05, 0x60, 0x7c \n\t" // r3 = darn 3, 0
+ "mr %0, 3 \n\t" // val = r3
+ #endif
+ : "=r" (*ptr) : : "r3"
+ );
+ } while (*ptr == 0xFFFFFFFFu);
+#elif defined(_ARCH_PWR9)
+ // This is probably going to break some platforms.
+ // We will deal with them as we encounter them.
+ *ptr = __builtin_darn_32();
+#else
+ int XXX[-1];
+#endif
+}
+#endif // PPC32
+
+// *************************** 64-bit *************************** //
+
+#if (CRYPTOPP_BOOL_PPC64)
+
+// Fills 8 bytes, buffer must be aligned
+inline void DARN64(void* output)
+{
+ CRYPTOPP_ASSERT(IsAlignedOn(output, GetAlignmentOf<word64>()));
+ word64* ptr = reinterpret_cast<word64*>(output);
+
+#if defined(GCC_DARN_ASM_AVAILABLE)
+ // This is "darn r3, 1". When L=1 a 64-bit conditioned word
+ // is returned. On failure 0xffffffffffffffff is returned.
+ // See Power ISA 3.0 specification, p. 78.
+ do
+ {
+ __asm__ __volatile__ (
+ #if (CRYPTOPP_BIG_ENDIAN)
+ ".byte 0x7c, 0x61, 0x05, 0xe6 \n\t" // r3 = darn 3, 1
+ "mr %0, 3 \n\t" // val = r3
+ #else
+ ".byte 0xe6, 0x05, 0x61, 0x7c \n\t" // r3 = darn 3, 1
+ "mr %0, 3 \n\t" // val = r3
+ #endif
+ : "=r" (*ptr) : : "r3"
+ );
+ } while (*ptr == 0xFFFFFFFFFFFFFFFFull);
+#elif defined(_ARCH_PWR9)
+ // This is probably going to break some platforms.
+ // We will deal with them as we encounter them.
+ *ptr = __builtin_darn();
+#else
+ int XXX[-1];
+#endif
+}
+#endif // PPC64
+
+// ************************ Standard C++ ************************ //
+
+DARN::DARN()
+{
+ if (!HasDARN())
+ throw DARN_Err("HasDARN");
+}
+
+void DARN::GenerateBlock(byte *output, size_t size)
+{
+ CRYPTOPP_ASSERT((output && size) || !(output || size));
+ if (size == 0) return;
+ size_t i = 0;
+
+#if (CRYPTOPP_BOOL_PPC64)
+
+ word64 val;
+ i = reinterpret_cast<uintptr_t>(output) & 0x7;
+
+ if (i != 0)
+ {
+ DARN64(&val);
+ std::memcpy(output, &val, i);
+
+ output += i;
+ size -= i;
+ }
+
+ // Output is aligned
+ for (i = 0; i < size/8; i++)
+ DARN64(output+i*8);
+
+ output += i*8;
+ size -= i*8;
+
+ if (size)
+ {
+ DARN64(&val);
+ std::memcpy(output, &val, size);
+ }
+
+#elif (CRYPTOPP_BOOL_PPC32)
+
+ word32 val;
+ i = reinterpret_cast<uintptr_t>(output) & 0x3;
+
+ if (i != 0)
+ {
+ DARN32(&val);
+ std::memcpy(output, &val, i);
+
+ output += i;
+ size -= i;
+ }
+
+ for (i = 0; i < size/4; i++)
+ DARN32(output+i*4);
+
+ output += 4;
+ size -= 4;
+
+ if (size)
+ {
+ DARN32(&val);
+ std::memcpy(output, &val, size);
+ }
+
+#else
+ // No suitable compiler found
+ CRYPTOPP_UNUSED(output);
+ throw NotImplemented("DARN: failed to find a suitable implementation");
+#endif
+}
+
+void DARN::DiscardBytes(size_t n)
+{
+ // RoundUpToMultipleOf is used because a full word is read, and its cheaper
+ // to discard full words. There's no sense in dealing with tail bytes.
+ FixedSizeSecBlock<word64, 16> discard;
+ n = RoundUpToMultipleOf(n, sizeof(word64));
+
+ size_t count = STDMIN(n, discard.SizeInBytes());
+ while (count)
+ {
+ GenerateBlock(discard.BytePtr(), count);
+ n -= count;
+ count = STDMIN(n, discard.SizeInBytes());
+ }
+}
+
+#else // not PPC32 or PPC64
+
+DARN::DARN()
+{
+ throw DARN_Err("HasDARN");
+}
+
+void DARN::GenerateBlock(byte *output, size_t size)
+{
+ // Constructor will throw, should not get here
+ CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size);
+}
+
+void DARN::DiscardBytes(size_t n)
+{
+ // Constructor will throw, should not get here
+ CRYPTOPP_UNUSED(n);
+}
+
+#endif // PPC32 or PPC64
+
+NAMESPACE_END