t[merge] - vx32 - Local 9vx git repository for patches.
Log
Files
Refs
---
commit 8a44751270915a0ad6e86adf68a111dd509be9be
parent ed98fad5e2bfe95b6f631ab547df24e4d575900c
Author: Russ Cox 
Date:   Fri,  4 Jul 2008 12:06:36 -0400

t[merge]

Diffstat:
  src/9vx/a/dat.h                     |      24 +++++++++++++++---------
  src/9vx/a/fault.c                   |       4 ++--
  src/9vx/a/sysproc.c                 |       2 ++
  src/9vx/devfs-posix.c               |      36 ++++++++++++++++++++++++++------
  src/9vx/main.c                      |      11 ++++++++++-
  src/9vx/mmu.c                       |     195 +++++++++++++++++++++++--------
  src/9vx/osx/screen.c                |      15 +++++++++++++--
  src/9vx/sched.c                     |     114 ++++++++-----------------------
  src/9vx/stub.c                      |      24 ++++++++++++++----------
  src/9vx/term.c                      |       1 +
  src/9vx/trap.c                      |      14 +++++++++-----
  src/9vx/vx32.c                      |     114 ++++++++++++++++---------------
  src/BUGS                            |       2 --

13 files changed, 327 insertions(+), 229 deletions(-)
---
diff --git a/src/9vx/a/dat.h b/src/9vx/a/dat.h
@@ -1,4 +1,5 @@
 #include 
+#include "libvx32/vx32.h"
 
 ttypedef struct BIOS32si        BIOS32si;
 ttypedef struct Conf        Conf;
@@ -114,12 +115,14 @@ struct Conf
  *  MMU stuff in proc
  */
 #define NCOLOR 1
+ttypedef struct Uspace Uspace;
 struct PMMU
 {
-        ulong lo;        // Plan 9 VX
-        ulong hi;        // Plan 9 VX
         struct vxproc *vxproc;        // Plan 9 VX
-        struct vxmmap *vxmm;        // Plan 9 VX
+        struct vxmem vxmem;
+        struct vxmmap vxmm;        // Plan 9 VX
+        Uspace *us;
+        uchar *uzero;
 };
 
 /*
@@ -348,7 +351,6 @@ struct DevConf
 // Plan 9 VX
 extern int traceprocs;
 extern int tracesyscalls;
-extern uchar *uzero;
 extern int doabort;
 
 /* Pthreads-based sleep and wakeup. */
@@ -356,12 +358,16 @@ typedef struct Psleep Psleep;
 ttypedef struct Pwaiter Pwaiter;
 struct Psleep
 {
+        int init;
         pthread_mutex_t mutex;
-        pthread_cond_t cond;
-        int condinit;
         Pwaiter *waiter;
-        int fd[2];
-        vlong nread;
-        vlong nwrite;
+};
+
+struct Uspace
+{
+        Proc *p;        // proc currently mapped
+        uchar *uzero;
+        ulong lo;
+        ulong hi;
 };
 
diff --git a/src/9vx/a/fault.c b/src/9vx/a/fault.c
@@ -343,7 +343,7 @@ okaddr(ulong addr, ulong len, int write)
                                 continue;
                         }
                         qunlock(&s->lk);
-                        return uzero+addr0;
+                        return up->pmmu.uzero+addr0;
                 }
         }
         pprint("suicide: invalid address 0x%lux/%lud in sys call pc=0x%lux\n", addr, len, userpc());
@@ -400,7 +400,7 @@ vmemchr(void *s, int c, int n)
                 a += m_;
                 n -= m_;
                 if(isuaddr(a))
-                        uvalidaddr(a-uzero, 1, 0);
+                        uvalidaddr(a-up->pmmu.uzero, 1, 0);
         }
 
         /* fits in one page */
diff --git a/src/9vx/a/sysproc.c b/src/9vx/a/sysproc.c
@@ -379,6 +379,8 @@ sysexec(ulong *arg)
         /*
          * Top-of-stack structure.
          */
+        uchar *uzero;
+        uzero = up->pmmu.uzero;
         Tos *tos;
         ulong utos;
         utos = USTKTOP - sizeof(Tos);
diff --git a/src/9vx/devfs-posix.c b/src/9vx/devfs-posix.c
@@ -4,16 +4,23 @@
 #include                /* going to regret this - getgrgid is a stack smasher */
 #include        
 #include        
