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