Commit 0a635c7a authored by Richard Henderson's avatar Richard Henderson Committed by Richard Henderson

Fill in siginfo_t.

parent 9527cd1e
......@@ -844,7 +844,7 @@ osf_setsysinfo(unsigned long op, void *buffer, unsigned long nbytes,
{
switch (op) {
case SSI_IEEE_FP_CONTROL: {
unsigned long swcr, fpcr;
unsigned long swcr, fpcr, fex;
/*
* Alpha Architecture Handbook 4.7.7.3:
......@@ -867,9 +867,24 @@ osf_setsysinfo(unsigned long op, void *buffer, unsigned long nbytes,
wrfpcr(fpcr);
/* If any exceptions are now unmasked, send a signal. */
if (((swcr & IEEE_STATUS_MASK)
>> IEEE_STATUS_TO_EXCSUM_SHIFT) & swcr) {
send_sig(SIGFPE, current, 1);
fex = ((swcr & IEEE_STATUS_MASK)
>> IEEE_STATUS_TO_EXCSUM_SHIFT) & swcr;
if (fex) {
siginfo_t info;
int si_code = 0;
if (fex & IEEE_TRAP_ENABLE_DNO) si_code = FPE_FLTUND;
if (fex & IEEE_TRAP_ENABLE_INE) si_code = FPE_FLTRES;
if (fex & IEEE_TRAP_ENABLE_UNF) si_code = FPE_FLTUND;
if (fex & IEEE_TRAP_ENABLE_OVF) si_code = FPE_FLTOVF;
if (fex & IEEE_TRAP_ENABLE_DZE) si_code = FPE_FLTDIV;
if (fex & IEEE_TRAP_ENABLE_INV) si_code = FPE_FLTINV;
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_code = si_code;
info.si_addr = 0; /* FIXME */
send_sig_info(SIGFPE, &info, current);
}
return 0;
......
......@@ -293,8 +293,16 @@ do_sigreturn(struct sigframe *frame, struct pt_regs *regs,
goto give_sigsegv;
/* Send SIGTRAP if we're single-stepping: */
if (ptrace_cancel_bpt (current))
send_sig(SIGTRAP, current, 1);
if (ptrace_cancel_bpt (current)) {
siginfo_t info;
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
info.si_addr = (void *) regs->pc;
info.si_trapno = 0;
send_sig_info(SIGTRAP, &info, current);
}
return;
give_sigsegv:
......@@ -330,8 +338,16 @@ do_rt_sigreturn(struct rt_sigframe *frame, struct pt_regs *regs,
do_sigaltstack(&st, NULL, rdusp());
/* Send SIGTRAP if we're single-stepping: */
if (ptrace_cancel_bpt (current))
send_sig(SIGTRAP, current, 1);
if (ptrace_cancel_bpt (current)) {
siginfo_t info;
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
info.si_addr = (void *) regs->pc;
info.si_trapno = 0;
send_sig_info(SIGTRAP, &info, current);
}
return;
give_sigsegv:
......
......@@ -213,25 +213,25 @@ do_entArith(unsigned long summary, unsigned long write_mask,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, struct pt_regs regs)
{
long si_code = FPE_FLTINV;
siginfo_t info;
if (summary & 1) {
/* Software-completion summary bit is set, so try to
emulate the instruction. */
if (!amask(AMASK_PRECISE_TRAP)) {
/* 21264 (except pass 1) has precise exceptions. */
if (alpha_fp_emul(regs.pc - 4))
return;
} else {
if (alpha_fp_emul_imprecise(&regs, write_mask))
return;
}
emulate the instruction. If the processor supports
precise exceptions, we don't have to search. */
if (!amask(AMASK_PRECISE_TRAP))
si_code = alpha_fp_emul(regs.pc - 4);
else
si_code = alpha_fp_emul_imprecise(&regs, write_mask);
}
#if 0
printk("%s: arithmetic trap at %016lx: %02lx %016lx\n",
current->comm, regs.pc, summary, write_mask);
#endif
die_if_kernel("Arithmetic fault", &regs, 0, 0);
send_sig(SIGFPE, current, 1);
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_code = si_code;
info.si_addr = (void *) regs.pc;
send_sig_info(SIGFPE, &info, current);
}
asmlinkage void
......@@ -239,6 +239,9 @@ do_entIF(unsigned long type, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, struct pt_regs regs)
{
siginfo_t info;
int signo, code;
if (!opDEC_testing || type != 4) {
if (type == 1) {
const unsigned int *data
......@@ -253,55 +256,99 @@ do_entIF(unsigned long type, unsigned long a1,
switch (type) {
case 0: /* breakpoint */
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
info.si_trapno = 0;
info.si_addr = (void *) regs.pc;
if (ptrace_cancel_bpt(current)) {
regs.pc -= 4; /* make pc point to former bpt */
}
send_sig(SIGTRAP, current, 1);
send_sig_info(SIGTRAP, &info, current);
return;
case 1: /* bugcheck */
send_sig(SIGTRAP, current, 1);
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = __SI_FAULT;
info.si_addr = (void *) regs.pc;
info.si_trapno = 0;
send_sig_info(SIGTRAP, &info, current);
return;
case 2: /* gentrap */
/*
* The exception code should be passed on to the signal
* handler as the second argument. Linux doesn't do that
* yet (also notice that Linux *always* behaves like
* DEC Unix with SA_SIGINFO off; see DEC Unix man page
* for sigaction(2)).
*/
info.si_addr = (void *) regs.pc;
info.si_trapno = regs.r16;
switch ((long) regs.r16) {
case GEN_INTOVF: case GEN_INTDIV: case GEN_FLTOVF:
case GEN_FLTDIV: case GEN_FLTUND: case GEN_FLTINV:
case GEN_FLTINE: case GEN_ROPRAND:
send_sig(SIGFPE, current, 1);
return;
case GEN_DECOVF:
case GEN_DECDIV:
case GEN_DECINV:
case GEN_ASSERTERR:
case GEN_NULPTRERR:
case GEN_STKOVF:
case GEN_STRLENERR:
case GEN_SUBSTRERR:
case GEN_RANGERR:
case GEN_SUBRNG:
case GEN_SUBRNG1:
case GEN_SUBRNG2:
case GEN_SUBRNG3:
case GEN_SUBRNG4:
case GEN_SUBRNG5:
case GEN_SUBRNG6:
case GEN_SUBRNG7:
send_sig(SIGTRAP, current, 1);
return;
case GEN_INTOVF:
signo = SIGFPE;
code = FPE_INTOVF;
break;
case GEN_INTDIV:
signo = SIGFPE;
code = FPE_INTDIV;
break;
case GEN_FLTOVF:
signo = SIGFPE;
code = FPE_FLTOVF;
break;
case GEN_FLTDIV:
signo = SIGFPE;
code = FPE_FLTDIV;
break;
case GEN_FLTUND:
signo = SIGFPE;
code = FPE_FLTUND;
break;
case GEN_FLTINV:
signo = SIGFPE;
code = FPE_FLTINV;
break;
case GEN_FLTINE:
signo = SIGFPE;
code = FPE_FLTRES;
break;
case GEN_ROPRAND:
signo = SIGFPE;
code = __SI_FAULT;
break;
case GEN_DECOVF:
case GEN_DECDIV:
case GEN_DECINV:
case GEN_ASSERTERR:
case GEN_NULPTRERR:
case GEN_STKOVF:
case GEN_STRLENERR:
case GEN_SUBSTRERR:
case GEN_RANGERR:
case GEN_SUBRNG:
case GEN_SUBRNG1:
case GEN_SUBRNG2:
case GEN_SUBRNG3:
case GEN_SUBRNG4:
case GEN_SUBRNG5:
case GEN_SUBRNG6:
case GEN_SUBRNG7:
default:
signo = SIGTRAP;
code = __SI_FAULT;
break;
}
break;
info.si_signo = signo;
info.si_errno = 0;
info.si_code = code;
info.si_addr = (void *) regs.pc;
send_sig_info(signo, &info, current);
return;
case 4: /* opDEC */
if (implver() == IMPLVER_EV4) {
long si_code;
/* The some versions of SRM do not handle
the opDEC properly - they return the PC of the
opDEC fault, not the instruction after as the
......@@ -309,8 +356,7 @@ do_entIF(unsigned long type, unsigned long a1,
We do this by intentionally causing an opDEC
fault during the boot sequence and testing if
we get the correct PC. If not, we set a flag
to correct it every time through.
*/
to correct it every time through. */
if (opDEC_testing) {
if (regs.pc == opDEC_test_pc) {
opDEC_fix = 4;
......@@ -324,8 +370,17 @@ do_entIF(unsigned long type, unsigned long a1,
/* EV4 does not implement anything except normal
rounding. Everything else will come here as
an illegal instruction. Emulate them. */
if (alpha_fp_emul(regs.pc-4))
si_code = alpha_fp_emul(regs.pc - 4);
if (si_code == 0)
return;
if (si_code > 0) {
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_code = si_code;
info.si_addr = (void *) regs.pc;
send_sig_info(SIGFPE, &info, current);
return;
}
}
break;
......@@ -347,7 +402,12 @@ do_entIF(unsigned long type, unsigned long a1,
default: /* unexpected instruction-fault type */
;
}
send_sig(SIGILL, current, 1);
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLOPC;
info.si_addr = regs.pc;
send_sig_info(SIGILL, &info, current);
}
/* There is an ifdef in the PALcode in MILO that enables a
......@@ -362,8 +422,15 @@ do_entDbg(unsigned long type, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, struct pt_regs regs)
{
siginfo_t info;
die_if_kernel("Instruction fault", &regs, type, 0);
force_sig(SIGILL, current);
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLOPC;
info.si_addr = regs.pc;
force_sig_info(SIGILL, &info, current);
}
......@@ -720,6 +787,7 @@ do_entUnaUser(void * va, unsigned long opcode,
unsigned long tmp1, tmp2, tmp3, tmp4;
unsigned long fake_reg, *reg_addr = &fake_reg;
siginfo_t info;
long error;
/* Check the UAC bits to decide what the user wants us to do
......@@ -984,12 +1052,34 @@ do_entUnaUser(void * va, unsigned long opcode,
give_sigsegv:
regs->pc -= 4; /* make pc point to faulting insn */
send_sig(SIGSEGV, current, 1);
info.si_signo = SIGSEGV;
info.si_errno = 0;
/* We need to replicate some of the logic in mm/fault.c,
since we don't have access to the fault code in the
exception handling return path. */
if (!__access_ok((unsigned long)va, 0, USER_DS))
info.si_code = SEGV_ACCERR;
else {
struct mm_struct *mm = current->mm;
down_read(&mm->mmap_sem);
if (find_vma(mm, (unsigned long)va))
info.si_code = SEGV_ACCERR;
else
info.si_code = SEGV_MAPERR;
up_read(&mm->mmap_sem);
}
info.si_addr = va;
send_sig_info(SIGSEGV, &info, current);
return;
give_sigbus:
regs->pc -= 4;
send_sig(SIGBUS, current, 1);
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRALN;
info.si_addr = va;
send_sig_info(SIGBUS, &info, current);
return;
}
......
......@@ -86,11 +86,13 @@ void cleanup_module(void)
/*
* Emulate the floating point instruction at address PC. Returns 0 if
* emulation fails. Notice that the kernel does not and cannot use FP
* regs. This is good because it means that instead of
* saving/restoring all fp regs, we simply stick the result of the
* operation into the appropriate register.
* Emulate the floating point instruction at address PC. Returns -1 if the
* instruction to be emulated is illegal (such as with the opDEC trap), else
* the SI_CODE for a SIGFPE signal, else 0 if everything's ok.
*
* Notice that the kernel does not and cannot use FP regs. This is good
* because it means that instead of saving/restoring all fp regs, we simply
* stick the result of the operation into the appropriate register.
*/
long
alpha_fp_emul (unsigned long pc)
......@@ -102,6 +104,7 @@ alpha_fp_emul (unsigned long pc)
unsigned long fa, fb, fc, func, mode, src;
unsigned long res, va, vb, vc, swcr, fpcr;
__u32 insn;
long si_code;
MOD_INC_USE_COUNT;
......@@ -306,10 +309,19 @@ alpha_fp_emul (unsigned long pc)
wrfpcr(fpcr);
/* Do we generate a signal? */
if (_fex & swcr & IEEE_TRAP_ENABLE_MASK) {
MOD_DEC_USE_COUNT;
return 0;
_fex = _fex & swcr & IEEE_TRAP_ENABLE_MASK;
si_code = 0;
if (_fex) {
if (_fex & IEEE_TRAP_ENABLE_DNO) si_code = FPE_FLTUND;
if (_fex & IEEE_TRAP_ENABLE_INE) si_code = FPE_FLTRES;
if (_fex & IEEE_TRAP_ENABLE_UNF) si_code = FPE_FLTUND;
if (_fex & IEEE_TRAP_ENABLE_OVF) si_code = FPE_FLTOVF;
if (_fex & IEEE_TRAP_ENABLE_DZE) si_code = FPE_FLTDIV;
if (_fex & IEEE_TRAP_ENABLE_INV) si_code = FPE_FLTINV;
}
MOD_DEC_USE_COUNT;
return si_code;
}
/* We used to write the destination register here, but DEC FORTRAN
......@@ -317,20 +329,20 @@ alpha_fp_emul (unsigned long pc)
immediately after the operations above. */
MOD_DEC_USE_COUNT;
return 1;
return 0;
bad_insn:
printk(KERN_ERR "alpha_fp_emul: Invalid FP insn %#x at %#lx\n",
insn, pc);
MOD_DEC_USE_COUNT;
return 0;
return -1;
}
long
alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
{
unsigned long trigger_pc = regs->pc - 4;
unsigned long insn, opcode, rc, no_signal = 0;
unsigned long insn, opcode, rc, si_code = 0;
MOD_INC_USE_COUNT;
......@@ -384,7 +396,7 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
if (!write_mask) {
/* Re-execute insns in the trap-shadow. */
regs->pc = trigger_pc + 4;
no_signal = alpha_fp_emul(trigger_pc);
si_code = alpha_fp_emul(trigger_pc);
goto egress;
}
trigger_pc -= 4;
......@@ -392,5 +404,5 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
egress:
MOD_DEC_USE_COUNT;
return no_signal;
return si_code;
}
......@@ -89,7 +89,8 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
struct vm_area_struct * vma;
struct mm_struct *mm = current->mm;
unsigned int fixup;
int fault;
int fault, si_code = SEGV_MAPERR;
siginfo_t info;
/* As of EV6, a load into $31/$f31 is a prefetch, and never faults
(or is suppressed by the PALcode). Support that for older CPUs
......@@ -129,6 +130,7 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
/* Ok, we have a good vm_area for this memory access, so
we can handle it. */
good_area:
si_code = SEGV_ACCERR;
if (cause < 0) {
if (!(vma->vm_flags & VM_EXEC))
goto bad_area;
......@@ -148,10 +150,20 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
fault = handle_mm_fault(mm, vma, address, cause > 0);
up_read(&mm->mmap_sem);
if (fault < 0)
goto out_of_memory;
if (fault == 0)
switch (fault) {
case VM_FAULT_MINOR:
current->min_flt++;
break;
case VM_FAULT_MAJOR:
current->maj_flt++;
break;
case VM_FAULT_SIGBUS:
goto do_sigbus;
case VM_FAULT_OOM:
goto out_of_memory;
default:
BUG();
}
return;
/* Something tried to access memory that isn't in our memory map.
......@@ -159,20 +171,14 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
bad_area:
up_read(&mm->mmap_sem);
if (user_mode(regs)) {
force_sig(SIGSEGV, current);
return;
}
if (user_mode(regs))
goto do_sigsegv;
no_context:
/* Are we prepared to handle this fault as an exception? */
if ((fixup = search_exception_table(regs->pc, regs->gp)) != 0) {
unsigned long newpc;
newpc = fixup_exception(dpf_reg, fixup, regs->pc);
#if 0
printk("%s: Exception at [<%lx>] (%lx) handled successfully\n",
current->comm, regs->pc, newpc);
#endif
regs->pc = newpc;
return;
}
......@@ -201,17 +207,28 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
do_sigbus:
/* Send a sigbus, regardless of whether we were in kernel
or user mode. */
force_sig(SIGBUS, current);
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRERR;
info.si_addr = (void *) address;
force_sig_info(SIGBUS, &info, current);
if (!user_mode(regs))
goto no_context;
return;
do_sigsegv:
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = si_code;
info.si_addr = (void *) address;
force_sig_info(SIGSEGV, &info, current);
return;
#ifdef CONFIG_ALPHA_LARGE_VMALLOC
vmalloc_fault:
if (user_mode(regs)) {
force_sig(SIGSEGV, current);
return;
} else {
if (user_mode(regs))
goto do_sigsegv;
else {
/* Synchronize this task's top level page-table
with the "reference" page table from init. */
long offset = __pgd_offset(address);
......
#ifndef _ALPHA_SIGINFO_H
#define _ALPHA_SIGINFO_H
#define SI_PAD_SIZE ((SI_MAX_SIZE/sizeof(int)) - 4)
#define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int))
#define __ARCH_SI_TRAPNO
#define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE/sizeof(int)) - 4)
#define HAVE_ARCH_COPY_SIGINFO
#define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE/sizeof(int)) - 4)
#include <asm-generic/siginfo.h>
#ifdef __KERNEL__
#include <linux/string.h>
extern inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
{
if (from->si_code < 0)
memcpy(to, from, sizeof(siginfo_t));
else
/* _sigchld is currently the largest know union member */
memcpy(to, from, 4*sizeof(int) + sizeof(from->_sifields._sigchld));
}
#endif /* __KERNEL__ */
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment