162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Test interface for Jitter RNG.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2023, Stephan Mueller <smueller@chronox.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/debugfs.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/uaccess.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "jitterentropy.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define JENT_TEST_RINGBUFFER_SIZE	(1<<10)
1562306a36Sopenharmony_ci#define JENT_TEST_RINGBUFFER_MASK	(JENT_TEST_RINGBUFFER_SIZE - 1)
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct jent_testing {
1862306a36Sopenharmony_ci	u32 jent_testing_rb[JENT_TEST_RINGBUFFER_SIZE];
1962306a36Sopenharmony_ci	u32 rb_reader;
2062306a36Sopenharmony_ci	atomic_t rb_writer;
2162306a36Sopenharmony_ci	atomic_t jent_testing_enabled;
2262306a36Sopenharmony_ci	spinlock_t lock;
2362306a36Sopenharmony_ci	wait_queue_head_t read_wait;
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic struct dentry *jent_raw_debugfs_root = NULL;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*************************** Generic Data Handling ****************************/
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * boot variable:
3262306a36Sopenharmony_ci * 0 ==> No boot test, gathering of runtime data allowed
3362306a36Sopenharmony_ci * 1 ==> Boot test enabled and ready for collecting data, gathering runtime
3462306a36Sopenharmony_ci *	 data is disabled
3562306a36Sopenharmony_ci * 2 ==> Boot test completed and disabled, gathering of runtime data is
3662306a36Sopenharmony_ci *	 disabled
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void jent_testing_reset(struct jent_testing *data)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	unsigned long flags;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	spin_lock_irqsave(&data->lock, flags);
4462306a36Sopenharmony_ci	data->rb_reader = 0;
4562306a36Sopenharmony_ci	atomic_set(&data->rb_writer, 0);
4662306a36Sopenharmony_ci	spin_unlock_irqrestore(&data->lock, flags);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void jent_testing_data_init(struct jent_testing *data, u32 boot)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	/*
5262306a36Sopenharmony_ci	 * The boot time testing implies we have a running test. If the
5362306a36Sopenharmony_ci	 * caller wants to clear it, he has to unset the boot_test flag
5462306a36Sopenharmony_ci	 * at runtime via sysfs to enable regular runtime testing
5562306a36Sopenharmony_ci	 */
5662306a36Sopenharmony_ci	if (boot)
5762306a36Sopenharmony_ci		return;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	jent_testing_reset(data);
6062306a36Sopenharmony_ci	atomic_set(&data->jent_testing_enabled, 1);
6162306a36Sopenharmony_ci	pr_warn("Enabling data collection\n");
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void jent_testing_fini(struct jent_testing *data, u32 boot)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	/* If we have boot data, we do not reset yet to allow data to be read */
6762306a36Sopenharmony_ci	if (boot)
6862306a36Sopenharmony_ci		return;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	atomic_set(&data->jent_testing_enabled, 0);
7162306a36Sopenharmony_ci	jent_testing_reset(data);
7262306a36Sopenharmony_ci	pr_warn("Disabling data collection\n");
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic bool jent_testing_store(struct jent_testing *data, u32 value,
7662306a36Sopenharmony_ci			       u32 *boot)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	unsigned long flags;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (!atomic_read(&data->jent_testing_enabled) && (*boot != 1))
8162306a36Sopenharmony_ci		return false;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	spin_lock_irqsave(&data->lock, flags);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/*
8662306a36Sopenharmony_ci	 * Disable entropy testing for boot time testing after ring buffer
8762306a36Sopenharmony_ci	 * is filled.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	if (*boot) {
9062306a36Sopenharmony_ci		if (((u32)atomic_read(&data->rb_writer)) >
9162306a36Sopenharmony_ci		     JENT_TEST_RINGBUFFER_SIZE) {
9262306a36Sopenharmony_ci			*boot = 2;
9362306a36Sopenharmony_ci			pr_warn_once("One time data collection test disabled\n");
9462306a36Sopenharmony_ci			spin_unlock_irqrestore(&data->lock, flags);
9562306a36Sopenharmony_ci			return false;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		if (atomic_read(&data->rb_writer) == 1)
9962306a36Sopenharmony_ci			pr_warn("One time data collection test enabled\n");
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	data->jent_testing_rb[((u32)atomic_read(&data->rb_writer)) &
10362306a36Sopenharmony_ci			      JENT_TEST_RINGBUFFER_MASK] = value;
10462306a36Sopenharmony_ci	atomic_inc(&data->rb_writer);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	spin_unlock_irqrestore(&data->lock, flags);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (wq_has_sleeper(&data->read_wait))
10962306a36Sopenharmony_ci		wake_up_interruptible(&data->read_wait);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return true;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic bool jent_testing_have_data(struct jent_testing *data)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	return ((((u32)atomic_read(&data->rb_writer)) &
11762306a36Sopenharmony_ci		 JENT_TEST_RINGBUFFER_MASK) !=
11862306a36Sopenharmony_ci		 (data->rb_reader & JENT_TEST_RINGBUFFER_MASK));
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int jent_testing_reader(struct jent_testing *data, u32 *boot,
12262306a36Sopenharmony_ci			       u8 *outbuf, u32 outbuflen)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	unsigned long flags;
12562306a36Sopenharmony_ci	int collected_data = 0;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	jent_testing_data_init(data, *boot);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	while (outbuflen) {
13062306a36Sopenharmony_ci		u32 writer = (u32)atomic_read(&data->rb_writer);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		spin_lock_irqsave(&data->lock, flags);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		/* We have no data or reached the writer. */
13562306a36Sopenharmony_ci		if (!writer || (writer == data->rb_reader)) {
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci			spin_unlock_irqrestore(&data->lock, flags);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci			/*
14062306a36Sopenharmony_ci			 * Now we gathered all boot data, enable regular data
14162306a36Sopenharmony_ci			 * collection.
14262306a36Sopenharmony_ci			 */
14362306a36Sopenharmony_ci			if (*boot) {
14462306a36Sopenharmony_ci				*boot = 0;
14562306a36Sopenharmony_ci				goto out;
14662306a36Sopenharmony_ci			}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci			wait_event_interruptible(data->read_wait,
14962306a36Sopenharmony_ci						 jent_testing_have_data(data));
15062306a36Sopenharmony_ci			if (signal_pending(current)) {
15162306a36Sopenharmony_ci				collected_data = -ERESTARTSYS;
15262306a36Sopenharmony_ci				goto out;
15362306a36Sopenharmony_ci			}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci			continue;
15662306a36Sopenharmony_ci		}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		/* We copy out word-wise */
15962306a36Sopenharmony_ci		if (outbuflen < sizeof(u32)) {
16062306a36Sopenharmony_ci			spin_unlock_irqrestore(&data->lock, flags);
16162306a36Sopenharmony_ci			goto out;
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		memcpy(outbuf, &data->jent_testing_rb[data->rb_reader],
16562306a36Sopenharmony_ci		       sizeof(u32));
16662306a36Sopenharmony_ci		data->rb_reader++;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		spin_unlock_irqrestore(&data->lock, flags);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		outbuf += sizeof(u32);
17162306a36Sopenharmony_ci		outbuflen -= sizeof(u32);
17262306a36Sopenharmony_ci		collected_data += sizeof(u32);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ciout:
17662306a36Sopenharmony_ci	jent_testing_fini(data, *boot);
17762306a36Sopenharmony_ci	return collected_data;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int jent_testing_extract_user(struct file *file, char __user *buf,
18162306a36Sopenharmony_ci				     size_t nbytes, loff_t *ppos,
18262306a36Sopenharmony_ci				     int (*reader)(u8 *outbuf, u32 outbuflen))
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	u8 *tmp, *tmp_aligned;
18562306a36Sopenharmony_ci	int ret = 0, large_request = (nbytes > 256);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (!nbytes)
18862306a36Sopenharmony_ci		return 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/*
19162306a36Sopenharmony_ci	 * The intention of this interface is for collecting at least
19262306a36Sopenharmony_ci	 * 1000 samples due to the SP800-90B requirements. So, we make no
19362306a36Sopenharmony_ci	 * effort in avoiding allocating more memory that actually needed
19462306a36Sopenharmony_ci	 * by the user. Hence, we allocate sufficient memory to always hold
19562306a36Sopenharmony_ci	 * that amount of data.
19662306a36Sopenharmony_ci	 */
19762306a36Sopenharmony_ci	tmp = kmalloc(JENT_TEST_RINGBUFFER_SIZE + sizeof(u32), GFP_KERNEL);
19862306a36Sopenharmony_ci	if (!tmp)
19962306a36Sopenharmony_ci		return -ENOMEM;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	tmp_aligned = PTR_ALIGN(tmp, sizeof(u32));
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	while (nbytes) {
20462306a36Sopenharmony_ci		int i;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		if (large_request && need_resched()) {
20762306a36Sopenharmony_ci			if (signal_pending(current)) {
20862306a36Sopenharmony_ci				if (ret == 0)
20962306a36Sopenharmony_ci					ret = -ERESTARTSYS;
21062306a36Sopenharmony_ci				break;
21162306a36Sopenharmony_ci			}
21262306a36Sopenharmony_ci			schedule();
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		i = min_t(int, nbytes, JENT_TEST_RINGBUFFER_SIZE);
21662306a36Sopenharmony_ci		i = reader(tmp_aligned, i);
21762306a36Sopenharmony_ci		if (i <= 0) {
21862306a36Sopenharmony_ci			if (i < 0)
21962306a36Sopenharmony_ci				ret = i;
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci		if (copy_to_user(buf, tmp_aligned, i)) {
22362306a36Sopenharmony_ci			ret = -EFAULT;
22462306a36Sopenharmony_ci			break;
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci		nbytes -= i;
22862306a36Sopenharmony_ci		buf += i;
22962306a36Sopenharmony_ci		ret += i;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	kfree_sensitive(tmp);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (ret > 0)
23562306a36Sopenharmony_ci		*ppos += ret;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return ret;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/************** Raw High-Resolution Timer Entropy Data Handling **************/
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic u32 boot_raw_hires_test = 0;
24362306a36Sopenharmony_cimodule_param(boot_raw_hires_test, uint, 0644);
24462306a36Sopenharmony_ciMODULE_PARM_DESC(boot_raw_hires_test,
24562306a36Sopenharmony_ci		 "Enable gathering boot time high resolution timer entropy of the first Jitter RNG entropy events");
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic struct jent_testing jent_raw_hires = {
24862306a36Sopenharmony_ci	.rb_reader = 0,
24962306a36Sopenharmony_ci	.rb_writer = ATOMIC_INIT(0),
25062306a36Sopenharmony_ci	.lock      = __SPIN_LOCK_UNLOCKED(jent_raw_hires.lock),
25162306a36Sopenharmony_ci	.read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(jent_raw_hires.read_wait)
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ciint jent_raw_hires_entropy_store(__u32 value)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	return jent_testing_store(&jent_raw_hires, value, &boot_raw_hires_test);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ciEXPORT_SYMBOL(jent_raw_hires_entropy_store);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int jent_raw_hires_entropy_reader(u8 *outbuf, u32 outbuflen)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	return jent_testing_reader(&jent_raw_hires, &boot_raw_hires_test,
26362306a36Sopenharmony_ci				   outbuf, outbuflen);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic ssize_t jent_raw_hires_read(struct file *file, char __user *to,
26762306a36Sopenharmony_ci				   size_t count, loff_t *ppos)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	return jent_testing_extract_user(file, to, count, ppos,
27062306a36Sopenharmony_ci					 jent_raw_hires_entropy_reader);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic const struct file_operations jent_raw_hires_fops = {
27462306a36Sopenharmony_ci	.owner = THIS_MODULE,
27562306a36Sopenharmony_ci	.read = jent_raw_hires_read,
27662306a36Sopenharmony_ci};
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/******************************* Initialization *******************************/
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_civoid jent_testing_init(void)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	jent_raw_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	debugfs_create_file_unsafe("jent_raw_hires", 0400,
28562306a36Sopenharmony_ci				   jent_raw_debugfs_root, NULL,
28662306a36Sopenharmony_ci				   &jent_raw_hires_fops);
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ciEXPORT_SYMBOL(jent_testing_init);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_civoid jent_testing_exit(void)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	debugfs_remove_recursive(jent_raw_debugfs_root);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ciEXPORT_SYMBOL(jent_testing_exit);
295