xref: /freebsd-src/sys/dev/pci/pci_iov.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
19bfb1e36SRyan Stone /*-
2faf139ccSRyan Stone  * Copyright (c) 2013-2015 Sandvine Inc.
39bfb1e36SRyan Stone  * All rights reserved.
49bfb1e36SRyan Stone  *
59bfb1e36SRyan Stone  * Redistribution and use in source and binary forms, with or without
69bfb1e36SRyan Stone  * modification, are permitted provided that the following conditions
79bfb1e36SRyan Stone  * are met:
89bfb1e36SRyan Stone  * 1. Redistributions of source code must retain the above copyright
99bfb1e36SRyan Stone  *    notice, this list of conditions and the following disclaimer.
109bfb1e36SRyan Stone  * 2. Redistributions in binary form must reproduce the above copyright
119bfb1e36SRyan Stone  *    notice, this list of conditions and the following disclaimer in the
129bfb1e36SRyan Stone  *    documentation and/or other materials provided with the distribution.
139bfb1e36SRyan Stone  *
149bfb1e36SRyan Stone  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
159bfb1e36SRyan Stone  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
169bfb1e36SRyan Stone  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
179bfb1e36SRyan Stone  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
189bfb1e36SRyan Stone  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
199bfb1e36SRyan Stone  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
209bfb1e36SRyan Stone  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
219bfb1e36SRyan Stone  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
229bfb1e36SRyan Stone  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
239bfb1e36SRyan Stone  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
249bfb1e36SRyan Stone  * SUCH DAMAGE.
259bfb1e36SRyan Stone  */
269bfb1e36SRyan Stone 
279bfb1e36SRyan Stone #include <sys/cdefs.h>
289bfb1e36SRyan Stone #include "opt_bus.h"
299bfb1e36SRyan Stone 
309bfb1e36SRyan Stone #include <sys/param.h>
319bfb1e36SRyan Stone #include <sys/conf.h>
329bfb1e36SRyan Stone #include <sys/kernel.h>
339bfb1e36SRyan Stone #include <sys/systm.h>
349bfb1e36SRyan Stone #include <sys/bus.h>
359bfb1e36SRyan Stone #include <sys/fcntl.h>
369bfb1e36SRyan Stone #include <sys/ioccom.h>
379bfb1e36SRyan Stone #include <sys/iov.h>
389bfb1e36SRyan Stone #include <sys/linker.h>
39e2e050c8SConrad Meyer #include <sys/lock.h>
409bfb1e36SRyan Stone #include <sys/malloc.h>
419bfb1e36SRyan Stone #include <sys/module.h>
42e2e050c8SConrad Meyer #include <sys/mutex.h>
439bfb1e36SRyan Stone #include <sys/pciio.h>
449bfb1e36SRyan Stone #include <sys/queue.h>
459bfb1e36SRyan Stone #include <sys/rman.h>
469bfb1e36SRyan Stone #include <sys/sysctl.h>
479bfb1e36SRyan Stone 
489bfb1e36SRyan Stone #include <machine/bus.h>
491191f715SRyan Stone #include <machine/stdarg.h>
501191f715SRyan Stone 
511191f715SRyan Stone #include <sys/nv.h>
521191f715SRyan Stone #include <sys/iov_schema.h>
539bfb1e36SRyan Stone 
549bfb1e36SRyan Stone #include <dev/pci/pcireg.h>
559bfb1e36SRyan Stone #include <dev/pci/pcivar.h>
56f3bb9251SJohn Baldwin #include <dev/pci/pci_iov.h>
579bfb1e36SRyan Stone #include <dev/pci/pci_private.h>
589bfb1e36SRyan Stone #include <dev/pci/pci_iov_private.h>
591191f715SRyan Stone #include <dev/pci/schema_private.h>
609bfb1e36SRyan Stone 
619bfb1e36SRyan Stone #include "pcib_if.h"
629bfb1e36SRyan Stone 
639bfb1e36SRyan Stone static MALLOC_DEFINE(M_SRIOV, "sr_iov", "PCI SR-IOV allocations");
649bfb1e36SRyan Stone 
659bfb1e36SRyan Stone static d_ioctl_t pci_iov_ioctl;
669bfb1e36SRyan Stone 
679bfb1e36SRyan Stone static struct cdevsw iov_cdevsw = {
689bfb1e36SRyan Stone 	.d_version = D_VERSION,
699bfb1e36SRyan Stone 	.d_name = "iov",
709bfb1e36SRyan Stone 	.d_ioctl = pci_iov_ioctl
719bfb1e36SRyan Stone };
729bfb1e36SRyan Stone 
735cc26e63SRyan Stone SYSCTL_DECL(_hw_pci);
745cc26e63SRyan Stone 
755cc26e63SRyan Stone /*
765cc26e63SRyan Stone  * The maximum amount of memory we will allocate for user configuration of an
775cc26e63SRyan Stone  * SR-IOV device.  1MB ought to be enough for anyone, but leave this
785cc26e63SRyan Stone  * configurable just in case.
795cc26e63SRyan Stone  */
805cc26e63SRyan Stone static u_long pci_iov_max_config = 1024 * 1024;
815cc26e63SRyan Stone SYSCTL_ULONG(_hw_pci, OID_AUTO, iov_max_config, CTLFLAG_RWTUN,
825cc26e63SRyan Stone     &pci_iov_max_config, 0, "Maximum allowed size of SR-IOV configuration.");
835cc26e63SRyan Stone 
849bfb1e36SRyan Stone #define IOV_READ(d, r, w) \
859bfb1e36SRyan Stone 	pci_read_config((d)->cfg.dev, (d)->cfg.iov->iov_pos + r, w)
869bfb1e36SRyan Stone 
879bfb1e36SRyan Stone #define IOV_WRITE(d, r, v, w) \
889bfb1e36SRyan Stone 	pci_write_config((d)->cfg.dev, (d)->cfg.iov->iov_pos + r, v, w)
899bfb1e36SRyan Stone 
901191f715SRyan Stone static nvlist_t	*pci_iov_build_schema(nvlist_t **pf_schema,
911191f715SRyan Stone 		    nvlist_t **vf_schema);
921191f715SRyan Stone static void	pci_iov_build_pf_schema(nvlist_t *schema,
931191f715SRyan Stone 		    nvlist_t **driver_schema);
941191f715SRyan Stone static void	pci_iov_build_vf_schema(nvlist_t *schema,
951191f715SRyan Stone 		    nvlist_t **driver_schema);
9694f5c1ccSKonstantin Belousov static int	pci_iov_delete_iov_children(struct pci_devinfo *dinfo);
971191f715SRyan Stone static nvlist_t	*pci_iov_get_pf_subsystem_schema(void);
981191f715SRyan Stone static nvlist_t	*pci_iov_get_vf_subsystem_schema(void);
991191f715SRyan Stone 
1009bfb1e36SRyan Stone int
1010aee83ccSJohn Baldwin pci_iov_attach_name(device_t dev, struct nvlist *pf_schema,
1020aee83ccSJohn Baldwin     struct nvlist *vf_schema, const char *fmt, ...)
1030aee83ccSJohn Baldwin {
1040aee83ccSJohn Baldwin 	char buf[NAME_MAX + 1];
1050aee83ccSJohn Baldwin 	va_list ap;
1060aee83ccSJohn Baldwin 
1070aee83ccSJohn Baldwin 	va_start(ap, fmt);
1080aee83ccSJohn Baldwin 	vsnprintf(buf, sizeof(buf), fmt, ap);
1090aee83ccSJohn Baldwin 	va_end(ap);
1100aee83ccSJohn Baldwin 	return (PCI_IOV_ATTACH(device_get_parent(dev), dev, pf_schema,
1110aee83ccSJohn Baldwin 	    vf_schema, buf));
1120aee83ccSJohn Baldwin }
1130aee83ccSJohn Baldwin 
1140aee83ccSJohn Baldwin int
1151191f715SRyan Stone pci_iov_attach_method(device_t bus, device_t dev, nvlist_t *pf_schema,
1160aee83ccSJohn Baldwin     nvlist_t *vf_schema, const char *name)
1179bfb1e36SRyan Stone {
1189bfb1e36SRyan Stone 	struct pci_devinfo *dinfo;
1199bfb1e36SRyan Stone 	struct pcicfg_iov *iov;
1201191f715SRyan Stone 	nvlist_t *schema;
1219bfb1e36SRyan Stone 	uint32_t version;
1229bfb1e36SRyan Stone 	int error;
1239bfb1e36SRyan Stone 	int iov_pos;
1249bfb1e36SRyan Stone 
1259bfb1e36SRyan Stone 	dinfo = device_get_ivars(dev);
1261191f715SRyan Stone 	schema = NULL;
1279bfb1e36SRyan Stone 
1289bfb1e36SRyan Stone 	error = pci_find_extcap(dev, PCIZ_SRIOV, &iov_pos);
1299bfb1e36SRyan Stone 
1309bfb1e36SRyan Stone 	if (error != 0)
1319bfb1e36SRyan Stone 		return (error);
1329bfb1e36SRyan Stone 
1339bfb1e36SRyan Stone 	version = pci_read_config(dev, iov_pos, 4);
1349bfb1e36SRyan Stone 	if (PCI_EXTCAP_VER(version) != 1) {
1359bfb1e36SRyan Stone 		if (bootverbose)
1369bfb1e36SRyan Stone 			device_printf(dev,
1379bfb1e36SRyan Stone 			    "Unsupported version of SR-IOV (%d) detected\n",
1389bfb1e36SRyan Stone 			    PCI_EXTCAP_VER(version));
1399bfb1e36SRyan Stone 
1409bfb1e36SRyan Stone 		return (ENXIO);
1419bfb1e36SRyan Stone 	}
1429bfb1e36SRyan Stone 
1439bfb1e36SRyan Stone 	iov = malloc(sizeof(*dinfo->cfg.iov), M_SRIOV, M_WAITOK | M_ZERO);
1449bfb1e36SRyan Stone 
1459bfb1e36SRyan Stone 	mtx_lock(&Giant);
1469bfb1e36SRyan Stone 	if (dinfo->cfg.iov != NULL) {
1479bfb1e36SRyan Stone 		error = EBUSY;
1489bfb1e36SRyan Stone 		goto cleanup;
1499bfb1e36SRyan Stone 	}
1501f960e64SMark Johnston 	iov->iov_pf = dev;
1519bfb1e36SRyan Stone 	iov->iov_pos = iov_pos;
1529bfb1e36SRyan Stone 
1531191f715SRyan Stone 	schema = pci_iov_build_schema(&pf_schema, &vf_schema);
1541191f715SRyan Stone 	if (schema == NULL) {
1551191f715SRyan Stone 		error = ENOMEM;
1561191f715SRyan Stone 		goto cleanup;
1571191f715SRyan Stone 	}
158bdc48af2SRyan Stone 
159bdc48af2SRyan Stone 	error = pci_iov_validate_schema(schema);
160bdc48af2SRyan Stone 	if (error != 0)
161bdc48af2SRyan Stone 		goto cleanup;
1621191f715SRyan Stone 	iov->iov_schema = schema;
1631191f715SRyan Stone 
1649bfb1e36SRyan Stone 	iov->iov_cdev = make_dev(&iov_cdevsw, device_get_unit(dev),
1650aee83ccSJohn Baldwin 	    UID_ROOT, GID_WHEEL, 0600, "iov/%s", name);
1669bfb1e36SRyan Stone 
1679bfb1e36SRyan Stone 	if (iov->iov_cdev == NULL) {
1689bfb1e36SRyan Stone 		error = ENOMEM;
1699bfb1e36SRyan Stone 		goto cleanup;
1709bfb1e36SRyan Stone 	}
1719bfb1e36SRyan Stone 
1729bfb1e36SRyan Stone 	dinfo->cfg.iov = iov;
1739bfb1e36SRyan Stone 	iov->iov_cdev->si_drv1 = dinfo;
1749bfb1e36SRyan Stone 	mtx_unlock(&Giant);
1759bfb1e36SRyan Stone 
1769bfb1e36SRyan Stone 	return (0);
1779bfb1e36SRyan Stone 
1789bfb1e36SRyan Stone cleanup:
1791191f715SRyan Stone 	nvlist_destroy(schema);
1801191f715SRyan Stone 	nvlist_destroy(pf_schema);
1811191f715SRyan Stone 	nvlist_destroy(vf_schema);
1829bfb1e36SRyan Stone 	free(iov, M_SRIOV);
1839bfb1e36SRyan Stone 	mtx_unlock(&Giant);
1849bfb1e36SRyan Stone 	return (error);
1859bfb1e36SRyan Stone }
1869bfb1e36SRyan Stone 
1879bfb1e36SRyan Stone int
1889bfb1e36SRyan Stone pci_iov_detach_method(device_t bus, device_t dev)
1899bfb1e36SRyan Stone {
1909bfb1e36SRyan Stone 	struct pci_devinfo *dinfo;
1919bfb1e36SRyan Stone 	struct pcicfg_iov *iov;
19294f5c1ccSKonstantin Belousov 	int error;
1939bfb1e36SRyan Stone 
1949bfb1e36SRyan Stone 	mtx_lock(&Giant);
1959bfb1e36SRyan Stone 	dinfo = device_get_ivars(dev);
1969bfb1e36SRyan Stone 	iov = dinfo->cfg.iov;
1979bfb1e36SRyan Stone 
1989bfb1e36SRyan Stone 	if (iov == NULL) {
1999bfb1e36SRyan Stone 		mtx_unlock(&Giant);
2009bfb1e36SRyan Stone 		return (0);
2019bfb1e36SRyan Stone 	}
2029bfb1e36SRyan Stone 
20394f5c1ccSKonstantin Belousov 	if ((iov->iov_flags & IOV_BUSY) != 0) {
2049bfb1e36SRyan Stone 		mtx_unlock(&Giant);
2059bfb1e36SRyan Stone 		return (EBUSY);
2069bfb1e36SRyan Stone 	}
2079bfb1e36SRyan Stone 
20894f5c1ccSKonstantin Belousov 	error = pci_iov_delete_iov_children(dinfo);
20994f5c1ccSKonstantin Belousov 	if (error != 0) {
21094f5c1ccSKonstantin Belousov 		mtx_unlock(&Giant);
21194f5c1ccSKonstantin Belousov 		return (error);
21294f5c1ccSKonstantin Belousov 	}
21394f5c1ccSKonstantin Belousov 
2149bfb1e36SRyan Stone 	dinfo->cfg.iov = NULL;
2159bfb1e36SRyan Stone 
2169bfb1e36SRyan Stone 	if (iov->iov_cdev) {
2179bfb1e36SRyan Stone 		destroy_dev(iov->iov_cdev);
2189bfb1e36SRyan Stone 		iov->iov_cdev = NULL;
2199bfb1e36SRyan Stone 	}
2201191f715SRyan Stone 	nvlist_destroy(iov->iov_schema);
2219bfb1e36SRyan Stone 
2229bfb1e36SRyan Stone 	free(iov, M_SRIOV);
2239bfb1e36SRyan Stone 	mtx_unlock(&Giant);
2249bfb1e36SRyan Stone 
2259bfb1e36SRyan Stone 	return (0);
2269bfb1e36SRyan Stone }
2279bfb1e36SRyan Stone 
2281191f715SRyan Stone static nvlist_t *
2291191f715SRyan Stone pci_iov_build_schema(nvlist_t **pf, nvlist_t **vf)
2301191f715SRyan Stone {
2311191f715SRyan Stone 	nvlist_t *schema, *pf_driver, *vf_driver;
2321191f715SRyan Stone 
2331191f715SRyan Stone 	/* We always take ownership of the schemas. */
2341191f715SRyan Stone 	pf_driver = *pf;
2351191f715SRyan Stone 	*pf = NULL;
2361191f715SRyan Stone 	vf_driver = *vf;
2371191f715SRyan Stone 	*vf = NULL;
2381191f715SRyan Stone 
2391191f715SRyan Stone 	schema = pci_iov_schema_alloc_node();
2401191f715SRyan Stone 	if (schema == NULL)
2411191f715SRyan Stone 		goto cleanup;
2421191f715SRyan Stone 
2431191f715SRyan Stone 	pci_iov_build_pf_schema(schema, &pf_driver);
2441191f715SRyan Stone 	pci_iov_build_vf_schema(schema, &vf_driver);
2451191f715SRyan Stone 
2461191f715SRyan Stone 	if (nvlist_error(schema) != 0)
2471191f715SRyan Stone 		goto cleanup;
2481191f715SRyan Stone 
2491191f715SRyan Stone 	return (schema);
2501191f715SRyan Stone 
2511191f715SRyan Stone cleanup:
2521191f715SRyan Stone 	nvlist_destroy(schema);
2531191f715SRyan Stone 	nvlist_destroy(pf_driver);
2541191f715SRyan Stone 	nvlist_destroy(vf_driver);
2551191f715SRyan Stone 	return (NULL);
2561191f715SRyan Stone }
2571191f715SRyan Stone 
2581191f715SRyan Stone static void
2591191f715SRyan Stone pci_iov_build_pf_schema(nvlist_t *schema, nvlist_t **driver_schema)
2601191f715SRyan Stone {
2611191f715SRyan Stone 	nvlist_t *pf_schema, *iov_schema;
2621191f715SRyan Stone 
2631191f715SRyan Stone 	pf_schema = pci_iov_schema_alloc_node();
2641191f715SRyan Stone 	if (pf_schema == NULL) {
2651191f715SRyan Stone 		nvlist_set_error(schema, ENOMEM);
2661191f715SRyan Stone 		return;
2671191f715SRyan Stone 	}
2681191f715SRyan Stone 
2691191f715SRyan Stone 	iov_schema = pci_iov_get_pf_subsystem_schema();
2701191f715SRyan Stone 
2711191f715SRyan Stone 	/*
2721191f715SRyan Stone 	 * Note that if either *driver_schema or iov_schema is NULL, then
2731191f715SRyan Stone 	 * nvlist_move_nvlist will put the schema in the error state and
2741191f715SRyan Stone 	 * SR-IOV will fail to initialize later, so we don't have to explicitly
2751191f715SRyan Stone 	 * handle that case.
2761191f715SRyan Stone 	 */
2771191f715SRyan Stone 	nvlist_move_nvlist(pf_schema, DRIVER_CONFIG_NAME, *driver_schema);
2781191f715SRyan Stone 	nvlist_move_nvlist(pf_schema, IOV_CONFIG_NAME, iov_schema);
2791191f715SRyan Stone 	nvlist_move_nvlist(schema, PF_CONFIG_NAME, pf_schema);
2801191f715SRyan Stone 	*driver_schema = NULL;
2811191f715SRyan Stone }
2821191f715SRyan Stone 
2831191f715SRyan Stone static void
2841191f715SRyan Stone pci_iov_build_vf_schema(nvlist_t *schema, nvlist_t **driver_schema)
2851191f715SRyan Stone {
2861191f715SRyan Stone 	nvlist_t *vf_schema, *iov_schema;
2871191f715SRyan Stone 
2881191f715SRyan Stone 	vf_schema = pci_iov_schema_alloc_node();
2891191f715SRyan Stone 	if (vf_schema == NULL) {
2901191f715SRyan Stone 		nvlist_set_error(schema, ENOMEM);
2911191f715SRyan Stone 		return;
2921191f715SRyan Stone 	}
2931191f715SRyan Stone 
2941191f715SRyan Stone 	iov_schema = pci_iov_get_vf_subsystem_schema();
2951191f715SRyan Stone 
2961191f715SRyan Stone 	/*
2971191f715SRyan Stone 	 * Note that if either *driver_schema or iov_schema is NULL, then
2981191f715SRyan Stone 	 * nvlist_move_nvlist will put the schema in the error state and
2991191f715SRyan Stone 	 * SR-IOV will fail to initialize later, so we don't have to explicitly
3001191f715SRyan Stone 	 * handle that case.
3011191f715SRyan Stone 	 */
3021191f715SRyan Stone 	nvlist_move_nvlist(vf_schema, DRIVER_CONFIG_NAME, *driver_schema);
3031191f715SRyan Stone 	nvlist_move_nvlist(vf_schema, IOV_CONFIG_NAME, iov_schema);
3041191f715SRyan Stone 	nvlist_move_nvlist(schema, VF_SCHEMA_NAME, vf_schema);
3051191f715SRyan Stone 	*driver_schema = NULL;
3061191f715SRyan Stone }
3071191f715SRyan Stone 
3081191f715SRyan Stone static nvlist_t *
3091191f715SRyan Stone pci_iov_get_pf_subsystem_schema(void)
3101191f715SRyan Stone {
3111191f715SRyan Stone 	nvlist_t *pf;
3121191f715SRyan Stone 
3131191f715SRyan Stone 	pf = pci_iov_schema_alloc_node();
3141191f715SRyan Stone 	if (pf == NULL)
3151191f715SRyan Stone 		return (NULL);
3161191f715SRyan Stone 
3171191f715SRyan Stone 	pci_iov_schema_add_uint16(pf, "num_vfs", IOV_SCHEMA_REQUIRED, -1);
3181191f715SRyan Stone 	pci_iov_schema_add_string(pf, "device", IOV_SCHEMA_REQUIRED, NULL);
3191191f715SRyan Stone 
3201191f715SRyan Stone 	return (pf);
3211191f715SRyan Stone }
3221191f715SRyan Stone 
3231191f715SRyan Stone static nvlist_t *
3241191f715SRyan Stone pci_iov_get_vf_subsystem_schema(void)
3251191f715SRyan Stone {
3261191f715SRyan Stone 	nvlist_t *vf;
3271191f715SRyan Stone 
3281191f715SRyan Stone 	vf = pci_iov_schema_alloc_node();
3291191f715SRyan Stone 	if (vf == NULL)
3301191f715SRyan Stone 		return (NULL);
3311191f715SRyan Stone 
3321191f715SRyan Stone 	pci_iov_schema_add_bool(vf, "passthrough", IOV_SCHEMA_HASDEFAULT, 0);
3331191f715SRyan Stone 
3341191f715SRyan Stone 	return (vf);
3351191f715SRyan Stone }
3361191f715SRyan Stone 
337e9309eacSRyan Stone static int
338e9309eacSRyan Stone pci_iov_alloc_bar(struct pci_devinfo *dinfo, int bar, pci_addr_t bar_shift)
339e9309eacSRyan Stone {
340e9309eacSRyan Stone 	struct resource *res;
341e9309eacSRyan Stone 	struct pcicfg_iov *iov;
342e9309eacSRyan Stone 	device_t dev, bus;
3432dd1bdf1SJustin Hibbits 	rman_res_t start, end;
344e9309eacSRyan Stone 	pci_addr_t bar_size;
345e9309eacSRyan Stone 	int rid;
346e9309eacSRyan Stone 
347e9309eacSRyan Stone 	iov = dinfo->cfg.iov;
348e9309eacSRyan Stone 	dev = dinfo->cfg.dev;
349e9309eacSRyan Stone 	bus = device_get_parent(dev);
350e9309eacSRyan Stone 	rid = iov->iov_pos + PCIR_SRIOV_BAR(bar);
351e9309eacSRyan Stone 	bar_size = 1 << bar_shift;
352e9309eacSRyan Stone 
353534ccd7bSJustin Hibbits 	res = pci_alloc_multi_resource(bus, dev, SYS_RES_MEMORY, &rid, 0,
354534ccd7bSJustin Hibbits 	    ~0, 1, iov->iov_num_vfs, RF_ACTIVE);
355e9309eacSRyan Stone 
356e9309eacSRyan Stone 	if (res == NULL)
357e9309eacSRyan Stone 		return (ENXIO);
358e9309eacSRyan Stone 
359e9309eacSRyan Stone 	iov->iov_bar[bar].res = res;
360e9309eacSRyan Stone 	iov->iov_bar[bar].bar_size = bar_size;
361e9309eacSRyan Stone 	iov->iov_bar[bar].bar_shift = bar_shift;
362e9309eacSRyan Stone 
363e9309eacSRyan Stone 	start = rman_get_start(res);
364e9309eacSRyan Stone 	end = rman_get_end(res);
365e9309eacSRyan Stone 	return (rman_manage_region(&iov->rman, start, end));
366e9309eacSRyan Stone }
367e9309eacSRyan Stone 
368e9309eacSRyan Stone static void
369e9309eacSRyan Stone pci_iov_add_bars(struct pcicfg_iov *iov, struct pci_devinfo *dinfo)
370e9309eacSRyan Stone {
371e9309eacSRyan Stone 	struct pci_iov_bar *bar;
372e9309eacSRyan Stone 	uint64_t bar_start;
373e9309eacSRyan Stone 	int i;
374e9309eacSRyan Stone 
375e9309eacSRyan Stone 	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
376e9309eacSRyan Stone 		bar = &iov->iov_bar[i];
377e9309eacSRyan Stone 		if (bar->res != NULL) {
378e9309eacSRyan Stone 			bar_start = rman_get_start(bar->res) +
379e9309eacSRyan Stone 			    dinfo->cfg.vf.index * bar->bar_size;
380e9309eacSRyan Stone 
381e9309eacSRyan Stone 			pci_add_bar(dinfo->cfg.dev, PCIR_BAR(i), bar_start,
382e9309eacSRyan Stone 			    bar->bar_shift);
383e9309eacSRyan Stone 		}
384e9309eacSRyan Stone 	}
385e9309eacSRyan Stone }
386e9309eacSRyan Stone 
3875cc26e63SRyan Stone static int
3885cc26e63SRyan Stone pci_iov_parse_config(struct pcicfg_iov *iov, struct pci_iov_arg *arg,
3895cc26e63SRyan Stone     nvlist_t **ret)
3905cc26e63SRyan Stone {
3915cc26e63SRyan Stone 	void *packed_config;
3925cc26e63SRyan Stone 	nvlist_t *config;
3935cc26e63SRyan Stone 	int error;
3945cc26e63SRyan Stone 
3955cc26e63SRyan Stone 	config = NULL;
3965cc26e63SRyan Stone 	packed_config = NULL;
3975cc26e63SRyan Stone 
3985cc26e63SRyan Stone 	if (arg->len > pci_iov_max_config) {
3995cc26e63SRyan Stone 		error = EMSGSIZE;
4005cc26e63SRyan Stone 		goto out;
4015cc26e63SRyan Stone 	}
4025cc26e63SRyan Stone 
4035cc26e63SRyan Stone 	packed_config = malloc(arg->len, M_SRIOV, M_WAITOK);
4045cc26e63SRyan Stone 
4055cc26e63SRyan Stone 	error = copyin(arg->config, packed_config, arg->len);
4065cc26e63SRyan Stone 	if (error != 0)
4075cc26e63SRyan Stone 		goto out;
4085cc26e63SRyan Stone 
409bd1da0a0SMariusz Zaborski 	config = nvlist_unpack(packed_config, arg->len, NV_FLAG_IGNORE_CASE);
4105cc26e63SRyan Stone 	if (config == NULL) {
4115cc26e63SRyan Stone 		error = EINVAL;
4125cc26e63SRyan Stone 		goto out;
4135cc26e63SRyan Stone 	}
4145cc26e63SRyan Stone 
4155cc26e63SRyan Stone 	error = pci_iov_schema_validate_config(iov->iov_schema, config);
4165cc26e63SRyan Stone 	if (error != 0)
4175cc26e63SRyan Stone 		goto out;
4185cc26e63SRyan Stone 
4195cc26e63SRyan Stone 	error = nvlist_error(config);
4205cc26e63SRyan Stone 	if (error != 0)
4215cc26e63SRyan Stone 		goto out;
4225cc26e63SRyan Stone 
4235cc26e63SRyan Stone 	*ret = config;
4245cc26e63SRyan Stone 	config = NULL;
4255cc26e63SRyan Stone 
4265cc26e63SRyan Stone out:
4275cc26e63SRyan Stone 	nvlist_destroy(config);
4285cc26e63SRyan Stone 	free(packed_config, M_SRIOV);
4295cc26e63SRyan Stone 	return (error);
4305cc26e63SRyan Stone }
4315cc26e63SRyan Stone 
4329bfb1e36SRyan Stone /*
4339bfb1e36SRyan Stone  * Set the ARI_EN bit in the lowest-numbered PCI function with the SR-IOV
4349bfb1e36SRyan Stone  * capability.  This bit is only writeable on the lowest-numbered PF but
4359bfb1e36SRyan Stone  * affects all PFs on the device.
4369bfb1e36SRyan Stone  */
4379bfb1e36SRyan Stone static int
4387063f942SJohn Baldwin pci_iov_set_ari(device_t bus, bool *ari_enabled)
4399bfb1e36SRyan Stone {
4409bfb1e36SRyan Stone 	device_t lowest;
4419bfb1e36SRyan Stone 	device_t *devlist;
4429bfb1e36SRyan Stone 	int i, error, devcount, lowest_func, lowest_pos, iov_pos, dev_func;
4439bfb1e36SRyan Stone 	uint16_t iov_ctl;
4449bfb1e36SRyan Stone 
4459bfb1e36SRyan Stone 	/* If ARI is disabled on the downstream port there is nothing to do. */
4467063f942SJohn Baldwin 	if (!PCIB_ARI_ENABLED(device_get_parent(bus))) {
4477063f942SJohn Baldwin 		*ari_enabled = false;
4489bfb1e36SRyan Stone 		return (0);
4497063f942SJohn Baldwin 	}
4509bfb1e36SRyan Stone 
4519bfb1e36SRyan Stone 	error = device_get_children(bus, &devlist, &devcount);
4529bfb1e36SRyan Stone 
4539bfb1e36SRyan Stone 	if (error != 0)
4549bfb1e36SRyan Stone 		return (error);
4559bfb1e36SRyan Stone 
4569bfb1e36SRyan Stone 	lowest = NULL;
4579bfb1e36SRyan Stone 	for (i = 0; i < devcount; i++) {
4589bfb1e36SRyan Stone 		if (pci_find_extcap(devlist[i], PCIZ_SRIOV, &iov_pos) == 0) {
4599bfb1e36SRyan Stone 			dev_func = pci_get_function(devlist[i]);
4609bfb1e36SRyan Stone 			if (lowest == NULL || dev_func < lowest_func) {
4619bfb1e36SRyan Stone 				lowest = devlist[i];
4629bfb1e36SRyan Stone 				lowest_func = dev_func;
4639bfb1e36SRyan Stone 				lowest_pos = iov_pos;
4649bfb1e36SRyan Stone 			}
4659bfb1e36SRyan Stone 		}
4669bfb1e36SRyan Stone 	}
467b83c5d07SJohn Baldwin 	free(devlist, M_TEMP);
4689bfb1e36SRyan Stone 
4699bfb1e36SRyan Stone 	/*
4709bfb1e36SRyan Stone 	 * If we called this function some device must have the SR-IOV
4719bfb1e36SRyan Stone 	 * capability.
4729bfb1e36SRyan Stone 	 */
4739bfb1e36SRyan Stone 	KASSERT(lowest != NULL,
4749bfb1e36SRyan Stone 	    ("Could not find child of %s with SR-IOV capability",
4759bfb1e36SRyan Stone 	    device_get_nameunit(bus)));
4769bfb1e36SRyan Stone 
477b83c5d07SJohn Baldwin 	iov_ctl = pci_read_config(lowest, lowest_pos + PCIR_SRIOV_CTL, 2);
4789bfb1e36SRyan Stone 	iov_ctl |= PCIM_SRIOV_ARI_EN;
479b83c5d07SJohn Baldwin 	pci_write_config(lowest, lowest_pos + PCIR_SRIOV_CTL, iov_ctl, 2);
480b83c5d07SJohn Baldwin 	if ((pci_read_config(lowest, lowest_pos + PCIR_SRIOV_CTL, 2) &
481b83c5d07SJohn Baldwin 	    PCIM_SRIOV_ARI_EN) == 0) {
482b83c5d07SJohn Baldwin 		device_printf(lowest, "failed to enable ARI\n");
483b83c5d07SJohn Baldwin 		return (ENXIO);
484b83c5d07SJohn Baldwin 	}
4857063f942SJohn Baldwin 	*ari_enabled = true;
4869bfb1e36SRyan Stone 	return (0);
4879bfb1e36SRyan Stone }
4889bfb1e36SRyan Stone 
4899bfb1e36SRyan Stone static int
4909bfb1e36SRyan Stone pci_iov_config_page_size(struct pci_devinfo *dinfo)
4919bfb1e36SRyan Stone {
4929bfb1e36SRyan Stone 	uint32_t page_cap, page_size;
4939bfb1e36SRyan Stone 
4949bfb1e36SRyan Stone 	page_cap = IOV_READ(dinfo, PCIR_SRIOV_PAGE_CAP, 4);
4959bfb1e36SRyan Stone 
4969bfb1e36SRyan Stone 	/*
4979bfb1e36SRyan Stone 	 * If the system page size is less than the smallest SR-IOV page size
4989bfb1e36SRyan Stone 	 * then round up to the smallest SR-IOV page size.
4999bfb1e36SRyan Stone 	 */
5009bfb1e36SRyan Stone 	if (PAGE_SHIFT < PCI_SRIOV_BASE_PAGE_SHIFT)
5019bfb1e36SRyan Stone 		page_size = (1 << 0);
5029bfb1e36SRyan Stone 	else
5039bfb1e36SRyan Stone 		page_size = (1 << (PAGE_SHIFT - PCI_SRIOV_BASE_PAGE_SHIFT));
5049bfb1e36SRyan Stone 
5059bfb1e36SRyan Stone 	/* Check that the device supports the system page size. */
5069bfb1e36SRyan Stone 	if (!(page_size & page_cap))
5079bfb1e36SRyan Stone 		return (ENXIO);
5089bfb1e36SRyan Stone 
5099bfb1e36SRyan Stone 	IOV_WRITE(dinfo, PCIR_SRIOV_PAGE_SIZE, page_size, 4);
5109bfb1e36SRyan Stone 	return (0);
5119bfb1e36SRyan Stone }
5129bfb1e36SRyan Stone 
513e9309eacSRyan Stone static int
514f3bb9251SJohn Baldwin pci_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *config)
5155cc26e63SRyan Stone {
5165cc26e63SRyan Stone 	const nvlist_t *device, *driver_config;
5175cc26e63SRyan Stone 
5185cc26e63SRyan Stone 	device = nvlist_get_nvlist(config, PF_CONFIG_NAME);
5195cc26e63SRyan Stone 	driver_config = nvlist_get_nvlist(device, DRIVER_CONFIG_NAME);
520f3bb9251SJohn Baldwin 	return (PCI_IOV_INIT(dev, num_vfs, driver_config));
5215cc26e63SRyan Stone }
5225cc26e63SRyan Stone 
5235cc26e63SRyan Stone static int
524e9309eacSRyan Stone pci_iov_init_rman(device_t pf, struct pcicfg_iov *iov)
525e9309eacSRyan Stone {
526e9309eacSRyan Stone 	int error;
527e9309eacSRyan Stone 
528e9309eacSRyan Stone 	iov->rman.rm_start = 0;
529534ccd7bSJustin Hibbits 	iov->rman.rm_end = ~0;
530e9309eacSRyan Stone 	iov->rman.rm_type = RMAN_ARRAY;
531e9309eacSRyan Stone 	snprintf(iov->rman_name, sizeof(iov->rman_name), "%s VF I/O memory",
532e9309eacSRyan Stone 	    device_get_nameunit(pf));
533e9309eacSRyan Stone 	iov->rman.rm_descr = iov->rman_name;
534e9309eacSRyan Stone 
535e9309eacSRyan Stone 	error = rman_init(&iov->rman);
536e9309eacSRyan Stone 	if (error != 0)
537e9309eacSRyan Stone 		return (error);
538e9309eacSRyan Stone 
539e9309eacSRyan Stone 	iov->iov_flags |= IOV_RMAN_INITED;
540e9309eacSRyan Stone 	return (0);
541e9309eacSRyan Stone }
542e9309eacSRyan Stone 
543e9309eacSRyan Stone static int
5444d185754SWojciech Macek pci_iov_alloc_bar_ea(struct pci_devinfo *dinfo, int bar)
5454d185754SWojciech Macek {
5464d185754SWojciech Macek 	struct pcicfg_iov *iov;
5474d185754SWojciech Macek 	rman_res_t start, end;
5484d185754SWojciech Macek 	struct resource *res;
5494d185754SWojciech Macek 	struct resource_list *rl;
5504d185754SWojciech Macek 	struct resource_list_entry *rle;
5514d185754SWojciech Macek 
5524d185754SWojciech Macek 	rl = &dinfo->resources;
5534d185754SWojciech Macek 	iov = dinfo->cfg.iov;
5544d185754SWojciech Macek 
5554d185754SWojciech Macek 	rle = resource_list_find(rl, SYS_RES_MEMORY,
5564d185754SWojciech Macek 	    iov->iov_pos + PCIR_SRIOV_BAR(bar));
5574d185754SWojciech Macek 	if (rle == NULL)
5584d185754SWojciech Macek 		rle = resource_list_find(rl, SYS_RES_IOPORT,
5594d185754SWojciech Macek 		    iov->iov_pos + PCIR_SRIOV_BAR(bar));
5604d185754SWojciech Macek 	if (rle == NULL)
5614d185754SWojciech Macek 		return (ENXIO);
5624d185754SWojciech Macek 	res = rle->res;
5634d185754SWojciech Macek 
5644d185754SWojciech Macek 	iov->iov_bar[bar].res = res;
5654d185754SWojciech Macek 	iov->iov_bar[bar].bar_size = rman_get_size(res) / iov->iov_num_vfs;
5664d185754SWojciech Macek 	iov->iov_bar[bar].bar_shift = pci_mapsize(iov->iov_bar[bar].bar_size);
5674d185754SWojciech Macek 
5684d185754SWojciech Macek 	start = rman_get_start(res);
5694d185754SWojciech Macek 	end = rman_get_end(res);
5704d185754SWojciech Macek 
5714d185754SWojciech Macek 	return (rman_manage_region(&iov->rman, start, end));
5724d185754SWojciech Macek }
5734d185754SWojciech Macek 
5744d185754SWojciech Macek static int
575e9309eacSRyan Stone pci_iov_setup_bars(struct pci_devinfo *dinfo)
576e9309eacSRyan Stone {
577e9309eacSRyan Stone 	device_t dev;
578e9309eacSRyan Stone 	struct pcicfg_iov *iov;
579e9309eacSRyan Stone 	pci_addr_t bar_value, testval;
580e9309eacSRyan Stone 	int i, last_64, error;
581e9309eacSRyan Stone 
582e9309eacSRyan Stone 	iov = dinfo->cfg.iov;
583e9309eacSRyan Stone 	dev = dinfo->cfg.dev;
584e9309eacSRyan Stone 	last_64 = 0;
585e9309eacSRyan Stone 
5864d185754SWojciech Macek 	pci_add_resources_ea(device_get_parent(dev), dev, 1);
5874d185754SWojciech Macek 
588e9309eacSRyan Stone 	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
5894d185754SWojciech Macek 		/* First, try to use BARs allocated with EA */
5904d185754SWojciech Macek 		error = pci_iov_alloc_bar_ea(dinfo, i);
5914d185754SWojciech Macek 		if (error == 0)
5924d185754SWojciech Macek 			continue;
5934d185754SWojciech Macek 
5944d185754SWojciech Macek 		/* Allocate legacy-BAR only if EA is not enabled */
5954d185754SWojciech Macek 		if (pci_ea_is_enabled(dev, iov->iov_pos + PCIR_SRIOV_BAR(i)))
5964d185754SWojciech Macek 			continue;
5974d185754SWojciech Macek 
598e9309eacSRyan Stone 		/*
599e9309eacSRyan Stone 		 * If a PCI BAR is a 64-bit wide BAR, then it spans two
600e9309eacSRyan Stone 		 * consecutive registers.  Therefore if the last BAR that
601e9309eacSRyan Stone 		 * we looked at was a 64-bit BAR, we need to skip this
602e9309eacSRyan Stone 		 * register as it's the second half of the last BAR.
603e9309eacSRyan Stone 		 */
604e9309eacSRyan Stone 		if (!last_64) {
605e9309eacSRyan Stone 			pci_read_bar(dev,
606e9309eacSRyan Stone 			    iov->iov_pos + PCIR_SRIOV_BAR(i),
607e9309eacSRyan Stone 			    &bar_value, &testval, &last_64);
608e9309eacSRyan Stone 
609e9309eacSRyan Stone 			if (testval != 0) {
610e9309eacSRyan Stone 				error = pci_iov_alloc_bar(dinfo, i,
611e9309eacSRyan Stone 				   pci_mapsize(testval));
612e9309eacSRyan Stone 				if (error != 0)
613e9309eacSRyan Stone 					return (error);
614e9309eacSRyan Stone 			}
615e9309eacSRyan Stone 		} else
616e9309eacSRyan Stone 			last_64 = 0;
617e9309eacSRyan Stone 	}
618e9309eacSRyan Stone 
619e9309eacSRyan Stone 	return (0);
620e9309eacSRyan Stone }
621e9309eacSRyan Stone 
6229bfb1e36SRyan Stone static void
6235cc26e63SRyan Stone pci_iov_enumerate_vfs(struct pci_devinfo *dinfo, const nvlist_t *config,
6249bfb1e36SRyan Stone     uint16_t first_rid, uint16_t rid_stride)
6259bfb1e36SRyan Stone {
6265cc26e63SRyan Stone 	char device_name[VF_MAX_NAME];
6275cc26e63SRyan Stone 	const nvlist_t *device, *driver_config, *iov_config;
6289bfb1e36SRyan Stone 	device_t bus, dev, vf;
6299bfb1e36SRyan Stone 	struct pcicfg_iov *iov;
6309bfb1e36SRyan Stone 	struct pci_devinfo *vfinfo;
6319bfb1e36SRyan Stone 	int i, error;
6329bfb1e36SRyan Stone 	uint16_t vid, did, next_rid;
6339bfb1e36SRyan Stone 
6349bfb1e36SRyan Stone 	iov = dinfo->cfg.iov;
6359bfb1e36SRyan Stone 	dev = dinfo->cfg.dev;
6369bfb1e36SRyan Stone 	bus = device_get_parent(dev);
6379bfb1e36SRyan Stone 	next_rid = first_rid;
6389bfb1e36SRyan Stone 	vid = pci_get_vendor(dev);
6399bfb1e36SRyan Stone 	did = IOV_READ(dinfo, PCIR_SRIOV_VF_DID, 2);
6409bfb1e36SRyan Stone 
6419bfb1e36SRyan Stone 	for (i = 0; i < iov->iov_num_vfs; i++, next_rid += rid_stride) {
6425cc26e63SRyan Stone 		snprintf(device_name, sizeof(device_name), VF_PREFIX"%d", i);
6435cc26e63SRyan Stone 		device = nvlist_get_nvlist(config, device_name);
6445cc26e63SRyan Stone 		iov_config = nvlist_get_nvlist(device, IOV_CONFIG_NAME);
6455cc26e63SRyan Stone 		driver_config = nvlist_get_nvlist(device, DRIVER_CONFIG_NAME);
6469bfb1e36SRyan Stone 
6479bfb1e36SRyan Stone 		vf = PCI_CREATE_IOV_CHILD(bus, dev, next_rid, vid, did);
6489bfb1e36SRyan Stone 		if (vf == NULL)
6499bfb1e36SRyan Stone 			break;
6509bfb1e36SRyan Stone 
6515cc26e63SRyan Stone 		/*
6525cc26e63SRyan Stone 		 * If we are creating passthrough devices then force the ppt
6535cc26e63SRyan Stone 		 * driver to attach to prevent a VF driver from claiming the
6545cc26e63SRyan Stone 		 * VFs.
6555cc26e63SRyan Stone 		 */
6565cc26e63SRyan Stone 		if (nvlist_get_bool(iov_config, "passthrough"))
6571c229658SRyan Stone 			device_set_devclass_fixed(vf, "ppt");
6585cc26e63SRyan Stone 
6599bfb1e36SRyan Stone 		vfinfo = device_get_ivars(vf);
6609bfb1e36SRyan Stone 
6619bfb1e36SRyan Stone 		vfinfo->cfg.iov = iov;
6629bfb1e36SRyan Stone 		vfinfo->cfg.vf.index = i;
6639bfb1e36SRyan Stone 
664e9309eacSRyan Stone 		pci_iov_add_bars(iov, vfinfo);
665e9309eacSRyan Stone 
666f3bb9251SJohn Baldwin 		error = PCI_IOV_ADD_VF(dev, i, driver_config);
6679bfb1e36SRyan Stone 		if (error != 0) {
6689bfb1e36SRyan Stone 			device_printf(dev, "Failed to add VF %d\n", i);
669496dfa89SJohn Baldwin 			device_delete_child(bus, vf);
6709bfb1e36SRyan Stone 		}
6719bfb1e36SRyan Stone 	}
6729bfb1e36SRyan Stone 
673*18250ec6SJohn Baldwin 	bus_attach_children(bus);
6749bfb1e36SRyan Stone }
6759bfb1e36SRyan Stone 
6769bfb1e36SRyan Stone static int
6779bfb1e36SRyan Stone pci_iov_config(struct cdev *cdev, struct pci_iov_arg *arg)
6789bfb1e36SRyan Stone {
6799bfb1e36SRyan Stone 	device_t bus, dev;
6809bfb1e36SRyan Stone 	struct pci_devinfo *dinfo;
6819bfb1e36SRyan Stone 	struct pcicfg_iov *iov;
6825cc26e63SRyan Stone 	nvlist_t *config;
683e9309eacSRyan Stone 	int i, error;
6849bfb1e36SRyan Stone 	uint16_t rid_off, rid_stride;
6859bfb1e36SRyan Stone 	uint16_t first_rid, last_rid;
6869bfb1e36SRyan Stone 	uint16_t iov_ctl;
6875cc26e63SRyan Stone 	uint16_t num_vfs, total_vfs;
6889bfb1e36SRyan Stone 	int iov_inited;
6897063f942SJohn Baldwin 	bool ari_enabled;
6909bfb1e36SRyan Stone 
6919bfb1e36SRyan Stone 	mtx_lock(&Giant);
6929bfb1e36SRyan Stone 	dinfo = cdev->si_drv1;
6939bfb1e36SRyan Stone 	iov = dinfo->cfg.iov;
6949bfb1e36SRyan Stone 	dev = dinfo->cfg.dev;
6959bfb1e36SRyan Stone 	bus = device_get_parent(dev);
6969bfb1e36SRyan Stone 	iov_inited = 0;
6975cc26e63SRyan Stone 	config = NULL;
6989bfb1e36SRyan Stone 
6996c3162c4SRyan Stone 	if ((iov->iov_flags & IOV_BUSY) || iov->iov_num_vfs != 0) {
7009bfb1e36SRyan Stone 		mtx_unlock(&Giant);
7019bfb1e36SRyan Stone 		return (EBUSY);
7029bfb1e36SRyan Stone 	}
7036c3162c4SRyan Stone 	iov->iov_flags |= IOV_BUSY;
7049bfb1e36SRyan Stone 
7055cc26e63SRyan Stone 	error = pci_iov_parse_config(iov, arg, &config);
7065cc26e63SRyan Stone 	if (error != 0)
7075cc26e63SRyan Stone 		goto out;
7089bfb1e36SRyan Stone 
7095cc26e63SRyan Stone 	num_vfs = pci_iov_config_get_num_vfs(config);
7105cc26e63SRyan Stone 	total_vfs = IOV_READ(dinfo, PCIR_SRIOV_TOTAL_VFS, 2);
7115cc26e63SRyan Stone 	if (num_vfs > total_vfs) {
7129bfb1e36SRyan Stone 		error = EINVAL;
7139bfb1e36SRyan Stone 		goto out;
7149bfb1e36SRyan Stone 	}
7159bfb1e36SRyan Stone 
7169bfb1e36SRyan Stone 	error = pci_iov_config_page_size(dinfo);
7179bfb1e36SRyan Stone 	if (error != 0)
7189bfb1e36SRyan Stone 		goto out;
7199bfb1e36SRyan Stone 
7207063f942SJohn Baldwin 	error = pci_iov_set_ari(bus, &ari_enabled);
7219bfb1e36SRyan Stone 	if (error != 0)
7229bfb1e36SRyan Stone 		goto out;
7239bfb1e36SRyan Stone 
724f3bb9251SJohn Baldwin 	error = pci_iov_init(dev, num_vfs, config);
7259bfb1e36SRyan Stone 	if (error != 0)
7269bfb1e36SRyan Stone 		goto out;
7279bfb1e36SRyan Stone 	iov_inited = 1;
7285cc26e63SRyan Stone 
7295cc26e63SRyan Stone 	IOV_WRITE(dinfo, PCIR_SRIOV_NUM_VFS, num_vfs, 2);
7309bfb1e36SRyan Stone 
7319bfb1e36SRyan Stone 	rid_off = IOV_READ(dinfo, PCIR_SRIOV_VF_OFF, 2);
7329bfb1e36SRyan Stone 	rid_stride = IOV_READ(dinfo, PCIR_SRIOV_VF_STRIDE, 2);
7339bfb1e36SRyan Stone 
7349bfb1e36SRyan Stone 	first_rid = pci_get_rid(dev) + rid_off;
7355cc26e63SRyan Stone 	last_rid = first_rid + (num_vfs - 1) * rid_stride;
7369bfb1e36SRyan Stone 
7379bfb1e36SRyan Stone 	/* We don't yet support allocating extra bus numbers for VFs. */
7389bfb1e36SRyan Stone 	if (pci_get_bus(dev) != PCI_RID2BUS(last_rid)) {
7396a4f0c06SVal Packett 		device_printf(dev, "not enough PCIe bus numbers for VFs\n");
7409bfb1e36SRyan Stone 		error = ENOSPC;
7419bfb1e36SRyan Stone 		goto out;
7429bfb1e36SRyan Stone 	}
7439bfb1e36SRyan Stone 
7447063f942SJohn Baldwin 	if (!ari_enabled && PCI_RID2SLOT(last_rid) != 0) {
7457063f942SJohn Baldwin 		error = ENOSPC;
7467063f942SJohn Baldwin 		goto out;
7477063f942SJohn Baldwin 	}
7487063f942SJohn Baldwin 
7499bfb1e36SRyan Stone 	iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2);
7509bfb1e36SRyan Stone 	iov_ctl &= ~(PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE);
7519bfb1e36SRyan Stone 	IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2);
7529bfb1e36SRyan Stone 
753e9309eacSRyan Stone 	error = pci_iov_init_rman(dev, iov);
754e9309eacSRyan Stone 	if (error != 0)
755e9309eacSRyan Stone 		goto out;
756e9309eacSRyan Stone 
7575cc26e63SRyan Stone 	iov->iov_num_vfs = num_vfs;
7589bfb1e36SRyan Stone 
759e9309eacSRyan Stone 	error = pci_iov_setup_bars(dinfo);
760e9309eacSRyan Stone 	if (error != 0)
761e9309eacSRyan Stone 		goto out;
762e9309eacSRyan Stone 
7639bfb1e36SRyan Stone 	iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2);
764e9309eacSRyan Stone 	iov_ctl |= PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE;
7659bfb1e36SRyan Stone 	IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2);
7669bfb1e36SRyan Stone 
7679bfb1e36SRyan Stone 	/* Per specification, we must wait 100ms before accessing VFs. */
7689bfb1e36SRyan Stone 	pause("iov", roundup(hz, 10));
7695cc26e63SRyan Stone 	pci_iov_enumerate_vfs(dinfo, config, first_rid, rid_stride);
7705cc26e63SRyan Stone 
7715cc26e63SRyan Stone 	nvlist_destroy(config);
7725cc26e63SRyan Stone 	iov->iov_flags &= ~IOV_BUSY;
7739bfb1e36SRyan Stone 	mtx_unlock(&Giant);
7749bfb1e36SRyan Stone 
7759bfb1e36SRyan Stone 	return (0);
7769bfb1e36SRyan Stone out:
7779bfb1e36SRyan Stone 	if (iov_inited)
778f3bb9251SJohn Baldwin 		PCI_IOV_UNINIT(dev);
779e9309eacSRyan Stone 
780e9309eacSRyan Stone 	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
781e9309eacSRyan Stone 		if (iov->iov_bar[i].res != NULL) {
7829dbf5b0eSJohn Baldwin 			pci_release_resource(bus, dev, iov->iov_bar[i].res);
783e9309eacSRyan Stone 			pci_delete_resource(bus, dev, SYS_RES_MEMORY,
784e9309eacSRyan Stone 			    iov->iov_pos + PCIR_SRIOV_BAR(i));
785e9309eacSRyan Stone 			iov->iov_bar[i].res = NULL;
786e9309eacSRyan Stone 		}
787e9309eacSRyan Stone 	}
788e9309eacSRyan Stone 
789e9309eacSRyan Stone 	if (iov->iov_flags & IOV_RMAN_INITED) {
790e9309eacSRyan Stone 		rman_fini(&iov->rman);
791e9309eacSRyan Stone 		iov->iov_flags &= ~IOV_RMAN_INITED;
792e9309eacSRyan Stone 	}
7935cc26e63SRyan Stone 
7945cc26e63SRyan Stone 	nvlist_destroy(config);
7959bfb1e36SRyan Stone 	iov->iov_num_vfs = 0;
7966c3162c4SRyan Stone 	iov->iov_flags &= ~IOV_BUSY;
7979bfb1e36SRyan Stone 	mtx_unlock(&Giant);
7989bfb1e36SRyan Stone 	return (error);
7999bfb1e36SRyan Stone }
8009bfb1e36SRyan Stone 
801e402d55cSJohn Baldwin void
802e402d55cSJohn Baldwin pci_iov_cfg_restore(device_t dev, struct pci_devinfo *dinfo)
803e402d55cSJohn Baldwin {
804e402d55cSJohn Baldwin 	struct pcicfg_iov *iov;
805e402d55cSJohn Baldwin 
806e402d55cSJohn Baldwin 	iov = dinfo->cfg.iov;
807e402d55cSJohn Baldwin 
808e402d55cSJohn Baldwin 	IOV_WRITE(dinfo, PCIR_SRIOV_PAGE_SIZE, iov->iov_page_size, 4);
809e402d55cSJohn Baldwin 	IOV_WRITE(dinfo, PCIR_SRIOV_NUM_VFS, iov->iov_num_vfs, 2);
810e402d55cSJohn Baldwin 	IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov->iov_ctl, 2);
811e402d55cSJohn Baldwin }
812e402d55cSJohn Baldwin 
813e402d55cSJohn Baldwin void
814e402d55cSJohn Baldwin pci_iov_cfg_save(device_t dev, struct pci_devinfo *dinfo)
815e402d55cSJohn Baldwin {
816e402d55cSJohn Baldwin 	struct pcicfg_iov *iov;
817e402d55cSJohn Baldwin 
818e402d55cSJohn Baldwin 	iov = dinfo->cfg.iov;
819e402d55cSJohn Baldwin 
820e402d55cSJohn Baldwin 	iov->iov_page_size = IOV_READ(dinfo, PCIR_SRIOV_PAGE_SIZE, 4);
821e402d55cSJohn Baldwin 	iov->iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2);
822e402d55cSJohn Baldwin }
823e402d55cSJohn Baldwin 
8246c3162c4SRyan Stone /* Return true if child is a VF of the given PF. */
8256c3162c4SRyan Stone static int
8266c3162c4SRyan Stone pci_iov_is_child_vf(struct pcicfg_iov *pf, device_t child)
8276c3162c4SRyan Stone {
8286c3162c4SRyan Stone 	struct pci_devinfo *vfinfo;
8296c3162c4SRyan Stone 
8306c3162c4SRyan Stone 	vfinfo = device_get_ivars(child);
8316c3162c4SRyan Stone 
8326c3162c4SRyan Stone 	if (!(vfinfo->cfg.flags & PCICFG_VF))
8336c3162c4SRyan Stone 		return (0);
8346c3162c4SRyan Stone 
8356c3162c4SRyan Stone 	return (pf == vfinfo->cfg.iov);
8366c3162c4SRyan Stone }
8376c3162c4SRyan Stone 
8386c3162c4SRyan Stone static int
83994f5c1ccSKonstantin Belousov pci_iov_delete_iov_children(struct pci_devinfo *dinfo)
8406c3162c4SRyan Stone {
8416c3162c4SRyan Stone 	device_t bus, dev, vf, *devlist;
8426c3162c4SRyan Stone 	struct pcicfg_iov *iov;
8436c3162c4SRyan Stone 	int i, error, devcount;
8446c3162c4SRyan Stone 	uint32_t iov_ctl;
8456c3162c4SRyan Stone 
84694f5c1ccSKonstantin Belousov 	mtx_assert(&Giant, MA_OWNED);
84794f5c1ccSKonstantin Belousov 
8486c3162c4SRyan Stone 	iov = dinfo->cfg.iov;
8496c3162c4SRyan Stone 	dev = dinfo->cfg.dev;
8506c3162c4SRyan Stone 	bus = device_get_parent(dev);
8516c3162c4SRyan Stone 	devlist = NULL;
8526c3162c4SRyan Stone 
8536c3162c4SRyan Stone 	iov->iov_flags |= IOV_BUSY;
8546c3162c4SRyan Stone 
8556c3162c4SRyan Stone 	error = device_get_children(bus, &devlist, &devcount);
8566c3162c4SRyan Stone 
8576c3162c4SRyan Stone 	if (error != 0)
8586c3162c4SRyan Stone 		goto out;
8596c3162c4SRyan Stone 
8606c3162c4SRyan Stone 	for (i = 0; i < devcount; i++) {
8616c3162c4SRyan Stone 		vf = devlist[i];
8626c3162c4SRyan Stone 
8636c3162c4SRyan Stone 		if (!pci_iov_is_child_vf(iov, vf))
8646c3162c4SRyan Stone 			continue;
8656c3162c4SRyan Stone 
8666c3162c4SRyan Stone 		error = device_detach(vf);
8676c3162c4SRyan Stone 		if (error != 0) {
8686c3162c4SRyan Stone 			device_printf(dev,
8696c3162c4SRyan Stone 			   "Could not disable SR-IOV: failed to detach VF %s\n",
8706c3162c4SRyan Stone 			    device_get_nameunit(vf));
8716c3162c4SRyan Stone 			goto out;
8726c3162c4SRyan Stone 		}
8736c3162c4SRyan Stone 	}
8746c3162c4SRyan Stone 
8756c3162c4SRyan Stone 	for (i = 0; i < devcount; i++) {
8766c3162c4SRyan Stone 		vf = devlist[i];
8776c3162c4SRyan Stone 
8786c3162c4SRyan Stone 		if (pci_iov_is_child_vf(iov, vf))
879496dfa89SJohn Baldwin 			device_delete_child(bus, vf);
8806c3162c4SRyan Stone 	}
881f3bb9251SJohn Baldwin 	PCI_IOV_UNINIT(dev);
8826c3162c4SRyan Stone 
8836c3162c4SRyan Stone 	iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2);
8846c3162c4SRyan Stone 	iov_ctl &= ~(PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE);
8856c3162c4SRyan Stone 	IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2);
8866c3162c4SRyan Stone 	IOV_WRITE(dinfo, PCIR_SRIOV_NUM_VFS, 0, 2);
8876c3162c4SRyan Stone 
8886c3162c4SRyan Stone 	iov->iov_num_vfs = 0;
8896c3162c4SRyan Stone 
8906c3162c4SRyan Stone 	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
8916c3162c4SRyan Stone 		if (iov->iov_bar[i].res != NULL) {
8929dbf5b0eSJohn Baldwin 			pci_release_resource(bus, dev, iov->iov_bar[i].res);
8936c3162c4SRyan Stone 			pci_delete_resource(bus, dev, SYS_RES_MEMORY,
8946c3162c4SRyan Stone 			    iov->iov_pos + PCIR_SRIOV_BAR(i));
8956c3162c4SRyan Stone 			iov->iov_bar[i].res = NULL;
8966c3162c4SRyan Stone 		}
8976c3162c4SRyan Stone 	}
8986c3162c4SRyan Stone 
8996c3162c4SRyan Stone 	if (iov->iov_flags & IOV_RMAN_INITED) {
9006c3162c4SRyan Stone 		rman_fini(&iov->rman);
9016c3162c4SRyan Stone 		iov->iov_flags &= ~IOV_RMAN_INITED;
9026c3162c4SRyan Stone 	}
9036c3162c4SRyan Stone 
9046c3162c4SRyan Stone 	error = 0;
9056c3162c4SRyan Stone out:
9066c3162c4SRyan Stone 	free(devlist, M_TEMP);
9076c3162c4SRyan Stone 	iov->iov_flags &= ~IOV_BUSY;
90894f5c1ccSKonstantin Belousov 	return (error);
90994f5c1ccSKonstantin Belousov }
91094f5c1ccSKonstantin Belousov 
91194f5c1ccSKonstantin Belousov static int
91294f5c1ccSKonstantin Belousov pci_iov_delete(struct cdev *cdev)
91394f5c1ccSKonstantin Belousov {
91494f5c1ccSKonstantin Belousov 	struct pci_devinfo *dinfo;
91594f5c1ccSKonstantin Belousov 	struct pcicfg_iov *iov;
91694f5c1ccSKonstantin Belousov 	int error;
91794f5c1ccSKonstantin Belousov 
91894f5c1ccSKonstantin Belousov 	mtx_lock(&Giant);
91994f5c1ccSKonstantin Belousov 	dinfo = cdev->si_drv1;
92094f5c1ccSKonstantin Belousov 	iov = dinfo->cfg.iov;
92194f5c1ccSKonstantin Belousov 
92294f5c1ccSKonstantin Belousov 	if ((iov->iov_flags & IOV_BUSY) != 0) {
92394f5c1ccSKonstantin Belousov 		error = EBUSY;
92494f5c1ccSKonstantin Belousov 		goto out;
92594f5c1ccSKonstantin Belousov 	}
92694f5c1ccSKonstantin Belousov 	if (iov->iov_num_vfs == 0) {
92794f5c1ccSKonstantin Belousov 		error = ECHILD;
92894f5c1ccSKonstantin Belousov 		goto out;
92994f5c1ccSKonstantin Belousov 	}
93094f5c1ccSKonstantin Belousov 
93194f5c1ccSKonstantin Belousov 	error = pci_iov_delete_iov_children(dinfo);
93294f5c1ccSKonstantin Belousov 
93394f5c1ccSKonstantin Belousov out:
9346c3162c4SRyan Stone 	mtx_unlock(&Giant);
9356c3162c4SRyan Stone 	return (error);
9366c3162c4SRyan Stone }
9376c3162c4SRyan Stone 
9381191f715SRyan Stone static int
9391191f715SRyan Stone pci_iov_get_schema_ioctl(struct cdev *cdev, struct pci_iov_schema *output)
9401191f715SRyan Stone {
9411191f715SRyan Stone 	struct pci_devinfo *dinfo;
9421191f715SRyan Stone 	void *packed;
9431191f715SRyan Stone 	size_t output_len, size;
9441191f715SRyan Stone 	int error;
9451191f715SRyan Stone 
9461191f715SRyan Stone 	packed = NULL;
9471191f715SRyan Stone 
9481191f715SRyan Stone 	mtx_lock(&Giant);
9491191f715SRyan Stone 	dinfo = cdev->si_drv1;
9501191f715SRyan Stone 	packed = nvlist_pack(dinfo->cfg.iov->iov_schema, &size);
9511191f715SRyan Stone 	mtx_unlock(&Giant);
9521191f715SRyan Stone 
9531191f715SRyan Stone 	if (packed == NULL) {
9541191f715SRyan Stone 		error = ENOMEM;
9551191f715SRyan Stone 		goto fail;
9561191f715SRyan Stone 	}
9571191f715SRyan Stone 
9581191f715SRyan Stone 	output_len = output->len;
9591191f715SRyan Stone 	output->len = size;
9601191f715SRyan Stone 	if (size <= output_len) {
9611191f715SRyan Stone 		error = copyout(packed, output->schema, size);
9621191f715SRyan Stone 
9631191f715SRyan Stone 		if (error != 0)
9641191f715SRyan Stone 			goto fail;
9651191f715SRyan Stone 
9661191f715SRyan Stone 		output->error = 0;
9671191f715SRyan Stone 	} else
9681191f715SRyan Stone 		/*
9691191f715SRyan Stone 		 * If we return an error then the ioctl code won't copyout
9701191f715SRyan Stone 		 * output back to userland, so we flag the error in the struct
9711191f715SRyan Stone 		 * instead.
9721191f715SRyan Stone 		 */
9731191f715SRyan Stone 		output->error = EMSGSIZE;
9741191f715SRyan Stone 
9751191f715SRyan Stone 	error = 0;
9761191f715SRyan Stone 
9771191f715SRyan Stone fail:
9781191f715SRyan Stone 	free(packed, M_NVLIST);
9791191f715SRyan Stone 
9801191f715SRyan Stone 	return (error);
9811191f715SRyan Stone }
9826c3162c4SRyan Stone 
9839bfb1e36SRyan Stone static int
9849bfb1e36SRyan Stone pci_iov_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
9859bfb1e36SRyan Stone     struct thread *td)
9869bfb1e36SRyan Stone {
9879bfb1e36SRyan Stone 
9889bfb1e36SRyan Stone 	switch (cmd) {
9899bfb1e36SRyan Stone 	case IOV_CONFIG:
9909bfb1e36SRyan Stone 		return (pci_iov_config(dev, (struct pci_iov_arg *)data));
9916c3162c4SRyan Stone 	case IOV_DELETE:
9926c3162c4SRyan Stone 		return (pci_iov_delete(dev));
9931191f715SRyan Stone 	case IOV_GET_SCHEMA:
9941191f715SRyan Stone 		return (pci_iov_get_schema_ioctl(dev,
9951191f715SRyan Stone 		    (struct pci_iov_schema *)data));
9969bfb1e36SRyan Stone 	default:
9979bfb1e36SRyan Stone 		return (EINVAL);
9989bfb1e36SRyan Stone 	}
9999bfb1e36SRyan Stone }
10009bfb1e36SRyan Stone 
1001e9309eacSRyan Stone struct resource *
10022dd1bdf1SJustin Hibbits pci_vf_alloc_mem_resource(device_t dev, device_t child, int *rid,
10032dd1bdf1SJustin Hibbits     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
1004e9309eacSRyan Stone {
1005e9309eacSRyan Stone 	struct pci_devinfo *dinfo;
1006e9309eacSRyan Stone 	struct pcicfg_iov *iov;
1007e9309eacSRyan Stone 	struct pci_map *map;
1008e9309eacSRyan Stone 	struct resource *res;
1009e9309eacSRyan Stone 	struct resource_list_entry *rle;
10102dd1bdf1SJustin Hibbits 	rman_res_t bar_start, bar_end;
1011e9309eacSRyan Stone 	pci_addr_t bar_length;
1012e9309eacSRyan Stone 	int error;
1013e9309eacSRyan Stone 
1014e9309eacSRyan Stone 	dinfo = device_get_ivars(child);
1015e9309eacSRyan Stone 	iov = dinfo->cfg.iov;
1016e9309eacSRyan Stone 
1017e9309eacSRyan Stone 	map = pci_find_bar(child, *rid);
1018e9309eacSRyan Stone 	if (map == NULL)
1019e9309eacSRyan Stone 		return (NULL);
1020e9309eacSRyan Stone 
1021e9309eacSRyan Stone 	bar_length = 1 << map->pm_size;
1022e9309eacSRyan Stone 	bar_start = map->pm_value;
1023e9309eacSRyan Stone 	bar_end = bar_start + bar_length - 1;
1024e9309eacSRyan Stone 
1025e9309eacSRyan Stone 	/* Make sure that the resource fits the constraints. */
1026e9309eacSRyan Stone 	if (bar_start >= end || bar_end <= bar_start || count != 1)
1027e9309eacSRyan Stone 		return (NULL);
1028e9309eacSRyan Stone 
1029e9309eacSRyan Stone 	/* Clamp the resource to the constraints if necessary. */
1030e9309eacSRyan Stone 	if (bar_start < start)
1031e9309eacSRyan Stone 		bar_start = start;
1032e9309eacSRyan Stone 	if (bar_end > end)
1033e9309eacSRyan Stone 		bar_end = end;
1034e9309eacSRyan Stone 	bar_length = bar_end - bar_start + 1;
1035e9309eacSRyan Stone 
1036e9309eacSRyan Stone 	res = rman_reserve_resource(&iov->rman, bar_start, bar_end,
1037e9309eacSRyan Stone 	    bar_length, flags, child);
1038e9309eacSRyan Stone 	if (res == NULL)
1039e9309eacSRyan Stone 		return (NULL);
1040e9309eacSRyan Stone 
1041e9309eacSRyan Stone 	rle = resource_list_add(&dinfo->resources, SYS_RES_MEMORY, *rid,
1042e9309eacSRyan Stone 	    bar_start, bar_end, 1);
1043e9309eacSRyan Stone 	if (rle == NULL) {
1044e9309eacSRyan Stone 		rman_release_resource(res);
1045e9309eacSRyan Stone 		return (NULL);
1046e9309eacSRyan Stone 	}
1047e9309eacSRyan Stone 
1048e9309eacSRyan Stone 	rman_set_rid(res, *rid);
10491b9bcfffSJohn Baldwin 	rman_set_type(res, SYS_RES_MEMORY);
1050e9309eacSRyan Stone 
1051e9309eacSRyan Stone 	if (flags & RF_ACTIVE) {
1052e9309eacSRyan Stone 		error = bus_activate_resource(child, SYS_RES_MEMORY, *rid, res);
1053e9309eacSRyan Stone 		if (error != 0) {
1054e9309eacSRyan Stone 			resource_list_delete(&dinfo->resources, SYS_RES_MEMORY,
1055e9309eacSRyan Stone 			    *rid);
1056e9309eacSRyan Stone 			rman_release_resource(res);
1057e9309eacSRyan Stone 			return (NULL);
1058e9309eacSRyan Stone 		}
1059e9309eacSRyan Stone 	}
1060e9309eacSRyan Stone 	rle->res = res;
1061e9309eacSRyan Stone 
1062e9309eacSRyan Stone 	return (res);
1063e9309eacSRyan Stone }
1064e9309eacSRyan Stone 
1065e9309eacSRyan Stone int
10669dbf5b0eSJohn Baldwin pci_vf_release_mem_resource(device_t dev, device_t child, struct resource *r)
1067e9309eacSRyan Stone {
1068e9309eacSRyan Stone 	struct pci_devinfo *dinfo;
1069e9309eacSRyan Stone 	struct resource_list_entry *rle;
10709dbf5b0eSJohn Baldwin 	int error, rid;
1071e9309eacSRyan Stone 
1072e9309eacSRyan Stone 	dinfo = device_get_ivars(child);
1073e9309eacSRyan Stone 
1074871b33adSJohn Baldwin 	KASSERT(rman_get_type(r) == SYS_RES_MEMORY,
1075871b33adSJohn Baldwin 	    ("%s: invalid resource %p", __func__, r));
1076871b33adSJohn Baldwin 	KASSERT(rman_is_region_manager(r, &dinfo->cfg.iov->rman),
1077871b33adSJohn Baldwin 	    ("%s: rman %p doesn't match for resource %p", __func__,
1078871b33adSJohn Baldwin 	    &dinfo->cfg.iov->rman, r));
1079871b33adSJohn Baldwin 
1080e9309eacSRyan Stone 	if (rman_get_flags(r) & RF_ACTIVE) {
10819dbf5b0eSJohn Baldwin 		error = bus_deactivate_resource(child, r);
1082e9309eacSRyan Stone 		if (error != 0)
1083e9309eacSRyan Stone 			return (error);
1084e9309eacSRyan Stone 	}
1085e9309eacSRyan Stone 
10869dbf5b0eSJohn Baldwin 	rid = rman_get_rid(r);
1087e9309eacSRyan Stone 	rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY, rid);
1088e9309eacSRyan Stone 	if (rle != NULL) {
1089e9309eacSRyan Stone 		rle->res = NULL;
1090e9309eacSRyan Stone 		resource_list_delete(&dinfo->resources, SYS_RES_MEMORY,
1091e9309eacSRyan Stone 		    rid);
1092e9309eacSRyan Stone 	}
1093e9309eacSRyan Stone 
1094e9309eacSRyan Stone 	return (rman_release_resource(r));
1095e9309eacSRyan Stone }
1096871b33adSJohn Baldwin 
1097871b33adSJohn Baldwin int
1098871b33adSJohn Baldwin pci_vf_activate_mem_resource(device_t dev, device_t child, struct resource *r)
1099871b33adSJohn Baldwin {
1100871b33adSJohn Baldwin #ifdef INVARIANTS
1101871b33adSJohn Baldwin 	struct pci_devinfo *dinfo = device_get_ivars(child);
1102871b33adSJohn Baldwin #endif
1103871b33adSJohn Baldwin 	struct resource_map map;
1104871b33adSJohn Baldwin 	int error;
1105871b33adSJohn Baldwin 
1106871b33adSJohn Baldwin 	KASSERT(rman_get_type(r) == SYS_RES_MEMORY,
1107871b33adSJohn Baldwin 	    ("%s: invalid resource %p", __func__, r));
1108871b33adSJohn Baldwin 	KASSERT(rman_is_region_manager(r, &dinfo->cfg.iov->rman),
1109871b33adSJohn Baldwin 	    ("%s: rman %p doesn't match for resource %p", __func__,
1110871b33adSJohn Baldwin 	    &dinfo->cfg.iov->rman, r));
1111871b33adSJohn Baldwin 
1112871b33adSJohn Baldwin 	error = rman_activate_resource(r);
1113871b33adSJohn Baldwin 	if (error != 0)
1114871b33adSJohn Baldwin 		return (error);
1115871b33adSJohn Baldwin 
1116871b33adSJohn Baldwin 	if ((rman_get_flags(r) & RF_UNMAPPED) == 0) {
1117871b33adSJohn Baldwin 		error = BUS_MAP_RESOURCE(dev, child, r, NULL, &map);
1118871b33adSJohn Baldwin 		if (error != 0) {
1119871b33adSJohn Baldwin 			rman_deactivate_resource(r);
1120871b33adSJohn Baldwin 			return (error);
1121871b33adSJohn Baldwin 		}
1122871b33adSJohn Baldwin 
1123871b33adSJohn Baldwin 		rman_set_mapping(r, &map);
1124871b33adSJohn Baldwin 	}
1125871b33adSJohn Baldwin 	return (0);
1126871b33adSJohn Baldwin }
1127871b33adSJohn Baldwin 
1128871b33adSJohn Baldwin int
1129871b33adSJohn Baldwin pci_vf_deactivate_mem_resource(device_t dev, device_t child, struct resource *r)
1130871b33adSJohn Baldwin {
1131871b33adSJohn Baldwin #ifdef INVARIANTS
1132871b33adSJohn Baldwin 	struct pci_devinfo *dinfo = device_get_ivars(child);
1133871b33adSJohn Baldwin #endif
1134871b33adSJohn Baldwin 	struct resource_map map;
1135871b33adSJohn Baldwin 	int error;
1136871b33adSJohn Baldwin 
1137871b33adSJohn Baldwin 	KASSERT(rman_get_type(r) == SYS_RES_MEMORY,
1138871b33adSJohn Baldwin 	    ("%s: invalid resource %p", __func__, r));
1139871b33adSJohn Baldwin 	KASSERT(rman_is_region_manager(r, &dinfo->cfg.iov->rman),
1140871b33adSJohn Baldwin 	    ("%s: rman %p doesn't match for resource %p", __func__,
1141871b33adSJohn Baldwin 	    &dinfo->cfg.iov->rman, r));
1142871b33adSJohn Baldwin 
1143871b33adSJohn Baldwin 	error = rman_deactivate_resource(r);
1144871b33adSJohn Baldwin 	if (error != 0)
1145871b33adSJohn Baldwin 		return (error);
1146871b33adSJohn Baldwin 
1147871b33adSJohn Baldwin 	if ((rman_get_flags(r) & RF_UNMAPPED) == 0) {
1148871b33adSJohn Baldwin 		rman_get_mapping(r, &map);
1149871b33adSJohn Baldwin 		BUS_UNMAP_RESOURCE(dev, child, r, &map);
1150871b33adSJohn Baldwin 	}
1151871b33adSJohn Baldwin 	return (0);
1152871b33adSJohn Baldwin }
1153871b33adSJohn Baldwin 
1154871b33adSJohn Baldwin int
1155871b33adSJohn Baldwin pci_vf_adjust_mem_resource(device_t dev, device_t child, struct resource *r,
1156871b33adSJohn Baldwin     rman_res_t start, rman_res_t end)
1157871b33adSJohn Baldwin {
1158871b33adSJohn Baldwin #ifdef INVARIANTS
1159871b33adSJohn Baldwin 	struct pci_devinfo *dinfo = device_get_ivars(child);
1160871b33adSJohn Baldwin #endif
1161871b33adSJohn Baldwin 
1162871b33adSJohn Baldwin 	KASSERT(rman_get_type(r) == SYS_RES_MEMORY,
1163871b33adSJohn Baldwin 	    ("%s: invalid resource %p", __func__, r));
1164871b33adSJohn Baldwin 	KASSERT(rman_is_region_manager(r, &dinfo->cfg.iov->rman),
1165871b33adSJohn Baldwin 	    ("%s: rman %p doesn't match for resource %p", __func__,
1166871b33adSJohn Baldwin 	    &dinfo->cfg.iov->rman, r));
1167871b33adSJohn Baldwin 
1168871b33adSJohn Baldwin 	return (rman_adjust_resource(r, start, end));
1169871b33adSJohn Baldwin }
1170871b33adSJohn Baldwin 
1171871b33adSJohn Baldwin static struct resource *
1172871b33adSJohn Baldwin pci_vf_find_parent_resource(struct pcicfg_iov *iov, struct resource *r)
1173871b33adSJohn Baldwin {
1174871b33adSJohn Baldwin 	struct resource *pres;
1175871b33adSJohn Baldwin 
1176871b33adSJohn Baldwin 	for (u_int i = 0; i <= PCIR_MAX_BAR_0; i++) {
1177871b33adSJohn Baldwin 		pres = iov->iov_bar[i].res;
1178871b33adSJohn Baldwin 		if (pres != NULL) {
1179871b33adSJohn Baldwin 			if (rman_get_start(pres) <= rman_get_start(r) &&
1180871b33adSJohn Baldwin 			    rman_get_end(pres) >= rman_get_end(r))
1181871b33adSJohn Baldwin 				return (pres);
1182871b33adSJohn Baldwin 		}
1183871b33adSJohn Baldwin 	}
1184871b33adSJohn Baldwin 	return (NULL);
1185871b33adSJohn Baldwin }
1186871b33adSJohn Baldwin 
1187871b33adSJohn Baldwin int
1188871b33adSJohn Baldwin pci_vf_map_mem_resource(device_t dev, device_t child, struct resource *r,
1189871b33adSJohn Baldwin     struct resource_map_request *argsp, struct resource_map *map)
1190871b33adSJohn Baldwin {
1191871b33adSJohn Baldwin 	struct pci_devinfo *dinfo = device_get_ivars(child);
1192871b33adSJohn Baldwin 	struct pcicfg_iov *iov = dinfo->cfg.iov;
1193871b33adSJohn Baldwin 	struct resource_map_request args;
1194871b33adSJohn Baldwin 	struct resource *pres;
1195871b33adSJohn Baldwin 	rman_res_t length, start;
1196871b33adSJohn Baldwin 	int error;
1197871b33adSJohn Baldwin 
1198871b33adSJohn Baldwin 	KASSERT(rman_get_type(r) == SYS_RES_MEMORY,
1199871b33adSJohn Baldwin 	    ("%s: invalid resource %p", __func__, r));
1200871b33adSJohn Baldwin 	KASSERT(rman_is_region_manager(r, &iov->rman),
1201871b33adSJohn Baldwin 	    ("%s: rman %p doesn't match for resource %p", __func__,
1202871b33adSJohn Baldwin 	    &dinfo->cfg.iov->rman, r));
1203871b33adSJohn Baldwin 
1204871b33adSJohn Baldwin 	/* Resources must be active to be mapped. */
1205871b33adSJohn Baldwin 	if (!(rman_get_flags(r) & RF_ACTIVE))
1206871b33adSJohn Baldwin 		return (ENXIO);
1207871b33adSJohn Baldwin 
1208871b33adSJohn Baldwin 	resource_init_map_request(&args);
1209871b33adSJohn Baldwin 	error = resource_validate_map_request(r, argsp, &args, &start, &length);
1210871b33adSJohn Baldwin 	if (error)
1211871b33adSJohn Baldwin 		return (error);
1212871b33adSJohn Baldwin 
1213871b33adSJohn Baldwin 	pres = pci_vf_find_parent_resource(dinfo->cfg.iov, r);
1214871b33adSJohn Baldwin 	if (pres == NULL)
1215871b33adSJohn Baldwin 		return (ENOENT);
1216871b33adSJohn Baldwin 
1217871b33adSJohn Baldwin 	args.offset = start - rman_get_start(pres);
1218871b33adSJohn Baldwin 	args.length = length;
1219871b33adSJohn Baldwin 	return (bus_map_resource(iov->iov_pf, pres, &args, map));
1220871b33adSJohn Baldwin }
1221871b33adSJohn Baldwin 
1222871b33adSJohn Baldwin int
1223871b33adSJohn Baldwin pci_vf_unmap_mem_resource(device_t dev, device_t child, struct resource *r,
1224871b33adSJohn Baldwin     struct resource_map *map)
1225871b33adSJohn Baldwin {
1226871b33adSJohn Baldwin 	struct pci_devinfo *dinfo = device_get_ivars(child);
1227871b33adSJohn Baldwin 	struct pcicfg_iov *iov = dinfo->cfg.iov;
1228871b33adSJohn Baldwin 	struct resource *pres;
1229871b33adSJohn Baldwin 
1230871b33adSJohn Baldwin 	KASSERT(rman_get_type(r) == SYS_RES_MEMORY,
1231871b33adSJohn Baldwin 	    ("%s: invalid resource %p", __func__, r));
1232871b33adSJohn Baldwin 	KASSERT(rman_is_region_manager(r, &iov->rman),
1233871b33adSJohn Baldwin 	    ("%s: rman %p doesn't match for resource %p", __func__,
1234871b33adSJohn Baldwin 	    &dinfo->cfg.iov->rman, r));
1235871b33adSJohn Baldwin 
1236871b33adSJohn Baldwin 	pres = pci_vf_find_parent_resource(iov, r);
1237871b33adSJohn Baldwin 	if (pres == NULL)
1238871b33adSJohn Baldwin 		return (ENOENT);
1239871b33adSJohn Baldwin 	return (bus_unmap_resource(iov->iov_pf, pres, map));
1240871b33adSJohn Baldwin }
1241