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