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