diff options
author | Sanjay Lal <sanjayl@kymasys.com> | 2012-11-21 21:34:08 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2013-05-07 21:55:36 -0400 |
commit | 06d1838db012eca7df42dc09f8190ab8f8a9f9eb (patch) | |
tree | 99de0c83f0ba40a8323accd6c963cec1b145eb41 /arch/mips | |
parent | 3c20ef526253da02348ec45768d5a90813577d88 (diff) |
KVM/MIPS32: Guest interrupt delivery.
Signed-off-by: Sanjay Lal <sanjayl@kymasys.com>
Cc: kvm@vger.kernel.org
Cc: linux-mips@linux-mips.org
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/kvm/kvm_mips_int.c | 243 | ||||
-rw-r--r-- | arch/mips/kvm/kvm_mips_int.h | 49 |
2 files changed, 292 insertions, 0 deletions
diff --git a/arch/mips/kvm/kvm_mips_int.c b/arch/mips/kvm/kvm_mips_int.c new file mode 100644 index 000000000000..1e5de16afe29 --- /dev/null +++ b/arch/mips/kvm/kvm_mips_int.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * KVM/MIPS: Interrupt delivery | ||
7 | * | ||
8 | * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. | ||
9 | * Authors: Sanjay Lal <sanjayl@kymasys.com> | ||
10 | */ | ||
11 | |||
12 | #include <linux/errno.h> | ||
13 | #include <linux/err.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/vmalloc.h> | ||
16 | #include <linux/fs.h> | ||
17 | #include <linux/bootmem.h> | ||
18 | #include <asm/page.h> | ||
19 | #include <asm/cacheflush.h> | ||
20 | |||
21 | #include <linux/kvm_host.h> | ||
22 | |||
23 | #include "kvm_mips_int.h" | ||
24 | |||
25 | void kvm_mips_queue_irq(struct kvm_vcpu *vcpu, uint32_t priority) | ||
26 | { | ||
27 | set_bit(priority, &vcpu->arch.pending_exceptions); | ||
28 | } | ||
29 | |||
30 | void kvm_mips_dequeue_irq(struct kvm_vcpu *vcpu, uint32_t priority) | ||
31 | { | ||
32 | clear_bit(priority, &vcpu->arch.pending_exceptions); | ||
33 | } | ||
34 | |||
35 | void kvm_mips_queue_timer_int_cb(struct kvm_vcpu *vcpu) | ||
36 | { | ||
37 | /* Cause bits to reflect the pending timer interrupt, | ||
38 | * the EXC code will be set when we are actually | ||
39 | * delivering the interrupt: | ||
40 | */ | ||
41 | kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ5 | C_TI)); | ||
42 | |||
43 | /* Queue up an INT exception for the core */ | ||
44 | kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_TIMER); | ||
45 | |||
46 | } | ||
47 | |||
48 | void kvm_mips_dequeue_timer_int_cb(struct kvm_vcpu *vcpu) | ||
49 | { | ||
50 | kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ5 | C_TI)); | ||
51 | kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_TIMER); | ||
52 | } | ||
53 | |||
54 | void | ||
55 | kvm_mips_queue_io_int_cb(struct kvm_vcpu *vcpu, struct kvm_mips_interrupt *irq) | ||
56 | { | ||
57 | int intr = (int)irq->irq; | ||
58 | |||
59 | /* Cause bits to reflect the pending IO interrupt, | ||
60 | * the EXC code will be set when we are actually | ||
61 | * delivering the interrupt: | ||
62 | */ | ||
63 | switch (intr) { | ||
64 | case 2: | ||
65 | kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ0)); | ||
66 | /* Queue up an INT exception for the core */ | ||
67 | kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_IO); | ||
68 | break; | ||
69 | |||
70 | case 3: | ||
71 | kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ1)); | ||
72 | kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_IPI_1); | ||
73 | break; | ||
74 | |||
75 | case 4: | ||
76 | kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ2)); | ||
77 | kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_IPI_2); | ||
78 | break; | ||
79 | |||
80 | default: | ||
81 | break; | ||
82 | } | ||
83 | |||
84 | } | ||
85 | |||
86 | void | ||
87 | kvm_mips_dequeue_io_int_cb(struct kvm_vcpu *vcpu, | ||
88 | struct kvm_mips_interrupt *irq) | ||
89 | { | ||
90 | int intr = (int)irq->irq; | ||
91 | switch (intr) { | ||
92 | case -2: | ||
93 | kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ0)); | ||
94 | kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_IO); | ||
95 | break; | ||
96 | |||
97 | case -3: | ||
98 | kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ1)); | ||
99 | kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_IPI_1); | ||
100 | break; | ||
101 | |||
102 | case -4: | ||
103 | kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ2)); | ||
104 | kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_IPI_2); | ||
105 | break; | ||
106 | |||
107 | default: | ||
108 | break; | ||
109 | } | ||
110 | |||
111 | } | ||
112 | |||
113 | /* Deliver the interrupt of the corresponding priority, if possible. */ | ||
114 | int | ||
115 | kvm_mips_irq_deliver_cb(struct kvm_vcpu *vcpu, unsigned int priority, | ||
116 | uint32_t cause) | ||
117 | { | ||
118 | int allowed = 0; | ||
119 | uint32_t exccode; | ||
120 | |||
121 | struct kvm_vcpu_arch *arch = &vcpu->arch; | ||
122 | struct mips_coproc *cop0 = vcpu->arch.cop0; | ||
123 | |||
124 | switch (priority) { | ||
125 | case MIPS_EXC_INT_TIMER: | ||
126 | if ((kvm_read_c0_guest_status(cop0) & ST0_IE) | ||
127 | && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL))) | ||
128 | && (kvm_read_c0_guest_status(cop0) & IE_IRQ5)) { | ||
129 | allowed = 1; | ||
130 | exccode = T_INT; | ||
131 | } | ||
132 | break; | ||
133 | |||
134 | case MIPS_EXC_INT_IO: | ||
135 | if ((kvm_read_c0_guest_status(cop0) & ST0_IE) | ||
136 | && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL))) | ||
137 | && (kvm_read_c0_guest_status(cop0) & IE_IRQ0)) { | ||
138 | allowed = 1; | ||
139 | exccode = T_INT; | ||
140 | } | ||
141 | break; | ||
142 | |||
143 | case MIPS_EXC_INT_IPI_1: | ||
144 | if ((kvm_read_c0_guest_status(cop0) & ST0_IE) | ||
145 | && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL))) | ||
146 | && (kvm_read_c0_guest_status(cop0) & IE_IRQ1)) { | ||
147 | allowed = 1; | ||
148 | exccode = T_INT; | ||
149 | } | ||
150 | break; | ||
151 | |||
152 | case MIPS_EXC_INT_IPI_2: | ||
153 | if ((kvm_read_c0_guest_status(cop0) & ST0_IE) | ||
154 | && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL))) | ||
155 | && (kvm_read_c0_guest_status(cop0) & IE_IRQ2)) { | ||
156 | allowed = 1; | ||
157 | exccode = T_INT; | ||
158 | } | ||
159 | break; | ||
160 | |||
161 | default: | ||
162 | break; | ||
163 | } | ||
164 | |||
165 | /* Are we allowed to deliver the interrupt ??? */ | ||
166 | if (allowed) { | ||
167 | |||
168 | if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { | ||
169 | /* save old pc */ | ||
170 | kvm_write_c0_guest_epc(cop0, arch->pc); | ||
171 | kvm_set_c0_guest_status(cop0, ST0_EXL); | ||
172 | |||
173 | if (cause & CAUSEF_BD) | ||
174 | kvm_set_c0_guest_cause(cop0, CAUSEF_BD); | ||
175 | else | ||
176 | kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); | ||
177 | |||
178 | kvm_debug("Delivering INT @ pc %#lx\n", arch->pc); | ||
179 | |||
180 | } else | ||
181 | kvm_err("Trying to deliver interrupt when EXL is already set\n"); | ||
182 | |||
183 | kvm_change_c0_guest_cause(cop0, CAUSEF_EXCCODE, | ||
184 | (exccode << CAUSEB_EXCCODE)); | ||
185 | |||
186 | /* XXXSL Set PC to the interrupt exception entry point */ | ||
187 | if (kvm_read_c0_guest_cause(cop0) & CAUSEF_IV) | ||
188 | arch->pc = KVM_GUEST_KSEG0 + 0x200; | ||
189 | else | ||
190 | arch->pc = KVM_GUEST_KSEG0 + 0x180; | ||
191 | |||
192 | clear_bit(priority, &vcpu->arch.pending_exceptions); | ||
193 | } | ||
194 | |||
195 | return allowed; | ||
196 | } | ||
197 | |||
198 | int | ||
199 | kvm_mips_irq_clear_cb(struct kvm_vcpu *vcpu, unsigned int priority, | ||
200 | uint32_t cause) | ||
201 | { | ||
202 | return 1; | ||
203 | } | ||
204 | |||
205 | void kvm_mips_deliver_interrupts(struct kvm_vcpu *vcpu, uint32_t cause) | ||
206 | { | ||
207 | unsigned long *pending = &vcpu->arch.pending_exceptions; | ||
208 | unsigned long *pending_clr = &vcpu->arch.pending_exceptions_clr; | ||
209 | unsigned int priority; | ||
210 | |||
211 | if (!(*pending) && !(*pending_clr)) | ||
212 | return; | ||
213 | |||
214 | priority = __ffs(*pending_clr); | ||
215 | while (priority <= MIPS_EXC_MAX) { | ||
216 | if (kvm_mips_callbacks->irq_clear(vcpu, priority, cause)) { | ||
217 | if (!KVM_MIPS_IRQ_CLEAR_ALL_AT_ONCE) | ||
218 | break; | ||
219 | } | ||
220 | |||
221 | priority = find_next_bit(pending_clr, | ||
222 | BITS_PER_BYTE * sizeof(*pending_clr), | ||
223 | priority + 1); | ||
224 | } | ||
225 | |||
226 | priority = __ffs(*pending); | ||
227 | while (priority <= MIPS_EXC_MAX) { | ||
228 | if (kvm_mips_callbacks->irq_deliver(vcpu, priority, cause)) { | ||
229 | if (!KVM_MIPS_IRQ_DELIVER_ALL_AT_ONCE) | ||
230 | break; | ||
231 | } | ||
232 | |||
233 | priority = find_next_bit(pending, | ||
234 | BITS_PER_BYTE * sizeof(*pending), | ||
235 | priority + 1); | ||
236 | } | ||
237 | |||
238 | } | ||
239 | |||
240 | int kvm_mips_pending_timer(struct kvm_vcpu *vcpu) | ||
241 | { | ||
242 | return test_bit(MIPS_EXC_INT_TIMER, &vcpu->arch.pending_exceptions); | ||
243 | } | ||
diff --git a/arch/mips/kvm/kvm_mips_int.h b/arch/mips/kvm/kvm_mips_int.h new file mode 100644 index 000000000000..20da7d29eede --- /dev/null +++ b/arch/mips/kvm/kvm_mips_int.h | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * KVM/MIPS: Interrupts | ||
7 | * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. | ||
8 | * Authors: Sanjay Lal <sanjayl@kymasys.com> | ||
9 | */ | ||
10 | |||
11 | /* MIPS Exception Priorities, exceptions (including interrupts) are queued up | ||
12 | * for the guest in the order specified by their priorities | ||
13 | */ | ||
14 | |||
15 | #define MIPS_EXC_RESET 0 | ||
16 | #define MIPS_EXC_SRESET 1 | ||
17 | #define MIPS_EXC_DEBUG_ST 2 | ||
18 | #define MIPS_EXC_DEBUG 3 | ||
19 | #define MIPS_EXC_DDB 4 | ||
20 | #define MIPS_EXC_NMI 5 | ||
21 | #define MIPS_EXC_MCHK 6 | ||
22 | #define MIPS_EXC_INT_TIMER 7 | ||
23 | #define MIPS_EXC_INT_IO 8 | ||
24 | #define MIPS_EXC_EXECUTE 9 | ||
25 | #define MIPS_EXC_INT_IPI_1 10 | ||
26 | #define MIPS_EXC_INT_IPI_2 11 | ||
27 | #define MIPS_EXC_MAX 12 | ||
28 | /* XXXSL More to follow */ | ||
29 | |||
30 | #define C_TI (_ULCAST_(1) << 30) | ||
31 | |||
32 | #define KVM_MIPS_IRQ_DELIVER_ALL_AT_ONCE (0) | ||
33 | #define KVM_MIPS_IRQ_CLEAR_ALL_AT_ONCE (0) | ||
34 | |||
35 | void kvm_mips_queue_irq(struct kvm_vcpu *vcpu, uint32_t priority); | ||
36 | void kvm_mips_dequeue_irq(struct kvm_vcpu *vcpu, uint32_t priority); | ||
37 | int kvm_mips_pending_timer(struct kvm_vcpu *vcpu); | ||
38 | |||
39 | void kvm_mips_queue_timer_int_cb(struct kvm_vcpu *vcpu); | ||
40 | void kvm_mips_dequeue_timer_int_cb(struct kvm_vcpu *vcpu); | ||
41 | void kvm_mips_queue_io_int_cb(struct kvm_vcpu *vcpu, | ||
42 | struct kvm_mips_interrupt *irq); | ||
43 | void kvm_mips_dequeue_io_int_cb(struct kvm_vcpu *vcpu, | ||
44 | struct kvm_mips_interrupt *irq); | ||
45 | int kvm_mips_irq_deliver_cb(struct kvm_vcpu *vcpu, unsigned int priority, | ||
46 | uint32_t cause); | ||
47 | int kvm_mips_irq_clear_cb(struct kvm_vcpu *vcpu, unsigned int priority, | ||
48 | uint32_t cause); | ||
49 | void kvm_mips_deliver_interrupts(struct kvm_vcpu *vcpu, uint32_t cause); | ||