1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 5 * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD$"); 34 35#include <sys/types.h> 36#include <machine/vmm.h> 37#include <machine/vmm_snapshot.h> 38 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42 43#include <vmmapi.h> 44 45#include "acpi.h" 46#include "debug.h" 47#include "bootrom.h" 48#include "inout.h" 49#include "pci_emul.h" 50#include "pci_irq.h" 51#include "pci_lpc.h" 52#include "pctestdev.h" 53#include "uart_emul.h" 54 55#define IO_ICU1 0x20 56#define IO_ICU2 0xA0 57 58SET_DECLARE(lpc_dsdt_set, struct lpc_dsdt); 59SET_DECLARE(lpc_sysres_set, struct lpc_sysres); 60 61#define ELCR_PORT 0x4d0 62SYSRES_IO(ELCR_PORT, 2); 63 64#define IO_TIMER1_PORT 0x40 65 66#define NMISC_PORT 0x61 67SYSRES_IO(NMISC_PORT, 1); 68 69static struct pci_devinst *lpc_bridge; 70 71static const char *romfile; 72 73#define LPC_UART_NUM 4 74static struct lpc_uart_softc { 75 struct uart_softc *uart_softc; 76 const char *opts; 77 int iobase; 78 int irq; 79 int enabled; 80} lpc_uart_softc[LPC_UART_NUM]; 81 82static const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2", "COM3", "COM4" }; 83 84static bool pctestdev_present; 85 86/* 87 * LPC device configuration is in the following form: 88 * <lpc_device_name>[,<options>] 89 * For e.g. "com1,stdio" or "bootrom,/var/romfile" 90 */ 91int 92lpc_device_parse(const char *opts) 93{ 94 int unit, error; 95 char *str, *cpy, *lpcdev; 96 97 error = -1; 98 str = cpy = strdup(opts); 99 lpcdev = strsep(&str, ","); 100 if (lpcdev != NULL) { 101 if (strcasecmp(lpcdev, "bootrom") == 0) { 102 romfile = str; 103 error = 0; 104 goto done; 105 } 106 for (unit = 0; unit < LPC_UART_NUM; unit++) { 107 if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) { 108 lpc_uart_softc[unit].opts = str; 109 error = 0; 110 goto done; 111 } 112 } 113 if (strcasecmp(lpcdev, pctestdev_getname()) == 0) { 114 if (pctestdev_present) { 115 EPRINTLN("More than one %s device conf is " 116 "specified; only one is allowed.", 117 pctestdev_getname()); 118 } else if (pctestdev_parse(str) == 0) { 119 pctestdev_present = true; 120 error = 0; 121 free(cpy); 122 goto done; 123 } 124 } 125 } 126 127done: 128 if (error) 129 free(cpy); 130 131 return (error); 132} 133 134void 135lpc_print_supported_devices() 136{ 137 size_t i; 138 139 printf("bootrom\n"); 140 for (i = 0; i < LPC_UART_NUM; i++) 141 printf("%s\n", lpc_uart_names[i]); 142 printf("%s\n", pctestdev_getname()); 143} 144 145const char * 146lpc_bootrom(void) 147{ 148 149 return (romfile); 150} 151 152static void 153lpc_uart_intr_assert(void *arg) 154{ 155 struct lpc_uart_softc *sc = arg; 156 157 assert(sc->irq >= 0); 158 159 vm_isa_pulse_irq(lpc_bridge->pi_vmctx, sc->irq, sc->irq); 160} 161 162static void 163lpc_uart_intr_deassert(void *arg) 164{ 165 /* 166 * The COM devices on the LPC bus generate edge triggered interrupts, 167 * so nothing more to do here. 168 */ 169} 170 171static int 172lpc_uart_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 173 uint32_t *eax, void *arg) 174{ 175 int offset; 176 struct lpc_uart_softc *sc = arg; 177 178 offset = port - sc->iobase; 179 180 switch (bytes) { 181 case 1: 182 if (in) 183 *eax = uart_read(sc->uart_softc, offset); 184 else 185 uart_write(sc->uart_softc, offset, *eax); 186 break; 187 case 2: 188 if (in) { 189 *eax = uart_read(sc->uart_softc, offset); 190 *eax |= uart_read(sc->uart_softc, offset + 1) << 8; 191 } else { 192 uart_write(sc->uart_softc, offset, *eax); 193 uart_write(sc->uart_softc, offset + 1, *eax >> 8); 194 } 195 break; 196 default: 197 return (-1); 198 } 199 200 return (0); 201} 202 203static int 204lpc_init(struct vmctx *ctx) 205{ 206 struct lpc_uart_softc *sc; 207 struct inout_port iop; 208 const char *name; 209 int unit, error; 210 211 if (romfile != NULL) { 212 error = bootrom_loadrom(ctx, romfile); 213 if (error) 214 return (error); 215 } 216 217 /* COM1 and COM2 */ 218 for (unit = 0; unit < LPC_UART_NUM; unit++) { 219 sc = &lpc_uart_softc[unit]; 220 name = lpc_uart_names[unit]; 221 222 if (uart_legacy_alloc(unit, &sc->iobase, &sc->irq) != 0) { 223 EPRINTLN("Unable to allocate resources for " 224 "LPC device %s", name); 225 return (-1); 226 } 227 pci_irq_reserve(sc->irq); 228 229 sc->uart_softc = uart_init(lpc_uart_intr_assert, 230 lpc_uart_intr_deassert, sc); 231 232 if (uart_set_backend(sc->uart_softc, sc->opts) != 0) { 233 EPRINTLN("Unable to initialize backend '%s' " 234 "for LPC device %s", sc->opts, name); 235 return (-1); 236 } 237 238 bzero(&iop, sizeof(struct inout_port)); 239 iop.name = name; 240 iop.port = sc->iobase; 241 iop.size = UART_IO_BAR_SIZE; 242 iop.flags = IOPORT_F_INOUT; 243 iop.handler = lpc_uart_io_handler; 244 iop.arg = sc; 245 246 error = register_inout(&iop); 247 assert(error == 0); 248 sc->enabled = 1; 249 } 250 251 /* pc-testdev */ 252 if (pctestdev_present) { 253 error = pctestdev_init(ctx); 254 if (error) 255 return (error); 256 } 257 258 return (0); 259} 260 261static void 262pci_lpc_write_dsdt(struct pci_devinst *pi) 263{ 264 struct lpc_dsdt **ldpp, *ldp; 265 266 dsdt_line(""); 267 dsdt_line("Device (ISA)"); 268 dsdt_line("{"); 269 dsdt_line(" Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func); 270 dsdt_line(" OperationRegion (LPCR, PCI_Config, 0x00, 0x100)"); 271 dsdt_line(" Field (LPCR, AnyAcc, NoLock, Preserve)"); 272 dsdt_line(" {"); 273 dsdt_line(" Offset (0x60),"); 274 dsdt_line(" PIRA, 8,"); 275 dsdt_line(" PIRB, 8,"); 276 dsdt_line(" PIRC, 8,"); 277 dsdt_line(" PIRD, 8,"); 278 dsdt_line(" Offset (0x68),"); 279 dsdt_line(" PIRE, 8,"); 280 dsdt_line(" PIRF, 8,"); 281 dsdt_line(" PIRG, 8,"); 282 dsdt_line(" PIRH, 8"); 283 dsdt_line(" }"); 284 dsdt_line(""); 285 286 dsdt_indent(1); 287 SET_FOREACH(ldpp, lpc_dsdt_set) { 288 ldp = *ldpp; 289 ldp->handler(); 290 } 291 292 dsdt_line(""); 293 dsdt_line("Device (PIC)"); 294 dsdt_line("{"); 295 dsdt_line(" Name (_HID, EisaId (\"PNP0000\"))"); 296 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 297 dsdt_line(" {"); 298 dsdt_indent(2); 299 dsdt_fixed_ioport(IO_ICU1, 2); 300 dsdt_fixed_ioport(IO_ICU2, 2); 301 dsdt_fixed_irq(2); 302 dsdt_unindent(2); 303 dsdt_line(" })"); 304 dsdt_line("}"); 305 306 dsdt_line(""); 307 dsdt_line("Device (TIMR)"); 308 dsdt_line("{"); 309 dsdt_line(" Name (_HID, EisaId (\"PNP0100\"))"); 310 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 311 dsdt_line(" {"); 312 dsdt_indent(2); 313 dsdt_fixed_ioport(IO_TIMER1_PORT, 4); 314 dsdt_fixed_irq(0); 315 dsdt_unindent(2); 316 dsdt_line(" })"); 317 dsdt_line("}"); 318 dsdt_unindent(1); 319 320 dsdt_line("}"); 321} 322 323static void 324pci_lpc_sysres_dsdt(void) 325{ 326 struct lpc_sysres **lspp, *lsp; 327 328 dsdt_line(""); 329 dsdt_line("Device (SIO)"); 330 dsdt_line("{"); 331 dsdt_line(" Name (_HID, EisaId (\"PNP0C02\"))"); 332 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 333 dsdt_line(" {"); 334 335 dsdt_indent(2); 336 SET_FOREACH(lspp, lpc_sysres_set) { 337 lsp = *lspp; 338 switch (lsp->type) { 339 case LPC_SYSRES_IO: 340 dsdt_fixed_ioport(lsp->base, lsp->length); 341 break; 342 case LPC_SYSRES_MEM: 343 dsdt_fixed_mem32(lsp->base, lsp->length); 344 break; 345 } 346 } 347 dsdt_unindent(2); 348 349 dsdt_line(" })"); 350 dsdt_line("}"); 351} 352LPC_DSDT(pci_lpc_sysres_dsdt); 353 354static void 355pci_lpc_uart_dsdt(void) 356{ 357 struct lpc_uart_softc *sc; 358 int unit; 359 360 for (unit = 0; unit < LPC_UART_NUM; unit++) { 361 sc = &lpc_uart_softc[unit]; 362 if (!sc->enabled) 363 continue; 364 dsdt_line(""); 365 dsdt_line("Device (%s)", lpc_uart_names[unit]); 366 dsdt_line("{"); 367 dsdt_line(" Name (_HID, EisaId (\"PNP0501\"))"); 368 dsdt_line(" Name (_UID, %d)", unit + 1); 369 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 370 dsdt_line(" {"); 371 dsdt_indent(2); 372 dsdt_fixed_ioport(sc->iobase, UART_IO_BAR_SIZE); 373 dsdt_fixed_irq(sc->irq); 374 dsdt_unindent(2); 375 dsdt_line(" })"); 376 dsdt_line("}"); 377 } 378} 379LPC_DSDT(pci_lpc_uart_dsdt); 380 381static int 382pci_lpc_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 383 int coff, int bytes, uint32_t val) 384{ 385 int pirq_pin; 386 387 if (bytes == 1) { 388 pirq_pin = 0; 389 if (coff >= 0x60 && coff <= 0x63) 390 pirq_pin = coff - 0x60 + 1; 391 if (coff >= 0x68 && coff <= 0x6b) 392 pirq_pin = coff - 0x68 + 5; 393 if (pirq_pin != 0) { 394 pirq_write(ctx, pirq_pin, val); 395 pci_set_cfgdata8(pi, coff, pirq_read(pirq_pin)); 396 return (0); 397 } 398 } 399 return (-1); 400} 401 402static void 403pci_lpc_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 404 int baridx, uint64_t offset, int size, uint64_t value) 405{ 406} 407 408static uint64_t 409pci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 410 int baridx, uint64_t offset, int size) 411{ 412 return (0); 413} 414 415#define LPC_DEV 0x7000 416#define LPC_VENDOR 0x8086 417 418static int 419pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 420{ 421 422 /* 423 * Do not allow more than one LPC bridge to be configured. 424 */ 425 if (lpc_bridge != NULL) { 426 EPRINTLN("Only one LPC bridge is allowed."); 427 return (-1); 428 } 429 430 /* 431 * Enforce that the LPC can only be configured on bus 0. This 432 * simplifies the ACPI DSDT because it can provide a decode for 433 * all legacy i/o ports behind bus 0. 434 */ 435 if (pi->pi_bus != 0) { 436 EPRINTLN("LPC bridge can be present only on bus 0."); 437 return (-1); 438 } 439 440 if (lpc_init(ctx) != 0) 441 return (-1); 442 443 /* initialize config space */ 444 pci_set_cfgdata16(pi, PCIR_DEVICE, LPC_DEV); 445 pci_set_cfgdata16(pi, PCIR_VENDOR, LPC_VENDOR); 446 pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); 447 pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA); 448 449 lpc_bridge = pi; 450 451 return (0); 452} 453 454char * 455lpc_pirq_name(int pin) 456{ 457 char *name; 458 459 if (lpc_bridge == NULL) 460 return (NULL); 461 asprintf(&name, "\\_SB.PC00.ISA.LNK%c,", 'A' + pin - 1); 462 return (name); 463} 464 465void 466lpc_pirq_routed(void) 467{ 468 int pin; 469 470 if (lpc_bridge == NULL) 471 return; 472 473 for (pin = 0; pin < 4; pin++) 474 pci_set_cfgdata8(lpc_bridge, 0x60 + pin, pirq_read(pin + 1)); 475 for (pin = 0; pin < 4; pin++) 476 pci_set_cfgdata8(lpc_bridge, 0x68 + pin, pirq_read(pin + 5)); 477} 478 479#ifdef BHYVE_SNAPSHOT 480static int 481pci_lpc_snapshot(struct vm_snapshot_meta *meta) 482{ 483 int unit, ret; 484 struct uart_softc *sc; 485 486 for (unit = 0; unit < LPC_UART_NUM; unit++) { 487 sc = lpc_uart_softc[unit].uart_softc; 488 489 ret = uart_snapshot(sc, meta); 490 if (ret != 0) 491 goto done; 492 } 493 494done: 495 return (ret); 496} 497#endif 498 499struct pci_devemu pci_de_lpc = { 500 .pe_emu = "lpc", 501 .pe_init = pci_lpc_init, 502 .pe_write_dsdt = pci_lpc_write_dsdt, 503 .pe_cfgwrite = pci_lpc_cfgwrite, 504 .pe_barwrite = pci_lpc_write, 505 .pe_barread = pci_lpc_read, 506#ifdef BHYVE_SNAPSHOT 507 .pe_snapshot = pci_lpc_snapshot, 508#endif 509}; 510PCI_EMUL_SET(pci_de_lpc); 511