pci_emul.c revision 252331
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 252331 2013-06-28 05:01:25Z grehan $
27221828Sgrehan */
28221828Sgrehan
29221828Sgrehan#include <sys/cdefs.h>
30221828Sgrehan__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_emul.c 252331 2013-06-28 05:01:25Z grehan $");
31221828Sgrehan
32221828Sgrehan#include <sys/param.h>
33221828Sgrehan#include <sys/linker_set.h>
34249321Sneel#include <sys/errno.h>
35221828Sgrehan
36221828Sgrehan#include <ctype.h>
37221828Sgrehan#include <stdio.h>
38221828Sgrehan#include <stdlib.h>
39221828Sgrehan#include <string.h>
40221828Sgrehan#include <strings.h>
41221828Sgrehan#include <assert.h>
42249321Sneel#include <stdbool.h>
43221828Sgrehan
44221828Sgrehan#include <machine/vmm.h>
45221828Sgrehan#include <vmmapi.h>
46221828Sgrehan
47244167Sgrehan#include "bhyverun.h"
48221828Sgrehan#include "inout.h"
49241744Sgrehan#include "mem.h"
50221828Sgrehan#include "pci_emul.h"
51239045Sneel#include "ioapic.h"
52221828Sgrehan
53221828Sgrehan#define CONF1_ADDR_PORT    0x0cf8
54221828Sgrehan#define CONF1_DATA_PORT    0x0cfc
55221828Sgrehan
56252331Sgrehan#define CONF1_ENABLE	   0x80000000ul
57252331Sgrehan
58221828Sgrehan#define	CFGWRITE(pi,off,val,b)						\
59221828Sgrehando {									\
60221828Sgrehan	if ((b) == 1) {							\
61221828Sgrehan		pci_set_cfgdata8((pi),(off),(val));			\
62221828Sgrehan	} else if ((b) == 2) {						\
63221828Sgrehan		pci_set_cfgdata16((pi),(off),(val));			\
64221828Sgrehan	} else {							\
65221828Sgrehan		pci_set_cfgdata32((pi),(off),(val));			\
66221828Sgrehan	}								\
67221828Sgrehan} while (0)
68221828Sgrehan
69239085Sneel#define MAXSLOTS	(PCI_SLOTMAX + 1)
70239085Sneel#define	MAXFUNCS	(PCI_FUNCMAX + 1)
71221828Sgrehan
72221828Sgrehanstatic struct slotinfo {
73234938Sgrehan	char	*si_name;
74234938Sgrehan	char	*si_param;
75221828Sgrehan	struct pci_devinst *si_devi;
76234938Sgrehan	int	si_legacy;
77239085Sneel} pci_slotinfo[MAXSLOTS][MAXFUNCS];
78221828Sgrehan
79221828Sgrehan/*
80234938Sgrehan * Used to keep track of legacy interrupt owners/requestors
81234938Sgrehan */
82234938Sgrehan#define NLIRQ		16
83234938Sgrehan
84234938Sgrehanstatic struct lirqinfo {
85234938Sgrehan	int	li_generic;
86234938Sgrehan	int	li_acount;
87234938Sgrehan	struct pci_devinst *li_owner;	/* XXX should be a list */
88234938Sgrehan} lirq[NLIRQ];
89234938Sgrehan
90221828SgrehanSET_DECLARE(pci_devemu_set, struct pci_devemu);
91221828Sgrehan
92221828Sgrehanstatic uint64_t pci_emul_iobase;
93221828Sgrehanstatic uint64_t pci_emul_membase32;
94221828Sgrehanstatic uint64_t pci_emul_membase64;
95221828Sgrehan
96221828Sgrehan#define	PCI_EMUL_IOBASE		0x2000
97221828Sgrehan#define	PCI_EMUL_IOLIMIT	0x10000
98221828Sgrehan
99221828Sgrehan#define	PCI_EMUL_MEMLIMIT32	0xE0000000		/* 3.5GB */
100221828Sgrehan
101221828Sgrehan#define	PCI_EMUL_MEMBASE64	0xD000000000UL
102221828Sgrehan#define	PCI_EMUL_MEMLIMIT64	0xFD00000000UL
103221828Sgrehan
104249916Sneelstatic struct pci_devemu *pci_emul_finddev(char *name);
105249916Sneel
106221828Sgrehanstatic int pci_emul_devices;
107221828Sgrehan
108221828Sgrehan/*
109221828Sgrehan * I/O access
110221828Sgrehan */
111221828Sgrehan
112221828Sgrehan/*
113221828Sgrehan * Slot options are in the form:
114221828Sgrehan *
115239085Sneel *  <slot>[:<func>],<emul>[,<config>]
116221828Sgrehan *
117221828Sgrehan *  slot is 0..31
118239085Sneel *  func is 0..7
119221828Sgrehan *  emul is a string describing the type of PCI device e.g. virtio-net
120221828Sgrehan *  config is an optional string, depending on the device, that can be
121221828Sgrehan *  used for configuration.
122221828Sgrehan *   Examples are:
123221828Sgrehan *     1,virtio-net,tap0
124239085Sneel *     3:0,dummy
125221828Sgrehan */
126221828Sgrehanstatic void
127221828Sgrehanpci_parse_slot_usage(char *aopt)
128221828Sgrehan{
129249916Sneel
130249916Sneel	fprintf(stderr, "Invalid PCI slot info field \"%s\"\n", aopt);
131221828Sgrehan}
132221828Sgrehan
133249916Sneelint
134234938Sgrehanpci_parse_slot(char *opt, int legacy)
135221828Sgrehan{
136239085Sneel	char *slot, *func, *emul, *config;
137221828Sgrehan	char *str, *cpy;
138249916Sneel	int error, snum, fnum;
139221828Sgrehan
140249916Sneel	error = -1;
141221828Sgrehan	str = cpy = strdup(opt);
142239085Sneel
143221828Sgrehan	config = NULL;
144221828Sgrehan
145239085Sneel	if (strchr(str, ':') != NULL) {
146239085Sneel		slot = strsep(&str, ":");
147239085Sneel		func = strsep(&str, ",");
148239085Sneel	} else {
149239085Sneel		slot = strsep(&str, ",");
150239085Sneel		func = NULL;
151239085Sneel	}
152239085Sneel
153221828Sgrehan	emul = strsep(&str, ",");
154221828Sgrehan	if (str != NULL) {
155221828Sgrehan		config = strsep(&str, ",");
156221828Sgrehan	}
157221828Sgrehan
158221828Sgrehan	if (emul == NULL) {
159249916Sneel		pci_parse_slot_usage(opt);
160249916Sneel		goto done;
161221828Sgrehan	}
162221828Sgrehan
163221828Sgrehan	snum = atoi(slot);
164239085Sneel	fnum = func ? atoi(func) : 0;
165249916Sneel
166239085Sneel	if (snum < 0 || snum >= MAXSLOTS || fnum < 0 || fnum >= MAXFUNCS) {
167249916Sneel		pci_parse_slot_usage(opt);
168249916Sneel		goto done;
169221828Sgrehan	}
170249916Sneel
171249916Sneel	if (pci_slotinfo[snum][fnum].si_name != NULL) {
172249916Sneel		fprintf(stderr, "pci slot %d:%d already occupied!\n",
173249916Sneel			snum, fnum);
174249916Sneel		goto done;
175249916Sneel	}
176249916Sneel
177249916Sneel	if (pci_emul_finddev(emul) == NULL) {
178249916Sneel		fprintf(stderr, "pci slot %d:%d: unknown device \"%s\"\n",
179249916Sneel			snum, fnum, emul);
180249916Sneel		goto done;
181249916Sneel	}
182249916Sneel
183249916Sneel	error = 0;
184249916Sneel	pci_slotinfo[snum][fnum].si_name = emul;
185249916Sneel	pci_slotinfo[snum][fnum].si_param = config;
186249916Sneel	pci_slotinfo[snum][fnum].si_legacy = legacy;
187249916Sneel
188249916Sneeldone:
189249916Sneel	if (error)
190249916Sneel		free(cpy);
191249916Sneel
192249916Sneel	return (error);
193221828Sgrehan}
194221828Sgrehan
195221828Sgrehanstatic int
196246109Sneelpci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset)
197246109Sneel{
198246109Sneel
199246109Sneel	if (offset < pi->pi_msix.pba_offset)
200246109Sneel		return (0);
201246109Sneel
202246109Sneel	if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) {
203246109Sneel		return (0);
204246109Sneel	}
205246109Sneel
206246109Sneel	return (1);
207246109Sneel}
208246109Sneel
209246109Sneelint
210246109Sneelpci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size,
211246109Sneel		     uint64_t value)
212246109Sneel{
213246109Sneel	int msix_entry_offset;
214246109Sneel	int tab_index;
215246109Sneel	char *dest;
216246109Sneel
217246109Sneel	/* support only 4 or 8 byte writes */
218246109Sneel	if (size != 4 && size != 8)
219246109Sneel		return (-1);
220246109Sneel
221246109Sneel	/*
222246109Sneel	 * Return if table index is beyond what device supports
223246109Sneel	 */
224246109Sneel	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
225246109Sneel	if (tab_index >= pi->pi_msix.table_count)
226246109Sneel		return (-1);
227246109Sneel
228246109Sneel	msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
229246109Sneel
230246109Sneel	/* support only aligned writes */
231246109Sneel	if ((msix_entry_offset % size) != 0)
232246109Sneel		return (-1);
233246109Sneel
234246109Sneel	dest = (char *)(pi->pi_msix.table + tab_index);
235246109Sneel	dest += msix_entry_offset;
236246109Sneel
237246109Sneel	if (size == 4)
238246109Sneel		*((uint32_t *)dest) = value;
239246109Sneel	else
240246109Sneel		*((uint64_t *)dest) = value;
241246109Sneel
242246109Sneel	return (0);
243246109Sneel}
244246109Sneel
245246109Sneeluint64_t
246246109Sneelpci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size)
247246109Sneel{
248246109Sneel	char *dest;
249246109Sneel	int msix_entry_offset;
250246109Sneel	int tab_index;
251246109Sneel	uint64_t retval = ~0;
252246109Sneel
253246109Sneel	/* support only 4 or 8 byte reads */
254246109Sneel	if (size != 4 && size != 8)
255246109Sneel		return (retval);
256246109Sneel
257246109Sneel	msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
258246109Sneel
259246109Sneel	/* support only aligned reads */
260246109Sneel	if ((msix_entry_offset % size) != 0) {
261246109Sneel		return (retval);
262246109Sneel	}
263246109Sneel
264246109Sneel	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
265246109Sneel
266246109Sneel	if (tab_index < pi->pi_msix.table_count) {
267246109Sneel		/* valid MSI-X Table access */
268246109Sneel		dest = (char *)(pi->pi_msix.table + tab_index);
269246109Sneel		dest += msix_entry_offset;
270246109Sneel
271246109Sneel		if (size == 4)
272246109Sneel			retval = *((uint32_t *)dest);
273246109Sneel		else
274246109Sneel			retval = *((uint64_t *)dest);
275246109Sneel	} else if (pci_valid_pba_offset(pi, offset)) {
276246109Sneel		/* return 0 for PBA access */
277246109Sneel		retval = 0;
278246109Sneel	}
279246109Sneel
280246109Sneel	return (retval);
281246109Sneel}
282246109Sneel
283246190Sneelint
284246190Sneelpci_msix_table_bar(struct pci_devinst *pi)
285246190Sneel{
286246190Sneel
287246190Sneel	if (pi->pi_msix.table != NULL)
288246190Sneel		return (pi->pi_msix.table_bar);
289246190Sneel	else
290246190Sneel		return (-1);
291246190Sneel}
292246190Sneel
293246190Sneelint
294246190Sneelpci_msix_pba_bar(struct pci_devinst *pi)
295246190Sneel{
296246190Sneel
297246190Sneel	if (pi->pi_msix.table != NULL)
298246190Sneel		return (pi->pi_msix.pba_bar);
299246190Sneel	else
300246190Sneel		return (-1);
301246190Sneel}
302246190Sneel
303246109Sneelstatic int
304241744Sgrehanpci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
305241744Sgrehan		    uint32_t *eax, void *arg)
306221828Sgrehan{
307221828Sgrehan	struct pci_devinst *pdi = arg;
308221828Sgrehan	struct pci_devemu *pe = pdi->pi_d;
309241744Sgrehan	uint64_t offset;
310241744Sgrehan	int i;
311221828Sgrehan
312221828Sgrehan	for (i = 0; i <= PCI_BARMAX; i++) {
313221828Sgrehan		if (pdi->pi_bar[i].type == PCIBAR_IO &&
314221828Sgrehan		    port >= pdi->pi_bar[i].addr &&
315246109Sneel		    port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) {
316221828Sgrehan			offset = port - pdi->pi_bar[i].addr;
317221828Sgrehan			if (in)
318241744Sgrehan				*eax = (*pe->pe_barread)(ctx, vcpu, pdi, i,
319241744Sgrehan							 offset, bytes);
320221828Sgrehan			else
321241744Sgrehan				(*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset,
322241744Sgrehan						   bytes, *eax);
323221828Sgrehan			return (0);
324221828Sgrehan		}
325221828Sgrehan	}
326221828Sgrehan	return (-1);
327221828Sgrehan}
328221828Sgrehan
329221828Sgrehanstatic int
330241744Sgrehanpci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
331241744Sgrehan		     int size, uint64_t *val, void *arg1, long arg2)
332241744Sgrehan{
333241744Sgrehan	struct pci_devinst *pdi = arg1;
334241744Sgrehan	struct pci_devemu *pe = pdi->pi_d;
335241744Sgrehan	uint64_t offset;
336241744Sgrehan	int bidx = (int) arg2;
337241744Sgrehan
338241744Sgrehan	assert(bidx <= PCI_BARMAX);
339241744Sgrehan	assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 ||
340241744Sgrehan	       pdi->pi_bar[bidx].type == PCIBAR_MEM64);
341241744Sgrehan	assert(addr >= pdi->pi_bar[bidx].addr &&
342241744Sgrehan	       addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size);
343241744Sgrehan
344241744Sgrehan	offset = addr - pdi->pi_bar[bidx].addr;
345241744Sgrehan
346241744Sgrehan	if (dir == MEM_F_WRITE)
347241744Sgrehan		(*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, size, *val);
348241744Sgrehan	else
349241744Sgrehan		*val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset, size);
350241744Sgrehan
351241744Sgrehan	return (0);
352241744Sgrehan}
353241744Sgrehan
354241744Sgrehan
355241744Sgrehanstatic int
356221828Sgrehanpci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size,
357221828Sgrehan			uint64_t *addr)
358221828Sgrehan{
359221828Sgrehan	uint64_t base;
360221828Sgrehan
361221828Sgrehan	assert((size & (size - 1)) == 0);	/* must be a power of 2 */
362221828Sgrehan
363221828Sgrehan	base = roundup2(*baseptr, size);
364221828Sgrehan
365221828Sgrehan	if (base + size <= limit) {
366221828Sgrehan		*addr = base;
367221828Sgrehan		*baseptr = base + size;
368221828Sgrehan		return (0);
369221828Sgrehan	} else
370221828Sgrehan		return (-1);
371221828Sgrehan}
372221828Sgrehan
373221828Sgrehanint
374241744Sgrehanpci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type,
375241744Sgrehan		   uint64_t size)
376221828Sgrehan{
377241744Sgrehan
378241744Sgrehan	return (pci_emul_alloc_pbar(pdi, idx, 0, type, size));
379241744Sgrehan}
380241744Sgrehan
381249321Sneel/*
382249321Sneel * Register (or unregister) the MMIO or I/O region associated with the BAR
383249321Sneel * register 'idx' of an emulated pci device.
384249321Sneel */
385249321Sneelstatic void
386249321Sneelmodify_bar_registration(struct pci_devinst *pi, int idx, int registration)
387249321Sneel{
388249321Sneel	int error;
389249321Sneel	struct inout_port iop;
390249321Sneel	struct mem_range mr;
391249321Sneel
392249321Sneel	switch (pi->pi_bar[idx].type) {
393249321Sneel	case PCIBAR_IO:
394249321Sneel		bzero(&iop, sizeof(struct inout_port));
395249321Sneel		iop.name = pi->pi_name;
396249321Sneel		iop.port = pi->pi_bar[idx].addr;
397249321Sneel		iop.size = pi->pi_bar[idx].size;
398249321Sneel		if (registration) {
399249321Sneel			iop.flags = IOPORT_F_INOUT;
400249321Sneel			iop.handler = pci_emul_io_handler;
401249321Sneel			iop.arg = pi;
402249321Sneel			error = register_inout(&iop);
403249321Sneel		} else
404249321Sneel			error = unregister_inout(&iop);
405249321Sneel		break;
406249321Sneel	case PCIBAR_MEM32:
407249321Sneel	case PCIBAR_MEM64:
408249321Sneel		bzero(&mr, sizeof(struct mem_range));
409249321Sneel		mr.name = pi->pi_name;
410249321Sneel		mr.base = pi->pi_bar[idx].addr;
411249321Sneel		mr.size = pi->pi_bar[idx].size;
412249321Sneel		if (registration) {
413249321Sneel			mr.flags = MEM_F_RW;
414249321Sneel			mr.handler = pci_emul_mem_handler;
415249321Sneel			mr.arg1 = pi;
416249321Sneel			mr.arg2 = idx;
417249321Sneel			error = register_mem(&mr);
418249321Sneel		} else
419249321Sneel			error = unregister_mem(&mr);
420249321Sneel		break;
421249321Sneel	default:
422249321Sneel		error = EINVAL;
423249321Sneel		break;
424249321Sneel	}
425249321Sneel	assert(error == 0);
426249321Sneel}
427249321Sneel
428249321Sneelstatic void
429249321Sneelunregister_bar(struct pci_devinst *pi, int idx)
430249321Sneel{
431249321Sneel
432249321Sneel	modify_bar_registration(pi, idx, 0);
433249321Sneel}
434249321Sneel
435249321Sneelstatic void
436249321Sneelregister_bar(struct pci_devinst *pi, int idx)
437249321Sneel{
438249321Sneel
439249321Sneel	modify_bar_registration(pi, idx, 1);
440249321Sneel}
441249321Sneel
442249321Sneel/* Are we decoding i/o port accesses for the emulated pci device? */
443249321Sneelstatic int
444249321Sneelporten(struct pci_devinst *pi)
445249321Sneel{
446249321Sneel	uint16_t cmd;
447249321Sneel
448249321Sneel	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
449249321Sneel
450249321Sneel	return (cmd & PCIM_CMD_PORTEN);
451249321Sneel}
452249321Sneel
453249321Sneel/* Are we decoding memory accesses for the emulated pci device? */
454249321Sneelstatic int
455249321Sneelmemen(struct pci_devinst *pi)
456249321Sneel{
457249321Sneel	uint16_t cmd;
458249321Sneel
459249321Sneel	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
460249321Sneel
461249321Sneel	return (cmd & PCIM_CMD_MEMEN);
462249321Sneel}
463249321Sneel
464249321Sneel/*
465249321Sneel * Update the MMIO or I/O address that is decoded by the BAR register.
466249321Sneel *
467249321Sneel * If the pci device has enabled the address space decoding then intercept
468249321Sneel * the address range decoded by the BAR register.
469249321Sneel */
470249321Sneelstatic void
471249321Sneelupdate_bar_address(struct  pci_devinst *pi, uint64_t addr, int idx, int type)
472249321Sneel{
473249321Sneel	int decode;
474249321Sneel
475249321Sneel	if (pi->pi_bar[idx].type == PCIBAR_IO)
476249321Sneel		decode = porten(pi);
477249321Sneel	else
478249321Sneel		decode = memen(pi);
479249321Sneel
480249321Sneel	if (decode)
481249321Sneel		unregister_bar(pi, idx);
482249321Sneel
483249321Sneel	switch (type) {
484249321Sneel	case PCIBAR_IO:
485249321Sneel	case PCIBAR_MEM32:
486249321Sneel		pi->pi_bar[idx].addr = addr;
487249321Sneel		break;
488249321Sneel	case PCIBAR_MEM64:
489249321Sneel		pi->pi_bar[idx].addr &= ~0xffffffffUL;
490249321Sneel		pi->pi_bar[idx].addr |= addr;
491249321Sneel		break;
492249321Sneel	case PCIBAR_MEMHI64:
493249321Sneel		pi->pi_bar[idx].addr &= 0xffffffff;
494249321Sneel		pi->pi_bar[idx].addr |= addr;
495249321Sneel		break;
496249321Sneel	default:
497249321Sneel		assert(0);
498249321Sneel	}
499249321Sneel
500249321Sneel	if (decode)
501249321Sneel		register_bar(pi, idx);
502249321Sneel}
503249321Sneel
504241744Sgrehanint
505241744Sgrehanpci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase,
506241744Sgrehan		    enum pcibar_type type, uint64_t size)
507241744Sgrehan{
508249321Sneel	int error;
509221828Sgrehan	uint64_t *baseptr, limit, addr, mask, lobits, bar;
510221828Sgrehan
511221828Sgrehan	assert(idx >= 0 && idx <= PCI_BARMAX);
512221828Sgrehan
513221828Sgrehan	if ((size & (size - 1)) != 0)
514221828Sgrehan		size = 1UL << flsl(size);	/* round up to a power of 2 */
515221828Sgrehan
516249321Sneel	/* Enforce minimum BAR sizes required by the PCI standard */
517249321Sneel	if (type == PCIBAR_IO) {
518249321Sneel		if (size < 4)
519249321Sneel			size = 4;
520249321Sneel	} else {
521249321Sneel		if (size < 16)
522249321Sneel			size = 16;
523249321Sneel	}
524249321Sneel
525221828Sgrehan	switch (type) {
526221828Sgrehan	case PCIBAR_NONE:
527221828Sgrehan		baseptr = NULL;
528221828Sgrehan		addr = mask = lobits = 0;
529221828Sgrehan		break;
530221828Sgrehan	case PCIBAR_IO:
531239085Sneel		if (hostbase &&
532239085Sneel		    pci_slotinfo[pdi->pi_slot][pdi->pi_func].si_legacy) {
533234938Sgrehan			assert(hostbase < PCI_EMUL_IOBASE);
534234938Sgrehan			baseptr = &hostbase;
535234938Sgrehan		} else {
536234938Sgrehan			baseptr = &pci_emul_iobase;
537234938Sgrehan		}
538221828Sgrehan		limit = PCI_EMUL_IOLIMIT;
539221828Sgrehan		mask = PCIM_BAR_IO_BASE;
540221828Sgrehan		lobits = PCIM_BAR_IO_SPACE;
541221828Sgrehan		break;
542221828Sgrehan	case PCIBAR_MEM64:
543221828Sgrehan		/*
544221828Sgrehan		 * XXX
545221828Sgrehan		 * Some drivers do not work well if the 64-bit BAR is allocated
546221828Sgrehan		 * above 4GB. Allow for this by allocating small requests under
547221828Sgrehan		 * 4GB unless then allocation size is larger than some arbitrary
548221828Sgrehan		 * number (32MB currently).
549221828Sgrehan		 */
550221828Sgrehan		if (size > 32 * 1024 * 1024) {
551221828Sgrehan			/*
552221828Sgrehan			 * XXX special case for device requiring peer-peer DMA
553221828Sgrehan			 */
554221828Sgrehan			if (size == 0x100000000UL)
555221828Sgrehan				baseptr = &hostbase;
556221828Sgrehan			else
557221828Sgrehan				baseptr = &pci_emul_membase64;
558221828Sgrehan			limit = PCI_EMUL_MEMLIMIT64;
559221828Sgrehan			mask = PCIM_BAR_MEM_BASE;
560221828Sgrehan			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
561221828Sgrehan				 PCIM_BAR_MEM_PREFETCH;
562221828Sgrehan			break;
563239086Sneel		} else {
564239086Sneel			baseptr = &pci_emul_membase32;
565239086Sneel			limit = PCI_EMUL_MEMLIMIT32;
566239086Sneel			mask = PCIM_BAR_MEM_BASE;
567239086Sneel			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
568221828Sgrehan		}
569239086Sneel		break;
570221828Sgrehan	case PCIBAR_MEM32:
571221828Sgrehan		baseptr = &pci_emul_membase32;
572221828Sgrehan		limit = PCI_EMUL_MEMLIMIT32;
573221828Sgrehan		mask = PCIM_BAR_MEM_BASE;
574221828Sgrehan		lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
575221828Sgrehan		break;
576221828Sgrehan	default:
577221828Sgrehan		printf("pci_emul_alloc_base: invalid bar type %d\n", type);
578221828Sgrehan		assert(0);
579221828Sgrehan	}
580221828Sgrehan
581221828Sgrehan	if (baseptr != NULL) {
582221828Sgrehan		error = pci_emul_alloc_resource(baseptr, limit, size, &addr);
583221828Sgrehan		if (error != 0)
584221828Sgrehan			return (error);
585221828Sgrehan	}
586221828Sgrehan
587221828Sgrehan	pdi->pi_bar[idx].type = type;
588221828Sgrehan	pdi->pi_bar[idx].addr = addr;
589221828Sgrehan	pdi->pi_bar[idx].size = size;
590221828Sgrehan
591221828Sgrehan	/* Initialize the BAR register in config space */
592221828Sgrehan	bar = (addr & mask) | lobits;
593221828Sgrehan	pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar);
594221828Sgrehan
595221828Sgrehan	if (type == PCIBAR_MEM64) {
596221828Sgrehan		assert(idx + 1 <= PCI_BARMAX);
597221828Sgrehan		pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64;
598221828Sgrehan		pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
599221828Sgrehan	}
600221828Sgrehan
601249321Sneel	register_bar(pdi, idx);
602221828Sgrehan
603221828Sgrehan	return (0);
604221828Sgrehan}
605221828Sgrehan
606221828Sgrehan#define	CAP_START_OFFSET	0x40
607221828Sgrehanstatic int
608221828Sgrehanpci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen)
609221828Sgrehan{
610221828Sgrehan	int i, capoff, capid, reallen;
611221828Sgrehan	uint16_t sts;
612221828Sgrehan
613221828Sgrehan	static u_char endofcap[4] = {
614221828Sgrehan		PCIY_RESERVED, 0, 0, 0
615221828Sgrehan	};
616221828Sgrehan
617221828Sgrehan	assert(caplen > 0 && capdata[0] != PCIY_RESERVED);
618221828Sgrehan
619221828Sgrehan	reallen = roundup2(caplen, 4);		/* dword aligned */
620221828Sgrehan
621221828Sgrehan	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
622221828Sgrehan	if ((sts & PCIM_STATUS_CAPPRESENT) == 0) {
623221828Sgrehan		capoff = CAP_START_OFFSET;
624221828Sgrehan		pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff);
625221828Sgrehan		pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT);
626221828Sgrehan	} else {
627221828Sgrehan		capoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR);
628221828Sgrehan		while (1) {
629221828Sgrehan			assert((capoff & 0x3) == 0);
630221828Sgrehan			capid = pci_get_cfgdata8(pi, capoff);
631221828Sgrehan			if (capid == PCIY_RESERVED)
632221828Sgrehan				break;
633221828Sgrehan			capoff = pci_get_cfgdata8(pi, capoff + 1);
634221828Sgrehan		}
635221828Sgrehan	}
636221828Sgrehan
637221828Sgrehan	/* Check if we have enough space */
638221828Sgrehan	if (capoff + reallen + sizeof(endofcap) > PCI_REGMAX + 1)
639221828Sgrehan		return (-1);
640221828Sgrehan
641221828Sgrehan	/* Copy the capability */
642221828Sgrehan	for (i = 0; i < caplen; i++)
643221828Sgrehan		pci_set_cfgdata8(pi, capoff + i, capdata[i]);
644221828Sgrehan
645221828Sgrehan	/* Set the next capability pointer */
646221828Sgrehan	pci_set_cfgdata8(pi, capoff + 1, capoff + reallen);
647221828Sgrehan
648221828Sgrehan	/* Copy of the reserved capability which serves as the end marker */
649221828Sgrehan	for (i = 0; i < sizeof(endofcap); i++)
650221828Sgrehan		pci_set_cfgdata8(pi, capoff + reallen + i, endofcap[i]);
651221828Sgrehan
652221828Sgrehan	return (0);
653221828Sgrehan}
654221828Sgrehan
655221828Sgrehanstatic struct pci_devemu *
656221828Sgrehanpci_emul_finddev(char *name)
657221828Sgrehan{
658221828Sgrehan	struct pci_devemu **pdpp, *pdp;
659221828Sgrehan
660221828Sgrehan	SET_FOREACH(pdpp, pci_devemu_set) {
661221828Sgrehan		pdp = *pdpp;
662221828Sgrehan		if (!strcmp(pdp->pe_emu, name)) {
663221828Sgrehan			return (pdp);
664221828Sgrehan		}
665221828Sgrehan	}
666221828Sgrehan
667221828Sgrehan	return (NULL);
668221828Sgrehan}
669221828Sgrehan
670221828Sgrehanstatic void
671239085Sneelpci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
672239085Sneel	      char *params)
673221828Sgrehan{
674221828Sgrehan	struct pci_devinst *pdi;
675221828Sgrehan	pdi = malloc(sizeof(struct pci_devinst));
676221828Sgrehan	bzero(pdi, sizeof(*pdi));
677221828Sgrehan
678221828Sgrehan	pdi->pi_vmctx = ctx;
679221828Sgrehan	pdi->pi_bus = 0;
680221828Sgrehan	pdi->pi_slot = slot;
681239085Sneel	pdi->pi_func = func;
682221828Sgrehan	pdi->pi_d = pde;
683221828Sgrehan	snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
684221828Sgrehan
685221828Sgrehan	/* Disable legacy interrupts */
686221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_INTLINE, 255);
687221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_INTPIN, 0);
688221828Sgrehan
689221828Sgrehan	pci_set_cfgdata8(pdi, PCIR_COMMAND,
690221828Sgrehan		    PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
691221828Sgrehan
692221828Sgrehan	if ((*pde->pe_init)(ctx, pdi, params) != 0) {
693221828Sgrehan		free(pdi);
694221828Sgrehan	} else {
695221828Sgrehan		pci_emul_devices++;
696239085Sneel		pci_slotinfo[slot][func].si_devi = pdi;
697221828Sgrehan	}
698221828Sgrehan}
699221828Sgrehan
700221828Sgrehanvoid
701221828Sgrehanpci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr)
702221828Sgrehan{
703221828Sgrehan	int mmc;
704221828Sgrehan
705221828Sgrehan	CTASSERT(sizeof(struct msicap) == 14);
706221828Sgrehan
707221828Sgrehan	/* Number of msi messages must be a power of 2 between 1 and 32 */
708221828Sgrehan	assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32);
709221828Sgrehan	mmc = ffs(msgnum) - 1;
710221828Sgrehan
711221828Sgrehan	bzero(msicap, sizeof(struct msicap));
712221828Sgrehan	msicap->capid = PCIY_MSI;
713221828Sgrehan	msicap->nextptr = nextptr;
714221828Sgrehan	msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1);
715221828Sgrehan}
716221828Sgrehan
717221828Sgrehanint
718221828Sgrehanpci_emul_add_msicap(struct pci_devinst *pi, int msgnum)
719221828Sgrehan{
720221828Sgrehan	struct msicap msicap;
721221828Sgrehan
722221828Sgrehan	pci_populate_msicap(&msicap, msgnum, 0);
723221828Sgrehan
724221828Sgrehan	return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap)));
725221828Sgrehan}
726221828Sgrehan
727246109Sneelstatic void
728246109Sneelpci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum,
729246109Sneel		     uint32_t msix_tab_size, int nextptr)
730246109Sneel{
731246109Sneel	CTASSERT(sizeof(struct msixcap) == 12);
732246109Sneel
733246109Sneel	assert(msix_tab_size % 4096 == 0);
734246109Sneel
735246109Sneel	bzero(msixcap, sizeof(struct msixcap));
736246109Sneel	msixcap->capid = PCIY_MSIX;
737246109Sneel	msixcap->nextptr = nextptr;
738246109Sneel
739246109Sneel	/*
740246109Sneel	 * Message Control Register, all fields set to
741246109Sneel	 * zero except for the Table Size.
742246109Sneel	 * Note: Table size N is encoded as N-1
743246109Sneel	 */
744246109Sneel	msixcap->msgctrl = msgnum - 1;
745246109Sneel
746246109Sneel	/*
747246109Sneel	 * MSI-X BAR setup:
748246109Sneel	 * - MSI-X table start at offset 0
749246109Sneel	 * - PBA table starts at a 4K aligned offset after the MSI-X table
750246109Sneel	 */
751246109Sneel	msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK;
752246109Sneel	msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK);
753246109Sneel}
754246109Sneel
755246109Sneelstatic void
756246109Sneelpci_msix_table_init(struct pci_devinst *pi, int table_entries)
757246109Sneel{
758246109Sneel	int i, table_size;
759246109Sneel
760246109Sneel	assert(table_entries > 0);
761246109Sneel	assert(table_entries <= MAX_MSIX_TABLE_ENTRIES);
762246109Sneel
763246109Sneel	table_size = table_entries * MSIX_TABLE_ENTRY_SIZE;
764246109Sneel	pi->pi_msix.table = malloc(table_size);
765246109Sneel	bzero(pi->pi_msix.table, table_size);
766246109Sneel
767246109Sneel	/* set mask bit of vector control register */
768246109Sneel	for (i = 0; i < table_entries; i++)
769246109Sneel		pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK;
770246109Sneel}
771246109Sneel
772246109Sneelint
773246109Sneelpci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum)
774246109Sneel{
775246109Sneel	uint16_t pba_index;
776246109Sneel	uint32_t tab_size;
777246109Sneel	struct msixcap msixcap;
778246109Sneel
779246109Sneel	assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES);
780246109Sneel	assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0);
781246109Sneel
782246109Sneel	tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE;
783246109Sneel
784246109Sneel	/* Align table size to nearest 4K */
785246109Sneel	tab_size = roundup2(tab_size, 4096);
786246109Sneel
787246109Sneel	pi->pi_msix.table_bar = barnum;
788246109Sneel	pi->pi_msix.pba_bar   = barnum;
789246109Sneel	pi->pi_msix.table_offset = 0;
790246109Sneel	pi->pi_msix.table_count = msgnum;
791246109Sneel	pi->pi_msix.pba_offset = tab_size;
792246109Sneel
793246109Sneel	/* calculate the MMIO size required for MSI-X PBA */
794246109Sneel	pba_index = (msgnum - 1) / (PBA_TABLE_ENTRY_SIZE * 8);
795246109Sneel	pi->pi_msix.pba_size = (pba_index + 1) * PBA_TABLE_ENTRY_SIZE;
796246109Sneel
797246109Sneel	pci_msix_table_init(pi, msgnum);
798246109Sneel
799246109Sneel	pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size, 0);
800246109Sneel
801246109Sneel	/* allocate memory for MSI-X Table and PBA */
802246109Sneel	pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32,
803246109Sneel				tab_size + pi->pi_msix.pba_size);
804246109Sneel
805246109Sneel	return (pci_emul_add_capability(pi, (u_char *)&msixcap,
806246109Sneel					sizeof(msixcap)));
807246109Sneel}
808246109Sneel
809221828Sgrehanvoid
810234761Sgrehanmsixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
811234761Sgrehan		 int bytes, uint32_t val)
812234761Sgrehan{
813234761Sgrehan	uint16_t msgctrl, rwmask;
814234761Sgrehan	int off, table_bar;
815246109Sneel
816234761Sgrehan	off = offset - capoff;
817234761Sgrehan	table_bar = pi->pi_msix.table_bar;
818234761Sgrehan	/* Message Control Register */
819234761Sgrehan	if (off == 2 && bytes == 2) {
820234761Sgrehan		rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK;
821234761Sgrehan		msgctrl = pci_get_cfgdata16(pi, offset);
822234761Sgrehan		msgctrl &= ~rwmask;
823234761Sgrehan		msgctrl |= val & rwmask;
824234761Sgrehan		val = msgctrl;
825234761Sgrehan
826234761Sgrehan		pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE;
827246109Sneel		pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK;
828234761Sgrehan	}
829234761Sgrehan
830234761Sgrehan	CFGWRITE(pi, offset, val, bytes);
831234761Sgrehan}
832234761Sgrehan
833234761Sgrehanvoid
834221828Sgrehanmsicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
835221828Sgrehan		int bytes, uint32_t val)
836221828Sgrehan{
837221828Sgrehan	uint16_t msgctrl, rwmask, msgdata, mme;
838221828Sgrehan	uint32_t addrlo;
839221828Sgrehan
840221828Sgrehan	/*
841221828Sgrehan	 * If guest is writing to the message control register make sure
842221828Sgrehan	 * we do not overwrite read-only fields.
843221828Sgrehan	 */
844221828Sgrehan	if ((offset - capoff) == 2 && bytes == 2) {
845221828Sgrehan		rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE;
846221828Sgrehan		msgctrl = pci_get_cfgdata16(pi, offset);
847221828Sgrehan		msgctrl &= ~rwmask;
848221828Sgrehan		msgctrl |= val & rwmask;
849221828Sgrehan		val = msgctrl;
850221828Sgrehan
851221828Sgrehan		addrlo = pci_get_cfgdata32(pi, capoff + 4);
852221828Sgrehan		if (msgctrl & PCIM_MSICTRL_64BIT)
853221828Sgrehan			msgdata = pci_get_cfgdata16(pi, capoff + 12);
854221828Sgrehan		else
855221828Sgrehan			msgdata = pci_get_cfgdata16(pi, capoff + 8);
856221828Sgrehan
857221828Sgrehan		/*
858221828Sgrehan		 * XXX check delivery mode, destination mode etc
859221828Sgrehan		 */
860221828Sgrehan		mme = msgctrl & PCIM_MSICTRL_MME_MASK;
861221828Sgrehan		pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0;
862221828Sgrehan		if (pi->pi_msi.enabled) {
863221828Sgrehan			pi->pi_msi.cpu = (addrlo >> 12) & 0xff;
864221828Sgrehan			pi->pi_msi.vector = msgdata & 0xff;
865221828Sgrehan			pi->pi_msi.msgnum = 1 << (mme >> 4);
866221828Sgrehan		} else {
867221828Sgrehan			pi->pi_msi.cpu = 0;
868221828Sgrehan			pi->pi_msi.vector = 0;
869221828Sgrehan			pi->pi_msi.msgnum = 0;
870221828Sgrehan		}
871221828Sgrehan	}
872221828Sgrehan
873221828Sgrehan	CFGWRITE(pi, offset, val, bytes);
874221828Sgrehan}
875221828Sgrehan
876246846Sneelvoid
877246846Sneelpciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
878246846Sneel		 int bytes, uint32_t val)
879246846Sneel{
880246846Sneel
881246846Sneel	/* XXX don't write to the readonly parts */
882246846Sneel	CFGWRITE(pi, offset, val, bytes);
883246846Sneel}
884246846Sneel
885246846Sneel#define	PCIECAP_VERSION	0x2
886246846Sneelint
887246846Sneelpci_emul_add_pciecap(struct pci_devinst *pi, int type)
888246846Sneel{
889246846Sneel	int err;
890246846Sneel	struct pciecap pciecap;
891246846Sneel
892246846Sneel	CTASSERT(sizeof(struct pciecap) == 60);
893246846Sneel
894246846Sneel	if (type != PCIEM_TYPE_ROOT_PORT)
895246846Sneel		return (-1);
896246846Sneel
897246846Sneel	bzero(&pciecap, sizeof(pciecap));
898246846Sneel
899246846Sneel	pciecap.capid = PCIY_EXPRESS;
900246846Sneel	pciecap.pcie_capabilities = PCIECAP_VERSION | PCIEM_TYPE_ROOT_PORT;
901246846Sneel	pciecap.link_capabilities = 0x411;	/* gen1, x1 */
902246846Sneel	pciecap.link_status = 0x11;		/* gen1, x1 */
903246846Sneel
904246846Sneel	err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap));
905246846Sneel	return (err);
906246846Sneel}
907246846Sneel
908221828Sgrehan/*
909221828Sgrehan * This function assumes that 'coff' is in the capabilities region of the
910221828Sgrehan * config space.
911221828Sgrehan */
912221828Sgrehanstatic void
913221828Sgrehanpci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val)
914221828Sgrehan{
915221828Sgrehan	int capid;
916221828Sgrehan	uint8_t capoff, nextoff;
917221828Sgrehan
918221828Sgrehan	/* Do not allow un-aligned writes */
919221828Sgrehan	if ((offset & (bytes - 1)) != 0)
920221828Sgrehan		return;
921221828Sgrehan
922221828Sgrehan	/* Find the capability that we want to update */
923221828Sgrehan	capoff = CAP_START_OFFSET;
924221828Sgrehan	while (1) {
925221828Sgrehan		capid = pci_get_cfgdata8(pi, capoff);
926221828Sgrehan		if (capid == PCIY_RESERVED)
927221828Sgrehan			break;
928221828Sgrehan
929221828Sgrehan		nextoff = pci_get_cfgdata8(pi, capoff + 1);
930221828Sgrehan		if (offset >= capoff && offset < nextoff)
931221828Sgrehan			break;
932221828Sgrehan
933221828Sgrehan		capoff = nextoff;
934221828Sgrehan	}
935221828Sgrehan	assert(offset >= capoff);
936221828Sgrehan
937221828Sgrehan	/*
938221828Sgrehan	 * Capability ID and Next Capability Pointer are readonly
939221828Sgrehan	 */
940221828Sgrehan	if (offset == capoff || offset == capoff + 1)
941221828Sgrehan		return;
942221828Sgrehan
943221828Sgrehan	switch (capid) {
944221828Sgrehan	case PCIY_MSI:
945221828Sgrehan		msicap_cfgwrite(pi, capoff, offset, bytes, val);
946221828Sgrehan		break;
947246109Sneel	case PCIY_MSIX:
948246109Sneel		msixcap_cfgwrite(pi, capoff, offset, bytes, val);
949246109Sneel		break;
950246846Sneel	case PCIY_EXPRESS:
951246846Sneel		pciecap_cfgwrite(pi, capoff, offset, bytes, val);
952246846Sneel		break;
953221828Sgrehan	default:
954221828Sgrehan		break;
955221828Sgrehan	}
956221828Sgrehan}
957221828Sgrehan
958221828Sgrehanstatic int
959221828Sgrehanpci_emul_iscap(struct pci_devinst *pi, int offset)
960221828Sgrehan{
961221828Sgrehan	int found;
962221828Sgrehan	uint16_t sts;
963221828Sgrehan	uint8_t capid, lastoff;
964221828Sgrehan
965221828Sgrehan	found = 0;
966221828Sgrehan	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
967221828Sgrehan	if ((sts & PCIM_STATUS_CAPPRESENT) != 0) {
968221828Sgrehan		lastoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR);
969221828Sgrehan		while (1) {
970221828Sgrehan			assert((lastoff & 0x3) == 0);
971221828Sgrehan			capid = pci_get_cfgdata8(pi, lastoff);
972221828Sgrehan			if (capid == PCIY_RESERVED)
973221828Sgrehan				break;
974221828Sgrehan			lastoff = pci_get_cfgdata8(pi, lastoff + 1);
975221828Sgrehan		}
976221828Sgrehan		if (offset >= CAP_START_OFFSET && offset <= lastoff)
977221828Sgrehan			found = 1;
978221828Sgrehan	}
979221828Sgrehan	return (found);
980221828Sgrehan}
981221828Sgrehan
982247144Sgrehanstatic int
983247144Sgrehanpci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
984247144Sgrehan			  int size, uint64_t *val, void *arg1, long arg2)
985247144Sgrehan{
986247144Sgrehan	/*
987247144Sgrehan	 * Ignore writes; return 0xff's for reads. The mem read code
988247144Sgrehan	 * will take care of truncating to the correct size.
989247144Sgrehan	 */
990247144Sgrehan	if (dir == MEM_F_READ) {
991247144Sgrehan		*val = 0xffffffffffffffff;
992247144Sgrehan	}
993247144Sgrehan
994247144Sgrehan	return (0);
995247144Sgrehan}
996247144Sgrehan
997221828Sgrehanvoid
998221828Sgrehaninit_pci(struct vmctx *ctx)
999221828Sgrehan{
1000247144Sgrehan	struct mem_range memp;
1001221828Sgrehan	struct pci_devemu *pde;
1002221828Sgrehan	struct slotinfo *si;
1003249572Sneel	size_t lowmem;
1004239085Sneel	int slot, func;
1005247144Sgrehan	int error;
1006221828Sgrehan
1007221828Sgrehan	pci_emul_iobase = PCI_EMUL_IOBASE;
1008249572Sneel	pci_emul_membase32 = vm_get_lowmem_limit(ctx);
1009221828Sgrehan	pci_emul_membase64 = PCI_EMUL_MEMBASE64;
1010221828Sgrehan
1011239085Sneel	for (slot = 0; slot < MAXSLOTS; slot++) {
1012239085Sneel		for (func = 0; func < MAXFUNCS; func++) {
1013239085Sneel			si = &pci_slotinfo[slot][func];
1014239085Sneel			if (si->si_name != NULL) {
1015239085Sneel				pde = pci_emul_finddev(si->si_name);
1016249916Sneel				assert(pde != NULL);
1017249916Sneel				pci_emul_init(ctx, pde, slot, func,
1018249916Sneel					      si->si_param);
1019221828Sgrehan			}
1020221828Sgrehan		}
1021221828Sgrehan	}
1022234938Sgrehan
1023234938Sgrehan	/*
1024234938Sgrehan	 * Allow ISA IRQs 5,10,11,12, and 15 to be available for
1025234938Sgrehan	 * generic use
1026234938Sgrehan	 */
1027234938Sgrehan	lirq[5].li_generic = 1;
1028234938Sgrehan	lirq[10].li_generic = 1;
1029234938Sgrehan	lirq[11].li_generic = 1;
1030234938Sgrehan	lirq[12].li_generic = 1;
1031234938Sgrehan	lirq[15].li_generic = 1;
1032247144Sgrehan
1033247144Sgrehan	/*
1034249572Sneel	 * The guest physical memory map looks like the following:
1035249572Sneel	 * [0,		    lowmem)		guest system memory
1036249572Sneel	 * [lowmem,	    lowmem_limit)	memory hole (may be absent)
1037249572Sneel	 * [lowmem_limit,   4GB)		PCI hole (32-bit BAR allocation)
1038249572Sneel	 * [4GB,	    4GB + highmem)
1039249572Sneel	 *
1040249572Sneel	 * Accesses to memory addresses that are not allocated to system
1041249572Sneel	 * memory or PCI devices return 0xff's.
1042247144Sgrehan	 */
1043249572Sneel	error = vm_get_memory_seg(ctx, 0, &lowmem);
1044249572Sneel	assert(error == 0);
1045249572Sneel
1046247144Sgrehan	memset(&memp, 0, sizeof(struct mem_range));
1047247144Sgrehan	memp.name = "PCI hole";
1048247144Sgrehan	memp.flags = MEM_F_RW;
1049249572Sneel	memp.base = lowmem;
1050249572Sneel	memp.size = (4ULL * 1024 * 1024 * 1024) - lowmem;
1051247144Sgrehan	memp.handler = pci_emul_fallback_handler;
1052247144Sgrehan
1053247144Sgrehan	error = register_mem_fallback(&memp);
1054247144Sgrehan	assert(error == 0);
1055221828Sgrehan}
1056221828Sgrehan
1057221828Sgrehanint
1058221828Sgrehanpci_msi_enabled(struct pci_devinst *pi)
1059221828Sgrehan{
1060221828Sgrehan	return (pi->pi_msi.enabled);
1061221828Sgrehan}
1062221828Sgrehan
1063221828Sgrehanint
1064221828Sgrehanpci_msi_msgnum(struct pci_devinst *pi)
1065221828Sgrehan{
1066221828Sgrehan	if (pi->pi_msi.enabled)
1067221828Sgrehan		return (pi->pi_msi.msgnum);
1068221828Sgrehan	else
1069221828Sgrehan		return (0);
1070221828Sgrehan}
1071221828Sgrehan
1072246109Sneelint
1073246109Sneelpci_msix_enabled(struct pci_devinst *pi)
1074246109Sneel{
1075246109Sneel
1076246109Sneel	return (pi->pi_msix.enabled && !pi->pi_msi.enabled);
1077246109Sneel}
1078246109Sneel
1079221828Sgrehanvoid
1080246109Sneelpci_generate_msix(struct pci_devinst *pi, int index)
1081246109Sneel{
1082246109Sneel	struct msix_table_entry *mte;
1083246109Sneel
1084246109Sneel	if (!pci_msix_enabled(pi))
1085246109Sneel		return;
1086246109Sneel
1087246109Sneel	if (pi->pi_msix.function_mask)
1088246109Sneel		return;
1089246109Sneel
1090246109Sneel	if (index >= pi->pi_msix.table_count)
1091246109Sneel		return;
1092246109Sneel
1093246109Sneel	mte = &pi->pi_msix.table[index];
1094246109Sneel	if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
1095246109Sneel		/* XXX Set PBA bit if interrupt is disabled */
1096246109Sneel		vm_lapic_irq(pi->pi_vmctx,
1097246109Sneel			     (mte->addr >> 12) & 0xff, mte->msg_data & 0xff);
1098246109Sneel	}
1099246109Sneel}
1100246109Sneel
1101246109Sneelvoid
1102221828Sgrehanpci_generate_msi(struct pci_devinst *pi, int msg)
1103221828Sgrehan{
1104221828Sgrehan
1105221828Sgrehan	if (pci_msi_enabled(pi) && msg < pci_msi_msgnum(pi)) {
1106221828Sgrehan		vm_lapic_irq(pi->pi_vmctx,
1107221828Sgrehan			     pi->pi_msi.cpu,
1108221828Sgrehan			     pi->pi_msi.vector + msg);
1109221828Sgrehan	}
1110221828Sgrehan}
1111221828Sgrehan
1112234938Sgrehanint
1113234938Sgrehanpci_is_legacy(struct pci_devinst *pi)
1114234938Sgrehan{
1115234938Sgrehan
1116239085Sneel	return (pci_slotinfo[pi->pi_slot][pi->pi_func].si_legacy);
1117234938Sgrehan}
1118234938Sgrehan
1119234938Sgrehanstatic int
1120234938Sgrehanpci_lintr_alloc(struct pci_devinst *pi, int vec)
1121234938Sgrehan{
1122234938Sgrehan	int i;
1123234938Sgrehan
1124234938Sgrehan	assert(vec < NLIRQ);
1125234938Sgrehan
1126234938Sgrehan	if (vec == -1) {
1127234938Sgrehan		for (i = 0; i < NLIRQ; i++) {
1128234938Sgrehan			if (lirq[i].li_generic &&
1129234938Sgrehan			    lirq[i].li_owner == NULL) {
1130234938Sgrehan				vec = i;
1131234938Sgrehan				break;
1132234938Sgrehan			}
1133234938Sgrehan		}
1134234938Sgrehan	} else {
1135239029Sneel		if (lirq[vec].li_owner != NULL) {
1136234938Sgrehan			vec = -1;
1137234938Sgrehan		}
1138234938Sgrehan	}
1139234938Sgrehan	assert(vec != -1);
1140234938Sgrehan
1141234938Sgrehan	lirq[vec].li_owner = pi;
1142234938Sgrehan	pi->pi_lintr_pin = vec;
1143234938Sgrehan
1144234938Sgrehan	return (vec);
1145234938Sgrehan}
1146234938Sgrehan
1147234938Sgrehanint
1148234938Sgrehanpci_lintr_request(struct pci_devinst *pi, int vec)
1149234938Sgrehan{
1150234938Sgrehan
1151234938Sgrehan	vec = pci_lintr_alloc(pi, vec);
1152234938Sgrehan	pci_set_cfgdata8(pi, PCIR_INTLINE, vec);
1153234938Sgrehan	pci_set_cfgdata8(pi, PCIR_INTPIN, 1);
1154234938Sgrehan	return (0);
1155234938Sgrehan}
1156234938Sgrehan
1157234938Sgrehanvoid
1158234938Sgrehanpci_lintr_assert(struct pci_devinst *pi)
1159234938Sgrehan{
1160234938Sgrehan
1161234938Sgrehan	assert(pi->pi_lintr_pin);
1162239045Sneel	ioapic_assert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
1163234938Sgrehan}
1164234938Sgrehan
1165234938Sgrehanvoid
1166234938Sgrehanpci_lintr_deassert(struct pci_devinst *pi)
1167234938Sgrehan{
1168234938Sgrehan
1169234938Sgrehan	assert(pi->pi_lintr_pin);
1170239045Sneel	ioapic_deassert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
1171234938Sgrehan}
1172234938Sgrehan
1173239085Sneel/*
1174239085Sneel * Return 1 if the emulated device in 'slot' is a multi-function device.
1175239085Sneel * Return 0 otherwise.
1176239085Sneel */
1177239085Sneelstatic int
1178239085Sneelpci_emul_is_mfdev(int slot)
1179239085Sneel{
1180239085Sneel	int f, numfuncs;
1181234938Sgrehan
1182239085Sneel	numfuncs = 0;
1183239085Sneel	for (f = 0; f < MAXFUNCS; f++) {
1184239085Sneel		if (pci_slotinfo[slot][f].si_devi != NULL) {
1185239085Sneel			numfuncs++;
1186239085Sneel		}
1187239085Sneel	}
1188239085Sneel	return (numfuncs > 1);
1189239085Sneel}
1190234938Sgrehan
1191239085Sneel/*
1192239085Sneel * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on
1193239085Sneel * whether or not is a multi-function being emulated in the pci 'slot'.
1194239085Sneel */
1195239085Sneelstatic void
1196239085Sneelpci_emul_hdrtype_fixup(int slot, int off, int bytes, uint32_t *rv)
1197239085Sneel{
1198239085Sneel	int mfdev;
1199239085Sneel
1200239085Sneel	if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) {
1201239085Sneel		mfdev = pci_emul_is_mfdev(slot);
1202239085Sneel		switch (bytes) {
1203239085Sneel		case 1:
1204239085Sneel		case 2:
1205239085Sneel			*rv &= ~PCIM_MFDEV;
1206239085Sneel			if (mfdev) {
1207239085Sneel				*rv |= PCIM_MFDEV;
1208239085Sneel			}
1209239085Sneel			break;
1210239085Sneel		case 4:
1211239085Sneel			*rv &= ~(PCIM_MFDEV << 16);
1212239085Sneel			if (mfdev) {
1213239085Sneel				*rv |= (PCIM_MFDEV << 16);
1214239085Sneel			}
1215239085Sneel			break;
1216239085Sneel		}
1217239085Sneel	}
1218239085Sneel}
1219239085Sneel
1220221828Sgrehanstatic int cfgbus, cfgslot, cfgfunc, cfgoff;
1221221828Sgrehan
1222221828Sgrehanstatic int
1223221828Sgrehanpci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
1224221828Sgrehan		 uint32_t *eax, void *arg)
1225221828Sgrehan{
1226221828Sgrehan	uint32_t x;
1227221828Sgrehan
1228252331Sgrehan	if (bytes != 4) {
1229252331Sgrehan		if (in)
1230252331Sgrehan			*eax = (bytes == 2) ? 0xffff : 0xff;
1231252331Sgrehan		return (0);
1232252331Sgrehan	}
1233221828Sgrehan
1234252331Sgrehan	if (in) {
1235252331Sgrehan		x = (cfgbus << 16) |
1236252331Sgrehan		    (cfgslot << 11) |
1237252331Sgrehan		    (cfgfunc << 8) |
1238252331Sgrehan		    cfgoff;
1239252331Sgrehan		*eax = x | CONF1_ENABLE;
1240252331Sgrehan	} else {
1241252331Sgrehan		x = *eax;
1242252331Sgrehan		cfgoff = x & PCI_REGMAX;
1243252331Sgrehan		cfgfunc = (x >> 8) & PCI_FUNCMAX;
1244252331Sgrehan		cfgslot = (x >> 11) & PCI_SLOTMAX;
1245252331Sgrehan		cfgbus = (x >> 16) & PCI_BUSMAX;
1246252331Sgrehan	}
1247221828Sgrehan
1248221828Sgrehan	return (0);
1249221828Sgrehan}
1250252331SgrehanINOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr);
1251221828Sgrehan
1252249321Sneelstatic uint32_t
1253249321Sneelbits_changed(uint32_t old, uint32_t new, uint32_t mask)
1254249321Sneel{
1255249321Sneel
1256249321Sneel	return ((old ^ new) & mask);
1257249321Sneel}
1258249321Sneel
1259249321Sneelstatic void
1260249321Sneelpci_emul_cmdwrite(struct pci_devinst *pi, uint32_t new, int bytes)
1261249321Sneel{
1262249321Sneel	int i;
1263249321Sneel	uint16_t old;
1264249321Sneel
1265249321Sneel	/*
1266249321Sneel	 * The command register is at an offset of 4 bytes and thus the
1267249321Sneel	 * guest could write 1, 2 or 4 bytes starting at this offset.
1268249321Sneel	 */
1269249321Sneel
1270249321Sneel	old = pci_get_cfgdata16(pi, PCIR_COMMAND);	/* stash old value */
1271249321Sneel	CFGWRITE(pi, PCIR_COMMAND, new, bytes);		/* update config */
1272249321Sneel	new = pci_get_cfgdata16(pi, PCIR_COMMAND);	/* get updated value */
1273249321Sneel
1274249321Sneel	/*
1275249321Sneel	 * If the MMIO or I/O address space decoding has changed then
1276249321Sneel	 * register/unregister all BARs that decode that address space.
1277249321Sneel	 */
1278249321Sneel	for (i = 0; i < PCI_BARMAX; i++) {
1279249321Sneel		switch (pi->pi_bar[i].type) {
1280249321Sneel			case PCIBAR_NONE:
1281249321Sneel			case PCIBAR_MEMHI64:
1282249321Sneel				break;
1283249321Sneel			case PCIBAR_IO:
1284249321Sneel				/* I/O address space decoding changed? */
1285249321Sneel				if (bits_changed(old, new, PCIM_CMD_PORTEN)) {
1286249321Sneel					if (porten(pi))
1287249321Sneel						register_bar(pi, i);
1288249321Sneel					else
1289249321Sneel						unregister_bar(pi, i);
1290249321Sneel				}
1291249321Sneel				break;
1292249321Sneel			case PCIBAR_MEM32:
1293249321Sneel			case PCIBAR_MEM64:
1294249321Sneel				/* MMIO address space decoding changed? */
1295249321Sneel				if (bits_changed(old, new, PCIM_CMD_MEMEN)) {
1296249321Sneel					if (memen(pi))
1297249321Sneel						register_bar(pi, i);
1298249321Sneel					else
1299249321Sneel						unregister_bar(pi, i);
1300249321Sneel				}
1301249321Sneel				break;
1302249321Sneel			default:
1303249321Sneel				assert(0);
1304249321Sneel		}
1305249321Sneel	}
1306249321Sneel}
1307249321Sneel
1308221828Sgrehanstatic int
1309221828Sgrehanpci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
1310221828Sgrehan		 uint32_t *eax, void *arg)
1311221828Sgrehan{
1312221828Sgrehan	struct pci_devinst *pi;
1313221828Sgrehan	struct pci_devemu *pe;
1314239085Sneel	int coff, idx, needcfg;
1315249321Sneel	uint64_t addr, bar, mask;
1316221828Sgrehan
1317221828Sgrehan	assert(bytes == 1 || bytes == 2 || bytes == 4);
1318221828Sgrehan
1319242170Sneel	if (cfgbus == 0)
1320242170Sneel		pi = pci_slotinfo[cfgslot][cfgfunc].si_devi;
1321242170Sneel	else
1322242170Sneel		pi = NULL;
1323242170Sneel
1324221828Sgrehan	coff = cfgoff + (port - CONF1_DATA_PORT);
1325221828Sgrehan
1326221828Sgrehan#if 0
1327221828Sgrehan	printf("pcicfg-%s from 0x%0x of %d bytes (%d/%d/%d)\n\r",
1328221828Sgrehan		in ? "read" : "write", coff, bytes, cfgbus, cfgslot, cfgfunc);
1329221828Sgrehan#endif
1330221828Sgrehan
1331239085Sneel	/*
1332239085Sneel	 * Just return if there is no device at this cfgslot:cfgfunc or
1333239085Sneel	 * if the guest is doing an un-aligned access
1334239085Sneel	 */
1335239085Sneel	if (pi == NULL || (coff & (bytes - 1)) != 0) {
1336221828Sgrehan		if (in)
1337221828Sgrehan			*eax = 0xffffffff;
1338221828Sgrehan		return (0);
1339221828Sgrehan	}
1340221828Sgrehan
1341221828Sgrehan	pe = pi->pi_d;
1342221828Sgrehan
1343221828Sgrehan	/*
1344221828Sgrehan	 * Config read
1345221828Sgrehan	 */
1346221828Sgrehan	if (in) {
1347221828Sgrehan		/* Let the device emulation override the default handler */
1348239085Sneel		if (pe->pe_cfgread != NULL) {
1349239085Sneel			needcfg = pe->pe_cfgread(ctx, vcpu, pi,
1350239085Sneel						    coff, bytes, eax);
1351239085Sneel		} else {
1352239085Sneel			needcfg = 1;
1353239085Sneel		}
1354221828Sgrehan
1355239085Sneel		if (needcfg) {
1356239085Sneel			if (bytes == 1)
1357239085Sneel				*eax = pci_get_cfgdata8(pi, coff);
1358239085Sneel			else if (bytes == 2)
1359239085Sneel				*eax = pci_get_cfgdata16(pi, coff);
1360239085Sneel			else
1361239085Sneel				*eax = pci_get_cfgdata32(pi, coff);
1362239085Sneel		}
1363239085Sneel
1364239085Sneel		pci_emul_hdrtype_fixup(cfgslot, coff, bytes, eax);
1365221828Sgrehan	} else {
1366221828Sgrehan		/* Let the device emulation override the default handler */
1367221828Sgrehan		if (pe->pe_cfgwrite != NULL &&
1368221828Sgrehan		    (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0)
1369221828Sgrehan			return (0);
1370221828Sgrehan
1371221828Sgrehan		/*
1372221828Sgrehan		 * Special handling for write to BAR registers
1373221828Sgrehan		 */
1374221828Sgrehan		if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) {
1375221828Sgrehan			/*
1376221828Sgrehan			 * Ignore writes to BAR registers that are not
1377221828Sgrehan			 * 4-byte aligned.
1378221828Sgrehan			 */
1379221828Sgrehan			if (bytes != 4 || (coff & 0x3) != 0)
1380221828Sgrehan				return (0);
1381221828Sgrehan			idx = (coff - PCIR_BAR(0)) / 4;
1382249321Sneel			mask = ~(pi->pi_bar[idx].size - 1);
1383221828Sgrehan			switch (pi->pi_bar[idx].type) {
1384221828Sgrehan			case PCIBAR_NONE:
1385249321Sneel				pi->pi_bar[idx].addr = bar = 0;
1386221828Sgrehan				break;
1387221828Sgrehan			case PCIBAR_IO:
1388249321Sneel				addr = *eax & mask;
1389249321Sneel				addr &= 0xffff;
1390249321Sneel				bar = addr | PCIM_BAR_IO_SPACE;
1391249321Sneel				/*
1392249321Sneel				 * Register the new BAR value for interception
1393249321Sneel				 */
1394249321Sneel				if (addr != pi->pi_bar[idx].addr) {
1395249321Sneel					update_bar_address(pi, addr, idx,
1396249321Sneel							   PCIBAR_IO);
1397249321Sneel				}
1398221828Sgrehan				break;
1399221828Sgrehan			case PCIBAR_MEM32:
1400249321Sneel				addr = bar = *eax & mask;
1401221828Sgrehan				bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
1402249321Sneel				if (addr != pi->pi_bar[idx].addr) {
1403249321Sneel					update_bar_address(pi, addr, idx,
1404249321Sneel							   PCIBAR_MEM32);
1405249321Sneel				}
1406221828Sgrehan				break;
1407221828Sgrehan			case PCIBAR_MEM64:
1408249321Sneel				addr = bar = *eax & mask;
1409221828Sgrehan				bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
1410221828Sgrehan				       PCIM_BAR_MEM_PREFETCH;
1411249321Sneel				if (addr != (uint32_t)pi->pi_bar[idx].addr) {
1412249321Sneel					update_bar_address(pi, addr, idx,
1413249321Sneel							   PCIBAR_MEM64);
1414249321Sneel				}
1415221828Sgrehan				break;
1416221828Sgrehan			case PCIBAR_MEMHI64:
1417221828Sgrehan				mask = ~(pi->pi_bar[idx - 1].size - 1);
1418249321Sneel				addr = ((uint64_t)*eax << 32) & mask;
1419249321Sneel				bar = addr >> 32;
1420249321Sneel				if (bar != pi->pi_bar[idx - 1].addr >> 32) {
1421249321Sneel					update_bar_address(pi, addr, idx - 1,
1422249321Sneel							   PCIBAR_MEMHI64);
1423249321Sneel				}
1424221828Sgrehan				break;
1425221828Sgrehan			default:
1426221828Sgrehan				assert(0);
1427221828Sgrehan			}
1428221828Sgrehan			pci_set_cfgdata32(pi, coff, bar);
1429234761Sgrehan
1430221828Sgrehan		} else if (pci_emul_iscap(pi, coff)) {
1431221828Sgrehan			pci_emul_capwrite(pi, coff, bytes, *eax);
1432249321Sneel		} else if (coff == PCIR_COMMAND) {
1433249321Sneel			pci_emul_cmdwrite(pi, *eax, bytes);
1434221828Sgrehan		} else {
1435221828Sgrehan			CFGWRITE(pi, coff, *eax, bytes);
1436221828Sgrehan		}
1437221828Sgrehan	}
1438221828Sgrehan
1439221828Sgrehan	return (0);
1440221828Sgrehan}
1441221828Sgrehan
1442221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata);
1443221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata);
1444221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
1445221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
1446221828Sgrehan
1447221828Sgrehan/*
1448221828Sgrehan * I/O ports to configure PCI IRQ routing. We ignore all writes to it.
1449221828Sgrehan */
1450221828Sgrehanstatic int
1451221828Sgrehanpci_irq_port_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
1452221828Sgrehan		     uint32_t *eax, void *arg)
1453221828Sgrehan{
1454221828Sgrehan	assert(in == 0);
1455221828Sgrehan	return (0);
1456221828Sgrehan}
1457221828SgrehanINOUT_PORT(pci_irq, 0xC00, IOPORT_F_OUT, pci_irq_port_handler);
1458221828SgrehanINOUT_PORT(pci_irq, 0xC01, IOPORT_F_OUT, pci_irq_port_handler);
1459221828Sgrehan
1460221828Sgrehan#define PCI_EMUL_TEST
1461221828Sgrehan#ifdef PCI_EMUL_TEST
1462221828Sgrehan/*
1463221828Sgrehan * Define a dummy test device
1464221828Sgrehan */
1465241744Sgrehan#define DIOSZ	20
1466241744Sgrehan#define DMEMSZ	4096
1467221828Sgrehanstruct pci_emul_dsoftc {
1468241744Sgrehan	uint8_t   ioregs[DIOSZ];
1469241744Sgrehan	uint8_t	  memregs[DMEMSZ];
1470221828Sgrehan};
1471221828Sgrehan
1472241744Sgrehan#define	PCI_EMUL_MSI_MSGS	 4
1473241744Sgrehan#define	PCI_EMUL_MSIX_MSGS	16
1474221828Sgrehan
1475221942Sjhbstatic int
1476221828Sgrehanpci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
1477221828Sgrehan{
1478221828Sgrehan	int error;
1479221828Sgrehan	struct pci_emul_dsoftc *sc;
1480221828Sgrehan
1481221828Sgrehan	sc = malloc(sizeof(struct pci_emul_dsoftc));
1482221828Sgrehan	memset(sc, 0, sizeof(struct pci_emul_dsoftc));
1483221828Sgrehan
1484221828Sgrehan	pi->pi_arg = sc;
1485221828Sgrehan
1486221828Sgrehan	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001);
1487221828Sgrehan	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD);
1488221828Sgrehan	pci_set_cfgdata8(pi, PCIR_CLASS, 0x02);
1489221828Sgrehan
1490241744Sgrehan	error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS);
1491221828Sgrehan	assert(error == 0);
1492221828Sgrehan
1493241744Sgrehan	error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ);
1494221828Sgrehan	assert(error == 0);
1495221828Sgrehan
1496241744Sgrehan	error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ);
1497241744Sgrehan	assert(error == 0);
1498241744Sgrehan
1499221828Sgrehan	return (0);
1500221828Sgrehan}
1501221828Sgrehan
1502221942Sjhbstatic void
1503241744Sgrehanpci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
1504241744Sgrehan	      uint64_t offset, int size, uint64_t value)
1505221828Sgrehan{
1506221828Sgrehan	int i;
1507221828Sgrehan	struct pci_emul_dsoftc *sc = pi->pi_arg;
1508221828Sgrehan
1509241744Sgrehan	if (baridx == 0) {
1510241744Sgrehan		if (offset + size > DIOSZ) {
1511241744Sgrehan			printf("diow: iow too large, offset %ld size %d\n",
1512241744Sgrehan			       offset, size);
1513241744Sgrehan			return;
1514241744Sgrehan		}
1515221828Sgrehan
1516241744Sgrehan		if (size == 1) {
1517241744Sgrehan			sc->ioregs[offset] = value & 0xff;
1518241744Sgrehan		} else if (size == 2) {
1519241744Sgrehan			*(uint16_t *)&sc->ioregs[offset] = value & 0xffff;
1520241744Sgrehan		} else if (size == 4) {
1521241744Sgrehan			*(uint32_t *)&sc->ioregs[offset] = value;
1522241744Sgrehan		} else {
1523241744Sgrehan			printf("diow: iow unknown size %d\n", size);
1524241744Sgrehan		}
1525241744Sgrehan
1526241744Sgrehan		/*
1527241744Sgrehan		 * Special magic value to generate an interrupt
1528241744Sgrehan		 */
1529241744Sgrehan		if (offset == 4 && size == 4 && pci_msi_enabled(pi))
1530241744Sgrehan			pci_generate_msi(pi, value % pci_msi_msgnum(pi));
1531241744Sgrehan
1532241744Sgrehan		if (value == 0xabcdef) {
1533241744Sgrehan			for (i = 0; i < pci_msi_msgnum(pi); i++)
1534241744Sgrehan				pci_generate_msi(pi, i);
1535241744Sgrehan		}
1536221828Sgrehan	}
1537221828Sgrehan
1538241744Sgrehan	if (baridx == 1) {
1539241744Sgrehan		if (offset + size > DMEMSZ) {
1540241744Sgrehan			printf("diow: memw too large, offset %ld size %d\n",
1541241744Sgrehan			       offset, size);
1542241744Sgrehan			return;
1543241744Sgrehan		}
1544221828Sgrehan
1545241744Sgrehan		if (size == 1) {
1546241744Sgrehan			sc->memregs[offset] = value;
1547241744Sgrehan		} else if (size == 2) {
1548241744Sgrehan			*(uint16_t *)&sc->memregs[offset] = value;
1549241744Sgrehan		} else if (size == 4) {
1550241744Sgrehan			*(uint32_t *)&sc->memregs[offset] = value;
1551241744Sgrehan		} else if (size == 8) {
1552241744Sgrehan			*(uint64_t *)&sc->memregs[offset] = value;
1553241744Sgrehan		} else {
1554241744Sgrehan			printf("diow: memw unknown size %d\n", size);
1555241744Sgrehan		}
1556241744Sgrehan
1557241744Sgrehan		/*
1558241744Sgrehan		 * magic interrupt ??
1559241744Sgrehan		 */
1560221828Sgrehan	}
1561241744Sgrehan
1562241744Sgrehan	if (baridx > 1) {
1563241744Sgrehan		printf("diow: unknown bar idx %d\n", baridx);
1564241744Sgrehan	}
1565221828Sgrehan}
1566221828Sgrehan
1567241744Sgrehanstatic uint64_t
1568241744Sgrehanpci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
1569241744Sgrehan	      uint64_t offset, int size)
1570221828Sgrehan{
1571221828Sgrehan	struct pci_emul_dsoftc *sc = pi->pi_arg;
1572221828Sgrehan	uint32_t value;
1573221828Sgrehan
1574241744Sgrehan	if (baridx == 0) {
1575241744Sgrehan		if (offset + size > DIOSZ) {
1576241744Sgrehan			printf("dior: ior too large, offset %ld size %d\n",
1577241744Sgrehan			       offset, size);
1578241744Sgrehan			return (0);
1579241744Sgrehan		}
1580241744Sgrehan
1581241744Sgrehan		if (size == 1) {
1582241744Sgrehan			value = sc->ioregs[offset];
1583241744Sgrehan		} else if (size == 2) {
1584241744Sgrehan			value = *(uint16_t *) &sc->ioregs[offset];
1585241744Sgrehan		} else if (size == 4) {
1586241744Sgrehan			value = *(uint32_t *) &sc->ioregs[offset];
1587241744Sgrehan		} else {
1588241744Sgrehan			printf("dior: ior unknown size %d\n", size);
1589241744Sgrehan		}
1590221828Sgrehan	}
1591221828Sgrehan
1592241744Sgrehan	if (baridx == 1) {
1593241744Sgrehan		if (offset + size > DMEMSZ) {
1594241744Sgrehan			printf("dior: memr too large, offset %ld size %d\n",
1595241744Sgrehan			       offset, size);
1596241744Sgrehan			return (0);
1597241744Sgrehan		}
1598241744Sgrehan
1599241744Sgrehan		if (size == 1) {
1600241744Sgrehan			value = sc->memregs[offset];
1601241744Sgrehan		} else if (size == 2) {
1602241744Sgrehan			value = *(uint16_t *) &sc->memregs[offset];
1603241744Sgrehan		} else if (size == 4) {
1604241744Sgrehan			value = *(uint32_t *) &sc->memregs[offset];
1605241744Sgrehan		} else if (size == 8) {
1606241744Sgrehan			value = *(uint64_t *) &sc->memregs[offset];
1607241744Sgrehan		} else {
1608241744Sgrehan			printf("dior: ior unknown size %d\n", size);
1609241744Sgrehan		}
1610221828Sgrehan	}
1611221828Sgrehan
1612241744Sgrehan
1613241744Sgrehan	if (baridx > 1) {
1614241744Sgrehan		printf("dior: unknown bar idx %d\n", baridx);
1615241744Sgrehan		return (0);
1616241744Sgrehan	}
1617241744Sgrehan
1618221828Sgrehan	return (value);
1619221828Sgrehan}
1620221828Sgrehan
1621221828Sgrehanstruct pci_devemu pci_dummy = {
1622221828Sgrehan	.pe_emu = "dummy",
1623221828Sgrehan	.pe_init = pci_emul_dinit,
1624241744Sgrehan	.pe_barwrite = pci_emul_diow,
1625241744Sgrehan	.pe_barread = pci_emul_dior
1626221828Sgrehan};
1627221828SgrehanPCI_EMUL_SET(pci_dummy);
1628221828Sgrehan
1629221828Sgrehan#endif /* PCI_EMUL_TEST */
1630