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
mmap_region(struct vmproc * vmp,vir_bytes addr,u32_t vmm_flags,size_t len,u32_t vrflags,mem_type_t * mt,int execpriv)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
mmap_file(struct vmproc * vmp,int vmfd,off_t file_offset,int flags,ino_t ino,dev_t dev,u64_t filesize,vir_bytes addr,vir_bytes len,vir_bytes * retaddr,u16_t clearend,int writable,int mayclosefd)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
do_vfs_mmap(message * m)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
mmap_file_cont(struct vmproc * vmp,message * replymsg,void * cbarg,void * origmsg_v)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 *===========================================================================*/
do_mmap(message * m)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 *===========================================================================*/
map_perm_check(endpoint_t caller,endpoint_t target,phys_bytes physaddr,phys_bytes len)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 *===========================================================================*/
do_map_phys(message * m)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 *===========================================================================*/
do_remap(message * m)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 *===========================================================================*/
do_get_phys(message * m)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 *===========================================================================*/
do_get_refcount(message * m)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 *===========================================================================*/
munmap_vm_lin(vir_bytes addr,size_t len)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 *===========================================================================*/
do_munmap(message * m)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