FreeBSD-5.3/sys/alpha/osf1/osf1_misc.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: osf1_misc.c,v 1.14 1998/05/20 16:34:29 chs Exp $	*/
/*
 * Copyright (c) 1994, 1995 Carnegie-Mellon University.
 * All rights reserved.
 *
 * Author: Chris G. Demetriou
 *
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */
/*
 * Additional Copyright (c) 1999 by Andrew Gallatin
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/sys/alpha/osf1/osf1_misc.c,v 1.47.2.1 2004/09/03 15:30:20 jhb Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/exec.h>
#include <sys/fcntl.h>
#include <sys/filedesc.h>
#include <sys/imgact.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/file.h>		/* Must come after sys/malloc.h */
#include <sys/mman.h>
#include <sys/module.h>
#include <sys/mount.h>
#include <sys/mutex.h>
#include <sys/namei.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/resource.h>
#include <sys/resourcevar.h>
#include <sys/selinfo.h>
#include <sys/pipe.h>		/* Must come after sys/selinfo.h */
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/syscallsubr.h>
#include <sys/sysctl.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/systm.h>
#include <sys/unistd.h>
#include <sys/user.h>
#include <sys/utsname.h>
#include <sys/vnode.h>
#include <sys/wait.h>

#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_extern.h>

#include <machine/cpu.h>
#include <machine/cpuconf.h>
#include <machine/fpu.h>
#include <machine/md_var.h>
#include <machine/rpb.h>

#include <alpha/osf1/exec_ecoff.h>
#include <alpha/osf1/osf1_signal.h>
#include <alpha/osf1/osf1_proto.h>
#include <alpha/osf1/osf1_syscall.h>
#include <alpha/osf1/osf1_util.h>
#include <alpha/osf1/osf1.h>

static void cvtstat2osf1(struct stat *, struct osf1_stat *);
static int  osf2bsd_pathconf(int *);

static const char osf1_emul_path[] = "/compat/osf1";
/*
 * [ taken from the linux emulator ]
 * Search an alternate path before passing pathname arguments on
 * to system calls. Useful for keeping a separate 'emulation tree'.
 *
 * If cflag is set, we check if an attempt can be made to create
 * the named file, i.e. we check if the directory it should
 * be in exists.
 */
int
osf1_emul_find(td, sgp, prefix, path, pbuf, cflag)
	struct thread	*td;
	caddr_t		*sgp;          /* Pointer to stackgap memory */
	const char	*prefix;
	char		*path;
	char		**pbuf;
	int		cflag;
{
        int			error;
        size_t			len, sz;
        char			*buf, *cp, *ptr;
	struct ucred		*ucred;
        struct nameidata	nd;
        struct nameidata	ndroot;
        struct vattr		vat;
        struct vattr		vatroot;

	buf = (char *) malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
	*pbuf = path;

	for (ptr = buf; (*ptr = *prefix) != '\0'; ptr++, prefix++)
		continue;

	sz = MAXPATHLEN - (ptr - buf);

	/*
	 * If sgp is not given then the path is already in kernel space
	 */
	if (sgp == NULL)
		error = copystr(path, ptr, sz, &len);
	else
		error = copyinstr(path, ptr, sz, &len);

	if (error) {
		free(buf, M_TEMP);
		return error;
	}

	if (*ptr != '/') {
		free(buf, M_TEMP);
		return EINVAL;
	}

	/*
	 *  We know that there is a / somewhere in this pathname.
	 *  Search backwards for it, to find the file's parent dir
	 *  to see if it exists in the alternate tree. If it does,
	 *  and we want to create a file (cflag is set). We don't
	 *  need to worry about the root comparison in this case.
	 */

	if (cflag) {
		for (cp = &ptr[len] - 1; *cp != '/'; cp--)
			;
		*cp = '\0';

		NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, buf, td);

		if ((error = namei(&nd)) != 0) {
			free(buf, M_TEMP);
			return error;
		}

		*cp = '/';
	} else {
		NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, buf, td);

		if ((error = namei(&nd)) != 0) {
			free(buf, M_TEMP);
			return error;
		}

		/*
		 * We now compare the vnode of the osf1_root to the one
		 * vnode asked. If they resolve to be the same, then we
		 * ignore the match so that the real root gets used.
		 * This avoids the problem of traversing "../.." to find the
		 * root directory and never finding it, because "/" resolves
		 * to the emulation root directory. This is expensive :-(
		 */
		NDINIT(&ndroot, LOOKUP, FOLLOW, UIO_SYSSPACE, osf1_emul_path,
		    td);

		if ((error = namei(&ndroot)) != 0) {
			/* Cannot happen! */
			free(buf, M_TEMP);
			vrele(nd.ni_vp);
			return error;
		}

		ucred = td->td_ucred;
		if ((error = VOP_GETATTR(nd.ni_vp, &vat, ucred, td)) != 0) {
			goto bad;
		}

		if ((error = VOP_GETATTR(ndroot.ni_vp, &vatroot, ucred,
		    td)) != 0) {
			goto bad;
		}

		if (vat.va_fsid == vatroot.va_fsid &&
		    vat.va_fileid == vatroot.va_fileid) {
			error = ENOENT;
			goto bad;
		}

	}
	if (sgp == NULL)
		*pbuf = buf;
	else {
		sz = &ptr[len] - buf;
		*pbuf = stackgap_alloc(sgp, sz + 1);
		error = copyout(buf, *pbuf, sz);
		free(buf, M_TEMP);
	}

	vrele(nd.ni_vp);
	if (!cflag)
		vrele(ndroot.ni_vp);

	return error;

bad:
	vrele(ndroot.ni_vp);
	vrele(nd.ni_vp);
	free(buf, M_TEMP);
	return error;
}


int
osf1_open(td, uap)
	struct thread *td;
	struct osf1_open_args *uap;
{
	struct open_args /* {
		syscallarg(char *) path;
		syscallarg(int) flags;
		syscallarg(int) mode;
	} */ a;
	caddr_t sg;

	sg = stackgap_init();
	CHECKALTEXIST(td, &sg, uap->path);

	a.path = uap->path;
	a.flags = uap->flags;		/* XXX translate */
	a.mode = uap->mode;

	return open(td, &a);
}

extern long totalphysmem;

