summaryrefslogtreecommitdiffstats
path: root/arch/ppc/mm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ppc/mm')
-rw-r--r--arch/ppc/mm/fault.c93
-rw-r--r--arch/ppc/mm/init.c10
2 files changed, 82 insertions, 21 deletions
diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c
index 076ee56e5..7a899252f 100644
--- a/arch/ppc/mm/fault.c
+++ b/arch/ppc/mm/fault.c
@@ -62,10 +62,21 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
{
struct vm_area_struct * vma;
struct mm_struct *mm = current->mm;
+ siginfo_t info;
+ int code = SEGV_MAPERR;
#if defined(CONFIG_4xx)
int is_write = error_code & ESR_DST;
#else
int is_write = error_code & 0x02000000;
+
+ /*
+ * Fortunately the bit assignments in SRR1 for an instruction
+ * fault and DSISR for a data fault are mostly the same for the
+ * bits we are interested in. But there are some bits which
+ * indicate errors in DSISR but can validly be set in SRR1.
+ */
+ if (regs->trap == 0x400)
+ error_code &= 0x48200000;
#endif /* CONFIG_4xx */
#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
@@ -82,16 +93,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
#endif /* !CONFIG_4xx */
#endif /* CONFIG_XMON || CONFIG_KGDB */
- if (in_interrupt()) {
- static int complained;
- if (complained < 20) {
- ++complained;
- printk("page fault in interrupt handler, addr=%lx\n",
- address);
- show_regs(regs);
- }
- }
- if (current == NULL || mm == NULL) {
+ if (in_interrupt() || mm == NULL) {
bad_page_fault(regs, address);
return;
}
@@ -107,10 +109,12 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
goto bad_area;
good_area:
+ code = SEGV_ACCERR;
#if defined(CONFIG_6xx)
if (error_code & 0x95700000)
/* an error such as lwarx to I/O controller space,
address matching DABR, eciwx, etc. */
+ goto bad_area;
#endif /* CONFIG_6xx */
#if defined(CONFIG_8xx)
/* The MPC8xx seems to always set 0x80000000, which is
@@ -119,9 +123,8 @@ good_area:
*/
if (error_code & 0x10000000)
/* Guarded storage error. */
-#endif /* CONFIG_8xx */
goto bad_area;
-
+#endif /* CONFIG_8xx */
/* a write */
if (is_write) {
@@ -135,8 +138,25 @@ good_area:
if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
goto bad_area;
}
- if (!handle_mm_fault(mm, vma, address, is_write))
- goto bad_area;
+
+ /*
+ * If for any reason at all we couldn't handle the fault,
+ * make sure we exit gracefully rather than endlessly redo
+ * the fault.
+ */
+ switch (handle_mm_fault(mm, vma, address, is_write)) {
+ case 1:
+ current->min_flt++;
+ break;
+ case 2:
+ current->maj_flt++;
+ break;
+ case 0:
+ goto do_sigbus;
+ default:
+ goto out_of_memory;
+ }
+
up(&mm->mmap_sem);
/*
* keep track of tlb+htab misses that are good addrs but
@@ -147,22 +167,55 @@ good_area:
return;
bad_area:
-
up(&mm->mmap_sem);
pte_errors++;
+
+ /* User mode accesses cause a SIGSEGV */
+ if (user_mode(regs)) {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = code;
+ info.si_addr = (void *) address;
+ force_sig_info(SIGSEGV, &info, current);
+ return;
+ }
+
+ bad_page_fault(regs, address);
+ return;
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+ up(&mm->mmap_sem);
+ printk("VM: killing process %s\n", current->comm);
+ if (user_mode(regs))
+ do_exit(SIGKILL);
bad_page_fault(regs, address);
+ return;
+
+do_sigbus:
+ up(&mm->mmap_sem);
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRERR;
+ info.si_addr = (void *)address;
+ force_sig_info (SIGBUS, &info, current);
+ if (!user_mode(regs))
+ bad_page_fault(regs, address);
}
+/*
+ * 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.
+ */
void
bad_page_fault(struct pt_regs *regs, unsigned long address)
{
unsigned long fixup;
- if (user_mode(regs)) {
- force_sig(SIGSEGV, current);
- return;
- }
-
/* Are we prepared to handle this fault? */
if ((fixup = search_exception_table(regs->nip)) != 0) {
regs->nip = fixup;
diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c
index 80fb7575e..fc3acdd5c 100644
--- a/arch/ppc/mm/init.c
+++ b/arch/ppc/mm/init.c
@@ -83,6 +83,7 @@ extern char _start[], _end[];
extern char etext[], _stext[];
extern char __init_begin, __init_end;
extern char __prep_begin, __prep_end;
+extern char __chrp_begin, __chrp_end;
extern char __pmac_begin, __pmac_end;
extern char __apus_begin, __apus_end;
extern char __openfirmware_begin, __openfirmware_end;
@@ -777,7 +778,7 @@ void __init free_initmem(void)
unsigned long a;
unsigned long num_freed_pages = 0, num_prep_pages = 0,
num_pmac_pages = 0, num_openfirmware_pages = 0,
- num_apus_pages = 0;
+ num_apus_pages = 0, num_chrp_pages = 0;
#define FREESEC(START,END,CNT) do { \
a = (unsigned long)(&START); \
for (; a < (unsigned long)(&END); a += PAGE_SIZE) { \
@@ -794,6 +795,7 @@ void __init free_initmem(void)
case _MACH_Pmac:
FREESEC(__apus_begin,__apus_end,num_apus_pages);
FREESEC(__prep_begin,__prep_end,num_prep_pages);
+ FREESEC(__chrp_begin,__chrp_end,num_chrp_pages);
break;
case _MACH_chrp:
FREESEC(__apus_begin,__apus_end,num_apus_pages);
@@ -803,20 +805,24 @@ void __init free_initmem(void)
case _MACH_prep:
FREESEC(__apus_begin,__apus_end,num_apus_pages);
FREESEC(__pmac_begin,__pmac_end,num_pmac_pages);
+ FREESEC(__chrp_begin,__chrp_end,num_chrp_pages);
break;
case _MACH_mbx:
FREESEC(__apus_begin,__apus_end,num_apus_pages);
FREESEC(__pmac_begin,__pmac_end,num_pmac_pages);
FREESEC(__prep_begin,__prep_end,num_prep_pages);
+ FREESEC(__chrp_begin,__chrp_end,num_chrp_pages);
break;
case _MACH_apus:
FREESEC(__pmac_begin,__pmac_end,num_pmac_pages);
FREESEC(__prep_begin,__prep_end,num_prep_pages);
+ FREESEC(__chrp_begin,__chrp_end,num_chrp_pages);
break;
case _MACH_gemini:
FREESEC(__apus_begin,__apus_end,num_apus_pages);
FREESEC(__pmac_begin,__pmac_end,num_pmac_pages);
FREESEC(__prep_begin,__prep_end,num_prep_pages);
+ FREESEC(__chrp_begin,__chrp_end,num_chrp_pages);
break;
}
@@ -829,6 +835,8 @@ void __init free_initmem(void)
if ( num_prep_pages )
printk(" %ldk prep", PGTOKB(num_prep_pages));
+ if ( num_chrp_pages )
+ printk(" %ldk chrp", PGTOKB(num_chrp_pages));
if ( num_pmac_pages )
printk(" %ldk pmac", PGTOKB(num_pmac_pages));
if ( num_openfirmware_pages )