cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

s390-trng.c (5735B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * s390 TRNG device driver
      4 *
      5 * Driver for the TRNG (true random number generation) command
      6 * available via CPACF extension MSA 7 on the s390 arch.
      7
      8 * Copyright IBM Corp. 2017
      9 * Author(s): Harald Freudenberger <freude@de.ibm.com>
     10 */
     11
     12#define KMSG_COMPONENT "trng"
     13#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
     14
     15#include <linux/hw_random.h>
     16#include <linux/kernel.h>
     17#include <linux/module.h>
     18#include <linux/cpufeature.h>
     19#include <linux/miscdevice.h>
     20#include <linux/debugfs.h>
     21#include <linux/atomic.h>
     22#include <linux/random.h>
     23#include <linux/sched/signal.h>
     24#include <asm/debug.h>
     25#include <asm/cpacf.h>
     26
     27MODULE_LICENSE("GPL v2");
     28MODULE_AUTHOR("IBM Corporation");
     29MODULE_DESCRIPTION("s390 CPACF TRNG device driver");
     30
     31
     32/* trng related debug feature things */
     33
     34static debug_info_t *debug_info;
     35
     36#define DEBUG_DBG(...)	debug_sprintf_event(debug_info, 6, ##__VA_ARGS__)
     37#define DEBUG_INFO(...) debug_sprintf_event(debug_info, 5, ##__VA_ARGS__)
     38#define DEBUG_WARN(...) debug_sprintf_event(debug_info, 4, ##__VA_ARGS__)
     39#define DEBUG_ERR(...)	debug_sprintf_event(debug_info, 3, ##__VA_ARGS__)
     40
     41
     42/* trng helpers */
     43
     44static atomic64_t trng_dev_counter = ATOMIC64_INIT(0);
     45static atomic64_t trng_hwrng_counter = ATOMIC64_INIT(0);
     46
     47
     48/* file io functions */
     49
     50static int trng_open(struct inode *inode, struct file *file)
     51{
     52	return nonseekable_open(inode, file);
     53}
     54
     55static ssize_t trng_read(struct file *file, char __user *ubuf,
     56			 size_t nbytes, loff_t *ppos)
     57{
     58	u8 buf[32];
     59	u8 *p = buf;
     60	unsigned int n;
     61	ssize_t ret = 0;
     62
     63	/*
     64	 * use buf for requests <= sizeof(buf),
     65	 * otherwise allocate one page and fetch
     66	 * pagewise.
     67	 */
     68
     69	if (nbytes > sizeof(buf)) {
     70		p = (u8 *) __get_free_page(GFP_KERNEL);
     71		if (!p)
     72			return -ENOMEM;
     73	}
     74
     75	while (nbytes) {
     76		if (need_resched()) {
     77			if (signal_pending(current)) {
     78				if (ret == 0)
     79					ret = -ERESTARTSYS;
     80				break;
     81			}
     82			schedule();
     83		}
     84		n = nbytes > PAGE_SIZE ? PAGE_SIZE : nbytes;
     85		cpacf_trng(NULL, 0, p, n);
     86		atomic64_add(n, &trng_dev_counter);
     87		if (copy_to_user(ubuf, p, n)) {
     88			ret = -EFAULT;
     89			break;
     90		}
     91		nbytes -= n;
     92		ubuf += n;
     93		ret += n;
     94	}
     95
     96	if (p != buf)
     97		free_page((unsigned long) p);
     98
     99	DEBUG_DBG("trng_read()=%zd\n", ret);
    100	return ret;
    101}
    102
    103
    104/* sysfs */
    105
    106static ssize_t trng_counter_show(struct device *dev,
    107				 struct device_attribute *attr, char *buf)
    108{
    109	u64 dev_counter = atomic64_read(&trng_dev_counter);
    110	u64 hwrng_counter = atomic64_read(&trng_hwrng_counter);
    111#if IS_ENABLED(CONFIG_ARCH_RANDOM)
    112	u64 arch_counter = atomic64_read(&s390_arch_random_counter);
    113
    114	return sysfs_emit(buf,
    115			"trng:  %llu\n"
    116			"hwrng: %llu\n"
    117			"arch:  %llu\n"
    118			"total: %llu\n",
    119			dev_counter, hwrng_counter, arch_counter,
    120			dev_counter + hwrng_counter + arch_counter);
    121#else
    122	return sysfs_emit(buf,
    123			"trng:  %llu\n"
    124			"hwrng: %llu\n"
    125			"total: %llu\n",
    126			dev_counter, hwrng_counter,
    127			dev_counter + hwrng_counter);
    128#endif
    129}
    130static DEVICE_ATTR(byte_counter, 0444, trng_counter_show, NULL);
    131
    132static struct attribute *trng_dev_attrs[] = {
    133	&dev_attr_byte_counter.attr,
    134	NULL
    135};
    136
    137static const struct attribute_group trng_dev_attr_group = {
    138	.attrs = trng_dev_attrs
    139};
    140
    141static const struct attribute_group *trng_dev_attr_groups[] = {
    142	&trng_dev_attr_group,
    143	NULL
    144};
    145
    146static const struct file_operations trng_fops = {
    147	.owner		= THIS_MODULE,
    148	.open		= &trng_open,
    149	.release	= NULL,
    150	.read		= &trng_read,
    151	.llseek		= noop_llseek,
    152};
    153
    154static struct miscdevice trng_dev = {
    155	.name	= "trng",
    156	.minor	= MISC_DYNAMIC_MINOR,
    157	.mode	= 0444,
    158	.fops	= &trng_fops,
    159	.groups = trng_dev_attr_groups,
    160};
    161
    162
    163/* hwrng_register */
    164
    165static inline void _trng_hwrng_read(u8 *buf, size_t len)
    166{
    167	cpacf_trng(NULL, 0, buf, len);
    168	atomic64_add(len, &trng_hwrng_counter);
    169}
    170
    171static int trng_hwrng_data_read(struct hwrng *rng, u32 *data)
    172{
    173	size_t len = sizeof(*data);
    174
    175	_trng_hwrng_read((u8 *) data, len);
    176
    177	DEBUG_DBG("trng_hwrng_data_read()=%zu\n", len);
    178
    179	return len;
    180}
    181
    182static int trng_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
    183{
    184	size_t len = max <= PAGE_SIZE ? max : PAGE_SIZE;
    185
    186	_trng_hwrng_read((u8 *) data, len);
    187
    188	DEBUG_DBG("trng_hwrng_read()=%zu\n", len);
    189
    190	return len;
    191}
    192
    193/*
    194 * hwrng register struct
    195 * The trng is supposed to have 100% entropy, and thus we register with a very
    196 * high quality value. If we ever have a better driver in the future, we should
    197 * change this value again when we merge this driver.
    198 */
    199static struct hwrng trng_hwrng_dev = {
    200	.name		= "s390-trng",
    201	.data_read	= trng_hwrng_data_read,
    202	.read		= trng_hwrng_read,
    203	.quality	= 1024,
    204};
    205
    206
    207/* init and exit */
    208
    209static void __init trng_debug_init(void)
    210{
    211	debug_info = debug_register("trng", 1, 1, 4 * sizeof(long));
    212	debug_register_view(debug_info, &debug_sprintf_view);
    213	debug_set_level(debug_info, 3);
    214}
    215
    216static void trng_debug_exit(void)
    217{
    218	debug_unregister(debug_info);
    219}
    220
    221static int __init trng_init(void)
    222{
    223	int ret;
    224
    225	trng_debug_init();
    226
    227	/* check if subfunction CPACF_PRNO_TRNG is available */
    228	if (!cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG)) {
    229		DEBUG_INFO("trng_init CPACF_PRNO_TRNG not available\n");
    230		ret = -ENODEV;
    231		goto out_dbg;
    232	}
    233
    234	ret = misc_register(&trng_dev);
    235	if (ret) {
    236		DEBUG_WARN("trng_init misc_register() failed rc=%d\n", ret);
    237		goto out_dbg;
    238	}
    239
    240	ret = hwrng_register(&trng_hwrng_dev);
    241	if (ret) {
    242		DEBUG_WARN("trng_init hwrng_register() failed rc=%d\n", ret);
    243		goto out_misc;
    244	}
    245
    246	DEBUG_DBG("trng_init successful\n");
    247
    248	return 0;
    249
    250out_misc:
    251	misc_deregister(&trng_dev);
    252out_dbg:
    253	trng_debug_exit();
    254	return ret;
    255}
    256
    257static void __exit trng_exit(void)
    258{
    259	hwrng_unregister(&trng_hwrng_dev);
    260	misc_deregister(&trng_dev);
    261	trng_debug_exit();
    262}
    263
    264module_cpu_feature_match(MSA, trng_init);
    265module_exit(trng_exit);