]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/mtx/mmu.c
bcm64: fix usb xhci controller on pi4 8GB variant (thanks richard miller)
[plan9front.git] / sys / src / 9 / mtx / mmu.c
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         while(waserror())
80                 ;
81
82         for(;;) {
83                 if(PIDCOLOR(m->mmupid) != m->trigcolor)
84                         sleep(&m->sweepr, work, nil);
85
86                 sweepcolor = m->sweepcolor;
87                 x = splhi();
88                 for(i = 0; i < conf.nproc; i++) {
89                         p = proctab(i);
90                         if(PIDCOLOR(p->mmupid) == sweepcolor)
91                                 p->mmupid = 0;
92                 }
93                 splx(x);
94
95                 ptab = (ulong*)m->ptabbase;
96                 ptabend = (ulong*)(m->ptabbase+ptabsize);
97                 ptecol = PTECOL(sweepcolor);
98                 while(ptab < ptabend) {
99                         if((*ptab & PTECOL(3)) == ptecol)
100                                 *ptab = 0;
101                         ptab += 2;
102                 }
103                 tlbflushall();
104
105                 m->sweepcolor = (sweepcolor+1) & COLMASK;
106                 m->trigcolor = (m->trigcolor+1) & COLMASK;
107         }
108 }
109
110 int
111 newmmupid(void)
112 {
113         int pid, newcolor;
114
115         pid = m->mmupid++;
116         if(m->mmupid > PIDMAX)
117                 m->mmupid = PIDBASE;
118         newcolor = PIDCOLOR(m->mmupid);
119         if(newcolor != PIDCOLOR(pid)) {
120                 if(newcolor == m->sweepcolor) {
121                         /* desperation time.  can't block here.  punt to fault/putmmu */
122                         print("newmmupid: %uld: no free mmu pids\n", up->pid);
123                         if(m->mmupid == PIDBASE)
124                                 m->mmupid = PIDMAX;
125                         else
126                                 m->mmupid--;
127                         pid = 0;
128                 }
129                 else if(newcolor == m->trigcolor)
130                         wakeup(&m->sweepr);
131         }
132         up->mmupid = pid;
133         return pid;
134 }
135
136 void
137 flushmmu(void)
138 {
139         int x;
140
141         x = splhi();
142         up->newtlb = 1;
143         mmuswitch(up);
144         splx(x);
145 }
146
147 /*
148  * called with splhi
149  */
150 void
151 mmuswitch(Proc *p)
152 {
153         int i, mp;
154
155         if(p->kp) {
156                 for(i = 0; i < 8; i++)
157                         putsr(i<<28, 0);
158                 return;
159         }
160
161         if(p->newtlb) {
162                 p->mmupid = 0;
163                 p->newtlb = 0;
164         }
165         mp = p->mmupid;
166         if(mp == 0)
167                 mp = newmmupid();
168
169         for(i = 0; i < 8; i++)
170                 putsr(i<<28, VSID(mp, i)|BIT(1)|BIT(2));
171 }
172
173 void
174 mmurelease(Proc* p)
175 {
176         p->mmupid = 0;
177 }
178
179 void
180 putmmu(uintptr va, uintptr pa, Page *pg)
181 {
182         int mp;
183         ulong *p, *ep, *q, pteg;
184         ulong vsid, ptehi, x, hash;
185
186         /*
187          *      If mmupid is 0, mmuswitch/newmmupid was unable to assign us
188          *      a pid, hence we faulted.  Keep calling sched() until the mmusweep
189          *      proc catches up, and we are able to get a pid.
190          */
191         while((mp = up->mmupid) == 0)
192                 sched();
193
194         vsid = VSID(mp, va>>28);
195         hash = (vsid ^ (va>>12)&0xffff) & ptabmask;
196         ptehi = PTE0(1, vsid, 0, va);
197
198         pteg = m->ptabbase + BY2PTEG*hash;
199         p = (ulong*)pteg;
200         ep = (ulong*)(pteg+BY2PTEG);
201         q = nil;
202         tlbflush(va);
203         while(p < ep) {
204                 x = p[0];
205                 if(x == ptehi) {
206                         q = p;
207                         break;
208                 }
209                 if(q == nil && (x & BIT(0)) == 0)
210                         q = p;
211                 p += 2;
212         }
213         if(q == nil) {
214                 q = (ulong*)(pteg+m->slotgen);
215                 m->slotgen = (m->slotgen + BY2PTE) & (BY2PTEG-1);
216         }
217         q[0] = ptehi;
218         q[1] = pa;
219         sync();
220
221         if(pg->txtflush & (1<<m->machno)){
222                 dcflush((void*)pg->va, BY2PG);
223                 icflush((void*)pg->va, BY2PG);
224                 pg->txtflush &= ~(1<<m->machno);
225         }
226 }
227
228 void
229 checkmmu(uintptr, uintptr)
230 {
231 }
232
233 /*
234  * Return the number of bytes that can be accessed via KADDR(pa).
235  * If pa is not a valid argument to KADDR, return 0.
236  */
237 ulong
238 cankaddr(ulong pa)
239 {
240         ulong kzero;
241
242         kzero = -KZERO;
243         if(pa >= kzero)
244                 return 0;
245         return kzero - pa;
246 }