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