xref: /minix3/minix/servers/vm/utility.c (revision b5e2faaaaf60a8b9a02f8d72f64caa56a87eb312)
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