cregit-Linux how code gets into the kernel

Release 4.7 drivers/char/hw_random/core.c

/*
        Added support for the AMD Geode LX RNG
        (c) Copyright 2004-2005 Advanced Micro Devices, Inc.

        derived from

        Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
        (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>

        derived from

        Hardware driver for the AMD 768 Random Number Generator (RNG)
        (c) Copyright 2001 Red Hat Inc <alan@redhat.com>

        derived from

        Hardware driver for Intel i810 Random Number Generator (RNG)
        Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
        Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>

        Added generic RNG API
        Copyright 2006 Michael Buesch <m@bues.ch>
        Copyright 2005 (c) MontaVista Software, Inc.

        Please read Documentation/hw_random.txt for details on use.

        ----------------------------------------------------------
        This software may be used and distributed according to the terms
        of the GNU General Public License, incorporated herein by reference.

 */


#include <linux/device.h>
#include <linux/hw_random.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/err.h>
#include <asm/uaccess.h>



#define RNG_MODULE_NAME		"hw_random"

#define PFX			RNG_MODULE_NAME ": "

#define RNG_MISCDEV_MINOR	183 
/* official */



static struct hwrng *current_rng;

static struct task_struct *hwrng_fill;
static LIST_HEAD(rng_list);
/* Protects rng_list and current_rng */
static DEFINE_MUTEX(rng_mutex);
/* Protects rng read functions, data_avail, rng_buffer and rng_fillbuf */
static DEFINE_MUTEX(reading_mutex);

static int data_avail;


static u8 *rng_buffer, *rng_fillbuf;

static unsigned short current_quality;

static unsigned short default_quality; 
/* = 0; default to "off" */

module_param(current_quality, ushort, 0644);
MODULE_PARM_DESC(current_quality,
		 "current hwrng entropy estimation per mill");
module_param(default_quality, ushort, 0644);
MODULE_PARM_DESC(default_quality,
		 "default entropy content of hwrng per mill");

static void drop_current_rng(void);
static int hwrng_init(struct hwrng *rng);
static void start_khwrngd(void);

static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
			       int wait);


static size_t rng_buffer_size(void) { return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES; }

Contributors

PersonTokensPropCommitsCommitProp
rusty russellrusty russell1058.82%133.33%
ian moltonian molton741.18%266.67%
Total17100.00%3100.00%


static void add_early_randomness(struct hwrng *rng) { unsigned char bytes[16]; int bytes_read; mutex_lock(&reading_mutex); bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1); mutex_unlock(&reading_mutex); if (bytes_read > 0) add_device_randomness(bytes, bytes_read); }

Contributors

PersonTokensPropCommitsCommitProp
amit shahamit shah5080.65%150.00%
rusty russellrusty russell1219.35%150.00%
Total62100.00%2100.00%


static inline void cleanup_rng(struct kref *kref) { struct hwrng *rng = container_of(kref, struct hwrng, ref); if (rng->cleanup) rng->cleanup(rng); complete(&rng->cleanup_done); }

Contributors

PersonTokensPropCommitsCommitProp
rusty russellrusty russell4593.75%266.67%
herbert xuherbert xu36.25%133.33%
Total48100.00%3100.00%


