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