/*
* linux/arch/alpha/kernel/osf_sys.c
*
* Copyright (C) 1995 Linus Torvalds
*/
/*
* This file handles some of the stranger OSF/1 system call interfaces.
* Some of the system calls expect a non-C calling standard, others have
* special parameter blocks..
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/syscalls.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/utsname.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/major.h>
#include <linux/stat.h>
#include <linux/mman.h>
#include <linux/shm.h>
#include <linux/poll.h>
#include <linux/file.h>
#include <linux/types.h>
#include <linux/ipc.h>
#include <linux/namei.h>
#include <linux/uio.h>
#include <linux/vfs.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
#include <asm/fpu.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/sysinfo.h>
#include <asm/thread_info.h>
#include <asm/hwrpb.h>
#include <asm/processor.h>
/*
* Brk needs to return an error. Still support Linux's brk(0) query idiom,
* which OSF programs just shouldn't be doing. We're still not quite
* identical to OSF as we don't return 0 on success, but doing otherwise
* would require changes to libc. Hopefully this is good enough.
*/
SYSCALL_DEFINE1(osf_brk, unsigned long, brk)
{
unsigned long retval = sys_brk(brk);
if (brk && brk != retval)
retval = -ENOMEM;
return retval;
}
/*
* This is pure guess-work..
*/
SYSCALL_DEFINE4(osf_set_program_attributes, unsigned long, text_start,
unsigned long, text_len, unsigned long, bss_start,
unsigned long, bss_len)
{
struct mm_struct *mm;
mm = current->mm;
mm->end_code = bss_start + bss_len;
mm->start_brk = bss_start + bss_len;
mm->brk = bss_start + bss_len;
#if 0
printk("set_program_attributes(%lx %lx %lx %lx)\n",
text_start, text_len, bss_start, bss_len);
#endif
return 0;
}
/*
* OSF/1 directory handling functions...
*
* The "getdents()" interface is much more sane: the "basep" stuff is
* braindamage (it can't really handle filesystems where the directory
* offset differences aren't the same as "d_reclen").
*/
#define NAME_OFFSET offsetof (struct osf_dirent, d_name)
struct osf_dirent {
unsigned int d_ino;
unsigned short d_reclen;
unsigned short d_namlen;
char d_name[1];
};
struct osf_dirent_callback {
struct dir_context ctx;
struct osf_dirent __user *dirent;
long __user *basep;
unsigned int count;
int error;
};
static int
osf_filldir(struct dir_context *ctx, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type)
{
struct osf_dirent __user *dirent;
struct osf_dirent_callback *buf =
container_of(ctx, struct osf_dirent_callback, ctx);
unsigned int reclen = ALIGN(NAME_OFFSET + namlen + 1, sizeof(u32));
unsigned int d_ino;
buf->error = -EINVAL; /* only used if we fail */
if (reclen > buf->count)
return -EINVAL;
d_ino = ino;
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
buf->error = -EOVERFLOW;
return -EOVERFLOW;
}
if (buf->basep) {
if (put_user(offset, buf->basep))
goto Efault;
buf->basep = NULL;
}
dirent = buf->dirent;
if (put_user(d_ino, &dirent->d_ino) ||
put_user(namlen, &dirent->d_namlen) ||
put_user(reclen, &dirent->d_reclen) ||
copy_to_user(dirent->d_name, name, namlen) ||
put_user(0, dirent->d_name + namlen))
goto Efault;
dirent = (void __user *)dirent + reclen;
buf->dirent = dirent;
buf->count -= reclen;
return 0;
Efault:
buf->error = -EFAULT;
return -EFAULT;
}
SYSCALL_DEFINE4(osf_getdirentries, unsigned int, fd,
struct osf_dirent __user *, dirent, unsigned int, count,
long __user *, basep)
{
int error;
struct fd arg = fdget(fd);
struct osf_dirent_callback buf = {
.ctx.actor = osf_filldir,
.dirent = dirent,
.basep = basep,
.count = count
};
if (!arg.file)
return -EBADF;
error = iterate_dir(arg.file, &buf.ctx);
if (error >= 0)
error = buf.error;
if (count != buf.count)
error = count - buf.count;
fdput(arg);
return error;
}
#undef NAME_OFFSET
SYSCALL_DEFINE6(osf_mmap, unsigned long, addr, unsigned long, len,
unsigned long, prot, unsigned long, flags, unsigned long, fd,
unsigned long, off)
{
unsigned long ret = -EINVAL;
#if 0
if (flags & (_MAP_HASSEMAPHORE | _MAP_INHERIT | _MAP_UNALIGNED))
printk("%s: unimplemented OSF mmap flags %04lx\n",
current->comm, flags);
#endif
if ((off + PAGE_ALIGN(len)) < off)
goto out;
if (off & ~PAGE_MASK)
goto out;
ret = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
out:
return ret;
}
struct osf_stat {
int st_dev;
int st_pad1;
unsigned st_mode;
unsigned short st_nlink;
short st_nlink_reserved;
unsigned st_uid;
unsigned st_gid;
int st_rdev;
int st_ldev;
long st_size;
int st_pad2;
int st_uatime;
int st_pad3;
int st_umtime;
int st_pad4;
int st_uctime;
int st_pad5;
int st_pad6;
unsigned st_flags;
unsigned st_gen;
long st_spare[4];
unsigned st_ino;
int st_ino_reserved;
int st_atime;
int st_atime_reserved;
int st_mtime;
int st_mtime_reserved;
int st_ctime;
int st_ctime_reserved;
long st_blksize;
long st_blocks;
};
/*
* The OSF/1 statfs structure is much larger, but this should
* match the beginning, at least.
*/
struct osf_statfs {
short f_type;
short f_flags;
int f_fsize;
int f_bsize;
int f_blocks;
int f_bfree;
int f_bavail;
int f_files;
int f_ffree;
__kernel_fsid_t f_fsid;
};
struct osf_statfs64 {
short f_type;
short f_flags;
int f_pad1;
int f_pad2;
int f_pad3;
int f_pad4;
int f_pad5;
int f_pad6;
int f_pad7;
__kernel_fsid_t f_fsid;
u_short f_namemax;
short f_reserved1;
int f_spare[8];
char f_pad8[90];
char f_pad9[90];
long mount_info[10];
u_long f_flags2;
long f_spare2[14];
long f_fsize;
long f_bsize;
long f_blocks;
long f_bfree;
long f_bavail;
long f_files;
long f_ffree;
};
static int
linux_to_osf_stat(struct kstat *lstat, struct osf_stat __user *osf_stat)
{
struct osf_stat tmp = { 0 };
tmp.st_dev = lstat->dev;
tmp.st_mode = lstat->mode;
tmp.st_nlink = lstat->nlink;
tmp.st_uid = from_kuid_munged(current_user_ns(), lstat->uid);
tmp.st_gid = from_kgid_munged(current_user_ns(), lstat->gid);
tmp.st_rdev = lstat->rdev;
tmp.st_ldev = lstat->rdev;
tmp.st_size = lstat->size;
tmp.st_uatime = lstat->atime.tv_nsec / 1000;
tmp.st_umtime = lstat->mtime.tv_nsec / 1000;
tmp.st_uctime = lstat->ctime.tv_nsec / 1000;
tmp.st_ino = lstat->ino;
tmp.st_atime = lstat->atime.tv_sec;
tmp.st_mtime = lstat->mtime.tv_sec;
tmp.st_ctime = lstat->ctime.tv_sec;
tmp.st_blksize = lstat->blksize;
tmp.st_blocks = lstat->blocks;
return copy_to_user(osf_stat, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}
static int
linux_to_osf_statfs(struct kstatfs *linux_stat, struct osf_statfs __user *osf_stat,
unsigned long bufsiz)
{
struct osf_statfs tmp_stat;
tmp_stat.f_type = linux_stat->f_type;
tmp_stat.f_flags = 0; /* mount flags */
tmp_stat.f_fsize = linux_stat->f_frsize;
tmp_stat.f_bsize = linux_stat->f_bsize;
tmp_stat.f_blocks = linux_stat->f_blocks;
tmp_stat.f_bfree = linux_stat->f_bfree;
tmp_stat.f_bavail = linux_stat->f_bavail;
tmp_stat.f_files = linux_stat->f_files;
tmp_stat.f_ffree = linux_stat->f_ffree;
tmp_stat.f_fsid = linux_stat->f_fsid;
if (bufsiz > sizeof(tmp_stat))
bufsiz = sizeof(tmp_stat);
return copy_to_user(osf_stat, &tmp_stat, bufsiz) ? -EFAULT : 0;
}
static int
linux_to_osf_statfs64(struct kstatfs *linux_stat, struct osf_statfs64 __user *osf_stat,
unsigned long bufsiz)
{
struct osf_statfs64 tmp_stat = { 0 };
tmp_stat.f_type = linux_stat->f_type;
tmp_stat.f_fsize = linux_stat->f_frsize;
tmp_stat.f_bsize = linux_stat->f_bsize;
tmp_stat.f_blocks = linux_stat->f_blocks;
tmp_stat.f_bfree = linux_stat->f_bfree;
tmp_stat.f_bavail = linux_stat->f_bavail;
tmp_stat.f_files = linux_stat->f_files;
tmp_stat.f_ffree = linux_stat->f_ffree;
tmp_stat.f_fsid