Basic VM and other minor improvements.

Not complete, probably not fully debugged or optimized.
This commit is contained in:
Ben Gras
2008-11-19 12:26:10 +00:00
parent c888305e21
commit c078ec0331
273 changed files with 10814 additions and 4305 deletions

33
servers/vm/Makefile Normal file
View File

@@ -0,0 +1,33 @@
# Makefile for VM server
SERVER = vm
include /etc/make.conf
OBJ = main.o alloc.o utility.o exec.o exit.o fork.o break.o \
signal.o vfs.o mmap.o slaballoc.o region.o pagefaults.o
ARCHOBJ = $(ARCH)/vm.o $(ARCH)/pagetable.o $(ARCH)/arch_pagefaults.o $(ARCH)/util.o
CPPFLAGS=-I../../kernel/arch/$(ARCH)/include -I$(ARCH)
CFLAGS = $(CPROFILE) $(CPPFLAGS)
# build local binary
all build install: $(SERVER)
#install $(SERVER)
$(SERVER): $(OBJ) $(ARCHOBJ)
cd $(ARCH) && $(MAKE)
$(CC) -o $@ $(LDFLAGS) $(OBJ) $(ARCHOBJ) -lsys
# clean up local files
clean:
rm -f $(SERVER) *.o *.bak
cd $(ARCH) && $(MAKE) $@
depend:
cd $(ARCH) && $(MAKE) $@
mkdep "$(CC) -E $(CPPFLAGS)" *.c $(ARCH)/*.c > .depend
# Include generated dependencies.
include .depend

832
servers/vm/alloc.c Normal file
View File

@@ -0,0 +1,832 @@
/* This file is concerned with allocating and freeing arbitrary-size blocks of
* physical memory on behalf of the FORK and EXEC system calls. The key data
* structure used is the hole table, which maintains a list of holes in memory.
* It is kept sorted in order of increasing memory address. The addresses
* it contains refers to physical memory, starting at absolute address 0
* (i.e., they are not relative to the start of PM). During system
* initialization, that part of memory containing the interrupt vectors,
* kernel, and PM are "allocated" to mark them as not available and to
* remove them from the hole list.
*
* The entry points into this file are:
* alloc_mem: allocate a given sized chunk of memory
* free_mem: release a previously allocated chunk of memory
* mem_init: initialize the tables when PM start up
*/
#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 <memory.h>
#include "vm.h"
#include "proto.h"
#include "util.h"
#include "glo.h"
/* Initially, no free pages are known. */
PRIVATE phys_bytes free_pages_head = NO_MEM; /* Physical address in bytes. */
/* Used for sanity check. */
PRIVATE phys_bytes mem_low, mem_high;
#define vm_assert_range(addr, len) \
vm_assert((addr) >= mem_low); \
vm_assert((addr) + (len) - 1 <= mem_high);
struct hole {
struct hole *h_next; /* pointer to next entry on the list */
phys_clicks h_base; /* where does the hole begin? */
phys_clicks h_len; /* how big is the hole? */
int freelist;
int holelist;
};
#define NIL_HOLE (struct hole *) 0
#define _NR_HOLES (_NR_PROCS*2) /* No. of memory holes maintained by VM */
PRIVATE struct hole hole[_NR_HOLES];
PRIVATE struct hole *hole_head; /* pointer to first hole */
PRIVATE struct hole *free_slots;/* ptr to list of unused table slots */
FORWARD _PROTOTYPE( void del_slot, (struct hole *prev_ptr, struct hole *hp) );
FORWARD _PROTOTYPE( void merge, (struct hole *hp) );
FORWARD _PROTOTYPE( void free_pages, (phys_bytes addr, int pages) );
FORWARD _PROTOTYPE( phys_bytes alloc_pages, (int pages, int flags) );
#if SANITYCHECKS
FORWARD _PROTOTYPE( void holes_sanity_f, (char *fn, int line) );
#define CHECKHOLES holes_sanity_f(__FILE__, __LINE__)
#else
#define CHECKHOLES
#endif
/* Sanity check for parameters of node p. */
#define vm_assert_params(p, bytes, next) { \
vm_assert((p) != NO_MEM); \
vm_assert(!((bytes) % VM_PAGE_SIZE)); \
vm_assert(!((next) % VM_PAGE_SIZE)); \
vm_assert((bytes) > 0); \
vm_assert((p) + (bytes) > (p)); \
vm_assert((next) == NO_MEM || ((p) + (bytes) <= (next))); \
vm_assert_range((p), (bytes)); \
vm_assert_range((next), 1); \
}
/* Retrieve size of free block and pointer to next block from physical
* address (page) p.
*/
#define GET_PARAMS(p, bytes, next) { \
phys_readaddr((p), &(bytes), &(next)); \
vm_assert_params((p), (bytes), (next)); \
}
/* Write parameters to physical page p. */
#define SET_PARAMS(p, bytes, next) { \
vm_assert_params((p), (bytes), (next)); \
phys_writeaddr((p), (bytes), (next)); \
}
void availbytes(vir_bytes *bytes, vir_bytes *chunks)
{
phys_bytes p, nextp;
*bytes = 0;
*chunks = 0;
for(p = free_pages_head; p != NO_MEM; p = nextp) {
phys_bytes thissize, ret;
GET_PARAMS(p, thissize, nextp);
(*bytes) += thissize;
(*chunks)++;
if(nextp != NO_MEM) {
vm_assert(nextp > p);
vm_assert(nextp > p + thissize);
}
}
return;
}
#if SANITYCHECKS
/*===========================================================================*
* holes_sanity_f *
*===========================================================================*/
PRIVATE void holes_sanity_f(file, line)
char *file;
int line;
{
#define myassert(c) { \
if(!(c)) { \
printf("holes_sanity_f:%s:%d: %s failed\n", file, line, #c); \
util_stacktrace(); \
vm_panic("assert failed.", NO_NUM); } \
}
int h, c = 0, n = 0;
struct hole *hp;
/* Reset flags */
for(h = 0; h < _NR_HOLES; h++) {
hole[h].freelist = 0;
hole[h].holelist = 0;
}
/* Mark all holes on freelist. */
for(hp = free_slots; hp; hp = hp->h_next) {
myassert(!hp->freelist);
myassert(!hp->holelist);
hp->freelist = 1;
myassert(c < _NR_HOLES);
c++;
n++;
}
/* Mark all holes on holelist. */
c = 0;
for(hp = hole_head; hp; hp = hp->h_next) {
myassert(!hp->freelist);
myassert(!hp->holelist);
hp->holelist = 1;
myassert(c < _NR_HOLES);
c++;
n++;
}
/* Check there are exactly the right number of nodes. */
myassert(n == _NR_HOLES);
/* Make sure each slot is on exactly one of the list. */
c = 0;
for(h = 0; h < _NR_HOLES; h++) {
hp = &hole[h];
myassert(hp->holelist || hp->freelist);
myassert(!(hp->holelist && hp->freelist));
myassert(c < _NR_HOLES);
c++;
}
/* Make sure no holes overlap. */
for(hp = hole_head; hp && hp->h_next; hp = hp->h_next) {
myassert(hp->holelist);
hp->holelist = 1;
/* No holes overlap. */
myassert(hp->h_base + hp->h_len <= hp->h_next->h_base);
/* No uncoalesced holes. */
myassert(hp->h_base + hp->h_len < hp->h_next->h_base);
}
}
#endif
/*===========================================================================*
* alloc_mem_f *
*===========================================================================*/
PUBLIC phys_clicks alloc_mem_f(phys_clicks clicks, u32_t memflags)
{
/* Allocate a block of memory from the free list using first fit. The block
* consists of a sequence of contiguous bytes, whose length in clicks is
* given by 'clicks'. A pointer to the block is returned. The block is
* always on a click boundary. This procedure is called when memory is
* needed for FORK or EXEC.
*/
register struct hole *hp, *prev_ptr;
phys_clicks old_base;
int s;
if(vm_paged) {
vm_assert(CLICK_SIZE == VM_PAGE_SIZE);
return alloc_pages(clicks, memflags);
}
CHECKHOLES;
{
prev_ptr = NIL_HOLE;
hp = hole_head;
while (hp != NIL_HOLE) {
if (hp->h_len >= clicks) {
/* We found a hole that is big enough. Use it. */
old_base = hp->h_base; /* remember where it started */
hp->h_base += clicks; /* bite a piece off */
hp->h_len -= clicks; /* ditto */
/* Delete the hole if used up completely. */
if (hp->h_len == 0) del_slot(prev_ptr, hp);
/* Anything special needs to happen? */
if(memflags & PAF_CLEAR) {
if ((s= sys_memset(0, CLICK_SIZE*old_base,
CLICK_SIZE*clicks)) != OK) {
vm_panic("alloc_mem: sys_memset failed", s);
}
}
/* Return the start address of the acquired block. */
CHECKHOLES;
return(old_base);
}
prev_ptr = hp;
hp = hp->h_next;
}
}
CHECKHOLES;
return(NO_MEM);
}
/*===========================================================================*
* free_mem_f *
*===========================================================================*/
PUBLIC void free_mem_f(phys_clicks base, phys_clicks clicks)
{
/* Return a block of free memory to the hole list. The parameters tell where
* the block starts in physical memory and how big it is. The block is added
* to the hole list. If it is contiguous with an existing hole on either end,
* it is merged with the hole or holes.
*/
register struct hole *hp, *new_ptr, *prev_ptr;
CHECKHOLES;
if (clicks == 0) return;
if(vm_paged) {
vm_assert(CLICK_SIZE == VM_PAGE_SIZE);
free_pages(base, clicks);
return;
}
if ( (new_ptr = free_slots) == NIL_HOLE)
vm_panic("hole table full", NO_NUM);
new_ptr->h_base = base;
new_ptr->h_len = clicks;
free_slots = new_ptr->h_next;
hp = hole_head;
/* If this block's address is numerically less than the lowest hole currently
* available, or if no holes are currently available, put this hole on the
* front of the hole list.
*/
if (hp == NIL_HOLE || base <= hp->h_base) {
/* Block to be freed goes on front of the hole list. */
new_ptr->h_next = hp;
hole_head = new_ptr;
merge(new_ptr);
CHECKHOLES;
return;
}
/* Block to be returned does not go on front of hole list. */
prev_ptr = NIL_HOLE;
while (hp != NIL_HOLE && base > hp->h_base) {
prev_ptr = hp;
hp = hp->h_next;
}
/* We found where it goes. Insert block after 'prev_ptr'. */
new_ptr->h_next = prev_ptr->h_next;
prev_ptr->h_next = new_ptr;
merge(prev_ptr); /* sequence is 'prev_ptr', 'new_ptr', 'hp' */
CHECKHOLES;
}
/*===========================================================================*
* del_slot *
*===========================================================================*/
PRIVATE void del_slot(prev_ptr, hp)
/* pointer to hole entry just ahead of 'hp' */
register struct hole *prev_ptr;
/* pointer to hole entry to be removed */
register struct hole *hp;
{
/* Remove an entry from the hole list. This procedure is called when a
* request to allocate memory removes a hole in its entirety, thus reducing
* the numbers of holes in memory, and requiring the elimination of one
* entry in the hole list.
*/
if (hp == hole_head)
hole_head = hp->h_next;
else
prev_ptr->h_next = hp->h_next;
hp->h_next = free_slots;
hp->h_base = hp->h_len = 0;
free_slots = hp;
}
/*===========================================================================*
* merge *
*===========================================================================*/
PRIVATE void merge(hp)
register struct hole *hp; /* ptr to hole to merge with its successors */
{
/* Check for contiguous holes and merge any found. Contiguous holes can occur
* when a block of memory is freed, and it happens to abut another hole on
* either or both ends. The pointer 'hp' points to the first of a series of
* three holes that can potentially all be merged together.
*/
register struct hole *next_ptr;
/* If 'hp' points to the last hole, no merging is possible. If it does not,
* try to absorb its successor into it and free the successor's table entry.
*/
if ( (next_ptr = hp->h_next) == NIL_HOLE) return;
if (hp->h_base + hp->h_len == next_ptr->h_base) {
hp->h_len += next_ptr->h_len; /* first one gets second one's mem */
del_slot(hp, next_ptr);
} else {
hp = next_ptr;
}
/* If 'hp' now points to the last hole, return; otherwise, try to absorb its
* successor into it.
*/
if ( (next_ptr = hp->h_next) == NIL_HOLE) return;
if (hp->h_base + hp->h_len == next_ptr->h_base) {
hp->h_len += next_ptr->h_len;
del_slot(hp, next_ptr);
}
}
/*===========================================================================*
* mem_init *
*===========================================================================*/
PUBLIC void mem_init(chunks)
struct memory *chunks; /* list of free memory chunks */
{
/* Initialize hole lists. There are two lists: 'hole_head' points to a linked
* list of all the holes (unused memory) in the system; 'free_slots' points to
* a linked list of table entries that are not in use. Initially, the former
* list has one entry for each chunk of physical memory, and the second
* list links together the remaining table slots. As memory becomes more
* fragmented in the course of time (i.e., the initial big holes break up into
* smaller holes), new table slots are needed to represent them. These slots
* are taken from the list headed by 'free_slots'.
*/
int i, first = 0;
register struct hole *hp;
/* Put all holes on the free list. */
for (hp = &hole[0]; hp < &hole[_NR_HOLES]; hp++) {
hp->h_next = hp + 1;
hp->h_base = hp->h_len = 0;
}
hole[_NR_HOLES-1].h_next = NIL_HOLE;
hole_head = NIL_HOLE;
free_slots = &hole[0];
/* Use the chunks of physical memory to allocate holes. */
for (i=NR_MEMS-1; i>=0; i--) {
if (chunks[i].size > 0) {
phys_bytes from = CLICK2ABS(chunks[i].base),
to = CLICK2ABS(chunks[i].base+chunks[i].size)-1;
if(first || from < mem_low) mem_low = from;
if(first || to > mem_high) mem_high = to;
FREE_MEM(chunks[i].base, chunks[i].size);
first = 0;
}
}
CHECKHOLES;
}
/*===========================================================================*
* alloc_pages *
*===========================================================================*/
PRIVATE PUBLIC phys_bytes alloc_pages(int pages, int memflags)
{
phys_bytes bytes, p, nextp, prevp = NO_MEM;
phys_bytes prevsize = 0;
#if SANITYCHECKS
vir_bytes avail1, avail2, chunks1, chunks2;
availbytes(&avail1, &chunks1);
#endif
vm_assert(pages > 0);
bytes = CLICK2ABS(pages);
vm_assert(ABS2CLICK(bytes) == pages);
#if SANITYCHECKS
#define ALLOCRETURNCHECK \
availbytes(&avail2, &chunks2); \
vm_assert(avail1 - bytes == avail2); \
vm_assert(chunks1 == chunks2 || chunks1-1 == chunks2); \
if(verbosealloc) \
printf("memory: 0x%lx bytes in %d chunks\n", avail2, chunks2);
#else
#define ALLOCRETURNCHECK
#endif
for(p = free_pages_head; p != NO_MEM; p = nextp) {
phys_bytes thissize, ret;
GET_PARAMS(p, thissize, nextp);
if(thissize >= bytes) {
/* We found a chunk that's big enough. */
ret = p + thissize - bytes;
thissize -= bytes;
if(thissize == 0) {
/* Special case: remove this link entirely. */
if(prevp == NO_MEM)
free_pages_head = nextp;
else {
vm_assert(prevsize > 0);
SET_PARAMS(prevp, prevsize, nextp);
}
} else {
/* Remove memory from this chunk. */
SET_PARAMS(p, thissize, nextp);
}
/* Clear memory if requested. */
if(memflags & PAF_CLEAR) {
int s;
if ((s= sys_memset(0, ret, bytes)) != OK) {
vm_panic("alloc_pages: sys_memset failed", s);
}
}
/* Check if returned range is actual good memory. */
vm_assert_range(ret, bytes);
ALLOCRETURNCHECK;
/* Return it in clicks. */
return ABS2CLICK(ret);
}
prevp = p;
prevsize = thissize;
}
return NO_MEM;
}
/*===========================================================================*
* free_pages *
*===========================================================================*/
PRIVATE PUBLIC void free_pages(phys_bytes pageno, int npages)
{
phys_bytes p, origsize,
size, nextaddr, thissize, prevp = NO_MEM, pageaddr;
#if SANITYCHECKS
vir_bytes avail1, avail2, chunks1, chunks2;
availbytes(&avail1, &chunks1);
#endif
#if SANITYCHECKS
#define FREERETURNCHECK \
availbytes(&avail2, &chunks2); \
vm_assert(avail1 + origsize == avail2); \
vm_assert(chunks1 == chunks2 || chunks1+1 == chunks2 || chunks1-1 == chunks2); \
if(verbosealloc) \
printf("memory: 0x%lx bytes in %d chunks\n", avail2, chunks2);
#else
#define FREERETURNCHECK
#endif
/* Basic sanity check. */
vm_assert(npages > 0);
vm_assert(pageno != NO_MEM); /* Page number must be reasonable. */
/* Convert page and pages to bytes. */
pageaddr = CLICK2ABS(pageno);
origsize = size = npages * VM_PAGE_SIZE; /* Size in bytes. */
vm_assert(pageaddr != NO_MEM);
vm_assert(ABS2CLICK(pageaddr) == pageno);
vm_assert_range(pageaddr, size);
/* More sanity checks. */
vm_assert(ABS2CLICK(size) == npages); /* Sanity. */
vm_assert(pageaddr + size > pageaddr); /* Must not overflow. */
/* Special case: no free pages. */
if(free_pages_head == NO_MEM) {
free_pages_head = pageaddr;
SET_PARAMS(pageaddr, size, NO_MEM);
FREERETURNCHECK;
return;
}
/* Special case: the free block is before the current head. */
if(pageaddr < free_pages_head) {
phys_bytes newsize, newnext, headsize, headnext;
vm_assert(pageaddr + size <= free_pages_head);
GET_PARAMS(free_pages_head, headsize, headnext);
newsize = size;
if(pageaddr + size == free_pages_head) {
/* Special case: contiguous. */
newsize += headsize;
newnext = headnext;
} else {
newnext = free_pages_head;
}
SET_PARAMS(pageaddr, newsize, newnext);
free_pages_head = pageaddr;
FREERETURNCHECK;
return;
}
/* Find where to put the block in the free list. */
for(p = free_pages_head; p < pageaddr; p = nextaddr) {
GET_PARAMS(p, thissize, nextaddr);
if(nextaddr == NO_MEM) {
/* Special case: page is at the end of the list. */
if(p + thissize == pageaddr) {
/* Special case: contiguous. */
SET_PARAMS(p, thissize + size, NO_MEM);
FREERETURNCHECK;
} else {
SET_PARAMS(p, thissize, pageaddr);
SET_PARAMS(pageaddr, size, NO_MEM);
FREERETURNCHECK;
}
return;
}
prevp = p;
}
/* Normal case: insert page block between two others.
* The first block starts at 'prevp' and is 'thissize'.
* The second block starts at 'p' and is 'nextsize'.
* The block that has to come in between starts at
* 'pageaddr' and is size 'size'.
*/
vm_assert(p != NO_MEM);
vm_assert(prevp != NO_MEM);
vm_assert(prevp < p);
vm_assert(p == nextaddr);
#if SANITYCHECKS
{
vir_bytes prevpsize, prevpnext;
GET_PARAMS(prevp, prevpsize, prevpnext);
vm_assert(prevpsize == thissize);
vm_assert(prevpnext == p);
availbytes(&avail2, &chunks2);
vm_assert(avail1 == avail2);
}
#endif
if(prevp + thissize == pageaddr) {
/* Special case: first block is contiguous with freed one. */
phys_bytes newsize = thissize + size;
SET_PARAMS(prevp, newsize, p);
pageaddr = prevp;
size = newsize;
} else {
SET_PARAMS(prevp, thissize, pageaddr);
}
/* The block has been inserted (and possibly merged with the
* first one). Check if it has to be merged with the second one.
*/
if(pageaddr + size == p) {
phys_bytes nextsize, nextnextaddr;
/* Special case: freed block is contiguous with next one. */
GET_PARAMS(p, nextsize, nextnextaddr);
SET_PARAMS(pageaddr, size+nextsize, nextnextaddr);
FREERETURNCHECK;
} else {
SET_PARAMS(pageaddr, size, p);
FREERETURNCHECK;
}
return;
}
#define NR_DMA 16
PRIVATE struct dmatab
{
int dt_flags;
endpoint_t dt_proc;
phys_bytes dt_base;
phys_bytes dt_size;
phys_clicks dt_seg_base;
phys_clicks dt_seg_size;
} dmatab[NR_DMA];
#define DTF_INUSE 1
#define DTF_RELEASE_DMA 2
#define DTF_RELEASE_SEG 4
/*===========================================================================*
* do_adddma *
*===========================================================================*/
PUBLIC int do_adddma(message *msg)
{
endpoint_t req_proc_e, target_proc_e;
int i, proc_n;
phys_bytes base, size;
struct vmproc *vmp;
req_proc_e= msg->VMAD_REQ;
target_proc_e= msg->VMAD_EP;
base= msg->VMAD_START;
size= msg->VMAD_SIZE;
/* Find empty slot */
for (i= 0; i<NR_DMA; i++)
{
if (!(dmatab[i].dt_flags & DTF_INUSE))
break;
}
if (i >= NR_DMA)
{
printf("vm:do_adddma: dma table full\n");
for (i= 0; i<NR_DMA; i++)
{
printf("%d: flags 0x%x proc %d base 0x%x size 0x%x\n",
i, dmatab[i].dt_flags,
dmatab[i].dt_proc,
dmatab[i].dt_base,
dmatab[i].dt_size);
}
vm_panic("adddma: table full", NO_NUM);
return ENOSPC;
}
/* Find target process */
if (vm_isokendpt(target_proc_e, &proc_n) != OK)
{
printf("vm:do_adddma: endpoint %d not found\n", target_proc_e);
return EINVAL;
}
vmp= &vmproc[proc_n];
vmp->vm_flags |= VMF_HAS_DMA;
dmatab[i].dt_flags= DTF_INUSE;
dmatab[i].dt_proc= target_proc_e;
dmatab[i].dt_base= base;
dmatab[i].dt_size= size;
return OK;
}
/*===========================================================================*
* do_deldma *
*===========================================================================*/
PUBLIC int do_deldma(message *msg)
{
endpoint_t req_proc_e, target_proc_e;
int i, j, proc_n;
phys_bytes base, size;
struct vmproc *vmp;
req_proc_e= msg->VMDD_REQ;
target_proc_e= msg->VMDD_EP;
base= msg->VMDD_START;
size= msg->VMDD_SIZE;
/* Find slot */
for (i= 0; i<NR_DMA; i++)
{
if (!(dmatab[i].dt_flags & DTF_INUSE))
continue;
if (dmatab[i].dt_proc == target_proc_e &&
dmatab[i].dt_base == base &&
dmatab[i].dt_size == size)
{
break;
}
}
if (i >= NR_DMA)
{
printf("vm:do_deldma: slot not found\n");
return ESRCH;
}
if (dmatab[i].dt_flags & DTF_RELEASE_SEG)
{
/* Check if we have to release the segment */
for (j= 0; j<NR_DMA; j++)
{
if (j == i)
continue;
if (!(dmatab[j].dt_flags & DTF_INUSE))
continue;
if (!(dmatab[j].dt_flags & DTF_RELEASE_SEG))
continue;
if (dmatab[i].dt_proc == target_proc_e)
break;
}
if (j >= NR_DMA)
{
/* Last segment */
FREE_MEM(dmatab[i].dt_seg_base,
dmatab[i].dt_seg_size);
}
}
dmatab[i].dt_flags &= ~DTF_INUSE;
return OK;
}
/*===========================================================================*
* do_getdma *
*===========================================================================*/
PUBLIC int do_getdma(message *msg)
{
endpoint_t target_proc_e;
int i, proc_n;
phys_bytes base, size;
struct vmproc *vmp;
/* Find slot to report */
for (i= 0; i<NR_DMA; i++)
{
if (!(dmatab[i].dt_flags & DTF_INUSE))
continue;
if (!(dmatab[i].dt_flags & DTF_RELEASE_DMA))
continue;
printf("do_getdma: setting reply to 0x%x@0x%x proc %d\n",
dmatab[i].dt_size, dmatab[i].dt_base,
dmatab[i].dt_proc);
msg->VMGD_PROCP= dmatab[i].dt_proc;
msg->VMGD_BASEP= dmatab[i].dt_base;
msg->VMGD_SIZEP= dmatab[i].dt_size;
return OK;
}
/* Nothing */
return EAGAIN;
}
/*===========================================================================*
* release_dma *
*===========================================================================*/
PUBLIC void release_dma(struct vmproc *vmp)
{
int i, found_one;
vm_panic("release_dma not done", NO_NUM);
#if 0
found_one= FALSE;
for (i= 0; i<NR_DMA; i++)
{
if (!(dmatab[i].dt_flags & DTF_INUSE))
continue;
if (dmatab[i].dt_proc != vmp->vm_endpoint)
continue;
dmatab[i].dt_flags |= DTF_RELEASE_DMA | DTF_RELEASE_SEG;
dmatab[i].dt_seg_base= base;
dmatab[i].dt_seg_size= size;
found_one= TRUE;
}
if (!found_one)
FREE_MEM(base, size);
msg->VMRD_FOUND = found_one;
#endif
return;
}
/*===========================================================================*
* do_allocmem *
*===========================================================================*/
PUBLIC int do_allocmem(message *m)
{
phys_clicks mem;
if((mem=ALLOC_MEM((phys_clicks) m->VMAM_CLICKS, PAF_CLEAR)) == NO_MEM) {
return ENOMEM;
}
m->VMAM_MEMBASE = mem;
printf("VM: do_allocmem: 0x%lx clicks OK at 0x%lx\n", m->VMAM_CLICKS, mem);
return OK;
}

