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 38#include <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41 42#include <vmmapi.h> 43 44#include "acpi.h" 45#include "debug.h" 46#include "bootrom.h" 47#include "inout.h" 48#include "pci_emul.h" 49#include "pci_irq.h" 50#include "pci_lpc.h" 51#include "pctestdev.h" 52#include "uart_emul.h" 53 54#define IO_ICU1 0x20 55#define IO_ICU2 0xA0 56 57SET_DECLARE(lpc_dsdt_set, struct lpc_dsdt); 58SET_DECLARE(lpc_sysres_set, struct lpc_sysres); 59 60#define ELCR_PORT 0x4d0 61SYSRES_IO(ELCR_PORT, 2); 62 63#define IO_TIMER1_PORT 0x40 64 65#define NMISC_PORT 0x61 66SYSRES_IO(NMISC_PORT, 1); 67 68static struct pci_devinst *lpc_bridge; 69 70static const char *romfile; 71 72#define LPC_UART_NUM 2 73static struct lpc_uart_softc { 74 struct uart_softc *uart_softc; 75 const char *opts; 76 int iobase; 77 int irq; 78 int enabled; 79} lpc_uart_softc[LPC_UART_NUM]; 80 81static const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2" }; 82 83static bool pctestdev_present; 84 85/* 86 * LPC device configuration is in the following form: 87 * <lpc_device_name>[,<options>] 88 * For e.g. "com1,stdio" or "bootrom,/var/romfile" 89 */ 90int 91lpc_device_parse(const char *opts) 92{ 93 int unit, error; 94 char *str, *cpy, *lpcdev; 95 96 error = -1; 97 str = cpy = strdup(opts); 98 lpcdev = strsep(&str, ","); 99 if (lpcdev != NULL) { 100 if (strcasecmp(lpcdev, "bootrom") == 0) { 101 romfile = str; 102 error = 0; 103 goto done; 104 } 105 for (unit = 0; unit < LPC_UART_NUM; unit++) { 106 if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) { 107 lpc_uart_softc[unit].opts = str; 108 error = 0; 109 goto done; 110 } 111 } 112 if (strcasecmp(lpcdev, pctestdev_getname()) == 0) { 113 if (pctestdev_present) { 114 EPRINTLN("More than one %s device conf is " 115 "specified; only one is allowed.", 116 pctestdev_getname()); 117 } else if (pctestdev_parse(str) == 0) { 118 pctestdev_present = true; 119 error = 0; 120 free(cpy); 121 goto done; 122 } 123 } 124 } 125 126done: 127 if (error) 128 free(cpy); 129 130 return (error); 131} 132 133void 134lpc_print_supported_devices() 135{ 136 size_t i; 137 138 printf("bootrom\n"); 139 for (i = 0; i < LPC_UART_NUM; i++) 140 printf("%s\n", lpc_uart_names[i]); 141 printf("%s\n", pctestdev_getname()); 142} 143 144const char * 145lpc_bootrom(void) 146{ 147 148 return (romfile); 149} 150 151static void 152lpc_uart_intr_assert(void *arg) 153{ 154 struct lpc_uart_softc *sc = arg; 155 156 assert(sc->irq >= 0); 157 158 vm_isa_pulse_irq(lpc_bridge->pi_vmctx, sc->irq, sc->irq); 159} 160 161static void 162lpc_uart_intr_deassert(void *arg) 163{ 164 /* 165 * The COM devices on the LPC bus generate edge triggered interrupts, 166 * so nothing more to do here. 167 */ 168} 169 170static int 171lpc_uart_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 172 uint32_t *eax, void *arg) 173{ 174 int offset; 175 struct lpc_uart_softc *sc = arg; 176 177 offset = port - sc->iobase; 178 179 switch (bytes) { 180 case 1: 181 if (in) 182 *eax = uart_read(sc->uart_softc, offset); 183 else 184 uart_write(sc->uart_softc, offset, *eax); 185 break; 186 case 2: 187 if (in) { 188 *eax = uart_read(sc->uart_softc, offset); 189 *eax |= uart_read(sc->uart_softc, offset + 1) << 8; 190 } else { 191 uart_write(sc->uart_softc, offset, *eax); 192 uart_write(sc->uart_softc, offset + 1, *eax >> 8); 193 } 194 break; 195 default: 196 return (-1); 197 } 198 199 return (0); 200} 201 202static int 203lpc_init(struct vmctx *ctx) 204{ 205 struct lpc_uart_softc *sc; 206 struct inout_port iop; 207 const char *name; 208 int unit, error; 209 210 if (romfile != NULL) { 211 error = bootrom_init(ctx, romfile); 212 if (error) 213 return (error); 214 } 215 216 /* COM1 and COM2 */ 217 for (unit = 0; unit < LPC_UART_NUM; unit++) { 218 sc = &lpc_uart_softc[unit]; 219 name = lpc_uart_names[unit]; 220 221 if (uart_legacy_alloc(unit, &sc->iobase, &sc->irq) != 0) { 222 EPRINTLN("Unable to allocate resources for " 223 "LPC device %s", name); 224 return (-1); 225 } 226 pci_irq_reserve(sc->irq); 227 228 sc->uart_softc = uart_init(lpc_uart_intr_assert, 229 lpc_uart_intr_deassert, sc); 230 231 if (uart_set_backend(sc->uart_softc, sc->opts) != 0) { 232 EPRINTLN("Unable to initialize backend '%s' " 233 "for LPC device %s", sc->opts, name); 234 return (-1); 235 } 236 237 bzero(&iop, sizeof(struct inout_port)); 238 iop.name = name; 239 iop.port = sc->iobase; 240 iop.size = UART_IO_BAR_SIZE; 241 iop.flags = IOPORT_F_INOUT; 242 iop.handler = lpc_uart_io_handler; 243 iop.arg = sc; 244 245 error = register_inout(&iop); 246 assert(error == 0); 247 sc->enabled = 1; 248 } 249 250 /* pc-testdev */ 251 if (pctestdev_present) { 252 error = pctestdev_init(ctx); 253 if (error) 254 return (error); 255 } 256 257 return (0); 258} 259 260static void 261pci_lpc_write_dsdt(struct pci_devinst *pi) 262{ 263 struct lpc_dsdt **ldpp, *ldp; 264 265 dsdt_line(""); 266 dsdt_line("Device (ISA)"); 267 dsdt_line("{"); 268 dsdt_line(" Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func); 269 dsdt_line(" OperationRegion (LPCR, PCI_Config, 0x00, 0x100)"); 270 dsdt_line(" Field (LPCR, AnyAcc, NoLock, Preserve)"); 271 dsdt_line(" {"); 272 dsdt_line(" Offset (0x60),"); 273 dsdt_line(" PIRA, 8,"); 274 dsdt_line(" PIRB, 8,"); 275 dsdt_line(" PIRC, 8,"); 276 dsdt_line(" PIRD, 8,"); 277 dsdt_line(" Offset (0x68),"); 278 dsdt_line(" PIRE, 8,"); 279 dsdt_line(" PIRF, 8,"); 280 dsdt_line(" PIRG, 8,"); 281 dsdt_line(" PIRH, 8"); 282 dsdt_line(" }"); 283 dsdt_line(""); 284 285 dsdt_indent(1); 286 SET_FOREACH(ldpp, lpc_dsdt_set) { 287 ldp = *ldpp; 288 ldp->handler(); 289 } 290 291 dsdt_line(""); 292 dsdt_line("Device (PIC)"); 293 dsdt_line("{"); 294 dsdt_line(" Name (_HID, EisaId (\"PNP0000\"))"); 295 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 296 dsdt_line(" {"); 297 dsdt_indent(2); 298 dsdt_fixed_ioport(IO_ICU1, 2); 299 dsdt_fixed_ioport(IO_ICU2, 2); 300 dsdt_fixed_irq(2); 301 dsdt_unindent(2); 302 dsdt_line(" })"); 303 dsdt_line("}"); 304 305 dsdt_line(""); 306 dsdt_line("Device (TIMR)"); 307 dsdt_line("{"); 308 dsdt_line(" Name (_HID, EisaId (\"PNP0100\"))"); 309 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 310 dsdt_line(" {"); 311 dsdt_indent(2); 312 dsdt_fixed_ioport(IO_TIMER1_PORT, 4); 313 dsdt_fixed_irq(0); 314 dsdt_unindent(2); 315 dsdt_line(" })"); 316 dsdt_line("}"); 317 dsdt_unindent(1); 318 319 dsdt_line("}"); 320} 321 322static void 323pci_lpc_sysres_dsdt(void) 324{ 325 struct lpc_sysres **lspp, *lsp; 326 327 dsdt_line(""); 328 dsdt_line("Device (SIO)"); 329 dsdt_line("{"); 330 dsdt_line(" Name (_HID, EisaId (\"PNP0C02\"))"); 331 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 332 dsdt_line(" {"); 333 334 dsdt_indent(2); 335 SET_FOREACH(lspp, lpc_sysres_set) { 336 lsp = *lspp; 337 switch (lsp->type) { 338 case LPC_SYSRES_IO: 339 dsdt_fixed_ioport(lsp->base, lsp->length); 340 break; 341 case LPC_SYSRES_MEM: 342 dsdt_fixed_mem32(lsp->base, lsp->length); 343 break; 344 } 345 } 346 dsdt_unindent(2); 347 348 dsdt_line(" })"); 349 dsdt_line("}"); 350} 351LPC_DSDT(pci_lpc_sysres_dsdt); 352 353static void 354pci_lpc_uart_dsdt(void) 355{ 356 struct lpc_uart_softc *sc; 357 int unit; 358 359 for (unit = 0; unit < LPC_UART_NUM; unit++) { 360 sc = &lpc_uart_softc[unit]; 361 if (!sc->enabled) 362 continue; 363 dsdt_line(""); 364 dsdt_line("Device (%s)", lpc_uart_names[unit]); 365 dsdt_line("{"); 366 dsdt_line(" Name (_HID, EisaId (\"PNP0501\"))"); 367 dsdt_line(" Name (_UID, %d)", unit + 1); 368 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 369 dsdt_line(" {"); 370 dsdt_indent(2); 371 dsdt_fixed_ioport(sc->iobase, UART_IO_BAR_SIZE); 372 dsdt_fixed_irq(sc->irq); 373 dsdt_unindent(2); 374 dsdt_line(" })"); 375 dsdt_line("}"); 376 } 377} 378LPC_DSDT(pci_lpc_uart_dsdt); 379 380static int 381pci_lpc_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 382 int coff, int bytes, uint32_t val) 383{ 384 int pirq_pin; 385 386 if (bytes == 1) { 387 pirq_pin = 0; 388 if (coff >= 0x60 && coff <= 0x63) 389 pirq_pin = coff - 0x60 + 1; 390 if (coff >= 0x68 && coff <= 0x6b) 391 pirq_pin = coff - 0x68 + 5; 392 if (pirq_pin != 0) { 393 pirq_write(ctx, pirq_pin, val); 394 pci_set_cfgdata8(pi, coff, pirq_read(pirq_pin)); 395 return (0); 396 } 397 } 398 return (-1); 399} 400 401static void 402pci_lpc_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 403 int baridx, uint64_t offset, int size, uint64_t value) 404{ 405} 406 407static uint64_t 408pci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 409 int baridx, uint64_t offset, int size) 410{ 411 return (0); 412} 413 414#define LPC_DEV 0x7000 415#define LPC_VENDOR 0x8086 416 417static int 418pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 419{ 420 421 /* 422 * Do not allow more than one LPC bridge to be configured. 423 */ 424 if (lpc_bridge != NULL) { 425 EPRINTLN("Only one LPC bridge is allowed."); 426 return (-1); 427 } 428 429 /* 430 * Enforce that the LPC can only be configured on bus 0. This 431 * simplifies the ACPI DSDT because it can provide a decode for 432 * all legacy i/o ports behind bus 0. 433 */ 434 if (pi->pi_bus != 0) { 435 EPRINTLN("LPC bridge can be present only on bus 0."); 436 return (-1); 437 } 438 439 if (lpc_init(ctx) != 0) 440 return (-1); 441 442 /* initialize config space */ 443 pci_set_cfgdata16(pi, PCIR_DEVICE, LPC_DEV); 444 pci_set_cfgdata16(pi, PCIR_VENDOR, LPC_VENDOR); 445 pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); 446 pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA); 447 448 lpc_bridge = pi; 449 450 return (0); 451} 452 453char * 454lpc_pirq_name(int pin) 455{ 456 char *name; 457 458 if (lpc_bridge == NULL) 459 return (NULL); 460 asprintf(&name, "\\_SB.PC00.ISA.LNK%c,", 'A' + pin - 1); 461 return (name); 462} 463 464void 465lpc_pirq_routed(void) 466{ 467 int pin; 468 469 if (lpc_bridge == NULL) 470 return; 471 472 for (pin = 0; pin < 4; pin++) 473 pci_set_cfgdata8(lpc_bridge, 0x60 + pin, pirq_read(pin + 1)); 474 for (pin = 0; pin < 4; pin++) 475 pci_set_cfgdata8(lpc_bridge, 0x68 + pin, pirq_read(pin + 5)); 476} 477 478struct pci_devemu pci_de_lpc = { 479 .pe_emu = "lpc", 480 .pe_init = pci_lpc_init, 481 .pe_write_dsdt = pci_lpc_write_dsdt, 482 .pe_cfgwrite = pci_lpc_cfgwrite, 483 .pe_barwrite = pci_lpc_write, 484 .pe_barread = pci_lpc_read 485}; 486PCI_EMUL_SET(pci_de_lpc); 487