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