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