File Index
Files and directories
Sign in
FILE

ptrace.c

File preview with syntax highlighting.

linux-6.19.14/arch/x86/um/ptrace.c C
  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
 14static 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
 28static inline unsigned long
 29twd_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 */
 80static 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
 99int um_i387_from_fxsr(struct user_i387_struct *i387,
100		      const struct user_fxsr_struct *fxsave);
101
102int 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
113static 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
122int um_fxsr_from_i387(struct user_fxsr_struct *fxsave,
123		      const struct user_i387_struct *from);
124
125int 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
147static 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
168static 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
179static 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
211static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset)
212{
213	return regset->n;
214}
215
216static 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
226static 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
237static struct user_regset uml_regsets[] __ro_after_init = {
238	[REGSET_GENERAL] = {
239		USER_REGSET_NOTE_TYPE(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		USER_REGSET_NOTE_TYPE(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		USER_REGSET_NOTE_TYPE(PRXFPREG),
261		.n		= sizeof(struct user32_fxsr_struct) / sizeof(long),
262#else
263		USER_REGSET_NOTE_TYPE(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		USER_REGSET_NOTE_TYPE(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
283static 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
292const struct user_regset_view *
293task_user_regset_view(struct task_struct *tsk)
294{
295	return &user_uml_view;
296}
297
298static 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}
305arch_initcall(init_regset_xstate_info);
~/ Read-only file preview.