mem.c revision 115683
1/*-
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
8 * Science Department, and code derived from software contributed to
9 * Berkeley by William Jolitz.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This product includes software developed by the University of
22 *	California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *	from: Utah $Hdr: mem.c 1.13 89/10/08$
40 *	from: @(#)mem.c	7.2 (Berkeley) 5/9/91
41 */
42
43#include <sys/cdefs.h>
44__FBSDID("$FreeBSD: head/sys/i386/i386/mem.c 115683 2003-06-02 06:43:15Z obrien $");
45
46/*
47 * Memory special file
48 */
49
50#include <sys/param.h>
51#include <sys/conf.h>
52#include <sys/fcntl.h>
53#include <sys/ioccom.h>
54#include <sys/kernel.h>
55#include <sys/lock.h>
56#include <sys/malloc.h>
57#include <sys/memrange.h>
58#include <sys/mutex.h>
59#include <sys/proc.h>
60#include <sys/signalvar.h>
61#include <sys/systm.h>
62#include <sys/uio.h>
63
64#include <machine/db_machdep.h>
65#include <machine/frame.h>
66#include <machine/psl.h>
67#include <machine/specialreg.h>
68
69#include <vm/vm.h>
70#include <vm/pmap.h>
71#include <vm/vm_extern.h>
72
73static dev_t memdev, kmemdev, iodev;
74
75static	d_open_t	mmopen;
76static	d_close_t	mmclose;
77static	d_read_t	mmrw;
78static	d_ioctl_t	mmioctl;
79static	d_mmap_t	memmmap;
80
81#define CDEV_MAJOR 2
82static struct cdevsw mem_cdevsw = {
83	.d_open =	mmopen,
84	.d_close =	mmclose,
85	.d_read =	mmrw,
86	.d_write =	mmrw,
87	.d_ioctl =	mmioctl,
88	.d_mmap =	memmmap,
89	.d_name =	"mem",
90	.d_maj =	CDEV_MAJOR,
91	.d_flags =	D_MEM,
92};
93
94MALLOC_DEFINE(M_MEMDESC, "memdesc", "memory range descriptors");
95
96struct mem_range_softc mem_range_softc;
97
98static int
99mmclose(dev_t dev, int flags, int fmt, struct thread *td)
100{
101	switch (minor(dev)) {
102	case 14:
103		td->td_frame->tf_eflags &= ~PSL_IOPL;
104	}
105	return (0);
106}
107
108static int
109mmopen(dev_t dev, int flags, int fmt, struct thread *td)
110{
111	int error;
112
113	switch (minor(dev)) {
114	case 0:
115	case 1:
116		if (flags & FWRITE) {
117			error = securelevel_gt(td->td_ucred, 0);
118			if (error != 0)
119				return (error);
120		}
121		break;
122	case 14:
123		error = suser(td);
124		if (error != 0)
125			return (error);
126		error = securelevel_gt(td->td_ucred, 0);
127		if (error != 0)
128			return (error);
129		td->td_frame->tf_eflags |= PSL_IOPL;
130		break;
131	}
132	return (0);
133}
134
135/*ARGSUSED*/
136static int
137mmrw(dev_t dev, struct uio *uio, int flags)
138{
139	int o;
140	u_int c = 0, v;
141	struct iovec *iov;
142	int error = 0;
143	vm_offset_t addr, eaddr;
144
145	GIANT_REQUIRED;
146
147	while (uio->uio_resid > 0 && error == 0) {
148		iov = uio->uio_iov;
149		if (iov->iov_len == 0) {
150			uio->uio_iov++;
151			uio->uio_iovcnt--;
152			if (uio->uio_iovcnt < 0)
153				panic("mmrw");
154			continue;
155		}
156		switch (minor(dev)) {
157
158/* minor device 0 is physical memory */
159		case 0:
160			v = uio->uio_offset;
161			v &= ~PAGE_MASK;
162			pmap_kenter((vm_offset_t)ptvmmap, v);
163			o = (int)uio->uio_offset & PAGE_MASK;
164			c = (u_int)(PAGE_SIZE - ((int)iov->iov_base & PAGE_MASK));
165			c = min(c, (u_int)(PAGE_SIZE - o));
166			c = min(c, (u_int)iov->iov_len);
167			error = uiomove((caddr_t)&ptvmmap[o], (int)c, uio);
168			pmap_qremove((vm_offset_t)ptvmmap, 1);
169			continue;
170
171/* minor device 1 is kernel memory */
172		case 1:
173			c = iov->iov_len;
174
175			/*
176			 * Make sure that all of the pages are currently resident so
177			 * that we don't create any zero-fill pages.
178			 */
179			addr = trunc_page(uio->uio_offset);
180			eaddr = round_page(uio->uio_offset + c);
181
182			if (addr < (vm_offset_t)VADDR(PTDPTDI, 0))
183				return (EFAULT);
184			for (; addr < eaddr; addr += PAGE_SIZE)
185				if (pmap_extract(kernel_pmap, addr) == 0)
186					return (EFAULT);
187
188			if (!kernacc((caddr_t)(int)uio->uio_offset, c,
189			    uio->uio_rw == UIO_READ ?
190			    VM_PROT_READ : VM_PROT_WRITE))
191				return (EFAULT);
192			error = uiomove((caddr_t)(int)uio->uio_offset, (int)c, uio);
193			continue;
194
195		default:
196			return (ENODEV);
197		}
198
199		if (error)
200			break;
201		iov->iov_base = (char *)iov->iov_base + c;
202		iov->iov_len -= c;
203		uio->uio_offset += c;
204		uio->uio_resid -= c;
205	}
206	return (error);
207}
208
209/*******************************************************\
210* allow user processes to MMAP some memory sections	*
211* instead of going through read/write			*
212\*******************************************************/
213static int
214memmmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int prot)
215{
216	switch (minor(dev))
217	{
218
219	/* minor device 0 is physical memory */
220	case 0:
221		*paddr = offset;
222		break;
223
224	/* minor device 1 is kernel memory */
225	case 1:
226        	*paddr = vtophys(offset);
227		break;
228
229	default:
230		return (-1);
231	}
232	return (0);
233}
234
235/*
236 * Operations for changing memory attributes.
237 *
238 * This is basically just an ioctl shim for mem_range_attr_get
239 * and mem_range_attr_set.
240 */
241static int
242mmioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
243{
244	int nd, error = 0;
245	struct mem_range_op *mo = (struct mem_range_op *)data;
246	struct mem_range_desc *md;
247
248	/* is this for us? */
249	if ((cmd != MEMRANGE_GET) &&
250	    (cmd != MEMRANGE_SET))
251		return (ENOTTY);
252
253	/* any chance we can handle this? */
254	if (mem_range_softc.mr_op == NULL)
255		return (EOPNOTSUPP);
256
257	/* do we have any descriptors? */
258	if (mem_range_softc.mr_ndesc == 0)
259		return (ENXIO);
260
261	switch (cmd) {
262	case MEMRANGE_GET:
263		nd = imin(mo->mo_arg[0], mem_range_softc.mr_ndesc);
264		if (nd > 0) {
265			md = (struct mem_range_desc *)
266				malloc(nd * sizeof(struct mem_range_desc),
267				       M_MEMDESC, M_WAITOK);
268			error = mem_range_attr_get(md, &nd);
269			if (!error)
270				error = copyout(md, mo->mo_desc,
271					nd * sizeof(struct mem_range_desc));
272			free(md, M_MEMDESC);
273		}
274		else
275			nd = mem_range_softc.mr_ndesc;
276		mo->mo_arg[0] = nd;
277		break;
278
279	case MEMRANGE_SET:
280		md = (struct mem_range_desc *)malloc(sizeof(struct mem_range_desc),
281						    M_MEMDESC, M_WAITOK);
282		error = copyin(mo->mo_desc, md, sizeof(struct mem_range_desc));
283		/* clamp description string */
284		md->mr_owner[sizeof(md->mr_owner) - 1] = 0;
285		if (error == 0)
286			error = mem_range_attr_set(md, &mo->mo_arg[0]);
287		free(md, M_MEMDESC);
288		break;
289	}
290	return (error);
291}
292
293/*
294 * Implementation-neutral, kernel-callable functions for manipulating
295 * memory range attributes.
296 */
297int
298mem_range_attr_get(struct mem_range_desc *mrd, int *arg)
299{
300	/* can we handle this? */
301	if (mem_range_softc.mr_op == NULL)
302		return (EOPNOTSUPP);
303
304	if (*arg == 0)
305		*arg = mem_range_softc.mr_ndesc;
306	else
307		bcopy(mem_range_softc.mr_desc, mrd,
308			(*arg) * sizeof(struct mem_range_desc));
309	return (0);
310}
311
312int
313mem_range_attr_set(struct mem_range_desc *mrd, int *arg)
314{
315	/* can we handle this? */
316	if (mem_range_softc.mr_op == NULL)
317		return (EOPNOTSUPP);
318
319	return (mem_range_softc.mr_op->set(&mem_range_softc, mrd, arg));
320}
321
322#ifdef SMP
323void
324mem_range_AP_init(void)
325{
326	if (mem_range_softc.mr_op && mem_range_softc.mr_op->initAP)
327		(mem_range_softc.mr_op->initAP(&mem_range_softc));
328}
329#endif
330
331static int
332mem_modevent(module_t mod, int type, void *data)
333{
334	switch(type) {
335	case MOD_LOAD:
336		if (bootverbose)
337			printf("mem: <memory & I/O>\n");
338		/* Initialise memory range handling */
339		if (mem_range_softc.mr_op != NULL)
340			mem_range_softc.mr_op->init(&mem_range_softc);
341
342		memdev = make_dev(&mem_cdevsw, 0, UID_ROOT, GID_KMEM,
343			0640, "mem");
344		kmemdev = make_dev(&mem_cdevsw, 1, UID_ROOT, GID_KMEM,
345			0640, "kmem");
346		iodev = make_dev(&mem_cdevsw, 14, UID_ROOT, GID_WHEEL,
347			0600, "io");
348		return (0);
349
350	case MOD_UNLOAD:
351		destroy_dev(memdev);
352		destroy_dev(kmemdev);
353		destroy_dev(iodev);
354		return (0);
355
356	case MOD_SHUTDOWN:
357		return (0);
358
359	default:
360		return (EOPNOTSUPP);
361	}
362}
363
364DEV_MODULE(mem, mem_modevent, NULL);
365