int
osf1_getsysinfo(td, uap)
	struct thread *td;
	struct osf1_getsysinfo_args *uap;
{
	int error, retval;
	int ncpus = 1; 	       /* XXX until SMP */
	int ophysmem;
	int unit;
	long percpu;
	long proctype;
	struct osf1_cpu_info cpuinfo;

	error = retval = 0;

	switch(uap->op) {
	case OSF_GET_MAX_UPROCS:
		error = copyout(&maxprocperuid, uap->buffer,
		    sizeof(maxprocperuid));
		retval = 1;
		break;
	case OSF_GET_PHYSMEM:
		ophysmem = totalphysmem * (PAGE_SIZE >> 10);	
		error = copyout(&ophysmem, uap->buffer,
		    sizeof(ophysmem));
		retval = 1;
		break;
	case OSF_GET_MAX_CPU:
	case OSF_GET_CPUS_IN_BOX:
		error = copyout(&ncpus, uap->buffer,
		    sizeof(ncpus));
		retval = 1;
		break;
	case OSF_GET_IEEE_FP_CONTROL:
		error = copyout(&td->td_pcb->pcb_fp_control,uap->buffer,
		    sizeof(td->td_pcb->pcb_fp_control));
		retval = 1;
		break;
	case OSF_GET_CPU_INFO:

		if (uap->nbytes < sizeof(cpuinfo))
			error = EINVAL;
		else {
			bzero(&cpuinfo, sizeof(cpuinfo));
			unit = alpha_pal_whami();
			cpuinfo.current_cpu = unit;
			cpuinfo.cpus_in_box = ncpus;
			cpuinfo.cpu_type = 
			    LOCATE_PCS(hwrpb, unit)->pcs_proc_type;
			cpuinfo.ncpus = ncpus;
			cpuinfo.cpus_present = ncpus;
			cpuinfo.cpus_running = ncpus;
			cpuinfo.cpu_binding = 1;
			cpuinfo.cpu_ex_binding = 0;
			cpuinfo.mhz = hwrpb->rpb_cc_freq / 1000000;
			error = copyout(&cpuinfo, uap->buffer,
			    sizeof(cpuinfo));
			retval = 1;
		}
		break;
	case OSF_GET_PROC_TYPE:
		if(uap->nbytes < sizeof(proctype))
			error = EINVAL;
		else {
			unit = alpha_pal_whami();
			proctype = LOCATE_PCS(hwrpb, unit)->pcs_proc_type;
			error = copyout (&proctype, uap->buffer,
			    sizeof(percpu));
			retval = 1;
		}
	break;
	case OSF_GET_HWRPB: {  /* note -- osf/1 doesn't have rpb_tbhint[8] */
		unsigned long rpb_size;
		rpb_size = (unsigned long)&hwrpb->rpb_tbhint -
		    (unsigned long)hwrpb;
		if(uap->nbytes < rpb_size){
			uprintf("nbytes = %ld, sizeof(struct rpb) = %ld\n",
			    uap->nbytes, rpb_size);
			error = EINVAL;
		}
		else {
			error = copyout(hwrpb, uap->buffer, rpb_size);
			retval = 1;
		}
	}
		break;
	case OSF_GET_PLATFORM_NAME:
		error = copyout(platform.model, uap->buffer, 
		    strlen(platform.model));
		retval = 1;
		break;
	default:
		printf("osf1_getsysinfo called with unknown op=%ld\n", uap->op);
		return EINVAL;
	}
	td->td_retval[0] = retval;
	return(error);
}


int
osf1_setsysinfo(td, uap)
	struct thread *td;
	struct osf1_setsysinfo_args *uap;
{
	int error;

	error = 0;

	switch(uap->op) {
	case OSF_SET_IEEE_FP_CONTROL:
	{
		u_int64_t temp, *fp_control;

		if ((error = copyin(uap->buffer, &temp, sizeof(temp))))
			break;
		fp_control = &td->td_pcb->pcb_fp_control;
		*fp_control = temp & IEEE_TRAP_ENABLE_MASK;
		break;
	}
	default:
		uprintf("osf1_setsysinfo called with op=%ld\n", uap->op);
		/*error = EINVAL;*/
	}
	return (error);
}


int
osf1_getrlimit(td, uap)
	struct thread *td;
	struct osf1_getrlimit_args *uap;
{
	struct rlimit bsd_rlim;
	struct proc *p;
	int which;

	if (uap->which >= OSF1_RLIMIT_NLIMITS)
		return (EINVAL);

	if (uap->which <= OSF1_RLIMIT_LASTCOMMON)
		which = uap->which;
	else if (uap->which == OSF1_RLIMIT_NOFILE)
		which = RLIMIT_NOFILE;
	else
		return (0);

	p = td->td_proc;
	PROC_LOCK(p);
	lim_rlimit(p, which, &bsd_rlim);
	PROC_UNLOCK(p);
	return (copyout(&bsd_rlim, uap->rlp, sizeof(bsd_rlim)));
}


int
osf1_setrlimit(td, uap)
	struct thread *td;
	struct osf1_setrlimit_args  *uap;
{
	struct rlimit bsd_rlim;
	int error, which;

	if (uap->which >= OSF1_RLIMIT_NLIMITS)
		return (EINVAL);

	if (uap->which <= OSF1_RLIMIT_LASTCOMMON)
		which = uap->which;
	else if (uap->which == OSF1_RLIMIT_NOFILE)
		which = RLIMIT_NOFILE;
	else
		return (0);

	error = copyin(uap->rlp, &bsd_rlim, sizeof(bsd_rlim));
	if (error)
		return (error);

	return (kern_setrlimit(td, which, &bsd_rlim));
}


/*
 *  As linux says, this is a total guess.
 */

int
osf1_set_program_attributes(td, uap)
	struct thread *td;
	struct osf1_set_program_attributes_args *uap;
{
	struct vmspace *vm = td->td_proc->p_vmspace;

	vm->vm_taddr = (caddr_t)uap->text_start;
	vm->vm_tsize = btoc(round_page(uap->text_len));
	vm->vm_daddr = (caddr_t)uap->bss_start;
	vm->vm_dsize = btoc(round_page(uap->bss_len));

	return(KERN_SUCCESS);
}


int
osf1_mmap(td, uap)
	struct thread *td;
	struct osf1_mmap_args *uap;
{
	struct mmap_args /* {
		syscallarg(caddr_t) addr;
		syscallarg(size_t) len;
		syscallarg(int) prot;
		syscallarg(int) flags;
		syscallarg(int) fd;
		syscallarg(long) pad;
		syscallarg(off_t) pos;
	} */ a;
	int retval;
	vm_map_t map;
	vm_offset_t addr, len, newaddr;

