patch-2.4.13 linux/arch/s390/mm/fault.c

Next file: linux/arch/s390/mm/init.c
Previous file: linux/arch/s390/mm/extable.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.12/linux/arch/s390/mm/fault.c linux/arch/s390/mm/fault.c
@@ -4,6 +4,7 @@
  *  S390 version
  *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
  *    Author(s): Hartmut Penner (hp@de.ibm.com)
+ *               Ulrich Weigand (uweigand@de.ibm.com)
  *
  *  Derived from "arch/i386/mm/fault.c"
  *    Copyright (C) 1995  Linus Torvalds
@@ -23,6 +24,7 @@
 #include <linux/smp_lock.h>
 #include <linux/compatmac.h>
 #include <linux/init.h>
+#include <linux/console.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -34,6 +36,34 @@
 #endif
 
 extern void die(const char *,struct pt_regs *,long);
+static void force_sigsegv(struct task_struct *tsk, int code, void *address);
+
+extern spinlock_t timerlist_lock;
+
+/*
+ * Unlock any spinlocks which will prevent us from getting the
+ * message out (timerlist_lock is acquired through the
+ * console unblank code)
+ */
+void bust_spinlocks(int yes)
+{
+	spin_lock_init(&timerlist_lock);
+	if (yes) {
+		oops_in_progress = 1;
+	} else {
+		int loglevel_save = console_loglevel;
+		oops_in_progress = 0;
+		console_unblank();
+		/*
+		 * OK, the message is on the console.  Now we call printk()
+		 * without oops_in_progress set so that printk will give klogd
+		 * a poke.  Hold onto your hats...
+		 */
+		console_loglevel = 15;
+		printk(" ");
+		console_loglevel = loglevel_save;
+	}
+}
 
 /*
  * This routine handles page faults.  It determines the address,
@@ -56,18 +86,36 @@
 	int si_code = SEGV_MAPERR;
 	int kernel_address = 0;
 
+        tsk = current;
+        mm = tsk->mm;
+	
+	/* 
+         * Check for low-address protection.  This needs to be treated
+	 * as a special case because the translation exception code 
+	 * field is not guaranteed to contain valid data in this case.
+	 */
+	if ((error_code & 0xff) == 4 && !(S390_lowcore.trans_exc_code & 4)) {
+
+		/* Low-address protection hit in kernel mode means 
+		   NULL pointer write access in kernel mode.  */
+ 		if (!(regs->psw.mask & PSW_PROBLEM_STATE)) {
+			address = 0;
+			kernel_address = 1;
+			goto no_context;
+		}
+
+		/* Low-address protection hit in user mode 'cannot happen'.  */
+		die ("Low-address protection", regs, error_code);
+        	do_exit(SIGKILL);
+	}
+
         /* 
          * get the failing address 
          * more specific the segment and page table portion of 
          * the address 
          */
-        address = S390_lowcore.trans_exc_code&0x7ffff000;
 
-        tsk = current;
-        mm = tsk->mm;
-
-        if (in_interrupt() || !mm)
-                goto no_context;
+        address = S390_lowcore.trans_exc_code&0x7ffff000;
 
 
 	/*
@@ -99,6 +147,7 @@
 			}
 		}
 		die("page fault via unknown access register", regs, error_code);
+        	do_exit(SIGKILL);
 		break;
 
 	case 2: /* Secondary Segment Table Descriptor */
@@ -107,6 +156,11 @@
 		break;
 	}
 
+	/*
+	 * Check whether we have a user MM in the first place.
+	 */
+        if (in_interrupt() || !mm)
+                goto no_context;
 
 	/*
 	 * When we get here, the fault happened in the current
@@ -146,6 +200,7 @@
                        goto bad_area;
         }
 
+ survive:
 	/*
 	 * If for any reason at all we couldn't handle the fault,
 	 * make sure we exit gracefully rather than endlessly redo
@@ -176,7 +231,6 @@
 
         /* User mode accesses just cause a SIGSEGV */
         if (regs->psw.mask & PSW_PROBLEM_STATE) {
-		struct siginfo si;
                 tsk->thread.prot_addr = address;
                 tsk->thread.trap_no = error_code;
 #ifndef CONFIG_SYSCTL
@@ -193,10 +247,8 @@
 			show_regs(regs);
 		}
 #endif
-		si.si_signo = SIGSEGV;
-		si.si_code = si_code;
-		si.si_addr = (void*) address;
-		force_sig_info(SIGSEGV, &si, tsk);
+
+		force_sigsegv(tsk, si_code, (void *)address);
                 return;
 	}
 
@@ -218,9 +270,6 @@
         else
                 printk(KERN_ALERT "Unable to handle kernel paging request"
 		       " at virtual user address %08lx\n", address);
-/*
- * need to define, which information is useful here
- */
 
         die("Oops", regs, error_code);
         do_exit(SIGKILL);
@@ -232,6 +281,12 @@
 */
 out_of_memory:
 	up_read(&mm->mmap_sem);
+	if (tsk->pid == 1) {
+		tsk->policy |= SCHED_YIELD;
+		schedule();
+		down_read(&mm->mmap_sem);
+		goto survive;
+	}
 	printk("VM: killing process %s\n", tsk->comm);
 	if (regs->psw.mask & PSW_PROBLEM_STATE)
 		do_exit(SIGKILL);
@@ -253,6 +308,18 @@
 		goto no_context;
 }
 
+/*
+ * Send SIGSEGV to task.  This is an external routine
+ * to keep the stack usage of do_page_fault small.
+ */
+static void force_sigsegv(struct task_struct *tsk, int code, void *address)
+{
+	struct siginfo si;
+	si.si_signo = SIGSEGV;
+	si.si_code = code;
+	si.si_addr = address;
+	force_sig_info(SIGSEGV, &si, tsk);
+}
 
 typedef struct _pseudo_wait_t {
        struct _pseudo_wait_t *next;
@@ -434,7 +501,6 @@
 asmlinkage void
 pfault_interrupt(struct pt_regs *regs, __u16 error_code)
 {
-        DECLARE_WAITQUEUE(wait, current);
 	struct task_struct *tsk;
 	wait_queue_head_t queue;
 	wait_queue_head_t *qp;
@@ -447,7 +513,7 @@
          * external interrupt. 
 	 */
 	subcode = S390_lowcore.cpu_addr;
-	if ((subcode & 0xff00) != 0x06)
+	if ((subcode & 0xff00) != 0x0600)
 		return;
 
 	/*

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)