Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | |
3 | * Licensed under the GPL | |
4 | */ | |
5 | ||
1da177e4 LT |
6 | #include <linux/compiler.h> |
7 | #include "linux/sched.h" | |
81efcd33 | 8 | #include "linux/mm.h" |
1da177e4 LT |
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 | ||
972410b0 | 17 | void arch_switch_to_skas(struct task_struct *from, struct task_struct *to) |
1da177e4 | 18 | { |
54d8d3b5 PBG |
19 | int err = arch_switch_tls_skas(from, to); |
20 | if (!err) | |
21 | return; | |
22 | ||
23 | if (err != -EINVAL) | |
24 | printk(KERN_WARNING "arch_switch_tls_skas failed, errno %d, not EINVAL\n", -err); | |
25 | else | |
26 | printk(KERN_WARNING "arch_switch_tls_skas failed, errno = EINVAL\n"); | |
1da177e4 LT |
27 | } |
28 | ||
29 | int is_syscall(unsigned long addr) | |
30 | { | |
31 | unsigned short instr; | |
32 | int n; | |
33 | ||
34 | n = copy_from_user(&instr, (void __user *) addr, sizeof(instr)); | |
35 | if(n){ | |
81efcd33 BS |
36 | /* access_process_vm() grants access to vsyscall and stub, |
37 | * while copy_from_user doesn't. Maybe access_process_vm is | |
38 | * slow, but that doesn't matter, since it will be called only | |
39 | * in case of singlestepping, if copy_from_user failed. | |
40 | */ | |
41 | n = access_process_vm(current, addr, &instr, sizeof(instr), 0); | |
42 | if(n != sizeof(instr)) { | |
43 | printk("is_syscall : failed to read instruction from " | |
44 | "0x%lx\n", addr); | |
45 | return(1); | |
46 | } | |
1da177e4 LT |
47 | } |
48 | /* int 0x80 or sysenter */ | |
49 | return((instr == 0x80cd) || (instr == 0x340f)); | |
50 | } | |
51 | ||
52 | /* determines which flags the user has access to. */ | |
53 | /* 1 = access 0 = no access */ | |
54 | #define FLAG_MASK 0x00044dd5 | |
55 | ||
56 | int putreg(struct task_struct *child, int regno, unsigned long value) | |
57 | { | |
58 | regno >>= 2; | |
59 | switch (regno) { | |
60 | case FS: | |
61 | if (value && (value & 3) != 3) | |
62 | return -EIO; | |
63 | PT_REGS_FS(&child->thread.regs) = value; | |
64 | return 0; | |
65 | case GS: | |
66 | if (value && (value & 3) != 3) | |
67 | return -EIO; | |
68 | PT_REGS_GS(&child->thread.regs) = value; | |
69 | return 0; | |
70 | case DS: | |
71 | case ES: | |
72 | if (value && (value & 3) != 3) | |
73 | return -EIO; | |
74 | value &= 0xffff; | |
75 | break; | |
76 | case SS: | |
77 | case CS: | |
78 | if ((value & 3) != 3) | |
79 | return -EIO; | |
80 | value &= 0xffff; | |
81 | break; | |
82 | case EFL: | |
83 | value &= FLAG_MASK; | |
84 | value |= PT_REGS_EFLAGS(&child->thread.regs); | |
85 | break; | |
86 | } | |
87 | PT_REGS_SET(&child->thread.regs, regno, value); | |
88 | return 0; | |
89 | } | |
90 | ||
82c1c11b BS |
91 | int poke_user(struct task_struct *child, long addr, long data) |
92 | { | |
93 | if ((addr & 3) || addr < 0) | |
94 | return -EIO; | |
95 | ||
96 | if (addr < MAX_REG_OFFSET) | |
97 | return putreg(child, addr, data); | |
98 | ||
99 | else if((addr >= offsetof(struct user, u_debugreg[0])) && | |
100 | (addr <= offsetof(struct user, u_debugreg[7]))){ | |
101 | addr -= offsetof(struct user, u_debugreg[0]); | |
102 | addr = addr >> 2; | |
103 | if((addr == 4) || (addr == 5)) return -EIO; | |
104 | child->thread.arch.debugregs[addr] = data; | |
105 | return 0; | |
106 | } | |
107 | return -EIO; | |
108 | } | |
109 | ||
1da177e4 LT |
110 | unsigned long getreg(struct task_struct *child, int regno) |
111 | { | |
112 | unsigned long retval = ~0UL; | |
113 | ||
114 | regno >>= 2; | |
115 | switch (regno) { | |
116 | case FS: | |
117 | case GS: | |
118 | case DS: | |
119 | case ES: | |
120 | case SS: | |
121 | case CS: | |
122 | retval = 0xffff; | |
123 | /* fall through */ | |
124 | default: | |
125 | retval &= PT_REG(&child->thread.regs, regno); | |
126 | } | |
127 | return retval; | |
128 | } | |
129 | ||
82c1c11b BS |
130 | int peek_user(struct task_struct *child, long addr, long data) |
131 | { | |
132 | /* read the word at location addr in the USER area. */ | |
4d338e1a | 133 | unsigned long tmp; |
82c1c11b | 134 | |
4d338e1a AV |
135 | if ((addr & 3) || addr < 0) |
136 | return -EIO; | |
82c1c11b | 137 | |
4d338e1a AV |
138 | tmp = 0; /* Default return condition */ |
139 | if(addr < MAX_REG_OFFSET){ | |
140 | tmp = getreg(child, addr); | |
141 | } | |
142 | else if((addr >= offsetof(struct user, u_debugreg[0])) && | |
143 | (addr <= offsetof(struct user, u_debugreg[7]))){ | |
144 | addr -= offsetof(struct user, u_debugreg[0]); | |
145 | addr = addr >> 2; | |
146 | tmp = child->thread.arch.debugregs[addr]; | |
147 | } | |
148 | return put_user(tmp, (unsigned long __user *) data); | |
82c1c11b BS |
149 | } |
150 | ||
1da177e4 LT |
151 | struct i387_fxsave_struct { |
152 | unsigned short cwd; | |
153 | unsigned short swd; | |
154 | unsigned short twd; | |
155 | unsigned short fop; | |
156 | long fip; | |
157 | long fcs; | |
158 | long foo; | |
159 | long fos; | |
160 | long mxcsr; | |
161 | long reserved; | |
162 | long st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ | |
163 | long xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */ | |
164 | long padding[56]; | |
165 | }; | |
166 | ||
167 | /* | |
168 | * FPU tag word conversions. | |
169 | */ | |
170 | ||
171 | static inline unsigned short twd_i387_to_fxsr( unsigned short twd ) | |
172 | { | |
173 | unsigned int tmp; /* to avoid 16 bit prefixes in the code */ | |
174 | ||
175 | /* Transform each pair of bits into 01 (valid) or 00 (empty) */ | |
176 | tmp = ~twd; | |
177 | tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ | |
178 | /* and move the valid bits to the lower byte. */ | |
179 | tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ | |
180 | tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ | |
181 | tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ | |
182 | return tmp; | |
183 | } | |
184 | ||
185 | static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave ) | |
186 | { | |
187 | struct _fpxreg *st = NULL; | |
188 | unsigned long twd = (unsigned long) fxsave->twd; | |
189 | unsigned long tag; | |
190 | unsigned long ret = 0xffff0000; | |
191 | int i; | |
192 | ||
193 | #define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16); | |
194 | ||
195 | for ( i = 0 ; i < 8 ; i++ ) { | |
196 | if ( twd & 0x1 ) { | |
197 | st = (struct _fpxreg *) FPREG_ADDR( fxsave, i ); | |
198 | ||
199 | switch ( st->exponent & 0x7fff ) { | |
200 | case 0x7fff: | |
201 | tag = 2; /* Special */ | |
202 | break; | |
203 | case 0x0000: | |
204 | if ( !st->significand[0] && | |
205 | !st->significand[1] && | |
206 | !st->significand[2] && | |
207 | !st->significand[3] ) { | |
208 | tag = 1; /* Zero */ | |
209 | } else { | |
210 | tag = 2; /* Special */ | |
211 | } | |
212 | break; | |
213 | default: | |
214 | if ( st->significand[3] & 0x8000 ) { | |
215 | tag = 0; /* Valid */ | |
216 | } else { | |
217 | tag = 2; /* Special */ | |
218 | } | |
219 | break; | |
220 | } | |
221 | } else { | |
222 | tag = 3; /* Empty */ | |
223 | } | |
224 | ret |= (tag << (2 * i)); | |
225 | twd = twd >> 1; | |
226 | } | |
227 | return ret; | |
228 | } | |
229 | ||
1da177e4 LT |
230 | static inline int convert_fxsr_to_user(struct _fpstate __user *buf, |
231 | struct pt_regs *regs) | |
232 | { | |
233 | return(CHOOSE_MODE(convert_fxsr_to_user_tt(buf, regs), 0)); | |
234 | } | |
235 | ||
1da177e4 LT |
236 | static inline int convert_fxsr_from_user(struct pt_regs *regs, |
237 | struct _fpstate __user *buf) | |
238 | { | |
239 | return(CHOOSE_MODE(convert_fxsr_from_user_tt(regs, buf), 0)); | |
240 | } | |
241 | ||
242 | int get_fpregs(unsigned long buf, struct task_struct *child) | |
243 | { | |
244 | int err; | |
245 | ||
246 | err = convert_fxsr_to_user((struct _fpstate __user *) buf, | |
247 | &child->thread.regs); | |
248 | if(err) return(-EFAULT); | |
249 | else return(0); | |
250 | } | |
251 | ||
252 | int set_fpregs(unsigned long buf, struct task_struct *child) | |
253 | { | |
254 | int err; | |
255 | ||
256 | err = convert_fxsr_from_user(&child->thread.regs, | |
257 | (struct _fpstate __user *) buf); | |
258 | if(err) return(-EFAULT); | |
259 | else return(0); | |
260 | } | |
261 | ||
1da177e4 LT |
262 | int get_fpxregs(unsigned long buf, struct task_struct *tsk) |
263 | { | |
264 | return(CHOOSE_MODE(get_fpxregs_tt(buf, tsk), 0)); | |
265 | } | |
266 | ||
1da177e4 LT |
267 | int set_fpxregs(unsigned long buf, struct task_struct *tsk) |
268 | { | |
269 | return(CHOOSE_MODE(set_fpxregs_tt(buf, tsk), 0)); | |
270 | } | |
271 | ||
272 | #ifdef notdef | |
273 | int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) | |
274 | { | |
275 | fpu->cwd = (((SC_FP_CW(PT_REGS_SC(regs)) & 0xffff) << 16) | | |
276 | (SC_FP_SW(PT_REGS_SC(regs)) & 0xffff)); | |
277 | fpu->swd = SC_FP_CSSEL(PT_REGS_SC(regs)) & 0xffff; | |
278 | fpu->twd = SC_FP_IPOFF(PT_REGS_SC(regs)); | |
279 | fpu->fip = SC_FP_CSSEL(PT_REGS_SC(regs)) & 0xffff; | |
280 | fpu->fcs = SC_FP_DATAOFF(PT_REGS_SC(regs)); | |
281 | fpu->foo = SC_FP_DATASEL(PT_REGS_SC(regs)); | |
282 | fpu->fos = 0; | |
283 | memcpy(fpu->st_space, (void *) SC_FP_ST(PT_REGS_SC(regs)), | |
284 | sizeof(fpu->st_space)); | |
285 | return(1); | |
286 | } | |
287 | #endif | |
288 | ||
1da177e4 LT |
289 | static inline void copy_fpu_fxsave(struct pt_regs *regs, |
290 | struct user_i387_struct *buf) | |
291 | { | |
292 | (void) CHOOSE_MODE(copy_fpu_fxsave_tt(regs, buf), 0); | |
293 | } | |
294 | ||
295 | int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu ) | |
296 | { | |
297 | copy_fpu_fxsave(regs, (struct user_i387_struct *) fpu); | |
298 | return(1); | |
299 | } | |
300 | ||
301 | /* | |
302 | * Overrides for Emacs so that we follow Linus's tabbing style. | |
303 | * Emacs will notice this stuff at the end of the file and automatically | |
304 | * adjust the settings for this buffer only. This must remain at the end | |
305 | * of the file. | |
306 | * --------------------------------------------------------------------------- | |
307 | * Local variables: | |
308 | * c-file-style: "linux" | |
309 | * End: | |
310 | */ |