ARM: show short message on segfault
[pandora-kernel.git] / arch / arm / mm / fault.c
index aa33949..ea34eb9 100644 (file)
 
 #include "fault.h"
 
-/*
- * Fault status register encodings.  We steal bit 31 for our own purposes.
- */
-#define FSR_LNX_PF             (1 << 31)
-#define FSR_WRITE              (1 << 11)
-#define FSR_FS4                        (1 << 10)
-#define FSR_FS3_0              (15)
-
-static inline int fsr_fs(unsigned int fsr)
-{
-       return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6;
-}
-
 #ifdef CONFIG_MMU
 
 #ifdef CONFIG_KPROBES
@@ -123,8 +110,10 @@ void show_pte(struct mm_struct *mm, unsigned long addr)
 
                pte = pte_offset_map(pmd, addr);
                printk(", *pte=%08llx", (long long)pte_val(*pte));
+#ifndef CONFIG_ARM_LPAE
                printk(", *ppte=%08llx",
                       (long long)pte_val(pte[PTE_HWTABLE_PTRS]));
+#endif
                pte_unmap(pte);
        } while(0);
 
@@ -163,6 +152,39 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
        do_exit(SIGKILL);
 }
 
+#ifdef CONFIG_DEBUG_USER
+static void
+print_user_faulter_location(const char *name, struct pt_regs *regs)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       char *p, *t, buf[128];
+
+       printk(KERN_DEBUG "%s: pc=%08lx",
+              name, regs->ARM_pc);
+
+       do {
+               if (!mm)
+                       break;
+               vma = find_vma(mm, regs->ARM_pc);
+               if (!vma || !vma->vm_file)
+                       break;
+
+               p = d_path(&vma->vm_file->f_path, buf, sizeof(buf));
+               if (IS_ERR(p))
+                       break;
+
+               t = strrchr(p, '/');
+               if (t)
+                       p = t + 1;
+
+               printk(KERN_CONT " (%s+%lx)", p, regs->ARM_pc - vma->vm_start);
+       } while (0);
+
+       printk(KERN_CONT ", lr=%08lx\n", regs->ARM_lr);
+}
+#endif
+
 /*
  * Something tried to access memory that isn't in our memory map..
  * User mode accesses just cause a SIGSEGV
@@ -175,9 +197,13 @@ __do_user_fault(struct task_struct *tsk, unsigned long addr,
        struct siginfo si;
 
 #ifdef CONFIG_DEBUG_USER
+       if (user_debug & (UDBG_SEGV | UDBG_SEGV_SHORT)) {
+               printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x (%s)\n",
+                      tsk->comm, sig, addr, fsr,
+                      (fsr & FSR_WRITE) ? "write" : "read");
+               print_user_faulter_location(tsk->comm, regs);
+       }
        if (user_debug & UDBG_SEGV) {
-               printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
-                      tsk->comm, sig, addr, fsr);
                show_pte(tsk->mm, addr);
                show_regs(regs);
        }
@@ -267,7 +293,9 @@ good_area:
        return fault;
 
 check_stack:
-       if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
+       /* Don't allow expansion below FIRST_USER_ADDRESS */
+       if (vma->vm_flags & VM_GROWSDOWN &&
+           addr >= FIRST_USER_ADDRESS && !expand_stack(vma, addr))
                goto good_area;
 out:
        return fault;
@@ -441,6 +469,12 @@ do_translation_fault(unsigned long addr, unsigned int fsr,
        pmd = pmd_offset(pud, addr);
        pmd_k = pmd_offset(pud_k, addr);
 
+#ifdef CONFIG_ARM_LPAE
+       /*
+        * Only one hardware entry per PMD with LPAE.
+        */
+       index = 0;
+#else
        /*
         * On ARM one Linux PGD entry contains two hardware entries (see page
         * tables layout in pgtable.h). We normally guarantee that we always
@@ -450,6 +484,7 @@ do_translation_fault(unsigned long addr, unsigned int fsr,
         * for the first of pair.
         */
        index = (addr >> SECTION_SHIFT) & 1;
+#endif
        if (pmd_none(pmd_k[index]))
                goto bad_area;
 
@@ -469,17 +504,6 @@ do_translation_fault(unsigned long addr, unsigned int fsr,
 }
 #endif                                 /* CONFIG_MMU */
 
-/*
- * Some section permission faults need to be handled gracefully.
- * They can happen due to a __{get,put}_user during an oops.
- */
-static int
-do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
-{
-       do_bad_area(addr, fsr, regs);
-       return 0;
-}
-
 /*
  * This abort handler always returns "fault".
  */
@@ -489,55 +513,20 @@ do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
        return 1;
 }
 
