| 1 | /* |
| 2 | * pcie_sriov.c: |
| 3 | * |
| 4 | * Implementation of SR/IOV emulation support. |
| 5 | * |
| 6 | * Copyright (c) 2015-2017 Knut Omang <knut.omang@oracle.com> |
| 7 | * |
| 8 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| 9 | * See the COPYING file in the top-level directory. |
| 10 | * |
| 11 | */ |
| 12 | |
| 13 | #include "qemu/osdep.h" |
| 14 | #include "hw/pci/pci_device.h" |
| 15 | #include "hw/pci/pcie.h" |
| 16 | #include "hw/pci/pci_bus.h" |
| 17 | #include "hw/core/qdev-properties.h" |
| 18 | #include "qemu/range.h" |
| 19 | #include "qapi/error.h" |
| 20 | #include "trace.h" |
| 21 | |
| 22 | static GHashTable *pfs; |
| 23 | |
| 24 | static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) |
| 25 | { |
| 26 | for (uint16_t i = 0; i < total_vfs; i++) { |
| 27 | PCIDevice *vf = dev->exp.sriov_pf.vf[i]; |
| 28 | object_unparent(OBJECT(vf)); |
| 29 | object_unref(OBJECT(vf)); |
| 30 | } |
| 31 | g_free(dev->exp.sriov_pf.vf); |
| 32 | dev->exp.sriov_pf.vf = NULL; |
| 33 | } |
| 34 | |
| 35 | static void register_vfs(PCIDevice *dev) |
| 36 | { |
| 37 | uint16_t num_vfs; |
| 38 | uint16_t i; |
| 39 | uint16_t sriov_cap = dev->exp.sriov_cap; |
| 40 | |
| 41 | assert(sriov_cap > 0); |
| 42 | num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); |
| 43 | |
| 44 | trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), |
| 45 | PCI_FUNC(dev->devfn), num_vfs); |
| 46 | for (i = 0; i < num_vfs; i++) { |
| 47 | pci_set_enabled(dev->exp.sriov_pf.vf[i], true); |
| 48 | } |
| 49 | |
| 50 | pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_NUM_VF, 0); |
| 51 | } |
| 52 | |
| 53 | static void unregister_vfs(PCIDevice *dev) |
| 54 | { |
| 55 | uint8_t *cfg = dev->config + dev->exp.sriov_cap; |
| 56 | uint16_t i; |
| 57 | |
| 58 | trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), |
| 59 | PCI_FUNC(dev->devfn)); |
| 60 | for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { |
| 61 | pci_set_enabled(dev->exp.sriov_pf.vf[i], false); |
| 62 | } |
| 63 | |
| 64 | pci_set_word(dev->wmask + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0xffff); |
| 65 | } |
| 66 | |
| 67 | static void consume_config(PCIDevice *dev) |
| 68 | { |
| 69 | uint8_t *cfg = dev->config + dev->exp.sriov_cap; |
| 70 | |
| 71 | if (pci_get_word(cfg + PCI_SRIOV_CTRL) & PCI_SRIOV_CTRL_VFE) { |
| 72 | register_vfs(dev); |
| 73 | } else { |
| 74 | uint8_t *wmask = dev->wmask + dev->exp.sriov_cap; |
| 75 | uint16_t num_vfs = pci_get_word(cfg + PCI_SRIOV_NUM_VF); |
| 76 | uint16_t wmask_val = PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI; |
| 77 | |
| 78 | unregister_vfs(dev); |
| 79 | |
| 80 | if (num_vfs <= pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)) { |
| 81 | wmask_val |= PCI_SRIOV_CTRL_VFE; |
| 82 | } |
| 83 | |
| 84 | pci_set_word(wmask + PCI_SRIOV_CTRL, wmask_val); |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | static bool pcie_sriov_pf_init_common(PCIDevice *dev, uint16_t offset, |
| 89 | uint16_t vf_dev_id, uint16_t init_vfs, |
| 90 | uint16_t total_vfs, uint16_t vf_offset, |
| 91 | uint16_t vf_stride, Error **errp) |
| 92 | { |
| 93 | int32_t devfn = dev->devfn + vf_offset; |
| 94 | uint8_t *cfg = dev->config + offset; |
| 95 | uint8_t *wmask; |
| 96 | |
| 97 | if (!pci_is_express(dev)) { |
| 98 | error_setg(errp, "PCI Express is required for SR-IOV PF"); |
| 99 | return false; |
| 100 | } |
| 101 | |
| 102 | if (pci_is_vf(dev)) { |
| 103 | error_setg(errp, "a device cannot be a SR-IOV PF and a VF at the same time"); |
| 104 | return false; |
| 105 | } |
| 106 | |
| 107 | if (total_vfs && |
| 108 | (uint32_t)devfn + (uint32_t)(total_vfs - 1) * vf_stride >= PCI_DEVFN_MAX) { |
| 109 | error_setg(errp, "VF addr overflows"); |
| 110 | return false; |
| 111 | } |
| 112 | |
| 113 | pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, |
| 114 | offset, PCI_EXT_CAP_SRIOV_SIZEOF); |
| 115 | dev->exp.sriov_cap = offset; |
| 116 | dev->exp.sriov_pf.vf = NULL; |
| 117 | |
| 118 | pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); |
| 119 | pci_set_word(cfg + PCI_SRIOV_VF_STRIDE, vf_stride); |
| 120 | |
| 121 | /* |
| 122 | * Mandatory page sizes to support. |
| 123 | * Device implementations can call pcie_sriov_pf_add_sup_pgsize() |
| 124 | * to set more bits: |
| 125 | */ |
| 126 | pci_set_word(cfg + PCI_SRIOV_SUP_PGSIZE, SRIOV_SUP_PGSIZE_MINREQ); |
| 127 | |
| 128 | /* |
| 129 | * Default is to use 4K pages, software can modify it |
| 130 | * to any of the supported bits |
| 131 | */ |
| 132 | pci_set_word(cfg + PCI_SRIOV_SYS_PGSIZE, 0x1); |
| 133 | |
| 134 | /* Set up device ID and initial/total number of VFs available */ |
| 135 | pci_set_word(cfg + PCI_SRIOV_VF_DID, vf_dev_id); |
| 136 | pci_set_word(cfg + PCI_SRIOV_INITIAL_VF, init_vfs); |
| 137 | pci_set_word(cfg + PCI_SRIOV_TOTAL_VF, total_vfs); |
| 138 | pci_set_word(cfg + PCI_SRIOV_NUM_VF, 0); |
| 139 | |
| 140 | /* Write enable control bits */ |
| 141 | wmask = dev->wmask + offset; |
| 142 | pci_set_word(wmask + PCI_SRIOV_CTRL, |
| 143 | PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI); |
| 144 | pci_set_word(wmask + PCI_SRIOV_NUM_VF, 0xffff); |
| 145 | pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553); |
| 146 | |
| 147 | qdev_prop_set_bit(&dev->qdev, "multifunction", true); |
| 148 | |
| 149 | return true; |
| 150 | } |
| 151 | |
| 152 | bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, |
| 153 | const char *vfname, uint16_t vf_dev_id, |
| 154 | uint16_t init_vfs, uint16_t total_vfs, |
| 155 | uint16_t vf_offset, uint16_t vf_stride, |
| 156 | Error **errp) |
| 157 | { |
| 158 | BusState *bus = qdev_get_parent_bus(&dev->qdev); |
| 159 | int32_t devfn = dev->devfn + vf_offset; |
| 160 | |
| 161 | if (pfs && g_hash_table_contains(pfs, dev->qdev.id)) { |
| 162 | error_setg(errp, "attaching user-created SR-IOV VF unsupported"); |
| 163 | return false; |
| 164 | } |
| 165 | |
| 166 | if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, init_vfs, |
| 167 | total_vfs, vf_offset, vf_stride, errp)) { |
| 168 | return false; |
| 169 | } |
| 170 | |
| 171 | dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs); |
| 172 | |
| 173 | for (uint16_t i = 0; i < total_vfs; i++) { |
| 174 | PCIDevice *vf = pci_new(devfn, vfname); |
| 175 | vf->exp.sriov_vf.pf = dev; |
| 176 | vf->exp.sriov_vf.vf_number = i; |
| 177 | |
| 178 | if (!qdev_realize(&vf->qdev, bus, errp)) { |
| 179 | object_unparent(OBJECT(vf)); |
| 180 | object_unref(vf); |
| 181 | unparent_vfs(dev, i); |
| 182 | return false; |
| 183 | } |
| 184 | |
| 185 | /* set vid/did according to sr/iov spec - they are not used */ |
| 186 | pci_config_set_vendor_id(vf->config, 0xffff); |
| 187 | pci_config_set_device_id(vf->config, 0xffff); |
| 188 | |
| 189 | dev->exp.sriov_pf.vf[i] = vf; |
| 190 | devfn += vf_stride; |
| 191 | } |
| 192 | |
| 193 | return true; |
| 194 | } |
| 195 | |
| 196 | void pcie_sriov_pf_exit(PCIDevice *dev) |
| 197 | { |
| 198 | uint8_t *cfg; |
| 199 | |
| 200 | if (dev->exp.sriov_cap == 0) { |
| 201 | return; |
| 202 | } |
| 203 | cfg = dev->config + dev->exp.sriov_cap; |
| 204 | |
| 205 | if (dev->exp.sriov_pf.vf_user_created) { |
| 206 | uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); |
| 207 | uint16_t total_vfs = pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); |
| 208 | uint16_t vf_dev_id = pci_get_word(cfg + PCI_SRIOV_VF_DID); |
| 209 | |
| 210 | unregister_vfs(dev); |
| 211 | |
| 212 | for (uint16_t i = 0; i < total_vfs; i++) { |
| 213 | dev->exp.sriov_pf.vf[i]->exp.sriov_vf.pf = NULL; |
| 214 | |
| 215 | pci_config_set_vendor_id(dev->exp.sriov_pf.vf[i]->config, ven_id); |
| 216 | pci_config_set_device_id(dev->exp.sriov_pf.vf[i]->config, vf_dev_id); |
| 217 | } |
| 218 | } else { |
| 219 | unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, |
| 224 | uint8_t type, dma_addr_t size) |
| 225 | { |
| 226 | uint32_t addr; |
| 227 | uint64_t wmask; |
| 228 | uint16_t sriov_cap = dev->exp.sriov_cap; |
| 229 | |
| 230 | assert(sriov_cap > 0); |
| 231 | assert(region_num >= 0); |
| 232 | assert(region_num < PCI_NUM_REGIONS); |
| 233 | assert(region_num != PCI_ROM_SLOT); |
| 234 | |
| 235 | wmask = ~(size - 1); |
| 236 | addr = sriov_cap + PCI_SRIOV_BAR + region_num * 4; |
| 237 | |
| 238 | pci_set_long(dev->config + addr, type); |
| 239 | if (!(type & PCI_BASE_ADDRESS_SPACE_IO) && |
| 240 | type & PCI_BASE_ADDRESS_MEM_TYPE_64) { |
| 241 | pci_set_quad(dev->wmask + addr, wmask); |
| 242 | pci_set_quad(dev->cmask + addr, ~0ULL); |
| 243 | } else { |
| 244 | pci_set_long(dev->wmask + addr, wmask & 0xffffffff); |
| 245 | pci_set_long(dev->cmask + addr, 0xffffffff); |
| 246 | } |
| 247 | dev->exp.sriov_pf.vf_bar_type[region_num] = type; |
| 248 | } |
| 249 | |
| 250 | static gint compare_vf_devfns(gconstpointer a, gconstpointer b) |
| 251 | { |
| 252 | return (*(PCIDevice **)a)->devfn - (*(PCIDevice **)b)->devfn; |
| 253 | } |
| 254 | |
| 255 | int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, |
| 256 | uint16_t offset, |
| 257 | Error **errp) |
| 258 | { |
| 259 | GPtrArray *pf; |
| 260 | PCIDevice **vfs; |
| 261 | BusState *bus = qdev_get_parent_bus(DEVICE(dev)); |
| 262 | uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); |
| 263 | uint16_t size = PCI_EXT_CAP_SRIOV_SIZEOF; |
| 264 | uint16_t vf_dev_id; |
| 265 | uint16_t vf_offset; |
| 266 | uint16_t vf_stride; |
| 267 | uint16_t i; |
| 268 | |
| 269 | if (!pfs || !dev->qdev.id) { |
| 270 | return 0; |
| 271 | } |
| 272 | |
| 273 | pf = g_hash_table_lookup(pfs, dev->qdev.id); |
| 274 | if (!pf) { |
| 275 | return 0; |
| 276 | } |
| 277 | |
| 278 | if (pf->len > UINT16_MAX) { |
| 279 | error_setg(errp, "too many VFs"); |
| 280 | return -1; |
| 281 | } |
| 282 | |
| 283 | g_ptr_array_sort(pf, compare_vf_devfns); |
| 284 | vfs = (void *)pf->pdata; |
| 285 | |
| 286 | if (vfs[0]->devfn <= dev->devfn) { |
| 287 | error_setg(errp, "a VF function number is less than the PF function number"); |
| 288 | return -1; |
| 289 | } |
| 290 | |
| 291 | vf_dev_id = pci_get_word(vfs[0]->config + PCI_DEVICE_ID); |
| 292 | vf_offset = vfs[0]->devfn - dev->devfn; |
| 293 | vf_stride = pf->len < 2 ? 0 : vfs[1]->devfn - vfs[0]->devfn; |
| 294 | |
| 295 | for (i = 0; i < pf->len; i++) { |
| 296 | if (bus != qdev_get_parent_bus(&vfs[i]->qdev)) { |
| 297 | error_setg(errp, "SR-IOV VF parent bus mismatches with PF"); |
| 298 | return -1; |
| 299 | } |
| 300 | |
| 301 | if (ven_id != pci_get_word(vfs[i]->config + PCI_VENDOR_ID)) { |
| 302 | error_setg(errp, "SR-IOV VF vendor ID mismatches with PF"); |
| 303 | return -1; |
| 304 | } |
| 305 | |
| 306 | if (vf_dev_id != pci_get_word(vfs[i]->config + PCI_DEVICE_ID)) { |
| 307 | error_setg(errp, "inconsistent SR-IOV VF device IDs"); |
| 308 | return -1; |
| 309 | } |
| 310 | |
| 311 | for (size_t j = 0; j < PCI_NUM_REGIONS; j++) { |
| 312 | if (vfs[i]->io_regions[j].size != vfs[0]->io_regions[j].size || |
| 313 | vfs[i]->io_regions[j].type != vfs[0]->io_regions[j].type) { |
| 314 | error_setg(errp, "inconsistent SR-IOV BARs"); |
| 315 | return -1; |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | if (vfs[i]->devfn - vfs[0]->devfn != vf_stride * i) { |
| 320 | error_setg(errp, "inconsistent SR-IOV stride"); |
| 321 | return -1; |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, pf->len, |
| 326 | pf->len, vf_offset, vf_stride, errp)) { |
| 327 | return -1; |
| 328 | } |
| 329 | |
| 330 | if (!pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI)) { |
| 331 | pcie_ari_init(dev, offset + size); |
| 332 | size += PCI_ARI_SIZEOF; |
| 333 | } |
| 334 | |
| 335 | for (i = 0; i < pf->len; i++) { |
| 336 | vfs[i]->exp.sriov_vf.pf = dev; |
| 337 | vfs[i]->exp.sriov_vf.vf_number = i; |
| 338 | |
| 339 | /* set vid/did according to sr/iov spec - they are not used */ |
| 340 | pci_config_set_vendor_id(vfs[i]->config, 0xffff); |
| 341 | pci_config_set_device_id(vfs[i]->config, 0xffff); |
| 342 | } |
| 343 | |
| 344 | dev->exp.sriov_pf.vf = vfs; |
| 345 | dev->exp.sriov_pf.vf_user_created = true; |
| 346 | |
| 347 | for (i = 0; i < PCI_NUM_REGIONS; i++) { |
| 348 | PCIIORegion *region = &vfs[0]->io_regions[i]; |
| 349 | |
| 350 | if (region->size) { |
| 351 | pcie_sriov_pf_init_vf_bar(dev, i, region->type, region->size); |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | return size; |
| 356 | } |
| 357 | |
| 358 | bool pcie_sriov_register_device(PCIDevice *dev, Error **errp) |
| 359 | { |
| 360 | if (!dev->exp.sriov_pf.vf && dev->qdev.id && |
| 361 | pfs && g_hash_table_contains(pfs, dev->qdev.id)) { |
| 362 | error_setg(errp, "attaching user-created SR-IOV VF unsupported"); |
| 363 | return false; |
| 364 | } |
| 365 | |
| 366 | if (dev->sriov_pf) { |
| 367 | PCIDevice *pci_pf; |
| 368 | GPtrArray *pf; |
| 369 | |
| 370 | if (!PCI_DEVICE_GET_CLASS(dev)->sriov_vf_user_creatable) { |
| 371 | error_setg(errp, "user cannot create SR-IOV VF with this device type"); |
| 372 | return false; |
| 373 | } |
| 374 | |
| 375 | if (!pci_is_express(dev)) { |
| 376 | error_setg(errp, "PCI Express is required for SR-IOV VF"); |
| 377 | return false; |
| 378 | } |
| 379 | |
| 380 | if (!pci_qdev_find_device(dev->sriov_pf, &pci_pf)) { |
| 381 | error_setg(errp, "PCI device specified as SR-IOV PF already exists"); |
| 382 | return false; |
| 383 | } |
| 384 | |
| 385 | if (!pfs) { |
| 386 | pfs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
| 387 | } |
| 388 | |
| 389 | pf = g_hash_table_lookup(pfs, dev->sriov_pf); |
| 390 | if (!pf) { |
| 391 | pf = g_ptr_array_new(); |
| 392 | g_hash_table_insert(pfs, g_strdup(dev->sriov_pf), pf); |
| 393 | } |
| 394 | |
| 395 | g_ptr_array_add(pf, dev); |
| 396 | } |
| 397 | |
| 398 | return true; |
| 399 | } |
| 400 | |
| 401 | void pcie_sriov_unregister_device(PCIDevice *dev) |
| 402 | { |
| 403 | if (dev->sriov_pf && pfs) { |
| 404 | GPtrArray *pf = g_hash_table_lookup(pfs, dev->sriov_pf); |
| 405 | |
| 406 | if (pf) { |
| 407 | g_ptr_array_remove_fast(pf, dev); |
| 408 | |
| 409 | if (!pf->len) { |
| 410 | g_hash_table_remove(pfs, dev->sriov_pf); |
| 411 | g_ptr_array_free(pf, FALSE); |
| 412 | } |
| 413 | } |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, |
| 418 | uint32_t val, int len) |
| 419 | { |
| 420 | uint32_t off; |
| 421 | uint16_t sriov_cap = dev->exp.sriov_cap; |
| 422 | |
| 423 | if (!sriov_cap || address < sriov_cap) { |
| 424 | return; |
| 425 | } |
| 426 | off = address - sriov_cap; |
| 427 | if (off >= PCI_EXT_CAP_SRIOV_SIZEOF) { |
| 428 | return; |
| 429 | } |
| 430 | |
| 431 | trace_sriov_config_write(dev->name, PCI_SLOT(dev->devfn), |
| 432 | PCI_FUNC(dev->devfn), off, val, len); |
| 433 | |
| 434 | consume_config(dev); |
| 435 | } |
| 436 | |
| 437 | void pcie_sriov_pf_post_load(PCIDevice *dev) |
| 438 | { |
| 439 | if (dev->exp.sriov_cap) { |
| 440 | consume_config(dev); |
| 441 | } |
| 442 | } |
| 443 | |
| 444 | |
| 445 | /* Reset SR/IOV */ |
| 446 | void pcie_sriov_pf_reset(PCIDevice *dev) |
| 447 | { |
| 448 | uint16_t sriov_cap = dev->exp.sriov_cap; |
| 449 | if (!sriov_cap) { |
| 450 | return; |
| 451 | } |
| 452 | |
| 453 | pci_set_word(dev->config + sriov_cap + PCI_SRIOV_CTRL, 0); |
| 454 | unregister_vfs(dev); |
| 455 | |
| 456 | pci_set_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF, 0); |
| 457 | pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_CTRL, |
| 458 | PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI); |
| 459 | |
| 460 | /* |
| 461 | * Default is to use 4K pages, software can modify it |
| 462 | * to any of the supported bits |
| 463 | */ |
| 464 | pci_set_word(dev->config + sriov_cap + PCI_SRIOV_SYS_PGSIZE, 0x1); |
| 465 | |
| 466 | for (uint16_t i = 0; i < PCI_NUM_REGIONS; i++) { |
| 467 | pci_set_quad(dev->config + sriov_cap + PCI_SRIOV_BAR + i * 4, |
| 468 | dev->exp.sriov_pf.vf_bar_type[i]); |
| 469 | } |
| 470 | } |
| 471 | |
| 472 | /* Add optional supported page sizes to the mask of supported page sizes */ |
| 473 | void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize) |
| 474 | { |
| 475 | uint8_t *cfg = dev->config + dev->exp.sriov_cap; |
| 476 | uint8_t *wmask = dev->wmask + dev->exp.sriov_cap; |
| 477 | |
| 478 | uint16_t sup_pgsize = pci_get_word(cfg + PCI_SRIOV_SUP_PGSIZE); |
| 479 | |
| 480 | sup_pgsize |= opt_sup_pgsize; |
| 481 | |
| 482 | /* |
| 483 | * Make sure the new bits are set, and that system page size |
| 484 | * also can be set to any of the new values according to spec: |
| 485 | */ |
| 486 | pci_set_word(cfg + PCI_SRIOV_SUP_PGSIZE, sup_pgsize); |
| 487 | pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, sup_pgsize); |
| 488 | } |
| 489 | |
| 490 | |
| 491 | uint16_t pcie_sriov_vf_number(PCIDevice *dev) |
| 492 | { |
| 493 | assert(dev->exp.sriov_vf.pf); |
| 494 | return dev->exp.sriov_vf.vf_number; |
| 495 | } |
| 496 | |
| 497 | PCIDevice *pcie_sriov_get_pf(PCIDevice *dev) |
| 498 | { |
| 499 | return dev->exp.sriov_vf.pf; |
| 500 | } |
| 501 | |
| 502 | PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) |
| 503 | { |
| 504 | assert(!pci_is_vf(dev)); |
| 505 | if (n < pcie_sriov_num_vfs(dev)) { |
| 506 | return dev->exp.sriov_pf.vf[n]; |
| 507 | } |
| 508 | return NULL; |
| 509 | } |
| 510 | |
| 511 | uint16_t pcie_sriov_num_vfs(PCIDevice *dev) |
| 512 | { |
| 513 | uint16_t sriov_cap = dev->exp.sriov_cap; |
| 514 | uint8_t *cfg = dev->config + sriov_cap; |
| 515 | |
| 516 | return sriov_cap && |
| 517 | (pci_get_word(cfg + PCI_SRIOV_CTRL) & PCI_SRIOV_CTRL_VFE) ? |
| 518 | pci_get_word(cfg + PCI_SRIOV_NUM_VF) : 0; |
| 519 | } |