	GIANT_REQUIRED;

	a.addr = uap->addr;
	a.len = uap->len;
	a.prot = uap->prot;
	a.fd = uap->fd;
	a.pad = 0;
	a.pos = uap->pos;

	a.flags = 0;

	/*
	 *  OSF/1's mmap, unlike FreeBSD's, does its best to map memory at the
	 *  user's requested address, even if MAP_FIXED is not set.  Here we
	 *  try to replicate this behaviour as much as we can because some
	 *  applications (like /sbin/loader) depend on having things put as
	 *  close to where they've requested as possible.
	 */

	if (uap->addr != NULL)
		addr = round_page((vm_offset_t)a.addr);
	else
	/*
	 *  Try to use the apparent OSF/1 default placement of 0x10000 for
	 *  NULL addrs, this helps to prevent non-64 bit clean binaries from
	 *  SEGV'ing.
	 */
		addr = round_page((vm_offset_t)0x10000UL);
	len = (vm_offset_t)a.len;
	map = &td->td_proc->p_vmspace->vm_map;
	if (!vm_map_findspace(map, addr, len, &newaddr)) {
		a.addr = (caddr_t) newaddr;
		a.flags |= (MAP_FIXED);
	}
#ifdef DEBUG
	else
		uprintf("osf1_mmap:vm_map_findspace failed for: %p 0x%lx\n",
		    (caddr_t)addr, len);
#endif
	if (uap->flags & OSF1_MAP_SHARED)
		a.flags |= MAP_SHARED;
	if (uap->flags & OSF1_MAP_PRIVATE)
		a.flags |= MAP_PRIVATE;

	switch (uap->flags & OSF1_MAP_TYPE) {
	case OSF1_MAP_ANONYMOUS:
		a.flags |= MAP_ANON;
		break;
	case OSF1_MAP_FILE:
		a.flags |= MAP_FILE;
		break;
	default:
		return (EINVAL);
	}
	if (uap->flags & OSF1_MAP_FIXED)
		a.flags |= MAP_FIXED;
	if (uap->flags & OSF1_MAP_HASSEMAPHORE)
		a.flags |= MAP_HASSEMAPHORE;
	if (uap->flags & OSF1_MAP_INHERIT)
		return (EINVAL);
	if (uap->flags & OSF1_MAP_UNALIGNED)
		return (EINVAL);
	/*
	 *  Emulate an osf/1 bug:  Apparently, mmap'ed segments are always
	 *  readable even if the user doesn't or in PROT_READ.  This causes
	 *  some buggy programs to segv.
	 */
	a.prot |= PROT_READ;


	retval = mmap(td, &a);
#ifdef DEBUG
	uprintf(
	    "\nosf1_mmap: addr=%p (%p), len = 0x%lx, prot=0x%x, fd=%d, pad=0, pos=0x%lx",
	    uap->addr, a.addr,uap->len, uap->prot,
	    uap->fd, uap->pos);
	printf(" flags = 0x%x\n",uap->flags);
#endif
	return (retval);
}

int
osf1_msync(td, uap)
	struct thread *td;
	struct osf1_msync_args *uap;
{
	struct msync_args a;

	a.addr = uap->addr;
	a.len  = uap->len;
	a.flags = 0;
	if(uap->flags & OSF1_MS_ASYNC)
		a.flags |= MS_ASYNC;
	if(uap->flags & OSF1_MS_SYNC)
		a.flags |= MS_SYNC;
	if(uap->flags & OSF1_MS_INVALIDATE)
		a.flags |= MS_INVALIDATE;
	return(msync(td, &a));
}

struct osf1_stat {
	int32_t		st_dev;
	u_int32_t	st_ino;
	u_int32_t	st_mode;
	u_int16_t	st_nlink;
	u_int32_t	st_uid;
	u_int32_t	st_gid;
	int32_t		st_rdev;
	u_int64_t	st_size;
	int32_t		st_atime_sec;
	int32_t		st_spare1;
	int32_t		st_mtime_sec;
	int32_t		st_spare2;
	int32_t		st_ctime_sec;
	int32_t		st_spare3;
	u_int32_t	st_blksize;
	int32_t		st_blocks;
	u_int32_t	st_flags;
	u_int32_t	st_gen;
};

/*
 *  Get file status; this version follows links.
 */
/* ARGSUSED */
int
osf1_stat(td, uap)
	struct thread *td;
	struct osf1_stat_args *uap;
{
	int error;
	struct stat sb;
	struct osf1_stat osb;
	struct nameidata nd;
	caddr_t sg;

	sg = stackgap_init();

	CHECKALTEXIST(td, &sg, uap->path);

	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
	    uap->path, td);
	if ((error = namei(&nd)))
		return (error);
	error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td);
	vput(nd.ni_vp);
	if (error)
		return (error);
	cvtstat2osf1(&sb, &osb);
	error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb));
	return (error);
}


/*
 *  Get file status; this version does not follow links.
 */
/* ARGSUSED */
int
osf1_lstat(td, uap)
	struct thread *td;
	register struct osf1_lstat_args *uap;
{
	struct stat sb;
	struct osf1_stat osb;
	int error;
	struct nameidata nd;
	caddr_t sg = stackgap_init();

	CHECKALTEXIST(td, &sg, uap->path);

	NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE,
	    uap->path, td);
	if ((error = namei(&nd)))
		return (error);
	error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td);
	vput(nd.ni_vp);
	if (error)
		return (error);
	cvtstat2osf1(&sb, &osb);
	error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb));
	return (error);
}


/*
 *  Return status information about a file descriptor.
 */
int
osf1_fstat(td, uap)
	struct thread *td;
	register struct osf1_fstat_args *uap;
{
	struct file *fp;
	struct stat ub;
	struct osf1_stat oub;
	int error;

	if ((error = fget(td, uap->fd, &fp)) != 0)
		return (error);
	error = fo_stat(fp, &ub, td->td_ucred, td);
	fdrop(fp, td);
	cvtstat2osf1(&ub, &oub);
	if (error == 0)
		error = copyout((caddr_t)&oub, (caddr_t)uap->sb,
		    sizeof (oub));
	return (error);
}


