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

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