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