Blackfin: fix MPU page permission masks overflow when dealing with async memory
authorBarry Song <barry.song@analog.com>
Mon, 7 Dec 2009 10:05:58 +0000 (10:05 +0000)
committerMike Frysinger <vapier@gentoo.org>
Tue, 9 Mar 2010 05:30:46 +0000 (00:30 -0500)
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>
arch/blackfin/include/asm/mmu_context.h
arch/blackfin/kernel/cplb-mpu/cplbmgr.c
arch/blackfin/kernel/setup.c

index ae8ef4ffd806d8078c0aec77525487ce1fbcf8df..7f363d7e43a53dc97b0f0207b129a3b5f9d08a19 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/page.h>
 #include <asm/pgalloc.h>
 #include <asm/cplbinit.h>
+#include <asm/sections.h>
 
 /* 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
@@ -117,9 +118,16 @@ static inline void protect_page(struct mm_struct *mm, unsigned long addr,
                                unsigned long flags)
 {
        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)
                mask[idx] |= bit;
index 930c01c0681348bd51f1ddc6a0d1e413cd9acad4..d4cc53a0ef89f136f70e1e5f945810d35490d133 100644 (file)
@@ -114,10 +114,15 @@ static noinline int dcplb_miss(unsigned int cpu)
                d_data = L2_DMEMORY;
        } else if (addr >= physical_mem_end) {
                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
                    && (status & (FAULT_RW | FAULT_USERSUPV)) == FAULT_USERSUPV) {
                        addr &= ~(1 * 1024 * 1024 - 1);
@@ -204,10 +209,19 @@ static noinline int icplb_miss(unsigned int cpu)
                i_data = L2_IMEMORY;
        } else if (addr >= physical_mem_end) {
                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
                    && (status & FAULT_USERSUPV)) {
                        addr &= ~(1 * 1024 * 1024 - 1);
index 69cbc1a812015071cbe80002b253b2f37dea9da7..8dc7ee1ef336c98f541d7ef9f4e35a20bcbfd08e 100644 (file)
@@ -597,7 +597,12 @@ static __init void memory_setup(void)
        }
 
 #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;
+#endif
        page_mask_order = get_order(3 * page_mask_nelts * sizeof(long));
 #endif