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