Basic VM and other minor improvements.
Not complete, probably not fully debugged or optimized.
This commit is contained in:
33
servers/vm/Makefile
Normal file
33
servers/vm/Makefile
Normal 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
832
servers/vm/alloc.c
Normal 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
67
servers/vm/asynsend.c
Normal 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
179
servers/vm/break.c
Normal 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
413
servers/vm/exec.c
Normal 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
118
servers/vm/exit.c
Normal 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
145
servers/vm/fork.c
Normal 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
29
servers/vm/glo.h
Normal 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
19
servers/vm/i386/Makefile
Normal 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
|
||||
|
||||
38
servers/vm/i386/arch_pagefaults.c
Normal file
38
servers/vm/i386/arch_pagefaults.c
Normal 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);
|
||||
}
|
||||
|
||||
15
servers/vm/i386/arch_vmproc.h
Normal file
15
servers/vm/i386/arch_vmproc.h
Normal 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
25
servers/vm/i386/memory.h
Normal 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)
|
||||
|
||||
13
servers/vm/i386/pagefaults.h
Normal file
13
servers/vm/i386/pagefaults.h
Normal 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
944
servers/vm/i386/pagetable.c
Normal 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);
|
||||
}
|
||||
37
servers/vm/i386/pagetable.h
Normal file
37
servers/vm/i386/pagetable.h
Normal 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
23
servers/vm/i386/util.s
Normal 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
115
servers/vm/i386/vm.c
Normal 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
274
servers/vm/main.c
Normal 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
161
servers/vm/mmap.c
Normal 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
175
servers/vm/pagefaults.c
Normal 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
139
servers/vm/proto.h
Normal 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
929
servers/vm/region.c
Normal 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
46
servers/vm/region.h
Normal 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
46
servers/vm/sanitycheck.h
Normal 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
64
servers/vm/signal.c
Normal 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
399
servers/vm/slaballoc.c
Normal 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
28
servers/vm/util.h
Normal 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
165
servers/vm/utility.c
Normal 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
140
servers/vm/vfs.c
Normal 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
33
servers/vm/vm.h
Normal 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
59
servers/vm/vmproc.h
Normal 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
|
||||
Reference in New Issue
Block a user