]> Git Repo - J-linux.git/blob - arch/x86/um/ptrace.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / arch / x86 / um / ptrace.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
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);
This page took 0.040689 seconds and 4 git commands to generate.