static int valid_prefix()

in math-emu/fpu_entry.c [98:539]


static int valid_prefix(u_char *Byte, u_char __user ** fpu_eip,
			overrides * override);

void math_emulate(struct math_emu_info *info)
{
	u_char FPU_modrm, byte1;
	unsigned short code;
	fpu_addr_modes addr_modes;
	int unmasked;
	FPU_REG loaded_data;
	FPU_REG *st0_ptr;
	u_char loaded_tag, st0_tag;
	void __user *data_address;
	struct address data_sel_off;
	struct address entry_sel_off;
	unsigned long code_base = 0;
	unsigned long code_limit = 0;	/* Initialized to stop compiler warnings */
	struct desc_struct code_descriptor;

#ifdef RE_ENTRANT_CHECKING
	if (emulating) {
		printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
	}
	RE_ENTRANT_CHECK_ON;
#endif /* RE_ENTRANT_CHECKING */

	FPU_info = info;

	FPU_ORIG_EIP = FPU_EIP;

	if ((FPU_EFLAGS & 0x00020000) != 0) {
		/* Virtual 8086 mode */
		addr_modes.default_mode = VM86;
		FPU_EIP += code_base = FPU_CS << 4;
		code_limit = code_base + 0xffff;	/* Assumes code_base <= 0xffff0000 */
	} else if (FPU_CS == __USER_CS && FPU_DS == __USER_DS) {
		addr_modes.default_mode = 0;
	} else if (FPU_CS == __KERNEL_CS) {
		printk("math_emulate: %04x:%08lx\n", FPU_CS, FPU_EIP);
		panic("Math emulation needed in kernel");
	} else {

		if ((FPU_CS & 4) != 4) {	/* Must be in the LDT */
			/* Can only handle segmented addressing via the LDT
			   for now, and it must be 16 bit */
			printk("FPU emulator: Unsupported addressing mode\n");
			math_abort(FPU_info, SIGILL);
		}

		code_descriptor = FPU_get_ldt_descriptor(FPU_CS);
		if (code_descriptor.d) {
			/* The above test may be wrong, the book is not clear */
			/* Segmented 32 bit protected mode */
			addr_modes.default_mode = SEG32;
		} else {
			/* 16 bit protected mode */
			addr_modes.default_mode = PM16;
		}
		FPU_EIP += code_base = seg_get_base(&code_descriptor);
		code_limit = seg_get_limit(&code_descriptor) + 1;
		code_limit *= seg_get_granularity(&code_descriptor);
		code_limit += code_base - 1;
		if (code_limit < code_base)
			code_limit = 0xffffffff;
	}

	FPU_lookahead = !(FPU_EFLAGS & X86_EFLAGS_TF);

	if (!valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
			  &addr_modes.override)) {
		RE_ENTRANT_CHECK_OFF;
		printk
		    ("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n"
		     "FPU emulator: self-modifying code! (emulation impossible)\n",
		     byte1);
		RE_ENTRANT_CHECK_ON;
		EXCEPTION(EX_INTERNAL | 0x126);
		math_abort(FPU_info, SIGILL);
	}

      do_another_FPU_instruction:

	no_ip_update = 0;

	FPU_EIP++;		/* We have fetched the prefix and first code bytes. */

	if (addr_modes.default_mode) {
		/* This checks for the minimum instruction bytes.
		   We also need to check any extra (address mode) code access. */
		if (FPU_EIP > code_limit)
			math_abort(FPU_info, SIGSEGV);
	}

	if ((byte1 & 0xf8) != 0xd8) {
		if (byte1 == FWAIT_OPCODE) {
			if (partial_status & SW_Summary)
				goto do_the_FPU_interrupt;
			else
				goto FPU_fwait_done;
		}
#ifdef PARANOID
		EXCEPTION(EX_INTERNAL | 0x128);
		math_abort(FPU_info, SIGILL);
#endif /* PARANOID */
	}

	RE_ENTRANT_CHECK_OFF;
	FPU_code_access_ok(1);
	FPU_get_user(FPU_modrm, (u_char __user *) FPU_EIP);
	RE_ENTRANT_CHECK_ON;
	FPU_EIP++;

	if (partial_status & SW_Summary) {
		/* Ignore the error for now if the current instruction is a no-wait
		   control instruction */
		/* The 80486 manual contradicts itself on this topic,
		   but a real 80486 uses the following instructions:
		   fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
		 */
		code = (FPU_modrm << 8) | byte1;
		if (!((((code & 0xf803) == 0xe003) ||	/* fnclex, fninit, fnstsw */
		       (((code & 0x3003) == 0x3001) &&	/* fnsave, fnstcw, fnstenv,
							   fnstsw */
			((code & 0xc000) != 0xc000))))) {
			/*
			 *  We need to simulate the action of the kernel to FPU
			 *  interrupts here.
			 */
		      do_the_FPU_interrupt:

			FPU_EIP = FPU_ORIG_EIP;	/* Point to current FPU instruction. */

			RE_ENTRANT_CHECK_OFF;
			current->thread.trap_nr = X86_TRAP_MF;
			current->thread.error_code = 0;
			send_sig(SIGFPE, current, 1);
			return;
		}
	}

	entry_sel_off.offset = FPU_ORIG_EIP;
	entry_sel_off.selector = FPU_CS;
	entry_sel_off.opcode = (byte1 << 8) | FPU_modrm;
	entry_sel_off.empty = 0;

	FPU_rm = FPU_modrm & 7;

	if (FPU_modrm < 0300) {
		/* All of these instructions use the mod/rm byte to get a data address */

		if ((addr_modes.default_mode & SIXTEEN)
		    ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX))
			data_address =
			    FPU_get_address_16(FPU_modrm, &FPU_EIP,
					       &data_sel_off, addr_modes);
		else
			data_address =
			    FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
					    addr_modes);

		if (addr_modes.default_mode) {
			if (FPU_EIP - 1 > code_limit)
				math_abort(FPU_info, SIGSEGV);
		}

		if (!(byte1 & 1)) {
			unsigned short status1 = partial_status;

			st0_ptr = &st(0);
			st0_tag = FPU_gettag0();

			/* Stack underflow has priority */
			if (NOT_EMPTY_ST0) {
				if (addr_modes.default_mode & PROTECTED) {
					/* This table works for 16 and 32 bit protected mode */
					if (access_limit <
					    data_sizes_16[(byte1 >> 1) & 3])
						math_abort(FPU_info, SIGSEGV);
				}

				unmasked = 0;	/* Do this here to stop compiler warnings. */
				switch ((byte1 >> 1) & 3) {
				case 0:
					unmasked =
					    FPU_load_single((float __user *)
							    data_address,
							    &loaded_data);
					loaded_tag = unmasked & 0xff;
					unmasked &= ~0xff;
					break;
				case 1:
					loaded_tag =
					    FPU_load_int32((long __user *)
							   data_address,
							   &loaded_data);
					break;
				case 2:
					unmasked =
					    FPU_load_double((double __user *)
							    data_address,
							    &loaded_data);
					loaded_tag = unmasked & 0xff;
					unmasked &= ~0xff;
					break;
				case 3:
				default:	/* Used here to suppress gcc warnings. */
					loaded_tag =
					    FPU_load_int16((short __user *)
							   data_address,
							   &loaded_data);
					break;
				}

				/* No more access to user memory, it is safe
				   to use static data now */

				/* NaN operands have the next priority. */
				/* We have to delay looking at st(0) until after
				   loading the data, because that data might contain an SNaN */
				if (((st0_tag == TAG_Special) && isNaN(st0_ptr))
				    || ((loaded_tag == TAG_Special)
					&& isNaN(&loaded_data))) {
					/* Restore the status word; we might have loaded a
					   denormal. */
					partial_status = status1;
					if ((FPU_modrm & 0x30) == 0x10) {
						/* fcom or fcomp */
						EXCEPTION(EX_Invalid);
						setcc(SW_C3 | SW_C2 | SW_C0);
						if ((FPU_modrm & 0x08)
						    && (control_word &
							CW_Invalid))
							FPU_pop();	/* fcomp, masked, so we pop. */
					} else {
						if (loaded_tag == TAG_Special)
							loaded_tag =
							    FPU_Special
							    (&loaded_data);
#ifdef PECULIAR_486
						/* This is not really needed, but gives behaviour
						   identical to an 80486 */
						if ((FPU_modrm & 0x28) == 0x20)
							/* fdiv or fsub */
							real_2op_NaN
							    (&loaded_data,
							     loaded_tag, 0,
							     &loaded_data);
						else
#endif /* PECULIAR_486 */
							/* fadd, fdivr, fmul, or fsubr */
							real_2op_NaN
							    (&loaded_data,
							     loaded_tag, 0,
							     st0_ptr);
					}
					goto reg_mem_instr_done;
				}

				if (unmasked && !((FPU_modrm & 0x30) == 0x10)) {
					/* Is not a comparison instruction. */
					if ((FPU_modrm & 0x38) == 0x38) {
						/* fdivr */
						if ((st0_tag == TAG_Zero) &&
						    ((loaded_tag == TAG_Valid)
						     || (loaded_tag ==
							 TAG_Special
							 &&
							 isdenormal
							 (&loaded_data)))) {
							if (FPU_divide_by_zero
							    (0,
							     getsign
							     (&loaded_data))
							    < 0) {
								/* We use the fact here that the unmasked
								   exception in the loaded data was for a
								   denormal operand */
								/* Restore the state of the denormal op bit */
								partial_status
								    &=
								    ~SW_Denorm_Op;
								partial_status
								    |=
								    status1 &
								    SW_Denorm_Op;
							} else
								setsign(st0_ptr,
									getsign
									(&loaded_data));
						}
					}
					goto reg_mem_instr_done;
				}

				switch ((FPU_modrm >> 3) & 7) {
				case 0:	/* fadd */
					clear_C1();
					FPU_add(&loaded_data, loaded_tag, 0,
						control_word);
					break;
				case 1:	/* fmul */
					clear_C1();
					FPU_mul(&loaded_data, loaded_tag, 0,
						control_word);
					break;
				case 2:	/* fcom */
					FPU_compare_st_data(&loaded_data,
							    loaded_tag);
					break;
				case 3:	/* fcomp */
					if (!FPU_compare_st_data
					    (&loaded_data, loaded_tag)
					    && !unmasked)
						FPU_pop();
					break;
				case 4:	/* fsub */
					clear_C1();
					FPU_sub(LOADED | loaded_tag,
						(int)&loaded_data,
						control_word);
					break;
				case 5:	/* fsubr */
					clear_C1();
					FPU_sub(REV | LOADED | loaded_tag,
						(int)&loaded_data,
						control_word);
					break;
				case 6:	/* fdiv */
					clear_C1();
					FPU_div(LOADED | loaded_tag,
						(int)&loaded_data,
						control_word);
					break;
				case 7:	/* fdivr */
					clear_C1();
					if (st0_tag == TAG_Zero)
						partial_status = status1;	/* Undo any denorm tag,
										   zero-divide has priority. */
					FPU_div(REV | LOADED | loaded_tag,
						(int)&loaded_data,
						control_word);
					break;
				}
			} else {
				if ((FPU_modrm & 0x30) == 0x10) {
					/* The instruction is fcom or fcomp */
					EXCEPTION(EX_StackUnder);
					setcc(SW_C3 | SW_C2 | SW_C0);
					if ((FPU_modrm & 0x08)
					    && (control_word & CW_Invalid))
						FPU_pop();	/* fcomp */
				} else
					FPU_stack_underflow();
			}
		      reg_mem_instr_done:
			operand_address = data_sel_off;
		} else {
			if (!(no_ip_update =
			      FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6))
					     >> 1, addr_modes, data_address))) {
				operand_address = data_sel_off;
			}
		}

	} else {
		/* None of these instructions access user memory */
		u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);

