summaryrefslogtreecommitdiffstats
path: root/arch/um/sys-x86_64/syscalls.c
blob: ab4b0abf8af3fe528881a2f6a95429d6469d8f57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
/*
 * Copyright 2003 PathScale, Inc.
 *
 * Licensed under the GPL
 */

#include "linux/linkage.h"
#include "linux/slab.h"
#include "linux/shm.h"
#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"

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;
}

#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:
 */