randomdev.c revision 128321
1254885Sdumbbell/*-
2254885Sdumbbell * Copyright (c) 2000-2004 Mark R V Murray
3254885Sdumbbell * All rights reserved.
4254885Sdumbbell *
5254885Sdumbbell * Redistribution and use in source and binary forms, with or without
6254885Sdumbbell * modification, are permitted provided that the following conditions
7254885Sdumbbell * are met:
8254885Sdumbbell * 1. Redistributions of source code must retain the above copyright
9254885Sdumbbell *    notice, this list of conditions and the following disclaimer
10254885Sdumbbell *    in this position and unchanged.
11254885Sdumbbell * 2. Redistributions in binary form must reproduce the above copyright
12254885Sdumbbell *    notice, this list of conditions and the following disclaimer in the
13254885Sdumbbell *    documentation and/or other materials provided with the distribution.
14254885Sdumbbell *
15254885Sdumbbell * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16254885Sdumbbell * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17254885Sdumbbell * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18254885Sdumbbell * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19254885Sdumbbell * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20254885Sdumbbell * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21254885Sdumbbell * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22254885Sdumbbell * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23254885Sdumbbell * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24254885Sdumbbell * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25254885Sdumbbell *
26254885Sdumbbell */
27254885Sdumbbell
28254885Sdumbbell#include <sys/cdefs.h>
29254885Sdumbbell__FBSDID("$FreeBSD: head/sys/dev/random/randomdev.c 128321 2004-04-16 17:10:54Z markm $");
30254885Sdumbbell
31254885Sdumbbell#include <sys/param.h>
32254885Sdumbbell#include <sys/systm.h>
33254885Sdumbbell#include <sys/bus.h>
34254885Sdumbbell#include <sys/conf.h>
35254885Sdumbbell#include <sys/fcntl.h>
36254885Sdumbbell#include <sys/filio.h>
37254885Sdumbbell#include <sys/kernel.h>
38254885Sdumbbell#include <sys/kthread.h>
39254885Sdumbbell#include <sys/lock.h>
40254885Sdumbbell#include <sys/malloc.h>
41254885Sdumbbell#include <sys/mutex.h>
42254885Sdumbbell#include <sys/poll.h>
43254885Sdumbbell#include <sys/proc.h>
44254885Sdumbbell#include <sys/selinfo.h>
45254885Sdumbbell#include <sys/uio.h>
46254885Sdumbbell#include <sys/unistd.h>
47254885Sdumbbell#include <sys/vnode.h>
48254885Sdumbbell
49254885Sdumbbell#include <machine/bus.h>
50254885Sdumbbell#include <machine/cpu.h>
51254885Sdumbbell
52254885Sdumbbell#include <dev/random/randomdev.h>
53254885Sdumbbell
54254885Sdumbbell#define RANDOM_MINOR	0
55254885Sdumbbell
56254885Sdumbbellstatic d_close_t random_close;
57254885Sdumbbellstatic d_read_t random_read;
58254885Sdumbbellstatic d_write_t random_write;
59254885Sdumbbellstatic d_ioctl_t random_ioctl;
60254885Sdumbbellstatic d_poll_t random_poll;
61254885Sdumbbell
62254885Sdumbbellstatic struct cdevsw random_cdevsw = {
63254885Sdumbbell	.d_version = D_VERSION,
64254885Sdumbbell	.d_flags = D_NEEDGIANT,
65254885Sdumbbell	.d_close = random_close,
66254885Sdumbbell	.d_read = random_read,
67254885Sdumbbell	.d_write = random_write,
68254885Sdumbbell	.d_ioctl = random_ioctl,
69254885Sdumbbell	.d_poll = random_poll,
70254885Sdumbbell	.d_name = "random",
71254885Sdumbbell};
72254885Sdumbbell
73254885Sdumbbellstatic void *random_buf;
74254885Sdumbbell
75254885Sdumbbellstruct random_systat random_systat;
76254885Sdumbbell
77254885Sdumbbell/* For use with make_dev(9)/destroy_dev(9). */
78254885Sdumbbellstatic dev_t random_dev;
79254885Sdumbbell
80254885Sdumbbell/* Used to fake out unused random calls in random_systat */
81254885Sdumbbellvoid
82254885Sdumbbellrandom_null_func(void)
83254885Sdumbbell{
84254885Sdumbbell}
85254885Sdumbbell
86254885Sdumbbell/* ARGSUSED */
87254885Sdumbbellstatic int
88254885Sdumbbellrandom_close(dev_t dev __unused, int flags, int fmt __unused,
89254885Sdumbbell    struct thread *td)
90254885Sdumbbell{
91254885Sdumbbell	if ((flags & FWRITE) && (suser(td) == 0)
92254885Sdumbbell	    && (securelevel_gt(td->td_ucred, 0) == 0)) {
93254885Sdumbbell		(*random_systat.reseed)();
94254885Sdumbbell		random_systat.seeded = 1;
95254885Sdumbbell	}
96254885Sdumbbell
97254885Sdumbbell	return (0);
98254885Sdumbbell}
99254885Sdumbbell
100254885Sdumbbell/* ARGSUSED */
101254885Sdumbbellstatic int
102254885Sdumbbellrandom_read(dev_t dev __unused, struct uio *uio, int flag)
103254885Sdumbbell{
104254885Sdumbbell	int c, error = 0;
105254885Sdumbbell
106254885Sdumbbell	/* Blocking logic */
107254885Sdumbbell	while (!random_systat.seeded && !error) {
108254885Sdumbbell		if (flag & IO_NDELAY)
109254885Sdumbbell			error = EWOULDBLOCK;
110254885Sdumbbell		else {
111254885Sdumbbell			/* No complaints please. This is temporary! */
112254885Sdumbbell			printf("Entropy device is blocking. "
113254885Sdumbbell			    "Dance fandango on keyboard to unblock.\n");
114254885Sdumbbell			error = tsleep(&random_systat,
115254885Sdumbbell			    PUSER | PCATCH, "block", 0);
116254885Sdumbbell		}
117254885Sdumbbell	}
118254885Sdumbbell
119254885Sdumbbell	/* The actual read */
120254885Sdumbbell	if (!error) {
121254885Sdumbbell		while (uio->uio_resid > 0 && !error) {
122254885Sdumbbell			c = MIN(uio->uio_resid, PAGE_SIZE);
123254885Sdumbbell			c = (*random_systat.read)(random_buf, c);
124254885Sdumbbell			error = uiomove(random_buf, c, uio);
125254885Sdumbbell		}
126254885Sdumbbell	}
127254885Sdumbbell
128254885Sdumbbell	return (error);
129254885Sdumbbell}
130254885Sdumbbell
131254885Sdumbbell/* ARGSUSED */
132254885Sdumbbellstatic int
133254885Sdumbbellrandom_write(dev_t dev __unused, struct uio *uio, int flag __unused)
134254885Sdumbbell{
135254885Sdumbbell	int c, error = 0;
136254885Sdumbbell
137254885Sdumbbell	while (uio->uio_resid > 0) {
138254885Sdumbbell		c = MIN((int)uio->uio_resid, PAGE_SIZE);
139254885Sdumbbell		error = uiomove(random_buf, c, uio);
140254885Sdumbbell		if (error)
141254885Sdumbbell			break;
142254885Sdumbbell		(*random_systat.write)(random_buf, c);
143254885Sdumbbell	}
144254885Sdumbbell
145254885Sdumbbell	return (error);
146254885Sdumbbell}
147254885Sdumbbell
148254885Sdumbbell/* ARGSUSED */
149254885Sdumbbellstatic int
150254885Sdumbbellrandom_ioctl(dev_t dev __unused, u_long cmd, caddr_t addr __unused,
151254885Sdumbbell    int flags __unused, struct thread *td __unused)
152254885Sdumbbell{
153254885Sdumbbell	int error = 0;
154254885Sdumbbell
155254885Sdumbbell	switch (cmd) {
156254885Sdumbbell		/* Really handled in upper layer */
157254885Sdumbbell	case FIOASYNC:
158254885Sdumbbell	case FIONBIO:
159254885Sdumbbell		break;
160254885Sdumbbell	default:
161254885Sdumbbell		error = ENOTTY;
162254885Sdumbbell	}
163254885Sdumbbell	return (error);
164254885Sdumbbell}
165254885Sdumbbell
166254885Sdumbbell/* ARGSUSED */
167254885Sdumbbellstatic int
168254885Sdumbbellrandom_poll(dev_t dev __unused, int events, struct thread *td)
169254885Sdumbbell{
170254885Sdumbbell	int revents = 0;
171254885Sdumbbell
172254885Sdumbbell	if (events & (POLLIN | POLLRDNORM)) {
173254885Sdumbbell		if (random_systat.seeded)
174254885Sdumbbell			revents = events & (POLLIN | POLLRDNORM);
175254885Sdumbbell		else
176254885Sdumbbell			selrecord(td, &random_systat.rsel);
177254885Sdumbbell	}
178254885Sdumbbell	return (revents);
179254885Sdumbbell}
180254885Sdumbbell
181254885Sdumbbell/* ARGSUSED */
182254885Sdumbbellstatic int
183254885Sdumbbellrandom_modevent(module_t mod __unused, int type, void *data __unused)
184254885Sdumbbell{
185254885Sdumbbell	int error = 0;
186254885Sdumbbell
187254885Sdumbbell	switch (type) {
188254885Sdumbbell	case MOD_LOAD:
189254885Sdumbbell		random_buf = (void *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
190254885Sdumbbell		random_ident_hardware(&random_systat);
191254885Sdumbbell		(*random_systat.init)();
192254885Sdumbbell
193254885Sdumbbell		printf("random: <entropy source, %s>\n", random_systat.ident);
194254885Sdumbbell
195254885Sdumbbell		random_dev = make_dev(&random_cdevsw, RANDOM_MINOR,
196254885Sdumbbell		    UID_ROOT, GID_WHEEL, 0666, "random");
197254885Sdumbbell		make_dev_alias(random_dev, "urandom");	/* XXX Deprecated */
198254885Sdumbbell
199254885Sdumbbell		break;
200254885Sdumbbell
201254885Sdumbbell	case MOD_UNLOAD:
202254885Sdumbbell		(*random_systat.deinit)();
203254885Sdumbbell		free(random_buf, M_TEMP);
204254885Sdumbbell
205254885Sdumbbell		destroy_dev(random_dev);
206254885Sdumbbell
207254885Sdumbbell		break;
208254885Sdumbbell
209254885Sdumbbell	case MOD_SHUTDOWN:
210254885Sdumbbell		break;
211254885Sdumbbell
212254885Sdumbbell	}
213254885Sdumbbell	return (error);
214254885Sdumbbell}
215254885Sdumbbell
216254885SdumbbellDEV_MODULE(random, random_modevent, NULL);
217254885Sdumbbell