hvc_xen.c revision 85fc3e3eba89272acb005f320ccafa0588a48f49
1/* 2 * xen console driver interface to hvc_console.c 3 * 4 * (c) 2007 Gerd Hoffmann <kraxel@suse.de> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21#include <linux/console.h> 22#include <linux/delay.h> 23#include <linux/err.h> 24#include <linux/init.h> 25#include <linux/types.h> 26#include <linux/list.h> 27 28#include <asm/io.h> 29#include <asm/xen/hypervisor.h> 30 31#include <xen/xen.h> 32#include <xen/interface/xen.h> 33#include <xen/hvm.h> 34#include <xen/grant_table.h> 35#include <xen/page.h> 36#include <xen/events.h> 37#include <xen/interface/io/console.h> 38#include <xen/hvc-console.h> 39#include <xen/xenbus.h> 40 41#include "hvc_console.h" 42 43#define HVC_COOKIE 0x58656e /* "Xen" in hex */ 44 45struct xencons_info { 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); 58 59/* ------------------------------------------------------------------ */ 60 61static struct xencons_info *vtermno_to_xencons(int vtermno) 62{ 63 struct xencons_info *entry, *n, *ret = NULL; 64 65 if (list_empty(&xenconsoles)) 66 return NULL; 67 68 list_for_each_entry_safe(entry, n, &xenconsoles, list) { 69 if (entry->vtermno == vtermno) { 70 ret = entry; 71 break; 72 } 73 } 74 75 return ret; 76} 77 78static inline int xenbus_devid_to_vtermno(int devid) 79{ 80 return devid + HVC_COOKIE; 81} 82 83static inline void notify_daemon(struct xencons_info *cons) 84{ 85 /* Use evtchn: this is called early, before irq is set up. */ 86 notify_remote_via_evtchn(cons->evtchn); 87} 88 89static int __write_console(struct xencons_info *xencons, 90 const char *data, int len) 91{ 92 XENCONS_RING_IDX cons, prod; 93 struct xencons_interface *intf = xencons->intf; 94 int sent = 0; 95 96 cons = intf->out_cons; 97 prod = intf->out_prod; 98 mb(); /* update queue values before going on */ 99 BUG_ON((prod - cons) > sizeof(intf->out)); 100 101 while ((sent < len) && ((prod - cons) < sizeof(intf->out))) 102 intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++]; 103 104 wmb(); /* write ring before updating pointer */ 105 intf->out_prod = prod; 106 107 if (sent) 108 notify_daemon(xencons); 109 return sent; 110} 111 112static int domU_write_console(uint32_t vtermno, const char *data, int len) 113{ 114 int ret = len; 115 struct xencons_info *cons = vtermno_to_xencons(vtermno); 116 if (cons == NULL) 117 return -EINVAL; 118 119 /* 120 * Make sure the whole buffer is emitted, polling if 121 * necessary. We don't ever want to rely on the hvc daemon 122 * because the most interesting console output is when the 123 * kernel is crippled. 124 */ 125 while (len) { 126 int sent = __write_console(cons, data, len); 127 128 data += sent; 129 len -= sent; 130 131 if (unlikely(len)) 132 HYPERVISOR_sched_op(SCHEDOP_yield, NULL); 133 } 134 135 return ret; 136} 137 138static int domU_read_console(uint32_t vtermno, char *buf, int len) 139{ 140 struct xencons_interface *intf; 141 XENCONS_RING_IDX cons, prod; 142 int recv = 0; 143 struct xencons_info *xencons = vtermno_to_xencons(vtermno); 144 if (xencons == NULL) 145 return -EINVAL; 146 intf = xencons->intf; 147 148 cons = intf->in_cons; 149 prod = intf->in_prod; 150 mb(); /* get pointers before reading ring */ 151 BUG_ON((prod - cons) > sizeof(intf->in)); 152 153 while (cons != prod && recv < len) 154 buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)]; 155 156 mb(); /* read ring before consuming */ 157 intf->in_cons = cons; 158 159 notify_daemon(xencons); 160 return recv; 161} 162 163static struct hv_ops domU_hvc_ops = { 164 .get_chars = domU_read_console, 165 .put_chars = domU_write_console, 166 .notifier_add = notifier_add_irq, 167 .notifier_del = notifier_del_irq, 168 .notifier_hangup = notifier_hangup_irq, 169}; 170 171static int dom0_read_console(uint32_t vtermno, char *buf, int len) 172{ 173 return HYPERVISOR_console_io(CONSOLEIO_read, len, buf); 174} 175 176/* 177 * Either for a dom0 to write to the system console, or a domU with a 178 * debug version of Xen 179 */ 180static int dom0_write_console(uint32_t vtermno, const char *str, int len) 181{ 182 int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); 183 if (rc < 0) 184 return 0; 185 186 return len; 187} 188 189static struct hv_ops dom0_hvc_ops = { 190 .get_chars = dom0_read_console, 191 .put_chars = dom0_write_console, 192 .notifier_add = notifier_add_irq, 193 .notifier_del = notifier_del_irq, 194 .notifier_hangup = notifier_hangup_irq, 195}; 196 197static int xen_hvm_console_init(void) 198{ 199 int r; 200 uint64_t v = 0; 201 unsigned long mfn; 202 struct xencons_info *info; 203 204 if (!xen_hvm_domain()) 205 return -ENODEV; 206 207 info = vtermno_to_xencons(HVC_COOKIE); 208 if (!info) { 209 info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); 210 if (!info) 211 return -ENOMEM; 212 } 213 214 /* already configured */ 215 if (info->intf != NULL) 216 return 0; 217 218 r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v); 219 if (r < 0) 220 goto err; 221 info->evtchn = v; 222 v = 0; 223 r = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v); 224 if (r < 0) 225 goto err; 226 mfn = v; 227 info->intf = ioremap(mfn << PAGE_SHIFT, PAGE_SIZE); 228 if (info->intf == NULL) 229 goto err; 230 info->vtermno = HVC_COOKIE; 231 232 spin_lock(&xencons_lock); 233 list_add_tail(&info->list, &xenconsoles); 234 spin_unlock(&xencons_lock); 235 236 return 0; 237err: 238 kfree(info); 239 return -ENODEV; 240} 241 242static int xen_pv_console_init(void) 243{ 244 struct xencons_info *info; 245 246 if (!xen_pv_domain()) 247 return -ENODEV; 248 249 if (!xen_start_info->console.domU.evtchn) 250 return -ENODEV; 251 252 info = vtermno_to_xencons(HVC_COOKIE); 253 if (!info) { 254 info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); 255 if (!info) 256 return -ENOMEM; 257 } 258 259 /* already configured */ 260 if (info->intf != NULL) 261 return 0; 262 263 info->evtchn = xen_start_info->console.domU.evtchn; 264 info->intf = mfn_to_virt(xen_start_info->console.domU.mfn); 265 info->vtermno = HVC_COOKIE; 266 267 spin_lock(&xencons_lock); 268 list_add_tail(&info->list, &xenconsoles); 269 spin_unlock(&xencons_lock); 270 271 return 0; 272} 273 274static int xen_initial_domain_console_init(void) 275{ 276 struct xencons_info *info; 277 278 if (!xen_initial_domain()) 279 return -ENODEV; 280 281 info = vtermno_to_xencons(HVC_COOKIE); 282 if (!info) { 283 info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); 284 if (!info) 285 return -ENOMEM; 286 } 287 288 info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); 289 info->vtermno = HVC_COOKIE; 290 291 spin_lock(&xencons_lock); 292 list_add_tail(&info->list, &xenconsoles); 293 spin_unlock(&xencons_lock); 294 295 return 0; 296} 297 298void xen_console_resume(void) 299{ 300 struct xencons_info *info = vtermno_to_xencons(HVC_COOKIE); 301 if (info != NULL && info->irq) 302 rebind_evtchn_irq(info->evtchn, info->irq); 303} 304 305static void xencons_disconnect_backend(struct xencons_info *info) 306{ 307 if (info->irq > 0) 308 unbind_from_irqhandler(info->irq, NULL); 309 info->irq = 0; 310 if (info->evtchn > 0) 311 xenbus_free_evtchn(info->xbdev, info->evtchn); 312 info->evtchn = 0; 313 if (info->gntref > 0) 314 gnttab_free_grant_references(info->gntref); 315 info->gntref = 0; 316 if (info->hvc != NULL) 317 hvc_remove(info->hvc); 318 info->hvc = NULL; 319} 320 321static void xencons_free(struct xencons_info *info) 322{ 323 free_page((unsigned long)info->intf); 324 info->intf = NULL; 325 info->vtermno = 0; 326 kfree(info); 327} 328 329static int xen_console_remove(struct xencons_info *info) 330{ 331 xencons_disconnect_backend(info); 332 spin_lock(&xencons_lock); 333 list_del(&info->list); 334 spin_unlock(&xencons_lock); 335 if (info->xbdev != NULL) 336 xencons_free(info); 337 else { 338 if (xen_hvm_domain()) 339 iounmap(info->intf); 340 kfree(info); 341 } 342 return 0; 343} 344 345#ifdef CONFIG_HVC_XEN_FRONTEND 346static struct xenbus_driver xencons_driver; 347 348static int xencons_remove(struct xenbus_device *dev) 349{ 350 return xen_console_remove(dev_get_drvdata(&dev->dev)); 351} 352 353static int xencons_connect_backend(struct xenbus_device *dev, 354 struct xencons_info *info) 355{ 356 int ret, evtchn, devid, ref, irq; 357 struct xenbus_transaction xbt; 358 grant_ref_t gref_head; 359 unsigned long mfn; 360 361 ret = xenbus_alloc_evtchn(dev, &evtchn); 362 if (ret) 363 return ret; 364 info->evtchn = evtchn; 365 irq = bind_evtchn_to_irq(evtchn); 366 if (irq < 0) 367 return irq; 368 info->irq = irq; 369 devid = dev->nodename[strlen(dev->nodename) - 1] - '0'; 370 info->hvc = hvc_alloc(xenbus_devid_to_vtermno(devid), 371 irq, &domU_hvc_ops, 256); 372 if (IS_ERR(info->hvc)) 373 return PTR_ERR(info->hvc); 374 if (xen_pv_domain()) 375 mfn = virt_to_mfn(info->intf); 376 else 377 mfn = __pa(info->intf) >> PAGE_SHIFT; 378 ret = gnttab_alloc_grant_references(1, &gref_head); 379 if (ret < 0) 380 return ret; 381 info->gntref = gref_head; 382 ref = gnttab_claim_grant_reference(&gref_head); 383 if (ref < 0) 384 return ref; 385 gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id, 386 mfn, 0); 387 388 again: 389 ret = xenbus_transaction_start(&xbt); 390 if (ret) { 391 xenbus_dev_fatal(dev, ret, "starting transaction"); 392 return ret; 393 } 394 ret = xenbus_printf(xbt, dev->nodename, "ring-ref", "%d", ref); 395 if (ret) 396 goto error_xenbus; 397 ret = xenbus_printf(xbt, dev->nodename, "port", "%u", 398 evtchn); 399 if (ret) 400 goto error_xenbus; 401 ret = xenbus_printf(xbt, dev->nodename, "type", "ioemu"); 402 if (ret) 403 goto error_xenbus; 404 ret = xenbus_transaction_end(xbt, 0); 405 if (ret) { 406 if (ret == -EAGAIN) 407 goto again; 408 xenbus_dev_fatal(dev, ret, "completing transaction"); 409 return ret; 410 } 411 412 xenbus_switch_state(dev, XenbusStateInitialised); 413 return 0; 414 415 error_xenbus: 416 xenbus_transaction_end(xbt, 1); 417 xenbus_dev_fatal(dev, ret, "writing xenstore"); 418 return ret; 419} 420 421static int __devinit xencons_probe(struct xenbus_device *dev, 422 const struct xenbus_device_id *id) 423{ 424 int ret, devid; 425 struct xencons_info *info; 426 427 devid = dev->nodename[strlen(dev->nodename) - 1] - '0'; 428 if (devid == 0) 429 return -ENODEV; 430 431 info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); 432 if (!info) 433 return -ENOMEM; 434 dev_set_drvdata(&dev->dev, info); 435 info->xbdev = dev; 436 info->vtermno = xenbus_devid_to_vtermno(devid); 437 info->intf = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 438 if (!info->intf) 439 goto error_nomem; 440 441 ret = xencons_connect_backend(dev, info); 442 if (ret < 0) 443 goto error; 444 spin_lock(&xencons_lock); 445 list_add_tail(&info->list, &xenconsoles); 446 spin_unlock(&xencons_lock); 447 448 return 0; 449 450 error_nomem: 451 ret = -ENOMEM; 452 xenbus_dev_fatal(dev, ret, "allocating device memory"); 453 error: 454 xencons_disconnect_backend(info); 455 xencons_free(info); 456 return ret; 457} 458 459static int xencons_resume(struct xenbus_device *dev) 460{ 461 struct xencons_info *info = dev_get_drvdata(&dev->dev); 462 463 xencons_disconnect_backend(info); 464 memset(info->intf, 0, PAGE_SIZE); 465 return xencons_connect_backend(dev, info); 466} 467 468static void xencons_backend_changed(struct xenbus_device *dev, 469 enum xenbus_state backend_state) 470{ 471 switch (backend_state) { 472 case XenbusStateReconfiguring: 473 case XenbusStateReconfigured: 474 case XenbusStateInitialising: 475 case XenbusStateInitialised: 476 case XenbusStateUnknown: 477 case XenbusStateClosed: 478 break; 479 480 case XenbusStateInitWait: 481 break; 482 483 case XenbusStateConnected: 484 xenbus_switch_state(dev, XenbusStateConnected); 485 break; 486 487 case XenbusStateClosing: 488 xenbus_frontend_closed(dev); 489 break; 490 } 491} 492 493static const struct xenbus_device_id xencons_ids[] = { 494 { "console" }, 495 { "" } 496}; 497 498 499static DEFINE_XENBUS_DRIVER(xencons, "xenconsole", 500 .probe = xencons_probe, 501 .remove = xencons_remove, 502 .resume = xencons_resume, 503 .otherend_changed = xencons_backend_changed, 504); 505#endif /* CONFIG_HVC_XEN_FRONTEND */ 506 507static int __init xen_hvc_init(void) 508{ 509 int r; 510 struct xencons_info *info; 511 const struct hv_ops *ops; 512 513 if (!xen_domain()) 514 return -ENODEV; 515 516 if (xen_initial_domain()) { 517 ops = &dom0_hvc_ops; 518 r = xen_initial_domain_console_init(); 519 if (r < 0) 520 return r; 521 info = vtermno_to_xencons(HVC_COOKIE); 522 } else { 523 ops = &domU_hvc_ops; 524 if (xen_hvm_domain()) 525 r = xen_hvm_console_init(); 526 else 527 r = xen_pv_console_init(); 528 if (r < 0) 529 return r; 530 531 info = vtermno_to_xencons(HVC_COOKIE); 532 info->irq = bind_evtchn_to_irq(info->evtchn); 533 } 534 if (info->irq < 0) 535 info->irq = 0; /* NO_IRQ */ 536 else 537 irq_set_noprobe(info->irq); 538 539 info->hvc = hvc_alloc(HVC_COOKIE, info->irq, ops, 256); 540 if (IS_ERR(info->hvc)) { 541 r = PTR_ERR(info->hvc); 542 spin_lock(&xencons_lock); 543 list_del(&info->list); 544 spin_unlock(&xencons_lock); 545 if (info->irq) 546 unbind_from_irqhandler(info->irq, NULL); 547 kfree(info); 548 return r; 549 } 550 551 r = 0; 552#ifdef CONFIG_HVC_XEN_FRONTEND 553 r = xenbus_register_frontend(&xencons_driver); 554#endif 555 return r; 556} 557 558static void __exit xen_hvc_fini(void) 559{ 560 struct xencons_info *entry, *next; 561 562 if (list_empty(&xenconsoles)) 563 return; 564 565 list_for_each_entry_safe(entry, next, &xenconsoles, list) { 566 xen_console_remove(entry); 567 } 568} 569 570static int xen_cons_init(void) 571{ 572 const struct hv_ops *ops; 573 574 if (!xen_domain()) 575 return 0; 576 577 if (xen_initial_domain()) 578 ops = &dom0_hvc_ops; 579 else { 580 int r; 581 ops = &domU_hvc_ops; 582 583 if (xen_hvm_domain()) 584 r = xen_hvm_console_init(); 585 else 586 r = xen_pv_console_init(); 587 if (r < 0) 588 return r; 589 } 590 591 hvc_instantiate(HVC_COOKIE, 0, ops); 592 return 0; 593} 594 595 596module_init(xen_hvc_init); 597module_exit(xen_hvc_fini); 598console_initcall(xen_cons_init); 599 600#ifdef CONFIG_EARLY_PRINTK 601static void xenboot_write_console(struct console *console, const char *string, 602 unsigned len) 603{ 604 unsigned int linelen, off = 0; 605 const char *pos; 606 607 if (!xen_pv_domain()) 608 return; 609 610 dom0_write_console(0, string, len); 611 612 if (xen_initial_domain()) 613 return; 614 615 domU_write_console(0, "(early) ", 8); 616 while (off < len && NULL != (pos = strchr(string+off, '\n'))) { 617 linelen = pos-string+off; 618 if (off + linelen > len) 619 break; 620 domU_write_console(0, string+off, linelen); 621 domU_write_console(0, "\r\n", 2); 622 off += linelen + 1; 623 } 624 if (off < len) 625 domU_write_console(0, string+off, len-off); 626} 627 628struct console xenboot_console = { 629 .name = "xenboot", 630 .write = xenboot_write_console, 631 .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, 632}; 633#endif /* CONFIG_EARLY_PRINTK */ 634 635void xen_raw_console_write(const char *str) 636{ 637 dom0_write_console(0, str, strlen(str)); 638} 639 640void xen_raw_printk(const char *fmt, ...) 641{ 642 static char buf[512]; 643 va_list ap; 644 645 va_start(ap, fmt); 646 vsnprintf(buf, sizeof(buf), fmt, ap); 647 va_end(ap); 648 649 xen_raw_console_write(buf); 650} 651