- pages that points to page directory values of all processes,

shared with the kernel, mapped into kernel address space; 
   kernel is notified of its location. kernel segment size is
   increased to make it fit.
 - map in kernel and other processes that don't have their
   own page table using single 4MB (global) mapping.
 - new sanity check facility: objects that are allocated with
   the slab allocator are, when running with sanity checking on,
   marked readonly until they are explicitly unlocked using the USE()
   macro.
 - another sanity check facility: collect all uses of memory and
   see if they don't overlap with (a) eachother and (b) free memory
 - own munmap() and munmap_text() functions.
 - exec() recovers from out-of-memory conditions properly now; this
   solves some weird exec() behaviour
 - chew off memory from the same side of the chunk as where we
   start scanning, solving some memory fragmentation issues
 - use avl trees for freelist and phys_ranges in regions
 - implement most useful part of munmap()
 - remap() stuff is GQ's for shared memory
This commit is contained in:
Ben Gras 2009-09-21 14:49:49 +00:00
parent f5459e38db
commit 32fbbd370c
36 changed files with 3607 additions and 997 deletions

View file

@ -4,7 +4,8 @@ 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
signal.o vfs.o mmap.o slaballoc.o region.o pagefaults.o addravl.o \
physravl.o rs.o queryexit.o
ARCHOBJ = $(ARCH)/vm.o $(ARCH)/pagetable.o $(ARCH)/arch_pagefaults.o $(ARCH)/util.o
CPPFLAGS=-I../../kernel/arch/$(ARCH)/include -I$(ARCH)
@ -13,7 +14,7 @@ CFLAGS = $(CPROFILE) $(CPPFLAGS)
# build local binary
all build install: $(SERVER)
#install $(SERVER)
install -S 100k $(SERVER)
$(SERVER): $(OBJ) phony
cd $(ARCH) && $(MAKE)

8
servers/vm/addravl.c Normal file
View file

@ -0,0 +1,8 @@
#include "sanitycheck.h"
#include "pagerange.h"
#include "addravl.h"
#include "proto.h"
#include "util.h"
#include "cavl_impl.h"

24
servers/vm/addravl.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef ADDRAVL
#define ADDRAVL 1
#define AVL_UNIQUE(id) addr_ ## id
#define AVL_HANDLE pagerange_t *
#define AVL_KEY phys_bytes
#define AVL_MAX_DEPTH 30 /* good for 2 million nodes */
#define AVL_NULL NULL
#define AVL_GET_LESS(h, a) (h)->less
#define AVL_GET_GREATER(h, a) (h)->greater
#define AVL_SET_LESS(h1, h2) USE((h1), (h1)->less = h2;);
#define AVL_SET_GREATER(h1, h2) USE((h1), (h1)->greater = h2;);
#define AVL_GET_BALANCE_FACTOR(h) (h)->factor
#define AVL_SET_BALANCE_FACTOR(h, f) USE((h), (h)->factor = f;);
#define AVL_SET_ROOT(h, v) (h)->root = v;
#define AVL_COMPARE_KEY_KEY(k1, k2) ((k1) > (k2) ? 1 : ((k1) < (k2) ? -1 : 0))
#define AVL_COMPARE_KEY_NODE(k, h) AVL_COMPARE_KEY_KEY((k), (h)->addr)
#define AVL_COMPARE_NODE_NODE(h1, h2) AVL_COMPARE_KEY_KEY((h1)->addr, (h2)->addr)
#define AVL_INSIDE_STRUCT char pad[4];
#include "cavl_if.h"
#endif

View file

@ -23,6 +23,8 @@
#include <minix/const.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/debug.h>
#include <minix/bitmap.h>
#include <sys/mman.h>
@ -36,9 +38,12 @@
#include "proto.h"
#include "util.h"
#include "glo.h"
#include "pagerange.h"
#include "addravl.h"
#include "sanitycheck.h"
/* Initially, no free pages are known. */
PRIVATE phys_bytes free_pages_head = NO_MEM; /* Physical address in bytes. */
/* AVL tree of free pages. */
addr_avl addravl;
/* Used for sanity check. */
PRIVATE phys_bytes mem_low, mem_high;
@ -54,6 +59,8 @@ struct hole {
int holelist;
};
static int startpages;
#define NIL_HOLE (struct hole *) 0
#define _NR_HOLES (_NR_PROCS*2) /* No. of memory holes maintained by VM */
@ -71,6 +78,11 @@ 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__)
#define MAXPAGES (1024*1024*1024/VM_PAGE_SIZE) /* 1GB of memory */
#define CHUNKS BITMAP_CHUNKS(MAXPAGES)
PRIVATE bitchunk_t pagemap[CHUNKS];
#else
#define CHECKHOLES
#endif
@ -102,26 +114,6 @@ FORWARD _PROTOTYPE( void holes_sanity_f, (char *fn, int line) );
}
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
/*===========================================================================*
@ -400,6 +392,7 @@ struct memory *chunks; /* list of free memory chunks */
*/
int i, first = 0;
register struct hole *hp;
int nodes, largest;
/* Put all holes on the free list. */
for (hp = &hole[0]; hp < &hole[_NR_HOLES]; hp++) {
@ -410,6 +403,8 @@ struct memory *chunks; /* list of free memory chunks */
hole_head = NIL_HOLE;
free_slots = &hole[0];
addr_init(&addravl);
/* Use the chunks of physical memory to allocate holes. */
for (i=NR_MEMS-1; i>=0; i--) {
if (chunks[i].size > 0) {
@ -422,217 +417,226 @@ struct memory *chunks; /* list of free memory chunks */
}
}
memstats(&nodes, &startpages, &largest);
printf("VM: %d nodes, %d pages, largest chunk %d\n",
nodes, startpages, largest);
CHECKHOLES;
}
#if SANITYCHECKS
PRIVATE void sanitycheck(void)
{
pagerange_t *p, *prevp = NULL;
addr_iter iter;
addr_start_iter_least(&addravl, &iter);
while((p=addr_get_iter(&iter))) {
SLABSANE(p);
vm_assert(p->size > 0);
if(prevp) {
vm_assert(prevp->addr < p->addr);
vm_assert(prevp->addr + p->addr < p->addr);
}
addr_incr_iter(&iter);
}
}
#endif
PUBLIC void memstats(int *nodes, int *pages, int *largest)
{
pagerange_t *p, *prevp = NULL;
addr_iter iter;
addr_start_iter_least(&addravl, &iter);
*nodes = 0;
*pages = 0;
*largest = 0;
#if SANITYCHECKS
sanitycheck();
#endif
while((p=addr_get_iter(&iter))) {
SLABSANE(p);
(*nodes)++;
(*pages)+= p->size;
if(p->size > *largest)
*largest = p->size;
addr_incr_iter(&iter);
}
}
/*===========================================================================*
* alloc_pages *
*===========================================================================*/
PRIVATE PUBLIC phys_bytes alloc_pages(int pages, int memflags)
{
phys_bytes bytes, p, nextp, prevp = NO_MEM;
phys_bytes prevsize = 0;
addr_iter iter;
pagerange_t *pr;
int incr;
phys_bytes boundary16 = 16 * 1024 * 1024 / VM_PAGE_SIZE;
phys_bytes boundary1 = 1 * 1024 * 1024 / VM_PAGE_SIZE;
phys_bytes mem;
#if SANITYCHECKS
vir_bytes avail1, avail2, chunks1, chunks2;
availbytes(&avail1, &chunks1);
int firstnodes, firstpages, wantnodes, wantpages;
int finalnodes, finalpages;
int largest;
memstats(&firstnodes, &firstpages, &largest);
sanitycheck();
wantnodes = firstnodes;
wantpages = firstpages - pages;
#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);
#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;
if(memflags & (PAF_LOWER16MB|PAF_LOWER1MB)) {
addr_start_iter_least(&addravl, &iter);
incr = 1;
} else {
addr_start_iter_greatest(&addravl, &iter);
incr = 0;
}
return NO_MEM;
while((pr = addr_get_iter(&iter))) {
SLABSANE(pr);
if(pr->size >= pages) {
if(memflags & PAF_LOWER16MB) {
if(pr->addr + pages > boundary16)
return NO_MEM;
}
if(memflags & PAF_LOWER1MB) {
if(pr->addr + pages > boundary1)
return NO_MEM;
}
/* good block found! */
break;
}
if(incr)
addr_incr_iter(&iter);
else
addr_decr_iter(&iter);
}
if(!pr) {
printf("VM: alloc_pages: alloc failed of %d pages\n", pages);
util_stacktrace();
printmemstats();
#if SANITYCHECKS
if(largest >= pages) {
vm_panic("no memory but largest was enough", NO_NUM);
}
#endif
return NO_MEM;
}
SLABSANE(pr);
/* Allocated chunk is off the end. */
mem = pr->addr + pr->size - pages;
vm_assert(pr->size >= pages);
if(pr->size == pages) {
pagerange_t *prr;
prr = addr_remove(&addravl, pr->addr);
vm_assert(prr);
vm_assert(prr == pr);
SLABFREE(pr);
#if SANITYCHECKS
wantnodes--;
#endif
} else {
USE(pr, pr->size -= pages;);
}
if(memflags & PAF_CLEAR) {
int s;
if ((s= sys_memset(0, CLICK_SIZE*mem,
VM_PAGE_SIZE*pages)) != OK)
vm_panic("alloc_mem: sys_memset failed", s);
}
#if SANITYCHECKS
memstats(&finalnodes, &finalpages, &largest);
sanitycheck();
vm_assert(finalnodes == wantnodes);
vm_assert(finalpages == wantpages);
#endif
return mem;
}
/*===========================================================================*
* free_pages *
*===========================================================================*/
PRIVATE PUBLIC void free_pages(phys_bytes pageno, int npages)
PRIVATE void free_pages(phys_bytes pageno, int npages)
{
phys_bytes p, origsize,
size, nextaddr, thissize, prevp = NO_MEM, pageaddr;
pagerange_t *pr, *p;
addr_iter iter;
#if SANITYCHECKS
vir_bytes avail1, avail2, chunks1, chunks2;
availbytes(&avail1, &chunks1);
int firstnodes, firstpages, wantnodes, wantpages;
int finalnodes, finalpages, largest;
memstats(&firstnodes, &firstpages, &largest);
sanitycheck();
wantnodes = firstnodes;
wantpages = firstpages + npages;
#endif
#if SANITYCHECKS
#define FREERETURNCHECK \
availbytes(&avail2, &chunks2); \
vm_assert(avail1 + origsize == avail2); \
vm_assert(chunks1 == chunks2 || chunks1+1 == chunks2 || chunks1-1 == chunks2);
#else
#define FREERETURNCHECK
#endif
vm_assert(!addr_search(&addravl, pageno, AVL_EQUAL));
/* 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;
/* try to merge with higher neighbour */
if((pr=addr_search(&addravl, pageno+npages, AVL_EQUAL))) {
USE(pr, pr->addr -= npages;
pr->size += npages;);
} else {
SET_PARAMS(prevp, thissize, pageaddr);
if(!SLABALLOC(pr))
vm_panic("alloc_pages: can't alloc", NO_NUM);
#if SANITYCHECKS
memstats(&firstnodes, &firstpages, &largest);
wantnodes = firstnodes;
wantpages = firstpages + npages;
sanitycheck();
#endif
vm_assert(npages > 0);
USE(pr, pr->addr = pageno;
pr->size = npages;);
addr_insert(&addravl, pr);
#if SANITYCHECKS
wantnodes++;
#endif
}
/* The block has been inserted (and possibly merged with the
* first one). Check if it has to be merged with the second one.
*/
addr_start_iter(&addravl, &iter, pr->addr, AVL_EQUAL);
p = addr_get_iter(&iter);
vm_assert(p);
vm_assert(p == pr);
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;
addr_decr_iter(&iter);
if((p = addr_get_iter(&iter))) {
SLABSANE(p);
if(p->addr + p->size == pr->addr) {
USE(p, p->size += pr->size;);
addr_remove(&addravl, pr->addr);
SLABFREE(pr);
#if SANITYCHECKS
wantnodes--;
#endif
}
}
return;
#if SANITYCHECKS
memstats(&finalnodes, &finalpages, &largest);
sanitycheck();
vm_assert(finalnodes == wantnodes);
vm_assert(finalpages == wantpages);
#endif
}
#define NR_DMA 16
PRIVATE struct dmatab
@ -850,3 +854,65 @@ PUBLIC int do_allocmem(message *m)
return OK;
}
/*===========================================================================*
* do_allocmem *
*===========================================================================*/
void printmemstats(void)
{
int nodes, pages, largest;
memstats(&nodes, &pages, &largest);
printf("%d blocks, %d pages (%ukB) free, largest %d pages (%ukB)\n",
nodes, pages, (u32_t) pages * (VM_PAGE_SIZE/1024),
largest, (u32_t) largest * (VM_PAGE_SIZE/1024));
}
#if SANITYCHECKS
/*===========================================================================*
* usedpages_reset *
*===========================================================================*/
void usedpages_reset(void)
{
memset(pagemap, 0, sizeof(pagemap));
}
/*===========================================================================*
* usedpages_add *
*===========================================================================*/
int usedpages_add_f(phys_bytes addr, phys_bytes len, char *file, int line)
{
pagerange_t *pr;
u32_t pagestart, pages;
if(!incheck)
return OK;
vm_assert(!(addr % VM_PAGE_SIZE));
vm_assert(!(len % VM_PAGE_SIZE));
vm_assert(len > 0);
vm_assert_range(addr, len);
pagestart = addr / VM_PAGE_SIZE;
pages = len / VM_PAGE_SIZE;
while(pages > 0) {
phys_bytes thisaddr;
vm_assert(pagestart > 0);
vm_assert(pagestart < MAXPAGES);
thisaddr = pagestart * VM_PAGE_SIZE;
if(GET_BIT(pagemap, pagestart)) {
int i;
printf("%s:%d: usedpages_add: addr 0x%lx reused.\n",
file, line, thisaddr);
return EFAULT;
}
SET_BIT(pagemap, pagestart);
pages--;
pagestart++;
}
return OK;
}
#endif

