pci_emul.c revision 242170
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$
27221828Sgrehan */
28221828Sgrehan
29221828Sgrehan#include <sys/cdefs.h>
30221828Sgrehan__FBSDID("$FreeBSD$");
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
45221828Sgrehan#include "fbsdrun.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
170241744Sgrehanpci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
171241744Sgrehan		    uint32_t *eax, void *arg)
172221828Sgrehan{
173221828Sgrehan	struct pci_devinst *pdi = arg;
174221828Sgrehan	struct pci_devemu *pe = pdi->pi_d;
175241744Sgrehan	uint64_t offset;
176241744Sgrehan	int i;
177221828Sgrehan
178221828Sgrehan	for (i = 0; i <= PCI_BARMAX; i++) {
179221828Sgrehan		if (pdi->pi_bar[i].type == PCIBAR_IO &&
180221828Sgrehan		    port >= pdi->pi_bar[i].addr &&
181241744Sgrehan		    port + bytes <=
182241744Sgrehan		        pdi->pi_bar[i].addr + pdi->pi_bar[i].size) {
183221828Sgrehan			offset = port - pdi->pi_bar[i].addr;
184221828Sgrehan			if (in)
185241744Sgrehan				*eax = (*pe->pe_barread)(ctx, vcpu, pdi, i,
186241744Sgrehan							 offset, bytes);
187221828Sgrehan			else
188241744Sgrehan				(*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset,
189241744Sgrehan						   bytes, *eax);
190221828Sgrehan			return (0);
191221828Sgrehan		}
192221828Sgrehan	}
193221828Sgrehan	return (-1);
194221828Sgrehan}
195221828Sgrehan
196221828Sgrehanstatic int
197241744Sgrehanpci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
198241744Sgrehan		     int size, uint64_t *val, void *arg1, long arg2)
199241744Sgrehan{
200241744Sgrehan	struct pci_devinst *pdi = arg1;
201241744Sgrehan	struct pci_devemu *pe = pdi->pi_d;
202241744Sgrehan	uint64_t offset;
203241744Sgrehan	int bidx = (int) arg2;
204241744Sgrehan
205241744Sgrehan	assert(bidx <= PCI_BARMAX);
206241744Sgrehan	assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 ||
207241744Sgrehan	       pdi->pi_bar[bidx].type == PCIBAR_MEM64);
208241744Sgrehan	assert(addr >= pdi->pi_bar[bidx].addr &&
209241744Sgrehan	       addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size);
210241744Sgrehan
211241744Sgrehan	offset = addr - pdi->pi_bar[bidx].addr;
212241744Sgrehan
213241744Sgrehan	if (dir == MEM_F_WRITE)
214241744Sgrehan		(*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, size, *val);
215241744Sgrehan	else
216241744Sgrehan		*val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset, size);
217241744Sgrehan
218241744Sgrehan	return (0);
219241744Sgrehan}
220241744Sgrehan
221241744Sgrehan
222241744Sgrehanstatic int
223221828Sgrehanpci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size,
224221828Sgrehan			uint64_t *addr)
225221828Sgrehan{
226221828Sgrehan	uint64_t base;
227221828Sgrehan
228221828Sgrehan	assert((size & (size - 1)) == 0);	/* must be a power of 2 */
229221828Sgrehan
230221828Sgrehan	base = roundup2(*baseptr, size);
231221828Sgrehan
232221828Sgrehan	if (base + size <= limit) {
233221828Sgrehan		*addr = base;
234221828Sgrehan		*baseptr = base + size;
235221828Sgrehan		return (0);
236221828Sgrehan	} else
237221828Sgrehan		return (-1);
238221828Sgrehan}
239221828Sgrehan
240221828Sgrehanint
241241744Sgrehanpci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type,
242241744Sgrehan		   uint64_t size)
243221828Sgrehan{
244241744Sgrehan
245241744Sgrehan	return (pci_emul_alloc_pbar(pdi, idx, 0, type, size));
246241744Sgrehan}
247241744Sgrehan
248241744Sgrehanint
249241744Sgrehanpci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase,
250241744Sgrehan		    enum pcibar_type type, uint64_t size)
251241744Sgrehan{
252221828Sgrehan	int i, error;
253221828Sgrehan	uint64_t *baseptr, limit, addr, mask, lobits, bar;
254221828Sgrehan	struct inout_port iop;
255241744Sgrehan	struct mem_range memp;
256221828Sgrehan
257221828Sgrehan	assert(idx >= 0 && idx <= PCI_BARMAX);
258221828Sgrehan
259221828Sgrehan	if ((size & (size - 1)) != 0)
260221828Sgrehan		size = 1UL << flsl(size);	/* round up to a power of 2 */
261221828Sgrehan
262221828Sgrehan	switch (type) {
263221828Sgrehan	case PCIBAR_NONE:
264221828Sgrehan		baseptr = NULL;
265221828Sgrehan		addr = mask = lobits = 0;
266221828Sgrehan		break;
267221828Sgrehan	case PCIBAR_IO:
268239085Sneel		if (hostbase &&
269239085Sneel		    pci_slotinfo[pdi->pi_slot][pdi->pi_func].si_legacy) {
270234938Sgrehan			assert(hostbase < PCI_EMUL_IOBASE);
271234938Sgrehan			baseptr = &hostbase;
272234938Sgrehan		} else {
273234938Sgrehan			baseptr = &pci_emul_iobase;
274234938Sgrehan		}
275221828Sgrehan		limit = PCI_EMUL_IOLIMIT;
276221828Sgrehan		mask = PCIM_BAR_IO_BASE;
277221828Sgrehan		lobits = PCIM_BAR_IO_SPACE;
278221828Sgrehan		break;
279221828Sgrehan	case PCIBAR_MEM64:
280221828Sgrehan		/*
281221828Sgrehan		 * XXX
282221828Sgrehan		 * Some drivers do not work well if the 64-bit BAR is allocated
283221828Sgrehan		 * above 4GB. Allow for this by allocating small requests under
284221828Sgrehan		 * 4GB unless then allocation size is larger than some arbitrary
285221828Sgrehan		 * number (32MB currently).
286221828Sgrehan		 */
287221828Sgrehan		if (size > 32 * 1024 * 1024) {
288221828Sgrehan			/*
289221828Sgrehan			 * XXX special case for device requiring peer-peer DMA
290221828Sgrehan			 */
291221828Sgrehan			if (size == 0x100000000UL)
292221828Sgrehan				baseptr = &hostbase;
293221828Sgrehan			else
294221828Sgrehan				baseptr = &pci_emul_membase64;
295221828Sgrehan			limit = PCI_EMUL_MEMLIMIT64;
296221828Sgrehan			mask = PCIM_BAR_MEM_BASE;
297221828Sgrehan			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
298221828Sgrehan				 PCIM_BAR_MEM_PREFETCH;
299221828Sgrehan			break;
300239086Sneel		} else {
301239086Sneel			baseptr = &pci_emul_membase32;
302239086Sneel			limit = PCI_EMUL_MEMLIMIT32;
303239086Sneel			mask = PCIM_BAR_MEM_BASE;
304239086Sneel			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
305221828Sgrehan		}
306239086Sneel		break;
307221828Sgrehan	case PCIBAR_MEM32:
308221828Sgrehan		baseptr = &pci_emul_membase32;
309221828Sgrehan		limit = PCI_EMUL_MEMLIMIT32;
310221828Sgrehan		mask = PCIM_BAR_MEM_BASE;
311221828Sgrehan		lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
312221828Sgrehan		break;
313221828Sgrehan	default:
314221828Sgrehan		printf("pci_emul_alloc_base: invalid bar type %d\n", type);
315221828Sgrehan		assert(0);
316221828Sgrehan	}
317221828Sgrehan
318221828Sgrehan	if (baseptr != NULL) {
319221828Sgrehan		error = pci_emul_alloc_resource(baseptr, limit, size, &addr);
320221828Sgrehan		if (error != 0)
321221828Sgrehan			return (error);
322221828Sgrehan	}
323221828Sgrehan
324221828Sgrehan	pdi->pi_bar[idx].type = type;
325221828Sgrehan	pdi->pi_bar[idx].addr = addr;
326221828Sgrehan	pdi->pi_bar[idx].size = size;
327221828Sgrehan
328221828Sgrehan	/* Initialize the BAR register in config space */
329221828Sgrehan	bar = (addr & mask) | lobits;
330221828Sgrehan	pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar);
331221828Sgrehan
332221828Sgrehan	if (type == PCIBAR_MEM64) {
333221828Sgrehan		assert(idx + 1 <= PCI_BARMAX);
334221828Sgrehan		pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64;
335221828Sgrehan		pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
336221828Sgrehan	}
337221828Sgrehan
338221828Sgrehan	/* add a handler to intercept accesses to the I/O bar */
339221828Sgrehan	if (type == PCIBAR_IO) {
340221828Sgrehan		iop.name = pdi->pi_name;
341221828Sgrehan		iop.flags = IOPORT_F_INOUT;
342241744Sgrehan		iop.handler = pci_emul_io_handler;
343221828Sgrehan		iop.arg = pdi;
344221828Sgrehan
345221828Sgrehan		for (i = 0; i < size; i++) {
346221828Sgrehan			iop.port = addr + i;
347221828Sgrehan			register_inout(&iop);
348221828Sgrehan		}
349241744Sgrehan	} else if (type == PCIBAR_MEM32 || type == PCIBAR_MEM64) {
350241744Sgrehan		/* add memory bar intercept handler */
351241744Sgrehan		memp.name = pdi->pi_name;
352241744Sgrehan		memp.flags = MEM_F_RW;
353241744Sgrehan		memp.base = addr;
354241744Sgrehan		memp.size = size;
355241744Sgrehan		memp.handler = pci_emul_mem_handler;
356241744Sgrehan		memp.arg1 = pdi;
357241744Sgrehan		memp.arg2 = idx;
358241744Sgrehan
359241744Sgrehan		error = register_mem(&memp);
360241744Sgrehan		assert(error == 0);
361221828Sgrehan	}
362221828Sgrehan
363221828Sgrehan	return (0);
364221828Sgrehan}
365221828Sgrehan
366221828Sgrehan#define	CAP_START_OFFSET	0x40
367221828Sgrehanstatic int
368221828Sgrehanpci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen)
369221828Sgrehan{
370221828Sgrehan	int i, capoff, capid, reallen;
371221828Sgrehan	uint16_t sts;
372221828Sgrehan
373221828Sgrehan	static u_char endofcap[4] = {
374221828Sgrehan		PCIY_RESERVED, 0, 0, 0
375221828Sgrehan	};
376221828Sgrehan
377221828Sgrehan	assert(caplen > 0 && capdata[0] != PCIY_RESERVED);
378221828Sgrehan
379221828Sgrehan	reallen = roundup2(caplen, 4);		/* dword aligned */
380221828Sgrehan
381221828Sgrehan	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
382221828Sgrehan	if ((sts & PCIM_STATUS_CAPPRESENT) == 0) {
383221828Sgrehan		capoff = CAP_START_OFFSET;
384221828Sgrehan		pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff);
385221828Sgrehan		pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT);
386221828Sgrehan	} else {
387221828Sgrehan		capoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR);
388221828Sgrehan		while (1) {
389221828Sgrehan			assert((capoff & 0x3) == 0);
390221828Sgrehan			capid = pci_get_cfgdata8(pi, capoff);
391221828Sgrehan			if (capid == PCIY_RESERVED)
392221828Sgrehan				break;
393221828Sgrehan			capoff = pci_get_cfgdata8(pi, capoff + 1);
394221828Sgrehan		}
395221828Sgrehan	}
396221828Sgrehan
397221828Sgrehan	/* Check if we have enough space */
398221828Sgrehan	if (capoff + reallen + sizeof(endofcap) > PCI_REGMAX + 1)
399221828Sgrehan		return (-1);
400221828Sgrehan
401221828Sgrehan	/* Copy the capability */
402221828Sgrehan	for (i = 0; i < caplen; i++)
403221828Sgrehan		pci_set_cfgdata8(pi, capoff + i, capdata[i]);
404221828Sgrehan
405221828Sgrehan	/* Set the next capability pointer */
406221828Sgrehan	pci_set_cfgdata8(pi, capoff + 1, capoff + reallen);
407221828Sgrehan
408221828Sgrehan	/* Copy of the reserved capability which serves as the end marker */
409221828Sgrehan	for (i = 0; i < sizeof(endofcap); i++)
410221828Sgrehan		pci_set_cfgdata8(pi, capoff + reallen + i, endofcap[i]);
411221828Sgrehan
412221828Sgrehan	return (0);
413221828Sgrehan}
414221828Sgrehan
415221828Sgrehanstatic struct pci_devemu *
416221828Sgrehanpci_emul_finddev(char *name)
417221828Sgrehan{
418221828Sgrehan	struct pci_devemu **pdpp, *pdp;
419221828Sgrehan
420221828Sgrehan	SET_FOREACH(pdpp, pci_devemu_set) {
421221828Sgrehan		pdp = *pdpp;
422221828Sgrehan		if (!strcmp(pdp->pe_emu, name)) {
423221828Sgrehan			return (pdp);
424221828Sgrehan		}
425221828Sgrehan	}
426221828Sgrehan
427221828Sgrehan	return (NULL);
428221828Sgrehan}
429221828Sgrehan
430221828Sgrehanstatic void
431239085Sneelpci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
432239085Sneel	      char *params)
433221828Sgrehan{
434221828Sgrehan	struct pci_devinst *pdi;
435221828Sgrehan	pdi = malloc(sizeof(struct pci_devinst));
436221828Sgrehan	bzero(pdi, sizeof(*pdi));
437221828Sgrehan
438221828Sgrehan	pdi->pi_vmctx = ctx;
439221828Sgrehan	pdi->pi_bus = 0;
440221828Sgrehan	pdi->pi_slot = slot;
441239085Sneel	pdi->pi_func = func;
442221828Sgrehan	pdi->pi_d = pde;
443221828Sgrehan	snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
444221828Sgrehan
445221828Sgrehan	/* Disable legacy interrupts */
446221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_INTLINE, 255);
447221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_INTPIN, 0);
448221828Sgrehan
449221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_COMMAND,
450221828Sgrehan		    PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
451221828Sgrehan
452221828Sgrehan	if ((*pde->pe_init)(ctx, pdi, params) != 0) {
453221828Sgrehan		free(pdi);
454221828Sgrehan	} else {
455221828Sgrehan		pci_emul_devices++;
456239085Sneel		pci_slotinfo[slot][func].si_devi = pdi;
457221828Sgrehan	}
458221828Sgrehan}
459221828Sgrehan
460221828Sgrehanvoid
461221828Sgrehanpci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr)
462221828Sgrehan{
463221828Sgrehan	int mmc;
464221828Sgrehan
465221828Sgrehan	CTASSERT(sizeof(struct msicap) == 14);
466221828Sgrehan
467221828Sgrehan	/* Number of msi messages must be a power of 2 between 1 and 32 */
468221828Sgrehan	assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32);
469221828Sgrehan	mmc = ffs(msgnum) - 1;
470221828Sgrehan
471221828Sgrehan	bzero(msicap, sizeof(struct msicap));
472221828Sgrehan	msicap->capid = PCIY_MSI;
473221828Sgrehan	msicap->nextptr = nextptr;
474221828Sgrehan	msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1);
475221828Sgrehan}
476221828Sgrehan
477221828Sgrehanint
478221828Sgrehanpci_emul_add_msicap(struct pci_devinst *pi, int msgnum)
479221828Sgrehan{
480221828Sgrehan	struct msicap msicap;
481221828Sgrehan
482221828Sgrehan	pci_populate_msicap(&msicap, msgnum, 0);
483221828Sgrehan
484221828Sgrehan	return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap)));
485221828Sgrehan}
486221828Sgrehan
487221828Sgrehanvoid
488234761Sgrehanmsixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
489234761Sgrehan		 int bytes, uint32_t val)
490234761Sgrehan{
491234761Sgrehan	uint16_t msgctrl, rwmask;
492234761Sgrehan	int off, table_bar;
493234761Sgrehan
494234761Sgrehan	off = offset - capoff;
495234761Sgrehan	table_bar = pi->pi_msix.table_bar;
496234761Sgrehan	/* Message Control Register */
497234761Sgrehan	if (off == 2 && bytes == 2) {
498234761Sgrehan		rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK;
499234761Sgrehan		msgctrl = pci_get_cfgdata16(pi, offset);
500234761Sgrehan		msgctrl &= ~rwmask;
501234761Sgrehan		msgctrl |= val & rwmask;
502234761Sgrehan		val = msgctrl;
503234761Sgrehan
504234761Sgrehan		pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE;
505234761Sgrehan	}
506234761Sgrehan
507234761Sgrehan	CFGWRITE(pi, offset, val, bytes);
508234761Sgrehan}
509234761Sgrehan
510234761Sgrehanvoid
511221828Sgrehanmsicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
512221828Sgrehan		int bytes, uint32_t val)
513221828Sgrehan{
514221828Sgrehan	uint16_t msgctrl, rwmask, msgdata, mme;
515221828Sgrehan	uint32_t addrlo;
516221828Sgrehan
517221828Sgrehan	/*
518221828Sgrehan	 * If guest is writing to the message control register make sure
519221828Sgrehan	 * we do not overwrite read-only fields.
520221828Sgrehan	 */
521221828Sgrehan	if ((offset - capoff) == 2 && bytes == 2) {
522221828Sgrehan		rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE;
523221828Sgrehan		msgctrl = pci_get_cfgdata16(pi, offset);
524221828Sgrehan		msgctrl &= ~rwmask;
525221828Sgrehan		msgctrl |= val & rwmask;
526221828Sgrehan		val = msgctrl;
527221828Sgrehan
528221828Sgrehan		addrlo = pci_get_cfgdata32(pi, capoff + 4);
529221828Sgrehan		if (msgctrl & PCIM_MSICTRL_64BIT)
530221828Sgrehan			msgdata = pci_get_cfgdata16(pi, capoff + 12);
531221828Sgrehan		else
532221828Sgrehan			msgdata = pci_get_cfgdata16(pi, capoff + 8);
533221828Sgrehan
534221828Sgrehan		/*
535221828Sgrehan		 * XXX check delivery mode, destination mode etc
536221828Sgrehan		 */
537221828Sgrehan		mme = msgctrl & PCIM_MSICTRL_MME_MASK;
538221828Sgrehan		pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0;
539221828Sgrehan		if (pi->pi_msi.enabled) {
540221828Sgrehan			pi->pi_msi.cpu = (addrlo >> 12) & 0xff;
541221828Sgrehan			pi->pi_msi.vector = msgdata & 0xff;
542221828Sgrehan			pi->pi_msi.msgnum = 1 << (mme >> 4);
543221828Sgrehan		} else {
544221828Sgrehan			pi->pi_msi.cpu = 0;
545221828Sgrehan			pi->pi_msi.vector = 0;
546221828Sgrehan			pi->pi_msi.msgnum = 0;
547221828Sgrehan		}
548221828Sgrehan	}
549221828Sgrehan
550221828Sgrehan	CFGWRITE(pi, offset, val, bytes);
551221828Sgrehan}
552221828Sgrehan
553221828Sgrehan/*
554221828Sgrehan * This function assumes that 'coff' is in the capabilities region of the
555221828Sgrehan * config space.
556221828Sgrehan */
557221828Sgrehanstatic void
558221828Sgrehanpci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val)
559221828Sgrehan{
560221828Sgrehan	int capid;
561221828Sgrehan	uint8_t capoff, nextoff;
562221828Sgrehan
563221828Sgrehan	/* Do not allow un-aligned writes */
564221828Sgrehan	if ((offset & (bytes - 1)) != 0)
565221828Sgrehan		return;
566221828Sgrehan
567221828Sgrehan	/* Find the capability that we want to update */
568221828Sgrehan	capoff = CAP_START_OFFSET;
569221828Sgrehan	while (1) {
570221828Sgrehan		capid = pci_get_cfgdata8(pi, capoff);
571221828Sgrehan		if (capid == PCIY_RESERVED)
572221828Sgrehan			break;
573221828Sgrehan
574221828Sgrehan		nextoff = pci_get_cfgdata8(pi, capoff + 1);
575221828Sgrehan		if (offset >= capoff && offset < nextoff)
576221828Sgrehan			break;
577221828Sgrehan
578221828Sgrehan		capoff = nextoff;
579221828Sgrehan	}
580221828Sgrehan	assert(offset >= capoff);
581221828Sgrehan
582221828Sgrehan	/*
583221828Sgrehan	 * Capability ID and Next Capability Pointer are readonly
584221828Sgrehan	 */
585221828Sgrehan	if (offset == capoff || offset == capoff + 1)
586221828Sgrehan		return;
587221828Sgrehan
588221828Sgrehan	switch (capid) {
589221828Sgrehan	case PCIY_MSI:
590221828Sgrehan		msicap_cfgwrite(pi, capoff, offset, bytes, val);
591221828Sgrehan		break;
592221828Sgrehan	default:
593221828Sgrehan		break;
594221828Sgrehan	}
595221828Sgrehan}
596221828Sgrehan
597221828Sgrehanstatic int
598221828Sgrehanpci_emul_iscap(struct pci_devinst *pi, int offset)
599221828Sgrehan{
600221828Sgrehan	int found;
601221828Sgrehan	uint16_t sts;
602221828Sgrehan	uint8_t capid, lastoff;
603221828Sgrehan
604221828Sgrehan	found = 0;
605221828Sgrehan	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
606221828Sgrehan	if ((sts & PCIM_STATUS_CAPPRESENT) != 0) {
607221828Sgrehan		lastoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR);
608221828Sgrehan		while (1) {
609221828Sgrehan			assert((lastoff & 0x3) == 0);
610221828Sgrehan			capid = pci_get_cfgdata8(pi, lastoff);
611221828Sgrehan			if (capid == PCIY_RESERVED)
612221828Sgrehan				break;
613221828Sgrehan			lastoff = pci_get_cfgdata8(pi, lastoff + 1);
614221828Sgrehan		}
615221828Sgrehan		if (offset >= CAP_START_OFFSET && offset <= lastoff)
616221828Sgrehan			found = 1;
617221828Sgrehan	}
618221828Sgrehan	return (found);
619221828Sgrehan}
620221828Sgrehan
621221828Sgrehanvoid
622221828Sgrehaninit_pci(struct vmctx *ctx)
623221828Sgrehan{
624221828Sgrehan	struct pci_devemu *pde;
625221828Sgrehan	struct slotinfo *si;
626239085Sneel	int slot, func;
627221828Sgrehan
628221828Sgrehan	pci_emul_iobase = PCI_EMUL_IOBASE;
629221828Sgrehan	pci_emul_membase32 = PCI_EMUL_MEMBASE32;
630221828Sgrehan	pci_emul_membase64 = PCI_EMUL_MEMBASE64;
631221828Sgrehan
632239085Sneel	for (slot = 0; slot < MAXSLOTS; slot++) {
633239085Sneel		for (func = 0; func < MAXFUNCS; func++) {
634239085Sneel			si = &pci_slotinfo[slot][func];
635239085Sneel			if (si->si_name != NULL) {
636239085Sneel				pde = pci_emul_finddev(si->si_name);
637239085Sneel				if (pde != NULL) {
638239085Sneel					pci_emul_init(ctx, pde, slot, func,
639239085Sneel						      si->si_param);
640239085Sneel				}
641221828Sgrehan			}
642221828Sgrehan		}
643221828Sgrehan	}
644234938Sgrehan
645234938Sgrehan	/*
646234938Sgrehan	 * Allow ISA IRQs 5,10,11,12, and 15 to be available for
647234938Sgrehan	 * generic use
648234938Sgrehan	 */
649234938Sgrehan	lirq[5].li_generic = 1;
650234938Sgrehan	lirq[10].li_generic = 1;
651234938Sgrehan	lirq[11].li_generic = 1;
652234938Sgrehan	lirq[12].li_generic = 1;
653234938Sgrehan	lirq[15].li_generic = 1;
654221828Sgrehan}
655221828Sgrehan
656221828Sgrehanint
657221828Sgrehanpci_msi_enabled(struct pci_devinst *pi)
658221828Sgrehan{
659221828Sgrehan	return (pi->pi_msi.enabled);
660221828Sgrehan}
661221828Sgrehan
662221828Sgrehanint
663221828Sgrehanpci_msi_msgnum(struct pci_devinst *pi)
664221828Sgrehan{
665221828Sgrehan	if (pi->pi_msi.enabled)
666221828Sgrehan		return (pi->pi_msi.msgnum);
667221828Sgrehan	else
668221828Sgrehan		return (0);
669221828Sgrehan}
670221828Sgrehan
671221828Sgrehanvoid
672221828Sgrehanpci_generate_msi(struct pci_devinst *pi, int msg)
673221828Sgrehan{
674221828Sgrehan
675221828Sgrehan	if (pci_msi_enabled(pi) && msg < pci_msi_msgnum(pi)) {
676221828Sgrehan		vm_lapic_irq(pi->pi_vmctx,
677221828Sgrehan			     pi->pi_msi.cpu,
678221828Sgrehan			     pi->pi_msi.vector + msg);
679221828Sgrehan	}
680221828Sgrehan}
681221828Sgrehan
682234938Sgrehanint
683234938Sgrehanpci_is_legacy(struct pci_devinst *pi)
684234938Sgrehan{
685234938Sgrehan
686239085Sneel	return (pci_slotinfo[pi->pi_slot][pi->pi_func].si_legacy);
687234938Sgrehan}
688234938Sgrehan
689234938Sgrehanstatic int
690234938Sgrehanpci_lintr_alloc(struct pci_devinst *pi, int vec)
691234938Sgrehan{
692234938Sgrehan	int i;
693234938Sgrehan
694234938Sgrehan	assert(vec < NLIRQ);
695234938Sgrehan
696234938Sgrehan	if (vec == -1) {
697234938Sgrehan		for (i = 0; i < NLIRQ; i++) {
698234938Sgrehan			if (lirq[i].li_generic &&
699234938Sgrehan			    lirq[i].li_owner == NULL) {
700234938Sgrehan				vec = i;
701234938Sgrehan				break;
702234938Sgrehan			}
703234938Sgrehan		}
704234938Sgrehan	} else {
705239029Sneel		if (lirq[vec].li_owner != NULL) {
706234938Sgrehan			vec = -1;
707234938Sgrehan		}
708234938Sgrehan	}
709234938Sgrehan	assert(vec != -1);
710234938Sgrehan
711234938Sgrehan	lirq[vec].li_owner = pi;
712234938Sgrehan	pi->pi_lintr_pin = vec;
713234938Sgrehan
714234938Sgrehan	return (vec);
715234938Sgrehan}
716234938Sgrehan
717234938Sgrehanint
718234938Sgrehanpci_lintr_request(struct pci_devinst *pi, int vec)
719234938Sgrehan{
720234938Sgrehan
721234938Sgrehan	vec = pci_lintr_alloc(pi, vec);
722234938Sgrehan	pci_set_cfgdata8(pi, PCIR_INTLINE, vec);
723234938Sgrehan	pci_set_cfgdata8(pi, PCIR_INTPIN, 1);
724234938Sgrehan	return (0);
725234938Sgrehan}
726234938Sgrehan
727234938Sgrehanvoid
728234938Sgrehanpci_lintr_assert(struct pci_devinst *pi)
729234938Sgrehan{
730234938Sgrehan
731234938Sgrehan	assert(pi->pi_lintr_pin);
732239045Sneel	ioapic_assert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
733234938Sgrehan}
734234938Sgrehan
735234938Sgrehanvoid
736234938Sgrehanpci_lintr_deassert(struct pci_devinst *pi)
737234938Sgrehan{
738234938Sgrehan
739234938Sgrehan	assert(pi->pi_lintr_pin);
740239045Sneel	ioapic_deassert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
741234938Sgrehan}
742234938Sgrehan
743239085Sneel/*
744239085Sneel * Return 1 if the emulated device in 'slot' is a multi-function device.
745239085Sneel * Return 0 otherwise.
746239085Sneel */
747239085Sneelstatic int
748239085Sneelpci_emul_is_mfdev(int slot)
749239085Sneel{
750239085Sneel	int f, numfuncs;
751234938Sgrehan
752239085Sneel	numfuncs = 0;
753239085Sneel	for (f = 0; f < MAXFUNCS; f++) {
754239085Sneel		if (pci_slotinfo[slot][f].si_devi != NULL) {
755239085Sneel			numfuncs++;
756239085Sneel		}
757239085Sneel	}
758239085Sneel	return (numfuncs > 1);
759239085Sneel}
760234938Sgrehan
761239085Sneel/*
762239085Sneel * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on
763239085Sneel * whether or not is a multi-function being emulated in the pci 'slot'.
764239085Sneel */
765239085Sneelstatic void
766239085Sneelpci_emul_hdrtype_fixup(int slot, int off, int bytes, uint32_t *rv)
767239085Sneel{
768239085Sneel	int mfdev;
769239085Sneel
770239085Sneel	if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) {
771239085Sneel		mfdev = pci_emul_is_mfdev(slot);
772239085Sneel		switch (bytes) {
773239085Sneel		case 1:
774239085Sneel		case 2:
775239085Sneel			*rv &= ~PCIM_MFDEV;
776239085Sneel			if (mfdev) {
777239085Sneel				*rv |= PCIM_MFDEV;
778239085Sneel			}
779239085Sneel			break;
780239085Sneel		case 4:
781239085Sneel			*rv &= ~(PCIM_MFDEV << 16);
782239085Sneel			if (mfdev) {
783239085Sneel				*rv |= (PCIM_MFDEV << 16);
784239085Sneel			}
785239085Sneel			break;
786239085Sneel		}
787239085Sneel	}
788239085Sneel}
789239085Sneel
790221828Sgrehanstatic int cfgbus, cfgslot, cfgfunc, cfgoff;
791221828Sgrehan
792221828Sgrehanstatic int
793221828Sgrehanpci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
794221828Sgrehan		 uint32_t *eax, void *arg)
795221828Sgrehan{
796221828Sgrehan	uint32_t x;
797221828Sgrehan
798221828Sgrehan	assert(!in);
799221828Sgrehan
800221828Sgrehan	if (bytes != 4)
801221828Sgrehan		return (-1);
802221828Sgrehan
803221828Sgrehan	x = *eax;
804221828Sgrehan	cfgoff = x & PCI_REGMAX;
805221828Sgrehan	cfgfunc = (x >> 8) & PCI_FUNCMAX;
806221828Sgrehan	cfgslot = (x >> 11) & PCI_SLOTMAX;
807221828Sgrehan	cfgbus = (x >> 16) & PCI_BUSMAX;
808221828Sgrehan
809221828Sgrehan	return (0);
810221828Sgrehan}
811221828SgrehanINOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_OUT, pci_emul_cfgaddr);
812221828Sgrehan
813221828Sgrehanstatic int
814221828Sgrehanpci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
815221828Sgrehan		 uint32_t *eax, void *arg)
816221828Sgrehan{
817221828Sgrehan	struct pci_devinst *pi;
818221828Sgrehan	struct pci_devemu *pe;
819239085Sneel	int coff, idx, needcfg;
820221828Sgrehan	uint64_t mask, bar;
821221828Sgrehan
822221828Sgrehan	assert(bytes == 1 || bytes == 2 || bytes == 4);
823221828Sgrehan
824242170Sneel	if (cfgbus == 0)
825242170Sneel		pi = pci_slotinfo[cfgslot][cfgfunc].si_devi;
826242170Sneel	else
827242170Sneel		pi = NULL;
828242170Sneel
829221828Sgrehan	coff = cfgoff + (port - CONF1_DATA_PORT);
830221828Sgrehan
831221828Sgrehan#if 0
832221828Sgrehan	printf("pcicfg-%s from 0x%0x of %d bytes (%d/%d/%d)\n\r",
833221828Sgrehan		in ? "read" : "write", coff, bytes, cfgbus, cfgslot, cfgfunc);
834221828Sgrehan#endif
835221828Sgrehan
836239085Sneel	/*
837239085Sneel	 * Just return if there is no device at this cfgslot:cfgfunc or
838239085Sneel	 * if the guest is doing an un-aligned access
839239085Sneel	 */
840239085Sneel	if (pi == NULL || (coff & (bytes - 1)) != 0) {
841221828Sgrehan		if (in)
842221828Sgrehan			*eax = 0xffffffff;
843221828Sgrehan		return (0);
844221828Sgrehan	}
845221828Sgrehan
846221828Sgrehan	pe = pi->pi_d;
847221828Sgrehan
848221828Sgrehan	/*
849221828Sgrehan	 * Config read
850221828Sgrehan	 */
851221828Sgrehan	if (in) {
852221828Sgrehan		/* Let the device emulation override the default handler */
853239085Sneel		if (pe->pe_cfgread != NULL) {
854239085Sneel			needcfg = pe->pe_cfgread(ctx, vcpu, pi,
855239085Sneel						    coff, bytes, eax);
856239085Sneel		} else {
857239085Sneel			needcfg = 1;
858239085Sneel		}
859221828Sgrehan
860239085Sneel		if (needcfg) {
861239085Sneel			if (bytes == 1)
862239085Sneel				*eax = pci_get_cfgdata8(pi, coff);
863239085Sneel			else if (bytes == 2)
864239085Sneel				*eax = pci_get_cfgdata16(pi, coff);
865239085Sneel			else
866239085Sneel				*eax = pci_get_cfgdata32(pi, coff);
867239085Sneel		}
868239085Sneel
869239085Sneel		pci_emul_hdrtype_fixup(cfgslot, coff, bytes, eax);
870221828Sgrehan	} else {
871221828Sgrehan		/* Let the device emulation override the default handler */
872221828Sgrehan		if (pe->pe_cfgwrite != NULL &&
873221828Sgrehan		    (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0)
874221828Sgrehan			return (0);
875221828Sgrehan
876221828Sgrehan		/*
877221828Sgrehan		 * Special handling for write to BAR registers
878221828Sgrehan		 */
879221828Sgrehan		if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) {
880221828Sgrehan			/*
881221828Sgrehan			 * Ignore writes to BAR registers that are not
882221828Sgrehan			 * 4-byte aligned.
883221828Sgrehan			 */
884221828Sgrehan			if (bytes != 4 || (coff & 0x3) != 0)
885221828Sgrehan				return (0);
886221828Sgrehan			idx = (coff - PCIR_BAR(0)) / 4;
887221828Sgrehan			switch (pi->pi_bar[idx].type) {
888221828Sgrehan			case PCIBAR_NONE:
889221828Sgrehan				bar = 0;
890221828Sgrehan				break;
891221828Sgrehan			case PCIBAR_IO:
892221828Sgrehan				mask = ~(pi->pi_bar[idx].size - 1);
893221828Sgrehan				mask &= PCIM_BAR_IO_BASE;
894221828Sgrehan				bar = (*eax & mask) | PCIM_BAR_IO_SPACE;
895221828Sgrehan				break;
896221828Sgrehan			case PCIBAR_MEM32:
897221828Sgrehan				mask = ~(pi->pi_bar[idx].size - 1);
898221828Sgrehan				mask &= PCIM_BAR_MEM_BASE;
899221828Sgrehan				bar = *eax & mask;
900221828Sgrehan				bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
901221828Sgrehan				break;
902221828Sgrehan			case PCIBAR_MEM64:
903221828Sgrehan				mask = ~(pi->pi_bar[idx].size - 1);
904221828Sgrehan				mask &= PCIM_BAR_MEM_BASE;
905221828Sgrehan				bar = *eax & mask;
906221828Sgrehan				bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
907221828Sgrehan				       PCIM_BAR_MEM_PREFETCH;
908221828Sgrehan				break;
909221828Sgrehan			case PCIBAR_MEMHI64:
910221828Sgrehan				mask = ~(pi->pi_bar[idx - 1].size - 1);
911221828Sgrehan				mask &= PCIM_BAR_MEM_BASE;
912221828Sgrehan				bar = ((uint64_t)*eax << 32) & mask;
913221828Sgrehan				bar = bar >> 32;
914221828Sgrehan				break;
915221828Sgrehan			default:
916221828Sgrehan				assert(0);
917221828Sgrehan			}
918221828Sgrehan			pci_set_cfgdata32(pi, coff, bar);
919234761Sgrehan
920221828Sgrehan		} else if (pci_emul_iscap(pi, coff)) {
921221828Sgrehan			pci_emul_capwrite(pi, coff, bytes, *eax);
922221828Sgrehan		} else {
923221828Sgrehan			CFGWRITE(pi, coff, *eax, bytes);
924221828Sgrehan		}
925221828Sgrehan	}
926221828Sgrehan
927221828Sgrehan	return (0);
928221828Sgrehan}
929221828Sgrehan
930221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata);
931221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata);
932221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
933221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
934221828Sgrehan
935221828Sgrehan/*
936221828Sgrehan * I/O ports to configure PCI IRQ routing. We ignore all writes to it.
937221828Sgrehan */
938221828Sgrehanstatic int
939221828Sgrehanpci_irq_port_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
940221828Sgrehan		     uint32_t *eax, void *arg)
941221828Sgrehan{
942221828Sgrehan	assert(in == 0);
943221828Sgrehan	return (0);
944221828Sgrehan}
945221828SgrehanINOUT_PORT(pci_irq, 0xC00, IOPORT_F_OUT, pci_irq_port_handler);
946221828SgrehanINOUT_PORT(pci_irq, 0xC01, IOPORT_F_OUT, pci_irq_port_handler);
947221828Sgrehan
948221828Sgrehan#define PCI_EMUL_TEST
949221828Sgrehan#ifdef PCI_EMUL_TEST
950221828Sgrehan/*
951221828Sgrehan * Define a dummy test device
952221828Sgrehan */
953241744Sgrehan#define DIOSZ	20
954241744Sgrehan#define DMEMSZ	4096
955221828Sgrehanstruct pci_emul_dsoftc {
956241744Sgrehan	uint8_t   ioregs[DIOSZ];
957241744Sgrehan	uint8_t	  memregs[DMEMSZ];
958221828Sgrehan};
959221828Sgrehan
960241744Sgrehan#define	PCI_EMUL_MSI_MSGS	 4
961241744Sgrehan#define	PCI_EMUL_MSIX_MSGS	16
962221828Sgrehan
963221942Sjhbstatic int
964221828Sgrehanpci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
965221828Sgrehan{
966221828Sgrehan	int error;
967221828Sgrehan	struct pci_emul_dsoftc *sc;
968221828Sgrehan
969221828Sgrehan	sc = malloc(sizeof(struct pci_emul_dsoftc));
970221828Sgrehan	memset(sc, 0, sizeof(struct pci_emul_dsoftc));
971221828Sgrehan
972221828Sgrehan	pi->pi_arg = sc;
973221828Sgrehan
974221828Sgrehan	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001);
975221828Sgrehan	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD);
976221828Sgrehan	pci_set_cfgdata8(pi, PCIR_CLASS, 0x02);
977221828Sgrehan
978241744Sgrehan	error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS);
979221828Sgrehan	assert(error == 0);
980221828Sgrehan
981241744Sgrehan	error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ);
982221828Sgrehan	assert(error == 0);
983221828Sgrehan
984241744Sgrehan	error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ);
985241744Sgrehan	assert(error == 0);
986241744Sgrehan
987221828Sgrehan	return (0);
988221828Sgrehan}
989221828Sgrehan
990221942Sjhbstatic void
991241744Sgrehanpci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
992241744Sgrehan	      uint64_t offset, int size, uint64_t value)
993221828Sgrehan{
994221828Sgrehan	int i;
995221828Sgrehan	struct pci_emul_dsoftc *sc = pi->pi_arg;
996221828Sgrehan
997241744Sgrehan	if (baridx == 0) {
998241744Sgrehan		if (offset + size > DIOSZ) {
999241744Sgrehan			printf("diow: iow too large, offset %ld size %d\n",
1000241744Sgrehan			       offset, size);
1001241744Sgrehan			return;
1002241744Sgrehan		}
1003221828Sgrehan
1004241744Sgrehan		if (size == 1) {
1005241744Sgrehan			sc->ioregs[offset] = value & 0xff;
1006241744Sgrehan		} else if (size == 2) {
1007241744Sgrehan			*(uint16_t *)&sc->ioregs[offset] = value & 0xffff;
1008241744Sgrehan		} else if (size == 4) {
1009241744Sgrehan			*(uint32_t *)&sc->ioregs[offset] = value;
1010241744Sgrehan		} else {
1011241744Sgrehan			printf("diow: iow unknown size %d\n", size);
1012241744Sgrehan		}
1013241744Sgrehan
1014241744Sgrehan		/*
1015241744Sgrehan		 * Special magic value to generate an interrupt
1016241744Sgrehan		 */
1017241744Sgrehan		if (offset == 4 && size == 4 && pci_msi_enabled(pi))
1018241744Sgrehan			pci_generate_msi(pi, value % pci_msi_msgnum(pi));
1019241744Sgrehan
1020241744Sgrehan		if (value == 0xabcdef) {
1021241744Sgrehan			for (i = 0; i < pci_msi_msgnum(pi); i++)
1022241744Sgrehan				pci_generate_msi(pi, i);
1023241744Sgrehan		}
1024221828Sgrehan	}
1025221828Sgrehan
1026241744Sgrehan	if (baridx == 1) {
1027241744Sgrehan		if (offset + size > DMEMSZ) {
1028241744Sgrehan			printf("diow: memw too large, offset %ld size %d\n",
1029241744Sgrehan			       offset, size);
1030241744Sgrehan			return;
1031241744Sgrehan		}
1032221828Sgrehan
1033241744Sgrehan		if (size == 1) {
1034241744Sgrehan			sc->memregs[offset] = value;
1035241744Sgrehan		} else if (size == 2) {
1036241744Sgrehan			*(uint16_t *)&sc->memregs[offset] = value;
1037241744Sgrehan		} else if (size == 4) {
1038241744Sgrehan			*(uint32_t *)&sc->memregs[offset] = value;
1039241744Sgrehan		} else if (size == 8) {
1040241744Sgrehan			*(uint64_t *)&sc->memregs[offset] = value;
1041241744Sgrehan		} else {
1042241744Sgrehan			printf("diow: memw unknown size %d\n", size);
1043241744Sgrehan		}
1044241744Sgrehan
1045241744Sgrehan		/*
1046241744Sgrehan		 * magic interrupt ??
1047241744Sgrehan		 */
1048221828Sgrehan	}
1049241744Sgrehan
1050241744Sgrehan	if (baridx > 1) {
1051241744Sgrehan		printf("diow: unknown bar idx %d\n", baridx);
1052241744Sgrehan	}
1053221828Sgrehan}
1054221828Sgrehan
1055241744Sgrehanstatic uint64_t
1056241744Sgrehanpci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
1057241744Sgrehan	      uint64_t offset, int size)
1058221828Sgrehan{
1059221828Sgrehan	struct pci_emul_dsoftc *sc = pi->pi_arg;
1060221828Sgrehan	uint32_t value;
1061221828Sgrehan
1062241744Sgrehan	if (baridx == 0) {
1063241744Sgrehan		if (offset + size > DIOSZ) {
1064241744Sgrehan			printf("dior: ior too large, offset %ld size %d\n",
1065241744Sgrehan			       offset, size);
1066241744Sgrehan			return (0);
1067241744Sgrehan		}
1068241744Sgrehan
1069241744Sgrehan		if (size == 1) {
1070241744Sgrehan			value = sc->ioregs[offset];
1071241744Sgrehan		} else if (size == 2) {
1072241744Sgrehan			value = *(uint16_t *) &sc->ioregs[offset];
1073241744Sgrehan		} else if (size == 4) {
1074241744Sgrehan			value = *(uint32_t *) &sc->ioregs[offset];
1075241744Sgrehan		} else {
1076241744Sgrehan			printf("dior: ior unknown size %d\n", size);
1077241744Sgrehan		}
1078221828Sgrehan	}
1079221828Sgrehan
1080241744Sgrehan	if (baridx == 1) {
1081241744Sgrehan		if (offset + size > DMEMSZ) {
1082241744Sgrehan			printf("dior: memr too large, offset %ld size %d\n",
1083241744Sgrehan			       offset, size);
1084241744Sgrehan			return (0);
1085241744Sgrehan		}
1086241744Sgrehan
1087241744Sgrehan		if (size == 1) {
1088241744Sgrehan			value = sc->memregs[offset];
1089241744Sgrehan		} else if (size == 2) {
1090241744Sgrehan			value = *(uint16_t *) &sc->memregs[offset];
1091241744Sgrehan		} else if (size == 4) {
1092241744Sgrehan			value = *(uint32_t *) &sc->memregs[offset];
1093241744Sgrehan		} else if (size == 8) {
1094241744Sgrehan			value = *(uint64_t *) &sc->memregs[offset];
1095241744Sgrehan		} else {
1096241744Sgrehan			printf("dior: ior unknown size %d\n", size);
1097241744Sgrehan		}
1098221828Sgrehan	}
1099221828Sgrehan
1100241744Sgrehan
1101241744Sgrehan	if (baridx > 1) {
1102241744Sgrehan		printf("dior: unknown bar idx %d\n", baridx);
1103241744Sgrehan		return (0);
1104241744Sgrehan	}
1105241744Sgrehan
1106221828Sgrehan	return (value);
1107221828Sgrehan}
1108221828Sgrehan
1109221828Sgrehanstruct pci_devemu pci_dummy = {
1110221828Sgrehan	.pe_emu = "dummy",
1111221828Sgrehan	.pe_init = pci_emul_dinit,
1112241744Sgrehan	.pe_barwrite = pci_emul_diow,
1113241744Sgrehan	.pe_barread = pci_emul_dior
1114221828Sgrehan};
1115221828SgrehanPCI_EMUL_SET(pci_dummy);
1116221828Sgrehan
1117221828Sgrehan#endif /* PCI_EMUL_TEST */
1118