]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/pci_lpc.c
MFV: xz 5.4.3.
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / pci_lpc.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 <err.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include <vmmapi.h>
45
46 #include "acpi.h"
47 #include "debug.h"
48 #include "bootrom.h"
49 #include "config.h"
50 #include "inout.h"
51 #include "pci_emul.h"
52 #include "pci_irq.h"
53 #include "pci_lpc.h"
54 #include "pci_passthru.h"
55 #include "pctestdev.h"
56 #include "uart_emul.h"
57
58 #define IO_ICU1         0x20
59 #define IO_ICU2         0xA0
60
61 SET_DECLARE(lpc_dsdt_set, struct lpc_dsdt);
62 SET_DECLARE(lpc_sysres_set, struct lpc_sysres);
63
64 #define ELCR_PORT       0x4d0
65 SYSRES_IO(ELCR_PORT, 2);
66
67 #define IO_TIMER1_PORT  0x40
68
69 #define NMISC_PORT      0x61
70 SYSRES_IO(NMISC_PORT, 1);
71
72 static struct pci_devinst *lpc_bridge;
73
74 #define LPC_UART_NUM    4
75 static struct lpc_uart_softc {
76         struct uart_softc *uart_softc;
77         int     iobase;
78         int     irq;
79         int     enabled;
80 } lpc_uart_softc[LPC_UART_NUM];
81
82 static const char *lpc_uart_names[LPC_UART_NUM] = {
83         "com1", "com2", "com3", "com4"
84 };
85
86 static const char *lpc_uart_acpi_names[LPC_UART_NUM] = {
87         "COM1", "COM2", "COM3", "COM4"
88 };
89
90 /*
91  * LPC device configuration is in the following form:
92  * <lpc_device_name>[,<options>]
93  * For e.g. "com1,stdio" or "bootrom,/var/romfile"
94  */
95 int
96 lpc_device_parse(const char *opts)
97 {
98         int unit, error;
99         char *str, *cpy, *lpcdev, *node_name;
100         const char *romfile, *varfile;
101
102         error = -1;
103         str = cpy = strdup(opts);
104         lpcdev = strsep(&str, ",");
105         if (lpcdev != NULL) {
106                 if (strcasecmp(lpcdev, "bootrom") == 0) {
107                         romfile = strsep(&str, ",");
108                         if (romfile == NULL) {
109                                 errx(4, "invalid bootrom option \"%s\"", opts);
110                         }
111                         set_config_value("lpc.bootrom", romfile);
112
113                         varfile = strsep(&str, ",");
114                         if (varfile == NULL) {
115                                 error = 0;
116                                 goto done;
117                         }
118                         if (strchr(varfile, '=') == NULL) {
119                                 set_config_value("lpc.bootvars", varfile);
120                         } else {
121                                 /* varfile doesn't exist, it's another config
122                                  * option */
123                                 pci_parse_legacy_config(find_config_node("lpc"),
124                                     varfile);
125                         }
126
127                         pci_parse_legacy_config(find_config_node("lpc"), str);
128                         error = 0;
129                         goto done;
130                 }
131                 for (unit = 0; unit < LPC_UART_NUM; unit++) {
132                         if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) {
133                                 asprintf(&node_name, "lpc.%s.path",
134                                     lpc_uart_names[unit]);
135                                 set_config_value(node_name, str);
136                                 free(node_name);
137                                 error = 0;
138                                 goto done;
139                         }
140                 }
141                 if (strcasecmp(lpcdev, pctestdev_getname()) == 0) {
142                         asprintf(&node_name, "lpc.%s", pctestdev_getname());
143                         set_config_bool(node_name, true);
144                         free(node_name);
145                         error = 0;
146                         goto done;
147                 }
148         }
149
150 done:
151         free(cpy);
152
153         return (error);
154 }
155
156 void
157 lpc_print_supported_devices(void)
158 {
159         size_t i;
160
161         printf("bootrom\n");
162         for (i = 0; i < LPC_UART_NUM; i++)
163                 printf("%s\n", lpc_uart_names[i]);
164         printf("%s\n", pctestdev_getname());
165 }
166
167 const char *
168 lpc_bootrom(void)
169 {
170
171         return (get_config_value("lpc.bootrom"));
172 }
173
174 const char *
175 lpc_fwcfg(void)
176 {
177         return (get_config_value("lpc.fwcfg"));
178 }
179
180 static void
181 lpc_uart_intr_assert(void *arg)
182 {
183         struct lpc_uart_softc *sc = arg;
184
185         assert(sc->irq >= 0);
186
187         vm_isa_pulse_irq(lpc_bridge->pi_vmctx, sc->irq, sc->irq);
188 }
189
190 static void
191 lpc_uart_intr_deassert(void *arg __unused)
192 {
193         /*
194          * The COM devices on the LPC bus generate edge triggered interrupts,
195          * so nothing more to do here.
196          */
197 }
198
199 static int
200 lpc_uart_io_handler(struct vmctx *ctx __unused, int in,
201     int port, int bytes, uint32_t *eax, void *arg)
202 {
203         int offset;
204         struct lpc_uart_softc *sc = arg;
205
206         offset = port - sc->iobase;
207
208         switch (bytes) {
209         case 1:
210                 if (in)
211                         *eax = uart_read(sc->uart_softc, offset);
212                 else
213                         uart_write(sc->uart_softc, offset, *eax);
214                 break;
215         case 2:
216                 if (in) {
217                         *eax = uart_read(sc->uart_softc, offset);
218                         *eax |= uart_read(sc->uart_softc, offset + 1) << 8;
219                 } else {
220                         uart_write(sc->uart_softc, offset, *eax);
221                         uart_write(sc->uart_softc, offset + 1, *eax >> 8);
222                 }
223                 break;
224         default:
225                 return (-1);
226         }
227
228         return (0);
229 }
230
231 static int
232 lpc_init(struct vmctx *ctx)
233 {
234         struct lpc_uart_softc *sc;
235         struct inout_port iop;
236         const char *backend, *name;
237         char *node_name;
238         int unit, error;
239         const nvlist_t *nvl;
240
241         nvl = find_config_node("lpc");
242         if (nvl != NULL && nvlist_exists(nvl, "bootrom")) {
243                 error = bootrom_loadrom(ctx, nvl);
244                 if (error)
245                         return (error);
246         }
247
248         /* COM1 and COM2 */
249         for (unit = 0; unit < LPC_UART_NUM; unit++) {
250                 sc = &lpc_uart_softc[unit];
251                 name = lpc_uart_names[unit];
252
253                 if (uart_legacy_alloc(unit, &sc->iobase, &sc->irq) != 0) {
254                         EPRINTLN("Unable to allocate resources for "
255                             "LPC device %s", name);
256                         return (-1);
257                 }
258                 pci_irq_reserve(sc->irq);
259
260                 sc->uart_softc = uart_init(lpc_uart_intr_assert,
261                                     lpc_uart_intr_deassert, sc);
262
263                 asprintf(&node_name, "lpc.%s.path", name);
264                 backend = get_config_value(node_name);
265                 free(node_name);
266                 if (uart_set_backend(sc->uart_softc, backend) != 0) {
267                         EPRINTLN("Unable to initialize backend '%s' "
268                             "for LPC device %s", backend, name);
269                         return (-1);
270                 }
271
272                 bzero(&iop, sizeof(struct inout_port));
273                 iop.name = name;
274                 iop.port = sc->iobase;
275                 iop.size = UART_IO_BAR_SIZE;
276                 iop.flags = IOPORT_F_INOUT;
277                 iop.handler = lpc_uart_io_handler;
278                 iop.arg = sc;
279
280                 error = register_inout(&iop);
281                 assert(error == 0);
282                 sc->enabled = 1;
283         }
284
285         /* pc-testdev */
286         asprintf(&node_name, "lpc.%s", pctestdev_getname());
287         if (get_config_bool_default(node_name, false)) {
288                 error = pctestdev_init(ctx);
289                 if (error)
290                         return (error);
291         }
292         free(node_name);
293
294         return (0);
295 }
296
297 static void
298 pci_lpc_write_dsdt(struct pci_devinst *pi)
299 {
300         struct lpc_dsdt **ldpp, *ldp;
301
302         dsdt_line("");
303         dsdt_line("Device (ISA)");
304         dsdt_line("{");
305         dsdt_line("  Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func);
306         dsdt_line("  OperationRegion (LPCR, PCI_Config, 0x00, 0x100)");
307         dsdt_line("  Field (LPCR, AnyAcc, NoLock, Preserve)");
308         dsdt_line("  {");
309         dsdt_line("    Offset (0x60),");
310         dsdt_line("    PIRA,   8,");
311         dsdt_line("    PIRB,   8,");
312         dsdt_line("    PIRC,   8,");
313         dsdt_line("    PIRD,   8,");
314         dsdt_line("    Offset (0x68),");
315         dsdt_line("    PIRE,   8,");
316         dsdt_line("    PIRF,   8,");
317         dsdt_line("    PIRG,   8,");
318         dsdt_line("    PIRH,   8");
319         dsdt_line("  }");
320         dsdt_line("");
321
322         dsdt_indent(1);
323         SET_FOREACH(ldpp, lpc_dsdt_set) {
324                 ldp = *ldpp;
325                 ldp->handler();
326         }
327
328         dsdt_line("");
329         dsdt_line("Device (PIC)");
330         dsdt_line("{");
331         dsdt_line("  Name (_HID, EisaId (\"PNP0000\"))");
332         dsdt_line("  Name (_CRS, ResourceTemplate ()");
333         dsdt_line("  {");
334         dsdt_indent(2);
335         dsdt_fixed_ioport(IO_ICU1, 2);
336         dsdt_fixed_ioport(IO_ICU2, 2);
337         dsdt_fixed_irq(2);
338         dsdt_unindent(2);
339         dsdt_line("  })");
340         dsdt_line("}");
341
342         dsdt_line("");
343         dsdt_line("Device (TIMR)");
344         dsdt_line("{");
345         dsdt_line("  Name (_HID, EisaId (\"PNP0100\"))");
346         dsdt_line("  Name (_CRS, ResourceTemplate ()");
347         dsdt_line("  {");
348         dsdt_indent(2);
349         dsdt_fixed_ioport(IO_TIMER1_PORT, 4);
350         dsdt_fixed_irq(0);
351         dsdt_unindent(2);
352         dsdt_line("  })");
353         dsdt_line("}");
354         dsdt_unindent(1);
355
356         dsdt_line("}");
357 }
358
359 static void
360 pci_lpc_sysres_dsdt(void)
361 {
362         struct lpc_sysres **lspp, *lsp;
363
364         dsdt_line("");
365         dsdt_line("Device (SIO)");
366         dsdt_line("{");
367         dsdt_line("  Name (_HID, EisaId (\"PNP0C02\"))");
368         dsdt_line("  Name (_CRS, ResourceTemplate ()");
369         dsdt_line("  {");
370
371         dsdt_indent(2);
372         SET_FOREACH(lspp, lpc_sysres_set) {
373                 lsp = *lspp;
374                 switch (lsp->type) {
375                 case LPC_SYSRES_IO:
376                         dsdt_fixed_ioport(lsp->base, lsp->length);
377                         break;
378                 case LPC_SYSRES_MEM:
379                         dsdt_fixed_mem32(lsp->base, lsp->length);
380                         break;
381                 }
382         }
383         dsdt_unindent(2);
384
385         dsdt_line("  })");
386         dsdt_line("}");
387 }
388 LPC_DSDT(pci_lpc_sysres_dsdt);
389
390 static void
391 pci_lpc_uart_dsdt(void)
392 {
393         struct lpc_uart_softc *sc;
394         int unit;
395
396         for (unit = 0; unit < LPC_UART_NUM; unit++) {
397                 sc = &lpc_uart_softc[unit];
398                 if (!sc->enabled)
399                         continue;
400                 dsdt_line("");
401                 dsdt_line("Device (%s)", lpc_uart_acpi_names[unit]);
402                 dsdt_line("{");
403                 dsdt_line("  Name (_HID, EisaId (\"PNP0501\"))");
404                 dsdt_line("  Name (_UID, %d)", unit + 1);
405                 dsdt_line("  Name (_CRS, ResourceTemplate ()");
406                 dsdt_line("  {");
407                 dsdt_indent(2);
408                 dsdt_fixed_ioport(sc->iobase, UART_IO_BAR_SIZE);
409                 dsdt_fixed_irq(sc->irq);
410                 dsdt_unindent(2);
411                 dsdt_line("  })");
412                 dsdt_line("}");
413         }
414 }
415 LPC_DSDT(pci_lpc_uart_dsdt);
416
417 static int
418 pci_lpc_cfgwrite(struct pci_devinst *pi, int coff, int bytes, uint32_t val)
419 {
420         int pirq_pin;
421
422         if (bytes == 1) {
423                 pirq_pin = 0;
424                 if (coff >= 0x60 && coff <= 0x63)
425                         pirq_pin = coff - 0x60 + 1;
426                 if (coff >= 0x68 && coff <= 0x6b)
427                         pirq_pin = coff - 0x68 + 5;
428                 if (pirq_pin != 0) {
429                         pirq_write(pi->pi_vmctx, pirq_pin, val);
430                         pci_set_cfgdata8(pi, coff, pirq_read(pirq_pin));
431                         return (0);
432                 }
433         }
434         return (-1);
435 }
436
437 static void
438 pci_lpc_write(struct pci_devinst *pi __unused, int baridx __unused,
439     uint64_t offset __unused, int size __unused, uint64_t value __unused)
440 {
441 }
442
443 static uint64_t
444 pci_lpc_read(struct pci_devinst *pi __unused, int baridx __unused,
445     uint64_t offset __unused, int size __unused)
446 {
447         return (0);
448 }
449
450 #define LPC_DEV         0x7000
451 #define LPC_VENDOR      0x8086
452 #define LPC_REVID       0x00
453 #define LPC_SUBVEND_0   0x0000
454 #define LPC_SUBDEV_0    0x0000
455
456 static int
457 pci_lpc_get_sel(struct pcisel *const sel)
458 {
459         assert(sel != NULL);
460
461         memset(sel, 0, sizeof(*sel));
462
463         for (uint8_t slot = 0; slot <= PCI_SLOTMAX; ++slot) {
464                 uint8_t max_func = 0;
465
466                 sel->pc_dev = slot;
467                 sel->pc_func = 0;
468
469                 if (read_config(sel, PCIR_HDRTYPE, 1) & PCIM_MFDEV)
470                         max_func = PCI_FUNCMAX;
471
472                 for (uint8_t func = 0; func <= max_func; ++func) {
473                         sel->pc_func = func;
474
475                         if ((read_config(sel, PCIR_CLASS, 1) == PCIC_BRIDGE) &&
476                             (read_config(sel, PCIR_SUBCLASS, 1) ==
477                                 PCIS_BRIDGE_ISA)) {
478                                 return (0);
479                         }
480                 }
481         }
482
483         warnx("%s: Unable to find host selector of LPC bridge.", __func__);
484
485         return (-1);
486 }
487
488 static int
489 pci_lpc_init(struct pci_devinst *pi, nvlist_t *nvl)
490 {
491         struct pcisel sel = { 0 };
492         struct pcisel *selp = NULL;
493         uint16_t device, subdevice, subvendor, vendor;
494         uint8_t revid;
495
496         /*
497          * Do not allow more than one LPC bridge to be configured.
498          */
499         if (lpc_bridge != NULL) {
500                 EPRINTLN("Only one LPC bridge is allowed.");
501                 return (-1);
502         }
503
504         /*
505          * Enforce that the LPC can only be configured on bus 0. This
506          * simplifies the ACPI DSDT because it can provide a decode for
507          * all legacy i/o ports behind bus 0.
508          */
509         if (pi->pi_bus != 0) {
510                 EPRINTLN("LPC bridge can be present only on bus 0.");
511                 return (-1);
512         }
513
514         if (lpc_init(pi->pi_vmctx) != 0)
515                 return (-1);
516
517         if (pci_lpc_get_sel(&sel) == 0)
518                 selp = &sel;
519
520         vendor = pci_config_read_reg(selp, nvl, PCIR_VENDOR, 2, LPC_VENDOR);
521         device = pci_config_read_reg(selp, nvl, PCIR_DEVICE, 2, LPC_DEV);
522         revid = pci_config_read_reg(selp, nvl, PCIR_REVID, 1, LPC_REVID);
523         subvendor = pci_config_read_reg(selp, nvl, PCIR_SUBVEND_0, 2,
524             LPC_SUBVEND_0);
525         subdevice = pci_config_read_reg(selp, nvl, PCIR_SUBDEV_0, 2,
526             LPC_SUBDEV_0);
527
528         /* initialize config space */
529         pci_set_cfgdata16(pi, PCIR_VENDOR, vendor);
530         pci_set_cfgdata16(pi, PCIR_DEVICE, device);
531         pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE);
532         pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA);
533         pci_set_cfgdata8(pi, PCIR_REVID, revid);
534         pci_set_cfgdata16(pi, PCIR_SUBVEND_0, subvendor);
535         pci_set_cfgdata16(pi, PCIR_SUBDEV_0, subdevice);
536
537         lpc_bridge = pi;
538
539         return (0);
540 }
541
542 char *
543 lpc_pirq_name(int pin)
544 {
545         char *name;
546
547         if (lpc_bridge == NULL)
548                 return (NULL);
549         asprintf(&name, "\\_SB.PC00.ISA.LNK%c,", 'A' + pin - 1);
550         return (name);
551 }
552
553 void
554 lpc_pirq_routed(void)
555 {
556         int pin;
557
558         if (lpc_bridge == NULL)
559                 return;
560
561         for (pin = 0; pin < 4; pin++)
562                 pci_set_cfgdata8(lpc_bridge, 0x60 + pin, pirq_read(pin + 1));
563         for (pin = 0; pin < 4; pin++)
564                 pci_set_cfgdata8(lpc_bridge, 0x68 + pin, pirq_read(pin + 5));
565 }
566
567 #ifdef BHYVE_SNAPSHOT
568 static int
569 pci_lpc_snapshot(struct vm_snapshot_meta *meta)
570 {
571         int unit, ret;
572         struct uart_softc *sc;
573
574         for (unit = 0; unit < LPC_UART_NUM; unit++) {
575                 sc = lpc_uart_softc[unit].uart_softc;
576
577                 ret = uart_snapshot(sc, meta);
578                 if (ret != 0)
579                         goto done;
580         }
581
582 done:
583         return (ret);
584 }
585 #endif
586
587 static const struct pci_devemu pci_de_lpc = {
588         .pe_emu =       "lpc",
589         .pe_init =      pci_lpc_init,
590         .pe_write_dsdt = pci_lpc_write_dsdt,
591         .pe_cfgwrite =  pci_lpc_cfgwrite,
592         .pe_barwrite =  pci_lpc_write,
593         .pe_barread =   pci_lpc_read,
594 #ifdef BHYVE_SNAPSHOT
595         .pe_snapshot =  pci_lpc_snapshot,
596 #endif
597 };
598 PCI_EMUL_SET(pci_de_lpc);