diff options
author | Stefano Stabellini <stefano.stabellini@eu.citrix.com> | 2012-01-30 11:02:31 -0500 |
---|---|---|
committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2012-03-13 19:23:41 -0400 |
commit | 02e19f9c7cacfb33d7b2f5cace7972fa60f92319 (patch) | |
tree | c62a54d17a617d648cb1213fe6844d1687a54988 /drivers/tty/hvc | |
parent | eb5ef07151ba3c3cb4bcef0c8f146ff1115eaa55 (diff) |
hvc_xen: implement multiconsole support
This patch implements support for multiple consoles:
consoles other than the first one are setup using the traditional xenbus
and grant-table based mechanism.
We use a list to keep track of the allocated consoles, we don't
expect too many of them anyway.
Changes in v3:
- call hvc_remove before removing the console from xenconsoles;
- do not lock xencons_lock twice in the destruction path;
- use the DEFINE_XENBUS_DRIVER macro.
Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Diffstat (limited to 'drivers/tty/hvc')
-rw-r--r-- | drivers/tty/hvc/hvc_xen.c | 435 |
1 files changed, 377 insertions, 58 deletions
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index d5000aa02864..26090c736bcf 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/err.h> | 23 | #include <linux/err.h> |
24 | #include <linux/init.h> | 24 | #include <linux/init.h> |
25 | #include <linux/types.h> | 25 | #include <linux/types.h> |
26 | #include <linux/list.h> | ||
26 | 27 | ||
27 | #include <asm/io.h> | 28 | #include <asm/io.h> |
28 | #include <asm/xen/hypervisor.h> | 29 | #include <asm/xen/hypervisor.h> |
@@ -30,47 +31,67 @@ | |||
30 | #include <xen/xen.h> | 31 | #include <xen/xen.h> |
31 | #include <xen/interface/xen.h> | 32 | #include <xen/interface/xen.h> |
32 | #include <xen/hvm.h> | 33 | #include <xen/hvm.h> |
34 | #include <xen/grant_table.h> | ||
33 | #include <xen/page.h> | 35 | #include <xen/page.h> |
34 | #include <xen/events.h> | 36 | #include <xen/events.h> |
35 | #include <xen/interface/io/console.h> | 37 | #include <xen/interface/io/console.h> |
36 | #include <xen/hvc-console.h> | 38 | #include <xen/hvc-console.h> |
39 | #include <xen/xenbus.h> | ||
37 | 40 | ||
38 | #include "hvc_console.h" | 41 | #include "hvc_console.h" |
39 | 42 | ||
40 | #define HVC_COOKIE 0x58656e /* "Xen" in hex */ | 43 | #define HVC_COOKIE 0x58656e /* "Xen" in hex */ |
41 | 44 | ||
42 | static struct hvc_struct *hvc; | 45 | struct xencons_info { |
43 | static int xencons_irq; | 46 | struct list_head list; |
47 | struct xenbus_device *xbdev; | ||
48 | struct xencons_interface *intf; | ||
49 | unsigned int evtchn; | ||
50 | struct hvc_struct *hvc; | ||
51 | int irq; | ||
52 | int vtermno; | ||
53 | grant_ref_t gntref; | ||
54 | }; | ||
55 | |||
56 | static LIST_HEAD(xenconsoles); | ||
57 | static DEFINE_SPINLOCK(xencons_lock); | ||
58 | static struct xenbus_driver xencons_driver; | ||
44 | 59 | ||
45 | /* ------------------------------------------------------------------ */ | 60 | /* ------------------------------------------------------------------ */ |
46 | 61 | ||
47 | static unsigned long console_pfn = ~0ul; | 62 | static struct xencons_info *vtermno_to_xencons(int vtermno) |
48 | static unsigned int console_evtchn = ~0ul; | 63 | { |
49 | static struct xencons_interface *xencons_if = NULL; | 64 | struct xencons_info *entry, *n, *ret = NULL; |
65 | |||
66 | if (list_empty(&xenconsoles)) | ||
67 | return NULL; | ||
50 | 68 | ||
51 | static inline struct xencons_interface *xencons_interface(void) | 69 | list_for_each_entry_safe(entry, n, &xenconsoles, list) { |
70 | if (entry->vtermno == vtermno) { | ||
71 | ret = entry; | ||
72 | break; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | return ret; | ||
77 | } | ||
78 | |||
79 | static inline int xenbus_devid_to_vtermno(int devid) | ||
52 | { | 80 | { |
53 | if (xencons_if != NULL) | 81 | return devid + HVC_COOKIE; |
54 | return xencons_if; | ||
55 | if (console_pfn == ~0ul) | ||
56 | return mfn_to_virt(xen_start_info->console.domU.mfn); | ||
57 | else | ||
58 | return __va(console_pfn << PAGE_SHIFT); | ||
59 | } | 82 | } |
60 | 83 | ||
61 | static inline void notify_daemon(void) | 84 | static inline void notify_daemon(struct xencons_info *cons) |
62 | { | 85 | { |
63 | /* Use evtchn: this is called early, before irq is set up. */ | 86 | /* Use evtchn: this is called early, before irq is set up. */ |
64 | if (console_evtchn == ~0ul) | 87 | notify_remote_via_evtchn(cons->evtchn); |
65 | notify_remote_via_evtchn(xen_start_info->console.domU.evtchn); | ||
66 | else | ||
67 | notify_remote_via_evtchn(console_evtchn); | ||
68 | } | 88 | } |
69 | 89 | ||
70 | static int __write_console(const char *data, int len) | 90 | static int __write_console(struct xencons_info *xencons, |
91 | const char *data, int len) | ||
71 | { | 92 | { |
72 | struct xencons_interface *intf = xencons_interface(); | ||
73 | XENCONS_RING_IDX cons, prod; | 93 | XENCONS_RING_IDX cons, prod; |
94 | struct xencons_interface *intf = xencons->intf; | ||
74 | int sent = 0; | 95 | int sent = 0; |
75 | 96 | ||
76 | cons = intf->out_cons; | 97 | cons = intf->out_cons; |
@@ -85,13 +106,16 @@ static int __write_console(const char *data, int len) | |||
85 | intf->out_prod = prod; | 106 | intf->out_prod = prod; |
86 | 107 | ||
87 | if (sent) | 108 | if (sent) |
88 | notify_daemon(); | 109 | notify_daemon(xencons); |
89 | return sent; | 110 | return sent; |
90 | } | 111 | } |
91 | 112 | ||
92 | static int domU_write_console(uint32_t vtermno, const char *data, int len) | 113 | static int domU_write_console(uint32_t vtermno, const char *data, int len) |
93 | { | 114 | { |
94 | int ret = len; | 115 | int ret = len; |
116 | struct xencons_info *cons = vtermno_to_xencons(vtermno); | ||
117 | if (cons == NULL) | ||
118 | return -EINVAL; | ||
95 | 119 | ||
96 | /* | 120 | /* |
97 | * Make sure the whole buffer is emitted, polling if | 121 | * Make sure the whole buffer is emitted, polling if |
@@ -100,7 +124,7 @@ static int domU_write_console(uint32_t vtermno, const char *data, int len) | |||
100 | * kernel is crippled. | 124 | * kernel is crippled. |
101 | */ | 125 | */ |
102 | while (len) { | 126 | while (len) { |
103 | int sent = __write_console(data, len); | 127 | int sent = __write_console(cons, data, len); |
104 | 128 | ||
105 | data += sent; | 129 | data += sent; |
106 | len -= sent; | 130 | len -= sent; |
@@ -114,9 +138,13 @@ static int domU_write_console(uint32_t vtermno, const char *data, int len) | |||
114 | 138 | ||
115 | static int domU_read_console(uint32_t vtermno, char *buf, int len) | 139 | static int domU_read_console(uint32_t vtermno, char *buf, int len) |
116 | { | 140 | { |
117 | struct xencons_interface *intf = xencons_interface(); | 141 | struct xencons_interface *intf; |
118 | XENCONS_RING_IDX cons, prod; | 142 | XENCONS_RING_IDX cons, prod; |
119 | int recv = 0; | 143 | int recv = 0; |
144 | struct xencons_info *xencons = vtermno_to_xencons(vtermno); | ||
145 | if (xencons == NULL) | ||
146 | return -EINVAL; | ||
147 | intf = xencons->intf; | ||
120 | 148 | ||
121 | cons = intf->in_cons; | 149 | cons = intf->in_cons; |
122 | prod = intf->in_prod; | 150 | prod = intf->in_prod; |
@@ -129,7 +157,7 @@ static int domU_read_console(uint32_t vtermno, char *buf, int len) | |||
129 | mb(); /* read ring before consuming */ | 157 | mb(); /* read ring before consuming */ |
130 | intf->in_cons = cons; | 158 | intf->in_cons = cons; |
131 | 159 | ||
132 | notify_daemon(); | 160 | notify_daemon(xencons); |
133 | return recv; | 161 | return recv; |
134 | } | 162 | } |
135 | 163 | ||
@@ -172,78 +200,359 @@ static int xen_hvm_console_init(void) | |||
172 | int r; | 200 | int r; |
173 | uint64_t v = 0; | 201 | uint64_t v = 0; |
174 | unsigned long mfn; | 202 | unsigned long mfn; |
203 | struct xencons_info *info; | ||
175 | 204 | ||
176 | if (!xen_hvm_domain()) | 205 | if (!xen_hvm_domain()) |
177 | return -ENODEV; | 206 | return -ENODEV; |
178 | 207 | ||
179 | if (xencons_if != NULL) | 208 | info = vtermno_to_xencons(HVC_COOKIE); |
180 | return -EBUSY; | 209 | if (!info) { |
210 | info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); | ||
211 | if (!info) | ||
212 | return -ENOMEM; | ||
213 | } | ||
214 | |||
215 | /* already configured */ | ||
216 | if (info->intf != NULL) | ||
217 | return 0; | ||
181 | 218 | ||
182 | r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v); | 219 | r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v); |
183 | if (r < 0) | 220 | if (r < 0) { |
221 | kfree(info); | ||
184 | return -ENODEV; | 222 | return -ENODEV; |
185 | console_evtchn = v; | 223 | } |
224 | info->evtchn = v; | ||
186 | hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v); | 225 | hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v); |
187 | if (r < 0) | 226 | if (r < 0) { |
227 | kfree(info); | ||
188 | return -ENODEV; | 228 | return -ENODEV; |
229 | } | ||
189 | mfn = v; | 230 | mfn = v; |
190 | xencons_if = ioremap(mfn << PAGE_SHIFT, PAGE_SIZE); | 231 | info->intf = ioremap(mfn << PAGE_SHIFT, PAGE_SIZE); |
191 | if (xencons_if == NULL) | 232 | if (info->intf == NULL) { |
233 | kfree(info); | ||
234 | return -ENODEV; | ||
235 | } | ||
236 | info->vtermno = HVC_COOKIE; | ||
237 | |||
238 | spin_lock(&xencons_lock); | ||
239 | list_add_tail(&info->list, &xenconsoles); | ||
240 | spin_unlock(&xencons_lock); | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | static int xen_pv_console_init(void) | ||
246 | { | ||
247 | struct xencons_info *info; | ||
248 | |||
249 | if (!xen_pv_domain()) | ||
250 | return -ENODEV; | ||
251 | |||
252 | if (!xen_start_info->console.domU.evtchn) | ||
253 | return -ENODEV; | ||
254 | |||
255 | info = vtermno_to_xencons(HVC_COOKIE); | ||
256 | if (!info) { | ||
257 | info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); | ||
258 | if (!info) | ||
259 | return -ENOMEM; | ||
260 | } | ||
261 | |||
262 | /* already configured */ | ||
263 | if (info->intf != NULL) | ||
264 | return 0; | ||
265 | |||
266 | info->evtchn = xen_start_info->console.domU.evtchn; | ||
267 | info->intf = mfn_to_virt(xen_start_info->console.domU.mfn); | ||
268 | info->vtermno = HVC_COOKIE; | ||
269 | |||
270 | spin_lock(&xencons_lock); | ||
271 | list_add_tail(&info->list, &xenconsoles); | ||
272 | spin_unlock(&xencons_lock); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int xen_initial_domain_console_init(void) | ||
278 | { | ||
279 | struct xencons_info *info; | ||
280 | |||
281 | if (!xen_initial_domain()) | ||
192 | return -ENODEV; | 282 | return -ENODEV; |
193 | 283 | ||
284 | info = vtermno_to_xencons(HVC_COOKIE); | ||
285 | if (!info) { | ||
286 | info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); | ||
287 | if (!info) | ||
288 | return -ENOMEM; | ||
289 | } | ||
290 | |||
291 | info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); | ||
292 | info->vtermno = HVC_COOKIE; | ||
293 | |||
294 | spin_lock(&xencons_lock); | ||
295 | list_add_tail(&info->list, &xenconsoles); | ||
296 | spin_unlock(&xencons_lock); | ||
297 | |||
194 | return 0; | 298 | return 0; |
195 | } | 299 | } |
196 | 300 | ||
197 | static int __init xen_hvc_init(void) | 301 | static int __init xen_hvc_init(void) |
198 | { | 302 | { |
199 | struct hvc_struct *hp; | ||
200 | struct hv_ops *ops; | ||
201 | int r; | 303 | int r; |
304 | struct xencons_info *info; | ||
305 | const struct hv_ops *ops; | ||
202 | 306 | ||
203 | if (!xen_domain()) | 307 | if (!xen_domain()) |
204 | return -ENODEV; | 308 | return -ENODEV; |
205 | 309 | ||
206 | if (xen_initial_domain()) { | 310 | if (xen_initial_domain()) { |
207 | ops = &dom0_hvc_ops; | 311 | ops = &dom0_hvc_ops; |
208 | xencons_irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); | 312 | r = xen_initial_domain_console_init(); |
313 | if (r < 0) | ||
314 | return r; | ||
315 | info = vtermno_to_xencons(HVC_COOKIE); | ||
209 | } else { | 316 | } else { |
210 | ops = &domU_hvc_ops; | 317 | ops = &domU_hvc_ops; |
211 | if (xen_pv_domain()) { | 318 | if (xen_hvm_domain()) |
212 | if (!xen_start_info->console.domU.evtchn) | ||
213 | return -ENODEV; | ||
214 | console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn); | ||
215 | console_evtchn = xen_start_info->console.domU.evtchn; | ||
216 | } else { | ||
217 | r = xen_hvm_console_init(); | 319 | r = xen_hvm_console_init(); |
218 | if (r < 0) | ||
219 | return r; | ||
220 | } | ||
221 | xencons_irq = bind_evtchn_to_irq(console_evtchn); | ||
222 | if (xencons_irq < 0) | ||
223 | xencons_irq = 0; /* NO_IRQ */ | ||
224 | else | 320 | else |
225 | irq_set_noprobe(xencons_irq); | 321 | r = xen_pv_console_init(); |
322 | if (r < 0) | ||
323 | return r; | ||
324 | |||
325 | info = vtermno_to_xencons(HVC_COOKIE); | ||
326 | info->irq = bind_evtchn_to_irq(info->evtchn); | ||
327 | } | ||
328 | if (info->irq < 0) | ||
329 | info->irq = 0; /* NO_IRQ */ | ||
330 | else | ||
331 | irq_set_noprobe(info->irq); | ||
332 | |||
333 | info->hvc = hvc_alloc(HVC_COOKIE, info->irq, ops, 256); | ||
334 | if (IS_ERR(info->hvc)) { | ||
335 | r = PTR_ERR(info->hvc); | ||
336 | spin_lock(&xencons_lock); | ||
337 | list_del(&info->list); | ||
338 | spin_unlock(&xencons_lock); | ||
339 | if (info->irq) | ||
340 | unbind_from_irqhandler(info->irq, NULL); | ||
341 | kfree(info); | ||
342 | return r; | ||
226 | } | 343 | } |
227 | 344 | ||
228 | hp = hvc_alloc(HVC_COOKIE, xencons_irq, ops, 256); | 345 | return xenbus_register_frontend(&xencons_driver); |
229 | if (IS_ERR(hp)) | 346 | } |
230 | return PTR_ERR(hp); | ||
231 | 347 | ||
232 | hvc = hp; | 348 | void xen_console_resume(void) |
349 | { | ||
350 | struct xencons_info *info = vtermno_to_xencons(HVC_COOKIE); | ||
351 | if (info != NULL && info->irq) | ||
352 | rebind_evtchn_irq(info->evtchn, info->irq); | ||
353 | } | ||
354 | |||
355 | static void xencons_disconnect_backend(struct xencons_info *info) | ||
356 | { | ||
357 | if (info->irq > 0) | ||
358 | unbind_from_irqhandler(info->irq, NULL); | ||
359 | info->irq = 0; | ||
360 | if (info->evtchn > 0) | ||
361 | xenbus_free_evtchn(info->xbdev, info->evtchn); | ||
362 | info->evtchn = 0; | ||
363 | if (info->gntref > 0) | ||
364 | gnttab_free_grant_references(info->gntref); | ||
365 | info->gntref = 0; | ||
366 | if (info->hvc != NULL) | ||
367 | hvc_remove(info->hvc); | ||
368 | info->hvc = NULL; | ||
369 | } | ||
233 | 370 | ||
371 | static void xencons_free(struct xencons_info *info) | ||
372 | { | ||
373 | free_page((unsigned long)info->intf); | ||
374 | info->intf = NULL; | ||
375 | info->vtermno = 0; | ||
376 | kfree(info); | ||
377 | } | ||
378 | |||
379 | static int xen_console_remove(struct xencons_info *info) | ||
380 | { | ||
381 | xencons_disconnect_backend(info); | ||
382 | spin_lock(&xencons_lock); | ||
383 | list_del(&info->list); | ||
384 | spin_unlock(&xencons_lock); | ||
385 | if (info->xbdev != NULL) | ||
386 | xencons_free(info); | ||
387 | else { | ||
388 | if (xen_hvm_domain()) | ||
389 | iounmap(info->intf); | ||
390 | kfree(info); | ||
391 | } | ||
234 | return 0; | 392 | return 0; |
235 | } | 393 | } |
236 | 394 | ||
237 | void xen_console_resume(void) | 395 | static int xencons_remove(struct xenbus_device *dev) |
396 | { | ||
397 | return xen_console_remove(dev_get_drvdata(&dev->dev)); | ||
398 | } | ||
399 | |||
400 | static int xencons_connect_backend(struct xenbus_device *dev, | ||
401 | struct xencons_info *info) | ||
402 | { | ||
403 | int ret, evtchn, devid, ref, irq; | ||
404 | struct xenbus_transaction xbt; | ||
405 | grant_ref_t gref_head; | ||
406 | unsigned long mfn; | ||
407 | |||
408 | ret = xenbus_alloc_evtchn(dev, &evtchn); | ||
409 | if (ret) | ||
410 | return ret; | ||
411 | info->evtchn = evtchn; | ||
412 | irq = bind_evtchn_to_irq(evtchn); | ||
413 | if (irq < 0) | ||
414 | return irq; | ||
415 | info->irq = irq; | ||
416 | devid = dev->nodename[strlen(dev->nodename) - 1] - '0'; | ||
417 | info->hvc = hvc_alloc(xenbus_devid_to_vtermno(devid), | ||
418 | irq, &domU_hvc_ops, 256); | ||
419 | if (IS_ERR(info->hvc)) | ||
420 | return PTR_ERR(info->hvc); | ||
421 | if (xen_pv_domain()) | ||
422 | mfn = virt_to_mfn(info->intf); | ||
423 | else | ||
424 | mfn = __pa(info->intf) >> PAGE_SHIFT; | ||
425 | ret = gnttab_alloc_grant_references(1, &gref_head); | ||
426 | if (ret < 0) | ||
427 | return ret; | ||
428 | info->gntref = gref_head; | ||
429 | ref = gnttab_claim_grant_reference(&gref_head); | ||
430 | if (ref < 0) | ||
431 | return ref; | ||
432 | gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id, | ||
433 | mfn, 0); | ||
434 | |||
435 | again: | ||
436 | ret = xenbus_transaction_start(&xbt); | ||
437 | if (ret) { | ||
438 | xenbus_dev_fatal(dev, ret, "starting transaction"); | ||
439 | return ret; | ||
440 | } | ||
441 | ret = xenbus_printf(xbt, dev->nodename, "ring-ref", "%d", ref); | ||
442 | if (ret) | ||
443 | goto error_xenbus; | ||
444 | ret = xenbus_printf(xbt, dev->nodename, "port", "%u", | ||
445 | evtchn); | ||
446 | if (ret) | ||
447 | goto error_xenbus; | ||
448 | ret = xenbus_printf(xbt, dev->nodename, "type", "ioemu"); | ||
449 | if (ret) | ||
450 | goto error_xenbus; | ||
451 | ret = xenbus_transaction_end(xbt, 0); | ||
452 | if (ret) { | ||
453 | if (ret == -EAGAIN) | ||
454 | goto again; | ||
455 | xenbus_dev_fatal(dev, ret, "completing transaction"); | ||
456 | return ret; | ||
457 | } | ||
458 | |||
459 | xenbus_switch_state(dev, XenbusStateInitialised); | ||
460 | return 0; | ||
461 | |||
462 | error_xenbus: | ||
463 | xenbus_transaction_end(xbt, 1); | ||
464 | xenbus_dev_fatal(dev, ret, "writing xenstore"); | ||
465 | return ret; | ||
466 | } | ||
467 | |||
468 | static int __devinit xencons_probe(struct xenbus_device *dev, | ||
469 | const struct xenbus_device_id *id) | ||
470 | { | ||
471 | int ret, devid; | ||
472 | struct xencons_info *info; | ||
473 | |||
474 | devid = dev->nodename[strlen(dev->nodename) - 1] - '0'; | ||
475 | if (devid == 0) | ||
476 | return -ENODEV; | ||
477 | |||
478 | info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); | ||
479 | if (!info) | ||
480 | goto error_nomem; | ||
481 | dev_set_drvdata(&dev->dev, info); | ||
482 | info->xbdev = dev; | ||
483 | info->vtermno = xenbus_devid_to_vtermno(devid); | ||
484 | info->intf = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); | ||
485 | if (!info->intf) | ||
486 | goto error_nomem; | ||
487 | |||
488 | ret = xencons_connect_backend(dev, info); | ||
489 | if (ret < 0) | ||
490 | goto error; | ||
491 | spin_lock(&xencons_lock); | ||
492 | list_add_tail(&info->list, &xenconsoles); | ||
493 | spin_unlock(&xencons_lock); | ||
494 | |||
495 | return 0; | ||
496 | |||
497 | error_nomem: | ||
498 | ret = -ENOMEM; | ||
499 | xenbus_dev_fatal(dev, ret, "allocating device memory"); | ||
500 | error: | ||
501 | xencons_disconnect_backend(info); | ||
502 | xencons_free(info); | ||
503 | return ret; | ||
504 | } | ||
505 | |||
506 | static int xencons_resume(struct xenbus_device *dev) | ||
238 | { | 507 | { |
239 | if (xencons_irq) | 508 | struct xencons_info *info = dev_get_drvdata(&dev->dev); |
240 | rebind_evtchn_irq(console_evtchn, xencons_irq); | 509 | |
510 | xencons_disconnect_backend(info); | ||
511 | memset(info->intf, 0, PAGE_SIZE); | ||
512 | return xencons_connect_backend(dev, info); | ||
241 | } | 513 | } |
242 | 514 | ||
515 | static void xencons_backend_changed(struct xenbus_device *dev, | ||
516 | enum xenbus_state backend_state) | ||
517 | { | ||
518 | switch (backend_state) { | ||
519 | case XenbusStateReconfiguring: | ||
520 | case XenbusStateReconfigured: | ||
521 | case XenbusStateInitialising: | ||
522 | case XenbusStateInitialised: | ||
523 | case XenbusStateUnknown: | ||
524 | case XenbusStateClosed: | ||
525 | break; | ||
526 | |||
527 | case XenbusStateInitWait: | ||
528 | break; | ||
529 | |||
530 | case XenbusStateConnected: | ||
531 | xenbus_switch_state(dev, XenbusStateConnected); | ||
532 | break; | ||
533 | |||
534 | case XenbusStateClosing: | ||
535 | xenbus_frontend_closed(dev); | ||
536 | break; | ||
537 | } | ||
538 | } | ||
539 | |||
540 | static const struct xenbus_device_id xencons_ids[] = { | ||
541 | { "console" }, | ||
542 | { "" } | ||
543 | }; | ||
544 | |||
545 | |||
243 | static void __exit xen_hvc_fini(void) | 546 | static void __exit xen_hvc_fini(void) |
244 | { | 547 | { |
245 | if (hvc) | 548 | struct xencons_info *entry, *next; |
246 | hvc_remove(hvc); | 549 | |
550 | if (list_empty(&xenconsoles)) | ||
551 | return; | ||
552 | |||
553 | list_for_each_entry_safe(entry, next, &xenconsoles, list) { | ||
554 | xen_console_remove(entry); | ||
555 | } | ||
247 | } | 556 | } |
248 | 557 | ||
249 | static int xen_cons_init(void) | 558 | static int xen_cons_init(void) |
@@ -256,18 +565,28 @@ static int xen_cons_init(void) | |||
256 | if (xen_initial_domain()) | 565 | if (xen_initial_domain()) |
257 | ops = &dom0_hvc_ops; | 566 | ops = &dom0_hvc_ops; |
258 | else { | 567 | else { |
568 | int r; | ||
259 | ops = &domU_hvc_ops; | 569 | ops = &domU_hvc_ops; |
260 | 570 | ||
261 | if (xen_pv_domain()) | 571 | if (xen_hvm_domain()) |
262 | console_evtchn = xen_start_info->console.domU.evtchn; | 572 | r = xen_hvm_console_init(); |
263 | else | 573 | else |
264 | xen_hvm_console_init(); | 574 | r = xen_pv_console_init(); |
575 | if (r < 0) | ||
576 | return r; | ||
265 | } | 577 | } |
266 | 578 | ||
267 | hvc_instantiate(HVC_COOKIE, 0, ops); | 579 | hvc_instantiate(HVC_COOKIE, 0, ops); |
268 | return 0; | 580 | return 0; |
269 | } | 581 | } |
270 | 582 | ||
583 | static DEFINE_XENBUS_DRIVER(xencons, "xenconsole", | ||
584 | .probe = xencons_probe, | ||
585 | .remove = xencons_remove, | ||
586 | .resume = xencons_resume, | ||
587 | .otherend_changed = xencons_backend_changed, | ||
588 | ); | ||
589 | |||
271 | module_init(xen_hvc_init); | 590 | module_init(xen_hvc_init); |
272 | module_exit(xen_hvc_fini); | 591 | module_exit(xen_hvc_fini); |
273 | console_initcall(xen_cons_init); | 592 | console_initcall(xen_cons_init); |