67
servers/vm/asynsend.c Normal file
View File

@@ -0,0 +1,67 @@
#define _SYSTEM 1
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#include <stdlib.h>
#include "proto.h"
#include "util.h"
#define SENDSLOTS _NR_PROCS
PRIVATE asynmsg_t msgtable[SENDSLOTS];
PRIVATE size_t msgtable_n= SENDSLOTS;
PUBLIC int asynsend(dst, mp)
endpoint_t dst;
message *mp;
{
int i;
unsigned flags;
/* Find slot in table */
for (i= 0; i<msgtable_n; i++)
{
flags= msgtable[i].flags;
if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE))
{
if (msgtable[i].result != OK)
{
printf(
"VM: asynsend: found completed entry %d with error %d\n",
i, msgtable[i].result);
}
break;
}
if (flags == AMF_EMPTY)
break;
}
if (i >= msgtable_n)
vm_panic("asynsend: should resize table", i);
msgtable[i].dst= dst;
msgtable[i].msg= *mp;
msgtable[i].flags= AMF_VALID; /* Has to be last. The kernel
* scans this table while we are
* sleeping.
*/
/* Tell the kernel to rescan the table */
return senda(msgtable, msgtable_n);
}

179
servers/vm/break.c Normal file
View File

@@ -0,0 +1,179 @@
/* The MINIX model of memory allocation reserves a fixed amount of memory for
* the combined text, data, and stack segments. The amount used for a child
* process created by FORK is the same as the parent had. If the child does
* an EXEC later, the new size is taken from the header of the file EXEC'ed.
*
* The layout in memory consists of the text segment, followed by the data
* segment, followed by a gap (unused memory), followed by the stack segment.
* The data segment grows upward and the stack grows downward, so each can
* take memory from the gap. If they meet, the process must be killed. The
* procedures in this file deal with the growth of the data and stack segments.
*
* The entry points into this file are:
* do_brk: BRK/SBRK system calls to grow or shrink the data segment
* adjust: see if a proposed segment adjustment is allowed
*/
#define _SYSTEM 1
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <errno.h>
#include <env.h>
#include "glo.h"
#include "vm.h"
#include "proto.h"
#include "util.h"
#define DATA_CHANGED 1 /* flag value when data segment size changed */
#define STACK_CHANGED 2 /* flag value when stack size changed */
/*===========================================================================*
* do_brk *
*===========================================================================*/
PUBLIC int do_brk(message *msg)
{
/* Perform the brk(addr) system call.
* The parameter, 'addr' is the new virtual address in D space.
*/
int proc;
if(vm_isokendpt(msg->VMB_ENDPOINT, &proc) != OK) {
printf("VM: bogus endpoint VM_BRK %d\n", msg->VMB_ENDPOINT);
return EINVAL;
}
return real_brk(&vmproc[proc], (vir_bytes) msg->VMB_ADDR);
}
/*===========================================================================*
* adjust *
*===========================================================================*/
PUBLIC int adjust(rmp, data_clicks, sp)
struct vmproc *rmp; /* whose memory is being adjusted? */
vir_clicks data_clicks; /* how big is data segment to become? */
vir_bytes sp; /* new value of sp */
{
/* See if data and stack segments can coexist, adjusting them if need be.
* Memory is never allocated or freed. Instead it is added or removed from the
* gap between data segment and stack segment. If the gap size becomes
* negative, the adjustment of data or stack fails and ENOMEM is returned.
*/
register struct mem_map *mem_sp, *mem_dp;
vir_clicks sp_click, gap_base, sp_lower, old_clicks;
int changed, r;
long base_of_stack, sp_delta; /* longs avoid certain problems */
mem_dp = &rmp->vm_arch.vm_seg[D]; /* pointer to data segment map */
mem_sp = &rmp->vm_arch.vm_seg[S]; /* pointer to stack segment map */
changed = 0; /* set when either segment changed */
/* See if stack size has gone negative (i.e., sp too close to 0xFFFF...) */
base_of_stack = (long) mem_sp->mem_vir + (long) mem_sp->mem_len;
sp_click = sp >> CLICK_SHIFT; /* click containing sp */
if (sp_click >= base_of_stack)
{
return(ENOMEM); /* sp too high */
}
/* Compute size of gap between stack and data segments. */
sp_delta = (long) mem_sp->mem_vir - (long) sp_click;
sp_lower = (sp_delta > 0 ? sp_click : mem_sp->mem_vir);
/* Add a safety margin for future stack growth. Impossible to do right. */
#define SAFETY_BYTES (384 * sizeof(char *))
#define SAFETY_CLICKS ((SAFETY_BYTES + CLICK_SIZE - 1) / CLICK_SIZE)
gap_base = mem_dp->mem_vir + data_clicks + SAFETY_CLICKS;
if (sp_lower < gap_base)
{
return(ENOMEM); /* data and stack collided */
}
/* Update data length (but not data orgin) on behalf of brk() system call. */
old_clicks = mem_dp->mem_len;
if (data_clicks != mem_dp->mem_len) {
mem_dp->mem_len = data_clicks;
changed |= DATA_CHANGED;
}
/* Update stack length and origin due to change in stack pointer. */
if (sp_delta > 0) {
mem_sp->mem_vir -= sp_delta;
mem_sp->mem_phys -= sp_delta;
mem_sp->mem_len += sp_delta;
changed |= STACK_CHANGED;
}
/* Do the new data and stack segment sizes fit in the address space? */
r = (rmp->vm_arch.vm_seg[D].mem_vir + rmp->vm_arch.vm_seg[D].mem_len >
rmp->vm_arch.vm_seg[S].mem_vir) ? ENOMEM : OK;
if(r == OK && (rmp->vm_flags & VMF_HASPT) &&
rmp->vm_endpoint != VM_PROC_NR) {
vm_assert(rmp->vm_heap);
if(old_clicks < data_clicks) {
vir_bytes more;
more = (data_clicks - old_clicks) << CLICK_SHIFT;
if(map_region_extend(rmp->vm_heap, more) != OK) {
printf("VM: brk: map_region_extend failed\n");
return ENOMEM;
}
} else if(old_clicks > data_clicks) {
vir_bytes less;
less = (old_clicks - data_clicks) << CLICK_SHIFT;
if(map_region_shrink(rmp->vm_heap, less) != OK) {
printf("VM: brk: map_region_shrink failed\n");
return ENOMEM;
}
}
}
if (r == OK)
return(OK);
/* New sizes don't fit or require too many page/segment registers. Restore.*/
if (changed & DATA_CHANGED) mem_dp->mem_len = old_clicks;
if (changed & STACK_CHANGED) {
mem_sp->mem_vir += sp_delta;
mem_sp->mem_phys += sp_delta;
mem_sp->mem_len -= sp_delta;
}
return(ENOMEM);
}
/*===========================================================================*
* real_brk *
*===========================================================================*/
PUBLIC int real_brk(vmp, v)
struct vmproc *vmp;
vir_bytes v;
{
vir_bytes new_sp;
vir_clicks new_clicks;
int r;
new_clicks = (vir_clicks) ( ((long) v + CLICK_SIZE - 1) >> CLICK_SHIFT);
if (new_clicks < vmp->vm_arch.vm_seg[D].mem_vir) {
printf("VM: real_brk failed because new_clicks too high: %d\n",
new_clicks);
return(ENOMEM);
}
new_clicks -= vmp->vm_arch.vm_seg[D].mem_vir;
if ((r=get_stack_ptr(vmp->vm_endpoint, &new_sp)) != OK)
vm_panic("couldn't get stack pointer", r);
r = adjust(vmp, new_clicks, new_sp);
return r;
}

413
servers/vm/exec.c Normal file
View File

