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

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