1 // SPDX-License-Identifier: GPL-2.0
3 #include <linux/sched.h>
5 #include <linux/regset.h>
6 #include <asm/user32.h>
7 #include <asm/sigcontext.h>
11 * FPU tag word conversions.
14 static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
16 unsigned int tmp; /* to avoid 16 bit prefixes in the code */
18 /* Transform each pair of bits into 01 (valid) or 00 (empty) */
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 */
28 static inline unsigned long twd_fxsr_to_i387(struct user_fxsr_struct *fxsave)
30 struct _fpxreg *st = NULL;
31 unsigned long twd = (unsigned long) fxsave->twd;
33 unsigned long ret = 0xffff0000;
36 #define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16)
38 for (i = 0; i < 8; i++) {
40 st = (struct _fpxreg *) FPREG_ADDR(fxsave, i);
42 switch (st->exponent & 0x7fff) {
44 tag = 2; /* Special */
47 if (!st->significand[0] &&
48 !st->significand[1] &&
49 !st->significand[2] &&
50 !st->significand[3]) {
53 tag = 2; /* Special */
57 if (st->significand[3] & 0x8000)
60 tag = 2; /* Special */
66 ret |= (tag << (2 * i));
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,
77 struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
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);
88 for (i = 0; i < 8; i++)
89 membuf_write(&to, (void *)fxsave->st_space + i * 16, 10);
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)
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;
105 if (copy_from_user(&buf, ubuf, sizeof(buf)))
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;
121 for (i = 0; i < 8; i++) {
122 memcpy((void *)fxsave->st_space + i * 16,
123 (void *)from->st_space + i * 10, 10);
130 static int genregs_get(struct task_struct *target,
131 const struct user_regset *regset,
136 for (reg = 0; to.left; reg++)
137 membuf_store(&to, getreg(target, reg * sizeof(unsigned long)));
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)
149 const unsigned long *k = kbuf;
151 while (count >= sizeof(*k) && !ret) {
152 ret = putreg(target, pos, *k++);
157 const unsigned long __user *u = ubuf;
159 while (count >= sizeof(*u) && !ret) {
162 ret = __get_user(word, u++);
165 ret = putreg(target, pos, word);
173 static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset)
178 static int generic_fpregs_get(struct task_struct *target,
179 const struct user_regset *regset,
182 void *fpregs = task_pt_regs(target)->regs.fp;
184 membuf_write(&to, fpregs, regset->size * regset->n);
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)
193 void *fpregs = task_pt_regs(target)->regs.fp;
195 return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
196 fpregs, 0, regset->size * regset->n);
199 static struct user_regset uml_regsets[] __ro_after_init = {
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,
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,
222 .core_note_type = NT_PRXFPREG,
223 .n = sizeof(struct user32_fxsr_struct) / sizeof(long),
225 .core_note_type = NT_PRFPREG,
226 .n = sizeof(struct user_i387_struct) / sizeof(long),
228 .size = sizeof(long),
229 .align = sizeof(long),
230 .active = generic_fpregs_active,
231 .regset_get = generic_fpregs_get,
232 .set = generic_fpregs_set,
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,
242 /* TODO: Add TLS regset for 32bit */
245 static const struct user_regset_view user_uml_view = {
247 .name = "i386", .e_machine = EM_386,
249 .name = "x86_64", .e_machine = EM_X86_64,
251 .regsets = uml_regsets, .n = ARRAY_SIZE(uml_regsets)
254 const struct user_regset_view *
255 task_user_regset_view(struct task_struct *tsk)
257 return &user_uml_view;
260 static int __init init_regset_xstate_info(void)
262 uml_regsets[REGSET_XSTATE].n =
263 host_fp_size / uml_regsets[REGSET_XSTATE].size;
267 arch_initcall(init_regset_xstate_info);