@@ -0,0 +1,413 @@
#define _SYSTEM 1
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/const.h>
#include <errno.h>
#include <assert.h>
#include <env.h>
#include <pagetable.h>
#include "glo.h"
#include "proto.h"
#include "util.h"
#include "vm.h"
#include "region.h"
#include "sanitycheck.h"
#include "memory.h"
FORWARD _PROTOTYPE( int new_mem, (struct vmproc *vmp, struct vmproc *sh_vmp,
vir_bytes text_bytes, vir_bytes data_bytes, vir_bytes bss_bytes,
vir_bytes stk_bytes, phys_bytes tot_bytes) );
FORWARD _PROTOTYPE( u32_t find_kernel_top, (void));
/*===========================================================================*
* find_share *
*===========================================================================*/
PUBLIC struct vmproc *find_share(vmp_ign, ino, dev, ctime)
struct vmproc *vmp_ign; /* process that should not be looked at */
ino_t ino; /* parameters that uniquely identify a file */
dev_t dev;
time_t ctime;
{
/* Look for a process that is the file <ino, dev, ctime> in execution. Don't
* accidentally "find" vmp_ign, because it is the process on whose behalf this
* call is made.
*/
struct vmproc *vmp;
for (vmp = &vmproc[0]; vmp < &vmproc[NR_PROCS]; vmp++) {
if (!(vmp->vm_flags & VMF_INUSE)) continue;
if (!(vmp->vm_flags & VMF_SEPARATE)) continue;
if (vmp->vm_flags & VMF_HASPT) continue;
if (vmp == vmp_ign) continue;
if (vmp->vm_ino != ino) continue;
if (vmp->vm_dev != dev) continue;
if (vmp->vm_ctime != ctime) continue;
return vmp;
}
return(NULL);
}
/*===========================================================================*
* exec_newmem *
*===========================================================================*/
PUBLIC int do_exec_newmem(message *msg)
{
int r, proc_e, proc_n;
vir_bytes stack_top;
vir_clicks tc, dc, sc, totc, dvir, s_vir;
struct vmproc *vmp, *sh_mp;
char *ptr;
struct exec_newmem args;
SANITYCHECK(SCL_FUNCTIONS);
proc_e= msg->VMEN_ENDPOINT;
if (vm_isokendpt(proc_e, &proc_n) != OK)
{
printf("VM:exec_newmem: bad endpoint %d from %d\n",
proc_e, msg->m_source);
return ESRCH;
}
vmp= &vmproc[proc_n];
ptr= msg->VMEN_ARGSPTR;
if(msg->VMEN_ARGSSIZE != sizeof(args)) {
printf("VM:exec_newmem: args size %d != %ld\n",
msg->VMEN_ARGSSIZE, sizeof(args));
return EINVAL;
}
SANITYCHECK(SCL_DETAIL);
r= sys_datacopy(msg->m_source, (vir_bytes)ptr,
SELF, (vir_bytes)&args, sizeof(args));
if (r != OK)
vm_panic("exec_newmem: sys_datacopy failed", r);
/* Check to see if segment sizes are feasible. */
tc = ((unsigned long) args.text_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
dc = (args.data_bytes+args.bss_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
totc = (args.tot_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
sc = (args.args_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
if (dc >= totc) return(ENOEXEC); /* stack must be at least 1 click */
dvir = (args.sep_id ? 0 : tc);
s_vir = dvir + (totc - sc);
r = (dvir + dc > s_vir) ? ENOMEM : OK;
if (r != OK)
return r;
/* Can the process' text be shared with that of one already running? */
if(!vm_paged) {
sh_mp = find_share(vmp, args.st_ino, args.st_dev, args.st_ctime);
} else {
sh_mp = NULL;
}
/* Allocate new memory and release old memory. Fix map and tell
* kernel.
*/
r = new_mem(vmp, sh_mp, args.text_bytes, args.data_bytes,
args.bss_bytes, args.args_bytes, args.tot_bytes);
if (r != OK) return(r);
/* Save file identification to allow it to be shared. */
vmp->vm_ino = args.st_ino;
vmp->vm_dev = args.st_dev;
vmp->vm_ctime = args.st_ctime;
stack_top= ((vir_bytes)vmp->vm_arch.vm_seg[S].mem_vir << CLICK_SHIFT) +
((vir_bytes)vmp->vm_arch.vm_seg[S].mem_len << CLICK_SHIFT);
/* set/clear separate I&D flag */
if (args.sep_id)
vmp->vm_flags |= VMF_SEPARATE;
else
vmp->vm_flags &= ~VMF_SEPARATE;
msg->VMEN_STACK_TOP = (void *) stack_top;
msg->VMEN_FLAGS = 0;
if (!sh_mp) /* Load text if sh_mp = NULL */
msg->VMEN_FLAGS |= EXC_NM_RF_LOAD_TEXT;
return OK;
}
/*===========================================================================*
* new_mem *
*===========================================================================*/
PRIVATE int new_mem(rmp, sh_mp, text_bytes, data_bytes,
bss_bytes,stk_bytes,tot_bytes)
struct vmproc *rmp; /* process to get a new memory map */
struct vmproc *sh_mp; /* text can be shared with this process */
vir_bytes text_bytes; /* text segment size in bytes */
vir_bytes data_bytes; /* size of initialized data in bytes */
vir_bytes bss_bytes; /* size of bss in bytes */
vir_bytes stk_bytes; /* size of initial stack segment in bytes */
phys_bytes tot_bytes; /* total memory to allocate, including gap */
{
/* Allocate new memory and release the old memory. Change the map and report
* the new map to the kernel. Zero the new core image's bss, gap and stack.
*/
vir_clicks text_clicks, data_clicks, gap_clicks, stack_clicks, tot_clicks;
phys_bytes bytes, base, bss_offset;
int s, r2;
static u32_t kernel_top = 0;
SANITYCHECK(SCL_FUNCTIONS);
/* No need to allocate text if it can be shared. */
if (sh_mp != NULL) {
text_bytes = 0;
vm_assert(!vm_paged);
}
/* Acquire the new memory. Each of the 4 parts: text, (data+bss), gap,
* and stack occupies an integral number of clicks, starting at click
* boundary. The data and bss parts are run together with no space.
*/
text_clicks = ((unsigned long) text_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
data_clicks = (data_bytes + bss_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
stack_clicks = (stk_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
tot_clicks = (tot_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
gap_clicks = tot_clicks - data_clicks - stack_clicks;
if ( (int) gap_clicks < 0) return(ENOMEM);
SANITYCHECK(SCL_DETAIL);
/* We've got memory for the new core image. Release the old one. */
if(rmp->vm_flags & VMF_HASPT) {
/* Free page table and memory allocated by pagetable functions. */
rmp->vm_flags &= ~VMF_HASPT;
free_proc(rmp);
} else {
if (find_share(rmp, rmp->vm_ino, rmp->vm_dev, rmp->vm_ctime) == NULL) {
/* No other process shares the text segment, so free it. */
FREE_MEM(rmp->vm_arch.vm_seg[T].mem_phys, rmp->vm_arch.vm_seg[T].mem_len);
}
/* Free the data and stack segments. */
FREE_MEM(rmp->vm_arch.vm_seg[D].mem_phys,
rmp->vm_arch.vm_seg[S].mem_vir
+ rmp->vm_arch.vm_seg[S].mem_len
- rmp->vm_arch.vm_seg[D].mem_vir);
}
/* We have now passed the point of no return. The old core image has been
* forever lost, memory for a new core image has been allocated. Set up
* and report new map.
*/
if(vm_paged) {
vir_bytes hole_clicks;
if(pt_new(&rmp->vm_pt) != OK)
vm_panic("exec_newmem: no new pagetable", NO_NUM);
SANITYCHECK(SCL_DETAIL);
if(!map_proc_kernel(rmp)) {
printf("VM: exec: map_proc_kernel failed\n");
return ENOMEM;
}
if(!kernel_top)
kernel_top = find_kernel_top();
/* Place text at kernel top. */
rmp->vm_arch.vm_seg[T].mem_phys = kernel_top;
rmp->vm_arch.vm_seg[T].mem_vir = 0;
rmp->vm_arch.vm_seg[T].mem_len = text_clicks;
rmp->vm_offset = CLICK2ABS(kernel_top);
vm_assert(!sh_mp);
/* page mapping flags for code */
#define TEXTFLAGS (PTF_PRESENT | PTF_USER | PTF_WRITE)
SANITYCHECK(SCL_DETAIL);
if(text_clicks > 0) {
if(!map_page_region(rmp, CLICK2ABS(kernel_top), 0,
CLICK2ABS(rmp->vm_arch.vm_seg[T].mem_len), 0,
VR_ANON | VR_WRITABLE, 0)) {
SANITYCHECK(SCL_DETAIL);
printf("VM: map_page_region failed (text)\n");
return(ENOMEM);
}
SANITYCHECK(SCL_DETAIL);
}
SANITYCHECK(SCL_DETAIL);
/* Allocate memory for data (including bss, but not including gap
* or stack), make sure it's cleared, and map it in after text
* (if any).
*/
if(!(rmp->vm_heap = map_page_region(rmp,
CLICK2ABS(kernel_top + text_clicks), 0,
CLICK2ABS(data_clicks), 0, VR_ANON | VR_WRITABLE, 0))) {
printf("VM: exec: map_page_region for data failed\n");
return ENOMEM;
}
map_region_set_tag(rmp->vm_heap, VRT_HEAP);
/* How many address space clicks between end of data
* and start of stack?
* VM_STACKTOP is the first address after the stack, as addressed
* from within the user process.
*/
hole_clicks = VM_STACKTOP >> CLICK_SHIFT;
hole_clicks -= data_clicks + stack_clicks + gap_clicks;
if(!map_page_region(rmp,
CLICK2ABS(kernel_top + text_clicks + data_clicks + hole_clicks),
0, CLICK2ABS(stack_clicks+gap_clicks), 0,
VR_ANON | VR_WRITABLE, 0) != OK) {
vm_panic("map_page_region failed for stack", NO_NUM);
}
rmp->vm_arch.vm_seg[D].mem_phys = kernel_top + text_clicks;
rmp->vm_arch.vm_seg[D].mem_vir = 0;
rmp->vm_arch.vm_seg[D].mem_len = data_clicks;
rmp->vm_arch.vm_seg[S].mem_phys = kernel_top +
text_clicks + data_clicks + gap_clicks + hole_clicks;
rmp->vm_arch.vm_seg[S].mem_vir = data_clicks + gap_clicks + hole_clicks;
/* Pretend the stack is the full size of the data segment, so
* we get a full-sized data segment, up to VM_DATATOP.
* After sys_newmap(),, change the stack to what we know the
* stack to be (up to VM_STACKTOP).
*/
rmp->vm_arch.vm_seg[S].mem_len = (VM_DATATOP >> CLICK_SHIFT) -
rmp->vm_arch.vm_seg[S].mem_vir - kernel_top - text_clicks;
/* Where are we allowed to start using the rest of the virtual
* address space?
*/
rmp->vm_stacktop = VM_STACKTOP;
/* What is the final size of the data segment in bytes? */
rmp->vm_arch.vm_data_top =
(rmp->vm_arch.vm_seg[S].mem_vir +
rmp->vm_arch.vm_seg[S].mem_len) << CLICK_SHIFT;
rmp->vm_flags |= VMF_HASPT;
if((s=sys_newmap(rmp->vm_endpoint, rmp->vm_arch.vm_seg)) != OK) {
vm_panic("sys_newmap (vm) failed", s);
}
/* This is the real stack clicks. */
rmp->vm_arch.vm_seg[S].mem_len = stack_clicks;
} else {
phys_clicks new_base;
new_base = ALLOC_MEM(text_clicks + tot_clicks, 0);
if (new_base == NO_MEM) return(ENOMEM);
if (sh_mp != NULL) {
/* Share the text segment. */
rmp->vm_arch.vm_seg[T] = sh_mp->vm_arch.vm_seg[T];
} else {
rmp->vm_arch.vm_seg[T].mem_phys = new_base;
rmp->vm_arch.vm_seg[T].mem_vir = 0;
rmp->vm_arch.vm_seg[T].mem_len = text_clicks;
if (text_clicks > 0)
{
/* Zero the last click of the text segment. Otherwise the
* part of that click may remain unchanged.
*/
base = (phys_bytes)(new_base+text_clicks-1) << CLICK_SHIFT;
if ((s= sys_memset(0, base, CLICK_SIZE)) != OK)
vm_panic("new_mem: sys_memset failed", s);
}
}
/* No paging stuff. */
rmp->vm_flags &= ~VMF_HASPT;
rmp->vm_regions = NULL;
rmp->vm_arch.vm_seg[D].mem_phys = new_base + text_clicks;
rmp->vm_arch.vm_seg[D].mem_vir = 0;
rmp->vm_arch.vm_seg[D].mem_len = data_clicks;
rmp->vm_arch.vm_seg[S].mem_phys = rmp->vm_arch.vm_seg[D].mem_phys +
data_clicks + gap_clicks;
rmp->vm_arch.vm_seg[S].mem_vir = rmp->vm_arch.vm_seg[D].mem_vir +
data_clicks + gap_clicks;
rmp->vm_arch.vm_seg[S].mem_len = stack_clicks;
rmp->vm_stacktop =
CLICK2ABS(rmp->vm_arch.vm_seg[S].mem_vir +
rmp->vm_arch.vm_seg[S].mem_len);
rmp->vm_arch.vm_data_top =
(rmp->vm_arch.vm_seg[S].mem_vir +
rmp->vm_arch.vm_seg[S].mem_len) << CLICK_SHIFT;
if((r2=sys_newmap(rmp->vm_endpoint, rmp->vm_arch.vm_seg)) != OK) {
/* report new map to the kernel */
vm_panic("sys_newmap failed", r2);
}
/* Zero the bss, gap, and stack segment. */
bytes = (phys_bytes)(data_clicks + gap_clicks + stack_clicks) << CLICK_SHIFT;
base = (phys_bytes) rmp->vm_arch.vm_seg[D].mem_phys << CLICK_SHIFT;
bss_offset = (data_bytes >> CLICK_SHIFT) << CLICK_SHIFT;
base += bss_offset;
bytes -= bss_offset;
if ((s=sys_memset(0, base, bytes)) != OK) {
vm_panic("new_mem can't zero", s);
}
}
/* Whether vm_pt is NULL or a new pagetable, tell kernel about it. */
if((s=pt_bind(&rmp->vm_pt, rmp)) != OK)
vm_panic("exec_newmem: pt_bind failed", s);
SANITYCHECK(SCL_FUNCTIONS);
return(OK);
}
/*===========================================================================*
* find_kernel_top *
*===========================================================================*/
PRIVATE u32_t find_kernel_top(void)
{
/* Find out where the kernel is, so we know where to start mapping
* user processes.
*/
u32_t kernel_top = 0;
#define MEMTOP(v, i) \
(vmproc[v].vm_arch.vm_seg[i].mem_phys + vmproc[v].vm_arch.vm_seg[i].mem_len)
vm_assert(vmproc[VMP_SYSTEM].vm_flags & VMF_INUSE);
kernel_top = MEMTOP(VMP_SYSTEM, T);
kernel_top = MAX(kernel_top, MEMTOP(VMP_SYSTEM, D));
kernel_top = MAX(kernel_top, MEMTOP(VMP_SYSTEM, S));
vm_assert(kernel_top);
return kernel_top;
}

118
servers/vm/exit.c Normal file
View File

@@ -0,0 +1,118 @@
#define _SYSTEM 1
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <errno.h>
#include <env.h>
#include "glo.h"
#include "proto.h"
#include "util.h"
#include "sanitycheck.h"
PUBLIC void free_proc(struct vmproc *vmp)
{
vmp->vm_flags &= ~VMF_HASPT;
pt_free(&vmp->vm_pt);
map_free_proc(vmp);
vmp->vm_regions = NULL;
#if VMSTATS
vmp->vm_bytecopies = 0;
#endif
}
PUBLIC void clear_proc(struct vmproc *vmp)
{
vmp->vm_regions = NULL;
vmp->vm_callback = NULL; /* No pending vfs callback. */
vmp->vm_flags = 0; /* Clear INUSE, so slot is free. */
vmp->vm_count = 0;
vmp->vm_heap = NULL;
#if VMSTATS
vmp->vm_bytecopies = 0;
#endif
}
/*===========================================================================*
* do_exit *
*===========================================================================*/
PUBLIC int do_exit(message *msg)
{
int proc;
struct vmproc *vmp;
SANITYCHECK(SCL_FUNCTIONS);
if(vm_isokendpt(msg->VME_ENDPOINT, &proc) != OK) {
printf("VM: bogus endpoint VM_EXIT %d\n", msg->VME_ENDPOINT);
return EINVAL;
}
vmp = &vmproc[proc];
if(!(vmp->vm_flags & VMF_EXITING)) {
printf("VM: unannounced VM_EXIT %d\n", msg->VME_ENDPOINT);
return EINVAL;
}
if(vmp->vm_flags & VMF_HAS_DMA) {
release_dma(vmp);
} else if(vmp->vm_flags & VMF_HASPT) {
/* Free pagetable and pages allocated by pt code. */
SANITYCHECK(SCL_DETAIL);
free_proc(vmp);
SANITYCHECK(SCL_DETAIL);
} else {
/* Free the data and stack segments. */
FREE_MEM(vmp->vm_arch.vm_seg[D].mem_phys,
vmp->vm_arch.vm_seg[S].mem_vir +
vmp->vm_arch.vm_seg[S].mem_len -
vmp->vm_arch.vm_seg[D].mem_vir);
if (find_share(vmp, vmp->vm_ino, vmp->vm_dev, vmp->vm_ctime) == NULL) {
/* No other process shares the text segment,
* so free it.
*/
FREE_MEM(vmp->vm_arch.vm_seg[T].mem_phys,
vmp->vm_arch.vm_seg[T].mem_len);
}
}
SANITYCHECK(SCL_DETAIL);
/* Reset process slot fields. */
clear_proc(vmp);
SANITYCHECK(SCL_FUNCTIONS);
return OK;
}
/*===========================================================================*
* do_willexit *
*===========================================================================*/
PUBLIC int do_willexit(message *msg)
{
int proc;
struct vmproc *vmp;
if(vm_isokendpt(msg->VMWE_ENDPOINT, &proc) != OK) {
printf("VM: bogus endpoint VM_EXITING %d\n",
msg->VMWE_ENDPOINT);
return EINVAL;
}
vmp = &vmproc[proc];
vmp->vm_flags |= VMF_EXITING;
return OK;
}

145
servers/vm/fork.c Normal file
View File

@@ -0,0 +1,145 @@
#define _SYSTEM 1
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <errno.h>
#include <env.h>
#include "glo.h"
#include "vm.h"
#include "proto.h"
#include "util.h"
#include "sanitycheck.h"
#include "region.h"
/*===========================================================================*
* do_fork *
*===========================================================================*/
PUBLIC int do_fork(message *msg)
{
int r, proc, s, childproc, fullvm;
struct vmproc *vmp, *vmc;
SANITYCHECK(SCL_FUNCTIONS);
if(vm_isokendpt(msg->VMF_ENDPOINT, &proc) != OK) {
printf("VM: bogus endpoint VM_FORK %d\n", msg->VMF_ENDPOINT);
SANITYCHECK(SCL_FUNCTIONS);
return EINVAL;
}
childproc = msg->VMF_SLOTNO;
if(childproc < 0 || childproc >= NR_PROCS) {
printf("VM: bogus slotno VM_FORK %d\n", msg->VMF_SLOTNO);
SANITYCHECK(SCL_FUNCTIONS);
return EINVAL;
}
vmp = &vmproc[proc]; /* parent */
vmc = &vmproc[childproc]; /* child */
if(vmp->vm_flags & VMF_HAS_DMA) {
printf("VM: %d has DMA memory and may not fork\n", msg->VMF_ENDPOINT);
return EINVAL;
}
fullvm = vmp->vm_flags & VMF_HASPT;
/* The child is basically a copy of the parent. */
*vmc = *vmp;
vmc->vm_regions = NULL;
vmc->vm_endpoint = NONE; /* In case someone tries to use it. */
#if VMSTATS
vmc->vm_bytecopies = 0;
#endif
if(fullvm) {
SANITYCHECK(SCL_DETAIL);
if(pt_new(&vmc->vm_pt) != OK) {
printf("VM: fork: pt_new failed\n");
return ENOMEM;
}
SANITYCHECK(SCL_DETAIL);
if(map_proc_copy(vmc, vmp) != OK) {
printf("VM: fork: map_proc_copy failed\n");
pt_free(&vmc->vm_pt);
return(ENOMEM);
}
if(vmp->vm_heap) {
vmc->vm_heap = map_region_lookup_tag(vmc, VRT_HEAP);
vm_assert(vmc->vm_heap);
}
SANITYCHECK(SCL_DETAIL);
} else {
phys_bytes prog_bytes, parent_abs, child_abs; /* Intel only */
phys_clicks prog_clicks, child_base;
/* Determine how much memory to allocate. Only the data and stack
* need to be copied, because the text segment is either shared or
* of zero length.
*/
prog_clicks = (phys_clicks) vmp->vm_arch.vm_seg[S].mem_len;
prog_clicks += (vmp->vm_arch.vm_seg[S].mem_vir - vmp->vm_arch.vm_seg[D].mem_vir);
prog_bytes = (phys_bytes) prog_clicks << CLICK_SHIFT;
if ( (child_base = ALLOC_MEM(prog_clicks, 0)) == NO_MEM) {
SANITYCHECK(SCL_FUNCTIONS);
return(ENOMEM);
}
/* Create a copy of the parent's core image for the child. */
child_abs = (phys_bytes) child_base << CLICK_SHIFT;
parent_abs = (phys_bytes) vmp->vm_arch.vm_seg[D].mem_phys << CLICK_SHIFT;
s = sys_abscopy(parent_abs, child_abs, prog_bytes);
if (s < 0) vm_panic("do_fork can't copy", s);
/* A separate I&D child keeps the parents text segment. The data and stack
* segments must refer to the new copy.
*/
if (!(vmc->vm_flags & VMF_SEPARATE))
vmc->vm_arch.vm_seg[T].mem_phys = child_base;
vmc->vm_arch.vm_seg[D].mem_phys = child_base;
vmc->vm_arch.vm_seg[S].mem_phys = vmc->vm_arch.vm_seg[D].mem_phys +
(vmp->vm_arch.vm_seg[S].mem_vir - vmp->vm_arch.vm_seg[D].mem_vir);
}
/* Only inherit these flags. */
vmc->vm_flags &= (VMF_INUSE|VMF_SEPARATE|VMF_HASPT);
/* Tell kernel about the (now successful) FORK. */
if((r=sys_fork(vmp->vm_endpoint, childproc,
&vmc->vm_endpoint, vmc->vm_arch.vm_seg,
fullvm ? PFF_VMINHIBIT : 0)) != OK) {
vm_panic("do_fork can't sys_fork", r);
}
if(fullvm) {
if((r=pt_bind(&vmc->vm_pt, vmc)) != OK)
vm_panic("fork can't pt_bind", r);
}
/* Inform caller of new child endpoint. */
msg->VMF_CHILD_ENDPOINT = vmc->vm_endpoint;
SANITYCHECK(SCL_FUNCTIONS);
return OK;
}

29
servers/vm/glo.h Normal file
View File

@@ -0,0 +1,29 @@
#include <minix/sys_config.h>
#include <sys/stat.h>
#include <a.out.h>
#include <tools.h>
#include "vm.h"
#include "vmproc.h"
#if _MAIN
#undef EXTERN
#define EXTERN
#endif
EXTERN struct vmproc vmproc[_NR_PROCS+1];
#if SANITYCHECKS
u32_t data1[200];
#define CHECKADDR 0
EXTERN long vm_sanitychecklevel;
#endif
int verbosealloc;
#define VMP_SYSTEM _NR_PROCS
/* vm operation mode state and values */
EXTERN long vm_paged;

19
servers/vm/i386/Makefile Normal file
View File

@@ -0,0 +1,19 @@
include /etc/make.conf
OBJ = vm.o pagetable.o arch_pagefaults.o util.o
CPPFLAGS=-I../../../kernel/arch/$(ARCH)/include -I.
CFLAGS = $(CPROFILE) $(CPPFLAGS)
all: $(OBJ)
clean:
rm -f $(OBJ)
depend:
mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
# Include generated dependencies.
include .depend

View File

@@ -0,0 +1,38 @@
#define _SYSTEM 1
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#include <fcntl.h>
#include "../glo.h"
#include "../proto.h"
#include "../util.h"
/*===========================================================================*
* arch_handle_pagefaults *
*===========================================================================*/
PUBLIC int arch_get_pagefault(who, addr, err)
endpoint_t *who;
vir_bytes *addr;
u32_t *err;
{
return sys_vmctl_get_pagefault_i386(who, addr, err);
}

View File

@@ -0,0 +1,15 @@
#include <archtypes.h>
struct vm_arch {
struct mem_map vm_seg[NR_LOCAL_SEGS]; /* text, data, stack */
/* vm_data_top points to top of data address space, as visible
* from user-space, in bytes.
* for segments processes this is the same
* as the top of vm_seg[S] segment. for paged processes this
* can be much higher (so more memory is available above the
* stack).
*/
u32_t vm_data_top; /* virtual process space in bytes */
};

25
servers/vm/i386/memory.h Normal file
View File

@@ -0,0 +1,25 @@
#include <sys/vm_i386.h>
/* As visible from the user space process, where is the top of the
* stack (first non-stack byte), when in paged mode?
*/
#define VM_STACKTOP 0x80000000
/* And what is the highest addressable piece of memory, when in paged
* mode? Some data for kernel and stack are subtracted from this, the
* final results stored in bytes in arch.vm_data_top.
*/
#define VM_DATATOP 0xFFFFF000
#define SLAB_PAGESIZE I386_PAGE_SIZE
#define VM_PAGE_SIZE I386_PAGE_SIZE
#define CLICKSPERPAGE (I386_PAGE_SIZE/CLICK_SIZE)
/* Where is the kernel? */
#define KERNEL_TEXT CLICK2ABS(vmproc[VMP_SYSTEM].vm_arch.vm_seg[T].mem_phys)
#define KERNEL_TEXT_LEN CLICK2ABS(vmproc[VMP_SYSTEM].vm_arch.vm_seg[T].mem_len)
#define KERNEL_DATA CLICK2ABS(vmproc[VMP_SYSTEM].vm_arch.vm_seg[D].mem_phys)
#define KERNEL_DATA_LEN CLICK2ABS(vmproc[VMP_SYSTEM].vm_arch.vm_seg[D].mem_len \
+ vmproc[VMP_SYSTEM].vm_arch.vm_seg[S].mem_len)

View File

@@ -0,0 +1,13 @@
#ifndef _PAGEFAULTS_H
#define _PAGEFAULTS_H 1
#include <sys/vm_i386.h>
#define PFERR_NOPAGE(e) (!((e) & I386_VM_PFE_P))
#define PFERR_PROT(e) (((e) & I386_VM_PFE_P))
#define PFERR_WRITE(e) ((e) & I386_VM_PFE_W)
#define PFERR_READ(e) (!((e) & I386_VM_PFE_W))
#endif

944
servers/vm/i386/pagetable.c Normal file
View File

@@ -0,0 +1,944 @@
#define _SYSTEM 1
#define VERBOSE 0
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include "../proto.h"
#include "../glo.h"
#include "../util.h"
#include "../vm.h"
#include "../sanitycheck.h"
#include "memory.h"
/* Location in our virtual address space where we can map in
* any physical page we want.
*/
static unsigned char *varmap = NULL; /* Our address space. */
static u32_t varmap_loc; /* Our page table. */
/* Our process table entry. */
struct vmproc *vmp = &vmproc[VM_PROC_NR];
/* Spare memory, ready to go after initialization, to avoid a
* circular dependency on allocating memory and writing it into VM's
* page table.
*/
#define SPAREPAGES 3
static struct {
void *page;
u32_t phys;
} sparepages[SPAREPAGES];
/* Clicks must be pages, as
* - they must be page aligned to map them
* - they must be a multiple of the page size
* - it's inconvenient to have them bigger than pages, because we often want
* just one page
* May as well require them to be equal then.
*/
#if CLICK_SIZE != I386_PAGE_SIZE
#error CLICK_SIZE must be page size.
#endif
/* Bytes of virtual address space one pde controls. */
#define BYTESPERPDE (I386_VM_PT_ENTRIES * I386_PAGE_SIZE)
/* Nevertheless, introduce these macros to make the code readable. */
#define CLICK2PAGE(c) ((c) / CLICKSPERPAGE)
#if SANITYCHECKS
#define PT_SANE(p) { pt_sanitycheck((p), __FILE__, __LINE__); SANITYCHECK(SCL_DETAIL); }
/*===========================================================================*
* pt_sanitycheck *
*===========================================================================*/
PUBLIC void pt_sanitycheck(pt_t *pt, char *file, int line)
{
/* Basic pt sanity check. */
int i;
MYASSERT(pt);
MYASSERT(pt->pt_dir);
MYASSERT(pt->pt_dir_phys);
for(i = 0; i < I386_VM_DIR_ENTRIES; i++) {
if(pt->pt_pt[i]) {
MYASSERT(pt->pt_dir[i] & I386_VM_PRESENT);
} else {
MYASSERT(!(pt->pt_dir[i] & I386_VM_PRESENT));
}
}
}
#else
#define PT_SANE(p)
#endif
/*===========================================================================*
* aalloc *
*===========================================================================*/
PRIVATE void *aalloc(size_t bytes)
{
/* Page-aligned malloc(). only used if vm_allocpages can't be used. */
u32_t b;
b = (u32_t) malloc(I386_PAGE_SIZE + bytes);
if(!b) vm_panic("aalloc: out of memory", bytes);
b += I386_PAGE_SIZE - (b % I386_PAGE_SIZE);
return (void *) b;
}
/*===========================================================================*
* findhole *
*===========================================================================*/
PRIVATE u32_t findhole(pt_t *pt, u32_t virbytes, u32_t vmin, u32_t vmax)
{
/* Find a space in the virtual address space of pageteble 'pt',
* between page-aligned BYTE offsets vmin and vmax, to fit
* 'virbytes' in. Return byte offset.
*
* As a simple way to speed up the search a bit, we start searching
* after the location we found the previous hole, if that's in range.
* If that's not in range (or if that doesn't work), search the entire
* range (as well). try_restart controls whether we have to restart
* the search if it fails. (Just once of course.)
*/
u32_t freeneeded, freefound = 0, freestart = 0, curv;
int pde = 0, try_restart;
/* Input sanity check. */
vm_assert(vmin + virbytes >= vmin);
vm_assert(vmax >= vmin + virbytes);
vm_assert((virbytes % I386_PAGE_SIZE) == 0);
vm_assert((vmin % I386_PAGE_SIZE) == 0);
vm_assert((vmax % I386_PAGE_SIZE) == 0);
/* How many pages do we need? */
freeneeded = virbytes / I386_PAGE_SIZE;
if(pt->pt_virtop >= vmin && pt->pt_virtop <= vmax - virbytes) {
curv = pt->pt_virtop;
try_restart = 1;
} else {
curv = vmin;
try_restart = 0;
}
/* Start looking for a consecutive block of free pages
* starting at vmin.
*/
for(freestart = curv; curv < vmax; ) {
int pte;
pde = I386_VM_PDE(curv);
pte = I386_VM_PTE(curv);
if(!(pt->pt_dir[pde] & I386_VM_PRESENT)) {
int rempte;
rempte = I386_VM_PT_ENTRIES - pte;
freefound += rempte;
curv += rempte * I386_PAGE_SIZE;
} else {
if(pt->pt_pt[pde][pte] & I386_VM_PRESENT) {
freefound = 0;
freestart = curv + I386_PAGE_SIZE;
} else {
freefound++;
}
curv+=I386_PAGE_SIZE;
}
if(freefound >= freeneeded) {
u32_t v;
v = freestart;
vm_assert(v != NO_MEM);
vm_assert(v >= vmin);
vm_assert(v < vmax);
/* Next time, start looking here. */
pt->pt_virtop = v + virbytes;
return v;
}
if(curv >= vmax && try_restart) {
curv = vmin;
try_restart = 0;
}
}
printf("VM: out of virtual address space in a process\n");
return NO_MEM;
}
/*===========================================================================*
* vm_freepages *
*===========================================================================*/
PRIVATE void vm_freepages(vir_bytes vir, vir_bytes phys, int pages, int reason)
{
vm_assert(reason >= 0 && reason < VMP_CATEGORIES);
if(vir >= vmp->vm_stacktop) {
vm_assert(!(vir % I386_PAGE_SIZE));
vm_assert(!(phys % I386_PAGE_SIZE));
FREE_MEM(ABS2CLICK(phys), pages);
if(pt_writemap(&vmp->vm_pt,
vir + CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys),
0, pages*I386_PAGE_SIZE, 0, WMF_OVERWRITE) != OK)
vm_panic("vm_freepages: pt_writemap failed",
NO_NUM);
} else {
printf("VM: vm_freepages not freeing VM heap pages (%d)\n",
pages);
}
}
/*===========================================================================*
* vm_getsparepage *
*===========================================================================*/
PRIVATE void *vm_getsparepage(u32_t *phys)
{
int s;
for(s = 0; s < SPAREPAGES; s++) {
if(sparepages[s].page) {
void *sp;
sp = sparepages[s].page;
*phys = sparepages[s].phys;
sparepages[s].page = NULL;
return sp;
}
}
vm_panic("VM: out of spare pages", NO_NUM);
return NULL;
}
/*===========================================================================*
* vm_checkspares *
*===========================================================================*/
PRIVATE void *vm_checkspares(void)
{
int s, n = 0;
static int total = 0, worst = 0;
for(s = 0; s < SPAREPAGES; s++)
if(!sparepages[s].page) {
n++;
sparepages[s].page = vm_allocpages(&sparepages[s].phys, 1,
VMP_SPARE);
}
if(worst < n) worst = n;
total += n;
#if 0
if(n > 0)
printf("VM: made %d spares, total %d, worst %d\n", n, total, worst);
#endif
return NULL;
}
/*===========================================================================*
* vm_allocpages *
*===========================================================================*/
PUBLIC void *vm_allocpages(phys_bytes *phys, int pages, int reason)
{
/* Allocate a number of pages for use by VM itself. */
phys_bytes newpage;
vir_bytes loc;
pt_t *pt;
int r;
vir_bytes bytes = pages * I386_PAGE_SIZE;
static int level = 0;
#define MAXDEPTH 10
static int reasons[MAXDEPTH];
pt = &vmp->vm_pt;
vm_assert(reason >= 0 && reason < VMP_CATEGORIES);
vm_assert(pages > 0);
reasons[level++] = reason;
vm_assert(level >= 1);
vm_assert(level <= 2);
if(level > 1 || !(vmp->vm_flags & VMF_HASPT)) {
int r;
void *s;
vm_assert(pages == 1);
s=vm_getsparepage(phys);
level--;
return s;
}
/* VM does have a pagetable, so get a page and map it in there.
* Where in our virtual address space can we put it?
*/
loc = findhole(pt, I386_PAGE_SIZE * pages,
CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys) + vmp->vm_stacktop,
vmp->vm_arch.vm_data_top);
if(loc == NO_MEM) {
level--;
return NULL;
}
/* Allocate 'pages' pages of memory for use by VM. As VM
* is trusted, we don't have to pre-clear it.
*/
if((newpage = ALLOC_MEM(CLICKSPERPAGE * pages, 0)) == NO_MEM) {
level--;
return NULL;
}
*phys = CLICK2ABS(newpage);
/* Map this page into our address space. */
if((r=pt_writemap(pt, loc, *phys, bytes,
I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE, 0)) != OK) {
FREE_MEM(newpage, CLICKSPERPAGE * pages / I386_PAGE_SIZE);
return NULL;
}
level--;
/* Return user-space-ready pointer to it. */
return (void *) (loc - CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys));
}
/*===========================================================================*
* pt_ptalloc *
*===========================================================================*/
PRIVATE int pt_ptalloc(pt_t *pt, int pde, u32_t flags)
{
/* Allocate a page table and write its address into the page directory. */
int i;
u32_t pt_phys;
/* Argument must make sense. */
vm_assert(pde >= 0 && pde < I386_VM_DIR_ENTRIES);
vm_assert(!(flags & ~(PTF_ALLFLAGS | PTF_MAPALLOC)));
/* We don't expect to overwrite page directory entry, nor
* storage for the page table.
*/
vm_assert(!(pt->pt_dir[pde] & I386_VM_PRESENT));
vm_assert(!pt->pt_pt[pde]);
PT_SANE(pt);
/* Get storage for the page table. */
if(!(pt->pt_pt[pde] = vm_allocpages(&pt_phys, 1, VMP_PAGETABLE)))
return ENOMEM;
for(i = 0; i < I386_VM_PT_ENTRIES; i++)
pt->pt_pt[pde][i] = 0; /* Empty entry. */
/* Make page directory entry.
* The PDE is always 'present,' 'writable,' and 'user accessible,'
* relying on the PTE for protection.
*/
pt->pt_dir[pde] = (pt_phys & I386_VM_ADDR_MASK) | flags
| I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE;
vm_assert(flags & I386_VM_PRESENT);
PT_SANE(pt);
return OK;
}
/*===========================================================================*
* pt_writemap *
*===========================================================================*/
PUBLIC int pt_writemap(pt_t *pt, vir_bytes v, phys_bytes physaddr,
size_t bytes, u32_t flags, u32_t writemapflags)
{
/* Write mapping into page table. Allocate a new page table if necessary. */
/* Page directory and table entries for this virtual address. */
int p, pages, pde;
SANITYCHECK(SCL_FUNCTIONS);
vm_assert(!(bytes % I386_PAGE_SIZE));
vm_assert(!(flags & ~(PTF_ALLFLAGS | PTF_MAPALLOC)));
pages = bytes / I386_PAGE_SIZE;
#if SANITYCHECKS
if(physaddr && !(flags & I386_VM_PRESENT)) {
vm_panic("pt_writemap: writing dir with !P\n", NO_NUM);
}
if(!physaddr && flags) {
vm_panic("pt_writemap: writing 0 with flags\n", NO_NUM);
}
#endif
PT_SANE(pt);
/* First make sure all the necessary page tables are allocated,
* before we start writing in any of them, because it's a pain
* to undo our work properly. Walk the range in page-directory-entry
* sized leaps.
*/
for(pde = I386_VM_PDE(v); pde <= I386_VM_PDE(v + I386_PAGE_SIZE * pages); pde++) {
vm_assert(pde >= 0 && pde < I386_VM_DIR_ENTRIES);
if(!(pt->pt_dir[pde] & I386_VM_PRESENT)) {
int r;
vm_assert(!pt->pt_dir[pde]);
if((r=pt_ptalloc(pt, pde, flags)) != OK) {
/* Couldn't do (complete) mapping.
* Don't bother freeing any previously
* allocated page tables, they're
* still writable, don't point to nonsense,
* and pt_ptalloc leaves the directory
* and other data in a consistent state.
*/
return r;
}
}
vm_assert(pt->pt_dir[pde] & I386_VM_PRESENT);
}
PT_SANE(pt);
/* Now write in them. */
for(p = 0; p < pages; p++) {
int pde = I386_VM_PDE(v);
int pte = I386_VM_PTE(v);
PT_SANE(pt);
vm_assert(!(v % I386_PAGE_SIZE));
vm_assert(pte >= 0 && pte < I386_VM_PT_ENTRIES);
vm_assert(pde >= 0 && pde < I386_VM_DIR_ENTRIES);
/* Page table has to be there. */
vm_assert(pt->pt_dir[pde] & I386_VM_PRESENT);
/* Make sure page directory entry for this page table
* is marked present and page table entry is available.
*/
vm_assert((pt->pt_dir[pde] & I386_VM_PRESENT) && pt->pt_pt[pde]);
PT_SANE(pt);
#if SANITYCHECKS
/* We don't expect to overwrite a page. */
if(!(writemapflags & WMF_OVERWRITE))
vm_assert(!(pt->pt_pt[pde][pte] & I386_VM_PRESENT));
#endif
/* Write pagetable entry. */
pt->pt_pt[pde][pte] = (physaddr & I386_VM_ADDR_MASK) | flags;
physaddr += I386_PAGE_SIZE;
v += I386_PAGE_SIZE;
PT_SANE(pt);
}
SANITYCHECK(SCL_FUNCTIONS);
PT_SANE(pt);
return OK;
}
/*===========================================================================*
* pt_new *
*===========================================================================*/
PUBLIC int pt_new(pt_t *pt)
{
/* Allocate a pagetable root. On i386, allocate a page-aligned page directory
* and set them to 0 (indicating no page tables are allocated). Lookup
* its physical address as we'll need that in the future. Verify it's
* page-aligned.
*/
int i;
if(!(pt->pt_dir = vm_allocpages(&pt->pt_dir_phys, 1, VMP_PAGEDIR))) {
return ENOMEM;
}
for(i = 0; i < I386_VM_DIR_ENTRIES; i++) {
pt->pt_dir[i] = 0; /* invalid entry (I386_VM_PRESENT bit = 0) */
pt->pt_pt[i] = NULL;
}
/* Where to start looking for free virtual address space? */
pt->pt_virtop = VM_STACKTOP +
CLICK2ABS(vmproc[VMP_SYSTEM].vm_arch.vm_seg[D].mem_phys);
return OK;
}
/*===========================================================================*
* pt_allocmap *
*===========================================================================*/
PUBLIC int pt_allocmap(pt_t *pt, vir_bytes v_min, vir_bytes v_max,
size_t bytes, u32_t pageflags, u32_t memflags, vir_bytes *v_final)
{
/* Allocate new memory, and map it into the page table. */
u32_t newpage;
u32_t v;
int r;
/* Input sanity check. */
PT_SANE(pt);
vm_assert(!(pageflags & ~PTF_ALLFLAGS));
/* Valid no-op. */
if(bytes == 0) return OK;
/* Round no. of bytes up to a page. */
if(bytes % I386_PAGE_SIZE) {
bytes += I386_PAGE_SIZE - (bytes % I386_PAGE_SIZE);
}
/* Special case; if v_max is 0, the request is to map the memory
* into v_min at exactly that location. We raise v_max as necessary,
* so the check to see if the virtual space is free does happen.
*/
if(v_max == 0) {
v_max = v_min + bytes;
/* Sanity check. */
if(v_max < v_min) {
printf("pt_allocmap: v_min 0x%lx and bytes 0x%lx\n",
v_min, bytes);
return ENOMEM;
}
}
/* Basic sanity check. */
if(v_max < v_min) {
printf("pt_allocmap: v_min 0x%lx, v_max 0x%lx\n", v_min, v_max);
return ENOMEM;
}
/* v_max itself may not be used. Bytes may be 0. */
if(v_max < v_min + bytes) {
printf("pt_allocmap: v_min 0x%lx, bytes 0x%lx, v_max 0x%lx\n",
v_min, bytes, v_max);
return ENOMEM;
}
/* Find where to fit this into the virtual address space. */
v = findhole(pt, bytes, v_min, v_max);
if(v == NO_MEM) {
printf("pt_allocmap: no hole found to map 0x%lx bytes into\n",
bytes);
return ENOSPC;
}
vm_assert(!(v % I386_PAGE_SIZE));
if(v_final) *v_final = v;
/* Memory is currently always allocated contiguously physically,
* but if that were to change, note the setting of
* PAF_CONTIG in memflags.
*/
newpage = ALLOC_MEM(CLICKSPERPAGE * bytes / I386_PAGE_SIZE, memflags);
if(newpage == NO_MEM) {
printf("pt_allocmap: out of memory\n");
return ENOMEM;
}
/* Write into the page table. */
if((r=pt_writemap(pt, v, CLICK2ABS(newpage), bytes,
pageflags | PTF_MAPALLOC, 0)) != OK) {
FREE_MEM(newpage, CLICKSPERPAGE * bytes / I386_PAGE_SIZE);
return r;
}
/* Sanity check result. */
PT_SANE(pt);
return OK;
}
/*===========================================================================*
* raw_readmap *
*===========================================================================*/
PRIVATE int raw_readmap(phys_bytes root, u32_t v, u32_t *phys, u32_t *flags)
{
u32_t dir[I386_VM_DIR_ENTRIES];
u32_t tab[I386_VM_PT_ENTRIES];
int pde, pte, r;
/* Sanity check. */
vm_assert((root % I386_PAGE_SIZE) == 0);
vm_assert((v % I386_PAGE_SIZE) == 0);
/* Get entry in page directory. */
pde = I386_VM_PDE(v);
if((r=sys_physcopy(SYSTEM, PHYS_SEG, root,
SELF, VM_D, (phys_bytes) dir, sizeof(dir))) != OK) {
printf("VM: raw_readmap: sys_physcopy failed (dir) (%d)\n", r);
return EFAULT;
}
if(!(dir[pde] & I386_VM_PRESENT)) {
printf("raw_readmap: 0x%lx: pde %d not present: 0x%lx\n",
v, pde, dir[pde]);
return EFAULT;
}
/* Get entry in page table. */
if((r=sys_physcopy(SYSTEM, PHYS_SEG, I386_VM_PFA(dir[pde]),
SELF, VM_D, (vir_bytes) tab, sizeof(tab))) != OK) {
printf("VM: raw_readmap: sys_physcopy failed (tab) (r)\n");
return EFAULT;
}
pte = I386_VM_PTE(v);
if(!(tab[pte] & I386_VM_PRESENT)) {
printf("raw_readmap: 0x%lx: pde %d not present: 0x%lx\n",
v, pte, tab[pte]);
return EFAULT;
}
/* Get address and flags. */
*phys = I386_VM_PFA(tab[pte]);
*flags = tab[pte] & PTF_ALLFLAGS;
return OK;
}
/*===========================================================================*
* pt_init *
*===========================================================================*/
PUBLIC void pt_init(void)
{
/* By default, the kernel gives us a data segment with pre-allocated
* memory that then can't grow. We want to be able to allocate memory
* dynamically, however. So here we copy the part of the page table
* that's ours, so we get a private page table. Then we increase the
* hardware segment size so we can allocate memory above our stack.
*/
u32_t my_cr3;
pt_t *newpt;
int s, r;
vir_bytes v;
phys_bytes lo, hi;
vir_bytes extra_clicks;
/* Retrieve current CR3 - shared page table. */
if((r=sys_vmctl_get_cr3_i386(SELF, &my_cr3)) != OK)
vm_panic("pt_init: sys_vmctl_get_cr3_i386 failed", r);
/* Shorthand. */
newpt = &vmp->vm_pt;
/* Get ourselves a spare page. */
for(s = 0; s < SPAREPAGES; s++) {
if(!(sparepages[s].page = aalloc(I386_PAGE_SIZE)))
vm_panic("pt_init: aalloc for spare failed", NO_NUM);
if((r=sys_umap(SELF, VM_D, (vir_bytes) sparepages[s].page,
I386_PAGE_SIZE, &sparepages[s].phys)) != OK)
vm_panic("pt_init: sys_umap failed", r);
}
/* Make new page table for ourselves, partly copied
* from the current one.
*/
if(pt_new(newpt) != OK)
vm_panic("pt_init: pt_new failed", NO_NUM);
/* Initial (current) range of our virtual address space. */
lo = CLICK2ABS(vmp->vm_arch.vm_seg[T].mem_phys);
hi = CLICK2ABS(vmp->vm_arch.vm_seg[S].mem_phys +
vmp->vm_arch.vm_seg[S].mem_len);
/* Copy the mappings from the shared page table to our private one. */
for(v = lo; v < hi; v += I386_PAGE_SIZE) {
phys_bytes addr;
u32_t flags;
if(raw_readmap(my_cr3, v, &addr, &flags) != OK)
vm_panic("pt_init: raw_readmap failed", NO_NUM);
if(pt_writemap(newpt, v, addr, I386_PAGE_SIZE, flags, 0) != OK)
vm_panic("pt_init: pt_writemap failed", NO_NUM);
}
/* Map in kernel. */
if(pt_mapkernel(newpt) != OK)
vm_panic("pt_init: pt_mapkernel failed", NO_NUM);
/* Give our process the new, copied, private page table. */
pt_bind(newpt, vmp);
/* Increase our hardware data segment to create virtual address
* space above our stack. We want to increase it to VM_DATATOP,
* like regular processes have.
*/
extra_clicks = ABS2CLICK(VM_DATATOP - hi);
vmp->vm_arch.vm_seg[S].mem_len += extra_clicks;
/* We pretend to the kernel we have a huge stack segment to
* increase our data segment.
*/
vmp->vm_arch.vm_data_top =
(vmp->vm_arch.vm_seg[S].mem_vir +
vmp->vm_arch.vm_seg[S].mem_len) << CLICK_SHIFT;
if((s=sys_newmap(VM_PROC_NR, vmp->vm_arch.vm_seg)) != OK)
vm_panic("VM: pt_init: sys_newmap failed", s);
/* Back to reality - this is where the stack actually is. */
vmp->vm_arch.vm_seg[S].mem_len -= extra_clicks;
/* Where our free virtual address space starts.
* This is only a hint to the VM system.
*/
newpt->pt_virtop = (vmp->vm_arch.vm_seg[S].mem_vir +
vmp->vm_arch.vm_seg[S].mem_len) << CLICK_SHIFT;
/* Let other functions know VM now has a private page table. */
vmp->vm_flags |= VMF_HASPT;
/* Reserve a page in our virtual address space that we
* can use to map in arbitrary physical pages.
*/
varmap_loc = findhole(newpt, I386_PAGE_SIZE,
CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys) + vmp->vm_stacktop,
vmp->vm_arch.vm_data_top);
if(varmap_loc == NO_MEM) {
vm_panic("no virt addr for vm mappings", NO_NUM);
}
varmap = (unsigned char *) (varmap_loc -
CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys));
/* All OK. */
return;
}
/*===========================================================================*
* pt_bind *
*===========================================================================*/
PUBLIC int pt_bind(pt_t *pt, struct vmproc *who)
{
/* Basic sanity checks. */
vm_assert(who);
vm_assert(who->vm_flags & VMF_INUSE);
if(pt) PT_SANE(pt);
/* Tell kernel about new page table root. */
return sys_vmctl(who->vm_endpoint, VMCTL_I386_SETCR3,
pt ? pt->pt_dir_phys : 0);
}
/*===========================================================================*
* pt_free *
*===========================================================================*/
PUBLIC void pt_free(pt_t *pt)
{
/* Free memory associated with this pagetable. */
int i;
PT_SANE(pt);
for(i = 0; i < I386_VM_DIR_ENTRIES; i++) {
int p;
if(pt->pt_pt[i]) {
for(p = 0; p < I386_VM_PT_ENTRIES; p++) {
if((pt->pt_pt[i][p] & (PTF_MAPALLOC | I386_VM_PRESENT))
== (PTF_MAPALLOC | I386_VM_PRESENT)) {
u32_t pa = I386_VM_PFA(pt->pt_pt[i][p]);
FREE_MEM(ABS2CLICK(pa), CLICKSPERPAGE);
}
}
vm_freepages((vir_bytes) pt->pt_pt[i],
I386_VM_PFA(pt->pt_dir[i]), 1, VMP_PAGETABLE);
}
}
vm_freepages((vir_bytes) pt->pt_dir, pt->pt_dir_phys, 1, VMP_PAGEDIR);
return;
}
/*===========================================================================*
* pt_mapkernel *
*===========================================================================*/
PUBLIC int pt_mapkernel(pt_t *pt)
{
int r;
/* Any i386 page table needs to map in the kernel address space. */
vm_assert(vmproc[VMP_SYSTEM].vm_flags & VMF_INUSE);
/* Map in text. flags: don't write, supervisor only */
if((r=pt_writemap(pt, KERNEL_TEXT, KERNEL_TEXT, KERNEL_TEXT_LEN,
I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE, 0)) != OK)
return r;
/* Map in data. flags: read-write, supervisor only */
if((r=pt_writemap(pt, KERNEL_DATA, KERNEL_DATA, KERNEL_DATA_LEN,
I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE, 0)) != OK)
return r;
return OK;
}
/*===========================================================================*
* pt_freerange *
*===========================================================================*/
PUBLIC void pt_freerange(pt_t *pt, vir_bytes low, vir_bytes high)
{
/* Free memory allocated by pagetable functions in this range. */
int pde;
u32_t v;
PT_SANE(pt);
for(v = low; v < high; v += I386_PAGE_SIZE) {
int pte;
pde = I386_VM_PDE(v);
pte = I386_VM_PTE(v);
if(!(pt->pt_dir[pde] & I386_VM_PRESENT))
continue;
if((pt->pt_pt[pde][pte] & (PTF_MAPALLOC | I386_VM_PRESENT))
== (PTF_MAPALLOC | I386_VM_PRESENT)) {
u32_t pa = I386_VM_PFA(pt->pt_pt[pde][pte]);
FREE_MEM(ABS2CLICK(pa), CLICKSPERPAGE);
pt->pt_pt[pde][pte] = 0;
}
}
PT_SANE(pt);
return;
}
/*===========================================================================*
* pt_cycle *
*===========================================================================*/
PUBLIC void pt_cycle(void)
{
vm_checkspares();
}
/*===========================================================================*
* pt_copy *
*===========================================================================*/
PUBLIC int pt_copy(pt_t *src, pt_t *dst)
{
int i, r;
SANITYCHECK(SCL_FUNCTIONS);
PT_SANE(src);
if((r=pt_new(dst)) != OK)
return r;
for(i = 0; i < I386_VM_DIR_ENTRIES; i++) {
int p;
if(!(src->pt_dir[i] & I386_VM_PRESENT))
continue;
for(p = 0; p < I386_VM_PT_ENTRIES; p++) {
u32_t v = i * I386_VM_PT_ENTRIES * I386_PAGE_SIZE +
p * I386_PAGE_SIZE;
u32_t pa1, pa2, flags;
if(!(src->pt_pt[i][p] & I386_VM_PRESENT))
continue;
#if 0
if((dst->pt_pt[i] &&
(dst->pt_pt[i][p] & I386_VM_PRESENT)))
continue;
#endif
flags = src->pt_pt[i][p] & (PTF_WRITE | PTF_USER);
flags |= I386_VM_PRESENT;
pa1 = I386_VM_PFA(src->pt_pt[i][p]);
if(PTF_MAPALLOC & src->pt_pt[i][p]) {
PT_SANE(dst);
if(pt_allocmap(dst, v, 0,
I386_PAGE_SIZE, flags, 0, NULL) != OK) {
pt_free(dst);
return ENOMEM;
}
pa2 = I386_VM_PFA(dst->pt_pt[i][p]);
sys_abscopy(pa1, pa2, I386_PAGE_SIZE);
} else {
PT_SANE(dst);
if(pt_writemap(dst, v, pa1, I386_PAGE_SIZE, flags, 0) != OK) {
pt_free(dst);
return ENOMEM;
}
}
}
}
PT_SANE(src);
PT_SANE(dst);
SANITYCHECK(SCL_FUNCTIONS);
return OK;
}
#define PHYS_MAP(a, o) \
{ int r; \
vm_assert(varmap); \
(o) = (a) % I386_PAGE_SIZE; \
r = pt_writemap(&vmp->vm_pt, varmap_loc, (a) - (o), I386_PAGE_SIZE, \
I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE, 0); \
if(r != OK) \
vm_panic("PHYS_MAP: pt_writemap failed", NO_NUM); \
/* pt_bind() flushes TLB. */ \
pt_bind(&vmp->vm_pt, vmp); \
}
#define PHYSMAGIC 0x7b9a0590
#define PHYS_UNMAP if(OK != pt_writemap(&vmp->vm_pt, varmap_loc, 0, \
I386_PAGE_SIZE, 0, WMF_OVERWRITE)) { \
vm_panic("PHYS_UNMAP: pt_writemap failed", NO_NUM); }
#define PHYS_VAL(o) (* (phys_bytes *) (varmap + (o)))
/*===========================================================================*
* phys_writeaddr *
*===========================================================================*/
PUBLIC void phys_writeaddr(phys_bytes addr, phys_bytes v1, phys_bytes v2)
{
phys_bytes offset;
SANITYCHECK(SCL_DETAIL);
PHYS_MAP(addr, offset);
PHYS_VAL(offset) = v1;
PHYS_VAL(offset + sizeof(phys_bytes)) = v2;
#if SANITYCHECKS
PHYS_VAL(offset + 2*sizeof(phys_bytes)) = PHYSMAGIC;
#endif
PHYS_UNMAP;
SANITYCHECK(SCL_DETAIL);
}
/*===========================================================================*
* phys_readaddr *
*===========================================================================*/
PUBLIC void phys_readaddr(phys_bytes addr, phys_bytes *v1, phys_bytes *v2)
{
phys_bytes offset;
SANITYCHECK(SCL_DETAIL);
PHYS_MAP(addr, offset);
*v1 = PHYS_VAL(offset);
*v2 = PHYS_VAL(offset + sizeof(phys_bytes));
#if SANITYCHECKS
vm_assert(PHYS_VAL(offset + 2*sizeof(phys_bytes)) == PHYSMAGIC);
#endif
PHYS_UNMAP;
SANITYCHECK(SCL_DETAIL);
}

