diff --git a/headers/private/kernel/arch/arm/arm_mmu.h b/headers/private/kernel/arch/arm/arm_mmu.h index a5debe1a38..cc688e545b 100644 --- a/headers/private/kernel/arch/arm/arm_mmu.h +++ b/headers/private/kernel/arch/arm/arm_mmu.h @@ -60,11 +60,13 @@ #define ARM_MMU_L2_FLAG_S 0x400 #define ARM_MMU_L2_FLAG_NG 0x800 -#define ARM_MMU_L2_FLAG_AP_KRW 0x010 - // allow read and write for kernel only +#define ARM_MMU_L2_FLAG_HAIKU_SWDBM ARM_MMU_L2_FLAG_TEX2 + // reuse TEX[2] for software-emulated Dirty Bit Modifier -#define ARM_MMU_L2_FLAG_AP_RW 0x030 - // allow read and write for user and system +#define ARM_MMU_L2_FLAG_HAIKU_KERNEL_RW (ARM_MMU_L2_FLAG_TEX2 | ARM_MMU_L2_FLAG_AP0) + // allow read and write for kernel only + // set Accessed and Modified flag during early page mapping + // as the exception handler may be not available yet #define ARM_MMU_L1_TABLE_ENTRY_COUNT 4096 #define ARM_MMU_L1_TABLE_SIZE (ARM_MMU_L1_TABLE_ENTRY_COUNT \ @@ -97,7 +99,7 @@ #define ARM_PTE_ADDRESS_MASK 0xfffff000 #define ARM_PTE_TYPE_MASK 0x00000003 -#define ARM_PTE_PROTECTION_MASK 0x00000221 // AP[2:1], XN -#define ARM_PTE_MEMORY_TYPE_MASK 0x000001cc // TEX, B, C +#define ARM_PTE_PROTECTION_MASK 0x00000121 // SWDBM, AP[1], XN +#define ARM_PTE_MEMORY_TYPE_MASK 0x0000004c // TEX[0], B, C #endif /* _ARCH_ARM_ARM_MMU_H */ diff --git a/headers/private/system/arch/arm/arch_cpu_defs.h b/headers/private/system/arch/arm/arch_cpu_defs.h index 53c580a089..f5e50dbf0c 100644 --- a/headers/private/system/arch/arm/arch_cpu_defs.h +++ b/headers/private/system/arch/arm/arch_cpu_defs.h @@ -34,4 +34,7 @@ #define FSR_FS_PERMISSION_FAULT_L1 0x0d #define FSR_FS_PERMISSION_FAULT_L2 0x0f +#define FSR_FS_MASK 0x040f +#define FSR_LPAE_MASK 0x0200 + #endif /* _SYSTEM_ARCH_ARM_DEFS_H */ diff --git a/src/system/boot/platform/efi/arch/arm/arch_mmu.cpp b/src/system/boot/platform/efi/arch/arm/arch_mmu.cpp index 38fd96cf4f..137a75d811 100644 --- a/src/system/boot/platform/efi/arch/arm/arch_mmu.cpp +++ b/src/system/boot/platform/efi/arch/arm/arch_mmu.cpp @@ -263,7 +263,7 @@ arch_mmu_generate_post_efi_page_tables(size_t memoryMapSize, (efi_memory_descriptor *)(memoryMapAddr + i * descriptorSize); if ((entry->Attribute & EFI_MEMORY_RUNTIME) != 0) { map_range_to_new_area(entry, - ARM_MMU_L2_FLAG_B | ARM_MMU_L2_FLAG_C | ARM_MMU_L2_FLAG_AP_KRW); + ARM_MMU_L2_FLAG_B | ARM_MMU_L2_FLAG_C | ARM_MMU_L2_FLAG_HAIKU_KERNEL_RW); } } @@ -273,11 +273,11 @@ arch_mmu_generate_post_efi_page_tables(size_t memoryMapSize, size_t size; while (mmu_next_region(&cookie, &vaddr, &paddr, &size)) { map_range(vaddr, paddr, size, - ARM_MMU_L2_FLAG_B | ARM_MMU_L2_FLAG_C | ARM_MMU_L2_FLAG_AP_KRW); + ARM_MMU_L2_FLAG_B | ARM_MMU_L2_FLAG_C | ARM_MMU_L2_FLAG_HAIKU_KERNEL_RW); } map_range_to_new_area(gKernelArgs.arch_args.uart.regs, - ARM_MMU_L2_FLAG_B | ARM_MMU_L2_FLAG_AP_KRW | ARM_MMU_L2_FLAG_XN); + ARM_MMU_L2_FLAG_B | ARM_MMU_L2_FLAG_HAIKU_KERNEL_RW | ARM_MMU_L2_FLAG_XN); sort_address_ranges(gKernelArgs.virtual_allocated_range, gKernelArgs.num_virtual_allocated_ranges); diff --git a/src/system/kernel/arch/arm/arch_int.cpp b/src/system/kernel/arch/arm/arch_int.cpp index 31be5687f6..91f4434507 100644 --- a/src/system/kernel/arch/arm/arch_int.cpp +++ b/src/system/kernel/arch/arm/arch_int.cpp @@ -311,7 +311,7 @@ arch_arm_handle_access_flag_fault(addr_t far, uint32 fsr, bool isWrite, bool isE ARMVMTranslationMap *map = (ARMVMTranslationMap *)addressSpace->TranslationMap(); - if ((fsr & 0x060f) == FSR_FS_ACCESS_FLAG_FAULT) { + if ((fsr & (FSR_FS_MASK | FSR_LPAE_MASK)) == FSR_FS_ACCESS_FLAG_FAULT) { phys_addr_t physAddr; uint32 pageFlags; @@ -326,6 +326,21 @@ arch_arm_handle_access_flag_fault(addr_t far, uint32 fsr, bool isWrite, bool isE } } + if (isWrite && ((fsr & (FSR_FS_MASK | FSR_LPAE_MASK)) == FSR_FS_PERMISSION_FAULT_L2)) { + phys_addr_t physAddr; + uint32 pageFlags; + + map->QueryInterrupt(far, &physAddr, &pageFlags); + + if ((PAGE_PRESENT & pageFlags) == 0) + return false; + + if (((pageFlags & B_KERNEL_WRITE_AREA) && ((pageFlags & PAGE_MODIFIED) == 0))) { + map->SetFlags(far, PAGE_MODIFIED); + return true; + } + } + return false; } diff --git a/src/system/kernel/arch/arm/paging/32bit/ARMPagingMethod32Bit.cpp b/src/system/kernel/arch/arm/paging/32bit/ARMPagingMethod32Bit.cpp index 2b852e9947..cf3cba3a4e 100644 --- a/src/system/kernel/arch/arm/paging/32bit/ARMPagingMethod32Bit.cpp +++ b/src/system/kernel/arch/arm/paging/32bit/ARMPagingMethod32Bit.cpp @@ -183,7 +183,7 @@ ARMPagingMethod32Bit::PhysicalPageSlotPool::Map(phys_addr_t physicalAddress, pte = (physicalAddress & ARM_PTE_ADDRESS_MASK) | ARM_MMU_L2_TYPE_SMALLNEW | ARM_MMU_L2_FLAG_B | ARM_MMU_L2_FLAG_C - | ARM_MMU_L2_FLAG_AP_KRW | ARM_MMU_L2_FLAG_XN; + | ARM_MMU_L2_FLAG_HAIKU_KERNEL_RW | ARM_MMU_L2_FLAG_XN; arch_cpu_invalidate_TLB_page(virtualAddress); } @@ -408,7 +408,8 @@ ARMPagingMethod32Bit::MapEarly(kernel_args* args, addr_t virtualAddress, // now, fill in the pentry PutPageTableEntryInTable(ptEntry, - physicalAddress, attributes | PAGE_ACCESSED, 0, IS_KERNEL_ADDRESS(virtualAddress)); + physicalAddress, attributes | PAGE_ACCESSED | PAGE_MODIFIED, 0, + IS_KERNEL_ADDRESS(virtualAddress)); return B_OK; } diff --git a/src/system/kernel/arch/arm/paging/32bit/ARMPagingMethod32Bit.h b/src/system/kernel/arch/arm/paging/32bit/ARMPagingMethod32Bit.h index bb9a5d2f60..068553acd6 100644 --- a/src/system/kernel/arch/arm/paging/32bit/ARMPagingMethod32Bit.h +++ b/src/system/kernel/arch/arm/paging/32bit/ARMPagingMethod32Bit.h @@ -70,6 +70,8 @@ public: static page_table_entry ClearPageTableEntry(page_table_entry* entry); static page_table_entry ClearPageTableEntryFlags( page_table_entry* entry, uint32 flags); + static page_table_entry SetAndClearPageTableEntryFlags(page_table_entry* entry, + uint32 flagsToSet, uint32 flagsToClear); static uint32 AttributesToPageTableEntryFlags( uint32 attributes); @@ -145,6 +147,23 @@ ARMPagingMethod32Bit::ClearPageTableEntryFlags(page_table_entry* entry, uint32 f } +/*static*/ inline page_table_entry +ARMPagingMethod32Bit::SetAndClearPageTableEntryFlags(page_table_entry* entry, uint32 flagsToSet, uint32 flagsToClear) +{ + page_table_entry originalValue = *entry; + + while (true) { + page_table_entry oldEntry = atomic_test_and_set((int32*)entry, + (originalValue & ~flagsToClear) | flagsToSet, originalValue); + if (oldEntry == originalValue) + break; + originalValue = oldEntry; + } + + return originalValue; +} + + /*static*/ inline uint32 ARMPagingMethod32Bit::AttributesToPageTableEntryFlags(uint32 attributes) { @@ -153,15 +172,16 @@ ARMPagingMethod32Bit::AttributesToPageTableEntryFlags(uint32 attributes) if ((attributes & B_READ_AREA) != 0) { // user accessible apFlags = ARM_MMU_L2_FLAG_AP1; - if ((attributes & B_WRITE_AREA) == 0) { + if ((attributes & B_WRITE_AREA) == 0) apFlags |= ARM_MMU_L2_FLAG_AP2; - } + else + apFlags |= ARM_MMU_L2_FLAG_HAIKU_SWDBM; } else if ((attributes & B_KERNEL_WRITE_AREA) == 0) { // kernel ro apFlags = ARM_MMU_L2_FLAG_AP2; } else { // kernel rw - apFlags = 0; + apFlags = ARM_MMU_L2_FLAG_HAIKU_SWDBM; } if (((attributes & B_KERNEL_EXECUTE_AREA) == 0) && @@ -172,6 +192,9 @@ ARMPagingMethod32Bit::AttributesToPageTableEntryFlags(uint32 attributes) if ((attributes & PAGE_ACCESSED) != 0) apFlags |= ARM_MMU_L2_FLAG_AP0; + if ((attributes & PAGE_MODIFIED) == 0) + apFlags |= ARM_MMU_L2_FLAG_AP2; + return apFlags; } @@ -181,7 +204,7 @@ ARMPagingMethod32Bit::PageTableEntryFlagsToAttributes(uint32 pageTableEntry) { uint32 attributes; - if ((pageTableEntry & ARM_MMU_L2_FLAG_AP2) == 0) { + if ((pageTableEntry & ARM_MMU_L2_FLAG_HAIKU_SWDBM) != 0) { if ((pageTableEntry & ARM_MMU_L2_FLAG_AP1) != 0) { attributes = B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA; } else { @@ -197,6 +220,9 @@ ARMPagingMethod32Bit::PageTableEntryFlagsToAttributes(uint32 pageTableEntry) if ((pageTableEntry & ARM_MMU_L2_FLAG_AP0) != 0) attributes |= PAGE_ACCESSED; + if ((pageTableEntry & ARM_MMU_L2_FLAG_AP2) == 0) + attributes |= PAGE_MODIFIED; + if ((pageTableEntry & ARM_MMU_L2_FLAG_XN) == 0) { if ((attributes & B_KERNEL_READ_AREA) != 0) attributes |= B_KERNEL_EXECUTE_AREA; diff --git a/src/system/kernel/arch/arm/paging/32bit/ARMVMTranslationMap32Bit.cpp b/src/system/kernel/arch/arm/paging/32bit/ARMVMTranslationMap32Bit.cpp index 5bc1606085..90b03f6d94 100644 --- a/src/system/kernel/arch/arm/paging/32bit/ARMVMTranslationMap32Bit.cpp +++ b/src/system/kernel/arch/arm/paging/32bit/ARMVMTranslationMap32Bit.cpp @@ -462,7 +462,7 @@ ARMVMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size, // transfer the accessed/dirty flags to the page if ((oldEntry & ARM_MMU_L2_FLAG_AP0) != 0) page->accessed = true; - if (/*(oldEntry & ARM_PTE_DIRTY) != 0 */ false) // XXX + if ((oldEntry & ARM_MMU_L2_FLAG_AP2) == 0) page->modified = true; // remove the mapping object/decrement the wired_count of the @@ -771,17 +771,20 @@ ARMVMTranslationMap32Bit::SetFlags(addr_t virtualAddress, uint32 flags) } uint32 flagsToSet = (flags & PAGE_ACCESSED) ? ARM_MMU_L2_FLAG_AP0 : 0; + uint32 flagsToClear = (flags & PAGE_MODIFIED) ? ARM_MMU_L2_FLAG_AP2 : 0; page_table_entry* pt = (page_table_entry*)ARMPagingMethod32Bit::Method() ->PhysicalPageMapper()->InterruptGetPageTableAt( pd[index] & ARM_PDE_ADDRESS_MASK); index = VADDR_TO_PTENT(virtualAddress); - ARMPagingMethod32Bit::SetPageTableEntryFlags(&pt[index], flagsToSet); + ARMPagingMethod32Bit::SetAndClearPageTableEntryFlags(&pt[index], flagsToSet, flagsToClear); - // No need to flush TLB as we currently handle only accessed flag. - // TLB flush will be needed once modified flag is implemented. - //InvalidatePage(virtualAddress); + // normally we would call InvalidatePage() here and then Flush() later when all updates are done + // however, as this scenario happens only in case of Modified flag handling, + // we can directly call TLBIMVAIS from here as we need to update only a single TLB entry + if (flagsToClear) + arch_cpu_invalidate_TLB_page(virtualAddress); return B_OK; } @@ -796,12 +799,10 @@ ARMVMTranslationMap32Bit::ClearFlags(addr_t va, uint32 flags) // no pagetable here return B_OK; } -#if 0 //IRA - uint32 flagsToClear = ((flags & PAGE_MODIFIED) ? X86_PTE_DIRTY : 0) - | ((flags & PAGE_ACCESSED) ? ARM_MMU_L2_FLAG_AP0 : 0); -#else + uint32 flagsToClear = (flags & PAGE_ACCESSED) ? ARM_MMU_L2_FLAG_AP0 : 0; -#endif + uint32 flagsToSet = (flags & PAGE_MODIFIED) ? ARM_MMU_L2_FLAG_AP2 : 0; + Thread* thread = thread_get_current_thread(); ThreadCPUPinner pinner(thread); @@ -809,14 +810,14 @@ ARMVMTranslationMap32Bit::ClearFlags(addr_t va, uint32 flags) pd[index] & ARM_PDE_ADDRESS_MASK); index = VADDR_TO_PTENT(va); - // clear out the flags we've been requested to clear + // adjust the flags we've been requested to set/clear page_table_entry oldEntry - = ARMPagingMethod32Bit::ClearPageTableEntryFlags(&pt[index], - flagsToClear); + = ARMPagingMethod32Bit::SetAndClearPageTableEntryFlags(&pt[index], + flagsToSet, flagsToClear); pinner.Unlock(); - if ((oldEntry & flagsToClear) != 0) + if (((oldEntry & flagsToClear) != 0) || ((oldEntry & flagsToSet) == 0)) InvalidatePage(va); return B_OK; @@ -859,8 +860,8 @@ ARMVMTranslationMap32Bit::ClearAccessedAndModified(VMArea* area, addr_t address, } if (oldEntry & ARM_MMU_L2_FLAG_AP0) { // page was accessed -- just clear the flags - oldEntry = ARMPagingMethod32Bit::ClearPageTableEntryFlags( - &pt[index], ARM_MMU_L2_FLAG_AP0 /* | ARM_PTE_DIRTY*/ ); // XXX + oldEntry = ARMPagingMethod32Bit::SetAndClearPageTableEntryFlags( + &pt[index], ARM_MMU_L2_FLAG_AP2, ARM_MMU_L2_FLAG_AP0); break; } @@ -873,13 +874,13 @@ ARMVMTranslationMap32Bit::ClearAccessedAndModified(VMArea* area, addr_t address, // something changed -- check again } } else { - oldEntry = ARMPagingMethod32Bit::ClearPageTableEntryFlags(&pt[index], - ARM_MMU_L2_FLAG_AP0 /* | ARM_PTE_DIRTY*/ ); // XXX + oldEntry = ARMPagingMethod32Bit::SetAndClearPageTableEntryFlags(&pt[index], + ARM_MMU_L2_FLAG_AP2, ARM_MMU_L2_FLAG_AP0); } pinner.Unlock(); - _modified = false /* (oldEntry & X86_PTE_DIRTY) != 0 */; // XXX IRA + _modified = (oldEntry & ARM_MMU_L2_FLAG_AP2) == 0; if ((oldEntry & ARM_MMU_L2_FLAG_AP0) != 0) { // Note, that we only need to invalidate the address, if the