arm64: mm: print out correct page table entries
authorKristina Martsenko <kristina.martsenko@arm.com>
Fri, 9 Jun 2017 15:35:52 +0000 (16:35 +0100)
committerWill Deacon <will.deacon@arm.com>
Mon, 12 Jun 2017 11:33:37 +0000 (12:33 +0100)
When we take a fault that can't be handled, we print out the page table
entries associated with the faulting address. In some cases we currently
print out the wrong entries. For a faulting TTBR1 address, we sometimes
print out TTBR0 table entries instead, and for a faulting TTBR0 address
we sometimes print out TTBR1 table entries. Fix this by choosing the
tables based on the faulting address.

Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
[will: zero-extend addrs to 64-bit, don't walk swapper w/ TTBR0 addr]
Signed-off-by: Will Deacon <will.deacon@arm.com>
arch/arm64/include/asm/system_misc.h
arch/arm64/mm/fault.c

index bc812435bc76278080851920744defcc1d78e270..d0beefeb6d25811797d852eee212888fa0bf087d 100644 (file)
@@ -40,7 +40,7 @@ void hook_debug_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
                           int sig, int code, const char *name);
 
 struct mm_struct;
-extern void show_pte(struct mm_struct *mm, unsigned long addr);
+extern void show_pte(unsigned long addr);
 extern void __show_regs(struct pt_regs *);
 
 extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
index c3f2b1048f83b9c4764b9673434ea3ba4eea3bae..9d27b1720c524af2b8c628b3dd38256512098367 100644 (file)
@@ -80,18 +80,33 @@ static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr)
 #endif
 
 /*
- * Dump out the page tables associated with 'addr' in mm 'mm'.
+ * Dump out the page tables associated with 'addr' in the currently active mm.
  */
-void show_pte(struct mm_struct *mm, unsigned long addr)
+void show_pte(unsigned long addr)
 {
+       struct mm_struct *mm;
        pgd_t *pgd;
 
-       if (!mm)
+       if (addr < TASK_SIZE) {
+               /* TTBR0 */
+               mm = current->active_mm;
+               if (mm == &init_mm) {
+                       pr_alert("[%016lx] user address but active_mm is swapper\n",
+                                addr);
+                       return;
+               }
+       } else if (addr >= VA_START) {
+               /* TTBR1 */
                mm = &init_mm;
+       } else {
+               pr_alert("[%016lx] address between user and kernel address ranges\n",
+                        addr);
+               return;
+       }
 
        pr_alert("pgd = %p\n", mm->pgd);
        pgd = pgd_offset(mm, addr);
-       pr_alert("[%08lx] *pgd=%016llx", addr, pgd_val(*pgd));
+       pr_alert("[%016lx] *pgd=%016llx", addr, pgd_val(*pgd));
 
        do {
                pud_t *pud;
@@ -196,8 +211,8 @@ static inline bool is_permission_fault(unsigned int esr, struct pt_regs *regs,
 /*
  * The kernel tried to access some page that wasn't present.
  */
-static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr,
-                             unsigned int esr, struct pt_regs *regs)
+static void __do_kernel_fault(unsigned long addr, unsigned int esr,
+                             struct pt_regs *regs)
 {
        const char *msg;
 
@@ -227,7 +242,7 @@ static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr,
        pr_alert("Unable to handle kernel %s at virtual address %08lx\n", msg,
                 addr);
 
-       show_pte(mm, addr);
+       show_pte(addr);
        die("Oops", regs, esr);
        bust_spinlocks(0);
        do_exit(SIGKILL);
@@ -249,7 +264,7 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
                pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n",
                        tsk->comm, task_pid_nr(tsk), inf->name, sig,
                        addr, esr);
-               show_pte(tsk->mm, addr);
+               show_pte(addr);
                __show_regs(regs);
        }
 
@@ -265,7 +280,6 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
 static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
 {
        struct task_struct *tsk = current;
-       struct mm_struct *mm = tsk->active_mm;
        const struct fault_info *inf;
 
        /*
@@ -276,7 +290,7 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
                inf = esr_to_fault_info(esr);
                __do_user_fault(tsk, addr, esr, inf->sig, inf->code, regs);
        } else
-               __do_kernel_fault(mm, addr, esr, regs);
+               __do_kernel_fault(addr, esr, regs);
 }
 
 #define VM_FAULT_BADMAP                0x010000
@@ -475,7 +489,7 @@ retry:
        return 0;
 
 no_context:
-       __do_kernel_fault(mm, addr, esr, regs);
+       __do_kernel_fault(addr, esr, regs);
        return 0;
 }