View file

@ -28,6 +28,7 @@
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/bitmap.h>
#include <errno.h>
#include <env.h>

216
servers/vm/cavl_if.h Executable file
View file

@ -0,0 +1,216 @@
/* Abstract AVL Tree Generic C Package.
** Interface generation header file.
**
** This code is in the public domain. See cavl_tree.html for interface
** documentation.
**
** Version: 1.5 Author: Walt Karas
*/
/* This header contains the definition of CHAR_BIT (number of bits in a
** char). */
#include <limits.h>
#undef L__
#undef L__EST_LONG_BIT
#undef L__SIZE
#undef L__SC
#undef L__LONG_BIT
#undef L__BIT_ARR_DEFN
#ifndef AVL_SEARCH_TYPE_DEFINED_
#define AVL_SEARCH_TYPE_DEFINED_
typedef enum
{
AVL_EQUAL = 1,
AVL_LESS = 2,
AVL_GREATER = 4,
AVL_LESS_EQUAL = AVL_EQUAL | AVL_LESS,
AVL_GREATER_EQUAL = AVL_EQUAL | AVL_GREATER
}
avl_search_type;
#endif
#ifdef AVL_UNIQUE
#define L__ AVL_UNIQUE
#else
#define L__(X) X
#endif
/* Determine storage class for function prototypes. */
#ifdef AVL_PRIVATE
#define L__SC static
#else
#define L__SC extern
#endif
#ifdef AVL_SIZE
#define L__SIZE AVL_SIZE
#else
#define L__SIZE unsigned long
#endif
typedef struct
{
#ifdef AVL_INSIDE_STRUCT
AVL_INSIDE_STRUCT
#endif
AVL_HANDLE root;
}
L__(avl);
/* Function prototypes. */
L__SC void L__(init)(L__(avl) *tree);
L__SC int L__(is_empty)(L__(avl) *tree);
L__SC AVL_HANDLE L__(insert)(L__(avl) *tree, AVL_HANDLE h);
L__SC AVL_HANDLE L__(search)(L__(avl) *tree, AVL_KEY k, avl_search_type st);
L__SC AVL_HANDLE L__(search_least)(L__(avl) *tree);
L__SC AVL_HANDLE L__(search_greatest)(L__(avl) *tree);
L__SC AVL_HANDLE L__(remove)(L__(avl) *tree, AVL_KEY k);
L__SC AVL_HANDLE L__(subst)(L__(avl) *tree, AVL_HANDLE new_node);
#ifdef AVL_BUILD_ITER_TYPE
L__SC int L__(build)(
L__(avl) *tree, AVL_BUILD_ITER_TYPE p, L__SIZE num_nodes);
#endif
/* ANSI C/ISO C++ require that a long have at least 32 bits. Set
** L__EST_LONG_BIT to be the greatest multiple of 8 in the range
** 32 - 64 (inclusive) that is less than or equal to the number of
** bits in a long.
*/
#if (((LONG_MAX >> 31) >> 7) == 0)
#define L__EST_LONG_BIT 32
#elif (((LONG_MAX >> 31) >> 15) == 0)
#define L__EST_LONG_BIT 40
#elif (((LONG_MAX >> 31) >> 23) == 0)
#define L__EST_LONG_BIT 48
#elif (((LONG_MAX >> 31) >> 31) == 0)
#define L__EST_LONG_BIT 56
#else
#define L__EST_LONG_BIT 64
#endif
/* Number of bits in a long. */
#define L__LONG_BIT (sizeof(long) * CHAR_BIT)
/* The macro L__BIT_ARR_DEFN defines a bit array whose index is a (0-based)
** node depth. The definition depends on whether the maximum depth is more
** or less than the number of bits in a single long.
*/
#if ((AVL_MAX_DEPTH) > L__EST_LONG_BIT)
/* Maximum depth may be more than number of bits in a long. */
#define L__BIT_ARR_DEFN(NAME) \
unsigned long NAME[((AVL_MAX_DEPTH) + L__LONG_BIT - 1) / L__LONG_BIT];
#else
/* Maximum depth is definitely less than number of bits in a long. */
#define L__BIT_ARR_DEFN(NAME) unsigned long NAME;
#endif
/* Iterator structure. */
typedef struct
{
/* Tree being iterated over. */
L__(avl) *tree_;
/* Records a path into the tree. If bit n is true, indicates
** take greater branch from the nth node in the path, otherwise
** take the less branch. bit 0 gives branch from root, and
** so on. */
L__BIT_ARR_DEFN(branch)
/* Zero-based depth of path into tree. */
unsigned depth;
/* Handles of nodes in path from root to current node (returned by *). */
AVL_HANDLE path_h[(AVL_MAX_DEPTH) - 1];
}
L__(iter);
/* Iterator function prototypes. */
L__SC void L__(start_iter)(
L__(avl) *tree, L__(iter) *iter, AVL_KEY k, avl_search_type st);
L__SC void L__(start_iter_least)(L__(avl) *tree, L__(iter) *iter);
L__SC void L__(start_iter_greatest)(L__(avl) *tree, L__(iter) *iter);
L__SC AVL_HANDLE L__(get_iter)(L__(iter) *iter);
L__SC void L__(incr_iter)(L__(iter) *iter);
L__SC void L__(decr_iter)(L__(iter) *iter);
L__SC void L__(init_iter)(L__(iter) *iter);
#define AVL_IMPL_INIT 1
#define AVL_IMPL_IS_EMPTY (1 << 1)
#define AVL_IMPL_INSERT (1 << 2)
#define AVL_IMPL_SEARCH (1 << 3)
#define AVL_IMPL_SEARCH_LEAST (1 << 4)
#define AVL_IMPL_SEARCH_GREATEST (1 << 5)
#define AVL_IMPL_REMOVE (1 << 6)
#define AVL_IMPL_BUILD (1 << 7)
#define AVL_IMPL_START_ITER (1 << 8)
#define AVL_IMPL_START_ITER_LEAST (1 << 9)
#define AVL_IMPL_START_ITER_GREATEST (1 << 10)
#define AVL_IMPL_GET_ITER (1 << 11)
#define AVL_IMPL_INCR_ITER (1 << 12)
#define AVL_IMPL_DECR_ITER (1 << 13)
#define AVL_IMPL_INIT_ITER (1 << 14)
#define AVL_IMPL_SUBST (1 << 15)
#define AVL_IMPL_ALL (~0)
#undef L__
#undef L__EST_LONG_BIT
#undef L__SIZE
#undef L__SC
#undef L__LONG_BIT
#undef L__BIT_ARR_DEFN

