xref: /freebsd/usr.sbin/bhyve/pci_emul.c (revision 6a284cac)
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 };
11907d82562SMark Johnston 
12007d82562SMark Johnston static TAILQ_HEAD(pci_bar_list, pci_bar_allocation) pci_bars =
12107d82562SMark Johnston     TAILQ_HEAD_INITIALIZER(pci_bars);
12201f9362eSCorvin Köhne 
123366f6083SPeter Grehan #define	PCI_EMUL_IOBASE		0x2000
124366f6083SPeter Grehan #define	PCI_EMUL_IOLIMIT	0x10000
125366f6083SPeter Grehan 
126e47fe318SCorvin Köhne #define PCI_EMUL_ROMSIZE 0x10000000
127e47fe318SCorvin Köhne 
12812a6eb99SNeel Natu #define	PCI_EMUL_ECFG_BASE	0xE0000000		    /* 3.5GB */
12912a6eb99SNeel Natu #define	PCI_EMUL_ECFG_SIZE	(MAXBUSES * 1024 * 1024)    /* 1MB per bus */
13012a6eb99SNeel Natu SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE);
13112a6eb99SNeel Natu 
1325cf21e48SCorvin Köhne /*
1335cf21e48SCorvin Köhne  * OVMF always uses 0xC0000000 as base address for 32 bit PCI MMIO. Don't
1345cf21e48SCorvin Köhne  * change this address without changing it in OVMF.
1355cf21e48SCorvin Köhne  */
1365cf21e48SCorvin Köhne #define PCI_EMUL_MEMBASE32 0xC0000000
13712a6eb99SNeel Natu #define	PCI_EMUL_MEMLIMIT32	PCI_EMUL_ECFG_BASE
1384a4053e1SCorvin Köhne #define PCI_EMUL_MEMSIZE64	(32*GB)
139366f6083SPeter Grehan 
140621b5090SJohn Baldwin static struct pci_devemu *pci_emul_finddev(const char *name);
141b3e9732aSJohn Baldwin static void pci_lintr_route(struct pci_devinst *pi);
1423cbf3585SJohn Baldwin static void pci_lintr_update(struct pci_devinst *pi);
1436a284cacSJohn Baldwin static void pci_cfgrw(int in, int bus, int slot, int func, int coff,
1446a284cacSJohn Baldwin     int bytes, uint32_t *val);
145366f6083SPeter Grehan 
14654335630SNeel Natu static __inline void
14754335630SNeel Natu CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes)
14854335630SNeel Natu {
14954335630SNeel Natu 
15054335630SNeel Natu 	if (bytes == 1)
15154335630SNeel Natu 		pci_set_cfgdata8(pi, coff, val);
15254335630SNeel Natu 	else if (bytes == 2)
15354335630SNeel Natu 		pci_set_cfgdata16(pi, coff, val);
15454335630SNeel Natu 	else
15554335630SNeel Natu 		pci_set_cfgdata32(pi, coff, val);
15654335630SNeel Natu }
15754335630SNeel Natu 
15854335630SNeel Natu static __inline uint32_t
15954335630SNeel Natu CFGREAD(struct pci_devinst *pi, int coff, int bytes)
16054335630SNeel Natu {
16154335630SNeel Natu 
16254335630SNeel Natu 	if (bytes == 1)
16354335630SNeel Natu 		return (pci_get_cfgdata8(pi, coff));
16454335630SNeel Natu 	else if (bytes == 2)
16554335630SNeel Natu 		return (pci_get_cfgdata16(pi, coff));
16654335630SNeel Natu 	else
16754335630SNeel Natu 		return (pci_get_cfgdata32(pi, coff));
16854335630SNeel Natu }
16954335630SNeel Natu 
17045ddbf21SCorvin Köhne static int
17145ddbf21SCorvin Köhne is_pcir_bar(int coff)
17245ddbf21SCorvin Köhne {
17345ddbf21SCorvin Köhne 	return (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1));
17445ddbf21SCorvin Köhne }
17545ddbf21SCorvin Köhne 
17645ddbf21SCorvin Köhne static int
17745ddbf21SCorvin Köhne is_pcir_bios(int coff)
17845ddbf21SCorvin Köhne {
17945ddbf21SCorvin Köhne 	return (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4);
18045ddbf21SCorvin Köhne }
18145ddbf21SCorvin Köhne 
182366f6083SPeter Grehan /*
183366f6083SPeter Grehan  * I/O access
184366f6083SPeter Grehan  */
185366f6083SPeter Grehan 
186366f6083SPeter Grehan /*
187366f6083SPeter Grehan  * Slot options are in the form:
188366f6083SPeter Grehan  *
189d84882caSNeel Natu  *  <bus>:<slot>:<func>,<emul>[,<config>]
19099d65389SNeel Natu  *  <slot>[:<func>],<emul>[,<config>]
191366f6083SPeter Grehan  *
192366f6083SPeter Grehan  *  slot is 0..31
19399d65389SNeel Natu  *  func is 0..7
194366f6083SPeter Grehan  *  emul is a string describing the type of PCI device e.g. virtio-net
195366f6083SPeter Grehan  *  config is an optional string, depending on the device, that can be
196366f6083SPeter Grehan  *  used for configuration.
197366f6083SPeter Grehan  *   Examples are:
198366f6083SPeter Grehan  *     1,virtio-net,tap0
19999d65389SNeel Natu  *     3:0,dummy
200366f6083SPeter Grehan  */
201366f6083SPeter Grehan static void
202366f6083SPeter Grehan pci_parse_slot_usage(char *aopt)
203366f6083SPeter Grehan {
204b05c77ffSNeel Natu 
205332eff95SVincenzo Maffione 	EPRINTLN("Invalid PCI slot info field \"%s\"", aopt);
206366f6083SPeter Grehan }
207366f6083SPeter Grehan 
208621b5090SJohn Baldwin /*
209621b5090SJohn Baldwin  * Helper function to parse a list of comma-separated options where
210621b5090SJohn Baldwin  * each option is formatted as "name[=value]".  If no value is
211621b5090SJohn Baldwin  * provided, the option is treated as a boolean and is given a value
212621b5090SJohn Baldwin  * of true.
213621b5090SJohn Baldwin  */
214621b5090SJohn Baldwin int
215621b5090SJohn Baldwin pci_parse_legacy_config(nvlist_t *nvl, const char *opt)
216621b5090SJohn Baldwin {
217621b5090SJohn Baldwin 	char *config, *name, *tofree, *value;
218621b5090SJohn Baldwin 
219621b5090SJohn Baldwin 	if (opt == NULL)
220621b5090SJohn Baldwin 		return (0);
221621b5090SJohn Baldwin 
222621b5090SJohn Baldwin 	config = tofree = strdup(opt);
223621b5090SJohn Baldwin 	while ((name = strsep(&config, ",")) != NULL) {
224621b5090SJohn Baldwin 		value = strchr(name, '=');
225621b5090SJohn Baldwin 		if (value != NULL) {
226621b5090SJohn Baldwin 			*value = '\0';
227621b5090SJohn Baldwin 			value++;
228621b5090SJohn Baldwin 			set_config_value_node(nvl, name, value);
229621b5090SJohn Baldwin 		} else
230621b5090SJohn Baldwin 			set_config_bool_node(nvl, name, true);
231621b5090SJohn Baldwin 	}
232621b5090SJohn Baldwin 	free(tofree);
233621b5090SJohn Baldwin 	return (0);
234621b5090SJohn Baldwin }
235621b5090SJohn Baldwin 
236621b5090SJohn Baldwin /*
237621b5090SJohn Baldwin  * PCI device configuration is stored in MIBs that encode the device's
238621b5090SJohn Baldwin  * location:
239621b5090SJohn Baldwin  *
240621b5090SJohn Baldwin  * pci.<bus>.<slot>.<func>
241621b5090SJohn Baldwin  *
242621b5090SJohn Baldwin  * Where "bus", "slot", and "func" are all decimal values without
243621b5090SJohn Baldwin  * leading zeroes.  Each valid device must have a "device" node which
244621b5090SJohn Baldwin  * identifies the driver model of the device.
245621b5090SJohn Baldwin  *
246621b5090SJohn Baldwin  * Device backends can provide a parser for the "config" string.  If
247621b5090SJohn Baldwin  * a custom parser is not provided, pci_parse_legacy_config() is used
248621b5090SJohn Baldwin  * to parse the string.
249621b5090SJohn Baldwin  */
250b05c77ffSNeel Natu int
251d2bc4816SJohn Baldwin pci_parse_slot(char *opt)
252366f6083SPeter Grehan {
253621b5090SJohn Baldwin 	char node_name[sizeof("pci.XXX.XX.X")];
254621b5090SJohn Baldwin 	struct pci_devemu *pde;
255d84882caSNeel Natu 	char *emul, *config, *str, *cp;
256d84882caSNeel Natu 	int error, bnum, snum, fnum;
257621b5090SJohn Baldwin 	nvlist_t *nvl;
258366f6083SPeter Grehan 
259b05c77ffSNeel Natu 	error = -1;
260d84882caSNeel Natu 	str = strdup(opt);
26199d65389SNeel Natu 
262d84882caSNeel Natu 	emul = config = NULL;
263d84882caSNeel Natu 	if ((cp = strchr(str, ',')) != NULL) {
264d84882caSNeel Natu 		*cp = '\0';
265d84882caSNeel Natu 		emul = cp + 1;
266d84882caSNeel Natu 		if ((cp = strchr(emul, ',')) != NULL) {
267d84882caSNeel Natu 			*cp = '\0';
268d84882caSNeel Natu 			config = cp + 1;
26999d65389SNeel Natu 		}
270d84882caSNeel Natu 	} else {
271b05c77ffSNeel Natu 		pci_parse_slot_usage(opt);
272b05c77ffSNeel Natu 		goto done;
273366f6083SPeter Grehan 	}
274366f6083SPeter Grehan 
275d84882caSNeel Natu 	/* <bus>:<slot>:<func> */
276d84882caSNeel Natu 	if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) {
277d84882caSNeel Natu 		bnum = 0;
278d84882caSNeel Natu 		/* <slot>:<func> */
279d84882caSNeel Natu 		if (sscanf(str, "%d:%d", &snum, &fnum) != 2) {
280d84882caSNeel Natu 			fnum = 0;
281d84882caSNeel Natu 			/* <slot> */
282d84882caSNeel Natu 			if (sscanf(str, "%d", &snum) != 1) {
283d84882caSNeel Natu 				snum = -1;
284d84882caSNeel Natu 			}
285d84882caSNeel Natu 		}
286d84882caSNeel Natu 	}
287b05c77ffSNeel Natu 
288d84882caSNeel Natu 	if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS ||
289d84882caSNeel Natu 	    fnum < 0 || fnum >= MAXFUNCS) {
290b05c77ffSNeel Natu 		pci_parse_slot_usage(opt);
291b05c77ffSNeel Natu 		goto done;
292b05c77ffSNeel Natu 	}
293b05c77ffSNeel Natu 
294621b5090SJohn Baldwin 	pde = pci_emul_finddev(emul);
295621b5090SJohn Baldwin 	if (pde == NULL) {
296621b5090SJohn Baldwin 		EPRINTLN("pci slot %d:%d:%d: unknown device \"%s\"", bnum, snum,
297621b5090SJohn Baldwin 		    fnum, emul);
298b05c77ffSNeel Natu 		goto done;
299b05c77ffSNeel Natu 	}
300b05c77ffSNeel Natu 
301621b5090SJohn Baldwin 	snprintf(node_name, sizeof(node_name), "pci.%d.%d.%d", bnum, snum,
302621b5090SJohn Baldwin 	    fnum);
303621b5090SJohn Baldwin 	nvl = find_config_node(node_name);
304621b5090SJohn Baldwin 	if (nvl != NULL) {
305621b5090SJohn Baldwin 		EPRINTLN("pci slot %d:%d:%d already occupied!", bnum, snum,
306621b5090SJohn Baldwin 		    fnum);
307b05c77ffSNeel Natu 		goto done;
308b05c77ffSNeel Natu 	}
309621b5090SJohn Baldwin 	nvl = create_config_node(node_name);
310621b5090SJohn Baldwin 	if (pde->pe_alias != NULL)
311621b5090SJohn Baldwin 		set_config_value_node(nvl, "device", pde->pe_alias);
312621b5090SJohn Baldwin 	else
313621b5090SJohn Baldwin 		set_config_value_node(nvl, "device", pde->pe_emu);
314b05c77ffSNeel Natu 
315621b5090SJohn Baldwin 	if (pde->pe_legacy_config != NULL)
316621b5090SJohn Baldwin 		error = pde->pe_legacy_config(nvl, config);
317621b5090SJohn Baldwin 	else
318621b5090SJohn Baldwin 		error = pci_parse_legacy_config(nvl, config);
319b05c77ffSNeel Natu done:
320d84882caSNeel Natu 	free(str);
321b05c77ffSNeel Natu 	return (error);
322366f6083SPeter Grehan }
323366f6083SPeter Grehan 
324657d2158SMarcelo Araujo void
32575ce327aSMark Johnston pci_print_supported_devices(void)
326657d2158SMarcelo Araujo {
327657d2158SMarcelo Araujo 	struct pci_devemu **pdpp, *pdp;
328657d2158SMarcelo Araujo 
329657d2158SMarcelo Araujo 	SET_FOREACH(pdpp, pci_devemu_set) {
330657d2158SMarcelo Araujo 		pdp = *pdpp;
331657d2158SMarcelo Araujo 		printf("%s\n", pdp->pe_emu);
332657d2158SMarcelo Araujo 	}
333657d2158SMarcelo Araujo }
334657d2158SMarcelo Araujo 
335366f6083SPeter Grehan static int
336c9b4e987SNeel Natu pci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset)
337c9b4e987SNeel Natu {
338c9b4e987SNeel Natu 
339c9b4e987SNeel Natu 	if (offset < pi->pi_msix.pba_offset)
340c9b4e987SNeel Natu 		return (0);
341c9b4e987SNeel Natu 
342c9b4e987SNeel Natu 	if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) {
343c9b4e987SNeel Natu 		return (0);
344c9b4e987SNeel Natu 	}
345c9b4e987SNeel Natu 
346c9b4e987SNeel Natu 	return (1);
347c9b4e987SNeel Natu }
348c9b4e987SNeel Natu 
349c9b4e987SNeel Natu int
350c9b4e987SNeel Natu pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size,
351c9b4e987SNeel Natu 		     uint64_t value)
352c9b4e987SNeel Natu {
353c9b4e987SNeel Natu 	int msix_entry_offset;
354c9b4e987SNeel Natu 	int tab_index;
355c9b4e987SNeel Natu 	char *dest;
356c9b4e987SNeel Natu 
357c9b4e987SNeel Natu 	/* support only 4 or 8 byte writes */
358c9b4e987SNeel Natu 	if (size != 4 && size != 8)
359c9b4e987SNeel Natu 		return (-1);
360c9b4e987SNeel Natu 
361c9b4e987SNeel Natu 	/*
362c9b4e987SNeel Natu 	 * Return if table index is beyond what device supports
363c9b4e987SNeel Natu 	 */
364c9b4e987SNeel Natu 	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
365c9b4e987SNeel Natu 	if (tab_index >= pi->pi_msix.table_count)
366c9b4e987SNeel Natu 		return (-1);
367c9b4e987SNeel Natu 
368c9b4e987SNeel Natu 	msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
369c9b4e987SNeel Natu 
370c9b4e987SNeel Natu 	/* support only aligned writes */
371c9b4e987SNeel Natu 	if ((msix_entry_offset % size) != 0)
372c9b4e987SNeel Natu 		return (-1);
373c9b4e987SNeel Natu 
374c9b4e987SNeel Natu 	dest = (char *)(pi->pi_msix.table + tab_index);
375c9b4e987SNeel Natu 	dest += msix_entry_offset;
376c9b4e987SNeel Natu 
377c9b4e987SNeel Natu 	if (size == 4)
378c9b4e987SNeel Natu 		*((uint32_t *)dest) = value;
379c9b4e987SNeel Natu 	else
380c9b4e987SNeel Natu 		*((uint64_t *)dest) = value;
381c9b4e987SNeel Natu 
382c9b4e987SNeel Natu 	return (0);
383c9b4e987SNeel Natu }
384c9b4e987SNeel Natu 
385c9b4e987SNeel Natu uint64_t
386c9b4e987SNeel Natu pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size)
387c9b4e987SNeel Natu {
388c9b4e987SNeel Natu 	char *dest;
389c9b4e987SNeel Natu 	int msix_entry_offset;
390c9b4e987SNeel Natu 	int tab_index;
391c9b4e987SNeel Natu 	uint64_t retval = ~0;
392c9b4e987SNeel Natu 
3936a52209fSNeel Natu 	/*
3946a52209fSNeel Natu 	 * The PCI standard only allows 4 and 8 byte accesses to the MSI-X
395463a577bSEitan Adler 	 * table but we also allow 1 byte access to accommodate reads from
3966a52209fSNeel Natu 	 * ddb.
3976a52209fSNeel Natu 	 */
3986a52209fSNeel Natu 	if (size != 1 && size != 4 && size != 8)
399c9b4e987SNeel Natu 		return (retval);
400c9b4e987SNeel Natu 
401c9b4e987SNeel Natu 	msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
402c9b4e987SNeel Natu 
403c9b4e987SNeel Natu 	/* support only aligned reads */
404c9b4e987SNeel Natu 	if ((msix_entry_offset % size) != 0) {
405c9b4e987SNeel Natu 		return (retval);
406c9b4e987SNeel Natu 	}
407c9b4e987SNeel Natu 
408c9b4e987SNeel Natu 	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
409c9b4e987SNeel Natu 
410c9b4e987SNeel Natu 	if (tab_index < pi->pi_msix.table_count) {
411c9b4e987SNeel Natu 		/* valid MSI-X Table access */
412c9b4e987SNeel Natu 		dest = (char *)(pi->pi_msix.table + tab_index);
413c9b4e987SNeel Natu 		dest += msix_entry_offset;
414c9b4e987SNeel Natu 
4156a52209fSNeel Natu 		if (size == 1)
4166a52209fSNeel Natu 			retval = *((uint8_t *)dest);
4176a52209fSNeel Natu 		else if (size == 4)
418c9b4e987SNeel Natu 			retval = *((uint32_t *)dest);
419c9b4e987SNeel Natu 		else
420c9b4e987SNeel Natu 			retval = *((uint64_t *)dest);
421c9b4e987SNeel Natu 	} else if (pci_valid_pba_offset(pi, offset)) {
422c9b4e987SNeel Natu 		/* return 0 for PBA access */
423c9b4e987SNeel Natu 		retval = 0;
424c9b4e987SNeel Natu 	}
425c9b4e987SNeel Natu 
426c9b4e987SNeel Natu 	return (retval);
427c9b4e987SNeel Natu }
428c9b4e987SNeel Natu 
429aa12663fSNeel Natu int
430aa12663fSNeel Natu pci_msix_table_bar(struct pci_devinst *pi)
431aa12663fSNeel Natu {
432aa12663fSNeel Natu 
433aa12663fSNeel Natu 	if (pi->pi_msix.table != NULL)
434aa12663fSNeel Natu 		return (pi->pi_msix.table_bar);
435aa12663fSNeel Natu 	else
436aa12663fSNeel Natu 		return (-1);
437aa12663fSNeel Natu }
438aa12663fSNeel Natu 
439aa12663fSNeel Natu int
440aa12663fSNeel Natu pci_msix_pba_bar(struct pci_devinst *pi)
441aa12663fSNeel Natu {
442aa12663fSNeel Natu 
443aa12663fSNeel Natu 	if (pi->pi_msix.table != NULL)
444aa12663fSNeel Natu 		return (pi->pi_msix.pba_bar);
445aa12663fSNeel Natu 	else
446aa12663fSNeel Natu 		return (-1);
447aa12663fSNeel Natu }
448aa12663fSNeel Natu 
449c9b4e987SNeel Natu static int
4506a284cacSJohn Baldwin pci_emul_io_handler(struct vmctx *ctx __unused, int in, int port,
45178c2cd83SJohn Baldwin     int bytes, uint32_t *eax, void *arg)
452366f6083SPeter Grehan {
453366f6083SPeter Grehan 	struct pci_devinst *pdi = arg;
454366f6083SPeter Grehan 	struct pci_devemu *pe = pdi->pi_d;
4554d1e669cSPeter Grehan 	uint64_t offset;
4564d1e669cSPeter Grehan 	int i;
457366f6083SPeter Grehan 
458ed721684SMark Johnston 	assert(port >= 0);
459ed721684SMark Johnston 
460366f6083SPeter Grehan 	for (i = 0; i <= PCI_BARMAX; i++) {
461366f6083SPeter Grehan 		if (pdi->pi_bar[i].type == PCIBAR_IO &&
462ed721684SMark Johnston 		    (uint64_t)port >= pdi->pi_bar[i].addr &&
463ed721684SMark Johnston 		    (uint64_t)port + bytes <=
464ed721684SMark Johnston 		    pdi->pi_bar[i].addr + pdi->pi_bar[i].size) {
465366f6083SPeter Grehan 			offset = port - pdi->pi_bar[i].addr;
466366f6083SPeter Grehan 			if (in)
4676a284cacSJohn Baldwin 				*eax = (*pe->pe_barread)(pdi, i,
4684d1e669cSPeter Grehan 							 offset, bytes);
469366f6083SPeter Grehan 			else
4706a284cacSJohn Baldwin 				(*pe->pe_barwrite)(pdi, i, offset,
4714d1e669cSPeter Grehan 						   bytes, *eax);
472366f6083SPeter Grehan 			return (0);
473366f6083SPeter Grehan 		}
474366f6083SPeter Grehan 	}
475366f6083SPeter Grehan 	return (-1);
476366f6083SPeter Grehan }
477366f6083SPeter Grehan 
478366f6083SPeter Grehan static int
4796a284cacSJohn Baldwin pci_emul_mem_handler(struct vmctx *ctx __unused, int vcpu __unused, int dir,
48078c2cd83SJohn Baldwin     uint64_t addr, int size, uint64_t *val, void *arg1, long arg2)
4814d1e669cSPeter Grehan {
4824d1e669cSPeter Grehan 	struct pci_devinst *pdi = arg1;
4834d1e669cSPeter Grehan 	struct pci_devemu *pe = pdi->pi_d;
4844d1e669cSPeter Grehan 	uint64_t offset;
4854d1e669cSPeter Grehan 	int bidx = (int) arg2;
4864d1e669cSPeter Grehan 
4874d1e669cSPeter Grehan 	assert(bidx <= PCI_BARMAX);
4884d1e669cSPeter Grehan 	assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 ||
4894d1e669cSPeter Grehan 	       pdi->pi_bar[bidx].type == PCIBAR_MEM64);
4904d1e669cSPeter Grehan 	assert(addr >= pdi->pi_bar[bidx].addr &&
4914d1e669cSPeter Grehan 	       addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size);
4924d1e669cSPeter Grehan 
4934d1e669cSPeter Grehan 	offset = addr - pdi->pi_bar[bidx].addr;
4944d1e669cSPeter Grehan 
495b6ae8b05STycho Nightingale 	if (dir == MEM_F_WRITE) {
49667b6ffaaSTycho Nightingale 		if (size == 8) {
4976a284cacSJohn Baldwin 			(*pe->pe_barwrite)(pdi, bidx, offset,
498b6ae8b05STycho Nightingale 					   4, *val & 0xffffffff);
4996a284cacSJohn Baldwin 			(*pe->pe_barwrite)(pdi, bidx, offset + 4,
500b6ae8b05STycho Nightingale 					   4, *val >> 32);
501b6ae8b05STycho Nightingale 		} else {
5026a284cacSJohn Baldwin 			(*pe->pe_barwrite)(pdi, bidx, offset,
503b6ae8b05STycho Nightingale 					   size, *val);
504b6ae8b05STycho Nightingale 		}
505b6ae8b05STycho Nightingale 	} else {
50667b6ffaaSTycho Nightingale 		if (size == 8) {
5076a284cacSJohn Baldwin 			*val = (*pe->pe_barread)(pdi, bidx,
508b6ae8b05STycho Nightingale 						 offset, 4);
5096a284cacSJohn Baldwin 			*val |= (*pe->pe_barread)(pdi, bidx,
510b6ae8b05STycho Nightingale 						  offset + 4, 4) << 32;
511b6ae8b05STycho Nightingale 		} else {
5126a284cacSJohn Baldwin 			*val = (*pe->pe_barread)(pdi, bidx,
513b6ae8b05STycho Nightingale 						 offset, size);
514b6ae8b05STycho Nightingale 		}
515b6ae8b05STycho Nightingale 	}
5164d1e669cSPeter Grehan 
5174d1e669cSPeter Grehan 	return (0);
5184d1e669cSPeter Grehan }
5194d1e669cSPeter Grehan 
5204d1e669cSPeter Grehan 
5214d1e669cSPeter Grehan static int
522366f6083SPeter Grehan pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size,
523366f6083SPeter Grehan 			uint64_t *addr)
524366f6083SPeter Grehan {
525366f6083SPeter Grehan 	uint64_t base;
526366f6083SPeter Grehan 
527366f6083SPeter Grehan 	assert((size & (size - 1)) == 0);	/* must be a power of 2 */
528366f6083SPeter Grehan 
529366f6083SPeter Grehan 	base = roundup2(*baseptr, size);
530366f6083SPeter Grehan 
531366f6083SPeter Grehan 	if (base + size <= limit) {
532366f6083SPeter Grehan 		*addr = base;
533366f6083SPeter Grehan 		*baseptr = base + size;
534366f6083SPeter Grehan 		return (0);
535366f6083SPeter Grehan 	} else
536366f6083SPeter Grehan 		return (-1);
537366f6083SPeter Grehan }
538366f6083SPeter Grehan 
539028d9311SNeel Natu /*
540028d9311SNeel Natu  * Register (or unregister) the MMIO or I/O region associated with the BAR
541028d9311SNeel Natu  * register 'idx' of an emulated pci device.
542028d9311SNeel Natu  */
543028d9311SNeel Natu static void
544028d9311SNeel Natu modify_bar_registration(struct pci_devinst *pi, int idx, int registration)
545028d9311SNeel Natu {
546f8a6ec2dSD Scott Phillips 	struct pci_devemu *pe;
547028d9311SNeel Natu 	int error;
548028d9311SNeel Natu 	struct inout_port iop;
549028d9311SNeel Natu 	struct mem_range mr;
550028d9311SNeel Natu 
551f8a6ec2dSD Scott Phillips 	pe = pi->pi_d;
552028d9311SNeel Natu 	switch (pi->pi_bar[idx].type) {
553028d9311SNeel Natu 	case PCIBAR_IO:
554028d9311SNeel Natu 		bzero(&iop, sizeof(struct inout_port));
555028d9311SNeel Natu 		iop.name = pi->pi_name;
556028d9311SNeel Natu 		iop.port = pi->pi_bar[idx].addr;
557028d9311SNeel Natu 		iop.size = pi->pi_bar[idx].size;
558028d9311SNeel Natu 		if (registration) {
559028d9311SNeel Natu 			iop.flags = IOPORT_F_INOUT;
560028d9311SNeel Natu 			iop.handler = pci_emul_io_handler;
561028d9311SNeel Natu 			iop.arg = pi;
562028d9311SNeel Natu 			error = register_inout(&iop);
563028d9311SNeel Natu 		} else
564028d9311SNeel Natu 			error = unregister_inout(&iop);
565f8a6ec2dSD Scott Phillips 		if (pe->pe_baraddr != NULL)
5666a284cacSJohn Baldwin 			(*pe->pe_baraddr)(pi, idx, registration,
567f8a6ec2dSD Scott Phillips 					  pi->pi_bar[idx].addr);
568028d9311SNeel Natu 		break;
569028d9311SNeel Natu 	case PCIBAR_MEM32:
570028d9311SNeel Natu 	case PCIBAR_MEM64:
571028d9311SNeel Natu 		bzero(&mr, sizeof(struct mem_range));
572028d9311SNeel Natu 		mr.name = pi->pi_name;
573028d9311SNeel Natu 		mr.base = pi->pi_bar[idx].addr;
574028d9311SNeel Natu 		mr.size = pi->pi_bar[idx].size;
575028d9311SNeel Natu 		if (registration) {
576028d9311SNeel Natu 			mr.flags = MEM_F_RW;
577028d9311SNeel Natu 			mr.handler = pci_emul_mem_handler;
578028d9311SNeel Natu 			mr.arg1 = pi;
579028d9311SNeel Natu 			mr.arg2 = idx;
580028d9311SNeel Natu 			error = register_mem(&mr);
581028d9311SNeel Natu 		} else
582028d9311SNeel Natu 			error = unregister_mem(&mr);
583f8a6ec2dSD Scott Phillips 		if (pe->pe_baraddr != NULL)
5846a284cacSJohn Baldwin 			(*pe->pe_baraddr)(pi, idx, registration,
585f8a6ec2dSD Scott Phillips 					  pi->pi_bar[idx].addr);
586028d9311SNeel Natu 		break;
587e47fe318SCorvin Köhne 	case PCIBAR_ROM:
588e47fe318SCorvin Köhne 		error = 0;
589e47fe318SCorvin Köhne 		if (pe->pe_baraddr != NULL)
5906a284cacSJohn Baldwin 			(*pe->pe_baraddr)(pi, idx, registration,
591e47fe318SCorvin Köhne 			    pi->pi_bar[idx].addr);
592e47fe318SCorvin Köhne 		break;
593028d9311SNeel Natu 	default:
594028d9311SNeel Natu 		error = EINVAL;
595028d9311SNeel Natu 		break;
596028d9311SNeel Natu 	}
597028d9311SNeel Natu 	assert(error == 0);
598028d9311SNeel Natu }
599028d9311SNeel Natu 
600028d9311SNeel Natu static void
601028d9311SNeel Natu unregister_bar(struct pci_devinst *pi, int idx)
602028d9311SNeel Natu {
603028d9311SNeel Natu 
604028d9311SNeel Natu 	modify_bar_registration(pi, idx, 0);
605028d9311SNeel Natu }
606028d9311SNeel Natu 
607028d9311SNeel Natu static void
608028d9311SNeel Natu register_bar(struct pci_devinst *pi, int idx)
609028d9311SNeel Natu {
610028d9311SNeel Natu 
611028d9311SNeel Natu 	modify_bar_registration(pi, idx, 1);
612028d9311SNeel Natu }
613028d9311SNeel Natu 
614e47fe318SCorvin Köhne /* Is the ROM enabled for the emulated pci device? */
615e47fe318SCorvin Köhne static int
616e47fe318SCorvin Köhne romen(struct pci_devinst *pi)
617e47fe318SCorvin Köhne {
618e47fe318SCorvin Köhne 	return (pi->pi_bar[PCI_ROM_IDX].lobits & PCIM_BIOS_ENABLE) ==
619e47fe318SCorvin Köhne 	    PCIM_BIOS_ENABLE;
620e47fe318SCorvin Köhne }
621e47fe318SCorvin Köhne 
622028d9311SNeel Natu /* Are we decoding i/o port accesses for the emulated pci device? */
623028d9311SNeel Natu static int
624028d9311SNeel Natu porten(struct pci_devinst *pi)
625028d9311SNeel Natu {
626028d9311SNeel Natu 	uint16_t cmd;
627028d9311SNeel Natu 
628028d9311SNeel Natu 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
629028d9311SNeel Natu 
630028d9311SNeel Natu 	return (cmd & PCIM_CMD_PORTEN);
631028d9311SNeel Natu }
632028d9311SNeel Natu 
633028d9311SNeel Natu /* Are we decoding memory accesses for the emulated pci device? */
634028d9311SNeel Natu static int
635028d9311SNeel Natu memen(struct pci_devinst *pi)
636028d9311SNeel Natu {
637028d9311SNeel Natu 	uint16_t cmd;
638028d9311SNeel Natu 
639028d9311SNeel Natu 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
640028d9311SNeel Natu 
641028d9311SNeel Natu 	return (cmd & PCIM_CMD_MEMEN);
642028d9311SNeel Natu }
643028d9311SNeel Natu 
644028d9311SNeel Natu /*
645028d9311SNeel Natu  * Update the MMIO or I/O address that is decoded by the BAR register.
646028d9311SNeel Natu  *
647028d9311SNeel Natu  * If the pci device has enabled the address space decoding then intercept
648028d9311SNeel Natu  * the address range decoded by the BAR register.
649028d9311SNeel Natu  */
650028d9311SNeel Natu static void
651028d9311SNeel Natu update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type)
652028d9311SNeel Natu {
653028d9311SNeel Natu 	int decode;
654028d9311SNeel Natu 
655028d9311SNeel Natu 	if (pi->pi_bar[idx].type == PCIBAR_IO)
656028d9311SNeel Natu 		decode = porten(pi);
657028d9311SNeel Natu 	else
658028d9311SNeel Natu 		decode = memen(pi);
659028d9311SNeel Natu 
660028d9311SNeel Natu 	if (decode)
661028d9311SNeel Natu 		unregister_bar(pi, idx);
662028d9311SNeel Natu 
663028d9311SNeel Natu 	switch (type) {
664028d9311SNeel Natu 	case PCIBAR_IO:
665028d9311SNeel Natu 	case PCIBAR_MEM32:
666028d9311SNeel Natu 		pi->pi_bar[idx].addr = addr;
667028d9311SNeel Natu 		break;
668028d9311SNeel Natu 	case PCIBAR_MEM64:
669028d9311SNeel Natu 		pi->pi_bar[idx].addr &= ~0xffffffffUL;
670028d9311SNeel Natu 		pi->pi_bar[idx].addr |= addr;
671028d9311SNeel Natu 		break;
672028d9311SNeel Natu 	case PCIBAR_MEMHI64:
673028d9311SNeel Natu 		pi->pi_bar[idx].addr &= 0xffffffff;
674028d9311SNeel Natu 		pi->pi_bar[idx].addr |= addr;
675028d9311SNeel Natu 		break;
676028d9311SNeel Natu 	default:
677028d9311SNeel Natu 		assert(0);
678028d9311SNeel Natu 	}
679028d9311SNeel Natu 
680028d9311SNeel Natu 	if (decode)
681028d9311SNeel Natu 		register_bar(pi, idx);
682028d9311SNeel Natu }
683028d9311SNeel Natu 
6844d1e669cSPeter Grehan int
685038f5c7bSKonstantin Belousov pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type,
686038f5c7bSKonstantin Belousov     uint64_t size)
687366f6083SPeter Grehan {
688e47fe318SCorvin Köhne 	assert((type == PCIBAR_ROM) || (idx >= 0 && idx <= PCI_BARMAX));
689e47fe318SCorvin Köhne 	assert((type != PCIBAR_ROM) || (idx == PCI_ROM_IDX));
690366f6083SPeter Grehan 
691366f6083SPeter Grehan 	if ((size & (size - 1)) != 0)
692366f6083SPeter Grehan 		size = 1UL << flsl(size);	/* round up to a power of 2 */
693366f6083SPeter Grehan 
694028d9311SNeel Natu 	/* Enforce minimum BAR sizes required by the PCI standard */
695028d9311SNeel Natu 	if (type == PCIBAR_IO) {
696028d9311SNeel Natu 		if (size < 4)
697028d9311SNeel Natu 			size = 4;
698e47fe318SCorvin Köhne 	} else if (type == PCIBAR_ROM) {
699e47fe318SCorvin Köhne 		if (size < ~PCIM_BIOS_ADDR_MASK + 1)
700e47fe318SCorvin Köhne 			size = ~PCIM_BIOS_ADDR_MASK + 1;
701028d9311SNeel Natu 	} else {
702028d9311SNeel Natu 		if (size < 16)
703028d9311SNeel Natu 			size = 16;
704028d9311SNeel Natu 	}
705028d9311SNeel Natu 
70601f9362eSCorvin Köhne 	/*
70701f9362eSCorvin Köhne 	 * To reduce fragmentation of the MMIO space, we allocate the BARs by
70801f9362eSCorvin Köhne 	 * size. Therefore, don't allocate the BAR yet. We create a list of all
70901f9362eSCorvin Köhne 	 * BAR allocation which is sorted by BAR size. When all PCI devices are
71001f9362eSCorvin Köhne 	 * initialized, we will assign an address to the BARs.
71101f9362eSCorvin Köhne 	 */
71201f9362eSCorvin Köhne 
71301f9362eSCorvin Köhne 	/* create a new list entry */
71401f9362eSCorvin Köhne 	struct pci_bar_allocation *const new_bar = malloc(sizeof(*new_bar));
71501f9362eSCorvin Köhne 	memset(new_bar, 0, sizeof(*new_bar));
71601f9362eSCorvin Köhne 	new_bar->pdi = pdi;
71701f9362eSCorvin Köhne 	new_bar->idx = idx;
71801f9362eSCorvin Köhne 	new_bar->type = type;
71901f9362eSCorvin Köhne 	new_bar->size = size;
72001f9362eSCorvin Köhne 
72101f9362eSCorvin Köhne 	/*
72201f9362eSCorvin Köhne 	 * Search for a BAR which size is lower than the size of our newly
72301f9362eSCorvin Köhne 	 * allocated BAR.
72401f9362eSCorvin Köhne 	 */
72501f9362eSCorvin Köhne 	struct pci_bar_allocation *bar = NULL;
72601f9362eSCorvin Köhne 	TAILQ_FOREACH(bar, &pci_bars, chain) {
72701f9362eSCorvin Köhne 		if (bar->size < size) {
72801f9362eSCorvin Köhne 			break;
72901f9362eSCorvin Köhne 		}
73001f9362eSCorvin Köhne 	}
73101f9362eSCorvin Köhne 
73201f9362eSCorvin Köhne 	if (bar == NULL) {
73301f9362eSCorvin Köhne 		/*
73401f9362eSCorvin Köhne 		 * Either the list is empty or new BAR is the smallest BAR of
73501f9362eSCorvin Köhne 		 * the list. Append it to the end of our list.
73601f9362eSCorvin Köhne 		 */
73701f9362eSCorvin Köhne 		TAILQ_INSERT_TAIL(&pci_bars, new_bar, chain);
73801f9362eSCorvin Köhne 	} else {
73901f9362eSCorvin Köhne 		/*
74001f9362eSCorvin Köhne 		 * The found BAR is smaller than our new BAR. For that reason,
74101f9362eSCorvin Köhne 		 * insert our new BAR before the found BAR.
74201f9362eSCorvin Köhne 		 */
74301f9362eSCorvin Köhne 		TAILQ_INSERT_BEFORE(bar, new_bar, chain);
74401f9362eSCorvin Köhne 	}
74501f9362eSCorvin Köhne 
74601f9362eSCorvin Köhne 	/*
74701f9362eSCorvin Köhne 	 * pci_passthru devices synchronize their physical and virtual command
74801f9362eSCorvin Köhne 	 * register on init. For that reason, the virtual cmd reg should be
74901f9362eSCorvin Köhne 	 * updated as early as possible.
75001f9362eSCorvin Köhne 	 */
75101f9362eSCorvin Köhne 	uint16_t enbit = 0;
75201f9362eSCorvin Köhne 	switch (type) {
75301f9362eSCorvin Köhne 	case PCIBAR_IO:
75401f9362eSCorvin Köhne 		enbit = PCIM_CMD_PORTEN;
75501f9362eSCorvin Köhne 		break;
75601f9362eSCorvin Köhne 	case PCIBAR_MEM64:
75701f9362eSCorvin Köhne 	case PCIBAR_MEM32:
75801f9362eSCorvin Köhne 		enbit = PCIM_CMD_MEMEN;
75901f9362eSCorvin Köhne 		break;
76001f9362eSCorvin Köhne 	default:
76101f9362eSCorvin Köhne 		enbit = 0;
76201f9362eSCorvin Köhne 		break;
76301f9362eSCorvin Köhne 	}
76401f9362eSCorvin Köhne 
76501f9362eSCorvin Köhne 	const uint16_t cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND);
76601f9362eSCorvin Köhne 	pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit);
76701f9362eSCorvin Köhne 
76801f9362eSCorvin Köhne 	return (0);
76901f9362eSCorvin Köhne }
77001f9362eSCorvin Köhne 
77101f9362eSCorvin Köhne static int
77201f9362eSCorvin Köhne pci_emul_assign_bar(struct pci_devinst *const pdi, const int idx,
77301f9362eSCorvin Köhne     const enum pcibar_type type, const uint64_t size)
77401f9362eSCorvin Köhne {
77501f9362eSCorvin Köhne 	int error;
77601f9362eSCorvin Köhne 	uint64_t *baseptr, limit, addr, mask, lobits, bar;
77701f9362eSCorvin Köhne 
778366f6083SPeter Grehan 	switch (type) {
779366f6083SPeter Grehan 	case PCIBAR_NONE:
780366f6083SPeter Grehan 		baseptr = NULL;
78101f9362eSCorvin Köhne 		addr = mask = lobits = 0;
782366f6083SPeter Grehan 		break;
783366f6083SPeter Grehan 	case PCIBAR_IO:
784366f6083SPeter Grehan 		baseptr = &pci_emul_iobase;
785366f6083SPeter Grehan 		limit = PCI_EMUL_IOLIMIT;
786366f6083SPeter Grehan 		mask = PCIM_BAR_IO_BASE;
787366f6083SPeter Grehan 		lobits = PCIM_BAR_IO_SPACE;
788366f6083SPeter Grehan 		break;
789366f6083SPeter Grehan 	case PCIBAR_MEM64:
790366f6083SPeter Grehan 		/*
791366f6083SPeter Grehan 		 * XXX
792366f6083SPeter Grehan 		 * Some drivers do not work well if the 64-bit BAR is allocated
793366f6083SPeter Grehan 		 * above 4GB. Allow for this by allocating small requests under
794366f6083SPeter Grehan 		 * 4GB unless then allocation size is larger than some arbitrary
795670b364bSKonstantin Belousov 		 * number (128MB currently).
796366f6083SPeter Grehan 		 */
797670b364bSKonstantin Belousov 		if (size > 128 * 1024 * 1024) {
798366f6083SPeter Grehan 			baseptr = &pci_emul_membase64;
7999922872bSKonstantin Belousov 			limit = pci_emul_memlim64;
800366f6083SPeter Grehan 			mask = PCIM_BAR_MEM_BASE;
801366f6083SPeter Grehan 			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
802366f6083SPeter Grehan 				 PCIM_BAR_MEM_PREFETCH;
80325d4944eSNeel Natu 		} else {
80425d4944eSNeel Natu 			baseptr = &pci_emul_membase32;
80525d4944eSNeel Natu 			limit = PCI_EMUL_MEMLIMIT32;
80625d4944eSNeel Natu 			mask = PCIM_BAR_MEM_BASE;
80725d4944eSNeel Natu 			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
808366f6083SPeter Grehan 		}
80925d4944eSNeel Natu 		break;
810366f6083SPeter Grehan 	case PCIBAR_MEM32:
811366f6083SPeter Grehan 		baseptr = &pci_emul_membase32;
812366f6083SPeter Grehan 		limit = PCI_EMUL_MEMLIMIT32;
813366f6083SPeter Grehan 		mask = PCIM_BAR_MEM_BASE;
814366f6083SPeter Grehan 		lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
815366f6083SPeter Grehan 		break;
816e47fe318SCorvin Köhne 	case PCIBAR_ROM:
817e47fe318SCorvin Köhne 		/* do not claim memory for ROM. OVMF will do it for us. */
818e47fe318SCorvin Köhne 		baseptr = NULL;
819e47fe318SCorvin Köhne 		limit = 0;
820e47fe318SCorvin Köhne 		mask = PCIM_BIOS_ADDR_MASK;
821e47fe318SCorvin Köhne 		lobits = 0;
822e47fe318SCorvin Köhne 		break;
823366f6083SPeter Grehan 	default:
824366f6083SPeter Grehan 		printf("pci_emul_alloc_base: invalid bar type %d\n", type);
825366f6083SPeter Grehan 		assert(0);
826366f6083SPeter Grehan 	}
827366f6083SPeter Grehan 
828366f6083SPeter Grehan 	if (baseptr != NULL) {
829366f6083SPeter Grehan 		error = pci_emul_alloc_resource(baseptr, limit, size, &addr);
830366f6083SPeter Grehan 		if (error != 0)
831366f6083SPeter Grehan 			return (error);
8328ac8addaSCorvin Köhne 	} else {
8338ac8addaSCorvin Köhne 		addr = 0;
834366f6083SPeter Grehan 	}
835366f6083SPeter Grehan 
836366f6083SPeter Grehan 	pdi->pi_bar[idx].type = type;
837366f6083SPeter Grehan 	pdi->pi_bar[idx].addr = addr;
838366f6083SPeter Grehan 	pdi->pi_bar[idx].size = size;
839e87a6f3eSCorvin Köhne 	/*
840e87a6f3eSCorvin Köhne 	 * passthru devices are using same lobits as physical device they set
841e87a6f3eSCorvin Köhne 	 * this property
842e87a6f3eSCorvin Köhne 	 */
843e87a6f3eSCorvin Köhne 	if (pdi->pi_bar[idx].lobits != 0) {
844e87a6f3eSCorvin Köhne 		lobits = pdi->pi_bar[idx].lobits;
845e87a6f3eSCorvin Köhne 	} else {
846e87a6f3eSCorvin Köhne 		pdi->pi_bar[idx].lobits = lobits;
847e87a6f3eSCorvin Köhne 	}
848366f6083SPeter Grehan 
849366f6083SPeter Grehan 	/* Initialize the BAR register in config space */
850366f6083SPeter Grehan 	bar = (addr & mask) | lobits;
851366f6083SPeter Grehan 	pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar);
852366f6083SPeter Grehan 
853366f6083SPeter Grehan 	if (type == PCIBAR_MEM64) {
854366f6083SPeter Grehan 		assert(idx + 1 <= PCI_BARMAX);
855366f6083SPeter Grehan 		pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64;
856366f6083SPeter Grehan 		pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
857366f6083SPeter Grehan 	}
858366f6083SPeter Grehan 
859e47fe318SCorvin Köhne 	if (type != PCIBAR_ROM) {
860028d9311SNeel Natu 		register_bar(pdi, idx);
861e47fe318SCorvin Köhne 	}
862e47fe318SCorvin Köhne 
863e47fe318SCorvin Köhne 	return (0);
864e47fe318SCorvin Köhne }
865e47fe318SCorvin Köhne 
866e47fe318SCorvin Köhne int
867e47fe318SCorvin Köhne pci_emul_alloc_rom(struct pci_devinst *const pdi, const uint64_t size,
868e47fe318SCorvin Köhne     void **const addr)
869e47fe318SCorvin Köhne {
870e47fe318SCorvin Köhne 	/* allocate ROM space once on first call */
871e47fe318SCorvin Köhne 	if (pci_emul_rombase == 0) {
872e47fe318SCorvin Köhne 		pci_emul_rombase = vm_create_devmem(pdi->pi_vmctx, VM_PCIROM,
873e47fe318SCorvin Köhne 		    "pcirom", PCI_EMUL_ROMSIZE);
874e47fe318SCorvin Köhne 		if (pci_emul_rombase == MAP_FAILED) {
875e47fe318SCorvin Köhne 			warnx("%s: failed to create rom segment", __func__);
876e47fe318SCorvin Köhne 			return (-1);
877e47fe318SCorvin Köhne 		}
878e47fe318SCorvin Köhne 		pci_emul_romlim = pci_emul_rombase + PCI_EMUL_ROMSIZE;
879e47fe318SCorvin Köhne 		pci_emul_romoffset = 0;
880e47fe318SCorvin Köhne 	}
881e47fe318SCorvin Köhne 
882e47fe318SCorvin Köhne 	/* ROM size should be a power of 2 and greater than 2 KB */
883e47fe318SCorvin Köhne 	const uint64_t rom_size = MAX(1UL << flsl(size),
884e47fe318SCorvin Köhne 	    ~PCIM_BIOS_ADDR_MASK + 1);
885e47fe318SCorvin Köhne 
886e47fe318SCorvin Köhne 	/* check if ROM fits into ROM space */
887e47fe318SCorvin Köhne 	if (pci_emul_romoffset + rom_size > PCI_EMUL_ROMSIZE) {
888e47fe318SCorvin Köhne 		warnx("%s: no space left in rom segment:", __func__);
889e47fe318SCorvin Köhne 		warnx("%16lu bytes left",
890e47fe318SCorvin Köhne 		    PCI_EMUL_ROMSIZE - pci_emul_romoffset);
891e47fe318SCorvin Köhne 		warnx("%16lu bytes required by %d/%d/%d", rom_size, pdi->pi_bus,
892e47fe318SCorvin Köhne 		    pdi->pi_slot, pdi->pi_func);
893e47fe318SCorvin Köhne 		return (-1);
894e47fe318SCorvin Köhne 	}
895e47fe318SCorvin Köhne 
896e47fe318SCorvin Köhne 	/* allocate ROM BAR */
897e47fe318SCorvin Köhne 	const int error = pci_emul_alloc_bar(pdi, PCI_ROM_IDX, PCIBAR_ROM,
898e47fe318SCorvin Köhne 	    rom_size);
899e47fe318SCorvin Köhne 	if (error)
900e47fe318SCorvin Köhne 		return error;
901e47fe318SCorvin Köhne 
902e47fe318SCorvin Köhne 	/* return address */
903e47fe318SCorvin Köhne 	*addr = pci_emul_rombase + pci_emul_romoffset;
904e47fe318SCorvin Köhne 
905e47fe318SCorvin Köhne 	/* save offset into ROM Space */
906e47fe318SCorvin Köhne 	pdi->pi_romoffset = pci_emul_romoffset;
907e47fe318SCorvin Köhne 
908e47fe318SCorvin Köhne 	/* increase offset for next ROM */
909e47fe318SCorvin Köhne 	pci_emul_romoffset += rom_size;
910366f6083SPeter Grehan 
911366f6083SPeter Grehan 	return (0);
912366f6083SPeter Grehan }
913366f6083SPeter Grehan 
914366f6083SPeter Grehan #define	CAP_START_OFFSET	0x40
915366f6083SPeter Grehan static int
916366f6083SPeter Grehan pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen)
917366f6083SPeter Grehan {
918a96b8b80SJohn Baldwin 	int i, capoff, reallen;
919366f6083SPeter Grehan 	uint16_t sts;
920366f6083SPeter Grehan 
921a96b8b80SJohn Baldwin 	assert(caplen > 0);
922366f6083SPeter Grehan 
923366f6083SPeter Grehan 	reallen = roundup2(caplen, 4);		/* dword aligned */
924366f6083SPeter Grehan 
925366f6083SPeter Grehan 	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
926a96b8b80SJohn Baldwin 	if ((sts & PCIM_STATUS_CAPPRESENT) == 0)
927366f6083SPeter Grehan 		capoff = CAP_START_OFFSET;
928a96b8b80SJohn Baldwin 	else
929a96b8b80SJohn Baldwin 		capoff = pi->pi_capend + 1;
930366f6083SPeter Grehan 
931366f6083SPeter Grehan 	/* Check if we have enough space */
932a96b8b80SJohn Baldwin 	if (capoff + reallen > PCI_REGMAX + 1)
933366f6083SPeter Grehan 		return (-1);
934366f6083SPeter Grehan 
935a96b8b80SJohn Baldwin 	/* Set the previous capability pointer */
936a96b8b80SJohn Baldwin 	if ((sts & PCIM_STATUS_CAPPRESENT) == 0) {
937a96b8b80SJohn Baldwin 		pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff);
938a96b8b80SJohn Baldwin 		pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT);
939a96b8b80SJohn Baldwin 	} else
940a96b8b80SJohn Baldwin 		pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff);
941a96b8b80SJohn Baldwin 
942366f6083SPeter Grehan 	/* Copy the capability */
943366f6083SPeter Grehan 	for (i = 0; i < caplen; i++)
944366f6083SPeter Grehan 		pci_set_cfgdata8(pi, capoff + i, capdata[i]);
945366f6083SPeter Grehan 
946366f6083SPeter Grehan 	/* Set the next capability pointer */
947a96b8b80SJohn Baldwin 	pci_set_cfgdata8(pi, capoff + 1, 0);
948366f6083SPeter Grehan 
949a96b8b80SJohn Baldwin 	pi->pi_prevcap = capoff;
950a96b8b80SJohn Baldwin 	pi->pi_capend = capoff + reallen - 1;
951366f6083SPeter Grehan 	return (0);
952366f6083SPeter Grehan }
953366f6083SPeter Grehan 
954366f6083SPeter Grehan static struct pci_devemu *
955621b5090SJohn Baldwin pci_emul_finddev(const char *name)
956366f6083SPeter Grehan {
957366f6083SPeter Grehan 	struct pci_devemu **pdpp, *pdp;
958366f6083SPeter Grehan 
959366f6083SPeter Grehan 	SET_FOREACH(pdpp, pci_devemu_set) {
960366f6083SPeter Grehan 		pdp = *pdpp;
961366f6083SPeter Grehan 		if (!strcmp(pdp->pe_emu, name)) {
962366f6083SPeter Grehan 			return (pdp);
963366f6083SPeter Grehan 		}
964366f6083SPeter Grehan 	}
965366f6083SPeter Grehan 
966366f6083SPeter Grehan 	return (NULL);
967366f6083SPeter Grehan }
968366f6083SPeter Grehan 
969a38e2a64SPeter Grehan static int
970d84882caSNeel Natu pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot,
971d84882caSNeel Natu     int func, struct funcinfo *fi)
972366f6083SPeter Grehan {
973366f6083SPeter Grehan 	struct pci_devinst *pdi;
974a38e2a64SPeter Grehan 	int err;
975a38e2a64SPeter Grehan 
976994f858aSXin LI 	pdi = calloc(1, sizeof(struct pci_devinst));
977366f6083SPeter Grehan 
978366f6083SPeter Grehan 	pdi->pi_vmctx = ctx;
979d84882caSNeel Natu 	pdi->pi_bus = bus;
980366f6083SPeter Grehan 	pdi->pi_slot = slot;
98199d65389SNeel Natu 	pdi->pi_func = func;
9823cbf3585SJohn Baldwin 	pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
9833cbf3585SJohn Baldwin 	pdi->pi_lintr.pin = 0;
9843cbf3585SJohn Baldwin 	pdi->pi_lintr.state = IDLE;
985b3e9732aSJohn Baldwin 	pdi->pi_lintr.pirq_pin = 0;
9863cbf3585SJohn Baldwin 	pdi->pi_lintr.ioapic_irq = 0;
987366f6083SPeter Grehan 	pdi->pi_d = pde;
988366f6083SPeter Grehan 	snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
989366f6083SPeter Grehan 
990366f6083SPeter Grehan 	/* Disable legacy interrupts */
991366f6083SPeter Grehan 	pci_set_cfgdata8(pdi, PCIR_INTLINE, 255);
992366f6083SPeter Grehan 	pci_set_cfgdata8(pdi, PCIR_INTPIN, 0);
993366f6083SPeter Grehan 
9942729c9bbSJohn Baldwin 	pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN);
995366f6083SPeter Grehan 
9966a284cacSJohn Baldwin 	err = (*pde->pe_init)(pdi, fi->fi_config);
997d84882caSNeel Natu 	if (err == 0)
998d84882caSNeel Natu 		fi->fi_devi = pdi;
999d84882caSNeel Natu 	else
1000366f6083SPeter Grehan 		free(pdi);
1001a38e2a64SPeter Grehan 
1002a38e2a64SPeter Grehan 	return (err);
1003366f6083SPeter Grehan }
1004366f6083SPeter Grehan 
1005366f6083SPeter Grehan void
1006366f6083SPeter Grehan pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr)
1007366f6083SPeter Grehan {
1008366f6083SPeter Grehan 	int mmc;
1009366f6083SPeter Grehan 
1010366f6083SPeter Grehan 	/* Number of msi messages must be a power of 2 between 1 and 32 */
1011366f6083SPeter Grehan 	assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32);
1012366f6083SPeter Grehan 	mmc = ffs(msgnum) - 1;
1013366f6083SPeter Grehan 
1014366f6083SPeter Grehan 	bzero(msicap, sizeof(struct msicap));
1015366f6083SPeter Grehan 	msicap->capid = PCIY_MSI;
1016366f6083SPeter Grehan 	msicap->nextptr = nextptr;
1017366f6083SPeter Grehan 	msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1);
1018366f6083SPeter Grehan }
1019366f6083SPeter Grehan 
1020366f6083SPeter Grehan int
1021366f6083SPeter Grehan pci_emul_add_msicap(struct pci_devinst *pi, int msgnum)
1022366f6083SPeter Grehan {
1023366f6083SPeter Grehan 	struct msicap msicap;
1024366f6083SPeter Grehan 
1025366f6083SPeter Grehan 	pci_populate_msicap(&msicap, msgnum, 0);
1026366f6083SPeter Grehan 
1027366f6083SPeter Grehan 	return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap)));
1028366f6083SPeter Grehan }
1029366f6083SPeter Grehan 
1030c9b4e987SNeel Natu static void
1031c9b4e987SNeel Natu pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum,
1032a96b8b80SJohn Baldwin 		     uint32_t msix_tab_size)
1033c9b4e987SNeel Natu {
1034c9b4e987SNeel Natu 
1035c9b4e987SNeel Natu 	assert(msix_tab_size % 4096 == 0);
1036c9b4e987SNeel Natu 
1037c9b4e987SNeel Natu 	bzero(msixcap, sizeof(struct msixcap));
1038c9b4e987SNeel Natu 	msixcap->capid = PCIY_MSIX;
1039c9b4e987SNeel Natu 
1040c9b4e987SNeel Natu 	/*
1041c9b4e987SNeel Natu 	 * Message Control Register, all fields set to
1042c9b4e987SNeel Natu 	 * zero except for the Table Size.
1043c9b4e987SNeel Natu 	 * Note: Table size N is encoded as N-1
1044c9b4e987SNeel Natu 	 */
1045c9b4e987SNeel Natu 	msixcap->msgctrl = msgnum - 1;
1046c9b4e987SNeel Natu 
1047c9b4e987SNeel Natu 	/*
1048c9b4e987SNeel Natu 	 * MSI-X BAR setup:
1049c9b4e987SNeel Natu 	 * - MSI-X table start at offset 0
1050c9b4e987SNeel Natu 	 * - PBA table starts at a 4K aligned offset after the MSI-X table
1051c9b4e987SNeel Natu 	 */
1052c9b4e987SNeel Natu 	msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK;
1053c9b4e987SNeel Natu 	msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK);
1054c9b4e987SNeel Natu }
1055c9b4e987SNeel Natu 
1056c9b4e987SNeel Natu static void
1057c9b4e987SNeel Natu pci_msix_table_init(struct pci_devinst *pi, int table_entries)
1058c9b4e987SNeel Natu {
1059c9b4e987SNeel Natu 	int i, table_size;
1060c9b4e987SNeel Natu 
1061c9b4e987SNeel Natu 	assert(table_entries > 0);
1062c9b4e987SNeel Natu 	assert(table_entries <= MAX_MSIX_TABLE_ENTRIES);
1063c9b4e987SNeel Natu 
1064c9b4e987SNeel Natu 	table_size = table_entries * MSIX_TABLE_ENTRY_SIZE;
1065994f858aSXin LI 	pi->pi_msix.table = calloc(1, table_size);
1066c9b4e987SNeel Natu 
1067c9b4e987SNeel Natu 	/* set mask bit of vector control register */
1068c9b4e987SNeel Natu 	for (i = 0; i < table_entries; i++)
1069c9b4e987SNeel Natu 		pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK;
1070c9b4e987SNeel Natu }
1071c9b4e987SNeel Natu 
1072c9b4e987SNeel Natu int
1073c9b4e987SNeel Natu pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum)
1074c9b4e987SNeel Natu {
1075c9b4e987SNeel Natu 	uint32_t tab_size;
1076c9b4e987SNeel Natu 	struct msixcap msixcap;
1077c9b4e987SNeel Natu 
1078c9b4e987SNeel Natu 	assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES);
1079c9b4e987SNeel Natu 	assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0);
1080c9b4e987SNeel Natu 
1081c9b4e987SNeel Natu 	tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE;
1082c9b4e987SNeel Natu 
1083c9b4e987SNeel Natu 	/* Align table size to nearest 4K */
1084c9b4e987SNeel Natu 	tab_size = roundup2(tab_size, 4096);
1085c9b4e987SNeel Natu 
1086c9b4e987SNeel Natu 	pi->pi_msix.table_bar = barnum;
1087c9b4e987SNeel Natu 	pi->pi_msix.pba_bar   = barnum;
1088c9b4e987SNeel Natu 	pi->pi_msix.table_offset = 0;
1089c9b4e987SNeel Natu 	pi->pi_msix.table_count = msgnum;
1090c9b4e987SNeel Natu 	pi->pi_msix.pba_offset = tab_size;
10917a902ec0SNeel Natu 	pi->pi_msix.pba_size = PBA_SIZE(msgnum);
1092c9b4e987SNeel Natu 
1093c9b4e987SNeel Natu 	pci_msix_table_init(pi, msgnum);
1094c9b4e987SNeel Natu 
1095a96b8b80SJohn Baldwin 	pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size);
1096c9b4e987SNeel Natu 
1097c9b4e987SNeel Natu 	/* allocate memory for MSI-X Table and PBA */
1098c9b4e987SNeel Natu 	pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32,
1099c9b4e987SNeel Natu 				tab_size + pi->pi_msix.pba_size);
1100c9b4e987SNeel Natu 
1101c9b4e987SNeel Natu 	return (pci_emul_add_capability(pi, (u_char *)&msixcap,
1102c9b4e987SNeel Natu 					sizeof(msixcap)));
1103c9b4e987SNeel Natu }
1104c9b4e987SNeel Natu 
110521368498SPeter Grehan static void
1106cd942e0fSPeter Grehan msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
1107cd942e0fSPeter Grehan 		 int bytes, uint32_t val)
1108cd942e0fSPeter Grehan {
1109cd942e0fSPeter Grehan 	uint16_t msgctrl, rwmask;
1110d74fdc6aSMarcelo Araujo 	int off;
1111cd942e0fSPeter Grehan 
1112cd942e0fSPeter Grehan 	off = offset - capoff;
1113cd942e0fSPeter Grehan 	/* Message Control Register */
1114cd942e0fSPeter Grehan 	if (off == 2 && bytes == 2) {
1115cd942e0fSPeter Grehan 		rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK;
1116cd942e0fSPeter Grehan 		msgctrl = pci_get_cfgdata16(pi, offset);
1117cd942e0fSPeter Grehan 		msgctrl &= ~rwmask;
1118cd942e0fSPeter Grehan 		msgctrl |= val & rwmask;
1119cd942e0fSPeter Grehan 		val = msgctrl;
1120cd942e0fSPeter Grehan 
1121cd942e0fSPeter Grehan 		pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE;
1122c9b4e987SNeel Natu 		pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK;
11233cbf3585SJohn Baldwin 		pci_lintr_update(pi);
1124cd942e0fSPeter Grehan 	}
1125cd942e0fSPeter Grehan 
1126cd942e0fSPeter Grehan 	CFGWRITE(pi, offset, val, bytes);
1127cd942e0fSPeter Grehan }
1128cd942e0fSPeter Grehan 
112921368498SPeter Grehan static void
1130366f6083SPeter Grehan msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
1131366f6083SPeter Grehan 		int bytes, uint32_t val)
1132366f6083SPeter Grehan {
1133366f6083SPeter Grehan 	uint16_t msgctrl, rwmask, msgdata, mme;
1134366f6083SPeter Grehan 	uint32_t addrlo;
1135366f6083SPeter Grehan 
1136366f6083SPeter Grehan 	/*
1137366f6083SPeter Grehan 	 * If guest is writing to the message control register make sure
1138366f6083SPeter Grehan 	 * we do not overwrite read-only fields.
1139366f6083SPeter Grehan 	 */
1140366f6083SPeter Grehan 	if ((offset - capoff) == 2 && bytes == 2) {
1141366f6083SPeter Grehan 		rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE;
1142366f6083SPeter Grehan 		msgctrl = pci_get_cfgdata16(pi, offset);
1143366f6083SPeter Grehan 		msgctrl &= ~rwmask;
1144366f6083SPeter Grehan 		msgctrl |= val & rwmask;
1145366f6083SPeter Grehan 		val = msgctrl;
11467840d1c4SJohn Baldwin 	}
11477840d1c4SJohn Baldwin 	CFGWRITE(pi, offset, val, bytes);
1148366f6083SPeter Grehan 
11497840d1c4SJohn Baldwin 	msgctrl = pci_get_cfgdata16(pi, capoff + 2);
1150366f6083SPeter Grehan 	addrlo = pci_get_cfgdata32(pi, capoff + 4);
1151366f6083SPeter Grehan 	if (msgctrl & PCIM_MSICTRL_64BIT)
1152366f6083SPeter Grehan 		msgdata = pci_get_cfgdata16(pi, capoff + 12);
1153366f6083SPeter Grehan 	else
1154366f6083SPeter Grehan 		msgdata = pci_get_cfgdata16(pi, capoff + 8);
1155366f6083SPeter Grehan 
1156366f6083SPeter Grehan 	mme = msgctrl & PCIM_MSICTRL_MME_MASK;
1157366f6083SPeter Grehan 	pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0;
1158366f6083SPeter Grehan 	if (pi->pi_msi.enabled) {
11594f8be175SNeel Natu 		pi->pi_msi.addr = addrlo;
11604f8be175SNeel Natu 		pi->pi_msi.msg_data = msgdata;
11614f8be175SNeel Natu 		pi->pi_msi.maxmsgnum = 1 << (mme >> 4);
1162366f6083SPeter Grehan 	} else {
11634f8be175SNeel Natu 		pi->pi_msi.maxmsgnum = 0;
1164366f6083SPeter Grehan 	}
11653cbf3585SJohn Baldwin 	pci_lintr_update(pi);
1166366f6083SPeter Grehan }
1167366f6083SPeter Grehan 
116898d920d9SMark Johnston static void
116998d920d9SMark Johnston pciecap_cfgwrite(struct pci_devinst *pi, int capoff __unused, int offset,
117074f80b23SNeel Natu     int bytes, uint32_t val)
117174f80b23SNeel Natu {
117274f80b23SNeel Natu 
117374f80b23SNeel Natu 	/* XXX don't write to the readonly parts */
117474f80b23SNeel Natu 	CFGWRITE(pi, offset, val, bytes);
117574f80b23SNeel Natu }
117674f80b23SNeel Natu 
117774f80b23SNeel Natu #define	PCIECAP_VERSION	0x2
117874f80b23SNeel Natu int
117974f80b23SNeel Natu pci_emul_add_pciecap(struct pci_devinst *pi, int type)
118074f80b23SNeel Natu {
118174f80b23SNeel Natu 	int err;
118274f80b23SNeel Natu 	struct pciecap pciecap;
118374f80b23SNeel Natu 
118474f80b23SNeel Natu 	bzero(&pciecap, sizeof(pciecap));
118574f80b23SNeel Natu 
1186129f93c5SChuck Tuffli 	/*
1187129f93c5SChuck Tuffli 	 * Use the integrated endpoint type for endpoints on a root complex bus.
1188129f93c5SChuck Tuffli 	 *
1189129f93c5SChuck Tuffli 	 * NB: bhyve currently only supports a single PCI bus that is the root
1190129f93c5SChuck Tuffli 	 * complex bus, so all endpoints are integrated.
1191129f93c5SChuck Tuffli 	 */
1192129f93c5SChuck Tuffli 	if ((type == PCIEM_TYPE_ENDPOINT) && (pi->pi_bus == 0))
1193129f93c5SChuck Tuffli 		type = PCIEM_TYPE_ROOT_INT_EP;
1194129f93c5SChuck Tuffli 
119574f80b23SNeel Natu 	pciecap.capid = PCIY_EXPRESS;
1196129f93c5SChuck Tuffli 	pciecap.pcie_capabilities = PCIECAP_VERSION | type;
1197129f93c5SChuck Tuffli 	if (type != PCIEM_TYPE_ROOT_INT_EP) {
119874f80b23SNeel Natu 		pciecap.link_capabilities = 0x411;	/* gen1, x1 */
119974f80b23SNeel Natu 		pciecap.link_status = 0x11;		/* gen1, x1 */
1200129f93c5SChuck Tuffli 	}
120174f80b23SNeel Natu 
120274f80b23SNeel Natu 	err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap));
120374f80b23SNeel Natu 	return (err);
120474f80b23SNeel Natu }
120574f80b23SNeel Natu 
1206366f6083SPeter Grehan /*
1207366f6083SPeter Grehan  * This function assumes that 'coff' is in the capabilities region of the
120821368498SPeter Grehan  * config space. A capoff parameter of zero will force a search for the
120921368498SPeter Grehan  * offset and type.
1210366f6083SPeter Grehan  */
121121368498SPeter Grehan void
121221368498SPeter Grehan pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val,
121321368498SPeter Grehan     uint8_t capoff, int capid)
1214366f6083SPeter Grehan {
121521368498SPeter Grehan 	uint8_t nextoff;
1216366f6083SPeter Grehan 
1217366f6083SPeter Grehan 	/* Do not allow un-aligned writes */
1218366f6083SPeter Grehan 	if ((offset & (bytes - 1)) != 0)
1219366f6083SPeter Grehan 		return;
1220366f6083SPeter Grehan 
122121368498SPeter Grehan 	if (capoff == 0) {
1222366f6083SPeter Grehan 		/* Find the capability that we want to update */
1223366f6083SPeter Grehan 		capoff = CAP_START_OFFSET;
1224366f6083SPeter Grehan 		while (1) {
1225366f6083SPeter Grehan 			nextoff = pci_get_cfgdata8(pi, capoff + 1);
1226a96b8b80SJohn Baldwin 			if (nextoff == 0)
1227a96b8b80SJohn Baldwin 				break;
1228366f6083SPeter Grehan 			if (offset >= capoff && offset < nextoff)
1229366f6083SPeter Grehan 				break;
1230366f6083SPeter Grehan 
1231366f6083SPeter Grehan 			capoff = nextoff;
1232366f6083SPeter Grehan 		}
1233366f6083SPeter Grehan 		assert(offset >= capoff);
123421368498SPeter Grehan 		capid = pci_get_cfgdata8(pi, capoff);
123521368498SPeter Grehan 	}
1236366f6083SPeter Grehan 
1237366f6083SPeter Grehan 	/*
12382a8d400aSPeter Grehan 	 * Capability ID and Next Capability Pointer are readonly.
12392a8d400aSPeter Grehan 	 * However, some o/s's do 4-byte writes that include these.
12402a8d400aSPeter Grehan 	 * For this case, trim the write back to 2 bytes and adjust
12412a8d400aSPeter Grehan 	 * the data.
1242366f6083SPeter Grehan 	 */
12432a8d400aSPeter Grehan 	if (offset == capoff || offset == capoff + 1) {
12442a8d400aSPeter Grehan 		if (offset == capoff && bytes == 4) {
12452a8d400aSPeter Grehan 			bytes = 2;
12462a8d400aSPeter Grehan 			offset += 2;
12472a8d400aSPeter Grehan 			val >>= 16;
12482a8d400aSPeter Grehan 		} else
1249366f6083SPeter Grehan 			return;
12502a8d400aSPeter Grehan 	}
1251366f6083SPeter Grehan 
1252366f6083SPeter Grehan 	switch (capid) {
1253366f6083SPeter Grehan 	case PCIY_MSI:
1254366f6083SPeter Grehan 		msicap_cfgwrite(pi, capoff, offset, bytes, val);
1255366f6083SPeter Grehan 		break;
1256c9b4e987SNeel Natu 	case PCIY_MSIX:
1257c9b4e987SNeel Natu 		msixcap_cfgwrite(pi, capoff, offset, bytes, val);
1258c9b4e987SNeel Natu 		break;
125974f80b23SNeel Natu 	case PCIY_EXPRESS:
126074f80b23SNeel Natu 		pciecap_cfgwrite(pi, capoff, offset, bytes, val);
126174f80b23SNeel Natu 		break;
1262366f6083SPeter Grehan 	default:
1263366f6083SPeter Grehan 		break;
1264366f6083SPeter Grehan 	}
1265366f6083SPeter Grehan }
1266366f6083SPeter Grehan 
1267366f6083SPeter Grehan static int
1268366f6083SPeter Grehan pci_emul_iscap(struct pci_devinst *pi, int offset)
1269366f6083SPeter Grehan {
1270366f6083SPeter Grehan 	uint16_t sts;
1271366f6083SPeter Grehan 
1272366f6083SPeter Grehan 	sts = pci_get_cfgdata16(pi, PCIR_STATUS);
1273366f6083SPeter Grehan 	if ((sts & PCIM_STATUS_CAPPRESENT) != 0) {
1274a96b8b80SJohn Baldwin 		if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend)
1275a96b8b80SJohn Baldwin 			return (1);
1276366f6083SPeter Grehan 	}
1277a96b8b80SJohn Baldwin 	return (0);
1278366f6083SPeter Grehan }
1279366f6083SPeter Grehan 
12800ab13648SPeter Grehan static int
128198d920d9SMark Johnston pci_emul_fallback_handler(struct vmctx *ctx __unused, int vcpu __unused,
128298d920d9SMark Johnston     int dir, uint64_t addr __unused, int size __unused, uint64_t *val,
128398d920d9SMark Johnston     void *arg1 __unused, long arg2 __unused)
12840ab13648SPeter Grehan {
12850ab13648SPeter Grehan 	/*
12860ab13648SPeter Grehan 	 * Ignore writes; return 0xff's for reads. The mem read code
12870ab13648SPeter Grehan 	 * will take care of truncating to the correct size.
12880ab13648SPeter Grehan 	 */
12890ab13648SPeter Grehan 	if (dir == MEM_F_READ) {
12900ab13648SPeter Grehan 		*val = 0xffffffffffffffff;
12910ab13648SPeter Grehan 	}
12920ab13648SPeter Grehan 
12930ab13648SPeter Grehan 	return (0);
12940ab13648SPeter Grehan }
12950ab13648SPeter Grehan 
129612a6eb99SNeel Natu static int
12976a284cacSJohn Baldwin pci_emul_ecfg_handler(struct vmctx *ctx __unused, int vcpu __unused, int dir,
129878c2cd83SJohn Baldwin     uint64_t addr, int bytes, uint64_t *val, void *arg1 __unused,
129978c2cd83SJohn Baldwin     long arg2 __unused)
130012a6eb99SNeel Natu {
130112a6eb99SNeel Natu 	int bus, slot, func, coff, in;
130212a6eb99SNeel Natu 
130312a6eb99SNeel Natu 	coff = addr & 0xfff;
130412a6eb99SNeel Natu 	func = (addr >> 12) & 0x7;
130512a6eb99SNeel Natu 	slot = (addr >> 15) & 0x1f;
130612a6eb99SNeel Natu 	bus = (addr >> 20) & 0xff;
130712a6eb99SNeel Natu 	in = (dir == MEM_F_READ);
130812a6eb99SNeel Natu 	if (in)
130912a6eb99SNeel Natu 		*val = ~0UL;
13106a284cacSJohn Baldwin 	pci_cfgrw(in, bus, slot, func, coff, bytes, (uint32_t *)val);
131112a6eb99SNeel Natu 	return (0);
131212a6eb99SNeel Natu }
131312a6eb99SNeel Natu 
131412a6eb99SNeel Natu uint64_t
131512a6eb99SNeel Natu pci_ecfg_base(void)
131612a6eb99SNeel Natu {
131712a6eb99SNeel Natu 
131812a6eb99SNeel Natu 	return (PCI_EMUL_ECFG_BASE);
131912a6eb99SNeel Natu }
132012a6eb99SNeel Natu 
1321d84882caSNeel Natu #define	BUSIO_ROUNDUP		32
13227d55d295SCorvin Köhne #define	BUSMEM32_ROUNDUP	(1024 * 1024)
13237d55d295SCorvin Köhne #define	BUSMEM64_ROUNDUP	(512 * 1024 * 1024)
1324d84882caSNeel Natu 
1325a38e2a64SPeter Grehan int
1326366f6083SPeter Grehan init_pci(struct vmctx *ctx)
1327366f6083SPeter Grehan {
1328621b5090SJohn Baldwin 	char node_name[sizeof("pci.XXX.XX.X")];
132912a6eb99SNeel Natu 	struct mem_range mr;
1330366f6083SPeter Grehan 	struct pci_devemu *pde;
1331d84882caSNeel Natu 	struct businfo *bi;
1332d84882caSNeel Natu 	struct slotinfo *si;
13333cbf3585SJohn Baldwin 	struct funcinfo *fi;
1334621b5090SJohn Baldwin 	nvlist_t *nvl;
1335621b5090SJohn Baldwin 	const char *emul;
13369f08548dSNeel Natu 	size_t lowmem;
13374a4053e1SCorvin Köhne 	int bus, slot, func;
13384a4053e1SCorvin Köhne 	int error;
1339366f6083SPeter Grehan 
13405cf21e48SCorvin Köhne 	if (vm_get_lowmem_limit(ctx) > PCI_EMUL_MEMBASE32)
13415cf21e48SCorvin Köhne 		errx(EX_OSERR, "Invalid lowmem limit");
13425cf21e48SCorvin Köhne 
1343366f6083SPeter Grehan 	pci_emul_iobase = PCI_EMUL_IOBASE;
13445cf21e48SCorvin Köhne 	pci_emul_membase32 = PCI_EMUL_MEMBASE32;
13459922872bSKonstantin Belousov 
13464a4053e1SCorvin Köhne 	pci_emul_membase64 = 4*GB + vm_get_highmem_size(ctx);
13474a4053e1SCorvin Köhne 	pci_emul_membase64 = roundup2(pci_emul_membase64, PCI_EMUL_MEMSIZE64);
13484a4053e1SCorvin Köhne 	pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64;
1349366f6083SPeter Grehan 
1350d84882caSNeel Natu 	for (bus = 0; bus < MAXBUSES; bus++) {
1351621b5090SJohn Baldwin 		snprintf(node_name, sizeof(node_name), "pci.%d", bus);
1352621b5090SJohn Baldwin 		nvl = find_config_node(node_name);
1353621b5090SJohn Baldwin 		if (nvl == NULL)
1354d84882caSNeel Natu 			continue;
1355621b5090SJohn Baldwin 		pci_businfo[bus] = calloc(1, sizeof(struct businfo));
1356621b5090SJohn Baldwin 		bi = pci_businfo[bus];
1357621b5090SJohn Baldwin 
1358d84882caSNeel Natu 		/*
1359d84882caSNeel Natu 		 * Keep track of the i/o and memory resources allocated to
1360d84882caSNeel Natu 		 * this bus.
1361d84882caSNeel Natu 		 */
1362d84882caSNeel Natu 		bi->iobase = pci_emul_iobase;
1363d84882caSNeel Natu 		bi->membase32 = pci_emul_membase32;
1364d84882caSNeel Natu 		bi->membase64 = pci_emul_membase64;
1365d84882caSNeel Natu 
136601f9362eSCorvin Köhne 		/* first run: init devices */
136799d65389SNeel Natu 		for (slot = 0; slot < MAXSLOTS; slot++) {
1368d84882caSNeel Natu 			si = &bi->slotinfo[slot];
136999d65389SNeel Natu 			for (func = 0; func < MAXFUNCS; func++) {
1370d84882caSNeel Natu 				fi = &si->si_funcs[func];
1371621b5090SJohn Baldwin 				snprintf(node_name, sizeof(node_name),
1372621b5090SJohn Baldwin 				    "pci.%d.%d.%d", bus, slot, func);
1373621b5090SJohn Baldwin 				nvl = find_config_node(node_name);
1374621b5090SJohn Baldwin 				if (nvl == NULL)
1375d84882caSNeel Natu 					continue;
1376621b5090SJohn Baldwin 
1377621b5090SJohn Baldwin 				fi->fi_config = nvl;
1378621b5090SJohn Baldwin 				emul = get_config_value_node(nvl, "device");
1379621b5090SJohn Baldwin 				if (emul == NULL) {
1380621b5090SJohn Baldwin 					EPRINTLN("pci slot %d:%d:%d: missing "
1381621b5090SJohn Baldwin 					    "\"device\" value", bus, slot, func);
1382621b5090SJohn Baldwin 					return (EINVAL);
1383621b5090SJohn Baldwin 				}
1384621b5090SJohn Baldwin 				pde = pci_emul_finddev(emul);
1385621b5090SJohn Baldwin 				if (pde == NULL) {
1386621b5090SJohn Baldwin 					EPRINTLN("pci slot %d:%d:%d: unknown "
1387621b5090SJohn Baldwin 					    "device \"%s\"", bus, slot, func,
1388621b5090SJohn Baldwin 					    emul);
1389621b5090SJohn Baldwin 					return (EINVAL);
1390621b5090SJohn Baldwin 				}
1391621b5090SJohn Baldwin 				if (pde->pe_alias != NULL) {
1392621b5090SJohn Baldwin 					EPRINTLN("pci slot %d:%d:%d: legacy "
1393621b5090SJohn Baldwin 					    "device \"%s\", use \"%s\" instead",
1394621b5090SJohn Baldwin 					    bus, slot, func, emul,
1395621b5090SJohn Baldwin 					    pde->pe_alias);
1396621b5090SJohn Baldwin 					return (EINVAL);
1397621b5090SJohn Baldwin 				}
1398621b5090SJohn Baldwin 				fi->fi_pde = pde;
1399d84882caSNeel Natu 				error = pci_emul_init(ctx, pde, bus, slot,
1400d84882caSNeel Natu 				    func, fi);
1401a38e2a64SPeter Grehan 				if (error)
1402a38e2a64SPeter Grehan 					return (error);
1403366f6083SPeter Grehan 			}
1404366f6083SPeter Grehan 		}
1405d84882caSNeel Natu 
140601f9362eSCorvin Köhne 		/* second run: assign BARs and free list */
140701f9362eSCorvin Köhne 		struct pci_bar_allocation *bar;
140801f9362eSCorvin Köhne 		struct pci_bar_allocation *bar_tmp;
140901f9362eSCorvin Köhne 		TAILQ_FOREACH_SAFE(bar, &pci_bars, chain, bar_tmp) {
141001f9362eSCorvin Köhne 			pci_emul_assign_bar(bar->pdi, bar->idx, bar->type,
141101f9362eSCorvin Köhne 			    bar->size);
141201f9362eSCorvin Köhne 			free(bar);
141301f9362eSCorvin Köhne 		}
141401f9362eSCorvin Köhne 		TAILQ_INIT(&pci_bars);
141501f9362eSCorvin Köhne 
1416d84882caSNeel Natu 		/*
1417d84882caSNeel Natu 		 * Add some slop to the I/O and memory resources decoded by
1418d84882caSNeel Natu 		 * this bus to give a guest some flexibility if it wants to
1419d84882caSNeel Natu 		 * reprogram the BARs.
1420d84882caSNeel Natu 		 */
1421d84882caSNeel Natu 		pci_emul_iobase += BUSIO_ROUNDUP;
1422d84882caSNeel Natu 		pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP);
1423d84882caSNeel Natu 		bi->iolimit = pci_emul_iobase;
1424d84882caSNeel Natu 
14257d55d295SCorvin Köhne 		pci_emul_membase32 += BUSMEM32_ROUNDUP;
1426d84882caSNeel Natu 		pci_emul_membase32 = roundup2(pci_emul_membase32,
14277d55d295SCorvin Köhne 		    BUSMEM32_ROUNDUP);
1428d84882caSNeel Natu 		bi->memlimit32 = pci_emul_membase32;
1429d84882caSNeel Natu 
14307d55d295SCorvin Köhne 		pci_emul_membase64 += BUSMEM64_ROUNDUP;
1431d84882caSNeel Natu 		pci_emul_membase64 = roundup2(pci_emul_membase64,
14327d55d295SCorvin Köhne 		    BUSMEM64_ROUNDUP);
1433d84882caSNeel Natu 		bi->memlimit64 = pci_emul_membase64;
1434366f6083SPeter Grehan 	}
14350038ee98SPeter Grehan 
14360038ee98SPeter Grehan 	/*
1437b3e9732aSJohn Baldwin 	 * PCI backends are initialized before routing INTx interrupts
1438b3e9732aSJohn Baldwin 	 * so that LPC devices are able to reserve ISA IRQs before
1439b3e9732aSJohn Baldwin 	 * routing PIRQ pins.
1440b3e9732aSJohn Baldwin 	 */
1441b3e9732aSJohn Baldwin 	for (bus = 0; bus < MAXBUSES; bus++) {
1442b3e9732aSJohn Baldwin 		if ((bi = pci_businfo[bus]) == NULL)
1443b3e9732aSJohn Baldwin 			continue;
1444b3e9732aSJohn Baldwin 
1445b3e9732aSJohn Baldwin 		for (slot = 0; slot < MAXSLOTS; slot++) {
1446b3e9732aSJohn Baldwin 			si = &bi->slotinfo[slot];
1447b3e9732aSJohn Baldwin 			for (func = 0; func < MAXFUNCS; func++) {
1448b3e9732aSJohn Baldwin 				fi = &si->si_funcs[func];
1449b3e9732aSJohn Baldwin 				if (fi->fi_devi == NULL)
1450b3e9732aSJohn Baldwin 					continue;
1451b3e9732aSJohn Baldwin 				pci_lintr_route(fi->fi_devi);
1452b3e9732aSJohn Baldwin 			}
1453b3e9732aSJohn Baldwin 		}
1454b3e9732aSJohn Baldwin 	}
1455b3e9732aSJohn Baldwin 	lpc_pirq_routed();
1456b3e9732aSJohn Baldwin 
1457b3e9732aSJohn Baldwin 	/*
14589f08548dSNeel Natu 	 * The guest physical memory map looks like the following:
14599f08548dSNeel Natu 	 * [0,		    lowmem)		guest system memory
14605cf21e48SCorvin Köhne 	 * [lowmem,	    0xC0000000)		memory hole (may be absent)
14615cf21e48SCorvin Köhne 	 * [0xC0000000,     0xE0000000)		PCI hole (32-bit BAR allocation)
146212a6eb99SNeel Natu 	 * [0xE0000000,	    0xF0000000)		PCI extended config window
146312a6eb99SNeel Natu 	 * [0xF0000000,	    4GB)		LAPIC, IOAPIC, HPET, firmware
14649f08548dSNeel Natu 	 * [4GB,	    4GB + highmem)
146512a6eb99SNeel Natu 	 */
146612a6eb99SNeel Natu 
146712a6eb99SNeel Natu 	/*
14689f08548dSNeel Natu 	 * Accesses to memory addresses that are not allocated to system
14699f08548dSNeel Natu 	 * memory or PCI devices return 0xff's.
14700ab13648SPeter Grehan 	 */
1471be679db4SNeel Natu 	lowmem = vm_get_lowmem_size(ctx);
147212a6eb99SNeel Natu 	bzero(&mr, sizeof(struct mem_range));
147312a6eb99SNeel Natu 	mr.name = "PCI hole";
147412a6eb99SNeel Natu 	mr.flags = MEM_F_RW | MEM_F_IMMUTABLE;
147512a6eb99SNeel Natu 	mr.base = lowmem;
147612a6eb99SNeel Natu 	mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem;
147712a6eb99SNeel Natu 	mr.handler = pci_emul_fallback_handler;
147812a6eb99SNeel Natu 	error = register_mem_fallback(&mr);
147912a6eb99SNeel Natu 	assert(error == 0);
14809f08548dSNeel Natu 
148112a6eb99SNeel Natu 	/* PCI extended config space */
148212a6eb99SNeel Natu 	bzero(&mr, sizeof(struct mem_range));
148312a6eb99SNeel Natu 	mr.name = "PCI ECFG";
148412a6eb99SNeel Natu 	mr.flags = MEM_F_RW | MEM_F_IMMUTABLE;
148512a6eb99SNeel Natu 	mr.base = PCI_EMUL_ECFG_BASE;
148612a6eb99SNeel Natu 	mr.size = PCI_EMUL_ECFG_SIZE;
148712a6eb99SNeel Natu 	mr.handler = pci_emul_ecfg_handler;
148812a6eb99SNeel Natu 	error = register_mem(&mr);
14890ab13648SPeter Grehan 	assert(error == 0);
1490a38e2a64SPeter Grehan 
1491a38e2a64SPeter Grehan 	return (0);
1492366f6083SPeter Grehan }
1493366f6083SPeter Grehan 
14943cbf3585SJohn Baldwin static void
149598d920d9SMark Johnston pci_apic_prt_entry(int bus __unused, int slot, int pin, int pirq_pin __unused,
149698d920d9SMark Johnston     int ioapic_irq, void *arg __unused)
14973cbf3585SJohn Baldwin {
14983cbf3585SJohn Baldwin 
1499b3e9732aSJohn Baldwin 	dsdt_line("  Package ()");
15003cbf3585SJohn Baldwin 	dsdt_line("  {");
15013cbf3585SJohn Baldwin 	dsdt_line("    0x%X,", slot << 16 | 0xffff);
15023cbf3585SJohn Baldwin 	dsdt_line("    0x%02X,", pin - 1);
15033cbf3585SJohn Baldwin 	dsdt_line("    Zero,");
15043cbf3585SJohn Baldwin 	dsdt_line("    0x%X", ioapic_irq);
1505b3e9732aSJohn Baldwin 	dsdt_line("  },");
1506b3e9732aSJohn Baldwin }
1507b3e9732aSJohn Baldwin 
1508b3e9732aSJohn Baldwin static void
150998d920d9SMark Johnston pci_pirq_prt_entry(int bus __unused, int slot, int pin, int pirq_pin,
151098d920d9SMark Johnston     int ioapic_irq __unused, void *arg __unused)
1511b3e9732aSJohn Baldwin {
1512b3e9732aSJohn Baldwin 	char *name;
1513b3e9732aSJohn Baldwin 
1514b3e9732aSJohn Baldwin 	name = lpc_pirq_name(pirq_pin);
1515b3e9732aSJohn Baldwin 	if (name == NULL)
1516b3e9732aSJohn Baldwin 		return;
1517b3e9732aSJohn Baldwin 	dsdt_line("  Package ()");
1518b3e9732aSJohn Baldwin 	dsdt_line("  {");
1519b3e9732aSJohn Baldwin 	dsdt_line("    0x%X,", slot << 16 | 0xffff);
1520b3e9732aSJohn Baldwin 	dsdt_line("    0x%02X,", pin - 1);
1521b3e9732aSJohn Baldwin 	dsdt_line("    %s,", name);
1522b3e9732aSJohn Baldwin 	dsdt_line("    0x00");
1523b3e9732aSJohn Baldwin 	dsdt_line("  },");
1524b3e9732aSJohn Baldwin 	free(name);
15253cbf3585SJohn Baldwin }
15263cbf3585SJohn Baldwin 
1527d84882caSNeel Natu /*
1528d84882caSNeel Natu  * A bhyve virtual machine has a flat PCI hierarchy with a root port
1529d84882caSNeel Natu  * corresponding to each PCI bus.
1530d84882caSNeel Natu  */
1531d84882caSNeel Natu static void
1532d84882caSNeel Natu pci_bus_write_dsdt(int bus)
1533e6c8bc29SJohn Baldwin {
1534d84882caSNeel Natu 	struct businfo *bi;
1535d84882caSNeel Natu 	struct slotinfo *si;
1536e6c8bc29SJohn Baldwin 	struct pci_devinst *pi;
1537b3e9732aSJohn Baldwin 	int count, func, slot;
1538e6c8bc29SJohn Baldwin 
1539d84882caSNeel Natu 	/*
1540d84882caSNeel Natu 	 * If there are no devices on this 'bus' then just return.
1541d84882caSNeel Natu 	 */
1542d84882caSNeel Natu 	if ((bi = pci_businfo[bus]) == NULL) {
1543d84882caSNeel Natu 		/*
1544d84882caSNeel Natu 		 * Bus 0 is special because it decodes the I/O ports used
1545d84882caSNeel Natu 		 * for PCI config space access even if there are no devices
1546d84882caSNeel Natu 		 * on it.
1547d84882caSNeel Natu 		 */
1548d84882caSNeel Natu 		if (bus != 0)
1549d84882caSNeel Natu 			return;
1550d84882caSNeel Natu 	}
1551d84882caSNeel Natu 
1552d84882caSNeel Natu 	dsdt_line("  Device (PC%02X)", bus);
1553e6c8bc29SJohn Baldwin 	dsdt_line("  {");
1554e6c8bc29SJohn Baldwin 	dsdt_line("    Name (_HID, EisaId (\"PNP0A03\"))");
1555d84882caSNeel Natu 
1556d84882caSNeel Natu 	dsdt_line("    Method (_BBN, 0, NotSerialized)");
1557d84882caSNeel Natu 	dsdt_line("    {");
1558d84882caSNeel Natu 	dsdt_line("        Return (0x%08X)", bus);
1559d84882caSNeel Natu 	dsdt_line("    }");
1560e6c8bc29SJohn Baldwin 	dsdt_line("    Name (_CRS, ResourceTemplate ()");
1561e6c8bc29SJohn Baldwin 	dsdt_line("    {");
1562e6c8bc29SJohn Baldwin 	dsdt_line("      WordBusNumber (ResourceProducer, MinFixed, "
1563e6c8bc29SJohn Baldwin 	    "MaxFixed, PosDecode,");
1564e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000,             // Granularity");
1565d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Minimum", bus);
1566d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Maximum", bus);
1567e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000,             // Translation Offset");
1568d84882caSNeel Natu 	dsdt_line("        0x0001,             // Length");
1569e6c8bc29SJohn Baldwin 	dsdt_line("        ,, )");
1570d84882caSNeel Natu 
1571d84882caSNeel Natu 	if (bus == 0) {
1572e6c8bc29SJohn Baldwin 		dsdt_indent(3);
1573e6c8bc29SJohn Baldwin 		dsdt_fixed_ioport(0xCF8, 8);
1574e6c8bc29SJohn Baldwin 		dsdt_unindent(3);
1575d84882caSNeel Natu 
1576e6c8bc29SJohn Baldwin 		dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
1577e6c8bc29SJohn Baldwin 		    "PosDecode, EntireRange,");
1578e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Granularity");
1579e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Range Minimum");
1580e6c8bc29SJohn Baldwin 		dsdt_line("        0x0CF7,             // Range Maximum");
1581e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Translation Offset");
1582e6c8bc29SJohn Baldwin 		dsdt_line("        0x0CF8,             // Length");
1583e6c8bc29SJohn Baldwin 		dsdt_line("        ,, , TypeStatic)");
1584d84882caSNeel Natu 
1585e6c8bc29SJohn Baldwin 		dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
1586e6c8bc29SJohn Baldwin 		    "PosDecode, EntireRange,");
1587e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Granularity");
1588e6c8bc29SJohn Baldwin 		dsdt_line("        0x0D00,             // Range Minimum");
1589d84882caSNeel Natu 		dsdt_line("        0x%04X,             // Range Maximum",
1590d84882caSNeel Natu 		    PCI_EMUL_IOBASE - 1);
1591e6c8bc29SJohn Baldwin 		dsdt_line("        0x0000,             // Translation Offset");
1592d84882caSNeel Natu 		dsdt_line("        0x%04X,             // Length",
1593d84882caSNeel Natu 		    PCI_EMUL_IOBASE - 0x0D00);
1594e6c8bc29SJohn Baldwin 		dsdt_line("        ,, , TypeStatic)");
1595d84882caSNeel Natu 
1596d84882caSNeel Natu 		if (bi == NULL) {
1597d84882caSNeel Natu 			dsdt_line("    })");
1598d84882caSNeel Natu 			goto done;
1599d84882caSNeel Natu 		}
1600d84882caSNeel Natu 	}
1601d84882caSNeel Natu 	assert(bi != NULL);
1602d84882caSNeel Natu 
1603d84882caSNeel Natu 	/* i/o window */
1604d84882caSNeel Natu 	dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
1605d84882caSNeel Natu 	    "PosDecode, EntireRange,");
1606d84882caSNeel Natu 	dsdt_line("        0x0000,             // Granularity");
1607d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Minimum", bi->iobase);
1608d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Range Maximum",
1609d84882caSNeel Natu 	    bi->iolimit - 1);
1610d84882caSNeel Natu 	dsdt_line("        0x0000,             // Translation Offset");
1611d84882caSNeel Natu 	dsdt_line("        0x%04X,             // Length",
1612d84882caSNeel Natu 	    bi->iolimit - bi->iobase);
1613d84882caSNeel Natu 	dsdt_line("        ,, , TypeStatic)");
1614d84882caSNeel Natu 
1615d84882caSNeel Natu 	/* mmio window (32-bit) */
1616e6c8bc29SJohn Baldwin 	dsdt_line("      DWordMemory (ResourceProducer, PosDecode, "
1617e6c8bc29SJohn Baldwin 	    "MinFixed, MaxFixed, NonCacheable, ReadWrite,");
1618e6c8bc29SJohn Baldwin 	dsdt_line("        0x00000000,         // Granularity");
1619d84882caSNeel Natu 	dsdt_line("        0x%08X,         // Range Minimum\n", bi->membase32);
1620e6c8bc29SJohn Baldwin 	dsdt_line("        0x%08X,         // Range Maximum\n",
1621d84882caSNeel Natu 	    bi->memlimit32 - 1);
1622e6c8bc29SJohn Baldwin 	dsdt_line("        0x00000000,         // Translation Offset");
1623d84882caSNeel Natu 	dsdt_line("        0x%08X,         // Length\n",
1624d84882caSNeel Natu 	    bi->memlimit32 - bi->membase32);
1625e6c8bc29SJohn Baldwin 	dsdt_line("        ,, , AddressRangeMemory, TypeStatic)");
1626d84882caSNeel Natu 
1627d84882caSNeel Natu 	/* mmio window (64-bit) */
1628e6c8bc29SJohn Baldwin 	dsdt_line("      QWordMemory (ResourceProducer, PosDecode, "
1629e6c8bc29SJohn Baldwin 	    "MinFixed, MaxFixed, NonCacheable, ReadWrite,");
1630e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000000000000000, // Granularity");
1631d84882caSNeel Natu 	dsdt_line("        0x%016lX, // Range Minimum\n", bi->membase64);
1632e6c8bc29SJohn Baldwin 	dsdt_line("        0x%016lX, // Range Maximum\n",
1633d84882caSNeel Natu 	    bi->memlimit64 - 1);
1634e6c8bc29SJohn Baldwin 	dsdt_line("        0x0000000000000000, // Translation Offset");
1635e6c8bc29SJohn Baldwin 	dsdt_line("        0x%016lX, // Length\n",
1636d84882caSNeel Natu 	    bi->memlimit64 - bi->membase64);
1637e6c8bc29SJohn Baldwin 	dsdt_line("        ,, , AddressRangeMemory, TypeStatic)");
1638e6c8bc29SJohn Baldwin 	dsdt_line("    })");
1639d84882caSNeel Natu 
1640d84882caSNeel Natu 	count = pci_count_lintr(bus);
16413cbf3585SJohn Baldwin 	if (count != 0) {
16423cbf3585SJohn Baldwin 		dsdt_indent(2);
1643b3e9732aSJohn Baldwin 		dsdt_line("Name (PPRT, Package ()");
16443cbf3585SJohn Baldwin 		dsdt_line("{");
1645b3e9732aSJohn Baldwin 		pci_walk_lintr(bus, pci_pirq_prt_entry, NULL);
16463cbf3585SJohn Baldwin 		dsdt_line("})");
1647b3e9732aSJohn Baldwin 		dsdt_line("Name (APRT, Package ()");
1648b3e9732aSJohn Baldwin 		dsdt_line("{");
1649b3e9732aSJohn Baldwin 		pci_walk_lintr(bus, pci_apic_prt_entry, NULL);
1650b3e9732aSJohn Baldwin 		dsdt_line("})");
1651b3e9732aSJohn Baldwin 		dsdt_line("Method (_PRT, 0, NotSerialized)");
1652b3e9732aSJohn Baldwin 		dsdt_line("{");
1653b3e9732aSJohn Baldwin 		dsdt_line("  If (PICM)");
1654b3e9732aSJohn Baldwin 		dsdt_line("  {");
1655b3e9732aSJohn Baldwin 		dsdt_line("    Return (APRT)");
1656b3e9732aSJohn Baldwin 		dsdt_line("  }");
1657b3e9732aSJohn Baldwin 		dsdt_line("  Else");
1658b3e9732aSJohn Baldwin 		dsdt_line("  {");
1659b3e9732aSJohn Baldwin 		dsdt_line("    Return (PPRT)");
1660b3e9732aSJohn Baldwin 		dsdt_line("  }");
1661b3e9732aSJohn Baldwin 		dsdt_line("}");
16623cbf3585SJohn Baldwin 		dsdt_unindent(2);
16633cbf3585SJohn Baldwin 	}
1664e6c8bc29SJohn Baldwin 
1665e6c8bc29SJohn Baldwin 	dsdt_indent(2);
1666e6c8bc29SJohn Baldwin 	for (slot = 0; slot < MAXSLOTS; slot++) {
1667d84882caSNeel Natu 		si = &bi->slotinfo[slot];
1668e6c8bc29SJohn Baldwin 		for (func = 0; func < MAXFUNCS; func++) {
1669d84882caSNeel Natu 			pi = si->si_funcs[func].fi_devi;
1670e6c8bc29SJohn Baldwin 			if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL)
1671e6c8bc29SJohn Baldwin 				pi->pi_d->pe_write_dsdt(pi);
1672e6c8bc29SJohn Baldwin 		}
1673e6c8bc29SJohn Baldwin 	}
1674e6c8bc29SJohn Baldwin 	dsdt_unindent(2);
1675d84882caSNeel Natu done:
1676e6c8bc29SJohn Baldwin 	dsdt_line("  }");
1677e6c8bc29SJohn Baldwin }
1678e6c8bc29SJohn Baldwin 
1679d84882caSNeel Natu void
1680d84882caSNeel Natu pci_write_dsdt(void)
1681d84882caSNeel Natu {
1682d84882caSNeel Natu 	int bus;
1683d84882caSNeel Natu 
1684b3e9732aSJohn Baldwin 	dsdt_indent(1);
1685b3e9732aSJohn Baldwin 	dsdt_line("Name (PICM, 0x00)");
1686b3e9732aSJohn Baldwin 	dsdt_line("Method (_PIC, 1, NotSerialized)");
1687b3e9732aSJohn Baldwin 	dsdt_line("{");
1688b3e9732aSJohn Baldwin 	dsdt_line("  Store (Arg0, PICM)");
1689b3e9732aSJohn Baldwin 	dsdt_line("}");
1690b3e9732aSJohn Baldwin 	dsdt_line("");
1691b3e9732aSJohn Baldwin 	dsdt_line("Scope (_SB)");
1692b3e9732aSJohn Baldwin 	dsdt_line("{");
1693d84882caSNeel Natu 	for (bus = 0; bus < MAXBUSES; bus++)
1694d84882caSNeel Natu 		pci_bus_write_dsdt(bus);
1695b3e9732aSJohn Baldwin 	dsdt_line("}");
1696b3e9732aSJohn Baldwin 	dsdt_unindent(1);
1697d84882caSNeel Natu }
1698d84882caSNeel Natu 
1699366f6083SPeter Grehan int
1700b100acf2SNeel Natu pci_bus_configured(int bus)
1701b100acf2SNeel Natu {
1702b100acf2SNeel Natu 	assert(bus >= 0 && bus < MAXBUSES);
1703b100acf2SNeel Natu 	return (pci_businfo[bus] != NULL);
1704b100acf2SNeel Natu }
1705b100acf2SNeel Natu 
1706b100acf2SNeel Natu int
1707366f6083SPeter Grehan pci_msi_enabled(struct pci_devinst *pi)
1708366f6083SPeter Grehan {
1709366f6083SPeter Grehan 	return (pi->pi_msi.enabled);
1710366f6083SPeter Grehan }
1711366f6083SPeter Grehan 
1712366f6083SPeter Grehan int
17134f8be175SNeel Natu pci_msi_maxmsgnum(struct pci_devinst *pi)
1714366f6083SPeter Grehan {
1715366f6083SPeter Grehan 	if (pi->pi_msi.enabled)
17164f8be175SNeel Natu 		return (pi->pi_msi.maxmsgnum);
1717366f6083SPeter Grehan 	else
1718366f6083SPeter Grehan 		return (0);
1719366f6083SPeter Grehan }
1720366f6083SPeter Grehan 
1721c9b4e987SNeel Natu int
1722c9b4e987SNeel Natu pci_msix_enabled(struct pci_devinst *pi)
1723c9b4e987SNeel Natu {
1724c9b4e987SNeel Natu 
1725c9b4e987SNeel Natu 	return (pi->pi_msix.enabled && !pi->pi_msi.enabled);
1726c9b4e987SNeel Natu }
1727c9b4e987SNeel Natu 
1728c9b4e987SNeel Natu void
1729c9b4e987SNeel Natu pci_generate_msix(struct pci_devinst *pi, int index)
1730c9b4e987SNeel Natu {
1731c9b4e987SNeel Natu 	struct msix_table_entry *mte;
1732c9b4e987SNeel Natu 
1733c9b4e987SNeel Natu 	if (!pci_msix_enabled(pi))
1734c9b4e987SNeel Natu 		return;
1735c9b4e987SNeel Natu 
1736c9b4e987SNeel Natu 	if (pi->pi_msix.function_mask)
1737c9b4e987SNeel Natu 		return;
1738c9b4e987SNeel Natu 
1739c9b4e987SNeel Natu 	if (index >= pi->pi_msix.table_count)
1740c9b4e987SNeel Natu 		return;
1741c9b4e987SNeel Natu 
1742c9b4e987SNeel Natu 	mte = &pi->pi_msix.table[index];
1743c9b4e987SNeel Natu 	if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
1744c9b4e987SNeel Natu 		/* XXX Set PBA bit if interrupt is disabled */
17454f8be175SNeel Natu 		vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data);
1746c9b4e987SNeel Natu 	}
1747c9b4e987SNeel Natu }
1748c9b4e987SNeel Natu 
1749366f6083SPeter Grehan void
17504f8be175SNeel Natu pci_generate_msi(struct pci_devinst *pi, int index)
1751366f6083SPeter Grehan {
1752366f6083SPeter Grehan 
17534f8be175SNeel Natu 	if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) {
17544f8be175SNeel Natu 		vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr,
17554f8be175SNeel Natu 			     pi->pi_msi.msg_data + index);
1756366f6083SPeter Grehan 	}
1757366f6083SPeter Grehan }
1758366f6083SPeter Grehan 
17593cbf3585SJohn Baldwin static bool
17603cbf3585SJohn Baldwin pci_lintr_permitted(struct pci_devinst *pi)
17610038ee98SPeter Grehan {
17623cbf3585SJohn Baldwin 	uint16_t cmd;
17630038ee98SPeter Grehan 
17643cbf3585SJohn Baldwin 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
17653cbf3585SJohn Baldwin 	return (!(pi->pi_msi.enabled || pi->pi_msix.enabled ||
17663cbf3585SJohn Baldwin 		(cmd & PCIM_CMD_INTxDIS)));
17673cbf3585SJohn Baldwin }
17683cbf3585SJohn Baldwin 
1769b3e9732aSJohn Baldwin void
17703cbf3585SJohn Baldwin pci_lintr_request(struct pci_devinst *pi)
17713cbf3585SJohn Baldwin {
1772d84882caSNeel Natu 	struct businfo *bi;
17733cbf3585SJohn Baldwin 	struct slotinfo *si;
1774b3e9732aSJohn Baldwin 	int bestpin, bestcount, pin;
17753cbf3585SJohn Baldwin 
1776d84882caSNeel Natu 	bi = pci_businfo[pi->pi_bus];
1777d84882caSNeel Natu 	assert(bi != NULL);
1778d84882caSNeel Natu 
17793cbf3585SJohn Baldwin 	/*
1780b3e9732aSJohn Baldwin 	 * Just allocate a pin from our slot.  The pin will be
1781b3e9732aSJohn Baldwin 	 * assigned IRQs later when interrupts are routed.
17823cbf3585SJohn Baldwin 	 */
1783d84882caSNeel Natu 	si = &bi->slotinfo[pi->pi_slot];
17843cbf3585SJohn Baldwin 	bestpin = 0;
17853cbf3585SJohn Baldwin 	bestcount = si->si_intpins[0].ii_count;
17863cbf3585SJohn Baldwin 	for (pin = 1; pin < 4; pin++) {
17873cbf3585SJohn Baldwin 		if (si->si_intpins[pin].ii_count < bestcount) {
17883cbf3585SJohn Baldwin 			bestpin = pin;
17893cbf3585SJohn Baldwin 			bestcount = si->si_intpins[pin].ii_count;
17903cbf3585SJohn Baldwin 		}
17913cbf3585SJohn Baldwin 	}
17923cbf3585SJohn Baldwin 
17933cbf3585SJohn Baldwin 	si->si_intpins[bestpin].ii_count++;
17943cbf3585SJohn Baldwin 	pi->pi_lintr.pin = bestpin + 1;
17953cbf3585SJohn Baldwin 	pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1);
1796b3e9732aSJohn Baldwin }
1797b3e9732aSJohn Baldwin 
1798b3e9732aSJohn Baldwin static void
1799b3e9732aSJohn Baldwin pci_lintr_route(struct pci_devinst *pi)
1800b3e9732aSJohn Baldwin {
1801b3e9732aSJohn Baldwin 	struct businfo *bi;
1802b3e9732aSJohn Baldwin 	struct intxinfo *ii;
1803b3e9732aSJohn Baldwin 
1804b3e9732aSJohn Baldwin 	if (pi->pi_lintr.pin == 0)
1805b3e9732aSJohn Baldwin 		return;
1806b3e9732aSJohn Baldwin 
1807b3e9732aSJohn Baldwin 	bi = pci_businfo[pi->pi_bus];
1808b3e9732aSJohn Baldwin 	assert(bi != NULL);
1809b3e9732aSJohn Baldwin 	ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1];
1810b3e9732aSJohn Baldwin 
1811b3e9732aSJohn Baldwin 	/*
1812b3e9732aSJohn Baldwin 	 * Attempt to allocate an I/O APIC pin for this intpin if one
1813b3e9732aSJohn Baldwin 	 * is not yet assigned.
1814b3e9732aSJohn Baldwin 	 */
1815b3e9732aSJohn Baldwin 	if (ii->ii_ioapic_irq == 0)
18161b4496d0SAlexander Motin 		ii->ii_ioapic_irq = ioapic_pci_alloc_irq(pi);
1817b3e9732aSJohn Baldwin 	assert(ii->ii_ioapic_irq > 0);
1818b3e9732aSJohn Baldwin 
1819b3e9732aSJohn Baldwin 	/*
1820b3e9732aSJohn Baldwin 	 * Attempt to allocate a PIRQ pin for this intpin if one is
1821b3e9732aSJohn Baldwin 	 * not yet assigned.
1822b3e9732aSJohn Baldwin 	 */
1823b3e9732aSJohn Baldwin 	if (ii->ii_pirq_pin == 0)
18241b4496d0SAlexander Motin 		ii->ii_pirq_pin = pirq_alloc_pin(pi);
1825b3e9732aSJohn Baldwin 	assert(ii->ii_pirq_pin > 0);
1826b3e9732aSJohn Baldwin 
1827b3e9732aSJohn Baldwin 	pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq;
1828b3e9732aSJohn Baldwin 	pi->pi_lintr.pirq_pin = ii->ii_pirq_pin;
1829b3e9732aSJohn Baldwin 	pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin));
18300038ee98SPeter Grehan }
18310038ee98SPeter Grehan 
18320038ee98SPeter Grehan void
18330038ee98SPeter Grehan pci_lintr_assert(struct pci_devinst *pi)
18340038ee98SPeter Grehan {
18350038ee98SPeter Grehan 
18363cbf3585SJohn Baldwin 	assert(pi->pi_lintr.pin > 0);
1837ac7304a7SNeel Natu 
18383cbf3585SJohn Baldwin 	pthread_mutex_lock(&pi->pi_lintr.lock);
18393cbf3585SJohn Baldwin 	if (pi->pi_lintr.state == IDLE) {
18403cbf3585SJohn Baldwin 		if (pci_lintr_permitted(pi)) {
18413cbf3585SJohn Baldwin 			pi->pi_lintr.state = ASSERTED;
1842b3e9732aSJohn Baldwin 			pci_irq_assert(pi);
18433cbf3585SJohn Baldwin 		} else
18443cbf3585SJohn Baldwin 			pi->pi_lintr.state = PENDING;
18450038ee98SPeter Grehan 	}
18463cbf3585SJohn Baldwin 	pthread_mutex_unlock(&pi->pi_lintr.lock);
1847ac7304a7SNeel Natu }
18480038ee98SPeter Grehan 
18490038ee98SPeter Grehan void
18500038ee98SPeter Grehan pci_lintr_deassert(struct pci_devinst *pi)
18510038ee98SPeter Grehan {
18520038ee98SPeter Grehan 
18533cbf3585SJohn Baldwin 	assert(pi->pi_lintr.pin > 0);
1854ac7304a7SNeel Natu 
18553cbf3585SJohn Baldwin 	pthread_mutex_lock(&pi->pi_lintr.lock);
18563cbf3585SJohn Baldwin 	if (pi->pi_lintr.state == ASSERTED) {
18573cbf3585SJohn Baldwin 		pi->pi_lintr.state = IDLE;
1858b3e9732aSJohn Baldwin 		pci_irq_deassert(pi);
18593cbf3585SJohn Baldwin 	} else if (pi->pi_lintr.state == PENDING)
18603cbf3585SJohn Baldwin 		pi->pi_lintr.state = IDLE;
18613cbf3585SJohn Baldwin 	pthread_mutex_unlock(&pi->pi_lintr.lock);
18623cbf3585SJohn Baldwin }
18633cbf3585SJohn Baldwin 
18643cbf3585SJohn Baldwin static void
18653cbf3585SJohn Baldwin pci_lintr_update(struct pci_devinst *pi)
18663cbf3585SJohn Baldwin {
18673cbf3585SJohn Baldwin 
18683cbf3585SJohn Baldwin 	pthread_mutex_lock(&pi->pi_lintr.lock);
18693cbf3585SJohn Baldwin 	if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) {
1870b3e9732aSJohn Baldwin 		pci_irq_deassert(pi);
18713cbf3585SJohn Baldwin 		pi->pi_lintr.state = PENDING;
18723cbf3585SJohn Baldwin 	} else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) {
18733cbf3585SJohn Baldwin 		pi->pi_lintr.state = ASSERTED;
1874b3e9732aSJohn Baldwin 		pci_irq_assert(pi);
18753cbf3585SJohn Baldwin 	}
18763cbf3585SJohn Baldwin 	pthread_mutex_unlock(&pi->pi_lintr.lock);
18773cbf3585SJohn Baldwin }
18783cbf3585SJohn Baldwin 
18793cbf3585SJohn Baldwin int
1880d84882caSNeel Natu pci_count_lintr(int bus)
18813cbf3585SJohn Baldwin {
18823cbf3585SJohn Baldwin 	int count, slot, pin;
1883d84882caSNeel Natu 	struct slotinfo *slotinfo;
18843cbf3585SJohn Baldwin 
18853cbf3585SJohn Baldwin 	count = 0;
1886d84882caSNeel Natu 	if (pci_businfo[bus] != NULL) {
18873cbf3585SJohn Baldwin 		for (slot = 0; slot < MAXSLOTS; slot++) {
1888d84882caSNeel Natu 			slotinfo = &pci_businfo[bus]->slotinfo[slot];
18893cbf3585SJohn Baldwin 			for (pin = 0; pin < 4; pin++) {
1890d84882caSNeel Natu 				if (slotinfo->si_intpins[pin].ii_count != 0)
18913cbf3585SJohn Baldwin 					count++;
18923cbf3585SJohn Baldwin 			}
18933cbf3585SJohn Baldwin 		}
1894d84882caSNeel Natu 	}
18953cbf3585SJohn Baldwin 	return (count);
18963cbf3585SJohn Baldwin }
18973cbf3585SJohn Baldwin 
18983cbf3585SJohn Baldwin void
1899d84882caSNeel Natu pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg)
19003cbf3585SJohn Baldwin {
1901d84882caSNeel Natu 	struct businfo *bi;
1902d84882caSNeel Natu 	struct slotinfo *si;
19033cbf3585SJohn Baldwin 	struct intxinfo *ii;
19043cbf3585SJohn Baldwin 	int slot, pin;
19053cbf3585SJohn Baldwin 
1906d84882caSNeel Natu 	if ((bi = pci_businfo[bus]) == NULL)
1907d84882caSNeel Natu 		return;
1908d84882caSNeel Natu 
19093cbf3585SJohn Baldwin 	for (slot = 0; slot < MAXSLOTS; slot++) {
1910d84882caSNeel Natu 		si = &bi->slotinfo[slot];
19113cbf3585SJohn Baldwin 		for (pin = 0; pin < 4; pin++) {
1912d84882caSNeel Natu 			ii = &si->si_intpins[pin];
19133cbf3585SJohn Baldwin 			if (ii->ii_count != 0)
1914b3e9732aSJohn Baldwin 				cb(bus, slot, pin + 1, ii->ii_pirq_pin,
1915b3e9732aSJohn Baldwin 				    ii->ii_ioapic_irq, arg);
19163cbf3585SJohn Baldwin 		}
19170038ee98SPeter Grehan 	}
1918ac7304a7SNeel Natu }
19190038ee98SPeter Grehan 
192099d65389SNeel Natu /*
192199d65389SNeel Natu  * Return 1 if the emulated device in 'slot' is a multi-function device.
192299d65389SNeel Natu  * Return 0 otherwise.
192399d65389SNeel Natu  */
192499d65389SNeel Natu static int
1925d84882caSNeel Natu pci_emul_is_mfdev(int bus, int slot)
192699d65389SNeel Natu {
1927d84882caSNeel Natu 	struct businfo *bi;
1928d84882caSNeel Natu 	struct slotinfo *si;
192999d65389SNeel Natu 	int f, numfuncs;
19300038ee98SPeter Grehan 
193199d65389SNeel Natu 	numfuncs = 0;
1932d84882caSNeel Natu 	if ((bi = pci_businfo[bus]) != NULL) {
1933d84882caSNeel Natu 		si = &bi->slotinfo[slot];
193499d65389SNeel Natu 		for (f = 0; f < MAXFUNCS; f++) {
1935d84882caSNeel Natu 			if (si->si_funcs[f].fi_devi != NULL) {
193699d65389SNeel Natu 				numfuncs++;
193799d65389SNeel Natu 			}
193899d65389SNeel Natu 		}
1939d84882caSNeel Natu 	}
194099d65389SNeel Natu 	return (numfuncs > 1);
194199d65389SNeel Natu }
194299d65389SNeel Natu 
194399d65389SNeel Natu /*
194499d65389SNeel Natu  * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on
194599d65389SNeel Natu  * whether or not is a multi-function being emulated in the pci 'slot'.
194699d65389SNeel Natu  */
194799d65389SNeel Natu static void
1948d84882caSNeel Natu pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv)
194999d65389SNeel Natu {
195099d65389SNeel Natu 	int mfdev;
195199d65389SNeel Natu 
195299d65389SNeel Natu 	if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) {
1953d84882caSNeel Natu 		mfdev = pci_emul_is_mfdev(bus, slot);
195499d65389SNeel Natu 		switch (bytes) {
195599d65389SNeel Natu 		case 1:
195699d65389SNeel Natu 		case 2:
195799d65389SNeel Natu 			*rv &= ~PCIM_MFDEV;
195899d65389SNeel Natu 			if (mfdev) {
195999d65389SNeel Natu 				*rv |= PCIM_MFDEV;
196099d65389SNeel Natu 			}
196199d65389SNeel Natu 			break;
196299d65389SNeel Natu 		case 4:
196399d65389SNeel Natu 			*rv &= ~(PCIM_MFDEV << 16);
196499d65389SNeel Natu 			if (mfdev) {
196599d65389SNeel Natu 				*rv |= (PCIM_MFDEV << 16);
196699d65389SNeel Natu 			}
196799d65389SNeel Natu 			break;
196899d65389SNeel Natu 		}
196999d65389SNeel Natu 	}
197099d65389SNeel Natu }
19710038ee98SPeter Grehan 
197256282675SJohn Baldwin /*
197356282675SJohn Baldwin  * Update device state in response to changes to the PCI command
197456282675SJohn Baldwin  * register.
197556282675SJohn Baldwin  */
197656282675SJohn Baldwin void
197756282675SJohn Baldwin pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old)
197856282675SJohn Baldwin {
197956282675SJohn Baldwin 	int i;
198056282675SJohn Baldwin 	uint16_t changed, new;
198156282675SJohn Baldwin 
198256282675SJohn Baldwin 	new = pci_get_cfgdata16(pi, PCIR_COMMAND);
198356282675SJohn Baldwin 	changed = old ^ new;
198456282675SJohn Baldwin 
198556282675SJohn Baldwin 	/*
198656282675SJohn Baldwin 	 * If the MMIO or I/O address space decoding has changed then
198756282675SJohn Baldwin 	 * register/unregister all BARs that decode that address space.
198856282675SJohn Baldwin 	 */
1989e47fe318SCorvin Köhne 	for (i = 0; i <= PCI_BARMAX_WITH_ROM; i++) {
199056282675SJohn Baldwin 		switch (pi->pi_bar[i].type) {
199156282675SJohn Baldwin 			case PCIBAR_NONE:
199256282675SJohn Baldwin 			case PCIBAR_MEMHI64:
199356282675SJohn Baldwin 				break;
199456282675SJohn Baldwin 			case PCIBAR_IO:
199556282675SJohn Baldwin 				/* I/O address space decoding changed? */
199656282675SJohn Baldwin 				if (changed & PCIM_CMD_PORTEN) {
199756282675SJohn Baldwin 					if (new & PCIM_CMD_PORTEN)
199856282675SJohn Baldwin 						register_bar(pi, i);
199956282675SJohn Baldwin 					else
200056282675SJohn Baldwin 						unregister_bar(pi, i);
200156282675SJohn Baldwin 				}
200256282675SJohn Baldwin 				break;
2003e47fe318SCorvin Köhne 			case PCIBAR_ROM:
2004e47fe318SCorvin Köhne 				/* skip (un-)register of ROM if it disabled */
2005e47fe318SCorvin Köhne 				if (!romen(pi))
2006e47fe318SCorvin Köhne 					break;
2007e47fe318SCorvin Köhne 				/* fallthrough */
200856282675SJohn Baldwin 			case PCIBAR_MEM32:
200956282675SJohn Baldwin 			case PCIBAR_MEM64:
201056282675SJohn Baldwin 				/* MMIO address space decoding changed? */
201156282675SJohn Baldwin 				if (changed & PCIM_CMD_MEMEN) {
201256282675SJohn Baldwin 					if (new & PCIM_CMD_MEMEN)
201356282675SJohn Baldwin 						register_bar(pi, i);
201456282675SJohn Baldwin 					else
201556282675SJohn Baldwin 						unregister_bar(pi, i);
201656282675SJohn Baldwin 				}
201756282675SJohn Baldwin 				break;
201856282675SJohn Baldwin 			default:
201956282675SJohn Baldwin 				assert(0);
202056282675SJohn Baldwin 		}
202156282675SJohn Baldwin 	}
202256282675SJohn Baldwin 
202356282675SJohn Baldwin 	/*
202456282675SJohn Baldwin 	 * If INTx has been unmasked and is pending, assert the
202556282675SJohn Baldwin 	 * interrupt.
202656282675SJohn Baldwin 	 */
202756282675SJohn Baldwin 	pci_lintr_update(pi);
202856282675SJohn Baldwin }
202956282675SJohn Baldwin 
2030028d9311SNeel Natu static void
203154335630SNeel Natu pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes)
2032028d9311SNeel Natu {
203356282675SJohn Baldwin 	int rshift;
203456282675SJohn Baldwin 	uint32_t cmd, old, readonly;
203554335630SNeel Natu 
203654335630SNeel Natu 	cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);	/* stash old value */
2037028d9311SNeel Natu 
2038028d9311SNeel Natu 	/*
203954335630SNeel Natu 	 * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3.
204054335630SNeel Natu 	 *
204154335630SNeel Natu 	 * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are
204254335630SNeel Natu 	 * 'write 1 to clear'. However these bits are not set to '1' by
204354335630SNeel Natu 	 * any device emulation so it is simpler to treat them as readonly.
2044028d9311SNeel Natu 	 */
204554335630SNeel Natu 	rshift = (coff & 0x3) * 8;
204654335630SNeel Natu 	readonly = 0xFFFFF880 >> rshift;
2047028d9311SNeel Natu 
204854335630SNeel Natu 	old = CFGREAD(pi, coff, bytes);
204954335630SNeel Natu 	new &= ~readonly;
205054335630SNeel Natu 	new |= (old & readonly);
205154335630SNeel Natu 	CFGWRITE(pi, coff, new, bytes);			/* update config */
205254335630SNeel Natu 
205356282675SJohn Baldwin 	pci_emul_cmd_changed(pi, cmd);
2054028d9311SNeel Natu }
2055028d9311SNeel Natu 
205612a6eb99SNeel Natu static void
20576a284cacSJohn Baldwin pci_cfgrw(int in, int bus, int slot, int func, int coff, int bytes,
20586a284cacSJohn Baldwin     uint32_t *eax)
2059366f6083SPeter Grehan {
2060d84882caSNeel Natu 	struct businfo *bi;
2061d84882caSNeel Natu 	struct slotinfo *si;
2062366f6083SPeter Grehan 	struct pci_devinst *pi;
2063366f6083SPeter Grehan 	struct pci_devemu *pe;
206412a6eb99SNeel Natu 	int idx, needcfg;
2065028d9311SNeel Natu 	uint64_t addr, bar, mask;
2066366f6083SPeter Grehan 
206712a6eb99SNeel Natu 	if ((bi = pci_businfo[bus]) != NULL) {
206812a6eb99SNeel Natu 		si = &bi->slotinfo[slot];
206912a6eb99SNeel Natu 		pi = si->si_funcs[func].fi_devi;
2070d84882caSNeel Natu 	} else
207190415e0bSNeel Natu 		pi = NULL;
207290415e0bSNeel Natu 
207399d65389SNeel Natu 	/*
207412a6eb99SNeel Natu 	 * Just return if there is no device at this slot:func or if the
207512a6eb99SNeel Natu 	 * the guest is doing an un-aligned access.
207699d65389SNeel Natu 	 */
207712a6eb99SNeel Natu 	if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) ||
207812a6eb99SNeel Natu 	    (coff & (bytes - 1)) != 0) {
2079366f6083SPeter Grehan 		if (in)
2080366f6083SPeter Grehan 			*eax = 0xffffffff;
208112a6eb99SNeel Natu 		return;
208212a6eb99SNeel Natu 	}
208312a6eb99SNeel Natu 
208412a6eb99SNeel Natu 	/*
208512a6eb99SNeel Natu 	 * Ignore all writes beyond the standard config space and return all
208612a6eb99SNeel Natu 	 * ones on reads.
208712a6eb99SNeel Natu 	 */
208812a6eb99SNeel Natu 	if (coff >= PCI_REGMAX + 1) {
208912a6eb99SNeel Natu 		if (in) {
209012a6eb99SNeel Natu 			*eax = 0xffffffff;
209112a6eb99SNeel Natu 			/*
209212a6eb99SNeel Natu 			 * Extended capabilities begin at offset 256 in config
209312a6eb99SNeel Natu 			 * space. Absence of extended capabilities is signaled
209412a6eb99SNeel Natu 			 * with all 0s in the extended capability header at
209512a6eb99SNeel Natu 			 * offset 256.
209612a6eb99SNeel Natu 			 */
209712a6eb99SNeel Natu 			if (coff <= PCI_REGMAX + 4)
209812a6eb99SNeel Natu 				*eax = 0x00000000;
209912a6eb99SNeel Natu 		}
210012a6eb99SNeel Natu 		return;
2101366f6083SPeter Grehan 	}
2102366f6083SPeter Grehan 
2103366f6083SPeter Grehan 	pe = pi->pi_d;
2104366f6083SPeter Grehan 
2105366f6083SPeter Grehan 	/*
2106366f6083SPeter Grehan 	 * Config read
2107366f6083SPeter Grehan 	 */
2108366f6083SPeter Grehan 	if (in) {
2109366f6083SPeter Grehan 		/* Let the device emulation override the default handler */
211099d65389SNeel Natu 		if (pe->pe_cfgread != NULL) {
21116a284cacSJohn Baldwin 			needcfg = pe->pe_cfgread(pi, coff, bytes, eax);
211299d65389SNeel Natu 		} else {
211399d65389SNeel Natu 			needcfg = 1;
211499d65389SNeel Natu 		}
2115366f6083SPeter Grehan 
211654335630SNeel Natu 		if (needcfg)
211754335630SNeel Natu 			*eax = CFGREAD(pi, coff, bytes);
211899d65389SNeel Natu 
211912a6eb99SNeel Natu 		pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax);
2120366f6083SPeter Grehan 	} else {
2121366f6083SPeter Grehan 		/* Let the device emulation override the default handler */
2122366f6083SPeter Grehan 		if (pe->pe_cfgwrite != NULL &&
21236a284cacSJohn Baldwin 		    (*pe->pe_cfgwrite)(pi, coff, bytes, *eax) == 0)
212412a6eb99SNeel Natu 			return;
2125366f6083SPeter Grehan 
2126366f6083SPeter Grehan 		/*
2127e47fe318SCorvin Köhne 		 * Special handling for write to BAR and ROM registers
2128366f6083SPeter Grehan 		 */
212945ddbf21SCorvin Köhne 		if (is_pcir_bar(coff) || is_pcir_bios(coff)) {
2130366f6083SPeter Grehan 			/*
2131366f6083SPeter Grehan 			 * Ignore writes to BAR registers that are not
2132366f6083SPeter Grehan 			 * 4-byte aligned.
2133366f6083SPeter Grehan 			 */
2134366f6083SPeter Grehan 			if (bytes != 4 || (coff & 0x3) != 0)
213512a6eb99SNeel Natu 				return;
213645ddbf21SCorvin Köhne 
213745ddbf21SCorvin Köhne 			if (is_pcir_bar(coff)) {
2138366f6083SPeter Grehan 				idx = (coff - PCIR_BAR(0)) / 4;
213945ddbf21SCorvin Köhne 			} else if (is_pcir_bios(coff)) {
2140e47fe318SCorvin Köhne 				idx = PCI_ROM_IDX;
214145ddbf21SCorvin Köhne 			} else {
214245ddbf21SCorvin Köhne 				errx(4, "%s: invalid BAR offset %d", __func__,
214345ddbf21SCorvin Köhne 				    coff);
2144e47fe318SCorvin Köhne 			}
214545ddbf21SCorvin Köhne 
2146028d9311SNeel Natu 			mask = ~(pi->pi_bar[idx].size - 1);
2147366f6083SPeter Grehan 			switch (pi->pi_bar[idx].type) {
2148366f6083SPeter Grehan 			case PCIBAR_NONE:
2149028d9311SNeel Natu 				pi->pi_bar[idx].addr = bar = 0;
2150366f6083SPeter Grehan 				break;
2151366f6083SPeter Grehan 			case PCIBAR_IO:
2152028d9311SNeel Natu 				addr = *eax & mask;
2153028d9311SNeel Natu 				addr &= 0xffff;
2154e87a6f3eSCorvin Köhne 				bar = addr | pi->pi_bar[idx].lobits;
2155028d9311SNeel Natu 				/*
2156028d9311SNeel Natu 				 * Register the new BAR value for interception
2157028d9311SNeel Natu 				 */
2158028d9311SNeel Natu 				if (addr != pi->pi_bar[idx].addr) {
2159028d9311SNeel Natu 					update_bar_address(pi, addr, idx,
2160028d9311SNeel Natu 							   PCIBAR_IO);
2161028d9311SNeel Natu 				}
2162366f6083SPeter Grehan 				break;
2163366f6083SPeter Grehan 			case PCIBAR_MEM32:
2164028d9311SNeel Natu 				addr = bar = *eax & mask;
2165e87a6f3eSCorvin Köhne 				bar |= pi->pi_bar[idx].lobits;
2166028d9311SNeel Natu 				if (addr != pi->pi_bar[idx].addr) {
2167028d9311SNeel Natu 					update_bar_address(pi, addr, idx,
2168028d9311SNeel Natu 							   PCIBAR_MEM32);
2169028d9311SNeel Natu 				}
2170366f6083SPeter Grehan 				break;
2171366f6083SPeter Grehan 			case PCIBAR_MEM64:
2172028d9311SNeel Natu 				addr = bar = *eax & mask;
2173e87a6f3eSCorvin Köhne 				bar |= pi->pi_bar[idx].lobits;
2174028d9311SNeel Natu 				if (addr != (uint32_t)pi->pi_bar[idx].addr) {
2175028d9311SNeel Natu 					update_bar_address(pi, addr, idx,
2176028d9311SNeel Natu 							   PCIBAR_MEM64);
2177028d9311SNeel Natu 				}
2178366f6083SPeter Grehan 				break;
2179366f6083SPeter Grehan 			case PCIBAR_MEMHI64:
2180366f6083SPeter Grehan 				mask = ~(pi->pi_bar[idx - 1].size - 1);
2181028d9311SNeel Natu 				addr = ((uint64_t)*eax << 32) & mask;
2182028d9311SNeel Natu 				bar = addr >> 32;
2183028d9311SNeel Natu 				if (bar != pi->pi_bar[idx - 1].addr >> 32) {
2184028d9311SNeel Natu 					update_bar_address(pi, addr, idx - 1,
2185028d9311SNeel Natu 							   PCIBAR_MEMHI64);
2186028d9311SNeel Natu 				}
2187366f6083SPeter Grehan 				break;
2188e47fe318SCorvin Köhne 			case PCIBAR_ROM:
2189e47fe318SCorvin Köhne 				addr = bar = *eax & mask;
2190e47fe318SCorvin Köhne 				if (memen(pi) && romen(pi)) {
2191e47fe318SCorvin Köhne 					unregister_bar(pi, idx);
2192e47fe318SCorvin Köhne 				}
2193e47fe318SCorvin Köhne 				pi->pi_bar[idx].addr = addr;
2194e47fe318SCorvin Köhne 				pi->pi_bar[idx].lobits = *eax &
2195e47fe318SCorvin Köhne 				    PCIM_BIOS_ENABLE;
2196e47fe318SCorvin Köhne 				/* romen could have changed it value */
2197e47fe318SCorvin Köhne 				if (memen(pi) && romen(pi)) {
2198e47fe318SCorvin Köhne 					register_bar(pi, idx);
2199e47fe318SCorvin Köhne 				}
2200e47fe318SCorvin Köhne 				bar |= pi->pi_bar[idx].lobits;
2201e47fe318SCorvin Köhne 				break;
2202366f6083SPeter Grehan 			default:
2203366f6083SPeter Grehan 				assert(0);
2204366f6083SPeter Grehan 			}
2205366f6083SPeter Grehan 			pci_set_cfgdata32(pi, coff, bar);
2206cd942e0fSPeter Grehan 
2207366f6083SPeter Grehan 		} else if (pci_emul_iscap(pi, coff)) {
220821368498SPeter Grehan 			pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0);
220954335630SNeel Natu 		} else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) {
221054335630SNeel Natu 			pci_emul_cmdsts_write(pi, coff, *eax, bytes);
2211366f6083SPeter Grehan 		} else {
2212366f6083SPeter Grehan 			CFGWRITE(pi, coff, *eax, bytes);
2213366f6083SPeter Grehan 		}
2214366f6083SPeter Grehan 	}
221512a6eb99SNeel Natu }
2216366f6083SPeter Grehan 
221712a6eb99SNeel Natu static int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff;
221812a6eb99SNeel Natu 
221912a6eb99SNeel Natu static int
222008b05de1SJohn Baldwin pci_emul_cfgaddr(struct vmctx *ctx __unused, int in,
222198d920d9SMark Johnston     int port __unused, int bytes, uint32_t *eax, void *arg __unused)
222212a6eb99SNeel Natu {
222312a6eb99SNeel Natu 	uint32_t x;
222412a6eb99SNeel Natu 
222512a6eb99SNeel Natu 	if (bytes != 4) {
222612a6eb99SNeel Natu 		if (in)
222712a6eb99SNeel Natu 			*eax = (bytes == 2) ? 0xffff : 0xff;
222812a6eb99SNeel Natu 		return (0);
222912a6eb99SNeel Natu 	}
223012a6eb99SNeel Natu 
223112a6eb99SNeel Natu 	if (in) {
223212a6eb99SNeel Natu 		x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff;
223312a6eb99SNeel Natu 		if (cfgenable)
223412a6eb99SNeel Natu 			x |= CONF1_ENABLE;
223512a6eb99SNeel Natu 		*eax = x;
223612a6eb99SNeel Natu 	} else {
223712a6eb99SNeel Natu 		x = *eax;
223812a6eb99SNeel Natu 		cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE;
22391b0e2f0bSCorvin Köhne 		cfgoff = (x & PCI_REGMAX) & ~0x03;
224012a6eb99SNeel Natu 		cfgfunc = (x >> 8) & PCI_FUNCMAX;
224112a6eb99SNeel Natu 		cfgslot = (x >> 11) & PCI_SLOTMAX;
224212a6eb99SNeel Natu 		cfgbus = (x >> 16) & PCI_BUSMAX;
224312a6eb99SNeel Natu 	}
224412a6eb99SNeel Natu 
224512a6eb99SNeel Natu 	return (0);
224612a6eb99SNeel Natu }
224712a6eb99SNeel Natu INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr);
224812a6eb99SNeel Natu 
224912a6eb99SNeel Natu static int
22506a284cacSJohn Baldwin pci_emul_cfgdata(struct vmctx *ctx __unused, int in, int port,
225178c2cd83SJohn Baldwin     int bytes, uint32_t *eax, void *arg __unused)
225212a6eb99SNeel Natu {
225312a6eb99SNeel Natu 	int coff;
225412a6eb99SNeel Natu 
225512a6eb99SNeel Natu 	assert(bytes == 1 || bytes == 2 || bytes == 4);
225612a6eb99SNeel Natu 
225712a6eb99SNeel Natu 	coff = cfgoff + (port - CONF1_DATA_PORT);
225812a6eb99SNeel Natu 	if (cfgenable) {
22596a284cacSJohn Baldwin 		pci_cfgrw(in, cfgbus, cfgslot, cfgfunc, coff, bytes, eax);
226012a6eb99SNeel Natu 	} else {
226112a6eb99SNeel Natu 		/* Ignore accesses to cfgdata if not enabled by cfgaddr */
226212a6eb99SNeel Natu 		if (in)
226312a6eb99SNeel Natu 			*eax = 0xffffffff;
226412a6eb99SNeel Natu 	}
2265366f6083SPeter Grehan 	return (0);
2266366f6083SPeter Grehan }
2267366f6083SPeter Grehan 
2268366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata);
2269366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata);
2270366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
2271366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
2272366f6083SPeter Grehan 
2273483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2274483d953aSJohn Baldwin /*
2275483d953aSJohn Baldwin  * Saves/restores PCI device emulated state. Returns 0 on success.
2276483d953aSJohn Baldwin  */
2277483d953aSJohn Baldwin static int
2278483d953aSJohn Baldwin pci_snapshot_pci_dev(struct vm_snapshot_meta *meta)
2279483d953aSJohn Baldwin {
2280483d953aSJohn Baldwin 	struct pci_devinst *pi;
2281483d953aSJohn Baldwin 	int i;
2282483d953aSJohn Baldwin 	int ret;
2283483d953aSJohn Baldwin 
2284483d953aSJohn Baldwin 	pi = meta->dev_data;
2285483d953aSJohn Baldwin 
2286483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.enabled, meta, ret, done);
2287483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.addr, meta, ret, done);
2288483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.msg_data, meta, ret, done);
2289483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.maxmsgnum, meta, ret, done);
2290483d953aSJohn Baldwin 
2291483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.enabled, meta, ret, done);
2292483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_bar, meta, ret, done);
2293483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_bar, meta, ret, done);
2294483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_offset, meta, ret, done);
2295483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_count, meta, ret, done);
2296483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_offset, meta, ret, done);
2297483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_size, meta, ret, done);
2298483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.function_mask, meta, ret, done);
2299483d953aSJohn Baldwin 
2300483d953aSJohn Baldwin 	SNAPSHOT_BUF_OR_LEAVE(pi->pi_cfgdata, sizeof(pi->pi_cfgdata),
2301483d953aSJohn Baldwin 			      meta, ret, done);
2302483d953aSJohn Baldwin 
2303ed721684SMark Johnston 	for (i = 0; i < (int)nitems(pi->pi_bar); i++) {
2304483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].type, meta, ret, done);
2305483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].size, meta, ret, done);
2306483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].addr, meta, ret, done);
2307483d953aSJohn Baldwin 	}
2308483d953aSJohn Baldwin 
2309483d953aSJohn Baldwin 	/* Restore MSI-X table. */
2310483d953aSJohn Baldwin 	for (i = 0; i < pi->pi_msix.table_count; i++) {
2311483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].addr,
2312483d953aSJohn Baldwin 				      meta, ret, done);
2313483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].msg_data,
2314483d953aSJohn Baldwin 				      meta, ret, done);
2315483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].vector_control,
2316483d953aSJohn Baldwin 				      meta, ret, done);
2317483d953aSJohn Baldwin 	}
2318483d953aSJohn Baldwin 
2319483d953aSJohn Baldwin done:
2320483d953aSJohn Baldwin 	return (ret);
2321483d953aSJohn Baldwin }
2322483d953aSJohn Baldwin 
2323483d953aSJohn Baldwin static int
2324483d953aSJohn Baldwin pci_find_slotted_dev(const char *dev_name, struct pci_devemu **pde,
2325483d953aSJohn Baldwin 		     struct pci_devinst **pdi)
2326483d953aSJohn Baldwin {
2327483d953aSJohn Baldwin 	struct businfo *bi;
2328483d953aSJohn Baldwin 	struct slotinfo *si;
2329483d953aSJohn Baldwin 	struct funcinfo *fi;
2330483d953aSJohn Baldwin 	int bus, slot, func;
2331483d953aSJohn Baldwin 
2332483d953aSJohn Baldwin 	assert(dev_name != NULL);
2333483d953aSJohn Baldwin 	assert(pde != NULL);
2334483d953aSJohn Baldwin 	assert(pdi != NULL);
2335483d953aSJohn Baldwin 
2336483d953aSJohn Baldwin 	for (bus = 0; bus < MAXBUSES; bus++) {
2337483d953aSJohn Baldwin 		if ((bi = pci_businfo[bus]) == NULL)
2338483d953aSJohn Baldwin 			continue;
2339483d953aSJohn Baldwin 
2340483d953aSJohn Baldwin 		for (slot = 0; slot < MAXSLOTS; slot++) {
2341483d953aSJohn Baldwin 			si = &bi->slotinfo[slot];
2342483d953aSJohn Baldwin 			for (func = 0; func < MAXFUNCS; func++) {
2343483d953aSJohn Baldwin 				fi = &si->si_funcs[func];
2344621b5090SJohn Baldwin 				if (fi->fi_pde == NULL)
2345483d953aSJohn Baldwin 					continue;
2346621b5090SJohn Baldwin 				if (strcmp(dev_name, fi->fi_pde->pe_emu) != 0)
2347483d953aSJohn Baldwin 					continue;
2348483d953aSJohn Baldwin 
2349621b5090SJohn Baldwin 				*pde = fi->fi_pde;
2350483d953aSJohn Baldwin 				*pdi = fi->fi_devi;
2351483d953aSJohn Baldwin 				return (0);
2352483d953aSJohn Baldwin 			}
2353483d953aSJohn Baldwin 		}
2354483d953aSJohn Baldwin 	}
2355483d953aSJohn Baldwin 
2356483d953aSJohn Baldwin 	return (EINVAL);
2357483d953aSJohn Baldwin }
2358483d953aSJohn Baldwin 
2359483d953aSJohn Baldwin int
2360483d953aSJohn Baldwin pci_snapshot(struct vm_snapshot_meta *meta)
2361483d953aSJohn Baldwin {
2362483d953aSJohn Baldwin 	struct pci_devemu *pde;
2363483d953aSJohn Baldwin 	struct pci_devinst *pdi;
2364483d953aSJohn Baldwin 	int ret;
2365483d953aSJohn Baldwin 
2366483d953aSJohn Baldwin 	assert(meta->dev_name != NULL);
2367483d953aSJohn Baldwin 
2368483d953aSJohn Baldwin 	ret = pci_find_slotted_dev(meta->dev_name, &pde, &pdi);
2369483d953aSJohn Baldwin 	if (ret != 0) {
2370483d953aSJohn Baldwin 		fprintf(stderr, "%s: no such name: %s\r\n",
2371483d953aSJohn Baldwin 			__func__, meta->dev_name);
2372483d953aSJohn Baldwin 		memset(meta->buffer.buf_start, 0, meta->buffer.buf_size);
2373483d953aSJohn Baldwin 		return (0);
2374483d953aSJohn Baldwin 	}
2375483d953aSJohn Baldwin 
2376483d953aSJohn Baldwin 	meta->dev_data = pdi;
2377483d953aSJohn Baldwin 
2378483d953aSJohn Baldwin 	if (pde->pe_snapshot == NULL) {
2379483d953aSJohn Baldwin 		fprintf(stderr, "%s: not implemented yet for: %s\r\n",
2380483d953aSJohn Baldwin 			__func__, meta->dev_name);
2381483d953aSJohn Baldwin 		return (-1);
2382483d953aSJohn Baldwin 	}
2383483d953aSJohn Baldwin 
2384483d953aSJohn Baldwin 	ret = pci_snapshot_pci_dev(meta);
2385483d953aSJohn Baldwin 	if (ret != 0) {
2386483d953aSJohn Baldwin 		fprintf(stderr, "%s: failed to snapshot pci dev\r\n",
2387483d953aSJohn Baldwin 			__func__);
2388483d953aSJohn Baldwin 		return (-1);
2389483d953aSJohn Baldwin 	}
2390483d953aSJohn Baldwin 
2391483d953aSJohn Baldwin 	ret = (*pde->pe_snapshot)(meta);
2392483d953aSJohn Baldwin 
2393483d953aSJohn Baldwin 	return (ret);
2394483d953aSJohn Baldwin }
2395483d953aSJohn Baldwin 
2396483d953aSJohn Baldwin int
23976a284cacSJohn Baldwin pci_pause(const char *dev_name)
2398483d953aSJohn Baldwin {
2399483d953aSJohn Baldwin 	struct pci_devemu *pde;
2400483d953aSJohn Baldwin 	struct pci_devinst *pdi;
2401483d953aSJohn Baldwin 	int ret;
2402483d953aSJohn Baldwin 
2403483d953aSJohn Baldwin 	assert(dev_name != NULL);
2404483d953aSJohn Baldwin 
2405483d953aSJohn Baldwin 	ret = pci_find_slotted_dev(dev_name, &pde, &pdi);
2406483d953aSJohn Baldwin 	if (ret != 0) {
2407483d953aSJohn Baldwin 		/*
2408483d953aSJohn Baldwin 		 * It is possible to call this function without
2409483d953aSJohn Baldwin 		 * checking that the device is inserted first.
2410483d953aSJohn Baldwin 		 */
2411483d953aSJohn Baldwin 		fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name);
2412483d953aSJohn Baldwin 		return (0);
2413483d953aSJohn Baldwin 	}
2414483d953aSJohn Baldwin 
2415483d953aSJohn Baldwin 	if (pde->pe_pause == NULL) {
2416483d953aSJohn Baldwin 		/* The pause/resume functionality is optional. */
2417483d953aSJohn Baldwin 		fprintf(stderr, "%s: not implemented for: %s\n",
2418483d953aSJohn Baldwin 			__func__, dev_name);
2419483d953aSJohn Baldwin 		return (0);
2420483d953aSJohn Baldwin 	}
2421483d953aSJohn Baldwin 
24226a284cacSJohn Baldwin 	return (*pde->pe_pause)(pdi);
2423483d953aSJohn Baldwin }
2424483d953aSJohn Baldwin 
2425483d953aSJohn Baldwin int
24266a284cacSJohn Baldwin pci_resume(const char *dev_name)
2427483d953aSJohn Baldwin {
2428483d953aSJohn Baldwin 	struct pci_devemu *pde;
2429483d953aSJohn Baldwin 	struct pci_devinst *pdi;
2430483d953aSJohn Baldwin 	int ret;
2431483d953aSJohn Baldwin 
2432483d953aSJohn Baldwin 	assert(dev_name != NULL);
2433483d953aSJohn Baldwin 
2434483d953aSJohn Baldwin 	ret = pci_find_slotted_dev(dev_name, &pde, &pdi);
2435483d953aSJohn Baldwin 	if (ret != 0) {
2436483d953aSJohn Baldwin 		/*
2437483d953aSJohn Baldwin 		 * It is possible to call this function without
2438483d953aSJohn Baldwin 		 * checking that the device is inserted first.
2439483d953aSJohn Baldwin 		 */
2440483d953aSJohn Baldwin 		fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name);
2441483d953aSJohn Baldwin 		return (0);
2442483d953aSJohn Baldwin 	}
2443483d953aSJohn Baldwin 
2444483d953aSJohn Baldwin 	if (pde->pe_resume == NULL) {
2445483d953aSJohn Baldwin 		/* The pause/resume functionality is optional. */
2446483d953aSJohn Baldwin 		fprintf(stderr, "%s: not implemented for: %s\n",
2447483d953aSJohn Baldwin 			__func__, dev_name);
2448483d953aSJohn Baldwin 		return (0);
2449483d953aSJohn Baldwin 	}
2450483d953aSJohn Baldwin 
24516a284cacSJohn Baldwin 	return (*pde->pe_resume)(pdi);
2452483d953aSJohn Baldwin }
2453483d953aSJohn Baldwin #endif
2454483d953aSJohn Baldwin 
2455366f6083SPeter Grehan #define PCI_EMUL_TEST
2456366f6083SPeter Grehan #ifdef PCI_EMUL_TEST
2457366f6083SPeter Grehan /*
2458366f6083SPeter Grehan  * Define a dummy test device
2459366f6083SPeter Grehan  */
2460d84882caSNeel Natu #define DIOSZ	8
24614d1e669cSPeter Grehan #define DMEMSZ	4096
2462366f6083SPeter Grehan struct pci_emul_dsoftc {
24634d1e669cSPeter Grehan 	uint8_t   ioregs[DIOSZ];
2464fd4e0d4cSNeel Natu 	uint8_t	  memregs[2][DMEMSZ];
2465366f6083SPeter Grehan };
2466366f6083SPeter Grehan 
24674d1e669cSPeter Grehan #define	PCI_EMUL_MSI_MSGS	 4
24684d1e669cSPeter Grehan #define	PCI_EMUL_MSIX_MSGS	16
2469366f6083SPeter Grehan 
2470b67e81dbSJohn Baldwin static int
24716a284cacSJohn Baldwin pci_emul_dinit(struct pci_devinst *pi, nvlist_t *nvl __unused)
2472366f6083SPeter Grehan {
2473366f6083SPeter Grehan 	int error;
2474366f6083SPeter Grehan 	struct pci_emul_dsoftc *sc;
2475366f6083SPeter Grehan 
2476994f858aSXin LI 	sc = calloc(1, sizeof(struct pci_emul_dsoftc));
2477366f6083SPeter Grehan 
2478366f6083SPeter Grehan 	pi->pi_arg = sc;
2479366f6083SPeter Grehan 
2480366f6083SPeter Grehan 	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001);
2481366f6083SPeter Grehan 	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD);
2482366f6083SPeter Grehan 	pci_set_cfgdata8(pi, PCIR_CLASS, 0x02);
2483366f6083SPeter Grehan 
24844d1e669cSPeter Grehan 	error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS);
2485366f6083SPeter Grehan 	assert(error == 0);
2486366f6083SPeter Grehan 
24874d1e669cSPeter Grehan 	error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ);
24884d1e669cSPeter Grehan 	assert(error == 0);
24894d1e669cSPeter Grehan 
24904d1e669cSPeter Grehan 	error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ);
2491366f6083SPeter Grehan 	assert(error == 0);
2492366f6083SPeter Grehan 
2493fd4e0d4cSNeel Natu 	error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ);
2494fd4e0d4cSNeel Natu 	assert(error == 0);
2495fd4e0d4cSNeel Natu 
2496366f6083SPeter Grehan 	return (0);
2497366f6083SPeter Grehan }
2498366f6083SPeter Grehan 
2499b67e81dbSJohn Baldwin static void
25006a284cacSJohn Baldwin pci_emul_diow(struct pci_devinst *pi, int baridx, uint64_t offset, int size,
250198d920d9SMark Johnston     uint64_t value)
2502366f6083SPeter Grehan {
2503366f6083SPeter Grehan 	int i;
2504366f6083SPeter Grehan 	struct pci_emul_dsoftc *sc = pi->pi_arg;
2505366f6083SPeter Grehan 
25064d1e669cSPeter Grehan 	if (baridx == 0) {
25074d1e669cSPeter Grehan 		if (offset + size > DIOSZ) {
25084d1e669cSPeter Grehan 			printf("diow: iow too large, offset %ld size %d\n",
25094d1e669cSPeter Grehan 			       offset, size);
2510366f6083SPeter Grehan 			return;
2511366f6083SPeter Grehan 		}
2512366f6083SPeter Grehan 
2513366f6083SPeter Grehan 		if (size == 1) {
25144d1e669cSPeter Grehan 			sc->ioregs[offset] = value & 0xff;
2515366f6083SPeter Grehan 		} else if (size == 2) {
25164d1e669cSPeter Grehan 			*(uint16_t *)&sc->ioregs[offset] = value & 0xffff;
25174d1e669cSPeter Grehan 		} else if (size == 4) {
25184d1e669cSPeter Grehan 			*(uint32_t *)&sc->ioregs[offset] = value;
2519366f6083SPeter Grehan 		} else {
25204d1e669cSPeter Grehan 			printf("diow: iow unknown size %d\n", size);
2521366f6083SPeter Grehan 		}
2522366f6083SPeter Grehan 
2523366f6083SPeter Grehan 		/*
2524366f6083SPeter Grehan 		 * Special magic value to generate an interrupt
2525366f6083SPeter Grehan 		 */
2526366f6083SPeter Grehan 		if (offset == 4 && size == 4 && pci_msi_enabled(pi))
25274f8be175SNeel Natu 			pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi));
2528366f6083SPeter Grehan 
2529366f6083SPeter Grehan 		if (value == 0xabcdef) {
25304f8be175SNeel Natu 			for (i = 0; i < pci_msi_maxmsgnum(pi); i++)
2531366f6083SPeter Grehan 				pci_generate_msi(pi, i);
2532366f6083SPeter Grehan 		}
2533366f6083SPeter Grehan 	}
2534366f6083SPeter Grehan 
2535fd4e0d4cSNeel Natu 	if (baridx == 1 || baridx == 2) {
25364d1e669cSPeter Grehan 		if (offset + size > DMEMSZ) {
25374d1e669cSPeter Grehan 			printf("diow: memw too large, offset %ld size %d\n",
25384d1e669cSPeter Grehan 			       offset, size);
25394d1e669cSPeter Grehan 			return;
25404d1e669cSPeter Grehan 		}
25414d1e669cSPeter Grehan 
2542fd4e0d4cSNeel Natu 		i = baridx - 1;		/* 'memregs' index */
2543fd4e0d4cSNeel Natu 
25444d1e669cSPeter Grehan 		if (size == 1) {
2545fd4e0d4cSNeel Natu 			sc->memregs[i][offset] = value;
25464d1e669cSPeter Grehan 		} else if (size == 2) {
2547fd4e0d4cSNeel Natu 			*(uint16_t *)&sc->memregs[i][offset] = value;
25484d1e669cSPeter Grehan 		} else if (size == 4) {
2549fd4e0d4cSNeel Natu 			*(uint32_t *)&sc->memregs[i][offset] = value;
25504d1e669cSPeter Grehan 		} else if (size == 8) {
2551fd4e0d4cSNeel Natu 			*(uint64_t *)&sc->memregs[i][offset] = value;
25524d1e669cSPeter Grehan 		} else {
25534d1e669cSPeter Grehan 			printf("diow: memw unknown size %d\n", size);
25544d1e669cSPeter Grehan 		}
25554d1e669cSPeter Grehan 
25564d1e669cSPeter Grehan 		/*
25574d1e669cSPeter Grehan 		 * magic interrupt ??
25584d1e669cSPeter Grehan 		 */
25594d1e669cSPeter Grehan 	}
25604d1e669cSPeter Grehan 
25619f3dba68SPedro F. Giffuni 	if (baridx > 2 || baridx < 0) {
25624d1e669cSPeter Grehan 		printf("diow: unknown bar idx %d\n", baridx);
25634d1e669cSPeter Grehan 	}
25644d1e669cSPeter Grehan }
25654d1e669cSPeter Grehan 
25664d1e669cSPeter Grehan static uint64_t
25676a284cacSJohn Baldwin pci_emul_dior(struct pci_devinst *pi, int baridx, uint64_t offset, int size)
2568366f6083SPeter Grehan {
2569366f6083SPeter Grehan 	struct pci_emul_dsoftc *sc = pi->pi_arg;
2570366f6083SPeter Grehan 	uint32_t value;
2571fd4e0d4cSNeel Natu 	int i;
2572366f6083SPeter Grehan 
25734d1e669cSPeter Grehan 	if (baridx == 0) {
25744d1e669cSPeter Grehan 		if (offset + size > DIOSZ) {
25754d1e669cSPeter Grehan 			printf("dior: ior too large, offset %ld size %d\n",
25764d1e669cSPeter Grehan 			       offset, size);
2577366f6083SPeter Grehan 			return (0);
2578366f6083SPeter Grehan 		}
2579366f6083SPeter Grehan 
25806e43f3edSPedro F. Giffuni 		value = 0;
2581366f6083SPeter Grehan 		if (size == 1) {
25824d1e669cSPeter Grehan 			value = sc->ioregs[offset];
2583366f6083SPeter Grehan 		} else if (size == 2) {
25844d1e669cSPeter Grehan 			value = *(uint16_t *) &sc->ioregs[offset];
25854d1e669cSPeter Grehan 		} else if (size == 4) {
25864d1e669cSPeter Grehan 			value = *(uint32_t *) &sc->ioregs[offset];
2587366f6083SPeter Grehan 		} else {
25884d1e669cSPeter Grehan 			printf("dior: ior unknown size %d\n", size);
25894d1e669cSPeter Grehan 		}
25904d1e669cSPeter Grehan 	}
25914d1e669cSPeter Grehan 
2592fd4e0d4cSNeel Natu 	if (baridx == 1 || baridx == 2) {
25934d1e669cSPeter Grehan 		if (offset + size > DMEMSZ) {
25944d1e669cSPeter Grehan 			printf("dior: memr too large, offset %ld size %d\n",
25954d1e669cSPeter Grehan 			       offset, size);
25964d1e669cSPeter Grehan 			return (0);
25974d1e669cSPeter Grehan 		}
25984d1e669cSPeter Grehan 
2599fd4e0d4cSNeel Natu 		i = baridx - 1;		/* 'memregs' index */
2600fd4e0d4cSNeel Natu 
26014d1e669cSPeter Grehan 		if (size == 1) {
2602fd4e0d4cSNeel Natu 			value = sc->memregs[i][offset];
26034d1e669cSPeter Grehan 		} else if (size == 2) {
2604fd4e0d4cSNeel Natu 			value = *(uint16_t *) &sc->memregs[i][offset];
26054d1e669cSPeter Grehan 		} else if (size == 4) {
2606fd4e0d4cSNeel Natu 			value = *(uint32_t *) &sc->memregs[i][offset];
26074d1e669cSPeter Grehan 		} else if (size == 8) {
2608fd4e0d4cSNeel Natu 			value = *(uint64_t *) &sc->memregs[i][offset];
26094d1e669cSPeter Grehan 		} else {
26104d1e669cSPeter Grehan 			printf("dior: ior unknown size %d\n", size);
26114d1e669cSPeter Grehan 		}
26124d1e669cSPeter Grehan 	}
26134d1e669cSPeter Grehan 
26144d1e669cSPeter Grehan 
26159f3dba68SPedro F. Giffuni 	if (baridx > 2 || baridx < 0) {
26164d1e669cSPeter Grehan 		printf("dior: unknown bar idx %d\n", baridx);
26174d1e669cSPeter Grehan 		return (0);
2618366f6083SPeter Grehan 	}
2619366f6083SPeter Grehan 
2620366f6083SPeter Grehan 	return (value);
2621366f6083SPeter Grehan }
2622366f6083SPeter Grehan 
2623483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2624c9faf698SMark Johnston static int
262598d920d9SMark Johnston pci_emul_snapshot(struct vm_snapshot_meta *meta __unused)
2626483d953aSJohn Baldwin {
2627483d953aSJohn Baldwin 	return (0);
2628483d953aSJohn Baldwin }
2629483d953aSJohn Baldwin #endif
2630483d953aSJohn Baldwin 
263137045dfaSMark Johnston static const struct pci_devemu pci_dummy = {
2632366f6083SPeter Grehan 	.pe_emu = "dummy",
2633366f6083SPeter Grehan 	.pe_init = pci_emul_dinit,
26344d1e669cSPeter Grehan 	.pe_barwrite = pci_emul_diow,
2635483d953aSJohn Baldwin 	.pe_barread = pci_emul_dior,
2636483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2637483d953aSJohn Baldwin 	.pe_snapshot = pci_emul_snapshot,
2638483d953aSJohn Baldwin #endif
2639366f6083SPeter Grehan };
2640366f6083SPeter Grehan PCI_EMUL_SET(pci_dummy);
2641366f6083SPeter Grehan 
2642366f6083SPeter Grehan #endif /* PCI_EMUL_TEST */
2643