xref: /freebsd/sys/amd64/amd64/mem.c (revision d4af7a50dcfa42f11a65e43f78481f064310a566)
15b81b6b3SRodney W. Grimes /*-
25b81b6b3SRodney W. Grimes  * Copyright (c) 1988 University of Utah.
35b81b6b3SRodney W. Grimes  * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
45b81b6b3SRodney W. Grimes  * All rights reserved.
55b81b6b3SRodney W. Grimes  *
65b81b6b3SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
75b81b6b3SRodney W. Grimes  * the Systems Programming Group of the University of Utah Computer
85b81b6b3SRodney W. Grimes  * Science Department, and code derived from software contributed to
95b81b6b3SRodney W. Grimes  * Berkeley by William Jolitz.
105b81b6b3SRodney W. Grimes  *
115b81b6b3SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
125b81b6b3SRodney W. Grimes  * modification, are permitted provided that the following conditions
135b81b6b3SRodney W. Grimes  * are met:
145b81b6b3SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
155b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
165b81b6b3SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
175b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
185b81b6b3SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
195b81b6b3SRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
205b81b6b3SRodney W. Grimes  *    must display the following acknowledgement:
215b81b6b3SRodney W. Grimes  *	This product includes software developed by the University of
225b81b6b3SRodney W. Grimes  *	California, Berkeley and its contributors.
235b81b6b3SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
245b81b6b3SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
255b81b6b3SRodney W. Grimes  *    without specific prior written permission.
265b81b6b3SRodney W. Grimes  *
275b81b6b3SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
285b81b6b3SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
295b81b6b3SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
305b81b6b3SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
315b81b6b3SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
325b81b6b3SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
335b81b6b3SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
345b81b6b3SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
355b81b6b3SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
365b81b6b3SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
375b81b6b3SRodney W. Grimes  * SUCH DAMAGE.
385b81b6b3SRodney W. Grimes  *
395b81b6b3SRodney W. Grimes  *	from: Utah $Hdr: mem.c 1.13 89/10/08$
4047cacd38SRodney W. Grimes  *	from: @(#)mem.c	7.2 (Berkeley) 5/9/91
41c3aac50fSPeter Wemm  * $FreeBSD$
425b81b6b3SRodney W. Grimes  */
435b81b6b3SRodney W. Grimes 
445b81b6b3SRodney W. Grimes /*
455b81b6b3SRodney W. Grimes  * Memory special file
465b81b6b3SRodney W. Grimes  */
475b81b6b3SRodney W. Grimes 
4826f9a767SRodney W. Grimes #include <sys/param.h>
4926f9a767SRodney W. Grimes #include <sys/conf.h>
50265cdeddSBruce Evans #include <sys/fcntl.h>
514ffd949eSMike Smith #include <sys/ioccom.h>
52d4af7a50SDavid E. O'Brien #include <sys/kernel.h>
5326f9a767SRodney W. Grimes #include <sys/malloc.h>
544ffd949eSMike Smith #include <sys/memrange.h>
5526f9a767SRodney W. Grimes #include <sys/proc.h>
56b0e56cdeSDavid E. O'Brien #include <sys/random.h>
57bb25f0ddSBruce Evans #include <sys/signalvar.h>
58d4af7a50SDavid E. O'Brien #include <sys/systm.h>
59d4af7a50SDavid E. O'Brien #include <sys/uio.h>
605b81b6b3SRodney W. Grimes 
61d1d9d260SBruce Evans #include <machine/frame.h>
6226f9a767SRodney W. Grimes #include <machine/psl.h>
634ffd949eSMike Smith #include <machine/specialreg.h>
6468352337SDoug Rabson #include <i386/isa/intr_machdep.h>
655b81b6b3SRodney W. Grimes 
66c87801feSDavid Greenman #include <vm/vm.h>
6726f9a767SRodney W. Grimes #include <vm/pmap.h>
68efeaf95aSDavid Greenman #include <vm/vm_extern.h>
695b81b6b3SRodney W. Grimes 
7087f6c662SJulian Elischer 
7187f6c662SJulian Elischer static	d_open_t	mmopen;
7287f6c662SJulian Elischer static	d_close_t	mmclose;
73c73feca0SBruce Evans static	d_read_t	mmrw;
7487f6c662SJulian Elischer static	d_ioctl_t	mmioctl;
7587f6c662SJulian Elischer static	d_mmap_t	memmmap;
7635b8b2ddSPeter Wemm static	d_poll_t	mmpoll;
7787f6c662SJulian Elischer 
7853ac6efbSJulian Elischer #define CDEV_MAJOR 2
794e2f199eSPoul-Henning Kamp static struct cdevsw mem_cdevsw = {
804e2f199eSPoul-Henning Kamp 	/* open */	mmopen,
814e2f199eSPoul-Henning Kamp 	/* close */	mmclose,
824e2f199eSPoul-Henning Kamp 	/* read */	mmrw,
834e2f199eSPoul-Henning Kamp 	/* write */	mmrw,
844e2f199eSPoul-Henning Kamp 	/* ioctl */	mmioctl,
854e2f199eSPoul-Henning Kamp 	/* poll */	mmpoll,
864e2f199eSPoul-Henning Kamp 	/* mmap */	memmmap,
874e2f199eSPoul-Henning Kamp 	/* strategy */	nostrategy,
884e2f199eSPoul-Henning Kamp 	/* name */	"mem",
894e2f199eSPoul-Henning Kamp 	/* maj */	CDEV_MAJOR,
904e2f199eSPoul-Henning Kamp 	/* dump */	nodump,
914e2f199eSPoul-Henning Kamp 	/* psize */	nopsize,
9249c68457SBrian Feldman 	/* flags */	D_MEM,
934e2f199eSPoul-Henning Kamp 	/* bmaj */	-1
944e2f199eSPoul-Henning Kamp };
9553ac6efbSJulian Elischer 
96316bbd5cSBruce Evans static struct random_softc random_softc[16];
9720073b6dSNate Williams static caddr_t	zbuf;
9820073b6dSNate Williams 
994ffd949eSMike Smith MALLOC_DEFINE(M_MEMDESC, "memdesc", "memory range descriptors");
1004ffd949eSMike Smith static int mem_ioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
1014ffd949eSMike Smith static int random_ioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
1024ffd949eSMike Smith 
1034ffd949eSMike Smith struct mem_range_softc mem_range_softc;
1044ffd949eSMike Smith 
1058af5d536SJulian Elischer 
10687f6c662SJulian Elischer static int
10760039670SBruce Evans mmclose(dev, flags, fmt, p)
10878d172caSRodney W. Grimes 	dev_t dev;
10978d172caSRodney W. Grimes 	int flags;
11060039670SBruce Evans 	int fmt;
11160039670SBruce Evans 	struct proc *p;
11278d172caSRodney W. Grimes {
11378d172caSRodney W. Grimes 	switch (minor(dev)) {
11478d172caSRodney W. Grimes 	case 14:
115265cdeddSBruce Evans 		p->p_md.md_regs->tf_eflags &= ~PSL_IOPL;
11678d172caSRodney W. Grimes 		break;
11778d172caSRodney W. Grimes 	default:
11878d172caSRodney W. Grimes 		break;
11978d172caSRodney W. Grimes 	}
12078d172caSRodney W. Grimes 	return (0);
12178d172caSRodney W. Grimes }
12233f538b9SBruce Evans 
12387f6c662SJulian Elischer static int
12460039670SBruce Evans mmopen(dev, flags, fmt, p)
12578d172caSRodney W. Grimes 	dev_t dev;
12678d172caSRodney W. Grimes 	int flags;
12760039670SBruce Evans 	int fmt;
12860039670SBruce Evans 	struct proc *p;
12978d172caSRodney W. Grimes {
130da3df630SBruce Evans 	int error;
13178d172caSRodney W. Grimes 
13278d172caSRodney W. Grimes 	switch (minor(dev)) {
13318284c94SPoul-Henning Kamp 	case 0:
13418284c94SPoul-Henning Kamp 	case 1:
135265cdeddSBruce Evans 		if ((flags & FWRITE) && securelevel > 0)
13618284c94SPoul-Henning Kamp 			return (EPERM);
13718284c94SPoul-Henning Kamp 		break;
13878d172caSRodney W. Grimes 	case 14:
139f711d546SPoul-Henning Kamp 		error = suser(p);
140da3df630SBruce Evans 		if (error != 0)
141da3df630SBruce Evans 			return (error);
142da3df630SBruce Evans 		if (securelevel > 0)
143da3df630SBruce Evans 			return (EPERM);
144265cdeddSBruce Evans 		p->p_md.md_regs->tf_eflags |= PSL_IOPL;
14578d172caSRodney W. Grimes 		break;
14678d172caSRodney W. Grimes 	default:
14778d172caSRodney W. Grimes 		break;
14878d172caSRodney W. Grimes 	}
14978d172caSRodney W. Grimes 	return (0);
15078d172caSRodney W. Grimes }
15133f538b9SBruce Evans 
15287f6c662SJulian Elischer static int
1535b81b6b3SRodney W. Grimes mmrw(dev, uio, flags)
1545b81b6b3SRodney W. Grimes 	dev_t dev;
1555b81b6b3SRodney W. Grimes 	struct uio *uio;
1565b81b6b3SRodney W. Grimes 	int flags;
1575b81b6b3SRodney W. Grimes {
1585b81b6b3SRodney W. Grimes 	register int o;
1595b81b6b3SRodney W. Grimes 	register u_int c, v;
1601bb2d314SMark Murray 	u_int poolsize;
1615b81b6b3SRodney W. Grimes 	register struct iovec *iov;
1625b81b6b3SRodney W. Grimes 	int error = 0;
1631bb2d314SMark Murray 	caddr_t buf = NULL;
1645b81b6b3SRodney W. Grimes 
1655b81b6b3SRodney W. Grimes 	while (uio->uio_resid > 0 && error == 0) {
1665b81b6b3SRodney W. Grimes 		iov = uio->uio_iov;
1675b81b6b3SRodney W. Grimes 		if (iov->iov_len == 0) {
1685b81b6b3SRodney W. Grimes 			uio->uio_iov++;
1695b81b6b3SRodney W. Grimes 			uio->uio_iovcnt--;
1705b81b6b3SRodney W. Grimes 			if (uio->uio_iovcnt < 0)
1715b81b6b3SRodney W. Grimes 				panic("mmrw");
1725b81b6b3SRodney W. Grimes 			continue;
1735b81b6b3SRodney W. Grimes 		}
1745b81b6b3SRodney W. Grimes 		switch (minor(dev)) {
1755b81b6b3SRodney W. Grimes 
1765b81b6b3SRodney W. Grimes /* minor device 0 is physical memory */
1775b81b6b3SRodney W. Grimes 		case 0:
1785b81b6b3SRodney W. Grimes 			v = uio->uio_offset;
1790385347cSPeter Wemm 			pmap_kenter((vm_offset_t)ptvmmap, v);
180f8845af0SPoul-Henning Kamp 			o = (int)uio->uio_offset & PAGE_MASK;
181f8845af0SPoul-Henning Kamp 			c = (u_int)(PAGE_SIZE - ((int)iov->iov_base & PAGE_MASK));
182f8845af0SPoul-Henning Kamp 			c = min(c, (u_int)(PAGE_SIZE - o));
18326f9a767SRodney W. Grimes 			c = min(c, (u_int)iov->iov_len);
18426f9a767SRodney W. Grimes 			error = uiomove((caddr_t)&ptvmmap[o], (int)c, uio);
1850385347cSPeter Wemm 			pmap_kremove((vm_offset_t)ptvmmap);
1865b81b6b3SRodney W. Grimes 			continue;
1875b81b6b3SRodney W. Grimes 
1885b81b6b3SRodney W. Grimes /* minor device 1 is kernel memory */
18997e11262SDavid Greenman 		case 1: {
19097e11262SDavid Greenman 			vm_offset_t addr, eaddr;
1915b81b6b3SRodney W. Grimes 			c = iov->iov_len;
19297e11262SDavid Greenman 
19397e11262SDavid Greenman 			/*
19497e11262SDavid Greenman 			 * Make sure that all of the pages are currently resident so
19597e11262SDavid Greenman 			 * that we don't create any zero-fill pages.
19697e11262SDavid Greenman 			 */
19797e11262SDavid Greenman 			addr = trunc_page(uio->uio_offset);
1980704324aSDavid Greenman 			eaddr = round_page(uio->uio_offset + c);
199d021fc11SPeter Wemm 
200d021fc11SPeter Wemm 			if (addr < (vm_offset_t)VADDR(PTDPTDI, 0))
201d021fc11SPeter Wemm 				return EFAULT;
202d021fc11SPeter Wemm 			if (eaddr >= (vm_offset_t)VADDR(APTDPTDI, 0))
203d021fc11SPeter Wemm 				return EFAULT;
20497e11262SDavid Greenman 			for (; addr < eaddr; addr += PAGE_SIZE)
20597e11262SDavid Greenman 				if (pmap_extract(kernel_pmap, addr) == 0)
20697e11262SDavid Greenman 					return EFAULT;
20797e11262SDavid Greenman 
20826f9a767SRodney W. Grimes 			if (!kernacc((caddr_t)(int)uio->uio_offset, c,
20902c58685SPoul-Henning Kamp 			    uio->uio_rw == UIO_READ ?
21002c58685SPoul-Henning Kamp 			    VM_PROT_READ : VM_PROT_WRITE))
2115b81b6b3SRodney W. Grimes 				return (EFAULT);
21226f9a767SRodney W. Grimes 			error = uiomove((caddr_t)(int)uio->uio_offset, (int)c, uio);
2135b81b6b3SRodney W. Grimes 			continue;
21497e11262SDavid Greenman 		}
2155b81b6b3SRodney W. Grimes 
2165b81b6b3SRodney W. Grimes /* minor device 2 is EOF/RATHOLE */
2175b81b6b3SRodney W. Grimes 		case 2:
2185b81b6b3SRodney W. Grimes 			if (uio->uio_rw == UIO_READ)
2195b81b6b3SRodney W. Grimes 				return (0);
2205b81b6b3SRodney W. Grimes 			c = iov->iov_len;
2215b81b6b3SRodney W. Grimes 			break;
2225b81b6b3SRodney W. Grimes 
2231bb2d314SMark Murray /* minor device 3 (/dev/random) is source of filth on read, rathole on write */
2241bb2d314SMark Murray 		case 3:
2251bb2d314SMark Murray 			if (uio->uio_rw == UIO_WRITE) {
2261bb2d314SMark Murray 				c = iov->iov_len;
2271bb2d314SMark Murray 				break;
2281bb2d314SMark Murray 			}
2291bb2d314SMark Murray 			if (buf == NULL)
2301bb2d314SMark Murray 				buf = (caddr_t)
231f8845af0SPoul-Henning Kamp 				    malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
232f8845af0SPoul-Henning Kamp 			c = min(iov->iov_len, PAGE_SIZE);
2331bb2d314SMark Murray 			poolsize = read_random(buf, c);
2341bb2d314SMark Murray 			if (poolsize == 0) {
2351bb2d314SMark Murray 				if (buf)
2361bb2d314SMark Murray 					free(buf, M_TEMP);
2371bb2d314SMark Murray 				return (0);
2381bb2d314SMark Murray 			}
2391bb2d314SMark Murray 			c = min(c, poolsize);
2401bb2d314SMark Murray 			error = uiomove(buf, (int)c, uio);
2411bb2d314SMark Murray 			continue;
2421bb2d314SMark Murray 
2431bb2d314SMark Murray /* minor device 4 (/dev/urandom) is source of muck on read, rathole on write */
2441bb2d314SMark Murray 		case 4:
2451bb2d314SMark Murray 			if (uio->uio_rw == UIO_WRITE) {
2461bb2d314SMark Murray 				c = iov->iov_len;
2471bb2d314SMark Murray 				break;
2481bb2d314SMark Murray 			}
249bb25f0ddSBruce Evans 			if (CURSIG(curproc) != 0) {
250bb25f0ddSBruce Evans 				/*
251bb25f0ddSBruce Evans 				 * Use tsleep() to get the error code right.
252bb25f0ddSBruce Evans 				 * It should return immediately.
253bb25f0ddSBruce Evans 				 */
254bb25f0ddSBruce Evans 				error = tsleep(&random_softc[0],
255bb25f0ddSBruce Evans 				    PZERO | PCATCH, "urand", 1);
256bb25f0ddSBruce Evans 				if (error != 0 && error != EWOULDBLOCK)
257bb25f0ddSBruce Evans 					continue;
258bb25f0ddSBruce Evans 			}
2591bb2d314SMark Murray 			if (buf == NULL)
2601bb2d314SMark Murray 				buf = (caddr_t)
261f8845af0SPoul-Henning Kamp 				    malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
262f8845af0SPoul-Henning Kamp 			c = min(iov->iov_len, PAGE_SIZE);
2631bb2d314SMark Murray 			poolsize = read_random_unlimited(buf, c);
2641bb2d314SMark Murray 			c = min(c, poolsize);
2651bb2d314SMark Murray 			error = uiomove(buf, (int)c, uio);
2661bb2d314SMark Murray 			continue;
2671bb2d314SMark Murray 
2685b81b6b3SRodney W. Grimes /* minor device 12 (/dev/zero) is source of nulls on read, rathole on write */
2695b81b6b3SRodney W. Grimes 		case 12:
2705b81b6b3SRodney W. Grimes 			if (uio->uio_rw == UIO_WRITE) {
2715b81b6b3SRodney W. Grimes 				c = iov->iov_len;
2725b81b6b3SRodney W. Grimes 				break;
2735b81b6b3SRodney W. Grimes 			}
274f381a0c0SJohn Dyson 			if (zbuf == NULL) {
275f381a0c0SJohn Dyson 				zbuf = (caddr_t)
276f8845af0SPoul-Henning Kamp 				    malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
277f381a0c0SJohn Dyson 				bzero(zbuf, PAGE_SIZE);
2785b81b6b3SRodney W. Grimes 			}
279f8845af0SPoul-Henning Kamp 			c = min(iov->iov_len, PAGE_SIZE);
280f381a0c0SJohn Dyson 			error = uiomove(zbuf, (int)c, uio);
2815b81b6b3SRodney W. Grimes 			continue;
2825b81b6b3SRodney W. Grimes 
2835b81b6b3SRodney W. Grimes 		default:
2845b81b6b3SRodney W. Grimes 			return (ENXIO);
2855b81b6b3SRodney W. Grimes 		}
2865b81b6b3SRodney W. Grimes 		if (error)
2875b81b6b3SRodney W. Grimes 			break;
2885b81b6b3SRodney W. Grimes 		iov->iov_base += c;
2895b81b6b3SRodney W. Grimes 		iov->iov_len -= c;
2905b81b6b3SRodney W. Grimes 		uio->uio_offset += c;
2915b81b6b3SRodney W. Grimes 		uio->uio_resid -= c;
2925b81b6b3SRodney W. Grimes 	}
2931bb2d314SMark Murray 	if (buf)
2941bb2d314SMark Murray 		free(buf, M_TEMP);
2955b81b6b3SRodney W. Grimes 	return (error);
2965b81b6b3SRodney W. Grimes }
297b513c262SDavid Greenman 
298b513c262SDavid Greenman 
299b513c262SDavid Greenman 
300b513c262SDavid Greenman 
301b513c262SDavid Greenman /*******************************************************\
302b513c262SDavid Greenman * allow user processes to MMAP some memory sections	*
303b513c262SDavid Greenman * instead of going through read/write			*
304b513c262SDavid Greenman \*******************************************************/
30587f6c662SJulian Elischer static int
3067095ee91SDoug Rabson memmmap(dev_t dev, vm_offset_t offset, int nprot)
307b513c262SDavid Greenman {
308b513c262SDavid Greenman 	switch (minor(dev))
309b513c262SDavid Greenman 	{
310b513c262SDavid Greenman 
311b513c262SDavid Greenman /* minor device 0 is physical memory */
312b513c262SDavid Greenman 	case 0:
313b513c262SDavid Greenman         	return i386_btop(offset);
314b513c262SDavid Greenman 
315b513c262SDavid Greenman /* minor device 1 is kernel memory */
316b513c262SDavid Greenman 	case 1:
317b513c262SDavid Greenman         	return i386_btop(vtophys(offset));
318b513c262SDavid Greenman 
319b513c262SDavid Greenman 	default:
320b513c262SDavid Greenman 		return -1;
321b513c262SDavid Greenman 	}
322b513c262SDavid Greenman }
323b513c262SDavid Greenman 
32487f6c662SJulian Elischer static int
325316bbd5cSBruce Evans mmioctl(dev, cmd, data, flags, p)
32600f7f6beSBruce Evans 	dev_t dev;
327ecbb00a2SDoug Rabson 	u_long cmd;
328316bbd5cSBruce Evans 	caddr_t data;
32900f7f6beSBruce Evans 	int flags;
33000f7f6beSBruce Evans 	struct proc *p;
3311bb2d314SMark Murray {
332a0135d7eSMark Murray 
333d69e8502SGarrett Wollman 	switch (minor(dev)) {
3344ffd949eSMike Smith 	case 0:
3354ffd949eSMike Smith 		return mem_ioctl(dev, cmd, data, flags, p);
336d69e8502SGarrett Wollman 	case 3:
337d69e8502SGarrett Wollman 	case 4:
3384ffd949eSMike Smith 		return random_ioctl(dev, cmd, data, flags, p);
3394ffd949eSMike Smith 	}
340316bbd5cSBruce Evans 	return (ENODEV);
341d69e8502SGarrett Wollman }
3421bb2d314SMark Murray 
343316bbd5cSBruce Evans /*
3444ffd949eSMike Smith  * Operations for changing memory attributes.
3454ffd949eSMike Smith  *
3464ffd949eSMike Smith  * This is basically just an ioctl shim for mem_range_attr_get
3474ffd949eSMike Smith  * and mem_range_attr_set.
3484ffd949eSMike Smith  */
3494ffd949eSMike Smith static int
3504ffd949eSMike Smith mem_ioctl(dev, cmd, data, flags, p)
3514ffd949eSMike Smith 	dev_t dev;
3524ffd949eSMike Smith 	u_long cmd;
3534ffd949eSMike Smith 	caddr_t data;
3544ffd949eSMike Smith 	int flags;
3554ffd949eSMike Smith 	struct proc *p;
3564ffd949eSMike Smith {
3574ffd949eSMike Smith 	int nd, error = 0;
3584ffd949eSMike Smith 	struct mem_range_op *mo = (struct mem_range_op *)data;
3594ffd949eSMike Smith 	struct mem_range_desc *md;
3604ffd949eSMike Smith 
3614ffd949eSMike Smith 	/* is this for us? */
3624ffd949eSMike Smith 	if ((cmd != MEMRANGE_GET) &&
3634ffd949eSMike Smith 	    (cmd != MEMRANGE_SET))
3644871de2fSBrian Feldman 		return (ENOTTY);
3654ffd949eSMike Smith 
3664ffd949eSMike Smith 	/* any chance we can handle this? */
3674ffd949eSMike Smith 	if (mem_range_softc.mr_op == NULL)
3684ffd949eSMike Smith 		return (EOPNOTSUPP);
3694ffd949eSMike Smith 
3704ffd949eSMike Smith 	/* do we have any descriptors? */
3714ffd949eSMike Smith 	if (mem_range_softc.mr_ndesc == 0)
3724ffd949eSMike Smith 		return (ENXIO);
3734ffd949eSMike Smith 
3744ffd949eSMike Smith 	switch (cmd) {
3754ffd949eSMike Smith 	case MEMRANGE_GET:
3764ffd949eSMike Smith 		nd = imin(mo->mo_arg[0], mem_range_softc.mr_ndesc);
3774ffd949eSMike Smith 		if (nd > 0) {
3784ffd949eSMike Smith 			md = (struct mem_range_desc *)
3794ffd949eSMike Smith 				malloc(nd * sizeof(struct mem_range_desc),
3804ffd949eSMike Smith 				       M_MEMDESC, M_WAITOK);
381ac3595b0SMike Smith 			error = mem_range_attr_get(md, &nd);
382ac3595b0SMike Smith 			if (!error)
3834ffd949eSMike Smith 				error = copyout(md, mo->mo_desc,
3844ffd949eSMike Smith 					nd * sizeof(struct mem_range_desc));
3854ffd949eSMike Smith 			free(md, M_MEMDESC);
3864ffd949eSMike Smith 		} else {
3874ffd949eSMike Smith 			nd = mem_range_softc.mr_ndesc;
3884ffd949eSMike Smith 		}
3894ffd949eSMike Smith 		mo->mo_arg[0] = nd;
3904ffd949eSMike Smith 		break;
3914ffd949eSMike Smith 
3924ffd949eSMike Smith 	case MEMRANGE_SET:
3934ffd949eSMike Smith 		md = (struct mem_range_desc *)malloc(sizeof(struct mem_range_desc),
3944ffd949eSMike Smith 						    M_MEMDESC, M_WAITOK);
3954ffd949eSMike Smith 		error = copyin(mo->mo_desc, md, sizeof(struct mem_range_desc));
3964ffd949eSMike Smith 		/* clamp description string */
3974ffd949eSMike Smith 		md->mr_owner[sizeof(md->mr_owner) - 1] = 0;
3984ffd949eSMike Smith 		if (error == 0)
3994ffd949eSMike Smith 			error = mem_range_attr_set(md, &mo->mo_arg[0]);
4004ffd949eSMike Smith 		free(md, M_MEMDESC);
4014ffd949eSMike Smith 		break;
4024ffd949eSMike Smith 	}
4034ffd949eSMike Smith 	return (error);
4044ffd949eSMike Smith }
4054ffd949eSMike Smith 
4064ffd949eSMike Smith /*
4074ffd949eSMike Smith  * Implementation-neutral, kernel-callable functions for manipulating
4084ffd949eSMike Smith  * memory range attributes.
4094ffd949eSMike Smith  */
410ac3595b0SMike Smith int
4114af396a5SMike Smith mem_range_attr_get(mrd, arg)
4124af396a5SMike Smith 	struct mem_range_desc *mrd;
4134af396a5SMike Smith 	int *arg;
4144ffd949eSMike Smith {
4154af396a5SMike Smith 	/* can we handle this? */
4164af396a5SMike Smith 	if (mem_range_softc.mr_op == NULL)
4174af396a5SMike Smith 		return (EOPNOTSUPP);
4184af396a5SMike Smith 
4194ffd949eSMike Smith 	if (*arg == 0) {
4204ffd949eSMike Smith 		*arg = mem_range_softc.mr_ndesc;
4214ffd949eSMike Smith 	} else {
4224ffd949eSMike Smith 		bcopy(mem_range_softc.mr_desc, mrd, (*arg) * sizeof(struct mem_range_desc));
4234ffd949eSMike Smith 	}
424ac3595b0SMike Smith 	return (0);
4254ffd949eSMike Smith }
4264ffd949eSMike Smith 
4274ffd949eSMike Smith int
4284af396a5SMike Smith mem_range_attr_set(mrd, arg)
4294af396a5SMike Smith 	struct mem_range_desc *mrd;
4304af396a5SMike Smith 	int *arg;
4314ffd949eSMike Smith {
4324af396a5SMike Smith 	/* can we handle this? */
4334af396a5SMike Smith 	if (mem_range_softc.mr_op == NULL)
4344af396a5SMike Smith 		return (EOPNOTSUPP);
4354af396a5SMike Smith 
4364ffd949eSMike Smith 	return (mem_range_softc.mr_op->set(&mem_range_softc, mrd, arg));
4374ffd949eSMike Smith }
4384ffd949eSMike Smith 
4399c06a386SPeter Wemm #ifdef SMP
4404a034f21SMike Smith void
4414a034f21SMike Smith mem_range_AP_init(void)
4424a034f21SMike Smith {
443cbdfdcb2SPeter Wemm 	if (mem_range_softc.mr_op && mem_range_softc.mr_op->initAP)
4444a034f21SMike Smith 		return (mem_range_softc.mr_op->initAP(&mem_range_softc));
4454a034f21SMike Smith }
4469c06a386SPeter Wemm #endif
4474a034f21SMike Smith 
4484ffd949eSMike Smith static int
4494ffd949eSMike Smith random_ioctl(dev, cmd, data, flags, p)
4504ffd949eSMike Smith 	dev_t dev;
4514ffd949eSMike Smith 	u_long cmd;
4524ffd949eSMike Smith 	caddr_t data;
4534ffd949eSMike Smith 	int flags;
4544ffd949eSMike Smith 	struct proc *p;
4554ffd949eSMike Smith {
4564ffd949eSMike Smith 	static intrmask_t interrupt_allowed;
4574ffd949eSMike Smith 	intrmask_t interrupt_mask;
4584ffd949eSMike Smith 	int error, intr;
4594ffd949eSMike Smith 	struct random_softc *sc;
4604ffd949eSMike Smith 
4614ffd949eSMike Smith 	/*
462316bbd5cSBruce Evans 	 * We're the random or urandom device.  The only ioctls are for
463316bbd5cSBruce Evans 	 * selecting and inspecting which interrupts are used in the muck
464316bbd5cSBruce Evans 	 * gathering business.
465316bbd5cSBruce Evans 	 */
466316bbd5cSBruce Evans 	if (cmd != MEM_SETIRQ && cmd != MEM_CLEARIRQ && cmd != MEM_RETURNIRQ)
467316bbd5cSBruce Evans 		return (ENOTTY);
468316bbd5cSBruce Evans 
469316bbd5cSBruce Evans 	/*
470316bbd5cSBruce Evans 	 * Even inspecting the state is privileged, since it gives a hint
471316bbd5cSBruce Evans 	 * about how easily the randomness might be guessed.
472316bbd5cSBruce Evans 	 */
473f711d546SPoul-Henning Kamp 	error = suser(p);
474316bbd5cSBruce Evans 	if (error != 0)
475316bbd5cSBruce Evans 		return (error);
476316bbd5cSBruce Evans 
477316bbd5cSBruce Evans 	/*
478316bbd5cSBruce Evans 	 * XXX the data is 16-bit due to a historical botch, so we use
479316bbd5cSBruce Evans 	 * magic 16's instead of ICU_LEN and can't support 24 interrupts
480316bbd5cSBruce Evans 	 * under SMP.
481316bbd5cSBruce Evans 	 */
482316bbd5cSBruce Evans 	intr = *(int16_t *)data;
483316bbd5cSBruce Evans 	if (cmd != MEM_RETURNIRQ && (intr < 0 || intr >= 16))
4841bb2d314SMark Murray 		return (EINVAL);
4851bb2d314SMark Murray 
486316bbd5cSBruce Evans 	interrupt_mask = 1 << intr;
487316bbd5cSBruce Evans 	sc = &random_softc[intr];
4881bb2d314SMark Murray 	switch (cmd) {
4891bb2d314SMark Murray 	case MEM_SETIRQ:
490316bbd5cSBruce Evans 		if (interrupt_allowed & interrupt_mask)
491316bbd5cSBruce Evans 			break;
492e85ceed0SMark Murray 		interrupt_allowed |= interrupt_mask;
493316bbd5cSBruce Evans 		sc->sc_intr = intr;
494e85ceed0SMark Murray 		disable_intr();
495316bbd5cSBruce Evans 		sc->sc_handler = intr_handler[intr];
496316bbd5cSBruce Evans 		intr_handler[intr] = add_interrupt_randomness;
497316bbd5cSBruce Evans 		sc->sc_arg = intr_unit[intr];
498316bbd5cSBruce Evans 		intr_unit[intr] = sc;
499e85ceed0SMark Murray 		enable_intr();
5001bb2d314SMark Murray 		break;
501316bbd5cSBruce Evans 	case MEM_CLEARIRQ:
502316bbd5cSBruce Evans 		if (!(interrupt_allowed & interrupt_mask))
503316bbd5cSBruce Evans 			break;
504316bbd5cSBruce Evans 		interrupt_allowed &= ~interrupt_mask;
505316bbd5cSBruce Evans 		disable_intr();
506316bbd5cSBruce Evans 		intr_handler[intr] = sc->sc_handler;
507316bbd5cSBruce Evans 		intr_unit[intr] = sc->sc_arg;
508316bbd5cSBruce Evans 		enable_intr();
509316bbd5cSBruce Evans 		break;
5101bb2d314SMark Murray 	case MEM_RETURNIRQ:
511316bbd5cSBruce Evans 		*(u_int16_t *)data = interrupt_allowed;
5121bb2d314SMark Murray 		break;
5131bb2d314SMark Murray 	}
5141bb2d314SMark Murray 	return (0);
51500f7f6beSBruce Evans }
51653ac6efbSJulian Elischer 
517983febf3SPeter Wemm int
51835b8b2ddSPeter Wemm mmpoll(dev, events, p)
519983febf3SPeter Wemm 	dev_t dev;
52035b8b2ddSPeter Wemm 	int events;
521983febf3SPeter Wemm 	struct proc *p;
522983febf3SPeter Wemm {
523983febf3SPeter Wemm 	switch (minor(dev)) {
524983febf3SPeter Wemm 	case 3:		/* /dev/random */
52535b8b2ddSPeter Wemm 		return random_poll(dev, events, p);
526983febf3SPeter Wemm 	case 4:		/* /dev/urandom */
527983febf3SPeter Wemm 	default:
52835b8b2ddSPeter Wemm 		return seltrue(dev, events, p);
529983febf3SPeter Wemm 	}
530983febf3SPeter Wemm }
531983febf3SPeter Wemm 
532ee0ef569SJulian Elischer int
533ee0ef569SJulian Elischer iszerodev(dev)
534ee0ef569SJulian Elischer 	dev_t dev;
535ee0ef569SJulian Elischer {
536ee0ef569SJulian Elischer 	return ((major(dev) == mem_cdevsw.d_maj)
537ee0ef569SJulian Elischer 	  && minor(dev) == 12);
538ee0ef569SJulian Elischer }
539ee0ef569SJulian Elischer 
54087f6c662SJulian Elischer static void
54187f6c662SJulian Elischer mem_drvinit(void *unused)
54253ac6efbSJulian Elischer {
54353ac6efbSJulian Elischer 
5444ffd949eSMike Smith 	/* Initialise memory range handling */
5454ffd949eSMike Smith 	if (mem_range_softc.mr_op != NULL)
5464ffd949eSMike Smith 		mem_range_softc.mr_op->init(&mem_range_softc);
5474ffd949eSMike Smith 
5489dcbe240SPoul-Henning Kamp 	make_dev(&mem_cdevsw, 0, UID_ROOT, GID_KMEM, 0640, "mem");
5499dcbe240SPoul-Henning Kamp 	make_dev(&mem_cdevsw, 1, UID_ROOT, GID_KMEM, 0640, "kmem");
5509dcbe240SPoul-Henning Kamp 	make_dev(&mem_cdevsw, 2, UID_ROOT, GID_WHEEL, 0666, "null");
5519dcbe240SPoul-Henning Kamp 	make_dev(&mem_cdevsw, 3, UID_ROOT, GID_WHEEL, 0644, "random");
5529dcbe240SPoul-Henning Kamp 	make_dev(&mem_cdevsw, 4, UID_ROOT, GID_WHEEL, 0644, "urandom");
5539dcbe240SPoul-Henning Kamp 	make_dev(&mem_cdevsw, 12, UID_ROOT, GID_WHEEL, 0666, "zero");
5549dcbe240SPoul-Henning Kamp 	make_dev(&mem_cdevsw, 14, UID_ROOT, GID_WHEEL, 0600, "io");
5557198bf47SJulian Elischer }
55653ac6efbSJulian Elischer 
55753ac6efbSJulian Elischer SYSINIT(memdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,mem_drvinit,NULL)
55853ac6efbSJulian Elischer 
559