]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm/mmu.c
kernel: reduce Page structure size by changing Page.cachectl[]
[plan9front.git] / sys / src / 9 / bcm / mmu.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6
7 #include "arm.h"
8
9 #define FEXT(d, o, w)   (((d)>>(o)) & ((1<<(w))-1))
10 #define L1X(va)         FEXT((va), 20, 12)
11 #define L2X(va)         FEXT((va), 12, 8)
12
13 enum {
14         L1lo            = UZERO/MiB,            /* L1X(UZERO)? */
15         L1hi            = (USTKTOP+MiB-1)/MiB,  /* L1X(USTKTOP+MiB-1)? */
16 };
17
18 void
19 mmuinit(void)
20 {
21         PTE *l1, *l2;
22         uintptr pa, va;
23
24         l1 = (PTE*)PADDR(L1);
25         l2 = (PTE*)PADDR(L2);
26
27         /*
28          * map all of ram at KZERO
29          */
30         va = KZERO;
31         for(pa = PHYSDRAM; pa < PHYSDRAM+DRAMSIZE; pa += MiB){
32                 l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|Cached|Buffered;
33                 va += MiB;
34         }
35
36         /*
37          * identity map first MB of ram so mmu can be enabled
38          */
39         l1[L1X(PHYSDRAM)] = PHYSDRAM|Dom0|L1AP(Krw)|Section|Cached|Buffered;
40
41         /*
42          * map i/o registers 
43          */
44         va = VIRTIO;
45         for(pa = PHYSIO; pa < PHYSIO+IOSIZE; pa += MiB){
46                 l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section;
47                 va += MiB;
48         }
49
50         /*
51          * double map exception vectors at top of virtual memory
52          */
53         va = HVECTORS;
54         l1[L1X(va)] = (uintptr)l2|Dom0|Coarse;
55         l2[L2X(va)] = PHYSDRAM|L2AP(Krw)|Small;
56 }
57
58 void
59 mmuinit1(void)
60 {
61         PTE *l1;
62
63         l1 = (PTE*)L1;
64         m->mmul1 = l1;
65
66         /*
67          * undo identity map of first MB of ram
68          */
69         l1[L1X(PHYSDRAM)] = 0;
70         cachedwbse(&l1[L1X(PHYSDRAM)], sizeof(PTE));
71         mmuinvalidate();
72 }
73
74 static void
75 mmul2empty(Proc* proc, int clear)
76 {
77         PTE *l1;
78         Page **l2, *page;
79
80         l1 = m->mmul1;
81         l2 = &proc->mmul2;
82         for(page = *l2; page != nil; page = page->next){
83                 if(clear)
84                         memset(UINT2PTR(page->va), 0, BY2PG);
85                 l1[page->daddr] = Fault;
86                 l2 = &page->next;
87         }
88         *l2 = proc->mmul2cache;
89         proc->mmul2cache = proc->mmul2;
90         proc->mmul2 = nil;
91 }
92
93 static void
94 mmul1empty(void)
95 {
96 #ifdef notdef
97 /* there's a bug in here */
98         PTE *l1;
99
100         /* clean out any user mappings still in l1 */
101         if(m->mmul1lo > L1lo){
102                 if(m->mmul1lo == 1)
103                         m->mmul1[L1lo] = Fault;
104                 else
105                         memset(&m->mmul1[L1lo], 0, m->mmul1lo*sizeof(PTE));
106                 m->mmul1lo = L1lo;
107         }
108         if(m->mmul1hi < L1hi){
109                 l1 = &m->mmul1[m->mmul1hi];
110                 if((L1hi - m->mmul1hi) == 1)
111                         *l1 = Fault;
112                 else
113                         memset(l1, 0, (L1hi - m->mmul1hi)*sizeof(PTE));
114                 m->mmul1hi = L1hi;
115         }
116 #else
117         memset(&m->mmul1[L1lo], 0, (L1hi - L1lo)*sizeof(PTE));
118 #endif /* notdef */
119 }
120
121 void
122 mmuswitch(Proc* proc)
123 {
124         int x;
125         PTE *l1;
126         Page *page;
127
128         /* do kprocs get here and if so, do they need to? */
129         if(m->mmupid == proc->pid && !proc->newtlb)
130                 return;
131         m->mmupid = proc->pid;
132
133         /* write back dirty and invalidate l1 caches */
134         cacheuwbinv();
135
136         if(proc->newtlb){
137                 mmul2empty(proc, 1);
138                 proc->newtlb = 0;
139         }
140
141         mmul1empty();
142
143         /* move in new map */
144         l1 = m->mmul1;
145         for(page = proc->mmul2; page != nil; page = page->next){
146                 x = page->daddr;
147                 l1[x] = PPN(page->pa)|Dom0|Coarse;
148                 /* know here that L1lo < x < L1hi */
149                 if(x+1 - m->mmul1lo < m->mmul1hi - x)
150                         m->mmul1lo = x+1;
151                 else
152                         m->mmul1hi = x;
153         }
154
155         /* make sure map is in memory */
156         /* could be smarter about how much? */
157         cachedwbse(&l1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE));
158
159         /* lose any possible stale tlb entries */
160         mmuinvalidate();
161 }
162
163 void
164 flushmmu(void)
165 {
166         int s;
167
168         s = splhi();
169         up->newtlb = 1;
170         mmuswitch(up);
171         splx(s);
172 }
173
174 void
175 mmurelease(Proc* proc)
176 {
177         Page *page, *next;
178
179         /* write back dirty and invalidate l1 caches */
180         cacheuwbinv();
181
182         mmul2empty(proc, 0);
183         for(page = proc->mmul2cache; page != nil; page = next){
184                 next = page->next;
185                 if(--page->ref)
186                         panic("mmurelease: page->ref %d", page->ref);
187                 pagechainhead(page);
188         }
189         if(proc->mmul2cache && palloc.r.p)
190                 wakeup(&palloc.r);
191         proc->mmul2cache = nil;
192
193         mmul1empty();
194
195         /* make sure map is in memory */
196         /* could be smarter about how much? */
197         cachedwbse(&m->mmul1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE));
198
199         /* lose any possible stale tlb entries */
200         mmuinvalidate();
201 }
202
203 void
204 putmmu(uintptr va, uintptr pa, Page* page)
205 {
206         int x;
207         Page *pg;
208         PTE *l1, *pte;
209
210         x = L1X(va);
211         l1 = &m->mmul1[x];
212         if(*l1 == Fault){
213                 /* wasteful - l2 pages only have 256 entries - fix */
214                 if(up->mmul2cache == nil){
215                         /* auxpg since we don't need much? memset if so */
216                         pg = newpage(1, 0, 0);
217                         pg->va = VA(kmap(pg));
218                 }
219                 else{
220                         pg = up->mmul2cache;
221                         up->mmul2cache = pg->next;
222                         memset(UINT2PTR(pg->va), 0, BY2PG);
223                 }
224                 pg->daddr = x;
225                 pg->next = up->mmul2;
226                 up->mmul2 = pg;
227
228                 /* force l2 page to memory */
229                 cachedwbse((void *)pg->va, BY2PG);
230
231                 *l1 = PPN(pg->pa)|Dom0|Coarse;
232                 cachedwbse(l1, sizeof *l1);
233
234                 if(x >= m->mmul1lo && x < m->mmul1hi){
235                         if(x+1 - m->mmul1lo < m->mmul1hi - x)
236                                 m->mmul1lo = x+1;
237                         else
238                                 m->mmul1hi = x;
239                 }
240         }
241         pte = UINT2PTR(KADDR(PPN(*l1)));
242
243         /* protection bits are
244          *      PTERONLY|PTEVALID;
245          *      PTEWRITE|PTEVALID;
246          *      PTEWRITE|PTEUNCACHED|PTEVALID;
247          */
248         x = Small;
249         if(!(pa & PTEUNCACHED))
250                 x |= Cached|Buffered;
251         if(pa & PTEWRITE)
252                 x |= L2AP(Urw);
253         else
254                 x |= L2AP(Uro);
255         pte[L2X(va)] = PPN(pa)|x;
256         cachedwbse(&pte[L2X(va)], sizeof pte[0]);
257
258         /* clear out the current entry */
259         mmuinvalidateaddr(PPN(va));
260
261         /*  write back dirty entries - we need this because the pio() in
262          *  fault.c is writing via a different virt addr and won't clean
263          *  its changes out of the dcache.  Page coloring doesn't work
264          *  on this mmu because the virtual cache is set associative
265          *  rather than direct mapped.
266          */
267         cachedwbinv();
268         if(page->txtflush){
269                 cacheiinv();
270                 page->txtflush = 0;
271         }
272         checkmmu(va, PPN(pa));
273 }
274
275 /*
276  * Return the number of bytes that can be accessed via KADDR(pa).
277  * If pa is not a valid argument to KADDR, return 0.
278  */
279 uintptr
280 cankaddr(uintptr pa)
281 {
282         if(pa < PHYSDRAM + memsize)             /* assumes PHYSDRAM is 0 */
283                 return PHYSDRAM + memsize - pa;
284         return 0;
285 }
286
287 uintptr
288 mmukmap(uintptr va, uintptr pa, usize size)
289 {
290         int o;
291         usize n;
292         PTE *pte, *pte0;
293
294         assert((va & (MiB-1)) == 0);
295         o = pa & (MiB-1);
296         pa -= o;
297         size += o;
298         pte = pte0 = &m->mmul1[L1X(va)];
299         for(n = 0; n < size; n += MiB)
300                 if(*pte++ != Fault)
301                         return 0;
302         pte = pte0;
303         for(n = 0; n < size; n += MiB){
304                 *pte++ = (pa+n)|Dom0|L1AP(Krw)|Section;
305                 mmuinvalidateaddr(va+n);
306         }
307         cachedwbse(pte0, (uintptr)pte - (uintptr)pte0);
308         return va + o;
309 }
310
311
312 void
313 checkmmu(uintptr va, uintptr pa)
314 {
315         USED(va);
316         USED(pa);
317 }
318