View File

@@ -0,0 +1,37 @@
#ifndef _PAGETABLE_H
#define _PAGETABLE_H 1
#include <stdint.h>
#include <sys/vm_i386.h>
/* An i386 pagetable. */
typedef struct {
/* Directory entries in VM addr space - root of page table. */
u32_t *pt_dir; /* page aligned (I386_VM_DIR_ENTRIES) */
u32_t pt_dir_phys; /* physical address of pt_dir */
/* Pointers to page tables in VM address space. */
u32_t *pt_pt[I386_VM_DIR_ENTRIES];
/* When looking for a hole in virtual address space, start
* looking here. This is in linear addresses, i.e.,
* not as the process sees it but the position in the page
* page table. This is just a hint.
*/
u32_t pt_virtop;
} pt_t;
/* Mapping flags. */
#define PTF_WRITE I386_VM_WRITE
#define PTF_PRESENT I386_VM_PRESENT
#define PTF_USER I386_VM_USER
#define PTF_MAPALLOC I386_VM_PTAVAIL1 /* Page allocated by pt code. */
/* For arch-specific PT routines to check if no bits outside
* the regular flags are set.
*/
#define PTF_ALLFLAGS (PTF_WRITE|PTF_PRESENT|PTF_USER)
#endif

23
servers/vm/i386/util.s Normal file
View File

