]>
Commit | Line | Data |
---|---|---|
108f7bba PD |
1 | /* |
2 | * Copyright (c) 2018 Citrix Systems Inc. | |
3 | * | |
4 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
5 | * See the COPYING file in the top-level directory. | |
6 | */ | |
7 | ||
8 | #include "qemu/osdep.h" | |
82a29e30 | 9 | #include "qemu/main-loop.h" |
0b8fa32f | 10 | #include "qemu/module.h" |
82a29e30 | 11 | #include "qemu/uuid.h" |
a27bd6c7 | 12 | #include "hw/qdev-properties.h" |
108f7bba | 13 | #include "hw/sysbus.h" |
094a2239 | 14 | #include "hw/xen/xen.h" |
a783f8ad | 15 | #include "hw/xen/xen-backend.h" |
108f7bba | 16 | #include "hw/xen/xen-bus.h" |
094a2239 PD |
17 | #include "hw/xen/xen-bus-helper.h" |
18 | #include "monitor/monitor.h" | |
108f7bba | 19 | #include "qapi/error.h" |
a783f8ad | 20 | #include "qapi/qmp/qdict.h" |
094a2239 | 21 | #include "sysemu/sysemu.h" |
108f7bba PD |
22 | #include "trace.h" |
23 | ||
094a2239 PD |
24 | static char *xen_device_get_backend_path(XenDevice *xendev) |
25 | { | |
26 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
27 | XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); | |
28 | const char *type = object_get_typename(OBJECT(xendev)); | |
29 | const char *backend = xendev_class->backend; | |
30 | ||
31 | if (!backend) { | |
32 | backend = type; | |
33 | } | |
34 | ||
35 | return g_strdup_printf("/local/domain/%u/backend/%s/%u/%s", | |
36 | xenbus->backend_id, backend, xendev->frontend_id, | |
37 | xendev->name); | |
38 | } | |
39 | ||
40 | static char *xen_device_get_frontend_path(XenDevice *xendev) | |
41 | { | |
42 | XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); | |
43 | const char *type = object_get_typename(OBJECT(xendev)); | |
44 | const char *device = xendev_class->device; | |
45 | ||
46 | if (!device) { | |
47 | device = type; | |
48 | } | |
49 | ||
50 | return g_strdup_printf("/local/domain/%u/device/%s/%s", | |
51 | xendev->frontend_id, device, xendev->name); | |
52 | } | |
53 | ||
b6af8926 PD |
54 | static void xen_device_unplug(XenDevice *xendev, Error **errp) |
55 | { | |
1de7096d | 56 | ERRP_GUARD(); |
b6af8926 PD |
57 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); |
58 | const char *type = object_get_typename(OBJECT(xendev)); | |
b6af8926 PD |
59 | xs_transaction_t tid; |
60 | ||
61 | trace_xen_device_unplug(type, xendev->name); | |
62 | ||
63 | /* Mimic the way the Xen toolstack does an unplug */ | |
64 | again: | |
ba2a92db | 65 | tid = qemu_xen_xs_transaction_start(xenbus->xsh); |
b6af8926 PD |
66 | if (tid == XBT_NULL) { |
67 | error_setg_errno(errp, errno, "failed xs_transaction_start"); | |
68 | return; | |
69 | } | |
70 | ||
71 | xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "online", | |
1de7096d VSO |
72 | errp, "%u", 0); |
73 | if (*errp) { | |
b6af8926 PD |
74 | goto abort; |
75 | } | |
76 | ||
77 | xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "state", | |
1de7096d VSO |
78 | errp, "%u", XenbusStateClosing); |
79 | if (*errp) { | |
b6af8926 PD |
80 | goto abort; |
81 | } | |
82 | ||
ba2a92db | 83 | if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) { |
b6af8926 PD |
84 | if (errno == EAGAIN) { |
85 | goto again; | |
86 | } | |
87 | ||
88 | error_setg_errno(errp, errno, "failed xs_transaction_end"); | |
89 | } | |
90 | ||
91 | return; | |
92 | ||
93 | abort: | |
94 | /* | |
95 | * We only abort if there is already a failure so ignore any error | |
96 | * from ending the transaction. | |
97 | */ | |
ba2a92db | 98 | qemu_xen_xs_transaction_end(xenbus->xsh, tid, true); |
b6af8926 PD |
99 | } |
100 | ||
094a2239 PD |
101 | static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent) |
102 | { | |
103 | XenDevice *xendev = XEN_DEVICE(dev); | |
104 | ||
105 | monitor_printf(mon, "%*sname = '%s' frontend_id = %u\n", | |
106 | indent, "", xendev->name, xendev->frontend_id); | |
107 | } | |
108 | ||
109 | static char *xen_bus_get_dev_path(DeviceState *dev) | |
110 | { | |
111 | return xen_device_get_backend_path(XEN_DEVICE(dev)); | |
112 | } | |
113 | ||
a783f8ad PD |
114 | static void xen_bus_backend_create(XenBus *xenbus, const char *type, |
115 | const char *name, char *path, | |
116 | Error **errp) | |
117 | { | |
1de7096d | 118 | ERRP_GUARD(); |
a783f8ad PD |
119 | xs_transaction_t tid; |
120 | char **key; | |
121 | QDict *opts; | |
122 | unsigned int i, n; | |
a783f8ad PD |
123 | |
124 | trace_xen_bus_backend_create(type, path); | |
125 | ||
126 | again: | |
ba2a92db | 127 | tid = qemu_xen_xs_transaction_start(xenbus->xsh); |
a783f8ad PD |
128 | if (tid == XBT_NULL) { |
129 | error_setg(errp, "failed xs_transaction_start"); | |
130 | return; | |
131 | } | |
132 | ||
ba2a92db | 133 | key = qemu_xen_xs_directory(xenbus->xsh, tid, path, &n); |
a783f8ad | 134 | if (!key) { |
ba2a92db | 135 | if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, true)) { |
a783f8ad PD |
136 | error_setg_errno(errp, errno, "failed xs_transaction_end"); |
137 | } | |
138 | return; | |
139 | } | |
140 | ||
141 | opts = qdict_new(); | |
142 | for (i = 0; i < n; i++) { | |
143 | char *val; | |
144 | ||
145 | /* | |
146 | * Assume anything found in the xenstore backend area, other than | |
147 | * the keys created for a generic XenDevice, are parameters | |
148 | * to be used to configure the backend. | |
149 | */ | |
150 | if (!strcmp(key[i], "state") || | |
151 | !strcmp(key[i], "online") || | |
152 | !strcmp(key[i], "frontend") || | |
153 | !strcmp(key[i], "frontend-id") || | |
154 | !strcmp(key[i], "hotplug-status")) | |
155 | continue; | |
156 | ||
157 | if (xs_node_scanf(xenbus->xsh, tid, path, key[i], NULL, "%ms", | |
158 | &val) == 1) { | |
159 | qdict_put_str(opts, key[i], val); | |
160 | free(val); | |
161 | } | |
162 | } | |
163 | ||
164 | free(key); | |
165 | ||
ba2a92db | 166 | if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) { |
a783f8ad PD |
167 | qobject_unref(opts); |
168 | ||
169 | if (errno == EAGAIN) { | |
170 | goto again; | |
171 | } | |
172 | ||
173 | error_setg_errno(errp, errno, "failed xs_transaction_end"); | |
174 | return; | |
175 | } | |
176 | ||
1de7096d | 177 | xen_backend_device_create(xenbus, type, name, opts, errp); |
a783f8ad PD |
178 | qobject_unref(opts); |
179 | ||
1de7096d VSO |
180 | if (*errp) { |
181 | error_prepend(errp, "failed to create '%s' device '%s': ", type, name); | |
a783f8ad PD |
182 | } |
183 | } | |
184 | ||
185 | static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) | |
186 | { | |
187 | char *domain_path = g_strdup_printf("backend/%s/%u", type, xen_domid); | |
188 | char **backend; | |
189 | unsigned int i, n; | |
190 | ||
191 | trace_xen_bus_type_enumerate(type); | |
192 | ||
ba2a92db | 193 | backend = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n); |
a783f8ad PD |
194 | if (!backend) { |
195 | goto out; | |
196 | } | |
197 | ||
198 | for (i = 0; i < n; i++) { | |
199 | char *backend_path = g_strdup_printf("%s/%s", domain_path, | |
200 | backend[i]); | |
3809f758 PD |
201 | enum xenbus_state state; |
202 | unsigned int online; | |
a783f8ad PD |
203 | |
204 | if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "state", | |
3809f758 PD |
205 | NULL, "%u", &state) != 1) |
206 | state = XenbusStateUnknown; | |
a783f8ad | 207 | |
3809f758 PD |
208 | if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "online", |
209 | NULL, "%u", &online) != 1) | |
210 | online = 0; | |
211 | ||
212 | if (online && state == XenbusStateInitialising) { | |
a783f8ad PD |
213 | Error *local_err = NULL; |
214 | ||
215 | xen_bus_backend_create(xenbus, type, backend[i], backend_path, | |
216 | &local_err); | |
217 | if (local_err) { | |
218 | error_report_err(local_err); | |
219 | } | |
220 | } | |
221 | ||
222 | g_free(backend_path); | |
223 | } | |
224 | ||
225 | free(backend); | |
226 | ||
227 | out: | |
228 | g_free(domain_path); | |
229 | } | |
230 | ||
3809f758 | 231 | static void xen_bus_enumerate(XenBus *xenbus) |
a783f8ad | 232 | { |
a783f8ad PD |
233 | char **type; |
234 | unsigned int i, n; | |
235 | ||
236 | trace_xen_bus_enumerate(); | |
237 | ||
ba2a92db | 238 | type = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, "backend", &n); |
a783f8ad PD |
239 | if (!type) { |
240 | return; | |
241 | } | |
242 | ||
243 | for (i = 0; i < n; i++) { | |
244 | xen_bus_type_enumerate(xenbus, type[i]); | |
245 | } | |
246 | ||
247 | free(type); | |
248 | } | |
249 | ||
3809f758 PD |
250 | static void xen_bus_device_cleanup(XenDevice *xendev) |
251 | { | |
252 | const char *type = object_get_typename(OBJECT(xendev)); | |
253 | Error *local_err = NULL; | |
254 | ||
255 | trace_xen_bus_device_cleanup(type, xendev->name); | |
256 | ||
257 | g_assert(!xendev->backend_online); | |
258 | ||
259 | if (!xen_backend_try_device_destroy(xendev, &local_err)) { | |
260 | object_unparent(OBJECT(xendev)); | |
261 | } | |
262 | ||
263 | if (local_err) { | |
264 | error_report_err(local_err); | |
265 | } | |
266 | } | |
267 | ||
268 | static void xen_bus_cleanup(XenBus *xenbus) | |
269 | { | |
270 | XenDevice *xendev, *next; | |
271 | ||
272 | trace_xen_bus_cleanup(); | |
273 | ||
274 | QLIST_FOREACH_SAFE(xendev, &xenbus->inactive_devices, list, next) { | |
275 | g_assert(xendev->inactive); | |
276 | QLIST_REMOVE(xendev, list); | |
277 | xen_bus_device_cleanup(xendev); | |
278 | } | |
279 | } | |
280 | ||
ba2a92db | 281 | static void xen_bus_backend_changed(void *opaque, const char *path) |
3809f758 PD |
282 | { |
283 | XenBus *xenbus = opaque; | |
284 | ||
285 | xen_bus_enumerate(xenbus); | |
286 | xen_bus_cleanup(xenbus); | |
287 | } | |
288 | ||
b69c3c21 | 289 | static void xen_bus_unrealize(BusState *bus) |
108f7bba | 290 | { |
094a2239 PD |
291 | XenBus *xenbus = XEN_BUS(bus); |
292 | ||
108f7bba | 293 | trace_xen_bus_unrealize(); |
094a2239 | 294 | |
a783f8ad | 295 | if (xenbus->backend_watch) { |
c4583c8c PD |
296 | unsigned int i; |
297 | ||
298 | for (i = 0; i < xenbus->backend_types; i++) { | |
299 | if (xenbus->backend_watch[i]) { | |
ba2a92db | 300 | xs_node_unwatch(xenbus->xsh, xenbus->backend_watch[i]); |
c4583c8c PD |
301 | } |
302 | } | |
303 | ||
304 | g_free(xenbus->backend_watch); | |
a783f8ad PD |
305 | xenbus->backend_watch = NULL; |
306 | } | |
307 | ||
374752a2 | 308 | if (xenbus->xsh) { |
ba2a92db | 309 | qemu_xen_xs_close(xenbus->xsh); |
82a29e30 | 310 | } |
82a29e30 PD |
311 | } |
312 | ||
108f7bba PD |
313 | static void xen_bus_realize(BusState *bus, Error **errp) |
314 | { | |
c4583c8c | 315 | char *key = g_strdup_printf("%u", xen_domid); |
094a2239 PD |
316 | XenBus *xenbus = XEN_BUS(bus); |
317 | unsigned int domid; | |
c4583c8c PD |
318 | const char **type; |
319 | unsigned int i; | |
a783f8ad | 320 | Error *local_err = NULL; |
094a2239 | 321 | |
108f7bba | 322 | trace_xen_bus_realize(); |
094a2239 | 323 | |
ba2a92db | 324 | xenbus->xsh = qemu_xen_xs_open(); |
094a2239 PD |
325 | if (!xenbus->xsh) { |
326 | error_setg_errno(errp, errno, "failed xs_open"); | |
327 | goto fail; | |
328 | } | |
329 | ||
330 | if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */ | |
331 | "domid", NULL, "%u", &domid) == 1) { | |
332 | xenbus->backend_id = domid; | |
333 | } else { | |
334 | xenbus->backend_id = 0; /* Assume lack of node means dom0 */ | |
335 | } | |
336 | ||
a783f8ad PD |
337 | module_call_init(MODULE_INIT_XEN_BACKEND); |
338 | ||
c4583c8c | 339 | type = xen_backend_get_types(&xenbus->backend_types); |
ba2a92db PD |
340 | xenbus->backend_watch = g_new(struct qemu_xs_watch *, |
341 | xenbus->backend_types); | |
c4583c8c PD |
342 | |
343 | for (i = 0; i < xenbus->backend_types; i++) { | |
344 | char *node = g_strdup_printf("backend/%s", type[i]); | |
345 | ||
346 | xenbus->backend_watch[i] = | |
ba2a92db PD |
347 | xs_node_watch(xenbus->xsh, node, key, xen_bus_backend_changed, |
348 | xenbus, &local_err); | |
c4583c8c PD |
349 | if (local_err) { |
350 | /* This need not be treated as a hard error so don't propagate */ | |
351 | error_reportf_err(local_err, | |
352 | "failed to set up '%s' enumeration watch: ", | |
353 | type[i]); | |
354 | } | |
355 | ||
356 | g_free(node); | |
a783f8ad PD |
357 | } |
358 | ||
c4583c8c PD |
359 | g_free(type); |
360 | g_free(key); | |
094a2239 PD |
361 | return; |
362 | ||
363 | fail: | |
b69c3c21 | 364 | xen_bus_unrealize(bus); |
c4583c8c | 365 | g_free(key); |
108f7bba PD |
366 | } |
367 | ||
b6af8926 PD |
368 | static void xen_bus_unplug_request(HotplugHandler *hotplug, |
369 | DeviceState *dev, | |
370 | Error **errp) | |
371 | { | |
372 | XenDevice *xendev = XEN_DEVICE(dev); | |
373 | ||
374 | xen_device_unplug(xendev, errp); | |
375 | } | |
376 | ||
108f7bba PD |
377 | static void xen_bus_class_init(ObjectClass *class, void *data) |
378 | { | |
379 | BusClass *bus_class = BUS_CLASS(class); | |
b6af8926 | 380 | HotplugHandlerClass *hotplug_class = HOTPLUG_HANDLER_CLASS(class); |
108f7bba | 381 | |
094a2239 PD |
382 | bus_class->print_dev = xen_bus_print_dev; |
383 | bus_class->get_dev_path = xen_bus_get_dev_path; | |
108f7bba PD |
384 | bus_class->realize = xen_bus_realize; |
385 | bus_class->unrealize = xen_bus_unrealize; | |
b6af8926 PD |
386 | |
387 | hotplug_class->unplug_request = xen_bus_unplug_request; | |
108f7bba PD |
388 | } |
389 | ||
390 | static const TypeInfo xen_bus_type_info = { | |
391 | .name = TYPE_XEN_BUS, | |
392 | .parent = TYPE_BUS, | |
393 | .instance_size = sizeof(XenBus), | |
394 | .class_size = sizeof(XenBusClass), | |
395 | .class_init = xen_bus_class_init, | |
396 | .interfaces = (InterfaceInfo[]) { | |
397 | { TYPE_HOTPLUG_HANDLER }, | |
398 | { } | |
399 | }, | |
400 | }; | |
401 | ||
b6af8926 PD |
402 | void xen_device_backend_printf(XenDevice *xendev, const char *key, |
403 | const char *fmt, ...) | |
094a2239 PD |
404 | { |
405 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
406 | Error *local_err = NULL; | |
407 | va_list ap; | |
408 | ||
409 | g_assert(xenbus->xsh); | |
410 | ||
411 | va_start(ap, fmt); | |
412 | xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, | |
413 | &local_err, fmt, ap); | |
414 | va_end(ap); | |
415 | ||
416 | if (local_err) { | |
417 | error_report_err(local_err); | |
418 | } | |
419 | } | |
420 | ||
d62449da | 421 | G_GNUC_SCANF(3, 4) |
82a29e30 PD |
422 | static int xen_device_backend_scanf(XenDevice *xendev, const char *key, |
423 | const char *fmt, ...) | |
424 | { | |
425 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
426 | va_list ap; | |
427 | int rc; | |
428 | ||
429 | g_assert(xenbus->xsh); | |
430 | ||
431 | va_start(ap, fmt); | |
432 | rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, | |
433 | NULL, fmt, ap); | |
434 | va_end(ap); | |
435 | ||
436 | return rc; | |
437 | } | |
438 | ||
439 | void xen_device_backend_set_state(XenDevice *xendev, | |
440 | enum xenbus_state state) | |
094a2239 PD |
441 | { |
442 | const char *type = object_get_typename(OBJECT(xendev)); | |
443 | ||
444 | if (xendev->backend_state == state) { | |
445 | return; | |
446 | } | |
447 | ||
448 | trace_xen_device_backend_state(type, xendev->name, | |
449 | xs_strstate(state)); | |
450 | ||
451 | xendev->backend_state = state; | |
452 | xen_device_backend_printf(xendev, "state", "%u", state); | |
453 | } | |
454 | ||
82a29e30 PD |
455 | enum xenbus_state xen_device_backend_get_state(XenDevice *xendev) |
456 | { | |
457 | return xendev->backend_state; | |
458 | } | |
459 | ||
b6af8926 PD |
460 | static void xen_device_backend_set_online(XenDevice *xendev, bool online) |
461 | { | |
462 | const char *type = object_get_typename(OBJECT(xendev)); | |
463 | ||
464 | if (xendev->backend_online == online) { | |
465 | return; | |
466 | } | |
467 | ||
468 | trace_xen_device_backend_online(type, xendev->name, online); | |
469 | ||
470 | xendev->backend_online = online; | |
471 | xen_device_backend_printf(xendev, "online", "%u", online); | |
472 | } | |
473 | ||
cb323146 AP |
474 | /* |
475 | * Tell from the state whether the frontend is likely alive, | |
476 | * i.e. it will react to a change of state of the backend. | |
477 | */ | |
3809f758 | 478 | static bool xen_device_frontend_is_active(XenDevice *xendev) |
cb323146 | 479 | { |
3809f758 | 480 | switch (xendev->frontend_state) { |
cb323146 AP |
481 | case XenbusStateInitWait: |
482 | case XenbusStateInitialised: | |
483 | case XenbusStateConnected: | |
484 | case XenbusStateClosing: | |
485 | return true; | |
486 | default: | |
487 | return false; | |
488 | } | |
489 | } | |
490 | ||
ba2a92db | 491 | static void xen_device_backend_changed(void *opaque, const char *path) |
b6af8926 PD |
492 | { |
493 | XenDevice *xendev = opaque; | |
494 | const char *type = object_get_typename(OBJECT(xendev)); | |
495 | enum xenbus_state state; | |
496 | unsigned int online; | |
497 | ||
498 | trace_xen_device_backend_changed(type, xendev->name); | |
499 | ||
500 | if (xen_device_backend_scanf(xendev, "state", "%u", &state) != 1) { | |
501 | state = XenbusStateUnknown; | |
502 | } | |
503 | ||
504 | xen_device_backend_set_state(xendev, state); | |
505 | ||
506 | if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) { | |
507 | online = 0; | |
508 | } | |
509 | ||
510 | xen_device_backend_set_online(xendev, !!online); | |
511 | ||
512 | /* | |
513 | * If the toolstack (or unplug request callback) has set the backend | |
cb323146 AP |
514 | * state to Closing, but there is no active frontend then set the |
515 | * backend state to Closed. | |
b6af8926 | 516 | */ |
3809f758 PD |
517 | if (state == XenbusStateClosing && |
518 | !xen_device_frontend_is_active(xendev)) { | |
b6af8926 PD |
519 | xen_device_backend_set_state(xendev, XenbusStateClosed); |
520 | } | |
521 | ||
522 | /* | |
67bc8e00 | 523 | * If a backend is still 'online' then we should leave it alone but, |
3809f758 PD |
524 | * if a backend is not 'online', then the device is a candidate |
525 | * for destruction. Hence add it to the 'inactive' list to be cleaned | |
526 | * by xen_bus_cleanup(). | |
b6af8926 | 527 | */ |
3809f758 PD |
528 | if (!online && |
529 | (state == XenbusStateClosed || state == XenbusStateInitialising || | |
530 | state == XenbusStateInitWait || state == XenbusStateUnknown) && | |
531 | !xendev->inactive) { | |
532 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
a783f8ad | 533 | |
3809f758 PD |
534 | xendev->inactive = true; |
535 | QLIST_INSERT_HEAD(&xenbus->inactive_devices, xendev, list); | |
536 | ||
537 | /* | |
538 | * Re-write the state to cause a XenBus backend_watch notification, | |
539 | * resulting in a call to xen_bus_cleanup(). | |
540 | */ | |
541 | xen_device_backend_printf(xendev, "state", "%u", state); | |
b6af8926 PD |
542 | } |
543 | } | |
544 | ||
094a2239 PD |
545 | static void xen_device_backend_create(XenDevice *xendev, Error **errp) |
546 | { | |
1de7096d | 547 | ERRP_GUARD(); |
094a2239 | 548 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); |
094a2239 PD |
549 | |
550 | xendev->backend_path = xen_device_get_backend_path(xendev); | |
551 | ||
094a2239 PD |
552 | g_assert(xenbus->xsh); |
553 | ||
ba2a92db PD |
554 | xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, |
555 | xenbus->backend_id, xendev->frontend_id, XS_PERM_READ, errp); | |
1de7096d VSO |
556 | if (*errp) { |
557 | error_prepend(errp, "failed to create backend: "); | |
b6af8926 PD |
558 | return; |
559 | } | |
560 | ||
561 | xendev->backend_state_watch = | |
ba2a92db PD |
562 | xs_node_watch(xendev->xsh, xendev->backend_path, |
563 | "state", xen_device_backend_changed, xendev, | |
564 | errp); | |
1de7096d VSO |
565 | if (*errp) { |
566 | error_prepend(errp, "failed to watch backend state: "); | |
b6af8926 PD |
567 | return; |
568 | } | |
569 | ||
570 | xendev->backend_online_watch = | |
ba2a92db PD |
571 | xs_node_watch(xendev->xsh, xendev->backend_path, |
572 | "online", xen_device_backend_changed, xendev, | |
573 | errp); | |
1de7096d VSO |
574 | if (*errp) { |
575 | error_prepend(errp, "failed to watch backend online: "); | |
b6af8926 | 576 | return; |
094a2239 PD |
577 | } |
578 | } | |
579 | ||
580 | static void xen_device_backend_destroy(XenDevice *xendev) | |
581 | { | |
582 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
583 | Error *local_err = NULL; | |
584 | ||
b6af8926 | 585 | if (xendev->backend_online_watch) { |
ba2a92db | 586 | xs_node_unwatch(xendev->xsh, xendev->backend_online_watch); |
b6af8926 PD |
587 | xendev->backend_online_watch = NULL; |
588 | } | |
589 | ||
590 | if (xendev->backend_state_watch) { | |
ba2a92db | 591 | xs_node_unwatch(xendev->xsh, xendev->backend_state_watch); |
b6af8926 PD |
592 | xendev->backend_state_watch = NULL; |
593 | } | |
594 | ||
094a2239 PD |
595 | if (!xendev->backend_path) { |
596 | return; | |
597 | } | |
598 | ||
599 | g_assert(xenbus->xsh); | |
600 | ||
601 | xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->backend_path, | |
602 | &local_err); | |
603 | g_free(xendev->backend_path); | |
604 | xendev->backend_path = NULL; | |
605 | ||
606 | if (local_err) { | |
607 | error_report_err(local_err); | |
608 | } | |
609 | } | |
610 | ||
b6af8926 PD |
611 | void xen_device_frontend_printf(XenDevice *xendev, const char *key, |
612 | const char *fmt, ...) | |
094a2239 PD |
613 | { |
614 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
615 | Error *local_err = NULL; | |
616 | va_list ap; | |
617 | ||
618 | g_assert(xenbus->xsh); | |
619 | ||
620 | va_start(ap, fmt); | |
621 | xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, | |
622 | &local_err, fmt, ap); | |
623 | va_end(ap); | |
624 | ||
625 | if (local_err) { | |
626 | error_report_err(local_err); | |
627 | } | |
628 | } | |
629 | ||
b6af8926 PD |
630 | int xen_device_frontend_scanf(XenDevice *xendev, const char *key, |
631 | const char *fmt, ...) | |
82a29e30 PD |
632 | { |
633 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
634 | va_list ap; | |
635 | int rc; | |
636 | ||
637 | g_assert(xenbus->xsh); | |
638 | ||
639 | va_start(ap, fmt); | |
640 | rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, | |
641 | NULL, fmt, ap); | |
642 | va_end(ap); | |
643 | ||
644 | return rc; | |
645 | } | |
646 | ||
094a2239 | 647 | static void xen_device_frontend_set_state(XenDevice *xendev, |
705be570 AP |
648 | enum xenbus_state state, |
649 | bool publish) | |
094a2239 PD |
650 | { |
651 | const char *type = object_get_typename(OBJECT(xendev)); | |
652 | ||
653 | if (xendev->frontend_state == state) { | |
654 | return; | |
655 | } | |
656 | ||
657 | trace_xen_device_frontend_state(type, xendev->name, | |
658 | xs_strstate(state)); | |
659 | ||
660 | xendev->frontend_state = state; | |
705be570 AP |
661 | if (publish) { |
662 | xen_device_frontend_printf(xendev, "state", "%u", state); | |
663 | } | |
094a2239 PD |
664 | } |
665 | ||
ba2a92db | 666 | static void xen_device_frontend_changed(void *opaque, const char *path) |
82a29e30 PD |
667 | { |
668 | XenDevice *xendev = opaque; | |
669 | XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); | |
670 | const char *type = object_get_typename(OBJECT(xendev)); | |
671 | enum xenbus_state state; | |
672 | ||
673 | trace_xen_device_frontend_changed(type, xendev->name); | |
674 | ||
675 | if (xen_device_frontend_scanf(xendev, "state", "%u", &state) != 1) { | |
676 | state = XenbusStateUnknown; | |
677 | } | |
678 | ||
705be570 | 679 | xen_device_frontend_set_state(xendev, state, false); |
82a29e30 | 680 | |
67bc8e00 PD |
681 | if (state == XenbusStateInitialising && |
682 | xendev->backend_state == XenbusStateClosed && | |
683 | xendev->backend_online) { | |
684 | /* | |
685 | * The frontend is re-initializing so switch back to | |
686 | * InitWait. | |
687 | */ | |
688 | xen_device_backend_set_state(xendev, XenbusStateInitWait); | |
689 | return; | |
690 | } | |
691 | ||
82a29e30 PD |
692 | if (xendev_class->frontend_changed) { |
693 | Error *local_err = NULL; | |
694 | ||
695 | xendev_class->frontend_changed(xendev, state, &local_err); | |
696 | ||
697 | if (local_err) { | |
698 | error_reportf_err(local_err, "frontend change error: "); | |
699 | } | |
700 | } | |
82a29e30 PD |
701 | } |
702 | ||
6bd6b955 MS |
703 | static bool xen_device_frontend_exists(XenDevice *xendev) |
704 | { | |
705 | enum xenbus_state state; | |
706 | ||
707 | return (xen_device_frontend_scanf(xendev, "state", "%u", &state) == 1); | |
708 | } | |
709 | ||
094a2239 PD |
710 | static void xen_device_frontend_create(XenDevice *xendev, Error **errp) |
711 | { | |
1de7096d | 712 | ERRP_GUARD(); |
094a2239 | 713 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); |
094a2239 PD |
714 | |
715 | xendev->frontend_path = xen_device_get_frontend_path(xendev); | |
716 | ||
6bd6b955 MS |
717 | /* |
718 | * The frontend area may have already been created by a legacy | |
719 | * toolstack. | |
720 | */ | |
721 | if (!xen_device_frontend_exists(xendev)) { | |
6bd6b955 | 722 | g_assert(xenbus->xsh); |
094a2239 | 723 | |
ba2a92db PD |
724 | xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, |
725 | xendev->frontend_id, xenbus->backend_id, | |
726 | XS_PERM_READ | XS_PERM_WRITE, errp); | |
1de7096d VSO |
727 | if (*errp) { |
728 | error_prepend(errp, "failed to create frontend: "); | |
6bd6b955 MS |
729 | return; |
730 | } | |
82a29e30 PD |
731 | } |
732 | ||
733 | xendev->frontend_state_watch = | |
ba2a92db PD |
734 | xs_node_watch(xendev->xsh, xendev->frontend_path, "state", |
735 | xen_device_frontend_changed, xendev, errp); | |
1de7096d VSO |
736 | if (*errp) { |
737 | error_prepend(errp, "failed to watch frontend state: "); | |
094a2239 PD |
738 | } |
739 | } | |
740 | ||
741 | static void xen_device_frontend_destroy(XenDevice *xendev) | |
742 | { | |
743 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
744 | Error *local_err = NULL; | |
745 | ||
82a29e30 | 746 | if (xendev->frontend_state_watch) { |
ba2a92db | 747 | xs_node_unwatch(xendev->xsh, xendev->frontend_state_watch); |
82a29e30 PD |
748 | xendev->frontend_state_watch = NULL; |
749 | } | |
750 | ||
094a2239 PD |
751 | if (!xendev->frontend_path) { |
752 | return; | |
753 | } | |
754 | ||
755 | g_assert(xenbus->xsh); | |
756 | ||
757 | xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->frontend_path, | |
758 | &local_err); | |
759 | g_free(xendev->frontend_path); | |
760 | xendev->frontend_path = NULL; | |
761 | ||
762 | if (local_err) { | |
763 | error_report_err(local_err); | |
764 | } | |
765 | } | |
766 | ||
4b34b5b1 PD |
767 | void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, |
768 | Error **errp) | |
769 | { | |
c412ba47 | 770 | if (qemu_xen_gnttab_set_max_grants(xendev->xgth, nr_refs)) { |
4b34b5b1 PD |
771 | error_setg_errno(errp, errno, "xengnttab_set_max_grants failed"); |
772 | } | |
773 | } | |
774 | ||
775 | void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, | |
776 | unsigned int nr_refs, int prot, | |
777 | Error **errp) | |
778 | { | |
c412ba47 DW |
779 | void *map = qemu_xen_gnttab_map_refs(xendev->xgth, nr_refs, |
780 | xendev->frontend_id, refs, prot); | |
4b34b5b1 PD |
781 | |
782 | if (!map) { | |
783 | error_setg_errno(errp, errno, | |
784 | "xengnttab_map_domain_grant_refs failed"); | |
785 | } | |
786 | ||
787 | return map; | |
788 | } | |
789 | ||
f80fad16 | 790 | void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, uint32_t *refs, |
4b34b5b1 PD |
791 | unsigned int nr_refs, Error **errp) |
792 | { | |
f80fad16 | 793 | if (qemu_xen_gnttab_unmap(xendev->xgth, map, refs, nr_refs)) { |
4b34b5b1 PD |
794 | error_setg_errno(errp, errno, "xengnttab_unmap failed"); |
795 | } | |
796 | } | |
797 | ||
4b34b5b1 PD |
798 | void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain, |
799 | XenDeviceGrantCopySegment segs[], | |
800 | unsigned int nr_segs, Error **errp) | |
801 | { | |
c412ba47 DW |
802 | qemu_xen_gnttab_grant_copy(xendev->xgth, to_domain, xendev->frontend_id, |
803 | (XenGrantCopySegment *)segs, nr_segs, errp); | |
4b34b5b1 PD |
804 | } |
805 | ||
a3d669c8 | 806 | struct XenEventChannel { |
c0b336ea | 807 | QLIST_ENTRY(XenEventChannel) list; |
83361a8a | 808 | AioContext *ctx; |
c0b336ea | 809 | xenevtchn_handle *xeh; |
a3d669c8 PD |
810 | evtchn_port_t local_port; |
811 | XenEventHandler handler; | |
812 | void *opaque; | |
a3d669c8 PD |
813 | }; |
814 | ||
345f42b4 PD |
815 | static bool xen_device_poll(void *opaque) |
816 | { | |
817 | XenEventChannel *channel = opaque; | |
818 | ||
819 | return channel->handler(channel->opaque); | |
820 | } | |
821 | ||
c0b336ea | 822 | static void xen_device_event(void *opaque) |
a3d669c8 | 823 | { |
c0b336ea | 824 | XenEventChannel *channel = opaque; |
b6cacfea | 825 | unsigned long port = qemu_xen_evtchn_pending(channel->xeh); |
a3d669c8 PD |
826 | |
827 | if (port == channel->local_port) { | |
345f42b4 | 828 | xen_device_poll(channel); |
c0b336ea | 829 | |
b6cacfea | 830 | qemu_xen_evtchn_unmask(channel->xeh, port); |
a3d669c8 PD |
831 | } |
832 | } | |
833 | ||
32d0b7be PD |
834 | void xen_device_set_event_channel_context(XenDevice *xendev, |
835 | XenEventChannel *channel, | |
836 | AioContext *ctx, | |
837 | Error **errp) | |
838 | { | |
839 | if (!channel) { | |
840 | error_setg(errp, "bad channel"); | |
841 | return; | |
842 | } | |
843 | ||
844 | if (channel->ctx) | |
60f782b6 | 845 | aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), |
826cc324 | 846 | NULL, NULL, NULL, NULL, NULL); |
32d0b7be PD |
847 | |
848 | channel->ctx = ctx; | |
f6eac904 SH |
849 | if (ctx) { |
850 | aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), | |
60f782b6 SH |
851 | xen_device_event, NULL, xen_device_poll, NULL, |
852 | channel); | |
f6eac904 | 853 | } |
32d0b7be PD |
854 | } |
855 | ||
a3d669c8 PD |
856 | XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, |
857 | unsigned int port, | |
858 | XenEventHandler handler, | |
859 | void *opaque, Error **errp) | |
860 | { | |
861 | XenEventChannel *channel = g_new0(XenEventChannel, 1); | |
862 | xenevtchn_port_or_error_t local_port; | |
863 | ||
b6cacfea | 864 | channel->xeh = qemu_xen_evtchn_open(); |
c0b336ea PD |
865 | if (!channel->xeh) { |
866 | error_setg_errno(errp, errno, "failed xenevtchn_open"); | |
867 | goto fail; | |
868 | } | |
869 | ||
b6cacfea | 870 | local_port = qemu_xen_evtchn_bind_interdomain(channel->xeh, |
a3d669c8 PD |
871 | xendev->frontend_id, |
872 | port); | |
873 | if (local_port < 0) { | |
874 | error_setg_errno(errp, errno, "xenevtchn_bind_interdomain failed"); | |
c0b336ea | 875 | goto fail; |
a3d669c8 PD |
876 | } |
877 | ||
878 | channel->local_port = local_port; | |
879 | channel->handler = handler; | |
880 | channel->opaque = opaque; | |
a3d669c8 | 881 | |
32d0b7be PD |
882 | /* Only reason for failure is a NULL channel */ |
883 | xen_device_set_event_channel_context(xendev, channel, | |
884 | qemu_get_aio_context(), | |
885 | &error_abort); | |
c0b336ea PD |
886 | |
887 | QLIST_INSERT_HEAD(&xendev->event_channels, channel, list); | |
a3d669c8 PD |
888 | |
889 | return channel; | |
c0b336ea PD |
890 | |
891 | fail: | |
892 | if (channel->xeh) { | |
b6cacfea | 893 | qemu_xen_evtchn_close(channel->xeh); |
c0b336ea PD |
894 | } |
895 | ||
896 | g_free(channel); | |
897 | ||
898 | return NULL; | |
a3d669c8 PD |
899 | } |
900 | ||
901 | void xen_device_notify_event_channel(XenDevice *xendev, | |
902 | XenEventChannel *channel, | |
903 | Error **errp) | |
904 | { | |
905 | if (!channel) { | |
906 | error_setg(errp, "bad channel"); | |
907 | return; | |
908 | } | |
909 | ||
b6cacfea | 910 | if (qemu_xen_evtchn_notify(channel->xeh, channel->local_port) < 0) { |
a3d669c8 PD |
911 | error_setg_errno(errp, errno, "xenevtchn_notify failed"); |
912 | } | |
913 | } | |
914 | ||
915 | void xen_device_unbind_event_channel(XenDevice *xendev, | |
916 | XenEventChannel *channel, | |
917 | Error **errp) | |
918 | { | |
919 | if (!channel) { | |
920 | error_setg(errp, "bad channel"); | |
921 | return; | |
922 | } | |
923 | ||
c0b336ea | 924 | QLIST_REMOVE(channel, list); |
a3d669c8 | 925 | |
60f782b6 | 926 | aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), |
826cc324 | 927 | NULL, NULL, NULL, NULL, NULL); |
c0b336ea | 928 | |
b6cacfea | 929 | if (qemu_xen_evtchn_unbind(channel->xeh, channel->local_port) < 0) { |
a3d669c8 PD |
930 | error_setg_errno(errp, errno, "xenevtchn_unbind failed"); |
931 | } | |
932 | ||
b6cacfea | 933 | qemu_xen_evtchn_close(channel->xeh); |
a3d669c8 PD |
934 | g_free(channel); |
935 | } | |
936 | ||
b69c3c21 | 937 | static void xen_device_unrealize(DeviceState *dev) |
108f7bba PD |
938 | { |
939 | XenDevice *xendev = XEN_DEVICE(dev); | |
940 | XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); | |
941 | const char *type = object_get_typename(OBJECT(xendev)); | |
c0b336ea | 942 | XenEventChannel *channel, *next; |
108f7bba | 943 | |
094a2239 PD |
944 | if (!xendev->name) { |
945 | return; | |
946 | } | |
947 | ||
948 | trace_xen_device_unrealize(type, xendev->name); | |
949 | ||
950 | if (xendev->exit.notify) { | |
951 | qemu_remove_exit_notifier(&xendev->exit); | |
952 | xendev->exit.notify = NULL; | |
953 | } | |
108f7bba PD |
954 | |
955 | if (xendev_class->unrealize) { | |
b69c3c21 | 956 | xendev_class->unrealize(xendev); |
108f7bba | 957 | } |
094a2239 | 958 | |
c0b336ea PD |
959 | /* Make sure all event channels are cleaned up */ |
960 | QLIST_FOREACH_SAFE(channel, &xendev->event_channels, list, next) { | |
961 | xen_device_unbind_event_channel(xendev, channel, NULL); | |
962 | } | |
963 | ||
094a2239 PD |
964 | xen_device_frontend_destroy(xendev); |
965 | xen_device_backend_destroy(xendev); | |
966 | ||
4b34b5b1 | 967 | if (xendev->xgth) { |
c412ba47 | 968 | qemu_xen_gnttab_close(xendev->xgth); |
4b34b5b1 PD |
969 | xendev->xgth = NULL; |
970 | } | |
971 | ||
d198b711 | 972 | if (xendev->xsh) { |
ba2a92db | 973 | qemu_xen_xs_close(xendev->xsh); |
d198b711 PD |
974 | xendev->xsh = NULL; |
975 | } | |
976 | ||
094a2239 PD |
977 | g_free(xendev->name); |
978 | xendev->name = NULL; | |
979 | } | |
980 | ||
981 | static void xen_device_exit(Notifier *n, void *data) | |
982 | { | |
983 | XenDevice *xendev = container_of(n, XenDevice, exit); | |
984 | ||
b69c3c21 | 985 | xen_device_unrealize(DEVICE(xendev)); |
108f7bba PD |
986 | } |
987 | ||
988 | static void xen_device_realize(DeviceState *dev, Error **errp) | |
989 | { | |
1de7096d | 990 | ERRP_GUARD(); |
108f7bba PD |
991 | XenDevice *xendev = XEN_DEVICE(dev); |
992 | XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); | |
094a2239 | 993 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); |
108f7bba | 994 | const char *type = object_get_typename(OBJECT(xendev)); |
108f7bba | 995 | |
094a2239 PD |
996 | if (xendev->frontend_id == DOMID_INVALID) { |
997 | xendev->frontend_id = xen_domid; | |
998 | } | |
999 | ||
1000 | if (xendev->frontend_id >= DOMID_FIRST_RESERVED) { | |
1001 | error_setg(errp, "invalid frontend-id"); | |
1002 | goto unrealize; | |
1003 | } | |
1004 | ||
1005 | if (!xendev_class->get_name) { | |
1006 | error_setg(errp, "get_name method not implemented"); | |
1007 | goto unrealize; | |
1008 | } | |
1009 | ||
1de7096d VSO |
1010 | xendev->name = xendev_class->get_name(xendev, errp); |
1011 | if (*errp) { | |
1012 | error_prepend(errp, "failed to get device name: "); | |
094a2239 PD |
1013 | goto unrealize; |
1014 | } | |
1015 | ||
1016 | trace_xen_device_realize(type, xendev->name); | |
1017 | ||
ba2a92db | 1018 | xendev->xsh = qemu_xen_xs_open(); |
d198b711 PD |
1019 | if (!xendev->xsh) { |
1020 | error_setg_errno(errp, errno, "failed xs_open"); | |
1021 | goto unrealize; | |
1022 | } | |
1023 | ||
c412ba47 | 1024 | xendev->xgth = qemu_xen_gnttab_open(); |
4b34b5b1 PD |
1025 | if (!xendev->xgth) { |
1026 | error_setg_errno(errp, errno, "failed xengnttab_open"); | |
1027 | goto unrealize; | |
1028 | } | |
1029 | ||
1de7096d VSO |
1030 | xen_device_backend_create(xendev, errp); |
1031 | if (*errp) { | |
094a2239 PD |
1032 | goto unrealize; |
1033 | } | |
1034 | ||
1de7096d VSO |
1035 | xen_device_frontend_create(xendev, errp); |
1036 | if (*errp) { | |
094a2239 PD |
1037 | goto unrealize; |
1038 | } | |
108f7bba | 1039 | |
094a2239 PD |
1040 | xen_device_backend_printf(xendev, "frontend", "%s", |
1041 | xendev->frontend_path); | |
1042 | xen_device_backend_printf(xendev, "frontend-id", "%u", | |
1043 | xendev->frontend_id); | |
094a2239 PD |
1044 | xen_device_backend_printf(xendev, "hotplug-status", "connected"); |
1045 | ||
b6af8926 | 1046 | xen_device_backend_set_online(xendev, true); |
094a2239 PD |
1047 | xen_device_backend_set_state(xendev, XenbusStateInitWait); |
1048 | ||
6bd6b955 MS |
1049 | if (!xen_device_frontend_exists(xendev)) { |
1050 | xen_device_frontend_printf(xendev, "backend", "%s", | |
1051 | xendev->backend_path); | |
1052 | xen_device_frontend_printf(xendev, "backend-id", "%u", | |
1053 | xenbus->backend_id); | |
094a2239 | 1054 | |
6bd6b955 MS |
1055 | xen_device_frontend_set_state(xendev, XenbusStateInitialising, true); |
1056 | } | |
094a2239 | 1057 | |
240cc113 PD |
1058 | if (xendev_class->realize) { |
1059 | xendev_class->realize(xendev, errp); | |
1060 | if (*errp) { | |
1061 | goto unrealize; | |
1062 | } | |
1063 | } | |
1064 | ||
094a2239 PD |
1065 | xendev->exit.notify = xen_device_exit; |
1066 | qemu_add_exit_notifier(&xendev->exit); | |
108f7bba PD |
1067 | return; |
1068 | ||
1069 | unrealize: | |
b69c3c21 | 1070 | xen_device_unrealize(dev); |
108f7bba PD |
1071 | } |
1072 | ||
094a2239 PD |
1073 | static Property xen_device_props[] = { |
1074 | DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id, | |
1075 | DOMID_INVALID), | |
1076 | DEFINE_PROP_END_OF_LIST() | |
1077 | }; | |
1078 | ||
108f7bba PD |
1079 | static void xen_device_class_init(ObjectClass *class, void *data) |
1080 | { | |
1081 | DeviceClass *dev_class = DEVICE_CLASS(class); | |
1082 | ||
1083 | dev_class->realize = xen_device_realize; | |
1084 | dev_class->unrealize = xen_device_unrealize; | |
4f67d30b | 1085 | device_class_set_props(dev_class, xen_device_props); |
108f7bba PD |
1086 | dev_class->bus_type = TYPE_XEN_BUS; |
1087 | } | |
1088 | ||
1089 | static const TypeInfo xen_device_type_info = { | |
1090 | .name = TYPE_XEN_DEVICE, | |
1091 | .parent = TYPE_DEVICE, | |
1092 | .instance_size = sizeof(XenDevice), | |
1093 | .abstract = true, | |
1094 | .class_size = sizeof(XenDeviceClass), | |
1095 | .class_init = xen_device_class_init, | |
1096 | }; | |
1097 | ||
1098 | typedef struct XenBridge { | |
1099 | SysBusDevice busdev; | |
1100 | } XenBridge; | |
1101 | ||
1102 | #define TYPE_XEN_BRIDGE "xen-bridge" | |
1103 | ||
1104 | static const TypeInfo xen_bridge_type_info = { | |
1105 | .name = TYPE_XEN_BRIDGE, | |
1106 | .parent = TYPE_SYS_BUS_DEVICE, | |
1107 | .instance_size = sizeof(XenBridge), | |
1108 | }; | |
1109 | ||
1110 | static void xen_register_types(void) | |
1111 | { | |
1112 | type_register_static(&xen_bridge_type_info); | |
1113 | type_register_static(&xen_bus_type_info); | |
1114 | type_register_static(&xen_device_type_info); | |
1115 | } | |
1116 | ||
1117 | type_init(xen_register_types) | |
1118 | ||
1119 | void xen_bus_init(void) | |
1120 | { | |
3e80f690 | 1121 | DeviceState *dev = qdev_new(TYPE_XEN_BRIDGE); |
9388d170 | 1122 | BusState *bus = qbus_new(TYPE_XEN_BUS, dev, NULL); |
108f7bba | 1123 | |
3c6ef471 | 1124 | sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); |
cd7c8660 | 1125 | qbus_set_bus_hotplug_handler(bus); |
108f7bba | 1126 | } |