Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/um/ptrace.c
26442 views
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
29
twd_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
*/
80
static 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
99
int um_i387_from_fxsr(struct user_i387_struct *i387,
100
const struct user_fxsr_struct *fxsave);
101
102
int 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
113
static 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
122
int um_fxsr_from_i387(struct user_fxsr_struct *fxsave,
123
const struct user_i387_struct *from);
124
125
int 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
147
static 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
168
static 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
179
static 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
211
static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset)
212
{
213
return regset->n;
214
}
215
216
static 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
226
static 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
237
static 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
283
static 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
292
const struct user_regset_view *
293
task_user_regset_view(struct task_struct *tsk)
294
{
295
return &user_uml_view;
296
}
297
298
static 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
}
305
arch_initcall(init_regset_xstate_info);
306
307