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