b13d3b3713048ff76395912c6750c700e4a3283a
[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 MmuTrapHandler callback[0x30];
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, GdbAttach = 0, Booted = 0, Vsid[16];
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 void SerialSetUp(int deviceType, void *deviceAddr, int baud);
71 int SerialInterrupt(int n, ppc_trap_frame_t *tf);
72 void TakeException(int n, ppc_trap_frame_t *tf);
73
74 int _mmumain(int action, void *arg1, void *arg2, void *arg3, void *tf)
75 {
76 ppc_trap_frame_t *trap_frame = (action >= 0x100) ? tf : arg1;
77 int ret = 0;
78
79 switch(action)
80 {
81 /* Trap Handlers */
82 case 3:
83 if(!ptegreload(trap_frame, trap_frame->dar))
84 {
85 __asm__("mfmsr 3\n\tori 3,3,0x30\n\tmtmsr 3\n\t");
86 if (!callback[action](action,trap_frame)) TakeException(action, trap_frame);
87 }
88 break;
89 case 4:
90 if(!ptegreload(trap_frame, trap_frame->srr0))
91 {
92 __asm__("mfmsr 3\n\tori 3,3,0x30\n\tmtmsr 3\n\t");
93 if (!callback[action](action,trap_frame)) TakeException(action, trap_frame);
94 }
95 break;
96
97 case 5:
98 /* EE -- Try to get a serial interrupt if debugging enabled, then fall
99 * back to primary handler
100 */
101 if (!SerialInterrupt(action, trap_frame))
102 callback[action](action, trap_frame);
103 else
104 trap_frame->srr1 |= 0x8000;
105 break;
106 case 0:
107 case 2:
108 case 6:
109 case 7:
110 case 8:
111 case 9:
112 case 0xa:
113 case 0xc:
114 case 0x20:
115 if (!callback[action](action,trap_frame)) TakeException(action, trap_frame);
116 break;
117
118 /* MMU Functions */
119 case 0x100:
120 initme();
121 trap_frame->srr1 |= 0x8000;
122 break;
123 case 0x101:
124 ret = mmuaddpage(arg1, (int)arg2);
125 break;
126 case 0x102:
127 mmudelpage(arg1, (int)arg2);
128 break;
129 case 0x103:
130 mmusetvsid((int)arg1, (int)arg2, (int)arg3);
131 break;
132 case 0x104:
133 ret = (int)callback[(int)arg1];
134 callback[(int)arg1] = (MmuTrapHandler)arg2;
135 break;
136 case 0x105:
137 mmugetpage(arg1, (int)arg2);
138 break;
139 case 0x106:
140 ret = mmunitest();
141 break;
142 case 0x107:
143 callkernel(arg1, arg2);
144 break;
145 case 0x108:
146 mmusetramsize((paddr_t)arg1);
147 break;
148 case 0x109:
149 return FirstUsablePage;
150 case 0x10a:
151 mmuallocvsid((int)arg1, (int)arg2);
152 break;
153 case 0x10b:
154 mmufreevsid((int)arg1, (int)arg2);
155 break;
156
157 case 0x200:
158 SerialSetUp((int)arg1, arg2, 9600);
159 break;
160 case 0x201:
161 TakeException((int)arg1, trap_frame);
162 break;
163
164 default:
165 while(1);
166 }
167
168 return ret;
169 }
170
171 void outchar(char c)
172 {
173 SetPhysByte(0x800003f8, c);
174 }
175
176 void outstr(const char *str)
177 {
178 while(*str) outchar(*str);
179 }
180
181 void outdig(int dig)
182 {
183 if(dig < 10) outchar(dig + '0');
184 else outchar(dig - 10 + 'A');
185 }
186
187 void outnum(unsigned long num)
188 {
189 int i;
190 for( i = 0; i < 8; i++ )
191 {
192 outdig(num >> 28);
193 num <<= 4;
194 }
195 }
196
197 void fmtout(const char *str, ...)
198 {
199 va_list ap;
200 va_start(ap, str);
201 while(*str)
202 {
203 if(*str == '%')
204 {
205 if(str[1] == '%')
206 {
207 outchar('%');
208 }
209 else if(str[1] == 's')
210 {
211 outstr(va_arg(ap, const char *));
212 }
213 else
214 {
215 outnum(va_arg(ap, int));
216 }
217 str++;
218 }
219 else
220 {
221 outchar(*str);
222 }
223 str++;
224 }
225 va_end(ap);
226 }
227
228 void mmusetramsize(paddr_t ramsize)
229 {
230 ppc_map_t *last_map = &PpcPageTable[PPC_PAGE_NUMBER(ramsize)];
231 if(!RamSize)
232 {
233 RamSize = ramsize;
234 FirstUsablePage = (paddr_t)last_map;
235 NextPage = PPC_PAGE_NUMBER(FirstUsablePage);
236 }
237 }
238
239 int ignore(int trapCode, ppc_trap_frame_t *trap)
240 {
241 return 1;
242 }
243
244 int fpenable(int trapCode, ppc_trap_frame_t *trap)
245 {
246 /* Turn on FP */
247 trap->srr1 |= 8192;
248 return 1;
249 }
250
251 extern int trap_start[], trap_end[];
252 void copy_trap_handler(int trap)
253 {
254 int i;
255 paddr_t targetArea = trap * 0x100;
256
257 /* Set target addr */
258 trap_end[0] = (int)_mmumain;
259
260 for (i = 0; i <= trap_end - trap_start; i++)
261 {
262 SetPhys(targetArea + (i * sizeof(int)), trap_start[i]);
263 }
264 }
265
266 void initme()
267 {
268 int i;
269
270 for(i = 0; i < HTABSIZ / sizeof(int); i++)
271 {
272 ((int *)HTABORG)[i] = 0;
273 }
274
275 /* Default to hang on unknown exception */
276 for(i = 0; i < 30; i++)
277 {
278 callback[i] = TakeException;
279 if (i != 1) /* Preserve reset handler */
280 copy_trap_handler(i);
281 }
282
283 /* Serial Interrupt */
284 callback[5] = SerialInterrupt;
285
286 /* Program Exception */
287 callback[6] = TakeException;
288
289 /* Floating point exception */
290 callback[8] = fpenable;
291
292 /* Ignore decrementer and EE */
293 callback[9] = ignore;
294
295 /* Single Step */
296 callback[0x20] = TakeException;
297 }
298
299 ppc_map_t *allocpage()
300 {
301 MmuFreePage *FreePage = 0;
302
303 if(NextPage < PPC_PAGE_NUMBER(RamSize)) {
304 return &PpcPageTable[NextPage++];
305 } else {
306 FreePage = FreeList;
307 FreeList = FreeList->next;
308 return ((ppc_map_t*)FreePage);
309 }
310 }
311
312 void freepage(ppc_map_t *PagePtr)
313 {
314 MmuFreePage *FreePage = (MmuFreePage*)PagePtr;
315 PagePtr->proc = PagePtr->addr = 0;
316 FreePage->next = FreeList;
317 FreeList = FreePage;
318 }
319
320 MmuVsidTree *allocvsidtree()
321 {
322 if(FreeTree)
323 {
324 MmuVsidTree *result = (MmuVsidTree*)FreeTree;
325 FreeTree = FreeTree->next;
326 return result;
327 }
328 else if(TreeAlloc >= 3 || !NextTreePage)
329 {
330 ppc_map_t *map = allocpage();
331 NextTreePage = (MmuVsidTree*)PPC_PAGE_ADDR((map - PpcPageTable));
332 TreeAlloc = 1;
333 return NextTreePage;
334 }
335 else
336 {
337 return &NextTreePage[TreeAlloc++];
338 }
339 }
340
341 void freevsidtree(MmuVsidTree *tree)
342 {
343 int i;
344 for(i = 0; i < 256; i++)
345 if(tree->leaves[i])
346 freepage(tree->leaves[i]);
347 MmuFreeTree *NextFreeTree = (MmuFreeTree *)tree;
348 NextFreeTree->next = FreeTree;
349 FreeTree = NextFreeTree;
350 }
351
352 void *allocvsid(int vsid)
353 {
354 ppc_map_t *map = allocpage();
355 MmuVsidInfo *info;
356 if(!map) return 0;
357 map->pte.pteh = map->pte.ptel = 0;
358 info = (MmuVsidInfo*)PPC_PAGE_ADDR((map - PpcPageTable));
359 info->vsid = vsid;
360 info->next = VsidHead;
361 VsidHead = info;
362 return info;
363 }
364
365 void mmuallocvsid(int vsid, int mask)
366 {
367 int i;
368 for(i = 0; i < 16; i++)
369 {
370 if(mask & (1 << i))
371 allocvsid((vsid << 4) + i);
372 }
373 }
374
375 MmuVsidInfo *findvsid(int vsid)
376 {
377 MmuVsidInfo *info;
378 for(info = VsidHead; info; info = info->next)
379 {
380 if(info->vsid == vsid) return info;
381 }
382 return 0;
383 }
384
385 void freevsid(int vsid)
386 {
387 int i;
388 MmuVsidInfo *info = findvsid(vsid);
389 if(!info) return;
390 ppc_map_t *map = &PpcPageTable[PPC_PAGE_NUMBER((paddr_t)info)];
391 for(i = 0; i < 256; i++)
392 {
393 if(info->tree[i])
394 freevsidtree(info->tree[i]);
395 }
396 freepage(map);
397 }
398
399 void mmufreevsid(int vsid, int mask)
400 {
401 int i;
402 for(i = 0; i < 16; i++)
403 {
404 if(mask & (1 << i))
405 freevsid((vsid << 4) + i);
406 }
407 }
408
409 int mmuaddpage(ppc_map_info_t *info, int count)
410 {
411 int i, iva = 0, vsid, phys, virt;
412 int ptehi;
413 int ptelo, vsid_table_hi, vsid_table_lo;
414 ppc_map_t *PagePtr;
415 MmuVsidInfo *VsidInfo;
416 MmuVsidTree *VsidTree;
417
418 for(i = 0; i < count; i++)
419 {
420 virt = info[i].addr;
421 vsid = ((info[i].addr >> 28) & 15) | (info[i].proc << 4);
422 VsidInfo = findvsid(vsid);
423
424 if(!VsidInfo) return -1;
425
426 ptehi = (1 << 31) | (vsid << 7) | ((virt >> 22) & 0x3f);
427
428 if(info[i].phys) {
429 PagePtr = &PpcPageTable[PPC_PAGE_NUMBER(info[i].phys)];
430 } else {
431 PagePtr = allocpage();
432 if(!PagePtr)
433 {
434 return 0;
435 }
436 }
437
438 phys = PPC_PAGE_ADDR((PagePtr - PpcPageTable));
439 ptelo = phys & ~PPC_PAGE_MASK;
440
441 /* Update page data */
442 PagePtr->pte.pteh = ptehi;
443 PagePtr->pte.ptel = ptelo;
444 PagePtr->proc = info[i].proc;
445 PagePtr->addr = virt;
446
447 vsid_table_hi = virt >> 20 & 255;
448 vsid_table_lo = virt >> 12 & 255;
449
450 if(!VsidInfo->tree[vsid_table_hi])
451 VsidInfo->tree[vsid_table_hi] = allocvsidtree();
452 VsidTree = VsidInfo->tree[vsid_table_hi];
453 if(!VsidTree) return 0;
454 VsidTree->leaves[vsid_table_lo] = PagePtr;
455
456 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (iva));
457 }
458 return 1;
459 }
460
461 ppc_pteg_t *PtegFromPage(ppc_map_t *map, int hfun)
462 {
463 if(!map->proc && !map->addr) return 0;
464 return &PpcHashedPTE[PtegNumber(map->addr, hfun)];
465 }
466
467 int PageMatch(vaddr_t addr, ppc_pte_t pte)
468 {
469 int vsid_pte = (pte.pteh >> 7) & 15, api_pte = pte.pteh & 63;
470 return
471 (((addr >> 28) & 15) == vsid_pte) &&
472 (((addr >> 22) & 63) == api_pte);
473 }
474
475 ppc_map_t *mmuvirtmap(vaddr_t addr, int vsid)
476 {
477 int seg = (addr >> 28) & 15;
478 MmuVsidInfo *seginfo = Segs[seg];
479 MmuVsidTree *segtree = 0;
480 if(!seginfo) return 0;
481 segtree = seginfo->tree[(addr >> 20) & 255];
482 if(!segtree) return 0;
483 return segtree->leaves[(addr >> 12) & 255];
484 }
485
486 void mmudelpage(ppc_map_info_t *info, int count)
487 {
488 int i, j, k, ipa;
489 ppc_map_t *PagePtr;
490 ppc_pteg_t *PageEntry;
491 ppc_pte_t ZeroPte = { 0 };
492
493 for(i = 0; i < count; i++)
494 {
495 if (info[i].phys)
496 {
497 ipa = info[i].phys;
498 PagePtr = &PpcPageTable[ipa];
499 info[i].proc = PagePtr->proc;
500 info[i].addr = PagePtr->addr;
501 }
502 else
503 {
504 PagePtr = mmuvirtmap(info[i].proc, info[i].addr);
505 ipa = PPC_PAGE_ADDR(PagePtr - PpcPageTable);
506 }
507
508 for(j = 0; j < 2; j++)
509 {
510 PageEntry = PtegFromPage(PagePtr, j);
511 for(k = 0; k < 8; k++)
512 {
513 if(PageMatch(ipa, PageEntry->block[k]))
514 {
515 if(PageEntry->block[k].ptel & 0x100)
516 info[i].flags |= MMU_PAGE_DIRTY;
517 PageEntry->block[k] = ZeroPte;
518 }
519 }
520 }
521 freepage(PagePtr);
522 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (info[i].addr));
523 }
524 }
525
526 void mmugetpage(ppc_map_info_t *info, int count)
527 {
528 int i;
529 ppc_map_t *PagePtr;
530
531 for( i = 0; i < count; i++ )
532 {
533 if(!info[i].addr && !info[i].proc)
534 {
535 PagePtr = &((ppc_map_t*)PAGETAB)[info[i].phys];
536 info[i].proc = PagePtr->proc;
537 info[i].addr = PagePtr->addr;
538 info[i].flags = MMU_ALL_RW;
539 } else {
540 vaddr_t addr = info[i].addr;
541 int vsid = ((addr >> 28) & 15) | (info[i].proc << 4);
542 PagePtr = mmuvirtmap(info[i].addr, vsid);
543 if(!PagePtr)
544 info[i].phys = 0;
545 else
546 {
547 info[i].phys = PPC_PAGE_ADDR(PagePtr - PpcPageTable);
548 info[i].flags = MMU_ALL_RW; // HACK
549 }
550 }
551 }
552 }
553
554 void mmusetvsid(int start, int end, int vsid)
555 {
556 int i, sr, s_vsid;
557 for(i = start; i < end; i++)
558 {
559 s_vsid = (vsid << 4) | (i & 15);
560 sr = (GetSR(i) & ~PPC_VSID_MASK) | s_vsid;
561 if (Booted)
562 SetSR(i, sr);
563 Segs[i] = findvsid(s_vsid);
564 Vsid[i] = vsid;
565 }
566 }
567
568 int ptegreload(ppc_trap_frame_t *frame, vaddr_t addr)
569 {
570 int hfun = (Clock >> 3) & 1, ptegnum = PtegNumber(addr, hfun);
571 int vsid = GetSR((addr >> 28) & 15) & PPC_VSID_MASK;
572 ppc_map_t *map = mmuvirtmap(addr, vsid);
573 if(!map) return 0;
574 map->pte.pteh = (map->pte.pteh & ~64) | (hfun << 6);
575 PpcHashedPTE[ptegnum].block[Clock & 7] = map->pte;
576 #if 0
577 fmtout("Reloading addr %x (phys %x) at %x[%x] (%x:%x)\r\n",
578 addr, PPC_PAGE_ADDR(map - PpcPageTable), ptegnum, Clock & 15,
579 PpcHashedPTE[ptegnum].block[Clock&7].pteh,
580 PpcHashedPTE[ptegnum].block[Clock&7].ptel);
581 #endif
582 Clock++;
583 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (addr));
584 return 1;
585 }
586
587 void callkernel(void *fun_ptr, void *arg)
588 {
589 int i;
590
591 Booted = 1;
592
593 for (i = 0; i < 16; i++)
594 {
595 // Patch up the vsid map. We shouldn't muck with these until we're
596 // booted.
597 mmusetvsid(i, i+1, Vsid[i]);
598 }
599
600 void (*fun)(void *) = fun_ptr;
601 __asm__("mfmsr 3\n\t"
602 "ori 3,3,0x30\n\t"
603 "mtmsr 3\n\t"
604 "mtsdr1 %0\n\t"
605 "mr 0,%2\n\t"
606 "mtctr 0\n\t"
607 "mr 3,%1\n\t"
608 "bctrl\n\t"
609 : : "r" (HTABORG), "r" (arg), "r" (fun));
610 /* BYE ! */
611 }