1 | /* |
---|
2 | * intr.c: Interrupt handling for SVM. |
---|
3 | * Copyright (c) 2005, AMD 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, write to the Free Software Foundation, Inc., 59 Temple |
---|
17 | * Place - Suite 330, Boston, MA 02111-1307 USA. |
---|
18 | * |
---|
19 | */ |
---|
20 | |
---|
21 | #include <xen/config.h> |
---|
22 | #include <xen/init.h> |
---|
23 | #include <xen/mm.h> |
---|
24 | #include <xen/lib.h> |
---|
25 | #include <xen/trace.h> |
---|
26 | #include <xen/errno.h> |
---|
27 | #include <asm/cpufeature.h> |
---|
28 | #include <asm/processor.h> |
---|
29 | #include <asm/msr.h> |
---|
30 | #include <asm/paging.h> |
---|
31 | #include <asm/hvm/hvm.h> |
---|
32 | #include <asm/hvm/io.h> |
---|
33 | #include <asm/hvm/support.h> |
---|
34 | #include <asm/hvm/svm/svm.h> |
---|
35 | #include <asm/hvm/svm/intr.h> |
---|
36 | #include <xen/event.h> |
---|
37 | #include <xen/kernel.h> |
---|
38 | #include <public/hvm/ioreq.h> |
---|
39 | #include <xen/domain_page.h> |
---|
40 | #include <asm/hvm/trace.h> |
---|
41 | |
---|
42 | /* |
---|
43 | * Most of this code is copied from vmx_io.c and modified |
---|
44 | * to be suitable for SVM. |
---|
45 | */ |
---|
46 | |
---|
47 | static inline int svm_inject_extint(struct vcpu *v, int trap) |
---|
48 | { |
---|
49 | struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; |
---|
50 | vintr_t intr = vmcb->vintr; |
---|
51 | |
---|
52 | /* Update only relevant fields */ |
---|
53 | intr.fields.irq = 1; |
---|
54 | intr.fields.intr_masking = 1; |
---|
55 | intr.fields.vector = trap; |
---|
56 | intr.fields.prio = 0xF; |
---|
57 | intr.fields.ign_tpr = 1; |
---|
58 | vmcb->vintr = intr; |
---|
59 | |
---|
60 | return 0; |
---|
61 | } |
---|
62 | |
---|
63 | asmlinkage void svm_intr_assist(void) |
---|
64 | { |
---|
65 | struct vcpu *v = current; |
---|
66 | struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; |
---|
67 | int intr_type = APIC_DM_EXTINT; |
---|
68 | int intr_vector = -1; |
---|
69 | |
---|
70 | /* |
---|
71 | * Previous Interrupt delivery caused this intercept? |
---|
72 | * This will happen if the injection is latched by the processor (hence |
---|
73 | * clearing vintr.fields.irq) but then subsequently a fault occurs (e.g., |
---|
74 | * due to lack of shadow mapping of guest IDT or guest-kernel stack). |
---|
75 | * |
---|
76 | * NB. Exceptions that fault during delivery are lost. This needs to be |
---|
77 | * fixed but we'll usually get away with it since faults are usually |
---|
78 | * idempotent. But this isn't the case for e.g. software interrupts! |
---|
79 | */ |
---|
80 | if ( vmcb->exitintinfo.fields.v && (vmcb->exitintinfo.fields.type == 0) ) |
---|
81 | { |
---|
82 | intr_vector = vmcb->exitintinfo.fields.vector; |
---|
83 | vmcb->exitintinfo.bytes = 0; |
---|
84 | HVMTRACE_1D(REINJ_VIRQ, v, intr_vector); |
---|
85 | svm_inject_extint(v, intr_vector); |
---|
86 | return; |
---|
87 | } |
---|
88 | |
---|
89 | /* |
---|
90 | * Previous interrupt still pending? This occurs if we return from VMRUN |
---|
91 | * very early in the entry-to-guest process. Usually this is because an |
---|
92 | * external physical interrupt was pending when we executed VMRUN. |
---|
93 | */ |
---|
94 | if ( vmcb->vintr.fields.irq ) |
---|
95 | return; |
---|
96 | |
---|
97 | /* Crank the handle on interrupt state and check for new interrrupts. */ |
---|
98 | pt_update_irq(v); |
---|
99 | hvm_set_callback_irq_level(); |
---|
100 | if ( !cpu_has_pending_irq(v) ) |
---|
101 | return; |
---|
102 | |
---|
103 | /* |
---|
104 | * If the guest can't take an interrupt right now, create a 'fake' |
---|
105 | * virtual interrupt on to intercept as soon as the guest _can_ take |
---|
106 | * interrupts. Do not obtain the next interrupt from the vlapic/pic |
---|
107 | * if unable to inject. |
---|
108 | * |
---|
109 | * Also do this if there is an exception pending. This is because |
---|
110 | * the delivery of the exception can arbitrarily delay the injection |
---|
111 | * of the vintr (for example, if the exception is handled via an |
---|
112 | * interrupt gate, hence zeroing RFLAGS.IF). In the meantime: |
---|
113 | * - the vTPR could be modified upwards, so we need to wait until the |
---|
114 | * exception is delivered before we can safely decide that an |
---|
115 | * interrupt is deliverable; and |
---|
116 | * - the guest might look at the APIC/PIC state, so we ought not to have |
---|
117 | * cleared the interrupt out of the IRR. |
---|
118 | */ |
---|
119 | if ( irq_masked(vmcb->rflags) || vmcb->interrupt_shadow |
---|
120 | || vmcb->eventinj.fields.v ) |
---|
121 | { |
---|
122 | vmcb->general1_intercepts |= GENERAL1_INTERCEPT_VINTR; |
---|
123 | HVMTRACE_2D(INJ_VIRQ, v, 0x0, /*fake=*/ 1); |
---|
124 | svm_inject_extint(v, 0x0); /* actual vector doesn't matter */ |
---|
125 | return; |
---|
126 | } |
---|
127 | |
---|
128 | /* Okay, we can deliver the interrupt: grab it and update PIC state. */ |
---|
129 | intr_vector = cpu_get_interrupt(v, &intr_type); |
---|
130 | BUG_ON(intr_vector < 0); |
---|
131 | |
---|
132 | HVMTRACE_2D(INJ_VIRQ, v, intr_vector, /*fake=*/ 0); |
---|
133 | svm_inject_extint(v, intr_vector); |
---|
134 | |
---|
135 | pt_intr_post(v, intr_vector, intr_type); |
---|
136 | } |
---|
137 | |
---|
138 | /* |
---|
139 | * Local variables: |
---|
140 | * mode: C |
---|
141 | * c-set-style: "BSD" |
---|
142 | * c-basic-offset: 4 |
---|
143 | * tab-width: 4 |
---|
144 | * indent-tabs-mode: nil |
---|
145 | * End: |
---|
146 | */ |
---|