Remaining fixes from branch transition. MmuTurnOn, now with GDB stub.
[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;
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)) hang(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 hang(int trapCode, ppc_trap_frame_t *trap)
245 {
246 if (!GdbAttach)
247 {
248 GdbAttach = 1;
249 SerialSetUp(0, (void *)0x800003f8, 9600);
250 }
251 while(1) SerialInterrupt(trapCode, trap);
252 return 1;
253 }
254
255 int fpenable(int trapCode, ppc_trap_frame_t *trap)
256 {
257 /* Turn on FP */
258 trap->srr0 |= 8192;
259 return 1;
260 }
261
262 extern int trap_start[], trap_end[];
263 void copy_trap_handler(int trap)
264 {
265 int i;
266 paddr_t targetArea = trap * 0x100;
267
268 /* Set target addr */
269 trap_end[0] = (int)_mmumain;
270
271 for (i = 0; i <= trap_end - trap_start; i++)
272 {
273 SetPhys(targetArea + (i * sizeof(int)), trap_start[i]);
274 }
275 }
276
277 void initme()
278 {
279 int i;
280
281 for(i = 0; i < HTABSIZ / sizeof(int); i++)
282 {
283 ((int *)HTABORG)[i] = 0;
284 }
285
286 /* Default to hang on unknown exception */
287 for(i = 0; i < 30; i++)
288 {
289 callback[i] = hang;
290 if (i != 1) /* Preserve reset handler */
291 copy_trap_handler(i);
292 }
293
294 /* Serial Interrupt */
295 callback[5] = SerialInterrupt;
296
297 /* Program Exception */
298 callback[6] = TakeException;
299
300 /* Floating point exception */
301 callback[8] = fpenable;
302
303 /* Ignore decrementer and EE */
304 callback[9] = ignore;
305
306 /* Single Step */
307 callback[0x20] = TakeException;
308 }
309
310 ppc_map_t *allocpage()
311 {
312 MmuFreePage *FreePage = 0;
313
314 if(NextPage < PPC_PAGE_NUMBER(RamSize)) {
315 return &PpcPageTable[NextPage++];
316 } else {
317 FreePage = FreeList;
318 FreeList = FreeList->next;
319 return ((ppc_map_t*)FreePage);
320 }
321 }
322
323 void freepage(ppc_map_t *PagePtr)
324 {
325 MmuFreePage *FreePage = (MmuFreePage*)PagePtr;
326 PagePtr->proc = PagePtr->addr = 0;
327 FreePage->next = FreeList;
328 FreeList = FreePage;
329 }
330
331 MmuVsidTree *allocvsidtree()
332 {
333 if(FreeTree)
334 {
335 MmuVsidTree *result = (MmuVsidTree*)FreeTree;
336 FreeTree = FreeTree->next;
337 return result;
338 }
339 else if(TreeAlloc >= 3 || !NextTreePage)
340 {
341 ppc_map_t *map = allocpage();
342 NextTreePage = (MmuVsidTree*)PPC_PAGE_ADDR((map - PpcPageTable));
343 TreeAlloc = 1;
344 return NextTreePage;
345 }
346 else
347 {
348 return &NextTreePage[TreeAlloc++];
349 }
350 }
351
352 void freevsidtree(MmuVsidTree *tree)
353 {
354 int i;
355 for(i = 0; i < 256; i++)
356 if(tree->leaves[i])
357 freepage(tree->leaves[i]);
358 MmuFreeTree *NextFreeTree = (MmuFreeTree *)tree;
359 NextFreeTree->next = FreeTree;
360 FreeTree = NextFreeTree;
361 }
362
363 void *allocvsid(int vsid)
364 {
365 ppc_map_t *map = allocpage();
366 MmuVsidInfo *info;
367 if(!map) return 0;
368 map->pte.pteh = map->pte.ptel = 0;
369 info = (MmuVsidInfo*)PPC_PAGE_ADDR((map - PpcPageTable));
370 info->vsid = vsid;
371 info->next = VsidHead;
372 VsidHead = info;
373 return info;
374 }
375
376 void mmuallocvsid(int vsid, int mask)
377 {
378 int i;
379 for(i = 0; i < 16; i++)
380 {
381 if(mask & (1 << i))
382 allocvsid((vsid << 4) + i);
383 }
384 }
385
386 MmuVsidInfo *findvsid(int vsid)
387 {
388 MmuVsidInfo *info;
389 for(info = VsidHead; info; info = info->next)
390 {
391 if(info->vsid == vsid) return info;
392 }
393 return 0;
394 }
395
396 void freevsid(int vsid)
397 {
398 int i;
399 MmuVsidInfo *info = findvsid(vsid);
400 if(!info) return;
401 ppc_map_t *map = &PpcPageTable[PPC_PAGE_NUMBER((paddr_t)info)];
402 for(i = 0; i < 256; i++)
403 {
404 if(info->tree[i])
405 freevsidtree(info->tree[i]);
406 }
407 freepage(map);
408 }
409
410 void mmufreevsid(int vsid, int mask)
411 {
412 int i;
413 for(i = 0; i < 16; i++)
414 {
415 if(mask & (1 << i))
416 freevsid((vsid << 4) + i);
417 }
418 }
419
420 int mmuaddpage(ppc_map_info_t *info, int count)
421 {
422 int i, iva = 0, vsid, phys, virt;
423 int ptehi;
424 int ptelo, vsid_table_hi, vsid_table_lo;
425 ppc_map_t *PagePtr;
426 MmuVsidInfo *VsidInfo;
427 MmuVsidTree *VsidTree;
428
429 for(i = 0; i < count; i++)
430 {
431 virt = info[i].addr;
432 vsid = ((info[i].addr >> 28) & 15) | (info[i].proc << 4);
433 VsidInfo = findvsid(vsid);
434
435 if(!VsidInfo) return -1;
436
437 ptehi = (1 << 31) | (vsid << 7) | ((virt >> 22) & 0x3f);
438
439 if(info[i].phys) {
440 PagePtr = &PpcPageTable[PPC_PAGE_NUMBER(info[i].phys)];
441 } else {
442 PagePtr = allocpage();
443 if(!PagePtr)
444 {
445 return 0;
446 }
447 }
448
449 phys = PPC_PAGE_ADDR((PagePtr - PpcPageTable));
450 ptelo = phys & ~PPC_PAGE_MASK;
451
452 /* Update page data */
453 PagePtr->pte.pteh = ptehi;
454 PagePtr->pte.ptel = ptelo;
455 PagePtr->proc = info[i].proc;
456 PagePtr->addr = virt;
457
458 vsid_table_hi = virt >> 20 & 255;
459 vsid_table_lo = virt >> 12 & 255;
460
461 if(!VsidInfo->tree[vsid_table_hi])
462 VsidInfo->tree[vsid_table_hi] = allocvsidtree();
463 VsidTree = VsidInfo->tree[vsid_table_hi];
464 if(!VsidTree) return 0;
465 VsidTree->leaves[vsid_table_lo] = PagePtr;
466
467 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (iva));
468 }
469 return 1;
470 }
471
472 ppc_pteg_t *PtegFromPage(ppc_map_t *map, int hfun)
473 {
474 if(!map->proc && !map->addr) return 0;
475 return &PpcHashedPTE[PtegNumber(map->addr, hfun)];
476 }
477
478 int PageMatch(vaddr_t addr, ppc_pte_t pte)
479 {
480 int vsid_pte = (pte.pteh >> 7) & 15, api_pte = pte.pteh & 63;
481 return
482 (((addr >> 28) & 15) == vsid_pte) &&
483 (((addr >> 22) & 63) == api_pte);
484 }
485
486 ppc_map_t *mmuvirtmap(vaddr_t addr, int vsid)
487 {
488 int seg = (addr >> 28) & 15;
489 MmuVsidInfo *seginfo = Segs[seg];
490 MmuVsidTree *segtree = 0;
491 if(!seginfo) return 0;
492 segtree = seginfo->tree[(addr >> 20) & 255];
493 if(!segtree) return 0;
494 return segtree->leaves[(addr >> 12) & 255];
495 }
496
497 void mmudelpage(ppc_map_info_t *info, int count)
498 {
499 int i, j, k, ipa;
500 ppc_map_t *PagePtr;
501 ppc_pteg_t *PageEntry;
502 ppc_pte_t ZeroPte = { 0 };
503
504 for(i = 0; i < count; i++)
505 {
506 if (info[i].phys)
507 {
508 ipa = info[i].phys;
509 PagePtr = &PpcPageTable[ipa];
510 info[i].proc = PagePtr->proc;
511 info[i].addr = PagePtr->addr;
512 }
513 else
514 {
515 PagePtr = mmuvirtmap(info[i].proc, info[i].addr);
516 ipa = PPC_PAGE_ADDR(PagePtr - PpcPageTable);
517 }
518
519 for(j = 0; j < 2; j++)
520 {
521 PageEntry = PtegFromPage(PagePtr, j);
522 for(k = 0; k < 8; k++)
523 {
524 if(PageMatch(ipa, PageEntry->block[k]))
525 {
526 if(PageEntry->block[k].ptel & 0x100)
527 info[i].flags |= MMU_PAGE_DIRTY;
528 PageEntry->block[k] = ZeroPte;
529 }
530 }
531 }
532 freepage(PagePtr);
533 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (info[i].addr));
534 }
535 }
536
537 void mmugetpage(ppc_map_info_t *info, int count)
538 {
539 int i;
540 ppc_map_t *PagePtr;
541
542 for( i = 0; i < count; i++ )
543 {
544 if(!info[i].addr && !info[i].proc)
545 {
546 PagePtr = &((ppc_map_t*)PAGETAB)[info[i].phys];
547 info[i].proc = PagePtr->proc;
548 info[i].addr = PagePtr->addr;
549 info[i].flags = MMU_ALL_RW;
550 } else {
551 vaddr_t addr = info[i].addr;
552 int vsid = ((addr >> 28) & 15) | (info[i].proc << 4);
553 PagePtr = mmuvirtmap(info[i].addr, vsid);
554 if(!PagePtr)
555 info[i].phys = 0;
556 else
557 {
558 info[i].phys = PPC_PAGE_ADDR(PagePtr - PpcPageTable);
559 info[i].flags = MMU_ALL_RW; // HACK
560 }
561 }
562 }
563 }
564
565 void mmusetvsid(int start, int end, int vsid)
566 {
567 int i, sr, s_vsid;
568 for(i = start; i < end; i++)
569 {
570 s_vsid = (vsid << 4) | (i & 15);
571 sr = (GetSR(i) & ~PPC_VSID_MASK) | s_vsid;
572 SetSR(i, sr);
573 Segs[i] = findvsid(s_vsid);
574 }
575 }
576
577 int ptegreload(ppc_trap_frame_t *frame, vaddr_t addr)
578 {
579 int hfun = (Clock >> 3) & 1, ptegnum = PtegNumber(addr, hfun);
580 int vsid = GetSR((addr >> 28) & 15) & PPC_VSID_MASK;
581 ppc_map_t *map = mmuvirtmap(addr, vsid);
582 if(!map) return 0;
583 map->pte.pteh = (map->pte.pteh & ~64) | (hfun << 6);
584 PpcHashedPTE[ptegnum].block[Clock & 7] = map->pte;
585 #if 0
586 fmtout("Reloading addr %x (phys %x) at %x[%x] (%x:%x)\r\n",
587 addr, PPC_PAGE_ADDR(map - PpcPageTable), ptegnum, Clock & 15,
588 PpcHashedPTE[ptegnum].block[Clock&7].pteh,
589 PpcHashedPTE[ptegnum].block[Clock&7].ptel);
590 #endif
591 Clock++;
592 __asm__("tlbie %0\n\tsync\n\tisync" : : "r" (addr));
593 return 1;
594 }
595
596 void callkernel(void *fun_ptr, void *arg)
597 {
598 void (*fun)(void *) = fun_ptr;
599 __asm__("mfmsr 3\n\t"
600 "ori 3,3,0x30\n\t"
601 "mtmsr 3\n\t"
602 "mtsdr1 %0\n\t"
603 "mr 0,%2\n\t"
604 "mtctr 0\n\t"
605 "mr 3,%1\n\t"
606 "bctrl\n\t"
607 : : "r" (HTABORG), "r" (arg), "r" (fun));
608 /* BYE ! */
609 }