349 lines
11 KiB
C
349 lines
11 KiB
C
/*
|
|
* 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 <asm/guest/vm.h>
|
|
#include <asm/guest/ept.h>
|
|
#include <ptdev.h>
|
|
#include <vpci.h>
|
|
#include <asm/pci_dev.h>
|
|
#include <logmsg.h>
|
|
#include <delay.h>
|
|
|
|
#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));
|
|
}
|