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