1187
servers/vm/cavl_impl.h Executable file

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,7 @@
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/const.h>
#include <minix/bitmap.h>
#include <errno.h>
#include <assert.h>
@ -31,7 +32,9 @@
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) );
vir_bytes stk_bytes, phys_bytes tot_bytes, vir_bytes *stack_top));
static int failcount;
/*===========================================================================*
* find_share *
@ -78,15 +81,17 @@ PUBLIC int do_exec_newmem(message *msg)
proc_e= msg->VMEN_ENDPOINT;
if (vm_isokendpt(proc_e, &proc_n) != OK)
{
printf("VM:exec_newmem: bad endpoint %d from %d\n",
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;
NOTRUNNABLE(vmp->vm_endpoint);
if(msg->VMEN_ARGSSIZE != sizeof(args)) {
printf("VM:exec_newmem: args size %d != %ld\n",
printf("VM: exec_newmem: args size %d != %ld\n",
msg->VMEN_ARGSSIZE, sizeof(args));
return EINVAL;
}
@ -97,18 +102,30 @@ SANITYCHECK(SCL_DETAIL);
if (r != OK)
vm_panic("exec_newmem: sys_datacopy failed", r);
/* Minimum stack region (not preallocated)
* Stopgap for better rlimit-based stack size system
*/
if(args.tot_bytes < MINSTACKREGION) {
args.tot_bytes = MINSTACKREGION;
}
/* 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 */
if (dc >= totc) {
printf("VM: newmem: no stack?\n");
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)
if (r != OK) {
printf("VM: newmem: no virtual space?\n");
return r;
}
/* Can the process' text be shared with that of one already running? */
if(!vm_paged) {
@ -121,29 +138,30 @@ SANITYCHECK(SCL_DETAIL);
* 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);
args.bss_bytes, args.args_bytes, args.tot_bytes, &stack_top);
if (r != OK) {
printf("VM: newmem: new_mem failed\n");
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;
NOTRUNNABLE(vmp->vm_endpoint);
return OK;
}
@ -151,7 +169,7 @@ SANITYCHECK(SCL_DETAIL);
* new_mem *
*===========================================================================*/
PRIVATE int new_mem(rmp, sh_mp, text_bytes, data_bytes,
bss_bytes,stk_bytes,tot_bytes)
bss_bytes,stk_bytes,tot_bytes,stack_top)
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 */
@ -159,6 +177,7 @@ 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 */
vir_bytes *stack_top; /* top of process stack */
{
/* 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.
@ -166,10 +185,15 @@ phys_bytes tot_bytes; /* total memory to allocate, including gap */
vir_clicks text_clicks, data_clicks, gap_clicks, stack_clicks, tot_clicks;
phys_bytes bytes, base, bss_offset;
int s, r2;
int s, r2, r, hadpt = 0;
struct vmproc *vmpold = &vmproc[VMP_EXECTMP];
SANITYCHECK(SCL_FUNCTIONS);
if(rmp->vm_flags & VMF_HASPT) {
hadpt = 1;
}
/* No need to allocate text if it can be shared. */
if (sh_mp != NULL) {
text_bytes = 0;
@ -185,19 +209,31 @@ phys_bytes tot_bytes; /* total memory to allocate, including gap */
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);
if ( (int) gap_clicks < 0) {
printf("VM: new_mem: no gap?\n");
return(ENOMEM);
}
/* Keep previous process state for recovery; the sanity check functions
* know about the 'vmpold' slot, so the memory that the exec()ing
* process is still holding is referenced there.
*
* Throw away the old page table to avoid having two process slots
* using the same vm_pt.
* Just recreate it in the case that we have to revert.
*/
SANITYCHECK(SCL_DETAIL);
if(hadpt) {
pt_free(&rmp->vm_pt);
rmp->vm_flags &= ~VMF_HASPT;
}
vm_assert(!(vmpold->vm_flags & VMF_INUSE));
*vmpold = *rmp; /* copy current state. */
rmp->vm_regions = NULL; /* exec()ing process regions thrown out. */
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(!hadpt) {
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);
@ -210,17 +246,20 @@ SANITYCHECK(SCL_DETAIL);
- 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.
/* Build new process in current slot, without freeing old
* one. If it fails, revert.
*/
if(vm_paged) {
if(pt_new(&rmp->vm_pt) != OK)
vm_panic("exec_newmem: no new pagetable", NO_NUM);
int ptok = 1;
SANITYCHECK(SCL_DETAIL);
if((r=pt_new(&rmp->vm_pt)) != OK) {
ptok = 0;
printf("exec_newmem: no new pagetable\n");
}
SANITYCHECK(SCL_DETAIL);
proc_new(rmp,
if(r != OK || (r=proc_new(rmp,
VM_PROCSTART, /* where to start the process in the page table */
CLICK2ABS(text_clicks),/* how big is the text in bytes, page-aligned */
CLICK2ABS(data_clicks),/* how big is data+bss, page-aligned */
@ -228,13 +267,48 @@ SANITYCHECK(SCL_DETAIL);
CLICK2ABS(gap_clicks), /* how big is gap, page-aligned */
0,0, /* not preallocated */
VM_STACKTOP /* regular stack top */
);
)) != OK) {
SANITYCHECK(SCL_DETAIL);
printf("VM: new_mem: failed\n");
if(ptok) {
pt_free(&rmp->vm_pt);
}
*rmp = *vmpold; /* undo. */
clear_proc(vmpold); /* disappear. */
SANITYCHECK(SCL_DETAIL);
if(hadpt) {
if(pt_new(&rmp->vm_pt) != OK) {
/* We secretly know that making a new pagetable
* in the same slot if one was there will never fail.
*/
vm_panic("new_mem: pt_new failed", s);
}
rmp->vm_flags |= VMF_HASPT;
SANITYCHECK(SCL_DETAIL);
if(map_writept(rmp) != OK) {
printf("VM: warning: exec undo failed\n");
}
SANITYCHECK(SCL_DETAIL);
}
return r;
}
SANITYCHECK(SCL_DETAIL);
/* new process is made; free and unreference
* page table and memory still held by exec()ing process.
*/
SANITYCHECK(SCL_DETAIL);
free_proc(vmpold);
clear_proc(vmpold); /* disappear. */
SANITYCHECK(SCL_DETAIL);
*stack_top = VM_STACKTOP;
} else {
phys_clicks new_base;
new_base = ALLOC_MEM(text_clicks + tot_clicks, 0);
if (new_base == NO_MEM) return(ENOMEM);
if (new_base == NO_MEM) {
printf("VM: new_mem: ALLOC_MEM failed\n");
return(ENOMEM);
}
if (sh_mp != NULL) {
/* Share the text segment. */
@ -294,6 +368,8 @@ SANITYCHECK(SCL_DETAIL);
/* Tell kernel this thing has no page table. */
if((s=pt_bind(NULL, rmp)) != OK)
vm_panic("exec_newmem: pt_bind failed", s);
*stack_top= ((vir_bytes)rmp->vm_arch.vm_seg[S].mem_vir << CLICK_SHIFT) +
((vir_bytes)rmp->vm_arch.vm_seg[S].mem_len << CLICK_SHIFT);
}
SANITYCHECK(SCL_FUNCTIONS);
@ -348,13 +424,6 @@ PUBLIC int proc_new(struct vmproc *vmp,
vm_assert(!(data_start % VM_PAGE_SIZE));
vm_assert((!text_start && !data_start) || (text_start && data_start));
#if 0
if(!map_proc_kernel(vmp)) {
printf("VM: exec: map_proc_kernel failed\n");
return ENOMEM;
}
#endif
/* Place text at start of process. */
vmp->vm_arch.vm_seg[T].mem_phys = ABS2CLICK(vstart);
vmp->vm_arch.vm_seg[T].mem_vir = 0;
@ -371,6 +440,8 @@ PUBLIC int proc_new(struct vmproc *vmp,
VR_ANON | VR_WRITABLE, text_start ? 0 : MF_PREALLOC)) {
SANITYCHECK(SCL_DETAIL);
printf("VM: proc_new: map_page_region failed (text)\n");
map_free_proc(vmp);
SANITYCHECK(SCL_DETAIL);
return(ENOMEM);
}
SANITYCHECK(SCL_DETAIL);
@ -385,6 +456,8 @@ PUBLIC int proc_new(struct vmproc *vmp,
data_bytes, data_start ? data_start : MAP_NONE, VR_ANON | VR_WRITABLE,
data_start ? 0 : MF_PREALLOC))) {
printf("VM: exec: map_page_region for data failed\n");
map_free_proc(vmp);
SANITYCHECK(SCL_DETAIL);
return ENOMEM;
}
@ -432,13 +505,8 @@ PUBLIC int proc_new(struct vmproc *vmp,
vmp->vm_flags |= VMF_HASPT;
if((s=sys_newmap(vmp->vm_endpoint, vmp->vm_arch.vm_seg)) != OK) {
if((s=sys_newmap(vmp->vm_endpoint, vmp->vm_arch.vm_seg)) != OK)
vm_panic("sys_newmap (vm) failed", s);
}
/* This is the real stack clicks. */
vmp->vm_arch.vm_seg[S].mem_len = ABS2CLICK(stack_bytes);
if((s=pt_bind(&vmp->vm_pt, vmp)) != OK)
vm_panic("exec_newmem: pt_bind failed", s);

View file

@ -13,6 +13,7 @@
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/bitmap.h>
#include <errno.h>
#include <env.h>
@ -24,8 +25,10 @@
PUBLIC void free_proc(struct vmproc *vmp)
{
vmp->vm_flags &= ~VMF_HASPT;
pt_free(&vmp->vm_pt);
if(vmp->vm_flags & VMF_HASPT) {
vmp->vm_flags &= ~VMF_HASPT;
pt_free(&vmp->vm_pt);
}
map_free_proc(vmp);
vmp->vm_regions = NULL;
#if VMSTATS

View file

@ -13,7 +13,10 @@
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/debug.h>
#include <minix/bitmap.h>
#include <string.h>
#include <errno.h>
#include <env.h>
@ -31,6 +34,8 @@ PUBLIC int do_fork(message *msg)
{
int r, proc, s, childproc, fullvm;
struct vmproc *vmp, *vmc;
pt_t origpt;
vir_bytes msgaddr;
SANITYCHECK(SCL_FUNCTIONS);
@ -49,6 +54,9 @@ PUBLIC int do_fork(message *msg)
vmp = &vmproc[proc]; /* parent */
vmc = &vmproc[childproc]; /* child */
vm_assert(vmc->vm_slot == childproc);
NOTRUNNABLE(vmp->vm_endpoint);
if(vmp->vm_flags & VMF_HAS_DMA) {
printf("VM: %d has DMA memory and may not fork\n", msg->VMF_ENDPOINT);
@ -58,14 +66,20 @@ PUBLIC int do_fork(message *msg)
fullvm = vmp->vm_flags & VMF_HASPT;
/* The child is basically a copy of the parent. */
origpt = vmc->vm_pt;
*vmc = *vmp;
vmc->vm_slot = childproc;
vmc->vm_regions = NULL;
vmc->vm_endpoint = NONE; /* In case someone tries to use it. */
vmc->vm_pt = origpt;
vmc->vm_flags &= ~VMF_HASPT;
#if VMSTATS
vmc->vm_bytecopies = 0;
#endif
SANITYCHECK(SCL_DETAIL);
if(fullvm) {
SANITYCHECK(SCL_DETAIL);
@ -74,6 +88,8 @@ PUBLIC int do_fork(message *msg)
return ENOMEM;
}
vmc->vm_flags |= VMF_HASPT;
SANITYCHECK(SCL_DETAIL);
if(map_proc_copy(vmc, vmp) != OK) {
@ -108,6 +124,7 @@ PUBLIC int do_fork(message *msg)
/* 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;
FIXME("VM uses kernel for abscopy");
s = sys_abscopy(parent_abs, child_abs, prog_bytes);
if (s < 0) vm_panic("do_fork can't copy", s);
@ -124,14 +141,29 @@ PUBLIC int do_fork(message *msg)
/* Only inherit these flags. */
vmc->vm_flags &= (VMF_INUSE|VMF_SEPARATE|VMF_HASPT);
/* inherit the priv call bitmaps */
memcpy(&vmc->vm_call_priv_mask, &vmp->vm_call_priv_mask,
sizeof(vmc->vm_call_priv_mask));
/* 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) {
fullvm ? PFF_VMINHIBIT : 0, &msgaddr)) != OK) {
vm_panic("do_fork can't sys_fork", r);
}
NOTRUNNABLE(vmp->vm_endpoint);
NOTRUNNABLE(vmc->vm_endpoint);
if(fullvm) {
vir_bytes vir;
/* making these messages writable is an optimisation
* and its return value needn't be checked.
*/
vir = arch_vir2map(vmc, msgaddr);
handle_memory(vmc, vir, sizeof(message), 1);
vir = arch_vir2map(vmp, msgaddr);
handle_memory(vmp, vir, sizeof(message), 1);
if((r=pt_bind(&vmc->vm_pt, vmc)) != OK)
vm_panic("fork can't pt_bind", r);
}

View file

@ -12,17 +12,19 @@
#define EXTERN
#endif
EXTERN struct vmproc vmproc[_NR_PROCS+1];
#define VMP_SYSTEM _NR_PROCS
#define VMP_EXECTMP _NR_PROCS+1
#define VMP_NR _NR_PROCS+2
EXTERN struct vmproc vmproc[VMP_NR];
#if SANITYCHECKS
EXTERN int nocheck;
u32_t data1[200];
#define CHECKADDR 0
EXTERN int incheck;
EXTERN long vm_sanitychecklevel;
#endif
#define VMP_SYSTEM _NR_PROCS
/* vm operation mode state and values */
EXTERN long vm_paged;
EXTERN phys_bytes kernel_top_bytes;
EXTERN int meminit_done;

View file

@ -14,6 +14,7 @@
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <minix/bitmap.h>
#include <errno.h>
#include <string.h>

View file

@ -1,5 +1,12 @@
#include <archtypes.h>
#include <minix/config.h>
#include <minix/const.h>
#include <minix/type.h>
#include <minix/com.h>
#include <minix/ipc.h>
#include <minix/safecopies.h>
#include <timers.h>
struct vm_arch {
struct mem_map vm_seg[NR_LOCAL_SEGS]; /* text, data, stack */

View file

@ -15,7 +15,7 @@
#define VM_PAGE_SIZE I386_PAGE_SIZE
/* Where do processes start in linear (i.e. page table) memory? */
#define VM_PROCSTART (I386_BIG_PAGE_SIZE*10)
#define VM_PROCSTART (I386_BIG_PAGE_SIZE*100)
#define CLICKSPERPAGE (I386_PAGE_SIZE/CLICK_SIZE)

View file

@ -17,6 +17,7 @@
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <minix/cpufeature.h>
#include <minix/bitmap.h>
#include <errno.h>
#include <assert.h>
@ -34,14 +35,14 @@
#include "memory.h"
int global_bit_ok = 0;
int bigpage_ok = 0;
/* PDE used to map in kernel, kernel physical address. */
PRIVATE int kernel_pde = -1, pagedir_pde = -1;
PRIVATE u32_t kern_pde_val = 0, global_bit = 0, pagedir_pde_val;
/* 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. */
PRIVATE int proc_pde = 0;
/* 4MB page size available in hardware? */
PRIVATE int bigpage_ok = 0;
/* Our process table entry. */
struct vmproc *vmp = &vmproc[VM_PROC_NR];
@ -52,7 +53,7 @@ struct vmproc *vmp = &vmproc[VM_PROC_NR];
*/
#define SPAREPAGES 5
int missing_spares = SPAREPAGES;
static struct {
PRIVATE struct {
void *page;
u32_t phys;
} sparepages[SPAREPAGES];
@ -78,7 +79,6 @@ static struct {
u32_t page_directories_phys, *page_directories = NULL;
#if SANITYCHECKS
#define PT_SANE(p) { pt_sanitycheck((p), __FILE__, __LINE__); SANITYCHECK(SCL_DETAIL); }
/*===========================================================================*
* pt_sanitycheck *
*===========================================================================*/
@ -86,21 +86,37 @@ PUBLIC void pt_sanitycheck(pt_t *pt, char *file, int line)
{
/* Basic pt sanity check. */
int i;
int slot;
MYASSERT(pt);
MYASSERT(pt->pt_dir);
MYASSERT(pt->pt_dir_phys);
for(i = 0; i < I386_VM_DIR_ENTRIES; i++) {
for(slot = 0; slot < ELEMENTS(vmproc); slot++) {
if(pt == &vmproc[slot].vm_pt)
break;
}
if(slot >= ELEMENTS(vmproc)) {
vm_panic("pt_sanitycheck: passed pt not in any proc", NO_NUM);
}
MYASSERT(usedpages_add(pt->pt_dir_phys, I386_PAGE_SIZE) == OK);
for(i = proc_pde; i < I386_VM_DIR_ENTRIES; i++) {
if(pt->pt_pt[i]) {
if(!(pt->pt_dir[i] & I386_VM_PRESENT)) {
printf("slot %d: pt->pt_pt[%d] = 0x%lx, but pt_dir entry 0x%lx\n",
slot, i, pt->pt_pt[i], pt->pt_dir[i]);
}
MYASSERT(pt->pt_dir[i] & I386_VM_PRESENT);
MYASSERT(usedpages_add(I386_VM_PFA(pt->pt_dir[i]),
I386_PAGE_SIZE) == OK);
} else {
MYASSERT(!(pt->pt_dir[i] & I386_VM_PRESENT));
}
}
}
#else
#define PT_SANE(p)
#endif
/*===========================================================================*
@ -240,7 +256,6 @@ PRIVATE void *vm_getsparepage(u32_t *phys)
return sp;
}
}
vm_panic("VM: out of spare pages", NO_NUM);
return NULL;
}
@ -255,17 +270,16 @@ PRIVATE void *vm_checkspares(void)
for(s = 0; s < SPAREPAGES && missing_spares > 0; s++)
if(!sparepages[s].page) {
n++;
sparepages[s].page = vm_allocpages(&sparepages[s].phys, 1,
VMP_SPARE);
missing_spares--;
vm_assert(missing_spares >= 0 && missing_spares <= SPAREPAGES);
if((sparepages[s].page = vm_allocpages(&sparepages[s].phys, 1,
VMP_SPARE))) {
missing_spares--;
vm_assert(missing_spares >= 0);
vm_assert(missing_spares <= SPAREPAGES);
}
}
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;
}
@ -293,7 +307,7 @@ PUBLIC void *vm_allocpages(phys_bytes *phys, int pages, int reason)
vm_assert(level >= 1);
vm_assert(level <= 2);
if(level > 1 || !(vmp->vm_flags & VMF_HASPT)) {
if(level > 1 || !(vmp->vm_flags & VMF_HASPT) || !meminit_done) {
int r;
void *s;
vm_assert(pages == 1);
@ -336,6 +350,38 @@ PUBLIC void *vm_allocpages(phys_bytes *phys, int pages, int reason)
return (void *) arch_map2vir(vmp, loc);
}
/*===========================================================================*
* vm_pagelock *
*===========================================================================*/
PUBLIC void vm_pagelock(void *vir, int lockflag)
{
/* Mark a page allocated by vm_allocpages() unwritable, i.e. only for VM. */
vir_bytes m;
int r;
u32_t flags = I386_VM_PRESENT | I386_VM_USER;
pt_t *pt;
pt = &vmp->vm_pt;
m = arch_vir2map(vmp, (vir_bytes) vir);
vm_assert(!(m % I386_PAGE_SIZE));
if(!lockflag)
flags |= I386_VM_WRITE;
/* Update flags. */
if((r=pt_writemap(pt, m, 0, I386_PAGE_SIZE,
flags, WMF_OVERWRITE | WMF_WRITEFLAGSONLY)) != OK) {
vm_panic("vm_lockpage: pt_writemap failed\n", NO_NUM);
}
if((r=sys_vmctl(SELF, VMCTL_FLUSHTLB, 0)) != OK) {
vm_panic("VMCTL_FLUSHTLB failed", r);
}
return;
}
/*===========================================================================*
* pt_ptalloc *
*===========================================================================*/
@ -347,14 +393,13 @@ PRIVATE int pt_ptalloc(pt_t *pt, int pde, u32_t flags)
/* Argument must make sense. */
vm_assert(pde >= 0 && pde < I386_VM_DIR_ENTRIES);
vm_assert(!(flags & ~(PTF_ALLFLAGS | PTF_MAPALLOC)));
vm_assert(!(flags & ~(PTF_ALLFLAGS)));
/* 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)))
@ -370,7 +415,6 @@ PRIVATE int pt_ptalloc(pt_t *pt, int pde, u32_t flags)
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;
}
@ -385,10 +429,9 @@ PUBLIC int pt_writemap(pt_t *pt, vir_bytes v, phys_bytes physaddr,
/* Page directory and table entries for this virtual address. */
int p, pages, pde;
int finalpde;
SANITYCHECK(SCL_FUNCTIONS);
vm_assert(!(bytes % I386_PAGE_SIZE));
vm_assert(!(flags & ~(PTF_ALLFLAGS | PTF_MAPALLOC)));
vm_assert(!(flags & ~(PTF_ALLFLAGS)));
pages = bytes / I386_PAGE_SIZE;
@ -405,8 +448,6 @@ PUBLIC int pt_writemap(pt_t *pt, vir_bytes v, phys_bytes physaddr,
}
#endif
PT_SANE(pt);
finalpde = I386_VM_PDE(v + I386_PAGE_SIZE * pages);
/* First make sure all the necessary page tables are allocated,
@ -417,6 +458,8 @@ PUBLIC int pt_writemap(pt_t *pt, vir_bytes v, phys_bytes physaddr,
for(pde = I386_VM_PDE(v); pde <= finalpde; pde++) {
vm_assert(pde >= 0 && pde < I386_VM_DIR_ENTRIES);
if(pt->pt_dir[pde] & I386_VM_BIGPAGE) {
printf("pt_writemap: trying to write 0x%lx into 0x%lx\n",
physaddr, v);
vm_panic("pt_writemap: BIGPAGE found", NO_NUM);
}
if(!(pt->pt_dir[pde] & I386_VM_PRESENT)) {
@ -436,13 +479,10 @@ PUBLIC int pt_writemap(pt_t *pt, vir_bytes v, phys_bytes physaddr,
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);
@ -456,22 +496,25 @@ PUBLIC int pt_writemap(pt_t *pt, vir_bytes v, phys_bytes physaddr,
*/
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
if(writemapflags & WMF_WRITEFLAGSONLY) {
physaddr = pt->pt_pt[pde][pte] & I386_VM_ADDR_MASK;
}
if(writemapflags & WMF_FREE) {
printf("pt_writemap: should free 0x%lx\n", physaddr);
}
/* 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;
}
@ -488,7 +531,14 @@ PUBLIC int pt_new(pt_t *pt)
*/
int i;
if(!(pt->pt_dir = vm_allocpages(&pt->pt_dir_phys, 1, VMP_PAGEDIR))) {
/* Don't ever re-allocate/re-move a certain process slot's
* page directory once it's been created. This is a fraction
* faster, but also avoids having to invalidate the page
* mappings from in-kernel page tables pointing to
* the page directories (the page_directories data).
*/
if(!pt->pt_dir &&
!(pt->pt_dir = vm_allocpages(&pt->pt_dir_phys, 1, VMP_PAGEDIR))) {
return ENOMEM;
}
@ -520,13 +570,14 @@ PUBLIC void pt_init(void)
*/
pt_t *newpt;
int s, r;
vir_bytes v;
vir_bytes v, kpagedir;
phys_bytes lo, hi;
vir_bytes extra_clicks;
u32_t moveup = 0;
global_bit_ok = _cpufeature(_CPUF_I386_PGE);
bigpage_ok = _cpufeature(_CPUF_I386_PSE);
int global_bit_ok = 0;
int free_pde;
int p;
vir_bytes kernlimit;
/* Shorthand. */
newpt = &vmp->vm_pt;
@ -541,12 +592,37 @@ PUBLIC void pt_init(void)
}
missing_spares = 0;
/* 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);
/* global bit and 4MB pages available? */
global_bit_ok = _cpufeature(_CPUF_I386_PGE);
bigpage_ok = _cpufeature(_CPUF_I386_PSE);
/* Set bit for PTE's and PDE's if available. */
if(global_bit_ok)
global_bit = I386_VM_GLOBAL;
/* Figure out kernel pde slot. */
{
int pde1, pde2;
pde1 = I386_VM_PDE(KERNEL_TEXT);
pde2 = I386_VM_PDE(KERNEL_DATA+KERNEL_DATA_LEN);
if(pde1 != pde2)
vm_panic("pt_init: kernel too big", NO_NUM);
/* Map in kernel with this single pde value if 4MB pages
* supported.
*/
kern_pde_val = (KERNEL_TEXT & I386_VM_ADDR_MASK_4MB) |
I386_VM_BIGPAGE|
I386_VM_USER|
I386_VM_PRESENT|I386_VM_WRITE|global_bit;
kernel_pde = pde1;
vm_assert(kernel_pde >= 0);
free_pde = kernel_pde+1;
}
/* First unused pde. */
proc_pde = free_pde;
/* Initial (current) range of our virtual address space. */
lo = CLICK2ABS(vmp->vm_arch.vm_seg[T].mem_phys);
@ -562,21 +638,27 @@ PUBLIC void pt_init(void)
vm_assert(!(lo % I386_PAGE_SIZE));
vm_assert(!(moveup % I386_PAGE_SIZE));
}
/* 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);
/* Old position mapped in? */
pt_check(vmp);
/* Set up mappings for VM process. */
for(v = lo; v < hi; v += I386_PAGE_SIZE) {
phys_bytes addr;
u32_t flags;
/* We have to write the old and new position in the PT,
/* We have to write the new position in the PT,
* so we can move our segments.
*/
if(pt_writemap(newpt, v+moveup, v, I386_PAGE_SIZE,
I386_VM_PRESENT|I386_VM_WRITE|I386_VM_USER, 0) != OK)
vm_panic("pt_init: pt_writemap failed", NO_NUM);
if(pt_writemap(newpt, v, v, I386_PAGE_SIZE,
I386_VM_PRESENT|I386_VM_WRITE|I386_VM_USER, 0) != OK)
vm_panic("pt_init: pt_writemap failed", NO_NUM);
}
/* Move segments up too. */
@ -584,21 +666,14 @@ PUBLIC void pt_init(void)
vmp->vm_arch.vm_seg[D].mem_phys += ABS2CLICK(moveup);
vmp->vm_arch.vm_seg[S].mem_phys += ABS2CLICK(moveup);
#if 0
/* Map in kernel. */
if(pt_mapkernel(newpt) != OK)
vm_panic("pt_init: pt_mapkernel failed", NO_NUM);
/* Allocate us a page table in which to remember page directory
* pointers.
*/
if(!(page_directories = vm_allocpages(&page_directories_phys,
1, VMP_PAGETABLE)))
vm_panic("no virt addr for vm mappings", NO_NUM);
#endif
/* Give our process the new, copied, private page table. */
pt_bind(newpt, vmp);
memset(page_directories, 0, I386_PAGE_SIZE);
/* Increase our hardware data segment to create virtual address
* space above our stack. We want to increase it to VM_DATATOP,
@ -614,19 +689,6 @@ PUBLIC void pt_init(void)
(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;
/* Wipe old mappings from VM. */
for(v = lo; v < hi; v += I386_PAGE_SIZE) {
if(pt_writemap(newpt, v, MAP_NONE, I386_PAGE_SIZE,
0, WMF_OVERWRITE) != OK)
vm_panic("pt_init: pt_writemap failed", NO_NUM);
}
/* Where our free virtual address space starts.
* This is only a hint to the VM system.
*/
@ -635,17 +697,49 @@ PUBLIC void pt_init(void)
/* 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,
arch_vir2map(vmp, 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 *) arch_map2vir(vmp, varmap_loc);
/* Find a PDE below processes available for mapping in the
* page directories (readonly).
*/
pagedir_pde = free_pde++;
pagedir_pde_val = (page_directories_phys & I386_VM_ADDR_MASK) |
I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE;
/* Tell kernel about free pde's. */
while(free_pde*I386_BIG_PAGE_SIZE < VM_PROCSTART) {
if((r=sys_vmctl(SELF, VMCTL_I386_FREEPDE, free_pde++)) != OK) {
vm_panic("VMCTL_I386_FREEPDE failed", r);
}
}
/* first pde in use by process. */
proc_pde = free_pde;
kernlimit = free_pde*I386_BIG_PAGE_SIZE;
/* Increase kernel segment to address this memory. */
if((r=sys_vmctl(SELF, VMCTL_I386_KERNELLIMIT, kernlimit)) != OK) {
vm_panic("VMCTL_I386_KERNELLIMIT failed", r);
}
kpagedir = arch_map2vir(&vmproc[VMP_SYSTEM],
pagedir_pde*I386_BIG_PAGE_SIZE);
/* Tell kernel how to get at the page directories. */
if((r=sys_vmctl(SELF, VMCTL_I386_PAGEDIRS, kpagedir)) != OK) {
vm_panic("VMCTL_I386_KERNELLIMIT failed", r);
}
/* Give our process the new, copied, private page table. */
pt_mapkernel(newpt); /* didn't know about vm_dir pages earlier */
pt_bind(newpt, vmp);
/* Now actually enable paging. */
if(sys_vmctl_enable_paging(vmp->vm_arch.vm_seg) != OK)
vm_panic("pt_init: enable paging failed", NO_NUM);
/* Back to reality - this is where the stack actually is. */
vmp->vm_arch.vm_seg[S].mem_len -= extra_clicks;
/* All OK. */
return;
}
@ -656,24 +750,28 @@ PUBLIC void pt_init(void)
*===========================================================================*/
PUBLIC int pt_bind(pt_t *pt, struct vmproc *who)
{
int slot;
int slot, ispt;
u32_t phys;
/* Basic sanity checks. */
vm_assert(who);
vm_assert(who->vm_flags & VMF_INUSE);
if(pt) PT_SANE(pt);
vm_assert(pt);
#if 0
slot = who->vm_slot;
vm_assert(slot >= 0);
vm_assert(slot < ELEMENTS(vmproc));
vm_assert(!(pt->pt_dir_phys & ~I386_VM_ADDR_MASK));
vm_assert(slot < I386_VM_PT_ENTRIES);
page_directories[slot] = (pt->pt_dir_phys & I386_VM_ADDR_MASK) |
(I386_VM_PRESENT|I386_VM_WRITE);
phys = pt->pt_dir_phys & I386_VM_ADDR_MASK;
vm_assert(pt->pt_dir_phys == phys);
/* Update "page directory pagetable." */
page_directories[slot] = phys | I386_VM_PRESENT|I386_VM_WRITE;
#if 0
printf("VM: slot %d has pde val 0x%lx\n", slot, page_directories[slot]);
#endif
/* Tell kernel about new page table root. */
return sys_vmctl(who->vm_endpoint, VMCTL_I386_SETCR3,
pt ? pt->pt_dir_phys : 0);
@ -687,24 +785,10 @@ 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);
for(i = 0; i < I386_VM_DIR_ENTRIES; i++)
if(pt->pt_pt[i])
vm_freepages((vir_bytes) pt->pt_pt[i],
I386_VM_PFA(pt->pt_dir[i]), 1, VMP_PAGETABLE);
return;
}
@ -715,77 +799,51 @@ PUBLIC void pt_free(pt_t *pt)
PUBLIC int pt_mapkernel(pt_t *pt)
{
int r;
static int pde = -1, do_bigpage = 0;
u32_t global = 0;
static u32_t kern_phys;
static int printed = 0;
if(global_bit_ok) global = I386_VM_GLOBAL;
/* Any i386 page table needs to map in the kernel address space. */
vm_assert(vmproc[VMP_SYSTEM].vm_flags & VMF_INUSE);
if(pde == -1 && bigpage_ok) {
int pde1, pde2;
pde1 = I386_VM_PDE(KERNEL_TEXT);
pde2 = I386_VM_PDE(KERNEL_DATA+KERNEL_DATA_LEN);
if(pde1 != pde2) {
printf("VM: pt_mapkernel: kernel too big?");
bigpage_ok = 0;
} else {
kern_phys = KERNEL_TEXT & I386_VM_ADDR_MASK_4MB;
pde = pde1;
do_bigpage = 1;
vm_assert(pde >= 0);
}
}
if(do_bigpage) {
pt->pt_dir[pde] = kern_phys |
I386_VM_BIGPAGE|I386_VM_PRESENT|I386_VM_WRITE|global;
if(bigpage_ok) {
if(kernel_pde >= 0) {
pt->pt_dir[kernel_pde] = kern_pde_val;
} else
vm_panic("VM: pt_mapkernel: no kernel pde", NO_NUM);
} else {
vm_panic("VM: pt_mapkernel: no bigpage", NO_NUM);
/* Map in text. flags: don't write, supervisor only */
if((r=pt_writemap(pt, KERNEL_TEXT, KERNEL_TEXT, KERNEL_TEXT_LEN,
I386_VM_PRESENT|global, 0)) != OK)
I386_VM_PRESENT|global_bit, 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_WRITE|global, 0)) != OK)
I386_VM_PRESENT|I386_VM_WRITE, 0)) != OK)
return r;
}
if(pagedir_pde >= 0) {
/* Kernel also wants to know about all page directories. */
pt->pt_dir[pagedir_pde] = pagedir_pde_val;
}
return OK;
}
/*===========================================================================*
* pt_freerange *
* pt_check *
*===========================================================================*/
PUBLIC void pt_freerange(pt_t *pt, vir_bytes low, vir_bytes high)
PUBLIC void pt_check(struct vmproc *vmp)
{
/* 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;
}
phys_bytes hi;
hi = CLICK2ABS(vmp->vm_arch.vm_seg[S].mem_phys +
vmp->vm_arch.vm_seg[S].mem_len);
if(hi > (kernel_pde+1) * I386_BIG_PAGE_SIZE) {
printf("VM: %d doesn't fit in kernel range (0x%lx)\n",
vmp->vm_endpoint, hi);
vm_panic("boot time processes too big", NO_NUM);
}
PT_SANE(pt);
return;
}
/*===========================================================================*
@ -796,82 +854,3 @@ PUBLIC void pt_cycle(void)
vm_checkspares();
}
/* In sanity check mode, pages are mapped and unmapped explicitly, so
* unexpected double mappings (overwriting a page table entry) are caught.
* If not sanity checking, simply keep the page mapped in and overwrite
* the mapping entry; we need WMF_OVERWRITE for that in PHYS_MAP though.
*/
#if SANITYCHECKS
#define MAPFLAGS 0
#else
#define MAPFLAGS WMF_OVERWRITE
#endif
static u32_t ismapped = MAP_NONE;
#define PHYS_MAP(a, o) \
{ int r; \
u32_t wantmapped; \
vm_assert(varmap); \
(o) = (a) % I386_PAGE_SIZE; \
wantmapped = (a) - (o); \
if(wantmapped != ismapped || ismapped == MAP_NONE) { \
r = pt_writemap(&vmp->vm_pt, (vir_bytes) varmap_loc, \
wantmapped, I386_PAGE_SIZE, \
I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE, \
MAPFLAGS); \
if(r != OK) \
vm_panic("PHYS_MAP: pt_writemap", NO_NUM); \
ismapped = wantmapped; \
/* pt_bind() flushes TLB. */ \
pt_bind(&vmp->vm_pt, vmp); \
} \
}
#define PHYSMAGIC 0x7b9a0590
#if SANITYCHECKS
#define PHYS_UNMAP if(OK != pt_writemap(&vmp->vm_pt, varmap_loc, MAP_NONE,\
I386_PAGE_SIZE, 0, WMF_OVERWRITE)) { \
vm_panic("PHYS_UNMAP: pt_writemap failed", NO_NUM); }
ismapped = MAP_NONE;
#endif
#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;
PHYS_UNMAP;
#endif
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);
PHYS_UNMAP;
#endif
SANITYCHECK(SCL_DETAIL);
}

