Create the AHCI branch for Aman's work
[reactos.git] / sdk / lib / ppcmmu / mmuobject.c
1 #include <stdarg.h>
2 #include "ppcmmu/mmu.h"
3 #include "ppcmmu/mmuutil.h"
4 #include "mmuobject.h"
5
6 typedef unsigned long ULONG;
7
8 /*
9
10 The MMU Object:
11 0x00300 -- Data miss
12 0x00400 -- Instruction miss
13 0x10000 -- Entry point
14 ... Code
15 0x20000 -- Physical map (PTE + Process Ptr + Address : 16 bytes)
16
17 4096 / 16 bytes = 256 entries per page
18 256 pages = 1Megabyte = 1 page table page
19
20 Setup by freeldr and used to build the kernel map, then used by the kernel
21
22 Calling:
23
24 r3 -- Action
25 r4 .. r6 -- Args
26
27 Actions:
28 00 Init
29 01 Map pages
30 02 erase pages
31 03 set segment vsid
32 04 page miss callback
33 05 inquire page
34 06 unit test
35 07 alloc page
36 08 set memory size
37 09 get first usable page
38 10 alloc vsid
39 11 revoke vsid
40 */
41
42 #define MMU_ADDR_RESERVED ((vaddr_t)-2)
43
44 MmuTrapHandler callback[0x30];
45 typedef struct _MmuFreePage {
46 int page;
47 struct _MmuFreePage *next;
48 } MmuFreePage;
49 typedef struct _MmuFreeTree {
50 struct _MmuFreeTree *next;
51 } MmuFreeTree;
52 typedef struct _MmuVsidTree {
53 ppc_map_t *leaves[256];
54 } MmuVsidTree;
55 typedef struct _MmuVsidInfo {
56 int vsid;
57 struct _MmuVsidInfo *next;
58 MmuVsidTree *tree[256];
59 } MmuVsidInfo;
60 MmuFreePage *FreeList = 0;
61 // Pages are allocated one by one until NextPage == RamSize >> PPC_PAGE_SHIFT
62 // Then we take only from the free list
63 int Clock = 0, TreeAlloc = 0, GdbAttach = 0, Booted = 0, Vsid[16];
64 paddr_t RamSize, FirstUsablePage, NextPage;
65 MmuVsidTree *NextTreePage = 0;
66 MmuFreeTree *FreeTree;
67 MmuVsidInfo *Segs[16], *VsidHead = 0;
68
69 extern void fmtout(const char *fmt, ...);
70 extern char *serport;
71 int ptegreload(ppc_trap_frame_t *frame, vaddr_t addr);
72 void SerialSetUp(int deviceType, void *deviceAddr, int baud);
73 int SerialInterrupt(int n, ppc_trap_frame_t *tf);
74 void TakeException(int n, ppc_trap_frame_t *tf);
75 int mmuisfreepage(paddr_t pageno);
76 void copy(void *t, void *s, int b);
77 paddr_t mmunewpage();
78 void dumpmap();
79 void trapcallback(int action, ppc_trap_frame_t *trap_frame);
80
81 int _mmumain(int action, void *arg1, void *arg2, void *arg3, void *tf)
82 {
83 ppc_trap_frame_t *trap_frame = (action >= 0x100) ? tf : arg1;
84 int ret = 0, tmp, i;
85
86 switch(action)
87 {
88 /* Trap Handlers */
89 case 3:
90 if(!ptegreload(trap_frame, trap_frame->dar))
91 {
92 trapcallback(action, trap_frame);
93 }
94 break;
95 case 4:
96 if(!ptegreload(trap_frame, trap_frame->srr0))
97 {
98 trapcallback(action, trap_frame);
99 }
100 break;
101
102 case 5:
103 /* EE -- Try to get a serial interrupt if debugging enabled, then fall
104 * back to primary handler
105 */
106 if (!SerialInterrupt(action, trap_frame) && callback[action])
107 {
108 trapcallback(action, trap_frame);
109 }
110 break;
111 case 0:
112 case 2:
113 case 6:
114 case 7:
115 case 8:
116 case 9:
117 case 0xa:
118 case 0xc:
119 case 0x20:
120 trapcallback(action, trap_frame);
121 break;
122
123 /* MMU Functions */
124 case 0x100:
125 initme();
126 trap_frame->srr1 |= 0x8000;
127 break;
128 case 0x101:
129 ret = mmuaddpage(arg1, (int)arg2);
130 break;
131 case 0x102:
132 mmudelpage(arg1, (int)arg2);
133 break;
134 case 0x103:
135 mmusetvsid((int)arg1, (int)arg2, (int)arg3);
136 break;
137 case 0x104:
138 ret = (int)callback[(int)arg1];
139 callback[(int)arg1] = (MmuTrapHandler)arg2;
140 break;
141 case 0x105:
142 mmugetpage(arg1, (int)arg2);
143 break;
144 case 0x106:
145 ret = mmunitest();
146 break;
147 case 0x107:
148 callkernel(arg1, arg2);
149 break;
150 case 0x108:
151 mmusetramsize((paddr_t)arg1);
152 break;
153 case 0x109:
154 return FirstUsablePage;
155 case 0x10a:
156 mmuallocvsid((int)arg1, (int)arg2);
157 break;
158 case 0x10b:
159 mmufreevsid((int)arg1, (int)arg2);
160 break;
161 case 0x10c:
162 ret = mmunewpage();
163 break;
164 case 0x10d:
165 copy(trap_frame, (void *)0xf040, sizeof(*trap_frame));
166 __asm__("mr 1,%0\n\tb trap_finish_start" : : "r"
167 (((int)trap_frame) - 16));
168 break;
169 case 0x10e:
170 dumpmap();
171 break;
172
173 case 0x200:
174 SerialSetUp((int)arg1, arg2, 9600);
175 break;
176 case 0x201:
177 TakeException((int)arg1, trap_frame);
178 break;
179
180 default:
181 while(1);
182 }
183
184 /* Restore bats when we were called voluntarily. We may not get a chance
185 * to do this after returning.
186 *
187 * At this point, we're in address space that matches physical space.
188 * We turn off mapping, restore bats, then let rfi switch us back to where
189 * we came.
190 */
191
192 if (action >= 0x100)
193 {
194 __asm__("mfmsr %0" : "=r" (tmp));
195 tmp &= ~0x30;
196 __asm__("mtmsr %0" : : "r" (tmp));
197
198 for(i = 0; i < 4; i++) {
199 SetBat(i, 0, GetPhys(0xf000 + i * 16), GetPhys(0xf004 + i * 16));
200 SetBat(i, 1, GetPhys(0xf008 + i * 16), GetPhys(0xf00c + i * 16));
201 }
202 }
203
204 return ret;
205 }
206
207 void trapcallback(int action, ppc_trap_frame_t *trap_frame)
208 {
209 if ((paddr_t)callback[action] < PAGETAB)
210 callback[action](action, trap_frame);
211 else
212 {
213 int framecopy = 0xf040;
214 copy((void *)framecopy, trap_frame, sizeof(*trap_frame));
215 trap_frame->srr0 = (int)callback[action];
216 trap_frame->srr1 &= 0x7fff;
217 trap_frame->gpr[3] = action;
218 trap_frame->gpr[4] = framecopy;
219 __asm__("mr 1,%0\n\tsubi 1,1,16\n\tb trap_finish_start" : : "r" (trap_frame));
220 }
221 }
222
223 void outchar(char c)
224 {
225 SetPhysByte(0x800003f8, c);
226 }
227
228 void copy(void *target, void *src, int bytes)
229 {
230 while(bytes--) *((char *)target++) = *((char *)src++);
231 }
232
233 void outstr(const char *str)
234 {
235 while(*str) outchar(*str);
236 }
237
238 void outdig(int dig)
239 {
240 if(dig < 10) outchar(dig + '0');
241 else outchar(dig - 10 + 'A');
242 }
243
244 void outnum(unsigned long num)
245 {
246 int i;
247 for( i = 0; i < 8; i++ )
248 {
249 outdig(num >> 28);
250 num <<= 4;
251 }
252 }
253
254 void fmtout(const char *str, ...)
255 {
256 va_list ap;
257 va_start(ap, str);
258 while(*str)
259 {
260 if(*str == '%')
261 {
262 if(str[1] == '%')
263 {
264 outchar('%');
265 }
266 else if(str[1] == 's')
267 {
268 outstr(va_arg(ap, const char *));
269 }
270 else
271 {
272 outnum(va_arg(ap, int));
273 }
274 str++;
275 }
276 else
277 {
278 outchar(*str);
279 }
280 str++;
281 }
282 va_end(ap);
283 }
284
285 void mmusetramsize(paddr_t ramsize)
286 {
287 ppc_map_t *last_map = &PpcPageTable[PPC_PAGE_NUMBER(ramsize)];
288 if(!RamSize)
289 {
290 RamSize = ramsize;
291 FirstUsablePage = (paddr_t)last_map;
292 NextPage = PPC_PAGE_NUMBER(FirstUsablePage) + 1;
293 }
294 }
295
296 int ignore(int trapCode, ppc_trap_frame_t *trap)
297 {
298 return 1;
299 }
300
301 int fpenable(int trapCode, ppc_trap_frame_t *trap)
302 {
303 /* Turn on FP */
304 trap->srr1 |= 8192;
305 return 1;
306 }
307
308 extern int trap_start[], trap_end[];
309 void copy_trap_handler(int trap)
310 {
311 int i;
312 paddr_t targetArea = trap * 0x100;
313
314 /* Set target addr */
315 trap_end[0] = (int)_mmumain;
316
317 for (i = 0; i <= trap_end - trap_start; i++)
318 {
319 SetPhys(targetArea + (i * sizeof(int)), trap_start[i]);
320 }
321 }
322
323 void initme()
324 {
325 int i;
326
327 for(i = 0; i < HTABSIZ / sizeof(int); i++)
328 {
329 ((int *)HTABORG)[i] = 0;
330 }
331
332 /* Default to hang on unknown exception */
333 for(i = 0; i < 30; i++)
334 {
335 callback[i] = (MmuTrapHandler)TakeException;
336 if (i != 1) /* Preserve reset handler */
337 copy_trap_handler(i);
338 }
339
340 /* Serial Interrupt */
341 callback[5] = 0; /* Do nothing until the user asks */
342
343 /* Program Exception */
344 callback[6] = (MmuTrapHandler)TakeException;
345
346 /* Floating point exception */
347 callback[8] = fpenable;
348
349 /* Ignore decrementer and EE */
350 callback[9] = ignore;
351
352 /* Single Step */
353 callback[0x20] = (MmuTrapHandler)TakeException;
354 }
355
356 ppc_map_t *allocpage()
357 {
358 MmuFreePage *FreePage = 0;
359
360 if (FreeList)
361 {
362 if ((void *)FreeList == (void *)PpcPageTable)
363 {
364 fmtout("Problem! FreeList: page 0 is free\n");
365 while(1);
366 }
367
368 FreePage = FreeList;
369 FreeList = FreeList->next;
370 ((ppc_map_t*)FreePage)->addr = MMU_ADDR_RESERVED;
371 return ((ppc_map_t*)FreePage);
372 }
373 else
374 {
375 while(!mmuisfreepage(NextPage) && NextPage < PPC_PAGE_NUMBER(RamSize))
376 {
377 NextPage++;
378 }
379 if (NextPage < PPC_PAGE_NUMBER(RamSize))
380 {
381 if (NextPage < 0x30)
382 {
383 fmtout("Problem! NextPage is low (%x)\n", NextPage);
384 while(1);
385 }
386
387 PpcPageTable[NextPage].addr = MMU_ADDR_RESERVED;
388 return &PpcPageTable[NextPage++];
389 }
390 else
391 {
392 return NULL;
393 }
394 }
395 }
396
397 void freepage(ppc_map_t *PagePtr)
398 {
399 MmuFreePage *FreePage = (MmuFreePage*)PagePtr;
400 PagePtr->proc = PagePtr->addr = 0;
401 FreePage->next = FreeList;
402 FreeList = FreePage;
403 }
404
405 MmuVsidTree *allocvsidtree()
406 {
407 if(FreeTree)
408 {
409 MmuVsidTree *result = (MmuVsidTree*)FreeTree;
410 FreeTree = FreeTree->next;
411 return result;
412 }
413 else if(TreeAlloc >= 3 || !NextTreePage)
414 {
415 ppc_map_t *map = allocpage();
416 NextTreePage = (MmuVsidTree*)PPC_PAGE_ADDR((map - PpcPageTable));
417 TreeAlloc = 1;
418 return NextTreePage;
419 }
420 else
421 {
422 return &NextTreePage[TreeAlloc++];
423 }
424 }
425
426 void freevsidtree(MmuVsidTree *tree)
427 {
428 int i;
429 for(i = 0; i < 256; i++)
430 if(tree->leaves[i])
431 freepage(tree->leaves[i]);
432 MmuFreeTree *NextFreeTree = (MmuFreeTree *)tree;
433 NextFreeTree->next = FreeTree;
434 FreeTree = NextFreeTree;
435 }
436
437 void *allocvsid(int vsid)
438 {
439 ppc_map_t *map = allocpage();
440 MmuVsidInfo *info;
441 if(!map) return 0;
442 map->pte.pteh = map->pte.ptel = 0;
443 info = (MmuVsidInfo*)PPC_PAGE_ADDR((map - PpcPageTable));
444 info->vsid = vsid;
445 info->next = VsidHead;
446 VsidHead = info;
447 return info;
448 }
449
450 void mmuallocvsid(int vsid, int mask)
451 {
452 int i;
453 for(i = 0; i < 16; i++)
454 {
455 if(mask & (1 << i))
456 allocvsid((vsid << 4) + i);
457 }
458 }
459
460 MmuVsidInfo *findvsid(int vsid)
461 {
462 MmuVsidInfo *info;
463 for(info = VsidHead; info; info = info->next)
464 {
465 if(info->vsid == vsid) return info;
466 }
467 return 0;
468 }
469
470 void freevsid(int vsid)
471 {
472 int i;
473 MmuVsidInfo *info = findvsid(vsid);
474 if(!info) return;
475 ppc_map_t *map = &PpcPageTable[PPC_PAGE_NUMBER((paddr_t)info)];
476 for(i = 0; i < 256; i++)
477 {
478 if(info->tree[i])
479 freevsidtree(info->tree[i]);
480 }
481 freepage(map);
482 }
483
484 void mmufreevsid(int vsid, int mask)
485 {
486 int i;
487 for(i = 0; i < 16; i++)
488 {
489 if(mask & (1 << i))
490 freevsid((vsid << 4) + i);
491 }
492 }
493
494 int mmuaddpage(ppc_map_info_t *info, int count)
495 {
496 int i, iva = 0, vsid, phys, virt;
497 int ptehi;
498 int ptelo, vsid_table_hi, vsid_table_lo;
499 ppc_map_t *PagePtr;
500 MmuVsidInfo *VsidInfo;
501 MmuVsidTree *VsidTree;
502
503 for(i = 0; i < count; i++)
504 {
505 info[i].phys &= ~PPC_PAGE_MASK;
506 info[i].addr &= ~PPC_PAGE_MASK;
507
508 virt = info[i].addr;
509 vsid = ((info[i].addr >> 28) & 15) | (info[i].proc << 4);
510 VsidInfo = findvsid(vsid);
511
512 if(!VsidInfo) return -1;
513
514 ptehi = (1 << 31) | (vsid << 7) | ((virt >> 22) & 0x3f);
515
516 if(info[i].phys) {
517 PagePtr = &PpcPageTable[PPC_PAGE_NUMBER(info[i].phys)];
518 } else {
519 PagePtr = allocpage();
520 if(!PagePtr)
521 {
522 return 0;
523 }
524 }
525
526 phys = PPC_PAGE_ADDR((PagePtr - PpcPageTable));
527 ptelo = phys & ~PPC_PAGE_MASK;
528
529 if (phys < 0x30000)
530 {
531 /* Should not be allocating physical */
532 fmtout("Allocated physical: %x, logical %x\n", phys, virt);
533 fmtout("PagePtr %x (page %d)\n", PagePtr, i);
534 fmtout("info [ %x %x %x %x ]\n", info[i].proc, info[i].addr, info[i].flags, info[i].phys);
535 while(1);
536 }
537
538 /* Update page data */
539 PagePtr->pte.pteh = ptehi;
540 PagePtr->pte.ptel = ptelo;
541 PagePtr->proc = info[i].proc;
542 PagePtr->addr = virt;
543
544 vsid_table_hi = virt >> 20 & 255;
545 vsid_table_lo = virt >> 12 & 255;
546
547 if(!VsidInfo->tree[vsid_table_hi])
548 VsidInfo->tree[vsid_table_hi] = allocvsidtree();
549 VsidTree = VsidInfo->tree[vsid_table_hi];
550 if(!VsidTree) return 0;
551 VsidTree->leaves[vsid_table_lo] = PagePtr;
552
553 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (iva));
554 }
555 return 1;
556 }
557
558 paddr_t mmunewpage()
559 {
560 ppc_map_t *PagePtr = allocpage();
561 if (!PagePtr) return 0;
562 return PPC_PAGE_ADDR(PagePtr - PpcPageTable);
563 }
564
565 ppc_pteg_t *PtegFromPage(ppc_map_t *map, int hfun)
566 {
567 if(!map->proc && !map->addr) return 0;
568 return &PpcHashedPTE[PtegNumber(map->addr, hfun)];
569 }
570
571 int PageMatch(vaddr_t addr, ppc_pte_t pte)
572 {
573 int vsid_pte = (pte.pteh >> 7) & 15, api_pte = pte.pteh & 63;
574 return
575 (((addr >> 28) & 15) == vsid_pte) &&
576 (((addr >> 22) & 63) == api_pte);
577 }
578
579 ppc_map_t *mmuvirtmap(vaddr_t addr)
580 {
581 int seg = (addr >> 28) & 15;
582 MmuVsidInfo *seginfo = Segs[seg];
583 MmuVsidTree *segtree = 0;
584 if(!seginfo) return 0;
585 segtree = seginfo->tree[(addr >> 20) & 255];
586 if(!segtree) return 0;
587 return segtree->leaves[(addr >> 12) & 255];
588 }
589
590 void mmudelpage(ppc_map_info_t *info, int count)
591 {
592 int i, j, k, ipa;
593 ppc_map_t *PagePtr;
594 ppc_pteg_t *PageEntry;
595 ppc_pte_t ZeroPte = { 0 };
596
597 for(i = 0; i < count; i++)
598 {
599 if (info[i].phys)
600 {
601 ipa = info[i].phys;
602 PagePtr = &PpcPageTable[ipa];
603 info[i].proc = PagePtr->proc;
604 info[i].addr = PagePtr->addr;
605 }
606 else
607 {
608 PagePtr = mmuvirtmap(info[i].addr);
609 ipa = PPC_PAGE_ADDR(PagePtr - PpcPageTable);
610 }
611
612 for(j = 0; j < 2; j++)
613 {
614 PageEntry = PtegFromPage(PagePtr, j);
615 for(k = 0; k < 8; k++)
616 {
617 if(PageMatch(ipa, PageEntry->block[k]))
618 {
619 if(PageEntry->block[k].ptel & 0x100)
620 info[i].flags |= MMU_PAGE_DIRTY;
621 PageEntry->block[k] = ZeroPte;
622 }
623 }
624 }
625 freepage(PagePtr);
626 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (info[i].addr));
627 }
628 }
629
630 void mmugetpage(ppc_map_info_t *info, int count)
631 {
632 int i;
633 ppc_map_t *PagePtr;
634
635 for( i = 0; i < count; i++ )
636 {
637 if(!info[i].addr && !info[i].proc)
638 {
639 PagePtr = &((ppc_map_t*)PAGETAB)[info[i].phys];
640 info[i].proc = PagePtr->proc;
641 info[i].addr = PagePtr->addr;
642 info[i].flags = MMU_ALL_RW;
643 } else {
644 vaddr_t addr = info[i].addr;
645 int vsid = ((addr >> 28) & 15) | (info[i].proc << 4);
646 PagePtr = mmuvirtmap(info[i].addr);
647 if(!PagePtr)
648 info[i].phys = 0;
649 else
650 {
651 info[i].phys = PPC_PAGE_ADDR(PagePtr - PpcPageTable);
652 info[i].flags = MMU_ALL_RW; // HACK
653 }
654 }
655 }
656 }
657
658 int mmuisfreepage(paddr_t pageno)
659 {
660 ppc_map_t *PagePtr = PpcPageTable + pageno;
661 return !PagePtr->addr;
662 }
663
664 void mmusetvsid(int start, int end, int vsid)
665 {
666 int i, sr, s_vsid;
667 for(i = start; i < end; i++)
668 {
669 s_vsid = (vsid << 4) | (i & 15);
670 sr = (GetSR(i) & ~PPC_VSID_MASK) | s_vsid;
671 if (Booted)
672 SetSR(i, sr);
673 Segs[i] = findvsid(s_vsid);
674 Vsid[i] = vsid;
675 }
676 }
677
678 int ptegreload(ppc_trap_frame_t *frame, vaddr_t addr)
679 {
680 int hfun = (Clock >> 3) & 1, ptegnum = PtegNumber(addr, hfun);
681 ppc_map_t *map = mmuvirtmap(addr);
682 if(!map) return 0;
683 map->pte.pteh = (map->pte.pteh & ~64) | (hfun << 6);
684 PpcHashedPTE[ptegnum].block[Clock & 7] = map->pte;
685 #if 0
686 fmtout("Reloading addr %x (phys %x) at %x[%x] (%x:%x)\r\n",
687 addr, PPC_PAGE_ADDR(map - PpcPageTable), ptegnum, Clock & 15,
688 PpcHashedPTE[ptegnum].block[Clock&7].pteh,
689 PpcHashedPTE[ptegnum].block[Clock&7].ptel);
690 #endif
691 Clock++;
692 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (addr));
693 return 1;
694 }
695
696 void printmap(vaddr_t vaddr, ppc_map_t *map)
697 {
698 fmtout("%x: proc %x addr %x\n",
699 PPC_PAGE_ADDR(map - PpcPageTable),
700 map->proc, vaddr);
701 }
702
703 void dumptree(vaddr_t vaddr, MmuVsidTree *tree)
704 {
705 int j;
706
707 for (j = 0; j < 256; j++)
708 {
709 if (tree->leaves[j])
710 {
711 printmap(vaddr | (j << 12), tree->leaves[j]);
712 }
713 }
714 }
715
716 void dumpvsid(MmuVsidInfo *vsid)
717 {
718 int i;
719
720 fmtout("vsid %d (%x):\n", vsid->vsid>>4, vsid->vsid<<28);
721 for (i = 0; i < 256; i++)
722 {
723 if (vsid->tree[i])
724 {
725 dumptree((vsid->vsid<<28) | i << 20, vsid->tree[i]);
726 }
727 }
728 }
729
730 void dumpmap()
731 {
732 int i,j;
733 ppc_map_t *map;
734 MmuVsidInfo *vsid;
735 fmtout("Address spaces:\n");
736 for (vsid = VsidHead; vsid; vsid = vsid->next)
737 {
738 dumpvsid(vsid);
739 }
740 }
741
742 void callkernel(void *fun_ptr, void *arg)
743 {
744 int i;
745
746 Booted = 1;
747
748 for (i = 0; i < 16; i++)
749 {
750 // Patch up the vsid map. We shouldn't muck with these until we're
751 // booted.
752 mmusetvsid(i, i+1, Vsid[i]);
753 }
754
755 void (*fun)(void *) = fun_ptr;
756 __asm__("mfmsr 3\n\t"
757 "ori 3,3,0x30\n\t"
758 "mtmsr 3\n\t"
759 "mtsdr1 %0\n\t"
760 "mr 0,%2\n\t"
761 "mtctr 0\n\t"
762 "mr 3,%1\n\t"
763 "bctrl\n\t"
764 : : "r" (HTABORG), "r" (arg), "r" (fun));
765 /* BYE ! */
766 }