xref: /freebsd/usr.sbin/bhyve/pci_emul.c (revision e47fe318)
1366f6083SPeter Grehan /*-
21de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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  * $FreeBSD$
29366f6083SPeter Grehan  */
30366f6083SPeter Grehan 
31366f6083SPeter Grehan #include <sys/cdefs.h>
32366f6083SPeter Grehan __FBSDID("$FreeBSD$");
33366f6083SPeter Grehan 
34366f6083SPeter Grehan #include <sys/param.h>
35366f6083SPeter Grehan #include <sys/linker_set.h>
36e47fe318SCorvin Köhne #include <sys/mman.h>
37366f6083SPeter Grehan 
38366f6083SPeter Grehan #include <ctype.h>
39fc7207c8SEmmanuel Vadot #include <err.h>
407e12dfe5SEnji Cooper #include <errno.h>
413cbf3585SJohn Baldwin #include <pthread.h>
42366f6083SPeter Grehan #include <stdio.h>
43366f6083SPeter Grehan #include <stdlib.h>
44366f6083SPeter Grehan #include <string.h>
45366f6083SPeter Grehan #include <strings.h>
46366f6083SPeter Grehan #include <assert.h>
47028d9311SNeel Natu #include <stdbool.h>
485cf21e48SCorvin Köhne #include <sysexits.h>
49366f6083SPeter Grehan 
50366f6083SPeter Grehan #include <machine/vmm.h>
51483d953aSJohn Baldwin #include <machine/vmm_snapshot.h>
52366f6083SPeter Grehan #include <vmmapi.h>
53366f6083SPeter Grehan 
54e6c8bc29SJohn Baldwin #include "acpi.h"
55e285ef8dSPeter Grehan #include "bhyverun.h"
56621b5090SJohn Baldwin #include "config.h"
57332eff95SVincenzo Maffione #include "debug.h"
58366f6083SPeter Grehan #include "inout.h"
593cbf3585SJohn Baldwin #include "ioapic.h"
604d1e669cSPeter Grehan #include "mem.h"
61366f6083SPeter Grehan #include "pci_emul.h"
62b3e9732aSJohn Baldwin #include "pci_irq.h"
63e6c8bc29SJohn Baldwin #include "pci_lpc.h"
64366f6083SPeter Grehan 
65366f6083SPeter Grehan #define CONF1_ADDR_PORT	   0x0cf8
66366f6083SPeter Grehan #define CONF1_DATA_PORT	   0x0cfc
67366f6083SPeter Grehan 
6875543036SPeter Grehan #define CONF1_ENABLE	   0x80000000ul
6975543036SPeter Grehan 
70d84882caSNeel Natu #define	MAXBUSES	(PCI_BUSMAX + 1)
7199d65389SNeel Natu #define MAXSLOTS	(PCI_SLOTMAX + 1)
7299d65389SNeel Natu #define	MAXFUNCS	(PCI_FUNCMAX + 1)
73366f6083SPeter Grehan 
744a4053e1SCorvin Köhne #define GB		(1024 * 1024 * 1024UL)
754a4053e1SCorvin Köhne 
763cbf3585SJohn Baldwin struct funcinfo {
77621b5090SJohn Baldwin 	nvlist_t *fi_config;
78621b5090SJohn Baldwin 	struct pci_devemu *fi_pde;
793cbf3585SJohn Baldwin 	struct pci_devinst *fi_devi;
803cbf3585SJohn Baldwin };
813cbf3585SJohn Baldwin 
823cbf3585SJohn Baldwin struct intxinfo {
833cbf3585SJohn Baldwin 	int	ii_count;
84b3e9732aSJohn Baldwin 	int	ii_pirq_pin;
853cbf3585SJohn Baldwin 	int	ii_ioapic_irq;
863cbf3585SJohn Baldwin };
873cbf3585SJohn Baldwin 
883cbf3585SJohn Baldwin struct slotinfo {
893cbf3585SJohn Baldwin 	struct intxinfo si_intpins[4];
903cbf3585SJohn Baldwin 	struct funcinfo si_funcs[MAXFUNCS];
91d84882caSNeel Natu };
92d84882caSNeel Natu 
93d84882caSNeel Natu struct businfo {
94d84882caSNeel Natu 	uint16_t iobase, iolimit;		/* I/O window */
95d84882caSNeel Natu 	uint32_t membase32, memlimit32;		/* mmio window below 4GB */
96d84882caSNeel Natu 	uint64_t membase64, memlimit64;		/* mmio window above 4GB */
97d84882caSNeel Natu 	struct slotinfo slotinfo[MAXSLOTS];
98d84882caSNeel Natu };
99d84882caSNeel Natu 
100d84882caSNeel Natu static struct businfo *pci_businfo[MAXBUSES];
101366f6083SPeter Grehan 
102366f6083SPeter Grehan SET_DECLARE(pci_devemu_set, struct pci_devemu);
103366f6083SPeter Grehan 
104366f6083SPeter Grehan static uint64_t pci_emul_iobase;
105e47fe318SCorvin Köhne static uint8_t *pci_emul_rombase;
106e47fe318SCorvin Köhne static uint64_t pci_emul_romoffset;
107e47fe318SCorvin Köhne static uint8_t *pci_emul_romlim;
108366f6083SPeter Grehan static uint64_t pci_emul_membase32;
109366f6083SPeter Grehan static uint64_t pci_emul_membase64;
1109922872bSKonstantin Belousov static uint64_t pci_emul_memlim64;
111366f6083SPeter Grehan 
11201f9362eSCorvin Köhne struct pci_bar_allocation {
11301f9362eSCorvin Köhne 	TAILQ_ENTRY(pci_bar_allocation) chain;
11401f9362eSCorvin Köhne 	struct pci_devinst *pdi;
11501f9362eSCorvin Köhne 	int idx;
11601f9362eSCorvin Köhne 	enum pcibar_type type;
11701f9362eSCorvin Köhne 	uint64_t size;
11801f9362eSCorvin Köhne };
11901f9362eSCorvin Köhne TAILQ_HEAD(pci_bar_list, pci_bar_allocation) pci_bars = TAILQ_HEAD_INITIALIZER(
12001f9362eSCorvin Köhne     pci_bars);
12101f9362eSCorvin Köhne 
122366f6083SPeter Grehan #define	PCI_EMUL_IOBASE		0x2000
123366f6083SPeter Grehan #define	PCI_EMUL_IOLIMIT	0x10000
124366f6083SPeter Grehan 
125e47fe318SCorvin Köhne #define PCI_EMUL_ROMSIZE 0x10000000
126e47fe318SCorvin Köhne 
12712a6eb99SNeel Natu #define	PCI_EMUL_ECFG_BASE	0xE0000000		    /* 3.5GB */
12812a6eb99SNeel Natu #define	PCI_EMUL_ECFG_SIZE	(MAXBUSES * 1024 * 1024)    /* 1MB per bus */
12912a6eb99SNeel Natu SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE);
13012a6eb99SNeel Natu 
1315cf21e48SCorvin Köhne /*
1325cf21e48SCorvin Köhne  * OVMF always uses 0xC0000000 as base address for 32 bit PCI MMIO. Don't
1335cf21e48SCorvin Köhne  * change this address without changing it in OVMF.
1345cf21e48SCorvin Köhne  */
1355cf21e48SCorvin Köhne #define PCI_EMUL_MEMBASE32 0xC0000000
13612a6eb99SNeel Natu #define	PCI_EMUL_MEMLIMIT32	PCI_EMUL_ECFG_BASE
1374a4053e1SCorvin Köhne #define PCI_EMUL_MEMSIZE64	(32*GB)
138366f6083SPeter Grehan 
139621b5090SJohn Baldwin static struct pci_devemu *pci_emul_finddev(const char *name);
140b3e9732aSJohn Baldwin static void pci_lintr_route(struct pci_devinst *pi);
1413cbf3585SJohn Baldwin static void pci_lintr_update(struct pci_devinst *pi);
14212a6eb99SNeel Natu static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot,
14312a6eb99SNeel Natu     int func, int coff, int bytes, uint32_t *val);
144366f6083SPeter Grehan 
14554335630SNeel Natu static __inline void
14654335630SNeel Natu CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes)
14754335630SNeel Natu {
14854335630SNeel Natu 
14954335630SNeel Natu 	if (bytes == 1)
15054335630SNeel Natu 		pci_set_cfgdata8(pi, coff, val);
15154335630SNeel Natu 	else if (bytes == 2)
15254335630SNeel Natu 		pci_set_cfgdata16(pi, coff, val);
15354335630SNeel Natu 	else
15454335630SNeel Natu 		pci_set_cfgdata32(pi, coff, val);
15554335630SNeel Natu }
15654335630SNeel Natu 
15754335630SNeel Natu static __inline uint32_t
15854335630SNeel Natu CFGREAD(struct pci_devinst *pi, int coff, int bytes)
15954335630SNeel Natu {
16054335630SNeel Natu 
16154335630SNeel Natu 	if (bytes == 1)
16254335630SNeel Natu 		return (pci_get_cfgdata8(pi, coff));
16354335630SNeel Natu 	else if (bytes == 2)
16454335630SNeel Natu 		return (pci_get_cfgdata16(pi, coff));
16554335630SNeel Natu 	else
16654335630SNeel Natu 		return (pci_get_cfgdata32(pi, coff));
16754335630SNeel Natu }
16854335630SNeel Natu 
169366f6083SPeter Grehan /*
170366f6083SPeter Grehan  * I/O access
171366f6083SPeter Grehan  */
172366f6083SPeter Grehan 
173366f6083SPeter Grehan /*
174366f6083SPeter Grehan  * Slot options are in the form:
175366f6083SPeter Grehan  *
176d84882caSNeel Natu  *  <bus>:<slot>:<func>,<emul>[,<config>]
17799d65389SNeel Natu  *  <slot>[:<func>],<emul>[,<config>]
178366f6083SPeter Grehan  *
179366f6083SPeter Grehan  *  slot is 0..31
18099d65389SNeel Natu  *  func is 0..7
181366f6083SPeter Grehan  *  emul is a string describing the type of PCI device e.g. virtio-net
182366f6083SPeter Grehan  *  config is an optional string, depending on the device, that can be
183366f6083SPeter Grehan  *  used for configuration.
184366f6083SPeter Grehan  *   Examples are:
185366f6083SPeter Grehan  *     1,virtio-net,tap0
18699d65389SNeel Natu  *     3:0,dummy
187366f6083SPeter Grehan  */
188366f6083SPeter Grehan static void
189366f6083SPeter Grehan pci_parse_slot_usage(char *aopt)
190366f6083SPeter Grehan {
191b05c77ffSNeel Natu 
192332eff95SVincenzo Maffione 	EPRINTLN("Invalid PCI slot info field \"%s\"", aopt);
193366f6083SPeter Grehan }
194366f6083SPeter Grehan 
195621b5090SJohn Baldwin /*
196621b5090SJohn Baldwin  * Helper function to parse a list of comma-separated options where
197621b5090SJohn Baldwin  * each option is formatted as "name[=value]".  If no value is
198621b5090SJohn Baldwin  * provided, the option is treated as a boolean and is given a value
199621b5090SJohn Baldwin  * of true.
200621b5090SJohn Baldwin  */
201621b5090SJohn Baldwin int
202621b5090SJohn Baldwin pci_parse_legacy_config(nvlist_t *nvl, const char *opt)
203621b5090SJohn Baldwin {
204621b5090SJohn Baldwin 	char *config, *name, *tofree, *value;
205621b5090SJohn Baldwin 
206621b5090SJohn Baldwin 	if (opt == NULL)
207621b5090SJohn Baldwin 		return (0);
208621b5090SJohn Baldwin 
209621b5090SJohn Baldwin 	config = tofree = strdup(opt);
210621b5090SJohn Baldwin 	while ((name = strsep(&config, ",")) != NULL) {
211621b5090SJohn Baldwin 		value = strchr(name, '=');
212621b5090SJohn Baldwin 		if (value != NULL) {
213621b5090SJohn Baldwin 			*value = '\0';
214621b5090SJohn Baldwin 			value++;
215621b5090SJohn Baldwin 			set_config_value_node(nvl, name, value);
216621b5090SJohn Baldwin 		} else
217621b5090SJohn Baldwin 			set_config_bool_node(nvl, name, true);
218621b5090SJohn Baldwin 	}
219621b5090SJohn Baldwin 	free(tofree);
220621b5090SJohn Baldwin 	return (0);
221621b5090SJohn Baldwin }
222621b5090SJohn Baldwin 
223621b5090SJohn Baldwin /*
224621b5090SJohn Baldwin  * PCI device configuration is stored in MIBs that encode the device's
225621b5090SJohn Baldwin  * location:
226621b5090SJohn Baldwin  *
227621b5090SJohn Baldwin  * pci.<bus>.<slot>.<func>
228621b5090SJohn Baldwin  *
229621b5090SJohn Baldwin  * Where "bus", "slot", and "func" are all decimal values without
230621b5090SJohn Baldwin  * leading zeroes.  Each valid device must have a "device" node which
231621b5090SJohn Baldwin  * identifies the driver model of the device.
232621b5090SJohn Baldwin  *
233621b5090SJohn Baldwin  * Device backends can provide a parser for the "config" string.  If
234621b5090SJohn Baldwin  * a custom parser is not provided, pci_parse_legacy_config() is used
235621b5090SJohn Baldwin  * to parse the string.
236621b5090SJohn Baldwin  */
237b05c77ffSNeel Natu int
238d2bc4816SJohn Baldwin pci_parse_slot(char *opt)
239366f6083SPeter Grehan {
240621b5090SJohn Baldwin 	char node_name[sizeof("pci.XXX.XX.X")];
241621b5090SJohn Baldwin 	struct pci_devemu *pde;
242d84882caSNeel Natu 	char *emul, *config, *str, *cp;
243d84882caSNeel Natu 	int error, bnum, snum, fnum;
244621b5090SJohn Baldwin 	nvlist_t *nvl;
245366f6083SPeter Grehan 
246b05c77ffSNeel Natu 	error = -1;
247d84882caSNeel Natu 	str = strdup(opt);
24899d65389SNeel Natu 
249d84882caSNeel Natu 	emul = config = NULL;
250d84882caSNeel Natu 	if ((cp = strchr(str, ',')) != NULL) {
251d84882caSNeel Natu 		*cp = '\0';
252d84882caSNeel Natu 		emul = cp + 1;
253d84882caSNeel Natu 		if ((cp = strchr(emul, ',')) != NULL) {
254d84882caSNeel Natu 			*cp = '\0';
255d84882caSNeel Natu 			config = cp + 1;
25699d65389SNeel Natu 		}
257d84882caSNeel Natu 	} else {
258b05c77ffSNeel Natu 		pci_parse_slot_usage(opt);
259b05c77ffSNeel Natu 		goto done;
260366f6083SPeter Grehan 	}
261366f6083SPeter Grehan 
262d84882caSNeel Natu 	/* <bus>:<slot>:<func> */
263d84882caSNeel Natu 	if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) {
264d84882caSNeel Natu 		bnum = 0;
265d84882caSNeel Natu 		/* <slot>:<func> */
266d84882caSNeel Natu 		if (sscanf(str, "%d:%d", &snum, &fnum) != 2) {
267d84882caSNeel Natu 			fnum = 0;
268d84882caSNeel Natu 			/* <slot> */
269d84882caSNeel Natu 			if (sscanf(str, "%d", &snum) != 1) {
270d84882caSNeel Natu 				snum = -1;
271d84882caSNeel Natu 			}
272d84882caSNeel Natu 		}
273d84882caSNeel Natu 	}
274b05c77ffSNeel Natu 
275d84882caSNeel Natu 	if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS ||
276d84882caSNeel Natu 	    fnum < 0 || fnum >= MAXFUNCS) {
277b05c77ffSNeel Natu 		pci_parse_slot_usage(opt);
278b05c77ffSNeel Natu 		goto done;
279b05c77ffSNeel Natu 	}
280b05c77ffSNeel Natu 
281621b5090SJohn Baldwin 	pde = pci_emul_finddev(emul);
282621b5090SJohn Baldwin 	if (pde == NULL) {
283621b5090SJohn Baldwin 		EPRINTLN("pci slot %d:%d:%d: unknown device \"%s\"", bnum, snum,
284621b5090SJohn Baldwin 		    fnum, emul);
285b05c77ffSNeel Natu 		goto done;
286b05c77ffSNeel Natu 	}
287b05c77ffSNeel Natu 
288621b5090SJohn Baldwin 	snprintf(node_name, sizeof(node_name), "pci.%d.%d.%d", bnum, snum,
289621b5090SJohn Baldwin 	    fnum);
290621b5090SJohn Baldwin 	nvl = find_config_node(node_name);
291621b5090SJohn Baldwin 	if (nvl != NULL) {
292621b5090SJohn Baldwin 		EPRINTLN("pci slot %d:%d:%d already occupied!", bnum, snum,
293621b5090SJohn Baldwin 		    fnum);
294b05c77ffSNeel Natu 		goto done;
295b05c77ffSNeel Natu 	}
296621b5090SJohn Baldwin 	nvl = create_config_node(node_name);
297621b5090SJohn Baldwin 	if (pde->pe_alias != NULL)
298621b5090SJohn Baldwin 		set_config_value_node(nvl, "device", pde->pe_alias);
299621b5090SJohn Baldwin 	else
300621b5090SJohn Baldwin 		set_config_value_node(nvl, "device", pde->pe_emu);
301b05c77ffSNeel Natu 
302621b5090SJohn Baldwin 	if (pde->pe_legacy_config != NULL)
303621b5090SJohn Baldwin 		error = pde->pe_legacy_config(nvl, config);
304621b5090SJohn Baldwin 	else
305621b5090SJohn Baldwin 		error = pci_parse_legacy_config(nvl, config);
306b05c77ffSNeel Natu done:
307d84882caSNeel Natu 	free(str);
308b05c77ffSNeel Natu 	return (error);
309366f6083SPeter Grehan }
310366f6083SPeter Grehan 
311657d2158SMarcelo Araujo void
312657d2158SMarcelo Araujo pci_print_supported_devices()
313657d2158SMarcelo Araujo {
314657d2158SMarcelo Araujo 	struct pci_devemu **pdpp, *pdp;
315657d2158SMarcelo Araujo 
316657d2158SMarcelo Araujo 	SET_FOREACH(pdpp, pci_devemu_set) {
317657d2158SMarcelo Araujo 		pdp = *pdpp;
318657d2158SMarcelo Araujo 		printf("%s\n", pdp->pe_emu);
319657d2158SMarcelo Araujo 	}
320657d2158SMarcelo Araujo }
321657d2158SMarcelo Araujo 
322366f6083SPeter Grehan static int
323c9b4e987SNeel Natu pci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset)
324c9b4e987SNeel Natu {
325c9b4e987SNeel Natu 
326c9b4e987SNeel Natu 	if (offset < pi->pi_msix.pba_offset)
327c9b4e987SNeel Natu 		return (0);
328c9b4e987SNeel Natu 
329c9b4e987SNeel Natu 	if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) {
330c9b4e987SNeel Natu 		return (0);
331c9b4e987SNeel Natu 	}
332c9b4e987SNeel Natu 
333c9b4e987SNeel Natu 	return (1);
334c9b4e987SNeel Natu }
335c9b4e987SNeel Natu 
336c9b4e987SNeel Natu int
337c9b4e987SNeel Natu pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size,
338c9b4e987SNeel Natu 		     uint64_t value)
339c9b4e987SNeel Natu {
340c9b4e987SNeel Natu 	int msix_entry_offset;
341c9b4e987SNeel Natu 	int tab_index;
342c9b4e987SNeel Natu 	char *dest;
343c9b4e987SNeel Natu 
344c9b4e987SNeel Natu 	/* support only 4 or 8 byte writes */
345c9b4e987SNeel Natu 	if (size != 4 && size != 8)
346c9b4e987SNeel Natu 		return (-1);
347c9b4e987SNeel Natu 
348c9b4e987SNeel Natu 	/*
349c9b4e987SNeel Natu 	 * Return if table index is beyond what device supports
350c9b4e987SNeel Natu 	 */
351c9b4e987SNeel Natu 	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
352c9b4e987SNeel Natu 	if (tab_index >= pi->pi_msix.table_count)
353c9b4e987SNeel Natu 		return (-1);
354c9b4e987SNeel Natu 
355c9b4e987SNeel Natu 	msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
356c9b4e987SNeel Natu 
357c9b4e987SNeel Natu 	/* support only aligned writes */
358c9b4e987SNeel Natu 	if ((msix_entry_offset % size) != 0)
359c9b4e987SNeel Natu 		return (-1);
360c9b4e987SNeel Natu 
361c9b4e987SNeel Natu 	dest = (char *)(pi->pi_msix.table + tab_index);
362c9b4e987SNeel Natu 	dest += msix_entry_offset;
363c9b4e987SNeel Natu 
364c9b4e987SNeel Natu 	if (size == 4)
365c9b4e987SNeel Natu 		*((uint32_t *)dest) = value;
366c9b4e987SNeel Natu 	else
367c9b4e987SNeel Natu 		*((uint64_t *)dest) = value;
368c9b4e987SNeel Natu 
369c9b4e987SNeel Natu 	return (0);
370c9b4e987SNeel Natu }
371c9b4e987SNeel Natu 
372c9b4e987SNeel Natu uint64_t
373c9b4e987SNeel Natu pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size)
374c9b4e987SNeel Natu {
375c9b4e987SNeel Natu 	char *dest;
376c9b4e987SNeel Natu 	int msix_entry_offset;
377c9b4e987SNeel Natu 	int tab_index;
378c9b4e987SNeel Natu 	uint64_t retval = ~0;
379c9b4e987SNeel Natu 
3806a52209fSNeel Natu 	/*
3816a52209fSNeel Natu 	 * The PCI standard only allows 4 and 8 byte accesses to the MSI-X
382463a577bSEitan Adler 	 * table but we also allow 1 byte access to accommodate reads from
3836a52209fSNeel Natu 	 * ddb.
3846a52209fSNeel Natu 	 */
3856a52209fSNeel Natu 	if (size != 1 && size != 4 && size != 8)
386c9b4e987SNeel Natu 		return (retval);
387c9b4e987SNeel Natu 
388c9b4e987SNeel Natu 	msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
389c9b4e987SNeel Natu 
390c9b4e987SNeel Natu 	/* support only aligned reads */
391c9b4e987SNeel Natu 	if ((msix_entry_offset % size) != 0) {
392c9b4e987SNeel Natu 		return (retval);
393c9b4e987SNeel Natu 	}
394c9b4e987SNeel Natu 
395c9b4e987SNeel Natu 	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
396c9b4e987SNeel Natu 
397c9b4e987SNeel Natu 	if (tab_index < pi->pi_msix.table_count) {
398c9b4e987SNeel Natu 		/* valid MSI-X Table access */
399c9b4e987SNeel Natu 		dest = (char *)(pi->pi_msix.table + tab_index);
400c9b4e987SNeel Natu 		dest += msix_entry_offset;
401c9b4e987SNeel Natu 
4026a52209fSNeel Natu 		if (size == 1)
4036a52209fSNeel Natu 			retval = *((uint8_t *)dest);
4046a52209fSNeel Natu 		else if (size == 4)
405c9b4e987SNeel Natu 			retval = *((uint32_t *)dest);
406c9b4e987SNeel Natu 		else
407c9b4e987SNeel Natu 			retval = *((uint64_t *)dest);
408c9b4e987SNeel Natu 	} else if (pci_valid_pba_offset(pi, offset)) {
409c9b4e987SNeel Natu 		/* return 0 for PBA access */
410c9b4e987SNeel Natu 		retval = 0;
411c9b4e987SNeel Natu 	}
412c9b4e987SNeel Natu 
413c9b4e987SNeel Natu 	return (retval);
414c9b4e987SNeel Natu }
415c9b4e987SNeel Natu 
416aa12663fSNeel Natu int
417aa12663fSNeel Natu pci_msix_table_bar(struct pci_devinst *pi)
418aa12663fSNeel Natu {
419aa12663fSNeel Natu 
420aa12663fSNeel Natu 	if (pi->pi_msix.table != NULL)
421aa12663fSNeel Natu 		return (pi->pi_msix.table_bar);
422aa12663fSNeel Natu 	else
423aa12663fSNeel Natu 		return (-1);
424aa12663fSNeel Natu }
425aa12663fSNeel Natu 
426aa12663fSNeel Natu int
427aa12663fSNeel Natu pci_msix_pba_bar(struct pci_devinst *pi)
428aa12663fSNeel Natu {
429aa12663fSNeel Natu 
430aa12663fSNeel Natu 	if (pi->pi_msix.table != NULL)
431aa12663fSNeel Natu 		return (pi->pi_msix.pba_bar);
432aa12663fSNeel Natu 	else
433aa12663fSNeel Natu 		return (-1);
434aa12663fSNeel Natu }
435aa12663fSNeel Natu 
436c9b4e987SNeel Natu static int
4374d1e669cSPeter Grehan pci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
438366f6083SPeter Grehan 		    uint32_t *eax, void *arg)
439366f6083SPeter Grehan {
440366f6083SPeter Grehan 	struct pci_devinst *pdi = arg;
441366f6083SPeter Grehan 	struct pci_devemu *pe = pdi->pi_d;
4424d1e669cSPeter Grehan 	uint64_t offset;
4434d1e669cSPeter Grehan 	int i;
444366f6083SPeter Grehan 
445366f6083SPeter Grehan 	for (i = 0; i <= PCI_BARMAX; i++) {
446366f6083SPeter Grehan 		if (pdi->pi_bar[i].type == PCIBAR_IO &&
447366f6083SPeter Grehan 		    port >= pdi->pi_bar[i].addr &&
448c9b4e987SNeel Natu 		    port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) {
449366f6083SPeter Grehan 			offset = port - pdi->pi_bar[i].addr;
450366f6083SPeter Grehan 			if (in)
4514d1e669cSPeter Grehan 				*eax = (*pe->pe_barread)(ctx, vcpu, pdi, i,
4524d1e669cSPeter Grehan 							 offset, bytes);
453366f6083SPeter Grehan 			else
4544d1e669cSPeter Grehan 				(*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset,
4554d1e669cSPeter Grehan 						   bytes, *eax);
456366f6083SPeter Grehan 			return (0);
457366f6083SPeter Grehan 		}
458366f6083SPeter Grehan 	}
459366f6083SPeter Grehan 	return (-1);
460366f6083SPeter Grehan }
461366f6083SPeter Grehan 
462366f6083SPeter Grehan static int
4634d1e669cSPeter Grehan pci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
4644d1e669cSPeter Grehan 		     int size, uint64_t *val, void *arg1, long arg2)
4654d1e669cSPeter Grehan {
4664d1e669cSPeter Grehan 	struct pci_devinst *pdi = arg1;
4674d1e669cSPeter Grehan 	struct pci_devemu *pe = pdi->pi_d;
4684d1e669cSPeter Grehan 	uint64_t offset;
4694d1e669cSPeter Grehan 	int bidx = (int) arg2;
4704d1e669cSPeter Grehan 
4714d1e669cSPeter Grehan 	assert(bidx <= PCI_BARMAX);
4724d1e669cSPeter Grehan 	assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 ||
4734d1e669cSPeter Grehan 	       pdi->pi_bar[bidx].type == PCIBAR_MEM64);
4744d1e669cSPeter Grehan 	assert(addr >= pdi->pi_bar[bidx].addr &&
4754d1e669cSPeter Grehan 	       addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size);
4764d1e669cSPeter Grehan 
4774d1e669cSPeter Grehan 	offset = addr - pdi->pi_bar[bidx].addr;
4784d1e669cSPeter Grehan 
479b6ae8b05STycho Nightingale 	if (dir == MEM_F_WRITE) {
48067b6ffaaSTycho Nightingale 		if (size == 8) {
481b6ae8b05STycho Nightingale 			(*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset,
482b6ae8b05STycho Nightingale 					   4, *val & 0xffffffff);
483b6ae8b05STycho Nightingale 			(*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset + 4,
484b6ae8b05STycho Nightingale 					   4, *val >> 32);
485b6ae8b05STycho Nightingale 		} else {
486b6ae8b05STycho Nightingale 			(*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset,
487b6ae8b05STycho Nightingale 					   size, *val);
488b6ae8b05STycho Nightingale 		}
489b6ae8b05STycho Nightingale 	} else {
49067b6ffaaSTycho Nightingale 		if (size == 8) {
491b6ae8b05STycho Nightingale 			*val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx,
492b6ae8b05STycho Nightingale 						 offset, 4);
493b6ae8b05STycho Nightingale 			*val |= (*pe->pe_barread)(ctx, vcpu, pdi, bidx,
494b6ae8b05STycho Nightingale 						  offset + 4, 4) << 32;
495b6ae8b05STycho Nightingale 		} else {
496b6ae8b05STycho Nightingale 			*val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx,
497b6ae8b05STycho Nightingale 						 offset, size);
498b6ae8b05STycho Nightingale 		}
499b6ae8b05STycho Nightingale 	}
5004d1e669cSPeter Grehan 
5014d1e669cSPeter Grehan 	return (0);
5024d1e669cSPeter Grehan }
5034d1e669cSPeter Grehan 
5044d1e669cSPeter Grehan 
5054d1e669cSPeter Grehan static int
506366f6083SPeter Grehan pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size,
507366f6083SPeter Grehan 			uint64_t *addr)
508366f6083SPeter Grehan {
509366f6083SPeter Grehan 	uint64_t base;
510366f6083SPeter Grehan 
511366f6083SPeter Grehan 	assert((size & (size - 1)) == 0);	/* must be a power of 2 */
512366f6083SPeter Grehan 
513366f6083SPeter Grehan 	base = roundup2(*baseptr, size);
514366f6083SPeter Grehan 
515366f6083SPeter Grehan 	if (base + size <= limit) {
516366f6083SPeter Grehan 		*addr = base;
517366f6083SPeter Grehan 		*baseptr = base + size;
518366f6083SPeter Grehan 		return (0);
519366f6083SPeter Grehan 	} else
520366f6083SPeter Grehan 		return (-1);
521366f6083SPeter Grehan }
522366f6083SPeter Grehan 
523028d9311SNeel Natu /*
524028d9311SNeel Natu  * Register (or unregister) the MMIO or I/O region associated with the BAR
525028d9311SNeel Natu  * register 'idx' of an emulated pci device.
526028d9311SNeel Natu  */
527028d9311SNeel Natu static void
528028d9311SNeel Natu modify_bar_registration(struct pci_devinst *pi, int idx, int registration)
529028d9311SNeel Natu {
530f8a6ec2dSD Scott Phillips 	struct pci_devemu *pe;
531028d9311SNeel Natu 	int error;
532028d9311SNeel Natu 	struct inout_port iop;
533028d9311SNeel Natu 	struct mem_range mr;
534028d9311SNeel Natu 
535f8a6ec2dSD Scott Phillips 	pe = pi->pi_d;
536028d9311SNeel Natu 	switch (pi->pi_bar[idx].type) {
537028d9311SNeel Natu 	case PCIBAR_IO:
538028d9311SNeel Natu 		bzero(&iop, sizeof(struct inout_port));
539028d9311SNeel Natu 		iop.name = pi->pi_name;
540028d9311SNeel Natu 		iop.port = pi->pi_bar[idx].addr;
541028d9311SNeel Natu 		iop.size = pi->pi_bar[idx].size;
542028d9311SNeel Natu 		if (registration) {
543028d9311SNeel Natu 			iop.flags = IOPORT_F_INOUT;
544028d9311SNeel Natu 			iop.handler = pci_emul_io_handler;
545028d9311SNeel Natu 			iop.arg = pi;
546028d9311SNeel Natu 			error = register_inout(&iop);
547028d9311SNeel Natu 		} else
548028d9311SNeel Natu 			error = unregister_inout(&iop);
549f8a6ec2dSD Scott Phillips 		if (pe->pe_baraddr != NULL)
550f8a6ec2dSD Scott Phillips 			(*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration,
551f8a6ec2dSD Scott Phillips 					  pi->pi_bar[idx].addr);
552028d9311SNeel Natu 		break;
553028d9311SNeel Natu 	case PCIBAR_MEM32:
554028d9311SNeel Natu 	case PCIBAR_MEM64:
555028d9311SNeel Natu 		bzero(&mr, sizeof(struct mem_range));
556028d9311SNeel Natu 		mr.name = pi->pi_name;
557028d9311SNeel Natu 		mr.base = pi->pi_bar[idx].addr;
558028d9311SNeel Natu 		mr.size = pi->pi_bar[idx].size;
559028d9311SNeel Natu 		if (registration) {
560028d9311SNeel Natu 			mr.flags = MEM_F_RW;
561028d9311SNeel Natu 			mr.handler = pci_emul_mem_handler;
562028d9311SNeel Natu 			mr.arg1 = pi;
563028d9311SNeel Natu 			mr.arg2 = idx;
564028d9311SNeel Natu 			error = register_mem(&mr);
565028d9311SNeel Natu 		} else
566028d9311SNeel Natu 			error = unregister_mem(&mr);
567f8a6ec2dSD Scott Phillips 		if (pe->pe_baraddr != NULL)
568f8a6ec2dSD Scott Phillips 			(*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration,
569f8a6ec2dSD Scott Phillips 					  pi->pi_bar[idx].addr);
570028d9311SNeel Natu 		break;
571e47fe318SCorvin Köhne 	case PCIBAR_ROM:
572e47fe318SCorvin Köhne 		error = 0;
573e47fe318SCorvin Köhne 		if (pe->pe_baraddr != NULL)
574e47fe318SCorvin Köhne 			(*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration,
575e47fe318SCorvin Köhne 			    pi->pi_bar[idx].addr);
576e47fe318SCorvin Köhne 		break;
577028d9311SNeel Natu 	default:
578028d9311SNeel Natu 		error = EINVAL;
579028d9311SNeel Natu 		break;
580028d9311SNeel Natu 	}
581028d9311SNeel Natu 	assert(error == 0);
582028d9311SNeel Natu }
583028d9311SNeel Natu 
584028d9311SNeel Natu static void
585028d9311SNeel Natu unregister_bar(struct pci_devinst *pi, int idx)
586028d9311SNeel Natu {
587028d9311SNeel Natu 
588028d9311SNeel Natu 	modify_bar_registration(pi, idx, 0);
589028d9311SNeel Natu }
590028d9311SNeel Natu 
591028d9311SNeel Natu static void
592028d9311SNeel Natu register_bar(struct pci_devinst *pi, int idx)
593028d9311SNeel Natu {
594028d9311SNeel Natu 
595028d9311SNeel Natu 	modify_bar_registration(pi, idx, 1);
596028d9311SNeel Natu }
597028d9311SNeel Natu 
598e47fe318SCorvin Köhne /* Is the ROM enabled for the emulated pci device? */
599e47fe318SCorvin Köhne static int
600e47fe318SCorvin Köhne romen(struct pci_devinst *pi)
601e47fe318SCorvin Köhne {
602e47fe318SCorvin Köhne 	return (pi->pi_bar[PCI_ROM_IDX].lobits & PCIM_BIOS_ENABLE) ==
603e47fe318SCorvin Köhne 	    PCIM_BIOS_ENABLE;
604e47fe318SCorvin Köhne }
605e47fe318SCorvin Köhne 
606028d9311SNeel Natu /* Are we decoding i/o port accesses for the emulated pci device? */
607028d9311SNeel Natu static int
608028d9311SNeel Natu porten(struct pci_devinst *pi)
609028d9311SNeel Natu {
610028d9311SNeel Natu 	uint16_t cmd;
611028d9311SNeel Natu 
612028d9311SNeel Natu 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
613028d9311SNeel Natu 
614028d9311SNeel Natu 	return (cmd & PCIM_CMD_PORTEN);
615028d9311SNeel Natu }
616028d9311SNeel Natu 
617028d9311SNeel Natu /* Are we decoding memory accesses for the emulated pci device? */
618028d9311SNeel Natu static int
619028d9311SNeel Natu memen(struct pci_devinst *pi)
620028d9311SNeel Natu {
621028d9311SNeel Natu 	uint16_t cmd;
622028d9311SNeel Natu 
623028d9311SNeel Natu 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
624028d9311SNeel Natu 
625028d9311SNeel Natu 	return (cmd & PCIM_CMD_MEMEN);
626028d9311SNeel Natu }
627028d9311SNeel Natu 
628028d9311SNeel Natu /*
629028d9311SNeel Natu  * Update the MMIO or I/O address that is decoded by the BAR register.
630028d9311SNeel Natu  *
631028d9311SNeel Natu  * If the pci device has enabled the address space decoding then intercept
632028d9311SNeel Natu  * the address range decoded by the BAR register.
633028d9311SNeel Natu  */
634028d9311SNeel Natu static void
635028d9311SNeel Natu update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type)
636028d9311SNeel Natu {
637028d9311SNeel Natu 	int decode;
638028d9311SNeel Natu 
639028d9311SNeel Natu 	if (pi->pi_bar[idx].type == PCIBAR_IO)
640028d9311SNeel Natu 		decode = porten(pi);
641028d9311SNeel Natu 	else
642028d9311SNeel Natu 		decode = memen(pi);
643028d9311SNeel Natu 
644028d9311SNeel Natu 	if (decode)
645028d9311SNeel Natu 		unregister_bar(pi, idx);
646028d9311SNeel Natu 
647028d9311SNeel Natu 	switch (type) {
648028d9311SNeel Natu 	case PCIBAR_IO:
649028d9311SNeel Natu 	case PCIBAR_MEM32:
650028d9311SNeel Natu 		pi->pi_bar[idx].addr = addr;
651028d9311SNeel Natu 		break;
652028d9311SNeel Natu 	case PCIBAR_MEM64:
653028d9311SNeel Natu 		pi->pi_bar[idx].addr &= ~0xffffffffUL;
654028d9311SNeel Natu 		pi->pi_bar[idx].addr |= addr;
655028d9311SNeel Natu 		break;
656028d9311SNeel Natu 	case PCIBAR_MEMHI64:
657028d9311SNeel Natu 		pi->pi_bar[idx].addr &= 0xffffffff;
658028d9311SNeel Natu 		pi->pi_bar[idx].addr |= addr;
659028d9311SNeel Natu 		break;
660028d9311SNeel Natu 	default:
661028d9311SNeel Natu 		assert(0);
662028d9311SNeel Natu 	}
663028d9311SNeel Natu 
664028d9311SNeel Natu 	if (decode)
665028d9311SNeel Natu 		register_bar(pi, idx);
666028d9311SNeel Natu }
667028d9311SNeel Natu 
6684d1e669cSPeter Grehan int
669038f5c7bSKonstantin Belousov pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type,
670038f5c7bSKonstantin Belousov     uint64_t size)
671366f6083SPeter Grehan {
672e47fe318SCorvin Köhne 	assert((type == PCIBAR_ROM) || (idx >= 0 && idx <= PCI_BARMAX));
673e47fe318SCorvin Köhne 	assert((type != PCIBAR_ROM) || (idx == PCI_ROM_IDX));
674366f6083SPeter Grehan 
675366f6083SPeter Grehan 	if ((size & (size - 1)) != 0)
676366f6083SPeter Grehan 		size = 1UL << flsl(size);	/* round up to a power of 2 */
677366f6083SPeter Grehan 
678028d9311SNeel Natu 	/* Enforce minimum BAR sizes required by the PCI standard */
679028d9311SNeel Natu 	if (type == PCIBAR_IO) {
680028d9311SNeel Natu 		if (size < 4)
681028d9311SNeel Natu 			size = 4;
682e47fe318SCorvin Köhne 	} else if (type == PCIBAR_ROM) {
683e47fe318SCorvin Köhne 		if (size < ~PCIM_BIOS_ADDR_MASK + 1)
684e47fe318SCorvin Köhne 			size = ~PCIM_BIOS_ADDR_MASK + 1;
685028d9311SNeel Natu 	} else {
686028d9311SNeel Natu 		if (size < 16)
687028d9311SNeel Natu 			size = 16;
688028d9311SNeel Natu 	}
689028d9311SNeel Natu 
69001f9362eSCorvin Köhne 	/*
69101f9362eSCorvin Köhne 	 * To reduce fragmentation of the MMIO space, we allocate the BARs by
69201f9362eSCorvin Köhne 	 * size. Therefore, don't allocate the BAR yet. We create a list of all
69301f9362eSCorvin Köhne 	 * BAR allocation which is sorted by BAR size. When all PCI devices are
69401f9362eSCorvin Köhne 	 * initialized, we will assign an address to the BARs.
69501f9362eSCorvin Köhne 	 */
69601f9362eSCorvin Köhne 
69701f9362eSCorvin Köhne 	/* create a new list entry */
69801f9362eSCorvin Köhne 	struct pci_bar_allocation *const new_bar = malloc(sizeof(*new_bar));
69901f9362eSCorvin Köhne 	memset(new_bar, 0, sizeof(*new_bar));
70001f9362eSCorvin Köhne 	new_bar->pdi = pdi;
70101f9362eSCorvin Köhne 	new_bar->idx = idx;
70201f9362eSCorvin Köhne 	new_bar->type = type;
70301f9362eSCorvin Köhne 	new_bar->size = size;
70401f9362eSCorvin Köhne 
70501f9362eSCorvin Köhne 	/*
70601f9362eSCorvin Köhne 	 * Search for a BAR which size is lower than the size of our newly
70701f9362eSCorvin Köhne 	 * allocated BAR.
70801f9362eSCorvin Köhne 	 */
70901f9362eSCorvin Köhne 	struct pci_bar_allocation *bar = NULL;
71001f9362eSCorvin Köhne 	TAILQ_FOREACH(bar, &pci_bars, chain) {
71101f9362eSCorvin Köhne 		if (bar->size < size) {
71201f9362eSCorvin Köhne 			break;
71301f9362eSCorvin Köhne 		}
71401f9362eSCorvin Köhne 	}
71501f9362eSCorvin Köhne 
71601f9362eSCorvin Köhne 	if (bar == NULL) {
71701f9362eSCorvin Köhne 		/*
71801f9362eSCorvin Köhne 		 * Either the list is empty or new BAR is the smallest BAR of
71901f9362eSCorvin Köhne 		 * the list. Append it to the end of our list.
72001f9362eSCorvin Köhne 		 */
72101f9362eSCorvin Köhne 		TAILQ_INSERT_TAIL(&pci_bars, new_bar, chain);
72201f9362eSCorvin Köhne 	} else {
72301f9362eSCorvin Köhne 		/*
72401f9362eSCorvin Köhne 		 * The found BAR is smaller than our new BAR. For that reason,
72501f9362eSCorvin Köhne 		 * insert our new BAR before the found BAR.
72601f9362eSCorvin Köhne 		 */
72701f9362eSCorvin Köhne 		TAILQ_INSERT_BEFORE(bar, new_bar, chain);
72801f9362eSCorvin Köhne 	}
72901f9362eSCorvin Köhne 
73001f9362eSCorvin Köhne 	/*
73101f9362eSCorvin Köhne 	 * pci_passthru devices synchronize their physical and virtual command
73201f9362eSCorvin Köhne 	 * register on init. For that reason, the virtual cmd reg should be
73301f9362eSCorvin Köhne 	 * updated as early as possible.
73401f9362eSCorvin Köhne 	 */
73501f9362eSCorvin Köhne 	uint16_t enbit = 0;
73601f9362eSCorvin Köhne 	switch (type) {
73701f9362eSCorvin Köhne 	case PCIBAR_IO:
73801f9362eSCorvin Köhne 		enbit = PCIM_CMD_PORTEN;
73901f9362eSCorvin Köhne 		break;
74001f9362eSCorvin Köhne 	case PCIBAR_MEM64:
74101f9362eSCorvin Köhne 	case PCIBAR_MEM32:
74201f9362eSCorvin Köhne 		enbit = PCIM_CMD_MEMEN;
74301f9362eSCorvin Köhne 		break;
74401f9362eSCorvin Köhne 	default:
74501f9362eSCorvin Köhne 		enbit = 0;
74601f9362eSCorvin Köhne 		break;
74701f9362eSCorvin Köhne 	}
74801f9362eSCorvin Köhne 
74901f9362eSCorvin Köhne 	const uint16_t cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND);
75001f9362eSCorvin Köhne 	pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit);
75101f9362eSCorvin Köhne 
75201f9362eSCorvin Köhne 	return (0);
75301f9362eSCorvin Köhne }
75401f9362eSCorvin Köhne 
75501f9362eSCorvin Köhne static int
75601f9362eSCorvin Köhne pci_emul_assign_bar(struct pci_devinst *const pdi, const int idx,
75701f9362eSCorvin Köhne     const enum pcibar_type type, const uint64_t size)
75801f9362eSCorvin Köhne {
75901f9362eSCorvin Köhne 	int error;
76001f9362eSCorvin Köhne 	uint64_t *baseptr, limit, addr, mask, lobits, bar;
76101f9362eSCorvin Köhne 
762366f6083SPeter Grehan 	switch (type) {
763366f6083SPeter Grehan 	case PCIBAR_NONE:
764366f6083SPeter Grehan 		baseptr = NULL;
76501f9362eSCorvin Köhne 		addr = mask = lobits = 0;
766366f6083SPeter Grehan 		break;
767366f6083SPeter Grehan 	case PCIBAR_IO:
768366f6083SPeter Grehan 		baseptr = &pci_emul_iobase;
769366f6083SPeter Grehan 		limit = PCI_EMUL_IOLIMIT;
770366f6083SPeter Grehan 		mask = PCIM_BAR_IO_BASE;
771366f6083SPeter Grehan 		lobits = PCIM_BAR_IO_SPACE;
772366f6083SPeter Grehan 		break;
773366f6083SPeter Grehan 	case PCIBAR_MEM64:
774366f6083SPeter Grehan 		/*
775366f6083SPeter Grehan 		 * XXX
776366f6083SPeter Grehan 		 * Some drivers do not work well if the 64-bit BAR is allocated
777366f6083SPeter Grehan 		 * above 4GB. Allow for this by allocating small requests under
778366f6083SPeter Grehan 		 * 4GB unless then allocation size is larger than some arbitrary
779670b364bSKonstantin Belousov 		 * number (128MB currently).
780366f6083SPeter Grehan 		 */
781670b364bSKonstantin Belousov 		if (size > 128 * 1024 * 1024) {
782366f6083SPeter Grehan 			baseptr = &pci_emul_membase64;
7839922872bSKonstantin Belousov 			limit = pci_emul_memlim64;
784366f6083SPeter Grehan 			mask = PCIM_BAR_MEM_BASE;
785366f6083SPeter Grehan 			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
786366f6083SPeter Grehan 				 PCIM_BAR_MEM_PREFETCH;
78725d4944eSNeel Natu 		} else {
78825d4944eSNeel Natu 			baseptr = &pci_emul_membase32;
78925d4944eSNeel Natu 			limit = PCI_EMUL_MEMLIMIT32;
79025d4944eSNeel Natu 			mask = PCIM_BAR_MEM_BASE;
79125d4944eSNeel Natu 			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
792366f6083SPeter Grehan 		}
79325d4944eSNeel Natu 		break;
794366f6083SPeter Grehan 	case PCIBAR_MEM32:
795366f6083SPeter Grehan 		baseptr = &pci_emul_membase32;
796366f6083SPeter Grehan 		limit = PCI_EMUL_MEMLIMIT32;
797366f6083SPeter Grehan 		mask = PCIM_BAR_MEM_BASE;
798366f6083SPeter Grehan 		lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
799366f6083SPeter Grehan 		break;
800e47fe318SCorvin Köhne 	case PCIBAR_ROM:
801e47fe318SCorvin Köhne 		/* do not claim memory for ROM. OVMF will do it for us. */
802e47fe318SCorvin Köhne 		baseptr = NULL;
803e47fe318SCorvin Köhne 		limit = 0;
804e47fe318SCorvin Köhne 		mask = PCIM_BIOS_ADDR_MASK;
805e47fe318SCorvin Köhne 		lobits = 0;
806e47fe318SCorvin Köhne 		break;
807366f6083SPeter Grehan 	default:
808366f6083SPeter Grehan 		printf("pci_emul_alloc_base: invalid bar type %d\n", type);
809366f6083SPeter Grehan 		assert(0);
810366f6083SPeter Grehan 	}
811366f6083SPeter Grehan 
812366f6083SPeter Grehan 	if (baseptr != NULL) {
813366f6083SPeter Grehan 		error = pci_emul_alloc_resource(baseptr, limit, size, &addr);
814366f6083SPeter Grehan 		if (error != 0)
815366f6083SPeter Grehan 			return (error);
816366f6083SPeter Grehan 	}
817366f6083SPeter Grehan 
818366f6083SPeter Grehan 	pdi->pi_bar[idx].type = type;
819366f6083SPeter Grehan 	pdi->pi_bar[idx].addr = addr;
820366f6083SPeter Grehan 	pdi->pi_bar[idx].size = size;
821e87a6f3eSCorvin Köhne 	/*
822e87a6f3eSCorvin Köhne 	 * passthru devices are using same lobits as physical device they set
823e87a6f3eSCorvin Köhne 	 * this property
824e87a6f3eSCorvin Köhne 	 */
825e87a6f3eSCorvin Köhne 	if (pdi->pi_bar[idx].lobits != 0) {
826e87a6f3eSCorvin Köhne 		lobits = pdi->pi_bar[idx].lobits;
827e87a6f3eSCorvin Köhne 	} else {
828e87a6f3eSCorvin Köhne 		pdi->pi_bar[idx].lobits = lobits;
829e87a6f3eSCorvin Köhne 	}
830366f6083SPeter Grehan 
831366f6083SPeter Grehan 	/* Initialize the BAR register in config space */
832366f6083SPeter Grehan 	bar = (addr & mask) | lobits;
833366f6083SPeter Grehan 	pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar);
834366f6083SPeter Grehan 
835366f6083SPeter Grehan 	if (type == PCIBAR_MEM64) {
836366f6083SPeter Grehan 		assert(idx + 1 <= PCI_BARMAX);
837366f6083SPeter Grehan 		pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64;
838366f6083SPeter Grehan 		pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
839366f6083SPeter Grehan 	}
840366f6083SPeter Grehan 
841e47fe318SCorvin Köhne 	if (type != PCIBAR_ROM) {
842028d9311SNeel Natu 		register_bar(pdi, idx);
843e47fe318SCorvin Köhne 	}
844e47fe318SCorvin Köhne 
845e47fe318SCorvin Köhne 	return (0);
846e47fe318SCorvin Köhne }
847e47fe318SCorvin Köhne 
848e47fe318SCorvin Köhne int
849e47fe318SCorvin Köhne pci_emul_alloc_rom(struct pci_devinst *const pdi, const uint64_t size,
850e47fe318SCorvin Köhne     void **const addr)
851e47fe318SCorvin Köhne {
852e47fe318SCorvin Köhne 	/* allocate ROM space once on first call */
853e47fe318SCorvin Köhne 	if (pci_emul_rombase == 0) {
854e47fe318SCorvin Köhne 		pci_emul_rombase = vm_create_devmem(pdi->pi_vmctx, VM_PCIROM,
855e47fe318SCorvin Köhne 		    "pcirom", PCI_EMUL_ROMSIZE);
856e47fe318SCorvin Köhne 		if (pci_emul_rombase == MAP_FAILED) {
857e47fe318SCorvin Köhne 			warnx("%s: failed to create rom segment", __func__);
858e47fe318SCorvin Köhne 			return (-1);
859e47fe318SCorvin Köhne 		}
860e47fe318SCorvin Köhne 		pci_emul_romlim = pci_emul_rombase + PCI_EMUL_ROMSIZE;
861e47fe318SCorvin Köhne 		pci_emul_romoffset = 0;
862e47fe318SCorvin Köhne 	}
863e47fe318SCorvin Köhne 
864e47fe318SCorvin Köhne 	/* ROM size should be a power of 2 and greater than 2 KB */
865e47fe318SCorvin Köhne 	const uint64_t rom_size = MAX(1UL << flsl(size),
866e47fe318SCorvin Köhne 	    ~PCIM_BIOS_ADDR_MASK + 1);
867e47fe318SCorvin Köhne 
868e47fe318SCorvin Köhne 	/* check if ROM fits into ROM space */
869e47fe318SCorvin Köhne 	if (pci_emul_romoffset + rom_size > PCI_EMUL_ROMSIZE) {
870e47fe318SCorvin Köhne 		warnx("%s: no space left in rom segment:", __func__);
871e47fe318SCorvin Köhne 		warnx("%16lu bytes left",
872e47fe318SCorvin Köhne 		    PCI_EMUL_ROMSIZE - pci_emul_romoffset);
873e47fe318SCorvin Köhne 		warnx("%16lu bytes required by %d/%d/%d", rom_size, pdi->pi_bus,
874e47fe318SCorvin Köhne 		    pdi->pi_slot, pdi->pi_func);
875e47fe318SCorvin Köhne 		return (-1);
876e47fe318SCorvin Köhne 	}
877e47fe318SCorvin Köhne 
878e47fe318SCorvin Köhne 	/* allocate ROM BAR */
879e47fe318SCorvin Köhne 	const int error = pci_emul_alloc_bar(pdi, PCI_ROM_IDX, PCIBAR_ROM,
880e47fe318SCorvin Köhne 	    rom_size);
881e47fe318SCorvin Köhne 	if (error)
882e47fe318SCorvin Köhne 		return error;
883e47fe318SCorvin Köhne 
884e47fe318SCorvin Köhne 	/* return address */
885e47fe318SCorvin Köhne 	*addr = pci_emul_rombase + pci_emul_romoffset;
886e47fe318SCorvin Köhne 
887e47fe318SCorvin Köhne 	/* save offset into ROM Space */
888e47fe318SCorvin Köhne 	pdi->pi_romoffset = pci_emul_romoffset;
889e47fe318SCorvin Köhne 
890e47fe318SCorvin Köhne 	/* increase offset for next ROM */
891e47fe318SCorvin Köhne 	pci_emul_romoffset += rom_size;
892366f6083SPeter Grehan 
893366f6083SPeter Grehan 	return (0);
894366f6083SPeter Grehan }
895366f6083SPeter Grehan 
896366f6083SPeter Grehan #define	CAP_START_OFFSET	0x40
897366f6083SPeter Grehan static int
898366f6083SPeter Grehan pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen)
899366f6083SPeter Grehan {
900a96b8b80SJohn Baldwin 	int i, capoff, reallen;
901366f6083SPeter Grehan 	uint16_t sts;
902366f6083SPeter Grehan 
903a96b8b80SJohn Baldwin 	assert(caplen > 0);
904366f6083SPeter Grehan 
905366f6083SPeter Grehan 	reallen = roundup2(caplen, 4);		/* dword aligned */
906366f6083SPeter Grehan 
907366f6083SPeter Grehan 	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
908a96b8b80SJohn Baldwin 	if ((sts & PCIM_STATUS_CAPPRESENT) == 0)
909366f6083SPeter Grehan 		capoff = CAP_START_OFFSET;
910a96b8b80SJohn Baldwin 	else
911a96b8b80SJohn Baldwin 		capoff = pi->pi_capend + 1;
912366f6083SPeter Grehan 
913366f6083SPeter Grehan 	/* Check if we have enough space */
914a96b8b80SJohn Baldwin 	if (capoff + reallen > PCI_REGMAX + 1)
915366f6083SPeter Grehan 		return (-1);
916366f6083SPeter Grehan 
917a96b8b80SJohn Baldwin 	/* Set the previous capability pointer */
918a96b8b80SJohn Baldwin 	if ((sts & PCIM_STATUS_CAPPRESENT) == 0) {
919a96b8b80SJohn Baldwin 		pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff);
920a96b8b80SJohn Baldwin 		pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT);
921a96b8b80SJohn Baldwin 	} else
922a96b8b80SJohn Baldwin 		pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff);
923a96b8b80SJohn Baldwin 
924366f6083SPeter Grehan 	/* Copy the capability */
925366f6083SPeter Grehan 	for (i = 0; i < caplen; i++)
926366f6083SPeter Grehan 		pci_set_cfgdata8(pi, capoff + i, capdata[i]);
927366f6083SPeter Grehan 
928366f6083SPeter Grehan 	/* Set the next capability pointer */
929a96b8b80SJohn Baldwin 	pci_set_cfgdata8(pi, capoff + 1, 0);
930366f6083SPeter Grehan 
931a96b8b80SJohn Baldwin 	pi->pi_prevcap = capoff;
932a96b8b80SJohn Baldwin 	pi->pi_capend = capoff + reallen - 1;
933366f6083SPeter Grehan 	return (0);
934366f6083SPeter Grehan }
935366f6083SPeter Grehan 
936366f6083SPeter Grehan static struct pci_devemu *
937621b5090SJohn Baldwin pci_emul_finddev(const char *name)
938366f6083SPeter Grehan {
939366f6083SPeter Grehan 	struct pci_devemu **pdpp, *pdp;
940366f6083SPeter Grehan 
941366f6083SPeter Grehan 	SET_FOREACH(pdpp, pci_devemu_set) {
942366f6083SPeter Grehan 		pdp = *pdpp;
943366f6083SPeter Grehan 		if (!strcmp(pdp->pe_emu, name)) {
944366f6083SPeter Grehan 			return (pdp);
945366f6083SPeter Grehan 		}
946366f6083SPeter Grehan 	}
947366f6083SPeter Grehan 
948366f6083SPeter Grehan 	return (NULL);
949366f6083SPeter Grehan }
950366f6083SPeter Grehan 
951a38e2a64SPeter Grehan static int
952d84882caSNeel Natu pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot,
953d84882caSNeel Natu     int func, struct funcinfo *fi)
954366f6083SPeter Grehan {
955366f6083SPeter Grehan 	struct pci_devinst *pdi;
956a38e2a64SPeter Grehan 	int err;
957a38e2a64SPeter Grehan 
958994f858aSXin LI 	pdi = calloc(1, sizeof(struct pci_devinst));
959366f6083SPeter Grehan 
960366f6083SPeter Grehan 	pdi->pi_vmctx = ctx;
961d84882caSNeel Natu 	pdi->pi_bus = bus;
962366f6083SPeter Grehan 	pdi->pi_slot = slot;
96399d65389SNeel Natu 	pdi->pi_func = func;
9643cbf3585SJohn Baldwin 	pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
9653cbf3585SJohn Baldwin 	pdi->pi_lintr.pin = 0;
9663cbf3585SJohn Baldwin 	pdi->pi_lintr.state = IDLE;
967b3e9732aSJohn Baldwin 	pdi->pi_lintr.pirq_pin = 0;
9683cbf3585SJohn Baldwin 	pdi->pi_lintr.ioapic_irq = 0;
969366f6083SPeter Grehan 	pdi->pi_d = pde;
970366f6083SPeter Grehan 	snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
971366f6083SPeter Grehan 
972366f6083SPeter Grehan 	/* Disable legacy interrupts */
973366f6083SPeter Grehan 	pci_set_cfgdata8(pdi, PCIR_INTLINE, 255);
974366f6083SPeter Grehan 	pci_set_cfgdata8(pdi, PCIR_INTPIN, 0);
975366f6083SPeter Grehan 
9762729c9bbSJohn Baldwin 	pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN);
977366f6083SPeter Grehan 
978621b5090SJohn Baldwin 	err = (*pde->pe_init)(ctx, pdi, fi->fi_config);
979d84882caSNeel Natu 	if (err == 0)
980d84882caSNeel Natu 		fi->fi_devi = pdi;
981d84882caSNeel Natu 	else
982366f6083SPeter Grehan 		free(pdi);
983a38e2a64SPeter Grehan 
984a38e2a64SPeter Grehan 	return (err);
985366f6083SPeter Grehan }
986366f6083SPeter Grehan 
987366f6083SPeter Grehan void
988366f6083SPeter Grehan pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr)
989366f6083SPeter Grehan {
990366f6083SPeter Grehan 	int mmc;
991366f6083SPeter Grehan 
992366f6083SPeter Grehan 	/* Number of msi messages must be a power of 2 between 1 and 32 */
993366f6083SPeter Grehan 	assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32);
994366f6083SPeter Grehan 	mmc = ffs(msgnum) - 1;
995366f6083SPeter Grehan 
996366f6083SPeter Grehan 	bzero(msicap, sizeof(struct msicap));
997366f6083SPeter Grehan 	msicap->capid = PCIY_MSI;
998366f6083SPeter Grehan 	msicap->nextptr = nextptr;
999366f6083SPeter Grehan 	msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1);
1000366f6083SPeter Grehan }
1001366f6083SPeter Grehan 
1002366f6083SPeter Grehan int
1003366f6083SPeter Grehan pci_emul_add_msicap(struct pci_devinst *pi, int msgnum)
1004366f6083SPeter Grehan {
1005366f6083SPeter Grehan 	struct msicap msicap;
1006366f6083SPeter Grehan 
1007366f6083SPeter Grehan 	pci_populate_msicap(&msicap, msgnum, 0);
1008366f6083SPeter Grehan 
1009366f6083SPeter Grehan 	return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap)));
1010366f6083SPeter Grehan }
1011366f6083SPeter Grehan 
1012c9b4e987SNeel Natu static void
1013c9b4e987SNeel Natu pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum,
1014a96b8b80SJohn Baldwin 		     uint32_t msix_tab_size)
1015c9b4e987SNeel Natu {
1016c9b4e987SNeel Natu 
1017c9b4e987SNeel Natu 	assert(msix_tab_size % 4096 == 0);
1018c9b4e987SNeel Natu 
1019c9b4e987SNeel Natu 	bzero(msixcap, sizeof(struct msixcap));
1020c9b4e987SNeel Natu 	msixcap->capid = PCIY_MSIX;
1021c9b4e987SNeel Natu 
1022c9b4e987SNeel Natu 	/*
1023c9b4e987SNeel Natu 	 * Message Control Register, all fields set to
1024c9b4e987SNeel Natu 	 * zero except for the Table Size.
1025c9b4e987SNeel Natu 	 * Note: Table size N is encoded as N-1
1026c9b4e987SNeel Natu 	 */
1027c9b4e987SNeel Natu 	msixcap->msgctrl = msgnum - 1;
1028c9b4e987SNeel Natu 
1029c9b4e987SNeel Natu 	/*
1030c9b4e987SNeel Natu 	 * MSI-X BAR setup:
1031c9b4e987SNeel Natu 	 * - MSI-X table start at offset 0
1032c9b4e987SNeel Natu 	 * - PBA table starts at a 4K aligned offset after the MSI-X table
1033c9b4e987SNeel Natu 	 */
1034c9b4e987SNeel Natu 	msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK;
1035c9b4e987SNeel Natu 	msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK);
1036c9b4e987SNeel Natu }
1037c9b4e987SNeel Natu 
1038c9b4e987SNeel Natu static void
1039c9b4e987SNeel Natu pci_msix_table_init(struct pci_devinst *pi, int table_entries)
1040c9b4e987SNeel Natu {
1041c9b4e987SNeel Natu 	int i, table_size;
1042c9b4e987SNeel Natu 
1043c9b4e987SNeel Natu 	assert(table_entries > 0);
1044c9b4e987SNeel Natu 	assert(table_entries <= MAX_MSIX_TABLE_ENTRIES);
1045c9b4e987SNeel Natu 
1046c9b4e987SNeel Natu 	table_size = table_entries * MSIX_TABLE_ENTRY_SIZE;
1047994f858aSXin LI 	pi->pi_msix.table = calloc(1, table_size);
1048c9b4e987SNeel Natu 
1049c9b4e987SNeel Natu 	/* set mask bit of vector control register */
1050c9b4e987SNeel Natu 	for (i = 0; i < table_entries; i++)
1051c9b4e987SNeel Natu 		pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK;
1052c9b4e987SNeel Natu }
1053c9b4e987SNeel Natu 
1054c9b4e987SNeel Natu int
1055c9b4e987SNeel Natu pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum)
1056c9b4e987SNeel Natu {
1057c9b4e987SNeel Natu 	uint32_t tab_size;
1058c9b4e987SNeel Natu 	struct msixcap msixcap;
1059c9b4e987SNeel Natu 
1060c9b4e987SNeel Natu 	assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES);
1061c9b4e987SNeel Natu 	assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0);
1062c9b4e987SNeel Natu 
1063c9b4e987SNeel Natu 	tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE;
1064c9b4e987SNeel Natu 
1065c9b4e987SNeel Natu 	/* Align table size to nearest 4K */
1066c9b4e987SNeel Natu 	tab_size = roundup2(tab_size, 4096);
1067c9b4e987SNeel Natu 
1068c9b4e987SNeel Natu 	pi->pi_msix.table_bar = barnum;
1069c9b4e987SNeel Natu 	pi->pi_msix.pba_bar   = barnum;
1070c9b4e987SNeel Natu 	pi->pi_msix.table_offset = 0;
1071c9b4e987SNeel Natu 	pi->pi_msix.table_count = msgnum;
1072c9b4e987SNeel Natu 	pi->pi_msix.pba_offset = tab_size;
10737a902ec0SNeel Natu 	pi->pi_msix.pba_size = PBA_SIZE(msgnum);
1074c9b4e987SNeel Natu 
1075c9b4e987SNeel Natu 	pci_msix_table_init(pi, msgnum);
1076c9b4e987SNeel Natu 
1077a96b8b80SJohn Baldwin 	pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size);
1078c9b4e987SNeel Natu 
1079c9b4e987SNeel Natu 	/* allocate memory for MSI-X Table and PBA */
1080c9b4e987SNeel Natu 	pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32,
1081c9b4e987SNeel Natu 				tab_size + pi->pi_msix.pba_size);
1082c9b4e987SNeel Natu 
1083c9b4e987SNeel Natu 	return (pci_emul_add_capability(pi, (u_char *)&msixcap,
1084c9b4e987SNeel Natu 					sizeof(msixcap)));
1085c9b4e987SNeel Natu }
1086c9b4e987SNeel Natu 
108721368498SPeter Grehan static void
1088cd942e0fSPeter Grehan msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
1089cd942e0fSPeter Grehan 		 int bytes, uint32_t val)
1090cd942e0fSPeter Grehan {
1091cd942e0fSPeter Grehan 	uint16_t msgctrl, rwmask;
1092d74fdc6aSMarcelo Araujo 	int off;
1093cd942e0fSPeter Grehan 
1094cd942e0fSPeter Grehan 	off = offset - capoff;
1095cd942e0fSPeter Grehan 	/* Message Control Register */
1096cd942e0fSPeter Grehan 	if (off == 2 && bytes == 2) {
1097cd942e0fSPeter Grehan 		rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK;
1098cd942e0fSPeter Grehan 		msgctrl = pci_get_cfgdata16(pi, offset);
1099cd942e0fSPeter Grehan 		msgctrl &= ~rwmask;
1100cd942e0fSPeter Grehan 		msgctrl |= val & rwmask;
1101cd942e0fSPeter Grehan 		val = msgctrl;
1102cd942e0fSPeter Grehan 
1103cd942e0fSPeter Grehan 		pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE;
1104c9b4e987SNeel Natu 		pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK;
11053cbf3585SJohn Baldwin 		pci_lintr_update(pi);
1106cd942e0fSPeter Grehan 	}
1107cd942e0fSPeter Grehan 
1108cd942e0fSPeter Grehan 	CFGWRITE(pi, offset, val, bytes);
1109cd942e0fSPeter Grehan }
1110cd942e0fSPeter Grehan 
111121368498SPeter Grehan static void
1112366f6083SPeter Grehan msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
1113366f6083SPeter Grehan 		int bytes, uint32_t val)
1114366f6083SPeter Grehan {
1115366f6083SPeter Grehan 	uint16_t msgctrl, rwmask, msgdata, mme;
1116366f6083SPeter Grehan 	uint32_t addrlo;
1117366f6083SPeter Grehan 
1118366f6083SPeter Grehan 	/*
1119366f6083SPeter Grehan 	 * If guest is writing to the message control register make sure
1120366f6083SPeter Grehan 	 * we do not overwrite read-only fields.
1121366f6083SPeter Grehan 	 */
1122366f6083SPeter Grehan 	if ((offset - capoff) == 2 && bytes == 2) {
1123366f6083SPeter Grehan 		rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE;
1124366f6083SPeter Grehan 		msgctrl = pci_get_cfgdata16(pi, offset);
1125366f6083SPeter Grehan 		msgctrl &= ~rwmask;
1126366f6083SPeter Grehan 		msgctrl |= val & rwmask;
1127366f6083SPeter Grehan 		val = msgctrl;
11287840d1c4SJohn Baldwin 	}
11297840d1c4SJohn Baldwin 	CFGWRITE(pi, offset, val, bytes);
1130366f6083SPeter Grehan 
11317840d1c4SJohn Baldwin 	msgctrl = pci_get_cfgdata16(pi, capoff + 2);
1132366f6083SPeter Grehan 	addrlo = pci_get_cfgdata32(pi, capoff + 4);
1133366f6083SPeter Grehan 	if (msgctrl & PCIM_MSICTRL_64BIT)
1134366f6083SPeter Grehan 		msgdata = pci_get_cfgdata16(pi, capoff + 12);
1135366f6083SPeter Grehan 	else
1136366f6083SPeter Grehan 		msgdata = pci_get_cfgdata16(pi, capoff + 8);
1137366f6083SPeter Grehan 
1138366f6083SPeter Grehan 	mme = msgctrl & PCIM_MSICTRL_MME_MASK;
1139366f6083SPeter Grehan 	pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0;
1140366f6083SPeter Grehan 	if (pi->pi_msi.enabled) {
11414f8be175SNeel Natu 		pi->pi_msi.addr = addrlo;
11424f8be175SNeel Natu 		pi->pi_msi.msg_data = msgdata;
11434f8be175SNeel Natu 		pi->pi_msi.maxmsgnum = 1 << (mme >> 4);
1144366f6083SPeter Grehan 	} else {
11454f8be175SNeel Natu 		pi->pi_msi.maxmsgnum = 0;
1146366f6083SPeter Grehan 	}
11473cbf3585SJohn Baldwin 	pci_lintr_update(pi);
1148366f6083SPeter Grehan }
1149366f6083SPeter Grehan 
115074f80b23SNeel Natu void
115174f80b23SNeel Natu pciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
115274f80b23SNeel Natu 		 int bytes, uint32_t val)
115374f80b23SNeel Natu {
115474f80b23SNeel Natu 
115574f80b23SNeel Natu 	/* XXX don't write to the readonly parts */
115674f80b23SNeel Natu 	CFGWRITE(pi, offset, val, bytes);
115774f80b23SNeel Natu }
115874f80b23SNeel Natu 
115974f80b23SNeel Natu #define	PCIECAP_VERSION	0x2
116074f80b23SNeel Natu int
116174f80b23SNeel Natu pci_emul_add_pciecap(struct pci_devinst *pi, int type)
116274f80b23SNeel Natu {
116374f80b23SNeel Natu 	int err;
116474f80b23SNeel Natu 	struct pciecap pciecap;
116574f80b23SNeel Natu 
116674f80b23SNeel Natu 	bzero(&pciecap, sizeof(pciecap));
116774f80b23SNeel Natu 
1168129f93c5SChuck Tuffli 	/*
1169129f93c5SChuck Tuffli 	 * Use the integrated endpoint type for endpoints on a root complex bus.
1170129f93c5SChuck Tuffli 	 *
1171129f93c5SChuck Tuffli 	 * NB: bhyve currently only supports a single PCI bus that is the root
1172129f93c5SChuck Tuffli 	 * complex bus, so all endpoints are integrated.
1173129f93c5SChuck Tuffli 	 */
1174129f93c5SChuck Tuffli 	if ((type == PCIEM_TYPE_ENDPOINT) && (pi->pi_bus == 0))
1175129f93c5SChuck Tuffli 		type = PCIEM_TYPE_ROOT_INT_EP;
1176129f93c5SChuck Tuffli 
117774f80b23SNeel Natu 	pciecap.capid = PCIY_EXPRESS;
1178129f93c5SChuck Tuffli 	pciecap.pcie_capabilities = PCIECAP_VERSION | type;
1179129f93c5SChuck Tuffli 	if (type != PCIEM_TYPE_ROOT_INT_EP) {
118074f80b23SNeel Natu 		pciecap.link_capabilities = 0x411;	/* gen1, x1 */
118174f80b23SNeel Natu 		pciecap.link_status = 0x11;		/* gen1, x1 */
1182129f93c5SChuck Tuffli 	}
118374f80b23SNeel Natu 
118474f80b23SNeel Natu 	err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap));
118574f80b23SNeel Natu 	return (err);
118674f80b23SNeel Natu }
118774f80b23SNeel Natu 
1188366f6083SPeter Grehan /*
1189366f6083SPeter Grehan  * This function assumes that 'coff' is in the capabilities region of the
119021368498SPeter Grehan  * config space. A capoff parameter of zero will force a search for the
119121368498SPeter Grehan  * offset and type.
1192366f6083SPeter Grehan  */
119321368498SPeter Grehan void
119421368498SPeter Grehan pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val,
119521368498SPeter Grehan     uint8_t capoff, int capid)
1196366f6083SPeter Grehan {
119721368498SPeter Grehan 	uint8_t nextoff;
1198366f6083SPeter Grehan 
1199366f6083SPeter Grehan 	/* Do not allow un-aligned writes */
1200366f6083SPeter Grehan 	if ((offset & (bytes - 1)) != 0)
1201366f6083SPeter Grehan 		return;
1202366f6083SPeter Grehan 
120321368498SPeter Grehan 	if (capoff == 0) {
1204366f6083SPeter Grehan 		/* Find the capability that we want to update */
1205366f6083SPeter Grehan 		capoff = CAP_START_OFFSET;
1206366f6083SPeter Grehan 		while (1) {
1207366f6083SPeter Grehan 			nextoff = pci_get_cfgdata8(pi, capoff + 1);
1208a96b8b80SJohn Baldwin 			if (nextoff == 0)
1209a96b8b80SJohn Baldwin 				break;
1210366f6083SPeter Grehan 			if (offset >= capoff && offset < nextoff)
1211366f6083SPeter Grehan 				break;
1212366f6083SPeter Grehan 
1213366f6083SPeter Grehan 			capoff = nextoff;
1214366f6083SPeter Grehan 		}
1215366f6083SPeter Grehan 		assert(offset >= capoff);
121621368498SPeter Grehan 		capid = pci_get_cfgdata8(pi, capoff);
121721368498SPeter Grehan 	}
1218366f6083SPeter Grehan 
1219366f6083SPeter Grehan 	/*
12202a8d400aSPeter Grehan 	 * Capability ID and Next Capability Pointer are readonly.
12212a8d400aSPeter Grehan 	 * However, some o/s's do 4-byte writes that include these.
12222a8d400aSPeter Grehan 	 * For this case, trim the write back to 2 bytes and adjust
12232a8d400aSPeter Grehan 	 * the data.
1224366f6083SPeter Grehan 	 */
12252a8d400aSPeter Grehan 	if (offset == capoff || offset == capoff + 1) {
12262a8d400aSPeter Grehan 		if (offset == capoff && bytes == 4) {
12272a8d400aSPeter Grehan 			bytes = 2;
12282a8d400aSPeter Grehan 			offset += 2;
12292a8d400aSPeter Grehan 			val >>= 16;
12302a8d400aSPeter Grehan 		} else
1231366f6083SPeter Grehan 			return;
12322a8d400aSPeter Grehan 	}
1233366f6083SPeter Grehan 
1234366f6083SPeter Grehan 	switch (capid) {
1235366f6083SPeter Grehan 	case PCIY_MSI:
1236366f6083SPeter Grehan 		msicap_cfgwrite(pi, capoff, offset, bytes, val);
1237366f6083SPeter Grehan 		break;
1238c9b4e987SNeel Natu 	case PCIY_MSIX:
1239c9b4e987SNeel Natu 		msixcap_cfgwrite(pi, capoff, offset, bytes, val);
1240c9b4e987SNeel Natu 		break;
124174f80b23SNeel Natu 	case PCIY_EXPRESS:
124274f80b23SNeel Natu 		pciecap_cfgwrite(pi, capoff, offset, bytes, val);
124374f80b23SNeel Natu 		break;
1244366f6083SPeter Grehan 	default:
1245366f6083SPeter Grehan 		break;
1246366f6083SPeter Grehan 	}
1247366f6083SPeter Grehan }
1248366f6083SPeter Grehan 
1249366f6083SPeter Grehan static int
1250366f6083SPeter Grehan pci_emul_iscap(struct pci_devinst *pi, int offset)
1251366f6083SPeter Grehan {
1252366f6083SPeter Grehan 	uint16_t sts;
1253366f6083SPeter Grehan 
1254366f6083SPeter Grehan 	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
1255366f6083SPeter Grehan 	if ((sts & PCIM_STATUS_CAPPRESENT) != 0) {
1256a96b8b80SJohn Baldwin 		if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend)
1257a96b8b80SJohn Baldwin 			return (1);
1258366f6083SPeter Grehan 	}
1259a96b8b80SJohn Baldwin 	return (0);
1260366f6083SPeter Grehan }
1261366f6083SPeter Grehan 
12620ab13648SPeter Grehan static int
12630ab13648SPeter Grehan pci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
12640ab13648SPeter Grehan 			  int size, uint64_t *val, void *arg1, long arg2)
12650ab13648SPeter Grehan {
12660ab13648SPeter Grehan 	/*
12670ab13648SPeter Grehan 	 * Ignore writes; return 0xff's for reads. The mem read code
12680ab13648SPeter Grehan 	 * will take care of truncating to the correct size.
12690ab13648SPeter Grehan 	 */
12700ab13648SPeter Grehan 	if (dir == MEM_F_READ) {
12710ab13648SPeter Grehan 		*val = 0xffffffffffffffff;
12720ab13648SPeter Grehan 	}
12730ab13648SPeter Grehan 
12740ab13648SPeter Grehan 	return (0);
12750ab13648SPeter Grehan }
12760ab13648SPeter Grehan 
127712a6eb99SNeel Natu static int
127812a6eb99SNeel Natu pci_emul_ecfg_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
127912a6eb99SNeel Natu     int bytes, uint64_t *val, void *arg1, long arg2)
128012a6eb99SNeel Natu {
128112a6eb99SNeel Natu 	int bus, slot, func, coff, in;
128212a6eb99SNeel Natu 
128312a6eb99SNeel Natu 	coff = addr & 0xfff;
128412a6eb99SNeel Natu 	func = (addr >> 12) & 0x7;
128512a6eb99SNeel Natu 	slot = (addr >> 15) & 0x1f;
128612a6eb99SNeel Natu 	bus = (addr >> 20) & 0xff;
128712a6eb99SNeel Natu 	in = (dir == MEM_F_READ);
128812a6eb99SNeel Natu 	if (in)
128912a6eb99SNeel Natu 		*val = ~0UL;
129012a6eb99SNeel Natu 	pci_cfgrw(ctx, vcpu, in, bus, slot, func, coff, bytes, (uint32_t *)val);
129112a6eb99SNeel Natu 	return (0);
129212a6eb99SNeel Natu }
129312a6eb99SNeel Natu 
129412a6eb99SNeel Natu uint64_t
129512a6eb99SNeel Natu pci_ecfg_base(void)
129612a6eb99SNeel Natu {
129712a6eb99SNeel Natu 
129812a6eb99SNeel Natu 	return (PCI_EMUL_ECFG_BASE);
129912a6eb99SNeel Natu }
130012a6eb99SNeel Natu 
1301d84882caSNeel Natu #define	BUSIO_ROUNDUP		32
13027d55d295SCorvin Köhne #define	BUSMEM32_ROUNDUP	(1024 * 1024)
13037d55d295SCorvin Köhne #define	BUSMEM64_ROUNDUP	(512 * 1024 * 1024)
1304d84882caSNeel Natu 
1305a38e2a64SPeter Grehan int
1306366f6083SPeter Grehan init_pci(struct vmctx *ctx)
1307366f6083SPeter Grehan {
1308621b5090SJohn Baldwin 	char node_name[sizeof("pci.XXX.XX.X")];
130912a6eb99SNeel Natu 	struct mem_range mr;
1310366f6083SPeter Grehan 	struct pci_devemu *pde;
1311d84882caSNeel Natu 	struct businfo *bi;
1312d84882caSNeel Natu 	struct slotinfo *si;
13133cbf3585SJohn Baldwin 	struct funcinfo *fi;
1314621b5090SJohn Baldwin 	nvlist_t *nvl;
1315621b5090SJohn Baldwin 	const char *emul;
13169f08548dSNeel Natu 	size_t lowmem;
13174a4053e1SCorvin Köhne 	int bus, slot, func;
13184a4053e1SCorvin Köhne 	int error;
1319366f6083SPeter Grehan 
13205cf21e48SCorvin Köhne 	if (vm_get_lowmem_limit(ctx) > PCI_EMUL_MEMBASE32)
13215cf21e48SCorvin Köhne 		errx(EX_OSERR, "Invalid lowmem limit");
13225cf21e48SCorvin Köhne 
1323366f6083SPeter Grehan 	pci_emul_iobase = PCI_EMUL_IOBASE;
13245cf21e48SCorvin Köhne 	pci_emul_membase32 = PCI_EMUL_MEMBASE32;
13259922872bSKonstantin Belousov 
13264a4053e1SCorvin Köhne 	pci_emul_membase64 = 4*GB + vm_get_highmem_size(ctx);
13274a4053e1SCorvin Köhne 	pci_emul_membase64 = roundup2(pci_emul_membase64, PCI_EMUL_MEMSIZE64);
13284a4053e1SCorvin Köhne 	pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64;
1329366f6083SPeter Grehan 
1330d84882caSNeel Natu 	for (bus = 0; bus < MAXBUSES; bus++) {
1331621b5090SJohn Baldwin 		snprintf(node_name, sizeof(node_name), "pci.%d", bus);
1332621b5090SJohn Baldwin 		nvl = find_config_node(node_name);
1333621b5090SJohn Baldwin 		if (nvl == NULL)
1334d84882caSNeel Natu 			continue;
1335621b5090SJohn Baldwin 		pci_businfo[bus] = calloc(1, sizeof(struct businfo));
1336621b5090SJohn Baldwin 		bi = pci_businfo[bus];
1337621b5090SJohn Baldwin 
1338d84882caSNeel Natu 		/*
1339d84882caSNeel Natu 		 * Keep track of the i/o and memory resources allocated to
1340d84882caSNeel Natu 		 * this bus.
1341d84882caSNeel Natu 		 */
1342d84882caSNeel Natu 		bi->iobase = pci_emul_iobase;
1343d84882caSNeel Natu 		bi->membase32 = pci_emul_membase32;
1344d84882caSNeel Natu 		bi->membase64 = pci_emul_membase64;
1345d84882caSNeel Natu 
134601f9362eSCorvin Köhne 		/* first run: init devices */
134799d65389SNeel Natu 		for (slot = 0; slot < MAXSLOTS; slot++) {
1348d84882caSNeel Natu 			si = &bi->slotinfo[slot];
134999d65389SNeel Natu 			for (func = 0; func < MAXFUNCS; func++) {
1350d84882caSNeel Natu 				fi = &si->si_funcs[func];
1351621b5090SJohn Baldwin 				snprintf(node_name, sizeof(node_name),
1352621b5090SJohn Baldwin 				    "pci.%d.%d.%d", bus, slot, func);
1353621b5090SJohn Baldwin 				nvl = find_config_node(node_name);
1354621b5090SJohn Baldwin 				if (nvl == NULL)
1355d84882caSNeel Natu 					continue;
1356621b5090SJohn Baldwin 
1357621b5090SJohn Baldwin 				fi->fi_config = nvl;
1358621b5090SJohn Baldwin 				emul = get_config_value_node(nvl, "device");
1359621b5090SJohn Baldwin 				if (emul == NULL) {
1360621b5090SJohn Baldwin 					EPRINTLN("pci slot %d:%d:%d: missing "
1361621b5090SJohn Baldwin 					    "\"device\" value", bus, slot, func);
1362621b5090SJohn Baldwin 					return (EINVAL);
1363621b5090SJohn Baldwin 				}
1364621b5090SJohn Baldwin 				pde = pci_emul_finddev(emul);
1365621b5090SJohn Baldwin 				if (pde == NULL) {
1366621b5090SJohn Baldwin 					EPRINTLN("pci slot %d:%d:%d: unknown "
1367621b5090SJohn Baldwin 					    "device \"%s\"", bus, slot, func,
1368621b5090SJohn Baldwin 					    emul);
1369621b5090SJohn Baldwin 					return (EINVAL);
1370621b5090SJohn Baldwin 				}
1371621b5090SJohn Baldwin 				if (pde->pe_alias != NULL) {
1372621b5090SJohn Baldwin 					EPRINTLN("pci slot %d:%d:%d: legacy "
1373621b5090SJohn Baldwin 					    "device \"%s\", use \"%s\" instead",
1374621b5090SJohn Baldwin 					    bus, slot, func, emul,
1375621b5090SJohn Baldwin 					    pde->pe_alias);
1376621b5090SJohn Baldwin 					return (EINVAL);
1377621b5090SJohn Baldwin 				}
1378621b5090SJohn Baldwin 				fi->fi_pde = pde;
1379d84882caSNeel Natu 				error = pci_emul_init(ctx, pde, bus, slot,
1380d84882caSNeel Natu 				    func, fi);
1381a38e2a64SPeter Grehan 				if (error)
1382a38e2a64SPeter Grehan 					return (error);
1383366f6083SPeter Grehan 			}
1384366f6083SPeter Grehan 		}
1385d84882caSNeel Natu 
138601f9362eSCorvin Köhne 		/* second run: assign BARs and free list */
138701f9362eSCorvin Köhne 		struct pci_bar_allocation *bar;
138801f9362eSCorvin Köhne 		struct pci_bar_allocation *bar_tmp;
138901f9362eSCorvin Köhne 		TAILQ_FOREACH_SAFE(bar, &pci_bars, chain, bar_tmp) {
139001f9362eSCorvin Köhne 			pci_emul_assign_bar(bar->pdi, bar->idx, bar->type,
139101f9362eSCorvin Köhne 			    bar->size);
139201f9362eSCorvin Köhne 			free(bar);
139301f9362eSCorvin Köhne 		}
139401f9362eSCorvin Köhne 		TAILQ_INIT(&pci_bars);
139501f9362eSCorvin Köhne 
1396d84882caSNeel Natu 		/*
1397d84882caSNeel Natu 		 * Add some slop to the I/O and memory resources decoded by
1398d84882caSNeel Natu 		 * this bus to give a guest some flexibility if it wants to
1399d84882caSNeel Natu 		 * reprogram the BARs.
1400d84882caSNeel Natu 		 */
1401d84882caSNeel Natu 		pci_emul_iobase += BUSIO_ROUNDUP;
1402d84882caSNeel Natu 		pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP);
1403d84882caSNeel Natu 		bi->iolimit = pci_emul_iobase;
1404d84882caSNeel Natu 
14057d55d295SCorvin Köhne 		pci_emul_membase32 += BUSMEM32_ROUNDUP;
1406d84882caSNeel Natu 		pci_emul_membase32 = roundup2(pci_emul_membase32,
14077d55d295SCorvin Köhne 		    BUSMEM32_ROUNDUP);
1408d84882caSNeel Natu 		bi->memlimit32 = pci_emul_membase32;
1409d84882caSNeel Natu 
14107d55d295SCorvin Köhne 		pci_emul_membase64 += BUSMEM64_ROUNDUP;
1411d84882caSNeel Natu 		pci_emul_membase64 = roundup2(pci_emul_membase64,
14127d55d295SCorvin Köhne 		    BUSMEM64_ROUNDUP);
1413d84882caSNeel Natu 		bi->memlimit64 = pci_emul_membase64;
1414366f6083SPeter Grehan 	}
14150038ee98SPeter Grehan 
14160038ee98SPeter Grehan 	/*
1417b3e9732aSJohn Baldwin 	 * PCI backends are initialized before routing INTx interrupts
1418b3e9732aSJohn Baldwin 	 * so that LPC devices are able to reserve ISA IRQs before
1419b3e9732aSJohn Baldwin 	 * routing PIRQ pins.
1420b3e9732aSJohn Baldwin 	 */
1421b3e9732aSJohn Baldwin 	for (bus = 0; bus < MAXBUSES; bus++) {
1422b3e9732aSJohn Baldwin 		if ((bi = pci_businfo[bus]) == NULL)
1423b3e9732aSJohn Baldwin 			continue;
1424b3e9732aSJohn Baldwin 
1425b3e9732aSJohn Baldwin 		for (slot = 0; slot < MAXSLOTS; slot++) {
1426b3e9732aSJohn Baldwin 			si = &bi->slotinfo[slot];
1427b3e9732aSJohn Baldwin 			for (func = 0; func < MAXFUNCS; func++) {
1428b3e9732aSJohn Baldwin 				fi = &si->si_funcs[func];
1429b3e9732aSJohn Baldwin 				if (fi->fi_devi == NULL)
1430b3e9732aSJohn Baldwin 					continue;
1431b3e9732aSJohn Baldwin 				pci_lintr_route(fi->fi_devi);
1432b3e9732aSJohn Baldwin 			}
1433b3e9732aSJohn Baldwin 		}
1434b3e9732aSJohn Baldwin 	}
1435b3e9732aSJohn Baldwin 	lpc_pirq_routed();
1436b3e9732aSJohn Baldwin 
1437b3e9732aSJohn Baldwin 	/*
14389f08548dSNeel Natu 	 * The guest physical memory map looks like the following:
14399f08548dSNeel Natu 	 * [0,		    lowmem)		guest system memory
14405cf21e48SCorvin Köhne 	 * [lowmem,	    0xC0000000)		memory hole (may be absent)
14415cf21e48SCorvin Köhne 	 * [0xC0000000,     0xE0000000)		PCI hole (32-bit BAR allocation)
144212a6eb99SNeel Natu 	 * [0xE0000000,	    0xF0000000)		PCI extended config window
144312a6eb99SNeel Natu 	 * [0xF0000000,	    4GB)		LAPIC, IOAPIC, HPET, firmware
14449f08548dSNeel Natu 	 * [4GB,	    4GB + highmem)
144512a6eb99SNeel Natu 	 */
144612a6eb99SNeel Natu 
144712a6eb99SNeel Natu 	/*
14489f08548dSNeel Natu 	 * Accesses to memory addresses that are not allocated to system
14499f08548dSNeel Natu 	 * memory or PCI devices return 0xff's.
14500ab13648SPeter Grehan 	 */
1451be679db4SNeel Natu 	lowmem = vm_get_lowmem_size(ctx);
145212a6eb99SNeel Natu 	bzero(&mr, sizeof(struct mem_range));
145312a6eb99SNeel Natu 	mr.name = "PCI hole";
145412a6eb99SNeel Natu 	mr.flags = MEM_F_RW | MEM_F_IMMUTABLE;
145512a6eb99SNeel Natu 	mr.base = lowmem;
145612a6eb99SNeel Natu 	mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem;
145712a6eb99SNeel Natu 	mr.handler = pci_emul_fallback_handler;
145812a6eb99SNeel Natu 	error = register_mem_fallback(&mr);
145912a6eb99SNeel Natu 	assert(error == 0);
14609f08548dSNeel Natu 
146112a6eb99SNeel Natu 	/* PCI extended config space */
146212a6eb99SNeel Natu 	bzero(&mr, sizeof(struct mem_range));
146312a6eb99SNeel Natu 	mr.name = "PCI ECFG";
146412a6eb99SNeel Natu 	mr.flags = MEM_F_RW | MEM_F_IMMUTABLE;
146512a6eb99SNeel Natu 	mr.base = PCI_EMUL_ECFG_BASE;
146612a6eb99SNeel Natu 	mr.size = PCI_EMUL_ECFG_SIZE;
146712a6eb99SNeel Natu 	mr.handler = pci_emul_ecfg_handler;
146812a6eb99SNeel Natu 	error = register_mem(&mr);
14690ab13648SPeter Grehan 	assert(error == 0);
1470a38e2a64SPeter Grehan 
1471a38e2a64SPeter Grehan 	return (0);
1472366f6083SPeter Grehan }
1473366f6083SPeter Grehan 
14743cbf3585SJohn Baldwin static void
1475b3e9732aSJohn Baldwin pci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
1476b3e9732aSJohn Baldwin     void *arg)
14773cbf3585SJohn Baldwin {
14783cbf3585SJohn Baldwin 
1479b3e9732aSJohn Baldwin 	dsdt_line("  Package ()");
14803cbf3585SJohn Baldwin 	dsdt_line("  {");
14813cbf3585SJohn Baldwin 	dsdt_line("    0x%X,", slot << 16 | 0xffff);
14823cbf3585SJohn Baldwin 	dsdt_line("    0x%02X,", pin - 1);
14833cbf3585SJohn Baldwin 	dsdt_line("    Zero,");
14843cbf3585SJohn Baldwin 	dsdt_line("    0x%X", ioapic_irq);
1485b3e9732aSJohn Baldwin 	dsdt_line("  },");
1486b3e9732aSJohn Baldwin }
1487b3e9732aSJohn Baldwin 
1488b3e9732aSJohn Baldwin static void
1489b3e9732aSJohn Baldwin pci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
1490b3e9732aSJohn Baldwin     void *arg)
1491b3e9732aSJohn Baldwin {
1492b3e9732aSJohn Baldwin 	char *name;
1493b3e9732aSJohn Baldwin 
1494b3e9732aSJohn Baldwin 	name = lpc_pirq_name(pirq_pin);
1495b3e9732aSJohn Baldwin 	if (name == NULL)
1496b3e9732aSJohn Baldwin 		return;
1497b3e9732aSJohn Baldwin 	dsdt_line("  Package ()");
1498b3e9732aSJohn Baldwin 	dsdt_line("  {");
1499b3e9732aSJohn Baldwin 	dsdt_line("    0x%X,", slot << 16 | 0xffff);
1500b3e9732aSJohn Baldwin 	dsdt_line("    0x%02X,", pin - 1);
1501b3e9732aSJohn Baldwin 	dsdt_line("    %s,", name);
1502b3e9732aSJohn Baldwin 	dsdt_line("    0x00");
1503b3e9732aSJohn Baldwin 	dsdt_line("  },");
1504b3e9732aSJohn Baldwin 	free(name);
15053cbf3585SJohn Baldwin }
15063cbf3585SJohn Baldwin 
1507d84882caSNeel Natu /*
1508d84882caSNeel Natu  * A bhyve virtual machine has a flat PCI hierarchy with a root port
1509d84882caSNeel Natu  * corresponding to each PCI bus.
1510d84882caSNeel Natu  */
1511d84882caSNeel Natu static void
1512d84882caSNeel Natu pci_bus_write_dsdt(int bus)
1513e6c8bc29SJohn Baldwin {
1514d84882caSNeel Natu 	struct businfo *bi;
1515d84882caSNeel Natu 	struct slotinfo *si;
1516e6c8bc29SJohn Baldwin 	struct pci_devinst *pi;
1517b3e9732aSJohn Baldwin 	int count, func, slot;
1518e6c8bc29SJohn Baldwin 
1519d84882caSNeel Natu 	/*
1520d84882caSNeel Natu 	 * If there are no devices on this 'bus' then just return.
1521d84882caSNeel Natu 	 */
1522d84882caSNeel Natu 	if ((bi = pci_businfo[bus]) == NULL) {
1523d84882caSNeel Natu 		/*
1524d84882caSNeel Natu 		 * Bus 0 is special because it decodes the I/O ports used
1525d84882caSNeel Natu 		 * for PCI config space access even if there are no devices
1526d84882caSNeel Natu 		 * on it.
1527d84882caSNeel Natu 		 */
1528d84882caSNeel Natu 		if (bus != 0)
1529d84882caSNeel Natu 			return;
1530d84882caSNeel Natu 	}
1531d84882caSNeel Natu 
1532d84882caSNeel Natu 	dsdt_line("  Device (PC%02X)", bus);
1533e6c8bc29SJohn Baldwin 	dsdt_line("  {");
1534e6c8bc29SJohn Baldwin 	dsdt_line("    Name (_HID, EisaId (\"PNP0A03\"))");
1535d84882caSNeel Natu 
1536d84882caSNeel Natu 	dsdt_line("    Method (_BBN, 0, NotSerialized)");
1537d84882caSNeel Natu 	dsdt_line("    {");
1538d84882caSNeel Natu 	dsdt_line("        Return (0x%08X)", bus);
1539d84882caSNeel Natu 	dsdt_line("    }");
1540e6c8bc29SJohn Baldwin 	dsdt_line("    Name (_CRS, ResourceTemplate ()");
1541e6c8bc29SJohn Baldwin 	dsdt_line("    {");
1542e6c8bc29SJohn Baldwin 	dsdt_line("      WordBusNumber (ResourceProducer, MinFixed, "
1543e6c8bc29SJohn Baldwin 	    "MaxFixed, PosDecode,");
1544e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000,             // Granularity");
1545d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Minimum", bus);
1546d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Maximum", bus);
1547e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000,             // Translation Offset");
1548d84882caSNeel Natu 	dsdt_line("        0x0001,             // Length");
1549e6c8bc29SJohn Baldwin 	dsdt_line("        ,, )");
1550d84882caSNeel Natu 
1551d84882caSNeel Natu 	if (bus == 0) {
1552e6c8bc29SJohn Baldwin 		dsdt_indent(3);
1553e6c8bc29SJohn Baldwin 		dsdt_fixed_ioport(0xCF8, 8);
1554e6c8bc29SJohn Baldwin 		dsdt_unindent(3);
1555d84882caSNeel Natu 
1556e6c8bc29SJohn Baldwin 		dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
1557e6c8bc29SJohn Baldwin 		    "PosDecode, EntireRange,");
1558e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Granularity");
1559e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Range Minimum");
1560e6c8bc29SJohn Baldwin 		dsdt_line("        0x0CF7,             // Range Maximum");
1561e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Translation Offset");
1562e6c8bc29SJohn Baldwin 		dsdt_line("        0x0CF8,             // Length");
1563e6c8bc29SJohn Baldwin 		dsdt_line("        ,, , TypeStatic)");
1564d84882caSNeel Natu 
1565e6c8bc29SJohn Baldwin 		dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
1566e6c8bc29SJohn Baldwin 		    "PosDecode, EntireRange,");
1567e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Granularity");
1568e6c8bc29SJohn Baldwin 		dsdt_line("        0x0D00,             // Range Minimum");
1569d84882caSNeel Natu 		dsdt_line("        0x%04X,             // Range Maximum",
1570d84882caSNeel Natu 		    PCI_EMUL_IOBASE - 1);
1571e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Translation Offset");
1572d84882caSNeel Natu 		dsdt_line("        0x%04X,             // Length",
1573d84882caSNeel Natu 		    PCI_EMUL_IOBASE - 0x0D00);
1574e6c8bc29SJohn Baldwin 		dsdt_line("        ,, , TypeStatic)");
1575d84882caSNeel Natu 
1576d84882caSNeel Natu 		if (bi == NULL) {
1577d84882caSNeel Natu 			dsdt_line("    })");
1578d84882caSNeel Natu 			goto done;
1579d84882caSNeel Natu 		}
1580d84882caSNeel Natu 	}
1581d84882caSNeel Natu 	assert(bi != NULL);
1582d84882caSNeel Natu 
1583d84882caSNeel Natu 	/* i/o window */
1584d84882caSNeel Natu 	dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
1585d84882caSNeel Natu 	    "PosDecode, EntireRange,");
1586d84882caSNeel Natu 	dsdt_line("        0x0000,             // Granularity");
1587d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Minimum", bi->iobase);
1588d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Maximum",
1589d84882caSNeel Natu 	    bi->iolimit - 1);
1590d84882caSNeel Natu 	dsdt_line("        0x0000,             // Translation Offset");
1591d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Length",
1592d84882caSNeel Natu 	    bi->iolimit - bi->iobase);
1593d84882caSNeel Natu 	dsdt_line("        ,, , TypeStatic)");
1594d84882caSNeel Natu 
1595d84882caSNeel Natu 	/* mmio window (32-bit) */
1596e6c8bc29SJohn Baldwin 	dsdt_line("      DWordMemory (ResourceProducer, PosDecode, "
1597e6c8bc29SJohn Baldwin 	    "MinFixed, MaxFixed, NonCacheable, ReadWrite,");
1598e6c8bc29SJohn Baldwin 	dsdt_line("        0x00000000,         // Granularity");
1599d84882caSNeel Natu 	dsdt_line("        0x%08X,         // Range Minimum\n", bi->membase32);
1600e6c8bc29SJohn Baldwin 	dsdt_line("        0x%08X,         // Range Maximum\n",
1601d84882caSNeel Natu 	    bi->memlimit32 - 1);
1602e6c8bc29SJohn Baldwin 	dsdt_line("        0x00000000,         // Translation Offset");
1603d84882caSNeel Natu 	dsdt_line("        0x%08X,         // Length\n",
1604d84882caSNeel Natu 	    bi->memlimit32 - bi->membase32);
1605e6c8bc29SJohn Baldwin 	dsdt_line("        ,, , AddressRangeMemory, TypeStatic)");
1606d84882caSNeel Natu 
1607d84882caSNeel Natu 	/* mmio window (64-bit) */
1608e6c8bc29SJohn Baldwin 	dsdt_line("      QWordMemory (ResourceProducer, PosDecode, "
1609e6c8bc29SJohn Baldwin 	    "MinFixed, MaxFixed, NonCacheable, ReadWrite,");
1610e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000000000000000, // Granularity");
1611d84882caSNeel Natu 	dsdt_line("        0x%016lX, // Range Minimum\n", bi->membase64);
1612e6c8bc29SJohn Baldwin 	dsdt_line("        0x%016lX, // Range Maximum\n",
1613d84882caSNeel Natu 	    bi->memlimit64 - 1);
1614e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000000000000000, // Translation Offset");
1615e6c8bc29SJohn Baldwin 	dsdt_line("        0x%016lX, // Length\n",
1616d84882caSNeel Natu 	    bi->memlimit64 - bi->membase64);
1617e6c8bc29SJohn Baldwin 	dsdt_line("        ,, , AddressRangeMemory, TypeStatic)");
1618e6c8bc29SJohn Baldwin 	dsdt_line("    })");
1619d84882caSNeel Natu 
1620d84882caSNeel Natu 	count = pci_count_lintr(bus);
16213cbf3585SJohn Baldwin 	if (count != 0) {
16223cbf3585SJohn Baldwin 		dsdt_indent(2);
1623b3e9732aSJohn Baldwin 		dsdt_line("Name (PPRT, Package ()");
16243cbf3585SJohn Baldwin 		dsdt_line("{");
1625b3e9732aSJohn Baldwin 		pci_walk_lintr(bus, pci_pirq_prt_entry, NULL);
16263cbf3585SJohn Baldwin 		dsdt_line("})");
1627b3e9732aSJohn Baldwin 		dsdt_line("Name (APRT, Package ()");
1628b3e9732aSJohn Baldwin 		dsdt_line("{");
1629b3e9732aSJohn Baldwin 		pci_walk_lintr(bus, pci_apic_prt_entry, NULL);
1630b3e9732aSJohn Baldwin 		dsdt_line("})");
1631b3e9732aSJohn Baldwin 		dsdt_line("Method (_PRT, 0, NotSerialized)");
1632b3e9732aSJohn Baldwin 		dsdt_line("{");
1633b3e9732aSJohn Baldwin 		dsdt_line("  If (PICM)");
1634b3e9732aSJohn Baldwin 		dsdt_line("  {");
1635b3e9732aSJohn Baldwin 		dsdt_line("    Return (APRT)");
1636b3e9732aSJohn Baldwin 		dsdt_line("  }");
1637b3e9732aSJohn Baldwin 		dsdt_line("  Else");
1638b3e9732aSJohn Baldwin 		dsdt_line("  {");
1639b3e9732aSJohn Baldwin 		dsdt_line("    Return (PPRT)");
1640b3e9732aSJohn Baldwin 		dsdt_line("  }");
1641b3e9732aSJohn Baldwin 		dsdt_line("}");
16423cbf3585SJohn Baldwin 		dsdt_unindent(2);
16433cbf3585SJohn Baldwin 	}
1644e6c8bc29SJohn Baldwin 
1645e6c8bc29SJohn Baldwin 	dsdt_indent(2);
1646e6c8bc29SJohn Baldwin 	for (slot = 0; slot < MAXSLOTS; slot++) {
1647d84882caSNeel Natu 		si = &bi->slotinfo[slot];
1648e6c8bc29SJohn Baldwin 		for (func = 0; func < MAXFUNCS; func++) {
1649d84882caSNeel Natu 			pi = si->si_funcs[func].fi_devi;
1650e6c8bc29SJohn Baldwin 			if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL)
1651e6c8bc29SJohn Baldwin 				pi->pi_d->pe_write_dsdt(pi);
1652e6c8bc29SJohn Baldwin 		}
1653e6c8bc29SJohn Baldwin 	}
1654e6c8bc29SJohn Baldwin 	dsdt_unindent(2);
1655d84882caSNeel Natu done:
1656e6c8bc29SJohn Baldwin 	dsdt_line("  }");
1657e6c8bc29SJohn Baldwin }
1658e6c8bc29SJohn Baldwin 
1659d84882caSNeel Natu void
1660d84882caSNeel Natu pci_write_dsdt(void)
1661d84882caSNeel Natu {
1662d84882caSNeel Natu 	int bus;
1663d84882caSNeel Natu 
1664b3e9732aSJohn Baldwin 	dsdt_indent(1);
1665b3e9732aSJohn Baldwin 	dsdt_line("Name (PICM, 0x00)");
1666b3e9732aSJohn Baldwin 	dsdt_line("Method (_PIC, 1, NotSerialized)");
1667b3e9732aSJohn Baldwin 	dsdt_line("{");
1668b3e9732aSJohn Baldwin 	dsdt_line("  Store (Arg0, PICM)");
1669b3e9732aSJohn Baldwin 	dsdt_line("}");
1670b3e9732aSJohn Baldwin 	dsdt_line("");
1671b3e9732aSJohn Baldwin 	dsdt_line("Scope (_SB)");
1672b3e9732aSJohn Baldwin 	dsdt_line("{");
1673d84882caSNeel Natu 	for (bus = 0; bus < MAXBUSES; bus++)
1674d84882caSNeel Natu 		pci_bus_write_dsdt(bus);
1675b3e9732aSJohn Baldwin 	dsdt_line("}");
1676b3e9732aSJohn Baldwin 	dsdt_unindent(1);
1677d84882caSNeel Natu }
1678d84882caSNeel Natu 
1679366f6083SPeter Grehan int
1680b100acf2SNeel Natu pci_bus_configured(int bus)
1681b100acf2SNeel Natu {
1682b100acf2SNeel Natu 	assert(bus >= 0 && bus < MAXBUSES);
1683b100acf2SNeel Natu 	return (pci_businfo[bus] != NULL);
1684b100acf2SNeel Natu }
1685b100acf2SNeel Natu 
1686b100acf2SNeel Natu int
1687366f6083SPeter Grehan pci_msi_enabled(struct pci_devinst *pi)
1688366f6083SPeter Grehan {
1689366f6083SPeter Grehan 	return (pi->pi_msi.enabled);
1690366f6083SPeter Grehan }
1691366f6083SPeter Grehan 
1692366f6083SPeter Grehan int
16934f8be175SNeel Natu pci_msi_maxmsgnum(struct pci_devinst *pi)
1694366f6083SPeter Grehan {
1695366f6083SPeter Grehan 	if (pi->pi_msi.enabled)
16964f8be175SNeel Natu 		return (pi->pi_msi.maxmsgnum);
1697366f6083SPeter Grehan 	else
1698366f6083SPeter Grehan 		return (0);
1699366f6083SPeter Grehan }
1700366f6083SPeter Grehan 
1701c9b4e987SNeel Natu int
1702c9b4e987SNeel Natu pci_msix_enabled(struct pci_devinst *pi)
1703c9b4e987SNeel Natu {
1704c9b4e987SNeel Natu 
1705c9b4e987SNeel Natu 	return (pi->pi_msix.enabled && !pi->pi_msi.enabled);
1706c9b4e987SNeel Natu }
1707c9b4e987SNeel Natu 
1708c9b4e987SNeel Natu void
1709c9b4e987SNeel Natu pci_generate_msix(struct pci_devinst *pi, int index)
1710c9b4e987SNeel Natu {
1711c9b4e987SNeel Natu 	struct msix_table_entry *mte;
1712c9b4e987SNeel Natu 
1713c9b4e987SNeel Natu 	if (!pci_msix_enabled(pi))
1714c9b4e987SNeel Natu 		return;
1715c9b4e987SNeel Natu 
1716c9b4e987SNeel Natu 	if (pi->pi_msix.function_mask)
1717c9b4e987SNeel Natu 		return;
1718c9b4e987SNeel Natu 
1719c9b4e987SNeel Natu 	if (index >= pi->pi_msix.table_count)
1720c9b4e987SNeel Natu 		return;
1721c9b4e987SNeel Natu 
1722c9b4e987SNeel Natu 	mte = &pi->pi_msix.table[index];
1723c9b4e987SNeel Natu 	if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
1724c9b4e987SNeel Natu 		/* XXX Set PBA bit if interrupt is disabled */
17254f8be175SNeel Natu 		vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data);
1726c9b4e987SNeel Natu 	}
1727c9b4e987SNeel Natu }
1728c9b4e987SNeel Natu 
1729366f6083SPeter Grehan void
17304f8be175SNeel Natu pci_generate_msi(struct pci_devinst *pi, int index)
1731366f6083SPeter Grehan {
1732366f6083SPeter Grehan 
17334f8be175SNeel Natu 	if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) {
17344f8be175SNeel Natu 		vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr,
17354f8be175SNeel Natu 			     pi->pi_msi.msg_data + index);
1736366f6083SPeter Grehan 	}
1737366f6083SPeter Grehan }
1738366f6083SPeter Grehan 
17393cbf3585SJohn Baldwin static bool
17403cbf3585SJohn Baldwin pci_lintr_permitted(struct pci_devinst *pi)
17410038ee98SPeter Grehan {
17423cbf3585SJohn Baldwin 	uint16_t cmd;
17430038ee98SPeter Grehan 
17443cbf3585SJohn Baldwin 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
17453cbf3585SJohn Baldwin 	return (!(pi->pi_msi.enabled || pi->pi_msix.enabled ||
17463cbf3585SJohn Baldwin 		(cmd & PCIM_CMD_INTxDIS)));
17473cbf3585SJohn Baldwin }
17483cbf3585SJohn Baldwin 
1749b3e9732aSJohn Baldwin void
17503cbf3585SJohn Baldwin pci_lintr_request(struct pci_devinst *pi)
17513cbf3585SJohn Baldwin {
1752d84882caSNeel Natu 	struct businfo *bi;
17533cbf3585SJohn Baldwin 	struct slotinfo *si;
1754b3e9732aSJohn Baldwin 	int bestpin, bestcount, pin;
17553cbf3585SJohn Baldwin 
1756d84882caSNeel Natu 	bi = pci_businfo[pi->pi_bus];
1757d84882caSNeel Natu 	assert(bi != NULL);
1758d84882caSNeel Natu 
17593cbf3585SJohn Baldwin 	/*
1760b3e9732aSJohn Baldwin 	 * Just allocate a pin from our slot.  The pin will be
1761b3e9732aSJohn Baldwin 	 * assigned IRQs later when interrupts are routed.
17623cbf3585SJohn Baldwin 	 */
1763d84882caSNeel Natu 	si = &bi->slotinfo[pi->pi_slot];
17643cbf3585SJohn Baldwin 	bestpin = 0;
17653cbf3585SJohn Baldwin 	bestcount = si->si_intpins[0].ii_count;
17663cbf3585SJohn Baldwin 	for (pin = 1; pin < 4; pin++) {
17673cbf3585SJohn Baldwin 		if (si->si_intpins[pin].ii_count < bestcount) {
17683cbf3585SJohn Baldwin 			bestpin = pin;
17693cbf3585SJohn Baldwin 			bestcount = si->si_intpins[pin].ii_count;
17703cbf3585SJohn Baldwin 		}
17713cbf3585SJohn Baldwin 	}
17723cbf3585SJohn Baldwin 
17733cbf3585SJohn Baldwin 	si->si_intpins[bestpin].ii_count++;
17743cbf3585SJohn Baldwin 	pi->pi_lintr.pin = bestpin + 1;
17753cbf3585SJohn Baldwin 	pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1);
1776b3e9732aSJohn Baldwin }
1777b3e9732aSJohn Baldwin 
1778b3e9732aSJohn Baldwin static void
1779b3e9732aSJohn Baldwin pci_lintr_route(struct pci_devinst *pi)
1780b3e9732aSJohn Baldwin {
1781b3e9732aSJohn Baldwin 	struct businfo *bi;
1782b3e9732aSJohn Baldwin 	struct intxinfo *ii;
1783b3e9732aSJohn Baldwin 
1784b3e9732aSJohn Baldwin 	if (pi->pi_lintr.pin == 0)
1785b3e9732aSJohn Baldwin 		return;
1786b3e9732aSJohn Baldwin 
1787b3e9732aSJohn Baldwin 	bi = pci_businfo[pi->pi_bus];
1788b3e9732aSJohn Baldwin 	assert(bi != NULL);
1789b3e9732aSJohn Baldwin 	ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1];
1790b3e9732aSJohn Baldwin 
1791b3e9732aSJohn Baldwin 	/*
1792b3e9732aSJohn Baldwin 	 * Attempt to allocate an I/O APIC pin for this intpin if one
1793b3e9732aSJohn Baldwin 	 * is not yet assigned.
1794b3e9732aSJohn Baldwin 	 */
1795b3e9732aSJohn Baldwin 	if (ii->ii_ioapic_irq == 0)
17961b4496d0SAlexander Motin 		ii->ii_ioapic_irq = ioapic_pci_alloc_irq(pi);
1797b3e9732aSJohn Baldwin 	assert(ii->ii_ioapic_irq > 0);
1798b3e9732aSJohn Baldwin 
1799b3e9732aSJohn Baldwin 	/*
1800b3e9732aSJohn Baldwin 	 * Attempt to allocate a PIRQ pin for this intpin if one is
1801b3e9732aSJohn Baldwin 	 * not yet assigned.
1802b3e9732aSJohn Baldwin 	 */
1803b3e9732aSJohn Baldwin 	if (ii->ii_pirq_pin == 0)
18041b4496d0SAlexander Motin 		ii->ii_pirq_pin = pirq_alloc_pin(pi);
1805b3e9732aSJohn Baldwin 	assert(ii->ii_pirq_pin > 0);
1806b3e9732aSJohn Baldwin 
1807b3e9732aSJohn Baldwin 	pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq;
1808b3e9732aSJohn Baldwin 	pi->pi_lintr.pirq_pin = ii->ii_pirq_pin;
1809b3e9732aSJohn Baldwin 	pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin));
18100038ee98SPeter Grehan }
18110038ee98SPeter Grehan 
18120038ee98SPeter Grehan void
18130038ee98SPeter Grehan pci_lintr_assert(struct pci_devinst *pi)
18140038ee98SPeter Grehan {
18150038ee98SPeter Grehan 
18163cbf3585SJohn Baldwin 	assert(pi->pi_lintr.pin > 0);
1817ac7304a7SNeel Natu 
18183cbf3585SJohn Baldwin 	pthread_mutex_lock(&pi->pi_lintr.lock);
18193cbf3585SJohn Baldwin 	if (pi->pi_lintr.state == IDLE) {
18203cbf3585SJohn Baldwin 		if (pci_lintr_permitted(pi)) {
18213cbf3585SJohn Baldwin 			pi->pi_lintr.state = ASSERTED;
1822b3e9732aSJohn Baldwin 			pci_irq_assert(pi);
18233cbf3585SJohn Baldwin 		} else
18243cbf3585SJohn Baldwin 			pi->pi_lintr.state = PENDING;
18250038ee98SPeter Grehan 	}
18263cbf3585SJohn Baldwin 	pthread_mutex_unlock(&pi->pi_lintr.lock);
1827ac7304a7SNeel Natu }
18280038ee98SPeter Grehan 
18290038ee98SPeter Grehan void
18300038ee98SPeter Grehan pci_lintr_deassert(struct pci_devinst *pi)
18310038ee98SPeter Grehan {
18320038ee98SPeter Grehan 
18333cbf3585SJohn Baldwin 	assert(pi->pi_lintr.pin > 0);
1834ac7304a7SNeel Natu 
18353cbf3585SJohn Baldwin 	pthread_mutex_lock(&pi->pi_lintr.lock);
18363cbf3585SJohn Baldwin 	if (pi->pi_lintr.state == ASSERTED) {
18373cbf3585SJohn Baldwin 		pi->pi_lintr.state = IDLE;
1838b3e9732aSJohn Baldwin 		pci_irq_deassert(pi);
18393cbf3585SJohn Baldwin 	} else if (pi->pi_lintr.state == PENDING)
18403cbf3585SJohn Baldwin 		pi->pi_lintr.state = IDLE;
18413cbf3585SJohn Baldwin 	pthread_mutex_unlock(&pi->pi_lintr.lock);
18423cbf3585SJohn Baldwin }
18433cbf3585SJohn Baldwin 
18443cbf3585SJohn Baldwin static void
18453cbf3585SJohn Baldwin pci_lintr_update(struct pci_devinst *pi)
18463cbf3585SJohn Baldwin {
18473cbf3585SJohn Baldwin 
18483cbf3585SJohn Baldwin 	pthread_mutex_lock(&pi->pi_lintr.lock);
18493cbf3585SJohn Baldwin 	if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) {
1850b3e9732aSJohn Baldwin 		pci_irq_deassert(pi);
18513cbf3585SJohn Baldwin 		pi->pi_lintr.state = PENDING;
18523cbf3585SJohn Baldwin 	} else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) {
18533cbf3585SJohn Baldwin 		pi->pi_lintr.state = ASSERTED;
1854b3e9732aSJohn Baldwin 		pci_irq_assert(pi);
18553cbf3585SJohn Baldwin 	}
18563cbf3585SJohn Baldwin 	pthread_mutex_unlock(&pi->pi_lintr.lock);
18573cbf3585SJohn Baldwin }
18583cbf3585SJohn Baldwin 
18593cbf3585SJohn Baldwin int
1860d84882caSNeel Natu pci_count_lintr(int bus)
18613cbf3585SJohn Baldwin {
18623cbf3585SJohn Baldwin 	int count, slot, pin;
1863d84882caSNeel Natu 	struct slotinfo *slotinfo;
18643cbf3585SJohn Baldwin 
18653cbf3585SJohn Baldwin 	count = 0;
1866d84882caSNeel Natu 	if (pci_businfo[bus] != NULL) {
18673cbf3585SJohn Baldwin 		for (slot = 0; slot < MAXSLOTS; slot++) {
1868d84882caSNeel Natu 			slotinfo = &pci_businfo[bus]->slotinfo[slot];
18693cbf3585SJohn Baldwin 			for (pin = 0; pin < 4; pin++) {
1870d84882caSNeel Natu 				if (slotinfo->si_intpins[pin].ii_count != 0)
18713cbf3585SJohn Baldwin 					count++;
18723cbf3585SJohn Baldwin 			}
18733cbf3585SJohn Baldwin 		}
1874d84882caSNeel Natu 	}
18753cbf3585SJohn Baldwin 	return (count);
18763cbf3585SJohn Baldwin }
18773cbf3585SJohn Baldwin 
18783cbf3585SJohn Baldwin void
1879d84882caSNeel Natu pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg)
18803cbf3585SJohn Baldwin {
1881d84882caSNeel Natu 	struct businfo *bi;
1882d84882caSNeel Natu 	struct slotinfo *si;
18833cbf3585SJohn Baldwin 	struct intxinfo *ii;
18843cbf3585SJohn Baldwin 	int slot, pin;
18853cbf3585SJohn Baldwin 
1886d84882caSNeel Natu 	if ((bi = pci_businfo[bus]) == NULL)
1887d84882caSNeel Natu 		return;
1888d84882caSNeel Natu 
18893cbf3585SJohn Baldwin 	for (slot = 0; slot < MAXSLOTS; slot++) {
1890d84882caSNeel Natu 		si = &bi->slotinfo[slot];
18913cbf3585SJohn Baldwin 		for (pin = 0; pin < 4; pin++) {
1892d84882caSNeel Natu 			ii = &si->si_intpins[pin];
18933cbf3585SJohn Baldwin 			if (ii->ii_count != 0)
1894b3e9732aSJohn Baldwin 				cb(bus, slot, pin + 1, ii->ii_pirq_pin,
1895b3e9732aSJohn Baldwin 				    ii->ii_ioapic_irq, arg);
18963cbf3585SJohn Baldwin 		}
18970038ee98SPeter Grehan 	}
1898ac7304a7SNeel Natu }
18990038ee98SPeter Grehan 
190099d65389SNeel Natu /*
190199d65389SNeel Natu  * Return 1 if the emulated device in 'slot' is a multi-function device.
190299d65389SNeel Natu  * Return 0 otherwise.
190399d65389SNeel Natu  */
190499d65389SNeel Natu static int
1905d84882caSNeel Natu pci_emul_is_mfdev(int bus, int slot)
190699d65389SNeel Natu {
1907d84882caSNeel Natu 	struct businfo *bi;
1908d84882caSNeel Natu 	struct slotinfo *si;
190999d65389SNeel Natu 	int f, numfuncs;
19100038ee98SPeter Grehan 
191199d65389SNeel Natu 	numfuncs = 0;
1912d84882caSNeel Natu 	if ((bi = pci_businfo[bus]) != NULL) {
1913d84882caSNeel Natu 		si = &bi->slotinfo[slot];
191499d65389SNeel Natu 		for (f = 0; f < MAXFUNCS; f++) {
1915d84882caSNeel Natu 			if (si->si_funcs[f].fi_devi != NULL) {
191699d65389SNeel Natu 				numfuncs++;
191799d65389SNeel Natu 			}
191899d65389SNeel Natu 		}
1919d84882caSNeel Natu 	}
192099d65389SNeel Natu 	return (numfuncs > 1);
192199d65389SNeel Natu }
192299d65389SNeel Natu 
192399d65389SNeel Natu /*
192499d65389SNeel Natu  * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on
192599d65389SNeel Natu  * whether or not is a multi-function being emulated in the pci 'slot'.
192699d65389SNeel Natu  */
192799d65389SNeel Natu static void
1928d84882caSNeel Natu pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv)
192999d65389SNeel Natu {
193099d65389SNeel Natu 	int mfdev;
193199d65389SNeel Natu 
193299d65389SNeel Natu 	if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) {
1933d84882caSNeel Natu 		mfdev = pci_emul_is_mfdev(bus, slot);
193499d65389SNeel Natu 		switch (bytes) {
193599d65389SNeel Natu 		case 1:
193699d65389SNeel Natu 		case 2:
193799d65389SNeel Natu 			*rv &= ~PCIM_MFDEV;
193899d65389SNeel Natu 			if (mfdev) {
193999d65389SNeel Natu 				*rv |= PCIM_MFDEV;
194099d65389SNeel Natu 			}
194199d65389SNeel Natu 			break;
194299d65389SNeel Natu 		case 4:
194399d65389SNeel Natu 			*rv &= ~(PCIM_MFDEV << 16);
194499d65389SNeel Natu 			if (mfdev) {
194599d65389SNeel Natu 				*rv |= (PCIM_MFDEV << 16);
194699d65389SNeel Natu 			}
194799d65389SNeel Natu 			break;
194899d65389SNeel Natu 		}
194999d65389SNeel Natu 	}
195099d65389SNeel Natu }
19510038ee98SPeter Grehan 
195256282675SJohn Baldwin /*
195356282675SJohn Baldwin  * Update device state in response to changes to the PCI command
195456282675SJohn Baldwin  * register.
195556282675SJohn Baldwin  */
195656282675SJohn Baldwin void
195756282675SJohn Baldwin pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old)
195856282675SJohn Baldwin {
195956282675SJohn Baldwin 	int i;
196056282675SJohn Baldwin 	uint16_t changed, new;
196156282675SJohn Baldwin 
196256282675SJohn Baldwin 	new = pci_get_cfgdata16(pi, PCIR_COMMAND);
196356282675SJohn Baldwin 	changed = old ^ new;
196456282675SJohn Baldwin 
196556282675SJohn Baldwin 	/*
196656282675SJohn Baldwin 	 * If the MMIO or I/O address space decoding has changed then
196756282675SJohn Baldwin 	 * register/unregister all BARs that decode that address space.
196856282675SJohn Baldwin 	 */
1969e47fe318SCorvin Köhne 	for (i = 0; i <= PCI_BARMAX_WITH_ROM; i++) {
197056282675SJohn Baldwin 		switch (pi->pi_bar[i].type) {
197156282675SJohn Baldwin 			case PCIBAR_NONE:
197256282675SJohn Baldwin 			case PCIBAR_MEMHI64:
197356282675SJohn Baldwin 				break;
197456282675SJohn Baldwin 			case PCIBAR_IO:
197556282675SJohn Baldwin 				/* I/O address space decoding changed? */
197656282675SJohn Baldwin 				if (changed & PCIM_CMD_PORTEN) {
197756282675SJohn Baldwin 					if (new & PCIM_CMD_PORTEN)
197856282675SJohn Baldwin 						register_bar(pi, i);
197956282675SJohn Baldwin 					else
198056282675SJohn Baldwin 						unregister_bar(pi, i);
198156282675SJohn Baldwin 				}
198256282675SJohn Baldwin 				break;
1983e47fe318SCorvin Köhne 			case PCIBAR_ROM:
1984e47fe318SCorvin Köhne 				/* skip (un-)register of ROM if it disabled */
1985e47fe318SCorvin Köhne 				if (!romen(pi))
1986e47fe318SCorvin Köhne 					break;
1987e47fe318SCorvin Köhne 				/* fallthrough */
198856282675SJohn Baldwin 			case PCIBAR_MEM32:
198956282675SJohn Baldwin 			case PCIBAR_MEM64:
199056282675SJohn Baldwin 				/* MMIO address space decoding changed? */
199156282675SJohn Baldwin 				if (changed & PCIM_CMD_MEMEN) {
199256282675SJohn Baldwin 					if (new & PCIM_CMD_MEMEN)
199356282675SJohn Baldwin 						register_bar(pi, i);
199456282675SJohn Baldwin 					else
199556282675SJohn Baldwin 						unregister_bar(pi, i);
199656282675SJohn Baldwin 				}
199756282675SJohn Baldwin 				break;
199856282675SJohn Baldwin 			default:
199956282675SJohn Baldwin 				assert(0);
200056282675SJohn Baldwin 		}
200156282675SJohn Baldwin 	}
200256282675SJohn Baldwin 
200356282675SJohn Baldwin 	/*
200456282675SJohn Baldwin 	 * If INTx has been unmasked and is pending, assert the
200556282675SJohn Baldwin 	 * interrupt.
200656282675SJohn Baldwin 	 */
200756282675SJohn Baldwin 	pci_lintr_update(pi);
200856282675SJohn Baldwin }
200956282675SJohn Baldwin 
2010028d9311SNeel Natu static void
201154335630SNeel Natu pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes)
2012028d9311SNeel Natu {
201356282675SJohn Baldwin 	int rshift;
201456282675SJohn Baldwin 	uint32_t cmd, old, readonly;
201554335630SNeel Natu 
201654335630SNeel Natu 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);	/* stash old value */
2017028d9311SNeel Natu 
2018028d9311SNeel Natu 	/*
201954335630SNeel Natu 	 * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3.
202054335630SNeel Natu 	 *
202154335630SNeel Natu 	 * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are
202254335630SNeel Natu 	 * 'write 1 to clear'. However these bits are not set to '1' by
202354335630SNeel Natu 	 * any device emulation so it is simpler to treat them as readonly.
2024028d9311SNeel Natu 	 */
202554335630SNeel Natu 	rshift = (coff & 0x3) * 8;
202654335630SNeel Natu 	readonly = 0xFFFFF880 >> rshift;
2027028d9311SNeel Natu 
202854335630SNeel Natu 	old = CFGREAD(pi, coff, bytes);
202954335630SNeel Natu 	new &= ~readonly;
203054335630SNeel Natu 	new |= (old & readonly);
203154335630SNeel Natu 	CFGWRITE(pi, coff, new, bytes);			/* update config */
203254335630SNeel Natu 
203356282675SJohn Baldwin 	pci_emul_cmd_changed(pi, cmd);
2034028d9311SNeel Natu }
2035028d9311SNeel Natu 
203612a6eb99SNeel Natu static void
203712a6eb99SNeel Natu pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
203812a6eb99SNeel Natu     int coff, int bytes, uint32_t *eax)
2039366f6083SPeter Grehan {
2040d84882caSNeel Natu 	struct businfo *bi;
2041d84882caSNeel Natu 	struct slotinfo *si;
2042366f6083SPeter Grehan 	struct pci_devinst *pi;
2043366f6083SPeter Grehan 	struct pci_devemu *pe;
204412a6eb99SNeel Natu 	int idx, needcfg;
2045028d9311SNeel Natu 	uint64_t addr, bar, mask;
2046366f6083SPeter Grehan 
204712a6eb99SNeel Natu 	if ((bi = pci_businfo[bus]) != NULL) {
204812a6eb99SNeel Natu 		si = &bi->slotinfo[slot];
204912a6eb99SNeel Natu 		pi = si->si_funcs[func].fi_devi;
2050d84882caSNeel Natu 	} else
205190415e0bSNeel Natu 		pi = NULL;
205290415e0bSNeel Natu 
205399d65389SNeel Natu 	/*
205412a6eb99SNeel Natu 	 * Just return if there is no device at this slot:func or if the
205512a6eb99SNeel Natu 	 * the guest is doing an un-aligned access.
205699d65389SNeel Natu 	 */
205712a6eb99SNeel Natu 	if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) ||
205812a6eb99SNeel Natu 	    (coff & (bytes - 1)) != 0) {
2059366f6083SPeter Grehan 		if (in)
2060366f6083SPeter Grehan 			*eax = 0xffffffff;
206112a6eb99SNeel Natu 		return;
206212a6eb99SNeel Natu 	}
206312a6eb99SNeel Natu 
206412a6eb99SNeel Natu 	/*
206512a6eb99SNeel Natu 	 * Ignore all writes beyond the standard config space and return all
206612a6eb99SNeel Natu 	 * ones on reads.
206712a6eb99SNeel Natu 	 */
206812a6eb99SNeel Natu 	if (coff >= PCI_REGMAX + 1) {
206912a6eb99SNeel Natu 		if (in) {
207012a6eb99SNeel Natu 			*eax = 0xffffffff;
207112a6eb99SNeel Natu 			/*
207212a6eb99SNeel Natu 			 * Extended capabilities begin at offset 256 in config
207312a6eb99SNeel Natu 			 * space. Absence of extended capabilities is signaled
207412a6eb99SNeel Natu 			 * with all 0s in the extended capability header at
207512a6eb99SNeel Natu 			 * offset 256.
207612a6eb99SNeel Natu 			 */
207712a6eb99SNeel Natu 			if (coff <= PCI_REGMAX + 4)
207812a6eb99SNeel Natu 				*eax = 0x00000000;
207912a6eb99SNeel Natu 		}
208012a6eb99SNeel Natu 		return;
2081366f6083SPeter Grehan 	}
2082366f6083SPeter Grehan 
2083366f6083SPeter Grehan 	pe = pi->pi_d;
2084366f6083SPeter Grehan 
2085366f6083SPeter Grehan 	/*
2086366f6083SPeter Grehan 	 * Config read
2087366f6083SPeter Grehan 	 */
2088366f6083SPeter Grehan 	if (in) {
2089366f6083SPeter Grehan 		/* Let the device emulation override the default handler */
209099d65389SNeel Natu 		if (pe->pe_cfgread != NULL) {
209112a6eb99SNeel Natu 			needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes,
209212a6eb99SNeel Natu 			    eax);
209399d65389SNeel Natu 		} else {
209499d65389SNeel Natu 			needcfg = 1;
209599d65389SNeel Natu 		}
2096366f6083SPeter Grehan 
209754335630SNeel Natu 		if (needcfg)
209854335630SNeel Natu 			*eax = CFGREAD(pi, coff, bytes);
209999d65389SNeel Natu 
210012a6eb99SNeel Natu 		pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax);
2101366f6083SPeter Grehan 	} else {
2102366f6083SPeter Grehan 		/* Let the device emulation override the default handler */
2103366f6083SPeter Grehan 		if (pe->pe_cfgwrite != NULL &&
2104366f6083SPeter Grehan 		    (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0)
210512a6eb99SNeel Natu 			return;
2106366f6083SPeter Grehan 
2107366f6083SPeter Grehan 		/*
2108e47fe318SCorvin Köhne 		 * Special handling for write to BAR and ROM registers
2109366f6083SPeter Grehan 		 */
2110e47fe318SCorvin Köhne 		if ((coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) ||
2111e47fe318SCorvin Köhne 		    (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4)) {
2112366f6083SPeter Grehan 			/*
2113366f6083SPeter Grehan 			 * Ignore writes to BAR registers that are not
2114366f6083SPeter Grehan 			 * 4-byte aligned.
2115366f6083SPeter Grehan 			 */
2116366f6083SPeter Grehan 			if (bytes != 4 || (coff & 0x3) != 0)
211712a6eb99SNeel Natu 				return;
2118e47fe318SCorvin Köhne 			if (coff != PCIR_BIOS) {
2119366f6083SPeter Grehan 				idx = (coff - PCIR_BAR(0)) / 4;
2120e47fe318SCorvin Köhne 			} else {
2121e47fe318SCorvin Köhne 				idx = PCI_ROM_IDX;
2122e47fe318SCorvin Köhne 			}
2123028d9311SNeel Natu 			mask = ~(pi->pi_bar[idx].size - 1);
2124366f6083SPeter Grehan 			switch (pi->pi_bar[idx].type) {
2125366f6083SPeter Grehan 			case PCIBAR_NONE:
2126028d9311SNeel Natu 				pi->pi_bar[idx].addr = bar = 0;
2127366f6083SPeter Grehan 				break;
2128366f6083SPeter Grehan 			case PCIBAR_IO:
2129028d9311SNeel Natu 				addr = *eax & mask;
2130028d9311SNeel Natu 				addr &= 0xffff;
2131e87a6f3eSCorvin Köhne 				bar = addr | pi->pi_bar[idx].lobits;
2132028d9311SNeel Natu 				/*
2133028d9311SNeel Natu 				 * Register the new BAR value for interception
2134028d9311SNeel Natu 				 */
2135028d9311SNeel Natu 				if (addr != pi->pi_bar[idx].addr) {
2136028d9311SNeel Natu 					update_bar_address(pi, addr, idx,
2137028d9311SNeel Natu 							   PCIBAR_IO);
2138028d9311SNeel Natu 				}
2139366f6083SPeter Grehan 				break;
2140366f6083SPeter Grehan 			case PCIBAR_MEM32:
2141028d9311SNeel Natu 				addr = bar = *eax & mask;
2142e87a6f3eSCorvin Köhne 				bar |= pi->pi_bar[idx].lobits;
2143028d9311SNeel Natu 				if (addr != pi->pi_bar[idx].addr) {
2144028d9311SNeel Natu 					update_bar_address(pi, addr, idx,
2145028d9311SNeel Natu 							   PCIBAR_MEM32);
2146028d9311SNeel Natu 				}
2147366f6083SPeter Grehan 				break;
2148366f6083SPeter Grehan 			case PCIBAR_MEM64:
2149028d9311SNeel Natu 				addr = bar = *eax & mask;
2150e87a6f3eSCorvin Köhne 				bar |= pi->pi_bar[idx].lobits;
2151028d9311SNeel Natu 				if (addr != (uint32_t)pi->pi_bar[idx].addr) {
2152028d9311SNeel Natu 					update_bar_address(pi, addr, idx,
2153028d9311SNeel Natu 							   PCIBAR_MEM64);
2154028d9311SNeel Natu 				}
2155366f6083SPeter Grehan 				break;
2156366f6083SPeter Grehan 			case PCIBAR_MEMHI64:
2157366f6083SPeter Grehan 				mask = ~(pi->pi_bar[idx - 1].size - 1);
2158028d9311SNeel Natu 				addr = ((uint64_t)*eax << 32) & mask;
2159028d9311SNeel Natu 				bar = addr >> 32;
2160028d9311SNeel Natu 				if (bar != pi->pi_bar[idx - 1].addr >> 32) {
2161028d9311SNeel Natu 					update_bar_address(pi, addr, idx - 1,
2162028d9311SNeel Natu 							   PCIBAR_MEMHI64);
2163028d9311SNeel Natu 				}
2164366f6083SPeter Grehan 				break;
2165e47fe318SCorvin Köhne 			case PCIBAR_ROM:
2166e47fe318SCorvin Köhne 				addr = bar = *eax & mask;
2167e47fe318SCorvin Köhne 				if (memen(pi) && romen(pi)) {
2168e47fe318SCorvin Köhne 					unregister_bar(pi, idx);
2169e47fe318SCorvin Köhne 				}
2170e47fe318SCorvin Köhne 				pi->pi_bar[idx].addr = addr;
2171e47fe318SCorvin Köhne 				pi->pi_bar[idx].lobits = *eax &
2172e47fe318SCorvin Köhne 				    PCIM_BIOS_ENABLE;
2173e47fe318SCorvin Köhne 				/* romen could have changed it value */
2174e47fe318SCorvin Köhne 				if (memen(pi) && romen(pi)) {
2175e47fe318SCorvin Köhne 					register_bar(pi, idx);
2176e47fe318SCorvin Köhne 				}
2177e47fe318SCorvin Köhne 				bar |= pi->pi_bar[idx].lobits;
2178e47fe318SCorvin Köhne 				break;
2179366f6083SPeter Grehan 			default:
2180366f6083SPeter Grehan 				assert(0);
2181366f6083SPeter Grehan 			}
2182366f6083SPeter Grehan 			pci_set_cfgdata32(pi, coff, bar);
2183cd942e0fSPeter Grehan 
2184366f6083SPeter Grehan 		} else if (pci_emul_iscap(pi, coff)) {
218521368498SPeter Grehan 			pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0);
218654335630SNeel Natu 		} else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) {
218754335630SNeel Natu 			pci_emul_cmdsts_write(pi, coff, *eax, bytes);
2188366f6083SPeter Grehan 		} else {
2189366f6083SPeter Grehan 			CFGWRITE(pi, coff, *eax, bytes);
2190366f6083SPeter Grehan 		}
2191366f6083SPeter Grehan 	}
219212a6eb99SNeel Natu }
2193366f6083SPeter Grehan 
219412a6eb99SNeel Natu static int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff;
219512a6eb99SNeel Natu 
219612a6eb99SNeel Natu static int
219712a6eb99SNeel Natu pci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
219812a6eb99SNeel Natu 		 uint32_t *eax, void *arg)
219912a6eb99SNeel Natu {
220012a6eb99SNeel Natu 	uint32_t x;
220112a6eb99SNeel Natu 
220212a6eb99SNeel Natu 	if (bytes != 4) {
220312a6eb99SNeel Natu 		if (in)
220412a6eb99SNeel Natu 			*eax = (bytes == 2) ? 0xffff : 0xff;
220512a6eb99SNeel Natu 		return (0);
220612a6eb99SNeel Natu 	}
220712a6eb99SNeel Natu 
220812a6eb99SNeel Natu 	if (in) {
220912a6eb99SNeel Natu 		x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff;
221012a6eb99SNeel Natu 		if (cfgenable)
221112a6eb99SNeel Natu 			x |= CONF1_ENABLE;
221212a6eb99SNeel Natu 		*eax = x;
221312a6eb99SNeel Natu 	} else {
221412a6eb99SNeel Natu 		x = *eax;
221512a6eb99SNeel Natu 		cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE;
22161b0e2f0bSCorvin Köhne 		cfgoff = (x & PCI_REGMAX) & ~0x03;
221712a6eb99SNeel Natu 		cfgfunc = (x >> 8) & PCI_FUNCMAX;
221812a6eb99SNeel Natu 		cfgslot = (x >> 11) & PCI_SLOTMAX;
221912a6eb99SNeel Natu 		cfgbus = (x >> 16) & PCI_BUSMAX;
222012a6eb99SNeel Natu 	}
222112a6eb99SNeel Natu 
222212a6eb99SNeel Natu 	return (0);
222312a6eb99SNeel Natu }
222412a6eb99SNeel Natu INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr);
222512a6eb99SNeel Natu 
222612a6eb99SNeel Natu static int
222712a6eb99SNeel Natu pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
222812a6eb99SNeel Natu 		 uint32_t *eax, void *arg)
222912a6eb99SNeel Natu {
223012a6eb99SNeel Natu 	int coff;
223112a6eb99SNeel Natu 
223212a6eb99SNeel Natu 	assert(bytes == 1 || bytes == 2 || bytes == 4);
223312a6eb99SNeel Natu 
223412a6eb99SNeel Natu 	coff = cfgoff + (port - CONF1_DATA_PORT);
223512a6eb99SNeel Natu 	if (cfgenable) {
223612a6eb99SNeel Natu 		pci_cfgrw(ctx, vcpu, in, cfgbus, cfgslot, cfgfunc, coff, bytes,
223712a6eb99SNeel Natu 		    eax);
223812a6eb99SNeel Natu 	} else {
223912a6eb99SNeel Natu 		/* Ignore accesses to cfgdata if not enabled by cfgaddr */
224012a6eb99SNeel Natu 		if (in)
224112a6eb99SNeel Natu 			*eax = 0xffffffff;
224212a6eb99SNeel Natu 	}
2243366f6083SPeter Grehan 	return (0);
2244366f6083SPeter Grehan }
2245366f6083SPeter Grehan 
2246366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata);
2247366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata);
2248366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
2249366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
2250366f6083SPeter Grehan 
2251483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2252483d953aSJohn Baldwin /*
2253483d953aSJohn Baldwin  * Saves/restores PCI device emulated state. Returns 0 on success.
2254483d953aSJohn Baldwin  */
2255483d953aSJohn Baldwin static int
2256483d953aSJohn Baldwin pci_snapshot_pci_dev(struct vm_snapshot_meta *meta)
2257483d953aSJohn Baldwin {
2258483d953aSJohn Baldwin 	struct pci_devinst *pi;
2259483d953aSJohn Baldwin 	int i;
2260483d953aSJohn Baldwin 	int ret;
2261483d953aSJohn Baldwin 
2262483d953aSJohn Baldwin 	pi = meta->dev_data;
2263483d953aSJohn Baldwin 
2264483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.enabled, meta, ret, done);
2265483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.addr, meta, ret, done);
2266483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.msg_data, meta, ret, done);
2267483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.maxmsgnum, meta, ret, done);
2268483d953aSJohn Baldwin 
2269483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.enabled, meta, ret, done);
2270483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_bar, meta, ret, done);
2271483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_bar, meta, ret, done);
2272483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_offset, meta, ret, done);
2273483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_count, meta, ret, done);
2274483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_offset, meta, ret, done);
2275483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_size, meta, ret, done);
2276483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.function_mask, meta, ret, done);
2277483d953aSJohn Baldwin 
2278483d953aSJohn Baldwin 	SNAPSHOT_BUF_OR_LEAVE(pi->pi_cfgdata, sizeof(pi->pi_cfgdata),
2279483d953aSJohn Baldwin 			      meta, ret, done);
2280483d953aSJohn Baldwin 
2281483d953aSJohn Baldwin 	for (i = 0; i < nitems(pi->pi_bar); i++) {
2282483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].type, meta, ret, done);
2283483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].size, meta, ret, done);
2284483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].addr, meta, ret, done);
2285483d953aSJohn Baldwin 	}
2286483d953aSJohn Baldwin 
2287483d953aSJohn Baldwin 	/* Restore MSI-X table. */
2288483d953aSJohn Baldwin 	for (i = 0; i < pi->pi_msix.table_count; i++) {
2289483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].addr,
2290483d953aSJohn Baldwin 				      meta, ret, done);
2291483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].msg_data,
2292483d953aSJohn Baldwin 				      meta, ret, done);
2293483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].vector_control,
2294483d953aSJohn Baldwin 				      meta, ret, done);
2295483d953aSJohn Baldwin 	}
2296483d953aSJohn Baldwin 
2297483d953aSJohn Baldwin done:
2298483d953aSJohn Baldwin 	return (ret);
2299483d953aSJohn Baldwin }
2300483d953aSJohn Baldwin 
2301483d953aSJohn Baldwin static int
2302483d953aSJohn Baldwin pci_find_slotted_dev(const char *dev_name, struct pci_devemu **pde,
2303483d953aSJohn Baldwin 		     struct pci_devinst **pdi)
2304483d953aSJohn Baldwin {
2305483d953aSJohn Baldwin 	struct businfo *bi;
2306483d953aSJohn Baldwin 	struct slotinfo *si;
2307483d953aSJohn Baldwin 	struct funcinfo *fi;
2308483d953aSJohn Baldwin 	int bus, slot, func;
2309483d953aSJohn Baldwin 
2310483d953aSJohn Baldwin 	assert(dev_name != NULL);
2311483d953aSJohn Baldwin 	assert(pde != NULL);
2312483d953aSJohn Baldwin 	assert(pdi != NULL);
2313483d953aSJohn Baldwin 
2314483d953aSJohn Baldwin 	for (bus = 0; bus < MAXBUSES; bus++) {
2315483d953aSJohn Baldwin 		if ((bi = pci_businfo[bus]) == NULL)
2316483d953aSJohn Baldwin 			continue;
2317483d953aSJohn Baldwin 
2318483d953aSJohn Baldwin 		for (slot = 0; slot < MAXSLOTS; slot++) {
2319483d953aSJohn Baldwin 			si = &bi->slotinfo[slot];
2320483d953aSJohn Baldwin 			for (func = 0; func < MAXFUNCS; func++) {
2321483d953aSJohn Baldwin 				fi = &si->si_funcs[func];
2322621b5090SJohn Baldwin 				if (fi->fi_pde == NULL)
2323483d953aSJohn Baldwin 					continue;
2324621b5090SJohn Baldwin 				if (strcmp(dev_name, fi->fi_pde->pe_emu) != 0)
2325483d953aSJohn Baldwin 					continue;
2326483d953aSJohn Baldwin 
2327621b5090SJohn Baldwin 				*pde = fi->fi_pde;
2328483d953aSJohn Baldwin 				*pdi = fi->fi_devi;
2329483d953aSJohn Baldwin 				return (0);
2330483d953aSJohn Baldwin 			}
2331483d953aSJohn Baldwin 		}
2332483d953aSJohn Baldwin 	}
2333483d953aSJohn Baldwin 
2334483d953aSJohn Baldwin 	return (EINVAL);
2335483d953aSJohn Baldwin }
2336483d953aSJohn Baldwin 
2337483d953aSJohn Baldwin int
2338483d953aSJohn Baldwin pci_snapshot(struct vm_snapshot_meta *meta)
2339483d953aSJohn Baldwin {
2340483d953aSJohn Baldwin 	struct pci_devemu *pde;
2341483d953aSJohn Baldwin 	struct pci_devinst *pdi;
2342483d953aSJohn Baldwin 	int ret;
2343483d953aSJohn Baldwin 
2344483d953aSJohn Baldwin 	assert(meta->dev_name != NULL);
2345483d953aSJohn Baldwin 
2346483d953aSJohn Baldwin 	ret = pci_find_slotted_dev(meta->dev_name, &pde, &pdi);
2347483d953aSJohn Baldwin 	if (ret != 0) {
2348483d953aSJohn Baldwin 		fprintf(stderr, "%s: no such name: %s\r\n",
2349483d953aSJohn Baldwin 			__func__, meta->dev_name);
2350483d953aSJohn Baldwin 		memset(meta->buffer.buf_start, 0, meta->buffer.buf_size);
2351483d953aSJohn Baldwin 		return (0);
2352483d953aSJohn Baldwin 	}
2353483d953aSJohn Baldwin 
2354483d953aSJohn Baldwin 	meta->dev_data = pdi;
2355483d953aSJohn Baldwin 
2356483d953aSJohn Baldwin 	if (pde->pe_snapshot == NULL) {
2357483d953aSJohn Baldwin 		fprintf(stderr, "%s: not implemented yet for: %s\r\n",
2358483d953aSJohn Baldwin 			__func__, meta->dev_name);
2359483d953aSJohn Baldwin 		return (-1);
2360483d953aSJohn Baldwin 	}
2361483d953aSJohn Baldwin 
2362483d953aSJohn Baldwin 	ret = pci_snapshot_pci_dev(meta);
2363483d953aSJohn Baldwin 	if (ret != 0) {
2364483d953aSJohn Baldwin 		fprintf(stderr, "%s: failed to snapshot pci dev\r\n",
2365483d953aSJohn Baldwin 			__func__);
2366483d953aSJohn Baldwin 		return (-1);
2367483d953aSJohn Baldwin 	}
2368483d953aSJohn Baldwin 
2369483d953aSJohn Baldwin 	ret = (*pde->pe_snapshot)(meta);
2370483d953aSJohn Baldwin 
2371483d953aSJohn Baldwin 	return (ret);
2372483d953aSJohn Baldwin }
2373483d953aSJohn Baldwin 
2374483d953aSJohn Baldwin int
2375483d953aSJohn Baldwin pci_pause(struct vmctx *ctx, const char *dev_name)
2376483d953aSJohn Baldwin {
2377483d953aSJohn Baldwin 	struct pci_devemu *pde;
2378483d953aSJohn Baldwin 	struct pci_devinst *pdi;
2379483d953aSJohn Baldwin 	int ret;
2380483d953aSJohn Baldwin 
2381483d953aSJohn Baldwin 	assert(dev_name != NULL);
2382483d953aSJohn Baldwin 
2383483d953aSJohn Baldwin 	ret = pci_find_slotted_dev(dev_name, &pde, &pdi);
2384483d953aSJohn Baldwin 	if (ret != 0) {
2385483d953aSJohn Baldwin 		/*
2386483d953aSJohn Baldwin 		 * It is possible to call this function without
2387483d953aSJohn Baldwin 		 * checking that the device is inserted first.
2388483d953aSJohn Baldwin 		 */
2389483d953aSJohn Baldwin 		fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name);
2390483d953aSJohn Baldwin 		return (0);
2391483d953aSJohn Baldwin 	}
2392483d953aSJohn Baldwin 
2393483d953aSJohn Baldwin 	if (pde->pe_pause == NULL) {
2394483d953aSJohn Baldwin 		/* The pause/resume functionality is optional. */
2395483d953aSJohn Baldwin 		fprintf(stderr, "%s: not implemented for: %s\n",
2396483d953aSJohn Baldwin 			__func__, dev_name);
2397483d953aSJohn Baldwin 		return (0);
2398483d953aSJohn Baldwin 	}
2399483d953aSJohn Baldwin 
2400483d953aSJohn Baldwin 	return (*pde->pe_pause)(ctx, pdi);
2401483d953aSJohn Baldwin }
2402483d953aSJohn Baldwin 
2403483d953aSJohn Baldwin int
2404483d953aSJohn Baldwin pci_resume(struct vmctx *ctx, const char *dev_name)
2405483d953aSJohn Baldwin {
2406483d953aSJohn Baldwin 	struct pci_devemu *pde;
2407483d953aSJohn Baldwin 	struct pci_devinst *pdi;
2408483d953aSJohn Baldwin 	int ret;
2409483d953aSJohn Baldwin 
2410483d953aSJohn Baldwin 	assert(dev_name != NULL);
2411483d953aSJohn Baldwin 
2412483d953aSJohn Baldwin 	ret = pci_find_slotted_dev(dev_name, &pde, &pdi);
2413483d953aSJohn Baldwin 	if (ret != 0) {
2414483d953aSJohn Baldwin 		/*
2415483d953aSJohn Baldwin 		 * It is possible to call this function without
2416483d953aSJohn Baldwin 		 * checking that the device is inserted first.
2417483d953aSJohn Baldwin 		 */
2418483d953aSJohn Baldwin 		fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name);
2419483d953aSJohn Baldwin 		return (0);
2420483d953aSJohn Baldwin 	}
2421483d953aSJohn Baldwin 
2422483d953aSJohn Baldwin 	if (pde->pe_resume == NULL) {
2423483d953aSJohn Baldwin 		/* The pause/resume functionality is optional. */
2424483d953aSJohn Baldwin 		fprintf(stderr, "%s: not implemented for: %s\n",
2425483d953aSJohn Baldwin 			__func__, dev_name);
2426483d953aSJohn Baldwin 		return (0);
2427483d953aSJohn Baldwin 	}
2428483d953aSJohn Baldwin 
2429483d953aSJohn Baldwin 	return (*pde->pe_resume)(ctx, pdi);
2430483d953aSJohn Baldwin }
2431483d953aSJohn Baldwin #endif
2432483d953aSJohn Baldwin 
2433366f6083SPeter Grehan #define PCI_EMUL_TEST
2434366f6083SPeter Grehan #ifdef PCI_EMUL_TEST
2435366f6083SPeter Grehan /*
2436366f6083SPeter Grehan  * Define a dummy test device
2437366f6083SPeter Grehan  */
2438d84882caSNeel Natu #define DIOSZ	8
24394d1e669cSPeter Grehan #define DMEMSZ	4096
2440366f6083SPeter Grehan struct pci_emul_dsoftc {
24414d1e669cSPeter Grehan 	uint8_t   ioregs[DIOSZ];
2442fd4e0d4cSNeel Natu 	uint8_t	  memregs[2][DMEMSZ];
2443366f6083SPeter Grehan };
2444366f6083SPeter Grehan 
24454d1e669cSPeter Grehan #define	PCI_EMUL_MSI_MSGS	 4
24464d1e669cSPeter Grehan #define	PCI_EMUL_MSIX_MSGS	16
2447366f6083SPeter Grehan 
2448b67e81dbSJohn Baldwin static int
2449621b5090SJohn Baldwin pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
2450366f6083SPeter Grehan {
2451366f6083SPeter Grehan 	int error;
2452366f6083SPeter Grehan 	struct pci_emul_dsoftc *sc;
2453366f6083SPeter Grehan 
2454994f858aSXin LI 	sc = calloc(1, sizeof(struct pci_emul_dsoftc));
2455366f6083SPeter Grehan 
2456366f6083SPeter Grehan 	pi->pi_arg = sc;
2457366f6083SPeter Grehan 
2458366f6083SPeter Grehan 	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001);
2459366f6083SPeter Grehan 	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD);
2460366f6083SPeter Grehan 	pci_set_cfgdata8(pi, PCIR_CLASS, 0x02);
2461366f6083SPeter Grehan 
24624d1e669cSPeter Grehan 	error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS);
2463366f6083SPeter Grehan 	assert(error == 0);
2464366f6083SPeter Grehan 
24654d1e669cSPeter Grehan 	error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ);
24664d1e669cSPeter Grehan 	assert(error == 0);
24674d1e669cSPeter Grehan 
24684d1e669cSPeter Grehan 	error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ);
2469366f6083SPeter Grehan 	assert(error == 0);
2470366f6083SPeter Grehan 
2471fd4e0d4cSNeel Natu 	error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ);
2472fd4e0d4cSNeel Natu 	assert(error == 0);
2473fd4e0d4cSNeel Natu 
2474366f6083SPeter Grehan 	return (0);
2475366f6083SPeter Grehan }
2476366f6083SPeter Grehan 
2477b67e81dbSJohn Baldwin static void
24784d1e669cSPeter Grehan pci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
24794d1e669cSPeter Grehan 	      uint64_t offset, int size, uint64_t value)
2480366f6083SPeter Grehan {
2481366f6083SPeter Grehan 	int i;
2482366f6083SPeter Grehan 	struct pci_emul_dsoftc *sc = pi->pi_arg;
2483366f6083SPeter Grehan 
24844d1e669cSPeter Grehan 	if (baridx == 0) {
24854d1e669cSPeter Grehan 		if (offset + size > DIOSZ) {
24864d1e669cSPeter Grehan 			printf("diow: iow too large, offset %ld size %d\n",
24874d1e669cSPeter Grehan 			       offset, size);
2488366f6083SPeter Grehan 			return;
2489366f6083SPeter Grehan 		}
2490366f6083SPeter Grehan 
2491366f6083SPeter Grehan 		if (size == 1) {
24924d1e669cSPeter Grehan 			sc->ioregs[offset] = value & 0xff;
2493366f6083SPeter Grehan 		} else if (size == 2) {
24944d1e669cSPeter Grehan 			*(uint16_t *)&sc->ioregs[offset] = value & 0xffff;
24954d1e669cSPeter Grehan 		} else if (size == 4) {
24964d1e669cSPeter Grehan 			*(uint32_t *)&sc->ioregs[offset] = value;
2497366f6083SPeter Grehan 		} else {
24984d1e669cSPeter Grehan 			printf("diow: iow unknown size %d\n", size);
2499366f6083SPeter Grehan 		}
2500366f6083SPeter Grehan 
2501366f6083SPeter Grehan 		/*
2502366f6083SPeter Grehan 		 * Special magic value to generate an interrupt
2503366f6083SPeter Grehan 		 */
2504366f6083SPeter Grehan 		if (offset == 4 && size == 4 && pci_msi_enabled(pi))
25054f8be175SNeel Natu 			pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi));
2506366f6083SPeter Grehan 
2507366f6083SPeter Grehan 		if (value == 0xabcdef) {
25084f8be175SNeel Natu 			for (i = 0; i < pci_msi_maxmsgnum(pi); i++)
2509366f6083SPeter Grehan 				pci_generate_msi(pi, i);
2510366f6083SPeter Grehan 		}
2511366f6083SPeter Grehan 	}
2512366f6083SPeter Grehan 
2513fd4e0d4cSNeel Natu 	if (baridx == 1 || baridx == 2) {
25144d1e669cSPeter Grehan 		if (offset + size > DMEMSZ) {
25154d1e669cSPeter Grehan 			printf("diow: memw too large, offset %ld size %d\n",
25164d1e669cSPeter Grehan 			       offset, size);
25174d1e669cSPeter Grehan 			return;
25184d1e669cSPeter Grehan 		}
25194d1e669cSPeter Grehan 
2520fd4e0d4cSNeel Natu 		i = baridx - 1;		/* 'memregs' index */
2521fd4e0d4cSNeel Natu 
25224d1e669cSPeter Grehan 		if (size == 1) {
2523fd4e0d4cSNeel Natu 			sc->memregs[i][offset] = value;
25244d1e669cSPeter Grehan 		} else if (size == 2) {
2525fd4e0d4cSNeel Natu 			*(uint16_t *)&sc->memregs[i][offset] = value;
25264d1e669cSPeter Grehan 		} else if (size == 4) {
2527fd4e0d4cSNeel Natu 			*(uint32_t *)&sc->memregs[i][offset] = value;
25284d1e669cSPeter Grehan 		} else if (size == 8) {
2529fd4e0d4cSNeel Natu 			*(uint64_t *)&sc->memregs[i][offset] = value;
25304d1e669cSPeter Grehan 		} else {
25314d1e669cSPeter Grehan 			printf("diow: memw unknown size %d\n", size);
25324d1e669cSPeter Grehan 		}
25334d1e669cSPeter Grehan 
25344d1e669cSPeter Grehan 		/*
25354d1e669cSPeter Grehan 		 * magic interrupt ??
25364d1e669cSPeter Grehan 		 */
25374d1e669cSPeter Grehan 	}
25384d1e669cSPeter Grehan 
25399f3dba68SPedro F. Giffuni 	if (baridx > 2 || baridx < 0) {
25404d1e669cSPeter Grehan 		printf("diow: unknown bar idx %d\n", baridx);
25414d1e669cSPeter Grehan 	}
25424d1e669cSPeter Grehan }
25434d1e669cSPeter Grehan 
25444d1e669cSPeter Grehan static uint64_t
25454d1e669cSPeter Grehan pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
25464d1e669cSPeter Grehan 	      uint64_t offset, int size)
2547366f6083SPeter Grehan {
2548366f6083SPeter Grehan 	struct pci_emul_dsoftc *sc = pi->pi_arg;
2549366f6083SPeter Grehan 	uint32_t value;
2550fd4e0d4cSNeel Natu 	int i;
2551366f6083SPeter Grehan 
25524d1e669cSPeter Grehan 	if (baridx == 0) {
25534d1e669cSPeter Grehan 		if (offset + size > DIOSZ) {
25544d1e669cSPeter Grehan 			printf("dior: ior too large, offset %ld size %d\n",
25554d1e669cSPeter Grehan 			       offset, size);
2556366f6083SPeter Grehan 			return (0);
2557366f6083SPeter Grehan 		}
2558366f6083SPeter Grehan 
25596e43f3edSPedro F. Giffuni 		value = 0;
2560366f6083SPeter Grehan 		if (size == 1) {
25614d1e669cSPeter Grehan 			value = sc->ioregs[offset];
2562366f6083SPeter Grehan 		} else if (size == 2) {
25634d1e669cSPeter Grehan 			value = *(uint16_t *) &sc->ioregs[offset];
25644d1e669cSPeter Grehan 		} else if (size == 4) {
25654d1e669cSPeter Grehan 			value = *(uint32_t *) &sc->ioregs[offset];
2566366f6083SPeter Grehan 		} else {
25674d1e669cSPeter Grehan 			printf("dior: ior unknown size %d\n", size);
25684d1e669cSPeter Grehan 		}
25694d1e669cSPeter Grehan 	}
25704d1e669cSPeter Grehan 
2571fd4e0d4cSNeel Natu 	if (baridx == 1 || baridx == 2) {
25724d1e669cSPeter Grehan 		if (offset + size > DMEMSZ) {
25734d1e669cSPeter Grehan 			printf("dior: memr too large, offset %ld size %d\n",
25744d1e669cSPeter Grehan 			       offset, size);
25754d1e669cSPeter Grehan 			return (0);
25764d1e669cSPeter Grehan 		}
25774d1e669cSPeter Grehan 
2578fd4e0d4cSNeel Natu 		i = baridx - 1;		/* 'memregs' index */
2579fd4e0d4cSNeel Natu 
25804d1e669cSPeter Grehan 		if (size == 1) {
2581fd4e0d4cSNeel Natu 			value = sc->memregs[i][offset];
25824d1e669cSPeter Grehan 		} else if (size == 2) {
2583fd4e0d4cSNeel Natu 			value = *(uint16_t *) &sc->memregs[i][offset];
25844d1e669cSPeter Grehan 		} else if (size == 4) {
2585fd4e0d4cSNeel Natu 			value = *(uint32_t *) &sc->memregs[i][offset];
25864d1e669cSPeter Grehan 		} else if (size == 8) {
2587fd4e0d4cSNeel Natu 			value = *(uint64_t *) &sc->memregs[i][offset];
25884d1e669cSPeter Grehan 		} else {
25894d1e669cSPeter Grehan 			printf("dior: ior unknown size %d\n", size);
25904d1e669cSPeter Grehan 		}
25914d1e669cSPeter Grehan 	}
25924d1e669cSPeter Grehan 
25934d1e669cSPeter Grehan 
25949f3dba68SPedro F. Giffuni 	if (baridx > 2 || baridx < 0) {
25954d1e669cSPeter Grehan 		printf("dior: unknown bar idx %d\n", baridx);
25964d1e669cSPeter Grehan 		return (0);
2597366f6083SPeter Grehan 	}
2598366f6083SPeter Grehan 
2599366f6083SPeter Grehan 	return (value);
2600366f6083SPeter Grehan }
2601366f6083SPeter Grehan 
2602483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2603483d953aSJohn Baldwin int
2604483d953aSJohn Baldwin pci_emul_snapshot(struct vm_snapshot_meta *meta)
2605483d953aSJohn Baldwin {
2606483d953aSJohn Baldwin 
2607483d953aSJohn Baldwin 	return (0);
2608483d953aSJohn Baldwin }
2609483d953aSJohn Baldwin #endif
2610483d953aSJohn Baldwin 
2611366f6083SPeter Grehan struct pci_devemu pci_dummy = {
2612366f6083SPeter Grehan 	.pe_emu = "dummy",
2613366f6083SPeter Grehan 	.pe_init = pci_emul_dinit,
26144d1e669cSPeter Grehan 	.pe_barwrite = pci_emul_diow,
2615483d953aSJohn Baldwin 	.pe_barread = pci_emul_dior,
2616483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2617483d953aSJohn Baldwin 	.pe_snapshot = pci_emul_snapshot,
2618483d953aSJohn Baldwin #endif
2619366f6083SPeter Grehan };
2620366f6083SPeter Grehan PCI_EMUL_SET(pci_dummy);
2621366f6083SPeter Grehan 
2622366f6083SPeter Grehan #endif /* PCI_EMUL_TEST */
2623