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

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