@@ -0,0 +1,23 @@
.sect .text; .sect .rom; .sect .data; .sect .bss
.define _i386_invlpg
.sect .text
!*===========================================================================*
!* i386_invlpg *
!*===========================================================================*
! PUBLIC void i386_invlpg(u32_t addr)
! Tell the processor to invalidate a tlb entry at virtual address addr.
_i386_invlpg:
push ebp
mov ebp, esp
push eax
mov eax, 8(ebp)
invlpg eax
pop eax
pop ebp
ret

115
servers/vm/i386/vm.c Normal file
View File

@@ -0,0 +1,115 @@
#define _SYSTEM 1
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <sys/mman.h>
#include <errno.h>
#include <env.h>
#include "../proto.h"
#include "../vm.h"
#include "../util.h"
#include "memory.h"
#define PAGE_SIZE 4096
#define PAGE_DIR_SIZE (1024*PAGE_SIZE)
#define PAGE_TABLE_COVER (1024*PAGE_SIZE)
/*=========================================================================*
* arch_init_vm *
*=========================================================================*/
PUBLIC void arch_init_vm(mem_chunks)
struct memory mem_chunks[NR_MEMS];
{
phys_bytes high, bytes;
phys_clicks clicks, base_click;
unsigned pages;
int i, r;
/* Compute the highest memory location */
high= 0;
for (i= 0; i<NR_MEMS; i++)
{
if (mem_chunks[i].size == 0)
continue;
if (mem_chunks[i].base + mem_chunks[i].size > high)
high= mem_chunks[i].base + mem_chunks[i].size;
}
high <<= CLICK_SHIFT;
#if VERBOSE_VM
printf("do_x86_vm: found high 0x%x\n", high);
#endif
/* Rounding up */
high= (high-1+PAGE_DIR_SIZE) & ~(PAGE_DIR_SIZE-1);
/* The number of pages we need is one for the page directory, enough
* page tables to cover the memory, and one page for alignement.
*/
pages= 1 + (high + PAGE_TABLE_COVER-1)/PAGE_TABLE_COVER + 1;
bytes= pages*PAGE_SIZE;
clicks= (bytes + CLICK_SIZE-1) >> CLICK_SHIFT;
#if VERBOSE_VM
printf("do_x86_vm: need %d pages\n", pages);
printf("do_x86_vm: need %d bytes\n", bytes);
printf("do_x86_vm: need %d clicks\n", clicks);
#endif
for (i= 0; i<NR_MEMS; i++)
{
if (mem_chunks[i].size <= clicks)
continue;
break;
}
if (i >= NR_MEMS)
panic("VM", "not enough memory for VM page tables?", NO_NUM);
base_click= mem_chunks[i].base;
mem_chunks[i].base += clicks;
mem_chunks[i].size -= clicks;
#if VERBOSE_VM
printf("do_x86_vm: using 0x%x clicks @ 0x%x\n", clicks, base_click);
#endif
r= sys_vm_setbuf(base_click << CLICK_SHIFT, clicks << CLICK_SHIFT,
high);
if (r != 0)
printf("do_x86_vm: sys_vm_setbuf failed: %d\n", r);
}
/*===========================================================================*
* arch_map2vir *
*===========================================================================*/
PUBLIC vir_bytes arch_map2vir(struct vmproc *vmp, vir_bytes addr)
{
vir_bytes bottom = CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys);
vm_assert(bottom <= addr);
return addr - bottom;
}
/*===========================================================================*
* arch_vir2map *
*===========================================================================*/
PUBLIC vir_bytes arch_vir2map(struct vmproc *vmp, vir_bytes addr)
{
vir_bytes bottom = CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys);
return addr + bottom;
}

274
servers/vm/main.c Normal file
View File

