/root/src/xen/xen/arch/x86/hvm/svm/vmcb.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * vmcb.c: VMCB management |
3 | | * Copyright (c) 2005-2007, Advanced Micro Devices, Inc. |
4 | | * Copyright (c) 2004, Intel Corporation. |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify it |
7 | | * under the terms and conditions of the GNU General Public License, |
8 | | * version 2, as published by the Free Software Foundation. |
9 | | * |
10 | | * This program is distributed in the hope it will be useful, but WITHOUT |
11 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
12 | | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
13 | | * more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public License along with |
16 | | * this program; If not, see <http://www.gnu.org/licenses/>. |
17 | | * |
18 | | */ |
19 | | |
20 | | #include <xen/init.h> |
21 | | #include <xen/lib.h> |
22 | | #include <xen/keyhandler.h> |
23 | | #include <xen/mm.h> |
24 | | #include <xen/rcupdate.h> |
25 | | #include <xen/sched.h> |
26 | | #include <asm/hvm/svm/vmcb.h> |
27 | | #include <asm/msr-index.h> |
28 | | #include <asm/p2m.h> |
29 | | #include <asm/hvm/support.h> |
30 | | #include <asm/hvm/svm/svm.h> |
31 | | #include <asm/hvm/svm/svmdebug.h> |
32 | | |
33 | | struct vmcb_struct *alloc_vmcb(void) |
34 | 0 | { |
35 | 0 | struct vmcb_struct *vmcb; |
36 | 0 |
|
37 | 0 | vmcb = alloc_xenheap_page(); |
38 | 0 | if ( vmcb == NULL ) |
39 | 0 | { |
40 | 0 | printk(XENLOG_WARNING "Warning: failed to allocate vmcb.\n"); |
41 | 0 | return NULL; |
42 | 0 | } |
43 | 0 |
|
44 | 0 | clear_page(vmcb); |
45 | 0 | return vmcb; |
46 | 0 | } |
47 | | |
48 | | void free_vmcb(struct vmcb_struct *vmcb) |
49 | 0 | { |
50 | 0 | free_xenheap_page(vmcb); |
51 | 0 | } |
52 | | |
53 | | /* This function can directly access fields which are covered by clean bits. */ |
54 | | static int construct_vmcb(struct vcpu *v) |
55 | 0 | { |
56 | 0 | struct arch_svm_struct *arch_svm = &v->arch.hvm_svm; |
57 | 0 | struct vmcb_struct *vmcb = arch_svm->vmcb; |
58 | 0 |
|
59 | 0 | /* Build-time check of the size of VMCB AMD structure. */ |
60 | 0 | BUILD_BUG_ON(sizeof(*vmcb) != PAGE_SIZE); |
61 | 0 |
|
62 | 0 | vmcb->_general1_intercepts = |
63 | 0 | GENERAL1_INTERCEPT_INTR | GENERAL1_INTERCEPT_NMI | |
64 | 0 | GENERAL1_INTERCEPT_SMI | GENERAL1_INTERCEPT_INIT | |
65 | 0 | GENERAL1_INTERCEPT_CPUID | GENERAL1_INTERCEPT_INVD | |
66 | 0 | GENERAL1_INTERCEPT_HLT | GENERAL1_INTERCEPT_INVLPG | |
67 | 0 | GENERAL1_INTERCEPT_INVLPGA | GENERAL1_INTERCEPT_IOIO_PROT | |
68 | 0 | GENERAL1_INTERCEPT_MSR_PROT | GENERAL1_INTERCEPT_SHUTDOWN_EVT| |
69 | 0 | GENERAL1_INTERCEPT_TASK_SWITCH; |
70 | 0 | vmcb->_general2_intercepts = |
71 | 0 | GENERAL2_INTERCEPT_VMRUN | GENERAL2_INTERCEPT_VMMCALL | |
72 | 0 | GENERAL2_INTERCEPT_VMLOAD | GENERAL2_INTERCEPT_VMSAVE | |
73 | 0 | GENERAL2_INTERCEPT_STGI | GENERAL2_INTERCEPT_CLGI | |
74 | 0 | GENERAL2_INTERCEPT_SKINIT | GENERAL2_INTERCEPT_MWAIT | |
75 | 0 | GENERAL2_INTERCEPT_WBINVD | GENERAL2_INTERCEPT_MONITOR | |
76 | 0 | GENERAL2_INTERCEPT_XSETBV; |
77 | 0 |
|
78 | 0 | /* Intercept all debug-register writes. */ |
79 | 0 | vmcb->_dr_intercepts = ~0u; |
80 | 0 |
|
81 | 0 | /* Intercept all control-register accesses except for CR2 and CR8. */ |
82 | 0 | vmcb->_cr_intercepts = ~(CR_INTERCEPT_CR2_READ | |
83 | 0 | CR_INTERCEPT_CR2_WRITE | |
84 | 0 | CR_INTERCEPT_CR8_READ | |
85 | 0 | CR_INTERCEPT_CR8_WRITE); |
86 | 0 |
|
87 | 0 | /* I/O and MSR permission bitmaps. */ |
88 | 0 | arch_svm->msrpm = alloc_xenheap_pages(get_order_from_bytes(MSRPM_SIZE), 0); |
89 | 0 | if ( arch_svm->msrpm == NULL ) |
90 | 0 | return -ENOMEM; |
91 | 0 | memset(arch_svm->msrpm, 0xff, MSRPM_SIZE); |
92 | 0 |
|
93 | 0 | svm_disable_intercept_for_msr(v, MSR_FS_BASE); |
94 | 0 | svm_disable_intercept_for_msr(v, MSR_GS_BASE); |
95 | 0 | svm_disable_intercept_for_msr(v, MSR_SHADOW_GS_BASE); |
96 | 0 | svm_disable_intercept_for_msr(v, MSR_CSTAR); |
97 | 0 | svm_disable_intercept_for_msr(v, MSR_LSTAR); |
98 | 0 | svm_disable_intercept_for_msr(v, MSR_STAR); |
99 | 0 | svm_disable_intercept_for_msr(v, MSR_SYSCALL_MASK); |
100 | 0 |
|
101 | 0 | /* LWP_CBADDR MSR is saved and restored by FPU code. So SVM doesn't need to |
102 | 0 | * intercept it. */ |
103 | 0 | if ( cpu_has_lwp ) |
104 | 0 | svm_disable_intercept_for_msr(v, MSR_AMD64_LWP_CBADDR); |
105 | 0 |
|
106 | 0 | vmcb->_msrpm_base_pa = (u64)virt_to_maddr(arch_svm->msrpm); |
107 | 0 | vmcb->_iopm_base_pa = __pa(v->domain->arch.hvm_domain.io_bitmap); |
108 | 0 |
|
109 | 0 | /* Virtualise EFLAGS.IF and LAPIC TPR (CR8). */ |
110 | 0 | vmcb->_vintr.fields.intr_masking = 1; |
111 | 0 | |
112 | 0 | /* Initialise event injection to no-op. */ |
113 | 0 | vmcb->eventinj.bytes = 0; |
114 | 0 |
|
115 | 0 | /* TSC. */ |
116 | 0 | vmcb->_tsc_offset = 0; |
117 | 0 |
|
118 | 0 | /* Don't need to intercept RDTSC if CPU supports TSC rate scaling */ |
119 | 0 | if ( v->domain->arch.vtsc && !cpu_has_tsc_ratio ) |
120 | 0 | { |
121 | 0 | vmcb->_general1_intercepts |= GENERAL1_INTERCEPT_RDTSC; |
122 | 0 | vmcb->_general2_intercepts |= GENERAL2_INTERCEPT_RDTSCP; |
123 | 0 | } |
124 | 0 |
|
125 | 0 | /* Guest EFER. */ |
126 | 0 | v->arch.hvm_vcpu.guest_efer = 0; |
127 | 0 | hvm_update_guest_efer(v); |
128 | 0 |
|
129 | 0 | /* Guest segment limits. */ |
130 | 0 | vmcb->cs.limit = ~0u; |
131 | 0 | vmcb->es.limit = ~0u; |
132 | 0 | vmcb->ss.limit = ~0u; |
133 | 0 | vmcb->ds.limit = ~0u; |
134 | 0 | vmcb->fs.limit = ~0u; |
135 | 0 | vmcb->gs.limit = ~0u; |
136 | 0 |
|
137 | 0 | /* Guest segment bases. */ |
138 | 0 | vmcb->cs.base = 0; |
139 | 0 | vmcb->es.base = 0; |
140 | 0 | vmcb->ss.base = 0; |
141 | 0 | vmcb->ds.base = 0; |
142 | 0 | vmcb->fs.base = 0; |
143 | 0 | vmcb->gs.base = 0; |
144 | 0 |
|
145 | 0 | /* Guest segment AR bytes. */ |
146 | 0 | vmcb->es.attr = 0xc93; /* read/write, accessed */ |
147 | 0 | vmcb->ss.attr = 0xc93; |
148 | 0 | vmcb->ds.attr = 0xc93; |
149 | 0 | vmcb->fs.attr = 0xc93; |
150 | 0 | vmcb->gs.attr = 0xc93; |
151 | 0 | vmcb->cs.attr = 0xc9b; /* exec/read, accessed */ |
152 | 0 |
|
153 | 0 | /* Guest IDT. */ |
154 | 0 | vmcb->idtr.base = 0; |
155 | 0 | vmcb->idtr.limit = 0; |
156 | 0 |
|
157 | 0 | /* Guest GDT. */ |
158 | 0 | vmcb->gdtr.base = 0; |
159 | 0 | vmcb->gdtr.limit = 0; |
160 | 0 |
|
161 | 0 | /* Guest LDT. */ |
162 | 0 | vmcb->ldtr.sel = 0; |
163 | 0 | vmcb->ldtr.base = 0; |
164 | 0 | vmcb->ldtr.limit = 0; |
165 | 0 | vmcb->ldtr.attr = 0; |
166 | 0 |
|
167 | 0 | /* Guest TSS. */ |
168 | 0 | vmcb->tr.attr = 0x08b; /* 32-bit TSS (busy) */ |
169 | 0 | vmcb->tr.base = 0; |
170 | 0 | vmcb->tr.limit = 0xff; |
171 | 0 |
|
172 | 0 | v->arch.hvm_vcpu.guest_cr[0] = X86_CR0_PE | X86_CR0_ET; |
173 | 0 | hvm_update_guest_cr(v, 0); |
174 | 0 |
|
175 | 0 | v->arch.hvm_vcpu.guest_cr[4] = 0; |
176 | 0 | hvm_update_guest_cr(v, 4); |
177 | 0 |
|
178 | 0 | paging_update_paging_modes(v); |
179 | 0 |
|
180 | 0 | vmcb->_exception_intercepts = |
181 | 0 | HVM_TRAP_MASK |
182 | 0 | | (1U << TRAP_no_device); |
183 | 0 |
|
184 | 0 | if ( paging_mode_hap(v->domain) ) |
185 | 0 | { |
186 | 0 | vmcb->_np_enable = 1; /* enable nested paging */ |
187 | 0 | vmcb->_g_pat = MSR_IA32_CR_PAT_RESET; /* guest PAT */ |
188 | 0 | vmcb->_h_cr3 = pagetable_get_paddr( |
189 | 0 | p2m_get_pagetable(p2m_get_hostp2m(v->domain))); |
190 | 0 |
|
191 | 0 | /* No point in intercepting CR3 reads/writes. */ |
192 | 0 | vmcb->_cr_intercepts &= |
193 | 0 | ~(CR_INTERCEPT_CR3_READ|CR_INTERCEPT_CR3_WRITE); |
194 | 0 |
|
195 | 0 | /* |
196 | 0 | * No point in intercepting INVLPG if we don't have shadow pagetables |
197 | 0 | * that need to be fixed up. |
198 | 0 | */ |
199 | 0 | vmcb->_general1_intercepts &= ~GENERAL1_INTERCEPT_INVLPG; |
200 | 0 |
|
201 | 0 | /* PAT is under complete control of SVM when using nested paging. */ |
202 | 0 | svm_disable_intercept_for_msr(v, MSR_IA32_CR_PAT); |
203 | 0 | } |
204 | 0 | else |
205 | 0 | { |
206 | 0 | vmcb->_exception_intercepts |= (1U << TRAP_page_fault); |
207 | 0 | } |
208 | 0 |
|
209 | 0 | if ( cpu_has_pause_filter ) |
210 | 0 | { |
211 | 0 | vmcb->_pause_filter_count = SVM_PAUSEFILTER_INIT; |
212 | 0 | vmcb->_general1_intercepts |= GENERAL1_INTERCEPT_PAUSE; |
213 | 0 | } |
214 | 0 |
|
215 | 0 | vmcb->cleanbits.bytes = 0; |
216 | 0 |
|
217 | 0 | return 0; |
218 | 0 | } |
219 | | |
220 | | int svm_create_vmcb(struct vcpu *v) |
221 | 0 | { |
222 | 0 | struct nestedvcpu *nv = &vcpu_nestedhvm(v); |
223 | 0 | struct arch_svm_struct *arch_svm = &v->arch.hvm_svm; |
224 | 0 | int rc; |
225 | 0 |
|
226 | 0 | if ( (nv->nv_n1vmcx == NULL) && |
227 | 0 | (nv->nv_n1vmcx = alloc_vmcb()) == NULL ) |
228 | 0 | { |
229 | 0 | printk("Failed to create a new VMCB\n"); |
230 | 0 | return -ENOMEM; |
231 | 0 | } |
232 | 0 |
|
233 | 0 | arch_svm->vmcb = nv->nv_n1vmcx; |
234 | 0 | rc = construct_vmcb(v); |
235 | 0 | if ( rc != 0 ) |
236 | 0 | { |
237 | 0 | free_vmcb(nv->nv_n1vmcx); |
238 | 0 | nv->nv_n1vmcx = NULL; |
239 | 0 | arch_svm->vmcb = NULL; |
240 | 0 | return rc; |
241 | 0 | } |
242 | 0 |
|
243 | 0 | arch_svm->vmcb_pa = nv->nv_n1vmcx_pa = virt_to_maddr(arch_svm->vmcb); |
244 | 0 | return 0; |
245 | 0 | } |
246 | | |
247 | | void svm_destroy_vmcb(struct vcpu *v) |
248 | 0 | { |
249 | 0 | struct nestedvcpu *nv = &vcpu_nestedhvm(v); |
250 | 0 | struct arch_svm_struct *arch_svm = &v->arch.hvm_svm; |
251 | 0 |
|
252 | 0 | if ( nv->nv_n1vmcx != NULL ) |
253 | 0 | free_vmcb(nv->nv_n1vmcx); |
254 | 0 |
|
255 | 0 | if ( arch_svm->msrpm != NULL ) |
256 | 0 | { |
257 | 0 | free_xenheap_pages( |
258 | 0 | arch_svm->msrpm, get_order_from_bytes(MSRPM_SIZE)); |
259 | 0 | arch_svm->msrpm = NULL; |
260 | 0 | } |
261 | 0 |
|
262 | 0 | nv->nv_n1vmcx = NULL; |
263 | 0 | nv->nv_n1vmcx_pa = INVALID_PADDR; |
264 | 0 | arch_svm->vmcb = NULL; |
265 | 0 | } |
266 | | |
267 | | static void vmcb_dump(unsigned char ch) |
268 | 0 | { |
269 | 0 | struct domain *d; |
270 | 0 | struct vcpu *v; |
271 | 0 | |
272 | 0 | printk("*********** VMCB Areas **************\n"); |
273 | 0 |
|
274 | 0 | rcu_read_lock(&domlist_read_lock); |
275 | 0 |
|
276 | 0 | for_each_domain ( d ) |
277 | 0 | { |
278 | 0 | if ( !is_hvm_domain(d) ) |
279 | 0 | continue; |
280 | 0 | printk("\n>>> Domain %d <<<\n", d->domain_id); |
281 | 0 | for_each_vcpu ( d, v ) |
282 | 0 | { |
283 | 0 | printk("\tVCPU %d\n", v->vcpu_id); |
284 | 0 | svm_vmcb_dump("key_handler", v->arch.hvm_svm.vmcb); |
285 | 0 | } |
286 | 0 | } |
287 | 0 |
|
288 | 0 | rcu_read_unlock(&domlist_read_lock); |
289 | 0 |
|
290 | 0 | printk("**************************************\n"); |
291 | 0 | } |
292 | | |
293 | | void __init setup_vmcb_dump(void) |
294 | 0 | { |
295 | 0 | register_keyhandler('v', vmcb_dump, "dump AMD-V VMCBs", 1); |
296 | 0 | } |
297 | | |
298 | | static void __init __maybe_unused build_assertions(void) |
299 | 0 | { |
300 | 0 | struct segment_register sreg; |
301 | 0 |
|
302 | 0 | /* Check struct segment_register against the VMCB segment layout. */ |
303 | 0 | BUILD_BUG_ON(sizeof(sreg) != 16); |
304 | 0 | BUILD_BUG_ON(sizeof(sreg.sel) != 2); |
305 | 0 | BUILD_BUG_ON(sizeof(sreg.attr) != 2); |
306 | 0 | BUILD_BUG_ON(sizeof(sreg.limit) != 4); |
307 | 0 | BUILD_BUG_ON(sizeof(sreg.base) != 8); |
308 | 0 | BUILD_BUG_ON(offsetof(struct segment_register, sel) != 0); |
309 | 0 | BUILD_BUG_ON(offsetof(struct segment_register, attr) != 2); |
310 | 0 | BUILD_BUG_ON(offsetof(struct segment_register, limit) != 4); |
311 | 0 | BUILD_BUG_ON(offsetof(struct segment_register, base) != 8); |
312 | 0 | } |
313 | | |
314 | | /* |
315 | | * Local variables: |
316 | | * mode: C |
317 | | * c-file-style: "BSD" |
318 | | * c-basic-offset: 4 |
319 | | * tab-width: 4 |
320 | | * indent-tabs-mode: nil |
321 | | * End: |
322 | | */ |