1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "io.h" 7 8 /* 9 * We have one page table per processor. 10 * 11 * Different processes are distinguished via the VSID field in 12 * the segment registers. As flushing the entire page table is an 13 * expensive operation, we implement an aging algorithm for 14 * mmu pids, with a background kproc to purge stale pids en mass. 15 * 16 * This needs modifications to run on a multiprocessor. 17 */ 18 19 static ulong ptabsize; /* number of bytes in page table */ 20 static ulong ptabmask; /* hash mask */ 21 22 /* 23 * VSID is 24 bits. 3 are required to distinguish segments in user 24 * space (kernel space only uses the BATs). pid 0 is reserved. 25 * The top 2 bits of the pid are used as a `color' for the background 26 * pid reclaimation algorithm. 27 */ 28 29 enum { 30 PIDBASE = 1, 31 PIDBITS = 21, 32 COLBITS = 2, 33 PIDMAX = ((1<<PIDBITS)-1), 34 COLMASK = ((1<<COLBITS)-1), 35 }; 36 37 #define VSID(pid, i) (((pid)<<3)|i) 38 #define PIDCOLOR(pid) ((pid)>>(PIDBITS-COLBITS)) 39 #define PTECOL(color) PTE0(1, VSID(((color)<<(PIDBITS-COLBITS)), 0), 0, 0) 40 41 void 42 mmuinit(void) 43 { 44 int lhash, mem; 45 extern ulong memsize; /* passed in from ROM monitor */ 46 47 if(ptabsize == 0) { 48 /* heuristically size the hash table */ 49 lhash = 10; 50 mem = (1<<23); 51 while(mem < memsize) { 52 lhash++; 53 mem <<= 1; 54 } 55 ptabsize = (1<<(lhash+6)); 56 ptabmask = (1<<lhash)-1; 57 } 58 59 m->ptabbase = (ulong)xspanalloc(ptabsize, 0, ptabsize); 60 putsdr1(PADDR(m->ptabbase) | (ptabmask>>10)); 61 m->mmupid = PIDBASE; 62 m->sweepcolor = 0; 63 m->trigcolor = COLMASK; 64 } 65 66 static int 67 work(void*) 68 { 69 return PIDCOLOR(m->mmupid) == m->trigcolor; 70 } 71 72 void 73 mmusweep(void*) 74 { 75 Proc *p; 76 int i, x, sweepcolor; 77 ulong *ptab, *ptabend, ptecol; 78 79 for(;;) { 80 if(PIDCOLOR(m->mmupid) != m->trigcolor) 81 sleep(&m->sweepr, work, nil); 82 83 sweepcolor = m->sweepcolor; 84 x = splhi(); 85 p = proctab(0); 86 for(i = 0; i < conf.nproc; i++, p++) 87 if(PIDCOLOR(p->mmupid) == sweepcolor) 88 p->mmupid = 0; 89 splx(x); 90 91 ptab = (ulong*)m->ptabbase; 92 ptabend = (ulong*)(m->ptabbase+ptabsize); 93 ptecol = PTECOL(sweepcolor); 94 while(ptab < ptabend) { 95 if((*ptab & PTECOL(3)) == ptecol) 96 *ptab = 0; 97 ptab += 2; 98 } 99 tlbflushall(); 100 101 m->sweepcolor = (sweepcolor+1) & COLMASK; 102 m->trigcolor = (m->trigcolor+1) & COLMASK; 103 } 104 } 105 106 int 107 newmmupid(void) 108 { 109 int pid, newcolor; 110 111 pid = m->mmupid++; 112 if(m->mmupid > PIDMAX) 113 m->mmupid = PIDBASE; 114 newcolor = PIDCOLOR(m->mmupid); 115 if(newcolor != PIDCOLOR(pid)) { 116 if(newcolor == m->sweepcolor) { 117 /* desperation time. can't block here. punt to fault/putmmu */ 118 print("newmmupid: %uld: no free mmu pids\n", up->pid); 119 if(m->mmupid == PIDBASE) 120 m->mmupid = PIDMAX; 121 else 122 m->mmupid--; 123 pid = 0; 124 } 125 else if(newcolor == m->trigcolor) 126 wakeup(&m->sweepr); 127 } 128 up->mmupid = pid; 129 return pid; 130 } 131 132 void 133 flushmmu(void) 134 { 135 int x; 136 137 x = splhi(); 138 up->newtlb = 1; 139 mmuswitch(up); 140 splx(x); 141 } 142 143 /* 144 * called with splhi 145 */ 146 void 147 mmuswitch(Proc *p) 148 { 149 int i, mp; 150 151 if(p->kp) { 152 for(i = 0; i < 8; i++) 153 putsr(i<<28, 0); 154 return; 155 } 156 157 if(p->newtlb) { 158 p->mmupid = 0; 159 p->newtlb = 0; 160 } 161 mp = p->mmupid; 162 if(mp == 0) 163 mp = newmmupid(); 164 165 for(i = 0; i < 8; i++) 166 putsr(i<<28, VSID(mp, i)|BIT(1)|BIT(2)); 167 } 168 169 void 170 mmurelease(Proc* p) 171 { 172 p->mmupid = 0; 173 } 174 175 void 176 putmmu(ulong va, ulong pa, Page *pg) 177 { 178 int mp; 179 char *ctl; 180 ulong *p, *ep, *q, pteg; 181 ulong vsid, ptehi, x, hash; 182 183 /* 184 * If mmupid is 0, mmuswitch/newmmupid was unable to assign us 185 * a pid, hence we faulted. Keep calling sched() until the mmusweep 186 * proc catches up, and we are able to get a pid. 187 */ 188 while((mp = up->mmupid) == 0) 189 sched(); 190 191 vsid = VSID(mp, va>>28); 192 hash = (vsid ^ (va>>12)&0xffff) & ptabmask; 193 ptehi = PTE0(1, vsid, 0, va); 194 195 pteg = m->ptabbase + BY2PTEG*hash; 196 p = (ulong*)pteg; 197 ep = (ulong*)(pteg+BY2PTEG); 198 q = nil; 199 tlbflush(va); 200 while(p < ep) { 201 x = p[0]; 202 if(x == ptehi) { 203 q = p; 204 break; 205 } 206 if(q == nil && (x & BIT(0)) == 0) 207 q = p; 208 p += 2; 209 } 210 if(q == nil) { 211 q = (ulong*)(pteg+m->slotgen); 212 m->slotgen = (m->slotgen + BY2PTE) & (BY2PTEG-1); 213 } 214 q[0] = ptehi; 215 q[1] = pa; 216 sync(); 217 218 ctl = &pg->cachectl[m->machno]; 219 switch(*ctl) { 220 case PG_NEWCOL: 221 default: 222 panic("putmmu: %d\n", *ctl); 223 break; 224 case PG_NOFLUSH: 225 break; 226 case PG_TXTFLUSH: 227 dcflush((void*)pg->va, BY2PG); 228 icflush((void*)pg->va, BY2PG); 229 *ctl = PG_NOFLUSH; 230 break; 231 } 232 } 233 234 void 235 checkmmu(ulong, ulong) 236 { 237 } 238 239 void 240 countpagerefs(ulong*, int) 241 { 242 } 243 244 /* 245 * Return the number of bytes that can be accessed via KADDR(pa). 246 * If pa is not a valid argument to KADDR, return 0. 247 */ 248 ulong 249 cankaddr(ulong pa) 250 { 251 ulong kzero; 252 253 kzero = -KZERO; 254 if(pa >= kzero) 255 return 0; 256 return kzero - pa; 257 } 258