1 2 /* This file contains some utility routines for VM. */ 3 4 #define _SYSTEM 1 5 6 #include <minix/callnr.h> 7 #include <minix/com.h> 8 #include <minix/config.h> 9 #include <minix/const.h> 10 #include <minix/ds.h> 11 #include <minix/endpoint.h> 12 #include <minix/minlib.h> 13 #include <minix/type.h> 14 #include <minix/ipc.h> 15 #include <minix/sysutil.h> 16 #include <minix/syslib.h> 17 #include <minix/type.h> 18 #include <minix/bitmap.h> 19 #include <minix/rs.h> 20 #include <string.h> 21 #include <errno.h> 22 #include <env.h> 23 #include <unistd.h> 24 #include <assert.h> 25 #include <sys/cdefs.h> 26 #include <sys/param.h> 27 #include <sys/mman.h> 28 #include <sys/resource.h> 29 30 #include "proto.h" 31 #include "glo.h" 32 #include "util.h" 33 #include "region.h" 34 #include "sanitycheck.h" 35 36 #include <machine/archtypes.h> 37 #include "kernel/const.h" 38 #include "kernel/config.h" 39 #include "kernel/type.h" 40 #include "kernel/proc.h" 41 42 /*===========================================================================* 43 * get_mem_chunks * 44 *===========================================================================*/ 45 void get_mem_chunks( 46 struct memory *mem_chunks) /* store mem chunks here */ 47 { 48 /* Initialize the free memory list from the kernel-provided memory map. Translate 49 * the byte offsets and sizes in this list to clicks, properly truncated. 50 */ 51 phys_bytes base, size, limit; 52 int i; 53 struct memory *memp; 54 55 /* Initialize everything to zero. */ 56 memset(mem_chunks, 0, NR_MEMS*sizeof(*mem_chunks)); 57 58 /* Obtain and parse memory from kernel environment. */ 59 /* XXX Any memory chunk in excess of NR_MEMS is silently ignored. */ 60 for(i = 0; i < MIN(MAXMEMMAP, NR_MEMS); i++) { 61 mem_chunks[i].base = kernel_boot_info.memmap[i].mm_base_addr; 62 mem_chunks[i].size = kernel_boot_info.memmap[i].mm_length; 63 } 64 65 /* Round physical memory to clicks. Round start up, round end down. */ 66 for (i = 0; i < NR_MEMS; i++) { 67 memp = &mem_chunks[i]; /* next mem chunk is stored here */ 68 base = mem_chunks[i].base; 69 size = mem_chunks[i].size; 70 limit = base + size; 71 base = (phys_bytes) (CLICK_CEIL(base)); 72 limit = (phys_bytes) (CLICK_FLOOR(limit)); 73 if (limit <= base) { 74 memp->base = memp->size = 0; 75 } else { 76 memp->base = base >> CLICK_SHIFT; 77 memp->size = (limit - base) >> CLICK_SHIFT; 78 } 79 } 80 } 81 82 /*===========================================================================* 83 * vm_isokendpt * 84 *===========================================================================*/ 85 int vm_isokendpt(endpoint_t endpoint, int *procn) 86 { 87 *procn = _ENDPOINT_P(endpoint); 88 if(*procn < 0 || *procn >= NR_PROCS) 89 return EINVAL; 90 if(*procn >= 0 && endpoint != vmproc[*procn].vm_endpoint) 91 return EDEADEPT; 92 if(*procn >= 0 && !(vmproc[*procn].vm_flags & VMF_INUSE)) 93 return EDEADEPT; 94 return OK; 95 } 96 97 98 /*===========================================================================* 99 * do_info * 100 *===========================================================================*/ 101 int do_info(message *m) 102 { 103 struct vm_stats_info vsi; 104 struct vm_usage_info vui; 105 static struct vm_region_info vri[MAX_VRI_COUNT]; 106 struct vmproc *vmp; 107 vir_bytes addr, size, next, ptr; 108 int r, pr, dummy, count, free_pages, largest_contig; 109 110 if (vm_isokendpt(m->m_source, &pr) != OK) 111 return EINVAL; 112 vmp = &vmproc[pr]; 113 114 ptr = (vir_bytes) m->m_lsys_vm_info.ptr; 115 116 switch(m->m_lsys_vm_info.what) { 117 case VMIW_STATS: 118 vsi.vsi_pagesize = VM_PAGE_SIZE; 119 vsi.vsi_total = total_pages; 120 memstats(&dummy, &free_pages, &largest_contig); 121 vsi.vsi_free = free_pages; 122 vsi.vsi_largest = largest_contig; 123 124 get_stats_info(&vsi); 125 126 addr = (vir_bytes) &vsi; 127 size = sizeof(vsi); 128 129 break; 130 131 case VMIW_USAGE: 132 if(m->m_lsys_vm_info.ep < 0) 133 get_usage_info_kernel(&vui); 134 else if (vm_isokendpt(m->m_lsys_vm_info.ep, &pr) != OK) 135 return EINVAL; 136 else get_usage_info(&vmproc[pr], &vui); 137 138 addr = (vir_bytes) &vui; 139 size = sizeof(vui); 140 141 break; 142 143 case VMIW_REGION: 144 if(m->m_lsys_vm_info.ep == SELF) { 145 m->m_lsys_vm_info.ep = m->m_source; 146 } 147 if (vm_isokendpt(m->m_lsys_vm_info.ep, &pr) != OK) 148 return EINVAL; 149 150 count = MIN(m->m_lsys_vm_info.count, MAX_VRI_COUNT); 151 next = m->m_lsys_vm_info.next; 152 153 count = get_region_info(&vmproc[pr], vri, count, &next); 154 155 m->m_lsys_vm_info.count = count; 156 m->m_lsys_vm_info.next = next; 157 158 addr = (vir_bytes) vri; 159 size = sizeof(vri[0]) * count; 160 161 break; 162 163 default: 164 return EINVAL; 165 } 166 167 if (size == 0) 168 return OK; 169 170 /* Make sure that no page faults can occur while copying out. A page 171 * fault would cause the kernel to send a notify to us, while we would 172 * be waiting for the result of the copy system call, resulting in a 173 * deadlock. Note that no memory mapping can be undone without the 174 * involvement of VM, so we are safe until we're done. 175 */ 176 r = handle_memory_once(vmp, ptr, size, 1 /*wrflag*/); 177 if (r != OK) return r; 178 179 /* Now that we know the copy out will succeed, perform the actual copy 180 * operation. 181 */ 182 return sys_datacopy(SELF, addr, 183 (vir_bytes) vmp->vm_endpoint, ptr, size); 184 } 185 186 /*===========================================================================* 187 * swap_proc_slot * 188 *===========================================================================*/ 189 int swap_proc_slot(struct vmproc *src_vmp, struct vmproc *dst_vmp) 190 { 191 struct vmproc orig_src_vmproc, orig_dst_vmproc; 192 193 #if LU_DEBUG 194 printf("VM: swap_proc: swapping %d (%d) and %d (%d)\n", 195 src_vmp->vm_endpoint, src_vmp->vm_slot, 196 dst_vmp->vm_endpoint, dst_vmp->vm_slot); 197 #endif 198 199 /* Save existing data. */ 200 orig_src_vmproc = *src_vmp; 201 orig_dst_vmproc = *dst_vmp; 202 203 /* Swap slots. */ 204 *src_vmp = orig_dst_vmproc; 205 *dst_vmp = orig_src_vmproc; 206 207 /* Preserve endpoints and slot numbers. */ 208 src_vmp->vm_endpoint = orig_src_vmproc.vm_endpoint; 209 src_vmp->vm_slot = orig_src_vmproc.vm_slot; 210 dst_vmp->vm_endpoint = orig_dst_vmproc.vm_endpoint; 211 dst_vmp->vm_slot = orig_dst_vmproc.vm_slot; 212 213 #if LU_DEBUG 214 printf("VM: swap_proc: swapped %d (%d) and %d (%d)\n", 215 src_vmp->vm_endpoint, src_vmp->vm_slot, 216 dst_vmp->vm_endpoint, dst_vmp->vm_slot); 217 #endif 218 219 return OK; 220 } 221 222 /* 223 * Transfer memory mapped regions, using CoW sharing, from 'src_vmp' to 224 * 'dst_vmp', for the source process's address range of 'start_addr' 225 * (inclusive) to 'end_addr' (exclusive). Return OK or an error code. 226 */ 227 static int 228 transfer_mmap_regions(struct vmproc *dst_vmp, struct vmproc *src_vmp, 229 vir_bytes start_addr, vir_bytes end_addr) 230 { 231 struct vir_region *start_vr, *end_vr; 232 233 start_vr = region_search(&src_vmp->vm_regions_avl, start_addr, 234 AVL_GREATER_EQUAL); 235 236 if (start_vr == NULL || start_vr->vaddr >= end_addr) 237 return OK; /* nothing to do */ 238 239 end_vr = region_search(&src_vmp->vm_regions_avl, end_addr, AVL_LESS); 240 assert(end_vr != NULL); 241 assert(start_vr->vaddr <= end_vr->vaddr); 242 243 #if LU_DEBUG 244 printf("VM: transfer_mmap_regions: transferring memory mapped regions " 245 "from %d to %d (0x%lx to 0x%lx)\n", src_vmp->vm_endpoint, 246 dst_vmp->vm_endpoint, start_vr->vaddr, end_vr->vaddr); 247 #endif 248 249 return map_proc_copy_range(dst_vmp, src_vmp, start_vr, end_vr); 250 } 251 252 /*===========================================================================* 253 * swap_proc_dyn_data * 254 *===========================================================================*/ 255 int swap_proc_dyn_data(struct vmproc *src_vmp, struct vmproc *dst_vmp, 256 int sys_upd_flags) 257 { 258 int is_vm; 259 int r; 260 261 is_vm = (dst_vmp->vm_endpoint == VM_PROC_NR); 262 263 /* For VM, transfer memory mapped regions first. */ 264 if(is_vm) { 265 #if LU_DEBUG 266 printf("VM: swap_proc_dyn_data: tranferring memory mapped regions from old (%d) to new VM (%d)\n", 267 src_vmp->vm_endpoint, dst_vmp->vm_endpoint); 268 #endif 269 r = pt_map_in_range(src_vmp, dst_vmp, VM_OWN_HEAPBASE, VM_OWN_MMAPTOP); 270 if(r != OK) { 271 printf("swap_proc_dyn_data: pt_map_in_range failed\n"); 272 return r; 273 } 274 r = pt_map_in_range(src_vmp, dst_vmp, VM_STACKTOP, VM_DATATOP); 275 if(r != OK) { 276 printf("swap_proc_dyn_data: pt_map_in_range failed\n"); 277 return r; 278 } 279 280 } 281 282 #if LU_DEBUG 283 printf("VM: swap_proc_dyn_data: swapping regions' parents for %d (%d) and %d (%d)\n", 284 src_vmp->vm_endpoint, src_vmp->vm_slot, 285 dst_vmp->vm_endpoint, dst_vmp->vm_slot); 286 #endif 287 288 /* Swap vir_regions' parents. */ 289 map_setparent(src_vmp); 290 map_setparent(dst_vmp); 291 292 /* Don't transfer mmapped regions if not required. */ 293 if(is_vm || (sys_upd_flags & (SF_VM_ROLLBACK|SF_VM_NOMMAP))) { 294 return OK; 295 } 296 297 /* Make sure regions are consistent. */ 298 assert(region_search_root(&src_vmp->vm_regions_avl) && region_search_root(&dst_vmp->vm_regions_avl)); 299 300 /* Transfer memory mapped regions now. To sandbox the new instance and 301 * prevent state corruption on rollback, we share all the regions 302 * between the two instances as COW. Source and destination are 303 * intentionally swapped in these calls! 304 */ 305 r = transfer_mmap_regions(src_vmp, dst_vmp, VM_MMAPBASE, VM_MMAPTOP); 306 307 /* If the stack is not mapped at the VM_DATATOP, there might be some 308 * more regions hiding above the stack. We also have to transfer 309 * those. 310 */ 311 if (r == OK && VM_STACKTOP < VM_DATATOP) 312 r = transfer_mmap_regions(src_vmp, dst_vmp, VM_STACKTOP, 313 VM_DATATOP); 314 315 return r; 316 } 317 318 void *mmap(void *addr, size_t len, int f, int f2, int f3, off_t o) 319 { 320 void *ret; 321 phys_bytes p; 322 323 assert(!addr); 324 assert(!(len % VM_PAGE_SIZE)); 325 326 ret = vm_allocpages(&p, VMP_SLAB, len/VM_PAGE_SIZE); 327 328 if(!ret) return MAP_FAILED; 329 memset(ret, 0, len); 330 return ret; 331 } 332 333 int munmap(void * addr, size_t len) 334 { 335 vm_freepages((vir_bytes) addr, roundup(len, VM_PAGE_SIZE)/VM_PAGE_SIZE); 336 return 0; 337 } 338 339 #ifdef __weak_alias 340 __weak_alias(brk, _brk) 341 #endif 342 int _brk(void *addr) 343 { 344 /* brk is a special case function to allow vm itself to 345 allocate memory in it's own (cacheable) HEAP */ 346 vir_bytes target = roundup((vir_bytes)addr, VM_PAGE_SIZE), v; 347 extern char _end; 348 extern char *_brksize; 349 static vir_bytes prevbrk = (vir_bytes) &_end; 350 struct vmproc *vmprocess = &vmproc[VM_PROC_NR]; 351 352 for(v = roundup(prevbrk, VM_PAGE_SIZE); v < target; 353 v += VM_PAGE_SIZE) { 354 phys_bytes mem, newpage = alloc_mem(1, 0); 355 if(newpage == NO_MEM) return -1; 356 mem = CLICK2ABS(newpage); 357 if(pt_writemap(vmprocess, &vmprocess->vm_pt, 358 v, mem, VM_PAGE_SIZE, 359 ARCH_VM_PTE_PRESENT 360 | ARCH_VM_PTE_USER 361 | ARCH_VM_PTE_RW 362 #if defined(__arm__) 363 | ARM_VM_PTE_CACHED 364 #endif 365 , 0) != OK) { 366 free_mem(newpage, 1); 367 return -1; 368 } 369 prevbrk = v + VM_PAGE_SIZE; 370 } 371 372 _brksize = (char *) addr; 373 374 if(sys_vmctl(SELF, VMCTL_FLUSHTLB, 0) != OK) 375 panic("flushtlb failed"); 376 377 return 0; 378 } 379 380 /*===========================================================================* 381 * do_getrusage * 382 *===========================================================================*/ 383 int do_getrusage(message *m) 384 { 385 int res, slot; 386 struct vmproc *vmp; 387 struct rusage r_usage; 388 if ((res = vm_isokendpt(m->m_source, &slot)) != OK) 389 return ESRCH; 390 391 vmp = &vmproc[slot]; 392 393 if ((res = sys_datacopy(m->m_source, m->m_lc_vm_rusage.addr, 394 SELF, (vir_bytes) &r_usage, (vir_bytes) sizeof(r_usage))) < 0) 395 return res; 396 397 r_usage.ru_maxrss = vmp->vm_total_max; 398 r_usage.ru_minflt = vmp->vm_minor_page_fault; 399 r_usage.ru_majflt = vmp->vm_major_page_fault; 400 401 return sys_datacopy(SELF, (vir_bytes) &r_usage, m->m_source, 402 m->m_lc_vm_rusage.addr, (vir_bytes) sizeof(r_usage)); 403 } 404 405 /*===========================================================================* 406 * adjust_proc_refs * 407 *===========================================================================*/ 408 void adjust_proc_refs() 409 { 410 struct vmproc *vmp; 411 region_iter iter; 412 413 /* Fix up region parents. */ 414 for(vmp = vmproc; vmp < &vmproc[VMP_NR]; vmp++) { 415 struct vir_region *vr; 416 if(!(vmp->vm_flags & VMF_INUSE)) 417 continue; 418 region_start_iter_least(&vmp->vm_regions_avl, &iter); 419 while((vr = region_get_iter(&iter))) { 420 USE(vr, vr->parent = vmp;); 421 region_incr_iter(&iter); 422 } 423 } 424 } 425 426