diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h --- a/sys/dev/pci/pcireg.h +++ b/sys/dev/pci/pcireg.h @@ -1098,3 +1098,8 @@ #define PCIM_OSC_CTL_PCIE_PME 0x04 /* PCIe Native Power Mgt Events */ #define PCIM_OSC_CTL_PCIE_AER 0x08 /* PCIe Advanced Error Reporting */ #define PCIM_OSC_CTL_PCIE_CAP_STRUCT 0x10 /* Various Capability Structures */ + +/* + * PCI Vendors + */ +#define PCI_VENDOR_INTEL 0x8086 diff --git a/usr.sbin/bhyve/bhyve_config.5 b/usr.sbin/bhyve/bhyve_config.5 --- a/usr.sbin/bhyve/bhyve_config.5 +++ b/usr.sbin/bhyve/bhyve_config.5 @@ -133,6 +133,21 @@ .It Va gdb.wait Ta bool Ta false Ta If the debug server is enabled, wait for a debugger to connect before starting the guest. +.It Va lpc.pcir.vendor Ta integer Ta 0x8086 Ta +Vendor id of the LPC bridge as hex number. On Intel systems +the LPC bridge uses the physical vendor id as default value. +.It Va lpc.pcir.device Ta integer Ta 0x7000 Ta +Device id of the LPC bridge as hex number. On Intel systems +the LPC bridge uses the physical device id as default value. +.It Va lpc.pcir.revid Ta integer Ta 0 Ta +Revision id of the LPC bridge as hex number. On Intel systems +the LPC bridge uses the physical revision id as default value. +.It Va lpc.pcir.subvendor Ta integer Ta 0 Ta +Subvendor id of the LPC bridge as hex number. On Intel systems +the LPC bridge uses the physical subvendor id as default value. +.It Va lpc.pcir.subdevice Ta integer Ta 0 Ta +Subdevice id of the LPC bridge as hex number. On Intel systems +the LPC bridge uses the physical subdevice id as default value. .It Va rtc.use_localtime Ta bool Ta true Ta The real time clock uses the local time of the host. If this is set to false, the real time clock uses UTC. diff --git a/usr.sbin/bhyve/config.h b/usr.sbin/bhyve/config.h --- a/usr.sbin/bhyve/config.h +++ b/usr.sbin/bhyve/config.h @@ -99,12 +99,23 @@ void set_config_value_node(nvlist_t *parent, const char *name, const char *value); +/* + * Similar to set_config_value_node but only sets value if it's unset yet. + */ +void set_config_value_node_if_unset(nvlist_t *const parent, + const char *const name, const char *const value); + /* * Similar to set_config_value_node but expects a full path to the * leaf node. */ void set_config_value(const char *path, const char *value); +/* + * Similar to set_config_value but only sets the value if it's unset yet. + */ +void set_config_value_if_unset(const char *const path, const char *const value); + /* Convenience wrappers for boolean variables. */ bool get_config_bool(const char *path); bool get_config_bool_node(const nvlist_t *parent, const char *name); diff --git a/usr.sbin/bhyve/config.c b/usr.sbin/bhyve/config.c --- a/usr.sbin/bhyve/config.c +++ b/usr.sbin/bhyve/config.c @@ -135,6 +135,17 @@ nvlist_add_string(parent, name, value); } +void +set_config_value_node_if_unset(nvlist_t *const parent, const char *const name, + const char *const value) +{ + if (get_config_value_node(parent, name) != NULL) { + return; + } + + set_config_value_node(parent, name, value); +} + void set_config_value(const char *path, const char *value) { @@ -167,6 +178,16 @@ set_config_value_node(nvl, name, value); } +void +set_config_value_if_unset(const char *const path, const char *const value) +{ + if (get_config_value(path) != NULL) { + return; + } + + set_config_value(path, value); +} + static const char * get_raw_config_value(const char *path) { diff --git a/usr.sbin/bhyve/pci_lpc.c b/usr.sbin/bhyve/pci_lpc.c --- a/usr.sbin/bhyve/pci_lpc.c +++ b/usr.sbin/bhyve/pci_lpc.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include @@ -50,6 +51,7 @@ #include "pci_emul.h" #include "pci_irq.h" #include "pci_lpc.h" +#include "pci_passthru.h" #include "pctestdev.h" #include "uart_emul.h" @@ -446,11 +448,57 @@ if (lpc_init(ctx) != 0) return (-1); + char value[16]; + + /* on Intel systems lpc is always connected to 0:1f.0 */ + const struct pcisel sel = { .pc_dev = 0x1f }; + if (read_config(&sel, PCIR_VENDOR, 2) == PCI_VENDOR_INTEL) { + /* + * The VID, DID, REVID, SUBVID and SUBDID of lpc need to be + * aligned with the physical ones. Without these physical + * values, GVT-d GOP driver couldn't work. + */ + snprintf(value, sizeof(value), "0x%04x", + read_config(&sel, PCIR_VENDOR, 2)); + set_config_value_if_unset("lpc.pcir.vendor", value); + snprintf(value, sizeof(value), "0x%04x", + read_config(&sel, PCIR_DEVICE, 2)); + set_config_value_if_unset("lpc.pcir.device", value); + snprintf(value, sizeof(value), "0x%02x", + read_config(&sel, PCIR_REVID, 1)); + set_config_value_if_unset("lpc.pcir.revid", value); + snprintf(value, sizeof(value), "0x%04x", + read_config(&sel, PCIR_SUBVEND_0, 2)); + set_config_value_if_unset("lpc.pcir.subvendor", value); + snprintf(value, sizeof(value), "0x%04x", + read_config(&sel, PCIR_SUBDEV_0, 2)); + set_config_value_if_unset("lpc.pcir.subdevice", value); + } + + snprintf(value, sizeof(value), "0x%04x", LPC_VENDOR); + set_config_value_if_unset("lpc.pcir.vendor", value); + snprintf(value, sizeof(value), "0x%04x", LPC_DEV); + set_config_value_if_unset("lpc.pcir.device", value); + snprintf(value, sizeof(value), "0x%02x", 0); + set_config_value_if_unset("lpc.pcir.revid", 0); + snprintf(value, sizeof(value), "0x%04x", 0); + set_config_value_if_unset("lpc.pcir.subvendor", 0); + snprintf(value, sizeof(value), "0x%04x", 0); + set_config_value_if_unset("lpc.pcir.subdevice", 0); + /* initialize config space */ - pci_set_cfgdata16(pi, PCIR_DEVICE, LPC_DEV); - pci_set_cfgdata16(pi, PCIR_VENDOR, LPC_VENDOR); + pci_set_cfgdata16(pi, PCIR_VENDOR, + strtol(get_config_value("lpc.pcir.vendor"), NULL, 16)); + pci_set_cfgdata16(pi, PCIR_DEVICE, + strtol(get_config_value("lpc.pcir.device"), NULL, 16)); pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA); + pci_set_cfgdata8(pi, PCIR_REVID, + strtol(get_config_value("lpc.pcir.revid"), NULL, 16)); + pci_set_cfgdata16(pi, PCIR_SUBVEND_0, + strtol(get_config_value("lpc.pcir.subvendor"), NULL, 16)); + pci_set_cfgdata16(pi, PCIR_SUBDEV_0, + strtol(get_config_value("lpc.pcir.subdevice"), NULL, 16)); lpc_bridge = pi; diff --git a/usr.sbin/bhyve/pci_passthru.h b/usr.sbin/bhyve/pci_passthru.h new file mode 100644 --- /dev/null +++ b/usr.sbin/bhyve/pci_passthru.h @@ -0,0 +1,31 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#pragma once + +#include + +#include + +#include "pci_emul.h" + +struct passthru_softc { + struct pci_devinst *psc_pi; + struct pcibar psc_bar[PCI_BARMAX + 1]; + struct { + int capoff; + int msgctrl; + int emulated; + } psc_msi; + struct { + int capoff; + } psc_msix; + struct pcisel psc_sel; +}; + +uint32_t read_config(const struct pcisel *sel, long reg, int width); +void write_config(const struct pcisel *sel, long reg, int width, uint32_t data); diff --git a/usr.sbin/bhyve/pci_passthru.c b/usr.sbin/bhyve/pci_passthru.c --- a/usr.sbin/bhyve/pci_passthru.c +++ b/usr.sbin/bhyve/pci_passthru.c @@ -61,12 +61,11 @@ #include #include -#include #include "config.h" #include "debug.h" -#include "pci_emul.h" #include "mem.h" +#include "pci_passthru.h" #ifndef _PATH_DEVPCI #define _PATH_DEVPCI "/dev/pci" @@ -79,20 +78,6 @@ static int pcifd = -1; -struct passthru_softc { - struct pci_devinst *psc_pi; - struct pcibar psc_bar[PCI_BARMAX + 1]; - struct { - int capoff; - int msgctrl; - int emulated; - } psc_msi; - struct { - int capoff; - } psc_msix; - struct pcisel psc_sel; -}; - static int msi_caplen(int msgctrl) { @@ -115,9 +100,36 @@ return (len); } -static uint32_t +static int +pcifd_init() { + pcifd = open(_PATH_DEVPCI, O_RDWR, 0); + if (pcifd < 0) { + warn("failed to open %s", _PATH_DEVPCI); + return (1); + } + +#ifndef WITHOUT_CAPSICUM + cap_rights_t pcifd_rights; + cap_rights_init(&pcifd_rights, CAP_IOCTL, CAP_READ, CAP_WRITE); + if (caph_rights_limit(pcifd, &pcifd_rights) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); + + const cap_ioctl_t pcifd_ioctls[] = { PCIOCREAD, PCIOCWRITE, PCIOCGETBAR, + PCIOCBARIO, PCIOCBARMMAP }; + if (caph_ioctls_limit(pcifd, pcifd_ioctls, nitems(pcifd_ioctls)) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); +#endif + + return (0); +} + +uint32_t read_config(const struct pcisel *sel, long reg, int width) { + if (pcifd < 0 && pcifd_init()) { + return (0); + } + struct pci_io pi; bzero(&pi, sizeof(pi)); @@ -131,9 +143,13 @@ return (pi.pi_data); } -static void +void write_config(const struct pcisel *sel, long reg, int width, uint32_t data) { + if (pcifd < 0 && pcifd_init()) { + return; + } + struct pci_io pi; bzero(&pi, sizeof(pi)); @@ -630,40 +646,20 @@ int bus, slot, func, error, memflags; struct passthru_softc *sc; const char *value; -#ifndef WITHOUT_CAPSICUM - cap_rights_t rights; - cap_ioctl_t pci_ioctls[] = - { PCIOCREAD, PCIOCWRITE, PCIOCGETBAR, PCIOCBARIO, PCIOCBARMMAP }; -#endif sc = NULL; error = 1; -#ifndef WITHOUT_CAPSICUM - cap_rights_init(&rights, CAP_IOCTL, CAP_READ, CAP_WRITE); -#endif - memflags = vm_get_memflags(ctx); if (!(memflags & VM_MEM_F_WIRED)) { warnx("passthru requires guest memory to be wired"); return (error); } - if (pcifd < 0) { - pcifd = open(_PATH_DEVPCI, O_RDWR, 0); - if (pcifd < 0) { - warn("failed to open %s", _PATH_DEVPCI); - return (error); - } + if (pcifd < 0 && pcifd_init()) { + return (error); } -#ifndef WITHOUT_CAPSICUM - if (caph_rights_limit(pcifd, &rights) == -1) - errx(EX_OSERR, "Unable to apply rights for sandbox"); - if (caph_ioctls_limit(pcifd, pci_ioctls, nitems(pci_ioctls)) == -1) - errx(EX_OSERR, "Unable to apply rights for sandbox"); -#endif - #define GET_INT_CONFIG(var, name) do { \ value = get_config_value_node(nvl, name); \ if (value == NULL) { \