4 * ARM7 Virtual Memory Manager
5 * - arch/arm7/mm_virt.c
15 #define AP_KRW_ONLY 1 // Kernel page
16 #define AP_KRO_ONLY 5 // Kernel RO page
17 #define AP_RW_BOTH 3 // Standard RW
18 #define AP_RO_BOTH 7 // COW Page
19 #define AP_RO_USER 2 // User RO Page
20 #define PADDR_MASK_LVL1 0xFFFFFC00
22 const char * const caAPValueNames[] = {
23 "AP_NOACCESS", "AP_KRW_ONLY",
24 "AP_RO_USER", "AP_RW_BOTH",
25 "AP_???_4", "AP_KRO_ONLY",
26 "AP_???_6", "AP_RO_BOTH"
30 extern Uint32 kernel_table0[];
44 //#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>20)])
45 #define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>22)])
46 #define USRFRACTAL(addr) (*((Uint32*)(0x7FDFF000) + ((addr)>>22)))
47 #define TLBIALL() __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0))
48 #define TLBIMVA(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 1;dsb;isb" : : "r" (((addr)&~0xFFF)|1):"memory")
49 #define DCCMVAC(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 1" : : "r" ((addr)&~0xFFF))
52 void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1);
53 int MM_int_AllocateCoarse(tVAddr VAddr, int Domain);
54 int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi);
55 int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi);
56 tVAddr MM_NewUserStack(void);
57 //tPAddr MM_AllocateZero(volatile void *VAddr);
58 tPAddr MM_AllocateRootTable(void);
59 void MM_int_CloneTable(Uint32 *DestEnt, int Table);
60 tPAddr MM_Clone(int ClearUser);
61 tVAddr MM_NewKStack(int bGlobal);
62 void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info);
63 //void MM_DumpTables(tVAddr Start, tVAddr End);
64 void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch, Uint32 SystemLR, Uint32 UserLR);
70 int MM_InitialiseVirtual(void)
75 void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1)
77 if(VAddr & 0x80000000) {
78 *Table0 = (void*)&kernel_table0; // Level 0
79 *Table1 = (void*)MM_TABLE1KERN; // Level 1
82 *Table0 = (void*)MM_TABLE0USER;
83 *Table1 = (void*)MM_TABLE1USER;
87 int MM_int_AllocateCoarse(tVAddr VAddr, int Domain)
89 Uint32 *table0, *table1;
93 ENTER("xVAddr iDomain", VAddr, Domain);
95 MM_int_GetTables(VAddr, &table0, &table1);
97 VAddr &= ~(0x400000-1); // 4MiB per "block", 1 Page
99 desc = &table0[ VAddr>>20];
100 LOG("desc = %p", desc);
102 // table0: 4 bytes = 1 MiB
104 LOG("desc[0] = %x", desc[0]);
105 LOG("desc[1] = %x", desc[1]);
106 LOG("desc[2] = %x", desc[2]);
107 LOG("desc[3] = %x", desc[3]);
109 if( (desc[0] & 3) != 0 || (desc[1] & 3) != 0
110 || (desc[2] & 3) != 0 || (desc[3] & 3) != 0 )
117 paddr = MM_AllocPhys();
125 *desc = paddr | (Domain << 5) | 1;
126 desc[1] = desc[0] + 0x400;
127 desc[2] = desc[0] + 0x800;
128 desc[3] = desc[0] + 0xC00;
130 if( VAddr < 0x80000000 ) {
131 USRFRACTAL(VAddr) = paddr | 0x13;
134 FRACTAL(table1, VAddr) = paddr | 0x13;
140 memset( (void*)&table1[ (VAddr >> 12) & ~(1024-1) ], 0, 0x1000 );
146 int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
148 Uint32 *table0, *table1;
151 ENTER("pVAddr ppi", VAddr, pi);
153 MM_int_GetTables(VAddr, &table0, &table1);
155 desc = &table0[ VAddr >> 20 ];
156 LOG("desc = %p", desc);
160 case 12: // Small Page
161 case 16: // Large Page
163 if( (*desc & 3) == 0 ) {
164 MM_int_AllocateCoarse( VAddr, pi->Domain );
166 desc = &table1[ VAddr >> 12 ];
167 LOG("desc (2) = %p", desc);
171 // - Error if overwriting a large page
172 if( (*desc & 3) == 1 ) LEAVE_RET('i', 1);
173 if( pi->PhysAddr == 0 ) {
176 DCCMVAC( (tVAddr) desc );
177 // #warning "HACK: TLBIALL"
183 *desc = (pi->PhysAddr & 0xFFFFF000) | 2;
184 if(!pi->bExecutable) *desc |= 1; // XN
185 if(!pi->bGlobal) *desc |= 1 << 11; // nG
186 if( pi->bShared) *desc |= 1 << 10; // S
187 *desc |= (pi->AP & 3) << 4; // AP
188 *desc |= ((pi->AP >> 2) & 1) << 9; // APX
190 // #warning "HACK: TLBIALL"
192 DCCMVAC( (tVAddr) desc );
199 Log_Warning("MMVirt", "TODO: Implement large pages in MM_int_SetPageInfo");
202 case 20: // Section or unmapped
203 Log_Warning("MMVirt", "TODO: Implement sections in MM_int_SetPageInfo");
205 case 24: // Supersection
206 // Error if not aligned
207 if( VAddr & 0xFFFFFF ) {
211 if( (*desc & 3) == 0 || ((*desc & 3) == 2 && (*desc & (1 << 18))) )
213 if( pi->PhysAddr == 0 ) {
218 *desc = pi->PhysAddr & 0xFF000000;
219 // *desc |= ((pi->PhysAddr >> 32) & 0xF) << 20;
220 // *desc |= ((pi->PhysAddr >> 36) & 0x7) << 5;
221 *desc |= 2 | (1 << 18);
223 // TODO: Apply to all entries
224 Log_Warning("MMVirt", "TODO: Apply changes to all entries of supersections");
229 Log_Warning("MMVirt", "TODO: 24-bit not on supersection?");
238 int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
240 Uint32 *table0, *table1;
243 // LogF("MM_int_GetPageInfo: VAddr=%p, pi=%p\n", VAddr, pi);
245 MM_int_GetTables(VAddr, &table0, &table1);
247 desc = table0[ VAddr >> 20 ];
249 // if( VAddr > 0x90000000)
250 // LOG("table0 desc(%p) = %x", &table0[ VAddr >> 20 ], desc);
266 // 1: Coarse page table
268 // Domain from top level table
269 pi->Domain = (desc >> 5) & 7;
271 desc = table1[ VAddr >> 12 ];
272 // LOG("table1 desc(%p) = %x", &table1[ VAddr >> 12 ], desc);
279 // 1: Large Page (64KiB)
282 pi->PhysAddr = desc & 0xFFFF0000;
283 pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2);
284 pi->bExecutable = !(desc & 0x8000);
285 pi->bShared = (desc >> 10) & 1;
291 pi->PhysAddr = desc & 0xFFFFF000;
292 pi->bExecutable = !(desc & 1);
293 pi->bGlobal = !(desc >> 11);
294 pi->bShared = (desc >> 10) & 1;
295 pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2);
300 // 2: Section (or Supersection)
302 if( desc & (1 << 18) ) {
304 pi->PhysAddr = desc & 0xFF000000;
305 pi->PhysAddr |= (Uint64)((desc >> 20) & 0xF) << 32;
306 pi->PhysAddr |= (Uint64)((desc >> 5) & 0x7) << 36;
308 pi->Domain = 0; // Supersections default to zero
309 pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
314 pi->PhysAddr = desc & 0xFFF80000;
316 pi->Domain = (desc >> 5) & 7;
317 pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
320 // 3: Reserved (invalid)
331 tPAddr MM_GetPhysAddr(volatile const void *Ptr)
334 if( MM_int_GetPageInfo((tVAddr)Ptr, &pi) )
336 return pi.PhysAddr | ((tVAddr)Ptr & ((1 << pi.Size)-1));
339 Uint MM_GetFlags(const volatile void *VAddr)
344 if( MM_int_GetPageInfo((tVAddr)VAddr, &pi) )
354 ret |= MM_PFLAG_KERNEL;
357 ret |= MM_PFLAG_KERNEL|MM_PFLAG_RO;
369 if( pi.bExecutable ) ret |= MM_PFLAG_EXEC;
373 void MM_SetFlags(volatile void *VAddr, Uint Flags, Uint Mask)
378 if( MM_int_GetPageInfo((tVAddr)VAddr, &pi) )
381 curFlags = MM_GetFlags(VAddr);
382 if( (curFlags & Mask) == Flags )
387 if( curFlags & MM_PFLAG_COW )
391 switch(curFlags & (MM_PFLAG_KERNEL|MM_PFLAG_RO) )
394 pi.AP = AP_RW_BOTH; break;
395 case MM_PFLAG_KERNEL:
396 pi.AP = AP_KRW_ONLY; break;
398 pi.AP = AP_RO_USER; break;
399 case MM_PFLAG_KERNEL|MM_PFLAG_RO:
400 pi.AP = AP_KRO_ONLY; break;
404 pi.bExecutable = !!(curFlags & MM_PFLAG_EXEC);
406 MM_int_SetPageInfo((tVAddr)VAddr, &pi);
409 int MM_IsValidBuffer(tVAddr Addr, size_t Size)
414 Size += Addr & (PAGE_SIZE-1);
415 Addr &= ~(PAGE_SIZE-1);
417 if( MM_int_GetPageInfo((tVAddr)Addr, &pi) ) return 0;
420 if(pi.AP != AP_KRW_ONLY && pi.AP != AP_KRO_ONLY)
423 while( Size >= PAGE_SIZE )
425 if( MM_int_GetPageInfo(Addr, &pi) )
427 if(bUser && (pi.AP == AP_KRW_ONLY || pi.AP == AP_KRO_ONLY))
436 int MM_Map(volatile void *VAddr, tPAddr PAddr)
438 tMM_PageInfo pi = {0};
440 Log("MM_Map %P=>%p", PAddr, VAddr);
443 // TODO: Double check that an address isn't being clobbered
447 pi.AP = ( (tVAddr)VAddr < USER_STACK_TOP ? AP_RW_BOTH : AP_KRW_ONLY );
449 if( MM_int_SetPageInfo( (tVAddr)VAddr, &pi) ) {
450 // MM_DerefPhys(pi.PhysAddr);
456 tPAddr MM_Allocate(volatile void *VAddr)
458 tMM_PageInfo pi = {0};
460 ENTER("pVAddr", VAddr);
462 pi.PhysAddr = MM_AllocPhys();
463 if( pi.PhysAddr == 0 ) LEAVE_RET('i', 0);
465 pi.AP = ( (tVAddr)VAddr < USER_STACK_TOP ? AP_RW_BOTH : AP_KRW_ONLY );
467 if( MM_int_SetPageInfo( (tVAddr)VAddr, &pi ) )
469 MM_DerefPhys(pi.PhysAddr);
473 LEAVE('x', pi.PhysAddr);
477 void MM_AllocateZero(volatile void *VAddr)
479 if( !giMM_ZeroPage ) {
480 giMM_ZeroPage = MM_Allocate(VAddr);
481 MM_RefPhys(giMM_ZeroPage);
482 memset((void*)VAddr, 0, PAGE_SIZE);
485 MM_RefPhys(giMM_ZeroPage);
486 MM_Map(VAddr, giMM_ZeroPage);
488 MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW);
491 void MM_Deallocate(volatile void *VAddr)
495 if( MM_int_GetPageInfo((tVAddr)VAddr, &pi) ) return ;
496 if( pi.PhysAddr == 0 ) return;
497 MM_DerefPhys(pi.PhysAddr);
502 MM_int_SetPageInfo((tVAddr)VAddr, &pi);
505 tPAddr MM_AllocateRootTable(void)
509 ret = MM_AllocPhysRange(2, -1);
512 MM_DerefPhys(ret+0x1000);
513 ret = MM_AllocPhysRange(3, -1);
517 // Log("MM_AllocateRootTable: Second try not aligned, %P", ret);
520 MM_DerefPhys(ret + 0x2000);
521 // Log("MM_AllocateRootTable: Second try aligned, %P", ret);
525 // Log("MM_AllocateRootTable: Got it in one, %P", ret);
529 void MM_int_CloneTable(Uint32 *DestEnt, int Table)
533 Uint32 *cur = (void*)MM_TABLE1USER;
534 // Uint32 *cur = &FRACTAL(MM_TABLE1USER,0);
537 table = MM_AllocPhys();
542 tmp_map = MM_MapTemp(table);
544 for( i = 0; i < 1024; i ++ )
546 // Log_Debug("MMVirt", "cur[%i] (%p) = %x", Table*256+i, &cur[Table*256+i], cur[Table*256+i]);
549 case 0: tmp_map[i] = 0; break;
552 Log_Error("MMVirt", "TODO: Support large pages in MM_int_CloneTable (%p)", (Table*256+i)*0x1000);
559 // Debug("%p cur[%i] & 0x230 = 0x%x", Table*256*0x1000, i, cur[i] & 0x230);
560 if( (cur[i] & 0x230) == 0x010 )
564 newpage = MM_AllocPhys();
565 src = (void*)( (Table*256+i)*0x1000 );
566 dst = MM_MapTemp(newpage);
567 // Debug("Taking a copy of kernel page %p (%P)", src, cur[i] & ~0xFFF);
568 memcpy(dst, src, PAGE_SIZE);
570 tmp_map[i] = newpage | (cur[i] & 0xFFF);
574 if( (cur[i] & 0x230) == 0x030 )
575 cur[i] |= 0x200; // Set to full RO (Full RO=COW, User RO = RO)
577 MM_RefPhys( tmp_map[i] & ~0xFFF );
582 MM_FreeTemp( tmp_map );
584 DestEnt[0] = table + 0*0x400 + 1;
585 DestEnt[1] = table + 1*0x400 + 1;
586 DestEnt[2] = table + 2*0x400 + 1;
587 DestEnt[3] = table + 3*0x400 + 1;
590 tPAddr MM_Clone(int EmptyUser)
594 // MM_DumpTables(0, KERNEL_BASE);
596 tPAddr ret = MM_AllocateRootTable();
598 Uint32 *new_lvl1_1 = MM_MapTemp(ret);
599 Uint32 *new_lvl1_2 = MM_MapTemp(ret+0x1000);
603 Uint32 *cur = (void*)MM_TABLE0USER;
604 tmp_map = new_lvl1_1;
605 for( int i = 0; i < 0x800-4; i ++ )
607 // HACK! Ignore the original identity mapping
608 if( i == 0 && Threads_GetTID() == 0 ) {
613 tmp_map = &new_lvl1_2[-0x400];
616 case 0: tmp_map[i] = 0; break;
618 MM_int_CloneTable(&tmp_map[i], i);
619 i += 3; // Tables are alocated in blocks of 4
623 Log_Error("MMVirt", "TODO: Support Sections/Supersections in MM_Clone (i=%i)", i);
630 // Allocate Fractal table
632 tPAddr tmp = MM_AllocPhys();
633 Uint32 *table = MM_MapTemp(tmp);
635 // Map table to last 4MiB of user space
636 new_lvl1_2[0x3FC] = tmp + 0*0x400 + 1;
637 new_lvl1_2[0x3FD] = tmp + 1*0x400 + 1;
638 new_lvl1_2[0x3FE] = tmp + 2*0x400 + 1;
639 new_lvl1_2[0x3FF] = tmp + 3*0x400 + 1;
641 tmp_map = new_lvl1_1;
643 for( ; j < 512; j ++ )
646 tmp_map = &new_lvl1_2[-0x400];
647 if( (tmp_map[j*4] & 3) == 1 )
649 table[j] = tmp_map[j*4] & PADDR_MASK_LVL1;// 0xFFFFFC00;
650 table[j] |= 0x813; // nG, Kernel Only, Small page, XN
656 table[j++] = (ret + 0x0000) | 0x813;
657 table[j++] = (ret + 0x1000) | 0x813;
658 // Clear the rest of the table
659 for( ; j < 1024; j ++ )
662 // Get kernel stack bottom
663 register Uint32 __SP asm("sp");
664 Uint32 sp = __SP & ~(MM_KSTACK_SIZE-1);
665 j = (sp / 0x1000) % 1024;
668 for(int num = MM_KSTACK_SIZE/PAGE_SIZE; num--; j ++, sp += PAGE_SIZE)
673 page = MM_AllocPhys();
674 // Log("page = %P", page);
675 table[j] = page | 0x813;
677 tmp_page = MM_MapTemp(page);
678 memcpy(tmp_page, (void*)sp, 0x1000);
679 MM_FreeTemp( tmp_page );
682 MM_FreeTemp( table );
685 MM_FreeTemp( new_lvl1_1 );
686 MM_FreeTemp( new_lvl1_2 );
688 // Log("MM_Clone: ret = %P", ret);
693 void MM_ClearUser(void)
696 const int user_table_count = USER_STACK_TOP / (256*0x1000);
697 Uint32 *cur = (void*)MM_TABLE0USER;
700 // MM_DumpTables(0, 0x80000000);
702 // Log("user_table_count = %i (as opposed to %i)", user_table_count, 0x800-4);
704 for( i = 0; i < user_table_count; i ++ )
708 case 0: break; // Already unmapped
710 tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
711 for( j = 0; j < 1024; j ++ )
715 case 0: break; // Unmapped
717 Log_Error("MMVirt", "TODO: Support large pages in MM_ClearUser");
721 MM_DerefPhys( tab[j] & ~(PAGE_SIZE-1) );
725 MM_DerefPhys( cur[i] & ~(PAGE_SIZE-1) );
733 Log_Error("MMVirt", "TODO: Implement sections/supersections in MM_ClearUser");
739 // Final block of 4 tables are KStack
742 // Clear out unused stacks
744 register Uint32 __SP asm("sp");
745 int cur_stack_base = ((__SP & ~(MM_KSTACK_SIZE-1)) / PAGE_SIZE) % 1024;
747 tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
749 // First 512 is the Table1 mapping + 2 for Table0 mapping
750 for( j = 512+2; j < 1024; j ++ )
752 // Skip current stack
753 if( j == cur_stack_base ) {
754 j += (MM_KSTACK_SIZE / PAGE_SIZE) - 1;
757 if( !(tab[j] & 3) ) continue;
758 ASSERT( (tab[j] & 3) == 2 );
759 MM_DerefPhys( tab[j] & ~(PAGE_SIZE) );
765 // MM_DumpTables(0, 0x80000000);
768 void *MM_MapTemp(tPAddr PAddr)
770 for( tVAddr ret = MM_TMPMAP_BASE; ret < MM_TMPMAP_END - PAGE_SIZE; ret += PAGE_SIZE )
774 if( MM_int_GetPageInfo(ret, &pi) == 0 )
777 // Log("MapTemp %P at %p by %p", PAddr, ret, __builtin_return_address(0));
778 MM_RefPhys(PAddr); // Counter the MM_Deallocate in FreeTemp
779 MM_Map( (void*)ret, PAddr );
783 Log_Warning("MMVirt", "MM_MapTemp: All slots taken");
787 void MM_FreeTemp(void *Ptr)
789 tVAddr VAddr = (tVAddr)Ptr;
790 if( VAddr < MM_TMPMAP_BASE || VAddr >= MM_TMPMAP_END ) {
791 Log_Warning("MMVirt", "MM_FreeTemp: Passed an addr not from MM_MapTemp (%p)", VAddr);
798 void *MM_MapHWPages(tPAddr PAddr, Uint NPages)
804 ENTER("xPAddr iNPages", PAddr, NPages);
806 // Scan for a location
807 for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_END - NPages * PAGE_SIZE; ret += PAGE_SIZE )
809 // LOG("checking %p", ret);
810 // Check if there is `NPages` free pages
811 for( i = 0; i < NPages; i ++ )
813 if( MM_int_GetPageInfo(ret + i*PAGE_SIZE, &pi) == 0 )
816 // Nope, jump to after the used page found and try again
817 // LOG("i = %i, ==? %i", i, NPages);
819 ret += i * PAGE_SIZE;
824 for( i = 0; i < NPages; i ++ )
825 MM_Map( (tPage*)ret + i, PAddr+i*PAGE_SIZE);
830 Log_Warning("MMVirt", "MM_MapHWPages: No space for a %i page block", NPages);
835 void *MM_AllocDMA(int Pages, int MaxBits, tPAddr *PAddr)
840 phys = MM_AllocPhysRange(Pages, MaxBits);
842 Log_Warning("MMVirt", "No space left for a %i page block (MM_AllocDMA)", Pages);
846 ret = MM_MapHWPages(phys, Pages);
857 void MM_UnmapHWPages(volatile void *VAddr, Uint Number)
859 Log_Error("MMVirt", "TODO: Implement MM_UnmapHWPages");
862 tVAddr MM_NewKStack(int bShared)
864 tVAddr min_addr, max_addr;
868 min_addr = MM_GLOBALSTACKS;
869 max_addr = MM_GLOBALSTACKS_END;
872 min_addr = MM_KSTACK_BASE;
873 max_addr = MM_KSTACK_END;
876 // Locate a free slot
877 for( addr = min_addr; addr < max_addr; addr += MM_KSTACK_SIZE )
880 if( MM_int_GetPageInfo(addr+MM_KSTACK_SIZE-PAGE_SIZE, &pi) ) break;
883 // Check for an error
884 if(addr >= max_addr) {
889 tPage *pageptr = (void*)(addr + PAGE_SIZE);
890 for( ofs = PAGE_SIZE; ofs < MM_KSTACK_SIZE; ofs += PAGE_SIZE )
892 if( MM_Allocate( pageptr ) == 0 )
897 MM_Deallocate( pageptr-- );
899 Log_Warning("MMVirt", "MM_NewKStack: Unable to allocate");
906 tVAddr MM_NewUserStack(void)
910 addr = USER_STACK_TOP - USER_STACK_SIZE;
911 if( MM_GetPhysAddr( (void*)(addr + PAGE_SIZE) ) ) {
912 Log_Error("MMVirt", "Unable to create initial user stack, addr %p taken",
915 MM_DumpTables(0,KERNEL_BASE);
920 tPage *pageptr = (void*)addr;
921 for( ofs = PAGE_SIZE; ofs < USER_STACK_SIZE; ofs += PAGE_SIZE, pageptr ++ )
923 if(ofs >= USER_STACK_SIZE - USER_STACK_COMM) {
924 tPAddr rv = MM_Allocate(pageptr);
930 MM_Deallocate(pageptr --);
932 Log_Warning("MMVirt", "MM_NewUserStack: Unable to allocate");
937 MM_AllocateZero(pageptr);
939 MM_SetFlags(pageptr, 0, MM_PFLAG_KERNEL);
941 Log("Return %p", addr + ofs);
942 // MM_DumpTables(0, 0x80000000);
946 void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info)
948 if( giMM_ZeroPage && Info->PhysAddr == giMM_ZeroPage )
950 Debug("0x%08x => %8s - 0x%7x D%i %x %s %s",
952 Info->Domain, Info->AP,
953 Info->bExecutable ? " X" : "nX",
954 Info->bGlobal ? " G" : "nG"
960 MM_GetPageNode(Info->PhysAddr, &node);
961 Debug("0x%08x => %8x - 0x%7x D%i %x %s %s %p",
962 Start, Info->PhysAddr-Len, Len,
963 Info->Domain, Info->AP,
964 Info->bExecutable ? " X" : "nX",
965 Info->bGlobal ? " G" : "nG",
971 void MM_DumpTables(tVAddr Start, tVAddr End)
973 tVAddr range_start = 0, addr;
974 tMM_PageInfo pi, pi_old;
975 int i = 0, inRange=0;
977 memset(&pi_old, 0, sizeof(pi_old));
979 Debug("Page Table Dump (%p to %p):", Start, End);
981 for( addr = Start; i == 0 || (addr && addr < End); i = 1 )
984 // Log("addr = %p", addr);
985 rv = MM_int_GetPageInfo(addr, &pi);
987 || pi.Size != pi_old.Size
988 || pi.Domain != pi_old.Domain
989 || pi.AP != pi_old.AP
990 || pi.bGlobal != pi_old.bGlobal
991 || pi_old.PhysAddr != pi.PhysAddr )
994 MM_int_DumpTableEnt(range_start, addr - range_start, &pi_old);
996 addr &= ~((1 << pi.Size)-1);
1001 // Handle the zero page
1002 if( !giMM_ZeroPage || pi_old.Size != 12 || pi_old.PhysAddr != giMM_ZeroPage )
1003 pi_old.PhysAddr += 1 << pi_old.Size;
1004 addr += 1 << pi_old.Size;
1005 inRange = (rv == 0);
1008 MM_int_DumpTableEnt(range_start, addr - range_start, &pi);
1012 // NOTE: Runs in abort context, not much difference, just a smaller stack
1013 void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch, Uint32 SystemLR, Uint32 UserLR)
1018 rv = MM_int_GetPageInfo(Addr, &pi);
1021 if( rv == 0 && pi.AP == AP_RO_BOTH )
1024 if( giMM_ZeroPage && pi.PhysAddr == giMM_ZeroPage )
1027 newpage = MM_AllocPhys();
1029 Log_Error("MMVirt", "Unable to allocate new page for COW of ZERO");
1034 Log_Notice("MMVirt", "COW %p caused by %p, ZERO duped to %P (RefCnt(%i)--)", Addr, PC,
1035 newpage, MM_GetRefCount(pi.PhysAddr));
1038 MM_DerefPhys(pi.PhysAddr);
1039 pi.PhysAddr = newpage;
1041 MM_int_SetPageInfo(Addr, &pi);
1043 memset( (void*)(Addr & ~(PAGE_SIZE-1)), 0, PAGE_SIZE );
1047 else if( MM_GetRefCount(pi.PhysAddr) > 1 )
1049 // Duplicate the page
1053 newpage = MM_AllocPhys();
1055 Log_Error("MMVirt", "Unable to allocate new page for COW");
1058 dst = MM_MapTemp(newpage);
1059 src = (void*)(Addr & ~(PAGE_SIZE-1));
1060 memcpy( dst, src, PAGE_SIZE );
1064 Log_Notice("MMVirt", "COW %p caused by %p, %P duped to %P (RefCnt(%i)--)", Addr, PC,
1065 pi.PhysAddr, newpage, MM_GetRefCount(pi.PhysAddr));
1068 MM_DerefPhys(pi.PhysAddr);
1069 pi.PhysAddr = newpage;
1073 Log_Notice("MMVirt", "COW %p caused by %p, took last reference to %P",
1074 Addr, PC, pi.PhysAddr);
1079 MM_int_SetPageInfo(Addr, &pi);
1084 Log_Error("MMVirt", "Code at %p accessed %p (DFSR = 0x%x)%s", PC, Addr, DFSR,
1085 (bPrefetch ? " - Prefetch" : "")
1087 Log_Error("MMVirt", "- User LR = 0x%x, System LR = 0x%x", UserLR, SystemLR);
1088 const char * const dfsr_errors[] = {
1089 /* 00000 */ "-", "Alignment Fault",
1090 /* 00010 */ "Debug event", "Access Flag (Section)",
1091 /* 00100 */ "Instr Cache Maint", "Translation (Section)",
1092 /* 00110 */ "Access Flag (Page)", "Translation (Page)",
1093 /* 01000 */ "Sync. External abort", "Domain (Section)",
1094 /* 01010 */ "-", "Domain (Page)",
1095 /* 01100 */ "Table Walk sync ext (lvl 1)", "Permission (Section)",
1096 /* 01110 */ "Table Walk sync ext (lvl 2)", "Permission (Page)",
1098 /* 10000 */ "-", "-",
1099 /* 10010 */ "-", "-",
1100 /* 10100 */ "IMPL (Lockdown)", "-",
1101 /* 10110 */ "Async. Extern. Abort", "-",
1102 /* 11000 */ "Mem. access async pairity error", "Mem. access async pairity error",
1103 /* 11010 */ "IMPL (Coprocessor abort)", "-",
1104 /* 11100 */ "Table Walk Sync parity (lvl 1)", "-",
1105 /* 11110 */ "Table Walk Sync parity (lvl 2)", "-"
1107 int errcode = (DFSR & 0xF) | (((DFSR >> 10) & 1) << 4);
1108 Log_Error("MMVirt", "- Errcode 0b%05b", errcode);
1109 Log_Error("MMVirt", "- Dom %i %s %s",
1110 (DFSR >> 4) & 0xF, (DFSR & 0x800 ? "Write": "Read"),
1111 dfsr_errors[errcode]
1113 Log_Error("MMVirt", "- AP=%i(%s) %s", pi.AP, caAPValueNames[pi.AP], pi.bExecutable ? " Executable":"");
1114 if( Addr < 0x80000000 )
1115 MM_DumpTables(0, 0x80000000);
1117 MM_DumpTables(0x80000000, -1);