1 /*
2 * emulate.c: handling SVM emulate instructions help.
3 * Copyright (c) 2005 AMD Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <xen/err.h>
19 #include <xen/init.h>
20 #include <xen/lib.h>
21 #include <xen/trace.h>
22 #include <asm/msr.h>
23 #include <asm/hvm/hvm.h>
24 #include <asm/hvm/support.h>
25 #include <asm/hvm/svm/svm.h>
26 #include <asm/hvm/svm/vmcb.h>
27 #include <asm/hvm/svm/emulate.h>
28
svm_nextrip_insn_length(struct vcpu * v)29 static unsigned long svm_nextrip_insn_length(struct vcpu *v)
30 {
31 struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
32
33 if ( !cpu_has_svm_nrips )
34 return 0;
35
36 #ifndef NDEBUG
37 switch ( vmcb->exitcode )
38 {
39 case VMEXIT_CR0_READ ... VMEXIT_DR15_WRITE:
40 /* faults due to instruction intercepts */
41 /* (exitcodes 84-95) are reserved */
42 case VMEXIT_IDTR_READ ... VMEXIT_TR_WRITE:
43 case VMEXIT_RDTSC ... VMEXIT_MSR:
44 case VMEXIT_VMRUN ... VMEXIT_XSETBV:
45 /* ...and the rest of the #VMEXITs */
46 case VMEXIT_CR0_SEL_WRITE:
47 case VMEXIT_EXCEPTION_BP:
48 break;
49 default:
50 BUG();
51 }
52 #endif
53
54 return vmcb->nextrip - vmcb->rip;
55 }
56
57 static const struct {
58 unsigned int opcode;
59 struct {
60 unsigned int rm:3;
61 unsigned int reg:3;
62 unsigned int mod:2;
63 #define MODRM(mod, reg, rm) { rm, reg, mod }
64 } modrm;
65 } opc_tab[INSTR_MAX_COUNT] = {
66 [INSTR_PAUSE] = { X86EMUL_OPC_F3(0, 0x90) },
67 [INSTR_INT3] = { X86EMUL_OPC( 0, 0xcc) },
68 [INSTR_HLT] = { X86EMUL_OPC( 0, 0xf4) },
69 [INSTR_XSETBV] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 2, 1) },
70 [INSTR_VMRUN] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 0) },
71 [INSTR_VMCALL] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 1) },
72 [INSTR_VMLOAD] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 2) },
73 [INSTR_VMSAVE] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 3) },
74 [INSTR_STGI] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 4) },
75 [INSTR_CLGI] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 5) },
76 [INSTR_INVLPGA] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 7) },
77 [INSTR_INVD] = { X86EMUL_OPC(0x0f, 0x08) },
78 [INSTR_WBINVD] = { X86EMUL_OPC(0x0f, 0x09) },
79 [INSTR_WRMSR] = { X86EMUL_OPC(0x0f, 0x30) },
80 [INSTR_RDTSC] = { X86EMUL_OPC(0x0f, 0x31) },
81 [INSTR_RDMSR] = { X86EMUL_OPC(0x0f, 0x32) },
82 [INSTR_CPUID] = { X86EMUL_OPC(0x0f, 0xa2) },
83 };
84
__get_instruction_length_from_list(struct vcpu * v,const enum instruction_index * list,unsigned int list_count)85 int __get_instruction_length_from_list(struct vcpu *v,
86 const enum instruction_index *list, unsigned int list_count)
87 {
88 struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
89 struct hvm_emulate_ctxt ctxt;
90 struct x86_emulate_state *state;
91 unsigned long inst_len, j;
92 unsigned int modrm_rm, modrm_reg;
93 int modrm_mod;
94
95 /*
96 * In debug builds, always use x86_decode_insn() and compare with
97 * hardware.
98 */
99 #ifdef NDEBUG
100 if ( (inst_len = svm_nextrip_insn_length(v)) > MAX_INST_LEN )
101 gprintk(XENLOG_WARNING, "NRip reported inst_len %lu\n", inst_len);
102 else if ( inst_len != 0 )
103 return inst_len;
104
105 if ( vmcb->exitcode == VMEXIT_IOIO )
106 return vmcb->exitinfo2 - vmcb->rip;
107 #endif
108
109 ASSERT(v == current);
110 hvm_emulate_init_once(&ctxt, NULL, guest_cpu_user_regs());
111 hvm_emulate_init_per_insn(&ctxt, NULL, 0);
112 state = x86_decode_insn(&ctxt.ctxt, hvmemul_insn_fetch);
113 if ( IS_ERR_OR_NULL(state) )
114 return 0;
115
116 inst_len = x86_insn_length(state, &ctxt.ctxt);
117 modrm_mod = x86_insn_modrm(state, &modrm_rm, &modrm_reg);
118 x86_emulate_free_state(state);
119 #ifndef NDEBUG
120 if ( vmcb->exitcode == VMEXIT_IOIO )
121 j = vmcb->exitinfo2 - vmcb->rip;
122 else
123 j = svm_nextrip_insn_length(v);
124 if ( j && j != inst_len )
125 {
126 gprintk(XENLOG_WARNING, "insn-len[%02x]=%lu (exp %lu)\n",
127 ctxt.ctxt.opcode, inst_len, j);
128 return j;
129 }
130 #endif
131
132 for ( j = 0; j < list_count; j++ )
133 {
134 unsigned int instr = list[j];
135
136 if ( instr >= ARRAY_SIZE(opc_tab) )
137 {
138 ASSERT_UNREACHABLE();
139 break;
140 }
141 if ( opc_tab[instr].opcode == ctxt.ctxt.opcode )
142 {
143 if ( !opc_tab[instr].modrm.mod )
144 return inst_len;
145
146 if ( modrm_mod == opc_tab[instr].modrm.mod &&
147 (modrm_rm & 7) == opc_tab[instr].modrm.rm &&
148 (modrm_reg & 7) == opc_tab[instr].modrm.reg )
149 return inst_len;
150 }
151 }
152
153 gdprintk(XENLOG_WARNING,
154 "%s: Mismatch between expected and actual instruction: "
155 "eip = %lx\n", __func__, (unsigned long)vmcb->rip);
156 hvm_inject_hw_exception(TRAP_gp_fault, 0);
157 return 0;
158 }
159
160 /*
161 * Local variables:
162 * mode: C
163 * c-file-style: "BSD"
164 * c-basic-offset: 4
165 * tab-width: 4
166 * indent-tabs-mode: nil
167 * End:
168 */
169