1263035Stychon/*-
2263035Stychon * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3263035Stychon * All rights reserved.
4263035Stychon *
5263035Stychon * Redistribution and use in source and binary forms, with or without
6263035Stychon * modification, are permitted provided that the following conditions
7263035Stychon * are met:
8263035Stychon * 1. Redistributions of source code must retain the above copyright
9263035Stychon *    notice, this list of conditions and the following disclaimer.
10263035Stychon * 2. Redistributions in binary form must reproduce the above copyright
11263035Stychon *    notice, this list of conditions and the following disclaimer in the
12263035Stychon *    documentation and/or other materials provided with the distribution.
13263035Stychon *
14263035Stychon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
15263035Stychon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16263035Stychon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17263035Stychon * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18263035Stychon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19263035Stychon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20263035Stychon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21263035Stychon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22263035Stychon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23263035Stychon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24263035Stychon * SUCH DAMAGE.
25263035Stychon */
26263035Stychon
27263035Stychon#include <sys/cdefs.h>
28263035Stychon__FBSDID("$FreeBSD$");
29263035Stychon
30263035Stychon#include <sys/param.h>
31263035Stychon#include <sys/types.h>
32263035Stychon#include <sys/queue.h>
33263035Stychon#include <sys/kernel.h>
34263035Stychon#include <sys/lock.h>
35263035Stychon#include <sys/malloc.h>
36263035Stychon#include <sys/mutex.h>
37263035Stychon#include <sys/systm.h>
38263035Stychon
39263035Stychon#include <x86/apicreg.h>
40263035Stychon#include <dev/ic/i8259.h>
41263035Stychon
42263035Stychon#include <machine/vmm.h>
43263035Stychon
44263035Stychon#include "vmm_ktr.h"
45263035Stychon#include "vmm_lapic.h"
46263035Stychon#include "vioapic.h"
47263035Stychon#include "vatpic.h"
48263035Stychon
49263035Stychonstatic MALLOC_DEFINE(M_VATPIC, "atpic", "bhyve virtual atpic (8259)");
50263035Stychon
51263035Stychon#define	VATPIC_LOCK(vatpic)		mtx_lock_spin(&((vatpic)->mtx))
52263035Stychon#define	VATPIC_UNLOCK(vatpic)		mtx_unlock_spin(&((vatpic)->mtx))
53263035Stychon#define	VATPIC_LOCKED(vatpic)		mtx_owned(&((vatpic)->mtx))
54263035Stychon
55263035Stychonenum irqstate {
56263035Stychon	IRQSTATE_ASSERT,
57263035Stychon	IRQSTATE_DEASSERT,
58263035Stychon	IRQSTATE_PULSE
59263035Stychon};
60263035Stychon
61263035Stychonstruct atpic {
62263035Stychon	bool		ready;
63263035Stychon	int		icw_num;
64263035Stychon	int		rd_cmd_reg;
65263035Stychon
66263035Stychon	bool		aeoi;
67263035Stychon	bool		poll;
68263035Stychon	bool		rotate;
69268891Sjhb	bool		sfn;		/* special fully-nested mode */
70263035Stychon
71263035Stychon	int		irq_base;
72263035Stychon	uint8_t		request;	/* Interrupt Request Register (IIR) */
73263035Stychon	uint8_t		service;	/* Interrupt Service (ISR) */
74263035Stychon	uint8_t		mask;		/* Interrupt Mask Register (IMR) */
75276447Sneel	uint8_t		smm;		/* special mask mode */
76263035Stychon
77263035Stychon	int		acnt[8];	/* sum of pin asserts and deasserts */
78276429Sneel	int		lowprio;	/* lowest priority irq */
79268891Sjhb
80268891Sjhb	bool		intr_raised;
81263035Stychon};
82263035Stychon
83263035Stychonstruct vatpic {
84263035Stychon	struct vm	*vm;
85263035Stychon	struct mtx	mtx;
86263035Stychon	struct atpic	atpic[2];
87263035Stychon	uint8_t		elc[2];
88263035Stychon};
89263035Stychon
90263035Stychon#define	VATPIC_CTR0(vatpic, fmt)					\
91263035Stychon	VM_CTR0((vatpic)->vm, fmt)
92263035Stychon
93263035Stychon#define	VATPIC_CTR1(vatpic, fmt, a1)					\
94263035Stychon	VM_CTR1((vatpic)->vm, fmt, a1)
95263035Stychon
96263035Stychon#define	VATPIC_CTR2(vatpic, fmt, a1, a2)				\
97263035Stychon	VM_CTR2((vatpic)->vm, fmt, a1, a2)
98263035Stychon
99263035Stychon#define	VATPIC_CTR3(vatpic, fmt, a1, a2, a3)				\
100263035Stychon	VM_CTR3((vatpic)->vm, fmt, a1, a2, a3)
101263035Stychon
102263035Stychon#define	VATPIC_CTR4(vatpic, fmt, a1, a2, a3, a4)			\
103263035Stychon	VM_CTR4((vatpic)->vm, fmt, a1, a2, a3, a4)
104263035Stychon
105276429Sneel/*
106276429Sneel * Loop over all the pins in priority order from highest to lowest.
107276429Sneel */
108276429Sneel#define	ATPIC_PIN_FOREACH(pinvar, atpic, tmpvar)			\
109276429Sneel	for (tmpvar = 0, pinvar = (atpic->lowprio + 1) & 0x7;		\
110276429Sneel	    tmpvar < 8;							\
111276429Sneel	    tmpvar++, pinvar = (pinvar + 1) & 0x7)
112276429Sneel
113268891Sjhbstatic void vatpic_set_pinstate(struct vatpic *vatpic, int pin, bool newstate);
114263035Stychon
115276429Sneelstatic __inline bool
116276429Sneelmaster_atpic(struct vatpic *vatpic, struct atpic *atpic)
117276429Sneel{
118276429Sneel
119276429Sneel	if (atpic == &vatpic->atpic[0])
120276429Sneel		return (true);
121276429Sneel	else
122276429Sneel		return (false);
123276429Sneel}
124276429Sneel
125263035Stychonstatic __inline int
126263035Stychonvatpic_get_highest_isrpin(struct atpic *atpic)
127263035Stychon{
128263035Stychon	int bit, pin;
129263035Stychon	int i;
130263035Stychon
131276429Sneel	ATPIC_PIN_FOREACH(pin, atpic, i) {
132263035Stychon                bit = (1 << pin);
133263035Stychon
134276447Sneel		if (atpic->service & bit) {
135276447Sneel			/*
136276447Sneel			 * An IS bit that is masked by an IMR bit will not be
137276447Sneel			 * cleared by a non-specific EOI in Special Mask Mode.
138276447Sneel			 */
139276447Sneel			if (atpic->smm && (atpic->mask & bit) != 0)
140276447Sneel				continue;
141276447Sneel			else
142276447Sneel				return (pin);
143276447Sneel		}
144263035Stychon	}
145263035Stychon
146263035Stychon	return (-1);
147263035Stychon}
148263035Stychon
149263035Stychonstatic __inline int
150263035Stychonvatpic_get_highest_irrpin(struct atpic *atpic)
151263035Stychon{
152268891Sjhb	int serviced;
153276429Sneel	int bit, pin, tmp;
154263035Stychon
155268891Sjhb	/*
156268891Sjhb	 * In 'Special Fully-Nested Mode' when an interrupt request from
157268891Sjhb	 * a slave is in service, the slave is not locked out from the
158268891Sjhb	 * master's priority logic.
159268891Sjhb	 */
160268891Sjhb	serviced = atpic->service;
161268891Sjhb	if (atpic->sfn)
162268891Sjhb		serviced &= ~(1 << 2);
163268891Sjhb
164276447Sneel	/*
165276447Sneel	 * In 'Special Mask Mode', when a mask bit is set in OCW1 it inhibits
166276447Sneel	 * further interrupts at that level and enables interrupts from all
167276447Sneel	 * other levels that are not masked. In other words the ISR has no
168276447Sneel	 * bearing on the levels that can generate interrupts.
169276447Sneel	 */
170276447Sneel	if (atpic->smm)
171276447Sneel		serviced = 0;
172276447Sneel
173276429Sneel	ATPIC_PIN_FOREACH(pin, atpic, tmp) {
174276429Sneel		bit = 1 << pin;
175276429Sneel
176276429Sneel		/*
177276429Sneel		 * If there is already an interrupt in service at the same
178276429Sneel		 * or higher priority then bail.
179276429Sneel		 */
180276429Sneel		if ((serviced & bit) != 0)
181263035Stychon			break;
182263035Stychon
183276429Sneel		/*
184276429Sneel		 * If an interrupt is asserted and not masked then return
185276429Sneel		 * the corresponding 'pin' to the caller.
186276429Sneel		 */
187276429Sneel		if ((atpic->request & bit) != 0 && (atpic->mask & bit) == 0)
188263035Stychon			return (pin);
189263035Stychon	}
190263035Stychon
191263035Stychon	return (-1);
192263035Stychon}
193263035Stychon
194263035Stychonstatic void
195263035Stychonvatpic_notify_intr(struct vatpic *vatpic)
196263035Stychon{
197263035Stychon	struct atpic *atpic;
198263035Stychon	int pin;
199263035Stychon
200263035Stychon	KASSERT(VATPIC_LOCKED(vatpic), ("vatpic_notify_intr not locked"));
201263035Stychon
202268891Sjhb	/*
203268891Sjhb	 * First check the slave.
204268891Sjhb	 */
205268891Sjhb	atpic = &vatpic->atpic[1];
206268891Sjhb	if (!atpic->intr_raised &&
207268891Sjhb	    (pin = vatpic_get_highest_irrpin(atpic)) != -1) {
208268891Sjhb		VATPIC_CTR4(vatpic, "atpic slave notify pin = %d "
209268891Sjhb		    "(imr 0x%x irr 0x%x isr 0x%x)", pin,
210268891Sjhb		    atpic->mask, atpic->request, atpic->service);
211268891Sjhb
212268891Sjhb		/*
213268891Sjhb		 * Cascade the request from the slave to the master.
214268891Sjhb		 */
215268891Sjhb		atpic->intr_raised = true;
216268891Sjhb		vatpic_set_pinstate(vatpic, 2, true);
217268891Sjhb		vatpic_set_pinstate(vatpic, 2, false);
218268891Sjhb	} else {
219268891Sjhb		VATPIC_CTR3(vatpic, "atpic slave no eligible interrupts "
220268891Sjhb		    "(imr 0x%x irr 0x%x isr 0x%x)",
221268891Sjhb		    atpic->mask, atpic->request, atpic->service);
222268891Sjhb	}
223268891Sjhb
224268891Sjhb	/*
225268891Sjhb	 * Then check the master.
226268891Sjhb	 */
227263035Stychon	atpic = &vatpic->atpic[0];
228268891Sjhb	if (!atpic->intr_raised &&
229268891Sjhb	    (pin = vatpic_get_highest_irrpin(atpic)) != -1) {
230268891Sjhb		VATPIC_CTR4(vatpic, "atpic master notify pin = %d "
231263035Stychon		    "(imr 0x%x irr 0x%x isr 0x%x)", pin,
232263035Stychon		    atpic->mask, atpic->request, atpic->service);
233268891Sjhb
234268891Sjhb		/*
235270159Sgrehan		 * From Section 3.6.2, "Interrupt Modes", in the
236270159Sgrehan		 * MPtable Specification, Version 1.4
237270159Sgrehan		 *
238268891Sjhb		 * PIC interrupts are routed to both the Local APIC
239268891Sjhb		 * and the I/O APIC to support operation in 1 of 3
240268891Sjhb		 * modes.
241268891Sjhb		 *
242268891Sjhb		 * 1. Legacy PIC Mode: the PIC effectively bypasses
243270159Sgrehan		 * all APIC components.  In this mode the local APIC is
244268891Sjhb		 * disabled and LINT0 is reconfigured as INTR to
245268891Sjhb		 * deliver the PIC interrupt directly to the CPU.
246268891Sjhb		 *
247268891Sjhb		 * 2. Virtual Wire Mode: the APIC is treated as a
248268891Sjhb		 * virtual wire which delivers interrupts from the PIC
249270159Sgrehan		 * to the CPU.  In this mode LINT0 is programmed as
250268891Sjhb		 * ExtINT to indicate that the PIC is the source of
251268891Sjhb		 * the interrupt.
252268891Sjhb		 *
253270159Sgrehan		 * 3. Virtual Wire Mode via I/O APIC: PIC interrupts are
254270159Sgrehan		 * fielded by the I/O APIC and delivered to the appropriate
255270159Sgrehan		 * CPU.  In this mode the I/O APIC input 0 is programmed
256270159Sgrehan		 * as ExtINT to indicate that the PIC is the source of the
257270159Sgrehan		 * interrupt.
258268891Sjhb		 */
259268891Sjhb		atpic->intr_raised = true;
260263035Stychon		lapic_set_local_intr(vatpic->vm, -1, APIC_LVT_LINT0);
261263035Stychon		vioapic_pulse_irq(vatpic->vm, 0);
262263035Stychon	} else {
263268891Sjhb		VATPIC_CTR3(vatpic, "atpic master no eligible interrupts "
264263035Stychon		    "(imr 0x%x irr 0x%x isr 0x%x)",
265263035Stychon		    atpic->mask, atpic->request, atpic->service);
266263035Stychon	}
267263035Stychon}
268263035Stychon
269263035Stychonstatic int
270263035Stychonvatpic_icw1(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
271263035Stychon{
272263035Stychon	VATPIC_CTR1(vatpic, "atpic icw1 0x%x", val);
273263035Stychon
274263035Stychon	atpic->ready = false;
275263035Stychon
276263035Stychon	atpic->icw_num = 1;
277284899Sneel	atpic->request = 0;
278263035Stychon	atpic->mask = 0;
279276429Sneel	atpic->lowprio = 7;
280263035Stychon	atpic->rd_cmd_reg = 0;
281276429Sneel	atpic->poll = 0;
282276447Sneel	atpic->smm = 0;
283263035Stychon
284263035Stychon	if ((val & ICW1_SNGL) != 0) {
285263035Stychon		VATPIC_CTR0(vatpic, "vatpic cascade mode required");
286263035Stychon		return (-1);
287263035Stychon	}
288263035Stychon
289263035Stychon	if ((val & ICW1_IC4) == 0) {
290263035Stychon		VATPIC_CTR0(vatpic, "vatpic icw4 required");
291263035Stychon		return (-1);
292263035Stychon	}
293263035Stychon
294263035Stychon	atpic->icw_num++;
295263035Stychon
296263035Stychon	return (0);
297263035Stychon}
298263035Stychon
299263035Stychonstatic int
300263035Stychonvatpic_icw2(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
301263035Stychon{
302263035Stychon	VATPIC_CTR1(vatpic, "atpic icw2 0x%x", val);
303263035Stychon
304263035Stychon	atpic->irq_base = val & 0xf8;
305263035Stychon
306263035Stychon	atpic->icw_num++;
307263035Stychon
308263035Stychon	return (0);
309263035Stychon}
310263035Stychon
311263035Stychonstatic int
312263035Stychonvatpic_icw3(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
313263035Stychon{
314263035Stychon	VATPIC_CTR1(vatpic, "atpic icw3 0x%x", val);
315263035Stychon
316263035Stychon	atpic->icw_num++;
317263035Stychon
318263035Stychon	return (0);
319263035Stychon}
320263035Stychon
321263035Stychonstatic int
322263035Stychonvatpic_icw4(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
323263035Stychon{
324263035Stychon	VATPIC_CTR1(vatpic, "atpic icw4 0x%x", val);
325263035Stychon
326263035Stychon	if ((val & ICW4_8086) == 0) {
327263035Stychon		VATPIC_CTR0(vatpic, "vatpic microprocessor mode required");
328263035Stychon		return (-1);
329263035Stychon	}
330263035Stychon
331263035Stychon	if ((val & ICW4_AEOI) != 0)
332263035Stychon		atpic->aeoi = true;
333263035Stychon
334276429Sneel	if ((val & ICW4_SFNM) != 0) {
335276429Sneel		if (master_atpic(vatpic, atpic)) {
336276429Sneel			atpic->sfn = true;
337276429Sneel		} else {
338276429Sneel			VATPIC_CTR1(vatpic, "Ignoring special fully nested "
339276429Sneel			    "mode on slave atpic: %#x", val);
340276429Sneel		}
341276429Sneel	}
342276429Sneel
343263035Stychon	atpic->icw_num = 0;
344263035Stychon	atpic->ready = true;
345263035Stychon
346263035Stychon	return (0);
347263035Stychon}
348263035Stychon
349263035Stychonstatic int
350263035Stychonvatpic_ocw1(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
351263035Stychon{
352263035Stychon	VATPIC_CTR1(vatpic, "atpic ocw1 0x%x", val);
353263035Stychon
354263035Stychon	atpic->mask = val & 0xff;
355263035Stychon
356263035Stychon	return (0);
357263035Stychon}
358263035Stychon
359263035Stychonstatic int
360263035Stychonvatpic_ocw2(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
361263035Stychon{
362263035Stychon	VATPIC_CTR1(vatpic, "atpic ocw2 0x%x", val);
363263035Stychon
364263035Stychon	atpic->rotate = ((val & OCW2_R) != 0);
365263035Stychon
366263035Stychon	if ((val & OCW2_EOI) != 0) {
367263035Stychon		int isr_bit;
368263035Stychon
369263035Stychon		if ((val & OCW2_SL) != 0) {
370263035Stychon			/* specific EOI */
371263035Stychon			isr_bit = val & 0x7;
372263035Stychon		} else {
373263035Stychon			/* non-specific EOI */
374263035Stychon			isr_bit = vatpic_get_highest_isrpin(atpic);
375263035Stychon		}
376263035Stychon
377263035Stychon		if (isr_bit != -1) {
378263035Stychon			atpic->service &= ~(1 << isr_bit);
379263035Stychon
380263035Stychon			if (atpic->rotate)
381276429Sneel				atpic->lowprio = isr_bit;
382263035Stychon		}
383263035Stychon	} else if ((val & OCW2_SL) != 0 && atpic->rotate == true) {
384263035Stychon		/* specific priority */
385276429Sneel		atpic->lowprio = val & 0x7;
386263035Stychon	}
387263035Stychon
388263035Stychon	return (0);
389263035Stychon}
390263035Stychon
391263035Stychonstatic int
392263035Stychonvatpic_ocw3(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
393263035Stychon{
394263035Stychon	VATPIC_CTR1(vatpic, "atpic ocw3 0x%x", val);
395263035Stychon
396276429Sneel	if (val & OCW3_ESMM) {
397276447Sneel		atpic->smm = val & OCW3_SMM ? 1 : 0;
398276447Sneel		VATPIC_CTR2(vatpic, "%s atpic special mask mode %s",
399276447Sneel		    master_atpic(vatpic, atpic) ? "master" : "slave",
400276447Sneel		    atpic->smm ?  "enabled" : "disabled");
401276429Sneel	}
402263035Stychon
403263035Stychon	if (val & OCW3_RR) {
404263035Stychon		/* read register command */
405263035Stychon		atpic->rd_cmd_reg = val & OCW3_RIS;
406276429Sneel
407276429Sneel		/* Polling mode */
408276429Sneel		atpic->poll = ((val & OCW3_P) != 0);
409263035Stychon	}
410263035Stychon
411263035Stychon	return (0);
412263035Stychon}
413263035Stychon
414263035Stychonstatic void
415263035Stychonvatpic_set_pinstate(struct vatpic *vatpic, int pin, bool newstate)
416263035Stychon{
417263035Stychon	struct atpic *atpic;
418263035Stychon	int oldcnt, newcnt;
419263035Stychon	bool level;
420263035Stychon
421263035Stychon	KASSERT(pin >= 0 && pin < 16,
422263035Stychon	    ("vatpic_set_pinstate: invalid pin number %d", pin));
423263035Stychon	KASSERT(VATPIC_LOCKED(vatpic),
424263035Stychon	    ("vatpic_set_pinstate: vatpic is not locked"));
425263035Stychon
426263035Stychon	atpic = &vatpic->atpic[pin >> 3];
427263035Stychon
428263035Stychon	oldcnt = atpic->acnt[pin & 0x7];
429263035Stychon	if (newstate)
430263035Stychon		atpic->acnt[pin & 0x7]++;
431263035Stychon	else
432263035Stychon		atpic->acnt[pin & 0x7]--;
433263035Stychon	newcnt = atpic->acnt[pin & 0x7];
434263035Stychon
435263035Stychon	if (newcnt < 0) {
436263035Stychon		VATPIC_CTR2(vatpic, "atpic pin%d: bad acnt %d", pin, newcnt);
437263035Stychon	}
438263035Stychon
439263035Stychon	level = ((vatpic->elc[pin >> 3] & (1 << (pin & 0x7))) != 0);
440263035Stychon
441263035Stychon	if ((oldcnt == 0 && newcnt == 1) || (newcnt > 0 && level == true)) {
442263035Stychon		/* rising edge or level */
443263035Stychon		VATPIC_CTR1(vatpic, "atpic pin%d: asserted", pin);
444263035Stychon		atpic->request |= (1 << (pin & 0x7));
445263035Stychon	} else if (oldcnt == 1 && newcnt == 0) {
446263035Stychon		/* falling edge */
447263035Stychon		VATPIC_CTR1(vatpic, "atpic pin%d: deasserted", pin);
448276429Sneel		if (level)
449276429Sneel			atpic->request &= ~(1 << (pin & 0x7));
450263035Stychon	} else {
451263035Stychon		VATPIC_CTR3(vatpic, "atpic pin%d: %s, ignored, acnt %d",
452263035Stychon		    pin, newstate ? "asserted" : "deasserted", newcnt);
453263035Stychon	}
454263035Stychon
455263035Stychon	vatpic_notify_intr(vatpic);
456263035Stychon}
457263035Stychon
458263035Stychonstatic int
459263035Stychonvatpic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
460263035Stychon{
461263035Stychon	struct vatpic *vatpic;
462263035Stychon	struct atpic *atpic;
463263035Stychon
464263035Stychon	if (irq < 0 || irq > 15)
465263035Stychon		return (EINVAL);
466263035Stychon
467263035Stychon	vatpic = vm_atpic(vm);
468263035Stychon	atpic = &vatpic->atpic[irq >> 3];
469263035Stychon
470263035Stychon	if (atpic->ready == false)
471263035Stychon		return (0);
472263035Stychon
473263035Stychon	VATPIC_LOCK(vatpic);
474263035Stychon	switch (irqstate) {
475263035Stychon	case IRQSTATE_ASSERT:
476263035Stychon		vatpic_set_pinstate(vatpic, irq, true);
477263035Stychon		break;
478263035Stychon	case IRQSTATE_DEASSERT:
479263035Stychon		vatpic_set_pinstate(vatpic, irq, false);
480263035Stychon		break;
481263035Stychon	case IRQSTATE_PULSE:
482263035Stychon		vatpic_set_pinstate(vatpic, irq, true);
483263035Stychon		vatpic_set_pinstate(vatpic, irq, false);
484263035Stychon		break;
485263035Stychon	default:
486263035Stychon		panic("vatpic_set_irqstate: invalid irqstate %d", irqstate);
487263035Stychon	}
488263035Stychon	VATPIC_UNLOCK(vatpic);
489263035Stychon
490263035Stychon	return (0);
491263035Stychon}
492263035Stychon
493263035Stychonint
494263035Stychonvatpic_assert_irq(struct vm *vm, int irq)
495263035Stychon{
496263035Stychon	return (vatpic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
497263035Stychon}
498263035Stychon
499263035Stychonint
500263035Stychonvatpic_deassert_irq(struct vm *vm, int irq)
501263035Stychon{
502263035Stychon	return (vatpic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
503263035Stychon}
504263035Stychon
505263035Stychonint
506263035Stychonvatpic_pulse_irq(struct vm *vm, int irq)
507263035Stychon{
508263035Stychon	return (vatpic_set_irqstate(vm, irq, IRQSTATE_PULSE));
509263035Stychon}
510263035Stychon
511268972Sjhbint
512268972Sjhbvatpic_set_irq_trigger(struct vm *vm, int irq, enum vm_intr_trigger trigger)
513268972Sjhb{
514268972Sjhb	struct vatpic *vatpic;
515268972Sjhb
516268972Sjhb	if (irq < 0 || irq > 15)
517268972Sjhb		return (EINVAL);
518268972Sjhb
519268972Sjhb	/*
520268972Sjhb	 * See comment in vatpic_elc_handler.  These IRQs must be
521268972Sjhb	 * edge triggered.
522268972Sjhb	 */
523268972Sjhb	if (trigger == LEVEL_TRIGGER) {
524268972Sjhb		switch (irq) {
525268972Sjhb		case 0:
526268972Sjhb		case 1:
527268972Sjhb		case 2:
528268972Sjhb		case 8:
529268972Sjhb		case 13:
530268972Sjhb			return (EINVAL);
531268972Sjhb		}
532268972Sjhb	}
533268972Sjhb
534268972Sjhb	vatpic = vm_atpic(vm);
535268972Sjhb
536268972Sjhb	VATPIC_LOCK(vatpic);
537268972Sjhb
538268972Sjhb	if (trigger == LEVEL_TRIGGER)
539268972Sjhb		vatpic->elc[irq >> 3] |=  1 << (irq & 0x7);
540268972Sjhb	else
541268972Sjhb		vatpic->elc[irq >> 3] &=  ~(1 << (irq & 0x7));
542268972Sjhb
543268972Sjhb	VATPIC_UNLOCK(vatpic);
544268972Sjhb
545268972Sjhb	return (0);
546268972Sjhb}
547268972Sjhb
548268891Sjhbvoid
549263035Stychonvatpic_pending_intr(struct vm *vm, int *vecptr)
550263035Stychon{
551263035Stychon	struct vatpic *vatpic;
552263035Stychon	struct atpic *atpic;
553263035Stychon	int pin;
554263035Stychon
555263035Stychon	vatpic = vm_atpic(vm);
556263035Stychon
557263035Stychon	atpic = &vatpic->atpic[0];
558263035Stychon
559263035Stychon	VATPIC_LOCK(vatpic);
560263035Stychon
561263035Stychon	pin = vatpic_get_highest_irrpin(atpic);
562268891Sjhb	if (pin == 2) {
563268891Sjhb		atpic = &vatpic->atpic[1];
564268891Sjhb		pin = vatpic_get_highest_irrpin(atpic);
565268891Sjhb	}
566263035Stychon
567276349Sneel	/*
568276349Sneel	 * If there are no pins active at this moment then return the spurious
569276349Sneel	 * interrupt vector instead.
570276349Sneel	 */
571276349Sneel	if (pin == -1)
572276349Sneel		pin = 7;
573276349Sneel
574276349Sneel	KASSERT(pin >= 0 && pin <= 7, ("%s: invalid pin %d", __func__, pin));
575263035Stychon	*vecptr = atpic->irq_base + pin;
576263035Stychon
577263035Stychon	VATPIC_UNLOCK(vatpic);
578268891Sjhb}
579263035Stychon
580268891Sjhbstatic void
581268891Sjhbvatpic_pin_accepted(struct atpic *atpic, int pin)
582268891Sjhb{
583268891Sjhb	atpic->intr_raised = false;
584268891Sjhb
585268891Sjhb	if (atpic->acnt[pin] == 0)
586268891Sjhb		atpic->request &= ~(1 << pin);
587268891Sjhb
588268891Sjhb	if (atpic->aeoi == true) {
589268891Sjhb		if (atpic->rotate == true)
590276429Sneel			atpic->lowprio = pin;
591268891Sjhb	} else {
592268891Sjhb		atpic->service |= (1 << pin);
593268891Sjhb	}
594263035Stychon}
595263035Stychon
596263035Stychonvoid
597263035Stychonvatpic_intr_accepted(struct vm *vm, int vector)
598263035Stychon{
599263035Stychon	struct vatpic *vatpic;
600263035Stychon	int pin;
601263035Stychon
602263035Stychon	vatpic = vm_atpic(vm);
603263035Stychon
604268891Sjhb	VATPIC_LOCK(vatpic);
605263035Stychon
606263035Stychon	pin = vector & 0x7;
607263035Stychon
608268891Sjhb	if ((vector & ~0x7) == vatpic->atpic[1].irq_base) {
609268891Sjhb		vatpic_pin_accepted(&vatpic->atpic[1], pin);
610268891Sjhb		/*
611268891Sjhb		 * If this vector originated from the slave,
612268891Sjhb		 * accept the cascaded interrupt too.
613268891Sjhb		 */
614268891Sjhb		vatpic_pin_accepted(&vatpic->atpic[0], 2);
615263035Stychon	} else {
616268891Sjhb		vatpic_pin_accepted(&vatpic->atpic[0], pin);
617263035Stychon	}
618263035Stychon
619263035Stychon	vatpic_notify_intr(vatpic);
620263035Stychon
621263035Stychon	VATPIC_UNLOCK(vatpic);
622263035Stychon}
623263035Stychon
624268891Sjhbstatic int
625268891Sjhbvatpic_read(struct vatpic *vatpic, struct atpic *atpic, bool in, int port,
626268891Sjhb	    int bytes, uint32_t *eax)
627263035Stychon{
628276429Sneel	int pin;
629276429Sneel
630268891Sjhb	VATPIC_LOCK(vatpic);
631263035Stychon
632268891Sjhb	if (atpic->poll) {
633276429Sneel		atpic->poll = 0;
634276429Sneel		pin = vatpic_get_highest_irrpin(atpic);
635276429Sneel		if (pin >= 0) {
636276429Sneel			vatpic_pin_accepted(atpic, pin);
637276429Sneel			*eax = 0x80 | pin;
638276429Sneel		} else {
639276429Sneel			*eax = 0;
640276429Sneel		}
641268891Sjhb	} else {
642268891Sjhb		if (port & ICU_IMR_OFFSET) {
643268891Sjhb			/* read interrrupt mask register */
644268891Sjhb			*eax = atpic->mask;
645263035Stychon		} else {
646268891Sjhb			if (atpic->rd_cmd_reg == OCW3_RIS) {
647268891Sjhb				/* read interrupt service register */
648268891Sjhb				*eax = atpic->service;
649263035Stychon			} else {
650268891Sjhb				/* read interrupt request register */
651268891Sjhb				*eax = atpic->request;
652263035Stychon			}
653263035Stychon		}
654263035Stychon	}
655263035Stychon
656268891Sjhb	VATPIC_UNLOCK(vatpic);
657263035Stychon
658268891Sjhb	return (0);
659268891Sjhb
660268891Sjhb}
661268891Sjhb
662268891Sjhbstatic int
663268891Sjhbvatpic_write(struct vatpic *vatpic, struct atpic *atpic, bool in, int port,
664268891Sjhb    int bytes, uint32_t *eax)
665268891Sjhb{
666268891Sjhb	int error;
667268891Sjhb	uint8_t val;
668268891Sjhb
669268891Sjhb	error = 0;
670268891Sjhb	val = *eax;
671268891Sjhb
672263035Stychon	VATPIC_LOCK(vatpic);
673263035Stychon
674268891Sjhb	if (port & ICU_IMR_OFFSET) {
675272388Sgrehan		switch (atpic->icw_num) {
676272388Sgrehan		case 2:
677272388Sgrehan			error = vatpic_icw2(vatpic, atpic, val);
678272388Sgrehan			break;
679272388Sgrehan		case 3:
680272388Sgrehan			error = vatpic_icw3(vatpic, atpic, val);
681272388Sgrehan			break;
682272388Sgrehan		case 4:
683272388Sgrehan			error = vatpic_icw4(vatpic, atpic, val);
684272388Sgrehan			break;
685272388Sgrehan		default:
686263035Stychon			error = vatpic_ocw1(vatpic, atpic, val);
687272388Sgrehan			break;
688263035Stychon		}
689263035Stychon	} else {
690263035Stychon		if (val & (1 << 4))
691263035Stychon			error = vatpic_icw1(vatpic, atpic, val);
692263035Stychon
693263035Stychon		if (atpic->ready) {
694263035Stychon			if (val & (1 << 3))
695263035Stychon				error = vatpic_ocw3(vatpic, atpic, val);
696263035Stychon			else
697263035Stychon				error = vatpic_ocw2(vatpic, atpic, val);
698263035Stychon		}
699263035Stychon	}
700263035Stychon
701263035Stychon	if (atpic->ready)
702263035Stychon		vatpic_notify_intr(vatpic);
703263035Stychon
704263035Stychon	VATPIC_UNLOCK(vatpic);
705263035Stychon
706263035Stychon	return (error);
707263035Stychon}
708263035Stychon
709263035Stychonint
710276429Sneelvatpic_master_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
711268891Sjhb    uint32_t *eax)
712263035Stychon{
713268891Sjhb	struct vatpic *vatpic;
714268891Sjhb	struct atpic *atpic;
715268891Sjhb
716268891Sjhb	vatpic = vm_atpic(vm);
717268891Sjhb	atpic = &vatpic->atpic[0];
718268891Sjhb
719268891Sjhb	if (bytes != 1)
720263035Stychon		return (-1);
721263035Stychon
722268891Sjhb	if (in) {
723268891Sjhb		return (vatpic_read(vatpic, atpic, in, port, bytes, eax));
724263035Stychon	}
725263035Stychon
726268891Sjhb	return (vatpic_write(vatpic, atpic, in, port, bytes, eax));
727263035Stychon}
728263035Stychon
729263035Stychonint
730276429Sneelvatpic_slave_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
731268891Sjhb    uint32_t *eax)
732263035Stychon{
733263035Stychon	struct vatpic *vatpic;
734268891Sjhb	struct atpic *atpic;
735268891Sjhb
736268891Sjhb	vatpic = vm_atpic(vm);
737268891Sjhb	atpic = &vatpic->atpic[1];
738268891Sjhb
739268891Sjhb	if (bytes != 1)
740268891Sjhb		return (-1);
741268891Sjhb
742268891Sjhb	if (in) {
743268891Sjhb		return (vatpic_read(vatpic, atpic, in, port, bytes, eax));
744268891Sjhb	}
745268891Sjhb
746268891Sjhb	return (vatpic_write(vatpic, atpic, in, port, bytes, eax));
747268891Sjhb}
748268891Sjhb
749268891Sjhbint
750276429Sneelvatpic_elc_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
751268891Sjhb    uint32_t *eax)
752268891Sjhb{
753268891Sjhb	struct vatpic *vatpic;
754263035Stychon	bool is_master;
755263035Stychon
756263035Stychon	vatpic = vm_atpic(vm);
757268891Sjhb	is_master = (port == IO_ELCR1);
758263035Stychon
759268891Sjhb	if (bytes != 1)
760263035Stychon		return (-1);
761263035Stychon
762268891Sjhb	VATPIC_LOCK(vatpic);
763268891Sjhb
764268891Sjhb	if (in) {
765263035Stychon		if (is_master)
766268891Sjhb			*eax = vatpic->elc[0];
767263035Stychon		else
768268891Sjhb			*eax = vatpic->elc[1];
769263035Stychon	} else {
770263035Stychon		/*
771263035Stychon		 * For the master PIC the cascade channel (IRQ2), the
772263035Stychon		 * heart beat timer (IRQ0), and the keyboard
773263035Stychon		 * controller (IRQ1) cannot be programmed for level
774263035Stychon		 * mode.
775263035Stychon		 *
776263035Stychon		 * For the slave PIC the real time clock (IRQ8) and
777263035Stychon		 * the floating point error interrupt (IRQ13) cannot
778263035Stychon		 * be programmed for level mode.
779263035Stychon		 */
780263035Stychon		if (is_master)
781268891Sjhb			vatpic->elc[0] = (*eax & 0xf8);
782263035Stychon		else
783268891Sjhb			vatpic->elc[1] = (*eax & 0xde);
784263035Stychon	}
785263035Stychon
786268891Sjhb	VATPIC_UNLOCK(vatpic);
787268891Sjhb
788263035Stychon	return (0);
789263035Stychon}
790263035Stychon
791263035Stychonstruct vatpic *
792263035Stychonvatpic_init(struct vm *vm)
793263035Stychon{
794263035Stychon	struct vatpic *vatpic;
795263035Stychon
796263035Stychon	vatpic = malloc(sizeof(struct vatpic), M_VATPIC, M_WAITOK | M_ZERO);
797263035Stychon	vatpic->vm = vm;
798263035Stychon
799263035Stychon	mtx_init(&vatpic->mtx, "vatpic lock", NULL, MTX_SPIN);
800263035Stychon
801263035Stychon	return (vatpic);
802263035Stychon}
803263035Stychon
804263035Stychonvoid
805263035Stychonvatpic_cleanup(struct vatpic *vatpic)
806263035Stychon{
807263035Stychon	free(vatpic, M_VATPIC);
808263035Stychon}
809