/*-
* Copyright (c) 1995, 2000, 2008, 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Frank van der Linden, and by Andrew Doran.
*
* 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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.
*/
/*
* Send an interrupt to process.
*
* Stack is set up to allow sigcode stored
* in u. to call routine, followed by kcall
* to sigreturn routine below. After sigreturn
* resets the signal mask, the stack, and the
* frame pointer, it returns to the user
* specified pc, psl.
*/
tf = l->l_md.md_regs;
/* Do we need to jump onto the signal stack? */
onstack = (sas->ss_flags & (SS_DISABLE | SS_ONSTACK)) == 0 &&
(SIGACTION(p, sig).sa_flags & SA_ONSTACK) != 0;
/* Allocate space for the signal handler context. */
if (onstack)
fp = (struct linux_rt_sigframe *)((char *)sas->ss_sp +
sas->ss_size);
else
fp = (struct linux_rt_sigframe *)tf->tf_esp;
fp--;
/* Build stack frame for signal trampoline. */
frame.sf_handler = catcher;
frame.sf_sig = native_to_linux_signo[sig];
frame.sf_sip = &fp->sf_si;
frame.sf_ucp = &fp->sf_uc;
/*
* XXX: the following code assumes that the constants for
* siginfo are the same between linux and NetBSD.
*/
native_to_linux_siginfo(&frame.sf_si, &ksi->ksi_info);
if (error != 0) {
/*
* Process has trashed its stack; give it an illegal
* instruction to halt it in its tracks.
*/
sigexit(l, SIGILL);
/* NOTREACHED */
}
/* Do we need to jump onto the signal stack? */
onstack = (sas->ss_flags & (SS_DISABLE | SS_ONSTACK)) == 0 &&
(SIGACTION(p, sig).sa_flags & SA_ONSTACK) != 0;
/* Allocate space for the signal handler context. */
if (onstack)
fp = (struct linux_sigframe *) ((char *)sas->ss_sp +
sas->ss_size);
else
fp = (struct linux_sigframe *)tf->tf_esp;
fp--;
if (error != 0) {
/*
* Process has trashed its stack; give it an illegal
* instruction to halt it in its tracks.
*/
sigexit(l, SIGILL);
/* NOTREACHED */
}
/* Remember that we're now on the signal stack. */
if (onstack)
sas->ss_flags |= SS_ONSTACK;
}
/*
* System call to cleanup state after a signal
* has been taken. Reset signal mask and
* stack state from context left by sendsig (above).
* Return to previous pc and psl as specified by
* context left by sendsig. Check carefully to
* make sure that the user has not modified the
* psl to gain improper privileges or to cause
* a machine fault.
*/
int
linux_sys_rt_sigreturn(struct lwp *l, const struct linux_sys_rt_sigreturn_args *uap, register_t *retval)
{
/* {
syscallarg(struct linux_ucontext *) ucp;
} */
struct linux_ucontext context, *ucp = SCARG(uap, ucp);
int error;
/*
* The trampoline code hands us the context.
* It is unsafe to keep track of it ourselves, in the event that a
* program jumps out of a signal handler.
*/
if ((error = copyin(ucp, &context, sizeof(*ucp))) != 0)
return error;
/* XXX XAX we can do better here by using more of the ucontext */
return linux_restore_sigcontext(l, &context.uc_mcontext, retval);
}
/*
* The trampoline code hands us the context.
* It is unsafe to keep track of it ourselves, in the event that a
* program jumps out of a signal handler.
*/
if ((error = copyin((void *)scp, &context, sizeof(*scp))) != 0)
return error;
return linux_restore_sigcontext(l, &context, retval);
}
/*
* Check for security violations. If we're returning to
* protected mode, the CPU will validate the segment registers
* automatically and generate a trap on violations. We handle
* the trap, rather than doing all of the checking here.
*/
if (((scp->sc_eflags ^ tf->tf_eflags) & PSL_USERSTATIC) != 0 ||
!USERMODE(scp->sc_cs))
return EINVAL;
/* Restore signal stack. */
/*
* Linux really does it this way; it doesn't have space in sigframe
* to save the onstack flag.
*/
mutex_enter(p->p_lock);
ss_gap = (ssize_t)((char *)scp->sc_esp_at_signal - (char *)sas->ss_sp);
if (ss_gap >= 0 && ss_gap < sas->ss_size)
sas->ss_flags |= SS_ONSTACK;
else
sas->ss_flags &= ~SS_ONSTACK;
static int
linux_read_ldt(struct lwp *l, const struct linux_sys_modify_ldt_args *uap,
register_t *retval)
{
struct x86_get_ldt_args gl;
int error;
union descriptor *ldt_buf;
size_t sz;
/*
* I've checked the linux code - this function is asymmetric with
* linux_write_ldt, and returns raw ldt entries.
* NB, the code I saw zerod the spare parts of the user buffer.
*/
/*
* XXX Pathetic hack to make svgalib work. This will fake the major
* device number of an opened VT so that svgalib likes it. grmbl.
* Should probably do it 'wrong the right way' and use a mapping
* array for all major device numbers, and map linux_mknod too.
*/
dev_t
linux_fakedev(dev_t dev, int raw)
{
extern const struct cdevsw ptc_cdevsw, pts_cdevsw;
const struct cdevsw *cd = cdevsw_lookup(dev);
for (i = 0; i < dl->dl_nnativedisks; i++) {
nip = &dl->dl_nativedisks[i];
if (strcmp(diskname, nip->ni_devname))
continue;
if (nip->ni_nmatches != 0)
return &dl->dl_biosdisks[nip->ni_biosmatches[0]];
}
return NULL;
}
/*
* We come here in a last attempt to satisfy a Linux ioctl() call
*/
int
linux_machdepioctl(struct lwp *l, const struct linux_sys_ioctl_args *uap, register_t *retval)
{
/* {
syscallarg(int) fd;
syscallarg(u_long) com;
syscallarg(void *) data;
} */
struct sys_ioctl_args bia;
u_long com;
int error, error1;
#if (NWSDISPLAY > 0)
struct vt_mode lvt;
struct kbentry kbe;
#endif
struct linux_hd_geometry hdg;
struct linux_hd_big_geometry hdg_big;
struct biosdisk_info *bip;
file_t *fp;
int fd;
struct disklabel label;
struct partinfo partp;
int (*ioctlf)(struct file *, u_long, void *);
u_long start, biostotal, realtotal;
u_char heads, sectors;
u_int cylinders;
struct ioctl_pt pt;
if ((fp = fd_getfile(fd)) == NULL)
return (EBADF);
switch (com) {
#if (NWSDISPLAY > 0)
case LINUX_KDGKBMODE:
com = KDGKBMODE;
break;
case LINUX_KDSKBMODE:
com = KDSKBMODE;
if ((unsigned)SCARG(uap, data) == LINUX_K_MEDIUMRAW)
SCARG(&bia, data) = (void *)K_RAW;
break;
case LINUX_KIOCSOUND:
SCARG(&bia, data) =
(void *)(((unsigned long)SCARG(&bia, data)) & 0xffff);
/* fall through */
case LINUX_KDMKTONE:
com = KDMKTONE;
break;
case LINUX_KDSETMODE:
com = KDSETMODE;
break;
case LINUX_KDGETMODE:
/* KD_* values are equal to the wscons numbers */
com = WSDISPLAYIO_GMODE;
break;
case LINUX_KDENABIO:
com = KDENABIO;
break;
case LINUX_KDDISABIO:
com = KDDISABIO;
break;
case LINUX_KDGETLED:
com = KDGETLED;
break;
case LINUX_KDSETLED:
com = KDSETLED;
break;
case LINUX_VT_OPENQRY:
com = VT_OPENQRY;
break;
case LINUX_VT_GETMODE:
memset(&lvt, 0, sizeof(lvt));
error = fp->f_ops->fo_ioctl(fp, VT_GETMODE, &lvt);
if (error != 0)
goto out;
lvt.relsig = native_to_linux_signo[lvt.relsig];
lvt.acqsig = native_to_linux_signo[lvt.acqsig];
lvt.frsig = native_to_linux_signo[lvt.frsig];
error = copyout(&lvt, SCARG(uap, data), sizeof (lvt));
goto out;
case LINUX_VT_SETMODE:
error = copyin(SCARG(uap, data), &lvt, sizeof (lvt));
if (error != 0)
goto out;
lvt.relsig = linux_to_native_signo[lvt.relsig];
lvt.acqsig = linux_to_native_signo[lvt.acqsig];
lvt.frsig = linux_to_native_signo[lvt.frsig];
error = fp->f_ops->fo_ioctl(fp, VT_SETMODE, &lvt);
goto out;
case LINUX_VT_DISALLOCATE:
/* XXX should use WSDISPLAYIO_DELSCREEN */
error = 0;
goto out;
case LINUX_VT_RELDISP:
com = VT_RELDISP;
break;
case LINUX_VT_ACTIVATE:
com = VT_ACTIVATE;
break;
case LINUX_VT_WAITACTIVE:
com = VT_WAITACTIVE;
break;
case LINUX_VT_GETSTATE:
com = VT_GETSTATE;
break;
case LINUX_KDGKBTYPE:
{
static const u_int8_t kb101 = KB_101;
/* This is what Linux does. */
error = copyout(&kb101, SCARG(uap, data), 1);
goto out;
}
case LINUX_KDGKBENT:
/*
* The Linux KDGKBENT ioctl is different from the
* SYSV original. So we handle it in machdep code.
* XXX We should use keyboard mapping information
* from wsdisplay, but this would be expensive.
*/
if ((error = copyin(SCARG(uap, data), &kbe,
sizeof(struct kbentry))))
goto out;
if (kbe.kb_table >= sizeof(linux_keytabs) / sizeof(u_short *)
|| kbe.kb_index >= NR_KEYS) {
error = EINVAL;
goto out;
}
kbe.kb_value = linux_keytabs[kbe.kb_table][kbe.kb_index];
error = copyout(&kbe, SCARG(uap, data),
sizeof(struct kbentry));
goto out;
#endif
case LINUX_HDIO_GETGEO:
case LINUX_HDIO_GETGEO_BIG:
/*
* Try to mimic Linux behaviour: return the BIOS geometry
* if possible (extending its # of cylinders if it's beyond
* the 1023 limit), fall back to the MI geometry (i.e.
* the real geometry) if not found, by returning an
* error. See common/linux_hdio.c
*/
bip = fd2biosinfo(curproc, fp);
ioctlf = fp->f_ops->fo_ioctl;
error = ioctlf(fp, DIOCGDINFO, (void *)&label);
error1 = ioctlf(fp, DIOCGPARTINFO, (void *)&partp);
if (error != 0 && error1 != 0) {
error = error1;
goto out;
}
start = error1 != 0 ? partp.pi_offset : 0;
if (bip != NULL && bip->bi_head != 0 && bip->bi_sec != 0
&& bip->bi_cyl != 0) {
heads = bip->bi_head;
sectors = bip->bi_sec;
cylinders = bip->bi_cyl;
biostotal = heads * sectors * cylinders;
realtotal = label.d_ntracks * label.d_nsectors *
label.d_ncylinders;
if (realtotal > biostotal)
cylinders = realtotal / (heads * sectors);
} else {
heads = label.d_ntracks;
cylinders = label.d_ncylinders;
sectors = label.d_nsectors;
}
if (com == LINUX_HDIO_GETGEO) {
memset(&hdg, 0, sizeof(hdg));
hdg.start = start;
hdg.heads = heads;
hdg.cylinders = cylinders;
hdg.sectors = sectors;
error = copyout(&hdg, SCARG(uap, data), sizeof hdg);
goto out;
} else {
memset(&hdg_big, 0, sizeof(hdg_big));
hdg_big.start = start;
hdg_big.heads = heads;
hdg_big.cylinders = cylinders;
hdg_big.sectors = sectors;
error = copyout(&hdg_big, SCARG(uap, data),
sizeof hdg_big);
goto out;
}
default:
/*
* Unknown to us. If it's on a device, just pass it through
* using PTIOCLINUX, the device itself might be able to
* make some sense of it.
* XXX hack: if the function returns EJUSTRETURN,
* it has stuffed a sysctl return value in pt.data.
*/
ioctlf = fp->f_ops->fo_ioctl;
pt.com = SCARG(uap, com);
pt.data = SCARG(uap, data);
error = ioctlf(fp, PTIOCLINUX, &pt);
if (error == EJUSTRETURN) {
retval[0] = (register_t)pt.data;
error = 0;
}
/*
* Set I/O permissions for a process. Just set the maximum level
* right away (ignoring the argument), otherwise we would have
* to rely on I/O permission maps, which are not implemented.
*/
int
linux_sys_iopl(struct lwp *l, const struct linux_sys_iopl_args *uap, register_t *retval)
{
/* {
syscallarg(int) level;
} */
struct trapframe *fp = l->l_md.md_regs;
/*
* See above. If a root process tries to set access to an I/O port,
* just let it have the whole range.
*/
int
linux_sys_ioperm(struct lwp *l, const struct linux_sys_ioperm_args *uap, register_t *retval)
{
/* {
syscallarg(unsigned int) lo;
syscallarg(unsigned int) hi;
syscallarg(int) val;
} */
struct trapframe *fp = l->l_md.md_regs;