xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/ppt.c (revision 32640292)
1eb9a1df2SHans Rosenfeld /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
3eb9a1df2SHans Rosenfeld  *
4eb9a1df2SHans Rosenfeld  * Copyright (c) 2011 NetApp, Inc.
5eb9a1df2SHans Rosenfeld  * All rights reserved.
6eb9a1df2SHans Rosenfeld  *
7eb9a1df2SHans Rosenfeld  * Redistribution and use in source and binary forms, with or without
8eb9a1df2SHans Rosenfeld  * modification, are permitted provided that the following conditions
9eb9a1df2SHans Rosenfeld  * are met:
10eb9a1df2SHans Rosenfeld  * 1. Redistributions of source code must retain the above copyright
11eb9a1df2SHans Rosenfeld  *    notice, this list of conditions and the following disclaimer.
12eb9a1df2SHans Rosenfeld  * 2. Redistributions in binary form must reproduce the above copyright
13eb9a1df2SHans Rosenfeld  *    notice, this list of conditions and the following disclaimer in the
14eb9a1df2SHans Rosenfeld  *    documentation and/or other materials provided with the distribution.
15eb9a1df2SHans Rosenfeld  *
16eb9a1df2SHans Rosenfeld  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
17eb9a1df2SHans Rosenfeld  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18eb9a1df2SHans Rosenfeld  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19eb9a1df2SHans Rosenfeld  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
20eb9a1df2SHans Rosenfeld  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21eb9a1df2SHans Rosenfeld  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22eb9a1df2SHans Rosenfeld  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23eb9a1df2SHans Rosenfeld  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24eb9a1df2SHans Rosenfeld  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25eb9a1df2SHans Rosenfeld  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26eb9a1df2SHans Rosenfeld  * SUCH DAMAGE.
27eb9a1df2SHans Rosenfeld  */
28eb9a1df2SHans Rosenfeld 
29eb9a1df2SHans Rosenfeld /*
30eb9a1df2SHans Rosenfeld  * Copyright 2019 Joyent, Inc.
317ca35597SAndy Fiddaman  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
32eb9a1df2SHans Rosenfeld  */
33eb9a1df2SHans Rosenfeld 
34eb9a1df2SHans Rosenfeld #include <sys/cdefs.h>
35eb9a1df2SHans Rosenfeld 
36eb9a1df2SHans Rosenfeld #include <sys/param.h>
37eb9a1df2SHans Rosenfeld #include <sys/systm.h>
38eb9a1df2SHans Rosenfeld #include <sys/kernel.h>
398130f8e1SPatrick Mooney #include <sys/kmem.h>
40eb9a1df2SHans Rosenfeld #include <sys/module.h>
41eb9a1df2SHans Rosenfeld #include <sys/bus.h>
42eb9a1df2SHans Rosenfeld #include <sys/pciio.h>
43eb9a1df2SHans Rosenfeld #include <sys/sysctl.h>
44eb9a1df2SHans Rosenfeld 
45eb9a1df2SHans Rosenfeld #include <dev/pci/pcivar.h>
46eb9a1df2SHans Rosenfeld #include <dev/pci/pcireg.h>
47eb9a1df2SHans Rosenfeld 
48eb9a1df2SHans Rosenfeld #include <machine/vmm.h>
49eb9a1df2SHans Rosenfeld #include <machine/vmm_dev.h>
50eb9a1df2SHans Rosenfeld 
51eb9a1df2SHans Rosenfeld #include <sys/conf.h>
52eb9a1df2SHans Rosenfeld #include <sys/ddi.h>
53eb9a1df2SHans Rosenfeld #include <sys/stat.h>
54eb9a1df2SHans Rosenfeld #include <sys/sunddi.h>
55eb9a1df2SHans Rosenfeld #include <sys/pci.h>
56eb9a1df2SHans Rosenfeld #include <sys/pci_cap.h>
57eb9a1df2SHans Rosenfeld #include <sys/pcie_impl.h>
58eb9a1df2SHans Rosenfeld #include <sys/ppt_dev.h>
59eb9a1df2SHans Rosenfeld #include <sys/mkdev.h>
60eb9a1df2SHans Rosenfeld #include <sys/sysmacros.h>
61eb9a1df2SHans Rosenfeld 
62eb9a1df2SHans Rosenfeld #include "vmm_lapic.h"
63eb9a1df2SHans Rosenfeld 
64eb9a1df2SHans Rosenfeld #include "iommu.h"
65eb9a1df2SHans Rosenfeld #include "ppt.h"
66eb9a1df2SHans Rosenfeld 
67eb9a1df2SHans Rosenfeld #define	MAX_MSIMSGS	32
68eb9a1df2SHans Rosenfeld 
69eb9a1df2SHans Rosenfeld /*
70eb9a1df2SHans Rosenfeld  * If the MSI-X table is located in the middle of a BAR then that MMIO
71eb9a1df2SHans Rosenfeld  * region gets split into two segments - one segment above the MSI-X table
72eb9a1df2SHans Rosenfeld  * and the other segment below the MSI-X table - with a hole in place of
73eb9a1df2SHans Rosenfeld  * the MSI-X table so accesses to it can be trapped and emulated.
74eb9a1df2SHans Rosenfeld  *
75eb9a1df2SHans Rosenfeld  * So, allocate a MMIO segment for each BAR register + 1 additional segment.
76eb9a1df2SHans Rosenfeld  */
77eb9a1df2SHans Rosenfeld #define	MAX_MMIOSEGS	((PCIR_MAX_BAR_0 + 1) + 1)
78eb9a1df2SHans Rosenfeld 
79eb9a1df2SHans Rosenfeld struct pptintr_arg {
80eb9a1df2SHans Rosenfeld 	struct pptdev	*pptdev;
81eb9a1df2SHans Rosenfeld 	uint64_t	addr;
82eb9a1df2SHans Rosenfeld 	uint64_t	msg_data;
83eb9a1df2SHans Rosenfeld };
84eb9a1df2SHans Rosenfeld 
85eb9a1df2SHans Rosenfeld struct pptseg {
86eb9a1df2SHans Rosenfeld 	vm_paddr_t	gpa;
87eb9a1df2SHans Rosenfeld 	size_t		len;
88eb9a1df2SHans Rosenfeld 	int		wired;
89eb9a1df2SHans Rosenfeld };
90eb9a1df2SHans Rosenfeld 
91eb9a1df2SHans Rosenfeld struct pptbar {
92eb9a1df2SHans Rosenfeld 	uint64_t base;
93eb9a1df2SHans Rosenfeld 	uint64_t size;
94eb9a1df2SHans Rosenfeld 	uint_t type;
95eb9a1df2SHans Rosenfeld 	ddi_acc_handle_t io_handle;
96eb9a1df2SHans Rosenfeld 	caddr_t io_ptr;
977ca35597SAndy Fiddaman 	uint_t ddireg;
98eb9a1df2SHans Rosenfeld };
99eb9a1df2SHans Rosenfeld 
100eb9a1df2SHans Rosenfeld struct pptdev {
101eb9a1df2SHans Rosenfeld 	dev_info_t		*pptd_dip;
102eb9a1df2SHans Rosenfeld 	list_node_t		pptd_node;
103eb9a1df2SHans Rosenfeld 	ddi_acc_handle_t	pptd_cfg;
104eb9a1df2SHans Rosenfeld 	struct pptbar		pptd_bars[PCI_BASE_NUM];
105eb9a1df2SHans Rosenfeld 	struct vm		*vm;
106eb9a1df2SHans Rosenfeld 	struct pptseg mmio[MAX_MMIOSEGS];
107eb9a1df2SHans Rosenfeld 	struct {
108eb9a1df2SHans Rosenfeld 		int	num_msgs;		/* guest state */
109eb9a1df2SHans Rosenfeld 		boolean_t is_fixed;
110eb9a1df2SHans Rosenfeld 		size_t	inth_sz;
111eb9a1df2SHans Rosenfeld 		ddi_intr_handle_t *inth;
112eb9a1df2SHans Rosenfeld 		struct pptintr_arg arg[MAX_MSIMSGS];
113eb9a1df2SHans Rosenfeld 	} msi;
114eb9a1df2SHans Rosenfeld 
115eb9a1df2SHans Rosenfeld 	struct {
116eb9a1df2SHans Rosenfeld 		int num_msgs;
117eb9a1df2SHans Rosenfeld 		size_t inth_sz;
118eb9a1df2SHans Rosenfeld 		size_t arg_sz;
119eb9a1df2SHans Rosenfeld 		ddi_intr_handle_t *inth;
120eb9a1df2SHans Rosenfeld 		struct pptintr_arg *arg;
121eb9a1df2SHans Rosenfeld 	} msix;
122eb9a1df2SHans Rosenfeld };
123eb9a1df2SHans Rosenfeld 
124eb9a1df2SHans Rosenfeld 
125eb9a1df2SHans Rosenfeld static major_t		ppt_major;
126eb9a1df2SHans Rosenfeld static void		*ppt_state;
127eb9a1df2SHans Rosenfeld static kmutex_t		pptdev_mtx;
128eb9a1df2SHans Rosenfeld static list_t		pptdev_list;
129eb9a1df2SHans Rosenfeld 
130eb9a1df2SHans Rosenfeld #define	PPT_MINOR_NAME	"ppt"
131eb9a1df2SHans Rosenfeld 
132eb9a1df2SHans Rosenfeld static ddi_device_acc_attr_t ppt_attr = {
133eb9a1df2SHans Rosenfeld 	DDI_DEVICE_ATTR_V0,
134eb9a1df2SHans Rosenfeld 	DDI_NEVERSWAP_ACC,
135eb9a1df2SHans Rosenfeld 	DDI_STORECACHING_OK_ACC,
136eb9a1df2SHans Rosenfeld 	DDI_DEFAULT_ACC
137eb9a1df2SHans Rosenfeld };
138eb9a1df2SHans Rosenfeld 
139eb9a1df2SHans Rosenfeld static int
ppt_open(dev_t * devp,int flag,int otyp,cred_t * cr)140eb9a1df2SHans Rosenfeld ppt_open(dev_t *devp, int flag, int otyp, cred_t *cr)
141eb9a1df2SHans Rosenfeld {
142eb9a1df2SHans Rosenfeld 	/* XXX: require extra privs? */
143eb9a1df2SHans Rosenfeld 	return (0);
144eb9a1df2SHans Rosenfeld }
145eb9a1df2SHans Rosenfeld 
146eb9a1df2SHans Rosenfeld #define	BAR_TO_IDX(bar)	(((bar) - PCI_CONF_BASE0) / PCI_BAR_SZ_32)
147eb9a1df2SHans Rosenfeld #define	BAR_VALID(b)	(			\
148eb9a1df2SHans Rosenfeld 		(b) >= PCI_CONF_BASE0 &&	\
149eb9a1df2SHans Rosenfeld 		(b) <= PCI_CONF_BASE5 &&	\
150eb9a1df2SHans Rosenfeld 		((b) & (PCI_BAR_SZ_32-1)) == 0)
151eb9a1df2SHans Rosenfeld 
152eb9a1df2SHans Rosenfeld static int
ppt_ioctl(dev_t dev,int cmd,intptr_t arg,int md,cred_t * cr,int * rv)153eb9a1df2SHans Rosenfeld ppt_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
154eb9a1df2SHans Rosenfeld {
155eb9a1df2SHans Rosenfeld 	minor_t minor = getminor(dev);
156eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
157eb9a1df2SHans Rosenfeld 	void *data = (void *)arg;
158eb9a1df2SHans Rosenfeld 
159eb9a1df2SHans Rosenfeld 	if ((ppt = ddi_get_soft_state(ppt_state, minor)) == NULL) {
160eb9a1df2SHans Rosenfeld 		return (ENOENT);
161eb9a1df2SHans Rosenfeld 	}
162eb9a1df2SHans Rosenfeld 
163eb9a1df2SHans Rosenfeld 	switch (cmd) {
164eb9a1df2SHans Rosenfeld 	case PPT_CFG_READ: {
165eb9a1df2SHans Rosenfeld 		struct ppt_cfg_io cio;
166eb9a1df2SHans Rosenfeld 		ddi_acc_handle_t cfg = ppt->pptd_cfg;
167eb9a1df2SHans Rosenfeld 
168eb9a1df2SHans Rosenfeld 		if (ddi_copyin(data, &cio, sizeof (cio), md) != 0) {
169eb9a1df2SHans Rosenfeld 			return (EFAULT);
170eb9a1df2SHans Rosenfeld 		}
171eb9a1df2SHans Rosenfeld 		switch (cio.pci_width) {
172eb9a1df2SHans Rosenfeld 		case 4:
173eb9a1df2SHans Rosenfeld 			cio.pci_data = pci_config_get32(cfg, cio.pci_off);
174eb9a1df2SHans Rosenfeld 			break;
175eb9a1df2SHans Rosenfeld 		case 2:
176eb9a1df2SHans Rosenfeld 			cio.pci_data = pci_config_get16(cfg, cio.pci_off);
177eb9a1df2SHans Rosenfeld 			break;
178eb9a1df2SHans Rosenfeld 		case 1:
179eb9a1df2SHans Rosenfeld 			cio.pci_data = pci_config_get8(cfg, cio.pci_off);
180eb9a1df2SHans Rosenfeld 			break;
181eb9a1df2SHans Rosenfeld 		default:
182eb9a1df2SHans Rosenfeld 			return (EINVAL);
183eb9a1df2SHans Rosenfeld 		}
184eb9a1df2SHans Rosenfeld 
185eb9a1df2SHans Rosenfeld 		if (ddi_copyout(&cio, data, sizeof (cio), md) != 0) {
186eb9a1df2SHans Rosenfeld 			return (EFAULT);
187eb9a1df2SHans Rosenfeld 		}
188eb9a1df2SHans Rosenfeld 		return (0);
189eb9a1df2SHans Rosenfeld 	}
190eb9a1df2SHans Rosenfeld 	case PPT_CFG_WRITE: {
191eb9a1df2SHans Rosenfeld 		struct ppt_cfg_io cio;
192eb9a1df2SHans Rosenfeld 		ddi_acc_handle_t cfg = ppt->pptd_cfg;
193eb9a1df2SHans Rosenfeld 
194eb9a1df2SHans Rosenfeld 		if (ddi_copyin(data, &cio, sizeof (cio), md) != 0) {
195eb9a1df2SHans Rosenfeld 			return (EFAULT);
196eb9a1df2SHans Rosenfeld 		}
197eb9a1df2SHans Rosenfeld 		switch (cio.pci_width) {
198eb9a1df2SHans Rosenfeld 		case 4:
199eb9a1df2SHans Rosenfeld 			pci_config_put32(cfg, cio.pci_off, cio.pci_data);
200eb9a1df2SHans Rosenfeld 			break;
201eb9a1df2SHans Rosenfeld 		case 2:
202eb9a1df2SHans Rosenfeld 			pci_config_put16(cfg, cio.pci_off, cio.pci_data);
203eb9a1df2SHans Rosenfeld 			break;
204eb9a1df2SHans Rosenfeld 		case 1:
205eb9a1df2SHans Rosenfeld 			pci_config_put8(cfg, cio.pci_off, cio.pci_data);
206eb9a1df2SHans Rosenfeld 			break;
207eb9a1df2SHans Rosenfeld 		default:
208eb9a1df2SHans Rosenfeld 			return (EINVAL);
209eb9a1df2SHans Rosenfeld 		}
210eb9a1df2SHans Rosenfeld 
211eb9a1df2SHans Rosenfeld 		return (0);
212eb9a1df2SHans Rosenfeld 	}
213eb9a1df2SHans Rosenfeld 	case PPT_BAR_QUERY: {
214eb9a1df2SHans Rosenfeld 		struct ppt_bar_query barg;
215eb9a1df2SHans Rosenfeld 		struct pptbar *pbar;
216eb9a1df2SHans Rosenfeld 
217eb9a1df2SHans Rosenfeld 		if (ddi_copyin(data, &barg, sizeof (barg), md) != 0) {
218eb9a1df2SHans Rosenfeld 			return (EFAULT);
219eb9a1df2SHans Rosenfeld 		}
220eb9a1df2SHans Rosenfeld 		if (barg.pbq_baridx >= PCI_BASE_NUM) {
221eb9a1df2SHans Rosenfeld 			return (EINVAL);
222eb9a1df2SHans Rosenfeld 		}
223eb9a1df2SHans Rosenfeld 		pbar = &ppt->pptd_bars[barg.pbq_baridx];
224eb9a1df2SHans Rosenfeld 
225eb9a1df2SHans Rosenfeld 		if (pbar->base == 0 || pbar->size == 0) {
226eb9a1df2SHans Rosenfeld 			return (ENOENT);
227eb9a1df2SHans Rosenfeld 		}
228eb9a1df2SHans Rosenfeld 		barg.pbq_type = pbar->type;
229eb9a1df2SHans Rosenfeld 		barg.pbq_base = pbar->base;
230eb9a1df2SHans Rosenfeld 		barg.pbq_size = pbar->size;
231eb9a1df2SHans Rosenfeld 
232eb9a1df2SHans Rosenfeld 		if (ddi_copyout(&barg, data, sizeof (barg), md) != 0) {
233eb9a1df2SHans Rosenfeld 			return (EFAULT);
234eb9a1df2SHans Rosenfeld 		}
235eb9a1df2SHans Rosenfeld 		return (0);
236eb9a1df2SHans Rosenfeld 	}
237eb9a1df2SHans Rosenfeld 	case PPT_BAR_READ: {
238eb9a1df2SHans Rosenfeld 		struct ppt_bar_io bio;
239eb9a1df2SHans Rosenfeld 		struct pptbar *pbar;
240eb9a1df2SHans Rosenfeld 		void *addr;
241eb9a1df2SHans Rosenfeld 		uint_t rnum;
242eb9a1df2SHans Rosenfeld 		ddi_acc_handle_t cfg;
243eb9a1df2SHans Rosenfeld 
244eb9a1df2SHans Rosenfeld 		if (ddi_copyin(data, &bio, sizeof (bio), md) != 0) {
245eb9a1df2SHans Rosenfeld 			return (EFAULT);
246eb9a1df2SHans Rosenfeld 		}
247eb9a1df2SHans Rosenfeld 		rnum = bio.pbi_bar;
248eb9a1df2SHans Rosenfeld 		if (rnum >= PCI_BASE_NUM) {
249eb9a1df2SHans Rosenfeld 			return (EINVAL);
250eb9a1df2SHans Rosenfeld 		}
251eb9a1df2SHans Rosenfeld 		pbar = &ppt->pptd_bars[rnum];
252eb9a1df2SHans Rosenfeld 		if (pbar->type != PCI_ADDR_IO || pbar->io_handle == NULL) {
253eb9a1df2SHans Rosenfeld 			return (EINVAL);
254eb9a1df2SHans Rosenfeld 		}
255eb9a1df2SHans Rosenfeld 		addr = pbar->io_ptr + bio.pbi_off;
256eb9a1df2SHans Rosenfeld 
257eb9a1df2SHans Rosenfeld 		switch (bio.pbi_width) {
258eb9a1df2SHans Rosenfeld 		case 4:
259eb9a1df2SHans Rosenfeld 			bio.pbi_data = ddi_get32(pbar->io_handle, addr);
260eb9a1df2SHans Rosenfeld 			break;
261eb9a1df2SHans Rosenfeld 		case 2:
262eb9a1df2SHans Rosenfeld 			bio.pbi_data = ddi_get16(pbar->io_handle, addr);
263eb9a1df2SHans Rosenfeld 			break;
264eb9a1df2SHans Rosenfeld 		case 1:
265eb9a1df2SHans Rosenfeld 			bio.pbi_data = ddi_get8(pbar->io_handle, addr);
266eb9a1df2SHans Rosenfeld 			break;
267eb9a1df2SHans Rosenfeld 		default:
268eb9a1df2SHans Rosenfeld 			return (EINVAL);
269eb9a1df2SHans Rosenfeld 		}
270eb9a1df2SHans Rosenfeld 
271eb9a1df2SHans Rosenfeld 		if (ddi_copyout(&bio, data, sizeof (bio), md) != 0) {
272eb9a1df2SHans Rosenfeld 			return (EFAULT);
273eb9a1df2SHans Rosenfeld 		}
274eb9a1df2SHans Rosenfeld 		return (0);
275eb9a1df2SHans Rosenfeld 	}
276eb9a1df2SHans Rosenfeld 	case PPT_BAR_WRITE: {
277eb9a1df2SHans Rosenfeld 		struct ppt_bar_io bio;
278eb9a1df2SHans Rosenfeld 		struct pptbar *pbar;
279eb9a1df2SHans Rosenfeld 		void *addr;
280eb9a1df2SHans Rosenfeld 		uint_t rnum;
281eb9a1df2SHans Rosenfeld 		ddi_acc_handle_t cfg;
282eb9a1df2SHans Rosenfeld 
283eb9a1df2SHans Rosenfeld 		if (ddi_copyin(data, &bio, sizeof (bio), md) != 0) {
284eb9a1df2SHans Rosenfeld 			return (EFAULT);
285eb9a1df2SHans Rosenfeld 		}
286eb9a1df2SHans Rosenfeld 		rnum = bio.pbi_bar;
287eb9a1df2SHans Rosenfeld 		if (rnum >= PCI_BASE_NUM) {
288eb9a1df2SHans Rosenfeld 			return (EINVAL);
289eb9a1df2SHans Rosenfeld 		}
290eb9a1df2SHans Rosenfeld 		pbar = &ppt->pptd_bars[rnum];
291eb9a1df2SHans Rosenfeld 		if (pbar->type != PCI_ADDR_IO || pbar->io_handle == NULL) {
292eb9a1df2SHans Rosenfeld 			return (EINVAL);
293eb9a1df2SHans Rosenfeld 		}
294eb9a1df2SHans Rosenfeld 		addr = pbar->io_ptr + bio.pbi_off;
295eb9a1df2SHans Rosenfeld 
296eb9a1df2SHans Rosenfeld 		switch (bio.pbi_width) {
297eb9a1df2SHans Rosenfeld 		case 4:
298eb9a1df2SHans Rosenfeld 			ddi_put32(pbar->io_handle, addr, bio.pbi_data);
299eb9a1df2SHans Rosenfeld 			break;
300eb9a1df2SHans Rosenfeld 		case 2:
301eb9a1df2SHans Rosenfeld 			ddi_put16(pbar->io_handle, addr, bio.pbi_data);
302eb9a1df2SHans Rosenfeld 			break;
303eb9a1df2SHans Rosenfeld 		case 1:
304eb9a1df2SHans Rosenfeld 			ddi_put8(pbar->io_handle, addr, bio.pbi_data);
305eb9a1df2SHans Rosenfeld 			break;
306eb9a1df2SHans Rosenfeld 		default:
307eb9a1df2SHans Rosenfeld 			return (EINVAL);
308eb9a1df2SHans Rosenfeld 		}
309eb9a1df2SHans Rosenfeld 
310eb9a1df2SHans Rosenfeld 		return (0);
311eb9a1df2SHans Rosenfeld 	}
312eb9a1df2SHans Rosenfeld 
313eb9a1df2SHans Rosenfeld 	default:
314eb9a1df2SHans Rosenfeld 		return (ENOTTY);
315eb9a1df2SHans Rosenfeld 	}
316eb9a1df2SHans Rosenfeld 
317eb9a1df2SHans Rosenfeld 	return (0);
318eb9a1df2SHans Rosenfeld }
319eb9a1df2SHans Rosenfeld 
320eb9a1df2SHans Rosenfeld static int
ppt_find_msix_table_bar(struct pptdev * ppt)3216dc98349SAndy Fiddaman ppt_find_msix_table_bar(struct pptdev *ppt)
322eb9a1df2SHans Rosenfeld {
323eb9a1df2SHans Rosenfeld 	uint16_t base;
3246dc98349SAndy Fiddaman 	uint32_t off;
325eb9a1df2SHans Rosenfeld 
326eb9a1df2SHans Rosenfeld 	if (PCI_CAP_LOCATE(ppt->pptd_cfg, PCI_CAP_ID_MSI_X, &base) !=
327eb9a1df2SHans Rosenfeld 	    DDI_SUCCESS)
328eb9a1df2SHans Rosenfeld 		return (-1);
329eb9a1df2SHans Rosenfeld 
3306dc98349SAndy Fiddaman 	off = pci_config_get32(ppt->pptd_cfg, base + PCI_MSIX_TBL_OFFSET);
331eb9a1df2SHans Rosenfeld 
3326dc98349SAndy Fiddaman 	if (off == PCI_EINVAL32)
333eb9a1df2SHans Rosenfeld 		return (-1);
334eb9a1df2SHans Rosenfeld 
3356dc98349SAndy Fiddaman 	return (off & PCI_MSIX_TBL_BIR_MASK);
336eb9a1df2SHans Rosenfeld }
337eb9a1df2SHans Rosenfeld 
338eb9a1df2SHans Rosenfeld static int
ppt_devmap(dev_t dev,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model)339eb9a1df2SHans Rosenfeld ppt_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
340eb9a1df2SHans Rosenfeld     size_t *maplen, uint_t model)
341eb9a1df2SHans Rosenfeld {
342eb9a1df2SHans Rosenfeld 	minor_t minor;
343eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
3447ca35597SAndy Fiddaman 	int err, bar;
3457ca35597SAndy Fiddaman 	uint_t ddireg;
346eb9a1df2SHans Rosenfeld 
347eb9a1df2SHans Rosenfeld 	minor = getminor(dev);
348eb9a1df2SHans Rosenfeld 
349eb9a1df2SHans Rosenfeld 	if ((ppt = ddi_get_soft_state(ppt_state, minor)) == NULL)
350eb9a1df2SHans Rosenfeld 		return (ENXIO);
351eb9a1df2SHans Rosenfeld 
352eb9a1df2SHans Rosenfeld #ifdef _MULTI_DATAMODEL
353eb9a1df2SHans Rosenfeld 	if (ddi_model_convert_from(model) != DDI_MODEL_NONE)
354eb9a1df2SHans Rosenfeld 		return (ENXIO);
355eb9a1df2SHans Rosenfeld #endif
356eb9a1df2SHans Rosenfeld 
357eb9a1df2SHans Rosenfeld 	if (off < 0 || off != P2ALIGN(off, PAGESIZE))
358eb9a1df2SHans Rosenfeld 		return (EINVAL);
359eb9a1df2SHans Rosenfeld 
3606dc98349SAndy Fiddaman 	if ((bar = ppt_find_msix_table_bar(ppt)) == -1)
361eb9a1df2SHans Rosenfeld 		return (EINVAL);
362eb9a1df2SHans Rosenfeld 
3637ca35597SAndy Fiddaman 	ddireg = ppt->pptd_bars[bar].ddireg;
364eb9a1df2SHans Rosenfeld 
3657ca35597SAndy Fiddaman 	if (ddireg == 0)
3667ca35597SAndy Fiddaman 		return (EINVAL);
3677ca35597SAndy Fiddaman 
3687ca35597SAndy Fiddaman 	err = devmap_devmem_setup(dhp, ppt->pptd_dip, NULL, ddireg, off, len,
369eb9a1df2SHans Rosenfeld 	    PROT_USER | PROT_READ | PROT_WRITE, IOMEM_DATA_CACHED, &ppt_attr);
370eb9a1df2SHans Rosenfeld 
371eb9a1df2SHans Rosenfeld 	if (err == DDI_SUCCESS)
372eb9a1df2SHans Rosenfeld 		*maplen = len;
373eb9a1df2SHans Rosenfeld 
374eb9a1df2SHans Rosenfeld 	return (err);
375eb9a1df2SHans Rosenfeld }
376eb9a1df2SHans Rosenfeld 
377eb9a1df2SHans Rosenfeld static void
ppt_bar_wipe(struct pptdev * ppt)378eb9a1df2SHans Rosenfeld ppt_bar_wipe(struct pptdev *ppt)
379eb9a1df2SHans Rosenfeld {
380eb9a1df2SHans Rosenfeld 	uint_t i;
381eb9a1df2SHans Rosenfeld 
382eb9a1df2SHans Rosenfeld 	for (i = 0; i < PCI_BASE_NUM; i++) {
383eb9a1df2SHans Rosenfeld 		struct pptbar *pbar = &ppt->pptd_bars[i];
384eb9a1df2SHans Rosenfeld 		if (pbar->type == PCI_ADDR_IO && pbar->io_handle != NULL) {
385eb9a1df2SHans Rosenfeld 			ddi_regs_map_free(&pbar->io_handle);
386eb9a1df2SHans Rosenfeld 		}
387eb9a1df2SHans Rosenfeld 	}
388eb9a1df2SHans Rosenfeld 	bzero(&ppt->pptd_bars, sizeof (ppt->pptd_bars));
389eb9a1df2SHans Rosenfeld }
390eb9a1df2SHans Rosenfeld 
391eb9a1df2SHans Rosenfeld static int
ppt_bar_crawl(struct pptdev * ppt)392eb9a1df2SHans Rosenfeld ppt_bar_crawl(struct pptdev *ppt)
393eb9a1df2SHans Rosenfeld {
394eb9a1df2SHans Rosenfeld 	pci_regspec_t *regs;
395eb9a1df2SHans Rosenfeld 	uint_t rcount, i;
396eb9a1df2SHans Rosenfeld 	int err = 0, rlen;
397eb9a1df2SHans Rosenfeld 
398eb9a1df2SHans Rosenfeld 	if (ddi_getlongprop(DDI_DEV_T_ANY, ppt->pptd_dip, DDI_PROP_DONTPASS,
399eb9a1df2SHans Rosenfeld 	    "assigned-addresses", (caddr_t)&regs, &rlen) != DDI_PROP_SUCCESS) {
400eb9a1df2SHans Rosenfeld 		return (EIO);
401eb9a1df2SHans Rosenfeld 	}
402eb9a1df2SHans Rosenfeld 
403eb9a1df2SHans Rosenfeld 	VERIFY3S(rlen, >, 0);
404eb9a1df2SHans Rosenfeld 	rcount = rlen / sizeof (pci_regspec_t);
405eb9a1df2SHans Rosenfeld 	for (i = 0; i < rcount; i++) {
406eb9a1df2SHans Rosenfeld 		pci_regspec_t *reg = &regs[i];
407eb9a1df2SHans Rosenfeld 		struct pptbar *pbar;
408eb9a1df2SHans Rosenfeld 		uint_t bar, rnum;
409eb9a1df2SHans Rosenfeld 
410eb9a1df2SHans Rosenfeld 		DTRACE_PROBE1(ppt__crawl__reg, pci_regspec_t *, reg);
411eb9a1df2SHans Rosenfeld 		bar = PCI_REG_REG_G(reg->pci_phys_hi);
412eb9a1df2SHans Rosenfeld 		if (!BAR_VALID(bar)) {
413eb9a1df2SHans Rosenfeld 			continue;
414eb9a1df2SHans Rosenfeld 		}
415eb9a1df2SHans Rosenfeld 
416eb9a1df2SHans Rosenfeld 		rnum = BAR_TO_IDX(bar);
417eb9a1df2SHans Rosenfeld 		pbar = &ppt->pptd_bars[rnum];
418eb9a1df2SHans Rosenfeld 		/* is this somehow already populated? */
419eb9a1df2SHans Rosenfeld 		if (pbar->base != 0 || pbar->size != 0) {
420eb9a1df2SHans Rosenfeld 			err = EEXIST;
421eb9a1df2SHans Rosenfeld 			break;
422eb9a1df2SHans Rosenfeld 		}
423eb9a1df2SHans Rosenfeld 
4247ca35597SAndy Fiddaman 		/*
4257ca35597SAndy Fiddaman 		 * Register 0 corresponds to the PCI config space.
4267ca35597SAndy Fiddaman 		 * The registers which match the assigned-addresses list are
4277ca35597SAndy Fiddaman 		 * offset by 1.
4287ca35597SAndy Fiddaman 		 */
4297ca35597SAndy Fiddaman 		pbar->ddireg = i + 1;
4307ca35597SAndy Fiddaman 
431eb9a1df2SHans Rosenfeld 		pbar->type = reg->pci_phys_hi & PCI_ADDR_MASK;
432eb9a1df2SHans Rosenfeld 		pbar->base = ((uint64_t)reg->pci_phys_mid << 32) |
433eb9a1df2SHans Rosenfeld 		    (uint64_t)reg->pci_phys_low;
434eb9a1df2SHans Rosenfeld 		pbar->size = ((uint64_t)reg->pci_size_hi << 32) |
435eb9a1df2SHans Rosenfeld 		    (uint64_t)reg->pci_size_low;
436eb9a1df2SHans Rosenfeld 		if (pbar->type == PCI_ADDR_IO) {
437eb9a1df2SHans Rosenfeld 			err = ddi_regs_map_setup(ppt->pptd_dip, rnum,
438eb9a1df2SHans Rosenfeld 			    &pbar->io_ptr, 0, 0, &ppt_attr, &pbar->io_handle);
439eb9a1df2SHans Rosenfeld 			if (err != 0) {
440eb9a1df2SHans Rosenfeld 				break;
441eb9a1df2SHans Rosenfeld 			}
442eb9a1df2SHans Rosenfeld 		}
443eb9a1df2SHans Rosenfeld 	}
444eb9a1df2SHans Rosenfeld 	kmem_free(regs, rlen);
445eb9a1df2SHans Rosenfeld 
446eb9a1df2SHans Rosenfeld 	if (err != 0) {
447eb9a1df2SHans Rosenfeld 		ppt_bar_wipe(ppt);
448eb9a1df2SHans Rosenfeld 	}
449eb9a1df2SHans Rosenfeld 	return (err);
450eb9a1df2SHans Rosenfeld }
451eb9a1df2SHans Rosenfeld 
452eb9a1df2SHans Rosenfeld static boolean_t
ppt_bar_verify_mmio(struct pptdev * ppt,uint64_t base,uint64_t size)453eb9a1df2SHans Rosenfeld ppt_bar_verify_mmio(struct pptdev *ppt, uint64_t base, uint64_t size)
454eb9a1df2SHans Rosenfeld {
455eb9a1df2SHans Rosenfeld 	const uint64_t map_end = base + size;
456eb9a1df2SHans Rosenfeld 
457eb9a1df2SHans Rosenfeld 	/* Zero-length or overflow mappings are not valid */
458eb9a1df2SHans Rosenfeld 	if (map_end <= base) {
459eb9a1df2SHans Rosenfeld 		return (B_FALSE);
460eb9a1df2SHans Rosenfeld 	}
461eb9a1df2SHans Rosenfeld 	/* MMIO bounds should be page-aligned */
462eb9a1df2SHans Rosenfeld 	if ((base & PAGEOFFSET) != 0 || (size & PAGEOFFSET) != 0) {
463eb9a1df2SHans Rosenfeld 		return (B_FALSE);
464eb9a1df2SHans Rosenfeld 	}
465eb9a1df2SHans Rosenfeld 
466eb9a1df2SHans Rosenfeld 	for (uint_t i = 0; i < PCI_BASE_NUM; i++) {
467eb9a1df2SHans Rosenfeld 		const struct pptbar *bar = &ppt->pptd_bars[i];
468eb9a1df2SHans Rosenfeld 		const uint64_t bar_end = bar->base + bar->size;
469eb9a1df2SHans Rosenfeld 
470eb9a1df2SHans Rosenfeld 		/* Only memory BARs can be mapped */
471eb9a1df2SHans Rosenfeld 		if (bar->type != PCI_ADDR_MEM32 &&
472eb9a1df2SHans Rosenfeld 		    bar->type != PCI_ADDR_MEM64) {
473eb9a1df2SHans Rosenfeld 			continue;
474eb9a1df2SHans Rosenfeld 		}
475eb9a1df2SHans Rosenfeld 
476eb9a1df2SHans Rosenfeld 		/* Does the mapping fit within this BAR? */
477eb9a1df2SHans Rosenfeld 		if (base < bar->base || base >= bar_end ||
478eb9a1df2SHans Rosenfeld 		    map_end < bar->base || map_end > bar_end) {
479eb9a1df2SHans Rosenfeld 			continue;
480eb9a1df2SHans Rosenfeld 		}
481eb9a1df2SHans Rosenfeld 
482eb9a1df2SHans Rosenfeld 		/* This BAR satisfies the provided map */
483eb9a1df2SHans Rosenfeld 		return (B_TRUE);
484eb9a1df2SHans Rosenfeld 	}
485eb9a1df2SHans Rosenfeld 	return (B_FALSE);
486eb9a1df2SHans Rosenfeld }
487eb9a1df2SHans Rosenfeld 
488eb9a1df2SHans Rosenfeld static int
ppt_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)489eb9a1df2SHans Rosenfeld ppt_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
490eb9a1df2SHans Rosenfeld {
491eb9a1df2SHans Rosenfeld 	struct pptdev *ppt = NULL;
492eb9a1df2SHans Rosenfeld 	char name[PPT_MAXNAMELEN];
493eb9a1df2SHans Rosenfeld 	int inst;
494eb9a1df2SHans Rosenfeld 
495eb9a1df2SHans Rosenfeld 	if (cmd != DDI_ATTACH)
496eb9a1df2SHans Rosenfeld 		return (DDI_FAILURE);
497eb9a1df2SHans Rosenfeld 
498eb9a1df2SHans Rosenfeld 	inst = ddi_get_instance(dip);
499eb9a1df2SHans Rosenfeld 
500eb9a1df2SHans Rosenfeld 	if (ddi_soft_state_zalloc(ppt_state, inst) != DDI_SUCCESS) {
501eb9a1df2SHans Rosenfeld 		goto fail;
502eb9a1df2SHans Rosenfeld 	}
503eb9a1df2SHans Rosenfeld 	VERIFY(ppt = ddi_get_soft_state(ppt_state, inst));
504eb9a1df2SHans Rosenfeld 	ppt->pptd_dip = dip;
505eb9a1df2SHans Rosenfeld 	ddi_set_driver_private(dip, ppt);
506eb9a1df2SHans Rosenfeld 
507eb9a1df2SHans Rosenfeld 	if (pci_config_setup(dip, &ppt->pptd_cfg) != DDI_SUCCESS) {
508eb9a1df2SHans Rosenfeld 		goto fail;
509eb9a1df2SHans Rosenfeld 	}
510eb9a1df2SHans Rosenfeld 	if (ppt_bar_crawl(ppt) != 0) {
511eb9a1df2SHans Rosenfeld 		goto fail;
512eb9a1df2SHans Rosenfeld 	}
513eb9a1df2SHans Rosenfeld 	if (ddi_create_minor_node(dip, PPT_MINOR_NAME, S_IFCHR, inst,
514eb9a1df2SHans Rosenfeld 	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
515eb9a1df2SHans Rosenfeld 		goto fail;
516eb9a1df2SHans Rosenfeld 	}
517eb9a1df2SHans Rosenfeld 
518eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
519eb9a1df2SHans Rosenfeld 	list_insert_tail(&pptdev_list, ppt);
520eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
521eb9a1df2SHans Rosenfeld 
522eb9a1df2SHans Rosenfeld 	return (DDI_SUCCESS);
523eb9a1df2SHans Rosenfeld 
524eb9a1df2SHans Rosenfeld fail:
525eb9a1df2SHans Rosenfeld 	if (ppt != NULL) {
526eb9a1df2SHans Rosenfeld 		ddi_remove_minor_node(dip, NULL);
527eb9a1df2SHans Rosenfeld 		if (ppt->pptd_cfg != NULL) {
528eb9a1df2SHans Rosenfeld 			pci_config_teardown(&ppt->pptd_cfg);
529eb9a1df2SHans Rosenfeld 		}
530eb9a1df2SHans Rosenfeld 		ppt_bar_wipe(ppt);
531eb9a1df2SHans Rosenfeld 		ddi_soft_state_free(ppt_state, inst);
532eb9a1df2SHans Rosenfeld 	}
533eb9a1df2SHans Rosenfeld 	return (DDI_FAILURE);
534eb9a1df2SHans Rosenfeld }
535eb9a1df2SHans Rosenfeld 
536eb9a1df2SHans Rosenfeld static int
ppt_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)537eb9a1df2SHans Rosenfeld ppt_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
538eb9a1df2SHans Rosenfeld {
539eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
540eb9a1df2SHans Rosenfeld 	int inst;
541eb9a1df2SHans Rosenfeld 
542eb9a1df2SHans Rosenfeld 	if (cmd != DDI_DETACH)
543eb9a1df2SHans Rosenfeld 		return (DDI_FAILURE);
544eb9a1df2SHans Rosenfeld 
545eb9a1df2SHans Rosenfeld 	ppt = ddi_get_driver_private(dip);
546eb9a1df2SHans Rosenfeld 	inst = ddi_get_instance(dip);
547eb9a1df2SHans Rosenfeld 
548eb9a1df2SHans Rosenfeld 	ASSERT3P(ddi_get_soft_state(ppt_state, inst), ==, ppt);
549eb9a1df2SHans Rosenfeld 
550eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
551eb9a1df2SHans Rosenfeld 	if (ppt->vm != NULL) {
552eb9a1df2SHans Rosenfeld 		mutex_exit(&pptdev_mtx);
553eb9a1df2SHans Rosenfeld 		return (DDI_FAILURE);
554eb9a1df2SHans Rosenfeld 	}
555eb9a1df2SHans Rosenfeld 	list_remove(&pptdev_list, ppt);
556eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
557eb9a1df2SHans Rosenfeld 
558eb9a1df2SHans Rosenfeld 	ddi_remove_minor_node(dip, PPT_MINOR_NAME);
559eb9a1df2SHans Rosenfeld 	ppt_bar_wipe(ppt);
560eb9a1df2SHans Rosenfeld 	pci_config_teardown(&ppt->pptd_cfg);
561eb9a1df2SHans Rosenfeld 	ddi_set_driver_private(dip, NULL);
562eb9a1df2SHans Rosenfeld 	ddi_soft_state_free(ppt_state, inst);
563eb9a1df2SHans Rosenfeld 
564eb9a1df2SHans Rosenfeld 	return (DDI_SUCCESS);
565eb9a1df2SHans Rosenfeld }
566eb9a1df2SHans Rosenfeld 
567eb9a1df2SHans Rosenfeld static int
ppt_ddi_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)568eb9a1df2SHans Rosenfeld ppt_ddi_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
569eb9a1df2SHans Rosenfeld {
570eb9a1df2SHans Rosenfeld 	int error = DDI_FAILURE;
571eb9a1df2SHans Rosenfeld 	int inst = getminor((dev_t)arg);
572eb9a1df2SHans Rosenfeld 
573eb9a1df2SHans Rosenfeld 	switch (cmd) {
574eb9a1df2SHans Rosenfeld 	case DDI_INFO_DEVT2DEVINFO: {
575eb9a1df2SHans Rosenfeld 		struct pptdev *ppt = ddi_get_soft_state(ppt_state, inst);
576eb9a1df2SHans Rosenfeld 
577eb9a1df2SHans Rosenfeld 		if (ppt != NULL) {
578eb9a1df2SHans Rosenfeld 			*result = (void *)ppt->pptd_dip;
579eb9a1df2SHans Rosenfeld 			error = DDI_SUCCESS;
580eb9a1df2SHans Rosenfeld 		}
581eb9a1df2SHans Rosenfeld 		break;
582eb9a1df2SHans Rosenfeld 	}
583eb9a1df2SHans Rosenfeld 	case DDI_INFO_DEVT2INSTANCE: {
584eb9a1df2SHans Rosenfeld 		*result = (void *)(uintptr_t)inst;
585eb9a1df2SHans Rosenfeld 		error = DDI_SUCCESS;
586eb9a1df2SHans Rosenfeld 		break;
587eb9a1df2SHans Rosenfeld 	}
588eb9a1df2SHans Rosenfeld 	default:
589eb9a1df2SHans Rosenfeld 		break;
590eb9a1df2SHans Rosenfeld 	}
591eb9a1df2SHans Rosenfeld 	return (error);
592eb9a1df2SHans Rosenfeld }
593eb9a1df2SHans Rosenfeld 
594eb9a1df2SHans Rosenfeld static struct cb_ops ppt_cb_ops = {
595eb9a1df2SHans Rosenfeld 	ppt_open,
596eb9a1df2SHans Rosenfeld 	nulldev,	/* close */
597eb9a1df2SHans Rosenfeld 	nodev,		/* strategy */
598eb9a1df2SHans Rosenfeld 	nodev,		/* print */
599eb9a1df2SHans Rosenfeld 	nodev,		/* dump */
600eb9a1df2SHans Rosenfeld 	nodev,		/* read */
601eb9a1df2SHans Rosenfeld 	nodev,		/* write */
602eb9a1df2SHans Rosenfeld 	ppt_ioctl,
603eb9a1df2SHans Rosenfeld 	ppt_devmap,	/* devmap */
604eb9a1df2SHans Rosenfeld 	NULL,		/* mmap */
605eb9a1df2SHans Rosenfeld 	NULL,		/* segmap */
606eb9a1df2SHans Rosenfeld 	nochpoll,	/* poll */
607eb9a1df2SHans Rosenfeld 	ddi_prop_op,
608eb9a1df2SHans Rosenfeld 	NULL,
609eb9a1df2SHans Rosenfeld 	D_NEW | D_MP | D_64BIT | D_DEVMAP,
610eb9a1df2SHans Rosenfeld 	CB_REV
611eb9a1df2SHans Rosenfeld };
612eb9a1df2SHans Rosenfeld 
613eb9a1df2SHans Rosenfeld static struct dev_ops ppt_ops = {
614eb9a1df2SHans Rosenfeld 	DEVO_REV,
615eb9a1df2SHans Rosenfeld 	0,
616eb9a1df2SHans Rosenfeld 	ppt_ddi_info,
617eb9a1df2SHans Rosenfeld 	nulldev,	/* identify */
618eb9a1df2SHans Rosenfeld 	nulldev,	/* probe */
619eb9a1df2SHans Rosenfeld 	ppt_ddi_attach,
620eb9a1df2SHans Rosenfeld 	ppt_ddi_detach,
621eb9a1df2SHans Rosenfeld 	nodev,		/* reset */
622eb9a1df2SHans Rosenfeld 	&ppt_cb_ops,
623eb9a1df2SHans Rosenfeld 	(struct bus_ops *)NULL
624eb9a1df2SHans Rosenfeld };
625eb9a1df2SHans Rosenfeld 
626eb9a1df2SHans Rosenfeld static struct modldrv modldrv = {
627eb9a1df2SHans Rosenfeld 	&mod_driverops,
628eb9a1df2SHans Rosenfeld 	"bhyve pci pass-thru",
629eb9a1df2SHans Rosenfeld 	&ppt_ops
630eb9a1df2SHans Rosenfeld };
631eb9a1df2SHans Rosenfeld 
632eb9a1df2SHans Rosenfeld static struct modlinkage modlinkage = {
633eb9a1df2SHans Rosenfeld 	MODREV_1,
634eb9a1df2SHans Rosenfeld 	&modldrv,
635eb9a1df2SHans Rosenfeld 	NULL
636eb9a1df2SHans Rosenfeld };
637eb9a1df2SHans Rosenfeld 
638eb9a1df2SHans Rosenfeld int
_init(void)639eb9a1df2SHans Rosenfeld _init(void)
640eb9a1df2SHans Rosenfeld {
641eb9a1df2SHans Rosenfeld 	int error;
642eb9a1df2SHans Rosenfeld 
643eb9a1df2SHans Rosenfeld 	mutex_init(&pptdev_mtx, NULL, MUTEX_DRIVER, NULL);
644eb9a1df2SHans Rosenfeld 	list_create(&pptdev_list, sizeof (struct pptdev),
645eb9a1df2SHans Rosenfeld 	    offsetof(struct pptdev, pptd_node));
646eb9a1df2SHans Rosenfeld 
647eb9a1df2SHans Rosenfeld 	error = ddi_soft_state_init(&ppt_state, sizeof (struct pptdev), 0);
648eb9a1df2SHans Rosenfeld 	if (error) {
649eb9a1df2SHans Rosenfeld 		goto fail;
650eb9a1df2SHans Rosenfeld 	}
651eb9a1df2SHans Rosenfeld 
652eb9a1df2SHans Rosenfeld 	error = mod_install(&modlinkage);
653eb9a1df2SHans Rosenfeld 
654eb9a1df2SHans Rosenfeld 	ppt_major = ddi_name_to_major("ppt");
655eb9a1df2SHans Rosenfeld fail:
656eb9a1df2SHans Rosenfeld 	if (error) {
657eb9a1df2SHans Rosenfeld 		ddi_soft_state_fini(&ppt_state);
658eb9a1df2SHans Rosenfeld 	}
659eb9a1df2SHans Rosenfeld 	return (error);
660eb9a1df2SHans Rosenfeld }
661eb9a1df2SHans Rosenfeld 
662eb9a1df2SHans Rosenfeld int
_fini(void)663eb9a1df2SHans Rosenfeld _fini(void)
664eb9a1df2SHans Rosenfeld {
665eb9a1df2SHans Rosenfeld 	int error;
666eb9a1df2SHans Rosenfeld 
667eb9a1df2SHans Rosenfeld 	error = mod_remove(&modlinkage);
668eb9a1df2SHans Rosenfeld 	if (error)
669eb9a1df2SHans Rosenfeld 		return (error);
670eb9a1df2SHans Rosenfeld 	ddi_soft_state_fini(&ppt_state);
671eb9a1df2SHans Rosenfeld 
672eb9a1df2SHans Rosenfeld 	return (0);
673eb9a1df2SHans Rosenfeld }
674eb9a1df2SHans Rosenfeld 
675eb9a1df2SHans Rosenfeld int
_info(struct modinfo * modinfop)676eb9a1df2SHans Rosenfeld _info(struct modinfo *modinfop)
677eb9a1df2SHans Rosenfeld {
678eb9a1df2SHans Rosenfeld 	return (mod_info(&modlinkage, modinfop));
679eb9a1df2SHans Rosenfeld }
680eb9a1df2SHans Rosenfeld 
681eb9a1df2SHans Rosenfeld static boolean_t
ppt_wait_for_pending_txn(dev_info_t * dip,uint_t max_delay_us)682eb9a1df2SHans Rosenfeld ppt_wait_for_pending_txn(dev_info_t *dip, uint_t max_delay_us)
683eb9a1df2SHans Rosenfeld {
684eb9a1df2SHans Rosenfeld 	uint16_t cap_ptr, devsts;
685eb9a1df2SHans Rosenfeld 	ddi_acc_handle_t hdl;
686eb9a1df2SHans Rosenfeld 
687eb9a1df2SHans Rosenfeld 	if (pci_config_setup(dip, &hdl) != DDI_SUCCESS)
688eb9a1df2SHans Rosenfeld 		return (B_FALSE);
689eb9a1df2SHans Rosenfeld 
690eb9a1df2SHans Rosenfeld 	if (PCI_CAP_LOCATE(hdl, PCI_CAP_ID_PCI_E, &cap_ptr) != DDI_SUCCESS) {
691eb9a1df2SHans Rosenfeld 		pci_config_teardown(&hdl);
692eb9a1df2SHans Rosenfeld 		return (B_FALSE);
693eb9a1df2SHans Rosenfeld 	}
694eb9a1df2SHans Rosenfeld 
695eb9a1df2SHans Rosenfeld 	devsts = PCI_CAP_GET16(hdl, 0, cap_ptr, PCIE_DEVSTS);
696eb9a1df2SHans Rosenfeld 	while ((devsts & PCIE_DEVSTS_TRANS_PENDING) != 0) {
697eb9a1df2SHans Rosenfeld 		if (max_delay_us == 0) {
698eb9a1df2SHans Rosenfeld 			pci_config_teardown(&hdl);
699eb9a1df2SHans Rosenfeld 			return (B_FALSE);
700eb9a1df2SHans Rosenfeld 		}
701eb9a1df2SHans Rosenfeld 
702eb9a1df2SHans Rosenfeld 		/* Poll once every 100 milliseconds up to the timeout. */
703eb9a1df2SHans Rosenfeld 		if (max_delay_us > 100000) {
704eb9a1df2SHans Rosenfeld 			delay(drv_usectohz(100000));
705eb9a1df2SHans Rosenfeld 			max_delay_us -= 100000;
706eb9a1df2SHans Rosenfeld 		} else {
707eb9a1df2SHans Rosenfeld 			delay(drv_usectohz(max_delay_us));
708eb9a1df2SHans Rosenfeld 			max_delay_us = 0;
709eb9a1df2SHans Rosenfeld 		}
710eb9a1df2SHans Rosenfeld 		devsts = PCI_CAP_GET16(hdl, 0, cap_ptr, PCIE_DEVSTS);
711eb9a1df2SHans Rosenfeld 	}
712eb9a1df2SHans Rosenfeld 
713eb9a1df2SHans Rosenfeld 	pci_config_teardown(&hdl);
714eb9a1df2SHans Rosenfeld 	return (B_TRUE);
715eb9a1df2SHans Rosenfeld }
716eb9a1df2SHans Rosenfeld 
717eb9a1df2SHans Rosenfeld static uint_t
ppt_max_completion_tmo_us(dev_info_t * dip)718eb9a1df2SHans Rosenfeld ppt_max_completion_tmo_us(dev_info_t *dip)
719eb9a1df2SHans Rosenfeld {
720eb9a1df2SHans Rosenfeld 	uint_t timo = 0;
721eb9a1df2SHans Rosenfeld 	uint16_t cap_ptr;
722eb9a1df2SHans Rosenfeld 	ddi_acc_handle_t hdl;
723eb9a1df2SHans Rosenfeld 	uint_t timo_ranges[] = {	/* timeout ranges */
724eb9a1df2SHans Rosenfeld 		50000,		/* 50ms */
725eb9a1df2SHans Rosenfeld 		100,		/* 100us */
726eb9a1df2SHans Rosenfeld 		10000,		/* 10ms */
727eb9a1df2SHans Rosenfeld 		0,
728eb9a1df2SHans Rosenfeld 		0,
729eb9a1df2SHans Rosenfeld 		55000,		/* 55ms */
730eb9a1df2SHans Rosenfeld 		210000,		/* 210ms */
731eb9a1df2SHans Rosenfeld 		0,
732eb9a1df2SHans Rosenfeld 		0,
733eb9a1df2SHans Rosenfeld 		900000,		/* 900ms */
734eb9a1df2SHans Rosenfeld 		3500000,	/* 3.5s */
735eb9a1df2SHans Rosenfeld 		0,
736eb9a1df2SHans Rosenfeld 		0,
737eb9a1df2SHans Rosenfeld 		13000000,	/* 13s */
738eb9a1df2SHans Rosenfeld 		64000000,	/* 64s */
739eb9a1df2SHans Rosenfeld 		0
740eb9a1df2SHans Rosenfeld 	};
741eb9a1df2SHans Rosenfeld 
742eb9a1df2SHans Rosenfeld 	if (pci_config_setup(dip, &hdl) != DDI_SUCCESS)
743eb9a1df2SHans Rosenfeld 		return (50000); /* default 50ms */
744eb9a1df2SHans Rosenfeld 
745eb9a1df2SHans Rosenfeld 	if (PCI_CAP_LOCATE(hdl, PCI_CAP_ID_PCI_E, &cap_ptr) != DDI_SUCCESS)
746eb9a1df2SHans Rosenfeld 		goto out;
747eb9a1df2SHans Rosenfeld 
748eb9a1df2SHans Rosenfeld 	if ((PCI_CAP_GET16(hdl, 0, cap_ptr, PCIE_PCIECAP) &
749eb9a1df2SHans Rosenfeld 	    PCIE_PCIECAP_VER_MASK) < PCIE_PCIECAP_VER_2_0)
750eb9a1df2SHans Rosenfeld 		goto out;
751eb9a1df2SHans Rosenfeld 
7529ddf79f9SAndy Fiddaman 	if ((PCI_CAP_GET32(hdl, 0, cap_ptr, PCIE_DEVCAP2) &
753eb9a1df2SHans Rosenfeld 	    PCIE_DEVCTL2_COM_TO_RANGE_MASK) == 0)
754eb9a1df2SHans Rosenfeld 		goto out;
755eb9a1df2SHans Rosenfeld 
756eb9a1df2SHans Rosenfeld 	timo = timo_ranges[PCI_CAP_GET16(hdl, 0, cap_ptr, PCIE_DEVCTL2) &
757eb9a1df2SHans Rosenfeld 	    PCIE_DEVCAP2_COM_TO_RANGE_MASK];
758eb9a1df2SHans Rosenfeld 
759eb9a1df2SHans Rosenfeld out:
760eb9a1df2SHans Rosenfeld 	if (timo == 0)
761eb9a1df2SHans Rosenfeld 		timo = 50000; /* default 50ms */
762eb9a1df2SHans Rosenfeld 
763eb9a1df2SHans Rosenfeld 	pci_config_teardown(&hdl);
764eb9a1df2SHans Rosenfeld 	return (timo);
765eb9a1df2SHans Rosenfeld }
766eb9a1df2SHans Rosenfeld 
767eb9a1df2SHans Rosenfeld static boolean_t
ppt_flr(dev_info_t * dip,boolean_t force)768eb9a1df2SHans Rosenfeld ppt_flr(dev_info_t *dip, boolean_t force)
769eb9a1df2SHans Rosenfeld {
770eb9a1df2SHans Rosenfeld 	uint16_t cap_ptr, ctl, cmd;
771eb9a1df2SHans Rosenfeld 	ddi_acc_handle_t hdl;
772eb9a1df2SHans Rosenfeld 	uint_t compl_delay = 0, max_delay_us;
773eb9a1df2SHans Rosenfeld 
774eb9a1df2SHans Rosenfeld 	if (pci_config_setup(dip, &hdl) != DDI_SUCCESS)
775eb9a1df2SHans Rosenfeld 		return (B_FALSE);
776eb9a1df2SHans Rosenfeld 
777eb9a1df2SHans Rosenfeld 	if (PCI_CAP_LOCATE(hdl, PCI_CAP_ID_PCI_E, &cap_ptr) != DDI_SUCCESS)
778eb9a1df2SHans Rosenfeld 		goto fail;
779eb9a1df2SHans Rosenfeld 
7809ddf79f9SAndy Fiddaman 	if ((PCI_CAP_GET32(hdl, 0, cap_ptr, PCIE_DEVCAP) & PCIE_DEVCAP_FLR)
781eb9a1df2SHans Rosenfeld 	    == 0)
782eb9a1df2SHans Rosenfeld 		goto fail;
783eb9a1df2SHans Rosenfeld 
784eb9a1df2SHans Rosenfeld 	max_delay_us = MAX(ppt_max_completion_tmo_us(dip), 10000);
785eb9a1df2SHans Rosenfeld 
786eb9a1df2SHans Rosenfeld 	/*
787eb9a1df2SHans Rosenfeld 	 * Disable busmastering to prevent generation of new transactions while
788eb9a1df2SHans Rosenfeld 	 * waiting for the device to go idle.  If the idle timeout fails, the
789eb9a1df2SHans Rosenfeld 	 * command register is restored which will re-enable busmastering.
790eb9a1df2SHans Rosenfeld 	 */
791eb9a1df2SHans Rosenfeld 	cmd = pci_config_get16(hdl, PCI_CONF_COMM);
792eb9a1df2SHans Rosenfeld 	pci_config_put16(hdl, PCI_CONF_COMM, cmd & ~PCI_COMM_ME);
793eb9a1df2SHans Rosenfeld 	if (!ppt_wait_for_pending_txn(dip, max_delay_us)) {
794eb9a1df2SHans Rosenfeld 		if (!force) {
795eb9a1df2SHans Rosenfeld 			pci_config_put16(hdl, PCI_CONF_COMM, cmd);
796eb9a1df2SHans Rosenfeld 			goto fail;
797eb9a1df2SHans Rosenfeld 		}
798eb9a1df2SHans Rosenfeld 		dev_err(dip, CE_WARN,
799eb9a1df2SHans Rosenfeld 		    "?Resetting with transactions pending after %u us\n",
800eb9a1df2SHans Rosenfeld 		    max_delay_us);
801eb9a1df2SHans Rosenfeld 
802eb9a1df2SHans Rosenfeld 		/*
803eb9a1df2SHans Rosenfeld 		 * Extend the post-FLR delay to cover the maximum Completion
804eb9a1df2SHans Rosenfeld 		 * Timeout delay of anything in flight during the FLR delay.
805eb9a1df2SHans Rosenfeld 		 * Enforce a minimum delay of at least 10ms.
806eb9a1df2SHans Rosenfeld 		 */
807eb9a1df2SHans Rosenfeld 		compl_delay = MAX(10, (ppt_max_completion_tmo_us(dip) / 1000));
808eb9a1df2SHans Rosenfeld 	}
809eb9a1df2SHans Rosenfeld 
810eb9a1df2SHans Rosenfeld 	/* Initiate the reset. */
811eb9a1df2SHans Rosenfeld 	ctl = PCI_CAP_GET16(hdl, 0, cap_ptr, PCIE_DEVCTL);
812eb9a1df2SHans Rosenfeld 	(void) PCI_CAP_PUT16(hdl, 0, cap_ptr, PCIE_DEVCTL,
813eb9a1df2SHans Rosenfeld 	    ctl | PCIE_DEVCTL_INITIATE_FLR);
814eb9a1df2SHans Rosenfeld 
815eb9a1df2SHans Rosenfeld 	/* Wait for at least 100ms */
816eb9a1df2SHans Rosenfeld 	delay(drv_usectohz((100 + compl_delay) * 1000));
817eb9a1df2SHans Rosenfeld 
818eb9a1df2SHans Rosenfeld 	pci_config_teardown(&hdl);
819eb9a1df2SHans Rosenfeld 	return (B_TRUE);
820eb9a1df2SHans Rosenfeld 
821eb9a1df2SHans Rosenfeld fail:
822eb9a1df2SHans Rosenfeld 	/*
823eb9a1df2SHans Rosenfeld 	 * TODO: If the FLR fails for some reason, we should attempt a reset
824eb9a1df2SHans Rosenfeld 	 * using the PCI power management facilities (if possible).
825eb9a1df2SHans Rosenfeld 	 */
826eb9a1df2SHans Rosenfeld 	pci_config_teardown(&hdl);
827eb9a1df2SHans Rosenfeld 	return (B_FALSE);
828eb9a1df2SHans Rosenfeld }
829eb9a1df2SHans Rosenfeld 
8306960cd89SAndy Fiddaman static int
ppt_findf(struct vm * vm,int fd,struct pptdev ** pptp)8316960cd89SAndy Fiddaman ppt_findf(struct vm *vm, int fd, struct pptdev **pptp)
832eb9a1df2SHans Rosenfeld {
833eb9a1df2SHans Rosenfeld 	struct pptdev *ppt = NULL;
834eb9a1df2SHans Rosenfeld 	file_t *fp;
835eb9a1df2SHans Rosenfeld 	vattr_t va;
8366960cd89SAndy Fiddaman 	int err = 0;
837eb9a1df2SHans Rosenfeld 
8386960cd89SAndy Fiddaman 	ASSERT(MUTEX_HELD(&pptdev_mtx));
8396960cd89SAndy Fiddaman 
8406960cd89SAndy Fiddaman 	if ((fp = getf(fd)) == NULL)
8416960cd89SAndy Fiddaman 		return (EBADF);
842eb9a1df2SHans Rosenfeld 
843eb9a1df2SHans Rosenfeld 	va.va_mask = AT_RDEV;
844eb9a1df2SHans Rosenfeld 	if (VOP_GETATTR(fp->f_vnode, &va, NO_FOLLOW, fp->f_cred, NULL) != 0 ||
8456960cd89SAndy Fiddaman 	    getmajor(va.va_rdev) != ppt_major) {
8466960cd89SAndy Fiddaman 		err = EBADF;
847eb9a1df2SHans Rosenfeld 		goto fail;
8486960cd89SAndy Fiddaman 	}
849eb9a1df2SHans Rosenfeld 
850eb9a1df2SHans Rosenfeld 	ppt = ddi_get_soft_state(ppt_state, getminor(va.va_rdev));
851eb9a1df2SHans Rosenfeld 
8526960cd89SAndy Fiddaman 	if (ppt == NULL) {
8536960cd89SAndy Fiddaman 		err = EBADF;
8546960cd89SAndy Fiddaman 		goto fail;
8556960cd89SAndy Fiddaman 	}
8566960cd89SAndy Fiddaman 
8576960cd89SAndy Fiddaman 	if (ppt->vm != vm) {
8586960cd89SAndy Fiddaman 		err = EBUSY;
8596960cd89SAndy Fiddaman 		goto fail;
8606960cd89SAndy Fiddaman 	}
8616960cd89SAndy Fiddaman 
8626960cd89SAndy Fiddaman 	*pptp = ppt;
8636960cd89SAndy Fiddaman 	return (0);
864eb9a1df2SHans Rosenfeld 
865eb9a1df2SHans Rosenfeld fail:
866eb9a1df2SHans Rosenfeld 	releasef(fd);
8676960cd89SAndy Fiddaman 	return (err);
868eb9a1df2SHans Rosenfeld }
869eb9a1df2SHans Rosenfeld 
870eb9a1df2SHans Rosenfeld static void
ppt_unmap_all_mmio(struct vm * vm,struct pptdev * ppt)8712b948146SAndy Fiddaman ppt_unmap_all_mmio(struct vm *vm, struct pptdev *ppt)
872eb9a1df2SHans Rosenfeld {
873eb9a1df2SHans Rosenfeld 	int i;
874eb9a1df2SHans Rosenfeld 	struct pptseg *seg;
875eb9a1df2SHans Rosenfeld 
876eb9a1df2SHans Rosenfeld 	for (i = 0; i < MAX_MMIOSEGS; i++) {
877eb9a1df2SHans Rosenfeld 		seg = &ppt->mmio[i];
878eb9a1df2SHans Rosenfeld 		if (seg->len == 0)
879eb9a1df2SHans Rosenfeld 			continue;
880eb9a1df2SHans Rosenfeld 		(void) vm_unmap_mmio(vm, seg->gpa, seg->len);
881eb9a1df2SHans Rosenfeld 		bzero(seg, sizeof (struct pptseg));
882eb9a1df2SHans Rosenfeld 	}
883eb9a1df2SHans Rosenfeld }
884eb9a1df2SHans Rosenfeld 
885eb9a1df2SHans Rosenfeld static void
ppt_teardown_msi(struct pptdev * ppt)886eb9a1df2SHans Rosenfeld ppt_teardown_msi(struct pptdev *ppt)
887eb9a1df2SHans Rosenfeld {
888eb9a1df2SHans Rosenfeld 	int i;
889eb9a1df2SHans Rosenfeld 
890eb9a1df2SHans Rosenfeld 	if (ppt->msi.num_msgs == 0)
891eb9a1df2SHans Rosenfeld 		return;
892eb9a1df2SHans Rosenfeld 
893eb9a1df2SHans Rosenfeld 	for (i = 0; i < ppt->msi.num_msgs; i++) {
894eb9a1df2SHans Rosenfeld 		int intr_cap;
895eb9a1df2SHans Rosenfeld 
896eb9a1df2SHans Rosenfeld 		(void) ddi_intr_get_cap(ppt->msi.inth[i], &intr_cap);
897eb9a1df2SHans Rosenfeld 		if (intr_cap & DDI_INTR_FLAG_BLOCK)
898eb9a1df2SHans Rosenfeld 			ddi_intr_block_disable(&ppt->msi.inth[i], 1);
899eb9a1df2SHans Rosenfeld 		else
900eb9a1df2SHans Rosenfeld 			ddi_intr_disable(ppt->msi.inth[i]);
901eb9a1df2SHans Rosenfeld 
902eb9a1df2SHans Rosenfeld 		ddi_intr_remove_handler(ppt->msi.inth[i]);
903eb9a1df2SHans Rosenfeld 		ddi_intr_free(ppt->msi.inth[i]);
904eb9a1df2SHans Rosenfeld 
905eb9a1df2SHans Rosenfeld 		ppt->msi.inth[i] = NULL;
906eb9a1df2SHans Rosenfeld 	}
907eb9a1df2SHans Rosenfeld 
908eb9a1df2SHans Rosenfeld 	kmem_free(ppt->msi.inth, ppt->msi.inth_sz);
909eb9a1df2SHans Rosenfeld 	ppt->msi.inth = NULL;
910eb9a1df2SHans Rosenfeld 	ppt->msi.inth_sz = 0;
911eb9a1df2SHans Rosenfeld 	ppt->msi.is_fixed = B_FALSE;
912eb9a1df2SHans Rosenfeld 
913eb9a1df2SHans Rosenfeld 	ppt->msi.num_msgs = 0;
914eb9a1df2SHans Rosenfeld }
915eb9a1df2SHans Rosenfeld 
916eb9a1df2SHans Rosenfeld static void
ppt_teardown_msix_intr(struct pptdev * ppt,int idx)917eb9a1df2SHans Rosenfeld ppt_teardown_msix_intr(struct pptdev *ppt, int idx)
918eb9a1df2SHans Rosenfeld {
919eb9a1df2SHans Rosenfeld 	if (ppt->msix.inth != NULL && ppt->msix.inth[idx] != NULL) {
920eb9a1df2SHans Rosenfeld 		int intr_cap;
921eb9a1df2SHans Rosenfeld 
922eb9a1df2SHans Rosenfeld 		(void) ddi_intr_get_cap(ppt->msix.inth[idx], &intr_cap);
923eb9a1df2SHans Rosenfeld 		if (intr_cap & DDI_INTR_FLAG_BLOCK)
924eb9a1df2SHans Rosenfeld 			ddi_intr_block_disable(&ppt->msix.inth[idx], 1);
925eb9a1df2SHans Rosenfeld 		else
926eb9a1df2SHans Rosenfeld 			ddi_intr_disable(ppt->msix.inth[idx]);
927eb9a1df2SHans Rosenfeld 
928eb9a1df2SHans Rosenfeld 		ddi_intr_remove_handler(ppt->msix.inth[idx]);
929eb9a1df2SHans Rosenfeld 	}
930eb9a1df2SHans Rosenfeld }
931eb9a1df2SHans Rosenfeld 
932eb9a1df2SHans Rosenfeld static void
ppt_teardown_msix(struct pptdev * ppt)933eb9a1df2SHans Rosenfeld ppt_teardown_msix(struct pptdev *ppt)
934eb9a1df2SHans Rosenfeld {
935eb9a1df2SHans Rosenfeld 	uint_t i;
936eb9a1df2SHans Rosenfeld 
937eb9a1df2SHans Rosenfeld 	if (ppt->msix.num_msgs == 0)
938eb9a1df2SHans Rosenfeld 		return;
939eb9a1df2SHans Rosenfeld 
940eb9a1df2SHans Rosenfeld 	for (i = 0; i < ppt->msix.num_msgs; i++)
941eb9a1df2SHans Rosenfeld 		ppt_teardown_msix_intr(ppt, i);
942eb9a1df2SHans Rosenfeld 
943eb9a1df2SHans Rosenfeld 	if (ppt->msix.inth) {
944eb9a1df2SHans Rosenfeld 		for (i = 0; i < ppt->msix.num_msgs; i++)
945eb9a1df2SHans Rosenfeld 			ddi_intr_free(ppt->msix.inth[i]);
946eb9a1df2SHans Rosenfeld 		kmem_free(ppt->msix.inth, ppt->msix.inth_sz);
947eb9a1df2SHans Rosenfeld 		ppt->msix.inth = NULL;
948eb9a1df2SHans Rosenfeld 		ppt->msix.inth_sz = 0;
949eb9a1df2SHans Rosenfeld 		kmem_free(ppt->msix.arg, ppt->msix.arg_sz);
950eb9a1df2SHans Rosenfeld 		ppt->msix.arg = NULL;
951eb9a1df2SHans Rosenfeld 		ppt->msix.arg_sz = 0;
952eb9a1df2SHans Rosenfeld 	}
953eb9a1df2SHans Rosenfeld 
954eb9a1df2SHans Rosenfeld 	ppt->msix.num_msgs = 0;
955eb9a1df2SHans Rosenfeld }
956eb9a1df2SHans Rosenfeld 
957eb9a1df2SHans Rosenfeld int
ppt_assigned_devices(struct vm * vm)958eb9a1df2SHans Rosenfeld ppt_assigned_devices(struct vm *vm)
959eb9a1df2SHans Rosenfeld {
960eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
961eb9a1df2SHans Rosenfeld 	uint_t num = 0;
962eb9a1df2SHans Rosenfeld 
963eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
964eb9a1df2SHans Rosenfeld 	for (ppt = list_head(&pptdev_list); ppt != NULL;
965eb9a1df2SHans Rosenfeld 	    ppt = list_next(&pptdev_list, ppt)) {
966eb9a1df2SHans Rosenfeld 		if (ppt->vm == vm) {
967eb9a1df2SHans Rosenfeld 			num++;
968eb9a1df2SHans Rosenfeld 		}
969eb9a1df2SHans Rosenfeld 	}
970eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
971eb9a1df2SHans Rosenfeld 	return (num);
972eb9a1df2SHans Rosenfeld }
973eb9a1df2SHans Rosenfeld 
974eb9a1df2SHans Rosenfeld boolean_t
ppt_is_mmio(struct vm * vm,vm_paddr_t gpa)975eb9a1df2SHans Rosenfeld ppt_is_mmio(struct vm *vm, vm_paddr_t gpa)
976eb9a1df2SHans Rosenfeld {
977eb9a1df2SHans Rosenfeld 	struct pptdev *ppt = list_head(&pptdev_list);
978eb9a1df2SHans Rosenfeld 
979eb9a1df2SHans Rosenfeld 	/* XXX: this should probably be restructured to avoid the lock */
980eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
981eb9a1df2SHans Rosenfeld 	for (ppt = list_head(&pptdev_list); ppt != NULL;
982eb9a1df2SHans Rosenfeld 	    ppt = list_next(&pptdev_list, ppt)) {
983eb9a1df2SHans Rosenfeld 		if (ppt->vm != vm) {
984eb9a1df2SHans Rosenfeld 			continue;
985eb9a1df2SHans Rosenfeld 		}
986eb9a1df2SHans Rosenfeld 
987eb9a1df2SHans Rosenfeld 		for (uint_t i = 0; i < MAX_MMIOSEGS; i++) {
988eb9a1df2SHans Rosenfeld 			struct pptseg *seg = &ppt->mmio[i];
989eb9a1df2SHans Rosenfeld 
990eb9a1df2SHans Rosenfeld 			if (seg->len == 0)
991eb9a1df2SHans Rosenfeld 				continue;
992eb9a1df2SHans Rosenfeld 			if (gpa >= seg->gpa && gpa < seg->gpa + seg->len) {
993eb9a1df2SHans Rosenfeld 				mutex_exit(&pptdev_mtx);
994eb9a1df2SHans Rosenfeld 				return (B_TRUE);
995eb9a1df2SHans Rosenfeld 			}
996eb9a1df2SHans Rosenfeld 		}
997eb9a1df2SHans Rosenfeld 	}
998eb9a1df2SHans Rosenfeld 
999eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
1000eb9a1df2SHans Rosenfeld 	return (B_FALSE);
1001eb9a1df2SHans Rosenfeld }
1002eb9a1df2SHans Rosenfeld 
1003eb9a1df2SHans Rosenfeld int
ppt_assign_device(struct vm * vm,int pptfd)1004eb9a1df2SHans Rosenfeld ppt_assign_device(struct vm *vm, int pptfd)
1005eb9a1df2SHans Rosenfeld {
1006eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
1007eb9a1df2SHans Rosenfeld 	int err = 0;
1008eb9a1df2SHans Rosenfeld 
1009eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
10106960cd89SAndy Fiddaman 	/* Passing NULL requires the device to be unowned. */
10116960cd89SAndy Fiddaman 	err = ppt_findf(NULL, pptfd, &ppt);
10126960cd89SAndy Fiddaman 	if (err != 0) {
1013eb9a1df2SHans Rosenfeld 		mutex_exit(&pptdev_mtx);
10146960cd89SAndy Fiddaman 		return (err);
1015eb9a1df2SHans Rosenfeld 	}
1016eb9a1df2SHans Rosenfeld 
1017eb9a1df2SHans Rosenfeld 	if (pci_save_config_regs(ppt->pptd_dip) != DDI_SUCCESS) {
1018eb9a1df2SHans Rosenfeld 		err = EIO;
1019eb9a1df2SHans Rosenfeld 		goto done;
1020eb9a1df2SHans Rosenfeld 	}
1021eb9a1df2SHans Rosenfeld 	ppt_flr(ppt->pptd_dip, B_TRUE);
1022eb9a1df2SHans Rosenfeld 
1023eb9a1df2SHans Rosenfeld 	/*
1024eb9a1df2SHans Rosenfeld 	 * Restore the device state after reset and then perform another save
1025eb9a1df2SHans Rosenfeld 	 * so the "pristine" state can be restored when the device is removed
1026eb9a1df2SHans Rosenfeld 	 * from the guest.
1027eb9a1df2SHans Rosenfeld 	 */
1028eb9a1df2SHans Rosenfeld 	if (pci_restore_config_regs(ppt->pptd_dip) != DDI_SUCCESS ||
1029eb9a1df2SHans Rosenfeld 	    pci_save_config_regs(ppt->pptd_dip) != DDI_SUCCESS) {
1030eb9a1df2SHans Rosenfeld 		err = EIO;
1031eb9a1df2SHans Rosenfeld 		goto done;
1032eb9a1df2SHans Rosenfeld 	}
1033eb9a1df2SHans Rosenfeld 
1034eb9a1df2SHans Rosenfeld 	ppt->vm = vm;
1035eb9a1df2SHans Rosenfeld 	iommu_remove_device(iommu_host_domain(), pci_get_bdf(ppt->pptd_dip));
1036eb9a1df2SHans Rosenfeld 	iommu_add_device(vm_iommu_domain(vm), pci_get_bdf(ppt->pptd_dip));
1037eb9a1df2SHans Rosenfeld 	pf_set_passthru(ppt->pptd_dip, B_TRUE);
1038eb9a1df2SHans Rosenfeld 
1039eb9a1df2SHans Rosenfeld done:
1040eb9a1df2SHans Rosenfeld 	releasef(pptfd);
1041eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
1042eb9a1df2SHans Rosenfeld 	return (err);
1043eb9a1df2SHans Rosenfeld }
1044eb9a1df2SHans Rosenfeld 
1045eb9a1df2SHans Rosenfeld static void
ppt_reset_pci_power_state(dev_info_t * dip)1046eb9a1df2SHans Rosenfeld ppt_reset_pci_power_state(dev_info_t *dip)
1047eb9a1df2SHans Rosenfeld {
1048eb9a1df2SHans Rosenfeld 	ddi_acc_handle_t cfg;
1049eb9a1df2SHans Rosenfeld 	uint16_t cap_ptr;
1050eb9a1df2SHans Rosenfeld 
1051eb9a1df2SHans Rosenfeld 	if (pci_config_setup(dip, &cfg) != DDI_SUCCESS)
1052eb9a1df2SHans Rosenfeld 		return;
1053eb9a1df2SHans Rosenfeld 
1054eb9a1df2SHans Rosenfeld 	if (PCI_CAP_LOCATE(cfg, PCI_CAP_ID_PM, &cap_ptr) == DDI_SUCCESS) {
1055eb9a1df2SHans Rosenfeld 		uint16_t val;
1056eb9a1df2SHans Rosenfeld 
1057eb9a1df2SHans Rosenfeld 		val = PCI_CAP_GET16(cfg, 0, cap_ptr, PCI_PMCSR);
1058eb9a1df2SHans Rosenfeld 		if ((val & PCI_PMCSR_STATE_MASK) != PCI_PMCSR_D0) {
1059eb9a1df2SHans Rosenfeld 			val = (val & ~PCI_PMCSR_STATE_MASK) | PCI_PMCSR_D0;
1060eb9a1df2SHans Rosenfeld 			(void) PCI_CAP_PUT16(cfg, 0, cap_ptr, PCI_PMCSR,
1061eb9a1df2SHans Rosenfeld 			    val);
1062eb9a1df2SHans Rosenfeld 		}
1063eb9a1df2SHans Rosenfeld 	}
1064eb9a1df2SHans Rosenfeld 
1065eb9a1df2SHans Rosenfeld 	pci_config_teardown(&cfg);
1066eb9a1df2SHans Rosenfeld }
1067eb9a1df2SHans Rosenfeld 
1068eb9a1df2SHans Rosenfeld static void
ppt_do_unassign(struct pptdev * ppt)1069eb9a1df2SHans Rosenfeld ppt_do_unassign(struct pptdev *ppt)
1070eb9a1df2SHans Rosenfeld {
1071eb9a1df2SHans Rosenfeld 	struct vm *vm = ppt->vm;
1072eb9a1df2SHans Rosenfeld 
1073eb9a1df2SHans Rosenfeld 	ASSERT3P(vm, !=, NULL);
1074eb9a1df2SHans Rosenfeld 	ASSERT(MUTEX_HELD(&pptdev_mtx));
1075eb9a1df2SHans Rosenfeld 
1076eb9a1df2SHans Rosenfeld 
1077eb9a1df2SHans Rosenfeld 	ppt_flr(ppt->pptd_dip, B_TRUE);
1078eb9a1df2SHans Rosenfeld 
1079eb9a1df2SHans Rosenfeld 	/*
1080eb9a1df2SHans Rosenfeld 	 * Restore from the state saved during device assignment.
1081eb9a1df2SHans Rosenfeld 	 * If the device power state has been altered, that must be remedied
1082eb9a1df2SHans Rosenfeld 	 * first, as it will reset register state during the transition.
1083eb9a1df2SHans Rosenfeld 	 */
1084eb9a1df2SHans Rosenfeld 	ppt_reset_pci_power_state(ppt->pptd_dip);
1085eb9a1df2SHans Rosenfeld 	(void) pci_restore_config_regs(ppt->pptd_dip);
1086eb9a1df2SHans Rosenfeld 
1087eb9a1df2SHans Rosenfeld 	pf_set_passthru(ppt->pptd_dip, B_FALSE);
1088eb9a1df2SHans Rosenfeld 
10892b948146SAndy Fiddaman 	ppt_unmap_all_mmio(vm, ppt);
1090eb9a1df2SHans Rosenfeld 	ppt_teardown_msi(ppt);
1091eb9a1df2SHans Rosenfeld 	ppt_teardown_msix(ppt);
1092eb9a1df2SHans Rosenfeld 	iommu_remove_device(vm_iommu_domain(vm), pci_get_bdf(ppt->pptd_dip));
1093eb9a1df2SHans Rosenfeld 	iommu_add_device(iommu_host_domain(), pci_get_bdf(ppt->pptd_dip));
1094eb9a1df2SHans Rosenfeld 	ppt->vm = NULL;
1095eb9a1df2SHans Rosenfeld }
1096eb9a1df2SHans Rosenfeld 
1097eb9a1df2SHans Rosenfeld int
ppt_unassign_device(struct vm * vm,int pptfd)1098eb9a1df2SHans Rosenfeld ppt_unassign_device(struct vm *vm, int pptfd)
1099eb9a1df2SHans Rosenfeld {
1100eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
1101eb9a1df2SHans Rosenfeld 	int err = 0;
1102eb9a1df2SHans Rosenfeld 
1103eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
11046960cd89SAndy Fiddaman 	err = ppt_findf(vm, pptfd, &ppt);
11056960cd89SAndy Fiddaman 	if (err != 0) {
1106eb9a1df2SHans Rosenfeld 		mutex_exit(&pptdev_mtx);
11076960cd89SAndy Fiddaman 		return (err);
1108eb9a1df2SHans Rosenfeld 	}
1109eb9a1df2SHans Rosenfeld 
1110eb9a1df2SHans Rosenfeld 	ppt_do_unassign(ppt);
1111eb9a1df2SHans Rosenfeld 
1112eb9a1df2SHans Rosenfeld 	releasef(pptfd);
1113eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
1114eb9a1df2SHans Rosenfeld 	return (err);
1115eb9a1df2SHans Rosenfeld }
1116eb9a1df2SHans Rosenfeld 
1117e0994bd2SPatrick Mooney void
ppt_unassign_all(struct vm * vm)1118eb9a1df2SHans Rosenfeld ppt_unassign_all(struct vm *vm)
1119eb9a1df2SHans Rosenfeld {
1120eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
1121eb9a1df2SHans Rosenfeld 
1122eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
1123eb9a1df2SHans Rosenfeld 	for (ppt = list_head(&pptdev_list); ppt != NULL;
1124eb9a1df2SHans Rosenfeld 	    ppt = list_next(&pptdev_list, ppt)) {
1125eb9a1df2SHans Rosenfeld 		if (ppt->vm == vm) {
1126eb9a1df2SHans Rosenfeld 			ppt_do_unassign(ppt);
1127eb9a1df2SHans Rosenfeld 		}
1128eb9a1df2SHans Rosenfeld 	}
1129eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
1130eb9a1df2SHans Rosenfeld }
1131eb9a1df2SHans Rosenfeld 
1132eb9a1df2SHans Rosenfeld int
ppt_map_mmio(struct vm * vm,int pptfd,vm_paddr_t gpa,size_t len,vm_paddr_t hpa)1133eb9a1df2SHans Rosenfeld ppt_map_mmio(struct vm *vm, int pptfd, vm_paddr_t gpa, size_t len,
1134eb9a1df2SHans Rosenfeld     vm_paddr_t hpa)
1135eb9a1df2SHans Rosenfeld {
1136eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
1137eb9a1df2SHans Rosenfeld 	int err = 0;
1138eb9a1df2SHans Rosenfeld 
113959d65d31SAndy Fiddaman 	if ((len & PAGEOFFSET) != 0 || len == 0 || (gpa & PAGEOFFSET) != 0 ||
114059d65d31SAndy Fiddaman 	    (hpa & PAGEOFFSET) != 0 || gpa + len < gpa || hpa + len < hpa) {
114159d65d31SAndy Fiddaman 		return (EINVAL);
114259d65d31SAndy Fiddaman 	}
114359d65d31SAndy Fiddaman 
1144eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
11456960cd89SAndy Fiddaman 	err = ppt_findf(vm, pptfd, &ppt);
11466960cd89SAndy Fiddaman 	if (err != 0) {
1147eb9a1df2SHans Rosenfeld 		mutex_exit(&pptdev_mtx);
11486960cd89SAndy Fiddaman 		return (err);
1149eb9a1df2SHans Rosenfeld 	}
1150eb9a1df2SHans Rosenfeld 
1151eb9a1df2SHans Rosenfeld 	/*
1152eb9a1df2SHans Rosenfeld 	 * Ensure that the host-physical range of the requested mapping fits
1153eb9a1df2SHans Rosenfeld 	 * within one of the MMIO BARs of the device.
1154eb9a1df2SHans Rosenfeld 	 */
1155eb9a1df2SHans Rosenfeld 	if (!ppt_bar_verify_mmio(ppt, hpa, len)) {
1156eb9a1df2SHans Rosenfeld 		err = EINVAL;
1157eb9a1df2SHans Rosenfeld 		goto done;
1158eb9a1df2SHans Rosenfeld 	}
1159eb9a1df2SHans Rosenfeld 
1160eb9a1df2SHans Rosenfeld 	for (uint_t i = 0; i < MAX_MMIOSEGS; i++) {
1161eb9a1df2SHans Rosenfeld 		struct pptseg *seg = &ppt->mmio[i];
1162eb9a1df2SHans Rosenfeld 
1163eb9a1df2SHans Rosenfeld 		if (seg->len == 0) {
1164eb9a1df2SHans Rosenfeld 			err = vm_map_mmio(vm, gpa, len, hpa);
1165eb9a1df2SHans Rosenfeld 			if (err == 0) {
1166eb9a1df2SHans Rosenfeld 				seg->gpa = gpa;
1167eb9a1df2SHans Rosenfeld 				seg->len = len;
1168eb9a1df2SHans Rosenfeld 			}
1169eb9a1df2SHans Rosenfeld 			goto done;
1170eb9a1df2SHans Rosenfeld 		}
1171eb9a1df2SHans Rosenfeld 	}
1172eb9a1df2SHans Rosenfeld 	err = ENOSPC;
1173eb9a1df2SHans Rosenfeld 
1174eb9a1df2SHans Rosenfeld done:
1175eb9a1df2SHans Rosenfeld 	releasef(pptfd);
1176eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
11772b948146SAndy Fiddaman 	return (err);
11782b948146SAndy Fiddaman }
11792b948146SAndy Fiddaman 
11802b948146SAndy Fiddaman int
ppt_unmap_mmio(struct vm * vm,int pptfd,vm_paddr_t gpa,size_t len)11812b948146SAndy Fiddaman ppt_unmap_mmio(struct vm *vm, int pptfd, vm_paddr_t gpa, size_t len)
11822b948146SAndy Fiddaman {
11832b948146SAndy Fiddaman 	struct pptdev *ppt;
11842b948146SAndy Fiddaman 	int err = 0;
11852b948146SAndy Fiddaman 	uint_t i;
11862b948146SAndy Fiddaman 
11872b948146SAndy Fiddaman 	mutex_enter(&pptdev_mtx);
11882b948146SAndy Fiddaman 	err = ppt_findf(vm, pptfd, &ppt);
11892b948146SAndy Fiddaman 	if (err != 0) {
11902b948146SAndy Fiddaman 		mutex_exit(&pptdev_mtx);
11912b948146SAndy Fiddaman 		return (err);
11922b948146SAndy Fiddaman 	}
11932b948146SAndy Fiddaman 
11942b948146SAndy Fiddaman 	for (i = 0; i < MAX_MMIOSEGS; i++) {
11952b948146SAndy Fiddaman 		struct pptseg *seg = &ppt->mmio[i];
11962b948146SAndy Fiddaman 
11972b948146SAndy Fiddaman 		if (seg->gpa == gpa && seg->len == len) {
11982b948146SAndy Fiddaman 			err = vm_unmap_mmio(vm, seg->gpa, seg->len);
11992b948146SAndy Fiddaman 			if (err == 0) {
12002b948146SAndy Fiddaman 				seg->gpa = 0;
12012b948146SAndy Fiddaman 				seg->len = 0;
12022b948146SAndy Fiddaman 			}
12032b948146SAndy Fiddaman 			goto out;
12042b948146SAndy Fiddaman 		}
12052b948146SAndy Fiddaman 	}
12062b948146SAndy Fiddaman 	err = ENOENT;
12072b948146SAndy Fiddaman out:
12082b948146SAndy Fiddaman 	releasef(pptfd);
12092b948146SAndy Fiddaman 	mutex_exit(&pptdev_mtx);
1210eb9a1df2SHans Rosenfeld 	return (err);
1211eb9a1df2SHans Rosenfeld }
1212eb9a1df2SHans Rosenfeld 
1213eb9a1df2SHans Rosenfeld static uint_t
pptintr(caddr_t arg,caddr_t unused)1214eb9a1df2SHans Rosenfeld pptintr(caddr_t arg, caddr_t unused)
1215eb9a1df2SHans Rosenfeld {
1216eb9a1df2SHans Rosenfeld 	struct pptintr_arg *pptarg = (struct pptintr_arg *)arg;
1217eb9a1df2SHans Rosenfeld 	struct pptdev *ppt = pptarg->pptdev;
1218eb9a1df2SHans Rosenfeld 
1219eb9a1df2SHans Rosenfeld 	if (ppt->vm != NULL) {
1220eb9a1df2SHans Rosenfeld 		lapic_intr_msi(ppt->vm, pptarg->addr, pptarg->msg_data);
1221eb9a1df2SHans Rosenfeld 	} else {
1222eb9a1df2SHans Rosenfeld 		/*
1223eb9a1df2SHans Rosenfeld 		 * XXX
1224eb9a1df2SHans Rosenfeld 		 * This is not expected to happen - panic?
1225eb9a1df2SHans Rosenfeld 		 */
1226eb9a1df2SHans Rosenfeld 	}
1227eb9a1df2SHans Rosenfeld 
1228eb9a1df2SHans Rosenfeld 	/*
1229eb9a1df2SHans Rosenfeld 	 * For legacy interrupts give other filters a chance in case
1230eb9a1df2SHans Rosenfeld 	 * the interrupt was not generated by the passthrough device.
1231eb9a1df2SHans Rosenfeld 	 */
1232eb9a1df2SHans Rosenfeld 	return (ppt->msi.is_fixed ? DDI_INTR_UNCLAIMED : DDI_INTR_CLAIMED);
1233eb9a1df2SHans Rosenfeld }
1234eb9a1df2SHans Rosenfeld 
1235eb9a1df2SHans Rosenfeld int
ppt_setup_msi(struct vm * vm,int vcpu,int pptfd,uint64_t addr,uint64_t msg,int numvec)1236eb9a1df2SHans Rosenfeld ppt_setup_msi(struct vm *vm, int vcpu, int pptfd, uint64_t addr, uint64_t msg,
1237eb9a1df2SHans Rosenfeld     int numvec)
1238eb9a1df2SHans Rosenfeld {
1239eb9a1df2SHans Rosenfeld 	int i, msi_count, intr_type;
1240eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
1241eb9a1df2SHans Rosenfeld 	int err = 0;
1242eb9a1df2SHans Rosenfeld 
1243eb9a1df2SHans Rosenfeld 	if (numvec < 0 || numvec > MAX_MSIMSGS)
1244eb9a1df2SHans Rosenfeld 		return (EINVAL);
1245eb9a1df2SHans Rosenfeld 
1246eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
12476960cd89SAndy Fiddaman 	err = ppt_findf(vm, pptfd, &ppt);
12486960cd89SAndy Fiddaman 	if (err != 0) {
1249eb9a1df2SHans Rosenfeld 		mutex_exit(&pptdev_mtx);
12506960cd89SAndy Fiddaman 		return (err);
1251eb9a1df2SHans Rosenfeld 	}
12526960cd89SAndy Fiddaman 
12536960cd89SAndy Fiddaman 	/* Reject attempts to enable MSI while MSI-X is active. */
12546960cd89SAndy Fiddaman 	if (ppt->msix.num_msgs != 0 && numvec != 0) {
1255eb9a1df2SHans Rosenfeld 		err = EBUSY;
1256eb9a1df2SHans Rosenfeld 		goto done;
1257eb9a1df2SHans Rosenfeld 	}
1258eb9a1df2SHans Rosenfeld 
1259eb9a1df2SHans Rosenfeld 	/* Free any allocated resources */
1260eb9a1df2SHans Rosenfeld 	ppt_teardown_msi(ppt);
1261eb9a1df2SHans Rosenfeld 
1262eb9a1df2SHans Rosenfeld 	if (numvec == 0) {
1263eb9a1df2SHans Rosenfeld 		/* nothing more to do */
1264eb9a1df2SHans Rosenfeld 		goto done;
1265eb9a1df2SHans Rosenfeld 	}
1266eb9a1df2SHans Rosenfeld 
1267eb9a1df2SHans Rosenfeld 	if (ddi_intr_get_navail(ppt->pptd_dip, DDI_INTR_TYPE_MSI,
1268eb9a1df2SHans Rosenfeld 	    &msi_count) != DDI_SUCCESS) {
1269eb9a1df2SHans Rosenfeld 		if (ddi_intr_get_navail(ppt->pptd_dip, DDI_INTR_TYPE_FIXED,
1270eb9a1df2SHans Rosenfeld 		    &msi_count) != DDI_SUCCESS) {
1271eb9a1df2SHans Rosenfeld 			err = EINVAL;
1272eb9a1df2SHans Rosenfeld 			goto done;
1273eb9a1df2SHans Rosenfeld 		}
1274eb9a1df2SHans Rosenfeld 
1275eb9a1df2SHans Rosenfeld 		intr_type = DDI_INTR_TYPE_FIXED;
1276eb9a1df2SHans Rosenfeld 		ppt->msi.is_fixed = B_TRUE;
1277eb9a1df2SHans Rosenfeld 	} else {
1278eb9a1df2SHans Rosenfeld 		intr_type = DDI_INTR_TYPE_MSI;
1279eb9a1df2SHans Rosenfeld 	}
1280eb9a1df2SHans Rosenfeld 
1281eb9a1df2SHans Rosenfeld 	/*
1282eb9a1df2SHans Rosenfeld 	 * The device must be capable of supporting the number of vectors
1283eb9a1df2SHans Rosenfeld 	 * the guest wants to allocate.
1284eb9a1df2SHans Rosenfeld 	 */
1285eb9a1df2SHans Rosenfeld 	if (numvec > msi_count) {
1286eb9a1df2SHans Rosenfeld 		err = EINVAL;
1287eb9a1df2SHans Rosenfeld 		goto done;
1288eb9a1df2SHans Rosenfeld 	}
1289eb9a1df2SHans Rosenfeld 
1290eb9a1df2SHans Rosenfeld 	ppt->msi.inth_sz = numvec * sizeof (ddi_intr_handle_t);
1291eb9a1df2SHans Rosenfeld 	ppt->msi.inth = kmem_zalloc(ppt->msi.inth_sz, KM_SLEEP);
1292eb9a1df2SHans Rosenfeld 	if (ddi_intr_alloc(ppt->pptd_dip, ppt->msi.inth, intr_type, 0,
1293eb9a1df2SHans Rosenfeld 	    numvec, &msi_count, 0) != DDI_SUCCESS) {
1294eb9a1df2SHans Rosenfeld 		kmem_free(ppt->msi.inth, ppt->msi.inth_sz);
1295eb9a1df2SHans Rosenfeld 		err = EINVAL;
1296eb9a1df2SHans Rosenfeld 		goto done;
1297eb9a1df2SHans Rosenfeld 	}
1298eb9a1df2SHans Rosenfeld 
1299eb9a1df2SHans Rosenfeld 	/* Verify that we got as many vectors as the guest requested */
1300eb9a1df2SHans Rosenfeld 	if (numvec != msi_count) {
1301eb9a1df2SHans Rosenfeld 		ppt_teardown_msi(ppt);
1302eb9a1df2SHans Rosenfeld 		err = EINVAL;
1303eb9a1df2SHans Rosenfeld 		goto done;
1304eb9a1df2SHans Rosenfeld 	}
1305eb9a1df2SHans Rosenfeld 
1306eb9a1df2SHans Rosenfeld 	/* Set up & enable interrupt handler for each vector. */
1307eb9a1df2SHans Rosenfeld 	for (i = 0; i < numvec; i++) {
1308eb9a1df2SHans Rosenfeld 		int res, intr_cap = 0;
1309eb9a1df2SHans Rosenfeld 
1310eb9a1df2SHans Rosenfeld 		ppt->msi.num_msgs = i + 1;
1311eb9a1df2SHans Rosenfeld 		ppt->msi.arg[i].pptdev = ppt;
1312eb9a1df2SHans Rosenfeld 		ppt->msi.arg[i].addr = addr;
1313eb9a1df2SHans Rosenfeld 		ppt->msi.arg[i].msg_data = msg + i;
1314eb9a1df2SHans Rosenfeld 
1315eb9a1df2SHans Rosenfeld 		if (ddi_intr_add_handler(ppt->msi.inth[i], pptintr,
1316eb9a1df2SHans Rosenfeld 		    &ppt->msi.arg[i], NULL) != DDI_SUCCESS)
1317eb9a1df2SHans Rosenfeld 			break;
1318eb9a1df2SHans Rosenfeld 
1319eb9a1df2SHans Rosenfeld 		(void) ddi_intr_get_cap(ppt->msi.inth[i], &intr_cap);
1320eb9a1df2SHans Rosenfeld 		if (intr_cap & DDI_INTR_FLAG_BLOCK)
1321eb9a1df2SHans Rosenfeld 			res = ddi_intr_block_enable(&ppt->msi.inth[i], 1);
1322eb9a1df2SHans Rosenfeld 		else
1323eb9a1df2SHans Rosenfeld 			res = ddi_intr_enable(ppt->msi.inth[i]);
1324eb9a1df2SHans Rosenfeld 
1325eb9a1df2SHans Rosenfeld 		if (res != DDI_SUCCESS)
1326eb9a1df2SHans Rosenfeld 			break;
1327eb9a1df2SHans Rosenfeld 	}
1328eb9a1df2SHans Rosenfeld 	if (i < numvec) {
1329eb9a1df2SHans Rosenfeld 		ppt_teardown_msi(ppt);
1330eb9a1df2SHans Rosenfeld 		err = ENXIO;
1331eb9a1df2SHans Rosenfeld 	}
1332eb9a1df2SHans Rosenfeld 
1333eb9a1df2SHans Rosenfeld done:
1334eb9a1df2SHans Rosenfeld 	releasef(pptfd);
1335eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
1336eb9a1df2SHans Rosenfeld 	return (err);
1337eb9a1df2SHans Rosenfeld }
1338eb9a1df2SHans Rosenfeld 
1339eb9a1df2SHans Rosenfeld int
ppt_setup_msix(struct vm * vm,int vcpu,int pptfd,int idx,uint64_t addr,uint64_t msg,uint32_t vector_control)1340eb9a1df2SHans Rosenfeld ppt_setup_msix(struct vm *vm, int vcpu, int pptfd, int idx, uint64_t addr,
1341eb9a1df2SHans Rosenfeld     uint64_t msg, uint32_t vector_control)
1342eb9a1df2SHans Rosenfeld {
1343eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
1344eb9a1df2SHans Rosenfeld 	int numvec, alloced;
1345eb9a1df2SHans Rosenfeld 	int err = 0;
1346eb9a1df2SHans Rosenfeld 
1347eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
13486960cd89SAndy Fiddaman 	err = ppt_findf(vm, pptfd, &ppt);
13496960cd89SAndy Fiddaman 	if (err != 0) {
1350eb9a1df2SHans Rosenfeld 		mutex_exit(&pptdev_mtx);
13516960cd89SAndy Fiddaman 		return (err);
1352eb9a1df2SHans Rosenfeld 	}
13536960cd89SAndy Fiddaman 
13546960cd89SAndy Fiddaman 	/* Reject attempts to enable MSI-X while MSI is active. */
13556960cd89SAndy Fiddaman 	if (ppt->msi.num_msgs != 0) {
1356eb9a1df2SHans Rosenfeld 		err = EBUSY;
1357eb9a1df2SHans Rosenfeld 		goto done;
1358eb9a1df2SHans Rosenfeld 	}
1359eb9a1df2SHans Rosenfeld 
1360eb9a1df2SHans Rosenfeld 	/*
1361eb9a1df2SHans Rosenfeld 	 * First-time configuration:
136284971882SPatrick Mooney 	 *	Allocate the MSI-X table
1363eb9a1df2SHans Rosenfeld 	 *	Allocate the IRQ resources
1364eb9a1df2SHans Rosenfeld 	 *	Set up some variables in ppt->msix
1365eb9a1df2SHans Rosenfeld 	 */
1366eb9a1df2SHans Rosenfeld 	if (ppt->msix.num_msgs == 0) {
1367eb9a1df2SHans Rosenfeld 		dev_info_t *dip = ppt->pptd_dip;
1368eb9a1df2SHans Rosenfeld 
1369eb9a1df2SHans Rosenfeld 		if (ddi_intr_get_navail(dip, DDI_INTR_TYPE_MSIX,
1370eb9a1df2SHans Rosenfeld 		    &numvec) != DDI_SUCCESS) {
1371eb9a1df2SHans Rosenfeld 			err = EINVAL;
1372eb9a1df2SHans Rosenfeld 			goto done;
1373eb9a1df2SHans Rosenfeld 		}
1374eb9a1df2SHans Rosenfeld 
1375eb9a1df2SHans Rosenfeld 		ppt->msix.num_msgs = numvec;
1376eb9a1df2SHans Rosenfeld 
1377eb9a1df2SHans Rosenfeld 		ppt->msix.arg_sz = numvec * sizeof (ppt->msix.arg[0]);
1378eb9a1df2SHans Rosenfeld 		ppt->msix.arg = kmem_zalloc(ppt->msix.arg_sz, KM_SLEEP);
1379eb9a1df2SHans Rosenfeld 		ppt->msix.inth_sz = numvec * sizeof (ddi_intr_handle_t);
1380eb9a1df2SHans Rosenfeld 		ppt->msix.inth = kmem_zalloc(ppt->msix.inth_sz, KM_SLEEP);
1381eb9a1df2SHans Rosenfeld 
1382eb9a1df2SHans Rosenfeld 		if (ddi_intr_alloc(dip, ppt->msix.inth, DDI_INTR_TYPE_MSIX, 0,
1383eb9a1df2SHans Rosenfeld 		    numvec, &alloced, 0) != DDI_SUCCESS) {
1384eb9a1df2SHans Rosenfeld 			kmem_free(ppt->msix.arg, ppt->msix.arg_sz);
1385eb9a1df2SHans Rosenfeld 			kmem_free(ppt->msix.inth, ppt->msix.inth_sz);
1386eb9a1df2SHans Rosenfeld 			ppt->msix.arg = NULL;
1387eb9a1df2SHans Rosenfeld 			ppt->msix.inth = NULL;
1388eb9a1df2SHans Rosenfeld 			ppt->msix.arg_sz = ppt->msix.inth_sz = 0;
1389eb9a1df2SHans Rosenfeld 			err = EINVAL;
1390eb9a1df2SHans Rosenfeld 			goto done;
1391eb9a1df2SHans Rosenfeld 		}
1392eb9a1df2SHans Rosenfeld 
1393eb9a1df2SHans Rosenfeld 		if (numvec != alloced) {
1394eb9a1df2SHans Rosenfeld 			ppt_teardown_msix(ppt);
1395eb9a1df2SHans Rosenfeld 			err = EINVAL;
1396eb9a1df2SHans Rosenfeld 			goto done;
1397eb9a1df2SHans Rosenfeld 		}
1398eb9a1df2SHans Rosenfeld 	}
1399eb9a1df2SHans Rosenfeld 
1400eb9a1df2SHans Rosenfeld 	if (idx >= ppt->msix.num_msgs) {
1401eb9a1df2SHans Rosenfeld 		err = EINVAL;
1402eb9a1df2SHans Rosenfeld 		goto done;
1403eb9a1df2SHans Rosenfeld 	}
1404eb9a1df2SHans Rosenfeld 
1405eb9a1df2SHans Rosenfeld 	if ((vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
1406eb9a1df2SHans Rosenfeld 		int intr_cap, res;
1407eb9a1df2SHans Rosenfeld 
1408eb9a1df2SHans Rosenfeld 		/* Tear down the IRQ if it's already set up */
1409eb9a1df2SHans Rosenfeld 		ppt_teardown_msix_intr(ppt, idx);
1410eb9a1df2SHans Rosenfeld 
1411eb9a1df2SHans Rosenfeld 		ppt->msix.arg[idx].pptdev = ppt;
1412eb9a1df2SHans Rosenfeld 		ppt->msix.arg[idx].addr = addr;
1413eb9a1df2SHans Rosenfeld 		ppt->msix.arg[idx].msg_data = msg;
1414eb9a1df2SHans Rosenfeld 
1415eb9a1df2SHans Rosenfeld 		/* Setup the MSI-X interrupt */
1416eb9a1df2SHans Rosenfeld 		if (ddi_intr_add_handler(ppt->msix.inth[idx], pptintr,
1417eb9a1df2SHans Rosenfeld 		    &ppt->msix.arg[idx], NULL) != DDI_SUCCESS) {
1418eb9a1df2SHans Rosenfeld 			err = ENXIO;
1419eb9a1df2SHans Rosenfeld 			goto done;
1420eb9a1df2SHans Rosenfeld 		}
1421eb9a1df2SHans Rosenfeld 
1422eb9a1df2SHans Rosenfeld 		(void) ddi_intr_get_cap(ppt->msix.inth[idx], &intr_cap);
1423eb9a1df2SHans Rosenfeld 		if (intr_cap & DDI_INTR_FLAG_BLOCK)
1424eb9a1df2SHans Rosenfeld 			res = ddi_intr_block_enable(&ppt->msix.inth[idx], 1);
1425eb9a1df2SHans Rosenfeld 		else
1426eb9a1df2SHans Rosenfeld 			res = ddi_intr_enable(ppt->msix.inth[idx]);
1427eb9a1df2SHans Rosenfeld 
1428eb9a1df2SHans Rosenfeld 		if (res != DDI_SUCCESS) {
1429eb9a1df2SHans Rosenfeld 			ddi_intr_remove_handler(ppt->msix.inth[idx]);
1430eb9a1df2SHans Rosenfeld 			err = ENXIO;
1431eb9a1df2SHans Rosenfeld 			goto done;
1432eb9a1df2SHans Rosenfeld 		}
1433eb9a1df2SHans Rosenfeld 	} else {
1434eb9a1df2SHans Rosenfeld 		/* Masked, tear it down if it's already been set up */
1435eb9a1df2SHans Rosenfeld 		ppt_teardown_msix_intr(ppt, idx);
1436eb9a1df2SHans Rosenfeld 	}
1437eb9a1df2SHans Rosenfeld 
1438eb9a1df2SHans Rosenfeld done:
1439eb9a1df2SHans Rosenfeld 	releasef(pptfd);
1440eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
1441eb9a1df2SHans Rosenfeld 	return (err);
1442eb9a1df2SHans Rosenfeld }
1443eb9a1df2SHans Rosenfeld 
1444eb9a1df2SHans Rosenfeld int
ppt_get_limits(struct vm * vm,int pptfd,int * msilimit,int * msixlimit)1445eb9a1df2SHans Rosenfeld ppt_get_limits(struct vm *vm, int pptfd, int *msilimit, int *msixlimit)
1446eb9a1df2SHans Rosenfeld {
1447eb9a1df2SHans Rosenfeld 	struct pptdev *ppt;
1448eb9a1df2SHans Rosenfeld 	int err = 0;
1449eb9a1df2SHans Rosenfeld 
1450eb9a1df2SHans Rosenfeld 	mutex_enter(&pptdev_mtx);
14516960cd89SAndy Fiddaman 	err = ppt_findf(vm, pptfd, &ppt);
14526960cd89SAndy Fiddaman 	if (err != 0) {
1453eb9a1df2SHans Rosenfeld 		mutex_exit(&pptdev_mtx);
14546960cd89SAndy Fiddaman 		return (err);
1455eb9a1df2SHans Rosenfeld 	}
1456eb9a1df2SHans Rosenfeld 
1457eb9a1df2SHans Rosenfeld 	if (ddi_intr_get_navail(ppt->pptd_dip, DDI_INTR_TYPE_MSI,
1458eb9a1df2SHans Rosenfeld 	    msilimit) != DDI_SUCCESS) {
1459eb9a1df2SHans Rosenfeld 		*msilimit = -1;
1460eb9a1df2SHans Rosenfeld 	}
1461eb9a1df2SHans Rosenfeld 	if (ddi_intr_get_navail(ppt->pptd_dip, DDI_INTR_TYPE_MSIX,
1462eb9a1df2SHans Rosenfeld 	    msixlimit) != DDI_SUCCESS) {
1463eb9a1df2SHans Rosenfeld 		*msixlimit = -1;
1464eb9a1df2SHans Rosenfeld 	}
1465eb9a1df2SHans Rosenfeld 
14666960cd89SAndy Fiddaman 	releasef(pptfd);
14676960cd89SAndy Fiddaman 	mutex_exit(&pptdev_mtx);
14686960cd89SAndy Fiddaman 	return (err);
14696960cd89SAndy Fiddaman }
14706960cd89SAndy Fiddaman 
14716960cd89SAndy Fiddaman int
ppt_disable_msix(struct vm * vm,int pptfd)14726960cd89SAndy Fiddaman ppt_disable_msix(struct vm *vm, int pptfd)
14736960cd89SAndy Fiddaman {
14746960cd89SAndy Fiddaman 	struct pptdev *ppt;
14756960cd89SAndy Fiddaman 	int err = 0;
14766960cd89SAndy Fiddaman 
14776960cd89SAndy Fiddaman 	mutex_enter(&pptdev_mtx);
14786960cd89SAndy Fiddaman 	err = ppt_findf(vm, pptfd, &ppt);
14796960cd89SAndy Fiddaman 	if (err != 0) {
14806960cd89SAndy Fiddaman 		mutex_exit(&pptdev_mtx);
14816960cd89SAndy Fiddaman 		return (err);
14826960cd89SAndy Fiddaman 	}
14836960cd89SAndy Fiddaman 
14846960cd89SAndy Fiddaman 	ppt_teardown_msix(ppt);
14856960cd89SAndy Fiddaman 
1486eb9a1df2SHans Rosenfeld 	releasef(pptfd);
1487eb9a1df2SHans Rosenfeld 	mutex_exit(&pptdev_mtx);
1488eb9a1df2SHans Rosenfeld 	return (err);
1489eb9a1df2SHans Rosenfeld }
1490