xref: /openbsd/usr.sbin/vmd/ns8250.c (revision 771fbea0)
1 /* $OpenBSD: ns8250.c,v 1.30 2021/03/29 13:09:41 dv Exp $ */
2 /*
3  * Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/ttycom.h>
20 
21 #include <dev/ic/comreg.h>
22 
23 #include <machine/vmmvar.h>
24 
25 #include <errno.h>
26 #include <event.h>
27 #include <pthread.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "ns8250.h"
32 #include "proc.h"
33 #include "vmd.h"
34 #include "vmm.h"
35 #include "atomicio.h"
36 
37 extern char *__progname;
38 struct ns8250_dev com1_dev;
39 
40 /* Flags to distinguish calling threads to com_rcv */
41 #define NS8250_DEV_THREAD	0
42 #define NS8250_CPU_THREAD 	1
43 
44 static struct vm_dev_pipe dev_pipe;
45 
46 static void com_rcv_event(int, short, void *);
47 static void com_rcv(struct ns8250_dev *, uint32_t, uint32_t, int);
48 
49 /*
50  * ns8250_pipe_dispatch
51  *
52  * Reads a message off the pipe, expecting a reguest to reset events after a
53  * zero-byte read from the com device.
54  */
55 static void
56 ns8250_pipe_dispatch(int fd, short event, void *arg)
57 {
58 	enum pipe_msg_type msg;
59 
60 	msg = vm_pipe_recv(&dev_pipe);
61 	switch(msg) {
62 	case NS8250_ZERO_READ:
63 		log_debug("%s: resetting events after zero byte read", __func__);
64 		event_del(&com1_dev.event);
65 		event_add(&com1_dev.wake, NULL);
66 		break;
67 	case NS8250_RATELIMIT:
68 		evtimer_add(&com1_dev.rate, &com1_dev.rate_tv);
69 		break;
70 	default:
71 		fatalx("%s: unexpected pipe message %d", __func__, msg);
72 	}
73 }
74 
75 /*
76  * ratelimit
77  *
78  * Timeout callback function used when we have to slow down the output rate
79  * from the emulated serial port.
80  *
81  * Parameters:
82  *  fd: unused
83  *  type: unused
84  *  arg: unused
85  */
86 static void
87 ratelimit(int fd, short type, void *arg)
88 {
89 	/* Set TXRDY and clear "no pending interrupt" */
90 	mutex_lock(&com1_dev.mutex);
91 	com1_dev.regs.iir |= IIR_TXRDY;
92 	com1_dev.regs.iir &= ~IIR_NOPEND;
93 
94 	vcpu_assert_pic_irq(com1_dev.vmid, 0, com1_dev.irq);
95 	vcpu_deassert_pic_irq(com1_dev.vmid, 0, com1_dev.irq);
96 	mutex_unlock(&com1_dev.mutex);
97 }
98 
99 void
100 ns8250_init(int fd, uint32_t vmid)
101 {
102 	int ret;
103 
104 	memset(&com1_dev, 0, sizeof(com1_dev));
105 	ret = pthread_mutex_init(&com1_dev.mutex, NULL);
106 	if (ret) {
107 		errno = ret;
108 		fatal("could not initialize com1 mutex");
109 	}
110 
111 	com1_dev.fd = fd;
112 	com1_dev.irq = 4;
113 	com1_dev.portid = NS8250_COM1;
114 	com1_dev.rcv_pending = 0;
115 	com1_dev.vmid = vmid;
116 	com1_dev.byte_out = 0;
117 	com1_dev.regs.divlo = 1;
118 	com1_dev.baudrate = 115200;
119 
120 	/*
121 	 * Our serial port is essentially instantaneous, with infinite
122 	 * baudrate capability. To adjust for the selected baudrate,
123 	 * we calculate how many characters could be transmitted in a 10ms
124 	 * period (pause_ct) and then delay 10ms after each pause_ct sized
125 	 * group of characters have been transmitted. Since it takes nearly
126 	 * zero time to send the actual characters, the total amount of time
127 	 * spent is roughly equal to what it would be on real hardware.
128 	 *
129 	 * To make things simple, we don't adjust for different sized bytes
130 	 * (and parity, stop bits, etc) and simply assume each character
131 	 * output is 8 bits.
132 	 */
133 	com1_dev.pause_ct = (com1_dev.baudrate / 8) / 1000 * 10;
134 
135 	event_set(&com1_dev.event, com1_dev.fd, EV_READ | EV_PERSIST,
136 	    com_rcv_event, (void *)(intptr_t)vmid);
137 
138 	/*
139 	 * Whenever fd is writable implies that the pty slave is connected.
140 	 * Until then, avoid waiting for read events since EOF would constantly
141 	 * be reached.
142 	 */
143 	event_set(&com1_dev.wake, com1_dev.fd, EV_WRITE,
144 	    com_rcv_event, (void *)(intptr_t)vmid);
145 	event_add(&com1_dev.wake, NULL);
146 
147 	/* Rate limiter for simulating baud rate */
148 	timerclear(&com1_dev.rate_tv);
149 	com1_dev.rate_tv.tv_usec = 10000;
150 	evtimer_set(&com1_dev.rate, ratelimit, NULL);
151 
152 	vm_pipe_init(&dev_pipe, ns8250_pipe_dispatch);
153 	event_add(&dev_pipe.read_ev, NULL);
154 }
155 
156 static void
157 com_rcv_event(int fd, short kind, void *arg)
158 {
159 	mutex_lock(&com1_dev.mutex);
160 
161 	if (kind == EV_WRITE) {
162 		event_add(&com1_dev.event, NULL);
163 		mutex_unlock(&com1_dev.mutex);
164 		return;
165 	}
166 
167 	/*
168 	 * We already have other data pending to be received. The data that
169 	 * has become available now will be moved to the com port later by
170 	 * the vcpu.
171 	 */
172 	if (!com1_dev.rcv_pending) {
173 		if (com1_dev.regs.lsr & LSR_RXRDY)
174 			com1_dev.rcv_pending = 1;
175 		else
176 			com_rcv(&com1_dev, (uintptr_t)arg, 0, NS8250_DEV_THREAD);
177 	}
178 
179 	/* If pending interrupt, inject */
180 	if ((com1_dev.regs.iir & IIR_NOPEND) == 0) {
181 		/* XXX: vcpu_id */
182 		vcpu_assert_pic_irq((uintptr_t)arg, 0, com1_dev.irq);
183 		vcpu_deassert_pic_irq((uintptr_t)arg, 0, com1_dev.irq);
184 	}
185 
186 	mutex_unlock(&com1_dev.mutex);
187 }
188 
189 /*
190  * com_rcv_handle_break
191  *
192  * Set/clear break detected condition based on received TIOCUCNTL_{S,C}BRK.
193  */
194 static int
195 com_rcv_handle_break(struct ns8250_dev *com, uint8_t cmd)
196 {
197 	switch (cmd) {
198 	case 0: /* DATA */
199 		return 0;
200 	case TIOCUCNTL_SBRK:
201 		com->regs.lsr |= LSR_BI;
202 		break;
203 	case TIOCUCNTL_CBRK:
204 		com->regs.lsr &= ~LSR_BI;
205 		break;
206 	default:
207 		log_warnx("unexpected UCNTL ioctl: %d", cmd);
208 	}
209 
210 	return 1;
211 }
212 
213 /*
214  * com_rcv
215  *
216  * Move received byte into com data register.
217  * Must be called with the mutex of the com device acquired
218  */
219 static void
220 com_rcv(struct ns8250_dev *com, uint32_t vm_id, uint32_t vcpu_id, int thread)
221 {
222 	char buf[2];
223 	ssize_t sz;
224 
225 	/*
226 	 * Is there a new character available on com1?
227 	 * If so, consume the character, buffer it into the com1 data register
228 	 * assert IRQ4, and set the line status register RXRDY bit.
229 	 */
230 	sz = read(com->fd, buf, sizeof(buf));
231 	if (sz == -1) {
232 		/*
233 		 * If we get EAGAIN, we'll retry and get the character later.
234 		 * This error can happen when typing two characters at once
235 		 * at the keyboard, for example.
236 		 */
237 		if (errno != EAGAIN)
238 			log_warn("unexpected read error on com device");
239 	} else if (sz == 0) {
240 		if (thread == NS8250_DEV_THREAD) {
241 			event_del(&com->event);
242 			event_add(&com->wake, NULL);
243 		} else {
244 			/* Called by vcpu thread, use pipe for event changes */
245 			vm_pipe_send(&dev_pipe, NS8250_ZERO_READ);
246 		}
247 		return;
248 	} else if (sz != 1 && sz != 2)
249 		log_warnx("unexpected read return value %zd on com device", sz);
250 	else {
251 		if (com_rcv_handle_break(com, buf[0]))
252 			buf[1] = 0;
253 
254 		com->regs.lsr |= LSR_RXRDY;
255 		com->regs.data = buf[1];
256 
257 		if (com->regs.ier & IER_ERXRDY) {
258 			com->regs.iir |= IIR_RXRDY;
259 			com->regs.iir &= ~IIR_NOPEND;
260 		}
261 	}
262 
263 	com->rcv_pending = fd_hasdata(com->fd);
264 }
265 
266 /*
267  * vcpu_process_com_data
268  *
269  * Emulate in/out instructions to the com1 (ns8250) UART data register
270  *
271  * Parameters:
272  *  vei: vm exit information from vmm(4) containing information on the in/out
273  *      instruction being performed
274  *
275  * Return value:
276  *  interrupt to inject, or 0xFF if nothing to inject
277  */
278 uint8_t
279 vcpu_process_com_data(struct vm_exit *vei, uint32_t vm_id, uint32_t vcpu_id)
280 {
281 	/*
282 	 * vei_dir == VEI_DIR_OUT : out instruction
283 	 *
284 	 * The guest wrote to the data register. Since we are emulating a
285 	 * no-fifo chip, write the character immediately to the pty and
286 	 * assert TXRDY in IIR (if the guest has requested TXRDY interrupt
287 	 * reporting)
288 	 */
289 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
290 		if (com1_dev.regs.lcr & LCR_DLAB) {
291 			com1_dev.regs.divlo = vei->vei.vei_data;
292 			return 0xFF;
293 		}
294 
295 		write(com1_dev.fd, &vei->vei.vei_data, 1);
296 		com1_dev.byte_out++;
297 
298 		if (com1_dev.regs.ier & IER_ETXRDY) {
299 			/* Limit output rate if needed */
300 			if (com1_dev.pause_ct > 0 &&
301 			    com1_dev.byte_out % com1_dev.pause_ct == 0) {
302 				vm_pipe_send(&dev_pipe, NS8250_RATELIMIT);
303 			} else {
304 				/* Set TXRDY and clear "no pending interrupt" */
305 				com1_dev.regs.iir |= IIR_TXRDY;
306 				com1_dev.regs.iir &= ~IIR_NOPEND;
307 			}
308 		}
309 	} else {
310 		if (com1_dev.regs.lcr & LCR_DLAB) {
311 			set_return_data(vei, com1_dev.regs.divlo);
312 			return 0xFF;
313 		}
314 		/*
315 		 * vei_dir == VEI_DIR_IN : in instruction
316 		 *
317 		 * The guest read from the data register. Check to see if
318 		 * there is data available (RXRDY) and if so, consume the
319 		 * input data and return to the guest. Also clear the
320 		 * interrupt info register regardless.
321 		 */
322 		if (com1_dev.regs.lsr & LSR_RXRDY) {
323 			set_return_data(vei, com1_dev.regs.data);
324 			com1_dev.regs.data = 0x0;
325 			com1_dev.regs.lsr &= ~LSR_RXRDY;
326 		} else {
327 			set_return_data(vei, com1_dev.regs.data);
328 			log_warnx("%s: guest reading com1 when not ready",
329 			    __func__);
330 		}
331 
332 		/* Reading the data register always clears RXRDY from IIR */
333 		com1_dev.regs.iir &= ~IIR_RXRDY;
334 
335 		/*
336 		 * Clear "interrupt pending" by setting IIR low bit to 1
337 		 * if no interrupt are pending
338 		 */
339 		if (com1_dev.regs.iir == 0x0)
340 			com1_dev.regs.iir = 0x1;
341 
342 		if (com1_dev.rcv_pending)
343 			com_rcv(&com1_dev, vm_id, vcpu_id, NS8250_CPU_THREAD);
344 	}
345 
346 	/* If pending interrupt, make sure it gets injected */
347 	if ((com1_dev.regs.iir & IIR_NOPEND) == 0)
348 		return (com1_dev.irq);
349 
350 	return (0xFF);
351 }
352 
353 /*
354  * vcpu_process_com_lcr
355  *
356  * Emulate in/out instructions to the com1 (ns8250) UART line control register
357  *
358  * Paramters:
359  *  vei: vm exit information from vmm(4) containing information on the in/out
360  *      instruction being performed
361  */
362 void
363 vcpu_process_com_lcr(struct vm_exit *vei)
364 {
365 	uint8_t data = (uint8_t)vei->vei.vei_data;
366 	uint16_t divisor;
367 
368 	/*
369 	 * vei_dir == VEI_DIR_OUT : out instruction
370 	 *
371 	 * Write content to line control register
372 	 */
373 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
374 		if (com1_dev.regs.lcr & LCR_DLAB) {
375 			if (!(data & LCR_DLAB)) {
376 				if (com1_dev.regs.divlo == 0 &&
377 				    com1_dev.regs.divhi == 0) {
378 					log_warnx("%s: ignoring invalid "
379 					    "baudrate", __func__);
380 				} else {
381 					divisor = com1_dev.regs.divlo |
382 					     com1_dev.regs.divhi << 8;
383 					com1_dev.baudrate = 115200 / divisor;
384 					com1_dev.pause_ct =
385 					    (com1_dev.baudrate / 8) / 1000 * 10;
386 				}
387 
388 				log_debug("%s: set baudrate = %d", __func__,
389 				    com1_dev.baudrate);
390 			}
391 		}
392 		com1_dev.regs.lcr = (uint8_t)vei->vei.vei_data;
393 	} else {
394 		/*
395 		 * vei_dir == VEI_DIR_IN : in instruction
396 		 *
397 		 * Read line control register
398 		 */
399 		set_return_data(vei, com1_dev.regs.lcr);
400 	}
401 }
402 
403 /*
404  * vcpu_process_com_iir
405  *
406  * Emulate in/out instructions to the com1 (ns8250) UART interrupt information
407  * register. Note that writes to this register actually are to a different
408  * register, the FCR (FIFO control register) that we don't emulate but still
409  * consume the data provided.
410  *
411  * Parameters:
412  *  vei: vm exit information from vmm(4) containing information on the in/out
413  *      instruction being performed
414  */
415 void
416 vcpu_process_com_iir(struct vm_exit *vei)
417 {
418 	/*
419 	 * vei_dir == VEI_DIR_OUT : out instruction
420 	 *
421 	 * Write to FCR
422 	 */
423 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
424 		com1_dev.regs.fcr = vei->vei.vei_data;
425 	} else {
426 		/*
427 		 * vei_dir == VEI_DIR_IN : in instruction
428 		 *
429 		 * Read IIR. Reading the IIR resets the TXRDY bit in the IIR
430 		 * after the data is read.
431 		 */
432 		set_return_data(vei, com1_dev.regs.iir);
433 		com1_dev.regs.iir &= ~IIR_TXRDY;
434 
435 		/*
436 		 * Clear "interrupt pending" by setting IIR low bit to 1
437 		 * if no interrupts are pending
438 		 */
439 		if (com1_dev.regs.iir == 0x0)
440 			com1_dev.regs.iir = 0x1;
441 	}
442 }
443 
444 /*
445  * vcpu_process_com_mcr
446  *
447  * Emulate in/out instructions to the com1 (ns8250) UART modem control
448  * register.
449  *
450  * Parameters:
451  *  vei: vm exit information from vmm(4) containing information on the in/out
452  *      instruction being performed
453  */
454 void
455 vcpu_process_com_mcr(struct vm_exit *vei)
456 {
457 	/*
458 	 * vei_dir == VEI_DIR_OUT : out instruction
459 	 *
460 	 * Write to MCR
461 	 */
462 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
463 		com1_dev.regs.mcr = vei->vei.vei_data;
464 	} else {
465 		/*
466 		 * vei_dir == VEI_DIR_IN : in instruction
467 		 *
468 		 * Read from MCR
469 		 */
470 		set_return_data(vei, com1_dev.regs.mcr);
471 	}
472 }
473 
474 /*
475  * vcpu_process_com_lsr
476  *
477  * Emulate in/out instructions to the com1 (ns8250) UART line status register.
478  *
479  * Parameters:
480  *  vei: vm exit information from vmm(4) containing information on the in/out
481  *      instruction being performed
482  */
483 void
484 vcpu_process_com_lsr(struct vm_exit *vei)
485 {
486 	/*
487 	 * vei_dir == VEI_DIR_OUT : out instruction
488 	 *
489 	 * Write to LSR. This is an illegal operation, so we just log it and
490 	 * continue.
491 	 */
492 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
493 		log_warnx("%s: LSR UART write 0x%x unsupported",
494 		    __progname, vei->vei.vei_data);
495 	} else {
496 		/*
497 		 * vei_dir == VEI_DIR_IN : in instruction
498 		 *
499 		 * Read from LSR. We always report TXRDY and TSRE since we
500 		 * can process output characters immediately (at any time).
501 		 */
502 		set_return_data(vei, com1_dev.regs.lsr | LSR_TSRE | LSR_TXRDY);
503 	}
504 }
505 
506 /*
507  * vcpu_process_com_msr
508  *
509  * Emulate in/out instructions to the com1 (ns8250) UART modem status register.
510  *
511  * Parameters:
512  *  vei: vm exit information from vmm(4) containing information on the in/out
513  *      instruction being performed
514  */
515 void
516 vcpu_process_com_msr(struct vm_exit *vei)
517 {
518 	/*
519 	 * vei_dir == VEI_DIR_OUT : out instruction
520 	 *
521 	 * Write to MSR. This is an illegal operation, so we just log it and
522 	 * continue.
523 	 */
524 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
525 		log_warnx("%s: MSR UART write 0x%x unsupported",
526 		    __progname, vei->vei.vei_data);
527 	} else {
528 		/*
529 		 * vei_dir == VEI_DIR_IN : in instruction
530 		 *
531 		 * Read from MSR. We always report DCD, DSR, and CTS.
532 		 */
533 		set_return_data(vei, com1_dev.regs.lsr | MSR_DCD | MSR_DSR |
534 		    MSR_CTS);
535 	}
536 }
537 
538 /*
539  * vcpu_process_com_scr
540  *
541  * Emulate in/out instructions to the com1 (ns8250) UART scratch register.
542  *
543  * Parameters:
544  *  vei: vm exit information from vmm(4) containing information on the in/out
545  *      instruction being performed
546  */
547 void
548 vcpu_process_com_scr(struct vm_exit *vei)
549 {
550 	/*
551 	 * vei_dir == VEI_DIR_OUT : out instruction
552 	 *
553 	 * The 8250 does not have a scratch register.
554 	 */
555 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
556 		com1_dev.regs.scr = 0xFF;
557 	} else {
558 		/*
559 		 * vei_dir == VEI_DIR_IN : in instruction
560 		 *
561 		 * Read from SCR
562 		 */
563 		set_return_data(vei, com1_dev.regs.scr);
564 	}
565 }
566 
567 /*
568  * vcpu_process_com_ier
569  *
570  * Emulate in/out instructions to the com1 (ns8250) UART interrupt enable
571  * register.
572  *
573  * Parameters:
574  *  vei: vm exit information from vmm(4) containing information on the in/out
575  *      instruction being performed
576  */
577 void
578 vcpu_process_com_ier(struct vm_exit *vei)
579 {
580 	/*
581 	 * vei_dir == VEI_DIR_OUT : out instruction
582 	 *
583 	 * Write to IER
584 	 */
585 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
586 		if (com1_dev.regs.lcr & LCR_DLAB) {
587 			com1_dev.regs.divhi = vei->vei.vei_data;
588 			return;
589 		}
590 		com1_dev.regs.ier = vei->vei.vei_data;
591 		if (com1_dev.regs.ier & IER_ETXRDY)
592 			com1_dev.regs.iir |= IIR_TXRDY;
593 	} else {
594 		if (com1_dev.regs.lcr & LCR_DLAB) {
595 			set_return_data(vei, com1_dev.regs.divhi);
596 			return;
597 		}
598 		/*
599 		 * vei_dir == VEI_DIR_IN : in instruction
600 		 *
601 		 * Read from IER
602 		 */
603 		set_return_data(vei, com1_dev.regs.ier);
604 	}
605 }
606 
607 /*
608  * vcpu_exit_com
609  *
610  * Process com1 (ns8250) UART exits. vmd handles most basic 8250
611  * features
612  *
613  * Parameters:
614  *  vrp: vcpu run parameters containing guest state for this exit
615  *
616  * Return value:
617  *  Interrupt to inject to the guest VM, or 0xFF if no interrupt should
618  *      be injected.
619  */
620 uint8_t
621 vcpu_exit_com(struct vm_run_params *vrp)
622 {
623 	uint8_t intr = 0xFF;
624 	struct vm_exit *vei = vrp->vrp_exit;
625 
626 	mutex_lock(&com1_dev.mutex);
627 
628 	switch (vei->vei.vei_port) {
629 	case COM1_LCR:
630 		vcpu_process_com_lcr(vei);
631 		break;
632 	case COM1_IER:
633 		vcpu_process_com_ier(vei);
634 		break;
635 	case COM1_IIR:
636 		vcpu_process_com_iir(vei);
637 		break;
638 	case COM1_MCR:
639 		vcpu_process_com_mcr(vei);
640 		break;
641 	case COM1_LSR:
642 		vcpu_process_com_lsr(vei);
643 		break;
644 	case COM1_MSR:
645 		vcpu_process_com_msr(vei);
646 		break;
647 	case COM1_SCR:
648 		vcpu_process_com_scr(vei);
649 		break;
650 	case COM1_DATA:
651 		intr = vcpu_process_com_data(vei, vrp->vrp_vm_id,
652 		    vrp->vrp_vcpu_id);
653 		break;
654 	}
655 
656 	mutex_unlock(&com1_dev.mutex);
657 
658 	return (intr);
659 }
660 
661 int
662 ns8250_dump(int fd)
663 {
664 	log_debug("%s: sending UART", __func__);
665 	if (atomicio(vwrite, fd, &com1_dev.regs,
666 	    sizeof(com1_dev.regs)) != sizeof(com1_dev.regs)) {
667 		log_warnx("%s: error writing UART to fd", __func__);
668 		return (-1);
669 	}
670 	return (0);
671 }
672 
673 int
674 ns8250_restore(int fd, int con_fd, uint32_t vmid)
675 {
676 	int ret;
677 	log_debug("%s: receiving UART", __func__);
678 	if (atomicio(read, fd, &com1_dev.regs,
679 	    sizeof(com1_dev.regs)) != sizeof(com1_dev.regs)) {
680 		log_warnx("%s: error reading UART from fd", __func__);
681 		return (-1);
682 	}
683 
684 	ret = pthread_mutex_init(&com1_dev.mutex, NULL);
685 	if (ret) {
686 		errno = ret;
687 		fatal("could not initialize com1 mutex");
688 	}
689 	com1_dev.fd = con_fd;
690 	com1_dev.irq = 4;
691 	com1_dev.portid = NS8250_COM1;
692 	com1_dev.rcv_pending = 0;
693 	com1_dev.vmid = vmid;
694 	com1_dev.byte_out = 0;
695 	com1_dev.regs.divlo = 1;
696 	com1_dev.baudrate = 115200;
697 	com1_dev.pause_ct = (com1_dev.baudrate / 8) / 1000 * 10;
698 
699 	event_set(&com1_dev.event, com1_dev.fd, EV_READ | EV_PERSIST,
700 	    com_rcv_event, (void *)(intptr_t)vmid);
701 
702 	event_set(&com1_dev.wake, com1_dev.fd, EV_WRITE,
703 	    com_rcv_event, (void *)(intptr_t)vmid);
704 
705 	timerclear(&com1_dev.rate_tv);
706 	com1_dev.rate_tv.tv_usec = 10000;
707 	evtimer_set(&com1_dev.rate, ratelimit, NULL);
708 
709 	vm_pipe_init(&dev_pipe, ns8250_pipe_dispatch);
710 
711 	return (0);
712 }
713 
714 void
715 ns8250_stop()
716 {
717 	if(event_del(&com1_dev.event))
718 		log_warn("could not delete ns8250 event handler");
719 	event_del(&dev_pipe.read_ev);
720 	evtimer_del(&com1_dev.rate);
721 }
722 
723 void
724 ns8250_start()
725 {
726 	event_add(&com1_dev.event, NULL);
727 	event_add(&com1_dev.wake, NULL);
728 	event_add(&dev_pipe.read_ev, NULL);
729 	evtimer_add(&com1_dev.rate, &com1_dev.rate_tv);
730 }
731