#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" /* * The page table is shared across all processes and processors * (hence needs to be locked for updates on a multiprocessor). * Different processes are distinguished via the VSID field in * the segment registers. As flushing the entire page table is an * expensive operation, we implement an aging algorithm for * mmu pids, with a background kproc to purge stale pids en mass. */ static ulong ptabsize; /* number of bytes in page table */ static ulong ptabmask; /* hash mask */ /* * VSID is 24 bits. 3 are required to distinguish segments in user * space (kernel space only uses the BATs). */ #define VSID(pid, i) (((pid)<<3)|i) enum { PIDBASE = 1, PIDMAX = ((1<<21)-1), }; void mmuinit(void) { int lhash, mem; extern ulong memsize; /* passed in from ROM monitor */ if(ptabsize == 0) { /* heuristically size the hash table */ lhash = 10; mem = (1<<23); while(mem < memsize) { lhash++; mem <<= 1; } ptabsize = (1<<(lhash+6)); ptabmask = (1<ptabbase = (ulong)xspanalloc(ptabsize, 0, ptabsize); putsdr1(PADDR(m->ptabbase) | (ptabmask>>10)); m->mmupid = PIDBASE; } void flushmmu(void) { int x; x = splhi(); up->newtlb = 1; mmuswitch(up); splx(x); } /* * called with splhi */ void mmuswitch(Proc *p) { int i, mp; if(p->kp) { for(i = 0; i < 8; i++) putsr(i<<28, 0); return; } if(p->newtlb) { p->mmupid = 0; p->newtlb = 0; } mp = p->mmupid; if(mp == 0) mp = newmmupid(); for(i = 0; i < 8; i++) putsr(i<<28, VSID(mp, i)|BIT(1)|BIT(2)); } void mmurelease(Proc* p) { p->mmupid = 0; } void putmmu(ulong va, ulong pa, Page *pg) { int mp; char *ctl; ulong *p, *ep, *q, pteg; ulong vsid, ptehi, x, hash; mp = up->mmupid; if(mp == 0) panic("putmmu pid"); vsid = VSID(mp, va>>28); hash = (vsid ^ (va>>12)&0xffff) & ptabmask; ptehi = PTE0(1, vsid, 0, va); pteg = m->ptabbase + BY2PTEG*hash; p = (ulong*)pteg; ep = (ulong*)(pteg+BY2PTEG); q = nil; tlbflush(va); while(p < ep) { x = p[0]; if(x == ptehi) { q = p; if(q[1] == pa) print("putmmu already set pte\n"); break; } if(q == nil && (x & BIT(0)) == 0) q = p; p += 2; } if(q == nil) { q = (ulong*)(pteg+m->slotgen); m->slotgen = (m->slotgen + BY2PTE) & (BY2PTEG-1); } q[0] = ptehi; q[1] = pa; sync(); ctl = &pg->cachectl[m->machno]; switch(*ctl) { case PG_NEWCOL: default: panic("putmmu: %d\n", *ctl); break; case PG_NOFLUSH: break; case PG_TXTFLUSH: dcflush((void*)pg->va, BY2PG); icflush((void*)pg->va, BY2PG); *ctl = PG_NOFLUSH; break; } } int newmmupid(void) { int pid; pid = m->mmupid++; if(m->mmupid > PIDMAX) panic("ran out of mmu pids"); // m->mmupid = PIDBASE; up->mmupid = pid; return pid; }