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 51 static struct hwrng *current_rng; 52 static LIST_HEAD(rng_list); 53 static DEFINE_MUTEX(rng_mutex); 54 55 56 static inline int hwrng_init(struct hwrng *rng) 57 { 58 if (!rng->init) 59 return 0; 60 return rng->init(rng); 61 } 62 63 static inline void hwrng_cleanup(struct hwrng *rng) 64 { 65 if (rng && rng->cleanup) 66 rng->cleanup(rng); 67 } 68 69 static 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 76 static inline int hwrng_data_read(struct hwrng *rng, u32 *data) 77 { 78 return rng->data_read(rng, data); 79 } 80 81 82 static 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 92 static 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 } 148 out: 149 return ret ? : err; 150 } 151 152 153 static const struct file_operations rng_chrdev_ops = { 154 .owner = THIS_MODULE, 155 .open = rng_dev_open, 156 .read = rng_dev_read, 157 }; 158 159 static struct miscdevice rng_miscdev = { 160 .minor = RNG_MISCDEV_MINOR, 161 .name = RNG_MODULE_NAME, 162 .fops = &rng_chrdev_ops, 163 }; 164 165 166 static 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 197 static 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 216 static 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 241 static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR, 242 hwrng_attr_current_show, 243 hwrng_attr_current_store); 244 static DEVICE_ATTR(rng_available, S_IRUGO, 245 hwrng_attr_available_show, 246 NULL); 247 248 249 static 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 256 static 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; 271 out: 272 return err; 273 274 err_remove_current: 275 device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current); 276 err_misc_dereg: 277 misc_deregister(&rng_miscdev); 278 goto out; 279 } 280 281 int 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); 321 out_unlock: 322 mutex_unlock(&rng_mutex); 323 out: 324 return err; 325 } 326 EXPORT_SYMBOL_GPL(hwrng_register); 327 328 void 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 } 351 EXPORT_SYMBOL_GPL(hwrng_unregister); 352 353 354 MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); 355 MODULE_LICENSE("GPL"); 356