diff options
author | marcel <marcel@FreeBSD.org> | 2004-09-25 04:27:44 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2004-09-25 04:27:44 +0000 |
commit | 883da4df6b30c6a6a1889e38615386b37af594de (patch) | |
tree | 8de6820f7af882f731e1d689d5d242fb96cc1bc7 /sys/ia64/ia32/ia32_trap.c | |
parent | 954ae57b986db4b14140e76aebb189c88e5634d8 (diff) | |
download | FreeBSD-src-883da4df6b30c6a6a1889e38615386b37af594de.zip FreeBSD-src-883da4df6b30c6a6a1889e38615386b37af594de.tar.gz |
Move the IA-32 trap handling from trap() to ia32_trap(). Move the
ia32_syscall() function along with it to ia32_trap.c. When COMPAT_IA32
is not defined, we'll raise SIGEMT instead.
Diffstat (limited to 'sys/ia64/ia32/ia32_trap.c')
-rw-r--r-- | sys/ia64/ia32/ia32_trap.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/sys/ia64/ia32/ia32_trap.c b/sys/ia64/ia32/ia32_trap.c new file mode 100644 index 0000000..cbc6c09 --- /dev/null +++ b/sys/ia64/ia32/ia32_trap.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2004 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/ktr.h> +#include <sys/sysproto.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/pioctl.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/syscall.h> +#include <sys/sysent.h> +#include <machine/fpu.h> +#include <machine/frame.h> +#include <machine/md_var.h> +#include <i386/include/psl.h> + +static void +ia32_syscall(struct trapframe *tf) +{ + uint64_t args64[8]; + uint32_t args[8]; + struct thread *td; + struct proc *p; + struct sysent *callp; + caddr_t params; + register_t eflags; + u_int code; + int error, i, narg; + + atomic_add_int(&cnt.v_syscall, 1); + + td = curthread; + params = (caddr_t)(tf->tf_special.sp & ((1L<<32)-1)) + + sizeof(uint32_t); + code = tf->tf_scratch.gr8; /* eax */ + eflags = ia64_get_eflag(); + p = td->td_proc; + + if (p->p_sysent->sv_prepsyscall == NULL) { + if (code == SYS_syscall) { + /* Code is first argument, followed by actual args. */ + code = fuword32(params); + params += sizeof(int); + } else if (code == SYS___syscall) { + /* + * Like syscall, but code is a quad, so as to maintain + * quad alignment for the rest of the arguments. We + * use a 32-bit fetch in case params is not aligned. + */ + code = fuword32(params); + params += sizeof(quad_t); + } + } else + (*p->p_sysent->sv_prepsyscall)(tf, args, &code, ¶ms); + + if (p->p_sysent->sv_mask) + code &= p->p_sysent->sv_mask; + + if (code >= p->p_sysent->sv_size) + callp = &p->p_sysent->sv_table[0]; + else + callp = &p->p_sysent->sv_table[code]; + + narg = callp->sy_narg & SYF_ARGMASK; + + /* copyin and the ktrsyscall()/ktrsysret() code is MP-aware */ + if (params != NULL && narg != 0) + error = copyin(params, (caddr_t)args, narg * sizeof(int)); + else + error = 0; + + for (i = 0; i < narg; i++) + args64[i] = args[i]; + +#ifdef KTRACE + if (KTRPOINT(td, KTR_SYSCALL)) + ktrsyscall(code, narg, args64); +#endif + /* + * Try to run the syscall without Giant if the syscall + * is MP safe. + */ + if ((callp->sy_narg & SYF_MPSAFE) == 0) + mtx_lock(&Giant); + + if (error == 0) { + td->td_retval[0] = 0; + td->td_retval[1] = tf->tf_scratch.gr10; /* edx */ + + STOPEVENT(p, S_SCE, narg); + + error = (*callp->sy_call)(td, args64); + } + + switch (error) { + case 0: + tf->tf_scratch.gr8 = td->td_retval[0]; /* eax */ + tf->tf_scratch.gr10 = td->td_retval[1]; /* edx */ + ia64_set_eflag(ia64_get_eflag() & ~PSL_C); + break; + + case ERESTART: + /* + * Reconstruct pc, assuming lcall $X,y is 7 bytes, + * int 0x80 is 2 bytes. XXX Assume int 0x80. + */ + tf->tf_special.iip -= 2; + break; + + case EJUSTRETURN: + break; + + default: + if (p->p_sysent->sv_errsize) { + if (error >= p->p_sysent->sv_errsize) + error = -1; /* XXX */ + else + error = p->p_sysent->sv_errtbl[error]; + } + tf->tf_scratch.gr8 = error; + ia64_set_eflag(ia64_get_eflag() | PSL_C); + break; + } + + /* + * Release Giant if we previously set it. + */ + if ((callp->sy_narg & SYF_MPSAFE) == 0) + mtx_unlock(&Giant); + + /* + * Traced syscall. + */ + if ((eflags & PSL_T) && !(eflags & PSL_VM)) { + ia64_set_eflag(ia64_get_eflag() & ~PSL_T); + trapsignal(td, SIGTRAP, 0); + } + +#ifdef KTRACE + if (KTRPOINT(td, KTR_SYSRET)) + ktrsysret(code, error, td->td_retval[0]); +#endif + + /* + * This works because errno is findable through the + * register set. If we ever support an emulation where this + * is not the case, this code will need to be revisited. + */ + STOPEVENT(p, S_SCX, code); + + WITNESS_WARN(WARN_PANIC, NULL, "System call %s returning", + (code >= 0 && code < SYS_MAXSYSCALL) ? syscallnames[code] : "???"); + mtx_assert(&sched_lock, MA_NOTOWNED); + mtx_assert(&Giant, MA_NOTOWNED); +} + +/* + * ia32_trap() is called from exception.S to handle the IA-32 specific + * interruption vectors. + */ +void +ia32_trap(int vector, struct trapframe *tf) +{ + struct proc *p; + struct thread *td; + uint64_t ucode; + int sig; + u_int sticks; + + KASSERT(TRAPF_USERMODE(tf), ("%s: In kernel mode???", __func__)); + + ia64_set_fpsr(IA64_FPSR_DEFAULT); + atomic_add_int(&cnt.v_trap, 1); + + td = curthread; + td->td_frame = tf; + sticks = td->td_sticks; + p = td->td_proc; + if (td->td_ucred != p->p_ucred) + cred_update_thread(td); + sig = 0; + ucode = 0; + switch (vector) { + case IA64_VEC_IA32_EXCEPTION: + switch ((tf->tf_special.isr >> 16) & 0xffff) { + case IA32_EXCEPTION_DIVIDE: + ucode = FPE_INTDIV; + sig = SIGFPE; + break; + case IA32_EXCEPTION_DEBUG: + case IA32_EXCEPTION_BREAK: + sig = SIGTRAP; + break; + case IA32_EXCEPTION_OVERFLOW: + ucode = FPE_INTOVF; + sig = SIGFPE; + break; + case IA32_EXCEPTION_BOUND: + ucode = FPE_FLTSUB; + sig = SIGFPE; + break; + case IA32_EXCEPTION_DNA: + ucode = 0; + sig = SIGFPE; + break; + case IA32_EXCEPTION_NOT_PRESENT: + case IA32_EXCEPTION_STACK_FAULT: + case IA32_EXCEPTION_GPFAULT: + ucode = (tf->tf_special.isr & 0xffff) + BUS_SEGM_FAULT; + sig = SIGBUS; + break; + case IA32_EXCEPTION_FPERROR: + ucode = 0; /* XXX */ + sig = SIGFPE; + break; + case IA32_EXCEPTION_ALIGNMENT_CHECK: + ucode = tf->tf_special.ifa; /* VA */ + sig = SIGBUS; + break; + case IA32_EXCEPTION_STREAMING_SIMD: + ucode = 0; /* XXX */ + sig = SIGFPE; + break; + default: + trap_panic(vector, tf); + break; + } + break; + + case IA64_VEC_IA32_INTERCEPT: + /* XXX Maybe need to emulate ia32 instruction. */ + trap_panic(vector, tf); + + case IA64_VEC_IA32_INTERRUPT: + /* INT n instruction - probably a syscall. */ + if (((tf->tf_special.isr >> 16) & 0xffff) == 0x80) { + ia32_syscall(tf); + goto out; + } + ucode = (tf->tf_special.isr >> 16) & 0xffff; + sig = SIGILL; + break; + + default: + /* Should never happen of course. */ + trap_panic(vector, tf); + break; + } + + KASSERT(sig != 0, ("%s: signal not set", __func__)); + + trapsignal(td, sig, ucode); + +out: + userret(td, tf, sticks); + mtx_assert(&Giant, MA_NOTOWNED); + do_ast(tf); +} |