Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.9 707 lines 16 kB view raw
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/irq.h> 25#include <linux/init.h> 26#include <linux/types.h> 27#include <linux/list.h> 28#include <linux/serial_core.h> 29 30#include <asm/io.h> 31#include <asm/xen/hypervisor.h> 32 33#include <xen/xen.h> 34#include <xen/interface/xen.h> 35#include <xen/hvm.h> 36#include <xen/grant_table.h> 37#include <xen/page.h> 38#include <xen/events.h> 39#include <xen/interface/io/console.h> 40#include <xen/interface/sched.h> 41#include <xen/hvc-console.h> 42#include <xen/xenbus.h> 43 44#include "hvc_console.h" 45 46#define HVC_COOKIE 0x58656e /* "Xen" in hex */ 47 48struct xencons_info { 49 struct list_head list; 50 struct xenbus_device *xbdev; 51 struct xencons_interface *intf; 52 unsigned int evtchn; 53 struct hvc_struct *hvc; 54 int irq; 55 int vtermno; 56 grant_ref_t gntref; 57}; 58 59static LIST_HEAD(xenconsoles); 60static DEFINE_SPINLOCK(xencons_lock); 61 62/* ------------------------------------------------------------------ */ 63 64static struct xencons_info *vtermno_to_xencons(int vtermno) 65{ 66 struct xencons_info *entry, *n, *ret = NULL; 67 68 if (list_empty(&xenconsoles)) 69 return NULL; 70 71 list_for_each_entry_safe(entry, n, &xenconsoles, list) { 72 if (entry->vtermno == vtermno) { 73 ret = entry; 74 break; 75 } 76 } 77 78 return ret; 79} 80 81static inline int xenbus_devid_to_vtermno(int devid) 82{ 83 return devid + HVC_COOKIE; 84} 85 86static inline void notify_daemon(struct xencons_info *cons) 87{ 88 /* Use evtchn: this is called early, before irq is set up. */ 89 notify_remote_via_evtchn(cons->evtchn); 90} 91 92static int __write_console(struct xencons_info *xencons, 93 const char *data, int len) 94{ 95 XENCONS_RING_IDX cons, prod; 96 struct xencons_interface *intf = xencons->intf; 97 int sent = 0; 98 99 cons = intf->out_cons; 100 prod = intf->out_prod; 101 mb(); /* update queue values before going on */ 102 BUG_ON((prod - cons) > sizeof(intf->out)); 103 104 while ((sent < len) && ((prod - cons) < sizeof(intf->out))) 105 intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++]; 106 107 wmb(); /* write ring before updating pointer */ 108 intf->out_prod = prod; 109 110 if (sent) 111 notify_daemon(xencons); 112 return sent; 113} 114 115static int domU_write_console(uint32_t vtermno, const char *data, int len) 116{ 117 int ret = len; 118 struct xencons_info *cons = vtermno_to_xencons(vtermno); 119 if (cons == NULL) 120 return -EINVAL; 121 122 /* 123 * Make sure the whole buffer is emitted, polling if 124 * necessary. We don't ever want to rely on the hvc daemon 125 * because the most interesting console output is when the 126 * kernel is crippled. 127 */ 128 while (len) { 129 int sent = __write_console(cons, data, len); 130 131 data += sent; 132 len -= sent; 133 134 if (unlikely(len)) 135 HYPERVISOR_sched_op(SCHEDOP_yield, NULL); 136 } 137 138 return ret; 139} 140 141static int domU_read_console(uint32_t vtermno, char *buf, int len) 142{ 143 struct xencons_interface *intf; 144 XENCONS_RING_IDX cons, prod; 145 int recv = 0; 146 struct xencons_info *xencons = vtermno_to_xencons(vtermno); 147 if (xencons == NULL) 148 return -EINVAL; 149 intf = xencons->intf; 150 151 cons = intf->in_cons; 152 prod = intf->in_prod; 153 mb(); /* get pointers before reading ring */ 154 BUG_ON((prod - cons) > sizeof(intf->in)); 155 156 while (cons != prod && recv < len) 157 buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)]; 158 159 mb(); /* read ring before consuming */ 160 intf->in_cons = cons; 161 162 notify_daemon(xencons); 163 return recv; 164} 165 166static const struct hv_ops domU_hvc_ops = { 167 .get_chars = domU_read_console, 168 .put_chars = domU_write_console, 169 .notifier_add = notifier_add_irq, 170 .notifier_del = notifier_del_irq, 171 .notifier_hangup = notifier_hangup_irq, 172}; 173 174static int dom0_read_console(uint32_t vtermno, char *buf, int len) 175{ 176 return HYPERVISOR_console_io(CONSOLEIO_read, len, buf); 177} 178 179/* 180 * Either for a dom0 to write to the system console, or a domU with a 181 * debug version of Xen 182 */ 183static int dom0_write_console(uint32_t vtermno, const char *str, int len) 184{ 185 int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); 186 if (rc < 0) 187 return rc; 188 189 return len; 190} 191 192static const struct hv_ops dom0_hvc_ops = { 193 .get_chars = dom0_read_console, 194 .put_chars = dom0_write_console, 195 .notifier_add = notifier_add_irq, 196 .notifier_del = notifier_del_irq, 197 .notifier_hangup = notifier_hangup_irq, 198}; 199 200static int xen_hvm_console_init(void) 201{ 202 int r; 203 uint64_t v = 0; 204 unsigned long gfn; 205 struct xencons_info *info; 206 207 if (!xen_hvm_domain()) 208 return -ENODEV; 209 210 info = vtermno_to_xencons(HVC_COOKIE); 211 if (!info) { 212 info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); 213 if (!info) 214 return -ENOMEM; 215 } else if (info->intf != NULL) { 216 /* already configured */ 217 return 0; 218 } 219 /* 220 * If the toolstack (or the hypervisor) hasn't set these values, the 221 * default value is 0. Even though gfn = 0 and evtchn = 0 are 222 * theoretically correct values, in practice they never are and they 223 * mean that a legacy toolstack hasn't initialized the pv console correctly. 224 */ 225 r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v); 226 if (r < 0 || v == 0) 227 goto err; 228 info->evtchn = v; 229 v = 0; 230 r = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v); 231 if (r < 0 || v == 0) 232 goto err; 233 gfn = v; 234 info->intf = xen_remap(gfn << XEN_PAGE_SHIFT, XEN_PAGE_SIZE); 235 if (info->intf == NULL) 236 goto err; 237 info->vtermno = HVC_COOKIE; 238 239 spin_lock(&xencons_lock); 240 list_add_tail(&info->list, &xenconsoles); 241 spin_unlock(&xencons_lock); 242 243 return 0; 244err: 245 kfree(info); 246 return -ENODEV; 247} 248 249static int xencons_info_pv_init(struct xencons_info *info, int vtermno) 250{ 251 info->evtchn = xen_start_info->console.domU.evtchn; 252 /* GFN == MFN for PV guest */ 253 info->intf = gfn_to_virt(xen_start_info->console.domU.mfn); 254 info->vtermno = vtermno; 255 256 list_add_tail(&info->list, &xenconsoles); 257 258 return 0; 259} 260 261static int xen_pv_console_init(void) 262{ 263 struct xencons_info *info; 264 265 if (!xen_pv_domain()) 266 return -ENODEV; 267 268 if (!xen_start_info->console.domU.evtchn) 269 return -ENODEV; 270 271 info = vtermno_to_xencons(HVC_COOKIE); 272 if (!info) { 273 info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); 274 if (!info) 275 return -ENOMEM; 276 } else if (info->intf != NULL) { 277 /* already configured */ 278 return 0; 279 } 280 spin_lock(&xencons_lock); 281 xencons_info_pv_init(info, HVC_COOKIE); 282 spin_unlock(&xencons_lock); 283 284 return 0; 285} 286 287static int xen_initial_domain_console_init(void) 288{ 289 struct xencons_info *info; 290 291 if (!xen_initial_domain()) 292 return -ENODEV; 293 294 info = vtermno_to_xencons(HVC_COOKIE); 295 if (!info) { 296 info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); 297 if (!info) 298 return -ENOMEM; 299 } 300 301 info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false); 302 info->vtermno = HVC_COOKIE; 303 304 spin_lock(&xencons_lock); 305 list_add_tail(&info->list, &xenconsoles); 306 spin_unlock(&xencons_lock); 307 308 return 0; 309} 310 311static void xen_console_update_evtchn(struct xencons_info *info) 312{ 313 if (xen_hvm_domain()) { 314 uint64_t v = 0; 315 int err; 316 317 err = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v); 318 if (!err && v) 319 info->evtchn = v; 320 } else 321 info->evtchn = xen_start_info->console.domU.evtchn; 322} 323 324void xen_console_resume(void) 325{ 326 struct xencons_info *info = vtermno_to_xencons(HVC_COOKIE); 327 if (info != NULL && info->irq) { 328 if (!xen_initial_domain()) 329 xen_console_update_evtchn(info); 330 rebind_evtchn_irq(info->evtchn, info->irq); 331 } 332} 333 334#ifdef CONFIG_HVC_XEN_FRONTEND 335static void xencons_disconnect_backend(struct xencons_info *info) 336{ 337 if (info->irq > 0) 338 unbind_from_irqhandler(info->irq, NULL); 339 info->irq = 0; 340 if (info->evtchn > 0) 341 xenbus_free_evtchn(info->xbdev, info->evtchn); 342 info->evtchn = 0; 343 if (info->gntref > 0) 344 gnttab_free_grant_references(info->gntref); 345 info->gntref = 0; 346 if (info->hvc != NULL) 347 hvc_remove(info->hvc); 348 info->hvc = NULL; 349} 350 351static void xencons_free(struct xencons_info *info) 352{ 353 free_page((unsigned long)info->intf); 354 info->intf = NULL; 355 info->vtermno = 0; 356 kfree(info); 357} 358 359static int xen_console_remove(struct xencons_info *info) 360{ 361 xencons_disconnect_backend(info); 362 spin_lock(&xencons_lock); 363 list_del(&info->list); 364 spin_unlock(&xencons_lock); 365 if (info->xbdev != NULL) 366 xencons_free(info); 367 else { 368 if (xen_hvm_domain()) 369 iounmap(info->intf); 370 kfree(info); 371 } 372 return 0; 373} 374 375static int xencons_remove(struct xenbus_device *dev) 376{ 377 return xen_console_remove(dev_get_drvdata(&dev->dev)); 378} 379 380static int xencons_connect_backend(struct xenbus_device *dev, 381 struct xencons_info *info) 382{ 383 int ret, evtchn, devid, ref, irq; 384 struct xenbus_transaction xbt; 385 grant_ref_t gref_head; 386 387 ret = xenbus_alloc_evtchn(dev, &evtchn); 388 if (ret) 389 return ret; 390 info->evtchn = evtchn; 391 irq = bind_evtchn_to_irq(evtchn); 392 if (irq < 0) 393 return irq; 394 info->irq = irq; 395 devid = dev->nodename[strlen(dev->nodename) - 1] - '0'; 396 info->hvc = hvc_alloc(xenbus_devid_to_vtermno(devid), 397 irq, &domU_hvc_ops, 256); 398 if (IS_ERR(info->hvc)) 399 return PTR_ERR(info->hvc); 400 ret = gnttab_alloc_grant_references(1, &gref_head); 401 if (ret < 0) 402 return ret; 403 info->gntref = gref_head; 404 ref = gnttab_claim_grant_reference(&gref_head); 405 if (ref < 0) 406 return ref; 407 gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id, 408 virt_to_gfn(info->intf), 0); 409 410 again: 411 ret = xenbus_transaction_start(&xbt); 412 if (ret) { 413 xenbus_dev_fatal(dev, ret, "starting transaction"); 414 return ret; 415 } 416 ret = xenbus_printf(xbt, dev->nodename, "ring-ref", "%d", ref); 417 if (ret) 418 goto error_xenbus; 419 ret = xenbus_printf(xbt, dev->nodename, "port", "%u", 420 evtchn); 421 if (ret) 422 goto error_xenbus; 423 ret = xenbus_transaction_end(xbt, 0); 424 if (ret) { 425 if (ret == -EAGAIN) 426 goto again; 427 xenbus_dev_fatal(dev, ret, "completing transaction"); 428 return ret; 429 } 430 431 xenbus_switch_state(dev, XenbusStateInitialised); 432 return 0; 433 434 error_xenbus: 435 xenbus_transaction_end(xbt, 1); 436 xenbus_dev_fatal(dev, ret, "writing xenstore"); 437 return ret; 438} 439 440static int xencons_probe(struct xenbus_device *dev, 441 const struct xenbus_device_id *id) 442{ 443 int ret, devid; 444 struct xencons_info *info; 445 446 devid = dev->nodename[strlen(dev->nodename) - 1] - '0'; 447 if (devid == 0) 448 return -ENODEV; 449 450 info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); 451 if (!info) 452 return -ENOMEM; 453 dev_set_drvdata(&dev->dev, info); 454 info->xbdev = dev; 455 info->vtermno = xenbus_devid_to_vtermno(devid); 456 info->intf = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 457 if (!info->intf) 458 goto error_nomem; 459 460 ret = xencons_connect_backend(dev, info); 461 if (ret < 0) 462 goto error; 463 spin_lock(&xencons_lock); 464 list_add_tail(&info->list, &xenconsoles); 465 spin_unlock(&xencons_lock); 466 467 return 0; 468 469 error_nomem: 470 ret = -ENOMEM; 471 xenbus_dev_fatal(dev, ret, "allocating device memory"); 472 error: 473 xencons_disconnect_backend(info); 474 xencons_free(info); 475 return ret; 476} 477 478static int xencons_resume(struct xenbus_device *dev) 479{ 480 struct xencons_info *info = dev_get_drvdata(&dev->dev); 481 482 xencons_disconnect_backend(info); 483 memset(info->intf, 0, XEN_PAGE_SIZE); 484 return xencons_connect_backend(dev, info); 485} 486 487static void xencons_backend_changed(struct xenbus_device *dev, 488 enum xenbus_state backend_state) 489{ 490 switch (backend_state) { 491 case XenbusStateReconfiguring: 492 case XenbusStateReconfigured: 493 case XenbusStateInitialising: 494 case XenbusStateInitialised: 495 case XenbusStateUnknown: 496 break; 497 498 case XenbusStateInitWait: 499 break; 500 501 case XenbusStateConnected: 502 xenbus_switch_state(dev, XenbusStateConnected); 503 break; 504 505 case XenbusStateClosed: 506 if (dev->state == XenbusStateClosed) 507 break; 508 /* Missed the backend's CLOSING state -- fallthrough */ 509 case XenbusStateClosing: 510 xenbus_frontend_closed(dev); 511 break; 512 } 513} 514 515static const struct xenbus_device_id xencons_ids[] = { 516 { "console" }, 517 { "" } 518}; 519 520static struct xenbus_driver xencons_driver = { 521 .name = "xenconsole", 522 .ids = xencons_ids, 523 .probe = xencons_probe, 524 .remove = xencons_remove, 525 .resume = xencons_resume, 526 .otherend_changed = xencons_backend_changed, 527}; 528#endif /* CONFIG_HVC_XEN_FRONTEND */ 529 530static int __init xen_hvc_init(void) 531{ 532 int r; 533 struct xencons_info *info; 534 const struct hv_ops *ops; 535 536 if (!xen_domain()) 537 return -ENODEV; 538 539 if (xen_initial_domain()) { 540 ops = &dom0_hvc_ops; 541 r = xen_initial_domain_console_init(); 542 if (r < 0) 543 return r; 544 info = vtermno_to_xencons(HVC_COOKIE); 545 } else { 546 ops = &domU_hvc_ops; 547 if (xen_hvm_domain()) 548 r = xen_hvm_console_init(); 549 else 550 r = xen_pv_console_init(); 551 if (r < 0) 552 return r; 553 554 info = vtermno_to_xencons(HVC_COOKIE); 555 info->irq = bind_evtchn_to_irq(info->evtchn); 556 } 557 if (info->irq < 0) 558 info->irq = 0; /* NO_IRQ */ 559 else 560 irq_set_noprobe(info->irq); 561 562 info->hvc = hvc_alloc(HVC_COOKIE, info->irq, ops, 256); 563 if (IS_ERR(info->hvc)) { 564 r = PTR_ERR(info->hvc); 565 spin_lock(&xencons_lock); 566 list_del(&info->list); 567 spin_unlock(&xencons_lock); 568 if (info->irq) 569 unbind_from_irqhandler(info->irq, NULL); 570 kfree(info); 571 return r; 572 } 573 574 r = 0; 575#ifdef CONFIG_HVC_XEN_FRONTEND 576 r = xenbus_register_frontend(&xencons_driver); 577#endif 578 return r; 579} 580device_initcall(xen_hvc_init); 581 582static int xen_cons_init(void) 583{ 584 const struct hv_ops *ops; 585 586 if (!xen_domain()) 587 return 0; 588 589 if (xen_initial_domain()) 590 ops = &dom0_hvc_ops; 591 else { 592 int r; 593 ops = &domU_hvc_ops; 594 595 if (xen_hvm_domain()) 596 r = xen_hvm_console_init(); 597 else 598 r = xen_pv_console_init(); 599 if (r < 0) 600 return r; 601 } 602 603 hvc_instantiate(HVC_COOKIE, 0, ops); 604 return 0; 605} 606console_initcall(xen_cons_init); 607 608#ifdef CONFIG_X86 609static void xen_hvm_early_write(uint32_t vtermno, const char *str, int len) 610{ 611 if (xen_cpuid_base()) 612 outsb(0xe9, str, len); 613} 614#else 615static void xen_hvm_early_write(uint32_t vtermno, const char *str, int len) { } 616#endif 617 618#ifdef CONFIG_EARLY_PRINTK 619static int __init xenboot_setup_console(struct console *console, char *string) 620{ 621 static struct xencons_info xenboot; 622 623 if (xen_initial_domain()) 624 return 0; 625 if (!xen_pv_domain()) 626 return -ENODEV; 627 628 return xencons_info_pv_init(&xenboot, 0); 629} 630 631static void xenboot_write_console(struct console *console, const char *string, 632 unsigned len) 633{ 634 unsigned int linelen, off = 0; 635 const char *pos; 636 637 if (!xen_pv_domain()) { 638 xen_hvm_early_write(0, string, len); 639 return; 640 } 641 642 dom0_write_console(0, string, len); 643 644 if (xen_initial_domain()) 645 return; 646 647 domU_write_console(0, "(early) ", 8); 648 while (off < len && NULL != (pos = strchr(string+off, '\n'))) { 649 linelen = pos-string+off; 650 if (off + linelen > len) 651 break; 652 domU_write_console(0, string+off, linelen); 653 domU_write_console(0, "\r\n", 2); 654 off += linelen + 1; 655 } 656 if (off < len) 657 domU_write_console(0, string+off, len-off); 658} 659 660struct console xenboot_console = { 661 .name = "xenboot", 662 .write = xenboot_write_console, 663 .setup = xenboot_setup_console, 664 .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, 665 .index = -1, 666}; 667#endif /* CONFIG_EARLY_PRINTK */ 668 669void xen_raw_console_write(const char *str) 670{ 671 ssize_t len = strlen(str); 672 int rc = 0; 673 674 if (xen_domain()) { 675 rc = dom0_write_console(0, str, len); 676 if (rc != -ENOSYS || !xen_hvm_domain()) 677 return; 678 } 679 xen_hvm_early_write(0, str, len); 680} 681 682void xen_raw_printk(const char *fmt, ...) 683{ 684 static char buf[512]; 685 va_list ap; 686 687 va_start(ap, fmt); 688 vsnprintf(buf, sizeof(buf), fmt, ap); 689 va_end(ap); 690 691 xen_raw_console_write(buf); 692} 693 694static void xenboot_earlycon_write(struct console *console, 695 const char *string, 696 unsigned len) 697{ 698 dom0_write_console(0, string, len); 699} 700 701static int __init xenboot_earlycon_setup(struct earlycon_device *device, 702 const char *opt) 703{ 704 device->con->write = xenboot_earlycon_write; 705 return 0; 706} 707EARLYCON_DECLARE(xenboot, xenboot_earlycon_setup);