Commit 7495a2b5 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] PPC32: Get full register set on bad kernel accesses

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.
parent fff28794
...@@ -451,6 +451,29 @@ ppc_swapcontext: ...@@ -451,6 +451,29 @@ ppc_swapcontext:
stw r0,TRAP(r1) /* register set saved */ stw r0,TRAP(r1) /* register set saved */
b sys_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 * This routine switches between two different tasks. The process
* state of one is saved on its kernel stack. Then the state * state of one is saved on its kernel stack. Then the state
......
...@@ -412,9 +412,7 @@ DataAccess: ...@@ -412,9 +412,7 @@ DataAccess:
1: stw r10,_DSISR(r11) 1: stw r10,_DSISR(r11)
mr r5,r10 mr r5,r10
mfspr r4,DAR mfspr r4,DAR
stw r4,_DAR(r11) EXC_XFER_EE_LITE(0x300, handle_page_fault)
addi r3,r1,STACK_FRAME_OVERHEAD
EXC_XFER_EE_LITE(0x300, do_page_fault)
#ifdef CONFIG_PPC64BRIDGE #ifdef CONFIG_PPC64BRIDGE
/* SLB fault on data access. */ /* SLB fault on data access. */
...@@ -436,10 +434,9 @@ InstructionAccess: ...@@ -436,10 +434,9 @@ InstructionAccess:
li r3,0 /* into the hash table */ li r3,0 /* into the hash table */
mr r4,r12 /* SRR0 is fault address */ mr r4,r12 /* SRR0 is fault address */
bl hash_page bl hash_page
1: addi r3,r1,STACK_FRAME_OVERHEAD 1: mr r4,r12
mr r4,r12
mr r5,r9 mr r5,r9
EXC_XFER_EE_LITE(0x400, do_page_fault) EXC_XFER_EE_LITE(0x400, handle_page_fault)
#ifdef CONFIG_PPC64BRIDGE #ifdef CONFIG_PPC64BRIDGE
/* SLB fault on instruction access. */ /* SLB fault on instruction access. */
......
...@@ -92,8 +92,8 @@ static int store_updates_sp(struct pt_regs *regs) ...@@ -92,8 +92,8 @@ static int store_updates_sp(struct pt_regs *regs)
* the error_code parameter is ESR for a data fault, 0 for an instruction * the error_code parameter is ESR for a data fault, 0 for an instruction
* fault. * fault.
*/ */
void do_page_fault(struct pt_regs *regs, unsigned long address, int do_page_fault(struct pt_regs *regs, unsigned long address,
unsigned long error_code) unsigned long error_code)
{ {
struct vm_area_struct * vma; struct vm_area_struct * vma;
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
...@@ -119,21 +119,20 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, ...@@ -119,21 +119,20 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
if (debugger_fault_handler && TRAP(regs) == 0x300) { if (debugger_fault_handler && TRAP(regs) == 0x300) {
debugger_fault_handler(regs); debugger_fault_handler(regs);
return; return 0;
} }
#if !defined(CONFIG_4xx) #if !defined(CONFIG_4xx)
if (error_code & 0x00400000) { if (error_code & 0x00400000) {
/* DABR match */ /* DABR match */
if (debugger_dabr_match(regs)) if (debugger_dabr_match(regs))
return; return 0;
} }
#endif /* !CONFIG_4xx */ #endif /* !CONFIG_4xx */
#endif /* CONFIG_XMON || CONFIG_KGDB */ #endif /* CONFIG_XMON || CONFIG_KGDB */
if (in_atomic() || mm == NULL) { if (in_atomic() || mm == NULL)
bad_page_fault(regs, address, SIGSEGV); return SIGSEGV;
return;
}
down_read(&mm->mmap_sem); down_read(&mm->mmap_sem);
vma = find_vma(mm, address); vma = find_vma(mm, address);
if (!vma) if (!vma)
...@@ -229,7 +228,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, ...@@ -229,7 +228,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
_tlbie(address); _tlbie(address);
pte_unmap(ptep); pte_unmap(ptep);
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
return; return 0;
} }
if (ptep != NULL) if (ptep != NULL)
pte_unmap(ptep); pte_unmap(ptep);
...@@ -271,7 +270,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, ...@@ -271,7 +270,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
* -- Cort * -- Cort
*/ */
pte_misses++; pte_misses++;
return; return 0;
bad_area: bad_area:
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
...@@ -284,11 +283,10 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, ...@@ -284,11 +283,10 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
info.si_code = code; info.si_code = code;
info.si_addr = (void *) address; info.si_addr = (void *) address;
force_sig_info(SIGSEGV, &info, current); force_sig_info(SIGSEGV, &info, current);
return; return 0;
} }
bad_page_fault(regs, address, SIGSEGV); return SIGSEGV;
return;
/* /*
* We ran out of memory, or some other thing happened to us that made * We ran out of memory, or some other thing happened to us that made
...@@ -304,8 +302,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, ...@@ -304,8 +302,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
printk("VM: killing process %s\n", current->comm); printk("VM: killing process %s\n", current->comm);
if (user_mode(regs)) if (user_mode(regs))
do_exit(SIGKILL); do_exit(SIGKILL);
bad_page_fault(regs, address, SIGKILL); return SIGKILL;
return;
do_sigbus: do_sigbus:
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
...@@ -315,13 +312,14 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, ...@@ -315,13 +312,14 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
info.si_addr = (void *)address; info.si_addr = (void *)address;
force_sig_info (SIGBUS, &info, current); force_sig_info (SIGBUS, &info, current);
if (!user_mode(regs)) 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. * 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 * It is called from the DSI and ISI handlers in head.S and from some
* in traps.c. * of the procedures in traps.c.
*/ */
void void
bad_page_fault(struct pt_regs *regs, unsigned long address, int sig) bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
......
...@@ -82,7 +82,7 @@ extern void cvt_df(double *from, float *to, unsigned long *fpscr); ...@@ -82,7 +82,7 @@ extern void cvt_df(double *from, float *to, unsigned long *fpscr);
extern int call_rtas(const char *, int, int, unsigned long *, ...); extern int call_rtas(const char *, int, int, unsigned long *, ...);
extern int abs(int); extern int abs(int);
extern void cacheable_memzero(void *p, unsigned int nb); 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 bad_page_fault(struct pt_regs *, unsigned long, int);
extern void die(const char *, struct pt_regs *, long); extern void die(const char *, struct pt_regs *, long);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment