
From: Paul Mackerras <paulus@samba.org>

At present on ppc32, if the kernel accesses a bad address and causes an
oops, or drops into the xmon debugger, we only have the contents of the
volatile registers available to print.  The reason is that we only save the
volatile registers on entry for a page fault.

This patch restructures the code a bit so that if do_page_fault()
determines that the page fault is caused by a bad kernel access, it returns
to the caller, which then saves the full register set into the exception
frame before calling bad_page_fault().  This way we get the full set of
registers printed in the oops message.


---

 25-akpm/arch/ppc/kernel/entry.S  |   23 +++++++++++++++++++++++
 25-akpm/arch/ppc/kernel/head.S   |    9 +++------
 25-akpm/arch/ppc/mm/fault.c      |   34 ++++++++++++++++------------------
 25-akpm/include/asm-ppc/system.h |    2 +-
 4 files changed, 43 insertions(+), 25 deletions(-)

diff -puN arch/ppc/kernel/entry.S~ppc32-get-full-register-set-on-bad-kernel-accesses arch/ppc/kernel/entry.S
--- 25/arch/ppc/kernel/entry.S~ppc32-get-full-register-set-on-bad-kernel-accesses	2004-05-18 21:57:51.131078008 -0700
+++ 25-akpm/arch/ppc/kernel/entry.S	2004-05-18 21:57:51.139076792 -0700
@@ -452,6 +452,29 @@ ppc_swapcontext:
 	b	sys_swapcontext
 
 /*
+ * Top-level page fault handling.
+ * This is in assembler because if do_page_fault tells us that
+ * it is a bad kernel page fault, we want to save the non-volatile
+ * registers before calling bad_page_fault.
+ */
+	.globl	handle_page_fault
+handle_page_fault:
+	stw	r4,_DAR(r1)
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bl	do_page_fault
+	cmpwi	r3,0
+	beq+	ret_from_except
+	SAVE_NVGPRS(r1)
+	lwz	r0,TRAP(r1)
+	clrrwi	r0,r0,1
+	stw	r0,TRAP(r1)
+	mr	r5,r3
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	lwz	r4,_DAR(r1)
+	bl	bad_page_fault
+	b	ret_from_except_full
+
+/*
  * This routine switches between two different tasks.  The process
  * state of one is saved on its kernel stack.  Then the state
  * of the other is restored from its kernel stack.  The memory
diff -puN arch/ppc/kernel/head.S~ppc32-get-full-register-set-on-bad-kernel-accesses arch/ppc/kernel/head.S
--- 25/arch/ppc/kernel/head.S~ppc32-get-full-register-set-on-bad-kernel-accesses	2004-05-18 21:57:51.133077704 -0700
+++ 25-akpm/arch/ppc/kernel/head.S	2004-05-18 21:57:51.141076488 -0700
@@ -412,9 +412,7 @@ DataAccess:
 1:	stw	r10,_DSISR(r11)
 	mr	r5,r10
 	mfspr	r4,DAR
-	stw	r4,_DAR(r11)
-	addi	r3,r1,STACK_FRAME_OVERHEAD
-	EXC_XFER_EE_LITE(0x300, do_page_fault)
+	EXC_XFER_EE_LITE(0x300, handle_page_fault)
 
 #ifdef CONFIG_PPC64BRIDGE
 /* SLB fault on data access. */
@@ -436,10 +434,9 @@ InstructionAccess:
 	li	r3,0			/* into the hash table */
 	mr	r4,r12			/* SRR0 is fault address */
 	bl	hash_page
-1:	addi	r3,r1,STACK_FRAME_OVERHEAD
-	mr	r4,r12
+1:	mr	r4,r12
 	mr	r5,r9
-	EXC_XFER_EE_LITE(0x400, do_page_fault)
+	EXC_XFER_EE_LITE(0x400, handle_page_fault)
 
 #ifdef CONFIG_PPC64BRIDGE
 /* SLB fault on instruction access. */
