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,72 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Beckhoff Automation GmbH & Co. KG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR OR CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#pragma once + +#include + +#include + +#include "pci_emul.h" + +typedef int (*cfgread_handler)(struct vmctx *ctx, int vcpu, + struct pci_devinst *pi, int coff, int bytes, uint32_t *rv); +typedef int (*cfgwrite_handler)(struct vmctx *ctx, int vcpu, + struct pci_devinst *pi, int coff, int bytes, uint32_t val); + +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; + + cfgread_handler psc_pcir_rhandler[PCI_REGMAX + 1]; + cfgwrite_handler psc_pcir_whandler[PCI_REGMAX + 1]; +}; + +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); +int passthru_cfgread_default(struct vmctx *ctx, int vcpu, + struct pci_devinst *pi, int coff, int bytes, uint32_t *rv); +int passthru_cfgread_emulate(struct vmctx *ctx, int vcpu, + struct pci_devinst *pi, int coff, int bytes, uint32_t *rv); +int passthru_cfgwrite_default(struct vmctx *ctx, int vcpu, + struct pci_devinst *pi, int coff, int bytes, uint32_t val); +int passthru_cfgwrite_emulate(struct vmctx *ctx, int vcpu, + struct pci_devinst *pi, int coff, int bytes, uint32_t val); +int set_pcir_handler(struct passthru_softc *sc, uint32_t reg, uint32_t len, + cfgread_handler rhandler, cfgwrite_handler whandler); 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" @@ -77,21 +76,9 @@ #define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1) #define MSIX_CAPLEN 12 -static int pcifd = -1; +#define PCI_CAP_START_OFFSET 0x40 -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 pcifd = -1; static int msi_caplen(int msgctrl) @@ -115,7 +102,7 @@ return (len); } -static uint32_t +uint32_t read_config(const struct pcisel *sel, long reg, int width) { struct pci_io pi; @@ -131,7 +118,7 @@ return (pi.pi_data); } -static void +void write_config(const struct pcisel *sel, long reg, int width, uint32_t data) { struct pci_io pi; @@ -570,6 +557,17 @@ sc->psc_sel.pc_dev = slot; sc->psc_sel.pc_func = func; + /* copy physical PCI header to virtual cfgspace */ + for (uint32_t i = 0; i < PCI_CAP_START_OFFSET; ++i) { + /* + * INTLINE and INTPIN shouldn't be aligned with it's physical + * value and they are already set by pci_emul_init + */ + if (i == PCIR_INTLINE || i == PCIR_INTPIN) + continue; + pci_set_cfgdata8(pi, i, read_config(&sc->psc_sel, i, 1)); + } + if (cfginitmsi(sc) != 0) { warnx("failed to initialize MSI for PCI %d/%d/%d", bus, slot, func); @@ -590,6 +588,20 @@ return (error); } +int +set_pcir_handler(struct passthru_softc *sc, uint32_t reg, uint32_t len, cfgread_handler rhandler, cfgwrite_handler whandler) +{ + if (reg > PCI_REGMAX || reg + len > PCI_REGMAX + 1) + return (-1); + + for (uint32_t i = reg; i < reg + len; ++i) { + sc->psc_pcir_rhandler[i] = rhandler; + sc->psc_pcir_whandler[i] = whandler; + } + + return 0; +} + static int passthru_legacy_config(nvlist_t *nvl, const char *opts) { @@ -678,7 +690,23 @@ sc->psc_pi = pi; /* initialize config space */ - error = cfginit(ctx, pi, bus, slot, func); + if ((error = cfginit(ctx, pi, bus, slot, func)) != 0) + goto done; + + /* set default handler for all PCI registers */ + if ((error = set_pcir_handler(sc, 0, PCI_REGMAX + 1, + passthru_cfgread_default, passthru_cfgwrite_default)) != 0) + goto done; + /* protect PCI header */ + if ((error = set_pcir_handler(sc, 0, PCI_CAP_START_OFFSET, + passthru_cfgread_emulate, passthru_cfgwrite_emulate)) != 0) + goto done; + /* allow access to command and status register */ + if ((error = set_pcir_handler(sc, PCIR_COMMAND, 0x04, + passthru_cfgread_default, passthru_cfgwrite_default)) != 0) + goto done; + + error = 0; /* success */ done: if (error) { free(sc); @@ -723,29 +751,29 @@ } static int -passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, - int coff, int bytes, uint32_t *rv) +passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, + int bytes, uint32_t *rv) { struct passthru_softc *sc; sc = pi->pi_arg; - /* - * PCI BARs and MSI capability is emulated. - */ - if (bar_access(coff) || msicap_access(sc, coff)) - return (-1); + return sc->psc_pcir_rhandler[coff](ctx, vcpu, pi, coff, bytes, rv); +} + +int +passthru_cfgread_default(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t *rv) +{ + struct passthru_softc *sc; + + sc = pi->pi_arg; -#ifdef LEGACY_SUPPORT /* - * Emulate PCIR_CAP_PTR if this device does not support MSI capability - * natively. + * MSI capability is emulated. */ - if (sc->psc_msi.emulated) { - if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4) - return (-1); - } -#endif + if (msicap_access(sc, coff)) + return (-1); /* * Emulate the command register. If a single read reads both the @@ -766,9 +794,28 @@ return (0); } +int +passthru_cfgread_emulate(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t *rv) +{ + return (-1); +} + static int -passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, - int coff, int bytes, uint32_t val) +passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, + int bytes, uint32_t val) +{ + + struct passthru_softc *sc; + + sc = pi->pi_arg; + + return sc->psc_pcir_whandler[coff](ctx, vcpu, pi, coff, bytes, val); +} + +int +passthru_cfgwrite_default(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t val) { int error, msix_table_entries, i; struct passthru_softc *sc; @@ -776,12 +823,6 @@ sc = pi->pi_arg; - /* - * PCI BARs are emulated - */ - if (bar_access(coff)) - return (-1); - /* * MSI capability is emulated */ @@ -847,6 +888,13 @@ return (0); } +int +passthru_cfgwrite_emulate(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t val) +{ + return (-1); +} + static void passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value)