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