Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/x86/math-emu/get_address.c
10818 views
1
/*---------------------------------------------------------------------------+
2
| get_address.c |
3
| |
4
| Get the effective address from an FPU instruction. |
5
| |
6
| Copyright (C) 1992,1993,1994,1997 |
7
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
8
| Australia. E-mail [email protected] |
9
| |
10
| |
11
+---------------------------------------------------------------------------*/
12
13
/*---------------------------------------------------------------------------+
14
| Note: |
15
| The file contains code which accesses user memory. |
16
| Emulator static data may change when user memory is accessed, due to |
17
| other processes using the emulator while swapping is in progress. |
18
+---------------------------------------------------------------------------*/
19
20
#include <linux/stddef.h>
21
22
#include <asm/uaccess.h>
23
#include <asm/desc.h>
24
25
#include "fpu_system.h"
26
#include "exception.h"
27
#include "fpu_emu.h"
28
29
#define FPU_WRITE_BIT 0x10
30
31
static int reg_offset[] = {
32
offsetof(struct pt_regs, ax),
33
offsetof(struct pt_regs, cx),
34
offsetof(struct pt_regs, dx),
35
offsetof(struct pt_regs, bx),
36
offsetof(struct pt_regs, sp),
37
offsetof(struct pt_regs, bp),
38
offsetof(struct pt_regs, si),
39
offsetof(struct pt_regs, di)
40
};
41
42
#define REG_(x) (*(long *)(reg_offset[(x)] + (u_char *)FPU_info->regs))
43
44
static int reg_offset_vm86[] = {
45
offsetof(struct pt_regs, cs),
46
offsetof(struct kernel_vm86_regs, ds),
47
offsetof(struct kernel_vm86_regs, es),
48
offsetof(struct kernel_vm86_regs, fs),
49
offsetof(struct kernel_vm86_regs, gs),
50
offsetof(struct pt_regs, ss),
51
offsetof(struct kernel_vm86_regs, ds)
52
};
53
54
#define VM86_REG_(x) (*(unsigned short *) \
55
(reg_offset_vm86[((unsigned)x)] + (u_char *)FPU_info->regs))
56
57
static int reg_offset_pm[] = {
58
offsetof(struct pt_regs, cs),
59
offsetof(struct pt_regs, ds),
60
offsetof(struct pt_regs, es),
61
offsetof(struct pt_regs, fs),
62
offsetof(struct pt_regs, ds), /* dummy, not saved on stack */
63
offsetof(struct pt_regs, ss),
64
offsetof(struct pt_regs, ds)
65
};
66
67
#define PM_REG_(x) (*(unsigned short *) \
68
(reg_offset_pm[((unsigned)x)] + (u_char *)FPU_info->regs))
69
70
/* Decode the SIB byte. This function assumes mod != 0 */
71
static int sib(int mod, unsigned long *fpu_eip)
72
{
73
u_char ss, index, base;
74
long offset;
75
76
RE_ENTRANT_CHECK_OFF;
77
FPU_code_access_ok(1);
78
FPU_get_user(base, (u_char __user *) (*fpu_eip)); /* The SIB byte */
79
RE_ENTRANT_CHECK_ON;
80
(*fpu_eip)++;
81
ss = base >> 6;
82
index = (base >> 3) & 7;
83
base &= 7;
84
85
if ((mod == 0) && (base == 5))
86
offset = 0; /* No base register */
87
else
88
offset = REG_(base);
89
90
if (index == 4) {
91
/* No index register */
92
/* A non-zero ss is illegal */
93
if (ss)
94
EXCEPTION(EX_Invalid);
95
} else {
96
offset += (REG_(index)) << ss;
97
}
98
99
if (mod == 1) {
100
/* 8 bit signed displacement */
101
long displacement;
102
RE_ENTRANT_CHECK_OFF;
103
FPU_code_access_ok(1);
104
FPU_get_user(displacement, (signed char __user *)(*fpu_eip));
105
offset += displacement;
106
RE_ENTRANT_CHECK_ON;
107
(*fpu_eip)++;
108
} else if (mod == 2 || base == 5) { /* The second condition also has mod==0 */
109
/* 32 bit displacement */
110
long displacement;
111
RE_ENTRANT_CHECK_OFF;
112
FPU_code_access_ok(4);
113
FPU_get_user(displacement, (long __user *)(*fpu_eip));
114
offset += displacement;
115
RE_ENTRANT_CHECK_ON;
116
(*fpu_eip) += 4;
117
}
118
119
return offset;
120
}
121
122
static unsigned long vm86_segment(u_char segment, struct address *addr)
123
{
124
segment--;
125
#ifdef PARANOID
126
if (segment > PREFIX_SS_) {
127
EXCEPTION(EX_INTERNAL | 0x130);
128
math_abort(FPU_info, SIGSEGV);
129
}
130
#endif /* PARANOID */
131
addr->selector = VM86_REG_(segment);
132
return (unsigned long)VM86_REG_(segment) << 4;
133
}
134
135
/* This should work for 16 and 32 bit protected mode. */
136
static long pm_address(u_char FPU_modrm, u_char segment,
137
struct address *addr, long offset)
138
{
139
struct desc_struct descriptor;
140
unsigned long base_address, limit, address, seg_top;
141
142
segment--;
143
144
#ifdef PARANOID
145
/* segment is unsigned, so this also detects if segment was 0: */
146
if (segment > PREFIX_SS_) {
147
EXCEPTION(EX_INTERNAL | 0x132);
148
math_abort(FPU_info, SIGSEGV);
149
}
150
#endif /* PARANOID */
151
152
switch (segment) {
153
case PREFIX_GS_ - 1:
154
/* user gs handling can be lazy, use special accessors */
155
addr->selector = get_user_gs(FPU_info->regs);
156
break;
157
default:
158
addr->selector = PM_REG_(segment);
159
}
160
161
descriptor = LDT_DESCRIPTOR(PM_REG_(segment));
162
base_address = SEG_BASE_ADDR(descriptor);
163
address = base_address + offset;
164
limit = base_address
165
+ (SEG_LIMIT(descriptor) + 1) * SEG_GRANULARITY(descriptor) - 1;
166
if (limit < base_address)
167
limit = 0xffffffff;
168
169
if (SEG_EXPAND_DOWN(descriptor)) {
170
if (SEG_G_BIT(descriptor))
171
seg_top = 0xffffffff;
172
else {
173
seg_top = base_address + (1 << 20);
174
if (seg_top < base_address)
175
seg_top = 0xffffffff;
176
}
177
access_limit =
178
(address <= limit) || (address >= seg_top) ? 0 :
179
((seg_top - address) >= 255 ? 255 : seg_top - address);
180
} else {
181
access_limit =
182
(address > limit) || (address < base_address) ? 0 :
183
((limit - address) >= 254 ? 255 : limit - address + 1);
184
}
185
if (SEG_EXECUTE_ONLY(descriptor) ||
186
(!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT))) {
187
access_limit = 0;
188
}
189
return address;
190
}
191
192
/*
193
MOD R/M byte: MOD == 3 has a special use for the FPU
194
SIB byte used iff R/M = 100b
195
196
7 6 5 4 3 2 1 0
197
..... ......... .........
198
MOD OPCODE(2) R/M
199
200
SIB byte
201
202
7 6 5 4 3 2 1 0
203
..... ......... .........
204
SS INDEX BASE
205
206
*/
207
208
void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
209
struct address *addr, fpu_addr_modes addr_modes)
210
{
211
u_char mod;
212
unsigned rm = FPU_modrm & 7;
213
long *cpu_reg_ptr;
214
int address = 0; /* Initialized just to stop compiler warnings. */
215
216
/* Memory accessed via the cs selector is write protected
217
in `non-segmented' 32 bit protected mode. */
218
if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
219
&& (addr_modes.override.segment == PREFIX_CS_)) {
220
math_abort(FPU_info, SIGSEGV);
221
}
222
223
addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */
224
225
mod = (FPU_modrm >> 6) & 3;
226
227
if (rm == 4 && mod != 3) {
228
address = sib(mod, fpu_eip);
229
} else {
230
cpu_reg_ptr = &REG_(rm);
231
switch (mod) {
232
case 0:
233
if (rm == 5) {
234
/* Special case: disp32 */
235
RE_ENTRANT_CHECK_OFF;
236
FPU_code_access_ok(4);
237
FPU_get_user(address,
238
(unsigned long __user
239
*)(*fpu_eip));
240
(*fpu_eip) += 4;
241
RE_ENTRANT_CHECK_ON;
242
addr->offset = address;
243
return (void __user *)address;
244
} else {
245
address = *cpu_reg_ptr; /* Just return the contents
246
of the cpu register */
247
addr->offset = address;
248
return (void __user *)address;
249
}
250
case 1:
251
/* 8 bit signed displacement */
252
RE_ENTRANT_CHECK_OFF;
253
FPU_code_access_ok(1);
254
FPU_get_user(address, (signed char __user *)(*fpu_eip));
255
RE_ENTRANT_CHECK_ON;
256
(*fpu_eip)++;
257
break;
258
case 2:
259
/* 32 bit displacement */
260
RE_ENTRANT_CHECK_OFF;
261
FPU_code_access_ok(4);
262
FPU_get_user(address, (long __user *)(*fpu_eip));
263
(*fpu_eip) += 4;
264
RE_ENTRANT_CHECK_ON;
265
break;
266
case 3:
267
/* Not legal for the FPU */
268
EXCEPTION(EX_Invalid);
269
}
270
address += *cpu_reg_ptr;
271
}
272
273
addr->offset = address;
274
275
switch (addr_modes.default_mode) {
276
case 0:
277
break;
278
case VM86:
279
address += vm86_segment(addr_modes.override.segment, addr);
280
break;
281
case PM16:
282
case SEG32:
283
address = pm_address(FPU_modrm, addr_modes.override.segment,
284
addr, address);
285
break;
286
default:
287
EXCEPTION(EX_INTERNAL | 0x133);
288
}
289
290
return (void __user *)address;
291
}
292
293
void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
294
struct address *addr, fpu_addr_modes addr_modes)
295
{
296
u_char mod;
297
unsigned rm = FPU_modrm & 7;
298
int address = 0; /* Default used for mod == 0 */
299
300
/* Memory accessed via the cs selector is write protected
301
in `non-segmented' 32 bit protected mode. */
302
if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
303
&& (addr_modes.override.segment == PREFIX_CS_)) {
304
math_abort(FPU_info, SIGSEGV);
305
}
306
307
addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */
308
309
mod = (FPU_modrm >> 6) & 3;
310
311
switch (mod) {
312
case 0:
313
if (rm == 6) {
314
/* Special case: disp16 */
315
RE_ENTRANT_CHECK_OFF;
316
FPU_code_access_ok(2);
317
FPU_get_user(address,
318
(unsigned short __user *)(*fpu_eip));
319
(*fpu_eip) += 2;
320
RE_ENTRANT_CHECK_ON;
321
goto add_segment;
322
}
323
break;
324
case 1:
325
/* 8 bit signed displacement */
326
RE_ENTRANT_CHECK_OFF;
327
FPU_code_access_ok(1);
328
FPU_get_user(address, (signed char __user *)(*fpu_eip));
329
RE_ENTRANT_CHECK_ON;
330
(*fpu_eip)++;
331
break;
332
case 2:
333
/* 16 bit displacement */
334
RE_ENTRANT_CHECK_OFF;
335
FPU_code_access_ok(2);
336
FPU_get_user(address, (unsigned short __user *)(*fpu_eip));
337
(*fpu_eip) += 2;
338
RE_ENTRANT_CHECK_ON;
339
break;
340
case 3:
341
/* Not legal for the FPU */
342
EXCEPTION(EX_Invalid);
343
break;
344
}
345
switch (rm) {
346
case 0:
347
address += FPU_info->regs->bx + FPU_info->regs->si;
348
break;
349
case 1:
350
address += FPU_info->regs->bx + FPU_info->regs->di;
351
break;
352
case 2:
353
address += FPU_info->regs->bp + FPU_info->regs->si;
354
if (addr_modes.override.segment == PREFIX_DEFAULT)
355
addr_modes.override.segment = PREFIX_SS_;
356
break;
357
case 3:
358
address += FPU_info->regs->bp + FPU_info->regs->di;
359
if (addr_modes.override.segment == PREFIX_DEFAULT)
360
addr_modes.override.segment = PREFIX_SS_;
361
break;
362
case 4:
363
address += FPU_info->regs->si;
364
break;
365
case 5:
366
address += FPU_info->regs->di;
367
break;
368
case 6:
369
address += FPU_info->regs->bp;
370
if (addr_modes.override.segment == PREFIX_DEFAULT)
371
addr_modes.override.segment = PREFIX_SS_;
372
break;
373
case 7:
374
address += FPU_info->regs->bx;
375
break;
376
}
377
378
add_segment:
379
address &= 0xffff;
380
381
addr->offset = address;
382
383
switch (addr_modes.default_mode) {
384
case 0:
385
break;
386
case VM86:
387
address += vm86_segment(addr_modes.override.segment, addr);
388
break;
389
case PM16:
390
case SEG32:
391
address = pm_address(FPU_modrm, addr_modes.override.segment,
392
addr, address);
393
break;
394
default:
395
EXCEPTION(EX_INTERNAL | 0x131);
396
}
397
398
return (void __user *)address;
399
}
400
401