#define	bsd2osf_dev(dev)	(umajor(dev) << 20 | uminor(dev))
/*
 * Convert from a stat structure to an osf1 stat structure.
 */
static void
cvtstat2osf1(st, ost)
	struct stat *st;
	struct osf1_stat *ost;
{

	ost->st_dev = bsd2osf_dev(st->st_dev);
	ost->st_ino = st->st_ino;
	ost->st_mode = st->st_mode;
	ost->st_nlink = st->st_nlink;
	ost->st_uid = st->st_uid == -2 ? (u_int16_t) -2 : st->st_uid;
	ost->st_gid = st->st_gid == -2 ? (u_int16_t) -2 : st->st_gid;
	ost->st_rdev = bsd2osf_dev(st->st_rdev);
	ost->st_size = st->st_size;
	ost->st_atime_sec = st->st_atime;
	ost->st_spare1 = 0;
	ost->st_mtime_sec = st->st_mtime;
	ost->st_spare2 = 0;
	ost->st_ctime_sec = st->st_ctime;
	ost->st_spare3 = 0;
	ost->st_blksize = st->st_blksize;
	ost->st_blocks = st->st_blocks;
	ost->st_flags = st->st_flags;
	ost->st_gen = st->st_gen;
}


int
osf1_mknod(td, uap)
	struct thread *td;
	struct osf1_mknod_args *uap;
{

	printf("osf1_mknod no longer implemented\n");
	return ENOSYS;
}


int
osf1_access(td, uap)
	struct thread *td;
	struct osf1_access_args *uap;
{
	caddr_t sg;

	sg = stackgap_init();
	CHECKALTEXIST(td, &sg, uap->path);

	return access(td, (struct access_args *)uap);
}


struct osf1_flock   {
	short	l_type;
	short	l_whence;
	off_t	l_start;
	off_t	l_len;
	pid_t	l_pid;
	};

int
osf1_fcntl(td, uap)
	struct thread *td;
	struct osf1_fcntl_args *uap;
{
	int error;
	long tmp;
	struct osf1_flock osf_flock;
	struct flock bsd_flock;

	error = 0;

	switch (uap->cmd) {

	case F_SETFL:
		/* need to translate flags here */
		tmp = 0;
		if ((long)uap->arg & OSF1_FNONBLOCK)
			tmp |= FNONBLOCK;
		if ((long)uap->arg & OSF1_FAPPEND)
			tmp |= FAPPEND;
		if ((long)uap->arg & OSF1_FDEFER)
			tmp |= FDEFER;
		if ((long)uap->arg & OSF1_FASYNC)
			tmp |= FASYNC;
		if ((long)uap->arg & OSF1_FCREAT)
			tmp |= O_CREAT;
		if ((long)uap->arg & OSF1_FTRUNC)
			tmp |= O_TRUNC;
		if ((long)uap->arg & OSF1_FEXCL)
			tmp |= O_EXCL;
		if ((long)uap->arg & OSF1_FNDELAY)
			tmp |= FNDELAY;
		if ((long)uap->arg & OSF1_FSYNC)
			tmp |= FFSYNC;
		error = kern_fcntl(td, uap->fd, F_SETFL, tmp);
		break;

	case F_SETLK:
	case F_SETLKW:
	case F_GETLK:
		/*
		 *  The OSF/1 flock stucture has a different order than
		 *  the BSD one, but all else is the same.  We must
		 *  reorder the one we've gotten so that flock() groks it.
		 */
		error = copyin(uap->arg, &osf_flock, sizeof(osf_flock));
		if (error)
			return (error);
		bsd_flock.l_type = osf_flock.l_type;
		bsd_flock.l_whence = osf_flock.l_whence;
		bsd_flock.l_start = osf_flock.l_start;
		bsd_flock.l_len = osf_flock.l_len;
		bsd_flock.l_pid = osf_flock.l_pid;
		error = kern_fcntl(td, uap->fd, uap->cmd, (intptr_t)&bsd_flock);
/*		if (error) {
			printf("fcntl called with cmd=%d, args=0x%lx\n returns %d\n",uap->cmd,(long)uap->arg,error);
			printf("bsd_flock.l_type = 0x%x\n", bsd_flock.l_type);
			printf("bsd_flock.l_whence = 0x%x\n", bsd_flock.l_whence);
			printf("bsd_flock.l_start = 0x%lx\n", bsd_flock.l_start);
			printf("bsd_flock.l_len = 0x%lx\n", bsd_flock.l_len);
			printf("bsd_flock.l_pid = 0x%x\n", bsd_flock.l_pid);
		}
*/
		if ((uap->cmd == F_GETLK) && !error) {
			/*
			 * XXX: Why are we hardcoding F_UNLCK here instead of
			 * copying the structure members from bsd_flock?
			 */
			osf_flock.l_type = F_UNLCK;
			error = copyout(&osf_flock, uap->arg,
			    sizeof(osf_flock));
		}
		break;
	default:
		error = kern_fcntl(td, uap->fd, uap->cmd, (intptr_t)uap->arg);

		if ((uap->cmd == OSF1_F_GETFL) && !error ) {
			tmp = td->td_retval[0] & O_ACCMODE;
			if (td->td_retval[0] & FNONBLOCK)
				tmp |= OSF1_FNONBLOCK;
			if (td->td_retval[0] & FAPPEND)
				tmp |= OSF1_FAPPEND;
			if (td->td_retval[0] & FDEFER)
				tmp |= OSF1_FDEFER;
			if (td->td_retval[0] & FASYNC)
				tmp |= OSF1_FASYNC;
			if (td->td_retval[0] & O_CREAT)
				tmp |= OSF1_FCREAT;
			if (td->td_retval[0] & O_TRUNC)
				tmp |= OSF1_FTRUNC;
			if (td->td_retval[0] & O_EXCL)
				tmp |= OSF1_FEXCL;
			if (td->td_retval[0] & FNDELAY)
				tmp |= OSF1_FNDELAY;
			if (td->td_retval[0] & FFSYNC)
				tmp |= OSF1_FSYNC;
			td->td_retval[0] = tmp;
		}
	}

	return (error);
}


#if 0
int
osf1_fcntl(td, uap)
	struct thread *td;
	struct osf1_fcntl_args *uap;
{
	struct fcntl_args a;
	long tmp;
	int error;

	a.fd = uap->fd;

