295 lines · 7.0 KB · C
RawDownload
/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause *//* * Test interface for Jitter RNG. * * Copyright (C) 2023, Stephan Mueller <smueller@chronox.de> */ #include <linux/debugfs.h>#include <linux/module.h>#include <linux/uaccess.h> #include "jitterentropy.h" #define JENT_TEST_RINGBUFFER_SIZE	(1<<10)#define JENT_TEST_RINGBUFFER_MASK	(JENT_TEST_RINGBUFFER_SIZE - 1) struct jent_testing {	u64 jent_testing_rb[JENT_TEST_RINGBUFFER_SIZE];	u32 rb_reader;	atomic_t rb_writer;	atomic_t jent_testing_enabled;	spinlock_t lock;	wait_queue_head_t read_wait;}; static struct dentry *jent_raw_debugfs_root = NULL; /*************************** Generic Data Handling ****************************/ /* * boot variable: * 0 ==> No boot test, gathering of runtime data allowed * 1 ==> Boot test enabled and ready for collecting data, gathering runtime *	 data is disabled * 2 ==> Boot test completed and disabled, gathering of runtime data is *	 disabled */ static void jent_testing_reset(struct jent_testing *data){	unsigned long flags; 	spin_lock_irqsave(&data->lock, flags);	data->rb_reader = 0;	atomic_set(&data->rb_writer, 0);	spin_unlock_irqrestore(&data->lock, flags);} static void jent_testing_data_init(struct jent_testing *data, u32 boot){	/*	 * The boot time testing implies we have a running test. If the	 * caller wants to clear it, he has to unset the boot_test flag	 * at runtime via sysfs to enable regular runtime testing	 */	if (boot)		return; 	jent_testing_reset(data);	atomic_set(&data->jent_testing_enabled, 1);	pr_warn("Enabling data collection\n");} static void jent_testing_fini(struct jent_testing *data, u32 boot){	/* If we have boot data, we do not reset yet to allow data to be read */	if (boot)		return; 	atomic_set(&data->jent_testing_enabled, 0);	jent_testing_reset(data);	pr_warn("Disabling data collection\n");} static bool jent_testing_store(struct jent_testing *data, u64 value,			       u32 *boot){	unsigned long flags; 	if (!atomic_read(&data->jent_testing_enabled) && (*boot != 1))		return false; 	spin_lock_irqsave(&data->lock, flags); 	/*	 * Disable entropy testing for boot time testing after ring buffer	 * is filled.	 */	if (*boot) {		if (((u32)atomic_read(&data->rb_writer)) >		     JENT_TEST_RINGBUFFER_SIZE) {			*boot = 2;			pr_warn_once("One time data collection test disabled\n");			spin_unlock_irqrestore(&data->lock, flags);			return false;		} 		if (atomic_read(&data->rb_writer) == 1)			pr_warn("One time data collection test enabled\n");	} 	data->jent_testing_rb[((u32)atomic_read(&data->rb_writer)) &			      JENT_TEST_RINGBUFFER_MASK] = value;	atomic_inc(&data->rb_writer); 	spin_unlock_irqrestore(&data->lock, flags); 	if (wq_has_sleeper(&data->read_wait))		wake_up_interruptible(&data->read_wait); 	return true;} static bool jent_testing_have_data(struct jent_testing *data){	return ((((u32)atomic_read(&data->rb_writer)) &		 JENT_TEST_RINGBUFFER_MASK) !=		 (data->rb_reader & JENT_TEST_RINGBUFFER_MASK));} static int jent_testing_reader(struct jent_testing *data, u32 *boot,			       u8 *outbuf, u32 outbuflen){	unsigned long flags;	int collected_data = 0; 	jent_testing_data_init(data, *boot); 	while (outbuflen) {		u32 writer = (u32)atomic_read(&data->rb_writer); 		spin_lock_irqsave(&data->lock, flags); 		/* We have no data or reached the writer. */		if (!writer || (writer == data->rb_reader)) { 			spin_unlock_irqrestore(&data->lock, flags); 			/*			 * Now we gathered all boot data, enable regular data			 * collection.			 */			if (*boot) {				*boot = 0;				goto out;			} 			wait_event_interruptible(data->read_wait,						 jent_testing_have_data(data));			if (signal_pending(current)) {				collected_data = -ERESTARTSYS;				goto out;			} 			continue;		} 		/* We copy out word-wise */		if (outbuflen < sizeof(u64)) {			spin_unlock_irqrestore(&data->lock, flags);			goto out;		} 		memcpy(outbuf, &data->jent_testing_rb[data->rb_reader],		       sizeof(u64));		data->rb_reader++; 		spin_unlock_irqrestore(&data->lock, flags); 		outbuf += sizeof(u64);		outbuflen -= sizeof(u64);		collected_data += sizeof(u64);	} out:	jent_testing_fini(data, *boot);	return collected_data;} static int jent_testing_extract_user(struct file *file, char __user *buf,				     size_t nbytes, loff_t *ppos,				     int (*reader)(u8 *outbuf, u32 outbuflen)){	u8 *tmp, *tmp_aligned;	int ret = 0, large_request = (nbytes > 256); 	if (!nbytes)		return 0; 	/*	 * The intention of this interface is for collecting at least	 * 1000 samples due to the SP800-90B requirements. However, due to	 * memory and performance constraints, it is not desirable to allocate	 * 8000 bytes of memory. Instead, we allocate space for only 125	 * samples, which will allow the user to collect all 1000 samples using	 * 8 calls to this interface.	 */	tmp = kmalloc(125 * sizeof(u64) + sizeof(u64), GFP_KERNEL);	if (!tmp)		return -ENOMEM; 	tmp_aligned = PTR_ALIGN(tmp, sizeof(u64)); 	while (nbytes) {		int i; 		if (large_request && need_resched()) {			if (signal_pending(current)) {				if (ret == 0)					ret = -ERESTARTSYS;				break;			}			schedule();		} 		i = min_t(int, nbytes, 125 * sizeof(u64));		i = reader(tmp_aligned, i);		if (i <= 0) {			if (i < 0)				ret = i;			break;		}		if (copy_to_user(buf, tmp_aligned, i)) {			ret = -EFAULT;			break;		} 		nbytes -= i;		buf += i;		ret += i;	} 	kfree_sensitive(tmp); 	if (ret > 0)		*ppos += ret; 	return ret;} /************** Raw High-Resolution Timer Entropy Data Handling **************/ static u32 boot_raw_hires_test = 0;module_param(boot_raw_hires_test, uint, 0644);MODULE_PARM_DESC(boot_raw_hires_test,		 "Enable gathering boot time high resolution timer entropy of the first Jitter RNG entropy events"); static struct jent_testing jent_raw_hires = {	.rb_reader = 0,	.rb_writer = ATOMIC_INIT(0),	.lock      = __SPIN_LOCK_UNLOCKED(jent_raw_hires.lock),	.read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(jent_raw_hires.read_wait)}; int jent_raw_hires_entropy_store(__u64 value){	return jent_testing_store(&jent_raw_hires, value, &boot_raw_hires_test);}EXPORT_SYMBOL(jent_raw_hires_entropy_store); static int jent_raw_hires_entropy_reader(u8 *outbuf, u32 outbuflen){	return jent_testing_reader(&jent_raw_hires, &boot_raw_hires_test,				   outbuf, outbuflen);} static ssize_t jent_raw_hires_read(struct file *file, char __user *to,				   size_t count, loff_t *ppos){	return jent_testing_extract_user(file, to, count, ppos,					 jent_raw_hires_entropy_reader);} static const struct file_operations jent_raw_hires_fops = {	.owner = THIS_MODULE,	.read = jent_raw_hires_read,}; /******************************* Initialization *******************************/ void jent_testing_init(void){	jent_raw_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); 	debugfs_create_file_unsafe("jent_raw_hires", 0400,				   jent_raw_debugfs_root, NULL,				   &jent_raw_hires_fops);}EXPORT_SYMBOL(jent_testing_init); void jent_testing_exit(void){	debugfs_remove_recursive(jent_raw_debugfs_root);}EXPORT_SYMBOL(jent_testing_exit);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t