xref: /linux/drivers/char/hw_random/core.c (revision 5e8d780d745c1619aba81fe7166c5a4b5cad2b84)
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