| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*---------------------------------------------------------------------------+
 | 
					
						
							|  |  |  |  |  get_address.c                                                            | | 
					
						
							|  |  |  |  |                                                                           | | 
					
						
							|  |  |  |  | Get the effective address from an FPU instruction.                        | | 
					
						
							|  |  |  |  |                                                                           | | 
					
						
							|  |  |  |  | Copyright (C) 1992,1993,1994,1997                                         | | 
					
						
							|  |  |  |  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      | | 
					
						
							|  |  |  |  |                       Australia.  E-mail   billm@suburbia.net             | | 
					
						
							|  |  |  |  |                                                                           | | 
					
						
							|  |  |  |  |                                                                           | | 
					
						
							|  |  |  |  +---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------+
 | 
					
						
							|  |  |  |  | Note:                                                                     | | 
					
						
							|  |  |  |  |    The file contains code which accesses user memory.                     | | 
					
						
							|  |  |  |  |    Emulator static data may change when user memory is accessed, due to   | | 
					
						
							|  |  |  |  |    other processes using the emulator while swapping is in progress.      | | 
					
						
							|  |  |  |  +---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/stddef.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/uaccess.h>
 | 
					
						
							|  |  |  | #include <asm/desc.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "fpu_system.h"
 | 
					
						
							|  |  |  | #include "exception.h"
 | 
					
						
							|  |  |  | #include "fpu_emu.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define FPU_WRITE_BIT 0x10
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int reg_offset[] = { | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 	offsetof(struct pt_regs, ax), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, cx), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, dx), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, bx), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, sp), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, bp), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, si), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, di) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | #define REG_(x) (*(long *)(reg_offset[(x)] + (u_char *)FPU_info->regs))
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int reg_offset_vm86[] = { | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 	offsetof(struct pt_regs, cs), | 
					
						
							|  |  |  | 	offsetof(struct kernel_vm86_regs, ds), | 
					
						
							|  |  |  | 	offsetof(struct kernel_vm86_regs, es), | 
					
						
							|  |  |  | 	offsetof(struct kernel_vm86_regs, fs), | 
					
						
							|  |  |  | 	offsetof(struct kernel_vm86_regs, gs), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, ss), | 
					
						
							|  |  |  | 	offsetof(struct kernel_vm86_regs, ds) | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define VM86_REG_(x) (*(unsigned short *) \
 | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 		(reg_offset_vm86[((unsigned)x)] + (u_char *)FPU_info->regs)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int reg_offset_pm[] = { | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 	offsetof(struct pt_regs, cs), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, ds), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, es), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, fs), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, ds),	/* dummy, not saved on stack */ | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, ss), | 
					
						
							|  |  |  | 	offsetof(struct pt_regs, ds) | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define PM_REG_(x) (*(unsigned short *) \
 | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 		(reg_offset_pm[((unsigned)x)] + (u_char *)FPU_info->regs)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* Decode the SIB byte. This function assumes mod != 0 */ | 
					
						
							|  |  |  | static int sib(int mod, unsigned long *fpu_eip) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	u_char ss, index, base; | 
					
						
							|  |  |  | 	long offset; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	RE_ENTRANT_CHECK_OFF; | 
					
						
							|  |  |  | 	FPU_code_access_ok(1); | 
					
						
							|  |  |  | 	FPU_get_user(base, (u_char __user *) (*fpu_eip));	/* The SIB byte */ | 
					
						
							|  |  |  | 	RE_ENTRANT_CHECK_ON; | 
					
						
							|  |  |  | 	(*fpu_eip)++; | 
					
						
							|  |  |  | 	ss = base >> 6; | 
					
						
							|  |  |  | 	index = (base >> 3) & 7; | 
					
						
							|  |  |  | 	base &= 7; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((mod == 0) && (base == 5)) | 
					
						
							|  |  |  | 		offset = 0;	/* No base register */ | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		offset = REG_(base); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (index == 4) { | 
					
						
							|  |  |  | 		/* No index register */ | 
					
						
							|  |  |  | 		/* A non-zero ss is illegal */ | 
					
						
							|  |  |  | 		if (ss) | 
					
						
							|  |  |  | 			EXCEPTION(EX_Invalid); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		offset += (REG_(index)) << ss; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mod == 1) { | 
					
						
							|  |  |  | 		/* 8 bit signed displacement */ | 
					
						
							|  |  |  | 		long displacement; | 
					
						
							|  |  |  | 		RE_ENTRANT_CHECK_OFF; | 
					
						
							|  |  |  | 		FPU_code_access_ok(1); | 
					
						
							|  |  |  | 		FPU_get_user(displacement, (signed char __user *)(*fpu_eip)); | 
					
						
							|  |  |  | 		offset += displacement; | 
					
						
							|  |  |  | 		RE_ENTRANT_CHECK_ON; | 
					
						
							|  |  |  | 		(*fpu_eip)++; | 
					
						
							|  |  |  | 	} else if (mod == 2 || base == 5) {	/* The second condition also has mod==0 */ | 
					
						
							|  |  |  | 		/* 32 bit displacement */ | 
					
						
							|  |  |  | 		long displacement; | 
					
						
							|  |  |  | 		RE_ENTRANT_CHECK_OFF; | 
					
						
							|  |  |  | 		FPU_code_access_ok(4); | 
					
						
							|  |  |  | 		FPU_get_user(displacement, (long __user *)(*fpu_eip)); | 
					
						
							|  |  |  | 		offset += displacement; | 
					
						
							|  |  |  | 		RE_ENTRANT_CHECK_ON; | 
					
						
							|  |  |  | 		(*fpu_eip) += 4; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	return offset; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | static unsigned long vm86_segment(u_char segment, struct address *addr) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	segment--; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #ifdef PARANOID
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	if (segment > PREFIX_SS_) { | 
					
						
							|  |  |  | 		EXCEPTION(EX_INTERNAL | 0x130); | 
					
						
							|  |  |  | 		math_abort(FPU_info, SIGSEGV); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif /* PARANOID */
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	addr->selector = VM86_REG_(segment); | 
					
						
							|  |  |  | 	return (unsigned long)VM86_REG_(segment) << 4; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* This should work for 16 and 32 bit protected mode. */ | 
					
						
							|  |  |  | static long pm_address(u_char FPU_modrm, u_char segment, | 
					
						
							|  |  |  | 		       struct address *addr, long offset) | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct desc_struct descriptor; | 
					
						
							|  |  |  | 	unsigned long base_address, limit, address, seg_top; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	segment--; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #ifdef PARANOID
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	/* segment is unsigned, so this also detects if segment was 0: */ | 
					
						
							|  |  |  | 	if (segment > PREFIX_SS_) { | 
					
						
							|  |  |  | 		EXCEPTION(EX_INTERNAL | 0x132); | 
					
						
							|  |  |  | 		math_abort(FPU_info, SIGSEGV); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif /* PARANOID */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	switch (segment) { | 
					
						
							|  |  |  | 	case PREFIX_GS_ - 1: | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:40 +09:00
										 |  |  | 		/* user gs handling can be lazy, use special accessors */ | 
					
						
							|  |  |  | 		addr->selector = get_user_gs(FPU_info->regs); | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		addr->selector = PM_REG_(segment); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	descriptor = LDT_DESCRIPTOR(PM_REG_(segment)); | 
					
						
							|  |  |  | 	base_address = SEG_BASE_ADDR(descriptor); | 
					
						
							|  |  |  | 	address = base_address + offset; | 
					
						
							|  |  |  | 	limit = base_address | 
					
						
							|  |  |  | 	    + (SEG_LIMIT(descriptor) + 1) * SEG_GRANULARITY(descriptor) - 1; | 
					
						
							|  |  |  | 	if (limit < base_address) | 
					
						
							|  |  |  | 		limit = 0xffffffff; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (SEG_EXPAND_DOWN(descriptor)) { | 
					
						
							|  |  |  | 		if (SEG_G_BIT(descriptor)) | 
					
						
							|  |  |  | 			seg_top = 0xffffffff; | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			seg_top = base_address + (1 << 20); | 
					
						
							|  |  |  | 			if (seg_top < base_address) | 
					
						
							|  |  |  | 				seg_top = 0xffffffff; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		access_limit = | 
					
						
							|  |  |  | 		    (address <= limit) || (address >= seg_top) ? 0 : | 
					
						
							|  |  |  | 		    ((seg_top - address) >= 255 ? 255 : seg_top - address); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		access_limit = | 
					
						
							|  |  |  | 		    (address > limit) || (address < base_address) ? 0 : | 
					
						
							|  |  |  | 		    ((limit - address) >= 254 ? 255 : limit - address + 1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (SEG_EXECUTE_ONLY(descriptor) || | 
					
						
							|  |  |  | 	    (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT))) { | 
					
						
							|  |  |  | 		access_limit = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return address; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |        MOD R/M byte:  MOD == 3 has a special use for the FPU | 
					
						
							|  |  |  |                       SIB byte used iff R/M = 100b | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |        7   6   5   4   3   2   1   0 | 
					
						
							|  |  |  |        .....   .........   ......... | 
					
						
							|  |  |  |         MOD    OPCODE(2)     R/M | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |        SIB byte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |        7   6   5   4   3   2   1   0 | 
					
						
							|  |  |  |        .....   .........   ......... | 
					
						
							|  |  |  |         SS      INDEX        BASE | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip, | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 			     struct address *addr, fpu_addr_modes addr_modes) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u_char mod; | 
					
						
							|  |  |  | 	unsigned rm = FPU_modrm & 7; | 
					
						
							|  |  |  | 	long *cpu_reg_ptr; | 
					
						
							|  |  |  | 	int address = 0;	/* Initialized just to stop compiler warnings. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Memory accessed via the cs selector is write protected
 | 
					
						
							|  |  |  | 	   in `non-segmented' 32 bit protected mode. */ | 
					
						
							|  |  |  | 	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) | 
					
						
							|  |  |  | 	    && (addr_modes.override.segment == PREFIX_CS_)) { | 
					
						
							|  |  |  | 		math_abort(FPU_info, SIGSEGV); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mod = (FPU_modrm >> 6) & 3; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rm == 4 && mod != 3) { | 
					
						
							|  |  |  | 		address = sib(mod, fpu_eip); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		cpu_reg_ptr = ®_(rm); | 
					
						
							|  |  |  | 		switch (mod) { | 
					
						
							|  |  |  | 		case 0: | 
					
						
							|  |  |  | 			if (rm == 5) { | 
					
						
							|  |  |  | 				/* Special case: disp32 */ | 
					
						
							|  |  |  | 				RE_ENTRANT_CHECK_OFF; | 
					
						
							|  |  |  | 				FPU_code_access_ok(4); | 
					
						
							|  |  |  | 				FPU_get_user(address, | 
					
						
							|  |  |  | 					     (unsigned long __user | 
					
						
							|  |  |  | 					      *)(*fpu_eip)); | 
					
						
							|  |  |  | 				(*fpu_eip) += 4; | 
					
						
							|  |  |  | 				RE_ENTRANT_CHECK_ON; | 
					
						
							|  |  |  | 				addr->offset = address; | 
					
						
							|  |  |  | 				return (void __user *)address; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				address = *cpu_reg_ptr;	/* Just return the contents
 | 
					
						
							|  |  |  | 							   of the cpu register */ | 
					
						
							|  |  |  | 				addr->offset = address; | 
					
						
							|  |  |  | 				return (void __user *)address; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case 1: | 
					
						
							|  |  |  | 			/* 8 bit signed displacement */ | 
					
						
							|  |  |  | 			RE_ENTRANT_CHECK_OFF; | 
					
						
							|  |  |  | 			FPU_code_access_ok(1); | 
					
						
							|  |  |  | 			FPU_get_user(address, (signed char __user *)(*fpu_eip)); | 
					
						
							|  |  |  | 			RE_ENTRANT_CHECK_ON; | 
					
						
							|  |  |  | 			(*fpu_eip)++; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 2: | 
					
						
							|  |  |  | 			/* 32 bit displacement */ | 
					
						
							|  |  |  | 			RE_ENTRANT_CHECK_OFF; | 
					
						
							|  |  |  | 			FPU_code_access_ok(4); | 
					
						
							|  |  |  | 			FPU_get_user(address, (long __user *)(*fpu_eip)); | 
					
						
							|  |  |  | 			(*fpu_eip) += 4; | 
					
						
							|  |  |  | 			RE_ENTRANT_CHECK_ON; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 3: | 
					
						
							|  |  |  | 			/* Not legal for the FPU */ | 
					
						
							|  |  |  | 			EXCEPTION(EX_Invalid); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		address += *cpu_reg_ptr; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr->offset = address; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (addr_modes.default_mode) { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case VM86: | 
					
						
							|  |  |  | 		address += vm86_segment(addr_modes.override.segment, addr); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case PM16: | 
					
						
							|  |  |  | 	case SEG32: | 
					
						
							|  |  |  | 		address = pm_address(FPU_modrm, addr_modes.override.segment, | 
					
						
							|  |  |  | 				     addr, address); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		EXCEPTION(EX_INTERNAL | 0x133); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (void __user *)address; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip, | 
					
						
							|  |  |  | 				struct address *addr, fpu_addr_modes addr_modes) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	u_char mod; | 
					
						
							|  |  |  | 	unsigned rm = FPU_modrm & 7; | 
					
						
							|  |  |  | 	int address = 0;	/* Default used for mod == 0 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Memory accessed via the cs selector is write protected
 | 
					
						
							|  |  |  | 	   in `non-segmented' 32 bit protected mode. */ | 
					
						
							|  |  |  | 	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) | 
					
						
							|  |  |  | 	    && (addr_modes.override.segment == PREFIX_CS_)) { | 
					
						
							|  |  |  | 		math_abort(FPU_info, SIGSEGV); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mod = (FPU_modrm >> 6) & 3; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (mod) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	case 0: | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		if (rm == 6) { | 
					
						
							|  |  |  | 			/* Special case: disp16 */ | 
					
						
							|  |  |  | 			RE_ENTRANT_CHECK_OFF; | 
					
						
							|  |  |  | 			FPU_code_access_ok(2); | 
					
						
							|  |  |  | 			FPU_get_user(address, | 
					
						
							|  |  |  | 				     (unsigned short __user *)(*fpu_eip)); | 
					
						
							|  |  |  | 			(*fpu_eip) += 2; | 
					
						
							|  |  |  | 			RE_ENTRANT_CHECK_ON; | 
					
						
							|  |  |  | 			goto add_segment; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	case 1: | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		/* 8 bit signed displacement */ | 
					
						
							|  |  |  | 		RE_ENTRANT_CHECK_OFF; | 
					
						
							|  |  |  | 		FPU_code_access_ok(1); | 
					
						
							|  |  |  | 		FPU_get_user(address, (signed char __user *)(*fpu_eip)); | 
					
						
							|  |  |  | 		RE_ENTRANT_CHECK_ON; | 
					
						
							|  |  |  | 		(*fpu_eip)++; | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	case 2: | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		/* 16 bit displacement */ | 
					
						
							|  |  |  | 		RE_ENTRANT_CHECK_OFF; | 
					
						
							|  |  |  | 		FPU_code_access_ok(2); | 
					
						
							|  |  |  | 		FPU_get_user(address, (unsigned short __user *)(*fpu_eip)); | 
					
						
							|  |  |  | 		(*fpu_eip) += 2; | 
					
						
							|  |  |  | 		RE_ENTRANT_CHECK_ON; | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	case 3: | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		/* Not legal for the FPU */ | 
					
						
							|  |  |  | 		EXCEPTION(EX_Invalid); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	switch (rm) { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 		address += FPU_info->regs->bx + FPU_info->regs->si; | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 1: | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 		address += FPU_info->regs->bx + FPU_info->regs->di; | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 2: | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 		address += FPU_info->regs->bp + FPU_info->regs->si; | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		if (addr_modes.override.segment == PREFIX_DEFAULT) | 
					
						
							|  |  |  | 			addr_modes.override.segment = PREFIX_SS_; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case 3: | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 		address += FPU_info->regs->bp + FPU_info->regs->di; | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		if (addr_modes.override.segment == PREFIX_DEFAULT) | 
					
						
							|  |  |  | 			addr_modes.override.segment = PREFIX_SS_; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case 4: | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 		address += FPU_info->regs->si; | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 5: | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 		address += FPU_info->regs->di; | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 6: | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 		address += FPU_info->regs->bp; | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		if (addr_modes.override.segment == PREFIX_DEFAULT) | 
					
						
							|  |  |  | 			addr_modes.override.segment = PREFIX_SS_; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case 7: | 
					
						
							| 
									
										
										
										
											2009-02-09 22:17:39 +09:00
										 |  |  | 		address += FPU_info->regs->bx; | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  |       add_segment: | 
					
						
							|  |  |  | 	address &= 0xffff; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 	addr->offset = address; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (addr_modes.default_mode) { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case VM86: | 
					
						
							|  |  |  | 		address += vm86_segment(addr_modes.override.segment, addr); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case PM16: | 
					
						
							|  |  |  | 	case SEG32: | 
					
						
							|  |  |  | 		address = pm_address(FPU_modrm, addr_modes.override.segment, | 
					
						
							|  |  |  | 				     addr, address); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		EXCEPTION(EX_INTERNAL | 0x131); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-01-30 13:30:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return (void __user *)address; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } |