162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * s390 TRNG device driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Driver for the TRNG (true random number generation) command
662306a36Sopenharmony_ci * available via CPACF extension MSA 7 on the s390 arch.
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci * Copyright IBM Corp. 2017
962306a36Sopenharmony_ci * Author(s): Harald Freudenberger <freude@de.ibm.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define KMSG_COMPONENT "trng"
1362306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/hw_random.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/cpufeature.h>
1962306a36Sopenharmony_ci#include <linux/miscdevice.h>
2062306a36Sopenharmony_ci#include <linux/debugfs.h>
2162306a36Sopenharmony_ci#include <linux/atomic.h>
2262306a36Sopenharmony_ci#include <linux/random.h>
2362306a36Sopenharmony_ci#include <linux/sched/signal.h>
2462306a36Sopenharmony_ci#include <asm/debug.h>
2562306a36Sopenharmony_ci#include <asm/cpacf.h>
2662306a36Sopenharmony_ci#include <asm/archrandom.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
2962306a36Sopenharmony_ciMODULE_AUTHOR("IBM Corporation");
3062306a36Sopenharmony_ciMODULE_DESCRIPTION("s390 CPACF TRNG device driver");
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* trng related debug feature things */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic debug_info_t *debug_info;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define DEBUG_DBG(...)	debug_sprintf_event(debug_info, 6, ##__VA_ARGS__)
3862306a36Sopenharmony_ci#define DEBUG_INFO(...) debug_sprintf_event(debug_info, 5, ##__VA_ARGS__)
3962306a36Sopenharmony_ci#define DEBUG_WARN(...) debug_sprintf_event(debug_info, 4, ##__VA_ARGS__)
4062306a36Sopenharmony_ci#define DEBUG_ERR(...)	debug_sprintf_event(debug_info, 3, ##__VA_ARGS__)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* trng helpers */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic atomic64_t trng_dev_counter = ATOMIC64_INIT(0);
4662306a36Sopenharmony_cistatic atomic64_t trng_hwrng_counter = ATOMIC64_INIT(0);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* file io functions */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int trng_open(struct inode *inode, struct file *file)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	return nonseekable_open(inode, file);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic ssize_t trng_read(struct file *file, char __user *ubuf,
5762306a36Sopenharmony_ci			 size_t nbytes, loff_t *ppos)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	u8 buf[32];
6062306a36Sopenharmony_ci	u8 *p = buf;
6162306a36Sopenharmony_ci	unsigned int n;
6262306a36Sopenharmony_ci	ssize_t ret = 0;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/*
6562306a36Sopenharmony_ci	 * use buf for requests <= sizeof(buf),
6662306a36Sopenharmony_ci	 * otherwise allocate one page and fetch
6762306a36Sopenharmony_ci	 * pagewise.
6862306a36Sopenharmony_ci	 */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (nbytes > sizeof(buf)) {
7162306a36Sopenharmony_ci		p = (u8 *) __get_free_page(GFP_KERNEL);
7262306a36Sopenharmony_ci		if (!p)
7362306a36Sopenharmony_ci			return -ENOMEM;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	while (nbytes) {
7762306a36Sopenharmony_ci		if (need_resched()) {
7862306a36Sopenharmony_ci			if (signal_pending(current)) {
7962306a36Sopenharmony_ci				if (ret == 0)
8062306a36Sopenharmony_ci					ret = -ERESTARTSYS;
8162306a36Sopenharmony_ci				break;
8262306a36Sopenharmony_ci			}
8362306a36Sopenharmony_ci			schedule();
8462306a36Sopenharmony_ci		}
8562306a36Sopenharmony_ci		n = nbytes > PAGE_SIZE ? PAGE_SIZE : nbytes;
8662306a36Sopenharmony_ci		cpacf_trng(NULL, 0, p, n);
8762306a36Sopenharmony_ci		atomic64_add(n, &trng_dev_counter);
8862306a36Sopenharmony_ci		if (copy_to_user(ubuf, p, n)) {
8962306a36Sopenharmony_ci			ret = -EFAULT;
9062306a36Sopenharmony_ci			break;
9162306a36Sopenharmony_ci		}
9262306a36Sopenharmony_ci		nbytes -= n;
9362306a36Sopenharmony_ci		ubuf += n;
9462306a36Sopenharmony_ci		ret += n;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (p != buf)
9862306a36Sopenharmony_ci		free_page((unsigned long) p);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	DEBUG_DBG("trng_read()=%zd\n", ret);
10162306a36Sopenharmony_ci	return ret;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* sysfs */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic ssize_t trng_counter_show(struct device *dev,
10862306a36Sopenharmony_ci				 struct device_attribute *attr, char *buf)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	u64 dev_counter = atomic64_read(&trng_dev_counter);
11162306a36Sopenharmony_ci	u64 hwrng_counter = atomic64_read(&trng_hwrng_counter);
11262306a36Sopenharmony_ci	u64 arch_counter = atomic64_read(&s390_arch_random_counter);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return sysfs_emit(buf,
11562306a36Sopenharmony_ci			"trng:  %llu\n"
11662306a36Sopenharmony_ci			"hwrng: %llu\n"
11762306a36Sopenharmony_ci			"arch:  %llu\n"
11862306a36Sopenharmony_ci			"total: %llu\n",
11962306a36Sopenharmony_ci			dev_counter, hwrng_counter, arch_counter,
12062306a36Sopenharmony_ci			dev_counter + hwrng_counter + arch_counter);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_cistatic DEVICE_ATTR(byte_counter, 0444, trng_counter_show, NULL);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic struct attribute *trng_dev_attrs[] = {
12562306a36Sopenharmony_ci	&dev_attr_byte_counter.attr,
12662306a36Sopenharmony_ci	NULL
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic const struct attribute_group trng_dev_attr_group = {
13062306a36Sopenharmony_ci	.attrs = trng_dev_attrs
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic const struct attribute_group *trng_dev_attr_groups[] = {
13462306a36Sopenharmony_ci	&trng_dev_attr_group,
13562306a36Sopenharmony_ci	NULL
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic const struct file_operations trng_fops = {
13962306a36Sopenharmony_ci	.owner		= THIS_MODULE,
14062306a36Sopenharmony_ci	.open		= &trng_open,
14162306a36Sopenharmony_ci	.release	= NULL,
14262306a36Sopenharmony_ci	.read		= &trng_read,
14362306a36Sopenharmony_ci	.llseek		= noop_llseek,
14462306a36Sopenharmony_ci};
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic struct miscdevice trng_dev = {
14762306a36Sopenharmony_ci	.name	= "trng",
14862306a36Sopenharmony_ci	.minor	= MISC_DYNAMIC_MINOR,
14962306a36Sopenharmony_ci	.mode	= 0444,
15062306a36Sopenharmony_ci	.fops	= &trng_fops,
15162306a36Sopenharmony_ci	.groups = trng_dev_attr_groups,
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* hwrng_register */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic inline void _trng_hwrng_read(u8 *buf, size_t len)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	cpacf_trng(NULL, 0, buf, len);
16062306a36Sopenharmony_ci	atomic64_add(len, &trng_hwrng_counter);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int trng_hwrng_data_read(struct hwrng *rng, u32 *data)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	size_t len = sizeof(*data);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	_trng_hwrng_read((u8 *) data, len);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	DEBUG_DBG("trng_hwrng_data_read()=%zu\n", len);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return len;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int trng_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	size_t len = max <= PAGE_SIZE ? max : PAGE_SIZE;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	_trng_hwrng_read((u8 *) data, len);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	DEBUG_DBG("trng_hwrng_read()=%zu\n", len);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return len;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/*
18662306a36Sopenharmony_ci * hwrng register struct
18762306a36Sopenharmony_ci * The trng is supposed to have 100% entropy, and thus we register with a very
18862306a36Sopenharmony_ci * high quality value. If we ever have a better driver in the future, we should
18962306a36Sopenharmony_ci * change this value again when we merge this driver.
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_cistatic struct hwrng trng_hwrng_dev = {
19262306a36Sopenharmony_ci	.name		= "s390-trng",
19362306a36Sopenharmony_ci	.data_read	= trng_hwrng_data_read,
19462306a36Sopenharmony_ci	.read		= trng_hwrng_read,
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/* init and exit */
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic void __init trng_debug_init(void)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	debug_info = debug_register("trng", 1, 1, 4 * sizeof(long));
20362306a36Sopenharmony_ci	debug_register_view(debug_info, &debug_sprintf_view);
20462306a36Sopenharmony_ci	debug_set_level(debug_info, 3);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void trng_debug_exit(void)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	debug_unregister(debug_info);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int __init trng_init(void)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	int ret;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	trng_debug_init();
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* check if subfunction CPACF_PRNO_TRNG is available */
21962306a36Sopenharmony_ci	if (!cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG)) {
22062306a36Sopenharmony_ci		DEBUG_INFO("trng_init CPACF_PRNO_TRNG not available\n");
22162306a36Sopenharmony_ci		ret = -ENODEV;
22262306a36Sopenharmony_ci		goto out_dbg;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	ret = misc_register(&trng_dev);
22662306a36Sopenharmony_ci	if (ret) {
22762306a36Sopenharmony_ci		DEBUG_WARN("trng_init misc_register() failed rc=%d\n", ret);
22862306a36Sopenharmony_ci		goto out_dbg;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	ret = hwrng_register(&trng_hwrng_dev);
23262306a36Sopenharmony_ci	if (ret) {
23362306a36Sopenharmony_ci		DEBUG_WARN("trng_init hwrng_register() failed rc=%d\n", ret);
23462306a36Sopenharmony_ci		goto out_misc;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	DEBUG_DBG("trng_init successful\n");
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return 0;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ciout_misc:
24262306a36Sopenharmony_ci	misc_deregister(&trng_dev);
24362306a36Sopenharmony_ciout_dbg:
24462306a36Sopenharmony_ci	trng_debug_exit();
24562306a36Sopenharmony_ci	return ret;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic void __exit trng_exit(void)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	hwrng_unregister(&trng_hwrng_dev);
25162306a36Sopenharmony_ci	misc_deregister(&trng_dev);
25262306a36Sopenharmony_ci	trng_debug_exit();
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cimodule_cpu_feature_match(S390_CPU_FEATURE_MSA, trng_init);
25662306a36Sopenharmony_cimodule_exit(trng_exit);
257