|
@@ -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 */
|
|
|
|
|
|
/*
|