xref: /plan9-contrib/sys/src/9/vt4/mmu.c (revision d6dfd9ef91cf0fa8514a249d5f2a550978c19369)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 
7 /*
8  * l.s has set some global TLB entries for the kernel at
9  * the top of the tlb (NTLB-1 down), partly to account for
10  * the way the firmware sets things up on some platforms (eg, 440).
11  * The first entry not used by the kernel (eg, by kmapphys) is m->utlbhi.
12  * User tlbs are assigned indices from 0 to m->utlbhi, round-robin.
13  * m->utlbnext is the next to use.  Kernel tlbs are added at utlbhi, which then moves.
14  *
15  * In this version, the kernel TLB entries are in zone 0,
16  * and user pages are in zone 1.  The kernel entries are also PID 0 (global)
17  * so they are usable as the PID changes (0=no user; non-zero=user process mapped).
18   */
19 
20 enum {
21 	DEBUG = 0,
22 	Minutlb=	NTLB/2,	/* at least this many for user process */
23 };
24 
25 Softtlb	softtlb[MAXMACH][STLBSIZE];
26 
27 static int	newtlbpid(Proc*);
28 static void	purgetlb(int);
29 static void putstlb(int, u32int, u32int);
30 
31 /* clobbers SPR_PID */
32 static int
probehi(uintptr va)33 probehi(uintptr va)
34 {
35 	int i;
36 
37 	for (i = 0; i < NTLBPID; i++) {
38 		putpid(i);
39 		if (tlbsxcc(va) >= 0)
40 			return i;
41 	}
42 	return -1;
43 }
44 
45 void
tlbdump(char * s)46 tlbdump(char *s)
47 {
48 	int i, p, ppid;
49 	u32int hi, lo;
50 
51 	p = getpid();
52 	iprint("tlb[%s] pid %d lastpid %d\n", s, p, m->lastpid);
53 	for(i=0; i<NTLB; i++){
54 		hi = tlbrehi(i);
55 		if(hi & TLBVALID){
56 			ppid = probehi(TLBEPN(hi));
57 			lo = tlbrelo(i);
58 			iprint("%.2d hi %#8.8ux pid %d lo %#8.8ux\n",
59 				i, hi, ppid, lo);
60 		}
61 		if(i == m->utlbhi)
62 			iprint("-----\n");
63 	}
64 	putpid(p);	/* tlbrehi changes PID */
65 }
66 
67 /*
68  * l.s provides a set of tlb entries for the kernel, allocating
69  * them starting at the highest index down.  user-level entries
70  * will be allocated from index 0 up.
71  */
72 void
mmuinit(void)73 mmuinit(void)
74 {
75 	int i;
76 
77 	if(DEBUG)
78 		tlbdump("init0");
79 	m->lastpid = 0;
80 	m->utlbhi = 0;
81 	for(i = 0; i < NTLB; i++){
82 		if(tlbrehi(i) & TLBVALID)
83 			break;
84 		m->utlbhi = i;
85 		tlbwrx(i, 0, 0);
86 	}
87 	if(DEBUG)
88 		tlbdump("init1");
89 	putpid(0);
90 
91 	m->stlb = softtlb[m->machno];
92 	m->pstlb = PADDR(m->stlb);
93 
94 	/*
95 	 * set OCM mapping, assuming:
96 	 *	caches were invalidated earlier;
97 	 *	and we aren't currently using it
98 	 * must also set a tlb entry that validates the virtual address but
99 	 * the translation is not used (see p. 5-2)
100 	 */
101 /*
102 	putdcr(Isarc, OCMZERO);
103 	putdcr(Dsarc, OCMZERO);
104 	putdcr(Iscntl, Isen);
105 	putdcr(Iscntl, Dsen|Dof);
106 	tlbwrx(tlbx, OCMZERO|TLB4K|TLBVALID, OCMZERO|TLBZONE(0)|TLBWR|TLBEX|TLBI);
107 	tlbx--;
108 */
109 }
110 
111 void
flushmmu(void)112 flushmmu(void)
113 {
114 	int x;
115 
116 	x = splhi();
117 	up->newtlb = 1;
118 	mmuswitch(up);
119 	splx(x);
120 }
121 
122 /*
123  * called with splhi
124  */
125 void
mmuswitch(Proc * p)126 mmuswitch(Proc *p)
127 {
128 	int tp;
129 
130 	if(p->newtlb){
131 		p->mmupid = 0;
132 		p->newtlb = 0;
133 	}
134 	tp = p->mmupid;
135 	if(tp == 0 && !p->kp)
136 		tp = newtlbpid(p);
137 	putpid(tp);
138 }
139 
140 void
mmurelease(Proc * p)141 mmurelease(Proc* p)
142 {
143 	p->mmupid = 0;
144 	putpid(0);
145 }
146 
147 void
putmmu(uintptr va,uintptr pa,Page * page)148 putmmu(uintptr va, uintptr pa, Page* page)
149 {
150 	int x, s, tp;
151 	char *ctl;
152 	u32int tlbhi, tlblo;
153 
154 	if(va >= KZERO)
155 		panic("mmuput");
156 
157 	tlbhi = TLBEPN(va) | TLB4K | TLBVALID;
158 	tlblo = TLBRPN(pa) | TLBEX | TLBZONE(1);	/* user page */
159 	if(pa & PTEWRITE)
160 		tlblo |= TLBWR;
161 	/* must not set TLBG on instruction pages */
162 	if(pa & PTEUNCACHED)
163 		tlblo |= TLBI;
164 	/* else use write-back cache; write-through would need TLBW set */
165 
166 	s = splhi();
167 	tp = up->mmupid;
168 	if(tp == 0){
169 		if(up->kp)
170 			panic("mmuput kp");
171 		tp = newtlbpid(up);
172 		putpid(tp);
173 	}else if(getpid() != tp)
174 		panic("mmuput pid %#ux %#ux", tp, getpid());
175 
176 	/* see if it's already there: note that tlbsx[cc] uses current PID */
177 	x = tlbsxcc(va);
178 	if(x < 0){
179 		if(m->utlbnext > m->utlbhi)
180 			m->utlbnext = 0;
181 		x = m->utlbnext++;
182 	}else if(x > m->utlbhi)		/* shouldn't touch kernel entries */
183 		panic("mmuput index va=%#p x=%d klo=%d", va, x, m->utlbhi);
184 	if(DEBUG)
185 		iprint("put %#p %#p-> %d: %#ux %8.8ux\n", va, pa, x, tlbhi, tlblo);
186 	barriers(); sync(); isync();
187 	tlbwrx(x, tlbhi, tlblo);
188 	putstlb(tp, TLBEPN(va), tlblo);
189 	barriers(); sync(); isync();
190 	/* verify that tlb entry was written into the tlb okay */
191 	if (tlbsxcc(va) < 0)
192 		panic("tlb entry for va %#lux written into but not received by tlb",
193 			va);
194 	splx(s);
195 
196 	ctl = &page->cachectl[m->machno];
197 	switch(*ctl){
198 	case PG_TXTFLUSH:
199 		dcflush(page->va, BY2PG);
200 		icflush(page->va, BY2PG);
201 		*ctl = PG_NOFLUSH;
202 		break;
203 	case PG_DATFLUSH:
204 		dcflush(page->va, BY2PG);
205 		*ctl = PG_NOFLUSH;
206 		break;
207 	case PG_NEWCOL:
208 //		cleancache();	/* expensive, but fortunately not needed here */
209 		*ctl = PG_NOFLUSH;
210 		break;
211 	}
212 }
213 
214 /*
215  * Process must be splhi
216  */
217 static int
newtlbpid(Proc * p)218 newtlbpid(Proc *p)
219 {
220 	int i, s;
221 	Proc **h;
222 
223 	i = m->lastpid;
224 	h = m->pidproc;
225 	for(s = 0; s < NTLBPID; s++) {
226 		i++;
227 		if(i >= NTLBPID)
228 			i = 1;
229 		if(h[i] == nil)
230 			break;
231 	}
232 
233 	if(h[i] != nil){
234 		purgetlb(i);
235 		if(h[i] != nil)
236 			panic("newtlb");
237 	}
238 
239 	m->pidproc[i] = p;
240 	p->mmupid = i;
241 	m->lastpid = i;
242 
243 	return i;
244 }
245 
246 static void
purgetlb(int pid)247 purgetlb(int pid)
248 {
249 	int i, p;
250 	Proc *sp, **pidproc;
251 	Softtlb *entry, *etab;
252 	u32int hi;
253 
254 	m->tlbpurge++;
255 
256 	/*
257 	 * find all pid entries that are no longer used by processes
258 	 */
259 	pidproc = m->pidproc;
260 	for(i=1; i<NTLBPID; i++) {
261 		sp = pidproc[i];
262 		if(sp != nil && sp->mmupid != i)
263 			pidproc[i] = nil;
264 	}
265 
266 	/*
267 	 * shoot down the one we want
268 	 */
269 	sp = pidproc[pid];
270 	if(sp != nil)
271 		sp->mmupid = 0;
272 	pidproc[pid] = nil;
273 
274 	/*
275 	 * clean out all dead pids from the stlb;
276 	 */
277 	entry = m->stlb;
278 	for(etab = &entry[STLBSIZE]; entry < etab; entry++)
279 		if(pidproc[(entry->hi>>2)&0xFF] == nil){
280 			entry->hi = 0;
281 			entry->lo = 0;
282 		}
283 
284 	/*
285 	 * clean up the hardware
286 	 */
287 	p = getpid();
288 	for(i = 0; i <= m->utlbhi; i++){
289 		hi = tlbrehi(i);
290 		if(hi & TLBVALID && pidproc[getpid()] == nil)
291 			tlbwrx(i, 0, 0);
292 	}
293 	putpid(p);
294 }
295 
296 /*
297  * return required size and alignment to map n bytes in a tlb entry
298  */
299 ulong
mmumapsize(ulong n)300 mmumapsize(ulong n)
301 {
302 	ulong size;
303 	int i;
304 
305 	size = 1024;
306 	for(i = 0; i < 8 && size < n; i++)
307 		size <<= 2;
308 	return size;
309 }
310 
311 /*
312  * map a physical addresses at pa to va, with the given attributes.
313  * the virtual address must not be mapped already.
314  * if va is nil, map it at pa in virtual space.
315  */
316 void*
kmapphys(uintptr va,uintptr pa,ulong nb,ulong attr,ulong le)317 kmapphys(uintptr va, uintptr pa, ulong nb, ulong attr, ulong le)
318 {
319 	int s, i, p;
320 	ulong size;
321 
322 	if(va == 0)
323 		va = pa;	/* simplest is to use a 1-1 map */
324 	size = 1024;
325 	for(i = 0; i < 8 && size < nb; i++)
326 		size <<= 2;
327 	if(i >= 8)
328 		return 0;
329 	if(m->utlbhi <= Minutlb)
330 		panic("kmapphys");
331 	s = splhi();
332 	p = getpid();
333 	putpid(0);
334 	tlbwrx(m->utlbhi, va | (i<<7) | TLBVALID | le, pa | TLBZONE(0) | attr);
335 	m->utlbhi--;
336 	putpid(p);
337 	splx(s);
338 	if(DEBUG)
339 		tlbdump("kmapphys");
340 
341 	return UINT2PTR(va);
342 }
343 
344 /*
345  * return an uncached alias for the memory at a
346  * (unused)
347 void*
348 mmucacheinhib(void* a, ulong nb)
349 {
350 	uintptr pa;
351 
352 	if(a == nil)
353 		return nil;
354 	dcflush(PTR2UINT(a), nb);
355 	pa = PADDR(a);
356 	return kmapphys(KSEG1|pa, pa, nb, TLBWR | TLBI | TLBG, 0);
357 }
358  */
359 
360 static void
putstlb(int pid,u32int va,u32int tlblo)361 putstlb(int pid, u32int va, u32int tlblo)
362 {
363 	Softtlb *entry;
364 
365 	pid <<= 2;
366 	entry = &m->stlb[((va>>12)^pid)&(STLBSIZE-1)];
367 	entry->hi = va | pid;
368 	entry->lo = tlblo;
369 }
370 
371 /*
372  * Return the number of bytes that can be accessed via KADDR(pa).
373  * If pa is not a valid argument to KADDR, return 0.
374  */
375 uintptr
cankaddr(uintptr pa)376 cankaddr(uintptr pa)
377 {
378 	if( /* pa >= PHYSDRAM && */ pa < PHYSDRAM + 512*MiB)
379 		return PHYSDRAM + 512*MiB - pa;
380 	return 0;
381 }
382