summaryrefslogtreecommitdiffstats
path: root/arch/um/kernel/trap_kern.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/um/kernel/trap_kern.c')
-rw-r--r--arch/um/kernel/trap_kern.c25
1 files changed, 16 insertions, 9 deletions
diff --git a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c
index 47e766e6ba1..54e2ec33a43 100644
--- a/arch/um/kernel/trap_kern.c
+++ b/arch/um/kernel/trap_kern.c
@@ -133,12 +133,19 @@ static int check_remapped_addr(unsigned long address, int is_write)
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 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write,
}
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 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write,
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 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write,
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 @@ void relay_signal(int sig, union uml_pt_regs *regs)
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);
}