Commit | Line | Data |
---|---|---|
844dd05f MB |
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 | |
eb032b98 | 22 | Copyright 2006 Michael Buesch <m@bues.ch> |
844dd05f MB |
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> | |
914e2637 | 39 | #include <linux/sched.h> |
844dd05f | 40 | #include <linux/miscdevice.h> |
be4000bc | 41 | #include <linux/kthread.h> |
844dd05f | 42 | #include <linux/delay.h> |
f7f154f1 | 43 | #include <linux/slab.h> |
d9e79726 | 44 | #include <linux/random.h> |
844dd05f MB |
45 | #include <asm/uaccess.h> |
46 | ||
47 | ||
48 | #define RNG_MODULE_NAME "hw_random" | |
49 | #define PFX RNG_MODULE_NAME ": " | |
50 | #define RNG_MISCDEV_MINOR 183 /* official */ | |
51 | ||
52 | ||
53 | static struct hwrng *current_rng; | |
be4000bc | 54 | static struct task_struct *hwrng_fill; |
844dd05f | 55 | static LIST_HEAD(rng_list); |
9372b35e | 56 | /* Protects rng_list and current_rng */ |
844dd05f | 57 | static DEFINE_MUTEX(rng_mutex); |
9372b35e RR |
58 | /* Protects rng read functions, data_avail, rng_buffer and rng_fillbuf */ |
59 | static DEFINE_MUTEX(reading_mutex); | |
9996508b | 60 | static int data_avail; |
be4000bc | 61 | static u8 *rng_buffer, *rng_fillbuf; |
0f734e6e TD |
62 | static unsigned short current_quality; |
63 | static unsigned short default_quality; /* = 0; default to "off" */ | |
be4000bc TD |
64 | |
65 | module_param(current_quality, ushort, 0644); | |
66 | MODULE_PARM_DESC(current_quality, | |
67 | "current hwrng entropy estimation per mill"); | |
0f734e6e TD |
68 | module_param(default_quality, ushort, 0644); |
69 | MODULE_PARM_DESC(default_quality, | |
70 | "default entropy content of hwrng per mill"); | |
be4000bc TD |
71 | |
72 | static void start_khwrngd(void); | |
f7f154f1 | 73 | |
d3cc7996 AS |
74 | static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, |
75 | int wait); | |
76 | ||
f7f154f1 RR |
77 | static size_t rng_buffer_size(void) |
78 | { | |
79 | return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES; | |
80 | } | |
844dd05f | 81 | |
d3cc7996 AS |
82 | static void add_early_randomness(struct hwrng *rng) |
83 | { | |
84 | unsigned char bytes[16]; | |
85 | int bytes_read; | |
86 | ||
9372b35e | 87 | mutex_lock(&reading_mutex); |
d3cc7996 | 88 | bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1); |
9372b35e | 89 | mutex_unlock(&reading_mutex); |
d3cc7996 AS |
90 | if (bytes_read > 0) |
91 | add_device_randomness(bytes, bytes_read); | |
92 | } | |
93 | ||
844dd05f MB |
94 | static inline int hwrng_init(struct hwrng *rng) |
95 | { | |
d3cc7996 AS |
96 | if (rng->init) { |
97 | int ret; | |
98 | ||
99 | ret = rng->init(rng); | |
100 | if (ret) | |
101 | return ret; | |
102 | } | |
103 | add_early_randomness(rng); | |
be4000bc | 104 | |
0f734e6e TD |
105 | current_quality = rng->quality ? : default_quality; |
106 | current_quality &= 1023; | |
107 | ||
108 | if (current_quality == 0 && hwrng_fill) | |
109 | kthread_stop(hwrng_fill); | |
be4000bc TD |
110 | if (current_quality > 0 && !hwrng_fill) |
111 | start_khwrngd(); | |
112 | ||
d3cc7996 | 113 | return 0; |
844dd05f MB |
114 | } |
115 | ||
116 | static inline void hwrng_cleanup(struct hwrng *rng) | |
117 | { | |
118 | if (rng && rng->cleanup) | |
119 | rng->cleanup(rng); | |
120 | } | |
121 | ||
844dd05f MB |
122 | static int rng_dev_open(struct inode *inode, struct file *filp) |
123 | { | |
124 | /* enforce read-only access to this chrdev */ | |
125 | if ((filp->f_mode & FMODE_READ) == 0) | |
126 | return -EINVAL; | |
127 | if (filp->f_mode & FMODE_WRITE) | |
128 | return -EINVAL; | |
129 | return 0; | |
130 | } | |
131 | ||
9996508b IM |
132 | static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, |
133 | int wait) { | |
134 | int present; | |
135 | ||
9372b35e | 136 | BUG_ON(!mutex_is_locked(&reading_mutex)); |
9996508b IM |
137 | if (rng->read) |
138 | return rng->read(rng, (void *)buffer, size, wait); | |
139 | ||
140 | if (rng->data_present) | |
141 | present = rng->data_present(rng, wait); | |
142 | else | |
143 | present = 1; | |
144 | ||
145 | if (present) | |
146 | return rng->data_read(rng, (u32 *)buffer); | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
844dd05f MB |
151 | static ssize_t rng_dev_read(struct file *filp, char __user *buf, |
152 | size_t size, loff_t *offp) | |
153 | { | |
844dd05f | 154 | ssize_t ret = 0; |
984e976f | 155 | int err = 0; |
9996508b | 156 | int bytes_read, len; |
844dd05f MB |
157 | |
158 | while (size) { | |
9996508b IM |
159 | if (mutex_lock_interruptible(&rng_mutex)) { |
160 | err = -ERESTARTSYS; | |
844dd05f | 161 | goto out; |
9996508b IM |
162 | } |
163 | ||
844dd05f | 164 | if (!current_rng) { |
844dd05f | 165 | err = -ENODEV; |
9996508b | 166 | goto out_unlock; |
844dd05f | 167 | } |
984e976f | 168 | |
9372b35e | 169 | mutex_lock(&reading_mutex); |
9996508b IM |
170 | if (!data_avail) { |
171 | bytes_read = rng_get_data(current_rng, rng_buffer, | |
f7f154f1 | 172 | rng_buffer_size(), |
9996508b IM |
173 | !(filp->f_flags & O_NONBLOCK)); |
174 | if (bytes_read < 0) { | |
175 | err = bytes_read; | |
9372b35e | 176 | goto out_unlock_reading; |
9996508b IM |
177 | } |
178 | data_avail = bytes_read; | |
893f1128 | 179 | } |
844dd05f | 180 | |
9996508b IM |
181 | if (!data_avail) { |
182 | if (filp->f_flags & O_NONBLOCK) { | |
183 | err = -EAGAIN; | |
9372b35e | 184 | goto out_unlock_reading; |
9996508b IM |
185 | } |
186 | } else { | |
187 | len = data_avail; | |
188 | if (len > size) | |
189 | len = size; | |
190 | ||
191 | data_avail -= len; | |
192 | ||
193 | if (copy_to_user(buf + ret, rng_buffer + data_avail, | |
194 | len)) { | |
195 | err = -EFAULT; | |
9372b35e | 196 | goto out_unlock_reading; |
9996508b IM |
197 | } |
198 | ||
199 | size -= len; | |
200 | ret += len; | |
844dd05f MB |
201 | } |
202 | ||
9996508b | 203 | mutex_unlock(&rng_mutex); |
9372b35e | 204 | mutex_unlock(&reading_mutex); |
9996508b | 205 | |
844dd05f MB |
206 | if (need_resched()) |
207 | schedule_timeout_interruptible(1); | |
9996508b IM |
208 | |
209 | if (signal_pending(current)) { | |
210 | err = -ERESTARTSYS; | |
844dd05f | 211 | goto out; |
9996508b | 212 | } |
844dd05f MB |
213 | } |
214 | out: | |
215 | return ret ? : err; | |
f5908267 HX |
216 | out_unlock: |
217 | mutex_unlock(&rng_mutex); | |
218 | goto out; | |
9372b35e RR |
219 | out_unlock_reading: |
220 | mutex_unlock(&reading_mutex); | |
221 | goto out_unlock; | |
844dd05f MB |
222 | } |
223 | ||
224 | ||
62322d25 | 225 | static const struct file_operations rng_chrdev_ops = { |
844dd05f MB |
226 | .owner = THIS_MODULE, |
227 | .open = rng_dev_open, | |
228 | .read = rng_dev_read, | |
6038f373 | 229 | .llseek = noop_llseek, |
844dd05f MB |
230 | }; |
231 | ||
232 | static struct miscdevice rng_miscdev = { | |
233 | .minor = RNG_MISCDEV_MINOR, | |
234 | .name = RNG_MODULE_NAME, | |
e454cea2 | 235 | .nodename = "hwrng", |
844dd05f MB |
236 | .fops = &rng_chrdev_ops, |
237 | }; | |
238 | ||
239 | ||
94fbcded GKH |
240 | static ssize_t hwrng_attr_current_store(struct device *dev, |
241 | struct device_attribute *attr, | |
844dd05f MB |
242 | const char *buf, size_t len) |
243 | { | |
244 | int err; | |
245 | struct hwrng *rng; | |
246 | ||
247 | err = mutex_lock_interruptible(&rng_mutex); | |
248 | if (err) | |
249 | return -ERESTARTSYS; | |
250 | err = -ENODEV; | |
251 | list_for_each_entry(rng, &rng_list, list) { | |
252 | if (strcmp(rng->name, buf) == 0) { | |
253 | if (rng == current_rng) { | |
254 | err = 0; | |
255 | break; | |
256 | } | |
257 | err = hwrng_init(rng); | |
258 | if (err) | |
259 | break; | |
260 | hwrng_cleanup(current_rng); | |
261 | current_rng = rng; | |
262 | err = 0; | |
263 | break; | |
264 | } | |
265 | } | |
266 | mutex_unlock(&rng_mutex); | |
267 | ||
268 | return err ? : len; | |
269 | } | |
270 | ||
94fbcded GKH |
271 | static ssize_t hwrng_attr_current_show(struct device *dev, |
272 | struct device_attribute *attr, | |
844dd05f MB |
273 | char *buf) |
274 | { | |
275 | int err; | |
276 | ssize_t ret; | |
277 | const char *name = "none"; | |
278 | ||
279 | err = mutex_lock_interruptible(&rng_mutex); | |
280 | if (err) | |
281 | return -ERESTARTSYS; | |
282 | if (current_rng) | |
283 | name = current_rng->name; | |
284 | ret = snprintf(buf, PAGE_SIZE, "%s\n", name); | |
285 | mutex_unlock(&rng_mutex); | |
286 | ||
287 | return ret; | |
288 | } | |
289 | ||
94fbcded GKH |
290 | static ssize_t hwrng_attr_available_show(struct device *dev, |
291 | struct device_attribute *attr, | |
844dd05f MB |
292 | char *buf) |
293 | { | |
294 | int err; | |
844dd05f MB |
295 | struct hwrng *rng; |
296 | ||
297 | err = mutex_lock_interruptible(&rng_mutex); | |
298 | if (err) | |
299 | return -ERESTARTSYS; | |
300 | buf[0] = '\0'; | |
301 | list_for_each_entry(rng, &rng_list, list) { | |
61daf055 RS |
302 | strlcat(buf, rng->name, PAGE_SIZE); |
303 | strlcat(buf, " ", PAGE_SIZE); | |
844dd05f | 304 | } |
61daf055 | 305 | strlcat(buf, "\n", PAGE_SIZE); |
844dd05f MB |
306 | mutex_unlock(&rng_mutex); |
307 | ||
61daf055 | 308 | return strlen(buf); |
844dd05f MB |
309 | } |
310 | ||
94fbcded GKH |
311 | static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR, |
312 | hwrng_attr_current_show, | |
313 | hwrng_attr_current_store); | |
314 | static DEVICE_ATTR(rng_available, S_IRUGO, | |
315 | hwrng_attr_available_show, | |
316 | NULL); | |
844dd05f MB |
317 | |
318 | ||
b844eba2 | 319 | static void unregister_miscdev(void) |
844dd05f | 320 | { |
94fbcded GKH |
321 | device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available); |
322 | device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current); | |
b844eba2 | 323 | misc_deregister(&rng_miscdev); |
844dd05f MB |
324 | } |
325 | ||
326 | static int register_miscdev(void) | |
327 | { | |
328 | int err; | |
329 | ||
330 | err = misc_register(&rng_miscdev); | |
331 | if (err) | |
332 | goto out; | |
94fbcded GKH |
333 | err = device_create_file(rng_miscdev.this_device, |
334 | &dev_attr_rng_current); | |
844dd05f MB |
335 | if (err) |
336 | goto err_misc_dereg; | |
94fbcded GKH |
337 | err = device_create_file(rng_miscdev.this_device, |
338 | &dev_attr_rng_available); | |
844dd05f MB |
339 | if (err) |
340 | goto err_remove_current; | |
341 | out: | |
342 | return err; | |
343 | ||
344 | err_remove_current: | |
94fbcded | 345 | device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current); |
844dd05f MB |
346 | err_misc_dereg: |
347 | misc_deregister(&rng_miscdev); | |
348 | goto out; | |
349 | } | |
350 | ||
be4000bc TD |
351 | static int hwrng_fillfn(void *unused) |
352 | { | |
353 | long rc; | |
354 | ||
355 | while (!kthread_should_stop()) { | |
356 | if (!current_rng) | |
357 | break; | |
9372b35e | 358 | mutex_lock(&reading_mutex); |
be4000bc TD |
359 | rc = rng_get_data(current_rng, rng_fillbuf, |
360 | rng_buffer_size(), 1); | |
9372b35e | 361 | mutex_unlock(&reading_mutex); |
be4000bc TD |
362 | if (rc <= 0) { |
363 | pr_warn("hwrng: no data available\n"); | |
364 | msleep_interruptible(10000); | |
365 | continue; | |
366 | } | |
9372b35e | 367 | /* Outside lock, sure, but y'know: randomness. */ |
be4000bc | 368 | add_hwgenerator_randomness((void *)rng_fillbuf, rc, |
e02b8765 | 369 | rc * current_quality * 8 >> 10); |
be4000bc | 370 | } |
9dda727d | 371 | hwrng_fill = NULL; |
be4000bc TD |
372 | return 0; |
373 | } | |
374 | ||
375 | static void start_khwrngd(void) | |
376 | { | |
377 | hwrng_fill = kthread_run(hwrng_fillfn, NULL, "hwrng"); | |
378 | if (hwrng_fill == ERR_PTR(-ENOMEM)) { | |
379 | pr_err("hwrng_fill thread creation failed"); | |
380 | hwrng_fill = NULL; | |
381 | } | |
382 | } | |
383 | ||
844dd05f MB |
384 | int hwrng_register(struct hwrng *rng) |
385 | { | |
844dd05f MB |
386 | int err = -EINVAL; |
387 | struct hwrng *old_rng, *tmp; | |
388 | ||
389 | if (rng->name == NULL || | |
9996508b | 390 | (rng->data_read == NULL && rng->read == NULL)) |
844dd05f MB |
391 | goto out; |
392 | ||
393 | mutex_lock(&rng_mutex); | |
394 | ||
f7f154f1 RR |
395 | /* kmalloc makes this safe for virt_to_page() in virtio_rng.c */ |
396 | err = -ENOMEM; | |
397 | if (!rng_buffer) { | |
398 | rng_buffer = kmalloc(rng_buffer_size(), GFP_KERNEL); | |
399 | if (!rng_buffer) | |
400 | goto out_unlock; | |
401 | } | |
be4000bc TD |
402 | if (!rng_fillbuf) { |
403 | rng_fillbuf = kmalloc(rng_buffer_size(), GFP_KERNEL); | |
404 | if (!rng_fillbuf) { | |
405 | kfree(rng_buffer); | |
406 | goto out_unlock; | |
407 | } | |
408 | } | |
f7f154f1 | 409 | |
844dd05f MB |
410 | /* Must not register two RNGs with the same name. */ |
411 | err = -EEXIST; | |
412 | list_for_each_entry(tmp, &rng_list, list) { | |
413 | if (strcmp(tmp->name, rng->name) == 0) | |
414 | goto out_unlock; | |
415 | } | |
416 | ||
844dd05f MB |
417 | old_rng = current_rng; |
418 | if (!old_rng) { | |
419 | err = hwrng_init(rng); | |
420 | if (err) | |
421 | goto out_unlock; | |
422 | current_rng = rng; | |
423 | } | |
424 | err = 0; | |
d167b6e1 | 425 | if (!old_rng) { |
844dd05f MB |
426 | err = register_miscdev(); |
427 | if (err) { | |
d167b6e1 DC |
428 | hwrng_cleanup(rng); |
429 | current_rng = NULL; | |
844dd05f MB |
430 | goto out_unlock; |
431 | } | |
432 | } | |
433 | INIT_LIST_HEAD(&rng->list); | |
434 | list_add_tail(&rng->list, &rng_list); | |
d9e79726 | 435 | |
d3cc7996 AS |
436 | if (old_rng && !rng->init) { |
437 | /* | |
438 | * Use a new device's input to add some randomness to | |
439 | * the system. If this rng device isn't going to be | |
440 | * used right away, its init function hasn't been | |
441 | * called yet; so only use the randomness from devices | |
442 | * that don't need an init callback. | |
443 | */ | |
444 | add_early_randomness(rng); | |
445 | } | |
446 | ||
844dd05f MB |
447 | out_unlock: |
448 | mutex_unlock(&rng_mutex); | |
449 | out: | |
450 | return err; | |
451 | } | |
452 | EXPORT_SYMBOL_GPL(hwrng_register); | |
453 | ||
b844eba2 | 454 | void hwrng_unregister(struct hwrng *rng) |
844dd05f MB |
455 | { |
456 | int err; | |
457 | ||
458 | mutex_lock(&rng_mutex); | |
459 | ||
460 | list_del(&rng->list); | |
461 | if (current_rng == rng) { | |
462 | hwrng_cleanup(rng); | |
463 | if (list_empty(&rng_list)) { | |
464 | current_rng = NULL; | |
465 | } else { | |
466 | current_rng = list_entry(rng_list.prev, struct hwrng, list); | |
467 | err = hwrng_init(current_rng); | |
468 | if (err) | |
469 | current_rng = NULL; | |
470 | } | |
471 | } | |
be4000bc | 472 | if (list_empty(&rng_list)) { |
b844eba2 | 473 | unregister_miscdev(); |
be4000bc TD |
474 | if (hwrng_fill) |
475 | kthread_stop(hwrng_fill); | |
476 | } | |
844dd05f MB |
477 | |
478 | mutex_unlock(&rng_mutex); | |
479 | } | |
b844eba2 | 480 | EXPORT_SYMBOL_GPL(hwrng_unregister); |
844dd05f | 481 | |
b7d44d94 ST |
482 | static void __exit hwrng_exit(void) |
483 | { | |
484 | mutex_lock(&rng_mutex); | |
485 | BUG_ON(current_rng); | |
486 | kfree(rng_buffer); | |
be4000bc | 487 | kfree(rng_fillbuf); |
b7d44d94 ST |
488 | mutex_unlock(&rng_mutex); |
489 | } | |
490 | ||
491 | module_exit(hwrng_exit); | |
844dd05f MB |
492 | |
493 | MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); | |
494 | MODULE_LICENSE("GPL"); |