Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  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 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) */
 73static 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
 94static 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
130static 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
141static 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
173static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset)
174{
175	return regset->n;
176}
177
178static 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
188static 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
199static 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
245static 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
254const struct user_regset_view *
255task_user_regset_view(struct task_struct *tsk)
256{
257	return &user_uml_view;
258}
259
260static 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}
267arch_initcall(init_regset_xstate_info);