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/init.h> 40 #include <linux/miscdevice.h> 41 #include <linux/delay.h> 42 #include <asm/uaccess.h> 43 44 45 #define RNG_MODULE_NAME "hw_random" 46 #define PFX RNG_MODULE_NAME ": " 47 #define RNG_MISCDEV_MINOR 183 /* official */ 48 49 50 static struct hwrng *current_rng; 51 static LIST_HEAD(rng_list); 52 static DEFINE_MUTEX(rng_mutex); 53 54 55 static inline int hwrng_init(struct hwrng *rng) 56 { 57 if (!rng->init) 58 return 0; 59 return rng->init(rng); 60 } 61 62 static inline void hwrng_cleanup(struct hwrng *rng) 63 { 64 if (rng && rng->cleanup) 65 rng->cleanup(rng); 66 } 67 68 static inline int hwrng_data_present(struct hwrng *rng) 69 { 70 if (!rng->data_present) 71 return 1; 72 return rng->data_present(rng); 73 } 74 75 static inline int hwrng_data_read(struct hwrng *rng, u32 *data) 76 { 77 return rng->data_read(rng, data); 78 } 79 80 81 static int rng_dev_open(struct inode *inode, struct file *filp) 82 { 83 /* enforce read-only access to this chrdev */ 84 if ((filp->f_mode & FMODE_READ) == 0) 85 return -EINVAL; 86 if (filp->f_mode & FMODE_WRITE) 87 return -EINVAL; 88 return 0; 89 } 90 91 static ssize_t rng_dev_read(struct file *filp, char __user *buf, 92 size_t size, loff_t *offp) 93 { 94 u32 data; 95 ssize_t ret = 0; 96 int i, err = 0; 97 int data_present; 98 int bytes_read; 99 100 while (size) { 101 err = -ERESTARTSYS; 102 if (mutex_lock_interruptible(&rng_mutex)) 103 goto out; 104 if (!current_rng) { 105 mutex_unlock(&rng_mutex); 106 err = -ENODEV; 107 goto out; 108 } 109 if (filp->f_flags & O_NONBLOCK) { 110 data_present = hwrng_data_present(current_rng); 111 } else { 112 /* Some RNG require some time between data_reads to gather 113 * new entropy. Poll it. 114 */ 115 for (i = 0; i < 20; i++) { 116 data_present = hwrng_data_present(current_rng); 117 if (data_present) 118 break; 119 udelay(10); 120 } 121 } 122 bytes_read = 0; 123 if (data_present) 124 bytes_read = hwrng_data_read(current_rng, &data); 125 mutex_unlock(&rng_mutex); 126 127 err = -EAGAIN; 128 if (!bytes_read && (filp->f_flags & O_NONBLOCK)) 129 goto out; 130 131 err = -EFAULT; 132 while (bytes_read && size) { 133 if (put_user((u8)data, buf++)) 134 goto out; 135 size--; 136 ret++; 137 bytes_read--; 138 data >>= 8; 139 } 140 141 if (need_resched()) 142 schedule_timeout_interruptible(1); 143 err = -ERESTARTSYS; 144 if (signal_pending(current)) 145 goto out; 146 } 147 out: 148 return ret ? : err; 149 } 150 151 152 static struct file_operations rng_chrdev_ops = { 153 .owner = THIS_MODULE, 154 .open = rng_dev_open, 155 .read = rng_dev_read, 156 }; 157 158 static struct miscdevice rng_miscdev = { 159 .minor = RNG_MISCDEV_MINOR, 160 .name = RNG_MODULE_NAME, 161 .fops = &rng_chrdev_ops, 162 }; 163 164 165 static ssize_t hwrng_attr_current_store(struct class_device *class, 166 const char *buf, size_t len) 167 { 168 int err; 169 struct hwrng *rng; 170 171 err = mutex_lock_interruptible(&rng_mutex); 172 if (err) 173 return -ERESTARTSYS; 174 err = -ENODEV; 175 list_for_each_entry(rng, &rng_list, list) { 176 if (strcmp(rng->name, buf) == 0) { 177 if (rng == current_rng) { 178 err = 0; 179 break; 180 } 181 err = hwrng_init(rng); 182 if (err) 183 break; 184 hwrng_cleanup(current_rng); 185 current_rng = rng; 186 err = 0; 187 break; 188 } 189 } 190 mutex_unlock(&rng_mutex); 191 192 return err ? : len; 193 } 194 195 static ssize_t hwrng_attr_current_show(struct class_device *class, 196 char *buf) 197 { 198 int err; 199 ssize_t ret; 200 const char *name = "none"; 201 202 err = mutex_lock_interruptible(&rng_mutex); 203 if (err) 204 return -ERESTARTSYS; 205 if (current_rng) 206 name = current_rng->name; 207 ret = snprintf(buf, PAGE_SIZE, "%s\n", name); 208 mutex_unlock(&rng_mutex); 209 210 return ret; 211 } 212 213 static ssize_t hwrng_attr_available_show(struct class_device *class, 214 char *buf) 215 { 216 int err; 217 ssize_t ret = 0; 218 struct hwrng *rng; 219 220 err = mutex_lock_interruptible(&rng_mutex); 221 if (err) 222 return -ERESTARTSYS; 223 buf[0] = '\0'; 224 list_for_each_entry(rng, &rng_list, list) { 225 strncat(buf, rng->name, PAGE_SIZE - ret - 1); 226 ret += strlen(rng->name); 227 strncat(buf, " ", PAGE_SIZE - ret - 1); 228 ret++; 229 } 230 strncat(buf, "\n", PAGE_SIZE - ret - 1); 231 ret++; 232 mutex_unlock(&rng_mutex); 233 234 return ret; 235 } 236 237 static CLASS_DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR, 238 hwrng_attr_current_show, 239 hwrng_attr_current_store); 240 static CLASS_DEVICE_ATTR(rng_available, S_IRUGO, 241 hwrng_attr_available_show, 242 NULL); 243 244 245 static void unregister_miscdev(void) 246 { 247 class_device_remove_file(rng_miscdev.class, 248 &class_device_attr_rng_available); 249 class_device_remove_file(rng_miscdev.class, 250 &class_device_attr_rng_current); 251 misc_deregister(&rng_miscdev); 252 } 253 254 static int register_miscdev(void) 255 { 256 int err; 257 258 err = misc_register(&rng_miscdev); 259 if (err) 260 goto out; 261 err = class_device_create_file(rng_miscdev.class, 262 &class_device_attr_rng_current); 263 if (err) 264 goto err_misc_dereg; 265 err = class_device_create_file(rng_miscdev.class, 266 &class_device_attr_rng_available); 267 if (err) 268 goto err_remove_current; 269 out: 270 return err; 271 272 err_remove_current: 273 class_device_remove_file(rng_miscdev.class, 274 &class_device_attr_rng_current); 275 err_misc_dereg: 276 misc_deregister(&rng_miscdev); 277 goto out; 278 } 279 280 int hwrng_register(struct hwrng *rng) 281 { 282 int must_register_misc; 283 int err = -EINVAL; 284 struct hwrng *old_rng, *tmp; 285 286 if (rng->name == NULL || 287 rng->data_read == NULL) 288 goto out; 289 290 mutex_lock(&rng_mutex); 291 292 /* Must not register two RNGs with the same name. */ 293 err = -EEXIST; 294 list_for_each_entry(tmp, &rng_list, list) { 295 if (strcmp(tmp->name, rng->name) == 0) 296 goto out_unlock; 297 } 298 299 must_register_misc = (current_rng == NULL); 300 old_rng = current_rng; 301 if (!old_rng) { 302 err = hwrng_init(rng); 303 if (err) 304 goto out_unlock; 305 current_rng = rng; 306 } 307 err = 0; 308 if (must_register_misc) { 309 err = register_miscdev(); 310 if (err) { 311 if (!old_rng) { 312 hwrng_cleanup(rng); 313 current_rng = NULL; 314 } 315 goto out_unlock; 316 } 317 } 318 INIT_LIST_HEAD(&rng->list); 319 list_add_tail(&rng->list, &rng_list); 320 out_unlock: 321 mutex_unlock(&rng_mutex); 322 out: 323 return err; 324 } 325 EXPORT_SYMBOL_GPL(hwrng_register); 326 327 void hwrng_unregister(struct hwrng *rng) 328 { 329 int err; 330 331 mutex_lock(&rng_mutex); 332 333 list_del(&rng->list); 334 if (current_rng == rng) { 335 hwrng_cleanup(rng); 336 if (list_empty(&rng_list)) { 337 current_rng = NULL; 338 } else { 339 current_rng = list_entry(rng_list.prev, struct hwrng, list); 340 err = hwrng_init(current_rng); 341 if (err) 342 current_rng = NULL; 343 } 344 } 345 if (list_empty(&rng_list)) 346 unregister_miscdev(); 347 348 mutex_unlock(&rng_mutex); 349 } 350 EXPORT_SYMBOL_GPL(hwrng_unregister); 351 352 353 MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); 354 MODULE_LICENSE("GPL"); 355