4 * ARM7 Virtual Memory Manager
5 * - arch/arm7/mm_virt.c
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
22 extern Uint32 kernel_table0[];
36 //#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>20)])
37 #define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>22)])
38 #define USRFRACTAL(addr) (*((Uint32*)(0x7FDFF000) + ((addr)>>22)))
39 #define TLBIALL() __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0))
40 #define TLBIMVA(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 1;dsb;isb" : : "r" (((addr)&~0xFFF)|1):"memory")
41 #define DCCMVAC(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 1" : : "r" ((addr)&~0xFFF))
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);
62 int MM_InitialiseVirtual(void)
67 void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1)
69 if(VAddr & 0x80000000) {
70 *Table0 = (void*)&kernel_table0; // Level 0
71 *Table1 = (void*)MM_TABLE1KERN; // Level 1
74 *Table0 = (void*)MM_TABLE0USER;
75 *Table1 = (void*)MM_TABLE1USER;
79 int MM_int_AllocateCoarse(tVAddr VAddr, int Domain)
81 Uint32 *table0, *table1;
85 ENTER("xVAddr iDomain", VAddr, Domain);
87 MM_int_GetTables(VAddr, &table0, &table1);
89 VAddr &= ~(0x400000-1); // 4MiB per "block", 1 Page
91 desc = &table0[ VAddr>>20];
92 LOG("desc = %p", desc);
94 // table0: 4 bytes = 1 MiB
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]);
101 if( (desc[0] & 3) != 0 || (desc[1] & 3) != 0
102 || (desc[2] & 3) != 0 || (desc[3] & 3) != 0 )
109 paddr = MM_AllocPhys();
117 *desc = paddr | (Domain << 5) | 1;
118 desc[1] = desc[0] + 0x400;
119 desc[2] = desc[0] + 0x800;
120 desc[3] = desc[0] + 0xC00;
122 if( VAddr < 0x80000000 ) {
123 USRFRACTAL(VAddr) = paddr | 0x13;
126 FRACTAL(table1, VAddr) = paddr | 0x13;
132 memset( (void*)&table1[ (VAddr >> 12) & ~(1024-1) ], 0, 0x1000 );
138 int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
140 Uint32 *table0, *table1;
143 ENTER("pVAddr ppi", VAddr, pi);
145 MM_int_GetTables(VAddr, &table0, &table1);
147 desc = &table0[ VAddr >> 20 ];
148 LOG("desc = %p", desc);
152 case 12: // Small Page
153 case 16: // Large Page
155 if( (*desc & 3) == 0 ) {
156 MM_int_AllocateCoarse( VAddr, pi->Domain );
158 desc = &table1[ VAddr >> 12 ];
159 LOG("desc (2) = %p", desc);
163 // - Error if overwriting a large page
164 if( (*desc & 3) == 1 ) LEAVE_RET('i', 1);
165 if( pi->PhysAddr == 0 ) {
168 DCCMVAC( (tVAddr) desc );
169 // #warning "HACK: TLBIALL"
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
182 // #warning "HACK: TLBIALL"
184 DCCMVAC( (tVAddr) desc );
191 Log_Warning("MMVirt", "TODO: Implement large pages in MM_int_SetPageInfo");
194 case 20: // Section or unmapped
195 Log_Warning("MMVirt", "TODO: Implement sections in MM_int_SetPageInfo");
197 case 24: // Supersection
198 // Error if not aligned
199 if( VAddr & 0xFFFFFF ) {
203 if( (*desc & 3) == 0 || ((*desc & 3) == 2 && (*desc & (1 << 18))) )
205 if( pi->PhysAddr == 0 ) {
210 *desc = pi->PhysAddr & 0xFF000000;
211 // *desc |= ((pi->PhysAddr >> 32) & 0xF) << 20;
212 // *desc |= ((pi->PhysAddr >> 36) & 0x7) << 5;
213 *desc |= 2 | (1 << 18);
215 // TODO: Apply to all entries
216 Log_Warning("MMVirt", "TODO: Apply changes to all entries of supersections");
221 Log_Warning("MMVirt", "TODO: 24-bit not on supersection?");
230 int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
232 Uint32 *table0, *table1;
235 // LogF("MM_int_GetPageInfo: VAddr=%p, pi=%p\n", VAddr, pi);
237 MM_int_GetTables(VAddr, &table0, &table1);
239 desc = table0[ VAddr >> 20 ];
241 // if( VAddr > 0x90000000)
242 // LOG("table0 desc(%p) = %x", &table0[ VAddr >> 20 ], desc);
258 // 1: Coarse page table
260 // Domain from top level table
261 pi->Domain = (desc >> 5) & 7;
263 desc = table1[ VAddr >> 12 ];
264 // LOG("table1 desc(%p) = %x", &table1[ VAddr >> 12 ], desc);
271 // 1: Large Page (64KiB)
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;
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);
292 // 2: Section (or Supersection)
294 if( desc & (1 << 18) ) {
296 pi->PhysAddr = desc & 0xFF000000;
297 pi->PhysAddr |= (Uint64)((desc >> 20) & 0xF) << 32;
298 pi->PhysAddr |= (Uint64)((desc >> 5) & 0x7) << 36;
300 pi->Domain = 0; // Supersections default to zero
301 pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
306 pi->PhysAddr = desc & 0xFFF80000;
308 pi->Domain = (desc >> 5) & 7;
309 pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
312 // 3: Reserved (invalid)
323 tPAddr MM_GetPhysAddr(tVAddr VAddr)
326 if( MM_int_GetPageInfo(VAddr, &pi) )
328 return pi.PhysAddr | (VAddr & ((1 << pi.Size)-1));
331 Uint MM_GetFlags(tVAddr VAddr)
336 if( MM_int_GetPageInfo(VAddr, &pi) )
346 ret |= MM_PFLAG_KERNEL;
349 ret |= MM_PFLAG_KERNEL|MM_PFLAG_RO;
361 if( pi.bExecutable ) ret |= MM_PFLAG_EXEC;
365 void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
370 if( MM_int_GetPageInfo(VAddr, &pi) )
373 curFlags = MM_GetFlags(VAddr);
374 if( (curFlags & Mask) == Flags )
379 if( curFlags & MM_PFLAG_COW )
383 switch(curFlags & (MM_PFLAG_KERNEL|MM_PFLAG_RO) )
386 pi.AP = AP_RW_BOTH; break;
387 case MM_PFLAG_KERNEL:
388 pi.AP = AP_KRW_ONLY; break;
390 pi.AP = AP_RO_USER; break;
391 case MM_PFLAG_KERNEL|MM_PFLAG_RO:
392 pi.AP = AP_KRO_ONLY; break;
396 pi.bExecutable = !!(curFlags & MM_PFLAG_EXEC);
398 MM_int_SetPageInfo(VAddr, &pi);
401 int MM_IsValidBuffer(tVAddr Addr, size_t Size)
406 Size += Addr & (PAGE_SIZE-1);
407 Addr &= ~(PAGE_SIZE-1);
409 if( MM_int_GetPageInfo(Addr, &pi) ) return 0;
412 if(pi.AP != AP_KRW_ONLY && pi.AP != AP_KRO_ONLY)
415 while( Size >= PAGE_SIZE )
417 if( MM_int_GetPageInfo(Addr, &pi) )
419 if(bUser && (pi.AP == AP_KRW_ONLY || pi.AP == AP_KRO_ONLY))
428 int MM_Map(tVAddr VAddr, tPAddr PAddr)
430 tMM_PageInfo pi = {0};
432 Log("MM_Map %P=>%p", PAddr, VAddr);
437 if(VAddr < USER_STACK_TOP)
440 pi.AP = AP_KRW_ONLY; // Kernel Read/Write
442 if( MM_int_SetPageInfo(VAddr, &pi) ) {
443 // MM_DerefPhys(pi.PhysAddr);
449 tPAddr MM_Allocate(tVAddr VAddr)
451 tMM_PageInfo pi = {0};
453 ENTER("pVAddr", VAddr);
455 pi.PhysAddr = MM_AllocPhys();
456 if( pi.PhysAddr == 0 ) LEAVE_RET('i', 0);
458 if(VAddr < USER_STACK_TOP)
463 if( MM_int_SetPageInfo(VAddr, &pi) ) {
464 MM_DerefPhys(pi.PhysAddr);
468 LEAVE('x', pi.PhysAddr);
472 tPAddr MM_AllocateZero(tVAddr VAddr)
474 if( !giMM_ZeroPage ) {
475 giMM_ZeroPage = MM_Allocate(VAddr);
476 MM_RefPhys(giMM_ZeroPage);
477 memset((void*)VAddr, 0, PAGE_SIZE);
480 MM_RefPhys(giMM_ZeroPage);
481 MM_Map(VAddr, giMM_ZeroPage);
483 MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW);
484 return giMM_ZeroPage;
487 void MM_Deallocate(tVAddr VAddr)
491 if( MM_int_GetPageInfo(VAddr, &pi) ) return ;
492 if( pi.PhysAddr == 0 ) return;
493 MM_DerefPhys(pi.PhysAddr);
498 MM_int_SetPageInfo(VAddr, &pi);
501 tPAddr MM_AllocateRootTable(void)
505 ret = MM_AllocPhysRange(2, -1);
508 MM_DerefPhys(ret+0x1000);
509 ret = MM_AllocPhysRange(3, -1);
513 // Log("MM_AllocateRootTable: Second try not aligned, %P", ret);
516 MM_DerefPhys(ret + 0x2000);
517 // Log("MM_AllocateRootTable: Second try aligned, %P", ret);
521 // Log("MM_AllocateRootTable: Got it in one, %P", ret);
525 void MM_int_CloneTable(Uint32 *DestEnt, int Table)
529 Uint32 *cur = (void*)MM_TABLE1USER;
530 // Uint32 *cur = &FRACTAL(MM_TABLE1USER,0);
533 table = MM_AllocPhys();
538 tmp_map = (void*)MM_MapTemp(table);
540 for( i = 0; i < 1024; i ++ )
542 // Log_Debug("MMVirt", "cur[%i] (%p) = %x", Table*256+i, &cur[Table*256+i], cur[Table*256+i]);
545 case 0: tmp_map[i] = 0; break;
548 Log_Error("MMVirt", "TODO: Support large pages in MM_int_CloneTable (%p)", (Table*256+i)*0x1000);
555 // Debug("%p cur[%i] & 0x230 = 0x%x", Table*256*0x1000, i, cur[i] & 0x230);
556 if( (cur[i] & 0x230) == 0x010 )
560 newpage = MM_AllocPhys();
561 src = (void*)( (Table*256+i)*0x1000 );
562 dst = (void*)MM_MapTemp(newpage);
563 // Debug("Taking a copy of kernel page %p (%P)", src, cur[i] & ~0xFFF);
564 memcpy(dst, src, PAGE_SIZE);
565 MM_FreeTemp( (tVAddr)dst );
566 tmp_map[i] = newpage | (cur[i] & 0xFFF);
570 if( (cur[i] & 0x230) == 0x030 )
571 cur[i] |= 0x200; // Set to full RO (Full RO=COW, User RO = RO)
573 MM_RefPhys( tmp_map[i] & ~0xFFF );
578 MM_FreeTemp( (tVAddr) tmp_map );
580 DestEnt[0] = table + 0*0x400 + 1;
581 DestEnt[1] = table + 1*0x400 + 1;
582 DestEnt[2] = table + 2*0x400 + 1;
583 DestEnt[3] = table + 3*0x400 + 1;
586 tPAddr MM_Clone(void)
589 Uint32 *new_lvl1_1, *new_lvl1_2, *cur;
593 // MM_DumpTables(0, KERNEL_BASE);
595 ret = MM_AllocateRootTable();
597 cur = (void*)MM_TABLE0USER;
598 new_lvl1_1 = (void*)MM_MapTemp(ret);
599 new_lvl1_2 = (void*)MM_MapTemp(ret+0x1000);
600 tmp_map = new_lvl1_1;
601 for( i = 0; i < 0x800-4; i ++ )
603 // HACK! Ignore the original identity mapping
604 if( i == 0 && Threads_GetTID() == 0 ) {
609 tmp_map = &new_lvl1_2[-0x400];
612 case 0: tmp_map[i] = 0; break;
614 MM_int_CloneTable(&tmp_map[i], i);
615 i += 3; // Tables are alocated in blocks of 4
619 Log_Error("MMVirt", "TODO: Support Sections/Supersections in MM_Clone (i=%i)", i);
625 // Allocate Fractal table
628 tPAddr tmp = MM_AllocPhys();
629 Uint32 *table = (void*)MM_MapTemp(tmp);
631 register Uint32 __SP asm("sp");
633 // Map table to last 4MiB of user space
634 new_lvl1_2[0x3FC] = tmp + 0*0x400 + 1;
635 new_lvl1_2[0x3FD] = tmp + 1*0x400 + 1;
636 new_lvl1_2[0x3FE] = tmp + 2*0x400 + 1;
637 new_lvl1_2[0x3FF] = tmp + 3*0x400 + 1;
639 tmp_map = new_lvl1_1;
640 for( j = 0; j < 512; j ++ )
643 tmp_map = &new_lvl1_2[-0x400];
644 if( (tmp_map[j*4] & 3) == 1 )
646 table[j] = tmp_map[j*4] & PADDR_MASK_LVL1;// 0xFFFFFC00;
647 table[j] |= 0x813; // nG, Kernel Only, Small page, XN
653 table[j++] = (ret + 0x0000) | 0x813;
654 table[j++] = (ret + 0x1000) | 0x813;
656 for( ; j < 1024; j ++ )
659 // Get kernel stack bottom
660 sp = __SP & ~(MM_KSTACK_SIZE-1);
661 j = (sp / 0x1000) % 1024;
662 num = MM_KSTACK_SIZE/0x1000;
664 // Log("num = %i, sp = %p, j = %i", num, sp, j);
667 for(; num--; j ++, sp += 0x1000)
672 page = MM_AllocPhys();
673 // Log("page = %P", page);
674 table[j] = page | 0x813;
676 tmp_page = (void*)MM_MapTemp(page);
677 memcpy(tmp_page, (void*)sp, 0x1000);
678 MM_FreeTemp( (tVAddr) tmp_page );
681 MM_FreeTemp( (tVAddr)table );
684 MM_FreeTemp( (tVAddr)new_lvl1_1 );
685 MM_FreeTemp( (tVAddr)new_lvl1_2 );
687 // Log("MM_Clone: ret = %P", ret);
692 void MM_ClearUser(void)
695 const int user_table_count = USER_STACK_TOP / (256*0x1000);
696 Uint32 *cur = (void*)MM_TABLE0USER;
699 // MM_DumpTables(0, 0x80000000);
701 // Log("user_table_count = %i (as opposed to %i)", user_table_count, 0x800-4);
703 for( i = 0; i < user_table_count; i ++ )
707 case 0: break; // Already unmapped
709 tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
710 for( j = 0; j < 1024; j ++ )
714 case 0: break; // Unmapped
716 Log_Error("MMVirt", "TODO: Support large pages in MM_ClearUser");
720 MM_DerefPhys( tab[j] & ~(PAGE_SIZE-1) );
724 MM_DerefPhys( cur[i] & ~(PAGE_SIZE-1) );
732 Log_Error("MMVirt", "TODO: Implement sections/supersections in MM_ClearUser");
738 // Final block of 4 tables are KStack
741 // Clear out unused stacks
743 register Uint32 __SP asm("sp");
744 int cur_stack_base = ((__SP & ~(MM_KSTACK_SIZE-1)) / PAGE_SIZE) % 1024;
746 tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
748 // First 512 is the Table1 mapping + 2 for Table0 mapping
749 for( j = 512+2; j < 1024; j ++ )
751 // Skip current stack
752 if( j == cur_stack_base ) {
753 j += (MM_KSTACK_SIZE / PAGE_SIZE) - 1;
756 if( !(tab[j] & 3) ) continue;
757 ASSERT( (tab[j] & 3) == 2 );
758 MM_DerefPhys( tab[j] & ~(PAGE_SIZE) );
764 // MM_DumpTables(0, 0x80000000);
767 tVAddr MM_MapTemp(tPAddr PAddr)
772 for( 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
783 Log_Warning("MMVirt", "MM_MapTemp: All slots taken");
787 void MM_FreeTemp(tVAddr VAddr)
789 if( VAddr < MM_TMPMAP_BASE || VAddr >= MM_TMPMAP_END ) {
790 Log_Warning("MMVirt", "MM_FreeTemp: Passed an addr not from MM_MapTemp (%p)", VAddr);
794 MM_Deallocate(VAddr);
797 tVAddr MM_MapHWPages(tPAddr PAddr, Uint NPages)
803 ENTER("xPAddr iNPages", PAddr, NPages);
805 // Scan for a location
806 for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_END - NPages * PAGE_SIZE; ret += PAGE_SIZE )
808 // LOG("checking %p", ret);
809 // Check if there is `NPages` free pages
810 for( i = 0; i < NPages; i ++ )
812 if( MM_int_GetPageInfo(ret + i*PAGE_SIZE, &pi) == 0 )
815 // Nope, jump to after the used page found and try again
816 // LOG("i = %i, ==? %i", i, NPages);
818 ret += i * PAGE_SIZE;
823 for( i = 0; i < NPages; i ++ )
824 MM_Map(ret+i*PAGE_SIZE, PAddr+i*PAGE_SIZE);
829 Log_Warning("MMVirt", "MM_MapHWPages: No space for a %i page block", NPages);
834 tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PAddr)
839 phys = MM_AllocPhysRange(Pages, MaxBits);
841 Log_Warning("MMVirt", "No space left for a %i page block (MM_AllocDMA)", Pages);
845 ret = MM_MapHWPages(phys, Pages);
851 void MM_UnmapHWPages(tVAddr Vaddr, Uint Number)
853 Log_Error("MMVirt", "TODO: Implement MM_UnmapHWPages");
856 tVAddr MM_NewKStack(int bShared)
858 tVAddr min_addr, max_addr;
862 min_addr = MM_GLOBALSTACKS;
863 max_addr = MM_GLOBALSTACKS_END;
866 min_addr = MM_KSTACK_BASE;
867 max_addr = MM_KSTACK_END;
870 // Locate a free slot
871 for( addr = min_addr; addr < max_addr; addr += MM_KSTACK_SIZE )
874 if( MM_int_GetPageInfo(addr+MM_KSTACK_SIZE-PAGE_SIZE, &pi) ) break;
877 // Check for an error
878 if(addr >= max_addr) {
883 for( ofs = PAGE_SIZE; ofs < MM_KSTACK_SIZE; ofs += PAGE_SIZE )
885 if( MM_Allocate(addr + ofs) == 0 )
890 MM_Deallocate(addr + ofs);
892 Log_Warning("MMVirt", "MM_NewKStack: Unable to allocate");
899 tVAddr MM_NewUserStack(void)
903 addr = USER_STACK_TOP - USER_STACK_SIZE;
904 if( MM_GetPhysAddr(addr + PAGE_SIZE) ) {
905 Log_Error("MMVirt", "Unable to create initial user stack, addr %p taken",
912 for( ofs = PAGE_SIZE; ofs < USER_STACK_SIZE; ofs += PAGE_SIZE )
915 if(ofs >= USER_STACK_SIZE - USER_STACK_COMM)
916 rv = MM_Allocate(addr + ofs);
918 rv = MM_AllocateZero(addr + ofs);
924 MM_Deallocate(addr + ofs);
926 Log_Warning("MMVirt", "MM_NewUserStack: Unable to allocate");
929 MM_SetFlags(addr+ofs, 0, MM_PFLAG_KERNEL);
931 // Log("Return %p", addr + ofs);
932 // MM_DumpTables(0, 0x80000000);
936 void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info)
938 if( giMM_ZeroPage && Info->PhysAddr == giMM_ZeroPage )
940 Debug("%p => %8s - 0x%7x %i %x %s",
942 Info->Domain, Info->AP,
943 Info->bGlobal ? "G" : "nG"
948 Debug("%p => %8x - 0x%7x %i %x %s",
949 Start, Info->PhysAddr-Len, Len,
950 Info->Domain, Info->AP,
951 Info->bGlobal ? "G" : "nG"
956 void MM_DumpTables(tVAddr Start, tVAddr End)
958 tVAddr range_start = 0, addr;
959 tMM_PageInfo pi, pi_old;
960 int i = 0, inRange=0;
962 memset(&pi_old, 0, sizeof(pi_old));
964 Debug("Page Table Dump (%p to %p):", Start, End);
966 for( addr = Start; i == 0 || (addr && addr < End); i = 1 )
969 // Log("addr = %p", addr);
970 rv = MM_int_GetPageInfo(addr, &pi);
972 || pi.Size != pi_old.Size
973 || pi.Domain != pi_old.Domain
974 || pi.AP != pi_old.AP
975 || pi.bGlobal != pi_old.bGlobal
976 || pi_old.PhysAddr != pi.PhysAddr )
979 MM_int_DumpTableEnt(range_start, addr - range_start, &pi_old);
981 addr &= ~((1 << pi.Size)-1);
986 // Handle the zero page
987 if( !giMM_ZeroPage || pi_old.Size != 12 || pi_old.PhysAddr != giMM_ZeroPage )
988 pi_old.PhysAddr += 1 << pi_old.Size;
989 addr += 1 << pi_old.Size;
993 MM_int_DumpTableEnt(range_start, addr - range_start, &pi);
997 // NOTE: Runs in abort context, not much difference, just a smaller stack
998 void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch)
1003 rv = MM_int_GetPageInfo(Addr, &pi);
1006 if( rv == 0 && pi.AP == AP_RO_BOTH )
1009 if( giMM_ZeroPage && pi.PhysAddr == giMM_ZeroPage )
1012 newpage = MM_AllocPhys();
1014 Log_Error("MMVirt", "Unable to allocate new page for COW of ZERO");
1019 Log_Notice("MMVirt", "COW %p caused by %p, ZERO duped to %P (RefCnt(%i)--)", Addr, PC,
1020 newpage, MM_GetRefCount(pi.PhysAddr));
1023 MM_DerefPhys(pi.PhysAddr);
1024 pi.PhysAddr = newpage;
1026 MM_int_SetPageInfo(Addr, &pi);
1028 memset( (void*)(Addr & ~(PAGE_SIZE-1)), 0, PAGE_SIZE );
1032 else if( MM_GetRefCount(pi.PhysAddr) > 1 )
1034 // Duplicate the page
1038 newpage = MM_AllocPhys();
1040 Log_Error("MMVirt", "Unable to allocate new page for COW");
1043 dst = (void*)MM_MapTemp(newpage);
1044 src = (void*)(Addr & ~(PAGE_SIZE-1));
1045 memcpy( dst, src, PAGE_SIZE );
1046 MM_FreeTemp( (tVAddr)dst );
1049 Log_Notice("MMVirt", "COW %p caused by %p, %P duped to %P (RefCnt(%i)--)", Addr, PC,
1050 pi.PhysAddr, newpage, MM_GetRefCount(pi.PhysAddr));
1053 MM_DerefPhys(pi.PhysAddr);
1054 pi.PhysAddr = newpage;
1058 Log_Notice("MMVirt", "COW %p caused by %p, took last reference to %P",
1059 Addr, PC, pi.PhysAddr);
1064 MM_int_SetPageInfo(Addr, &pi);
1069 Log_Error("MMVirt", "Code at %p accessed %p (DFSR = 0x%x)%s", PC, Addr, DFSR,
1070 (bPrefetch ? " - Prefetch" : "")
1072 if( Addr < 0x80000000 )
1073 MM_DumpTables(0, 0x80000000);
1075 MM_DumpTables(0x80000000, -1);