Commit 1c73ef66 authored by Avi Kivity's avatar Avi Kivity
Browse files

KVM: x86 emulator: Hoist modrm and abs decoding into separate functions



Signed-off-by: default avatarAvi Kivity <avi@qumranet.com>
parent 3b6fff19
Loading
Loading
Loading
Loading
+177 −160
Original line number Diff line number Diff line
@@ -548,122 +548,20 @@ static void decode_register_operand(struct operand *op,
	op->orig_val = op->val;
}

int
x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
static int decode_modrm(struct x86_emulate_ctxt *ctxt,
			struct x86_emulate_ops *ops)
{
	struct decode_cache *c = &ctxt->decode;
	u8 sib;
	int rc = 0;
	int mode = ctxt->mode;
	int index_reg = 0, base_reg = 0, scale, rip_relative = 0;
	int rc = 0;

	/* Shadow copy of register state. Committed on successful emulation. */

	memset(c, 0, sizeof(struct decode_cache));
	c->eip = ctxt->vcpu->rip;
	memcpy(c->regs, ctxt->vcpu->regs, sizeof c->regs);

	switch (mode) {
	case X86EMUL_MODE_REAL:
	case X86EMUL_MODE_PROT16:
		c->op_bytes = c->ad_bytes = 2;
		break;
	case X86EMUL_MODE_PROT32:
		c->op_bytes = c->ad_bytes = 4;
		break;
#ifdef CONFIG_X86_64
	case X86EMUL_MODE_PROT64:
		c->op_bytes = 4;
		c->ad_bytes = 8;
		break;
#endif
	default:
		return -1;
	}

	/* Legacy prefixes. */
	for (;;) {
		switch (c->b = insn_fetch(u8, 1, c->eip)) {
		case 0x66:	/* operand-size override */
			c->op_bytes ^= 6;	/* switch between 2/4 bytes */
			break;
		case 0x67:	/* address-size override */
			if (mode == X86EMUL_MODE_PROT64)
				/* switch between 4/8 bytes */
				c->ad_bytes ^= 12;
			else
				/* switch between 2/4 bytes */
				c->ad_bytes ^= 6;
			break;
		case 0x2e:	/* CS override */
			c->override_base = &ctxt->cs_base;
			break;
		case 0x3e:	/* DS override */
			c->override_base = &ctxt->ds_base;
			break;
		case 0x26:	/* ES override */
			c->override_base = &ctxt->es_base;
			break;
		case 0x64:	/* FS override */
			c->override_base = &ctxt->fs_base;
			break;
		case 0x65:	/* GS override */
			c->override_base = &ctxt->gs_base;
			break;
		case 0x36:	/* SS override */
			c->override_base = &ctxt->ss_base;
			break;
		case 0x40 ... 0x4f: /* REX */
			if (mode != X86EMUL_MODE_PROT64)
				goto done_prefixes;
			c->rex_prefix = c->b;
			continue;
		case 0xf0:	/* LOCK */
			c->lock_prefix = 1;
			break;
		case 0xf2:	/* REPNE/REPNZ */
		case 0xf3:	/* REP/REPE/REPZ */
			c->rep_prefix = 1;
			break;
		default:
			goto done_prefixes;
		}

		/* Any legacy prefix after a REX prefix nullifies its effect. */

		c->rex_prefix = 0;
	}

done_prefixes:

	/* REX prefix. */
	if (c->rex_prefix) {
		if (c->rex_prefix & 8)
			c->op_bytes = 8;	/* REX.W */
		c->modrm_reg = (c->rex_prefix & 4) << 1;	/* REX.R */
		index_reg = (c->rex_prefix & 2) << 2; /* REX.X */
		c->modrm_rm = base_reg = (c->rex_prefix & 1) << 3; /* REG.B */
	}

	/* Opcode byte(s). */
	c->d = opcode_table[c->b];
	if (c->d == 0) {
		/* Two-byte opcode? */
		if (c->b == 0x0f) {
			c->twobyte = 1;
			c->b = insn_fetch(u8, 1, c->eip);
			c->d = twobyte_table[c->b];
		}

		/* Unrecognised? */
		if (c->d == 0) {
			DPRINTF("Cannot emulate %02x\n", c->b);
			return -1;
		}
	}

