]>
Commit | Line | Data |
---|---|---|
d94f9486 AL |
1 | /* |
2 | * xen backend driver infrastructure | |
3 | * (c) 2008 Gerd Hoffmann <[email protected]> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; under version 2 of the License. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along | |
8167ee88 | 15 | * with this program; if not, see <http://www.gnu.org/licenses/>. |
6b620ca3 PB |
16 | * |
17 | * Contributions after 2012-01-13 are licensed under the terms of the | |
18 | * GNU GPL, version 2 or (at your option) any later version. | |
d94f9486 AL |
19 | */ |
20 | ||
21 | /* | |
22 | * TODO: add some xenbus / xenstore concepts overview here. | |
23 | */ | |
24 | ||
21cbfe5f | 25 | #include "qemu/osdep.h" |
d94f9486 AL |
26 | #include <sys/signal.h> |
27 | ||
25f8f6b4 | 28 | #include "hw/sysbus.h" |
3a6c9172 | 29 | #include "hw/boards.h" |
1de7afc9 | 30 | #include "qemu/log.h" |
db725815 | 31 | #include "qemu/main-loop.h" |
873d57ab | 32 | #include "qapi/error.h" |
2d0ed5e6 | 33 | #include "hw/xen/xen-legacy-backend.h" |
f0021dba | 34 | #include "hw/xen/xen_pvdev.h" |
3a6c9172 | 35 | #include "monitor/qdev.h" |
d94f9486 | 36 | |
25f8f6b4 | 37 | DeviceState *xen_sysdev; |
873d57ab | 38 | BusState *xen_sysbus; |
25f8f6b4 | 39 | |
d94f9486 AL |
40 | /* ------------------------------------------------------------- */ |
41 | ||
42 | /* public */ | |
2d0ed5e6 | 43 | struct xs_handle *xenstore; |
2c8b24a3 | 44 | const char *xen_protocol; |
d94f9486 AL |
45 | |
46 | /* private */ | |
a68bf540 | 47 | static bool xen_feature_grant_copy; |
c22e91b1 | 48 | static int debug; |
d94f9486 | 49 | |
2d0ed5e6 PD |
50 | int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node, |
51 | const char *val) | |
d94f9486 AL |
52 | { |
53 | return xenstore_write_str(xendev->be, node, val); | |
54 | } | |
55 | ||
2d0ed5e6 PD |
56 | int xenstore_write_be_int(struct XenLegacyDevice *xendev, const char *node, |
57 | int ival) | |
d94f9486 AL |
58 | { |
59 | return xenstore_write_int(xendev->be, node, ival); | |
60 | } | |
61 | ||
2d0ed5e6 PD |
62 | int xenstore_write_be_int64(struct XenLegacyDevice *xendev, const char *node, |
63 | int64_t ival) | |
10bb3c62 FF |
64 | { |
65 | return xenstore_write_int64(xendev->be, node, ival); | |
66 | } | |
67 | ||
2d0ed5e6 | 68 | char *xenstore_read_be_str(struct XenLegacyDevice *xendev, const char *node) |
d94f9486 AL |
69 | { |
70 | return xenstore_read_str(xendev->be, node); | |
71 | } | |
72 | ||
2d0ed5e6 PD |
73 | int xenstore_read_be_int(struct XenLegacyDevice *xendev, const char *node, |
74 | int *ival) | |
d94f9486 AL |
75 | { |
76 | return xenstore_read_int(xendev->be, node, ival); | |
77 | } | |
78 | ||
2d0ed5e6 | 79 | char *xenstore_read_fe_str(struct XenLegacyDevice *xendev, const char *node) |
d94f9486 AL |
80 | { |
81 | return xenstore_read_str(xendev->fe, node); | |
82 | } | |
83 | ||
2d0ed5e6 PD |
84 | int xenstore_read_fe_int(struct XenLegacyDevice *xendev, const char *node, |
85 | int *ival) | |
d94f9486 AL |
86 | { |
87 | return xenstore_read_int(xendev->fe, node, ival); | |
88 | } | |
89 | ||
2d0ed5e6 | 90 | int xenstore_read_fe_uint64(struct XenLegacyDevice *xendev, const char *node, |
b9730c5b | 91 | uint64_t *uval) |
4aba9eb1 SS |
92 | { |
93 | return xenstore_read_uint64(xendev->fe, node, uval); | |
94 | } | |
95 | ||
d94f9486 AL |
96 | /* ------------------------------------------------------------- */ |
97 | ||
2d0ed5e6 | 98 | int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state) |
d94f9486 AL |
99 | { |
100 | int rc; | |
101 | ||
102 | rc = xenstore_write_be_int(xendev, "state", state); | |
209cd7ab AP |
103 | if (rc < 0) { |
104 | return rc; | |
105 | } | |
96c77dba | 106 | xen_pv_printf(xendev, 1, "backend state: %s -> %s\n", |
209cd7ab | 107 | xenbus_strstate(xendev->be_state), xenbus_strstate(state)); |
d94f9486 AL |
108 | xendev->be_state = state; |
109 | return 0; | |
110 | } | |
111 | ||
2d0ed5e6 | 112 | void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev, |
9838824a PD |
113 | unsigned int nr_refs) |
114 | { | |
115 | assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); | |
116 | ||
117 | if (xengnttab_set_max_grants(xendev->gnttabdev, nr_refs)) { | |
118 | xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", | |
119 | strerror(errno)); | |
120 | } | |
121 | } | |
122 | ||
2d0ed5e6 | 123 | void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs, |
9838824a PD |
124 | unsigned int nr_refs, int prot) |
125 | { | |
126 | void *ptr; | |
127 | ||
128 | assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); | |
129 | ||
130 | ptr = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_refs, | |
131 | xen_domid, refs, prot); | |
132 | if (!ptr) { | |
133 | xen_pv_printf(xendev, 0, | |
134 | "xengnttab_map_domain_grant_refs failed: %s\n", | |
135 | strerror(errno)); | |
136 | } | |
137 | ||
138 | return ptr; | |
139 | } | |
140 | ||
2d0ed5e6 | 141 | void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr, |
9838824a PD |
142 | unsigned int nr_refs) |
143 | { | |
144 | assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); | |
145 | ||
146 | if (xengnttab_unmap(xendev->gnttabdev, ptr, nr_refs)) { | |
147 | xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", | |
148 | strerror(errno)); | |
149 | } | |
150 | } | |
151 | ||
2d0ed5e6 | 152 | static int compat_copy_grant_refs(struct XenLegacyDevice *xendev, |
3fe12b84 PD |
153 | bool to_domain, |
154 | XenGrantCopySegment segs[], | |
155 | unsigned int nr_segs) | |
156 | { | |
157 | uint32_t *refs = g_new(uint32_t, nr_segs); | |
158 | int prot = to_domain ? PROT_WRITE : PROT_READ; | |
159 | void *pages; | |
160 | unsigned int i; | |
161 | ||
162 | for (i = 0; i < nr_segs; i++) { | |
163 | XenGrantCopySegment *seg = &segs[i]; | |
164 | ||
165 | refs[i] = to_domain ? | |
166 | seg->dest.foreign.ref : seg->source.foreign.ref; | |
167 | } | |
168 | ||
169 | pages = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_segs, | |
170 | xen_domid, refs, prot); | |
171 | if (!pages) { | |
172 | xen_pv_printf(xendev, 0, | |
173 | "xengnttab_map_domain_grant_refs failed: %s\n", | |
174 | strerror(errno)); | |
175 | g_free(refs); | |
176 | return -1; | |
177 | } | |
178 | ||
179 | for (i = 0; i < nr_segs; i++) { | |
180 | XenGrantCopySegment *seg = &segs[i]; | |
181 | void *page = pages + (i * XC_PAGE_SIZE); | |
182 | ||
183 | if (to_domain) { | |
184 | memcpy(page + seg->dest.foreign.offset, seg->source.virt, | |
185 | seg->len); | |
186 | } else { | |
187 | memcpy(seg->dest.virt, page + seg->source.foreign.offset, | |
188 | seg->len); | |
189 | } | |
190 | } | |
191 | ||
192 | if (xengnttab_unmap(xendev->gnttabdev, pages, nr_segs)) { | |
193 | xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", | |
194 | strerror(errno)); | |
195 | } | |
196 | ||
197 | g_free(refs); | |
198 | return 0; | |
199 | } | |
200 | ||
2d0ed5e6 | 201 | int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev, |
9838824a PD |
202 | bool to_domain, |
203 | XenGrantCopySegment segs[], | |
204 | unsigned int nr_segs) | |
205 | { | |
206 | xengnttab_grant_copy_segment_t *xengnttab_segs; | |
207 | unsigned int i; | |
208 | int rc; | |
209 | ||
210 | assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); | |
211 | ||
3fe12b84 PD |
212 | if (!xen_feature_grant_copy) { |
213 | return compat_copy_grant_refs(xendev, to_domain, segs, nr_segs); | |
214 | } | |
215 | ||
9838824a PD |
216 | xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); |
217 | ||
218 | for (i = 0; i < nr_segs; i++) { | |
219 | XenGrantCopySegment *seg = &segs[i]; | |
220 | xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; | |
221 | ||
222 | if (to_domain) { | |
223 | xengnttab_seg->flags = GNTCOPY_dest_gref; | |
224 | xengnttab_seg->dest.foreign.domid = xen_domid; | |
225 | xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; | |
226 | xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; | |
227 | xengnttab_seg->source.virt = seg->source.virt; | |
228 | } else { | |
229 | xengnttab_seg->flags = GNTCOPY_source_gref; | |
230 | xengnttab_seg->source.foreign.domid = xen_domid; | |
231 | xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; | |
232 | xengnttab_seg->source.foreign.offset = | |
233 | seg->source.foreign.offset; | |
234 | xengnttab_seg->dest.virt = seg->dest.virt; | |
235 | } | |
236 | ||
237 | xengnttab_seg->len = seg->len; | |
238 | } | |
239 | ||
240 | rc = xengnttab_grant_copy(xendev->gnttabdev, nr_segs, xengnttab_segs); | |
241 | ||
242 | if (rc) { | |
243 | xen_pv_printf(xendev, 0, "xengnttab_copy failed: %s\n", | |
244 | strerror(errno)); | |
245 | } | |
246 | ||
247 | for (i = 0; i < nr_segs; i++) { | |
248 | xengnttab_grant_copy_segment_t *xengnttab_seg = | |
249 | &xengnttab_segs[i]; | |
250 | ||
251 | if (xengnttab_seg->status != GNTST_okay) { | |
252 | xen_pv_printf(xendev, 0, "segment[%u] status: %d\n", i, | |
253 | xengnttab_seg->status); | |
254 | rc = -1; | |
255 | } | |
256 | } | |
257 | ||
258 | g_free(xengnttab_segs); | |
259 | return rc; | |
260 | } | |
261 | ||
d94f9486 AL |
262 | /* |
263 | * get xen backend device, allocate a new one if it doesn't exist. | |
264 | */ | |
2d0ed5e6 PD |
265 | static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom, |
266 | int dev, | |
267 | struct XenDevOps *ops) | |
d94f9486 | 268 | { |
2d0ed5e6 | 269 | struct XenLegacyDevice *xendev; |
d94f9486 | 270 | |
fa0253d0 | 271 | xendev = xen_pv_find_xendev(type, dom, dev); |
209cd7ab AP |
272 | if (xendev) { |
273 | return xendev; | |
274 | } | |
d94f9486 AL |
275 | |
276 | /* init new xendev */ | |
7267c094 | 277 | xendev = g_malloc0(ops->size); |
3a6c9172 | 278 | object_initialize(&xendev->qdev, ops->size, TYPE_XENBACKEND); |
e9dcbc86 JG |
279 | OBJECT(xendev)->free = g_free; |
280 | qdev_set_parent_bus(DEVICE(xendev), xen_sysbus); | |
281 | qdev_set_id(DEVICE(xendev), g_strdup_printf("xen-%s-%d", type, dev)); | |
282 | qdev_init_nofail(DEVICE(xendev)); | |
283 | object_unref(OBJECT(xendev)); | |
3a6c9172 | 284 | |
d94f9486 AL |
285 | xendev->type = type; |
286 | xendev->dom = dom; | |
287 | xendev->dev = dev; | |
288 | xendev->ops = ops; | |
289 | ||
33876dfa RPM |
290 | snprintf(xendev->be, sizeof(xendev->be), "backend/%s/%d/%d", |
291 | xendev->type, xendev->dom, xendev->dev); | |
d94f9486 | 292 | snprintf(xendev->name, sizeof(xendev->name), "%s-%d", |
209cd7ab | 293 | xendev->type, xendev->dev); |
d94f9486 AL |
294 | |
295 | xendev->debug = debug; | |
296 | xendev->local_port = -1; | |
297 | ||
a2db2a1e IC |
298 | xendev->evtchndev = xenevtchn_open(NULL, 0); |
299 | if (xendev->evtchndev == NULL) { | |
96c77dba | 300 | xen_pv_printf(NULL, 0, "can't open evtchn device\n"); |
e9dcbc86 | 301 | qdev_unplug(DEVICE(xendev), NULL); |
209cd7ab | 302 | return NULL; |
d94f9486 | 303 | } |
01cd90b6 | 304 | qemu_set_cloexec(xenevtchn_fd(xendev->evtchndev)); |
d94f9486 | 305 | |
148512e0 | 306 | xen_pv_insert_xendev(xendev); |
d94f9486 | 307 | |
209cd7ab AP |
308 | if (xendev->ops->alloc) { |
309 | xendev->ops->alloc(xendev); | |
310 | } | |
d94f9486 AL |
311 | |
312 | return xendev; | |
313 | } | |
314 | ||
d94f9486 AL |
315 | |
316 | /* | |
317 | * Sync internal data structures on xenstore updates. | |
318 | * Node specifies the changed field. node = NULL means | |
319 | * update all fields (used for initialization). | |
320 | */ | |
2d0ed5e6 PD |
321 | static void xen_be_backend_changed(struct XenLegacyDevice *xendev, |
322 | const char *node) | |
d94f9486 AL |
323 | { |
324 | if (node == NULL || strcmp(node, "online") == 0) { | |
209cd7ab AP |
325 | if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) { |
326 | xendev->online = 0; | |
327 | } | |
d94f9486 AL |
328 | } |
329 | ||
330 | if (node) { | |
96c77dba | 331 | xen_pv_printf(xendev, 2, "backend update: %s\n", node); |
209cd7ab AP |
332 | if (xendev->ops->backend_changed) { |
333 | xendev->ops->backend_changed(xendev, node); | |
334 | } | |
d94f9486 AL |
335 | } |
336 | } | |
337 | ||
2d0ed5e6 PD |
338 | static void xen_be_frontend_changed(struct XenLegacyDevice *xendev, |
339 | const char *node) | |
d94f9486 AL |
340 | { |
341 | int fe_state; | |
342 | ||
343 | if (node == NULL || strcmp(node, "state") == 0) { | |
209cd7ab AP |
344 | if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) { |
345 | fe_state = XenbusStateUnknown; | |
346 | } | |
347 | if (xendev->fe_state != fe_state) { | |
96c77dba | 348 | xen_pv_printf(xendev, 1, "frontend state: %s -> %s\n", |
209cd7ab AP |
349 | xenbus_strstate(xendev->fe_state), |
350 | xenbus_strstate(fe_state)); | |
351 | } | |
352 | xendev->fe_state = fe_state; | |
d94f9486 AL |
353 | } |
354 | if (node == NULL || strcmp(node, "protocol") == 0) { | |
7267c094 | 355 | g_free(xendev->protocol); |
209cd7ab AP |
356 | xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); |
357 | if (xendev->protocol) { | |
96c77dba | 358 | xen_pv_printf(xendev, 1, "frontend protocol: %s\n", |
b9730c5b | 359 | xendev->protocol); |
209cd7ab | 360 | } |
d94f9486 AL |
361 | } |
362 | ||
363 | if (node) { | |
96c77dba | 364 | xen_pv_printf(xendev, 2, "frontend update: %s\n", node); |
209cd7ab AP |
365 | if (xendev->ops->frontend_changed) { |
366 | xendev->ops->frontend_changed(xendev, node); | |
367 | } | |
d94f9486 AL |
368 | } |
369 | } | |
370 | ||
371 | /* ------------------------------------------------------------- */ | |
372 | /* Check for possible state transitions and perform them. */ | |
373 | ||
374 | /* | |
375 | * Initial xendev setup. Read frontend path, register watch for it. | |
376 | * Should succeed once xend finished setting up the backend device. | |
377 | * | |
378 | * Also sets initial state (-> Initializing) when done. Which | |
379 | * only affects the xendev->be_state variable as xenbus should | |
380 | * already be put into that state by xend. | |
381 | */ | |
2d0ed5e6 | 382 | static int xen_be_try_setup(struct XenLegacyDevice *xendev) |
d94f9486 AL |
383 | { |
384 | char token[XEN_BUFSIZE]; | |
385 | int be_state; | |
386 | ||
387 | if (xenstore_read_be_int(xendev, "state", &be_state) == -1) { | |
96c77dba | 388 | xen_pv_printf(xendev, 0, "reading backend state failed\n"); |
209cd7ab | 389 | return -1; |
d94f9486 AL |
390 | } |
391 | ||
392 | if (be_state != XenbusStateInitialising) { | |
96c77dba | 393 | xen_pv_printf(xendev, 0, "initial backend state is wrong (%s)\n", |
209cd7ab AP |
394 | xenbus_strstate(be_state)); |
395 | return -1; | |
d94f9486 AL |
396 | } |
397 | ||
398 | xendev->fe = xenstore_read_be_str(xendev, "frontend"); | |
399 | if (xendev->fe == NULL) { | |
96c77dba | 400 | xen_pv_printf(xendev, 0, "reading frontend path failed\n"); |
209cd7ab | 401 | return -1; |
d94f9486 AL |
402 | } |
403 | ||
404 | /* setup frontend watch */ | |
405 | snprintf(token, sizeof(token), "fe:%p", xendev); | |
406 | if (!xs_watch(xenstore, xendev->fe, token)) { | |
96c77dba | 407 | xen_pv_printf(xendev, 0, "watching frontend path (%s) failed\n", |
209cd7ab AP |
408 | xendev->fe); |
409 | return -1; | |
d94f9486 AL |
410 | } |
411 | xen_be_set_state(xendev, XenbusStateInitialising); | |
412 | ||
413 | xen_be_backend_changed(xendev, NULL); | |
414 | xen_be_frontend_changed(xendev, NULL); | |
415 | return 0; | |
416 | } | |
417 | ||
418 | /* | |
419 | * Try initialize xendev. Prepare everything the backend can do | |
420 | * without synchronizing with the frontend. Fakes hotplug-status. No | |
421 | * hotplug involved here because this is about userspace drivers, thus | |
422 | * there are kernel backend devices which could invoke hotplug. | |
423 | * | |
424 | * Goes to InitWait on success. | |
425 | */ | |
2d0ed5e6 | 426 | static int xen_be_try_init(struct XenLegacyDevice *xendev) |
d94f9486 AL |
427 | { |
428 | int rc = 0; | |
429 | ||
430 | if (!xendev->online) { | |
96c77dba | 431 | xen_pv_printf(xendev, 1, "not online\n"); |
209cd7ab | 432 | return -1; |
d94f9486 AL |
433 | } |
434 | ||
209cd7ab AP |
435 | if (xendev->ops->init) { |
436 | rc = xendev->ops->init(xendev); | |
437 | } | |
d94f9486 | 438 | if (rc != 0) { |
96c77dba | 439 | xen_pv_printf(xendev, 1, "init() failed\n"); |
209cd7ab | 440 | return rc; |
d94f9486 AL |
441 | } |
442 | ||
443 | xenstore_write_be_str(xendev, "hotplug-status", "connected"); | |
444 | xen_be_set_state(xendev, XenbusStateInitWait); | |
445 | return 0; | |
446 | } | |
447 | ||
448 | /* | |
384087b2 | 449 | * Try to initialise xendev. Depends on the frontend being ready |
d94f9486 AL |
450 | * for it (shared ring and evtchn info in xenstore, state being |
451 | * Initialised or Connected). | |
452 | * | |
453 | * Goes to Connected on success. | |
454 | */ | |
2d0ed5e6 | 455 | static int xen_be_try_initialise(struct XenLegacyDevice *xendev) |
d94f9486 AL |
456 | { |
457 | int rc = 0; | |
458 | ||
459 | if (xendev->fe_state != XenbusStateInitialised && | |
209cd7ab AP |
460 | xendev->fe_state != XenbusStateConnected) { |
461 | if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { | |
96c77dba | 462 | xen_pv_printf(xendev, 2, "frontend not ready, ignoring\n"); |
209cd7ab | 463 | } else { |
96c77dba | 464 | xen_pv_printf(xendev, 2, "frontend not ready (yet)\n"); |
209cd7ab AP |
465 | return -1; |
466 | } | |
d94f9486 AL |
467 | } |
468 | ||
9838824a PD |
469 | if (xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { |
470 | xendev->gnttabdev = xengnttab_open(NULL, 0); | |
471 | if (xendev->gnttabdev == NULL) { | |
472 | xen_pv_printf(NULL, 0, "can't open gnttab device\n"); | |
473 | return -1; | |
474 | } | |
475 | } else { | |
476 | xendev->gnttabdev = NULL; | |
477 | } | |
478 | ||
384087b2 JH |
479 | if (xendev->ops->initialise) { |
480 | rc = xendev->ops->initialise(xendev); | |
209cd7ab | 481 | } |
d94f9486 | 482 | if (rc != 0) { |
96c77dba | 483 | xen_pv_printf(xendev, 0, "initialise() failed\n"); |
209cd7ab | 484 | return rc; |
d94f9486 AL |
485 | } |
486 | ||
487 | xen_be_set_state(xendev, XenbusStateConnected); | |
488 | return 0; | |
489 | } | |
490 | ||
384087b2 JH |
491 | /* |
492 | * Try to let xendev know that it is connected. Depends on the | |
493 | * frontend being Connected. Note that this may be called more | |
494 | * than once since the backend state is not modified. | |
495 | */ | |
2d0ed5e6 | 496 | static void xen_be_try_connected(struct XenLegacyDevice *xendev) |
384087b2 JH |
497 | { |
498 | if (!xendev->ops->connected) { | |
499 | return; | |
500 | } | |
501 | ||
502 | if (xendev->fe_state != XenbusStateConnected) { | |
503 | if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { | |
96c77dba | 504 | xen_pv_printf(xendev, 2, "frontend not ready, ignoring\n"); |
384087b2 | 505 | } else { |
96c77dba | 506 | xen_pv_printf(xendev, 2, "frontend not ready (yet)\n"); |
384087b2 JH |
507 | return; |
508 | } | |
509 | } | |
510 | ||
511 | xendev->ops->connected(xendev); | |
512 | } | |
513 | ||
d94f9486 AL |
514 | /* |
515 | * Teardown connection. | |
516 | * | |
517 | * Goes to Closed when done. | |
518 | */ | |
2d0ed5e6 PD |
519 | static void xen_be_disconnect(struct XenLegacyDevice *xendev, |
520 | enum xenbus_state state) | |
d94f9486 AL |
521 | { |
522 | if (xendev->be_state != XenbusStateClosing && | |
523 | xendev->be_state != XenbusStateClosed && | |
209cd7ab AP |
524 | xendev->ops->disconnect) { |
525 | xendev->ops->disconnect(xendev); | |
526 | } | |
9838824a PD |
527 | if (xendev->gnttabdev) { |
528 | xengnttab_close(xendev->gnttabdev); | |
529 | xendev->gnttabdev = NULL; | |
530 | } | |
209cd7ab | 531 | if (xendev->be_state != state) { |
d94f9486 | 532 | xen_be_set_state(xendev, state); |
209cd7ab | 533 | } |
d94f9486 AL |
534 | } |
535 | ||
536 | /* | |
537 | * Try to reset xendev, for reconnection by another frontend instance. | |
538 | */ | |
2d0ed5e6 | 539 | static int xen_be_try_reset(struct XenLegacyDevice *xendev) |
d94f9486 | 540 | { |
209cd7ab | 541 | if (xendev->fe_state != XenbusStateInitialising) { |
d94f9486 | 542 | return -1; |
209cd7ab | 543 | } |
d94f9486 | 544 | |
96c77dba | 545 | xen_pv_printf(xendev, 1, "device reset (for re-connect)\n"); |
d94f9486 AL |
546 | xen_be_set_state(xendev, XenbusStateInitialising); |
547 | return 0; | |
548 | } | |
549 | ||
550 | /* | |
551 | * state change dispatcher function | |
552 | */ | |
2d0ed5e6 | 553 | void xen_be_check_state(struct XenLegacyDevice *xendev) |
d94f9486 AL |
554 | { |
555 | int rc = 0; | |
556 | ||
557 | /* frontend may request shutdown from almost anywhere */ | |
558 | if (xendev->fe_state == XenbusStateClosing || | |
209cd7ab AP |
559 | xendev->fe_state == XenbusStateClosed) { |
560 | xen_be_disconnect(xendev, xendev->fe_state); | |
561 | return; | |
d94f9486 AL |
562 | } |
563 | ||
564 | /* check for possible backend state transitions */ | |
565 | for (;;) { | |
209cd7ab AP |
566 | switch (xendev->be_state) { |
567 | case XenbusStateUnknown: | |
568 | rc = xen_be_try_setup(xendev); | |
569 | break; | |
570 | case XenbusStateInitialising: | |
571 | rc = xen_be_try_init(xendev); | |
572 | break; | |
573 | case XenbusStateInitWait: | |
384087b2 JH |
574 | rc = xen_be_try_initialise(xendev); |
575 | break; | |
576 | case XenbusStateConnected: | |
577 | /* xendev->be_state doesn't change */ | |
578 | xen_be_try_connected(xendev); | |
579 | rc = -1; | |
209cd7ab | 580 | break; |
d94f9486 AL |
581 | case XenbusStateClosed: |
582 | rc = xen_be_try_reset(xendev); | |
583 | break; | |
209cd7ab AP |
584 | default: |
585 | rc = -1; | |
586 | } | |
587 | if (rc != 0) { | |
588 | break; | |
589 | } | |
d94f9486 AL |
590 | } |
591 | } | |
592 | ||
593 | /* ------------------------------------------------------------- */ | |
594 | ||
595 | static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) | |
596 | { | |
2d0ed5e6 | 597 | struct XenLegacyDevice *xendev; |
d94f9486 | 598 | char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; |
33876dfa | 599 | char **dev = NULL; |
d94f9486 AL |
600 | unsigned int cdev, j; |
601 | ||
602 | /* setup watch */ | |
d94f9486 | 603 | snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); |
33876dfa | 604 | snprintf(path, sizeof(path), "backend/%s/%d", type, dom); |
d94f9486 | 605 | if (!xs_watch(xenstore, path, token)) { |
96c77dba | 606 | xen_pv_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", |
b9730c5b | 607 | path); |
209cd7ab | 608 | return -1; |
d94f9486 AL |
609 | } |
610 | ||
611 | /* look for backends */ | |
612 | dev = xs_directory(xenstore, 0, path, &cdev); | |
209cd7ab AP |
613 | if (!dev) { |
614 | return 0; | |
615 | } | |
d94f9486 | 616 | for (j = 0; j < cdev; j++) { |
209cd7ab AP |
617 | xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); |
618 | if (xendev == NULL) { | |
619 | continue; | |
620 | } | |
621 | xen_be_check_state(xendev); | |
d94f9486 AL |
622 | } |
623 | free(dev); | |
624 | return 0; | |
625 | } | |
626 | ||
046db9be EC |
627 | void xenstore_update_be(char *watch, char *type, int dom, |
628 | struct XenDevOps *ops) | |
d94f9486 | 629 | { |
2d0ed5e6 | 630 | struct XenLegacyDevice *xendev; |
33876dfa | 631 | char path[XEN_BUFSIZE], *bepath; |
d94f9486 AL |
632 | unsigned int len, dev; |
633 | ||
33876dfa | 634 | len = snprintf(path, sizeof(path), "backend/%s/%d", type, dom); |
209cd7ab AP |
635 | if (strncmp(path, watch, len) != 0) { |
636 | return; | |
637 | } | |
2d0ed5e6 | 638 | if (sscanf(watch + len, "/%u/%255s", &dev, path) != 2) { |
209cd7ab | 639 | strcpy(path, ""); |
2d0ed5e6 | 640 | if (sscanf(watch + len, "/%u", &dev) != 1) { |
209cd7ab AP |
641 | dev = -1; |
642 | } | |
643 | } | |
644 | if (dev == -1) { | |
645 | return; | |
d94f9486 | 646 | } |
d94f9486 | 647 | |
d94f9486 AL |
648 | xendev = xen_be_get_xendev(type, dom, dev, ops); |
649 | if (xendev != NULL) { | |
77ba8fef SS |
650 | bepath = xs_read(xenstore, 0, xendev->be, &len); |
651 | if (bepath == NULL) { | |
71981364 | 652 | xen_pv_del_xendev(xendev); |
77ba8fef SS |
653 | } else { |
654 | free(bepath); | |
655 | xen_be_backend_changed(xendev, path); | |
656 | xen_be_check_state(xendev); | |
657 | } | |
d94f9486 AL |
658 | } |
659 | } | |
660 | ||
2d0ed5e6 | 661 | void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev) |
d94f9486 AL |
662 | { |
663 | char *node; | |
664 | unsigned int len; | |
665 | ||
666 | len = strlen(xendev->fe); | |
209cd7ab AP |
667 | if (strncmp(xendev->fe, watch, len) != 0) { |
668 | return; | |
669 | } | |
670 | if (watch[len] != '/') { | |
671 | return; | |
672 | } | |
d94f9486 AL |
673 | node = watch + len + 1; |
674 | ||
675 | xen_be_frontend_changed(xendev, node); | |
676 | xen_be_check_state(xendev); | |
677 | } | |
d94f9486 AL |
678 | /* -------------------------------------------------------------------- */ |
679 | ||
680 | int xen_be_init(void) | |
681 | { | |
b5e397a7 JG |
682 | xengnttab_handle *gnttabdev; |
683 | ||
d94f9486 AL |
684 | xenstore = xs_daemon_open(); |
685 | if (!xenstore) { | |
96c77dba | 686 | xen_pv_printf(NULL, 0, "can't connect to xenstored\n"); |
209cd7ab | 687 | return -1; |
d94f9486 AL |
688 | } |
689 | ||
6b5166f8 | 690 | qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL); |
d94f9486 | 691 | |
81daba58 | 692 | if (xen_xc == NULL || xen_fmem == NULL) { |
3285cf4f | 693 | /* Check if xen_init() have been called */ |
209cd7ab | 694 | goto err; |
d94f9486 | 695 | } |
25f8f6b4 | 696 | |
b5e397a7 JG |
697 | gnttabdev = xengnttab_open(NULL, 0); |
698 | if (gnttabdev != NULL) { | |
699 | if (xengnttab_grant_copy(gnttabdev, 0, NULL) == 0) { | |
700 | xen_feature_grant_copy = true; | |
701 | } | |
702 | xengnttab_close(gnttabdev); | |
703 | } | |
704 | ||
25f8f6b4 JG |
705 | xen_sysdev = qdev_create(NULL, TYPE_XENSYSDEV); |
706 | qdev_init_nofail(xen_sysdev); | |
873d57ab JG |
707 | xen_sysbus = qbus_create(TYPE_XENSYSBUS, DEVICE(xen_sysdev), "xen-sysbus"); |
708 | qbus_set_bus_hotplug_handler(xen_sysbus, &error_abort); | |
25f8f6b4 | 709 | |
d94f9486 AL |
710 | return 0; |
711 | ||
712 | err: | |
713 | qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); | |
714 | xs_daemon_close(xenstore); | |
715 | xenstore = NULL; | |
716 | ||
717 | return -1; | |
718 | } | |
719 | ||
3a6c9172 JG |
720 | static void xen_set_dynamic_sysbus(void) |
721 | { | |
722 | Object *machine = qdev_get_machine(); | |
723 | ObjectClass *oc = object_get_class(machine); | |
724 | MachineClass *mc = MACHINE_CLASS(oc); | |
725 | ||
b1b68e10 | 726 | machine_class_allow_dynamic_sysbus_dev(mc, TYPE_XENSYSDEV); |
3a6c9172 JG |
727 | } |
728 | ||
d94f9486 AL |
729 | int xen_be_register(const char *type, struct XenDevOps *ops) |
730 | { | |
637c53ff JG |
731 | char path[50]; |
732 | int rc; | |
733 | ||
734 | if (ops->backend_register) { | |
735 | rc = ops->backend_register(); | |
736 | if (rc) { | |
737 | return rc; | |
738 | } | |
739 | } | |
740 | ||
741 | snprintf(path, sizeof(path), "device-model/%u/backends/%s", xen_domid, | |
742 | type); | |
743 | xenstore_mkdir(path, XS_PERM_NONE); | |
744 | ||
d94f9486 AL |
745 | return xenstore_scan(type, xen_domid, ops); |
746 | } | |
747 | ||
0e39bb02 JG |
748 | void xen_be_register_common(void) |
749 | { | |
3a6c9172 JG |
750 | xen_set_dynamic_sysbus(); |
751 | ||
0e39bb02 JG |
752 | xen_be_register("console", &xen_console_ops); |
753 | xen_be_register("vkbd", &xen_kbdmouse_ops); | |
e737b6d5 SS |
754 | #ifdef CONFIG_VIRTFS |
755 | xen_be_register("9pfs", &xen_9pfs_ops); | |
756 | #endif | |
0e39bb02 JG |
757 | #ifdef CONFIG_USB_LIBUSB |
758 | xen_be_register("qusb", &xen_usb_ops); | |
759 | #endif | |
760 | } | |
761 | ||
2d0ed5e6 | 762 | int xen_be_bind_evtchn(struct XenLegacyDevice *xendev) |
d94f9486 | 763 | { |
209cd7ab AP |
764 | if (xendev->local_port != -1) { |
765 | return 0; | |
766 | } | |
a2db2a1e | 767 | xendev->local_port = xenevtchn_bind_interdomain |
209cd7ab | 768 | (xendev->evtchndev, xendev->dom, xendev->remote_port); |
d94f9486 | 769 | if (xendev->local_port == -1) { |
96c77dba | 770 | xen_pv_printf(xendev, 0, "xenevtchn_bind_interdomain failed\n"); |
209cd7ab | 771 | return -1; |
d94f9486 | 772 | } |
96c77dba | 773 | xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); |
a2db2a1e | 774 | qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), |
49442d96 | 775 | xen_pv_evtchn_event, NULL, xendev); |
d94f9486 AL |
776 | return 0; |
777 | } | |
778 | ||
25f8f6b4 | 779 | |
3a6c9172 JG |
780 | static Property xendev_properties[] = { |
781 | DEFINE_PROP_END_OF_LIST(), | |
782 | }; | |
783 | ||
784 | static void xendev_class_init(ObjectClass *klass, void *data) | |
785 | { | |
786 | DeviceClass *dc = DEVICE_CLASS(klass); | |
787 | ||
788 | dc->props = xendev_properties; | |
789 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); | |
950b31dd | 790 | /* xen-backend devices can be plugged/unplugged dynamically */ |
e4f4fb1e | 791 | dc->user_creatable = true; |
3a6c9172 JG |
792 | } |
793 | ||
794 | static const TypeInfo xendev_type_info = { | |
795 | .name = TYPE_XENBACKEND, | |
796 | .parent = TYPE_XENSYSDEV, | |
797 | .class_init = xendev_class_init, | |
2d0ed5e6 | 798 | .instance_size = sizeof(struct XenLegacyDevice), |
3a6c9172 JG |
799 | }; |
800 | ||
801 | static void xen_sysbus_class_init(ObjectClass *klass, void *data) | |
802 | { | |
803 | HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); | |
804 | ||
805 | hc->unplug = qdev_simple_device_unplug_cb; | |
806 | } | |
807 | ||
873d57ab JG |
808 | static const TypeInfo xensysbus_info = { |
809 | .name = TYPE_XENSYSBUS, | |
810 | .parent = TYPE_BUS, | |
3a6c9172 | 811 | .class_init = xen_sysbus_class_init, |
873d57ab JG |
812 | .interfaces = (InterfaceInfo[]) { |
813 | { TYPE_HOTPLUG_HANDLER }, | |
814 | { } | |
815 | } | |
816 | }; | |
817 | ||
25f8f6b4 JG |
818 | static Property xen_sysdev_properties[] = { |
819 | {/* end of property list */}, | |
820 | }; | |
821 | ||
822 | static void xen_sysdev_class_init(ObjectClass *klass, void *data) | |
823 | { | |
824 | DeviceClass *dc = DEVICE_CLASS(klass); | |
25f8f6b4 | 825 | |
25f8f6b4 | 826 | dc->props = xen_sysdev_properties; |
873d57ab | 827 | dc->bus_type = TYPE_XENSYSBUS; |
25f8f6b4 JG |
828 | } |
829 | ||
830 | static const TypeInfo xensysdev_info = { | |
831 | .name = TYPE_XENSYSDEV, | |
832 | .parent = TYPE_SYS_BUS_DEVICE, | |
833 | .instance_size = sizeof(SysBusDevice), | |
834 | .class_init = xen_sysdev_class_init, | |
835 | }; | |
836 | ||
837 | static void xenbe_register_types(void) | |
838 | { | |
873d57ab | 839 | type_register_static(&xensysbus_info); |
25f8f6b4 | 840 | type_register_static(&xensysdev_info); |
3a6c9172 | 841 | type_register_static(&xendev_type_info); |
25f8f6b4 JG |
842 | } |
843 | ||
873d57ab | 844 | type_init(xenbe_register_types) |