+
 #if defined(__FreeBSD__)
-#include 
-#include 
-#include 
+#include        
+#include        
+#include        
+#endif
+
+#if defined(__APPLE__)
+#include        
 #endif
+
 #if defined(__linux__)
-#include 
-#include 
-#include 
+#include        
+#include        
+#include        
 #endif
+
 #include        "lib.h"
 #include        "mem.h"
 #include        "dat.h"
@@ -907,6 +914,23 @@ disksize(int fd, struct stat *st)
         return 0;
 }
 
+#elif defined(__APPLE__)
+
+static vlong
+disksize(int fd, struct stat *st)
+{
+        uvlong bc;
+        unsigned int bs;
+
+        bs = 0;
+        bc = 0;
+        ioctl(fd, DKIOCGETBLOCKSIZE, &bs);
+        ioctl(fd, DKIOCGETBLOCKCOUNT, &bc);
+        if(bs >0 && bc > 0)
+                return bc*bs;
+        return 0;
+}
+
 #else
 
 static vlong
diff --git a/src/9vx/main.c b/src/9vx/main.c
@@ -36,7 +36,7 @@ extern Dev drawdevtab;
 extern Dev fsdevtab;
 extern Dev audiodevtab;
 
-int        doabort;
+int        doabort = 1;        // for now
 char*        argv0;
 char*        conffile = "9vx";
 Conf        conf;
@@ -49,6 +49,7 @@ static Mach mach0;
 extern char*        localroot;
 extern int        tracemmu;
 extern int tracekdev;
+extern int nuspace;
 static int singlethread;
 
 static void        bootinit(void);
@@ -109,6 +110,9 @@ main(int argc, char **argv)
         case 'S':
                 tracesyscalls++;
                 break;
+        case 'U':
+                nuspace = atoi(EARGF(usage()));
+                break;
         case 'X':
                 vx32_debugxlate++;
                 break;
