pci_lpc.c revision 268887
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 268887 2014-07-19 20:13:01Z jhb $ 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/pci_lpc.c 268887 2014-07-19 20:13:01Z jhb $"); 32 33#include <sys/types.h> 34#include <machine/vmm.h> 35#include <machine/vmm_dev.h> 36 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40 41#include <vmmapi.h> 42 43#include "acpi.h" 44#include "inout.h" 45#include "pci_emul.h" 46#include "pci_lpc.h" 47#include "uart_emul.h" 48 49SET_DECLARE(lpc_dsdt_set, struct lpc_dsdt); 50SET_DECLARE(lpc_sysres_set, struct lpc_sysres); 51 52static struct pci_devinst *lpc_bridge; 53 54#define LPC_UART_NUM 2 55static struct lpc_uart_softc { 56 struct uart_softc *uart_softc; 57 const char *opts; 58 int iobase; 59 int irq; 60 int enabled; 61} lpc_uart_softc[LPC_UART_NUM]; 62 63static const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2" }; 64 65/* 66 * LPC device configuration is in the following form: 67 * <lpc_device_name>[,<options>] 68 * For e.g. "com1,stdio" 69 */ 70int 71lpc_device_parse(const char *opts) 72{ 73 int unit, error; 74 char *str, *cpy, *lpcdev; 75 76 error = -1; 77 str = cpy = strdup(opts); 78 lpcdev = strsep(&str, ","); 79 if (lpcdev != NULL) { 80 for (unit = 0; unit < LPC_UART_NUM; unit++) { 81 if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) { 82 lpc_uart_softc[unit].opts = str; 83 error = 0; 84 goto done; 85 } 86 } 87 } 88 89done: 90 if (error) 91 free(cpy); 92 93 return (error); 94} 95 96static void 97lpc_uart_intr_assert(void *arg) 98{ 99 struct lpc_uart_softc *sc = arg; 100 101 assert(sc->irq >= 0); 102 103 vm_ioapic_pulse_irq(lpc_bridge->pi_vmctx, sc->irq); 104} 105 106static void 107lpc_uart_intr_deassert(void *arg) 108{ 109 /* 110 * The COM devices on the LPC bus generate edge triggered interrupts, 111 * so nothing more to do here. 112 */ 113} 114 115static int 116lpc_uart_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 117 uint32_t *eax, void *arg) 118{ 119 int offset; 120 struct lpc_uart_softc *sc = arg; 121 122 if (bytes != 1) 123 return (-1); 124 125 offset = port - sc->iobase; 126 127 if (in) 128 *eax = uart_read(sc->uart_softc, offset); 129 else 130 uart_write(sc->uart_softc, offset, *eax); 131 132 return (0); 133} 134 135static int 136lpc_init(void) 137{ 138 struct lpc_uart_softc *sc; 139 struct inout_port iop; 140 const char *name; 141 int unit, error; 142 143 /* COM1 and COM2 */ 144 for (unit = 0; unit < LPC_UART_NUM; unit++) { 145 sc = &lpc_uart_softc[unit]; 146 name = lpc_uart_names[unit]; 147 148 if (uart_legacy_alloc(unit, &sc->iobase, &sc->irq) != 0) { 149 fprintf(stderr, "Unable to allocate resources for " 150 "LPC device %s\n", name); 151 return (-1); 152 } 153 154 sc->uart_softc = uart_init(lpc_uart_intr_assert, 155 lpc_uart_intr_deassert, sc); 156 157 if (uart_set_backend(sc->uart_softc, sc->opts) != 0) { 158 fprintf(stderr, "Unable to initialize backend '%s' " 159 "for LPC device %s\n", sc->opts, name); 160 return (-1); 161 } 162 163 bzero(&iop, sizeof(struct inout_port)); 164 iop.name = name; 165 iop.port = sc->iobase; 166 iop.size = UART_IO_BAR_SIZE; 167 iop.flags = IOPORT_F_INOUT; 168 iop.handler = lpc_uart_io_handler; 169 iop.arg = sc; 170 171 error = register_inout(&iop); 172 assert(error == 0); 173 sc->enabled = 1; 174 } 175 176 return (0); 177} 178 179static void 180pci_lpc_write_dsdt(struct pci_devinst *pi) 181{ 182 struct lpc_dsdt **ldpp, *ldp; 183 184 dsdt_line(""); 185 dsdt_line("Device (ISA)"); 186 dsdt_line("{"); 187 dsdt_line(" Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func); 188 dsdt_line(" OperationRegion (P40C, PCI_Config, 0x60, 0x04)"); 189 190 dsdt_indent(1); 191 SET_FOREACH(ldpp, lpc_dsdt_set) { 192 ldp = *ldpp; 193 ldp->handler(); 194 } 195 dsdt_unindent(1); 196 197 dsdt_line("}"); 198} 199 200static void 201pci_lpc_sysres_dsdt(void) 202{ 203 struct lpc_sysres **lspp, *lsp; 204 205 dsdt_line(""); 206 dsdt_line("Device (SIO)"); 207 dsdt_line("{"); 208 dsdt_line(" Name (_HID, EisaId (\"PNP0C02\"))"); 209 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 210 dsdt_line(" {"); 211 212 dsdt_indent(2); 213 SET_FOREACH(lspp, lpc_sysres_set) { 214 lsp = *lspp; 215 switch (lsp->type) { 216 case LPC_SYSRES_IO: 217 dsdt_fixed_ioport(lsp->base, lsp->length); 218 break; 219 case LPC_SYSRES_MEM: 220 dsdt_fixed_mem32(lsp->base, lsp->length); 221 break; 222 } 223 } 224 dsdt_unindent(2); 225 226 dsdt_line(" })"); 227 dsdt_line("}"); 228} 229LPC_DSDT(pci_lpc_sysres_dsdt); 230 231static void 232pci_lpc_uart_dsdt(void) 233{ 234 struct lpc_uart_softc *sc; 235 int unit; 236 237 for (unit = 0; unit < LPC_UART_NUM; unit++) { 238 sc = &lpc_uart_softc[unit]; 239 if (!sc->enabled) 240 continue; 241 dsdt_line(""); 242 dsdt_line("Device (%s)", lpc_uart_names[unit]); 243 dsdt_line("{"); 244 dsdt_line(" Name (_HID, EisaId (\"PNP0501\"))"); 245 dsdt_line(" Name (_UID, %d)", unit + 1); 246 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 247 dsdt_line(" {"); 248 dsdt_indent(2); 249 dsdt_fixed_ioport(sc->iobase, UART_IO_BAR_SIZE); 250 dsdt_fixed_irq(sc->irq); 251 dsdt_unindent(2); 252 dsdt_line(" })"); 253 dsdt_line("}"); 254 } 255} 256LPC_DSDT(pci_lpc_uart_dsdt); 257 258static void 259pci_lpc_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 260 int baridx, uint64_t offset, int size, uint64_t value) 261{ 262} 263 264uint64_t 265pci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 266 int baridx, uint64_t offset, int size) 267{ 268 return (0); 269} 270 271#define LPC_DEV 0x7000 272#define LPC_VENDOR 0x8086 273 274static int 275pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 276{ 277 /* 278 * Do not allow more than one LPC bridge to be configured. 279 */ 280 if (lpc_bridge != NULL) { 281 fprintf(stderr, "Only one LPC bridge is allowed.\n"); 282 return (-1); 283 } 284 285 /* 286 * Enforce that the LPC can only be configured on bus 0. This 287 * simplifies the ACPI DSDT because it can provide a decode for 288 * all legacy i/o ports behind bus 0. 289 */ 290 if (pi->pi_bus != 0) { 291 fprintf(stderr, "LPC bridge can be present only on bus 0.\n"); 292 return (-1); 293 } 294 295 if (lpc_init() != 0) 296 return (-1); 297 298 /* initialize config space */ 299 pci_set_cfgdata16(pi, PCIR_DEVICE, LPC_DEV); 300 pci_set_cfgdata16(pi, PCIR_VENDOR, LPC_VENDOR); 301 pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); 302 pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA); 303 304 lpc_bridge = pi; 305 306 return (0); 307} 308 309struct pci_devemu pci_de_lpc = { 310 .pe_emu = "lpc", 311 .pe_init = pci_lpc_init, 312 .pe_write_dsdt = pci_lpc_write_dsdt, 313 .pe_barwrite = pci_lpc_write, 314 .pe_barread = pci_lpc_read 315}; 316PCI_EMUL_SET(pci_de_lpc); 317