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