xref: /openbmc/qemu/hw/ppc/spapr_pci_vfio.c (revision 4fe6d78b)
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 "qapi/error.h"
23 #include "qemu-common.h"
24 #include "cpu.h"
25 #include "hw/ppc/spapr.h"
26 #include "hw/pci-host/spapr.h"
27 #include "hw/pci/msix.h"
28 #include "hw/vfio/vfio.h"
29 #include "qemu/error-report.h"
30 #include "sysemu/qtest.h"
31 
32 bool spapr_phb_eeh_available(sPAPRPHBState *sphb)
33 {
34     return vfio_eeh_as_ok(&sphb->iommu_as);
35 }
36 
37 static void spapr_phb_vfio_eeh_reenable(sPAPRPHBState *sphb)
38 {
39     vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_ENABLE);
40 }
41 
42 void spapr_phb_vfio_reset(DeviceState *qdev)
43 {
44     /*
45      * The PE might be in frozen state. To reenable the EEH
46      * functionality on it will clean the frozen state, which
47      * ensures that the contained PCI devices will work properly
48      * after reboot.
49      */
50     spapr_phb_vfio_eeh_reenable(SPAPR_PCI_HOST_BRIDGE(qdev));
51 }
52 
53 int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb,
54                                   unsigned int addr, int option)
55 {
56     uint32_t op;
57     int ret;
58 
59     switch (option) {
60     case RTAS_EEH_DISABLE:
61         op = VFIO_EEH_PE_DISABLE;
62         break;
63     case RTAS_EEH_ENABLE: {
64         PCIHostState *phb;
65         PCIDevice *pdev;
66 
67         /*
68          * The EEH functionality is enabled on basis of PCI device,
69          * instead of PE. We need check the validity of the PCI
70          * device address.
71          */
72         phb = PCI_HOST_BRIDGE(sphb);
73         pdev = pci_find_device(phb->bus,
74                                (addr >> 16) & 0xFF, (addr >> 8) & 0xFF);
75         if (!pdev || !object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
76             return RTAS_OUT_PARAM_ERROR;
77         }
78 
79         op = VFIO_EEH_PE_ENABLE;
80         break;
81     }
82     case RTAS_EEH_THAW_IO:
83         op = VFIO_EEH_PE_UNFREEZE_IO;
84         break;
85     case RTAS_EEH_THAW_DMA:
86         op = VFIO_EEH_PE_UNFREEZE_DMA;
87         break;
88     default:
89         return RTAS_OUT_PARAM_ERROR;
90     }
91 
92     ret = vfio_eeh_as_op(&sphb->iommu_as, op);
93     if (ret < 0) {
94         return RTAS_OUT_HW_ERROR;
95     }
96 
97     return RTAS_OUT_SUCCESS;
98 }
99 
100 int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state)
101 {
102     int ret;
103 
104     ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_GET_STATE);
105     if (ret < 0) {
106         return RTAS_OUT_PARAM_ERROR;
107     }
108 
109     *state = ret;
110     return RTAS_OUT_SUCCESS;
111 }
112 
113 static void spapr_phb_vfio_eeh_clear_dev_msix(PCIBus *bus,
114                                               PCIDevice *pdev,
115                                               void *opaque)
116 {
117     /* Check if the device is VFIO PCI device */
118     if (!object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
119         return;
120     }
121 
122     /*
123      * The MSIx table will be cleaned out by reset. We need
124      * disable it so that it can be reenabled properly. Also,
125      * the cached MSIx table should be cleared as it's not
126      * reflecting the contents in hardware.
127      */
128     if (msix_enabled(pdev)) {
129         uint16_t flags;
130 
131         flags = pci_host_config_read_common(pdev,
132                                             pdev->msix_cap + PCI_MSIX_FLAGS,
133                                             pci_config_size(pdev), 2);
134         flags &= ~PCI_MSIX_FLAGS_ENABLE;
135         pci_host_config_write_common(pdev,
136                                      pdev->msix_cap + PCI_MSIX_FLAGS,
137                                      pci_config_size(pdev), flags, 2);
138     }
139 
140     msix_reset(pdev);
141 }
142 
143 static void spapr_phb_vfio_eeh_clear_bus_msix(PCIBus *bus, void *opaque)
144 {
145        pci_for_each_device(bus, pci_bus_num(bus),
146                            spapr_phb_vfio_eeh_clear_dev_msix, NULL);
147 }
148 
149 static void spapr_phb_vfio_eeh_pre_reset(sPAPRPHBState *sphb)
150 {
151        PCIHostState *phb = PCI_HOST_BRIDGE(sphb);
152 
153        pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_clear_bus_msix, NULL);
154 }
155 
156 int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option)
157 {
158     uint32_t op;
159     int ret;
160 
161     switch (option) {
162     case RTAS_SLOT_RESET_DEACTIVATE:
163         op = VFIO_EEH_PE_RESET_DEACTIVATE;
164         break;
165     case RTAS_SLOT_RESET_HOT:
166         spapr_phb_vfio_eeh_pre_reset(sphb);
167         op = VFIO_EEH_PE_RESET_HOT;
168         break;
169     case RTAS_SLOT_RESET_FUNDAMENTAL:
170         spapr_phb_vfio_eeh_pre_reset(sphb);
171         op = VFIO_EEH_PE_RESET_FUNDAMENTAL;
172         break;
173     default:
174         return RTAS_OUT_PARAM_ERROR;
175     }
176 
177     ret = vfio_eeh_as_op(&sphb->iommu_as, op);
178     if (ret < 0) {
179         return RTAS_OUT_HW_ERROR;
180     }
181 
182     return RTAS_OUT_SUCCESS;
183 }
184 
185 int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb)
186 {
187     int ret;
188 
189     ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_CONFIGURE);
190     if (ret < 0) {
191         return RTAS_OUT_PARAM_ERROR;
192     }
193 
194     return RTAS_OUT_SUCCESS;
195 }
196