kernel/arm: implement memory attributes

Set AP[2:0] and XN flags based on page attributes.
PXN is not implemented as it seems to be available only
in L1 descriptors on ARMv7.

Set TEX, B, C flags based on memoryType:
* B_MTR_UC is mapped to Strongly Ordered (TEX=0, B=0, C=0)
* B_MTR_WC is mapped to Shareable Device Memory (TEX=0, B=1, C=0)
* B_MTR_WT is mapped to Outer and Inner Write-Through, no Write-Allocate (TEX=0, B=0, C=1)
* B_MTR_WB is mapped to Outer and Inner Write-Back, no Write-Allocate (TEx=0, B=1, C=1)
* B_MTR_WP has no direct equivalent on the ARM so it's mapped as B_MTR_WB
* default is Write-Back

Implement ARMPagingMethod32Bit::AttributesToPageTableEntryFlags()
for mapping from page attributes to AP[2:0] and XN flags.

Implement ARMPagingMethod32Bit::PageTableEntryFlagsToAttributes()
for the reverse mapping used in Query() and QueryInterrupt()
i.e. recover page attributes from AP[2:0] and XN flags.

Implement ARMPagingMethod32Bit::MemoryTypeToPageTableEntryFlags()
fr mapping from memoryType to TEX, B, C flags.

Implement ARMVMTranslationMap32Bit::Protect() which used to be commented out.

Accessed and modified flags are not implemented yet, so no such
flags are returned from Query() and QueryInterrupt().

Also because of this, we just invalidate TLB on any call to Protect()
without checking whether the page has been accessed.