	switch (uap->cmd) {

	case OSF1_F_DUPFD:
		a.cmd = F_DUPFD;
		a.arg = (long)uap->arg;
		break;

	case OSF1_F_GETFD:
		a.cmd = F_GETFD;
		a.arg = (long)uap->arg;
		break;

	case OSF1_F_SETFD:
		a.cmd = F_SETFD;
		a.arg = (long)uap->arg;
		break;

	case OSF1_F_GETFL:
		a.cmd = F_GETFL;
		a.arg = (long)uap->arg;		/* ignored */
		break;

	case OSF1_F_SETFL:
		a.cmd = F_SETFL;
		tmp = 0;
		if ((long)uap->arg & OSF1_FAPPEND)
			tmp |= FAPPEND;
		if ((long)uap->arg & OSF1_FNONBLOCK)
			tmp |= FNONBLOCK;
		if ((long)uap->arg & OSF1_FASYNC)
			tmp |= FASYNC;
		if ((long)uap->arg & OSF1_FSYNC)
			tmp |= FFSYNC;
		a.arg = tmp;
		break;

	default:					/* XXX other cases */
		return (EINVAL);
	}

	error = fcntl(td, &a);

	if (error)
		return error;

	switch (uap->cmd) {
	case OSF1_F_GETFL:
		/* XXX */
		break;
	}

	return error;
}
#endif

int
osf1_socket(td, uap)
	struct thread *td;
	struct osf1_socket_args *uap;
{
	struct socket_args a;

	if (uap->type > AF_LINK)
		return (EINVAL);	/* XXX After AF_LINK, divergence. */

	a.domain = uap->domain;
	a.type = uap->type;
	a.protocol = uap->protocol;

	return socket(td, &a);
}


int
osf1_sendto(td, uap)
	struct thread *td;
	register struct osf1_sendto_args *uap;
{
	struct sendto_args a;

	if (uap->flags & ~0x7f)		/* unsupported flags */
		return (EINVAL);

	a.s = uap->s;
	a.buf = uap->buf;
	a.len = uap->len;
	a.flags = uap->flags;
	a.to = (caddr_t)uap->to;
	a.tolen = uap->tolen;

	return sendto(td, &a);
}


int
osf1_reboot(td, uap)
	struct thread *td;
	struct osf1_reboot_args *uap;
{
	struct reboot_args a;

	if (uap->opt & ~OSF1_RB_ALLFLAGS &&
	    uap->opt & (OSF1_RB_ALTBOOT|OSF1_RB_UNIPROC))
		return (EINVAL);

	a.opt = 0;

	if (uap->opt & OSF1_RB_ASKNAME)
		a.opt |= RB_ASKNAME;
	if (uap->opt & OSF1_RB_SINGLE)
		a.opt |= RB_SINGLE;
	if (uap->opt & OSF1_RB_NOSYNC)
		a.opt |= RB_NOSYNC;
	if (uap->opt & OSF1_RB_HALT)
		a.opt |= RB_HALT;
	if (uap->opt & OSF1_RB_INITNAME)
		a.opt |= RB_INITNAME;
	if (uap->opt & OSF1_RB_DFLTROOT)
		a.opt |= RB_DFLTROOT;

	return reboot(td, &a);
}


int
osf1_lseek(td, uap)
	struct thread *td;
	struct osf1_lseek_args *uap;
{
	struct lseek_args a;

	a.fd = uap->fd;
	a.pad = 0;
	a.offset = uap->offset;
	a.whence = uap->whence;

	return lseek(td, &a);
}


/*
 *  OSF/1 defines _POSIX_SAVED_IDS, which means that our normal
 *  setuid() won't work.
 *
 *  Instead, by P1003.1b-1993, setuid() is supposed to work like:
 *	If the process has appropriate [super-user] privileges, the
 *	    setuid() function sets the real user ID, effective user
 *	    ID, and the saved set-user-ID to uid.
 *	If the process does not have appropriate privileges, but uid
 *	    is equal to the real user ID or the saved set-user-ID, the
 *	    setuid() function sets the effective user ID to uid; the
 *	    real user ID and saved set-user-ID remain unchanged by
 *	    this function call.
 */
int
osf1_setuid(td, uap)
	struct thread *td;
	struct osf1_setuid_args *uap;
{
	struct proc *p;
	int error;
	uid_t uid;
	struct uidinfo *uip;
	struct ucred *newcred, *oldcred;

	p = td->td_proc;
	uid = uap->uid;
	newcred = crget();
	uip = uifind(uid);
	PROC_LOCK(p);
	oldcred = p->p_ucred;

	if ((error = suser_cred(p->p_ucred, SUSER_ALLOWJAIL)) != 0 &&
	    uid != oldcred->cr_ruid && uid != oldcred->cr_svuid) {
		PROC_UNLOCK(p);
		uifree(uip);
		crfree(newcred);
		return (error);
	}

	crcopy(newcred, oldcred);
	if (error == 0) {
		if (uid != oldcred->cr_ruid) {
			change_ruid(newcred, uip);
			setsugid(p);
		}
		if (oldcred->cr_svuid != uid) {
			change_svuid(newcred, uid);
			setsugid(p);
		}
	}
	if (newcred->cr_uid != uid) {
		change_euid(newcred, uip);
		setsugid(p);
	}
	p->p_ucred = newcred;
	PROC_UNLOCK(p);
	uifree(uip);
	crfree(oldcred);
	return (0);
}


/*
 *  OSF/1 defines _POSIX_SAVED_IDS, which means that our normal
 *  setgid() won't work.
 *
 *  If you change "uid" to "gid" in the discussion, above, about
 *  setuid(), you'll get a correct description of setgid().
 */
int
osf1_setgid(td, uap)
	struct thread *td;
	struct osf1_setgid_args *uap;
{
	struct proc *p;
	int error;
	gid_t gid;
	struct ucred *newcred, *oldcred;

	p = td->td_proc;
	gid = uap->gid;
	newcred = crget();
	PROC_LOCK(p);
	oldcred = p->p_ucred;

	if (((error = suser_cred(p->p_ucred, SUSER_ALLOWJAIL)) != 0 ) &&
	    gid != oldcred->cr_rgid && gid != oldcred->cr_svgid) {
		PROC_UNLOCK(p);
		crfree(newcred);
		return (error);
	}

