diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 34 | ||||
-rw-r--r-- | src/Makefile.in | 794 | ||||
-rw-r--r-- | src/cpuid-43.h | 181 | ||||
-rw-r--r-- | src/havege.c | 785 | ||||
-rw-r--r-- | src/havege.h | 308 | ||||
-rw-r--r-- | src/havegecmd.c | 460 | ||||
-rw-r--r-- | src/havegecmd.h | 103 | ||||
-rw-r--r-- | src/havegecollect.c | 480 | ||||
-rw-r--r-- | src/havegecollect.h | 215 | ||||
-rw-r--r-- | src/haveged.c | 1008 | ||||
-rw-r--r-- | src/haveged.h | 100 | ||||
-rw-r--r-- | src/havegetest.c | 1030 | ||||
-rw-r--r-- | src/havegetest.h | 225 | ||||
-rw-r--r-- | src/havegetune.c | 926 | ||||
-rw-r--r-- | src/havegetune.h | 121 | ||||
-rw-r--r-- | src/oneiteration.h | 201 |
16 files changed, 6971 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..c507ea4 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,34 @@ +## Process this file with automake to produce Makefile.in. + +if ENABLE_BIN +bin_PROGRAMS = haveged +else +sbin_PROGRAMS = haveged +endif + +AM_CFLAGS=-Wall -Wextra -Wpedantic -I.. + +####nolibtool_start## +##haveged_SOURCES = haveged.c havege.c havegetune.c havegecollect.c havegetest.c havegecmd.c \ +## cpuid-43.h haveged.h havege.h havegetune.h havegecollect.h havegetest.h oneiteration.h \ +## havegecmd.h +## +##haveged_LDADD = @HA_LDFLAGS@ +####nolibtool_end## +##libtool_start## +lib_LTLIBRARIES = libhavege.la +libhavege_la_CPPFLAGS = +libhavege_la_LDFLAGS = -version-number @HAVEGE_LT_VERSION@ +libhavege_la_LIBADD = @HA_LDFLAGS@ + +libhavege_la_SOURCES = havege.c havegetune.c havegecollect.c havegetest.c \ + cpuid-43.h havege.h havegetune.h havegecollect.h havegetest.h oneiteration.h + +pkginclude_HEADERS = havege.h + +haveged_SOURCES = haveged.c haveged.h havegecmd.c havegecmd.h + +haveged_LDADD = @HA_LDFLAGS@ libhavege.la +##libtool_end## + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..559af60 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,794 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@ENABLE_BIN_TRUE@bin_PROGRAMS = haveged$(EXEEXT) +@ENABLE_BIN_FALSE@sbin_PROGRAMS = haveged$(EXEEXT) +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(pkginclude_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" \ + "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgincludedir)" +PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +LTLIBRARIES = $(lib_LTLIBRARIES) +libhavege_la_DEPENDENCIES = +am_libhavege_la_OBJECTS = libhavege_la-havege.lo \ + libhavege_la-havegetune.lo libhavege_la-havegecollect.lo \ + libhavege_la-havegetest.lo +libhavege_la_OBJECTS = $(am_libhavege_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libhavege_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libhavege_la_LDFLAGS) $(LDFLAGS) -o $@ +am_haveged_OBJECTS = haveged.$(OBJEXT) havegecmd.$(OBJEXT) +haveged_OBJECTS = $(am_haveged_OBJECTS) +haveged_DEPENDENCIES = libhavege.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = +am__maybe_remake_depfiles = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libhavege_la_SOURCES) $(haveged_SOURCES) +DIST_SOURCES = $(libhavege_la_SOURCES) $(haveged_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(pkginclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVEGE_LT_VERSION = @HAVEGE_LT_VERSION@ +HA_DISTRO = @HA_DISTRO@ +HA_LDFLAGS = @HA_LDFLAGS@ +HA_UNITD = @HA_UNITD@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__leading_dot = @am__leading_dot@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CFLAGS = -Wall -Wextra -Wpedantic -I.. + +####nolibtool_start## +####nolibtool_end## +lib_LTLIBRARIES = libhavege.la +libhavege_la_CPPFLAGS = +libhavege_la_LDFLAGS = -version-number @HAVEGE_LT_VERSION@ +libhavege_la_LIBADD = @HA_LDFLAGS@ +libhavege_la_SOURCES = havege.c havegetune.c havegecollect.c havegetest.c \ + cpuid-43.h havege.h havegetune.h havegecollect.h havegetest.h oneiteration.h + +pkginclude_HEADERS = havege.h +haveged_SOURCES = haveged.c haveged.h havegecmd.c havegecmd.h +haveged_LDADD = @HA_LDFLAGS@ libhavege.la +MAINTAINERCLEANFILES = Makefile.in +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu --ignore-deps src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu --ignore-deps src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libhavege.la: $(libhavege_la_OBJECTS) $(libhavege_la_DEPENDENCIES) $(EXTRA_libhavege_la_DEPENDENCIES) + $(AM_V_CCLD)$(libhavege_la_LINK) -rpath $(libdir) $(libhavege_la_OBJECTS) $(libhavege_la_LIBADD) $(LIBS) + +haveged$(EXEEXT): $(haveged_OBJECTS) $(haveged_DEPENDENCIES) $(EXTRA_haveged_DEPENDENCIES) + @rm -f haveged$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(haveged_OBJECTS) $(haveged_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +.c.o: + $(AM_V_CC)$(COMPILE) -c -o $@ $< + +.c.obj: + $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: + $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< + +libhavege_la-havege.lo: havege.c + $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhavege_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhavege_la-havege.lo `test -f 'havege.c' || echo '$(srcdir)/'`havege.c + +libhavege_la-havegetune.lo: havegetune.c + $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhavege_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhavege_la-havegetune.lo `test -f 'havegetune.c' || echo '$(srcdir)/'`havegetune.c + +libhavege_la-havegecollect.lo: havegecollect.c + $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhavege_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhavege_la-havegecollect.lo `test -f 'havegecollect.c' || echo '$(srcdir)/'`havegecollect.c + +libhavege_la-havegetest.lo: havegetest.c + $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhavege_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhavege_la-havegetest.lo `test -f 'havegetest.c' || echo '$(srcdir)/'`havegetest.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgincludeHEADERS: $(pkginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkgincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \ + install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-pkgincludeHEADERS uninstall-sbinPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-sbinPROGRAMS cscopelist-am ctags ctags-am \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-man install-pdf install-pdf-am \ + install-pkgincludeHEADERS install-ps install-ps-am \ + install-sbinPROGRAMS install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-libLTLIBRARIES uninstall-pkgincludeHEADERS \ + uninstall-sbinPROGRAMS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/cpuid-43.h b/src/cpuid-43.h new file mode 100644 index 0000000..a9d90a6 --- /dev/null +++ b/src/cpuid-43.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + * + * This file 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, or (at your option) any + * later version. + * + * This file 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. + * + * Under Section 7 of GPL version 3, you are granted additional + * permissions described in the GCC Runtime Library Exception, version + * 3.1, as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License and + * a copy of the GCC Runtime Library Exception along with this program; + * see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + * <http://www.gnu.org/licenses/>. + */ + +/* %ecx */ +#define bit_SSE3 (1 << 0) +#define bit_PCLMUL (1 << 1) +#define bit_SSSE3 (1 << 9) +#define bit_FMA (1 << 12) +#define bit_CMPXCHG16B (1 << 13) +#define bit_SSE4_1 (1 << 19) +#define bit_SSE4_2 (1 << 20) +#define bit_MOVBE (1 << 22) +#define bit_POPCNT (1 << 23) +#define bit_AES (1 << 25) +#define bit_XSAVE (1 << 26) +#define bit_OSXSAVE (1 << 27) +#define bit_AVX (1 << 28) + +/* %edx */ +#define bit_CMPXCHG8B (1 << 8) +#define bit_CMOV (1 << 15) +#define bit_MMX (1 << 23) +#define bit_FXSAVE (1 << 24) +#define bit_SSE (1 << 25) +#define bit_SSE2 (1 << 26) + +/* Extended Features */ +/* %ecx */ +#define bit_LAHF_LM (1 << 0) +#define bit_ABM (1 << 5) +#define bit_SSE4a (1 << 6) +#define bit_XOP (1 << 11) +#define bit_LWP (1 << 15) +#define bit_FMA4 (1 << 16) + +/* %edx */ +#define bit_LM (1 << 29) +#define bit_3DNOWP (1 << 30) +#define bit_3DNOW (1 << 31) + + +#if defined(__i386__) && defined(__PIC__) +/* %ebx may be the PIC register. */ +#if __GNUC__ >= 3 +#define __cpuid(level, a, b, c, d) \ + __asm__ ("xchg{l}\t{%%}ebx, %1\n\t" \ + "cpuid\n\t" \ + "xchg{l}\t{%%}ebx, %1\n\t" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "0" (level)) + +#define __cpuid_count(level, count, a, b, c, d) \ + __asm__ ("xchg{l}\t{%%}ebx, %1\n\t" \ + "cpuid\n\t" \ + "xchg{l}\t{%%}ebx, %1\n\t" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "0" (level), "2" (count)) +#else +/* Host GCCs older than 3.0 weren't supporting Intel asm syntax + nor alternatives in i386 code. */ +#define __cpuid(level, a, b, c, d) \ + __asm__ ("xchgl\t%%ebx, %1\n\t" \ + "cpuid\n\t" \ + "xchgl\t%%ebx, %1\n\t" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "0" (level)) + +#define __cpuid_count(level, count, a, b, c, d) \ + __asm__ ("xchgl\t%%ebx, %1\n\t" \ + "cpuid\n\t" \ + "xchgl\t%%ebx, %1\n\t" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "0" (level), "2" (count)) +#endif +#else +#define __cpuid(level, a, b, c, d) \ + __asm__ ("cpuid\n\t" \ + : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \ + : "0" (level)) + +#define __cpuid_count(level, count, a, b, c, d) \ + __asm__ ("cpuid\n\t" \ + : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \ + : "0" (level), "2" (count)) +#endif + +/* Return highest supported input value for cpuid instruction. ext can + be either 0x0 or 0x8000000 to return highest supported value for + basic or extended cpuid information. Function returns 0 if cpuid + is not supported or whatever cpuid returns in eax register. If sig + pointer is non-null, then first four bytes of the signature + (as found in ebx register) are returned in location pointed by sig. */ + +static __inline unsigned int +__get_cpuid_max (unsigned int __ext, unsigned int *__sig) +{ + unsigned int __eax, __ebx, __ecx, __edx; + +#ifndef __x86_64__ + /* See if we can use cpuid. On AMD64 we always can. */ +#if __GNUC__ >= 3 + __asm__ ("pushf{l|d}\n\t" + "pushf{l|d}\n\t" + "pop{l}\t%0\n\t" + "mov{l}\t{%0, %1|%1, %0}\n\t" + "xor{l}\t{%2, %0|%0, %2}\n\t" + "push{l}\t%0\n\t" + "popf{l|d}\n\t" + "pushf{l|d}\n\t" + "pop{l}\t%0\n\t" + "popf{l|d}\n\t" + : "=&r" (__eax), "=&r" (__ebx) + : "i" (0x00200000)); +#else +/* Host GCCs older than 3.0 weren't supporting Intel asm syntax + nor alternatives in i386 code. */ + __asm__ ("pushfl\n\t" + "pushfl\n\t" + "popl\t%0\n\t" + "movl\t%0, %1\n\t" + "xorl\t%2, %0\n\t" + "pushl\t%0\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl\t%0\n\t" + "popfl\n\t" + : "=&r" (__eax), "=&r" (__ebx) + : "i" (0x00200000)); +#endif + + if (!((__eax ^ __ebx) & 0x00200000)) + return 0; +#endif + + /* Host supports cpuid. Return highest supported cpuid input value. */ + __cpuid (__ext, __eax, __ebx, __ecx, __edx); + + if (__sig) + *__sig = __ebx; + + return __eax; +} + +/* Return cpuid data for requested cpuid level, as found in returned + eax, ebx, ecx and edx registers. The function checks if cpuid is + supported and returns 1 for valid cpuid information or 0 for + unsupported cpuid level. All pointers are required to be non-null. */ + +static __inline int +__get_cpuid (unsigned int __level, + unsigned int *__eax, unsigned int *__ebx, + unsigned int *__ecx, unsigned int *__edx) +{ + unsigned int __ext = __level & 0x80000000; + + if (__get_cpuid_max (__ext, 0) < __level) + return 0; + + __cpuid (__level, *__eax, *__ebx, *__ecx, *__edx); + return 1; +} diff --git a/src/havege.c b/src/havege.c new file mode 100644 index 0000000..b0070d1 --- /dev/null +++ b/src/havege.c @@ -0,0 +1,785 @@ +/** + ** 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 + diff --git a/src/havege.h b/src/havege.h new file mode 100644 index 0000000..1483c69 --- /dev/null +++ b/src/havege.h @@ -0,0 +1,308 @@ +/** + ** 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/>. + */ +#ifndef HAVEGE_H +#define HAVEGE_H + +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif +/** + * header/package version as a numeric major, minor, patch triple. See havege_version() + * below for usage. + */ +#define HAVEGE_PREP_VERSION "1.9.14" +/** + * Basic types + */ +#define H_UINT uint32_t +#define H_UINT8 uint8_t +/** + * Optional metering call-back. Called with event=0 at start of collection buffer fill. + * Called with event=1 at end of collection buffer fill. The nCollect parameter indicates + * the calling process if multiple collection processes are enabled. Use a value of 0 + * to disable metering. + */ +typedef void (*pMeter)(H_UINT nCollect, H_UINT event); +/** + * Optional message display call-back. This printf style method is used for all diagnostic + * output including havege_status(). Use a value of 0 to disable output. + */ +typedef void (*pMsg)(const char *format, ...); +/** + * Injection call-back for RAW diagnostics. Use a value of 0 to disable diagnostic. Ignored + * except for diaqnotic builds. + */ +typedef int (*pRawIn)(volatile H_UINT *pData, H_UINT szData); +/** + * options for H_PARAMS below. Lower byte transferred from verbose settings + * upper byte set by diagnositic run options + */ +#define H_VERBOSE 0x001 /* deprecated from ver 1.7 */ +#define H_DEBUG_INFO 0x001 /* Show config info, retries */ +#define H_DEBUG_OLTR 0x002 /* Show detailed test retry info */ +#define H_DEBUG_TIME 0x004 /* Show collection times */ +#define H_DEBUG_LOOP 0x008 /* Show loop parameters */ +#define H_DEBUG_COMPILE 0x010 /* Show assembly info */ +#define H_DEBUG_OLT 0x020 /* Show all test info */ + +#define H_DEBUG_RAW_OUT 0x100 /* diagnostic output */ +#define H_DEBUG_RAW_IN 0x200 /* diagnostic input */ +#define H_DEBUG_TEST_IN 0x400 /* input test data */ +/** + * Initialization parameters. Use non-zero values to override default values. + * Notes: + * + * 1) Correspondence between provided value and value of H_PTR members are: + * ioSz <==> i_readSz, collectSize <==> i_collectSz, nCores <==> n_cores, + * options <==> havege_opts + * 2) ioSz is specified in bytes. collectSize sizes is specified as number + * of H_UINT. The default for ioSz is 1024*sizeof(H_UINT). The default + * for collecSize is 128K * sizeof(H_UINT). + * 3) The icacheSize and dcacheSize override cache sizes. Both are specified in KB. + * Either may be specified to override the tuning value. If both are provided, + * tuning code is bypassed. The fallback tuning values can be overridden + * by defining GENERIC_DCACHE and GENERIC_ICACHE (16 will be used if not + * otherwise defined) + * 4) null callback values suppress the function. + * 5) sysFs default is '/sys', procFs default is '/proc'. + * 6) testSpec same as haveged option "[t<x>][c<x>] x=[a[n][w]][b[w]]". If + * not specified (NULL) the default is "ta8b" - i.e. run the tot tests + */ +typedef struct { + H_UINT ioSz; /* size of write buffer */ + H_UINT collectSize; /* size of collection buffer */ + H_UINT icacheSize; /* Instruction cache size */ + H_UINT dcacheSize; /* Data cache size */ + H_UINT options; /* Other options */ + H_UINT nCores; /* If multi-core */ + pMeter metering; /* meterming method */ + pMsg msg_out; /* output display method */ + pRawIn injection; /* injection method */ + char *procFs; /* proc mount point override */ + char *sysFs; /* sys mount point override */ + char *testSpec; /* test specification */ +} H_PARAMS; +/** + * Status codes used in the error member of h_anchor + */ +typedef enum { + H_NOERR, /* 00 No error */ + H_NOHANDLE, /* 01 No memory for handle */ + H_NOBUF, /* 02 Output buffer allocation failed */ + H_NOINIT, /* 03 semaphore init failed */ + H_NOCOLLECT, /* 04 H_COLLECT allocation failed */ + H_NOWALK, /* 05 Walk buffer allocation failed */ + H_NOTESTSPEC, /* 06 invalid test specification */ + H_NOTESTINIT, /* 07 test setup failed */ + H_NOTESTMEM, /* 08 Unable to allocate test memory */ + H_NOTESTTOT, /* 09 tot test failed */ + H_NOTESTRUN, /* 10 production test failed */ + H_NOCORES, /* 11 too many cores specified */ + H_NOTASK, /* 12 Unable to create child task */ + H_NOWAIT, /* 13 sem_wait failed */ + H_NOPOST, /* 14 sem_post failed */ + H_NODONE, /* 15 sem_post done failed */ + H_NORQST, /* 16 sem_post request failed */ + H_NOCOMP, /* 17 wait for completion failed */ + H_EXIT, /* 18 Exit signal */ + H_NOTIMER /* 19 timer failed */ +} H_ERR; +/** + * Keep compiler honest + */ +typedef volatile void *H_VOL; +/** + * Anchor for the RNG. Should be read only at devel level and above. + */ +typedef struct h_anchor { + H_UINT *io_buf; /* output buffer */ + char *arch; /* "x86","sparc","ppc","ia64",etc */ + void *cpu; /* information on the cpu */ + void *instCache; /* instruction cache info */ + void *dataCache; /* data cache info */ + pMsg print_msg; /* output display method */ + pMeter metering; /* metering method */ + pRawIn inject; /* Injection diagnostic only */ + H_VOL collector; /* single thread collector */ + H_VOL threads; /* multi thread collectors */ + void *testData; /* online test data */ + void *tuneData; /* tuning data */ + H_UINT error; /* H_ERR enum for status */ + H_UINT havege_opts; /* option flags */ + H_UINT i_maxidx; /* maximum instruction loop index */ + H_UINT i_maxsz; /* maximum code size */ + H_UINT i_idx; /* code index used */ + H_UINT i_sz; /* code size used */ + H_UINT i_collectSz; /* size of collection buffer */ + H_UINT i_readSz; /* size of read buffer (bytes) */ + H_UINT m_sz; /* size of thread ipc area (bytes) */ + H_UINT n_cores; /* number of cores */ + H_UINT n_fills; /* number of buffer fills */ +} *H_PTR; +/** + * Fail/Success counters for tot and production tests. + */ +typedef enum { + H_OLT_TOT_A_F, /* tot Procedure A failed */ + H_OLT_TOT_A_P, /* tot Procedure A passed */ + H_OLT_TOT_B_F, /* tot Procedure B failed */ + H_OLT_TOT_B_P, /* tot Procedure B passed */ + H_OLT_PROD_A_F, /* prod Procedure A failed */ + H_OLT_PROD_A_P, /* prod Procedure A passed */ + H_OLT_PROD_B_F, /* prod Procedure B failed */ + H_OLT_PROD_B_P /* prod Procedure B passed */ +} H_OLT_METERS; +/** + * Structure used to query RNG anchor settings for information not exposed by + * H_PTR. List formats are strings with one or more tokens separated by space. + * Sources lists show how tuning parameters are derived. D is a build default, + * P is a run time override, items V* come from linux virtual file system, + * other items trace various cpuid sources. Tuning is skipped if both cache + * sizes have 'P' sources. + * + * Notes: + * + * 1) Build: package version of source + * 2) Build options: compiler version followed by build configuration encoded + * as string of: [C][I][M][T][V] where C=clock_gettime, I=tune with cpuid, + * M=multi-core, T=online-test, V=tune with vfs + * 3) Tuning source lists: D=default, P=parameter, C=cpuid present, + * H=hyperthreading, A=AMD cpuid, A5=AMD fn5, A6=AMD fn6, A8=AMD fn8 + * L2=Intel has leaf2, L4=Intel has leaf4, B=Intel leaf b, + * 4=intel leaf4, V=virtual file system available + * VS=/sys/devices/system/cpu/cpu%d/cache/index<n>/level, + * VO=/sys/devices/system/cpu/online, VI=/proc/cpuinfo + * VC=/sys/devices/system/cpu + * 4) test spec [A[1..8]][B], see H_PARAMS above. + * 5) zero unless tests are enabled + * 6) Last Coron's entropy estimate from Procedure B, test 8 + */ +typedef struct h_status { + const char *version; /* Package version [1] */ + const char *buildOptions; /* Options [2] */ + const char *vendor; /* cpuid machines only */ + const char *cpuSources; /* Tuning sources list [3] */ + const char *i_cacheSources; /* Tuning sources list [3] */ + const char *d_cacheSources; /* Tuning sources list [3] */ + const char *tot_tests; /* tot test spec [4] */ + const char *prod_tests; /* prod test spec [4] */ + H_UINT i_cache; /* size of L1 instruction cache KB */ + H_UINT d_cache; /* size of L1 data cache KB */ + H_UINT n_tests[H_OLT_PROD_B_P+1]; /* test statistics [5] */ + double last_test8; /* last test8 result [6] */ +} *H_STATUS; +/** + * Standard presentation formats for havege_status_dump. + */ +typedef enum { + H_SD_TOPIC_BUILD, +/* ver: %s; arch: %s; vend: %s; build: (%s); collect: %dK */ + H_SD_TOPIC_TUNE, +/* cpu: (%s); data: %dK (%s); inst: %dK (%s); idx: %d/%d; sz: %d/%d */ + H_SD_TOPIC_TEST, +/* [tot tests (%s): A:%d/%d B: %d/%d;][continuous tests (%s): A:%d/%d B: %d/%d;][last entropy estimate %g] */ + H_SD_TOPIC_SUM, +/* fills: %d, generated: %.4g %c bytes */ +} H_SD_TOPIC; +/** + * Public prototypes. Library users note that "havege_*" is reserved for library + * public functions. Note that the multi-core option is experimental and must + * enabled in the build. + */ +/** + * Create an anchor. The caller should check for a non-null return value with + * a error value of H_NOERR. Any non-null return should be disposed of by a + * call to havege_destroy() to free all allocated resources. + * + * Possible error values: H_NOERR, H_NOTESTSPEC, H_NOBUF, H_NOTESTMEM, + * H_NOINIT + */ +H_PTR havege_create(H_PARAMS *params); + +/** + * haveger_create() remembers parent pid and uses it to identify deallocating thread. + * daemonize() forks parent and effectively loses parent thread. + * havege_reparent(void) allows recovering new parent pid before havege_run() is started. + */ +void havege_reparent(H_PTR hptr); + +/** + * Frees all allocated anchor resources. If the multi-core option is used, this + * method should be called from a signal handler to prevent zombie processes. + * If called by the process that called haveged_create(), hptr will be freed + * when all child processes (if any) have terminated. If called by a child + * process, H_EXIT will be set and all children awakened to exit. + */ +void havege_destroy(H_PTR hptr); +/** + * Read random words from an active anchor. The RNG must have been readied + * by a previous call to havege_run(). The read must take place within the + * allocated buffer, hptr->io_buf, and the range is specified in number of + * H_UINT to read. If the multi-core option is used, this buffer is + * memory-mapped between collectors. + * + * Returns the number of H_UINT read. + * + * Possible error values: H_NOERR, H_NOTESRUN, H_NOPOST, H_NODONE, + * H_NORQST, H_NOCOMP, H_EXIT + */ +int havege_rng(H_PTR hptr, H_UINT *buf, H_UINT sz); +/** + * Warm up the RNG and run the start-up tests. The operation suceeded if the + * error member of the handle is H_NOERR. A failed handle should be disposed + * of by a call to havege_destroy(). + * + * Returns non-zero on failure. + * + * Possible error values: H_NOERR, H_NOCOLLECT, H_NOWALK, H_NOTESTMEM, + * H_NOTASK, H_NOTESTTOT, H_NOWAIT, + * any havege_rng error + */ +int havege_run(H_PTR hptr); +/** + * Fill in the h_status structure with read-only information collected from + * the package build, run-time tuning, and test components. + */ +void havege_status(H_PTR hptr, H_STATUS hsts); +/** + * Call havege_status() and generate a standard presentation of H_STATUS content. + * See the H_SD_TOPIC enum above for the formats. + * + * Returns the number of bytes placed in buf. + */ +int havege_status_dump(H_PTR hptr, H_SD_TOPIC topic, char *buf, size_t len); +/** + * 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); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/havegecmd.c b/src/havegecmd.c new file mode 100644 index 0000000..9ced105 --- /dev/null +++ b/src/havegecmd.c @@ -0,0 +1,460 @@ +/** + ** Provide HAVEGE socket communication API + ** + ** Copyright 2018-2021 Jirka Hladky hladky DOT jiri AT gmail DOT com + ** Copyright 2018 Werner Fink <werner@suse.de> + ** + ** 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/>. + ** + */ + +#include "config.h" + +#include <stddef.h> +#include <stdint.h> + +#ifndef NO_COMMAND_MODE +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <unistd.h> + +#ifndef HAVE_STRUCT_UCRED +struct ucred +{ + pid_t pid; /* PID of sending process. */ + uid_t uid; /* UID of sending process. */ + gid_t gid; /* GID of sending process. */ +}; +#endif + +#include "havegecmd.h" + +int first_byte; +int socket_fd; +static char errmsg[1024]; + +static int new_root( /* RETURN: status */ + const char *root, /* IN: path of the new root file system */ + const volatile char *path, /* IN: path of the haveged executable */ + char *const argv[], /* IN: arguments for the haveged process */ + struct pparams *params) /* IN: input params */ +{ + int ret; + struct stat st; + const char *realtive = (const char*)&path[0]; + + ret = chdir(root); + if (ret < 0) { + snprintf(&errmsg[0], sizeof(errmsg)-1, + "can't change to working directory %s: %s\n", + root, strerror(errno)); + goto err; + } + if (path[0] == '/') + realtive++; + ret = fstatat(AT_FDCWD, realtive, &st, 0); + if (ret < 0) { + snprintf(&errmsg[0], sizeof(errmsg)-1, + "can't restart %s: %s\n", + path, strerror(errno)); + goto err; + } + ret = chroot("."); + if (ret < 0) { + snprintf(&errmsg[0], sizeof(errmsg)-1, + "can't change root directory %s\n", + strerror(errno)); + goto err; + } + ret = chdir("/"); + if (ret < 0) { + snprintf(&errmsg[0], sizeof(errmsg)-1, + "can't change to working directory / : %s\n", + strerror(errno)); + goto err; + } + ret = execv((const char *)path, argv); + if (ret < 0) { + snprintf(&errmsg[0], sizeof(errmsg)-1, + "can't restart %s: %s\n", + path, strerror(errno)); + } +err: + if (ret < 0) + print_msg("%s", errmsg); + return ret; +} + +/** + * Open and listen on a UNIX socket to get command from there + */ +int cmd_listen( /* RETURN: UNIX socket file descriptor */ + struct pparams *params) /* IN: input params */ +{ + struct sockaddr_un su = { /* The abstract UNIX socket of haveged */ + .sun_family = AF_UNIX, + .sun_path = HAVEGED_SOCKET_PATH, + }; + const int one = 1; + int fd, ret; + + fd = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) { + print_msg("%s: can not open UNIX socket\n", params->daemon); + goto err; + } + + ret = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, (socklen_t)sizeof(one)); + if (ret < 0) { + close(fd); + fd = -1; + print_msg("%s: can not set option for UNIX socket\n", params->daemon); + goto err; + } + + ret = bind(fd, (struct sockaddr *)&su, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(su.sun_path+1)); + if (ret < 0) { + close(fd); + fd = -1; + if (errno != EADDRINUSE) + print_msg("%s: can not bind a name to UNIX socket\n", params->daemon); + else + fd = -2; + goto err; + } + + ret = listen(fd, SOMAXCONN); + if (ret < 0) { + close(fd); + fd = -1; + print_msg("%s: can not listen on UNIX socket\n", params->daemon); + goto err; + } +err: + return fd; +} + +/** + * Open and connect on a UNIX socket to send command over this + */ +int cmd_connect( /* RETURN: UNIX socket file descriptor */ + struct pparams *params) /* IN: input params */ +{ + struct sockaddr_un su = { /* The abstract UNIX socket of haveged */ + .sun_family = AF_UNIX, + .sun_path = HAVEGED_SOCKET_PATH, + }; + const int one = 1; + int fd, ret; + + fd = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) { + print_msg("%s: can not open UNIX socket\n", params->daemon); + goto err; + } + + ret = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, (socklen_t)sizeof(one)); + if (ret < 0) { + print_msg("%s: can not set option for UNIX socket\n", params->daemon); + close(fd); + fd = -1; + goto err; + } + + ret = connect(fd, (struct sockaddr *)&su, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(su.sun_path+1)); + if (ret < 0) { + if (errno != ECONNREFUSED) + print_msg("%s: can not connect on UNIX socket\n", params->daemon); + close(fd); + fd = -1; + goto err; + } +err: + return fd; +} + +/** + * Handle arguments in command mode + */ +int getcmd( /* RETURN: success or error */ + char *arg) /* handle current known commands */ +{ + static const struct { + const char* cmd; + const int req; + const int arg; + const char* opt; + } cmds[] = { + { "root=", MAGIC_CHROOT, 1, NULL }, /* New root */ + { "close", MAGIC_CLOSE, 0, NULL }, /* Close socket */ + {0} + }, *cmd = cmds; + int ret = -1; + + if (!arg || !*arg) + goto err; + + optarg = NULL; + for (; cmd->cmd; cmd++) + if (cmd->arg) { + if (strncmp(cmd->cmd, arg, strlen(cmd->cmd)) == 0) { + optarg = strchr(arg, '='); + optarg++; + ret = cmd->req; + break; + } + } + else { + if (strcmp(cmd->cmd, arg) == 0) { + ret = cmd->req; + break; + } + } +err: + return ret; +} + +/** + * Handle incomming messages from socket + */ +int socket_handler( /* RETURN: closed file descriptor */ + int fd, /* IN: connected socket file descriptor */ + const volatile char *path, /* IN: path of the haveged executable */ + char *const argv[], /* IN: arguments for the haveged process */ + struct pparams *params) /* IN: input params */ +{ + struct ucred cred = {0}; + unsigned char magic[2], *ptr; + char *enqry; + char *optarg = NULL; + socklen_t clen; + int ret = -1, len; + + if (fd < 0) { + print_msg("%s: no connection jet\n", params->daemon); + } + + ptr = &magic[0]; + len = sizeof(magic); + ret = safein(fd, ptr, len); + if (ret < 0) { + print_msg("%s: can not read from UNIX socket\n", params->daemon); + goto out; + } + + if (magic[1] == '\002') { /* ASCII start of text: read argument provided */ + uint32_t alen; + + ret = receive_uinteger(fd, &alen); + if (ret < 0) { + print_msg("%s: can not read from UNIX socket\n", params->daemon); + goto out; + } + + optarg = calloc(alen, sizeof(char)); + if (!optarg) { + print_msg("can not allocate memory for message from UNIX socket"); + goto out; + } + ptr = (unsigned char*)optarg; + + ret = safein(fd, ptr, alen); + if (ret < 0) { + print_msg("%s: can not read from UNIX socket\n", params->daemon); + goto out; + } + } + + clen = sizeof(struct ucred); + ret = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &clen); + if (ret < 0) { + print_msg("%s: can not get credentials from UNIX socket part1\n", params->daemon); + goto out; + } + if (clen != sizeof(struct ucred)) { + print_msg("%s: can not get credentials from UNIX socket part2\n", params->daemon); + goto out; + } + if (cred.uid != 0) { + enqry = ASCII_NAK; + + ptr = (unsigned char *)enqry; + len = (int)strlen(enqry)+1; + safeout(fd, ptr, len); + } + + switch (magic[0]) { + case MAGIC_CHROOT: + enqry = ASCII_ACK; + + ret = new_root(optarg, path, argv, params); + if (ret < 0) { + uint32_t size = strlen(errmsg); + safeout(fd, ASCII_STX, strlen(ASCII_STX)); + send_uinteger(fd, size); + safeout(fd, errmsg, size+1); + break; + } + + ptr = (unsigned char *)enqry; + len = (int)strlen(enqry); + safeout(fd, ptr, len); + + break; + case MAGIC_CLOSE: + enqry = ASCII_ACK; + + close(socket_fd); + socket_fd = -1; + + ptr = (unsigned char *)enqry; + len = (int)strlen(enqry); + safeout(fd, ptr, len); + argv[0][0] = first_byte; + + break; + default: + enqry = ASCII_NAK; + + ptr = (unsigned char *)enqry; + len = (int)strlen(enqry); + safeout(fd, ptr, len); + break; + } +out: + if (optarg) + free(optarg); + if (fd > 0) { + close(fd); + fd = -1; + } + return fd; +} + +/** + * Receive incomming messages from socket + */ +ssize_t safein( /* RETURN: read bytes */ + int fd, /* IN: file descriptor */ + void *ptr, /* OUT: pointer to buffer */ + size_t len) /* IN: size of buffer */ +{ + int saveerr = errno, avail; + ssize_t ret = 0; + + if (len > SSIZE_MAX) + len = SSIZE_MAX; + + ret = ioctl(fd, FIONREAD, &avail); + if (ret < 0 || avail <=0) + goto out; + + if (len > (unsigned int) avail) + len = avail; + + do { + errno = saveerr; + ssize_t p = recv(fd, ptr, len, 0 /* MSG_DONTWAIT */); + if (p < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + print_msg("Unable to read from socket %d: %s", socket_fd, strerror(errno)); + } + ptr = (char *) ptr + p; + ret += p; + len -= p; + } + while (len > 0); +out: + return ret; +} + +/** + * Send outgoing messages to socket + */ +void safeout( /* RETURN: nothing */ + int fd, /* IN: file descriptor */ + const void *ptr, /* IN: pointer to buffer */ + size_t len) /* IN: size of buffer */ +{ + int saveerr = errno; + + do { + ssize_t p = send(fd, ptr, len, MSG_NOSIGNAL); + if (p < 0) { + if (errno == EINTR) + continue; + if (errno == EPIPE || errno == EAGAIN || errno == EWOULDBLOCK) + break; + print_msg("Unable to write to socket %d: %s", fd, strerror(errno)); + } + ptr = (char *) ptr + p; + len -= p; + } + while (len > 0); + + errno = saveerr; +} + +/** + * Send outgoing unsigned integer to socket + */ +void send_uinteger( /* RETURN: nothing */ + int fd, /* IN: file descriptor */ + uint32_t value) /* IN: 32 bit unsigned integer */ +{ + uint8_t buffer[4]; + + buffer[0] = (uint8_t)((value >> 24) & 0xFF); + buffer[1] = (uint8_t)((value >> 16) & 0xFF); + buffer[2] = (uint8_t)((value >> 8) & 0xFF); + buffer[3] = (uint8_t)((value >> 0) & 0xFF); + + safeout(fd, buffer, 4 * sizeof(uint8_t)); +} + +/** + * Receive incomming unsigned integer from socket + */ +int receive_uinteger( /* RETURN: status */ + int fd, /* IN: file descriptor */ + uint32_t *value) /* OUT: 32 bit unsigned integer */ +{ + uint8_t buffer[4]; + + if (safein(fd, buffer, 4 * sizeof(uint8_t)) < 0) + return -1; + + *value = (((uint32_t)buffer[0]) << 24) | + (((uint32_t)buffer[1]) << 16) | + (((uint32_t)buffer[2]) << 8) | + (((uint32_t)buffer[3]) << 0); + + return 0; +} + +#endif diff --git a/src/havegecmd.h b/src/havegecmd.h new file mode 100644 index 0000000..19a1823 --- /dev/null +++ b/src/havegecmd.h @@ -0,0 +1,103 @@ +/** + ** Provide HAVEGE socket communication API + ** + ** Copyright 2018-2021 Jirka Hladky hladky DOT jiri AT gmail DOT com + ** Copyright 2018 Werner Fink <werner@suse.de> + ** + ** 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/>. + ** + */ + +#ifndef HAVEGECMD_H +#define HAVEGECMD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "havege.h" +#include "haveged.h" + +#include <sys/types.h> +#include <sys/socket.h> + +#define HAVEGED_SOCKET_PATH "\0/sys/entropy/haveged" +#define MAGIC_CHROOT 'R' +#define MAGIC_CLOSE 'X' +#define MAGIC_PATH 'P' + +#define ASCII_ACK "\x6" /* ASCII acknowledge */ +#define ASCII_NAK "\x15" /* ASCII negative acknowledge */ +#define ASCII_STX "\x2" /* ASCII start of text */ + +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 0 +#endif + +#ifndef SOCK_NONBLOCK +#define SOCK_NONBLOCK 0 +#endif + +/** + * Open and listen on a UNIX socket to get command from there + */ +int cmd_listen(struct pparams *); + +/** + * Open and connect on a UNIX socket to send command over this + */ +int cmd_connect(struct pparams *); + +/** + * Handle arguments in command mode + */ +int getcmd(char *); + +/** + * Handle incomming messages from socket + */ +int socket_handler(int, const volatile char *, char *const [], struct pparams *); + +/** + * Receive incomming messages from socket + */ +ssize_t safein(int, void *, size_t); + +/** + * Send outgoing messages to socket + */ +void safeout(int, const void *, size_t); + +/** + * Send outgoing unsigned integer to socket + */ +void send_uinteger(int, uint32_t); + +/** + * Receive incomming unsigned integer from socket + */ +int receive_uinteger(int, uint32_t *); + +/** + * Socket file descriptor used for communication + */ + +extern int socket_fd; +extern int first_byte; + +#ifdef __cplusplus +} +#endif + +#endif 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]; +} diff --git a/src/havegecollect.h b/src/havegecollect.h new file mode 100644 index 0000000..55ffbfb --- /dev/null +++ b/src/havegecollect.h @@ -0,0 +1,215 @@ +/** + ** 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/>. + */ +#ifndef HAVEGECOLLECT_H +#define HAVEGECOLLECT_H +/** + ** Definitions needed to build haveged + */ +#include "havege.h" +/** + * The collection context + */ +typedef struct h_collect { + void *havege_app; /* Application block */ + H_UINT havege_idx; /* Identifer */ + H_UINT havege_szCollect; /* Size of collection buffer */ + H_UINT havege_raw; /* RAW mode control flags */ + H_UINT havege_szFill; /* Fill size */ + H_UINT havege_nptr; /* Input pointer */ + pRawIn havege_rawInput; /* Injection function */ + pRawIn havege_testInput; /* Injection function for test */ + H_UINT havege_cdidx; /* normal mode control flags */ + H_UINT *havege_pwalk; /* Instance variable */ + H_UINT havege_andpt; /* Instance variable */ + H_UINT havege_PT; /* Instance variable */ + H_UINT havege_PT2; /* Instance variable */ + H_UINT havege_pt2; /* Instance variable */ + H_UINT havege_PTtest; /* Instance variable */ + H_UINT havege_tic; /* Instance variable */ + H_UINT *havege_tics; /* loop timer noise buffer */ + H_UINT havege_err; /* H_ERR enum for status */ + void *havege_tests; /* opague test context */ + void *havege_extra; /* other allocations */ + H_UINT havege_bigarray[1]; /* collection buffer */ +} volatile H_COLLECT; +/** + ** Compiler intrinsics are used to make the build more portable and stable + ** with fall-backs provided where the intrisics cannot be used. + */ +#ifdef __GNUC__ +/* ################################################################################# */ + +/** + ** For the GNU compiler, the use of a cpuid intrinsic is somewhat garbled by the + ** fact that some distributions (Centos 5.x) carry an empty cpuid.h (in order + ** to back patch glicb?). AFAIK cpuid did not appear in gcc until version 4.3 + ** although it was in existence before. If we do not have a valid cpuid.h, + ** we provide our own copy of the file (from gcc 4.3) + ** + ** Also, gcc 4.4 and later provide an optimize attribute which remedies the + ** effect ever increasing optimization on the collection loop + */ +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + +#define ASM __asm__ volatile +/** + ** For the intel world... + */ +#ifdef HAVE_ISA_X86 +#define ARCH "x86" + +#if GCC_VERSION<40300 +#undef HAVE_CPUID_H +#endif +#ifdef HAVE_CPUID_H +#include <cpuid.h> +#else +#include "cpuid-43.h" +#endif +/** + ** Compatibility wrappers + */ +#define CPUID(level,p)\ + {\ + register int ecx asm ("ecx") = p[2];\ + __cpuid(level,p[0],p[1],p[2],p[3]);\ + (void) ecx;\ + } +#define HASCPUID(p) __get_cpuid_max(0, p) +/** + ** The rdtsc intrinsic is called in by x86intrin.h - also a recent gcc innovation + ** There have been some discussions of the code in 4.5 and 4.6, so you may opt + ** to use the inline alternative based on GCC_VERSION + */ +#ifdef HAVE_X86INTRIN_H +#include <x86intrin.h> +#endif +#ifdef HAVE___RDTSC +#define HARDCLOCK(x) x=__rdtsc() +#else +#define HARDCLOCK(x) ASM("rdtsc;movl %%eax,%0":"=m"(x)::"ax","dx") +#endif +#else +/** + * Outside the x86 family + */ +#ifdef HAVE_ISA_GENERIC +#define ARCH "generic" +#define ENABLE_CLOCKGETTIME 1 +#endif + +#ifdef HAVE_ISA_IA64 +#define ARCH "ia64" +#define HARDCLOCK(x) ASM("mov %0=ar.itc" : "=r"(x)) +#endif + +#ifdef HAVE_ISA_SPARC +#define ARCH "sparc" +#define HARDCLOCK(x) ASM("rd %%tick, %0":"=r"(x):"r"(x)) +#endif + +#ifdef HAVE_ISA_SPARCLITE +#define ARCH "sparclite" +#define HARDCLOCK(x) ASM(".byte 0x83, 0x41, 0x00, 0x00");\ + ASM("mov %%g1, %0" : "=r"(x)) +#endif + +#ifdef HAVE_ISA_PPC +#define ARCH "ppc" +#define HARDCLOCK(x) ASM("mftb %0":"=r"(x)) /* eq. to mftb %0, 268 */ +#endif + +#ifdef HAVE_ISA_S390 +#define ARCH "s390" +#define HARDCLOCK(x) { unsigned long long tsc; ASM("stck %0":"=Q"(tsc)::"cc"); x = (unsigned int)tsc; } +#endif +/** + * /Outside the x86 family + */ +#endif +/** + * Use the "&&" extension to calculate the LOOP_PT + */ +#define CODE_PT(a) a +#define LOOP_PT(a) &&loop##a + +/* ################################################################################# */ +#endif +/** + * For the MSVC world + */ +#if _MSVC_VERS +/* ################################################################################# */ +#define ARCH "x86" +/** + * For the MSVC compilers V8 and above + */ +#include <intrin.h> +/** + * Read the processor timestamp counter + */ +#define HARDCLOCK(x) x=__rdtsc() +/** + * Normalize to the gcc interface + */ +#define CPUID(level,p) return __cpuidx(p, level, p[2]) +#define HASCPUID(p) \ + { \ + CPUID(0,a,b,c,d) \ + } +/** + * Use the __ReturnAddress intrinsic to calculate the LOOP_PT + */ +#define CODE_PT(a) __ReturnAddress() +#define LOOP_PT(a) 0 +#endif +/* ################################################################################# */ +/** + * Configuration defaults - allow override at compile + */ +#ifndef COLLECT_BUFSIZE +#define COLLECT_BUFSIZE 128 /* collection buffer size in KW */ +#endif +#ifndef GENERIC_DCACHE +#define GENERIC_DCACHE 16 /* size of L1 data cache */ +#endif +#ifndef GENERIC_ICACHE +#define GENERIC_ICACHE 16 /* size of L1 instruction cache */ +#endif +#ifndef LOOP_CT +#define LOOP_CT 40 /* Max interations per collection loop */ +#endif +/** + * Other useful definitions + */ +#define BITS_PER_H_UINT (8*sizeof(H_UINT)) /* Bit packing constant */ +#define DEFAULT_BUFSZ 1024*sizeof(H_UINT) /* Default for ioSz */ +#define MININITRAND 32 /* Number of initial fills to prime RNG */ +#define NDSIZECOLLECT (COLLECT_BUFSIZE*1024) /* Collection size: 128K*H_UINT = .5M byte */ +/** + ** The public collection interface + */ +H_COLLECT *havege_ndcreate(H_PTR hptr, H_UINT nCollector); +void havege_nddestroy(H_COLLECT *rdr); +H_UINT havege_ndread(H_COLLECT *rdr); +void havege_ndsetup(H_PTR hptr); + +#endif diff --git a/src/haveged.c b/src/haveged.c new file mode 100644 index 0000000..b9cb77b --- /dev/null +++ b/src/haveged.c @@ -0,0 +1,1008 @@ +/** + ** 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/>. + */ +#include "config.h" +#include <sys/auxv.h> +#include <stdlib.h> +#include <stdio.h> +#include <getopt.h> +#include <string.h> +#include <stdarg.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#ifndef NO_DAEMON +#include <syslog.h> +#include <sys/ioctl.h> +#include <asm/types.h> +#include <linux/random.h> +#endif + +#ifndef NO_COMMAND_MODE +#include "havegecmd.h" +#include <limits.h> +#endif + +#include <errno.h> +#include "haveged.h" +#include "havegecollect.h" +/** + * stringize operators for maintainable text + */ +#define STRZ(a) #a +#define SETTINGL(msg,val) STRZ(val) msg +#define SETTINGR(msg,val) msg STRZ(val) + +// {{{ VERSION_TEXT +static const char* VERSION_TEXT = + "haveged %s\n\n" + "Copyright (C) 2018-2021 Jirka Hladky <hladky.jiri@gmail.com>\n" + "Copyright (C) 2009-2014 Gary Wuertz <gary@issiweb.com>\n" + "Copyright (C) 2011-2012 BenEleventh Consulting <manolson@beneleventh.com>\n\n" + "License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n"; +// }}} VERSION_TEXT + +/** + * Parameters + */ +static struct pparams defaults = { + .daemon = PACKAGE, + .exit_code = 1, + .setup = 0, + .ncores = 0, + .buffersz = 0, + .detached = 0, + .foreground = 0, + .d_cache = 0, + .i_cache = 0, + .run_level = 0, + .low_water = 0, + .tests_config = 0, + .os_rel = "/proc/sys/kernel/osrelease", + .pid_file = PID_DEFAULT, + .poolsize = "/proc/sys/kernel/random/poolsize", + .random_device = "/dev/random", + .sample_in = INPUT_DEFAULT, + .sample_out = OUTPUT_DEFAULT, + .verbose = 0, + .watermark = "/proc/sys/kernel/random/write_wakeup_threshold", + .command = 0 + }; +struct pparams *params = &defaults; + +#ifdef RAW_IN_ENABLE +FILE *fd_in; +/** + * The injection diagnostic + */ +static int injectFile(volatile H_UINT *pData, H_UINT szData); +#endif +/** + * havege instance used by application + */ +static H_PTR handle = NULL; +/** + * Local prototypes + */ +#ifndef NO_DAEMON +static H_UINT poolSize = 0; + +static void daemonize(void); +static int get_poolsize(void); +static void run_daemon(H_PTR handle, const volatile char *path, char *const argv[]); +static void set_watermark(int level); +#endif + +static void anchor_info(H_PTR h); +static int get_runsize(unsigned int *bufct, unsigned int *bufrem, char *bp); +static char *ppSize(char *buffer, double sz); + +static void run_app(H_PTR handle, H_UINT bufct, H_UINT bufres); +static void show_meterInfo(H_UINT id, H_UINT event); +static void tidy_exit(int signum); +static void usage(int db, int nopts, struct option *long_options, const char **cmds); + +static sigset_t mask, omask; + +#define ATOU(a) (unsigned int)atoi(a) +/** + * Entry point + */ +int main(int argc, char **argv) +{ + volatile char *path = strdup(argv[0]); + volatile char *arg0 = argv[0]; + if (path[0] != '/') + path = (char*)getauxval(AT_EXECFN); + static const char* cmds[] = { + "b", "buffer", "1", SETTINGR("Buffer size [KW], default: ",COLLECT_BUFSIZE), + "d", "data", "1", SETTINGR("Data cache size [KB], with fallback to: ", GENERIC_DCACHE ), +#ifndef NO_COMMAND_MODE + "c", "command", "1", "Send a command mode to an already running haveged", +#endif + "i", "inst", "1", SETTINGR("Instruction cache size [KB], with fallback to: ", GENERIC_ICACHE), + "f", "file", "1", "Sample output file, default: '" OUTPUT_DEFAULT "', '-' for stdout", + "F", "Foreground", "0", "Run daemon in foreground", + "r", "run", "1", "0=daemon, 1=config info, >1=<r>KB sample", + "n", "number", "1", "Output size in [k|m|g|t] bytes, 0 = unlimited to stdout", + "o", "onlinetest", "1", "[t<x>][c<x>] x=[a[n][w]][b[w]] 't'ot, 'c'ontinuous, default: ta8b", + "p", "pidfile", "1", "daemon pidfile, default: " PID_DEFAULT , + "s", "source", "1", "Injection source file, default: '" INPUT_DEFAULT "', '-' for stdin", +#if NUMBER_CORES>1 + "t", "threads", "1", "Number of threads", +#endif + "v", "verbose", "1", "Verbose mask 0=none,1=summary,2=retries,4=timing,8=loop,16=code,32=test", + "w", "write", "1", "Set write_wakeup_threshold [bits]", + "V", "version", "0", "Print version information and exit", + "h", "help", "0", "This help" + }; + static int nopts = sizeof(cmds)/(4*sizeof(char *)); + struct option long_options[nopts+1]; + char short_options[1+nopts*2]; + int c,i,j; + H_UINT bufct, bufrem, ierr; + H_PARAMS cmd; + + if (havege_version(HAVEGE_PREP_VERSION)==NULL) + error_exit("version conflict %s!=%s", HAVEGE_PREP_VERSION, havege_version(NULL)); +#if NO_DAEMON==1 + params->setup |= RUN_AS_APP; +#endif +#ifdef RAW_IN_ENABLE +#define DIAG_USAGE2 SETTINGL("=inject ticks,", DIAG_RUN_INJECT)\ + SETTINGL("=inject data", DIAG_RUN_TEST) + + params->setup |= INJECT | RUN_AS_APP; +#else +#define DIAG_USAGE2 "" +#endif +#ifdef RAW_OUT_ENABLE +#define DIAG_USAGE1 SETTINGL("=capture,", DIAG_RUN_CAPTURE) + + params->setup |= CAPTURE | RUN_AS_APP; +#else +#define DIAG_USAGE1 "" +#endif +#if NUMBER_CORES>1 + params->setup |= MULTI_CORE; +#endif + + first_byte = arg0[0]; + if (access("/etc/initrd-release", F_OK) >= 0) { + arg0[0] = '@'; + path[0] = '/'; + } +#ifdef SIGHUP + signal(SIGHUP, tidy_exit); +#endif + signal(SIGINT, tidy_exit); + signal(SIGTERM, tidy_exit); +#ifndef NO_COMMAND_MODE + signal(SIGPIPE, SIG_IGN); +#endif + + sigemptyset(&mask); +#ifdef SIGHUP + sigaddset(&mask, SIGHUP); +#endif + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); +#ifndef NO_COMMAND_MODE + sigaddset(&mask, SIGPIPE); +#endif +#if NUMBER_CORES>1 + /* Hmmm ... currently the code does not use pthread_create(3) but fork(2) */ + pthread_sigmask(SIG_UNBLOCK, &mask, NULL); +#else + sigprocmask(SIG_UNBLOCK, &mask, NULL); +#endif + strcpy(short_options,""); + bufct = bufrem = 0; + /** + * Build options + */ + for(i=j=0;j<(nopts*4);j+=4) { + switch(cmds[j][0]) { + case 'o': +#ifdef ONLINE_TESTS_ENABLE + break; +#else + continue; +#endif + case 'r': +#if defined(RAW_IN_ENABLE) || defined (RAW_OUT_ENABLE) + if (0!=(params->setup & (INJECT|CAPTURE))) { + params->daemon = "havege_diagnostic"; + cmds[j+3] = "run level, 0=diagnostic off,1=config info," DIAG_USAGE1 DIAG_USAGE2 ; + } + else +#endif + if (0!=(params->setup & RUN_AS_APP)) + continue; + break; + case 's': + if (0 == (params->setup & INJECT)) + continue; + break; + case 't': + if (0 == (params->setup & MULTI_CORE)) + continue; + break; + case 'p': case 'w': case 'F': + if (0 !=(params->setup & RUN_AS_APP)) + continue; + break; + } + long_options[i].name = cmds[j+1]; + long_options[i].has_arg = atoi(cmds[j+2]); + long_options[i].flag = NULL; + long_options[i].val = cmds[j][0]; + strcat(short_options,cmds[j]); + if (long_options[i].has_arg!=0) strcat(short_options,":"); + i += 1; + } + memset(&long_options[i], 0, sizeof(struct option)); + + do { + c = getopt_long (argc, argv, short_options, long_options, NULL); + switch(c) { + case 'F': + params->setup |= RUN_IN_FG; + params->foreground = 1; + break; + case 'b': + params->buffersz = ATOU(optarg) * 1024; + if (params->buffersz<4) + error_exit("invalid size %s", optarg); + break; +#ifndef NO_COMMAND_MODE + case 'c': + params->command = optarg; + params->setup |= CMD_MODE; + break; +#endif + case 'd': + params->d_cache = ATOU(optarg); + break; + case 'i': + params->i_cache = ATOU(optarg); + break; + case 'f': + params->sample_out = optarg; + if (strcmp(optarg,"-") == 0 ) + params->setup |= USE_STDOUT; + break; + case 'n': + if (get_runsize(&bufct, &bufrem, optarg)) + error_exit("invalid count: %s", optarg); + params->setup |= RUN_AS_APP|RANGE_SPEC; + if (bufct==0 && bufrem==0) + params->setup |= USE_STDOUT; /* ugly but documented behavior! */ + break; + case 'o': + params->tests_config = optarg; + break; + case 'p': + params->pid_file = optarg; + break; + case 'r': + params->run_level = ATOU(optarg); + if (params->run_level != 0) + params->setup |= RUN_AS_APP; + break; + case 's': + params->sample_in = optarg; + break; + case 't': + params->ncores = ATOU(optarg); + if (params->ncores > NUMBER_CORES) + error_exit("invalid thread count: %s", optarg); + break; + case 'v': + params->verbose = ATOU(optarg); + break; + case 'w': + params->setup |= SET_LWM; + params->low_water = ATOU(optarg); + break; + case '?': + case 'h': + usage(0, nopts, long_options, cmds); + /* fallthrough */ + case 'V': + printf(VERSION_TEXT, HAVEGE_PREP_VERSION); + exit(EXIT_SUCCESS); + case -1: + break; + } + } while (c!=-1); +#ifndef NO_COMMAND_MODE + if (params->setup & CMD_MODE) { + int ret = 0, len; + uint32_t size; + char *ptr, answer[6], cmd[2]; + fd_set read_fd; + sigset_t block; + + socket_fd = cmd_connect(params); + if (socket_fd < 0) { + ret = -1; + goto err; + } + cmd[0] = getcmd(params->command); + if (cmd[0] < 0) { + ret = -1; + goto err; + } + cmd[1] = '\0'; + switch (cmd[0]) { + char *root; + case MAGIC_CHROOT: + root = optarg; + size = (uint32_t)strlen(root)+1; + cmd[1] = '\002'; + safeout(socket_fd, &cmd[0], 2); + send_uinteger(socket_fd, size); + safeout(socket_fd, root, size); + break; + case MAGIC_CLOSE: + ptr = &cmd[0]; + len = (int)strlen(cmd)+1; + safeout(socket_fd, ptr, len); + break; + case '?': + default: + ret = -1; + break; + } + answer[0] = '\0'; + ptr = &answer[0]; + + sigemptyset(&block); + sigaddset(&block, SIGPIPE); + + FD_ZERO(&read_fd); + FD_SET(socket_fd, &read_fd); + + do { + struct timeval two = {6, 0}; + ret = select(socket_fd+1, &read_fd, NULL, NULL, &two); + if (ret >= 0) break; + if (errno != EINTR) + error_exit("Select error: %s", strerror(errno)); + } + while (1); + + ret = safein(socket_fd, ptr, 1); + if (ret < 0) + goto err; + switch (answer[0]) { + case '\002': { + char *msg; + ret = receive_uinteger(socket_fd, &size); + if (ret < 0) + goto err; + msg = calloc(size, sizeof(char)); + if (!msg) + error_exit("can not allocate memory for message from UNIX socket: %s", + strerror(errno)); + ret = safein(socket_fd, msg, size); + if (ret < 0) + goto err; + fprintf(stderr, "%s: %s", params->daemon, msg); + free(msg); + ret = -1; + } + break; + case '\x6': + ret = 0; + break; + case '\x15': + default: + ret = -1; + break; + } + err: + close(socket_fd); + return ret; + } + else { + socket_fd = cmd_listen(params); + if (socket_fd >= 0) + fprintf(stderr, "%s: command socket is listening at fd %d\n", params->daemon, socket_fd); + else { + if (socket_fd == -2) { + fprintf(stderr, "%s: command socket already in use\n", params->daemon); + error_exit("%s: please check if there is another instance of haveged running\n", params->daemon); + } else { + fprintf(stderr, "%s: can not initialize command socket: %s\n", params->daemon, strerror(errno)); + fprintf(stderr, "%s: disabling command mode for this instance\n", params->daemon); + } + } + } +#endif + if (params->tests_config == 0) + params->tests_config = (0 != (params->setup & RUN_AS_APP))? TESTS_DEFAULT_APP : TESTS_DEFAULT_RUN; + memset(&cmd, 0, sizeof(H_PARAMS)); + cmd.collectSize = params->buffersz; + cmd.icacheSize = params->i_cache; + cmd.dcacheSize = params->d_cache; + cmd.options = params->verbose & 0xff; + cmd.nCores = params->ncores; + cmd.testSpec = params->tests_config; + cmd.msg_out = print_msg; + if (0 != (params->setup & RUN_AS_APP)) { + cmd.ioSz = APP_BUFF_SIZE * sizeof(H_UINT); + if (params->verbose!=0 && 0==(params->setup & RANGE_SPEC)) + params->run_level = 1; + } +#ifndef NO_DAEMON + else { + poolSize = get_poolsize(); + i = (poolSize + 7)/8 * sizeof(H_UINT); + cmd.ioSz = sizeof(struct rand_pool_info) + i *sizeof(H_UINT); + } +#endif + if (0 != (params->verbose & H_DEBUG_TIME)) + cmd.metering = show_meterInfo; + + if (0 !=(params->setup & CAPTURE) && 0 != (params->run_level == DIAG_RUN_CAPTURE)) + cmd.options |= H_DEBUG_RAW_OUT; +#ifdef RAW_IN_ENABLE + if (0 !=(params->setup & INJECT) && 0 != (params->run_level & (DIAG_RUN_INJECT|DIAG_RUN_TEST))) { + if (strcmp(params->sample_in,"-") == 0 ) + fd_in = stdin; + else fd_in = fopen(params->sample_in, "rb"); + if (NULL == fd_in) + error_exit("Unable to open: %s", params->sample_in); + cmd.injection = injectFile; + if (params->run_level==DIAG_RUN_INJECT) + cmd.options |= H_DEBUG_RAW_IN; + else if (params->run_level==DIAG_RUN_TEST) + cmd.options |= H_DEBUG_TEST_IN; + else usage(1, nopts, long_options, cmds); + } +#endif + handle = havege_create(&cmd); + ierr = handle==NULL? H_NOHANDLE : handle->error; + switch(ierr) { + case H_NOERR: + break; + case H_NOTESTSPEC: + error_exit("unrecognized test setup: %s", cmd.testSpec); + break; + default: + error_exit("Couldn't initialize haveged (%d)", ierr); + } + if (0 != (params->setup & RUN_AS_APP)) { + if (params->run_level==1) + anchor_info(handle); + else if (0==(params->setup&(INJECT|CAPTURE))) { + /* must specify range with --number or --run > 1 but not both */ + if (params->run_level>1) { + if (0==(params->setup&RANGE_SPEC)) { /* --run specified */ + bufct = params->run_level/sizeof(H_UINT); + bufrem = (params->run_level%sizeof(H_UINT))*1024; + } + else usage(2, nopts, long_options, cmds); /* both specified */ + } + else if (0==(params->setup&RANGE_SPEC)) + usage(3,nopts, long_options, cmds); /* neither specified */ + else if (0==(params->setup&USE_STDOUT)&&(bufct+bufrem)==0) + usage(4, nopts, long_options, cmds); /* only with stdout */ + run_app(handle, bufct, bufrem); + } + else if (0==(params->setup&USE_STDOUT)&&(bufct+bufrem)==0) + usage(5, nopts, long_options, cmds); /* only with stdout */ + else run_app(handle, bufct, bufrem); + } +#ifndef NO_DAEMON + else run_daemon(handle, path, argv); +#endif + havege_destroy(handle); + exit(0); +} +#ifndef NO_DAEMON +/** + * The usual daemon setup + */ +static void daemonize( /* RETURN: nothing */ + void) /* IN: nothing */ +{ + FILE *fh; + openlog(params->daemon, LOG_CONS, LOG_DAEMON); + syslog(LOG_NOTICE, "%s starting up", params->daemon); + if (daemon(0, 0) == -1) + error_exit("Cannot fork into the background"); + fh = fopen(params->pid_file, "w"); + if (!fh) + error_exit("Couldn't open PID file \"%s\" for writing: %s.", params->pid_file, strerror(errno)); + fprintf(fh, "%i", getpid()); + fclose(fh); + params->detached = 1; +} +/** + * Get configured poolsize + */ +static int get_poolsize( /* RETURN: number of bits */ + void) /* IN: nothing */ +{ + FILE *poolsize_fh,*osrel_fh; + unsigned int major,minor; + int max_bits; + + poolsize_fh = fopen(params->poolsize, "rb"); + if (poolsize_fh) { + if (fscanf(poolsize_fh, "%d", &max_bits)!=1) + max_bits = -1; + fclose(poolsize_fh); + osrel_fh = fopen(params->os_rel, "rb"); + if (osrel_fh) { + if (fscanf(osrel_fh,"%u.%u", &major, &minor)<2) + major = minor = 0; + fclose(osrel_fh); + if (major==2 && minor==4) max_bits *= 8; + } + } + else max_bits = -1; + if (max_bits < 1) + error_exit("Couldn't get poolsize"); + return max_bits; +} +/** + * Run as a daemon writing to random device entropy pool + */ +static void run_daemon( /* RETURN: nothing */ + H_PTR h, /* IN: app instance */ + const volatile char *path, + char *const argv[]) +{ + int random_fd = -1; +#ifndef NO_COMMAND_MODE + int conn_fd = -1; +#endif + struct rand_pool_info *output; + struct stat stat_buf; + + if (0 != params->run_level) { + anchor_info(h); + return; + } + if (params->foreground==0) { + daemonize(); + havege_reparent(handle); + } + else printf ("%s starting up\n", params->daemon); + if (0 != havege_run(h)) + error_exit("Couldn't initialize HAVEGE rng %d", h->error); + if (0 != (params->verbose & H_DEBUG_INFO)) + anchor_info(h); + if (params->low_water>0) + set_watermark(params->low_water); + if ( lstat(params->random_device, &stat_buf) != 0 ) + error_exit("lstat has failed for the random device \"%s\": %s", params->random_device, strerror(errno)); + if ( S_ISLNK(stat_buf.st_mode) ) + error_exit("random device \"%s\" is a link. This is not supported for the security reasons.", params->random_device); + random_fd = open(params->random_device, O_RDWR); + if (random_fd == -1) + error_exit("Couldn't open random device: %s", strerror(errno)); + + output = (struct rand_pool_info *) h->io_buf; + +#if NUMBER_CORES>1 + pthread_sigmask(SIG_BLOCK, &mask, &omask); +#else + sigprocmask(SIG_BLOCK, &mask, &omask); +#endif + for(;;) { + int current,nbytes,r,max=0; + fd_set write_fd; +#ifndef NO_COMMAND_MODE + fd_set read_fd; +#endif + + if (params->exit_code > 128) + error_exit("Stopping due to signal %d\n", params->exit_code - 128); + + FD_ZERO(&write_fd); +#ifndef NO_COMMAND_MODE + if (socket_fd >= 0) { + FD_ZERO(&read_fd); + } +#endif + FD_SET(random_fd, &write_fd); + if (random_fd > max) + max = random_fd; +#ifndef NO_COMMAND_MODE + if (socket_fd >= 0) { + FD_SET(socket_fd, &read_fd); + if (socket_fd > max) + max = socket_fd; + if (conn_fd >= 0) { + FD_SET(conn_fd, &read_fd); + if (conn_fd > max) + max = conn_fd; + } + } +#endif + for(;;) { + struct timespec two = {2, 0}; + int rc; +#ifndef NO_COMMAND_MODE + if (socket_fd >= 0) { + rc = pselect(max+1, &read_fd, &write_fd, NULL, &two, &omask); + } else { + rc = pselect(max+1, NULL, &write_fd, NULL, &two, &omask); + } +#else + rc = pselect(max+1, NULL, &write_fd, NULL, &two, &omask); +#endif + if (rc >= 0) break; + if (params->exit_code > 128) + break; + if (errno != EINTR) + error_exit("Select error: %s", strerror(errno)); + } + if (params->exit_code > 128) + continue; + +#ifndef NO_COMMAND_MODE + if ( socket_fd >= 0 && FD_ISSET(socket_fd, &read_fd) && conn_fd < 0) { +# ifdef HAVE_ACCEPT4 + conn_fd = accept4(socket_fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); + if (conn_fd < 0 && (errno == ENOSYS || errno == ENOTSUP)) { +# endif + conn_fd = accept(socket_fd, NULL, NULL); + if (conn_fd >= 0) { + fcntl(conn_fd, F_SETFL, O_NONBLOCK); + fcntl(conn_fd, F_SETFD, FD_CLOEXEC); + } +# ifdef HAVE_ACCEPT4 + } +# endif + if (conn_fd >= 0) + continue; + } + + if (conn_fd >= 0 && FD_ISSET(conn_fd, &read_fd)) + conn_fd = socket_handler(conn_fd, path, argv, params); +#endif + if (!FD_ISSET(random_fd, &write_fd)) + continue; + + if (ioctl(random_fd, RNDGETENTCNT, ¤t) == -1) + error_exit("Couldn't query entropy-level from kernel"); + /* get number of bytes needed to fill pool */ + nbytes = (poolSize - current)/8; + if(nbytes<1) continue; + /* get that many random bytes */ + r = (nbytes+sizeof(H_UINT)-1)/sizeof(H_UINT); + if (havege_rng(h, (H_UINT *)output->buf, r)<1) + error_exit("RNG failed! %d", h->error); + output->buf_size = nbytes; + /* entropy is 8 bits per byte */ + output->entropy_count = nbytes * 8; + if (ioctl(random_fd, RNDADDENTROPY, output) == -1) + error_exit("RNDADDENTROPY failed!"); + } +} +/** + * Set random write threshold + */ +static void set_watermark( /* RETURN: nothing */ + int level) /* IN: threshold */ +{ + FILE *wm_fh; + + if ( (H_UINT) level > (poolSize - 32)) + level = poolSize - 32; + wm_fh = fopen(params->watermark, "w"); + if (wm_fh) { + fprintf(wm_fh, "%d\n", level); + fclose(wm_fh); + } + else if (errno == EACCES) + fprintf(stderr, "No access to %s, can't set watermark (maybe running in a container?)\n", + params->watermark); + else error_exit("Fail:set_watermark()!"); +} +#endif +/** + * Display handle information + */ +static void anchor_info(H_PTR h) +{ + char buf[120]; + H_SD_TOPIC topics[4] = {H_SD_TOPIC_BUILD, H_SD_TOPIC_TUNE, H_SD_TOPIC_TEST, H_SD_TOPIC_SUM}; + int i; + + for(i=0;i<4;i++) + if (havege_status_dump(h, topics[i], buf, sizeof(buf))>0) + print_msg("%s\n", buf); +} +/** + * Bail.... + */ +void error_exit( /* RETURN: nothing */ + const char *format, /* IN: msg format */ + ...) /* IN: varadic args */ +{ + char buffer[4096]; + + va_list ap; + va_start(ap, format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); +#ifndef NO_DAEMON + if (params->detached!=0) { + unlink(params->pid_file); + syslog(LOG_INFO, "%s: %s", params->daemon, buffer); + } + else +#endif + { + fprintf(stderr, "%s: %s\n", params->daemon, buffer); + if (0 !=(params->setup & RUN_AS_APP) && 0 != handle) { + if (havege_status_dump(handle, H_SD_TOPIC_TEST, buffer, sizeof(buffer))>0) + fprintf(stderr, "%s\n", buffer); + if (havege_status_dump(handle, H_SD_TOPIC_SUM, buffer, sizeof(buffer))>0) + fprintf(stderr, "%s\n", buffer); + } + } + havege_destroy(handle); + exit(params->exit_code); +} +/** + * Implement fixed point shorthand for run sizes + */ +static int get_runsize( /* RETURN: the size */ + H_UINT *bufct, /* OUT: nbr app buffers */ + H_UINT *bufrem, /* OUT: residue */ + char *bp) /* IN: the specification */ +{ + char *suffix; + double f; + int p2 = 0; + int p10 = APP_BUFF_SIZE * sizeof(H_UINT); + long long ct; + + + f = strtod(bp, &suffix); + if (f < 0 || strlen(suffix)>1) + return 1; + switch(*suffix) { + case 't': case 'T': + p2 += 1; + /* fallthrough */ + case 'g': case 'G': + p2 += 1; + /* fallthrough */ + case 'm': case 'M': + p2 += 1; + /* fallthrough */ + case 'k': case 'K': + p2 += 1; + /* fallthrough */ + case 0: + break; + default: + return 2; + } + while(p2-- > 0) + f *= 1024; + ct = f; + if (f != 0 && ct==0) + return 3; + if ((double) (ct+1) < f) + return 3; + *bufrem = (H_UINT)(ct%p10); + *bufct = (H_UINT)(ct/p10); + if (*bufct == (ct/p10)) + return 0; + /* hack to allow 16t */ + ct -= 1; + *bufrem = (H_UINT)(ct%p10); + *bufct = (H_UINT)(ct/p10); + return (*bufct == (ct/p10))? 0 : 4; +} +#ifdef RAW_IN_ENABLE +/** + * The injection diagnostic + */ +static int injectFile( /* RETURN: not used */ + volatile H_UINT *pData, /* OUT: data buffer */ + H_UINT szData) /* IN: H_UINT needed */ +{ + int r; + if ((r=fread((void *)pData, sizeof(H_UINT), szData, fd_in)) != szData) + error_exit("Cannot read data in file: %d!=%d", r, szData); + return 0; +} +#endif +/** + * Pretty print the collection size + */ +static char *ppSize( /* RETURN: the formatted size */ + char *buffer, /* IN: work space */ + double sz) /* IN: the size */ +{ + char units[] = {'T', 'G', 'M', 'K', 0}; + double factor = 1024.0 * 1024.0 * 1024.0 * 1024.0; + int i; + + for (i=0;0 != units[i];i++) { + if (sz >= factor) + break; + factor /= 1024.0; + } + snprintf(buffer, 32, "%.4g %c byte", sz / factor, units[i]); + return buffer; +} +/** + * Execution notices - to stderr or syslog + */ +void print_msg( /* RETURN: nothing */ + const char *format, /* IN: format string */ + ...) /* IN: args */ +{ + char buffer[128]; + + va_list ap; + va_start(ap, format); + snprintf(buffer, sizeof(buffer), "%s: %s", params->daemon, format); +#ifndef NO_DAEMON + if (params->detached != 0) + vsyslog(LOG_INFO, buffer, ap); + else +#endif + vfprintf(stderr, buffer, ap); + va_end(ap); +} +/** +* Run as application writing to a file +*/ +static void run_app( /* RETURN: nothing */ + H_PTR h, /* IN: app instance */ + H_UINT bufct, /* IN: # buffers to fill */ + H_UINT bufres) /* IN: # bytes extra */ +{ + H_UINT *buffer; + FILE *fout = NULL; + H_UINT ct=0; + int limits = bufct; + + if (0 != havege_run(h)) + error_exit("Couldn't initialize HAVEGE rng %d", h->error); + if (0 != (params->setup & USE_STDOUT)) { + params->sample_out = "stdout"; + fout = stdout; + } + else if (!(fout = fopen (params->sample_out, "wb"))) + error_exit("Cannot open file <%s> for writing.\n", params->sample_out); + limits = bufct!=0? 1 : bufres != 0; + buffer = (H_UINT *)h->io_buf; +#ifdef RAW_IN_ENABLE + { + char *format, *in="",*out,*sz,*src=""; + + if (params->run_level==DIAG_RUN_INJECT) + in = "tics"; + else if (params->run_level==DIAG_RUN_TEST) + in = "data"; + if (*in!=0) { + src =(fd_in==stdin)? "stdin" : params->sample_in; + format = "Inject %s from %s, writing %s bytes to %s\n"; + } + else format = "Writing %s%s%s bytes to %s\n"; + if (limits) + sz = ppSize((char *)buffer, (1.0 * bufct) * APP_BUFF_SIZE * sizeof(H_UINT) + bufres); + else sz = "unlimited"; + out = (fout==stdout)? "stdout" : params->sample_out; + fprintf(stderr, format, in, src, sz, out); + } +#else + if (limits) + fprintf(stderr, "Writing %s output to %s\n", + ppSize((char *)buffer, (1.0 * bufct) * APP_BUFF_SIZE * sizeof(H_UINT) + bufres), params->sample_out); + else fprintf(stderr, "Writing unlimited bytes to stdout\n"); +#endif + while(!limits || ct++ < bufct) { + if (havege_rng(h, buffer, APP_BUFF_SIZE)<1) + error_exit("RNG failed %d!", h->error); + if (fwrite (buffer, 1, APP_BUFF_SIZE * sizeof(H_UINT), fout) == 0) + error_exit("Cannot write data in file: %s", strerror(errno)); + } + ct = (bufres + sizeof(H_UINT) - 1)/sizeof(H_UINT); + if (ct) { + if (havege_rng(h, buffer, ct)<1) + error_exit("RNG failed %d!", h->error); + if (fwrite (buffer, 1, bufres, fout) == 0) + error_exit("Cannot write data in file: %s", strerror(errno)); + } + fclose(fout); + if (0 != (params->verbose & H_DEBUG_INFO)) + anchor_info(h); +} +/** + * Show collection info. + */ +static void show_meterInfo( /* RETURN: nothing */ + H_UINT id, /* IN: identifier */ + H_UINT event) /* IN: start/stop */ +{ + struct timeval tm; + /* N.B. if multiple thread, each child gets its own copy of this */ + static H_METER status; + + gettimeofday(&tm, NULL); + if (event == 0) + status.estart = ((double)tm.tv_sec*1000.0 + (double)tm.tv_usec/1000.0); + else { + status.etime = ((double)tm.tv_sec*1000.0 + (double)tm.tv_usec/1000.0); + if ((status.etime -= status.estart)<0.0) + status.etime=0.0; + status.n_fill += 1; + print_msg("%d fill %g ms\n", id, status.etime); + } +} +/** + * Signal handler + */ +static void tidy_exit( /* OUT: nothing */ + int signum) /* IN: signal number */ +{ + params->exit_code = 128 + signum; +} + +/** + * send usage display to stderr + */ +static void usage( /* OUT: nothing */ + int loc, /* IN: debugging aid */ + int nopts, /* IN: number of options */ + struct option *long_options, /* IN: long options */ + const char **cmds) /* IN: associated text */ +{ + int i, j; + + (void)loc; + fprintf(stderr, "\nUsage: %s [options]\n\n", params->daemon); +#ifndef NO_DAEMON + fprintf(stderr, "Collect entropy and feed into random pool or write to file.\n"); +#else + fprintf(stderr, "Collect entropy and write to file.\n"); +#endif + fprintf(stderr, " Options:\n"); + for(i=j=0;long_options[i].val != 0;i++,j+=4) { + while(cmds[j][0] != long_options[i].val && (j+4) < (nopts * 4)) + j += 4; + fprintf(stderr," --%-10s, -%c %s %s\n", + long_options[i].name, long_options[i].val, + long_options[i].has_arg? "[]":" ",cmds[j+3]); + } + fprintf(stderr, "\n"); + exit(1); +} diff --git a/src/haveged.h b/src/haveged.h new file mode 100644 index 0000000..6f38c17 --- /dev/null +++ b/src/haveged.h @@ -0,0 +1,100 @@ +/** + ** 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 + ** + ** 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/>. + */ +#ifndef HAVEGED_H +#define HAVEGED_H + +#include "havege.h" +/** + * Settings - default values declared in haveged.c + */ +struct pparams { + char *daemon; /* Daemon name - default is "haveged" */ + H_UINT exit_code; /* Exit code */ + H_UINT setup; /* setup options */ + H_UINT ncores; /* number of cores to use */ + H_UINT buffersz; /* size of collection buffer (kb) */ + H_UINT detached; /* non-zero if daemonized */ + H_UINT foreground; /* non-zero if running in foreground */ + H_UINT run_level; /* type of run 0=daemon,1=setup,2=pip,sample kb */ + H_UINT d_cache; /* size of data cache (kb) */ + H_UINT i_cache; /* size of instruction cache (kb) */ + H_UINT low_water; /* write threshold to set - 0 for none */ + char *tests_config; /* online test configuration */ + char *os_rel; /* path to operating system release */ + char *pid_file; /* name of pid file */ + char *poolsize; /* path to poolsize */ + char *random_device; /* path to random device */ + char *sample_in; /* input path for injection diagnostic */ + char *sample_out; /* path to sample file */ + H_UINT verbose; /* Output level for log or stdout */ + char *version; /* Our version */ + char *watermark; /* path to write_wakeup_threshold */ + char *command; /* command which will be send/received */ + }; +/** + * Buffer size used when not running as daemon + */ +#define APP_BUFF_SIZE 1024 +#define INPUT_DEFAULT "data" +#define OUTPUT_DEFAULT "sample" +#define PID_DEFAULT "/var/run/haveged.pid" +/** + * Setup options (for app) + */ +#define RUN_AS_APP 0x001 +#define RANGE_SPEC 0x002 +#define USE_STDOUT 0x004 +#define CAPTURE 0x008 +#define INJECT 0x010 +#define RUN_IN_FG 0x020 +#define SET_LWM 0x040 +#define MULTI_CORE 0x080 +#define CMD_MODE 0x100 +/** + * Default tests settings + */ +#define TESTS_DEFAULT_APP "ta8b" /* startup tests */ +#define TESTS_DEFAULT_RUN "ta8bcb" /* startup + continuous B */ +/** + * Run levels for diagnostic build + */ +#define DIAG_RUN_CAPTURE 2 /* output clock ticks */ +#define DIAG_RUN_INJECT 4 /* inject clock ticks */ +#define DIAG_RUN_TEST 8 /* inject test data */ +/** + * Status/monitoring information + */ +typedef struct { + H_UINT n_fill; /* number times filled */ + double etime; /* milliseconds for last collection */ + double estart; /* start time for calculation */ +} H_METER; + +/** + * Bail.... + */ +void error_exit(const char *, ...); + +/** + * Execution notices - to stderr or syslog + */ +void print_msg(const char *, ...); + +#endif diff --git a/src/havegetest.c b/src/havegetest.c new file mode 100644 index 0000000..74c72b0 --- /dev/null +++ b/src/havegetest.c @@ -0,0 +1,1030 @@ +/** + ** Simple entropy harvester based upon the havege RNG + ** + ** Copyright 2018-2021 Jirka Hladky hladky DOT jiri AT gmail DOT com + ** Copyright 2012-2014 Gary Wuertz gary@issiweb.com + ** Copyright 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 online tests for haveged through the public functions tests*. + * Online tests are run directly against the contents of the collection buffer immediately after + * a buffer fill. Because collection buffer size does not have any direct relationship with + * the data requirements of the individual tests, all tests implement a state machine to + * handle segmented input. + * + * Note code directly related to the havege interface has been moved to a conditional + * in that unit for easier maintainability. + */ +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "havegetest.h" + +#ifdef ONLINE_TESTS_ENABLE +/** + * Final value for aisSeq() when no transition found. Originally, Initially this used + * INFINITY from <math.h> but definition is undefined some gcc versions - foo! + */ +#define NO_TRANSITION 999999 +/** + * This structure is used only to pack the test structures into a single memory allocation. + * This is necessary because some architectures have stringent alignment requirements that + * cannot be met unless (compiler generated) padding is included. On mips in particular + * double must be dword aligned or bus errors result. + */ +typedef struct { + onlineTests olt; + procA pa; + procB pb; +} testsMemory; +/** + * The tests and supporting methods + */ +static H_UINT aisProcedureA(H_COLLECT *h_ctxt, procShared *tps, + procA *context, H_UINT *buffer, H_UINT sz, H_UINT offs, H_UINT prod); +static H_UINT aisProcedureB(H_COLLECT *h_ctxt, procShared *tps, + procB *context, H_UINT *buffer, H_UINT sz, H_UINT offs, H_UINT prod); +static H_UINT aisSeq(procB *p, H_UINT offs, H_UINT id); +static H_UINT aisTest(H_COLLECT * h_ctxt, H_UINT prod, H_UINT *buffer, H_UINT sz); +static H_UINT copyBits(procA *p, H_UINT ct,H_UINT sz); +static H_UINT fips140(procShared *tps, procA *p, H_UINT offs, H_UINT id); +static H_UINT test0(procA *p, H_UINT offs, H_UINT id); +static int test0cmp(const void *aa, const void *bb); +static H_UINT test5(procA *p, H_UINT offs, H_UINT id); +static H_UINT test5XOR(H_UINT8 *src, H_UINT shift); +static H_UINT test6a(procB *p, H_UINT offs, H_UINT id); +static H_UINT test8(procShared *tps, procB *p, H_UINT offs, H_UINT id); +static int testsDiscard(H_COLLECT *rdr); +static void testsMute(H_COLLECT * h_ctxt, H_UINT action, H_UINT prod, H_UINT state, H_UINT ct); +static int testsRun(H_COLLECT *rdr, H_UINT prod); + +/** + * The following suite of macros encapsulate the major bit operations of the test suite. + * The intention is to write simple rather than clever code and let the optimizer strut + * it's sutff. Note bit index starts with MSB for direct comparison with the test suit'e + * Java reference implementation. + */ +#define BITSTREAM_BIT() ((*bitstream_src)&bitstream_in)==0? 0 : 1 +#define BITSTREAM_NEXT() {if (bitstream_in==1) {\ + bitstream_src+=1;\ + bitstream_in=0x80;\ + }\ + else bitstream_in>>=1;} +#define BITSTREAM_OPEN(a,b) H_UINT8 *bitstream_src=(H_UINT8 *)(a);\ + H_UINT bitstream_in=0x80>>((b)%8);\ + bitstream_src+=(b)/8 +/** + * Setup shared resources for online tests by sorting the test options into "tot" + * and production groupings and allocating any resources used by the tests. + * Caller is responsible for initializing the procShared structure with the + * report, testsUsed, totTests[], runTests[], totText, and prodText fields. + */ +int havege_test( /* RETURN: nz on failure */ + procShared *tps, /* IN-OUT: test anchor */ + H_PARAMS *params) /* IN: app parameters */ +{ + H_UINT i; + + tps->discard = testsDiscard; + if (0==tps->report) + tps->report = testsMute; + tps->run = testsRun; + tps->options = params->options; + + if (0!=(tps->testsUsed & A_RUN)) { + H_UINT low[6] = {FIPS_RUNS_LOW}; + H_UINT high[6] = {FIPS_RUNS_HIGH}; + + tps->procReps = 1 + (5 * AIS_A_REPS); + for (i=0;i<6;i++) { + tps->fips_low[i] = low[i]; + tps->fips_high[i] = high[i]; + } + } + if (0!=(tps->testsUsed & B_RUN)) { + tps->G = (double *) malloc((Q+K+1)*sizeof(double)); + if (0 == tps->G) + return 1; + tps->G[1] = 0.0; + for(i=1; i<=(K+Q-1); i++) + tps->G[i+1]=tps->G[i]+1.0/i; + for(i=1; i<=(K+Q); i++) + tps->G[i] /= LN2; + } + return 0; +} +/** + * Check if the current buffer should be released if continuous testing is + * being performed. The buffer must be discarded if it contains an + * uncompleted retry or an uncompleted procedure with a failed test + * or a failed procedure. + */ +static int testsDiscard( /* RETURN: non-zero to discard */ + H_COLLECT * h_ctxt) /* IN-OUT: collector context */ +{ + onlineTests *context = (onlineTests *) h_ctxt->havege_tests; + procShared *tps = TESTS_SHARED(h_ctxt); + procInst *p; + H_UINT i; + + if (0==tps->testsUsed) + return 0; + if (context->result!=0) + return -1; + p = tps->runTests + context->runIdx; + switch(p->action) { + case A_RUN: + if (0 != context->pA->procRetry) + return 1; + for (i = 0;i<context->pA->testRun;i++) + if (0 !=(context->pA->results[i].testResult & 1)) + return 1; + break; + case B_RUN: + if (0 != context->pB->procRetry) + return 1; + for (i=0;i<context->pB->testNbr;i++) + if (0!=(context->pB->results[i].testResult & 0xff)) + return 1; + break; + } + return 0; +} +/** + * Place holder for when report is not configured + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +static void testsMute( + 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 */ +{ + ; +} +#pragma GCC diagnostic pop +/** + * The public wrapper that runs the tests. On the first call, the necessary machinery is built. + * The calls to aisTest() actually run the tests. The test shared structure is read only in this + * case, since testsRun could be called in a multi-threaded environment where an onlineTests + * structure is associated with each collection thread. + */ +static int testsRun( /* RETURN: nz if input needed */ + H_COLLECT * h_ctxt, /* IN-OUT: collector context */ + H_UINT prod) /* IN: nz if production else tot */ +{ + procShared *tps = TESTS_SHARED(h_ctxt); + onlineTests *context; + testsMemory *mem; + procB *pb; + + if (0 ==(tps->testsUsed)) + return 0; + if (0 == h_ctxt->havege_tests) { + H_UINT sz = sizeof(testsMemory); + + if (0==(tps->testsUsed & A_RUN)) + sz -= sizeof(procA); + if (0==(tps->testsUsed & B_RUN)) + sz -= sizeof(procB); + mem = (testsMemory *) malloc(sz); + if (NULL==mem) { + h_ctxt->havege_err = H_NOTESTMEM; + return 1; + } + context = (onlineTests *) mem; + memset(context, 0, sizeof(onlineTests)); + if (0!=(tps->testsUsed & A_RUN)) { + context->pA = &mem->pa; + context->pA->procState = TEST_INIT; + pb = &mem->pb; + } + else pb = (procB *)((void *) &mem->pa); + if (0!=(tps->testsUsed & B_RUN)) { + context->pB = pb; + context->pB->procState = TEST_INIT; + } + h_ctxt->havege_tests = context; + if (0 != (h_ctxt->havege_raw & H_DEBUG_TEST_IN)) + return 0; + } + return aisTest(h_ctxt, prod, (H_UINT *)h_ctxt->havege_bigarray, h_ctxt->havege_szFill); +} +/** + * AIS-31 test procedure A. The test is initiated by setting procState to TEST_INIT and + * the test is performed by calling the procedure adding input until completion is indicated + * by a proc state of TEST_DONE. The first test requires 3145728 bits (393,216 bytes) and + * the remaining 5 tests are repeated on sucessive 2500 byte samples for 257 times. + * + * Exit states TEST_DONE, TEST_IGNORE, TEST_INPUT, TEST_RETRY + * + * An ideal RNG will pass this test with a probablilty of 0.9987. If there is a single failed + * test, the test will be repeated. An ideal RNG should almost never fail the retry. The goal + * of this procedure is to verify RNG output is statisically inconspicuous. + */ +static H_UINT aisProcedureA( /* RETURN: bits used */ + H_COLLECT *h_ctxt, /* IN-OUT: collection instance */ + procShared *tps, /* IN-OUT: shared data */ + procA *p, /* IN: the context */ + H_UINT *buffer, /* IN: the input */ + H_UINT sz, /* IN: the input range */ + H_UINT ct, /* IN: initial bit offset */ + H_UINT prod) /* IN: production if nz */ +{ + onlineTests *context = TESTS_CONTEXT(h_ctxt); + H_UINT i, r; + + switch(p->procState) { + case TEST_INIT: + p->bytesUsed = 0; + p->procRetry = 0; + /* fallthrough */ + case TEST_RETRY: + p->procState = TEST_INPUT; + p->testState = TEST_INIT; + p->testId = p->testRun = 0; + /* fallthrough */ + case TEST_INPUT: + p->data = (H_UINT8 *)buffer; + p->range = sz * sizeof(H_UINT) <<3; + while(p->testRun < tps->procReps) { + p->testId = p->testRun<6? p->testRun : (1+(p->testRun-6) % 5); + switch(p->testId) { + case 0: + ct = test0(p, ct, p->testRun); + break; + case 1: case 2: case 3: case 4: + ct = fips140(tps, p, ct, p->testRun); + break; + case 5: + ct = test5(p, ct, p->testRun); + break; + } + context->szCarry = ct; + if (p->testState == TEST_DONE) + p->testState = TEST_INPUT; + else if (p->testState == TEST_INPUT) + return 0; + } + /* fallthrough */ + case TEST_EVAL: + p->procState = TEST_DONE; + for (r = i = 0;i<p->testRun;i++) + r += p->results[i].testResult & 1; + if (0!=r) { + tps->meters[prod? H_OLT_PROD_A_F : H_OLT_TOT_A_F] += 1; + if (1==r && 0==p->procRetry) { + p->procRetry = 1; + p->procState = TEST_RETRY; + } + else if (0!=(p->options & A_WARN)) + p->procState = TEST_IGNORE; + else { + context->result = A_RUN; + h_ctxt->havege_err = prod? H_NOTESTRUN : H_NOTESTTOT; + } + break; + } + else tps->meters[prod? H_OLT_PROD_A_P : H_OLT_TOT_A_P] += 1; + if (0!=(tps->options & (H_DEBUG_OLT|H_DEBUG_OLT))|| TEST_DONE != p->procState) + tps->report(h_ctxt, A_RUN, prod, p->procState, p->bytesUsed); + break; + } + return p->bytesUsed<<3; +} +/** + * AIS-31 test procedure B. The test is initiated by setting procState to TEST_INIT and + * the test is performed by calling the procedure adding input until completion is indicated + * by a proc state of TEST_DONE. Unlike procedure A, the number of input bits is not fixed + * but depends on the input. + * + * Exit states TEST_DONE, TEST_IGNORE, TEST_INPUT, TEST_RETRY + * + * The probability that an ideal RNG will pass this test is 0.9998. If a single test fails, + * the test is repeated. An ideal RNG should almost never fail the retry. The goal of this + * procedure is to ensure the entropy of the output is sufficiently large. + */ +static H_UINT aisProcedureB( /* RETURN: bits used */ + H_COLLECT *h_ctxt, /* IN-OUT: collection instance */ + procShared *tps, /* IN-OUT: shared data */ + procB *p, /* IN: the context */ + H_UINT *buffer, /* IN: the input */ + H_UINT sz, /* IN: the input range */ + H_UINT ct, /* IN: initial bit offset */ + H_UINT prod) /* IN: production if nz */ +{ + onlineTests *context = TESTS_CONTEXT(h_ctxt); + H_UINT i, r; + + switch(p->procState) { + case TEST_INIT: + p->bitsUsed = 0; + p->procRetry = 0; + /* fallthrough */ + case TEST_RETRY: + p->testId = p->testNbr = 0; + p->procState = TEST_INPUT; + p->testState = TEST_INIT; + /* fallthrough */ + case TEST_INPUT: + p->noise = buffer; + p->range = sz * BITS_PER_H_UINT; + i = p->testId; + while(p->testState != TEST_DONE && i < 5) { + p->seq = 1<<i; + switch(i) { + case 0: ct = test6a(p, ct, i); break; + case 4: ct = test8(tps,p,ct,i); break; + default: ct = aisSeq(p,ct,i); break; + } + if (p->testState == TEST_INPUT) + break; + p->testId = ++i; + p->testState = TEST_INIT; + } + context->szCarry = ct; + if (p->testState == TEST_INPUT) + return 0; + /* fallthrough */ + case TEST_EVAL: + p->procState = TEST_DONE; + for (i=r=0;i<p->testNbr;i++) + r += p->results[i].testResult & 1; + if (0!=r) { + tps->meters[prod? H_OLT_PROD_B_F : H_OLT_TOT_B_F] += 1; + if (1==r && 0==p->procRetry) { + p->procRetry = 1; + p->procState = TEST_RETRY; + } + else if (0!=(p->options & B_WARN)) + p->procState = TEST_IGNORE; + else { + context->result = B_RUN; + h_ctxt->havege_err = prod? H_NOTESTRUN : H_NOTESTTOT; + } + } + else tps->meters[prod? H_OLT_PROD_B_P : H_OLT_TOT_B_P] += 1; + if (0!=(tps->options & H_DEBUG_OLT)|| TEST_DONE != p->procState) + tps->report(h_ctxt, B_RUN, prod, p->procState, p->bitsUsed/8); + break; + } + return p->bitsUsed; +} +/** + * Driver for disjoint sequence tests - steps 2,3,4 of AIS-31 procedure B (aka test6b, test7a, and test7b). + * Input tid is the width of the transition to be analyzed: tid=1 { 0x, 1x }, tid=2 {00x, 01x, 10x, 11x}, + * tid=3 {000x, 001x, 010x, 011x, 100x, 101x, 110x, 111x}. The seq menber of procB gives # categories. + * For a tuple of width n, transition probabilities are calculated for log2(n) transitions for the first + * 100000 sequences. The deadman counter prevents total runaways with pathalogical input by counting + * interations that fail to update any counter. If the deadman value exceeds the limit, evaluation of + * the result forced. The probability of a forced evaluation is 10e-15. + * + * The macros below use fields in the procB structure to save/restore context when the input is + * segmented. + */ +#define RESTORE(a,b,c) a=p->bridge;b=p->lastpos[0];c=p->lastpos[1] +#define SAVE(a,b,c) p->bridge=a;p->lastpos[0]=b;p->lastpos[1]=c + +static H_UINT aisSeq( /* RETURN: last bit index */ + procB *p, /* IN-OUT: the context */ + H_UINT offs, /* IN: starting bit offset */ + H_UINT tid) /* IN: test id == #bits */ +{ + static const H_UINT seq_dead[5] = {0, 50, 120, 258, 0}; /* dead man limit */ + static const H_UINT seq_mask[5] = {0, 3, 15, 255, 0}; /* full mask */ + H_UINT i=0, c, deadman, r, s, j, hilf; + + switch(p->testState) { + case TEST_INIT: + for(j=0;j<p->seq;j++) + p->counter[j] = p->einsen[j] = 0; + p->full = 0; + p->testState = TEST_INPUT; + SAVE(0,0,0); + /* fallthrough */ + case TEST_INPUT: + RESTORE(j, hilf, deadman); + offs %= p->range; + r = p->range - offs; + s = r % (tid+1); + r -= s; + { + BITSTREAM_OPEN(p->noise,offs); + while(i<r) { + for(;j<tid;i++,j++) { + hilf += hilf+(BITSTREAM_BIT());BITSTREAM_NEXT(); + } + c = BITSTREAM_BIT();BITSTREAM_NEXT(); + i += 1; + if (0!=(p->full & (1<<hilf))) { + if ((deadman+=1)> seq_dead[tid]) { + p->testState = TEST_DONE; + break; + } + } + else { + deadman = 0; + p->einsen[hilf] += c; + if ((p->counter[hilf]+=1)==AIS_LENGTH) { + p->full |= (1<<hilf); + if (p->full == seq_mask[tid]) { + p->testState = TEST_EVAL; + break; + } + } + } + j = hilf = 0; + } + if (p->testState==TEST_INPUT) { + for(j=hilf=0;j<s;j++,i++) { + hilf += hilf+(BITSTREAM_BIT());BITSTREAM_NEXT(); + } + SAVE(j,hilf,deadman); + } + } + p->bitsUsed += i; + if (p->testState == TEST_INPUT) + break; + /* fallthrough */ + case TEST_EVAL: + if (tid==1) { + double q[2]; + + if (p->testState == TEST_EVAL) { + for(j=0;j<2;j++) + q[j] = (double)(p->einsen[j]) / (double) AIS_LENGTH; + p->results[p->testNbr].finalValue = q[0] - q[1]; + } + else p->results[p->testNbr].finalValue = NO_TRANSITION; + hilf = tid << 8; + if (p->results[p->testNbr].finalValue <= -0.02 || p->results[p->testNbr].finalValue >= 0.02) + hilf |= 1; + p->results[p->testNbr++].testResult = hilf; + } + else { + /** + * The spec is very confusing but the reference implementation is correct. The test + * operates on observed transions, i.e. the difference between pairs of successive + * einsen and nullen (AIS_LENGTH - einsen) + */ + for(j=0; j<p->seq; j+=2) { + if (p->testState == TEST_EVAL) { + double qn = (double)((int)p->einsen[j] - (int)p->einsen[j+1]); + double qd = (double)(p->einsen[j] + p->einsen[j+1]); + double pd = AIS_LENGTH * 2.0 - qd; + p->results[p->testNbr].finalValue = ((qn * qn) / pd) + ((qn * qn) / qd); + } + else p->results[p->testNbr].finalValue = NO_TRANSITION; + hilf = tid << 8; + if (p->results[p->testNbr].finalValue > 15.13) + hilf |= 1; + p->results[p->testNbr++].testResult = hilf; + } + } + p->testState = TEST_DONE; + break; + } + return i+offs; +} +/** + * Run the configured test procedures. This function cycles the procedure calls + * setup by the configuration using tail recursion to sequence multiple tests. + */ +static H_UINT aisTest( /* RETURN: nz if input needed */ + H_COLLECT * h_ctxt, /* IN-OUT: collector context */ + H_UINT prod, /* IN: production indicator */ + H_UINT *buffer, /* IN: test data, H_UINT aligned */ + H_UINT sz) /* IN: size of data in H_UINT */ +{ + procShared *tps = TESTS_SHARED(h_ctxt); + onlineTests *context = (onlineTests *) h_ctxt->havege_tests; + procInst *p; + H_UINT offs,state=TEST_DONE, tot=0; + + if (context->result!=0) + return 0; + if (prod==0) + p = tps->totTests + context->totIdx; + else p = tps->runTests + context->runIdx; + + switch(p->action) { + case A_RUN: + if (context->pA->procState==TEST_INIT) + context->pA->options = p->options; + tot = aisProcedureA(h_ctxt, tps, context->pA, + buffer, sz, context->szCarry, prod); + state = context->pA->procState; + break; + case B_RUN: + if (context->pB->procState==TEST_INIT) + context->pB->options = p->options; + tot = aisProcedureB(h_ctxt, tps, context->pB, + buffer, sz, context->szCarry, prod); + state = context->pB->procState; + break; + } + if (state==TEST_INPUT) { + context->szCarry = 0; + return 1; + } + context->szTotal += tot; + if (prod==0) { + if (context->totIdx>=1) /* check for end of tot */ + return 0; + context->totIdx += 1; + p = tps->totTests + context->totIdx; + } + else { + if (0==tps->runTests[0].action) /* check for no cont tests */ + return 0; + else if (0!=tps->runTests[1].action) /* check for only 1 cont test */ + context->runIdx = context->runIdx? 0 : 1; + p = tps->runTests + context->runIdx; + } + switch(p->action) { + case A_RUN: + context->pA->procState=TEST_INIT; + break; + case B_RUN: + context->pB->procState=TEST_INIT; + break; + } + offs = context->szCarry/BITS_PER_H_UINT; + if (offs<sz) { + context->szCarry -= offs * BITS_PER_H_UINT; + return aisTest(h_ctxt, prod, buffer+offs, sz - offs); + } + return 1; +} +/** + * Procedure A input is obtained by copying bits from p->data to p->aux using + * p->bridge as position. This realigns the input to a byte boundary and + * resolves segmentation issues. Originally implemented in BITSTREAM macros + * performance was bad enough to justify serious tuning. Returns the updated + * bit offset. + */ +/** + * The BITSTREAM macros were totally inadequate for the proecedure A needs. These + * helpers are used to implement a high performance copyBit(). + */ +#define COPY_BYTE() {c = (*src<<bit_diff_ls)|(*(src+1)>>bit_diff_rs);src++;} +#define COPY_FIRST() if ( (int) xfr >= (8 - dst_bits)) {\ + *dst &= rm[dst_bits];\ + xfr -= 8 - dst_bits;\ + }\ + else {\ + *dst &= rm[dst_bits] | rm_xor[dst_bits + xfr + 1];\ + c &= rm[dst_bits + xfr];\ + xfr = 0;\ + }\ + xfr_bytes = xfr>>3;\ + xfr_bits = xfr&7;\ + *dst++ |= c; +/** + * Each procedure A repetition moves TEST0_USED + 257*FIPS_USED bits + * to the auxilary work space - a little more than 1MB + */ +static H_UINT copyBits( /* RETURN: updated bit offset */ + procA *p, /* IN-OUT: the context */ + H_UINT offs, /* IN: the input bit offset */ + H_UINT sz) /* IN: number of bits to copy */ +{ + H_UINT avail = p->range; + H_UINT need = sz - p->bridge; + H_UINT xfer, xfr; + + offs %= avail; + xfer = (avail-offs)<need? (avail-offs) : need; + if ((xfr = xfer)!=0) { + static const H_UINT8 rm[] = { 0x55, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + static const H_UINT8 rm_xor[] = { 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00 }; + H_UINT8 *src = p->data+(offs>>3); + H_UINT8 *dst = p->aux +(p->bridge>>3); + H_UINT8 src_bits = offs&7; + H_UINT8 dst_bits = p->bridge&7; + H_UINT xfr_bytes = xfr>>3; + H_UINT xfr_bits = xfr&7; + H_UINT8 c; + + if (src_bits==dst_bits) { + if (src_bits!=0){ + c = rm_xor[dst_bits] & *src++; + COPY_FIRST(); + } + if (xfr_bytes!=0) { + memcpy(dst, src, xfr_bytes); + src += xfr_bytes; + dst += xfr_bytes; + } + if (xfr_bits) { + *dst &= rm_xor[xfr_bits]; + *dst |= rm[xfr_bits] & *src++; + } + } + else { + H_UINT bit_diff_ls, bit_diff_rs; + if (src_bits>dst_bits) { + bit_diff_ls = src_bits - dst_bits; + bit_diff_rs = 8 - bit_diff_ls; + COPY_BYTE(); + c &= rm_xor[dst_bits]; + } + else { + bit_diff_rs = dst_bits - src_bits; + bit_diff_ls = 8 - bit_diff_rs; + c = *src >> bit_diff_rs & rm_xor[dst_bits]; + } + COPY_FIRST(); + while (xfr_bytes-- != 0) { + COPY_BYTE(); + *dst++ = c; + } + if (xfr_bits!=0) { + COPY_BYTE(); + c &= rm[xfr_bits]; + *dst &= rm_xor[xfr_bits]; + *dst |= c; + } + } + } + p->bridge += xfer; + if (p->bridge>=sz) { + p->bytesUsed += sz>>3; + p->bridge = 0; + p->testState = TEST_EVAL; + } + return offs + xfer; +} +/** + * Procedure A tests 1 through 4 correspond to the fips140-1 tests. These tests + * are conducted on the same input stream, so the calculations can be + * done in parallel. + */ +#define FIPS_ADD() {\ + if (runLength < 5)\ + runs[runLength + (6*current)]++;\ + else runs[5 + (6*current)]++;\ + } + +static H_UINT fips140( /* RETURN: updated bit offset */ + procShared *tps, /* IN: shared data */ + procA *p, /* IN-OUT: the context */ + H_UINT offs, /* IN: starting offset */ + H_UINT tid) /* IN: test id */ +{ + H_UINT poker[16]; /* counters for poker test */ + H_UINT ones; /* counter for monbit test */ + H_UINT runs[12]; /* counters for runs tests */ + H_UINT runLength; /* current run length */ + H_UINT maxRun; /* largest run encountered */ + H_UINT current; /* current bit */ + H_UINT last; /* last bit index */ + H_UINT c, i, j, k; + + switch(p->testState) { + case TEST_INIT: + p->testState = TEST_INPUT; + p->bridge = 0; + /* fallthrough */ + case TEST_INPUT: + offs = copyBits(p, offs, FIPS_USED); + if (p->testState!=TEST_EVAL) + break; + /* fallthrough */ + case TEST_EVAL: + maxRun = ones = runLength = 0; + memset(poker, 0, 16*sizeof(H_UINT)); + memset(runs, 0, 12*sizeof(H_UINT)); + { + BITSTREAM_OPEN(p->aux,0); + last = BITSTREAM_BIT(); + for (c=i=0;i<FIPS_USED;i++) { + current = BITSTREAM_BIT(); + if (current==last) { + if (++runLength>maxRun) + maxRun = runLength; + } + else { + FIPS_ADD(); + runLength = 0; + last = current; + } + c += c + current; + ones += current; + if (bitstream_in==1) { + poker[c&15] += 1; + c = 0; + } + else if (bitstream_in==16) + poker[c] += 1; + BITSTREAM_NEXT(); + } + FIPS_ADD(); + } + /* 1 = monobit test */ + k = (ones >= FIPS_ONES_HIGH || ones <= FIPS_ONES_LOW)? 1 : 0; + p->results[tid].testResult = k | (1<<8); + p->results[tid++].finalValue = ones; + /* 2 = poker test */ + for(j=k=0;j<16;j++) k += poker[j]*poker[j]; + j = (k <= FIPS_POKER_LOW || k >= FIPS_POKER_HIGH)? 1 : 0; + p->results[tid].testResult = j | (2<<8); + p->results[tid++].finalValue = k; + /* 3 = runs test */ + for(i=j=k=0;j<12;j++) + if (runs[j] < tps->fips_low[j%6] || runs[j] > tps->fips_high[j%6]) { + k |= 1; + i = runs[j]; + } + p->results[tid].testResult = k | (3<<8); + p->results[tid++].finalValue = i; + /* 4 = max run length */ + k = maxRun>=FIPS_MAX_RUN? 1 : 0; + p->results[tid].testResult = k | (4<<8); + p->results[tid++].finalValue = maxRun; + p->testRun = tid; + p->testState = TEST_DONE; + } + return offs; +} +/** + * Procedure A disjointness test on 48 bit strings. Rejection probability for ideal + * RNG is 2e^-17 + */ +static H_UINT test0( /* RETURN: updated bit offset */ + procA *p, /* IN-OUT: the context */ + H_UINT offs, /* IN: starting bit offset */ + H_UINT tid) /* IN: test id */ +{ + H_UINT i, j; + + switch(p->testState) { + case TEST_INIT: + p->testState = TEST_INPUT; + p->bridge = 0; + /* fallthrough */ + case TEST_INPUT: + offs = copyBits(p, offs, TEST0_USED); + if (p->testState!=TEST_EVAL) + break; + /* fallthrough */ + case TEST_EVAL: + qsort(p->aux, TEST0_LENGTH, 6, test0cmp); + for (i=6,j=0;i<TEST0_LENGTH && j==0;i+=6) + if (!memcmp(p->aux+i-6, p->aux+i, 6)) { + j=1; + } + p->results[tid].testResult = j; + p->results[tid++].finalValue = i; + p->testRun = tid; + p->testState = TEST_DONE; + } + return offs; +} +/** + * Comparison method for the test0 sort + */ +static int test0cmp(const void *aa, const void *bb) +{ + return memcmp(aa,bb,6); +} +/** + * Procedure A autocorrelation test. Brutal bit twiddling. Uses same + * data as FIPS - no update to bit offset + */ +static H_UINT test5( /* RETURN: updated bit offset */ + procA *p, /* IN-OUT: the context */ + H_UINT offs, /* IN: starting bit offset */ + H_UINT tid) /* IN: test id */ +{ + H_UINT8 *dp = (H_UINT8 *)p->aux; + H_UINT j, k, max, tau, Z_tau; + + /** + * Because this test is so slow it can be skipped on one or more repetitions + */ + if (0 != (p->options & A_CYCLE)) { + j = p->options & A_CYCLE; + if (j==0 || ((tid-1)/5 % j)!=0) { + p->results[tid++].testResult = 0xff00; + p->testRun = tid; + p->testState = TEST_DONE; + return offs; + } + } + /** + * This test always uses the same data as test1 through test4 + */ + for (max = k = 0,tau=1;tau<=TEST5_LENGTH;tau++){ + Z_tau = abs( (int) test5XOR(dp, tau) - 2500); + if (Z_tau > max) { + max = Z_tau; + k = tau - 1; + } + } + dp += TEST5_LENGTH/8; + Z_tau = test5XOR(dp, k + 1); + j = 5<<8; + if (( Z_tau <= 2326) || ( Z_tau >= 2674)) + j |= 1; + + p->results[tid].testResult = j; + p->results[tid++].finalValue = Z_tau; + p->testRun = tid; + p->testState = TEST_DONE; + return offs; +} +/** + * The test5 reference implementation looks something like this: + * + * for(i=0,j=shift;i<TEST5_LENGTH;i++,j++) + * rv += 1 & (((src[i>>3]>>(i & 7))) ^ ((src[j>>3]>>(j & 7)))); + * return rv; + * + * A high performance optimization using multi-byte casts is 3x as fast as the above but blows up + * because of alignment issues (leftovers from the test0 implementation) + * The optimized single byte optimization is 2x as fast as the above but uses no alignment games + */ +static H_UINT test5XOR(H_UINT8 *src, H_UINT shift) +{ + H_UINT8 *src1; + H_UINT i,rest, rv; + + src1 = src + (shift>>3); + shift &= 7; + rest = 8 - shift; + for(i=rv=0;i<(TEST5_LENGTH>>3);i++) { + H_UINT8 lw = *src++; + H_UINT8 rw = *src1++; + H_UINT8 w; + + for (w = (lw & (0xff>>shift)) ^ (rw>>shift);w!=0;w>>=1) + rv += w & 1; + for (w = (lw>>rest) ^ (*src1 & (0xff>>rest));w!=0;w>>=1) + rv += w & 1; + } + return rv; +} +/** + * Procedure B uniform distribution test for parameter set (1,100000,0.25). Very simple, just + * count bits. Fixed input size, deadman not needed + */ +#define SIDEWAYS_ADD(c,i) {H_UINT in = i;\ + in -= ((in >> 1) & 0x55555555);\ + in = (in & 0x33333333) + ((in >> 2) & 0x33333333);\ + c=(((in + (in >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;\ + } + +static H_UINT test6a( /* RETURN: bit offset */ + procB *p, /* IN-OUT: the context */ + H_UINT offs, /* IN: starting bit offset */ + H_UINT tid) /* IN: test id */ +{ + H_UINT r = p->range - offs; + H_UINT i=0,j=p->bridge; + + switch(p->testState) { + case TEST_INIT: + j = p->counter[0] = 0; + p->testState = TEST_INPUT; + /* fallthrough */ + case TEST_INPUT: + { + BITSTREAM_OPEN(p->noise,offs); + H_UINT c; + + /* align to a byte boundary, then shift gears to gobble bytes */ + while(i < r && j < AIS_LENGTH && bitstream_in != 0x80){ + p->counter[0] += BITSTREAM_BIT();BITSTREAM_NEXT(); + i++;j++; + } + /* align to a word boundary, then shift gears to gobble words */ + while((i+8) < r && (j+8) < AIS_LENGTH) { + if (0==((char *)bitstream_src - (char *)p->noise) % sizeof(H_UINT)) + break; + SIDEWAYS_ADD(c, *bitstream_src++); + p->counter[0] += c; + i+=8;j+=8; + } + /* gobble all words available */ + while((i+BITS_PER_H_UINT) < r && (j+BITS_PER_H_UINT) < AIS_LENGTH) { + SIDEWAYS_ADD(c, *((H_UINT *)bitstream_src)); + bitstream_src += sizeof(H_UINT); + p->counter[0] += c; + i+=BITS_PER_H_UINT;j+=BITS_PER_H_UINT; + } + /* shift back to bits & cleanup the leftovers */ + for(;i < r && j < AIS_LENGTH;i++,j++) { + p->counter[0] += BITSTREAM_BIT();BITSTREAM_NEXT(); + } + p->bitsUsed += i; + if (j < AIS_LENGTH) { + p->bridge = j; + break; + } + } + /* fallthrough */ + case TEST_EVAL: + p->results[p->testNbr].finalValue = (double)(p->counter[0]) / (double) AIS_LENGTH; + r = tid << 8; + if (p->results[p->testNbr].finalValue <= 0.25 || p->results[p->testNbr].finalValue >= 0.75) + r |= 1; + p->results[p->testNbr++].testResult = r; + p->testState = TEST_DONE; + } + return i+offs; +} +/** + * Context is saved and restored using inactive members of the anchor. + */ +#define RESTORE8(a,b,c,d) a=p->bridge;b=p->full;c=p->einsen[0];\ + d=p->results[p->testNbr].finalValue +#define SAVE8(a,b,c,d) p->bridge=a;p->full=b;p->einsen[0]=c;\ + p->results[p->testNbr].finalValue = d +/** + * Procedure B entropy estimator (Coron). Find the distribution of the distance between + * bytes and their predecessors. Fixed input size, no deadman needed. + */ +static H_UINT test8( /* RETURN: bit offset */ + procShared *tps, /* IN-OUT: shared data */ + procB *p, /* IN-OUT: the context */ + H_UINT offs, /* IN: starting bit offset */ + H_UINT tid) /* IN: test id */ +{ + H_UINT hilf, j, k, r, i=0; + double TG=0.0; + + switch(p->testState) { + case TEST_INIT: + memset(p->lastpos, 0, 256*sizeof(H_UINT)); + SAVE8(0,0,0,0.0); + p->testState = TEST_INPUT; + /* fallthrough */ + case TEST_INPUT: + RESTORE8(k,j,hilf,TG); + r = p->range - offs; + { + H_UINT align; + /* gobble bits up to a byte boundary */ + BITSTREAM_OPEN(p->noise,offs); + for(;j<8 && i<r && bitstream_in!=0x80;i++,j++) { + hilf += hilf+(BITSTREAM_BIT());BITSTREAM_NEXT(); + } + align = (j &= 7); + while(i<r) { + if (j==0 && (i+8)<r) { /* gobble a byte */ + hilf = (0xff & (bitstream_src[0]<<(8-align))) | (bitstream_src[1]>>align); + bitstream_src++;i+=8;j=8; + } + for(;j<8 && i<r;i++,j++) { /* gobble loose bits */ + hilf += hilf+(BITSTREAM_BIT());BITSTREAM_NEXT(); + } + if (j!=8) + break; + if (k<Q) + p->lastpos[hilf] = k++; + else { + TG += tps->G[k - p->lastpos[hilf]]; + p->lastpos[hilf] = k++; + if (k==(K+Q)) { + p->testState = TEST_EVAL; + break; + } + } + j = hilf = 0; + } + if (p->testState==TEST_INPUT) { + SAVE8(k,j,hilf,TG); + } + } + p->bitsUsed += i; + if (p->testState == TEST_INPUT) + break; + /* fallthrough */ + case TEST_EVAL: + tps->lastCoron = p->results[p->testNbr].finalValue = TG/(double)K; + r = tid<<8; + if (p->results[p->testNbr].finalValue <= 7.967) + r |= 1; + p->results[p->testNbr++].testResult = r; + p->testState = TEST_DONE; + } + return i+offs; +} +#endif diff --git a/src/havegetest.h b/src/havegetest.h new file mode 100644 index 0000000..8e9ac28 --- /dev/null +++ b/src/havegetest.h @@ -0,0 +1,225 @@ +/** + ** Simple entropy harvester based upon the havege RNG + ** + ** Copyright 2018-2021 Jirka Hladky hladky DOT jiri AT gmail DOT com + ** Copyright 2012-2014 Gary Wuertz gary@issiweb.com + ** Copyright 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/>. + */ +#ifndef HAVEGETEST_H +#define HAVEGETEST_H +/** + * The haveged test suite is built from the 8 tests specified in AIS-31 + * organized into test procedure A and test procedure B and structured + * as state machines capable of processing segmented input streams. + */ +#include "havegecollect.h" +/** + * All individual tests and the test procedures use the following + * simple state machine to manage input. + */ +typedef enum { + TEST_INIT, /* initialize test (internal) */ + TEST_INPUT, /* test input needed */ + TEST_EVAL, /* evaluating results (internal) */ + TEST_DONE, /* test complete */ + TEST_RETRY, /* retry the test */ + TEST_IGNORE, /* ignore failure and continue */ + TEST_FAIL /* Test has failed */ + } TEST_STATE; +/** + * AIS-31 procedure A uses the FIPS140-1 as test1 thru test4. A disjointedness test is + * used as test0 and a autocorrelation test is used as test5. test0 is executed only + * once, the other tests are repeated in sequence 257 times. + */ +#define AIS_A_REPS 257 /* reps for test1 through test 5 */ +/** + * Constants for the fips tests. Note AIS-31 v1 uses the unamended FIPS test limits + */ +#define FIPS_USED 20000 +#ifndef USE_AMENDED_FIPS +#define FIPS_MAX_RUN 34 +#define FIPS_ONES_LOW 9654 +#define FIPS_ONES_HIGH 10346 +#define FIPS_POKER_LOW 1562822 /* 1.03 */ +#define FIPS_POKER_HIGH 1580438 /* 57.4 */ +#define FIPS_RUNS_LOW 2267,1079,502,223,90,90 +#define FIPS_RUNS_HIGH 2733,1421,748,402,223,223 +#else +#define FIPS_MAX_RUN 25 +#define FIPS_ONES_LOW 9725 +#define FIPS_ONES_HIGH 10275 +#define FIPS_POKER_LOW 1576928 /* 2.16 */ +#define FIPS_POKER_HIGH 1576928 /* 46.17 */ +#define FIPS_RUNS_LOW 2315,1114,525,240,103,103 +#define FIPS_RUNS_HIGH 2685,1386,723,384,209,209 +#endif +/** + * test 0 consumes 64k * 48 bits + */ +#define TEST0_LENGTH 65536 +#define TEST0_USED (TEST0_LENGTH * 48) +#define TEST5_LENGTH 5000 +/** + * Fixed size input for procedure A + */ +#define AIS_A_SIZE (TEST0_USED+(2500*257)) +/** + * AIS-31 procedure A results + */ +typedef struct { + H_UINT testResult; /* id 8 bits, pass/fail 8bits */ + H_UINT finalValue; /* end result */ +} resultA; +/** + * AIS-31 procedure A context. Options are defined in haveged.h + * This puppy weighs in at ~3 MB. + */ +typedef struct { + H_UINT8 *data; /* input for test */ + H_UINT range; /* number of bits of input */ + H_UINT procState; /* procedure state */ + H_UINT procRetry; /* retry indication */ + H_UINT testId; /* test selector 0-5 */ + H_UINT testRun; /* test index 1 - 1285 */ + H_UINT testState; /* FSM state of current test */ + H_UINT bridge; /* index for data bridge */ + H_UINT bytesUsed; /* number of bytes used */ + H_UINT options; /* duty cycle for test5 */ + H_UINT8 aux[TEST0_USED]; /* extra work space */ + resultA results[1286]; /* test results */ + } procA; +/** + * AIS-31 procedure B is a set of multinomial distribution tests + * and an entropy estimate (Coron' test). The distribution tests, + * test6 and test7, require at least AIS_LENGTH sequences of 1, 2 + * 4, and 8 bits. + */ +/* + * Bit range of AIS-31 procedure B distribution tests + */ +#define AIS_LENGTH 100000 +/** + * AIS-31 test8 constants (Coron's test) + */ +#define Q 2560 +#define K 256000 +#define LN2 0.69314718055994530941 +/** + * AIS-31 procedure B results + */ +typedef struct { + H_UINT testResult; /* id 8 bits, pass/fail 8bits */ + double finalValue; /* final value */ + } resultB; +/** + * AIS-31 procedure B context, a svelt 1.25 KB + */ +typedef struct { + H_UINT *noise; /* input for test */ + H_UINT range; /* number of bits of input */ + H_UINT procState; /* procedure state */ + H_UINT procRetry; /* retry indication */ + H_UINT testId; /* test selector 6-8 */ + H_UINT testNbr; /* current test number */ + H_UINT testState; /* FSM state of current test */ + H_UINT seq; /* aisSeq() sequence needed */ + H_UINT bitsUsed; /* bits used by procedure */ + H_UINT bridge; /* data bridge test6,7 */ + H_UINT counter[8]; /* sequence lengths */ + H_UINT einsen[8]; /* sequence counts (ones) */ + H_UINT full; /* sequence flags */ + H_UINT lastpos[256]; /* counters for test 8 */ + H_UINT options; /* RFU */ + resultB results[9]; /* test results */ + } procB; +/** + * Testing options + */ +#define A_CYCLE 0x000001ff /* test5 duty cycle */ +#define A_WARN 0x00000200 /* Only warn of A fails */ +#define A_RUN 0x00000400 /* Run procedure A */ +#define A_OPTIONS 0x000003ff +#define B_WARN 0x00001000 /* Only warn of B fails */ +#define B_RUN 0x00002000 /* Run proceure B */ +#define B_OPTIONS 0x00001000 +#define X_OPTIONS 0x000f0000 /* isolated test index */ +#define X_RUN 0x00100000 /* diagnostic isolated test */ +/** + * A test procedure run consists of an indicator and options + */ +typedef struct { + H_UINT action; /* action code A_RUN, B_RUN */ + H_UINT options; /* WARN and other options */ + } procInst; + +/** + * Services provided + */ +typedef int (*ptrDiscard)(H_COLLECT *rdr); +typedef void (*ptrReport)(H_COLLECT * h_ctxt, H_UINT action, H_UINT prod, H_UINT state, H_UINT ct); +typedef int (*ptrRun)(H_COLLECT *rdr, H_UINT prod); + +/** + * A test procedure is associated with a collection buffer. Some + * resources are shared by all collection buffers. This includes + * roll-your-own vtable used to avoid polluting the RNG name space + * This structure ends up in hptr->testData + */ +typedef struct { + ptrDiscard discard; /* release test resources */ + ptrRun run; /* run test suite */ + ptrReport report; /* report test results */ + H_UINT options; /* verbosity, etc. */ + H_UINT testsUsed; /* tests used */ + procInst totTests[2]; /* tot tests to run */ + procInst runTests[2]; /* production tests to run */ + H_UINT procReps; /* Number of A repetitions */ + H_UINT fips_low[6]; /* low runs thresholds */ + H_UINT fips_high[6]; /* high runs thresholds */ + char totText[8]; /* tot test text rep */ + char prodText[8]; /* production test text rep */ + H_UINT meters[H_OLT_PROD_B_P+1]; /* test counters */ + double lastCoron; /* last test8 result */ + double *G; /* test8 lookup table */ +} procShared; +/** + * How to get test context and shared data from H_COLLECT + */ +#define TESTS_CONTEXT(c) (onlineTests *)(c->havege_tests) +#define TESTS_SHARED(c) (procShared *)(((H_PTR)(c->havege_app))->testData) +/** + * Online testing context - one per collector. Note szTotal is for diagnostic + * use only, no effort is made to account for overflow. + */ +typedef struct { + H_UINT result; /* nz if failed */ + H_UINT totIdx; /* tot test idx */ + H_UINT runIdx; /* run test idx */ + H_UINT szCarry; /* bits carried in next proc */ + H_UINT szTotal; /* total bits processed */ + procA *pA; /* procedure A instance */ + procB *pB; /* procedure B instance */ +} onlineTests; +/** + * Default options are to run the tot tests. + */ +#define DEFAULT_TEST_OPTIONS "ta8b" +/** + * Public interface + */ +int havege_test(procShared *tps, H_PARAMS *params); + +#endif diff --git a/src/havegetune.c b/src/havegetune.c new file mode 100644 index 0000000..f558e0d --- /dev/null +++ b/src/havegetune.c @@ -0,0 +1,926 @@ +/** + ** Determine HAVEGE environment + ** + ** 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 automatic tuning for the havege algorithm. Two + * general methods are used CPUID (intel specific) and VFS (Linux specific). The + * best result of the available methods is returned. + */ +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> +#include <string.h> +#include <errno.h> +#include "havegetune.h" +/** + * Text representations of build options + */ +#define BUILD_CLOCK 'C' +#define BUILD_DIAGS 'D' +#define BUILD_CPUID 'I' +#define BUILD_THREADS 'M' +#define BUILD_OLT 'T' +#define BUILD_VFS 'V' +/** + * Text representations of TOPO_MAP sources + */ +static const char *topoReps[] = { + "D", /* SRC_DEFAULT 0x00001 */ + "P", /* SRC_PARAM 0x00002 */ + "A6", /* SRC_CPUID_AMD6 0x00004 */ + "A5", /* SRC_CPUID_AMD5 0x00008 */ + "L2", /* SRC_CPUID_INTEL2 0x00010 */ + "L4", /* SRC_CPUID_INTEL4 0x00020 */ + "V", /* SRC_VFS_INDEX 0x00040 */ + "", /* not used 0x00080 */ + "C", /* SRC_CPUID_PRESENT 0x00100 */ + "H", /* SRC_CPUID_HT 0x00200 */ + "A", /* SRC_CPUID_AMD 0x00400 */ + "A8", /* SRC_CPUID_AMD8 0x00800 */ + "B", /* SRC_CPUID_LEAFB 0x01000 */ + "4", /* SRC_CPUID_LEAF4 0x02000 */ + "VS", /* SRC_VFS_STATUS 0x04000 */ + "VO" /* SRC_VFS_ONLINE 0x08000 */ + "VI", /* SRC_VFS_CPUINFO 0x10000 */ + "VC", /* SRC_VFS_CPUDIR 0x20000 */ + 0 + }; +/** + * Local debugging + */ +#if 0 +#define TUNE_DEBUG(...) fprintf(stderr,__VA_ARGS__) +#define TUNE_DUMP 1 +static void cfg_dump(HOST_CFG *anchor); +#else +#define TUNE_DEBUG(...) +#endif + +/** + * Local prototypes + */ +#ifdef TUNING_VFS_ENABLE +static void cfg_bitClear(TOPO_MAP *m); +static int cfg_bitCount(TOPO_MAP *m); +#endif +static void cfg_bitDecode(char *dest, const char **reps, H_UINT value, H_UINT size); +#if 0 +static void cfg_bitDisplay(TOPO_MAP *m); +#endif +static int cfg_bitIntersect(TOPO_MAP *m, TOPO_MAP *t); +static void cfg_bitMerge(TOPO_MAP *m,TOPO_MAP *t); +static int cfg_bitNext(TOPO_MAP *m, int n); +static void cfg_bitSet(TOPO_MAP *m, int n); +static void cfg_cacheAdd(HOST_CFG *anchor, H_UINT src, H_UINT cpu, + H_UINT level, H_UINT type, H_UINT kb); +static void cfg_cpuAdd(HOST_CFG *anchor, H_UINT src, CPU_INST *INST); +/** + * If cpuid not present, no need to generate the code + */ +#ifndef CPUID +#undef TUNING_CPUID_ENABLE +#endif + +#ifdef TUNING_CPUID_ENABLE +/************************* CPUID support ***************************************/ +/** + * Register names + */ +typedef enum { + EAX, EBX, ECX, EDX +} CPUID_REGNAMES; + +#define CPUID_CONFIG(a) cpuid_config(a) +#define CPUID_VENDOR(r) *((H_UINT *)s) = regs[r];s+= sizeof(H_UINT) +/** + * Local CPUID prototypes + */ +#if defined (GCC_VERSION) && GCC_VERSION >= 40400 +static void cpuid(int fn, int sfn, H_UINT *regs) __attribute__((optimize(0))); +#else +static void cpuid(int fn, int sfn, H_UINT *regs); +#endif +static void cpuid_config(HOST_CFG *anchor); +static void cpuid_configAmd(HOST_CFG *anchor, CPU_INST *w); +static void cpuid_configIntel(HOST_CFG *anchor, CPU_INST *w); +static void cpuid_configIntel2(HOST_CFG *anchor, CPU_INST *w, H_UINT *regs); +static void cpuid_configIntel4(HOST_CFG *anchor, CPU_INST *w, H_UINT *regs); +#else +#define CPUID_CONFIG(a) +#endif +/************************ /CPUID support ***************************************/ +/************************ VFS support ***************************************/ +#ifdef TUNING_VFS_ENABLE + +#define VFS_LINESIZE 256 +/** + * Filter function used by configuration + */ +typedef int (*pFilter)(HOST_CFG *pAnchor, char *input); +typedef int (*pDirFilter)(HOST_CFG *pAnchor, char *input, H_UINT *pArg); +/** + * Definitions + */ +#define VFS_CONFIG(a) vfs_config(a) +/** + * Local filesystem prototypes + */ +static void vfs_config(HOST_CFG *anchor); +static int vfs_configCpuDir(HOST_CFG *anchor, char *input, H_UINT *pArg); +static int vfs_configCpuInfo(HOST_CFG *anchor, char *input); +static int vfs_configDir(HOST_CFG *pAnchor, char *path, pDirFilter filter, H_UINT *pArg); +static int vfs_configFile(HOST_CFG *anchor, char *path, pFilter filter); +static int vfs_configInfoCache(HOST_CFG *pAnchor, char *input, H_UINT *pArg); +static int vfs_configOnline(HOST_CFG *anchor, char *input); +static int vfs_configInt(HOST_CFG *anchor, char *input); +static int vfs_configStatus(HOST_CFG *anchor, char *input); +static int vfs_configType(HOST_CFG *anchor, char *input); + +static void vfs_parseList(TOPO_MAP *map, char *input); +static void vfs_parseMask(TOPO_MAP *map, char *input); +#else +#define VFS_CONFIG(a) +#endif +/************************* /VFS support ***************************************/ + +/** + * Get tuning values for collector + */ +void havege_tune( /* RETURN: none */ + HOST_CFG *anchor, /* OUT: tuning info */ + H_PARAMS *param) /* IN: config parameters */ +{ + char *bp = anchor->buildOpts; + int i; + + /** + * Capture build options + */ +#ifdef __GNUC__ + bp += snprintf(bp, 24, "gcc %d.%d.%d ", __GNUC__ ,__GNUC_MINOR__ , __GNUC_PATCHLEVEL__); +#endif +#if defined(ENABLE_CLOCK_GETTIME) + *bp++ = BUILD_CLOCK; +#endif +#if defined(RAW_IN_ENABLE) || defined(RAW_OUT_ENABLE) + *bp++ = BUILD_DIAGS; +#endif +#ifdef TUNING_CPUID_ENABLE + *bp++ = BUILD_CPUID; +#endif +#if NUMBER_CORES>1 + *bp++ = BUILD_THREADS; +#endif +#ifdef ONLINE_TESTS_ENABLE + *bp++ = BUILD_OLT; +#endif +#ifdef TUNING_VFS_ENABLE + *bp++ = BUILD_VFS; +#endif + *bp = 0; + /** + * Virtual file system setup + */ + anchor->procfs = param->procFs==NULL? "/proc" : param->procFs; + anchor->sysfs = param->sysFs==NULL? "/sys" : param->sysFs; + /** + * The order determines preference + */ + if (param->icacheSize != 0) + cfg_cacheAdd(anchor, SRC_PARAM, -1, 1, 'I', param->icacheSize); + if (param->dcacheSize != 0) + cfg_cacheAdd(anchor, SRC_PARAM, -1, 1, 'D', param->dcacheSize); + /** + * Bypass configuration if entirely specified + */ + if (param->icacheSize == 0 || param->dcacheSize == 0) { + CPUID_CONFIG(anchor); + VFS_CONFIG(anchor); + cfg_cacheAdd(anchor, SRC_DEFAULT, -1, 1, 'I', GENERIC_ICACHE); + cfg_cacheAdd(anchor, SRC_DEFAULT, -1, 1, 'D', GENERIC_DCACHE); + } + /** + * Make sure there is at least 1 cpu instance. cpus configuration is considered + * homogeneous so only the first will be reported/used. + */ + if (0 == anchor->ctCpu) + cfg_cpuAdd(anchor, 0, NULL); + cfg_bitDecode(anchor->cpuOpts, topoReps, anchor->cpus[0].cpuMap.source, SZ_CPUREP); + #ifdef TUNE_DUMP + cfg_dump(anchor); + #endif + anchor->d_tune = anchor->i_tune = MAX_CACHES+2; + for (i=0;i<anchor->ctCache;i++) { + if (anchor->caches[i].level==1) { + switch(anchor->caches[i].type) { + case 'I': case 'T': + if (i < (int)anchor->i_tune) + anchor->i_tune = i; + break; + case 'D': + if (i < (int)anchor->d_tune) + anchor->d_tune = i; + break; + } + } + } + cfg_bitDecode(anchor->icacheOpts, topoReps, + anchor->caches[anchor->i_tune].cpuMap.source, SZ_CACHEREP); + cfg_bitDecode(anchor->dcacheOpts, topoReps, + anchor->caches[anchor->d_tune].cpuMap.source, SZ_CACHEREP); + TUNE_DEBUG("havege_tune %d/%d\n", anchor->i_tune, anchor->d_tune); +} +#ifdef TUNING_VFS_ENABLE +/** + * Return number of bits set in map + */ +static void cfg_bitClear( /* RETURN : None */ + TOPO_MAP *m) /* IN: bitmap */ +{ + memset(&m->bits[0], 0, MAX_BIT_IDX * sizeof(H_UINT)); +} +/** + * Return number of bits set in map + */ +static int cfg_bitCount( /* RETURN : None */ + TOPO_MAP *m) /* IN: bitmap */ +{ + int n, ct=0; + + for(n=-1;(n=cfg_bitNext(m,n))!=-1;ct++) ; + return ct; +} +#endif +/** + * decode bit representation + */ +static void cfg_bitDecode( /* RETURN: None */ + char *dest, /* OUT: target */ + const char **reps, /* IN: codes */ + H_UINT value, /* IN: value to decode */ + H_UINT size) /* IN: max range */ +{ + H_UINT i=0; + const char *s; + + size -= 1; + while(value!= 0 && *reps != 0) { + s = *reps++; + if ((value & 1) != 0) { + if (i>0 && i < size) + dest[i++] = ' '; + while(*s != 0 && i < size) + dest[i++] = *s++; + } + value >>= 1; + } + dest[i] = 0; +} +#if 0 +/** + * Display topo bit map - cpuset(7) convention is big-endian + */ +static void cfg_bitDisplay( /* RETURN : None */ + TOPO_MAP *m) /* IN: bitmap */ +{ + int n; + + for(n=m->msw;n>=0 && n < (int)MAX_BIT_IDX;n--) + printf(" %08x", m->bits[n]); +} +#endif +/** + * Test if maps intersect + */ +static int cfg_bitIntersect( /* RETURN: None */ + TOPO_MAP *m, /* OUT: bitmap */ + TOPO_MAP *t) /* IN: bit to set */ +{ + H_UINT i; + + for (i=0;i < MAX_BIT_IDX;i++) + if (0!=(m->bits[i] & t->bits[i])) + return 1; + return 0; +} +/** + * Merge two maps + */ +static void cfg_bitMerge( /* RETURN: None */ + TOPO_MAP *m, /* OUT: bitmap */ + TOPO_MAP *t) /* IN: bits to set */ +{ + int i; + + for (i=0;i<(int)MAX_BIT_IDX;i++) { + m->bits[i] |= t->bits[i]; + if (0 != m->bits[i] && i > m->msw) + m->msw = i; + } +} +/** + * Find next bit in topo bit map + */ +static int cfg_bitNext ( /* RETURN: index of next bit or -1 */ + TOPO_MAP *m, /* IN: bitmap */ + int n) /* IN: prev bit use -1 for first */ +{ + int bit, word; + + bit = (n+1) % BITS_PER_H_UINT; + for(word = (n+1) / BITS_PER_H_UINT;word <= m->msw && word < (int)MAX_BIT_IDX;word++) { + for(;bit<(int)BITS_PER_H_UINT; bit++) + if (m->bits[word] & 1<< bit) + return word * BITS_PER_H_UINT + bit; + bit = 0; + } + return -1; +} +/** + * Set a bit in the topo bit map + */ +static void cfg_bitSet( /* RETURN: None */ + TOPO_MAP *m, /* OUT: bitmap */ + int n) /* IN: bit to set */ +{ + int word; + + word = n / BITS_PER_H_UINT; + if (word < (int)MAX_BIT_IDX) { + if (word > m->msw) + m->msw = word; + m->bits[word] |= 1 << (n % BITS_PER_H_UINT); + } +} +/** + * Add cache description to configuration + */ +static void cfg_cacheAdd( /* RETURN: None */ + HOST_CFG *anchor, /* IN-OUT: configuration */ + H_UINT src, /* IN: source */ + H_UINT cpu, /* IN: use -1 for all */ + H_UINT level, /* IN: cache level */ + H_UINT type, /* IN: cache type */ + H_UINT kb) /* IN: cache size in kb */ +{ + int i; + + TUNE_DEBUG("cacheAdd(%x, %d,%d,%d,%c)\n", src, cpu, level, kb, type); + if (3 < level || 0 ==kb) return; + for(i = 0;i < anchor->ctCache;i++) + if (anchor->caches[i].level == level && + anchor->caches[i].type == type && + anchor->caches[i].size == kb) + break; + if (i >= MAX_CACHES) return; + if (-1 == (int)cpu) + cfg_bitMerge(&anchor->caches[i].cpuMap, &anchor->pCacheInfo); + else cfg_bitSet(&anchor->caches[i].cpuMap, cpu); + anchor->caches[i].cpuMap.source |= src; + if (i < anchor->ctCache) return; + anchor->caches[i].level = level; + anchor->caches[i].type = type; + anchor->caches[i].size = kb; + anchor->ctCache += 1; +} +/** + * Add cpu description to configuration + */ +static void cfg_cpuAdd( /* RETURN: None */ + HOST_CFG *anchor, /* IN-OUT: configuration */ + H_UINT src, /* IN: source */ + CPU_INST *inst) /* IN: instance */ +{ + int i=0; + + TUNE_DEBUG("cpuAdd(%x)\n", src); + if (NULL==inst) { + cfg_bitSet(&anchor->cpus[i].cpuMap, 0); + anchor->ctCpu = 1; + return; + } + for(i=0;i < anchor->ctCpu;i++) + if (0 != cfg_bitIntersect(&anchor->cpus[i].cpuMap, &inst->cpuMap)) { + cfg_bitMerge(&anchor->cpus[i].cpuMap, &inst->cpuMap); + anchor->cpus[i].cpuMap.source |= src; + return; + } + if (i >= MAX_CPUS) return; + memcpy(&anchor->cpus[i], inst, sizeof(CPU_INST)); + anchor->cpus[i].cpuMap.source = src; + anchor->ctCpu += 1; +} +#ifdef TUNE_DUMP +/** + * Diagnostic dump + */ +static void cfg_dump( /* RETURN: None */ + HOST_CFG *anchor) /* IN: configuration */ +{ + int i; + + for(i=0;i < anchor->ctCpu;i++) + printf("Cpu: %08x\n", + anchor->caches[i].cpuMap.source + ); + for(i = 0;i < anchor->ctCache;i++) + printf("Cache: %08x %d, %c, %d\n", + anchor->caches[i].cpuMap.source, + anchor->caches[i].level, + anchor->caches[i].type, + anchor->caches[i].size + ); +} +#endif + +#ifdef TUNING_CPUID_ENABLE +/************************* CPUID support ***************************************/ +/** + * Wrapper around the cpuid macro to assist in debugging + */ +static void cpuid( /* RETURN: none */ + int fn, /* IN: function code */ + int sfn, /* IN: subfunction */ + H_UINT *regs) /* IN-OUT: Workspace */ +{ + regs[2] = sfn; + CPUID(fn, regs); +} +/** + * Get configuration + */ +static void cpuid_config( /* RETURN: none */ + HOST_CFG *anchor) /* IN-OUT: result */ +{ + CPU_INST wsp; + H_UINT regs[4]; + char *s; + + if (HASCPUID(regs)) { + memset(&wsp, 0, sizeof(CPU_INST)); + wsp.flags |= SRC_CPUID_PRESENT; + cpuid(0x00,0,regs); + wsp.maxFn = regs[EAX]; + s = wsp.vendor; + CPUID_VENDOR(EBX); + CPUID_VENDOR(EDX); + CPUID_VENDOR(ECX); + if (regs[EBX] == 0x68747541) + wsp.flags |= SRC_CPUID_AMD; + (void)cpuid(0x80000000,0,regs); + wsp.maxFnx = regs[EAX]; + (void)cpuid(0x01,0,regs); + wsp.signature = regs[EAX]; + if ((regs[EDX] & (1<<28)) != 0) + wsp.flags |= SRC_CPUID_HT; + TUNE_DEBUG("cpuid_config %s fn=%d fnx=%x flags=%x\n", (char *)wsp.vendor, + wsp.maxFn, wsp.maxFnx, wsp.flags); + if (0!=(wsp.flags & SRC_CPUID_AMD)) + cpuid_configAmd(anchor, &wsp); + else cpuid_configIntel(anchor, &wsp); + } +} +/** + * AMD cpuid Configuration. Reference: Publication 25481, Revision 2.34, Sept 2010 + */ +static void cpuid_configAmd( + HOST_CFG *anchor, /* IN-OUT: result */ + CPU_INST *w) /* IN-OUT: Workspace */ +{ + H_UINT regs[4]; + int i, n; + + switch((w->maxFnx&15)) { + case 8: + cpuid(0x80000008,0,regs); + n = 1 + (regs[ECX] & 0xff); + for(i=0;i<n;i++) + cfg_bitSet(&w->cpuMap, i); + cfg_cpuAdd(anchor, SRC_CPUID_AMD8, w); + /* fallthrough */ + case 6: + cpuid(0x80000006,0,regs); + cfg_cacheAdd(anchor, SRC_CPUID_AMD6, -1, 2, 'U', (regs[ECX]>>16) & 0xffff); + cfg_cacheAdd(anchor, SRC_CPUID_AMD6, -1, 3, 'U', ((regs[EDX]>>18) & 0x3fff)<<9); + /* fallthrough */ + case 5: + cpuid(0x80000005,0,regs); + cfg_cacheAdd(anchor, SRC_CPUID_AMD5, -1, 1, 'D', (regs[ECX]>>24) & 0xff); + cfg_cacheAdd(anchor, SRC_CPUID_AMD5, -1, 1, 'I', (regs[EDX]>>24) & 0xff); + break; + } +} +/** + * Intel cpuid configuration. Reference: Publication 241618 + * Processor Identification and CPUID Instruction + * Application node 485, Jan 2011 + */ +static void cpuid_configIntel( + HOST_CFG *anchor, /* IN-OUT: result */ + CPU_INST *w) /* IN-OUT: Workspace */ +{ + H_UINT regs[4]; + + if (w->maxFn >=0x0b) { + regs[ECX] = 0; + cpuid(0x0b,0,regs); + if (regs[EBX]!=0) + w->flags |= SRC_CPUID_LEAFB; + } +#if 0 + if (w->flags & SRC_CPUID_HT) + ; + else + ; +#endif + if (w->maxFn >=4) + cpuid_configIntel4(anchor, w, regs); + if (w->maxFn >= 2) + cpuid_configIntel2(anchor, w, regs); +} +/** + * Configure caches using cpuid leaf 2. This is a legacy method that only determines + * level 1 cache. Still needed because trace cache is not reported elsewhere. + */ +static void cpuid_configIntel2( + HOST_CFG *anchor, /* IN-OUT: result */ + CPU_INST *w, /* IN-OUT: Workspace */ + H_UINT *regs) /* IN-OUT: registers */ +{ + /* L1 and Trace as per Intel application note 485, May 2012 */ + static const H_UINT defs[] = { + 0x06, 'I', 8 , /* 4-way set assoc, 32 byte line size */ + 0x08, 'I', 16 , /* 4-way set assoc, 32 byte line size */ + 0x09, 'I', 32 , /* 4-way set assoc, 64 byte line size + */ + 0x0a, 'D', 8 , /* 2 way set assoc, 32 byte line size */ + 0x0c, 'D', 16 , /* 4-way set assoc, 32 byte line size */ + 0x0d, 'D', 16 , /* 4-way set assoc, 64 byte line size + */ + 0x0e, 'D', 24 , /* 6-way set assoc, 64 byte line size */ + 0x10, 'D', 16 , /* 4-way set assoc, 64 byte line size */ + 0x15, 'I', 16 , /* 4-way set assoc, 64 byte line size */ + 0x2c, 'D', 32 , /* 8-way set assoc, 64 byte line size */ + 0x30, 'I', 32 , /* 8-way set assoc, 64 byte line size */ + 0x60, 'D', 16 , /* 8-way set assoc, sectored cache, 64 byte line size */ + 0x66, 'D', 8 , /* 4-way set assoc, sectored cache, 64 byte line size */ + 0x67, 'D', 16 , /* 4-way set assoc, sectored cache, 64 byte line size */ + 0x68, 'D', 32 , /* 4-way set assoc, sectored cache, 64 byte line size */ + 0x70, 'T', 12 , /* 8-way set assoc, trace cache */ + 0x71, 'T', 16 , /* 8-way set assoc, trace cache */ + 0x72, 'T', 32 , /* 8-way set assoc, trace cache */ + 0x73, 'T', 64 , /* 8-way set assoc, trace cache */ + 0x00, 0, 0 /* sentinel */ + }; + H_UINT i, j, n, m; + + cpuid(0x02,0,regs); + n = regs[EAX]&0xff; + while(n--) { + for (i=0;i<4;i++) { + if (0==(regs[i] & 0x80000000)) { + while(0 != regs[i]) { + m = regs[i] & 0xff; + if (m==0xff) + w->flags |= SRC_CPUID_LEAF4; + else for (j=0;0 != defs[j];j += 3) + if (defs[j]==m) { + cfg_cacheAdd(anchor, SRC_CPUID_INTEL2, -1, 1, defs[j+1], defs[j+2]); + break; + } + regs[i]>>=8; + } + } + } + if (n) cpuid(0x02,0,regs); + } +} +/** + * Configure caches using cpuid leaf 4 + */ +static void cpuid_configIntel4( + HOST_CFG *anchor, /* IN-OUT: result */ + CPU_INST *w, /* IN-OUT: Workspace */ + H_UINT *regs) /* IN-OUT: registers */ +{ + H_UINT level, type; + H_UINT i, j, lineSz, nParts, nWays, sz; + + for(i=0;i<MAX_CACHES;i++) { + cpuid(0x04,i,regs); + if (0==i) { + H_UINT n = 1 + (regs[EAX]>>26); + for(j=0;j<n;j++) + cfg_bitSet(&w->cpuMap, j); + cfg_cpuAdd(anchor, SRC_CPUID_INTEL4, w); + } + switch(regs[EAX] & 31) { + case 0: type = 0 ; break; + case 1: type = 'D'; break; + case 2: type = 'I'; break; + case 3: type = 'U'; break; + default: type = '?'; break; + } + if (0==type) break; + regs[EAX] >>= 5; + level = (H_UINT)(regs[EAX] & 7); + lineSz = 1 + (regs[EBX] & 0xfff); + regs[EBX] >>= 12; + nParts = 1 + (regs[EBX] & 0x3ff); + regs[EBX] >>= 10; + nWays = 1 + regs[EBX]; + sz = (nWays * nParts * lineSz * (regs[ECX]+1)) / 1024; + cfg_cacheAdd(anchor, SRC_CPUID_INTEL4, -1, level, type, sz); + } +} +#endif +/************************* CPUID support ***************************************/ +/************************* VFS support ***************************************/ +#ifdef TUNING_VFS_ENABLE +/** + * Get configuration + */ +static void vfs_config( + HOST_CFG *anchor) /* IN-OUT: result */ +{ + char path[FILENAME_MAX]; + CPU_INST inst; + H_UINT args[2]; + int n; + + args[0] = args[1] = 0; + snprintf(path, FILENAME_MAX, "%s/self/status", anchor->procfs); + if (-1 != vfs_configFile(anchor, path, vfs_configStatus)) + args[0] |= SRC_VFS_STATUS; + snprintf(path, FILENAME_MAX, "%s/devices/system/cpu/online", anchor->sysfs); + if (-1 != vfs_configFile(anchor, path, vfs_configOnline)) + args[0] |= SRC_VFS_ONLINE; + snprintf(path, FILENAME_MAX, "%s/cpuinfo", anchor->procfs); + if (-1 != vfs_configFile(anchor, path, vfs_configCpuInfo)) + args[0] |= SRC_VFS_CPUINFO; + snprintf(path, FILENAME_MAX, "%s/devices/system/cpu", anchor->sysfs); + if (-1 != vfs_configDir(anchor, path, vfs_configCpuDir, args)) { + memset(&inst, 0, sizeof(CPU_INST)); + args[0] |= SRC_VFS_CPUDIR; + for(n=-1;(n=cfg_bitNext(&anchor->pCpuInfo,n))!=-1;) + cfg_bitSet(&inst.cpuMap, n); + if (cfg_bitCount(&inst.cpuMap)>0) + cfg_cpuAdd(anchor, SRC_VFS_CPUINFO, &inst); + } + for(n=-1;(n=cfg_bitNext(&anchor->pCacheInfo,n))!=-1;) { + snprintf(path, FILENAME_MAX, "%s/devices/system/cpu/cpu%d/cache", anchor->sysfs, n); + args[1] = n; + vfs_configDir(anchor, path, vfs_configInfoCache, args); + } +} +/** + * Call back to get cpus and from cpu subdirectories + */ +static int vfs_configCpuDir( + HOST_CFG *anchor, /* IN-OUT: result */ + char *input, /* filename */ + H_UINT *pArg) /* parameter */ +{ + + (void)pArg; + if (strlen(input)> 3) { + char term[32]; + int cpu; + + cpu = atoi(input+3); + (void)snprintf(term, 32, "cpu%d", cpu); + if (!strcmp(term, input)) + cfg_bitSet(&anchor->pCacheInfo, cpu); + } + return 0; +} +/** + * Call back to get cpus from cpuinfo + */ +static int vfs_configCpuInfo( + HOST_CFG *anchor, /* IN-OUT: result */ + char *input) /* input text */ +{ + char *s = strchr(input, ':'); + + if (NULL != s) { + char key[32], value[32]; + *s++ = '\0'; + if (1==sscanf(input, "%31s", key) && 1==sscanf(s, "%31s", value)) { + if (!strcmp("processor",key)) + cfg_bitSet(&anchor->pCpuInfo, atoi(value)); + } + } + return 0; +} +/** + * Process a configuration directory + */ +static int vfs_configDir( + HOST_CFG *pAnchor, /* IN-OUT: result */ + char *path, /* IN: directory path */ + pDirFilter filter, /* IN: entry filter */ + H_UINT *pArg) /* IN: filter arg */ +{ + DIR *d; + int rv=-1; + + if (NULL != (d = opendir(path))) { + struct dirent *ent; + + while (NULL!=(ent = readdir(d))) { + if (0!=(rv = (*filter)(pAnchor, ent->d_name, pArg))) + break; + } + (void)closedir(d); + } + return rv; +} +/** + * Process a configuration file + */ +static int vfs_configFile( + HOST_CFG *pAnchor, /* IN-OUT: result */ + char *path, /* IN: file path */ + pFilter filter) /* IN: input filter */ +{ + FILE *f; + int rv=-1; + + if (NULL != (f = fopen(path, "rb"))) { + char buf[VFS_LINESIZE]; + + while(fgets(buf, VFS_LINESIZE, f)) + if (0!=(rv = (*filter)(pAnchor, buf))) + break; + fclose(f); + } + return rv; +} +/** + * Call back to get cache details + */ +static int vfs_configInfoCache( + HOST_CFG *pAnchor, /* IN-OUT: result */ + char *input, /* IN: path name */ + H_UINT *pArgs) +{ + if (strlen(input)> 5) { + char path[FILENAME_MAX]; + int idx; + + idx = atoi(input+5); + (void)snprintf(path, 32, "index%d", idx); + if (!strcmp(path, input)) { + int plen, ctype, level, size; + + plen = snprintf(path, FILENAME_MAX, "%s/devices/system/cpu/cpu%d/cache/index%d/level", + pAnchor->sysfs, pArgs[1], idx) - 5; + level = vfs_configFile(pAnchor, path, vfs_configInt); + strcpy(path+plen, "type"); + ctype = vfs_configFile(pAnchor, path, vfs_configType); + strcpy(path+plen, "size"); + size = vfs_configFile(pAnchor, path, vfs_configInt); + if (size == -1) + size = ctype == 'I' ? GENERIC_ICACHE : GENERIC_DCACHE; + cfg_cacheAdd(pAnchor, SRC_VFS_INDEX, pArgs[1], level, ctype, size); + } + } + return 0; +} +/** + * Call back to get cpus from online file + */ +static int vfs_configOnline( + HOST_CFG *anchor, /* IN-OUT: result */ + char *input) /* IN: input text */ +{ + vfs_parseList(&anchor->pOnline, input); + return 1; +} +/** + * Call back to return a single integer input + */ +static int vfs_configInt( + HOST_CFG *anchor, /* IN-OUT: result */ + char *input) /* IN: input text */ +{ + (void)anchor; + return atoi(input); +} +/** + * Call back to get cpus and memory from status file + */ +static int vfs_configStatus( + HOST_CFG *anchor, /* IN-OUT: result */ + char *input) /* IN: input text */ +{ + char *s = strchr(input, ':'); + + if (NULL != s) { + char key[32], value[VFS_LINESIZE-32]; + + *s++ = '\0'; + if (1==sscanf(input, "%31s", key) && 1==sscanf(s, "%223s", value)) { + if (!strcmp("Cpus_allowed", key)) + vfs_parseMask(&anchor->pAllowed, value); + else if (!strcmp("Mems_allowed", key)) + vfs_parseMask(&anchor->mAllowed, value); + } + } + return 0; +} +/** + * Call back to return the first byte of input + */ +int vfs_configType( + HOST_CFG *anchor, /* IN-OUT: result */ + char *input) /* IN: input text */ +{ + (void) anchor; + return input[0]; +} +/** +* Parse the List format described in cpuset(7) +*/ +static void vfs_parseList( /* RETURN: None */ + TOPO_MAP *map, /* OUT: bit map */ + char *input) /* IN: list text */ +{ + char *term, c; + int bounds[2], n; + + cfg_bitClear(map); + for(term = strtok(input, ",");term != NULL;term = strtok(NULL, ",")) { + n = bounds[0] = bounds[1] = 0; + while(0 != (int)(c = *term++)) + switch(c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + bounds[n] = bounds[n] * 10 + (int)(c - '0'); + break; + case '-': + n = 1; + } + cfg_bitSet(map, bounds[0]); + if (0 != n) + while(++bounds[0] <= bounds[1]) + cfg_bitSet(map, bounds[0]); + } +} +/** + * Parse the Mask format described in cpuset(7) + */ +static void vfs_parseMask( /* RETURN: None */ + TOPO_MAP *map, /* OUT: bit map */ + char *input) /* IN: list text */ +{ + char *term, c; + int m, n; + + cfg_bitClear(map); + for(term = strtok(input, ",");term != NULL;term = strtok(NULL, ",")) { + m = 0; + while(0 != (int)(c = *term++)) + switch(c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + m = m * 16 + (int)(c - '0'); + break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + m = m * 16 + (int)(c - 'A') + 10; + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + m = m * 16 + (int)(c - 'a') + 10; + } + for(n=map->msw;n>=0;n--) + map->bits[n+1] = map->bits[n]; + if (0 != map->bits[map->msw+1]) + map->msw++; + map->bits[0] = 0; + for(n=0;n<32;n++) + if (m & (1<<n)) + cfg_bitSet(map, n); + } +} +#endif + +/************************* VFS support ***************************************/ + diff --git a/src/havegetune.h b/src/havegetune.h new file mode 100644 index 0000000..5283a0b --- /dev/null +++ b/src/havegetune.h @@ -0,0 +1,121 @@ +/** + ** 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 + ** + ** 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/>. + */ +#ifndef HAVEGETUNE_H +#define HAVEGETUNE_H + +#include "havegecollect.h" +/** + * Some systems supply a maximum + */ +#ifndef FILENAME_MAX +#define FILENAME_MAX 256 /* Max path length */ +#endif +/** + * Limits + */ +#define MAX_BIT_IDX (256/BITS_PER_H_UINT) /* Size of resource bitmaps */ +#define MAX_CACHES 8 /* Max cache types */ +#define MAX_CPUS 8 /* Max cpu types */ +/** + * Object used to represent a set of objects + */ +typedef struct { + H_UINT bits[MAX_BIT_IDX]; + int msw; + H_UINT source; +} TOPO_MAP; +/** + * Cache instance + */ +typedef struct { + TOPO_MAP cpuMap; /* what cpus have this cache */ + H_UINT type; /* 'I'nstruction, 'D'ata, 'U'nified, 'T'race */ + H_UINT level; /* 0-15................ */ + H_UINT size; /* size in KB */ +} CACHE_INST; +/** + * Sources for CACHE_INST TOPO_MAP + */ +#define SRC_DEFAULT 0x00001 +#define SRC_PARAM 0x00002 +#define SRC_CPUID_AMD6 0x00004 +#define SRC_CPUID_AMD5 0x00008 +#define SRC_CPUID_INTEL2 0x00010 +#define SRC_CPUID_INTEL4 0x00020 +#define SRC_VFS_INDEX 0x00040 +/** + * CPU instance + */ +typedef struct { + TOPO_MAP cpuMap; /* what cpus have this config */ + H_UINT signature; /* processor signature */ + H_UINT flags; + H_UINT maxFn; + H_UINT maxFnx; + char vendor[16]; +} CPU_INST; +/** + * Sources for CPU_INST TOPO_MAP + */ +#define SRC_CPUID_PRESENT 0x00100 +#define SRC_CPUID_HT 0x00200 +#define SRC_CPUID_AMD 0x00400 +#define SRC_CPUID_AMD8 0x00800 +#define SRC_CPUID_LEAFB 0x01000 +#define SRC_CPUID_LEAF4 0x02000 +#define SRC_VFS_STATUS 0x04000 +#define SRC_VFS_ONLINE 0x08000 +#define SRC_VFS_CPUINFO 0x10000 +#define SRC_VFS_CPUDIR 0x20000 +/** + * Size of representation fields + */ +#define SZ_BUILDREP 32 +#define SZ_CPUREP 64 +#define SZ_CACHEREP 32 +/** + * The result of tuning + */ +typedef struct { + char *procfs; /* where proc is mounted */ + char *sysfs; /* where sys is mounted */ + char buildOpts[SZ_BUILDREP]; /* build options */ + char cpuOpts[SZ_CPUREP]; /* cpu options */ + char icacheOpts[SZ_CACHEREP]; /* icache options */ + char dcacheOpts[SZ_CACHEREP]; /* dcache options */ + TOPO_MAP pAllowed; /* allowed processors */ + TOPO_MAP pOnline; /* processors online */ + TOPO_MAP pCpuInfo; /* processors with info */ + TOPO_MAP pCacheInfo; /* processors with cache info */ + TOPO_MAP mAllowed; /* allowed memory */ + H_UINT a_cpu; /* suggested cpu */ + H_UINT i_tune; /* suggested i cache value */ + H_UINT d_tune; /* suggested d cache value */ + int ctCpu; /* number of cpu types */ + int ctCache; /* number of cache items */ + CPU_INST cpus[MAX_CPUS]; /* cpu instances */ + CACHE_INST caches[MAX_CACHES+2]; /* cache instances */ +} HOST_CFG; +/** + * Tuning interface + */ +void havege_tune(HOST_CFG *env, H_PARAMS *params); + +#endif diff --git a/src/oneiteration.h b/src/oneiteration.h new file mode 100644 index 0000000..e6b16d8 --- /dev/null +++ b/src/oneiteration.h @@ -0,0 +1,201 @@ +/** + ** Simple entropy harvester based upon the havege RNG + ** + ** Copyright 2018-2021 Jirka Hladky hladky DOT jiri AT gmail DOT com + ** Copyright 2009-2013 Gary Wuertz gary@issiweb.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 source is an adaptation of work released as + ** + ** Copyright (C) 2006 - AndrĂ© Seznec - Olivier Rochecouste + ** + ** under version 2.1 of the GNU Lesser General Public License + ** + ** The original form is retained with minor variable renames for + ** more consistent macro itilization. See havegecollect.c for + ** details. + */ + +/* ------------------------------------------------------------------------ + * On average, one iteration accesses two 8-word blocks in the PWALK + * table, and generates 16 words in the RESULT array. + * + * The data read in the Walk table are updated and permuted after each use. + * The result of the hardware clock counter read is used for this update. + * + * 21 conditional tests are present. The conditional tests are grouped in + * two nested groups of 10 conditional tests and 1 test that controls the + * permutation. + * + * In average, there should be 4 tests executed and, in average, 2 of them + * should be mispredicted. + * ------------------------------------------------------------------------ + */ + + PTTEST = PT >> 20; + + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + } + } + } + } + } + } + } + } + } + }; + + PTTEST = PTTEST >> 1; + pt = (PT >> 18) & 7; + + PT = PT & ANDPT; + + HARDCLOCKR(HTICK1); + + Pt0 = &PWALK[PT]; + Pt1 = &PWALK[PT2]; + Pt2 = &PWALK[PT ^ 1]; + Pt3 = &PWALK[PT2 ^ 4]; + + RESULT[i++] ^= *Pt0; + RESULT[i++] ^= *Pt1; + RESULT[i++] ^= *Pt2; + RESULT[i++] ^= *Pt3; + + inter = ROR32(*Pt0,1) ^ HTICK1; + *Pt0 = ROR32(*Pt1,2) ^ HTICK1; + *Pt1 = inter; + *Pt2 = ROR32(*Pt2, 3) ^ HTICK1; + *Pt3 = ROR32(*Pt3, 4) ^ HTICK1; + + Pt0 = &PWALK[PT ^ 2]; + Pt1 = &PWALK[PT2 ^ 2]; + Pt2 = &PWALK[PT ^ 3]; + Pt3 = &PWALK[PT2 ^ 6]; + + RESULT[i++] ^= *Pt0; + RESULT[i++] ^= *Pt1; + RESULT[i++] ^= *Pt2; + RESULT[i++] ^= *Pt3; + + if (PTTEST & 1) { + Ptinter = Pt0; + Pt2 = Pt0; + Pt0 = Ptinter; + } + + PTTEST = (PT2 >> 18); + inter = ROR32(*Pt0, 5) ^ HTICK1; + *Pt0 = ROR32(*Pt1, 6) ^ HTICK1; + *Pt1 = inter; + + HARDCLOCKR(HTICK2); + + *Pt2 = ROR32(*Pt2, 7) ^ HTICK2; + *Pt3 = ROR32(*Pt3, 8) ^ HTICK2; + + Pt0 = &PWALK[PT ^ 4]; + Pt1 = &PWALK[PT2 ^ 1]; + + PT2 = (RESULT[(i - 8) ^ PT1] ^ PWALK[PT2 ^ PT1 ^ 7]); + PT2 = ((PT2 & ANDPT) & (0xfffffff7)) ^ ((PT ^ 8) & 0x8); + + /* avoid PT and PT2 to point on the same cache block */ + PT1 = ((PT2 >> 28) & 7); + + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + if (PTTEST & 1) { + PTTEST ^= 3; PTTEST = PTTEST >> 1; + } + } + } + } + } + } + } + } + } + }; + + Pt2 = &PWALK[PT ^ 5]; + Pt3 = &PWALK[PT2 ^ 5]; + + RESULT[i++] ^= *Pt0; + RESULT[i++] ^= *Pt1; + RESULT[i++] ^= *Pt2; + RESULT[i++] ^= *Pt3; + + inter = ROR32(*Pt0 , 9) ^ HTICK2; + *Pt0 = ROR32(*Pt1 , 10) ^ HTICK2; + *Pt1 = inter; + *Pt2 = ROR32(*Pt2, 11) ^ HTICK2; + *Pt3 = ROR32(*Pt3, 12) ^ HTICK2; + + Pt0 = &PWALK[PT ^ 6]; + Pt1 = &PWALK[PT2 ^ 3]; + Pt2 = &PWALK[PT ^ 7]; + Pt3 = &PWALK[PT2 ^ 7]; + + RESULT[i++] ^= *Pt0; + RESULT[i++] ^= *Pt1; + RESULT[i++] ^= *Pt2; + RESULT[i++] ^= *Pt3; + + inter = ROR32(*Pt0, 13) ^ HTICK2; + *Pt0 = ROR32(*Pt1, 14) ^ HTICK2; + *Pt1 = inter; + *Pt2 = ROR32(*Pt2, 15) ^ HTICK2; + *Pt3 = ROR32(*Pt3, 16) ^ HTICK2; + + /* avoid PT and PT2 to point on the same cache block */ + PT = (((RESULT[(i - 8) ^ pt] ^ PWALK[PT ^ pt ^ 7])) & + (0xffffffef)) ^ ((PT2 ^ 0x10) & 0x10); |