diff options
-rw-r--r-- | arch/x86/kvm/emulate.c | 146 |
1 files changed, 99 insertions, 47 deletions
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 77a5f54f151..8e1d0c8196b 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -2589,6 +2589,95 @@ static int em_clts(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } +static int em_vmcall(struct x86_emulate_ctxt *ctxt) +{ + struct decode_cache *c = &ctxt->decode; + int rc; + + if (c->modrm_mod != 3 || c->modrm_rm != 1) + return X86EMUL_UNHANDLEABLE; + + rc = ctxt->ops->fix_hypercall(ctxt); + if (rc != X86EMUL_CONTINUE) + return rc; + + /* Let the processor re-execute the fixed hypercall */ + c->eip = ctxt->eip; + /* Disable writeback. */ + c->dst.type = OP_NONE; + return X86EMUL_CONTINUE; +} + +static int em_lgdt(struct x86_emulate_ctxt *ctxt) +{ + struct decode_cache *c = &ctxt->decode; + struct desc_ptr desc_ptr; + int rc; + + rc = read_descriptor(ctxt, ctxt->ops, c->src.addr.mem, + &desc_ptr.size, &desc_ptr.address, + c->op_bytes); + if (rc != X86EMUL_CONTINUE) + return rc; + ctxt->ops->set_gdt(ctxt, &desc_ptr); + /* Disable writeback. */ + c->dst.type = OP_NONE; + return X86EMUL_CONTINUE; +} + +static int em_svm(struct x86_emulate_ctxt *ctxt) +{ + struct decode_cache *c = &ctxt->decode; + int rc; + + switch (c->modrm_rm) { + case 1: + rc = ctxt->ops->fix_hypercall(ctxt); + break; + default: + return X86EMUL_UNHANDLEABLE; + } + /* Disable writeback. */ + c->dst.type = OP_NONE; + return rc; +} + +static int em_lidt(struct x86_emulate_ctxt *ctxt) +{ + struct decode_cache *c = &ctxt->decode; + struct desc_ptr desc_ptr; + int rc; + + rc = read_descriptor(ctxt, ctxt->ops, c->src.addr.mem, + &desc_ptr.size, + &desc_ptr.address, + c->op_bytes); + if (rc != X86EMUL_CONTINUE) + return rc; + ctxt->ops->set_idt(ctxt, &desc_ptr); + /* Disable writeback. */ + c->dst.type = OP_NONE; + return X86EMUL_CONTINUE; +} + +static int em_smsw(struct x86_emulate_ctxt *ctxt) +{ + struct decode_cache *c = &ctxt->decode; + + c->dst.bytes = 2; + c->dst.val = ctxt->ops->get_cr(ctxt, 0); + return X86EMUL_CONTINUE; +} + +static int em_lmsw(struct x86_emulate_ctxt *ctxt) +{ + struct decode_cache *c = &ctxt->decode; + ctxt->ops->set_cr(ctxt, 0, (ctxt->ops->get_cr(ctxt, 0) & ~0x0eul) + | (c->src.val & 0x0f)); + c->dst.type = OP_NONE; + return X86EMUL_CONTINUE; +} + static bool valid_cr(int nr) { switch (nr) { @@ -3509,7 +3598,6 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt) int rc = X86EMUL_CONTINUE; int saved_dst_type = c->dst.type; int irq; /* Used for int 3, int, and into */ - struct desc_ptr desc_ptr; ctxt->decode.mem_read.pos = 0; @@ -4022,62 +4110,26 @@ twobyte_insn: case 0x01: /* lgdt, lidt, lmsw */ switch (c->modrm_reg) { case 0: /* vmcall */ - if (c->modrm_mod != 3 || c->modrm_rm != 1) - goto cannot_emulate; - - rc = ctxt->ops->fix_hypercall(ctxt); - if (rc != X86EMUL_CONTINUE) - goto done; - - /* Let the processor re-execute the fixed hypercall */ - c->eip = ctxt->eip; - /* Disable writeback. */ - c->dst.type = OP_NONE; + rc = em_vmcall(ctxt); break; case 2: /* lgdt */ - rc = read_descriptor(ctxt, ops, c->src.addr.mem, - &desc_ptr.size, &desc_ptr.address, - c->op_bytes); - if (rc != X86EMUL_CONTINUE) - goto done; - ctxt->ops->set_gdt(ctxt, &desc_ptr); - /* Disable writeback. */ - c->dst.type = OP_NONE; + rc = em_lgdt(ctxt); break; case 3: /* lidt/vmmcall */ - if (c->modrm_mod == 3) { - switch (c->modrm_rm) { - case 1: - rc = ctxt->ops->fix_hypercall(ctxt); - break; - default: - goto cannot_emulate; - } - } else { - rc = read_descriptor(ctxt, ops, c->src.addr.mem, - &desc_ptr.size, - &desc_ptr.address, - c->op_bytes); - if (rc != X86EMUL_CONTINUE) - goto done; - ctxt->ops->set_idt(ctxt, &desc_ptr); - } - /* Disable writeback. */ - c->dst.type = OP_NONE; + if (c->modrm_mod == 3) + return em_svm(ctxt); + else + return em_lidt(ctxt); break; case 4: /* smsw */ - c->dst.bytes = 2; - c->dst.val = ops->get_cr(ctxt, 0); + rc = em_smsw(ctxt); break; case 6: /* lmsw */ - ops->set_cr(ctxt, 0, (ops->get_cr(ctxt, 0) & ~0x0eul) | - (c->src.val & 0x0f)); - c->dst.type = OP_NONE; + rc = em_lmsw(ctxt); break; case 5: /* not defined */ - emulate_ud(ctxt); - rc = X86EMUL_PROPAGATE_FAULT; - goto done; + rc = emulate_ud(ctxt); + break; case 7: /* invlpg*/ rc = em_invlpg(ctxt); break; |