/root/src/xen/xen/arch/x86/hvm/vmx/intr.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * intr.c: handling I/O, interrupts related VMX entry/exit |
3 | | * Copyright (c) 2004, Intel Corporation. |
4 | | * Copyright (c) 2004-2007, XenSource Inc. |
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 | | #include <xen/init.h> |
20 | | #include <xen/mm.h> |
21 | | #include <xen/lib.h> |
22 | | #include <xen/errno.h> |
23 | | #include <xen/trace.h> |
24 | | #include <xen/event.h> |
25 | | #include <asm/apicdef.h> |
26 | | #include <asm/current.h> |
27 | | #include <asm/cpufeature.h> |
28 | | #include <asm/processor.h> |
29 | | #include <asm/msr.h> |
30 | | #include <asm/hvm/hvm.h> |
31 | | #include <asm/hvm/io.h> |
32 | | #include <asm/hvm/support.h> |
33 | | #include <asm/hvm/vmx/vmx.h> |
34 | | #include <asm/hvm/vmx/vmcs.h> |
35 | | #include <asm/hvm/vpic.h> |
36 | | #include <asm/hvm/vlapic.h> |
37 | | #include <asm/hvm/nestedhvm.h> |
38 | | #include <public/hvm/ioreq.h> |
39 | | #include <asm/hvm/trace.h> |
40 | | |
41 | | /* |
42 | | * A few notes on virtual NMI and INTR delivery, and interactions with |
43 | | * interruptibility states: |
44 | | * |
45 | | * We can only inject an ExtInt if EFLAGS.IF = 1 and no blocking by |
46 | | * STI nor MOV SS. Otherwise the VM entry fails. The 'virtual interrupt |
47 | | * pending' control causes a VM exit when all these checks succeed. It will |
48 | | * exit immediately after VM entry if the checks succeed at that point. |
49 | | * |
50 | | * We can only inject an NMI if no blocking by MOV SS (also, depending on |
51 | | * implementation, if no blocking by STI). If pin-based 'virtual NMIs' |
52 | | * control is specified then the NMI-blocking interruptibility flag is |
53 | | * also checked. The 'virtual NMI pending' control (available only in |
54 | | * conjunction with 'virtual NMIs') causes a VM exit when all these checks |
55 | | * succeed. It will exit immediately after VM entry if the checks succeed |
56 | | * at that point. |
57 | | * |
58 | | * Because a processor may or may not check blocking-by-STI when injecting |
59 | | * a virtual NMI, it will be necessary to convert that to block-by-MOV-SS |
60 | | * before specifying the 'virtual NMI pending' control. Otherwise we could |
61 | | * enter an infinite loop where we check blocking-by-STI in software and |
62 | | * thus delay delivery of a virtual NMI, but the processor causes immediate |
63 | | * VM exit because it does not check blocking-by-STI. |
64 | | * |
65 | | * Injecting a virtual NMI sets the NMI-blocking interruptibility flag only |
66 | | * if the 'virtual NMIs' control is set. Injecting *any* kind of event clears |
67 | | * the STI- and MOV-SS-blocking interruptibility-state flags. |
68 | | */ |
69 | | |
70 | | static void vmx_enable_intr_window(struct vcpu *v, struct hvm_intack intack) |
71 | 2.02M | { |
72 | 2.02M | u32 ctl = CPU_BASED_VIRTUAL_INTR_PENDING; |
73 | 2.02M | |
74 | 2.02M | ASSERT(intack.source != hvm_intsrc_none); |
75 | 2.02M | |
76 | 2.02M | if ( unlikely(tb_init_done) ) |
77 | 0 | { |
78 | 0 | unsigned long intr; |
79 | 0 |
|
80 | 0 | __vmread(VM_ENTRY_INTR_INFO, &intr); |
81 | 0 | HVMTRACE_3D(INTR_WINDOW, intack.vector, intack.source, |
82 | 0 | (intr & INTR_INFO_VALID_MASK) ? intr & 0xff : -1); |
83 | 0 | } |
84 | 2.02M | |
85 | 2.02M | if ( (intack.source == hvm_intsrc_nmi) && cpu_has_vmx_vnmi ) |
86 | 0 | { |
87 | 0 | /* |
88 | 0 | * We set MOV-SS blocking in lieu of STI blocking when delivering an |
89 | 0 | * NMI. This is because it is processor-specific whether STI-blocking |
90 | 0 | * blocks NMIs. Hence we *must* check for STI-blocking on NMI delivery |
91 | 0 | * (otherwise vmentry will fail on processors that check for STI- |
92 | 0 | * blocking) but if the processor does not check for STI-blocking then |
93 | 0 | * we may immediately vmexit and hance make no progress! |
94 | 0 | * (see SDM 3B 21.3, "Other Causes of VM Exits"). |
95 | 0 | */ |
96 | 0 | unsigned long intr_shadow; |
97 | 0 |
|
98 | 0 | __vmread(GUEST_INTERRUPTIBILITY_INFO, &intr_shadow); |
99 | 0 | if ( intr_shadow & VMX_INTR_SHADOW_STI ) |
100 | 0 | { |
101 | 0 | /* Having both STI-blocking and MOV-SS-blocking fails vmentry. */ |
102 | 0 | intr_shadow &= ~VMX_INTR_SHADOW_STI; |
103 | 0 | intr_shadow |= VMX_INTR_SHADOW_MOV_SS; |
104 | 0 | __vmwrite(GUEST_INTERRUPTIBILITY_INFO, intr_shadow); |
105 | 0 | } |
106 | 0 | ctl = CPU_BASED_VIRTUAL_NMI_PENDING; |
107 | 0 | } |
108 | 2.02M | |
109 | 2.02M | if ( !(v->arch.hvm_vmx.exec_control & ctl) ) |
110 | 99.1k | { |
111 | 99.1k | v->arch.hvm_vmx.exec_control |= ctl; |
112 | 99.1k | vmx_update_cpu_exec_control(v); |
113 | 99.1k | } |
114 | 2.02M | } |
115 | | |
116 | | /* |
117 | | * Injecting interrupts for nested virtualization |
118 | | * |
119 | | * When injecting virtual interrupts (originated from L0), there are |
120 | | * two major possibilities, within L1 context and within L2 context |
121 | | * 1. L1 context (in_nesting == 0) |
122 | | * Everything is the same as without nested, check RFLAGS.IF to |
123 | | * see if the injection can be done, using VMCS to inject the |
124 | | * interrupt |
125 | | * |
126 | | * 2. L2 context (in_nesting == 1) |
127 | | * Causes a virtual VMExit, RFLAGS.IF is ignored, whether to ack |
128 | | * irq according to intr_ack_on_exit, shouldn't block normally, |
129 | | * except for: |
130 | | * a. context transition |
131 | | * interrupt needs to be blocked at virtual VMEntry time |
132 | | * b. L2 idtv reinjection |
133 | | * if L2 idtv is handled within L0 (e.g. L0 shadow page fault), |
134 | | * it needs to be reinjected without exiting to L1, interrupt |
135 | | * injection should be blocked as well at this point. |
136 | | * |
137 | | * Unfortunately, interrupt blocking in L2 won't work with simple |
138 | | * intr_window_open (which depends on L2's IF). To solve this, |
139 | | * the following algorithm can be used: |
140 | | * v->arch.hvm_vmx.exec_control.VIRTUAL_INTR_PENDING now denotes |
141 | | * only L0 control, physical control may be different from it. |
142 | | * - if in L1, it behaves normally, intr window is written |
143 | | * to physical control as it is |
144 | | * - if in L2, replace it to MTF (or NMI window) if possible |
145 | | * - if MTF/NMI window is not used, intr window can still be |
146 | | * used but may have negative impact on interrupt performance. |
147 | | */ |
148 | | |
149 | | enum hvm_intblk nvmx_intr_blocked(struct vcpu *v) |
150 | 2.05M | { |
151 | 2.05M | int r = hvm_intblk_none; |
152 | 2.05M | struct nestedvcpu *nvcpu = &vcpu_nestedhvm(v); |
153 | 2.05M | |
154 | 2.05M | if ( nestedhvm_vcpu_in_guestmode(v) ) |
155 | 0 | { |
156 | 0 | if ( nvcpu->nv_vmexit_pending || |
157 | 0 | nvcpu->nv_vmswitch_in_progress ) |
158 | 0 | r = hvm_intblk_rflags_ie; |
159 | 0 | else |
160 | 0 | { |
161 | 0 | unsigned long intr_info; |
162 | 0 |
|
163 | 0 | __vmread(VM_ENTRY_INTR_INFO, &intr_info); |
164 | 0 | if ( intr_info & INTR_INFO_VALID_MASK ) |
165 | 0 | r = hvm_intblk_rflags_ie; |
166 | 0 | } |
167 | 0 | } |
168 | 2.05M | else if ( nvcpu->nv_vmentry_pending ) |
169 | 0 | r = hvm_intblk_rflags_ie; |
170 | 2.05M | |
171 | 2.05M | return r; |
172 | 2.05M | } |
173 | | |
174 | | static int nvmx_intr_intercept(struct vcpu *v, struct hvm_intack intack) |
175 | 2.07M | { |
176 | 2.07M | u32 ctrl; |
177 | 2.07M | |
178 | 2.07M | /* If blocked by L1's tpr, then nothing to do. */ |
179 | 2.07M | if ( nestedhvm_vcpu_in_guestmode(v) && |
180 | 0 | hvm_interrupt_blocked(v, intack) == hvm_intblk_tpr ) |
181 | 0 | return 1; |
182 | 2.07M | |
183 | 2.07M | if ( nvmx_intr_blocked(v) != hvm_intblk_none ) |
184 | 0 | { |
185 | 0 | vmx_enable_intr_window(v, intack); |
186 | 0 | return 1; |
187 | 0 | } |
188 | 2.07M | |
189 | 2.07M | if ( nestedhvm_vcpu_in_guestmode(v) ) |
190 | 0 | { |
191 | 0 | ctrl = get_vvmcs(v, PIN_BASED_VM_EXEC_CONTROL); |
192 | 0 | if ( !(ctrl & PIN_BASED_EXT_INTR_MASK) ) |
193 | 0 | return 0; |
194 | 0 |
|
195 | 0 | if ( intack.source == hvm_intsrc_pic || |
196 | 0 | intack.source == hvm_intsrc_lapic ) |
197 | 0 | { |
198 | 0 | vmx_inject_extint(intack.vector, intack.source); |
199 | 0 |
|
200 | 0 | ctrl = get_vvmcs(v, VM_EXIT_CONTROLS); |
201 | 0 | if ( ctrl & VM_EXIT_ACK_INTR_ON_EXIT ) |
202 | 0 | { |
203 | 0 | /* for now, duplicate the ack path in vmx_intr_assist */ |
204 | 0 | hvm_vcpu_ack_pending_irq(v, intack); |
205 | 0 | pt_intr_post(v, intack); |
206 | 0 |
|
207 | 0 | intack = hvm_vcpu_has_pending_irq(v); |
208 | 0 | if ( unlikely(intack.source != hvm_intsrc_none) ) |
209 | 0 | vmx_enable_intr_window(v, intack); |
210 | 0 | } |
211 | 0 | else |
212 | 0 | vmx_enable_intr_window(v, intack); |
213 | 0 |
|
214 | 0 | return 1; |
215 | 0 | } |
216 | 0 | else if ( intack.source == hvm_intsrc_vector ) |
217 | 0 | { |
218 | 0 | vmx_inject_extint(intack.vector, intack.source); |
219 | 0 | return 1; |
220 | 0 | } |
221 | 0 | } |
222 | 2.07M | |
223 | 2.07M | return 0; |
224 | 2.07M | } |
225 | | |
226 | | void vmx_intr_assist(void) |
227 | 9.85M | { |
228 | 9.85M | struct hvm_intack intack; |
229 | 9.85M | struct vcpu *v = current; |
230 | 9.85M | unsigned int tpr_threshold = 0; |
231 | 9.85M | enum hvm_intblk intblk; |
232 | 9.85M | int pt_vector = -1; |
233 | 9.85M | |
234 | 9.85M | /* Block event injection when single step with MTF. */ |
235 | 9.85M | if ( unlikely(v->arch.hvm_vcpu.single_step) ) |
236 | 0 | { |
237 | 0 | v->arch.hvm_vmx.exec_control |= CPU_BASED_MONITOR_TRAP_FLAG; |
238 | 0 | vmx_update_cpu_exec_control(v); |
239 | 0 | return; |
240 | 0 | } |
241 | 9.85M | |
242 | 9.85M | /* Crank the handle on interrupt state. */ |
243 | 9.85M | if ( is_hvm_vcpu(v) ) |
244 | 9.83M | pt_vector = pt_update_irq(v); |
245 | 9.85M | |
246 | 9.85M | do { |
247 | 9.85M | unsigned long intr_info; |
248 | 9.85M | |
249 | 9.85M | intack = hvm_vcpu_has_pending_irq(v); |
250 | 9.85M | if ( likely(intack.source == hvm_intsrc_none) ) |
251 | 8.04M | goto out; |
252 | 9.85M | |
253 | 1.80M | if ( unlikely(nvmx_intr_intercept(v, intack)) ) |
254 | 0 | goto out; |
255 | 1.80M | |
256 | 1.80M | intblk = hvm_interrupt_blocked(v, intack); |
257 | 1.80M | if ( cpu_has_vmx_virtual_intr_delivery ) |
258 | 2.05M | { |
259 | 2.05M | /* Set "Interrupt-window exiting" for ExtINT and NMI. */ |
260 | 2.05M | if ( (intblk != hvm_intblk_none) && |
261 | 1.91M | (intack.source == hvm_intsrc_pic || |
262 | 1.92M | intack.source == hvm_intsrc_vector || |
263 | 134 | intack.source == hvm_intsrc_nmi) ) |
264 | 1.92M | { |
265 | 1.92M | vmx_enable_intr_window(v, intack); |
266 | 1.92M | goto out; |
267 | 1.92M | } |
268 | 2.05M | |
269 | 130k | __vmread(VM_ENTRY_INTR_INFO, &intr_info); |
270 | 130k | if ( intr_info & INTR_INFO_VALID_MASK ) |
271 | 43.0k | { |
272 | 43.0k | if ( (intack.source == hvm_intsrc_pic) || |
273 | 43.0k | (intack.source == hvm_intsrc_nmi) || |
274 | 43.1k | (intack.source == hvm_intsrc_mce) ) |
275 | 0 | vmx_enable_intr_window(v, intack); |
276 | 43.0k | |
277 | 43.0k | goto out; |
278 | 43.0k | } |
279 | 18.4E | } else if ( intblk == hvm_intblk_tpr ) |
280 | 0 | { |
281 | 0 | ASSERT(vlapic_enabled(vcpu_vlapic(v))); |
282 | 0 | ASSERT(intack.source == hvm_intsrc_lapic); |
283 | 0 | tpr_threshold = intack.vector >> 4; |
284 | 0 | goto out; |
285 | 0 | } |
286 | 18.4E | else if ( intblk != hvm_intblk_none ) |
287 | 0 | { |
288 | 0 | vmx_enable_intr_window(v, intack); |
289 | 0 | goto out; |
290 | 0 | } |
291 | 18.4E | else |
292 | 18.4E | { |
293 | 18.4E | __vmread(VM_ENTRY_INTR_INFO, &intr_info); |
294 | 18.4E | if ( intr_info & INTR_INFO_VALID_MASK ) |
295 | 0 | { |
296 | 0 | vmx_enable_intr_window(v, intack); |
297 | 0 | goto out; |
298 | 0 | } |
299 | 18.4E | } |
300 | 1.80M | |
301 | 18.4E | intack = hvm_vcpu_ack_pending_irq(v, intack); |
302 | 18.4E | } while ( intack.source == hvm_intsrc_none ); |
303 | 9.85M | |
304 | 18.4E | if ( intack.source == hvm_intsrc_nmi ) |
305 | 0 | { |
306 | 0 | vmx_inject_nmi(); |
307 | 0 | } |
308 | 18.4E | else if ( intack.source == hvm_intsrc_mce ) |
309 | 0 | { |
310 | 0 | hvm_inject_hw_exception(TRAP_machine_check, X86_EVENT_NO_EC); |
311 | 0 | } |
312 | 18.4E | else if ( cpu_has_vmx_virtual_intr_delivery && |
313 | 102k | intack.source != hvm_intsrc_pic && |
314 | 102k | intack.source != hvm_intsrc_vector ) |
315 | 4.21k | { |
316 | 4.21k | unsigned long status; |
317 | 4.21k | unsigned int i, n; |
318 | 4.21k | |
319 | 4.21k | /* |
320 | 4.21k | * intack.vector is the highest priority vector. So we set eoi_exit_bitmap |
321 | 4.21k | * for intack.vector - give a chance to post periodic time interrupts when |
322 | 4.21k | * periodic time interrupts become the highest one |
323 | 4.21k | */ |
324 | 4.21k | if ( pt_vector != -1 ) |
325 | 0 | { |
326 | 0 | #ifndef NDEBUG |
327 | 0 | /* |
328 | 0 | * We assert that intack.vector is the highest priority vector for |
329 | 0 | * only an interrupt from vlapic can reach this point and the |
330 | 0 | * highest vector is chosen in hvm_vcpu_has_pending_irq(). |
331 | 0 | * But, in fact, the assertion failed sometimes. It is suspected |
332 | 0 | * that PIR is not synced to vIRR which makes pt_vector is left in |
333 | 0 | * PIR. In order to verify this suspicion, dump some information |
334 | 0 | * when the assertion fails. |
335 | 0 | */ |
336 | 0 | if ( unlikely(intack.vector < pt_vector) ) |
337 | 0 | { |
338 | 0 | const struct vlapic *vlapic; |
339 | 0 | const struct pi_desc *pi_desc; |
340 | 0 | const uint32_t *word; |
341 | 0 | unsigned int i; |
342 | 0 |
|
343 | 0 | printk(XENLOG_ERR "%pv: intack: %u:%02x pt: %02x\n", |
344 | 0 | current, intack.source, intack.vector, pt_vector); |
345 | 0 |
|
346 | 0 | vlapic = vcpu_vlapic(v); |
347 | 0 | if ( vlapic && vlapic->regs ) |
348 | 0 | { |
349 | 0 | word = (const void *)&vlapic->regs->data[APIC_IRR]; |
350 | 0 | printk(XENLOG_ERR "vIRR:"); |
351 | 0 | for ( i = NR_VECTORS / 32; i-- ; ) |
352 | 0 | printk(" %08x", word[i*4]); |
353 | 0 | printk("\n"); |
354 | 0 | } |
355 | 0 |
|
356 | 0 | pi_desc = &v->arch.hvm_vmx.pi_desc; |
357 | 0 | if ( pi_desc ) |
358 | 0 | { |
359 | 0 | word = (const void *)&pi_desc->pir; |
360 | 0 | printk(XENLOG_ERR " PIR:"); |
361 | 0 | for ( i = NR_VECTORS / 32; i-- ; ) |
362 | 0 | printk(" %08x", word[i]); |
363 | 0 | printk("\n"); |
364 | 0 | } |
365 | 0 | } |
366 | 0 | #endif |
367 | 0 | ASSERT(intack.vector >= pt_vector); |
368 | 0 | vmx_set_eoi_exit_bitmap(v, intack.vector); |
369 | 0 | } |
370 | 4.21k | |
371 | 4.21k | /* we need update the RVI field */ |
372 | 4.21k | __vmread(GUEST_INTR_STATUS, &status); |
373 | 4.21k | status &= ~VMX_GUEST_INTR_STATUS_SUBFIELD_BITMASK; |
374 | 4.21k | status |= VMX_GUEST_INTR_STATUS_SUBFIELD_BITMASK & |
375 | 4.21k | intack.vector; |
376 | 4.21k | __vmwrite(GUEST_INTR_STATUS, status); |
377 | 4.21k | |
378 | 4.21k | n = ARRAY_SIZE(v->arch.hvm_vmx.eoi_exit_bitmap); |
379 | 4.21k | while ( (i = find_first_bit(&v->arch.hvm_vmx.eoi_exitmap_changed, |
380 | 4.21k | n)) < n ) |
381 | 3 | { |
382 | 3 | clear_bit(i, &v->arch.hvm_vmx.eoi_exitmap_changed); |
383 | 3 | __vmwrite(EOI_EXIT_BITMAP(i), v->arch.hvm_vmx.eoi_exit_bitmap[i]); |
384 | 3 | } |
385 | 4.21k | |
386 | 4.21k | pt_intr_post(v, intack); |
387 | 4.21k | } |
388 | 18.4E | else |
389 | 18.4E | { |
390 | 18.4E | HVMTRACE_2D(INJ_VIRQ, intack.vector, /*fake=*/ 0); |
391 | 18.4E | vmx_inject_extint(intack.vector, intack.source); |
392 | 18.4E | pt_intr_post(v, intack); |
393 | 18.4E | } |
394 | 18.4E | |
395 | 18.4E | /* Is there another IRQ to queue up behind this one? */ |
396 | 18.4E | intack = hvm_vcpu_has_pending_irq(v); |
397 | 18.4E | if ( !cpu_has_vmx_virtual_intr_delivery || |
398 | 102k | intack.source == hvm_intsrc_pic || |
399 | 102k | intack.source == hvm_intsrc_vector ) |
400 | 98.0k | { |
401 | 98.0k | if ( unlikely(intack.source != hvm_intsrc_none) ) |
402 | 98.2k | vmx_enable_intr_window(v, intack); |
403 | 98.0k | } |
404 | 18.4E | |
405 | 10.1M | out: |
406 | 10.1M | if ( !nestedhvm_vcpu_in_guestmode(v) && |
407 | 10.0M | !cpu_has_vmx_virtual_intr_delivery && |
408 | 0 | cpu_has_vmx_tpr_shadow ) |
409 | 0 | __vmwrite(TPR_THRESHOLD, tpr_threshold); |
410 | 10.1M | } |
411 | | |
412 | | /* |
413 | | * Local variables: |
414 | | * mode: C |
415 | | * c-file-style: "BSD" |
416 | | * c-basic-offset: 4 |
417 | | * tab-width: 4 |
418 | | * indent-tabs-mode: nil |
419 | | * End: |
420 | | */ |