60924cd4d5c2447ae744094bc833c7e4efd2bd3a
[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 MmuPageCallback callback;
44 typedef struct _MmuFreePage {
45 int page;
46 struct _MmuFreePage *next;
47 } MmuFreePage;
48 typedef struct _MmuFreeTree {
49 struct _MmuFreeTree *next;
50 } MmuFreeTree;
51 typedef struct _MmuVsidTree {
52 ppc_map_t *leaves[256];
53 } MmuVsidTree;
54 typedef struct _MmuVsidInfo {
55 int vsid;
56 struct _MmuVsidInfo *next;
57 MmuVsidTree *tree[256];
58 } MmuVsidInfo;
59 MmuFreePage *FreeList;
60 // Pages are allocated one by one until NextPage == RamSize >> PPC_PAGE_SHIFT
61 // Then we take only from the free list
62 int Clock = 0, TreeAlloc = 0;
63 paddr_t RamSize, FirstUsablePage, NextPage;
64 MmuVsidTree *NextTreePage = 0;
65 MmuFreeTree *FreeTree;
66 MmuVsidInfo *Segs[16], *VsidHead = 0;
67
68 extern void fmtout(const char *fmt, ...);
69 int ptegreload(ppc_trap_frame_t *frame, vaddr_t addr);
70
71 __asm__(".text\n\t"
72 ".globl mmumain\n\t"
73 ".globl _mmumain\n\t"
74 ".globl oldstack\n\t"
75 "mmumain:\n\t"
76 "lis 7,oldstack@ha\n\t"
77 "addi 7,7,oldstack@l\n\t"
78 "mflr 0\n\t"
79 "stw 1,0(7)\n\t"
80 "lis 1,2\n\t"
81 "subi 1,1,16\n\t"
82 "stw 0,0(1)\n\t"
83 "bl _mmumain\n\t"
84 "lis 7,oldstack@ha\n\t"
85 "addi 7,7,oldstack@l\n\t"
86 "lwz 0,0(1)\n\t"
87 "lwz 1,0(7)\n\t"
88 "mtlr 0\n\t"
89 "blr\n"
90 "oldstack:\n\t"
91 ".long 0\n\t");
92
93 __asm__(".text\n\t"
94 ".globl data_miss_finish_start\n"
95 "data_miss_finish_start:\n\t"
96 "lwz 2,8(1)\n\t"
97 "lwz 3,12(1)\n\t"
98 "lwz 4,16(1)\n\t"
99 "lwz 5,20(1)\n\t"
100 "lwz 6,24(1)\n\t"
101 "lwz 7,28(1)\n\t"
102 "lwz 8,32(1)\n\t"
103 "lwz 9,36(1)\n\t"
104 "lwz 10,40(1)\n\t"
105 "lwz 11,44(1)\n\t"
106 "lwz 12,48(1)\n\t"
107 "lwz 13,52(1)\n\t"
108 "lwz 14,56(1)\n\t"
109 "lwz 15,60(1)\n\t"
110 "lwz 16,64(1)\n\t"
111 "lwz 17,68(1)\n\t"
112 "lwz 18,72(1)\n\t"
113 "lwz 19,76(1)\n\t"
114 "lwz 20,80(1)\n\t"
115 "lwz 21,84(1)\n\t"
116 "lwz 22,88(1)\n\t"
117 "lwz 23,92(1)\n\t"
118 "lwz 24,96(1)\n\t"
119 "lwz 25,100(1)\n\t"
120 "lwz 26,104(1)\n\t"
121 "lwz 27,108(1)\n\t"
122 "lwz 28,112(1)\n\t"
123 "lwz 29,116(1)\n\t"
124 "lwz 30,120(1)\n\t"
125 "lwz 31,124(1)\n\t"
126 "lwz 0,128(1)\n\t"
127 "mtlr 0\n\t"
128 "lwz 0,132(1)\n\t"
129 "mtcr 0\n\t"
130 "lwz 0,136(1)\n\t"
131 "mtctr 0\n\t"
132 "lwz 0,0(1)\n\t"
133 "mfsprg1 1\n\t"
134 "rfi\n\t");
135
136 /*
137 * Trap frame:
138 * r0 .. r32
139 * lr, ctr, srr0, srr1, dsisr
140 */
141 __asm__(".text\n\t"
142 ".globl data_miss_start\n\t"
143 ".globl data_miss_end\n\t"
144 "data_miss_start:\n\t"
145 "mtsprg1 1\n\t"
146 "lis 1,2\n\t"
147 "subi 1,1,256\n\t"
148 "stw 0,0(1)\n\t"
149 "mfsprg1 0\n\t"
150 "stw 0,4(1)\n\t"
151 "stw 2,8(1)\n\t"
152 "stw 3,12(1)\n\t"
153 "stw 4,16(1)\n\t"
154 "stw 5,20(1)\n\t"
155 "stw 6,24(1)\n\t"
156 "stw 7,28(1)\n\t"
157 "stw 8,32(1)\n\t"
158 "stw 9,36(1)\n\t"
159 "stw 10,40(1)\n\t"
160 "stw 11,44(1)\n\t"
161 "stw 12,48(1)\n\t"
162 "stw 13,52(1)\n\t"
163 "stw 14,56(1)\n\t"
164 "stw 15,60(1)\n\t"
165 "stw 16,64(1)\n\t"
166 "stw 17,68(1)\n\t"
167 "stw 18,72(1)\n\t"
168 "stw 19,76(1)\n\t"
169 "stw 20,80(1)\n\t"
170 "stw 21,84(1)\n\t"
171 "stw 22,88(1)\n\t"
172 "stw 23,92(1)\n\t"
173 "stw 24,96(1)\n\t"
174 "stw 25,100(1)\n\t"
175 "stw 26,104(1)\n\t"
176 "stw 27,108(1)\n\t"
177 "stw 28,112(1)\n\t"
178 "stw 29,116(1)\n\t"
179 "stw 30,120(1)\n\t"
180 "stw 31,124(1)\n\t"
181 "mflr 0\n\t"
182 "stw 0,128(1)\n\t"
183 "mfcr 0\n\t"
184 "stw 0,132(1)\n\t"
185 "mfctr 0\n\t"
186 "stw 0,136(1)\n\t"
187 "mfsrr0 0\n\t"
188 "stw 0,140(1)\n\t"
189 "mfsrr1 0\n\t"
190 "stw 0,144(1)\n\t"
191 "mfdsisr 0\n\t"
192 "stw 0,148(1)\n\t"
193 "mfdar 0\n\t"
194 "stw 0,152(1)\n\t"
195 "mfxer 0\n\t"
196 "stw 0,156(1)\n\t"
197 "li 3,100\n\t"
198 "mr 4,1\n\t"
199 "lis 5,data_miss_finish_start@ha\n\t"
200 "addi 5,5,data_miss_finish_start@l\n\t"
201 "mtlr 5\n\t"
202 "lis 5,_mmumain@ha\n\t"
203 "addi 5,5,_mmumain@l\n\t"
204 "mtctr 5\n\t"
205 "bctr\n"
206 "data_miss_end:\n\t"
207 ".space 4");
208
209 extern int data_miss_end, data_miss_start;
210
211 int _mmumain(int action, void *arg1, void *arg2, void *arg3)
212 {
213 void (*fun)(void *) = arg1;
214 ppc_trap_frame_t *trap_frame = arg1;
215 int ret = 0;
216
217 switch(action)
218 {
219 case 0:
220 initme();
221 break;
222 case 1:
223 ret = mmuaddpage(arg1, (int)arg2);
224 break;
225 case 2:
226 mmudelpage(arg1, (int)arg2);
227 break;
228 case 3:
229 mmusetvsid((int)arg1, (int)arg2, (int)arg3);
230 break;
231 case 4:
232 /* Miss callback = arg1 */
233 ret = (int)callback;
234 callback = arg1;
235 break;
236 case 5:
237 mmugetpage(arg1, (int)arg2);
238 break;
239 case 6:
240 ret = mmunitest();
241 break;
242 case 7:
243 __asm__("mfmsr 3\n\t"
244 "ori 3,3,0x30\n\t"
245 "mtmsr 3\n\t"
246 "mtsdr1 %0\n\t"
247 "mr 0,%2\n\t"
248 "mtctr 0\n\t"
249 "mr 3,%1\n\t"
250 "bctrl\n\t"
251 : : "r" (HTABORG), "r" (arg2), "r" (fun));
252 /* BYE ! */
253 break;
254 case 8:
255 mmusetramsize((paddr_t)arg1);
256 break;
257 case 9:
258 return FirstUsablePage;
259 case 10:
260 mmuallocvsid((int)arg1, (int)arg2);
261 break;
262 case 11:
263 mmufreevsid((int)arg1, (int)arg2);
264 break;
265 case 100:
266 if(!ptegreload(trap_frame, trap_frame->dar))
267 {
268 __asm__("mfmsr 3\n\tori 3,3,0x30\n\tmtmsr 3\n\t");
269 callback(0,arg1);
270 }
271 break;
272 case 101:
273 if(!ptegreload(trap_frame, trap_frame->srr0))
274 {
275 __asm__("mfmsr 3\n\tori 3,3,0x30\n\tmtmsr 3\n\t");
276 callback(1,arg1);
277 }
278 break;
279 default:
280 while(1);
281 }
282
283 return ret;
284 }
285
286 void outchar(char c)
287 {
288 SetPhysByte(0x800003f8, c);
289 }
290
291 void outstr(const char *str)
292 {
293 while(*str) outchar(*str);
294 }
295
296 void outdig(int dig)
297 {
298 if(dig < 10) outchar(dig + '0');
299 else outchar(dig - 10 + 'A');
300 }
301
302 void outnum(unsigned long num)
303 {
304 int i;
305 for( i = 0; i < 8; i++ )
306 {
307 outdig(num >> 28);
308 num <<= 4;
309 }
310 }
311
312 void fmtout(const char *str, ...)
313 {
314 va_list ap;
315 va_start(ap, str);
316 while(*str)
317 {
318 if(*str == '%')
319 {
320 if(str[1] == '%')
321 {
322 outchar('%');
323 }
324 else if(str[1] == 's')
325 {
326 outstr(va_arg(ap, const char *));
327 }
328 else
329 {
330 outnum(va_arg(ap, int));
331 }
332 str++;
333 }
334 else
335 {
336 outchar(*str);
337 }
338 str++;
339 }
340 va_end(ap);
341 }
342
343 void mmusetramsize(paddr_t ramsize)
344 {
345 ppc_map_t *last_map = &PpcPageTable[PPC_PAGE_NUMBER(ramsize)];
346 if(!RamSize)
347 {
348 RamSize = ramsize;
349 FirstUsablePage = (paddr_t)last_map;
350 NextPage = PPC_PAGE_NUMBER(FirstUsablePage);
351 }
352 }
353
354 void initme()
355 {
356 int i;
357 int *target, *start;
358
359 for(i = 0; i < HTABSIZ / sizeof(int); i++)
360 {
361 ((int *)HTABORG)[i] = 0;
362 }
363
364 for(target = (int *)0x300, start = &data_miss_start; start < &data_miss_end; start++, target++)
365 {
366 SetPhys((paddr_t)target, *start);
367 }
368
369 (&data_miss_start)[50]++;
370
371 for(target = (int *)0x400, start = &data_miss_start; start < &data_miss_end; start++, target++)
372 {
373 SetPhys((paddr_t)target, *start);
374 }
375 }
376
377 ppc_map_t *allocpage()
378 {
379 MmuFreePage *FreePage = 0;
380
381 if(NextPage < PPC_PAGE_NUMBER(RamSize)) {
382 return &PpcPageTable[NextPage++];
383 } else {
384 FreePage = FreeList;
385 FreeList = FreeList->next;
386 return ((ppc_map_t*)FreePage);
387 }
388 }
389
390 void freepage(ppc_map_t *PagePtr)
391 {
392 MmuFreePage *FreePage = (MmuFreePage*)PagePtr;
393 PagePtr->proc = PagePtr->addr = 0;
394 FreePage->next = FreeList;
395 FreeList = FreePage;
396 }
397
398 MmuVsidTree *allocvsidtree()
399 {
400 if(FreeTree)
401 {
402 MmuVsidTree *result = (MmuVsidTree*)FreeTree;
403 FreeTree = FreeTree->next;
404 return result;
405 }
406 else if(TreeAlloc >= 3 || !NextTreePage)
407 {
408 ppc_map_t *map = allocpage();
409 NextTreePage = (MmuVsidTree*)PPC_PAGE_ADDR((map - PpcPageTable));
410 TreeAlloc = 1;
411 return NextTreePage;
412 }
413 else
414 {
415 return &NextTreePage[TreeAlloc++];
416 }
417 }
418
419 void freevsidtree(MmuVsidTree *tree)
420 {
421 int i;
422 for(i = 0; i < 256; i++)
423 if(tree->leaves[i])
424 freepage(tree->leaves[i]);
425 MmuFreeTree *NextFreeTree = (MmuFreeTree *)tree;
426 NextFreeTree->next = FreeTree;
427 FreeTree = NextFreeTree;
428 }
429
430 void *allocvsid(int vsid)
431 {
432 ppc_map_t *map = allocpage();
433 MmuVsidInfo *info;
434 if(!map) return 0;
435 map->pte.pteh = map->pte.ptel = 0;
436 info = (MmuVsidInfo*)PPC_PAGE_ADDR((map - PpcPageTable));
437 info->vsid = vsid;
438 info->next = VsidHead;
439 VsidHead = info;
440 return info;
441 }
442
443 void mmuallocvsid(int vsid, int mask)
444 {
445 int i;
446 for(i = 0; i < 16; i++)
447 {
448 if(mask & (1 << i))
449 allocvsid((vsid << 4) + i);
450 }
451 }
452
453 MmuVsidInfo *findvsid(int vsid)
454 {
455 MmuVsidInfo *info;
456 for(info = VsidHead; info; info = info->next)
457 {
458 if(info->vsid == vsid) return info;
459 }
460 return 0;
461 }
462
463 void freevsid(int vsid)
464 {
465 int i;
466 MmuVsidInfo *info = findvsid(vsid);
467 if(!info) return;
468 ppc_map_t *map = &PpcPageTable[PPC_PAGE_NUMBER((paddr_t)info)];
469 for(i = 0; i < 256; i++)
470 {
471 if(info->tree[i])
472 freevsidtree(info->tree[i]);
473 }
474 freepage(map);
475 }
476
477 void mmufreevsid(int vsid, int mask)
478 {
479 int i;
480 for(i = 0; i < 16; i++)
481 {
482 if(mask & (1 << i))
483 allocvsid((vsid << 4) + i);
484 }
485 }
486
487 int mmuaddpage(ppc_map_info_t *info, int count)
488 {
489 int i, iva = 0, vsid, phys, virt;
490 int ptehi;
491 int ptelo, vsid_table_hi, vsid_table_lo;
492 ppc_map_t *PagePtr;
493 MmuVsidInfo *VsidInfo;
494 MmuVsidTree *VsidTree;
495
496 for(i = 0; i < count; i++)
497 {
498 virt = info[i].addr;
499 vsid = ((info[i].addr >> 28) & 15) | (info[i].proc << 4);
500 VsidInfo = findvsid(vsid);
501
502 if(!VsidInfo) return -1;
503
504 ptehi = (1 << 31) | (vsid << 7) | ((virt >> 22) & 0x3f);
505
506 if(info[i].phys) {
507 PagePtr = &PpcPageTable[PPC_PAGE_NUMBER(info[i].phys)];
508 } else {
509 PagePtr = allocpage();
510 if(!PagePtr)
511 {
512 return 0;
513 }
514 }
515
516 phys = PPC_PAGE_ADDR((PagePtr - PpcPageTable));
517 ptelo = phys & ~PPC_PAGE_MASK;
518
519 /* Update page data */
520 PagePtr->pte.pteh = ptehi;
521 PagePtr->pte.ptel = ptelo;
522 PagePtr->proc = info[i].proc;
523 PagePtr->addr = virt;
524
525 vsid_table_hi = virt >> 20 & 255;
526 vsid_table_lo = virt >> 12 & 255;
527
528 if(!VsidInfo->tree[vsid_table_hi])
529 VsidInfo->tree[vsid_table_hi] = allocvsidtree();
530 VsidTree = VsidInfo->tree[vsid_table_hi];
531 if(!VsidTree) return 0;
532 VsidTree->leaves[vsid_table_lo] = PagePtr;
533
534 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (iva));
535 }
536 return 1;
537 }
538
539 ppc_pteg_t *PtegFromPage(ppc_map_t *map, int hfun)
540 {
541 if(!map->proc && !map->addr) return 0;
542 return &PpcHashedPTE[PtegNumber(map->addr, hfun)];
543 }
544
545 int PageMatch(vaddr_t addr, ppc_pte_t pte)
546 {
547 int vsid_pte = (pte.pteh >> 7) & 15, api_pte = pte.pteh & 63;
548 return
549 (((addr >> 28) & 15) == vsid_pte) &&
550 (((addr >> 22) & 63) == api_pte);
551 }
552
553 ppc_map_t *mmuvirtmap(vaddr_t addr, int vsid)
554 {
555 int seg = (addr >> 28) & 15;
556 MmuVsidInfo *seginfo = Segs[seg];
557 MmuVsidTree *segtree = 0;
558 if(!seginfo) return 0;
559 segtree = seginfo->tree[(addr >> 20) & 255];
560 if(!segtree) return 0;
561 return segtree->leaves[(addr >> 12) & 255];
562 }
563
564 void mmudelpage(ppc_map_info_t *info, int count)
565 {
566 int i, j, k, ipa;
567 ppc_map_t *PagePtr;
568 ppc_pteg_t *PageEntry;
569 ppc_pte_t ZeroPte = { 0 };
570
571 for(i = 0; i < count; i++)
572 {
573 if (info[i].phys)
574 {
575 ipa = info[i].phys;
576 PagePtr = &PpcPageTable[ipa];
577 info[i].proc = PagePtr->proc;
578 info[i].addr = PagePtr->addr;
579 }
580 else
581 {
582 PagePtr = mmuvirtmap(info[i].proc, info[i].addr);
583 ipa = PPC_PAGE_ADDR(PagePtr - PpcPageTable);
584 }
585
586 for(j = 0; j < 2; j++)
587 {
588 PageEntry = PtegFromPage(PagePtr, j);
589 for(k = 0; k < 8; k++)
590 {
591 if(PageMatch(ipa, PageEntry->block[k]))
592 {
593 if(PageEntry->block[k].ptel & 0x100)
594 info[i].flags |= MMU_PAGE_DIRTY;
595 PageEntry->block[k] = ZeroPte;
596 }
597 }
598 }
599 freepage(PagePtr);
600 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (info[i].addr));
601 }
602 }
603
604 void mmugetpage(ppc_map_info_t *info, int count)
605 {
606 int i;
607 ppc_map_t *PagePtr;
608
609 for( i = 0; i < count; i++ )
610 {
611 if(!info[i].addr && !info[i].proc)
612 {
613 PagePtr = &((ppc_map_t*)PAGETAB)[info[i].phys];
614 info[i].proc = PagePtr->proc;
615 info[i].addr = PagePtr->addr;
616 info[i].flags = MMU_ALL_RW;
617 } else {
618 vaddr_t addr = info[i].addr;
619 int vsid = ((addr >> 28) & 15) | (info[i].proc << 4);
620 PagePtr = mmuvirtmap(info[i].addr, vsid);
621 if(!PagePtr)
622 info[i].phys = 0;
623 else
624 {
625 info[i].phys = PPC_PAGE_ADDR(PagePtr - PpcPageTable);
626 info[i].flags = MMU_ALL_RW; // HACK
627 }
628 }
629 }
630 }
631
632 void mmusetvsid(int start, int end, int vsid)
633 {
634 int i, sr, s_vsid;
635 for(i = start; i < end; i++)
636 {
637 s_vsid = (vsid << 4) | (i & 15);
638 sr = (GetSR(i) & ~PPC_VSID_MASK) | s_vsid;
639 SetSR(i, sr);
640 Segs[i] = findvsid(s_vsid);
641 }
642 }
643
644 int ptegreload(ppc_trap_frame_t *frame, vaddr_t addr)
645 {
646 int hfun = (Clock >> 3) & 1, ptegnum = PtegNumber(addr, hfun);
647 int vsid = GetSR((addr >> 28) & 15) & PPC_VSID_MASK;
648 ppc_map_t *map = mmuvirtmap(addr, vsid);
649 if(!map) return 0;
650 map->pte.pteh = (map->pte.pteh & ~64) | (hfun << 6);
651 PpcHashedPTE[ptegnum].block[Clock & 7] = map->pte;
652 #if 0
653 fmtout("Reloading addr %x (phys %x) at %x[%x] (%x:%x)\r\n",
654 addr, PPC_PAGE_ADDR(map - PpcPageTable), ptegnum, Clock & 15,
655 PpcHashedPTE[ptegnum].block[Clock&7].pteh,
656 PpcHashedPTE[ptegnum].block[Clock&7].ptel);
657 #endif
658 Clock++;
659 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (addr));
660 return 1;
661 }