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