@@ -0,0 +1,274 @@
#define _SYSTEM 1
#define VERBOSE 0
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#define _MAIN 1
#include "glo.h"
#include "proto.h"
#include "util.h"
#include "vm.h"
#include "sanitycheck.h"
#include <archtypes.h>
#include "../../kernel/const.h"
#include "../../kernel/config.h"
#include "../../kernel/proc.h"
/* Table of calls and a macro to test for being in range. */
struct {
endpoint_t vmc_caller; /* Process that does this, or ANY */
int (*vmc_func)(message *); /* Call handles message. */
char *vmc_name; /* Human-readable string. */
} vm_calls[VM_NCALLS];
/* Macro to verify call range and map 'high' range to 'base' range
* (starting at 0) in one. Evaluates to zero-based call number if call
* number is valid, returns -1 otherwise.
*/
#define CALLNUMBER(c) (((c) >= VM_RQ_BASE && \
(c) < VM_RQ_BASE + ELEMENTS(vm_calls)) ? \
((c) - VM_RQ_BASE) : -1)
FORWARD _PROTOTYPE(void vm_init, (void));
#if SANITYCHECKS
extern int kputc_use_private_grants;
#endif
/*===========================================================================*
* main *
*===========================================================================*/
PUBLIC int main(void)
{
message msg;
int result, who_e;
#if SANITYCHECKS
memcpy(data1, CHECKADDR, sizeof(data1));
#endif
SANITYCHECK(SCL_TOP);
vm_paged = 0;
env_parse("vm_paged", "d", 0, &vm_paged, 0, 1);
#if SANITYCHECKS
env_parse("vm_sanitychecklevel", "d", 0, &vm_sanitychecklevel, 0, SCL_MAX);
#endif
SANITYCHECK(SCL_TOP);
vm_init();
SANITYCHECK(SCL_TOP);
/* This is VM's main loop. */
while (TRUE) {
int r, c;
SANITYCHECK(SCL_TOP);
pt_cycle(); /* pagetable code wants to be called */
#if SANITYCHECKS
slabstats();
#endif
SANITYCHECK(SCL_DETAIL);
if ((r=receive(ANY, &msg)) != OK)
vm_panic("receive() error", r);
if(msg.m_source == LOG_PROC_NR ||
msg.m_source == TTY_PROC_NR)
continue;
SANITYCHECK(SCL_DETAIL);
if(msg.m_type & NOTIFY_MESSAGE) {
switch(msg.m_source) {
case SYSTEM:
/* Kernel wants to have memory ranges
* verified.
*/
handle_memory();
break;
case PM_PROC_NR:
/* PM sends a notify() on shutdown, which
* is OK and we ignore.
*/
break;
case HARDWARE:
/* This indicates a page fault has happened,
* which we have to handle.
*/
handle_pagefaults();
break;
default:
/* No-one else should send us notifies. */
printf("VM: ignoring notify() from %d\n",
msg.m_source);
break;
}
continue;
}
who_e = msg.m_source;
c = msg.m_type - VM_RQ_BASE;
result = ENOSYS; /* Out of range or restricted calls return this. */
if((c=CALLNUMBER(msg.m_type)) < 0 || !vm_calls[c].vmc_func) {
printf("VM: out of range or missing callnr %d from %d\n",
msg.m_type, msg.m_source);
} else if(vm_calls[c].vmc_caller != ANY &&
vm_calls[c].vmc_caller != msg.m_source) {
printf("VM: restricted callnr %d (%s) from %d instead of %d\n",
c,
vm_calls[c].vmc_name, msg.m_source,
vm_calls[c].vmc_caller);
} else {
SANITYCHECK(SCL_FUNCTIONS);
result = vm_calls[c].vmc_func(&msg);
SANITYCHECK(SCL_FUNCTIONS);
}
/* Send reply message, unless the return code is SUSPEND,
* which is a pseudo-result suppressing the reply message.
*/
if(result != SUSPEND) {
SANITYCHECK(SCL_DETAIL);
msg.m_type = result;
if((r=send(who_e, &msg)) != OK) {
printf("VM: couldn't send %d to %d (err %d)\n",
msg.m_type, who_e, r);
vm_panic("send() error", NO_NUM);
}
SANITYCHECK(SCL_DETAIL);
}
SANITYCHECK(SCL_DETAIL);
}
return(OK);
}
/*===========================================================================*
* vm_init *
*===========================================================================*/
PRIVATE void vm_init(void)
{
int s;
struct memory mem_chunks[NR_MEMS];
struct boot_image image[NR_BOOT_PROCS];
struct boot_image *ip;
struct vmproc *vmp;
/* Get chunks of available memory. */
get_mem_chunks(mem_chunks);
/* Initialize VM's process table. Request a copy of the system
* image table that is defined at the kernel level to see which
* slots to fill in.
*/
if (OK != (s=sys_getimage(image)))
vm_panic("couldn't get image table: %d\n", s);
/* Set table to 0. This invalidates all slots (clear VMF_INUSE). */
memset(vmproc, 0, sizeof(vmproc));
/* Walk through boot-time system processes that are alive
* now and make valid slot entries for them.
*/
for (ip = &image[0]; ip < &image[NR_BOOT_PROCS]; ip++) {
if(ip->proc_nr >= _NR_PROCS) { vm_panic("proc", ip->proc_nr); }
if(ip->proc_nr < 0 && ip->proc_nr != SYSTEM) continue;
/* Initialize normal process table slot or special SYSTEM
* table slot. Kernel memory is already reserved.
*/
if(ip->proc_nr >= 0) {
vmp = &vmproc[ip->proc_nr];
} else if(ip->proc_nr == SYSTEM) {
vmp = &vmproc[VMP_SYSTEM];
} else {
vm_panic("init: crazy proc_nr", ip->proc_nr);
}
/* reset fields as if exited */
clear_proc(vmp);
/* Get memory map for this process from the kernel. */
if ((s=get_mem_map(ip->proc_nr, vmp->vm_arch.vm_seg)) != OK)
vm_panic("couldn't get process mem_map",s);
/* Remove this memory from the free list. */
reserve_proc_mem(mem_chunks, vmp->vm_arch.vm_seg);
vmp->vm_flags = VMF_INUSE;
vmp->vm_endpoint = ip->endpoint;
vmp->vm_stacktop =
CLICK2ABS(vmp->vm_arch.vm_seg[S].mem_vir +
vmp->vm_arch.vm_seg[S].mem_len);
if (vmp->vm_arch.vm_seg[T].mem_len != 0)
vmp->vm_flags |= VMF_SEPARATE;
}
/* Let architecture-dependent VM initialization use some memory. */
arch_init_vm(mem_chunks);
/* Architecture-dependent initialization. */
pt_init();
/* Initialize tables to all physical memory. */
mem_init(mem_chunks);
/* Set up table of calls. */
#define CALLMAP(code, func, thecaller) { int i; \
if((i=CALLNUMBER(code)) < 0) { vm_panic(#code " invalid", (code)); } \
if(vm_calls[i].vmc_func) { vm_panic("dup " #code , (code)); } \
vm_calls[i].vmc_func = (func); \
vm_calls[i].vmc_name = #code; \
vm_calls[i].vmc_caller = (thecaller); \
}
/* Set call table to 0. This invalidates all calls (clear
* vmc_func).
*/
memset(vm_calls, 0, sizeof(vm_calls));
/* Requests from PM (restricted to be from PM only). */
CALLMAP(VM_EXIT, do_exit, PM_PROC_NR);
CALLMAP(VM_FORK, do_fork, PM_PROC_NR);
CALLMAP(VM_BRK, do_brk, PM_PROC_NR);
CALLMAP(VM_EXEC_NEWMEM, do_exec_newmem, PM_PROC_NR);
CALLMAP(VM_PUSH_SIG, do_push_sig, PM_PROC_NR);
CALLMAP(VM_WILLEXIT, do_willexit, PM_PROC_NR);
CALLMAP(VM_ADDDMA, do_adddma, PM_PROC_NR);
CALLMAP(VM_DELDMA, do_deldma, PM_PROC_NR);
CALLMAP(VM_GETDMA, do_getdma, PM_PROC_NR);
CALLMAP(VM_ALLOCMEM, do_allocmem, PM_PROC_NR);
/* Requests from tty device driver (/dev/video). */
CALLMAP(VM_MAP_PHYS, do_map_phys, TTY_PROC_NR);
CALLMAP(VM_UNMAP_PHYS, do_unmap_phys, TTY_PROC_NR);
/* Requests from userland (source unrestricted). */
CALLMAP(VM_MMAP, do_mmap, ANY);
/* Requests (actually replies) from VFS (restricted to VFS only). */
CALLMAP(VM_VFS_REPLY_OPEN, do_vfs_reply, VFS_PROC_NR);
CALLMAP(VM_VFS_REPLY_MMAP, do_vfs_reply, VFS_PROC_NR);
CALLMAP(VM_VFS_REPLY_CLOSE, do_vfs_reply, VFS_PROC_NR);
}

161
servers/vm/mmap.c Normal file
View File

@@ -0,0 +1,161 @@
#define _SYSTEM 1
#define VERBOSE 0
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#include <fcntl.h>
#include <memory.h>
#include "glo.h"
#include "proto.h"
#include "util.h"
#include "region.h"
/*===========================================================================*
* do_mmap *
*===========================================================================*/
PUBLIC int do_mmap(message *m)
{
int r, n;
struct vmproc *vmp;
int mfflags = 0;
struct vir_region *vr = NULL;
if((r=vm_isokendpt(m->m_source, &n)) != OK) {
vm_panic("do_mmap: message from strange source", m->m_source);
}
vmp = &vmproc[n];
if(m->VMM_FLAGS & MAP_LOWER16M)
printf("VM: warning for %d: MAP_LOWER16M not implemented\n",
m->m_source);
if(!(vmp->vm_flags & VMF_HASPT))
return ENXIO;
if(m->VMM_FD == -1 || (m->VMM_FLAGS & MAP_ANON)) {
int s;
vir_bytes v;
size_t len = (vir_bytes) m->VMM_LEN;
if(m->VMM_FD != -1) {
return EINVAL;
}
if(m->VMM_FLAGS & MAP_CONTIG) mfflags |= MF_CONTIG;
if(m->VMM_FLAGS & MAP_PREALLOC) mfflags |= MF_PREALLOC;
if(len % VM_PAGE_SIZE)
len += VM_PAGE_SIZE - (len % VM_PAGE_SIZE);
if(!(vr = map_page_region(vmp, vmp->vm_stacktop,
VM_DATATOP, len, 0,
VR_ANON | VR_WRITABLE, mfflags))) {
return ENOMEM;
}
} else {
return ENOSYS;
}
/* Return mapping, as seen from process. */
vm_assert(vr);
m->VMM_RETADDR = arch_map2vir(vmp, vr->vaddr);
return OK;
}
/*===========================================================================*
* do_map_phys *
*===========================================================================*/
PUBLIC int do_map_phys(message *m)
{
int r, n;
struct vmproc *vmp;
endpoint_t target;
struct vir_region *vr;
target = m->VMMP_EP;
if(target == SELF)
target = m->m_source;
if((r=vm_isokendpt(target, &n)) != OK) {
printf("do_map_phys: bogus target %d\n", target);
return EINVAL;
}
vmp = &vmproc[n];
if(!(vmp->vm_flags & VMF_HASPT))
return ENXIO;
if(!(vr = map_page_region(vmp, vmp->vm_stacktop, VM_DATATOP,
(vir_bytes) m->VMMP_LEN, (vir_bytes)m->VMMP_PHADDR,
VR_DIRECT | VR_NOPF | VR_WRITABLE, 0))) {
printf("VM:do_map_phys: map_page_region failed\n");
return ENOMEM;
}
m->VMMP_VADDR_REPLY = (void *) arch_map2vir(vmp, vr->vaddr);
return OK;
}
/*===========================================================================*
* do_unmap_phys *
*===========================================================================*/
PUBLIC int do_unmap_phys(message *m)
{
int r, n;
struct vmproc *vmp;
endpoint_t target;
struct vir_region *region;
target = m->VMUP_EP;
if(target == SELF)
target = m->m_source;
if((r=vm_isokendpt(target, &n)) != OK) {
printf("VM:do_unmap_phys: bogus target %d\n", target);
return EINVAL;
}
vmp = &vmproc[n];
if(!(region = map_lookup(vmp, (vir_bytes) m->VMUM_ADDR))) {
printf("VM:do_unmap_phys: map_lookup failed\n");
return EINVAL;
}
if(!(region->flags & VR_DIRECT)) {
printf("VM:do_unmap_phys: region not a DIRECT mapping\n");
return EINVAL;
}
if(map_unmap_region(vmp, region) != OK) {
printf("VM:do_unmap_phys: map_unmap_region failed\n");
return EINVAL;
}
return OK;
}

175
servers/vm/pagefaults.c Normal file
View File

@@ -0,0 +1,175 @@
#define _SYSTEM 1
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <pagefaults.h>
#include "glo.h"
#include "proto.h"
#include "memory.h"
#include "util.h"
#include "region.h"
static char *pferr(int err)
{
static char buf[100];
sprintf(buf, "err 0x%lx ", err);
if(PFERR_NOPAGE(err)) strcat(buf, "nopage ");
if(PFERR_PROT(err)) strcat(buf, "protection ");
if(PFERR_WRITE(err)) strcat(buf, "write");
if(PFERR_READ(err)) strcat(buf, "read");
return buf;
}
/*===========================================================================*
* handle_pagefaults *
*===========================================================================*/
PUBLIC void handle_pagefaults(void)
{
endpoint_t ep;
u32_t addr, err;
struct vmproc *vmp;
int r, s;
while((r=arch_get_pagefault(&ep, &addr, &err)) == OK) {
struct vir_region *region;
vir_bytes offset;
int p, wr = PFERR_WRITE(err);
if(vm_isokendpt(ep, &p) != OK)
vm_panic("handle_pagefaults: endpoint wrong", ep);
vmp = &vmproc[p];
vm_assert(vmp->vm_flags & VMF_INUSE);
/* See if address is valid at all. */
if(!(region = map_lookup(vmp, addr))) {
vm_assert(PFERR_NOPAGE(err));
printf("VM: SIGSEGV %d bad addr 0x%lx error 0x%lx\n",
ep, addr, err);
if((s=sys_kill(vmp->vm_endpoint, SIGSEGV)) != OK)
vm_panic("sys_kill failed", s);
continue;
}
/* Make sure this isn't a region that isn't supposed
* to cause pagefaults.
*/
vm_assert(!(region->flags & VR_NOPF));
/* If process was writing, see if it's writable. */
if(!(region->flags & VR_WRITABLE) && wr) {
printf("VM: SIGSEGV %d ro map 0x%lx error 0x%lx\n",
ep, addr, err);
if((s=sys_kill(vmp->vm_endpoint, SIGSEGV)) != OK)
vm_panic("sys_kill failed", s);
continue;
}
vm_assert(addr > region->vaddr);
offset = addr - region->vaddr;
/* Access is allowed; handle it. */
if((r=map_pagefault(vmp, region, offset, wr)) != OK) {
printf("VM: SIGSEGV %d pagefault not handled\n", ep);
if((s=sys_kill(vmp->vm_endpoint, SIGSEGV)) != OK)
vm_panic("sys_kill failed", s);
continue;
}
/* Pagefault is handled, so now reactivate the process. */
if((s=sys_vmctl(ep, VMCTL_CLEAR_PAGEFAULT, r)) != OK)
vm_panic("handle_pagefaults: sys_vmctl failed", ep);
}
return;
}
/*===========================================================================*
* handle_memory *
*===========================================================================*/
PUBLIC void handle_memory(void)
{
int r, s;
endpoint_t who;
vir_bytes mem;
vir_bytes len;
int wrflag;
while((r=sys_vmctl_get_memreq(&who, &mem, &len, &wrflag)) == OK) {
int p, r = OK;
struct vir_region *region;
struct vmproc *vmp;
vir_bytes o;
if(vm_isokendpt(who, &p) != OK)
vm_panic("handle_memory: endpoint wrong", who);
vmp = &vmproc[p];
/* Page-align memory and length. */
o = mem % VM_PAGE_SIZE;
mem -= o;
len += o;
o = len % VM_PAGE_SIZE;
if(o > 0) len += VM_PAGE_SIZE - o;
if(!(region = map_lookup(vmp, mem))) {
printf("VM: handle_memory: memory doesn't exist\n");
r = EFAULT;
} else if(mem + len > region->vaddr + region->length) {
vm_assert(region->vaddr <= mem);
vm_panic("handle_memory: not contained", NO_NUM);
} else if(!(region->flags & VR_WRITABLE) && wrflag) {
printf("VM: handle_memory: write to unwritable map\n");
r = EFAULT;
} else {
vir_bytes offset;
vm_assert(region->vaddr <= mem);
vm_assert(!(region->flags & VR_NOPF));
vm_assert(!(region->vaddr % VM_PAGE_SIZE));
offset = mem - region->vaddr;
r = map_handle_memory(vmp, region, offset, len, wrflag);
}
if(r != OK) {
printf("VM: SIGSEGV %d, memory range not available\n",
vmp->vm_endpoint);
if((s=sys_kill(vmp->vm_endpoint, SIGSEGV)) != OK)
vm_panic("sys_kill failed", s);
}
if(sys_vmctl(who, VMCTL_MEMREQ_REPLY, r) != OK)
vm_panic("handle_memory: sys_vmctl failed", r);
if(r != OK) {
printf("VM: killing %d\n", vmp->vm_endpoint);
if((s=sys_kill(vmp->vm_endpoint, SIGSEGV)) != OK)
vm_panic("sys_kill failed", s);
}
}
}

139
servers/vm/proto.h Normal file
View File

@@ -0,0 +1,139 @@
/* Function prototypes. */
struct vmproc;
struct stat;
struct mem_map;
struct memory;
#include <minix/ipc.h>
#include <minix/endpoint.h>
#include <minix/safecopies.h>
#include <timers.h>
#include <stdio.h>
#include <pagetable.h>
#include "vmproc.h"
#include "vm.h"
/* alloc.c */
_PROTOTYPE( phys_clicks alloc_mem_f, (phys_clicks clicks, u32_t flags) );
_PROTOTYPE( int do_adddma, (message *msg) );
_PROTOTYPE( int do_deldma, (message *msg) );
_PROTOTYPE( int do_getdma, (message *msg) );
_PROTOTYPE( int do_allocmem, (message *msg) );
_PROTOTYPE( void release_dma, (struct vmproc *vmp) );
_PROTOTYPE( void free_mem_f, (phys_clicks base, phys_clicks clicks) );
#define ALLOC_MEM(clicks, flags) alloc_mem_f(clicks, flags)
#define FREE_MEM(base, clicks) free_mem_f(base, clicks)
_PROTOTYPE( void mem_init, (struct memory *chunks) );
_PROTOTYPE( void memstats, (void) );
/* utility.c */
_PROTOTYPE( int get_mem_map, (int proc_nr, struct mem_map *mem_map) );
_PROTOTYPE( void get_mem_chunks, (struct memory *mem_chunks));
_PROTOTYPE( void reserve_proc_mem, (struct memory *mem_chunks,
struct mem_map *map_ptr));
_PROTOTYPE( int vm_isokendpt, (endpoint_t ep, int *proc) );
_PROTOTYPE( int get_stack_ptr, (int proc_nr, vir_bytes *sp) );
/* exit.c */
_PROTOTYPE( void clear_proc, (struct vmproc *vmp) );
_PROTOTYPE( int do_exit, (message *msg) );
_PROTOTYPE( int do_willexit, (message *msg) );
_PROTOTYPE( void free_proc, (struct vmproc *vmp) );
/* fork.c */
_PROTOTYPE( int do_fork, (message *msg) );
/* exec.c */
_PROTOTYPE( struct vmproc *find_share, (struct vmproc *vmp_ign, Ino_t ino,
Dev_t dev, time_t ctime) );
_PROTOTYPE( int do_exec_newmem, (message *msg) );
/* break.c */
_PROTOTYPE( int do_brk, (message *msg) );
_PROTOTYPE( int adjust, (struct vmproc *rmp,
vir_clicks data_clicks, vir_bytes sp) );
_PROTOTYPE( int real_brk, (struct vmproc *vmp, vir_bytes v));
/* signal.c */
_PROTOTYPE( int do_push_sig, (message *msg) );
/* vfs.c */
_PROTOTYPE( int do_vfs_reply, (message *msg) );
_PROTOTYPE( int vfs_open, (struct vmproc *for_who, callback_t callback,
cp_grant_id_t filename_gid, int filename_len, int flags, int mode));
_PROTOTYPE( int vfs_close, (struct vmproc *for_who, callback_t callback,
int fd));
/* mmap.c */
_PROTOTYPE(int do_mmap, (message *msg) );
_PROTOTYPE(int do_map_phys, (message *msg) );
_PROTOTYPE(int do_unmap_phys, (message *msg) );
/* pagefaults.c */
_PROTOTYPE( void handle_pagefaults, (void) );
_PROTOTYPE( void handle_memory, (void) );
/* $(ARCH)/pagetable.c */
_PROTOTYPE( void pt_init, (void) );
_PROTOTYPE( int pt_new, (pt_t *pt) );
_PROTOTYPE( int pt_copy, (pt_t *src, pt_t *dst) );
_PROTOTYPE( void pt_free, (pt_t *pt) );
_PROTOTYPE( void pt_freerange, (pt_t *pt, vir_bytes lo, vir_bytes hi) );
_PROTOTYPE( int pt_allocmap, (pt_t *pt, vir_bytes minv, vir_bytes maxv,
size_t bytes, u32_t pageflags, u32_t allocflags, vir_bytes *newv));
_PROTOTYPE( int pt_writemap, (pt_t *pt, vir_bytes v, phys_bytes physaddr,
size_t bytes, u32_t flags, u32_t writemapflags));
_PROTOTYPE( int pt_bind, (pt_t *pt, struct vmproc *who) );
_PROTOTYPE( void *vm_allocpages, (phys_bytes *p, int pages, int cat));
_PROTOTYPE( void pt_cycle, (void));
_PROTOTYPE( int pt_mapkernel, (pt_t *pt));
_PROTOTYPE( void phys_readaddr, (phys_bytes addr, phys_bytes *v1, phys_bytes *v2));
_PROTOTYPE( void phys_writeaddr, (phys_bytes addr, phys_bytes v1, phys_bytes v2));
#if SANITYCHECKS
_PROTOTYPE( void pt_sanitycheck, (pt_t *pt, char *file, int line) );
#endif
/* $(ARCH)/pagefaults.c */
_PROTOTYPE( int arch_get_pagefault, (endpoint_t *who, vir_bytes *addr, u32_t *err));
/* slaballoc.c */
_PROTOTYPE(void *slaballoc,(int bytes));
_PROTOTYPE(void slabfree,(void *mem, int bytes));
_PROTOTYPE(void slabstats,(void));
#define SLABALLOC(var) (var = slaballoc(sizeof(*var)))
#define SLABFREE(ptr) slabfree(ptr, sizeof(*(ptr)))
/* region.c */
_PROTOTYPE(struct vir_region * map_page_region,(struct vmproc *vmp, \
vir_bytes min, vir_bytes max, vir_bytes length, vir_bytes what, \
u32_t flags, int mapflags));
_PROTOTYPE(struct vir_region * map_proc_kernel,(struct vmproc *dst));
_PROTOTYPE(int map_region_extend,(struct vir_region *vr, vir_bytes delta));
_PROTOTYPE(int map_region_shrink,(struct vir_region *vr, vir_bytes delta));
_PROTOTYPE(int map_unmap_region,(struct vmproc *vmp, struct vir_region *vr));
_PROTOTYPE(int map_free_proc,(struct vmproc *vmp));
_PROTOTYPE(int map_proc_copy,(struct vmproc *dst, struct vmproc *src));
_PROTOTYPE(struct vir_region *map_lookup,(struct vmproc *vmp, vir_bytes addr));
_PROTOTYPE(int map_pagefault,(struct vmproc *vmp,
struct vir_region *region, vir_bytes offset, int write));
_PROTOTYPE(int map_handle_memory,(struct vmproc *vmp,
struct vir_region *region, vir_bytes offset, vir_bytes len, int write));
_PROTOTYPE(struct vir_region * map_region_lookup_tag, (struct vmproc *vmp, u32_t tag));
_PROTOTYPE(void map_region_set_tag, (struct vir_region *vr, u32_t tag));
_PROTOTYPE(u32_t map_region_get_tag, (struct vir_region *vr));
#if SANITYCHECKS
_PROTOTYPE(void map_sanitycheck,(char *file, int line));
#endif
/* $(ARCH)/vm.c */
_PROTOTYPE( void arch_init_vm, (struct memory mem_chunks[NR_MEMS]));
_PROTOTYPE( vir_bytes, arch_map2vir(struct vmproc *vmp, vir_bytes addr));
_PROTOTYPE( vir_bytes, arch_vir2map(struct vmproc *vmp, vir_bytes addr));

929
servers/vm/region.c Normal file
View File

@@ -0,0 +1,929 @@
#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 *));
#if SANITYCHECKS
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("%d:\n", vmp->vm_endpoint);
for(vr = vmp->vm_regions; vr; vr = vr->next) {
struct phys_region *ph;
printf("\t0x%08lx - 0x%08lx: %s (0x%lx)\n",
vr->vaddr, vr->vaddr + vr->length, map_name(vr), vr);
for(ph = vr->first; ph; ph = ph->next) {
printf("0x%lx-0x%lx(%d) ",
vr->vaddr + ph->ph->offset,
vr->vaddr + ph->ph->offset + ph->ph->length,
ph->ph->refcount);
}
printf("\n");
}
}
/*===========================================================================*
* 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; \
} \
} \
}
/* 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);
}
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_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);
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);
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;
/* If this is a 'direct' mapping, try to actually map it. */
if(flags & VR_DIRECT) {
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;
}
/*===========================================================================*
* map_free *
*===========================================================================*/
PRIVATE int map_free(struct vir_region *region)
{
struct phys_region *pr, *nextpr;
for(pr = region->first; pr; pr = nextpr) {
vm_assert(pr->ph->refcount > 0);
pr->ph->refcount--;
nextpr = pr->next;
region->first = nextpr; /* For sanity checks. */
if(pr->ph->refcount == 0) {
if(region->flags & VR_ANON) {
FREE_MEM(ABS2CLICK(pr->ph->phys),
ABS2CLICK(pr->ph->length));
} else if(region->flags & VR_DIRECT) {
; /* No action required. */
} else {
vm_panic("strange phys flags", NO_NUM);
}
SLABFREE(pr->ph);
}
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;
map_free(r);
vmp->vm_regions = nextr; /* For sanity checks. */
}
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 *physr, *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) {
if((mem_clicks = ALLOC_MEM(clicks, PAF_CLEAR)) == NO_MEM) {
SLABFREE(newpb);
SLABFREE(newphysr);
return ENOMEM;
}
mem = CLICK2ABS(mem_clicks);
} else {
mem = what_mem;
}
/* New physical block. */
newpb->phys = mem;
newpb->refcount = 1;
newpb->offset = offset;
newpb->length = length;
/* New physical region. */
newphysr->ph = newpb;
/* Update pagetable. */
vm_assert(!(length % VM_PAGE_SIZE));
vm_assert(!(newpb->length % VM_PAGE_SIZE));
if(map_ph_writept(vmp, region, newpb, NULL, NULL) != OK) {
if(!what_mem)
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 {
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;
SANITYCHECK(SCL_FUNCTIONS);
/* This is only to be done if there is more than one copy. */
vm_assert(ph->ph->refcount > 1);
/* Do actal 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((newmem_cl = ALLOC_MEM(clicks, 0)) == NO_MEM) {
SLABFREE(newpb);
return ENOMEM;
}
newmem = CLICK2ABS(newmem_cl);
ph->ph->refcount--;
vm_assert(ph->ph->refcount > 0);
newpb->length = ph->ph->length;
newpb->offset = ph->ph->offset;
newpb->refcount = 1;
newpb->phys = newmem;
/* 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_pagefault *
*===========================================================================*/
PUBLIC int map_pagefault(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, 0,
region->first);
}
if(r != OK)
printf("VM: map_pagefault: 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, 0, 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)
{
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;
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 *ph;
SANITYCHECK(SCL_DETAIL);
if(!(newvr = map_copy_region(vr))) {
map_free_proc(dst);
SANITYCHECK(SCL_FUNCTIONS);
return ENOMEM;
}
SANITYCHECK(SCL_DETAIL);
if(prevvr) { prevvr->next = newvr; }
else { dst->vm_regions = newvr; }
for(ph = vr->first; ph; ph = ph->next) {
vm_assert(ph->ph->refcount > 0);
ph->ph->refcount++;
vm_assert(ph->ph->refcount > 1);
}
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 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;
}
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));
printf("VM: ignoring region shrink\n");
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,
0, r->length, 0, WMF_OVERWRITE) != OK) {
printf("VM: map_unmap_region: pt_writemap failed\n");
return ENOMEM;
}
SANITYCHECK(SCL_FUNCTIONS);
return OK;
}

