summaryrefslogtreecommitdiffstats
path: root/arch/x86/kvm/emulate.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kvm/emulate.c')
-rw-r--r--arch/x86/kvm/emulate.c146
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;