aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/hvc
diff options
context:
space:
mode:
authorStefano Stabellini <stefano.stabellini@eu.citrix.com>2012-01-30 11:02:31 -0500
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2012-03-13 19:23:41 -0400
commit02e19f9c7cacfb33d7b2f5cace7972fa60f92319 (patch)
treec62a54d17a617d648cb1213fe6844d1687a54988 /drivers/tty/hvc
parenteb5ef07151ba3c3cb4bcef0c8f146ff1115eaa55 (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.c435
1 files changed, 377 insertions, 58 deletions
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c
index d5000aa0286..26090c736bc 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
42static struct hvc_struct *hvc; 45struct xencons_info {
43static 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
56static LIST_HEAD(xenconsoles);
57static DEFINE_SPINLOCK(xencons_lock);
58static struct xenbus_driver xencons_driver;
44 59
45/* ------------------------------------------------------------------ */ 60/* ------------------------------------------------------------------ */
46 61
47static unsigned long console_pfn = ~0ul; 62static struct xencons_info *vtermno_to_xencons(int vtermno)
48static unsigned int console_evtchn = ~0ul; 63{
49static 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
51static 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
79static 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
61static inline void notify_daemon(void) 84static 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
70static int __write_console(const char *data, int len) 90static 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
92static int domU_write_console(uint32_t vtermno, const char *data, int len) 113static 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
115static int domU_read_console(uint32_t vtermno, char *buf, int len) 139static 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
245static 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
277static 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
197static int __init xen_hvc_init(void) 301static 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; 348void 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
355static 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
371static 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
379static 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
237void xen_console_resume(void) 395static int xencons_remove(struct xenbus_device *dev)
396{
397 return xen_console_remove(dev_get_drvdata(&dev->dev));
398}
399
400static 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
468static 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
506static 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
515static 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
540static const struct xenbus_device_id xencons_ids[] = {
541 { "console" },
542 { "" }
543};
544
545
243static void __exit xen_hvc_fini(void) 546static 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
249static int xen_cons_init(void) 558static 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
583static 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
271module_init(xen_hvc_init); 590module_init(xen_hvc_init);
272module_exit(xen_hvc_fini); 591module_exit(xen_hvc_fini);
273console_initcall(xen_cons_init); 592console_initcall(xen_cons_init);