]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/xen/mmu.c
bcm: flush out early boot messages on uart and screen initialization
[plan9front.git] / sys / src / 9 / xen / 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 int paemode;
9 uvlong *xenpdpt;        /* this needs to go in Mach for multiprocessor guest */
10
11 #define LOG(a)  
12 #define PUTMMULOG(a)
13 #define MFN(pa)         (patomfn[(pa)>>PGSHIFT])
14 #define MAPPN(x)        (paemode? matopfn[*(uvlong*)(&x)>>PGSHIFT]<<PGSHIFT : matopfn[(x)>>PGSHIFT]<<PGSHIFT)
15
16 /* note: pdb must already be pinned */
17 static void
18 taskswitch(Page *pdb, ulong stack)
19 {
20         HYPERVISOR_stack_switch(KDSEL, stack);
21         mmuflushtlb(pdb);
22 }
23
24 void
25 mmuflushtlb(Page *pdb)
26 {
27         int s, i;
28
29         if(!paemode){
30                 if(pdb)
31                         xenptswitch(pdb->pa);
32                 else
33                         xenptswitch(PADDR(m->pdb));
34         }else{
35                 if(pdb){
36                         s = splhi();
37                         for(i = 0; i < 3; i++){
38                                 xenupdate((ulong*)&xenpdpt[i], pdb->pa | PTEVALID);
39                                 pdb = pdb->next;
40                         }
41                         splx(s);
42                 }else{
43                         s = splhi();
44                         for(i = 0; i < 3; i++)
45                                 xenupdatema((ulong*)&xenpdpt[i], ((uvlong*)m->pdb)[i]);
46                         splx(s);
47                 }
48                 xentlbflush();
49         }
50 }
51
52 /* 
53  * On processors that support it, we set the PTEGLOBAL bit in
54  * page table and page directory entries that map kernel memory.
55  * Doing this tells the processor not to bother flushing them
56  * from the TLB when doing the TLB flush associated with a 
57  * context switch (write to CR3).  Since kernel memory mappings
58  * are never removed, this is safe.  (If we ever remove kernel memory
59  * mappings, we can do a full flush by turning off the PGE bit in CR4,
60  * writing to CR3, and then turning the PGE bit back on.) 
61  *
62  * See also mmukmap below.
63  * 
64  * Processor support for the PTEGLOBAL bit is enabled in devarch.c.
65  */
66 static void
67 memglobal(void)
68 {
69         int i, j;
70         ulong *pde, *pte;
71
72         /* only need to do this once, on bootstrap processor */
73         if(m->machno != 0)
74                 return;
75
76         if(!m->havepge)
77                 return;
78
79         pde = m->pdb;
80         for(i=512; i<1024; i++){        /* 512: start at entry for virtual 0x80000000 */
81                 if(pde[i] & PTEVALID){
82                         pde[i] |= PTEGLOBAL;
83                         if(!(pde[i] & PTESIZE)){
84                                 pte = KADDR(pde[i]&~(BY2PG-1));
85                                 for(j=0; j<1024; j++)
86                                         if(pte[j] & PTEVALID)
87                                                 pte[j] |= PTEGLOBAL;
88                         }
89                 }
90         }                       
91 }
92
93 ulong
94 mmumapframe(ulong va, ulong mfn)
95 {
96         ulong *pte, pdbx;
97         uvlong ma;
98
99         /* 
100          * map machine frame number to a virtual address.
101          * When called the pagedir and page table exist, we just
102          * need to fill in a page table entry.
103          */
104         ma = ((uvlong)mfn<<PGSHIFT) | PTEVALID|PTEWRITE;
105         pdbx = PDX(va);
106         pte = KADDR(MAPPN(PDB(m->pdb,va)[pdbx]));
107         xenupdatema(&pte[PTX(va)], ma);
108         return va;
109 }
110
111 void
112 mmumapcpu0(void)
113 {
114         ulong *pdb, *pte, va, pa, pdbx;
115
116         if(strstr(xenstart->magic, "x86_32p"))
117                 paemode = 1;
118         hypervisor_virt_start = paemode ? 0xF5800000 : 0xFC000000;
119         patomfn = (ulong*)xenstart->mfn_list;
120         matopfn = (ulong*)hypervisor_virt_start;
121         /* Xen bug ? can't touch top entry in PDPT */
122         if(paemode)
123                 hypervisor_virt_start = 0xC0000000;
124
125         /* 
126          * map CPU0MACH at MACHADDR.
127          * When called the pagedir and page table exist, we just
128          * need to fill in a page table entry.
129          */
130         pdb = (ulong*)xenstart->pt_base;
131         va = MACHADDR;
132         pa = PADDR(CPU0MACH) | PTEVALID|PTEWRITE;
133         pdbx = PDX(va);
134         pdb = PDB(pdb, va);
135         pte = KADDR(MAPPN(pdb[pdbx]));
136         xenupdate(&pte[PTX(va)], pa);
137 }
138
139 void
140 mmuinit(void)
141 {
142         ulong *pte, npgs, pa;
143
144         if(paemode){
145                 int i;
146                 xenpdpt = (uvlong*)m->pdb;
147                 m->pdb = xspanalloc(32, 32, 0);
148                 /* clear "reserved" bits in initial page directory pointers -- Xen bug? */
149                 for(i = 0; i < 4; i++)
150                         ((uvlong*)m->pdb)[i] = xenpdpt[i] & ~0x1E6LL;
151         }
152
153         /* 
154          * So far only memory up to xentop is mapped, map the rest.
155          * We cant use large pages because our contiguous PA space
156          * is not necessarily contiguous in MA.
157          */
158         npgs = conf.mem[0].npage;
159         for(pa=conf.mem[0].base; npgs; npgs--, pa+=BY2PG) {
160                 pte = mmuwalk(m->pdb, (ulong)KADDR(pa), 2, 1);
161                 if(!pte)
162                         panic("mmuinit");
163                 xenupdate(pte, pa|PTEVALID|PTEWRITE);
164         }
165
166         memglobal();
167
168 #ifdef we_may_eventually_want_this
169         /* make kernel text unwritable */
170         for(x = KTZERO; x < (ulong)etext; x += BY2PG){
171                 p = mmuwalk(m->pdb, x, 2, 0);
172                 if(p == nil)
173                         panic("mmuinit");
174                 *p &= ~PTEWRITE;
175         }
176 #endif
177
178         taskswitch(0,  (ulong)m + BY2PG);
179 }
180
181 void
182 flushmmu(void)
183 {
184         int s;
185
186         s = splhi();
187         up->newtlb = 1;
188         mmuswitch(up);
189         splx(s);
190 }
191
192 static ulong*
193 mmupdb(Page *pg, ulong va)
194 {
195         int i;
196
197         for(i = PAX(va); i > 0; i -= 2)
198                 pg = pg->next;
199         return (ulong*)pg->va;
200 }
201         
202 /* this can be called with an active pdb, so use Xen calls to zero it out.
203   */
204 static void
205 mmuptefree(Proc* proc)
206 {
207         ulong *pdb, va;
208         Page **last, *page;
209
210         if(proc->mmupdb && proc->mmuused){
211                 last = &proc->mmuused;
212                 for(page = *last; page; page = page->next){
213                         /* this is no longer a pte page so make it readwrite */
214                         va = page->daddr;
215                         pdb = mmupdb(proc->mmupdb, va);
216                         xenupdatema(&pdb[PDX(va)], 0);
217                         xenptunpin(page->va);
218                         last = &page->next;
219                 }
220                 *last = proc->mmufree;
221                 proc->mmufree = proc->mmuused;
222                 proc->mmuused = 0;
223         }
224 }
225
226 void
227 mmuswitch(Proc* proc)
228 {
229         //ulong *pdb;
230
231         if(proc->newtlb){
232                 mmuptefree(proc);
233                 proc->newtlb = 0;
234         }
235
236         if(proc->mmupdb){
237                 //XXX doesn't work for some reason, but it's not needed for uniprocessor
238                 //pdb = (ulong*)proc->mmupdb->va;
239                 //xenupdate(&pdb[PDX(MACHADDR)], m->pdb[PDX(MACHADDR)]);
240                 taskswitch(proc->mmupdb, (ulong)(proc->kstack+KSTACK));
241         }
242         else
243                 taskswitch(0, (ulong)(proc->kstack+KSTACK));
244 }
245
246 void
247 mmurelease(Proc* proc)
248 {
249         Page *page, *next;
250
251         /*
252          * Release any pages allocated for a page directory base or page-tables
253          * for this process:
254          *   switch to the prototype pdb for this processor (m->pdb);
255          *   call mmuptefree() to place all pages used for page-tables (proc->mmuused)
256          *   onto the process' free list (proc->mmufree). This has the side-effect of
257          *   cleaning any user entries in the pdb (proc->mmupdb);
258          *   if there's a pdb put it in the cache of pre-initialised pdb's
259          *   for this processor (m->pdbpool) or on the process' free list;
260          *   finally, place any pages freed back into the free pool (palloc).
261          * This routine is only called from sched() with palloc locked.
262          */
263         taskswitch(0, (ulong)m + BY2PG);
264         mmuptefree(proc);
265
266         if((page = proc->mmupdb) != 0){
267                 proc->mmupdb = 0;
268                 while(page){
269                         next = page->next;
270                         /* its not a page table anymore, mark it rw */
271                         xenptunpin(page->va);
272                         if(paemode || m->pdbcnt > 10){
273                                 page->next = proc->mmufree;
274                                 proc->mmufree = page;
275                         }
276                         else{
277                                 page->next = m->pdbpool;
278                                 m->pdbpool = page;
279                                 m->pdbcnt++;
280                         }
281                         page = next;
282                 }
283         }
284
285         for(page = proc->mmufree; page; page = next){
286                 next = page->next;
287                 if(--page->ref)
288                         panic("mmurelease: page->ref %ld\n", page->ref);
289                 pagechainhead(page);
290         }
291         if(proc->mmufree)
292                 pagechaindone();
293         proc->mmufree = 0;
294 }
295
296 static Page*
297 mmupdballoc(ulong va, void *mpdb)
298 {
299         int s;
300         Page *page;
301         Page *badpages, *pg;
302
303         s = splhi();
304         /*
305          * All page tables must be read-only.  We will mark them
306          * readwrite later when we free them and they are no
307          * longer used as page tables.
308          */
309         if(m->pdbpool == 0){
310                 spllo();
311                 badpages = 0;
312                 for (;;) {
313                         page = newpage(0, 0, 0);
314                         page->va = VA(kmap(page));
315                         if(mpdb)
316                                 memmove((void*)page->va, mpdb, BY2PG);
317                         else
318                                 memset((void*)page->va, 0, BY2PG);
319                         if (xenpgdpin(page->va))
320                                 break;
321                         /*
322                          * XXX Plan 9 is a bit lax about putting pages on the free list when they are
323                          * still mapped (r/w) by some process's page table.  From Plan 9's point
324                          * of view this is safe because the any such process will have up->newtlb set,
325                          * so the mapping will be cleared before the process is dispatched.  But the Xen
326                          * hypervisor has no way of knowing this, so it refuses to pin the page for use
327                          * as a pagetable.
328                          */
329                         if(0) print("bad pgdpin %lux va %lux copy %lux %s\n", MFN(PADDR(page->va)), va, (ulong)mpdb, up? up->text: "");
330                         page->next = badpages;
331                         badpages = page;
332                 }
333                 while (badpages != 0) {
334                         pg = badpages;
335                         badpages = badpages->next;
336                         putpage(pg);
337                 }
338         }
339         else{
340                 page = m->pdbpool;
341                 m->pdbpool = page->next;
342                 m->pdbcnt--;
343                 if (!xenpgdpin(page->va))
344                         panic("xenpgdpin");
345         }
346         splx(s);
347
348         page->next = 0;
349         return page;
350 }
351
352 void
353 checkmmu(ulong va, ulong pa)
354 {
355         ulong *pdb, *pte;
356         int pdbx;
357         
358         if(up->mmupdb == 0)
359                 return;
360
361         pdb = mmupdb(up->mmupdb, va);
362         pdbx = PDX(va);
363         if(MAPPN(pdb[pdbx]) == 0){
364                 /* okay to be empty - will fault and get filled */
365                 return;
366         }
367         
368         pte = KADDR(MAPPN(pdb[pdbx]));
369         if(MAPPN(pte[PTX(va)]) != pa){
370                 if(!paemode)
371                   print("%ld %s: va=0x%08lux pa=0x%08lux pte=0x%08lux (0x%08lux)\n",
372                         up->pid, up->text,
373                         va, pa, pte[PTX(va)], MAPPN(pte[PTX(va)]));
374                 else
375                   print("%ld %s: va=0x%08lux pa=0x%08lux pte=0x%16llux (0x%08lux)\n",
376                         up->pid, up->text,
377                         va, pa, *(uvlong*)&pte[PTX(va)], MAPPN(pte[PTX(va)]));
378         }
379 }
380
381 void
382 putmmu(ulong va, ulong pa, Page*)
383 {
384         int pdbx;
385         Page *page;
386         Page *badpages, *pg;
387         ulong *pdb, *pte;
388         int i, s;
389
390         PUTMMULOG(dprint("putmmu va %lux pa %lux\n", va, pa);)
391         if(up->mmupdb == 0){
392                 if(!paemode)
393                         up->mmupdb = mmupdballoc(va, m->pdb);
394                 else {
395                         page = 0;
396                         for(i = 4; i >= 0; i -= 2){
397                                 if(m->pdb[i])
398                                         pg = mmupdballoc(va, KADDR(MAPPN(m->pdb[i])));
399                                 else
400                                         pg = mmupdballoc(va, 0);
401                                 pg->next = page;
402                                 page = pg;
403                         }
404                         up->mmupdb = page;
405                 }
406         }
407         pdb = mmupdb(up->mmupdb, va);
408         pdbx = PDX(va);
409
410         if(PPN(pdb[pdbx]) == 0){
411                 PUTMMULOG(dprint("new pt page for index %d pdb %lux\n", pdbx, (ulong)pdb);)
412                 /* mark page as readonly before using as a page table */
413                 if(up->mmufree == 0){
414                         badpages = 0;
415                         for (;;) {
416                                 page = newpage(1, 0, 0);
417                                 page->va = VA(kmap(page));
418                                 if (xenptpin(page->va))
419                                         break;
420                                 if(0) print("bad pin %lux va %lux %s\n", MFN(PADDR(page->va)), va, up->text);
421                                 page->next = badpages;
422                                 badpages = page;
423                         }
424                         while (badpages != 0) {
425                                 pg = badpages;
426                                 badpages = badpages->next;
427                                 putpage(pg);
428                         }
429                 }
430                 else {
431                         page = up->mmufree;
432                         up->mmufree = page->next;
433                         memset((void*)page->va, 0, BY2PG);
434                         if (!xenptpin(page->va))
435                                 panic("xenptpin");
436                 }
437
438                 xenupdate(&pdb[pdbx], page->pa|PTEVALID|PTEUSER|PTEWRITE);
439
440                 page->daddr = va;
441                 page->next = up->mmuused;
442                 up->mmuused = page;
443         }
444
445         pte = KADDR(MAPPN(pdb[pdbx]));
446         PUTMMULOG(dprint("pte %lux index %lud old %lux new %lux mfn %lux\n", (ulong)pte, PTX(va), pte[PTX(va)], pa|PTEUSER, MFN(pa));)
447         xenupdate(&pte[PTX(va)], pa|PTEUSER);
448
449         s = splhi();
450         //XXX doesn't work for some reason, but it's not needed for uniprocessor
451         //xenupdate(&pdb[PDX(MACHADDR)], m->pdb[PDX(MACHADDR)]);
452         mmuflushtlb(up->mmupdb);
453         splx(s);
454 }
455
456 ulong*
457 mmuwalk(ulong* pdb, ulong va, int level, int create)
458 {
459         ulong pa, va2, *table;
460
461         /*
462          * Walk the page-table pointed to by pdb and return a pointer
463          * to the entry for virtual address va at the requested level.
464          * If the entry is invalid and create isn't requested then bail
465          * out early. Otherwise, for the 2nd level walk, allocate a new
466          * page-table page and register it in the 1st level.
467          */
468         if(paemode){
469                 pdb = &pdb[PAX(va)];
470                 if(!(*pdb & PTEVALID)){
471                         if(create == 0)
472                                 return 0;
473                         panic("mmuwalk: missing pgdir ptr for va=%lux\n", va);
474                 }
475                 pdb = KADDR(MAPPN(*pdb));
476         }
477         table = &pdb[PDX(va)];
478         if(!(*table & PTEVALID) && create == 0)
479                 return 0;
480
481         switch(level){
482
483         default:
484                 return 0;
485
486         case 1:
487                 return table;
488
489         case 2:
490                 if(*table & PTESIZE)
491                         panic("mmuwalk2: va %luX entry %luX\n", va, *table);
492                 if(!(*table & PTEVALID)){
493                         va2 = (ulong)xspanalloc(BY2PG, BY2PG, 0);
494                         pa = PADDR(va2);
495                         xenptpin(va2);
496                         xenupdate(table, pa|PTEWRITE|PTEVALID);
497                 }
498                 table = KADDR(MAPPN(*table));
499
500                 return &table[PTX(va)];
501         }
502 }
503
504 int
505 mmukmapsync(ulong va)
506 {
507         USED(va);
508         return 0;
509 }
510
511 /*
512  * Return the number of bytes that can be accessed via KADDR(pa).
513  * If pa is not a valid argument to KADDR, return 0.
514  */
515 ulong
516 cankaddr(ulong pa)
517 {
518         if(pa >= -KZERO)
519                 return 0;
520         return -KZERO - pa;
521 }