The branch main has been updated by markj:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=1db1e4bdc64f29661592a47f2feb690d7cf37957

commit 1db1e4bdc64f29661592a47f2feb690d7cf37957
Author:     Mark Johnston <[email protected]>
AuthorDate: 2026-01-26 21:05:06 +0000
Commit:     Mark Johnston <[email protected]>
CommitDate: 2026-01-26 22:03:51 +0000

    bhyve: Use PCIOCGETCONF to find the host LPC bridge
    
    pci_host_read_config() requires write access to /dev/pci so cannot be
    used with unprivileged bhyve.  The lpc init code uses it to find the
    host system's LPC bridge device and so was generating warnings with
    bhyve running as a non-root user.
    
    Refactor the implementation to use PCIOCGETCONF instead, which doesn't
    require any special privileges.  This isn't formally necessary, as we
    only care about copying the host system's identifiers in order to
    support Intel GPU passthrough (see commit f4ceaff56ddaa), but it's
    straightforward and lets an unprivileged user run bhyve without seeing
    warnings about failing to open /dev/pci with write permissions.
    
    Reviewed by:    corvink, rew
    MFC after:      3 weeks
    Sponsored by:   The FreeBSD Foundation
    Sponsored by:   Klara, Inc.
    Differential Revision:  https://reviews.freebsd.org/D54851
---
 usr.sbin/bhyve/amd64/pci_lpc.c | 79 +++++++++++++++++++++++++-----------------
 usr.sbin/bhyve/pci_emul.c      | 22 ++++++------
 usr.sbin/bhyve/pci_emul.h      |  2 +-
 3 files changed, 60 insertions(+), 43 deletions(-)

diff --git a/usr.sbin/bhyve/amd64/pci_lpc.c b/usr.sbin/bhyve/amd64/pci_lpc.c
index ed41a800a2ea..52e9faec22af 100644
--- a/usr.sbin/bhyve/amd64/pci_lpc.c
+++ b/usr.sbin/bhyve/amd64/pci_lpc.c
@@ -28,10 +28,12 @@
  */
 
 #include <sys/types.h>
+
 #include <machine/vmm.h>
 #include <machine/vmm_snapshot.h>
 
 #include <err.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -46,7 +48,6 @@
 #include "pci_emul.h"
 #include "pci_irq.h"
 #include "pci_lpc.h"
-#include "pci_passthru.h"
 #include "pctestdev.h"
 #include "tpm_device.h"
 #include "uart_emul.h"
