core.c 10.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
        Added support for the AMD Geode LX RNG
	(c) Copyright 2004-2005 Advanced Micro Devices, Inc.

	derived from

 	Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
	(c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>

 	derived from

        Hardware driver for the AMD 768 Random Number Generator (RNG)
        (c) Copyright 2001 Red Hat Inc <alan@redhat.com>

 	derived from

	Hardware driver for Intel i810 Random Number Generator (RNG)
	Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
	Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>

	Added generic RNG API
M
Michael Büsch 已提交
22
	Copyright 2006 Michael Buesch <m@bues.ch>
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
	Copyright 2005 (c) MontaVista Software, Inc.

	Please read Documentation/hw_random.txt for details on use.

	----------------------------------------------------------
	This software may be used and distributed according to the terms
        of the GNU General Public License, incorporated herein by reference.

 */


#include <linux/device.h>
#include <linux/hw_random.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
39
#include <linux/sched.h>
40
#include <linux/miscdevice.h>
T
Torsten Duwe 已提交
41
#include <linux/kthread.h>
42
#include <linux/delay.h>
43
#include <linux/slab.h>
44
#include <linux/random.h>
45 46 47 48 49 50 51 52 53
#include <asm/uaccess.h>


#define RNG_MODULE_NAME		"hw_random"
#define PFX			RNG_MODULE_NAME ": "
#define RNG_MISCDEV_MINOR	183 /* official */


static struct hwrng *current_rng;
T
Torsten Duwe 已提交
54
static struct task_struct *hwrng_fill;
55 56
static LIST_HEAD(rng_list);
static DEFINE_MUTEX(rng_mutex);
57
static int data_avail;
T
Torsten Duwe 已提交
58
static u8 *rng_buffer, *rng_fillbuf;
59 60
static unsigned short current_quality;
static unsigned short default_quality; /* = 0; default to "off" */
T
Torsten Duwe 已提交
61 62 63 64

module_param(current_quality, ushort, 0644);
MODULE_PARM_DESC(current_quality,
		 "current hwrng entropy estimation per mill");
65 66 67
module_param(default_quality, ushort, 0644);
MODULE_PARM_DESC(default_quality,
		 "default entropy content of hwrng per mill");
T
Torsten Duwe 已提交
68 69

static void start_khwrngd(void);
70

71 72 73
static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
			       int wait);

74 75 76 77
static size_t rng_buffer_size(void)
{
	return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES;
}
78

79 80 81 82 83 84 85 86 87 88
static void add_early_randomness(struct hwrng *rng)
{
	unsigned char bytes[16];
	int bytes_read;

	bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1);
	if (bytes_read > 0)
		add_device_randomness(bytes, bytes_read);
}

89 90
static inline int hwrng_init(struct hwrng *rng)
{
91 92 93 94 95 96 97 98
	if (rng->init) {
		int ret;

		ret =  rng->init(rng);
		if (ret)
			return ret;
	}
	add_early_randomness(rng);
T
Torsten Duwe 已提交
99

100 101 102 103 104
	current_quality = rng->quality ? : default_quality;
	current_quality &= 1023;

	if (current_quality == 0 && hwrng_fill)
		kthread_stop(hwrng_fill);
T
Torsten Duwe 已提交
105 106 107
	if (current_quality > 0 && !hwrng_fill)
		start_khwrngd();

108
	return 0;
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
}

static inline void hwrng_cleanup(struct hwrng *rng)
{
	if (rng && rng->cleanup)
		rng->cleanup(rng);
}

static int rng_dev_open(struct inode *inode, struct file *filp)
{
	/* enforce read-only access to this chrdev */
	if ((filp->f_mode & FMODE_READ) == 0)
		return -EINVAL;
	if (filp->f_mode & FMODE_WRITE)
		return -EINVAL;
	return 0;
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
			int wait) {
	int present;

	if (rng->read)
		return rng->read(rng, (void *)buffer, size, wait);

	if (rng->data_present)
		present = rng->data_present(rng, wait);
	else
		present = 1;

	if (present)
		return rng->data_read(rng, (u32 *)buffer);

	return 0;
}

