xref: /linux/arch/x86/um/ptrace.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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 
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(struct user_fxsr_struct *fxsave)
29 {
30 	struct _fpxreg *st = NULL;
31 	unsigned long twd = (unsigned long) fxsave->twd;
32 	unsigned long tag;
33 	unsigned long ret = 0xffff0000;
34 	int i;
35 
36 #define FPREG_ADDR(f, n)	((char *)&(f)->st_space + (n) * 16)
37 
38 	for (i = 0; i < 8; i++) {
39 		if (twd & 0x1) {
40 			st = (struct _fpxreg *) FPREG_ADDR(fxsave, i);
41 
42 			switch (st->exponent & 0x7fff) {
43 			case 0x7fff:
44 				tag = 2;		/* Special */
45 				break;
46 			case 0x0000:
47 				if (!st->significand[0] &&
48 				    !st->significand[1] &&
49 				    !st->significand[2] &&
50 				    !st->significand[3]) {
51 					tag = 1;	/* Zero */
52 				} else {
53 					tag = 2;	/* Special */
54 				}
55 				break;
56 			default:
57 				if (st->significand[3] & 0x8000)
58 					tag = 0;	/* Valid */
59 				else
60 					tag = 2;	/* Special */
61 				break;
62 			}
63 		} else {
64 			tag = 3;			/* Empty */
65 		}
66 		ret |= (tag << (2 * i));
67 		twd = twd >> 1;
68 	}
69 	return ret;
70 }
71 
72 /* Get/set the old 32bit i387 registers (pre-FPX) */
73 static int fpregs_legacy_get(struct task_struct *target,
74 			     const struct user_regset *regset,
75 			     struct membuf to)
76 {
77 	struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
78 	int i;
79 
80 	membuf_store(&to, (unsigned long)fxsave->cwd | 0xffff0000ul);
81 	membuf_store(&to, (unsigned long)fxsave->swd | 0xffff0000ul);
82 	membuf_store(&to, twd_fxsr_to_i387(fxsave));
83 	membuf_store(&to, fxsave->fip);
84 	membuf_store(&to, fxsave->fcs | ((unsigned long)fxsave->fop << 16));
85 	membuf_store(&to, fxsave->foo);
86 	membuf_store(&to, fxsave->fos);
87 
88 	for (i = 0; i < 8; i++)
89 		membuf_write(&to, (void *)fxsave->st_space + i * 16, 10);
90 
91 	return 0;
92 }
93 
94 static int fpregs_legacy_set(struct task_struct *target,
95 			     const struct user_regset *regset,
96 			     unsigned int pos, unsigned int count,
97 			     const void *kbuf, const void __user *ubuf)
98 {
99 	struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
100 	const struct user_i387_struct *from;
101 	struct user_i387_struct buf;
102 	int i;
103 
104 	if (ubuf) {
105 		if (copy_from_user(&buf, ubuf, sizeof(buf)))
106 			return -EFAULT;
107 		from = &buf;
108 	} else {
109 		from = kbuf;
110 	}
111 
112 	fxsave->cwd = (unsigned short)(from->cwd & 0xffff);
113 	fxsave->swd = (unsigned short)(from->swd & 0xffff);
114 	fxsave->twd = twd_i387_to_fxsr((unsigned short)(from->twd & 0xffff));
115 	fxsave->fip = from->fip;
116 	fxsave->fop = (unsigned short)((from->fcs & 0xffff0000ul) >> 16);
117 	fxsave->fcs = (from->fcs & 0xffff);
118 	fxsave->foo = from->foo;
119 	fxsave->fos = from->fos;
120 
121 	for (i = 0; i < 8; i++) {
122 		memcpy((void *)fxsave->st_space + i * 16,
123 		       (void *)from->st_space + i * 10, 10);
124 	}
125 
126 	return 0;
127 }
128 #endif
129 
130 static int genregs_get(struct task_struct *target,
131 		       const struct user_regset *regset,
132 		       struct membuf to)
133 {
134 	int reg;
135 
136 	for (reg = 0; to.left; reg++)
137 		membuf_store(&to, getreg(target, reg * sizeof(unsigned long)));
138 	return 0;
139 }
140 
141 static int genregs_set(struct task_struct *target,
142 		       const struct user_regset *regset,
143 		       unsigned int pos, unsigned int count,
144 		       const void *kbuf, const void __user *ubuf)
145 {
146 	int ret = 0;
147 
148 	if (kbuf) {
149 		const unsigned long *k = kbuf;
150 
151 		while (count >= sizeof(*k) && !ret) {
152 			ret = putreg(target, pos, *k++);
153 			count -= sizeof(*k);
154 			pos += sizeof(*k);
155 		}
156 	} else {
157 		const unsigned long  __user *u = ubuf;
158 
159 		while (count >= sizeof(*u) && !ret) {
160 			unsigned long word;
161 
162 			ret = __get_user(word, u++);
163 			if (ret)
164 				break;
165 			ret = putreg(target, pos, word);
166 			count -= sizeof(*u);
167 			pos += sizeof(*u);
168 		}
169 	}
170 	return ret;
171 }
172 
173 static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset)
174 {
175 	return regset->n;
176 }
177 
178 static int generic_fpregs_get(struct task_struct *target,
179 			      const struct user_regset *regset,
180 			      struct membuf to)
181 {
182 	void *fpregs = task_pt_regs(target)->regs.fp;
183 
184 	membuf_write(&to, fpregs, regset->size * regset->n);
185 	return 0;
186 }
187 
188 static int generic_fpregs_set(struct task_struct *target,
189 			      const struct user_regset *regset,
190 			      unsigned int pos, unsigned int count,
191 			      const void *kbuf, const void __user *ubuf)
192 {
193 	void *fpregs = task_pt_regs(target)->regs.fp;
194 
195 	return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
196 				  fpregs, 0, regset->size * regset->n);
197 }
198 
199 static struct user_regset uml_regsets[] __ro_after_init = {
200 	[REGSET_GENERAL] = {
201 		.core_note_type	= NT_PRSTATUS,
202 		.n		= sizeof(struct user_regs_struct) / sizeof(long),
203 		.size		= sizeof(long),
204 		.align		= sizeof(long),
205 		.regset_get	= genregs_get,
206 		.set		= genregs_set
207 	},
208 #ifdef CONFIG_X86_32
209 	/* Old FP registers, they are needed in signal frames */
210 	[REGSET_FP_LEGACY] = {
211 		.core_note_type	= NT_PRFPREG,
212 		.n		= sizeof(struct user_i387_ia32_struct) / sizeof(long),
213 		.size		= sizeof(long),
214 		.align		= sizeof(long),
215 		.active		= generic_fpregs_active,
216 		.regset_get	= fpregs_legacy_get,
217 		.set		= fpregs_legacy_set,
218 	},
219 #endif
220 	[REGSET_FP] = {
221 #ifdef CONFIG_X86_32
222 		.core_note_type	= NT_PRXFPREG,
223 		.n		= sizeof(struct user32_fxsr_struct) / sizeof(long),
224 #else
225 		.core_note_type	= NT_PRFPREG,
226 		.n		= sizeof(struct user_i387_struct) / sizeof(long),
227 #endif
228 		.size		= sizeof(long),
229 		.align		= sizeof(long),
230 		.active		= generic_fpregs_active,
231 		.regset_get	= generic_fpregs_get,
232 		.set		= generic_fpregs_set,
233 	},
234 	[REGSET_XSTATE] = {
235 		.core_note_type	= NT_X86_XSTATE,
236 		.size		= sizeof(long),
237 		.align		= sizeof(long),
238 		.active		= generic_fpregs_active,
239 		.regset_get	= generic_fpregs_get,
240 		.set		= generic_fpregs_set,
241 	},
242 	/* TODO: Add TLS regset for 32bit */
243 };
244 
245 static const struct user_regset_view user_uml_view = {
246 #ifdef CONFIG_X86_32
247 	.name = "i386", .e_machine = EM_386,
248 #else
249 	.name = "x86_64", .e_machine = EM_X86_64,
250 #endif
251 	.regsets = uml_regsets, .n = ARRAY_SIZE(uml_regsets)
252 };
253 
254 const struct user_regset_view *
255 task_user_regset_view(struct task_struct *tsk)
256 {
257 	return &user_uml_view;
258 }
259 
260 static int __init init_regset_xstate_info(void)
261 {
262 	uml_regsets[REGSET_XSTATE].n =
263 		host_fp_size / uml_regsets[REGSET_XSTATE].size;
264 
265 	return 0;
266 }
267 arch_initcall(init_regset_xstate_info);
268