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