View file

@ -5,6 +5,8 @@
#include <stdint.h>
#include <sys/vm_i386.h>
#include "../vm.h"
/* An i386 pagetable. */
typedef struct {
/* Directory entries in VM addr space - root of page table. */
@ -34,5 +36,12 @@ typedef struct {
*/
#define PTF_ALLFLAGS (PTF_WRITE|PTF_PRESENT|PTF_USER|PTF_GLOBAL)
#if SANITYCHECKS
#define PT_SANE(p) { pt_sanitycheck((p), __FILE__, __LINE__); }
#else
#define PT_SANE(p)
#endif
#endif

View file

@ -13,6 +13,7 @@
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/bitmap.h>
#include <sys/mman.h>
@ -25,83 +26,18 @@
#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);
vir_bytes textstart = CLICK2ABS(vmp->vm_arch.vm_seg[T].mem_phys);
vir_bytes datastart = CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys);
vm_assert(bottom <= addr);
/* Could be a text address. */
vm_assert(datastart <= addr || textstart <= addr);
return addr - bottom;
return addr - datastart;
}
/*===========================================================================*
@ -113,3 +49,13 @@ PUBLIC vir_bytes arch_vir2map(struct vmproc *vmp, vir_bytes addr)
return addr + bottom;
}
/*===========================================================================*
* arch_vir2map_text *
*===========================================================================*/
PUBLIC vir_bytes arch_vir2map_text(struct vmproc *vmp, vir_bytes addr)
{
vir_bytes bottom = CLICK2ABS(vmp->vm_arch.vm_seg[T].mem_phys);
return addr + bottom;
}

