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