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