	crcopy(newcred, oldcred);
	if (error == 0) {
		if (gid != oldcred->cr_rgid) {
			change_rgid(newcred, gid);
			setsugid(p);
		}
		if (oldcred->cr_svgid != gid) {
			change_svgid(newcred, gid);
			setsugid(p);
		}
	}
	if (newcred->cr_groups[0] != gid) {
		change_egid(newcred, gid);
		setsugid(p);
	}
	p->p_ucred = newcred;
	PROC_UNLOCK(p);
	crfree(oldcred);
	return (0);
}


/*
 *  The structures end up being the same... but we can't be sure that
 *  the other word of our iov_len is zero!
 */
struct osf1_iovec {
	char	*iov_base;
	int	iov_len;
};
#define	STACKGAPLEN	400
int
osf1_readv(td, uap)
	struct thread *td;
	struct osf1_readv_args *uap;
{
	int error, osize, nsize, i;
	caddr_t sg;
	struct readv_args /* {
		syscallarg(int) fd;
		syscallarg(struct iovec *) iovp;
		syscallarg(u_int) iovcnt;
	} */ a;
	struct osf1_iovec *oio;
	struct iovec *nio;

	sg = stackgap_init();

	if (uap->iovcnt > (STACKGAPLEN / sizeof (struct iovec)))
		return (EINVAL);

	osize = uap->iovcnt * sizeof (struct osf1_iovec);
	nsize = uap->iovcnt * sizeof (struct iovec);

	oio = malloc(osize, M_TEMP, M_WAITOK);
	nio = malloc(nsize, M_TEMP, M_WAITOK);

	error = 0;
	if ((error = copyin(uap->iovp, oio, osize)))
		goto punt;
	for (i = 0; i < uap->iovcnt; i++) {
		nio[i].iov_base = oio[i].iov_base;
		nio[i].iov_len = oio[i].iov_len;
	}

	a.fd = uap->fd;
	a.iovp = stackgap_alloc(&sg, nsize);
	a.iovcnt = uap->iovcnt;

	if ((error = copyout(nio, (caddr_t)a.iovp, nsize)))
		goto punt;
	error = readv(td, &a);

punt:
	free(oio, M_TEMP);
	free(nio, M_TEMP);
	return (error);
}


int
osf1_writev(td, uap)
	struct thread *td;
	struct osf1_writev_args *uap;
{
	int error, i, nsize, osize;
	caddr_t sg;
	struct writev_args /* {
		syscallarg(int) fd;
		syscallarg(struct iovec *) iovp;
		syscallarg(u_int) iovcnt;
	} */ a;
	struct osf1_iovec *oio;
	struct iovec *nio;

	sg = stackgap_init();

	if (uap->iovcnt > (STACKGAPLEN / sizeof (struct iovec)))
		return (EINVAL);

	osize = uap->iovcnt * sizeof (struct osf1_iovec);
	nsize = uap->iovcnt * sizeof (struct iovec);

	oio = malloc(osize, M_TEMP, M_WAITOK);
	nio = malloc(nsize, M_TEMP, M_WAITOK);

	error = 0;
	if ((error = copyin(uap->iovp, oio, osize)))
		goto punt;
	for (i = 0; i < uap->iovcnt; i++) {
		nio[i].iov_base = oio[i].iov_base;
		nio[i].iov_len = oio[i].iov_len;
	}

	a.fd = uap->fd;
	a.iovp = stackgap_alloc(&sg, nsize);
	a.iovcnt = uap->iovcnt;

	if ((error = copyout(nio, (caddr_t)a.iovp, nsize)))
		goto punt;
	error = writev(td, &a);

punt:
	free(oio, M_TEMP);
	free(nio, M_TEMP);
	return (error);
}


/*
 *  More of the stupid off_t padding!
 */
int
osf1_truncate(td, uap)
	struct thread *td;
	struct osf1_truncate_args *uap;
{
	caddr_t sg;
	struct truncate_args a;

	sg = stackgap_init();
        CHECKALTEXIST(td, &sg, uap->path);

	a.path = uap->path;
	a.pad = 0;
	a.length = uap->length;

	return truncate(td, &a);
}


int
osf1_ftruncate(td, uap)
	struct thread *td;
	struct osf1_ftruncate_args *uap;
{
	struct ftruncate_args a;

	a.fd = uap->fd;
	a.pad = 0;
	a.length = uap->length;

	return ftruncate(td, &a);
}


static int
osf2bsd_pathconf(name)
	int *name;
{

	switch (*name) {
	case _OSF1_PC_LINK_MAX:
	case _OSF1_PC_MAX_CANON:
	case _OSF1_PC_MAX_INPUT:
	case _OSF1_PC_NAME_MAX:
		*name -= 10;
		break;

	case _OSF1_PC_PATH_MAX:
	case _OSF1_PC_PIPE_BUF:
		*name -= 9;

	case _OSF1_PC_NO_TRUNC:
		*name = _PC_NO_TRUNC;
		break;

	case _OSF1_PC_CHOWN_RESTRICTED:
		*name = _PC_CHOWN_RESTRICTED;
		break;

	case _OSF1_PC_VDISABLE:
		*name = _PC_VDISABLE;
		break;

	default:
		return (EINVAL);
	}
	return 0;
}


int
osf1_pathconf(td, uap)
	struct thread *td;
	struct osf1_pathconf_args *uap;
{

	if (osf2bsd_pathconf(&uap->name))
		return (EINVAL);
	else
		return (pathconf(td, (void *)uap));
}


int
osf1_fpathconf(td, uap)
	struct thread *td;
	struct osf1_fpathconf_args *uap;
{

	if (osf2bsd_pathconf(&uap->name))
		return (EINVAL);
	else
		return (fpathconf(td, (void *)uap));
}


int
osf1_getrusage(td, uap)
	struct thread *td;
	struct osf1_getrusage_args *uap;
{
	struct proc *p;
	struct rusage *rup;
	struct osf1_rusage oru;

	p = td->td_proc;
	switch (uap->who) {
	case RUSAGE_SELF:
		rup = &p->p_stats->p_ru;
		mtx_lock_spin(&sched_lock);
		calcru(p, &rup->ru_utime, &rup->ru_stime, NULL);
		mtx_unlock_spin(&sched_lock);
		break;

	case RUSAGE_CHILDREN:
		rup = &p->p_stats->p_cru;
		break;

	default:
		return (EINVAL);
	}
	TV_CP(rup->ru_utime, oru.ru_utime);
	TV_CP(rup->ru_stime, oru.ru_stime);
	bcopy(&(rup->ru_first), &(oru.ru_first),
	    (&(oru.ru_last) - &(oru.ru_first)));

