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