xref: /linux/drivers/char/hw_random/core.c (revision ed3174d93c342b8b2eeba6bbd124707d55304a7b)
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, int wait)
70 {
71 	if (!rng->data_present)
72 		return 1;
73 	return rng->data_present(rng, wait);
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 err = 0;
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 
110 		bytes_read = 0;
111 		if (hwrng_data_present(current_rng,
112 				       !(filp->f_flags & O_NONBLOCK)))
113 			bytes_read = hwrng_data_read(current_rng, &data);
114 		mutex_unlock(&rng_mutex);
115 
116 		err = -EAGAIN;
117 		if (!bytes_read && (filp->f_flags & O_NONBLOCK))
118 			goto out;
119 
120 		err = -EFAULT;
121 		while (bytes_read && size) {
122 			if (put_user((u8)data, buf++))
123 				goto out;
124 			size--;
125 			ret++;
126 			bytes_read--;
127 			data >>= 8;
128 		}
129 
130 		if (need_resched())
131 			schedule_timeout_interruptible(1);
132 		err = -ERESTARTSYS;
133 		if (signal_pending(current))
134 			goto out;
135 	}
136 out:
137 	return ret ? : err;
138 }
139 
140 
141 static const struct file_operations rng_chrdev_ops = {
142 	.owner		= THIS_MODULE,
143 	.open		= rng_dev_open,
144 	.read		= rng_dev_read,
145 };
146 
147 static struct miscdevice rng_miscdev = {
148 	.minor		= RNG_MISCDEV_MINOR,
149 	.name		= RNG_MODULE_NAME,
150 	.fops		= &rng_chrdev_ops,
151 };
152 
153 
154 static ssize_t hwrng_attr_current_store(struct device *dev,
155 					struct device_attribute *attr,
156 					const char *buf, size_t len)
157 {
158 	int err;
159 	struct hwrng *rng;
160 
161 	err = mutex_lock_interruptible(&rng_mutex);
162 	if (err)
163 		return -ERESTARTSYS;
164 	err = -ENODEV;
165 	list_for_each_entry(rng, &rng_list, list) {
166 		if (strcmp(rng->name, buf) == 0) {
167 			if (rng == current_rng) {
168 				err = 0;
169 				break;
170 			}
171 			err = hwrng_init(rng);
172 			if (err)
173 				break;
174 			hwrng_cleanup(current_rng);
175 			current_rng = rng;
176 			err = 0;
177 			break;
178 		}
179 	}
180 	mutex_unlock(&rng_mutex);
181 
182 	return err ? : len;
183 }
184 
185 static ssize_t hwrng_attr_current_show(struct device *dev,
186 				       struct device_attribute *attr,
187 				       char *buf)
188 {
189 	int err;
190 	ssize_t ret;
191 	const char *name = "none";
192 
193 	err = mutex_lock_interruptible(&rng_mutex);
194 	if (err)
195 		return -ERESTARTSYS;
196 	if (current_rng)
197 		name = current_rng->name;
198 	ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
199 	mutex_unlock(&rng_mutex);
200 
201 	return ret;
202 }
203 
204 static ssize_t hwrng_attr_available_show(struct device *dev,
205 					 struct device_attribute *attr,
206 					 char *buf)
207 {
208 	int err;
209 	ssize_t ret = 0;
210 	struct hwrng *rng;
211 
212 	err = mutex_lock_interruptible(&rng_mutex);
213 	if (err)
214 		return -ERESTARTSYS;
215 	buf[0] = '\0';
216 	list_for_each_entry(rng, &rng_list, list) {
217 		strncat(buf, rng->name, PAGE_SIZE - ret - 1);
218 		ret += strlen(rng->name);
219 		strncat(buf, " ", PAGE_SIZE - ret - 1);
220 		ret++;
221 	}
222 	strncat(buf, "\n", PAGE_SIZE - ret - 1);
223 	ret++;
224 	mutex_unlock(&rng_mutex);
225 
226 	return ret;
227 }
228 
229 static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR,
230 		   hwrng_attr_current_show,
231 		   hwrng_attr_current_store);
232 static DEVICE_ATTR(rng_available, S_IRUGO,
233 		   hwrng_attr_available_show,
234 		   NULL);
235 
236 
237 static void unregister_miscdev(bool suspended)
238 {
239 	device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available);
240 	device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
241 	__misc_deregister(&rng_miscdev, suspended);
242 }
243 
244 static int register_miscdev(void)
245 {
246 	int err;
247 
248 	err = misc_register(&rng_miscdev);
249 	if (err)
250 		goto out;
251 	err = device_create_file(rng_miscdev.this_device,
252 				 &dev_attr_rng_current);
253 	if (err)
254 		goto err_misc_dereg;
255 	err = device_create_file(rng_miscdev.this_device,
256 				 &dev_attr_rng_available);
257 	if (err)
258 		goto err_remove_current;
259 out:
260 	return err;
261 
262 err_remove_current:
263 	device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
264 err_misc_dereg:
265 	misc_deregister(&rng_miscdev);
266 	goto out;
267 }
268 
269 int hwrng_register(struct hwrng *rng)
270 {
271 	int must_register_misc;
272 	int err = -EINVAL;
273 	struct hwrng *old_rng, *tmp;
274 
275 	if (rng->name == NULL ||
276 	    rng->data_read == NULL)
277 		goto out;
278 
279 	mutex_lock(&rng_mutex);
280 
281 	/* Must not register two RNGs with the same name. */
282 	err = -EEXIST;
283 	list_for_each_entry(tmp, &rng_list, list) {
284 		if (strcmp(tmp->name, rng->name) == 0)
285 			goto out_unlock;
286 	}
287 
288 	must_register_misc = (current_rng == NULL);
289 	old_rng = current_rng;
290 	if (!old_rng) {
291 		err = hwrng_init(rng);
292 		if (err)
293 			goto out_unlock;
294 		current_rng = rng;
295 	}
296 	err = 0;
297 	if (must_register_misc) {
298 		err = register_miscdev();
299 		if (err) {
300 			if (!old_rng) {
301 				hwrng_cleanup(rng);
302 				current_rng = NULL;
303 			}
304 			goto out_unlock;
305 		}
306 	}
307 	INIT_LIST_HEAD(&rng->list);
308 	list_add_tail(&rng->list, &rng_list);
309 out_unlock:
310 	mutex_unlock(&rng_mutex);
311 out:
312 	return err;
313 }
314 EXPORT_SYMBOL_GPL(hwrng_register);
315 
316 void __hwrng_unregister(struct hwrng *rng, bool suspended)
317 {
318 	int err;
319 
320 	mutex_lock(&rng_mutex);
321 
322 	list_del(&rng->list);
323 	if (current_rng == rng) {
324 		hwrng_cleanup(rng);
325 		if (list_empty(&rng_list)) {
326 			current_rng = NULL;
327 		} else {
328 			current_rng = list_entry(rng_list.prev, struct hwrng, list);
329 			err = hwrng_init(current_rng);
330 			if (err)
331 				current_rng = NULL;
332 		}
333 	}
334 	if (list_empty(&rng_list))
335 		unregister_miscdev(suspended);
336 
337 	mutex_unlock(&rng_mutex);
338 }
339 EXPORT_SYMBOL_GPL(__hwrng_unregister);
340 
341 
342 MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
343 MODULE_LICENSE("GPL");
344