Remove building with NOCRYPTO option
[minix.git] / minix / servers / vm / mmap.c
blobb1394a29e5b6d016a286ba98bd644f6f2fd24ccd
2 #define _SYSTEM 1
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>
19 #include <machine/vmparam.h>
21 #include <sys/mman.h>
22 #include <sys/param.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <fcntl.h>
30 #include "glo.h"
31 #include "proto.h"
32 #include "util.h"
33 #include "region.h"
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)
40 u32_t mfflags = 0;
41 struct vir_region *vr = NULL;
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;
52 if(len <= 0) {
53 return NULL;
56 if(len % VM_PAGE_SIZE)
57 len += VM_PAGE_SIZE - (len % VM_PAGE_SIZE);
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;
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;
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);
81 return vr;
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)
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;
95 if(writable) vrflags |= VR_WRITABLE;
97 /* Do some page alignments. */
98 if((page_offset = (file_offset % VM_PAGE_SIZE))) {
99 file_offset -= page_offset;
100 len += page_offset;
103 len = roundup(len, VM_PAGE_SIZE);
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));
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;
119 #endif
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;
128 mappedfile_setfile(vmp, vr, vmfd,
129 file_offset, dev, ino, clearend, 1, mayclosefd);
132 return result;
135 int do_vfs_mmap(message *m)
137 vir_bytes v;
138 struct vmproc *vmp;
139 int r, n;
140 u16_t clearend, flags = 0;
142 /* It might be disabled */
143 if(!enable_filemap) return ENXIO;
145 clearend = m->m_vm_vfs_mmap.clearend;
146 flags = m->m_vm_vfs_mmap.flags;
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];
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);
160 static void mmap_file_cont(struct vmproc *vmp, message *replymsg, void *cbarg,
161 void *origmsg_v)
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;
169 if(origmsg->m_mmap.prot & PROT_WRITE)
170 writable = 1;
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);
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;
193 if(ipc_send(vmp->vm_endpoint, &mmap_reply) != OK)
194 panic("VM: mmap_file_cont: ipc_send() failed");
197 /*===========================================================================*
198 * do_mmap *
199 *===========================================================================*/
200 int do_mmap(message *m)
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;
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;
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);
225 vmp = &vmproc[n];
227 /* "SUSv3 specifies that mmap() should fail if length is 0" */
228 if(len <= 0) {
229 return EINVAL;
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;
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;
241 /* Contiguous phys memory has to be preallocated. */
242 if((m->m_mmap.flags & (MAP_CONTIG|MAP_PREALLOC)) == MAP_CONTIG) {
243 return EINVAL;
246 if(m->m_mmap.flags & MAP_CONTIG) {
247 mt = &mem_type_anon_contig;
248 } else mt = &mem_type_anon;
250 if(!(vr = mmap_region(vmp, addr, m->m_mmap.flags, len,
251 VR_WRITABLE | VR_ANON, mt, execpriv))) {
252 return ENOMEM;
254 } else {
255 /* File mapping might be disabled */
256 if(!enable_filemap) return ENXIO;
258 /* For files, we only can't accept writable MAP_SHARED
259 * mappings.
261 if((m->m_mmap.flags & MAP_SHARED) && (m->m_mmap.prot & PROT_WRITE)) {
262 return ENXIO;
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;
271 /* request queued; don't reply. */
272 return SUSPEND;
275 /* Return mapping, as seen from process. */
276 m->m_mmap.retaddr = (void *) vr->vaddr;
278 return OK;
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)
287 int r;
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.
294 if(caller == TTY_PROC_NR)
295 return OK;
296 if(caller == MEM_PROC_NR)
297 return OK;
299 /* Anyone else needs explicit permission from the kernel (ultimately
300 * set by PCI).
302 r = sys_privquery_mem(target, physaddr, len);
304 return r;
307 /*===========================================================================*
308 * do_map_phys *
309 *===========================================================================*/
310 int do_map_phys(message *m)
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;
320 target = m->m_lsys_vm_map_phys.ep;
321 len = m->m_lsys_vm_map_phys.len;
323 if (len <= 0) return EINVAL;
325 if(target == SELF)
326 target = m->m_source;
328 if((r=vm_isokendpt(target, &n)) != OK)
329 return EINVAL;
331 startaddr = (vir_bytes)m->m_lsys_vm_map_phys.phaddr;
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.
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;
342 vmp = &vmproc[n];
344 offset = startaddr % VM_PAGE_SIZE;
345 len += offset;
346 startaddr -= offset;
348 if(len % VM_PAGE_SIZE)
349 len += VM_PAGE_SIZE - (len % VM_PAGE_SIZE);
351 if(!(vr = map_page_region(vmp, VM_MMAPBASE, VM_MMAPTOP, len,
352 VR_DIRECT | VR_WRITABLE, 0, &mem_type_directphys))) {
353 return ENOMEM;
356 phys_setphys(vr, startaddr);
358 m->m_lsys_vm_map_phys.reply = (void *) (vr->vaddr + offset);
360 return OK;
363 /*===========================================================================*
364 * do_remap *
365 *===========================================================================*/
366 int do_remap(message *m)
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;
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");
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;
387 if (size <= 0) return EINVAL;
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;
394 dvmp = &vmproc[dn];
395 svmp = &vmproc[sn];
397 if (!(src_region = map_lookup(svmp, sa, NULL)))
398 return EINVAL;
400 if(src_region->vaddr != sa) {
401 printf("VM: do_remap: not start of region.\n");
402 return EFAULT;
405 if (size % VM_PAGE_SIZE)
406 size += VM_PAGE_SIZE - size % VM_PAGE_SIZE;
408 if(size != src_region->length) {
409 printf("VM: do_remap: not size of region.\n");
410 return EFAULT;
413 flags = VR_SHARED;
414 if(!readonly)
415 flags |= VR_WRITABLE;
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);
424 if(!vr) {
425 printf("VM: re-map of shared area failed\n");
426 return ENOMEM;
429 shared_setsource(vr, svmp->vm_endpoint, src_region);
431 m->m_lsys_vm_vmremap.ret_addr = (void *) vr->vaddr;
432 return OK;
435 /*===========================================================================*
436 * do_get_phys *
437 *===========================================================================*/
438 int do_get_phys(message *m)
440 int r, n;
441 struct vmproc *vmp;
442 endpoint_t target;
443 phys_bytes ret;
444 vir_bytes addr;
446 target = m->m_lc_vm_getphys.endpt;
447 addr = (vir_bytes) m->m_lc_vm_getphys.addr;
449 if ((r = vm_isokendpt(target, &n)) != OK)
450 return EINVAL;
452 vmp = &vmproc[n];
454 r = map_get_phys(vmp, addr, &ret);
456 m->m_lc_vm_getphys.ret_addr = (void *) ret;
457 return r;
460 /*===========================================================================*
461 * do_get_refcount *
462 *===========================================================================*/
463 int do_get_refcount(message *m)
465 int r, n;
466 struct vmproc *vmp;
467 endpoint_t target;
468 u8_t cnt;
469 vir_bytes addr;
471 target = m->m_lsys_vm_getref.endpt;
472 addr = (vir_bytes) m->m_lsys_vm_getref.addr;
474 if ((r = vm_isokendpt(target, &n)) != OK)
475 return EINVAL;
477 vmp = &vmproc[n];
479 r = map_get_ref(vmp, addr, &cnt);
481 m->m_lsys_vm_getref.retc = cnt;
482 return r;
485 /*===========================================================================*
486 * munmap_vm_lin *
487 *===========================================================================*/
488 int munmap_vm_lin(vir_bytes addr, size_t len)
490 if(addr % VM_PAGE_SIZE) {
491 printf("munmap_vm_lin: offset not page aligned\n");
492 return EFAULT;
495 if(len % VM_PAGE_SIZE) {
496 printf("munmap_vm_lin: len not page aligned\n");
497 return EFAULT;
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;
506 return OK;
509 /*===========================================================================*
510 * do_munmap *
511 *===========================================================================*/
512 int do_munmap(message *m)
514 int r, n;
515 struct vmproc *vmp;
516 struct vir_region *vr;
517 vir_bytes addr, len;
518 endpoint_t target = SELF;
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;
526 if(target == SELF)
527 target = m->m_source;
529 if((r=vm_isokendpt(target, &n)) != OK) {
530 panic("do_mmap: message from strange source: %d", m->m_source);
533 vmp = &vmproc[n];
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.
540 if(!region_search_root(&vmp->vm_regions_avl)) {
541 munmap_vm_lin(addr, m->VMUM_LEN);
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");
548 return SUSPEND;
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;
557 if(addr % VM_PAGE_SIZE)
558 return EFAULT;
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;
568 len = vr->length;
569 } else len = roundup(m->VMUM_LEN, VM_PAGE_SIZE);
571 return map_unmap_range(vmp, addr, len);