pci_emul.c revision 246109
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 246109 2013-01-30 04:30:36Z neel $
27221828Sgrehan */
28221828Sgrehan
29221828Sgrehan#include <sys/cdefs.h>
30221828Sgrehan__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_emul.c 246109 2013-01-30 04:30:36Z 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
257246109Sneelstatic int
258241744Sgrehanpci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
259241744Sgrehan		    uint32_t *eax, void *arg)
260221828Sgrehan{
261221828Sgrehan	struct pci_devinst *pdi = arg;
262221828Sgrehan	struct pci_devemu *pe = pdi->pi_d;
263241744Sgrehan	uint64_t offset;
264241744Sgrehan	int i;
265221828Sgrehan
266221828Sgrehan	for (i = 0; i <= PCI_BARMAX; i++) {
267221828Sgrehan		if (pdi->pi_bar[i].type == PCIBAR_IO &&
268221828Sgrehan		    port >= pdi->pi_bar[i].addr &&
269246109Sneel		    port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) {
270221828Sgrehan			offset = port - pdi->pi_bar[i].addr;
271221828Sgrehan			if (in)
272241744Sgrehan				*eax = (*pe->pe_barread)(ctx, vcpu, pdi, i,
273241744Sgrehan							 offset, bytes);
274221828Sgrehan			else
275241744Sgrehan				(*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset,
276241744Sgrehan						   bytes, *eax);
277221828Sgrehan			return (0);
278221828Sgrehan		}
279221828Sgrehan	}
280221828Sgrehan	return (-1);
281221828Sgrehan}
282221828Sgrehan
283221828Sgrehanstatic int
284241744Sgrehanpci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
285241744Sgrehan		     int size, uint64_t *val, void *arg1, long arg2)
286241744Sgrehan{
287241744Sgrehan	struct pci_devinst *pdi = arg1;
288241744Sgrehan	struct pci_devemu *pe = pdi->pi_d;
289241744Sgrehan	uint64_t offset;
290241744Sgrehan	int bidx = (int) arg2;
291241744Sgrehan
292241744Sgrehan	assert(bidx <= PCI_BARMAX);
293241744Sgrehan	assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 ||
294241744Sgrehan	       pdi->pi_bar[bidx].type == PCIBAR_MEM64);
295241744Sgrehan	assert(addr >= pdi->pi_bar[bidx].addr &&
296241744Sgrehan	       addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size);
297241744Sgrehan
298241744Sgrehan	offset = addr - pdi->pi_bar[bidx].addr;
299241744Sgrehan
300241744Sgrehan	if (dir == MEM_F_WRITE)
301241744Sgrehan		(*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, size, *val);
302241744Sgrehan	else
303241744Sgrehan		*val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset, size);
304241744Sgrehan
305241744Sgrehan	return (0);
306241744Sgrehan}
307241744Sgrehan
308241744Sgrehan
309241744Sgrehanstatic int
310221828Sgrehanpci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size,
311221828Sgrehan			uint64_t *addr)
312221828Sgrehan{
313221828Sgrehan	uint64_t base;
314221828Sgrehan
315221828Sgrehan	assert((size & (size - 1)) == 0);	/* must be a power of 2 */
316221828Sgrehan
317221828Sgrehan	base = roundup2(*baseptr, size);
318221828Sgrehan
319221828Sgrehan	if (base + size <= limit) {
320221828Sgrehan		*addr = base;
321221828Sgrehan		*baseptr = base + size;
322221828Sgrehan		return (0);
323221828Sgrehan	} else
324221828Sgrehan		return (-1);
325221828Sgrehan}
326221828Sgrehan
327221828Sgrehanint
328241744Sgrehanpci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type,
329241744Sgrehan		   uint64_t size)
330221828Sgrehan{
331241744Sgrehan
332241744Sgrehan	return (pci_emul_alloc_pbar(pdi, idx, 0, type, size));
333241744Sgrehan}
334241744Sgrehan
335241744Sgrehanint
336241744Sgrehanpci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase,
337241744Sgrehan		    enum pcibar_type type, uint64_t size)
338241744Sgrehan{
339221828Sgrehan	int i, error;
340221828Sgrehan	uint64_t *baseptr, limit, addr, mask, lobits, bar;
341221828Sgrehan	struct inout_port iop;
342241744Sgrehan	struct mem_range memp;
343221828Sgrehan
344221828Sgrehan	assert(idx >= 0 && idx <= PCI_BARMAX);
345221828Sgrehan
346221828Sgrehan	if ((size & (size - 1)) != 0)
347221828Sgrehan		size = 1UL << flsl(size);	/* round up to a power of 2 */
348221828Sgrehan
349221828Sgrehan	switch (type) {
350221828Sgrehan	case PCIBAR_NONE:
351221828Sgrehan		baseptr = NULL;
352221828Sgrehan		addr = mask = lobits = 0;
353221828Sgrehan		break;
354221828Sgrehan	case PCIBAR_IO:
355239085Sneel		if (hostbase &&
356239085Sneel		    pci_slotinfo[pdi->pi_slot][pdi->pi_func].si_legacy) {
357234938Sgrehan			assert(hostbase < PCI_EMUL_IOBASE);
358234938Sgrehan			baseptr = &hostbase;
359234938Sgrehan		} else {
360234938Sgrehan			baseptr = &pci_emul_iobase;
361234938Sgrehan		}
362221828Sgrehan		limit = PCI_EMUL_IOLIMIT;
363221828Sgrehan		mask = PCIM_BAR_IO_BASE;
364221828Sgrehan		lobits = PCIM_BAR_IO_SPACE;
365221828Sgrehan		break;
366221828Sgrehan	case PCIBAR_MEM64:
367221828Sgrehan		/*
368221828Sgrehan		 * XXX
369221828Sgrehan		 * Some drivers do not work well if the 64-bit BAR is allocated
370221828Sgrehan		 * above 4GB. Allow for this by allocating small requests under
371221828Sgrehan		 * 4GB unless then allocation size is larger than some arbitrary
372221828Sgrehan		 * number (32MB currently).
373221828Sgrehan		 */
374221828Sgrehan		if (size > 32 * 1024 * 1024) {
375221828Sgrehan			/*
376221828Sgrehan			 * XXX special case for device requiring peer-peer DMA
377221828Sgrehan			 */
378221828Sgrehan			if (size == 0x100000000UL)
379221828Sgrehan				baseptr = &hostbase;
380221828Sgrehan			else
381221828Sgrehan				baseptr = &pci_emul_membase64;
382221828Sgrehan			limit = PCI_EMUL_MEMLIMIT64;
383221828Sgrehan			mask = PCIM_BAR_MEM_BASE;
384221828Sgrehan			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
385221828Sgrehan				 PCIM_BAR_MEM_PREFETCH;
386221828Sgrehan			break;
387239086Sneel		} else {
388239086Sneel			baseptr = &pci_emul_membase32;
389239086Sneel			limit = PCI_EMUL_MEMLIMIT32;
390239086Sneel			mask = PCIM_BAR_MEM_BASE;
391239086Sneel			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
392221828Sgrehan		}
393239086Sneel		break;
394221828Sgrehan	case PCIBAR_MEM32:
395221828Sgrehan		baseptr = &pci_emul_membase32;
396221828Sgrehan		limit = PCI_EMUL_MEMLIMIT32;
397221828Sgrehan		mask = PCIM_BAR_MEM_BASE;
398221828Sgrehan		lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
399221828Sgrehan		break;
400221828Sgrehan	default:
401221828Sgrehan		printf("pci_emul_alloc_base: invalid bar type %d\n", type);
402221828Sgrehan		assert(0);
403221828Sgrehan	}
404221828Sgrehan
405221828Sgrehan	if (baseptr != NULL) {
406221828Sgrehan		error = pci_emul_alloc_resource(baseptr, limit, size, &addr);
407221828Sgrehan		if (error != 0)
408221828Sgrehan			return (error);
409221828Sgrehan	}
410221828Sgrehan
411221828Sgrehan	pdi->pi_bar[idx].type = type;
412221828Sgrehan	pdi->pi_bar[idx].addr = addr;
413221828Sgrehan	pdi->pi_bar[idx].size = size;
414221828Sgrehan
415221828Sgrehan	/* Initialize the BAR register in config space */
416221828Sgrehan	bar = (addr & mask) | lobits;
417221828Sgrehan	pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar);
418221828Sgrehan
419221828Sgrehan	if (type == PCIBAR_MEM64) {
420221828Sgrehan		assert(idx + 1 <= PCI_BARMAX);
421221828Sgrehan		pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64;
422221828Sgrehan		pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
423221828Sgrehan	}
424221828Sgrehan
425221828Sgrehan	/* add a handler to intercept accesses to the I/O bar */
426221828Sgrehan	if (type == PCIBAR_IO) {
427221828Sgrehan		iop.name = pdi->pi_name;
428221828Sgrehan		iop.flags = IOPORT_F_INOUT;
429241744Sgrehan		iop.handler = pci_emul_io_handler;
430221828Sgrehan		iop.arg = pdi;
431221828Sgrehan
432221828Sgrehan		for (i = 0; i < size; i++) {
433221828Sgrehan			iop.port = addr + i;
434221828Sgrehan			register_inout(&iop);
435221828Sgrehan		}
436241744Sgrehan	} else if (type == PCIBAR_MEM32 || type == PCIBAR_MEM64) {
437241744Sgrehan		/* add memory bar intercept handler */
438241744Sgrehan		memp.name = pdi->pi_name;
439241744Sgrehan		memp.flags = MEM_F_RW;
440241744Sgrehan		memp.base = addr;
441241744Sgrehan		memp.size = size;
442241744Sgrehan		memp.handler = pci_emul_mem_handler;
443241744Sgrehan		memp.arg1 = pdi;
444241744Sgrehan		memp.arg2 = idx;
445241744Sgrehan
446241744Sgrehan		error = register_mem(&memp);
447241744Sgrehan		assert(error == 0);
448221828Sgrehan	}
449221828Sgrehan
450221828Sgrehan	return (0);
451221828Sgrehan}
452221828Sgrehan
453221828Sgrehan#define	CAP_START_OFFSET	0x40
454221828Sgrehanstatic int
455221828Sgrehanpci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen)
456221828Sgrehan{
457221828Sgrehan	int i, capoff, capid, reallen;
458221828Sgrehan	uint16_t sts;
459221828Sgrehan
460221828Sgrehan	static u_char endofcap[4] = {
461221828Sgrehan		PCIY_RESERVED, 0, 0, 0
462221828Sgrehan	};
463221828Sgrehan
464221828Sgrehan	assert(caplen > 0 && capdata[0] != PCIY_RESERVED);
465221828Sgrehan
466221828Sgrehan	reallen = roundup2(caplen, 4);		/* dword aligned */
467221828Sgrehan
468221828Sgrehan	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
469221828Sgrehan	if ((sts & PCIM_STATUS_CAPPRESENT) == 0) {
470221828Sgrehan		capoff = CAP_START_OFFSET;
471221828Sgrehan		pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff);
472221828Sgrehan		pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT);
473221828Sgrehan	} else {
474221828Sgrehan		capoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR);
475221828Sgrehan		while (1) {
476221828Sgrehan			assert((capoff & 0x3) == 0);
477221828Sgrehan			capid = pci_get_cfgdata8(pi, capoff);
478221828Sgrehan			if (capid == PCIY_RESERVED)
479221828Sgrehan				break;
480221828Sgrehan			capoff = pci_get_cfgdata8(pi, capoff + 1);
481221828Sgrehan		}
482221828Sgrehan	}
483221828Sgrehan
484221828Sgrehan	/* Check if we have enough space */
485221828Sgrehan	if (capoff + reallen + sizeof(endofcap) > PCI_REGMAX + 1)
486221828Sgrehan		return (-1);
487221828Sgrehan
488221828Sgrehan	/* Copy the capability */
489221828Sgrehan	for (i = 0; i < caplen; i++)
490221828Sgrehan		pci_set_cfgdata8(pi, capoff + i, capdata[i]);
491221828Sgrehan
492221828Sgrehan	/* Set the next capability pointer */
493221828Sgrehan	pci_set_cfgdata8(pi, capoff + 1, capoff + reallen);
494221828Sgrehan
495221828Sgrehan	/* Copy of the reserved capability which serves as the end marker */
496221828Sgrehan	for (i = 0; i < sizeof(endofcap); i++)
497221828Sgrehan		pci_set_cfgdata8(pi, capoff + reallen + i, endofcap[i]);
498221828Sgrehan
499221828Sgrehan	return (0);
500221828Sgrehan}
501221828Sgrehan
502221828Sgrehanstatic struct pci_devemu *
503221828Sgrehanpci_emul_finddev(char *name)
504221828Sgrehan{
505221828Sgrehan	struct pci_devemu **pdpp, *pdp;
506221828Sgrehan
507221828Sgrehan	SET_FOREACH(pdpp, pci_devemu_set) {
508221828Sgrehan		pdp = *pdpp;
509221828Sgrehan		if (!strcmp(pdp->pe_emu, name)) {
510221828Sgrehan			return (pdp);
511221828Sgrehan		}
512221828Sgrehan	}
513221828Sgrehan
514221828Sgrehan	return (NULL);
515221828Sgrehan}
516221828Sgrehan
517221828Sgrehanstatic void
518239085Sneelpci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
519239085Sneel	      char *params)
520221828Sgrehan{
521221828Sgrehan	struct pci_devinst *pdi;
522221828Sgrehan	pdi = malloc(sizeof(struct pci_devinst));
523221828Sgrehan	bzero(pdi, sizeof(*pdi));
524221828Sgrehan
525221828Sgrehan	pdi->pi_vmctx = ctx;
526221828Sgrehan	pdi->pi_bus = 0;
527221828Sgrehan	pdi->pi_slot = slot;
528239085Sneel	pdi->pi_func = func;
529221828Sgrehan	pdi->pi_d = pde;
530221828Sgrehan	snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
531221828Sgrehan
532221828Sgrehan	/* Disable legacy interrupts */
533221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_INTLINE, 255);
534221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_INTPIN, 0);
535221828Sgrehan
536221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_COMMAND,
537221828Sgrehan		    PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
538221828Sgrehan
539221828Sgrehan	if ((*pde->pe_init)(ctx, pdi, params) != 0) {
540221828Sgrehan		free(pdi);
541221828Sgrehan	} else {
542221828Sgrehan		pci_emul_devices++;
543239085Sneel		pci_slotinfo[slot][func].si_devi = pdi;
544221828Sgrehan	}
545221828Sgrehan}
546221828Sgrehan
547221828Sgrehanvoid
548221828Sgrehanpci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr)
549221828Sgrehan{
550221828Sgrehan	int mmc;
551221828Sgrehan
552221828Sgrehan	CTASSERT(sizeof(struct msicap) == 14);
553221828Sgrehan
554221828Sgrehan	/* Number of msi messages must be a power of 2 between 1 and 32 */
555221828Sgrehan	assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32);
556221828Sgrehan	mmc = ffs(msgnum) - 1;
557221828Sgrehan
558221828Sgrehan	bzero(msicap, sizeof(struct msicap));
559221828Sgrehan	msicap->capid = PCIY_MSI;
560221828Sgrehan	msicap->nextptr = nextptr;
561221828Sgrehan	msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1);
562221828Sgrehan}
563221828Sgrehan
564221828Sgrehanint
565221828Sgrehanpci_emul_add_msicap(struct pci_devinst *pi, int msgnum)
566221828Sgrehan{
567221828Sgrehan	struct msicap msicap;
568221828Sgrehan
569221828Sgrehan	pci_populate_msicap(&msicap, msgnum, 0);
570221828Sgrehan
571221828Sgrehan	return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap)));
572221828Sgrehan}
573221828Sgrehan
574246109Sneelstatic void
575246109Sneelpci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum,
576246109Sneel		     uint32_t msix_tab_size, int nextptr)
577246109Sneel{
578246109Sneel	CTASSERT(sizeof(struct msixcap) == 12);
579246109Sneel
580246109Sneel	assert(msix_tab_size % 4096 == 0);
581246109Sneel
582246109Sneel	bzero(msixcap, sizeof(struct msixcap));
583246109Sneel	msixcap->capid = PCIY_MSIX;
584246109Sneel	msixcap->nextptr = nextptr;
585246109Sneel
586246109Sneel	/*
587246109Sneel	 * Message Control Register, all fields set to
588246109Sneel	 * zero except for the Table Size.
589246109Sneel	 * Note: Table size N is encoded as N-1
590246109Sneel	 */
591246109Sneel	msixcap->msgctrl = msgnum - 1;
592246109Sneel
593246109Sneel	/*
594246109Sneel	 * MSI-X BAR setup:
595246109Sneel	 * - MSI-X table start at offset 0
596246109Sneel	 * - PBA table starts at a 4K aligned offset after the MSI-X table
597246109Sneel	 */
598246109Sneel	msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK;
599246109Sneel	msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK);
600246109Sneel}
601246109Sneel
602246109Sneelstatic void
603246109Sneelpci_msix_table_init(struct pci_devinst *pi, int table_entries)
604246109Sneel{
605246109Sneel	int i, table_size;
606246109Sneel
607246109Sneel	assert(table_entries > 0);
608246109Sneel	assert(table_entries <= MAX_MSIX_TABLE_ENTRIES);
609246109Sneel
610246109Sneel	table_size = table_entries * MSIX_TABLE_ENTRY_SIZE;
611246109Sneel	pi->pi_msix.table = malloc(table_size);
612246109Sneel	bzero(pi->pi_msix.table, table_size);
613246109Sneel
614246109Sneel	/* set mask bit of vector control register */
615246109Sneel	for (i = 0; i < table_entries; i++)
616246109Sneel		pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK;
617246109Sneel}
618246109Sneel
619246109Sneelint
620246109Sneelpci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum)
621246109Sneel{
622246109Sneel	uint16_t pba_index;
623246109Sneel	uint32_t tab_size;
624246109Sneel	struct msixcap msixcap;
625246109Sneel
626246109Sneel	assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES);
627246109Sneel	assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0);
628246109Sneel
629246109Sneel	tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE;
630246109Sneel
631246109Sneel	/* Align table size to nearest 4K */
632246109Sneel	tab_size = roundup2(tab_size, 4096);
633246109Sneel
634246109Sneel	pi->pi_msix.table_bar = barnum;
635246109Sneel	pi->pi_msix.pba_bar   = barnum;
636246109Sneel	pi->pi_msix.table_offset = 0;
637246109Sneel	pi->pi_msix.table_count = msgnum;
638246109Sneel	pi->pi_msix.pba_offset = tab_size;
639246109Sneel
640246109Sneel	/* calculate the MMIO size required for MSI-X PBA */
641246109Sneel	pba_index = (msgnum - 1) / (PBA_TABLE_ENTRY_SIZE * 8);
642246109Sneel	pi->pi_msix.pba_size = (pba_index + 1) * PBA_TABLE_ENTRY_SIZE;
643246109Sneel
644246109Sneel	pci_msix_table_init(pi, msgnum);
645246109Sneel
646246109Sneel	pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size, 0);
647246109Sneel
648246109Sneel	/* allocate memory for MSI-X Table and PBA */
649246109Sneel	pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32,
650246109Sneel				tab_size + pi->pi_msix.pba_size);
651246109Sneel
652246109Sneel	return (pci_emul_add_capability(pi, (u_char *)&msixcap,
653246109Sneel					sizeof(msixcap)));
654246109Sneel}
655246109Sneel
656221828Sgrehanvoid
657234761Sgrehanmsixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
658234761Sgrehan		 int bytes, uint32_t val)
659234761Sgrehan{
660234761Sgrehan	uint16_t msgctrl, rwmask;
661234761Sgrehan	int off, table_bar;
662246109Sneel
663234761Sgrehan	off = offset - capoff;
664234761Sgrehan	table_bar = pi->pi_msix.table_bar;
665234761Sgrehan	/* Message Control Register */
666234761Sgrehan	if (off == 2 && bytes == 2) {
667234761Sgrehan		rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK;
668234761Sgrehan		msgctrl = pci_get_cfgdata16(pi, offset);
669234761Sgrehan		msgctrl &= ~rwmask;
670234761Sgrehan		msgctrl |= val & rwmask;
671234761Sgrehan		val = msgctrl;
672234761Sgrehan
673234761Sgrehan		pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE;
674246109Sneel		pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK;
675234761Sgrehan	}
676234761Sgrehan
677234761Sgrehan	CFGWRITE(pi, offset, val, bytes);
678234761Sgrehan}
679234761Sgrehan
680234761Sgrehanvoid
681221828Sgrehanmsicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
682221828Sgrehan		int bytes, uint32_t val)
683221828Sgrehan{
684221828Sgrehan	uint16_t msgctrl, rwmask, msgdata, mme;
685221828Sgrehan	uint32_t addrlo;
686221828Sgrehan
687221828Sgrehan	/*
688221828Sgrehan	 * If guest is writing to the message control register make sure
689221828Sgrehan	 * we do not overwrite read-only fields.
690221828Sgrehan	 */
691221828Sgrehan	if ((offset - capoff) == 2 && bytes == 2) {
692221828Sgrehan		rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE;
693221828Sgrehan		msgctrl = pci_get_cfgdata16(pi, offset);
694221828Sgrehan		msgctrl &= ~rwmask;
695221828Sgrehan		msgctrl |= val & rwmask;
696221828Sgrehan		val = msgctrl;
697221828Sgrehan
698221828Sgrehan		addrlo = pci_get_cfgdata32(pi, capoff + 4);
699221828Sgrehan		if (msgctrl & PCIM_MSICTRL_64BIT)
700221828Sgrehan			msgdata = pci_get_cfgdata16(pi, capoff + 12);
701221828Sgrehan		else
702221828Sgrehan			msgdata = pci_get_cfgdata16(pi, capoff + 8);
703221828Sgrehan
704221828Sgrehan		/*
705221828Sgrehan		 * XXX check delivery mode, destination mode etc
706221828Sgrehan		 */
707221828Sgrehan		mme = msgctrl & PCIM_MSICTRL_MME_MASK;
708221828Sgrehan		pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0;
709221828Sgrehan		if (pi->pi_msi.enabled) {
710221828Sgrehan			pi->pi_msi.cpu = (addrlo >> 12) & 0xff;
711221828Sgrehan			pi->pi_msi.vector = msgdata & 0xff;
712221828Sgrehan			pi->pi_msi.msgnum = 1 << (mme >> 4);
713221828Sgrehan		} else {
714221828Sgrehan			pi->pi_msi.cpu = 0;
715221828Sgrehan			pi->pi_msi.vector = 0;
716221828Sgrehan			pi->pi_msi.msgnum = 0;
717221828Sgrehan		}
718221828Sgrehan	}
719221828Sgrehan
720221828Sgrehan	CFGWRITE(pi, offset, val, bytes);
721221828Sgrehan}
722221828Sgrehan
723221828Sgrehan/*
724221828Sgrehan * This function assumes that 'coff' is in the capabilities region of the
725221828Sgrehan * config space.
726221828Sgrehan */
727221828Sgrehanstatic void
728221828Sgrehanpci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val)
729221828Sgrehan{
730221828Sgrehan	int capid;
731221828Sgrehan	uint8_t capoff, nextoff;
732221828Sgrehan
733221828Sgrehan	/* Do not allow un-aligned writes */
734221828Sgrehan	if ((offset & (bytes - 1)) != 0)
735221828Sgrehan		return;
736221828Sgrehan
737221828Sgrehan	/* Find the capability that we want to update */
738221828Sgrehan	capoff = CAP_START_OFFSET;
739221828Sgrehan	while (1) {
740221828Sgrehan		capid = pci_get_cfgdata8(pi, capoff);
741221828Sgrehan		if (capid == PCIY_RESERVED)
742221828Sgrehan			break;
743221828Sgrehan
744221828Sgrehan		nextoff = pci_get_cfgdata8(pi, capoff + 1);
745221828Sgrehan		if (offset >= capoff && offset < nextoff)
746221828Sgrehan			break;
747221828Sgrehan
748221828Sgrehan		capoff = nextoff;
749221828Sgrehan	}
750221828Sgrehan	assert(offset >= capoff);
751221828Sgrehan
752221828Sgrehan	/*
753221828Sgrehan	 * Capability ID and Next Capability Pointer are readonly
754221828Sgrehan	 */
755221828Sgrehan	if (offset == capoff || offset == capoff + 1)
756221828Sgrehan		return;
757221828Sgrehan
758221828Sgrehan	switch (capid) {
759221828Sgrehan	case PCIY_MSI:
760221828Sgrehan		msicap_cfgwrite(pi, capoff, offset, bytes, val);
761221828Sgrehan		break;
762246109Sneel	case PCIY_MSIX:
763246109Sneel		msixcap_cfgwrite(pi, capoff, offset, bytes, val);
764246109Sneel		break;
765221828Sgrehan	default:
766221828Sgrehan		break;
767221828Sgrehan	}
768221828Sgrehan}
769221828Sgrehan
770221828Sgrehanstatic int
771221828Sgrehanpci_emul_iscap(struct pci_devinst *pi, int offset)
772221828Sgrehan{
773221828Sgrehan	int found;
774221828Sgrehan	uint16_t sts;
775221828Sgrehan	uint8_t capid, lastoff;
776221828Sgrehan
777221828Sgrehan	found = 0;
778221828Sgrehan	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
779221828Sgrehan	if ((sts & PCIM_STATUS_CAPPRESENT) != 0) {
780221828Sgrehan		lastoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR);
781221828Sgrehan		while (1) {
782221828Sgrehan			assert((lastoff & 0x3) == 0);
783221828Sgrehan			capid = pci_get_cfgdata8(pi, lastoff);
784221828Sgrehan			if (capid == PCIY_RESERVED)
785221828Sgrehan				break;
786221828Sgrehan			lastoff = pci_get_cfgdata8(pi, lastoff + 1);
787221828Sgrehan		}
788221828Sgrehan		if (offset >= CAP_START_OFFSET && offset <= lastoff)
789221828Sgrehan			found = 1;
790221828Sgrehan	}
791221828Sgrehan	return (found);
792221828Sgrehan}
793221828Sgrehan
794221828Sgrehanvoid
795221828Sgrehaninit_pci(struct vmctx *ctx)
796221828Sgrehan{
797221828Sgrehan	struct pci_devemu *pde;
798221828Sgrehan	struct slotinfo *si;
799239085Sneel	int slot, func;
800221828Sgrehan
801221828Sgrehan	pci_emul_iobase = PCI_EMUL_IOBASE;
802221828Sgrehan	pci_emul_membase32 = PCI_EMUL_MEMBASE32;
803221828Sgrehan	pci_emul_membase64 = PCI_EMUL_MEMBASE64;
804221828Sgrehan
805239085Sneel	for (slot = 0; slot < MAXSLOTS; slot++) {
806239085Sneel		for (func = 0; func < MAXFUNCS; func++) {
807239085Sneel			si = &pci_slotinfo[slot][func];
808239085Sneel			if (si->si_name != NULL) {
809239085Sneel				pde = pci_emul_finddev(si->si_name);
810239085Sneel				if (pde != NULL) {
811239085Sneel					pci_emul_init(ctx, pde, slot, func,
812239085Sneel						      si->si_param);
813239085Sneel				}
814221828Sgrehan			}
815221828Sgrehan		}
816221828Sgrehan	}
817234938Sgrehan
818234938Sgrehan	/*
819234938Sgrehan	 * Allow ISA IRQs 5,10,11,12, and 15 to be available for
820234938Sgrehan	 * generic use
821234938Sgrehan	 */
822234938Sgrehan	lirq[5].li_generic = 1;
823234938Sgrehan	lirq[10].li_generic = 1;
824234938Sgrehan	lirq[11].li_generic = 1;
825234938Sgrehan	lirq[12].li_generic = 1;
826234938Sgrehan	lirq[15].li_generic = 1;
827221828Sgrehan}
828221828Sgrehan
829221828Sgrehanint
830221828Sgrehanpci_msi_enabled(struct pci_devinst *pi)
831221828Sgrehan{
832221828Sgrehan	return (pi->pi_msi.enabled);
833221828Sgrehan}
834221828Sgrehan
835221828Sgrehanint
836221828Sgrehanpci_msi_msgnum(struct pci_devinst *pi)
837221828Sgrehan{
838221828Sgrehan	if (pi->pi_msi.enabled)
839221828Sgrehan		return (pi->pi_msi.msgnum);
840221828Sgrehan	else
841221828Sgrehan		return (0);
842221828Sgrehan}
843221828Sgrehan
844246109Sneelint
845246109Sneelpci_msix_enabled(struct pci_devinst *pi)
846246109Sneel{
847246109Sneel
848246109Sneel	return (pi->pi_msix.enabled && !pi->pi_msi.enabled);
849246109Sneel}
850246109Sneel
851221828Sgrehanvoid
852246109Sneelpci_generate_msix(struct pci_devinst *pi, int index)
853246109Sneel{
854246109Sneel	struct msix_table_entry *mte;
855246109Sneel
856246109Sneel	if (!pci_msix_enabled(pi))
857246109Sneel		return;
858246109Sneel
859246109Sneel	if (pi->pi_msix.function_mask)
860246109Sneel		return;
861246109Sneel
862246109Sneel	if (index >= pi->pi_msix.table_count)
863246109Sneel		return;
864246109Sneel
865246109Sneel	mte = &pi->pi_msix.table[index];
866246109Sneel	if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
867246109Sneel		/* XXX Set PBA bit if interrupt is disabled */
868246109Sneel		vm_lapic_irq(pi->pi_vmctx,
869246109Sneel			     (mte->addr >> 12) & 0xff, mte->msg_data & 0xff);
870246109Sneel	}
871246109Sneel}
872246109Sneel
873246109Sneelvoid
874221828Sgrehanpci_generate_msi(struct pci_devinst *pi, int msg)
875221828Sgrehan{
876221828Sgrehan
877221828Sgrehan	if (pci_msi_enabled(pi) && msg < pci_msi_msgnum(pi)) {
878221828Sgrehan		vm_lapic_irq(pi->pi_vmctx,
879221828Sgrehan			     pi->pi_msi.cpu,
880221828Sgrehan			     pi->pi_msi.vector + msg);
881221828Sgrehan	}
882221828Sgrehan}
883221828Sgrehan
884234938Sgrehanint
885234938Sgrehanpci_is_legacy(struct pci_devinst *pi)
886234938Sgrehan{
887234938Sgrehan
888239085Sneel	return (pci_slotinfo[pi->pi_slot][pi->pi_func].si_legacy);
889234938Sgrehan}
890234938Sgrehan
891234938Sgrehanstatic int
892234938Sgrehanpci_lintr_alloc(struct pci_devinst *pi, int vec)
893234938Sgrehan{
894234938Sgrehan	int i;
895234938Sgrehan
896234938Sgrehan	assert(vec < NLIRQ);
897234938Sgrehan
898234938Sgrehan	if (vec == -1) {
899234938Sgrehan		for (i = 0; i < NLIRQ; i++) {
900234938Sgrehan			if (lirq[i].li_generic &&
901234938Sgrehan			    lirq[i].li_owner == NULL) {
902234938Sgrehan				vec = i;
903234938Sgrehan				break;
904234938Sgrehan			}
905234938Sgrehan		}
906234938Sgrehan	} else {
907239029Sneel		if (lirq[vec].li_owner != NULL) {
908234938Sgrehan			vec = -1;
909234938Sgrehan		}
910234938Sgrehan	}
911234938Sgrehan	assert(vec != -1);
912234938Sgrehan
913234938Sgrehan	lirq[vec].li_owner = pi;
914234938Sgrehan	pi->pi_lintr_pin = vec;
915234938Sgrehan
916234938Sgrehan	return (vec);
917234938Sgrehan}
918234938Sgrehan
919234938Sgrehanint
920234938Sgrehanpci_lintr_request(struct pci_devinst *pi, int vec)
921234938Sgrehan{
922234938Sgrehan
923234938Sgrehan	vec = pci_lintr_alloc(pi, vec);
924234938Sgrehan	pci_set_cfgdata8(pi, PCIR_INTLINE, vec);
925234938Sgrehan	pci_set_cfgdata8(pi, PCIR_INTPIN, 1);
926234938Sgrehan	return (0);
927234938Sgrehan}
928234938Sgrehan
929234938Sgrehanvoid
930234938Sgrehanpci_lintr_assert(struct pci_devinst *pi)
931234938Sgrehan{
932234938Sgrehan
933234938Sgrehan	assert(pi->pi_lintr_pin);
934239045Sneel	ioapic_assert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
935234938Sgrehan}
936234938Sgrehan
937234938Sgrehanvoid
938234938Sgrehanpci_lintr_deassert(struct pci_devinst *pi)
939234938Sgrehan{
940234938Sgrehan
941234938Sgrehan	assert(pi->pi_lintr_pin);
942239045Sneel	ioapic_deassert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
943234938Sgrehan}
944234938Sgrehan
945239085Sneel/*
946239085Sneel * Return 1 if the emulated device in 'slot' is a multi-function device.
947239085Sneel * Return 0 otherwise.
948239085Sneel */
949239085Sneelstatic int
950239085Sneelpci_emul_is_mfdev(int slot)
951239085Sneel{
952239085Sneel	int f, numfuncs;
953234938Sgrehan
954239085Sneel	numfuncs = 0;
955239085Sneel	for (f = 0; f < MAXFUNCS; f++) {
956239085Sneel		if (pci_slotinfo[slot][f].si_devi != NULL) {
957239085Sneel			numfuncs++;
958239085Sneel		}
959239085Sneel	}
960239085Sneel	return (numfuncs > 1);
961239085Sneel}
962234938Sgrehan
963239085Sneel/*
964239085Sneel * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on
965239085Sneel * whether or not is a multi-function being emulated in the pci 'slot'.
966239085Sneel */
967239085Sneelstatic void
968239085Sneelpci_emul_hdrtype_fixup(int slot, int off, int bytes, uint32_t *rv)
969239085Sneel{
970239085Sneel	int mfdev;
971239085Sneel
972239085Sneel	if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) {
973239085Sneel		mfdev = pci_emul_is_mfdev(slot);
974239085Sneel		switch (bytes) {
975239085Sneel		case 1:
976239085Sneel		case 2:
977239085Sneel			*rv &= ~PCIM_MFDEV;
978239085Sneel			if (mfdev) {
979239085Sneel				*rv |= PCIM_MFDEV;
980239085Sneel			}
981239085Sneel			break;
982239085Sneel		case 4:
983239085Sneel			*rv &= ~(PCIM_MFDEV << 16);
984239085Sneel			if (mfdev) {
985239085Sneel				*rv |= (PCIM_MFDEV << 16);
986239085Sneel			}
987239085Sneel			break;
988239085Sneel		}
989239085Sneel	}
990239085Sneel}
991239085Sneel
992221828Sgrehanstatic int cfgbus, cfgslot, cfgfunc, cfgoff;
993221828Sgrehan
994221828Sgrehanstatic int
995221828Sgrehanpci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
996221828Sgrehan		 uint32_t *eax, void *arg)
997221828Sgrehan{
998221828Sgrehan	uint32_t x;
999221828Sgrehan
1000221828Sgrehan	assert(!in);
1001221828Sgrehan
1002221828Sgrehan	if (bytes != 4)
1003221828Sgrehan		return (-1);
1004221828Sgrehan
1005221828Sgrehan	x = *eax;
1006221828Sgrehan	cfgoff = x & PCI_REGMAX;
1007221828Sgrehan	cfgfunc = (x >> 8) & PCI_FUNCMAX;
1008221828Sgrehan	cfgslot = (x >> 11) & PCI_SLOTMAX;
1009221828Sgrehan	cfgbus = (x >> 16) & PCI_BUSMAX;
1010221828Sgrehan
1011221828Sgrehan	return (0);
1012221828Sgrehan}
1013221828SgrehanINOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_OUT, pci_emul_cfgaddr);
1014221828Sgrehan
1015221828Sgrehanstatic int
1016221828Sgrehanpci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
1017221828Sgrehan		 uint32_t *eax, void *arg)
1018221828Sgrehan{
1019221828Sgrehan	struct pci_devinst *pi;
1020221828Sgrehan	struct pci_devemu *pe;
1021239085Sneel	int coff, idx, needcfg;
1022221828Sgrehan	uint64_t mask, bar;
1023221828Sgrehan
1024221828Sgrehan	assert(bytes == 1 || bytes == 2 || bytes == 4);
1025221828Sgrehan
1026242170Sneel	if (cfgbus == 0)
1027242170Sneel		pi = pci_slotinfo[cfgslot][cfgfunc].si_devi;
1028242170Sneel	else
1029242170Sneel		pi = NULL;
1030242170Sneel
1031221828Sgrehan	coff = cfgoff + (port - CONF1_DATA_PORT);
1032221828Sgrehan
1033221828Sgrehan#if 0
1034221828Sgrehan	printf("pcicfg-%s from 0x%0x of %d bytes (%d/%d/%d)\n\r",
1035221828Sgrehan		in ? "read" : "write", coff, bytes, cfgbus, cfgslot, cfgfunc);
1036221828Sgrehan#endif
1037221828Sgrehan
1038239085Sneel	/*
1039239085Sneel	 * Just return if there is no device at this cfgslot:cfgfunc or
1040239085Sneel	 * if the guest is doing an un-aligned access
1041239085Sneel	 */
1042239085Sneel	if (pi == NULL || (coff & (bytes - 1)) != 0) {
1043221828Sgrehan		if (in)
1044221828Sgrehan			*eax = 0xffffffff;
1045221828Sgrehan		return (0);
1046221828Sgrehan	}
1047221828Sgrehan
1048221828Sgrehan	pe = pi->pi_d;
1049221828Sgrehan
1050221828Sgrehan	/*
1051221828Sgrehan	 * Config read
1052221828Sgrehan	 */
1053221828Sgrehan	if (in) {
1054221828Sgrehan		/* Let the device emulation override the default handler */
1055239085Sneel		if (pe->pe_cfgread != NULL) {
1056239085Sneel			needcfg = pe->pe_cfgread(ctx, vcpu, pi,
1057239085Sneel						    coff, bytes, eax);
1058239085Sneel		} else {
1059239085Sneel			needcfg = 1;
1060239085Sneel		}
1061221828Sgrehan
1062239085Sneel		if (needcfg) {
1063239085Sneel			if (bytes == 1)
1064239085Sneel				*eax = pci_get_cfgdata8(pi, coff);
1065239085Sneel			else if (bytes == 2)
1066239085Sneel				*eax = pci_get_cfgdata16(pi, coff);
1067239085Sneel			else
1068239085Sneel				*eax = pci_get_cfgdata32(pi, coff);
1069239085Sneel		}
1070239085Sneel
1071239085Sneel		pci_emul_hdrtype_fixup(cfgslot, coff, bytes, eax);
1072221828Sgrehan	} else {
1073221828Sgrehan		/* Let the device emulation override the default handler */
1074221828Sgrehan		if (pe->pe_cfgwrite != NULL &&
1075221828Sgrehan		    (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0)
1076221828Sgrehan			return (0);
1077221828Sgrehan
1078221828Sgrehan		/*
1079221828Sgrehan		 * Special handling for write to BAR registers
1080221828Sgrehan		 */
1081221828Sgrehan		if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) {
1082221828Sgrehan			/*
1083221828Sgrehan			 * Ignore writes to BAR registers that are not
1084221828Sgrehan			 * 4-byte aligned.
1085221828Sgrehan			 */
1086221828Sgrehan			if (bytes != 4 || (coff & 0x3) != 0)
1087221828Sgrehan				return (0);
1088221828Sgrehan			idx = (coff - PCIR_BAR(0)) / 4;
1089221828Sgrehan			switch (pi->pi_bar[idx].type) {
1090221828Sgrehan			case PCIBAR_NONE:
1091221828Sgrehan				bar = 0;
1092221828Sgrehan				break;
1093221828Sgrehan			case PCIBAR_IO:
1094221828Sgrehan				mask = ~(pi->pi_bar[idx].size - 1);
1095221828Sgrehan				mask &= PCIM_BAR_IO_BASE;
1096221828Sgrehan				bar = (*eax & mask) | PCIM_BAR_IO_SPACE;
1097221828Sgrehan				break;
1098221828Sgrehan			case PCIBAR_MEM32:
1099221828Sgrehan				mask = ~(pi->pi_bar[idx].size - 1);
1100221828Sgrehan				mask &= PCIM_BAR_MEM_BASE;
1101221828Sgrehan				bar = *eax & mask;
1102221828Sgrehan				bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
1103221828Sgrehan				break;
1104221828Sgrehan			case PCIBAR_MEM64:
1105221828Sgrehan				mask = ~(pi->pi_bar[idx].size - 1);
1106221828Sgrehan				mask &= PCIM_BAR_MEM_BASE;
1107221828Sgrehan				bar = *eax & mask;
1108221828Sgrehan				bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
1109221828Sgrehan				       PCIM_BAR_MEM_PREFETCH;
1110221828Sgrehan				break;
1111221828Sgrehan			case PCIBAR_MEMHI64:
1112221828Sgrehan				mask = ~(pi->pi_bar[idx - 1].size - 1);
1113221828Sgrehan				mask &= PCIM_BAR_MEM_BASE;
1114221828Sgrehan				bar = ((uint64_t)*eax << 32) & mask;
1115221828Sgrehan				bar = bar >> 32;
1116221828Sgrehan				break;
1117221828Sgrehan			default:
1118221828Sgrehan				assert(0);
1119221828Sgrehan			}
1120221828Sgrehan			pci_set_cfgdata32(pi, coff, bar);
1121234761Sgrehan
1122221828Sgrehan		} else if (pci_emul_iscap(pi, coff)) {
1123221828Sgrehan			pci_emul_capwrite(pi, coff, bytes, *eax);
1124221828Sgrehan		} else {
1125221828Sgrehan			CFGWRITE(pi, coff, *eax, bytes);
1126221828Sgrehan		}
1127221828Sgrehan	}
1128221828Sgrehan
1129221828Sgrehan	return (0);
1130221828Sgrehan}
1131221828Sgrehan
1132221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata);
1133221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata);
1134221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
1135221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
1136221828Sgrehan
1137221828Sgrehan/*
1138221828Sgrehan * I/O ports to configure PCI IRQ routing. We ignore all writes to it.
1139221828Sgrehan */
1140221828Sgrehanstatic int
1141221828Sgrehanpci_irq_port_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
1142221828Sgrehan		     uint32_t *eax, void *arg)
1143221828Sgrehan{
1144221828Sgrehan	assert(in == 0);
1145221828Sgrehan	return (0);
1146221828Sgrehan}
1147221828SgrehanINOUT_PORT(pci_irq, 0xC00, IOPORT_F_OUT, pci_irq_port_handler);
1148221828SgrehanINOUT_PORT(pci_irq, 0xC01, IOPORT_F_OUT, pci_irq_port_handler);
1149221828Sgrehan
1150221828Sgrehan#define PCI_EMUL_TEST
1151221828Sgrehan#ifdef PCI_EMUL_TEST
1152221828Sgrehan/*
1153221828Sgrehan * Define a dummy test device
1154221828Sgrehan */
1155241744Sgrehan#define DIOSZ	20
1156241744Sgrehan#define DMEMSZ	4096
1157221828Sgrehanstruct pci_emul_dsoftc {
1158241744Sgrehan	uint8_t   ioregs[DIOSZ];
1159241744Sgrehan	uint8_t	  memregs[DMEMSZ];
1160221828Sgrehan};
1161221828Sgrehan
1162241744Sgrehan#define	PCI_EMUL_MSI_MSGS	 4
1163241744Sgrehan#define	PCI_EMUL_MSIX_MSGS	16
1164221828Sgrehan
1165221942Sjhbstatic int
1166221828Sgrehanpci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
1167221828Sgrehan{
1168221828Sgrehan	int error;
1169221828Sgrehan	struct pci_emul_dsoftc *sc;
1170221828Sgrehan
1171221828Sgrehan	sc = malloc(sizeof(struct pci_emul_dsoftc));
1172221828Sgrehan	memset(sc, 0, sizeof(struct pci_emul_dsoftc));
1173221828Sgrehan
1174221828Sgrehan	pi->pi_arg = sc;
1175221828Sgrehan
1176221828Sgrehan	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001);
1177221828Sgrehan	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD);
1178221828Sgrehan	pci_set_cfgdata8(pi, PCIR_CLASS, 0x02);
1179221828Sgrehan
1180241744Sgrehan	error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS);
1181221828Sgrehan	assert(error == 0);
1182221828Sgrehan
1183241744Sgrehan	error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ);
1184221828Sgrehan	assert(error == 0);
1185221828Sgrehan
1186241744Sgrehan	error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ);
1187241744Sgrehan	assert(error == 0);
1188241744Sgrehan
1189221828Sgrehan	return (0);
1190221828Sgrehan}
1191221828Sgrehan
1192221942Sjhbstatic void
1193241744Sgrehanpci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
1194241744Sgrehan	      uint64_t offset, int size, uint64_t value)
1195221828Sgrehan{
1196221828Sgrehan	int i;
1197221828Sgrehan	struct pci_emul_dsoftc *sc = pi->pi_arg;
1198221828Sgrehan
1199241744Sgrehan	if (baridx == 0) {
1200241744Sgrehan		if (offset + size > DIOSZ) {
1201241744Sgrehan			printf("diow: iow too large, offset %ld size %d\n",
1202241744Sgrehan			       offset, size);
1203241744Sgrehan			return;
1204241744Sgrehan		}
1205221828Sgrehan
1206241744Sgrehan		if (size == 1) {
1207241744Sgrehan			sc->ioregs[offset] = value & 0xff;
1208241744Sgrehan		} else if (size == 2) {
1209241744Sgrehan			*(uint16_t *)&sc->ioregs[offset] = value & 0xffff;
1210241744Sgrehan		} else if (size == 4) {
1211241744Sgrehan			*(uint32_t *)&sc->ioregs[offset] = value;
1212241744Sgrehan		} else {
1213241744Sgrehan			printf("diow: iow unknown size %d\n", size);
1214241744Sgrehan		}
1215241744Sgrehan
1216241744Sgrehan		/*
1217241744Sgrehan		 * Special magic value to generate an interrupt
1218241744Sgrehan		 */
1219241744Sgrehan		if (offset == 4 && size == 4 && pci_msi_enabled(pi))
1220241744Sgrehan			pci_generate_msi(pi, value % pci_msi_msgnum(pi));
1221241744Sgrehan
1222241744Sgrehan		if (value == 0xabcdef) {
1223241744Sgrehan			for (i = 0; i < pci_msi_msgnum(pi); i++)
1224241744Sgrehan				pci_generate_msi(pi, i);
1225241744Sgrehan		}
1226221828Sgrehan	}
1227221828Sgrehan
1228241744Sgrehan	if (baridx == 1) {
1229241744Sgrehan		if (offset + size > DMEMSZ) {
1230241744Sgrehan			printf("diow: memw too large, offset %ld size %d\n",
1231241744Sgrehan			       offset, size);
1232241744Sgrehan			return;
1233241744Sgrehan		}
1234221828Sgrehan
1235241744Sgrehan		if (size == 1) {
1236241744Sgrehan			sc->memregs[offset] = value;
1237241744Sgrehan		} else if (size == 2) {
1238241744Sgrehan			*(uint16_t *)&sc->memregs[offset] = value;
1239241744Sgrehan		} else if (size == 4) {
1240241744Sgrehan			*(uint32_t *)&sc->memregs[offset] = value;
1241241744Sgrehan		} else if (size == 8) {
1242241744Sgrehan			*(uint64_t *)&sc->memregs[offset] = value;
1243241744Sgrehan		} else {
1244241744Sgrehan			printf("diow: memw unknown size %d\n", size);
1245241744Sgrehan		}
1246241744Sgrehan
1247241744Sgrehan		/*
1248241744Sgrehan		 * magic interrupt ??
1249241744Sgrehan		 */
1250221828Sgrehan	}
1251241744Sgrehan
1252241744Sgrehan	if (baridx > 1) {
1253241744Sgrehan		printf("diow: unknown bar idx %d\n", baridx);
1254241744Sgrehan	}
1255221828Sgrehan}
1256221828Sgrehan
1257241744Sgrehanstatic uint64_t
1258241744Sgrehanpci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
1259241744Sgrehan	      uint64_t offset, int size)
1260221828Sgrehan{
1261221828Sgrehan	struct pci_emul_dsoftc *sc = pi->pi_arg;
1262221828Sgrehan	uint32_t value;
1263221828Sgrehan
1264241744Sgrehan	if (baridx == 0) {
1265241744Sgrehan		if (offset + size > DIOSZ) {
1266241744Sgrehan			printf("dior: ior too large, offset %ld size %d\n",
1267241744Sgrehan			       offset, size);
1268241744Sgrehan			return (0);
1269241744Sgrehan		}
1270241744Sgrehan
1271241744Sgrehan		if (size == 1) {
1272241744Sgrehan			value = sc->ioregs[offset];
1273241744Sgrehan		} else if (size == 2) {
1274241744Sgrehan			value = *(uint16_t *) &sc->ioregs[offset];
1275241744Sgrehan		} else if (size == 4) {
1276241744Sgrehan			value = *(uint32_t *) &sc->ioregs[offset];
1277241744Sgrehan		} else {
1278241744Sgrehan			printf("dior: ior unknown size %d\n", size);
1279241744Sgrehan		}
1280221828Sgrehan	}
1281221828Sgrehan
1282241744Sgrehan	if (baridx == 1) {
1283241744Sgrehan		if (offset + size > DMEMSZ) {
1284241744Sgrehan			printf("dior: memr too large, offset %ld size %d\n",
1285241744Sgrehan			       offset, size);
1286241744Sgrehan			return (0);
1287241744Sgrehan		}
1288241744Sgrehan
1289241744Sgrehan		if (size == 1) {
1290241744Sgrehan			value = sc->memregs[offset];
1291241744Sgrehan		} else if (size == 2) {
1292241744Sgrehan			value = *(uint16_t *) &sc->memregs[offset];
1293241744Sgrehan		} else if (size == 4) {
1294241744Sgrehan			value = *(uint32_t *) &sc->memregs[offset];
1295241744Sgrehan		} else if (size == 8) {
1296241744Sgrehan			value = *(uint64_t *) &sc->memregs[offset];
1297241744Sgrehan		} else {
1298241744Sgrehan			printf("dior: ior unknown size %d\n", size);
1299241744Sgrehan		}
1300221828Sgrehan	}
1301221828Sgrehan
1302241744Sgrehan
1303241744Sgrehan	if (baridx > 1) {
1304241744Sgrehan		printf("dior: unknown bar idx %d\n", baridx);
1305241744Sgrehan		return (0);
1306241744Sgrehan	}
1307241744Sgrehan
1308221828Sgrehan	return (value);
1309221828Sgrehan}
1310221828Sgrehan
1311221828Sgrehanstruct pci_devemu pci_dummy = {
1312221828Sgrehan	.pe_emu = "dummy",
1313221828Sgrehan	.pe_init = pci_emul_dinit,
1314241744Sgrehan	.pe_barwrite = pci_emul_diow,
1315241744Sgrehan	.pe_barread = pci_emul_dior
1316221828Sgrehan};
1317221828SgrehanPCI_EMUL_SET(pci_dummy);
1318221828Sgrehan
1319221828Sgrehan#endif /* PCI_EMUL_TEST */
1320