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