Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
get_address.c
Go to the documentation of this file.
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 
78  FPU_get_user(base, (u_char __user *) (*fpu_eip)); /* The SIB byte */
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)
95  } else {
96  offset += (REG_(index)) << ss;
97  }
98 
99  if (mod == 1) {
100  /* 8 bit signed displacement */
101  long displacement;
104  FPU_get_user(displacement, (signed char __user *)(*fpu_eip));
105  offset += displacement;
107  (*fpu_eip)++;
108  } else if (mod == 2 || base == 5) { /* The second condition also has mod==0 */
109  /* 32 bit displacement */
110  long displacement;
113  FPU_get_user(displacement, (long __user *)(*fpu_eip));
114  offset += displacement;
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);
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);
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
166  if (limit < base_address)
167  limit = 0xffffffff;
168 
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  }
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_)) {
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 */
237  FPU_get_user(address,
238  (unsigned long __user
239  *)(*fpu_eip));
240  (*fpu_eip) += 4;
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 */
254  FPU_get_user(address, (signed char __user *)(*fpu_eip));
256  (*fpu_eip)++;
257  break;
258  case 2:
259  /* 32 bit displacement */
262  FPU_get_user(address, (long __user *)(*fpu_eip));
263  (*fpu_eip) += 4;
265  break;
266  case 3:
267  /* Not legal for the FPU */
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_)) {
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 */
317  FPU_get_user(address,
318  (unsigned short __user *)(*fpu_eip));
319  (*fpu_eip) += 2;
321  goto add_segment;
322  }
323  break;
324  case 1:
325  /* 8 bit signed displacement */
328  FPU_get_user(address, (signed char __user *)(*fpu_eip));
330  (*fpu_eip)++;
331  break;
332  case 2:
333  /* 16 bit displacement */
336  FPU_get_user(address, (unsigned short __user *)(*fpu_eip));
337  (*fpu_eip) += 2;
339  break;
340  case 3:
341  /* Not legal for the FPU */
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 }