	return (copyout((caddr_t)&oru, (caddr_t)uap->rusage,
	    sizeof (struct osf1_rusage)));
}


int
osf1_wait4(td, uap)
	struct thread *td;
	struct osf1_wait4_args *uap;
{
	int error, status;
	struct osf1_rusage oru;
	struct rusage ru;

	error = kern_wait(td, uap->pid, &status, uap->options, &ru);
	if (error)
		return (error);
	if (uap->status != NULL)
		error = copyout(&status, uap->status, sizeof(status));
	if (uap->rusage != NULL && error == 0) {
		TV_CP(ru.ru_utime, oru.ru_utime);
		TV_CP(ru.ru_stime, oru.ru_stime);
		bcopy(&ru.ru_first, &oru.ru_first,
		    (&(oru.ru_last) - &(oru.ru_first)));
		error = copyout(&oru, uap->rusage, sizeof (struct osf1_rusage));
	}
	return (error);
}


int
osf1_madvise(td, uap)
	struct thread *td;
	struct osf1_madvise_args *uap;
{

	/* XXX */
	return EINVAL;
}


int
osf1_execve(td, uap)
	struct thread *td;
	struct osf1_execve_args *uap;
{
	caddr_t sg;
	struct execve_args ap;

	sg = stackgap_init();
	CHECKALTEXIST(td, &sg, uap->path);

	ap.fname = uap->path;
	ap.argv = uap->argp;
	ap.envv = uap->envp;

	return execve(td, &ap);
}


int
osf1_usleep_thread(td, uap)
	struct thread *td;
	struct osf1_usleep_thread_args *uap;
{
	int error, s, timo;
	struct osf1_timeval time;
	struct timeval difftv, endtv, sleeptv, tv;

	if ((error = copyin(uap->sleep, &time, sizeof time)))
		return (error);

	sleeptv.tv_sec =  (u_long)time.tv_sec;
	sleeptv.tv_usec = (u_long)time.tv_usec;
	timo = tvtohz(&sleeptv);

	/*
	 *  Some callers use usleep(0) as a sort of thread-yield so make
	 *  sure that the timeout is non-zero.
	 */

	if (timo == 0)
		timo = 1;
	s = splclock();
	microtime(&tv);
	splx(s);

	tsleep(td, PUSER|PCATCH, "OSF/1", timo);

	if (uap->slept != NULL) {
		s = splclock();
		microtime(&endtv);
		timersub(&time, &endtv, &difftv);
		splx(s);
		if (tv.tv_sec < 0 || tv.tv_usec < 0)
			tv.tv_sec = tv.tv_usec = 0;
		TV_CP(difftv, time)
		error = copyout(&time, uap->slept, sizeof time);
	}
	return (error);
}


int osf1_gettimeofday(td, uap)
	struct thread *td;
	register struct osf1_gettimeofday_args *uap;
{
	int error;
	struct timeval atv;
	struct timezone tz;
	struct osf1_timeval otv;

	error = 0;

	if (uap->tp) {
		microtime(&atv);
		otv.tv_sec = atv.tv_sec;
		otv.tv_usec = atv.tv_usec;
		if ((error = copyout((caddr_t)&otv, (caddr_t)uap->tp,
		    sizeof (otv))))
			return (error);
	}
	if (uap->tzp) {
		tz.tz_minuteswest = tz_minuteswest;
		tz.tz_dsttime = tz_dsttime;
		error = copyout((caddr_t)&tz, (caddr_t)uap->tzp, sizeof (tz));
	}
	return (error);
}


int osf1_select(td, uap)
	struct thread *td;
	register struct osf1_select_args *uap;
{
	if (uap->tv) {
		int error;
		caddr_t sg;
		struct osf1_timeval otv;
		struct timeval tv;

		sg = stackgap_init();

		if ((error=copyin((caddr_t)uap->tv,(caddr_t)&otv,sizeof(otv))))
			return(error);
		TV_CP(otv,tv);
		uap->tv = stackgap_alloc(&sg, sizeof(struct timeval));
		if ((error=copyout((caddr_t)&tv, (caddr_t)uap->tv,sizeof(tv))))
			return(error);
	}
	return(select(td, (struct select_args *)uap));
}


int
osf1_setitimer(td, uap)
	struct thread *td;
	struct osf1_setitimer_args *uap;
{

	int error;
	caddr_t old_oitv, sg;
	struct itimerval itv;
	struct osf1_itimerval otv;

	error = 0;
	old_oitv = (caddr_t)uap->oitv;
	sg = stackgap_init();

	if ((error = copyin((caddr_t)uap->itv,(caddr_t)&otv,sizeof(otv)))) {
		printf("%s(%d): error = %d\n", __FILE__, __LINE__, error);
		return error;
	}
	TV_CP(otv.it_interval,itv.it_interval);
	TV_CP(otv.it_value,itv.it_value);
	uap->itv = stackgap_alloc(&sg, sizeof(struct itimerval));
	if ((error = copyout((caddr_t)&itv,(caddr_t)uap->itv,sizeof(itv)))) {
		printf("%s(%d): error = %d\n", __FILE__, __LINE__, error);
		return error;
	}
	uap->oitv = stackgap_alloc(&sg, sizeof(struct itimerval));
	if ((error = setitimer(td, (struct setitimer_args *)uap))) {
		printf("%s(%d): error = %d\n", __FILE__, __LINE__, error);
		return error;
	}
	if ((error = copyin((caddr_t)uap->oitv,(caddr_t)&itv,sizeof(itv)))) {
		printf("%s(%d): error = %d\n", __FILE__, __LINE__, error);
		return error;
	}
	TV_CP(itv.it_interval,otv.it_interval);
	TV_CP(itv.it_value,otv.it_value);
	if (old_oitv
	    && (error = copyout((caddr_t)&otv, old_oitv, sizeof(otv)))) {
		printf("%s(%d): error = %d\n", __FILE__, __LINE__, error);
	}
	return error;
}


int
osf1_getitimer(td, uap)
	struct thread *td;
	struct osf1_getitimer_args *uap;
{
	int error;
	caddr_t old_itv, sg;
	struct itimerval itv;
	struct osf1_itimerval otv;

	error = 0;
	old_itv = (caddr_t)uap->itv;
	sg = stackgap_init();

