|  | /* 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); |