1 /*
2 * USB xHCI controller with PCI bus emulation
3 *
4 * SPDX-FileCopyrightText: 2011 Securiforest
5 * SPDX-FileContributor: Hector Martin <hector@marcansoft.com>
6 * SPDX-sourceInfo: Based on usb-ohci.c, emulates Renesas NEC USB 3.0
7 * SPDX-FileCopyrightText: 2020 Xilinx
8 * SPDX-FileContributor: Sai Pavan Boddu <sai.pavan.boddu@xilinx.com>
9 * SPDX-sourceInfo: Moved the pci specific content for hcd-xhci.c to
10 * hcd-xhci-pci.c
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
24 */
25 #include "qemu/osdep.h"
26 #include "hw/pci/pci.h"
27 #include "hw/qdev-properties.h"
28 #include "migration/vmstate.h"
29 #include "hw/pci/msi.h"
30 #include "hw/pci/msix.h"
31 #include "hcd-xhci-pci.h"
32 #include "trace.h"
33 #include "qapi/error.h"
34
35 #define OFF_MSIX_TABLE 0x3000
36 #define OFF_MSIX_PBA 0x3800
37
xhci_pci_intr_update(XHCIState * xhci,int n,bool enable)38 static void xhci_pci_intr_update(XHCIState *xhci, int n, bool enable)
39 {
40 XHCIPciState *s = container_of(xhci, XHCIPciState, xhci);
41 PCIDevice *pci_dev = PCI_DEVICE(s);
42
43 if (!msix_enabled(pci_dev)) {
44 return;
45 }
46 if (enable == !!xhci->intr[n].msix_used) {
47 return;
48 }
49 if (enable) {
50 trace_usb_xhci_irq_msix_use(n);
51 msix_vector_use(pci_dev, n);
52 xhci->intr[n].msix_used = true;
53 } else {
54 trace_usb_xhci_irq_msix_unuse(n);
55 msix_vector_unuse(pci_dev, n);
56 xhci->intr[n].msix_used = false;
57 }
58 }
59
xhci_pci_intr_raise(XHCIState * xhci,int n,bool level)60 static bool xhci_pci_intr_raise(XHCIState *xhci, int n, bool level)
61 {
62 XHCIPciState *s = container_of(xhci, XHCIPciState, xhci);
63 PCIDevice *pci_dev = PCI_DEVICE(s);
64
65 if (n == 0 &&
66 !(msix_enabled(pci_dev) ||
67 msi_enabled(pci_dev))) {
68 pci_set_irq(pci_dev, level);
69 }
70
71 if (msix_enabled(pci_dev) && level) {
72 msix_notify(pci_dev, n);
73 return true;
74 }
75
76 if (msi_enabled(pci_dev) && level) {
77 n %= msi_nr_vectors_allocated(pci_dev);
78 msi_notify(pci_dev, n);
79 return true;
80 }
81
82 return false;
83 }
84
xhci_pci_intr_mapping_conditional(XHCIState * xhci)85 static bool xhci_pci_intr_mapping_conditional(XHCIState *xhci)
86 {
87 XHCIPciState *s = container_of(xhci, XHCIPciState, xhci);
88 PCIDevice *pci_dev = PCI_DEVICE(s);
89
90 /*
91 * Implementation of the "conditional-intr-mapping" property, which only
92 * enables interrupter mapping if MSI or MSI-X is available and active.
93 * Forces all events onto interrupter/event ring 0 in pin-based IRQ mode.
94 * Provides compatibility with macOS guests on machine types where MSI(-X)
95 * is not available.
96 */
97 return msix_enabled(pci_dev) || msi_enabled(pci_dev);
98 }
99
xhci_pci_reset(DeviceState * dev)100 static void xhci_pci_reset(DeviceState *dev)
101 {
102 XHCIPciState *s = XHCI_PCI(dev);
103
104 device_cold_reset(DEVICE(&s->xhci));
105 }
106
xhci_pci_vmstate_post_load(void * opaque,int version_id)107 static int xhci_pci_vmstate_post_load(void *opaque, int version_id)
108 {
109 XHCIPciState *s = XHCI_PCI(opaque);
110 PCIDevice *pci_dev = PCI_DEVICE(s);
111 int intr;
112
113 for (intr = 0; intr < s->xhci.numintrs; intr++) {
114 if (s->xhci.intr[intr].msix_used) {
115 msix_vector_use(pci_dev, intr);
116 } else {
117 msix_vector_unuse(pci_dev, intr);
118 }
119 }
120 return 0;
121 }
122
usb_xhci_pci_realize(struct PCIDevice * dev,Error ** errp)123 static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp)
124 {
125 int ret;
126 Error *err = NULL;
127 XHCIPciState *s = XHCI_PCI(dev);
128
129 dev->config[PCI_CLASS_PROG] = 0x30; /* xHCI */
130 dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
131 dev->config[PCI_CACHE_LINE_SIZE] = 0x10;
132 dev->config[0x60] = 0x30; /* release number */
133
134 object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL);
135 s->xhci.intr_update = xhci_pci_intr_update;
136 s->xhci.intr_raise = xhci_pci_intr_raise;
137 if (s->conditional_intr_mapping) {
138 s->xhci.intr_mapping_supported = xhci_pci_intr_mapping_conditional;
139 }
140 if (!qdev_realize(DEVICE(&s->xhci), NULL, errp)) {
141 return;
142 }
143 if (strcmp(object_get_typename(OBJECT(dev)), TYPE_NEC_XHCI) == 0) {
144 s->xhci.nec_quirks = true;
145 }
146
147 if (s->msi != ON_OFF_AUTO_OFF) {
148 ret = msi_init(dev, 0x70, s->xhci.numintrs, true, false, &err);
149 /*
150 * Any error other than -ENOTSUP(board's MSI support is broken)
151 * is a programming error
152 */
153 assert(!ret || ret == -ENOTSUP);
154 if (ret && s->msi == ON_OFF_AUTO_ON) {
155 /* Can't satisfy user's explicit msi=on request, fail */
156 error_append_hint(&err, "You have to use msi=auto (default) or "
157 "msi=off with this machine type.\n");
158 error_propagate(errp, err);
159 return;
160 }
161 assert(!err || s->msi == ON_OFF_AUTO_AUTO);
162 /* With msi=auto, we fall back to MSI off silently */
163 error_free(err);
164 }
165 pci_register_bar(dev, 0,
166 PCI_BASE_ADDRESS_SPACE_MEMORY |
167 PCI_BASE_ADDRESS_MEM_TYPE_64,
168 &s->xhci.mem);
169
170 if (pci_bus_is_express(pci_get_bus(dev))) {
171 ret = pcie_endpoint_cap_init(dev, 0xa0);
172 assert(ret > 0);
173 }
174
175 if (s->msix != ON_OFF_AUTO_OFF) {
176 /* TODO check for errors, and should fail when msix=on */
177 msix_init(dev, s->xhci.numintrs,
178 &s->xhci.mem, 0, OFF_MSIX_TABLE,
179 &s->xhci.mem, 0, OFF_MSIX_PBA,
180 0x90, NULL);
181 }
182 s->xhci.as = pci_get_address_space(dev);
183 }
184
usb_xhci_pci_exit(PCIDevice * dev)185 static void usb_xhci_pci_exit(PCIDevice *dev)
186 {
187 XHCIPciState *s = XHCI_PCI(dev);
188 /* destroy msix memory region */
189 if (dev->msix_table && dev->msix_pba
190 && dev->msix_entry_used) {
191 msix_uninit(dev, &s->xhci.mem, &s->xhci.mem);
192 }
193 }
194
195 static const VMStateDescription vmstate_xhci_pci = {
196 .name = "xhci",
197 .version_id = 1,
198 .post_load = xhci_pci_vmstate_post_load,
199 .fields = (const VMStateField[]) {
200 VMSTATE_PCI_DEVICE(parent_obj, XHCIPciState),
201 VMSTATE_MSIX(parent_obj, XHCIPciState),
202 VMSTATE_STRUCT(xhci, XHCIPciState, 1, vmstate_xhci, XHCIState),
203 VMSTATE_END_OF_LIST()
204 }
205 };
206
xhci_instance_init(Object * obj)207 static void xhci_instance_init(Object *obj)
208 {
209 XHCIPciState *s = XHCI_PCI(obj);
210 /*
211 * QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command
212 * line, therefore, no need to wait to realize like other devices
213 */
214 PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS;
215 object_initialize_child(obj, "xhci-core", &s->xhci, TYPE_XHCI);
216 qdev_alias_all_properties(DEVICE(&s->xhci), obj);
217 }
218
219 static const Property xhci_pci_properties[] = {
220 DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO),
221 DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO),
222 DEFINE_PROP_BOOL("conditional-intr-mapping", XHCIPciState,
223 conditional_intr_mapping, false),
224 };
225
xhci_class_init(ObjectClass * klass,const void * data)226 static void xhci_class_init(ObjectClass *klass, const void *data)
227 {
228 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
229 DeviceClass *dc = DEVICE_CLASS(klass);
230
231 device_class_set_legacy_reset(dc, xhci_pci_reset);
232 dc->vmsd = &vmstate_xhci_pci;
233 set_bit(DEVICE_CATEGORY_USB, dc->categories);
234 k->realize = usb_xhci_pci_realize;
235 k->exit = usb_xhci_pci_exit;
236 k->class_id = PCI_CLASS_SERIAL_USB;
237 device_class_set_props(dc, xhci_pci_properties);
238 object_class_property_set_description(klass, "conditional-intr-mapping",
239 "When true, disables interrupter mapping for pin-based IRQ mode. "
240 "Intended to be used with guest drivers with questionable behaviour, "
241 "such as macOS's.");
242 }
243
244 static const TypeInfo xhci_pci_info = {
245 .name = TYPE_XHCI_PCI,
246 .parent = TYPE_PCI_DEVICE,
247 .instance_size = sizeof(XHCIPciState),
248 .class_init = xhci_class_init,
249 .instance_init = xhci_instance_init,
250 .abstract = true,
251 .interfaces = (const InterfaceInfo[]) {
252 { INTERFACE_PCIE_DEVICE },
253 { INTERFACE_CONVENTIONAL_PCI_DEVICE },
254 { }
255 },
256 };
257
qemu_xhci_class_init(ObjectClass * klass,const void * data)258 static void qemu_xhci_class_init(ObjectClass *klass, const void *data)
259 {
260 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
261
262 k->vendor_id = PCI_VENDOR_ID_REDHAT;
263 k->device_id = PCI_DEVICE_ID_REDHAT_XHCI;
264 k->revision = 0x01;
265 }
266
qemu_xhci_instance_init(Object * obj)267 static void qemu_xhci_instance_init(Object *obj)
268 {
269 XHCIPciState *s = XHCI_PCI(obj);
270 XHCIState *xhci = &s->xhci;
271
272 s->msi = ON_OFF_AUTO_OFF;
273 s->msix = ON_OFF_AUTO_AUTO;
274 xhci->numintrs = XHCI_MAXINTRS;
275 xhci->numslots = XHCI_MAXSLOTS;
276 }
277
278 static const TypeInfo qemu_xhci_info = {
279 .name = TYPE_QEMU_XHCI,
280 .parent = TYPE_XHCI_PCI,
281 .class_init = qemu_xhci_class_init,
282 .instance_init = qemu_xhci_instance_init,
283 };
284
xhci_register_types(void)285 static void xhci_register_types(void)
286 {
287 type_register_static(&xhci_pci_info);
288 type_register_static(&qemu_xhci_info);
289 }
290
291 type_init(xhci_register_types)
292