View file

@ -16,6 +16,8 @@
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/const.h>
#include <minix/bitmap.h>
#include <minix/crtso.h>
#include <errno.h>
#include <string.h>
@ -45,6 +47,7 @@ typedef u32_t mask_t;
#define MAXEPM (ANYEPM-1)
#define EPM(e) ((1L) << ((e)-MINEPM))
#define EPMOK(mask, ep) (((mask) & EPM(ANYEPM)) || ((ep) >= MINEPM && (ep) <= MAXEPM && (EPM(ep) & (mask))))
#define EPMANYOK(mask, ep) ((mask) & EPM(ANYEPM))
/* Table of calls and a macro to test for being in range. */
struct {
@ -76,10 +79,9 @@ PUBLIC int main(void)
int result, who_e;
#if SANITYCHECKS
nocheck = 0;
memcpy(data1, CHECKADDR, sizeof(data1));
incheck = nocheck = 0;
FIXME("VM SANITYCHECKS are on");
#endif
SANITYCHECK(SCL_TOP);
vm_paged = 1;
env_parse("vm_paged", "d", 0, &vm_paged, 0, 1);
@ -87,10 +89,7 @@ PUBLIC int main(void)
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) {
@ -100,9 +99,6 @@ PUBLIC int main(void)
if(missing_spares > 0) {
pt_cycle(); /* pagetable code wants to be called */
}
#if SANITYCHECKS
slabstats();
#endif
SANITYCHECK(SCL_DETAIL);
if ((r=receive(ANY, &msg)) != OK)
@ -114,21 +110,18 @@ PUBLIC int main(void)
switch(msg.m_source) {
case SYSTEM:
/* Kernel wants to have memory ranges
* verified.
* verified, and/or pagefaults handled.
*/
do_memory();
break;
case HARDWARE:
do_pagefaults();
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.
*/
do_pagefaults();
break;
default:
/* No-one else should send us notifies. */
printf("VM: ignoring notify() from %d\n",
@ -147,6 +140,26 @@ PUBLIC int main(void)
printf("VM: restricted call %s from %d instead of 0x%lx\n",
vm_calls[c].vmc_name, msg.m_source,
vm_calls[c].vmc_callers);
} else if (EPMANYOK(vm_calls[c].vmc_callers, who_e) &&
c != VM_MMAP-VM_RQ_BASE &&
c != VM_MUNMAP_TEXT-VM_RQ_BASE &&
c != VM_MUNMAP-VM_RQ_BASE) {
/* check VM acl, we care ANYEPM only,
* and omit other hard-coded permission checks.
*/
int n;
if ((r = vm_isokendpt(who_e, &n)) != OK)
vm_panic("VM: from strange source.", who_e);
if (!GET_BIT(vmproc[n].vm_call_priv_mask, c))
printf("VM: restricted call %s from %d\n",
vm_calls[c].vmc_name, who_e);
else {
SANITYCHECK(SCL_FUNCTIONS);
result = vm_calls[c].vmc_func(&msg);
SANITYCHECK(SCL_FUNCTIONS);
}
} else {
SANITYCHECK(SCL_FUNCTIONS);
result = vm_calls[c].vmc_func(&msg);
@ -171,12 +184,15 @@ PUBLIC int main(void)
return(OK);
}
extern int unmap_ok;
/*===========================================================================*
* vm_init *
*===========================================================================*/
PRIVATE void vm_init(void)
{
int s, i;
int click, clicksforgotten = 0;
struct memory mem_chunks[NR_MEMS];
struct boot_image image[NR_BOOT_PROCS];
struct boot_image *ip;
@ -241,37 +257,12 @@ PRIVATE void vm_init(void)
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);
/* Bits of code need to know where a process can
* start in a pagetable.
*/
kernel_top_bytes = find_kernel_top();
/* Can first kernel pages of code and data be (left) mapped out?
* If so, change the SYSTEM process' memory map to reflect this
* (future mappings of SYSTEM into other processes will not include
* first pages), and free the first pages.
*/
if(vm_paged && sys_vmctl(SELF, VMCTL_NOPAGEZERO, 0) == OK) {
struct vmproc *vmp;
vmp = &vmproc[VMP_SYSTEM];
if(vmp->vm_arch.vm_seg[T].mem_len > 0) {
#define DIFF CLICKSPERPAGE
vmp->vm_arch.vm_seg[T].mem_phys += DIFF;
vmp->vm_arch.vm_seg[T].mem_len -= DIFF;
}
vmp->vm_arch.vm_seg[D].mem_phys += DIFF;
vmp->vm_arch.vm_seg[D].mem_len -= DIFF;
}
meminit_done = 1;
/* Give these processes their own page table. */
for (ip = &image[0]; ip < &image[NR_BOOT_PROCS]; ip++) {
@ -283,14 +274,22 @@ PRIVATE void vm_init(void)
GETVMP(vmp, ip->proc_nr);
if(!(ip->flags & PROC_FULLVM)) {
/* See if this process fits in kernel
* mapping. VM has its own pagetable,
* don't check it.
*/
if(!(vmp->vm_flags & VMF_HASPT)) {
pt_check(vmp);
}
continue;
}
old_stack =
vmp->vm_arch.vm_seg[S].mem_vir +
vmp->vm_arch.vm_seg[S].mem_len -
vmp->vm_arch.vm_seg[D].mem_len;
if(!(ip->flags & PROC_FULLVM))
continue;
if(pt_new(&vmp->vm_pt) != OK)
vm_panic("vm_init: no new pagetable", NO_NUM);
#define BASICSTACK VM_PAGE_SIZE
@ -305,7 +304,7 @@ PRIVATE void vm_init(void)
vmp->vm_arch.vm_seg[D].mem_len,
old_stack);
proc_new(vmp,
if(proc_new(vmp,
VM_PROCSTART,
CLICK2ABS(vmp->vm_arch.vm_seg[T].mem_len),
CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_len),
@ -315,7 +314,9 @@ PRIVATE void vm_init(void)
vmp->vm_arch.vm_seg[D].mem_len) - BASICSTACK,
CLICK2ABS(vmp->vm_arch.vm_seg[T].mem_phys),
CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys),
VM_STACKTOP);
VM_STACKTOP) != OK) {
vm_panic("failed proc_new for boot process", NO_NUM);
}
}
/* Set up table of calls. */
@ -347,6 +348,7 @@ PRIVATE void vm_init(void)
CALLMAP(VM_DELDMA, do_deldma, PM_PROC_NR);
CALLMAP(VM_GETDMA, do_getdma, PM_PROC_NR);
CALLMAP(VM_ALLOCMEM, do_allocmem, PM_PROC_NR);
CALLMAP(VM_NOTIFY_SIG, do_notify_sig, PM_PROC_NR);
/* Physical mapping requests.
* tty (for /dev/video) does this.
@ -359,22 +361,34 @@ PRIVATE void vm_init(void)
/* Requests from userland (source unrestricted). */
CALLMAP(VM_MMAP, do_mmap, ANYEPM);
CALLMAP(VM_MUNMAP, do_munmap, ANYEPM);
CALLMAP(VM_MUNMAP_TEXT, do_munmap, ANYEPM);
CALLMAP(VM_REMAP, do_remap, ANYEPM);
CALLMAP(VM_GETPHYS, do_get_phys, ANYEPM);
CALLMAP(VM_SHM_UNMAP, do_shared_unmap, ANYEPM);
CALLMAP(VM_GETREF, do_get_refcount, ANYEPM);
CALLMAP(VM_CTL, do_ctl, ANYEPM);
/* Request only from IPC server */
CALLMAP(VM_QUERY_EXIT, do_query_exit, ANYEPM);
/* 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);
/* Requests from RS */
CALLMAP(VM_RS_SET_PRIV, do_rs_set_priv, RS_PROC_NR);
/* Sanity checks */
if(find_kernel_top() >= VM_PROCSTART)
vm_panic("kernel loaded too high", NO_NUM);
/* Initialize the structures for queryexit */
init_query_exit();
/* Unmap our own low pages. */
unmap_ok = 1;
_minix_unmapzero();
}
#if 0
void kputc(int c)
{
if(c == '\n')
ser_putc('\r');
ser_putc(c);
}
#endif

View file

