1 | /* |
2 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
3 | * Licensed under the GPL |
4 | */ |
5 | |
6 | #include <linux/mm.h> |
7 | #include <linux/sched.h> |
8 | #include <linux/uaccess.h> |
9 | #include <asm/ptrace-abi.h> |
10 | #include <registers.h> |
11 | #include <skas.h> |
12 | |
13 | extern int arch_switch_tls(struct task_struct *to); |
14 | |
15 | void arch_switch_to(struct task_struct *to) |
16 | { |
17 | int err = arch_switch_tls(to); |
18 | if (!err) |
19 | return; |
20 | |
21 | if (err != -EINVAL) |
22 | printk(KERN_WARNING "arch_switch_tls failed, errno %d, " |
23 | "not EINVAL\n" , -err); |
24 | else |
25 | printk(KERN_WARNING "arch_switch_tls failed, errno = EINVAL\n" ); |
26 | } |
27 | |
28 | /* determines which flags the user has access to. */ |
29 | /* 1 = access 0 = no access */ |
30 | #define FLAG_MASK 0x00044dd5 |
31 | |
32 | static const int reg_offsets[] = { |
33 | [EBX] = HOST_BX, |
34 | [ECX] = HOST_CX, |
35 | [EDX] = HOST_DX, |
36 | [ESI] = HOST_SI, |
37 | [EDI] = HOST_DI, |
38 | [EBP] = HOST_BP, |
39 | [EAX] = HOST_AX, |
40 | [DS] = HOST_DS, |
41 | [ES] = HOST_ES, |
42 | [FS] = HOST_FS, |
43 | [GS] = HOST_GS, |
44 | [EIP] = HOST_IP, |
45 | [CS] = HOST_CS, |
46 | [EFL] = HOST_EFLAGS, |
47 | [UESP] = HOST_SP, |
48 | [SS] = HOST_SS, |
49 | [ORIG_EAX] = HOST_ORIG_AX, |
50 | }; |
51 | |
52 | int putreg(struct task_struct *child, int regno, unsigned long value) |
53 | { |
54 | regno >>= 2; |
55 | switch (regno) { |
56 | case EBX: |
57 | case ECX: |
58 | case EDX: |
59 | case ESI: |
60 | case EDI: |
61 | case EBP: |
62 | case EAX: |
63 | case EIP: |
64 | case UESP: |
65 | break; |
66 | case ORIG_EAX: |
67 | /* Update the syscall number. */ |
68 | UPT_SYSCALL_NR(&child->thread.regs.regs) = value; |
69 | break; |
70 | case FS: |
71 | if (value && (value & 3) != 3) |
72 | return -EIO; |
73 | break; |
74 | case GS: |
75 | if (value && (value & 3) != 3) |
76 | return -EIO; |
77 | break; |
78 | case DS: |
79 | case ES: |
80 | if (value && (value & 3) != 3) |
81 | return -EIO; |
82 | value &= 0xffff; |
83 | break; |
84 | case SS: |
85 | case CS: |
86 | if ((value & 3) != 3) |
87 | return -EIO; |
88 | value &= 0xffff; |
89 | break; |
90 | case EFL: |
91 | value &= FLAG_MASK; |
92 | child->thread.regs.regs.gp[HOST_EFLAGS] |= value; |
93 | return 0; |
94 | default : |
95 | panic(fmt: "Bad register in putreg() : %d\n" , regno); |
96 | } |
97 | child->thread.regs.regs.gp[reg_offsets[regno]] = value; |
98 | return 0; |
99 | } |
100 | |
101 | int poke_user(struct task_struct *child, long addr, long data) |
102 | { |
103 | if ((addr & 3) || addr < 0) |
104 | return -EIO; |
105 | |
106 | if (addr < MAX_REG_OFFSET) |
107 | return putreg(child, regno: addr, value: data); |
108 | else if ((addr >= offsetof(struct user, u_debugreg[0])) && |
109 | (addr <= offsetof(struct user, u_debugreg[7]))) { |
110 | addr -= offsetof(struct user, u_debugreg[0]); |
111 | addr = addr >> 2; |
112 | if ((addr == 4) || (addr == 5)) |
113 | return -EIO; |
114 | child->thread.arch.debugregs[addr] = data; |
115 | return 0; |
116 | } |
117 | return -EIO; |
118 | } |
119 | |
120 | unsigned long getreg(struct task_struct *child, int regno) |
121 | { |
122 | unsigned long mask = ~0UL; |
123 | |
124 | regno >>= 2; |
125 | switch (regno) { |
126 | case FS: |
127 | case GS: |
128 | case DS: |
129 | case ES: |
130 | case SS: |
131 | case CS: |
132 | mask = 0xffff; |
133 | break; |
134 | case EIP: |
135 | case UESP: |
136 | case EAX: |
137 | case EBX: |
138 | case ECX: |
139 | case EDX: |
140 | case ESI: |
141 | case EDI: |
142 | case EBP: |
143 | case EFL: |
144 | case ORIG_EAX: |
145 | break; |
146 | default: |
147 | panic(fmt: "Bad register in getreg() : %d\n" , regno); |
148 | } |
149 | return mask & child->thread.regs.regs.gp[reg_offsets[regno]]; |
150 | } |
151 | |
152 | /* read the word at location addr in the USER area. */ |
153 | int peek_user(struct task_struct *child, long addr, long data) |
154 | { |
155 | unsigned long tmp; |
156 | |
157 | if ((addr & 3) || addr < 0) |
158 | return -EIO; |
159 | |
160 | tmp = 0; /* Default return condition */ |
161 | if (addr < MAX_REG_OFFSET) { |
162 | tmp = getreg(child, regno: addr); |
163 | } |
164 | else if ((addr >= offsetof(struct user, u_debugreg[0])) && |
165 | (addr <= offsetof(struct user, u_debugreg[7]))) { |
166 | addr -= offsetof(struct user, u_debugreg[0]); |
167 | addr = addr >> 2; |
168 | tmp = child->thread.arch.debugregs[addr]; |
169 | } |
170 | return put_user(tmp, (unsigned long __user *) data); |
171 | } |
172 | |
173 | static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) |
174 | { |
175 | int err, n, cpu = task_cpu(p: child); |
176 | struct user_i387_struct fpregs; |
177 | |
178 | err = save_i387_registers(userspace_pid[cpu], |
179 | (unsigned long *) &fpregs); |
180 | if (err) |
181 | return err; |
182 | |
183 | n = copy_to_user(to: buf, from: &fpregs, n: sizeof(fpregs)); |
184 | if(n > 0) |
185 | return -EFAULT; |
186 | |
187 | return n; |
188 | } |
189 | |
190 | static int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) |
191 | { |
192 | int n, cpu = task_cpu(p: child); |
193 | struct user_i387_struct fpregs; |
194 | |
195 | n = copy_from_user(to: &fpregs, from: buf, n: sizeof(fpregs)); |
196 | if (n > 0) |
197 | return -EFAULT; |
198 | |
199 | return restore_i387_registers(userspace_pid[cpu], |
200 | (unsigned long *) &fpregs); |
201 | } |
202 | |
203 | static int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child) |
204 | { |
205 | int err, n, cpu = task_cpu(p: child); |
206 | struct user_fxsr_struct fpregs; |
207 | |
208 | err = save_fpx_registers(userspace_pid[cpu], (unsigned long *) &fpregs); |
209 | if (err) |
210 | return err; |
211 | |
212 | n = copy_to_user(to: buf, from: &fpregs, n: sizeof(fpregs)); |
213 | if(n > 0) |
214 | return -EFAULT; |
215 | |
216 | return n; |
217 | } |
218 | |
219 | static int set_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child) |
220 | { |
221 | int n, cpu = task_cpu(p: child); |
222 | struct user_fxsr_struct fpregs; |
223 | |
224 | n = copy_from_user(to: &fpregs, from: buf, n: sizeof(fpregs)); |
225 | if (n > 0) |
226 | return -EFAULT; |
227 | |
228 | return restore_fpx_registers(userspace_pid[cpu], |
229 | (unsigned long *) &fpregs); |
230 | } |
231 | |
232 | long subarch_ptrace(struct task_struct *child, long request, |
233 | unsigned long addr, unsigned long data) |
234 | { |
235 | int ret = -EIO; |
236 | void __user *datap = (void __user *) data; |
237 | switch (request) { |
238 | case PTRACE_GETFPREGS: /* Get the child FPU state. */ |
239 | ret = get_fpregs(buf: datap, child); |
240 | break; |
241 | case PTRACE_SETFPREGS: /* Set the child FPU state. */ |
242 | ret = set_fpregs(buf: datap, child); |
243 | break; |
244 | case PTRACE_GETFPXREGS: /* Get the child FPU state. */ |
245 | ret = get_fpxregs(buf: datap, child); |
246 | break; |
247 | case PTRACE_SETFPXREGS: /* Set the child FPU state. */ |
248 | ret = set_fpxregs(buf: datap, child); |
249 | break; |
250 | default: |
251 | ret = -EIO; |
252 | } |
253 | return ret; |
254 | } |
255 | |