static int set_current_rng(struct hwrng *rng) { int err; BUG_ON(!mutex_is_locked(&rng_mutex)); err = hwrng_init(rng); if (err) return err; drop_current_rng(); current_rng = rng; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
herbert xuherbert xu2450.00%266.67%
rusty russellrusty russell2450.00%133.33%
Total48100.00%3100.00%


static void drop_current_rng(void) { BUG_ON(!mutex_is_locked(&rng_mutex)); if (!current_rng) return; /* decrease last reference for triggering the cleanup */ kref_put(&current_rng->ref, cleanup_rng); current_rng = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
rusty russellrusty russell39100.00%1100.00%
Total39100.00%1100.00%

/* Returns ERR_PTR(), NULL or refcounted hwrng */
static struct hwrng *get_current_rng(void) { struct hwrng *rng; if (mutex_lock_interruptible(&rng_mutex)) return ERR_PTR(-ERESTARTSYS); rng = current_rng; if (rng) kref_get(&rng->ref); mutex_unlock(&rng_mutex); return rng; }

Contributors

PersonTokensPropCommitsCommitProp
rusty russellrusty russell55100.00%1100.00%
Total55100.00%1100.00%


static void put_rng(struct hwrng *rng) { /* * Hold rng_mutex here so we serialize in case they set_current_rng * on rng again immediately. */ mutex_lock(&rng_mutex); if (rng) kref_put(&rng->ref, cleanup_rng); mutex_unlock(&rng_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
rusty russellrusty russell38100.00%1100.00%
Total38100.00%1100.00%


static int hwrng_init(struct hwrng *rng) { if (kref_get_unless_zero(&rng->ref)) goto skip_init; if (rng->init) { int ret; ret = rng->init(rng); if (ret) return ret; } kref_init(&rng->ref); reinit_completion(&rng->cleanup_done); skip_init: add_early_randomness(rng); current_quality = rng->quality ? : default_quality; if (current_quality > 1024) current_quality = 1024; if (current_quality == 0 && hwrng_fill) kthread_stop(hwrng_fill); if (current_quality > 0 && !hwrng_fill) start_khwrngd(); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
torsten duwetorsten duwe3629.75%233.33%
herbert xuherbert xu3125.62%116.67%
michael bueschmichael buesch2419.83%116.67%
amit shahamit shah2218.18%116.67%
keith packardkeith packard86.61%116.67%
Total121100.00%6100.00%


static int rng_dev_open(struct inode *inode, struct file *filp) { /* enforce read-only access to this chrdev */ if ((filp->f_mode & FMODE_READ) == 0) return -EINVAL; if (filp->f_mode & FMODE_WRITE) return -EINVAL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
michael bueschmichael buesch48100.00%1100.00%
Total48100.00%1100.00%


static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, int wait) { int present; BUG_ON(!mutex_is_locked(&reading_mutex)); if (rng->read) return rng->read(rng, (void *)buffer, size, wait); if (rng->data_present) present = rng->data_present(rng, wait); else present = 1; if (present) return rng->data_read(rng, (u32 *)buffer); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
ian moltonian molton9290.20%150.00%
rusty russellrusty russell109.80%150.00%
Total102100.00%2100.00%


static ssize_t rng_dev_read(struct file *filp, char __user *buf, size_t size, loff_t *offp) { ssize_t ret = 0; int err = 0; int bytes_read, len; struct hwrng *rng; while (size) { rng = get_current_rng(); if (IS_ERR(rng)) { err = PTR_ERR(rng); goto out; } if (!rng) { err = -ENODEV; goto out; } if (mutex_lock_interruptible(&reading_mutex)) { err = -ERESTARTSYS; goto out_put; } if (!data_avail) { bytes_read = rng_get_data(rng, rng_buffer, rng_buffer_size(), !(filp->f_flags & O_NONBLOCK)); if (bytes_read < 0) { err = bytes_read; goto out_unlock_reading; } data_avail = bytes_read; } if (!data_avail) { if (filp->f_flags & O_NONBLOCK) { err = -EAGAIN; goto out_unlock_reading; } } else { len = data_avail; if (len > size) len = size; data_avail -= len; if (copy_to_user(buf + ret, rng_buffer + data_avail, len)) { err = -EFAULT; goto out_unlock_reading; } size -= len; ret += len; } mutex_unlock(&reading_mutex); put_rng(rng); if (need_resched()) schedule_timeout_interruptible(1); if (signal_pending(current)) { err = -ERESTARTSYS; goto out; } } out: return ret ? : err; out_unlock_reading: mutex_unlock(&reading_mutex); out_put: put_rng(rng); goto out; }

Contributors

PersonTokensPropCommitsCommitProp
michael bueschmichael buesch11839.86%111.11%
ian moltonian molton9532.09%111.11%
rusty russellrusty russell4515.20%333.33%
jiri slabyjiri slaby155.07%111.11%
ralph wuerthnerralph wuerthner144.73%111.11%
herbert xuherbert xu82.70%111.11%
patrick mchardypatrick mchardy10.34%111.11%
Total296100.00%9100.00%

static const struct file_operations rng_chrdev_ops = { .owner = THIS_MODULE, .open = rng_dev_open, .read = rng_dev_read, .llseek = noop_llseek, }; static const struct attribute_group *rng_dev_groups[]; static struct miscdevice rng_miscdev = { .minor = RNG_MISCDEV_MINOR, .name = RNG_MODULE_NAME, .nodename = "hwrng", .fops = &rng_chrdev_ops, .groups = rng_dev_groups, };
static ssize_t hwrng_attr_current_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int err; struct hwrng *rng; err = mutex_lock_interruptible(&rng_mutex); if (err) return -ERESTARTSYS; err = -ENODEV; list_for_each_entry(rng, &rng_list, list) { if (sysfs_streq(rng->name, buf)) { err = 0; if (rng != current_rng) err = set_current_rng(rng); break; } } mutex_unlock(&rng_mutex); return err ? : len; }

Contributors

PersonTokensPropCommitsCommitProp
michael bueschmichael buesch9084.91%120.00%
greg kroah-hartmangreg kroah-hartman76.60%120.00%
herbert xuherbert xu54.72%120.00%
rusty russellrusty russell32.83%120.00%
lee joneslee jones10.94%120.00%
Total106100.00%5100.00%


static ssize_t hwrng_attr_current_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; struct hwrng *rng; rng = get_current_rng(); if (IS_ERR(rng)) return PTR_ERR(rng); ret = snprintf(buf, PAGE_SIZE, "%s\n", rng ? rng->name : "none"); put_rng(rng); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
michael bueschmichael buesch4460.27%133.33%
rusty russellrusty russell2230.14%133.33%
greg kroah-hartmangreg kroah-hartman79.59%133.33%
Total73100.00%3100.00%


static ssize_t hwrng_attr_available_show(struct device *dev, struct device_attribute *attr, char *buf) { int err; struct hwrng *rng; err = mutex_lock_interruptible(&rng_mutex); if (err) return -ERESTARTSYS; buf[0] = '\0'; list_for_each_entry(rng, &rng_list, list) { strlcat(buf, rng->name, PAGE_SIZE); strlcat(buf, " ", PAGE_SIZE); } strlcat(buf, "\n", PAGE_SIZE); mutex_unlock(&rng_mutex); return strlen(buf); }

Contributors

PersonTokensPropCommitsCommitProp
michael bueschmichael buesch8886.27%133.33%
rickard strandqvistrickard strandqvist76.86%133.33%
greg kroah-hartmangreg kroah-hartman76.86%133.33%
Total102100.00%3100.00%

static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR, hwrng_attr_current_show, hwrng_attr_current_store); static DEVICE_ATTR(rng_available, S_IRUGO, hwrng_attr_available_show, NULL); static struct attribute *rng_dev_attrs[] = { &dev_attr_rng_current.attr, &dev_attr_rng_available.attr, NULL }; ATTRIBUTE_GROUPS(rng_dev);
static void __exit unregister_miscdev(void) { misc_deregister(&rng_miscdev); }

Contributors

PersonTokensPropCommitsCommitProp
michael bueschmichael buesch1280.00%133.33%
rafael j. wysockirafael j. wysocki213.33%133.33%
herbert xuherbert xu16.67%133.33%
Total15100.00%3100.00%


static int __init register_miscdev(void) { return misc_register(&rng_miscdev); }

Contributors

PersonTokensPropCommitsCommitProp
michael bueschmichael buesch1487.50%133.33%
herbert xuherbert xu16.25%133.33%
takashi iwaitakashi iwai16.25%133.33%
Total16100.00%3100.00%


static int hwrng_fillfn(void *unused) { long rc; while (!kthread_should_stop()) { struct hwrng *rng; rng = get_current_rng(); if (IS_ERR(rng) || !rng) break; mutex_lock(&reading_mutex); rc = rng_get_data(rng, rng_fillbuf, rng_buffer_size(), 1); mutex_unlock(&reading_mutex); put_rng(rng); if (rc <= 0) { pr_warn("hwrng: no data available\n"); msleep_interruptible(10000); continue; } /* Outside lock, sure, but y'know: randomness. */ add_hwgenerator_randomness((void *)rng_fillbuf, rc, rc * current_quality * 8 >> 10); } hwrng_fill = NULL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
torsten duwetorsten duwe8268.91%240.00%
rusty russellrusty russell3529.41%240.00%
stephen boydstephen boyd21.68%120.00%
Total119100.00%5100.00%


static void start_khwrngd(void) { hwrng_fill = kthread_run(hwrng_fillfn, NULL, "hwrng"); if (IS_ERR(hwrng_fill)) { pr_err("hwrng_fill thread creation failed"); hwrng_fill = NULL; } }

Contributors

PersonTokensPropCommitsCommitProp
torsten duwetorsten duwe3594.59%150.00%
martin schwidefskymartin schwidefsky25.41%150.00%
Total37100.00%2100.00%


int hwrng_register(struct hwrng *rng) { int err = -EINVAL; struct hwrng *old_rng, *tmp; if (rng->name == NULL || (rng->data_read == NULL && rng->read == NULL)) goto out; mutex_lock(&rng_mutex); /* kmalloc makes this safe for virt_to_page() in virtio_rng.c */ err = -ENOMEM; if (!rng_buffer) { rng_buffer = kmalloc(rng_buffer_size(), GFP_KERNEL); if (!rng_buffer) goto out_unlock; } if (!rng_fillbuf) { rng_fillbuf = kmalloc(rng_buffer_size(), GFP_KERNEL); if (!rng_fillbuf) { kfree(rng_buffer); goto out_unlock; } } /* Must not register two RNGs with the same name. */ err = -EEXIST; list_for_each_entry(tmp, &rng_list, list) { if (strcmp(tmp->name, rng->name) == 0) goto out_unlock; } init_completion(&rng->cleanup_done); complete(&rng->cleanup_done); old_rng = current_rng; err = 0; if (!old_rng) { err = set_current_rng(rng); if (err) goto out_unlock; } list_add_tail(&rng->list, &rng_list); if (old_rng && !rng->init) { /* * Use a new device's input to add some randomness to * the system. If this rng device isn't going to be * used right away, its init function hasn't been * called yet; so only use the randomness from devices * that don't need an init callback. */ add_early_randomness(rng); } out_unlock: mutex_unlock(&rng_mutex); out: return err; }

Contributors

PersonTokensPropCommitsCommitProp
michael bueschmichael buesch12954.20%111.11%
rusty russellrusty russell3514.71%222.22%
torsten duwetorsten duwe3213.45%111.11%
herbert xuherbert xu177.14%222.22%
amit shahamit shah114.62%111.11%
ian moltonian molton83.36%111.11%
kees cookkees cook62.52%111.11%
Total238100.00%9100.00%

EXPORT_SYMBOL_GPL(hwrng_register);
void hwrng_unregister(struct hwrng *rng) { mutex_lock(&rng_mutex); list_del(&rng->list); if (current_rng == rng) { drop_current_rng(); if (!list_empty(&rng_list)) { struct hwrng *tail; tail = list_entry(rng_list.prev, struct hwrng, list); set_current_rng(tail); } } if (list_empty(&rng_list)) { mutex_unlock(&rng_mutex); if (hwrng_fill) kthread_stop(hwrng_fill); } else mutex_unlock(&rng_mutex); wait_for_completion(&rng->cleanup_done); }

Contributors

PersonTokensPropCommitsCommitProp
michael bueschmichael buesch7063.64%114.29%
rusty russellrusty russell1917.27%228.57%
torsten duwetorsten duwe1110.00%114.29%
amos kongamos kong76.36%114.29%
herbert xuherbert xu21.82%114.29%
rafael j. wysockirafael j. wysocki10.91%114.29%
Total110100.00%7100.00%

EXPORT_SYMBOL_GPL(hwrng_unregister);
static void devm_hwrng_release(struct device *dev, void *res) { hwrng_unregister(*(struct hwrng **)res); }

Contributors

PersonTokensPropCommitsCommitProp
dmitry torokhovdmitry torokhov27100.00%1100.00%
Total27100.00%1100.00%


static int devm_hwrng_match(struct device *dev, void *res, void *data) { struct hwrng **r = res; if (WARN_ON(!r || !*r)) return 0; return *r == data; }

Contributors

PersonTokensPropCommitsCommitProp
dmitry torokhovdmitry torokhov48100.00%1100.00%
Total48100.00%1100.00%


int devm_hwrng_register(struct device *dev, struct hwrng *rng) { struct hwrng **ptr; int error; ptr = devres_alloc(devm_hwrng_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return -ENOMEM; error = hwrng_register(rng); if (error) { devres_free(ptr); return error; } *ptr = rng; devres_add(dev, ptr); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
dmitry torokhovdmitry torokhov84100.00%1100.00%
Total84100.00%1100.00%

EXPORT_SYMBOL_GPL(devm_hwrng_register);
void devm_hwrng_unregister(struct device *dev, struct hwrng *rng) { devres_release(dev, devm_hwrng_release, devm_hwrng_match, rng); }

Contributors

PersonTokensPropCommitsCommitProp
dmitry torokhovdmitry torokhov26100.00%1100.00%
Total26100.00%1100.00%

EXPORT_SYMBOL_GPL(devm_hwrng_unregister);
static int __init hwrng_modinit(void) { return register_miscdev(); }

Contributors

PersonTokensPropCommitsCommitProp
herbert xuherbert xu13100.00%1100.00%
Total13100.00%1100.00%


static void __exit hwrng_modexit(void) { mutex_lock(&rng_mutex); BUG_ON(current_rng); kfree(rng_buffer); kfree(rng_fillbuf); mutex_unlock(&rng_mutex); unregister_miscdev(); }

Contributors

PersonTokensPropCommitsCommitProp
satoru takeuchisatoru takeuchi3076.92%133.33%
torsten duwetorsten duwe512.82%133.33%
herbert xuherbert xu410.26%133.33%
Total39100.00%3100.00%

module_init(hwrng_modinit); module_exit(hwrng_modexit); MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
michael bueschmichael buesch78134.34%12.56%
rusty russellrusty russell41018.03%512.82%
torsten duwetorsten duwe26211.52%37.69%
ian moltonian molton2099.19%25.13%
dmitry torokhovdmitry torokhov1958.58%12.56%
herbert xuherbert xu1325.80%615.38%
amit shahamit shah1044.57%12.56%
takashi iwaitakashi iwai401.76%12.56%
satoru takeuchisatoru takeuchi341.50%12.56%
greg kroah-hartmangreg kroah-hartman231.01%12.56%
jiri slabyjiri slaby150.66%12.56%
ralph wuerthnerralph wuerthner140.62%12.56%
kees cookkees cook90.40%12.56%
keith packardkeith packard80.35%12.56%
rickard strandqvistrickard strandqvist70.31%12.56%
amos kongamos kong70.31%12.56%
arnd bergmannarnd bergmann50.22%12.56%
kay sieverskay sievers50.22%25.13%
rafael j. wysockirafael j. wysocki40.18%12.56%
stephen boydstephen boyd20.09%12.56%
martin schwidefskymartin schwidefsky20.09%12.56%
al viroal viro20.09%12.56%
patrick mchardypatrick mchardy10.04%12.56%
michael buschmichael busch10.04%12.56%
arjan van de venarjan van de ven10.04%12.56%
lee joneslee jones10.04%12.56%
Total2274100.00%39100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}