xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/vioapic.c (revision 32640292)
1bf21cd93STycho Nightingale /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
34c87aefeSPatrick Mooney  *
4bf21cd93STycho Nightingale  * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5bf21cd93STycho Nightingale  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6bf21cd93STycho Nightingale  * All rights reserved.
7bf21cd93STycho Nightingale  *
8bf21cd93STycho Nightingale  * Redistribution and use in source and binary forms, with or without
9bf21cd93STycho Nightingale  * modification, are permitted provided that the following conditions
10bf21cd93STycho Nightingale  * are met:
11bf21cd93STycho Nightingale  * 1. Redistributions of source code must retain the above copyright
12bf21cd93STycho Nightingale  *    notice, this list of conditions and the following disclaimer.
13bf21cd93STycho Nightingale  * 2. Redistributions in binary form must reproduce the above copyright
14bf21cd93STycho Nightingale  *    notice, this list of conditions and the following disclaimer in the
15bf21cd93STycho Nightingale  *    documentation and/or other materials provided with the distribution.
16bf21cd93STycho Nightingale  *
17bf21cd93STycho Nightingale  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18bf21cd93STycho Nightingale  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19bf21cd93STycho Nightingale  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20bf21cd93STycho Nightingale  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21bf21cd93STycho Nightingale  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22bf21cd93STycho Nightingale  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23bf21cd93STycho Nightingale  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24bf21cd93STycho Nightingale  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25bf21cd93STycho Nightingale  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26bf21cd93STycho Nightingale  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27bf21cd93STycho Nightingale  * SUCH DAMAGE.
28bf21cd93STycho Nightingale  */
29bf21cd93STycho Nightingale /*
30bf21cd93STycho Nightingale  * This file and its contents are supplied under the terms of the
31bf21cd93STycho Nightingale  * Common Development and Distribution License ("CDDL"), version 1.0.
32bf21cd93STycho Nightingale  * You may only use this file in accordance with the terms of version
33bf21cd93STycho Nightingale  * 1.0 of the CDDL.
34bf21cd93STycho Nightingale  *
35bf21cd93STycho Nightingale  * A full copy of the text of the CDDL should have accompanied this
36bf21cd93STycho Nightingale  * source.  A copy of the CDDL is also available via the Internet at
37bf21cd93STycho Nightingale  * http://www.illumos.org/license/CDDL.
38bf21cd93STycho Nightingale  *
39bf21cd93STycho Nightingale  * Copyright 2014 Pluribus Networks Inc.
404c87aefeSPatrick Mooney  * Copyright 2017 Joyent, Inc.
41c6282d59SPatrick Mooney  * Copyright 2021 Oxide Computer Company
42bf21cd93STycho Nightingale  */
43bf21cd93STycho Nightingale 
44bf21cd93STycho Nightingale #include <sys/cdefs.h>
45bf21cd93STycho Nightingale 
46bf21cd93STycho Nightingale #include <sys/param.h>
47bf21cd93STycho Nightingale #include <sys/queue.h>
48bf21cd93STycho Nightingale #include <sys/mutex.h>
49bf21cd93STycho Nightingale #include <sys/systm.h>
50bf21cd93STycho Nightingale #include <sys/kernel.h>
518130f8e1SPatrick Mooney #include <sys/kmem.h>
524c87aefeSPatrick Mooney #include <sys/cpuset.h>
53bf21cd93STycho Nightingale 
54bf21cd93STycho Nightingale #include <x86/apicreg.h>
55bf21cd93STycho Nightingale #include <machine/vmm.h>
56d515dd77SPatrick Mooney #include <sys/vmm_data.h>
57bf21cd93STycho Nightingale 
58bf21cd93STycho Nightingale #include "vmm_lapic.h"
59bf21cd93STycho Nightingale #include "vlapic.h"
60bf21cd93STycho Nightingale #include "vioapic.h"
61bf21cd93STycho Nightingale 
62bf21cd93STycho Nightingale #define	IOREGSEL	0x00
63bf21cd93STycho Nightingale #define	IOWIN		0x10
64bf21cd93STycho Nightingale 
654c87aefeSPatrick Mooney #define	REDIR_ENTRIES	32
66bf21cd93STycho Nightingale #define	RTBL_RO_BITS	((uint64_t)(IOART_REM_IRR | IOART_DELIVS))
67bf21cd93STycho Nightingale 
68c6282d59SPatrick Mooney struct ioapic_stats {
69c6282d59SPatrick Mooney 	uint64_t	is_interrupts;
70c6282d59SPatrick Mooney 	uint64_t	is_saturate_low;
71c6282d59SPatrick Mooney 	uint64_t	is_saturate_high;
72c6282d59SPatrick Mooney };
73c6282d59SPatrick Mooney 
74bf21cd93STycho Nightingale struct vioapic {
75bf21cd93STycho Nightingale 	struct vm	*vm;
76fb29dee0SPatrick Mooney 	kmutex_t	lock;
77bf21cd93STycho Nightingale 	uint32_t	id;
78bf21cd93STycho Nightingale 	uint32_t	ioregsel;
79bf21cd93STycho Nightingale 	struct {
80bf21cd93STycho Nightingale 		uint64_t reg;
81c6282d59SPatrick Mooney 		/*
82c6282d59SPatrick Mooney 		 * The sum of pin asserts (+1) and deasserts (-1) are tracked in
83c6282d59SPatrick Mooney 		 * 'acnt'.  It is clamped to prevent overflow or underflow
84c6282d59SPatrick Mooney 		 * should emulation consumers feed it an invalid set of
85c6282d59SPatrick Mooney 		 * transitions.
86c6282d59SPatrick Mooney 		 */
87c6282d59SPatrick Mooney 		uint_t acnt;
88bf21cd93STycho Nightingale 	} rtbl[REDIR_ENTRIES];
89c6282d59SPatrick Mooney 	struct ioapic_stats stats;
90bf21cd93STycho Nightingale };
91bf21cd93STycho Nightingale 
92fb29dee0SPatrick Mooney #define	VIOAPIC_LOCK(vioapic)		mutex_enter(&((vioapic)->lock))
93fb29dee0SPatrick Mooney #define	VIOAPIC_UNLOCK(vioapic)		mutex_exit(&((vioapic)->lock))
94fb29dee0SPatrick Mooney #define	VIOAPIC_LOCKED(vioapic)		MUTEX_HELD(&((vioapic)->lock))
95bf21cd93STycho Nightingale 
96bf21cd93STycho Nightingale 
97bf21cd93STycho Nightingale static void
vioapic_send_intr(struct vioapic * vioapic,int pin)98bf21cd93STycho Nightingale vioapic_send_intr(struct vioapic *vioapic, int pin)
99bf21cd93STycho Nightingale {
100bf21cd93STycho Nightingale 	int vector, delmode;
101bf21cd93STycho Nightingale 	uint32_t low, high, dest;
102bf21cd93STycho Nightingale 	bool level, phys;
103bf21cd93STycho Nightingale 
104c6282d59SPatrick Mooney 	VERIFY(pin >= 0 && pin < REDIR_ENTRIES);
105c6282d59SPatrick Mooney 	ASSERT(VIOAPIC_LOCKED(vioapic));
106bf21cd93STycho Nightingale 
107bf21cd93STycho Nightingale 	low = vioapic->rtbl[pin].reg;
108bf21cd93STycho Nightingale 	high = vioapic->rtbl[pin].reg >> 32;
109bf21cd93STycho Nightingale 
110bf21cd93STycho Nightingale 	if ((low & IOART_INTMASK) == IOART_INTMSET) {
111d4f59ae5SPatrick Mooney 		/* Pin is masked */
112bf21cd93STycho Nightingale 		return;
113bf21cd93STycho Nightingale 	}
114bf21cd93STycho Nightingale 
115bf21cd93STycho Nightingale 	phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
116bf21cd93STycho Nightingale 	delmode = low & IOART_DELMOD;
117bf21cd93STycho Nightingale 	level = low & IOART_TRGRLVL ? true : false;
1182b948146SAndy Fiddaman 	if (level) {
1192b948146SAndy Fiddaman 		if ((low & IOART_REM_IRR) != 0) {
120d4f59ae5SPatrick Mooney 			/* IRR already pending */
1212b948146SAndy Fiddaman 			return;
1222b948146SAndy Fiddaman 		}
123bf21cd93STycho Nightingale 		vioapic->rtbl[pin].reg |= IOART_REM_IRR;
1242b948146SAndy Fiddaman 	}
125bf21cd93STycho Nightingale 
126bf21cd93STycho Nightingale 	vector = low & IOART_INTVEC;
127bf21cd93STycho Nightingale 	dest = high >> APIC_ID_SHIFT;
128bf21cd93STycho Nightingale 	vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector);
129c6282d59SPatrick Mooney 	vioapic->stats.is_interrupts++;
130bf21cd93STycho Nightingale }
131bf21cd93STycho Nightingale 
132c6282d59SPatrick Mooney static int
vioapic_set_pinstate(struct vioapic * vioapic,int pin,bool newstate)133bf21cd93STycho Nightingale vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
134bf21cd93STycho Nightingale {
135c6282d59SPatrick Mooney 	uint_t oldcnt, newcnt;
136c6282d59SPatrick Mooney 	bool needintr = false;
137c6282d59SPatrick Mooney 	int err = 0;
138c6282d59SPatrick Mooney 
139c6282d59SPatrick Mooney 	VERIFY(pin >= 0 && pin < REDIR_ENTRIES);
140c6282d59SPatrick Mooney 	ASSERT(VIOAPIC_LOCKED(vioapic));
141c6282d59SPatrick Mooney 
142c6282d59SPatrick Mooney 	oldcnt = newcnt = vioapic->rtbl[pin].acnt;
143c6282d59SPatrick Mooney 	if (newstate) {
144c6282d59SPatrick Mooney 		if (newcnt != UINT_MAX) {
145c6282d59SPatrick Mooney 			newcnt++;
146c6282d59SPatrick Mooney 		} else {
147c6282d59SPatrick Mooney 			err = E2BIG;
148c6282d59SPatrick Mooney 			DTRACE_PROBE2(vioapic__sat_high,
149c6282d59SPatrick Mooney 			    struct vioapic *, vioapic, int, pin);
150c6282d59SPatrick Mooney 			vioapic->stats.is_saturate_high++;
151c6282d59SPatrick Mooney 		}
152c6282d59SPatrick Mooney 	} else {
153c6282d59SPatrick Mooney 		if (newcnt != 0) {
154c6282d59SPatrick Mooney 			newcnt--;
155c6282d59SPatrick Mooney 		} else {
156c6282d59SPatrick Mooney 			err = ERANGE;
157c6282d59SPatrick Mooney 			DTRACE_PROBE2(vioapic__sat_low,
158c6282d59SPatrick Mooney 			    struct vioapic *, vioapic, int, pin);
159c6282d59SPatrick Mooney 			vioapic->stats.is_saturate_low++;
160c6282d59SPatrick Mooney 		}
161bf21cd93STycho Nightingale 	}
162c6282d59SPatrick Mooney 	vioapic->rtbl[pin].acnt = newcnt;
163bf21cd93STycho Nightingale 
164bf21cd93STycho Nightingale 	if (oldcnt == 0 && newcnt == 1) {
165bf21cd93STycho Nightingale 		needintr = true;
166c6282d59SPatrick Mooney 		DTRACE_PROBE2(vioapic__assert, struct vioapic *, vioapic,
167c6282d59SPatrick Mooney 		    int, pin);
168bf21cd93STycho Nightingale 	} else if (oldcnt == 1 && newcnt == 0) {
169c6282d59SPatrick Mooney 		DTRACE_PROBE2(vioapic__deassert, struct vioapic *, vioapic,
170c6282d59SPatrick Mooney 		    int, pin);
171bf21cd93STycho Nightingale 	}
172bf21cd93STycho Nightingale 
173c6282d59SPatrick Mooney 	if (needintr) {
174bf21cd93STycho Nightingale 		vioapic_send_intr(vioapic, pin);
175c6282d59SPatrick Mooney 	}
176c6282d59SPatrick Mooney 	return (err);
177bf21cd93STycho Nightingale }
178bf21cd93STycho Nightingale 
179bf21cd93STycho Nightingale enum irqstate {
180bf21cd93STycho Nightingale 	IRQSTATE_ASSERT,
181bf21cd93STycho Nightingale 	IRQSTATE_DEASSERT,
182bf21cd93STycho Nightingale 	IRQSTATE_PULSE
183bf21cd93STycho Nightingale };
184bf21cd93STycho Nightingale 
185bf21cd93STycho Nightingale static int
vioapic_set_irqstate(struct vm * vm,int irq,enum irqstate irqstate)186bf21cd93STycho Nightingale vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
187bf21cd93STycho Nightingale {
188bf21cd93STycho Nightingale 	struct vioapic *vioapic;
189c6282d59SPatrick Mooney 	int err = 0;
190bf21cd93STycho Nightingale 
191bf21cd93STycho Nightingale 	if (irq < 0 || irq >= REDIR_ENTRIES)
192bf21cd93STycho Nightingale 		return (EINVAL);
193bf21cd93STycho Nightingale 
194bf21cd93STycho Nightingale 	vioapic = vm_ioapic(vm);
195bf21cd93STycho Nightingale 
196bf21cd93STycho Nightingale 	VIOAPIC_LOCK(vioapic);
197bf21cd93STycho Nightingale 	switch (irqstate) {
198bf21cd93STycho Nightingale 	case IRQSTATE_ASSERT:
199c6282d59SPatrick Mooney 		err = vioapic_set_pinstate(vioapic, irq, true);
200bf21cd93STycho Nightingale 		break;
201bf21cd93STycho Nightingale 	case IRQSTATE_DEASSERT:
202c6282d59SPatrick Mooney 		err = vioapic_set_pinstate(vioapic, irq, false);
203bf21cd93STycho Nightingale 		break;
204bf21cd93STycho Nightingale 	case IRQSTATE_PULSE:
205c6282d59SPatrick Mooney 		err = vioapic_set_pinstate(vioapic, irq, true);
206c6282d59SPatrick Mooney 		if (err == 0) {
207c6282d59SPatrick Mooney 			err = vioapic_set_pinstate(vioapic, irq, false);
208c6282d59SPatrick Mooney 		}
209bf21cd93STycho Nightingale 		break;
210bf21cd93STycho Nightingale 	default:
211bf21cd93STycho Nightingale 		panic("vioapic_set_irqstate: invalid irqstate %d", irqstate);
212bf21cd93STycho Nightingale 	}
213bf21cd93STycho Nightingale 	VIOAPIC_UNLOCK(vioapic);
214bf21cd93STycho Nightingale 
215c6282d59SPatrick Mooney 	return (err);
216bf21cd93STycho Nightingale }
217bf21cd93STycho Nightingale 
218bf21cd93STycho Nightingale int
vioapic_assert_irq(struct vm * vm,int irq)219bf21cd93STycho Nightingale vioapic_assert_irq(struct vm *vm, int irq)
220bf21cd93STycho Nightingale {
221bf21cd93STycho Nightingale 
222bf21cd93STycho Nightingale 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
223bf21cd93STycho Nightingale }
224bf21cd93STycho Nightingale 
225bf21cd93STycho Nightingale int
vioapic_deassert_irq(struct vm * vm,int irq)226bf21cd93STycho Nightingale vioapic_deassert_irq(struct vm *vm, int irq)
227bf21cd93STycho Nightingale {
228bf21cd93STycho Nightingale 
229bf21cd93STycho Nightingale 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
230bf21cd93STycho Nightingale }
231bf21cd93STycho Nightingale 
232bf21cd93STycho Nightingale int
vioapic_pulse_irq(struct vm * vm,int irq)233bf21cd93STycho Nightingale vioapic_pulse_irq(struct vm *vm, int irq)
234bf21cd93STycho Nightingale {
235bf21cd93STycho Nightingale 
236bf21cd93STycho Nightingale 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE));
237bf21cd93STycho Nightingale }
238bf21cd93STycho Nightingale 
239bf21cd93STycho Nightingale static uint32_t
vioapic_read(struct vioapic * vioapic,int vcpuid,uint32_t addr)240bf21cd93STycho Nightingale vioapic_read(struct vioapic *vioapic, int vcpuid, uint32_t addr)
241bf21cd93STycho Nightingale {
242bf21cd93STycho Nightingale 	int regnum, pin, rshift;
243bf21cd93STycho Nightingale 
244bf21cd93STycho Nightingale 	regnum = addr & 0xff;
245bf21cd93STycho Nightingale 	switch (regnum) {
246bf21cd93STycho Nightingale 	case IOAPIC_ID:
247bf21cd93STycho Nightingale 		return (vioapic->id);
248bf21cd93STycho Nightingale 		break;
249bf21cd93STycho Nightingale 	case IOAPIC_VER:
250bf21cd93STycho Nightingale 		return (((REDIR_ENTRIES - 1) << MAXREDIRSHIFT) | 0x11);
251bf21cd93STycho Nightingale 		break;
252bf21cd93STycho Nightingale 	case IOAPIC_ARB:
253bf21cd93STycho Nightingale 		return (vioapic->id);
254bf21cd93STycho Nightingale 		break;
255bf21cd93STycho Nightingale 	default:
256bf21cd93STycho Nightingale 		break;
257bf21cd93STycho Nightingale 	}
258bf21cd93STycho Nightingale 
259bf21cd93STycho Nightingale 	/* redirection table entries */
260bf21cd93STycho Nightingale 	if (regnum >= IOAPIC_REDTBL &&
261bf21cd93STycho Nightingale 	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
262bf21cd93STycho Nightingale 		pin = (regnum - IOAPIC_REDTBL) / 2;
263bf21cd93STycho Nightingale 		if ((regnum - IOAPIC_REDTBL) % 2)
264bf21cd93STycho Nightingale 			rshift = 32;
265bf21cd93STycho Nightingale 		else
266bf21cd93STycho Nightingale 			rshift = 0;
267bf21cd93STycho Nightingale 
268bf21cd93STycho Nightingale 		return (vioapic->rtbl[pin].reg >> rshift);
269bf21cd93STycho Nightingale 	}
270bf21cd93STycho Nightingale 
271bf21cd93STycho Nightingale 	return (0);
272bf21cd93STycho Nightingale }
273bf21cd93STycho Nightingale 
274bf21cd93STycho Nightingale static void
vioapic_write(struct vioapic * vioapic,int vcpuid,uint32_t addr,uint32_t data)275bf21cd93STycho Nightingale vioapic_write(struct vioapic *vioapic, int vcpuid, uint32_t addr, uint32_t data)
276bf21cd93STycho Nightingale {
277bf21cd93STycho Nightingale 	uint64_t data64, mask64;
278bf21cd93STycho Nightingale 	int regnum, pin, lshift;
279bf21cd93STycho Nightingale 
280bf21cd93STycho Nightingale 	regnum = addr & 0xff;
281bf21cd93STycho Nightingale 	switch (regnum) {
282bf21cd93STycho Nightingale 	case IOAPIC_ID:
283bf21cd93STycho Nightingale 		vioapic->id = data & APIC_ID_MASK;
284bf21cd93STycho Nightingale 		break;
285bf21cd93STycho Nightingale 	case IOAPIC_VER:
286bf21cd93STycho Nightingale 	case IOAPIC_ARB:
287bf21cd93STycho Nightingale 		/* readonly */
288bf21cd93STycho Nightingale 		break;
289bf21cd93STycho Nightingale 	default:
290bf21cd93STycho Nightingale 		break;
291bf21cd93STycho Nightingale 	}
292bf21cd93STycho Nightingale 
293bf21cd93STycho Nightingale 	/* redirection table entries */
294bf21cd93STycho Nightingale 	if (regnum >= IOAPIC_REDTBL &&
295bf21cd93STycho Nightingale 	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
296bf21cd93STycho Nightingale 		pin = (regnum - IOAPIC_REDTBL) / 2;
297bf21cd93STycho Nightingale 		if ((regnum - IOAPIC_REDTBL) % 2)
298bf21cd93STycho Nightingale 			lshift = 32;
299bf21cd93STycho Nightingale 		else
300bf21cd93STycho Nightingale 			lshift = 0;
301bf21cd93STycho Nightingale 
302bf21cd93STycho Nightingale 		data64 = (uint64_t)data << lshift;
303bf21cd93STycho Nightingale 		mask64 = (uint64_t)0xffffffff << lshift;
304bf21cd93STycho Nightingale 		vioapic->rtbl[pin].reg &= ~mask64 | RTBL_RO_BITS;
305bf21cd93STycho Nightingale 		vioapic->rtbl[pin].reg |= data64 & ~RTBL_RO_BITS;
306bf21cd93STycho Nightingale 
3072b948146SAndy Fiddaman 		/*
3082b948146SAndy Fiddaman 		 * Switching from level to edge triggering will clear the IRR
3092b948146SAndy Fiddaman 		 * bit. This is what FreeBSD will do in order to EOI an
3102b948146SAndy Fiddaman 		 * interrupt when the IO-APIC doesn't support targeted EOI (see
3112b948146SAndy Fiddaman 		 * _ioapic_eoi_source).
3122b948146SAndy Fiddaman 		 */
3132b948146SAndy Fiddaman 		if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGREDG &&
3142b948146SAndy Fiddaman 		    (vioapic->rtbl[pin].reg & IOART_REM_IRR) != 0)
3152b948146SAndy Fiddaman 			vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
3162b948146SAndy Fiddaman 
317bf21cd93STycho Nightingale 		/*
318bf21cd93STycho Nightingale 		 * Generate an interrupt if the following conditions are met:
3192b948146SAndy Fiddaman 		 * - pin trigger mode is level
320bf21cd93STycho Nightingale 		 * - pin level is asserted
321bf21cd93STycho Nightingale 		 */
3222b948146SAndy Fiddaman 		if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGRLVL &&
323bf21cd93STycho Nightingale 		    (vioapic->rtbl[pin].acnt > 0)) {
324bf21cd93STycho Nightingale 			vioapic_send_intr(vioapic, pin);
325bf21cd93STycho Nightingale 		}
326bf21cd93STycho Nightingale 	}
327bf21cd93STycho Nightingale }
328bf21cd93STycho Nightingale 
329bf21cd93STycho Nightingale static int
vioapic_mmio_rw(struct vioapic * vioapic,int vcpuid,uint64_t gpa,uint64_t * data,int size,bool doread)330bf21cd93STycho Nightingale vioapic_mmio_rw(struct vioapic *vioapic, int vcpuid, uint64_t gpa,
331bf21cd93STycho Nightingale     uint64_t *data, int size, bool doread)
332bf21cd93STycho Nightingale {
333bf21cd93STycho Nightingale 	uint64_t offset;
334bf21cd93STycho Nightingale 
335bf21cd93STycho Nightingale 	offset = gpa - VIOAPIC_BASE;
336bf21cd93STycho Nightingale 
337bf21cd93STycho Nightingale 	/*
338bf21cd93STycho Nightingale 	 * The IOAPIC specification allows 32-bit wide accesses to the
339bf21cd93STycho Nightingale 	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
340bf21cd93STycho Nightingale 	 */
341bf21cd93STycho Nightingale 	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
342bf21cd93STycho Nightingale 		if (doread)
343bf21cd93STycho Nightingale 			*data = 0;
344bf21cd93STycho Nightingale 		return (0);
345bf21cd93STycho Nightingale 	}
346bf21cd93STycho Nightingale 
347bf21cd93STycho Nightingale 	VIOAPIC_LOCK(vioapic);
348bf21cd93STycho Nightingale 	if (offset == IOREGSEL) {
349bf21cd93STycho Nightingale 		if (doread)
350bf21cd93STycho Nightingale 			*data = vioapic->ioregsel;
351bf21cd93STycho Nightingale 		else
352bf21cd93STycho Nightingale 			vioapic->ioregsel = *data;
353bf21cd93STycho Nightingale 	} else {
354bf21cd93STycho Nightingale 		if (doread) {
355bf21cd93STycho Nightingale 			*data = vioapic_read(vioapic, vcpuid,
356bf21cd93STycho Nightingale 			    vioapic->ioregsel);
357bf21cd93STycho Nightingale 		} else {
358bf21cd93STycho Nightingale 			vioapic_write(vioapic, vcpuid, vioapic->ioregsel,
359bf21cd93STycho Nightingale 			    *data);
360bf21cd93STycho Nightingale 		}
361bf21cd93STycho Nightingale 	}
362bf21cd93STycho Nightingale 	VIOAPIC_UNLOCK(vioapic);
363bf21cd93STycho Nightingale 
364bf21cd93STycho Nightingale 	return (0);
365bf21cd93STycho Nightingale }
366bf21cd93STycho Nightingale 
367bf21cd93STycho Nightingale int
vioapic_mmio_read(struct vm * vm,int vcpuid,uint64_t gpa,uint64_t * rval,int size)3683e1c5f3aSPatrick Mooney vioapic_mmio_read(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
3693e1c5f3aSPatrick Mooney     int size)
370bf21cd93STycho Nightingale {
371bf21cd93STycho Nightingale 	int error;
372bf21cd93STycho Nightingale 	struct vioapic *vioapic;
373bf21cd93STycho Nightingale 
374bf21cd93STycho Nightingale 	vioapic = vm_ioapic(vm);
375bf21cd93STycho Nightingale 	error = vioapic_mmio_rw(vioapic, vcpuid, gpa, rval, size, true);
376bf21cd93STycho Nightingale 	return (error);
377bf21cd93STycho Nightingale }
378bf21cd93STycho Nightingale 
379bf21cd93STycho Nightingale int
vioapic_mmio_write(struct vm * vm,int vcpuid,uint64_t gpa,uint64_t wval,int size)3803e1c5f3aSPatrick Mooney vioapic_mmio_write(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t wval,
3813e1c5f3aSPatrick Mooney     int size)
382bf21cd93STycho Nightingale {
383bf21cd93STycho Nightingale 	int error;
384bf21cd93STycho Nightingale 	struct vioapic *vioapic;
385bf21cd93STycho Nightingale 
386bf21cd93STycho Nightingale 	vioapic = vm_ioapic(vm);
387bf21cd93STycho Nightingale 	error = vioapic_mmio_rw(vioapic, vcpuid, gpa, &wval, size, false);
388bf21cd93STycho Nightingale 	return (error);
389bf21cd93STycho Nightingale }
390bf21cd93STycho Nightingale 
391bf21cd93STycho Nightingale void
vioapic_process_eoi(struct vm * vm,int vcpuid,int vector)392bf21cd93STycho Nightingale vioapic_process_eoi(struct vm *vm, int vcpuid, int vector)
393bf21cd93STycho Nightingale {
394bf21cd93STycho Nightingale 	struct vioapic *vioapic;
395bf21cd93STycho Nightingale 	int pin;
396bf21cd93STycho Nightingale 
397bf21cd93STycho Nightingale 	KASSERT(vector >= 0 && vector < 256,
398bf21cd93STycho Nightingale 	    ("vioapic_process_eoi: invalid vector %d", vector));
399bf21cd93STycho Nightingale 
400bf21cd93STycho Nightingale 	vioapic = vm_ioapic(vm);
401bf21cd93STycho Nightingale 
402bf21cd93STycho Nightingale 	/*
403bf21cd93STycho Nightingale 	 * XXX keep track of the pins associated with this vector instead
404bf21cd93STycho Nightingale 	 * of iterating on every single pin each time.
405bf21cd93STycho Nightingale 	 */
406bf21cd93STycho Nightingale 	VIOAPIC_LOCK(vioapic);
407bf21cd93STycho Nightingale 	for (pin = 0; pin < REDIR_ENTRIES; pin++) {
408bf21cd93STycho Nightingale 		if ((vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0)
409bf21cd93STycho Nightingale 			continue;
410bf21cd93STycho Nightingale 		if ((vioapic->rtbl[pin].reg & IOART_INTVEC) != vector)
411bf21cd93STycho Nightingale 			continue;
412bf21cd93STycho Nightingale 		vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
413bf21cd93STycho Nightingale 		if (vioapic->rtbl[pin].acnt > 0) {
414d4f59ae5SPatrick Mooney 			/* Pin asserted at EOI */
415bf21cd93STycho Nightingale 			vioapic_send_intr(vioapic, pin);
416bf21cd93STycho Nightingale 		}
417bf21cd93STycho Nightingale 	}
418bf21cd93STycho Nightingale 	VIOAPIC_UNLOCK(vioapic);
419bf21cd93STycho Nightingale }
420bf21cd93STycho Nightingale 
421bf21cd93STycho Nightingale struct vioapic *
vioapic_init(struct vm * vm)422bf21cd93STycho Nightingale vioapic_init(struct vm *vm)
423bf21cd93STycho Nightingale {
424bf21cd93STycho Nightingale 	int i;
425bf21cd93STycho Nightingale 	struct vioapic *vioapic;
426bf21cd93STycho Nightingale 
4278130f8e1SPatrick Mooney 	vioapic = kmem_zalloc(sizeof (struct vioapic), KM_SLEEP);
428bf21cd93STycho Nightingale 
429bf21cd93STycho Nightingale 	vioapic->vm = vm;
430fb29dee0SPatrick Mooney 	mutex_init(&vioapic->lock, NULL, MUTEX_ADAPTIVE, NULL);
431bf21cd93STycho Nightingale 
432bf21cd93STycho Nightingale 	/* Initialize all redirection entries to mask all interrupts */
433bf21cd93STycho Nightingale 	for (i = 0; i < REDIR_ENTRIES; i++)
434bf21cd93STycho Nightingale 		vioapic->rtbl[i].reg = 0x0001000000010000UL;
435bf21cd93STycho Nightingale 
436bf21cd93STycho Nightingale 	return (vioapic);
437bf21cd93STycho Nightingale }
438bf21cd93STycho Nightingale 
439bf21cd93STycho Nightingale void
vioapic_cleanup(struct vioapic * vioapic)440bf21cd93STycho Nightingale vioapic_cleanup(struct vioapic *vioapic)
441bf21cd93STycho Nightingale {
442fb29dee0SPatrick Mooney 	mutex_destroy(&vioapic->lock);
4438130f8e1SPatrick Mooney 	kmem_free(vioapic, sizeof (*vioapic));
444bf21cd93STycho Nightingale }
445bf21cd93STycho Nightingale 
446bf21cd93STycho Nightingale int
vioapic_pincount(struct vm * vm)447bf21cd93STycho Nightingale vioapic_pincount(struct vm *vm)
448bf21cd93STycho Nightingale {
449bf21cd93STycho Nightingale 
450bf21cd93STycho Nightingale 	return (REDIR_ENTRIES);
451bf21cd93STycho Nightingale }
452d515dd77SPatrick Mooney 
453d515dd77SPatrick Mooney static int
vioapic_data_read(void * datap,const vmm_data_req_t * req)454d515dd77SPatrick Mooney vioapic_data_read(void *datap, const vmm_data_req_t *req)
455d515dd77SPatrick Mooney {
456d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_class, ==, VDC_IOAPIC);
457d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_version, ==, 1);
458a77feb92SPatrick Mooney 	VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_ioapic_v1));
459d515dd77SPatrick Mooney 
460d515dd77SPatrick Mooney 	struct vioapic *vioapic = datap;
461d515dd77SPatrick Mooney 	struct vdi_ioapic_v1 *out = req->vdr_data;
462d515dd77SPatrick Mooney 
463d515dd77SPatrick Mooney 	VIOAPIC_LOCK(vioapic);
464d515dd77SPatrick Mooney 	out->vi_id = vioapic->id;
465d515dd77SPatrick Mooney 	out->vi_reg_sel = vioapic->ioregsel;
466d515dd77SPatrick Mooney 	for (uint_t i = 0; i < REDIR_ENTRIES; i++) {
467d515dd77SPatrick Mooney 		out->vi_pin_reg[i] = vioapic->rtbl[i].reg;
468d515dd77SPatrick Mooney 		out->vi_pin_level[i] = vioapic->rtbl[i].acnt;
469d515dd77SPatrick Mooney 	}
470d515dd77SPatrick Mooney 	VIOAPIC_UNLOCK(vioapic);
471d515dd77SPatrick Mooney 
472d515dd77SPatrick Mooney 	return (0);
473d515dd77SPatrick Mooney }
474d515dd77SPatrick Mooney 
475d515dd77SPatrick Mooney static int
vioapic_data_write(void * datap,const vmm_data_req_t * req)476d515dd77SPatrick Mooney vioapic_data_write(void *datap, const vmm_data_req_t *req)
477d515dd77SPatrick Mooney {
478d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_class, ==, VDC_IOAPIC);
479d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_version, ==, 1);
480a77feb92SPatrick Mooney 	VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_ioapic_v1));
481d515dd77SPatrick Mooney 
482d515dd77SPatrick Mooney 	struct vioapic *vioapic = datap;
483d515dd77SPatrick Mooney 	const struct vdi_ioapic_v1 *src = req->vdr_data;
484d515dd77SPatrick Mooney 
485d515dd77SPatrick Mooney 	VIOAPIC_LOCK(vioapic);
486d515dd77SPatrick Mooney 	vioapic->id = src->vi_id;
487d515dd77SPatrick Mooney 	vioapic->ioregsel = src->vi_reg_sel;
488d515dd77SPatrick Mooney 	for (uint_t i = 0; i < REDIR_ENTRIES; i++) {
489d515dd77SPatrick Mooney 		vioapic->rtbl[i].reg = src->vi_pin_reg[i] & ~RTBL_RO_BITS;
490d515dd77SPatrick Mooney 		vioapic->rtbl[i].acnt = src->vi_pin_level[i];
491d515dd77SPatrick Mooney 	}
492d515dd77SPatrick Mooney 	VIOAPIC_UNLOCK(vioapic);
493d515dd77SPatrick Mooney 
494d515dd77SPatrick Mooney 	return (0);
495d515dd77SPatrick Mooney }
496d515dd77SPatrick Mooney 
497d515dd77SPatrick Mooney static const vmm_data_version_entry_t ioapic_v1 = {
498d515dd77SPatrick Mooney 	.vdve_class = VDC_IOAPIC,
499d515dd77SPatrick Mooney 	.vdve_version = 1,
500d515dd77SPatrick Mooney 	.vdve_len_expect = sizeof (struct vdi_ioapic_v1),
501d515dd77SPatrick Mooney 	.vdve_readf = vioapic_data_read,
502d515dd77SPatrick Mooney 	.vdve_writef = vioapic_data_write,
503d515dd77SPatrick Mooney };
504d515dd77SPatrick Mooney VMM_DATA_VERSION(ioapic_v1);
505