summaryrefslogtreecommitdiffstats
path: root/src/havegecollect.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-11-10 15:31:03 +0100
committerDaniel Baumann <daniel@debian.org>2024-11-10 15:31:03 +0100
commit2b16d0d8a6bead5bcc2125b890bf1a13d19602dc (patch)
treeb67952daab2d78e87c103ed10ece33ad6a9a2167 /src/havegecollect.c
parentInitial commit. (diff)
downloadhaveged-2b16d0d8a6bead5bcc2125b890bf1a13d19602dc.tar.xz
haveged-2b16d0d8a6bead5bcc2125b890bf1a13d19602dc.zip
Adding upstream version 1.9.14.upstream/1.9.14
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'src/havegecollect.c')
-rw-r--r--src/havegecollect.c480
1 files changed, 480 insertions, 0 deletions
diff --git a/src/havegecollect.c b/src/havegecollect.c
new file mode 100644
index 0000000..1c82e51
--- /dev/null
+++ b/src/havegecollect.c
@@ -0,0 +1,480 @@
+/**
+ ** Simple entropy harvester based upon the havege RNG
+ **
+ ** Copyright 2018-2021 Jirka Hladky hladky DOT jiri AT gmail DOT com
+ ** Copyright 2009-2014 Gary Wuertz gary@issiweb.com
+ ** Copyright 2011-2012 BenEleventh Consulting manolson@beneleventh.com
+ **
+ ** This program is free software: you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License as published by
+ ** the Free Software Foundation, either version 3 of the License, or
+ ** (at your option) any later version.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * This compile unit isolates the operation of the HAVEGE algorithm to better
+ * deal with compiler issues. Extensive macro expansion used to deal with
+ * hardware variations.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "havegecollect.h"
+#include "havegetest.h"
+#include "havegetune.h"
+/**
+ * Injection and capture diagnostics
+ */
+#if defined(RAW_IN_ENABLE) || defined(RAW_OUT_ENABLE)
+#define DIAGNOSTICS_ENABLE
+#endif
+/**
+ * Option to use clockgettime() as timer source
+ */
+#if defined(ENABLE_CLOCK_GETTIME)
+#include <time.h>
+
+#undef HARDCLOCK
+#define HARDCLOCK(x) x = havege_clock()
+/**
+ * Provide a generic timer fallback
+ */
+static H_UINT havege_clock(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (H_UINT)(ts.tv_nsec + ts.tv_sec * 1000000000LL);
+}
+#endif
+
+/**
+ * Memory allocation sizing
+ */
+#define SZH_INIT sizeof(H_COLLECT)+sizeof(char *)*(LOOP_CT + 2)
+#define SZH_COLLECT(a) sizeof(H_COLLECT)+sizeof(H_UINT)*(a+16384-1)
+/**
+ * The HAVEGE collector is created by interleaving instructions generated by
+ * oneiteration.h with the LOOP() output to control the sequence. At each
+ * LOOP() point, the following actions are possible.
+ */
+typedef enum {
+ LOOP_NEXT, /* Next loop */
+ LOOP_ENTER, /* First loop */
+ LOOP_EXIT /* Last loop */
+} LOOP_BRANCH;
+/**
+ * The LOOP macro labels calculation sequences generated by oneiteration.h in
+ * decreasing order from LOOPCT down to 0. During a normal collection, the
+ * loop construct only introduces an extra conditional branch into the instruction
+ * stream. For the exceptional conditions (initialization, end-of-loop, and
+ * raw HARDCLOCK capture), the return from havege_cp() is used to determine
+ * the action to be taken.
+ */
+#define LOOP(n,m) loop##n: if (n < h_ctxt->havege_cdidx) { \
+ switch(havege_cp(h_ctxt,i,n,LOOP_PT(n))) { \
+ case LOOP_NEXT: goto loop##m; \
+ case LOOP_ENTER: goto loop_enter; \
+ case LOOP_EXIT: goto loop_exit; \
+ } \
+ }
+/**
+ * These macros below bind the code contained in oneiteration.h to the H_COLLECT
+ * instance defined above
+ */
+#define ANDPT (h_ctxt->havege_andpt)
+#define PTTEST (h_ctxt->havege_PTtest)
+#define PT (h_ctxt->havege_PT)
+#define PT1 (h_ctxt->havege_pt2)
+#define PT2 (h_ctxt->havege_PT2)
+#define PWALK (h_ctxt->havege_pwalk)
+#define RESULT (h_ctxt->havege_bigarray)
+/**
+ * Previous diagnostic support has been replaced. The new implementation provides
+ * simultaneous access to both the noise source (i.e. the timer tics) and the
+ * output. The tics buffer is also used by the injection diagnostic if enabled
+ */
+#ifdef DIAGNOSTICS_ENABLE
+#define HTICK1 (h_ctxt->havege_tics[i>>3])
+#define HTICK2 (h_ctxt->havege_tics[i>>3])
+#define SZ_TICK ((h_ptr->i_collectSz)>>3)
+#else
+#define HTICK1 (h_ctxt->havege_tic)
+#define HTICK2 (h_ctxt->havege_tic)
+#define SZ_TICK 0
+#endif
+
+/**
+ * If the injection diagnostic is enabled, use a wrapper for the timer source
+ */
+#ifdef RAW_IN_ENABLE
+static H_UINT havege_inject(H_COLLECT *h_ctxt, H_UINT x);
+
+#define HARDCLOCKR(x) x=havege_inject(h_ctxt, x)
+#else
+#define HARDCLOCKR(x) HARDCLOCK(x)
+#endif
+/**
+ * inline optimization - left conditional for legacy systems
+ */
+#if 0
+#define ROR32(value,shift) ((value >> (shift)) | (value << (32-shift)))
+#else
+inline static H_UINT ror32(const H_UINT value, const H_UINT shift) {
+ return (value >> shift) | (value << (32 - shift));
+}
+#define ROR32(value,shift) ror32(value, shift)
+#endif
+/**
+ * Local prototypes
+ */
+static LOOP_BRANCH havege_cp(H_COLLECT *h_ctxt, H_UINT i, H_UINT n, char *p);
+/**
+ * Protect the collection mechanism against ever-increasing gcc optimization
+ */
+#if defined (GCC_VERSION) && GCC_VERSION >= 40400
+static int havege_gather(H_COLLECT * h_ctxt) __attribute__((optimize(1)));
+#else
+static int havege_gather(H_COLLECT * h_ctxt);
+#endif
+static void havege_ndinit(H_PTR h_ptr, struct h_collect *h_ctxt);
+
+/**
+ * Create a collector
+ */
+H_COLLECT *havege_ndcreate(/* RETURN: NULL on failure */
+ H_PTR h_ptr, /* IN-OUT: application instance */
+ H_UINT nCollector) /* IN: The collector instance */
+{
+ H_UINT i,offs,*p,d_cache;
+ H_UINT szBuffer;
+ H_COLLECT *h_ctxt;
+
+ szBuffer = h_ptr->i_collectSz;
+ d_cache = ((CACHE_INST *)(h_ptr->dataCache))->size;
+ h_ctxt = (H_COLLECT *) calloc(SZH_COLLECT(szBuffer + SZ_TICK),1);
+ if (NULL != h_ctxt) {
+ h_ctxt->havege_app = h_ptr;
+ h_ctxt->havege_idx = nCollector;
+ h_ctxt->havege_raw = h_ptr->havege_opts & 0xff00;
+ h_ctxt->havege_rawInput = h_ptr->inject;
+ h_ctxt->havege_szCollect = szBuffer;
+ h_ctxt->havege_szFill = szBuffer>>3;
+ h_ctxt->havege_cdidx = h_ptr->i_idx;
+ p = (H_UINT *) RESULT;
+ h_ctxt->havege_err = H_NOERR;
+ h_ctxt->havege_tests = 0;
+ h_ctxt->havege_extra = 0;
+ h_ctxt->havege_tics = p+szBuffer;
+
+ /** An intermediate walk table twice the size of the L1 cache is allocated
+ ** for use in permuting time stamp readings. The is meant to exercise
+ ** processor TLBs.
+ */
+ ANDPT = ((2*d_cache*1024)/sizeof(H_UINT))-1;
+ p = (H_UINT *) calloc((ANDPT + 4097)*sizeof(H_UINT),1);
+ if (NULL != p) {
+ h_ctxt->havege_extra = p;
+ offs = (H_UINT)((((unsigned long)&p[4096])&0xfff)/sizeof(H_UINT));
+ PWALK = &p[4096-offs];
+ /**
+ * Warm up the generator, running the startup tests
+ */
+#if defined(RAW_IN_ENABLE)
+ if (0 == (h_ctxt->havege_raw & H_DEBUG_TEST_IN))
+#endif
+ {
+ H_UINT t0=0;
+
+ (void)havege_gather(h_ctxt); /* first sample */
+ t0 = HTICK1;
+ for(i=1;i<MININITRAND;i++)
+ (void)havege_gather(h_ctxt); /* warmup rng */
+ if (HTICK1==t0) { /* timer stuck? */
+ h_ptr->error = H_NOTIMER;
+ havege_nddestroy(h_ctxt);
+ return NULL;
+ }
+ }
+#ifdef ONLINE_TESTS_ENABLE
+ {
+ procShared *ps = (procShared *)(h_ptr->testData);
+ while(0!=ps->run(h_ctxt, 0)) { /* run tot tests */
+ (void)havege_gather(h_ctxt);
+ }
+ }
+ if (H_NOERR != (h_ptr->error = h_ctxt->havege_err)) {
+ havege_nddestroy(h_ctxt);
+ return NULL;
+ }
+#endif
+ h_ctxt->havege_nptr = szBuffer;
+ if (0 == (h_ctxt->havege_raw & H_DEBUG_RAW_OUT))
+ h_ctxt->havege_szFill = szBuffer;
+ }
+ else {
+ havege_nddestroy(h_ctxt);
+ h_ptr->error = H_NOWALK;
+ return NULL;
+ }
+ }
+ else h_ptr->error = H_NOCOLLECT;
+ return h_ctxt;
+}
+/**
+ * Destruct a collector
+ */
+void havege_nddestroy( /* RETURN: none */
+ H_COLLECT *h_ctxt) /* IN: collector context */
+{
+ if (0 != h_ctxt) {
+ if (h_ctxt->havege_extra!=0) {
+ free(h_ctxt->havege_extra);
+ h_ctxt->havege_extra = 0;
+ }
+ if (h_ctxt->havege_tests!=0) {
+ free(h_ctxt->havege_tests);
+ h_ctxt->havege_tests = 0;
+ }
+ free((void *)h_ctxt);
+ }
+}
+/**
+ * Read from the collector.
+ */
+H_UINT havege_ndread( /* RETURN: data value */
+ H_COLLECT *h_ctxt) /* IN: collector context */
+{
+ if (h_ctxt->havege_nptr >= h_ctxt->havege_szFill) {
+ H_PTR h_ptr = (H_PTR)(h_ctxt->havege_app);
+ pMeter pm;
+
+ if (0 != (pm = h_ptr->metering))
+ (*pm)(h_ctxt->havege_idx, 0);
+#ifdef ONLINE_TESTS_ENABLE
+ {
+ procShared *ps = (procShared *)(h_ptr->testData);
+ do {
+ (void) havege_gather(h_ctxt);
+ (void) ps->run(h_ctxt, 1);
+ } while(ps->discard(h_ctxt)>0);
+ }
+#else
+ (void) havege_gather(h_ctxt);
+#endif
+ h_ptr->n_fills += 1;
+ if (0 != pm)
+ (*pm)(h_ctxt->havege_idx, 1);
+ h_ctxt->havege_nptr = 0;
+ }
+#ifdef RAW_OUT_ENABLE
+ if (0!=(h_ctxt->havege_raw & H_DEBUG_RAW_OUT))
+ return h_ctxt->havege_tics[h_ctxt->havege_nptr++];
+#endif
+ return RESULT[h_ctxt->havege_nptr++];
+}
+/**
+ * Setup haveged
+ */
+void havege_ndsetup( /* RETURN: None */
+ H_PTR h_ptr) /* IN-OUT: application instance */
+{
+ char wkspc[SZH_INIT];
+
+ memset(wkspc, 0, SZH_INIT);
+ havege_ndinit(h_ptr, (struct h_collect *) wkspc);
+}
+/**
+ * This method is called only for control points NOT part of a normal collection:
+ *
+ * a) For a collection loop after all iterations are performed, this function
+ * determines if the collection buffer is full.
+ * b) For initialization, this method saves the address of the collection point
+ * for analysis at the end of the loop.
+ */
+static LOOP_BRANCH havege_cp( /* RETURN: branch to take */
+ H_COLLECT *h_ctxt, /* IN: collection context */
+ H_UINT i, /* IN: collection offset */
+ H_UINT n, /* IN: iteration index */
+ char *p) /* IN: code pointer */
+{
+
+ if (h_ctxt->havege_cdidx <= LOOP_CT)
+ return i < h_ctxt->havege_szCollect? LOOP_ENTER : LOOP_EXIT;
+ ((char **)RESULT)[n] = CODE_PT(p);
+ if (n==0) h_ctxt->havege_cdidx = 0;
+ return LOOP_NEXT;
+}
+
+/**
+ * The collection loop is constructed by repetitions of oneinteration.h interleaved
+ * with control points generated by the LOOP macro.
+ */
+static int havege_gather( /* RETURN: 1 if initialized */
+ H_COLLECT * h_ctxt) /* IN: collector context */
+{
+ H_UINT i=0,pt=0,inter=0;
+ H_UINT *Pt0, *Pt1, *Pt2, *Pt3, *Ptinter;
+
+#if defined(RAW_IN_ENABLE)
+if (0 != (h_ctxt->havege_raw & H_DEBUG_RAW_IN)) {
+ (*h_ctxt->havege_rawInput)(h_ctxt->havege_tics, h_ctxt->havege_szCollect>>3);
+ h_ctxt->havege_tic = h_ctxt->havege_tics[0];
+ }
+else if (0 != (h_ctxt->havege_raw & H_DEBUG_TEST_IN)) {
+ (*h_ctxt->havege_rawInput)(RESULT, h_ctxt->havege_szCollect);
+ return 1;
+ }
+#endif
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+
+loop_enter:
+LOOP(40,39)
+ #include "oneiteration.h"
+LOOP(39,38)
+ #include "oneiteration.h"
+LOOP(38,37)
+ #include "oneiteration.h"
+LOOP(37,36)
+ #include "oneiteration.h"
+LOOP(36,35)
+ #include "oneiteration.h"
+LOOP(35,34)
+ #include "oneiteration.h"
+LOOP(34,33)
+ #include "oneiteration.h"
+LOOP(33,32)
+ #include "oneiteration.h"
+LOOP(32,31)
+ #include "oneiteration.h"
+LOOP(31,30)
+ #include "oneiteration.h"
+LOOP(30,29)
+ #include "oneiteration.h"
+LOOP(29,28)
+ #include "oneiteration.h"
+LOOP(28,27)
+ #include "oneiteration.h"
+LOOP(27,26)
+ #include "oneiteration.h"
+LOOP(26,25)
+ #include "oneiteration.h"
+LOOP(25,24)
+ #include "oneiteration.h"
+LOOP(24,23)
+ #include "oneiteration.h"
+LOOP(23,22)
+ #include "oneiteration.h"
+LOOP(22,21)
+ #include "oneiteration.h"
+LOOP(21,20)
+ #include "oneiteration.h"
+LOOP(20,19)
+ #include "oneiteration.h"
+LOOP(19,18)
+ #include "oneiteration.h"
+LOOP(18,17)
+ #include "oneiteration.h"
+LOOP(17,16)
+ #include "oneiteration.h"
+LOOP(16,15)
+ #include "oneiteration.h"
+LOOP(15,14)
+ #include "oneiteration.h"
+LOOP(14,13)
+ #include "oneiteration.h"
+LOOP(13,12)
+ #include "oneiteration.h"
+LOOP(12,11)
+ #include "oneiteration.h"
+LOOP(11,10)
+ #include "oneiteration.h"
+LOOP(10,9)
+ #include "oneiteration.h"
+LOOP(9,8)
+ #include "oneiteration.h"
+LOOP(8,7)
+ #include "oneiteration.h"
+LOOP(7,6)
+ #include "oneiteration.h"
+LOOP(6,5)
+ #include "oneiteration.h"
+LOOP(5,4)
+ #include "oneiteration.h"
+LOOP(4,3)
+ #include "oneiteration.h"
+LOOP(3,2)
+ #include "oneiteration.h"
+LOOP(2,1)
+ #include "oneiteration.h"
+LOOP(1,0)
+ #include "oneiteration.h"
+LOOP(0,0)
+ (void)havege_cp(h_ctxt, i,0,LOOP_PT(0));
+#pragma GCC diagnostic pop
+
+loop_exit:
+ return ANDPT==0? 0 : 1;
+}
+#ifdef RAW_IN_ENABLE
+/**
+ * Wrapper for noise injector. When input is injected, the hardclock
+ * call is not made and the contents of the tic buffer are used
+ * unchanged from when the inject call was made at the top of the
+ * loop.
+ */
+static H_UINT havege_inject( /* RETURN: clock value */
+ H_COLLECT *h_ctxt, /* IN: workspace */
+ H_UINT x) /* IN: injected value */
+{
+ if (0==(h_ctxt->havege_raw & H_DEBUG_RAW_IN)) {
+ HARDCLOCK(x);
+ }
+ return x;
+}
+#endif
+/**
+ * Initialize the collection loop
+ */
+#if defined (GCC_VERSION) && GCC_VERSION >= 40600
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
+
+static void havege_ndinit( /* RETURN: None */
+ H_PTR h_ptr, /* IN-OUT: application instance */
+ struct h_collect *h_ctxt) /* IN: workspace */
+{
+ char **addr = (char **)(&RESULT[0]);
+ H_UINT sz;
+ int i;
+
+ h_ctxt->havege_cdidx = LOOP_CT + 1;
+ (void)havege_gather(h_ctxt);
+ for (i=0;i<=LOOP_CT;i++) {
+ if (0 != (h_ptr->havege_opts & H_DEBUG_COMPILE)) {
+ h_ptr->print_msg("Address %u=%p\n", i, addr[i]);
+ }
+ RESULT[i] = labs(addr[i] - addr[LOOP_CT]);
+ if (i > 0 && 0 != (h_ptr->havege_opts & H_DEBUG_LOOP)) {
+ h_ptr->print_msg("Loop %u: offset=%u, delta=%u\n", i,RESULT[i],RESULT[i-1]-RESULT[i]);
+ }
+ }
+ h_ptr->i_maxidx = LOOP_CT;
+ h_ptr->i_maxsz = RESULT[1];
+ sz = ((CACHE_INST *)(h_ptr->instCache))->size * 1024;
+ for(i=LOOP_CT;i>0;i--)
+ if (RESULT[i]>sz)
+ break;
+ h_ptr->i_idx = ++i;
+ h_ptr->i_sz = RESULT[i];
+}