Blackfin: improve async bank access checking (for cross-banks & XIP)
authorBernd Schmidt <bernds_cb1@t-online.de>
Wed, 23 Sep 2009 16:47:16 +0000 (16:47 +0000)
committerMike Frysinger <vapier@gentoo.org>
Tue, 15 Dec 2009 05:13:52 +0000 (00:13 -0500)
The access_ok() function did not accept ranges within the async banks
which made it impossible to do XIP in flash.  Fixing that also showed
that the current bfin_mem_access_type() code did not work with accesses
that spanned async banks (like a file system).  So split out and fix the
async bank checks so that all these scenarios work as expected.

Signed-off-by: Bernd Schmidt <bernds_cb1@t-online.de>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
arch/blackfin/kernel/process.c

index 4587642..a577238 100644 (file)
@@ -332,12 +332,58 @@ int in_mem_const(unsigned long addr, unsigned long size,
 {
        return in_mem_const_off(addr, size, 0, const_addr, const_size);
 }
-#define IN_ASYNC(bnum, bctlnum) \
+#define ASYNC_ENABLED(bnum, bctlnum) \
 ({ \
-       (bfin_read_EBIU_AMGCTL() & 0xe) < ((bnum + 1) << 1) ? -EFAULT : \
-       bfin_read_EBIU_AMBCTL##bctlnum() & B##bnum##RDYEN ? -EFAULT : \
-       BFIN_MEM_ACCESS_CORE; \
+       (bfin_read_EBIU_AMGCTL() & 0xe) < ((bnum + 1) << 1) ? 0 : \
+       bfin_read_EBIU_AMBCTL##bctlnum() & B##bnum##RDYEN ? 0 : \
+       1; \
 })
+/*
+ * We can't read EBIU banks that aren't enabled or we end up hanging
+ * on the access to the async space.  Make sure we validate accesses
+ * that cross async banks too.
+ *     0 - found, but unusable
+ *     1 - found & usable
+ *     2 - not found
+ */
+static
+int in_async(unsigned long addr, unsigned long size)
+{
+       if (addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK0_BASE + ASYNC_BANK0_SIZE) {
+               if (!ASYNC_ENABLED(0, 0))
+                       return 0;
+               if (addr + size <= ASYNC_BANK0_BASE + ASYNC_BANK0_SIZE)
+                       return 1;
+               size -= ASYNC_BANK0_BASE + ASYNC_BANK0_SIZE - addr;
+               addr = ASYNC_BANK0_BASE + ASYNC_BANK0_SIZE;
+       }
+       if (addr >= ASYNC_BANK1_BASE && addr < ASYNC_BANK1_BASE + ASYNC_BANK1_SIZE) {
+               if (!ASYNC_ENABLED(1, 0))
+                       return 0;
+               if (addr + size <= ASYNC_BANK1_BASE + ASYNC_BANK1_SIZE)
+                       return 1;
+               size -= ASYNC_BANK1_BASE + ASYNC_BANK1_SIZE - addr;
+               addr = ASYNC_BANK1_BASE + ASYNC_BANK1_SIZE;
+       }
+       if (addr >= ASYNC_BANK2_BASE && addr < ASYNC_BANK2_BASE + ASYNC_BANK2_SIZE) {
+               if (!ASYNC_ENABLED(2, 1))
+                       return 0;
+               if (addr + size <= ASYNC_BANK2_BASE + ASYNC_BANK2_SIZE)
+                       return 1;
+               size -= ASYNC_BANK2_BASE + ASYNC_BANK2_SIZE - addr;
+               addr = ASYNC_BANK2_BASE + ASYNC_BANK2_SIZE;
+       }
+       if (addr >= ASYNC_BANK3_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) {
+               if (ASYNC_ENABLED(3, 1))
+                       return 0;
+               if (addr + size <= ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE)
+                       return 1;
+               return 0;
+       }
+
+       /* not within async bounds */
+       return 2;
+}
 
 int bfin_mem_access_type(unsigned long addr, unsigned long size)
 {
@@ -374,17 +420,11 @@ int bfin_mem_access_type(unsigned long addr, unsigned long size)
        if (addr >= SYSMMR_BASE)
                return BFIN_MEM_ACCESS_CORE_ONLY;
 
-       /* We can't read EBIU banks that aren't enabled or we end up hanging
-        * on the access to the async space.
-        */
-       if (in_mem_const(addr, size, ASYNC_BANK0_BASE, ASYNC_BANK0_SIZE))
-               return IN_ASYNC(0, 0);
-       if (in_mem_const(addr, size, ASYNC_BANK1_BASE, ASYNC_BANK1_SIZE))
-               return IN_ASYNC(1, 0);
-       if (in_mem_const(addr, size, ASYNC_BANK2_BASE, ASYNC_BANK2_SIZE))
-               return IN_ASYNC(2, 1);
-       if (in_mem_const(addr, size, ASYNC_BANK3_BASE, ASYNC_BANK3_SIZE))
-               return IN_ASYNC(3, 1);
+       switch (in_async(addr, size)) {
+       case 0: return -EFAULT;
+       case 1: return BFIN_MEM_ACCESS_CORE;
+       case 2: /* fall through */;
+       }
 
        if (in_mem_const(addr, size, BOOT_ROM_START, BOOT_ROM_LENGTH))
                return BFIN_MEM_ACCESS_CORE;
@@ -401,6 +441,8 @@ __attribute__((l1_text))
 /* Return 1 if access to memory range is OK, 0 otherwise */
 int _access_ok(unsigned long addr, unsigned long size)
 {
+       int aret;
+
        if (size == 0)
                return 1;
        /* Check that things do not wrap around */
@@ -450,6 +492,11 @@ int _access_ok(unsigned long addr, unsigned long size)
        if (in_mem_const(addr, size, COREB_L1_DATA_B_START, COREB_L1_DATA_B_LENGTH))
                return 1;
 #endif
+
+       aret = in_async(addr, size);
+       if (aret < 2)
+               return aret;
+
        if (in_mem_const_off(addr, size, _ebss_l2 - _stext_l2, L2_START, L2_LENGTH))
                return 1;