xref: /plan9/sys/src/9/ppc/mmu.c (revision c717cbbd69b248e6faa1e4a778f20935703bf353)
1458db832SDavid du Colombier #include	<u.h>
2458db832SDavid du Colombier #include	<ureg.h>
3458db832SDavid du Colombier #include	"../port/lib.h"
4458db832SDavid du Colombier #include	"mem.h"
5458db832SDavid du Colombier #include	"dat.h"
6458db832SDavid du Colombier #include	"fns.h"
7458db832SDavid du Colombier 
8458db832SDavid du Colombier /*
9458db832SDavid du Colombier  *	We have one page table per processor.
10458db832SDavid du Colombier  *
11458db832SDavid du Colombier  *	Different processes are distinguished via the VSID field in
12458db832SDavid du Colombier  *	the segment registers.  As flushing the entire page table is an
13458db832SDavid du Colombier  *	expensive operation, we implement an aging algorithm for
14458db832SDavid du Colombier  *	mmu pids, with a background kproc to purge stale pids en mass.
15458db832SDavid du Colombier  *
16458db832SDavid du Colombier  *	This needs modifications to run on a multiprocessor.
17458db832SDavid du Colombier  */
18458db832SDavid du Colombier 
19458db832SDavid du Colombier static ulong	ptabsize;			/* number of bytes in page table */
20458db832SDavid du Colombier static ulong	ptabmask;		/* hash mask */
21458db832SDavid du Colombier 
22458db832SDavid du Colombier /*
23458db832SDavid du Colombier  *	VSID is 24 bits.  3 are required to distinguish segments in user
24458db832SDavid du Colombier  *	space (kernel space only uses the BATs).  pid 0 is reserved.
25458db832SDavid du Colombier  *	The top 2 bits of the pid are used as a `color' for the background
26a6756dc4SDavid du Colombier  *	pid reclamation algorithm.
27458db832SDavid du Colombier  */
28458db832SDavid du Colombier 
29458db832SDavid du Colombier enum {
30458db832SDavid du Colombier 	PIDBASE = 1,
31458db832SDavid du Colombier 	PIDBITS = 21,
32458db832SDavid du Colombier 	COLBITS = 2,
33458db832SDavid du Colombier 	PIDMAX = ((1<<PIDBITS)-1),
34458db832SDavid du Colombier 	COLMASK = ((1<<COLBITS)-1),
35458db832SDavid du Colombier };
36458db832SDavid du Colombier 
37458db832SDavid du Colombier #define	VSID(pid, i)	(((pid)<<3)|i)
38458db832SDavid du Colombier #define	PIDCOLOR(pid)	((pid)>>(PIDBITS-COLBITS))
39458db832SDavid du Colombier #define	PTECOL(color)	PTE0(1, VSID(((color)<<(PIDBITS-COLBITS)), 0), 0, 0)
40458db832SDavid du Colombier 
41458db832SDavid du Colombier void
mmuinit(void)42458db832SDavid du Colombier mmuinit(void)
43458db832SDavid du Colombier {
44458db832SDavid du Colombier 	int lhash, mem, i;
45458db832SDavid du Colombier 	ulong memsize;
46458db832SDavid du Colombier 
47458db832SDavid du Colombier 	memsize = conf.npage * BY2PG;
48458db832SDavid du Colombier 	if(ptabsize == 0) {
49458db832SDavid du Colombier 		/* heuristically size the hash table */
50458db832SDavid du Colombier 		lhash = 10;
51458db832SDavid du Colombier 		mem = (1<<23);
52458db832SDavid du Colombier 		while(mem < memsize) {
53458db832SDavid du Colombier 			lhash++;
54458db832SDavid du Colombier 			mem <<= 1;
55458db832SDavid du Colombier 		}
56458db832SDavid du Colombier 		ptabsize = (1<<(lhash+6));
57458db832SDavid du Colombier 		ptabmask = (1<<lhash)-1;
58458db832SDavid du Colombier 	}
59458db832SDavid du Colombier 	m->ptabbase = (ulong)xspanalloc(ptabsize, 0, ptabsize);
60458db832SDavid du Colombier 	/* set page table base address */
61458db832SDavid du Colombier 	putsdr1(PADDR(m->ptabbase) | (ptabmask>>10));
62458db832SDavid du Colombier 	m->mmupid = PIDBASE;
63458db832SDavid du Colombier 	m->sweepcolor = 0;
64458db832SDavid du Colombier 	m->trigcolor = COLMASK;
65458db832SDavid du Colombier 
66458db832SDavid du Colombier 	for(i = 0; i < 16; i++)
67458db832SDavid du Colombier 		putsr(i<<28, 0);
68458db832SDavid du Colombier }
69458db832SDavid du Colombier 
70458db832SDavid du Colombier static int
work(void *)71458db832SDavid du Colombier work(void*)
72458db832SDavid du Colombier {
73458db832SDavid du Colombier 	return PIDCOLOR(m->mmupid) == m->trigcolor;
74458db832SDavid du Colombier }
75458db832SDavid du Colombier 
76458db832SDavid du Colombier void
mmusweep(void *)77458db832SDavid du Colombier mmusweep(void*)
78458db832SDavid du Colombier {
79458db832SDavid du Colombier 	Proc *p;
80458db832SDavid du Colombier 	int i, x, sweepcolor;
81458db832SDavid du Colombier 	ulong *ptab, *ptabend, ptecol;
82458db832SDavid du Colombier 
83458db832SDavid du Colombier 	for(;;) {
84458db832SDavid du Colombier 		if(PIDCOLOR(m->mmupid) != m->trigcolor)
85458db832SDavid du Colombier 			sleep(&m->sweepr, work, nil);
86458db832SDavid du Colombier 
87458db832SDavid du Colombier 		sweepcolor = m->sweepcolor;
88458db832SDavid du Colombier 		x = splhi();
89458db832SDavid du Colombier 		p = proctab(0);
90458db832SDavid du Colombier 		for(i = 0; i < conf.nproc; i++, p++)
91458db832SDavid du Colombier 			if(PIDCOLOR(p->mmupid) == sweepcolor)
92458db832SDavid du Colombier 				p->mmupid = 0;
93458db832SDavid du Colombier 		splx(x);
94458db832SDavid du Colombier 
95458db832SDavid du Colombier 		ptab = (ulong*)m->ptabbase;
96458db832SDavid du Colombier 		ptabend = (ulong*)(m->ptabbase+ptabsize);
97458db832SDavid du Colombier 		ptecol = PTECOL(sweepcolor);
98458db832SDavid du Colombier 		while(ptab < ptabend) {
99458db832SDavid du Colombier 			if((*ptab & PTECOL(3)) == ptecol){
100458db832SDavid du Colombier 				*ptab = 0;
101458db832SDavid du Colombier 			}
102458db832SDavid du Colombier 			ptab += 2;
103458db832SDavid du Colombier 		}
104458db832SDavid du Colombier 
105458db832SDavid du Colombier 		m->sweepcolor = (sweepcolor+1) & COLMASK;
106458db832SDavid du Colombier 		m->trigcolor = (m->trigcolor+1) & COLMASK;
107458db832SDavid du Colombier 	}
108458db832SDavid du Colombier }
109458db832SDavid du Colombier 
110458db832SDavid du Colombier int
newmmupid(void)111458db832SDavid du Colombier newmmupid(void)
112458db832SDavid du Colombier {
113458db832SDavid du Colombier 	int pid, newcolor, i, x;
114458db832SDavid du Colombier 	Proc *p;
115458db832SDavid du Colombier 
116458db832SDavid du Colombier 	pid = m->mmupid++;
117458db832SDavid du Colombier 	if(m->mmupid > PIDMAX){
118458db832SDavid du Colombier 		/* Used up all mmupids, start again from first.  Flush the tlb
119458db832SDavid du Colombier 		 * to delete any entries with old pids remaining, then reassign
120458db832SDavid du Colombier 		 * all pids.
121458db832SDavid du Colombier 		 */
122458db832SDavid du Colombier 		m->mmupid = PIDBASE;
123458db832SDavid du Colombier 		x = splhi();
124458db832SDavid du Colombier 		tlbflushall();
125458db832SDavid du Colombier 		p = proctab(0);
126458db832SDavid du Colombier 		for(i = 0; i < conf.nproc; i++, p++)
127458db832SDavid du Colombier 			p->mmupid = 0;
128458db832SDavid du Colombier 		splx(x);
129458db832SDavid du Colombier 		wakeup(&m->sweepr);
130458db832SDavid du Colombier 	}
131458db832SDavid du Colombier 	newcolor = PIDCOLOR(m->mmupid);
132458db832SDavid du Colombier 	if(newcolor != PIDCOLOR(pid)) {
133458db832SDavid du Colombier 		if(newcolor == m->sweepcolor) {
134458db832SDavid du Colombier 			/* desperation time.  can't block here.  punt to fault/putmmu */
135458db832SDavid du Colombier 			print("newmmupid: %uld: no free mmu pids\n", up->pid);
136458db832SDavid du Colombier 			if(m->mmupid == PIDBASE)
137458db832SDavid du Colombier 				m->mmupid = PIDMAX;
138458db832SDavid du Colombier 			else
139458db832SDavid du Colombier 				m->mmupid--;
140458db832SDavid du Colombier 			pid = 0;
141458db832SDavid du Colombier 		}
142458db832SDavid du Colombier 		else if(newcolor == m->trigcolor)
143458db832SDavid du Colombier 			wakeup(&m->sweepr);
144458db832SDavid du Colombier 	}
145458db832SDavid du Colombier 	up->mmupid = pid;
146458db832SDavid du Colombier 	return pid;
147458db832SDavid du Colombier }
148458db832SDavid du Colombier 
149458db832SDavid du Colombier void
flushmmu(void)150458db832SDavid du Colombier flushmmu(void)
151458db832SDavid du Colombier {
152458db832SDavid du Colombier 	int x;
153458db832SDavid du Colombier 
154458db832SDavid du Colombier 	x = splhi();
155458db832SDavid du Colombier 	up->newtlb = 1;
156458db832SDavid du Colombier 	mmuswitch(up);
157458db832SDavid du Colombier 	splx(x);
158458db832SDavid du Colombier }
159458db832SDavid du Colombier 
160458db832SDavid du Colombier /*
161458db832SDavid du Colombier  * called with splhi
162458db832SDavid du Colombier  */
163458db832SDavid du Colombier void
mmuswitch(Proc * p)164458db832SDavid du Colombier mmuswitch(Proc *p)
165458db832SDavid du Colombier {
166458db832SDavid du Colombier 	int i, mp;
167458db832SDavid du Colombier 	ulong r;
168458db832SDavid du Colombier 
169458db832SDavid du Colombier 	if(p->kp) {
170458db832SDavid du Colombier 		for(i = 0; i < 8; i++)
171458db832SDavid du Colombier 			putsr(i<<28, 0);
172458db832SDavid du Colombier 		return;
173458db832SDavid du Colombier 	}
174458db832SDavid du Colombier 
175458db832SDavid du Colombier 	if(p->newtlb) {
176458db832SDavid du Colombier 		p->mmupid = 0;
177458db832SDavid du Colombier 		p->newtlb = 0;
178458db832SDavid du Colombier 	}
179458db832SDavid du Colombier 	mp = p->mmupid;
180458db832SDavid du Colombier 	if(mp == 0)
181458db832SDavid du Colombier 		mp = newmmupid();
182458db832SDavid du Colombier 
183458db832SDavid du Colombier 	for(i = 0; i < 8; i++){
184458db832SDavid du Colombier 		r = VSID(mp, i)|BIT(1)|BIT(2);
185458db832SDavid du Colombier 		putsr(i<<28, r);
186458db832SDavid du Colombier 	}
187458db832SDavid du Colombier }
188458db832SDavid du Colombier 
189458db832SDavid du Colombier void
mmurelease(Proc * p)190458db832SDavid du Colombier mmurelease(Proc* p)
191458db832SDavid du Colombier {
192458db832SDavid du Colombier 	p->mmupid = 0;
193458db832SDavid du Colombier }
194458db832SDavid du Colombier 
195458db832SDavid du Colombier void
putmmu(ulong va,ulong pa,Page * pg)196458db832SDavid du Colombier putmmu(ulong va, ulong pa, Page *pg)
197458db832SDavid du Colombier {
198458db832SDavid du Colombier 	int mp;
199458db832SDavid du Colombier 	char *ctl;
200458db832SDavid du Colombier 	ulong *p, *ep, *q, pteg;
201458db832SDavid du Colombier 	ulong vsid, hash;
202458db832SDavid du Colombier 	ulong ptehi, x;
203458db832SDavid du Colombier 	static ulong pva;
204458db832SDavid du Colombier 
205458db832SDavid du Colombier 	/*
206458db832SDavid du Colombier 	 *	If mmupid is 0, mmuswitch/newmmupid was unable to assign us
207458db832SDavid du Colombier 	 *	a pid, hence we faulted.  Keep calling sched() until the mmusweep
208458db832SDavid du Colombier 	 *	proc catches up, and we are able to get a pid.
209458db832SDavid du Colombier 	 */
210458db832SDavid du Colombier 	while((mp = up->mmupid) == 0)
211458db832SDavid du Colombier 		sched();
212458db832SDavid du Colombier 
213458db832SDavid du Colombier 	vsid = VSID(mp, va>>28);
214458db832SDavid du Colombier 	hash = (vsid ^ ((va>>12)&0xffff)) & ptabmask;
215458db832SDavid du Colombier 	ptehi = PTE0(1, vsid, 0, va);
216458db832SDavid du Colombier 	pteg = m->ptabbase + BY2PTEG*hash;
217458db832SDavid du Colombier 
218458db832SDavid du Colombier 	p = (ulong*)pteg;
219458db832SDavid du Colombier 	ep = (ulong*)(pteg+BY2PTEG);
220458db832SDavid du Colombier 	q = nil;
221458db832SDavid du Colombier 
222458db832SDavid du Colombier 	while(p < ep) {
223458db832SDavid du Colombier 		x = p[0];
224458db832SDavid du Colombier 		if(x == ptehi) {
225458db832SDavid du Colombier 			q = p;
226458db832SDavid du Colombier 			break;
227458db832SDavid du Colombier 		}
228458db832SDavid du Colombier 		if(q == nil && (x & BIT(0)) == 0)
229458db832SDavid du Colombier 			q = p;
230458db832SDavid du Colombier 		p += 2;
231458db832SDavid du Colombier 	}
232458db832SDavid du Colombier 	if(q == nil) {
233458db832SDavid du Colombier 		q = (ulong*)(pteg+m->slotgen);
234458db832SDavid du Colombier 		m->slotgen = (m->slotgen + BY2PTE) & (BY2PTEG-1);
235458db832SDavid du Colombier 	}
236458db832SDavid du Colombier 
237458db832SDavid du Colombier 	if (q[0] != ptehi || q[1] != pa){
238458db832SDavid du Colombier 		tlbflush(va);
239458db832SDavid du Colombier 		m->tlbpurge++;
240458db832SDavid du Colombier 	}
241458db832SDavid du Colombier 	q[0] = ptehi;
242458db832SDavid du Colombier 	q[1] = pa;
243458db832SDavid du Colombier 
244458db832SDavid du Colombier 	ctl = &pg->cachectl[m->machno];
245458db832SDavid du Colombier 	switch(*ctl) {
246458db832SDavid du Colombier 	case PG_NEWCOL:
247458db832SDavid du Colombier 	default:
248458db832SDavid du Colombier 		panic("putmmu: %d\n", *ctl);
249458db832SDavid du Colombier 		break;
250458db832SDavid du Colombier 	case PG_TXTFLUSH:
251458db832SDavid du Colombier 		dcflush((void*)pg->va, BY2PG);
252458db832SDavid du Colombier 		icflush((void*)pg->va, BY2PG);
253458db832SDavid du Colombier 		*ctl = PG_NOFLUSH;
254458db832SDavid du Colombier 		break;
255458db832SDavid du Colombier 	case PG_NOFLUSH:
256458db832SDavid du Colombier 		break;
257458db832SDavid du Colombier 	}
258458db832SDavid du Colombier 
259458db832SDavid du Colombier }
260d717568cSDavid du Colombier 
261ea15f0ccSDavid du Colombier void
checkmmu(ulong,ulong)262ea15f0ccSDavid du Colombier checkmmu(ulong, ulong)
263ea15f0ccSDavid du Colombier {
264ea15f0ccSDavid du Colombier }
265ea15f0ccSDavid du Colombier 
266ea15f0ccSDavid du Colombier void
countpagerefs(ulong *,int)267ea15f0ccSDavid du Colombier countpagerefs(ulong*, int)
268ea15f0ccSDavid du Colombier {
269ea15f0ccSDavid du Colombier }
270*c717cbbdSDavid du Colombier 
271*c717cbbdSDavid du Colombier /*
272*c717cbbdSDavid du Colombier  * Return the number of bytes that can be accessed via KADDR(pa).
273*c717cbbdSDavid du Colombier  * If pa is not a valid argument to KADDR, return 0.
274*c717cbbdSDavid du Colombier  */
275*c717cbbdSDavid du Colombier ulong
cankaddr(ulong pa)276*c717cbbdSDavid du Colombier cankaddr(ulong pa)
277*c717cbbdSDavid du Colombier {
278*c717cbbdSDavid du Colombier 	if(pa >= -KZERO)
279*c717cbbdSDavid du Colombier 		return 0;
280*c717cbbdSDavid du Colombier 	return -KZERO - pa;
281*c717cbbdSDavid du Colombier }
282*c717cbbdSDavid du Colombier 
283