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
13extern int arch_switch_tls(struct task_struct *to);
14
15void 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
32static 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
52int 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("Bad register in putreg() : %d\n", regno);
96	}
97	child->thread.regs.regs.gp[reg_offsets[regno]] = value;
98	return 0;
99}
100
101int 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, addr, 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
120unsigned 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("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. */
153int 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, 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
173static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child)
174{
175	int err, n, cpu = task_cpu(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(buf, &fpregs, sizeof(fpregs));
184	if(n > 0)
185		return -EFAULT;
186
187	return n;
188}
189
190static int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child)
191{
192	int n, cpu = task_cpu(child);
193	struct user_i387_struct fpregs;
194
195	n = copy_from_user(&fpregs, buf, sizeof(fpregs));
196	if (n > 0)
197		return -EFAULT;
198
199	return restore_i387_registers(userspace_pid[cpu],
200				    (unsigned long *) &fpregs);
201}
202
203static int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child)
204{
205	int err, n, cpu = task_cpu(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(buf, &fpregs, sizeof(fpregs));
213	if(n > 0)
214		return -EFAULT;
215
216	return n;
217}
218
219static int set_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child)
220{
221	int n, cpu = task_cpu(child);
222	struct user_fxsr_struct fpregs;
223
224	n = copy_from_user(&fpregs, buf, sizeof(fpregs));
225	if (n > 0)
226		return -EFAULT;
227
228	return restore_fpx_registers(userspace_pid[cpu],
229				     (unsigned long *) &fpregs);
230}
231
232long 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(datap, child);
240		break;
241	case PTRACE_SETFPREGS: /* Set the child FPU state. */
242		ret = set_fpregs(datap, child);
243		break;
244	case PTRACE_GETFPXREGS: /* Get the child FPU state. */
245		ret = get_fpxregs(datap, child);
246		break;
247	case PTRACE_SETFPXREGS: /* Set the child FPU state. */
248		ret = set_fpxregs(datap, child);
249		break;
250	default:
251		ret = -EIO;
252	}
253	return ret;
254}
255