ioapic.c revision 241744
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: projects/bhyve/usr.sbin/bhyve/ioapic.c 241744 2012-10-19 18:11:17Z grehan $
27239045Sneel */
28239045Sneel
29239045Sneel#include <sys/cdefs.h>
30239045Sneel__FBSDID("$FreeBSD: projects/bhyve/usr.sbin/bhyve/ioapic.c 241744 2012-10-19 18:11:17Z grehan $");
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>
40239045Sneel
41239045Sneel#include <vmmapi.h>
42239045Sneel
43239045Sneel#include "inout.h"
44241744Sgrehan#include "mem.h"
45239045Sneel#include "instruction_emul.h"
46239045Sneel#include "fbsdrun.h"
47239045Sneel
48239045Sneel#include <stdio.h>
49239045Sneel
50239045Sneel#define	IOAPIC_PADDR	0xFEC00000
51239045Sneel
52239045Sneel#define	IOREGSEL	0x00
53239045Sneel#define	IOWIN		0x10
54239045Sneel
55239045Sneel#define	REDIR_ENTRIES	16
56239045Sneel#define	INTR_ASSERTED(ioapic, pin)	((ioapic)->pinstate[(pin)] == true)
57239045Sneel
58239045Sneelstruct ioapic {
59239045Sneel	int		inited;
60239045Sneel	uint32_t	id;
61239045Sneel	uint64_t	redtbl[REDIR_ENTRIES];
62239045Sneel	bool		pinstate[REDIR_ENTRIES];
63239045Sneel
64239045Sneel	uintptr_t	paddr;		/* gpa where the ioapic is mapped */
65239045Sneel	uint32_t	ioregsel;
66239045Sneel	struct memory_region *region;
67239045Sneel};
68239045Sneel
69239045Sneelstatic struct ioapic ioapics[1];	/* only a single ioapic for now */
70239045Sneel
71241744Sgrehanstatic int ioapic_region_read(struct ioapic *ioapic, uintptr_t paddr,
72241744Sgrehan				int size, uint64_t *data);
73241744Sgrehanstatic int ioapic_region_write(struct ioapic *ioapic, uintptr_t paddr,
74241744Sgrehan				int size, uint64_t data);
75241744Sgrehanstatic int ioapic_region_handler(struct vmctx *vm, int vcpu, int dir,
76241744Sgrehan				 uintptr_t paddr, int size, uint64_t *val,
77241744Sgrehan				 void *arg1, long arg2);
78239045Sneel
79239045Sneelstatic void
80239045Sneelioapic_set_pinstate(struct vmctx *ctx, int pin, bool newstate)
81239045Sneel{
82239045Sneel	int vector, apicid, vcpu;
83239045Sneel	uint32_t low, high;
84239045Sneel	struct ioapic *ioapic;
85239045Sneel
86239045Sneel	ioapic = &ioapics[0];		/* assume a single ioapic */
87239045Sneel
88239045Sneel	if (pin < 0 || pin >= REDIR_ENTRIES)
89239045Sneel		return;
90239045Sneel
91239045Sneel	/* Nothing to do if interrupt pin has not changed state */
92239045Sneel	if (ioapic->pinstate[pin] == newstate)
93239045Sneel		return;
94239045Sneel
95239045Sneel	ioapic->pinstate[pin] = newstate;	/* record it */
96239045Sneel
97239045Sneel	/* Nothing to do if interrupt pin is deasserted */
98239045Sneel	if (!INTR_ASSERTED(ioapic, pin))
99239045Sneel		return;
100239045Sneel
101239045Sneel	/*
102239045Sneel	 * XXX
103239045Sneel	 * We only deal with:
104239045Sneel	 * - edge triggered interrupts
105239045Sneel	 * - physical destination mode
106239045Sneel	 * - fixed delivery mode
107239045Sneel	 */
108239045Sneel	low = ioapic->redtbl[pin];
109239045Sneel	high = ioapic->redtbl[pin] >> 32;
110239045Sneel	if ((low & IOART_INTMASK) == IOART_INTMCLR &&
111239045Sneel	    (low & IOART_TRGRMOD) == IOART_TRGREDG &&
112239045Sneel	    (low & IOART_DESTMOD) == IOART_DESTPHY &&
113239045Sneel	    (low & IOART_DELMOD) == IOART_DELFIXED) {
114239045Sneel		vector = low & IOART_INTVEC;
115239045Sneel		apicid = high >> APIC_ID_SHIFT;
116239045Sneel		if (apicid != 0xff) {
117239045Sneel			/* unicast */
118239045Sneel			vcpu = vm_apicid2vcpu(ctx, apicid);
119239045Sneel			vm_lapic_irq(ctx, vcpu, vector);
120239045Sneel		} else {
121239045Sneel			/* broadcast */
122239045Sneel			vcpu = 0;
123239045Sneel			while (vcpu < guest_ncpus) {
124239045Sneel				vm_lapic_irq(ctx, vcpu, vector);
125239045Sneel				vcpu++;
126239045Sneel			}
127239045Sneel		}
128239045Sneel	}
129239045Sneel}
130239045Sneel
131239045Sneelvoid
132239045Sneelioapic_deassert_pin(struct vmctx *ctx, int pin)
133239045Sneel{
134239045Sneel	ioapic_set_pinstate(ctx, pin, false);
135239045Sneel}
136239045Sneel
137239045Sneelvoid
138239045Sneelioapic_assert_pin(struct vmctx *ctx, int pin)
139239045Sneel{
140239045Sneel	ioapic_set_pinstate(ctx, pin, true);
141239045Sneel}
142239045Sneel
143239045Sneelvoid
144239045Sneelioapic_init(int which)
145239045Sneel{
146241744Sgrehan	struct mem_range memp;
147241744Sgrehan	struct ioapic *ioapic;
148241744Sgrehan	int error;
149239045Sneel	int i;
150239045Sneel
151239045Sneel	assert(which == 0);
152239045Sneel
153239045Sneel	ioapic = &ioapics[which];
154239045Sneel	assert(ioapic->inited == 0);
155239045Sneel
156239045Sneel	bzero(ioapic, sizeof(struct ioapic));
157239045Sneel
158239045Sneel	/* Initialize all redirection entries to mask all interrupts */
159239045Sneel	for (i = 0; i < REDIR_ENTRIES; i++)
160239045Sneel		ioapic->redtbl[i] = 0x0001000000010000UL;
161239045Sneel
162239045Sneel	ioapic->paddr = IOAPIC_PADDR;
163239045Sneel
164241744Sgrehan	/* Register emulated memory region */
165241744Sgrehan	memp.name = "ioapic";
166241744Sgrehan	memp.flags = MEM_F_RW;
167241744Sgrehan	memp.handler = ioapic_region_handler;
168241744Sgrehan	memp.arg1 = ioapic;
169241744Sgrehan	memp.arg2 = which;
170241744Sgrehan	memp.base = ioapic->paddr;
171241744Sgrehan	memp.size = sizeof(struct IOAPIC);
172241744Sgrehan	error = register_mem(&memp);
173241744Sgrehan
174241744Sgrehan	assert (error == 0);
175241744Sgrehan
176239045Sneel	ioapic->inited = 1;
177239045Sneel}
178239045Sneel
179239045Sneelstatic uint32_t
180239045Sneelioapic_read(struct ioapic *ioapic, uint32_t addr)
181239045Sneel{
182239045Sneel	int regnum, pin, rshift;
183239045Sneel
184239045Sneel	assert(ioapic->inited);
185239045Sneel
186239045Sneel	regnum = addr & 0xff;
187239045Sneel	switch (regnum) {
188239045Sneel	case IOAPIC_ID:
189239045Sneel		return (ioapic->id);
190239045Sneel		break;
191239045Sneel	case IOAPIC_VER:
192239045Sneel		return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11);
193239045Sneel		break;
194239045Sneel	case IOAPIC_ARB:
195239045Sneel		return (ioapic->id);
196239045Sneel		break;
197239045Sneel	default:
198239045Sneel		break;
199239045Sneel	}
200239045Sneel
201239045Sneel	/* redirection table entries */
202239045Sneel	if (regnum >= IOAPIC_REDTBL &&
203239045Sneel	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
204239045Sneel		pin = (regnum - IOAPIC_REDTBL) / 2;
205239045Sneel		if ((regnum - IOAPIC_REDTBL) % 2)
206239045Sneel			rshift = 32;
207239045Sneel		else
208239045Sneel			rshift = 0;
209239045Sneel
210239045Sneel		return (ioapic->redtbl[pin] >> rshift);
211239045Sneel	}
212239045Sneel
213239045Sneel	return (0);
214239045Sneel}
215239045Sneel
216239045Sneelstatic void
217239045Sneelioapic_write(struct ioapic *ioapic, uint32_t addr, uint32_t data)
218239045Sneel{
219239045Sneel	int regnum, pin, lshift;
220239045Sneel
221239045Sneel	assert(ioapic->inited);
222239045Sneel
223239045Sneel	regnum = addr & 0xff;
224239045Sneel	switch (regnum) {
225239045Sneel	case IOAPIC_ID:
226239045Sneel		ioapic->id = data & APIC_ID_MASK;
227239045Sneel		break;
228239045Sneel	case IOAPIC_VER:
229239045Sneel	case IOAPIC_ARB:
230239045Sneel		/* readonly */
231239045Sneel		break;
232239045Sneel	default:
233239045Sneel		break;
234239045Sneel	}
235239045Sneel
236239045Sneel	/* redirection table entries */
237239045Sneel	if (regnum >= IOAPIC_REDTBL &&
238239045Sneel	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
239239045Sneel		pin = (regnum - IOAPIC_REDTBL) / 2;
240239045Sneel		if ((regnum - IOAPIC_REDTBL) % 2)
241239045Sneel			lshift = 32;
242239045Sneel		else
243239045Sneel			lshift = 0;
244239045Sneel
245239045Sneel		ioapic->redtbl[pin] &= ~((uint64_t)0xffffffff << lshift);
246239045Sneel		ioapic->redtbl[pin] |= ((uint64_t)data << lshift);
247239045Sneel	}
248239045Sneel}
249239045Sneel
250239045Sneelstatic int
251241744Sgrehanioapic_region_read(struct ioapic *ioapic, uintptr_t paddr, int size,
252241744Sgrehan		   uint64_t *data)
253239045Sneel{
254241744Sgrehan	int offset;
255239045Sneel
256239045Sneel	offset = paddr - ioapic->paddr;
257239045Sneel
258239045Sneel	/*
259239045Sneel	 * The IOAPIC specification allows 32-bit wide accesses to the
260239045Sneel	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
261239045Sneel	 */
262239045Sneel	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
263239045Sneel#if 1
264239045Sneel		printf("invalid access to ioapic%d: size %d, offset %d\n",
265241744Sgrehan		       (int)(ioapic - ioapics), size, offset);
266239045Sneel#endif
267239045Sneel		*data = 0;
268239045Sneel		return (0);
269239045Sneel	}
270239045Sneel
271239045Sneel	if (offset == IOREGSEL)
272239045Sneel		*data = ioapic->ioregsel;
273239045Sneel	else
274239045Sneel		*data = ioapic_read(ioapic, ioapic->ioregsel);
275239045Sneel
276239045Sneel	return (0);
277239045Sneel}
278239045Sneel
279239045Sneelstatic int
280241744Sgrehanioapic_region_write(struct ioapic *ioapic, uintptr_t paddr, int size,
281241744Sgrehan		    uint64_t data)
282239045Sneel{
283241744Sgrehan	int offset;
284239045Sneel
285239045Sneel	offset = paddr - ioapic->paddr;
286239045Sneel
287239045Sneel	/*
288239045Sneel	 * The ioapic specification allows 32-bit wide accesses to the
289239045Sneel	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
290239045Sneel	 */
291239045Sneel	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
292239045Sneel#if 1
293239045Sneel		printf("invalid access to ioapic%d: size %d, offset %d\n",
294241744Sgrehan		       (int)(ioapic - ioapics), size, offset);
295239045Sneel#endif
296239045Sneel		return (0);
297239045Sneel	}
298239045Sneel
299239045Sneel	if (offset == IOREGSEL)
300239045Sneel		ioapic->ioregsel = data;
301239045Sneel	else
302239045Sneel		ioapic_write(ioapic, ioapic->ioregsel, data);
303239045Sneel
304239045Sneel	return (0);
305239045Sneel}
306241744Sgrehan
307241744Sgrehanstatic int
308241744Sgrehanioapic_region_handler(struct vmctx *vm, int vcpu, int dir, uintptr_t paddr,
309241744Sgrehan		      int size, uint64_t *val, void *arg1, long arg2)
310241744Sgrehan{
311241744Sgrehan	struct ioapic *ioapic;
312241744Sgrehan	int which;
313241744Sgrehan
314241744Sgrehan	ioapic = arg1;
315241744Sgrehan	which = arg2;
316241744Sgrehan
317241744Sgrehan	assert(ioapic == &ioapics[which]);
318241744Sgrehan
319241744Sgrehan	if (dir == MEM_F_READ)
320241744Sgrehan		ioapic_region_read(ioapic, paddr, size, val);
321241744Sgrehan	else
322241744Sgrehan		ioapic_region_write(ioapic, paddr, size, *val);
323241744Sgrehan
324241744Sgrehan	return (0);
325241744Sgrehan}
326