169 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
 | |
| #define	_SYSTEM 1
 | |
| 
 | |
| #include <minix/com.h>
 | |
| #include <minix/callnr.h>
 | |
| #include <minix/type.h>
 | |
| #include <minix/config.h>
 | |
| #include <minix/const.h>
 | |
| #include <minix/sysutil.h>
 | |
| #include <minix/syslib.h>
 | |
| #include <minix/debug.h>
 | |
| #include <minix/bitmap.h>
 | |
| #include <minix/hash.h>
 | |
| 
 | |
| #include <sys/mman.h>
 | |
| 
 | |
| #include <limits.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include <assert.h>
 | |
| #include <stdint.h>
 | |
| #include <sys/param.h>
 | |
| 
 | |
| #include "vm.h"
 | |
| #include "proto.h"
 | |
| #include "util.h"
 | |
| #include "glo.h"
 | |
| #include "region.h"
 | |
| #include "sanitycheck.h"
 | |
| #include "memlist.h"
 | |
| 
 | |
| struct	phys_block *pb_new(phys_bytes phys)
 | |
| {
 | |
| 	struct phys_block *newpb;
 | |
| 
 | |
| 	if(!SLABALLOC(newpb)) {
 | |
| 		printf("vm: pb_new: couldn't allocate phys block\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	if(phys != MAP_NONE)
 | |
| 		assert(!(phys % VM_PAGE_SIZE));
 | |
| 	
 | |
| USE(newpb,
 | |
| 	newpb->phys = phys;
 | |
| 	newpb->refcount = 0;
 | |
| 	newpb->firstregion = NULL;
 | |
| 	newpb->flags = 0;
 | |
| 	);
 | |
| 
 | |
| 	return newpb;
 | |
| }
 | |
| 
 | |
| void pb_free(struct phys_block *pb)
 | |
| {
 | |
| 	if(pb->phys != MAP_NONE)
 | |
| 		free_mem(ABS2CLICK(pb->phys), 1);
 | |
| 	SLABFREE(pb);
 | |
| }
 | |
| 
 | |
| void pb_link(struct phys_region *newphysr, struct phys_block *newpb,
 | |
| 	vir_bytes offset, struct vir_region *parent)
 | |
| {
 | |
| USE(newphysr,
 | |
| 	newphysr->offset = offset;
 | |
| 	newphysr->ph = newpb;
 | |
| 	newphysr->parent = parent;
 | |
| 	newphysr->next_ph_list = newpb->firstregion;
 | |
| 	newpb->firstregion = newphysr;);
 | |
| 	newpb->refcount++;
 | |
| }
 | |
| 
 | |
| struct	phys_region *pb_reference(struct phys_block *newpb,
 | |
| 	vir_bytes offset, struct vir_region *region, mem_type_t *memtype)
 | |
| {
 | |
| 	struct phys_region *newphysr;
 | |
| 
 | |
| 	if(!SLABALLOC(newphysr)) {
 | |
| 	printf("vm: pb_reference: couldn't allocate phys region\n");
 | |
| 	return NULL;
 | |
| 	}
 | |
| 
 | |
| 	newphysr->memtype = memtype;
 | |
| 
 | |
| 	/* New physical region. */
 | |
| 	pb_link(newphysr, newpb, offset, region);
 | |
| 
 | |
| 	physblock_set(region, offset, newphysr);
 | |
| 
 | |
| 	return newphysr;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				pb_unreferenced				     *
 | |
|  *===========================================================================*/
 | |
| void pb_unreferenced(struct vir_region *region, struct phys_region *pr, int rm)
 | |
| {
 | |
| 	struct phys_block *pb;
 | |
| 
 | |
| 	pb = pr->ph;
 | |
| 	assert(pb->refcount > 0);
 | |
| 	USE(pb, pb->refcount--;);
 | |
| /*	assert(pb->refcount >= 0); */ /* always true */
 | |
| 
 | |
| 	if(pb->firstregion == pr) {
 | |
| 		USE(pb, pb->firstregion = pr->next_ph_list;);
 | |
| 	} else {
 | |
| 		struct phys_region *others;
 | |
| 
 | |
| 		for(others = pb->firstregion; others;
 | |
| 			others = others->next_ph_list) {
 | |
| 			assert(others->ph == pb);
 | |
| 			if(others->next_ph_list == pr) {
 | |
| 				USE(others, others->next_ph_list = pr->next_ph_list;);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		assert(others); /* Otherwise, wasn't on the list. */
 | |
| 	}
 | |
| 
 | |
| 	if(pb->refcount == 0) {
 | |
| 		assert(!pb->firstregion);
 | |
| 		int r;
 | |
| 		if((r = pr->memtype->ev_unreference(pr)) != OK)
 | |
| 			panic("unref failed, %d", r);
 | |
| 
 | |
| 		SLABFREE(pb);
 | |
| 	}
 | |
| 
 | |
| 	pr->ph = NULL;
 | |
| 
 | |
| 	if(rm) physblock_set(region, pr->offset, NULL);
 | |
| }
 | |
| 
 | |
| int mem_cow(struct vir_region *region,
 | |
|         struct phys_region *ph, phys_bytes new_page_cl, phys_bytes new_page)
 | |
| {
 | |
|         struct phys_block *pb;
 | |
| 
 | |
|         if(new_page == MAP_NONE) {
 | |
|                 u32_t allocflags;
 | |
|                 allocflags = vrallocflags(region->flags);
 | |
| 
 | |
|                 if((new_page_cl = alloc_mem(1, allocflags)) == NO_MEM)
 | |
|                         return ENOMEM;
 | |
| 
 | |
|                 new_page = CLICK2ABS(new_page_cl);
 | |
|         }
 | |
| 
 | |
| 	assert(ph->ph->phys != MAP_NONE);
 | |
| 
 | |
|         if(sys_abscopy(ph->ph->phys, new_page, VM_PAGE_SIZE) != OK) {
 | |
|                 panic("VM: abscopy failed\n");
 | |
|                 return EFAULT;
 | |
|         }
 | |
| 
 | |
|         if(!(pb = pb_new(new_page))) {
 | |
|                 free_mem(new_page_cl, 1);
 | |
|                 return ENOMEM;
 | |
|         }
 | |
| 
 | |
|         pb_unreferenced(region, ph, 0);
 | |
|         pb_link(ph, pb, ph->offset, region);
 | |
| 	ph->memtype = &mem_type_anon;
 | |
| 
 | |
|         return OK;
 | |
| }
 | 
