162053Smarkm/*-
2256381Smarkm * Copyright (c) 2000-2013 Mark R V Murray
3254147Sobrien * Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
462053Smarkm * All rights reserved.
562053Smarkm *
662053Smarkm * Redistribution and use in source and binary forms, with or without
762053Smarkm * modification, are permitted provided that the following conditions
862053Smarkm * are met:
962053Smarkm * 1. Redistributions of source code must retain the above copyright
1062053Smarkm *    notice, this list of conditions and the following disclaimer
1162053Smarkm *    in this position and unchanged.
1262053Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1362053Smarkm *    notice, this list of conditions and the following disclaimer in the
1462053Smarkm *    documentation and/or other materials provided with the distribution.
1562053Smarkm *
1662053Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1762053Smarkm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1862053Smarkm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1962053Smarkm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2062053Smarkm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2162053Smarkm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2262053Smarkm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2362053Smarkm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2462053Smarkm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2562053Smarkm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2662053Smarkm *
2762053Smarkm */
2862053Smarkm
29119418Sobrien#include <sys/cdefs.h>
30119418Sobrien__FBSDID("$FreeBSD$");
31119418Sobrien
3262053Smarkm#include <sys/param.h>
3362053Smarkm#include <sys/systm.h>
3476166Smarkm#include <sys/bus.h>
3562053Smarkm#include <sys/conf.h>
3676166Smarkm#include <sys/fcntl.h>
3774771Smarkm#include <sys/filio.h>
3862053Smarkm#include <sys/kernel.h>
3974072Smarkm#include <sys/kthread.h>
4076166Smarkm#include <sys/lock.h>
4162053Smarkm#include <sys/malloc.h>
42129876Sphk#include <sys/module.h>
4376166Smarkm#include <sys/mutex.h>
4467112Smarkm#include <sys/poll.h>
45164033Srwatson#include <sys/priv.h>
4683366Sjulian#include <sys/proc.h>
47256381Smarkm#include <sys/random.h>
4870834Swollman#include <sys/selinfo.h>
4976166Smarkm#include <sys/uio.h>
5076166Smarkm#include <sys/unistd.h>
5174072Smarkm
5262053Smarkm#include <machine/bus.h>
5374072Smarkm#include <machine/cpu.h>
5462053Smarkm
55256381Smarkm#include <dev/random/randomdev.h>
56256381Smarkm#include <dev/random/randomdev_soft.h>
57255362Smarkm#include <dev/random/random_adaptors.h>
58256381Smarkm#include <dev/random/random_harvestq.h>
59256381Smarkm#include <dev/random/live_entropy_sources.h>
6062053Smarkm
61122871Smarkm#define RANDOM_MINOR	0
6262053Smarkm
63128059Smarkmstatic d_read_t random_read;
64128059Smarkmstatic d_write_t random_write;
65128059Smarkmstatic d_ioctl_t random_ioctl;
66128059Smarkmstatic d_poll_t random_poll;
67122871Smarkm
6862053Smarkmstatic struct cdevsw random_cdevsw = {
69128059Smarkm	.d_version = D_VERSION,
70128059Smarkm	.d_read = random_read,
71128059Smarkm	.d_write = random_write,
72128059Smarkm	.d_ioctl = random_ioctl,
73128059Smarkm	.d_poll = random_poll,
74128059Smarkm	.d_name = "random",
7562053Smarkm};
7662053Smarkm
7762053Smarkm/* For use with make_dev(9)/destroy_dev(9). */
78130585Sphkstatic struct cdev *random_dev;
7962053Smarkm
8091600Smarkm/* ARGSUSED */
8162053Smarkmstatic int
82130585Sphkrandom_read(struct cdev *dev __unused, struct uio *uio, int flag)
8362053Smarkm{
84128059Smarkm	int c, error = 0;
85128367Smarkm	void *random_buf;
8662053Smarkm
87128059Smarkm	/* Blocking logic */
88254147Sobrien	if (!random_adaptor->seeded)
89254147Sobrien		error = (*random_adaptor->block)(flag);
90128059Smarkm
91128059Smarkm	/* The actual read */
92128059Smarkm	if (!error) {
93128367Smarkm
94256381Smarkm		random_buf = (void *)malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
95128367Smarkm
96128059Smarkm		while (uio->uio_resid > 0 && !error) {
97128059Smarkm			c = MIN(uio->uio_resid, PAGE_SIZE);
98254147Sobrien			c = (*random_adaptor->read)(random_buf, c);
99128059Smarkm			error = uiomove(random_buf, c, uio);
100128059Smarkm		}
101256381Smarkm		/* Finished reading; let the source know so it can do some
102256381Smarkm		 * optional housekeeping */
103256381Smarkm		(*random_adaptor->read)(NULL, 0);
104128367Smarkm
105256381Smarkm		free(random_buf, M_ENTROPY);
106128367Smarkm
10767286Speter	}
108128151Smarkm
109128059Smarkm	return (error);
11062053Smarkm}
11162053Smarkm
11291600Smarkm/* ARGSUSED */
11362053Smarkmstatic int
114130585Sphkrandom_write(struct cdev *dev __unused, struct uio *uio, int flag __unused)
11562053Smarkm{
11662053Smarkm
117256381Smarkm	/* We used to allow this to insert userland entropy.
118256381Smarkm	 * We don't any more because (1) this so-called entropy
119256381Smarkm	 * is usually lousy and (b) its vaguely possible to
120256381Smarkm	 * mess with entropy harvesting by overdoing a write.
121256381Smarkm	 * Now we just ignore input like /dev/null does.
122256381Smarkm	 */
123256381Smarkm	uio->uio_resid = 0;
124128367Smarkm
125256381Smarkm	return (0);
12662053Smarkm}
12762053Smarkm
12891600Smarkm/* ARGSUSED */
12962053Smarkmstatic int
130130585Sphkrandom_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused,
13191600Smarkm    int flags __unused, struct thread *td __unused)
13265686Smarkm{
133128059Smarkm	int error = 0;
134128059Smarkm
13574771Smarkm	switch (cmd) {
136128059Smarkm		/* Really handled in upper layer */
13774771Smarkm	case FIOASYNC:
13874771Smarkm	case FIONBIO:
139128059Smarkm		break;
14074771Smarkm	default:
141128059Smarkm		error = ENOTTY;
14274771Smarkm	}
143128059Smarkm	return (error);
14465686Smarkm}
14565686Smarkm
14691600Smarkm/* ARGSUSED */
14765686Smarkmstatic int
148130585Sphkrandom_poll(struct cdev *dev __unused, int events, struct thread *td)
14967112Smarkm{
150128059Smarkm	int revents = 0;
15167112Smarkm
15267112Smarkm	if (events & (POLLIN | POLLRDNORM)) {
153254147Sobrien		if (random_adaptor->seeded)
15467112Smarkm			revents = events & (POLLIN | POLLRDNORM);
15567112Smarkm		else
156256381Smarkm			revents = (*random_adaptor->poll)(events, td);
15767112Smarkm	}
158128059Smarkm	return (revents);
15967112Smarkm}
16067112Smarkm
161254147Sobrienstatic void
162254147Sobrienrandom_initialize(void *p, struct random_adaptor *s)
163254147Sobrien{
164256381Smarkm	static int random_inited = 0;
165256381Smarkm
166254147Sobrien	if (random_inited) {
167254147Sobrien		printf("random: <%s> already initialized\n",
168254147Sobrien		    random_adaptor->ident);
169254147Sobrien		return;
170254147Sobrien	}
171254147Sobrien
172254147Sobrien	random_adaptor = s;
173254147Sobrien
174254147Sobrien	(s->init)();
175254147Sobrien
176254147Sobrien	printf("random: <%s> initialized\n", s->ident);
177254147Sobrien
178256381Smarkm	/* Use an appropriately evil mode for those who are concerned
179256381Smarkm	 * with daemons */
180254147Sobrien	random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw,
181254147Sobrien	    RANDOM_MINOR, NULL, UID_ROOT, GID_WHEEL, 0666, "random");
182256381Smarkm	make_dev_alias(random_dev, "urandom"); /* compatibility */
183254147Sobrien
184254147Sobrien	/* mark random(4) as initialized, to avoid being called again */
185254147Sobrien	random_inited = 1;
186254147Sobrien}
187254147Sobrien
18891600Smarkm/* ARGSUSED */
18967112Smarkmstatic int
19091600Smarkmrandom_modevent(module_t mod __unused, int type, void *data __unused)
19162053Smarkm{
192256381Smarkm	static eventhandler_tag attach_tag = NULL;
193128059Smarkm	int error = 0;
19465686Smarkm
195128059Smarkm	switch (type) {
19662053Smarkm	case MOD_LOAD:
197255362Smarkm		random_adaptor_choose(&random_adaptor);
19871037Smarkm
199254147Sobrien		if (random_adaptor == NULL) {
200256381Smarkm			printf("random: No random adaptor attached, "
201256381Smarkm			    "postponing initialization\n");
202254147Sobrien			attach_tag = EVENTHANDLER_REGISTER(random_adaptor_attach,
203254147Sobrien			    random_initialize, NULL, EVENTHANDLER_PRI_ANY);
204256381Smarkm		} else
205254147Sobrien			random_initialize(NULL, random_adaptor);
20671037Smarkm
207128059Smarkm		break;
20874072Smarkm
20962053Smarkm	case MOD_UNLOAD:
210254147Sobrien		if (random_adaptor != NULL) {
211254147Sobrien			(*random_adaptor->deinit)();
212254147Sobrien			destroy_dev(random_dev);
213254147Sobrien		}
214254147Sobrien		/* Unregister the event handler */
215256381Smarkm		if (attach_tag != NULL)
216254147Sobrien			EVENTHANDLER_DEREGISTER(random_adaptor_attach,
217254147Sobrien			    attach_tag);
21874072Smarkm
219128059Smarkm		break;
220122871Smarkm
22162053Smarkm	case MOD_SHUTDOWN:
222128059Smarkm		break;
22362053Smarkm
224132199Sphk	default:
225132199Sphk		error = EOPNOTSUPP;
226132199Sphk		break;
227132199Sphk
22862053Smarkm	}
229128059Smarkm	return (error);
23062053Smarkm}
23162053Smarkm
23262053SmarkmDEV_MODULE(random, random_modevent, NULL);
233133036SmarkmMODULE_VERSION(random, 1);
234