@ -16,6 +16,7 @@
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <minix/bitmap.h>
#include <sys/mman.h>
@ -47,10 +48,6 @@ PUBLIC int do_mmap(message *m)
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;
@ -66,14 +63,17 @@ PUBLIC int do_mmap(message *m)
if(m->VMM_FLAGS & MAP_CONTIG) mfflags |= MF_CONTIG;
if(m->VMM_FLAGS & MAP_PREALLOC) mfflags |= MF_PREALLOC;
if(m->VMM_FLAGS & MAP_LOWER16M) vrflags |= VR_LOWER16MB;
if(m->VMM_FLAGS & MAP_LOWER1M) vrflags |= VR_LOWER1MB;
if(m->VMM_FLAGS & MAP_ALIGN64K) vrflags |= VR_PHYS64K;
if(m->VMM_FLAGS & MAP_SHARED) vrflags |= VR_SHARED;
if(len % VM_PAGE_SIZE)
len += VM_PAGE_SIZE - (len % VM_PAGE_SIZE);
if(!(vr = map_page_region(vmp,
arch_vir2map(vmp, vmp->vm_stacktop), VM_DATATOP, len, MAP_NONE,
vrflags, mfflags))) {
arch_vir2map(vmp, vmp->vm_stacktop),
VM_DATATOP, len, MAP_NONE, vrflags, mfflags))) {
return ENOMEM;
}
} else {
@ -84,6 +84,7 @@ PUBLIC int do_mmap(message *m)
vm_assert(vr);
m->VMM_RETADDR = arch_map2vir(vmp, vr->vaddr);
return OK;
}
@ -153,9 +154,244 @@ PUBLIC int do_unmap_phys(message *m)
return EINVAL;
}
if(map_unmap_region(vmp, region) != OK) {
if(map_unmap_region(vmp, region, region->length) != OK) {
return EINVAL;
}
return OK;
}
/*===========================================================================*
* do_remap *
*===========================================================================*/
PUBLIC int do_remap(message *m)
{
int d, dn, s, sn;
vir_bytes da, sa, startv;
size_t size;
struct vir_region *vr, *region;
struct vmproc *dvmp, *svmp;
int r;
d = m->VMRE_D;
s = m->VMRE_S;
da = (vir_bytes) m->VMRE_DA;
sa = (vir_bytes) m->VMRE_SA;
size = m->VMRE_SIZE;
if ((r = vm_isokendpt(d, &dn)) != OK)
return EINVAL;
if ((r = vm_isokendpt(s, &sn)) != OK)
return EINVAL;
dvmp = &vmproc[dn];
svmp = &vmproc[sn];
/* da is not translated by arch_vir2map(),
* it's handled a little differently,
* since in map_remap(), we have to know
* about whether the user needs to bind to
* THAT address or be chosen by the system.
*/
sa = arch_vir2map(svmp, sa);
if (!(region = map_lookup(svmp, sa)))
return EINVAL;
if ((r = map_remap(dvmp, da, size, region, &startv)) != OK)
return r;
m->VMRE_RETA = (char *) arch_map2vir(dvmp, startv);
return OK;
}
/*===========================================================================*
* do_shared_unmap *
*===========================================================================*/
PUBLIC int do_shared_unmap(message *m)
{
int r, n;
struct vmproc *vmp;
endpoint_t target;
struct vir_region *vr;
vir_bytes addr;
target = m->VMUN_ENDPT;
if ((r = vm_isokendpt(target, &n)) != OK)
return EINVAL;
vmp = &vmproc[n];
addr = arch_vir2map(vmp, m->VMUN_ADDR);
if(!(vr = map_lookup(vmp, addr))) {
printf("VM: addr 0x%lx not found.\n", m->VMUN_ADDR);
return EFAULT;
}
if(vr->vaddr != addr) {
printf("VM: wrong address for shared_unmap.\n");
return EFAULT;
}
if(!(vr->flags & VR_SHARED)) {
printf("VM: address does not point to shared region.\n");
return EFAULT;
}
if(map_unmap_region(vmp, vr, vr->length) != OK)
vm_panic("do_shared_unmap: map_unmap_region failed", NO_NUM);
return OK;
}
/*===========================================================================*
* do_get_phys *
*===========================================================================*/
PUBLIC int do_get_phys(message *m)
{
int r, n;
struct vmproc *vmp;
endpoint_t target;
phys_bytes ret;
vir_bytes addr;
target = m->VMPHYS_ENDPT;
addr = m->VMPHYS_ADDR;
if ((r = vm_isokendpt(target, &n)) != OK)
return EINVAL;
vmp = &vmproc[n];
addr = arch_vir2map(vmp, addr);
r = map_get_phys(vmp, addr, &ret);
m->VMPHYS_RETA = ret;
return r;
}
/*===========================================================================*
* do_get_refcount *
*===========================================================================*/
PUBLIC int do_get_refcount(message *m)
{
int r, n;
struct vmproc *vmp;
endpoint_t target;
u8_t cnt;
vir_bytes addr;
target = m->VMREFCNT_ENDPT;
addr = m->VMREFCNT_ADDR;
if ((r = vm_isokendpt(target, &n)) != OK)
return EINVAL;
vmp = &vmproc[n];
addr = arch_vir2map(vmp, addr);
r = map_get_ref(vmp, addr, &cnt);
m->VMREFCNT_RETC = cnt;
return r;
}
/*===========================================================================*
* do_munmap *
*===========================================================================*/
PUBLIC int do_munmap(message *m)
{
int r, n;
struct vmproc *vmp;
vir_bytes addr, len;
struct vir_region *vr;
if((r=vm_isokendpt(m->m_source, &n)) != OK) {
vm_panic("do_mmap: message from strange source", m->m_source);
}
vmp = &vmproc[n];
if(!(vmp->vm_flags & VMF_HASPT))
return ENXIO;
if(m->m_type == VM_MUNMAP) {
addr = (vir_bytes) arch_vir2map(vmp, (vir_bytes) m->VMUM_ADDR);
} else if(m->m_type == VM_MUNMAP_TEXT) {
addr = (vir_bytes) arch_vir2map_text(vmp, (vir_bytes) m->VMUM_ADDR);
} else {
vm_panic("do_munmap: strange type", NO_NUM);
}
if(!(vr = map_lookup(vmp, addr))) {
printf("VM: unmap: virtual address 0x%lx not found in %d\n",
m->VMUM_ADDR, vmp->vm_endpoint);
return EFAULT;
}
len = m->VMUM_LEN;
len -= len % VM_PAGE_SIZE;
if(addr != vr->vaddr || len > vr->length || len < VM_PAGE_SIZE) {
return EFAULT;
}
if(map_unmap_region(vmp, vr, len) != OK)
vm_panic("do_munmap: map_unmap_region failed", NO_NUM);
return OK;
}
int unmap_ok = 0;
/*===========================================================================*
* munmap_lin (used for overrides for VM) *
*===========================================================================*/
PRIVATE int munmap_lin(vir_bytes addr, size_t len)
{
if(addr % VM_PAGE_SIZE) {
printf("munmap_lin: offset not page aligned\n");
return EFAULT;
}
if(len % VM_PAGE_SIZE) {
printf("munmap_lin: len not page aligned\n");
return EFAULT;
}
if(pt_writemap(&vmproc[VM_PROC_NR].vm_pt, addr, MAP_NONE, len, 0,
WMF_OVERWRITE | WMF_FREE) != OK) {
printf("munmap_lin: pt_writemap failed\n");
return EFAULT;
}
return OK;
}
/*===========================================================================*
* munmap (override for VM) *
*===========================================================================*/
PUBLIC int munmap(void *addr, size_t len)
{
vir_bytes laddr;
if(!unmap_ok)
return ENOSYS;
laddr = (vir_bytes) arch_vir2map(&vmproc[VM_PROC_NR], (vir_bytes) addr);
return munmap_lin(laddr, len);
}
/*===========================================================================*
* munmap_text (override for VM) *
*===========================================================================*/
PUBLIC int munmap_text(void *addr, size_t len)
{
vir_bytes laddr;
if(!unmap_ok)
return ENOSYS;
laddr = (vir_bytes) arch_vir2map_text(&vmproc[VM_PROC_NR],
(vir_bytes) addr);
return munmap_lin(laddr, len);
}

View file

