xref: /openbsd-src/usr.sbin/vmd/pci.c (revision a4052f0f2ce65d0f836241f4aa965b0baf29081f)
1*a4052f0fSdv /*	$OpenBSD: pci.c,v 1.35 2024/10/02 17:05:56 dv Exp $	*/
215caf263Sreyk 
3f3c0184aSmlarkin /*
4f3c0184aSmlarkin  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
5f3c0184aSmlarkin  *
6f3c0184aSmlarkin  * Permission to use, copy, modify, and distribute this software for any
7f3c0184aSmlarkin  * purpose with or without fee is hereby granted, provided that the above
8f3c0184aSmlarkin  * copyright notice and this permission notice appear in all copies.
9f3c0184aSmlarkin  *
10f3c0184aSmlarkin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f3c0184aSmlarkin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f3c0184aSmlarkin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f3c0184aSmlarkin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f3c0184aSmlarkin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15f3c0184aSmlarkin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16f3c0184aSmlarkin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f3c0184aSmlarkin  */
18f3c0184aSmlarkin 
19f3c0184aSmlarkin #include <sys/types.h>
2087302766Sderaadt 
21f3c0184aSmlarkin #include <dev/pci/pcireg.h>
22f3c0184aSmlarkin #include <dev/pci/pcidevs.h>
23ba66f564Sdv #include <dev/vmm/vmm.h>
2487302766Sderaadt 
2587302766Sderaadt #include <string.h>
26813e3047Spd #include <unistd.h>
276eb4c859Sdv 
28f3c0184aSmlarkin #include "vmd.h"
29f3c0184aSmlarkin #include "pci.h"
30813e3047Spd #include "atomicio.h"
31f3c0184aSmlarkin 
32f3c0184aSmlarkin struct pci pci;
33f3c0184aSmlarkin 
34c4fd4c5bSdv extern struct vmd_vm current_vm;
35f3c0184aSmlarkin extern char *__progname;
36f3c0184aSmlarkin 
37f3c0184aSmlarkin /* PIC IRQs, assigned to devices in order */
38a2ad4f71Smlarkin const uint8_t pci_pic_irqs[PCI_MAX_PIC_IRQS] = {3, 5, 6, 7, 9, 10, 11, 12,
39a2ad4f71Smlarkin     14, 15};
40f3c0184aSmlarkin 
41f3c0184aSmlarkin /*
42f3c0184aSmlarkin  * pci_add_bar
43f3c0184aSmlarkin  *
44f3c0184aSmlarkin  * Adds a BAR for the PCI device 'id'. On access, 'barfn' will be
45f3c0184aSmlarkin  * called, and passed 'cookie' as an identifier.
46f3c0184aSmlarkin  *
47f3c0184aSmlarkin  * BARs are fixed size, meaning all I/O BARs requested have the
48f3c0184aSmlarkin  * same size and all MMIO BARs have the same size.
49f3c0184aSmlarkin  *
50f3c0184aSmlarkin  * Parameters:
51f3c0184aSmlarkin  *  id: PCI device to add the BAR to (local count, eg if id == 4,
52f3c0184aSmlarkin  *      this BAR is to be added to the VM's 5th PCI device)
53f3c0184aSmlarkin  *  type: type of the BAR to add (PCI_MAPREG_TYPE_xxx)
54f3c0184aSmlarkin  *  barfn: callback function invoked on BAR access
55f3c0184aSmlarkin  *  cookie: cookie passed to barfn on access
56f3c0184aSmlarkin  *
57f3c0184aSmlarkin  * Returns 0 if the BAR was added successfully, 1 otherwise.
58f3c0184aSmlarkin  */
59f3c0184aSmlarkin int
60f3c0184aSmlarkin pci_add_bar(uint8_t id, uint32_t type, void *barfn, void *cookie)
61f3c0184aSmlarkin {
62f3c0184aSmlarkin 	uint8_t bar_reg_idx, bar_ct;
63f3c0184aSmlarkin 
64f3c0184aSmlarkin 	/* Check id */
65f3c0184aSmlarkin 	if (id >= pci.pci_dev_ct)
66f3c0184aSmlarkin 		return (1);
67f3c0184aSmlarkin 
68f3c0184aSmlarkin 	/* Can only add PCI_MAX_BARS BARs to any device */
69f3c0184aSmlarkin 	bar_ct = pci.pci_devices[id].pd_bar_ct;
70f3c0184aSmlarkin 	if (bar_ct >= PCI_MAX_BARS)
71f3c0184aSmlarkin 		return (1);
72f3c0184aSmlarkin 
73f3c0184aSmlarkin 	/* Compute BAR address and add */
74f3c0184aSmlarkin 	bar_reg_idx = (PCI_MAPREG_START + (bar_ct * 4)) / 4;
75f3c0184aSmlarkin 	if (type == PCI_MAPREG_TYPE_MEM) {
76*a4052f0fSdv 		if (pci.pci_next_mmio_bar >= PCI_MMIO_BAR_END)
77f3c0184aSmlarkin 			return (1);
78f3c0184aSmlarkin 
79f3c0184aSmlarkin 		pci.pci_devices[id].pd_cfg_space[bar_reg_idx] =
80f3c0184aSmlarkin 		    PCI_MAPREG_MEM_ADDR(pci.pci_next_mmio_bar);
81d489aa7eSdv 		pci.pci_next_mmio_bar += VM_PCI_MMIO_BAR_SIZE;
82f3c0184aSmlarkin 		pci.pci_devices[id].pd_barfunc[bar_ct] = barfn;
83f3c0184aSmlarkin 		pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie;
84f3c0184aSmlarkin 		pci.pci_devices[id].pd_bartype[bar_ct] = PCI_BAR_TYPE_MMIO;
85d489aa7eSdv 		pci.pci_devices[id].pd_barsize[bar_ct] = VM_PCI_MMIO_BAR_SIZE;
86f3c0184aSmlarkin 		pci.pci_devices[id].pd_bar_ct++;
87c4fd4c5bSdv 	}
88c4fd4c5bSdv #ifdef __amd64__
89c4fd4c5bSdv 	else if (type == PCI_MAPREG_TYPE_IO) {
90d489aa7eSdv 		if (pci.pci_next_io_bar >= VM_PCI_IO_BAR_END)
91f3c0184aSmlarkin 			return (1);
92f3c0184aSmlarkin 
93f3c0184aSmlarkin 		pci.pci_devices[id].pd_cfg_space[bar_reg_idx] =
94f3c0184aSmlarkin 		    PCI_MAPREG_IO_ADDR(pci.pci_next_io_bar) |
95f3c0184aSmlarkin 		    PCI_MAPREG_TYPE_IO;
96d489aa7eSdv 		pci.pci_next_io_bar += VM_PCI_IO_BAR_SIZE;
97f3c0184aSmlarkin 		pci.pci_devices[id].pd_barfunc[bar_ct] = barfn;
98f3c0184aSmlarkin 		pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie;
991f5e00e0Sreyk 		DPRINTF("%s: adding pci bar cookie for dev %d bar %d = %p",
100f3c0184aSmlarkin 		    __progname, id, bar_ct, cookie);
101f3c0184aSmlarkin 		pci.pci_devices[id].pd_bartype[bar_ct] = PCI_BAR_TYPE_IO;
102d489aa7eSdv 		pci.pci_devices[id].pd_barsize[bar_ct] = VM_PCI_IO_BAR_SIZE;
103f3c0184aSmlarkin 		pci.pci_devices[id].pd_bar_ct++;
104f3c0184aSmlarkin 	}
105c4fd4c5bSdv #endif /* __amd64__ */
106f3c0184aSmlarkin 
107f3c0184aSmlarkin 	return (0);
108f3c0184aSmlarkin }
109f3c0184aSmlarkin 
110813e3047Spd int
111813e3047Spd pci_set_bar_fn(uint8_t id, uint8_t bar_ct, void *barfn, void *cookie)
112813e3047Spd {
113813e3047Spd 	/* Check id */
114813e3047Spd 	if (id >= pci.pci_dev_ct)
115813e3047Spd 		return (1);
116813e3047Spd 
117813e3047Spd 	if (bar_ct >= PCI_MAX_BARS)
118813e3047Spd 		return (1);
119813e3047Spd 
120813e3047Spd 	pci.pci_devices[id].pd_barfunc[bar_ct] = barfn;
121813e3047Spd 	pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie;
122813e3047Spd 
123813e3047Spd 	return (0);
124813e3047Spd }
125813e3047Spd 
126f3c0184aSmlarkin /*
1270eab2803Smlarkin  * pci_get_dev_irq
1280eab2803Smlarkin  *
1290eab2803Smlarkin  * Returns the IRQ for the specified PCI device
1300eab2803Smlarkin  *
1310eab2803Smlarkin  * Parameters:
1320eab2803Smlarkin  *  id: PCI device id to return IRQ for
1330eab2803Smlarkin  *
1340eab2803Smlarkin  * Return values:
1350eab2803Smlarkin  *  The IRQ for the device, or 0xff if no device IRQ assigned
1360eab2803Smlarkin  */
1370eab2803Smlarkin uint8_t
1380eab2803Smlarkin pci_get_dev_irq(uint8_t id)
1390eab2803Smlarkin {
1400eab2803Smlarkin 	if (pci.pci_devices[id].pd_int)
1410eab2803Smlarkin 		return pci.pci_devices[id].pd_irq;
1420eab2803Smlarkin 	else
1430eab2803Smlarkin 		return 0xFF;
1440eab2803Smlarkin }
1450eab2803Smlarkin 
1460eab2803Smlarkin /*
147f3c0184aSmlarkin  * pci_add_device
148f3c0184aSmlarkin  *
149f3c0184aSmlarkin  * Adds a PCI device to the guest VM defined by the supplied parameters.
150f3c0184aSmlarkin  *
151f3c0184aSmlarkin  * Parameters:
152f3c0184aSmlarkin  *  id: the new PCI device ID (0 .. PCI_CONFIG_MAX_DEV)
153f3c0184aSmlarkin  *  vid: PCI VID of the new device
154f3c0184aSmlarkin  *  pid: PCI PID of the new device
155f3c0184aSmlarkin  *  class: PCI 'class' of the new device
156f3c0184aSmlarkin  *  subclass: PCI 'subclass' of the new device
157f3c0184aSmlarkin  *  subsys_vid: subsystem VID of the new device
158f3c0184aSmlarkin  *  subsys_id: subsystem ID of the new device
159f3c0184aSmlarkin  *  irq_needed: 1 if an IRQ should be assigned to this PCI device, 0 otherwise
160f3c0184aSmlarkin  *  csfunc: PCI config space callback function when the guest VM accesses
161f3c0184aSmlarkin  *      CS of this PCI device
162f3c0184aSmlarkin  *
163f3c0184aSmlarkin  * Return values:
164f3c0184aSmlarkin  *  0: the PCI device was added successfully. The PCI device ID is in 'id'.
165f3c0184aSmlarkin  *  1: the PCI device addition failed.
166f3c0184aSmlarkin  */
167f3c0184aSmlarkin int
168f3c0184aSmlarkin pci_add_device(uint8_t *id, uint16_t vid, uint16_t pid, uint8_t class,
169f3c0184aSmlarkin     uint8_t subclass, uint16_t subsys_vid, uint16_t subsys_id,
170f3c0184aSmlarkin     uint8_t irq_needed, pci_cs_fn_t csfunc)
171f3c0184aSmlarkin {
172f3c0184aSmlarkin 	/* Exceeded max devices? */
173f3c0184aSmlarkin 	if (pci.pci_dev_ct >= PCI_CONFIG_MAX_DEV)
174f3c0184aSmlarkin 		return (1);
175f3c0184aSmlarkin 
176f3c0184aSmlarkin 	/* Exceeded max IRQs? */
177f3c0184aSmlarkin 	/* XXX we could share IRQs ... */
178f3c0184aSmlarkin 	if (pci.pci_next_pic_irq >= PCI_MAX_PIC_IRQS && irq_needed)
179f3c0184aSmlarkin 		return (1);
180f3c0184aSmlarkin 
181f3c0184aSmlarkin 	*id = pci.pci_dev_ct;
182f3c0184aSmlarkin 
183f3c0184aSmlarkin 	pci.pci_devices[*id].pd_vid = vid;
184f3c0184aSmlarkin 	pci.pci_devices[*id].pd_did = pid;
185f3c0184aSmlarkin 	pci.pci_devices[*id].pd_class = class;
186f3c0184aSmlarkin 	pci.pci_devices[*id].pd_subclass = subclass;
187f3c0184aSmlarkin 	pci.pci_devices[*id].pd_subsys_vid = subsys_vid;
188f3c0184aSmlarkin 	pci.pci_devices[*id].pd_subsys_id = subsys_id;
189f3c0184aSmlarkin 
190f3c0184aSmlarkin 	pci.pci_devices[*id].pd_csfunc = csfunc;
191f3c0184aSmlarkin 
192f3c0184aSmlarkin 	if (irq_needed) {
193f3c0184aSmlarkin 		pci.pci_devices[*id].pd_irq =
194f3c0184aSmlarkin 		    pci_pic_irqs[pci.pci_next_pic_irq];
195f3c0184aSmlarkin 		pci.pci_devices[*id].pd_int = 1;
196f3c0184aSmlarkin 		pci.pci_next_pic_irq++;
1971f5e00e0Sreyk 		DPRINTF("assigned irq %d to pci dev %d",
198f3c0184aSmlarkin 		    pci.pci_devices[*id].pd_irq, *id);
199c4fd4c5bSdv 		intr_toggle_el(&current_vm, pci.pci_devices[*id].pd_irq, 1);
200f3c0184aSmlarkin 	}
201f3c0184aSmlarkin 
202f3c0184aSmlarkin 	pci.pci_dev_ct ++;
203f3c0184aSmlarkin 
204f3c0184aSmlarkin 	return (0);
205f3c0184aSmlarkin }
206f3c0184aSmlarkin 
207f3c0184aSmlarkin /*
208f3c0184aSmlarkin  * pci_init
209f3c0184aSmlarkin  *
210f3c0184aSmlarkin  * Initializes the PCI subsystem for the VM by adding a PCI host bridge
211f3c0184aSmlarkin  * as the first PCI device.
212f3c0184aSmlarkin  */
213f3c0184aSmlarkin void
214f3c0184aSmlarkin pci_init(void)
215f3c0184aSmlarkin {
216f3c0184aSmlarkin 	uint8_t id;
217f3c0184aSmlarkin 
218cb0fa87cSmlarkin 	memset(&pci, 0, sizeof(pci));
219*a4052f0fSdv 	pci.pci_next_mmio_bar = PCI_MMIO_BAR_BASE;
220c4fd4c5bSdv 
221c4fd4c5bSdv #ifdef __amd64__
222d489aa7eSdv 	pci.pci_next_io_bar = VM_PCI_IO_BAR_BASE;
223c4fd4c5bSdv #endif /* __amd64__ */
224f3c0184aSmlarkin 
225f3c0184aSmlarkin 	if (pci_add_device(&id, PCI_VENDOR_OPENBSD, PCI_PRODUCT_OPENBSD_PCHB,
226f3c0184aSmlarkin 	    PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_HOST,
227f3c0184aSmlarkin 	    PCI_VENDOR_OPENBSD, 0, 0, NULL)) {
2287da934edSreyk 		log_warnx("%s: can't add PCI host bridge", __progname);
229f3c0184aSmlarkin 		return;
230f3c0184aSmlarkin 	}
231f3c0184aSmlarkin }
232f3c0184aSmlarkin 
233c4fd4c5bSdv #ifdef __amd64__
234f3c0184aSmlarkin void
235f3c0184aSmlarkin pci_handle_address_reg(struct vm_run_params *vrp)
236f3c0184aSmlarkin {
23702ee787fSmlarkin 	struct vm_exit *vei = vrp->vrp_exit;
238f3c0184aSmlarkin 
239f3c0184aSmlarkin 	/*
2406ee12970Smlarkin 	 * vei_dir == VEI_DIR_OUT : out instruction
241f3c0184aSmlarkin 	 *
242f3c0184aSmlarkin 	 * The guest wrote to the address register.
243f3c0184aSmlarkin 	 */
2446ee12970Smlarkin 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
245b966d91aSmlarkin 		get_input_data(vei, &pci.pci_addr_reg);
246f3c0184aSmlarkin 	} else {
247f3c0184aSmlarkin 		/*
2486ee12970Smlarkin 		 * vei_dir == VEI_DIR_IN : in instruction
249f3c0184aSmlarkin 		 *
250de6a9f31Smlarkin 		 * The guest read the address register
251f3c0184aSmlarkin 		 */
252eef1411cSmlarkin 		set_return_data(vei, pci.pci_addr_reg);
253f3c0184aSmlarkin 	}
254f3c0184aSmlarkin }
255f3c0184aSmlarkin 
256f3c0184aSmlarkin uint8_t
257f3c0184aSmlarkin pci_handle_io(struct vm_run_params *vrp)
258f3c0184aSmlarkin {
259f3c0184aSmlarkin 	int i, j, k, l;
260f3c0184aSmlarkin 	uint16_t reg, b_hi, b_lo;
261f3c0184aSmlarkin 	pci_iobar_fn_t fn;
26202ee787fSmlarkin 	struct vm_exit *vei = vrp->vrp_exit;
263de6a9f31Smlarkin 	uint8_t intr, dir;
264f3c0184aSmlarkin 
265f3c0184aSmlarkin 	k = -1;
266f3c0184aSmlarkin 	l = -1;
267f3c0184aSmlarkin 	reg = vei->vei.vei_port;
268de6a9f31Smlarkin 	dir = vei->vei.vei_dir;
269f3c0184aSmlarkin 	intr = 0xFF;
270f3c0184aSmlarkin 
271f3c0184aSmlarkin 	for (i = 0 ; i < pci.pci_dev_ct ; i++) {
272f3c0184aSmlarkin 		for (j = 0 ; j < pci.pci_devices[i].pd_bar_ct; j++) {
273f3c0184aSmlarkin 			b_lo = PCI_MAPREG_IO_ADDR(pci.pci_devices[i].pd_bar[j]);
274d489aa7eSdv 			b_hi = b_lo + VM_PCI_IO_BAR_SIZE;
275f3c0184aSmlarkin 			if (reg >= b_lo && reg < b_hi) {
276f3c0184aSmlarkin 				if (pci.pci_devices[i].pd_barfunc[j]) {
277f3c0184aSmlarkin 					k = j;
278f3c0184aSmlarkin 					l = i;
279f3c0184aSmlarkin 				}
280f3c0184aSmlarkin 			}
281f3c0184aSmlarkin 		}
282f3c0184aSmlarkin 	}
283f3c0184aSmlarkin 
284f3c0184aSmlarkin 	if (k >= 0 && l >= 0) {
285f3c0184aSmlarkin 		fn = (pci_iobar_fn_t)pci.pci_devices[l].pd_barfunc[k];
286f3c0184aSmlarkin 		if (fn(vei->vei.vei_dir, reg -
287f3c0184aSmlarkin 		    PCI_MAPREG_IO_ADDR(pci.pci_devices[l].pd_bar[k]),
288f3c0184aSmlarkin 		    &vei->vei.vei_data, &intr,
289eef1411cSmlarkin 		    pci.pci_devices[l].pd_bar_cookie[k],
290eef1411cSmlarkin 		    vei->vei.vei_size)) {
2917da934edSreyk 			log_warnx("%s: pci i/o access function failed",
292f3c0184aSmlarkin 			    __progname);
293f3c0184aSmlarkin 		}
294f3c0184aSmlarkin 	} else {
2958e646ea0Smlarkin 		DPRINTF("%s: no pci i/o function for reg 0x%llx (dir=%d "
2968e646ea0Smlarkin 		    "guest %%rip=0x%llx", __progname, (uint64_t)reg, dir,
2978e646ea0Smlarkin 		    vei->vrs.vrs_gprs[VCPU_REGS_RIP]);
298de6a9f31Smlarkin 		/* Reads from undefined ports return 0xFF */
299afd1d05cSmlarkin 		if (dir == VEI_DIR_IN)
300eef1411cSmlarkin 			set_return_data(vei, 0xFFFFFFFF);
301f3c0184aSmlarkin 	}
302f3c0184aSmlarkin 
303f3c0184aSmlarkin 	if (intr != 0xFF) {
304f3c0184aSmlarkin 		intr = pci.pci_devices[l].pd_irq;
305f3c0184aSmlarkin 	}
306f3c0184aSmlarkin 
307f3c0184aSmlarkin 	return (intr);
308f3c0184aSmlarkin }
309f3c0184aSmlarkin 
310f3c0184aSmlarkin void
311f3c0184aSmlarkin pci_handle_data_reg(struct vm_run_params *vrp)
312f3c0184aSmlarkin {
31302ee787fSmlarkin 	struct vm_exit *vei = vrp->vrp_exit;
314eef1411cSmlarkin 	uint8_t b, d, f, o, baridx, ofs, sz;
315f3c0184aSmlarkin 	int ret;
316f3c0184aSmlarkin 	pci_cs_fn_t csfunc;
317f3c0184aSmlarkin 
318f3c0184aSmlarkin 	/* abort if the address register is wack */
319f3c0184aSmlarkin 	if (!(pci.pci_addr_reg & PCI_MODE1_ENABLE)) {
320f3c0184aSmlarkin 		/* if read, return FFs */
3216ee12970Smlarkin 		if (vei->vei.vei_dir == VEI_DIR_IN)
322eef1411cSmlarkin 			set_return_data(vei, 0xFFFFFFFF);
3237da934edSreyk 		log_warnx("invalid address register during pci read: "
3247da934edSreyk 		    "0x%llx", (uint64_t)pci.pci_addr_reg);
325f3c0184aSmlarkin 		return;
326f3c0184aSmlarkin 	}
327f3c0184aSmlarkin 
328eef1411cSmlarkin 	/* I/Os to 0xCFC..0xCFF are permitted */
329eef1411cSmlarkin 	ofs = vei->vei.vei_port - 0xCFC;
330eef1411cSmlarkin 	sz = vei->vei.vei_size;
331eef1411cSmlarkin 
332f3c0184aSmlarkin 	b = (pci.pci_addr_reg >> 16) & 0xff;
333f3c0184aSmlarkin 	d = (pci.pci_addr_reg >> 11) & 0x1f;
334f3c0184aSmlarkin 	f = (pci.pci_addr_reg >> 8) & 0x7;
335f3c0184aSmlarkin 	o = (pci.pci_addr_reg & 0xfc);
336f3c0184aSmlarkin 
337f3c0184aSmlarkin 	csfunc = pci.pci_devices[d].pd_csfunc;
338f3c0184aSmlarkin 	if (csfunc != NULL) {
339f3c0184aSmlarkin 		ret = csfunc(vei->vei.vei_dir, (o / 4), &vei->vei.vei_data);
340f3c0184aSmlarkin 		if (ret)
3417da934edSreyk 			log_warnx("cfg space access function failed for "
3427da934edSreyk 			    "pci device %d", d);
343f3c0184aSmlarkin 		return;
344f3c0184aSmlarkin 	}
345f3c0184aSmlarkin 
346f3c0184aSmlarkin 	/* No config space function, fallback to default simple r/w impl. */
347f3c0184aSmlarkin 
348eef1411cSmlarkin 	o += ofs;
349eef1411cSmlarkin 
350f3c0184aSmlarkin 	/*
3516ee12970Smlarkin 	 * vei_dir == VEI_DIR_OUT : out instruction
352f3c0184aSmlarkin 	 *
353f3c0184aSmlarkin 	 * The guest wrote to the config space location denoted by the current
354f3c0184aSmlarkin 	 * value in the address register.
355f3c0184aSmlarkin 	 */
3566ee12970Smlarkin 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
357f3c0184aSmlarkin 		if ((o >= 0x10 && o <= 0x24) &&
358f3c0184aSmlarkin 		    vei->vei.vei_data == 0xffffffff) {
359de6a9f31Smlarkin 			/*
360de6a9f31Smlarkin 			 * Compute BAR index:
361afd1d05cSmlarkin 			 * o = 0x10 -> baridx = 0
362afd1d05cSmlarkin 			 * o = 0x14 -> baridx = 1
363afd1d05cSmlarkin 			 * o = 0x18 -> baridx = 2
364afd1d05cSmlarkin 			 * o = 0x1c -> baridx = 3
365afd1d05cSmlarkin 			 * o = 0x20 -> baridx = 4
366afd1d05cSmlarkin 			 * o = 0x24 -> baridx = 5
367de6a9f31Smlarkin 			 */
368afd1d05cSmlarkin 			baridx = (o / 4) - 4;
369afd1d05cSmlarkin 			if (baridx < pci.pci_devices[d].pd_bar_ct)
370f3c0184aSmlarkin 				vei->vei.vei_data = 0xfffff000;
371de6a9f31Smlarkin 			else
372de6a9f31Smlarkin 				vei->vei.vei_data = 0;
373f3c0184aSmlarkin 		}
374eef1411cSmlarkin 
375afd1d05cSmlarkin 		/* IOBAR registers must have bit 0 set */
376afd1d05cSmlarkin 		if (o >= 0x10 && o <= 0x24) {
377afd1d05cSmlarkin 			baridx = (o / 4) - 4;
378afd1d05cSmlarkin 			if (baridx < pci.pci_devices[d].pd_bar_ct &&
379afd1d05cSmlarkin 			    pci.pci_devices[d].pd_bartype[baridx] ==
380afd1d05cSmlarkin 			    PCI_BAR_TYPE_IO)
381afd1d05cSmlarkin 				vei->vei.vei_data |= 1;
382afd1d05cSmlarkin 		}
383afd1d05cSmlarkin 
38481865e7dSmlarkin 		/*
38581865e7dSmlarkin 		 * Discard writes to "option rom base address" as none of our
386afd1d05cSmlarkin 		 * emulated devices have PCI option roms. Accept any other
387afd1d05cSmlarkin 		 * writes and copy data to config space registers.
38881865e7dSmlarkin 		 */
38981865e7dSmlarkin 		if (o != PCI_EXROMADDR_0)
39081865e7dSmlarkin 			get_input_data(vei,
39181865e7dSmlarkin 			    &pci.pci_devices[d].pd_cfg_space[o / 4]);
392f3c0184aSmlarkin 	} else {
393f3c0184aSmlarkin 		/*
3946ee12970Smlarkin 		 * vei_dir == VEI_DIR_IN : in instruction
395f3c0184aSmlarkin 		 *
396f3c0184aSmlarkin 		 * The guest read from the config space location determined by
397f3c0184aSmlarkin 		 * the current value in the address register.
398f3c0184aSmlarkin 		 */
399b966d91aSmlarkin 		if (d > pci.pci_dev_ct || b > 0 || f > 0)
400b966d91aSmlarkin 			set_return_data(vei, 0xFFFFFFFF);
401b966d91aSmlarkin 		else {
402b966d91aSmlarkin 			switch (sz) {
403b966d91aSmlarkin 			case 4:
404d2de69e7Sreyk 				set_return_data(vei,
405d2de69e7Sreyk 				    pci.pci_devices[d].pd_cfg_space[o / 4]);
406b966d91aSmlarkin 				break;
407b966d91aSmlarkin 			case 2:
408b966d91aSmlarkin 				if (ofs == 0)
409d2de69e7Sreyk 					set_return_data(vei, pci.pci_devices[d].
410d2de69e7Sreyk 					    pd_cfg_space[o / 4]);
411b966d91aSmlarkin 				else
412d2de69e7Sreyk 					set_return_data(vei, pci.pci_devices[d].
413d2de69e7Sreyk 					    pd_cfg_space[o / 4] >> 16);
414b966d91aSmlarkin 				break;
415b966d91aSmlarkin 			case 1:
416d2de69e7Sreyk 				set_return_data(vei, pci.pci_devices[d].
417d2de69e7Sreyk 				    pd_cfg_space[o / 4] >> (ofs * 8));
418b966d91aSmlarkin 				break;
419b966d91aSmlarkin 			}
420b966d91aSmlarkin 		}
421f3c0184aSmlarkin 	}
422f3c0184aSmlarkin }
423c4fd4c5bSdv #endif /* __amd64__ */
424813e3047Spd 
425813e3047Spd int
426813e3047Spd pci_dump(int fd)
427813e3047Spd {
428813e3047Spd 	log_debug("%s: sending pci", __func__);
429813e3047Spd 	if (atomicio(vwrite, fd, &pci, sizeof(pci)) != sizeof(pci)) {
430813e3047Spd 		log_warnx("%s: error writing pci to fd", __func__);
431813e3047Spd 		return (-1);
432813e3047Spd 	}
433813e3047Spd 	return (0);
434813e3047Spd }
435813e3047Spd 
436813e3047Spd int
437813e3047Spd pci_restore(int fd)
438813e3047Spd {
439813e3047Spd 	log_debug("%s: receiving pci", __func__);
440813e3047Spd 	if (atomicio(read, fd, &pci, sizeof(pci)) != sizeof(pci)) {
441813e3047Spd 		log_warnx("%s: error reading pci from fd", __func__);
442813e3047Spd 		return (-1);
443813e3047Spd 	}
444813e3047Spd 	return (0);
445813e3047Spd }
4461e8eabf2Sdv 
4471e8eabf2Sdv /*
4481e8eabf2Sdv  * Find the first PCI device based on PCI Subsystem ID
4491e8eabf2Sdv  * (e.g. PCI_PRODUCT_VIRTIO_BLOCK).
4501e8eabf2Sdv  *
4511e8eabf2Sdv  * Returns the PCI device id of the first matching device, if found.
4521e8eabf2Sdv  * Otherwise, returns -1.
4531e8eabf2Sdv  */
4541e8eabf2Sdv int
4551e8eabf2Sdv pci_find_first_device(uint16_t subsys_id)
4561e8eabf2Sdv {
4571e8eabf2Sdv 	int i;
4581e8eabf2Sdv 
4591e8eabf2Sdv 	for (i = 0; i < pci.pci_dev_ct; i++)
4601e8eabf2Sdv 		if (pci.pci_devices[i].pd_subsys_id == subsys_id)
4611e8eabf2Sdv 			return (i);
4621e8eabf2Sdv 	return (-1);
4631e8eabf2Sdv }
464