Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * s390 TRNG device driver
0004  *
0005  * Driver for the TRNG (true random number generation) command
0006  * available via CPACF extension MSA 7 on the s390 arch.
0007 
0008  * Copyright IBM Corp. 2017
0009  * Author(s): Harald Freudenberger <freude@de.ibm.com>
0010  */
0011 
0012 #define KMSG_COMPONENT "trng"
0013 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0014 
0015 #include <linux/hw_random.h>
0016 #include <linux/kernel.h>
0017 #include <linux/module.h>
0018 #include <linux/cpufeature.h>
0019 #include <linux/miscdevice.h>
0020 #include <linux/debugfs.h>
0021 #include <linux/atomic.h>
0022 #include <linux/random.h>
0023 #include <linux/sched/signal.h>
0024 #include <asm/debug.h>
0025 #include <asm/cpacf.h>
0026 
0027 MODULE_LICENSE("GPL v2");
0028 MODULE_AUTHOR("IBM Corporation");
0029 MODULE_DESCRIPTION("s390 CPACF TRNG device driver");
0030 
0031 
0032 /* trng related debug feature things */
0033 
0034 static debug_info_t *debug_info;
0035 
0036 #define DEBUG_DBG(...)  debug_sprintf_event(debug_info, 6, ##__VA_ARGS__)
0037 #define DEBUG_INFO(...) debug_sprintf_event(debug_info, 5, ##__VA_ARGS__)
0038 #define DEBUG_WARN(...) debug_sprintf_event(debug_info, 4, ##__VA_ARGS__)
0039 #define DEBUG_ERR(...)  debug_sprintf_event(debug_info, 3, ##__VA_ARGS__)
0040 
0041 
0042 /* trng helpers */
0043 
0044 static atomic64_t trng_dev_counter = ATOMIC64_INIT(0);
0045 static atomic64_t trng_hwrng_counter = ATOMIC64_INIT(0);
0046 
0047 
0048 /* file io functions */
0049 
0050 static int trng_open(struct inode *inode, struct file *file)
0051 {
0052     return nonseekable_open(inode, file);
0053 }
0054 
0055 static ssize_t trng_read(struct file *file, char __user *ubuf,
0056              size_t nbytes, loff_t *ppos)
0057 {
0058     u8 buf[32];
0059     u8 *p = buf;
0060     unsigned int n;
0061     ssize_t ret = 0;
0062 
0063     /*
0064      * use buf for requests <= sizeof(buf),
0065      * otherwise allocate one page and fetch
0066      * pagewise.
0067      */
0068 
0069     if (nbytes > sizeof(buf)) {
0070         p = (u8 *) __get_free_page(GFP_KERNEL);
0071         if (!p)
0072             return -ENOMEM;
0073     }
0074 
0075     while (nbytes) {
0076         if (need_resched()) {
0077             if (signal_pending(current)) {
0078                 if (ret == 0)
0079                     ret = -ERESTARTSYS;
0080                 break;
0081             }
0082             schedule();
0083         }
0084         n = nbytes > PAGE_SIZE ? PAGE_SIZE : nbytes;
0085         cpacf_trng(NULL, 0, p, n);
0086         atomic64_add(n, &trng_dev_counter);
0087         if (copy_to_user(ubuf, p, n)) {
0088             ret = -EFAULT;
0089             break;
0090         }
0091         nbytes -= n;
0092         ubuf += n;
0093         ret += n;
0094     }
0095 
0096     if (p != buf)
0097         free_page((unsigned long) p);
0098 
0099     DEBUG_DBG("trng_read()=%zd\n", ret);
0100     return ret;
0101 }
0102 
0103 
0104 /* sysfs */
0105 
0106 static ssize_t trng_counter_show(struct device *dev,
0107                  struct device_attribute *attr, char *buf)
0108 {
0109     u64 dev_counter = atomic64_read(&trng_dev_counter);
0110     u64 hwrng_counter = atomic64_read(&trng_hwrng_counter);
0111     u64 arch_counter = atomic64_read(&s390_arch_random_counter);
0112 
0113     return sysfs_emit(buf,
0114             "trng:  %llu\n"
0115             "hwrng: %llu\n"
0116             "arch:  %llu\n"
0117             "total: %llu\n",
0118             dev_counter, hwrng_counter, arch_counter,
0119             dev_counter + hwrng_counter + arch_counter);
0120 }
0121 static DEVICE_ATTR(byte_counter, 0444, trng_counter_show, NULL);
0122 
0123 static struct attribute *trng_dev_attrs[] = {
0124     &dev_attr_byte_counter.attr,
0125     NULL
0126 };
0127 
0128 static const struct attribute_group trng_dev_attr_group = {
0129     .attrs = trng_dev_attrs
0130 };
0131 
0132 static const struct attribute_group *trng_dev_attr_groups[] = {
0133     &trng_dev_attr_group,
0134     NULL
0135 };
0136 
0137 static const struct file_operations trng_fops = {
0138     .owner      = THIS_MODULE,
0139     .open       = &trng_open,
0140     .release    = NULL,
0141     .read       = &trng_read,
0142     .llseek     = noop_llseek,
0143 };
0144 
0145 static struct miscdevice trng_dev = {
0146     .name   = "trng",
0147     .minor  = MISC_DYNAMIC_MINOR,
0148     .mode   = 0444,
0149     .fops   = &trng_fops,
0150     .groups = trng_dev_attr_groups,
0151 };
0152 
0153 
0154 /* hwrng_register */
0155 
0156 static inline void _trng_hwrng_read(u8 *buf, size_t len)
0157 {
0158     cpacf_trng(NULL, 0, buf, len);
0159     atomic64_add(len, &trng_hwrng_counter);
0160 }
0161 
0162 static int trng_hwrng_data_read(struct hwrng *rng, u32 *data)
0163 {
0164     size_t len = sizeof(*data);
0165 
0166     _trng_hwrng_read((u8 *) data, len);
0167 
0168     DEBUG_DBG("trng_hwrng_data_read()=%zu\n", len);
0169 
0170     return len;
0171 }
0172 
0173 static int trng_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
0174 {
0175     size_t len = max <= PAGE_SIZE ? max : PAGE_SIZE;
0176 
0177     _trng_hwrng_read((u8 *) data, len);
0178 
0179     DEBUG_DBG("trng_hwrng_read()=%zu\n", len);
0180 
0181     return len;
0182 }
0183 
0184 /*
0185  * hwrng register struct
0186  * The trng is supposed to have 100% entropy, and thus we register with a very
0187  * high quality value. If we ever have a better driver in the future, we should
0188  * change this value again when we merge this driver.
0189  */
0190 static struct hwrng trng_hwrng_dev = {
0191     .name       = "s390-trng",
0192     .data_read  = trng_hwrng_data_read,
0193     .read       = trng_hwrng_read,
0194     .quality    = 1024,
0195 };
0196 
0197 
0198 /* init and exit */
0199 
0200 static void __init trng_debug_init(void)
0201 {
0202     debug_info = debug_register("trng", 1, 1, 4 * sizeof(long));
0203     debug_register_view(debug_info, &debug_sprintf_view);
0204     debug_set_level(debug_info, 3);
0205 }
0206 
0207 static void trng_debug_exit(void)
0208 {
0209     debug_unregister(debug_info);
0210 }
0211 
0212 static int __init trng_init(void)
0213 {
0214     int ret;
0215 
0216     trng_debug_init();
0217 
0218     /* check if subfunction CPACF_PRNO_TRNG is available */
0219     if (!cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG)) {
0220         DEBUG_INFO("trng_init CPACF_PRNO_TRNG not available\n");
0221         ret = -ENODEV;
0222         goto out_dbg;
0223     }
0224 
0225     ret = misc_register(&trng_dev);
0226     if (ret) {
0227         DEBUG_WARN("trng_init misc_register() failed rc=%d\n", ret);
0228         goto out_dbg;
0229     }
0230 
0231     ret = hwrng_register(&trng_hwrng_dev);
0232     if (ret) {
0233         DEBUG_WARN("trng_init hwrng_register() failed rc=%d\n", ret);
0234         goto out_misc;
0235     }
0236 
0237     DEBUG_DBG("trng_init successful\n");
0238 
0239     return 0;
0240 
0241 out_misc:
0242     misc_deregister(&trng_dev);
0243 out_dbg:
0244     trng_debug_exit();
0245     return ret;
0246 }
0247 
0248 static void __exit trng_exit(void)
0249 {
0250     hwrng_unregister(&trng_hwrng_dev);
0251     misc_deregister(&trng_dev);
0252     trng_debug_exit();
0253 }
0254 
0255 module_cpu_feature_match(S390_CPU_FEATURE_MSA, trng_init);
0256 module_exit(trng_exit);