root/drivers/tty/hvc/hvc_xen.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vtermno_to_xencons
  2. xenbus_devid_to_vtermno
  3. notify_daemon
  4. __write_console
  5. domU_write_console
  6. domU_read_console
  7. dom0_read_console
  8. dom0_write_console
  9. xen_hvm_console_init
  10. xencons_info_pv_init
  11. xen_pv_console_init
  12. xen_initial_domain_console_init
  13. xen_console_update_evtchn
  14. xen_console_resume
  15. xencons_disconnect_backend
  16. xencons_free
  17. xen_console_remove
  18. xencons_remove
  19. xencons_connect_backend
  20. xencons_probe
  21. xencons_resume
  22. xencons_backend_changed
  23. xen_hvc_init
  24. xen_cons_init
  25. xen_hvm_early_write
  26. xen_hvm_early_write
  27. xenboot_setup_console
  28. xenboot_write_console
  29. xen_raw_console_write
  30. xen_raw_printk
  31. xenboot_earlycon_write
  32. xenboot_earlycon_setup

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

/* [<][>][^][v][top][bottom][index][help] */