[PATCH] uml: S390 preparation, abstract host page fault data

This patch removes the arch-specific fault/trap-infos from thread and
skas-regs.

It adds a new struct faultinfo, that is arch-specific defined in
sysdep/faultinfo.h.

The structure is inserted in thread.arch and thread.regs.skas and
thread.regs.tt

Now, segv and other trap-handlers can copy the contents from regs.X.faultinfo
to thread.arch.faultinfo with one simple assignment.

Also, the number of macros necessary is reduced to

FAULT_ADDRESS(struct faultinfo)
    extracts the faulting address from faultinfo

FAULT_WRITE(struct faultinfo)
    extracts the "is_write" flag

SEGV_IS_FIXABLE(struct faultinfo)
    is true for the fixable segvs, i.e. (TRAP == 14)
    on i386

UPT_FAULTINFO(regs)
    result is (struct faultinfo *) to the faultinfo
    in regs->skas.faultinfo

GET_FAULTINFO_FROM_SC(struct faultinfo, struct sigcontext *)
    copies the relevant parts of the sigcontext to
    struct faultinfo.

On SIGSEGV, call user_signal() instead of handle_segv(), if the architecture
provides the information needed in PTRACE_FAULTINFO, or if PTRACE_FAULTINFO is
missing, because segv-stub will provide the info.

The benefit of the change is, that in case of a non-fixable SIGSEGV, we can
give user processes a SIGSEGV, instead of possibly looping on pagefault
handling.

Since handle_segv() sikked arch_fixup() implicitly by passing ip==0 to segv(),
I changed segv() to call arch_fixup() only, if !is_user.

Signed-off-by: Bodo Stroesser <bstroesser@fujitsu-siemens.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c
index 47e766e..54e2ec3 100644
--- a/arch/um/kernel/trap_kern.c
+++ b/arch/um/kernel/trap_kern.c
@@ -133,12 +133,19 @@
 	return(0);
 }
 
-unsigned long segv(unsigned long address, unsigned long ip, int is_write, 
-		   int is_user, void *sc)
+/*
+ * We give a *copy* of the faultinfo in the regs to segv.
+ * This must be done, since nesting SEGVs could overwrite
+ * the info in the regs. A pointer to the info then would
+ * give us bad data!
+ */
+unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, void *sc)
 {
 	struct siginfo si;
 	void *catcher;
 	int err;
+        int is_write = FAULT_WRITE(fi);
+        unsigned long address = FAULT_ADDRESS(fi);
 
         if(!is_user && (address >= start_vm) && (address < end_vm)){
                 flush_tlb_kernel_vm();
@@ -159,7 +166,7 @@
 	} 
 	else if(current->thread.fault_addr != NULL)
 		panic("fault_addr set but no fault catcher");
-	else if(arch_fixup(ip, sc))
+        else if(!is_user && arch_fixup(ip, sc))
 		return(0);
 
  	if(!is_user) 
@@ -171,6 +178,7 @@
 		si.si_errno = 0;
 		si.si_code = BUS_ADRERR;
 		si.si_addr = (void *)address;
+                current->thread.arch.faultinfo = fi;
 		force_sig_info(SIGBUS, &si, current);
 	}
 	else if(err == -ENOMEM){
@@ -180,22 +188,20 @@
 	else {
 		si.si_signo = SIGSEGV;
 		si.si_addr = (void *) address;
-		current->thread.cr2 = address;
-		current->thread.err = is_write;
+                current->thread.arch.faultinfo = fi;
 		force_sig_info(SIGSEGV, &si, current);
 	}
 	return(0);
 }
 
-void bad_segv(unsigned long address, unsigned long ip, int is_write)
+void bad_segv(struct faultinfo fi, unsigned long ip)
 {
 	struct siginfo si;
 
 	si.si_signo = SIGSEGV;
 	si.si_code = SEGV_ACCERR;
-	si.si_addr = (void *) address;
-	current->thread.cr2 = address;
-	current->thread.err = is_write;
+        si.si_addr = (void *) FAULT_ADDRESS(fi);
+        current->thread.arch.faultinfo = fi;
 	force_sig_info(SIGSEGV, &si, current);
 }
 
@@ -204,6 +210,7 @@
 	if(arch_handle_signal(sig, regs)) return;
 	if(!UPT_IS_USER(regs))
 		panic("Kernel mode signal %d", sig);
+        current->thread.arch.faultinfo = *UPT_FAULTINFO(regs);
 	force_sig(sig, current);
 }