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