From: Paul Mackerras 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); _