	uap->itv = stackgap_alloc(&sg, sizeof(struct itimerval));
	if ((error = getitimer(td, (struct getitimer_args *)uap))) {
		printf("%s(%d): error = %d\n", __FILE__, __LINE__, error);
		return error;
	}
	if ((error = copyin((caddr_t)uap->itv,(caddr_t)&itv,sizeof(itv)))) {
		printf("%s(%d): error = %d\n", __FILE__, __LINE__, error);
		return error;
	}
	TV_CP(itv.it_interval,otv.it_interval);
	TV_CP(itv.it_value,otv.it_value);
	if ((error = copyout((caddr_t)&otv, old_itv, sizeof(otv)))) {
		printf("%s(%d): error = %d\n", __FILE__, __LINE__, error);
	}
	return error;
}


int
osf1_proplist_syscall(td, uap)
	struct thread *td;
	struct osf1_proplist_syscall_args *uap;
{

	return(EOPNOTSUPP);
}


int
osf1_ntpgettime(td, uap)
	struct thread *td;
	struct  osf1_ntpgettime_args *uap;
{

	return(ENOSYS);
}


int
osf1_ntpadjtime(td, uap)
	struct thread *td;
	struct  osf1_ntpadjtime_args *uap;
{

	return(ENOSYS);
}


int
osf1_setpgrp(td, uap)
	struct thread *td;
	struct  osf1_setpgrp_args *uap;
{

	return(setpgid(td, (struct setpgid_args *)uap));
}


int
osf1_uswitch(td, uap)
	struct thread *td;
	struct osf1_uswitch_args *uap;
{
	struct proc *p;
	int rv;
	vm_map_entry_t entry;
	vm_offset_t zero;

	GIANT_REQUIRED;
	p = td->td_proc;
	zero = 0;

	if (uap->cmd == OSF1_USC_GET) {
		if (vm_map_lookup_entry(&(p->p_vmspace->vm_map), 0, &entry))
			td->td_retval[0] =  OSF1_USW_NULLP;
		else
			td->td_retval[0] =  0;
		return(KERN_SUCCESS);
	} else if (uap->cmd == OSF1_USC_SET)
		if (uap->mask & OSF1_USW_NULLP) {
			rv = vm_mmap(&(p->p_vmspace->vm_map), &zero, PAGE_SIZE,
			    VM_PROT_READ, VM_PROT_ALL,
			    MAP_PRIVATE | MAP_FIXED | MAP_ANON, NULL, 0);
			if (!rv)
				return(KERN_SUCCESS);
			else {
				printf(
				    "osf1_uswitch:vm_mmap of zero page failed with status %d\n",
				    rv);
				return(rv);
			}
		}
	return(EINVAL);
}


int
osf1_classcntl(td, uap)
	struct thread *td;
	struct  osf1_classcntl_args *uap;
{

	return(EACCES); /* class scheduling not enabled */
}


struct osf1_tbl_loadavg
{
	union {
		long   l[3];
		double d[3];
	} tl_avenrun;
	int  tl_lscale;
	long tl_mach_factor[3]; /* ???? */
};

struct osf1_tbl_sysinfo {
	long si_user;
	long si_nice;
	long si_sys;
	long si_idle;
	long si_hz;
	long si_phz;
	long si_boottime;
	long wait;
};

#define TBL_LOADAVG	3
#define TBL_SYSINFO    12

int
osf1_table(td, uap)
	struct thread *td;
	struct  osf1_table_args /*{
				long id;
				long index;
				void *addr;
				long nel;
				u_long lel;
				}*/ *uap;
{
	int retval;
	struct osf1_tbl_loadavg ld;
	struct osf1_tbl_sysinfo si;

	retval = 0;

	switch(uap->id) {
	case TBL_LOADAVG: /* xemacs wants this */
		if ((uap->index != 0) || (uap->nel != 1))
			retval = EINVAL;
		bcopy(&averunnable, &ld, sizeof(averunnable));
		ld.tl_lscale = (u_int)averunnable.fscale;
		retval = copyout(&ld, uap->addr, sizeof(ld));
		break;
	case TBL_SYSINFO:
		if ((uap->index != 0) || (uap->nel != 1))
			retval = EINVAL;
		bzero(&si, sizeof(si));
#if 0
		si.si_user = cp_time[CP_USER];
		si.si_nice = cp_time[CP_NICE];
		si.si_sys  = cp_time[CP_SYS];
		si.si_idle = cp_time[CP_IDLE];
		si.wait    = cp_time[CP_INTR];
#endif
		si.si_hz = hz;
		si.si_phz = profhz;
		si.si_boottime = boottime.tv_sec;
		retval = copyout(&si, uap->addr, sizeof(si));
		break;
	default:
		printf("osf1_table: %ld, %ld, %p, %ld %ld\n",
		    uap->id, uap->index, uap->addr, uap->nel, uap->lel);
		retval = EINVAL;
	}
	return retval;
}


/*
 * MPSAFE
 */
int
osf1_sysinfo(td, uap)
	struct thread *td;
	struct  osf1_sysinfo_args /*{
				int cmd;
				char *buf;
				long count;
				}*/ *uap;
{
	int name[2], retval;
	size_t bytes, len;
	char *string;

	string = NULL;

	switch(uap->cmd) {
	case 1: /* OS */
		string = "OSF1";
		break;
	case 2:	/* hostname, from ogethostname */
		len = uap->count;
		name[0] = CTL_KERN;
		name[1] = KERN_HOSTNAME;
		mtx_lock(&Giant);
		retval = userland_sysctl(td, name, 2, uap->buf, &len,
					1, 0, 0, &bytes);
		mtx_unlock(&Giant);
		td->td_retval[0] =  bytes;
		return(retval);
		break;
	case 3: /* release of osf1 */
		string = "V4.0";
		break;
	case 4: /* minor version of osf1 */
		string = "878";
		break;
	case 5: /* machine or arch */
	case 6:
		string = "alpha";
		break;
	case 7: /* serial number, real osf1 returns 0! */
		string = "0";
		break;
	case 8: /* HW vendor */
		string = "Digital";
		break;
	case 9: /* dunno, this is what du does.. */
		return(ENOSYS);
		break;
	default:
		return(EINVAL);
	}
	bytes = min(uap->count, strlen(string)+1);
	copyout(string, uap->buf, bytes);
	td->td_retval[0] =  bytes;
	return(0);
}