#ifdef PECULIAR_486
		/* This is supposed to be undefined, but a real 80486 seems
		   to do this: */
		operand_address.offset = 0;
		operand_address.selector = FPU_DS;
#endif /* PECULIAR_486 */

		st0_ptr = &st(0);
		st0_tag = FPU_gettag0();
		switch (type_table[(int)instr_index]) {
		case _NONE_:	/* also _REGIc: _REGIn */
			break;
		case _REG0_:
			if (!NOT_EMPTY_ST0) {
				FPU_stack_underflow();
				goto FPU_instruction_done;
			}
			break;
		case _REGIi:
			if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
				FPU_stack_underflow_i(FPU_rm);
				goto FPU_instruction_done;
			}
			break;
		case _REGIp:
			if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
				FPU_stack_underflow_pop(FPU_rm);
				goto FPU_instruction_done;
			}
			break;
		case _REGI_:
			if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
				FPU_stack_underflow();
				goto FPU_instruction_done;
			}
			break;
		case _PUSH_:	/* Only used by the fld st(i) instruction */
			break;
		case _null_:
			FPU_illegal();
			goto FPU_instruction_done;
		default:
			EXCEPTION(EX_INTERNAL | 0x111);
			goto FPU_instruction_done;
		}
		(*st_instr_table[(int)instr_index]) ();

	      FPU_instruction_done:
		;
	}

	if (!no_ip_update)
		instruction_address = entry_sel_off;

      FPU_fwait_done:

#ifdef DEBUG
	RE_ENTRANT_CHECK_OFF;
	FPU_printall();
	RE_ENTRANT_CHECK_ON;
#endif /* DEBUG */

	if (FPU_lookahead && !need_resched()) {
		FPU_ORIG_EIP = FPU_EIP - code_base;
		if (valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
				 &addr_modes.override))
			goto do_another_FPU_instruction;
	}

	if (addr_modes.default_mode)
		FPU_EIP -= code_base;

	RE_ENTRANT_CHECK_OFF;
}