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:
committed by
Adrien Destugues
parent
73c451087f
commit
b799d160f2
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user