Skip to content

Commit e66bb2c

Browse files
aprzywaravikivity
authored andcommitted
KVM: x86 emulator: add syscall emulation
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. [avi: fix build on i386 due to missing R11] Signed-off-by: Christoph Egger <[email protected]> Signed-off-by: Andre Przywara <[email protected]> Signed-off-by: Avi Kivity <[email protected]>
1 parent e99f050 commit e66bb2c

File tree

1 file changed

+83
-1
lines changed

1 file changed

+83
-1
lines changed

arch/x86/kvm/x86_emulate.c

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,85 @@ static void toggle_interruptibility(struct x86_emulate_ctxt *ctxt, u32 mask)
13971397
ctxt->interruptibility = mask;
13981398
}
13991399

1400+
static inline void
1401+
setup_syscalls_segments(struct x86_emulate_ctxt *ctxt,
1402+
struct kvm_segment *cs, struct kvm_segment *ss)
1403+
{
1404+
memset(cs, 0, sizeof(struct kvm_segment));
1405+
kvm_x86_ops->get_segment(ctxt->vcpu, cs, VCPU_SREG_CS);
1406+
memset(ss, 0, sizeof(struct kvm_segment));
1407+
1408+
cs->l = 0; /* will be adjusted later */
1409+
cs->base = 0; /* flat segment */
1410+
cs->g = 1; /* 4kb granularity */
1411+
cs->limit = 0xffffffff; /* 4GB limit */
1412+
cs->type = 0x0b; /* Read, Execute, Accessed */
1413+
cs->s = 1;
1414+
cs->dpl = 0; /* will be adjusted later */
1415+
cs->present = 1;
1416+
cs->db = 1;
1417+
1418+
ss->unusable = 0;
1419+
ss->base = 0; /* flat segment */
1420+
ss->limit = 0xffffffff; /* 4GB limit */
1421+
ss->g = 1; /* 4kb granularity */
1422+
ss->s = 1;
1423+
ss->type = 0x03; /* Read/Write, Accessed */
1424+
ss->db = 1; /* 32bit stack segment */
1425+
ss->dpl = 0;
1426+
ss->present = 1;
1427+
}
1428+
1429+
static int
1430+
emulate_syscall(struct x86_emulate_ctxt *ctxt)
1431+
{
1432+
struct decode_cache *c = &ctxt->decode;
1433+
struct kvm_segment cs, ss;
1434+
u64 msr_data;
1435+
1436+
/* syscall is not available in real mode */
1437+
if (c->lock_prefix || ctxt->mode == X86EMUL_MODE_REAL
1438+
|| !(ctxt->vcpu->arch.cr0 & X86_CR0_PE))
1439+
return -1;
1440+
1441+
setup_syscalls_segments(ctxt, &cs, &ss);
1442+
1443+
kvm_x86_ops->get_msr(ctxt->vcpu, MSR_STAR, &msr_data);
1444+
msr_data >>= 32;
1445+
cs.selector = (u16)(msr_data & 0xfffc);
1446+
ss.selector = (u16)(msr_data + 8);
1447+
1448+
if (is_long_mode(ctxt->vcpu)) {
1449+
cs.db = 0;
1450+
cs.l = 1;
1451+
}
1452+
kvm_x86_ops->set_segment(ctxt->vcpu, &cs, VCPU_SREG_CS);
1453+
kvm_x86_ops->set_segment(ctxt->vcpu, &ss, VCPU_SREG_SS);
1454+
1455+
c->regs[VCPU_REGS_RCX] = c->eip;
1456+
if (is_long_mode(ctxt->vcpu)) {
1457+
#ifdef CONFIG_X86_64
1458+
c->regs[VCPU_REGS_R11] = ctxt->eflags & ~EFLG_RF;
1459+
1460+
kvm_x86_ops->get_msr(ctxt->vcpu,
1461+
ctxt->mode == X86EMUL_MODE_PROT64 ?
1462+
MSR_LSTAR : MSR_CSTAR, &msr_data);
1463+
c->eip = msr_data;
1464+
1465+
kvm_x86_ops->get_msr(ctxt->vcpu, MSR_SYSCALL_MASK, &msr_data);
1466+
ctxt->eflags &= ~(msr_data | EFLG_RF);
1467+
#endif
1468+
} else {
1469+
/* legacy mode */
1470+
kvm_x86_ops->get_msr(ctxt->vcpu, MSR_STAR, &msr_data);
1471+
c->eip = (u32)msr_data;
1472+
1473+
ctxt->eflags &= ~(EFLG_VM | EFLG_IF | EFLG_RF);
1474+
}
1475+
1476+
return 0;
1477+
}
1478+
14001479
int
14011480
x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
14021481
{
@@ -1993,7 +2072,10 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
19932072
}
19942073
break;
19952074
case 0x05: /* syscall */
1996-
goto cannot_emulate;
2075+
if (emulate_syscall(ctxt) == -1)
2076+
goto cannot_emulate;
2077+
else
2078+
goto writeback;
19972079
break;
19982080
case 0x06:
19992081
emulate_clts(ctxt->vcpu);

0 commit comments

Comments
 (0)