Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.24 355 lines 7.5 kB view raw
1/* 2 Added support for the AMD Geode LX RNG 3 (c) Copyright 2004-2005 Advanced Micro Devices, Inc. 4 5 derived from 6 7 Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG) 8 (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com> 9 10 derived from 11 12 Hardware driver for the AMD 768 Random Number Generator (RNG) 13 (c) Copyright 2001 Red Hat Inc <alan@redhat.com> 14 15 derived from 16 17 Hardware driver for Intel i810 Random Number Generator (RNG) 18 Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com> 19 Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com> 20 21 Added generic RNG API 22 Copyright 2006 Michael Buesch <mbuesch@freenet.de> 23 Copyright 2005 (c) MontaVista Software, Inc. 24 25 Please read Documentation/hw_random.txt for details on use. 26 27 ---------------------------------------------------------- 28 This software may be used and distributed according to the terms 29 of the GNU General Public License, incorporated herein by reference. 30 31 */ 32 33 34#include <linux/device.h> 35#include <linux/hw_random.h> 36#include <linux/module.h> 37#include <linux/kernel.h> 38#include <linux/fs.h> 39#include <linux/sched.h> 40#include <linux/init.h> 41#include <linux/miscdevice.h> 42#include <linux/delay.h> 43#include <asm/uaccess.h> 44 45 46#define RNG_MODULE_NAME "hw_random" 47#define PFX RNG_MODULE_NAME ": " 48#define RNG_MISCDEV_MINOR 183 /* official */ 49 50 51static struct hwrng *current_rng; 52static LIST_HEAD(rng_list); 53static DEFINE_MUTEX(rng_mutex); 54 55 56static inline int hwrng_init(struct hwrng *rng) 57{ 58 if (!rng->init) 59 return 0; 60 return rng->init(rng); 61} 62 63static inline void hwrng_cleanup(struct hwrng *rng) 64{ 65 if (rng && rng->cleanup) 66 rng->cleanup(rng); 67} 68 69static inline int hwrng_data_present(struct hwrng *rng) 70{ 71 if (!rng->data_present) 72 return 1; 73 return rng->data_present(rng); 74} 75 76static inline int hwrng_data_read(struct hwrng *rng, u32 *data) 77{ 78 return rng->data_read(rng, data); 79} 80 81 82static int rng_dev_open(struct inode *inode, struct file *filp) 83{ 84 /* enforce read-only access to this chrdev */ 85 if ((filp->f_mode & FMODE_READ) == 0) 86 return -EINVAL; 87 if (filp->f_mode & FMODE_WRITE) 88 return -EINVAL; 89 return 0; 90} 91 92static ssize_t rng_dev_read(struct file *filp, char __user *buf, 93 size_t size, loff_t *offp) 94{ 95 u32 data; 96 ssize_t ret = 0; 97 int i, err = 0; 98 int data_present; 99 int bytes_read; 100 101 while (size) { 102 err = -ERESTARTSYS; 103 if (mutex_lock_interruptible(&rng_mutex)) 104 goto out; 105 if (!current_rng) { 106 mutex_unlock(&rng_mutex); 107 err = -ENODEV; 108 goto out; 109 } 110 if (filp->f_flags & O_NONBLOCK) { 111 data_present = hwrng_data_present(current_rng); 112 } else { 113 /* Some RNG require some time between data_reads to gather 114 * new entropy. Poll it. 115 */ 116 for (i = 0; i < 20; i++) { 117 data_present = hwrng_data_present(current_rng); 118 if (data_present) 119 break; 120 udelay(10); 121 } 122 } 123 bytes_read = 0; 124 if (data_present) 125 bytes_read = hwrng_data_read(current_rng, &data); 126 mutex_unlock(&rng_mutex); 127 128 err = -EAGAIN; 129 if (!bytes_read && (filp->f_flags & O_NONBLOCK)) 130 goto out; 131 132 err = -EFAULT; 133 while (bytes_read && size) { 134 if (put_user((u8)data, buf++)) 135 goto out; 136 size--; 137 ret++; 138 bytes_read--; 139 data >>= 8; 140 } 141 142 if (need_resched()) 143 schedule_timeout_interruptible(1); 144 err = -ERESTARTSYS; 145 if (signal_pending(current)) 146 goto out; 147 } 148out: 149 return ret ? : err; 150} 151 152 153static const struct file_operations rng_chrdev_ops = { 154 .owner = THIS_MODULE, 155 .open = rng_dev_open, 156 .read = rng_dev_read, 157}; 158 159static struct miscdevice rng_miscdev = { 160 .minor = RNG_MISCDEV_MINOR, 161 .name = RNG_MODULE_NAME, 162 .fops = &rng_chrdev_ops, 163}; 164 165 166static ssize_t hwrng_attr_current_store(struct device *dev, 167 struct device_attribute *attr, 168 const char *buf, size_t len) 169{ 170 int err; 171 struct hwrng *rng; 172 173 err = mutex_lock_interruptible(&rng_mutex); 174 if (err) 175 return -ERESTARTSYS; 176 err = -ENODEV; 177 list_for_each_entry(rng, &rng_list, list) { 178 if (strcmp(rng->name, buf) == 0) { 179 if (rng == current_rng) { 180 err = 0; 181 break; 182 } 183 err = hwrng_init(rng); 184 if (err) 185 break; 186 hwrng_cleanup(current_rng); 187 current_rng = rng; 188 err = 0; 189 break; 190 } 191 } 192 mutex_unlock(&rng_mutex); 193 194 return err ? : len; 195} 196 197static ssize_t hwrng_attr_current_show(struct device *dev, 198 struct device_attribute *attr, 199 char *buf) 200{ 201 int err; 202 ssize_t ret; 203 const char *name = "none"; 204 205 err = mutex_lock_interruptible(&rng_mutex); 206 if (err) 207 return -ERESTARTSYS; 208 if (current_rng) 209 name = current_rng->name; 210 ret = snprintf(buf, PAGE_SIZE, "%s\n", name); 211 mutex_unlock(&rng_mutex); 212 213 return ret; 214} 215 216static ssize_t hwrng_attr_available_show(struct device *dev, 217 struct device_attribute *attr, 218 char *buf) 219{ 220 int err; 221 ssize_t ret = 0; 222 struct hwrng *rng; 223 224 err = mutex_lock_interruptible(&rng_mutex); 225 if (err) 226 return -ERESTARTSYS; 227 buf[0] = '\0'; 228 list_for_each_entry(rng, &rng_list, list) { 229 strncat(buf, rng->name, PAGE_SIZE - ret - 1); 230 ret += strlen(rng->name); 231 strncat(buf, " ", PAGE_SIZE - ret - 1); 232 ret++; 233 } 234 strncat(buf, "\n", PAGE_SIZE - ret - 1); 235 ret++; 236 mutex_unlock(&rng_mutex); 237 238 return ret; 239} 240 241static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR, 242 hwrng_attr_current_show, 243 hwrng_attr_current_store); 244static DEVICE_ATTR(rng_available, S_IRUGO, 245 hwrng_attr_available_show, 246 NULL); 247 248 249static void unregister_miscdev(void) 250{ 251 device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available); 252 device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current); 253 misc_deregister(&rng_miscdev); 254} 255 256static int register_miscdev(void) 257{ 258 int err; 259 260 err = misc_register(&rng_miscdev); 261 if (err) 262 goto out; 263 err = device_create_file(rng_miscdev.this_device, 264 &dev_attr_rng_current); 265 if (err) 266 goto err_misc_dereg; 267 err = device_create_file(rng_miscdev.this_device, 268 &dev_attr_rng_available); 269 if (err) 270 goto err_remove_current; 271out: 272 return err; 273 274err_remove_current: 275 device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current); 276err_misc_dereg: 277 misc_deregister(&rng_miscdev); 278 goto out; 279} 280 281int hwrng_register(struct hwrng *rng) 282{ 283 int must_register_misc; 284 int err = -EINVAL; 285 struct hwrng *old_rng, *tmp; 286 287 if (rng->name == NULL || 288 rng->data_read == NULL) 289 goto out; 290 291 mutex_lock(&rng_mutex); 292 293 /* Must not register two RNGs with the same name. */ 294 err = -EEXIST; 295 list_for_each_entry(tmp, &rng_list, list) { 296 if (strcmp(tmp->name, rng->name) == 0) 297 goto out_unlock; 298 } 299 300 must_register_misc = (current_rng == NULL); 301 old_rng = current_rng; 302 if (!old_rng) { 303 err = hwrng_init(rng); 304 if (err) 305 goto out_unlock; 306 current_rng = rng; 307 } 308 err = 0; 309 if (must_register_misc) { 310 err = register_miscdev(); 311 if (err) { 312 if (!old_rng) { 313 hwrng_cleanup(rng); 314 current_rng = NULL; 315 } 316 goto out_unlock; 317 } 318 } 319 INIT_LIST_HEAD(&rng->list); 320 list_add_tail(&rng->list, &rng_list); 321out_unlock: 322 mutex_unlock(&rng_mutex); 323out: 324 return err; 325} 326EXPORT_SYMBOL_GPL(hwrng_register); 327 328void hwrng_unregister(struct hwrng *rng) 329{ 330 int err; 331 332 mutex_lock(&rng_mutex); 333 334 list_del(&rng->list); 335 if (current_rng == rng) { 336 hwrng_cleanup(rng); 337 if (list_empty(&rng_list)) { 338 current_rng = NULL; 339 } else { 340 current_rng = list_entry(rng_list.prev, struct hwrng, list); 341 err = hwrng_init(current_rng); 342 if (err) 343 current_rng = NULL; 344 } 345 } 346 if (list_empty(&rng_list)) 347 unregister_miscdev(); 348 349 mutex_unlock(&rng_mutex); 350} 351EXPORT_SYMBOL_GPL(hwrng_unregister); 352 353 354MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); 355MODULE_LICENSE("GPL");