• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   billm@suburbia.net             |
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 */
sib(int mod,unsigned long * fpu_eip)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 
vm86_segment(u_char segment,struct address * addr)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. */
pm_address(u_char FPU_modrm,u_char segment,struct address * addr,long offset)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 		/* gs isn't used by the kernel, so it still has its
154 		   user-space value. */
155 	case PREFIX_GS_ - 1:
156 		/* N.B. - movl %seg, mem is a 2 byte write regardless of prefix */
157 		savesegment(gs, addr->selector);
158 		break;
159 	default:
160 		addr->selector = PM_REG_(segment);
161 	}
162 
163 	descriptor = LDT_DESCRIPTOR(PM_REG_(segment));
164 	base_address = SEG_BASE_ADDR(descriptor);
165 	address = base_address + offset;
166 	limit = base_address
167 	    + (SEG_LIMIT(descriptor) + 1) * SEG_GRANULARITY(descriptor) - 1;
168 	if (limit < base_address)
169 		limit = 0xffffffff;
170 
171 	if (SEG_EXPAND_DOWN(descriptor)) {
172 		if (SEG_G_BIT(descriptor))
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_WRITE_PERM(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 
FPU_get_address(u_char FPU_modrm,unsigned long * fpu_eip,struct address * addr,fpu_addr_modes addr_modes)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 
FPU_get_address_16(u_char FPU_modrm,unsigned long * fpu_eip,struct address * addr,fpu_addr_modes addr_modes)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