@@ -460,34 +461,48 @@ pci_lpc_read(struct pci_devinst *pi __unused, int baridx 
__unused,
 #define LPC_SUBDEV_0   0x0000
 
 static int
-pci_lpc_get_sel(struct pcisel *const sel)
+pci_lpc_get_conf(struct pci_conf *conf)
 {
-       assert(sel != NULL);
-
-       memset(sel, 0, sizeof(*sel));
-
-       for (uint8_t slot = 0; slot <= PCI_SLOTMAX; ++slot) {
-               uint8_t max_func = 0;
-
-               sel->pc_dev = slot;
-               sel->pc_func = 0;
+       struct pci_conf_io pcio;
+       struct pci_match_conf pmc;
+       int pcifd;
 
-               if (pci_host_read_config(sel, PCIR_HDRTYPE, 1) & PCIM_MFDEV)
-                       max_func = PCI_FUNCMAX;
+       pcifd = open("/dev/pci", O_RDONLY);
+       if (pcifd < 0) {
+               warn("%s: Unable to open /dev/pci", __func__);
+               return (-1);
+       }
 
-               for (uint8_t func = 0; func <= max_func; ++func) {
-                       sel->pc_func = func;
+restart:
+       memset(&pcio, 0, sizeof(pcio));
+       memset(&pmc, 0, sizeof(pmc));
+       pmc.pc_class = PCIC_BRIDGE;
+       pmc.flags = PCI_GETCONF_MATCH_CLASS;
+       do {
+               pcio.pat_buf_len = sizeof(pmc);
+               pcio.num_patterns = 1;
+               pcio.patterns = &pmc;
+               pcio.match_buf_len = sizeof(*conf);
+               pcio.matches = conf;
+               if (ioctl(pcifd, PCIOCGETCONF, &pcio) == -1) {
+                       warn("%s: ioctl(PCIOCGETCONF) failed", __func__);
+                       break;
+               }
+               if (pcio.num_matches == 0)
+                       break;
+               if (pcio.status == PCI_GETCONF_LIST_CHANGED)
+                       goto restart;
 
-                       if (pci_host_read_config(sel, PCIR_CLASS, 1) ==
-                           PCIC_BRIDGE &&
-                           pci_host_read_config(sel, PCIR_SUBCLASS, 1) ==
-                           PCIS_BRIDGE_ISA) {
-                               return (0);
-                       }
+               if (conf->pc_class == PCIC_BRIDGE &&
+                   conf->pc_subclass == PCIS_BRIDGE_ISA) {
+                       close(pcifd);
+                       return (0);
                }
-       }
+       } while (pcio.status == PCI_GETCONF_MORE_DEVS);
+
+       close(pcifd);
 
-       warnx("%s: Unable to find host selector of LPC bridge.", __func__);
+       warnx("%s: Unable to find host selector of LPC bridge", __func__);
 
        return (-1);
 }
@@ -495,8 +510,7 @@ pci_lpc_get_sel(struct pcisel *const sel)
 static int
 pci_lpc_init(struct pci_devinst *pi, nvlist_t *nvl)
 {
-       struct pcisel sel = { 0 };
-       struct pcisel *selp = NULL;
+       struct pci_conf conf, *confp;
        uint16_t device, subdevice, subvendor, vendor;
        uint8_t revid;
 
@@ -521,15 +535,16 @@ pci_lpc_init(struct pci_devinst *pi, nvlist_t *nvl)
        if (lpc_init(pi->pi_vmctx) != 0)
                return (-1);
 
-       if (pci_lpc_get_sel(&sel) == 0)
-               selp = &sel;
+       confp = NULL;
+       if (pci_lpc_get_conf(&conf) == 0)
+               confp = &conf;
 
-       vendor = pci_config_read_reg(selp, nvl, PCIR_VENDOR, 2, LPC_VENDOR);
-       device = pci_config_read_reg(selp, nvl, PCIR_DEVICE, 2, LPC_DEV);
-       revid = pci_config_read_reg(selp, nvl, PCIR_REVID, 1, LPC_REVID);
-       subvendor = pci_config_read_reg(selp, nvl, PCIR_SUBVEND_0, 2,
+       vendor = pci_config_read_reg(confp, nvl, PCIR_VENDOR, 2, LPC_VENDOR);
+       device = pci_config_read_reg(confp, nvl, PCIR_DEVICE, 2, LPC_DEV);
+       revid = pci_config_read_reg(confp, nvl, PCIR_REVID, 1, LPC_REVID);
+       subvendor = pci_config_read_reg(confp, nvl, PCIR_SUBVEND_0, 2,
            LPC_SUBVEND_0);
-       subdevice = pci_config_read_reg(selp, nvl, PCIR_SUBDEV_0, 2,
+       subdevice = pci_config_read_reg(confp, nvl, PCIR_SUBDEV_0, 2,
            LPC_SUBDEV_0);
 
        /* initialize config space */
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
index 9d6060e3e254..dc27402675ee 100644
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -353,49 +353,51 @@ pci_print_supported_devices(void)
 }
 
 uint32_t
-pci_config_read_reg(const struct pcisel *const host_sel, nvlist_t *nvl,
+pci_config_read_reg(const struct pci_conf *host_conf, nvlist_t *nvl,
     const uint32_t reg, const uint8_t size, const uint32_t def)
 {
        const char *config;
        const nvlist_t *pci_regs;
+       uint32_t host;
 
        assert(size == 1 || size == 2 || size == 4);
 
        pci_regs = find_relative_config_node(nvl, "pcireg");
        if (pci_regs == NULL) {
-               return def;
+               return (def);
        }
 
        switch (reg) {
        case PCIR_DEVICE:
                config = get_config_value_node(pci_regs, "device");
+               host = host_conf != NULL ? host_conf->pc_device : 0;
                break;
        case PCIR_VENDOR:
                config = get_config_value_node(pci_regs, "vendor");
+               host = host_conf != NULL ? host_conf->pc_vendor : 0;
                break;
        case PCIR_REVID:
                config = get_config_value_node(pci_regs, "revid");
+               host = host_conf != NULL ? host_conf->pc_revid : 0;
                break;
        case PCIR_SUBVEND_0:
                config = get_config_value_node(pci_regs, "subvendor");
+               host = host_conf != NULL ? host_conf->pc_subvendor : 0;
                break;
        case PCIR_SUBDEV_0:
                config = get_config_value_node(pci_regs, "subdevice");
+               host = host_conf != NULL ? host_conf->pc_subdevice : 0;
                break;
        default:
                return (-1);
        }
 
        if (config == NULL) {
-               return def;
-       } else if (host_sel != NULL && strcmp(config, "host") == 0) {
-#ifdef __amd64__
-               return pci_host_read_config(host_sel, reg, size);
-#else
-               errx(1, "cannot fetch host PCI configuration");
-#endif
+               return (def);
+       } else if (host_conf != NULL && strcmp(config, "host") == 0) {
+               return (host);
        } else {
-               return strtol(config, NULL, 16);
+               return (strtol(config, NULL, 16));
        }
 }
 
diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h
index 063b4b84e044..29e9717c1593 100644
--- a/usr.sbin/bhyve/pci_emul.h
+++ b/usr.sbin/bhyve/pci_emul.h
@@ -230,7 +230,7 @@ void        pci_walk_lintr(int bus, pci_lintr_cb cb, void 
*arg);
 
 int    init_pci(struct vmctx *ctx);
 void   pci_callback(void);
-uint32_t pci_config_read_reg(const struct pcisel *host_sel, nvlist_t *nvl,
+uint32_t pci_config_read_reg(const struct pci_conf *host_conf, nvlist_t *nvl,
            uint32_t reg, uint8_t size, uint32_t def);
 int    pci_emul_alloc_bar(struct pci_devinst *pdi, int idx,
            enum pcibar_type type, uint64_t size);

Reply via email to