summaryrefslogtreecommitdiff
path: root/arch/arm/cpu/armv8/cache_v8.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/cpu/armv8/cache_v8.c')
-rw-r--r--arch/arm/cpu/armv8/cache_v8.c72
1 files changed, 68 insertions, 4 deletions
diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c
index 6c5630c0a8..bd1c3e0335 100644
--- a/arch/arm/cpu/armv8/cache_v8.c
+++ b/arch/arm/cpu/armv8/cache_v8.c
@@ -501,7 +501,8 @@ static bool is_aligned(u64 addr, u64 size, u64 align)
return !(addr & (align - 1)) && !(size & (align - 1));
}
-static u64 set_one_region(u64 start, u64 size, u64 attrs, int level)
+/* Use flag to indicate if attrs has more than d-cache attributes */
+static u64 set_one_region(u64 start, u64 size, u64 attrs, bool flag, int level)
{
int levelshift = level2shift(level);
u64 levelsize = 1ULL << levelshift;
@@ -509,8 +510,13 @@ static u64 set_one_region(u64 start, u64 size, u64 attrs, int level)
/* Can we can just modify the current level block PTE? */
if (is_aligned(start, size, levelsize)) {
- *pte &= ~PMD_ATTRINDX_MASK;
- *pte |= attrs;
+ if (flag) {
+ *pte &= ~PMD_ATTRMASK;
+ *pte |= attrs & PMD_ATTRMASK;
+ } else {
+ *pte &= ~PMD_ATTRINDX_MASK;
+ *pte |= attrs & PMD_ATTRINDX_MASK;
+ }
debug("Set attrs=%llx pte=%p level=%d\n", attrs, pte, level);
return levelsize;
@@ -560,7 +566,8 @@ void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size,
u64 r;
for (level = 1; level < 4; level++) {
- r = set_one_region(start, size, attrs, level);
+ /* Set d-cache attributes only */
+ r = set_one_region(start, size, attrs, false, level);
if (r) {
/* PTE successfully replaced */
size -= r;
@@ -581,6 +588,63 @@ void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size,
flush_dcache_range(real_start, real_start + real_size);
}
+/*
+ * Modify MMU table for a region with updated PXN/UXN/Memory type/valid bits.
+ * The procecess is break-before-make. The target region will be marked as
+ * invalid during the process of changing.
+ */
+void mmu_change_region_attr(phys_addr_t addr, size_t siz, u64 attrs)
+{
+ int level;
+ u64 r, size, start;
+
+ start = addr;
+ size = siz;
+ /*
+ * Loop through the address range until we find a page granule that fits
+ * our alignment constraints, then set it to "invalid".
+ */
+ while (size > 0) {
+ for (level = 1; level < 4; level++) {
+ /* Set PTE to fault */
+ r = set_one_region(start, size, PTE_TYPE_FAULT, true,
+ level);
+ if (r) {
+ /* PTE successfully invalidated */
+ size -= r;
+ start += r;
+ break;
+ }
+ }
+ }
+
+ flush_dcache_range(gd->arch.tlb_addr,
+ gd->arch.tlb_addr + gd->arch.tlb_size);
+ __asm_invalidate_tlb_all();
+
+ /*
+ * Loop through the address range until we find a page granule that fits
+ * our alignment constraints, then set it to the new cache attributes
+ */
+ start = addr;
+ size = siz;
+ while (size > 0) {
+ for (level = 1; level < 4; level++) {
+ /* Set PTE to new attributes */
+ r = set_one_region(start, size, attrs, true, level);
+ if (r) {
+ /* PTE successfully updated */
+ size -= r;
+ start += r;
+ break;
+ }
+ }
+ }
+ flush_dcache_range(gd->arch.tlb_addr,
+ gd->arch.tlb_addr + gd->arch.tlb_size);
+ __asm_invalidate_tlb_all();
+}
+
#else /* CONFIG_SYS_DCACHE_OFF */
/*