pci_passthru.c revision 246191
113546Sjulian/*-
2113658Sdeischen * Copyright (c) 2011 NetApp, Inc.
3113658Sdeischen * All rights reserved.
435509Sjb *
513546Sjulian * Redistribution and use in source and binary forms, with or without
613546Sjulian * modification, are permitted provided that the following conditions
713546Sjulian * are met:
813546Sjulian * 1. Redistributions of source code must retain the above copyright
913546Sjulian *    notice, this list of conditions and the following disclaimer.
1013546Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1113546Sjulian *    notice, this list of conditions and the following disclaimer in the
1213546Sjulian *    documentation and/or other materials provided with the distribution.
1313546Sjulian *
1413546Sjulian * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
1513546Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1613546Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1713546Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
1813546Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1913546Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2013546Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2113546Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2213546Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2313546Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2413546Sjulian * SUCH DAMAGE.
2544963Sjb *
2613546Sjulian * $FreeBSD: head/usr.sbin/bhyve/pci_passthru.c 246191 2013-02-01 03:49:09Z neel $
2713546Sjulian */
2813546Sjulian
2913546Sjulian#include <sys/cdefs.h>
3013546Sjulian__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_passthru.c 246191 2013-02-01 03:49:09Z neel $");
3113546Sjulian
3213546Sjulian#include <sys/param.h>
3313546Sjulian#include <sys/types.h>
3413546Sjulian#include <sys/pciio.h>
35113658Sdeischen#include <sys/ioctl.h>
36113662Sdeischen
37113658Sdeischen#include <dev/io/iodev.h>
38113658Sdeischen#include <dev/pci/pcireg.h>
39113658Sdeischen
40132120Sdavidxu#include <machine/iodev.h>
41113658Sdeischen
42113658Sdeischen#include <stdio.h>
43113658Sdeischen#include <stdlib.h>
44116977Sdavidxu#include <string.h>
45113658Sdeischen#include <errno.h>
46113658Sdeischen#include <fcntl.h>
47113870Sdeischen#include <unistd.h>
48113658Sdeischen
4913546Sjulian#include <machine/vmm.h>
5013546Sjulian#include <vmmapi.h>
51113658Sdeischen#include "pci_emul.h"
52113658Sdeischen#include "mem.h"
5313546Sjulian
54113658Sdeischen#ifndef _PATH_DEVPCI
55113658Sdeischen#define	_PATH_DEVPCI	"/dev/pci"
56103388Smini#endif
57113658Sdeischen
58150499Sbrian#ifndef	_PATH_DEVIO
59150499Sbrian#define	_PATH_DEVIO	"/dev/io"
60150499Sbrian#endif
6113546Sjulian
62139023Sdeischen#define	LEGACY_SUPPORT	1
6367097Sdeischen
6467097Sdeischen#define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1)
6567097Sdeischen#define MSIX_CAPLEN 12
6667097Sdeischen
6767097Sdeischenstatic int pcifd = -1;
6867097Sdeischenstatic int iofd = -1;
69113658Sdeischen
70113658Sdeischenstruct passthru_softc {
71113658Sdeischen	struct pci_devinst *psc_pi;
72113658Sdeischen	struct pcibar psc_bar[PCI_BARMAX + 1];
73113658Sdeischen	struct {
74113658Sdeischen		int		capoff;
75118676Sdavidxu		int		msgctrl;
76118676Sdavidxu		int		emulated;
77118676Sdavidxu	} psc_msi;
78118676Sdavidxu	struct {
79118676Sdavidxu		int		capoff;
80118676Sdavidxu	} psc_msix;
81133563Sdeischen	struct pcisel psc_sel;
82133563Sdeischen};
83118676Sdavidxu
84113658Sdeischenstatic int
85118510Sdeischenmsi_caplen(int msgctrl)
8613546Sjulian{
87113658Sdeischen	int len;
8848046Sjb
89113658Sdeischen	len = 10;		/* minimum length of msi capability */
90113658Sdeischen
91113658Sdeischen	if (msgctrl & PCIM_MSICTRL_64BIT)
92113658Sdeischen		len += 4;
93113658Sdeischen
94113658Sdeischen#if 0
95113658Sdeischen	/*
96113658Sdeischen	 * Ignore the 'mask' and 'pending' bits in the MSI capability.
97113658Sdeischen	 * We'll let the guest manipulate them directly.
98113658Sdeischen	 */
99113658Sdeischen	if (msgctrl & PCIM_MSICTRL_VECTOR)
100132120Sdavidxu		len += 10;
101132120Sdavidxu#endif
102132120Sdavidxu
103132120Sdavidxu	return (len);
104113658Sdeischen}
105114187Sdeischen
106113658Sdeischenstatic uint32_t
107120896Sdavidxuread_config(const struct pcisel *sel, long reg, int width)
108120896Sdavidxu{
109120896Sdavidxu	struct pci_io pi;
110120896Sdavidxu
111120896Sdavidxu	bzero(&pi, sizeof(pi));
112120896Sdavidxu	pi.pi_sel = *sel;
113120896Sdavidxu	pi.pi_reg = reg;
114120896Sdavidxu	pi.pi_width = width;
115120896Sdavidxu
116120896Sdavidxu	if (ioctl(pcifd, PCIOCREAD, &pi) < 0)
117120896Sdavidxu		return (0);				/* XXX */
118120896Sdavidxu	else
119113658Sdeischen		return (pi.pi_data);
120113658Sdeischen}
121113658Sdeischen
122113658Sdeischenstatic void
123113658Sdeischenwrite_config(const struct pcisel *sel, long reg, int width, uint32_t data)
124113658Sdeischen{
125113658Sdeischen	struct pci_io pi;
126113658Sdeischen
127113658Sdeischen	bzero(&pi, sizeof(pi));
128113661Sdeischen	pi.pi_sel = *sel;
129113658Sdeischen	pi.pi_reg = reg;
130113658Sdeischen	pi.pi_width = width;
131113658Sdeischen	pi.pi_data = data;
132113658Sdeischen
133113658Sdeischen	(void)ioctl(pcifd, PCIOCWRITE, &pi);		/* XXX */
134113658Sdeischen}
135113658Sdeischen
136113658Sdeischen#ifdef LEGACY_SUPPORT
137113658Sdeischenstatic int
138115278Sdeischenpassthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr)
139113658Sdeischen{
140117706Sdavidxu	int capoff, i;
141117706Sdavidxu	struct msicap msicap;
142117706Sdavidxu	u_char *capdata;
143117706Sdavidxu
144115278Sdeischen	pci_populate_msicap(&msicap, msgnum, nextptr);
145136846Sdavidxu
146136846Sdavidxu	/*
147136846Sdavidxu	 * XXX
148115173Sdeischen	 * Copy the msi capability structure in the last 16 bytes of the
149115173Sdeischen	 * config space. This is wrong because it could shadow something
150115173Sdeischen	 * useful to the device.
151113658Sdeischen	 */
152113658Sdeischen	capoff = 256 - roundup(sizeof(msicap), 4);
153113658Sdeischen	capdata = (u_char *)&msicap;
154117706Sdavidxu	for (i = 0; i < sizeof(msicap); i++)
155118510Sdeischen		pci_set_cfgdata8(pi, capoff + i, capdata[i]);
156118510Sdeischen
157113658Sdeischen	return (capoff);
158117706Sdavidxu}
159113661Sdeischen#endif	/* LEGACY_SUPPORT */
160118676Sdavidxu
161113870Sdeischenstatic int
162113658Sdeischencfginitmsi(struct passthru_softc *sc)
163113786Sdeischen{
164118676Sdavidxu	int i, ptr, capptr, cap, sts, caplen, table_size;
165113658Sdeischen	uint32_t u32;
166114187Sdeischen	struct pcisel sel;
167117907Sdeischen	struct pci_devinst *pi;
168113658Sdeischen	struct msixcap msixcap;
169115278Sdeischen	uint32_t *msixcap_ptr;
170116977Sdavidxu
171139023Sdeischen	pi = sc->psc_pi;
172113658Sdeischen	sel = sc->psc_sel;
173115278Sdeischen
174136846Sdavidxu	/*
175118676Sdavidxu	 * Parse the capabilities and cache the location of the MSI
176118676Sdavidxu	 * and MSI-X capabilities.
177118676Sdavidxu	 */
178113658Sdeischen	sts = read_config(&sel, PCIR_STATUS, 2);
179117715Sdeischen	if (sts & PCIM_STATUS_CAPPRESENT) {
180117715Sdeischen		ptr = read_config(&sel, PCIR_CAP_PTR, 1);
181117715Sdeischen		while (ptr != 0 && ptr != 0xff) {
182117715Sdeischen			cap = read_config(&sel, ptr + PCICAP_ID, 1);
183117715Sdeischen			if (cap == PCIY_MSI) {
184117715Sdeischen				/*
185118510Sdeischen				 * Copy the MSI capability into the config
186118510Sdeischen				 * space of the emulated pci device
187117715Sdeischen				 */
188117715Sdeischen				sc->psc_msi.capoff = ptr;
189117715Sdeischen				sc->psc_msi.msgctrl = read_config(&sel,
190117715Sdeischen								  ptr + 2, 2);
191118510Sdeischen				sc->psc_msi.emulated = 0;
192118510Sdeischen				caplen = msi_caplen(sc->psc_msi.msgctrl);
193117715Sdeischen				capptr = ptr;
194117715Sdeischen				while (caplen > 0) {
195113658Sdeischen					u32 = read_config(&sel, capptr, 4);
196113658Sdeischen					pci_set_cfgdata32(pi, capptr, u32);
197113658Sdeischen					caplen -= 4;
198113658Sdeischen					capptr += 4;
199116977Sdavidxu				}
200116977Sdavidxu			} else if (cap == PCIY_MSIX) {
201116977Sdavidxu				/*
202116977Sdavidxu				 * Copy the MSI-X capability
203116977Sdavidxu				 */
204116977Sdavidxu				sc->psc_msix.capoff = ptr;
205116977Sdavidxu				caplen = 12;
206123668Sdavidxu				msixcap_ptr = (uint32_t*) &msixcap;
207123668Sdavidxu				capptr = ptr;
208113658Sdeischen				while (caplen > 0) {
20913546Sjulian					u32 = read_config(&sel, capptr, 4);
210113658Sdeischen					*msixcap_ptr = u32;
21113546Sjulian					pci_set_cfgdata32(pi, capptr, u32);
212116977Sdavidxu					caplen -= 4;
213115278Sdeischen					capptr += 4;
214115278Sdeischen					msixcap_ptr++;
215115278Sdeischen				}
21671581Sdeischen			}
217150499Sbrian			ptr = read_config(&sel, ptr + PCICAP_NEXTPTR, 1);
218150499Sbrian		}
219118747Sdavidxu	}
220118747Sdavidxu
221118747Sdavidxu	if (sc->psc_msix.capoff != 0) {
222118747Sdavidxu		pi->pi_msix.pba_bar =
223118747Sdavidxu		    msixcap.pba_info & PCIM_MSIX_BIR_MASK;
22467097Sdeischen		pi->pi_msix.pba_offset =
225118747Sdavidxu		    msixcap.pba_info & ~PCIM_MSIX_BIR_MASK;
226118747Sdavidxu		pi->pi_msix.table_bar =
227118747Sdavidxu		    msixcap.table_info & PCIM_MSIX_BIR_MASK;
228153989Sbrian		pi->pi_msix.table_offset =
229132120Sdavidxu		    msixcap.table_info & ~PCIM_MSIX_BIR_MASK;
23013546Sjulian		pi->pi_msix.table_count = MSIX_TABLE_COUNT(msixcap.msgctrl);
231167241Sbrian
232167241Sbrian		/* Allocate the emulated MSI-X table array */
233167241Sbrian		table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE;
234167241Sbrian		pi->pi_msix.table = malloc(table_size);
235106191Smini		bzero(pi->pi_msix.table, table_size);
236113658Sdeischen
237113658Sdeischen		/* Mask all table entries */
238106191Smini		for (i = 0; i < pi->pi_msix.table_count; i++) {
239115278Sdeischen			pi->pi_msix.table[i].vector_control |=
240115278Sdeischen						PCIM_MSIX_VCTRL_MASK;
241106191Smini		}
242113658Sdeischen	}
243113658Sdeischen
244113658Sdeischen#ifdef LEGACY_SUPPORT
245106191Smini	/*
246113658Sdeischen	 * If the passthrough device does not support MSI then craft a
247113658Sdeischen	 * MSI capability for it. We link the new MSI capability at the
248113658Sdeischen	 * head of the list of capabilities.
249113658Sdeischen	 */
250113658Sdeischen	if ((sts & PCIM_STATUS_CAPPRESENT) != 0 && sc->psc_msi.capoff == 0) {
251113658Sdeischen		int origptr, msiptr;
252136846Sdavidxu		origptr = read_config(&sel, PCIR_CAP_PTR, 1);
253106191Smini		msiptr = passthru_add_msicap(pi, 1, origptr);
254106191Smini		sc->psc_msi.capoff = msiptr;
255106191Smini		sc->psc_msi.msgctrl = pci_get_cfgdata16(pi, msiptr + 2);
256113658Sdeischen		sc->psc_msi.emulated = 1;
257113658Sdeischen		pci_set_cfgdata8(pi, PCIR_CAP_PTR, msiptr);
258115278Sdeischen	}
259116977Sdavidxu#endif
260113658Sdeischen
261150499Sbrian	/* Make sure one of the capabilities is present */
262150499Sbrian	if (sc->psc_msi.capoff == 0 && sc->psc_msix.capoff == 0)
263113658Sdeischen		return (-1);
264113658Sdeischen	else
265113658Sdeischen		return (0);
266120074Sdavidxu}
267113658Sdeischen
268113658Sdeischenstatic uint64_t
269113658Sdeischenmsix_table_read(struct passthru_softc *sc, uint64_t offset, int size)
270113658Sdeischen{
271115278Sdeischen	struct pci_devinst *pi;
272113658Sdeischen	struct msix_table_entry *entry;
273120074Sdavidxu	uint8_t *src8;
274113658Sdeischen	uint16_t *src16;
275113658Sdeischen	uint32_t *src32;
276113658Sdeischen	uint64_t *src64;
277113658Sdeischen	uint64_t data;
278113658Sdeischen	size_t entry_offset;
279113658Sdeischen	int index;
280120074Sdavidxu
281113658Sdeischen	pi = sc->psc_pi;
282113658Sdeischen
283113658Sdeischen	index = offset / MSIX_TABLE_ENTRY_SIZE;
284113658Sdeischen	if (index >= pi->pi_msix.table_count)
285115278Sdeischen		return (-1);
286113658Sdeischen
287120074Sdavidxu	entry = &pi->pi_msix.table[index];
288113658Sdeischen	entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
289113658Sdeischen
290113658Sdeischen	switch(size) {
291113658Sdeischen	case 1:
292113658Sdeischen		src8 = (uint8_t *)((void *)entry + entry_offset);
293113658Sdeischen		data = *src8;
294136846Sdavidxu		break;
295113658Sdeischen	case 2:
296113658Sdeischen		src16 = (uint16_t *)((void *)entry + entry_offset);
297113658Sdeischen		data = *src16;
298113658Sdeischen		break;
299113658Sdeischen	case 4:
300113661Sdeischen		src32 = (uint32_t *)((void *)entry + entry_offset);
301136846Sdavidxu		data = *src32;
302113658Sdeischen		break;
303113661Sdeischen	case 8:
304113661Sdeischen		src64 = (uint64_t *)((void *)entry + entry_offset);
305113658Sdeischen		data = *src64;
306113658Sdeischen		break;
307113658Sdeischen	default:
308113658Sdeischen		return (-1);
309113658Sdeischen	}
310113658Sdeischen
311113658Sdeischen	return (data);
312113658Sdeischen}
313113658Sdeischen
314113658Sdeischenstatic void
315113658Sdeischenmsix_table_write(struct vmctx *ctx, int vcpu, struct passthru_softc *sc,
316113658Sdeischen		 uint64_t offset, int size, uint64_t data)
317150499Sbrian{
318150499Sbrian	struct pci_devinst *pi;
319150499Sbrian	struct msix_table_entry *entry;
320113658Sdeischen	uint32_t *dest;
321113658Sdeischen	size_t entry_offset;
322113658Sdeischen	uint32_t vector_control;
323113658Sdeischen	int error, index;
324113658Sdeischen
325113658Sdeischen	pi = sc->psc_pi;
326113658Sdeischen	index = offset / MSIX_TABLE_ENTRY_SIZE;
327113658Sdeischen	if (index >= pi->pi_msix.table_count)
328113658Sdeischen		return;
329113658Sdeischen
330113658Sdeischen	entry = &pi->pi_msix.table[index];
331116977Sdavidxu	entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
332122075Sdeischen
333122075Sdeischen	/* Only 4 byte naturally-aligned writes are supported */
334122075Sdeischen	assert(size == 4);
335122075Sdeischen	assert(entry_offset % 4 == 0);
336122075Sdeischen
337122075Sdeischen	vector_control = entry->vector_control;
338122075Sdeischen	dest = (uint32_t *)((void *)entry + entry_offset);
339122075Sdeischen	*dest = data;
340122075Sdeischen	/* If MSI-X hasn't been enabled, do nothing */
341122075Sdeischen	if (pi->pi_msix.enabled) {
342122075Sdeischen		/* If the entry is masked, don't set it up */
343122075Sdeischen		if ((entry->vector_control & PCIM_MSIX_VCTRL_MASK) == 0 ||
344122075Sdeischen		    (vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
345122075Sdeischen			error = vm_setup_msix(ctx, vcpu, sc->psc_sel.pc_bus,
346118747Sdavidxu					      sc->psc_sel.pc_dev,
347118747Sdavidxu					      sc->psc_sel.pc_func,
348117345Sdavidxu					      index, entry->msg_data,
349118747Sdavidxu					      entry->vector_control,
350118747Sdavidxu					      entry->addr);
351128041Sdeischen		}
352128041Sdeischen	}
353128041Sdeischen}
354139023Sdeischen
355139023Sdeischenstatic int
356139023Sdeischeninit_msix_table(struct vmctx *ctx, struct passthru_softc *sc, uint64_t base)
357118747Sdavidxu{
358118747Sdavidxu	int b, s, f;
359118747Sdavidxu	int error, idx;
360118747Sdavidxu	size_t len, remaining, table_size;
361118747Sdavidxu	vm_paddr_t start;
362132120Sdavidxu	struct pci_devinst *pi = sc->psc_pi;
363116977Sdavidxu
364113658Sdeischen	assert(pci_msix_table_bar(pi) >= 0 && pci_msix_pba_bar(pi) >= 0);
365113658Sdeischen
366113658Sdeischen	b = sc->psc_sel.pc_bus;
367113658Sdeischen	s = sc->psc_sel.pc_dev;
368113658Sdeischen	f = sc->psc_sel.pc_func;
369113658Sdeischen
370113658Sdeischen	/*
371113658Sdeischen	 * If the MSI-X table BAR maps memory intended for
372113658Sdeischen	 * other uses, it is at least assured that the table
373113658Sdeischen	 * either resides in its own page within the region,
374113658Sdeischen	 * or it resides in a page shared with only the PBA.
375113658Sdeischen	 */
376113658Sdeischen	if (pi->pi_msix.pba_bar == pi->pi_msix.table_bar &&
377113658Sdeischen	    ((pi->pi_msix.pba_offset - pi->pi_msix.table_offset) < 4096)) {
378113658Sdeischen		/* Need to also emulate the PBA, not supported yet */
379113661Sdeischen		printf("Unsupported MSI-X configuration: %d/%d/%d\n", b, s, f);
380113658Sdeischen		return (-1);
381113658Sdeischen	}
382113658Sdeischen
383113658Sdeischen	/* Compute the MSI-X table size */
384113658Sdeischen	table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE;
385113658Sdeischen	table_size = roundup2(table_size, 4096);
386113658Sdeischen
387113658Sdeischen	idx = pi->pi_msix.table_bar;
388113658Sdeischen	start = pi->pi_bar[idx].addr;
389136846Sdavidxu	remaining = pi->pi_bar[idx].size;
390113658Sdeischen
391113658Sdeischen	/* Map everything before the MSI-X table */
392113661Sdeischen	if (pi->pi_msix.table_offset > 0) {
393113658Sdeischen		len = pi->pi_msix.table_offset;
394113658Sdeischen		error = vm_map_pptdev_mmio(ctx, b, s, f, start, len, base);
395113658Sdeischen		if (error)
396113658Sdeischen			return (error);
397113658Sdeischen
398113658Sdeischen		base += len;
399113658Sdeischen		start += len;
400113658Sdeischen		remaining -= len;
401113786Sdeischen	}
402113658Sdeischen
403113658Sdeischen	/* Skip the MSI-X table */
404118747Sdavidxu	base += table_size;
405118747Sdavidxu	start += table_size;
406113658Sdeischen	remaining -= table_size;
407119063Sdavidxu
408119063Sdavidxu	/* Map everything beyond the end of the MSI-X table */
409119063Sdavidxu	if (remaining > 0) {
410106191Smini		len = remaining;
411113658Sdeischen		error = vm_map_pptdev_mmio(ctx, b, s, f, start, len, base);
412113658Sdeischen		if (error)
413113658Sdeischen			return (error);
414113786Sdeischen	}
415117706Sdavidxu
416133563Sdeischen	return (0);
417133269Sdeischen}
418133269Sdeischen
419133269Sdeischenstatic int
420133269Sdeischencfginitbar(struct vmctx *ctx, struct passthru_softc *sc)
421133269Sdeischen{
422133269Sdeischen	int i, error;
423133269Sdeischen	struct pci_devinst *pi;
424133269Sdeischen	struct pci_bar_io bar;
425133269Sdeischen	enum pcibar_type bartype;
426133269Sdeischen	uint64_t base;
427133269Sdeischen
428133269Sdeischen	pi = sc->psc_pi;
429133269Sdeischen
430117706Sdavidxu	/*
431118747Sdavidxu	 * Initialize BAR registers
432118747Sdavidxu	 */
433118747Sdavidxu	for (i = 0; i <= PCI_BARMAX; i++) {
434118747Sdavidxu		bzero(&bar, sizeof(bar));
435118747Sdavidxu		bar.pbi_sel = sc->psc_sel;
436118747Sdavidxu		bar.pbi_reg = PCIR_BAR(i);
437118747Sdavidxu
438118510Sdeischen		if (ioctl(pcifd, PCIOCGETBAR, &bar) < 0)
439113786Sdeischen			continue;
440114187Sdeischen
441116977Sdavidxu		if (PCI_BAR_IO(bar.pbi_base)) {
442113786Sdeischen			bartype = PCIBAR_IO;
443113786Sdeischen			base = bar.pbi_base & PCIM_BAR_IO_BASE;
444132120Sdavidxu		} else {
445132120Sdavidxu			switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) {
446132120Sdavidxu			case PCIM_BAR_MEM_64:
447117706Sdavidxu				bartype = PCIBAR_MEM64;
448135714Sssouhlal				break;
449133563Sdeischen			default:
450133269Sdeischen				bartype = PCIBAR_MEM32;
451133269Sdeischen				break;
452133269Sdeischen			}
453133269Sdeischen			base = bar.pbi_base & PCIM_BAR_MEM_BASE;
454133269Sdeischen		}
455133269Sdeischen
456135714Sssouhlal		/* Cache information about the "real" BAR */
457135714Sssouhlal		sc->psc_bar[i].type = bartype;
458133269Sdeischen		sc->psc_bar[i].size = bar.pbi_length;
459133269Sdeischen		sc->psc_bar[i].addr = base;
460113658Sdeischen
461113786Sdeischen		/* Allocate the BAR in the guest I/O or MMIO space */
462113658Sdeischen		error = pci_emul_alloc_pbar(pi, i, base, bartype,
463113658Sdeischen					    bar.pbi_length);
464113658Sdeischen		if (error)
465113658Sdeischen			return (-1);
466113658Sdeischen
467113658Sdeischen		/* The MSI-X table needs special handling */
468113658Sdeischen		if (i == pci_msix_table_bar(pi)) {
469113658Sdeischen			error = init_msix_table(ctx, sc, base);
470113658Sdeischen			if (error)
471113658Sdeischen				return (-1);
472113658Sdeischen		} else if (bartype != PCIBAR_IO) {
473113658Sdeischen			/* Map the physical MMIO space in the guest MMIO space */
474113658Sdeischen			error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
475113658Sdeischen				sc->psc_sel.pc_dev, sc->psc_sel.pc_func,
476113658Sdeischen				pi->pi_bar[i].addr, pi->pi_bar[i].size, base);
477113658Sdeischen			if (error)
478113658Sdeischen				return (-1);
479113658Sdeischen		}
480113658Sdeischen
481113786Sdeischen		/*
482113658Sdeischen		 * 64-bit BAR takes up two slots so skip the next one.
483118510Sdeischen		 */
484113786Sdeischen		if (bartype == PCIBAR_MEM64) {
485113658Sdeischen			i++;
486113658Sdeischen			assert(i <= PCI_BARMAX);
487113658Sdeischen			sc->psc_bar[i].type = PCIBAR_MEMHI64;
488113658Sdeischen		}
489113658Sdeischen	}
490115278Sdeischen	return (0);
491113658Sdeischen}
492113658Sdeischen
493113658Sdeischenstatic int
494113658Sdeischencfginit(struct vmctx *ctx, struct pci_devinst *pi, int bus, int slot, int func)
495118510Sdeischen{
496118510Sdeischen	int error;
497118510Sdeischen	struct passthru_softc *sc;
498113786Sdeischen
499118510Sdeischen	error = 1;
500113658Sdeischen	sc = pi->pi_arg;
501113658Sdeischen
502113658Sdeischen	bzero(&sc->psc_sel, sizeof(struct pcisel));
503113658Sdeischen	sc->psc_sel.pc_bus = bus;
504113658Sdeischen	sc->psc_sel.pc_dev = slot;
505113658Sdeischen	sc->psc_sel.pc_func = func;
506113658Sdeischen
507113658Sdeischen	if (cfginitmsi(sc) != 0)
508115080Sdeischen		goto done;
509113658Sdeischen
510113658Sdeischen	if (cfginitbar(ctx, sc) != 0)
511113658Sdeischen		goto done;
512113658Sdeischen
513113658Sdeischen	error = 0;				/* success */
514113658Sdeischendone:
515115080Sdeischen	return (error);
516118510Sdeischen}
517115080Sdeischen
518113658Sdeischenstatic int
519113658Sdeischenpassthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
520115080Sdeischen{
521115080Sdeischen	int bus, slot, func, error;
522115080Sdeischen	struct passthru_softc *sc;
523113658Sdeischen
524115080Sdeischen	sc = NULL;
525113658Sdeischen	error = 1;
526113658Sdeischen
527113658Sdeischen	if (pcifd < 0) {
528113658Sdeischen		pcifd = open(_PATH_DEVPCI, O_RDWR, 0);
529113658Sdeischen		if (pcifd < 0)
530113658Sdeischen			goto done;
531113658Sdeischen	}
532113658Sdeischen
533113658Sdeischen	if (iofd < 0) {
534113658Sdeischen		iofd = open(_PATH_DEVIO, O_RDWR, 0);
535113658Sdeischen		if (iofd < 0)
536113658Sdeischen			goto done;
537113658Sdeischen	}
538115080Sdeischen
539117907Sdeischen	if (opts == NULL ||
540115080Sdeischen	    sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3)
541117907Sdeischen		goto done;
542115278Sdeischen
543113658Sdeischen	if (vm_assign_pptdev(ctx, bus, slot, func) != 0)
544106191Smini		goto done;
545113658Sdeischen
546113658Sdeischen	sc = malloc(sizeof(struct passthru_softc));
547113658Sdeischen	memset(sc, 0, sizeof(struct passthru_softc));
548113658Sdeischen
549113658Sdeischen	pi->pi_arg = sc;
550117907Sdeischen	sc->psc_pi = pi;
551113658Sdeischen
552113658Sdeischen	/* initialize config space */
553113658Sdeischen	if ((error = cfginit(ctx, pi, bus, slot, func)) != 0)
554113658Sdeischen		goto done;
555115080Sdeischen
556115080Sdeischen	error = 0;		/* success */
557117907Sdeischendone:
558115080Sdeischen	if (error) {
559117907Sdeischen		free(sc);
560117907Sdeischen		vm_unassign_pptdev(ctx, bus, slot, func);
561113658Sdeischen	}
562113658Sdeischen	return (error);
563113658Sdeischen}
564113658Sdeischen
565113658Sdeischenstatic int
566113658Sdeischenbar_access(int coff)
567113658Sdeischen{
568118510Sdeischen	if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1))
569113658Sdeischen		return (1);
570113658Sdeischen	else
571113658Sdeischen		return (0);
572113658Sdeischen}
573113658Sdeischen
574113658Sdeischenstatic int
575113658Sdeischenmsicap_access(struct passthru_softc *sc, int coff)
576113658Sdeischen{
577118510Sdeischen	int caplen;
578113658Sdeischen
579113658Sdeischen	if (sc->psc_msi.capoff == 0)
580113658Sdeischen		return (0);
581113658Sdeischen
582113942Sdeischen	caplen = msi_caplen(sc->psc_msi.msgctrl);
583113942Sdeischen
584113942Sdeischen	if (coff >= sc->psc_msi.capoff && coff < sc->psc_msi.capoff + caplen)
585118510Sdeischen		return (1);
586113942Sdeischen	else
587113942Sdeischen		return (0);
588113658Sdeischen}
589113658Sdeischen
590113658Sdeischenstatic int
591113658Sdeischenmsixcap_access(struct passthru_softc *sc, int coff)
592113658Sdeischen{
593113658Sdeischen	if (sc->psc_msix.capoff == 0)
594113658Sdeischen		return (0);
595113658Sdeischen
596113658Sdeischen	return (coff >= sc->psc_msix.capoff &&
597113658Sdeischen	        coff < sc->psc_msix.capoff + MSIX_CAPLEN);
598113658Sdeischen}
599113658Sdeischen
600113658Sdeischenstatic int
601115080Sdeischenpassthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
602115080Sdeischen		 int coff, int bytes, uint32_t *rv)
603115080Sdeischen{
604115080Sdeischen	struct passthru_softc *sc;
605115080Sdeischen
606115080Sdeischen	sc = pi->pi_arg;
607115080Sdeischen
608115080Sdeischen	/*
609115080Sdeischen	 * PCI BARs and MSI capability is emulated.
610115080Sdeischen	 */
611115080Sdeischen	if (bar_access(coff) || msicap_access(sc, coff))
612113658Sdeischen		return (-1);
613113658Sdeischen
614113658Sdeischen#ifdef LEGACY_SUPPORT
615113658Sdeischen	/*
616113658Sdeischen	 * Emulate PCIR_CAP_PTR if this device does not support MSI capability
617113658Sdeischen	 * natively.
618115080Sdeischen	 */
619113658Sdeischen	if (sc->psc_msi.emulated) {
620113658Sdeischen		if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4)
621115080Sdeischen			return (-1);
622123048Sdavidxu	}
623113658Sdeischen#endif
624113658Sdeischen
625139023Sdeischen	/* Everything else just read from the device's config space */
626113658Sdeischen	*rv = read_config(&sc->psc_sel, coff, bytes);
627113658Sdeischen
628113658Sdeischen	return (0);
629113658Sdeischen}
630115080Sdeischen
631115080Sdeischenstatic int
632115080Sdeischenpassthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
633117706Sdavidxu		  int coff, int bytes, uint32_t val)
634118510Sdeischen{
635123048Sdavidxu	int error, msix_table_entries, i;
636132120Sdavidxu	struct passthru_softc *sc;
637132120Sdavidxu
638132120Sdavidxu	sc = pi->pi_arg;
639132120Sdavidxu
640132120Sdavidxu	/*
641132120Sdavidxu	 * PCI BARs are emulated
642132120Sdavidxu	 */
643132120Sdavidxu	if (bar_access(coff))
644132120Sdavidxu		return (-1);
645132120Sdavidxu
646132120Sdavidxu	/*
647132120Sdavidxu	 * MSI capability is emulated
648132120Sdavidxu	 */
649123312Sdavidxu	if (msicap_access(sc, coff)) {
650118510Sdeischen		msicap_cfgwrite(pi, sc->psc_msi.capoff, coff, bytes, val);
651116977Sdavidxu
652123048Sdavidxu		error = vm_setup_msi(ctx, vcpu, sc->psc_sel.pc_bus,
653123048Sdavidxu			sc->psc_sel.pc_dev, sc->psc_sel.pc_func, pi->pi_msi.cpu,
654123048Sdavidxu			pi->pi_msi.vector, pi->pi_msi.msgnum);
655123048Sdavidxu		if (error != 0) {
656123048Sdavidxu			printf("vm_setup_msi returned error %d\r\n", errno);
657123048Sdavidxu			exit(1);
658139023Sdeischen		}
659115080Sdeischen		return (0);
660123048Sdavidxu	}
661123048Sdavidxu
662123048Sdavidxu	if (msixcap_access(sc, coff)) {
663115080Sdeischen		msixcap_cfgwrite(pi, sc->psc_msix.capoff, coff, bytes, val);
664113658Sdeischen		if (pi->pi_msix.enabled) {
665113658Sdeischen			msix_table_entries = pi->pi_msix.table_count;
666113658Sdeischen			for (i = 0; i < msix_table_entries; i++) {
667139023Sdeischen				error = vm_setup_msix(ctx, vcpu, sc->psc_sel.pc_bus,
668123048Sdavidxu						      sc->psc_sel.pc_dev,
669116977Sdavidxu						      sc->psc_sel.pc_func, i,
670123048Sdavidxu						      pi->pi_msix.table[i].msg_data,
671116977Sdavidxu						      pi->pi_msix.table[i].vector_control,
672116977Sdavidxu						      pi->pi_msix.table[i].addr);
673116977Sdavidxu
674139023Sdeischen				if (error) {
675116977Sdavidxu					printf("vm_setup_msix returned error %d\r\n", errno);
676113658Sdeischen					exit(1);
677115080Sdeischen				}
678113658Sdeischen			}
679113658Sdeischen		}
680113658Sdeischen		return (0);
681113658Sdeischen	}
682113658Sdeischen
683113658Sdeischen#ifdef LEGACY_SUPPORT
684113658Sdeischen	/*
685113658Sdeischen	 * If this device does not support MSI natively then we cannot let
686113658Sdeischen	 * the guest disable legacy interrupts from the device. It is the
687117706Sdavidxu	 * legacy interrupt that is triggering the virtual MSI to the guest.
688113658Sdeischen	 */
689118510Sdeischen	if (sc->psc_msi.emulated && pci_msi_enabled(pi)) {
690113658Sdeischen		if (coff == PCIR_COMMAND && bytes == 2)
691118510Sdeischen			val &= ~PCIM_CMD_INTxDIS;
692118510Sdeischen	}
693113658Sdeischen#endif
694117706Sdavidxu
695117706Sdavidxu	write_config(&sc->psc_sel, coff, bytes, val);
696113658Sdeischen
697118510Sdeischen	return (0);
698118510Sdeischen}
699118510Sdeischen
700123312Sdavidxustatic void
701117706Sdavidxupassthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
702118510Sdeischen	       uint64_t offset, int size, uint64_t value)
703118510Sdeischen{
704117706Sdavidxu	struct passthru_softc *sc;
705117706Sdavidxu	struct iodev_pio_req pio;
706117706Sdavidxu
707132120Sdavidxu	sc = pi->pi_arg;
708117706Sdavidxu
709117706Sdavidxu	if (baridx == pci_msix_table_bar(pi)) {
710117706Sdavidxu		msix_table_write(ctx, vcpu, sc, offset, size, value);
711117706Sdavidxu	} else {
712117706Sdavidxu		assert(pi->pi_bar[baridx].type == PCIBAR_IO);
713117706Sdavidxu		bzero(&pio, sizeof(struct iodev_pio_req));
714117706Sdavidxu		pio.access = IODEV_PIO_WRITE;
715118510Sdeischen		pio.port = sc->psc_bar[baridx].addr + offset;
716118850Sdavidxu		pio.width = size;
717118850Sdavidxu		pio.val = value;
718118850Sdavidxu
719118850Sdavidxu		(void)ioctl(iofd, IODEV_PIO, &pio);
720118850Sdavidxu	}
721118850Sdavidxu}
722118850Sdavidxu
723118850Sdavidxustatic uint64_t
724106191Sminipassthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
725113786Sdeischen	      uint64_t offset, int size)
726113786Sdeischen{
727113658Sdeischen	struct passthru_softc *sc;
728113786Sdeischen	struct iodev_pio_req pio;
729113786Sdeischen	uint64_t val;
730113786Sdeischen
731113786Sdeischen	sc = pi->pi_arg;
732113786Sdeischen
733113786Sdeischen	if (baridx == pci_msix_table_bar(pi)) {
734117706Sdavidxu		val = msix_table_read(sc, offset, size);
735117706Sdavidxu	} else {
736113786Sdeischen		assert(pi->pi_bar[baridx].type == PCIBAR_IO);
737113786Sdeischen		bzero(&pio, sizeof(struct iodev_pio_req));
738113786Sdeischen		pio.access = IODEV_PIO_READ;
739113786Sdeischen		pio.port = sc->psc_bar[baridx].addr + offset;
740113786Sdeischen		pio.width = size;
741113786Sdeischen		pio.val = 0;
742113786Sdeischen
743120896Sdavidxu		(void)ioctl(iofd, IODEV_PIO, &pio);
744113786Sdeischen
745120896Sdavidxu		val = pio.val;
746120896Sdavidxu	}
747120896Sdavidxu
748120896Sdavidxu	return (val);
749120896Sdavidxu}
750113786Sdeischen
751113786Sdeischenstruct pci_devemu passthru = {
752113786Sdeischen	.pe_emu		= "passthru",
753117706Sdavidxu	.pe_init	= passthru_init,
754117706Sdavidxu	.pe_cfgwrite	= passthru_cfgwrite,
755117706Sdavidxu	.pe_cfgread	= passthru_cfgread,
756117706Sdavidxu	.pe_barwrite 	= passthru_write,
757117706Sdavidxu	.pe_barread    	= passthru_read,
758113786Sdeischen};
759117706SdavidxuPCI_EMUL_SET(passthru);
760113786Sdeischen