 0f23130180
			
		
	
	
		0f23130180
		
	
	
	
	
		
			
			Introduce ARM_VM_SECTION_CACHED and ARM_VM_PTE_CACHED to ensure we are using the correct caching flags everywhere.
		
			
				
	
	
		
			358 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			358 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
 | |
| /* This file contains some utility routines for VM.  */
 | |
| 
 | |
| #define _SYSTEM		1
 | |
| 
 | |
| #define brk _brk	/* get rid of no previous prototype warning */
 | |
| 
 | |
| #include <minix/callnr.h>
 | |
| #include <minix/com.h>
 | |
| #include <minix/config.h>
 | |
| #include <minix/const.h>
 | |
| #include <minix/ds.h>
 | |
| #include <minix/endpoint.h>
 | |
| #include <minix/minlib.h>
 | |
| #include <minix/type.h>
 | |
| #include <minix/ipc.h>
 | |
| #include <minix/sysutil.h>
 | |
| #include <minix/syslib.h>
 | |
| #include <minix/type.h>
 | |
| #include <minix/bitmap.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include <env.h>
 | |
| #include <unistd.h>
 | |
| #include <assert.h>
 | |
| #include <sys/param.h>
 | |
| #include <sys/mman.h>
 | |
| #include <sys/resource.h>
 | |
| 
 | |
| #include "proto.h"
 | |
| #include "glo.h"
 | |
| #include "util.h"
 | |
| #include "region.h"
 | |
| #include "sanitycheck.h"
 | |
| 
 | |
| #include <machine/archtypes.h>
 | |
| #include "kernel/const.h"
 | |
| #include "kernel/config.h"
 | |
| #include "kernel/type.h"
 | |
| #include "kernel/proc.h"
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *                              get_mem_chunks                               *
 | |
|  *===========================================================================*/
 | |
| void get_mem_chunks(
 | |
| struct memory *mem_chunks)                      /* store mem chunks here */ 
 | |
| {  
 | |
| /* Initialize the free memory list from the kernel-provided memory map.  Translate
 | |
|  * the byte offsets and sizes in this list to clicks, properly truncated.
 | |
|  */
 | |
|   phys_bytes base, size, limit;
 | |
|   int i;
 | |
|   struct memory *memp;
 | |
| 
 | |
|   /* Initialize everything to zero. */
 | |
|   memset(mem_chunks, 0, NR_MEMS*sizeof(*mem_chunks));
 | |
| 
 | |
|   /* Obtain and parse memory from kernel environment. */
 | |
|   /* XXX Any memory chunk in excess of NR_MEMS is silently ignored. */
 | |
|   for(i = 0; i < MIN(MAXMEMMAP, NR_MEMS); i++) {
 | |
|   	mem_chunks[i].base = kernel_boot_info.memmap[i].addr;
 | |
|   	mem_chunks[i].size = kernel_boot_info.memmap[i].len;
 | |
|   }
 | |
| 
 | |
|   /* Round physical memory to clicks. Round start up, round end down. */
 | |
|   for (i = 0; i < NR_MEMS; i++) {
 | |
|         memp = &mem_chunks[i];          /* next mem chunk is stored here */
 | |
|         base = mem_chunks[i].base;
 | |
|         size = mem_chunks[i].size;
 | |
|         limit = base + size;
 | |
|         base = (phys_bytes) (CLICK_CEIL(base));
 | |
|         limit = (phys_bytes) (CLICK_FLOOR(limit));
 | |
|         if (limit <= base) {
 | |
|                 memp->base = memp->size = 0;
 | |
|         } else { 
 | |
|                 memp->base = base >> CLICK_SHIFT;
 | |
|                 memp->size = (limit - base) >> CLICK_SHIFT;
 | |
|         }
 | |
|   }
 | |
| }  
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *                              vm_isokendpt                           	     *
 | |
|  *===========================================================================*/
 | |
| int vm_isokendpt(endpoint_t endpoint, int *procn)
 | |