	/* ModRM and SIB bytes. */
	if (c->d & ModRM) {
	c->modrm = insn_fetch(u8, 1, c->eip);
	c->modrm_mod |= (c->modrm & 0xc0) >> 6;
	c->modrm_reg |= (c->modrm & 0x38) >> 3;
@@ -674,7 +572,7 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
	if (c->modrm_mod == 3) {
		c->modrm_val = *(unsigned long *)
			decode_register(c->modrm_rm, c->regs, c->d & ByteOp);
			goto modrm_done;
		return rc;
	}

	if (c->ad_bytes == 2) {
@@ -687,8 +585,7 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
		switch (c->modrm_mod) {
		case 0:
			if (c->modrm_rm == 6)
					c->modrm_ea +=
						insn_fetch(u16, 2, c->eip);
				c->modrm_ea += insn_fetch(u16, 2, c->eip);
			break;
		case 1:
			c->modrm_ea += insn_fetch(s8, 1, c->eip);
@@ -742,8 +639,7 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
			switch (base_reg) {
			case 5:
				if (c->modrm_mod != 0)
						c->modrm_ea +=
							c->regs[base_reg];
					c->modrm_ea += c->regs[base_reg];
				else
					c->modrm_ea +=
						insn_fetch(s32, 4, c->eip);
@@ -755,15 +651,13 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
			case 4:
				break;
			default:
					c->modrm_ea +=
						c->regs[index_reg] << scale;

				c->modrm_ea += c->regs[index_reg] << scale;
			}
			break;
		case 5:
			if (c->modrm_mod != 0)
				c->modrm_ea += c->regs[c->modrm_rm];
				else if (mode == X86EMUL_MODE_PROT64)
			else if (ctxt->mode == X86EMUL_MODE_PROT64)
				rip_relative = 1;
			break;
		default:
@@ -773,8 +667,7 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
		switch (c->modrm_mod) {
		case 0:
			if (c->modrm_rm == 5)
					c->modrm_ea +=
						insn_fetch(s32, 4, c->eip);
				c->modrm_ea += insn_fetch(s32, 4, c->eip);
			break;
		case 1:
			c->modrm_ea += insn_fetch(s8, 1, c->eip);
@@ -800,9 +693,16 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
					c->modrm_ea += c->op_bytes;
		}
	}
modrm_done:
		;
	} else if (c->d & MemAbs) {
done:
	return rc;
}

static int decode_abs(struct x86_emulate_ctxt *ctxt,
		      struct x86_emulate_ops *ops)
{
	struct decode_cache *c = &ctxt->decode;
	int rc = 0;

	switch (c->ad_bytes) {
	case 2:
		c->modrm_ea = insn_fetch(u16, 2, c->eip);
@@ -814,9 +714,126 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
		c->modrm_ea = insn_fetch(u64, 8, c->eip);
		break;
	}
done:
	return rc;
}

int
x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
{
	struct decode_cache *c = &ctxt->decode;
	int rc = 0;
	int mode = ctxt->mode;

	/* Shadow copy of register state. Committed on successful emulation. */

	memset(c, 0, sizeof(struct decode_cache));
	c->eip = ctxt->vcpu->rip;
	memcpy(c->regs, ctxt->vcpu->regs, sizeof c->regs);

	switch (mode) {
	case X86EMUL_MODE_REAL:
	case X86EMUL_MODE_PROT16:
		c->op_bytes = c->ad_bytes = 2;
		break;
	case X86EMUL_MODE_PROT32:
		c->op_bytes = c->ad_bytes = 4;
		break;
#ifdef CONFIG_X86_64
	case X86EMUL_MODE_PROT64:
		c->op_bytes = 4;
		c->ad_bytes = 8;
		break;
#endif
	default:
		return -1;
	}

	/* Legacy prefixes. */
	for (;;) {
		switch (c->b = insn_fetch(u8, 1, c->eip)) {
		case 0x66:	/* operand-size override */
			c->op_bytes ^= 6;	/* switch between 2/4 bytes */
			break;
		case 0x67:	/* address-size override */
			if (mode == X86EMUL_MODE_PROT64)
				/* switch between 4/8 bytes */
				c->ad_bytes ^= 12;
			else
				/* switch between 2/4 bytes */
				c->ad_bytes ^= 6;
			break;
		case 0x2e:	/* CS override */
			c->override_base = &ctxt->cs_base;
			break;
		case 0x3e:	/* DS override */
			c->override_base = &ctxt->ds_base;
			break;
		case 0x26:	/* ES override */
			c->override_base = &ctxt->es_base;
			break;
		case 0x64:	/* FS override */
			c->override_base = &ctxt->fs_base;
			break;
		case 0x65:	/* GS override */
			c->override_base = &ctxt->gs_base;
			break;
		case 0x36:	/* SS override */
			c->override_base = &ctxt->ss_base;
			break;
		case 0x40 ... 0x4f: /* REX */
			if (mode != X86EMUL_MODE_PROT64)
				goto done_prefixes;
			c->rex_prefix = c->b;
			continue;
		case 0xf0:	/* LOCK */
			c->lock_prefix = 1;
			break;
		case 0xf2:	/* REPNE/REPNZ */
		case 0xf3:	/* REP/REPE/REPZ */
			c->rep_prefix = 1;
			break;
		default:
			goto done_prefixes;
		}

		/* Any legacy prefix after a REX prefix nullifies its effect. */

		c->rex_prefix = 0;
	}

done_prefixes:

	/* REX prefix. */
	if (c->rex_prefix)
		if (c->rex_prefix & 8)
			c->op_bytes = 8;	/* REX.W */

	/* Opcode byte(s). */
	c->d = opcode_table[c->b];
	if (c->d == 0) {
		/* Two-byte opcode? */
		if (c->b == 0x0f) {
			c->twobyte = 1;
			c->b = insn_fetch(u8, 1, c->eip);
			c->d = twobyte_table[c->b];
		}

		/* Unrecognised? */
		if (c->d == 0) {
			DPRINTF("Cannot emulate %02x\n", c->b);
			return -1;
		}
	}

	/* ModRM and SIB bytes. */
	if (c->d & ModRM)
		rc = decode_modrm(ctxt, ops);
	else if (c->d & MemAbs)
		rc = decode_abs(ctxt, ops);
	if (rc)
		goto done;

	if (!c->override_base)
		c->override_base = &ctxt->ds_base;
	if (mode == X86EMUL_MODE_PROT64 &&