xref: /linux/arch/x86/um/ptrace.c (revision 2dca89df0d1116f722b4be100e4bfcff858058e8)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <linux/sched.h>
4 #include <linux/elf.h>
5 #include <linux/regset.h>
6 #include <asm/user32.h>
7 #include <asm/sigcontext.h>
8 
9 #ifdef CONFIG_X86_32
10 /*
11  * FPU tag word conversions.
12  */
13 
twd_i387_to_fxsr(unsigned short twd)14 static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
15 {
16 	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
17 
18 	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
19 	tmp = ~twd;
20 	tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
21 	/* and move the valid bits to the lower byte. */
22 	tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
23 	tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
24 	tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
25 	return tmp;
26 }
27 
28 static inline unsigned long
twd_fxsr_to_i387(const struct user_fxsr_struct * fxsave)29 twd_fxsr_to_i387(const struct user_fxsr_struct *fxsave)
30 {
31 	struct _fpxreg *st = NULL;
32 	unsigned long twd = (unsigned long) fxsave->twd;
33 	unsigned long tag;
34 	unsigned long ret = 0xffff0000;
35 	int i;
36 
37 #define FPREG_ADDR(f, n)	((char *)&(f)->st_space + (n) * 16)
38 
39 	for (i = 0; i < 8; i++) {
40 		if (twd & 0x1) {
41 			st = (struct _fpxreg *) FPREG_ADDR(fxsave, i);
42 
43 			switch (st->exponent & 0x7fff) {
44 			case 0x7fff:
45 				tag = 2;		/* Special */
46 				break;
47 			case 0x0000:
48 				if (!st->significand[0] &&
49 				    !st->significand[1] &&
50 				    !st->significand[2] &&
51 				    !st->significand[3]) {
52 					tag = 1;	/* Zero */
53 				} else {
54 					tag = 2;	/* Special */
55 				}
56 				break;
57 			default:
58 				if (st->significand[3] & 0x8000)
59 					tag = 0;	/* Valid */
60 				else
61 					tag = 2;	/* Special */
62 				break;
63 			}
64 		} else {
65 			tag = 3;			/* Empty */
66 		}
67 		ret |= (tag << (2 * i));
68 		twd = twd >> 1;
69 	}
70 	return ret;
71 }
72 
73 /*
74  * Get/set the old 32bit i387 registers (pre-FPX)
75  *
76  * We provide simple wrappers for mcontext.c, they are only defined locally
77  * because mcontext.c is userspace facing and needs to a different definition
78  * of the structures.
79  */
_um_i387_from_fxsr(struct membuf to,const struct user_fxsr_struct * fxsave)80 static int _um_i387_from_fxsr(struct membuf to,
81 			      const struct user_fxsr_struct *fxsave)
82 {
83 	int i;
84 
85 	membuf_store(&to, (unsigned long)fxsave->cwd | 0xffff0000ul);
86 	membuf_store(&to, (unsigned long)fxsave->swd | 0xffff0000ul);
87 	membuf_store(&to, twd_fxsr_to_i387(fxsave));
88 	membuf_store(&to, fxsave->fip);
89 	membuf_store(&to, fxsave->fcs | ((unsigned long)fxsave->fop << 16));
90 	membuf_store(&to, fxsave->foo);
91 	membuf_store(&to, fxsave->fos);
92 
93 	for (i = 0; i < 8; i++)
94 		membuf_write(&to, (void *)fxsave->st_space + i * 16, 10);
95 
96 	return 0;
97 }
98 
99 int um_i387_from_fxsr(struct user_i387_struct *i387,
100 		      const struct user_fxsr_struct *fxsave);
101 
um_i387_from_fxsr(struct user_i387_struct * i387,const struct user_fxsr_struct * fxsave)102 int um_i387_from_fxsr(struct user_i387_struct *i387,
103 		      const struct user_fxsr_struct *fxsave)
104 {
105 	struct membuf to = {
106 		.p = i387,
107 		.left = sizeof(*i387),
108 	};
109 
110 	return _um_i387_from_fxsr(to, fxsave);
111 }
112 
fpregs_legacy_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)113 static int fpregs_legacy_get(struct task_struct *target,
114 			     const struct user_regset *regset,
115 			     struct membuf to)
116 {
117 	struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
118 
119 	return _um_i387_from_fxsr(to, fxsave);
120 }
121 
122 int um_fxsr_from_i387(struct user_fxsr_struct *fxsave,
123 		      const struct user_i387_struct *from);
124 
um_fxsr_from_i387(struct user_fxsr_struct * fxsave,const struct user_i387_struct * from)125 int um_fxsr_from_i387(struct user_fxsr_struct *fxsave,
126 		      const struct user_i387_struct *from)
127 {
128 	int i;
129 
130 	fxsave->cwd = (unsigned short)(from->cwd & 0xffff);
131 	fxsave->swd = (unsigned short)(from->swd & 0xffff);
132 	fxsave->twd = twd_i387_to_fxsr((unsigned short)(from->twd & 0xffff));
133 	fxsave->fip = from->fip;
134 	fxsave->fop = (unsigned short)((from->fcs & 0xffff0000ul) >> 16);
135 	fxsave->fcs = (from->fcs & 0xffff);
136 	fxsave->foo = from->foo;
137 	fxsave->fos = from->fos;
138 
139 	for (i = 0; i < 8; i++) {
140 		memcpy((void *)fxsave->st_space + i * 16,
141 		       (void *)from->st_space + i * 10, 10);
142 	}
143 
144 	return 0;
145 }
146 
fpregs_legacy_set(struct task_struct * target,const struct user_regset * regset,unsigned int pos,unsigned int count,const void * kbuf,const void __user * ubuf)147 static int fpregs_legacy_set(struct task_struct *target,
148 			     const struct user_regset *regset,
149 			     unsigned int pos, unsigned int count,
150 			     const void *kbuf, const void __user *ubuf)
151 {
152 	struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
153 	const struct user_i387_struct *from;
154 	struct user_i387_struct buf;
155 
156 	if (ubuf) {
157 		if (copy_from_user(&buf, ubuf, sizeof(buf)))
158 			return -EFAULT;
159 		from = &buf;
160 	} else {
161 		from = kbuf;
162 	}
163 
164 	return um_fxsr_from_i387(fxsave, from);
165 }
166 #endif
167 
genregs_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)168 static int genregs_get(struct task_struct *target,
169 		       const struct user_regset *regset,
170 		       struct membuf to)
171 {
172 	int reg;
173 
174 	for (reg = 0; to.left; reg++)
175 		membuf_store(&to, getreg(target, reg * sizeof(unsigned long)));
176 	return 0;
177 }
178 
genregs_set(struct task_struct * target,const struct user_regset * regset,unsigned int pos,unsigned int count,const void * kbuf,const void __user * ubuf)179 static int genregs_set(struct task_struct *target,
180 		       const struct user_regset *regset,
181 		       unsigned int pos, unsigned int count,
182 		       const void *kbuf, const void __user *ubuf)
183 {
184 	int ret = 0;
185 
186 	if (kbuf) {
187 		const unsigned long *k = kbuf;
188 
189 		while (count >= sizeof(*k) && !ret) {
190 			ret = putreg(target, pos, *k++);
191 			count -= sizeof(*k);
192 			pos += sizeof(*k);
193 		}
194 	} else {
195 		const unsigned long  __user *u = ubuf;
196 
197 		while (count >= sizeof(*u) && !ret) {
198 			unsigned long word;
199 
200 			ret = __get_user(word, u++);
201 			if (ret)
202 				break;
203 			ret = putreg(target, pos, word);
204 			count -= sizeof(*u);
205 			pos += sizeof(*u);
206 		}
207 	}
208 	return ret;
209 }
210 
generic_fpregs_active(struct task_struct * target,const struct user_regset * regset)211 static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset)
212 {
213 	return regset->n;
214 }
215 
generic_fpregs_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)216 static int generic_fpregs_get(struct task_struct *target,
217 			      const struct user_regset *regset,
218 			      struct membuf to)
219 {
220 	void *fpregs = task_pt_regs(target)->regs.fp;
221 
222 	membuf_write(&to, fpregs, regset->size * regset->n);
223 	return 0;
224 }
225 
generic_fpregs_set(struct task_struct * target,const struct user_regset * regset,unsigned int pos,unsigned int count,const void * kbuf,const void __user * ubuf)226 static int generic_fpregs_set(struct task_struct *target,
227 			      const struct user_regset *regset,
228 			      unsigned int pos, unsigned int count,
229 			      const void *kbuf, const void __user *ubuf)
230 {
231 	void *fpregs = task_pt_regs(target)->regs.fp;
232 
233 	return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
234 				  fpregs, 0, regset->size * regset->n);
235 }
236 
237 static struct user_regset uml_regsets[] __ro_after_init = {
238 	[REGSET_GENERAL] = {
239 		.core_note_type	= NT_PRSTATUS,
240 		.n		= sizeof(struct user_regs_struct) / sizeof(long),
241 		.size		= sizeof(long),
242 		.align		= sizeof(long),
243 		.regset_get	= genregs_get,
244 		.set		= genregs_set
245 	},
246 #ifdef CONFIG_X86_32
247 	/* Old FP registers, they are needed in signal frames */
248 	[REGSET_FP_LEGACY] = {
249 		.core_note_type	= NT_PRFPREG,
250 		.n		= sizeof(struct user_i387_ia32_struct) / sizeof(long),
251 		.size		= sizeof(long),
252 		.align		= sizeof(long),
253 		.active		= generic_fpregs_active,
254 		.regset_get	= fpregs_legacy_get,
255 		.set		= fpregs_legacy_set,
256 	},
257 #endif
258 	[REGSET_FP] = {
259 #ifdef CONFIG_X86_32
260 		.core_note_type	= NT_PRXFPREG,
261 		.n		= sizeof(struct user32_fxsr_struct) / sizeof(long),
262 #else
263 		.core_note_type	= NT_PRFPREG,
264 		.n		= sizeof(struct user_i387_struct) / sizeof(long),
265 #endif
266 		.size		= sizeof(long),
267 		.align		= sizeof(long),
268 		.active		= generic_fpregs_active,
269 		.regset_get	= generic_fpregs_get,
270 		.set		= generic_fpregs_set,
271 	},
272 	[REGSET_XSTATE] = {
273 		.core_note_type	= NT_X86_XSTATE,
274 		.size		= sizeof(long),
275 		.align		= sizeof(long),
276 		.active		= generic_fpregs_active,
277 		.regset_get	= generic_fpregs_get,
278 		.set		= generic_fpregs_set,
279 	},
280 	/* TODO: Add TLS regset for 32bit */
281 };
282 
283 static const struct user_regset_view user_uml_view = {
284 #ifdef CONFIG_X86_32
285 	.name = "i386", .e_machine = EM_386,
286 #else
287 	.name = "x86_64", .e_machine = EM_X86_64,
288 #endif
289 	.regsets = uml_regsets, .n = ARRAY_SIZE(uml_regsets)
290 };
291 
292 const struct user_regset_view *
task_user_regset_view(struct task_struct * tsk)293 task_user_regset_view(struct task_struct *tsk)
294 {
295 	return &user_uml_view;
296 }
297 
init_regset_xstate_info(void)298 static int __init init_regset_xstate_info(void)
299 {
300 	uml_regsets[REGSET_XSTATE].n =
301 		host_fp_size / uml_regsets[REGSET_XSTATE].size;
302 
303 	return 0;
304 }
305 arch_initcall(init_regset_xstate_info);
306