xref: /openbmc/qemu/hw/ppc/spapr_pci_vfio.c (revision cbb45ff0)
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 }
218