18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * s390 TRNG device driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Driver for the TRNG (true random number generation) command
68c2ecf20Sopenharmony_ci * available via CPACF extension MSA 7 on the s390 arch.
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2017
98c2ecf20Sopenharmony_ci * Author(s): Harald Freudenberger <freude@de.ibm.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "trng"
138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/hw_random.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/cpufeature.h>
198c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
208c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
218c2ecf20Sopenharmony_ci#include <linux/atomic.h>
228c2ecf20Sopenharmony_ci#include <linux/random.h>
238c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
248c2ecf20Sopenharmony_ci#include <asm/debug.h>
258c2ecf20Sopenharmony_ci#include <asm/cpacf.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
288c2ecf20Sopenharmony_ciMODULE_AUTHOR("IBM Corporation");
298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("s390 CPACF TRNG device driver");
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* trng related debug feature things */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic debug_info_t *debug_info;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define DEBUG_DBG(...)	debug_sprintf_event(debug_info, 6, ##__VA_ARGS__)
378c2ecf20Sopenharmony_ci#define DEBUG_INFO(...) debug_sprintf_event(debug_info, 5, ##__VA_ARGS__)
388c2ecf20Sopenharmony_ci#define DEBUG_WARN(...) debug_sprintf_event(debug_info, 4, ##__VA_ARGS__)
398c2ecf20Sopenharmony_ci#define DEBUG_ERR(...)	debug_sprintf_event(debug_info, 3, ##__VA_ARGS__)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* trng helpers */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic atomic64_t trng_dev_counter = ATOMIC64_INIT(0);
458c2ecf20Sopenharmony_cistatic atomic64_t trng_hwrng_counter = ATOMIC64_INIT(0);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* file io functions */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int trng_open(struct inode *inode, struct file *file)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	return nonseekable_open(inode, file);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic ssize_t trng_read(struct file *file, char __user *ubuf,
568c2ecf20Sopenharmony_ci			 size_t nbytes, loff_t *ppos)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	u8 buf[32];
598c2ecf20Sopenharmony_ci	u8 *p = buf;
608c2ecf20Sopenharmony_ci	unsigned int n;
618c2ecf20Sopenharmony_ci	ssize_t ret = 0;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/*
648c2ecf20Sopenharmony_ci	 * use buf for requests <= sizeof(buf),
658c2ecf20Sopenharmony_ci	 * otherwise allocate one page and fetch
668c2ecf20Sopenharmony_ci	 * pagewise.
678c2ecf20Sopenharmony_ci	 */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (nbytes > sizeof(buf)) {
708c2ecf20Sopenharmony_ci		p = (u8 *) __get_free_page(GFP_KERNEL);
718c2ecf20Sopenharmony_ci		if (!p)
728c2ecf20Sopenharmony_ci			return -ENOMEM;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	while (nbytes) {
768c2ecf20Sopenharmony_ci		if (need_resched()) {
778c2ecf20Sopenharmony_ci			if (signal_pending(current)) {
788c2ecf20Sopenharmony_ci				if (ret == 0)
798c2ecf20Sopenharmony_ci					ret = -ERESTARTSYS;
808c2ecf20Sopenharmony_ci				break;
818c2ecf20Sopenharmony_ci			}
828c2ecf20Sopenharmony_ci			schedule();
838c2ecf20Sopenharmony_ci		}
848c2ecf20Sopenharmony_ci		n = nbytes > PAGE_SIZE ? PAGE_SIZE : nbytes;
858c2ecf20Sopenharmony_ci		cpacf_trng(NULL, 0, p, n);
868c2ecf20Sopenharmony_ci		atomic64_add(n, &trng_dev_counter);
878c2ecf20Sopenharmony_ci		if (copy_to_user(ubuf, p, n)) {
888c2ecf20Sopenharmony_ci			ret = -EFAULT;
898c2ecf20Sopenharmony_ci			break;
908c2ecf20Sopenharmony_ci		}
918c2ecf20Sopenharmony_ci		nbytes -= n;
928c2ecf20Sopenharmony_ci		ubuf += n;
938c2ecf20Sopenharmony_ci		ret += n;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (p != buf)
978c2ecf20Sopenharmony_ci		free_page((unsigned long) p);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	DEBUG_DBG("trng_read()=%zd\n", ret);
1008c2ecf20Sopenharmony_ci	return ret;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/* sysfs */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic ssize_t trng_counter_show(struct device *dev,
1078c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	u64 dev_counter = atomic64_read(&trng_dev_counter);
1108c2ecf20Sopenharmony_ci	u64 hwrng_counter = atomic64_read(&trng_hwrng_counter);
1118c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ARCH_RANDOM)
1128c2ecf20Sopenharmony_ci	u64 arch_counter = atomic64_read(&s390_arch_random_counter);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE,
1158c2ecf20Sopenharmony_ci			"trng:  %llu\n"
1168c2ecf20Sopenharmony_ci			"hwrng: %llu\n"
1178c2ecf20Sopenharmony_ci			"arch:  %llu\n"
1188c2ecf20Sopenharmony_ci			"total: %llu\n",
1198c2ecf20Sopenharmony_ci			dev_counter, hwrng_counter, arch_counter,
1208c2ecf20Sopenharmony_ci			dev_counter + hwrng_counter + arch_counter);
1218c2ecf20Sopenharmony_ci#else
1228c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE,
1238c2ecf20Sopenharmony_ci			"trng:  %llu\n"
1248c2ecf20Sopenharmony_ci			"hwrng: %llu\n"
1258c2ecf20Sopenharmony_ci			"total: %llu\n",
1268c2ecf20Sopenharmony_ci			dev_counter, hwrng_counter,
1278c2ecf20Sopenharmony_ci			dev_counter + hwrng_counter);
1288c2ecf20Sopenharmony_ci#endif
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_cistatic DEVICE_ATTR(byte_counter, 0444, trng_counter_show, NULL);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic struct attribute *trng_dev_attrs[] = {
1338c2ecf20Sopenharmony_ci	&dev_attr_byte_counter.attr,
1348c2ecf20Sopenharmony_ci	NULL
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic const struct attribute_group trng_dev_attr_group = {
1388c2ecf20Sopenharmony_ci	.attrs = trng_dev_attrs
1398c2ecf20Sopenharmony_ci};
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic const struct attribute_group *trng_dev_attr_groups[] = {
1428c2ecf20Sopenharmony_ci	&trng_dev_attr_group,
1438c2ecf20Sopenharmony_ci	NULL
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic const struct file_operations trng_fops = {
1478c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1488c2ecf20Sopenharmony_ci	.open		= &trng_open,
1498c2ecf20Sopenharmony_ci	.release	= NULL,
1508c2ecf20Sopenharmony_ci	.read		= &trng_read,
1518c2ecf20Sopenharmony_ci	.llseek		= noop_llseek,
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic struct miscdevice trng_dev = {
1558c2ecf20Sopenharmony_ci	.name	= "trng",
1568c2ecf20Sopenharmony_ci	.minor	= MISC_DYNAMIC_MINOR,
1578c2ecf20Sopenharmony_ci	.mode	= 0444,
1588c2ecf20Sopenharmony_ci	.fops	= &trng_fops,
1598c2ecf20Sopenharmony_ci	.groups = trng_dev_attr_groups,
1608c2ecf20Sopenharmony_ci};
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/* hwrng_register */
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic inline void _trng_hwrng_read(u8 *buf, size_t len)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	cpacf_trng(NULL, 0, buf, len);
1688c2ecf20Sopenharmony_ci	atomic64_add(len, &trng_hwrng_counter);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int trng_hwrng_data_read(struct hwrng *rng, u32 *data)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	size_t len = sizeof(*data);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	_trng_hwrng_read((u8 *) data, len);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	DEBUG_DBG("trng_hwrng_data_read()=%zu\n", len);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return len;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int trng_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	size_t len = max <= PAGE_SIZE ? max : PAGE_SIZE;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	_trng_hwrng_read((u8 *) data, len);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	DEBUG_DBG("trng_hwrng_read()=%zu\n", len);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return len;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/*
1948c2ecf20Sopenharmony_ci * hwrng register struct
1958c2ecf20Sopenharmony_ci * The trng is suppost to have 100% entropy, and thus
1968c2ecf20Sopenharmony_ci * we register with a very high quality value.
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_cistatic struct hwrng trng_hwrng_dev = {
1998c2ecf20Sopenharmony_ci	.name		= "s390-trng",
2008c2ecf20Sopenharmony_ci	.data_read	= trng_hwrng_data_read,
2018c2ecf20Sopenharmony_ci	.read		= trng_hwrng_read,
2028c2ecf20Sopenharmony_ci	.quality	= 999,
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/* init and exit */
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic void __init trng_debug_init(void)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	debug_info = debug_register("trng", 1, 1, 4 * sizeof(long));
2118c2ecf20Sopenharmony_ci	debug_register_view(debug_info, &debug_sprintf_view);
2128c2ecf20Sopenharmony_ci	debug_set_level(debug_info, 3);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic void trng_debug_exit(void)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	debug_unregister(debug_info);
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int __init trng_init(void)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	int ret;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	trng_debug_init();
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* check if subfunction CPACF_PRNO_TRNG is available */
2278c2ecf20Sopenharmony_ci	if (!cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG)) {
2288c2ecf20Sopenharmony_ci		DEBUG_INFO("trng_init CPACF_PRNO_TRNG not available\n");
2298c2ecf20Sopenharmony_ci		ret = -ENODEV;
2308c2ecf20Sopenharmony_ci		goto out_dbg;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	ret = misc_register(&trng_dev);
2348c2ecf20Sopenharmony_ci	if (ret) {
2358c2ecf20Sopenharmony_ci		DEBUG_WARN("trng_init misc_register() failed rc=%d\n", ret);
2368c2ecf20Sopenharmony_ci		goto out_dbg;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	ret = hwrng_register(&trng_hwrng_dev);
2408c2ecf20Sopenharmony_ci	if (ret) {
2418c2ecf20Sopenharmony_ci		DEBUG_WARN("trng_init hwrng_register() failed rc=%d\n", ret);
2428c2ecf20Sopenharmony_ci		goto out_misc;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	DEBUG_DBG("trng_init successful\n");
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ciout_misc:
2508c2ecf20Sopenharmony_ci	misc_deregister(&trng_dev);
2518c2ecf20Sopenharmony_ciout_dbg:
2528c2ecf20Sopenharmony_ci	trng_debug_exit();
2538c2ecf20Sopenharmony_ci	return ret;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic void __exit trng_exit(void)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	hwrng_unregister(&trng_hwrng_dev);
2598c2ecf20Sopenharmony_ci	misc_deregister(&trng_dev);
2608c2ecf20Sopenharmony_ci	trng_debug_exit();
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cimodule_cpu_feature_match(MSA, trng_init);
2648c2ecf20Sopenharmony_cimodule_exit(trng_exit);
265