diff mbox

[1/2] arm64: mm: print out correct page table entries

Message ID 1495453554-9412-1-git-send-email-kristina.martsenko@arm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Kristina Martsenko May 22, 2017, 11:45 a.m. UTC
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.

Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
---
 arch/arm64/include/asm/system_misc.h |  2 +-
 arch/arm64/mm/fault.c                | 30 ++++++++++++++++++++----------
 2 files changed, 21 insertions(+), 11 deletions(-)

Comments

Will Deacon May 22, 2017, 12:18 p.m. UTC | #1
Hi Kristina,

On Mon, May 22, 2017 at 12:45:53PM +0100, Kristina Martsenko wrote:
> 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.
> 
> Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
> ---
>  arch/arm64/include/asm/system_misc.h |  2 +-
>  arch/arm64/mm/fault.c                | 30 ++++++++++++++++++++----------
>  2 files changed, 21 insertions(+), 11 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
> index bc812435bc76..d0beefeb6d25 100644
> --- a/arch/arm64/include/asm/system_misc.h
> +++ b/arch/arm64/include/asm/system_misc.h
> @@ -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);
> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
> index 37b95dff0b07..0c32f34fb4af 100644
> --- a/arch/arm64/mm/fault.c
> +++ b/arch/arm64/mm/fault.c
> @@ -80,14 +80,25 @@ 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)
>  {
> +	unsigned long ttbr_mask = ~0UL << VA_BITS;
> +	struct mm_struct *mm;
>  	pgd_t *pgd;
>  
> -	if (!mm)
> +	if (!(addr & ttbr_mask)) {
> +		/* TTBR0 */
> +		mm = current->active_mm;
> +	} else if ((addr & ttbr_mask) == ttbr_mask) {
> +		/* TTBR1 */
>  		mm = &init_mm;

I wonder if this would be better structured as:

	if (addr < TASK_SIZE)
		/* TTBR0 */
	else if (addr >= VA_START)
		/* TTBR1 */
	else
		/* no man's land */

what do you think?

> @@ -196,8 +207,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 +238,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);

Now that you're not passing the mm, __do_kernel_fault doesn't need it
for anything so there's further cleanup you can do here.

>  	die("Oops", regs, esr);
>  	bust_spinlocks(0);
>  	do_exit(SIGKILL);
> @@ -249,7 +260,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 +276,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 +286,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);

Similar thing here: I don't think the mm local variable is used anymore.
Does GCC not warn about this?

Will
Kristina Martsenko May 31, 2017, 7:37 p.m. UTC | #2
Hi Will,

On 22/05/17 13:18, Will Deacon wrote:
> On Mon, May 22, 2017 at 12:45:53PM +0100, Kristina Martsenko wrote:
>> 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.
>>
>> Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
>> ---
>>  arch/arm64/include/asm/system_misc.h |  2 +-
>>  arch/arm64/mm/fault.c                | 30 ++++++++++++++++++++----------
>>  2 files changed, 21 insertions(+), 11 deletions(-)
>>
>> diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
>> index bc812435bc76..d0beefeb6d25 100644
>> --- a/arch/arm64/include/asm/system_misc.h
>> +++ b/arch/arm64/include/asm/system_misc.h
>> @@ -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);
>> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
>> index 37b95dff0b07..0c32f34fb4af 100644
>> --- a/arch/arm64/mm/fault.c
>> +++ b/arch/arm64/mm/fault.c
>> @@ -80,14 +80,25 @@ 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)
>>  {
>> +	unsigned long ttbr_mask = ~0UL << VA_BITS;
>> +	struct mm_struct *mm;
>>  	pgd_t *pgd;
>>  
>> -	if (!mm)
>> +	if (!(addr & ttbr_mask)) {
>> +		/* TTBR0 */
>> +		mm = current->active_mm;
>> +	} else if ((addr & ttbr_mask) == ttbr_mask) {
>> +		/* TTBR1 */
>>  		mm = &init_mm;
> 
> I wonder if this would be better structured as:
> 
> 	if (addr < TASK_SIZE)
> 		/* TTBR0 */
> 	else if (addr >= VA_START)
> 		/* TTBR1 */
> 	else
> 		/* no man's land */
> 
> what do you think?

I agree this is better, I'll change it in v2.

>> @@ -196,8 +207,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 +238,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);
> 
> Now that you're not passing the mm, __do_kernel_fault doesn't need it
> for anything so there's further cleanup you can do here.

This patch already removes the mm from __do_kernel_fault.

>>  	die("Oops", regs, esr);
>>  	bust_spinlocks(0);
>>  	do_exit(SIGKILL);
>> @@ -249,7 +260,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 +276,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 +286,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);
> 
> Similar thing here: I don't think the mm local variable is used anymore.
> Does GCC not warn about this?

Same here - this patch already removes the mm variable.

Thanks for the review,
Kristina
diff mbox

Patch

diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
index bc812435bc76..d0beefeb6d25 100644
--- a/arch/arm64/include/asm/system_misc.h
+++ b/arch/arm64/include/asm/system_misc.h
@@ -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);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 37b95dff0b07..0c32f34fb4af 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -80,14 +80,25 @@  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)
 {
+	unsigned long ttbr_mask = ~0UL << VA_BITS;
+	struct mm_struct *mm;
 	pgd_t *pgd;
 
-	if (!mm)
+	if (!(addr & ttbr_mask)) {
+		/* TTBR0 */
+		mm = current->active_mm;
+	} else if ((addr & ttbr_mask) == ttbr_mask) {
+		/* TTBR1 */
 		mm = &init_mm;
+	} else {
+		/* Between TTBR0 and TTBR1, no table to walk */
+		pr_alert("[%08lx] address between TTBR ranges\n", addr);
+		return;
+	}
 
 	pr_alert("pgd = %p\n", mm->pgd);
 	pgd = pgd_offset(mm, addr);
@@ -196,8 +207,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 +238,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 +260,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 +276,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 +286,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 +485,7 @@  static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
 	return 0;
 
 no_context:
-	__do_kernel_fault(mm, addr, esr, regs);
+	__do_kernel_fault(addr, esr, regs);
 	return 0;
 }