xref: /freebsd/sys/amd64/amd64/mem.c (revision bfcd2ec739053bd70250c37869d1d295a35150dc)
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>
5323955314SAlfred Perlstein #include <sys/lock.h>
5426f9a767SRodney W. Grimes #include <sys/malloc.h>
554ffd949eSMike Smith #include <sys/memrange.h>
569dceb26bSJohn Baldwin #include <sys/mutex.h>
5726f9a767SRodney W. Grimes #include <sys/proc.h>
58bb25f0ddSBruce Evans #include <sys/signalvar.h>
59d4af7a50SDavid E. O'Brien #include <sys/systm.h>
60d4af7a50SDavid E. O'Brien #include <sys/uio.h>
615b81b6b3SRodney W. Grimes 
625afffbaaSMark Murray #include <machine/db_machdep.h>
63d1d9d260SBruce Evans #include <machine/frame.h>
6426f9a767SRodney W. Grimes #include <machine/psl.h>
654ffd949eSMike Smith #include <machine/specialreg.h>
663c9a3c9cSPeter Wemm #include <machine/vmparam.h>
675b81b6b3SRodney W. Grimes 
68c87801feSDavid Greenman #include <vm/vm.h>
6926f9a767SRodney W. Grimes #include <vm/pmap.h>
70efeaf95aSDavid Greenman #include <vm/vm_extern.h>
715b81b6b3SRodney W. Grimes 
725afffbaaSMark Murray static dev_t memdev, kmemdev, iodev;
7387f6c662SJulian Elischer 
7487f6c662SJulian Elischer static	d_open_t	mmopen;
7587f6c662SJulian Elischer static	d_close_t	mmclose;
76c73feca0SBruce Evans static	d_read_t	mmrw;
7787f6c662SJulian Elischer static	d_ioctl_t	mmioctl;
7887f6c662SJulian Elischer static	d_mmap_t	memmmap;
7987f6c662SJulian Elischer 
8053ac6efbSJulian Elischer #define CDEV_MAJOR 2
814e2f199eSPoul-Henning Kamp static struct cdevsw mem_cdevsw = {
827ac40f5fSPoul-Henning Kamp 	.d_open =	mmopen,
837ac40f5fSPoul-Henning Kamp 	.d_close =	mmclose,
847ac40f5fSPoul-Henning Kamp 	.d_read =	mmrw,
857ac40f5fSPoul-Henning Kamp 	.d_write =	mmrw,
867ac40f5fSPoul-Henning Kamp 	.d_ioctl =	mmioctl,
877ac40f5fSPoul-Henning Kamp 	.d_mmap =	memmmap,
887ac40f5fSPoul-Henning Kamp 	.d_name =	"mem",
897ac40f5fSPoul-Henning Kamp 	.d_maj =	CDEV_MAJOR,
907ac40f5fSPoul-Henning Kamp 	.d_flags =	D_MEM,
914e2f199eSPoul-Henning Kamp };
9253ac6efbSJulian Elischer 
934ffd949eSMike Smith MALLOC_DEFINE(M_MEMDESC, "memdesc", "memory range descriptors");
944ffd949eSMike Smith 
954ffd949eSMike Smith struct mem_range_softc mem_range_softc;
964ffd949eSMike Smith 
9787f6c662SJulian Elischer static int
98b40ce416SJulian Elischer mmclose(dev_t dev, int flags, int fmt, struct thread *td)
9978d172caSRodney W. Grimes {
10078d172caSRodney W. Grimes 	switch (minor(dev)) {
10178d172caSRodney W. Grimes 	case 14:
102afa88623SPeter Wemm 		td->td_frame->tf_rflags &= ~PSL_IOPL;
10378d172caSRodney W. Grimes 	}
10478d172caSRodney W. Grimes 	return (0);
10578d172caSRodney W. Grimes }
10633f538b9SBruce Evans 
10787f6c662SJulian Elischer static int
108b40ce416SJulian Elischer mmopen(dev_t dev, int flags, int fmt, struct thread *td)
10978d172caSRodney W. Grimes {
110da3df630SBruce Evans 	int error;
11178d172caSRodney W. Grimes 
11278d172caSRodney W. Grimes 	switch (minor(dev)) {
11318284c94SPoul-Henning Kamp 	case 0:
11418284c94SPoul-Henning Kamp 	case 1:
1151851c8fdSRobert Watson 		if (flags & FWRITE) {
116a854ed98SJohn Baldwin 			error = securelevel_gt(td->td_ucred, 0);
1171851c8fdSRobert Watson 			if (error != 0)
1181851c8fdSRobert Watson 				return (error);
1191851c8fdSRobert Watson 		}
12018284c94SPoul-Henning Kamp 		break;
12178d172caSRodney W. Grimes 	case 14:
12244731cabSJohn Baldwin 		error = suser(td);
123da3df630SBruce Evans 		if (error != 0)
124da3df630SBruce Evans 			return (error);
125a854ed98SJohn Baldwin 		error = securelevel_gt(td->td_ucred, 0);
1261851c8fdSRobert Watson 		if (error != 0)
1271851c8fdSRobert Watson 			return (error);
128afa88623SPeter Wemm 		td->td_frame->tf_rflags |= PSL_IOPL;
12978d172caSRodney W. Grimes 		break;
13078d172caSRodney W. Grimes 	}
13178d172caSRodney W. Grimes 	return (0);
13278d172caSRodney W. Grimes }
13333f538b9SBruce Evans 
1345afffbaaSMark Murray /*ARGSUSED*/
13587f6c662SJulian Elischer static int
1365afffbaaSMark Murray mmrw(dev_t dev, struct uio *uio, int flags)
1375b81b6b3SRodney W. Grimes {
1385afffbaaSMark Murray 	int o;
139afa88623SPeter Wemm 	u_long c = 0, v;
1405afffbaaSMark Murray 	struct iovec *iov;
1415b81b6b3SRodney W. Grimes 	int error = 0;
1425afffbaaSMark Murray 	vm_offset_t addr, eaddr;
1435b81b6b3SRodney W. Grimes 
1440cddd8f0SMatthew Dillon 	GIANT_REQUIRED;
1450cddd8f0SMatthew Dillon 
1465b81b6b3SRodney W. Grimes 	while (uio->uio_resid > 0 && error == 0) {
1475b81b6b3SRodney W. Grimes 		iov = uio->uio_iov;
1485b81b6b3SRodney W. Grimes 		if (iov->iov_len == 0) {
1495b81b6b3SRodney W. Grimes 			uio->uio_iov++;
1505b81b6b3SRodney W. Grimes 			uio->uio_iovcnt--;
1515b81b6b3SRodney W. Grimes 			if (uio->uio_iovcnt < 0)
1525b81b6b3SRodney W. Grimes 				panic("mmrw");
1535b81b6b3SRodney W. Grimes 			continue;
1545b81b6b3SRodney W. Grimes 		}
1555b81b6b3SRodney W. Grimes 		switch (minor(dev)) {
1565b81b6b3SRodney W. Grimes 
1575b81b6b3SRodney W. Grimes /* minor device 0 is physical memory */
1585b81b6b3SRodney W. Grimes 		case 0:
1595b81b6b3SRodney W. Grimes 			v = uio->uio_offset;
160bfcd2ec7SHidetoshi Shimokawa kmemphys:
161bfcd2ec7SHidetoshi Shimokawa 			o = v & PAGE_MASK;
162bfcd2ec7SHidetoshi Shimokawa 			c = min(uio->uio_resid, (u_int)(PAGE_SIZE - o));
163bfcd2ec7SHidetoshi Shimokawa 			error = uiomove((void *)PHYS_TO_DMAP(v), (int)c, uio);
1645b81b6b3SRodney W. Grimes 			continue;
1655b81b6b3SRodney W. Grimes 
1665b81b6b3SRodney W. Grimes /* minor device 1 is kernel memory */
1675afffbaaSMark Murray 		case 1:
168bfcd2ec7SHidetoshi Shimokawa 			v = uio->uio_offset;
16997e11262SDavid Greenman 
170bfcd2ec7SHidetoshi Shimokawa 			if (v >= DMAP_MIN_ADDRESS && v < DMAP_MAX_ADDRESS) {
171bfcd2ec7SHidetoshi Shimokawa 				v = DMAP_TO_PHYS(v);
172bfcd2ec7SHidetoshi Shimokawa 				goto kmemphys;
173bfcd2ec7SHidetoshi Shimokawa 			}
174bfcd2ec7SHidetoshi Shimokawa 
175bfcd2ec7SHidetoshi Shimokawa 			c = iov->iov_len;
17697e11262SDavid Greenman 			/*
17797e11262SDavid Greenman 			 * Make sure that all of the pages are currently resident so
17897e11262SDavid Greenman 			 * that we don't create any zero-fill pages.
17997e11262SDavid Greenman 			 */
180bfcd2ec7SHidetoshi Shimokawa 			addr = trunc_page(v);
181bfcd2ec7SHidetoshi Shimokawa 			eaddr = round_page(v + c);
182d021fc11SPeter Wemm 
1833c9a3c9cSPeter Wemm 			if (addr < (vm_offset_t)KERNBASE)
184e40ab3e9SMark Murray 				return (EFAULT);
18597e11262SDavid Greenman 			for (; addr < eaddr; addr += PAGE_SIZE)
186e40ab3e9SMark Murray 				if (pmap_extract(kernel_pmap, addr) == 0)
187e40ab3e9SMark Murray 					return (EFAULT);
18897e11262SDavid Greenman 
189bfcd2ec7SHidetoshi Shimokawa 			if (!kernacc((caddr_t)(long)v, c,
19002c58685SPoul-Henning Kamp 			    uio->uio_rw == UIO_READ ?
191e40ab3e9SMark Murray 			    VM_PROT_READ : VM_PROT_WRITE))
1925b81b6b3SRodney W. Grimes 				return (EFAULT);
193bfcd2ec7SHidetoshi Shimokawa 
194bfcd2ec7SHidetoshi Shimokawa 			error = uiomove((caddr_t)(long)v, (int)c, uio);
1955b81b6b3SRodney W. Grimes 			continue;
196d9ce8f5cSNate Lawson 
197d9ce8f5cSNate Lawson 		default:
198d9ce8f5cSNate Lawson 			return (ENODEV);
19997e11262SDavid Greenman 		}
2005b81b6b3SRodney W. Grimes 
2015b81b6b3SRodney W. Grimes 		if (error)
2025b81b6b3SRodney W. Grimes 			break;
2032b7f24d2SMike Barcroft 		iov->iov_base = (char *)iov->iov_base + c;
2045b81b6b3SRodney W. Grimes 		iov->iov_len -= c;
2055b81b6b3SRodney W. Grimes 		uio->uio_offset += c;
2065b81b6b3SRodney W. Grimes 		uio->uio_resid -= c;
2075b81b6b3SRodney W. Grimes 	}
2085b81b6b3SRodney W. Grimes 	return (error);
2095b81b6b3SRodney W. Grimes }
210b513c262SDavid Greenman 
211b513c262SDavid Greenman /*******************************************************\
212b513c262SDavid Greenman * allow user processes to MMAP some memory sections	*
213b513c262SDavid Greenman * instead of going through read/write			*
214b513c262SDavid Greenman \*******************************************************/
21587f6c662SJulian Elischer static int
216227f9a1cSJake Burkholder memmmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int prot)
217b513c262SDavid Greenman {
218b513c262SDavid Greenman 	switch (minor(dev))
219b513c262SDavid Greenman 	{
220b513c262SDavid Greenman 
221b513c262SDavid Greenman 	/* minor device 0 is physical memory */
222b513c262SDavid Greenman 	case 0:
22307159f9cSMaxime Henrion 		*paddr = offset;
22407159f9cSMaxime Henrion 		break;
225b513c262SDavid Greenman 
226b513c262SDavid Greenman 	/* minor device 1 is kernel memory */
227b513c262SDavid Greenman 	case 1:
22807159f9cSMaxime Henrion         	*paddr = vtophys(offset);
22907159f9cSMaxime Henrion 		break;
230493c240cSMark Murray 
231493c240cSMark Murray 	default:
232e40ab3e9SMark Murray 		return (-1);
233b513c262SDavid Greenman 	}
23407159f9cSMaxime Henrion 	return (0);
2358567cb9fSMark Murray }
236b513c262SDavid Greenman 
237316bbd5cSBruce Evans /*
2384ffd949eSMike Smith  * Operations for changing memory attributes.
2394ffd949eSMike Smith  *
2404ffd949eSMike Smith  * This is basically just an ioctl shim for mem_range_attr_get
2414ffd949eSMike Smith  * and mem_range_attr_set.
2424ffd949eSMike Smith  */
2434ffd949eSMike Smith static int
244b40ce416SJulian Elischer mmioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
2454ffd949eSMike Smith {
2464ffd949eSMike Smith 	int nd, error = 0;
2474ffd949eSMike Smith 	struct mem_range_op *mo = (struct mem_range_op *)data;
2484ffd949eSMike Smith 	struct mem_range_desc *md;
2494ffd949eSMike Smith 
2504ffd949eSMike Smith 	/* is this for us? */
2514ffd949eSMike Smith 	if ((cmd != MEMRANGE_GET) &&
2524ffd949eSMike Smith 	    (cmd != MEMRANGE_SET))
2534871de2fSBrian Feldman 		return (ENOTTY);
2544ffd949eSMike Smith 
2554ffd949eSMike Smith 	/* any chance we can handle this? */
2564ffd949eSMike Smith 	if (mem_range_softc.mr_op == NULL)
2574ffd949eSMike Smith 		return (EOPNOTSUPP);
2584ffd949eSMike Smith 
2594ffd949eSMike Smith 	/* do we have any descriptors? */
2604ffd949eSMike Smith 	if (mem_range_softc.mr_ndesc == 0)
2614ffd949eSMike Smith 		return (ENXIO);
2624ffd949eSMike Smith 
2634ffd949eSMike Smith 	switch (cmd) {
2644ffd949eSMike Smith 	case MEMRANGE_GET:
2654ffd949eSMike Smith 		nd = imin(mo->mo_arg[0], mem_range_softc.mr_ndesc);
2664ffd949eSMike Smith 		if (nd > 0) {
2674ffd949eSMike Smith 			md = (struct mem_range_desc *)
2684ffd949eSMike Smith 				malloc(nd * sizeof(struct mem_range_desc),
269a163d034SWarner Losh 				       M_MEMDESC, M_WAITOK);
270ac3595b0SMike Smith 			error = mem_range_attr_get(md, &nd);
271ac3595b0SMike Smith 			if (!error)
2724ffd949eSMike Smith 				error = copyout(md, mo->mo_desc,
2734ffd949eSMike Smith 					nd * sizeof(struct mem_range_desc));
2744ffd949eSMike Smith 			free(md, M_MEMDESC);
2754ffd949eSMike Smith 		}
276e40ab3e9SMark Murray 		else
277e40ab3e9SMark Murray 			nd = mem_range_softc.mr_ndesc;
2784ffd949eSMike Smith 		mo->mo_arg[0] = nd;
2794ffd949eSMike Smith 		break;
2804ffd949eSMike Smith 
2814ffd949eSMike Smith 	case MEMRANGE_SET:
2824ffd949eSMike Smith 		md = (struct mem_range_desc *)malloc(sizeof(struct mem_range_desc),
283a163d034SWarner Losh 						    M_MEMDESC, M_WAITOK);
2844ffd949eSMike Smith 		error = copyin(mo->mo_desc, md, sizeof(struct mem_range_desc));
2854ffd949eSMike Smith 		/* clamp description string */
2864ffd949eSMike Smith 		md->mr_owner[sizeof(md->mr_owner) - 1] = 0;
2874ffd949eSMike Smith 		if (error == 0)
2884ffd949eSMike Smith 			error = mem_range_attr_set(md, &mo->mo_arg[0]);
2894ffd949eSMike Smith 		free(md, M_MEMDESC);
2904ffd949eSMike Smith 		break;
2914ffd949eSMike Smith 	}
2924ffd949eSMike Smith 	return (error);
2934ffd949eSMike Smith }
2944ffd949eSMike Smith 
2954ffd949eSMike Smith /*
2964ffd949eSMike Smith  * Implementation-neutral, kernel-callable functions for manipulating
2974ffd949eSMike Smith  * memory range attributes.
2984ffd949eSMike Smith  */
299ac3595b0SMike Smith int
3005afffbaaSMark Murray mem_range_attr_get(struct mem_range_desc *mrd, int *arg)
3014ffd949eSMike Smith {
3024af396a5SMike Smith 	/* can we handle this? */
3034af396a5SMike Smith 	if (mem_range_softc.mr_op == NULL)
3044af396a5SMike Smith 		return (EOPNOTSUPP);
3054af396a5SMike Smith 
306e40ab3e9SMark Murray 	if (*arg == 0)
3074ffd949eSMike Smith 		*arg = mem_range_softc.mr_ndesc;
308e40ab3e9SMark Murray 	else
3095afffbaaSMark Murray 		bcopy(mem_range_softc.mr_desc, mrd,
3105afffbaaSMark Murray 			(*arg) * sizeof(struct mem_range_desc));
311ac3595b0SMike Smith 	return (0);
3124ffd949eSMike Smith }
3134ffd949eSMike Smith 
3144ffd949eSMike Smith int
3155afffbaaSMark Murray mem_range_attr_set(struct mem_range_desc *mrd, int *arg)
3164ffd949eSMike Smith {
3174af396a5SMike Smith 	/* can we handle this? */
3184af396a5SMike Smith 	if (mem_range_softc.mr_op == NULL)
3194af396a5SMike Smith 		return (EOPNOTSUPP);
3204af396a5SMike Smith 
3214ffd949eSMike Smith 	return (mem_range_softc.mr_op->set(&mem_range_softc, mrd, arg));
3224ffd949eSMike Smith }
3234ffd949eSMike Smith 
3244ffd949eSMike Smith static int
3255afffbaaSMark Murray mem_modevent(module_t mod, int type, void *data)
3264ffd949eSMike Smith {
3275afffbaaSMark Murray 	switch(type) {
3285afffbaaSMark Murray 	case MOD_LOAD:
3295afffbaaSMark Murray 		if (bootverbose)
3305afffbaaSMark Murray 			printf("mem: <memory & I/O>\n");
3314ffd949eSMike Smith 		/* Initialise memory range handling */
3324ffd949eSMike Smith 		if (mem_range_softc.mr_op != NULL)
3334ffd949eSMike Smith 			mem_range_softc.mr_op->init(&mem_range_softc);
3344ffd949eSMike Smith 
3355afffbaaSMark Murray 		memdev = make_dev(&mem_cdevsw, 0, UID_ROOT, GID_KMEM,
3365afffbaaSMark Murray 			0640, "mem");
3375afffbaaSMark Murray 		kmemdev = make_dev(&mem_cdevsw, 1, UID_ROOT, GID_KMEM,
3385afffbaaSMark Murray 			0640, "kmem");
3395afffbaaSMark Murray 		iodev = make_dev(&mem_cdevsw, 14, UID_ROOT, GID_WHEEL,
3405afffbaaSMark Murray 			0600, "io");
341e40ab3e9SMark Murray 		return (0);
3425afffbaaSMark Murray 
3435afffbaaSMark Murray 	case MOD_UNLOAD:
3445afffbaaSMark Murray 		destroy_dev(memdev);
3455afffbaaSMark Murray 		destroy_dev(kmemdev);
3465afffbaaSMark Murray 		destroy_dev(iodev);
347e40ab3e9SMark Murray 		return (0);
3485afffbaaSMark Murray 
3495afffbaaSMark Murray 	case MOD_SHUTDOWN:
350e40ab3e9SMark Murray 		return (0);
3515afffbaaSMark Murray 
3525afffbaaSMark Murray 	default:
353e40ab3e9SMark Murray 		return (EOPNOTSUPP);
3545afffbaaSMark Murray 	}
3557198bf47SJulian Elischer }
35653ac6efbSJulian Elischer 
3575afffbaaSMark Murray DEV_MODULE(mem, mem_modevent, NULL);
358