浏览代码

Blackfin: fix MPU page permission masks overflow when dealing with async memory

Attempting to use the MPU while doing XIP out of parallel flash hooked up
to the async memory bus would often result in random crashes as the MPU
slowly corrupted memory.

The fallout here is that the async banks gain MPU protection from user
space too.  So any accesses have to go through the mmap() interface rather
than just using hardcoded pointers.

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Barry Song 15 年之前
父节点
当前提交
e18e7dd334
共有 3 个文件被更改,包括 38 次插入11 次删除
  1. 11 3
      arch/blackfin/include/asm/mmu_context.h
  2. 22 8
      arch/blackfin/kernel/cplb-mpu/cplbmgr.c
  3. 5 0
      arch/blackfin/kernel/setup.c

+ 11 - 3
arch/blackfin/include/asm/mmu_context.h

@@ -13,6 +13,7 @@
 #include <asm/page.h>
 #include <asm/page.h>
 #include <asm/pgalloc.h>
 #include <asm/pgalloc.h>
 #include <asm/cplbinit.h>
 #include <asm/cplbinit.h>
+#include <asm/sections.h>
 
 
 /* Note: L1 stacks are CPU-private things, so we bluntly disable this
 /* Note: L1 stacks are CPU-private things, so we bluntly disable this
    feature in SMP mode, and use the per-CPU scratch SRAM bank only to
    feature in SMP mode, and use the per-CPU scratch SRAM bank only to
@@ -117,9 +118,16 @@ static inline void protect_page(struct mm_struct *mm, unsigned long addr,
 				unsigned long flags)
 				unsigned long flags)
 {
 {
 	unsigned long *mask = mm->context.page_rwx_mask;
 	unsigned long *mask = mm->context.page_rwx_mask;
-	unsigned long page = addr >> 12;
-	unsigned long idx = page >> 5;
-	unsigned long bit = 1 << (page & 31);
+	unsigned long page;
+	unsigned long idx;
+	unsigned long bit;
+
+	if (unlikely(addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE))
+		page = (addr - (ASYNC_BANK0_BASE - _ramend)) >> 12;
+	else
+		page = addr >> 12;
+	idx = page >> 5;
+	bit = 1 << (page & 31);
 
 
 	if (flags & VM_READ)
 	if (flags & VM_READ)
 		mask[idx] |= bit;
 		mask[idx] |= bit;

+ 22 - 8
arch/blackfin/kernel/cplb-mpu/cplbmgr.c

@@ -114,10 +114,15 @@ static noinline int dcplb_miss(unsigned int cpu)
 		d_data = L2_DMEMORY;
 		d_data = L2_DMEMORY;
 	} else if (addr >= physical_mem_end) {
 	} else if (addr >= physical_mem_end) {
 		if (addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) {
 		if (addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) {
-			addr &= ~(4 * 1024 * 1024 - 1);
-			d_data &= ~PAGE_SIZE_4KB;
-			d_data |= PAGE_SIZE_4MB;
-			d_data |= CPLB_USER_RD | CPLB_USER_WR;
+			mask = current_rwx_mask[cpu];
+			if (mask) {
+				int page = (addr - (ASYNC_BANK0_BASE - _ramend)) >> PAGE_SHIFT;
+				int idx = page >> 5;
+				int bit = 1 << (page & 31);
+
+				if (mask[idx] & bit)
+					d_data |= CPLB_USER_RD;
+			}
 		} else if (addr >= BOOT_ROM_START && addr < BOOT_ROM_START + BOOT_ROM_LENGTH
 		} else if (addr >= BOOT_ROM_START && addr < BOOT_ROM_START + BOOT_ROM_LENGTH
 		    && (status & (FAULT_RW | FAULT_USERSUPV)) == FAULT_USERSUPV) {
 		    && (status & (FAULT_RW | FAULT_USERSUPV)) == FAULT_USERSUPV) {
 			addr &= ~(1 * 1024 * 1024 - 1);
 			addr &= ~(1 * 1024 * 1024 - 1);
@@ -204,10 +209,19 @@ static noinline int icplb_miss(unsigned int cpu)
 		i_data = L2_IMEMORY;
 		i_data = L2_IMEMORY;
 	} else if (addr >= physical_mem_end) {
 	} else if (addr >= physical_mem_end) {
 		if (addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) {
 		if (addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) {
-			addr &= ~(4 * 1024 * 1024 - 1);
-			i_data &= ~PAGE_SIZE_4KB;
-			i_data |= PAGE_SIZE_4MB;
-			i_data |= CPLB_USER_RD;
+			if (!(status & FAULT_USERSUPV)) {
+				unsigned long *mask = current_rwx_mask[cpu];
+
+				if (mask) {
+					int page = (addr - (ASYNC_BANK0_BASE - _ramend)) >> PAGE_SHIFT;
+					int idx = page >> 5;
+					int bit = 1 << (page & 31);
+
+					mask += 2 * page_mask_nelts;
+					if (mask[idx] & bit)
+						i_data |= CPLB_USER_RD;
+				}
+			}
 		} else if (addr >= BOOT_ROM_START && addr < BOOT_ROM_START + BOOT_ROM_LENGTH
 		} else if (addr >= BOOT_ROM_START && addr < BOOT_ROM_START + BOOT_ROM_LENGTH
 		    && (status & FAULT_USERSUPV)) {
 		    && (status & FAULT_USERSUPV)) {
 			addr &= ~(1 * 1024 * 1024 - 1);
 			addr &= ~(1 * 1024 * 1024 - 1);

+ 5 - 0
arch/blackfin/kernel/setup.c

@@ -597,7 +597,12 @@ static __init void memory_setup(void)
 	}
 	}
 
 
 #ifdef CONFIG_MPU
 #ifdef CONFIG_MPU
+#if defined(CONFIG_ROMFS_ON_MTD) && defined(CONFIG_MTD_ROM)
+	page_mask_nelts = (((_ramend + ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE -
+					ASYNC_BANK0_BASE) >> PAGE_SHIFT) + 31) / 32;
+#else
 	page_mask_nelts = ((_ramend >> PAGE_SHIFT) + 31) / 32;
 	page_mask_nelts = ((_ramend >> PAGE_SHIFT) + 31) / 32;
+#endif
 	page_mask_order = get_order(3 * page_mask_nelts * sizeof(long));
 	page_mask_order = get_order(3 * page_mask_nelts * sizeof(long));
 #endif
 #endif