Kernel/armv7 - Debugging, fixed TLB issues with a HACK!
[tpg/acess2.git] / Kernel / arch / armv7 / mm_virt.c
1 /*
2  * Acess2
3  * 
4  * ARM7 Virtual Memory Manager
5  * - arch/arm7/mm_virt.c
6  */
7 #define DEBUG   0
8 #include <acess.h>
9 #include <mm_virt.h>
10 #include <hal_proc.h>
11
12 #define TRACE_MAPS      0
13
14 #define AP_KRW_ONLY     1       // Kernel page
15 #define AP_KRO_ONLY     5       // Kernel RO page
16 #define AP_RW_BOTH      3       // Standard RW
17 #define AP_RO_BOTH      7       // COW Page
18 #define AP_RO_USER      2       // User RO Page
19 #define PADDR_MASK_LVL1 0xFFFFFC00
20
21 // === IMPORTS ===
22 extern Uint32   kernel_table0[];
23
24 // === TYPES ===
25 typedef struct
26 {
27         tPAddr  PhysAddr;
28         Uint8   Size;
29         Uint8   Domain;
30         BOOL    bExecutable;
31         BOOL    bGlobal;
32         BOOL    bShared;
33          int    AP;
34 } tMM_PageInfo;
35
36 //#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>20)])
37 #define FRACTAL(table1, addr)   ((table1)[ (0xFF8/4*1024) + ((addr)>>22)])
38 #define USRFRACTAL(addr)        (*((Uint32*)(0x7FDFF000) + ((addr)>>22)))
39 #define TLBIALL()       __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0))
40 #define TLBIMVA(addr)   __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 1;dsb;isb" : : "r" ((addr)&~0xFFF):"memory")
41 #define DCCMVAC(addr)   __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 1" : : "r" ((addr)&~0xFFF))
42
43 // === PROTOTYPES ===
44 void    MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1);
45  int    MM_int_AllocateCoarse(tVAddr VAddr, int Domain);
46  int    MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi);
47  int    MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi);
48 tVAddr  MM_NewUserStack(void);
49 tPAddr  MM_AllocateZero(tVAddr VAddr);
50 tPAddr  MM_AllocateRootTable(void);
51 void    MM_int_CloneTable(Uint32 *DestEnt, int Table);
52 tPAddr  MM_Clone(void);
53 tVAddr  MM_NewKStack(int bGlobal);
54 void    MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info);
55 //void  MM_DumpTables(tVAddr Start, tVAddr End);
56 void    MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch);
57
58 // === GLOBALS ===
59 tPAddr  giMM_ZeroPage;
60
61 // === CODE ===
62 int MM_InitialiseVirtual(void)
63 {
64         return 0;
65 }
66
67 void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1)
68 {
69         if(VAddr & 0x80000000) {
70                 *Table0 = (void*)&kernel_table0;        // Level 0
71                 *Table1 = (void*)MM_TABLE1KERN; // Level 1
72         }
73         else {
74                 *Table0 = (void*)MM_TABLE0USER;
75                 *Table1 = (void*)MM_TABLE1USER;
76         }
77 }
78
79 int MM_int_AllocateCoarse(tVAddr VAddr, int Domain)
80 {
81         Uint32  *table0, *table1;
82         Uint32  *desc;
83         tPAddr  paddr;
84         
85         ENTER("xVAddr iDomain", VAddr, Domain);
86
87         MM_int_GetTables(VAddr, &table0, &table1);
88
89         VAddr &= ~(0x400000-1); // 4MiB per "block", 1 Page
90
91         desc = &table0[ VAddr>>20];
92         LOG("desc = %p", desc);
93         
94         // table0: 4 bytes = 1 MiB
95
96         LOG("desc[0] = %x", desc[0]);
97         LOG("desc[1] = %x", desc[1]);
98         LOG("desc[2] = %x", desc[2]);
99         LOG("desc[3] = %x", desc[3]);
100
101         if( (desc[0] & 3) != 0 || (desc[1] & 3) != 0
102          || (desc[2] & 3) != 0 || (desc[3] & 3) != 0 )
103         {
104                 // Error?
105                 LEAVE('i', 1);
106                 return 1;
107         }
108
109         paddr = MM_AllocPhys();
110         if( !paddr )
111         {
112                 // Error
113                 LEAVE('i', 2);
114                 return 2;
115         }
116         
117         *desc = paddr | (Domain << 5) | 1;
118         desc[1] = desc[0] + 0x400;
119         desc[2] = desc[0] + 0x800;
120         desc[3] = desc[0] + 0xC00;
121
122         if( VAddr < 0x80000000 ) {
123                 USRFRACTAL(VAddr) = paddr | 0x13;
124         }
125         else {
126                 FRACTAL(table1, VAddr) = paddr | 0x13;
127         }
128
129         // TLBIALL 
130         TLBIALL();
131         
132         memset( (void*)&table1[ (VAddr >> 12) & ~(1024-1) ], 0, 0x1000 );
133
134         LEAVE('i', 0);
135         return 0;
136 }       
137
138 int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
139 {
140         Uint32  *table0, *table1;
141         Uint32  *desc;
142
143         ENTER("pVAddr ppi", VAddr, pi);
144
145         MM_int_GetTables(VAddr, &table0, &table1);
146
147         desc = &table0[ VAddr >> 20 ];
148         LOG("desc = %p", desc);
149
150         switch(pi->Size)
151         {
152         case 12:        // Small Page
153         case 16:        // Large Page
154                 LOG("Page");
155                 if( (*desc & 3) == 0 ) {
156                         MM_int_AllocateCoarse( VAddr, pi->Domain );
157                 }
158                 desc = &table1[ VAddr >> 12 ];
159                 LOG("desc (2) = %p", desc);
160                 if( pi->Size == 12 )
161                 {
162                         // Small page
163                         // - Error if overwriting a large page
164                         if( (*desc & 3) == 1 )  LEAVE_RET('i', 1);
165                         if( pi->PhysAddr == 0 ) {
166                                 *desc = 0;
167                                 TLBIMVA( VAddr );
168                                 DCCMVAC( (tVAddr) desc );
169                                 TLBIALL();
170                                 LEAVE('i', 0);
171                                 return 0;
172                         }
173
174                         *desc = (pi->PhysAddr & 0xFFFFF000) | 2;
175                         if(!pi->bExecutable)    *desc |= 1;     // XN
176                         if(!pi->bGlobal)        *desc |= 1 << 11;       // nG
177                         if( pi->bShared)        *desc |= 1 << 10;       // S
178                         *desc |= (pi->AP & 3) << 4;     // AP
179                         *desc |= ((pi->AP >> 2) & 1) << 9;      // APX
180                         TLBIMVA( VAddr );       
181                         TLBIALL();
182                         DCCMVAC( (tVAddr) desc );
183                         LEAVE('i', 0);
184                         return 0;
185                 }
186                 else
187                 {
188                         // Large page
189                         Log_Warning("MMVirt", "TODO: Implement large pages in MM_int_SetPageInfo");
190                 }
191                 break;
192         case 20:        // Section or unmapped
193                 Log_Warning("MMVirt", "TODO: Implement sections in MM_int_SetPageInfo");
194                 break;
195         case 24:        // Supersection
196                 // Error if not aligned
197                 if( VAddr & 0xFFFFFF ) {
198                         LEAVE('i', 1);
199                         return 1;
200                 }
201                 if( (*desc & 3) == 0 || ((*desc & 3) == 2 && (*desc & (1 << 18)))  )
202                 {
203                         if( pi->PhysAddr == 0 ) {
204                                 *desc = 0;
205                         }
206                         else {
207                                 // Apply
208                                 *desc = pi->PhysAddr & 0xFF000000;
209 //                              *desc |= ((pi->PhysAddr >> 32) & 0xF) << 20;
210 //                              *desc |= ((pi->PhysAddr >> 36) & 0x7) << 5;
211                                 *desc |= 2 | (1 << 18);
212                         }
213                         // TODO: Apply to all entries
214                         Log_Warning("MMVirt", "TODO: Apply changes to all entries of supersections");
215                         LEAVE('i', 0);
216                         return 0;
217                 }
218                 // TODO: What here?
219                 Log_Warning("MMVirt", "TODO: 24-bit not on supersection?");
220                 LEAVE('i', 1);
221                 return 1;
222         }
223
224         LEAVE('i', 1);
225         return 1;
226 }
227
228 int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
229 {
230         Uint32  *table0, *table1;
231         Uint32  desc;
232
233 //      LogF("MM_int_GetPageInfo: VAddr=%p, pi=%p\n", VAddr, pi);
234         
235         MM_int_GetTables(VAddr, &table0, &table1);
236
237         desc = table0[ VAddr >> 20 ];
238
239 //      if( VAddr > 0x90000000)
240 //              LOG("table0 desc(%p) = %x", &table0[ VAddr >> 20 ], desc);
241         
242         pi->bExecutable = 1;
243         pi->bGlobal = 0;
244         pi->bShared = 0;
245         pi->AP = 0;
246
247         switch( (desc & 3) )
248         {
249         // 0: Unmapped
250         case 0:
251                 pi->PhysAddr = 0;
252                 pi->Size = 20;
253                 pi->Domain = 0;
254                 return 1;
255
256         // 1: Coarse page table
257         case 1:
258                 // Domain from top level table
259                 pi->Domain = (desc >> 5) & 7;
260                 // Get next level
261                 desc = table1[ VAddr >> 12 ];
262 //              LOG("table1 desc(%p) = %x", &table1[ VAddr >> 12 ], desc);
263                 switch( desc & 3 )
264                 {
265                 // 0: Unmapped
266                 case 0: 
267                         pi->Size = 12;
268                         return 1;
269                 // 1: Large Page (64KiB)
270                 case 1:
271                         pi->Size = 16;
272                         pi->PhysAddr = desc & 0xFFFF0000;
273                         pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2);
274                         pi->bExecutable = !(desc & 0x8000);
275                         pi->bShared = (desc >> 10) & 1;
276                         return 0;
277                 // 2/3: Small page
278                 case 2:
279                 case 3:
280                         pi->Size = 12;
281                         pi->PhysAddr = desc & 0xFFFFF000;
282                         pi->bExecutable = !(desc & 1);
283                         pi->bGlobal = !(desc >> 11);
284                         pi->bShared = (desc >> 10) & 1;
285                         pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2);
286                         return 0;
287                 }
288                 return 1;
289         
290         // 2: Section (or Supersection)
291         case 2:
292                 if( desc & (1 << 18) ) {
293                         // Supersection
294                         pi->PhysAddr = desc & 0xFF000000;
295                         pi->PhysAddr |= (Uint64)((desc >> 20) & 0xF) << 32;
296                         pi->PhysAddr |= (Uint64)((desc >> 5) & 0x7) << 36;
297                         pi->Size = 24;
298                         pi->Domain = 0; // Supersections default to zero
299                         pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
300                         return 0;
301                 }
302                 
303                 // Section
304                 pi->PhysAddr = desc & 0xFFF80000;
305                 pi->Size = 20;
306                 pi->Domain = (desc >> 5) & 7;
307                 pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
308                 return 0;
309
310         // 3: Reserved (invalid)
311         case 3:
312                 pi->PhysAddr = 0;
313                 pi->Size = 20;
314                 pi->Domain = 0;
315                 return 2;
316         }
317         return 2;
318 }
319
320 // --- Exports ---
321 tPAddr MM_GetPhysAddr(tVAddr VAddr)
322 {
323         tMM_PageInfo    pi;
324         if( MM_int_GetPageInfo(VAddr, &pi) )
325                 return 0;
326         return pi.PhysAddr | (VAddr & ((1 << pi.Size)-1));
327 }
328
329 Uint MM_GetFlags(tVAddr VAddr)
330 {
331         tMM_PageInfo    pi;
332          int    ret;
333
334         if( MM_int_GetPageInfo(VAddr, &pi) )
335                 return 0;
336
337         ret = 0;
338         
339         switch(pi.AP)
340         {
341         case 0:
342                 break;
343         case AP_KRW_ONLY:
344                 ret |= MM_PFLAG_KERNEL;
345                 break;
346         case AP_KRO_ONLY:
347                 ret |= MM_PFLAG_KERNEL|MM_PFLAG_RO;
348                 break;
349         case AP_RW_BOTH:
350                 break;
351         case AP_RO_BOTH:
352                 ret |= MM_PFLAG_COW;
353                 break;
354         case AP_RO_USER:
355                 ret |= MM_PFLAG_RO;
356                 break;
357         }
358
359         if( pi.bExecutable )    ret |= MM_PFLAG_EXEC;
360         return ret;
361 }
362
363 void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
364 {
365         tMM_PageInfo    pi;
366         Uint    curFlags;
367         
368         if( MM_int_GetPageInfo(VAddr, &pi) )
369                 return ;
370         
371         curFlags = MM_GetFlags(VAddr);
372         if( (curFlags & Mask) == Flags )
373                 return ;
374         curFlags &= ~Mask;
375         curFlags |= Flags;
376
377         if( curFlags & MM_PFLAG_COW )
378                 pi.AP = AP_RO_BOTH;
379         else
380         {
381                 switch(curFlags & (MM_PFLAG_KERNEL|MM_PFLAG_RO) )
382                 {
383                 case 0:
384                         pi.AP = AP_RW_BOTH;     break;
385                 case MM_PFLAG_KERNEL:
386                         pi.AP = AP_KRW_ONLY;    break;
387                 case MM_PFLAG_RO:
388                         pi.AP = AP_RO_USER;     break;
389                 case MM_PFLAG_KERNEL|MM_PFLAG_RO:
390                         pi.AP = AP_KRO_ONLY;    break;
391                 }
392         }
393         
394         pi.bExecutable = !!(curFlags & MM_PFLAG_EXEC);
395
396         MM_int_SetPageInfo(VAddr, &pi);
397 }
398
399 int MM_IsValidBuffer(tVAddr Addr, size_t Size)
400 {
401         tMM_PageInfo    pi;
402          int    bUser = 0;
403         
404         Size += Addr & (PAGE_SIZE-1);
405         Addr &= ~(PAGE_SIZE-1);
406
407         if( MM_int_GetPageInfo(Addr, &pi) )     return 0;
408         Addr += PAGE_SIZE;
409
410         if(pi.AP != AP_KRW_ONLY && pi.AP != AP_KRO_ONLY)
411                 bUser = 1;
412
413         while( Size >= PAGE_SIZE )
414         {
415                 if( MM_int_GetPageInfo(Addr, &pi) )
416                         return 0;
417                 if(bUser && (pi.AP == AP_KRW_ONLY || pi.AP == AP_KRO_ONLY))
418                         return 0;
419                 Addr += PAGE_SIZE;
420                 Size -= PAGE_SIZE;
421         }
422         
423         return 1;
424 }
425
426 int MM_Map(tVAddr VAddr, tPAddr PAddr)
427 {
428         tMM_PageInfo    pi = {0};
429         #if TRACE_MAPS
430         Log("MM_Map %P=>%p", PAddr, VAddr);
431         #endif
432         
433         pi.PhysAddr = PAddr;
434         pi.Size = 12;
435         if(VAddr < USER_STACK_TOP)
436                 pi.AP = AP_RW_BOTH;
437         else
438                 pi.AP = AP_KRW_ONLY;    // Kernel Read/Write
439         pi.bExecutable = 1;
440         if( MM_int_SetPageInfo(VAddr, &pi) ) {
441 //              MM_DerefPhys(pi.PhysAddr);
442                 return 0;
443         }
444         return pi.PhysAddr;
445 }
446
447 tPAddr MM_Allocate(tVAddr VAddr)
448 {
449         tMM_PageInfo    pi = {0};
450         
451         ENTER("pVAddr", VAddr);
452
453         pi.PhysAddr = MM_AllocPhys();
454         if( pi.PhysAddr == 0 )  LEAVE_RET('i', 0);
455         pi.Size = 12;
456         if(VAddr < USER_STACK_TOP)
457                 pi.AP = AP_RW_BOTH;
458         else
459                 pi.AP = AP_KRW_ONLY;
460         pi.bExecutable = 0;
461         if( MM_int_SetPageInfo(VAddr, &pi) ) {
462                 MM_DerefPhys(pi.PhysAddr);
463                 LEAVE('i', 0);
464                 return 0;
465         }
466         LEAVE('x', pi.PhysAddr);
467         return pi.PhysAddr;
468 }
469
470 tPAddr MM_AllocateZero(tVAddr VAddr)
471 {
472         if( !giMM_ZeroPage ) {
473                 giMM_ZeroPage = MM_Allocate(VAddr);
474                 MM_RefPhys(giMM_ZeroPage);
475                 memset((void*)VAddr, 0, PAGE_SIZE);
476         }
477         else {
478                 MM_RefPhys(giMM_ZeroPage);
479                 MM_Map(VAddr, giMM_ZeroPage);
480         }
481         MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW);
482         return giMM_ZeroPage;
483 }
484
485 void MM_Deallocate(tVAddr VAddr)
486 {
487         tMM_PageInfo    pi;
488         
489         if( MM_int_GetPageInfo(VAddr, &pi) )    return ;
490         if( pi.PhysAddr == 0 )  return;
491         MM_DerefPhys(pi.PhysAddr);
492         
493         pi.PhysAddr = 0;
494         pi.AP = 0;
495         pi.bExecutable = 0;
496         MM_int_SetPageInfo(VAddr, &pi);
497 }
498
499 tPAddr MM_AllocateRootTable(void)
500 {
501         tPAddr  ret;
502         
503         ret = MM_AllocPhysRange(2, -1);
504         if( ret & 0x1000 ) {
505                 MM_DerefPhys(ret);
506                 MM_DerefPhys(ret+0x1000);
507                 ret = MM_AllocPhysRange(3, -1);
508                 if( ret & 0x1000 ) {
509                         MM_DerefPhys(ret);
510                         ret += 0x1000;
511 //                      Log("MM_AllocateRootTable: Second try not aligned, %P", ret);
512                 }
513                 else {
514                         MM_DerefPhys(ret + 0x2000);
515 //                      Log("MM_AllocateRootTable: Second try aligned, %P", ret);
516                 }
517         }
518 //      else
519 //              Log("MM_AllocateRootTable: Got it in one, %P", ret);
520         return ret;
521 }
522
523 void MM_int_CloneTable(Uint32 *DestEnt, int Table)
524 {
525         tPAddr  table;
526         Uint32  *tmp_map;
527         Uint32  *cur = (void*)MM_TABLE1USER;
528 //      Uint32  *cur = &FRACTAL(MM_TABLE1USER,0);
529          int    i;
530         
531         table = MM_AllocPhys();
532         if(!table)      return ;
533
534         cur += 256*Table;
535         
536         tmp_map = (void*)MM_MapTemp(table);
537         
538         for( i = 0; i < 1024; i ++ )
539         {
540 //              Log_Debug("MMVirt", "cur[%i] (%p) = %x", Table*256+i, &cur[Table*256+i], cur[Table*256+i]);
541                 switch(cur[i] & 3)
542                 {
543                 case 0: tmp_map[i] = 0; break;
544                 case 1:
545                         tmp_map[i] = 0;
546                         Log_Error("MMVirt", "TODO: Support large pages in MM_int_CloneTable (%p)", (Table*256+i)*0x1000);
547                         // Large page?
548                         break;
549                 case 2:
550                 case 3:
551                         // Small page
552                         // - If full RW
553 //                      Debug("%p cur[%i] & 0x230 = 0x%x", Table*256*0x1000, i, cur[i] & 0x230);
554                         if( (cur[i] & 0x230) == 0x010 )
555                         {
556                                 void    *dst, *src;
557                                 tPAddr  newpage;
558                                 newpage = MM_AllocPhys();
559                                 src = (void*)( (Table*256+i)*0x1000 );
560                                 dst = (void*)MM_MapTemp(newpage);
561 //                              Debug("Taking a copy of kernel page %p (%P)", src, cur[i] & ~0xFFF);
562                                 memcpy(dst, src, PAGE_SIZE);
563                                 MM_FreeTemp( (tVAddr)dst );
564                                 tmp_map[i] = newpage | (cur[i] & 0xFFF);
565                         }
566                         else
567                         {
568                                 if( (cur[i] & 0x230) == 0x030 )
569                                         cur[i] |= 0x200;        // Set to full RO (Full RO=COW, User RO = RO)
570                                 tmp_map[i] = cur[i];
571                                 MM_RefPhys( tmp_map[i] & ~0xFFF );
572                         }
573                         break;
574                 }
575         }
576         MM_FreeTemp( (tVAddr) tmp_map );
577
578         DestEnt[0] = table + 0*0x400 + 1;
579         DestEnt[1] = table + 1*0x400 + 1;
580         DestEnt[2] = table + 2*0x400 + 1;
581         DestEnt[3] = table + 3*0x400 + 1;
582 }
583
584 tPAddr MM_Clone(void)
585 {
586         tPAddr  ret;
587         Uint32  *new_lvl1_1, *new_lvl1_2, *cur;
588         Uint32  *tmp_map;
589          int    i;
590
591 //      MM_DumpTables(0, KERNEL_BASE);
592         
593         ret = MM_AllocateRootTable();
594
595         cur = (void*)MM_TABLE0USER;
596         new_lvl1_1 = (void*)MM_MapTemp(ret);
597         new_lvl1_2 = (void*)MM_MapTemp(ret+0x1000);
598         tmp_map = new_lvl1_1;
599         for( i = 0; i < 0x800-4; i ++ )
600         {
601                 // HACK! Ignore the original identity mapping
602                 if( i == 0 && Threads_GetTID() == 0 ) {
603                         tmp_map[0] = 0;
604                         continue;
605                 }
606                 if( i == 0x400 )
607                         tmp_map = &new_lvl1_2[-0x400];
608                 switch( cur[i] & 3 )
609                 {
610                 case 0: tmp_map[i] = 0; break;
611                 case 1:
612                         MM_int_CloneTable(&tmp_map[i], i);
613                         i += 3; // Tables are alocated in blocks of 4
614                         break;
615                 case 2:
616                 case 3:
617                         Log_Error("MMVirt", "TODO: Support Sections/Supersections in MM_Clone (i=%i)", i);
618                         tmp_map[i] = 0;
619                         break;
620                 }
621         }
622
623         // Allocate Fractal table
624         {
625                  int    j, num;
626                 tPAddr  tmp = MM_AllocPhys();
627                 Uint32  *table = (void*)MM_MapTemp(tmp);
628                 Uint32  sp;
629                 register Uint32 __SP asm("sp");
630
631                 // Map table to last 4MiB of user space
632                 new_lvl1_2[0x3FC] = tmp + 0*0x400 + 1;
633                 new_lvl1_2[0x3FD] = tmp + 1*0x400 + 1;
634                 new_lvl1_2[0x3FE] = tmp + 2*0x400 + 1;
635                 new_lvl1_2[0x3FF] = tmp + 3*0x400 + 1;
636                 
637                 tmp_map = new_lvl1_1;
638                 for( j = 0; j < 512; j ++ )
639                 {
640                         if( j == 256 )
641                                 tmp_map = &new_lvl1_2[-0x400];
642                         if( (tmp_map[j*4] & 3) == 1 )
643                         {
644                                 table[j] = tmp_map[j*4] & PADDR_MASK_LVL1;// 0xFFFFFC00;
645                                 table[j] |= 0x813;      // nG, Kernel Only, Small page, XN
646                         }
647                         else
648                                 table[j] = 0;
649                 }
650                 // Fractal
651                 table[j++] = (ret + 0x0000) | 0x813;
652                 table[j++] = (ret + 0x1000) | 0x813;
653                 // Nuke the rest
654                 for(      ; j < 1024; j ++ )
655                         table[j] = 0;
656                 
657                 // Get kernel stack bottom
658                 sp = __SP & ~(MM_KSTACK_SIZE-1);
659                 j = (sp / 0x1000) % 1024;
660                 num = MM_KSTACK_SIZE/0x1000;
661
662 //              Log("num = %i, sp = %p, j = %i", num, sp, j);
663                 
664                 // Copy stack pages
665                 for(; num--; j ++, sp += 0x1000)
666                 {
667                         tVAddr  page;
668                         void    *tmp_page;
669                         
670                         page = MM_AllocPhys();
671 //                      Log("page = %P", page);
672                         table[j] = page | 0x813;
673
674                         tmp_page = (void*)MM_MapTemp(page);
675                         memcpy(tmp_page, (void*)sp, 0x1000);
676                         MM_FreeTemp( (tVAddr) tmp_page );
677                 }
678
679                 MM_FreeTemp( (tVAddr)table );
680         }
681
682         MM_FreeTemp( (tVAddr)new_lvl1_1 );
683         MM_FreeTemp( (tVAddr)new_lvl1_2 );
684
685 //      Log("MM_Clone: ret = %P", ret);
686
687         return ret;
688 }
689
690 void MM_ClearUser(void)
691 {
692          int    i, j;
693         const int       user_table_count = USER_STACK_TOP / (256*0x1000);
694         Uint32  *cur = (void*)MM_TABLE0USER;
695         Uint32  *tab;
696         
697 //      MM_DumpTables(0, 0x80000000);
698
699 //      Log("user_table_count = %i (as opposed to %i)", user_table_count, 0x800-4);
700
701         for( i = 0; i < user_table_count; i ++ )
702         {
703                 switch( cur[i] & 3 )
704                 {
705                 case 0: break;  // Already unmapped
706                 case 1: // Sub pages
707                         tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
708                         for( j = 0; j < 1024; j ++ )
709                         {
710                                 switch( tab[j] & 3 )
711                                 {
712                                 case 0: break;  // Unmapped
713                                 case 1:
714                                         Log_Error("MMVirt", "TODO: Support large pages in MM_ClearUser");
715                                         break;
716                                 case 2:
717                                 case 3:
718                                         MM_DerefPhys( tab[j] & ~(PAGE_SIZE-1) );
719                                         break;
720                                 }
721                         }
722                         MM_DerefPhys( cur[i] & ~(PAGE_SIZE-1) );
723                         cur[i+0] = 0;
724                         cur[i+1] = 0;
725                         cur[i+2] = 0;
726                         i += 3;
727                         break;
728                 case 2:
729                 case 3:
730                         Log_Error("MMVirt", "TODO: Implement sections/supersections in MM_ClearUser");
731                         break;
732                 }
733                 cur[i] = 0;
734         }
735         
736         // Final block of 4 tables are KStack
737         i = 0x800 - 4;
738         
739         // Clear out unused stacks
740         {
741                 register Uint32 __SP asm("sp");
742                  int    cur_stack_base = ((__SP & ~(MM_KSTACK_SIZE-1)) / PAGE_SIZE) % 1024;
743
744                 tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
745                 
746                 // First 512 is the Table1 mapping + 2 for Table0 mapping
747                 for( j = 512+2; j < 1024; j ++ )
748                 {
749                         // Skip current stack
750                         if( j == cur_stack_base ) {
751                                 j += (MM_KSTACK_SIZE / PAGE_SIZE) - 1;
752                                 continue ;
753                         }
754                         if( !(tab[j] & 3) )     continue;
755                         ASSERT( (tab[j] & 3) == 2 );
756                         MM_DerefPhys( tab[j] & ~(PAGE_SIZE) );
757                         tab[j] = 0;
758                 }
759         }
760         
761
762 //      MM_DumpTables(0, 0x80000000);
763 }
764
765 tVAddr MM_MapTemp(tPAddr PAddr)
766 {
767         tVAddr  ret;
768         tMM_PageInfo    pi;
769
770         for( ret = MM_TMPMAP_BASE; ret < MM_TMPMAP_END - PAGE_SIZE; ret += PAGE_SIZE )
771         {
772                 if( MM_int_GetPageInfo(ret, &pi) == 0 )
773                         continue;
774
775 //              Log("MapTemp %P at %p by %p", PAddr, ret, __builtin_return_address(0));
776                 MM_RefPhys(PAddr);      // Counter the MM_Deallocate in FreeTemp
777                 MM_Map(ret, PAddr);
778                 
779                 return ret;
780         }
781         Log_Warning("MMVirt", "MM_MapTemp: All slots taken");
782         return 0;
783 }
784
785 void MM_FreeTemp(tVAddr VAddr)
786 {
787         if( VAddr < MM_TMPMAP_BASE || VAddr >= MM_TMPMAP_END ) {
788                 Log_Warning("MMVirt", "MM_FreeTemp: Passed an addr not from MM_MapTemp (%p)", VAddr);
789                 return ;
790         }
791         
792         MM_Deallocate(VAddr);
793 }
794
795 tVAddr MM_MapHWPages(tPAddr PAddr, Uint NPages)
796 {
797         tVAddr  ret;
798          int    i;
799         tMM_PageInfo    pi;
800
801         ENTER("xPAddr iNPages", PAddr, NPages);
802
803         // Scan for a location
804         for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_END - NPages * PAGE_SIZE; ret += PAGE_SIZE )
805         {
806 //              LOG("checking %p", ret);
807                 // Check if there is `NPages` free pages
808                 for( i = 0; i < NPages; i ++ )
809                 {
810                         if( MM_int_GetPageInfo(ret + i*PAGE_SIZE, &pi) == 0 )
811                                 break;
812                 }
813                 // Nope, jump to after the used page found and try again
814 //              LOG("i = %i, ==? %i", i, NPages);
815                 if( i != NPages ) {
816                         ret += i * PAGE_SIZE;
817                         continue ;
818                 }
819         
820                 // Map the pages        
821                 for( i = 0; i < NPages; i ++ )
822                         MM_Map(ret+i*PAGE_SIZE, PAddr+i*PAGE_SIZE);
823                 // and return
824                 LEAVE('p', ret);
825                 return ret;
826         }
827         Log_Warning("MMVirt", "MM_MapHWPages: No space for a %i page block", NPages);
828         LEAVE('p', 0);
829         return 0;
830 }
831
832 tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PAddr)
833 {
834         tPAddr  phys;
835         tVAddr  ret;
836
837         phys = MM_AllocPhysRange(Pages, MaxBits);
838         if(!phys) {
839                 Log_Warning("MMVirt", "No space left for a %i page block (MM_AllocDMA)", Pages);
840                 return 0;
841         }
842         
843         ret = MM_MapHWPages(phys, Pages);
844         *PAddr = phys;
845
846         return ret;
847 }
848
849 void MM_UnmapHWPages(tVAddr Vaddr, Uint Number)
850 {
851         Log_Error("MMVirt", "TODO: Implement MM_UnmapHWPages");
852 }
853
854 tVAddr MM_NewKStack(int bShared)
855 {
856         tVAddr  min_addr, max_addr;
857         tVAddr  addr, ofs;
858
859         if( bShared ) {
860                 min_addr = MM_GLOBALSTACKS;
861                 max_addr = MM_GLOBALSTACKS_END;
862         }
863         else {
864                 min_addr = MM_KSTACK_BASE;
865                 max_addr = MM_KSTACK_END;
866         }
867
868         // Locate a free slot
869         for( addr = min_addr; addr < max_addr; addr += MM_KSTACK_SIZE )
870         {
871                 tMM_PageInfo    pi;
872                 if( MM_int_GetPageInfo(addr+MM_KSTACK_SIZE-PAGE_SIZE, &pi) )    break;
873         }
874
875         // Check for an error   
876         if(addr >= max_addr) {
877                 return 0;
878         }
879
880         // 1 guard page
881         for( ofs = PAGE_SIZE; ofs < MM_KSTACK_SIZE; ofs += PAGE_SIZE )
882         {
883                 if( MM_Allocate(addr + ofs) == 0 )
884                 {
885                         while(ofs)
886                         {
887                                 ofs -= PAGE_SIZE;
888                                 MM_Deallocate(addr + ofs);
889                         }
890                         Log_Warning("MMVirt", "MM_NewKStack: Unable to allocate");
891                         return 0;
892                 }
893         }
894         return addr + ofs;
895 }
896
897 tVAddr MM_NewUserStack(void)
898 {
899         tVAddr  addr, ofs;
900
901         addr = USER_STACK_TOP - USER_STACK_SIZE;
902         if( MM_GetPhysAddr(addr + PAGE_SIZE) ) {
903                 Log_Error("MMVirt", "Unable to create initial user stack, addr %p taken",
904                         addr + PAGE_SIZE
905                         );
906                 return 0;
907         }
908
909         // 1 guard page
910         for( ofs = PAGE_SIZE; ofs < USER_STACK_SIZE; ofs += PAGE_SIZE )
911         {
912                 tPAddr  rv;
913                 if(ofs >= USER_STACK_SIZE - USER_STACK_COMM)
914                         rv = MM_Allocate(addr + ofs);
915                 else
916                         rv = MM_AllocateZero(addr + ofs);
917                 if(rv == 0)
918                 {
919                         while(ofs)
920                         {
921                                 ofs -= PAGE_SIZE;
922                                 MM_Deallocate(addr + ofs);
923                         }
924                         Log_Warning("MMVirt", "MM_NewUserStack: Unable to allocate");
925                         return 0;
926                 }
927                 MM_SetFlags(addr+ofs, 0, MM_PFLAG_KERNEL);
928         }
929 //      Log("Return %p", addr + ofs);
930 //      MM_DumpTables(0, 0x80000000);
931         return addr + ofs;
932 }
933
934 void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info)
935 {
936         if( giMM_ZeroPage && Info->PhysAddr == giMM_ZeroPage )
937         {
938                 Debug("%p => %8s - 0x%7x %i %x",
939                         Start, "ZERO", Len,
940                         Info->Domain, Info->AP
941                         );
942         }
943         else
944         {
945                 Debug("%p => %8x - 0x%7x %i %x",
946                         Start, Info->PhysAddr-Len, Len,
947                         Info->Domain, Info->AP
948                         );
949         }
950 }
951
952 void MM_DumpTables(tVAddr Start, tVAddr End)
953 {
954         tVAddr  range_start = 0, addr;
955         tMM_PageInfo    pi, pi_old;
956          int    i = 0, inRange=0;
957         
958         pi_old.Size = 0;
959         pi_old.AP = 0;
960         pi_old.PhysAddr = 0;
961
962         Debug("Page Table Dump (%p to %p):", Start, End);
963         range_start = Start;
964         for( addr = Start; i == 0 || (addr && addr < End); i = 1 )
965         {
966                  int    rv;
967 //              Log("addr = %p", addr);
968                 rv = MM_int_GetPageInfo(addr, &pi);
969                 if( rv
970                  || pi.Size != pi_old.Size
971                  || pi.Domain != pi_old.Domain
972                  || pi.AP != pi_old.AP
973                  || pi_old.PhysAddr != pi.PhysAddr )
974                 {
975                         if(inRange) {
976                                 MM_int_DumpTableEnt(range_start, addr - range_start, &pi_old);
977                         }
978                         addr &= ~((1 << pi.Size)-1);
979                         range_start = addr;
980                 }
981                 
982                 pi_old = pi;
983                 // Handle the zero page
984                 if( !giMM_ZeroPage || pi_old.Size != 12 || pi_old.PhysAddr != giMM_ZeroPage )
985                         pi_old.PhysAddr += 1 << pi_old.Size;
986                 addr += 1 << pi_old.Size;
987                 inRange = (rv == 0);
988         }
989         if(inRange)
990                 MM_int_DumpTableEnt(range_start, addr - range_start, &pi);
991         Debug("Done");
992 }
993
994 // NOTE: Runs in abort context, not much difference, just a smaller stack
995 void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch)
996 {
997          int    rv;
998         tMM_PageInfo    pi;
999         
1000         rv = MM_int_GetPageInfo(Addr, &pi);
1001         
1002         // Check for COW
1003         if( rv == 0 &&  pi.AP == AP_RO_BOTH )
1004         {
1005                 pi.AP = AP_RW_BOTH;
1006                 if( giMM_ZeroPage && pi.PhysAddr == giMM_ZeroPage )
1007                 {
1008                         tPAddr  newpage;
1009                         newpage = MM_AllocPhys();
1010                         if( !newpage ) {
1011                                 Log_Error("MMVirt", "Unable to allocate new page for COW of ZERO");
1012                                 for(;;);
1013                         }
1014                         
1015                         #if TRACE_COW
1016                         Log_Notice("MMVirt", "COW %p caused by %p, ZERO duped to %P (RefCnt(%i)--)", Addr, PC,
1017                                 newpage, MM_GetRefCount(pi.PhysAddr));
1018                         #endif
1019
1020                         MM_DerefPhys(pi.PhysAddr);
1021                         pi.PhysAddr = newpage;
1022                         pi.AP = AP_RW_BOTH;
1023                         MM_int_SetPageInfo(Addr, &pi);
1024                         
1025                         memset( (void*)(Addr & ~(PAGE_SIZE-1)), 0, PAGE_SIZE );
1026
1027                         return ;
1028                 }
1029                 else if( MM_GetRefCount(pi.PhysAddr) > 1 )
1030                 {
1031                         // Duplicate the page
1032                         tPAddr  newpage;
1033                         void    *dst, *src;
1034                         
1035                         newpage = MM_AllocPhys();
1036                         if(!newpage) {
1037                                 Log_Error("MMVirt", "Unable to allocate new page for COW");
1038                                 for(;;);
1039                         }
1040                         dst = (void*)MM_MapTemp(newpage);
1041                         src = (void*)(Addr & ~(PAGE_SIZE-1));
1042                         memcpy( dst, src, PAGE_SIZE );
1043                         MM_FreeTemp( (tVAddr)dst );
1044                         
1045                         #if TRACE_COW
1046                         Log_Notice("MMVirt", "COW %p caused by %p, %P duped to %P (RefCnt(%i)--)", Addr, PC,
1047                                 pi.PhysAddr, newpage, MM_GetRefCount(pi.PhysAddr));
1048                         #endif
1049
1050                         MM_DerefPhys(pi.PhysAddr);
1051                         pi.PhysAddr = newpage;
1052                 }
1053                 #if TRACE_COW
1054                 else {
1055                         Log_Notice("MMVirt", "COW %p caused by %p, took last reference to %P",
1056                                 Addr, PC, pi.PhysAddr);
1057                 }
1058                 #endif
1059                 // Unset COW
1060                 pi.AP = AP_RW_BOTH;
1061                 MM_int_SetPageInfo(Addr, &pi);
1062                 return ;
1063         }
1064         
1065
1066         Log_Error("MMVirt", "Code at %p accessed %p (DFSR = 0x%x)%s", PC, Addr, DFSR,
1067                 (bPrefetch ? " - Prefetch" : "")
1068                 );
1069         if( Addr < 0x80000000 )
1070                 MM_DumpTables(0, 0x80000000);
1071         else
1072                 MM_DumpTables(0x80000000, -1);
1073         for(;;);
1074 }
1075

UCC git Repository :: git.ucc.asn.au