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