1/*
2 * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL
4 */
5
6#include <linux/compiler.h>
7#include "linux/sched.h"
8#include "linux/mm.h"
9#include "asm/elf.h"
10#include "asm/ptrace.h"
11#include "asm/uaccess.h"
12#include "asm/unistd.h"
13#include "sysdep/ptrace.h"
14#include "sysdep/sigcontext.h"
15#include "sysdep/sc.h"
16
17void arch_switch_to_tt(struct task_struct *from, struct task_struct *to)
18{
19	update_debugregs(to->thread.arch.debugregs_seq);
20	arch_switch_tls_tt(from, to);
21}
22
23void arch_switch_to_skas(struct task_struct *from, struct task_struct *to)
24{
25	int err = arch_switch_tls_skas(from, to);
26	if (!err)
27		return;
28
29	if (err != -EINVAL)
30		printk(KERN_WARNING "arch_switch_tls_skas failed, errno %d, not EINVAL\n", -err);
31	else
32		printk(KERN_WARNING "arch_switch_tls_skas failed, errno = EINVAL\n");
33}
34
35int is_syscall(unsigned long addr)
36{
37	unsigned short instr;
38	int n;
39
40	n = copy_from_user(&instr, (void __user *) addr, sizeof(instr));
41	if(n){
42		/* access_process_vm() grants access to vsyscall and stub,
43		 * while copy_from_user doesn't. Maybe access_process_vm is
44		 * slow, but that doesn't matter, since it will be called only
45		 * in case of singlestepping, if copy_from_user failed.
46		 */
47		n = access_process_vm(current, addr, &instr, sizeof(instr), 0);
48		if(n != sizeof(instr)) {
49			printk("is_syscall : failed to read instruction from "
50			       "0x%lx\n", addr);
51			return(1);
52		}
53	}
54	/* int 0x80 or sysenter */
55	return((instr == 0x80cd) || (instr == 0x340f));
56}
57
58/* determines which flags the user has access to. */
59/* 1 = access 0 = no access */
60#define FLAG_MASK 0x00044dd5
61
62int putreg(struct task_struct *child, int regno, unsigned long value)
63{
64	regno >>= 2;
65	switch (regno) {
66	case FS:
67		if (value && (value & 3) != 3)
68			return -EIO;
69		PT_REGS_FS(&child->thread.regs) = value;
70		return 0;
71	case GS:
72		if (value && (value & 3) != 3)
73			return -EIO;
74		PT_REGS_GS(&child->thread.regs) = value;
75		return 0;
76	case DS:
77	case ES:
78		if (value && (value & 3) != 3)
79			return -EIO;
80		value &= 0xffff;
81		break;
82	case SS:
83	case CS:
84		if ((value & 3) != 3)
85			return -EIO;
86		value &= 0xffff;
87		break;
88	case EFL:
89		value &= FLAG_MASK;
90		value |= PT_REGS_EFLAGS(&child->thread.regs);
91		break;
92	}
93	PT_REGS_SET(&child->thread.regs, regno, value);
94	return 0;
95}
96
97int poke_user(struct task_struct *child, long addr, long data)
98{
99        if ((addr & 3) || addr < 0)
100                return -EIO;
101
102        if (addr < MAX_REG_OFFSET)
103                return putreg(child, addr, data);
104
105        else if((addr >= offsetof(struct user, u_debugreg[0])) &&
106                (addr <= offsetof(struct user, u_debugreg[7]))){
107                addr -= offsetof(struct user, u_debugreg[0]);
108                addr = addr >> 2;
109                if((addr == 4) || (addr == 5)) return -EIO;
110                child->thread.arch.debugregs[addr] = data;
111                return 0;
112        }
113        return -EIO;
114}
115
116unsigned long getreg(struct task_struct *child, int regno)
117{
118	unsigned long retval = ~0UL;
119
120	regno >>= 2;
121	switch (regno) {
122	case FS:
123	case GS:
124	case DS:
125	case ES:
126	case SS:
127	case CS:
128		retval = 0xffff;
129		/* fall through */
130	default:
131		retval &= PT_REG(&child->thread.regs, regno);
132	}
133	return retval;
134}
135
136int peek_user(struct task_struct *child, long addr, long data)
137{
138/* read the word at location addr in the USER area. */
139	unsigned long tmp;
140
141	if ((addr & 3) || addr < 0)
142		return -EIO;
143
144	tmp = 0;  /* Default return condition */
145	if(addr < MAX_REG_OFFSET){
146		tmp = getreg(child, addr);
147	}
148	else if((addr >= offsetof(struct user, u_debugreg[0])) &&
149		(addr <= offsetof(struct user, u_debugreg[7]))){
150		addr -= offsetof(struct user, u_debugreg[0]);
151		addr = addr >> 2;
152		tmp = child->thread.arch.debugregs[addr];
153	}
154	return put_user(tmp, (unsigned long __user *) data);
155}
156
157struct i387_fxsave_struct {
158	unsigned short	cwd;
159	unsigned short	swd;
160	unsigned short	twd;
161	unsigned short	fop;
162	long	fip;
163	long	fcs;
164	long	foo;
165	long	fos;
166	long	mxcsr;
167	long	reserved;
168	long	st_space[32];	/* 8*16 bytes for each FP-reg = 128 bytes */
169	long	xmm_space[32];	/* 8*16 bytes for each XMM-reg = 128 bytes */
170	long	padding[56];
171};
172
173/*
174 * FPU tag word conversions.
175 */
176
177static inline unsigned short twd_i387_to_fxsr( unsigned short twd )
178{
179	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
180
181	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
182        tmp = ~twd;
183        tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
184        /* and move the valid bits to the lower byte. */
185        tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
186        tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
187        tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
188        return tmp;
189}
190
191static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave )
192{
193	struct _fpxreg *st = NULL;
194	unsigned long twd = (unsigned long) fxsave->twd;
195	unsigned long tag;
196	unsigned long ret = 0xffff0000;
197	int i;
198
199#define FPREG_ADDR(f, n)	((char *)&(f)->st_space + (n) * 16);
200
201	for ( i = 0 ; i < 8 ; i++ ) {
202		if ( twd & 0x1 ) {
203			st = (struct _fpxreg *) FPREG_ADDR( fxsave, i );
204
205			switch ( st->exponent & 0x7fff ) {
206			case 0x7fff:
207				tag = 2;		/* Special */
208				break;
209			case 0x0000:
210				if ( !st->significand[0] &&
211				     !st->significand[1] &&
212				     !st->significand[2] &&
213				     !st->significand[3] ) {
214					tag = 1;	/* Zero */
215				} else {
216					tag = 2;	/* Special */
217				}
218				break;
219			default:
220				if ( st->significand[3] & 0x8000 ) {
221					tag = 0;	/* Valid */
222				} else {
223					tag = 2;	/* Special */
224				}
225				break;
226			}
227		} else {
228			tag = 3;			/* Empty */
229		}
230		ret |= (tag << (2 * i));
231		twd = twd >> 1;
232	}
233	return ret;
234}
235
236/*
237 * FXSR floating point environment conversions.
238 */
239
240#ifdef CONFIG_MODE_TT
241static inline int convert_fxsr_to_user_tt(struct _fpstate __user *buf,
242					  struct pt_regs *regs)
243{
244	struct i387_fxsave_struct *fxsave = SC_FXSR_ENV(PT_REGS_SC(regs));
245	unsigned long env[7];
246	struct _fpreg __user *to;
247	struct _fpxreg *from;
248	int i;
249
250	env[0] = (unsigned long)fxsave->cwd | 0xffff0000;
251	env[1] = (unsigned long)fxsave->swd | 0xffff0000;
252	env[2] = twd_fxsr_to_i387(fxsave);
253	env[3] = fxsave->fip;
254	env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
255	env[5] = fxsave->foo;
256	env[6] = fxsave->fos;
257
258	if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) )
259		return 1;
260
261	to = &buf->_st[0];
262	from = (struct _fpxreg *) &fxsave->st_space[0];
263	for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
264		if ( __copy_to_user( to, from, sizeof(*to) ) )
265			return 1;
266	}
267	return 0;
268}
269#endif
270
271static inline int convert_fxsr_to_user(struct _fpstate __user *buf,
272				       struct pt_regs *regs)
273{
274	return(CHOOSE_MODE(convert_fxsr_to_user_tt(buf, regs), 0));
275}
276
277#ifdef CONFIG_MODE_TT
278static inline int convert_fxsr_from_user_tt(struct pt_regs *regs,
279					    struct _fpstate __user *buf)
280{
281	struct i387_fxsave_struct *fxsave = SC_FXSR_ENV(PT_REGS_SC(regs));
282	unsigned long env[7];
283	struct _fpxreg *to;
284	struct _fpreg __user *from;
285	int i;
286
287	if ( __copy_from_user( env, buf, 7 * sizeof(long) ) )
288		return 1;
289
290	fxsave->cwd = (unsigned short)(env[0] & 0xffff);
291	fxsave->swd = (unsigned short)(env[1] & 0xffff);
292	fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
293	fxsave->fip = env[3];
294	fxsave->fop = (unsigned short)((env[4] & 0xffff0000) >> 16);
295	fxsave->fcs = (env[4] & 0xffff);
296	fxsave->foo = env[5];
297	fxsave->fos = env[6];
298
299	to = (struct _fpxreg *) &fxsave->st_space[0];
300	from = &buf->_st[0];
301	for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
302		if ( __copy_from_user( to, from, sizeof(*from) ) )
303			return 1;
304	}
305	return 0;
306}
307#endif
308
309static inline int convert_fxsr_from_user(struct pt_regs *regs,
310					 struct _fpstate __user *buf)
311{
312	return(CHOOSE_MODE(convert_fxsr_from_user_tt(regs, buf), 0));
313}
314
315int get_fpregs(unsigned long buf, struct task_struct *child)
316{
317	int err;
318
319	err = convert_fxsr_to_user((struct _fpstate __user *) buf,
320				   &child->thread.regs);
321	if(err) return(-EFAULT);
322	else return(0);
323}
324
325int set_fpregs(unsigned long buf, struct task_struct *child)
326{
327	int err;
328
329	err = convert_fxsr_from_user(&child->thread.regs,
330				     (struct _fpstate __user *) buf);
331	if(err) return(-EFAULT);
332	else return(0);
333}
334
335#ifdef CONFIG_MODE_TT
336int get_fpxregs_tt(unsigned long buf, struct task_struct *tsk)
337{
338	struct pt_regs *regs = &tsk->thread.regs;
339	struct i387_fxsave_struct *fxsave = SC_FXSR_ENV(PT_REGS_SC(regs));
340	int err;
341
342	err = __copy_to_user((void __user *) buf, fxsave,
343			     sizeof(struct user_fxsr_struct));
344	if(err) return -EFAULT;
345	else return 0;
346}
347#endif
348
349int get_fpxregs(unsigned long buf, struct task_struct *tsk)
350{
351	return(CHOOSE_MODE(get_fpxregs_tt(buf, tsk), 0));
352}
353
354#ifdef CONFIG_MODE_TT
355int set_fpxregs_tt(unsigned long buf, struct task_struct *tsk)
356{
357	struct pt_regs *regs = &tsk->thread.regs;
358	struct i387_fxsave_struct *fxsave = SC_FXSR_ENV(PT_REGS_SC(regs));
359	int err;
360
361	err = __copy_from_user(fxsave, (void __user *) buf,
362			       sizeof(struct user_fxsr_struct) );
363	if(err) return -EFAULT;
364	else return 0;
365}
366#endif
367
368int set_fpxregs(unsigned long buf, struct task_struct *tsk)
369{
370	return(CHOOSE_MODE(set_fpxregs_tt(buf, tsk), 0));
371}
372
373#ifdef notdef
374int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
375{
376	fpu->cwd = (((SC_FP_CW(PT_REGS_SC(regs)) & 0xffff) << 16) |
377		    (SC_FP_SW(PT_REGS_SC(regs)) & 0xffff));
378	fpu->swd = SC_FP_CSSEL(PT_REGS_SC(regs)) & 0xffff;
379	fpu->twd = SC_FP_IPOFF(PT_REGS_SC(regs));
380	fpu->fip = SC_FP_CSSEL(PT_REGS_SC(regs)) & 0xffff;
381	fpu->fcs = SC_FP_DATAOFF(PT_REGS_SC(regs));
382	fpu->foo = SC_FP_DATASEL(PT_REGS_SC(regs));
383	fpu->fos = 0;
384	memcpy(fpu->st_space, (void *) SC_FP_ST(PT_REGS_SC(regs)),
385	       sizeof(fpu->st_space));
386	return(1);
387}
388#endif
389
390#ifdef CONFIG_MODE_TT
391static inline void copy_fpu_fxsave_tt(struct pt_regs *regs,
392				      struct user_i387_struct *buf)
393{
394	struct i387_fxsave_struct *fpu = SC_FXSR_ENV(PT_REGS_SC(regs));
395	unsigned short *to;
396	unsigned short *from;
397	int i;
398
399	memcpy( buf, fpu, 7 * sizeof(long) );
400
401	to = (unsigned short *) &buf->st_space[0];
402	from = (unsigned short *) &fpu->st_space[0];
403	for ( i = 0 ; i < 8 ; i++, to += 5, from += 8 ) {
404		memcpy( to, from, 5 * sizeof(unsigned short) );
405	}
406}
407#endif
408
409static inline void copy_fpu_fxsave(struct pt_regs *regs,
410				   struct user_i387_struct *buf)
411{
412	(void) CHOOSE_MODE(copy_fpu_fxsave_tt(regs, buf), 0);
413}
414
415int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu )
416{
417	copy_fpu_fxsave(regs, (struct user_i387_struct *) fpu);
418	return(1);
419}
420
421/*
422 * Overrides for Emacs so that we follow Linus's tabbing style.
423 * Emacs will notice this stuff at the end of the file and automatically
424 * adjust the settings for this buffer only.  This must remain at the end
425 * of the file.
426 * ---------------------------------------------------------------------------
427 * Local variables:
428 * c-file-style: "linux"
429 * End:
430 */
431