/root/src/xen/xen/arch/x86/hvm/svm/emulate.c
Line  | Count  | Source (jump to first uncovered line)  | 
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  |  |  | 
29  |  | static unsigned long svm_nextrip_insn_length(struct vcpu *v)  | 
30  | 0  | { | 
31  | 0  |     struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;  | 
32  | 0  | 
  | 
33  | 0  |     if ( !cpu_has_svm_nrips )  | 
34  | 0  |         return 0;  | 
35  | 0  | 
  | 
36  | 0  | #ifndef NDEBUG  | 
37  | 0  |     switch ( vmcb->exitcode )  | 
38  | 0  |     { | 
39  | 0  |     case VMEXIT_CR0_READ ... VMEXIT_DR15_WRITE:  | 
40  | 0  |         /* faults due to instruction intercepts */  | 
41  | 0  |         /* (exitcodes 84-95) are reserved */  | 
42  | 0  |     case VMEXIT_IDTR_READ ... VMEXIT_TR_WRITE:  | 
43  | 0  |     case VMEXIT_RDTSC ... VMEXIT_MSR:  | 
44  | 0  |     case VMEXIT_VMRUN ... VMEXIT_XSETBV:  | 
45  | 0  |         /* ...and the rest of the #VMEXITs */  | 
46  | 0  |     case VMEXIT_CR0_SEL_WRITE:  | 
47  | 0  |     case VMEXIT_EXCEPTION_BP:  | 
48  | 0  |         break;  | 
49  | 0  |     default:  | 
50  | 0  |         BUG();  | 
51  | 0  |     }  | 
52  | 0  | #endif  | 
53  | 0  | 
  | 
54  | 0  |     return vmcb->nextrip - vmcb->rip;  | 
55  | 0  | }  | 
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  |  |  | 
85  |  | int __get_instruction_length_from_list(struct vcpu *v,  | 
86  |  |         const enum instruction_index *list, unsigned int list_count)  | 
87  | 0  | { | 
88  | 0  |     struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;  | 
89  | 0  |     struct hvm_emulate_ctxt ctxt;  | 
90  | 0  |     struct x86_emulate_state *state;  | 
91  | 0  |     unsigned long inst_len, j;  | 
92  | 0  |     unsigned int modrm_rm, modrm_reg;  | 
93  | 0  |     int modrm_mod;  | 
94  | 0  | 
  | 
95  | 0  |     /*  | 
96  | 0  |      * In debug builds, always use x86_decode_insn() and compare with  | 
97  | 0  |      * hardware.  | 
98  | 0  |      */  | 
99  | 0  | #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  | 0  | 
  | 
109  | 0  |     ASSERT(v == current);  | 
110  | 0  |     hvm_emulate_init_once(&ctxt, NULL, guest_cpu_user_regs());  | 
111  | 0  |     hvm_emulate_init_per_insn(&ctxt, NULL, 0);  | 
112  | 0  |     state = x86_decode_insn(&ctxt.ctxt, hvmemul_insn_fetch);  | 
113  | 0  |     if ( IS_ERR_OR_NULL(state) )  | 
114  | 0  |         return 0;  | 
115  | 0  | 
  | 
116  | 0  |     inst_len = x86_insn_length(state, &ctxt.ctxt);  | 
117  | 0  |     modrm_mod = x86_insn_modrm(state, &modrm_rm, &modrm_reg);  | 
118  | 0  |     x86_emulate_free_state(state);  | 
119  | 0  | #ifndef NDEBUG  | 
120  | 0  |     if ( vmcb->exitcode == VMEXIT_IOIO )  | 
121  | 0  |         j = vmcb->exitinfo2 - vmcb->rip;  | 
122  | 0  |     else  | 
123  | 0  |         j = svm_nextrip_insn_length(v);  | 
124  | 0  |     if ( j && j != inst_len )  | 
125  | 0  |     { | 
126  | 0  |         gprintk(XENLOG_WARNING, "insn-len[%02x]=%lu (exp %lu)\n",  | 
127  | 0  |                 ctxt.ctxt.opcode, inst_len, j);  | 
128  | 0  |         return j;  | 
129  | 0  |     }  | 
130  | 0  | #endif  | 
131  | 0  | 
  | 
132  | 0  |     for ( j = 0; j < list_count; j++ )  | 
133  | 0  |     { | 
134  | 0  |         unsigned int instr = list[j];  | 
135  | 0  | 
  | 
136  | 0  |         if ( instr >= ARRAY_SIZE(opc_tab) )  | 
137  | 0  |         { | 
138  | 0  |             ASSERT_UNREACHABLE();  | 
139  | 0  |             break;  | 
140  | 0  |         }  | 
141  | 0  |         if ( opc_tab[instr].opcode == ctxt.ctxt.opcode )  | 
142  | 0  |         { | 
143  | 0  |             if ( !opc_tab[instr].modrm.mod )  | 
144  | 0  |                 return inst_len;  | 
145  | 0  | 
  | 
146  | 0  |             if ( modrm_mod == opc_tab[instr].modrm.mod &&  | 
147  | 0  |                  (modrm_rm & 7) == opc_tab[instr].modrm.rm &&  | 
148  | 0  |                  (modrm_reg & 7) == opc_tab[instr].modrm.reg )  | 
149  | 0  |                 return inst_len;  | 
150  | 0  |         }  | 
151  | 0  |     }  | 
152  | 0  | 
  | 
153  | 0  |     gdprintk(XENLOG_WARNING,  | 
154  | 0  |              "%s: Mismatch between expected and actual instruction: "  | 
155  | 0  |              "eip = %lx\n",  __func__, (unsigned long)vmcb->rip);  | 
156  | 0  |     hvm_inject_hw_exception(TRAP_gp_fault, 0);  | 
157  | 0  |     return 0;  | 
158  | 0  | }  | 
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  |  |  */  |