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