void handle_vm86_fault()

in kernel/vm86_32.c [548:715]


void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
{
	unsigned char opcode;
	unsigned char __user *csp;
	unsigned char __user *ssp;
	unsigned short ip, sp, orig_flags;
	int data32, pref_done;
	struct vm86plus_info_struct *vmpi = &current->thread.vm86->vm86plus;

#define CHECK_IF_IN_TRAP \
	if (vmpi->vm86dbg_active && vmpi->vm86dbg_TFpendig) \
		newflags |= X86_EFLAGS_TF

	orig_flags = *(unsigned short *)&regs->pt.flags;

	csp = (unsigned char __user *) (regs->pt.cs << 4);
	ssp = (unsigned char __user *) (regs->pt.ss << 4);
	sp = SP(regs);
	ip = IP(regs);

	data32 = 0;
	pref_done = 0;
	do {
		switch (opcode = popb(csp, ip, simulate_sigsegv)) {
		case 0x66:      /* 32-bit data */     data32 = 1; break;
		case 0x67:      /* 32-bit address */  break;
		case 0x2e:      /* CS */              break;
		case 0x3e:      /* DS */              break;
		case 0x26:      /* ES */              break;
		case 0x36:      /* SS */              break;
		case 0x65:      /* GS */              break;
		case 0x64:      /* FS */              break;
		case 0xf2:      /* repnz */       break;
		case 0xf3:      /* rep */             break;
		default: pref_done = 1;
		}
	} while (!pref_done);

	switch (opcode) {

	/* pushf */
	case 0x9c:
		if (data32) {
			pushl(ssp, sp, get_vflags(regs), simulate_sigsegv);
			SP(regs) -= 4;
		} else {
			pushw(ssp, sp, get_vflags(regs), simulate_sigsegv);
			SP(regs) -= 2;
		}
		IP(regs) = ip;
		goto vm86_fault_return;

	/* popf */
	case 0x9d:
		{
		unsigned long newflags;
		if (data32) {
			newflags = popl(ssp, sp, simulate_sigsegv);
			SP(regs) += 4;
		} else {
			newflags = popw(ssp, sp, simulate_sigsegv);
			SP(regs) += 2;
		}
		IP(regs) = ip;
		CHECK_IF_IN_TRAP;
		if (data32)
			set_vflags_long(newflags, regs);
		else
			set_vflags_short(newflags, regs);

		goto check_vip;
		}

	/* int xx */
	case 0xcd: {
		int intno = popb(csp, ip, simulate_sigsegv);
		IP(regs) = ip;
		if (vmpi->vm86dbg_active) {
			if ((1 << (intno & 7)) & vmpi->vm86dbg_intxxtab[intno >> 3]) {
				save_v86_state(regs, VM86_INTx + (intno << 8));
				return;
			}
		}
		do_int(regs, intno, ssp, sp);
		return;
	}

	/* iret */
	case 0xcf:
		{
		unsigned long newip;
		unsigned long newcs;
		unsigned long newflags;
		if (data32) {
			newip = popl(ssp, sp, simulate_sigsegv);
			newcs = popl(ssp, sp, simulate_sigsegv);
			newflags = popl(ssp, sp, simulate_sigsegv);
			SP(regs) += 12;
		} else {
			newip = popw(ssp, sp, simulate_sigsegv);
			newcs = popw(ssp, sp, simulate_sigsegv);
			newflags = popw(ssp, sp, simulate_sigsegv);
			SP(regs) += 6;
		}
		IP(regs) = newip;
		regs->pt.cs = newcs;
		CHECK_IF_IN_TRAP;
		if (data32) {
			set_vflags_long(newflags, regs);
		} else {
			set_vflags_short(newflags, regs);
		}
		goto check_vip;
		}

	/* cli */
	case 0xfa:
		IP(regs) = ip;
		clear_IF(regs);
		goto vm86_fault_return;

	/* sti */
	/*
	 * Damn. This is incorrect: the 'sti' instruction should actually
	 * enable interrupts after the /next/ instruction. Not good.
	 *
	 * Probably needs some horsing around with the TF flag. Aiee..
	 */
	case 0xfb:
		IP(regs) = ip;
		set_IF(regs);
		goto check_vip;

	default:
		save_v86_state(regs, VM86_UNKNOWN);
	}

	return;

check_vip:
	if ((VEFLAGS & (X86_EFLAGS_VIP | X86_EFLAGS_VIF)) ==
	    (X86_EFLAGS_VIP | X86_EFLAGS_VIF)) {
		save_v86_state(regs, VM86_STI);
		return;
	}

vm86_fault_return:
	if (vmpi->force_return_for_pic  && (VEFLAGS & (X86_EFLAGS_IF | X86_EFLAGS_VIF))) {
		save_v86_state(regs, VM86_PICRETURN);
		return;
	}
	if (orig_flags & X86_EFLAGS_TF)
		handle_vm86_trap(regs, 0, X86_TRAP_DB);
	return;

simulate_sigsegv:
	/* FIXME: After a long discussion with Stas we finally
	 *        agreed, that this is wrong. Here we should
	 *        really send a SIGSEGV to the user program.
	 *        But how do we create the correct context? We
	 *        are inside a general protection fault handler
	 *        and has just returned from a page fault handler.
	 *        The correct context for the signal handler
	 *        should be a mixture of the two, but how do we
	 *        get the information? [KD]
	 */
	save_v86_state(regs, VM86_UNKNOWN);
}