145 146 147 148
static ssize_t rng_dev_read(struct file *filp, char __user *buf,
			    size_t size, loff_t *offp)
{
	ssize_t ret = 0;
149
	int err = 0;
150
	int bytes_read, len;
151 152

	while (size) {
153 154
		if (mutex_lock_interruptible(&rng_mutex)) {
			err = -ERESTARTSYS;
155
			goto out;
156 157
		}

158 159
		if (!current_rng) {
			err = -ENODEV;
160
			goto out_unlock;
161
		}
162

163 164
		if (!data_avail) {
			bytes_read = rng_get_data(current_rng, rng_buffer,
165
				rng_buffer_size(),
166 167 168 169 170 171
				!(filp->f_flags & O_NONBLOCK));
			if (bytes_read < 0) {
				err = bytes_read;
				goto out_unlock;
			}
			data_avail = bytes_read;
172
		}
173

174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
		if (!data_avail) {
			if (filp->f_flags & O_NONBLOCK) {
				err = -EAGAIN;
				goto out_unlock;
			}
		} else {
			len = data_avail;
			if (len > size)
				len = size;

			data_avail -= len;

			if (copy_to_user(buf + ret, rng_buffer + data_avail,
								len)) {
				err = -EFAULT;
				goto out_unlock;
			}

			size -= len;
			ret += len;
194 195
		}

196 197
		mutex_unlock(&rng_mutex);

198 199
		if (need_resched())
			schedule_timeout_interruptible(1);
200 201 202

		if (signal_pending(current)) {
			err = -ERESTARTSYS;
203
			goto out;
204
		}
205 206 207
	}
out:
	return ret ? : err;
208 209 210
out_unlock:
	mutex_unlock(&rng_mutex);
	goto out;
211 212 213
}


214
static const struct file_operations rng_chrdev_ops = {
215 216 217
	.owner		= THIS_MODULE,
	.open		= rng_dev_open,
	.read		= rng_dev_read,
218
	.llseek		= noop_llseek,
219 220 221 222 223
};

static struct miscdevice rng_miscdev = {
	.minor		= RNG_MISCDEV_MINOR,
	.name		= RNG_MODULE_NAME,
224
	.nodename	= "hwrng",
225 226 227 228
	.fops		= &rng_chrdev_ops,
};


229 230
static ssize_t hwrng_attr_current_store(struct device *dev,
					struct device_attribute *attr,
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
					const char *buf, size_t len)
{
	int err;
	struct hwrng *rng;

	err = mutex_lock_interruptible(&rng_mutex);
	if (err)
		return -ERESTARTSYS;
	err = -ENODEV;
	list_for_each_entry(rng, &rng_list, list) {
		if (strcmp(rng->name, buf) == 0) {
			if (rng == current_rng) {
				err = 0;
				break;
			}
			err = hwrng_init(rng);
			if (err)
				break;
			hwrng_cleanup(current_rng);
			current_rng = rng;
			err = 0;
			break;
		}
	}
	mutex_unlock(&rng_mutex);

	return err ? : len;
}

260 261
static ssize_t hwrng_attr_current_show(struct device *dev,
				       struct device_attribute *attr,
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
				       char *buf)
{
	int err;
	ssize_t ret;
	const char *name = "none";

	err = mutex_lock_interruptible(&rng_mutex);
	if (err)
		return -ERESTARTSYS;
	if (current_rng)
		name = current_rng->name;
	ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
	mutex_unlock(&rng_mutex);

	return ret;
}

279 280
static ssize_t hwrng_attr_available_show(struct device *dev,
					 struct device_attribute *attr,
281 282 283 284 285 286 287 288 289 290
					 char *buf)
{
	int err;
	struct hwrng *rng;

	err = mutex_lock_interruptible(&rng_mutex);
	if (err)
		return -ERESTARTSYS;
	buf[0] = '\0';
	list_for_each_entry(rng, &rng_list, list) {
291 292
		strlcat(buf, rng->name, PAGE_SIZE);
		strlcat(buf, " ", PAGE_SIZE);
293
	}
294
	strlcat(buf, "\n", PAGE_SIZE);
295 296
	mutex_unlock(&rng_mutex);

297
	return strlen(buf);
298 299
}

300 301 302 303 304 305
static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR,
		   hwrng_attr_current_show,
		   hwrng_attr_current_store);
static DEVICE_ATTR(rng_available, S_IRUGO,
		   hwrng_attr_available_show,
		   NULL);
306 307


308
static void unregister_miscdev(void)
309
{
310 311
	device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available);
	device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
312
	misc_deregister(&rng_miscdev);
313 314 315 316 317 318 319 320 321
}

static int register_miscdev(void)
{
	int err;

	err = misc_register(&rng_miscdev);
	if (err)
		goto out;
322 323
	err = device_create_file(rng_miscdev.this_device,
				 &dev_attr_rng_current);
324 325
	if (err)
		goto err_misc_dereg;
326 327
	err = device_create_file(rng_miscdev.this_device,
				 &dev_attr_rng_available);
328 329 330 331 332 333
	if (err)
		goto err_remove_current;
out:
	return err;

err_remove_current:
334
	device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
335 336 337 338 339
err_misc_dereg:
	misc_deregister(&rng_miscdev);
	goto out;
}

