1 | /* $NetBSD: xenbus_probe.c,v 1.39 2016/07/07 06:55:40 msaitoh Exp $ */ |
2 | /****************************************************************************** |
3 | * Talks to Xen Store to figure out what devices we have. |
4 | * |
5 | * Copyright (C) 2005 Rusty Russell, IBM Corporation |
6 | * Copyright (C) 2005 Mike Wray, Hewlett-Packard |
7 | * Copyright (C) 2005 XenSource Ltd |
8 | * |
9 | * This file may be distributed separately from the Linux kernel, or |
10 | * incorporated into other software packages, subject to the following license: |
11 | * |
12 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
13 | * of this source file (the "Software"), to deal in the Software without |
14 | * restriction, including without limitation the rights to use, copy, modify, |
15 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, |
16 | * and to permit persons to whom the Software is furnished to do so, subject to |
17 | * the following conditions: |
18 | * |
19 | * The above copyright notice and this permission notice shall be included in |
20 | * all copies or substantial portions of the Software. |
21 | * |
22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
28 | * IN THE SOFTWARE. |
29 | */ |
30 | |
31 | #include <sys/cdefs.h> |
32 | __KERNEL_RCSID(0, "$NetBSD: xenbus_probe.c,v 1.39 2016/07/07 06:55:40 msaitoh Exp $" ); |
33 | |
34 | #if 0 |
35 | #define DPRINTK(fmt, args...) \ |
36 | printf("xenbus_probe (%s:%d) " fmt ".\n", __func__, __LINE__, ##args) |
37 | #else |
38 | #define DPRINTK(fmt, args...) ((void)0) |
39 | #endif |
40 | |
41 | #include <sys/types.h> |
42 | #include <sys/null.h> |
43 | #include <sys/errno.h> |
44 | #include <sys/malloc.h> |
45 | #include <sys/systm.h> |
46 | #include <sys/param.h> |
47 | #include <sys/kthread.h> |
48 | #include <uvm/uvm.h> |
49 | |
50 | #include <xen/xen.h> /* for xendomain_is_dom0() */ |
51 | #include <xen/hypervisor.h> |
52 | #include <xen/xenbus.h> |
53 | #include <xen/evtchn.h> |
54 | #include <xen/shutdown_xenbus.h> |
55 | |
56 | #include "xenbus_comms.h" |
57 | |
58 | extern struct semaphore xenwatch_mutex; |
59 | |
60 | #define streq(a, b) (strcmp((a), (b)) == 0) |
61 | |
62 | static int xenbus_match(device_t, cfdata_t, void *); |
63 | static void xenbus_attach(device_t, device_t, void *); |
64 | static int xenbus_print(void *, const char *); |
65 | |
66 | /* power management, for save/restore */ |
67 | static bool xenbus_suspend(device_t, const pmf_qual_t *); |
68 | static bool xenbus_resume(device_t, const pmf_qual_t *); |
69 | |
70 | /* routines gathering device information from XenStore */ |
71 | static int read_otherend_details(struct xenbus_device *, |
72 | const char *, const char *); |
73 | static int read_backend_details (struct xenbus_device *); |
74 | static int read_frontend_details(struct xenbus_device *); |
75 | static void free_otherend_details(struct xenbus_device *); |
76 | |
77 | static int watch_otherend (struct xenbus_device *); |
78 | static void free_otherend_watch(struct xenbus_device *); |
79 | |
80 | static void xenbus_probe_init(void *); |
81 | |
82 | static struct xenbus_device *xenbus_lookup_device_path(const char *); |
83 | |
84 | CFATTACH_DECL_NEW(xenbus, 0, xenbus_match, xenbus_attach, |
85 | NULL, NULL); |
86 | |
87 | device_t xenbus_dev; |
88 | |
89 | SLIST_HEAD(, xenbus_device) xenbus_device_list; |
90 | SLIST_HEAD(, xenbus_backend_driver) xenbus_backend_driver_list = |
91 | SLIST_HEAD_INITIALIZER(xenbus_backend_driver); |
92 | |
93 | int |
94 | xenbus_match(device_t parent, cfdata_t match, void *aux) |
95 | { |
96 | struct xenbus_attach_args *xa = (struct xenbus_attach_args *)aux; |
97 | |
98 | if (strcmp(xa->xa_device, "xenbus" ) == 0) |
99 | return 1; |
100 | return 0; |
101 | } |
102 | |
103 | static void |
104 | xenbus_attach(device_t parent, device_t self, void *aux) |
105 | { |
106 | int err; |
107 | |
108 | aprint_normal(": Xen Virtual Bus Interface\n" ); |
109 | xenbus_dev = self; |
110 | config_pending_incr(self); |
111 | |
112 | err = kthread_create(PRI_NONE, 0, NULL, xenbus_probe_init, NULL, |
113 | NULL, "xenbus_probe" ); |
114 | if (err) |
115 | aprint_error_dev(xenbus_dev, |
116 | "kthread_create(xenbus_probe): %d\n" , err); |
117 | |
118 | if (!pmf_device_register(self, xenbus_suspend, xenbus_resume)) |
119 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
120 | } |
121 | |
122 | static bool |
123 | xenbus_suspend(device_t dev, const pmf_qual_t *qual) |
124 | { |
125 | xs_suspend(); |
126 | xb_suspend_comms(dev); |
127 | |
128 | return true; |
129 | } |
130 | |
131 | static bool |
132 | xenbus_resume(device_t dev, const pmf_qual_t *qual) |
133 | { |
134 | xb_init_comms(dev); |
135 | xs_resume(); |
136 | |
137 | return true; |
138 | } |
139 | |
140 | /* |
141 | * Suspend a xenbus device |
142 | */ |
143 | bool |
144 | xenbus_device_suspend(struct xenbus_device *dev) { |
145 | |
146 | free_otherend_details(dev); |
147 | return true; |
148 | } |
149 | |
150 | /* |
151 | * Resume a xenbus device |
152 | */ |
153 | bool |
154 | xenbus_device_resume(struct xenbus_device *dev) { |
155 | |
156 | if (dev->xbusd_type == XENBUS_FRONTEND_DEVICE) { |
157 | read_backend_details(dev); |
158 | } |
159 | |
160 | return true; |
161 | } |
162 | |
163 | void |
164 | xenbus_backend_register(struct xenbus_backend_driver *xbakd) |
165 | { |
166 | SLIST_INSERT_HEAD(&xenbus_backend_driver_list, xbakd, xbakd_entries); |
167 | } |
168 | |
169 | static int |
170 | read_otherend_details(struct xenbus_device *xendev, |
171 | const char *id_node, const char *path_node) |
172 | { |
173 | int err; |
174 | char *val, *ep; |
175 | |
176 | err = xenbus_read(NULL, xendev->xbusd_path, id_node, NULL, &val); |
177 | if (err) { |
178 | printf("reading other end details %s from %s\n" , |
179 | id_node, xendev->xbusd_path); |
180 | xenbus_dev_fatal(xendev, err, |
181 | "reading other end details %s from %s" , |
182 | id_node, xendev->xbusd_path); |
183 | return err; |
184 | } |
185 | xendev->xbusd_otherend_id = strtoul(val, &ep, 10); |
186 | if (val[0] == '\0' || *ep != '\0') { |
187 | printf("reading other end details %s from %s: %s is not a number\n" , id_node, xendev->xbusd_path, val); |
188 | xenbus_dev_fatal(xendev, err, |
189 | "reading other end details %s from %s: %s is not a number" , |
190 | id_node, xendev->xbusd_path, val); |
191 | free(val, M_DEVBUF); |
192 | return EFTYPE; |
193 | } |
194 | free(val, M_DEVBUF); |
195 | err = xenbus_read(NULL, xendev->xbusd_path, path_node, NULL, &val); |
196 | if (err) { |
197 | printf("reading other end details %s from %s (%d)\n" , |
198 | path_node, xendev->xbusd_path, err); |
199 | xenbus_dev_fatal(xendev, err, |
200 | "reading other end details %s from %s" , |
201 | path_node, xendev->xbusd_path); |
202 | return err; |
203 | } |
204 | DPRINTK("read_otherend_details: read %s/%s returned %s\n" , |
205 | xendev->xbusd_path, path_node, val); |
206 | xendev->xbusd_otherend = val; |
207 | |
208 | if (strlen(xendev->xbusd_otherend) == 0 || |
209 | !xenbus_exists(NULL, xendev->xbusd_otherend, "" )) { |
210 | printf("missing other end from %s\n" , xendev->xbusd_path); |
211 | xenbus_dev_fatal(xendev, -ENOENT, "missing other end from %s" , |
212 | xendev->xbusd_path); |
213 | free_otherend_details(xendev); |
214 | return ENOENT; |
215 | } |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | static int |
221 | read_backend_details(struct xenbus_device *xendev) |
222 | { |
223 | return read_otherend_details(xendev, "backend-id" , "backend" ); |
224 | } |
225 | |
226 | |
227 | static int |
228 | read_frontend_details(struct xenbus_device *xendev) |
229 | { |
230 | return read_otherend_details(xendev, "frontend-id" , "frontend" ); |
231 | } |
232 | |
233 | static void |
234 | free_otherend_details(struct xenbus_device *dev) |
235 | { |
236 | free(dev->xbusd_otherend, M_DEVBUF); |
237 | dev->xbusd_otherend = NULL; |
238 | } |
239 | |
240 | static void |
241 | free_otherend_watch(struct xenbus_device *dev) |
242 | { |
243 | if (dev->xbusd_otherend_watch.node) { |
244 | unregister_xenbus_watch(&dev->xbusd_otherend_watch); |
245 | free(dev->xbusd_otherend_watch.node, M_DEVBUF); |
246 | dev->xbusd_otherend_watch.node = NULL; |
247 | } |
248 | } |
249 | |
250 | static void |
251 | otherend_changed(struct xenbus_watch *watch, |
252 | const char **vec, unsigned int len) |
253 | { |
254 | struct xenbus_device *xdev = watch->xbw_dev; |
255 | XenbusState state; |
256 | |
257 | /* Protect us against watches firing on old details when the otherend |
258 | details change, say immediately after a resume. */ |
259 | if (!xdev->xbusd_otherend || |
260 | strncmp(xdev->xbusd_otherend, vec[XS_WATCH_PATH], |
261 | strlen(xdev->xbusd_otherend))) { |
262 | DPRINTK("Ignoring watch at %s" , vec[XS_WATCH_PATH]); |
263 | return; |
264 | } |
265 | |
266 | state = xenbus_read_driver_state(xdev->xbusd_otherend); |
267 | |
268 | DPRINTK("state is %d, %s, %s" , |
269 | state, xdev->xbusd_otherend_watch.node, vec[XS_WATCH_PATH]); |
270 | if (state == XenbusStateClosed) { |
271 | int error; |
272 | if (xdev->xbusd_type == XENBUS_BACKEND_DEVICE) { |
273 | error = xdev->xbusd_u.b.b_detach( |
274 | xdev->xbusd_u.b.b_cookie); |
275 | if (error) { |
276 | printf("could not detach %s: %d\n" , |
277 | xdev->xbusd_path, error); |
278 | return; |
279 | } |
280 | } else { |
281 | error = config_detach(xdev->xbusd_u.f.f_dev, |
282 | DETACH_FORCE); |
283 | if (error) { |
284 | printf("could not detach %s: %d\n" , |
285 | device_xname(xdev->xbusd_u.f.f_dev), error); |
286 | return; |
287 | } |
288 | } |
289 | xenbus_free_device(xdev); |
290 | return; |
291 | } |
292 | if (xdev->xbusd_otherend_changed) |
293 | xdev->xbusd_otherend_changed( |
294 | (xdev->xbusd_type == XENBUS_BACKEND_DEVICE) ? |
295 | xdev->xbusd_u.b.b_cookie : xdev->xbusd_u.f.f_dev, state); |
296 | } |
297 | |
298 | static int |
299 | watch_otherend(struct xenbus_device *dev) |
300 | { |
301 | free_otherend_watch(dev); |
302 | |
303 | return xenbus_watch_path2(dev, dev->xbusd_otherend, "state" , |
304 | &dev->xbusd_otherend_watch, |
305 | otherend_changed); |
306 | } |
307 | |
308 | static struct xenbus_device * |
309 | xenbus_lookup_device_path(const char *path) |
310 | { |
311 | struct xenbus_device *xbusd; |
312 | |
313 | SLIST_FOREACH(xbusd, &xenbus_device_list, xbusd_entries) { |
314 | if (strcmp(xbusd->xbusd_path, path) == 0) |
315 | return xbusd; |
316 | } |
317 | return NULL; |
318 | } |
319 | |
320 | static int |
321 | xenbus_probe_device_type(const char *path, const char *type, |
322 | int (*create)(struct xenbus_device *)) |
323 | { |
324 | int err, i, pos, msize; |
325 | int *lookup = NULL; |
326 | unsigned long state; |
327 | char **dir; |
328 | unsigned int dir_n = 0; |
329 | struct xenbus_device *xbusd; |
330 | struct xenbusdev_attach_args xa; |
331 | char *ep; |
332 | |
333 | DPRINTK("probe %s type %s" , path, type); |
334 | err = xenbus_directory(NULL, path, "" , &dir_n, &dir); |
335 | DPRINTK("directory err %d dir_n %d" , err, dir_n); |
336 | if (err) |
337 | return err; |
338 | |
339 | /* Only sort frontend devices i.e. create == NULL*/ |
340 | if (dir_n > 1 && create == NULL) { |
341 | int minp; |
342 | unsigned long minv; |
343 | unsigned long *id; |
344 | |
345 | lookup = malloc(sizeof(int) * dir_n, M_DEVBUF, |
346 | M_WAITOK | M_ZERO); |
347 | if (lookup == NULL) |
348 | panic("can't malloc lookup" ); |
349 | |
350 | id = malloc(sizeof(unsigned long) * dir_n, M_DEVBUF, |
351 | M_WAITOK | M_ZERO); |
352 | if (id == NULL) |
353 | panic("can't malloc id" ); |
354 | |
355 | /* Convert string values to numeric; skip invalid */ |
356 | for (i = 0; i < dir_n; i++) { |
357 | /* |
358 | * Add one to differentiate numerical zero from invalid |
359 | * string. Has no effect on sort order. |
360 | */ |
361 | id[i] = strtoul(dir[i], &ep, 10) + 1; |
362 | if (dir[i][0] == '\0' || *ep != '\0') |
363 | id[i] = 0; |
364 | } |
365 | |
366 | /* Build lookup table in ascending order */ |
367 | for (pos = 0; pos < dir_n; ) { |
368 | minv = UINT32_MAX; |
369 | minp = -1; |
370 | for (i = 0; i < dir_n; i++) { |
371 | if (id[i] < minv && id[i] > 0) { |
372 | minv = id[i]; |
373 | minp = i; |
374 | } |
375 | } |
376 | if (minp >= 0) { |
377 | lookup[pos++] = minp; |
378 | id[minp] = 0; |
379 | } |
380 | else |
381 | break; |
382 | } |
383 | |
384 | free(id, M_DEVBUF); |
385 | /* Adjust in case we had to skip non-numeric entries */ |
386 | dir_n = pos; |
387 | } |
388 | |
389 | for (pos = 0; pos < dir_n; pos++) { |
390 | err = 0; |
391 | if (lookup) |
392 | i = lookup[pos]; |
393 | else |
394 | i = pos; |
395 | /* |
396 | * add size of path to size of xenbus_device. xenbus_device |
397 | * already has room for one char in xbusd_path. |
398 | */ |
399 | msize = sizeof(*xbusd) + strlen(path) + strlen(dir[i]) + 2; |
400 | xbusd = malloc(msize, M_DEVBUF, M_WAITOK | M_ZERO); |
401 | if (xbusd == NULL) |
402 | panic("can't malloc xbusd" ); |
403 | |
404 | snprintf(__UNCONST(xbusd->xbusd_path), |
405 | msize - sizeof(*xbusd) + 1, "%s/%s" , path, dir[i]); |
406 | if (xenbus_lookup_device_path(xbusd->xbusd_path) != NULL) { |
407 | /* device already registered */ |
408 | free(xbusd, M_DEVBUF); |
409 | continue; |
410 | } |
411 | err = xenbus_read_ul(NULL, xbusd->xbusd_path, "state" , |
412 | &state, 10); |
413 | if (err) { |
414 | printf("xenbus: can't get state " |
415 | "for %s (%d)\n" , xbusd->xbusd_path, err); |
416 | free(xbusd, M_DEVBUF); |
417 | err = 0; |
418 | continue; |
419 | } |
420 | if (state != XenbusStateInitialising) { |
421 | /* device is not new */ |
422 | free(xbusd, M_DEVBUF); |
423 | continue; |
424 | } |
425 | |
426 | xbusd->xbusd_otherend_watch.xbw_dev = xbusd; |
427 | DPRINTK("xenbus_probe_device_type probe %s\n" , |
428 | xbusd->xbusd_path); |
429 | if (create != NULL) { |
430 | xbusd->xbusd_type = XENBUS_BACKEND_DEVICE; |
431 | err = read_frontend_details(xbusd); |
432 | if (err != 0) { |
433 | printf("xenbus: can't get frontend details " |
434 | "for %s (%d)\n" , xbusd->xbusd_path, err); |
435 | break; |
436 | } |
437 | if (create(xbusd)) { |
438 | free(xbusd, M_DEVBUF); |
439 | continue; |
440 | } |
441 | } else { |
442 | xbusd->xbusd_type = XENBUS_FRONTEND_DEVICE; |
443 | xa.xa_xbusd = xbusd; |
444 | xa.xa_type = type; |
445 | xa.xa_id = strtoul(dir[i], &ep, 0); |
446 | if (dir[i][0] == '\0' || *ep != '\0') { |
447 | printf("xenbus device type %s: id %s is not a" |
448 | " number\n" , type, dir[i]); |
449 | err = EFTYPE; |
450 | free(xbusd, M_DEVBUF); |
451 | break; |
452 | } |
453 | err = read_backend_details(xbusd); |
454 | if (err != 0) { |
455 | printf("xenbus: can't get backend details " |
456 | "for %s (%d)\n" , xbusd->xbusd_path, err); |
457 | break; |
458 | } |
459 | xbusd->xbusd_u.f.f_dev = config_found_ia(xenbus_dev, |
460 | "xenbus" , &xa, xenbus_print); |
461 | if (xbusd->xbusd_u.f.f_dev == NULL) { |
462 | free(xbusd, M_DEVBUF); |
463 | continue; |
464 | } |
465 | } |
466 | SLIST_INSERT_HEAD(&xenbus_device_list, |
467 | xbusd, xbusd_entries); |
468 | watch_otherend(xbusd); |
469 | } |
470 | free(dir, M_DEVBUF); |
471 | if (lookup) |
472 | free(lookup, M_DEVBUF); |
473 | |
474 | return err; |
475 | } |
476 | |
477 | static int |
478 | xenbus_print(void *aux, const char *pnp) |
479 | { |
480 | struct xenbusdev_attach_args *xa = aux; |
481 | |
482 | if (pnp) { |
483 | if (strcmp(xa->xa_type, "vbd" ) == 0) |
484 | aprint_normal("xbd" ); |
485 | else if (strcmp(xa->xa_type, "vif" ) == 0) |
486 | aprint_normal("xennet" ); |
487 | else if (strcmp(xa->xa_type, "balloon" ) == 0) |
488 | aprint_normal("balloon" ); |
489 | else |
490 | aprint_normal("unknown type %s" , xa->xa_type); |
491 | aprint_normal(" at %s" , pnp); |
492 | } |
493 | aprint_normal(" id %d" , xa->xa_id); |
494 | return(UNCONF); |
495 | } |
496 | |
497 | static int |
498 | xenbus_probe_frontends(void) |
499 | { |
500 | int err; |
501 | char **dir; |
502 | unsigned int i, dir_n; |
503 | char path[30]; |
504 | |
505 | DPRINTK("probe device" ); |
506 | err = xenbus_directory(NULL, "device" , "" , &dir_n, &dir); |
507 | DPRINTK("directory err %d dir_n %d" , err, dir_n); |
508 | if (err) |
509 | return err; |
510 | |
511 | for (i = 0; i < dir_n; i++) { |
512 | /* |
513 | * console is configured through xen_start_info when |
514 | * xencons is attaching to hypervisor, so avoid console |
515 | * probing when configuring xenbus devices |
516 | */ |
517 | if (strcmp(dir[i], "console" ) == 0) |
518 | continue; |
519 | |
520 | snprintf(path, sizeof(path), "device/%s" , dir[i]); |
521 | err = xenbus_probe_device_type(path, dir[i], NULL); |
522 | if (err) |
523 | break; |
524 | } |
525 | free(dir, M_DEVBUF); |
526 | return err; |
527 | } |
528 | |
529 | static int |
530 | xenbus_probe_backends(void) |
531 | { |
532 | int err; |
533 | char **dirt, **dirid; |
534 | unsigned int type, id, dirt_n, dirid_n; |
535 | char path[30]; |
536 | struct xenbus_backend_driver *xbakd; |
537 | |
538 | DPRINTK("probe backend" ); |
539 | err = xenbus_directory(NULL, "backend" , "" , &dirt_n, &dirt); |
540 | DPRINTK("directory err %d dirt_n %d" , err, dirt_n); |
541 | if (err) |
542 | return err; |
543 | |
544 | for (type = 0; type < dirt_n; type++) { |
545 | SLIST_FOREACH(xbakd, &xenbus_backend_driver_list, |
546 | xbakd_entries) { |
547 | if (strcmp(dirt[type], xbakd->xbakd_type) == 0) |
548 | break; |
549 | } |
550 | if (xbakd == NULL) |
551 | continue; |
552 | err = xenbus_directory(NULL, "backend" , dirt[type], |
553 | &dirid_n, &dirid); |
554 | DPRINTK("directory backend/%s err %d dirid_n %d" , |
555 | dirt[type], err, dirid_n); |
556 | if (err) { |
557 | free(dirt, M_DEVBUF); /* to be checked */ |
558 | return err; |
559 | } |
560 | for (id = 0; id < dirid_n; id++) { |
561 | snprintf(path, sizeof(path), "backend/%s/%s" , |
562 | dirt[type], dirid[id]); |
563 | err = xenbus_probe_device_type(path, dirt[type], |
564 | xbakd->xbakd_create); |
565 | if (err) |
566 | break; |
567 | } |
568 | free(dirid, M_DEVBUF); |
569 | } |
570 | free(dirt, M_DEVBUF); |
571 | return err; |
572 | } |
573 | |
574 | int |
575 | xenbus_free_device(struct xenbus_device *xbusd) |
576 | { |
577 | KASSERT(xenbus_lookup_device_path(xbusd->xbusd_path) == xbusd); |
578 | SLIST_REMOVE(&xenbus_device_list, xbusd, xenbus_device, xbusd_entries); |
579 | free_otherend_watch(xbusd); |
580 | free_otherend_details(xbusd); |
581 | xenbus_switch_state(xbusd, NULL, XenbusStateClosed); |
582 | free(xbusd, M_DEVBUF); |
583 | return 0; |
584 | } |
585 | |
586 | static void |
587 | frontend_changed(struct xenbus_watch *watch, |
588 | const char **vec, unsigned int len) |
589 | { |
590 | DPRINTK("frontend_changed %s\n" , vec[XS_WATCH_PATH]); |
591 | xenbus_probe_frontends(); |
592 | } |
593 | |
594 | static void |
595 | backend_changed(struct xenbus_watch *watch, |
596 | const char **vec, unsigned int len) |
597 | { |
598 | DPRINTK("backend_changed %s\n" , vec[XS_WATCH_PATH]); |
599 | xenbus_probe_backends(); |
600 | } |
601 | |
602 | |
603 | /* We watch for devices appearing and vanishing. */ |
604 | static struct xenbus_watch fe_watch; |
605 | |
606 | static struct xenbus_watch be_watch; |
607 | |
608 | /* A flag to determine if xenstored is 'ready' (i.e. has started) */ |
609 | int xenstored_ready = 0; |
610 | |
611 | void |
612 | xenbus_probe(void *unused) |
613 | { |
614 | struct xenbusdev_attach_args balloon_xa = { |
615 | .xa_id = 0, |
616 | .xa_type = "balloon" |
617 | }; |
618 | |
619 | KASSERT((xenstored_ready > 0)); |
620 | |
621 | /* Enumerate devices in xenstore. */ |
622 | xenbus_probe_frontends(); |
623 | xenbus_probe_backends(); |
624 | |
625 | /* Watch for changes. */ |
626 | fe_watch.node = malloc(strlen("device" + 1), M_DEVBUF, M_NOWAIT); |
627 | strcpy(fe_watch.node, "device" ); |
628 | fe_watch.xbw_callback = frontend_changed; |
629 | register_xenbus_watch(&fe_watch); |
630 | be_watch.node = malloc(strlen("backend" + 1), M_DEVBUF, M_NOWAIT); |
631 | strcpy(be_watch.node, "backend" ); |
632 | be_watch.xbw_callback = backend_changed; |
633 | register_xenbus_watch(&be_watch); |
634 | |
635 | /* attach balloon. */ |
636 | config_found_ia(xenbus_dev, "xenbus" , &balloon_xa, xenbus_print); |
637 | |
638 | shutdown_xenbus_setup(); |
639 | |
640 | /* Notify others that xenstore is up */ |
641 | //notifier_call_chain(&xenstore_chain, 0, NULL); |
642 | } |
643 | |
644 | static void |
645 | xenbus_probe_init(void *unused) |
646 | { |
647 | int err = 0; |
648 | bool dom0; |
649 | vaddr_t page = 0; |
650 | |
651 | DPRINTK("" ); |
652 | |
653 | SLIST_INIT(&xenbus_device_list); |
654 | |
655 | /* |
656 | ** Domain0 doesn't have a store_evtchn or store_mfn yet. |
657 | */ |
658 | dom0 = xendomain_is_dom0(); |
659 | if (dom0) { |
660 | #if defined(DOM0OPS) |
661 | paddr_t ma; |
662 | evtchn_op_t op = { .cmd = 0 }; |
663 | |
664 | /* Allocate page. */ |
665 | page = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, |
666 | UVM_KMF_ZERO | UVM_KMF_WIRED); |
667 | if (!page) |
668 | panic("can't get xenstore page" ); |
669 | |
670 | (void)pmap_extract_ma(pmap_kernel(), page, &ma); |
671 | xen_start_info.store_mfn = ma >> PAGE_SHIFT; |
672 | xenstore_interface = (void *)page; |
673 | |
674 | /* Next allocate a local port which xenstored can bind to */ |
675 | op.cmd = EVTCHNOP_alloc_unbound; |
676 | op.u.alloc_unbound.dom = DOMID_SELF; |
677 | op.u.alloc_unbound.remote_dom = 0; |
678 | |
679 | err = HYPERVISOR_event_channel_op(&op); |
680 | if (err) { |
681 | aprint_error_dev(xenbus_dev, |
682 | "can't register xenstore event\n" ); |
683 | goto err0; |
684 | } |
685 | |
686 | xen_start_info.store_evtchn = op.u.alloc_unbound.port; |
687 | |
688 | DELAY(1000); |
689 | #else /* DOM0OPS */ |
690 | kthread_exit(0); /* can't get a working xenstore in this case */ |
691 | #endif /* DOM0OPS */ |
692 | } |
693 | |
694 | /* Publish xenbus and Xenstore info in /kern/xen */ |
695 | xenbus_kernfs_init(); |
696 | |
697 | /* register event handler */ |
698 | xb_init_comms(xenbus_dev); |
699 | |
700 | /* Initialize the interface to xenstore. */ |
701 | err = xs_init(xenbus_dev); |
702 | if (err) { |
703 | aprint_error_dev(xenbus_dev, |
704 | "Error initializing xenstore comms: %i\n" , err); |
705 | goto err0; |
706 | } |
707 | |
708 | if (!dom0) { |
709 | xenstored_ready = 1; |
710 | xenbus_probe(NULL); |
711 | } |
712 | |
713 | DPRINTK("done" ); |
714 | config_pending_decr(xenbus_dev); |
715 | #ifdef DOM0OPS |
716 | if (dom0) { |
717 | int s; |
718 | s = spltty(); |
719 | while (xenstored_ready == 0) { |
720 | tsleep(&xenstored_ready, PRIBIO, "xsready" , 0); |
721 | xenbus_probe(NULL); |
722 | } |
723 | splx(s); |
724 | } |
725 | #endif |
726 | kthread_exit(0); |
727 | |
728 | err0: |
729 | if (page) |
730 | uvm_km_free(kernel_map, page, PAGE_SIZE, |
731 | UVM_KMF_ZERO | UVM_KMF_WIRED); |
732 | kthread_exit(err); |
733 | } |
734 | |
735 | /* |
736 | * Local variables: |
737 | * c-file-style: "linux" |
738 | * indent-tabs-mode: t |
739 | * c-indent-level: 8 |
740 | * c-basic-offset: 8 |
741 | * tab-width: 8 |
742 | * End: |
743 | */ |
744 | |