46
servers/vm/region.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef _REGION_H
#define _REGION_H 1
struct phys_block {
#if SANITYCHECKS
u32_t seencount;
#endif
vir_bytes offset; /* offset from start of vir region */
vir_bytes length; /* no. of contiguous bytes */
phys_bytes phys; /* physical memory */
u8_t refcount; /* Refcount of these pages */
};
struct phys_region {
struct phys_region *next; /* next contiguous block */
struct phys_block *ph;
};
struct vir_region {
struct vir_region *next; /* next virtual region in this process */
vir_bytes vaddr; /* virtual address, offset from pagetable */
vir_bytes length; /* length in bytes */
struct phys_region *first; /* phys regions in vir region */
u16_t flags;
u32_t tag; /* Opaque to mapping code. */
};
/* Mapping flags: */
#define VR_WRITABLE 0x01 /* Process may write here. */
#define VR_NOPF 0x02 /* May not generate page faults. */
/* Mapping type: */
#define VR_ANON 0x10 /* Memory to be cleared and allocated */
#define VR_DIRECT 0x20 /* Mapped, but not managed by VM */
/* Tag values: */
#define VRT_NONE 0xBEEF0000
#define VRT_HEAP 0xBEEF0001
/* map_page_region flags */
#define MF_PREALLOC 0x01
#define MF_CONTIG 0x02
#endif

46
servers/vm/sanitycheck.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef _SANITYCHECK_H
#define _SANITYCHECK_H 1
#include "vm.h"
#include "glo.h"
#if SANITYCHECKS
/* This macro is used in the sanity check functions, where file and
* line are function arguments.
*/
#define MYASSERT(c) do { if(!(c)) { \
printf("VM:%s:%d: %s failed\n", file, line, #c); \
vm_panic("sanity check failed", NO_NUM); } } while(0)
#define SANITYCHECK(l) if((l) <= vm_sanitychecklevel) { \
int failflag = 0; \
u32_t *origptr = CHECKADDR;\
int _sanep; \
struct vmproc *vmp; \
\
for(_sanep = 0; _sanep < sizeof(data1) / sizeof(*origptr); \
_sanep++) { \
if(origptr[_sanep] != data1[_sanep]) { \
printf("%d: %08lx != %08lx ", \
_sanep, origptr[_sanep], data1[_sanep]); failflag = 1; \
} \
} \
if(failflag) { \
printf("%s:%d: memory corruption test failed\n", \
__FILE__, __LINE__); \
vm_panic("memory corruption", NO_NUM); \
} \
for(vmp = vmproc; vmp <= &vmproc[_NR_PROCS]; vmp++) { \
if((vmp->vm_flags & (VMF_INUSE | VMF_HASPT)) == \
(VMF_INUSE | VMF_HASPT)) { \
pt_sanitycheck(&vmp->vm_pt, __FILE__, __LINE__); \
} \
} \
map_sanitycheck(__FILE__, __LINE__); \
}
#else
#define SANITYCHECK
#endif
#endif

64
servers/vm/signal.c Normal file
View File

@@ -0,0 +1,64 @@
#define _SYSTEM 1
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <sys/sigcontext.h>
#include <errno.h>
#include <env.h>
#include "glo.h"
#include "vm.h"
#include "proto.h"
#include "util.h"
#define DATA_CHANGED 1 /* flag value when data segment size changed */
#define STACK_CHANGED 2 /* flag value when stack size changed */
/*===========================================================================*
* do_push_sig *
*===========================================================================*/
PUBLIC int do_push_sig(message *msg)
{
int r, n;
endpoint_t ep;
vir_bytes sp;
struct vmproc *vmp;
ep = msg->VMPS_ENDPOINT;
if((r=vm_isokendpt(ep, &n)) != OK) {
printf("VM: bogus endpoint %d from %d\n", ep, msg->m_source);
return r;
}
vmp = &vmproc[n];
if ((r=get_stack_ptr(ep, &sp)) != OK)
vm_panic("couldn't get new stack pointer (for sig)",r);
/* Save old SP for caller */
msg->VMPS_OLD_SP = (char *) sp;
/* Make room for the sigcontext and sigframe struct. */
sp -= sizeof(struct sigcontext)
+ 3 * sizeof(char *) + 2 * sizeof(int);
if ((r=adjust(vmp, vmp->vm_arch.vm_seg[D].mem_len, sp)) != OK) {
printf("VM: do_push_sig: adjust() failed: %d\n", r);
return r;
}
return OK;
}

399
servers/vm/slaballoc.c Normal file
View File

