/* * Copyright (c) 2011 NetApp, Inc. * Copyright (c) 2018-2022 Intel Corporation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include "vpci_priv.h" /** * @pre pf_vdev != NULL */ static inline uint8_t get_vf_devfun(const struct pci_vdev *pf_vdev, uint16_t fst_off, uint16_t stride, uint16_t id) { return ((uint8_t)((pf_vdev->bdf.fields.devfun + fst_off + (stride * id)) & 0xFFU)); } /** * @pre pf_vdev != NULL */ static inline uint8_t get_vf_bus(const struct pci_vdev *pf_vdev, uint16_t fst_off, uint16_t stride, uint16_t id) { return ((uint8_t)(pf_vdev->bdf.fields.bus + ((pf_vdev->bdf.fields.devfun + fst_off + (stride * id)) >> 8U))); } /** * @pre pf_vdev != NULL */ static inline uint16_t read_sriov_reg(const struct pci_vdev *pf_vdev, uint16_t reg) { return ((uint16_t)(pci_pdev_read_cfg(pf_vdev->bdf, pf_vdev->sriov.capoff + reg, 2U))); } /** * @pre pf_vdev != NULL */ static bool is_vf_enabled(const struct pci_vdev *pf_vdev) { uint16_t control; control = read_sriov_reg(pf_vdev, PCIR_SRIOV_CONTROL); return ((control & PCIM_SRIOV_VF_ENABLE) != 0U); } /** * @pre pf_vdev != NULL */ static void init_sriov_vf_bar(struct pci_vdev *pf_vdev) { init_vdev_pt(pf_vdev, true); } /** * @pre pf_vdev != NULL */ static void create_vf(struct pci_vdev *pf_vdev, union pci_bdf vf_bdf, uint16_t vf_id) { struct pci_pdev *vf_pdev; struct pci_vdev *vf_vdev = NULL; /* * Per VT-d 8.3.3, the VFs are under the scope of the same * remapping unit as the associated PF when SRIOV is enabled. */ vf_pdev = pci_init_pdev(vf_bdf, pf_vdev->pdev->drhd_index); if (vf_pdev != NULL) { struct acrn_vm_pci_dev_config *dev_cfg; dev_cfg = init_one_dev_config(vf_pdev); if (dev_cfg != NULL) { vf_vdev = vpci_init_vdev(&vpci2vm(pf_vdev->vpci)->vpci, dev_cfg, pf_vdev); } } /* * if a VF vdev failed to be created, the VF number is less than requested number * and the requested VF physical devices are ready at this time, clear VF_ENABLE. */ if (vf_vdev == NULL) { uint16_t control; control = read_sriov_reg(pf_vdev, PCIR_SRIOV_CONTROL); control &= (~PCIM_SRIOV_VF_ENABLE); pci_pdev_write_cfg(pf_vdev->bdf, pf_vdev->sriov.capoff + PCIR_SRIOV_CONTROL, 2U, control); pr_err("PF %x:%x.%x can't creat VF, unset VF_ENABLE", pf_vdev->bdf.bits.b, pf_vdev->bdf.bits.d, pf_vdev->bdf.bits.f); } else { uint32_t bar_idx; struct pci_vbar *vf_vbar; /* VF bars information from its PF SRIOV capability, no need to access physical device */ vf_vdev->nr_bars = PCI_BAR_COUNT; for (bar_idx = 0U; bar_idx < PCI_BAR_COUNT; bar_idx++) { vf_vbar = &vf_vdev->vbars[bar_idx]; *vf_vbar = vf_vdev->phyfun->sriov.vbars[bar_idx]; vf_vbar->base_hpa += (vf_vbar->size * vf_id); vf_vbar->base_gpa = vf_vbar->base_hpa; /* Map VF's BARs when it's first created. */ if (vf_vbar->base_gpa != 0UL) { struct acrn_vm *vm = vpci2vm(vf_vdev->vpci); ept_add_mr(vm, (uint64_t *)(vm->arch_vm.nworld_eptp), vf_vbar->base_hpa, vf_vbar->base_gpa, vf_vbar->size, EPT_WR | EPT_RD | EPT_UNCACHED); } if (has_msix_cap(vf_vdev) && (bar_idx == vf_vdev->msix.table_bar)) { vf_vdev->msix.mmio_hpa = vf_vbar->base_hpa; vf_vdev->msix.mmio_size = vf_vbar->size; vdev_pt_map_msix(vf_vdev, false); } /* * VF BARs value are zero and read only, according to PCI Express * Base 4.0 chapter 9.3.4.1.11, the VF */ pci_vdev_write_vcfg(vf_vdev, pci_bar_offset(bar_idx), 4U, 0U); } } } /** * @pre pf_vdev != NULL * @pre is_vf_enabled(pf_dev) == true * @Application constraints: PCIR_SRIOV_NUMVFS register value cannot be 0 if VF_ENABLE is set. */ static void enable_vfs(struct pci_vdev *pf_vdev) { union pci_bdf vf_bdf; uint16_t idx; uint16_t sub_vid = 0U; uint16_t num_vfs, stride, fst_off; /* Confirm that the physical VF_ENABLE register has been set successfully */ ASSERT(is_vf_enabled(pf_vdev), "VF_ENABLE was not set successfully on the hardware"); /* * All VFs bars information are located at PF VF_BAR fields of SRIOV capability. * Initialize the PF's VF_BAR registers before initialize each VF device bar. */ init_sriov_vf_bar(pf_vdev); /* * Per PCIE base spec 9.3.3.3.1, VF Enable bit from cleared to set, the * system is not perrmitted to issue requests to the VFs until one of * the following is true: * 1. at least 100ms has passed. * 2. An FRS message has been received from the PF with a reason code * of VF Enabled. * 3. At least VF Enable Time has passed since VF Enable was Set. * VF Enable Time is either the Reset Time value in the Readiness Time * Reporting capability associated with the VF or a value determined * by system software/firmware. * * Curerntly, we use the first way to wait for VF physical devices to be ready. */ udelay (100U * 1000U); /* * Due to VF's DEVICE ID and VENDOR ID are 0xFFFF, so check if VF physical * device has been created by the value of SUBSYSTEM VENDOR ID. * To check if all enabled VFs are ready, just check the first VF already exists, * do not need to check all. */ fst_off = read_sriov_reg(pf_vdev, PCIR_SRIOV_FST_VF_OFF); stride = read_sriov_reg(pf_vdev, PCIR_SRIOV_VF_STRIDE); vf_bdf.fields.bus = get_vf_bus(pf_vdev, fst_off, stride, 0U); vf_bdf.fields.devfun = get_vf_devfun(pf_vdev, fst_off, stride, 0U); sub_vid = (uint16_t) pci_pdev_read_cfg(vf_bdf, PCIV_SUB_VENDOR_ID, 2U); if ((sub_vid != 0xFFFFU) && (sub_vid != 0U)) { struct pci_vdev *vf_vdev; num_vfs = read_sriov_reg(pf_vdev, PCIR_SRIOV_NUMVFS); for (idx = 0U; idx < num_vfs; idx++) { vf_bdf.fields.bus = get_vf_bus(pf_vdev, fst_off, stride, idx); vf_bdf.fields.devfun = get_vf_devfun(pf_vdev, fst_off, stride, idx); /* * If one VF has never been created then create new one including pdev/vdev structures. * * The VF maybe have already existed but it is a zombie instance that vf_vdev->vpci * is NULL, in this case, we need to make the vf_vdev available again in here. */ vf_vdev = pci_find_vdev(&vpci2vm(pf_vdev->vpci)->vpci, vf_bdf); if (vf_vdev == NULL) { create_vf(pf_vdev, vf_bdf, idx); } else { /* Re-activate a zombie VF */ if (is_zombie_vf(vf_vdev)) { vf_vdev->vdev_ops->init_vdev(vf_vdev); } } } } else { /* * If the VF physical device was not created successfully, the pdev/vdev * will also not be created so that Service VM can aware of VF creation failure, */ pr_err("PF %x:%x.%x can't create VFs after 100 ms", pf_vdev->bdf.bits.b, pf_vdev->bdf.bits.d, pf_vdev->bdf.bits.f); } } /** * @pre pf_vdev != NULL */ static void disable_vfs(struct pci_vdev *pf_vdev) { uint16_t idx, num_vfs, stride, first; struct pci_vdev *vf_vdev; /* * PF can disable VFs only when all VFs are not used by any VM or any application * * Ideally, VF instances should be deleted after VFs are disabled, but for FuSa reasons, * we simply set the VF instance status to "zombie" to avoid dynamically adding/removing * resources * * If the VF drivers are still running in Service VM or User VM, the MMIO access will return 0xFF. */ num_vfs = read_sriov_reg(pf_vdev, PCIR_SRIOV_NUMVFS); first = read_sriov_reg(pf_vdev, PCIR_SRIOV_FST_VF_OFF); stride = read_sriov_reg(pf_vdev, PCIR_SRIOV_VF_STRIDE); for (idx = 0U; idx < num_vfs; idx++) { union pci_bdf bdf; bdf.fields.bus = get_vf_bus(pf_vdev, first, stride, idx); bdf.fields.devfun = get_vf_devfun(pf_vdev, first, stride, idx); vf_vdev = pci_find_vdev(&vpci2vm(pf_vdev->vpci)->vpci, bdf); if ((vf_vdev != NULL) && (!is_zombie_vf(vf_vdev))) { /* set disabled VF as zombie vdev instance */ vf_vdev->vdev_ops->deinit_vdev(vf_vdev); } } } /** * @pre vdev != NULL * @pre vdev->pdev != NULL */ void init_vsriov(struct pci_vdev *vdev) { struct pci_pdev *pdev = vdev->pdev; vdev->sriov.capoff = pdev->sriov.capoff; vdev->sriov.caplen = pdev->sriov.caplen; } /** * @pre vdev != NULL * @pre vdev->pdev != NULL */ void read_sriov_cap_reg(const struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t *val) { if (!vdev->pdev->sriov.hide_sriov) { /* no need to do emulation, passthrough to physical device directly */ *val = pci_pdev_read_cfg(vdev->pdev->bdf, offset, bytes); } else { *val = 0xffffffffU; } } /** * @pre vdev != NULL * @pre vdev->pdev != NULL */ void write_sriov_cap_reg(struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t val) { uint32_t reg; reg = offset - vdev->sriov.capoff; if (!vdev->pdev->sriov.hide_sriov) { if (reg == PCIR_SRIOV_CONTROL) { bool enable; enable = ((val & PCIM_SRIOV_VF_ENABLE) != 0U); if (enable != is_vf_enabled(vdev)) { if (enable) { /* * set VF_ENABLE to PF physical device before enable_vfs * since need to ask hardware to create VF physical * devices firstly */ pci_pdev_write_cfg(vdev->pdev->bdf, offset, bytes, val); enable_vfs(vdev); } else { disable_vfs(vdev); pci_pdev_write_cfg(vdev->pdev->bdf, offset, bytes, val); } } else { pci_pdev_write_cfg(vdev->pdev->bdf, offset, bytes, val); } } else if (reg == PCIR_SRIOV_NUMVFS) { uint16_t total; total = read_sriov_reg(vdev, PCIR_SRIOV_TOTAL_VFS); /* * sanity check for NumVFs register based on PCE Express Base 4.0 9.3.3.7 chapter * The results are undefined if NumVFs is set to a value greater than TotalVFs * NumVFs may only be written while VF Enable is Clear * If NumVFs is written when VF Enable is Set, the results are undefined */ if ((((uint16_t)(val & 0xFFU)) <= total) && (!is_vf_enabled(vdev))) { pci_pdev_write_cfg(vdev->pdev->bdf, offset, bytes, val); } } else { pci_pdev_write_cfg(vdev->pdev->bdf, offset, bytes, val); } } } /** * @pre vdev != NULL */ uint32_t sriov_bar_offset(const struct pci_vdev *vdev, uint32_t bar_idx) { return (vdev->sriov.capoff + PCIR_SRIOV_VF_BAR_OFF + (bar_idx << 2U)); }