1 /*
2 * Copyright (c) 2015 Julien Grall <julien.grall@citrix.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 #include <sys/param.h>
29 #include <sys/module.h>
30 #include <sys/systm.h>
31 #include <sys/eventhandler.h>
32 #include <sys/consio.h>
33 #include <sys/priv.h>
34 #include <sys/proc.h>
35 #include <sys/uio.h>
36 #include <sys/tty.h>
37 #include <sys/systm.h>
38 #include <sys/taskqueue.h>
39 #include <sys/conf.h>
40 #include <sys/kernel.h>
41 #include <sys/bus.h>
42 #include <sys/cons.h>
43 #include <sys/kdb.h>
44 #include <sys/proc.h>
45 #include <sys/reboot.h>
46
47 #include <machine/stdarg.h>
48
49 #include <vm/vm.h>
50 #include <vm/pmap.h>
51
52 #include <xen/xen-os.h>
53 #include <xen/hypervisor.h>
54 #include <xen/xen_intr.h>
55 #include <contrib/xen/io/console.h>
56
57 #include "opt_ddb.h"
58 #include "opt_printf.h"
59
60 #ifdef DDB
61 #include <ddb/ddb.h>
62 #endif
63
64 static char driver_name[] = "xc";
65
66 struct xencons_priv;
67
68 typedef void xencons_early_init_t(struct xencons_priv *cons);
69 typedef int xencons_init_t(device_t dev, struct tty *tp,
70 driver_intr_t intr_handler);
71 typedef int xencons_read_t(struct xencons_priv *cons, char *buffer,
72 unsigned int size);
73 typedef int xencons_write_t(struct xencons_priv *cons, const char *buffer,
74 unsigned int size);
75
76 struct xencons_ops {
77 /*
78 * Called by the low-level driver during early boot.
79 * Only the minimal set up to get a console should be done here.
80 */
81 xencons_early_init_t *early_init;
82 /* Prepare the console to be fully use */
83 xencons_init_t *init;
84 /* Read/write helpers */
85 xencons_read_t *read;
86 xencons_write_t *write;
87 };
88
89 struct xencons_priv {
90 /* Mutex to protect the shared ring and the internal buffers */
91 struct mtx mtx;
92 /* Interrupt handler used for notify the backend */
93 xen_intr_handle_t intr_handle;
94 /* KDB internal state */
95 #ifdef KDB
96 int altbrk;
97 #endif
98 /* Status of the tty */
99 bool opened;
100 /* Callout used when the write buffer is full */
101 struct callout callout;
102
103 /* Internal buffers must be used with mtx locked */
104 #define WBUF_SIZE 4096
105 #define WBUF_MASK(_i) ((_i)&(WBUF_SIZE-1))
106 char wbuf[WBUF_SIZE];
107 unsigned int wc, wp; /* Consumer/producer wbuf */
108
109 #define RBUF_SIZE 1024
110 #define RBUF_MASK(_i) ((_i)&(RBUF_SIZE-1))
111 char rbuf[RBUF_SIZE];
112 unsigned int rc, rp; /* Consumer/producer rbuf */
113
114 /* Pointer to the console operations */
115 const struct xencons_ops *ops;
116
117 /*
118 * Ring specific fields
119 * XXX: make an union?
120 */
121 /* Event channel number for early notification (PV only) */
122 uint32_t evtchn;
123 /* Console shared page */
124 struct xencons_interface *intf;
125 };
126
127 /*
128 * Data for the main console
129 * Necessary to support low-level console driver
130 */
131 static struct xencons_priv main_cons;
132
133 #define XC_POLLTIME (hz/10)
134
135 /*----------------------------- Debug function ------------------------------*/
136 struct putchar_arg {
137 char *buf;
138 size_t size;
139 size_t n_next;
140 };
141
142 static void
putchar(int c,void * arg)143 putchar(int c, void *arg)
144 {
145 struct putchar_arg *pca;
146
147 pca = (struct putchar_arg *)arg;
148
149 if (pca->buf == NULL) {
150 /*
151 * We have no buffer, output directly to the
152 * console char by char.
153 */
154 HYPERVISOR_console_write((char *)&c, 1);
155 } else {
156 pca->buf[pca->n_next++] = c;
157 if ((pca->size == pca->n_next) || (c = '\0')) {
158 /* Flush the buffer */
159 HYPERVISOR_console_write(pca->buf, pca->n_next);
160 pca->n_next = 0;
161 }
162 }
163 }
164
165 void
xc_printf(const char * fmt,...)166 xc_printf(const char *fmt, ...)
167 {
168 va_list ap;
169 struct putchar_arg pca;
170 #ifdef PRINTF_BUFR_SIZE
171 char buf[PRINTF_BUFR_SIZE];
172
173 pca.buf = buf;
174 pca.size = sizeof(buf);
175 pca.n_next = 0;
176 #else
177 pca.buf = NULL;
178 pca.size = 0;
179 #endif
180
181 KASSERT((xen_domain()), ("call to xc_printf from non Xen guest"));
182
183 va_start(ap, fmt);
184 kvprintf(fmt, putchar, &pca, 10, ap);
185 va_end(ap);
186
187 #ifdef PRINTF_BUFR_SIZE
188 if (pca.n_next != 0)
189 HYPERVISOR_console_write(buf, pca.n_next);
190 #endif
191 }
192
193 /*---------------------- Helpers for the console lock -----------------------*/
194 /*
195 * The lock is not used when the kernel is panicing as it will never recover
196 * and we want to output no matter what it costs.
197 */
xencons_lock(struct xencons_priv * cons)198 static inline void xencons_lock(struct xencons_priv *cons)
199 {
200
201 if (!KERNEL_PANICKED())
202 mtx_lock_spin(&cons->mtx);
203
204 }
205
xencons_unlock(struct xencons_priv * cons)206 static inline void xencons_unlock(struct xencons_priv *cons)
207 {
208
209 if (!KERNEL_PANICKED())
210 mtx_unlock_spin(&cons->mtx);
211 }
212
213 #define xencons_lock_assert(cons) mtx_assert(&(cons)->mtx, MA_OWNED)
214
215 /*------------------ Helpers for the hypervisor console ---------------------*/
216 static void
xencons_early_init_hypervisor(struct xencons_priv * cons)217 xencons_early_init_hypervisor(struct xencons_priv *cons)
218 {
219 /*
220 * Nothing to setup for the low-level console when using
221 * the hypervisor console.
222 */
223 }
224
225 static int
xencons_init_hypervisor(device_t dev,struct tty * tp,driver_intr_t intr_handler)226 xencons_init_hypervisor(device_t dev, struct tty *tp,
227 driver_intr_t intr_handler)
228 {
229 struct xencons_priv *cons;
230 int err;
231
232 cons = tty_softc(tp);
233
234 err = xen_intr_bind_virq(dev, VIRQ_CONSOLE, 0, NULL,
235 intr_handler, tp, INTR_TYPE_TTY | INTR_MPSAFE, &cons->intr_handle);
236 if (err != 0)
237 device_printf(dev, "Can't register console interrupt\n");
238
239 return (err);
240 }
241
242 static int
xencons_write_hypervisor(struct xencons_priv * cons,const char * buffer,unsigned int size)243 xencons_write_hypervisor(struct xencons_priv *cons, const char *buffer,
244 unsigned int size)
245 {
246
247 HYPERVISOR_console_io(CONSOLEIO_write, size, buffer);
248
249 return (size);
250 }
251
252 static int
xencons_read_hypervisor(struct xencons_priv * cons,char * buffer,unsigned int size)253 xencons_read_hypervisor(struct xencons_priv *cons, char *buffer,
254 unsigned int size)
255 {
256
257 xencons_lock_assert(cons);
258
259 return (HYPERVISOR_console_io(CONSOLEIO_read, size, buffer));
260 }
261
262 static const struct xencons_ops xencons_hypervisor_ops = {
263 .early_init = xencons_early_init_hypervisor,
264 .init = xencons_init_hypervisor,
265 .read = xencons_read_hypervisor,
266 .write = xencons_write_hypervisor,
267 };
268
269 /*------------------ Helpers for the ring console ---------------------------*/
270 static void
xencons_early_init_ring(struct xencons_priv * cons)271 xencons_early_init_ring(struct xencons_priv *cons)
272 {
273 cons->intf = pmap_mapdev_attr(ptoa(xen_get_console_mfn()), PAGE_SIZE,
274 VM_MEMATTR_XEN);
275 cons->evtchn = xen_get_console_evtchn();
276 }
277
278 static int
xencons_init_ring(device_t dev,struct tty * tp,driver_intr_t intr_handler)279 xencons_init_ring(device_t dev, struct tty *tp, driver_intr_t intr_handler)
280 {
281 struct xencons_priv *cons;
282 int err;
283
284 cons = tty_softc(tp);
285
286 if (cons->evtchn == 0)
287 return (ENODEV);
288
289 err = xen_intr_bind_local_port(dev, cons->evtchn, NULL,
290 intr_handler, tp, INTR_TYPE_TTY | INTR_MPSAFE, &cons->intr_handle);
291 if (err != 0)
292 return (err);
293
294 return (0);
295 }
296
297 static void
xencons_notify_ring(struct xencons_priv * cons)298 xencons_notify_ring(struct xencons_priv *cons)
299 {
300 /*
301 * The console may be used before the ring interrupt is properly
302 * initialized.
303 * If so, fallback to directly use the event channel hypercall.
304 */
305 if (__predict_true(cons->intr_handle != NULL))
306 xen_intr_signal(cons->intr_handle);
307 else {
308 struct evtchn_send send = {
309 .port = cons->evtchn
310 };
311
312 HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
313 }
314 }
315
316 static int
xencons_write_ring(struct xencons_priv * cons,const char * buffer,unsigned int size)317 xencons_write_ring(struct xencons_priv *cons, const char *buffer,
318 unsigned int size)
319 {
320 struct xencons_interface *intf;
321 XENCONS_RING_IDX wcons, wprod;
322 int sent;
323
324 intf = cons->intf;
325
326 xencons_lock_assert(cons);
327
328 wcons = intf->out_cons;
329 wprod = intf->out_prod;
330
331 mb();
332 KASSERT((wprod - wcons) <= sizeof(intf->out),
333 ("console send ring inconsistent"));
334
335 for (sent = 0; sent < size; sent++, wprod++) {
336 if ((wprod - wcons) >= sizeof(intf->out))
337 break;
338 intf->out[MASK_XENCONS_IDX(wprod, intf->out)] = buffer[sent];
339 }
340
341 wmb();
342 intf->out_prod = wprod;
343
344 xencons_notify_ring(cons);
345
346 return (sent);
347 }
348
349 static int
xencons_read_ring(struct xencons_priv * cons,char * buffer,unsigned int size)350 xencons_read_ring(struct xencons_priv *cons, char *buffer, unsigned int size)
351 {
352 struct xencons_interface *intf;
353 XENCONS_RING_IDX rcons, rprod;
354 unsigned int rsz;
355
356 intf = cons->intf;
357
358 xencons_lock_assert(cons);
359
360 rcons = intf->in_cons;
361 rprod = intf->in_prod;
362 rmb();
363
364 for (rsz = 0; rsz < size; rsz++, rcons++) {
365 if (rprod == rcons)
366 break;
367 buffer[rsz] = intf->in[MASK_XENCONS_IDX(rcons, intf->in)];
368 }
369
370 wmb();
371 intf->in_cons = rcons;
372
373 /* No need to notify the backend if nothing has been read */
374 if (rsz != 0)
375 xencons_notify_ring(cons);
376
377 return (rsz);
378 }
379
380 static const struct xencons_ops xencons_ring_ops = {
381 .early_init = xencons_early_init_ring,
382 .init = xencons_init_ring,
383 .read = xencons_read_ring,
384 .write = xencons_write_ring,
385 };
386
387 /*------------------ Common implementation of the console -------------------*/
388
389 /*
390 * Called by the low-level driver during early boot to initialize the
391 * main console driver.
392 * Only the minimal set up to get a console should be done here.
393 */
394 static void
xencons_early_init(void)395 xencons_early_init(void)
396 {
397
398 mtx_init(&main_cons.mtx, "XCONS LOCK", NULL, MTX_SPIN);
399
400 if (xen_get_console_evtchn() == 0)
401 main_cons.ops = &xencons_hypervisor_ops;
402 else
403 main_cons.ops = &xencons_ring_ops;
404
405 main_cons.ops->early_init(&main_cons);
406 }
407
408 /*
409 * Receive character from the console and put them in the internal buffer
410 * XXX: Handle overflow of the internal buffer
411 */
412 static void
xencons_rx(struct xencons_priv * cons)413 xencons_rx(struct xencons_priv *cons)
414 {
415 char buf[16];
416 int sz;
417
418 xencons_lock(cons);
419 while ((sz = cons->ops->read(cons, buf, sizeof(buf))) > 0) {
420 int i;
421
422 for (i = 0; i < sz; i++)
423 cons->rbuf[RBUF_MASK(cons->rp++)] = buf[i];
424 }
425 xencons_unlock(cons);
426 }
427
428 /* Return true if the write buffer is full */
429 static bool
xencons_tx_full(struct xencons_priv * cons)430 xencons_tx_full(struct xencons_priv *cons)
431 {
432 unsigned int used;
433
434 xencons_lock(cons);
435 used = cons->wp - cons->wc;
436 xencons_unlock(cons);
437
438 return (used >= WBUF_SIZE);
439 }
440
441 static void
xencons_tx_flush(struct xencons_priv * cons,int force)442 xencons_tx_flush(struct xencons_priv *cons, int force)
443 {
444 int sz;
445
446 xencons_lock(cons);
447 while (cons->wc != cons->wp) {
448 int sent;
449 sz = cons->wp - cons->wc;
450 if (sz > (WBUF_SIZE - WBUF_MASK(cons->wc)))
451 sz = WBUF_SIZE - WBUF_MASK(cons->wc);
452 sent = cons->ops->write(cons, &cons->wbuf[WBUF_MASK(cons->wc)],
453 sz);
454
455 /*
456 * The other end may not have been initialized. Ignore
457 * the force.
458 */
459 if (__predict_false(sent < 0))
460 break;
461
462 /*
463 * If force is set, spin until the console data is
464 * flushed through the domain controller.
465 */
466 if (sent == 0 && __predict_true(!force))
467 break;
468
469 cons->wc += sent;
470 }
471 xencons_unlock(cons);
472 }
473
474 static bool
xencons_putc(struct xencons_priv * cons,int c,bool force_flush)475 xencons_putc(struct xencons_priv *cons, int c, bool force_flush)
476 {
477
478 xencons_lock(cons);
479 if ((cons->wp - cons->wc) < WBUF_SIZE)
480 cons->wbuf[WBUF_MASK(cons->wp++)] = c;
481 xencons_unlock(cons);
482
483 xencons_tx_flush(cons, force_flush);
484
485 return (xencons_tx_full(cons));
486 }
487
488 static int
xencons_getc(struct xencons_priv * cons)489 xencons_getc(struct xencons_priv *cons)
490 {
491 int ret;
492
493 xencons_lock(cons);
494 if (cons->rp != cons->rc) {
495 /* We need to return only one char */
496 ret = (int)cons->rbuf[RBUF_MASK(cons->rc)];
497 cons->rc++;
498 } else {
499 ret = -1;
500 }
501
502 xencons_unlock(cons);
503
504 return (ret);
505 }
506
507 static bool
xencons_tx(struct tty * tp)508 xencons_tx(struct tty *tp)
509 {
510 bool cons_full;
511 char c;
512 struct xencons_priv *cons;
513
514 cons = tty_softc(tp);
515
516 tty_assert_locked(tp);
517
518 /*
519 * Don't transmit any character if the buffer is full. Otherwise,
520 * characters may be lost
521 */
522 if (xencons_tx_full(cons))
523 return (false);
524
525 cons_full = false;
526 while (!cons_full && ttydisc_getc(tp, &c, 1) == 1)
527 cons_full = xencons_putc(cons, c, false);
528
529 return (!cons_full);
530 }
531
532 static void
xencons_intr(void * arg)533 xencons_intr(void *arg)
534 {
535 struct tty *tp;
536 struct xencons_priv *cons;
537 int ret;
538
539 tp = arg;
540 cons = tty_softc(tp);
541
542 /*
543 * The input will be used by the low-level console when KDB is active
544 */
545 if (kdb_active)
546 return;
547
548 /*
549 * It's not necessary to retrieve input when the tty is not opened
550 */
551 if (!cons->opened)
552 return;
553
554 xencons_rx(cons);
555
556 tty_lock(tp);
557 while ((ret = xencons_getc(cons)) != -1) {
558 #ifdef KDB
559 kdb_alt_break(ret, &cons->altbrk);
560 #endif
561 ttydisc_rint(tp, ret, 0);
562 }
563 ttydisc_rint_done(tp);
564 tty_unlock(tp);
565
566 /* Try to flush remaining characters if necessary */
567 xencons_tx_flush(cons, 0);
568 }
569
570 /*
571 * Helpers to call while shutting down:
572 * - Force flush all output
573 */
574 static void
xencons_shutdown(void * arg,int howto)575 xencons_shutdown(void *arg, int howto)
576 {
577 struct tty *tp;
578
579 tp = arg;
580
581 xencons_tx_flush(tty_softc(tp), 1);
582 }
583
584 /*---------------------- Low-level console driver ---------------------------*/
585 static void
xencons_cnprobe(struct consdev * cp)586 xencons_cnprobe(struct consdev *cp)
587 {
588
589 if (!xen_domain())
590 return;
591
592 cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
593 sprintf(cp->cn_name, "%s0", driver_name);
594 }
595
596 static void
xencons_cninit(struct consdev * cp)597 xencons_cninit(struct consdev *cp)
598 {
599
600 xencons_early_init();
601 }
602
603 static void
xencons_cnterm(struct consdev * cp)604 xencons_cnterm(struct consdev *cp)
605 {
606 }
607
608 static void
xencons_cngrab(struct consdev * cp)609 xencons_cngrab(struct consdev *cp)
610 {
611 }
612
613 static void
xencons_cnungrab(struct consdev * cp)614 xencons_cnungrab(struct consdev *cp)
615 {
616 }
617
618 static int
xencons_cngetc(struct consdev * dev)619 xencons_cngetc(struct consdev *dev)
620 {
621
622 xencons_rx(&main_cons);
623
624 return (xencons_getc(&main_cons));
625 }
626
627 static void
xencons_cnputc(struct consdev * dev,int c)628 xencons_cnputc(struct consdev *dev, int c)
629 {
630 /*
631 * The low-level console is used by KDB and panic. We have to ensure
632 * that any character sent will be seen by the backend.
633 */
634 xencons_putc(&main_cons, c, true);
635 }
636
637 CONSOLE_DRIVER(xencons);
638
639 /*----------------------------- TTY driver ---------------------------------*/
640
641 static int
xencons_tty_open(struct tty * tp)642 xencons_tty_open(struct tty *tp)
643 {
644 struct xencons_priv *cons;
645
646 cons = tty_softc(tp);
647
648 cons->opened = true;
649
650 return (0);
651 }
652
653 static void
xencons_tty_close(struct tty * tp)654 xencons_tty_close(struct tty *tp)
655 {
656 struct xencons_priv *cons;
657
658 cons = tty_softc(tp);
659
660 cons->opened = false;
661 }
662
663 static void
xencons_timeout(void * v)664 xencons_timeout(void *v)
665 {
666 struct tty *tp;
667 struct xencons_priv *cons;
668
669 tp = v;
670 cons = tty_softc(tp);
671
672 if (!xencons_tx(tp))
673 callout_reset(&cons->callout, XC_POLLTIME,
674 xencons_timeout, tp);
675 }
676
677 static void
xencons_tty_outwakeup(struct tty * tp)678 xencons_tty_outwakeup(struct tty *tp)
679 {
680 struct xencons_priv *cons;
681
682 cons = tty_softc(tp);
683
684 callout_stop(&cons->callout);
685
686 if (!xencons_tx(tp))
687 callout_reset(&cons->callout, XC_POLLTIME,
688 xencons_timeout, tp);
689 }
690
691 static struct ttydevsw xencons_ttydevsw = {
692 .tsw_flags = TF_NOPREFIX,
693 .tsw_open = xencons_tty_open,
694 .tsw_close = xencons_tty_close,
695 .tsw_outwakeup = xencons_tty_outwakeup,
696 };
697
698 /*------------------------ Main console driver ------------------------------*/
699 static void
xencons_identify(driver_t * driver,device_t parent)700 xencons_identify(driver_t *driver, device_t parent)
701 {
702 device_t child __unused;
703
704 if (main_cons.ops == NULL)
705 return;
706
707 child = BUS_ADD_CHILD(parent, 0, driver_name, 0);
708 }
709
710 static int
xencons_probe(device_t dev)711 xencons_probe(device_t dev)
712 {
713
714 device_set_desc(dev, "Xen Console");
715 return (BUS_PROBE_NOWILDCARD);
716 }
717
718 static int
xencons_attach(device_t dev)719 xencons_attach(device_t dev)
720 {
721 struct tty *tp;
722 /*
723 * The main console is already allocated statically in order to
724 * support low-level console
725 */
726 struct xencons_priv *cons;
727 int err;
728
729 cons = &main_cons;
730
731 tp = tty_alloc(&xencons_ttydevsw, cons);
732 tty_makedev(tp, NULL, "%s%r", driver_name, 0);
733 device_set_softc(dev, tp);
734
735 callout_init_mtx(&cons->callout, tty_getlock(tp), 0);
736
737 err = cons->ops->init(dev, tp, xencons_intr);
738 if (err != 0) {
739 device_printf(dev, "Unable to initialize the console (%d)\n",
740 err);
741 return (err);
742 }
743
744 /* register handler to flush console on shutdown */
745 if ((EVENTHANDLER_REGISTER(shutdown_post_sync, xencons_shutdown,
746 tp, SHUTDOWN_PRI_DEFAULT)) == NULL)
747 device_printf(dev, "shutdown event registration failed!\n");
748
749 return (0);
750 }
751
752 static int
xencons_resume(device_t dev)753 xencons_resume(device_t dev)
754 {
755 struct xencons_priv *cons;
756 struct tty *tp;
757 int err;
758
759 tp = device_get_softc(dev);
760 cons = tty_softc(tp);
761 xen_intr_unbind(&cons->intr_handle);
762
763 err = cons->ops->init(dev, tp, xencons_intr);
764 if (err != 0) {
765 device_printf(dev, "Unable to resume the console (%d)\n", err);
766 return (err);
767 }
768
769 return (0);
770 }
771
772 static device_method_t xencons_methods[] = {
773 DEVMETHOD(device_identify, xencons_identify),
774 DEVMETHOD(device_probe, xencons_probe),
775 DEVMETHOD(device_attach, xencons_attach),
776 DEVMETHOD(device_resume, xencons_resume),
777
778 DEVMETHOD_END
779 };
780
781 static driver_t xencons_driver = {
782 driver_name,
783 xencons_methods,
784 0,
785 };
786
787 DRIVER_MODULE(xc, xenpv, xencons_driver, 0, 0);
788