kernel/arm/paging: implement Modified Flag

* Introduce SWDBM flag similarly to the arm64 port
* Reuse TEX[2] for SWDBM flag which should be availble
  to be used by the operating system if TEX remap
  is enabled.
* Introduce SetAndClearPageTableEntryFlags for updating
  accessed and modified flags atomically
* Startup sequence is handled similarly to accessed flag, i.e.
  set Modified flag in initially mapped pages in bootloader and early map.
* Once the kernel initialization has progressed enough,
  pages are mapped as read-only and modified flag handling is done
  in the page fault handler.

Change-Id: I8f761e2c6325d1b91481abd569d5e8befded0761
Reviewed-on: https://review.haiku-os.org/c/haiku/+/6518
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
David Karoly
2023-06-18 11:18:24 +00:00
committed by Adrien Destugues
parent 73c451087f
commit b799d160f2
7 changed files with 83 additions and 35 deletions
+8 -6
View File
@@ -60,11 +60,13 @@
#define ARM_MMU_L2_FLAG_S 0x400 #define ARM_MMU_L2_FLAG_S 0x400
#define ARM_MMU_L2_FLAG_NG 0x800 #define ARM_MMU_L2_FLAG_NG 0x800
#define ARM_MMU_L2_FLAG_AP_KRW 0x010 #define ARM_MMU_L2_FLAG_HAIKU_SWDBM ARM_MMU_L2_FLAG_TEX2
// allow read and write for kernel only // reuse TEX[2] for software-emulated Dirty Bit Modifier
#define ARM_MMU_L2_FLAG_AP_RW 0x030 #define ARM_MMU_L2_FLAG_HAIKU_KERNEL_RW (ARM_MMU_L2_FLAG_TEX2 | ARM_MMU_L2_FLAG_AP0)
// allow read and write for user and system // 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_ENTRY_COUNT 4096
#define ARM_MMU_L1_TABLE_SIZE (ARM_MMU_L1_TABLE_ENTRY_COUNT \ #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_ADDRESS_MASK 0xfffff000
#define ARM_PTE_TYPE_MASK 0x00000003 #define ARM_PTE_TYPE_MASK 0x00000003
#define ARM_PTE_PROTECTION_MASK 0x00000221 // AP[2:1], XN #define ARM_PTE_PROTECTION_MASK 0x00000121 // SWDBM, AP[1], XN
#define ARM_PTE_MEMORY_TYPE_MASK 0x000001cc // TEX, B, C #define ARM_PTE_MEMORY_TYPE_MASK 0x0000004c // TEX[0], B, C
#endif /* _ARCH_ARM_ARM_MMU_H */ #endif /* _ARCH_ARM_ARM_MMU_H */
@@ -34,4 +34,7 @@
#define FSR_FS_PERMISSION_FAULT_L1 0x0d #define FSR_FS_PERMISSION_FAULT_L1 0x0d
#define FSR_FS_PERMISSION_FAULT_L2 0x0f #define FSR_FS_PERMISSION_FAULT_L2 0x0f
#define FSR_FS_MASK 0x040f
#define FSR_LPAE_MASK 0x0200
#endif /* _SYSTEM_ARCH_ARM_DEFS_H */ #endif /* _SYSTEM_ARCH_ARM_DEFS_H */
@@ -263,7 +263,7 @@ arch_mmu_generate_post_efi_page_tables(size_t memoryMapSize,
(efi_memory_descriptor *)(memoryMapAddr + i * descriptorSize); (efi_memory_descriptor *)(memoryMapAddr + i * descriptorSize);
if ((entry->Attribute & EFI_MEMORY_RUNTIME) != 0) { if ((entry->Attribute & EFI_MEMORY_RUNTIME) != 0) {
map_range_to_new_area(entry, 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; size_t size;
while (mmu_next_region(&cookie, &vaddr, &paddr, &size)) { while (mmu_next_region(&cookie, &vaddr, &paddr, &size)) {
map_range(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, 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, sort_address_ranges(gKernelArgs.virtual_allocated_range,
gKernelArgs.num_virtual_allocated_ranges); gKernelArgs.num_virtual_allocated_ranges);
+16 -1
View File
@@ -311,7 +311,7 @@ arch_arm_handle_access_flag_fault(addr_t far, uint32 fsr, bool isWrite, bool isE
ARMVMTranslationMap *map = (ARMVMTranslationMap *)addressSpace->TranslationMap(); 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; phys_addr_t physAddr;
uint32 pageFlags; 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; return false;
} }
@@ -183,7 +183,7 @@ ARMPagingMethod32Bit::PhysicalPageSlotPool::Map(phys_addr_t physicalAddress,
pte = (physicalAddress & ARM_PTE_ADDRESS_MASK) pte = (physicalAddress & ARM_PTE_ADDRESS_MASK)
| ARM_MMU_L2_TYPE_SMALLNEW | ARM_MMU_L2_TYPE_SMALLNEW
| ARM_MMU_L2_FLAG_B | ARM_MMU_L2_FLAG_C | 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); arch_cpu_invalidate_TLB_page(virtualAddress);
} }
@@ -408,7 +408,8 @@ ARMPagingMethod32Bit::MapEarly(kernel_args* args, addr_t virtualAddress,
// now, fill in the pentry // now, fill in the pentry
PutPageTableEntryInTable(ptEntry, 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; return B_OK;
} }
@@ -70,6 +70,8 @@ public:
static page_table_entry ClearPageTableEntry(page_table_entry* entry); static page_table_entry ClearPageTableEntry(page_table_entry* entry);
static page_table_entry ClearPageTableEntryFlags( static page_table_entry ClearPageTableEntryFlags(
page_table_entry* entry, uint32 flags); page_table_entry* entry, uint32 flags);
static page_table_entry SetAndClearPageTableEntryFlags(page_table_entry* entry,
uint32 flagsToSet, uint32 flagsToClear);
static uint32 AttributesToPageTableEntryFlags( static uint32 AttributesToPageTableEntryFlags(
uint32 attributes); 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 /*static*/ inline uint32
ARMPagingMethod32Bit::AttributesToPageTableEntryFlags(uint32 attributes) ARMPagingMethod32Bit::AttributesToPageTableEntryFlags(uint32 attributes)
{ {
@@ -153,15 +172,16 @@ ARMPagingMethod32Bit::AttributesToPageTableEntryFlags(uint32 attributes)
if ((attributes & B_READ_AREA) != 0) { if ((attributes & B_READ_AREA) != 0) {
// user accessible // user accessible
apFlags = ARM_MMU_L2_FLAG_AP1; apFlags = ARM_MMU_L2_FLAG_AP1;
if ((attributes & B_WRITE_AREA) == 0) { if ((attributes & B_WRITE_AREA) == 0)
apFlags |= ARM_MMU_L2_FLAG_AP2; apFlags |= ARM_MMU_L2_FLAG_AP2;
} else
apFlags |= ARM_MMU_L2_FLAG_HAIKU_SWDBM;
} else if ((attributes & B_KERNEL_WRITE_AREA) == 0) { } else if ((attributes & B_KERNEL_WRITE_AREA) == 0) {
// kernel ro // kernel ro
apFlags = ARM_MMU_L2_FLAG_AP2; apFlags = ARM_MMU_L2_FLAG_AP2;
} else { } else {
// kernel rw // kernel rw
apFlags = 0; apFlags = ARM_MMU_L2_FLAG_HAIKU_SWDBM;
} }
if (((attributes & B_KERNEL_EXECUTE_AREA) == 0) && if (((attributes & B_KERNEL_EXECUTE_AREA) == 0) &&
@@ -172,6 +192,9 @@ ARMPagingMethod32Bit::AttributesToPageTableEntryFlags(uint32 attributes)
if ((attributes & PAGE_ACCESSED) != 0) if ((attributes & PAGE_ACCESSED) != 0)
apFlags |= ARM_MMU_L2_FLAG_AP0; apFlags |= ARM_MMU_L2_FLAG_AP0;
if ((attributes & PAGE_MODIFIED) == 0)
apFlags |= ARM_MMU_L2_FLAG_AP2;
return apFlags; return apFlags;
} }
@@ -181,7 +204,7 @@ ARMPagingMethod32Bit::PageTableEntryFlagsToAttributes(uint32 pageTableEntry)
{ {
uint32 attributes; 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) { if ((pageTableEntry & ARM_MMU_L2_FLAG_AP1) != 0) {
attributes = B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA; attributes = B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA;
} else { } else {
@@ -197,6 +220,9 @@ ARMPagingMethod32Bit::PageTableEntryFlagsToAttributes(uint32 pageTableEntry)
if ((pageTableEntry & ARM_MMU_L2_FLAG_AP0) != 0) if ((pageTableEntry & ARM_MMU_L2_FLAG_AP0) != 0)
attributes |= PAGE_ACCESSED; attributes |= PAGE_ACCESSED;
if ((pageTableEntry & ARM_MMU_L2_FLAG_AP2) == 0)
attributes |= PAGE_MODIFIED;
if ((pageTableEntry & ARM_MMU_L2_FLAG_XN) == 0) { if ((pageTableEntry & ARM_MMU_L2_FLAG_XN) == 0) {
if ((attributes & B_KERNEL_READ_AREA) != 0) if ((attributes & B_KERNEL_READ_AREA) != 0)
attributes |= B_KERNEL_EXECUTE_AREA; attributes |= B_KERNEL_EXECUTE_AREA;
@@ -462,7 +462,7 @@ ARMVMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
// transfer the accessed/dirty flags to the page // transfer the accessed/dirty flags to the page
if ((oldEntry & ARM_MMU_L2_FLAG_AP0) != 0) if ((oldEntry & ARM_MMU_L2_FLAG_AP0) != 0)
page->accessed = true; page->accessed = true;
if (/*(oldEntry & ARM_PTE_DIRTY) != 0 */ false) // XXX if ((oldEntry & ARM_MMU_L2_FLAG_AP2) == 0)
page->modified = true; page->modified = true;
// remove the mapping object/decrement the wired_count of the // 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 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() page_table_entry* pt = (page_table_entry*)ARMPagingMethod32Bit::Method()
->PhysicalPageMapper()->InterruptGetPageTableAt( ->PhysicalPageMapper()->InterruptGetPageTableAt(
pd[index] & ARM_PDE_ADDRESS_MASK); pd[index] & ARM_PDE_ADDRESS_MASK);
index = VADDR_TO_PTENT(virtualAddress); 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. // normally we would call InvalidatePage() here and then Flush() later when all updates are done
// TLB flush will be needed once modified flag is implemented. // however, as this scenario happens only in case of Modified flag handling,
//InvalidatePage(virtualAddress); // 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; return B_OK;
} }
@@ -796,12 +799,10 @@ ARMVMTranslationMap32Bit::ClearFlags(addr_t va, uint32 flags)
// no pagetable here // no pagetable here
return B_OK; 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; 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(); Thread* thread = thread_get_current_thread();
ThreadCPUPinner pinner(thread); ThreadCPUPinner pinner(thread);
@@ -809,14 +810,14 @@ ARMVMTranslationMap32Bit::ClearFlags(addr_t va, uint32 flags)
pd[index] & ARM_PDE_ADDRESS_MASK); pd[index] & ARM_PDE_ADDRESS_MASK);
index = VADDR_TO_PTENT(va); 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 page_table_entry oldEntry
= ARMPagingMethod32Bit::ClearPageTableEntryFlags(&pt[index], = ARMPagingMethod32Bit::SetAndClearPageTableEntryFlags(&pt[index],
flagsToClear); flagsToSet, flagsToClear);
pinner.Unlock(); pinner.Unlock();
if ((oldEntry & flagsToClear) != 0) if (((oldEntry & flagsToClear) != 0) || ((oldEntry & flagsToSet) == 0))
InvalidatePage(va); InvalidatePage(va);
return B_OK; return B_OK;
@@ -859,8 +860,8 @@ ARMVMTranslationMap32Bit::ClearAccessedAndModified(VMArea* area, addr_t address,
} }
if (oldEntry & ARM_MMU_L2_FLAG_AP0) { if (oldEntry & ARM_MMU_L2_FLAG_AP0) {
// page was accessed -- just clear the flags // page was accessed -- just clear the flags
oldEntry = ARMPagingMethod32Bit::ClearPageTableEntryFlags( oldEntry = ARMPagingMethod32Bit::SetAndClearPageTableEntryFlags(
&pt[index], ARM_MMU_L2_FLAG_AP0 /* | ARM_PTE_DIRTY*/ ); // XXX &pt[index], ARM_MMU_L2_FLAG_AP2, ARM_MMU_L2_FLAG_AP0);
break; break;
} }
@@ -873,13 +874,13 @@ ARMVMTranslationMap32Bit::ClearAccessedAndModified(VMArea* area, addr_t address,
// something changed -- check again // something changed -- check again
} }
} else { } else {
oldEntry = ARMPagingMethod32Bit::ClearPageTableEntryFlags(&pt[index], oldEntry = ARMPagingMethod32Bit::SetAndClearPageTableEntryFlags(&pt[index],
ARM_MMU_L2_FLAG_AP0 /* | ARM_PTE_DIRTY*/ ); // XXX ARM_MMU_L2_FLAG_AP2, ARM_MMU_L2_FLAG_AP0);
} }
pinner.Unlock(); 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) { if ((oldEntry & ARM_MMU_L2_FLAG_AP0) != 0) {
// Note, that we only need to invalidate the address, if the // Note, that we only need to invalidate the address, if the