syscalls.c 4.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9
/*
 * Copyright 2003 PathScale, Inc.
 *
 * Licensed under the GPL
 */

#include "linux/linkage.h"
#include "linux/slab.h"
#include "linux/shm.h"
10 11
#include "linux/utsname.h"
#include "linux/personality.h"
L
Linus Torvalds 已提交
12 13 14 15 16 17 18
#include "asm/uaccess.h"
#define __FRAME_OFFSETS
#include "asm/ptrace.h"
#include "asm/unistd.h"
#include "asm/prctl.h" /* XXX This should get the constants from libc */
#include "choose-mode.h"

19 20 21 22 23 24 25 26 27 28 29
asmlinkage long sys_uname64(struct new_utsname __user * name)
{
	int err;
	down_read(&uts_sem);
	err = copy_to_user(name, &system_utsname, sizeof (*name));
	up_read(&uts_sem);
	if (personality(current->personality) == PER_LINUX32)
		err |= copy_to_user(&name->machine, "i686", 5);
	return err ? -EFAULT : 0;
}

L
Linus Torvalds 已提交
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
#ifdef CONFIG_MODE_TT
extern int modify_ldt(int func, void *ptr, unsigned long bytecount);

long sys_modify_ldt_tt(int func, void *ptr, unsigned long bytecount)
{
	/* XXX This should check VERIFY_WRITE depending on func, check this
	 * in i386 as well.
	 */
	if (!access_ok(VERIFY_READ, ptr, bytecount))
		return -EFAULT;
	return(modify_ldt(func, ptr, bytecount));
}
#endif

#ifdef CONFIG_MODE_SKAS
extern int userspace_pid[];

long sys_modify_ldt_skas(int func, void *ptr, unsigned long bytecount)
{
	struct ptrace_ldt ldt;
        void *buf;
        int res, n;

        buf = kmalloc(bytecount, GFP_KERNEL);
        if(buf == NULL)
                return(-ENOMEM);

        res = 0;

        switch(func){
        case 1:
        case 0x11:
                res = copy_from_user(buf, ptr, bytecount);
                break;
        }

        if(res != 0){
                res = -EFAULT;
                goto out;
        }

	ldt = ((struct ptrace_ldt) { .func	= func,
				     .ptr	= buf,
				     .bytecount = bytecount });
#warning Need to look up userspace_pid by cpu
	res = ptrace(PTRACE_LDT, userspace_pid[0], 0, (unsigned long) &ldt);
        if(res < 0)
                goto out;

        switch(func){
        case 0:
        case 2:
                n = res;
                res = copy_to_user(ptr, buf, n);
                if(res != 0)
                        res = -EFAULT;
                else
                        res = n;
                break;
        }

 out:
        kfree(buf);
        return(res);
}
#endif

long sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
{
        return(CHOOSE_MODE_PROC(sys_modify_ldt_tt, sys_modify_ldt_skas, func,
                                ptr, bytecount));
}

#ifdef CONFIG_MODE_TT
extern long arch_prctl(int code, unsigned long addr);

static long arch_prctl_tt(int code, unsigned long addr)
{
	unsigned long tmp;
	long ret;

	switch(code){
	case ARCH_SET_GS:
	case ARCH_SET_FS:
		ret = arch_prctl(code, addr);
		break;
	case ARCH_GET_FS:
	case ARCH_GET_GS:
		ret = arch_prctl(code, (unsigned long) &tmp);
		if(!ret)
			ret = put_user(tmp, &addr);
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return(ret);
}
#endif

#ifdef CONFIG_MODE_SKAS

static long arch_prctl_skas(int code, unsigned long addr)
{
	long ret = 0;

	switch(code){
	case ARCH_SET_GS:
		current->thread.regs.regs.skas.regs[GS_BASE / sizeof(unsigned long)] = addr;
		break;
	case ARCH_SET_FS:
		current->thread.regs.regs.skas.regs[FS_BASE / sizeof(unsigned long)] = addr;
		break;
	case ARCH_GET_FS:
		ret = put_user(current->thread.regs.regs.skas.regs[GS / sizeof(unsigned long)], &addr);
	        break;
	case ARCH_GET_GS:
		ret = put_user(current->thread.regs.regs.skas.regs[FS / sizeof(unsigned \
long)], &addr);
	        break;
	default:
		ret = -EINVAL;
		break;
	}

	return(ret);
}
#endif

long sys_arch_prctl(int code, unsigned long addr)
{
	return(CHOOSE_MODE_PROC(arch_prctl_tt, arch_prctl_skas, code, addr));
}

long sys_clone(unsigned long clone_flags, unsigned long newsp,
	       void __user *parent_tid, void __user *child_tid)
{
	long ret;

	/* XXX: normal arch do here this pass, and also pass the regs to
	 * do_fork, instead of NULL. Currently the arch-independent code
	 * ignores these values, while the UML code (actually it's
	 * copy_thread) does the right thing. But this should change,
	 probably. */
	/*if (!newsp)
		newsp = UPT_SP(current->thread.regs);*/
	current->thread.forking = 1;
	ret = do_fork(clone_flags, newsp, NULL, 0, parent_tid, child_tid);
	current->thread.forking = 0;
	return(ret);
}

/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-file-style: "linux"
 * End:
 */