@ -14,6 +14,7 @@
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <minix/bitmap.h>
#include <errno.h>
#include <string.h>
@ -61,12 +62,21 @@ PUBLIC void do_pagefaults(void)
vir_bytes offset;
int p, wr = PFERR_WRITE(err);
#if 0
printf("VM: pagefault: ep %d 0x%lx %s\n",
ep, arch_map2vir(vmp, addr), pf_errstr(err));
#endif
if(vm_isokendpt(ep, &p) != OK)
vm_panic("do_pagefaults: endpoint wrong", ep);
vmp = &vmproc[p];
vm_assert(vmp->vm_flags & VMF_INUSE);
#if 0
map_printmap(vmp);
#endif
/* See if address is valid at all. */
if(!(region = map_lookup(vmp, addr))) {
vm_assert(PFERR_NOPAGE(err));
@ -75,6 +85,8 @@ PUBLIC void do_pagefaults(void)
sys_sysctl_stacktrace(vmp->vm_endpoint);
if((s=sys_kill(vmp->vm_endpoint, SIGSEGV)) != OK)
vm_panic("sys_kill failed", s);
if((s=sys_vmctl(ep, VMCTL_CLEAR_PAGEFAULT, r)) != OK)
vm_panic("do_pagefaults: sys_vmctl failed", ep);
continue;
}
@ -83,6 +95,11 @@ PUBLIC void do_pagefaults(void)
*/
vm_assert(!(region->flags & VR_NOPF));
/* We do not allow shared memory to cause pagefaults.
* These pages have to be pre-allocated.
*/
vm_assert(!(region->flags & VR_SHARED));
/* If process was writing, see if it's writable. */
if(!(region->flags & VR_WRITABLE) && wr) {
printf("VM: pagefault: SIGSEGV %d ro map 0x%lx %s\n",
@ -90,6 +107,8 @@ PUBLIC void do_pagefaults(void)
sys_sysctl_stacktrace(vmp->vm_endpoint);
if((s=sys_kill(vmp->vm_endpoint, SIGSEGV)) != OK)
vm_panic("sys_kill failed", s);
if((s=sys_vmctl(ep, VMCTL_CLEAR_PAGEFAULT, r)) != OK)
vm_panic("do_pagefaults: sys_vmctl failed", ep);
continue;
}
@ -102,13 +121,23 @@ PUBLIC void do_pagefaults(void)
sys_sysctl_stacktrace(vmp->vm_endpoint);
if((s=sys_kill(vmp->vm_endpoint, SIGSEGV)) != OK)
vm_panic("sys_kill failed", s);
if((s=sys_vmctl(ep, VMCTL_CLEAR_PAGEFAULT, r)) != OK)
vm_panic("do_pagefaults: sys_vmctl failed", ep);
continue;
}
#if 0
printf("VM: map_pf done; ep %d 0x%lx %s\n",
ep, arch_map2vir(vmp, addr), pf_errstr(err));
printf("VM: handling pagefault OK: %d addr 0x%lx %s\n",
ep, arch_map2vir(vmp, addr), pf_errstr(err));
#endif
/* Pagefault is handled, so now reactivate the process. */
if((s=sys_vmctl(ep, VMCTL_CLEAR_PAGEFAULT, r)) != OK)
vm_panic("do_pagefaults: sys_vmctl failed", ep);
}
return;
@ -120,55 +149,73 @@ PUBLIC void do_pagefaults(void)
PUBLIC void do_memory(void)
{
int r, s;
endpoint_t who;
endpoint_t who, requestor;
vir_bytes mem;
vir_bytes len;
int wrflag;
while((r=sys_vmctl_get_memreq(&who, &mem, &len, &wrflag)) == OK) {
while((r=sys_vmctl_get_memreq(&who, &mem, &len, &wrflag, &requestor))
== OK) {
int p, r = OK;
struct vir_region *region;
struct vmproc *vmp;
vir_bytes o;
if(vm_isokendpt(who, &p) != OK)
vm_panic("do_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;
r = handle_memory(vmp, mem, len, wrflag);
if(!(region = map_lookup(vmp, mem))) {
printf("VM: do_memory: memory doesn't exist\n");
r = EFAULT;
} else if(mem + len > region->vaddr + region->length) {
vm_assert(region->vaddr <= mem);
vm_panic("do_memory: not contained", NO_NUM);
} else if(!(region->flags & VR_WRITABLE) && wrflag) {
printf("VM: do_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: memory range 0x%lx-0x%lx not available in %d\n",
arch_map2vir(vmp, mem), arch_map2vir(vmp, mem+len),
vmp->vm_endpoint);
}
if(sys_vmctl(who, VMCTL_MEMREQ_REPLY, r) != OK)
if(sys_vmctl(requestor, VMCTL_MEMREQ_REPLY, r) != OK)
vm_panic("do_memory: sys_vmctl failed", r);
#if 0
printf("VM: handling memory request %d done OK\n",
who);
#endif
}
}
int handle_memory(struct vmproc *vmp, vir_bytes mem, vir_bytes len, int wrflag)
{
struct vir_region *region;
vir_bytes o;
int r;
#if 0
printf("VM: handling memory request: %d, 0x%lx-0x%lx, wr %d\n",
vmp->vm_endpoint, mem, mem+len, wrflag);
#endif
/* 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))) {
map_printmap(vmp);
printf("VM: do_memory: memory doesn't exist\n");
r = EFAULT;
} else if(mem + len > region->vaddr + region->length) {
vm_assert(region->vaddr <= mem);
vm_panic("do_memory: not contained", NO_NUM);
} else if(!(region->flags & VR_WRITABLE) && wrflag) {
printf("VM: do_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: memory range 0x%lx-0x%lx not available in %d\n",
arch_map2vir(vmp, mem), arch_map2vir(vmp, mem+len),
vmp->vm_endpoint);
}
}

24
servers/vm/pagerange.h Normal file
View file

@ -0,0 +1,24 @@
#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>
typedef struct pagerange {
phys_bytes addr; /* in pages */
phys_bytes size; /* in pages */
/* AVL fields */
struct pagerange *less, *greater; /* children */
int factor; /* AVL balance factor */
} pagerange_t;

8
servers/vm/physravl.c Normal file
View file

@ -0,0 +1,8 @@
#include "sanitycheck.h"
#include "region.h"
#include "physravl.h"
#include "util.h"
#include "proto.h"
#include "cavl_impl.h"

24
servers/vm/physravl.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef _PHYSRAVL_H
#define _PHYSRAVL_H
#define AVL_UNIQUE(id) physr_ ## id
#define AVL_HANDLE phys_region_t *
#define AVL_KEY phys_bytes
#define AVL_MAX_DEPTH 30 /* good for 2 million nodes */
#define AVL_NULL NULL
#define AVL_GET_LESS(h, a) (h)->less
#define AVL_GET_GREATER(h, a) (h)->greater
#define AVL_SET_LESS(h1, h2) USE((h1), (h1)->less = h2;);
#define AVL_SET_GREATER(h1, h2) USE((h1), (h1)->greater = h2;);
#define AVL_GET_BALANCE_FACTOR(h) (h)->factor
#define AVL_SET_BALANCE_FACTOR(h, f) USE((h), (h)->factor = f;);
#define AVL_SET_ROOT(h, v) USE((h), (h)->root = v;);
#define AVL_COMPARE_KEY_KEY(k1, k2) ((k1) > (k2) ? 1 : ((k1) < (k2) ? -1 : 0))
#define AVL_COMPARE_KEY_NODE(k, h) AVL_COMPARE_KEY_KEY((k), (h)->offset)
#define AVL_COMPARE_NODE_NODE(h1, h2) AVL_COMPARE_KEY_KEY((h1)->offset, (h2)->offset)
#define AVL_INSIDE_STRUCT char pad[4];
#include "cavl_if.h"
#endif

View file

@ -21,14 +21,18 @@ _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 memstats, (int *nodes, int *pages, int *largest) );
_PROTOTYPE( void printmemstats, (void) );
_PROTOTYPE( void usedpages_reset, (void) );
_PROTOTYPE( int usedpages_add_f, (phys_bytes phys, phys_bytes len,
char *file, int line) );
_PROTOTYPE( void free_mem_f, (phys_clicks base, phys_clicks clicks) );
#define usedpages_add(a, l) usedpages_add_f(a, l, __FILE__, __LINE__)
#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) );
@ -37,6 +41,7 @@ _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) );
_PROTOTYPE( int do_ctl, (message *) );
/* exit.c */
_PROTOTYPE( void clear_proc, (struct vmproc *vmp) );
@ -74,16 +79,24 @@ _PROTOTYPE( int vfs_close, (struct vmproc *for_who, callback_t callback,
/* mmap.c */
_PROTOTYPE(int do_mmap, (message *msg) );
_PROTOTYPE(int do_munmap, (message *msg) );
_PROTOTYPE(int do_map_phys, (message *msg) );
_PROTOTYPE(int do_unmap_phys, (message *msg) );
_PROTOTYPE(int do_remap, (message *m) );
_PROTOTYPE(int do_get_phys, (message *m) );
_PROTOTYPE(int do_shared_unmap, (message *m) );
_PROTOTYPE(int do_get_refcount, (message *m) );
/* pagefaults.c */
_PROTOTYPE( void do_pagefaults, (void) );
_PROTOTYPE( void do_memory, (void) );
_PROTOTYPE( char *pf_errstr, (u32_t err));
_PROTOTYPE( int handle_memory, (struct vmproc *vmp, vir_bytes mem,
vir_bytes len, int wrflag));
/* $(ARCH)/pagetable.c */
_PROTOTYPE( void pt_init, (void) );
_PROTOTYPE( void pt_check, (struct vmproc *vmp) );
_PROTOTYPE( int pt_new, (pt_t *pt) );
_PROTOTYPE( void pt_free, (pt_t *pt) );
_PROTOTYPE( void pt_freerange, (pt_t *pt, vir_bytes lo, vir_bytes hi) );
@ -93,8 +106,8 @@ _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));
_PROTOTYPE( void vm_pagelock, (void *vir, int lockflag) );
#if SANITYCHECKS
_PROTOTYPE( void pt_sanitycheck, (pt_t *pt, char *file, int line) );
#endif
@ -106,18 +119,14 @@ _PROTOTYPE( int arch_get_pagefault, (endpoint_t *who, vir_bytes *addr, u32_t *er
_PROTOTYPE(void *slaballoc,(int bytes));
_PROTOTYPE(void slabfree,(void *mem, int bytes));
_PROTOTYPE(void slabstats,(void));
_PROTOTYPE(void slab_sanitycheck, (char *file, int line));
#define SLABALLOC(var) (var = slaballoc(sizeof(*var)))
#define SLABFREE(ptr) slabfree(ptr, sizeof(*(ptr)))
#if SANITYCHECKS
_PROTOTYPE(int slabsane,(void *mem, int bytes));
#define SLABSANE(ptr) { \
if(!slabsane(ptr, sizeof(*(ptr)))) { \
printf("VM:%s:%d: SLABSANE(%s)\n", __FILE__, __LINE__, #ptr); \
vm_panic("SLABSANE failed", NO_NUM); \
} \
}
#else
#define SLABSANE(ptr)
_PROTOTYPE(void slabunlock,(void *mem, int bytes));
_PROTOTYPE(void slablock,(void *mem, int bytes));
_PROTOTYPE(int slabsane_f,(char *file, int line, void *mem, int bytes));
#endif
/* region.c */
@ -127,7 +136,7 @@ _PROTOTYPE(struct vir_region * map_page_region,(struct vmproc *vmp, \
_PROTOTYPE(struct vir_region * map_proc_kernel,(struct vmproc *dst));
_PROTOTYPE(int map_region_extend,(struct vmproc *vmp, 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_unmap_region,(struct vmproc *vmp, struct vir_region *vr, vir_bytes len));
_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));
@ -135,11 +144,17 @@ _PROTOTYPE(int map_pf,(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(void map_printmap, (struct vmproc *vmp));
_PROTOTYPE(int map_writept, (struct vmproc *vmp));
_PROTOTYPE(void printregionstats, (struct vmproc *vmp));
_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));
_PROTOTYPE(int map_remap, (struct vmproc *dvmp, vir_bytes da, size_t size,
struct vir_region *region, vir_bytes *r));
_PROTOTYPE(int map_get_phys, (struct vmproc *vmp, vir_bytes addr, phys_bytes *r));
_PROTOTYPE(int map_get_ref, (struct vmproc *vmp, vir_bytes addr, u8_t *cnt));
#if SANITYCHECKS
_PROTOTYPE(void map_sanitycheck,(char *file, int line));
@ -149,4 +164,12 @@ _PROTOTYPE(void map_sanitycheck,(char *file, int line));
_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));
_PROTOTYPE( vir_bytes, arch_vir2map_text(struct vmproc *vmp, vir_bytes addr));
/* rs.c */
_PROTOTYPE(int do_rs_set_priv, (message *m));
/* queryexit.c */
_PROTOTYPE(int do_query_exit, (message *m));
_PROTOTYPE(int do_notify_sig, (message *m));
_PROTOTYPE(void init_query_exit, (void));

123
servers/vm/queryexit.c Normal file
View file

@ -0,0 +1,123 @@
#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 <minix/bitmap.h>
#include <minix/vm.h>
#include <minix/ds.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#include "glo.h"
#include "proto.h"
#include "util.h"
struct query_exit_struct {
int avail;
endpoint_t ep;
};
static struct query_exit_struct array[NR_PROCS];
/*===========================================================================*
* do_query_exit *
*===========================================================================*/
PUBLIC int do_query_exit(message *m)
{
int i, nr;
endpoint_t ep;
for (i = 0; i < NR_PROCS; i++) {
if (!array[i].avail) {
array[i].avail = 1;
ep = array[i].ep;
array[i].ep = 0;
break;
}
}
nr = 0;
for (i = 0; i < NR_PROCS; i++) {
if (!array[i].avail)
nr++;
}
m->VM_QUERY_RET_PT = ep;
m->VM_QUERY_IS_MORE = (nr > 0);
return OK;
}
/*===========================================================================*
* do_notify_sig *
*===========================================================================*/
PUBLIC int do_notify_sig(message *m)
{
int i, avails = 0;
endpoint_t ep = m->VM_NOTIFY_SIG_ENDPOINT;
endpoint_t ipc_ep = m->VM_NOTIFY_SIG_IPC;
int r;
for (i = 0; i < NR_PROCS; i++) {
/* its signal is already here */
if (!array[i].avail && array[i].ep == ep)
goto out;
if (array[i].avail)
avails++;
}
if (!avails) {
/* no slot for signals, impossible */
printf("VM: no slot for signals!\n");
return ENOMEM;
}
for (i = 0; i < NR_PROCS; i++) {
if (array[i].avail) {
array[i].avail = 0;
array[i].ep = ep;
break;
}
}
out:
/* only care when IPC server starts up,
* and bypass the process to be signal is IPC itself.
*/
if (ipc_ep != 0 && ep != ipc_ep) {
r = notify(ipc_ep);
if (r != OK)
printf("VM: notify IPC error!\n");
}
return OK;
}
/*===========================================================================*
* init_query_exit *
*===========================================================================*/
PUBLIC void init_query_exit(void)
{
int i;
for (i = 0; i < NR_PROCS; i++) {
array[i].avail = 1;
array[i].ep = 0;
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,11 +2,24 @@
#ifndef _REGION_H
#define _REGION_H 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>
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 */
@ -15,33 +28,42 @@ struct phys_block {
struct phys_region *firstregion;
};
struct phys_region {
struct phys_region *next; /* next contiguous block */
typedef struct phys_region {
struct phys_block *ph;
struct vir_region *parent; /* Region that owns this phys_region. */
struct vir_region *parent; /* parent vir_region. */
vir_bytes offset; /* offset from start of vir region */
/* list of phys_regions that reference the same phys_block */
struct phys_region *next_ph_list;
};
/* AVL fields */
struct phys_region *less, *greater;
int factor;
} phys_region_t;
#include "physravl.h"
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 */
physr_avl *phys; /* avl tree of physical memory blocks */
u16_t flags;
u32_t tag; /* Opaque to mapping code. */
struct vmproc *parent; /* Process that owns this vir_region. */
};
/* Mapping flags: */
#define VR_WRITABLE 0x01 /* Process may write here. */
#define VR_NOPF 0x02 /* May not generate page faults. */
#define VR_PHYS64K 0x04 /* Physical memory must be 64k aligned. */
#define VR_WRITABLE 0x001 /* Process may write here. */
#define VR_NOPF 0x002 /* May not generate page faults. */
#define VR_PHYS64K 0x004 /* Physical memory must be 64k aligned. */
#define VR_LOWER16MB 0x008
#define VR_LOWER1MB 0x010
/* Mapping type: */
#define VR_ANON 0x10 /* Memory to be cleared and allocated */
#define VR_DIRECT 0x20 /* Mapped, but not managed by VM */
#define VR_ANON 0x100 /* Memory to be cleared and allocated */
#define VR_DIRECT 0x200 /* Mapped, but not managed by VM */
#define VR_SHARED 0x40
/* Tag values: */
#define VRT_NONE 0xBEEF0000

56
servers/vm/rs.c Normal file
View file

@ -0,0 +1,56 @@
#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 <minix/bitmap.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#include "glo.h"
#include "proto.h"
#include "util.h"
/*===========================================================================*
* do_rs_set_priv *
*===========================================================================*/
PUBLIC int do_rs_set_priv(message *m)
{
int r, n, nr;
struct vmproc *vmp;
nr = m->VM_RS_NR;
if ((r = vm_isokendpt(nr, &n)) != OK) {
printf("do_rs_set_priv: message from strange source %d\n", nr);
return EINVAL;
}
vmp = &vmproc[n];
if (m->VM_RS_BUF) {
r = sys_datacopy(m->m_source, (vir_bytes) m->VM_RS_BUF,
SELF, (vir_bytes) vmp->vm_call_priv_mask,
sizeof(vmp->vm_call_priv_mask));
if (r != OK)
return r;
}
return OK;
}

View file

@ -13,34 +13,59 @@
printf("VM:%s:%d: %s failed\n", file, line, #c); \
vm_panic("sanity check failed", NO_NUM); } } while(0)
#define SLABSANITYCHECK(l) if((l) <= vm_sanitychecklevel) { \
slab_sanitycheck(__FILE__, __LINE__); }
#define SANITYCHECK(l) if(!nocheck && ((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++) { \
vm_assert(incheck == 0); \
incheck = 1; \
usedpages_reset(); \
slab_sanitycheck(__FILE__, __LINE__); \
for(vmp = vmproc; vmp < &vmproc[VMP_NR]; vmp++) { \
if((vmp->vm_flags & (VMF_INUSE | VMF_HASPT)) == \
(VMF_INUSE | VMF_HASPT)) { \
pt_sanitycheck(&vmp->vm_pt, __FILE__, __LINE__); \
PT_SANE(&vmp->vm_pt); \
} \
} \
map_sanitycheck(__FILE__, __LINE__); \
vm_assert(incheck == 1); \
incheck = 0; \
}
#include "../../kernel/proc.h"
#define USE(obj, code) do { \
slabunlock(obj, sizeof(*obj)); \
do { \
code \
} while(0); \
slablock(obj, sizeof(*obj)); \
} while(0)
#define SLABSANE(ptr) { \
if(!slabsane_f(__FILE__, __LINE__, ptr, sizeof(*(ptr)))) { \
printf("VM:%s:%d: SLABSANE(%s)\n", __FILE__, __LINE__, #ptr); \
vm_panic("SLABSANE failed", NO_NUM); \
} \
}
#define NOTRUNNABLE(ep) { \
struct proc pr; \
if(sys_getproc(&pr, ep) != OK) { \
vm_panic("VM: sys_getproc failed", ep); \
} \
if(!pr.p_rts_flags) { \
vm_panic("VM: runnable", ep); \
} \
}
#else
#define SANITYCHECK
#define SLABSANITYCHECK(l)
#define USE(obj, code) do { code } while(0)
#define SLABSANE(ptr)
#define NOTRUNNABLE(ep)
#endif
#endif

View file

@ -13,6 +13,7 @@
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/bitmap.h>
#include <sys/sigcontext.h>
#include <errno.h>

View file

