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 @@ -139,6 +139,26 @@ This value only works when loaded with UEFI mode for VNC, and used a VNC client that don't support QEMU Extended Key Event Message (e.g. TightVNC). +.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/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,61 @@ 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) && + (read_config(&sel, PCIR_CLASS, 1) == PCIC_BRIDGE) && + (read_config(&sel, PCIR_SUBCLASS, 1) == PCIS_BRIDGE_ISA)) { + /* + * The VID, DID, REVID, SUBVID and SUBDID of lpc need to be + * aligned with the physical ones. Without these physical + * values, GPU passthrough of Intel integrated graphics devices + * won't work properly. The Intel GOP driver checks these values + * to proof that it runs on the correct platform. + */ + 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,15 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#pragma once + +#include + +#include "pci_emul.h" + +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" @@ -115,9 +114,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 +157,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)); @@ -638,40 +668,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) { \