pci_iov.c revision 296336
1139724Simp/*-
24Srgrimes * Copyright (c) 2013-2015 Sandvine Inc.  All rights reserved.
34Srgrimes * All rights reserved.
44Srgrimes *
58876Srgrimes * Redistribution and use in source and binary forms, with or without
64Srgrimes * modification, are permitted provided that the following conditions
74Srgrimes * are met:
84Srgrimes * 1. Redistributions of source code must retain the above copyright
94Srgrimes *    notice, this list of conditions and the following disclaimer.
104Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
118876Srgrimes *    notice, this list of conditions and the following disclaimer in the
128876Srgrimes *    documentation and/or other materials provided with the distribution.
134Srgrimes *
144Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
158876Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
164Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
178876Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
184Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
194Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
204Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
214Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
228876Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
234Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
244Srgrimes * SUCH DAMAGE.
254Srgrimes */
264Srgrimes
27115683Sobrien#include <sys/cdefs.h>
28115683Sobrien__FBSDID("$FreeBSD: head/sys/dev/pci/pci_iov.c 296336 2016-03-03 05:07:35Z jhibbits $");
29115683Sobrien
304Srgrimes#include "opt_bus.h"
314Srgrimes
324Srgrimes#include <sys/param.h>
332056Swollman#include <sys/conf.h>
3424494Sbde#include <sys/kernel.h>
352056Swollman#include <sys/systm.h>
364Srgrimes#include <sys/bus.h>
374Srgrimes#include <sys/fcntl.h>
384Srgrimes#include <sys/ioccom.h>
394Srgrimes#include <sys/iov.h>
404Srgrimes#include <sys/linker.h>
414Srgrimes#include <sys/malloc.h>
424Srgrimes#include <sys/module.h>
434Srgrimes#include <sys/pciio.h>
444Srgrimes#include <sys/queue.h>
454Srgrimes#include <sys/rman.h>
464Srgrimes#include <sys/sysctl.h>
474Srgrimes
484Srgrimes#include <machine/bus.h>
494Srgrimes#include <machine/stdarg.h>
504Srgrimes
514Srgrimes#include <sys/nv.h>
524Srgrimes#include <sys/iov_schema.h>
534Srgrimes
544Srgrimes#include <dev/pci/pcireg.h>
554Srgrimes#include <dev/pci/pcivar.h>
564Srgrimes#include <dev/pci/pci_iov.h>
574Srgrimes#include <dev/pci/pci_private.h>
584Srgrimes#include <dev/pci/pci_iov_private.h>
594Srgrimes#include <dev/pci/schema_private.h>
604Srgrimes
614Srgrimes#include "pcib_if.h"
624Srgrimes
634Srgrimesstatic MALLOC_DEFINE(M_SRIOV, "sr_iov", "PCI SR-IOV allocations");
644Srgrimes
654Srgrimesstatic d_ioctl_t pci_iov_ioctl;
664Srgrimes
674Srgrimesstatic struct cdevsw iov_cdevsw = {
684Srgrimes	.d_version = D_VERSION,
694Srgrimes	.d_name = "iov",
704Srgrimes	.d_ioctl = pci_iov_ioctl
714Srgrimes};
724Srgrimes
734SrgrimesSYSCTL_DECL(_hw_pci);
744Srgrimes
754Srgrimes/*
764Srgrimes * The maximum amount of memory we will allocate for user configuration of an
774Srgrimes * SR-IOV device.  1MB ought to be enough for anyone, but leave this
784Srgrimes * configurable just in case.
794Srgrimes */
804Srgrimesstatic u_long pci_iov_max_config = 1024 * 1024;
814SrgrimesSYSCTL_ULONG(_hw_pci, OID_AUTO, iov_max_config, CTLFLAG_RWTUN,
824Srgrimes    &pci_iov_max_config, 0, "Maximum allowed size of SR-IOV configuration.");
834Srgrimes
844Srgrimes
854Srgrimes#define IOV_READ(d, r, w) \
864Srgrimes	pci_read_config((d)->cfg.dev, (d)->cfg.iov->iov_pos + r, w)
874Srgrimes
8821277Sbde#define IOV_WRITE(d, r, v, w) \
8921277Sbde	pci_write_config((d)->cfg.dev, (d)->cfg.iov->iov_pos + r, v, w)
9021277Sbde
914Srgrimesstatic nvlist_t	*pci_iov_build_schema(nvlist_t **pf_schema,
9211940Sbde		    nvlist_t **vf_schema);
9314887Swollmanstatic void	pci_iov_build_pf_schema(nvlist_t *schema,
944Srgrimes		    nvlist_t **driver_schema);
954Srgrimesstatic void	pci_iov_build_vf_schema(nvlist_t *schema,
964Srgrimes		    nvlist_t **driver_schema);
9717109Sbdestatic nvlist_t	*pci_iov_get_pf_subsystem_schema(void);
984Srgrimesstatic nvlist_t	*pci_iov_get_vf_subsystem_schema(void);
994Srgrimes
1004Srgrimesint
1014Srgrimespci_iov_attach_method(device_t bus, device_t dev, nvlist_t *pf_schema,
1024Srgrimes    nvlist_t *vf_schema)
1034Srgrimes{
10411940Sbde	device_t pcib;
10514887Swollman	struct pci_devinfo *dinfo;
1064Srgrimes	struct pcicfg_iov *iov;
1074Srgrimes	nvlist_t *schema;
10817109Sbde	uint32_t version;
1094Srgrimes	int error;
1104Srgrimes	int iov_pos;
1114Srgrimes
11214887Swollman	dinfo = device_get_ivars(dev);
1134Srgrimes	pcib = device_get_parent(bus);
1144Srgrimes	schema = NULL;
1154Srgrimes
1164Srgrimes	error = pci_find_extcap(dev, PCIZ_SRIOV, &iov_pos);
1174Srgrimes
1184Srgrimes	if (error != 0)
1194Srgrimes		return (error);
1204Srgrimes
1214Srgrimes	version = pci_read_config(dev, iov_pos, 4);
1224Srgrimes	if (PCI_EXTCAP_VER(version) != 1) {
12314887Swollman		if (bootverbose)
1244Srgrimes			device_printf(dev,
1254Srgrimes			    "Unsupported version of SR-IOV (%d) detected\n",
1264Srgrimes			    PCI_EXTCAP_VER(version));
1274Srgrimes
1284Srgrimes		return (ENXIO);
1294Srgrimes	}
1304Srgrimes
1314Srgrimes	iov = malloc(sizeof(*dinfo->cfg.iov), M_SRIOV, M_WAITOK | M_ZERO);
1324Srgrimes
1334Srgrimes	mtx_lock(&Giant);
13414887Swollman	if (dinfo->cfg.iov != NULL) {
1354Srgrimes		error = EBUSY;
1364Srgrimes		goto cleanup;
1374Srgrimes	}
1384Srgrimes	iov->iov_pos = iov_pos;
1394Srgrimes
1404Srgrimes	schema = pci_iov_build_schema(&pf_schema, &vf_schema);
1414Srgrimes	if (schema == NULL) {
1424Srgrimes		error = ENOMEM;
1434Srgrimes		goto cleanup;
1444Srgrimes	}
14521277Sbde
14621277Sbde	error = pci_iov_validate_schema(schema);
14721277Sbde	if (error != 0)
14821277Sbde		goto cleanup;
14921277Sbde	iov->iov_schema = schema;
15021277Sbde
15121277Sbde	iov->iov_cdev = make_dev(&iov_cdevsw, device_get_unit(dev),
15221277Sbde	    UID_ROOT, GID_WHEEL, 0600, "iov/%s", device_get_nameunit(dev));
15321277Sbde
15421277Sbde	if (iov->iov_cdev == NULL) {
15521277Sbde		error = ENOMEM;
156181606Sjhb		goto cleanup;
157181606Sjhb	}
158181606Sjhb
159181606Sjhb	dinfo->cfg.iov = iov;
160181606Sjhb	iov->iov_cdev->si_drv1 = dinfo;
161181606Sjhb	mtx_unlock(&Giant);
162181606Sjhb
163181606Sjhb	return (0);
164181606Sjhb
165181606Sjhbcleanup:
166181606Sjhb	nvlist_destroy(schema);
167181606Sjhb	nvlist_destroy(pf_schema);
168181606Sjhb	nvlist_destroy(vf_schema);
169181606Sjhb	free(iov, M_SRIOV);
170181606Sjhb	mtx_unlock(&Giant);
171181606Sjhb	return (error);
172181606Sjhb}
173181606Sjhb
174181606Sjhbint
175181606Sjhbpci_iov_detach_method(device_t bus, device_t dev)
176181606Sjhb{
177181606Sjhb	struct pci_devinfo *dinfo;
17814887Swollman	struct pcicfg_iov *iov;
17917109Sbde
18017109Sbde	mtx_lock(&Giant);
1814Srgrimes	dinfo = device_get_ivars(dev);
1824Srgrimes	iov = dinfo->cfg.iov;
1834Srgrimes
184181606Sjhb	if (iov == NULL) {
1854Srgrimes		mtx_unlock(&Giant);
186181606Sjhb		return (0);
1874Srgrimes	}
1884Srgrimes
1894Srgrimes	if (iov->iov_num_vfs != 0 || iov->iov_flags & IOV_BUSY) {
1904Srgrimes		mtx_unlock(&Giant);
1914Srgrimes		return (EBUSY);
1924Srgrimes	}
1934Srgrimes
1944Srgrimes	dinfo->cfg.iov = NULL;
1954Srgrimes
1964Srgrimes	if (iov->iov_cdev) {
1974Srgrimes		destroy_dev(iov->iov_cdev);
198278655Smarkj		iov->iov_cdev = NULL;
199278655Smarkj	}
200278655Smarkj	nvlist_destroy(iov->iov_schema);
201278655Smarkj
202278655Smarkj	free(iov, M_SRIOV);
203278655Smarkj	mtx_unlock(&Giant);
204278655Smarkj
205278655Smarkj	return (0);
206278655Smarkj}
207278655Smarkj
208278655Smarkjstatic nvlist_t *
209278655Smarkjpci_iov_build_schema(nvlist_t **pf, nvlist_t **vf)
210278655Smarkj{
211278655Smarkj	nvlist_t *schema, *pf_driver, *vf_driver;
212278655Smarkj
213278655Smarkj	/* We always take ownership of the schemas. */
214278655Smarkj	pf_driver = *pf;
215278655Smarkj	*pf = NULL;
216278655Smarkj	vf_driver = *vf;
217278655Smarkj	*vf = NULL;
21817109Sbde
21921277Sbde	schema = pci_iov_schema_alloc_node();
22021277Sbde	if (schema == NULL)
22121277Sbde		goto cleanup;
22221277Sbde
22321277Sbde	pci_iov_build_pf_schema(schema, &pf_driver);
2244Srgrimes	pci_iov_build_vf_schema(schema, &vf_driver);
22521277Sbde
2264Srgrimes	if (nvlist_error(schema) != 0)
2274Srgrimes		goto cleanup;
2284Srgrimes
2294Srgrimes	return (schema);
2304Srgrimes
2314Srgrimescleanup:
2324Srgrimes	nvlist_destroy(schema);
2334Srgrimes	nvlist_destroy(pf_driver);
2344Srgrimes	nvlist_destroy(vf_driver);
2354Srgrimes	return (NULL);
2364Srgrimes}
2374Srgrimes
23814887Swollmanstatic void
23914887Swollmanpci_iov_build_pf_schema(nvlist_t *schema, nvlist_t **driver_schema)
24014887Swollman{
24114887Swollman	nvlist_t *pf_schema, *iov_schema;
24214887Swollman
243181606Sjhb	pf_schema = pci_iov_schema_alloc_node();
244181606Sjhb	if (pf_schema == NULL) {
24514887Swollman		nvlist_set_error(schema, ENOMEM);
246181606Sjhb		return;
24714887Swollman	}
24814887Swollman
24914887Swollman	iov_schema = pci_iov_get_pf_subsystem_schema();
25014887Swollman
25114887Swollman	/*
25214887Swollman	 * Note that if either *driver_schema or iov_schema is NULL, then
25314887Swollman	 * nvlist_move_nvlist will put the schema in the error state and
25414887Swollman	 * SR-IOV will fail to initialize later, so we don't have to explicitly
25514887Swollman	 * handle that case.
25614887Swollman	 */
25714887Swollman	nvlist_move_nvlist(pf_schema, DRIVER_CONFIG_NAME, *driver_schema);
258181603Sjhb	nvlist_move_nvlist(pf_schema, IOV_CONFIG_NAME, iov_schema);
259181603Sjhb	nvlist_move_nvlist(schema, PF_CONFIG_NAME, pf_schema);
260181603Sjhb	*driver_schema = NULL;
261181603Sjhb}
262181603Sjhb
263181603Sjhbstatic void
264181603Sjhbpci_iov_build_vf_schema(nvlist_t *schema, nvlist_t **driver_schema)
265181603Sjhb{
266181603Sjhb	nvlist_t *vf_schema, *iov_schema;
267181603Sjhb
268181603Sjhb	vf_schema = pci_iov_schema_alloc_node();
269181603Sjhb	if (vf_schema == NULL) {
270181603Sjhb		nvlist_set_error(schema, ENOMEM);
271181603Sjhb		return;
272181603Sjhb	}
273181603Sjhb
274181603Sjhb	iov_schema = pci_iov_get_vf_subsystem_schema();
275181603Sjhb
276181603Sjhb	/*
277181603Sjhb	 * Note that if either *driver_schema or iov_schema is NULL, then
27817109Sbde	 * nvlist_move_nvlist will put the schema in the error state and
2794Srgrimes	 * SR-IOV will fail to initialize later, so we don't have to explicitly
2804Srgrimes	 * handle that case.
2814Srgrimes	 */
2824Srgrimes	nvlist_move_nvlist(vf_schema, DRIVER_CONFIG_NAME, *driver_schema);
2834Srgrimes	nvlist_move_nvlist(vf_schema, IOV_CONFIG_NAME, iov_schema);
2844Srgrimes	nvlist_move_nvlist(schema, VF_SCHEMA_NAME, vf_schema);
2854Srgrimes	*driver_schema = NULL;
2864Srgrimes}
2874Srgrimes
2884Srgrimesstatic nvlist_t *
2894Srgrimespci_iov_get_pf_subsystem_schema(void)
2904Srgrimes{
2914Srgrimes	nvlist_t *pf;
2924Srgrimes
2934Srgrimes	pf = pci_iov_schema_alloc_node();
2944Srgrimes	if (pf == NULL)
2954Srgrimes		return (NULL);
2964Srgrimes
2974Srgrimes	pci_iov_schema_add_uint16(pf, "num_vfs", IOV_SCHEMA_REQUIRED, -1);
29817109Sbde	pci_iov_schema_add_string(pf, "device", IOV_SCHEMA_REQUIRED, NULL);
2994Srgrimes
3004Srgrimes	return (pf);
3014Srgrimes}
3024Srgrimes
3034Srgrimesstatic nvlist_t *
3044Srgrimespci_iov_get_vf_subsystem_schema(void)
3054Srgrimes{
3064Srgrimes	nvlist_t *vf;
3074Srgrimes
3084Srgrimes	vf = pci_iov_schema_alloc_node();
3094Srgrimes	if (vf == NULL)
3104Srgrimes		return (NULL);
3114Srgrimes
3124Srgrimes	pci_iov_schema_add_bool(vf, "passthrough", IOV_SCHEMA_HASDEFAULT, 0);
3134Srgrimes
3144Srgrimes	return (vf);
3154Srgrimes}
3164Srgrimes
3174Srgrimesstatic int
31817109Sbdepci_iov_alloc_bar(struct pci_devinfo *dinfo, int bar, pci_addr_t bar_shift)
3194Srgrimes{
3204Srgrimes	struct resource *res;
32121277Sbde	struct pcicfg_iov *iov;
32221277Sbde	device_t dev, bus;
32317109Sbde	rman_res_t start, end;
32417109Sbde	pci_addr_t bar_size;
3254Srgrimes	int rid;
3264Srgrimes
3274Srgrimes	iov = dinfo->cfg.iov;
3284Srgrimes	dev = dinfo->cfg.dev;
3294Srgrimes	bus = device_get_parent(dev);
33021277Sbde	rid = iov->iov_pos + PCIR_SRIOV_BAR(bar);
33121277Sbde	bar_size = 1 << bar_shift;
33217109Sbde
33317109Sbde	res = pci_alloc_multi_resource(bus, dev, SYS_RES_MEMORY, &rid, 0,
334181606Sjhb	    ~0, 1, iov->iov_num_vfs, RF_ACTIVE);
335181606Sjhb
3364Srgrimes	if (res == NULL)
3374Srgrimes		return (ENXIO);
33817109Sbde
33921277Sbde	iov->iov_bar[bar].res = res;
34021277Sbde	iov->iov_bar[bar].bar_size = bar_size;
3414Srgrimes	iov->iov_bar[bar].bar_shift = bar_shift;
34221277Sbde
3434Srgrimes	start = rman_get_start(res);
3444Srgrimes	end = rman_get_end(res);
34521277Sbde	return (rman_manage_region(&iov->rman, start, end));
34621277Sbde}
3474Srgrimes
3484Srgrimesstatic void
3494Srgrimespci_iov_add_bars(struct pcicfg_iov *iov, struct pci_devinfo *dinfo)
35017109Sbde{
3514Srgrimes	struct pci_iov_bar *bar;
3524Srgrimes	uint64_t bar_start;
3534Srgrimes	int i;
35421277Sbde
35521277Sbde	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
3564Srgrimes		bar = &iov->iov_bar[i];
3574Srgrimes		if (bar->res != NULL) {
35817109Sbde			bar_start = rman_get_start(bar->res) +
3594Srgrimes			    dinfo->cfg.vf.index * bar->bar_size;
3604Srgrimes
3614Srgrimes			pci_add_bar(dinfo->cfg.dev, PCIR_BAR(i), bar_start,
3624Srgrimes			    bar->bar_shift);
3634Srgrimes		}
3644Srgrimes	}
3654Srgrimes}
36621277Sbde
36721277Sbdestatic int
36821277Sbdepci_iov_parse_config(struct pcicfg_iov *iov, struct pci_iov_arg *arg,
36921277Sbde    nvlist_t **ret)
37021277Sbde{
37121277Sbde	void *packed_config;
37221277Sbde	nvlist_t *config;
37321277Sbde	int error;
37421277Sbde
3754Srgrimes	config = NULL;
3764Srgrimes	packed_config = NULL;
37714887Swollman
3784Srgrimes	if (arg->len > pci_iov_max_config) {
379278655Smarkj		error = EMSGSIZE;
3804Srgrimes		goto out;
38114887Swollman	}
382181603Sjhb
3834Srgrimes	packed_config = malloc(arg->len, M_SRIOV, M_WAITOK);
3844Srgrimes
3854Srgrimes	error = copyin(arg->config, packed_config, arg->len);
3864Srgrimes	if (error != 0)
3874Srgrimes		goto out;
3884Srgrimes
3894Srgrimes	config = nvlist_unpack(packed_config, arg->len, NV_FLAG_IGNORE_CASE);
3904Srgrimes	if (config == NULL) {
3914Srgrimes		error = EINVAL;
39221277Sbde		goto out;
3934Srgrimes	}
3944Srgrimes
3954Srgrimes	error = pci_iov_schema_validate_config(iov->iov_schema, config);
39614887Swollman	if (error != 0)
3974Srgrimes		goto out;
3984Srgrimes
39914887Swollman	error = nvlist_error(config);
4004Srgrimes	if (error != 0)
4014Srgrimes		goto out;
40217109Sbde
4034Srgrimes	*ret = config;
4044Srgrimes	config = NULL;
40517109Sbde
4064Srgrimesout:
4074Srgrimes	nvlist_destroy(config);
4084Srgrimes	free(packed_config, M_SRIOV);
40914887Swollman	return (error);
4104Srgrimes}
4114Srgrimes
4124Srgrimes/*
41321277Sbde * Set the ARI_EN bit in the lowest-numbered PCI function with the SR-IOV
4144Srgrimes * capability.  This bit is only writeable on the lowest-numbered PF but
4154Srgrimes * affects all PFs on the device.
4164Srgrimes */
41717109Sbdestatic int
41821277Sbdepci_iov_set_ari(device_t bus)
4194Srgrimes{
4204Srgrimes	device_t lowest;
42114887Swollman	device_t *devlist;
4224Srgrimes	int i, error, devcount, lowest_func, lowest_pos, iov_pos, dev_func;
4234Srgrimes	uint16_t iov_ctl;
4244Srgrimes
42517109Sbde	/* If ARI is disabled on the downstream port there is nothing to do. */
4264Srgrimes	if (!PCIB_ARI_ENABLED(device_get_parent(bus)))
4274Srgrimes		return (0);
4284Srgrimes
42914887Swollman	error = device_get_children(bus, &devlist, &devcount);
4304Srgrimes
4314Srgrimes	if (error != 0)
4324Srgrimes		return (error);
4334Srgrimes
4344Srgrimes	lowest = NULL;
4354Srgrimes	for (i = 0; i < devcount; i++) {
4364Srgrimes		if (pci_find_extcap(devlist[i], PCIZ_SRIOV, &iov_pos) == 0) {
4374Srgrimes			dev_func = pci_get_function(devlist[i]);
4384Srgrimes			if (lowest == NULL || dev_func < lowest_func) {
4394Srgrimes				lowest = devlist[i];
44014887Swollman				lowest_func = dev_func;
4414Srgrimes				lowest_pos = iov_pos;
4424Srgrimes			}
44317109Sbde		}
44421277Sbde	}
44517109Sbde
44617109Sbde	/*
44717109Sbde	 * If we called this function some device must have the SR-IOV
44817109Sbde	 * capability.
4494Srgrimes	 */
4504Srgrimes	KASSERT(lowest != NULL,
45114887Swollman	    ("Could not find child of %s with SR-IOV capability",
45221277Sbde	    device_get_nameunit(bus)));
45321277Sbde
45421277Sbde	iov_ctl = pci_read_config(lowest, iov_pos + PCIR_SRIOV_CTL, 2);
45521277Sbde	iov_ctl |= PCIM_SRIOV_ARI_EN;
45621277Sbde	pci_write_config(lowest, iov_pos + PCIR_SRIOV_CTL, iov_ctl, 2);
45721277Sbde	free(devlist, M_TEMP);
45821277Sbde	return (0);
45921277Sbde}
4604Srgrimes
4614Srgrimesstatic int
46214887Swollmanpci_iov_config_page_size(struct pci_devinfo *dinfo)
46321277Sbde{
4644Srgrimes	uint32_t page_cap, page_size;
46521277Sbde
46621277Sbde	page_cap = IOV_READ(dinfo, PCIR_SRIOV_PAGE_CAP, 4);
46717109Sbde
4684Srgrimes	/*
4694Srgrimes	 * If the system page size is less than the smallest SR-IOV page size
4704Srgrimes	 * then round up to the smallest SR-IOV page size.
4714Srgrimes	 */
4724Srgrimes	if (PAGE_SHIFT < PCI_SRIOV_BASE_PAGE_SHIFT)
47314887Swollman		page_size = (1 << 0);
4744Srgrimes	else
4754Srgrimes		page_size = (1 << (PAGE_SHIFT - PCI_SRIOV_BASE_PAGE_SHIFT));
47621277Sbde
47721277Sbde	/* Check that the device supports the system page size. */
4784Srgrimes	if (!(page_size & page_cap))
4794Srgrimes		return (ENXIO);
4804Srgrimes
4814Srgrimes	IOV_WRITE(dinfo, PCIR_SRIOV_PAGE_SIZE, page_size, 4);
4824Srgrimes	return (0);
4834Srgrimes}
48414887Swollman
4854Srgrimesstatic int
4864Srgrimespci_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *config)
4874Srgrimes{
4884Srgrimes	const nvlist_t *device, *driver_config;
4894Srgrimes
4904Srgrimes	device = nvlist_get_nvlist(config, PF_CONFIG_NAME);
4914Srgrimes	driver_config = nvlist_get_nvlist(device, DRIVER_CONFIG_NAME);
4924Srgrimes	return (PCI_IOV_INIT(dev, num_vfs, driver_config));
4934Srgrimes}
4944Srgrimes
49514887Swollmanstatic int
49621277Sbdepci_iov_init_rman(device_t pf, struct pcicfg_iov *iov)
49721277Sbde{
49821277Sbde	int error;
49921277Sbde
50021277Sbde	iov->rman.rm_start = 0;
50121277Sbde	iov->rman.rm_end = ~0;
50221277Sbde	iov->rman.rm_type = RMAN_ARRAY;
50321277Sbde	snprintf(iov->rman_name, sizeof(iov->rman_name), "%s VF I/O memory",
5044Srgrimes	    device_get_nameunit(pf));
5054Srgrimes	iov->rman.rm_descr = iov->rman_name;
50614887Swollman
50721277Sbde	error = rman_init(&iov->rman);
50821277Sbde	if (error != 0)
50921277Sbde		return (error);
51021277Sbde
51117109Sbde	iov->iov_flags |= IOV_RMAN_INITED;
51221277Sbde	return (0);
5134Srgrimes}
51421277Sbde
5154Srgrimesstatic int
5164Srgrimespci_iov_alloc_bar_ea(struct pci_devinfo *dinfo, int bar)
51717109Sbde{
5184Srgrimes	struct pcicfg_iov *iov;
5194Srgrimes	rman_res_t start, end;
5204Srgrimes	struct resource *res;
5214Srgrimes	struct resource_list *rl;
52214887Swollman	struct resource_list_entry *rle;
5234Srgrimes
5244Srgrimes	rl = &dinfo->resources;
5254Srgrimes	iov = dinfo->cfg.iov;
5264Srgrimes
5274Srgrimes	rle = resource_list_find(rl, SYS_RES_MEMORY,
5284Srgrimes	    iov->iov_pos + PCIR_SRIOV_BAR(bar));
5294Srgrimes	if (rle == NULL)
5304Srgrimes		rle = resource_list_find(rl, SYS_RES_IOPORT,
5314Srgrimes		    iov->iov_pos + PCIR_SRIOV_BAR(bar));
5324Srgrimes	if (rle == NULL)
53314887Swollman		return (ENXIO);
5344Srgrimes	res = rle->res;
5354Srgrimes
5364Srgrimes	iov->iov_bar[bar].res = res;
5374Srgrimes	iov->iov_bar[bar].bar_size = rman_get_size(res) / iov->iov_num_vfs;
5384Srgrimes	iov->iov_bar[bar].bar_shift = pci_mapsize(iov->iov_bar[bar].bar_size);
5394Srgrimes
5404Srgrimes	start = rman_get_start(res);
5414Srgrimes	end = rman_get_end(res);
5424Srgrimes
5434Srgrimes	return (rman_manage_region(&iov->rman, start, end));
54414887Swollman}
5454Srgrimes
5464Srgrimesstatic int
5474Srgrimespci_iov_setup_bars(struct pci_devinfo *dinfo)
5484Srgrimes{
5494Srgrimes	device_t dev;
5504Srgrimes	struct pcicfg_iov *iov;
5514Srgrimes	pci_addr_t bar_value, testval;
5524Srgrimes	int i, last_64, error;
5534Srgrimes
5544Srgrimes	iov = dinfo->cfg.iov;
55517109Sbde	dev = dinfo->cfg.dev;
5564Srgrimes	last_64 = 0;
5574Srgrimes
5584Srgrimes	pci_add_resources_ea(device_get_parent(dev), dev, 1);
5594Srgrimes
5604Srgrimes	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
5614Srgrimes		/* First, try to use BARs allocated with EA */
5624Srgrimes		error = pci_iov_alloc_bar_ea(dinfo, i);
5634Srgrimes		if (error == 0)
5644Srgrimes			continue;
5654Srgrimes
56617109Sbde		/* Allocate legacy-BAR only if EA is not enabled */
5674Srgrimes		if (pci_ea_is_enabled(dev, iov->iov_pos + PCIR_SRIOV_BAR(i)))
5684Srgrimes			continue;
56921277Sbde
57021277Sbde		/*
57121277Sbde		 * If a PCI BAR is a 64-bit wide BAR, then it spans two
57221277Sbde		 * consecutive registers.  Therefore if the last BAR that
5734Srgrimes		 * we looked at was a 64-bit BAR, we need to skip this
5744Srgrimes		 * register as it's the second half of the last BAR.
5754Srgrimes		 */
5764Srgrimes		if (!last_64) {
57714887Swollman			pci_read_bar(dev,
5784Srgrimes			    iov->iov_pos + PCIR_SRIOV_BAR(i),
5794Srgrimes			    &bar_value, &testval, &last_64);
5804Srgrimes
5814Srgrimes			if (testval != 0) {
58221277Sbde				error = pci_iov_alloc_bar(dinfo, i,
5834Srgrimes				   pci_mapsize(testval));
5844Srgrimes				if (error != 0)
5854Srgrimes					return (error);
5864Srgrimes			}
5874Srgrimes		} else
5884Srgrimes			last_64 = 0;
5894Srgrimes	}
5904Srgrimes
5914Srgrimes	return (0);
5924Srgrimes}
5934Srgrimes
5944Srgrimesstatic void
5954Srgrimespci_iov_enumerate_vfs(struct pci_devinfo *dinfo, const nvlist_t *config,
5964Srgrimes    uint16_t first_rid, uint16_t rid_stride)
5974Srgrimes{
5984Srgrimes	char device_name[VF_MAX_NAME];
5994Srgrimes	const nvlist_t *device, *driver_config, *iov_config;
60021277Sbde	device_t bus, dev, vf;
6014Srgrimes	struct pcicfg_iov *iov;
6024Srgrimes	struct pci_devinfo *vfinfo;
6034Srgrimes	size_t size;
6044Srgrimes	int i, error;
6054Srgrimes	uint16_t vid, did, next_rid;
6064Srgrimes
6074Srgrimes	iov = dinfo->cfg.iov;
6084Srgrimes	dev = dinfo->cfg.dev;
60921277Sbde	bus = device_get_parent(dev);
6104Srgrimes	size = dinfo->cfg.devinfo_size;
6114Srgrimes	next_rid = first_rid;
6124Srgrimes	vid = pci_get_vendor(dev);
6134Srgrimes	did = IOV_READ(dinfo, PCIR_SRIOV_VF_DID, 2);
6144Srgrimes
6154Srgrimes	for (i = 0; i < iov->iov_num_vfs; i++, next_rid += rid_stride) {
6164Srgrimes		snprintf(device_name, sizeof(device_name), VF_PREFIX"%d", i);
6174Srgrimes		device = nvlist_get_nvlist(config, device_name);
6184Srgrimes		iov_config = nvlist_get_nvlist(device, IOV_CONFIG_NAME);
6194Srgrimes		driver_config = nvlist_get_nvlist(device, DRIVER_CONFIG_NAME);
6204Srgrimes
62121277Sbde		vf = PCI_CREATE_IOV_CHILD(bus, dev, next_rid, vid, did);
6224Srgrimes		if (vf == NULL)
6234Srgrimes			break;
6244Srgrimes
6254Srgrimes		/*
6264Srgrimes		 * If we are creating passthrough devices then force the ppt
62721277Sbde		 * driver to attach to prevent a VF driver from claiming the
6284Srgrimes		 * VFs.
6294Srgrimes		 */
6304Srgrimes		if (nvlist_get_bool(iov_config, "passthrough"))
6314Srgrimes			device_set_devclass_fixed(vf, "ppt");
6324Srgrimes
6334Srgrimes		vfinfo = device_get_ivars(vf);
6344Srgrimes
6354Srgrimes		vfinfo->cfg.iov = iov;
6364Srgrimes		vfinfo->cfg.vf.index = i;
6374Srgrimes
6384Srgrimes		pci_iov_add_bars(iov, vfinfo);
63921277Sbde
6404Srgrimes		error = PCI_IOV_ADD_VF(dev, i, driver_config);
6414Srgrimes		if (error != 0) {
6424Srgrimes			device_printf(dev, "Failed to add VF %d\n", i);
6434Srgrimes			pci_delete_child(bus, vf);
6444Srgrimes		}
64521277Sbde	}
6464Srgrimes
6474Srgrimes	bus_generic_attach(bus);
6484Srgrimes}
6494Srgrimes
6504Srgrimesstatic int
6514Srgrimespci_iov_config(struct cdev *cdev, struct pci_iov_arg *arg)
6524Srgrimes{
6534Srgrimes	device_t bus, dev;
6544Srgrimes	struct pci_devinfo *dinfo;
6554Srgrimes	struct pcicfg_iov *iov;
6564Srgrimes	nvlist_t *config;
6574Srgrimes	int i, error;
6584Srgrimes	uint16_t rid_off, rid_stride;
6594Srgrimes	uint16_t first_rid, last_rid;
6604Srgrimes	uint16_t iov_ctl;
6614Srgrimes	uint16_t num_vfs, total_vfs;
6624Srgrimes	int iov_inited;
6634Srgrimes
6644Srgrimes	mtx_lock(&Giant);
6654Srgrimes	dinfo = cdev->si_drv1;
6664Srgrimes	iov = dinfo->cfg.iov;
6674Srgrimes	dev = dinfo->cfg.dev;
6684Srgrimes	bus = device_get_parent(dev);
6694Srgrimes	iov_inited = 0;
6704Srgrimes	config = NULL;
6714Srgrimes
6724Srgrimes	if ((iov->iov_flags & IOV_BUSY) || iov->iov_num_vfs != 0) {
6734Srgrimes		mtx_unlock(&Giant);
6744Srgrimes		return (EBUSY);
6754Srgrimes	}
6764Srgrimes	iov->iov_flags |= IOV_BUSY;
6774Srgrimes
6784Srgrimes	error = pci_iov_parse_config(iov, arg, &config);
6794Srgrimes	if (error != 0)
6804Srgrimes		goto out;
6814Srgrimes
6824Srgrimes	num_vfs = pci_iov_config_get_num_vfs(config);
6834Srgrimes	total_vfs = IOV_READ(dinfo, PCIR_SRIOV_TOTAL_VFS, 2);
6844Srgrimes	if (num_vfs > total_vfs) {
6854Srgrimes		error = EINVAL;
6864Srgrimes		goto out;
6874Srgrimes	}
6884Srgrimes
68921277Sbde	error = pci_iov_config_page_size(dinfo);
6904Srgrimes	if (error != 0)
6914Srgrimes		goto out;
6924Srgrimes
6934Srgrimes	error = pci_iov_set_ari(bus);
6944Srgrimes	if (error != 0)
6954Srgrimes		goto out;
6964Srgrimes
6974Srgrimes	error = pci_iov_init(dev, num_vfs, config);
69821277Sbde	if (error != 0)
6994Srgrimes		goto out;
7004Srgrimes	iov_inited = 1;
7014Srgrimes
7024Srgrimes	IOV_WRITE(dinfo, PCIR_SRIOV_NUM_VFS, num_vfs, 2);
7034Srgrimes
7044Srgrimes	rid_off = IOV_READ(dinfo, PCIR_SRIOV_VF_OFF, 2);
7054Srgrimes	rid_stride = IOV_READ(dinfo, PCIR_SRIOV_VF_STRIDE, 2);
7064Srgrimes
7074Srgrimes	first_rid = pci_get_rid(dev) + rid_off;
7084Srgrimes	last_rid = first_rid + (num_vfs - 1) * rid_stride;
7094Srgrimes
7104Srgrimes	/* We don't yet support allocating extra bus numbers for VFs. */
7114Srgrimes	if (pci_get_bus(dev) != PCI_RID2BUS(last_rid)) {
7124Srgrimes		error = ENOSPC;
7134Srgrimes		goto out;
7144Srgrimes	}
7154Srgrimes
7164Srgrimes	iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2);
7174Srgrimes	iov_ctl &= ~(PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE);
7184Srgrimes	IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2);
7194Srgrimes
7204Srgrimes	error = pci_iov_init_rman(dev, iov);
7214Srgrimes	if (error != 0)
7224Srgrimes		goto out;
72317109Sbde
72417109Sbde	iov->iov_num_vfs = num_vfs;
72521277Sbde
72617109Sbde	error = pci_iov_setup_bars(dinfo);
7274Srgrimes	if (error != 0)
7284Srgrimes		goto out;
7294Srgrimes
7304Srgrimes	iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2);
7314Srgrimes	iov_ctl |= PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE;
7324Srgrimes	IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2);
7334Srgrimes
7344Srgrimes	/* Per specification, we must wait 100ms before accessing VFs. */
7354Srgrimes	pause("iov", roundup(hz, 10));
7364Srgrimes	pci_iov_enumerate_vfs(dinfo, config, first_rid, rid_stride);
7374Srgrimes
7384Srgrimes	nvlist_destroy(config);
7394Srgrimes	iov->iov_flags &= ~IOV_BUSY;
7404Srgrimes	mtx_unlock(&Giant);
7414Srgrimes
7424Srgrimes	return (0);
7434Srgrimesout:
7444Srgrimes	if (iov_inited)
7454Srgrimes		PCI_IOV_UNINIT(dev);
7464Srgrimes
7474Srgrimes	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
7484Srgrimes		if (iov->iov_bar[i].res != NULL) {
7494Srgrimes			pci_release_resource(bus, dev, SYS_RES_MEMORY,
7504Srgrimes			    iov->iov_pos + PCIR_SRIOV_BAR(i),
7514Srgrimes			    iov->iov_bar[i].res);
7524Srgrimes			pci_delete_resource(bus, dev, SYS_RES_MEMORY,
7534Srgrimes			    iov->iov_pos + PCIR_SRIOV_BAR(i));
7544Srgrimes			iov->iov_bar[i].res = NULL;
7554Srgrimes		}
7564Srgrimes	}
7574Srgrimes
7584Srgrimes	if (iov->iov_flags & IOV_RMAN_INITED) {
7594Srgrimes		rman_fini(&iov->rman);
7604Srgrimes		iov->iov_flags &= ~IOV_RMAN_INITED;
7614Srgrimes	}
7624Srgrimes
7634Srgrimes	nvlist_destroy(config);
7644Srgrimes	iov->iov_num_vfs = 0;
7654Srgrimes	iov->iov_flags &= ~IOV_BUSY;
7664Srgrimes	mtx_unlock(&Giant);
7674Srgrimes	return (error);
7684Srgrimes}
7694Srgrimes
7704Srgrimes/* Return true if child is a VF of the given PF. */
7714Srgrimesstatic int
772118Srgrimespci_iov_is_child_vf(struct pcicfg_iov *pf, device_t child)
773118Srgrimes{
7744Srgrimes	struct pci_devinfo *vfinfo;
7754Srgrimes
7764Srgrimes	vfinfo = device_get_ivars(child);
7774Srgrimes
7784Srgrimes	if (!(vfinfo->cfg.flags & PCICFG_VF))
7794Srgrimes		return (0);
7804Srgrimes
7814Srgrimes	return (pf == vfinfo->cfg.iov);
7824Srgrimes}
7834Srgrimes
7844Srgrimesstatic int
7854Srgrimespci_iov_delete(struct cdev *cdev)
7864Srgrimes{
7874Srgrimes	device_t bus, dev, vf, *devlist;
7884Srgrimes	struct pci_devinfo *dinfo;
7894Srgrimes	struct pcicfg_iov *iov;
7904Srgrimes	int i, error, devcount;
7914Srgrimes	uint32_t iov_ctl;
7924Srgrimes
7934Srgrimes	mtx_lock(&Giant);
7944Srgrimes	dinfo = cdev->si_drv1;
79517109Sbde	iov = dinfo->cfg.iov;
79617109Sbde	dev = dinfo->cfg.dev;
7974Srgrimes	bus = device_get_parent(dev);
7984Srgrimes	devlist = NULL;
7994Srgrimes
8004Srgrimes	if (iov->iov_flags & IOV_BUSY) {
8014Srgrimes		mtx_unlock(&Giant);
8024Srgrimes		return (EBUSY);
8034Srgrimes	}
80421277Sbde
805270844Spfg	if (iov->iov_num_vfs == 0) {
8064Srgrimes		mtx_unlock(&Giant);
8074Srgrimes		return (ECHILD);
8084Srgrimes	}
8094Srgrimes
8104Srgrimes	iov->iov_flags |= IOV_BUSY;
8114Srgrimes
8124Srgrimes	error = device_get_children(bus, &devlist, &devcount);
81317109Sbde
81417109Sbde	if (error != 0)
81517109Sbde		goto out;
81617109Sbde
81721277Sbde	for (i = 0; i < devcount; i++) {
81821277Sbde		vf = devlist[i];
81921277Sbde
8204Srgrimes		if (!pci_iov_is_child_vf(iov, vf))
8214Srgrimes			continue;
82217109Sbde
82317109Sbde		error = device_detach(vf);
82417109Sbde		if (error != 0) {
82517109Sbde			device_printf(dev,
82617109Sbde			   "Could not disable SR-IOV: failed to detach VF %s\n",
82717109Sbde			    device_get_nameunit(vf));
82817109Sbde			goto out;
82917109Sbde		}
8304Srgrimes	}
8314Srgrimes
8324Srgrimes	for (i = 0; i < devcount; i++) {
8334Srgrimes		vf = devlist[i];
8344Srgrimes
8354Srgrimes		if (pci_iov_is_child_vf(iov, vf))
8364Srgrimes			pci_delete_child(bus, vf);
8374Srgrimes	}
8384Srgrimes	PCI_IOV_UNINIT(dev);
8394Srgrimes
8404Srgrimes	iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2);
8414Srgrimes	iov_ctl &= ~(PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE);
8424Srgrimes	IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2);
8434Srgrimes	IOV_WRITE(dinfo, PCIR_SRIOV_NUM_VFS, 0, 2);
8444Srgrimes
8454Srgrimes	iov->iov_num_vfs = 0;
8464Srgrimes
8474Srgrimes	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
8484Srgrimes		if (iov->iov_bar[i].res != NULL) {
8494Srgrimes			pci_release_resource(bus, dev, SYS_RES_MEMORY,
85021277Sbde			    iov->iov_pos + PCIR_SRIOV_BAR(i),
8514Srgrimes			    iov->iov_bar[i].res);
8524Srgrimes			pci_delete_resource(bus, dev, SYS_RES_MEMORY,
8534Srgrimes			    iov->iov_pos + PCIR_SRIOV_BAR(i));
8544Srgrimes			iov->iov_bar[i].res = NULL;
85517109Sbde		}
85617109Sbde	}
8574Srgrimes
8584Srgrimes	if (iov->iov_flags & IOV_RMAN_INITED) {
8594Srgrimes		rman_fini(&iov->rman);
8604Srgrimes		iov->iov_flags &= ~IOV_RMAN_INITED;
8614Srgrimes	}
8624Srgrimes
8634Srgrimes	error = 0;
86417109Sbdeout:
86517109Sbde	free(devlist, M_TEMP);
8664Srgrimes	iov->iov_flags &= ~IOV_BUSY;
8674Srgrimes	mtx_unlock(&Giant);
86817109Sbde	return (error);
8694Srgrimes}
8704Srgrimes
8714Srgrimesstatic int
8724Srgrimespci_iov_get_schema_ioctl(struct cdev *cdev, struct pci_iov_schema *output)
8734Srgrimes{
8744Srgrimes	struct pci_devinfo *dinfo;
8754Srgrimes	void *packed;
8764Srgrimes	size_t output_len, size;
8774Srgrimes	int error;
8784Srgrimes
8794Srgrimes	packed = NULL;
88011940Sbde
8814Srgrimes	mtx_lock(&Giant);
8824Srgrimes	dinfo = cdev->si_drv1;
88314887Swollman	packed = nvlist_pack(dinfo->cfg.iov->iov_schema, &size);
88414887Swollman	mtx_unlock(&Giant);
8854Srgrimes
8864Srgrimes	if (packed == NULL) {
8874Srgrimes		error = ENOMEM;
88814887Swollman		goto fail;
8894Srgrimes	}
8904Srgrimes
8914Srgrimes	output_len = output->len;
8924Srgrimes	output->len = size;
8934Srgrimes	if (size <= output_len) {
8944Srgrimes		error = copyout(packed, output->schema, size);
8954Srgrimes
8964Srgrimes		if (error != 0)
8974Srgrimes			goto fail;
8984Srgrimes
89914887Swollman		output->error = 0;
90043314Sdillon	} else
90143314Sdillon		/*
90243314Sdillon		 * If we return an error then the ioctl code won't copyout
9034Srgrimes		 * output back to userland, so we flag the error in the struct
9044Srgrimes		 * instead.
90517109Sbde		 */
9064Srgrimes		output->error = EMSGSIZE;
9074Srgrimes
9084Srgrimes	error = 0;
9094Srgrimes
9104Srgrimesfail:
9114Srgrimes	free(packed, M_NVLIST);
91214887Swollman
9134Srgrimes	return (error);
9144Srgrimes}
9154Srgrimes
9164Srgrimesstatic int
9174Srgrimespci_iov_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
9184Srgrimes    struct thread *td)
9194Srgrimes{
9204Srgrimes
9214Srgrimes	switch (cmd) {
9224Srgrimes	case IOV_CONFIG:
9234Srgrimes		return (pci_iov_config(dev, (struct pci_iov_arg *)data));
9244Srgrimes	case IOV_DELETE:
9254Srgrimes		return (pci_iov_delete(dev));
92611940Sbde	case IOV_GET_SCHEMA:
92792770Salfred		return (pci_iov_get_schema_ioctl(dev,
92893017Sbde		    (struct pci_iov_schema *)data));
92992770Salfred	default:
93093017Sbde		return (EINVAL);
93111940Sbde	}
93293017Sbde}
93393017Sbde
93411940Sbdestruct resource *
9354Srgrimespci_vf_alloc_mem_resource(device_t dev, device_t child, int *rid,
9364Srgrimes    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
9374Srgrimes{
93811921Sphk	struct pci_devinfo *dinfo;
9394Srgrimes	struct pcicfg_iov *iov;
9404Srgrimes	struct pci_map *map;
9414Srgrimes	struct resource *res;
9424Srgrimes	struct resource_list_entry *rle;
94317109Sbde	rman_res_t bar_start, bar_end;
9444Srgrimes	pci_addr_t bar_length;
9453436Sphk	int error;
9464Srgrimes
9474Srgrimes	dinfo = device_get_ivars(child);
9484Srgrimes	iov = dinfo->cfg.iov;
9494Srgrimes
9504Srgrimes	map = pci_find_bar(child, *rid);
9514Srgrimes	if (map == NULL)
9524Srgrimes		return (NULL);
9534Srgrimes
9544Srgrimes	bar_length = 1 << map->pm_size;
9554Srgrimes	bar_start = map->pm_value;
9564Srgrimes	bar_end = bar_start + bar_length - 1;
9574Srgrimes
9584Srgrimes	/* Make sure that the resource fits the constraints. */
9594Srgrimes	if (bar_start >= end || bar_end <= bar_start || count != 1)
9604Srgrimes		return (NULL);
9614Srgrimes
9624Srgrimes	/* Clamp the resource to the constraints if necessary. */
9634Srgrimes	if (bar_start < start)
96421277Sbde		bar_start = start;
9654Srgrimes	if (bar_end > end)
9664Srgrimes		bar_end = end;
9674Srgrimes	bar_length = bar_end - bar_start + 1;
9684Srgrimes
9694Srgrimes	res = rman_reserve_resource(&iov->rman, bar_start, bar_end,
9704Srgrimes	    bar_length, flags, child);
9714Srgrimes	if (res == NULL)
9724Srgrimes		return (NULL);
9734Srgrimes
9744Srgrimes	rle = resource_list_add(&dinfo->resources, SYS_RES_MEMORY, *rid,
97521277Sbde	    bar_start, bar_end, 1);
9764Srgrimes	if (rle == NULL) {
9774Srgrimes		rman_release_resource(res);
9784Srgrimes		return (NULL);
9794Srgrimes	}
98021277Sbde
9814Srgrimes	rman_set_rid(res, *rid);
9824Srgrimes
9834Srgrimes	if (flags & RF_ACTIVE) {
9844Srgrimes		error = bus_activate_resource(child, SYS_RES_MEMORY, *rid, res);
9854Srgrimes		if (error != 0) {
9864Srgrimes			resource_list_delete(&dinfo->resources, SYS_RES_MEMORY,
9874Srgrimes			    *rid);
9884Srgrimes			rman_release_resource(res);
9894Srgrimes			return (NULL);
9904Srgrimes		}
9914Srgrimes	}
9924Srgrimes	rle->res = res;
9934Srgrimes
9944Srgrimes	return (res);
9954Srgrimes}
9964Srgrimes
9974Srgrimesint
9984Srgrimespci_vf_release_mem_resource(device_t dev, device_t child, int rid,
9994Srgrimes    struct resource *r)
10004Srgrimes{
10014Srgrimes	struct pci_devinfo *dinfo;
10024Srgrimes	struct resource_list_entry *rle;
10034Srgrimes	int error;
10044Srgrimes
10054Srgrimes	dinfo = device_get_ivars(child);
10064Srgrimes
10074Srgrimes	if (rman_get_flags(r) & RF_ACTIVE) {
10084Srgrimes		error = bus_deactivate_resource(child, SYS_RES_MEMORY, rid, r);
10094Srgrimes		if (error != 0)
10104Srgrimes			return (error);
10114Srgrimes	}
10124Srgrimes
10134Srgrimes	rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY, rid);
10144Srgrimes	if (rle != NULL) {
10154Srgrimes		rle->res = NULL;
10164Srgrimes		resource_list_delete(&dinfo->resources, SYS_RES_MEMORY,
10174Srgrimes		    rid);
10184Srgrimes	}
10194Srgrimes
10204Srgrimes	return (rman_release_resource(r));
10214Srgrimes}
10224Srgrimes
10234Srgrimes