diff mbox

[4/6] add syscall emulation

Message ID 1245246636-30760-5-git-send-email-andre.przywara@amd.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andre Przywara June 17, 2009, 1:50 p.m. UTC
Handle #UD intercept of the syscall instruction in 32bit compat mode on
an Intel host.
Setup the segment descriptors for CS and SS and the EIP/ESP registers
according to the manual. Save the RIP and EFLAGS to the correct registers.

Signed-off-by: Christoph Egger <christoph.egger@amd.com>
Signed-off-by: Andre Przywara <andre.przywara@amd.com>
---
 arch/x86/kvm/x86_emulate.c |   89 +++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 88 insertions(+), 1 deletions(-)

Comments

Avi Kivity June 18, 2009, 8:47 a.m. UTC | #1
On 06/17/2009 04:50 PM, Andre Przywara wrote:
>
> +static inline void
> +setup_syscalls_segments(struct x86_emulate_ctxt *ctxt,
> +	struct kvm_segment *cs, struct kvm_segment *ss)
> +{
> +	memset(cs, 0, sizeof(struct kvm_segment));
> +	kvm_x86_ops->get_segment(ctxt->vcpu, cs, VCPU_SREG_CS);
> +	memset(ss, 0, sizeof(struct kvm_segment));
> +
> +	cs->l = 0;		/* will be adjusted later */
> +	cs->base = 0;		/* flat segment */
> +	cs->g = 1;		/* 4kb granularity */
> +	cs->limit = 0xfffff;	/* 4GB limit */
>    
...
> +	ss->limit = 0xfffff;	/* 4GB limit */
>    

limit in kvm_segment is expanded, so 4GB is 0xffffffff.
Andre Przywara June 18, 2009, 10:27 a.m. UTC | #2
Avi Kivity wrote:
> On 06/17/2009 04:50 PM, Andre Przywara wrote:
>>
>> +static inline void
>> +setup_syscalls_segments(struct x86_emulate_ctxt *ctxt,
>> +    struct kvm_segment *cs, struct kvm_segment *ss)
>> +{
>> +    memset(cs, 0, sizeof(struct kvm_segment));
>> +    kvm_x86_ops->get_segment(ctxt->vcpu, cs, VCPU_SREG_CS);
>> +    memset(ss, 0, sizeof(struct kvm_segment));
>> +
>> +    cs->l = 0;        /* will be adjusted later */
>> +    cs->base = 0;        /* flat segment */
>> +    cs->g = 1;        /* 4kb granularity */
>> +    cs->limit = 0xfffff;    /* 4GB limit */
>>    
> ...
>> +    ss->limit = 0xfffff;    /* 4GB limit */
>>    
> 
> limit in kvm_segment is expanded, so 4GB is 0xffffffff.
You are right. This was probably a leftover from this cross vendor 
migration g-bit issue.
Beside the fix here this removes some code in the separate paths, so I 
remade patch 4-6/6 and will resend them.

Thanks for spotting this.

Regards,
Andre.
Avi Kivity June 22, 2009, 8:43 a.m. UTC | #3
On 06/18/2009 01:27 PM, Andre Przywara wrote:
>> limit in kvm_segment is expanded, so 4GB is 0xffffffff.
>
> You are right. This was probably a leftover from this cross vendor 
> migration g-bit issue.
> Beside the fix here this removes some code in the separate paths, so I 
> remade patch 4-6/6 and will resend them.
>

Applied the new patchset; thanks.
diff mbox

Patch

diff --git a/arch/x86/kvm/x86_emulate.c b/arch/x86/kvm/x86_emulate.c
index 328ccba..89bd53e 100644
--- a/arch/x86/kvm/x86_emulate.c
+++ b/arch/x86/kvm/x86_emulate.c
@@ -1397,6 +1397,90 @@  void toggle_interruptibility(struct x86_emulate_ctxt *ctxt, u32 mask)
 		ctxt->interruptibility = mask;
 }
 
+static inline void
+setup_syscalls_segments(struct x86_emulate_ctxt *ctxt,
+	struct kvm_segment *cs, struct kvm_segment *ss)
+{
+	memset(cs, 0, sizeof(struct kvm_segment));
+	kvm_x86_ops->get_segment(ctxt->vcpu, cs, VCPU_SREG_CS);
+	memset(ss, 0, sizeof(struct kvm_segment));
+
+	cs->l = 0;		/* will be adjusted later */
+	cs->base = 0;		/* flat segment */
+	cs->g = 1;		/* 4kb granularity */
+	cs->limit = 0xfffff;	/* 4GB limit */
+	cs->type = 0x0b;	/* Read, Execute, Accessed */
+	cs->s = 1;
+	cs->dpl = 0;		/* will be adjusted later */
+	cs->present = 1;
+	cs->db = 1;
+
+	ss->unusable = 0;
+	ss->base = 0;		/* flat segment */
+	ss->limit = 0xfffff;	/* 4GB limit */
+	ss->g = 1;		/* 4kb granularity */
+	ss->s = 1;
+	ss->type = 0x03;	/* Read/Write, Accessed */
+	ss->db = 1;		/* 32bit stack segment */
+	ss->dpl = 0;
+	ss->present = 1;
+}
+
+static int
+emulate_syscall(struct x86_emulate_ctxt *ctxt)
+{
+	struct decode_cache *c = &ctxt->decode;
+	struct kvm_segment cs, ss;
+	u64 msr_data;
+
+	/* syscall is not available in real mode */
+	if (c->lock_prefix || ctxt->mode == X86EMUL_MODE_REAL
+		|| !(ctxt->vcpu->arch.cr0 & X86_CR0_PE))
+		return -1;
+
+	setup_syscalls_segments(ctxt, &cs, &ss);
+
+	kvm_x86_ops->get_msr(ctxt->vcpu, MSR_STAR, &msr_data);
+	msr_data >>= 32;
+	cs.selector = (u16)(msr_data & 0xfffc);
+	ss.selector = (u16)(msr_data + 8);
+
+	if (is_long_mode(ctxt->vcpu)) {
+		cs.db = 0;
+		cs.l = 1;
+		if (ctxt->mode == X86EMUL_MODE_PROT64) {
+			/* Intel cares about granularity (g bit),
+			 * so we don't set the effective limit.
+			 */
+			cs.g = 1;
+			cs.limit = 0xffffffff;
+		}
+	}
+	kvm_x86_ops->set_segment(ctxt->vcpu, &cs, VCPU_SREG_CS);
+	kvm_x86_ops->set_segment(ctxt->vcpu, &ss, VCPU_SREG_SS);
+
+	c->regs[VCPU_REGS_RCX] = c->eip;
+	if (is_long_mode(ctxt->vcpu)) {
+		c->regs[VCPU_REGS_R11] = ctxt->eflags & ~EFLG_RF;
+
+		kvm_x86_ops->get_msr(ctxt->vcpu,
+			ctxt->mode == X86EMUL_MODE_PROT64 ?
+			MSR_LSTAR : MSR_CSTAR, &msr_data);
+		c->eip = msr_data;
+
+		kvm_x86_ops->get_msr(ctxt->vcpu, MSR_SYSCALL_MASK, &msr_data);
+		ctxt->eflags &= ~(msr_data | EFLG_RF);
+	} else {
+		/* legacy mode */
+		kvm_x86_ops->get_msr(ctxt->vcpu, MSR_STAR, &msr_data);
+		c->eip = (u32)msr_data;
+
+		ctxt->eflags &= ~(EFLG_VM | EFLG_IF | EFLG_RF);
+	}
+
+	return 0;
+}
+
 int
 x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
 {
@@ -1993,7 +2077,10 @@  twobyte_insn:
 		}
 		break;
 	case 0x05: 		/* syscall */
-		goto cannot_emulate;
+		if (emulate_syscall(ctxt) == -1)
+			goto cannot_emulate;
+		else
+			goto writeback;
 		break;
 	case 0x06:
 		emulate_clts(ctxt->vcpu);