minix/servers/vm/region.c

1105 lines
28 KiB
C
Raw Normal View History

#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 <sys/mman.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <stdint.h>
#include <memory.h>
#include "vm.h"
#include "proto.h"
#include "util.h"
#include "glo.h"
#include "region.h"
#include "sanitycheck.h"
FORWARD _PROTOTYPE(int map_new_physblock, (struct vmproc *vmp,
struct vir_region *region, vir_bytes offset, vir_bytes length,
phys_bytes what, struct phys_region *physhint));
FORWARD _PROTOTYPE(int map_copy_ph_block, (struct vmproc *vmp, struct vir_region *region, struct phys_region *ph));
FORWARD _PROTOTYPE(struct vir_region *map_copy_region, (struct vir_region *));
FORWARD _PROTOTYPE(void map_printmap, (struct vmproc *vmp));
PRIVATE char *map_name(struct vir_region *vr)
{
int type = vr->flags & (VR_ANON|VR_DIRECT);
switch(type) {
case VR_ANON:
return "anonymous";
case VR_DIRECT:
return "direct";
default:
vm_panic("unknown mapping type", type);
}
return "NOTREACHED";
}
/*===========================================================================*
* map_printmap *
*===========================================================================*/
PRIVATE void map_printmap(vmp)
struct vmproc *vmp;
{
struct vir_region *vr;
printf("memory regions in process %d:\n", vmp->vm_endpoint);
for(vr = vmp->vm_regions; vr; vr = vr->next) {
struct phys_region *ph;
int nph = 0;
2009-05-12 13:36:15 +02:00
printf("\t0x%lx - 0x%lx (len 0x%lx), %s\n",
vr->vaddr, vr->vaddr + vr->length, vr->length,
2009-05-12 13:36:15 +02:00
vr->vaddr + vr->length, map_name(vr));
printf("\t\tphysical: ");
for(ph = vr->first; ph; ph = ph->next) {
printf("0x%lx-0x%lx (refs %d): phys 0x%lx ",
vr->vaddr + ph->ph->offset,
vr->vaddr + ph->ph->offset + ph->ph->length,
ph->ph->refcount,
ph->ph->phys);
nph++;
}
printf(" (phregions %d)\n", nph);
}
}
#if SANITYCHECKS
/*===========================================================================*
* map_sanitycheck *
*===========================================================================*/
PUBLIC void map_sanitycheck(char *file, int line)
{
struct vmproc *vmp;
/* Macro for looping over all physical blocks of all regions of
* all processes.
*/
#define ALLREGIONS(regioncode, physcode) \
for(vmp = vmproc; vmp <= &vmproc[_NR_PROCS]; vmp++) { \
struct vir_region *vr; \
if(!(vmp->vm_flags & VMF_INUSE)) \
continue; \
for(vr = vmp->vm_regions; vr; vr = vr->next) { \
struct phys_region *pr; \
regioncode; \
for(pr = vr->first; pr; pr = pr->next) { \
physcode; \
} \
} \
}
#define MYSLABSANE(s) MYASSERT(slabsane(s, sizeof(*(s))))
/* Basic pointers check. */
ALLREGIONS(MYSLABSANE(vr),MYSLABSANE(pr); MYSLABSANE(pr->ph);MYSLABSANE(pr->parent));
ALLREGIONS(MYASSERT(vr->parent == vmp),MYASSERT(pr->parent == vr););
/* Do counting for consistency check. */
ALLREGIONS(;,pr->ph->seencount = 0;);
ALLREGIONS(;,pr->ph->seencount++;);
/* Do consistency check. */
ALLREGIONS(if(vr->next) {
MYASSERT(vr->vaddr < vr->next->vaddr);
MYASSERT(vr->vaddr + vr->length <= vr->next->vaddr);
}
MYASSERT(!(vr->vaddr % VM_PAGE_SIZE));,
if(pr->ph->refcount != pr->ph->seencount) {
map_printmap(vmp);
printf("ph in vr 0x%lx: 0x%lx-0x%lx refcount %d "
"but seencount %lu\n",
vr, pr->ph->offset,
pr->ph->offset + pr->ph->length,
pr->ph->refcount, pr->ph->seencount);
}
{
int n_others = 0;
struct phys_region *others;
if(pr->ph->refcount > 0) {
MYASSERT(pr->ph->firstregion);
if(pr->ph->refcount == 1) {
MYASSERT(pr->ph->firstregion == pr);
}
} else {
MYASSERT(!pr->ph->firstregion);
}
for(others = pr->ph->firstregion; others;
others = others->next_ph_list) {
MYSLABSANE(others);
MYASSERT(others->ph == pr->ph);
n_others++;
}
MYASSERT(pr->ph->refcount == n_others);
}
MYASSERT(pr->ph->refcount == pr->ph->seencount);
MYASSERT(!(pr->ph->offset % VM_PAGE_SIZE));
MYASSERT(!(pr->ph->length % VM_PAGE_SIZE)););
}
#endif
/*=========================================================================*
* map_ph_writept *
*=========================================================================*/
PUBLIC int map_ph_writept(struct vmproc *vmp, struct vir_region *vr,
struct phys_block *pb, int *ropages, int *rwpages)
{
int rw;
vm_assert(!(vr->vaddr % VM_PAGE_SIZE));
vm_assert(!(pb->length % VM_PAGE_SIZE));
vm_assert(!(pb->offset % VM_PAGE_SIZE));
vm_assert(pb->refcount > 0);
if((vr->flags & VR_WRITABLE)
&& (pb->refcount == 1 || (vr->flags & VR_DIRECT)))
rw = PTF_WRITE;
else
rw = 0;
#if SANITYCHECKS
if(rwpages && ropages && (vr->flags & VR_ANON)) {
int pages;
pages = pb->length / VM_PAGE_SIZE;
if(rw)
(*rwpages) += pages;
else
(*ropages) += pages;
}
#endif
if(pt_writemap(&vmp->vm_pt, vr->vaddr + pb->offset,
pb->phys, pb->length, PTF_PRESENT | PTF_USER | rw,
WMF_OVERWRITE) != OK) {
printf("VM: map_writept: pt_writemap failed\n");
return ENOMEM;
}
return OK;
}
/*===========================================================================*
* map_page_region *
*===========================================================================*/
PUBLIC struct vir_region *map_page_region(vmp, minv, maxv, length,
what, flags, mapflags)
struct vmproc *vmp;
vir_bytes minv;
vir_bytes maxv;
vir_bytes length;
vir_bytes what;
u32_t flags;
int mapflags;
{
struct vir_region *vr, *prevregion = NULL, *newregion,
*firstregion = vmp->vm_regions;
vir_bytes startv;
int foundflag = 0;
SANITYCHECK(SCL_FUNCTIONS);
/* We must be in paged mode to be able to do this. */
vm_assert(vm_paged);
/* Length must be reasonable. */
vm_assert(length > 0);
/* Special case: allow caller to set maxv to 0 meaning 'I want
* it to be mapped in right here.'
*/
if(maxv == 0) {
maxv = minv + length;
/* Sanity check. */
if(maxv <= minv) {
printf("map_page_region: minv 0x%lx and bytes 0x%lx\n",
minv, length);
map_printmap(vmp);
return NULL;
}
}
/* Basic input sanity checks. */
vm_assert(!(length % VM_PAGE_SIZE));
if(minv >= maxv) {
printf("VM: 1 minv: 0x%lx maxv: 0x%lx length: 0x%lx\n",
minv, maxv, length);
}
vm_assert(minv < maxv);
vm_assert(minv + length <= maxv);
#define FREEVRANGE(rangestart, rangeend, foundcode) { \
vir_bytes frstart = (rangestart), frend = (rangeend); \
frstart = MAX(frstart, minv); \
frend = MIN(frend, maxv); \
if(frend > frstart && (frend - frstart) >= length) { \
startv = frstart; \
foundflag = 1; \
foundcode; \
} }
/* This is the free virtual address space before the first region. */
FREEVRANGE(0, firstregion ? firstregion->vaddr : VM_DATATOP, ;);
if(!foundflag) {
for(vr = vmp->vm_regions; vr && !foundflag; vr = vr->next) {
FREEVRANGE(vr->vaddr + vr->length,
vr->next ? vr->next->vaddr : VM_DATATOP,
prevregion = vr;);
}
}
if(!foundflag) {
printf("VM: map_page_region: no 0x%lx bytes found for %d between 0x%lx and 0x%lx\n",
length, vmp->vm_endpoint, minv, maxv);
map_printmap(vmp);
return NULL;
}
#if SANITYCHECKS
if(prevregion) vm_assert(prevregion->vaddr < startv);
#endif
/* However we got it, startv must be in the requested range. */
vm_assert(startv >= minv);
vm_assert(startv < maxv);
vm_assert(startv + length <= maxv);
/* Now we want a new region. */
if(!SLABALLOC(newregion)) {
printf("VM: map_page_region: allocating region failed\n");
return NULL;
}
/* Fill in node details. */
newregion->vaddr = startv;
newregion->length = length;
newregion->first = NULL;
newregion->flags = flags;
newregion->tag = VRT_NONE;
newregion->parent = vmp;
/* If we know what we're going to map to, map it right away. */
if(what != MAP_NONE) {
vm_assert(!(what % VM_PAGE_SIZE));
vm_assert(!(length % VM_PAGE_SIZE));
vm_assert(!(startv % VM_PAGE_SIZE));
vm_assert(!newregion->first);
vm_assert(!(mapflags & MF_PREALLOC));
if(map_new_physblock(vmp, newregion, 0, length, what, NULL) != OK) {
printf("VM: map_new_physblock failed\n");
SLABFREE(newregion);
return NULL;
}
vm_assert(newregion->first);
vm_assert(!newregion->first->next);
if(map_ph_writept(vmp, newregion, newregion->first->ph, NULL, NULL) != OK) {
printf("VM: map_region_writept failed\n");
SLABFREE(newregion);
return NULL;
}
}
if((flags & VR_ANON) && (mapflags & MF_PREALLOC)) {
if(map_handle_memory(vmp, newregion, 0, length, 1) != OK) {
printf("VM:map_page_region: prealloc failed\n");
SLABFREE(newregion);
return NULL;
}
}
/* Link it. */
if(prevregion) {
vm_assert(prevregion->vaddr < newregion->vaddr);
newregion->next = prevregion->next;
prevregion->next = newregion;
} else {
newregion->next = vmp->vm_regions;
vmp->vm_regions = newregion;
}
#if SANITYCHECKS
vm_assert(startv == newregion->vaddr);
if(newregion->next) {
vm_assert(newregion->vaddr < newregion->next->vaddr);
}
#endif
SANITYCHECK(SCL_FUNCTIONS);
return newregion;
}
/*===========================================================================*
* pb_unreferenced *
*===========================================================================*/
void pb_unreferenced(struct vir_region *region, struct phys_region *pr)
{
struct phys_block *pb;
int remap = 0;
SLABSANE(pr);
pb = pr->ph;
SLABSANE(pb);
vm_assert(pb->refcount > 0);
pb->refcount--;
vm_assert(pb->refcount >= 0);
SLABSANE(pb->firstregion);
if(pb->firstregion == pr) {
pb->firstregion = pr->next_ph_list;
if(pb->firstregion) {
SLABSANE(pb->firstregion);
}
} else {
struct phys_region *others;
for(others = pb->firstregion; others;
others = others->next_ph_list) {
SLABSANE(others);
vm_assert(others->ph == pb);
if(others->next_ph_list == pr) {
others->next_ph_list = pr->next_ph_list;
break;
}
}
vm_assert(others); /* Otherwise, wasn't on the list. */
}
if(pb->refcount == 0) {
vm_assert(!pb->firstregion);
if(region->flags & VR_ANON) {
FREE_MEM(ABS2CLICK(pb->phys),
ABS2CLICK(pb->length));
} else if(region->flags & VR_DIRECT) {
; /* No action required. */
} else {
vm_panic("strange phys flags", NO_NUM);
}
SLABFREE(pb);
} else {
SLABSANE(pb->firstregion);
/* If a writable piece of physical memory is now only
* referenced once, map it writable right away instead of
* waiting for a page fault.
*/
if(pb->refcount == 1 && (region->flags & VR_WRITABLE)) {
vm_assert(pb);
vm_assert(pb->firstregion);
vm_assert(!pb->firstregion->next_ph_list);
vm_assert(pb->firstregion->ph == pb);
vm_assert(pb->firstregion->ph == pb);
SLABSANE(pb);
SLABSANE(pb->firstregion);
SLABSANE(pb->firstregion->parent);
if(map_ph_writept(pb->firstregion->parent->parent,
pb->firstregion->parent, pb, NULL, NULL) != OK) {
vm_panic("pb_unreferenced: writept", NO_NUM);
}
}
}
}
/*===========================================================================*
* map_free *
*===========================================================================*/
PRIVATE int map_free(struct vir_region *region)
{
struct phys_region *pr, *nextpr;
#if SANITYCHECKS
for(pr = region->first; pr; pr = pr->next) {
struct phys_region *others;
struct phys_block *pb;
SLABSANE(pr);
pb = pr->ph;
SLABSANE(pb);
SLABSANE(pb->firstregion);
for(others = pb->firstregion; others;
others = others->next_ph_list) {
SLABSANE(others);
vm_assert(others->ph == pb);
}
}
#endif
for(pr = region->first; pr; pr = nextpr) {
SANITYCHECK(SCL_DETAIL);
pb_unreferenced(region, pr);
nextpr = pr->next;
region->first = nextpr; /* For sanity checks. */
SLABFREE(pr);
}
SLABFREE(region);
return OK;
}
/*========================================================================*
* map_free_proc *
*========================================================================*/
PUBLIC int map_free_proc(vmp)
struct vmproc *vmp;
{
struct vir_region *r, *nextr;
SANITYCHECK(SCL_FUNCTIONS);
for(r = vmp->vm_regions; r; r = nextr) {
nextr = r->next;
SANITYCHECK(SCL_DETAIL);
#if SANITYCHECKS
nocheck++;
#endif
map_free(r);
vmp->vm_regions = nextr; /* For sanity checks. */
#if SANITYCHECKS
nocheck--;
#endif
SANITYCHECK(SCL_DETAIL);
}
vmp->vm_regions = NULL;
SANITYCHECK(SCL_FUNCTIONS);
return OK;
}
/*===========================================================================*
* map_lookup *
*===========================================================================*/
PUBLIC struct vir_region *map_lookup(vmp, offset)
struct vmproc *vmp;
vir_bytes offset;
{
struct vir_region *r;
SANITYCHECK(SCL_FUNCTIONS);
if(!vmp->vm_regions)
vm_panic("process has no regions", vmp->vm_endpoint);
for(r = vmp->vm_regions; r; r = r->next) {
if(offset >= r->vaddr && offset < r->vaddr + r->length)
return r;
}
SANITYCHECK(SCL_FUNCTIONS);
return NULL;
}
/*===========================================================================*
* map_new_physblock *
*===========================================================================*/
PRIVATE int map_new_physblock(vmp, region, offset, length, what_mem, physhint)
struct vmproc *vmp;
struct vir_region *region;
vir_bytes offset;
vir_bytes length;
phys_bytes what_mem;
struct phys_region *physhint;
{
struct phys_region *newphysr;
struct phys_block *newpb;
phys_bytes mem_clicks, clicks;
vir_bytes mem;
SANITYCHECK(SCL_FUNCTIONS);
vm_assert(!(length % VM_PAGE_SIZE));
if(!physhint) physhint = region->first;
/* Allocate things necessary for this chunk of memory. */
if(!SLABALLOC(newphysr))
return ENOMEM;
if(!SLABALLOC(newpb)) {
SLABFREE(newphysr);
return ENOMEM;
}
/* Memory for new physical block. */
clicks = CLICKSPERPAGE * length / VM_PAGE_SIZE;
if(what_mem == MAP_NONE) {
u32_t af = PAF_CLEAR;
if(region->flags & VR_PHYS64K)
af |= PAF_ALIGN64K;
if((mem_clicks = ALLOC_MEM(clicks, af)) == NO_MEM) {
SLABFREE(newpb);
SLABFREE(newphysr);
return ENOMEM;
}
mem = CLICK2ABS(mem_clicks);
} else {
mem = what_mem;
}
SANITYCHECK(SCL_DETAIL);
/* New physical block. */
newpb->phys = mem;
newpb->refcount = 1;
newpb->offset = offset;
newpb->length = length;
newpb->firstregion = newphysr;
SLABSANE(newpb->firstregion);
/* New physical region. */
newphysr->ph = newpb;
newphysr->parent = region;
newphysr->next_ph_list = NULL; /* No other references to this block. */
/* Update pagetable. */
vm_assert(!(length % VM_PAGE_SIZE));
vm_assert(!(newpb->length % VM_PAGE_SIZE));
SANITYCHECK(SCL_DETAIL);
if(map_ph_writept(vmp, region, newpb, NULL, NULL) != OK) {
if(what_mem == MAP_NONE)
FREE_MEM(mem_clicks, clicks);
SLABFREE(newpb);
SLABFREE(newphysr);
return ENOMEM;
}
if(!region->first || offset < region->first->ph->offset) {
/* Special case: offset is before start. */
if(region->first) {
vm_assert(offset + length <= region->first->ph->offset);
}
newphysr->next = region->first;
region->first = newphysr;
} else {
struct phys_region *physr;
for(physr = physhint; physr; physr = physr->next) {
if(!physr->next || physr->next->ph->offset > offset) {
newphysr->next = physr->next;
physr->next = newphysr;
break;
}
}
/* Loop must have put the node somewhere. */
vm_assert(physr->next == newphysr);
}
SANITYCHECK(SCL_FUNCTIONS);
return OK;
}
/*===========================================================================*
* map_copy_ph_block *
*===========================================================================*/
PRIVATE int map_copy_ph_block(vmp, region, ph)
struct vmproc *vmp;
struct vir_region *region;
struct phys_region *ph;
{
int r;
phys_bytes newmem, newmem_cl, clicks;
struct phys_block *newpb;
u32_t af = 0;
SANITYCHECK(SCL_FUNCTIONS);
/* This is only to be done if there is more than one copy. */
vm_assert(ph->ph->refcount > 1);
/* Do actual copy on write; allocate new physblock. */
if(!SLABALLOC(newpb)) {
printf("VM: map_copy_ph_block: couldn't allocate newpb\n");
SANITYCHECK(SCL_FUNCTIONS);
return ENOMEM;
}
clicks = CLICKSPERPAGE * ph->ph->length / VM_PAGE_SIZE;
vm_assert(CLICK2ABS(clicks) == ph->ph->length);
if(region->flags & VR_PHYS64K)
af |= PAF_ALIGN64K;
if((newmem_cl = ALLOC_MEM(clicks, af)) == NO_MEM) {
SLABFREE(newpb);
return ENOMEM;
}
newmem = CLICK2ABS(newmem_cl);
vm_assert(ABS2CLICK(newmem) == newmem_cl);
pb_unreferenced(region, ph);
SLABSANE(ph);
SLABSANE(ph->ph);
vm_assert(ph->ph->refcount > 0);
newpb->length = ph->ph->length;
newpb->offset = ph->ph->offset;
newpb->refcount = 1;
newpb->phys = newmem;
newpb->firstregion = ph;
ph->next_ph_list = NULL;
/* Copy old memory to new memory. */
if((r=sys_abscopy(ph->ph->phys, newpb->phys, newpb->length)) != OK) {
printf("VM: map_copy_ph_block: sys_abscopy failed\n");
SANITYCHECK(SCL_FUNCTIONS);
return r;
}
#if VMSTATS
vmp->vm_bytecopies += newpb->length;
#endif
/* Reference new block. */
ph->ph = newpb;
/* Check reference counts. */
SANITYCHECK(SCL_DETAIL);
/* Update pagetable with new address.
* This will also make it writable.
*/
r = map_ph_writept(vmp, region, ph->ph, NULL, NULL);
if(r != OK)
vm_panic("map_copy_ph_block: map_ph_writept failed", r);
SANITYCHECK(SCL_FUNCTIONS);
return OK;
}
/*===========================================================================*
* map_pf *
*===========================================================================*/
PUBLIC int map_pf(vmp, region, offset, write)
struct vmproc *vmp;
struct vir_region *region;
vir_bytes offset;
int write;
{
vir_bytes virpage;
struct phys_region *ph;
int r;
vm_assert(offset >= 0);
vm_assert(offset < region->length);
vm_assert(region->flags & VR_ANON);
vm_assert(!(region->vaddr % VM_PAGE_SIZE));
virpage = offset - offset % VM_PAGE_SIZE;
SANITYCHECK(SCL_FUNCTIONS);
for(ph = region->first; ph; ph = ph->next)
if(ph->ph->offset <= offset && offset < ph->ph->offset + ph->ph->length)
break;
if(ph) {
/* Pagefault in existing block. Do copy-on-write. */
vm_assert(write);
vm_assert(region->flags & VR_WRITABLE);
vm_assert(ph->ph->refcount > 0);
if(ph->ph->refcount == 1)
r = map_ph_writept(vmp, region, ph->ph, NULL, NULL);
else
r = map_copy_ph_block(vmp, region, ph);
} else {
/* Pagefault in non-existing block. Map in new block. */
#if 0
if(!write) {
printf("VM: read from uninitialized memory by %d\n",
vmp->vm_endpoint);
}
#endif
r = map_new_physblock(vmp, region, virpage, VM_PAGE_SIZE,
MAP_NONE, region->first);
}
if(r != OK)
printf("VM: map_pf: failed (%d)\n", r);
SANITYCHECK(SCL_FUNCTIONS);
return r;
}
/*===========================================================================*
* map_handle_memory *
*===========================================================================*/
PUBLIC int map_handle_memory(vmp, region, offset, length, write)
struct vmproc *vmp;
struct vir_region *region;
vir_bytes offset, length;
int write;
{
struct phys_region *physr;
int changes = 0;
#define FREE_RANGE_HERE(er1, er2) { \
struct phys_region *r1 = (er1), *r2 = (er2); \
vir_bytes start = offset, end = offset + length; \
if(r1) { start = MAX(start, r1->ph->offset + r1->ph->length); } \
if(r2) { end = MIN(end, r2->ph->offset); } \
if(start < end) { \
int r; \
SANITYCHECK(SCL_DETAIL); \
if((r=map_new_physblock(vmp, region, start, \
end-start, MAP_NONE, r1 ? r1 : r2)) != OK) { \
SANITYCHECK(SCL_DETAIL); \
return r; \
} \
changes++; \
} }
SANITYCHECK(SCL_FUNCTIONS);
vm_assert(region->flags & VR_ANON);
vm_assert(!(region->vaddr % VM_PAGE_SIZE));
vm_assert(!(offset % VM_PAGE_SIZE));
vm_assert(!(length % VM_PAGE_SIZE));
vm_assert(!write || (region->flags & VR_WRITABLE));
FREE_RANGE_HERE(NULL, region->first);
for(physr = region->first; physr; physr = physr->next) {
int r;
SANITYCHECK(SCL_DETAIL);
if(write) {
vm_assert(physr->ph->refcount > 0);
if(physr->ph->refcount > 1) {
SANITYCHECK(SCL_DETAIL);
r = map_copy_ph_block(vmp, region, physr);
if(r != OK) {
printf("VM: map_handle_memory: no copy\n");
return r;
}
changes++;
SANITYCHECK(SCL_DETAIL);
} else {
SANITYCHECK(SCL_DETAIL);
if((r=map_ph_writept(vmp, region, physr->ph, NULL, NULL)) != OK) {
printf("VM: map_ph_writept failed\n");
return r;
}
changes++;
SANITYCHECK(SCL_DETAIL);
}
}
SANITYCHECK(SCL_DETAIL);
FREE_RANGE_HERE(physr, physr->next);
SANITYCHECK(SCL_DETAIL);
}
SANITYCHECK(SCL_FUNCTIONS);
#if SANITYCHECKS
if(changes == 0) {
vm_panic("no changes?!", changes);
}
#endif
return OK;
}
#if SANITYCHECKS
static int countregions(struct vir_region *vr)
{
int n = 0;
struct phys_region *ph;
for(ph = vr->first; ph; ph = ph->next)
n++;
return n;
}
#endif
/*===========================================================================*
* map_copy_region *
*===========================================================================*/
PRIVATE struct vir_region *map_copy_region(struct vir_region *vr)
{
/* map_copy_region creates a complete copy of the vir_region
* data structure, linking in the same phys_blocks directly,
* but all in limbo, i.e., the caller has to link the vir_region
* to a process. Therefore it doesn't increase the refcount in
* the phys_block; the caller has to do this once it's linked.
* The reason for this is to keep the sanity checks working
* within this function.
*/
struct vir_region *newvr;
struct phys_region *ph, *prevph = NULL;
#if SANITYCHECKS
int cr;
cr = countregions(vr);
#endif
if(!SLABALLOC(newvr))
return NULL;
*newvr = *vr;
newvr->first = NULL;
newvr->next = NULL;
SANITYCHECK(SCL_FUNCTIONS);
for(ph = vr->first; ph; ph = ph->next) {
struct phys_region *newph;
if(!SLABALLOC(newph)) {
map_free(newvr);
return NULL;
}
newph->next = NULL;
newph->ph = ph->ph;
newph->next_ph_list = NULL;
newph->parent = newvr;
if(prevph) prevph->next = newph;
else newvr->first = newph;
prevph = newph;
SANITYCHECK(SCL_DETAIL);
vm_assert(countregions(vr) == cr);
}
vm_assert(countregions(vr) == countregions(newvr));
SANITYCHECK(SCL_FUNCTIONS);
return newvr;
}
/*=========================================================================*
* map_writept *
*=========================================================================*/
PUBLIC int map_writept(struct vmproc *vmp)
{
struct vir_region *vr;
struct phys_region *ph;
int ropages = 0, rwpages = 0;
for(vr = vmp->vm_regions; vr; vr = vr->next)
for(ph = vr->first; ph; ph = ph->next) {
map_ph_writept(vmp, vr, ph->ph, &ropages, &rwpages);
}
return OK;
}
/*========================================================================*
* map_proc_copy *
*========================================================================*/
PUBLIC int map_proc_copy(dst, src)
struct vmproc *dst;
struct vmproc *src;
{
struct vir_region *vr, *prevvr = NULL;
dst->vm_regions = NULL;
SANITYCHECK(SCL_FUNCTIONS);
for(vr = src->vm_regions; vr; vr = vr->next) {
struct vir_region *newvr;
struct phys_region *orig_ph, *new_ph;
SANITYCHECK(SCL_DETAIL);
if(!(newvr = map_copy_region(vr))) {
map_free_proc(dst);
SANITYCHECK(SCL_FUNCTIONS);
return ENOMEM;
}
SANITYCHECK(SCL_DETAIL);
newvr->parent = dst;
if(prevvr) { prevvr->next = newvr; }
else { dst->vm_regions = newvr; }
new_ph = newvr->first;
for(orig_ph = vr->first; orig_ph; orig_ph = orig_ph->next) {
struct phys_block *pb;
/* Check two physregions both are nonnull,
* are different, and match physblocks.
*/
vm_assert(orig_ph && new_ph);
vm_assert(orig_ph != new_ph);
pb = orig_ph->ph;
vm_assert(pb == new_ph->ph);
/* Link in new physregion. */
vm_assert(!new_ph->next_ph_list);
new_ph->next_ph_list = pb->firstregion;
pb->firstregion = new_ph;
SLABSANE(new_ph);
SLABSANE(new_ph->next_ph_list);
/* Increase phys block refcount */
vm_assert(pb->refcount > 0);
pb->refcount++;
vm_assert(pb->refcount > 1);
/* Get next new physregion */
new_ph = new_ph->next;
}
vm_assert(!new_ph);
SANITYCHECK(SCL_DETAIL);
prevvr = newvr;
SANITYCHECK(SCL_DETAIL);
}
SANITYCHECK(SCL_DETAIL);
map_writept(src);
map_writept(dst);
SANITYCHECK(SCL_FUNCTIONS);
return OK;
}
/*========================================================================*
* map_proc_kernel *
*========================================================================*/
PUBLIC struct vir_region *map_proc_kernel(struct vmproc *vmp)
{
struct vir_region *vr;
/* We assume these are the first regions to be mapped to
* make the function a bit simpler (free all regions on error).
*/
vm_assert(!vmp->vm_regions);
vm_assert(vmproc[VMP_SYSTEM].vm_flags & VMF_INUSE);
vm_assert(!(KERNEL_TEXT % VM_PAGE_SIZE));
vm_assert(!(KERNEL_TEXT_LEN % VM_PAGE_SIZE));
vm_assert(!(KERNEL_DATA % VM_PAGE_SIZE));
vm_assert(!(KERNEL_DATA_LEN % VM_PAGE_SIZE));
if(!(vr = map_page_region(vmp, KERNEL_TEXT, 0, KERNEL_TEXT_LEN,
KERNEL_TEXT, VR_DIRECT | VR_WRITABLE | VR_NOPF, 0)) ||
!(vr = map_page_region(vmp, KERNEL_DATA, 0, KERNEL_DATA_LEN,
KERNEL_DATA, VR_DIRECT | VR_WRITABLE | VR_NOPF, 0))) {
map_free_proc(vmp);
return NULL;
}
return vr; /* Return pointer not useful, just non-NULL. */
}
/*========================================================================*
* map_region_extend *
*========================================================================*/
PUBLIC int map_region_extend(struct vmproc *vmp, struct vir_region *vr,
vir_bytes delta)
{
vir_bytes end;
vm_assert(vr);
vm_assert(vr->flags & VR_ANON);
vm_assert(!(delta % VM_PAGE_SIZE));
if(!delta) return OK;
end = vr->vaddr + vr->length;
vm_assert(end >= vr->vaddr);
if(end + delta <= end) {
printf("VM: strange delta 0x%lx\n", delta);
return ENOMEM;
}
if(!vr->next || end + delta <= vr->next->vaddr) {
vr->length += delta;
return OK;
}
map_printmap(vmp);
return ENOMEM;
}
/*========================================================================*
* map_region_shrink *
*========================================================================*/
PUBLIC int map_region_shrink(struct vir_region *vr, vir_bytes delta)
{
vm_assert(vr);
vm_assert(vr->flags & VR_ANON);
vm_assert(!(delta % VM_PAGE_SIZE));
#if 0
printf("VM: ignoring region shrink\n");
#endif
return OK;
}
PUBLIC struct vir_region *map_region_lookup_tag(vmp, tag)
struct vmproc *vmp;
u32_t tag;
{
struct vir_region *vr;
for(vr = vmp->vm_regions; vr; vr = vr->next)
if(vr->tag == tag)
return vr;
return NULL;
}
PUBLIC void map_region_set_tag(struct vir_region *vr, u32_t tag)
{
vr->tag = tag;
}
PUBLIC u32_t map_region_get_tag(struct vir_region *vr)
{
return vr->tag;
}
/*========================================================================*
* map_unmap_region *
*========================================================================*/
PUBLIC int map_unmap_region(struct vmproc *vmp, struct vir_region *region)
{
struct vir_region *r, *nextr, *prev = NULL;
SANITYCHECK(SCL_FUNCTIONS);
for(r = vmp->vm_regions; r; r = r->next) {
if(r == region)
break;
prev = r;
}
SANITYCHECK(SCL_DETAIL);
if(r == NULL)
vm_panic("map_unmap_region: region not found\n", NO_NUM);
if(!prev)
vmp->vm_regions = r->next;
else
prev->next = r->next;
map_free(r);
SANITYCHECK(SCL_DETAIL);
if(pt_writemap(&vmp->vm_pt, r->vaddr,
MAP_NONE, r->length, 0, WMF_OVERWRITE) != OK) {
printf("VM: map_unmap_region: pt_writemap failed\n");
return ENOMEM;
}
SANITYCHECK(SCL_FUNCTIONS);
return OK;
}