T
Torsten Duwe 已提交
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
static int hwrng_fillfn(void *unused)
{
	long rc;

	while (!kthread_should_stop()) {
		if (!current_rng)
			break;
		rc = rng_get_data(current_rng, rng_fillbuf,
				  rng_buffer_size(), 1);
		if (rc <= 0) {
			pr_warn("hwrng: no data available\n");
			msleep_interruptible(10000);
			continue;
		}
		add_hwgenerator_randomness((void *)rng_fillbuf, rc,
355
					   rc * current_quality * 8 >> 10);
T
Torsten Duwe 已提交
356
	}
357
	hwrng_fill = NULL;
T
Torsten Duwe 已提交
358 359 360 361 362 363 364 365 366 367 368 369
	return 0;
}

static void start_khwrngd(void)
{
	hwrng_fill = kthread_run(hwrng_fillfn, NULL, "hwrng");
	if (hwrng_fill == ERR_PTR(-ENOMEM)) {
		pr_err("hwrng_fill thread creation failed");
		hwrng_fill = NULL;
	}
}

370 371 372 373 374 375
int hwrng_register(struct hwrng *rng)
{
	int err = -EINVAL;
	struct hwrng *old_rng, *tmp;

	if (rng->name == NULL ||
376
	    (rng->data_read == NULL && rng->read == NULL))
377 378 379 380
		goto out;

	mutex_lock(&rng_mutex);

381 382 383 384 385 386 387
	/* kmalloc makes this safe for virt_to_page() in virtio_rng.c */
	err = -ENOMEM;
	if (!rng_buffer) {
		rng_buffer = kmalloc(rng_buffer_size(), GFP_KERNEL);
		if (!rng_buffer)
			goto out_unlock;
	}
T
Torsten Duwe 已提交
388 389 390 391 392 393 394
	if (!rng_fillbuf) {
		rng_fillbuf = kmalloc(rng_buffer_size(), GFP_KERNEL);
		if (!rng_fillbuf) {
			kfree(rng_buffer);
			goto out_unlock;
		}
	}
395

396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
	/* Must not register two RNGs with the same name. */
	err = -EEXIST;
	list_for_each_entry(tmp, &rng_list, list) {
		if (strcmp(tmp->name, rng->name) == 0)
			goto out_unlock;
	}

	old_rng = current_rng;
	if (!old_rng) {
		err = hwrng_init(rng);
		if (err)
			goto out_unlock;
		current_rng = rng;
	}
	err = 0;
411
	if (!old_rng) {
412 413
		err = register_miscdev();
		if (err) {
414 415
			hwrng_cleanup(rng);
			current_rng = NULL;
416 417 418 419 420
			goto out_unlock;
		}
	}
	INIT_LIST_HEAD(&rng->list);
	list_add_tail(&rng->list, &rng_list);
421

422 423 424 425 426 427 428 429 430 431 432
	if (old_rng && !rng->init) {
		/*
		 * Use a new device's input to add some randomness to
		 * the system.  If this rng device isn't going to be
		 * used right away, its init function hasn't been
		 * called yet; so only use the randomness from devices
		 * that don't need an init callback.
		 */
		add_early_randomness(rng);
	}

433 434 435 436 437 438 439
out_unlock:
	mutex_unlock(&rng_mutex);
out:
	return err;
}
EXPORT_SYMBOL_GPL(hwrng_register);

440
void hwrng_unregister(struct hwrng *rng)
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
{
	int err;

	mutex_lock(&rng_mutex);

	list_del(&rng->list);
	if (current_rng == rng) {
		hwrng_cleanup(rng);
		if (list_empty(&rng_list)) {
			current_rng = NULL;
		} else {
			current_rng = list_entry(rng_list.prev, struct hwrng, list);
			err = hwrng_init(current_rng);
			if (err)
				current_rng = NULL;
		}
	}
T
Torsten Duwe 已提交
458
	if (list_empty(&rng_list)) {
459
		unregister_miscdev();
T
Torsten Duwe 已提交
460 461 462
		if (hwrng_fill)
			kthread_stop(hwrng_fill);
	}
463 464 465

	mutex_unlock(&rng_mutex);
}
466
EXPORT_SYMBOL_GPL(hwrng_unregister);
467

468 469 470 471 472
static void __exit hwrng_exit(void)
{
	mutex_lock(&rng_mutex);
	BUG_ON(current_rng);
	kfree(rng_buffer);
T
Torsten Duwe 已提交
473
	kfree(rng_fillbuf);
474 475 476 477
	mutex_unlock(&rng_mutex);
}

module_exit(hwrng_exit);
478 479 480

MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
MODULE_LICENSE("GPL");