diff -puN arch/ppc/mm/fault.c~ppc32-get-full-register-set-on-bad-kernel-accesses arch/ppc/mm/fault.c
--- 25/arch/ppc/mm/fault.c~ppc32-get-full-register-set-on-bad-kernel-accesses	2004-05-18 21:57:51.134077552 -0700
+++ 25-akpm/arch/ppc/mm/fault.c	2004-05-18 21:57:51.141076488 -0700
@@ -92,8 +92,8 @@ static int store_updates_sp(struct pt_re
  * the error_code parameter is ESR for a data fault, 0 for an instruction
  * fault.
  */
-void do_page_fault(struct pt_regs *regs, unsigned long address,
-		   unsigned long error_code)
+int do_page_fault(struct pt_regs *regs, unsigned long address,
+		  unsigned long error_code)
 {
 	struct vm_area_struct * vma;
 	struct mm_struct *mm = current->mm;
@@ -119,21 +119,20 @@ void do_page_fault(struct pt_regs *regs,
 #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
 	if (debugger_fault_handler && TRAP(regs) == 0x300) {
 		debugger_fault_handler(regs);
-		return;
+		return 0;
 	}
 #if !defined(CONFIG_4xx)
 	if (error_code & 0x00400000) {
 		/* DABR match */
 		if (debugger_dabr_match(regs))
-			return;
+			return 0;
 	}
 #endif /* !CONFIG_4xx */
 #endif /* CONFIG_XMON || CONFIG_KGDB */
 
-	if (in_atomic() || mm == NULL) {
-		bad_page_fault(regs, address, SIGSEGV);
-		return;
-	}
+	if (in_atomic() || mm == NULL)
+		return SIGSEGV;
+
 	down_read(&mm->mmap_sem);
 	vma = find_vma(mm, address);
 	if (!vma)
@@ -229,7 +228,7 @@ good_area:
 			_tlbie(address);
 			pte_unmap(ptep);
 			up_read(&mm->mmap_sem);
-			return;
+			return 0;
 		}
 		if (ptep != NULL)
 			pte_unmap(ptep);
@@ -271,7 +270,7 @@ good_area:
 	 * -- Cort
 	 */
 	pte_misses++;
-	return;
+	return 0;
 
 bad_area:
 	up_read(&mm->mmap_sem);
@@ -284,11 +283,10 @@ bad_area:
 		info.si_code = code;
 		info.si_addr = (void *) address;
 		force_sig_info(SIGSEGV, &info, current);
-		return;
+		return 0;
 	}
 
-	bad_page_fault(regs, address, SIGSEGV);
-	return;
+	return SIGSEGV;
 
 /*
  * We ran out of memory, or some other thing happened to us that made
@@ -304,8 +302,7 @@ out_of_memory:
 	printk("VM: killing process %s\n", current->comm);
 	if (user_mode(regs))
 		do_exit(SIGKILL);
-	bad_page_fault(regs, address, SIGKILL);
-	return;
+	return SIGKILL;
 
 do_sigbus:
 	up_read(&mm->mmap_sem);
@@ -315,13 +312,14 @@ do_sigbus:
 	info.si_addr = (void *)address;
 	force_sig_info (SIGBUS, &info, current);
 	if (!user_mode(regs))
-		bad_page_fault(regs, address, SIGBUS);
+		return SIGBUS;
+	return 0;
 }
 
 /*
  * bad_page_fault is called when we have a bad access from the kernel.
- * It is called from do_page_fault above and from some of the procedures
- * in traps.c.
+ * It is called from the DSI and ISI handlers in head.S and from some
+ * of the procedures in traps.c.
  */
 void
 bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
diff -puN include/asm-ppc/system.h~ppc32-get-full-register-set-on-bad-kernel-accesses include/asm-ppc/system.h
--- 25/include/asm-ppc/system.h~ppc32-get-full-register-set-on-bad-kernel-accesses	2004-05-18 21:57:51.136077248 -0700
+++ 25-akpm/include/asm-ppc/system.h	2004-05-18 21:57:51.142076336 -0700
@@ -82,7 +82,7 @@ extern void cvt_df(double *from, float *
 extern int call_rtas(const char *, int, int, unsigned long *, ...);
 extern int abs(int);
 extern void cacheable_memzero(void *p, unsigned int nb);
-extern void do_page_fault(struct pt_regs *, unsigned long, unsigned long);
+extern int do_page_fault(struct pt_regs *, unsigned long, unsigned long);
 extern void bad_page_fault(struct pt_regs *, unsigned long, int);
 extern void die(const char *, struct pt_regs *, long);
 

_
