xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/vatpic.c (revision 7c8c0b8227679b4684566e408ccc96d6ef7175e9)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /*
29  * This file and its contents are supplied under the terms of the
30  * Common Development and Distribution License ("CDDL"), version 1.0.
31  * You may only use this file in accordance with the terms of version
32  * 1.0 of the CDDL.
33  *
34  * A full copy of the text of the CDDL should have accompanied this
35  * source.  A copy of the CDDL is also available via the Internet at
36  * http://www.illumos.org/license/CDDL.
37  *
38  * Copyright 2021 Oxide Computer Company
39  */
40 
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43 
44 #include <sys/param.h>
45 #include <sys/types.h>
46 #include <sys/queue.h>
47 #include <sys/kernel.h>
48 #include <sys/malloc.h>
49 #include <sys/mutex.h>
50 #include <sys/systm.h>
51 
52 #include <x86/apicreg.h>
53 #include <dev/ic/i8259.h>
54 
55 #include <machine/vmm.h>
56 
57 #include "vmm_ktr.h"
58 #include "vmm_lapic.h"
59 #include "vioapic.h"
60 #include "vatpic.h"
61 
62 static MALLOC_DEFINE(M_VATPIC, "atpic", "bhyve virtual atpic (8259)");
63 
64 #define	VATPIC_LOCK(vatpic)		mutex_enter(&((vatpic)->lock))
65 #define	VATPIC_UNLOCK(vatpic)		mutex_exit(&((vatpic)->lock))
66 #define	VATPIC_LOCKED(vatpic)		MUTEX_HELD(&((vatpic)->lock))
67 
68 #define	IRQ_BASE_MASK	0xf8
69 
70 enum irqstate {
71 	IRQSTATE_ASSERT,
72 	IRQSTATE_DEASSERT,
73 	IRQSTATE_PULSE
74 };
75 
76 enum icw_state {
77 	IS_ICW1 = 0,
78 	IS_ICW2,
79 	IS_ICW3,
80 	IS_ICW4,
81 };
82 
83 struct atpic {
84 	enum icw_state	icw_state;
85 
86 	bool		ready;
87 	bool		auto_eoi;
88 	bool		poll;
89 	bool		rotate;
90 	bool		special_full_nested;
91 	bool		read_isr_next;
92 	bool		intr_raised;
93 	bool		special_mask_mode;
94 
95 	uint8_t		reg_irr;	/* Interrupt Request Register (IIR) */
96 	uint8_t		reg_isr;	/* Interrupt Service (ISR) */
97 	uint8_t		reg_imr;	/* Interrupt Mask Register (IMR) */
98 	uint8_t		irq_base;	/* base interrupt vector */
99 	uint8_t		lowprio;	/* lowest priority irq */
100 	uint8_t		elc;		/* level-triggered mode bits */
101 
102 	uint_t		acnt[8];	/* sum of pin asserts and deasserts */
103 };
104 
105 struct atpic_stats {
106 	uint64_t	as_interrupts;
107 	uint64_t	as_saturate_low;
108 	uint64_t	as_saturate_high;
109 };
110 
111 struct vatpic {
112 	struct vm	*vm;
113 	kmutex_t	lock;
114 	struct atpic	atpic[2];
115 	struct atpic_stats stats;
116 };
117 
118 #define	VATPIC_CTR0(vatpic, fmt)					\
119 	VM_CTR0((vatpic)->vm, fmt)
120 
121 #define	VATPIC_CTR1(vatpic, fmt, a1)					\
122 	VM_CTR1((vatpic)->vm, fmt, a1)
123 
124 #define	VATPIC_CTR2(vatpic, fmt, a1, a2)				\
125 	VM_CTR2((vatpic)->vm, fmt, a1, a2)
126 
127 #define	VATPIC_CTR3(vatpic, fmt, a1, a2, a3)				\
128 	VM_CTR3((vatpic)->vm, fmt, a1, a2, a3)
129 
130 #define	VATPIC_CTR4(vatpic, fmt, a1, a2, a3, a4)			\
131 	VM_CTR4((vatpic)->vm, fmt, a1, a2, a3, a4)
132 
133 /*
134  * Loop over all the pins in priority order from highest to lowest.
135  */
136 #define	ATPIC_PIN_FOREACH(pinvar, atpic, tmpvar)			\
137 	for (tmpvar = 0, pinvar = (atpic->lowprio + 1) & 0x7;		\
138 	    tmpvar < 8;							\
139 	    tmpvar++, pinvar = (pinvar + 1) & 0x7)
140 
141 static int vatpic_set_pinstate(struct vatpic *vatpic, int pin, bool newstate);
142 
143 static __inline bool
144 master_atpic(struct vatpic *vatpic, struct atpic *atpic)
145 {
146 
147 	if (atpic == &vatpic->atpic[0])
148 		return (true);
149 	else
150 		return (false);
151 }
152 
153 static __inline int
154 vatpic_get_highest_isrpin(struct atpic *atpic)
155 {
156 	int bit, pin;
157 	int i;
158 
159 	ATPIC_PIN_FOREACH(pin, atpic, i) {
160 		bit = (1 << pin);
161 
162 		if (atpic->reg_isr & bit) {
163 			/*
164 			 * An IS bit that is masked by an IMR bit will not be
165 			 * cleared by a non-specific EOI in Special Mask Mode.
166 			 */
167 			if (atpic->special_mask_mode &&
168 			    (atpic->reg_imr & bit) != 0) {
169 				continue;
170 			} else {
171 				return (pin);
172 			}
173 		}
174 	}
175 
176 	return (-1);
177 }
178 
179 static __inline int
180 vatpic_get_highest_irrpin(struct atpic *atpic)
181 {
182 	int serviced;
183 	int bit, pin, tmp;
184 
185 	/*
186 	 * In 'Special Fully-Nested Mode' when an interrupt request from
187 	 * a slave is in service, the slave is not locked out from the
188 	 * master's priority logic.
189 	 */
190 	serviced = atpic->reg_isr;
191 	if (atpic->special_full_nested)
192 		serviced &= ~(1 << 2);
193 
194 	/*
195 	 * In 'Special Mask Mode', when a mask bit is set in OCW1 it inhibits
196 	 * further interrupts at that level and enables interrupts from all
197 	 * other levels that are not masked. In other words the ISR has no
198 	 * bearing on the levels that can generate interrupts.
199 	 */
200 	if (atpic->special_mask_mode)
201 		serviced = 0;
202 
203 	ATPIC_PIN_FOREACH(pin, atpic, tmp) {
204 		bit = 1 << pin;
205 
206 		/*
207 		 * If there is already an interrupt in service at the same
208 		 * or higher priority then bail.
209 		 */
210 		if ((serviced & bit) != 0)
211 			break;
212 
213 		/*
214 		 * If an interrupt is asserted and not masked then return
215 		 * the corresponding 'pin' to the caller.
216 		 */
217 		if ((atpic->reg_irr & bit) != 0 && (atpic->reg_imr & bit) == 0)
218 			return (pin);
219 	}
220 
221 	return (-1);
222 }
223 
224 static void
225 vatpic_notify_intr(struct vatpic *vatpic)
226 {
227 	struct atpic *atpic;
228 	int pin;
229 
230 	ASSERT(VATPIC_LOCKED(vatpic));
231 
232 	/*
233 	 * First check the slave.
234 	 */
235 	atpic = &vatpic->atpic[1];
236 	if (!atpic->intr_raised &&
237 	    (pin = vatpic_get_highest_irrpin(atpic)) != -1) {
238 		VATPIC_CTR4(vatpic, "atpic slave notify pin = %d "
239 		    "(imr 0x%x irr 0x%x isr 0x%x)", pin,
240 		    atpic->reg_imr, atpic->reg_irr, atpic->reg_isr);
241 
242 		/*
243 		 * Cascade the request from the slave to the master.
244 		 */
245 		atpic->intr_raised = true;
246 		if (vatpic_set_pinstate(vatpic, 2, true) == 0) {
247 			(void) vatpic_set_pinstate(vatpic, 2, false);
248 		}
249 	} else {
250 		VATPIC_CTR3(vatpic, "atpic slave no eligible interrupts "
251 		    "(imr 0x%x irr 0x%x isr 0x%x)",
252 		    atpic->reg_imr, atpic->reg_irr, atpic->reg_isr);
253 	}
254 
255 	/*
256 	 * Then check the master.
257 	 */
258 	atpic = &vatpic->atpic[0];
259 	if (!atpic->intr_raised &&
260 	    (pin = vatpic_get_highest_irrpin(atpic)) != -1) {
261 		VATPIC_CTR4(vatpic, "atpic master notify pin = %d "
262 		    "(imr 0x%x irr 0x%x isr 0x%x)", pin,
263 		    atpic->reg_imr, atpic->reg_irr, atpic->reg_isr);
264 
265 		/*
266 		 * From Section 3.6.2, "Interrupt Modes", in the
267 		 * MPtable Specification, Version 1.4
268 		 *
269 		 * PIC interrupts are routed to both the Local APIC
270 		 * and the I/O APIC to support operation in 1 of 3
271 		 * modes.
272 		 *
273 		 * 1. Legacy PIC Mode: the PIC effectively bypasses
274 		 * all APIC components.  In this mode the local APIC is
275 		 * disabled and LINT0 is reconfigured as INTR to
276 		 * deliver the PIC interrupt directly to the CPU.
277 		 *
278 		 * 2. Virtual Wire Mode: the APIC is treated as a
279 		 * virtual wire which delivers interrupts from the PIC
280 		 * to the CPU.  In this mode LINT0 is programmed as
281 		 * ExtINT to indicate that the PIC is the source of
282 		 * the interrupt.
283 		 *
284 		 * 3. Virtual Wire Mode via I/O APIC: PIC interrupts are
285 		 * fielded by the I/O APIC and delivered to the appropriate
286 		 * CPU.  In this mode the I/O APIC input 0 is programmed
287 		 * as ExtINT to indicate that the PIC is the source of the
288 		 * interrupt.
289 		 */
290 		atpic->intr_raised = true;
291 		lapic_set_local_intr(vatpic->vm, -1, APIC_LVT_LINT0);
292 		vioapic_pulse_irq(vatpic->vm, 0);
293 		vatpic->stats.as_interrupts++;
294 	} else {
295 		VATPIC_CTR3(vatpic, "atpic master no eligible interrupts "
296 		    "(imr 0x%x irr 0x%x isr 0x%x)",
297 		    atpic->reg_imr, atpic->reg_irr, atpic->reg_isr);
298 	}
299 }
300 
301 static int
302 vatpic_icw1(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
303 {
304 	atpic->ready = false;
305 
306 	atpic->icw_state = IS_ICW1;
307 	atpic->reg_irr = 0;
308 	atpic->reg_imr = 0;
309 	atpic->lowprio = 7;
310 	atpic->read_isr_next = false;
311 	atpic->poll = false;
312 	atpic->special_mask_mode = false;
313 
314 	if ((val & ICW1_SNGL) != 0) {
315 		VATPIC_CTR0(vatpic, "vatpic cascade mode required");
316 		return (-1);
317 	}
318 
319 	if ((val & ICW1_IC4) == 0) {
320 		VATPIC_CTR0(vatpic, "vatpic icw4 required");
321 		return (-1);
322 	}
323 
324 	atpic->icw_state = IS_ICW2;
325 
326 	return (0);
327 }
328 
329 static int
330 vatpic_icw2(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
331 {
332 	atpic->irq_base = val & IRQ_BASE_MASK;
333 	atpic->icw_state = IS_ICW3;
334 
335 	return (0);
336 }
337 
338 static int
339 vatpic_icw3(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
340 {
341 	atpic->icw_state = IS_ICW4;
342 
343 	return (0);
344 }
345 
346 static int
347 vatpic_icw4(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
348 {
349 	if ((val & ICW4_8086) == 0) {
350 		VATPIC_CTR0(vatpic, "vatpic microprocessor mode required");
351 		return (-1);
352 	}
353 
354 	atpic->auto_eoi = (val & ICW4_AEOI) != 0;
355 	if (master_atpic(vatpic, atpic)) {
356 		atpic->special_full_nested = (val & ICW4_SFNM) != 0;
357 	}
358 
359 	atpic->icw_state = IS_ICW1;
360 	atpic->ready = true;
361 
362 	return (0);
363 }
364 
365 static int
366 vatpic_ocw1(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
367 {
368 	atpic->reg_imr = val;
369 
370 	return (0);
371 }
372 
373 static int
374 vatpic_ocw2(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
375 {
376 	atpic->rotate = (val & OCW2_R) != 0;
377 
378 	if ((val & OCW2_EOI) != 0) {
379 		int isr_bit;
380 
381 		if ((val & OCW2_SL) != 0) {
382 			/* specific EOI */
383 			isr_bit = val & 0x7;
384 		} else {
385 			/* non-specific EOI */
386 			isr_bit = vatpic_get_highest_isrpin(atpic);
387 		}
388 
389 		if (isr_bit != -1) {
390 			atpic->reg_isr &= ~(1 << isr_bit);
391 
392 			if (atpic->rotate)
393 				atpic->lowprio = isr_bit;
394 		}
395 	} else if ((val & OCW2_SL) != 0 && atpic->rotate) {
396 		/* specific priority */
397 		atpic->lowprio = val & 0x7;
398 	}
399 
400 	return (0);
401 }
402 
403 static int
404 vatpic_ocw3(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
405 {
406 	if ((val & OCW3_ESMM) != 0) {
407 		atpic->special_mask_mode = (val & OCW3_SMM) != 0;
408 	}
409 	if ((val & OCW3_RR) != 0) {
410 		atpic->read_isr_next = (val & OCW3_RIS) != 0;
411 	}
412 	if ((val & OCW3_P) != 0) {
413 		atpic->poll = true;
414 	}
415 
416 	return (0);
417 }
418 
419 static int
420 vatpic_set_pinstate(struct vatpic *vatpic, int pin, bool newstate)
421 {
422 	struct atpic *atpic;
423 	uint_t oldcnt, newcnt;
424 	int err = 0;
425 
426 	VERIFY(pin >= 0 && pin < 16);
427 	ASSERT(VATPIC_LOCKED(vatpic));
428 
429 	const int lpin = pin & 0x7;
430 	atpic = &vatpic->atpic[pin >> 3];
431 
432 	oldcnt = newcnt = atpic->acnt[lpin];
433 	if (newstate) {
434 		if (newcnt != UINT_MAX) {
435 			newcnt++;
436 		} else {
437 			err = E2BIG;
438 			DTRACE_PROBE2(vatpic__sat_high, struct vatpic *, vatpic,
439 			    int, pin);
440 			vatpic->stats.as_saturate_high++;
441 		}
442 	} else {
443 		if (newcnt != 0) {
444 			newcnt--;
445 		} else {
446 			err = ERANGE;
447 			DTRACE_PROBE2(vatpic__sat_low, struct vatpic *, vatpic,
448 			    int, pin);
449 			vatpic->stats.as_saturate_low++;
450 		}
451 	}
452 	atpic->acnt[lpin] = newcnt;
453 
454 	const bool level = ((atpic->elc & (1 << (lpin))) != 0);
455 	if ((oldcnt == 0 && newcnt == 1) || (newcnt > 0 && level == true)) {
456 		/* rising edge or level */
457 		DTRACE_PROBE2(vatpic__assert, struct vatpic *, vatpic,
458 		    int, pin);
459 		atpic->reg_irr |= (1 << lpin);
460 	} else if (oldcnt == 1 && newcnt == 0) {
461 		/* falling edge */
462 		DTRACE_PROBE2(vatpic__deassert, struct vatpic *, vatpic,
463 		    int, pin);
464 		if (level) {
465 			atpic->reg_irr &= ~(1 << lpin);
466 		}
467 	}
468 
469 	vatpic_notify_intr(vatpic);
470 	return (err);
471 }
472 
473 static int
474 vatpic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
475 {
476 	struct vatpic *vatpic;
477 	struct atpic *atpic;
478 	int err = 0;
479 
480 	if (irq < 0 || irq > 15)
481 		return (EINVAL);
482 
483 	vatpic = vm_atpic(vm);
484 	atpic = &vatpic->atpic[irq >> 3];
485 
486 	if (!atpic->ready)
487 		return (0);
488 
489 	VATPIC_LOCK(vatpic);
490 	switch (irqstate) {
491 	case IRQSTATE_ASSERT:
492 		err = vatpic_set_pinstate(vatpic, irq, true);
493 		break;
494 	case IRQSTATE_DEASSERT:
495 		err = vatpic_set_pinstate(vatpic, irq, false);
496 		break;
497 	case IRQSTATE_PULSE:
498 		err = vatpic_set_pinstate(vatpic, irq, true);
499 		if (err == 0) {
500 			err = vatpic_set_pinstate(vatpic, irq, false);
501 		}
502 		break;
503 	default:
504 		panic("vatpic_set_irqstate: invalid irqstate %d", irqstate);
505 	}
506 	VATPIC_UNLOCK(vatpic);
507 
508 	return (err);
509 }
510 
511 int
512 vatpic_assert_irq(struct vm *vm, int irq)
513 {
514 	return (vatpic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
515 }
516 
517 int
518 vatpic_deassert_irq(struct vm *vm, int irq)
519 {
520 	return (vatpic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
521 }
522 
523 int
524 vatpic_pulse_irq(struct vm *vm, int irq)
525 {
526 	return (vatpic_set_irqstate(vm, irq, IRQSTATE_PULSE));
527 }
528 
529 int
530 vatpic_set_irq_trigger(struct vm *vm, int irq, enum vm_intr_trigger trigger)
531 {
532 	if (irq < 0 || irq > 15)
533 		return (EINVAL);
534 
535 	/*
536 	 * See comments in vatpic_elc_handler.
537 	 * These IRQs must be edge triggered.
538 	 */
539 	if (trigger == LEVEL_TRIGGER) {
540 		switch (irq) {
541 		case 0:
542 		case 1:
543 		case 2:
544 		case 8:
545 		case 13:
546 			return (EINVAL);
547 		}
548 	}
549 
550 	struct vatpic *vatpic = vm_atpic(vm);
551 	struct atpic *atpic = &vatpic->atpic[irq >> 3];
552 	const int pin = irq & 0x7;
553 
554 	VATPIC_LOCK(vatpic);
555 	if (trigger == LEVEL_TRIGGER) {
556 		atpic->elc |= (1 << pin);
557 	} else {
558 		atpic->elc &= ~(1 << pin);
559 	}
560 	VATPIC_UNLOCK(vatpic);
561 
562 	return (0);
563 }
564 
565 void
566 vatpic_pending_intr(struct vm *vm, int *vecptr)
567 {
568 	struct vatpic *vatpic;
569 	struct atpic *atpic;
570 	int pin;
571 
572 	vatpic = vm_atpic(vm);
573 
574 	atpic = &vatpic->atpic[0];
575 
576 	VATPIC_LOCK(vatpic);
577 
578 	pin = vatpic_get_highest_irrpin(atpic);
579 	if (pin == 2) {
580 		atpic = &vatpic->atpic[1];
581 		pin = vatpic_get_highest_irrpin(atpic);
582 	}
583 
584 	/*
585 	 * If there are no pins active at this moment then return the spurious
586 	 * interrupt vector instead.
587 	 */
588 	if (pin == -1)
589 		pin = 7;
590 
591 	KASSERT(pin >= 0 && pin <= 7, ("%s: invalid pin %d", __func__, pin));
592 	*vecptr = atpic->irq_base + pin;
593 
594 	VATPIC_UNLOCK(vatpic);
595 }
596 
597 static void
598 vatpic_pin_accepted(struct atpic *atpic, int pin)
599 {
600 	ASSERT(pin >= 0 && pin < 8);
601 
602 	atpic->intr_raised = false;
603 
604 	if (atpic->acnt[pin] == 0)
605 		atpic->reg_irr &= ~(1 << pin);
606 
607 	if (atpic->auto_eoi) {
608 		if (atpic->rotate)
609 			atpic->lowprio = pin;
610 	} else {
611 		atpic->reg_isr |= (1 << pin);
612 	}
613 }
614 
615 void
616 vatpic_intr_accepted(struct vm *vm, int vector)
617 {
618 	struct vatpic *vatpic;
619 	int pin;
620 
621 	vatpic = vm_atpic(vm);
622 
623 	VATPIC_LOCK(vatpic);
624 
625 	pin = vector & 0x7;
626 
627 	if ((vector & IRQ_BASE_MASK) == vatpic->atpic[1].irq_base) {
628 		vatpic_pin_accepted(&vatpic->atpic[1], pin);
629 		/*
630 		 * If this vector originated from the slave,
631 		 * accept the cascaded interrupt too.
632 		 */
633 		vatpic_pin_accepted(&vatpic->atpic[0], 2);
634 	} else {
635 		vatpic_pin_accepted(&vatpic->atpic[0], pin);
636 	}
637 
638 	vatpic_notify_intr(vatpic);
639 
640 	VATPIC_UNLOCK(vatpic);
641 }
642 
643 static int
644 vatpic_read(struct vatpic *vatpic, struct atpic *atpic, bool in, int port,
645     int bytes, uint32_t *eax)
646 {
647 	int pin;
648 
649 	VATPIC_LOCK(vatpic);
650 
651 	if (atpic->poll) {
652 		atpic->poll = false;
653 		pin = vatpic_get_highest_irrpin(atpic);
654 		if (pin >= 0) {
655 			vatpic_pin_accepted(atpic, pin);
656 			*eax = 0x80 | pin;
657 		} else {
658 			*eax = 0;
659 		}
660 	} else {
661 		if (port & ICU_IMR_OFFSET) {
662 			/* read interrrupt mask register */
663 			*eax = atpic->reg_imr;
664 		} else {
665 			if (atpic->read_isr_next) {
666 				/* read interrupt service register */
667 				*eax = atpic->reg_isr;
668 			} else {
669 				/* read interrupt request register */
670 				*eax = atpic->reg_irr;
671 			}
672 		}
673 	}
674 
675 	VATPIC_UNLOCK(vatpic);
676 
677 	return (0);
678 
679 }
680 
681 static int
682 vatpic_write(struct vatpic *vatpic, struct atpic *atpic, bool in, int port,
683     int bytes, uint32_t *eax)
684 {
685 	int error;
686 	uint8_t val;
687 
688 	error = 0;
689 	val = *eax;
690 
691 	VATPIC_LOCK(vatpic);
692 
693 	if (port & ICU_IMR_OFFSET) {
694 		switch (atpic->icw_state) {
695 		case IS_ICW2:
696 			error = vatpic_icw2(vatpic, atpic, val);
697 			break;
698 		case IS_ICW3:
699 			error = vatpic_icw3(vatpic, atpic, val);
700 			break;
701 		case IS_ICW4:
702 			error = vatpic_icw4(vatpic, atpic, val);
703 			break;
704 		default:
705 			error = vatpic_ocw1(vatpic, atpic, val);
706 			break;
707 		}
708 	} else {
709 		if (val & (1 << 4))
710 			error = vatpic_icw1(vatpic, atpic, val);
711 
712 		if (atpic->ready) {
713 			if (val & (1 << 3))
714 				error = vatpic_ocw3(vatpic, atpic, val);
715 			else
716 				error = vatpic_ocw2(vatpic, atpic, val);
717 		}
718 	}
719 
720 	if (atpic->ready)
721 		vatpic_notify_intr(vatpic);
722 
723 	VATPIC_UNLOCK(vatpic);
724 
725 	return (error);
726 }
727 
728 int
729 vatpic_master_handler(void *arg, bool in, uint16_t port, uint8_t bytes,
730     uint32_t *eax)
731 {
732 	struct vatpic *vatpic = arg;
733 	struct atpic *atpic = &vatpic->atpic[0];
734 
735 	if (bytes != 1)
736 		return (-1);
737 
738 	if (in) {
739 		return (vatpic_read(vatpic, atpic, in, port, bytes, eax));
740 	}
741 
742 	return (vatpic_write(vatpic, atpic, in, port, bytes, eax));
743 }
744 
745 int
746 vatpic_slave_handler(void *arg, bool in, uint16_t port, uint8_t bytes,
747     uint32_t *eax)
748 {
749 	struct vatpic *vatpic = arg;
750 	struct atpic *atpic = &vatpic->atpic[1];
751 
752 	if (bytes != 1)
753 		return (-1);
754 
755 	if (in) {
756 		return (vatpic_read(vatpic, atpic, in, port, bytes, eax));
757 	}
758 
759 	return (vatpic_write(vatpic, atpic, in, port, bytes, eax));
760 }
761 
762 int
763 vatpic_elc_handler(void *arg, bool in, uint16_t port, uint8_t bytes,
764     uint32_t *eax)
765 {
766 	struct vatpic *vatpic = arg;
767 	struct atpic *atpic = NULL;
768 	uint8_t elc_mask = 0;
769 
770 	switch (port) {
771 	case IO_ELCR1:
772 		atpic = &vatpic->atpic[0];
773 		/*
774 		 * For the master PIC the cascade channel (IRQ2), the heart beat
775 		 * timer (IRQ0), and the keyboard controller (IRQ1) cannot be
776 		 * programmed for level mode.
777 		 */
778 		elc_mask = 0xf8;
779 		break;
780 	case IO_ELCR2:
781 		atpic = &vatpic->atpic[1];
782 		/*
783 		 * For the slave PIC the real time clock (IRQ8) and the floating
784 		 * point error interrupt (IRQ13) cannot be programmed for level
785 		 * mode.
786 		 */
787 		elc_mask = 0xde;
788 		break;
789 	default:
790 		return (-1);
791 	}
792 
793 	if (bytes != 1)
794 		return (-1);
795 
796 	VATPIC_LOCK(vatpic);
797 	if (in) {
798 		*eax = atpic->elc;
799 	} else {
800 		atpic->elc = *eax & elc_mask;
801 	}
802 	VATPIC_UNLOCK(vatpic);
803 
804 	return (0);
805 }
806 
807 struct vatpic *
808 vatpic_init(struct vm *vm)
809 {
810 	struct vatpic *vatpic;
811 
812 	vatpic = malloc(sizeof (struct vatpic), M_VATPIC, M_WAITOK | M_ZERO);
813 	vatpic->vm = vm;
814 
815 	mutex_init(&vatpic->lock, NULL, MUTEX_ADAPTIVE, NULL);
816 
817 	return (vatpic);
818 }
819 
820 void
821 vatpic_cleanup(struct vatpic *vatpic)
822 {
823 	mutex_destroy(&vatpic->lock);
824 	free(vatpic, M_VATPIC);
825 }
826