Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzi...
[sfrench/cifs-2.6.git] / arch / arm / mm / fault.c
index cbfb2edcf7d12a3a1cae18f04c041d94dc055bcc..23b0b03af5ea84b8a01e10c59a97091d92f4618b 100644 (file)
@@ -413,7 +413,16 @@ do_translation_fault(unsigned long addr, unsigned int fsr,
        pmd_k = pmd_offset(pgd_k, addr);
        pmd   = pmd_offset(pgd, addr);
 
-       if (pmd_none(*pmd_k))
+       /*
+        * On ARM one Linux PGD entry contains two hardware entries (see page
+        * tables layout in pgtable.h). We normally guarantee that we always
+        * fill both L1 entries. But create_mapping() doesn't follow the rule.
+        * It can create inidividual L1 entries, so here we have to call
+        * pmd_none() check for the entry really corresponded to address, not
+        * for the first of pair.
+        */
+       index = (addr >> SECTION_SHIFT) & 1;
+       if (pmd_none(pmd_k[index]))
                goto bad_area;
 
        copy_pmd(pmd, pmd_k);
@@ -463,15 +472,10 @@ static struct fsr_info {
         * defines these to be "precise" aborts.
         */
        { do_bad,               SIGSEGV, 0,             "vector exception"                 },
-       { do_bad,               SIGILL,  BUS_ADRALN,    "alignment exception"              },
+       { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment exception"              },
        { do_bad,               SIGKILL, 0,             "terminal exception"               },
-       { do_bad,               SIGILL,  BUS_ADRALN,    "alignment exception"              },
-/* Do we need runtime check ? */
-#if __LINUX_ARM_ARCH__ < 6
+       { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment exception"              },
        { do_bad,               SIGBUS,  0,             "external abort on linefetch"      },
-#else
-       { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "I-cache maintenance fault"        },
-#endif
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "section translation fault"        },
        { do_bad,               SIGBUS,  0,             "external abort on linefetch"      },
        { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "page translation fault"           },
@@ -508,13 +512,15 @@ static struct fsr_info {
 
 void __init
 hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *),
-               int sig, const char *name)
+               int sig, int code, const char *name)
 {
-       if (nr >= 0 && nr < ARRAY_SIZE(fsr_info)) {
-               fsr_info[nr].fn   = fn;
-               fsr_info[nr].sig  = sig;
-               fsr_info[nr].name = name;
-       }
+       if (nr < 0 || nr >= ARRAY_SIZE(fsr_info))
+               BUG();
+
+       fsr_info[nr].fn   = fn;
+       fsr_info[nr].sig  = sig;
+       fsr_info[nr].code = code;
+       fsr_info[nr].name = name;
 }
 
 /*
@@ -594,3 +600,25 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
        arm_notify_die("", regs, &info, ifsr, 0);
 }
 
+static int __init exceptions_init(void)
+{
+       if (cpu_architecture() >= CPU_ARCH_ARMv6) {
+               hook_fault_code(4, do_translation_fault, SIGSEGV, SEGV_MAPERR,
+                               "I-cache maintenance fault");
+       }
+
+       if (cpu_architecture() >= CPU_ARCH_ARMv7) {
+               /*
+                * TODO: Access flag faults introduced in ARMv6K.
+                * Runtime check for 'K' extension is needed
+                */
+               hook_fault_code(3, do_bad, SIGSEGV, SEGV_MAPERR,
+                               "section access flag fault");
+               hook_fault_code(6, do_bad, SIGSEGV, SEGV_MAPERR,
+                               "section access flag fault");
+       }
+
+       return 0;
+}
+
+arch_initcall(exceptions_init);