@@ -419,7 +423,9 @@ showexec(ulong sp)
 {
         ulong *a, *argv;
         int i, n;
+        uchar *uzero;
         
+        uzero = up->pmmu.uzero;
         iprint("showexec %p\n", sp);
         if(sp >= USTKTOP || sp < USTKTOP-USTKSIZE)
                 panic("showexec: bad sp");
@@ -510,6 +516,7 @@ sigsegv(int signo, siginfo_t *info, void *v)
         int read;
         ulong addr, eip, esp;
         ucontext_t *uc;
+        uchar *uzero;
 
         if(m == nil)
                 panic("sigsegv: m == nil");
@@ -518,6 +525,8 @@ sigsegv(int signo, siginfo_t *info, void *v)
         if(up == nil)
                 panic("sigsegv: up == nil");
 
+        uzero = up->pmmu.uzero;
+
         uc = v;
 #if defined(__APPLE__)
         mcontext_t mc;
diff --git a/src/9vx/mmu.c b/src/9vx/mmu.c
@@ -30,14 +30,19 @@ int tracemmu;
 
 static int pagefile;
 static char* pagebase;
-uchar *uzero;
+
+static Uspace uspace[16];
+static Uspace *ulist[nelem(uspace)];
+int nuspace = 1;
 
 int
 isuaddr(void *v)
 {
         uchar *p;
+        uchar *uzero;
         
         p = v;
+        uzero = up->pmmu.uzero;
         return uzero <= p && p < uzero+USTKTOP;
 }
 
@@ -46,7 +51,7 @@ isuaddr(void *v)
  * The point is to reserve the space so that
  * nothing else ends up there later.
  */
-static void
+static void*
 mapzero(void)
 {
         int fd;
@@ -55,20 +60,16 @@ mapzero(void)
         /* First try mmaping /dev/zero.  Some OS'es don't allow this. */
         if((fd = open("/dev/zero", O_RDONLY)) >= 0){
                 v = mmap(nil, USTKTOP, PROT_NONE, MAP_PRIVATE, fd, 0);
-                if(v != MAP_FAILED){
-                        uzero = v;
-                        return;
-                }
+                if(v != MAP_FAILED)
+                        return v;
         }
         
         /* Next try an anonymous map. */
         v = mmap(nil, USTKTOP, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
-        if(v != MAP_FAILED){
-                uzero = v;
-                return;
-        }
-        
-        panic("mapzero: cannot reserve process address space");
+        if(v != MAP_FAILED)
+                return v;
+
+        return nil;
 }
 
 void
@@ -76,8 +77,8 @@ mmuinit(void)
 {
         char tmp[] = "/var/tmp/9vx.pages.XXXXXX";
         void *v;
-
-        mapzero();
+        int i;
+        
         if((pagefile = mkstemp(tmp)) < 0)
                 panic("mkstemp: %r");
         if(ftruncate(pagefile, MEMSIZE) < 0)
@@ -92,6 +93,17 @@ mmuinit(void)
                 panic("mmap pagefile: %r");
         pagebase = v;
 
+        if(nuspace <= 0)
+                nuspace = 1;
+        if(nuspace > nelem(uspace))
+                nuspace = nelem(uspace);
+        for(i=0; iflushmmu = 0;
 
         /* Nothing mapped? */
-        if(mmup == nil || mmup->pmmu.lo > mmup->pmmu.hi)
+        if(us == nil || us->lo > us->hi || us->uzero == nil)
                 return;
 
 #ifdef __FreeBSD__
@@ -154,20 +158,20 @@ mmapflush(void)
                  * tell whether a page is mapped, so we have to remap
                  * something with no pages here. 
                  */
-                if(mmap(uzero, mmup->pmmu.hi+BY2PG, PROT_NONE, 
+                if(mmap(us->uzero, us->hi+BY2PG, PROT_NONE, 
                                 MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) == MAP_FAILED)
                         panic("mmapflush mmap: %r");
-                mmup->pmmu.lo = 0x80000000UL;
-                mmup->pmmu.hi = 0;
+                us->lo = 0x80000000UL;
+                us->hi = 0;
                 return;
         }
 #endif
 
         /* Clear only as much as got mapped. */
-        if(mprotect(uzero, mmup->pmmu.hi+BY2PG, PROT_NONE) < 0)
+        if(mprotect(us->uzero, us->hi+BY2PG, PROT_NONE) < 0)
                 panic("mmapflush mprotect: %r");
-        mmup->pmmu.lo = 0x80000000UL;
-        mmup->pmmu.hi = 0;
+        us->lo = 0x80000000UL;
+        us->hi = 0;
 }
 
 /*
@@ -178,13 +182,15 @@ void
 putmmu(ulong va, ulong pa, Page *p)
 {
         int prot;
-        PMMU *pmmu;
+        Uspace *us;
 
         if(tracemmu || (pa&~(PTEWRITE|PTEVALID)) != p->pa)
                 print("putmmu va %lux pa %lux p->pa %lux\n", va, pa, p->pa);
 
         assert(p->pa < MEMSIZE && pa < MEMSIZE);
         assert(up);
+        us = up->pmmu.us;
+        assert(us);
 
         /* Map the page */
         prot = PROT_READ;
@@ -192,21 +198,20 @@ putmmu(ulong va, ulong pa, Page *p)
                 prot |= PROT_WRITE;
         pa &= ~(BY2PG-1);
         va  &= ~(BY2PG-1);
-        if(mmap(uzero+va, BY2PG, prot, MAP_FIXED|MAP_SHARED,
+        if(mmap(us->uzero+va, BY2PG, prot, MAP_FIXED|MAP_SHARED,
                         pagefile, pa) == MAP_FAILED)
                 panic("putmmu");
         
         /* Record high and low address range for quick unmap. */
-        pmmu = &up->pmmu;
-        if(pmmu->lo > va)
-                pmmu->lo = va;
-        if(pmmu->hi < va)
-                pmmu->hi = va;
+        if(us->lo > va)
+                us->lo = va;
+        if(us->hi < va)
+                us->hi = va;
 //        printlinuxmaps();
 }
 
 /*
- * The memory maps have changed.  Flush all cached state.
+ * The memory maps have changed for up.  Flush all cached state.
  */
 void
 flushmmu(void)
@@ -214,9 +219,78 @@ flushmmu(void)
         if(tracemmu)
                 print("flushmmu\n");
 
-        if(up)
+        if(up){
                 vxproc_flush(up->pmmu.vxproc);
-        mmapflush();
+                mmapflush(up->pmmu.us);
+        }
+}
+
+void
+usespace(Uspace *us)
+{
+        int i;
+        
+        for(i=0; i 0){
+                                ulist[i] = ulist[i-1];
+                                i--;
+                        }
+                        ulist[0] = us;
+                        break;
+                }
+}
+
+Uspace*
+getspace(Proc *p)
+{
+        Uspace *us;
+        
+        us = ulist[nuspace-1];
+        if(us->p){
+                if(tracemmu)
+                        print("^^^^^^^^^^ %ld %s [evict %d]\n", us->p->pid, us->p->text, us - uspace);
+                mmapflush(us);
+        }
+        us->p = p;
+        p->pmmu.vxmm.base = us->uzero;
+        p->pmmu.uzero = us->uzero;
+        p->pmmu.us = us;
+        usespace(us);
+        return us;
+}
+
+void
+ttakespace(Proc *p, Uspace *us)
+{
+        usespace(us);
+        if(us->p == p)
+                return;
+        if(tracemmu){
+                if(us->p)
+                        print("^^^^^^^^^^ %ld %s [steal %d]\n", us->p->pid, us->p->text, us - uspace);
+        }
+        us->p = p;
+        mmapflush(us);
+}
+
+void
+putspace(Uspace *us)
+{
+        int i;
+
+        mmapflush(us);
+        us->p->pmmu.us = nil;
+        us->p->pmmu.uzero = nil;
+        us->p->pmmu.vxmm.base = nil;
+        us->p = nil;
+        for(i=0; ikp && (mmup != p || p->newtlb || m->flushmmu)){
-                if(0) print("^^^^^^^^^^ %ld %s\n========== %ld %s\n",
-                        mmup ? mmup->pid : 0, mmup? mmup->text : "",
-                        p->pid, p->text);
-                /* No vxproc_flush - vxproc cache is okay */
-                mmapflush();
+        if(p->kp)
+                return;
+        
+        if(tracemmu)
+                print("mmuswitch %ld %s\n", p->pid, p->text);
+
+        if(p->pmmu.us && p->pmmu.us->p == p){
+                if(tracemmu) print("---------- %ld %s [%d]\n",
+                        p->pid, p->text, p->pmmu.us - uspace);
+                usespace(p->pmmu.us);
+                if(!p->newtlb && !m->flushmmu){
+                        usespace(p->pmmu.us);
+                        return;
+                }
+                mmapflush(p->pmmu.us);
                 p->newtlb = 0;
-                mmup = p;
+                return;
         }
+
+        if(p->pmmu.us == nil)
+                getspace(p);
+        else
+                takespace(p, p->pmmu.us);
+        if(tracemmu) print("========== %ld %s [%d]\n",
+                p->pid, p->text, p->pmmu.us - uspace);
 }
 
 /*
@@ -250,11 +340,16 @@ mmurelease(Proc *p)
 {
         if(p->kp)
                 return;
+        if(tracemmu)
+                print("mmurelease %ld %s\n", p->pid, p->text);
         if(p->pmmu.vxproc)
                 vxproc_flush(p->pmmu.vxproc);
-        if(p == mmup || m->flushmmu){
-                mmapflush();
-                mmup = nil;
+        if(p->pmmu.us){
+                if(tracemmu)
+                        print("^^^^^^^^^^ %ld %s [release %d]\n", p->pid, p->text, p->pmmu.us - uspace);
+                putspace(p->pmmu.us);
+                if(m->flushmmu)
+                        mmapflush(p->pmmu.us);
         }
 }
 
diff --git a/src/9vx/osx/screen.c b/src/9vx/osx/screen.c
@@ -316,7 +316,7 @@ mouseevent(EventRef event)
                 return eventNotHandledErr;
         }
 
-        mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
+        mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons|wheel, msec());
         return noErr;        
 }
 
@@ -407,6 +407,15 @@ kbdevent(EventRef event)
                         k = keycvt[code];
                 if(k >= 0)
                         latin1putc(k, kputc);
+                else{
+                        UniChar uc;
+                        OSStatus s;
+                        
+                        s = GetEventParameter(event, kEventParamKeyUnicodes,
+                                typeUnicodeText, nil, sizeof uc, nil, &uc);
+                        if(s == noErr)
+                                kputc(uc);
+                }
                 break;
 
         case kEventRawKeyModifiersChanged:
@@ -501,6 +510,7 @@ fullscreen(void)
 {
         static Ptr restore;
         static WindowRef oldwindow;
+        GDHandle device;
 
         if(osx.isfullscreen){
                 EndFullScreen(restore, 0);
@@ -510,7 +520,8 @@ fullscreen(void)
         }else{
                 HideWindow(osx.window);
                 oldwindow = osx.window;
-                BeginFullScreen(&restore, 0, 0, 0, &osx.window, 0, 0);
+                GetWindowGreatestAreaDevice(osx.window, kWindowTitleBarRgn, &device, nil);
+                BeginFullScreen(&restore, device, 0, 0, &osx.window, 0, 0);
                 osx.isfullscreen = 1;
                 osx.fullscreentime = msec();
         }
diff --git a/src/9vx/sched.c b/src/9vx/sched.c
@@ -61,7 +61,7 @@ noidlehands(void)
         if(m->machno == 0)
                 return;
         plock(&idling);
-        idlewakeup++;
+        idlewakeup = 1;
         pwakeup(&idling);
         punlock(&idling);
 }
@@ -154,7 +154,8 @@ runproc(void)
                 kprocq.tail = nil;
         kprocq.n--;
         if(traceprocs)
-                iprint("cpu%d: runproc %ld %s [%d %d]\n", m->machno, p->pid, p->text, kprocq.n, nrunproc);
+                iprint("cpu%d: runproc %ld %s [%d %d]\n",
+                        m->machno, p->pid, p->text, kprocq.n, nrunproc);
         unlock(&kprocq.lk);
         punlock(&run);
         return p;
@@ -162,48 +163,8 @@ runproc(void)
 
 /*
  * Host OS process sleep and wakeup.
- * This is complicated.
- *
- * Ideally, we'd just use a single pthread_cond_t, have everyone
- * pthread_cond_wait on it, and use pthread_cond_signal
- * to wake people up.  Unfortunately, that fails miserably
- * on OS X: sometimes the wakeups just plain get missed.
- * Perhaps it has something to do with all the signals that
- * are flying around.
- *
- * To work around the OS X pthreads problems, there is a
- * second implementation turned on by #defining PIPES to 1.
- * This implementation uses a pipe and reads and writes bytes
- * from the pipe to implement sleep and wakeup.  Perhaps not
- * surprisingly, the naive implementation of this hangs:
- * reads miss writes.  Instead, the actual implementation uses
- * select to poll whether the read would succeed, and once a
- * second it tries the read even if select doesn't think it will.
- * This timeout lets us make progress when an event gets missed
- * (happens only rarely).  This is enough to get things going on
- * OS X.
- *
- * On my Athlon 64 running Linux,
- * time to run mk -a in /sys/src/9/pc:
- *
- *         90s        default implementation (one pthread_cond_t)
- *         85s        WAITERS (pthread_cond_t for each waiter)
- *         88s        PIPES
- *
- * I implemented per-thread pthread_cond_t's to see if they
- * were any faster on non-OS X systems, but I can't see any
- * difference.  Running the WAITERS version on OS X causes
- * mysterious crashes.  I'm thoroughly confused.  
  */
-#define        PIPES        0
-#define        WAITERS        1
-
-#ifdef __APPLE__
-#undef        PIPES
-#define        PIPES        1
-#undef        WAITERS
-#define        WAITERS        0
-#endif
+static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER;
 
 struct Pwaiter
 {
@@ -215,78 +176,59 @@ struct Pwaiter
 void
 plock(Psleep *p)
 {
-        pthread_mutex_lock(&p->mutex);
-        if(!p->condinit){
-                p->condinit = 1;
-                pthread_cond_init(&p->cond, nil);
-        }
-#if PIPES
-        if(p->fd[1] == 0){
-                pipe(p->fd);
-                fcntl(p->fd[0], F_SETFL, fcntl(p->fd[0], F_GETFL)|O_NONBLOCK);
+        int r;
+
+        if(!p->init){
+                if((r = pthread_mutex_lock(&initmutex)) != 0)
+                        panic("pthread_mutex_lock initmutex: %d", r);
+                if(!p->init){
+                        p->init = 1;
+                        pthread_mutex_init(&p->mutex, nil);
+                }
+                if((r = pthread_mutex_unlock(&initmutex)) != 0)
+                        panic("pthread_mutex_unlock initmutex: %d", r);
         }
-#endif
+        if((r = pthread_mutex_lock(&p->mutex)) != 0)
+                panic("pthread_mutex_lock: %d", r);
 }
 
 void
 punlock(Psleep *p)
 {
-        pthread_mutex_unlock(&p->mutex);
+        int r;
+
+        if((r = pthread_mutex_unlock(&p->mutex)) != 0)
+                panic("pthread_mutex_unlock: %d", r);
 }
 
 void
 psleep(Psleep *p)
 {
-#if PIPES
-        p->nread++;
-        punlock(p);
-        char c;
-        while(read(p->fd[0], &c, 1) < 1){
-                struct pollfd pfd;
-                pfd.fd = p->fd[0];
-                pfd.events = POLLIN;
-                pfd.revents = 0;
-                poll(&pfd, 1, 1000);
-        }
-        plock(p);
-#elif WAITERS
+        int r;
         Pwaiter w;
+
         memset(&w, 0, sizeof w);
         pthread_cond_init(&w.cond, nil);
         w.next = p->waiter;
         p->waiter = &w;
         while(!w.awake)
-                pthread_cond_wait(&w.cond, &p->mutex);
+                if((r = pthread_cond_wait(&w.cond, &p->mutex)) != 0)
+                        panic("pthread_cond_wait: %d", r);
         pthread_cond_destroy(&w.cond);
-#else
-        pthread_cond_wait(&p->cond, &p->mutex);
-#endif
 }
 
 void
 pwakeup(Psleep *p)
 {
-#if PIPES
-        char c = 0;
-        int nbad = 0;
-        if(p->nwrite < p->nread){
-                p->nwrite++;
-                while(write(p->fd[1], &c, 1) < 1){
-                        if(++nbad%100 == 0)
-                                iprint("pwakeup: write keeps failing\n");
-                }
-        }
-#elif WAITERS
+        int r;
         Pwaiter *w;
 
         w = p->waiter;
         if(w){
                 p->waiter = w->next;
                 w->awake = 1;
-                pthread_cond_signal(&w->cond);
+                if((r = pthread_cond_signal(&w->cond)) != 0)
+                        panic("pthread_cond_signal: %d", r);
         }
-#else
-        pthread_cond_signal(&p->cond);
-#endif
 }
 
diff --git a/src/9vx/stub.c b/src/9vx/stub.c
@@ -276,18 +276,19 @@ _tas(void *x)
 int
 lock(Lock *lk)
 {
-        int i, printed;
+        int i, j, printed;
         
         for(i=0; i<1000; i++){
                 if(canlock(lk))
                         return 1;
                 sched_yield();
         }
-        for(i=0; i<100; i++){
-                if(canlock(lk))
-                        return 1;
-                microdelay(10);
-        }
+        for(j=10; j<=1000; j*=10)
+                for(i=0; i<10; i++){
+                        if(canlock(lk))
+                                return 1;
+                        microdelay(j);
+                }
         printed = 0;
         for(;;){
                 if(canlock(lk))
@@ -295,7 +296,7 @@ lock(Lock *lk)
                 if(!printed++)
                         iprint("cpu%d deadlock? %p caller=%p\n",
                                 m->machno, lk, getcallerpc(&lk));
-                microdelay(1000000);
+                microdelay(10000);
         }
         return 0;
 }
@@ -497,11 +498,14 @@ panic(char *fmt, ...)
         buf[n] = '\n';
         write(2, buf, n+1);
         if(doabort){
-#ifndef __APPLE__
-                abort();
-#endif
+#ifdef __APPLE__
+                fprint(2, "sleeping, so you can attach gdb to pid %d\n", (int)getpid());
                 for(;;)
                         microdelay(1000000);
+#else
+                fprint(2, "aborting, to dump core.\n");
+                abort();
+#endif
         }
         exit(0);
 }
diff --git a/src/9vx/term.c b/src/9vx/term.c
@@ -208,6 +208,7 @@ termscroll(void)
         r1.min.y += dy;
         memimagedraw(term.screen, r0, term.screen, r1.min,
                 memopaque, ZP, S);
+        r1.min.y = r0.max.y;
         memimagedraw(term.screen, r1, term.bg, ZP, memopaque, ZP, S);
         addflush(r0);
         addflush(r1);
diff --git a/src/9vx/trap.c b/src/9vx/trap.c
@@ -30,7 +30,7 @@ kexit(Ureg *ureg)
         Tos *tos;
 
         /* precise time accounting, kernel exit */
-        tos = (Tos*)(uzero+USTKTOP-sizeof(Tos));
+        tos = (Tos*)(up->pmmu.uzero+USTKTOP-sizeof(Tos));
         cycles(&t);
         tos->kcycles += t - up->kentry;
         tos->pcycles = up->pcycles;
@@ -90,7 +90,7 @@ trap(Ureg *ureg)
         
         case VXTRAP_SOFT+0x40:        /* int $0x40 - system call */
                 if(tracesyscalls){
-                        ulong *sp = (ulong*)(uzero + ureg->usp);
+                        ulong *sp = (ulong*)(up->pmmu.uzero + ureg->usp);
                         print("%d [%s] %s %#lux %08lux %08lux %08lux %08lux\n",
                                 up->pid, up->text,
                                 sysctab[ureg->ax], sp[0], sp[1], sp[2], sp[3]);
@@ -262,7 +262,7 @@ syscall(Ureg *ureg)
         up->psstate = 0;
 
         if(scallnr == NOTED)
-                noted(ureg, *(ulong*)(uzero + sp+BY2WD));
+                noted(ureg, *(ulong*)(up->pmmu.uzero + sp+BY2WD));
 
         if(scallnr!=RFORK && (up->procctl || up->nnote)){
                 splhi();
@@ -335,6 +335,8 @@ notify(Ureg* ureg)
                 pexit("Suicide", 0);
         }
 
+        uchar *uzero;
+        uzero = up->pmmu.uzero;
         upureg = (void*)(uzero + sp);
         memmove(upureg, ureg, sizeof(Ureg));
         *(ulong*)(uzero + sp-BY2WD) = up->ureg;        /* word under Ureg is old up->ureg */
@@ -383,6 +385,8 @@ noted(Ureg* ureg, ulong arg0)
                 pexit("Suicide", 0);
         }
         
+        uchar *uzero;
+        uzero = up->pmmu.uzero;
         oureg = up->ureg;
         nureg = (Ureg*)(uzero + up->ureg);
 
@@ -442,11 +446,11 @@ execregs(ulong entry, ulong ssize, ulong nargs)
         up->fpstate = FPinit;
         fpoff();
 
-        sp = (ulong*)(uzero + USTKTOP - ssize);
+        sp = (ulong*)(up->pmmu.uzero + USTKTOP - ssize);
         *--sp = nargs;
 
         ureg = up->dbgreg;
-        ureg->usp = (uchar*)sp - uzero;
+        ureg->usp = (uchar*)sp - up->pmmu.uzero;
 //showexec(ureg->usp);
         ureg->pc = entry;
         return USTKTOP-sizeof(Tos);                /* address of kernel/user shared data */
diff --git a/src/9vx/vx32.c b/src/9vx/vx32.c
@@ -17,7 +17,6 @@
 #include "u.h"
 #include 
 #include 
-#include "libvx32/vx32.h"
 #include "lib.h"
 #include "mem.h"
 #include "dat.h"
@@ -50,47 +49,6 @@ vx32sysr1(void)
 }
 
 /*
- * Vxnewproc is called at the end of newproc
- * to fill in vx32-specific entries in the Proc struct
- * before it gets used.
- */
-void
-vxnewproc(Proc *p)
-{
-        PMMU *pm;
-        
-        pm = &p->pmmu;
-
-        /*
-         * Kernel procs don't need vxprocs; if this proc
-         * already has one, take it away.  Also, give
-         * kernel procs very large stacks so they can call
-         * into non-thread-friendly routines like x11 
-         * and getgrgid.
-         */
-        if(p->kp){
-                if(pm->vxproc){
-                        pm->vxproc->mem = nil;
-                        vxproc_free(pm->vxproc);
-                        pm->vxproc = nil;
-                }
-                free(p->kstack);
-                p->kstack = nil;
-                p->kstack = smalloc(512*1024);
-                return;
-        }
-
-        pm->lo = 0x80000000UL;
-        pm->hi = 0;
-        if(pm->vxproc == nil){
-                pm->vxproc = vxproc_alloc();
-                if(pm->vxproc == nil)
-                        panic("vxproc_alloc");
-                pm->vxproc->mem = &thevxmem;
-        }
-}
-
-/*
  * Vx32 hooks to read, write, map, unmap, and check permissions
  * on user memory.  Normally these are more involved, but we're
  * using the processor to do everything.
@@ -98,29 +56,21 @@ vxnewproc(Proc *p)
 static ssize_t
 vmread(vxmem *vm, void *data, uint32_t addr, uint32_t len)
 {
-        memmove(data, uzero+addr, len);
+        memmove(data, vm->mapped->base+addr, len);
         return len;
 }
 
 static ssize_t
 vmwrite(vxmem *vm, const void *data, uint32_t addr, uint32_t len)
 {
-        memmove(uzero+addr, data, len);
+        memmove(vm->mapped->base+addr, data, len);
         return len;
 }
 
-static vxmmap thevxmmap =
-{
-        1,
-        (void*)-1,        /* to be filled in with user0 */
-        USTKTOP,
-};
-
 static vxmmap*
 vmmap(vxmem *vm, uint32_t flags)
 {
-        thevxmmap.base = uzero;
-        return &thevxmmap;
+        return vm->mapped;
 }
 
 static void
@@ -131,6 +81,14 @@ vmunmap(vxmem *vm, vxmmap *mm)
 static int
 vmcheckperm(vxmem *vm, uint32_t addr, uint32_t len, uint32_t perm, uint32_t *out_faultva)
 {
+        if(addr >= USTKTOP){
+                *out_faultva = addr;
+                return 0;
+        }
+        if(addr+len < addr || addr +len > USTKTOP){
+                *out_faultva = USTKTOP;
+                return 0;
+        }
         /* All is allowed - handle faults as they happen. */
         return 1;
 }
@@ -164,6 +122,50 @@ static vxmem thevxmem =
         vmfree,
 };
 
+/*
+ * Vxnewproc is called at the end of newproc
+ * to fill in vx32-specific entries in the Proc struct
+ * before it gets used.
+ */
+void
+vxnewproc(Proc *p)
+{
+        PMMU *pm;
+        
+        pm = &p->pmmu;
+
+        /*
+         * Kernel procs don't need vxprocs; if this proc
+         * already has one, take it away.  Also, give
+         * kernel procs very large stacks so they can call
+         * into non-thread-friendly routines like x11 
+         * and getgrgid.
+         */
+        if(p->kp){
+                if(pm->vxproc){
+                //        vxunmap(p);
+                        assert(pm->uzero == nil);
+                        pm->vxproc->mem = nil;
+                        vxproc_free(pm->vxproc);
+                        pm->vxproc = nil;
+                }
+                free(p->kstack);
+                p->kstack = nil;
+                p->kstack = smalloc(512*1024);
+                return;
+        }
+
+        if(pm->vxproc == nil){
+                pm->vxproc = vxproc_alloc();
+                if(pm->vxproc == nil)
+                        panic("vxproc_alloc");
+                pm->vxproc->mem = &pm->vxmem;
+                pm->vxmem = thevxmem;
+                pm->vxmem.mapped = &pm->vxmm;
+                memset(&pm->vxmm, 0, sizeof pm->vxmm);
+        }
+}
+
 static void
 setclock(int start)
 {
@@ -224,7 +226,7 @@ touser(void *initsp)
                  * Optimization: try to fault in code page and stack
                  * page right now, since we're likely to need them.
                  */
-                if(up->pmmu.hi == 0){
+                if(up->pmmu.us->hi == 0){
                         fault(vp->cpu->eip, 1);
                         fault(vp->cpu->reg[ESP], 0);
                 }
@@ -267,11 +269,11 @@ touser(void *initsp)
                         addr = (uchar*)vp->cpu->trapva;
                         if(traceprocs)
                                 print("fault %p read=%d\n", addr, read);
-                        if(isuaddr(addr) && fault(addr - uzero, read) >= 0)
+                        if(isuaddr(addr) && fault(addr - up->pmmu.uzero, read) >= 0)
                                 continue;
                         print("%ld %s: unhandled fault va=%lux [%lux] eip=%lux\n",
                                 up->pid, up->text,
-                                addr - uzero, vp->cpu->trapva, vp->cpu->eip);
+                                addr - up->pmmu.uzero, vp->cpu->trapva, vp->cpu->eip);
                         proc2ureg(vp, &u);
                         dumpregs(&u);
                         if(doabort)
diff --git a/src/BUGS b/src/BUGS
@@ -9,6 +9,4 @@ need to do a better job mapping SIGFPE back to hardware floating-point errors
 ---
 gs spins on startup, cause unknown.  (run "gs", no arguments)
 
-blank line inserted when kernel terminal scrolls
-
 can cause sigsegv panic resizing the window, rarely