1239045Sneel/*-
2239045Sneel * Copyright (c) 2012 NetApp, Inc.
3239045Sneel * All rights reserved.
4239045Sneel *
5239045Sneel * Redistribution and use in source and binary forms, with or without
6239045Sneel * modification, are permitted provided that the following conditions
7239045Sneel * are met:
8239045Sneel * 1. Redistributions of source code must retain the above copyright
9239045Sneel *    notice, this list of conditions and the following disclaimer.
10239045Sneel * 2. Redistributions in binary form must reproduce the above copyright
11239045Sneel *    notice, this list of conditions and the following disclaimer in the
12239045Sneel *    documentation and/or other materials provided with the distribution.
13239045Sneel *
14239045Sneel * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15239045Sneel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16239045Sneel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17239045Sneel * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18239045Sneel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19239045Sneel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20239045Sneel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21239045Sneel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22239045Sneel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23239045Sneel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24239045Sneel * SUCH DAMAGE.
25239045Sneel *
26239045Sneel * $FreeBSD$
27239045Sneel */
28239045Sneel
29239045Sneel#include <sys/cdefs.h>
30239045Sneel__FBSDID("$FreeBSD$");
31239045Sneel
32239045Sneel#include <sys/types.h>
33239045Sneel
34239045Sneel#include <x86/apicreg.h>
35239045Sneel#include <machine/vmm.h>
36239045Sneel
37239045Sneel#include <string.h>
38239045Sneel#include <assert.h>
39239045Sneel#include <stdbool.h>
40257397Sgrehan#include <pthread.h>
41239045Sneel
42239045Sneel#include <vmmapi.h>
43239045Sneel
44239045Sneel#include "inout.h"
45241744Sgrehan#include "mem.h"
46244167Sgrehan#include "bhyverun.h"
47239045Sneel
48239045Sneel#include <stdio.h>
49239045Sneel
50257397Sgrehanstatic uint64_t ioapic_clearpend, ioapic_togglepend, ioapic_setpend;
51257397Sgrehan
52239045Sneel#define	IOAPIC_PADDR	0xFEC00000
53239045Sneel
54239045Sneel#define	IOREGSEL	0x00
55239045Sneel#define	IOWIN		0x10
56239045Sneel
57239045Sneel#define	REDIR_ENTRIES	16
58257397Sgrehan#define	INTR_ASSERTED(ioapic, pin)	\
59257397Sgrehan	((ioapic)->rtbl[(pin)].pinstate == true)
60239045Sneel
61239045Sneelstruct ioapic {
62239045Sneel	int		inited;
63239045Sneel	uint32_t	id;
64257397Sgrehan	struct {
65257397Sgrehan		uint64_t reg;
66257397Sgrehan		bool     pinstate;
67257397Sgrehan		bool     pending;
68257397Sgrehan	} rtbl[REDIR_ENTRIES];
69239045Sneel
70239045Sneel	uintptr_t	paddr;		/* gpa where the ioapic is mapped */
71239045Sneel	uint32_t	ioregsel;
72239045Sneel	struct memory_region *region;
73257397Sgrehan	pthread_mutex_t	mtx;
74239045Sneel};
75239045Sneel
76239045Sneelstatic struct ioapic ioapics[1];	/* only a single ioapic for now */
77239045Sneel
78257397Sgrehanstatic int ioapic_region_read(struct vmctx *vm, struct ioapic *ioapic,
79257397Sgrehan    uintptr_t paddr, int size, uint64_t *data);
80257397Sgrehanstatic int ioapic_region_write(struct vmctx *vm, struct ioapic *ioapic,
81257397Sgrehan    uintptr_t paddr, int size, uint64_t data);
82241744Sgrehanstatic int ioapic_region_handler(struct vmctx *vm, int vcpu, int dir,
83257397Sgrehan    uintptr_t paddr, int size, uint64_t *val, void *arg1, long arg2);
84239045Sneel
85239045Sneelstatic void
86239045Sneelioapic_set_pinstate(struct vmctx *ctx, int pin, bool newstate)
87239045Sneel{
88239045Sneel	int vector, apicid, vcpu;
89239045Sneel	uint32_t low, high;
90239045Sneel	struct ioapic *ioapic;
91239045Sneel
92239045Sneel	ioapic = &ioapics[0];		/* assume a single ioapic */
93239045Sneel
94239045Sneel	/* Nothing to do if interrupt pin has not changed state */
95257397Sgrehan	if (ioapic->rtbl[pin].pinstate == newstate)
96239045Sneel		return;
97239045Sneel
98257397Sgrehan	ioapic->rtbl[pin].pinstate = newstate;	/* record it */
99239045Sneel
100239045Sneel	/* Nothing to do if interrupt pin is deasserted */
101239045Sneel	if (!INTR_ASSERTED(ioapic, pin))
102239045Sneel		return;
103239045Sneel
104239045Sneel	/*
105239045Sneel	 * XXX
106239045Sneel	 * We only deal with:
107239045Sneel	 * - edge triggered interrupts
108239045Sneel	 * - fixed delivery mode
109255293Sgrehan	 *  Level-triggered sources will work so long as there is
110255292Sgrehan	 * no sharing.
111239045Sneel	 */
112257397Sgrehan	low = ioapic->rtbl[pin].reg;
113257397Sgrehan	high = ioapic->rtbl[pin].reg >> 32;
114239045Sneel	if ((low & IOART_INTMASK) == IOART_INTMCLR &&
115239045Sneel	    (low & IOART_DESTMOD) == IOART_DESTPHY &&
116239045Sneel	    (low & IOART_DELMOD) == IOART_DELFIXED) {
117239045Sneel		vector = low & IOART_INTVEC;
118239045Sneel		apicid = high >> APIC_ID_SHIFT;
119239045Sneel		if (apicid != 0xff) {
120239045Sneel			/* unicast */
121239045Sneel			vcpu = vm_apicid2vcpu(ctx, apicid);
122239045Sneel			vm_lapic_irq(ctx, vcpu, vector);
123239045Sneel		} else {
124239045Sneel			/* broadcast */
125239045Sneel			vcpu = 0;
126239045Sneel			while (vcpu < guest_ncpus) {
127239045Sneel				vm_lapic_irq(ctx, vcpu, vector);
128239045Sneel				vcpu++;
129239045Sneel			}
130239045Sneel		}
131257397Sgrehan	} else if ((low & IOART_INTMASK) != IOART_INTMCLR &&
132257397Sgrehan		   low & IOART_TRGRLVL) {
133257397Sgrehan		/*
134257397Sgrehan		 * For level-triggered interrupts that have been
135257397Sgrehan		 * masked, set the pending bit so that an interrupt
136257397Sgrehan		 * will be generated on unmask and if the level is
137257397Sgrehan		 * still asserted
138257397Sgrehan		 */
139257397Sgrehan		ioapic_setpend++;
140257397Sgrehan		ioapic->rtbl[pin].pending = true;
141239045Sneel	}
142239045Sneel}
143239045Sneel
144257397Sgrehanstatic void
145257397Sgrehanioapic_set_pinstate_locked(struct vmctx *ctx, int pin, bool newstate)
146257397Sgrehan{
147257397Sgrehan	struct ioapic *ioapic;
148257397Sgrehan
149257397Sgrehan	if (pin < 0 || pin >= REDIR_ENTRIES)
150257397Sgrehan		return;
151257397Sgrehan
152257397Sgrehan	ioapic = &ioapics[0];
153257397Sgrehan
154257397Sgrehan	pthread_mutex_lock(&ioapic->mtx);
155257397Sgrehan	ioapic_set_pinstate(ctx, pin, newstate);
156257397Sgrehan	pthread_mutex_unlock(&ioapic->mtx);
157257397Sgrehan}
158257397Sgrehan
159257397Sgrehan/*
160257397Sgrehan * External entry points require locking
161257397Sgrehan */
162239045Sneelvoid
163239045Sneelioapic_deassert_pin(struct vmctx *ctx, int pin)
164239045Sneel{
165257397Sgrehan	ioapic_set_pinstate_locked(ctx, pin, false);
166239045Sneel}
167239045Sneel
168239045Sneelvoid
169239045Sneelioapic_assert_pin(struct vmctx *ctx, int pin)
170239045Sneel{
171257397Sgrehan	ioapic_set_pinstate_locked(ctx, pin, true);
172239045Sneel}
173239045Sneel
174239045Sneelvoid
175239045Sneelioapic_init(int which)
176239045Sneel{
177241744Sgrehan	struct mem_range memp;
178241744Sgrehan	struct ioapic *ioapic;
179241744Sgrehan	int error;
180239045Sneel	int i;
181239045Sneel
182239045Sneel	assert(which == 0);
183239045Sneel
184239045Sneel	ioapic = &ioapics[which];
185239045Sneel	assert(ioapic->inited == 0);
186239045Sneel
187239045Sneel	bzero(ioapic, sizeof(struct ioapic));
188239045Sneel
189257397Sgrehan	pthread_mutex_init(&ioapic->mtx, NULL);
190257397Sgrehan
191239045Sneel	/* Initialize all redirection entries to mask all interrupts */
192239045Sneel	for (i = 0; i < REDIR_ENTRIES; i++)
193257397Sgrehan		ioapic->rtbl[i].reg = 0x0001000000010000UL;
194239045Sneel
195239045Sneel	ioapic->paddr = IOAPIC_PADDR;
196239045Sneel
197241744Sgrehan	/* Register emulated memory region */
198241744Sgrehan	memp.name = "ioapic";
199241744Sgrehan	memp.flags = MEM_F_RW;
200241744Sgrehan	memp.handler = ioapic_region_handler;
201241744Sgrehan	memp.arg1 = ioapic;
202241744Sgrehan	memp.arg2 = which;
203241744Sgrehan	memp.base = ioapic->paddr;
204241744Sgrehan	memp.size = sizeof(struct IOAPIC);
205241744Sgrehan	error = register_mem(&memp);
206241744Sgrehan
207241744Sgrehan	assert (error == 0);
208241744Sgrehan
209239045Sneel	ioapic->inited = 1;
210239045Sneel}
211239045Sneel
212239045Sneelstatic uint32_t
213239045Sneelioapic_read(struct ioapic *ioapic, uint32_t addr)
214239045Sneel{
215239045Sneel	int regnum, pin, rshift;
216239045Sneel
217239045Sneel	assert(ioapic->inited);
218239045Sneel
219239045Sneel	regnum = addr & 0xff;
220239045Sneel	switch (regnum) {
221239045Sneel	case IOAPIC_ID:
222239045Sneel		return (ioapic->id);
223239045Sneel		break;
224239045Sneel	case IOAPIC_VER:
225239045Sneel		return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11);
226239045Sneel		break;
227239045Sneel	case IOAPIC_ARB:
228239045Sneel		return (ioapic->id);
229239045Sneel		break;
230239045Sneel	default:
231239045Sneel		break;
232239045Sneel	}
233239045Sneel
234239045Sneel	/* redirection table entries */
235239045Sneel	if (regnum >= IOAPIC_REDTBL &&
236239045Sneel	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
237239045Sneel		pin = (regnum - IOAPIC_REDTBL) / 2;
238239045Sneel		if ((regnum - IOAPIC_REDTBL) % 2)
239239045Sneel			rshift = 32;
240239045Sneel		else
241239045Sneel			rshift = 0;
242239045Sneel
243257397Sgrehan		return (ioapic->rtbl[pin].reg >> rshift);
244239045Sneel	}
245239045Sneel
246239045Sneel	return (0);
247239045Sneel}
248239045Sneel
249239045Sneelstatic void
250257397Sgrehanioapic_write(struct vmctx *vm, struct ioapic *ioapic, uint32_t addr,
251257397Sgrehan    uint32_t data)
252239045Sneel{
253239045Sneel	int regnum, pin, lshift;
254239045Sneel
255239045Sneel	assert(ioapic->inited);
256239045Sneel
257239045Sneel	regnum = addr & 0xff;
258239045Sneel	switch (regnum) {
259239045Sneel	case IOAPIC_ID:
260239045Sneel		ioapic->id = data & APIC_ID_MASK;
261239045Sneel		break;
262239045Sneel	case IOAPIC_VER:
263239045Sneel	case IOAPIC_ARB:
264239045Sneel		/* readonly */
265239045Sneel		break;
266239045Sneel	default:
267239045Sneel		break;
268239045Sneel	}
269239045Sneel
270239045Sneel	/* redirection table entries */
271239045Sneel	if (regnum >= IOAPIC_REDTBL &&
272239045Sneel	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
273239045Sneel		pin = (regnum - IOAPIC_REDTBL) / 2;
274239045Sneel		if ((regnum - IOAPIC_REDTBL) % 2)
275239045Sneel			lshift = 32;
276239045Sneel		else
277239045Sneel			lshift = 0;
278239045Sneel
279257397Sgrehan		ioapic->rtbl[pin].reg &= ~((uint64_t)0xffffffff << lshift);
280257397Sgrehan		ioapic->rtbl[pin].reg |= ((uint64_t)data << lshift);
281257397Sgrehan
282257397Sgrehan		if (ioapic->rtbl[pin].pending &&
283257397Sgrehan		    ((ioapic->rtbl[pin].reg & IOART_INTMASK) ==
284257397Sgrehan		         IOART_INTMCLR)) {
285257397Sgrehan			ioapic->rtbl[pin].pending = false;
286257397Sgrehan			ioapic_clearpend++;
287257397Sgrehan			/*
288257397Sgrehan			 * Inject the deferred level-triggered int if it is
289257397Sgrehan			 * still asserted. Simulate by toggling the pin
290257397Sgrehan			 * off and then on.
291257397Sgrehan			 */
292257397Sgrehan			if (ioapic->rtbl[pin].pinstate == true) {
293257397Sgrehan				ioapic_togglepend++;
294257397Sgrehan				ioapic_set_pinstate(vm, pin, false);
295257397Sgrehan				ioapic_set_pinstate(vm, pin, true);
296257397Sgrehan			}
297257397Sgrehan		}
298239045Sneel	}
299239045Sneel}
300239045Sneel
301239045Sneelstatic int
302257397Sgrehanioapic_region_read(struct vmctx *vm, struct ioapic *ioapic, uintptr_t paddr,
303257397Sgrehan    int size, uint64_t *data)
304239045Sneel{
305241744Sgrehan	int offset;
306239045Sneel
307239045Sneel	offset = paddr - ioapic->paddr;
308239045Sneel
309239045Sneel	/*
310239045Sneel	 * The IOAPIC specification allows 32-bit wide accesses to the
311239045Sneel	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
312239045Sneel	 */
313239045Sneel	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
314239045Sneel#if 1
315239045Sneel		printf("invalid access to ioapic%d: size %d, offset %d\n",
316241744Sgrehan		       (int)(ioapic - ioapics), size, offset);
317239045Sneel#endif
318239045Sneel		*data = 0;
319239045Sneel		return (0);
320239045Sneel	}
321239045Sneel
322239045Sneel	if (offset == IOREGSEL)
323239045Sneel		*data = ioapic->ioregsel;
324239045Sneel	else
325239045Sneel		*data = ioapic_read(ioapic, ioapic->ioregsel);
326239045Sneel
327239045Sneel	return (0);
328239045Sneel}
329239045Sneel
330239045Sneelstatic int
331257397Sgrehanioapic_region_write(struct vmctx *vm, struct ioapic *ioapic, uintptr_t paddr,
332257397Sgrehan    int size, uint64_t data)
333239045Sneel{
334241744Sgrehan	int offset;
335239045Sneel
336239045Sneel	offset = paddr - ioapic->paddr;
337239045Sneel
338239045Sneel	/*
339239045Sneel	 * The ioapic specification allows 32-bit wide accesses to the
340239045Sneel	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
341239045Sneel	 */
342239045Sneel	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
343239045Sneel#if 1
344239045Sneel		printf("invalid access to ioapic%d: size %d, offset %d\n",
345241744Sgrehan		       (int)(ioapic - ioapics), size, offset);
346239045Sneel#endif
347239045Sneel		return (0);
348239045Sneel	}
349239045Sneel
350239045Sneel	if (offset == IOREGSEL)
351239045Sneel		ioapic->ioregsel = data;
352239045Sneel	else
353257397Sgrehan		ioapic_write(vm, ioapic, ioapic->ioregsel, data);
354239045Sneel
355239045Sneel	return (0);
356239045Sneel}
357241744Sgrehan
358241744Sgrehanstatic int
359241744Sgrehanioapic_region_handler(struct vmctx *vm, int vcpu, int dir, uintptr_t paddr,
360257397Sgrehan    int size, uint64_t *val, void *arg1, long arg2)
361241744Sgrehan{
362241744Sgrehan	struct ioapic *ioapic;
363241744Sgrehan	int which;
364241744Sgrehan
365241744Sgrehan	ioapic = arg1;
366241744Sgrehan	which = arg2;
367241744Sgrehan
368241744Sgrehan	assert(ioapic == &ioapics[which]);
369241744Sgrehan
370257397Sgrehan	pthread_mutex_lock(&ioapic->mtx);
371241744Sgrehan	if (dir == MEM_F_READ)
372257397Sgrehan		ioapic_region_read(vm, ioapic, paddr, size, val);
373241744Sgrehan	else
374257397Sgrehan		ioapic_region_write(vm, ioapic, paddr, size, *val);
375257397Sgrehan	pthread_mutex_unlock(&ioapic->mtx);
376241744Sgrehan
377241744Sgrehan	return (0);
378241744Sgrehan}
379