From: Torsten Duwe <duwe@suse.de>
Subject: s390: provide hardware randomness from zcrypt card to /dev/random
Patch-mainline: not yet submitted
References: fate 310591, bnc#709266, bnc#709269, bnc#806825,bnc#814948

Running completely virtualised, system Z severely lacks good true random sources.
Gathering entropy in a virtual environment is difficult. To compensate, there is
specialised crypto hardware which includes a source for hardware randomness;
the zcrypt driver is able to access this random source. This patch adds an interface
to the random pool for feeding entropy in-kernel and enables the driver to utilise
it. This may be considered a kernel equivalent of the user-space entropy daemon which
works automatically and without the risk of random bits being compromised
in user space.

Signed-off-by: Torsten Duwe <duwe@suse.de>
Acked-by: Torsten Duwe <duwe@suse.de>

---
 drivers/char/random.c            |   21 +++++++++++++++++++++
 drivers/s390/crypto/zcrypt_api.c |   39 +++++++++++++++++++++++++++++++++++++++
 include/linux/hw_random.h        |    2 ++
 3 files changed, 62 insertions(+)

--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -250,6 +250,7 @@
 #include <linux/interrupt.h>
 #include <linux/mm.h>
 #include <linux/spinlock.h>
+#include <linux/kthread.h>
 #include <linux/percpu.h>
 #include <linux/cryptohash.h>
 #include <linux/fips.h>
@@ -1347,3 +1347,23 @@ randomize_range(unsigned long start, uns
 		return 0;
 	return PAGE_ALIGN(get_random_int() % range + start);
 }
+
+/* Interface for in-kernel drivers of true hardware RNGs.
+ * Those devices may produce endless random bits and will be throttled
+ * when our pool is full.
+ */
+void add_hwgenerator_randomness(const char *buffer, size_t count)
+{
+	struct entropy_store *poolp = &input_pool;
+
+	/* Suspend writing if we're above the trickle threshold.
+	 * We'll be woken up again once below random_write_wakeup_thresh,
+	 * or when the calling thread is about to terminate.
+	 */
+	wait_event_interruptible(random_write_wait, kthread_should_stop() ||
+				 input_pool.entropy_count <= trickle_thresh);
+	mix_pool_bytes(poolp, buffer, count, NULL);
+	credit_entropy_bits(poolp, count*8);
+}
+EXPORT_SYMBOL_GPL(add_hwgenerator_randomness);
+
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -39,6 +39,8 @@
 #include <asm/atomic.h>
 #include <asm/uaccess.h>
 #include <linux/hw_random.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
 
 #include "zcrypt_api.h"
 
@@ -1117,6 +1119,7 @@ static int zcrypt_rng_device_count;
 static u32 *zcrypt_rng_buffer;
 static int zcrypt_rng_buffer_index;
 static DEFINE_MUTEX(zcrypt_rng_mutex);
+static struct task_struct *zcrypt_hwrng_fill;
 
 static int zcrypt_rng_data_read(struct hwrng *rng, u32 *data)
 {
@@ -1141,6 +1144,36 @@ static struct hwrng zcrypt_rng_dev = {
 	.data_read	= zcrypt_rng_data_read,
 };
 
+static int zcrypt_hwrng_fillfn(void * unused)
+{
+	long rc;
+	
+	while (!kthread_should_stop()) {
+		rc = zcrypt_rng((char*)zcrypt_rng_buffer);
+		if (rc == -ENODEV || rc == -EINVAL|| rc == -ENOMEM) {
+			printk(KERN_ERR "zcrypt_rng unavailable: %ld", rc);
+			break;
+		}
+		if (rc < 0) {
+			printk(KERN_ERR "zcrypt_rng unknown error: %ld", rc);
+			break;
+		}
+		if (rc == -EAGAIN || rc == -ERESTARTSYS) {
+			printk(KERN_DEBUG "zcrypt_rng interrupted: %ld", rc);
+			msleep_interruptible(1000);
+			continue;
+		}
+		if (rc == 0) {
+			printk(KERN_ERR "zcrypt_rng: no data available");
+			msleep_interruptible(10000);
+			continue;
+		}
+		add_hwgenerator_randomness((void*)zcrypt_rng_buffer, rc);
+	}
+	zcrypt_hwrng_fill = 0;
+	return 0;
+}
+
 static int zcrypt_rng_device_add(void)
 {
 	int rc = 0;
@@ -1157,6 +1189,11 @@ static int zcrypt_rng_device_add(void)
 		if (rc)
 			goto out_free;
 		zcrypt_rng_device_count = 1;
+		zcrypt_hwrng_fill = kthread_run(zcrypt_hwrng_fillfn, NULL, "zc_hwrng");
+		if (zcrypt_hwrng_fill == ERR_PTR(-ENOMEM)) {
+			printk(KERN_ERR "zcrypt_hwrng_fill thread creation failed");
+			zcrypt_hwrng_fill = NULL;
+		}
 	} else
 		zcrypt_rng_device_count++;
 	mutex_unlock(&zcrypt_rng_mutex);
@@ -1174,6 +1211,8 @@ static void zcrypt_rng_device_remove(voi
 	mutex_lock(&zcrypt_rng_mutex);
 	zcrypt_rng_device_count--;
 	if (zcrypt_rng_device_count == 0) {
+		if (zcrypt_hwrng_fill)
+			kthread_stop(zcrypt_hwrng_fill);
 		hwrng_unregister(&zcrypt_rng_dev);
 		free_page((unsigned long) zcrypt_rng_buffer);
 	}
--- a/include/linux/hw_random.h
+++ b/include/linux/hw_random.h
@@ -47,5 +47,7 @@ struct hwrng {
 extern int hwrng_register(struct hwrng *rng);
 /** Unregister a Hardware Random Number Generator driver. */
 extern void hwrng_unregister(struct hwrng *rng);
+/** Feed random bits into the pool. */
+extern void add_hwgenerator_randomness(const char *buffer, size_t count);
 
 #endif /* LINUX_HWRANDOM_H_ */