-static struct fsr_info {
+struct fsr_info {
        int     (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
        int     sig;
        int     code;
        const char *name;
-} fsr_info[] = {
-       /*
-        * The following are the standard ARMv3 and ARMv4 aborts.  ARMv5
-        * defines these to be "precise" aborts.
-        */
-       { do_bad,               SIGSEGV, 0,             "vector exception"                 },
-       { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment exception"              },
-       { do_bad,               SIGKILL, 0,             "terminal exception"               },
-       { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment exception"              },
-       { do_bad,               SIGBUS,  0,             "external abort on linefetch"      },
-       { 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"           },
-       { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section domain fault"             },
-       { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page domain fault"                },
-       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
-       { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "section permission fault"         },
-       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
-       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "page permission fault"            },
-       /*
-        * The following are "imprecise" aborts, which are signalled by bit
-        * 10 of the FSR, and may not be recoverable.  These are only
-        * supported if the CPU abort handler supports bit 10.
-        */
-       { do_bad,               SIGBUS,  0,             "unknown 16"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 17"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 18"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 19"                       },
-       { do_bad,               SIGBUS,  0,             "lock abort"                       }, /* xscale */
-       { do_bad,               SIGBUS,  0,             "unknown 21"                       },
-       { do_bad,               SIGBUS,  BUS_OBJERR,    "imprecise external abort"         }, /* xscale */
-       { do_bad,               SIGBUS,  0,             "unknown 23"                       },
-       { do_bad,               SIGBUS,  0,             "dcache parity error"              }, /* xscale */
-       { do_bad,               SIGBUS,  0,             "unknown 25"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 26"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 27"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 28"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 29"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 30"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 31"                       }
 };
 
+/* FSR definition */
+#ifdef CONFIG_ARM_LPAE
+#include "fsr-3level.c"
+#else
+#include "fsr-2level.c"
+#endif
+
 void __init
 hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *),
                int sig, int code, const char *name)
@@ -573,42 +562,6 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
        arm_notify_die("", regs, &info, fsr, 0);
 }
 
-
-static struct fsr_info ifsr_info[] = {
-       { do_bad,               SIGBUS,  0,             "unknown 0"                        },
-       { do_bad,               SIGBUS,  0,             "unknown 1"                        },
-       { do_bad,               SIGBUS,  0,             "debug event"                      },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section access flag fault"        },
-       { do_bad,               SIGBUS,  0,             "unknown 4"                        },
-       { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "section translation fault"        },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page access flag fault"           },
-       { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "page translation fault"           },
-       { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section domain fault"             },
-       { do_bad,               SIGBUS,  0,             "unknown 10"                       },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page domain fault"                },
-       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
-       { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "section permission fault"         },
-       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
-       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "page permission fault"            },
-       { do_bad,               SIGBUS,  0,             "unknown 16"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 17"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 18"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 19"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 20"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 21"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 22"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 23"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 24"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 25"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 26"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 27"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 28"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 29"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 30"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 31"                       },
-};
-
 void __init
 hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *),
                 int sig, int code, const char *name)
@@ -641,6 +594,7 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
        arm_notify_die("", regs, &info, ifsr, 0);
 }
 
+#ifndef CONFIG_ARM_LPAE
 static int __init exceptions_init(void)
 {
        if (cpu_architecture() >= CPU_ARCH_ARMv6) {
@@ -663,3 +617,4 @@ static int __init exceptions_init(void)
 }
 
 arch_initcall(exceptions_init);
+#endif