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