xref: /freebsd/usr.sbin/bhyve/pci_emul.c (revision ec8a394d)
1366f6083SPeter Grehan /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4366f6083SPeter Grehan  * Copyright (c) 2011 NetApp, Inc.
5366f6083SPeter Grehan  * All rights reserved.
6366f6083SPeter Grehan  *
7366f6083SPeter Grehan  * Redistribution and use in source and binary forms, with or without
8366f6083SPeter Grehan  * modification, are permitted provided that the following conditions
9366f6083SPeter Grehan  * are met:
10366f6083SPeter Grehan  * 1. Redistributions of source code must retain the above copyright
11366f6083SPeter Grehan  *    notice, this list of conditions and the following disclaimer.
12366f6083SPeter Grehan  * 2. Redistributions in binary form must reproduce the above copyright
13366f6083SPeter Grehan  *    notice, this list of conditions and the following disclaimer in the
14366f6083SPeter Grehan  *    documentation and/or other materials provided with the distribution.
15366f6083SPeter Grehan  *
16366f6083SPeter Grehan  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
17366f6083SPeter Grehan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18366f6083SPeter Grehan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19366f6083SPeter Grehan  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
20366f6083SPeter Grehan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21366f6083SPeter Grehan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22366f6083SPeter Grehan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23366f6083SPeter Grehan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24366f6083SPeter Grehan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25366f6083SPeter Grehan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26366f6083SPeter Grehan  * SUCH DAMAGE.
27366f6083SPeter Grehan  */
28366f6083SPeter Grehan 
29366f6083SPeter Grehan #include <sys/param.h>
30366f6083SPeter Grehan #include <sys/linker_set.h>
31e47fe318SCorvin Köhne #include <sys/mman.h>
32366f6083SPeter Grehan 
33366f6083SPeter Grehan #include <ctype.h>
34fc7207c8SEmmanuel Vadot #include <err.h>
357e12dfe5SEnji Cooper #include <errno.h>
363cbf3585SJohn Baldwin #include <pthread.h>
37366f6083SPeter Grehan #include <stdio.h>
38366f6083SPeter Grehan #include <stdlib.h>
39366f6083SPeter Grehan #include <string.h>
40366f6083SPeter Grehan #include <strings.h>
41366f6083SPeter Grehan #include <assert.h>
42028d9311SNeel Natu #include <stdbool.h>
435cf21e48SCorvin Köhne #include <sysexits.h>
44366f6083SPeter Grehan 
45366f6083SPeter Grehan #include <machine/vmm.h>
46483d953aSJohn Baldwin #include <machine/vmm_snapshot.h>
47366f6083SPeter Grehan #include <vmmapi.h>
48366f6083SPeter Grehan 
49e6c8bc29SJohn Baldwin #include "acpi.h"
50e285ef8dSPeter Grehan #include "bhyverun.h"
51621b5090SJohn Baldwin #include "config.h"
52332eff95SVincenzo Maffione #include "debug.h"
5355c13f6eSMark Johnston #ifdef __amd64__
5431cf78c9SMark Johnston #include "amd64/inout.h"
5555c13f6eSMark Johnston #endif
564d1e669cSPeter Grehan #include "mem.h"
57366f6083SPeter Grehan #include "pci_emul.h"
5855c13f6eSMark Johnston #ifdef __amd64__
5955c13f6eSMark Johnston #include "amd64/pci_lpc.h"
60ffaed739SCorvin Köhne #include "pci_passthru.h"
61fc98569fSMark Johnston #endif
626632a0a4SCorvin Köhne #include "qemu_fwcfg.h"
63366f6083SPeter Grehan 
64366f6083SPeter Grehan #define CONF1_ADDR_PORT	   0x0cf8
65366f6083SPeter Grehan #define CONF1_DATA_PORT	   0x0cfc
66366f6083SPeter Grehan 
6775543036SPeter Grehan #define CONF1_ENABLE	   0x80000000ul
6875543036SPeter Grehan 
69d84882caSNeel Natu #define	MAXBUSES	(PCI_BUSMAX + 1)
7099d65389SNeel Natu #define MAXSLOTS	(PCI_SLOTMAX + 1)
7199d65389SNeel Natu #define	MAXFUNCS	(PCI_FUNCMAX + 1)
72366f6083SPeter Grehan 
734a4053e1SCorvin Köhne #define GB		(1024 * 1024 * 1024UL)
744a4053e1SCorvin Köhne 
753cbf3585SJohn Baldwin struct funcinfo {
76621b5090SJohn Baldwin 	nvlist_t *fi_config;
77621b5090SJohn Baldwin 	struct pci_devemu *fi_pde;
783cbf3585SJohn Baldwin 	struct pci_devinst *fi_devi;
793cbf3585SJohn Baldwin };
803cbf3585SJohn Baldwin 
813cbf3585SJohn Baldwin struct intxinfo {
823cbf3585SJohn Baldwin 	int		ii_count;
830efad4acSJessica Clarke 	struct pci_irq	ii_irq;
843cbf3585SJohn Baldwin };
853cbf3585SJohn Baldwin 
863cbf3585SJohn Baldwin struct slotinfo {
873cbf3585SJohn Baldwin 	struct intxinfo si_intpins[4];
883cbf3585SJohn Baldwin 	struct funcinfo si_funcs[MAXFUNCS];
89d84882caSNeel Natu };
90d84882caSNeel Natu 
91d84882caSNeel Natu struct businfo {
92d84882caSNeel Natu 	uint16_t iobase, iolimit;		/* I/O window */
93d84882caSNeel Natu 	uint32_t membase32, memlimit32;		/* mmio window below 4GB */
94d84882caSNeel Natu 	uint64_t membase64, memlimit64;		/* mmio window above 4GB */
95d84882caSNeel Natu 	struct slotinfo slotinfo[MAXSLOTS];
96d84882caSNeel Natu };
97d84882caSNeel Natu 
98d84882caSNeel Natu static struct businfo *pci_businfo[MAXBUSES];
99366f6083SPeter Grehan 
100366f6083SPeter Grehan SET_DECLARE(pci_devemu_set, struct pci_devemu);
101366f6083SPeter Grehan 
102366f6083SPeter Grehan static uint64_t pci_emul_iobase;
103e47fe318SCorvin Köhne static uint8_t *pci_emul_rombase;
104e47fe318SCorvin Köhne static uint64_t pci_emul_romoffset;
105e47fe318SCorvin Köhne static uint8_t *pci_emul_romlim;
106366f6083SPeter Grehan static uint64_t pci_emul_membase32;
107366f6083SPeter Grehan static uint64_t pci_emul_membase64;
1089922872bSKonstantin Belousov static uint64_t pci_emul_memlim64;
109366f6083SPeter Grehan 
11001f9362eSCorvin Köhne struct pci_bar_allocation {
11101f9362eSCorvin Köhne 	TAILQ_ENTRY(pci_bar_allocation) chain;
11201f9362eSCorvin Köhne 	struct pci_devinst *pdi;
11301f9362eSCorvin Köhne 	int idx;
11401f9362eSCorvin Köhne 	enum pcibar_type type;
11501f9362eSCorvin Köhne 	uint64_t size;
11601f9362eSCorvin Köhne };
11707d82562SMark Johnston 
11807d82562SMark Johnston static TAILQ_HEAD(pci_bar_list, pci_bar_allocation) pci_bars =
11907d82562SMark Johnston     TAILQ_HEAD_INITIALIZER(pci_bars);
12001f9362eSCorvin Köhne 
1216632a0a4SCorvin Köhne struct boot_device {
1226632a0a4SCorvin Köhne 	TAILQ_ENTRY(boot_device) boot_device_chain;
1236632a0a4SCorvin Köhne 	struct pci_devinst *pdi;
1246632a0a4SCorvin Köhne 	int bootindex;
1256632a0a4SCorvin Köhne };
1266632a0a4SCorvin Köhne static TAILQ_HEAD(boot_list, boot_device) boot_devices = TAILQ_HEAD_INITIALIZER(
1276632a0a4SCorvin Köhne     boot_devices);
1286632a0a4SCorvin Köhne 
129f286f746SMark Johnston #if defined(__amd64__)
130366f6083SPeter Grehan #define	PCI_EMUL_IOBASE		0x2000
131366f6083SPeter Grehan #define	PCI_EMUL_IOLIMIT	0x10000
132f286f746SMark Johnston #define	PCI_EMUL_IOMASK		0xffff
133f286f746SMark Johnston /*
134f286f746SMark Johnston  * OVMF always uses 0xc0000000 as base address for 32 bit PCI MMIO. Don't
135f286f746SMark Johnston  * change this address without changing it in OVMF.
136f286f746SMark Johnston  */
137f286f746SMark Johnston #define	PCI_EMUL_MEMBASE32	0xc0000000
138f286f746SMark Johnston #elif defined(__aarch64__)
13995b948c1SJessica Clarke #define	PCI_EMUL_IOBASE		0xdf000000UL
14095b948c1SJessica Clarke #define	PCI_EMUL_IOLIMIT	0xe0000000UL
14195b948c1SJessica Clarke #define	PCI_EMUL_MEMBASE32	0xa0000000UL
142f286f746SMark Johnston #else
143f286f746SMark Johnston #error Unsupported platform
144f286f746SMark Johnston #endif
145366f6083SPeter Grehan 
146e47fe318SCorvin Köhne #define	PCI_EMUL_ROMSIZE	0x10000000
147e47fe318SCorvin Köhne 
14812a6eb99SNeel Natu #define	PCI_EMUL_ECFG_BASE	0xE0000000		    /* 3.5GB */
14912a6eb99SNeel Natu #define	PCI_EMUL_ECFG_SIZE	(MAXBUSES * 1024 * 1024)    /* 1MB per bus */
150f286f746SMark Johnston #ifdef __amd64__
15112a6eb99SNeel Natu SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE);
152f286f746SMark Johnston #endif
15312a6eb99SNeel Natu 
15412a6eb99SNeel Natu #define	PCI_EMUL_MEMLIMIT32	PCI_EMUL_ECFG_BASE
1554a4053e1SCorvin Köhne #define PCI_EMUL_MEMSIZE64	(32*GB)
156366f6083SPeter Grehan 
157b3e9732aSJohn Baldwin static void pci_lintr_route(struct pci_devinst *pi);
1583cbf3585SJohn Baldwin static void pci_lintr_update(struct pci_devinst *pi);
15955c13f6eSMark Johnston 
16055c13f6eSMark Johnston static struct pci_devemu *pci_emul_finddev(const char *name);
1616a284cacSJohn Baldwin static void pci_cfgrw(int in, int bus, int slot, int func, int coff,
1626a284cacSJohn Baldwin     int bytes, uint32_t *val);
163366f6083SPeter Grehan 
16454335630SNeel Natu static __inline void
CFGWRITE(struct pci_devinst * pi,int coff,uint32_t val,int bytes)16554335630SNeel Natu CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes)
16654335630SNeel Natu {
16754335630SNeel Natu 
16854335630SNeel Natu 	if (bytes == 1)
16954335630SNeel Natu 		pci_set_cfgdata8(pi, coff, val);
17054335630SNeel Natu 	else if (bytes == 2)
17154335630SNeel Natu 		pci_set_cfgdata16(pi, coff, val);
17254335630SNeel Natu 	else
17354335630SNeel Natu 		pci_set_cfgdata32(pi, coff, val);
17454335630SNeel Natu }
17554335630SNeel Natu 
17654335630SNeel Natu static __inline uint32_t
CFGREAD(struct pci_devinst * pi,int coff,int bytes)17754335630SNeel Natu CFGREAD(struct pci_devinst *pi, int coff, int bytes)
17854335630SNeel Natu {
17954335630SNeel Natu 
18054335630SNeel Natu 	if (bytes == 1)
18154335630SNeel Natu 		return (pci_get_cfgdata8(pi, coff));
18254335630SNeel Natu 	else if (bytes == 2)
18354335630SNeel Natu 		return (pci_get_cfgdata16(pi, coff));
18454335630SNeel Natu 	else
18554335630SNeel Natu 		return (pci_get_cfgdata32(pi, coff));
18654335630SNeel Natu }
18754335630SNeel Natu 
18845ddbf21SCorvin Köhne static int
is_pcir_bar(int coff)18945ddbf21SCorvin Köhne is_pcir_bar(int coff)
19045ddbf21SCorvin Köhne {
19145ddbf21SCorvin Köhne 	return (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1));
19245ddbf21SCorvin Köhne }
19345ddbf21SCorvin Köhne 
19445ddbf21SCorvin Köhne static int
is_pcir_bios(int coff)19545ddbf21SCorvin Köhne is_pcir_bios(int coff)
19645ddbf21SCorvin Köhne {
19745ddbf21SCorvin Köhne 	return (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4);
19845ddbf21SCorvin Köhne }
19945ddbf21SCorvin Köhne 
200366f6083SPeter Grehan /*
201366f6083SPeter Grehan  * I/O access
202366f6083SPeter Grehan  */
203366f6083SPeter Grehan 
204366f6083SPeter Grehan /*
205366f6083SPeter Grehan  * Slot options are in the form:
206366f6083SPeter Grehan  *
207d84882caSNeel Natu  *  <bus>:<slot>:<func>,<emul>[,<config>]
20899d65389SNeel Natu  *  <slot>[:<func>],<emul>[,<config>]
209366f6083SPeter Grehan  *
210366f6083SPeter Grehan  *  slot is 0..31
21199d65389SNeel Natu  *  func is 0..7
212366f6083SPeter Grehan  *  emul is a string describing the type of PCI device e.g. virtio-net
213366f6083SPeter Grehan  *  config is an optional string, depending on the device, that can be
214366f6083SPeter Grehan  *  used for configuration.
215366f6083SPeter Grehan  *   Examples are:
216366f6083SPeter Grehan  *     1,virtio-net,tap0
21799d65389SNeel Natu  *     3:0,dummy
218366f6083SPeter Grehan  */
219366f6083SPeter Grehan static void
pci_parse_slot_usage(char * aopt)220366f6083SPeter Grehan pci_parse_slot_usage(char *aopt)
221366f6083SPeter Grehan {
222b05c77ffSNeel Natu 
223332eff95SVincenzo Maffione 	EPRINTLN("Invalid PCI slot info field \"%s\"", aopt);
224366f6083SPeter Grehan }
225366f6083SPeter Grehan 
226621b5090SJohn Baldwin /*
227621b5090SJohn Baldwin  * Helper function to parse a list of comma-separated options where
228621b5090SJohn Baldwin  * each option is formatted as "name[=value]".  If no value is
229621b5090SJohn Baldwin  * provided, the option is treated as a boolean and is given a value
230621b5090SJohn Baldwin  * of true.
231621b5090SJohn Baldwin  */
232621b5090SJohn Baldwin int
pci_parse_legacy_config(nvlist_t * nvl,const char * opt)233621b5090SJohn Baldwin pci_parse_legacy_config(nvlist_t *nvl, const char *opt)
234621b5090SJohn Baldwin {
235621b5090SJohn Baldwin 	char *config, *name, *tofree, *value;
236621b5090SJohn Baldwin 
237621b5090SJohn Baldwin 	if (opt == NULL)
238621b5090SJohn Baldwin 		return (0);
239621b5090SJohn Baldwin 
240621b5090SJohn Baldwin 	config = tofree = strdup(opt);
241621b5090SJohn Baldwin 	while ((name = strsep(&config, ",")) != NULL) {
242621b5090SJohn Baldwin 		value = strchr(name, '=');
243621b5090SJohn Baldwin 		if (value != NULL) {
244621b5090SJohn Baldwin 			*value = '\0';
245621b5090SJohn Baldwin 			value++;
246621b5090SJohn Baldwin 			set_config_value_node(nvl, name, value);
247621b5090SJohn Baldwin 		} else
248621b5090SJohn Baldwin 			set_config_bool_node(nvl, name, true);
249621b5090SJohn Baldwin 	}
250621b5090SJohn Baldwin 	free(tofree);
251621b5090SJohn Baldwin 	return (0);
252621b5090SJohn Baldwin }
253621b5090SJohn Baldwin 
254621b5090SJohn Baldwin /*
255621b5090SJohn Baldwin  * PCI device configuration is stored in MIBs that encode the device's
256621b5090SJohn Baldwin  * location:
257621b5090SJohn Baldwin  *
258621b5090SJohn Baldwin  * pci.<bus>.<slot>.<func>
259621b5090SJohn Baldwin  *
260621b5090SJohn Baldwin  * Where "bus", "slot", and "func" are all decimal values without
261621b5090SJohn Baldwin  * leading zeroes.  Each valid device must have a "device" node which
262621b5090SJohn Baldwin  * identifies the driver model of the device.
263621b5090SJohn Baldwin  *
264621b5090SJohn Baldwin  * Device backends can provide a parser for the "config" string.  If
265621b5090SJohn Baldwin  * a custom parser is not provided, pci_parse_legacy_config() is used
266621b5090SJohn Baldwin  * to parse the string.
267621b5090SJohn Baldwin  */
268b05c77ffSNeel Natu int
pci_parse_slot(char * opt)269d2bc4816SJohn Baldwin pci_parse_slot(char *opt)
270366f6083SPeter Grehan {
271621b5090SJohn Baldwin 	char node_name[sizeof("pci.XXX.XX.X")];
272621b5090SJohn Baldwin 	struct pci_devemu *pde;
273d84882caSNeel Natu 	char *emul, *config, *str, *cp;
274d84882caSNeel Natu 	int error, bnum, snum, fnum;
275621b5090SJohn Baldwin 	nvlist_t *nvl;
276366f6083SPeter Grehan 
277b05c77ffSNeel Natu 	error = -1;
278d84882caSNeel Natu 	str = strdup(opt);
27999d65389SNeel Natu 
280d84882caSNeel Natu 	emul = config = NULL;
281d84882caSNeel Natu 	if ((cp = strchr(str, ',')) != NULL) {
282d84882caSNeel Natu 		*cp = '\0';
283d84882caSNeel Natu 		emul = cp + 1;
284d84882caSNeel Natu 		if ((cp = strchr(emul, ',')) != NULL) {
285d84882caSNeel Natu 			*cp = '\0';
286d84882caSNeel Natu 			config = cp + 1;
28799d65389SNeel Natu 		}
288d84882caSNeel Natu 	} else {
289b05c77ffSNeel Natu 		pci_parse_slot_usage(opt);
290b05c77ffSNeel Natu 		goto done;
291366f6083SPeter Grehan 	}
292366f6083SPeter Grehan 
293d84882caSNeel Natu 	/* <bus>:<slot>:<func> */
294d84882caSNeel Natu 	if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) {
295d84882caSNeel Natu 		bnum = 0;
296d84882caSNeel Natu 		/* <slot>:<func> */
297d84882caSNeel Natu 		if (sscanf(str, "%d:%d", &snum, &fnum) != 2) {
298d84882caSNeel Natu 			fnum = 0;
299d84882caSNeel Natu 			/* <slot> */
300d84882caSNeel Natu 			if (sscanf(str, "%d", &snum) != 1) {
301d84882caSNeel Natu 				snum = -1;
302d84882caSNeel Natu 			}
303d84882caSNeel Natu 		}
304d84882caSNeel Natu 	}
305b05c77ffSNeel Natu 
306d84882caSNeel Natu 	if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS ||
307d84882caSNeel Natu 	    fnum < 0 || fnum >= MAXFUNCS) {
308b05c77ffSNeel Natu 		pci_parse_slot_usage(opt);
309b05c77ffSNeel Natu 		goto done;
310b05c77ffSNeel Natu 	}
311b05c77ffSNeel Natu 
312621b5090SJohn Baldwin 	pde = pci_emul_finddev(emul);
313621b5090SJohn Baldwin 	if (pde == NULL) {
314621b5090SJohn Baldwin 		EPRINTLN("pci slot %d:%d:%d: unknown device \"%s\"", bnum, snum,
315621b5090SJohn Baldwin 		    fnum, emul);
316b05c77ffSNeel Natu 		goto done;
317b05c77ffSNeel Natu 	}
318b05c77ffSNeel Natu 
319621b5090SJohn Baldwin 	snprintf(node_name, sizeof(node_name), "pci.%d.%d.%d", bnum, snum,
320621b5090SJohn Baldwin 	    fnum);
321621b5090SJohn Baldwin 	nvl = find_config_node(node_name);
322621b5090SJohn Baldwin 	if (nvl != NULL) {
323621b5090SJohn Baldwin 		EPRINTLN("pci slot %d:%d:%d already occupied!", bnum, snum,
324621b5090SJohn Baldwin 		    fnum);
325b05c77ffSNeel Natu 		goto done;
326b05c77ffSNeel Natu 	}
327621b5090SJohn Baldwin 	nvl = create_config_node(node_name);
328621b5090SJohn Baldwin 	if (pde->pe_alias != NULL)
329621b5090SJohn Baldwin 		set_config_value_node(nvl, "device", pde->pe_alias);
330621b5090SJohn Baldwin 	else
331621b5090SJohn Baldwin 		set_config_value_node(nvl, "device", pde->pe_emu);
332b05c77ffSNeel Natu 
333621b5090SJohn Baldwin 	if (pde->pe_legacy_config != NULL)
334621b5090SJohn Baldwin 		error = pde->pe_legacy_config(nvl, config);
335621b5090SJohn Baldwin 	else
336621b5090SJohn Baldwin 		error = pci_parse_legacy_config(nvl, config);
337b05c77ffSNeel Natu done:
338d84882caSNeel Natu 	free(str);
339b05c77ffSNeel Natu 	return (error);
340366f6083SPeter Grehan }
341366f6083SPeter Grehan 
342657d2158SMarcelo Araujo void
pci_print_supported_devices(void)34375ce327aSMark Johnston pci_print_supported_devices(void)
344657d2158SMarcelo Araujo {
345657d2158SMarcelo Araujo 	struct pci_devemu **pdpp, *pdp;
346657d2158SMarcelo Araujo 
347657d2158SMarcelo Araujo 	SET_FOREACH(pdpp, pci_devemu_set) {
348657d2158SMarcelo Araujo 		pdp = *pdpp;
349657d2158SMarcelo Araujo 		printf("%s\n", pdp->pe_emu);
350657d2158SMarcelo Araujo 	}
351657d2158SMarcelo Araujo }
352657d2158SMarcelo Araujo 
353ffaed739SCorvin Köhne uint32_t
pci_config_read_reg(const struct pcisel * const host_sel,nvlist_t * nvl,const uint32_t reg,const uint8_t size,const uint32_t def)354ffaed739SCorvin Köhne pci_config_read_reg(const struct pcisel *const host_sel, nvlist_t *nvl,
355ffaed739SCorvin Köhne     const uint32_t reg, const uint8_t size, const uint32_t def)
356ffaed739SCorvin Köhne {
357ffaed739SCorvin Köhne 	const char *config;
358ffaed739SCorvin Köhne 	const nvlist_t *pci_regs;
359ffaed739SCorvin Köhne 
360ffaed739SCorvin Köhne 	assert(size == 1 || size == 2 || size == 4);
361ffaed739SCorvin Köhne 
362ffaed739SCorvin Köhne 	pci_regs = find_relative_config_node(nvl, "pcireg");
363ffaed739SCorvin Köhne 	if (pci_regs == NULL) {
364ffaed739SCorvin Köhne 		return def;
365ffaed739SCorvin Köhne 	}
366ffaed739SCorvin Köhne 
367ffaed739SCorvin Köhne 	switch (reg) {
368ffaed739SCorvin Köhne 	case PCIR_DEVICE:
369ffaed739SCorvin Köhne 		config = get_config_value_node(pci_regs, "device");
370ffaed739SCorvin Köhne 		break;
371ffaed739SCorvin Köhne 	case PCIR_VENDOR:
372ffaed739SCorvin Köhne 		config = get_config_value_node(pci_regs, "vendor");
373ffaed739SCorvin Köhne 		break;
374ffaed739SCorvin Köhne 	case PCIR_REVID:
375ffaed739SCorvin Köhne 		config = get_config_value_node(pci_regs, "revid");
376ffaed739SCorvin Köhne 		break;
377ffaed739SCorvin Köhne 	case PCIR_SUBVEND_0:
378ffaed739SCorvin Köhne 		config = get_config_value_node(pci_regs, "subvendor");
379ffaed739SCorvin Köhne 		break;
380ffaed739SCorvin Köhne 	case PCIR_SUBDEV_0:
381ffaed739SCorvin Köhne 		config = get_config_value_node(pci_regs, "subdevice");
382ffaed739SCorvin Köhne 		break;
383ffaed739SCorvin Köhne 	default:
384ffaed739SCorvin Köhne 		return (-1);
385ffaed739SCorvin Köhne 	}
386ffaed739SCorvin Köhne 
387ffaed739SCorvin Köhne 	if (config == NULL) {
388ffaed739SCorvin Köhne 		return def;
389ffaed739SCorvin Köhne 	} else if (host_sel != NULL && strcmp(config, "host") == 0) {
390fc98569fSMark Johnston #ifdef __amd64__
39101d53c34SMark Johnston 		return pci_host_read_config(host_sel, reg, size);
392fc98569fSMark Johnston #else
393fc98569fSMark Johnston 		errx(1, "cannot fetch host PCI configuration");
394fc98569fSMark Johnston #endif
395ffaed739SCorvin Köhne 	} else {
396ffaed739SCorvin Köhne 		return strtol(config, NULL, 16);
397ffaed739SCorvin Köhne 	}
398ffaed739SCorvin Köhne }
399ffaed739SCorvin Köhne 
400366f6083SPeter Grehan static int
pci_valid_pba_offset(struct pci_devinst * pi,uint64_t offset)401c9b4e987SNeel Natu pci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset)
402c9b4e987SNeel Natu {
403c9b4e987SNeel Natu 
404c9b4e987SNeel Natu 	if (offset < pi->pi_msix.pba_offset)
405c9b4e987SNeel Natu 		return (0);
406c9b4e987SNeel Natu 
407c9b4e987SNeel Natu 	if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) {
408c9b4e987SNeel Natu 		return (0);
409c9b4e987SNeel Natu 	}
410c9b4e987SNeel Natu 
411c9b4e987SNeel Natu 	return (1);
412c9b4e987SNeel Natu }
413c9b4e987SNeel Natu 
414c9b4e987SNeel Natu int
pci_emul_msix_twrite(struct pci_devinst * pi,uint64_t offset,int size,uint64_t value)415c9b4e987SNeel Natu pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size,
416c9b4e987SNeel Natu 		     uint64_t value)
417c9b4e987SNeel Natu {
418c9b4e987SNeel Natu 	int msix_entry_offset;
419c9b4e987SNeel Natu 	int tab_index;
420c9b4e987SNeel Natu 	char *dest;
421c9b4e987SNeel Natu 
422c9b4e987SNeel Natu 	/* support only 4 or 8 byte writes */
423c9b4e987SNeel Natu 	if (size != 4 && size != 8)
424c9b4e987SNeel Natu 		return (-1);
425c9b4e987SNeel Natu 
426c9b4e987SNeel Natu 	/*
427c9b4e987SNeel Natu 	 * Return if table index is beyond what device supports
428c9b4e987SNeel Natu 	 */
429c9b4e987SNeel Natu 	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
430c9b4e987SNeel Natu 	if (tab_index >= pi->pi_msix.table_count)
431c9b4e987SNeel Natu 		return (-1);
432c9b4e987SNeel Natu 
433c9b4e987SNeel Natu 	msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
434c9b4e987SNeel Natu 
435c9b4e987SNeel Natu 	/* support only aligned writes */
436c9b4e987SNeel Natu 	if ((msix_entry_offset % size) != 0)
437c9b4e987SNeel Natu 		return (-1);
438c9b4e987SNeel Natu 
439c9b4e987SNeel Natu 	dest = (char *)(pi->pi_msix.table + tab_index);
440c9b4e987SNeel Natu 	dest += msix_entry_offset;
441c9b4e987SNeel Natu 
442c9b4e987SNeel Natu 	if (size == 4)
443c9b4e987SNeel Natu 		*((uint32_t *)dest) = value;
444c9b4e987SNeel Natu 	else
445c9b4e987SNeel Natu 		*((uint64_t *)dest) = value;
446c9b4e987SNeel Natu 
447c9b4e987SNeel Natu 	return (0);
448c9b4e987SNeel Natu }
449c9b4e987SNeel Natu 
450c9b4e987SNeel Natu uint64_t
pci_emul_msix_tread(struct pci_devinst * pi,uint64_t offset,int size)451c9b4e987SNeel Natu pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size)
452c9b4e987SNeel Natu {
453c9b4e987SNeel Natu 	char *dest;
454c9b4e987SNeel Natu 	int msix_entry_offset;
455c9b4e987SNeel Natu 	int tab_index;
456c9b4e987SNeel Natu 	uint64_t retval = ~0;
457c9b4e987SNeel Natu 
4586a52209fSNeel Natu 	/*
4596a52209fSNeel Natu 	 * The PCI standard only allows 4 and 8 byte accesses to the MSI-X
460463a577bSEitan Adler 	 * table but we also allow 1 byte access to accommodate reads from
4616a52209fSNeel Natu 	 * ddb.
4626a52209fSNeel Natu 	 */
4636a52209fSNeel Natu 	if (size != 1 && size != 4 && size != 8)
464c9b4e987SNeel Natu 		return (retval);
465c9b4e987SNeel Natu 
466c9b4e987SNeel Natu 	msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
467c9b4e987SNeel Natu 
468c9b4e987SNeel Natu 	/* support only aligned reads */
469c9b4e987SNeel Natu 	if ((msix_entry_offset % size) != 0) {
470c9b4e987SNeel Natu 		return (retval);
471c9b4e987SNeel Natu 	}
472c9b4e987SNeel Natu 
473c9b4e987SNeel Natu 	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
474c9b4e987SNeel Natu 
475c9b4e987SNeel Natu 	if (tab_index < pi->pi_msix.table_count) {
476c9b4e987SNeel Natu 		/* valid MSI-X Table access */
477c9b4e987SNeel Natu 		dest = (char *)(pi->pi_msix.table + tab_index);
478c9b4e987SNeel Natu 		dest += msix_entry_offset;
479c9b4e987SNeel Natu 
4806a52209fSNeel Natu 		if (size == 1)
4816a52209fSNeel Natu 			retval = *((uint8_t *)dest);
4826a52209fSNeel Natu 		else if (size == 4)
483c9b4e987SNeel Natu 			retval = *((uint32_t *)dest);
484c9b4e987SNeel Natu 		else
485c9b4e987SNeel Natu 			retval = *((uint64_t *)dest);
486c9b4e987SNeel Natu 	} else if (pci_valid_pba_offset(pi, offset)) {
487c9b4e987SNeel Natu 		/* return 0 for PBA access */
488c9b4e987SNeel Natu 		retval = 0;
489c9b4e987SNeel Natu 	}
490c9b4e987SNeel Natu 
491c9b4e987SNeel Natu 	return (retval);
492c9b4e987SNeel Natu }
493c9b4e987SNeel Natu 
494aa12663fSNeel Natu int
pci_msix_table_bar(struct pci_devinst * pi)495aa12663fSNeel Natu pci_msix_table_bar(struct pci_devinst *pi)
496aa12663fSNeel Natu {
497aa12663fSNeel Natu 
498aa12663fSNeel Natu 	if (pi->pi_msix.table != NULL)
499aa12663fSNeel Natu 		return (pi->pi_msix.table_bar);
500aa12663fSNeel Natu 	else
501aa12663fSNeel Natu 		return (-1);
502aa12663fSNeel Natu }
503aa12663fSNeel Natu 
504aa12663fSNeel Natu int
pci_msix_pba_bar(struct pci_devinst * pi)505aa12663fSNeel Natu pci_msix_pba_bar(struct pci_devinst *pi)
506aa12663fSNeel Natu {
507aa12663fSNeel Natu 
508aa12663fSNeel Natu 	if (pi->pi_msix.table != NULL)
509aa12663fSNeel Natu 		return (pi->pi_msix.pba_bar);
510aa12663fSNeel Natu 	else
511aa12663fSNeel Natu 		return (-1);
512aa12663fSNeel Natu }
513aa12663fSNeel Natu 
51431cf78c9SMark Johnston #ifdef __amd64__
515c9b4e987SNeel Natu static int
pci_emul_io_handler(struct vmctx * ctx __unused,int in,int port,int bytes,uint32_t * eax,void * arg)5166a284cacSJohn Baldwin pci_emul_io_handler(struct vmctx *ctx __unused, int in, int port,
51778c2cd83SJohn Baldwin     int bytes, uint32_t *eax, void *arg)
518366f6083SPeter Grehan {
519366f6083SPeter Grehan 	struct pci_devinst *pdi = arg;
520366f6083SPeter Grehan 	struct pci_devemu *pe = pdi->pi_d;
5214d1e669cSPeter Grehan 	uint64_t offset;
5224d1e669cSPeter Grehan 	int i;
523366f6083SPeter Grehan 
524ed721684SMark Johnston 	assert(port >= 0);
525ed721684SMark Johnston 
526366f6083SPeter Grehan 	for (i = 0; i <= PCI_BARMAX; i++) {
527366f6083SPeter Grehan 		if (pdi->pi_bar[i].type == PCIBAR_IO &&
528ed721684SMark Johnston 		    (uint64_t)port >= pdi->pi_bar[i].addr &&
529ed721684SMark Johnston 		    (uint64_t)port + bytes <=
530ed721684SMark Johnston 		    pdi->pi_bar[i].addr + pdi->pi_bar[i].size) {
531366f6083SPeter Grehan 			offset = port - pdi->pi_bar[i].addr;
532366f6083SPeter Grehan 			if (in)
5336a284cacSJohn Baldwin 				*eax = (*pe->pe_barread)(pdi, i,
5344d1e669cSPeter Grehan 							 offset, bytes);
535366f6083SPeter Grehan 			else
5366a284cacSJohn Baldwin 				(*pe->pe_barwrite)(pdi, i, offset,
5374d1e669cSPeter Grehan 						   bytes, *eax);
538366f6083SPeter Grehan 			return (0);
539366f6083SPeter Grehan 		}
540366f6083SPeter Grehan 	}
541366f6083SPeter Grehan 	return (-1);
542366f6083SPeter Grehan }
54331cf78c9SMark Johnston #else
54431cf78c9SMark Johnston static int
pci_emul_iomem_handler(struct vcpu * vcpu __unused,int dir,uint64_t addr,int size,uint64_t * val,void * arg1,long arg2)54531cf78c9SMark Johnston pci_emul_iomem_handler(struct vcpu *vcpu __unused, int dir,
54631cf78c9SMark Johnston     uint64_t addr, int size, uint64_t *val, void *arg1, long arg2)
54731cf78c9SMark Johnston {
54831cf78c9SMark Johnston 	struct pci_devinst *pdi = arg1;
54931cf78c9SMark Johnston 	struct pci_devemu *pe = pdi->pi_d;
55031cf78c9SMark Johnston 	uint64_t offset;
55131cf78c9SMark Johnston 	int bidx = (int)arg2;
55231cf78c9SMark Johnston 
55331cf78c9SMark Johnston 	assert(bidx <= PCI_BARMAX);
55431cf78c9SMark Johnston 	assert(pdi->pi_bar[bidx].type == PCIBAR_IO);
55531cf78c9SMark Johnston 	assert(addr >= pdi->pi_bar[bidx].addr &&
55631cf78c9SMark Johnston 	       addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size);
55731cf78c9SMark Johnston 	assert(size == 1 || size == 2 || size == 4);
55831cf78c9SMark Johnston 
55931cf78c9SMark Johnston 	offset = addr - pdi->pi_bar[bidx].addr;
56031cf78c9SMark Johnston 	if (dir == MEM_F_READ)
56131cf78c9SMark Johnston 		*val = (*pe->pe_barread)(pdi, bidx, offset, size);
56231cf78c9SMark Johnston 	else
56331cf78c9SMark Johnston 		(*pe->pe_barwrite)(pdi, bidx, offset, size, *val);
56431cf78c9SMark Johnston 
56531cf78c9SMark Johnston 	return (0);
56631cf78c9SMark Johnston }
56731cf78c9SMark Johnston #endif /* !__amd64__ */
568366f6083SPeter Grehan 
569366f6083SPeter Grehan static int
pci_emul_mem_handler(struct vcpu * vcpu __unused,int dir,uint64_t addr,int size,uint64_t * val,void * arg1,long arg2)5707d9ef309SJohn Baldwin pci_emul_mem_handler(struct vcpu *vcpu __unused, int dir,
57178c2cd83SJohn Baldwin     uint64_t addr, int size, uint64_t *val, void *arg1, long arg2)
5724d1e669cSPeter Grehan {
5734d1e669cSPeter Grehan 	struct pci_devinst *pdi = arg1;
5744d1e669cSPeter Grehan 	struct pci_devemu *pe = pdi->pi_d;
5754d1e669cSPeter Grehan 	uint64_t offset;
5764d1e669cSPeter Grehan 	int bidx = (int)arg2;
5774d1e669cSPeter Grehan 
5784d1e669cSPeter Grehan 	assert(bidx <= PCI_BARMAX);
5794d1e669cSPeter Grehan 	assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 ||
5804d1e669cSPeter Grehan 	       pdi->pi_bar[bidx].type == PCIBAR_MEM64);
5814d1e669cSPeter Grehan 	assert(addr >= pdi->pi_bar[bidx].addr &&
5824d1e669cSPeter Grehan 	       addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size);
5834d1e669cSPeter Grehan 
5844d1e669cSPeter Grehan 	offset = addr - pdi->pi_bar[bidx].addr;
5854d1e669cSPeter Grehan 
586b6ae8b05STycho Nightingale 	if (dir == MEM_F_WRITE) {
58767b6ffaaSTycho Nightingale 		if (size == 8) {
5886a284cacSJohn Baldwin 			(*pe->pe_barwrite)(pdi, bidx, offset,
589b6ae8b05STycho Nightingale 					   4, *val & 0xffffffff);
5906a284cacSJohn Baldwin 			(*pe->pe_barwrite)(pdi, bidx, offset + 4,
591b6ae8b05STycho Nightingale 					   4, *val >> 32);
592b6ae8b05STycho Nightingale 		} else {
5936a284cacSJohn Baldwin 			(*pe->pe_barwrite)(pdi, bidx, offset,
594b6ae8b05STycho Nightingale 					   size, *val);
595b6ae8b05STycho Nightingale 		}
596b6ae8b05STycho Nightingale 	} else {
59767b6ffaaSTycho Nightingale 		if (size == 8) {
5986a284cacSJohn Baldwin 			*val = (*pe->pe_barread)(pdi, bidx,
599b6ae8b05STycho Nightingale 						 offset, 4);
6006a284cacSJohn Baldwin 			*val |= (*pe->pe_barread)(pdi, bidx,
601b6ae8b05STycho Nightingale 						  offset + 4, 4) << 32;
602b6ae8b05STycho Nightingale 		} else {
6036a284cacSJohn Baldwin 			*val = (*pe->pe_barread)(pdi, bidx,
604b6ae8b05STycho Nightingale 						 offset, size);
605b6ae8b05STycho Nightingale 		}
606b6ae8b05STycho Nightingale 	}
6074d1e669cSPeter Grehan 
6084d1e669cSPeter Grehan 	return (0);
6094d1e669cSPeter Grehan }
6104d1e669cSPeter Grehan 
6114d1e669cSPeter Grehan 
6124d1e669cSPeter Grehan static int
pci_emul_alloc_resource(uint64_t * baseptr,uint64_t limit,uint64_t size,uint64_t * addr)613366f6083SPeter Grehan pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size,
614366f6083SPeter Grehan 			uint64_t *addr)
615366f6083SPeter Grehan {
616366f6083SPeter Grehan 	uint64_t base;
617366f6083SPeter Grehan 
618366f6083SPeter Grehan 	assert((size & (size - 1)) == 0);	/* must be a power of 2 */
619366f6083SPeter Grehan 
620366f6083SPeter Grehan 	base = roundup2(*baseptr, size);
621366f6083SPeter Grehan 
622366f6083SPeter Grehan 	if (base + size <= limit) {
623366f6083SPeter Grehan 		*addr = base;
624366f6083SPeter Grehan 		*baseptr = base + size;
625366f6083SPeter Grehan 		return (0);
626366f6083SPeter Grehan 	} else
627366f6083SPeter Grehan 		return (-1);
628366f6083SPeter Grehan }
629366f6083SPeter Grehan 
630028d9311SNeel Natu /*
631028d9311SNeel Natu  * Register (or unregister) the MMIO or I/O region associated with the BAR
632028d9311SNeel Natu  * register 'idx' of an emulated pci device.
633028d9311SNeel Natu  */
634028d9311SNeel Natu static void
modify_bar_registration(struct pci_devinst * pi,int idx,int registration)635028d9311SNeel Natu modify_bar_registration(struct pci_devinst *pi, int idx, int registration)
636028d9311SNeel Natu {
637f8a6ec2dSD Scott Phillips 	struct pci_devemu *pe;
638028d9311SNeel Natu 	int error;
63931cf78c9SMark Johnston 	enum pcibar_type type;
640028d9311SNeel Natu 
641f8a6ec2dSD Scott Phillips 	pe = pi->pi_d;
64231cf78c9SMark Johnston 	type = pi->pi_bar[idx].type;
64331cf78c9SMark Johnston 	switch (type) {
644028d9311SNeel Natu 	case PCIBAR_IO:
64531cf78c9SMark Johnston 	{
64631cf78c9SMark Johnston #ifdef __amd64__
64731cf78c9SMark Johnston 		struct inout_port iop;
64831cf78c9SMark Johnston 
649028d9311SNeel Natu 		bzero(&iop, sizeof(struct inout_port));
650028d9311SNeel Natu 		iop.name = pi->pi_name;
651028d9311SNeel Natu 		iop.port = pi->pi_bar[idx].addr;
652028d9311SNeel Natu 		iop.size = pi->pi_bar[idx].size;
653028d9311SNeel Natu 		if (registration) {
654028d9311SNeel Natu 			iop.flags = IOPORT_F_INOUT;
655028d9311SNeel Natu 			iop.handler = pci_emul_io_handler;
656028d9311SNeel Natu 			iop.arg = pi;
657028d9311SNeel Natu 			error = register_inout(&iop);
658028d9311SNeel Natu 		} else
659028d9311SNeel Natu 			error = unregister_inout(&iop);
66031cf78c9SMark Johnston #else
66131cf78c9SMark Johnston 		struct mem_range mr;
66231cf78c9SMark Johnston 
66331cf78c9SMark Johnston 		bzero(&mr, sizeof(struct mem_range));
66431cf78c9SMark Johnston 		mr.name = pi->pi_name;
66531cf78c9SMark Johnston 		mr.base = pi->pi_bar[idx].addr;
66631cf78c9SMark Johnston 		mr.size = pi->pi_bar[idx].size;
66731cf78c9SMark Johnston 		if (registration) {
66831cf78c9SMark Johnston 			mr.flags = MEM_F_RW;
66931cf78c9SMark Johnston 			mr.handler = pci_emul_iomem_handler;
67031cf78c9SMark Johnston 			mr.arg1 = pi;
67131cf78c9SMark Johnston 			mr.arg2 = idx;
67231cf78c9SMark Johnston 			error = register_mem(&mr);
67331cf78c9SMark Johnston 		} else
67431cf78c9SMark Johnston 			error = unregister_mem(&mr);
67531cf78c9SMark Johnston #endif
676028d9311SNeel Natu 		break;
67731cf78c9SMark Johnston 	}
678028d9311SNeel Natu 	case PCIBAR_MEM32:
679028d9311SNeel Natu 	case PCIBAR_MEM64:
68031cf78c9SMark Johnston 	{
68131cf78c9SMark Johnston 		struct mem_range mr;
68231cf78c9SMark Johnston 
683028d9311SNeel Natu 		bzero(&mr, sizeof(struct mem_range));
684028d9311SNeel Natu 		mr.name = pi->pi_name;
685028d9311SNeel Natu 		mr.base = pi->pi_bar[idx].addr;
686028d9311SNeel Natu 		mr.size = pi->pi_bar[idx].size;
687028d9311SNeel Natu 		if (registration) {
688028d9311SNeel Natu 			mr.flags = MEM_F_RW;
689028d9311SNeel Natu 			mr.handler = pci_emul_mem_handler;
690028d9311SNeel Natu 			mr.arg1 = pi;
691028d9311SNeel Natu 			mr.arg2 = idx;
692028d9311SNeel Natu 			error = register_mem(&mr);
693028d9311SNeel Natu 		} else
694028d9311SNeel Natu 			error = unregister_mem(&mr);
695028d9311SNeel Natu 		break;
69631cf78c9SMark Johnston 	}
697e47fe318SCorvin Köhne 	case PCIBAR_ROM:
698e47fe318SCorvin Köhne 		error = 0;
699e47fe318SCorvin Köhne 		break;
700028d9311SNeel Natu 	default:
701028d9311SNeel Natu 		error = EINVAL;
702028d9311SNeel Natu 		break;
703028d9311SNeel Natu 	}
704028d9311SNeel Natu 	assert(error == 0);
7050dea4f06SMark Johnston 
7060dea4f06SMark Johnston 	if (pe->pe_baraddr != NULL)
7070dea4f06SMark Johnston 		(*pe->pe_baraddr)(pi, idx, registration, pi->pi_bar[idx].addr);
708028d9311SNeel Natu }
709028d9311SNeel Natu 
710028d9311SNeel Natu static void
unregister_bar(struct pci_devinst * pi,int idx)711028d9311SNeel Natu unregister_bar(struct pci_devinst *pi, int idx)
712028d9311SNeel Natu {
713028d9311SNeel Natu 
714028d9311SNeel Natu 	modify_bar_registration(pi, idx, 0);
715028d9311SNeel Natu }
716028d9311SNeel Natu 
717028d9311SNeel Natu static void
register_bar(struct pci_devinst * pi,int idx)718028d9311SNeel Natu register_bar(struct pci_devinst *pi, int idx)
719028d9311SNeel Natu {
720028d9311SNeel Natu 
721028d9311SNeel Natu 	modify_bar_registration(pi, idx, 1);
722028d9311SNeel Natu }
723028d9311SNeel Natu 
724e47fe318SCorvin Köhne /* Is the ROM enabled for the emulated pci device? */
725e47fe318SCorvin Köhne static int
romen(struct pci_devinst * pi)726e47fe318SCorvin Köhne romen(struct pci_devinst *pi)
727e47fe318SCorvin Köhne {
728e47fe318SCorvin Köhne 	return (pi->pi_bar[PCI_ROM_IDX].lobits & PCIM_BIOS_ENABLE) ==
729e47fe318SCorvin Köhne 	    PCIM_BIOS_ENABLE;
730e47fe318SCorvin Köhne }
731e47fe318SCorvin Köhne 
732028d9311SNeel Natu /* Are we decoding i/o port accesses for the emulated pci device? */
733028d9311SNeel Natu static int
porten(struct pci_devinst * pi)734028d9311SNeel Natu porten(struct pci_devinst *pi)
735028d9311SNeel Natu {
736028d9311SNeel Natu 	uint16_t cmd;
737028d9311SNeel Natu 
738028d9311SNeel Natu 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
739028d9311SNeel Natu 
740028d9311SNeel Natu 	return (cmd & PCIM_CMD_PORTEN);
741028d9311SNeel Natu }
742028d9311SNeel Natu 
743028d9311SNeel Natu /* Are we decoding memory accesses for the emulated pci device? */
744028d9311SNeel Natu static int
memen(struct pci_devinst * pi)745028d9311SNeel Natu memen(struct pci_devinst *pi)
746028d9311SNeel Natu {
747028d9311SNeel Natu 	uint16_t cmd;
748028d9311SNeel Natu 
749028d9311SNeel Natu 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
750028d9311SNeel Natu 
751028d9311SNeel Natu 	return (cmd & PCIM_CMD_MEMEN);
752028d9311SNeel Natu }
753028d9311SNeel Natu 
754028d9311SNeel Natu /*
755028d9311SNeel Natu  * Update the MMIO or I/O address that is decoded by the BAR register.
756028d9311SNeel Natu  *
757028d9311SNeel Natu  * If the pci device has enabled the address space decoding then intercept
758028d9311SNeel Natu  * the address range decoded by the BAR register.
759028d9311SNeel Natu  */
760028d9311SNeel Natu static void
update_bar_address(struct pci_devinst * pi,uint64_t addr,int idx,int type)761028d9311SNeel Natu update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type)
762028d9311SNeel Natu {
763028d9311SNeel Natu 	int decode;
764028d9311SNeel Natu 
765028d9311SNeel Natu 	if (pi->pi_bar[idx].type == PCIBAR_IO)
766028d9311SNeel Natu 		decode = porten(pi);
767028d9311SNeel Natu 	else
768028d9311SNeel Natu 		decode = memen(pi);
769028d9311SNeel Natu 
770028d9311SNeel Natu 	if (decode)
771028d9311SNeel Natu 		unregister_bar(pi, idx);
772028d9311SNeel Natu 
773028d9311SNeel Natu 	switch (type) {
774028d9311SNeel Natu 	case PCIBAR_IO:
775028d9311SNeel Natu 	case PCIBAR_MEM32:
776028d9311SNeel Natu 		pi->pi_bar[idx].addr = addr;
777028d9311SNeel Natu 		break;
778028d9311SNeel Natu 	case PCIBAR_MEM64:
779028d9311SNeel Natu 		pi->pi_bar[idx].addr &= ~0xffffffffUL;
780028d9311SNeel Natu 		pi->pi_bar[idx].addr |= addr;
781028d9311SNeel Natu 		break;
782028d9311SNeel Natu 	case PCIBAR_MEMHI64:
783028d9311SNeel Natu 		pi->pi_bar[idx].addr &= 0xffffffff;
784028d9311SNeel Natu 		pi->pi_bar[idx].addr |= addr;
785028d9311SNeel Natu 		break;
786028d9311SNeel Natu 	default:
787028d9311SNeel Natu 		assert(0);
788028d9311SNeel Natu 	}
789028d9311SNeel Natu 
790028d9311SNeel Natu 	if (decode)
791028d9311SNeel Natu 		register_bar(pi, idx);
792028d9311SNeel Natu }
793028d9311SNeel Natu 
7944d1e669cSPeter Grehan int
pci_emul_alloc_bar(struct pci_devinst * pdi,int idx,enum pcibar_type type,uint64_t size)795038f5c7bSKonstantin Belousov pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type,
796038f5c7bSKonstantin Belousov     uint64_t size)
797366f6083SPeter Grehan {
798e47fe318SCorvin Köhne 	assert((type == PCIBAR_ROM) || (idx >= 0 && idx <= PCI_BARMAX));
799e47fe318SCorvin Köhne 	assert((type != PCIBAR_ROM) || (idx == PCI_ROM_IDX));
800366f6083SPeter Grehan 
801366f6083SPeter Grehan 	if ((size & (size - 1)) != 0)
802366f6083SPeter Grehan 		size = 1UL << flsl(size);	/* round up to a power of 2 */
803366f6083SPeter Grehan 
804028d9311SNeel Natu 	/* Enforce minimum BAR sizes required by the PCI standard */
805028d9311SNeel Natu 	if (type == PCIBAR_IO) {
806028d9311SNeel Natu 		if (size < 4)
807028d9311SNeel Natu 			size = 4;
808e47fe318SCorvin Köhne 	} else if (type == PCIBAR_ROM) {
809e47fe318SCorvin Köhne 		if (size < ~PCIM_BIOS_ADDR_MASK + 1)
810e47fe318SCorvin Köhne 			size = ~PCIM_BIOS_ADDR_MASK + 1;
811028d9311SNeel Natu 	} else {
812028d9311SNeel Natu 		if (size < 16)
813028d9311SNeel Natu 			size = 16;
814028d9311SNeel Natu 	}
815028d9311SNeel Natu 
81601f9362eSCorvin Köhne 	/*
81701f9362eSCorvin Köhne 	 * To reduce fragmentation of the MMIO space, we allocate the BARs by
81801f9362eSCorvin Köhne 	 * size. Therefore, don't allocate the BAR yet. We create a list of all
81901f9362eSCorvin Köhne 	 * BAR allocation which is sorted by BAR size. When all PCI devices are
82001f9362eSCorvin Köhne 	 * initialized, we will assign an address to the BARs.
82101f9362eSCorvin Köhne 	 */
82201f9362eSCorvin Köhne 
82301f9362eSCorvin Köhne 	/* create a new list entry */
82401f9362eSCorvin Köhne 	struct pci_bar_allocation *const new_bar = malloc(sizeof(*new_bar));
82501f9362eSCorvin Köhne 	memset(new_bar, 0, sizeof(*new_bar));
82601f9362eSCorvin Köhne 	new_bar->pdi = pdi;
82701f9362eSCorvin Köhne 	new_bar->idx = idx;
82801f9362eSCorvin Köhne 	new_bar->type = type;
82901f9362eSCorvin Köhne 	new_bar->size = size;
83001f9362eSCorvin Köhne 
83101f9362eSCorvin Köhne 	/*
83201f9362eSCorvin Köhne 	 * Search for a BAR which size is lower than the size of our newly
83301f9362eSCorvin Köhne 	 * allocated BAR.
83401f9362eSCorvin Köhne 	 */
83501f9362eSCorvin Köhne 	struct pci_bar_allocation *bar = NULL;
83601f9362eSCorvin Köhne 	TAILQ_FOREACH(bar, &pci_bars, chain) {
83701f9362eSCorvin Köhne 		if (bar->size < size) {
83801f9362eSCorvin Köhne 			break;
83901f9362eSCorvin Köhne 		}
84001f9362eSCorvin Köhne 	}
84101f9362eSCorvin Köhne 
84201f9362eSCorvin Köhne 	if (bar == NULL) {
84301f9362eSCorvin Köhne 		/*
84401f9362eSCorvin Köhne 		 * Either the list is empty or new BAR is the smallest BAR of
84501f9362eSCorvin Köhne 		 * the list. Append it to the end of our list.
84601f9362eSCorvin Köhne 		 */
84701f9362eSCorvin Köhne 		TAILQ_INSERT_TAIL(&pci_bars, new_bar, chain);
84801f9362eSCorvin Köhne 	} else {
84901f9362eSCorvin Köhne 		/*
85001f9362eSCorvin Köhne 		 * The found BAR is smaller than our new BAR. For that reason,
85101f9362eSCorvin Köhne 		 * insert our new BAR before the found BAR.
85201f9362eSCorvin Köhne 		 */
85301f9362eSCorvin Köhne 		TAILQ_INSERT_BEFORE(bar, new_bar, chain);
85401f9362eSCorvin Köhne 	}
85501f9362eSCorvin Köhne 
85601f9362eSCorvin Köhne 	/*
85701f9362eSCorvin Köhne 	 * pci_passthru devices synchronize their physical and virtual command
85801f9362eSCorvin Köhne 	 * register on init. For that reason, the virtual cmd reg should be
85901f9362eSCorvin Köhne 	 * updated as early as possible.
86001f9362eSCorvin Köhne 	 */
86101f9362eSCorvin Köhne 	uint16_t enbit = 0;
86201f9362eSCorvin Köhne 	switch (type) {
86301f9362eSCorvin Köhne 	case PCIBAR_IO:
86401f9362eSCorvin Köhne 		enbit = PCIM_CMD_PORTEN;
86501f9362eSCorvin Köhne 		break;
86601f9362eSCorvin Köhne 	case PCIBAR_MEM64:
86701f9362eSCorvin Köhne 	case PCIBAR_MEM32:
86801f9362eSCorvin Köhne 		enbit = PCIM_CMD_MEMEN;
86901f9362eSCorvin Köhne 		break;
87001f9362eSCorvin Köhne 	default:
87101f9362eSCorvin Köhne 		enbit = 0;
87201f9362eSCorvin Köhne 		break;
87301f9362eSCorvin Köhne 	}
87401f9362eSCorvin Köhne 
87501f9362eSCorvin Köhne 	const uint16_t cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND);
87601f9362eSCorvin Köhne 	pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit);
87701f9362eSCorvin Köhne 
87801f9362eSCorvin Köhne 	return (0);
87901f9362eSCorvin Köhne }
88001f9362eSCorvin Köhne 
88101f9362eSCorvin Köhne static int
pci_emul_assign_bar(struct pci_devinst * const pdi,const int idx,const enum pcibar_type type,const uint64_t size)88201f9362eSCorvin Köhne pci_emul_assign_bar(struct pci_devinst *const pdi, const int idx,
88301f9362eSCorvin Köhne     const enum pcibar_type type, const uint64_t size)
88401f9362eSCorvin Köhne {
88501f9362eSCorvin Köhne 	int error;
88601f9362eSCorvin Köhne 	uint64_t *baseptr, limit, addr, mask, lobits, bar;
88701f9362eSCorvin Köhne 
888366f6083SPeter Grehan 	switch (type) {
889366f6083SPeter Grehan 	case PCIBAR_NONE:
890366f6083SPeter Grehan 		baseptr = NULL;
89101f9362eSCorvin Köhne 		addr = mask = lobits = 0;
892366f6083SPeter Grehan 		break;
893366f6083SPeter Grehan 	case PCIBAR_IO:
894366f6083SPeter Grehan 		baseptr = &pci_emul_iobase;
895366f6083SPeter Grehan 		limit = PCI_EMUL_IOLIMIT;
896366f6083SPeter Grehan 		mask = PCIM_BAR_IO_BASE;
897366f6083SPeter Grehan 		lobits = PCIM_BAR_IO_SPACE;
898366f6083SPeter Grehan 		break;
899366f6083SPeter Grehan 	case PCIBAR_MEM64:
900366f6083SPeter Grehan 		/*
901366f6083SPeter Grehan 		 * XXX
902366f6083SPeter Grehan 		 * Some drivers do not work well if the 64-bit BAR is allocated
903366f6083SPeter Grehan 		 * above 4GB. Allow for this by allocating small requests under
904366f6083SPeter Grehan 		 * 4GB unless then allocation size is larger than some arbitrary
905670b364bSKonstantin Belousov 		 * number (128MB currently).
906366f6083SPeter Grehan 		 */
907670b364bSKonstantin Belousov 		if (size > 128 * 1024 * 1024) {
908366f6083SPeter Grehan 			baseptr = &pci_emul_membase64;
9099922872bSKonstantin Belousov 			limit = pci_emul_memlim64;
910366f6083SPeter Grehan 			mask = PCIM_BAR_MEM_BASE;
911366f6083SPeter Grehan 			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
912366f6083SPeter Grehan 				 PCIM_BAR_MEM_PREFETCH;
91325d4944eSNeel Natu 		} else {
91425d4944eSNeel Natu 			baseptr = &pci_emul_membase32;
91525d4944eSNeel Natu 			limit = PCI_EMUL_MEMLIMIT32;
91625d4944eSNeel Natu 			mask = PCIM_BAR_MEM_BASE;
91725d4944eSNeel Natu 			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
918366f6083SPeter Grehan 		}
91925d4944eSNeel Natu 		break;
920366f6083SPeter Grehan 	case PCIBAR_MEM32:
921366f6083SPeter Grehan 		baseptr = &pci_emul_membase32;
922366f6083SPeter Grehan 		limit = PCI_EMUL_MEMLIMIT32;
923366f6083SPeter Grehan 		mask = PCIM_BAR_MEM_BASE;
924366f6083SPeter Grehan 		lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
925366f6083SPeter Grehan 		break;
926e47fe318SCorvin Köhne 	case PCIBAR_ROM:
927e47fe318SCorvin Köhne 		/* do not claim memory for ROM. OVMF will do it for us. */
928e47fe318SCorvin Köhne 		baseptr = NULL;
929e47fe318SCorvin Köhne 		limit = 0;
930e47fe318SCorvin Köhne 		mask = PCIM_BIOS_ADDR_MASK;
931e47fe318SCorvin Köhne 		lobits = 0;
932e47fe318SCorvin Köhne 		break;
933366f6083SPeter Grehan 	default:
934366f6083SPeter Grehan 		printf("pci_emul_alloc_base: invalid bar type %d\n", type);
935366f6083SPeter Grehan 		assert(0);
936366f6083SPeter Grehan 	}
937366f6083SPeter Grehan 
938366f6083SPeter Grehan 	if (baseptr != NULL) {
939366f6083SPeter Grehan 		error = pci_emul_alloc_resource(baseptr, limit, size, &addr);
940366f6083SPeter Grehan 		if (error != 0)
941366f6083SPeter Grehan 			return (error);
9428ac8addaSCorvin Köhne 	} else {
9438ac8addaSCorvin Köhne 		addr = 0;
944366f6083SPeter Grehan 	}
945366f6083SPeter Grehan 
946366f6083SPeter Grehan 	pdi->pi_bar[idx].type = type;
947366f6083SPeter Grehan 	pdi->pi_bar[idx].addr = addr;
948366f6083SPeter Grehan 	pdi->pi_bar[idx].size = size;
949e87a6f3eSCorvin Köhne 	/*
950e87a6f3eSCorvin Köhne 	 * passthru devices are using same lobits as physical device they set
951e87a6f3eSCorvin Köhne 	 * this property
952e87a6f3eSCorvin Köhne 	 */
953e87a6f3eSCorvin Köhne 	if (pdi->pi_bar[idx].lobits != 0) {
954e87a6f3eSCorvin Köhne 		lobits = pdi->pi_bar[idx].lobits;
955e87a6f3eSCorvin Köhne 	} else {
956e87a6f3eSCorvin Köhne 		pdi->pi_bar[idx].lobits = lobits;
957e87a6f3eSCorvin Köhne 	}
958366f6083SPeter Grehan 
959366f6083SPeter Grehan 	/* Initialize the BAR register in config space */
960366f6083SPeter Grehan 	bar = (addr & mask) | lobits;
961366f6083SPeter Grehan 	pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar);
962366f6083SPeter Grehan 
963366f6083SPeter Grehan 	if (type == PCIBAR_MEM64) {
964366f6083SPeter Grehan 		assert(idx + 1 <= PCI_BARMAX);
965366f6083SPeter Grehan 		pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64;
966366f6083SPeter Grehan 		pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
967366f6083SPeter Grehan 	}
968366f6083SPeter Grehan 
969e47fe318SCorvin Köhne 	if (type != PCIBAR_ROM) {
970028d9311SNeel Natu 		register_bar(pdi, idx);
971e47fe318SCorvin Köhne 	}
972e47fe318SCorvin Köhne 
973e47fe318SCorvin Köhne 	return (0);
974e47fe318SCorvin Köhne }
975e47fe318SCorvin Köhne 
976e47fe318SCorvin Köhne int
pci_emul_alloc_rom(struct pci_devinst * const pdi,const uint64_t size,void ** const addr)977e47fe318SCorvin Köhne pci_emul_alloc_rom(struct pci_devinst *const pdi, const uint64_t size,
978e47fe318SCorvin Köhne     void **const addr)
979e47fe318SCorvin Köhne {
980e47fe318SCorvin Köhne 	/* allocate ROM space once on first call */
981e47fe318SCorvin Köhne 	if (pci_emul_rombase == 0) {
982e47fe318SCorvin Köhne 		pci_emul_rombase = vm_create_devmem(pdi->pi_vmctx, VM_PCIROM,
983e47fe318SCorvin Köhne 		    "pcirom", PCI_EMUL_ROMSIZE);
984e47fe318SCorvin Köhne 		if (pci_emul_rombase == MAP_FAILED) {
985e47fe318SCorvin Köhne 			warnx("%s: failed to create rom segment", __func__);
986e47fe318SCorvin Köhne 			return (-1);
987e47fe318SCorvin Köhne 		}
988e47fe318SCorvin Köhne 		pci_emul_romlim = pci_emul_rombase + PCI_EMUL_ROMSIZE;
989e47fe318SCorvin Köhne 		pci_emul_romoffset = 0;
990e47fe318SCorvin Köhne 	}
991e47fe318SCorvin Köhne 
992e47fe318SCorvin Köhne 	/* ROM size should be a power of 2 and greater than 2 KB */
993e47fe318SCorvin Köhne 	const uint64_t rom_size = MAX(1UL << flsl(size),
994e47fe318SCorvin Köhne 	    ~PCIM_BIOS_ADDR_MASK + 1);
995e47fe318SCorvin Köhne 
996e47fe318SCorvin Köhne 	/* check if ROM fits into ROM space */
997e47fe318SCorvin Köhne 	if (pci_emul_romoffset + rom_size > PCI_EMUL_ROMSIZE) {
998e47fe318SCorvin Köhne 		warnx("%s: no space left in rom segment:", __func__);
999e47fe318SCorvin Köhne 		warnx("%16lu bytes left",
1000e47fe318SCorvin Köhne 		    PCI_EMUL_ROMSIZE - pci_emul_romoffset);
1001e47fe318SCorvin Köhne 		warnx("%16lu bytes required by %d/%d/%d", rom_size, pdi->pi_bus,
1002e47fe318SCorvin Köhne 		    pdi->pi_slot, pdi->pi_func);
1003e47fe318SCorvin Köhne 		return (-1);
1004e47fe318SCorvin Köhne 	}
1005e47fe318SCorvin Köhne 
1006e47fe318SCorvin Köhne 	/* allocate ROM BAR */
1007e47fe318SCorvin Köhne 	const int error = pci_emul_alloc_bar(pdi, PCI_ROM_IDX, PCIBAR_ROM,
1008e47fe318SCorvin Köhne 	    rom_size);
1009e47fe318SCorvin Köhne 	if (error)
1010e47fe318SCorvin Köhne 		return error;
1011e47fe318SCorvin Köhne 
1012e47fe318SCorvin Köhne 	/* return address */
1013e47fe318SCorvin Köhne 	*addr = pci_emul_rombase + pci_emul_romoffset;
1014e47fe318SCorvin Köhne 
1015e47fe318SCorvin Köhne 	/* save offset into ROM Space */
1016e47fe318SCorvin Köhne 	pdi->pi_romoffset = pci_emul_romoffset;
1017e47fe318SCorvin Köhne 
1018e47fe318SCorvin Köhne 	/* increase offset for next ROM */
1019e47fe318SCorvin Köhne 	pci_emul_romoffset += rom_size;
1020366f6083SPeter Grehan 
1021366f6083SPeter Grehan 	return (0);
1022366f6083SPeter Grehan }
1023366f6083SPeter Grehan 
10246632a0a4SCorvin Köhne int
pci_emul_add_boot_device(struct pci_devinst * pi,int bootindex)10256632a0a4SCorvin Köhne pci_emul_add_boot_device(struct pci_devinst *pi, int bootindex)
10266632a0a4SCorvin Köhne {
10276632a0a4SCorvin Köhne 	struct boot_device *new_device, *device;
10286632a0a4SCorvin Köhne 
10296632a0a4SCorvin Köhne 	/* don't permit a negative bootindex */
10306632a0a4SCorvin Köhne 	if (bootindex < 0) {
10316632a0a4SCorvin Köhne 		errx(4, "Invalid bootindex %d for %s", bootindex, pi->pi_name);
10326632a0a4SCorvin Köhne 	}
10336632a0a4SCorvin Köhne 
10346632a0a4SCorvin Köhne 	/* alloc new boot device */
10356632a0a4SCorvin Köhne 	new_device = calloc(1, sizeof(struct boot_device));
10366632a0a4SCorvin Köhne 	if (new_device == NULL) {
10376632a0a4SCorvin Köhne 		return (ENOMEM);
10386632a0a4SCorvin Köhne 	}
10396632a0a4SCorvin Köhne 	new_device->pdi = pi;
10406632a0a4SCorvin Köhne 	new_device->bootindex = bootindex;
10416632a0a4SCorvin Köhne 
10426632a0a4SCorvin Köhne 	/* search for boot device with higher boot index */
10436632a0a4SCorvin Köhne 	TAILQ_FOREACH(device, &boot_devices, boot_device_chain) {
10446632a0a4SCorvin Köhne 		if (device->bootindex == bootindex) {
10456632a0a4SCorvin Köhne 			errx(4,
10466632a0a4SCorvin Köhne 			    "Could not set bootindex %d for %s. Bootindex already occupied by %s",
10476632a0a4SCorvin Köhne 			    bootindex, pi->pi_name, device->pdi->pi_name);
10486632a0a4SCorvin Köhne 		} else if (device->bootindex > bootindex) {
10496632a0a4SCorvin Köhne 			break;
10506632a0a4SCorvin Köhne 		}
10516632a0a4SCorvin Köhne 	}
10526632a0a4SCorvin Köhne 
10536632a0a4SCorvin Köhne 	/* add boot device to queue */
10546632a0a4SCorvin Köhne 	if (device == NULL) {
10556632a0a4SCorvin Köhne 		TAILQ_INSERT_TAIL(&boot_devices, new_device, boot_device_chain);
10566632a0a4SCorvin Köhne 	} else {
10576632a0a4SCorvin Köhne 		TAILQ_INSERT_BEFORE(device, new_device, boot_device_chain);
10586632a0a4SCorvin Köhne 	}
10596632a0a4SCorvin Köhne 
10606632a0a4SCorvin Köhne 	return (0);
10616632a0a4SCorvin Köhne }
10626632a0a4SCorvin Köhne 
1063366f6083SPeter Grehan #define	CAP_START_OFFSET	0x40
1064366f6083SPeter Grehan static int
pci_emul_add_capability(struct pci_devinst * pi,u_char * capdata,int caplen)1065366f6083SPeter Grehan pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen)
1066366f6083SPeter Grehan {
1067a96b8b80SJohn Baldwin 	int i, capoff, reallen;
1068366f6083SPeter Grehan 	uint16_t sts;
1069366f6083SPeter Grehan 
1070a96b8b80SJohn Baldwin 	assert(caplen > 0);
1071366f6083SPeter Grehan 
1072366f6083SPeter Grehan 	reallen = roundup2(caplen, 4);		/* dword aligned */
1073366f6083SPeter Grehan 
1074366f6083SPeter Grehan 	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
1075a96b8b80SJohn Baldwin 	if ((sts & PCIM_STATUS_CAPPRESENT) == 0)
1076366f6083SPeter Grehan 		capoff = CAP_START_OFFSET;
1077a96b8b80SJohn Baldwin 	else
1078a96b8b80SJohn Baldwin 		capoff = pi->pi_capend + 1;
1079366f6083SPeter Grehan 
1080366f6083SPeter Grehan 	/* Check if we have enough space */
1081a96b8b80SJohn Baldwin 	if (capoff + reallen > PCI_REGMAX + 1)
1082366f6083SPeter Grehan 		return (-1);
1083366f6083SPeter Grehan 
1084a96b8b80SJohn Baldwin 	/* Set the previous capability pointer */
1085a96b8b80SJohn Baldwin 	if ((sts & PCIM_STATUS_CAPPRESENT) == 0) {
1086a96b8b80SJohn Baldwin 		pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff);
1087a96b8b80SJohn Baldwin 		pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT);
1088a96b8b80SJohn Baldwin 	} else
1089a96b8b80SJohn Baldwin 		pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff);
1090a96b8b80SJohn Baldwin 
1091366f6083SPeter Grehan 	/* Copy the capability */
1092366f6083SPeter Grehan 	for (i = 0; i < caplen; i++)
1093366f6083SPeter Grehan 		pci_set_cfgdata8(pi, capoff + i, capdata[i]);
1094366f6083SPeter Grehan 
1095366f6083SPeter Grehan 	/* Set the next capability pointer */
1096a96b8b80SJohn Baldwin 	pci_set_cfgdata8(pi, capoff + 1, 0);
1097366f6083SPeter Grehan 
1098a96b8b80SJohn Baldwin 	pi->pi_prevcap = capoff;
1099a96b8b80SJohn Baldwin 	pi->pi_capend = capoff + reallen - 1;
1100366f6083SPeter Grehan 	return (0);
1101366f6083SPeter Grehan }
1102366f6083SPeter Grehan 
1103366f6083SPeter Grehan static struct pci_devemu *
pci_emul_finddev(const char * name)1104621b5090SJohn Baldwin pci_emul_finddev(const char *name)
1105366f6083SPeter Grehan {
1106366f6083SPeter Grehan 	struct pci_devemu **pdpp, *pdp;
1107366f6083SPeter Grehan 
1108366f6083SPeter Grehan 	SET_FOREACH(pdpp, pci_devemu_set) {
1109366f6083SPeter Grehan 		pdp = *pdpp;
1110366f6083SPeter Grehan 		if (!strcmp(pdp->pe_emu, name)) {
1111366f6083SPeter Grehan 			return (pdp);
1112366f6083SPeter Grehan 		}
1113366f6083SPeter Grehan 	}
1114366f6083SPeter Grehan 
1115366f6083SPeter Grehan 	return (NULL);
1116366f6083SPeter Grehan }
1117366f6083SPeter Grehan 
1118a38e2a64SPeter Grehan static int
pci_emul_init(struct vmctx * ctx,struct pci_devemu * pde,int bus,int slot,int func,struct funcinfo * fi)1119d84882caSNeel Natu pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot,
1120d84882caSNeel Natu     int func, struct funcinfo *fi)
1121366f6083SPeter Grehan {
1122366f6083SPeter Grehan 	struct pci_devinst *pdi;
1123a38e2a64SPeter Grehan 	int err;
1124a38e2a64SPeter Grehan 
1125994f858aSXin LI 	pdi = calloc(1, sizeof(struct pci_devinst));
1126366f6083SPeter Grehan 
1127366f6083SPeter Grehan 	pdi->pi_vmctx = ctx;
1128d84882caSNeel Natu 	pdi->pi_bus = bus;
1129366f6083SPeter Grehan 	pdi->pi_slot = slot;
113099d65389SNeel Natu 	pdi->pi_func = func;
11313cbf3585SJohn Baldwin 	pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
11323cbf3585SJohn Baldwin 	pdi->pi_lintr.pin = 0;
11333cbf3585SJohn Baldwin 	pdi->pi_lintr.state = IDLE;
11340efad4acSJessica Clarke 	pci_irq_init_irq(&pdi->pi_lintr.irq);
1135366f6083SPeter Grehan 	pdi->pi_d = pde;
113614c80457SVitaliy Gusev 	snprintf(pdi->pi_name, PI_NAMESZ, "%s@pci.%d.%d.%d", pde->pe_emu, bus,
113714c80457SVitaliy Gusev 	    slot, func);
1138366f6083SPeter Grehan 
1139366f6083SPeter Grehan 	/* Disable legacy interrupts */
1140366f6083SPeter Grehan 	pci_set_cfgdata8(pdi, PCIR_INTLINE, 255);
1141366f6083SPeter Grehan 	pci_set_cfgdata8(pdi, PCIR_INTPIN, 0);
1142366f6083SPeter Grehan 
11432729c9bbSJohn Baldwin 	pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN);
1144366f6083SPeter Grehan 
11456a284cacSJohn Baldwin 	err = (*pde->pe_init)(pdi, fi->fi_config);
1146d84882caSNeel Natu 	if (err == 0)
1147d84882caSNeel Natu 		fi->fi_devi = pdi;
1148d84882caSNeel Natu 	else
1149366f6083SPeter Grehan 		free(pdi);
1150a38e2a64SPeter Grehan 
1151a38e2a64SPeter Grehan 	return (err);
1152366f6083SPeter Grehan }
1153366f6083SPeter Grehan 
1154366f6083SPeter Grehan void
pci_populate_msicap(struct msicap * msicap,int msgnum,int nextptr)1155366f6083SPeter Grehan pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr)
1156366f6083SPeter Grehan {
1157366f6083SPeter Grehan 	int mmc;
1158366f6083SPeter Grehan 
1159366f6083SPeter Grehan 	/* Number of msi messages must be a power of 2 between 1 and 32 */
1160366f6083SPeter Grehan 	assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32);
1161366f6083SPeter Grehan 	mmc = ffs(msgnum) - 1;
1162366f6083SPeter Grehan 
1163366f6083SPeter Grehan 	bzero(msicap, sizeof(struct msicap));
1164366f6083SPeter Grehan 	msicap->capid = PCIY_MSI;
1165366f6083SPeter Grehan 	msicap->nextptr = nextptr;
1166366f6083SPeter Grehan 	msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1);
1167366f6083SPeter Grehan }
1168366f6083SPeter Grehan 
1169366f6083SPeter Grehan int
pci_emul_add_msicap(struct pci_devinst * pi,int msgnum)1170366f6083SPeter Grehan pci_emul_add_msicap(struct pci_devinst *pi, int msgnum)
1171366f6083SPeter Grehan {
1172366f6083SPeter Grehan 	struct msicap msicap;
1173366f6083SPeter Grehan 
1174366f6083SPeter Grehan 	pci_populate_msicap(&msicap, msgnum, 0);
1175366f6083SPeter Grehan 
1176366f6083SPeter Grehan 	return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap)));
1177366f6083SPeter Grehan }
1178366f6083SPeter Grehan 
1179c9b4e987SNeel Natu static void
pci_populate_msixcap(struct msixcap * msixcap,int msgnum,int barnum,uint32_t msix_tab_size)1180c9b4e987SNeel Natu pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum,
1181a96b8b80SJohn Baldwin 		     uint32_t msix_tab_size)
1182c9b4e987SNeel Natu {
1183c9b4e987SNeel Natu 
1184c9b4e987SNeel Natu 	assert(msix_tab_size % 4096 == 0);
1185c9b4e987SNeel Natu 
1186c9b4e987SNeel Natu 	bzero(msixcap, sizeof(struct msixcap));
1187c9b4e987SNeel Natu 	msixcap->capid = PCIY_MSIX;
1188c9b4e987SNeel Natu 
1189c9b4e987SNeel Natu 	/*
1190c9b4e987SNeel Natu 	 * Message Control Register, all fields set to
1191c9b4e987SNeel Natu 	 * zero except for the Table Size.
1192c9b4e987SNeel Natu 	 * Note: Table size N is encoded as N-1
1193c9b4e987SNeel Natu 	 */
1194c9b4e987SNeel Natu 	msixcap->msgctrl = msgnum - 1;
1195c9b4e987SNeel Natu 
1196c9b4e987SNeel Natu 	/*
1197c9b4e987SNeel Natu 	 * MSI-X BAR setup:
1198c9b4e987SNeel Natu 	 * - MSI-X table start at offset 0
1199c9b4e987SNeel Natu 	 * - PBA table starts at a 4K aligned offset after the MSI-X table
1200c9b4e987SNeel Natu 	 */
1201c9b4e987SNeel Natu 	msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK;
1202c9b4e987SNeel Natu 	msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK);
1203c9b4e987SNeel Natu }
1204c9b4e987SNeel Natu 
1205c9b4e987SNeel Natu static void
pci_msix_table_init(struct pci_devinst * pi,int table_entries)1206c9b4e987SNeel Natu pci_msix_table_init(struct pci_devinst *pi, int table_entries)
1207c9b4e987SNeel Natu {
1208c9b4e987SNeel Natu 	int i, table_size;
1209c9b4e987SNeel Natu 
1210c9b4e987SNeel Natu 	assert(table_entries > 0);
1211c9b4e987SNeel Natu 	assert(table_entries <= MAX_MSIX_TABLE_ENTRIES);
1212c9b4e987SNeel Natu 
1213c9b4e987SNeel Natu 	table_size = table_entries * MSIX_TABLE_ENTRY_SIZE;
1214994f858aSXin LI 	pi->pi_msix.table = calloc(1, table_size);
1215c9b4e987SNeel Natu 
1216c9b4e987SNeel Natu 	/* set mask bit of vector control register */
1217c9b4e987SNeel Natu 	for (i = 0; i < table_entries; i++)
1218c9b4e987SNeel Natu 		pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK;
1219c9b4e987SNeel Natu }
1220c9b4e987SNeel Natu 
1221c9b4e987SNeel Natu int
pci_emul_add_msixcap(struct pci_devinst * pi,int msgnum,int barnum)1222c9b4e987SNeel Natu pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum)
1223c9b4e987SNeel Natu {
1224c9b4e987SNeel Natu 	uint32_t tab_size;
1225c9b4e987SNeel Natu 	struct msixcap msixcap;
1226c9b4e987SNeel Natu 
1227c9b4e987SNeel Natu 	assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES);
1228c9b4e987SNeel Natu 	assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0);
1229c9b4e987SNeel Natu 
1230c9b4e987SNeel Natu 	tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE;
1231c9b4e987SNeel Natu 
1232c9b4e987SNeel Natu 	/* Align table size to nearest 4K */
1233c9b4e987SNeel Natu 	tab_size = roundup2(tab_size, 4096);
1234c9b4e987SNeel Natu 
1235c9b4e987SNeel Natu 	pi->pi_msix.table_bar = barnum;
1236c9b4e987SNeel Natu 	pi->pi_msix.pba_bar   = barnum;
1237c9b4e987SNeel Natu 	pi->pi_msix.table_offset = 0;
1238c9b4e987SNeel Natu 	pi->pi_msix.table_count = msgnum;
1239c9b4e987SNeel Natu 	pi->pi_msix.pba_offset = tab_size;
12407a902ec0SNeel Natu 	pi->pi_msix.pba_size = PBA_SIZE(msgnum);
1241c9b4e987SNeel Natu 
1242c9b4e987SNeel Natu 	pci_msix_table_init(pi, msgnum);
1243c9b4e987SNeel Natu 
1244a96b8b80SJohn Baldwin 	pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size);
1245c9b4e987SNeel Natu 
1246c9b4e987SNeel Natu 	/* allocate memory for MSI-X Table and PBA */
1247c9b4e987SNeel Natu 	pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32,
1248c9b4e987SNeel Natu 				tab_size + pi->pi_msix.pba_size);
1249c9b4e987SNeel Natu 
1250c9b4e987SNeel Natu 	return (pci_emul_add_capability(pi, (u_char *)&msixcap,
1251c9b4e987SNeel Natu 					sizeof(msixcap)));
1252c9b4e987SNeel Natu }
1253c9b4e987SNeel Natu 
125421368498SPeter Grehan static void
msixcap_cfgwrite(struct pci_devinst * pi,int capoff,int offset,int bytes,uint32_t val)1255cd942e0fSPeter Grehan msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
1256cd942e0fSPeter Grehan 		 int bytes, uint32_t val)
1257cd942e0fSPeter Grehan {
1258cd942e0fSPeter Grehan 	uint16_t msgctrl, rwmask;
1259d74fdc6aSMarcelo Araujo 	int off;
1260cd942e0fSPeter Grehan 
1261cd942e0fSPeter Grehan 	off = offset - capoff;
1262cd942e0fSPeter Grehan 	/* Message Control Register */
1263cd942e0fSPeter Grehan 	if (off == 2 && bytes == 2) {
1264cd942e0fSPeter Grehan 		rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK;
1265cd942e0fSPeter Grehan 		msgctrl = pci_get_cfgdata16(pi, offset);
1266cd942e0fSPeter Grehan 		msgctrl &= ~rwmask;
1267cd942e0fSPeter Grehan 		msgctrl |= val & rwmask;
1268cd942e0fSPeter Grehan 		val = msgctrl;
1269cd942e0fSPeter Grehan 
1270cd942e0fSPeter Grehan 		pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE;
1271c9b4e987SNeel Natu 		pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK;
12723cbf3585SJohn Baldwin 		pci_lintr_update(pi);
1273cd942e0fSPeter Grehan 	}
1274cd942e0fSPeter Grehan 
1275cd942e0fSPeter Grehan 	CFGWRITE(pi, offset, val, bytes);
1276cd942e0fSPeter Grehan }
1277cd942e0fSPeter Grehan 
127821368498SPeter Grehan static void
msicap_cfgwrite(struct pci_devinst * pi,int capoff,int offset,int bytes,uint32_t val)1279366f6083SPeter Grehan msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
1280366f6083SPeter Grehan 		int bytes, uint32_t val)
1281366f6083SPeter Grehan {
1282366f6083SPeter Grehan 	uint16_t msgctrl, rwmask, msgdata, mme;
1283366f6083SPeter Grehan 	uint32_t addrlo;
1284366f6083SPeter Grehan 
1285366f6083SPeter Grehan 	/*
1286366f6083SPeter Grehan 	 * If guest is writing to the message control register make sure
1287366f6083SPeter Grehan 	 * we do not overwrite read-only fields.
1288366f6083SPeter Grehan 	 */
1289366f6083SPeter Grehan 	if ((offset - capoff) == 2 && bytes == 2) {
1290366f6083SPeter Grehan 		rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE;
1291366f6083SPeter Grehan 		msgctrl = pci_get_cfgdata16(pi, offset);
1292366f6083SPeter Grehan 		msgctrl &= ~rwmask;
1293366f6083SPeter Grehan 		msgctrl |= val & rwmask;
1294366f6083SPeter Grehan 		val = msgctrl;
12957840d1c4SJohn Baldwin 	}
12967840d1c4SJohn Baldwin 	CFGWRITE(pi, offset, val, bytes);
1297366f6083SPeter Grehan 
12987840d1c4SJohn Baldwin 	msgctrl = pci_get_cfgdata16(pi, capoff + 2);
1299366f6083SPeter Grehan 	addrlo = pci_get_cfgdata32(pi, capoff + 4);
1300366f6083SPeter Grehan 	if (msgctrl & PCIM_MSICTRL_64BIT)
1301366f6083SPeter Grehan 		msgdata = pci_get_cfgdata16(pi, capoff + 12);
1302366f6083SPeter Grehan 	else
1303366f6083SPeter Grehan 		msgdata = pci_get_cfgdata16(pi, capoff + 8);
1304366f6083SPeter Grehan 
1305366f6083SPeter Grehan 	mme = msgctrl & PCIM_MSICTRL_MME_MASK;
1306366f6083SPeter Grehan 	pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0;
1307366f6083SPeter Grehan 	if (pi->pi_msi.enabled) {
13084f8be175SNeel Natu 		pi->pi_msi.addr = addrlo;
13094f8be175SNeel Natu 		pi->pi_msi.msg_data = msgdata;
13104f8be175SNeel Natu 		pi->pi_msi.maxmsgnum = 1 << (mme >> 4);
1311366f6083SPeter Grehan 	} else {
13124f8be175SNeel Natu 		pi->pi_msi.maxmsgnum = 0;
1313366f6083SPeter Grehan 	}
13143cbf3585SJohn Baldwin 	pci_lintr_update(pi);
1315366f6083SPeter Grehan }
1316366f6083SPeter Grehan 
131798d920d9SMark Johnston static void
pciecap_cfgwrite(struct pci_devinst * pi,int capoff __unused,int offset,int bytes,uint32_t val)131898d920d9SMark Johnston pciecap_cfgwrite(struct pci_devinst *pi, int capoff __unused, int offset,
131974f80b23SNeel Natu     int bytes, uint32_t val)
132074f80b23SNeel Natu {
132174f80b23SNeel Natu 
132274f80b23SNeel Natu 	/* XXX don't write to the readonly parts */
132374f80b23SNeel Natu 	CFGWRITE(pi, offset, val, bytes);
132474f80b23SNeel Natu }
132574f80b23SNeel Natu 
132674f80b23SNeel Natu #define	PCIECAP_VERSION	0x2
132774f80b23SNeel Natu int
pci_emul_add_pciecap(struct pci_devinst * pi,int type)132874f80b23SNeel Natu pci_emul_add_pciecap(struct pci_devinst *pi, int type)
132974f80b23SNeel Natu {
133074f80b23SNeel Natu 	int err;
133174f80b23SNeel Natu 	struct pciecap pciecap;
133274f80b23SNeel Natu 
133374f80b23SNeel Natu 	bzero(&pciecap, sizeof(pciecap));
133474f80b23SNeel Natu 
1335129f93c5SChuck Tuffli 	/*
1336129f93c5SChuck Tuffli 	 * Use the integrated endpoint type for endpoints on a root complex bus.
1337129f93c5SChuck Tuffli 	 *
1338129f93c5SChuck Tuffli 	 * NB: bhyve currently only supports a single PCI bus that is the root
1339129f93c5SChuck Tuffli 	 * complex bus, so all endpoints are integrated.
1340129f93c5SChuck Tuffli 	 */
1341129f93c5SChuck Tuffli 	if ((type == PCIEM_TYPE_ENDPOINT) && (pi->pi_bus == 0))
1342129f93c5SChuck Tuffli 		type = PCIEM_TYPE_ROOT_INT_EP;
1343129f93c5SChuck Tuffli 
134474f80b23SNeel Natu 	pciecap.capid = PCIY_EXPRESS;
1345129f93c5SChuck Tuffli 	pciecap.pcie_capabilities = PCIECAP_VERSION | type;
1346129f93c5SChuck Tuffli 	if (type != PCIEM_TYPE_ROOT_INT_EP) {
134774f80b23SNeel Natu 		pciecap.link_capabilities = 0x411;	/* gen1, x1 */
134874f80b23SNeel Natu 		pciecap.link_status = 0x11;		/* gen1, x1 */
1349129f93c5SChuck Tuffli 	}
135074f80b23SNeel Natu 
135174f80b23SNeel Natu 	err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap));
135274f80b23SNeel Natu 	return (err);
135374f80b23SNeel Natu }
135474f80b23SNeel Natu 
1355366f6083SPeter Grehan /*
1356366f6083SPeter Grehan  * This function assumes that 'coff' is in the capabilities region of the
135721368498SPeter Grehan  * config space. A capoff parameter of zero will force a search for the
135821368498SPeter Grehan  * offset and type.
1359366f6083SPeter Grehan  */
136021368498SPeter Grehan void
pci_emul_capwrite(struct pci_devinst * pi,int offset,int bytes,uint32_t val,uint8_t capoff,int capid)136121368498SPeter Grehan pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val,
136221368498SPeter Grehan     uint8_t capoff, int capid)
1363366f6083SPeter Grehan {
136421368498SPeter Grehan 	uint8_t nextoff;
1365366f6083SPeter Grehan 
1366366f6083SPeter Grehan 	/* Do not allow un-aligned writes */
1367366f6083SPeter Grehan 	if ((offset & (bytes - 1)) != 0)
1368366f6083SPeter Grehan 		return;
1369366f6083SPeter Grehan 
137021368498SPeter Grehan 	if (capoff == 0) {
1371366f6083SPeter Grehan 		/* Find the capability that we want to update */
1372366f6083SPeter Grehan 		capoff = CAP_START_OFFSET;
1373366f6083SPeter Grehan 		while (1) {
1374366f6083SPeter Grehan 			nextoff = pci_get_cfgdata8(pi, capoff + 1);
1375a96b8b80SJohn Baldwin 			if (nextoff == 0)
1376a96b8b80SJohn Baldwin 				break;
1377366f6083SPeter Grehan 			if (offset >= capoff && offset < nextoff)
1378366f6083SPeter Grehan 				break;
1379366f6083SPeter Grehan 
1380366f6083SPeter Grehan 			capoff = nextoff;
1381366f6083SPeter Grehan 		}
1382366f6083SPeter Grehan 		assert(offset >= capoff);
138321368498SPeter Grehan 		capid = pci_get_cfgdata8(pi, capoff);
138421368498SPeter Grehan 	}
1385366f6083SPeter Grehan 
1386366f6083SPeter Grehan 	/*
13872a8d400aSPeter Grehan 	 * Capability ID and Next Capability Pointer are readonly.
13882a8d400aSPeter Grehan 	 * However, some o/s's do 4-byte writes that include these.
13892a8d400aSPeter Grehan 	 * For this case, trim the write back to 2 bytes and adjust
13902a8d400aSPeter Grehan 	 * the data.
1391366f6083SPeter Grehan 	 */
13922a8d400aSPeter Grehan 	if (offset == capoff || offset == capoff + 1) {
13932a8d400aSPeter Grehan 		if (offset == capoff && bytes == 4) {
13942a8d400aSPeter Grehan 			bytes = 2;
13952a8d400aSPeter Grehan 			offset += 2;
13962a8d400aSPeter Grehan 			val >>= 16;
13972a8d400aSPeter Grehan 		} else
1398366f6083SPeter Grehan 			return;
13992a8d400aSPeter Grehan 	}
1400366f6083SPeter Grehan 
1401366f6083SPeter Grehan 	switch (capid) {
1402366f6083SPeter Grehan 	case PCIY_MSI:
1403366f6083SPeter Grehan 		msicap_cfgwrite(pi, capoff, offset, bytes, val);
1404366f6083SPeter Grehan 		break;
1405c9b4e987SNeel Natu 	case PCIY_MSIX:
1406c9b4e987SNeel Natu 		msixcap_cfgwrite(pi, capoff, offset, bytes, val);
1407c9b4e987SNeel Natu 		break;
140874f80b23SNeel Natu 	case PCIY_EXPRESS:
140974f80b23SNeel Natu 		pciecap_cfgwrite(pi, capoff, offset, bytes, val);
141074f80b23SNeel Natu 		break;
1411366f6083SPeter Grehan 	default:
1412366f6083SPeter Grehan 		break;
1413366f6083SPeter Grehan 	}
1414366f6083SPeter Grehan }
1415366f6083SPeter Grehan 
1416366f6083SPeter Grehan static int
pci_emul_iscap(struct pci_devinst * pi,int offset)1417366f6083SPeter Grehan pci_emul_iscap(struct pci_devinst *pi, int offset)
1418366f6083SPeter Grehan {
1419366f6083SPeter Grehan 	uint16_t sts;
1420366f6083SPeter Grehan 
1421366f6083SPeter Grehan 	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
1422366f6083SPeter Grehan 	if ((sts & PCIM_STATUS_CAPPRESENT) != 0) {
1423a96b8b80SJohn Baldwin 		if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend)
1424a96b8b80SJohn Baldwin 			return (1);
1425366f6083SPeter Grehan 	}
1426a96b8b80SJohn Baldwin 	return (0);
1427366f6083SPeter Grehan }
1428366f6083SPeter Grehan 
14290ab13648SPeter Grehan static int
pci_emul_fallback_handler(struct vcpu * vcpu __unused,int dir,uint64_t addr __unused,int size __unused,uint64_t * val,void * arg1 __unused,long arg2 __unused)14307d9ef309SJohn Baldwin pci_emul_fallback_handler(struct vcpu *vcpu __unused, int dir,
14317d9ef309SJohn Baldwin     uint64_t addr __unused, int size __unused, uint64_t *val,
143298d920d9SMark Johnston     void *arg1 __unused, long arg2 __unused)
14330ab13648SPeter Grehan {
14340ab13648SPeter Grehan 	/*
14350ab13648SPeter Grehan 	 * Ignore writes; return 0xff's for reads. The mem read code
14360ab13648SPeter Grehan 	 * will take care of truncating to the correct size.
14370ab13648SPeter Grehan 	 */
14380ab13648SPeter Grehan 	if (dir == MEM_F_READ) {
14390ab13648SPeter Grehan 		*val = 0xffffffffffffffff;
14400ab13648SPeter Grehan 	}
14410ab13648SPeter Grehan 
14420ab13648SPeter Grehan 	return (0);
14430ab13648SPeter Grehan }
14440ab13648SPeter Grehan 
144512a6eb99SNeel Natu static int
pci_emul_ecfg_handler(struct vcpu * vcpu __unused,int dir,uint64_t addr,int bytes,uint64_t * val,void * arg1 __unused,long arg2 __unused)14467d9ef309SJohn Baldwin pci_emul_ecfg_handler(struct vcpu *vcpu __unused, int dir, uint64_t addr,
14477d9ef309SJohn Baldwin     int bytes, uint64_t *val, void *arg1 __unused, long arg2 __unused)
144812a6eb99SNeel Natu {
144912a6eb99SNeel Natu 	int bus, slot, func, coff, in;
145012a6eb99SNeel Natu 
145112a6eb99SNeel Natu 	coff = addr & 0xfff;
145212a6eb99SNeel Natu 	func = (addr >> 12) & 0x7;
145312a6eb99SNeel Natu 	slot = (addr >> 15) & 0x1f;
145412a6eb99SNeel Natu 	bus = (addr >> 20) & 0xff;
145512a6eb99SNeel Natu 	in = (dir == MEM_F_READ);
145612a6eb99SNeel Natu 	if (in)
145712a6eb99SNeel Natu 		*val = ~0UL;
14586a284cacSJohn Baldwin 	pci_cfgrw(in, bus, slot, func, coff, bytes, (uint32_t *)val);
145912a6eb99SNeel Natu 	return (0);
146012a6eb99SNeel Natu }
146112a6eb99SNeel Natu 
146212a6eb99SNeel Natu uint64_t
pci_ecfg_base(void)146312a6eb99SNeel Natu pci_ecfg_base(void)
146412a6eb99SNeel Natu {
146512a6eb99SNeel Natu 
146612a6eb99SNeel Natu 	return (PCI_EMUL_ECFG_BASE);
146712a6eb99SNeel Natu }
146812a6eb99SNeel Natu 
14696632a0a4SCorvin Köhne static int
init_bootorder(void)14706632a0a4SCorvin Köhne init_bootorder(void)
14716632a0a4SCorvin Köhne {
14726632a0a4SCorvin Köhne 	struct boot_device *device;
14736632a0a4SCorvin Köhne 	FILE *fp;
14746632a0a4SCorvin Köhne 	char *bootorder;
14756632a0a4SCorvin Köhne 	size_t bootorder_len;
14766632a0a4SCorvin Köhne 
14776632a0a4SCorvin Köhne 	if (TAILQ_EMPTY(&boot_devices))
14786632a0a4SCorvin Köhne 		return (0);
14796632a0a4SCorvin Köhne 
14806632a0a4SCorvin Köhne 	fp = open_memstream(&bootorder, &bootorder_len);
14816632a0a4SCorvin Köhne 	TAILQ_FOREACH(device, &boot_devices, boot_device_chain) {
14826632a0a4SCorvin Köhne 		fprintf(fp, "/pci@i0cf8/pci@%d,%d\n",
14836632a0a4SCorvin Köhne 		    device->pdi->pi_slot, device->pdi->pi_func);
14846632a0a4SCorvin Köhne 	}
14856632a0a4SCorvin Köhne 	fclose(fp);
14866632a0a4SCorvin Köhne 
14876632a0a4SCorvin Köhne 	return (qemu_fwcfg_add_file("bootorder", bootorder_len, bootorder));
14886632a0a4SCorvin Köhne }
14896632a0a4SCorvin Köhne 
1490d84882caSNeel Natu #define	BUSIO_ROUNDUP		32
14917d55d295SCorvin Köhne #define	BUSMEM32_ROUNDUP	(1024 * 1024)
14927d55d295SCorvin Köhne #define	BUSMEM64_ROUNDUP	(512 * 1024 * 1024)
1493d84882caSNeel Natu 
1494a38e2a64SPeter Grehan int
init_pci(struct vmctx * ctx)1495366f6083SPeter Grehan init_pci(struct vmctx *ctx)
1496366f6083SPeter Grehan {
1497621b5090SJohn Baldwin 	char node_name[sizeof("pci.XXX.XX.X")];
149812a6eb99SNeel Natu 	struct mem_range mr;
1499366f6083SPeter Grehan 	struct pci_devemu *pde;
1500d84882caSNeel Natu 	struct businfo *bi;
1501d84882caSNeel Natu 	struct slotinfo *si;
15023cbf3585SJohn Baldwin 	struct funcinfo *fi;
1503621b5090SJohn Baldwin 	nvlist_t *nvl;
1504621b5090SJohn Baldwin 	const char *emul;
15059f08548dSNeel Natu 	size_t lowmem;
15064a4053e1SCorvin Köhne 	int bus, slot, func;
15074a4053e1SCorvin Köhne 	int error;
1508366f6083SPeter Grehan 
15095cf21e48SCorvin Köhne 	if (vm_get_lowmem_limit(ctx) > PCI_EMUL_MEMBASE32)
15105cf21e48SCorvin Köhne 		errx(EX_OSERR, "Invalid lowmem limit");
15115cf21e48SCorvin Köhne 
1512366f6083SPeter Grehan 	pci_emul_iobase = PCI_EMUL_IOBASE;
15135cf21e48SCorvin Köhne 	pci_emul_membase32 = PCI_EMUL_MEMBASE32;
15149922872bSKonstantin Belousov 
1515e497fe86SMark Johnston 	pci_emul_membase64 = vm_get_highmem_base(ctx) +
1516e497fe86SMark Johnston 	    vm_get_highmem_size(ctx);
15174a4053e1SCorvin Köhne 	pci_emul_membase64 = roundup2(pci_emul_membase64, PCI_EMUL_MEMSIZE64);
15184a4053e1SCorvin Köhne 	pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64;
1519366f6083SPeter Grehan 
15206632a0a4SCorvin Köhne 	TAILQ_INIT(&boot_devices);
15216632a0a4SCorvin Köhne 
1522d84882caSNeel Natu 	for (bus = 0; bus < MAXBUSES; bus++) {
1523621b5090SJohn Baldwin 		snprintf(node_name, sizeof(node_name), "pci.%d", bus);
1524621b5090SJohn Baldwin 		nvl = find_config_node(node_name);
1525621b5090SJohn Baldwin 		if (nvl == NULL)
1526d84882caSNeel Natu 			continue;
1527621b5090SJohn Baldwin 		pci_businfo[bus] = calloc(1, sizeof(struct businfo));
1528621b5090SJohn Baldwin 		bi = pci_businfo[bus];
1529621b5090SJohn Baldwin 
1530d84882caSNeel Natu 		/*
1531d84882caSNeel Natu 		 * Keep track of the i/o and memory resources allocated to
1532d84882caSNeel Natu 		 * this bus.
1533d84882caSNeel Natu 		 */
1534d84882caSNeel Natu 		bi->iobase = pci_emul_iobase;
1535d84882caSNeel Natu 		bi->membase32 = pci_emul_membase32;
1536d84882caSNeel Natu 		bi->membase64 = pci_emul_membase64;
1537d84882caSNeel Natu 
153801f9362eSCorvin Köhne 		/* first run: init devices */
153999d65389SNeel Natu 		for (slot = 0; slot < MAXSLOTS; slot++) {
1540d84882caSNeel Natu 			si = &bi->slotinfo[slot];
154199d65389SNeel Natu 			for (func = 0; func < MAXFUNCS; func++) {
1542d84882caSNeel Natu 				fi = &si->si_funcs[func];
1543621b5090SJohn Baldwin 				snprintf(node_name, sizeof(node_name),
1544621b5090SJohn Baldwin 				    "pci.%d.%d.%d", bus, slot, func);
1545621b5090SJohn Baldwin 				nvl = find_config_node(node_name);
1546621b5090SJohn Baldwin 				if (nvl == NULL)
1547d84882caSNeel Natu 					continue;
1548621b5090SJohn Baldwin 
1549621b5090SJohn Baldwin 				fi->fi_config = nvl;
1550621b5090SJohn Baldwin 				emul = get_config_value_node(nvl, "device");
1551621b5090SJohn Baldwin 				if (emul == NULL) {
1552621b5090SJohn Baldwin 					EPRINTLN("pci slot %d:%d:%d: missing "
1553621b5090SJohn Baldwin 					    "\"device\" value", bus, slot, func);
1554621b5090SJohn Baldwin 					return (EINVAL);
1555621b5090SJohn Baldwin 				}
1556621b5090SJohn Baldwin 				pde = pci_emul_finddev(emul);
1557621b5090SJohn Baldwin 				if (pde == NULL) {
1558621b5090SJohn Baldwin 					EPRINTLN("pci slot %d:%d:%d: unknown "
1559621b5090SJohn Baldwin 					    "device \"%s\"", bus, slot, func,
1560621b5090SJohn Baldwin 					    emul);
1561621b5090SJohn Baldwin 					return (EINVAL);
1562621b5090SJohn Baldwin 				}
1563621b5090SJohn Baldwin 				if (pde->pe_alias != NULL) {
1564621b5090SJohn Baldwin 					EPRINTLN("pci slot %d:%d:%d: legacy "
1565621b5090SJohn Baldwin 					    "device \"%s\", use \"%s\" instead",
1566621b5090SJohn Baldwin 					    bus, slot, func, emul,
1567621b5090SJohn Baldwin 					    pde->pe_alias);
1568621b5090SJohn Baldwin 					return (EINVAL);
1569621b5090SJohn Baldwin 				}
1570621b5090SJohn Baldwin 				fi->fi_pde = pde;
1571d84882caSNeel Natu 				error = pci_emul_init(ctx, pde, bus, slot,
1572d84882caSNeel Natu 				    func, fi);
1573a38e2a64SPeter Grehan 				if (error)
1574a38e2a64SPeter Grehan 					return (error);
1575366f6083SPeter Grehan 			}
1576366f6083SPeter Grehan 		}
1577d84882caSNeel Natu 
157801f9362eSCorvin Köhne 		/* second run: assign BARs and free list */
157901f9362eSCorvin Köhne 		struct pci_bar_allocation *bar;
158001f9362eSCorvin Köhne 		struct pci_bar_allocation *bar_tmp;
158101f9362eSCorvin Köhne 		TAILQ_FOREACH_SAFE(bar, &pci_bars, chain, bar_tmp) {
158201f9362eSCorvin Köhne 			pci_emul_assign_bar(bar->pdi, bar->idx, bar->type,
158301f9362eSCorvin Köhne 			    bar->size);
158401f9362eSCorvin Köhne 			free(bar);
158501f9362eSCorvin Köhne 		}
158601f9362eSCorvin Köhne 		TAILQ_INIT(&pci_bars);
158701f9362eSCorvin Köhne 
1588d84882caSNeel Natu 		/*
1589d84882caSNeel Natu 		 * Add some slop to the I/O and memory resources decoded by
1590d84882caSNeel Natu 		 * this bus to give a guest some flexibility if it wants to
1591d84882caSNeel Natu 		 * reprogram the BARs.
1592d84882caSNeel Natu 		 */
1593d84882caSNeel Natu 		pci_emul_iobase += BUSIO_ROUNDUP;
1594d84882caSNeel Natu 		pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP);
1595d84882caSNeel Natu 		bi->iolimit = pci_emul_iobase;
1596d84882caSNeel Natu 
15977d55d295SCorvin Köhne 		pci_emul_membase32 += BUSMEM32_ROUNDUP;
1598d84882caSNeel Natu 		pci_emul_membase32 = roundup2(pci_emul_membase32,
15997d55d295SCorvin Köhne 		    BUSMEM32_ROUNDUP);
1600d84882caSNeel Natu 		bi->memlimit32 = pci_emul_membase32;
1601d84882caSNeel Natu 
16027d55d295SCorvin Köhne 		pci_emul_membase64 += BUSMEM64_ROUNDUP;
1603d84882caSNeel Natu 		pci_emul_membase64 = roundup2(pci_emul_membase64,
16047d55d295SCorvin Köhne 		    BUSMEM64_ROUNDUP);
1605d84882caSNeel Natu 		bi->memlimit64 = pci_emul_membase64;
1606366f6083SPeter Grehan 	}
16070038ee98SPeter Grehan 
16080038ee98SPeter Grehan 	/*
1609b3e9732aSJohn Baldwin 	 * PCI backends are initialized before routing INTx interrupts
1610b3e9732aSJohn Baldwin 	 * so that LPC devices are able to reserve ISA IRQs before
1611b3e9732aSJohn Baldwin 	 * routing PIRQ pins.
1612b3e9732aSJohn Baldwin 	 */
1613b3e9732aSJohn Baldwin 	for (bus = 0; bus < MAXBUSES; bus++) {
1614b3e9732aSJohn Baldwin 		if ((bi = pci_businfo[bus]) == NULL)
1615b3e9732aSJohn Baldwin 			continue;
1616b3e9732aSJohn Baldwin 
1617b3e9732aSJohn Baldwin 		for (slot = 0; slot < MAXSLOTS; slot++) {
1618b3e9732aSJohn Baldwin 			si = &bi->slotinfo[slot];
1619b3e9732aSJohn Baldwin 			for (func = 0; func < MAXFUNCS; func++) {
1620b3e9732aSJohn Baldwin 				fi = &si->si_funcs[func];
1621b3e9732aSJohn Baldwin 				if (fi->fi_devi == NULL)
1622b3e9732aSJohn Baldwin 					continue;
1623b3e9732aSJohn Baldwin 				pci_lintr_route(fi->fi_devi);
1624b3e9732aSJohn Baldwin 			}
1625b3e9732aSJohn Baldwin 		}
1626b3e9732aSJohn Baldwin 	}
16270efad4acSJessica Clarke #ifdef __amd64__
1628b3e9732aSJohn Baldwin 	lpc_pirq_routed();
162955c13f6eSMark Johnston #endif
1630b3e9732aSJohn Baldwin 
16316632a0a4SCorvin Köhne 	if ((error = init_bootorder()) != 0) {
16326632a0a4SCorvin Köhne 		warnx("%s: Unable to init bootorder", __func__);
16336632a0a4SCorvin Köhne 		return (error);
16346632a0a4SCorvin Köhne 	}
16356632a0a4SCorvin Köhne 
1636b3e9732aSJohn Baldwin 	/*
1637f286f746SMark Johnston 	 * The guest physical memory map looks like the following on amd64:
16389f08548dSNeel Natu 	 * [0,		    lowmem)		guest system memory
16395cf21e48SCorvin Köhne 	 * [lowmem,	    0xC0000000)		memory hole (may be absent)
16405cf21e48SCorvin Köhne 	 * [0xC0000000,     0xE0000000)		PCI hole (32-bit BAR allocation)
164112a6eb99SNeel Natu 	 * [0xE0000000,	    0xF0000000)		PCI extended config window
164212a6eb99SNeel Natu 	 * [0xF0000000,	    4GB)		LAPIC, IOAPIC, HPET, firmware
1643f286f746SMark Johnston 	 * [4GB,	    4GB + highmem)	guest system memory
1644f286f746SMark Johnston 	 * [roundup(4GB + highmem, 32GB), ...)	PCI 64-bit BAR allocation
1645f286f746SMark Johnston 	 *
1646f286f746SMark Johnston 	 * On arm64 the guest physical memory map looks like this:
1647f286f746SMark Johnston 	 * [0x0DF00000,	    0x10000000)		PCI I/O memory
1648f286f746SMark Johnston 	 * [0xA0000000,	    0xE0000000)		PCI 32-bit BAR allocation
1649f286f746SMark Johnston 	 * [0xE0000000,	    0xF0000000)		PCI extended config window
1650f286f746SMark Johnston 	 * [4GB,	    4GB + highmem)	guest system memory
1651f286f746SMark Johnston 	 * [roundup(4GB + highmem, 32GB), ...)	PCI 64-bit BAR allocation
1652f286f746SMark Johnston 	 *
1653f286f746SMark Johnston 	 * "lowmem" is guest memory below 0xC0000000.  amd64 guests provisioned
1654f286f746SMark Johnston 	 * with less than 3GB of RAM will have no memory above the 4GB boundary.
1655f286f746SMark Johnston 	 * System memory for arm64 guests is all above the 4GB boundary.
165612a6eb99SNeel Natu 	 */
165712a6eb99SNeel Natu 
165812a6eb99SNeel Natu 	/*
16599f08548dSNeel Natu 	 * Accesses to memory addresses that are not allocated to system
16609f08548dSNeel Natu 	 * memory or PCI devices return 0xff's.
16610ab13648SPeter Grehan 	 */
1662be679db4SNeel Natu 	lowmem = vm_get_lowmem_size(ctx);
166312a6eb99SNeel Natu 	bzero(&mr, sizeof(struct mem_range));
166412a6eb99SNeel Natu 	mr.name = "PCI hole";
166512a6eb99SNeel Natu 	mr.flags = MEM_F_RW | MEM_F_IMMUTABLE;
166612a6eb99SNeel Natu 	mr.base = lowmem;
166712a6eb99SNeel Natu 	mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem;
166812a6eb99SNeel Natu 	mr.handler = pci_emul_fallback_handler;
166912a6eb99SNeel Natu 	error = register_mem_fallback(&mr);
167012a6eb99SNeel Natu 	assert(error == 0);
16719f08548dSNeel Natu 
167212a6eb99SNeel Natu 	/* PCI extended config space */
167312a6eb99SNeel Natu 	bzero(&mr, sizeof(struct mem_range));
167412a6eb99SNeel Natu 	mr.name = "PCI ECFG";
167512a6eb99SNeel Natu 	mr.flags = MEM_F_RW | MEM_F_IMMUTABLE;
167612a6eb99SNeel Natu 	mr.base = PCI_EMUL_ECFG_BASE;
167712a6eb99SNeel Natu 	mr.size = PCI_EMUL_ECFG_SIZE;
167812a6eb99SNeel Natu 	mr.handler = pci_emul_ecfg_handler;
167912a6eb99SNeel Natu 	error = register_mem(&mr);
16800ab13648SPeter Grehan 	assert(error == 0);
1681a38e2a64SPeter Grehan 
1682a38e2a64SPeter Grehan 	return (0);
1683366f6083SPeter Grehan }
1684366f6083SPeter Grehan 
168555c13f6eSMark Johnston #ifdef __amd64__
16863cbf3585SJohn Baldwin static void
pci_apic_prt_entry(int bus __unused,int slot,int pin,struct pci_irq * irq,void * arg __unused)16870efad4acSJessica Clarke pci_apic_prt_entry(int bus __unused, int slot, int pin, struct pci_irq *irq,
16880efad4acSJessica Clarke     void *arg __unused)
16893cbf3585SJohn Baldwin {
16903cbf3585SJohn Baldwin 
1691b3e9732aSJohn Baldwin 	dsdt_line("  Package ()");
16923cbf3585SJohn Baldwin 	dsdt_line("  {");
16933cbf3585SJohn Baldwin 	dsdt_line("    0x%X,", slot << 16 | 0xffff);
16943cbf3585SJohn Baldwin 	dsdt_line("    0x%02X,", pin - 1);
16953cbf3585SJohn Baldwin 	dsdt_line("    Zero,");
16960efad4acSJessica Clarke 	dsdt_line("    0x%X", irq->ioapic_irq);
1697b3e9732aSJohn Baldwin 	dsdt_line("  },");
1698b3e9732aSJohn Baldwin }
1699b3e9732aSJohn Baldwin 
1700b3e9732aSJohn Baldwin static void
pci_pirq_prt_entry(int bus __unused,int slot,int pin,struct pci_irq * irq,void * arg __unused)17010efad4acSJessica Clarke pci_pirq_prt_entry(int bus __unused, int slot, int pin, struct pci_irq *irq,
17020efad4acSJessica Clarke     void *arg __unused)
1703b3e9732aSJohn Baldwin {
1704b3e9732aSJohn Baldwin 	char *name;
1705b3e9732aSJohn Baldwin 
17060efad4acSJessica Clarke 	name = lpc_pirq_name(irq->pirq_pin);
1707b3e9732aSJohn Baldwin 	if (name == NULL)
1708b3e9732aSJohn Baldwin 		return;
1709b3e9732aSJohn Baldwin 	dsdt_line("  Package ()");
1710b3e9732aSJohn Baldwin 	dsdt_line("  {");
1711b3e9732aSJohn Baldwin 	dsdt_line("    0x%X,", slot << 16 | 0xffff);
1712b3e9732aSJohn Baldwin 	dsdt_line("    0x%02X,", pin - 1);
1713b3e9732aSJohn Baldwin 	dsdt_line("    %s,", name);
1714b3e9732aSJohn Baldwin 	dsdt_line("    0x00");
1715b3e9732aSJohn Baldwin 	dsdt_line("  },");
1716b3e9732aSJohn Baldwin 	free(name);
17173cbf3585SJohn Baldwin }
171855c13f6eSMark Johnston #endif
17193cbf3585SJohn Baldwin 
1720d84882caSNeel Natu /*
1721d84882caSNeel Natu  * A bhyve virtual machine has a flat PCI hierarchy with a root port
1722d84882caSNeel Natu  * corresponding to each PCI bus.
1723d84882caSNeel Natu  */
1724d84882caSNeel Natu static void
pci_bus_write_dsdt(int bus)1725d84882caSNeel Natu pci_bus_write_dsdt(int bus)
1726e6c8bc29SJohn Baldwin {
1727d84882caSNeel Natu 	struct businfo *bi;
1728d84882caSNeel Natu 	struct slotinfo *si;
1729e6c8bc29SJohn Baldwin 	struct pci_devinst *pi;
173055c13f6eSMark Johnston 	int func, slot;
1731e6c8bc29SJohn Baldwin 
1732d84882caSNeel Natu 	/*
1733d84882caSNeel Natu 	 * If there are no devices on this 'bus' then just return.
1734d84882caSNeel Natu 	 */
1735d84882caSNeel Natu 	if ((bi = pci_businfo[bus]) == NULL) {
1736d84882caSNeel Natu 		/*
1737d84882caSNeel Natu 		 * Bus 0 is special because it decodes the I/O ports used
1738d84882caSNeel Natu 		 * for PCI config space access even if there are no devices
1739d84882caSNeel Natu 		 * on it.
1740d84882caSNeel Natu 		 */
1741d84882caSNeel Natu 		if (bus != 0)
1742d84882caSNeel Natu 			return;
1743d84882caSNeel Natu 	}
1744d84882caSNeel Natu 
1745d84882caSNeel Natu 	dsdt_line("  Device (PC%02X)", bus);
1746e6c8bc29SJohn Baldwin 	dsdt_line("  {");
1747e6c8bc29SJohn Baldwin 	dsdt_line("    Name (_HID, EisaId (\"PNP0A03\"))");
1748d84882caSNeel Natu 
1749d84882caSNeel Natu 	dsdt_line("    Method (_BBN, 0, NotSerialized)");
1750d84882caSNeel Natu 	dsdt_line("    {");
1751d84882caSNeel Natu 	dsdt_line("        Return (0x%08X)", bus);
1752d84882caSNeel Natu 	dsdt_line("    }");
1753e6c8bc29SJohn Baldwin 	dsdt_line("    Name (_CRS, ResourceTemplate ()");
1754e6c8bc29SJohn Baldwin 	dsdt_line("    {");
1755e6c8bc29SJohn Baldwin 	dsdt_line("      WordBusNumber (ResourceProducer, MinFixed, "
1756e6c8bc29SJohn Baldwin 	    "MaxFixed, PosDecode,");
1757e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000,             // Granularity");
1758d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Minimum", bus);
1759d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Maximum", bus);
1760e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000,             // Translation Offset");
1761d84882caSNeel Natu 	dsdt_line("        0x0001,             // Length");
1762e6c8bc29SJohn Baldwin 	dsdt_line("        ,, )");
1763d84882caSNeel Natu 
1764f286f746SMark Johnston #ifdef __amd64__
1765d84882caSNeel Natu 	if (bus == 0) {
1766e6c8bc29SJohn Baldwin 		dsdt_indent(3);
1767e6c8bc29SJohn Baldwin 		dsdt_fixed_ioport(0xCF8, 8);
1768e6c8bc29SJohn Baldwin 		dsdt_unindent(3);
1769d84882caSNeel Natu 
1770e6c8bc29SJohn Baldwin 		dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
1771e6c8bc29SJohn Baldwin 		    "PosDecode, EntireRange,");
1772e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Granularity");
1773e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Range Minimum");
1774e6c8bc29SJohn Baldwin 		dsdt_line("        0x0CF7,             // Range Maximum");
1775e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Translation Offset");
1776e6c8bc29SJohn Baldwin 		dsdt_line("        0x0CF8,             // Length");
1777e6c8bc29SJohn Baldwin 		dsdt_line("        ,, , TypeStatic)");
1778d84882caSNeel Natu 
1779e6c8bc29SJohn Baldwin 		dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
1780e6c8bc29SJohn Baldwin 		    "PosDecode, EntireRange,");
1781e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Granularity");
1782e6c8bc29SJohn Baldwin 		dsdt_line("        0x0D00,             // Range Minimum");
1783d84882caSNeel Natu 		dsdt_line("        0x%04X,             // Range Maximum",
1784d84882caSNeel Natu 		    PCI_EMUL_IOBASE - 1);
1785e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Translation Offset");
1786d84882caSNeel Natu 		dsdt_line("        0x%04X,             // Length",
1787d84882caSNeel Natu 		    PCI_EMUL_IOBASE - 0x0D00);
1788e6c8bc29SJohn Baldwin 		dsdt_line("        ,, , TypeStatic)");
1789d84882caSNeel Natu 
1790d84882caSNeel Natu 		if (bi == NULL) {
1791d84882caSNeel Natu 			dsdt_line("    })");
1792d84882caSNeel Natu 			goto done;
1793d84882caSNeel Natu 		}
1794d84882caSNeel Natu 	}
1795f286f746SMark Johnston #endif
1796d84882caSNeel Natu 	assert(bi != NULL);
1797d84882caSNeel Natu 
1798d84882caSNeel Natu 	/* i/o window */
1799d84882caSNeel Natu 	dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
1800d84882caSNeel Natu 	    "PosDecode, EntireRange,");
1801d84882caSNeel Natu 	dsdt_line("        0x0000,             // Granularity");
1802d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Minimum", bi->iobase);
1803d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Maximum",
1804d84882caSNeel Natu 	    bi->iolimit - 1);
1805d84882caSNeel Natu 	dsdt_line("        0x0000,             // Translation Offset");
1806d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Length",
1807d84882caSNeel Natu 	    bi->iolimit - bi->iobase);
1808d84882caSNeel Natu 	dsdt_line("        ,, , TypeStatic)");
1809d84882caSNeel Natu 
1810d84882caSNeel Natu 	/* mmio window (32-bit) */
1811e6c8bc29SJohn Baldwin 	dsdt_line("      DWordMemory (ResourceProducer, PosDecode, "
1812e6c8bc29SJohn Baldwin 	    "MinFixed, MaxFixed, NonCacheable, ReadWrite,");
1813e6c8bc29SJohn Baldwin 	dsdt_line("        0x00000000,         // Granularity");
1814d84882caSNeel Natu 	dsdt_line("        0x%08X,         // Range Minimum\n", bi->membase32);
1815e6c8bc29SJohn Baldwin 	dsdt_line("        0x%08X,         // Range Maximum\n",
1816d84882caSNeel Natu 	    bi->memlimit32 - 1);
1817e6c8bc29SJohn Baldwin 	dsdt_line("        0x00000000,         // Translation Offset");
1818d84882caSNeel Natu 	dsdt_line("        0x%08X,         // Length\n",
1819d84882caSNeel Natu 	    bi->memlimit32 - bi->membase32);
1820e6c8bc29SJohn Baldwin 	dsdt_line("        ,, , AddressRangeMemory, TypeStatic)");
1821d84882caSNeel Natu 
1822d84882caSNeel Natu 	/* mmio window (64-bit) */
1823e6c8bc29SJohn Baldwin 	dsdt_line("      QWordMemory (ResourceProducer, PosDecode, "
1824e6c8bc29SJohn Baldwin 	    "MinFixed, MaxFixed, NonCacheable, ReadWrite,");
1825e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000000000000000, // Granularity");
1826d84882caSNeel Natu 	dsdt_line("        0x%016lX, // Range Minimum\n", bi->membase64);
1827e6c8bc29SJohn Baldwin 	dsdt_line("        0x%016lX, // Range Maximum\n",
1828d84882caSNeel Natu 	    bi->memlimit64 - 1);
1829e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000000000000000, // Translation Offset");
1830e6c8bc29SJohn Baldwin 	dsdt_line("        0x%016lX, // Length\n",
1831d84882caSNeel Natu 	    bi->memlimit64 - bi->membase64);
1832e6c8bc29SJohn Baldwin 	dsdt_line("        ,, , AddressRangeMemory, TypeStatic)");
1833e6c8bc29SJohn Baldwin 	dsdt_line("    })");
1834d84882caSNeel Natu 
183555c13f6eSMark Johnston #ifdef __amd64__
183655c13f6eSMark Johnston 	if (pci_count_lintr(bus) != 0) {
18373cbf3585SJohn Baldwin 		dsdt_indent(2);
1838b3e9732aSJohn Baldwin 		dsdt_line("Name (PPRT, Package ()");
18393cbf3585SJohn Baldwin 		dsdt_line("{");
1840b3e9732aSJohn Baldwin 		pci_walk_lintr(bus, pci_pirq_prt_entry, NULL);
18413cbf3585SJohn Baldwin 		dsdt_line("})");
1842b3e9732aSJohn Baldwin 		dsdt_line("Name (APRT, Package ()");
1843b3e9732aSJohn Baldwin 		dsdt_line("{");
1844b3e9732aSJohn Baldwin 		pci_walk_lintr(bus, pci_apic_prt_entry, NULL);
1845b3e9732aSJohn Baldwin 		dsdt_line("})");
1846b3e9732aSJohn Baldwin 		dsdt_line("Method (_PRT, 0, NotSerialized)");
1847b3e9732aSJohn Baldwin 		dsdt_line("{");
1848b3e9732aSJohn Baldwin 		dsdt_line("  If (PICM)");
1849b3e9732aSJohn Baldwin 		dsdt_line("  {");
1850b3e9732aSJohn Baldwin 		dsdt_line("    Return (APRT)");
1851b3e9732aSJohn Baldwin 		dsdt_line("  }");
1852b3e9732aSJohn Baldwin 		dsdt_line("  Else");
1853b3e9732aSJohn Baldwin 		dsdt_line("  {");
1854b3e9732aSJohn Baldwin 		dsdt_line("    Return (PPRT)");
1855b3e9732aSJohn Baldwin 		dsdt_line("  }");
1856b3e9732aSJohn Baldwin 		dsdt_line("}");
18573cbf3585SJohn Baldwin 		dsdt_unindent(2);
18583cbf3585SJohn Baldwin 	}
185955c13f6eSMark Johnston #endif
1860e6c8bc29SJohn Baldwin 
1861e6c8bc29SJohn Baldwin 	dsdt_indent(2);
1862e6c8bc29SJohn Baldwin 	for (slot = 0; slot < MAXSLOTS; slot++) {
1863d84882caSNeel Natu 		si = &bi->slotinfo[slot];
1864e6c8bc29SJohn Baldwin 		for (func = 0; func < MAXFUNCS; func++) {
1865d84882caSNeel Natu 			pi = si->si_funcs[func].fi_devi;
1866e6c8bc29SJohn Baldwin 			if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL)
1867e6c8bc29SJohn Baldwin 				pi->pi_d->pe_write_dsdt(pi);
1868e6c8bc29SJohn Baldwin 		}
1869e6c8bc29SJohn Baldwin 	}
1870e6c8bc29SJohn Baldwin 	dsdt_unindent(2);
1871f286f746SMark Johnston #ifdef __amd64__
1872d84882caSNeel Natu done:
1873f286f746SMark Johnston #endif
1874e6c8bc29SJohn Baldwin 	dsdt_line("  }");
1875e6c8bc29SJohn Baldwin }
1876e6c8bc29SJohn Baldwin 
1877d84882caSNeel Natu void
pci_write_dsdt(void)1878d84882caSNeel Natu pci_write_dsdt(void)
1879d84882caSNeel Natu {
1880d84882caSNeel Natu 	int bus;
1881d84882caSNeel Natu 
1882b3e9732aSJohn Baldwin 	dsdt_indent(1);
1883b3e9732aSJohn Baldwin 	dsdt_line("Name (PICM, 0x00)");
1884b3e9732aSJohn Baldwin 	dsdt_line("Method (_PIC, 1, NotSerialized)");
1885b3e9732aSJohn Baldwin 	dsdt_line("{");
1886b3e9732aSJohn Baldwin 	dsdt_line("  Store (Arg0, PICM)");
1887b3e9732aSJohn Baldwin 	dsdt_line("}");
1888b3e9732aSJohn Baldwin 	dsdt_line("");
1889b3e9732aSJohn Baldwin 	dsdt_line("Scope (_SB)");
1890b3e9732aSJohn Baldwin 	dsdt_line("{");
1891d84882caSNeel Natu 	for (bus = 0; bus < MAXBUSES; bus++)
1892d84882caSNeel Natu 		pci_bus_write_dsdt(bus);
1893b3e9732aSJohn Baldwin 	dsdt_line("}");
1894b3e9732aSJohn Baldwin 	dsdt_unindent(1);
1895d84882caSNeel Natu }
1896d84882caSNeel Natu 
1897366f6083SPeter Grehan int
pci_bus_configured(int bus)1898b100acf2SNeel Natu pci_bus_configured(int bus)
1899b100acf2SNeel Natu {
1900b100acf2SNeel Natu 	assert(bus >= 0 && bus < MAXBUSES);
1901b100acf2SNeel Natu 	return (pci_businfo[bus] != NULL);
1902b100acf2SNeel Natu }
1903b100acf2SNeel Natu 
1904b100acf2SNeel Natu int
pci_msi_enabled(struct pci_devinst * pi)1905366f6083SPeter Grehan pci_msi_enabled(struct pci_devinst *pi)
1906366f6083SPeter Grehan {
1907366f6083SPeter Grehan 	return (pi->pi_msi.enabled);
1908366f6083SPeter Grehan }
1909366f6083SPeter Grehan 
1910366f6083SPeter Grehan int
pci_msi_maxmsgnum(struct pci_devinst * pi)19114f8be175SNeel Natu pci_msi_maxmsgnum(struct pci_devinst *pi)
1912366f6083SPeter Grehan {
1913366f6083SPeter Grehan 	if (pi->pi_msi.enabled)
19144f8be175SNeel Natu 		return (pi->pi_msi.maxmsgnum);
1915366f6083SPeter Grehan 	else
1916366f6083SPeter Grehan 		return (0);
1917366f6083SPeter Grehan }
1918366f6083SPeter Grehan 
1919c9b4e987SNeel Natu int
pci_msix_enabled(struct pci_devinst * pi)1920c9b4e987SNeel Natu pci_msix_enabled(struct pci_devinst *pi)
1921c9b4e987SNeel Natu {
1922c9b4e987SNeel Natu 
1923c9b4e987SNeel Natu 	return (pi->pi_msix.enabled && !pi->pi_msi.enabled);
1924c9b4e987SNeel Natu }
1925c9b4e987SNeel Natu 
1926c9b4e987SNeel Natu void
pci_generate_msix(struct pci_devinst * pi,int index)1927c9b4e987SNeel Natu pci_generate_msix(struct pci_devinst *pi, int index)
1928c9b4e987SNeel Natu {
1929c9b4e987SNeel Natu 	struct msix_table_entry *mte;
1930c9b4e987SNeel Natu 
1931c9b4e987SNeel Natu 	if (!pci_msix_enabled(pi))
1932c9b4e987SNeel Natu 		return;
1933c9b4e987SNeel Natu 
1934c9b4e987SNeel Natu 	if (pi->pi_msix.function_mask)
1935c9b4e987SNeel Natu 		return;
1936c9b4e987SNeel Natu 
1937c9b4e987SNeel Natu 	if (index >= pi->pi_msix.table_count)
1938c9b4e987SNeel Natu 		return;
1939c9b4e987SNeel Natu 
1940c9b4e987SNeel Natu 	mte = &pi->pi_msix.table[index];
1941c9b4e987SNeel Natu 	if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
1942c9b4e987SNeel Natu 		/* XXX Set PBA bit if interrupt is disabled */
1943dc6a00f2SMark Johnston 		vm_raise_msi(pi->pi_vmctx, mte->addr, mte->msg_data,
1944dc6a00f2SMark Johnston 		    pi->pi_bus, pi->pi_slot, pi->pi_func);
1945c9b4e987SNeel Natu 	}
1946c9b4e987SNeel Natu }
1947c9b4e987SNeel Natu 
1948366f6083SPeter Grehan void
pci_generate_msi(struct pci_devinst * pi,int index)19494f8be175SNeel Natu pci_generate_msi(struct pci_devinst *pi, int index)
1950366f6083SPeter Grehan {
1951366f6083SPeter Grehan 
19524f8be175SNeel Natu 	if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) {
1953dc6a00f2SMark Johnston 		vm_raise_msi(pi->pi_vmctx, pi->pi_msi.addr,
1954dc6a00f2SMark Johnston 		    pi->pi_msi.msg_data + index,
1955dc6a00f2SMark Johnston 		    pi->pi_bus, pi->pi_slot, pi->pi_func);
1956366f6083SPeter Grehan 	}
1957366f6083SPeter Grehan }
1958366f6083SPeter Grehan 
19593cbf3585SJohn Baldwin static bool
pci_lintr_permitted(struct pci_devinst * pi)19603cbf3585SJohn Baldwin pci_lintr_permitted(struct pci_devinst *pi)
19610038ee98SPeter Grehan {
19623cbf3585SJohn Baldwin 	uint16_t cmd;
19630038ee98SPeter Grehan 
19643cbf3585SJohn Baldwin 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
19653cbf3585SJohn Baldwin 	return (!(pi->pi_msi.enabled || pi->pi_msix.enabled ||
19663cbf3585SJohn Baldwin 		(cmd & PCIM_CMD_INTxDIS)));
19673cbf3585SJohn Baldwin }
19683cbf3585SJohn Baldwin 
1969b3e9732aSJohn Baldwin void
pci_lintr_request(struct pci_devinst * pi)19703cbf3585SJohn Baldwin pci_lintr_request(struct pci_devinst *pi)
19713cbf3585SJohn Baldwin {
1972d84882caSNeel Natu 	struct businfo *bi;
19733cbf3585SJohn Baldwin 	struct slotinfo *si;
1974b3e9732aSJohn Baldwin 	int bestpin, bestcount, pin;
19753cbf3585SJohn Baldwin 
1976d84882caSNeel Natu 	bi = pci_businfo[pi->pi_bus];
1977d84882caSNeel Natu 	assert(bi != NULL);
1978d84882caSNeel Natu 
19793cbf3585SJohn Baldwin 	/*
1980b3e9732aSJohn Baldwin 	 * Just allocate a pin from our slot.  The pin will be
1981b3e9732aSJohn Baldwin 	 * assigned IRQs later when interrupts are routed.
19823cbf3585SJohn Baldwin 	 */
1983d84882caSNeel Natu 	si = &bi->slotinfo[pi->pi_slot];
19843cbf3585SJohn Baldwin 	bestpin = 0;
19853cbf3585SJohn Baldwin 	bestcount = si->si_intpins[0].ii_count;
19863cbf3585SJohn Baldwin 	for (pin = 1; pin < 4; pin++) {
19873cbf3585SJohn Baldwin 		if (si->si_intpins[pin].ii_count < bestcount) {
19883cbf3585SJohn Baldwin 			bestpin = pin;
19893cbf3585SJohn Baldwin 			bestcount = si->si_intpins[pin].ii_count;
19903cbf3585SJohn Baldwin 		}
19913cbf3585SJohn Baldwin 	}
19923cbf3585SJohn Baldwin 
19933cbf3585SJohn Baldwin 	si->si_intpins[bestpin].ii_count++;
19943cbf3585SJohn Baldwin 	pi->pi_lintr.pin = bestpin + 1;
19953cbf3585SJohn Baldwin 	pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1);
1996b3e9732aSJohn Baldwin }
1997b3e9732aSJohn Baldwin 
1998b3e9732aSJohn Baldwin static void
pci_lintr_route(struct pci_devinst * pi)1999b3e9732aSJohn Baldwin pci_lintr_route(struct pci_devinst *pi)
2000b3e9732aSJohn Baldwin {
2001b3e9732aSJohn Baldwin 	struct businfo *bi;
2002b3e9732aSJohn Baldwin 	struct intxinfo *ii;
20030efad4acSJessica Clarke 	struct pci_irq *irq;
2004b3e9732aSJohn Baldwin 
2005b3e9732aSJohn Baldwin 	if (pi->pi_lintr.pin == 0)
2006b3e9732aSJohn Baldwin 		return;
2007b3e9732aSJohn Baldwin 
2008b3e9732aSJohn Baldwin 	bi = pci_businfo[pi->pi_bus];
2009b3e9732aSJohn Baldwin 	assert(bi != NULL);
2010b3e9732aSJohn Baldwin 	ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1];
20110efad4acSJessica Clarke 	irq = &ii->ii_irq;
20120efad4acSJessica Clarke 	pci_irq_route(pi, irq);
20130efad4acSJessica Clarke 	pi->pi_lintr.irq = *irq;
20140efad4acSJessica Clarke 	pci_set_cfgdata8(pi, PCIR_INTLINE, pci_irq_intline(irq));
20150038ee98SPeter Grehan }
20160038ee98SPeter Grehan 
20170038ee98SPeter Grehan void
pci_lintr_assert(struct pci_devinst * pi)20180038ee98SPeter Grehan pci_lintr_assert(struct pci_devinst *pi)
20190038ee98SPeter Grehan {
20200038ee98SPeter Grehan 
20213cbf3585SJohn Baldwin 	assert(pi->pi_lintr.pin > 0);
2022ac7304a7SNeel Natu 
20233cbf3585SJohn Baldwin 	pthread_mutex_lock(&pi->pi_lintr.lock);
20243cbf3585SJohn Baldwin 	if (pi->pi_lintr.state == IDLE) {
20253cbf3585SJohn Baldwin 		if (pci_lintr_permitted(pi)) {
20263cbf3585SJohn Baldwin 			pi->pi_lintr.state = ASSERTED;
2027b3e9732aSJohn Baldwin 			pci_irq_assert(pi);
20283cbf3585SJohn Baldwin 		} else
20293cbf3585SJohn Baldwin 			pi->pi_lintr.state = PENDING;
20300038ee98SPeter Grehan 	}
20313cbf3585SJohn Baldwin 	pthread_mutex_unlock(&pi->pi_lintr.lock);
2032ac7304a7SNeel Natu }
20330038ee98SPeter Grehan 
20340038ee98SPeter Grehan void
pci_lintr_deassert(struct pci_devinst * pi)20350038ee98SPeter Grehan pci_lintr_deassert(struct pci_devinst *pi)
20360038ee98SPeter Grehan {
20370038ee98SPeter Grehan 
20383cbf3585SJohn Baldwin 	assert(pi->pi_lintr.pin > 0);
2039ac7304a7SNeel Natu 
20403cbf3585SJohn Baldwin 	pthread_mutex_lock(&pi->pi_lintr.lock);
20413cbf3585SJohn Baldwin 	if (pi->pi_lintr.state == ASSERTED) {
20423cbf3585SJohn Baldwin 		pi->pi_lintr.state = IDLE;
2043b3e9732aSJohn Baldwin 		pci_irq_deassert(pi);
20443cbf3585SJohn Baldwin 	} else if (pi->pi_lintr.state == PENDING)
20453cbf3585SJohn Baldwin 		pi->pi_lintr.state = IDLE;
20463cbf3585SJohn Baldwin 	pthread_mutex_unlock(&pi->pi_lintr.lock);
20473cbf3585SJohn Baldwin }
20483cbf3585SJohn Baldwin 
20493cbf3585SJohn Baldwin static void
pci_lintr_update(struct pci_devinst * pi)20503cbf3585SJohn Baldwin pci_lintr_update(struct pci_devinst *pi)
20513cbf3585SJohn Baldwin {
20523cbf3585SJohn Baldwin 
20533cbf3585SJohn Baldwin 	pthread_mutex_lock(&pi->pi_lintr.lock);
20543cbf3585SJohn Baldwin 	if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) {
2055b3e9732aSJohn Baldwin 		pci_irq_deassert(pi);
20563cbf3585SJohn Baldwin 		pi->pi_lintr.state = PENDING;
20573cbf3585SJohn Baldwin 	} else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) {
20583cbf3585SJohn Baldwin 		pi->pi_lintr.state = ASSERTED;
2059b3e9732aSJohn Baldwin 		pci_irq_assert(pi);
20603cbf3585SJohn Baldwin 	}
20613cbf3585SJohn Baldwin 	pthread_mutex_unlock(&pi->pi_lintr.lock);
20623cbf3585SJohn Baldwin }
20633cbf3585SJohn Baldwin 
20643cbf3585SJohn Baldwin int
pci_count_lintr(int bus)2065d84882caSNeel Natu pci_count_lintr(int bus)
20663cbf3585SJohn Baldwin {
20673cbf3585SJohn Baldwin 	int count, slot, pin;
2068d84882caSNeel Natu 	struct slotinfo *slotinfo;
20693cbf3585SJohn Baldwin 
20703cbf3585SJohn Baldwin 	count = 0;
2071d84882caSNeel Natu 	if (pci_businfo[bus] != NULL) {
20723cbf3585SJohn Baldwin 		for (slot = 0; slot < MAXSLOTS; slot++) {
2073d84882caSNeel Natu 			slotinfo = &pci_businfo[bus]->slotinfo[slot];
20743cbf3585SJohn Baldwin 			for (pin = 0; pin < 4; pin++) {
2075d84882caSNeel Natu 				if (slotinfo->si_intpins[pin].ii_count != 0)
20763cbf3585SJohn Baldwin 					count++;
20773cbf3585SJohn Baldwin 			}
20783cbf3585SJohn Baldwin 		}
2079d84882caSNeel Natu 	}
20803cbf3585SJohn Baldwin 	return (count);
20813cbf3585SJohn Baldwin }
20823cbf3585SJohn Baldwin 
20833cbf3585SJohn Baldwin void
pci_walk_lintr(int bus,pci_lintr_cb cb,void * arg)2084d84882caSNeel Natu pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg)
20853cbf3585SJohn Baldwin {
2086d84882caSNeel Natu 	struct businfo *bi;
2087d84882caSNeel Natu 	struct slotinfo *si;
20883cbf3585SJohn Baldwin 	struct intxinfo *ii;
20893cbf3585SJohn Baldwin 	int slot, pin;
20903cbf3585SJohn Baldwin 
2091d84882caSNeel Natu 	if ((bi = pci_businfo[bus]) == NULL)
2092d84882caSNeel Natu 		return;
2093d84882caSNeel Natu 
20943cbf3585SJohn Baldwin 	for (slot = 0; slot < MAXSLOTS; slot++) {
2095d84882caSNeel Natu 		si = &bi->slotinfo[slot];
20963cbf3585SJohn Baldwin 		for (pin = 0; pin < 4; pin++) {
2097d84882caSNeel Natu 			ii = &si->si_intpins[pin];
20983cbf3585SJohn Baldwin 			if (ii->ii_count != 0)
20990efad4acSJessica Clarke 				cb(bus, slot, pin + 1, &ii->ii_irq, arg);
21003cbf3585SJohn Baldwin 		}
21010038ee98SPeter Grehan 	}
2102ac7304a7SNeel Natu }
21030038ee98SPeter Grehan 
210499d65389SNeel Natu /*
210599d65389SNeel Natu  * Return 1 if the emulated device in 'slot' is a multi-function device.
210699d65389SNeel Natu  * Return 0 otherwise.
210799d65389SNeel Natu  */
210899d65389SNeel Natu static int
pci_emul_is_mfdev(int bus,int slot)2109d84882caSNeel Natu pci_emul_is_mfdev(int bus, int slot)
211099d65389SNeel Natu {
2111d84882caSNeel Natu 	struct businfo *bi;
2112d84882caSNeel Natu 	struct slotinfo *si;
211399d65389SNeel Natu 	int f, numfuncs;
21140038ee98SPeter Grehan 
211599d65389SNeel Natu 	numfuncs = 0;
2116d84882caSNeel Natu 	if ((bi = pci_businfo[bus]) != NULL) {
2117d84882caSNeel Natu 		si = &bi->slotinfo[slot];
211899d65389SNeel Natu 		for (f = 0; f < MAXFUNCS; f++) {
2119d84882caSNeel Natu 			if (si->si_funcs[f].fi_devi != NULL) {
212099d65389SNeel Natu 				numfuncs++;
212199d65389SNeel Natu 			}
212299d65389SNeel Natu 		}
2123d84882caSNeel Natu 	}
212499d65389SNeel Natu 	return (numfuncs > 1);
212599d65389SNeel Natu }
212699d65389SNeel Natu 
212799d65389SNeel Natu /*
212899d65389SNeel Natu  * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on
212999d65389SNeel Natu  * whether or not is a multi-function being emulated in the pci 'slot'.
213099d65389SNeel Natu  */
213199d65389SNeel Natu static void
pci_emul_hdrtype_fixup(int bus,int slot,int off,int bytes,uint32_t * rv)2132d84882caSNeel Natu pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv)
213399d65389SNeel Natu {
213499d65389SNeel Natu 	int mfdev;
213599d65389SNeel Natu 
213699d65389SNeel Natu 	if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) {
2137d84882caSNeel Natu 		mfdev = pci_emul_is_mfdev(bus, slot);
213899d65389SNeel Natu 		switch (bytes) {
213999d65389SNeel Natu 		case 1:
214099d65389SNeel Natu 		case 2:
214199d65389SNeel Natu 			*rv &= ~PCIM_MFDEV;
214299d65389SNeel Natu 			if (mfdev) {
214399d65389SNeel Natu 				*rv |= PCIM_MFDEV;
214499d65389SNeel Natu 			}
214599d65389SNeel Natu 			break;
214699d65389SNeel Natu 		case 4:
214799d65389SNeel Natu 			*rv &= ~(PCIM_MFDEV << 16);
214899d65389SNeel Natu 			if (mfdev) {
214999d65389SNeel Natu 				*rv |= (PCIM_MFDEV << 16);
215099d65389SNeel Natu 			}
215199d65389SNeel Natu 			break;
215299d65389SNeel Natu 		}
215399d65389SNeel Natu 	}
215499d65389SNeel Natu }
21550038ee98SPeter Grehan 
215656282675SJohn Baldwin /*
215756282675SJohn Baldwin  * Update device state in response to changes to the PCI command
215856282675SJohn Baldwin  * register.
215956282675SJohn Baldwin  */
216056282675SJohn Baldwin void
pci_emul_cmd_changed(struct pci_devinst * pi,uint16_t old)216156282675SJohn Baldwin pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old)
216256282675SJohn Baldwin {
216356282675SJohn Baldwin 	int i;
216456282675SJohn Baldwin 	uint16_t changed, new;
216556282675SJohn Baldwin 
216656282675SJohn Baldwin 	new = pci_get_cfgdata16(pi, PCIR_COMMAND);
216756282675SJohn Baldwin 	changed = old ^ new;
216856282675SJohn Baldwin 
216956282675SJohn Baldwin 	/*
217056282675SJohn Baldwin 	 * If the MMIO or I/O address space decoding has changed then
217156282675SJohn Baldwin 	 * register/unregister all BARs that decode that address space.
217256282675SJohn Baldwin 	 */
2173e47fe318SCorvin Köhne 	for (i = 0; i <= PCI_BARMAX_WITH_ROM; i++) {
217456282675SJohn Baldwin 		switch (pi->pi_bar[i].type) {
217556282675SJohn Baldwin 			case PCIBAR_NONE:
217656282675SJohn Baldwin 			case PCIBAR_MEMHI64:
217756282675SJohn Baldwin 				break;
217856282675SJohn Baldwin 			case PCIBAR_IO:
217956282675SJohn Baldwin 				/* I/O address space decoding changed? */
218056282675SJohn Baldwin 				if (changed & PCIM_CMD_PORTEN) {
218156282675SJohn Baldwin 					if (new & PCIM_CMD_PORTEN)
218256282675SJohn Baldwin 						register_bar(pi, i);
218356282675SJohn Baldwin 					else
218456282675SJohn Baldwin 						unregister_bar(pi, i);
218556282675SJohn Baldwin 				}
218656282675SJohn Baldwin 				break;
2187e47fe318SCorvin Köhne 			case PCIBAR_ROM:
2188e47fe318SCorvin Köhne 				/* skip (un-)register of ROM if it disabled */
2189e47fe318SCorvin Köhne 				if (!romen(pi))
2190e47fe318SCorvin Köhne 					break;
2191e47fe318SCorvin Köhne 				/* fallthrough */
219256282675SJohn Baldwin 			case PCIBAR_MEM32:
219356282675SJohn Baldwin 			case PCIBAR_MEM64:
219456282675SJohn Baldwin 				/* MMIO address space decoding changed? */
219556282675SJohn Baldwin 				if (changed & PCIM_CMD_MEMEN) {
219656282675SJohn Baldwin 					if (new & PCIM_CMD_MEMEN)
219756282675SJohn Baldwin 						register_bar(pi, i);
219856282675SJohn Baldwin 					else
219956282675SJohn Baldwin 						unregister_bar(pi, i);
220056282675SJohn Baldwin 				}
220156282675SJohn Baldwin 				break;
220256282675SJohn Baldwin 			default:
220356282675SJohn Baldwin 				assert(0);
220456282675SJohn Baldwin 		}
220556282675SJohn Baldwin 	}
220656282675SJohn Baldwin 
220756282675SJohn Baldwin 	/*
220856282675SJohn Baldwin 	 * If INTx has been unmasked and is pending, assert the
220956282675SJohn Baldwin 	 * interrupt.
221056282675SJohn Baldwin 	 */
221156282675SJohn Baldwin 	pci_lintr_update(pi);
221256282675SJohn Baldwin }
221356282675SJohn Baldwin 
2214028d9311SNeel Natu static void
pci_emul_cmdsts_write(struct pci_devinst * pi,int coff,uint32_t new,int bytes)221554335630SNeel Natu pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes)
2216028d9311SNeel Natu {
221756282675SJohn Baldwin 	int rshift;
221856282675SJohn Baldwin 	uint32_t cmd, old, readonly;
221954335630SNeel Natu 
222054335630SNeel Natu 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);	/* stash old value */
2221028d9311SNeel Natu 
2222028d9311SNeel Natu 	/*
222354335630SNeel Natu 	 * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3.
222454335630SNeel Natu 	 *
222554335630SNeel Natu 	 * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are
222654335630SNeel Natu 	 * 'write 1 to clear'. However these bits are not set to '1' by
222754335630SNeel Natu 	 * any device emulation so it is simpler to treat them as readonly.
2228028d9311SNeel Natu 	 */
222954335630SNeel Natu 	rshift = (coff & 0x3) * 8;
223054335630SNeel Natu 	readonly = 0xFFFFF880 >> rshift;
2231028d9311SNeel Natu 
223254335630SNeel Natu 	old = CFGREAD(pi, coff, bytes);
223354335630SNeel Natu 	new &= ~readonly;
223454335630SNeel Natu 	new |= (old & readonly);
223554335630SNeel Natu 	CFGWRITE(pi, coff, new, bytes);			/* update config */
223654335630SNeel Natu 
223756282675SJohn Baldwin 	pci_emul_cmd_changed(pi, cmd);
2238028d9311SNeel Natu }
2239028d9311SNeel Natu 
224012a6eb99SNeel Natu static void
pci_cfgrw(int in,int bus,int slot,int func,int coff,int bytes,uint32_t * valp)22416a284cacSJohn Baldwin pci_cfgrw(int in, int bus, int slot, int func, int coff, int bytes,
2242f4841d8aSMark Johnston     uint32_t *valp)
2243366f6083SPeter Grehan {
2244d84882caSNeel Natu 	struct businfo *bi;
2245d84882caSNeel Natu 	struct slotinfo *si;
2246366f6083SPeter Grehan 	struct pci_devinst *pi;
2247366f6083SPeter Grehan 	struct pci_devemu *pe;
224812a6eb99SNeel Natu 	int idx, needcfg;
2249028d9311SNeel Natu 	uint64_t addr, bar, mask;
2250366f6083SPeter Grehan 
225112a6eb99SNeel Natu 	if ((bi = pci_businfo[bus]) != NULL) {
225212a6eb99SNeel Natu 		si = &bi->slotinfo[slot];
225312a6eb99SNeel Natu 		pi = si->si_funcs[func].fi_devi;
2254d84882caSNeel Natu 	} else
225590415e0bSNeel Natu 		pi = NULL;
225690415e0bSNeel Natu 
225799d65389SNeel Natu 	/*
225812a6eb99SNeel Natu 	 * Just return if there is no device at this slot:func or if the
2259ec8a394dSElyes Haouas 	 * guest is doing an un-aligned access.
226099d65389SNeel Natu 	 */
226112a6eb99SNeel Natu 	if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) ||
226212a6eb99SNeel Natu 	    (coff & (bytes - 1)) != 0) {
2263366f6083SPeter Grehan 		if (in)
2264f4841d8aSMark Johnston 			*valp = 0xffffffff;
226512a6eb99SNeel Natu 		return;
226612a6eb99SNeel Natu 	}
226712a6eb99SNeel Natu 
226812a6eb99SNeel Natu 	/*
226912a6eb99SNeel Natu 	 * Ignore all writes beyond the standard config space and return all
227012a6eb99SNeel Natu 	 * ones on reads.
227112a6eb99SNeel Natu 	 */
227212a6eb99SNeel Natu 	if (coff >= PCI_REGMAX + 1) {
227312a6eb99SNeel Natu 		if (in) {
2274f4841d8aSMark Johnston 			*valp = 0xffffffff;
227512a6eb99SNeel Natu 			/*
227612a6eb99SNeel Natu 			 * Extended capabilities begin at offset 256 in config
227712a6eb99SNeel Natu 			 * space. Absence of extended capabilities is signaled
227812a6eb99SNeel Natu 			 * with all 0s in the extended capability header at
227912a6eb99SNeel Natu 			 * offset 256.
228012a6eb99SNeel Natu 			 */
228112a6eb99SNeel Natu 			if (coff <= PCI_REGMAX + 4)
2282f4841d8aSMark Johnston 				*valp = 0x00000000;
228312a6eb99SNeel Natu 		}
228412a6eb99SNeel Natu 		return;
2285366f6083SPeter Grehan 	}
2286366f6083SPeter Grehan 
2287366f6083SPeter Grehan 	pe = pi->pi_d;
2288366f6083SPeter Grehan 
2289366f6083SPeter Grehan 	/*
2290366f6083SPeter Grehan 	 * Config read
2291366f6083SPeter Grehan 	 */
2292366f6083SPeter Grehan 	if (in) {
2293366f6083SPeter Grehan 		/* Let the device emulation override the default handler */
229499d65389SNeel Natu 		if (pe->pe_cfgread != NULL) {
2295f4841d8aSMark Johnston 			needcfg = pe->pe_cfgread(pi, coff, bytes, valp);
229699d65389SNeel Natu 		} else {
229799d65389SNeel Natu 			needcfg = 1;
229899d65389SNeel Natu 		}
2299366f6083SPeter Grehan 
230054335630SNeel Natu 		if (needcfg)
2301f4841d8aSMark Johnston 			*valp = CFGREAD(pi, coff, bytes);
230299d65389SNeel Natu 
2303f4841d8aSMark Johnston 		pci_emul_hdrtype_fixup(bus, slot, coff, bytes, valp);
2304366f6083SPeter Grehan 	} else {
2305366f6083SPeter Grehan 		/* Let the device emulation override the default handler */
2306366f6083SPeter Grehan 		if (pe->pe_cfgwrite != NULL &&
2307f4841d8aSMark Johnston 		    (*pe->pe_cfgwrite)(pi, coff, bytes, *valp) == 0)
230812a6eb99SNeel Natu 			return;
2309366f6083SPeter Grehan 
2310366f6083SPeter Grehan 		/*
2311e47fe318SCorvin Köhne 		 * Special handling for write to BAR and ROM registers
2312366f6083SPeter Grehan 		 */
231345ddbf21SCorvin Köhne 		if (is_pcir_bar(coff) || is_pcir_bios(coff)) {
2314366f6083SPeter Grehan 			/*
2315366f6083SPeter Grehan 			 * Ignore writes to BAR registers that are not
2316366f6083SPeter Grehan 			 * 4-byte aligned.
2317366f6083SPeter Grehan 			 */
2318366f6083SPeter Grehan 			if (bytes != 4 || (coff & 0x3) != 0)
231912a6eb99SNeel Natu 				return;
232045ddbf21SCorvin Köhne 
232145ddbf21SCorvin Köhne 			if (is_pcir_bar(coff)) {
2322366f6083SPeter Grehan 				idx = (coff - PCIR_BAR(0)) / 4;
232345ddbf21SCorvin Köhne 			} else if (is_pcir_bios(coff)) {
2324e47fe318SCorvin Köhne 				idx = PCI_ROM_IDX;
232545ddbf21SCorvin Köhne 			} else {
232645ddbf21SCorvin Köhne 				errx(4, "%s: invalid BAR offset %d", __func__,
232745ddbf21SCorvin Köhne 				    coff);
2328e47fe318SCorvin Köhne 			}
232945ddbf21SCorvin Köhne 
2330028d9311SNeel Natu 			mask = ~(pi->pi_bar[idx].size - 1);
2331366f6083SPeter Grehan 			switch (pi->pi_bar[idx].type) {
2332366f6083SPeter Grehan 			case PCIBAR_NONE:
2333028d9311SNeel Natu 				pi->pi_bar[idx].addr = bar = 0;
2334366f6083SPeter Grehan 				break;
2335366f6083SPeter Grehan 			case PCIBAR_IO:
2336f4841d8aSMark Johnston 				addr = *valp & mask;
2337f286f746SMark Johnston #if defined(PCI_EMUL_IOMASK)
2338f286f746SMark Johnston 				addr &= PCI_EMUL_IOMASK;
2339f286f746SMark Johnston #endif
2340e87a6f3eSCorvin Köhne 				bar = addr | pi->pi_bar[idx].lobits;
2341028d9311SNeel Natu 				/*
2342028d9311SNeel Natu 				 * Register the new BAR value for interception
2343028d9311SNeel Natu 				 */
2344028d9311SNeel Natu 				if (addr != pi->pi_bar[idx].addr) {
2345028d9311SNeel Natu 					update_bar_address(pi, addr, idx,
2346028d9311SNeel Natu 							   PCIBAR_IO);
2347028d9311SNeel Natu 				}
2348366f6083SPeter Grehan 				break;
2349366f6083SPeter Grehan 			case PCIBAR_MEM32:
2350f4841d8aSMark Johnston 				addr = bar = *valp & mask;
2351e87a6f3eSCorvin Köhne 				bar |= pi->pi_bar[idx].lobits;
2352028d9311SNeel Natu 				if (addr != pi->pi_bar[idx].addr) {
2353028d9311SNeel Natu 					update_bar_address(pi, addr, idx,
2354028d9311SNeel Natu 							   PCIBAR_MEM32);
2355028d9311SNeel Natu 				}
2356366f6083SPeter Grehan 				break;
2357366f6083SPeter Grehan 			case PCIBAR_MEM64:
2358f4841d8aSMark Johnston 				addr = bar = *valp & mask;
2359e87a6f3eSCorvin Köhne 				bar |= pi->pi_bar[idx].lobits;
2360028d9311SNeel Natu 				if (addr != (uint32_t)pi->pi_bar[idx].addr) {
2361028d9311SNeel Natu 					update_bar_address(pi, addr, idx,
2362028d9311SNeel Natu 							   PCIBAR_MEM64);
2363028d9311SNeel Natu 				}
2364366f6083SPeter Grehan 				break;
2365366f6083SPeter Grehan 			case PCIBAR_MEMHI64:
2366366f6083SPeter Grehan 				mask = ~(pi->pi_bar[idx - 1].size - 1);
2367f4841d8aSMark Johnston 				addr = ((uint64_t)*valp << 32) & mask;
2368028d9311SNeel Natu 				bar = addr >> 32;
2369028d9311SNeel Natu 				if (bar != pi->pi_bar[idx - 1].addr >> 32) {
2370028d9311SNeel Natu 					update_bar_address(pi, addr, idx - 1,
2371028d9311SNeel Natu 							   PCIBAR_MEMHI64);
2372028d9311SNeel Natu 				}
2373366f6083SPeter Grehan 				break;
2374e47fe318SCorvin Köhne 			case PCIBAR_ROM:
2375f4841d8aSMark Johnston 				addr = bar = *valp & mask;
2376e47fe318SCorvin Köhne 				if (memen(pi) && romen(pi)) {
2377e47fe318SCorvin Köhne 					unregister_bar(pi, idx);
2378e47fe318SCorvin Köhne 				}
2379e47fe318SCorvin Köhne 				pi->pi_bar[idx].addr = addr;
2380f4841d8aSMark Johnston 				pi->pi_bar[idx].lobits = *valp &
2381e47fe318SCorvin Köhne 				    PCIM_BIOS_ENABLE;
2382e47fe318SCorvin Köhne 				/* romen could have changed it value */
2383e47fe318SCorvin Köhne 				if (memen(pi) && romen(pi)) {
2384e47fe318SCorvin Köhne 					register_bar(pi, idx);
2385e47fe318SCorvin Köhne 				}
2386e47fe318SCorvin Köhne 				bar |= pi->pi_bar[idx].lobits;
2387e47fe318SCorvin Köhne 				break;
2388366f6083SPeter Grehan 			default:
2389366f6083SPeter Grehan 				assert(0);
2390366f6083SPeter Grehan 			}
2391366f6083SPeter Grehan 			pci_set_cfgdata32(pi, coff, bar);
2392cd942e0fSPeter Grehan 
2393366f6083SPeter Grehan 		} else if (pci_emul_iscap(pi, coff)) {
2394f4841d8aSMark Johnston 			pci_emul_capwrite(pi, coff, bytes, *valp, 0, 0);
239554335630SNeel Natu 		} else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) {
2396f4841d8aSMark Johnston 			pci_emul_cmdsts_write(pi, coff, *valp, bytes);
2397366f6083SPeter Grehan 		} else {
2398f4841d8aSMark Johnston 			CFGWRITE(pi, coff, *valp, bytes);
2399366f6083SPeter Grehan 		}
2400366f6083SPeter Grehan 	}
240112a6eb99SNeel Natu }
2402366f6083SPeter Grehan 
240331cf78c9SMark Johnston #ifdef __amd64__
240412a6eb99SNeel Natu static int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff;
240512a6eb99SNeel Natu 
240612a6eb99SNeel Natu static int
pci_emul_cfgaddr(struct vmctx * ctx __unused,int in,int port __unused,int bytes,uint32_t * eax,void * arg __unused)240708b05de1SJohn Baldwin pci_emul_cfgaddr(struct vmctx *ctx __unused, int in,
240898d920d9SMark Johnston     int port __unused, int bytes, uint32_t *eax, void *arg __unused)
240912a6eb99SNeel Natu {
241012a6eb99SNeel Natu 	uint32_t x;
241112a6eb99SNeel Natu 
241212a6eb99SNeel Natu 	if (bytes != 4) {
241312a6eb99SNeel Natu 		if (in)
241412a6eb99SNeel Natu 			*eax = (bytes == 2) ? 0xffff : 0xff;
241512a6eb99SNeel Natu 		return (0);
241612a6eb99SNeel Natu 	}
241712a6eb99SNeel Natu 
241812a6eb99SNeel Natu 	if (in) {
241912a6eb99SNeel Natu 		x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff;
242012a6eb99SNeel Natu 		if (cfgenable)
242112a6eb99SNeel Natu 			x |= CONF1_ENABLE;
242212a6eb99SNeel Natu 		*eax = x;
242312a6eb99SNeel Natu 	} else {
242412a6eb99SNeel Natu 		x = *eax;
242512a6eb99SNeel Natu 		cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE;
24261b0e2f0bSCorvin Köhne 		cfgoff = (x & PCI_REGMAX) & ~0x03;
242712a6eb99SNeel Natu 		cfgfunc = (x >> 8) & PCI_FUNCMAX;
242812a6eb99SNeel Natu 		cfgslot = (x >> 11) & PCI_SLOTMAX;
242912a6eb99SNeel Natu 		cfgbus = (x >> 16) & PCI_BUSMAX;
243012a6eb99SNeel Natu 	}
243112a6eb99SNeel Natu 
243212a6eb99SNeel Natu 	return (0);
243312a6eb99SNeel Natu }
243412a6eb99SNeel Natu INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr);
243512a6eb99SNeel Natu 
243612a6eb99SNeel Natu static int
pci_emul_cfgdata(struct vmctx * ctx __unused,int in,int port,int bytes,uint32_t * eax,void * arg __unused)24376a284cacSJohn Baldwin pci_emul_cfgdata(struct vmctx *ctx __unused, int in, int port,
243878c2cd83SJohn Baldwin     int bytes, uint32_t *eax, void *arg __unused)
243912a6eb99SNeel Natu {
244012a6eb99SNeel Natu 	int coff;
244112a6eb99SNeel Natu 
244212a6eb99SNeel Natu 	assert(bytes == 1 || bytes == 2 || bytes == 4);
244312a6eb99SNeel Natu 
244412a6eb99SNeel Natu 	coff = cfgoff + (port - CONF1_DATA_PORT);
244512a6eb99SNeel Natu 	if (cfgenable) {
24466a284cacSJohn Baldwin 		pci_cfgrw(in, cfgbus, cfgslot, cfgfunc, coff, bytes, eax);
244712a6eb99SNeel Natu 	} else {
244812a6eb99SNeel Natu 		/* Ignore accesses to cfgdata if not enabled by cfgaddr */
244912a6eb99SNeel Natu 		if (in)
245012a6eb99SNeel Natu 			*eax = 0xffffffff;
245112a6eb99SNeel Natu 	}
2452366f6083SPeter Grehan 	return (0);
2453366f6083SPeter Grehan }
2454366f6083SPeter Grehan 
2455366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata);
2456366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata);
2457366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
2458366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
245931cf78c9SMark Johnston #endif
2460366f6083SPeter Grehan 
2461483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2462483d953aSJohn Baldwin /*
2463483d953aSJohn Baldwin  * Saves/restores PCI device emulated state. Returns 0 on success.
2464483d953aSJohn Baldwin  */
2465483d953aSJohn Baldwin static int
pci_snapshot_pci_dev(struct vm_snapshot_meta * meta)2466483d953aSJohn Baldwin pci_snapshot_pci_dev(struct vm_snapshot_meta *meta)
2467483d953aSJohn Baldwin {
2468483d953aSJohn Baldwin 	struct pci_devinst *pi;
2469483d953aSJohn Baldwin 	int i;
2470483d953aSJohn Baldwin 	int ret;
2471483d953aSJohn Baldwin 
2472483d953aSJohn Baldwin 	pi = meta->dev_data;
2473483d953aSJohn Baldwin 
2474483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.enabled, meta, ret, done);
2475483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.addr, meta, ret, done);
2476483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.msg_data, meta, ret, done);
2477483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.maxmsgnum, meta, ret, done);
2478483d953aSJohn Baldwin 
2479483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.enabled, meta, ret, done);
2480483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_bar, meta, ret, done);
2481483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_bar, meta, ret, done);
2482483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_offset, meta, ret, done);
2483483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_count, meta, ret, done);
2484483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_offset, meta, ret, done);
2485483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_size, meta, ret, done);
2486483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.function_mask, meta, ret, done);
2487483d953aSJohn Baldwin 
2488483d953aSJohn Baldwin 	SNAPSHOT_BUF_OR_LEAVE(pi->pi_cfgdata, sizeof(pi->pi_cfgdata),
2489483d953aSJohn Baldwin 			      meta, ret, done);
2490483d953aSJohn Baldwin 
2491ed721684SMark Johnston 	for (i = 0; i < (int)nitems(pi->pi_bar); i++) {
2492483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].type, meta, ret, done);
2493483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].size, meta, ret, done);
2494483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].addr, meta, ret, done);
2495483d953aSJohn Baldwin 	}
2496483d953aSJohn Baldwin 
2497483d953aSJohn Baldwin 	/* Restore MSI-X table. */
2498483d953aSJohn Baldwin 	for (i = 0; i < pi->pi_msix.table_count; i++) {
2499483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].addr,
2500483d953aSJohn Baldwin 				      meta, ret, done);
2501483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].msg_data,
2502483d953aSJohn Baldwin 				      meta, ret, done);
2503483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].vector_control,
2504483d953aSJohn Baldwin 				      meta, ret, done);
2505483d953aSJohn Baldwin 	}
2506483d953aSJohn Baldwin 
2507483d953aSJohn Baldwin done:
2508483d953aSJohn Baldwin 	return (ret);
2509483d953aSJohn Baldwin }
2510483d953aSJohn Baldwin 
2511483d953aSJohn Baldwin int
pci_snapshot(struct vm_snapshot_meta * meta)2512483d953aSJohn Baldwin pci_snapshot(struct vm_snapshot_meta *meta)
2513483d953aSJohn Baldwin {
2514483d953aSJohn Baldwin 	struct pci_devemu *pde;
2515483d953aSJohn Baldwin 	struct pci_devinst *pdi;
2516483d953aSJohn Baldwin 	int ret;
2517483d953aSJohn Baldwin 
2518483d953aSJohn Baldwin 	assert(meta->dev_name != NULL);
2519483d953aSJohn Baldwin 
2520381ef27dSVitaliy Gusev 	pdi = meta->dev_data;
2521381ef27dSVitaliy Gusev 	pde = pdi->pi_d;
2522483d953aSJohn Baldwin 
2523381ef27dSVitaliy Gusev 	if (pde->pe_snapshot == NULL)
2524381ef27dSVitaliy Gusev 		return (ENOTSUP);
2525483d953aSJohn Baldwin 
2526483d953aSJohn Baldwin 	ret = pci_snapshot_pci_dev(meta);
2527381ef27dSVitaliy Gusev 	if (ret == 0)
2528483d953aSJohn Baldwin 		ret = (*pde->pe_snapshot)(meta);
2529483d953aSJohn Baldwin 
2530483d953aSJohn Baldwin 	return (ret);
2531483d953aSJohn Baldwin }
2532483d953aSJohn Baldwin 
2533483d953aSJohn Baldwin int
pci_pause(struct pci_devinst * pdi)2534381ef27dSVitaliy Gusev pci_pause(struct pci_devinst *pdi)
2535483d953aSJohn Baldwin {
2536381ef27dSVitaliy Gusev 	struct pci_devemu *pde = pdi->pi_d;
2537483d953aSJohn Baldwin 
2538483d953aSJohn Baldwin 	if (pde->pe_pause == NULL) {
2539483d953aSJohn Baldwin 		/* The pause/resume functionality is optional. */
2540483d953aSJohn Baldwin 		return (0);
2541483d953aSJohn Baldwin 	}
2542483d953aSJohn Baldwin 
25436a284cacSJohn Baldwin 	return (*pde->pe_pause)(pdi);
2544483d953aSJohn Baldwin }
2545483d953aSJohn Baldwin 
2546483d953aSJohn Baldwin int
pci_resume(struct pci_devinst * pdi)2547381ef27dSVitaliy Gusev pci_resume(struct pci_devinst *pdi)
2548483d953aSJohn Baldwin {
2549381ef27dSVitaliy Gusev 	struct pci_devemu *pde = pdi->pi_d;
2550483d953aSJohn Baldwin 
2551483d953aSJohn Baldwin 	if (pde->pe_resume == NULL) {
2552483d953aSJohn Baldwin 		/* The pause/resume functionality is optional. */
2553483d953aSJohn Baldwin 		return (0);
2554483d953aSJohn Baldwin 	}
2555483d953aSJohn Baldwin 
25566a284cacSJohn Baldwin 	return (*pde->pe_resume)(pdi);
2557483d953aSJohn Baldwin }
2558483d953aSJohn Baldwin #endif
2559483d953aSJohn Baldwin 
2560366f6083SPeter Grehan #define PCI_EMUL_TEST
2561366f6083SPeter Grehan #ifdef PCI_EMUL_TEST
2562366f6083SPeter Grehan /*
2563366f6083SPeter Grehan  * Define a dummy test device
2564366f6083SPeter Grehan  */
2565d84882caSNeel Natu #define DIOSZ	8
25664d1e669cSPeter Grehan #define DMEMSZ	4096
2567366f6083SPeter Grehan struct pci_emul_dsoftc {
25684d1e669cSPeter Grehan 	uint8_t   ioregs[DIOSZ];
2569fd4e0d4cSNeel Natu 	uint8_t	  memregs[2][DMEMSZ];
2570366f6083SPeter Grehan };
2571366f6083SPeter Grehan 
25724d1e669cSPeter Grehan #define	PCI_EMUL_MSI_MSGS	 4
25734d1e669cSPeter Grehan #define	PCI_EMUL_MSIX_MSGS	16
2574366f6083SPeter Grehan 
2575b67e81dbSJohn Baldwin static int
pci_emul_dinit(struct pci_devinst * pi,nvlist_t * nvl __unused)25766a284cacSJohn Baldwin pci_emul_dinit(struct pci_devinst *pi, nvlist_t *nvl __unused)
2577366f6083SPeter Grehan {
2578366f6083SPeter Grehan 	int error;
2579366f6083SPeter Grehan 	struct pci_emul_dsoftc *sc;
2580366f6083SPeter Grehan 
2581994f858aSXin LI 	sc = calloc(1, sizeof(struct pci_emul_dsoftc));
2582366f6083SPeter Grehan 
2583366f6083SPeter Grehan 	pi->pi_arg = sc;
2584366f6083SPeter Grehan 
2585366f6083SPeter Grehan 	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001);
2586366f6083SPeter Grehan 	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD);
2587366f6083SPeter Grehan 	pci_set_cfgdata8(pi, PCIR_CLASS, 0x02);
2588366f6083SPeter Grehan 
25894d1e669cSPeter Grehan 	error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS);
2590366f6083SPeter Grehan 	assert(error == 0);
2591366f6083SPeter Grehan 
25924d1e669cSPeter Grehan 	error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ);
25934d1e669cSPeter Grehan 	assert(error == 0);
25944d1e669cSPeter Grehan 
25954d1e669cSPeter Grehan 	error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ);
2596366f6083SPeter Grehan 	assert(error == 0);
2597366f6083SPeter Grehan 
2598fd4e0d4cSNeel Natu 	error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ);
2599fd4e0d4cSNeel Natu 	assert(error == 0);
2600fd4e0d4cSNeel Natu 
2601366f6083SPeter Grehan 	return (0);
2602366f6083SPeter Grehan }
2603366f6083SPeter Grehan 
2604b67e81dbSJohn Baldwin static void
pci_emul_diow(struct pci_devinst * pi,int baridx,uint64_t offset,int size,uint64_t value)26056a284cacSJohn Baldwin pci_emul_diow(struct pci_devinst *pi, int baridx, uint64_t offset, int size,
260698d920d9SMark Johnston     uint64_t value)
2607366f6083SPeter Grehan {
2608366f6083SPeter Grehan 	int i;
2609366f6083SPeter Grehan 	struct pci_emul_dsoftc *sc = pi->pi_arg;
2610366f6083SPeter Grehan 
26114d1e669cSPeter Grehan 	if (baridx == 0) {
26124d1e669cSPeter Grehan 		if (offset + size > DIOSZ) {
26134d1e669cSPeter Grehan 			printf("diow: iow too large, offset %ld size %d\n",
26144d1e669cSPeter Grehan 			       offset, size);
2615366f6083SPeter Grehan 			return;
2616366f6083SPeter Grehan 		}
2617366f6083SPeter Grehan 
2618366f6083SPeter Grehan 		if (size == 1) {
26194d1e669cSPeter Grehan 			sc->ioregs[offset] = value & 0xff;
2620366f6083SPeter Grehan 		} else if (size == 2) {
26214d1e669cSPeter Grehan 			*(uint16_t *)&sc->ioregs[offset] = value & 0xffff;
26224d1e669cSPeter Grehan 		} else if (size == 4) {
26234d1e669cSPeter Grehan 			*(uint32_t *)&sc->ioregs[offset] = value;
2624366f6083SPeter Grehan 		} else {
26254d1e669cSPeter Grehan 			printf("diow: iow unknown size %d\n", size);
2626366f6083SPeter Grehan 		}
2627366f6083SPeter Grehan 
2628366f6083SPeter Grehan 		/*
2629366f6083SPeter Grehan 		 * Special magic value to generate an interrupt
2630366f6083SPeter Grehan 		 */
2631366f6083SPeter Grehan 		if (offset == 4 && size == 4 && pci_msi_enabled(pi))
26324f8be175SNeel Natu 			pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi));
2633366f6083SPeter Grehan 
2634366f6083SPeter Grehan 		if (value == 0xabcdef) {
26354f8be175SNeel Natu 			for (i = 0; i < pci_msi_maxmsgnum(pi); i++)
2636366f6083SPeter Grehan 				pci_generate_msi(pi, i);
2637366f6083SPeter Grehan 		}
2638366f6083SPeter Grehan 	}
2639366f6083SPeter Grehan 
2640fd4e0d4cSNeel Natu 	if (baridx == 1 || baridx == 2) {
26414d1e669cSPeter Grehan 		if (offset + size > DMEMSZ) {
26424d1e669cSPeter Grehan 			printf("diow: memw too large, offset %ld size %d\n",
26434d1e669cSPeter Grehan 			       offset, size);
26444d1e669cSPeter Grehan 			return;
26454d1e669cSPeter Grehan 		}
26464d1e669cSPeter Grehan 
2647fd4e0d4cSNeel Natu 		i = baridx - 1;		/* 'memregs' index */
2648fd4e0d4cSNeel Natu 
26494d1e669cSPeter Grehan 		if (size == 1) {
2650fd4e0d4cSNeel Natu 			sc->memregs[i][offset] = value;
26514d1e669cSPeter Grehan 		} else if (size == 2) {
2652fd4e0d4cSNeel Natu 			*(uint16_t *)&sc->memregs[i][offset] = value;
26534d1e669cSPeter Grehan 		} else if (size == 4) {
2654fd4e0d4cSNeel Natu 			*(uint32_t *)&sc->memregs[i][offset] = value;
26554d1e669cSPeter Grehan 		} else if (size == 8) {
2656fd4e0d4cSNeel Natu 			*(uint64_t *)&sc->memregs[i][offset] = value;
26574d1e669cSPeter Grehan 		} else {
26584d1e669cSPeter Grehan 			printf("diow: memw unknown size %d\n", size);
26594d1e669cSPeter Grehan 		}
26604d1e669cSPeter Grehan 
26614d1e669cSPeter Grehan 		/*
26624d1e669cSPeter Grehan 		 * magic interrupt ??
26634d1e669cSPeter Grehan 		 */
26644d1e669cSPeter Grehan 	}
26654d1e669cSPeter Grehan 
26669f3dba68SPedro F. Giffuni 	if (baridx > 2 || baridx < 0) {
26674d1e669cSPeter Grehan 		printf("diow: unknown bar idx %d\n", baridx);
26684d1e669cSPeter Grehan 	}
26694d1e669cSPeter Grehan }
26704d1e669cSPeter Grehan 
26714d1e669cSPeter Grehan static uint64_t
pci_emul_dior(struct pci_devinst * pi,int baridx,uint64_t offset,int size)26726a284cacSJohn Baldwin pci_emul_dior(struct pci_devinst *pi, int baridx, uint64_t offset, int size)
2673366f6083SPeter Grehan {
2674366f6083SPeter Grehan 	struct pci_emul_dsoftc *sc = pi->pi_arg;
2675366f6083SPeter Grehan 	uint32_t value;
2676fd4e0d4cSNeel Natu 	int i;
2677366f6083SPeter Grehan 
26784d1e669cSPeter Grehan 	if (baridx == 0) {
26794d1e669cSPeter Grehan 		if (offset + size > DIOSZ) {
26804d1e669cSPeter Grehan 			printf("dior: ior too large, offset %ld size %d\n",
26814d1e669cSPeter Grehan 			       offset, size);
2682366f6083SPeter Grehan 			return (0);
2683366f6083SPeter Grehan 		}
2684366f6083SPeter Grehan 
26856e43f3edSPedro F. Giffuni 		value = 0;
2686366f6083SPeter Grehan 		if (size == 1) {
26874d1e669cSPeter Grehan 			value = sc->ioregs[offset];
2688366f6083SPeter Grehan 		} else if (size == 2) {
26894d1e669cSPeter Grehan 			value = *(uint16_t *) &sc->ioregs[offset];
26904d1e669cSPeter Grehan 		} else if (size == 4) {
26914d1e669cSPeter Grehan 			value = *(uint32_t *) &sc->ioregs[offset];
2692366f6083SPeter Grehan 		} else {
26934d1e669cSPeter Grehan 			printf("dior: ior unknown size %d\n", size);
26944d1e669cSPeter Grehan 		}
26954d1e669cSPeter Grehan 	}
26964d1e669cSPeter Grehan 
2697fd4e0d4cSNeel Natu 	if (baridx == 1 || baridx == 2) {
26984d1e669cSPeter Grehan 		if (offset + size > DMEMSZ) {
26994d1e669cSPeter Grehan 			printf("dior: memr too large, offset %ld size %d\n",
27004d1e669cSPeter Grehan 			       offset, size);
27014d1e669cSPeter Grehan 			return (0);
27024d1e669cSPeter Grehan 		}
27034d1e669cSPeter Grehan 
2704fd4e0d4cSNeel Natu 		i = baridx - 1;		/* 'memregs' index */
2705fd4e0d4cSNeel Natu 
27064d1e669cSPeter Grehan 		if (size == 1) {
2707fd4e0d4cSNeel Natu 			value = sc->memregs[i][offset];
27084d1e669cSPeter Grehan 		} else if (size == 2) {
2709fd4e0d4cSNeel Natu 			value = *(uint16_t *) &sc->memregs[i][offset];
27104d1e669cSPeter Grehan 		} else if (size == 4) {
2711fd4e0d4cSNeel Natu 			value = *(uint32_t *) &sc->memregs[i][offset];
27124d1e669cSPeter Grehan 		} else if (size == 8) {
2713fd4e0d4cSNeel Natu 			value = *(uint64_t *) &sc->memregs[i][offset];
27144d1e669cSPeter Grehan 		} else {
27154d1e669cSPeter Grehan 			printf("dior: ior unknown size %d\n", size);
27164d1e669cSPeter Grehan 		}
27174d1e669cSPeter Grehan 	}
27184d1e669cSPeter Grehan 
27194d1e669cSPeter Grehan 
27209f3dba68SPedro F. Giffuni 	if (baridx > 2 || baridx < 0) {
27214d1e669cSPeter Grehan 		printf("dior: unknown bar idx %d\n", baridx);
27224d1e669cSPeter Grehan 		return (0);
2723366f6083SPeter Grehan 	}
2724366f6083SPeter Grehan 
2725366f6083SPeter Grehan 	return (value);
2726366f6083SPeter Grehan }
2727366f6083SPeter Grehan 
2728483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2729381ef27dSVitaliy Gusev struct pci_devinst *
pci_next(const struct pci_devinst * cursor)2730381ef27dSVitaliy Gusev pci_next(const struct pci_devinst *cursor)
2731381ef27dSVitaliy Gusev {
2732381ef27dSVitaliy Gusev 	unsigned bus = 0, slot = 0, func = 0;
2733381ef27dSVitaliy Gusev 	struct businfo *bi;
2734381ef27dSVitaliy Gusev 	struct slotinfo *si;
2735381ef27dSVitaliy Gusev 	struct funcinfo *fi;
2736381ef27dSVitaliy Gusev 
2737381ef27dSVitaliy Gusev 	bus = cursor ? cursor->pi_bus : 0;
2738381ef27dSVitaliy Gusev 	slot = cursor ? cursor->pi_slot : 0;
2739381ef27dSVitaliy Gusev 	func = cursor ? (cursor->pi_func + 1) : 0;
2740381ef27dSVitaliy Gusev 
2741381ef27dSVitaliy Gusev 	for (; bus < MAXBUSES; bus++) {
2742381ef27dSVitaliy Gusev 		if ((bi = pci_businfo[bus]) == NULL)
2743381ef27dSVitaliy Gusev 			continue;
2744381ef27dSVitaliy Gusev 
2745381ef27dSVitaliy Gusev 		if (slot >= MAXSLOTS)
2746381ef27dSVitaliy Gusev 			slot = 0;
2747381ef27dSVitaliy Gusev 
2748381ef27dSVitaliy Gusev 		for (; slot < MAXSLOTS; slot++) {
2749381ef27dSVitaliy Gusev 			si = &bi->slotinfo[slot];
2750381ef27dSVitaliy Gusev 			if (func >= MAXFUNCS)
2751381ef27dSVitaliy Gusev 				func = 0;
2752381ef27dSVitaliy Gusev 			for (; func < MAXFUNCS; func++) {
2753381ef27dSVitaliy Gusev 				fi = &si->si_funcs[func];
2754381ef27dSVitaliy Gusev 				if (fi->fi_devi == NULL)
2755381ef27dSVitaliy Gusev 					continue;
2756381ef27dSVitaliy Gusev 
2757381ef27dSVitaliy Gusev 				return (fi->fi_devi);
2758381ef27dSVitaliy Gusev 			}
2759381ef27dSVitaliy Gusev 		}
2760381ef27dSVitaliy Gusev 	}
2761381ef27dSVitaliy Gusev 
2762381ef27dSVitaliy Gusev 	return (NULL);
2763381ef27dSVitaliy Gusev }
2764381ef27dSVitaliy Gusev 
2765c9faf698SMark Johnston static int
pci_emul_snapshot(struct vm_snapshot_meta * meta __unused)276698d920d9SMark Johnston pci_emul_snapshot(struct vm_snapshot_meta *meta __unused)
2767483d953aSJohn Baldwin {
2768483d953aSJohn Baldwin 	return (0);
2769483d953aSJohn Baldwin }
2770483d953aSJohn Baldwin #endif
2771483d953aSJohn Baldwin 
277237045dfaSMark Johnston static const struct pci_devemu pci_dummy = {
2773366f6083SPeter Grehan 	.pe_emu = "dummy",
2774366f6083SPeter Grehan 	.pe_init = pci_emul_dinit,
27754d1e669cSPeter Grehan 	.pe_barwrite = pci_emul_diow,
2776483d953aSJohn Baldwin 	.pe_barread = pci_emul_dior,
2777483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2778483d953aSJohn Baldwin 	.pe_snapshot = pci_emul_snapshot,
2779483d953aSJohn Baldwin #endif
2780366f6083SPeter Grehan };
2781366f6083SPeter Grehan PCI_EMUL_SET(pci_dummy);
2782366f6083SPeter Grehan 
2783366f6083SPeter Grehan #endif /* PCI_EMUL_TEST */
2784