patch-2.4.2 linux/arch/s390/mm/fault.c
Next file: linux/arch/s390/mm/init.c
Previous file: linux/arch/s390/mm/Makefile
Back to the patch index
Back to the overall index
- Lines: 282
- Date:
Tue Feb 13 14:13:44 2001
- Orig file:
v2.4.1/linux/arch/s390/mm/fault.c
- Orig date:
Fri Aug 4 16:15:37 2000
diff -u --recursive --new-file v2.4.1/linux/arch/s390/mm/fault.c linux/arch/s390/mm/fault.c
@@ -9,6 +9,7 @@
* Copyright (C) 1995 Linus Torvalds
*/
+#include <linux/config.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -26,6 +27,10 @@
#include <asm/pgtable.h>
#include <asm/hardirq.h>
+#ifdef CONFIG_SYSCTL
+extern int sysctl_userprocess_debug;
+#endif
+
extern void die(const char *,struct pt_regs *,long);
/*
@@ -48,6 +53,8 @@
int write;
unsigned long psw_mask;
unsigned long psw_addr;
+ int si_code = SEGV_MAPERR;
+ int kernel_address = 0;
/*
* get psw mask of Program old psw to find out,
@@ -65,58 +72,106 @@
address = S390_lowcore.trans_exc_code&0x7ffff000;
- if (in_irq())
- die("page fault from irq handler",regs,error_code);
-
tsk = current;
mm = tsk->mm;
+ if (in_interrupt() || !mm)
+ goto no_context;
+
+
+ /*
+ * Check which address space the address belongs to
+ */
+ switch (S390_lowcore.trans_exc_code & 3)
+ {
+ case 0: /* Primary Segment Table Descriptor */
+ kernel_address = 1;
+ goto no_context;
+
+ case 1: /* STD determined via access register */
+ if (S390_lowcore.exc_access_id == 0)
+ {
+ kernel_address = 1;
+ goto no_context;
+ }
+ if (regs && S390_lowcore.exc_access_id < NUM_ACRS)
+ {
+ if (regs->acrs[S390_lowcore.exc_access_id] == 0)
+ {
+ kernel_address = 1;
+ goto no_context;
+ }
+ if (regs->acrs[S390_lowcore.exc_access_id] == 1)
+ {
+ /* user space address */
+ break;
+ }
+ }
+ die("page fault via unknown access register", regs, error_code);
+ break;
+
+ case 2: /* Secondary Segment Table Descriptor */
+ case 3: /* Home Segment Table Descriptor */
+ /* user space address */
+ break;
+ }
+
+
+ /*
+ * When we get here, the fault happened in the current
+ * task's user address space, so we search the VMAs
+ */
+
down(&mm->mmap_sem);
vma = find_vma(mm, address);
- if (!vma) {
- printk("no vma for address %lX\n",address);
+ if (!vma)
goto bad_area;
- }
if (vma->vm_start <= address)
goto good_area;
- if (!(vma->vm_flags & VM_GROWSDOWN)) {
- printk("VM_GROWSDOWN not set, but address %lX \n",address);
- printk("not in vma %p (start %lX end %lX)\n",vma,
- vma->vm_start,vma->vm_end);
+ if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- }
- if (expand_stack(vma, address)) {
- printk("expand of vma failed address %lX\n",address);
- printk("vma %p (start %lX end %lX)\n",vma,
- vma->vm_start,vma->vm_end);
+ if (expand_stack(vma, address))
goto bad_area;
- }
/*
* Ok, we have a good vm_area for this memory access, so
* we can handle it..
*/
good_area:
write = 0;
+ si_code = SEGV_ACCERR;
+
switch (error_code & 0xFF) {
case 0x04: /* write, present*/
write = 1;
break;
case 0x10: /* not present*/
case 0x11: /* not present*/
- if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) {
- printk("flags %X of vma for address %lX wrong \n",
- vma->vm_flags,address);
- printk("vma %p (start %lX end %lX)\n",vma,
- vma->vm_start,vma->vm_end);
+ if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
goto bad_area;
- }
break;
default:
printk("code should be 4, 10 or 11 (%lX) \n",error_code&0xFF);
goto bad_area;
}
- handle_mm_fault(tsk, vma, address, write);
+
+ /*
+ * 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, write)) {
+ case 1:
+ tsk->min_flt++;
+ break;
+ case 2:
+ tsk->maj_flt++;
+ break;
+ case 0:
+ goto do_sigbus;
+ default:
+ goto out_of_memory;
+ }
up(&mm->mmap_sem);
return;
@@ -130,19 +185,32 @@
/* User mode accesses just cause a SIGSEGV */
if (psw_mask & PSW_PROBLEM_STATE) {
+ struct siginfo si;
tsk->thread.prot_addr = address;
- tsk->thread.error_code = error_code;
- tsk->thread.trap_no = 14;
-
+ tsk->thread.trap_no = error_code;
+#ifndef CONFIG_SYSCTL
+#ifdef CONFIG_PROCESS_DEBUG
printk("User process fault: interruption code 0x%lX\n",error_code);
printk("failing address: %lX\n",address);
- show_crashed_task_info();
- force_sig(SIGSEGV, tsk);
+ show_regs(regs);
+#endif
+#else
+ if (sysctl_userprocess_debug) {
+ printk("User process fault: interruption code 0x%lX\n",
+ error_code);
+ printk("failing address: %lX\n", address);
+ show_regs(regs);
+ }
+#endif
+ si.si_signo = SIGSEGV;
+ si.si_code = si_code;
+ si.si_addr = (void*) address;
+ force_sig_info(SIGSEGV, &si, tsk);
return;
}
+no_context:
/* Are we prepared to handle this kernel fault? */
-
if ((fixup = search_exception_table(regs->psw.addr)) != 0) {
regs->psw.addr = fixup;
return;
@@ -151,53 +219,47 @@
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
- *
- * First we check if it was the bootup rw-test, though..
*/
- if (address < PAGE_SIZE)
- printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+
+ if (kernel_address)
+ printk(KERN_ALERT "Unable to handle kernel pointer dereference"
+ " at virtual kernel address %08lx\n", address);
else
- printk(KERN_ALERT "Unable to handle kernel paging request");
- printk(" at virtual address %08lx\n",address);
+ printk(KERN_ALERT "Unable to handle kernel paging request"
+ " at virtual user address %08lx\n", address);
/*
* need to define, which information is useful here
*/
- lock_kernel();
die("Oops", regs, error_code);
do_exit(SIGKILL);
- unlock_kernel();
-}
-/*
- {
- char c;
- int i,j;
- char *addr;
- addr = ((char*) psw_addr)-0x20;
- for (i=0;i<16;i++) {
- if (i == 2)
- printk("\n");
- printk ("%08X: ",(unsigned long) addr);
- for (j=0;j<4;j++) {
- printk("%08X ",*(unsigned long*)addr);
- addr += 4;
- }
- addr -=0x10;
- printk(" | ");
- for (j=0;j<16;j++) {
- printk("%c",(c=*addr++) < 0x20 ? '.' : c );
- }
-
- printk("\n");
- }
- printk("\n");
- }
+/*
+ * 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", tsk->comm);
+ if (psw_mask & PSW_PROBLEM_STATE)
+ do_exit(SIGKILL);
+ goto no_context;
+
+do_sigbus:
+ up(&mm->mmap_sem);
+
+ /*
+ * Send a sigbus, regardless of whether we were in kernel
+ * or user mode.
+ */
+ tsk->thread.prot_addr = address;
+ tsk->thread.trap_no = error_code;
+ force_sig(SIGBUS, tsk);
+
+ /* Kernel mode? Handle exceptions or die */
+ if (!(psw_mask & PSW_PROBLEM_STATE))
+ goto no_context;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)