]>
Commit | Line | Data |
---|---|---|
85f455f7 ED |
1 | /* |
2 | * 8259 interrupt controller emulation | |
3 | * | |
4 | * Copyright (c) 2003-2004 Fabrice Bellard | |
5 | * Copyright (c) 2007 Intel Corporation | |
9611c187 | 6 | * Copyright 2009 Red Hat, Inc. and/or its affiliates. |
85f455f7 ED |
7 | * |
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
9 | * of this software and associated documentation files (the "Software"), to deal | |
10 | * in the Software without restriction, including without limitation the rights | |
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
12 | * copies of the Software, and to permit persons to whom the Software is | |
13 | * furnished to do so, subject to the following conditions: | |
14 | * | |
15 | * The above copyright notice and this permission notice shall be included in | |
16 | * all copies or substantial portions of the Software. | |
17 | * | |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
24 | * THE SOFTWARE. | |
25 | * Authors: | |
26 | * Yaozu (Eddie) Dong <Eddie.dong@intel.com> | |
27 | * Port from Qemu. | |
28 | */ | |
29 | #include <linux/mm.h> | |
5a0e3ad6 | 30 | #include <linux/slab.h> |
3f353858 | 31 | #include <linux/bitops.h> |
85f455f7 | 32 | #include "irq.h" |
edf88417 AK |
33 | |
34 | #include <linux/kvm_host.h> | |
1000ff8d | 35 | #include "trace.h" |
85f455f7 | 36 | |
bd80158a JK |
37 | #define pr_pic_unimpl(fmt, ...) \ |
38 | pr_err_ratelimited("kvm: pic: " fmt, ## __VA_ARGS__) | |
39 | ||
073d4613 AK |
40 | static void pic_irq_request(struct kvm *kvm, int level); |
41 | ||
50a085bd JK |
42 | static void pic_lock(struct kvm_pic *s) |
43 | __acquires(&s->lock) | |
44 | { | |
f4f51050 | 45 | spin_lock(&s->lock); |
50a085bd JK |
46 | } |
47 | ||
48 | static void pic_unlock(struct kvm_pic *s) | |
49 | __releases(&s->lock) | |
50 | { | |
51 | bool wakeup = s->wakeup_needed; | |
e21d1758 | 52 | struct kvm_vcpu *vcpu; |
529df65e | 53 | int i; |
50a085bd JK |
54 | |
55 | s->wakeup_needed = false; | |
56 | ||
f4f51050 | 57 | spin_unlock(&s->lock); |
50a085bd JK |
58 | |
59 | if (wakeup) { | |
529df65e CL |
60 | kvm_for_each_vcpu(i, vcpu, s->kvm) { |
61 | if (kvm_apic_accept_pic_intr(vcpu)) { | |
e21d1758 DH |
62 | kvm_make_request(KVM_REQ_EVENT, vcpu); |
63 | kvm_vcpu_kick(vcpu); | |
64 | return; | |
529df65e CL |
65 | } |
66 | } | |
50a085bd JK |
67 | } |
68 | } | |
69 | ||
7edd0ce0 AK |
70 | static void pic_clear_isr(struct kvm_kpic_state *s, int irq) |
71 | { | |
72 | s->isr &= ~(1 << irq); | |
938396a2 GN |
73 | if (s != &s->pics_state->pics[0]) |
74 | irq += 8; | |
eba0226b GN |
75 | /* |
76 | * We are dropping lock while calling ack notifiers since ack | |
77 | * notifier callbacks for assigned devices call into PIC recursively. | |
78 | * Other interrupt may be delivered to PIC while lock is dropped but | |
79 | * it should be safe since PIC state is already updated at this stage. | |
80 | */ | |
50a085bd | 81 | pic_unlock(s->pics_state); |
938396a2 | 82 | kvm_notify_acked_irq(s->pics_state->kvm, SELECT_PIC(irq), irq); |
50a085bd | 83 | pic_lock(s->pics_state); |
e4825800 MT |
84 | } |
85 | ||
85f455f7 ED |
86 | /* |
87 | * set irq level. If an edge is detected, then the IRR is set to 1 | |
88 | */ | |
4925663a | 89 | static inline int pic_set_irq1(struct kvm_kpic_state *s, int irq, int level) |
85f455f7 | 90 | { |
4925663a | 91 | int mask, ret = 1; |
85f455f7 ED |
92 | mask = 1 << irq; |
93 | if (s->elcr & mask) /* level triggered */ | |
94 | if (level) { | |
4925663a | 95 | ret = !(s->irr & mask); |
85f455f7 ED |
96 | s->irr |= mask; |
97 | s->last_irr |= mask; | |
98 | } else { | |
99 | s->irr &= ~mask; | |
100 | s->last_irr &= ~mask; | |
101 | } | |
102 | else /* edge triggered */ | |
103 | if (level) { | |
4925663a GN |
104 | if ((s->last_irr & mask) == 0) { |
105 | ret = !(s->irr & mask); | |
85f455f7 | 106 | s->irr |= mask; |
4925663a | 107 | } |
85f455f7 ED |
108 | s->last_irr |= mask; |
109 | } else | |
110 | s->last_irr &= ~mask; | |
4925663a GN |
111 | |
112 | return (s->imr & mask) ? -1 : ret; | |
85f455f7 ED |
113 | } |
114 | ||
115 | /* | |
116 | * return the highest priority found in mask (highest = smallest | |
117 | * number). Return 8 if no irq | |
118 | */ | |
119 | static inline int get_priority(struct kvm_kpic_state *s, int mask) | |
120 | { | |
121 | int priority; | |
122 | if (mask == 0) | |
123 | return 8; | |
124 | priority = 0; | |
125 | while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) | |
126 | priority++; | |
127 | return priority; | |
128 | } | |
129 | ||
130 | /* | |
131 | * return the pic wanted interrupt. return -1 if none | |
132 | */ | |
133 | static int pic_get_irq(struct kvm_kpic_state *s) | |
134 | { | |
135 | int mask, cur_priority, priority; | |
136 | ||
137 | mask = s->irr & ~s->imr; | |
138 | priority = get_priority(s, mask); | |
139 | if (priority == 8) | |
140 | return -1; | |
141 | /* | |
142 | * compute current priority. If special fully nested mode on the | |
143 | * master, the IRQ coming from the slave is not taken into account | |
144 | * for the priority computation. | |
145 | */ | |
146 | mask = s->isr; | |
147 | if (s->special_fully_nested_mode && s == &s->pics_state->pics[0]) | |
148 | mask &= ~(1 << 2); | |
149 | cur_priority = get_priority(s, mask); | |
150 | if (priority < cur_priority) | |
151 | /* | |
152 | * higher priority found: an irq should be generated | |
153 | */ | |
154 | return (priority + s->priority_add) & 7; | |
155 | else | |
156 | return -1; | |
157 | } | |
158 | ||
159 | /* | |
160 | * raise irq to CPU if necessary. must be called every time the active | |
161 | * irq may change | |
162 | */ | |
163 | static void pic_update_irq(struct kvm_pic *s) | |
164 | { | |
165 | int irq2, irq; | |
166 | ||
167 | irq2 = pic_get_irq(&s->pics[1]); | |
168 | if (irq2 >= 0) { | |
169 | /* | |
170 | * if irq request by slave pic, signal master PIC | |
171 | */ | |
172 | pic_set_irq1(&s->pics[0], 2, 1); | |
173 | pic_set_irq1(&s->pics[0], 2, 0); | |
174 | } | |
175 | irq = pic_get_irq(&s->pics[0]); | |
36633f32 | 176 | pic_irq_request(s->kvm, irq >= 0); |
85f455f7 ED |
177 | } |
178 | ||
6ceb9d79 HQ |
179 | void kvm_pic_update_irq(struct kvm_pic *s) |
180 | { | |
50a085bd | 181 | pic_lock(s); |
6ceb9d79 | 182 | pic_update_irq(s); |
50a085bd | 183 | pic_unlock(s); |
6ceb9d79 HQ |
184 | } |
185 | ||
1a577b72 | 186 | int kvm_pic_set_irq(struct kvm_pic *s, int irq, int irq_source_id, int level) |
85f455f7 | 187 | { |
28a6fdab MT |
188 | int ret, irq_level; |
189 | ||
190 | BUG_ON(irq < 0 || irq >= PIC_NUM_PINS); | |
85f455f7 | 191 | |
50a085bd | 192 | pic_lock(s); |
28a6fdab MT |
193 | irq_level = __kvm_irq_line_state(&s->irq_states[irq], |
194 | irq_source_id, level); | |
195 | ret = pic_set_irq1(&s->pics[irq >> 3], irq & 7, irq_level); | |
196 | pic_update_irq(s); | |
197 | trace_kvm_pic_set_irq(irq >> 3, irq & 7, s->pics[irq >> 3].elcr, | |
198 | s->pics[irq >> 3].imr, ret == 0); | |
50a085bd | 199 | pic_unlock(s); |
4925663a GN |
200 | |
201 | return ret; | |
85f455f7 ED |
202 | } |
203 | ||
1a577b72 MT |
204 | void kvm_pic_clear_all(struct kvm_pic *s, int irq_source_id) |
205 | { | |
206 | int i; | |
207 | ||
208 | pic_lock(s); | |
209 | for (i = 0; i < PIC_NUM_PINS; i++) | |
210 | __clear_bit(irq_source_id, &s->irq_states[i]); | |
211 | pic_unlock(s); | |
212 | } | |
213 | ||
85f455f7 ED |
214 | /* |
215 | * acknowledge interrupt 'irq' | |
216 | */ | |
217 | static inline void pic_intack(struct kvm_kpic_state *s, int irq) | |
218 | { | |
7edd0ce0 | 219 | s->isr |= 1 << irq; |
85f455f7 ED |
220 | /* |
221 | * We don't clear a level sensitive interrupt here | |
222 | */ | |
223 | if (!(s->elcr & (1 << irq))) | |
224 | s->irr &= ~(1 << irq); | |
eba0226b GN |
225 | |
226 | if (s->auto_eoi) { | |
227 | if (s->rotate_on_auto_eoi) | |
228 | s->priority_add = (irq + 1) & 7; | |
229 | pic_clear_isr(s, irq); | |
230 | } | |
231 | ||
85f455f7 ED |
232 | } |
233 | ||
f5244726 | 234 | int kvm_pic_read_irq(struct kvm *kvm) |
85f455f7 ED |
235 | { |
236 | int irq, irq2, intno; | |
90bca052 | 237 | struct kvm_pic *s = kvm->arch.vpic; |
85f455f7 | 238 | |
f3200d00 GN |
239 | s->output = 0; |
240 | ||
50a085bd | 241 | pic_lock(s); |
85f455f7 ED |
242 | irq = pic_get_irq(&s->pics[0]); |
243 | if (irq >= 0) { | |
244 | pic_intack(&s->pics[0], irq); | |
245 | if (irq == 2) { | |
246 | irq2 = pic_get_irq(&s->pics[1]); | |
247 | if (irq2 >= 0) | |
248 | pic_intack(&s->pics[1], irq2); | |
249 | else | |
250 | /* | |
251 | * spurious IRQ on slave controller | |
252 | */ | |
253 | irq2 = 7; | |
254 | intno = s->pics[1].irq_base + irq2; | |
255 | irq = irq2 + 8; | |
256 | } else | |
257 | intno = s->pics[0].irq_base + irq; | |
258 | } else { | |
259 | /* | |
260 | * spurious IRQ on host controller | |
261 | */ | |
262 | irq = 7; | |
263 | intno = s->pics[0].irq_base + irq; | |
264 | } | |
265 | pic_update_irq(s); | |
50a085bd | 266 | pic_unlock(s); |
85f455f7 ED |
267 | |
268 | return intno; | |
269 | } | |
270 | ||
dc24d1d2 | 271 | static void kvm_pic_reset(struct kvm_kpic_state *s) |
85f455f7 | 272 | { |
d546cb40 GN |
273 | int irq, i; |
274 | struct kvm_vcpu *vcpu; | |
ec798660 | 275 | u8 edge_irr = s->irr & ~s->elcr; |
d546cb40 | 276 | bool found = false; |
f5244726 | 277 | |
85f455f7 | 278 | s->last_irr = 0; |
ec798660 | 279 | s->irr &= s->elcr; |
85f455f7 | 280 | s->imr = 0; |
85f455f7 | 281 | s->priority_add = 0; |
85f455f7 | 282 | s->special_mask = 0; |
ec798660 GN |
283 | s->read_reg_select = 0; |
284 | if (!s->init4) { | |
285 | s->special_fully_nested_mode = 0; | |
286 | s->auto_eoi = 0; | |
287 | } | |
288 | s->init_state = 1; | |
79c727d4 | 289 | |
d546cb40 GN |
290 | kvm_for_each_vcpu(i, vcpu, s->pics_state->kvm) |
291 | if (kvm_apic_accept_pic_intr(vcpu)) { | |
292 | found = true; | |
293 | break; | |
294 | } | |
295 | ||
296 | ||
297 | if (!found) | |
298 | return; | |
299 | ||
300 | for (irq = 0; irq < PIC_NUM_PINS/2; irq++) | |
ec798660 | 301 | if (edge_irr & (1 << irq)) |
d546cb40 | 302 | pic_clear_isr(s, irq); |
85f455f7 ED |
303 | } |
304 | ||
305 | static void pic_ioport_write(void *opaque, u32 addr, u32 val) | |
306 | { | |
307 | struct kvm_kpic_state *s = opaque; | |
308 | int priority, cmd, irq; | |
309 | ||
310 | addr &= 1; | |
311 | if (addr == 0) { | |
312 | if (val & 0x10) { | |
85f455f7 ED |
313 | s->init4 = val & 1; |
314 | if (val & 0x02) | |
bd80158a | 315 | pr_pic_unimpl("single mode not supported"); |
85f455f7 | 316 | if (val & 0x08) |
bd80158a | 317 | pr_pic_unimpl( |
ec798660 GN |
318 | "level sensitive irq not supported"); |
319 | kvm_pic_reset(s); | |
85f455f7 ED |
320 | } else if (val & 0x08) { |
321 | if (val & 0x04) | |
322 | s->poll = 1; | |
323 | if (val & 0x02) | |
324 | s->read_reg_select = val & 1; | |
325 | if (val & 0x40) | |
326 | s->special_mask = (val >> 5) & 1; | |
327 | } else { | |
328 | cmd = val >> 5; | |
329 | switch (cmd) { | |
330 | case 0: | |
331 | case 4: | |
332 | s->rotate_on_auto_eoi = cmd >> 2; | |
333 | break; | |
334 | case 1: /* end of interrupt */ | |
335 | case 5: | |
336 | priority = get_priority(s, s->isr); | |
337 | if (priority != 8) { | |
338 | irq = (priority + s->priority_add) & 7; | |
85f455f7 ED |
339 | if (cmd == 5) |
340 | s->priority_add = (irq + 1) & 7; | |
eba0226b | 341 | pic_clear_isr(s, irq); |
85f455f7 ED |
342 | pic_update_irq(s->pics_state); |
343 | } | |
344 | break; | |
345 | case 3: | |
346 | irq = val & 7; | |
7edd0ce0 | 347 | pic_clear_isr(s, irq); |
85f455f7 ED |
348 | pic_update_irq(s->pics_state); |
349 | break; | |
350 | case 6: | |
351 | s->priority_add = (val + 1) & 7; | |
352 | pic_update_irq(s->pics_state); | |
353 | break; | |
354 | case 7: | |
355 | irq = val & 7; | |
85f455f7 | 356 | s->priority_add = (irq + 1) & 7; |
7edd0ce0 | 357 | pic_clear_isr(s, irq); |
85f455f7 ED |
358 | pic_update_irq(s->pics_state); |
359 | break; | |
360 | default: | |
361 | break; /* no operation */ | |
362 | } | |
363 | } | |
364 | } else | |
365 | switch (s->init_state) { | |
9195c4da GN |
366 | case 0: { /* normal mode */ |
367 | u8 imr_diff = s->imr ^ val, | |
368 | off = (s == &s->pics_state->pics[0]) ? 0 : 8; | |
85f455f7 | 369 | s->imr = val; |
9195c4da GN |
370 | for (irq = 0; irq < PIC_NUM_PINS/2; irq++) |
371 | if (imr_diff & (1 << irq)) | |
372 | kvm_fire_mask_notifiers( | |
373 | s->pics_state->kvm, | |
374 | SELECT_PIC(irq + off), | |
375 | irq + off, | |
376 | !!(s->imr & (1 << irq))); | |
85f455f7 ED |
377 | pic_update_irq(s->pics_state); |
378 | break; | |
9195c4da | 379 | } |
85f455f7 ED |
380 | case 1: |
381 | s->irq_base = val & 0xf8; | |
382 | s->init_state = 2; | |
383 | break; | |
384 | case 2: | |
385 | if (s->init4) | |
386 | s->init_state = 3; | |
387 | else | |
388 | s->init_state = 0; | |
389 | break; | |
390 | case 3: | |
391 | s->special_fully_nested_mode = (val >> 4) & 1; | |
392 | s->auto_eoi = (val >> 1) & 1; | |
393 | s->init_state = 0; | |
394 | break; | |
395 | } | |
396 | } | |
397 | ||
398 | static u32 pic_poll_read(struct kvm_kpic_state *s, u32 addr1) | |
399 | { | |
400 | int ret; | |
401 | ||
402 | ret = pic_get_irq(s); | |
403 | if (ret >= 0) { | |
404 | if (addr1 >> 7) { | |
405 | s->pics_state->pics[0].isr &= ~(1 << 2); | |
406 | s->pics_state->pics[0].irr &= ~(1 << 2); | |
407 | } | |
408 | s->irr &= ~(1 << ret); | |
7edd0ce0 | 409 | pic_clear_isr(s, ret); |
85f455f7 ED |
410 | if (addr1 >> 7 || ret != 2) |
411 | pic_update_irq(s->pics_state); | |
412 | } else { | |
413 | ret = 0x07; | |
414 | pic_update_irq(s->pics_state); | |
415 | } | |
416 | ||
417 | return ret; | |
418 | } | |
419 | ||
b5e7cf52 | 420 | static u32 pic_ioport_read(void *opaque, u32 addr) |
85f455f7 ED |
421 | { |
422 | struct kvm_kpic_state *s = opaque; | |
85f455f7 ED |
423 | int ret; |
424 | ||
85f455f7 | 425 | if (s->poll) { |
b5e7cf52 | 426 | ret = pic_poll_read(s, addr); |
85f455f7 ED |
427 | s->poll = 0; |
428 | } else | |
b5e7cf52 | 429 | if ((addr & 1) == 0) |
85f455f7 ED |
430 | if (s->read_reg_select) |
431 | ret = s->isr; | |
432 | else | |
433 | ret = s->irr; | |
434 | else | |
435 | ret = s->imr; | |
436 | return ret; | |
437 | } | |
438 | ||
439 | static void elcr_ioport_write(void *opaque, u32 addr, u32 val) | |
440 | { | |
441 | struct kvm_kpic_state *s = opaque; | |
442 | s->elcr = val & s->elcr_mask; | |
443 | } | |
444 | ||
445 | static u32 elcr_ioport_read(void *opaque, u32 addr1) | |
446 | { | |
447 | struct kvm_kpic_state *s = opaque; | |
448 | return s->elcr; | |
449 | } | |
450 | ||
743eeb0b | 451 | static int picdev_write(struct kvm_pic *s, |
85f455f7 ED |
452 | gpa_t addr, int len, const void *val) |
453 | { | |
85f455f7 ED |
454 | unsigned char data = *(unsigned char *)val; |
455 | ||
456 | if (len != 1) { | |
bd80158a | 457 | pr_pic_unimpl("non byte write\n"); |
bda9020e | 458 | return 0; |
85f455f7 ED |
459 | } |
460 | switch (addr) { | |
461 | case 0x20: | |
462 | case 0x21: | |
569d2fab MP |
463 | pic_lock(s); |
464 | pic_ioport_write(&s->pics[0], addr, data); | |
465 | pic_unlock(s); | |
466 | break; | |
85f455f7 ED |
467 | case 0xa0: |
468 | case 0xa1: | |
9fecaa9e | 469 | pic_lock(s); |
569d2fab | 470 | pic_ioport_write(&s->pics[1], addr, data); |
9fecaa9e | 471 | pic_unlock(s); |
85f455f7 ED |
472 | break; |
473 | case 0x4d0: | |
474 | case 0x4d1: | |
9fecaa9e | 475 | pic_lock(s); |
85f455f7 | 476 | elcr_ioport_write(&s->pics[addr & 1], addr, data); |
9fecaa9e | 477 | pic_unlock(s); |
85f455f7 | 478 | break; |
9fecaa9e DH |
479 | default: |
480 | return -EOPNOTSUPP; | |
85f455f7 | 481 | } |
bda9020e | 482 | return 0; |
85f455f7 ED |
483 | } |
484 | ||
743eeb0b | 485 | static int picdev_read(struct kvm_pic *s, |
bda9020e | 486 | gpa_t addr, int len, void *val) |
85f455f7 | 487 | { |
84a5c79e | 488 | unsigned char *data = (unsigned char *)val; |
85f455f7 ED |
489 | |
490 | if (len != 1) { | |
2dccb4cd | 491 | memset(val, 0, len); |
bd80158a | 492 | pr_pic_unimpl("non byte read\n"); |
bda9020e | 493 | return 0; |
85f455f7 ED |
494 | } |
495 | switch (addr) { | |
496 | case 0x20: | |
497 | case 0x21: | |
498 | case 0xa0: | |
499 | case 0xa1: | |
9fecaa9e | 500 | pic_lock(s); |
84a5c79e | 501 | *data = pic_ioport_read(&s->pics[addr >> 7], addr); |
9fecaa9e | 502 | pic_unlock(s); |
85f455f7 ED |
503 | break; |
504 | case 0x4d0: | |
505 | case 0x4d1: | |
9fecaa9e | 506 | pic_lock(s); |
84a5c79e | 507 | *data = elcr_ioport_read(&s->pics[addr & 1], addr); |
9fecaa9e | 508 | pic_unlock(s); |
85f455f7 | 509 | break; |
9fecaa9e DH |
510 | default: |
511 | return -EOPNOTSUPP; | |
85f455f7 | 512 | } |
bda9020e | 513 | return 0; |
85f455f7 ED |
514 | } |
515 | ||
e32edf4f | 516 | static int picdev_master_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, |
743eeb0b SL |
517 | gpa_t addr, int len, const void *val) |
518 | { | |
519 | return picdev_write(container_of(dev, struct kvm_pic, dev_master), | |
520 | addr, len, val); | |
521 | } | |
522 | ||
e32edf4f | 523 | static int picdev_master_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, |
743eeb0b SL |
524 | gpa_t addr, int len, void *val) |
525 | { | |
526 | return picdev_read(container_of(dev, struct kvm_pic, dev_master), | |
527 | addr, len, val); | |
528 | } | |
529 | ||
e32edf4f | 530 | static int picdev_slave_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, |
743eeb0b SL |
531 | gpa_t addr, int len, const void *val) |
532 | { | |
533 | return picdev_write(container_of(dev, struct kvm_pic, dev_slave), | |
534 | addr, len, val); | |
535 | } | |
536 | ||
e32edf4f | 537 | static int picdev_slave_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, |
743eeb0b SL |
538 | gpa_t addr, int len, void *val) |
539 | { | |
540 | return picdev_read(container_of(dev, struct kvm_pic, dev_slave), | |
541 | addr, len, val); | |
542 | } | |
543 | ||
e32edf4f | 544 | static int picdev_eclr_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, |
743eeb0b SL |
545 | gpa_t addr, int len, const void *val) |
546 | { | |
547 | return picdev_write(container_of(dev, struct kvm_pic, dev_eclr), | |
548 | addr, len, val); | |
549 | } | |
550 | ||
e32edf4f | 551 | static int picdev_eclr_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, |
743eeb0b SL |
552 | gpa_t addr, int len, void *val) |
553 | { | |
554 | return picdev_read(container_of(dev, struct kvm_pic, dev_eclr), | |
555 | addr, len, val); | |
556 | } | |
557 | ||
85f455f7 ED |
558 | /* |
559 | * callback when PIC0 irq status changed | |
560 | */ | |
073d4613 | 561 | static void pic_irq_request(struct kvm *kvm, int level) |
85f455f7 | 562 | { |
90bca052 | 563 | struct kvm_pic *s = kvm->arch.vpic; |
85f455f7 | 564 | |
7049467b | 565 | if (!s->output) |
50a085bd | 566 | s->wakeup_needed = true; |
7049467b | 567 | s->output = level; |
85f455f7 ED |
568 | } |
569 | ||
743eeb0b SL |
570 | static const struct kvm_io_device_ops picdev_master_ops = { |
571 | .read = picdev_master_read, | |
572 | .write = picdev_master_write, | |
573 | }; | |
574 | ||
575 | static const struct kvm_io_device_ops picdev_slave_ops = { | |
576 | .read = picdev_slave_read, | |
577 | .write = picdev_slave_write, | |
578 | }; | |
579 | ||
580 | static const struct kvm_io_device_ops picdev_eclr_ops = { | |
581 | .read = picdev_eclr_read, | |
582 | .write = picdev_eclr_write, | |
d76685c4 GH |
583 | }; |
584 | ||
09941366 | 585 | int kvm_pic_init(struct kvm *kvm) |
85f455f7 ED |
586 | { |
587 | struct kvm_pic *s; | |
090b7aff GH |
588 | int ret; |
589 | ||
85f455f7 ED |
590 | s = kzalloc(sizeof(struct kvm_pic), GFP_KERNEL); |
591 | if (!s) | |
09941366 | 592 | return -ENOMEM; |
f4f51050 | 593 | spin_lock_init(&s->lock); |
3f353858 | 594 | s->kvm = kvm; |
85f455f7 ED |
595 | s->pics[0].elcr_mask = 0xf8; |
596 | s->pics[1].elcr_mask = 0xde; | |
85f455f7 ED |
597 | s->pics[0].pics_state = s; |
598 | s->pics[1].pics_state = s; | |
599 | ||
600 | /* | |
601 | * Initialize PIO device | |
602 | */ | |
743eeb0b SL |
603 | kvm_iodevice_init(&s->dev_master, &picdev_master_ops); |
604 | kvm_iodevice_init(&s->dev_slave, &picdev_slave_ops); | |
605 | kvm_iodevice_init(&s->dev_eclr, &picdev_eclr_ops); | |
79fac95e | 606 | mutex_lock(&kvm->slots_lock); |
743eeb0b SL |
607 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, 0x20, 2, |
608 | &s->dev_master); | |
609 | if (ret < 0) | |
610 | goto fail_unlock; | |
611 | ||
612 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, 0xa0, 2, &s->dev_slave); | |
613 | if (ret < 0) | |
614 | goto fail_unreg_2; | |
615 | ||
616 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, 0x4d0, 2, &s->dev_eclr); | |
617 | if (ret < 0) | |
618 | goto fail_unreg_1; | |
619 | ||
79fac95e | 620 | mutex_unlock(&kvm->slots_lock); |
090b7aff | 621 | |
09941366 RK |
622 | kvm->arch.vpic = s; |
623 | ||
624 | return 0; | |
743eeb0b SL |
625 | |
626 | fail_unreg_1: | |
627 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &s->dev_slave); | |
628 | ||
629 | fail_unreg_2: | |
630 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &s->dev_master); | |
631 | ||
632 | fail_unlock: | |
633 | mutex_unlock(&kvm->slots_lock); | |
634 | ||
635 | kfree(s); | |
636 | ||
09941366 | 637 | return ret; |
85f455f7 | 638 | } |
72bb2fcd | 639 | |
09941366 | 640 | void kvm_pic_destroy(struct kvm *kvm) |
72bb2fcd | 641 | { |
09941366 RK |
642 | struct kvm_pic *vpic = kvm->arch.vpic; |
643 | ||
950712eb PX |
644 | if (!vpic) |
645 | return; | |
646 | ||
49f520b9 | 647 | mutex_lock(&kvm->slots_lock); |
71ba994c PB |
648 | kvm_io_bus_unregister_dev(vpic->kvm, KVM_PIO_BUS, &vpic->dev_master); |
649 | kvm_io_bus_unregister_dev(vpic->kvm, KVM_PIO_BUS, &vpic->dev_slave); | |
650 | kvm_io_bus_unregister_dev(vpic->kvm, KVM_PIO_BUS, &vpic->dev_eclr); | |
49f520b9 | 651 | mutex_unlock(&kvm->slots_lock); |
09941366 RK |
652 | |
653 | kvm->arch.vpic = NULL; | |
71ba994c | 654 | kfree(vpic); |
72bb2fcd | 655 | } |