2006-01-08 16:01:31 +07:00
|
|
|
/*
|
2007-10-16 15:26:54 +07:00
|
|
|
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
2005-04-17 05:20:36 +07:00
|
|
|
* Licensed under the GPL
|
|
|
|
*/
|
|
|
|
|
2007-10-16 15:26:54 +07:00
|
|
|
#include <linux/mm.h>
|
2017-02-09 00:51:30 +07:00
|
|
|
#include <linux/sched/signal.h>
|
2007-10-16 15:26:54 +07:00
|
|
|
#include <linux/hardirq.h>
|
2011-08-19 02:14:10 +07:00
|
|
|
#include <linux/module.h>
|
2015-05-19 18:53:29 +07:00
|
|
|
#include <linux/uaccess.h>
|
2007-10-16 15:26:54 +07:00
|
|
|
#include <asm/current.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
|
|
#include <asm/tlbflush.h>
|
2012-10-08 09:27:32 +07:00
|
|
|
#include <arch.h>
|
|
|
|
#include <as-layout.h>
|
|
|
|
#include <kern_util.h>
|
|
|
|
#include <os.h>
|
|
|
|
#include <skas.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-10-16 15:26:54 +07:00
|
|
|
/*
|
|
|
|
* Note this is constrained to return 0, -EFAULT, -EACCESS, -ENOMEM by
|
|
|
|
* segv().
|
|
|
|
*/
|
2006-07-10 18:45:13 +07:00
|
|
|
int handle_page_fault(unsigned long address, unsigned long ip,
|
2005-04-17 05:20:36 +07:00
|
|
|
int is_write, int is_user, int *code_out)
|
|
|
|
{
|
|
|
|
struct mm_struct *mm = current->mm;
|
|
|
|
struct vm_area_struct *vma;
|
|
|
|
pgd_t *pgd;
|
|
|
|
pud_t *pud;
|
|
|
|
pmd_t *pmd;
|
|
|
|
pte_t *pte;
|
|
|
|
int err = -EFAULT;
|
2013-09-13 05:13:39 +07:00
|
|
|
unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
*code_out = SEGV_MAPERR;
|
2005-09-23 11:44:20 +07:00
|
|
|
|
2007-10-16 15:26:54 +07:00
|
|
|
/*
|
2015-05-11 22:52:11 +07:00
|
|
|
* If the fault was with pagefaults disabled, don't take the fault, just
|
2007-10-16 15:26:54 +07:00
|
|
|
* fail.
|
|
|
|
*/
|
2015-05-11 22:52:11 +07:00
|
|
|
if (faulthandler_disabled())
|
2005-09-23 11:44:20 +07:00
|
|
|
goto out_nosemaphore;
|
|
|
|
|
2013-09-13 05:13:39 +07:00
|
|
|
if (is_user)
|
|
|
|
flags |= FAULT_FLAG_USER;
|
2012-06-01 06:26:03 +07:00
|
|
|
retry:
|
2005-04-17 05:20:36 +07:00
|
|
|
down_read(&mm->mmap_sem);
|
|
|
|
vma = find_vma(mm, address);
|
2007-10-16 15:26:54 +07:00
|
|
|
if (!vma)
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
2007-10-16 15:26:54 +07:00
|
|
|
else if (vma->vm_start <= address)
|
2005-04-17 05:20:36 +07:00
|
|
|
goto good_area;
|
2007-10-16 15:26:54 +07:00
|
|
|
else if (!(vma->vm_flags & VM_GROWSDOWN))
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
2007-10-16 15:26:54 +07:00
|
|
|
else if (is_user && !ARCH_IS_STACKGROW(address))
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
2007-10-16 15:26:54 +07:00
|
|
|
else if (expand_stack(vma, address))
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
|
|
|
|
2005-09-04 05:57:26 +07:00
|
|
|
good_area:
|
2005-04-17 05:20:36 +07:00
|
|
|
*code_out = SEGV_ACCERR;
|
2013-09-13 05:13:39 +07:00
|
|
|
if (is_write) {
|
|
|
|
if (!(vma->vm_flags & VM_WRITE))
|
|
|
|
goto out;
|
|
|
|
flags |= FAULT_FLAG_WRITE;
|
|
|
|
} else {
|
|
|
|
/* Don't require VM_READ|VM_EXEC for write faults! */
|
|
|
|
if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
|
|
|
|
goto out;
|
|
|
|
}
|
2005-05-21 03:59:08 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
do {
|
2007-07-19 15:47:05 +07:00
|
|
|
int fault;
|
2009-01-07 05:38:59 +07:00
|
|
|
|
2016-07-27 05:25:18 +07:00
|
|
|
fault = handle_mm_fault(vma, address, flags);
|
2012-06-01 06:26:03 +07:00
|
|
|
|
|
|
|
if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
|
|
|
|
goto out_nosemaphore;
|
|
|
|
|
2007-07-19 15:47:05 +07:00
|
|
|
if (unlikely(fault & VM_FAULT_ERROR)) {
|
|
|
|
if (fault & VM_FAULT_OOM) {
|
|
|
|
goto out_of_memory;
|
vm: add VM_FAULT_SIGSEGV handling support
The core VM already knows about VM_FAULT_SIGBUS, but cannot return a
"you should SIGSEGV" error, because the SIGSEGV case was generally
handled by the caller - usually the architecture fault handler.
That results in lots of duplication - all the architecture fault
handlers end up doing very similar "look up vma, check permissions, do
retries etc" - but it generally works. However, there are cases where
the VM actually wants to SIGSEGV, and applications _expect_ SIGSEGV.
In particular, when accessing the stack guard page, libsigsegv expects a
SIGSEGV. And it usually got one, because the stack growth is handled by
that duplicated architecture fault handler.
However, when the generic VM layer started propagating the error return
from the stack expansion in commit fee7e49d4514 ("mm: propagate error
from stack expansion even for guard page"), that now exposed the
existing VM_FAULT_SIGBUS result to user space. And user space really
expected SIGSEGV, not SIGBUS.
To fix that case, we need to add a VM_FAULT_SIGSEGV, and teach all those
duplicate architecture fault handlers about it. They all already have
the code to handle SIGSEGV, so it's about just tying that new return
value to the existing code, but it's all a bit annoying.
This is the mindless minimal patch to do this. A more extensive patch
would be to try to gather up the mostly shared fault handling logic into
one generic helper routine, and long-term we really should do that
cleanup.
Just from this patch, you can generally see that most architectures just
copied (directly or indirectly) the old x86 way of doing things, but in
the meantime that original x86 model has been improved to hold the VM
semaphore for shorter times etc and to handle VM_FAULT_RETRY and other
"newer" things, so it would be a good idea to bring all those
improvements to the generic case and teach other architectures about
them too.
Reported-and-tested-by: Takashi Iwai <tiwai@suse.de>
Tested-by: Jan Engelhardt <jengelh@inai.de>
Acked-by: Heiko Carstens <heiko.carstens@de.ibm.com> # "s390 still compiles and boots"
Cc: linux-arch@vger.kernel.org
Cc: stable@vger.kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2015-01-30 01:51:32 +07:00
|
|
|
} else if (fault & VM_FAULT_SIGSEGV) {
|
|
|
|
goto out;
|
2007-07-19 15:47:05 +07:00
|
|
|
} else if (fault & VM_FAULT_SIGBUS) {
|
|
|
|
err = -EACCES;
|
|
|
|
goto out;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
BUG();
|
|
|
|
}
|
2012-06-01 06:26:03 +07:00
|
|
|
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
|
|
|
if (fault & VM_FAULT_MAJOR)
|
|
|
|
current->maj_flt++;
|
|
|
|
else
|
|
|
|
current->min_flt++;
|
|
|
|
if (fault & VM_FAULT_RETRY) {
|
|
|
|
flags &= ~FAULT_FLAG_ALLOW_RETRY;
|
2012-10-09 06:32:19 +07:00
|
|
|
flags |= FAULT_FLAG_TRIED;
|
2012-06-01 06:26:03 +07:00
|
|
|
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
}
|
2007-07-19 15:47:05 +07:00
|
|
|
|
2005-09-04 05:57:26 +07:00
|
|
|
pgd = pgd_offset(mm, address);
|
|
|
|
pud = pud_offset(pgd, address);
|
|
|
|
pmd = pmd_offset(pud, address);
|
|
|
|
pte = pte_offset_kernel(pmd, address);
|
2007-10-16 15:26:54 +07:00
|
|
|
} while (!pte_present(*pte));
|
2005-04-17 05:20:36 +07:00
|
|
|
err = 0;
|
2007-10-16 15:26:54 +07:00
|
|
|
/*
|
|
|
|
* The below warning was added in place of
|
[PATCH] uml: remove bogus WARN_ON, triggerable harmlessly on a page fault race
The below warning was added in place of pte_mkyoung(); if (is_write)
pte_mkdirty();
In fact, if the PTE is not marked young/dirty, our dirty/accessed bit
emulation would cause the TLB permission not to be changed, and so we'd loop,
and given we don't support preemption yet, we'd busy-hang here.
However, I've seen this warning trigger without crashes during a loop of
concurrent kernel builds, at random times (i.e. like a race condition), and I
realized that two concurrent faults on the same page, one on read and one on
write, can trigger it. The read fault gets serviced and the PTE gets marked
writable but clean (it's possible on a shared-writable mapping), while the
generic code sees the PTE was already installed and returns without action. In
this case, we'll see another fault and service it normally.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Acked-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-14 07:07:04 +07:00
|
|
|
* pte_mkyoung(); if (is_write) pte_mkdirty();
|
|
|
|
* If it's triggered, we'd see normally a hang here (a clean pte is
|
|
|
|
* marked read-only to emulate the dirty bit).
|
|
|
|
* However, the generic code can mark a PTE writable but clean on a
|
|
|
|
* concurrent read fault, triggering this harmlessly. So comment it out.
|
|
|
|
*/
|
|
|
|
#if 0
|
2005-09-11 00:44:58 +07:00
|
|
|
WARN_ON(!pte_young(*pte) || (is_write && !pte_dirty(*pte)));
|
[PATCH] uml: remove bogus WARN_ON, triggerable harmlessly on a page fault race
The below warning was added in place of pte_mkyoung(); if (is_write)
pte_mkdirty();
In fact, if the PTE is not marked young/dirty, our dirty/accessed bit
emulation would cause the TLB permission not to be changed, and so we'd loop,
and given we don't support preemption yet, we'd busy-hang here.
However, I've seen this warning trigger without crashes during a loop of
concurrent kernel builds, at random times (i.e. like a race condition), and I
realized that two concurrent faults on the same page, one on read and one on
write, can trigger it. The read fault gets serviced and the PTE gets marked
writable but clean (it's possible on a shared-writable mapping), while the
generic code sees the PTE was already installed and returns without action. In
this case, we'll see another fault and service it normally.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Acked-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-14 07:07:04 +07:00
|
|
|
#endif
|
2005-09-04 05:57:26 +07:00
|
|
|
flush_tlb_page(vma, address);
|
|
|
|
out:
|
2005-04-17 05:20:36 +07:00
|
|
|
up_read(&mm->mmap_sem);
|
2005-09-23 11:44:20 +07:00
|
|
|
out_nosemaphore:
|
2007-10-16 15:26:54 +07:00
|
|
|
return err;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
out_of_memory:
|
2009-01-07 05:38:59 +07:00
|
|
|
/*
|
|
|
|
* We ran out of memory, call the OOM killer, and return the userspace
|
|
|
|
* (which will retry the fault, or kill us if we got oom-killed).
|
|
|
|
*/
|
|
|
|
up_read(&mm->mmap_sem);
|
2013-09-13 05:13:38 +07:00
|
|
|
if (!is_user)
|
|
|
|
goto out_nosemaphore;
|
2009-01-07 05:38:59 +07:00
|
|
|
pagefault_out_of_memory();
|
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2011-08-19 02:14:10 +07:00
|
|
|
EXPORT_SYMBOL(handle_page_fault);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-05-25 07:13:03 +07:00
|
|
|
static void show_segv_info(struct uml_pt_regs *regs)
|
|
|
|
{
|
|
|
|
struct task_struct *tsk = current;
|
|
|
|
struct faultinfo *fi = UPT_FAULTINFO(regs);
|
|
|
|
|
|
|
|
if (!unhandled_signal(tsk, SIGSEGV))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!printk_ratelimit())
|
|
|
|
return;
|
|
|
|
|
|
|
|
printk("%s%s[%d]: segfault at %lx ip %p sp %p error %x",
|
|
|
|
task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG,
|
|
|
|
tsk->comm, task_pid_nr(tsk), FAULT_ADDRESS(*fi),
|
|
|
|
(void *)UPT_IP(regs), (void *)UPT_SP(regs),
|
|
|
|
fi->error_code);
|
|
|
|
|
|
|
|
print_vma_addr(KERN_CONT " in ", UPT_IP(regs));
|
|
|
|
printk(KERN_CONT "\n");
|
|
|
|
}
|
|
|
|
|
2007-02-10 16:44:14 +07:00
|
|
|
static 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 __user *) FAULT_ADDRESS(fi);
|
|
|
|
current->thread.arch.faultinfo = fi;
|
|
|
|
force_sig_info(SIGSEGV, &si, current);
|
|
|
|
}
|
|
|
|
|
2008-02-05 13:30:58 +07:00
|
|
|
void fatal_sigsegv(void)
|
|
|
|
{
|
|
|
|
force_sigsegv(SIGSEGV, current);
|
2015-07-04 02:44:20 +07:00
|
|
|
do_signal(¤t->thread.regs);
|
2008-02-05 13:30:58 +07:00
|
|
|
/*
|
|
|
|
* This is to tell gcc that we're not returning - do_signal
|
|
|
|
* can, in general, return, but in this case, it's not, since
|
|
|
|
* we just got a fatal SIGSEGV queued.
|
|
|
|
*/
|
|
|
|
os_dump_core();
|
|
|
|
}
|
|
|
|
|
2012-08-02 05:49:17 +07:00
|
|
|
void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
|
2006-01-08 16:01:32 +07:00
|
|
|
{
|
|
|
|
struct faultinfo * fi = UPT_FAULTINFO(regs);
|
|
|
|
|
2007-10-16 15:26:54 +07:00
|
|
|
if (UPT_IS_USER(regs) && !SEGV_IS_FIXABLE(fi)) {
|
2011-05-25 07:13:03 +07:00
|
|
|
show_segv_info(regs);
|
2006-01-08 16:01:32 +07:00
|
|
|
bad_segv(*fi, UPT_IP(regs));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs);
|
|
|
|
}
|
|
|
|
|
[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>
2005-05-06 06:15:31 +07:00
|
|
|
/*
|
|
|
|
* 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!
|
|
|
|
*/
|
2007-05-07 04:51:24 +07:00
|
|
|
unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
|
2007-10-16 15:26:58 +07:00
|
|
|
struct uml_pt_regs *regs)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct siginfo si;
|
2007-10-16 15:27:05 +07:00
|
|
|
jmp_buf *catcher;
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
2007-05-07 04:51:24 +07:00
|
|
|
int is_write = FAULT_WRITE(fi);
|
|
|
|
unsigned long address = FAULT_ADDRESS(fi);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-07-20 18:39:27 +07:00
|
|
|
if (!is_user && regs)
|
2013-09-23 22:38:02 +07:00
|
|
|
current->thread.segv_regs = container_of(regs, struct pt_regs, regs);
|
|
|
|
|
2007-10-16 15:26:54 +07:00
|
|
|
if (!is_user && (address >= start_vm) && (address < end_vm)) {
|
2007-05-07 04:51:24 +07:00
|
|
|
flush_tlb_kernel_vm();
|
2013-09-23 22:38:02 +07:00
|
|
|
goto out;
|
2007-05-07 04:51:24 +07:00
|
|
|
}
|
2007-10-16 15:26:54 +07:00
|
|
|
else if (current->mm == NULL) {
|
2007-05-07 04:51:25 +07:00
|
|
|
show_regs(container_of(regs, struct pt_regs, regs));
|
2007-10-16 15:26:54 +07:00
|
|
|
panic("Segfault with no mm");
|
2007-05-07 04:51:25 +07:00
|
|
|
}
|
2015-08-10 03:26:33 +07:00
|
|
|
else if (!is_user && address > PAGE_SIZE && address < TASK_SIZE) {
|
2015-06-01 00:21:51 +07:00
|
|
|
show_regs(container_of(regs, struct pt_regs, regs));
|
|
|
|
panic("Kernel tried to access user memory at addr 0x%lx, ip 0x%lx",
|
|
|
|
address, ip);
|
|
|
|
}
|
2005-09-23 11:44:16 +07:00
|
|
|
|
2015-03-19 03:31:27 +07:00
|
|
|
if (SEGV_IS_FIXABLE(&fi))
|
2007-10-16 15:26:54 +07:00
|
|
|
err = handle_page_fault(address, ip, is_write, is_user,
|
|
|
|
&si.si_code);
|
2005-09-23 11:44:16 +07:00
|
|
|
else {
|
|
|
|
err = -EFAULT;
|
2007-10-16 15:26:54 +07:00
|
|
|
/*
|
|
|
|
* A thread accessed NULL, we get a fault, but CR2 is invalid.
|
|
|
|
* This code is used in __do_copy_from_user() of TT mode.
|
|
|
|
* XXX tt mode is gone, so maybe this isn't needed any more
|
|
|
|
*/
|
2005-09-23 11:44:16 +07:00
|
|
|
address = 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
catcher = current->thread.fault_catcher;
|
2007-10-16 15:26:54 +07:00
|
|
|
if (!err)
|
2013-09-23 22:38:02 +07:00
|
|
|
goto out;
|
2007-10-16 15:26:54 +07:00
|
|
|
else if (catcher != NULL) {
|
2005-04-17 05:20:36 +07:00
|
|
|
current->thread.fault_addr = (void *) address;
|
2007-10-16 15:27:05 +07:00
|
|
|
UML_LONGJMP(catcher, 1);
|
2006-07-10 18:45:13 +07:00
|
|
|
}
|
2007-10-16 15:26:54 +07:00
|
|
|
else if (current->thread.fault_addr != NULL)
|
2005-04-17 05:20:36 +07:00
|
|
|
panic("fault_addr set but no fault catcher");
|
2007-10-16 15:26:54 +07:00
|
|
|
else if (!is_user && arch_fixup(ip, regs))
|
2013-09-23 22:38:02 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-10-16 15:26:54 +07:00
|
|
|
if (!is_user) {
|
2007-05-07 04:51:25 +07:00
|
|
|
show_regs(container_of(regs, struct pt_regs, regs));
|
2006-07-10 18:45:13 +07:00
|
|
|
panic("Kernel mode fault at addr 0x%lx, ip 0x%lx",
|
2005-04-17 05:20:36 +07:00
|
|
|
address, ip);
|
2007-05-07 04:51:25 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-05-25 07:13:03 +07:00
|
|
|
show_segv_info(regs);
|
|
|
|
|
2005-09-04 05:57:26 +07:00
|
|
|
if (err == -EACCES) {
|
2005-04-17 05:20:36 +07:00
|
|
|
si.si_signo = SIGBUS;
|
|
|
|
si.si_errno = 0;
|
|
|
|
si.si_code = BUS_ADRERR;
|
2006-03-31 17:30:15 +07:00
|
|
|
si.si_addr = (void __user *)address;
|
2007-05-07 04:51:24 +07:00
|
|
|
current->thread.arch.faultinfo = fi;
|
2005-04-17 05:20:36 +07:00
|
|
|
force_sig_info(SIGBUS, &si, current);
|
2005-09-04 05:57:26 +07:00
|
|
|
} else {
|
|
|
|
BUG_ON(err != -EFAULT);
|
2005-04-17 05:20:36 +07:00
|
|
|
si.si_signo = SIGSEGV;
|
2006-03-31 17:30:15 +07:00
|
|
|
si.si_addr = (void __user *) address;
|
2007-05-07 04:51:24 +07:00
|
|
|
current->thread.arch.faultinfo = fi;
|
2005-04-17 05:20:36 +07:00
|
|
|
force_sig_info(SIGSEGV, &si, current);
|
|
|
|
}
|
2013-09-23 22:38:02 +07:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (regs)
|
|
|
|
current->thread.segv_regs = NULL;
|
|
|
|
|
2007-05-07 04:51:24 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-08-02 05:49:17 +07:00
|
|
|
void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2012-08-02 05:49:17 +07:00
|
|
|
struct faultinfo *fi;
|
|
|
|
struct siginfo clean_si;
|
|
|
|
|
2007-10-16 15:26:54 +07:00
|
|
|
if (!UPT_IS_USER(regs)) {
|
|
|
|
if (sig == SIGBUS)
|
|
|
|
printk(KERN_ERR "Bus error - the host /dev/shm or /tmp "
|
|
|
|
"mount likely just ran out of space\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
panic("Kernel mode signal %d", sig);
|
2006-09-26 13:33:03 +07:00
|
|
|
}
|
|
|
|
|
uml: further bugs.c tidying
bugs.c, for both i386 and x86_64, can undergo further cleaning -
The i386 arch_check_bugs only does one thing, so we might as
well inline the cmov checking.
The i386 includes can be trimmed down a bit.
arch_init_thread wasn't used, so it is deleted.
The panics in arch_handle_signal are turned into printks
because the process is about to get segfaulted anyway, so something is
dying no matter what happens here. Also, the return value was always
the same, so it contained no information, so it can be void instead.
The name is changed to arch_examine_signal because it doesn't handle
anything.
The caller of arch_handle_signal, relay_signal, does things in
a different order. The kernel-mode signal check is now first, which
puts everything else together, making things a bit clearer conceptually.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-05 13:30:40 +07:00
|
|
|
arch_examine_signal(sig, regs);
|
|
|
|
|
2012-08-02 05:49:17 +07:00
|
|
|
memset(&clean_si, 0, sizeof(clean_si));
|
|
|
|
clean_si.si_signo = si->si_signo;
|
|
|
|
clean_si.si_errno = si->si_errno;
|
|
|
|
clean_si.si_code = si->si_code;
|
|
|
|
switch (sig) {
|
|
|
|
case SIGILL:
|
|
|
|
case SIGFPE:
|
|
|
|
case SIGSEGV:
|
|
|
|
case SIGBUS:
|
|
|
|
case SIGTRAP:
|
|
|
|
fi = UPT_FAULTINFO(regs);
|
|
|
|
clean_si.si_addr = (void __user *) FAULT_ADDRESS(*fi);
|
|
|
|
current->thread.arch.faultinfo = *fi;
|
|
|
|
#ifdef __ARCH_SI_TRAPNO
|
|
|
|
clean_si.si_trapno = si->si_trapno;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printk(KERN_ERR "Attempted to relay unknown signal %d (si_code = %d)\n",
|
|
|
|
sig, si->si_code);
|
|
|
|
}
|
|
|
|
|
|
|
|
force_sig_info(sig, &clean_si, current);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-08-02 05:49:17 +07:00
|
|
|
void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-10-16 15:26:54 +07:00
|
|
|
if (current->thread.fault_catcher != NULL)
|
2007-10-16 15:27:05 +07:00
|
|
|
UML_LONGJMP(current->thread.fault_catcher, 1);
|
2012-08-02 05:49:17 +07:00
|
|
|
else
|
|
|
|
relay_signal(sig, si, regs);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-08-02 05:49:17 +07:00
|
|
|
void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
do_IRQ(WINCH_IRQ, regs);
|
|
|
|
}
|
|
|
|
|
|
|
|
void trap_init(void)
|
|
|
|
{
|
|
|
|
}
|