linux_misc.c revision 95130
19313Ssos/*-
29313Ssos * Copyright (c) 1994-1995 S�ren Schmidt
39313Ssos * All rights reserved.
49313Ssos *
59313Ssos * Redistribution and use in source and binary forms, with or without
69313Ssos * modification, are permitted provided that the following conditions
79313Ssos * are met:
89313Ssos * 1. Redistributions of source code must retain the above copyright
914331Speter *    notice, this list of conditions and the following disclaimer
109313Ssos *    in this position and unchanged.
119313Ssos * 2. Redistributions in binary form must reproduce the above copyright
129313Ssos *    notice, this list of conditions and the following disclaimer in the
139313Ssos *    documentation and/or other materials provided with the distribution.
149313Ssos * 3. The name of the author may not be used to endorse or promote products
159313Ssos *    derived from this software withough specific prior written permission
169313Ssos *
179313Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
189313Ssos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
199313Ssos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
209313Ssos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
219313Ssos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
229313Ssos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
239313Ssos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
249313Ssos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
259313Ssos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
269313Ssos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
279313Ssos *
2850477Speter * $FreeBSD: head/sys/compat/linux/linux_misc.c 95130 2002-04-20 14:43:34Z rwatson $
299313Ssos */
309313Ssos
3149842Smarcel#include "opt_compat.h"
3249842Smarcel
339313Ssos#include <sys/param.h>
349313Ssos#include <sys/systm.h>
3576166Smarkm#include <sys/fcntl.h>
3676166Smarkm#include <sys/imgact_aout.h>
3791385Srobert#include <sys/jail.h>
3812458Sbde#include <sys/kernel.h>
3976166Smarkm#include <sys/lock.h>
409313Ssos#include <sys/mman.h>
419313Ssos#include <sys/mount.h>
4276166Smarkm#include <sys/mutex.h>
439313Ssos#include <sys/namei.h>
4483221Smarcel#include <sys/poll.h>
4576166Smarkm#include <sys/proc.h>
4680180Spirzyk#include <sys/blist.h>
4772538Sjlemon#include <sys/reboot.h>
489313Ssos#include <sys/resourcevar.h>
4976166Smarkm#include <sys/signalvar.h>
509313Ssos#include <sys/stat.h>
5112458Sbde#include <sys/sysctl.h>
5276166Smarkm#include <sys/sysproto.h>
5376166Smarkm#include <sys/time.h>
5441931Sjulian#include <sys/unistd.h>
5580180Spirzyk#include <sys/vmmeter.h>
569313Ssos#include <sys/vnode.h>
579313Ssos#include <sys/wait.h>
589313Ssos
5912652Sbde#include <vm/vm.h>
6012689Speter#include <vm/pmap.h>
6112458Sbde#include <vm/vm_kern.h>
6212689Speter#include <vm/vm_map.h>
6312842Sbde#include <vm/vm_extern.h>
6480180Spirzyk#include <vm/vm_object.h>
6580180Spirzyk#include <vm/swap_pager.h>
6612458Sbde
6730855Skato#include <machine/frame.h>
6865106Smarcel#include <machine/limits.h>
6930837Skato#include <machine/psl.h>
7050818Smarcel#include <machine/sysarch.h>
7168201Sobrien#ifdef __i386__
7250818Smarcel#include <machine/segments.h>
7368201Sobrien#endif
7430837Skato
7549849Smarcel#include <posix4/sched.h>
7649849Smarcel
7764909Smarcel#include <machine/../linux/linux.h>
7868583Smarcel#include <machine/../linux/linux_proto.h>
7964909Smarcel#include <compat/linux/linux_mib.h>
8064909Smarcel#include <compat/linux/linux_util.h>
8164909Smarcel
8268201Sobrien#ifdef __alpha__
8368201Sobrien#define BSD_TO_LINUX_SIGNAL(sig)       (sig)
8468201Sobrien#else
8551793Smarcel#define BSD_TO_LINUX_SIGNAL(sig)	\
8651793Smarcel	(((sig) <= LINUX_SIGTBLSZ) ? bsd_to_linux_signal[_SIG_IDX(sig)] : sig)
8768201Sobrien#endif
8851793Smarcel
8968201Sobrien#ifndef __alpha__
9083221Smarcelstatic unsigned int linux_to_bsd_resource[LINUX_RLIM_NLIMITS] = {
9183221Smarcel	RLIMIT_CPU, RLIMIT_FSIZE, RLIMIT_DATA, RLIMIT_STACK,
9283221Smarcel	RLIMIT_CORE, RLIMIT_RSS, RLIMIT_NPROC, RLIMIT_NOFILE,
9383221Smarcel	RLIMIT_MEMLOCK, -1
9449626Smarcel};
9568201Sobrien#endif /*!__alpha__*/
9649626Smarcel
9783221Smarcelstruct l_sysinfo {
9883221Smarcel	l_long		uptime;		/* Seconds since boot */
9983221Smarcel	l_ulong		loads[3];	/* 1, 5, and 15 minute load averages */
10083221Smarcel	l_ulong		totalram;	/* Total usable main memory size */
10183221Smarcel	l_ulong		freeram;	/* Available memory size */
10283221Smarcel	l_ulong		sharedram;	/* Amount of shared memory */
10383221Smarcel	l_ulong		bufferram;	/* Memory used by buffers */
10483221Smarcel	l_ulong		totalswap;	/* Total swap space size */
10583221Smarcel	l_ulong		freeswap;	/* swap space still available */
10683221Smarcel	l_ushort	procs;		/* Number of current processes */
10783221Smarcel	char		_f[22];		/* Pads structure to 64 bytes */
10880180Spirzyk};
10968201Sobrien#ifndef __alpha__
1109313Ssosint
11183366Sjulianlinux_sysinfo(struct thread *td, struct linux_sysinfo_args *args)
11280180Spirzyk{
11383221Smarcel	struct l_sysinfo sysinfo;
11483221Smarcel	vm_object_t object;
11583221Smarcel	int i;
11683221Smarcel	struct timespec ts;
11780180Spirzyk
11883221Smarcel	/* Uptime is copied out of print_uptime() in kern_shutdown.c */
11983221Smarcel	getnanouptime(&ts);
12083221Smarcel	i = 0;
12183221Smarcel	if (ts.tv_sec >= 86400) {
12283221Smarcel		ts.tv_sec %= 86400;
12383221Smarcel		i = 1;
12483221Smarcel	}
12583221Smarcel	if (i || ts.tv_sec >= 3600) {
12683221Smarcel		ts.tv_sec %= 3600;
12783221Smarcel		i = 1;
12883221Smarcel	}
12983221Smarcel	if (i || ts.tv_sec >= 60) {
13083221Smarcel		ts.tv_sec %= 60;
13183221Smarcel		i = 1;
13283221Smarcel	}
13383221Smarcel	sysinfo.uptime=ts.tv_sec;
13480180Spirzyk
13583221Smarcel	/* Use the information from the mib to get our load averages */
13683221Smarcel	for (i = 0; i < 3; i++)
13783221Smarcel		sysinfo.loads[i] = averunnable.ldavg[i];
13880180Spirzyk
13983221Smarcel	sysinfo.totalram = physmem * PAGE_SIZE;
14083221Smarcel	sysinfo.freeram = sysinfo.totalram - cnt.v_wire_count * PAGE_SIZE;
14180180Spirzyk
14283221Smarcel	sysinfo.sharedram = 0;
14383221Smarcel	for (object = TAILQ_FIRST(&vm_object_list); object != NULL;
14483221Smarcel	     object = TAILQ_NEXT(object, object_list))
14583221Smarcel		if (object->shadow_count > 1)
14683221Smarcel			sysinfo.sharedram += object->resident_page_count;
14780180Spirzyk
14883221Smarcel	sysinfo.sharedram *= PAGE_SIZE;
14983221Smarcel	sysinfo.bufferram = 0;
15080180Spirzyk
15183221Smarcel	if (swapblist == NULL) {
15283221Smarcel		sysinfo.totalswap= 0;
15383221Smarcel		sysinfo.freeswap = 0;
15483221Smarcel	} else {
15583221Smarcel		sysinfo.totalswap = swapblist->bl_blocks * 1024;
15683221Smarcel		sysinfo.freeswap = swapblist->bl_root->u.bmu_avail * PAGE_SIZE;
15783221Smarcel	}
15880180Spirzyk
15983221Smarcel	sysinfo.procs = 20; /* Hack */
16080180Spirzyk
16183221Smarcel	return copyout(&sysinfo, (caddr_t)args->info, sizeof(sysinfo));
16280180Spirzyk}
16380180Spirzyk#endif /*!__alpha__*/
16480180Spirzyk
16580180Spirzyk#ifndef __alpha__
16680180Spirzykint
16783366Sjulianlinux_alarm(struct thread *td, struct linux_alarm_args *args)
1689313Ssos{
16983221Smarcel	struct itimerval it, old_it;
17083221Smarcel	struct timeval tv;
17183221Smarcel	int s;
1729313Ssos
1739313Ssos#ifdef DEBUG
17472543Sjlemon	if (ldebug(alarm))
17572543Sjlemon		printf(ARGS(alarm, "%u"), args->secs);
1769313Ssos#endif
17783221Smarcel
17883221Smarcel	if (args->secs > 100000000)
17983221Smarcel		return EINVAL;
18083221Smarcel
18183221Smarcel	it.it_value.tv_sec = (long)args->secs;
18283221Smarcel	it.it_value.tv_usec = 0;
18383221Smarcel	it.it_interval.tv_sec = 0;
18483221Smarcel	it.it_interval.tv_usec = 0;
18583221Smarcel	s = splsoftclock();
18683366Sjulian	old_it = td->td_proc->p_realtimer;
18783221Smarcel	getmicrouptime(&tv);
18883221Smarcel	if (timevalisset(&old_it.it_value))
18983366Sjulian		callout_stop(&td->td_proc->p_itcallout);
19083221Smarcel	if (it.it_value.tv_sec != 0) {
19183366Sjulian		callout_reset(&td->td_proc->p_itcallout, tvtohz(&it.it_value),
19286852Sdes		    realitexpire, td->td_proc);
19383221Smarcel		timevaladd(&it.it_value, &tv);
19483221Smarcel	}
19583366Sjulian	td->td_proc->p_realtimer = it;
19683221Smarcel	splx(s);
19783221Smarcel	if (timevalcmp(&old_it.it_value, &tv, >)) {
19883221Smarcel		timevalsub(&old_it.it_value, &tv);
19983221Smarcel		if (old_it.it_value.tv_usec != 0)
20083221Smarcel			old_it.it_value.tv_sec++;
20183366Sjulian		td->td_retval[0] = old_it.it_value.tv_sec;
20283221Smarcel	}
20383221Smarcel	return 0;
2049313Ssos}
20568201Sobrien#endif /*!__alpha__*/
2069313Ssos
2079313Ssosint
20883366Sjulianlinux_brk(struct thread *td, struct linux_brk_args *args)
2099313Ssos{
21083366Sjulian	struct vmspace *vm = td->td_proc->p_vmspace;
21183221Smarcel	vm_offset_t new, old;
21283221Smarcel	struct obreak_args /* {
21383221Smarcel		char * nsize;
21483221Smarcel	} */ tmp;
2159313Ssos
2169313Ssos#ifdef DEBUG
21772543Sjlemon	if (ldebug(brk))
21872543Sjlemon		printf(ARGS(brk, "%p"), (void *)args->dsend);
2199313Ssos#endif
22083221Smarcel	old = (vm_offset_t)vm->vm_daddr + ctob(vm->vm_dsize);
22183221Smarcel	new = (vm_offset_t)args->dsend;
22283221Smarcel	tmp.nsize = (char *) new;
22383366Sjulian	if (((caddr_t)new > vm->vm_daddr) && !obreak(td, &tmp))
22483366Sjulian		td->td_retval[0] = (long)new;
22583221Smarcel	else
22683366Sjulian		td->td_retval[0] = (long)old;
2279313Ssos
22883221Smarcel	return 0;
2299313Ssos}
2309313Ssos
2319313Ssosint
23283366Sjulianlinux_uselib(struct thread *td, struct linux_uselib_args *args)
2339313Ssos{
23483221Smarcel	struct nameidata ni;
23583221Smarcel	struct vnode *vp;
23683221Smarcel	struct exec *a_out;
23783221Smarcel	struct vattr attr;
23883221Smarcel	vm_offset_t vmaddr;
23983221Smarcel	unsigned long file_offset;
24083221Smarcel	vm_offset_t buffer;
24183221Smarcel	unsigned long bss_size;
24283221Smarcel	int error;
24383221Smarcel	caddr_t sg;
24483221Smarcel	int locked;
2459313Ssos
24683221Smarcel	sg = stackgap_init();
24783366Sjulian	CHECKALTEXIST(td, &sg, args->library);
24814331Speter
2499313Ssos#ifdef DEBUG
25072543Sjlemon	if (ldebug(uselib))
25172543Sjlemon		printf(ARGS(uselib, "%s"), args->library);
2529313Ssos#endif
2539313Ssos
25483221Smarcel	a_out = NULL;
25583221Smarcel	locked = 0;
25683221Smarcel	vp = NULL;
25714114Speter
25895130Srwatson	/*
25995130Srwatson	 * XXX This code should make use of vn_open(), rather than doing
26095130Srwatson	 * all this stuff itself.
26195130Srwatson	 */
26283366Sjulian	NDINIT(&ni, LOOKUP, FOLLOW|LOCKLEAF, UIO_USERSPACE, args->library, td);
26383221Smarcel	error = namei(&ni);
26483221Smarcel	if (error)
26583221Smarcel		goto cleanup;
2669313Ssos
26783221Smarcel	vp = ni.ni_vp;
26883221Smarcel	/*
26983221Smarcel	 * XXX - This looks like a bogus check. A LOCKLEAF namei should not
27083221Smarcel	 * succeed without returning a vnode.
27183221Smarcel	 */
27283221Smarcel	if (vp == NULL) {
27383221Smarcel		error = ENOEXEC;	/* ?? */
27483221Smarcel		goto cleanup;
27583221Smarcel	}
27683221Smarcel	NDFREE(&ni, NDF_ONLY_PNBUF);
2779313Ssos
27883221Smarcel	/*
27983221Smarcel	 * From here on down, we have a locked vnode that must be unlocked.
28083221Smarcel	 */
28183221Smarcel	locked++;
28214114Speter
28383221Smarcel	/* Writable? */
28483221Smarcel	if (vp->v_writecount) {
28583221Smarcel		error = ETXTBSY;
28683221Smarcel		goto cleanup;
28783221Smarcel	}
2889313Ssos
28983221Smarcel	/* Executable? */
29091406Sjhb	error = VOP_GETATTR(vp, &attr, td->td_ucred, td);
29183221Smarcel	if (error)
29283221Smarcel		goto cleanup;
2939313Ssos
29483221Smarcel	if ((vp->v_mount->mnt_flag & MNT_NOEXEC) ||
29583221Smarcel	    ((attr.va_mode & 0111) == 0) || (attr.va_type != VREG)) {
29683221Smarcel		error = ENOEXEC;
29783221Smarcel		goto cleanup;
29883221Smarcel	}
2999313Ssos
30083221Smarcel	/* Sensible size? */
30183221Smarcel	if (attr.va_size == 0) {
30283221Smarcel		error = ENOEXEC;
30383221Smarcel		goto cleanup;
30483221Smarcel	}
3059313Ssos
30683221Smarcel	/* Can we access it? */
30791406Sjhb	error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td);
30883221Smarcel	if (error)
30983221Smarcel		goto cleanup;
3109313Ssos
31191406Sjhb	error = VOP_OPEN(vp, FREAD, td->td_ucred, td);
31283221Smarcel	if (error)
31383221Smarcel		goto cleanup;
3149313Ssos
31583221Smarcel	/*
31683221Smarcel	 * Lock no longer needed
31783221Smarcel	 */
31883366Sjulian	VOP_UNLOCK(vp, 0, td);
31983221Smarcel	locked = 0;
32011163Sjulian
32183221Smarcel	/* Pull in executable header into kernel_map */
32283221Smarcel	error = vm_mmap(kernel_map, (vm_offset_t *)&a_out, PAGE_SIZE,
32383221Smarcel	    VM_PROT_READ, VM_PROT_READ, 0, (caddr_t)vp, 0);
32483221Smarcel	if (error)
32583221Smarcel		goto cleanup;
3269313Ssos
32783221Smarcel	/* Is it a Linux binary ? */
32883221Smarcel	if (((a_out->a_magic >> 16) & 0xff) != 0x64) {
32983221Smarcel		error = ENOEXEC;
33083221Smarcel		goto cleanup;
33183221Smarcel	}
3329313Ssos
33383221Smarcel	/*
33483221Smarcel	 * While we are here, we should REALLY do some more checks
33583221Smarcel	 */
33614114Speter
33783221Smarcel	/* Set file/virtual offset based on a.out variant. */
33883221Smarcel	switch ((int)(a_out->a_magic & 0xffff)) {
33983221Smarcel	case 0413:	/* ZMAGIC */
34083221Smarcel		file_offset = 1024;
34183221Smarcel		break;
34283221Smarcel	case 0314:	/* QMAGIC */
34383221Smarcel		file_offset = 0;
34483221Smarcel		break;
34583221Smarcel	default:
34683221Smarcel		error = ENOEXEC;
34783221Smarcel		goto cleanup;
34883221Smarcel	}
3499313Ssos
35083221Smarcel	bss_size = round_page(a_out->a_bss);
35114114Speter
35283221Smarcel	/* Check various fields in header for validity/bounds. */
35383221Smarcel	if (a_out->a_text & PAGE_MASK || a_out->a_data & PAGE_MASK) {
35483221Smarcel		error = ENOEXEC;
35583221Smarcel		goto cleanup;
35683221Smarcel	}
35714114Speter
35883221Smarcel	/* text + data can't exceed file size */
35983221Smarcel	if (a_out->a_data + a_out->a_text > attr.va_size) {
36083221Smarcel		error = EFAULT;
36183221Smarcel		goto cleanup;
36283221Smarcel	}
36314114Speter
36483366Sjulian	/* To protect td->td_proc->p_rlimit in the if condition. */
36583221Smarcel	mtx_assert(&Giant, MA_OWNED);
36670061Sjhb
36783221Smarcel	/*
36883221Smarcel	 * text/data/bss must not exceed limits
36983221Smarcel	 * XXX - this is not complete. it should check current usage PLUS
37083221Smarcel	 * the resources needed by this library.
37183221Smarcel	 */
37284783Sps	if (a_out->a_text > maxtsiz ||
37383366Sjulian	    a_out->a_data + bss_size >
37483366Sjulian	    td->td_proc->p_rlimit[RLIMIT_DATA].rlim_cur) {
37583221Smarcel		error = ENOMEM;
37683221Smarcel		goto cleanup;
37783221Smarcel	}
37814114Speter
37983221Smarcel	/* prevent more writers */
38083221Smarcel	vp->v_flag |= VTEXT;
38114114Speter
38283221Smarcel	/*
38383221Smarcel	 * Check if file_offset page aligned. Currently we cannot handle
38483221Smarcel	 * misalinged file offsets, and so we read in the entire image
38583221Smarcel	 * (what a waste).
38683221Smarcel	 */
38783221Smarcel	if (file_offset & PAGE_MASK) {
3889313Ssos#ifdef DEBUG
38983221Smarcel		printf("uselib: Non page aligned binary %lu\n", file_offset);
3909313Ssos#endif
39183221Smarcel		/* Map text+data read/write/execute */
39214114Speter
39383221Smarcel		/* a_entry is the load address and is page aligned */
39483221Smarcel		vmaddr = trunc_page(a_out->a_entry);
39514114Speter
39683221Smarcel		/* get anon user mapping, read+write+execute */
39783366Sjulian		error = vm_map_find(&td->td_proc->p_vmspace->vm_map, NULL, 0,
39883366Sjulian		    &vmaddr, a_out->a_text + a_out->a_data, FALSE, VM_PROT_ALL,
39983221Smarcel		    VM_PROT_ALL, 0);
40083221Smarcel		if (error)
40183221Smarcel			goto cleanup;
4029313Ssos
40383221Smarcel		/* map file into kernel_map */
40483221Smarcel		error = vm_mmap(kernel_map, &buffer,
40583221Smarcel		    round_page(a_out->a_text + a_out->a_data + file_offset),
40683221Smarcel		    VM_PROT_READ, VM_PROT_READ, 0, (caddr_t)vp,
40783221Smarcel		    trunc_page(file_offset));
40883221Smarcel		if (error)
40983221Smarcel			goto cleanup;
4109313Ssos
41183221Smarcel		/* copy from kernel VM space to user space */
41283221Smarcel		error = copyout((caddr_t)(uintptr_t)(buffer + file_offset),
41383221Smarcel		    (caddr_t)vmaddr, a_out->a_text + a_out->a_data);
4149313Ssos
41583221Smarcel		/* release temporary kernel space */
41683221Smarcel		vm_map_remove(kernel_map, buffer, buffer +
41783221Smarcel		    round_page(a_out->a_text + a_out->a_data + file_offset));
4189313Ssos
41983221Smarcel		if (error)
42083221Smarcel			goto cleanup;
42183221Smarcel	} else {
4229313Ssos#ifdef DEBUG
42383221Smarcel		printf("uselib: Page aligned binary %lu\n", file_offset);
4249313Ssos#endif
42583221Smarcel		/*
42683221Smarcel		 * for QMAGIC, a_entry is 20 bytes beyond the load address
42783221Smarcel		 * to skip the executable header
42883221Smarcel		 */
42983221Smarcel		vmaddr = trunc_page(a_out->a_entry);
43014114Speter
43183221Smarcel		/*
43283221Smarcel		 * Map it all into the process's space as a single
43383221Smarcel		 * copy-on-write "data" segment.
43483221Smarcel		 */
43583366Sjulian		error = vm_mmap(&td->td_proc->p_vmspace->vm_map, &vmaddr,
43683221Smarcel		    a_out->a_text + a_out->a_data, VM_PROT_ALL, VM_PROT_ALL,
43783221Smarcel		    MAP_PRIVATE | MAP_FIXED, (caddr_t)vp, file_offset);
43883221Smarcel		if (error)
43983221Smarcel			goto cleanup;
44083221Smarcel	}
4419313Ssos#ifdef DEBUG
44283221Smarcel	printf("mem=%08lx = %08lx %08lx\n", (long)vmaddr, ((long*)vmaddr)[0],
44383221Smarcel	    ((long*)vmaddr)[1]);
4449313Ssos#endif
44583221Smarcel	if (bss_size != 0) {
44683221Smarcel		/* Calculate BSS start address */
44783221Smarcel		vmaddr = trunc_page(a_out->a_entry) + a_out->a_text +
44883221Smarcel		    a_out->a_data;
44914114Speter
45083221Smarcel		/* allocate some 'anon' space */
45183366Sjulian		error = vm_map_find(&td->td_proc->p_vmspace->vm_map, NULL, 0,
45283366Sjulian		    &vmaddr, bss_size, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0);
45383221Smarcel		if (error)
45483221Smarcel			goto cleanup;
45583221Smarcel	}
45614114Speter
45714114Spetercleanup:
45883221Smarcel	/* Unlock vnode if needed */
45983221Smarcel	if (locked)
46083366Sjulian		VOP_UNLOCK(vp, 0, td);
46114114Speter
46283221Smarcel	/* Release the kernel mapping. */
46383221Smarcel	if (a_out)
46483221Smarcel		vm_map_remove(kernel_map, (vm_offset_t)a_out,
46583221Smarcel		    (vm_offset_t)a_out + PAGE_SIZE);
46614114Speter
46783221Smarcel	return error;
4689313Ssos}
4699313Ssos
4709313Ssosint
47183366Sjulianlinux_select(struct thread *td, struct linux_select_args *args)
47214331Speter{
47383221Smarcel	struct select_args bsa;
47483221Smarcel	struct timeval tv0, tv1, utv, *tvp;
47583221Smarcel	caddr_t sg;
47683221Smarcel	int error;
47714331Speter
4789313Ssos#ifdef DEBUG
47983221Smarcel	if (ldebug(select))
48083221Smarcel		printf(ARGS(select, "%d, %p, %p, %p, %p"), args->nfds,
48183221Smarcel		    (void *)args->readfds, (void *)args->writefds,
48283221Smarcel		    (void *)args->exceptfds, (void *)args->timeout);
4839313Ssos#endif
48414331Speter
48583221Smarcel	error = 0;
48683221Smarcel	bsa.nd = args->nfds;
48783221Smarcel	bsa.in = args->readfds;
48883221Smarcel	bsa.ou = args->writefds;
48983221Smarcel	bsa.ex = args->exceptfds;
49083221Smarcel	bsa.tv = (struct timeval *)args->timeout;
49183221Smarcel
49283221Smarcel	/*
49383221Smarcel	 * Store current time for computation of the amount of
49483221Smarcel	 * time left.
49583221Smarcel	 */
49683221Smarcel	if (args->timeout) {
49783221Smarcel		if ((error = copyin((caddr_t)args->timeout, &utv,
49883221Smarcel		    sizeof(utv))))
49983221Smarcel			goto select_out;
50014331Speter#ifdef DEBUG
50183221Smarcel		if (ldebug(select))
50283221Smarcel			printf(LMSG("incoming timeout (%ld/%ld)"),
50383221Smarcel			    utv.tv_sec, utv.tv_usec);
50414331Speter#endif
50583221Smarcel
50683221Smarcel		if (itimerfix(&utv)) {
50783221Smarcel			/*
50883221Smarcel			 * The timeval was invalid.  Convert it to something
50983221Smarcel			 * valid that will act as it does under Linux.
51083221Smarcel			 */
51183221Smarcel			sg = stackgap_init();
51283221Smarcel			tvp = stackgap_alloc(&sg, sizeof(utv));
51383221Smarcel			utv.tv_sec += utv.tv_usec / 1000000;
51483221Smarcel			utv.tv_usec %= 1000000;
51583221Smarcel			if (utv.tv_usec < 0) {
51683221Smarcel				utv.tv_sec -= 1;
51783221Smarcel				utv.tv_usec += 1000000;
51883221Smarcel			}
51983221Smarcel			if (utv.tv_sec < 0)
52083221Smarcel				timevalclear(&utv);
52183221Smarcel			if ((error = copyout(&utv, tvp, sizeof(utv))))
52283221Smarcel				goto select_out;
52383221Smarcel			bsa.tv = tvp;
52483221Smarcel		}
52583221Smarcel		microtime(&tv0);
52614331Speter	}
52714331Speter
52883366Sjulian	error = select(td, &bsa);
52914331Speter#ifdef DEBUG
53083221Smarcel	if (ldebug(select))
53172543Sjlemon		printf(LMSG("real select returns %d"), error);
53214331Speter#endif
53383221Smarcel	if (error) {
53483221Smarcel		/*
53583221Smarcel		 * See fs/select.c in the Linux kernel.  Without this,
53683221Smarcel		 * Maelstrom doesn't work.
53783221Smarcel		 */
53883221Smarcel		if (error == ERESTART)
53983221Smarcel			error = EINTR;
54083221Smarcel		goto select_out;
54183221Smarcel	}
54214331Speter
54383221Smarcel	if (args->timeout) {
54483366Sjulian		if (td->td_retval[0]) {
54583221Smarcel			/*
54683221Smarcel			 * Compute how much time was left of the timeout,
54783221Smarcel			 * by subtracting the current time and the time
54883221Smarcel			 * before we started the call, and subtracting
54983221Smarcel			 * that result from the user-supplied value.
55083221Smarcel			 */
55183221Smarcel			microtime(&tv1);
55283221Smarcel			timevalsub(&tv1, &tv0);
55383221Smarcel			timevalsub(&utv, &tv1);
55483221Smarcel			if (utv.tv_sec < 0)
55583221Smarcel				timevalclear(&utv);
55683221Smarcel		} else
55783221Smarcel			timevalclear(&utv);
55814331Speter#ifdef DEBUG
55983221Smarcel		if (ldebug(select))
56083221Smarcel			printf(LMSG("outgoing timeout (%ld/%ld)"),
56183221Smarcel			    utv.tv_sec, utv.tv_usec);
56214331Speter#endif
56383221Smarcel		if ((error = copyout(&utv, (caddr_t)args->timeout,
56483221Smarcel		    sizeof(utv))))
56583221Smarcel			goto select_out;
56683221Smarcel	}
56714331Speter
56814331Speterselect_out:
56914331Speter#ifdef DEBUG
57083221Smarcel	if (ldebug(select))
57183221Smarcel		printf(LMSG("select_out -> %d"), error);
57214331Speter#endif
57383221Smarcel	return error;
5749313Ssos}
5759313Ssos
57637548Sjkhint
57783366Sjulianlinux_mremap(struct thread *td, struct linux_mremap_args *args)
57837548Sjkh{
57937548Sjkh	struct munmap_args /* {
58037548Sjkh		void *addr;
58137548Sjkh		size_t len;
58237548Sjkh	} */ bsd_args;
58337548Sjkh	int error = 0;
58437548Sjkh
58537548Sjkh#ifdef DEBUG
58672543Sjlemon	if (ldebug(mremap))
58772543Sjlemon		printf(ARGS(mremap, "%p, %08lx, %08lx, %08lx"),
58872543Sjlemon		    (void *)args->addr,
58972543Sjlemon		    (unsigned long)args->old_len,
59072543Sjlemon		    (unsigned long)args->new_len,
59172543Sjlemon		    (unsigned long)args->flags);
59237548Sjkh#endif
59337548Sjkh	args->new_len = round_page(args->new_len);
59437548Sjkh	args->old_len = round_page(args->old_len);
59537548Sjkh
59637548Sjkh	if (args->new_len > args->old_len) {
59783366Sjulian		td->td_retval[0] = 0;
59837548Sjkh		return ENOMEM;
59937548Sjkh	}
60037548Sjkh
60137548Sjkh	if (args->new_len < args->old_len) {
60283221Smarcel		bsd_args.addr = (caddr_t)(args->addr + args->new_len);
60337548Sjkh		bsd_args.len = args->old_len - args->new_len;
60483366Sjulian		error = munmap(td, &bsd_args);
60537548Sjkh	}
60637548Sjkh
60783366Sjulian	td->td_retval[0] = error ? 0 : (u_long)args->addr;
60837548Sjkh	return error;
60937548Sjkh}
61037548Sjkh
61114331Speterint
61283366Sjulianlinux_msync(struct thread *td, struct linux_msync_args *args)
61314331Speter{
61414331Speter	struct msync_args bsd_args;
6159313Ssos
61683221Smarcel	bsd_args.addr = (caddr_t)args->addr;
61714331Speter	bsd_args.len = args->len;
61814331Speter	bsd_args.flags = 0;	/* XXX ignore */
61914331Speter
62083366Sjulian	return msync(td, &bsd_args);
62114331Speter}
62214331Speter
62368201Sobrien#ifndef __alpha__
6249313Ssosint
62583366Sjulianlinux_time(struct thread *td, struct linux_time_args *args)
6269313Ssos{
62783221Smarcel	struct timeval tv;
62883221Smarcel	l_time_t tm;
62983221Smarcel	int error;
6309313Ssos
6319313Ssos#ifdef DEBUG
63272543Sjlemon	if (ldebug(time))
63372543Sjlemon		printf(ARGS(time, "*"));
6349313Ssos#endif
63583221Smarcel
63683221Smarcel	microtime(&tv);
63783221Smarcel	tm = tv.tv_sec;
63883221Smarcel	if (args->tm && (error = copyout(&tm, (caddr_t)args->tm, sizeof(tm))))
63983221Smarcel		return error;
64083366Sjulian	td->td_retval[0] = tm;
64183221Smarcel	return 0;
6429313Ssos}
64368201Sobrien#endif	/*!__alpha__*/
6449313Ssos
64583221Smarcelstruct l_times_argv {
64683221Smarcel	l_long		tms_utime;
64783221Smarcel	l_long		tms_stime;
64883221Smarcel	l_long		tms_cutime;
64983221Smarcel	l_long		tms_cstime;
6509313Ssos};
6519313Ssos
65274701Sgallatin#ifdef __alpha__
65374701Sgallatin#define CLK_TCK 1024	/* Linux uses 1024 on alpha */
65474701Sgallatin#else
65514381Speter#define CLK_TCK 100	/* Linux uses 100 */
65674701Sgallatin#endif
65774701Sgallatin
65814381Speter#define CONVTCK(r)	(r.tv_sec * CLK_TCK + r.tv_usec / (1000000 / CLK_TCK))
65914381Speter
6609313Ssosint
66183366Sjulianlinux_times(struct thread *td, struct linux_times_args *args)
6629313Ssos{
66383221Smarcel	struct timeval tv;
66483221Smarcel	struct l_times_argv tms;
66583221Smarcel	struct rusage ru;
66683221Smarcel	int error;
6679313Ssos
6689313Ssos#ifdef DEBUG
66972543Sjlemon	if (ldebug(times))
67072543Sjlemon		printf(ARGS(times, "*"));
6719313Ssos#endif
67214381Speter
67383221Smarcel	mtx_lock_spin(&sched_lock);
67483366Sjulian	calcru(td->td_proc, &ru.ru_utime, &ru.ru_stime, NULL);
67583221Smarcel	mtx_unlock_spin(&sched_lock);
67614381Speter
67783221Smarcel	tms.tms_utime = CONVTCK(ru.ru_utime);
67883221Smarcel	tms.tms_stime = CONVTCK(ru.ru_stime);
67914381Speter
68083366Sjulian	tms.tms_cutime = CONVTCK(td->td_proc->p_stats->p_cru.ru_utime);
68183366Sjulian	tms.tms_cstime = CONVTCK(td->td_proc->p_stats->p_cru.ru_stime);
68214381Speter
68383221Smarcel	if ((error = copyout(&tms, (caddr_t)args->buf, sizeof(tms))))
68483221Smarcel		return error;
68583221Smarcel
68683221Smarcel	microuptime(&tv);
68783366Sjulian	td->td_retval[0] = (int)CONVTCK(tv);
68883221Smarcel	return 0;
6899313Ssos}
6909313Ssos
6919313Ssosint
69283366Sjulianlinux_newuname(struct thread *td, struct linux_newuname_args *args)
6939313Ssos{
69483221Smarcel	struct l_new_utsname utsname;
69587275Srwatson	char osname[LINUX_MAX_UTSNAME];
69687275Srwatson	char osrelease[LINUX_MAX_UTSNAME];
6979313Ssos
6989313Ssos#ifdef DEBUG
69972543Sjlemon	if (ldebug(newuname))
70072543Sjlemon		printf(ARGS(newuname, "*"));
7019313Ssos#endif
70250345Smarcel
70387275Srwatson	linux_get_osname(td->td_proc, osname);
70487275Srwatson	linux_get_osrelease(td->td_proc, osrelease);
70550465Smarcel
70683221Smarcel	bzero(&utsname, sizeof(utsname));
70750465Smarcel	strncpy(utsname.sysname, osname, LINUX_MAX_UTSNAME-1);
70891392Srobert	getcredhostname(td->td_ucred, utsname.nodename, LINUX_MAX_UTSNAME-1);
70950465Smarcel	strncpy(utsname.release, osrelease, LINUX_MAX_UTSNAME-1);
71050345Smarcel	strncpy(utsname.version, version, LINUX_MAX_UTSNAME-1);
71150345Smarcel	strncpy(utsname.machine, machine, LINUX_MAX_UTSNAME-1);
71250345Smarcel	strncpy(utsname.domainname, domainname, LINUX_MAX_UTSNAME-1);
71350345Smarcel
71483221Smarcel	return (copyout(&utsname, (caddr_t)args->buf, sizeof(utsname)));
7159313Ssos}
7169313Ssos
71783221Smarcel#if defined(__i386__)
71883221Smarcelstruct l_utimbuf {
71983221Smarcel	l_time_t l_actime;
72083221Smarcel	l_time_t l_modtime;
72114381Speter};
7229313Ssos
7239313Ssosint
72483366Sjulianlinux_utime(struct thread *td, struct linux_utime_args *args)
7259313Ssos{
72683221Smarcel	struct utimes_args /* {
72783221Smarcel		char	*path;
72883221Smarcel		struct	timeval *tptr;
72983221Smarcel	} */ bsdutimes;
73083221Smarcel	struct timeval tv[2], *tvp;
73183221Smarcel	struct l_utimbuf lut;
73283221Smarcel	int error;
73383221Smarcel	caddr_t sg;
7349313Ssos
73583221Smarcel	sg = stackgap_init();
73683366Sjulian	CHECKALTEXIST(td, &sg, args->fname);
73714331Speter
7389313Ssos#ifdef DEBUG
73972543Sjlemon	if (ldebug(utime))
74072543Sjlemon		printf(ARGS(utime, "%s, *"), args->fname);
7419313Ssos#endif
74214381Speter
74383221Smarcel	if (args->times) {
74483221Smarcel		if ((error = copyin((caddr_t)args->times, &lut, sizeof lut)))
74583221Smarcel			return error;
74683221Smarcel		tv[0].tv_sec = lut.l_actime;
74783221Smarcel		tv[0].tv_usec = 0;
74883221Smarcel		tv[1].tv_sec = lut.l_modtime;
74983221Smarcel		tv[1].tv_usec = 0;
75083221Smarcel		/* so that utimes can copyin */
75183221Smarcel		tvp = (struct timeval *)stackgap_alloc(&sg, sizeof(tv));
75283221Smarcel		if (tvp == NULL)
75383221Smarcel			return (ENAMETOOLONG);
75483221Smarcel		if ((error = copyout(tv, tvp, sizeof(tv))))
75583221Smarcel			return error;
75683221Smarcel		bsdutimes.tptr = tvp;
75783221Smarcel	} else
75883221Smarcel		bsdutimes.tptr = NULL;
75983221Smarcel
76083221Smarcel	bsdutimes.path = args->fname;
76183366Sjulian	return utimes(td, &bsdutimes);
7629313Ssos}
76383221Smarcel#endif /* __i386__ */
7649313Ssos
76544384Sjulian#define __WCLONE 0x80000000
76644384Sjulian
76768201Sobrien#ifndef __alpha__
7689313Ssosint
76983366Sjulianlinux_waitpid(struct thread *td, struct linux_waitpid_args *args)
7709313Ssos{
77183221Smarcel	struct wait_args /* {
77283221Smarcel		int pid;
77383221Smarcel		int *status;
77483221Smarcel		int options;
77583221Smarcel		struct	rusage *rusage;
77683221Smarcel	} */ tmp;
77783221Smarcel	int error, tmpstat;
7789313Ssos
7799313Ssos#ifdef DEBUG
78072543Sjlemon	if (ldebug(waitpid))
78172543Sjlemon		printf(ARGS(waitpid, "%d, %p, %d"),
78272543Sjlemon		    args->pid, (void *)args->status, args->options);
7839313Ssos#endif
7849313Ssos
78583221Smarcel	tmp.pid = args->pid;
78683221Smarcel	tmp.status = args->status;
78783221Smarcel	tmp.options = (args->options & (WNOHANG | WUNTRACED));
78883221Smarcel	/* WLINUXCLONE should be equal to __WCLONE, but we make sure */
78983221Smarcel	if (args->options & __WCLONE)
79083221Smarcel		tmp.options |= WLINUXCLONE;
79183221Smarcel	tmp.rusage = NULL;
79243208Sjulian
79383366Sjulian	if ((error = wait4(td, &tmp)) != 0)
79483221Smarcel		return error;
79583221Smarcel
79683221Smarcel	if (args->status) {
79783221Smarcel		if ((error = copyin((caddr_t)args->status, &tmpstat,
79883221Smarcel		    sizeof(int))) != 0)
79983221Smarcel			return error;
80083221Smarcel		tmpstat &= 0xffff;
80183221Smarcel		if (WIFSIGNALED(tmpstat))
80283221Smarcel			tmpstat = (tmpstat & 0xffffff80) |
80383221Smarcel			    BSD_TO_LINUX_SIGNAL(WTERMSIG(tmpstat));
80483221Smarcel		else if (WIFSTOPPED(tmpstat))
80583221Smarcel			tmpstat = (tmpstat & 0xffff00ff) |
80683221Smarcel			    (BSD_TO_LINUX_SIGNAL(WSTOPSIG(tmpstat)) << 8);
80783221Smarcel		return copyout(&tmpstat, (caddr_t)args->status, sizeof(int));
80883221Smarcel	}
80983221Smarcel
81014331Speter	return 0;
8119313Ssos}
81268201Sobrien#endif	/*!__alpha__*/
8139313Ssos
81414331Speterint
81583366Sjulianlinux_wait4(struct thread *td, struct linux_wait4_args *args)
8169313Ssos{
81783221Smarcel	struct wait_args /* {
81883221Smarcel		int pid;
81983221Smarcel		int *status;
82083221Smarcel		int options;
82183221Smarcel		struct	rusage *rusage;
82283221Smarcel	} */ tmp;
82383221Smarcel	int error, tmpstat;
8249313Ssos
8259313Ssos#ifdef DEBUG
82672543Sjlemon	if (ldebug(wait4))
82772543Sjlemon		printf(ARGS(wait4, "%d, %p, %d, %p"),
82872543Sjlemon		    args->pid, (void *)args->status, args->options,
82972543Sjlemon		    (void *)args->rusage);
8309313Ssos#endif
8319313Ssos
83283221Smarcel	tmp.pid = args->pid;
83383221Smarcel	tmp.status = args->status;
83483221Smarcel	tmp.options = (args->options & (WNOHANG | WUNTRACED));
83583221Smarcel	/* WLINUXCLONE should be equal to __WCLONE, but we make sure */
83683221Smarcel	if (args->options & __WCLONE)
83783221Smarcel		tmp.options |= WLINUXCLONE;
83883221Smarcel	tmp.rusage = (struct rusage *)args->rusage;
83914331Speter
84083366Sjulian	if ((error = wait4(td, &tmp)) != 0)
84183221Smarcel		return error;
84214331Speter
84383366Sjulian	SIGDELSET(td->td_proc->p_siglist, SIGCHLD);
84483221Smarcel
84583221Smarcel	if (args->status) {
84683221Smarcel		if ((error = copyin((caddr_t)args->status, &tmpstat,
84783221Smarcel		    sizeof(int))) != 0)
84883221Smarcel			return error;
84983221Smarcel		tmpstat &= 0xffff;
85083221Smarcel		if (WIFSIGNALED(tmpstat))
85183221Smarcel			tmpstat = (tmpstat & 0xffffff80) |
85283221Smarcel			    BSD_TO_LINUX_SIGNAL(WTERMSIG(tmpstat));
85383221Smarcel		else if (WIFSTOPPED(tmpstat))
85483221Smarcel			tmpstat = (tmpstat & 0xffff00ff) |
85583221Smarcel			    (BSD_TO_LINUX_SIGNAL(WSTOPSIG(tmpstat)) << 8);
85683221Smarcel		return copyout(&tmpstat, (caddr_t)args->status, sizeof(int));
85783221Smarcel	}
85883221Smarcel
85914331Speter	return 0;
8609313Ssos}
86113420Ssos
86214331Speterint
86383366Sjulianlinux_mknod(struct thread *td, struct linux_mknod_args *args)
86413420Ssos{
86514331Speter	caddr_t sg;
86614331Speter	struct mknod_args bsd_mknod;
86714331Speter	struct mkfifo_args bsd_mkfifo;
86814331Speter
86914331Speter	sg = stackgap_init();
87014331Speter
87183366Sjulian	CHECKALTCREAT(td, &sg, args->path);
87214331Speter
87314331Speter#ifdef DEBUG
87472543Sjlemon	if (ldebug(mknod))
87572543Sjlemon		printf(ARGS(mknod, "%s, %d, %d"),
87672543Sjlemon		    args->path, args->mode, args->dev);
87714331Speter#endif
87814331Speter
87914331Speter	if (args->mode & S_IFIFO) {
88014331Speter		bsd_mkfifo.path = args->path;
88114331Speter		bsd_mkfifo.mode = args->mode;
88283366Sjulian		return mkfifo(td, &bsd_mkfifo);
88314331Speter	} else {
88414331Speter		bsd_mknod.path = args->path;
88514331Speter		bsd_mknod.mode = args->mode;
88614331Speter		bsd_mknod.dev = args->dev;
88783366Sjulian		return mknod(td, &bsd_mknod);
88814331Speter	}
88913420Ssos}
89014331Speter
89114331Speter/*
89214331Speter * UGH! This is just about the dumbest idea I've ever heard!!
89314331Speter */
89414331Speterint
89583366Sjulianlinux_personality(struct thread *td, struct linux_personality_args *args)
89614331Speter{
89714331Speter#ifdef DEBUG
89872543Sjlemon	if (ldebug(personality))
89972543Sjlemon		printf(ARGS(personality, "%d"), args->per);
90014331Speter#endif
90168201Sobrien#ifndef __alpha__
90214331Speter	if (args->per != 0)
90314331Speter		return EINVAL;
90468201Sobrien#endif
90514331Speter
90614331Speter	/* Yes Jim, it's still a Linux... */
90783366Sjulian	td->td_retval[0] = 0;
90814331Speter	return 0;
90914331Speter}
91014331Speter
91114331Speter/*
91214331Speter * Wrappers for get/setitimer for debugging..
91314331Speter */
91414331Speterint
91583366Sjulianlinux_setitimer(struct thread *td, struct linux_setitimer_args *args)
91614331Speter{
91714331Speter	struct setitimer_args bsa;
91814331Speter	struct itimerval foo;
91914331Speter	int error;
92014331Speter
92114331Speter#ifdef DEBUG
92272543Sjlemon	if (ldebug(setitimer))
92372543Sjlemon		printf(ARGS(setitimer, "%p, %p"),
92472543Sjlemon		    (void *)args->itv, (void *)args->oitv);
92514331Speter#endif
92614331Speter	bsa.which = args->which;
92783221Smarcel	bsa.itv = (struct itimerval *)args->itv;
92883221Smarcel	bsa.oitv = (struct itimerval *)args->oitv;
92914331Speter	if (args->itv) {
93083221Smarcel	    if ((error = copyin((caddr_t)args->itv, &foo, sizeof(foo))))
93114331Speter		return error;
93214331Speter#ifdef DEBUG
93372543Sjlemon	    if (ldebug(setitimer)) {
93472543Sjlemon	        printf("setitimer: value: sec: %ld, usec: %ld\n",
93572543Sjlemon		    foo.it_value.tv_sec, foo.it_value.tv_usec);
93672543Sjlemon	        printf("setitimer: interval: sec: %ld, usec: %ld\n",
93772543Sjlemon		    foo.it_interval.tv_sec, foo.it_interval.tv_usec);
93872543Sjlemon	    }
93914331Speter#endif
94014331Speter	}
94183366Sjulian	return setitimer(td, &bsa);
94214331Speter}
94314331Speter
94414331Speterint
94583366Sjulianlinux_getitimer(struct thread *td, struct linux_getitimer_args *args)
94614331Speter{
94714331Speter	struct getitimer_args bsa;
94814331Speter#ifdef DEBUG
94972543Sjlemon	if (ldebug(getitimer))
95072543Sjlemon		printf(ARGS(getitimer, "%p"), (void *)args->itv);
95114331Speter#endif
95214331Speter	bsa.which = args->which;
95383221Smarcel	bsa.itv = (struct itimerval *)args->itv;
95483366Sjulian	return getitimer(td, &bsa);
95514331Speter}
95630837Skato
95768201Sobrien#ifndef __alpha__
95830837Skatoint
95983366Sjulianlinux_nice(struct thread *td, struct linux_nice_args *args)
96030837Skato{
96130837Skato	struct setpriority_args	bsd_args;
96230837Skato
96330837Skato	bsd_args.which = PRIO_PROCESS;
96430837Skato	bsd_args.who = 0;	/* current process */
96530837Skato	bsd_args.prio = args->inc;
96683366Sjulian	return setpriority(td, &bsd_args);
96730837Skato}
96868201Sobrien#endif	/*!__alpha__*/
96930837Skato
97042185Ssosint
97183366Sjulianlinux_setgroups(struct thread *td, struct linux_setgroups_args *args)
97242185Ssos{
97377183Srwatson	struct ucred *newcred, *oldcred;
97483221Smarcel	l_gid_t linux_gidset[NGROUPS];
97550350Smarcel	gid_t *bsd_gidset;
97650350Smarcel	int ngrp, error;
97794621Sjhb	struct proc *p;
97842185Ssos
97983221Smarcel	ngrp = args->gidsetsize;
98094621Sjhb	if (ngrp >= NGROUPS)
98194621Sjhb		return (EINVAL);
98294621Sjhb	error = copyin((caddr_t)args->grouplist, linux_gidset,
98394621Sjhb	    ngrp * sizeof(l_gid_t));
98494621Sjhb	if (error)
98594621Sjhb		return (error);
98694621Sjhb	newcred = crget();
98794621Sjhb	p = td->td_proc;
98894621Sjhb	PROC_LOCK(p);
98994621Sjhb	oldcred = p->p_ucred;
99042185Ssos
99150350Smarcel	/*
99250350Smarcel	 * cr_groups[0] holds egid. Setting the whole set from
99350350Smarcel	 * the supplied set will cause egid to be changed too.
99450350Smarcel	 * Keep cr_groups[0] unchanged to prevent that.
99550350Smarcel	 */
99642185Ssos
99794621Sjhb	if ((error = suser_cred(oldcred, PRISON_ROOT)) != 0) {
99894621Sjhb		PROC_UNLOCK(p);
99994621Sjhb		crfree(newcred);
100050350Smarcel		return (error);
100194621Sjhb	}
100242185Ssos
100394621Sjhb	crcopy(newcred, oldcred);
100450350Smarcel	if (ngrp > 0) {
100577183Srwatson		newcred->cr_ngroups = ngrp + 1;
100650350Smarcel
100777183Srwatson		bsd_gidset = newcred->cr_groups;
100850350Smarcel		ngrp--;
100950350Smarcel		while (ngrp >= 0) {
101050350Smarcel			bsd_gidset[ngrp + 1] = linux_gidset[ngrp];
101150350Smarcel			ngrp--;
101250350Smarcel		}
101350350Smarcel	}
101450350Smarcel	else
101577183Srwatson		newcred->cr_ngroups = 1;
101650350Smarcel
101794621Sjhb	setsugid(p);
101894621Sjhb	p->p_ucred = newcred;
101994621Sjhb	PROC_UNLOCK(p);
102077183Srwatson	crfree(oldcred);
102150350Smarcel	return (0);
102242185Ssos}
102342185Ssos
102442185Ssosint
102583366Sjulianlinux_getgroups(struct thread *td, struct linux_getgroups_args *args)
102642185Ssos{
102777183Srwatson	struct ucred *cred;
102883221Smarcel	l_gid_t linux_gidset[NGROUPS];
102950350Smarcel	gid_t *bsd_gidset;
103050350Smarcel	int bsd_gidsetsz, ngrp, error;
103142185Ssos
103294454Sjhb	cred = td->td_ucred;
103377183Srwatson	bsd_gidset = cred->cr_groups;
103477183Srwatson	bsd_gidsetsz = cred->cr_ngroups - 1;
103542185Ssos
103650350Smarcel	/*
103750350Smarcel	 * cr_groups[0] holds egid. Returning the whole set
103850350Smarcel	 * here will cause a duplicate. Exclude cr_groups[0]
103950350Smarcel	 * to prevent that.
104050350Smarcel	 */
104142185Ssos
104283221Smarcel	if ((ngrp = args->gidsetsize) == 0) {
104383366Sjulian		td->td_retval[0] = bsd_gidsetsz;
104450350Smarcel		return (0);
104550350Smarcel	}
104642185Ssos
104750546Smarcel	if (ngrp < bsd_gidsetsz)
104850350Smarcel		return (EINVAL);
104942185Ssos
105050546Smarcel	ngrp = 0;
105150350Smarcel	while (ngrp < bsd_gidsetsz) {
105250546Smarcel		linux_gidset[ngrp] = bsd_gidset[ngrp + 1];
105350350Smarcel		ngrp++;
105450350Smarcel	}
105550350Smarcel
105683221Smarcel	if ((error = copyout(linux_gidset, (caddr_t)args->grouplist,
105783221Smarcel	    ngrp * sizeof(l_gid_t))))
105850350Smarcel		return (error);
105950350Smarcel
106083366Sjulian	td->td_retval[0] = ngrp;
106150350Smarcel	return (0);
106242185Ssos}
106349626Smarcel
106468201Sobrien#ifndef __alpha__
106549626Smarcelint
106683366Sjulianlinux_setrlimit(struct thread *td, struct linux_setrlimit_args *args)
106749626Smarcel{
106865099Smarcel	struct __setrlimit_args bsd;
106983221Smarcel	struct l_rlimit rlim;
107065099Smarcel	int error;
107165099Smarcel	caddr_t sg = stackgap_init();
107249842Smarcel
107349626Smarcel#ifdef DEBUG
107472543Sjlemon	if (ldebug(setrlimit))
107572543Sjlemon		printf(ARGS(setrlimit, "%d, %p"),
107683221Smarcel		    args->resource, (void *)args->rlim);
107749626Smarcel#endif
107849626Smarcel
107983221Smarcel	if (args->resource >= LINUX_RLIM_NLIMITS)
108065099Smarcel		return (EINVAL);
108149626Smarcel
108283221Smarcel	bsd.which = linux_to_bsd_resource[args->resource];
108365099Smarcel	if (bsd.which == -1)
108465099Smarcel		return (EINVAL);
108549626Smarcel
108683221Smarcel	error = copyin((caddr_t)args->rlim, &rlim, sizeof(rlim));
108765099Smarcel	if (error)
108865099Smarcel		return (error);
108949626Smarcel
109065099Smarcel	bsd.rlp = stackgap_alloc(&sg, sizeof(struct rlimit));
109165099Smarcel	bsd.rlp->rlim_cur = (rlim_t)rlim.rlim_cur;
109265099Smarcel	bsd.rlp->rlim_max = (rlim_t)rlim.rlim_max;
109383366Sjulian	return (setrlimit(td, &bsd));
109449626Smarcel}
109549626Smarcel
109649626Smarcelint
109783366Sjulianlinux_old_getrlimit(struct thread *td, struct linux_old_getrlimit_args *args)
109849626Smarcel{
109965099Smarcel	struct __getrlimit_args bsd;
110083221Smarcel	struct l_rlimit rlim;
110165099Smarcel	int error;
110265099Smarcel	caddr_t sg = stackgap_init();
110349842Smarcel
110449626Smarcel#ifdef DEBUG
110583221Smarcel	if (ldebug(old_getrlimit))
110683221Smarcel		printf(ARGS(old_getrlimit, "%d, %p"),
110783221Smarcel		    args->resource, (void *)args->rlim);
110849626Smarcel#endif
110949626Smarcel
111083221Smarcel	if (args->resource >= LINUX_RLIM_NLIMITS)
111165099Smarcel		return (EINVAL);
111249626Smarcel
111383221Smarcel	bsd.which = linux_to_bsd_resource[args->resource];
111465099Smarcel	if (bsd.which == -1)
111565099Smarcel		return (EINVAL);
111649626Smarcel
111765099Smarcel	bsd.rlp = stackgap_alloc(&sg, sizeof(struct rlimit));
111883366Sjulian	error = getrlimit(td, &bsd);
111965099Smarcel	if (error)
112065099Smarcel		return (error);
112149626Smarcel
112265099Smarcel	rlim.rlim_cur = (unsigned long)bsd.rlp->rlim_cur;
112365106Smarcel	if (rlim.rlim_cur == ULONG_MAX)
112465106Smarcel		rlim.rlim_cur = LONG_MAX;
112565099Smarcel	rlim.rlim_max = (unsigned long)bsd.rlp->rlim_max;
112665106Smarcel	if (rlim.rlim_max == ULONG_MAX)
112765106Smarcel		rlim.rlim_max = LONG_MAX;
112883221Smarcel	return (copyout(&rlim, (caddr_t)args->rlim, sizeof(rlim)));
112949626Smarcel}
113083221Smarcel
113183221Smarcelint
113283366Sjulianlinux_getrlimit(struct thread *td, struct linux_getrlimit_args *args)
113383221Smarcel{
113483221Smarcel	struct __getrlimit_args bsd;
113583221Smarcel	struct l_rlimit rlim;
113683221Smarcel	int error;
113783221Smarcel	caddr_t sg = stackgap_init();
113883221Smarcel
113983221Smarcel#ifdef DEBUG
114083221Smarcel	if (ldebug(getrlimit))
114183221Smarcel		printf(ARGS(getrlimit, "%d, %p"),
114283221Smarcel		    args->resource, (void *)args->rlim);
114383221Smarcel#endif
114483221Smarcel
114583221Smarcel	if (args->resource >= LINUX_RLIM_NLIMITS)
114683221Smarcel		return (EINVAL);
114783221Smarcel
114883221Smarcel	bsd.which = linux_to_bsd_resource[args->resource];
114983221Smarcel	if (bsd.which == -1)
115083221Smarcel		return (EINVAL);
115183221Smarcel
115283221Smarcel	bsd.rlp = stackgap_alloc(&sg, sizeof(struct rlimit));
115383366Sjulian	error = getrlimit(td, &bsd);
115483221Smarcel	if (error)
115583221Smarcel		return (error);
115683221Smarcel
115783221Smarcel	rlim.rlim_cur = (l_ulong)bsd.rlp->rlim_cur;
115883221Smarcel	rlim.rlim_max = (l_ulong)bsd.rlp->rlim_max;
115983221Smarcel	return (copyout(&rlim, (caddr_t)args->rlim, sizeof(rlim)));
116083221Smarcel}
116168201Sobrien#endif /*!__alpha__*/
116249849Smarcel
116349849Smarcelint
116483366Sjulianlinux_sched_setscheduler(struct thread *td,
116583221Smarcel    struct linux_sched_setscheduler_args *args)
116649849Smarcel{
116749849Smarcel	struct sched_setscheduler_args bsd;
116849849Smarcel
116949849Smarcel#ifdef DEBUG
117072543Sjlemon	if (ldebug(sched_setscheduler))
117172543Sjlemon		printf(ARGS(sched_setscheduler, "%d, %d, %p"),
117283221Smarcel		    args->pid, args->policy, (const void *)args->param);
117349849Smarcel#endif
117449849Smarcel
117583221Smarcel	switch (args->policy) {
117649849Smarcel	case LINUX_SCHED_OTHER:
117749849Smarcel		bsd.policy = SCHED_OTHER;
117849849Smarcel		break;
117949849Smarcel	case LINUX_SCHED_FIFO:
118049849Smarcel		bsd.policy = SCHED_FIFO;
118149849Smarcel		break;
118249849Smarcel	case LINUX_SCHED_RR:
118349849Smarcel		bsd.policy = SCHED_RR;
118449849Smarcel		break;
118549849Smarcel	default:
118649849Smarcel		return EINVAL;
118749849Smarcel	}
118849849Smarcel
118983221Smarcel	bsd.pid = args->pid;
119083221Smarcel	bsd.param = (struct sched_param *)args->param;
119183366Sjulian	return sched_setscheduler(td, &bsd);
119249849Smarcel}
119349849Smarcel
119449849Smarcelint
119583366Sjulianlinux_sched_getscheduler(struct thread *td,
119683221Smarcel    struct linux_sched_getscheduler_args *args)
119749849Smarcel{
119849849Smarcel	struct sched_getscheduler_args bsd;
119949849Smarcel	int error;
120049849Smarcel
120149849Smarcel#ifdef DEBUG
120272543Sjlemon	if (ldebug(sched_getscheduler))
120383221Smarcel		printf(ARGS(sched_getscheduler, "%d"), args->pid);
120449849Smarcel#endif
120549849Smarcel
120683221Smarcel	bsd.pid = args->pid;
120783366Sjulian	error = sched_getscheduler(td, &bsd);
120849849Smarcel
120983366Sjulian	switch (td->td_retval[0]) {
121049849Smarcel	case SCHED_OTHER:
121183366Sjulian		td->td_retval[0] = LINUX_SCHED_OTHER;
121249849Smarcel		break;
121349849Smarcel	case SCHED_FIFO:
121483366Sjulian		td->td_retval[0] = LINUX_SCHED_FIFO;
121549849Smarcel		break;
121649849Smarcel	case SCHED_RR:
121783366Sjulian		td->td_retval[0] = LINUX_SCHED_RR;
121849849Smarcel		break;
121949849Smarcel	}
122049849Smarcel
122149849Smarcel	return error;
122249849Smarcel}
122372538Sjlemon
122475053Salcint
122583366Sjulianlinux_sched_get_priority_max(struct thread *td,
122683221Smarcel    struct linux_sched_get_priority_max_args *args)
122775053Salc{
122875053Salc	struct sched_get_priority_max_args bsd;
122975053Salc
123075053Salc#ifdef DEBUG
123175053Salc	if (ldebug(sched_get_priority_max))
123283221Smarcel		printf(ARGS(sched_get_priority_max, "%d"), args->policy);
123375053Salc#endif
123475053Salc
123583221Smarcel	switch (args->policy) {
123675053Salc	case LINUX_SCHED_OTHER:
123775053Salc		bsd.policy = SCHED_OTHER;
123875053Salc		break;
123975053Salc	case LINUX_SCHED_FIFO:
124075053Salc		bsd.policy = SCHED_FIFO;
124175053Salc		break;
124275053Salc	case LINUX_SCHED_RR:
124375053Salc		bsd.policy = SCHED_RR;
124475053Salc		break;
124575053Salc	default:
124675053Salc		return EINVAL;
124775053Salc	}
124883366Sjulian	return sched_get_priority_max(td, &bsd);
124975053Salc}
125075053Salc
125175053Salcint
125283366Sjulianlinux_sched_get_priority_min(struct thread *td,
125383221Smarcel    struct linux_sched_get_priority_min_args *args)
125475053Salc{
125575053Salc	struct sched_get_priority_min_args bsd;
125675053Salc
125775053Salc#ifdef DEBUG
125875053Salc	if (ldebug(sched_get_priority_min))
125983221Smarcel		printf(ARGS(sched_get_priority_min, "%d"), args->policy);
126075053Salc#endif
126175053Salc
126283221Smarcel	switch (args->policy) {
126375053Salc	case LINUX_SCHED_OTHER:
126475053Salc		bsd.policy = SCHED_OTHER;
126575053Salc		break;
126675053Salc	case LINUX_SCHED_FIFO:
126775053Salc		bsd.policy = SCHED_FIFO;
126875053Salc		break;
126975053Salc	case LINUX_SCHED_RR:
127075053Salc		bsd.policy = SCHED_RR;
127175053Salc		break;
127275053Salc	default:
127375053Salc		return EINVAL;
127475053Salc	}
127583366Sjulian	return sched_get_priority_min(td, &bsd);
127675053Salc}
127775053Salc
127872538Sjlemon#define REBOOT_CAD_ON	0x89abcdef
127972538Sjlemon#define REBOOT_CAD_OFF	0
128072538Sjlemon#define REBOOT_HALT	0xcdef0123
128172538Sjlemon
128272538Sjlemonint
128383366Sjulianlinux_reboot(struct thread *td, struct linux_reboot_args *args)
128472538Sjlemon{
128572538Sjlemon	struct reboot_args bsd_args;
128672538Sjlemon
128772538Sjlemon#ifdef DEBUG
128872538Sjlemon	if (ldebug(reboot))
128983221Smarcel		printf(ARGS(reboot, "0x%x"), args->cmd);
129072538Sjlemon#endif
129183221Smarcel	if (args->cmd == REBOOT_CAD_ON || args->cmd == REBOOT_CAD_OFF)
129272538Sjlemon		return (0);
129383221Smarcel	bsd_args.opt = (args->cmd == REBOOT_HALT) ? RB_HALT : 0;
129483366Sjulian	return (reboot(td, &bsd_args));
129572538Sjlemon}
129683221Smarcel
129789717Sgallatin#ifndef __alpha__
129889717Sgallatin
129983221Smarcel/*
130083221Smarcel * The FreeBSD native getpid(2), getgid(2) and getuid(2) also modify
130183366Sjulian * td->td_retval[1] when COMPAT_43 or COMPAT_SUNOS is defined. This
130283221Smarcel * globbers registers that are assumed to be preserved. The following
130383221Smarcel * lightweight syscalls fixes this. See also linux_getgid16() and
130483221Smarcel * linux_getuid16() in linux_uid16.c.
130583221Smarcel *
130683221Smarcel * linux_getpid() - MP SAFE
130783221Smarcel * linux_getgid() - MP SAFE
130883221Smarcel * linux_getuid() - MP SAFE
130983221Smarcel */
131083221Smarcel
131183221Smarcelint
131283366Sjulianlinux_getpid(struct thread *td, struct linux_getpid_args *args)
131383221Smarcel{
131483366Sjulian
131583366Sjulian	td->td_retval[0] = td->td_proc->p_pid;
131683221Smarcel	return (0);
131783221Smarcel}
131883221Smarcel
131983221Smarcelint
132083366Sjulianlinux_getgid(struct thread *td, struct linux_getgid_args *args)
132183221Smarcel{
132283366Sjulian
132394454Sjhb	td->td_retval[0] = td->td_ucred->cr_rgid;
132483221Smarcel	return (0);
132583221Smarcel}
132683221Smarcel
132783221Smarcelint
132883366Sjulianlinux_getuid(struct thread *td, struct linux_getuid_args *args)
132983221Smarcel{
133083366Sjulian
133194454Sjhb	td->td_retval[0] = td->td_ucred->cr_ruid;
133283221Smarcel	return (0);
133383221Smarcel}
133483503Smr
133589717Sgallatin#endif /*!__alpha__*/
133689717Sgallatin
133783503Smrint
133883503Smrlinux_getsid(struct thread *td, struct linux_getsid_args *args)
133983503Smr{
134083503Smr	struct getsid_args bsd;
134183503Smr	bsd.pid = args->pid;
134283503Smr	return getsid(td, &bsd);
134383503Smr}
1344