armv8: mmu: Add a function to change mapping attributes

Function mmu_change_region_attr() is added to change existing mapping
with updated PXN, UXN and memory type. This is a break-before-make
process during which the mapping becomes fault (invalid) before final
attributres are set.

Signed-off-by: York Sun <york.sun@nxp.com>
diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c
index 6c5630c..bd1c3e0 100644
--- a/arch/arm/cpu/armv8/cache_v8.c
+++ b/arch/arm/cpu/armv8/cache_v8.c
@@ -501,7 +501,8 @@
 	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 @@
 
 	/* 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 @@
 		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 @@
 	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 */
 
 /*
diff --git a/arch/arm/include/asm/armv8/mmu.h b/arch/arm/include/asm/armv8/mmu.h
index e9b4cdb..a349903 100644
--- a/arch/arm/include/asm/armv8/mmu.h
+++ b/arch/arm/include/asm/armv8/mmu.h
@@ -53,6 +53,7 @@
 #define PTE_TYPE_FAULT		(0 << 0)
 #define PTE_TYPE_TABLE		(3 << 0)
 #define PTE_TYPE_BLOCK		(1 << 0)
+#define PTE_TYPE_VALID		(1 << 0)
 
 #define PTE_TABLE_PXN		(1UL << 59)
 #define PTE_TABLE_XN		(1UL << 60)
@@ -77,6 +78,10 @@
  */
 #define PMD_ATTRINDX(t)		((t) << 2)
 #define PMD_ATTRINDX_MASK	(7 << 2)
+#define PMD_ATTRMASK		(PTE_BLOCK_PXN		| \
+				 PTE_BLOCK_UXN		| \
+				 PMD_ATTRINDX_MASK	| \
+				 PTE_TYPE_VALID)
 
 /*
  * TCR flags.
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
index 766e929..9c3261c 100644
--- a/arch/arm/include/asm/system.h
+++ b/arch/arm/include/asm/system.h
@@ -226,6 +226,7 @@
 void smp_kick_all_cpus(void);
 
 void flush_l3_cache(void);
+void mmu_change_region_attr(phys_addr_t start, size_t size, u64 attrs);
 
 /*
  *Issue a secure monitor call in accordance with ARM "SMC Calling convention",