pci_emul.c revision 246190
1221828Sgrehan/*-
2221828Sgrehan * Copyright (c) 2011 NetApp, Inc.
3221828Sgrehan * All rights reserved.
4221828Sgrehan *
5221828Sgrehan * Redistribution and use in source and binary forms, with or without
6221828Sgrehan * modification, are permitted provided that the following conditions
7221828Sgrehan * are met:
8221828Sgrehan * 1. Redistributions of source code must retain the above copyright
9221828Sgrehan *    notice, this list of conditions and the following disclaimer.
10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11221828Sgrehan *    notice, this list of conditions and the following disclaimer in the
12221828Sgrehan *    documentation and/or other materials provided with the distribution.
13221828Sgrehan *
14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17221828Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24221828Sgrehan * SUCH DAMAGE.
25221828Sgrehan *
26221828Sgrehan * $FreeBSD: head/usr.sbin/bhyve/pci_emul.c 246190 2013-02-01 02:41:47Z neel $
27221828Sgrehan */
28221828Sgrehan
29221828Sgrehan#include <sys/cdefs.h>
30221828Sgrehan__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_emul.c 246190 2013-02-01 02:41:47Z neel $");
31221828Sgrehan
32221828Sgrehan#include <sys/param.h>
33221828Sgrehan#include <sys/linker_set.h>
34221828Sgrehan
35221828Sgrehan#include <ctype.h>
36221828Sgrehan#include <stdio.h>
37221828Sgrehan#include <stdlib.h>
38221828Sgrehan#include <string.h>
39221828Sgrehan#include <strings.h>
40221828Sgrehan#include <assert.h>
41221828Sgrehan
42221828Sgrehan#include <machine/vmm.h>
43221828Sgrehan#include <vmmapi.h>
44221828Sgrehan
45244167Sgrehan#include "bhyverun.h"
46221828Sgrehan#include "inout.h"
47241744Sgrehan#include "mem.h"
48242131Sgrehan#include "mptbl.h"
49221828Sgrehan#include "pci_emul.h"
50239045Sneel#include "ioapic.h"
51221828Sgrehan
52221828Sgrehan#define CONF1_ADDR_PORT    0x0cf8
53221828Sgrehan#define CONF1_DATA_PORT    0x0cfc
54221828Sgrehan
55221828Sgrehan#define	CFGWRITE(pi,off,val,b)						\
56221828Sgrehando {									\
57221828Sgrehan	if ((b) == 1) {							\
58221828Sgrehan		pci_set_cfgdata8((pi),(off),(val));			\
59221828Sgrehan	} else if ((b) == 2) {						\
60221828Sgrehan		pci_set_cfgdata16((pi),(off),(val));			\
61221828Sgrehan	} else {							\
62221828Sgrehan		pci_set_cfgdata32((pi),(off),(val));			\
63221828Sgrehan	}								\
64221828Sgrehan} while (0)
65221828Sgrehan
66239085Sneel#define MAXSLOTS	(PCI_SLOTMAX + 1)
67239085Sneel#define	MAXFUNCS	(PCI_FUNCMAX + 1)
68221828Sgrehan
69221828Sgrehanstatic struct slotinfo {
70234938Sgrehan	char	*si_name;
71234938Sgrehan	char	*si_param;
72221828Sgrehan	struct pci_devinst *si_devi;
73234938Sgrehan	int	si_legacy;
74239085Sneel} pci_slotinfo[MAXSLOTS][MAXFUNCS];
75221828Sgrehan
76221828Sgrehan/*
77234938Sgrehan * Used to keep track of legacy interrupt owners/requestors
78234938Sgrehan */
79234938Sgrehan#define NLIRQ		16
80234938Sgrehan
81234938Sgrehanstatic struct lirqinfo {
82234938Sgrehan	int	li_generic;
83234938Sgrehan	int	li_acount;
84234938Sgrehan	struct pci_devinst *li_owner;	/* XXX should be a list */
85234938Sgrehan} lirq[NLIRQ];
86234938Sgrehan
87221828SgrehanSET_DECLARE(pci_devemu_set, struct pci_devemu);
88221828Sgrehan
89221828Sgrehanstatic uint64_t pci_emul_iobase;
90221828Sgrehanstatic uint64_t pci_emul_membase32;
91221828Sgrehanstatic uint64_t pci_emul_membase64;
92221828Sgrehan
93221828Sgrehan#define	PCI_EMUL_IOBASE		0x2000
94221828Sgrehan#define	PCI_EMUL_IOLIMIT	0x10000
95221828Sgrehan
96221828Sgrehan#define	PCI_EMUL_MEMBASE32	(lomem_sz)
97221828Sgrehan#define	PCI_EMUL_MEMLIMIT32	0xE0000000		/* 3.5GB */
98221828Sgrehan
99221828Sgrehan#define	PCI_EMUL_MEMBASE64	0xD000000000UL
100221828Sgrehan#define	PCI_EMUL_MEMLIMIT64	0xFD00000000UL
101221828Sgrehan
102221828Sgrehanstatic int pci_emul_devices;
103221828Sgrehan
104221828Sgrehan/*
105221828Sgrehan * I/O access
106221828Sgrehan */
107221828Sgrehan
108221828Sgrehan/*
109221828Sgrehan * Slot options are in the form:
110221828Sgrehan *
111239085Sneel *  <slot>[:<func>],<emul>[,<config>]
112221828Sgrehan *
113221828Sgrehan *  slot is 0..31
114239085Sneel *  func is 0..7
115221828Sgrehan *  emul is a string describing the type of PCI device e.g. virtio-net
116221828Sgrehan *  config is an optional string, depending on the device, that can be
117221828Sgrehan *  used for configuration.
118221828Sgrehan *   Examples are:
119221828Sgrehan *     1,virtio-net,tap0
120239085Sneel *     3:0,dummy
121221828Sgrehan */
122221828Sgrehanstatic void
123221828Sgrehanpci_parse_slot_usage(char *aopt)
124221828Sgrehan{
125221828Sgrehan	printf("Invalid PCI slot info field \"%s\"\n", aopt);
126221828Sgrehan	free(aopt);
127221828Sgrehan}
128221828Sgrehan
129221828Sgrehanvoid
130234938Sgrehanpci_parse_slot(char *opt, int legacy)
131221828Sgrehan{
132239085Sneel	char *slot, *func, *emul, *config;
133221828Sgrehan	char *str, *cpy;
134239085Sneel	int snum, fnum;
135221828Sgrehan
136221828Sgrehan	str = cpy = strdup(opt);
137239085Sneel
138221828Sgrehan	config = NULL;
139221828Sgrehan
140239085Sneel	if (strchr(str, ':') != NULL) {
141239085Sneel		slot = strsep(&str, ":");
142239085Sneel		func = strsep(&str, ",");
143239085Sneel	} else {
144239085Sneel		slot = strsep(&str, ",");
145239085Sneel		func = NULL;
146239085Sneel	}
147239085Sneel
148221828Sgrehan	emul = strsep(&str, ",");
149221828Sgrehan	if (str != NULL) {
150221828Sgrehan		config = strsep(&str, ",");
151221828Sgrehan	}
152221828Sgrehan
153221828Sgrehan	if (emul == NULL) {
154221828Sgrehan		pci_parse_slot_usage(cpy);
155221828Sgrehan		return;
156221828Sgrehan	}
157221828Sgrehan
158221828Sgrehan	snum = atoi(slot);
159239085Sneel	fnum = func ? atoi(func) : 0;
160239085Sneel	if (snum < 0 || snum >= MAXSLOTS || fnum < 0 || fnum >= MAXFUNCS) {
161221828Sgrehan		pci_parse_slot_usage(cpy);
162221828Sgrehan	} else {
163239085Sneel		pci_slotinfo[snum][fnum].si_name = emul;
164239085Sneel		pci_slotinfo[snum][fnum].si_param = config;
165239085Sneel		pci_slotinfo[snum][fnum].si_legacy = legacy;
166221828Sgrehan	}
167221828Sgrehan}
168221828Sgrehan
169221828Sgrehanstatic int
170246109Sneelpci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset)
171246109Sneel{
172246109Sneel
173246109Sneel	if (offset < pi->pi_msix.pba_offset)
174246109Sneel		return (0);
175246109Sneel
176246109Sneel	if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) {
177246109Sneel		return (0);
178246109Sneel	}
179246109Sneel
180246109Sneel	return (1);
181246109Sneel}
182246109Sneel
183246109Sneelint
184246109Sneelpci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size,
185246109Sneel		     uint64_t value)
186246109Sneel{
187246109Sneel	int msix_entry_offset;
188246109Sneel	int tab_index;
189246109Sneel	char *dest;
190246109Sneel
191246109Sneel	/* support only 4 or 8 byte writes */
192246109Sneel	if (size != 4 && size != 8)
193246109Sneel		return (-1);
194246109Sneel
195246109Sneel	/*
196246109Sneel	 * Return if table index is beyond what device supports
197246109Sneel	 */
198246109Sneel	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
199246109Sneel	if (tab_index >= pi->pi_msix.table_count)
200246109Sneel		return (-1);
201246109Sneel
202246109Sneel	msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
203246109Sneel
204246109Sneel	/* support only aligned writes */
205246109Sneel	if ((msix_entry_offset % size) != 0)
206246109Sneel		return (-1);
207246109Sneel
208246109Sneel	dest = (char *)(pi->pi_msix.table + tab_index);
209246109Sneel	dest += msix_entry_offset;
210246109Sneel
211246109Sneel	if (size == 4)
212246109Sneel		*((uint32_t *)dest) = value;
213246109Sneel	else
214246109Sneel		*((uint64_t *)dest) = value;
215246109Sneel
216246109Sneel	return (0);
217246109Sneel}
218246109Sneel
219246109Sneeluint64_t
220246109Sneelpci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size)
221246109Sneel{
222246109Sneel	char *dest;
223246109Sneel	int msix_entry_offset;
224246109Sneel	int tab_index;
225246109Sneel	uint64_t retval = ~0;
226246109Sneel
227246109Sneel	/* support only 4 or 8 byte reads */
228246109Sneel	if (size != 4 && size != 8)
229246109Sneel		return (retval);
230246109Sneel
231246109Sneel	msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
232246109Sneel
233246109Sneel	/* support only aligned reads */
234246109Sneel	if ((msix_entry_offset % size) != 0) {
235246109Sneel		return (retval);
236246109Sneel	}
237246109Sneel
238246109Sneel	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
239246109Sneel
240246109Sneel	if (tab_index < pi->pi_msix.table_count) {
241246109Sneel		/* valid MSI-X Table access */
242246109Sneel		dest = (char *)(pi->pi_msix.table + tab_index);
243246109Sneel		dest += msix_entry_offset;
244246109Sneel
245246109Sneel		if (size == 4)
246246109Sneel			retval = *((uint32_t *)dest);
247246109Sneel		else
248246109Sneel			retval = *((uint64_t *)dest);
249246109Sneel	} else if (pci_valid_pba_offset(pi, offset)) {
250246109Sneel		/* return 0 for PBA access */
251246109Sneel		retval = 0;
252246109Sneel	}
253246109Sneel
254246109Sneel	return (retval);
255246109Sneel}
256246109Sneel
257246190Sneelint
258246190Sneelpci_msix_table_bar(struct pci_devinst *pi)
259246190Sneel{
260246190Sneel
261246190Sneel	if (pi->pi_msix.table != NULL)
262246190Sneel		return (pi->pi_msix.table_bar);
263246190Sneel	else
264246190Sneel		return (-1);
265246190Sneel}
266246190Sneel
267246190Sneelint
268246190Sneelpci_msix_pba_bar(struct pci_devinst *pi)
269246190Sneel{
270246190Sneel
271246190Sneel	if (pi->pi_msix.table != NULL)
272246190Sneel		return (pi->pi_msix.pba_bar);
273246190Sneel	else
274246190Sneel		return (-1);
275246190Sneel}
276246190Sneel
277246109Sneelstatic int
278241744Sgrehanpci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
279241744Sgrehan		    uint32_t *eax, void *arg)
280221828Sgrehan{
281221828Sgrehan	struct pci_devinst *pdi = arg;
282221828Sgrehan	struct pci_devemu *pe = pdi->pi_d;
283241744Sgrehan	uint64_t offset;
284241744Sgrehan	int i;
285221828Sgrehan
286221828Sgrehan	for (i = 0; i <= PCI_BARMAX; i++) {
287221828Sgrehan		if (pdi->pi_bar[i].type == PCIBAR_IO &&
288221828Sgrehan		    port >= pdi->pi_bar[i].addr &&
289246109Sneel		    port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) {
290221828Sgrehan			offset = port - pdi->pi_bar[i].addr;
291221828Sgrehan			if (in)
292241744Sgrehan				*eax = (*pe->pe_barread)(ctx, vcpu, pdi, i,
293241744Sgrehan							 offset, bytes);
294221828Sgrehan			else
295241744Sgrehan				(*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset,
296241744Sgrehan						   bytes, *eax);
297221828Sgrehan			return (0);
298221828Sgrehan		}
299221828Sgrehan	}
300221828Sgrehan	return (-1);
301221828Sgrehan}
302221828Sgrehan
303221828Sgrehanstatic int
304241744Sgrehanpci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
305241744Sgrehan		     int size, uint64_t *val, void *arg1, long arg2)
306241744Sgrehan{
307241744Sgrehan	struct pci_devinst *pdi = arg1;
308241744Sgrehan	struct pci_devemu *pe = pdi->pi_d;
309241744Sgrehan	uint64_t offset;
310241744Sgrehan	int bidx = (int) arg2;
311241744Sgrehan
312241744Sgrehan	assert(bidx <= PCI_BARMAX);
313241744Sgrehan	assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 ||
314241744Sgrehan	       pdi->pi_bar[bidx].type == PCIBAR_MEM64);
315241744Sgrehan	assert(addr >= pdi->pi_bar[bidx].addr &&
316241744Sgrehan	       addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size);
317241744Sgrehan
318241744Sgrehan	offset = addr - pdi->pi_bar[bidx].addr;
319241744Sgrehan
320241744Sgrehan	if (dir == MEM_F_WRITE)
321241744Sgrehan		(*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, size, *val);
322241744Sgrehan	else
323241744Sgrehan		*val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset, size);
324241744Sgrehan
325241744Sgrehan	return (0);
326241744Sgrehan}
327241744Sgrehan
328241744Sgrehan
329241744Sgrehanstatic int
330221828Sgrehanpci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size,
331221828Sgrehan			uint64_t *addr)
332221828Sgrehan{
333221828Sgrehan	uint64_t base;
334221828Sgrehan
335221828Sgrehan	assert((size & (size - 1)) == 0);	/* must be a power of 2 */
336221828Sgrehan
337221828Sgrehan	base = roundup2(*baseptr, size);
338221828Sgrehan
339221828Sgrehan	if (base + size <= limit) {
340221828Sgrehan		*addr = base;
341221828Sgrehan		*baseptr = base + size;
342221828Sgrehan		return (0);
343221828Sgrehan	} else
344221828Sgrehan		return (-1);
345221828Sgrehan}
346221828Sgrehan
347221828Sgrehanint
348241744Sgrehanpci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type,
349241744Sgrehan		   uint64_t size)
350221828Sgrehan{
351241744Sgrehan
352241744Sgrehan	return (pci_emul_alloc_pbar(pdi, idx, 0, type, size));
353241744Sgrehan}
354241744Sgrehan
355241744Sgrehanint
356241744Sgrehanpci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase,
357241744Sgrehan		    enum pcibar_type type, uint64_t size)
358241744Sgrehan{
359221828Sgrehan	int i, error;
360221828Sgrehan	uint64_t *baseptr, limit, addr, mask, lobits, bar;
361221828Sgrehan	struct inout_port iop;
362241744Sgrehan	struct mem_range memp;
363221828Sgrehan
364221828Sgrehan	assert(idx >= 0 && idx <= PCI_BARMAX);
365221828Sgrehan
366221828Sgrehan	if ((size & (size - 1)) != 0)
367221828Sgrehan		size = 1UL << flsl(size);	/* round up to a power of 2 */
368221828Sgrehan
369221828Sgrehan	switch (type) {
370221828Sgrehan	case PCIBAR_NONE:
371221828Sgrehan		baseptr = NULL;
372221828Sgrehan		addr = mask = lobits = 0;
373221828Sgrehan		break;
374221828Sgrehan	case PCIBAR_IO:
375239085Sneel		if (hostbase &&
376239085Sneel		    pci_slotinfo[pdi->pi_slot][pdi->pi_func].si_legacy) {
377234938Sgrehan			assert(hostbase < PCI_EMUL_IOBASE);
378234938Sgrehan			baseptr = &hostbase;
379234938Sgrehan		} else {
380234938Sgrehan			baseptr = &pci_emul_iobase;
381234938Sgrehan		}
382221828Sgrehan		limit = PCI_EMUL_IOLIMIT;
383221828Sgrehan		mask = PCIM_BAR_IO_BASE;
384221828Sgrehan		lobits = PCIM_BAR_IO_SPACE;
385221828Sgrehan		break;
386221828Sgrehan	case PCIBAR_MEM64:
387221828Sgrehan		/*
388221828Sgrehan		 * XXX
389221828Sgrehan		 * Some drivers do not work well if the 64-bit BAR is allocated
390221828Sgrehan		 * above 4GB. Allow for this by allocating small requests under
391221828Sgrehan		 * 4GB unless then allocation size is larger than some arbitrary
392221828Sgrehan		 * number (32MB currently).
393221828Sgrehan		 */
394221828Sgrehan		if (size > 32 * 1024 * 1024) {
395221828Sgrehan			/*
396221828Sgrehan			 * XXX special case for device requiring peer-peer DMA
397221828Sgrehan			 */
398221828Sgrehan			if (size == 0x100000000UL)
399221828Sgrehan				baseptr = &hostbase;
400221828Sgrehan			else
401221828Sgrehan				baseptr = &pci_emul_membase64;
402221828Sgrehan			limit = PCI_EMUL_MEMLIMIT64;
403221828Sgrehan			mask = PCIM_BAR_MEM_BASE;
404221828Sgrehan			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
405221828Sgrehan				 PCIM_BAR_MEM_PREFETCH;
406221828Sgrehan			break;
407239086Sneel		} else {
408239086Sneel			baseptr = &pci_emul_membase32;
409239086Sneel			limit = PCI_EMUL_MEMLIMIT32;
410239086Sneel			mask = PCIM_BAR_MEM_BASE;
411239086Sneel			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
412221828Sgrehan		}
413239086Sneel		break;
414221828Sgrehan	case PCIBAR_MEM32:
415221828Sgrehan		baseptr = &pci_emul_membase32;
416221828Sgrehan		limit = PCI_EMUL_MEMLIMIT32;
417221828Sgrehan		mask = PCIM_BAR_MEM_BASE;
418221828Sgrehan		lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
419221828Sgrehan		break;
420221828Sgrehan	default:
421221828Sgrehan		printf("pci_emul_alloc_base: invalid bar type %d\n", type);
422221828Sgrehan		assert(0);
423221828Sgrehan	}
424221828Sgrehan
425221828Sgrehan	if (baseptr != NULL) {
426221828Sgrehan		error = pci_emul_alloc_resource(baseptr, limit, size, &addr);
427221828Sgrehan		if (error != 0)
428221828Sgrehan			return (error);
429221828Sgrehan	}
430221828Sgrehan
431221828Sgrehan	pdi->pi_bar[idx].type = type;
432221828Sgrehan	pdi->pi_bar[idx].addr = addr;
433221828Sgrehan	pdi->pi_bar[idx].size = size;
434221828Sgrehan
435221828Sgrehan	/* Initialize the BAR register in config space */
436221828Sgrehan	bar = (addr & mask) | lobits;
437221828Sgrehan	pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar);
438221828Sgrehan
439221828Sgrehan	if (type == PCIBAR_MEM64) {
440221828Sgrehan		assert(idx + 1 <= PCI_BARMAX);
441221828Sgrehan		pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64;
442221828Sgrehan		pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
443221828Sgrehan	}
444221828Sgrehan
445221828Sgrehan	/* add a handler to intercept accesses to the I/O bar */
446221828Sgrehan	if (type == PCIBAR_IO) {
447221828Sgrehan		iop.name = pdi->pi_name;
448221828Sgrehan		iop.flags = IOPORT_F_INOUT;
449241744Sgrehan		iop.handler = pci_emul_io_handler;
450221828Sgrehan		iop.arg = pdi;
451221828Sgrehan
452221828Sgrehan		for (i = 0; i < size; i++) {
453221828Sgrehan			iop.port = addr + i;
454221828Sgrehan			register_inout(&iop);
455221828Sgrehan		}
456241744Sgrehan	} else if (type == PCIBAR_MEM32 || type == PCIBAR_MEM64) {
457241744Sgrehan		/* add memory bar intercept handler */
458241744Sgrehan		memp.name = pdi->pi_name;
459241744Sgrehan		memp.flags = MEM_F_RW;
460241744Sgrehan		memp.base = addr;
461241744Sgrehan		memp.size = size;
462241744Sgrehan		memp.handler = pci_emul_mem_handler;
463241744Sgrehan		memp.arg1 = pdi;
464241744Sgrehan		memp.arg2 = idx;
465241744Sgrehan
466241744Sgrehan		error = register_mem(&memp);
467241744Sgrehan		assert(error == 0);
468221828Sgrehan	}
469221828Sgrehan
470221828Sgrehan	return (0);
471221828Sgrehan}
472221828Sgrehan
473221828Sgrehan#define	CAP_START_OFFSET	0x40
474221828Sgrehanstatic int
475221828Sgrehanpci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen)
476221828Sgrehan{
477221828Sgrehan	int i, capoff, capid, reallen;
478221828Sgrehan	uint16_t sts;
479221828Sgrehan
480221828Sgrehan	static u_char endofcap[4] = {
481221828Sgrehan		PCIY_RESERVED, 0, 0, 0
482221828Sgrehan	};
483221828Sgrehan
484221828Sgrehan	assert(caplen > 0 && capdata[0] != PCIY_RESERVED);
485221828Sgrehan
486221828Sgrehan	reallen = roundup2(caplen, 4);		/* dword aligned */
487221828Sgrehan
488221828Sgrehan	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
489221828Sgrehan	if ((sts & PCIM_STATUS_CAPPRESENT) == 0) {
490221828Sgrehan		capoff = CAP_START_OFFSET;
491221828Sgrehan		pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff);
492221828Sgrehan		pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT);
493221828Sgrehan	} else {
494221828Sgrehan		capoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR);
495221828Sgrehan		while (1) {
496221828Sgrehan			assert((capoff & 0x3) == 0);
497221828Sgrehan			capid = pci_get_cfgdata8(pi, capoff);
498221828Sgrehan			if (capid == PCIY_RESERVED)
499221828Sgrehan				break;
500221828Sgrehan			capoff = pci_get_cfgdata8(pi, capoff + 1);
501221828Sgrehan		}
502221828Sgrehan	}
503221828Sgrehan
504221828Sgrehan	/* Check if we have enough space */
505221828Sgrehan	if (capoff + reallen + sizeof(endofcap) > PCI_REGMAX + 1)
506221828Sgrehan		return (-1);
507221828Sgrehan
508221828Sgrehan	/* Copy the capability */
509221828Sgrehan	for (i = 0; i < caplen; i++)
510221828Sgrehan		pci_set_cfgdata8(pi, capoff + i, capdata[i]);
511221828Sgrehan
512221828Sgrehan	/* Set the next capability pointer */
513221828Sgrehan	pci_set_cfgdata8(pi, capoff + 1, capoff + reallen);
514221828Sgrehan
515221828Sgrehan	/* Copy of the reserved capability which serves as the end marker */
516221828Sgrehan	for (i = 0; i < sizeof(endofcap); i++)
517221828Sgrehan		pci_set_cfgdata8(pi, capoff + reallen + i, endofcap[i]);
518221828Sgrehan
519221828Sgrehan	return (0);
520221828Sgrehan}
521221828Sgrehan
522221828Sgrehanstatic struct pci_devemu *
523221828Sgrehanpci_emul_finddev(char *name)
524221828Sgrehan{
525221828Sgrehan	struct pci_devemu **pdpp, *pdp;
526221828Sgrehan
527221828Sgrehan	SET_FOREACH(pdpp, pci_devemu_set) {
528221828Sgrehan		pdp = *pdpp;
529221828Sgrehan		if (!strcmp(pdp->pe_emu, name)) {
530221828Sgrehan			return (pdp);
531221828Sgrehan		}
532221828Sgrehan	}
533221828Sgrehan
534221828Sgrehan	return (NULL);
535221828Sgrehan}
536221828Sgrehan
537221828Sgrehanstatic void
538239085Sneelpci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
539239085Sneel	      char *params)
540221828Sgrehan{
541221828Sgrehan	struct pci_devinst *pdi;
542221828Sgrehan	pdi = malloc(sizeof(struct pci_devinst));
543221828Sgrehan	bzero(pdi, sizeof(*pdi));
544221828Sgrehan
545221828Sgrehan	pdi->pi_vmctx = ctx;
546221828Sgrehan	pdi->pi_bus = 0;
547221828Sgrehan	pdi->pi_slot = slot;
548239085Sneel	pdi->pi_func = func;
549221828Sgrehan	pdi->pi_d = pde;
550221828Sgrehan	snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
551221828Sgrehan
552221828Sgrehan	/* Disable legacy interrupts */
553221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_INTLINE, 255);
554221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_INTPIN, 0);
555221828Sgrehan
556221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_COMMAND,
557221828Sgrehan		    PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
558221828Sgrehan
559221828Sgrehan	if ((*pde->pe_init)(ctx, pdi, params) != 0) {
560221828Sgrehan		free(pdi);
561221828Sgrehan	} else {
562221828Sgrehan		pci_emul_devices++;
563239085Sneel		pci_slotinfo[slot][func].si_devi = pdi;
564221828Sgrehan	}
565221828Sgrehan}
566221828Sgrehan
567221828Sgrehanvoid
568221828Sgrehanpci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr)
569221828Sgrehan{
570221828Sgrehan	int mmc;
571221828Sgrehan
572221828Sgrehan	CTASSERT(sizeof(struct msicap) == 14);
573221828Sgrehan
574221828Sgrehan	/* Number of msi messages must be a power of 2 between 1 and 32 */
575221828Sgrehan	assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32);
576221828Sgrehan	mmc = ffs(msgnum) - 1;
577221828Sgrehan
578221828Sgrehan	bzero(msicap, sizeof(struct msicap));
579221828Sgrehan	msicap->capid = PCIY_MSI;
580221828Sgrehan	msicap->nextptr = nextptr;
581221828Sgrehan	msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1);
582221828Sgrehan}
583221828Sgrehan
584221828Sgrehanint
585221828Sgrehanpci_emul_add_msicap(struct pci_devinst *pi, int msgnum)
586221828Sgrehan{
587221828Sgrehan	struct msicap msicap;
588221828Sgrehan
589221828Sgrehan	pci_populate_msicap(&msicap, msgnum, 0);
590221828Sgrehan
591221828Sgrehan	return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap)));
592221828Sgrehan}
593221828Sgrehan
594246109Sneelstatic void
595246109Sneelpci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum,
596246109Sneel		     uint32_t msix_tab_size, int nextptr)
597246109Sneel{
598246109Sneel	CTASSERT(sizeof(struct msixcap) == 12);
599246109Sneel
600246109Sneel	assert(msix_tab_size % 4096 == 0);
601246109Sneel
602246109Sneel	bzero(msixcap, sizeof(struct msixcap));
603246109Sneel	msixcap->capid = PCIY_MSIX;
604246109Sneel	msixcap->nextptr = nextptr;
605246109Sneel
606246109Sneel	/*
607246109Sneel	 * Message Control Register, all fields set to
608246109Sneel	 * zero except for the Table Size.
609246109Sneel	 * Note: Table size N is encoded as N-1
610246109Sneel	 */
611246109Sneel	msixcap->msgctrl = msgnum - 1;
612246109Sneel
613246109Sneel	/*
614246109Sneel	 * MSI-X BAR setup:
615246109Sneel	 * - MSI-X table start at offset 0
616246109Sneel	 * - PBA table starts at a 4K aligned offset after the MSI-X table
617246109Sneel	 */
618246109Sneel	msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK;
619246109Sneel	msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK);
620246109Sneel}
621246109Sneel
622246109Sneelstatic void
623246109Sneelpci_msix_table_init(struct pci_devinst *pi, int table_entries)
624246109Sneel{
625246109Sneel	int i, table_size;
626246109Sneel
627246109Sneel	assert(table_entries > 0);
628246109Sneel	assert(table_entries <= MAX_MSIX_TABLE_ENTRIES);
629246109Sneel
630246109Sneel	table_size = table_entries * MSIX_TABLE_ENTRY_SIZE;
631246109Sneel	pi->pi_msix.table = malloc(table_size);
632246109Sneel	bzero(pi->pi_msix.table, table_size);
633246109Sneel
634246109Sneel	/* set mask bit of vector control register */
635246109Sneel	for (i = 0; i < table_entries; i++)
636246109Sneel		pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK;
637246109Sneel}
638246109Sneel
639246109Sneelint
640246109Sneelpci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum)
641246109Sneel{
642246109Sneel	uint16_t pba_index;
643246109Sneel	uint32_t tab_size;
644246109Sneel	struct msixcap msixcap;
645246109Sneel
646246109Sneel	assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES);
647246109Sneel	assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0);
648246109Sneel
649246109Sneel	tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE;
650246109Sneel
651246109Sneel	/* Align table size to nearest 4K */
652246109Sneel	tab_size = roundup2(tab_size, 4096);
653246109Sneel
654246109Sneel	pi->pi_msix.table_bar = barnum;
655246109Sneel	pi->pi_msix.pba_bar   = barnum;
656246109Sneel	pi->pi_msix.table_offset = 0;
657246109Sneel	pi->pi_msix.table_count = msgnum;
658246109Sneel	pi->pi_msix.pba_offset = tab_size;
659246109Sneel
660246109Sneel	/* calculate the MMIO size required for MSI-X PBA */
661246109Sneel	pba_index = (msgnum - 1) / (PBA_TABLE_ENTRY_SIZE * 8);
662246109Sneel	pi->pi_msix.pba_size = (pba_index + 1) * PBA_TABLE_ENTRY_SIZE;
663246109Sneel
664246109Sneel	pci_msix_table_init(pi, msgnum);
665246109Sneel
666246109Sneel	pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size, 0);
667246109Sneel
668246109Sneel	/* allocate memory for MSI-X Table and PBA */
669246109Sneel	pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32,
670246109Sneel				tab_size + pi->pi_msix.pba_size);
671246109Sneel
672246109Sneel	return (pci_emul_add_capability(pi, (u_char *)&msixcap,
673246109Sneel					sizeof(msixcap)));
674246109Sneel}
675246109Sneel
676221828Sgrehanvoid
677234761Sgrehanmsixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
678234761Sgrehan		 int bytes, uint32_t val)
679234761Sgrehan{
680234761Sgrehan	uint16_t msgctrl, rwmask;
681234761Sgrehan	int off, table_bar;
682246109Sneel
683234761Sgrehan	off = offset - capoff;
684234761Sgrehan	table_bar = pi->pi_msix.table_bar;
685234761Sgrehan	/* Message Control Register */
686234761Sgrehan	if (off == 2 && bytes == 2) {
687234761Sgrehan		rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK;
688234761Sgrehan		msgctrl = pci_get_cfgdata16(pi, offset);
689234761Sgrehan		msgctrl &= ~rwmask;
690234761Sgrehan		msgctrl |= val & rwmask;
691234761Sgrehan		val = msgctrl;
692234761Sgrehan
693234761Sgrehan		pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE;
694246109Sneel		pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK;
695234761Sgrehan	}
696234761Sgrehan
697234761Sgrehan	CFGWRITE(pi, offset, val, bytes);
698234761Sgrehan}
699234761Sgrehan
700234761Sgrehanvoid
701221828Sgrehanmsicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
702221828Sgrehan		int bytes, uint32_t val)
703221828Sgrehan{
704221828Sgrehan	uint16_t msgctrl, rwmask, msgdata, mme;
705221828Sgrehan	uint32_t addrlo;
706221828Sgrehan
707221828Sgrehan	/*
708221828Sgrehan	 * If guest is writing to the message control register make sure
709221828Sgrehan	 * we do not overwrite read-only fields.
710221828Sgrehan	 */
711221828Sgrehan	if ((offset - capoff) == 2 && bytes == 2) {
712221828Sgrehan		rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE;
713221828Sgrehan		msgctrl = pci_get_cfgdata16(pi, offset);
714221828Sgrehan		msgctrl &= ~rwmask;
715221828Sgrehan		msgctrl |= val & rwmask;
716221828Sgrehan		val = msgctrl;
717221828Sgrehan
718221828Sgrehan		addrlo = pci_get_cfgdata32(pi, capoff + 4);
719221828Sgrehan		if (msgctrl & PCIM_MSICTRL_64BIT)
720221828Sgrehan			msgdata = pci_get_cfgdata16(pi, capoff + 12);
721221828Sgrehan		else
722221828Sgrehan			msgdata = pci_get_cfgdata16(pi, capoff + 8);
723221828Sgrehan
724221828Sgrehan		/*
725221828Sgrehan		 * XXX check delivery mode, destination mode etc
726221828Sgrehan		 */
727221828Sgrehan		mme = msgctrl & PCIM_MSICTRL_MME_MASK;
728221828Sgrehan		pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0;
729221828Sgrehan		if (pi->pi_msi.enabled) {
730221828Sgrehan			pi->pi_msi.cpu = (addrlo >> 12) & 0xff;
731221828Sgrehan			pi->pi_msi.vector = msgdata & 0xff;
732221828Sgrehan			pi->pi_msi.msgnum = 1 << (mme >> 4);
733221828Sgrehan		} else {
734221828Sgrehan			pi->pi_msi.cpu = 0;
735221828Sgrehan			pi->pi_msi.vector = 0;
736221828Sgrehan			pi->pi_msi.msgnum = 0;
737221828Sgrehan		}
738221828Sgrehan	}
739221828Sgrehan
740221828Sgrehan	CFGWRITE(pi, offset, val, bytes);
741221828Sgrehan}
742221828Sgrehan
743221828Sgrehan/*
744221828Sgrehan * This function assumes that 'coff' is in the capabilities region of the
745221828Sgrehan * config space.
746221828Sgrehan */
747221828Sgrehanstatic void
748221828Sgrehanpci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val)
749221828Sgrehan{
750221828Sgrehan	int capid;
751221828Sgrehan	uint8_t capoff, nextoff;
752221828Sgrehan
753221828Sgrehan	/* Do not allow un-aligned writes */
754221828Sgrehan	if ((offset & (bytes - 1)) != 0)
755221828Sgrehan		return;
756221828Sgrehan
757221828Sgrehan	/* Find the capability that we want to update */
758221828Sgrehan	capoff = CAP_START_OFFSET;
759221828Sgrehan	while (1) {
760221828Sgrehan		capid = pci_get_cfgdata8(pi, capoff);
761221828Sgrehan		if (capid == PCIY_RESERVED)
762221828Sgrehan			break;
763221828Sgrehan
764221828Sgrehan		nextoff = pci_get_cfgdata8(pi, capoff + 1);
765221828Sgrehan		if (offset >= capoff && offset < nextoff)
766221828Sgrehan			break;
767221828Sgrehan
768221828Sgrehan		capoff = nextoff;
769221828Sgrehan	}
770221828Sgrehan	assert(offset >= capoff);
771221828Sgrehan
772221828Sgrehan	/*
773221828Sgrehan	 * Capability ID and Next Capability Pointer are readonly
774221828Sgrehan	 */
775221828Sgrehan	if (offset == capoff || offset == capoff + 1)
776221828Sgrehan		return;
777221828Sgrehan
778221828Sgrehan	switch (capid) {
779221828Sgrehan	case PCIY_MSI:
780221828Sgrehan		msicap_cfgwrite(pi, capoff, offset, bytes, val);
781221828Sgrehan		break;
782246109Sneel	case PCIY_MSIX:
783246109Sneel		msixcap_cfgwrite(pi, capoff, offset, bytes, val);
784246109Sneel		break;
785221828Sgrehan	default:
786221828Sgrehan		break;
787221828Sgrehan	}
788221828Sgrehan}
789221828Sgrehan
790221828Sgrehanstatic int
791221828Sgrehanpci_emul_iscap(struct pci_devinst *pi, int offset)
792221828Sgrehan{
793221828Sgrehan	int found;
794221828Sgrehan	uint16_t sts;
795221828Sgrehan	uint8_t capid, lastoff;
796221828Sgrehan
797221828Sgrehan	found = 0;
798221828Sgrehan	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
799221828Sgrehan	if ((sts & PCIM_STATUS_CAPPRESENT) != 0) {
800221828Sgrehan		lastoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR);
801221828Sgrehan		while (1) {
802221828Sgrehan			assert((lastoff & 0x3) == 0);
803221828Sgrehan			capid = pci_get_cfgdata8(pi, lastoff);
804221828Sgrehan			if (capid == PCIY_RESERVED)
805221828Sgrehan				break;
806221828Sgrehan			lastoff = pci_get_cfgdata8(pi, lastoff + 1);
807221828Sgrehan		}
808221828Sgrehan		if (offset >= CAP_START_OFFSET && offset <= lastoff)
809221828Sgrehan			found = 1;
810221828Sgrehan	}
811221828Sgrehan	return (found);
812221828Sgrehan}
813221828Sgrehan
814221828Sgrehanvoid
815221828Sgrehaninit_pci(struct vmctx *ctx)
816221828Sgrehan{
817221828Sgrehan	struct pci_devemu *pde;
818221828Sgrehan	struct slotinfo *si;
819239085Sneel	int slot, func;
820221828Sgrehan
821221828Sgrehan	pci_emul_iobase = PCI_EMUL_IOBASE;
822221828Sgrehan	pci_emul_membase32 = PCI_EMUL_MEMBASE32;
823221828Sgrehan	pci_emul_membase64 = PCI_EMUL_MEMBASE64;
824221828Sgrehan
825239085Sneel	for (slot = 0; slot < MAXSLOTS; slot++) {
826239085Sneel		for (func = 0; func < MAXFUNCS; func++) {
827239085Sneel			si = &pci_slotinfo[slot][func];
828239085Sneel			if (si->si_name != NULL) {
829239085Sneel				pde = pci_emul_finddev(si->si_name);
830239085Sneel				if (pde != NULL) {
831239085Sneel					pci_emul_init(ctx, pde, slot, func,
832239085Sneel						      si->si_param);
833239085Sneel				}
834221828Sgrehan			}
835221828Sgrehan		}
836221828Sgrehan	}
837234938Sgrehan
838234938Sgrehan	/*
839234938Sgrehan	 * Allow ISA IRQs 5,10,11,12, and 15 to be available for
840234938Sgrehan	 * generic use
841234938Sgrehan	 */
842234938Sgrehan	lirq[5].li_generic = 1;
843234938Sgrehan	lirq[10].li_generic = 1;
844234938Sgrehan	lirq[11].li_generic = 1;
845234938Sgrehan	lirq[12].li_generic = 1;
846234938Sgrehan	lirq[15].li_generic = 1;
847221828Sgrehan}
848221828Sgrehan
849221828Sgrehanint
850221828Sgrehanpci_msi_enabled(struct pci_devinst *pi)
851221828Sgrehan{
852221828Sgrehan	return (pi->pi_msi.enabled);
853221828Sgrehan}
854221828Sgrehan
855221828Sgrehanint
856221828Sgrehanpci_msi_msgnum(struct pci_devinst *pi)
857221828Sgrehan{
858221828Sgrehan	if (pi->pi_msi.enabled)
859221828Sgrehan		return (pi->pi_msi.msgnum);
860221828Sgrehan	else
861221828Sgrehan		return (0);
862221828Sgrehan}
863221828Sgrehan
864246109Sneelint
865246109Sneelpci_msix_enabled(struct pci_devinst *pi)
866246109Sneel{
867246109Sneel
868246109Sneel	return (pi->pi_msix.enabled && !pi->pi_msi.enabled);
869246109Sneel}
870246109Sneel
871221828Sgrehanvoid
872246109Sneelpci_generate_msix(struct pci_devinst *pi, int index)
873246109Sneel{
874246109Sneel	struct msix_table_entry *mte;
875246109Sneel
876246109Sneel	if (!pci_msix_enabled(pi))
877246109Sneel		return;
878246109Sneel
879246109Sneel	if (pi->pi_msix.function_mask)
880246109Sneel		return;
881246109Sneel
882246109Sneel	if (index >= pi->pi_msix.table_count)
883246109Sneel		return;
884246109Sneel
885246109Sneel	mte = &pi->pi_msix.table[index];
886246109Sneel	if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
887246109Sneel		/* XXX Set PBA bit if interrupt is disabled */
888246109Sneel		vm_lapic_irq(pi->pi_vmctx,
889246109Sneel			     (mte->addr >> 12) & 0xff, mte->msg_data & 0xff);
890246109Sneel	}
891246109Sneel}
892246109Sneel
893246109Sneelvoid
894221828Sgrehanpci_generate_msi(struct pci_devinst *pi, int msg)
895221828Sgrehan{
896221828Sgrehan
897221828Sgrehan	if (pci_msi_enabled(pi) && msg < pci_msi_msgnum(pi)) {
898221828Sgrehan		vm_lapic_irq(pi->pi_vmctx,
899221828Sgrehan			     pi->pi_msi.cpu,
900221828Sgrehan			     pi->pi_msi.vector + msg);
901221828Sgrehan	}
902221828Sgrehan}
903221828Sgrehan
904234938Sgrehanint
905234938Sgrehanpci_is_legacy(struct pci_devinst *pi)
906234938Sgrehan{
907234938Sgrehan
908239085Sneel	return (pci_slotinfo[pi->pi_slot][pi->pi_func].si_legacy);
909234938Sgrehan}
910234938Sgrehan
911234938Sgrehanstatic int
912234938Sgrehanpci_lintr_alloc(struct pci_devinst *pi, int vec)
913234938Sgrehan{
914234938Sgrehan	int i;
915234938Sgrehan
916234938Sgrehan	assert(vec < NLIRQ);
917234938Sgrehan
918234938Sgrehan	if (vec == -1) {
919234938Sgrehan		for (i = 0; i < NLIRQ; i++) {
920234938Sgrehan			if (lirq[i].li_generic &&
921234938Sgrehan			    lirq[i].li_owner == NULL) {
922234938Sgrehan				vec = i;
923234938Sgrehan				break;
924234938Sgrehan			}
925234938Sgrehan		}
926234938Sgrehan	} else {
927239029Sneel		if (lirq[vec].li_owner != NULL) {
928234938Sgrehan			vec = -1;
929234938Sgrehan		}
930234938Sgrehan	}
931234938Sgrehan	assert(vec != -1);
932234938Sgrehan
933234938Sgrehan	lirq[vec].li_owner = pi;
934234938Sgrehan	pi->pi_lintr_pin = vec;
935234938Sgrehan
936234938Sgrehan	return (vec);
937234938Sgrehan}
938234938Sgrehan
939234938Sgrehanint
940234938Sgrehanpci_lintr_request(struct pci_devinst *pi, int vec)
941234938Sgrehan{
942234938Sgrehan
943234938Sgrehan	vec = pci_lintr_alloc(pi, vec);
944234938Sgrehan	pci_set_cfgdata8(pi, PCIR_INTLINE, vec);
945234938Sgrehan	pci_set_cfgdata8(pi, PCIR_INTPIN, 1);
946234938Sgrehan	return (0);
947234938Sgrehan}
948234938Sgrehan
949234938Sgrehanvoid
950234938Sgrehanpci_lintr_assert(struct pci_devinst *pi)
951234938Sgrehan{
952234938Sgrehan
953234938Sgrehan	assert(pi->pi_lintr_pin);
954239045Sneel	ioapic_assert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
955234938Sgrehan}
956234938Sgrehan
957234938Sgrehanvoid
958234938Sgrehanpci_lintr_deassert(struct pci_devinst *pi)
959234938Sgrehan{
960234938Sgrehan
961234938Sgrehan	assert(pi->pi_lintr_pin);
962239045Sneel	ioapic_deassert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
963234938Sgrehan}
964234938Sgrehan
965239085Sneel/*
966239085Sneel * Return 1 if the emulated device in 'slot' is a multi-function device.
967239085Sneel * Return 0 otherwise.
968239085Sneel */
969239085Sneelstatic int
970239085Sneelpci_emul_is_mfdev(int slot)
971239085Sneel{
972239085Sneel	int f, numfuncs;
973234938Sgrehan
974239085Sneel	numfuncs = 0;
975239085Sneel	for (f = 0; f < MAXFUNCS; f++) {
976239085Sneel		if (pci_slotinfo[slot][f].si_devi != NULL) {
977239085Sneel			numfuncs++;
978239085Sneel		}
979239085Sneel	}
980239085Sneel	return (numfuncs > 1);
981239085Sneel}
982234938Sgrehan
983239085Sneel/*
984239085Sneel * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on
985239085Sneel * whether or not is a multi-function being emulated in the pci 'slot'.
986239085Sneel */
987239085Sneelstatic void
988239085Sneelpci_emul_hdrtype_fixup(int slot, int off, int bytes, uint32_t *rv)
989239085Sneel{
990239085Sneel	int mfdev;
991239085Sneel
992239085Sneel	if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) {
993239085Sneel		mfdev = pci_emul_is_mfdev(slot);
994239085Sneel		switch (bytes) {
995239085Sneel		case 1:
996239085Sneel		case 2:
997239085Sneel			*rv &= ~PCIM_MFDEV;
998239085Sneel			if (mfdev) {
999239085Sneel				*rv |= PCIM_MFDEV;
1000239085Sneel			}
1001239085Sneel			break;
1002239085Sneel		case 4:
1003239085Sneel			*rv &= ~(PCIM_MFDEV << 16);
1004239085Sneel			if (mfdev) {
1005239085Sneel				*rv |= (PCIM_MFDEV << 16);
1006239085Sneel			}
1007239085Sneel			break;
1008239085Sneel		}
1009239085Sneel	}
1010239085Sneel}
1011239085Sneel
1012221828Sgrehanstatic int cfgbus, cfgslot, cfgfunc, cfgoff;
1013221828Sgrehan
1014221828Sgrehanstatic int
1015221828Sgrehanpci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
1016221828Sgrehan		 uint32_t *eax, void *arg)
1017221828Sgrehan{
1018221828Sgrehan	uint32_t x;
1019221828Sgrehan
1020221828Sgrehan	assert(!in);
1021221828Sgrehan
1022221828Sgrehan	if (bytes != 4)
1023221828Sgrehan		return (-1);
1024221828Sgrehan
1025221828Sgrehan	x = *eax;
1026221828Sgrehan	cfgoff = x & PCI_REGMAX;
1027221828Sgrehan	cfgfunc = (x >> 8) & PCI_FUNCMAX;
1028221828Sgrehan	cfgslot = (x >> 11) & PCI_SLOTMAX;
1029221828Sgrehan	cfgbus = (x >> 16) & PCI_BUSMAX;
1030221828Sgrehan
1031221828Sgrehan	return (0);
1032221828Sgrehan}
1033221828SgrehanINOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_OUT, pci_emul_cfgaddr);
1034221828Sgrehan
1035221828Sgrehanstatic int
1036221828Sgrehanpci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
1037221828Sgrehan		 uint32_t *eax, void *arg)
1038221828Sgrehan{
1039221828Sgrehan	struct pci_devinst *pi;
1040221828Sgrehan	struct pci_devemu *pe;
1041239085Sneel	int coff, idx, needcfg;
1042221828Sgrehan	uint64_t mask, bar;
1043221828Sgrehan
1044221828Sgrehan	assert(bytes == 1 || bytes == 2 || bytes == 4);
1045221828Sgrehan
1046242170Sneel	if (cfgbus == 0)
1047242170Sneel		pi = pci_slotinfo[cfgslot][cfgfunc].si_devi;
1048242170Sneel	else
1049242170Sneel		pi = NULL;
1050242170Sneel
1051221828Sgrehan	coff = cfgoff + (port - CONF1_DATA_PORT);
1052221828Sgrehan
1053221828Sgrehan#if 0
1054221828Sgrehan	printf("pcicfg-%s from 0x%0x of %d bytes (%d/%d/%d)\n\r",
1055221828Sgrehan		in ? "read" : "write", coff, bytes, cfgbus, cfgslot, cfgfunc);
1056221828Sgrehan#endif
1057221828Sgrehan
1058239085Sneel	/*
1059239085Sneel	 * Just return if there is no device at this cfgslot:cfgfunc or
1060239085Sneel	 * if the guest is doing an un-aligned access
1061239085Sneel	 */
1062239085Sneel	if (pi == NULL || (coff & (bytes - 1)) != 0) {
1063221828Sgrehan		if (in)
1064221828Sgrehan			*eax = 0xffffffff;
1065221828Sgrehan		return (0);
1066221828Sgrehan	}
1067221828Sgrehan
1068221828Sgrehan	pe = pi->pi_d;
1069221828Sgrehan
1070221828Sgrehan	/*
1071221828Sgrehan	 * Config read
1072221828Sgrehan	 */
1073221828Sgrehan	if (in) {
1074221828Sgrehan		/* Let the device emulation override the default handler */
1075239085Sneel		if (pe->pe_cfgread != NULL) {
1076239085Sneel			needcfg = pe->pe_cfgread(ctx, vcpu, pi,
1077239085Sneel						    coff, bytes, eax);
1078239085Sneel		} else {
1079239085Sneel			needcfg = 1;
1080239085Sneel		}
1081221828Sgrehan
1082239085Sneel		if (needcfg) {
1083239085Sneel			if (bytes == 1)
1084239085Sneel				*eax = pci_get_cfgdata8(pi, coff);
1085239085Sneel			else if (bytes == 2)
1086239085Sneel				*eax = pci_get_cfgdata16(pi, coff);
1087239085Sneel			else
1088239085Sneel				*eax = pci_get_cfgdata32(pi, coff);
1089239085Sneel		}
1090239085Sneel
1091239085Sneel		pci_emul_hdrtype_fixup(cfgslot, coff, bytes, eax);
1092221828Sgrehan	} else {
1093221828Sgrehan		/* Let the device emulation override the default handler */
1094221828Sgrehan		if (pe->pe_cfgwrite != NULL &&
1095221828Sgrehan		    (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0)
1096221828Sgrehan			return (0);
1097221828Sgrehan
1098221828Sgrehan		/*
1099221828Sgrehan		 * Special handling for write to BAR registers
1100221828Sgrehan		 */
1101221828Sgrehan		if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) {
1102221828Sgrehan			/*
1103221828Sgrehan			 * Ignore writes to BAR registers that are not
1104221828Sgrehan			 * 4-byte aligned.
1105221828Sgrehan			 */
1106221828Sgrehan			if (bytes != 4 || (coff & 0x3) != 0)
1107221828Sgrehan				return (0);
1108221828Sgrehan			idx = (coff - PCIR_BAR(0)) / 4;
1109221828Sgrehan			switch (pi->pi_bar[idx].type) {
1110221828Sgrehan			case PCIBAR_NONE:
1111221828Sgrehan				bar = 0;
1112221828Sgrehan				break;
1113221828Sgrehan			case PCIBAR_IO:
1114221828Sgrehan				mask = ~(pi->pi_bar[idx].size - 1);
1115221828Sgrehan				mask &= PCIM_BAR_IO_BASE;
1116221828Sgrehan				bar = (*eax & mask) | PCIM_BAR_IO_SPACE;
1117221828Sgrehan				break;
1118221828Sgrehan			case PCIBAR_MEM32:
1119221828Sgrehan				mask = ~(pi->pi_bar[idx].size - 1);
1120221828Sgrehan				mask &= PCIM_BAR_MEM_BASE;
1121221828Sgrehan				bar = *eax & mask;
1122221828Sgrehan				bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
1123221828Sgrehan				break;
1124221828Sgrehan			case PCIBAR_MEM64:
1125221828Sgrehan				mask = ~(pi->pi_bar[idx].size - 1);
1126221828Sgrehan				mask &= PCIM_BAR_MEM_BASE;
1127221828Sgrehan				bar = *eax & mask;
1128221828Sgrehan				bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
1129221828Sgrehan				       PCIM_BAR_MEM_PREFETCH;
1130221828Sgrehan				break;
1131221828Sgrehan			case PCIBAR_MEMHI64:
1132221828Sgrehan				mask = ~(pi->pi_bar[idx - 1].size - 1);
1133221828Sgrehan				mask &= PCIM_BAR_MEM_BASE;
1134221828Sgrehan				bar = ((uint64_t)*eax << 32) & mask;
1135221828Sgrehan				bar = bar >> 32;
1136221828Sgrehan				break;
1137221828Sgrehan			default:
1138221828Sgrehan				assert(0);
1139221828Sgrehan			}
1140221828Sgrehan			pci_set_cfgdata32(pi, coff, bar);
1141234761Sgrehan
1142221828Sgrehan		} else if (pci_emul_iscap(pi, coff)) {
1143221828Sgrehan			pci_emul_capwrite(pi, coff, bytes, *eax);
1144221828Sgrehan		} else {
1145221828Sgrehan			CFGWRITE(pi, coff, *eax, bytes);
1146221828Sgrehan		}
1147221828Sgrehan	}
1148221828Sgrehan
1149221828Sgrehan	return (0);
1150221828Sgrehan}
1151221828Sgrehan
1152221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata);
1153221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata);
1154221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
1155221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
1156221828Sgrehan
1157221828Sgrehan/*
1158221828Sgrehan * I/O ports to configure PCI IRQ routing. We ignore all writes to it.
1159221828Sgrehan */
1160221828Sgrehanstatic int
1161221828Sgrehanpci_irq_port_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
1162221828Sgrehan		     uint32_t *eax, void *arg)
1163221828Sgrehan{
1164221828Sgrehan	assert(in == 0);
1165221828Sgrehan	return (0);
1166221828Sgrehan}
1167221828SgrehanINOUT_PORT(pci_irq, 0xC00, IOPORT_F_OUT, pci_irq_port_handler);
1168221828SgrehanINOUT_PORT(pci_irq, 0xC01, IOPORT_F_OUT, pci_irq_port_handler);
1169221828Sgrehan
1170221828Sgrehan#define PCI_EMUL_TEST
1171221828Sgrehan#ifdef PCI_EMUL_TEST
1172221828Sgrehan/*
1173221828Sgrehan * Define a dummy test device
1174221828Sgrehan */
1175241744Sgrehan#define DIOSZ	20
1176241744Sgrehan#define DMEMSZ	4096
1177221828Sgrehanstruct pci_emul_dsoftc {
1178241744Sgrehan	uint8_t   ioregs[DIOSZ];
1179241744Sgrehan	uint8_t	  memregs[DMEMSZ];
1180221828Sgrehan};
1181221828Sgrehan
1182241744Sgrehan#define	PCI_EMUL_MSI_MSGS	 4
1183241744Sgrehan#define	PCI_EMUL_MSIX_MSGS	16
1184221828Sgrehan
1185221942Sjhbstatic int
1186221828Sgrehanpci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
1187221828Sgrehan{
1188221828Sgrehan	int error;
1189221828Sgrehan	struct pci_emul_dsoftc *sc;
1190221828Sgrehan
1191221828Sgrehan	sc = malloc(sizeof(struct pci_emul_dsoftc));
1192221828Sgrehan	memset(sc, 0, sizeof(struct pci_emul_dsoftc));
1193221828Sgrehan
1194221828Sgrehan	pi->pi_arg = sc;
1195221828Sgrehan
1196221828Sgrehan	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001);
1197221828Sgrehan	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD);
1198221828Sgrehan	pci_set_cfgdata8(pi, PCIR_CLASS, 0x02);
1199221828Sgrehan
1200241744Sgrehan	error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS);
1201221828Sgrehan	assert(error == 0);
1202221828Sgrehan
1203241744Sgrehan	error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ);
1204221828Sgrehan	assert(error == 0);
1205221828Sgrehan
1206241744Sgrehan	error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ);
1207241744Sgrehan	assert(error == 0);
1208241744Sgrehan
1209221828Sgrehan	return (0);
1210221828Sgrehan}
1211221828Sgrehan
1212221942Sjhbstatic void
1213241744Sgrehanpci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
1214241744Sgrehan	      uint64_t offset, int size, uint64_t value)
1215221828Sgrehan{
1216221828Sgrehan	int i;
1217221828Sgrehan	struct pci_emul_dsoftc *sc = pi->pi_arg;
1218221828Sgrehan
1219241744Sgrehan	if (baridx == 0) {
1220241744Sgrehan		if (offset + size > DIOSZ) {
1221241744Sgrehan			printf("diow: iow too large, offset %ld size %d\n",
1222241744Sgrehan			       offset, size);
1223241744Sgrehan			return;
1224241744Sgrehan		}
1225221828Sgrehan
1226241744Sgrehan		if (size == 1) {
1227241744Sgrehan			sc->ioregs[offset] = value & 0xff;
1228241744Sgrehan		} else if (size == 2) {
1229241744Sgrehan			*(uint16_t *)&sc->ioregs[offset] = value & 0xffff;
1230241744Sgrehan		} else if (size == 4) {
1231241744Sgrehan			*(uint32_t *)&sc->ioregs[offset] = value;
1232241744Sgrehan		} else {
1233241744Sgrehan			printf("diow: iow unknown size %d\n", size);
1234241744Sgrehan		}
1235241744Sgrehan
1236241744Sgrehan		/*
1237241744Sgrehan		 * Special magic value to generate an interrupt
1238241744Sgrehan		 */
1239241744Sgrehan		if (offset == 4 && size == 4 && pci_msi_enabled(pi))
1240241744Sgrehan			pci_generate_msi(pi, value % pci_msi_msgnum(pi));
1241241744Sgrehan
1242241744Sgrehan		if (value == 0xabcdef) {
1243241744Sgrehan			for (i = 0; i < pci_msi_msgnum(pi); i++)
1244241744Sgrehan				pci_generate_msi(pi, i);
1245241744Sgrehan		}
1246221828Sgrehan	}
1247221828Sgrehan
1248241744Sgrehan	if (baridx == 1) {
1249241744Sgrehan		if (offset + size > DMEMSZ) {
1250241744Sgrehan			printf("diow: memw too large, offset %ld size %d\n",
1251241744Sgrehan			       offset, size);
1252241744Sgrehan			return;
1253241744Sgrehan		}
1254221828Sgrehan
1255241744Sgrehan		if (size == 1) {
1256241744Sgrehan			sc->memregs[offset] = value;
1257241744Sgrehan		} else if (size == 2) {
1258241744Sgrehan			*(uint16_t *)&sc->memregs[offset] = value;
1259241744Sgrehan		} else if (size == 4) {
1260241744Sgrehan			*(uint32_t *)&sc->memregs[offset] = value;
1261241744Sgrehan		} else if (size == 8) {
1262241744Sgrehan			*(uint64_t *)&sc->memregs[offset] = value;
1263241744Sgrehan		} else {
1264241744Sgrehan			printf("diow: memw unknown size %d\n", size);
1265241744Sgrehan		}
1266241744Sgrehan
1267241744Sgrehan		/*
1268241744Sgrehan		 * magic interrupt ??
1269241744Sgrehan		 */
1270221828Sgrehan	}
1271241744Sgrehan
1272241744Sgrehan	if (baridx > 1) {
1273241744Sgrehan		printf("diow: unknown bar idx %d\n", baridx);
1274241744Sgrehan	}
1275221828Sgrehan}
1276221828Sgrehan
1277241744Sgrehanstatic uint64_t
1278241744Sgrehanpci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
1279241744Sgrehan	      uint64_t offset, int size)
1280221828Sgrehan{
1281221828Sgrehan	struct pci_emul_dsoftc *sc = pi->pi_arg;
1282221828Sgrehan	uint32_t value;
1283221828Sgrehan
1284241744Sgrehan	if (baridx == 0) {
1285241744Sgrehan		if (offset + size > DIOSZ) {
1286241744Sgrehan			printf("dior: ior too large, offset %ld size %d\n",
1287241744Sgrehan			       offset, size);
1288241744Sgrehan			return (0);
1289241744Sgrehan		}
1290241744Sgrehan
1291241744Sgrehan		if (size == 1) {
1292241744Sgrehan			value = sc->ioregs[offset];
1293241744Sgrehan		} else if (size == 2) {
1294241744Sgrehan			value = *(uint16_t *) &sc->ioregs[offset];
1295241744Sgrehan		} else if (size == 4) {
1296241744Sgrehan			value = *(uint32_t *) &sc->ioregs[offset];
1297241744Sgrehan		} else {
1298241744Sgrehan			printf("dior: ior unknown size %d\n", size);
1299241744Sgrehan		}
1300221828Sgrehan	}
1301221828Sgrehan
1302241744Sgrehan	if (baridx == 1) {
1303241744Sgrehan		if (offset + size > DMEMSZ) {
1304241744Sgrehan			printf("dior: memr too large, offset %ld size %d\n",
1305241744Sgrehan			       offset, size);
1306241744Sgrehan			return (0);
1307241744Sgrehan		}
1308241744Sgrehan
1309241744Sgrehan		if (size == 1) {
1310241744Sgrehan			value = sc->memregs[offset];
1311241744Sgrehan		} else if (size == 2) {
1312241744Sgrehan			value = *(uint16_t *) &sc->memregs[offset];
1313241744Sgrehan		} else if (size == 4) {
1314241744Sgrehan			value = *(uint32_t *) &sc->memregs[offset];
1315241744Sgrehan		} else if (size == 8) {
1316241744Sgrehan			value = *(uint64_t *) &sc->memregs[offset];
1317241744Sgrehan		} else {
1318241744Sgrehan			printf("dior: ior unknown size %d\n", size);
1319241744Sgrehan		}
1320221828Sgrehan	}
1321221828Sgrehan
1322241744Sgrehan
1323241744Sgrehan	if (baridx > 1) {
1324241744Sgrehan		printf("dior: unknown bar idx %d\n", baridx);
1325241744Sgrehan		return (0);
1326241744Sgrehan	}
1327241744Sgrehan
1328221828Sgrehan	return (value);
1329221828Sgrehan}
1330221828Sgrehan
1331221828Sgrehanstruct pci_devemu pci_dummy = {
1332221828Sgrehan	.pe_emu = "dummy",
1333221828Sgrehan	.pe_init = pci_emul_dinit,
1334241744Sgrehan	.pe_barwrite = pci_emul_diow,
1335241744Sgrehan	.pe_barread = pci_emul_dior
1336221828Sgrehan};
1337221828SgrehanPCI_EMUL_SET(pci_dummy);
1338221828Sgrehan
1339221828Sgrehan#endif /* PCI_EMUL_TEST */
1340