@@ -0,0 +1,399 @@
#define _SYSTEM 1
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <memory.h>
#include "glo.h"
#include "proto.h"
#include "util.h"
#include "sanitycheck.h"
#define SLABSIZES 60
#define ITEMSPERPAGE(s, bytes) (DATABYTES / (bytes))
#define ELBITS (sizeof(element_t)*8)
#define BITPAT(b) (1UL << ((b) % ELBITS))
#define BITEL(f, b) (f)->sdh.usebits[(b)/ELBITS]
#define OFF(f, b) vm_assert(!GETBIT(f, b))
#define ON(f, b) vm_assert(GETBIT(f, b))
#define GETBIT(f, b) (BITEL(f,b) & BITPAT(b))
#define SETBIT(f, b) {OFF(f,b); (BITEL(f,b)|= BITPAT(b)); (f)->sdh.nused++; }
#define CLEARBIT(f, b) {ON(f, b); (BITEL(f,b)&=~BITPAT(b)); (f)->sdh.nused--; (f)->sdh.freeguess = (b); }
#define MINSIZE 8
#define MAXSIZE (SLABSIZES-1+MINSIZE)
#define USEELEMENTS (1+(VM_PAGE_SIZE/MINSIZE/8))
PRIVATE int pages = 0;
typedef u8_t element_t;
#define BITS_FULL (~(element_t)0)
typedef element_t elements_t[USEELEMENTS];
/* This file is too low-level to have global SANITYCHECKs everywhere,
* as the (other) data structures are often necessarily in an
* inconsistent state during a slaballoc() / slabfree(). So only do
* our own sanity checks here, with SLABSANITYCHECK.
*/
#if SANITYCHECKS
#define SLABSANITYCHECK(l) if((l) <= vm_sanitychecklevel) { \
slab_sanitycheck(__FILE__, __LINE__); }
#else
#define SLABSANITYCHECK(l)
#endif
struct sdh {
u8_t list;
u16_t nused; /* Number of data items used in this slab. */
#if SANITYCHECKS
u32_t magic;
#endif
int freeguess;
struct slabdata *next, *prev;
elements_t usebits;
phys_bytes phys;
};
#define DATABYTES (VM_PAGE_SIZE-sizeof(struct sdh))
#define MAGIC 0x1f5b842f
#define JUNK 0xdeadbeef
#define NOJUNK 0xc0ffee
#define LIST_UNUSED 0
#define LIST_FREE 1
#define LIST_USED 2
#define LIST_FULL 3
#define LIST_NUMBER 4
PRIVATE struct slabheader {
struct slabdata {
struct sdh sdh;
u8_t data[DATABYTES];
} *list_head[LIST_NUMBER];
} slabs[SLABSIZES];
#define GETSLAB(b, s) { \
int i; \
vm_assert((b) >= MINSIZE); \
i = (b) - MINSIZE; \
vm_assert((i) < SLABSIZES); \
vm_assert((i) >= 0); \
s = &slabs[i]; \
}
#define LH(sl, l) (sl)->list_head[l]
#define MOVEHEAD(sl, l1, l2) { \
struct slabdata *t; \
vm_assert(LH(sl,l1)); \
REMOVEHEAD(sl, l1, t); \
ADDHEAD(t, sl, l2); \
}
#define REMOVEHEAD(sl, list, to) { \
(to) = LH(sl, list); \
vm_assert(to); \
LH(sl, list) = (to)->sdh.next; \
if(LH(sl, list)) LH(sl, list) = NULL; \
vm_assert((to)->sdh.magic == MAGIC);\
vm_assert(!(to)->sdh.prev); \
}
#define ADDHEAD(nw, sl, l) { \
vm_assert((nw)->sdh.magic == MAGIC); \
(nw)->sdh.next = LH(sl, l); \
(nw)->sdh.prev = NULL; \
(nw)->sdh.list = l; \
LH(sl, l) = (nw); \
if((nw)->sdh.next) (nw)->sdh.next->sdh.prev = (nw); \
}
#define UNLINKNODE(n) { \
if((f)->sdh.prev) (f)->sdh.prev->sdh.next = (f)->sdh.next; \
if((f)->sdh.next) (f)->sdh.next->sdh.prev = (f)->sdh.prev; \
}
struct slabdata *newslabdata(int list)
{
struct slabdata *n;
phys_bytes p;
vm_assert(sizeof(*n) == VM_PAGE_SIZE);
if(!(n = vm_allocpages(&p, 1, VMP_SLAB)))
return NULL;
memset(n->sdh.usebits, 0, sizeof(n->sdh.usebits));
pages++;
n->sdh.phys = p;
#if SANITYCHECKS
n->sdh.magic = MAGIC;
#endif
n->sdh.nused = 0;
n->sdh.freeguess = 0;
n->sdh.list = list;
return n;
}
#if SANITYCHECKS
/*===========================================================================*
* checklist *
*===========================================================================*/
PRIVATE int checklist(char *file, int line,
struct slabheader *s, int l, int bytes)
{
struct slabdata *n = s->list_head[l];
int ch = 0;
while(n) {
int count = 0, i;
MYASSERT(n->sdh.list == l);
MYASSERT(n->sdh.magic == MAGIC);
if(n->sdh.prev)
MYASSERT(n->sdh.prev->sdh.next == n);
else
MYASSERT(s->list_head[l] == n);
if(n->sdh.next) MYASSERT(n->sdh.next->sdh.prev == n);
for(i = 0; i < USEELEMENTS*8; i++)
if(i >= ITEMSPERPAGE(s, bytes))
MYASSERT(!GETBIT(n, i));
else
if(GETBIT(n,i))
count++;
MYASSERT(count == n->sdh.nused);
ch += count;
n = n->sdh.next;
}
return ch;
}
/*===========================================================================*
* void slab_sanitycheck *
*===========================================================================*/
PUBLIC void slab_sanitycheck(char *file, int line)
{
int s;
for(s = 0; s < SLABSIZES; s++) {
int l;
for(l = 0; l < LIST_NUMBER; l++) {
checklist(file, line, &slabs[s], l, s + MINSIZE);
}
}
}
#endif
/*===========================================================================*
* void *slaballoc *
*===========================================================================*/
PUBLIC void *slaballoc(int bytes)
{
int i, n = 0;
struct slabheader *s;
struct slabdata *firstused;
SLABSANITYCHECK(SCL_FUNCTIONS);
/* Retrieve entry in slabs[]. */
GETSLAB(bytes, s);
vm_assert(s);
/* To make the common case more common, make space in the 'used'
* queue first.
*/
if(!LH(s, LIST_USED)) {
/* Make sure there is something on the freelist. */
SLABSANITYCHECK(SCL_DETAIL);
if(!LH(s, LIST_FREE)) {
struct slabdata *n = newslabdata(LIST_FREE);
SLABSANITYCHECK(SCL_DETAIL);
if(!n) return NULL;
ADDHEAD(n, s, LIST_FREE);
SLABSANITYCHECK(SCL_DETAIL);
}
SLABSANITYCHECK(SCL_DETAIL);
MOVEHEAD(s, LIST_FREE, LIST_USED);
SLABSANITYCHECK(SCL_DETAIL);
}
SLABSANITYCHECK(SCL_DETAIL);
vm_assert(s);
firstused = LH(s, LIST_USED);
vm_assert(firstused);
vm_assert(firstused->sdh.magic == MAGIC);
for(i = firstused->sdh.freeguess; n < ITEMSPERPAGE(s, bytes); n++, i++) {
SLABSANITYCHECK(SCL_DETAIL);
i = i % ITEMSPERPAGE(s, bytes);
if(!GETBIT(firstused, i)) {
struct slabdata *f;
char *ret;
SETBIT(firstused, i);
SLABSANITYCHECK(SCL_DETAIL);
if(firstused->sdh.nused == ITEMSPERPAGE(s, bytes)) {
SLABSANITYCHECK(SCL_DETAIL);
MOVEHEAD(s, LIST_USED, LIST_FULL);
SLABSANITYCHECK(SCL_DETAIL);
}
SLABSANITYCHECK(SCL_DETAIL);
ret = ((char *) firstused->data) + i*bytes;
#if SANITYCHECKS
f = (struct slabdata *) ((char *) ret - (vir_bytes) ret % VM_PAGE_SIZE);
if(f->sdh.magic != MAGIC) {
printf("slaballoc bogus pointer 0x%lx, "
"rounded 0x%lx, bad magic 0x%lx\n",
ret, f, f->sdh.magic);
vm_panic("slaballoc check failed", NO_NUM);
}
*(u32_t *) ret = NOJUNK;
#endif
SLABSANITYCHECK(SCL_FUNCTIONS);
firstused->sdh.freeguess = i+1;
return ret;
}
SLABSANITYCHECK(SCL_DETAIL);
}
SLABSANITYCHECK(SCL_FUNCTIONS);
vm_panic("slaballoc: no space in 'used' slabdata", NO_NUM);
/* Not reached. */
return NULL;
}
/*===========================================================================*
* void *slabfree *
*===========================================================================*/
PUBLIC void slabfree(void *mem, int bytes)
{
int i;
struct slabheader *s;
struct slabdata *f;
SLABSANITYCHECK(SCL_FUNCTIONS);
#if SANITYCHECKS
if(*(u32_t *) mem == JUNK) {
printf("VM: WARNING: likely double free, JUNK seen\n");
}
#endif
/* Retrieve entry in slabs[]. */
GETSLAB(bytes, s);
/* Round address down to VM_PAGE_SIZE boundary to get header. */
f = (struct slabdata *) ((char *) mem - (vir_bytes) mem % VM_PAGE_SIZE);
vm_assert(f->sdh.magic == MAGIC);
vm_assert(f->sdh.list == LIST_USED || f->sdh.list == LIST_FULL);
/* Make sure it's in range. */
vm_assert((char *) mem >= (char *) f->data);
vm_assert((char *) mem < (char *) f->data + sizeof(f->data));
/* Get position. */
i = (char *) mem - (char *) f->data;
vm_assert(!(i % bytes));
i = i / bytes;
/* Make sure it _was_ allocated. */
vm_assert(GETBIT(f, i));
/* Free this data. */
CLEARBIT(f, i);
#if SANITYCHECKS
*(u32_t *) mem = JUNK;
#endif
/* Check if this slab changes lists. */
if(f->sdh.nused == 0) {
/* Now become FREE; must've been USED */
vm_assert(f->sdh.list == LIST_USED);
UNLINKNODE(f);
if(f == LH(s, LIST_USED))
LH(s, LIST_USED) = f->sdh.next;
ADDHEAD(f, s, LIST_FREE);
SLABSANITYCHECK(SCL_DETAIL);
} else if(f->sdh.nused == ITEMSPERPAGE(s, bytes)-1) {
/* Now become USED; must've been FULL */
vm_assert(f->sdh.list == LIST_FULL);
UNLINKNODE(f);
if(f == LH(s, LIST_FULL))
LH(s, LIST_FULL) = f->sdh.next;
ADDHEAD(f, s, LIST_USED);
SLABSANITYCHECK(SCL_DETAIL);
} else {
/* Stay USED */
vm_assert(f->sdh.list == LIST_USED);
}
SLABSANITYCHECK(SCL_FUNCTIONS);
return;
}
#if SANITYCHECKS
/*===========================================================================*
* void slabstats *
*===========================================================================*/
PUBLIC void slabstats(void)
{
int s, total = 0, totalbytes = 0;
static int n;
n++;
if(n%1000) return;
for(s = 0; s < SLABSIZES; s++) {
int l;
for(l = 0; l < LIST_NUMBER; l++) {
int b, t;
b = s + MINSIZE;
t = checklist(__FILE__, __LINE__, &slabs[s], l, b);
if(t > 0) {
int bytes = t * b;
printf("VMSTATS: %2d slabs: %d (%dkB)\n", b, t, bytes/1024);
totalbytes += bytes;
}
}
}
if(pages > 0) {
printf("VMSTATS: %dK net used in slab objects in %d pages (%dkB): %d%% utilization\n",
totalbytes/1024, pages, pages*VM_PAGE_SIZE/1024,
100 * totalbytes / (pages*VM_PAGE_SIZE));
}
}
#endif

28
servers/vm/util.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef _UTIL_H
#define _UTIL_H 1
#include "vm.h"
#include "glo.h"
#define ELEMENTS(a) (sizeof(a)/sizeof((a)[0]))
#if SANITYCHECKS
#define vm_assert(cond) { \
if(vm_sanitychecklevel > 0 && !(cond)) { \
printf("VM:%s:%d: assert failed: %s\n", \
__FILE__, __LINE__, #cond); \
panic("VM", "assert failed", NO_NUM); \
} \
}
#else
#define vm_assert(cond) ;
#endif
#define vm_panic(str, n) { char _pline[100]; \
sprintf(_pline, "%s:%d: %s", __FILE__, __LINE__, (str)); \
panic("VM", _pline, (n)); \
}
#endif

165
servers/vm/utility.c Normal file
View File

@@ -0,0 +1,165 @@
/* This file contains some utility routines for VM. */
#define _SYSTEM 1
#define _MINIX 1 /* To get the brk() prototype (as _brk()). */
#define brk _brk /* Our brk() must redefine _brk(). */
#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 <string.h>
#include <errno.h>
#include <env.h>
#include <unistd.h>
#include "proto.h"
#include "glo.h"
#include "util.h"
#include <archconst.h>
#include <archtypes.h>
#include "../../kernel/const.h"
#include "../../kernel/config.h"
#include "../../kernel/type.h"
#include "../../kernel/proc.h"
/*===========================================================================*
* get_mem_map *
*===========================================================================*/
PUBLIC int get_mem_map(proc_nr, mem_map)
int proc_nr; /* process to get map of */
struct mem_map *mem_map; /* put memory map here */
{
struct proc p;
int s;
if ((s=sys_getproc(&p, proc_nr)) != OK)
return(s);
memcpy(mem_map, p.p_memmap, sizeof(p.p_memmap));
return(OK);
}
/*===========================================================================*
* get_mem_chunks *
*===========================================================================*/
PUBLIC void get_mem_chunks(mem_chunks)
struct memory *mem_chunks; /* store mem chunks here */
{
/* Initialize the free memory list from the 'memory' boot variable. Translate
* the byte offsets and sizes in this list to clicks, properly truncated.
*/
long base, size, limit;
int i;
struct memory *memp;
/* Obtain and parse memory from system environment. */
if(env_memory_parse(mem_chunks, NR_MEMS) != OK)
vm_panic("couldn't obtain memory chunks", NO_NUM);
/* 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 = (base + CLICK_SIZE-1) & ~(long)(CLICK_SIZE-1);
limit &= ~(long)(CLICK_SIZE-1);
if (limit <= base) {
memp->base = memp->size = 0;
} else {
memp->base = base >> CLICK_SHIFT;
memp->size = (limit - base) >> CLICK_SHIFT;
}
}
}
/*===========================================================================*
* reserve_proc_mem *
*===========================================================================*/
PUBLIC void reserve_proc_mem(mem_chunks, map_ptr)
struct memory *mem_chunks; /* store mem chunks here */
struct mem_map *map_ptr; /* memory to remove */
{
/* Remove server memory from the free memory list. The boot monitor
* promises to put processes at the start of memory chunks. The
* tasks all use same base address, so only the first task changes
* the memory lists. The servers and init have their own memory
* spaces and their memory will be removed from the list.
*/
struct memory *memp;
for (memp = mem_chunks; memp < &mem_chunks[NR_MEMS]; memp++) {
if (memp->base == map_ptr[T].mem_phys) {
memp->base += map_ptr[T].mem_len + map_ptr[S].mem_vir;
memp->size -= map_ptr[T].mem_len + map_ptr[S].mem_vir;
break;
}
}
if (memp >= &mem_chunks[NR_MEMS])
{
vm_panic("reserve_proc_mem: can't find map in mem_chunks ",
map_ptr[T].mem_phys);
}
}
/*===========================================================================*
* vm_isokendpt *
*===========================================================================*/
PUBLIC int vm_isokendpt(endpoint_t endpoint, int *proc)
{
*proc = _ENDPOINT_P(endpoint);
if(*proc < -NR_TASKS || *proc >= NR_PROCS)
return EINVAL;
if(*proc >= 0 && endpoint != vmproc[*proc].vm_endpoint)
return EDEADSRCDST;
if(*proc >= 0 && !(vmproc[*proc].vm_flags & VMF_INUSE))
return EDEADSRCDST;
return OK;
}
struct proc mytmpproc;
/*===========================================================================*
* get_stack_ptr *
*===========================================================================*/
PUBLIC int get_stack_ptr(proc_nr_e, sp)
int proc_nr_e; /* process to get sp of */
vir_bytes *sp; /* put stack pointer here */
{
int s;
if ((s=sys_getproc(&mytmpproc, proc_nr_e)) != OK)
return(s);
*sp = mytmpproc.p_reg.sp;
return(OK);
}
/*===========================================================================*
* _brk *
*===========================================================================*/
extern char *_brksize;
PUBLIC int brk(brk_addr)
char *brk_addr;
{
int r;
struct vmproc *vmm = &vmproc[VM_PROC_NR];
/* VM wants to call brk() itself. */
if((r=real_brk(vmm, (vir_bytes) brk_addr)) != OK)
vm_panic("VM: brk() on myself failed\n", NO_NUM);
_brksize = brk_addr;
return 0;
}

140
servers/vm/vfs.c Normal file
View File

@@ -0,0 +1,140 @@
#define _SYSTEM 1
#define VERBOSE 0
#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/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#include "glo.h"
#include "proto.h"
#include "util.h"
/*===========================================================================*
* register_callback *
*===========================================================================*/
PRIVATE void register_callback(struct vmproc *for_who, callback_t callback,
int callback_type)
{
if(for_who->vm_callback) {
vm_panic("register_callback: callback already registered",
for_who->vm_callback_type);
}
for_who->vm_callback = callback;
for_who->vm_callback_type = callback_type;
return;
}
/*===========================================================================*
* vfs_open *
*===========================================================================*/
PUBLIC int vfs_open(struct vmproc *for_who, callback_t callback,
cp_grant_id_t filename_gid, int filename_len, int flags, int mode)
{
static message m;
int r;
register_callback(for_who, callback, VM_VFS_REPLY_OPEN);
m.m_type = VM_VFS_OPEN;
m.VMVO_NAME_GRANT = filename_gid;
m.VMVO_NAME_LENGTH = filename_len;
m.VMVO_FLAGS = flags;
m.VMVO_MODE = mode;
m.VMVO_ENDPOINT = for_who->vm_endpoint;
if((r=asynsend(VFS_PROC_NR, &m)) != OK) {
vm_panic("vfs_open: asynsend failed", r);
}
return r;
}
/*===========================================================================*
* vfs_close *
*===========================================================================*/
PUBLIC int vfs_close(struct vmproc *for_who, callback_t callback, int fd)
{
static message m;
int r;
register_callback(for_who, callback, VM_VFS_REPLY_CLOSE);
m.m_type = VM_VFS_CLOSE;
m.VMVC_ENDPOINT = for_who->vm_endpoint;
m.VMVC_FD = fd;
if((r=asynsend(VFS_PROC_NR, &m)) != OK) {
vm_panic("vfs_close: asynsend failed", r);
}
return r;
}
/*===========================================================================*
* do_vfs_reply *
*===========================================================================*/
PUBLIC int do_vfs_reply(message *m)
{
/* Reply to a request has been received from vfs. Handle it. First verify
* and look up which process, identified by endpoint, this is about.
* Then call the callback function that was registered when the request
* was done. Return result to vfs.
*/
endpoint_t ep;
struct vmproc *vmp;
int procno;
callback_t cb;
ep = m->VMV_ENDPOINT;
if(vm_isokendpt(ep, &procno) != OK) {
printf("VM:do_vfs_reply: reply %d about invalid endpoint %d\n",
m->m_type, ep);
vm_panic("do_vfs_reply: invalid endpoint from vfs", NO_NUM);
}
vmp = &vmproc[procno];
if(!vmp->vm_callback) {
printf("VM:do_vfs_reply: reply %d: endpoint %d not waiting\n",
m->m_type, ep);
vm_panic("do_vfs_reply: invalid endpoint from vfs", NO_NUM);
}
if(vmp->vm_callback_type != m->m_type) {
printf("VM:do_vfs_reply: reply %d unexpected for endpoint %d\n"
" (expecting %d)\n", m->m_type, ep, vmp->vm_callback_type);
vm_panic("do_vfs_reply: invalid reply from vfs", NO_NUM);
}
if(vmp->vm_flags & VMF_EXITING) {
/* This is not fatal or impossible, but the callback
* function has to realize it shouldn't do any PM or
* VFS calls for this process.
*/
printf("VM:do_vfs_reply: reply %d for EXITING endpoint %d\n",
m->m_type, ep);
}
/* All desired callback state has been used, so save and reset
* the callback. This allows the callback to register another
* one.
*/
cb = vmp->vm_callback;
vmp->vm_callback = NULL;
cb(vmp, m);
return SUSPEND;
}

33
servers/vm/vm.h Normal file
View File

@@ -0,0 +1,33 @@
#define NO_MEM ((phys_clicks) 0) /* returned by alloc_mem() with mem is up */
/* Memory flags to pt_allocmap() and alloc_mem(). */
#define PAF_CLEAR 0x01 /* Clear physical memory. */
#define PAF_CONTIG 0x02 /* Physically contiguous. */
/* special value for v in pt_allocmap */
#define AM_AUTO ((u32_t) -1)
#define CLICK2ABS(v) ((v) << CLICK_SHIFT)
#define ABS2CLICK(a) ((a) >> CLICK_SHIFT)
/* Compile in asserts and custom sanity checks at all? */
#define SANITYCHECKS 0
#define VMSTATS 1
/* If so, this level: */
#define SCL_NONE 0 /* No sanity checks - vm_assert()s only. */
#define SCL_TOP 1 /* Main loop and other high-level places. */
#define SCL_FUNCTIONS 2 /* Function entry/exit. */
#define SCL_DETAIL 3 /* Detailled steps. */
#define SCL_MAX 3 /* Highest value. */
/* Type of page allocations. */
#define VMP_SPARE 0
#define VMP_PAGETABLE 1
#define VMP_PAGEDIR 2
#define VMP_SLAB 3
#define VMP_CATEGORIES 4
/* Flags to pt_writemap(). */
#define WMF_OVERWRITE 0x01 /* Caller knows map may overwrite. */

59
servers/vm/vmproc.h Normal file
View File

@@ -0,0 +1,59 @@
#ifndef _VMPROC_H
#define _VMPROC_H 1
#include <pagetable.h>
#include <arch_vmproc.h>
#include "vm.h"
struct vmproc;
typedef void (*callback_t)(struct vmproc *who, message *m);
struct vmproc {
struct vm_arch vm_arch; /* architecture-specific data */
int vm_flags;
endpoint_t vm_endpoint;
pt_t vm_pt; /* page table data, if VMF_HASPT is set */
vir_bytes vm_stacktop; /* top of stack as seen from process */
vir_bytes vm_offset; /* offset of addr 0 for process */
/* File identification for cs sharing. */
ino_t vm_ino; /* inode number of file */
dev_t vm_dev; /* device number of file system */
time_t vm_ctime; /* inode changed time */
/* Regions in virtual address space. */
struct vir_region *vm_regions;
int vm_count;
/* Heap for brk() to extend. */
struct vir_region *vm_heap;
/* State for requests pending to be done to vfs on behalf of
* this process.
*/
callback_t vm_callback; /* function to call on vfs reply */
int vm_callback_type; /* expected message type */
union {
struct {
cp_grant_id_t gid;
} open; /* VM_VFS_OPEN */
} vm_state; /* Callback state. */
#if VMSTATS
int vm_bytecopies;
#endif
};
/* Bits for vm_flags */
#define VMF_INUSE 0x001 /* slot contains a process */
#define VMF_SEPARATE 0x002 /* separate i&d */
#define VMF_HASPT 0x004 /* has private page table */
#define VMF_EXITING 0x008 /* PM is cleaning up this process */
#define VMF_HAS_DMA 0x010 /* Process directly or indirectly granted
* DMA buffers.
*/
#endif