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

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)