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