@ -13,6 +13,8 @@
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/bitmap.h>
#include <minix/debug.h>
#include <errno.h>
#include <string.h>
@ -27,7 +29,7 @@
#define SLABSIZES 60
#define ITEMSPERPAGE(s, bytes) (DATABYTES / (bytes))
#define ITEMSPERPAGE(bytes) (DATABYTES / (bytes))
#define ELBITS (sizeof(element_t)*8)
#define BITPAT(b) (1UL << ((b) % ELBITS))
@ -37,9 +39,37 @@
#define OFF(f, b) vm_assert(!GETBIT(f, b))
#define ON(f, b) vm_assert(GETBIT(f, b))
#if SANITYCHECKS
#define SLABDATAWRITABLE(data, wr) do { \
vm_assert(data->sdh.writable == WRITABLE_NONE); \
vm_assert(wr != WRITABLE_NONE); \
vm_pagelock(data, 0); \
data->sdh.writable = wr; \
} while(0)
#define SLABDATAUNWRITABLE(data) do { \
vm_assert(data->sdh.writable != WRITABLE_NONE); \
data->sdh.writable = WRITABLE_NONE; \
vm_pagelock(data, 1); \
} while(0)
#define SLABDATAUSE(data, code) do { \
SLABDATAWRITABLE(data, WRITABLE_HEADER); \
code \
SLABDATAUNWRITABLE(data); \
} while(0)
#else
#define SLABDATAWRITABLE(data, wr)
#define SLABDATAUNWRITABLE(data)
#define SLABDATAUSE(data, code) do { code } while(0)
#endif
#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 SETBIT(f, b) {OFF(f,b); SLABDATAUSE(f, BITEL(f,b)|= BITPAT(b); (f)->sdh.nused++;); }
#define CLEARBIT(f, b) {ON(f, b); SLABDATAUSE(f, BITEL(f,b)&=~BITPAT(b); (f)->sdh.nused--; (f)->sdh.freeguess = (b);); }
#define MINSIZE 8
#define MAXSIZE (SLABSIZES-1+MINSIZE)
@ -56,28 +86,32 @@ typedef element_t elements_t[USEELEMENTS];
* 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
/* Special writable values. */
#define WRITABLE_NONE -2
#define WRITABLE_HEADER -1
struct sdh {
#if SANITYCHECKS
u32_t magic1;
#endif
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;
#if SANITYCHECKS
int writable; /* data item number or WRITABLE_* */
u32_t magic2;
#endif
};
#define DATABYTES (VM_PAGE_SIZE-sizeof(struct sdh))
#define MAGIC 0x1f5b842f
#define MAGIC1 0x1f5b842f
#define MAGIC2 0x8bb5a420
#define JUNK 0xdeadbeef
#define NOJUNK 0xc0ffee
@ -107,6 +141,7 @@ FORWARD _PROTOTYPE( int objstats, (void *, int, struct slabheader **, struct sla
#define LH(sl, l) (sl)->list_head[l]
/* move head of list l1 to list of l2 in slabheader sl. */
#define MOVEHEAD(sl, l1, l2) { \
struct slabdata *t; \
vm_assert(LH(sl,l1)); \
@ -114,28 +149,35 @@ FORWARD _PROTOTYPE( int objstats, (void *, int, struct slabheader **, struct sla
ADDHEAD(t, sl, l2); \
}
/* remove head of list 'list' in sl, assign it unlinked to 'to'. */
#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); \
struct slabdata *dat; \
dat = (to) = LH(sl, list); \
vm_assert(dat); \
LH(sl, list) = dat->sdh.next; \
UNLINKNODE(dat); \
}
/* move slabdata nw to slabheader sl under list number l. */
#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; \
SLABDATAUSE(nw, \
(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); \
if((nw)->sdh.next) { \
SLABDATAUSE((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; \
}
#define UNLINKNODE(node) { \
struct slabdata *next, *prev; \
prev = (node)->sdh.prev; \
next = (node)->sdh.next; \
if(prev) { SLABDATAUSE(prev, prev->sdh.next = next;); } \
if(next) { SLABDATAUSE(next, next->sdh.prev = prev;); } \
}
struct slabdata *newslabdata(int list)
{
@ -151,12 +193,18 @@ struct slabdata *newslabdata(int list)
n->sdh.phys = p;
#if SANITYCHECKS
n->sdh.magic = MAGIC;
n->sdh.magic1 = MAGIC1;
n->sdh.magic2 = MAGIC2;
#endif
n->sdh.nused = 0;
n->sdh.freeguess = 0;
n->sdh.list = list;
#if SANITYCHECKS
n->sdh.writable = WRITABLE_HEADER;
SLABDATAUNWRITABLE(n);
#endif
return n;
}
@ -173,15 +221,17 @@ PRIVATE int checklist(char *file, int line,
while(n) {
int count = 0, i;
MYASSERT(n->sdh.magic1 == MAGIC1);
MYASSERT(n->sdh.magic2 == MAGIC2);
MYASSERT(n->sdh.list == l);
MYASSERT(n->sdh.magic == MAGIC);
MYASSERT(usedpages_add(n->sdh.phys, VM_PAGE_SIZE) == OK);
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))
if(i >= ITEMSPERPAGE(bytes))
MYASSERT(!GETBIT(n, i));
else
if(GETBIT(n,i))
@ -211,21 +261,25 @@ PUBLIC void slab_sanitycheck(char *file, int line)
/*===========================================================================*
* int slabsane *
*===========================================================================*/
PUBLIC int slabsane(void *mem, int bytes)
PUBLIC int slabsane_f(char *file, int line, void *mem, int bytes)
{
struct slabheader *s;
struct slabdata *f;
int i;
return (objstats(mem, bytes, &s, &f, &i) == OK);
}
#endif
static int nojunkwarning = 0;
/*===========================================================================*
* void *slaballoc *
*===========================================================================*/
PUBLIC void *slaballoc(int bytes)
{
int i, n = 0;
int i;
int count = 0;
struct slabheader *s;
struct slabdata *firstused;
@ -242,10 +296,10 @@ PUBLIC void *slaballoc(int bytes)
/* Make sure there is something on the freelist. */
SLABSANITYCHECK(SCL_DETAIL);
if(!LH(s, LIST_FREE)) {
struct slabdata *n = newslabdata(LIST_FREE);
struct slabdata *nd = newslabdata(LIST_FREE);
SLABSANITYCHECK(SCL_DETAIL);
if(!n) return NULL;
ADDHEAD(n, s, LIST_FREE);
if(!nd) return NULL;
ADDHEAD(nd, s, LIST_FREE);
SLABSANITYCHECK(SCL_DETAIL);
}
@ -260,18 +314,21 @@ PUBLIC void *slaballoc(int bytes)
vm_assert(s);
firstused = LH(s, LIST_USED);
vm_assert(firstused);
vm_assert(firstused->sdh.magic == MAGIC);
vm_assert(firstused->sdh.magic1 == MAGIC1);
vm_assert(firstused->sdh.magic2 == MAGIC2);
vm_assert(firstused->sdh.nused < ITEMSPERPAGE(bytes));
for(i = firstused->sdh.freeguess; n < ITEMSPERPAGE(s, bytes); n++, i++) {
for(i = firstused->sdh.freeguess;
count < ITEMSPERPAGE(bytes); count++, i++) {
SLABSANITYCHECK(SCL_DETAIL);
i = i % ITEMSPERPAGE(s, bytes);
i = i % ITEMSPERPAGE(bytes);
if(!GETBIT(firstused, i)) {
struct slabdata *f;
char *ret;
SETBIT(firstused, i);
SLABSANITYCHECK(SCL_DETAIL);
if(firstused->sdh.nused == ITEMSPERPAGE(s, bytes)) {
if(firstused->sdh.nused == ITEMSPERPAGE(bytes)) {
SLABSANITYCHECK(SCL_DETAIL);
MOVEHEAD(s, LIST_USED, LIST_FULL);
SLABSANITYCHECK(SCL_DETAIL);
@ -280,20 +337,21 @@ PUBLIC void *slaballoc(int bytes)
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);
}
nojunkwarning++;
slabunlock(ret, bytes);
nojunkwarning--;
vm_assert(!nojunkwarning);
*(u32_t *) ret = NOJUNK;
slablock(ret, bytes);
#endif
SLABSANITYCHECK(SCL_FUNCTIONS);
firstused->sdh.freeguess = i+1;
SLABDATAUSE(firstused, firstused->sdh.freeguess = i+1;);
#if SANITYCHECKS
if(!slabsane(ret, bytes))
if(bytes >= SLABSIZES+MINSIZE) {
printf("slaballoc: odd, bytes %d?\n", bytes);
}
if(!slabsane_f(__FILE__, __LINE__, ret, bytes))
vm_panic("slaballoc: slabsane failed", NO_NUM);
#endif
@ -317,12 +375,16 @@ PUBLIC void *slaballoc(int bytes)
PRIVATE int objstats(void *mem, int bytes,
struct slabheader **sp, struct slabdata **fp, int *ip)
{
#if SANITYCHECKS
#define OBJSTATSCHECK(cond) \
if(!(cond)) { \
printf("VM:objstats: %s failed for ptr 0x%p, %d bytes\n", \
printf("VM: objstats: %s failed for ptr 0x%p, %d bytes\n", \
#cond, mem, bytes); \
return EINVAL; \
}
#else
#define OBJSTATSCHECK(cond)
#endif
struct slabheader *s;
struct slabdata *f;
@ -331,21 +393,19 @@ PRIVATE int objstats(void *mem, int bytes,
OBJSTATSCHECK((char *) mem >= (char *) VM_PAGE_SIZE);
#if SANITYCHECKS
if(*(u32_t *) mem == JUNK) {
if(*(u32_t *) mem == JUNK && !nojunkwarning) {
util_stacktrace();
printf("VM: WARNING: JUNK seen in slab object\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);
#if SANITYCHECKS
OBJSTATSCHECK(f->sdh.magic == MAGIC);
#endif
OBJSTATSCHECK(f->sdh.magic1 == MAGIC1);
OBJSTATSCHECK(f->sdh.magic2 == MAGIC2);
OBJSTATSCHECK(f->sdh.list == LIST_USED || f->sdh.list == LIST_FULL);
/* Make sure it's in range. */
@ -379,22 +439,26 @@ PUBLIC void slabfree(void *mem, int bytes)
SLABSANITYCHECK(SCL_FUNCTIONS);
#if SANITYCHECKS
if(*(u32_t *) mem == JUNK) {
printf("VM: WARNING: likely double free, JUNK seen\n");
}
#endif
if(objstats(mem, bytes, &s, &f, &i) != OK) {
vm_panic("slabfree objstats failed", NO_NUM);
}
#if SANITYCHECKS
if(*(u32_t *) mem == JUNK) {
printf("VM: WARNING: likely double free, JUNK seen\n");
}
slabunlock(mem, bytes);
*(u32_t *) mem = JUNK;
nojunkwarning++;
slablock(mem, bytes);
nojunkwarning--;
vm_assert(!nojunkwarning);
#endif
/* 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 */
@ -404,7 +468,7 @@ PUBLIC void slabfree(void *mem, int bytes)
LH(s, LIST_USED) = f->sdh.next;
ADDHEAD(f, s, LIST_FREE);
SLABSANITYCHECK(SCL_DETAIL);
} else if(f->sdh.nused == ITEMSPERPAGE(s, bytes)-1) {
} else if(f->sdh.nused == ITEMSPERPAGE(bytes)-1) {
/* Now become USED; must've been FULL */
vm_assert(f->sdh.list == LIST_FULL);
UNLINKNODE(f);
@ -422,6 +486,42 @@ PUBLIC void slabfree(void *mem, int bytes)
return;
}
/*===========================================================================*
* void *slablock *
*===========================================================================*/
PUBLIC void slablock(void *mem, int bytes)
{
int i;
struct slabheader *s;
struct slabdata *f;
if(objstats(mem, bytes, &s, &f, &i) != OK)
vm_panic("slablock objstats failed", NO_NUM);
SLABDATAUNWRITABLE(f);
FIXME("verify new contents");
return;
}
/*===========================================================================*
* void *slabunlock *
*===========================================================================*/
PUBLIC void slabunlock(void *mem, int bytes)
{
int i;
struct slabheader *s;
struct slabdata *f;
if(objstats(mem, bytes, &s, &f, &i) != OK)
vm_panic("slablock objstats failed", NO_NUM);
SLABDATAWRITABLE(f, i);
return;
}
#if SANITYCHECKS
/*===========================================================================*
* void slabstats *

View file

@ -8,15 +8,15 @@
#define ELEMENTS(a) (sizeof(a)/sizeof((a)[0]))
#if SANITYCHECKS
#define vm_assert(cond) do { \
#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); \
} \
} while(0)
}
#else
#define vm_assert(cond)
#define vm_assert(cond) ;
#endif
#define vm_panic(str, n) { char _pline[100]; \

View file

@ -18,10 +18,12 @@
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/type.h>
#include <minix/bitmap.h>
#include <string.h>
#include <errno.h>
#include <env.h>
#include <unistd.h>
#include <memory.h>
#include "proto.h"
#include "glo.h"
@ -119,8 +121,8 @@ struct mem_map *map_ptr; /* memory to remove */
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 || *proc >= NR_PROCS)
vm_panic("crazy slot number", *proc);
if(*proc >= 0 && endpoint != vmproc[*proc].vm_endpoint)
return EDEADSRCDST;
if(*proc >= 0 && !(vmproc[*proc].vm_flags & VMF_INUSE))
@ -163,3 +165,28 @@ char *brk_addr;
return 0;
}
/*===========================================================================*
* do_ctl *
*===========================================================================*/
PUBLIC int do_ctl(message *m)
{
int pages, nodes;
int pr;
struct vmproc *vmp;
switch(m->VCTL_WHAT) {
case VCTLP_STATS_MEM:
printmemstats();
break;
case VCTLP_STATS_EP:
if(vm_isokendpt(m->VCTL_PARAM, &pr) != OK)
return EINVAL;
printregionstats(&vmproc[pr]);
break;
default:
return EINVAL;
}
return OK;
}

View file

@ -16,6 +16,7 @@
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <minix/bitmap.h>
#include <errno.h>
#include <string.h>

View file

@ -5,6 +5,8 @@
#define PAF_CLEAR 0x01 /* Clear physical memory. */
#define PAF_CONTIG 0x02 /* Physically contiguous. */
#define PAF_ALIGN64K 0x04 /* Aligned to 64k boundary. */
#define PAF_LOWER16MB 0x08
#define PAF_LOWER1MB 0x10
/* special value for v in pt_allocmap */
#define AM_AUTO ((u32_t) -1)
@ -14,7 +16,10 @@
/* Compile in asserts and custom sanity checks at all? */
#define SANITYCHECKS 0
#define VMSTATS 1
#define VMSTATS 0
/* Minimum stack region size - 64MB. */
#define MINSTACKREGION (64*1024*1024)
/* If so, this level: */
#define SCL_NONE 0 /* No sanity checks - vm_assert()s only. */
@ -31,7 +36,9 @@
#define VMP_CATEGORIES 4
/* Flags to pt_writemap(). */
#define WMF_OVERWRITE 0x01 /* Caller knows map may overwrite. */
#define WMF_OVERWRITE 0x01 /* Caller knows map may overwrite. */
#define WMF_WRITEFLAGSONLY 0x02 /* Copy physaddr and update flags. */
#define WMF_FREE 0x04 /* Free pages overwritten. */
/* Special value of 'what' to map_page_region meaning: unknown. */
#define MAP_NONE 0xFFFFFFFE

View file

@ -4,6 +4,7 @@
#include <pagetable.h>
#include <arch_vmproc.h>
#include <minix/bitmap.h>
#include "vm.h"
@ -31,6 +32,9 @@ struct vmproc {
/* Heap for brk() to extend. */
struct vir_region *vm_heap;
#define VM_CALL_PRIV_MASK_SIZE BITMAP_CHUNKS(VM_NCALLS)
bitchunk_t vm_call_priv_mask[VM_CALL_PRIV_MASK_SIZE];
/* State for requests pending to be done to vfs on behalf of
* this process.
*/