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