785 lines
24 KiB
C
785 lines
24 KiB
C
/**
|
|
** 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 implements the havege algorithm as an inteface to
|
|
* either a single collector in the calling process or an interface to
|
|
* multiple collector processes (experimental).
|
|
*/
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include "havegetest.h"
|
|
#include "havegetune.h"
|
|
/**
|
|
* The library version interface results in a pair of version definitions
|
|
* which must agree yet must also be string literals. No foolproof build
|
|
* mechanism could be devised to ensure this, so a run-time check was added
|
|
* instead - if the two definitions do not agree, the interface is diabled.
|
|
*/
|
|
#define INTERFACE_DISABLED() strcmp(PACKAGE_VERSION,HAVEGE_PREP_VERSION)
|
|
|
|
#if NUMBER_CORES>1
|
|
#include <errno.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/wait.h>
|
|
#include <semaphore.h>
|
|
#include <sched.h>
|
|
/**
|
|
* Collection thread directory
|
|
*/
|
|
typedef struct {
|
|
pid_t main; /* the main thread */
|
|
H_UINT exit_ct; /* shutdown counter */
|
|
H_UINT count; /* associated count */
|
|
H_UINT last; /* last output */
|
|
H_UINT *out; /* buffer pointer */
|
|
H_UINT fatal; /* fatal error in last */
|
|
sem_t flags[1]; /* thread signals */
|
|
} H_THREAD;
|
|
/**
|
|
* Local prototypes
|
|
*/
|
|
static int havege_exit(H_PTR h_ptr);
|
|
static void havege_ipc(H_PTR h_ptr, H_UINT n, H_UINT sz);
|
|
static int havege_rngChild(H_PTR h_ptr, H_UINT cNumber);
|
|
static void havege_unipc(H_PTR h_ptr);
|
|
#endif
|
|
/**
|
|
* Main allocation
|
|
*/
|
|
#ifdef ONLINE_TESTS_ENABLE
|
|
typedef struct {
|
|
struct h_anchor info; /* Application anchor */
|
|
HOST_CFG cfg; /* Runtime environment */
|
|
procShared std; /* Shared test data */
|
|
} H_SETUP;
|
|
|
|
static int testsConfigure(H_UINT *tot, H_UINT *run, char *options);
|
|
static void testsStatus(procShared *tps, char *tot, char *prod);
|
|
|
|
static void testReport(H_COLLECT * h_ctxt, H_UINT action, H_UINT prod, H_UINT state, H_UINT ct);
|
|
static void testReportA(H_PTR h, procA *context);
|
|
static void testReportB(H_PTR h, procB *context);
|
|
|
|
#else
|
|
typedef struct {
|
|
struct h_anchor info; /* Application anchor */
|
|
HOST_CFG cfg; /* Runtime environment */
|
|
} H_SETUP;
|
|
|
|
#endif
|
|
/**
|
|
* Local prototypes
|
|
*/
|
|
static void havege_mute(const char *format, ...);
|
|
/**
|
|
* Initialize the environment based upon the tuning survey. This includes,
|
|
* allocation the output buffer (in shared memory if mult-threaded) and
|
|
* fitting the collection code to the tuning inputs.
|
|
*/
|
|
H_PTR havege_create( /* RETURN: app state */
|
|
H_PARAMS *params) /* IN: input params */
|
|
{
|
|
H_SETUP *anchor;
|
|
HOST_CFG *env;
|
|
H_PTR h = 0;
|
|
H_UINT n = params->nCores;
|
|
H_UINT sz = params->ioSz;
|
|
|
|
if (INTERFACE_DISABLED())
|
|
return NULL;
|
|
if (0 == n)
|
|
n = 1;
|
|
if (0 == sz)
|
|
sz = DEFAULT_BUFSZ;
|
|
anchor = (H_SETUP *)calloc(sizeof(H_SETUP),1);
|
|
if (NULL==anchor)
|
|
return h;
|
|
h = &anchor->info;
|
|
h->print_msg = params->msg_out==0? havege_mute : params->msg_out;
|
|
h->metering = params->metering;
|
|
env = &anchor->cfg;
|
|
havege_tune(env, params);
|
|
h->error = H_NOERR;
|
|
h->arch = ARCH;
|
|
h->inject = params->injection;
|
|
h->n_cores = n;
|
|
h->havege_opts = params->options;
|
|
h->i_collectSz = params->collectSize==0? NDSIZECOLLECT : params->collectSize;
|
|
h->i_readSz = sz;
|
|
h->tuneData = env;
|
|
h->cpu = &env->cpus[env->a_cpu];
|
|
h->instCache = &env->caches[env->i_tune];
|
|
h->dataCache = &env->caches[env->d_tune];
|
|
#ifdef ONLINE_TESTS_ENABLE
|
|
{
|
|
static const H_UINT tests[5] = {B_RUN, A_RUN};
|
|
|
|
H_UINT tot=0,run=0;
|
|
H_UINT i, j;
|
|
|
|
procShared *tps = (procShared *)&anchor->std;
|
|
if (testsConfigure(&tot, &run, params->testSpec)) {
|
|
h->error = H_NOTESTSPEC;
|
|
return h;
|
|
}
|
|
for(i=j=0;i<2;i++)
|
|
if (0!=(tot & tests[i])) {
|
|
tps->testsUsed |= tests[i];
|
|
tps->totTests[j].action = tests[i];
|
|
tps->totTests[j++].options = tot;
|
|
}
|
|
for(i=j=0;i<2;i++)
|
|
if (0!=(run & tests[i])) {
|
|
tps->testsUsed |= tests[i];
|
|
tps->runTests[j].action = tests[i];
|
|
tps->runTests[j++].options = run;
|
|
}
|
|
testsStatus(tps, tps->totText, tps->prodText);
|
|
tps->report = testReport;
|
|
h->testData = tps;
|
|
if (havege_test(tps, params)) {
|
|
h->error = H_NOTESTMEM;
|
|
return h;
|
|
}
|
|
}
|
|
#endif
|
|
#if NUMBER_CORES>1
|
|
havege_ipc(h, n, sz);
|
|
#else
|
|
h->io_buf = malloc(sz);
|
|
h->threads = NULL;
|
|
#endif
|
|
if (NULL==h->io_buf) {
|
|
h->error = H_NOBUF;
|
|
return h;
|
|
}
|
|
havege_ndsetup(h);
|
|
return h;
|
|
}
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
void havege_reparent(
|
|
H_PTR hptr)
|
|
{
|
|
#if NUMBER_CORES>1
|
|
H_THREAD *t = (H_THREAD *)hptr->threads;
|
|
if (0 == t)
|
|
return; /* single-threaded */
|
|
|
|
t->main = getpid();
|
|
#endif
|
|
}
|
|
#pragma GCC diagnostic pop
|
|
|
|
/**
|
|
* Destructor. In a multi-collector build, this method should be called from a signal handler
|
|
* to avoid creating processes.
|
|
*/
|
|
void havege_destroy( /* RETURN: none */
|
|
H_PTR hptr) /* IN-OUT: app anchor */
|
|
{
|
|
if (NULL != hptr) {
|
|
H_COLLECT *htemp;
|
|
void *temp;
|
|
#if NUMBER_CORES>1
|
|
if (!havege_exit(hptr))
|
|
return; /* only main thread continues */
|
|
#endif
|
|
if (0 != (temp=hptr->io_buf)) {
|
|
hptr->io_buf = 0;
|
|
free(temp);
|
|
}
|
|
#ifdef ONLINE_TESTS_ENABLE
|
|
if (0 != (temp=hptr->testData)) {
|
|
double *g = ((procShared *)temp)->G;
|
|
hptr->testData = 0;
|
|
if (0 != g)
|
|
free(g);
|
|
}
|
|
#endif
|
|
if (0 != (htemp=hptr->collector)) {
|
|
hptr->collector = 0;
|
|
havege_nddestroy(htemp);
|
|
}
|
|
free(hptr);
|
|
}
|
|
}
|
|
/**
|
|
* Read random words. In the single-collector case, input is read by the calling
|
|
* the collection method directly. In the multi-collector case, the request info is
|
|
* signaled to the collector last read and the caller waits for a completion signal.
|
|
*/
|
|
int havege_rng( /* RETURN: number words read */
|
|
H_PTR h, /* IN-OUT: app state */
|
|
H_UINT *buffer, /* OUT: read buffer */
|
|
H_UINT sz) /* IN: number words to read */
|
|
{
|
|
#if NUMBER_CORES>1
|
|
H_THREAD *t = (H_THREAD *) h->threads;
|
|
|
|
t->count = sz;
|
|
t->out = buffer;
|
|
if (0!=sem_post(&t->flags[t->last]))
|
|
h->error = H_NORQST;
|
|
else if (0!=sem_wait(&t->flags[h->n_cores]))
|
|
h->error = H_NOCOMP;
|
|
else if (H_NOERR != t->fatal)
|
|
h->error = t->fatal;
|
|
#else
|
|
H_UINT i;
|
|
|
|
for(i=0;i<sz;i++)
|
|
buffer[i] = havege_ndread((H_COLLECT *)h->collector);
|
|
h->error = ((H_COLLECT *)h->collector)->havege_err;
|
|
#endif
|
|
return h->error==(H_UINT)H_NOERR? (int) sz : -1;
|
|
}
|
|
/**
|
|
* Start the entropy collector.
|
|
*/
|
|
int havege_run( /* RETURN: NZ on failure */
|
|
H_PTR h) /* IN-OUT: app anchor */
|
|
{
|
|
int i = 0;
|
|
|
|
#if NUMBER_CORES>1
|
|
for(i = 0; i < h->n_cores;i++)
|
|
if (0 == havege_rngChild(h, i))
|
|
return 1;
|
|
#else
|
|
if (NULL==(h->collector = havege_ndcreate(h, i)))
|
|
return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
/**
|
|
* Report concealed setup data
|
|
*/
|
|
void havege_status( /* RETURN: none */
|
|
H_PTR h_ptr, /* IN: app state */
|
|
H_STATUS h_sts) /* OUT: app state */
|
|
{
|
|
if (0 != h_sts) {
|
|
HOST_CFG *en = (HOST_CFG *) (h_ptr->tuneData);
|
|
CACHE_INST *cd = (CACHE_INST *)(h_ptr->dataCache);
|
|
CACHE_INST *ci = (CACHE_INST *)(h_ptr->instCache);
|
|
CPU_INST *cp = (CPU_INST *) (h_ptr->cpu);
|
|
procShared *ps = (procShared *)(h_ptr->testData);
|
|
|
|
h_sts->version = HAVEGE_PREP_VERSION;
|
|
h_sts->buildOptions = en->buildOpts;
|
|
h_sts->cpuSources = en->cpuOpts;
|
|
h_sts->i_cacheSources = en->icacheOpts;
|
|
h_sts->d_cacheSources = en->dcacheOpts;
|
|
h_sts->vendor = cp->vendor;
|
|
h_sts->d_cache = cd->size;
|
|
h_sts->i_cache = ci->size;
|
|
h_sts->tot_tests = (0 != ps)? ps->totText :"";
|
|
h_sts->prod_tests = (0 != ps)? ps->prodText :"";
|
|
if (0 != ps) {
|
|
memcpy(h_sts->n_tests, ps->meters, (H_OLT_PROD_B_P+1) * sizeof(H_UINT));
|
|
h_sts->last_test8 = ps->lastCoron;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Standard status presetations
|
|
*/
|
|
int havege_status_dump( /* RETURN: output length */
|
|
H_PTR hptr, /* IN: app state */
|
|
H_SD_TOPIC topic, /* IN: presentation topic */
|
|
char *buf, /* OUT: output area */
|
|
size_t len) /* IN: size of buf */
|
|
{
|
|
struct h_status status;
|
|
int n = 0;
|
|
|
|
if (buf != 0) {
|
|
*buf = 0;
|
|
len -= 1;
|
|
havege_status(hptr, &status);
|
|
switch(topic) {
|
|
case H_SD_TOPIC_BUILD:
|
|
n += snprintf(buf, len, "ver: %s; arch: %s; vend: %s; build: (%s); collect: %uK",
|
|
status.version,
|
|
hptr->arch,
|
|
status.vendor,
|
|
status.buildOptions,
|
|
hptr->i_collectSz/1024
|
|
);
|
|
break;
|
|
case H_SD_TOPIC_TUNE:
|
|
n += snprintf(buf, len, "cpu: (%s); data: %uK (%s); inst: %uK (%s); idx: %u/%u; sz: %u/%u",
|
|
status.cpuSources,
|
|
status.d_cache,
|
|
status.d_cacheSources,
|
|
status.i_cache,
|
|
status.i_cacheSources,
|
|
hptr->i_maxidx - hptr->i_idx, hptr->i_maxidx,
|
|
hptr->i_sz, hptr->i_maxsz
|
|
);
|
|
break;
|
|
case H_SD_TOPIC_TEST:
|
|
{
|
|
H_UINT m;
|
|
|
|
if (strlen(status.tot_tests)>0) {
|
|
n += snprintf(buf+n, len-n, "tot tests(%s): ", status.tot_tests);
|
|
if ((m = status.n_tests[ H_OLT_TOT_A_P] + status.n_tests[ H_OLT_TOT_A_F])>0)
|
|
n += snprintf(buf+n, len-n, "A:%u/%u ", status.n_tests[ H_OLT_TOT_A_P], m);
|
|
if ((m = status.n_tests[ H_OLT_TOT_B_P] + status.n_tests[ H_OLT_TOT_B_F])>0)
|
|
n += snprintf(buf+n, len, "B:%u/%u ", status.n_tests[ H_OLT_TOT_B_P], m);
|
|
}
|
|
if (strlen(status.prod_tests)>0) {
|
|
n += snprintf(buf+n, len-n, "continuous tests(%s): ", status.prod_tests);
|
|
if ((m = status.n_tests[ H_OLT_PROD_A_P] + status.n_tests[ H_OLT_PROD_A_F])>0)
|
|
n += snprintf(buf+n, len-n, "A:%u/%u ", status.n_tests[ H_OLT_PROD_A_P], m);
|
|
if ((m = status.n_tests[ H_OLT_PROD_B_P] + status.n_tests[ H_OLT_PROD_B_F])>0)
|
|
n += snprintf(buf+n, len, "B:%u/%u ", status.n_tests[ H_OLT_PROD_B_P], m);
|
|
}
|
|
if (n>0)
|
|
n += snprintf(buf+n, len-n, " last entropy estimate %g", status.last_test8);
|
|
}
|
|
break;
|
|
case H_SD_TOPIC_SUM:
|
|
{
|
|
char units[] = {'T', 'G', 'M', 'K', 0};
|
|
double factor = 1024.0 * 1024.0 * 1024.0 * 1024.0;
|
|
double sz = ((double)hptr->n_fills * hptr->i_collectSz) * sizeof(H_UINT);
|
|
int i;
|
|
|
|
for (i=0;0 != units[i];i++) {
|
|
if (sz >= factor)
|
|
break;
|
|
factor /= 1024.0;
|
|
}
|
|
n = snprintf(buf, len, "fills: %u, generated: %.4g %c bytes",
|
|
hptr->n_fills,
|
|
sz / factor,
|
|
units[i]
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
/**
|
|
* Return-check library prep version. Calling havege_version() with a NULL version
|
|
* returns the definition of HAVEGE_PREP_VERSION used to build the library. Calling
|
|
* with HAVEGE_PREP_VERSION as the version checks if this headers definition is
|
|
* compatible with that of the library, returning NULL if the input is incompatible
|
|
* with the library.
|
|
*/
|
|
const char *havege_version(const char *version)
|
|
{
|
|
if (INTERFACE_DISABLED())
|
|
return NULL;
|
|
/**
|
|
* Version check academic at the moment, but initial idea is to do a table
|
|
* lookup on the library version to get a pattern to match against the
|
|
* input version.
|
|
*/
|
|
if (version) {
|
|
H_UINT l_interface=0, l_revision=0, l_age=0;
|
|
H_UINT p, p_interface, p_revision, p_patch;
|
|
|
|
#ifdef HAVEGE_LIB_VERSION
|
|
sscanf(HAVEGE_LIB_VERSION, "%u:%u:%u", &l_interface, &l_revision, &l_age);
|
|
#endif
|
|
(void)l_interface;(void)l_revision;(void)l_age;(void)p_patch; /* No check for now */
|
|
|
|
p = sscanf(version, "%u.%u.%u", &p_interface, &p_revision, &p_patch);
|
|
if (p!=3 || p_interface != 1 || p_revision != 9)
|
|
return NULL;
|
|
}
|
|
return HAVEGE_PREP_VERSION;
|
|
}
|
|
|
|
/**
|
|
* Place holder if output display not provided
|
|
*/
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
static void havege_mute( /* RETURN: none */
|
|
const char *format, /* IN: printf format */
|
|
...) /* IN: args */
|
|
{
|
|
;
|
|
}
|
|
#pragma GCC diagnostic pop
|
|
|
|
#if NUMBER_CORES > 1
|
|
/**
|
|
* Cleanup collector(s). In a multi-collector environment, need to kill
|
|
* children to avoid zombies.
|
|
*/
|
|
static int havege_exit( /* RETURN: NZ if child */
|
|
H_PTR h_ptr) /* IN: app state */
|
|
{
|
|
H_THREAD *t = (H_THREAD *)h_ptr->threads;
|
|
pid_t p;
|
|
H_UINT i;
|
|
|
|
if (0 == t)
|
|
return 0; /* Must be main thread */
|
|
t->fatal = H_EXIT;
|
|
for(i=0;i<t->exit_ct;i++)
|
|
(void)sem_post(&t->flags[i]);
|
|
if (getpid() != t->main)
|
|
return 1;
|
|
do { /* Wait for children */
|
|
p = wait(NULL);
|
|
} while(p != -1 && errno != ECHILD);
|
|
for(i=0;i<t->exit_ct;i++)
|
|
(void)sem_destroy(&t->flags[i]);
|
|
if (i==h_ptr->n_cores)
|
|
(void)sem_destroy(&t->flags[i]);
|
|
havege_unipc(h_ptr); /* unmap memory */
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Initialize IPC mechanism. This consists of setting up a shared memory area
|
|
* containing the output buffer and the collection thread directory.
|
|
*/
|
|
static void havege_ipc( /* RETURN: None */
|
|
H_PTR h, /* IN: app state */
|
|
H_UINT n, /* IN: number of threads */
|
|
H_UINT sz) /* IN: size of buffer */
|
|
{
|
|
void *m;
|
|
H_THREAD *t;
|
|
H_UINT m_sz;
|
|
int i;
|
|
|
|
if (n > NUMBER_CORES) {
|
|
h->error = H_NOCORES;
|
|
return;
|
|
}
|
|
m = mmap(NULL,
|
|
m_sz = sz + sizeof(H_THREAD) + n * sizeof(sem_t),
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_SHARED | MAP_ANONYMOUS,
|
|
0,
|
|
0);
|
|
if (m != MAP_FAILED) {
|
|
h->io_buf = m;
|
|
h->m_sz = m_sz;
|
|
t = (H_THREAD *)((char *) m + sz);
|
|
memset(t, 0, sizeof(H_THREAD));
|
|
t->main = getpid();
|
|
h->threads = t;
|
|
for(i=0;i<=n;i++)
|
|
if(sem_init(&t->flags[i],1,0) < 0) {
|
|
h->error = H_NOINIT;
|
|
break;
|
|
}
|
|
t->exit_ct = i;
|
|
}
|
|
}
|
|
/**
|
|
* Child harvester task. If task fails to start H_PTR::error will be set to reason.
|
|
* If task fails after start, H_THREAD::fatal will be set to the reason and a completion
|
|
* will be posted to prevent the main thread from hanging waiting for a response.
|
|
*/
|
|
static int havege_rngChild(/* RETURN: none */
|
|
H_PTR h_ptr, /* IN: app state */
|
|
H_UINT cNumber) /* IN: collector index */
|
|
{
|
|
H_COLLECT *h_ctxt = 0;
|
|
H_THREAD *thds = (H_THREAD *) h_ptr->threads;
|
|
H_UINT cNext, i, r;
|
|
int pid;
|
|
|
|
switch(pid=fork()) {
|
|
case 0:
|
|
h_ctxt = havege_ndcreate(h_ptr, cNumber);
|
|
if (NULL != h_ctxt) {
|
|
cNext = (cNumber + 1) % h_ptr->n_cores;
|
|
while(1) {
|
|
if (0!=sem_wait(&thds->flags[cNumber])) {
|
|
thds->fatal = H_NOWAIT;
|
|
break;
|
|
}
|
|
if (H_NOERR != thds->fatal) {
|
|
havege_nddestroy(h_ctxt);
|
|
exit(0);
|
|
}
|
|
thds->last = cNumber;
|
|
r = h_ctxt->havege_szFill - h_ctxt->havege_nptr;
|
|
if (thds->count < r)
|
|
r = thds->count;
|
|
for(i=0;i<r;i++)
|
|
thds->out[i] = havege_ndread(h_ctxt);
|
|
thds->fatal = h_ctxt->havege_err;
|
|
if (0==(thds->count -= i)) {
|
|
if (0!=sem_post(&thds->flags[h_ptr->n_cores])) {
|
|
thds->fatal = H_NODONE;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
thds->out += i;
|
|
if (0!=sem_post(&thds->flags[cNext])) {
|
|
thds->fatal = H_NOPOST;
|
|
break;
|
|
}
|
|
#ifdef HAVE_SCHED_YIELD
|
|
(void)sched_yield();
|
|
#endif
|
|
(void)havege_ndread(h_ctxt);
|
|
h_ctxt->havege_nptr = 0;
|
|
}
|
|
havege_nddestroy(h_ctxt);
|
|
}
|
|
else thds->fatal = h_ptr->error; /* h_ptr is a copy!! */
|
|
(void)sem_post(&thds->flags[h_ptr->n_cores]); /* announce death! */
|
|
break;
|
|
case -1:
|
|
h_ptr->error = H_NOTASK;
|
|
}
|
|
return pid;
|
|
}
|
|
/**
|
|
* Unmap memory
|
|
*/
|
|
static void havege_unipc( /* RETURN: none */
|
|
H_PTR h_ptr) /* IN: app state */
|
|
{
|
|
if (0 != h_ptr->m_sz) {
|
|
munmap(h_ptr->io_buf, h_ptr->m_sz);
|
|
h_ptr->io_buf = 0;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
#ifdef ONLINE_TESTS_ENABLE
|
|
/**
|
|
* Interpret options string as settings. The option string consists of terms
|
|
* like "[t|c][a[1-8][w]|b[w]]".
|
|
*/
|
|
static int testsConfigure( /* RETURN: non-zero on error */
|
|
H_UINT *tot, /* OUT: tot test options */
|
|
H_UINT *run, /* OUT: run test options */
|
|
char *options) /* IN: option string */
|
|
{
|
|
H_UINT section=0;
|
|
int c;
|
|
|
|
if (options==0)
|
|
options = DEFAULT_TEST_OPTIONS;
|
|
while(0 != (c = *options++)) {
|
|
switch(c) {
|
|
case 'T': case 't': /* tot test */
|
|
section = 't';
|
|
*tot = 0;
|
|
break;
|
|
case 'C': case 'c': /* production test */
|
|
section = 'c';
|
|
*run = 0;
|
|
break;
|
|
case 'A': case 'a':
|
|
if (!section) return 1;
|
|
c = atoi(options);
|
|
if (c >= 1 && c < 9) {
|
|
c = 1<<c;
|
|
options +=1;
|
|
}
|
|
else c = 0;
|
|
c |= A_RUN;
|
|
if (*options=='W' || *options=='w') {
|
|
c |= A_WARN;
|
|
options++;
|
|
}
|
|
if (section=='t')
|
|
*tot |= c;
|
|
else *run |= c;
|
|
break;
|
|
case 'B': case 'b':
|
|
if (!section) return 1;
|
|
c = B_RUN;
|
|
if (*options=='W' || *options=='w') {
|
|
c |= B_WARN;
|
|
options++;
|
|
}
|
|
if (section=='t')
|
|
*tot |= c;
|
|
else *run |= c;
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
/**
|
|
* Show test setup. Output strings are [A[N]][B]..
|
|
*/
|
|
static void testsStatus( /* RETURN: test config */
|
|
procShared *tps, /* IN: shared data */
|
|
char *tot, /* OUT: tot tests */
|
|
char *prod) /* OUT: production tests */
|
|
{
|
|
procInst *p;
|
|
char *dst = tot;
|
|
H_UINT i, j, k, m;
|
|
|
|
*dst = *tot = 0;
|
|
p = tps->totTests;
|
|
for(i=0;i<2;i++,p = tps->runTests, dst = prod) {
|
|
for(j=0;j<2;j++,p++) {
|
|
switch(p->action) {
|
|
case A_RUN:
|
|
*dst++ = 'A';
|
|
if (0!=(m = p->options & A_CYCLE)) {
|
|
for(k=0;m>>=1 != 0;k++);
|
|
*dst++ = '0' + k;
|
|
}
|
|
if (0 != (p->options & A_WARN))
|
|
*dst++ = 'w';
|
|
break;
|
|
case B_RUN:
|
|
*dst++ = 'B';
|
|
if (0 != (p->options & B_WARN))
|
|
*dst++ = 'w';
|
|
break;
|
|
}
|
|
*dst = 0;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Reporting unit for tests
|
|
*/
|
|
static void testReport(
|
|
H_COLLECT * h_ctxt, /* IN-OUT: collector context */
|
|
H_UINT action, /* IN: A_RUN or B_RUN */
|
|
H_UINT prod, /* IN: 0==tot, else continuous */
|
|
H_UINT state, /* IN: state variable */
|
|
H_UINT ct) /* IN: bytes consumed */
|
|
{
|
|
H_PTR h_ptr = (H_PTR)(h_ctxt->havege_app);
|
|
onlineTests *context = (onlineTests *) h_ctxt->havege_tests;
|
|
char *result;
|
|
|
|
switch(state) {
|
|
case TEST_DONE: result = "success"; break;
|
|
case TEST_RETRY: result = "retry"; break;
|
|
case TEST_IGNORE: result = "warning"; break;
|
|
default: result = "failure";
|
|
}
|
|
h_ptr->print_msg("AIS-31 %s procedure %s: %s %d bytes fill %d\n",
|
|
prod==0? "tot" : "continuous", action==A_RUN? "A":"B", result, ct, h_ptr->n_fills);
|
|
if (0 != (h_ptr->havege_opts & (H_DEBUG_OLTR|H_DEBUG_OLT)))
|
|
switch(action){
|
|
case A_RUN:
|
|
testReportA(h_ptr, context->pA);
|
|
break;
|
|
case B_RUN:
|
|
testReportB(h_ptr, context->pB);
|
|
break;
|
|
}
|
|
}
|
|
/**
|
|
* Reporting unit for procedure A. Results are 0-257*(1-[4 or 5])
|
|
*/
|
|
static void testReportA( /* RETURN: nothing */
|
|
H_PTR h_ptr, /* IN: application instance */
|
|
procA *p) /* IN: proc instance */
|
|
{
|
|
static const char * pa_tests[6] = {"test0","test1","test2","test3","test4","test5"};
|
|
|
|
H_UINT ran[6],sum[6];
|
|
H_UINT ct, i, j, k;
|
|
|
|
for (i=0;i<6;i++)
|
|
ran[i] = sum[i] = 0;
|
|
for(i=0;i<p->testRun;i++){
|
|
ct = p->results[i].testResult;
|
|
j = ct>>8;
|
|
ran[j] += 1;
|
|
if (0==(ct & 0xff))
|
|
sum[j] += 1;
|
|
}
|
|
h_ptr->print_msg("procedure A: %s:%d/%d, %s:%d/%d, %s:%d/%d, %s:%d/%d, %s:%d/%d, %s:%d/%d\n",
|
|
pa_tests[0], sum[0], ran[0],
|
|
pa_tests[1], sum[1], ran[1],
|
|
pa_tests[2], sum[2], ran[2],
|
|
pa_tests[3], sum[3], ran[3],
|
|
pa_tests[4], sum[4], ran[4],
|
|
pa_tests[5], sum[5], ran[5]
|
|
);
|
|
for(i=k=0;i<p->testRun;i++){
|
|
ct = p->results[i].testResult;
|
|
j = ct>>8;
|
|
if (j==1)
|
|
k+=1;
|
|
if (0!=(ct & 0xff))
|
|
h_ptr->print_msg(" %s[%d] failed with %d\n", pa_tests[j%6],k,p->results[i].finalValue);
|
|
}
|
|
}
|
|
/**
|
|
* Reporting unit for procedure B. Results are 6a-6b-7a[0]-7a[1]-7b[0]-7b[1]-7b[2]-7b[3]-8
|
|
*/
|
|
static void testReportB( /* RETURN: nothing */
|
|
H_PTR h_ptr, /* IN: application instance */
|
|
procB *p) /* IN: proc instance */
|
|
{
|
|
static const char * pb_tests[5] = {"test6a","test6b","test7a","test7b","test8"};
|
|
|
|
H_UINT ct, i, j, ran[5],sum[5];
|
|
|
|
for (i=0;i<5;i++) {
|
|
ran[i] = sum[i] = 0;
|
|
}
|
|
for(i=0;i<p->testNbr;i++){
|
|
ct = p->results[i].testResult;
|
|
j = ct>>8;
|
|
ran[j] += 1;
|
|
if (0==(ct & 0xff))
|
|
sum[j] += 1;
|
|
}
|
|
h_ptr->print_msg("procedure B: %s:%d/%d, %s:%d/%d, %s:%d/%d, %s:%d/%d, %s:%d/%d\n",
|
|
pb_tests[0], sum[0], ran[0],
|
|
pb_tests[1], sum[1], ran[1],
|
|
pb_tests[2], sum[2], ran[2],
|
|
pb_tests[3], sum[3], ran[3],
|
|
pb_tests[4], sum[4], ran[4]
|
|
);
|
|
for(i=0;i<5;i++)
|
|
ran[i] = p->testNbr;
|
|
for(i=0;i<p->testNbr;i++){
|
|
ct = p->results[i].testResult;
|
|
j = ct>>8;
|
|
if (i < ran[j]) ran[j] = i;
|
|
if (0!=(ct & 0xff))
|
|
h_ptr->print_msg(" %s[%d] failed with %g\n", pb_tests[j],i-ran[j],p->results[i].finalValue);
|
|
}
|
|
}
|
|
#endif
|
|
|