xref: /minix3/minix/servers/vm/mmap.c (revision b80da2a01d0bb632707b7b4e974aa32eaebbcc6f)
1 
2 #define _SYSTEM 1
3 
4 #include <minix/callnr.h>
5 #include <minix/com.h>
6 #include <minix/config.h>
7 #include <minix/const.h>
8 #include <minix/ds.h>
9 #include <minix/endpoint.h>
10 #include <minix/minlib.h>
11 #include <minix/type.h>
12 #include <minix/ipc.h>
13 #include <minix/sysutil.h>
14 #include <minix/syslib.h>
15 #include <minix/safecopies.h>
16 #include <minix/bitmap.h>
17 #include <minix/debug.h>
18 
19 #include <machine/vmparam.h>
20 
21 #include <sys/mman.h>
22 #include <sys/param.h>
23 
24 #include <errno.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <env.h>
28 #include <stdio.h>
29 #include <fcntl.h>
30 
31 #include "glo.h"
32 #include "proto.h"
33 #include "util.h"
34 #include "region.h"
35 
36 
37 static struct vir_region *mmap_region(struct vmproc *vmp, vir_bytes addr,
38 	u32_t vmm_flags, size_t len, u32_t vrflags,
39 	mem_type_t *mt, int execpriv)
40 {
41 	u32_t mfflags = 0;
42 	struct vir_region *vr = NULL;
43 
44 	if(vmm_flags & MAP_LOWER16M) vrflags |= VR_LOWER16MB;
45 	if(vmm_flags & MAP_LOWER1M)  vrflags |= VR_LOWER1MB;
46 	if(vmm_flags & MAP_ALIGNMENT_64KB) vrflags |= VR_PHYS64K;
47 	if(vmm_flags & MAP_PREALLOC) mfflags |= MF_PREALLOC;
48 	if(vmm_flags & MAP_UNINITIALIZED) {
49 		if(!execpriv) return NULL;
50 		vrflags |= VR_UNINITIALIZED;
51 	}
52 
53 	if(len <= 0) {
54 		return NULL;
55 	}
56 
57 	if(len % VM_PAGE_SIZE)
58 		len += VM_PAGE_SIZE - (len % VM_PAGE_SIZE);
59 
60 	if (addr && (vmm_flags & MAP_FIXED)) {
61 		int r = map_unmap_range(vmp, addr, len);
62 		if(r != OK) {
63 			printf("mmap_region: map_unmap_range failed (%d)\n", r);
64 			return NULL;
65 		}
66 	}
67 
68 	if (addr || (vmm_flags & MAP_FIXED)) {
69 		/* An address is given, first try at that address. */
70 		vr = map_page_region(vmp, addr, 0, len,
71 			vrflags, mfflags, mt);
72 		if(!vr && (vmm_flags & MAP_FIXED))
73 			return NULL;
74 	}
75 
76 	if (!vr) {
77 		/* No address given or address already in use. */
78 		vr = map_page_region(vmp, VM_MMAPBASE, VM_MMAPTOP, len,
79 			vrflags, mfflags, mt);
80 	}
81 
82 	return vr;
83 }
84 
85 static int mmap_file(struct vmproc *vmp,
86 	int vmfd, off_t file_offset, int flags,
87 	ino_t ino, dev_t dev, u64_t filesize, vir_bytes addr, vir_bytes len,
88 	vir_bytes *retaddr, u16_t clearend, int writable, int mayclosefd)
89 {
90 /* VFS has replied to a VMVFSREQ_FDLOOKUP request. */
91 	struct vir_region *vr;
92 	u64_t page_offset;
93 	int result = OK;
94 	u32_t vrflags = 0;
95 
96 	if(writable) vrflags |= VR_WRITABLE;
97 
98 	/* Do some page alignments. */
99 	if((page_offset = (file_offset % VM_PAGE_SIZE))) {
100 		file_offset -= page_offset;
101 		len += page_offset;
102 	}
103 
104 	len = roundup(len, VM_PAGE_SIZE);
105 
106 	/* All numbers should be page-aligned now. */
107 	assert(!(len % VM_PAGE_SIZE));
108 	assert(!(filesize % VM_PAGE_SIZE));
109 	assert(!(file_offset % VM_PAGE_SIZE));
110 
111 #if 0
112 	/* XXX ld.so relies on longer-than-file mapping */
113 	if((u64_t) len + file_offset > filesize) {
114 		printf("VM: truncating mmap dev 0x%x ino %d beyond file size in %d; offset %llu, len %lu, size %llu; ",
115 			dev, ino, vmp->vm_endpoint,
116 			file_offset, len, filesize);
117 		len = filesize - file_offset;
118 		return EINVAL;
119 	}
120 #endif
121 
122 	if(!(vr = mmap_region(vmp, addr, flags, len,
123 		vrflags, &mem_type_mappedfile, 0))) {
124 		result = ENOMEM;
125 	} else {
126 		*retaddr = vr->vaddr + page_offset;
127 		result = OK;
128 
129 		mappedfile_setfile(vmp, vr, vmfd,
130 			file_offset, dev, ino, clearend, 1, mayclosefd);
131 	}
132 
133 	return result;
134 }
135 
136 int do_vfs_mmap(message *m)
137 {
138 	vir_bytes v;
139 	struct vmproc *vmp;
140 	int r, n;
141 	u16_t clearend, flags = 0;
142 
143 	/* It might be disabled */
144 	if(!enable_filemap) return ENXIO;
145 
146 	clearend = m->m_vm_vfs_mmap.clearend;
147 	flags = m->m_vm_vfs_mmap.flags;
148 
149 	if((r=vm_isokendpt(m->m_vm_vfs_mmap.who, &n)) != OK)
150 		panic("bad ep %d from vfs", m->m_vm_vfs_mmap.who);
151 	vmp = &vmproc[n];
152 
153 	return mmap_file(vmp, m->m_vm_vfs_mmap.fd, m->m_vm_vfs_mmap.offset,
154 		MAP_PRIVATE | MAP_FIXED,
155 		m->m_vm_vfs_mmap.ino, m->m_vm_vfs_mmap.dev,
156 		(u64_t) LONG_MAX * VM_PAGE_SIZE,
157 		m->m_vm_vfs_mmap.vaddr, m->m_vm_vfs_mmap.len, &v,
158 		clearend, flags, 0);
159 }
160 
161 static void mmap_file_cont(struct vmproc *vmp, message *replymsg, void *cbarg,
162 	void *origmsg_v)
163 {
164 	message *origmsg = (message *) origmsg_v;
165 	message mmap_reply;
166 	int result;
167 	int writable = 0;
168 	vir_bytes v = (vir_bytes) MAP_FAILED;
169 
170 	if(origmsg->m_mmap.prot & PROT_WRITE)
171 		writable = 1;
172 
173 	if(replymsg->VMV_RESULT != OK) {
174 #if 0   /* Noisy diagnostic for mmap() by ld.so */
175 		printf("VM: VFS reply failed (%d)\n", replymsg->VMV_RESULT);
176 		sys_diagctl_stacktrace(vmp->vm_endpoint);
177 #endif
178 		result = replymsg->VMV_RESULT;
179 	} else {
180 		/* Finish mmap */
181 		result = mmap_file(vmp, replymsg->VMV_FD, origmsg->m_mmap.offset,
182 			origmsg->m_mmap.flags,
183 			replymsg->VMV_INO, replymsg->VMV_DEV,
184 			(u64_t) replymsg->VMV_SIZE_PAGES*PAGE_SIZE,
185 			(vir_bytes) origmsg->m_mmap.addr,
186 			origmsg->m_mmap.len, &v, 0, writable, 1);
187 	}
188 
189 	/* Unblock requesting process. */
190 	memset(&mmap_reply, 0, sizeof(mmap_reply));
191 	mmap_reply.m_type = result;
192 	mmap_reply.m_mmap.retaddr = (void *) v;
193 
194 	if(ipc_send(vmp->vm_endpoint, &mmap_reply) != OK)
195 		panic("VM: mmap_file_cont: ipc_send() failed");
196 }
197 
198 /*===========================================================================*
199  *				do_mmap			     		     *
200  *===========================================================================*/
201 int do_mmap(message *m)
202 {
203 	int r, n;
204 	struct vmproc *vmp;
205 	vir_bytes addr = (vir_bytes) m->m_mmap.addr;
206 	struct vir_region *vr = NULL;
207 	int execpriv = 0;
208 	size_t len = (vir_bytes) m->m_mmap.len;
209 
210 	/* RS and VFS can do slightly more special mmap() things */
211 	if(m->m_source == VFS_PROC_NR || m->m_source == RS_PROC_NR)
212 		execpriv = 1;
213 
214 	if(m->m_mmap.flags & MAP_THIRDPARTY) {
215 		if(!execpriv) return EPERM;
216 		if((r=vm_isokendpt(m->m_mmap.forwhom, &n)) != OK)
217 			return ESRCH;
218 	} else {
219 		/* regular mmap, i.e. for caller */
220 		if((r=vm_isokendpt(m->m_source, &n)) != OK) {
221 			panic("do_mmap: message from strange source: %d",
222 				m->m_source);
223 		}
224 	}
225 
226 	vmp = &vmproc[n];
227 
228 	/* "SUSv3 specifies that mmap() should fail if length is 0" */
229 	if(len <= 0) {
230 		return EINVAL;
231 	}
232 
233 	if(m->m_mmap.fd == -1 || (m->m_mmap.flags & MAP_ANON)) {
234 		/* actual memory in some form */
235 		mem_type_t *mt = NULL;
236 
237 		if(m->m_mmap.fd != -1) {
238 			printf("VM: mmap: fd %d, len 0x%zx\n", m->m_mmap.fd, len);
239 			return EINVAL;
240 		}
241 
242 		/* Contiguous phys memory has to be preallocated. */
243 		if((m->m_mmap.flags & (MAP_CONTIG|MAP_PREALLOC)) == MAP_CONTIG) {
244 			return EINVAL;
245 		}
246 
247 		if(m->m_mmap.flags & MAP_CONTIG) {
248 			mt = &mem_type_anon_contig;
249 		} else	mt = &mem_type_anon;
250 
251 		if(!(vr = mmap_region(vmp, addr, m->m_mmap.flags, len,
252 			VR_WRITABLE | VR_ANON, mt, execpriv))) {
253 			return ENOMEM;
254 		}
255 	} else {
256 		/* File mapping might be disabled */
257 		if(!enable_filemap) return ENXIO;
258 
259 		/* For files, we only can't accept writable MAP_SHARED
260 		 * mappings.
261 		 */
262 		if((m->m_mmap.flags & MAP_SHARED) && (m->m_mmap.prot & PROT_WRITE)) {
263 			return ENXIO;
264 		}
265 
266 		if(vfs_request(VMVFSREQ_FDLOOKUP, m->m_mmap.fd, vmp, 0, 0,
267 			mmap_file_cont, NULL, m, sizeof(*m)) != OK) {
268 			printf("VM: vfs_request for mmap failed\n");
269 			return ENXIO;
270 		}
271 
272 		/* request queued; don't reply. */
273 		return SUSPEND;
274 	}
275 
276 	/* Return mapping, as seen from process. */
277 	m->m_mmap.retaddr = (void *) vr->vaddr;
278 
279 	return OK;
280 }
281 
282 /*===========================================================================*
283  *				map_perm_check		     		     *
284  *===========================================================================*/
285 static int map_perm_check(endpoint_t caller, endpoint_t target,
286 	phys_bytes physaddr, phys_bytes len)
287 {
288 	int r;
289 
290 	/* TTY and memory are allowed to do anything.
291 	 * They have to be special cases as they have to be able to do
292 	 * anything; TTY even on behalf of anyone for the TIOCMAPMEM
293 	 * ioctl. MEM just for itself.
294 	 */
295 	if(caller == TTY_PROC_NR)
296 		return OK;
297 	if(caller == MEM_PROC_NR)
298 		return OK;
299 
300 	/* Anyone else needs explicit permission from the kernel (ultimately
301 	 * set by PCI).
302 	 */
303 	r = sys_privquery_mem(target, physaddr, len);
304 
305 	return r;
306 }
307 
308 /*===========================================================================*
309  *				do_map_phys		     		     *
310  *===========================================================================*/
311 int do_map_phys(message *m)
312 {
313 	int r, n;
314 	struct vmproc *vmp;
315 	endpoint_t target;
316 	struct vir_region *vr;
317 	vir_bytes len;
318 	phys_bytes startaddr;
319 	size_t offset;
320 
321 	target = m->m_lsys_vm_map_phys.ep;
322 	len = m->m_lsys_vm_map_phys.len;
323 
324 	if (len <= 0) return EINVAL;
325 
326 	if(target == SELF)
327 		target = m->m_source;
328 
329 	if((r=vm_isokendpt(target, &n)) != OK)
330 		return EINVAL;
331 
332 	startaddr = (vir_bytes)m->m_lsys_vm_map_phys.phaddr;
333 
334 	/* First check permission, then round range down/up. Caller can't
335 	 * help it if we can't map in lower than page granularity.
336 	 */
337 	if(map_perm_check(m->m_source, target, startaddr, len) != OK) {
338 		printf("VM: unauthorized mapping of 0x%lx by %d for %d\n",
339 			startaddr, m->m_source, target);
340 		return EPERM;
341 	}
342 
343 	vmp = &vmproc[n];
344 
345 	offset = startaddr % VM_PAGE_SIZE;
346 	len += offset;
347 	startaddr -= offset;
348 
349 	if(len % VM_PAGE_SIZE)
350 		len += VM_PAGE_SIZE - (len % VM_PAGE_SIZE);
351 
352 	if(!(vr = map_page_region(vmp, VM_MMAPBASE, VM_MMAPTOP, len,
353 		VR_DIRECT | VR_WRITABLE, 0, &mem_type_directphys))) {
354 		return ENOMEM;
355 	}
356 
357 	phys_setphys(vr, startaddr);
358 
359 	m->m_lsys_vm_map_phys.reply = (void *) (vr->vaddr + offset);
360 
361 	return OK;
362 }
363 
364 /*===========================================================================*
365  *				do_remap		     		     *
366  *===========================================================================*/
367 int do_remap(message *m)
368 {
369 	int dn, sn;
370 	vir_bytes da, sa;
371 	size_t size;
372 	u32_t flags;
373 	struct vir_region *src_region, *vr;
374 	struct vmproc *dvmp, *svmp;
375 	int r;
376 	int readonly;
377 
378 	if(m->m_type == VM_REMAP)
379 		readonly = 0;
380 	else if(m->m_type == VM_REMAP_RO)
381 		readonly = 1;
382 	else panic("do_remap: can't be");
383 
384 	da = (vir_bytes) m->m_lsys_vm_vmremap.dest_addr;
385 	sa = (vir_bytes) m->m_lsys_vm_vmremap.src_addr;
386 	size = m->m_lsys_vm_vmremap.size;
387 
388 	if (size <= 0) return EINVAL;
389 
390 	if ((r = vm_isokendpt((endpoint_t) m->m_lsys_vm_vmremap.destination, &dn)) != OK)
391 		return EINVAL;
392 	if ((r = vm_isokendpt((endpoint_t) m->m_lsys_vm_vmremap.source, &sn)) != OK)
393 		return EINVAL;
394 
395 	dvmp = &vmproc[dn];
396 	svmp = &vmproc[sn];
397 
398 	if (!(src_region = map_lookup(svmp, sa, NULL)))
399 		return EINVAL;
400 
401 	if(src_region->vaddr != sa) {
402 		printf("VM: do_remap: not start of region.\n");
403 		return EFAULT;
404 	}
405 
406 	if (size % VM_PAGE_SIZE)
407 		size += VM_PAGE_SIZE - size % VM_PAGE_SIZE;
408 
409 	if(size != src_region->length) {
410 		printf("VM: do_remap: not size of region.\n");
411 		return EFAULT;
412 	}
413 
414 	flags = VR_SHARED;
415 	if(!readonly)
416 		flags |= VR_WRITABLE;
417 
418 	if(da)
419 		vr = map_page_region(dvmp, da, 0, size, flags, 0,
420 			&mem_type_shared);
421 	else
422 		vr = map_page_region(dvmp, VM_MMAPBASE, VM_MMAPTOP, size,
423 			flags, 0, &mem_type_shared);
424 
425 	if(!vr) {
426 		printf("VM: re-map of shared area failed\n");
427 		return ENOMEM;
428 	}
429 
430 	shared_setsource(vr, svmp->vm_endpoint, src_region);
431 
432 	m->m_lsys_vm_vmremap.ret_addr = (void *) vr->vaddr;
433 	return OK;
434 }
435 
436 /*===========================================================================*
437  *				do_get_phys		     		     *
438  *===========================================================================*/
439 int do_get_phys(message *m)
440 {
441 	int r, n;
442 	struct vmproc *vmp;
443 	endpoint_t target;
444 	phys_bytes ret;
445 	vir_bytes addr;
446 
447 	target = m->m_lc_vm_getphys.endpt;
448 	addr = (vir_bytes) m->m_lc_vm_getphys.addr;
449 
450 	if ((r = vm_isokendpt(target, &n)) != OK)
451 		return EINVAL;
452 
453 	vmp = &vmproc[n];
454 
455 	r = map_get_phys(vmp, addr, &ret);
456 
457 	m->m_lc_vm_getphys.ret_addr = (void *) ret;
458 	return r;
459 }
460 
461 /*===========================================================================*
462  *				do_get_refcount		     		     *
463  *===========================================================================*/
464 int do_get_refcount(message *m)
465 {
466 	int r, n;
467 	struct vmproc *vmp;
468 	endpoint_t target;
469 	u8_t cnt;
470 	vir_bytes addr;
471 
472 	target = m->m_lsys_vm_getref.endpt;
473 	addr = (vir_bytes) m->m_lsys_vm_getref.addr;
474 
475 	if ((r = vm_isokendpt(target, &n)) != OK)
476 		return EINVAL;
477 
478 	vmp = &vmproc[n];
479 
480 	r = map_get_ref(vmp, addr, &cnt);
481 
482 	m->m_lsys_vm_getref.retc = cnt;
483 	return r;
484 }
485 
486 /*===========================================================================*
487  *                             munmap_vm_lin                                 *
488  *===========================================================================*/
489 int munmap_vm_lin(vir_bytes addr, size_t len)
490 {
491 	if(addr % VM_PAGE_SIZE) {
492 		printf("munmap_vm_lin: offset not page aligned\n");
493 		return EFAULT;
494 	}
495 
496 	if(len % VM_PAGE_SIZE) {
497 		printf("munmap_vm_lin: len not page aligned\n");
498 		return EFAULT;
499 	}
500 
501 	if(pt_writemap(NULL, &vmproc[VM_PROC_NR].vm_pt, addr, MAP_NONE, len, 0,
502 		WMF_OVERWRITE | WMF_FREE) != OK) {
503 		printf("munmap_vm_lin: pt_writemap failed\n");
504 		return EFAULT;
505 	}
506 
507 	return OK;
508 }
509 
510 /*===========================================================================*
511  *                              do_munmap                                    *
512  *===========================================================================*/
513 int do_munmap(message *m)
514 {
515         int r, n;
516         struct vmproc *vmp;
517 	struct vir_region *vr;
518         vir_bytes addr, len;
519 	endpoint_t target = SELF;
520 
521 	if(m->m_type == VM_UNMAP_PHYS) {
522 		target = m->m_lsys_vm_unmap_phys.ep;
523 	} else if(m->m_type == VM_SHM_UNMAP) {
524 		target = m->m_lc_vm_shm_unmap.forwhom;
525 	}
526 
527 	if(target == SELF)
528 		target = m->m_source;
529 
530         if((r=vm_isokendpt(target, &n)) != OK) {
531                 panic("do_mmap: message from strange source: %d", m->m_source);
532         }
533 
534         vmp = &vmproc[n];
535 
536 	if(m->m_source == VM_PROC_NR) {
537 		/* VM munmap is a special case, the region we want to
538 		 * munmap may or may not be there in our data structures,
539 		 * depending on whether this is an updated VM instance or not.
540 		 */
541 		if(!region_search_root(&vmp->vm_regions_avl)) {
542 			munmap_vm_lin(addr, m->VMUM_LEN);
543 		}
544 		else if((vr = map_lookup(vmp, addr, NULL))) {
545 			if(map_unmap_region(vmp, vr, 0, m->VMUM_LEN) != OK) {
546 				printf("VM: self map_unmap_region failed\n");
547 			}
548 		}
549 		return SUSPEND;
550 	}
551 
552 	if(m->m_type == VM_UNMAP_PHYS) {
553 		addr = (vir_bytes) m->m_lsys_vm_unmap_phys.vaddr;
554 	} else if(m->m_type == VM_SHM_UNMAP) {
555 		addr = (vir_bytes) m->m_lc_vm_shm_unmap.addr;
556 	} else	addr = (vir_bytes) m->VMUM_ADDR;
557 
558 	if(addr % VM_PAGE_SIZE)
559 		return EFAULT;
560 
561 	if(m->m_type == VM_UNMAP_PHYS || m->m_type == VM_SHM_UNMAP) {
562 		struct vir_region *vr;
563 	        if(!(vr = map_lookup(vmp, addr, NULL))) {
564 			printf("VM: unmap: address 0x%lx not found in %d\n",
565 	                       addr, target);
566 			sys_diagctl_stacktrace(target);
567 	                return EFAULT;
568 		}
569 		len = vr->length;
570 	} else len = roundup(m->VMUM_LEN, VM_PAGE_SIZE);
571 
572 	return map_unmap_range(vmp, addr, len);
573 }
574 
575