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);
