xref: /freebsd/sys/amd64/amd64/mem.c (revision ce4946daa5ce852d28008dac492029500ab2ee95)
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  * $FreeBSD$
42  */
43 
44 /*
45  * Memory special file
46  */
47 
48 #include <sys/param.h>
49 #include <sys/conf.h>
50 #include <sys/fcntl.h>
51 #include <sys/ioccom.h>
52 #include <sys/kernel.h>
53 #include <sys/malloc.h>
54 #include <sys/memrange.h>
55 #include <sys/proc.h>
56 #include <sys/signalvar.h>
57 #include <sys/systm.h>
58 #include <sys/uio.h>
59 
60 #include <machine/db_machdep.h>
61 #include <machine/frame.h>
62 #include <machine/psl.h>
63 #include <machine/specialreg.h>
64 
65 #include <vm/vm.h>
66 #include <vm/pmap.h>
67 #include <vm/vm_extern.h>
68 
69 static dev_t memdev, kmemdev, iodev;
70 
71 static	d_open_t	mmopen;
72 static	d_close_t	mmclose;
73 static	d_read_t	mmrw;
74 static	d_ioctl_t	mmioctl;
75 static	d_mmap_t	memmmap;
76 
77 #define CDEV_MAJOR 2
78 static struct cdevsw mem_cdevsw = {
79 	/* open */	mmopen,
80 	/* close */	mmclose,
81 	/* read */	mmrw,
82 	/* write */	mmrw,
83 	/* ioctl */	mmioctl,
84 	/* poll */	(d_poll_t *)seltrue,
85 	/* mmap */	memmmap,
86 	/* strategy */	nostrategy,
87 	/* name */	"mem",
88 	/* maj */	CDEV_MAJOR,
89 	/* dump */	nodump,
90 	/* psize */	nopsize,
91 	/* flags */	D_MEM,
92 };
93 
94 MALLOC_DEFINE(M_MEMDESC, "memdesc", "memory range descriptors");
95 
96 struct mem_range_softc mem_range_softc;
97 
98 static int
99 mmclose(dev_t dev, int flags, int fmt, struct proc *p)
100 {
101 	switch (minor(dev)) {
102 	case 14:
103 		p->p_md.md_regs->tf_eflags &= ~PSL_IOPL;
104 	}
105 	return (0);
106 }
107 
108 static int
109 mmopen(dev_t dev, int flags, int fmt, struct proc *p)
110 {
111 	int error;
112 
113 	switch (minor(dev)) {
114 	case 0:
115 	case 1:
116 		if ((flags & FWRITE) && securelevel > 0)
117 			return (EPERM);
118 		break;
119 	case 14:
120 		error = suser(p);
121 		if (error != 0)
122 			return (error);
123 		if (securelevel > 0)
124 			return (EPERM);
125 		p->p_md.md_regs->tf_eflags |= PSL_IOPL;
126 		break;
127 	}
128 	return (0);
129 }
130 
131 /*ARGSUSED*/
132 static int
133 mmrw(dev_t dev, struct uio *uio, int flags)
134 {
135 	int o;
136 	u_int c = 0, v;
137 	struct iovec *iov;
138 	int error = 0;
139 	vm_offset_t addr, eaddr;
140 
141 	while (uio->uio_resid > 0 && error == 0) {
142 		iov = uio->uio_iov;
143 		if (iov->iov_len == 0) {
144 			uio->uio_iov++;
145 			uio->uio_iovcnt--;
146 			if (uio->uio_iovcnt < 0)
147 				panic("mmrw");
148 			continue;
149 		}
150 		switch (minor(dev)) {
151 
152 /* minor device 0 is physical memory */
153 		case 0:
154 			v = uio->uio_offset;
155 			v &= ~PAGE_MASK;
156 			pmap_kenter((vm_offset_t)ptvmmap, v);
157 			o = (int)uio->uio_offset & PAGE_MASK;
158 			c = (u_int)(PAGE_SIZE - ((int)iov->iov_base & PAGE_MASK));
159 			c = min(c, (u_int)(PAGE_SIZE - o));
160 			c = min(c, (u_int)iov->iov_len);
161 			error = uiomove((caddr_t)&ptvmmap[o], (int)c, uio);
162 			pmap_kremove((vm_offset_t)ptvmmap);
163 			continue;
164 
165 /* minor device 1 is kernel memory */
166 		case 1:
167 			c = iov->iov_len;
168 
169 			/*
170 			 * Make sure that all of the pages are currently resident so
171 			 * that we don't create any zero-fill pages.
172 			 */
173 			addr = trunc_page(uio->uio_offset);
174 			eaddr = round_page(uio->uio_offset + c);
175 
176 			if (addr < (vm_offset_t)VADDR(PTDPTDI, 0))
177 				return EFAULT;
178 			if (eaddr >= (vm_offset_t)VADDR(APTDPTDI, 0))
179 				return EFAULT;
180 			for (; addr < eaddr; addr += PAGE_SIZE)
181 				if (pmap_extract(kernel_pmap, addr) == 0)
182 					return EFAULT;
183 
184 			if (!kernacc((caddr_t)(int)uio->uio_offset, c,
185 			    uio->uio_rw == UIO_READ ?
186 			    VM_PROT_READ : VM_PROT_WRITE))
187 				return (EFAULT);
188 			error = uiomove((caddr_t)(int)uio->uio_offset, (int)c, uio);
189 			continue;
190 		}
191 
192 		if (error)
193 			break;
194 		iov->iov_base += c;
195 		iov->iov_len -= c;
196 		uio->uio_offset += c;
197 		uio->uio_resid -= c;
198 	}
199 	return (error);
200 }
201 
202 /*******************************************************\
203 * allow user processes to MMAP some memory sections	*
204 * instead of going through read/write			*
205 \*******************************************************/
206 static int
207 memmmap(dev_t dev, vm_offset_t offset, int prot)
208 {
209 	switch (minor(dev))
210 	{
211 
212 	/* minor device 0 is physical memory */
213 	case 0:
214         	return i386_btop(offset);
215 
216 	/* minor device 1 is kernel memory */
217 	case 1:
218         	return i386_btop(vtophys(offset));
219 
220 	default:
221 		return -1;
222 	}
223 }
224 
225 /*
226  * Operations for changing memory attributes.
227  *
228  * This is basically just an ioctl shim for mem_range_attr_get
229  * and mem_range_attr_set.
230  */
231 static int
232 mmioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
233 {
234 	int nd, error = 0;
235 	struct mem_range_op *mo = (struct mem_range_op *)data;
236 	struct mem_range_desc *md;
237 
238 	/* is this for us? */
239 	if ((cmd != MEMRANGE_GET) &&
240 	    (cmd != MEMRANGE_SET))
241 		return (ENOTTY);
242 
243 	/* any chance we can handle this? */
244 	if (mem_range_softc.mr_op == NULL)
245 		return (EOPNOTSUPP);
246 
247 	/* do we have any descriptors? */
248 	if (mem_range_softc.mr_ndesc == 0)
249 		return (ENXIO);
250 
251 	switch (cmd) {
252 	case MEMRANGE_GET:
253 		nd = imin(mo->mo_arg[0], mem_range_softc.mr_ndesc);
254 		if (nd > 0) {
255 			md = (struct mem_range_desc *)
256 				malloc(nd * sizeof(struct mem_range_desc),
257 				       M_MEMDESC, M_WAITOK);
258 			error = mem_range_attr_get(md, &nd);
259 			if (!error)
260 				error = copyout(md, mo->mo_desc,
261 					nd * sizeof(struct mem_range_desc));
262 			free(md, M_MEMDESC);
263 		} else {
264 			nd = mem_range_softc.mr_ndesc;
265 		}
266 		mo->mo_arg[0] = nd;
267 		break;
268 
269 	case MEMRANGE_SET:
270 		md = (struct mem_range_desc *)malloc(sizeof(struct mem_range_desc),
271 						    M_MEMDESC, M_WAITOK);
272 		error = copyin(mo->mo_desc, md, sizeof(struct mem_range_desc));
273 		/* clamp description string */
274 		md->mr_owner[sizeof(md->mr_owner) - 1] = 0;
275 		if (error == 0)
276 			error = mem_range_attr_set(md, &mo->mo_arg[0]);
277 		free(md, M_MEMDESC);
278 		break;
279 	}
280 	return (error);
281 }
282 
283 /*
284  * Implementation-neutral, kernel-callable functions for manipulating
285  * memory range attributes.
286  */
287 int
288 mem_range_attr_get(struct mem_range_desc *mrd, int *arg)
289 {
290 	/* can we handle this? */
291 	if (mem_range_softc.mr_op == NULL)
292 		return (EOPNOTSUPP);
293 
294 	if (*arg == 0) {
295 		*arg = mem_range_softc.mr_ndesc;
296 	}
297 	else {
298 		bcopy(mem_range_softc.mr_desc, mrd,
299 			(*arg) * sizeof(struct mem_range_desc));
300 	}
301 	return (0);
302 }
303 
304 int
305 mem_range_attr_set(struct mem_range_desc *mrd, int *arg)
306 {
307 	/* can we handle this? */
308 	if (mem_range_softc.mr_op == NULL)
309 		return (EOPNOTSUPP);
310 
311 	return (mem_range_softc.mr_op->set(&mem_range_softc, mrd, arg));
312 }
313 
314 #ifdef SMP
315 void
316 mem_range_AP_init(void)
317 {
318 	if (mem_range_softc.mr_op && mem_range_softc.mr_op->initAP)
319 		return (mem_range_softc.mr_op->initAP(&mem_range_softc));
320 }
321 #endif
322 
323 static int
324 mem_modevent(module_t mod, int type, void *data)
325 {
326 	switch(type) {
327 	case MOD_LOAD:
328 		if (bootverbose)
329 			printf("mem: <memory & I/O>\n");
330 		/* Initialise memory range handling */
331 		if (mem_range_softc.mr_op != NULL)
332 			mem_range_softc.mr_op->init(&mem_range_softc);
333 
334 		memdev = make_dev(&mem_cdevsw, 0, UID_ROOT, GID_KMEM,
335 			0640, "mem");
336 		kmemdev = make_dev(&mem_cdevsw, 1, UID_ROOT, GID_KMEM,
337 			0640, "kmem");
338 		iodev = make_dev(&mem_cdevsw, 14, UID_ROOT, GID_WHEEL,
339 			0600, "io");
340 		return 0;
341 
342 	case MOD_UNLOAD:
343 		destroy_dev(memdev);
344 		destroy_dev(kmemdev);
345 		destroy_dev(iodev);
346 		return 0;
347 
348 	case MOD_SHUTDOWN:
349 		return 0;
350 
351 	default:
352 		return EOPNOTSUPP;
353 	}
354 }
355 
356 DEV_MODULE(mem, mem_modevent, NULL);
357