Change-Id: I027af5c02bd6218d9f92a58044aeb26373e1956b
Reviewed-on: https://review.haiku-os.org/c/haiku/+/5236
Reviewed-by: Adrien Destugues <[email protected]>
Reviewed-by: Fredrik Holmqvist <[email protected]>
Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
David Karoly
2022-04-23 12:52:59 +00:00
committed by Adrien Destugues
parent d0c94afe0c
commit 745a40d78a
4 changed files with 102 additions and 85 deletions
@@ -96,4 +96,7 @@
#define ARM_PTE_ADDRESS_MASK 0xfffff000
#define ARM_PTE_TYPE_MASK 0x00000003
#define ARM_PTE_PROTECTION_MASK 0x00000231 // AP[2:0], XN
#define ARM_PTE_MEMORY_TYPE_MASK 0x000001cc // TEX, B, C
#endif /* _ARCH_ARM_ARM_MMU_H */
@@ -511,22 +511,10 @@ ARMPagingMethod32Bit::PutPageTableEntryInTable(page_table_entry* entry,
{
page_table_entry page = (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_RW;
#if 0 //IRA
| X86_PTE_PRESENT | (globalPage ? X86_PTE_GLOBAL : 0)
| MemoryTypeToPageTableEntryFlags(memoryType);
| MemoryTypeToPageTableEntryFlags(memoryType)
| AttributesToPageTableEntryFlags(attributes)
| (globalPage ? 0 : ARM_MMU_L2_FLAG_NG);
// if the page is user accessible, it's automatically
// accessible in kernel space, too (but with the same
// protection)
if ((attributes & B_USER_PROTECTION) != 0) {
page |= X86_PTE_USER;
if ((attributes & B_WRITE_AREA) != 0)
page |= X86_PTE_WRITABLE;
} else if ((attributes & B_KERNEL_WRITE_AREA) != 0)
page |= X86_PTE_WRITABLE;
#endif
// put it in the page table
*(volatile page_table_entry*)entry = page;
}
@@ -69,6 +69,10 @@ public:
static page_table_entry ClearPageTableEntryFlags(
page_table_entry* entry, uint32 flags);
static uint32 AttributesToPageTableEntryFlags(
uint32 attributes);
static uint32 PageTableEntryFlagsToAttributes(
uint32 pageTableEntry);
static uint32 MemoryTypeToPageTableEntryFlags(
uint32 memoryType);
@@ -139,37 +143,95 @@ ARMPagingMethod32Bit::ClearPageTableEntryFlags(page_table_entry* entry, uint32 f
}
/*static*/ inline uint32
ARMPagingMethod32Bit::AttributesToPageTableEntryFlags(uint32 attributes)
{
int apFlags = 0;
if ((attributes & B_WRITE_AREA) != 0) {
// kernel rw user rw
apFlags = ARM_MMU_L2_FLAG_AP1 | ARM_MMU_L2_FLAG_AP0;
} else if ((attributes & B_READ_AREA) != 0) {
if ((attributes & B_KERNEL_WRITE_AREA) != 0) {
// kernel rw user ro
apFlags = ARM_MMU_L2_FLAG_AP1;
} else {
// kernel ro user ro
apFlags = ARM_MMU_L2_FLAG_AP2 | ARM_MMU_L2_FLAG_AP1;
}
} else if ((attributes & B_KERNEL_WRITE_AREA) != 0) {
// kernel rw
apFlags = ARM_MMU_L2_FLAG_AP0;
} else {
// kernel ro
apFlags = ARM_MMU_L2_FLAG_AP2 | ARM_MMU_L2_FLAG_AP0;
}
if (((attributes & B_KERNEL_EXECUTE_AREA) == 0) &&
((attributes & B_EXECUTE_AREA) == 0)) {
apFlags |= ARM_MMU_L2_FLAG_XN;
}
return apFlags;
}
/*static*/ inline uint32
ARMPagingMethod32Bit::PageTableEntryFlagsToAttributes(uint32 pageTableEntry)
{
uint32 attributes;
if ((pageTableEntry & ARM_MMU_L2_FLAG_AP2) == 0) {
if ((pageTableEntry & ARM_MMU_L2_FLAG_AP1) != 0) {
if ((pageTableEntry & ARM_MMU_L2_FLAG_AP0) != 0)
attributes = B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA;
else
attributes = B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA;
} else {
if ((pageTableEntry & ARM_MMU_L2_FLAG_AP0) != 0)
attributes = B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA;
else
attributes = 0;
}
} else {
if ((pageTableEntry & ARM_MMU_L2_FLAG_AP1) != 0)
attributes = B_KERNEL_READ_AREA | B_READ_AREA;
else if ((pageTableEntry & ARM_MMU_L2_FLAG_AP0) != 0)
attributes = B_KERNEL_READ_AREA;
else
attributes = 0;
}
if ((pageTableEntry & ARM_MMU_L2_FLAG_XN) == 0) {
if ((attributes & B_KERNEL_READ_AREA) != 0)
attributes |= B_KERNEL_EXECUTE_AREA;
if ((attributes & B_READ_AREA) != 0)
attributes |= B_EXECUTE_AREA;
}
return attributes;
}
/*static*/ inline uint32
ARMPagingMethod32Bit::MemoryTypeToPageTableEntryFlags(uint32 memoryType)
{
#if 0 //IRA
// ATM we only handle the uncacheable and write-through type explicitly. For
// all other types we rely on the MTRRs to be set up correctly. Since we set
// the default memory type to write-back and since the uncacheable type in
// the PTE overrides any MTRR attribute (though, as per the specs, that is
// not recommended for performance reasons), this reduces the work we
// actually *have* to do with the MTRRs to setting the remaining types
// (usually only write-combining for the frame buffer).
switch (memoryType) {
case B_MTR_UC:
return X86_PTE_CACHING_DISABLED | X86_PTE_WRITE_THROUGH;
case B_MTR_WC:
// ARM_PTE_WRITE_THROUGH would be closer, but the combination with
// MTRR WC is "implementation defined" for Pentium Pro/II.
// Strongly Ordered
return 0;
case B_MTR_WC:
// Shareable Device Memory
return ARM_MMU_L2_FLAG_B;
case B_MTR_WT:
return X86_PTE_WRITE_THROUGH;
// Outer and Inner Write-Through, no Write-Allocate
return ARM_MMU_L2_FLAG_C;
case B_MTR_WP:
case B_MTR_WB:
default:
return 0;
// Outer and Inner Write-Back, no Write-Allocate
return ARM_MMU_L2_FLAG_B | ARM_MMU_L2_FLAG_C;
}
#else
return 0;
#endif
}
@@ -638,23 +638,11 @@ ARMVMTranslationMap32Bit::Query(addr_t va, phys_addr_t *_physical,
if ((entry & ARM_PTE_TYPE_MASK) != 0)
*_physical = (entry & ARM_PTE_ADDRESS_MASK);
#if 0 //IRA
// read in the page state flags
if ((entry & X86_PTE_USER) != 0) {
*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0)
| B_READ_AREA;
}
*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0)
| B_KERNEL_READ_AREA
| ((entry & ARM_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0)
| ((entry & ARM_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0)
| ((entry & ARM_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0);
#else
*_flags = B_KERNEL_WRITE_AREA | B_KERNEL_READ_AREA;
//TODO: read in the page state flags
*_flags = ARMPagingMethod32Bit::PageTableEntryFlagsToAttributes(entry);
if (*_physical != 0)
*_flags |= PAGE_PRESENT;
#endif
pinner.Unlock();
TRACE("query_tmap: returning pa 0x%lx for va 0x%lx\n", *_physical, va);
@@ -686,23 +674,11 @@ ARMVMTranslationMap32Bit::QueryInterrupt(addr_t va, phys_addr_t *_physical,
if ((entry & ARM_PTE_TYPE_MASK) != 0)
*_physical = (entry & ARM_PTE_ADDRESS_MASK);
#if 0
// read in the page state flags
if ((entry & X86_PTE_USER) != 0) {
*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0)
| B_READ_AREA;
}
*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0)
| B_KERNEL_READ_AREA
| ((entry & X86_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0)
| ((entry & X86_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0)
| ((entry & X86_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0);
#else
*_flags = B_KERNEL_WRITE_AREA | B_KERNEL_READ_AREA;
//TODO: read in the page state flags
*_flags = ARMPagingMethod32Bit::PageTableEntryFlagsToAttributes(entry);
if (*_physical != 0)
*_flags |= PAGE_PRESENT;
#endif
return B_OK;
}
@@ -717,15 +693,9 @@ ARMVMTranslationMap32Bit::Protect(addr_t start, addr_t end, uint32 attributes,
TRACE("protect_tmap: pages 0x%lx to 0x%lx, attributes %lx\n", start, end,
attributes);
#if 0 //IRA
// compute protection flags
uint32 newProtectionFlags = 0;
if ((attributes & B_USER_PROTECTION) != 0) {
newProtectionFlags = ARM_PTE_USER;
if ((attributes & B_WRITE_AREA) != 0)
newProtectionFlags |= ARM_PTE_WRITABLE;
} else if ((attributes & B_KERNEL_WRITE_AREA) != 0)
newProtectionFlags = ARM_PTE_WRITABLE;
uint32 newProtectionFlags = ARMPagingMethod32Bit::AttributesToPageTableEntryFlags(attributes);
uint32 newMemoryTypeFlags = ARMPagingMethod32Bit::MemoryTypeToPageTableEntryFlags(memoryType);
page_directory_entry *pd = fPagingStructures->pgdir_virt;
@@ -747,7 +717,7 @@ ARMVMTranslationMap32Bit::Protect(addr_t start, addr_t end, uint32 attributes,
for (index = VADDR_TO_PTENT(start); index < 256 && start < end;
index++, start += B_PAGE_SIZE) {
page_table_entry entry = pt[index];
if ((entry & ARM_PTE_PRESENT) == 0) {
if ((entry & ARM_PTE_TYPE_MASK) == 0) {
// page mapping not valid
continue;
}
@@ -762,24 +732,18 @@ ARMVMTranslationMap32Bit::Protect(addr_t start, addr_t end, uint32 attributes,
&pt[index],
(entry & ~(ARM_PTE_PROTECTION_MASK
| ARM_PTE_MEMORY_TYPE_MASK))
| newProtectionFlags
| ARMPagingMethod32Bit::MemoryTypeToPageTableEntryFlags(
memoryType),
| newProtectionFlags | newMemoryTypeFlags,
entry);
if (oldEntry == entry)
break;
entry = oldEntry;
}
if ((oldEntry & ARM_PTE_ACCESSED) != 0) {
// Note, that we only need to invalidate the address, if the
// accessed flag was set, since only then the entry could have
// been in any TLB.
InvalidatePage(start);
}
//TODO: invalidate only if the Accessed flag is set
InvalidatePage(start);
}
} while (start != 0 && start < end);
#endif
return B_OK;
}