]>
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 PD |
9 | #include "qemu/main-loop.h" |
10 | #include "qemu/uuid.h" | |
108f7bba PD |
11 | #include "hw/hw.h" |
12 | #include "hw/sysbus.h" | |
094a2239 | 13 | #include "hw/xen/xen.h" |
108f7bba | 14 | #include "hw/xen/xen-bus.h" |
094a2239 PD |
15 | #include "hw/xen/xen-bus-helper.h" |
16 | #include "monitor/monitor.h" | |
108f7bba | 17 | #include "qapi/error.h" |
094a2239 | 18 | #include "sysemu/sysemu.h" |
108f7bba PD |
19 | #include "trace.h" |
20 | ||
094a2239 PD |
21 | static char *xen_device_get_backend_path(XenDevice *xendev) |
22 | { | |
23 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
24 | XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); | |
25 | const char *type = object_get_typename(OBJECT(xendev)); | |
26 | const char *backend = xendev_class->backend; | |
27 | ||
28 | if (!backend) { | |
29 | backend = type; | |
30 | } | |
31 | ||
32 | return g_strdup_printf("/local/domain/%u/backend/%s/%u/%s", | |
33 | xenbus->backend_id, backend, xendev->frontend_id, | |
34 | xendev->name); | |
35 | } | |
36 | ||
37 | static char *xen_device_get_frontend_path(XenDevice *xendev) | |
38 | { | |
39 | XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); | |
40 | const char *type = object_get_typename(OBJECT(xendev)); | |
41 | const char *device = xendev_class->device; | |
42 | ||
43 | if (!device) { | |
44 | device = type; | |
45 | } | |
46 | ||
47 | return g_strdup_printf("/local/domain/%u/device/%s/%s", | |
48 | xendev->frontend_id, device, xendev->name); | |
49 | } | |
50 | ||
51 | static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent) | |
52 | { | |
53 | XenDevice *xendev = XEN_DEVICE(dev); | |
54 | ||
55 | monitor_printf(mon, "%*sname = '%s' frontend_id = %u\n", | |
56 | indent, "", xendev->name, xendev->frontend_id); | |
57 | } | |
58 | ||
59 | static char *xen_bus_get_dev_path(DeviceState *dev) | |
60 | { | |
61 | return xen_device_get_backend_path(XEN_DEVICE(dev)); | |
62 | } | |
63 | ||
82a29e30 PD |
64 | struct XenWatch { |
65 | char *node, *key; | |
66 | char *token; | |
67 | XenWatchHandler handler; | |
68 | void *opaque; | |
69 | Notifier notifier; | |
70 | }; | |
71 | ||
72 | static void watch_notify(Notifier *n, void *data) | |
73 | { | |
74 | XenWatch *watch = container_of(n, XenWatch, notifier); | |
75 | const char *token = data; | |
76 | ||
77 | if (!strcmp(watch->token, token)) { | |
78 | watch->handler(watch->opaque); | |
79 | } | |
80 | } | |
81 | ||
82 | static XenWatch *new_watch(const char *node, const char *key, | |
83 | XenWatchHandler handler, void *opaque) | |
84 | { | |
85 | XenWatch *watch = g_new0(XenWatch, 1); | |
86 | QemuUUID uuid; | |
87 | ||
88 | qemu_uuid_generate(&uuid); | |
89 | ||
90 | watch->token = qemu_uuid_unparse_strdup(&uuid); | |
91 | watch->node = g_strdup(node); | |
92 | watch->key = g_strdup(key); | |
93 | watch->handler = handler; | |
94 | watch->opaque = opaque; | |
95 | watch->notifier.notify = watch_notify; | |
96 | ||
97 | return watch; | |
98 | } | |
99 | ||
100 | static void free_watch(XenWatch *watch) | |
101 | { | |
102 | g_free(watch->token); | |
103 | g_free(watch->key); | |
104 | g_free(watch->node); | |
105 | ||
106 | g_free(watch); | |
107 | } | |
108 | ||
109 | static XenWatch *xen_bus_add_watch(XenBus *xenbus, const char *node, | |
110 | const char *key, XenWatchHandler handler, | |
111 | void *opaque, Error **errp) | |
112 | { | |
113 | XenWatch *watch = new_watch(node, key, handler, opaque); | |
114 | Error *local_err = NULL; | |
115 | ||
116 | trace_xen_bus_add_watch(watch->node, watch->key, watch->token); | |
117 | ||
118 | notifier_list_add(&xenbus->watch_notifiers, &watch->notifier); | |
119 | ||
120 | xs_node_watch(xenbus->xsh, node, key, watch->token, &local_err); | |
121 | if (local_err) { | |
122 | error_propagate(errp, local_err); | |
123 | ||
124 | notifier_remove(&watch->notifier); | |
125 | free_watch(watch); | |
126 | ||
127 | return NULL; | |
128 | } | |
129 | ||
130 | return watch; | |
131 | } | |
132 | ||
133 | static void xen_bus_remove_watch(XenBus *xenbus, XenWatch *watch, | |
134 | Error **errp) | |
135 | { | |
136 | trace_xen_bus_remove_watch(watch->node, watch->key, watch->token); | |
137 | ||
138 | xs_node_unwatch(xenbus->xsh, watch->node, watch->key, watch->token, | |
139 | errp); | |
140 | ||
141 | notifier_remove(&watch->notifier); | |
142 | free_watch(watch); | |
143 | } | |
144 | ||
108f7bba PD |
145 | static void xen_bus_unrealize(BusState *bus, Error **errp) |
146 | { | |
094a2239 PD |
147 | XenBus *xenbus = XEN_BUS(bus); |
148 | ||
108f7bba | 149 | trace_xen_bus_unrealize(); |
094a2239 PD |
150 | |
151 | if (!xenbus->xsh) { | |
152 | return; | |
153 | } | |
154 | ||
82a29e30 PD |
155 | qemu_set_fd_handler(xs_fileno(xenbus->xsh), NULL, NULL, NULL); |
156 | ||
094a2239 | 157 | xs_close(xenbus->xsh); |
108f7bba PD |
158 | } |
159 | ||
82a29e30 PD |
160 | static void xen_bus_watch(void *opaque) |
161 | { | |
162 | XenBus *xenbus = opaque; | |
163 | char **v; | |
164 | const char *token; | |
165 | ||
166 | g_assert(xenbus->xsh); | |
167 | ||
168 | v = xs_check_watch(xenbus->xsh); | |
169 | if (!v) { | |
170 | return; | |
171 | } | |
172 | ||
173 | token = v[XS_WATCH_TOKEN]; | |
174 | ||
175 | trace_xen_bus_watch(token); | |
176 | ||
177 | notifier_list_notify(&xenbus->watch_notifiers, (void *)token); | |
178 | ||
179 | free(v); | |
180 | } | |
181 | ||
108f7bba PD |
182 | static void xen_bus_realize(BusState *bus, Error **errp) |
183 | { | |
094a2239 PD |
184 | XenBus *xenbus = XEN_BUS(bus); |
185 | unsigned int domid; | |
186 | ||
108f7bba | 187 | trace_xen_bus_realize(); |
094a2239 PD |
188 | |
189 | xenbus->xsh = xs_open(0); | |
190 | if (!xenbus->xsh) { | |
191 | error_setg_errno(errp, errno, "failed xs_open"); | |
192 | goto fail; | |
193 | } | |
194 | ||
195 | if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */ | |
196 | "domid", NULL, "%u", &domid) == 1) { | |
197 | xenbus->backend_id = domid; | |
198 | } else { | |
199 | xenbus->backend_id = 0; /* Assume lack of node means dom0 */ | |
200 | } | |
201 | ||
82a29e30 PD |
202 | notifier_list_init(&xenbus->watch_notifiers); |
203 | qemu_set_fd_handler(xs_fileno(xenbus->xsh), xen_bus_watch, NULL, | |
204 | xenbus); | |
094a2239 PD |
205 | return; |
206 | ||
207 | fail: | |
208 | xen_bus_unrealize(bus, &error_abort); | |
108f7bba PD |
209 | } |
210 | ||
211 | static void xen_bus_class_init(ObjectClass *class, void *data) | |
212 | { | |
213 | BusClass *bus_class = BUS_CLASS(class); | |
214 | ||
094a2239 PD |
215 | bus_class->print_dev = xen_bus_print_dev; |
216 | bus_class->get_dev_path = xen_bus_get_dev_path; | |
108f7bba PD |
217 | bus_class->realize = xen_bus_realize; |
218 | bus_class->unrealize = xen_bus_unrealize; | |
219 | } | |
220 | ||
221 | static const TypeInfo xen_bus_type_info = { | |
222 | .name = TYPE_XEN_BUS, | |
223 | .parent = TYPE_BUS, | |
224 | .instance_size = sizeof(XenBus), | |
225 | .class_size = sizeof(XenBusClass), | |
226 | .class_init = xen_bus_class_init, | |
227 | .interfaces = (InterfaceInfo[]) { | |
228 | { TYPE_HOTPLUG_HANDLER }, | |
229 | { } | |
230 | }, | |
231 | }; | |
232 | ||
094a2239 PD |
233 | static void xen_device_backend_printf(XenDevice *xendev, const char *key, |
234 | const char *fmt, ...) | |
235 | { | |
236 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
237 | Error *local_err = NULL; | |
238 | va_list ap; | |
239 | ||
240 | g_assert(xenbus->xsh); | |
241 | ||
242 | va_start(ap, fmt); | |
243 | xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, | |
244 | &local_err, fmt, ap); | |
245 | va_end(ap); | |
246 | ||
247 | if (local_err) { | |
248 | error_report_err(local_err); | |
249 | } | |
250 | } | |
251 | ||
82a29e30 PD |
252 | static int xen_device_backend_scanf(XenDevice *xendev, const char *key, |
253 | const char *fmt, ...) | |
254 | { | |
255 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
256 | va_list ap; | |
257 | int rc; | |
258 | ||
259 | g_assert(xenbus->xsh); | |
260 | ||
261 | va_start(ap, fmt); | |
262 | rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, | |
263 | NULL, fmt, ap); | |
264 | va_end(ap); | |
265 | ||
266 | return rc; | |
267 | } | |
268 | ||
269 | void xen_device_backend_set_state(XenDevice *xendev, | |
270 | enum xenbus_state state) | |
094a2239 PD |
271 | { |
272 | const char *type = object_get_typename(OBJECT(xendev)); | |
273 | ||
274 | if (xendev->backend_state == state) { | |
275 | return; | |
276 | } | |
277 | ||
278 | trace_xen_device_backend_state(type, xendev->name, | |
279 | xs_strstate(state)); | |
280 | ||
281 | xendev->backend_state = state; | |
282 | xen_device_backend_printf(xendev, "state", "%u", state); | |
283 | } | |
284 | ||
82a29e30 PD |
285 | enum xenbus_state xen_device_backend_get_state(XenDevice *xendev) |
286 | { | |
287 | return xendev->backend_state; | |
288 | } | |
289 | ||
094a2239 PD |
290 | static void xen_device_backend_create(XenDevice *xendev, Error **errp) |
291 | { | |
292 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
293 | struct xs_permissions perms[2]; | |
294 | Error *local_err = NULL; | |
295 | ||
296 | xendev->backend_path = xen_device_get_backend_path(xendev); | |
297 | ||
298 | perms[0].id = xenbus->backend_id; | |
299 | perms[0].perms = XS_PERM_NONE; | |
300 | perms[1].id = xendev->frontend_id; | |
301 | perms[1].perms = XS_PERM_READ; | |
302 | ||
303 | g_assert(xenbus->xsh); | |
304 | ||
305 | xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, perms, | |
306 | ARRAY_SIZE(perms), &local_err); | |
307 | if (local_err) { | |
308 | error_propagate_prepend(errp, local_err, | |
309 | "failed to create backend: "); | |
310 | } | |
311 | } | |
312 | ||
313 | static void xen_device_backend_destroy(XenDevice *xendev) | |
314 | { | |
315 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
316 | Error *local_err = NULL; | |
317 | ||
318 | if (!xendev->backend_path) { | |
319 | return; | |
320 | } | |
321 | ||
322 | g_assert(xenbus->xsh); | |
323 | ||
324 | xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->backend_path, | |
325 | &local_err); | |
326 | g_free(xendev->backend_path); | |
327 | xendev->backend_path = NULL; | |
328 | ||
329 | if (local_err) { | |
330 | error_report_err(local_err); | |
331 | } | |
332 | } | |
333 | ||
334 | static void xen_device_frontend_printf(XenDevice *xendev, const char *key, | |
335 | const char *fmt, ...) | |
336 | { | |
337 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
338 | Error *local_err = NULL; | |
339 | va_list ap; | |
340 | ||
341 | g_assert(xenbus->xsh); | |
342 | ||
343 | va_start(ap, fmt); | |
344 | xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, | |
345 | &local_err, fmt, ap); | |
346 | va_end(ap); | |
347 | ||
348 | if (local_err) { | |
349 | error_report_err(local_err); | |
350 | } | |
351 | } | |
352 | ||
82a29e30 PD |
353 | static int xen_device_frontend_scanf(XenDevice *xendev, const char *key, |
354 | const char *fmt, ...) | |
355 | { | |
356 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
357 | va_list ap; | |
358 | int rc; | |
359 | ||
360 | g_assert(xenbus->xsh); | |
361 | ||
362 | va_start(ap, fmt); | |
363 | rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, | |
364 | NULL, fmt, ap); | |
365 | va_end(ap); | |
366 | ||
367 | return rc; | |
368 | } | |
369 | ||
094a2239 PD |
370 | static void xen_device_frontend_set_state(XenDevice *xendev, |
371 | enum xenbus_state state) | |
372 | { | |
373 | const char *type = object_get_typename(OBJECT(xendev)); | |
374 | ||
375 | if (xendev->frontend_state == state) { | |
376 | return; | |
377 | } | |
378 | ||
379 | trace_xen_device_frontend_state(type, xendev->name, | |
380 | xs_strstate(state)); | |
381 | ||
382 | xendev->frontend_state = state; | |
383 | xen_device_frontend_printf(xendev, "state", "%u", state); | |
384 | } | |
385 | ||
82a29e30 PD |
386 | static void xen_device_frontend_changed(void *opaque) |
387 | { | |
388 | XenDevice *xendev = opaque; | |
389 | XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); | |
390 | const char *type = object_get_typename(OBJECT(xendev)); | |
391 | enum xenbus_state state; | |
392 | ||
393 | trace_xen_device_frontend_changed(type, xendev->name); | |
394 | ||
395 | if (xen_device_frontend_scanf(xendev, "state", "%u", &state) != 1) { | |
396 | state = XenbusStateUnknown; | |
397 | } | |
398 | ||
399 | xen_device_frontend_set_state(xendev, state); | |
400 | ||
401 | if (xendev_class->frontend_changed) { | |
402 | Error *local_err = NULL; | |
403 | ||
404 | xendev_class->frontend_changed(xendev, state, &local_err); | |
405 | ||
406 | if (local_err) { | |
407 | error_reportf_err(local_err, "frontend change error: "); | |
408 | } | |
409 | } | |
410 | ||
411 | /* | |
412 | * If a backend is still 'online' then its state should be cycled | |
413 | * back round to InitWait in order for a new frontend instance to | |
414 | * connect. This may happen when, for example, a frontend driver is | |
415 | * re-installed or updated. | |
416 | */ | |
417 | if (xendev->backend_state == XenbusStateClosed) { | |
418 | unsigned int online; | |
419 | ||
420 | if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) { | |
421 | online = 0; | |
422 | } | |
423 | ||
424 | if (online) { | |
425 | xen_device_backend_set_state(xendev, XenbusStateInitWait); | |
426 | } | |
427 | } | |
428 | } | |
429 | ||
094a2239 PD |
430 | static void xen_device_frontend_create(XenDevice *xendev, Error **errp) |
431 | { | |
432 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
433 | struct xs_permissions perms[2]; | |
434 | Error *local_err = NULL; | |
435 | ||
436 | xendev->frontend_path = xen_device_get_frontend_path(xendev); | |
437 | ||
438 | perms[0].id = xendev->frontend_id; | |
439 | perms[0].perms = XS_PERM_NONE; | |
440 | perms[1].id = xenbus->backend_id; | |
441 | perms[1].perms = XS_PERM_READ | XS_PERM_WRITE; | |
442 | ||
443 | g_assert(xenbus->xsh); | |
444 | ||
445 | xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, perms, | |
446 | ARRAY_SIZE(perms), &local_err); | |
447 | if (local_err) { | |
448 | error_propagate_prepend(errp, local_err, | |
449 | "failed to create frontend: "); | |
82a29e30 PD |
450 | return; |
451 | } | |
452 | ||
453 | xendev->frontend_state_watch = | |
454 | xen_bus_add_watch(xenbus, xendev->frontend_path, "state", | |
455 | xen_device_frontend_changed, xendev, &local_err); | |
456 | if (local_err) { | |
457 | error_propagate_prepend(errp, local_err, | |
458 | "failed to watch frontend state: "); | |
094a2239 PD |
459 | } |
460 | } | |
461 | ||
462 | static void xen_device_frontend_destroy(XenDevice *xendev) | |
463 | { | |
464 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); | |
465 | Error *local_err = NULL; | |
466 | ||
82a29e30 PD |
467 | if (xendev->frontend_state_watch) { |
468 | xen_bus_remove_watch(xenbus, xendev->frontend_state_watch, NULL); | |
469 | xendev->frontend_state_watch = NULL; | |
470 | } | |
471 | ||
094a2239 PD |
472 | if (!xendev->frontend_path) { |
473 | return; | |
474 | } | |
475 | ||
476 | g_assert(xenbus->xsh); | |
477 | ||
478 | xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->frontend_path, | |
479 | &local_err); | |
480 | g_free(xendev->frontend_path); | |
481 | xendev->frontend_path = NULL; | |
482 | ||
483 | if (local_err) { | |
484 | error_report_err(local_err); | |
485 | } | |
486 | } | |
487 | ||
4b34b5b1 PD |
488 | void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, |
489 | Error **errp) | |
490 | { | |
491 | if (xengnttab_set_max_grants(xendev->xgth, nr_refs)) { | |
492 | error_setg_errno(errp, errno, "xengnttab_set_max_grants failed"); | |
493 | } | |
494 | } | |
495 | ||
496 | void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, | |
497 | unsigned int nr_refs, int prot, | |
498 | Error **errp) | |
499 | { | |
500 | void *map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_refs, | |
501 | xendev->frontend_id, refs, | |
502 | prot); | |
503 | ||
504 | if (!map) { | |
505 | error_setg_errno(errp, errno, | |
506 | "xengnttab_map_domain_grant_refs failed"); | |
507 | } | |
508 | ||
509 | return map; | |
510 | } | |
511 | ||
512 | void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, | |
513 | unsigned int nr_refs, Error **errp) | |
514 | { | |
515 | if (xengnttab_unmap(xendev->xgth, map, nr_refs)) { | |
516 | error_setg_errno(errp, errno, "xengnttab_unmap failed"); | |
517 | } | |
518 | } | |
519 | ||
520 | static void compat_copy_grant_refs(XenDevice *xendev, bool to_domain, | |
521 | XenDeviceGrantCopySegment segs[], | |
522 | unsigned int nr_segs, Error **errp) | |
523 | { | |
524 | uint32_t *refs = g_new(uint32_t, nr_segs); | |
525 | int prot = to_domain ? PROT_WRITE : PROT_READ; | |
526 | void *map; | |
527 | unsigned int i; | |
528 | ||
529 | for (i = 0; i < nr_segs; i++) { | |
530 | XenDeviceGrantCopySegment *seg = &segs[i]; | |
531 | ||
532 | refs[i] = to_domain ? seg->dest.foreign.ref : | |
533 | seg->source.foreign.ref; | |
534 | } | |
535 | ||
536 | map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_segs, | |
537 | xendev->frontend_id, refs, | |
538 | prot); | |
539 | if (!map) { | |
540 | error_setg_errno(errp, errno, | |
541 | "xengnttab_map_domain_grant_refs failed"); | |
542 | goto done; | |
543 | } | |
544 | ||
545 | for (i = 0; i < nr_segs; i++) { | |
546 | XenDeviceGrantCopySegment *seg = &segs[i]; | |
547 | void *page = map + (i * XC_PAGE_SIZE); | |
548 | ||
549 | if (to_domain) { | |
550 | memcpy(page + seg->dest.foreign.offset, seg->source.virt, | |
551 | seg->len); | |
552 | } else { | |
553 | memcpy(seg->dest.virt, page + seg->source.foreign.offset, | |
554 | seg->len); | |
555 | } | |
556 | } | |
557 | ||
558 | if (xengnttab_unmap(xendev->xgth, map, nr_segs)) { | |
559 | error_setg_errno(errp, errno, "xengnttab_unmap failed"); | |
560 | } | |
561 | ||
562 | done: | |
563 | g_free(refs); | |
564 | } | |
565 | ||
566 | void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain, | |
567 | XenDeviceGrantCopySegment segs[], | |
568 | unsigned int nr_segs, Error **errp) | |
569 | { | |
570 | xengnttab_grant_copy_segment_t *xengnttab_segs; | |
571 | unsigned int i; | |
572 | ||
573 | if (!xendev->feature_grant_copy) { | |
574 | compat_copy_grant_refs(xendev, to_domain, segs, nr_segs, errp); | |
575 | return; | |
576 | } | |
577 | ||
578 | xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); | |
579 | ||
580 | for (i = 0; i < nr_segs; i++) { | |
581 | XenDeviceGrantCopySegment *seg = &segs[i]; | |
582 | xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; | |
583 | ||
584 | if (to_domain) { | |
585 | xengnttab_seg->flags = GNTCOPY_dest_gref; | |
586 | xengnttab_seg->dest.foreign.domid = xendev->frontend_id; | |
587 | xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; | |
588 | xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; | |
589 | xengnttab_seg->source.virt = seg->source.virt; | |
590 | } else { | |
591 | xengnttab_seg->flags = GNTCOPY_source_gref; | |
592 | xengnttab_seg->source.foreign.domid = xendev->frontend_id; | |
593 | xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; | |
594 | xengnttab_seg->source.foreign.offset = | |
595 | seg->source.foreign.offset; | |
596 | xengnttab_seg->dest.virt = seg->dest.virt; | |
597 | } | |
598 | ||
599 | xengnttab_seg->len = seg->len; | |
600 | } | |
601 | ||
602 | if (xengnttab_grant_copy(xendev->xgth, nr_segs, xengnttab_segs)) { | |
603 | error_setg_errno(errp, errno, "xengnttab_grant_copy failed"); | |
604 | goto done; | |
605 | } | |
606 | ||
607 | for (i = 0; i < nr_segs; i++) { | |
608 | xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; | |
609 | ||
610 | if (xengnttab_seg->status != GNTST_okay) { | |
611 | error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i); | |
612 | break; | |
613 | } | |
614 | } | |
615 | ||
616 | done: | |
617 | g_free(xengnttab_segs); | |
618 | } | |
619 | ||
a3d669c8 PD |
620 | struct XenEventChannel { |
621 | evtchn_port_t local_port; | |
622 | XenEventHandler handler; | |
623 | void *opaque; | |
624 | Notifier notifier; | |
625 | }; | |
626 | ||
627 | static void event_notify(Notifier *n, void *data) | |
628 | { | |
629 | XenEventChannel *channel = container_of(n, XenEventChannel, notifier); | |
630 | unsigned long port = (unsigned long)data; | |
631 | ||
632 | if (port == channel->local_port) { | |
633 | channel->handler(channel->opaque); | |
634 | } | |
635 | } | |
636 | ||
637 | XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, | |
638 | unsigned int port, | |
639 | XenEventHandler handler, | |
640 | void *opaque, Error **errp) | |
641 | { | |
642 | XenEventChannel *channel = g_new0(XenEventChannel, 1); | |
643 | xenevtchn_port_or_error_t local_port; | |
644 | ||
645 | local_port = xenevtchn_bind_interdomain(xendev->xeh, | |
646 | xendev->frontend_id, | |
647 | port); | |
648 | if (local_port < 0) { | |
649 | error_setg_errno(errp, errno, "xenevtchn_bind_interdomain failed"); | |
650 | ||
651 | g_free(channel); | |
652 | return NULL; | |
653 | } | |
654 | ||
655 | channel->local_port = local_port; | |
656 | channel->handler = handler; | |
657 | channel->opaque = opaque; | |
658 | channel->notifier.notify = event_notify; | |
659 | ||
660 | notifier_list_add(&xendev->event_notifiers, &channel->notifier); | |
661 | ||
662 | return channel; | |
663 | } | |
664 | ||
665 | void xen_device_notify_event_channel(XenDevice *xendev, | |
666 | XenEventChannel *channel, | |
667 | Error **errp) | |
668 | { | |
669 | if (!channel) { | |
670 | error_setg(errp, "bad channel"); | |
671 | return; | |
672 | } | |
673 | ||
674 | if (xenevtchn_notify(xendev->xeh, channel->local_port) < 0) { | |
675 | error_setg_errno(errp, errno, "xenevtchn_notify failed"); | |
676 | } | |
677 | } | |
678 | ||
679 | void xen_device_unbind_event_channel(XenDevice *xendev, | |
680 | XenEventChannel *channel, | |
681 | Error **errp) | |
682 | { | |
683 | if (!channel) { | |
684 | error_setg(errp, "bad channel"); | |
685 | return; | |
686 | } | |
687 | ||
688 | notifier_remove(&channel->notifier); | |
689 | ||
690 | if (xenevtchn_unbind(xendev->xeh, channel->local_port) < 0) { | |
691 | error_setg_errno(errp, errno, "xenevtchn_unbind failed"); | |
692 | } | |
693 | ||
694 | g_free(channel); | |
695 | } | |
696 | ||
108f7bba PD |
697 | static void xen_device_unrealize(DeviceState *dev, Error **errp) |
698 | { | |
699 | XenDevice *xendev = XEN_DEVICE(dev); | |
700 | XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); | |
701 | const char *type = object_get_typename(OBJECT(xendev)); | |
702 | ||
094a2239 PD |
703 | if (!xendev->name) { |
704 | return; | |
705 | } | |
706 | ||
707 | trace_xen_device_unrealize(type, xendev->name); | |
708 | ||
709 | if (xendev->exit.notify) { | |
710 | qemu_remove_exit_notifier(&xendev->exit); | |
711 | xendev->exit.notify = NULL; | |
712 | } | |
108f7bba PD |
713 | |
714 | if (xendev_class->unrealize) { | |
715 | xendev_class->unrealize(xendev, errp); | |
716 | } | |
094a2239 PD |
717 | |
718 | xen_device_frontend_destroy(xendev); | |
719 | xen_device_backend_destroy(xendev); | |
720 | ||
a3d669c8 PD |
721 | if (xendev->xeh) { |
722 | qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), NULL, NULL, NULL); | |
723 | xenevtchn_close(xendev->xeh); | |
724 | xendev->xeh = NULL; | |
725 | } | |
726 | ||
4b34b5b1 PD |
727 | if (xendev->xgth) { |
728 | xengnttab_close(xendev->xgth); | |
729 | xendev->xgth = NULL; | |
730 | } | |
731 | ||
094a2239 PD |
732 | g_free(xendev->name); |
733 | xendev->name = NULL; | |
734 | } | |
735 | ||
736 | static void xen_device_exit(Notifier *n, void *data) | |
737 | { | |
738 | XenDevice *xendev = container_of(n, XenDevice, exit); | |
739 | ||
740 | xen_device_unrealize(DEVICE(xendev), &error_abort); | |
108f7bba PD |
741 | } |
742 | ||
a3d669c8 PD |
743 | static void xen_device_event(void *opaque) |
744 | { | |
745 | XenDevice *xendev = opaque; | |
746 | unsigned long port = xenevtchn_pending(xendev->xeh); | |
747 | ||
748 | notifier_list_notify(&xendev->event_notifiers, (void *)port); | |
749 | ||
750 | xenevtchn_unmask(xendev->xeh, port); | |
751 | } | |
752 | ||
108f7bba PD |
753 | static void xen_device_realize(DeviceState *dev, Error **errp) |
754 | { | |
755 | XenDevice *xendev = XEN_DEVICE(dev); | |
756 | XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); | |
094a2239 | 757 | XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); |
108f7bba PD |
758 | const char *type = object_get_typename(OBJECT(xendev)); |
759 | Error *local_err = NULL; | |
760 | ||
094a2239 PD |
761 | if (xendev->frontend_id == DOMID_INVALID) { |
762 | xendev->frontend_id = xen_domid; | |
763 | } | |
764 | ||
765 | if (xendev->frontend_id >= DOMID_FIRST_RESERVED) { | |
766 | error_setg(errp, "invalid frontend-id"); | |
767 | goto unrealize; | |
768 | } | |
769 | ||
770 | if (!xendev_class->get_name) { | |
771 | error_setg(errp, "get_name method not implemented"); | |
772 | goto unrealize; | |
773 | } | |
774 | ||
775 | xendev->name = xendev_class->get_name(xendev, &local_err); | |
776 | if (local_err) { | |
777 | error_propagate_prepend(errp, local_err, | |
778 | "failed to get device name: "); | |
779 | goto unrealize; | |
780 | } | |
781 | ||
782 | trace_xen_device_realize(type, xendev->name); | |
783 | ||
4b34b5b1 PD |
784 | xendev->xgth = xengnttab_open(NULL, 0); |
785 | if (!xendev->xgth) { | |
786 | error_setg_errno(errp, errno, "failed xengnttab_open"); | |
787 | goto unrealize; | |
788 | } | |
789 | ||
790 | xendev->feature_grant_copy = | |
791 | (xengnttab_grant_copy(xendev->xgth, 0, NULL) == 0); | |
792 | ||
a3d669c8 PD |
793 | xendev->xeh = xenevtchn_open(NULL, 0); |
794 | if (!xendev->xeh) { | |
795 | error_setg_errno(errp, errno, "failed xenevtchn_open"); | |
796 | goto unrealize; | |
797 | } | |
798 | ||
799 | notifier_list_init(&xendev->event_notifiers); | |
800 | qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), xen_device_event, NULL, | |
801 | xendev); | |
802 | ||
094a2239 PD |
803 | xen_device_backend_create(xendev, &local_err); |
804 | if (local_err) { | |
805 | error_propagate(errp, local_err); | |
806 | goto unrealize; | |
807 | } | |
808 | ||
809 | xen_device_frontend_create(xendev, &local_err); | |
810 | if (local_err) { | |
811 | error_propagate(errp, local_err); | |
812 | goto unrealize; | |
813 | } | |
108f7bba PD |
814 | |
815 | if (xendev_class->realize) { | |
816 | xendev_class->realize(xendev, &local_err); | |
817 | if (local_err) { | |
818 | error_propagate(errp, local_err); | |
819 | goto unrealize; | |
820 | } | |
821 | } | |
822 | ||
094a2239 PD |
823 | xen_device_backend_printf(xendev, "frontend", "%s", |
824 | xendev->frontend_path); | |
825 | xen_device_backend_printf(xendev, "frontend-id", "%u", | |
826 | xendev->frontend_id); | |
827 | xen_device_backend_printf(xendev, "online", "%u", 1); | |
828 | xen_device_backend_printf(xendev, "hotplug-status", "connected"); | |
829 | ||
830 | xen_device_backend_set_state(xendev, XenbusStateInitWait); | |
831 | ||
832 | xen_device_frontend_printf(xendev, "backend", "%s", | |
833 | xendev->backend_path); | |
834 | xen_device_frontend_printf(xendev, "backend-id", "%u", | |
835 | xenbus->backend_id); | |
836 | ||
837 | xen_device_frontend_set_state(xendev, XenbusStateInitialising); | |
838 | ||
839 | xendev->exit.notify = xen_device_exit; | |
840 | qemu_add_exit_notifier(&xendev->exit); | |
108f7bba PD |
841 | return; |
842 | ||
843 | unrealize: | |
844 | xen_device_unrealize(dev, &error_abort); | |
845 | } | |
846 | ||
094a2239 PD |
847 | static Property xen_device_props[] = { |
848 | DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id, | |
849 | DOMID_INVALID), | |
850 | DEFINE_PROP_END_OF_LIST() | |
851 | }; | |
852 | ||
108f7bba PD |
853 | static void xen_device_class_init(ObjectClass *class, void *data) |
854 | { | |
855 | DeviceClass *dev_class = DEVICE_CLASS(class); | |
856 | ||
857 | dev_class->realize = xen_device_realize; | |
858 | dev_class->unrealize = xen_device_unrealize; | |
094a2239 | 859 | dev_class->props = xen_device_props; |
108f7bba PD |
860 | dev_class->bus_type = TYPE_XEN_BUS; |
861 | } | |
862 | ||
863 | static const TypeInfo xen_device_type_info = { | |
864 | .name = TYPE_XEN_DEVICE, | |
865 | .parent = TYPE_DEVICE, | |
866 | .instance_size = sizeof(XenDevice), | |
867 | .abstract = true, | |
868 | .class_size = sizeof(XenDeviceClass), | |
869 | .class_init = xen_device_class_init, | |
870 | }; | |
871 | ||
872 | typedef struct XenBridge { | |
873 | SysBusDevice busdev; | |
874 | } XenBridge; | |
875 | ||
876 | #define TYPE_XEN_BRIDGE "xen-bridge" | |
877 | ||
878 | static const TypeInfo xen_bridge_type_info = { | |
879 | .name = TYPE_XEN_BRIDGE, | |
880 | .parent = TYPE_SYS_BUS_DEVICE, | |
881 | .instance_size = sizeof(XenBridge), | |
882 | }; | |
883 | ||
884 | static void xen_register_types(void) | |
885 | { | |
886 | type_register_static(&xen_bridge_type_info); | |
887 | type_register_static(&xen_bus_type_info); | |
888 | type_register_static(&xen_device_type_info); | |
889 | } | |
890 | ||
891 | type_init(xen_register_types) | |
892 | ||
893 | void xen_bus_init(void) | |
894 | { | |
895 | DeviceState *dev = qdev_create(NULL, TYPE_XEN_BRIDGE); | |
896 | BusState *bus = qbus_create(TYPE_XEN_BUS, dev, NULL); | |
897 | ||
898 | qdev_init_nofail(dev); | |
899 | qbus_set_bus_hotplug_handler(bus, &error_abort); | |
900 | } |