| {
 | |
|         *procn = _ENDPOINT_P(endpoint);
 | |
|         if(*procn < 0 || *procn >= NR_PROCS)
 | |
| 		return EINVAL;
 | |
|         if(*procn >= 0 && endpoint != vmproc[*procn].vm_endpoint)
 | |
|                 return EDEADEPT;
 | |
|         if(*procn >= 0 && !(vmproc[*procn].vm_flags & VMF_INUSE))
 | |
|                 return EDEADEPT;
 | |
|         return OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *                              do_info                                      *
 | |
|  *===========================================================================*/
 | |
| int do_info(message *m)
 | |
| {
 | |
| 	struct vm_stats_info vsi;
 | |
| 	struct vm_usage_info vui;
 | |
| 	static struct vm_region_info vri[MAX_VRI_COUNT];
 | |
| 	struct vmproc *vmp;
 | |
| 	vir_bytes addr, size, next, ptr;
 | |
| 	int r, pr, dummy, count, free_pages, largest_contig;
 | |
| 
 | |
| 	if (vm_isokendpt(m->m_source, &pr) != OK)
 | |
| 		return EINVAL;
 | |
| 	vmp = &vmproc[pr];
 | |
| 
 | |
| 	ptr = (vir_bytes) m->VMI_PTR;
 | |
| 
 | |
| 	switch(m->VMI_WHAT) {
 | |
| 	case VMIW_STATS:
 | |
| 		vsi.vsi_pagesize = VM_PAGE_SIZE;
 | |
| 		vsi.vsi_total = total_pages;
 | |
| 		memstats(&dummy, &free_pages, &largest_contig);
 | |
| 		vsi.vsi_free = free_pages;
 | |
| 		vsi.vsi_largest = largest_contig;
 | |
| 
 | |
| 		get_stats_info(&vsi);
 | |
| 
 | |
| 		addr = (vir_bytes) &vsi;
 | |
| 		size = sizeof(vsi);
 | |
| 
 | |
| 		break;
 | |
| 
 | |
| 	case VMIW_USAGE:
 | |
| 		if(m->VMI_EP < 0)
 | |
| 			get_usage_info_kernel(&vui);
 | |
| 		else if (vm_isokendpt(m->VMI_EP, &pr) != OK)
 | |
| 			return EINVAL;
 | |
| 		else get_usage_info(&vmproc[pr], &vui);
 | |
| 
 | |
| 		addr = (vir_bytes) &vui;
 | |
| 		size = sizeof(vui);
 | |
| 
 | |
| 		break;
 | |
| 
 | |
| 	case VMIW_REGION:
 | |
| 		if (vm_isokendpt(m->VMI_EP, &pr) != OK)
 | |
| 			return EINVAL;
 | |
| 
 | |
| 		count = MIN(m->VMI_COUNT, MAX_VRI_COUNT);
 | |
| 		next = m->VMI_NEXT;
 | |
| 
 | |
| 		count = get_region_info(&vmproc[pr], vri, count, &next);
 | |
| 
 | |
| 		m->VMI_COUNT = count;
 | |
| 		m->VMI_NEXT = next;
 | |
| 
 | |
| 		addr = (vir_bytes) vri;
 | |
| 		size = sizeof(vri[0]) * count;
 | |
| 
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (size == 0)
 | |
| 		return OK;
 | |
| 
 | |
| 	/* Make sure that no page faults can occur while copying out. A page
 | |
| 	 * fault would cause the kernel to send a notify to us, while we would
 | |
| 	 * be waiting for the result of the copy system call, resulting in a
 | |
| 	 * deadlock. Note that no memory mapping can be undone without the
 | |
| 	 * involvement of VM, so we are safe until we're done.
 | |
| 	 */
 | |
| 	r = handle_memory(vmp, ptr, size, 1 /*wrflag*/, NULL, NULL, 0);
 | |
| 	if (r != OK) return r;
 | |
| 
 | |
| 	/* Now that we know the copy out will succeed, perform the actual copy
 | |
| 	 * operation.
 | |
| 	 */
 | |
| 	return sys_datacopy(SELF, addr,
 | |
| 		(vir_bytes) vmp->vm_endpoint, ptr, size);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				swap_proc_slot	     			     *
 | |
|  *===========================================================================*/
 | |
| int swap_proc_slot(struct vmproc *src_vmp, struct vmproc *dst_vmp)
 | |
| {
 | |
| 	struct vmproc orig_src_vmproc, orig_dst_vmproc;
 | |
| 
 | |
| #if LU_DEBUG
 | |
| 	printf("VM: swap_proc: swapping %d (%d) and %d (%d)\n",
 | |
| 	    src_vmp->vm_endpoint, src_vmp->vm_slot,
 | |
| 	    dst_vmp->vm_endpoint, dst_vmp->vm_slot);
 | |
| #endif
 | |
| 
 | |
| 	/* Save existing data. */
 | |
| 	orig_src_vmproc = *src_vmp;
 | |
| 	orig_dst_vmproc = *dst_vmp;
 | |
| 
 | |
| 	/* Swap slots. */
 | |
| 	*src_vmp = orig_dst_vmproc;
 | |
| 	*dst_vmp = orig_src_vmproc;
 | |
| 
 | |
| 	/* Preserve endpoints and slot numbers. */
 | |
| 	src_vmp->vm_endpoint = orig_src_vmproc.vm_endpoint;
 | |
| 	src_vmp->vm_slot = orig_src_vmproc.vm_slot;
 | |
| 	dst_vmp->vm_endpoint = orig_dst_vmproc.vm_endpoint;
 | |
| 	dst_vmp->vm_slot = orig_dst_vmproc.vm_slot;
 | |
| 
 | |
| #if LU_DEBUG
 | |
| 	printf("VM: swap_proc: swapped %d (%d) and %d (%d)\n",
 | |
| 	    src_vmp->vm_endpoint, src_vmp->vm_slot,
 | |
| 	    dst_vmp->vm_endpoint, dst_vmp->vm_slot);
 | |
| #endif
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *			      swap_proc_dyn_data	     		     *
 | |
|  *===========================================================================*/
 | |
| int swap_proc_dyn_data(struct vmproc *src_vmp, struct vmproc *dst_vmp)
 | |
| {
 | |
| 	int is_vm;
 | |
| 	int r;
 | |
| 
 | |
| 	is_vm = (dst_vmp->vm_endpoint == VM_PROC_NR);
 | |
| 
 | |
|         /* For VM, transfer memory regions above the stack first. */
 | |
|         if(is_vm) {
 | |
| #if LU_DEBUG
 | |
| 		printf("VM: swap_proc_dyn_data: tranferring regions above the stack from old VM (%d) to new VM (%d)\n",
 | |
| 			src_vmp->vm_endpoint, dst_vmp->vm_endpoint);
 | |
| #endif
 | |
| 		r = pt_map_in_range(src_vmp, dst_vmp, VM_STACKTOP, 0);
 | |
| 		if(r != OK) {
 | |
| 			printf("swap_proc_dyn_data: pt_map_in_range failed\n");
 | |
| 			return r;
 | |
| 		}
 | |
|         }
 | |
| 
 | |
| #if LU_DEBUG
 | |
| 	printf("VM: swap_proc_dyn_data: swapping regions' parents for %d (%d) and %d (%d)\n",
 | |
| 	    src_vmp->vm_endpoint, src_vmp->vm_slot,
 | |
| 	    dst_vmp->vm_endpoint, dst_vmp->vm_slot);
 | |
| #endif
 | |
| 
 | |
| 	/* Swap vir_regions' parents. */
 | |
| 	map_setparent(src_vmp);
 | |
| 	map_setparent(dst_vmp);
 | |
| 
 | |
| 	/* For regular processes, transfer regions above the stack now.
 | |
| 	 * In case of rollback, we need to skip this step. To sandbox the
 | |
| 	 * new instance and prevent state corruption on rollback, we share all
 | |
| 	 * the regions between the two instances as COW.
 | |
| 	 */
 | |
| 	if(!is_vm) {
 | |
| 		struct vir_region *vr;
 | |
| 		vr = map_lookup(dst_vmp, VM_STACKTOP, NULL);
 | |
| 		if(vr && !map_lookup(src_vmp, VM_STACKTOP, NULL)) {
 | |
| #if LU_DEBUG
 | |
| 			printf("VM: swap_proc_dyn_data: tranferring regions above the stack from %d to %d\n",
 | |
| 				src_vmp->vm_endpoint, dst_vmp->vm_endpoint);
 | |
| #endif
 | |
| 			r = map_proc_copy_from(src_vmp, dst_vmp, vr);
 | |
| 			if(r != OK) {
 | |
| 				return r;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| void *minix_mmap(void *addr, size_t len, int f, int f2, int f3, off_t o)
 | |
| {
 | |
| 	void *ret;
 | |
| 	phys_bytes p;
 | |
| 
 | |
| 	assert(!addr);
 | |
| 	assert(!(len % VM_PAGE_SIZE));
 | |
| 
 | |
| 	ret = vm_allocpages(&p, VMP_SLAB, len/VM_PAGE_SIZE);
 | |
| 
 | |
| 	if(!ret) return MAP_FAILED;
 | |
| 	memset(ret, 0, len);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int minix_munmap(void * addr, size_t len)
 | |
| {
 | |
| 	vm_freepages((vir_bytes) addr, roundup(len, VM_PAGE_SIZE)/VM_PAGE_SIZE);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int brk(void *addr)
 | |
| {
 | |
| 	/* brk is a special case function to allow vm itself to
 | |
| 	   allocate memory in it's own (cacheable) HEAP */
 | |
| 	vir_bytes target = roundup((vir_bytes)addr, VM_PAGE_SIZE), v;
 | |
| 	extern char _end;
 | |
| 	extern char *_brksize;
 | |
| 	static vir_bytes prevbrk = (vir_bytes) &_end;
 | |
| 	struct vmproc *vmprocess = &vmproc[VM_PROC_NR];
 | |
| 
 | |
| 	for(v = roundup(prevbrk, VM_PAGE_SIZE); v < target;
 | |
| 		v += VM_PAGE_SIZE) {
 | |
| 		phys_bytes mem, newpage = alloc_mem(1, 0);
 | |
| 		if(newpage == NO_MEM) return -1;
 | |
| 		mem = CLICK2ABS(newpage);
 | |
| 		if(pt_writemap(vmprocess, &vmprocess->vm_pt,
 | |
| 			v, mem, VM_PAGE_SIZE,
 | |
| 			  ARCH_VM_PTE_PRESENT
 | |
| 			| ARCH_VM_PTE_USER
 | |
| 			| ARCH_VM_PTE_RW
 | |
| #if defined(__arm__)
 | |
| 			| ARM_VM_PTE_CACHED
 | |
| #endif
 | |
| 			, 0) != OK) {
 | |
| 			free_mem(newpage, 1);
 | |
| 			return -1;
 | |
| 		}
 | |
| 		prevbrk = v + VM_PAGE_SIZE;
 | |
| 	}
 | |
| 
 | |
|         _brksize = (char *) addr;
 | |
| 
 | |
|         if(sys_vmctl(SELF, VMCTL_FLUSHTLB, 0) != OK)
 | |
|         	panic("flushtlb failed");
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_getrusage		     		     *
 | |
|  *===========================================================================*/
 | |
| int do_getrusage(message *m)
 | |
| {
 | |
| 	int res, slot;
 | |
| 	struct vmproc *vmp;
 | |
| 	struct rusage r_usage;
 | |
| 	if ((res = vm_isokendpt(m->m_source, &slot)) != OK)
 | |
| 		return ESRCH;
 | |
| 
 | |
| 	vmp = &vmproc[slot];
 | |
| 
 | |
| 	if ((res = sys_datacopy(m->m_source, (vir_bytes) m->RU_RUSAGE_ADDR,
 | |
| 		SELF, (vir_bytes) &r_usage, (vir_bytes) sizeof(r_usage))) < 0)
 | |
| 		return res;
 | |
| 
 | |
| 	r_usage.ru_maxrss = vmp->vm_total_max;
 | |
| 	r_usage.ru_minflt = vmp->vm_minor_page_fault;
 | |
| 	r_usage.ru_majflt = vmp->vm_major_page_fault;
 | |
| 
 | |
| 	return sys_datacopy(SELF, (vir_bytes) &r_usage, m->m_source,
 | |
| 		(vir_bytes) m->RU_RUSAGE_ADDR, (vir_bytes) sizeof(r_usage));
 | |
| }
 |