t's a bit embarrasing that some of this was undone until recently, but we now
[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, tmp;
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 /* Restore bats when we were called voluntarily. We may not get a chance
169 * to do this after returning.
170 *
171 * At this point, we're in address space that matches physical space.
172 * We turn off mapping, restore bats, then let rfi switch us back to where
173 * we came.
174 */
175 if (action >= 0x100) {
176 int i;
177
178 __asm__("mfmsr %0" : "=r" (tmp));
179 tmp &= ~0x30;
180 __asm__("mtmsr %0" : : "r" (tmp));
181
182 for(i = 0; i < 4; i++) {
183 SetBat(i, 0, GetPhys(0xf000 + i * 16), GetPhys(0xf004 + i * 16));
184 SetBat(i, 1, GetPhys(0xf008 + i * 16), GetPhys(0xf00c + i * 16));
185 }
186 }
187
188 return ret;
189 }
190
191 void outchar(char c)
192 {
193 SetPhysByte(0x800003f8, c);
194 }
195
196 void outstr(const char *str)
197 {
198 while(*str) outchar(*str);
199 }
200
201 void outdig(int dig)
202 {
203 if(dig < 10) outchar(dig + '0');
204 else outchar(dig - 10 + 'A');
205 }
206
207 void outnum(unsigned long num)
208 {
209 int i;
210 for( i = 0; i < 8; i++ )
211 {
212 outdig(num >> 28);
213 num <<= 4;
214 }
215 }
216
217 void fmtout(const char *str, ...)
218 {
219 va_list ap;
220 va_start(ap, str);
221 while(*str)
222 {
223 if(*str == '%')
224 {
225 if(str[1] == '%')
226 {
227 outchar('%');
228 }
229 else if(str[1] == 's')
230 {
231 outstr(va_arg(ap, const char *));
232 }
233 else
234 {
235 outnum(va_arg(ap, int));
236 }
237 str++;
238 }
239 else
240 {
241 outchar(*str);
242 }
243 str++;
244 }
245 va_end(ap);
246 }
247
248 void mmusetramsize(paddr_t ramsize)
249 {
250 ppc_map_t *last_map = &PpcPageTable[PPC_PAGE_NUMBER(ramsize)];
251 if(!RamSize)
252 {
253 RamSize = ramsize;
254 FirstUsablePage = (paddr_t)last_map;
255 NextPage = PPC_PAGE_NUMBER(FirstUsablePage);
256 }
257 }
258
259 int ignore(int trapCode, ppc_trap_frame_t *trap)
260 {
261 return 1;
262 }
263
264 int fpenable(int trapCode, ppc_trap_frame_t *trap)
265 {
266 /* Turn on FP */
267 trap->srr1 |= 8192;
268 return 1;
269 }
270
271 extern int trap_start[], trap_end[];
272 void copy_trap_handler(int trap)
273 {
274 int i;
275 paddr_t targetArea = trap * 0x100;
276
277 /* Set target addr */
278 trap_end[0] = (int)_mmumain;
279
280 for (i = 0; i <= trap_end - trap_start; i++)
281 {
282 SetPhys(targetArea + (i * sizeof(int)), trap_start[i]);
283 }
284 }
285
286 void initme()
287 {
288 int i;
289
290 for(i = 0; i < HTABSIZ / sizeof(int); i++)
291 {
292 ((int *)HTABORG)[i] = 0;
293 }
294
295 /* Default to hang on unknown exception */
296 for(i = 0; i < 30; i++)
297 {
298 callback[i] = (MmuTrapHandler)TakeException;
299 if (i != 1) /* Preserve reset handler */
300 copy_trap_handler(i);
301 }
302
303 /* Serial Interrupt */
304 callback[5] = SerialInterrupt;
305
306 /* Program Exception */
307 callback[6] = (MmuTrapHandler)TakeException;
308
309 /* Floating point exception */
310 callback[8] = fpenable;
311
312 /* Ignore decrementer and EE */
313 callback[9] = ignore;
314
315 /* Single Step */
316 callback[0x20] = (MmuTrapHandler)TakeException;
317 }
318
319 ppc_map_t *allocpage()
320 {
321 MmuFreePage *FreePage = 0;
322
323 while(NextPage < PPC_PAGE_NUMBER(RamSize) && ) {
324 return &PpcPageTable[NextPage++];
325 } else {
326 FreePage = FreeList;
327 FreeList = FreeList->next;
328 return ((ppc_map_t*)FreePage);
329 }
330 }
331
332 void freepage(ppc_map_t *PagePtr)
333 {
334 MmuFreePage *FreePage = (MmuFreePage*)PagePtr;
335 PagePtr->proc = PagePtr->addr = 0;
336 FreePage->next = FreeList;
337 FreeList = FreePage;
338 }
339
340 MmuVsidTree *allocvsidtree()
341 {
342 if(FreeTree)
343 {
344 MmuVsidTree *result = (MmuVsidTree*)FreeTree;
345 FreeTree = FreeTree->next;
346 return result;
347 }
348 else if(TreeAlloc >= 3 || !NextTreePage)
349 {
350 ppc_map_t *map = allocpage();
351 NextTreePage = (MmuVsidTree*)PPC_PAGE_ADDR((map - PpcPageTable));
352 TreeAlloc = 1;
353 return NextTreePage;
354 }
355 else
356 {
357 return &NextTreePage[TreeAlloc++];
358 }
359 }
360
361 void freevsidtree(MmuVsidTree *tree)
362 {
363 int i;
364 for(i = 0; i < 256; i++)
365 if(tree->leaves[i])
366 freepage(tree->leaves[i]);
367 MmuFreeTree *NextFreeTree = (MmuFreeTree *)tree;
368 NextFreeTree->next = FreeTree;
369 FreeTree = NextFreeTree;
370 }
371
372 void *allocvsid(int vsid)
373 {
374 ppc_map_t *map = allocpage();
375 MmuVsidInfo *info;
376 if(!map) return 0;
377 map->pte.pteh = map->pte.ptel = 0;
378 info = (MmuVsidInfo*)PPC_PAGE_ADDR((map - PpcPageTable));
379 info->vsid = vsid;
380 info->next = VsidHead;
381 VsidHead = info;
382 return info;
383 }
384
385 void mmuallocvsid(int vsid, int mask)
386 {
387 int i;
388 for(i = 0; i < 16; i++)
389 {
390 if(mask & (1 << i))
391 allocvsid((vsid << 4) + i);
392 }
393 }
394
395 MmuVsidInfo *findvsid(int vsid)
396 {
397 MmuVsidInfo *info;
398 for(info = VsidHead; info; info = info->next)
399 {
400 if(info->vsid == vsid) return info;
401 }
402 return 0;
403 }
404
405 void freevsid(int vsid)
406 {
407 int i;
408 MmuVsidInfo *info = findvsid(vsid);
409 if(!info) return;
410 ppc_map_t *map = &PpcPageTable[PPC_PAGE_NUMBER((paddr_t)info)];
411 for(i = 0; i < 256; i++)
412 {
413 if(info->tree[i])
414 freevsidtree(info->tree[i]);
415 }
416 freepage(map);
417 }
418
419 void mmufreevsid(int vsid, int mask)
420 {
421 int i;
422 for(i = 0; i < 16; i++)
423 {
424 if(mask & (1 << i))
425 freevsid((vsid << 4) + i);
426 }
427 }
428
429 int mmuaddpage(ppc_map_info_t *info, int count)
430 {
431 int i, iva = 0, vsid, phys, virt;
432 int ptehi;
433 int ptelo, vsid_table_hi, vsid_table_lo;
434 ppc_map_t *PagePtr;
435 MmuVsidInfo *VsidInfo;
436 MmuVsidTree *VsidTree;
437
438 for(i = 0; i < count; i++)
439 {
440 virt = info[i].addr;
441 vsid = ((info[i].addr >> 28) & 15) | (info[i].proc << 4);
442 VsidInfo = findvsid(vsid);
443
444 if(!VsidInfo) return -1;
445
446 ptehi = (1 << 31) | (vsid << 7) | ((virt >> 22) & 0x3f);
447
448 if(info[i].phys) {
449 PagePtr = &PpcPageTable[PPC_PAGE_NUMBER(info[i].phys)];
450 } else {
451 PagePtr = allocpage();
452 if(!PagePtr)
453 {
454 return 0;
455 }
456 }
457
458 phys = PPC_PAGE_ADDR((PagePtr - PpcPageTable));
459 ptelo = phys & ~PPC_PAGE_MASK;
460
461 /* Update page data */
462 PagePtr->pte.pteh = ptehi;
463 PagePtr->pte.ptel = ptelo;
464 PagePtr->proc = info[i].proc;
465 PagePtr->addr = virt;
466
467 vsid_table_hi = virt >> 20 & 255;
468 vsid_table_lo = virt >> 12 & 255;
469
470 if(!VsidInfo->tree[vsid_table_hi])
471 VsidInfo->tree[vsid_table_hi] = allocvsidtree();
472 VsidTree = VsidInfo->tree[vsid_table_hi];
473 if(!VsidTree) return 0;
474 VsidTree->leaves[vsid_table_lo] = PagePtr;
475
476 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (iva));
477 }
478 return 1;
479 }
480
481 ppc_pteg_t *PtegFromPage(ppc_map_t *map, int hfun)
482 {
483 if(!map->proc && !map->addr) return 0;
484 return &PpcHashedPTE[PtegNumber(map->addr, hfun)];
485 }
486
487 int PageMatch(vaddr_t addr, ppc_pte_t pte)
488 {
489 int vsid_pte = (pte.pteh >> 7) & 15, api_pte = pte.pteh & 63;
490 return
491 (((addr >> 28) & 15) == vsid_pte) &&
492 (((addr >> 22) & 63) == api_pte);
493 }
494
495 ppc_map_t *mmuvirtmap(vaddr_t addr)
496 {
497 int seg = (addr >> 28) & 15;
498 MmuVsidInfo *seginfo = Segs[seg];
499 MmuVsidTree *segtree = 0;
500 if(!seginfo) return 0;
501 segtree = seginfo->tree[(addr >> 20) & 255];
502 if(!segtree) return 0;
503 return segtree->leaves[(addr >> 12) & 255];
504 }
505
506 void mmudelpage(ppc_map_info_t *info, int count)
507 {
508 int i, j, k, ipa;
509 ppc_map_t *PagePtr;
510 ppc_pteg_t *PageEntry;
511 ppc_pte_t ZeroPte = { 0 };
512
513 for(i = 0; i < count; i++)
514 {
515 if (info[i].phys)
516 {
517 ipa = info[i].phys;
518 PagePtr = &PpcPageTable[ipa];
519 info[i].proc = PagePtr->proc;
520 info[i].addr = PagePtr->addr;
521 }
522 else
523 {
524 PagePtr = mmuvirtmap(info[i].addr);
525 ipa = PPC_PAGE_ADDR(PagePtr - PpcPageTable);
526 }
527
528 for(j = 0; j < 2; j++)
529 {
530 PageEntry = PtegFromPage(PagePtr, j);
531 for(k = 0; k < 8; k++)
532 {
533 if(PageMatch(ipa, PageEntry->block[k]))
534 {
535 if(PageEntry->block[k].ptel & 0x100)
536 info[i].flags |= MMU_PAGE_DIRTY;
537 PageEntry->block[k] = ZeroPte;
538 }
539 }
540 }
541 freepage(PagePtr);
542 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (info[i].addr));
543 }
544 }
545
546 void mmugetpage(ppc_map_info_t *info, int count)
547 {
548 int i;
549 ppc_map_t *PagePtr;
550
551 for( i = 0; i < count; i++ )
552 {
553 if(!info[i].addr && !info[i].proc)
554 {
555 PagePtr = &((ppc_map_t*)PAGETAB)[info[i].phys];
556 info[i].proc = PagePtr->proc;
557 info[i].addr = PagePtr->addr;
558 info[i].flags = MMU_ALL_RW;
559 } else {
560 vaddr_t addr = info[i].addr;
561 int vsid = ((addr >> 28) & 15) | (info[i].proc << 4);
562 PagePtr = mmuvirtmap(info[i].addr);
563 if(!PagePtr)
564 info[i].phys = 0;
565 else
566 {
567 info[i].phys = PPC_PAGE_ADDR(PagePtr - PpcPageTable);
568 info[i].flags = MMU_ALL_RW; // HACK
569 }
570 }
571 }
572 }
573
574 int mmupagefree(paddr_t page)
575 {
576 }
577
578 void mmusetvsid(int start, int end, int vsid)
579 {
580 int i, sr, s_vsid;
581 for(i = start; i < end; i++)
582 {
583 s_vsid = (vsid << 4) | (i & 15);
584 sr = (GetSR(i) & ~PPC_VSID_MASK) | s_vsid;
585 if (Booted)
586 SetSR(i, sr);
587 Segs[i] = findvsid(s_vsid);
588 Vsid[i] = vsid;
589 }
590 }
591
592 int ptegreload(ppc_trap_frame_t *frame, vaddr_t addr)
593 {
594 int hfun = (Clock >> 3) & 1, ptegnum = PtegNumber(addr, hfun);
595 ppc_map_t *map = mmuvirtmap(addr);
596 if(!map) return 0;
597 map->pte.pteh = (map->pte.pteh & ~64) | (hfun << 6);
598 PpcHashedPTE[ptegnum].block[Clock & 7] = map->pte;
599 #if 0
600 fmtout("Reloading addr %x (phys %x) at %x[%x] (%x:%x)\r\n",
601 addr, PPC_PAGE_ADDR(map - PpcPageTable), ptegnum, Clock & 15,
602 PpcHashedPTE[ptegnum].block[Clock&7].pteh,
603 PpcHashedPTE[ptegnum].block[Clock&7].ptel);
604 #endif
605 Clock++;
606 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (addr));
607 return 1;
608 }
609
610 void callkernel(void *fun_ptr, void *arg)
611 {
612 int i;
613
614 Booted = 1;
615
616 for (i = 0; i < 16; i++)
617 {
618 // Patch up the vsid map. We shouldn't muck with these until we're
619 // booted.
620 mmusetvsid(i, i+1, Vsid[i]);
621 }
622
623 void (*fun)(void *) = fun_ptr;
624 __asm__("mfmsr 3\n\t"
625 "ori 3,3,0x30\n\t"
626 "mtmsr 3\n\t"
627 "mtsdr1 %0\n\t"
628 "mr 0,%2\n\t"
629 "mtctr 0\n\t"
630 "mr 3,%1\n\t"
631 "bctrl\n\t"
632 : : "r" (HTABORG), "r" (arg), "r" (fun));
633 /* BYE ! */
634 }