qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

spapr_pci_vfio.c (6537B)


      1 /*
      2  * QEMU sPAPR PCI host for VFIO
      3  *
      4  * Copyright (c) 2011-2014 Alexey Kardashevskiy, IBM Corporation.
      5  *
      6  *  This program is free software; you can redistribute it and/or modify
      7  *  it under the terms of the GNU General Public License as published by
      8  *  the Free Software Foundation; either version 2 of the License,
      9  *  or (at your option) any later version.
     10  *
     11  *  This program is distributed in the hope that it will be useful,
     12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *  GNU General Public License for more details.
     15  *
     16  *  You should have received a copy of the GNU General Public License
     17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include "qemu/osdep.h"
     21 #include <linux/vfio.h>
     22 #include "hw/ppc/spapr.h"
     23 #include "hw/pci-host/spapr.h"
     24 #include "hw/pci/msix.h"
     25 #include "hw/vfio/vfio.h"
     26 #include "qemu/error-report.h"
     27 
     28 bool spapr_phb_eeh_available(SpaprPhbState *sphb)
     29 {
     30     return vfio_eeh_as_ok(&sphb->iommu_as);
     31 }
     32 
     33 static void spapr_phb_vfio_eeh_reenable(SpaprPhbState *sphb)
     34 {
     35     vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_ENABLE);
     36 }
     37 
     38 void spapr_phb_vfio_reset(DeviceState *qdev)
     39 {
     40     /*
     41      * The PE might be in frozen state. To reenable the EEH
     42      * functionality on it will clean the frozen state, which
     43      * ensures that the contained PCI devices will work properly
     44      * after reboot.
     45      */
     46     spapr_phb_vfio_eeh_reenable(SPAPR_PCI_HOST_BRIDGE(qdev));
     47 }
     48 
     49 static void spapr_eeh_pci_find_device(PCIBus *bus, PCIDevice *pdev,
     50                                       void *opaque)
     51 {
     52     bool *found = opaque;
     53 
     54     if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
     55         *found = true;
     56     }
     57 }
     58 
     59 int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb,
     60                                   unsigned int addr, int option)
     61 {
     62     uint32_t op;
     63     int ret;
     64 
     65     switch (option) {
     66     case RTAS_EEH_DISABLE:
     67         op = VFIO_EEH_PE_DISABLE;
     68         break;
     69     case RTAS_EEH_ENABLE: {
     70         PCIHostState *phb;
     71         bool found = false;
     72 
     73         /*
     74          * The EEH functionality is enabled per sphb level instead of
     75          * per PCI device. We have already identified this specific sphb
     76          * based on buid passed as argument to ibm,set-eeh-option rtas
     77          * call. Now we just need to check the validity of the PCI
     78          * pass-through devices (vfio-pci) under this sphb bus.
     79          * We have already validated that all the devices under this sphb
     80          * are from same iommu group (within same PE) before comming here.
     81          *
     82          * Prior to linux commit 98ba956f6a389 ("powerpc/pseries/eeh:
     83          * Rework device EEH PE determination") kernel would call
     84          * eeh-set-option for each device in the PE using the device's
     85          * config_address as the argument rather than the PE address.
     86          * Hence if we check validity of supplied config_addr whether
     87          * it matches to this PHB will cause issues with older kernel
     88          * versions v5.9 and older. If we return an error from
     89          * eeh-set-option when the argument isn't a valid PE address
     90          * then older kernels (v5.9 and older) will interpret that as
     91          * EEH not being supported.
     92          */
     93         phb = PCI_HOST_BRIDGE(sphb);
     94         pci_for_each_device(phb->bus, (addr >> 16) & 0xFF,
     95                             spapr_eeh_pci_find_device, &found);
     96 
     97         if (!found) {
     98             return RTAS_OUT_PARAM_ERROR;
     99         }
    100 
    101         op = VFIO_EEH_PE_ENABLE;
    102         break;
    103     }
    104     case RTAS_EEH_THAW_IO:
    105         op = VFIO_EEH_PE_UNFREEZE_IO;
    106         break;
    107     case RTAS_EEH_THAW_DMA:
    108         op = VFIO_EEH_PE_UNFREEZE_DMA;
    109         break;
    110     default:
    111         return RTAS_OUT_PARAM_ERROR;
    112     }
    113 
    114     ret = vfio_eeh_as_op(&sphb->iommu_as, op);
    115     if (ret < 0) {
    116         return RTAS_OUT_HW_ERROR;
    117     }
    118 
    119     return RTAS_OUT_SUCCESS;
    120 }
    121 
    122 int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state)
    123 {
    124     int ret;
    125 
    126     ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_GET_STATE);
    127     if (ret < 0) {
    128         return RTAS_OUT_PARAM_ERROR;
    129     }
    130 
    131     *state = ret;
    132     return RTAS_OUT_SUCCESS;
    133 }
    134 
    135 static void spapr_phb_vfio_eeh_clear_dev_msix(PCIBus *bus,
    136                                               PCIDevice *pdev,
    137                                               void *opaque)
    138 {
    139     /* Check if the device is VFIO PCI device */
    140     if (!object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
    141         return;
    142     }
    143 
    144     /*
    145      * The MSIx table will be cleaned out by reset. We need
    146      * disable it so that it can be reenabled properly. Also,
    147      * the cached MSIx table should be cleared as it's not
    148      * reflecting the contents in hardware.
    149      */
    150     if (msix_enabled(pdev)) {
    151         uint16_t flags;
    152 
    153         flags = pci_host_config_read_common(pdev,
    154                                             pdev->msix_cap + PCI_MSIX_FLAGS,
    155                                             pci_config_size(pdev), 2);
    156         flags &= ~PCI_MSIX_FLAGS_ENABLE;
    157         pci_host_config_write_common(pdev,
    158                                      pdev->msix_cap + PCI_MSIX_FLAGS,
    159                                      pci_config_size(pdev), flags, 2);
    160     }
    161 
    162     msix_reset(pdev);
    163 }
    164 
    165 static void spapr_phb_vfio_eeh_clear_bus_msix(PCIBus *bus, void *opaque)
    166 {
    167        pci_for_each_device_under_bus(bus, spapr_phb_vfio_eeh_clear_dev_msix,
    168                                      NULL);
    169 }
    170 
    171 static void spapr_phb_vfio_eeh_pre_reset(SpaprPhbState *sphb)
    172 {
    173        PCIHostState *phb = PCI_HOST_BRIDGE(sphb);
    174 
    175        pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_clear_bus_msix, NULL);
    176 }
    177 
    178 int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option)
    179 {
    180     uint32_t op;
    181     int ret;
    182 
    183     switch (option) {
    184     case RTAS_SLOT_RESET_DEACTIVATE:
    185         op = VFIO_EEH_PE_RESET_DEACTIVATE;
    186         break;
    187     case RTAS_SLOT_RESET_HOT:
    188         spapr_phb_vfio_eeh_pre_reset(sphb);
    189         op = VFIO_EEH_PE_RESET_HOT;
    190         break;
    191     case RTAS_SLOT_RESET_FUNDAMENTAL:
    192         spapr_phb_vfio_eeh_pre_reset(sphb);
    193         op = VFIO_EEH_PE_RESET_FUNDAMENTAL;
    194         break;
    195     default:
    196         return RTAS_OUT_PARAM_ERROR;
    197     }
    198 
    199     ret = vfio_eeh_as_op(&sphb->iommu_as, op);
    200     if (ret < 0) {
    201         return RTAS_OUT_HW_ERROR;
    202     }
    203 
    204     return RTAS_OUT_SUCCESS;
    205 }
    206 
    207 int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb)
    208 {
    209     int ret;
    210 
    211     ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_CONFIGURE);
    212     if (ret < 0) {
    213         return RTAS_OUT_PARAM_ERROR;
    214     }
    215 
    216     return RTAS_OUT_SUCCESS;
    217 }