freebsd-dev/sys/alpha/linux/linux_machdep.c
Andrew Gallatin 922683e01a Enable linux thread support on the alpha. The guts of linux_clone was
mainly cut-n-pasted from the i386 port, except for the method of setting
the child's stack which is the only MD part of this function.

I've tested with the example apps shipped with the linux threads source
code (ex1-ex6) and with several binary builds of Mozilla.
2000-11-10 23:04:31 +00:00

413 lines
10 KiB
C

/*-
* Copyright (c) 2000 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
* in this position and unchanged.
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/sysproto.h>
#include <sys/systm.h>
#include <sys/unistd.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <alpha/linux/linux.h>
#include <linux_proto.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_util.h>
struct linux_select_argv {
int nfds;
fd_set *readfds;
fd_set *writefds;
fd_set *exceptfds;
struct timeval *timeout;
};
int
linux_execve(struct proc *p, struct linux_execve_args *args)
{
struct execve_args bsd;
caddr_t sg;
sg = stackgap_init();
CHECKALTEXIST(p, &sg, args->path);
#ifdef DEBUG
printf("Linux-emul(%d): execve(%s)\n",
p->p_pid, args->path);
#endif
bsd.fname = args->path;
bsd.argv = args->argp;
bsd.envv = args->envp;
return (execve(p, &bsd));
}
int
linux_fork(struct proc *p, struct linux_fork_args *args)
{
int error;
#ifdef DEBUG
printf("Linux-emul(%ld): fork()\n", (long)p->p_pid);
#endif
if ((error = fork(p, (struct fork_args *)args)) != 0)
return (error);
if (p->p_retval[1] == 1)
p->p_retval[0] = 0;
return (0);
}
int
linux_vfork(struct proc *p, struct linux_vfork_args *args)
{
int error;
#ifdef DEBUG
printf("Linux-emul(%ld): vfork()\n", (long)p->p_pid);
#endif
if ((error = vfork(p, (struct vfork_args *)args)) != 0)
return (error);
/* Are we the child? */
if (p->p_retval[1] == 1)
p->p_retval[0] = 0;
return (0);
}
#define CLONE_VM 0x100
#define CLONE_FS 0x200
#define CLONE_FILES 0x400
#define CLONE_SIGHAND 0x800
#define CLONE_PID 0x1000
int
linux_clone(struct proc *p, struct linux_clone_args *args)
{
int error, ff = RFPROC;
struct proc *p2;
int exit_signal;
vm_offset_t start;
struct rfork_args rf_args;
#ifdef DEBUG
if (args->flags & CLONE_PID)
printf("linux_clone(%ld): CLONE_PID not yet supported\n",
(long)p->p_pid);
uprintf("linux_clone(%ld): invoked with flags 0x%x and stack %p\n",
(long)p->p_pid, (unsigned int)args->flags,
args->stack);
#endif
if (!args->stack)
return (EINVAL);
exit_signal = args->flags & 0x000000ff;
if (exit_signal >= LINUX_NSIG)
return (EINVAL);
/* if (exit_signal <= LINUX_SIGTBLSZ)
exit_signal = linux_to_bsd_signal[_SIG_IDX(exit_signal)];
*/
/* RFTHREAD probably not necessary here, but it shouldn't hurt */
ff |= RFTHREAD;
if (args->flags & CLONE_VM)
ff |= RFMEM;
if (args->flags & CLONE_SIGHAND)
ff |= RFSIGSHARE;
if (!(args->flags & CLONE_FILES))
ff |= RFFDG;
error = 0;
start = 0;
rf_args.flags = ff;
if ((error = rfork(p, &rf_args)) != 0)
return (error);
p2 = pfind(p->p_retval[0]);
if (p2 == 0)
return (ESRCH);
p2->p_sigparent = exit_signal;
p2->p_addr->u_pcb.pcb_hw.apcb_usp = (unsigned long)args->stack;
#ifdef DEBUG
uprintf ("linux_clone(%ld): successful rfork to %ld, stack %p, sig = %d\n",
(long)p->p_pid, (long)p2->p_pid, args->stack, exit_signal);
#endif
return (0);
}
#define STACK_SIZE (2 * 1024 * 1024)
#define GUARD_SIZE (4 * PAGE_SIZE)
int
linux_mmap(struct proc *p, struct linux_mmap_args *linux_args)
{
struct mmap_args /* {
caddr_t addr;
size_t len;
int prot;
int flags;
int fd;
long pad;
off_t pos;
} */ bsd_args;
int error;
#ifdef DEBUG
printf("Linux-emul(%ld): mmap(%p, 0x%lx, 0x%x, 0x%x, 0x%x, 0x%lx)\n",
(long)p->p_pid, (void *)linux_args->addr, linux_args->len,
linux_args->prot, linux_args->flags, linux_args->fd, linux_args->pos);
#endif
bsd_args.prot = linux_args->prot | PROT_READ; /* always required */
bsd_args.flags = 0;
if (linux_args->flags & LINUX_MAP_SHARED)
bsd_args.flags |= MAP_SHARED;
if (linux_args->flags & LINUX_MAP_PRIVATE)
bsd_args.flags |= MAP_PRIVATE;
if (linux_args->flags & LINUX_MAP_FIXED){
bsd_args.flags |= MAP_FIXED;
bsd_args.pos = trunc_page(linux_args->pos);
} else {
bsd_args.pos = linux_args->pos;
}
if (linux_args->flags & LINUX_MAP_ANON)
bsd_args.flags |= MAP_ANON;
if (linux_args->flags & LINUX_MAP_GROWSDOWN) {
bsd_args.flags |= MAP_STACK;
/* The linux MAP_GROWSDOWN option does not limit auto
* growth of the region. Linux mmap with this option
* takes as addr the inital BOS, and as len, the initial
* region size. It can then grow down from addr without
* limit. However, linux threads has an implicit internal
* limit to stack size of STACK_SIZE. Its just not
* enforced explicitly in linux. But, here we impose
* a limit of (STACK_SIZE - GUARD_SIZE) on the stack
* region, since we can do this with our mmap.
*
* Our mmap with MAP_STACK takes addr as the maximum
* downsize limit on BOS, and as len the max size of
* the region. It them maps the top SGROWSIZ bytes,
* and autgrows the region down, up to the limit
* in addr.
*
* If we don't use the MAP_STACK option, the effect
* of this code is to allocate a stack region of a
* fixed size of (STACK_SIZE - GUARD_SIZE).
*/
/* This gives us TOS */
bsd_args.addr = linux_args->addr + linux_args->len;
/* This gives us our maximum stack size */
if (linux_args->len > STACK_SIZE - GUARD_SIZE)
bsd_args.len = linux_args->len;
else
bsd_args.len = STACK_SIZE - GUARD_SIZE;
/* This gives us a new BOS. If we're using VM_STACK, then
* mmap will just map the top SGROWSIZ bytes, and let
* the stack grow down to the limit at BOS. If we're
* not using VM_STACK we map the full stack, since we
* don't have a way to autogrow it.
*/
bsd_args.addr -= bsd_args.len;
bsd_args.addr = (caddr_t)round_page(bsd_args.addr); /* XXXX */
} else {
bsd_args.addr = linux_args->addr;
bsd_args.len = linux_args->len;
}
bsd_args.fd = linux_args->fd;
if(linux_args->fd == 0)
bsd_args.fd = -1;
bsd_args.pad = 0;
#ifdef DEBUG
printf("Linux-emul(%ld): mmap(%p, 0x%lx, 0x%x, 0x%x, 0x%x, 0x%lx)\n",
(long)p->p_pid,
(void *)bsd_args.addr,
bsd_args.len,
bsd_args.prot,
bsd_args.flags,
bsd_args.fd,
bsd_args.pos);
#endif
if (bsd_args.addr == 0)
bsd_args.addr = (caddr_t)0x40000000UL;
error = mmap(p, &bsd_args);
#ifdef DEBUG
printf("Linux-emul(%ld): mmap returns %d, 0x%lx\n",
(long)p->p_pid, error, p->p_retval[0]);
#endif
return (error);
}
int
linux_rt_sigsuspend(p, uap)
struct proc *p;
struct linux_rt_sigsuspend_args *uap;
{
int error;
linux_sigset_t lmask;
sigset_t *bmask;
struct sigsuspend_args bsd;
caddr_t sg;
sg = stackgap_init();
#ifdef DEBUG
printf("Linux-emul(%ld): rt_sigsuspend(%p, %d)\n", (long)p->p_pid,
(void *)uap->newset, uap->sigsetsize);
#endif
if (uap->sigsetsize != sizeof(linux_sigset_t))
return (EINVAL);
error = copyin(uap->newset, &lmask, sizeof(linux_sigset_t));
if (error)
return (error);
bmask = stackgap_alloc(&sg, sizeof(sigset_t));
linux_to_bsd_sigset(&lmask, bmask);
bsd.sigmask = bmask;
return (sigsuspend(p, &bsd));
}
int
linux_mprotect(p, uap)
struct proc *p;
struct linux_mprotect_args *uap;
{
#ifdef DEBUG
printf("Linux-emul(%ld): mprotect(%p, 0x%lx, 0x%x)\n",
(long)p->p_pid, (void *)uap->addr, uap->len, uap->prot);
#endif
return (mprotect(p, (void *)uap));
}
int
linux_munmap(p, uap)
struct proc *p;
struct linux_munmap_args *uap;
{
#ifdef DEBUG
printf("Linux-emul(%ld): munmap(%p, 0x%lx)\n",
(long)p->p_pid, (void *)uap->addr, uap->len);
#endif
return (munmap(p, (void *)uap));
}
/*
* linux/alpha has 2 mappings for this,
* This is here purely to shut the compiler up.
*/
int
linux_setpgid(p, uap)
struct proc *p;
struct linux_setpgid_args *uap;
{
return (setpgid(p, (void *)uap));
}
static unsigned int linux_to_bsd_resource[LINUX_RLIM_NLIMITS] = {
RLIMIT_CPU, RLIMIT_FSIZE, RLIMIT_DATA, RLIMIT_STACK,
RLIMIT_CORE, RLIMIT_RSS, RLIMIT_NOFILE, -1,
RLIMIT_NPROC, RLIMIT_MEMLOCK
};
int dosetrlimit __P((struct proc *p, u_int which, struct rlimit *limp));
int
linux_setrlimit(p, uap)
struct proc *p;
struct linux_setrlimit_args *uap;
{
struct rlimit rlim;
u_int which;
int error;
#ifdef DEBUG
printf("Linux-emul(%ld): setrlimit(%d, %p)\n",
(long)p->p_pid, uap->resource, (void *)uap->rlim);
#endif
if (uap->resource >= LINUX_RLIM_NLIMITS)
return EINVAL;
which = linux_to_bsd_resource[uap->resource];
if (which == -1)
return EINVAL;
if ((error =
copyin((caddr_t)uap->rlim, (caddr_t)&rlim, sizeof (struct rlimit))))
return (error);
return dosetrlimit(p, which, &rlim);
}
int
linux_getrlimit(p, uap)
struct proc *p;
struct linux_getrlimit_args *uap;
{
u_int which;
#ifdef DEBUG
printf("Linux-emul(%ld): getrlimit(%d, %p)\n",
(long)p->p_pid, uap->resource, (void *)uap->rlim);
#endif
if (uap->resource >= LINUX_RLIM_NLIMITS)
return EINVAL;
which = linux_to_bsd_resource[uap->resource];
if (which == -1)
return EINVAL;
return (copyout((caddr_t)&p->p_rlimit[which], (caddr_t)uap->rlim,
sizeof (struct rlimit)));
}