14c87aefeSPatrick Mooney /*- 24c87aefeSPatrick Mooney * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 34c87aefeSPatrick Mooney * 44c87aefeSPatrick Mooney * Copyright (c) 2011 NetApp, Inc. 54c87aefeSPatrick Mooney * All rights reserved. 64c87aefeSPatrick Mooney * 74c87aefeSPatrick Mooney * Redistribution and use in source and binary forms, with or without 84c87aefeSPatrick Mooney * modification, are permitted provided that the following conditions 94c87aefeSPatrick Mooney * are met: 104c87aefeSPatrick Mooney * 1. Redistributions of source code must retain the above copyright 114c87aefeSPatrick Mooney * notice, this list of conditions and the following disclaimer. 124c87aefeSPatrick Mooney * 2. Redistributions in binary form must reproduce the above copyright 134c87aefeSPatrick Mooney * notice, this list of conditions and the following disclaimer in the 144c87aefeSPatrick Mooney * documentation and/or other materials provided with the distribution. 154c87aefeSPatrick Mooney * 164c87aefeSPatrick Mooney * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 174c87aefeSPatrick Mooney * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 184c87aefeSPatrick Mooney * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 194c87aefeSPatrick Mooney * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 204c87aefeSPatrick Mooney * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 214c87aefeSPatrick Mooney * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 224c87aefeSPatrick Mooney * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 234c87aefeSPatrick Mooney * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 244c87aefeSPatrick Mooney * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 254c87aefeSPatrick Mooney * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 264c87aefeSPatrick Mooney * SUCH DAMAGE. 274c87aefeSPatrick Mooney * 284c87aefeSPatrick Mooney * $FreeBSD$ 294c87aefeSPatrick Mooney */ 304c87aefeSPatrick Mooney 314c87aefeSPatrick Mooney #include <sys/cdefs.h> 324c87aefeSPatrick Mooney __FBSDID("$FreeBSD$"); 334c87aefeSPatrick Mooney 344c87aefeSPatrick Mooney #include <sys/param.h> 354c87aefeSPatrick Mooney #ifndef WITHOUT_CAPSICUM 364c87aefeSPatrick Mooney #include <sys/capsicum.h> 374c87aefeSPatrick Mooney #endif 384c87aefeSPatrick Mooney #include <sys/types.h> 394c87aefeSPatrick Mooney #include <sys/mman.h> 404c87aefeSPatrick Mooney #include <sys/pciio.h> 414c87aefeSPatrick Mooney #include <sys/ioctl.h> 424c87aefeSPatrick Mooney 43eb9a1df2SHans Rosenfeld #include <sys/pci.h> 44eb9a1df2SHans Rosenfeld 454c87aefeSPatrick Mooney #include <dev/io/iodev.h> 464c87aefeSPatrick Mooney #include <dev/pci/pcireg.h> 474c87aefeSPatrick Mooney 484c87aefeSPatrick Mooney #include <machine/iodev.h> 494c87aefeSPatrick Mooney 504c87aefeSPatrick Mooney #ifndef WITHOUT_CAPSICUM 514c87aefeSPatrick Mooney #include <capsicum_helpers.h> 524c87aefeSPatrick Mooney #endif 534c87aefeSPatrick Mooney #include <stdio.h> 544c87aefeSPatrick Mooney #include <stdlib.h> 554c87aefeSPatrick Mooney #include <string.h> 564c87aefeSPatrick Mooney #include <err.h> 574c87aefeSPatrick Mooney #include <errno.h> 584c87aefeSPatrick Mooney #include <fcntl.h> 594c87aefeSPatrick Mooney #include <sysexits.h> 604c87aefeSPatrick Mooney #include <unistd.h> 614c87aefeSPatrick Mooney 624c87aefeSPatrick Mooney #include <machine/vmm.h> 634c87aefeSPatrick Mooney #include <vmmapi.h> 64eb9a1df2SHans Rosenfeld #include <sys/ppt_dev.h> 654c87aefeSPatrick Mooney #include "pci_emul.h" 664c87aefeSPatrick Mooney #include "mem.h" 674c87aefeSPatrick Mooney 684c87aefeSPatrick Mooney #define LEGACY_SUPPORT 1 694c87aefeSPatrick Mooney 704c87aefeSPatrick Mooney #define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1) 714c87aefeSPatrick Mooney #define MSIX_CAPLEN 12 724c87aefeSPatrick Mooney 734c87aefeSPatrick Mooney struct passthru_softc { 744c87aefeSPatrick Mooney struct pci_devinst *psc_pi; 754c87aefeSPatrick Mooney struct pcibar psc_bar[PCI_BARMAX + 1]; 764c87aefeSPatrick Mooney struct { 774c87aefeSPatrick Mooney int capoff; 784c87aefeSPatrick Mooney int msgctrl; 794c87aefeSPatrick Mooney int emulated; 804c87aefeSPatrick Mooney } psc_msi; 814c87aefeSPatrick Mooney struct { 824c87aefeSPatrick Mooney int capoff; 834c87aefeSPatrick Mooney } psc_msix; 84eb9a1df2SHans Rosenfeld int pptfd; 85eb9a1df2SHans Rosenfeld int msi_limit; 86eb9a1df2SHans Rosenfeld int msix_limit; 874c87aefeSPatrick Mooney }; 884c87aefeSPatrick Mooney 894c87aefeSPatrick Mooney static int 904c87aefeSPatrick Mooney msi_caplen(int msgctrl) 914c87aefeSPatrick Mooney { 924c87aefeSPatrick Mooney int len; 934c87aefeSPatrick Mooney 944c87aefeSPatrick Mooney len = 10; /* minimum length of msi capability */ 954c87aefeSPatrick Mooney 964c87aefeSPatrick Mooney if (msgctrl & PCIM_MSICTRL_64BIT) 974c87aefeSPatrick Mooney len += 4; 984c87aefeSPatrick Mooney 994c87aefeSPatrick Mooney #if 0 1004c87aefeSPatrick Mooney /* 1014c87aefeSPatrick Mooney * Ignore the 'mask' and 'pending' bits in the MSI capability. 1024c87aefeSPatrick Mooney * We'll let the guest manipulate them directly. 1034c87aefeSPatrick Mooney */ 1044c87aefeSPatrick Mooney if (msgctrl & PCIM_MSICTRL_VECTOR) 1054c87aefeSPatrick Mooney len += 10; 1064c87aefeSPatrick Mooney #endif 1074c87aefeSPatrick Mooney 1084c87aefeSPatrick Mooney return (len); 1094c87aefeSPatrick Mooney } 1104c87aefeSPatrick Mooney 1114c87aefeSPatrick Mooney static uint32_t 112eb9a1df2SHans Rosenfeld read_config(const struct passthru_softc *sc, long reg, int width) 1134c87aefeSPatrick Mooney { 114eb9a1df2SHans Rosenfeld struct ppt_cfg_io pi; 1154c87aefeSPatrick Mooney 116eb9a1df2SHans Rosenfeld pi.pci_off = reg; 117eb9a1df2SHans Rosenfeld pi.pci_width = width; 1184c87aefeSPatrick Mooney 119eb9a1df2SHans Rosenfeld if (ioctl(sc->pptfd, PPT_CFG_READ, &pi) != 0) { 120eb9a1df2SHans Rosenfeld return (0); 121eb9a1df2SHans Rosenfeld } 122eb9a1df2SHans Rosenfeld return (pi.pci_data); 1234c87aefeSPatrick Mooney } 1244c87aefeSPatrick Mooney 1254c87aefeSPatrick Mooney static void 126eb9a1df2SHans Rosenfeld write_config(const struct passthru_softc *sc, long reg, int width, 127eb9a1df2SHans Rosenfeld uint32_t data) 1284c87aefeSPatrick Mooney { 129eb9a1df2SHans Rosenfeld struct ppt_cfg_io pi; 1304c87aefeSPatrick Mooney 131eb9a1df2SHans Rosenfeld pi.pci_off = reg; 132eb9a1df2SHans Rosenfeld pi.pci_width = width; 133eb9a1df2SHans Rosenfeld pi.pci_data = data; 1344c87aefeSPatrick Mooney 135eb9a1df2SHans Rosenfeld (void) ioctl(sc->pptfd, PPT_CFG_WRITE, &pi); 136eb9a1df2SHans Rosenfeld } 137eb9a1df2SHans Rosenfeld 138eb9a1df2SHans Rosenfeld static int 139eb9a1df2SHans Rosenfeld passthru_get_bar(struct passthru_softc *sc, int bar, enum pcibar_type *type, 140eb9a1df2SHans Rosenfeld uint64_t *base, uint64_t *size) 141eb9a1df2SHans Rosenfeld { 142eb9a1df2SHans Rosenfeld struct ppt_bar_query pb; 143eb9a1df2SHans Rosenfeld 144eb9a1df2SHans Rosenfeld pb.pbq_baridx = bar; 145eb9a1df2SHans Rosenfeld 146eb9a1df2SHans Rosenfeld if (ioctl(sc->pptfd, PPT_BAR_QUERY, &pb) != 0) { 147eb9a1df2SHans Rosenfeld return (-1); 148eb9a1df2SHans Rosenfeld } 149eb9a1df2SHans Rosenfeld 150eb9a1df2SHans Rosenfeld switch (pb.pbq_type) { 151eb9a1df2SHans Rosenfeld case PCI_ADDR_IO: 152eb9a1df2SHans Rosenfeld *type = PCIBAR_IO; 153eb9a1df2SHans Rosenfeld break; 154eb9a1df2SHans Rosenfeld case PCI_ADDR_MEM32: 155eb9a1df2SHans Rosenfeld *type = PCIBAR_MEM32; 156eb9a1df2SHans Rosenfeld break; 157eb9a1df2SHans Rosenfeld case PCI_ADDR_MEM64: 158eb9a1df2SHans Rosenfeld *type = PCIBAR_MEM64; 159eb9a1df2SHans Rosenfeld break; 160eb9a1df2SHans Rosenfeld default: 161eb9a1df2SHans Rosenfeld err(1, "unrecognized BAR type: %u\n", pb.pbq_type); 162eb9a1df2SHans Rosenfeld break; 163eb9a1df2SHans Rosenfeld } 164eb9a1df2SHans Rosenfeld 165eb9a1df2SHans Rosenfeld *base = pb.pbq_base; 166eb9a1df2SHans Rosenfeld *size = pb.pbq_size; 167eb9a1df2SHans Rosenfeld return (0); 168eb9a1df2SHans Rosenfeld } 169eb9a1df2SHans Rosenfeld 170eb9a1df2SHans Rosenfeld static int 171eb9a1df2SHans Rosenfeld passthru_dev_open(const char *path, int *pptfdp) 172eb9a1df2SHans Rosenfeld { 173eb9a1df2SHans Rosenfeld int pptfd; 174eb9a1df2SHans Rosenfeld 175eb9a1df2SHans Rosenfeld if ((pptfd = open(path, O_RDWR)) < 0) { 176eb9a1df2SHans Rosenfeld return (errno); 177eb9a1df2SHans Rosenfeld } 178eb9a1df2SHans Rosenfeld 179eb9a1df2SHans Rosenfeld /* XXX: verify fd with ioctl? */ 180eb9a1df2SHans Rosenfeld *pptfdp = pptfd; 181eb9a1df2SHans Rosenfeld return (0); 1824c87aefeSPatrick Mooney } 1834c87aefeSPatrick Mooney 1844c87aefeSPatrick Mooney #ifdef LEGACY_SUPPORT 1854c87aefeSPatrick Mooney static int 1864c87aefeSPatrick Mooney passthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr) 1874c87aefeSPatrick Mooney { 1884c87aefeSPatrick Mooney int capoff, i; 1894c87aefeSPatrick Mooney struct msicap msicap; 1904c87aefeSPatrick Mooney u_char *capdata; 1914c87aefeSPatrick Mooney 1924c87aefeSPatrick Mooney pci_populate_msicap(&msicap, msgnum, nextptr); 1934c87aefeSPatrick Mooney 1944c87aefeSPatrick Mooney /* 1954c87aefeSPatrick Mooney * XXX 1964c87aefeSPatrick Mooney * Copy the msi capability structure in the last 16 bytes of the 1974c87aefeSPatrick Mooney * config space. This is wrong because it could shadow something 1984c87aefeSPatrick Mooney * useful to the device. 1994c87aefeSPatrick Mooney */ 2004c87aefeSPatrick Mooney capoff = 256 - roundup(sizeof(msicap), 4); 2014c87aefeSPatrick Mooney capdata = (u_char *)&msicap; 2024c87aefeSPatrick Mooney for (i = 0; i < sizeof(msicap); i++) 2034c87aefeSPatrick Mooney pci_set_cfgdata8(pi, capoff + i, capdata[i]); 2044c87aefeSPatrick Mooney 2054c87aefeSPatrick Mooney return (capoff); 2064c87aefeSPatrick Mooney } 2074c87aefeSPatrick Mooney #endif /* LEGACY_SUPPORT */ 2084c87aefeSPatrick Mooney 209eb9a1df2SHans Rosenfeld static void 210eb9a1df2SHans Rosenfeld passthru_intr_limit(struct passthru_softc *sc, struct msixcap *msixcap) 211eb9a1df2SHans Rosenfeld { 212eb9a1df2SHans Rosenfeld struct pci_devinst *pi = sc->psc_pi; 213eb9a1df2SHans Rosenfeld int off; 214eb9a1df2SHans Rosenfeld 215eb9a1df2SHans Rosenfeld /* Reduce the number of MSI vectors if higher than OS limit */ 216eb9a1df2SHans Rosenfeld if ((off = sc->psc_msi.capoff) != 0 && sc->msi_limit != -1) { 217eb9a1df2SHans Rosenfeld int msi_limit, mmc; 218eb9a1df2SHans Rosenfeld 219eb9a1df2SHans Rosenfeld msi_limit = 220eb9a1df2SHans Rosenfeld sc->msi_limit > 16 ? PCIM_MSICTRL_MMC_32 : 221eb9a1df2SHans Rosenfeld sc->msi_limit > 8 ? PCIM_MSICTRL_MMC_16 : 222eb9a1df2SHans Rosenfeld sc->msi_limit > 4 ? PCIM_MSICTRL_MMC_8 : 223eb9a1df2SHans Rosenfeld sc->msi_limit > 2 ? PCIM_MSICTRL_MMC_4 : 224eb9a1df2SHans Rosenfeld sc->msi_limit > 1 ? PCIM_MSICTRL_MMC_2 : 225eb9a1df2SHans Rosenfeld PCIM_MSICTRL_MMC_1; 226eb9a1df2SHans Rosenfeld mmc = sc->psc_msi.msgctrl & PCIM_MSICTRL_MMC_MASK; 227eb9a1df2SHans Rosenfeld 228eb9a1df2SHans Rosenfeld if (mmc > msi_limit) { 229eb9a1df2SHans Rosenfeld sc->psc_msi.msgctrl &= ~PCIM_MSICTRL_MMC_MASK; 230eb9a1df2SHans Rosenfeld sc->psc_msi.msgctrl |= msi_limit; 231eb9a1df2SHans Rosenfeld pci_set_cfgdata16(pi, off + 2, sc->psc_msi.msgctrl); 232eb9a1df2SHans Rosenfeld } 233eb9a1df2SHans Rosenfeld } 234eb9a1df2SHans Rosenfeld 235eb9a1df2SHans Rosenfeld /* Reduce the number of MSI-X vectors if higher than OS limit */ 236eb9a1df2SHans Rosenfeld if ((off = sc->psc_msix.capoff) != 0 && sc->msix_limit != -1) { 237eb9a1df2SHans Rosenfeld if (MSIX_TABLE_COUNT(msixcap->msgctrl) > sc->msix_limit) { 238eb9a1df2SHans Rosenfeld msixcap->msgctrl &= ~PCIM_MSIXCTRL_TABLE_SIZE; 239eb9a1df2SHans Rosenfeld msixcap->msgctrl |= sc->msix_limit - 1; 240eb9a1df2SHans Rosenfeld pci_set_cfgdata16(pi, off + 2, msixcap->msgctrl); 241eb9a1df2SHans Rosenfeld } 242eb9a1df2SHans Rosenfeld } 243eb9a1df2SHans Rosenfeld } 244eb9a1df2SHans Rosenfeld 2454c87aefeSPatrick Mooney static int 2464c87aefeSPatrick Mooney cfginitmsi(struct passthru_softc *sc) 2474c87aefeSPatrick Mooney { 2484c87aefeSPatrick Mooney int i, ptr, capptr, cap, sts, caplen, table_size; 2494c87aefeSPatrick Mooney uint32_t u32; 250eb9a1df2SHans Rosenfeld struct pci_devinst *pi = sc->psc_pi; 2514c87aefeSPatrick Mooney struct msixcap msixcap; 2524c87aefeSPatrick Mooney uint32_t *msixcap_ptr; 2534c87aefeSPatrick Mooney 2544c87aefeSPatrick Mooney /* 2554c87aefeSPatrick Mooney * Parse the capabilities and cache the location of the MSI 2564c87aefeSPatrick Mooney * and MSI-X capabilities. 2574c87aefeSPatrick Mooney */ 258eb9a1df2SHans Rosenfeld sts = read_config(sc, PCIR_STATUS, 2); 2594c87aefeSPatrick Mooney if (sts & PCIM_STATUS_CAPPRESENT) { 260eb9a1df2SHans Rosenfeld ptr = read_config(sc, PCIR_CAP_PTR, 1); 2614c87aefeSPatrick Mooney while (ptr != 0 && ptr != 0xff) { 262eb9a1df2SHans Rosenfeld cap = read_config(sc, ptr + PCICAP_ID, 1); 2634c87aefeSPatrick Mooney if (cap == PCIY_MSI) { 2644c87aefeSPatrick Mooney /* 2654c87aefeSPatrick Mooney * Copy the MSI capability into the config 2664c87aefeSPatrick Mooney * space of the emulated pci device 2674c87aefeSPatrick Mooney */ 2684c87aefeSPatrick Mooney sc->psc_msi.capoff = ptr; 269eb9a1df2SHans Rosenfeld sc->psc_msi.msgctrl = read_config(sc, 2704c87aefeSPatrick Mooney ptr + 2, 2); 2714c87aefeSPatrick Mooney sc->psc_msi.emulated = 0; 2724c87aefeSPatrick Mooney caplen = msi_caplen(sc->psc_msi.msgctrl); 2734c87aefeSPatrick Mooney capptr = ptr; 2744c87aefeSPatrick Mooney while (caplen > 0) { 275eb9a1df2SHans Rosenfeld u32 = read_config(sc, capptr, 4); 2764c87aefeSPatrick Mooney pci_set_cfgdata32(pi, capptr, u32); 2774c87aefeSPatrick Mooney caplen -= 4; 2784c87aefeSPatrick Mooney capptr += 4; 2794c87aefeSPatrick Mooney } 2804c87aefeSPatrick Mooney } else if (cap == PCIY_MSIX) { 2814c87aefeSPatrick Mooney /* 2824c87aefeSPatrick Mooney * Copy the MSI-X capability 2834c87aefeSPatrick Mooney */ 2844c87aefeSPatrick Mooney sc->psc_msix.capoff = ptr; 2854c87aefeSPatrick Mooney caplen = 12; 2864c87aefeSPatrick Mooney msixcap_ptr = (uint32_t*) &msixcap; 2874c87aefeSPatrick Mooney capptr = ptr; 2884c87aefeSPatrick Mooney while (caplen > 0) { 289eb9a1df2SHans Rosenfeld u32 = read_config(sc, capptr, 4); 2904c87aefeSPatrick Mooney *msixcap_ptr = u32; 2914c87aefeSPatrick Mooney pci_set_cfgdata32(pi, capptr, u32); 2924c87aefeSPatrick Mooney caplen -= 4; 2934c87aefeSPatrick Mooney capptr += 4; 2944c87aefeSPatrick Mooney msixcap_ptr++; 2954c87aefeSPatrick Mooney } 2964c87aefeSPatrick Mooney } 297eb9a1df2SHans Rosenfeld ptr = read_config(sc, ptr + PCICAP_NEXTPTR, 1); 2984c87aefeSPatrick Mooney } 2994c87aefeSPatrick Mooney } 3004c87aefeSPatrick Mooney 301eb9a1df2SHans Rosenfeld passthru_intr_limit(sc, &msixcap); 302eb9a1df2SHans Rosenfeld 3034c87aefeSPatrick Mooney if (sc->psc_msix.capoff != 0) { 3044c87aefeSPatrick Mooney pi->pi_msix.pba_bar = 3054c87aefeSPatrick Mooney msixcap.pba_info & PCIM_MSIX_BIR_MASK; 3064c87aefeSPatrick Mooney pi->pi_msix.pba_offset = 3074c87aefeSPatrick Mooney msixcap.pba_info & ~PCIM_MSIX_BIR_MASK; 3084c87aefeSPatrick Mooney pi->pi_msix.table_bar = 3094c87aefeSPatrick Mooney msixcap.table_info & PCIM_MSIX_BIR_MASK; 3104c87aefeSPatrick Mooney pi->pi_msix.table_offset = 3114c87aefeSPatrick Mooney msixcap.table_info & ~PCIM_MSIX_BIR_MASK; 3124c87aefeSPatrick Mooney pi->pi_msix.table_count = MSIX_TABLE_COUNT(msixcap.msgctrl); 3134c87aefeSPatrick Mooney pi->pi_msix.pba_size = PBA_SIZE(pi->pi_msix.table_count); 3144c87aefeSPatrick Mooney 3154c87aefeSPatrick Mooney /* Allocate the emulated MSI-X table array */ 3164c87aefeSPatrick Mooney table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; 3174c87aefeSPatrick Mooney pi->pi_msix.table = calloc(1, table_size); 3184c87aefeSPatrick Mooney 3194c87aefeSPatrick Mooney /* Mask all table entries */ 3204c87aefeSPatrick Mooney for (i = 0; i < pi->pi_msix.table_count; i++) { 3214c87aefeSPatrick Mooney pi->pi_msix.table[i].vector_control |= 3224c87aefeSPatrick Mooney PCIM_MSIX_VCTRL_MASK; 3234c87aefeSPatrick Mooney } 3244c87aefeSPatrick Mooney } 3254c87aefeSPatrick Mooney 3264c87aefeSPatrick Mooney #ifdef LEGACY_SUPPORT 3274c87aefeSPatrick Mooney /* 3284c87aefeSPatrick Mooney * If the passthrough device does not support MSI then craft a 3294c87aefeSPatrick Mooney * MSI capability for it. We link the new MSI capability at the 3304c87aefeSPatrick Mooney * head of the list of capabilities. 3314c87aefeSPatrick Mooney */ 3324c87aefeSPatrick Mooney if ((sts & PCIM_STATUS_CAPPRESENT) != 0 && sc->psc_msi.capoff == 0) { 3334c87aefeSPatrick Mooney int origptr, msiptr; 334eb9a1df2SHans Rosenfeld origptr = read_config(sc, PCIR_CAP_PTR, 1); 3354c87aefeSPatrick Mooney msiptr = passthru_add_msicap(pi, 1, origptr); 3364c87aefeSPatrick Mooney sc->psc_msi.capoff = msiptr; 3374c87aefeSPatrick Mooney sc->psc_msi.msgctrl = pci_get_cfgdata16(pi, msiptr + 2); 3384c87aefeSPatrick Mooney sc->psc_msi.emulated = 1; 3394c87aefeSPatrick Mooney pci_set_cfgdata8(pi, PCIR_CAP_PTR, msiptr); 3404c87aefeSPatrick Mooney } 3414c87aefeSPatrick Mooney #endif 3424c87aefeSPatrick Mooney 3434c87aefeSPatrick Mooney /* Make sure one of the capabilities is present */ 344eb9a1df2SHans Rosenfeld if (sc->psc_msi.capoff == 0 && sc->psc_msix.capoff == 0) { 3454c87aefeSPatrick Mooney return (-1); 346eb9a1df2SHans Rosenfeld } else { 3474c87aefeSPatrick Mooney return (0); 3484c87aefeSPatrick Mooney } 349eb9a1df2SHans Rosenfeld } 3504c87aefeSPatrick Mooney 3514c87aefeSPatrick Mooney static uint64_t 352eb9a1df2SHans Rosenfeld passthru_msix_table_read(struct passthru_softc *sc, uint64_t offset, int size) 3534c87aefeSPatrick Mooney { 3544c87aefeSPatrick Mooney struct pci_devinst *pi; 3554c87aefeSPatrick Mooney struct msix_table_entry *entry; 3564c87aefeSPatrick Mooney uint8_t *src8; 3574c87aefeSPatrick Mooney uint16_t *src16; 3584c87aefeSPatrick Mooney uint32_t *src32; 3594c87aefeSPatrick Mooney uint64_t *src64; 3604c87aefeSPatrick Mooney uint64_t data; 3614c87aefeSPatrick Mooney size_t entry_offset; 3624c87aefeSPatrick Mooney int index; 3634c87aefeSPatrick Mooney 3644c87aefeSPatrick Mooney pi = sc->psc_pi; 3654c87aefeSPatrick Mooney if (offset >= pi->pi_msix.pba_offset && 3664c87aefeSPatrick Mooney offset < pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { 3674c87aefeSPatrick Mooney switch(size) { 3684c87aefeSPatrick Mooney case 1: 3694c87aefeSPatrick Mooney src8 = (uint8_t *)(pi->pi_msix.pba_page + offset - 3704c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset); 3714c87aefeSPatrick Mooney data = *src8; 3724c87aefeSPatrick Mooney break; 3734c87aefeSPatrick Mooney case 2: 3744c87aefeSPatrick Mooney src16 = (uint16_t *)(pi->pi_msix.pba_page + offset - 3754c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset); 3764c87aefeSPatrick Mooney data = *src16; 3774c87aefeSPatrick Mooney break; 3784c87aefeSPatrick Mooney case 4: 3794c87aefeSPatrick Mooney src32 = (uint32_t *)(pi->pi_msix.pba_page + offset - 3804c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset); 3814c87aefeSPatrick Mooney data = *src32; 3824c87aefeSPatrick Mooney break; 3834c87aefeSPatrick Mooney case 8: 3844c87aefeSPatrick Mooney src64 = (uint64_t *)(pi->pi_msix.pba_page + offset - 3854c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset); 3864c87aefeSPatrick Mooney data = *src64; 3874c87aefeSPatrick Mooney break; 3884c87aefeSPatrick Mooney default: 3894c87aefeSPatrick Mooney return (-1); 3904c87aefeSPatrick Mooney } 3914c87aefeSPatrick Mooney return (data); 3924c87aefeSPatrick Mooney } 3934c87aefeSPatrick Mooney 3944c87aefeSPatrick Mooney if (offset < pi->pi_msix.table_offset) 3954c87aefeSPatrick Mooney return (-1); 3964c87aefeSPatrick Mooney 3974c87aefeSPatrick Mooney offset -= pi->pi_msix.table_offset; 3984c87aefeSPatrick Mooney index = offset / MSIX_TABLE_ENTRY_SIZE; 3994c87aefeSPatrick Mooney if (index >= pi->pi_msix.table_count) 4004c87aefeSPatrick Mooney return (-1); 4014c87aefeSPatrick Mooney 4024c87aefeSPatrick Mooney entry = &pi->pi_msix.table[index]; 4034c87aefeSPatrick Mooney entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 4044c87aefeSPatrick Mooney 4054c87aefeSPatrick Mooney switch(size) { 4064c87aefeSPatrick Mooney case 1: 4074c87aefeSPatrick Mooney src8 = (uint8_t *)((void *)entry + entry_offset); 4084c87aefeSPatrick Mooney data = *src8; 4094c87aefeSPatrick Mooney break; 4104c87aefeSPatrick Mooney case 2: 4114c87aefeSPatrick Mooney src16 = (uint16_t *)((void *)entry + entry_offset); 4124c87aefeSPatrick Mooney data = *src16; 4134c87aefeSPatrick Mooney break; 4144c87aefeSPatrick Mooney case 4: 4154c87aefeSPatrick Mooney src32 = (uint32_t *)((void *)entry + entry_offset); 4164c87aefeSPatrick Mooney data = *src32; 4174c87aefeSPatrick Mooney break; 4184c87aefeSPatrick Mooney case 8: 4194c87aefeSPatrick Mooney src64 = (uint64_t *)((void *)entry + entry_offset); 4204c87aefeSPatrick Mooney data = *src64; 4214c87aefeSPatrick Mooney break; 4224c87aefeSPatrick Mooney default: 4234c87aefeSPatrick Mooney return (-1); 4244c87aefeSPatrick Mooney } 4254c87aefeSPatrick Mooney 4264c87aefeSPatrick Mooney return (data); 4274c87aefeSPatrick Mooney } 4284c87aefeSPatrick Mooney 4294c87aefeSPatrick Mooney static void 430eb9a1df2SHans Rosenfeld passthru_msix_table_write(struct vmctx *ctx, int vcpu, 431eb9a1df2SHans Rosenfeld struct passthru_softc *sc, uint64_t offset, int size, uint64_t data) 4324c87aefeSPatrick Mooney { 4334c87aefeSPatrick Mooney struct pci_devinst *pi; 4344c87aefeSPatrick Mooney struct msix_table_entry *entry; 4354c87aefeSPatrick Mooney uint8_t *dest8; 4364c87aefeSPatrick Mooney uint16_t *dest16; 4374c87aefeSPatrick Mooney uint32_t *dest32; 4384c87aefeSPatrick Mooney uint64_t *dest64; 4394c87aefeSPatrick Mooney size_t entry_offset; 4404c87aefeSPatrick Mooney uint32_t vector_control; 4414c87aefeSPatrick Mooney int index; 4424c87aefeSPatrick Mooney 4434c87aefeSPatrick Mooney pi = sc->psc_pi; 4444c87aefeSPatrick Mooney if (offset >= pi->pi_msix.pba_offset && 4454c87aefeSPatrick Mooney offset < pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { 4464c87aefeSPatrick Mooney switch(size) { 4474c87aefeSPatrick Mooney case 1: 4484c87aefeSPatrick Mooney dest8 = (uint8_t *)(pi->pi_msix.pba_page + offset - 4494c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset); 4504c87aefeSPatrick Mooney *dest8 = data; 4514c87aefeSPatrick Mooney break; 4524c87aefeSPatrick Mooney case 2: 4534c87aefeSPatrick Mooney dest16 = (uint16_t *)(pi->pi_msix.pba_page + offset - 4544c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset); 4554c87aefeSPatrick Mooney *dest16 = data; 4564c87aefeSPatrick Mooney break; 4574c87aefeSPatrick Mooney case 4: 4584c87aefeSPatrick Mooney dest32 = (uint32_t *)(pi->pi_msix.pba_page + offset - 4594c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset); 4604c87aefeSPatrick Mooney *dest32 = data; 4614c87aefeSPatrick Mooney break; 4624c87aefeSPatrick Mooney case 8: 4634c87aefeSPatrick Mooney dest64 = (uint64_t *)(pi->pi_msix.pba_page + offset - 4644c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset); 4654c87aefeSPatrick Mooney *dest64 = data; 4664c87aefeSPatrick Mooney break; 4674c87aefeSPatrick Mooney default: 4684c87aefeSPatrick Mooney break; 4694c87aefeSPatrick Mooney } 4704c87aefeSPatrick Mooney return; 4714c87aefeSPatrick Mooney } 4724c87aefeSPatrick Mooney 4734c87aefeSPatrick Mooney if (offset < pi->pi_msix.table_offset) 4744c87aefeSPatrick Mooney return; 4754c87aefeSPatrick Mooney 4764c87aefeSPatrick Mooney offset -= pi->pi_msix.table_offset; 4774c87aefeSPatrick Mooney index = offset / MSIX_TABLE_ENTRY_SIZE; 4784c87aefeSPatrick Mooney if (index >= pi->pi_msix.table_count) 4794c87aefeSPatrick Mooney return; 4804c87aefeSPatrick Mooney 4814c87aefeSPatrick Mooney entry = &pi->pi_msix.table[index]; 4824c87aefeSPatrick Mooney entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 4834c87aefeSPatrick Mooney 4844c87aefeSPatrick Mooney /* Only 4 byte naturally-aligned writes are supported */ 4854c87aefeSPatrick Mooney assert(size == 4); 4864c87aefeSPatrick Mooney assert(entry_offset % 4 == 0); 4874c87aefeSPatrick Mooney 4884c87aefeSPatrick Mooney vector_control = entry->vector_control; 4894c87aefeSPatrick Mooney dest32 = (uint32_t *)((void *)entry + entry_offset); 4904c87aefeSPatrick Mooney *dest32 = data; 4914c87aefeSPatrick Mooney /* If MSI-X hasn't been enabled, do nothing */ 4924c87aefeSPatrick Mooney if (pi->pi_msix.enabled) { 4934c87aefeSPatrick Mooney /* If the entry is masked, don't set it up */ 4944c87aefeSPatrick Mooney if ((entry->vector_control & PCIM_MSIX_VCTRL_MASK) == 0 || 4954c87aefeSPatrick Mooney (vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 496eb9a1df2SHans Rosenfeld (void) vm_setup_pptdev_msix(ctx, vcpu, sc->pptfd, 497eb9a1df2SHans Rosenfeld index, entry->addr, entry->msg_data, 498eb9a1df2SHans Rosenfeld entry->vector_control); 4994c87aefeSPatrick Mooney } 5004c87aefeSPatrick Mooney } 5014c87aefeSPatrick Mooney } 5024c87aefeSPatrick Mooney 5034c87aefeSPatrick Mooney static int 5044c87aefeSPatrick Mooney init_msix_table(struct vmctx *ctx, struct passthru_softc *sc, uint64_t base) 5054c87aefeSPatrick Mooney { 5064c87aefeSPatrick Mooney int error, idx; 5074c87aefeSPatrick Mooney size_t len, remaining; 5084c87aefeSPatrick Mooney uint32_t table_size, table_offset; 5094c87aefeSPatrick Mooney uint32_t pba_size, pba_offset; 5104c87aefeSPatrick Mooney vm_paddr_t start; 5114c87aefeSPatrick Mooney struct pci_devinst *pi = sc->psc_pi; 5124c87aefeSPatrick Mooney 5134c87aefeSPatrick Mooney assert(pci_msix_table_bar(pi) >= 0 && pci_msix_pba_bar(pi) >= 0); 5144c87aefeSPatrick Mooney 5154c87aefeSPatrick Mooney /* 5164c87aefeSPatrick Mooney * If the MSI-X table BAR maps memory intended for 5174c87aefeSPatrick Mooney * other uses, it is at least assured that the table 5184c87aefeSPatrick Mooney * either resides in its own page within the region, 5194c87aefeSPatrick Mooney * or it resides in a page shared with only the PBA. 5204c87aefeSPatrick Mooney */ 5214c87aefeSPatrick Mooney table_offset = rounddown2(pi->pi_msix.table_offset, 4096); 5224c87aefeSPatrick Mooney 5234c87aefeSPatrick Mooney table_size = pi->pi_msix.table_offset - table_offset; 5244c87aefeSPatrick Mooney table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; 5254c87aefeSPatrick Mooney table_size = roundup2(table_size, 4096); 5264c87aefeSPatrick Mooney 5274c87aefeSPatrick Mooney idx = pi->pi_msix.table_bar; 5284c87aefeSPatrick Mooney start = pi->pi_bar[idx].addr; 5294c87aefeSPatrick Mooney remaining = pi->pi_bar[idx].size; 5304c87aefeSPatrick Mooney 5314c87aefeSPatrick Mooney if (pi->pi_msix.pba_bar == pi->pi_msix.table_bar) { 5324c87aefeSPatrick Mooney pba_offset = pi->pi_msix.pba_offset; 5334c87aefeSPatrick Mooney pba_size = pi->pi_msix.pba_size; 5344c87aefeSPatrick Mooney if (pba_offset >= table_offset + table_size || 5354c87aefeSPatrick Mooney table_offset >= pba_offset + pba_size) { 5364c87aefeSPatrick Mooney /* 5374c87aefeSPatrick Mooney * If the PBA does not share a page with the MSI-x 5384c87aefeSPatrick Mooney * tables, no PBA emulation is required. 5394c87aefeSPatrick Mooney */ 5404c87aefeSPatrick Mooney pi->pi_msix.pba_page = NULL; 5414c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset = 0; 5424c87aefeSPatrick Mooney } else { 5434c87aefeSPatrick Mooney /* 5444c87aefeSPatrick Mooney * The PBA overlaps with either the first or last 5454c87aefeSPatrick Mooney * page of the MSI-X table region. Map the 5464c87aefeSPatrick Mooney * appropriate page. 5474c87aefeSPatrick Mooney */ 5484c87aefeSPatrick Mooney if (pba_offset <= table_offset) 5494c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset = table_offset; 5504c87aefeSPatrick Mooney else 5514c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset = table_offset + 5524c87aefeSPatrick Mooney table_size - 4096; 5534c87aefeSPatrick Mooney pi->pi_msix.pba_page = mmap(NULL, 4096, PROT_READ | 554eb9a1df2SHans Rosenfeld PROT_WRITE, MAP_SHARED, sc->pptfd, 5554c87aefeSPatrick Mooney pi->pi_msix.pba_page_offset); 5564c87aefeSPatrick Mooney if (pi->pi_msix.pba_page == MAP_FAILED) { 557eb9a1df2SHans Rosenfeld warn("Failed to map PBA page for MSI-X on %d", 558eb9a1df2SHans Rosenfeld sc->pptfd); 5594c87aefeSPatrick Mooney return (-1); 5604c87aefeSPatrick Mooney } 5614c87aefeSPatrick Mooney } 5624c87aefeSPatrick Mooney } 5634c87aefeSPatrick Mooney 5644c87aefeSPatrick Mooney /* Map everything before the MSI-X table */ 5654c87aefeSPatrick Mooney if (table_offset > 0) { 5664c87aefeSPatrick Mooney len = table_offset; 567eb9a1df2SHans Rosenfeld error = vm_map_pptdev_mmio(ctx, sc->pptfd, start, len, base); 5684c87aefeSPatrick Mooney if (error) 5694c87aefeSPatrick Mooney return (error); 5704c87aefeSPatrick Mooney 5714c87aefeSPatrick Mooney base += len; 5724c87aefeSPatrick Mooney start += len; 5734c87aefeSPatrick Mooney remaining -= len; 5744c87aefeSPatrick Mooney } 5754c87aefeSPatrick Mooney 5764c87aefeSPatrick Mooney /* Skip the MSI-X table */ 5774c87aefeSPatrick Mooney base += table_size; 5784c87aefeSPatrick Mooney start += table_size; 5794c87aefeSPatrick Mooney remaining -= table_size; 5804c87aefeSPatrick Mooney 5814c87aefeSPatrick Mooney /* Map everything beyond the end of the MSI-X table */ 5824c87aefeSPatrick Mooney if (remaining > 0) { 5834c87aefeSPatrick Mooney len = remaining; 584eb9a1df2SHans Rosenfeld error = vm_map_pptdev_mmio(ctx, sc->pptfd, start, len, base); 5854c87aefeSPatrick Mooney if (error) 5864c87aefeSPatrick Mooney return (error); 5874c87aefeSPatrick Mooney } 5884c87aefeSPatrick Mooney 5894c87aefeSPatrick Mooney return (0); 5904c87aefeSPatrick Mooney } 5914c87aefeSPatrick Mooney 5924c87aefeSPatrick Mooney static int 5934c87aefeSPatrick Mooney cfginitbar(struct vmctx *ctx, struct passthru_softc *sc) 5944c87aefeSPatrick Mooney { 595eb9a1df2SHans Rosenfeld struct pci_devinst *pi = sc->psc_pi; 596eb9a1df2SHans Rosenfeld uint_t i; 5974c87aefeSPatrick Mooney 5984c87aefeSPatrick Mooney /* 5994c87aefeSPatrick Mooney * Initialize BAR registers 6004c87aefeSPatrick Mooney */ 6014c87aefeSPatrick Mooney for (i = 0; i <= PCI_BARMAX; i++) { 602eb9a1df2SHans Rosenfeld enum pcibar_type bartype; 603eb9a1df2SHans Rosenfeld uint64_t base, size; 604eb9a1df2SHans Rosenfeld int error; 6054c87aefeSPatrick Mooney 606eb9a1df2SHans Rosenfeld if (passthru_get_bar(sc, i, &bartype, &base, &size) != 0) { 6074c87aefeSPatrick Mooney continue; 6084c87aefeSPatrick Mooney } 6094c87aefeSPatrick Mooney 6104c87aefeSPatrick Mooney if (bartype != PCIBAR_IO) { 6114c87aefeSPatrick Mooney if (((base | size) & PAGE_MASK) != 0) { 612eb9a1df2SHans Rosenfeld warnx("passthru device %d BAR %d: " 6134c87aefeSPatrick Mooney "base %#lx or size %#lx not page aligned\n", 614eb9a1df2SHans Rosenfeld sc->pptfd, i, base, size); 6154c87aefeSPatrick Mooney return (-1); 6164c87aefeSPatrick Mooney } 6174c87aefeSPatrick Mooney } 6184c87aefeSPatrick Mooney 6194c87aefeSPatrick Mooney /* Cache information about the "real" BAR */ 6204c87aefeSPatrick Mooney sc->psc_bar[i].type = bartype; 6214c87aefeSPatrick Mooney sc->psc_bar[i].size = size; 6224c87aefeSPatrick Mooney sc->psc_bar[i].addr = base; 6234c87aefeSPatrick Mooney 6244c87aefeSPatrick Mooney /* Allocate the BAR in the guest I/O or MMIO space */ 625*6960cd89SAndy Fiddaman error = pci_emul_alloc_bar(pi, i, bartype, size); 6264c87aefeSPatrick Mooney if (error) 6274c87aefeSPatrick Mooney return (-1); 6284c87aefeSPatrick Mooney 6294c87aefeSPatrick Mooney /* The MSI-X table needs special handling */ 6304c87aefeSPatrick Mooney if (i == pci_msix_table_bar(pi)) { 6314c87aefeSPatrick Mooney error = init_msix_table(ctx, sc, base); 6324c87aefeSPatrick Mooney if (error) 6334c87aefeSPatrick Mooney return (-1); 6344c87aefeSPatrick Mooney } else if (bartype != PCIBAR_IO) { 6354c87aefeSPatrick Mooney /* Map the physical BAR in the guest MMIO space */ 636eb9a1df2SHans Rosenfeld error = vm_map_pptdev_mmio(ctx, sc->pptfd, 6374c87aefeSPatrick Mooney pi->pi_bar[i].addr, pi->pi_bar[i].size, base); 6384c87aefeSPatrick Mooney if (error) 6394c87aefeSPatrick Mooney return (-1); 6404c87aefeSPatrick Mooney } 6414c87aefeSPatrick Mooney 6424c87aefeSPatrick Mooney /* 6434c87aefeSPatrick Mooney * 64-bit BAR takes up two slots so skip the next one. 6444c87aefeSPatrick Mooney */ 6454c87aefeSPatrick Mooney if (bartype == PCIBAR_MEM64) { 6464c87aefeSPatrick Mooney i++; 6474c87aefeSPatrick Mooney assert(i <= PCI_BARMAX); 6484c87aefeSPatrick Mooney sc->psc_bar[i].type = PCIBAR_MEMHI64; 6494c87aefeSPatrick Mooney } 6504c87aefeSPatrick Mooney } 6514c87aefeSPatrick Mooney return (0); 6524c87aefeSPatrick Mooney } 6534c87aefeSPatrick Mooney 6544c87aefeSPatrick Mooney static int 655eb9a1df2SHans Rosenfeld cfginit(struct vmctx *ctx, struct passthru_softc *sc) 6564c87aefeSPatrick Mooney { 657e4321372SMichael Zeller struct pci_devinst *pi = sc->psc_pi; 658e4321372SMichael Zeller 6594c87aefeSPatrick Mooney if (cfginitmsi(sc) != 0) { 660eb9a1df2SHans Rosenfeld warnx("failed to initialize MSI for PCI %d", sc->pptfd); 661eb9a1df2SHans Rosenfeld return (-1); 6624c87aefeSPatrick Mooney } 6634c87aefeSPatrick Mooney 6644c87aefeSPatrick Mooney if (cfginitbar(ctx, sc) != 0) { 665eb9a1df2SHans Rosenfeld warnx("failed to initialize BARs for PCI %d", sc->pptfd); 666eb9a1df2SHans Rosenfeld return (-1); 6674c87aefeSPatrick Mooney } 6684c87aefeSPatrick Mooney 669e4321372SMichael Zeller pci_set_cfgdata16(pi, PCIR_COMMAND, read_config(sc, PCIR_COMMAND, 2)); 670e4321372SMichael Zeller 671eb9a1df2SHans Rosenfeld return (0); 6724c87aefeSPatrick Mooney } 6734c87aefeSPatrick Mooney 6744c87aefeSPatrick Mooney static int 6754c87aefeSPatrick Mooney passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 6764c87aefeSPatrick Mooney { 677eb9a1df2SHans Rosenfeld int error, memflags, pptfd; 6784c87aefeSPatrick Mooney struct passthru_softc *sc; 6794c87aefeSPatrick Mooney 6804c87aefeSPatrick Mooney sc = NULL; 6814c87aefeSPatrick Mooney error = 1; 6824c87aefeSPatrick Mooney 6834c87aefeSPatrick Mooney memflags = vm_get_memflags(ctx); 6844c87aefeSPatrick Mooney if (!(memflags & VM_MEM_F_WIRED)) { 6854c87aefeSPatrick Mooney warnx("passthru requires guest memory to be wired"); 6864c87aefeSPatrick Mooney goto done; 6874c87aefeSPatrick Mooney } 6884c87aefeSPatrick Mooney 689eb9a1df2SHans Rosenfeld if (opts == NULL || passthru_dev_open(opts, &pptfd) != 0) { 6904c87aefeSPatrick Mooney warnx("invalid passthru options"); 6914c87aefeSPatrick Mooney goto done; 6924c87aefeSPatrick Mooney } 6934c87aefeSPatrick Mooney 694eb9a1df2SHans Rosenfeld if (vm_assign_pptdev(ctx, pptfd) != 0) { 695eb9a1df2SHans Rosenfeld warnx("PCI device at %d is not using the ppt driver", pptfd); 6964c87aefeSPatrick Mooney goto done; 6974c87aefeSPatrick Mooney } 6984c87aefeSPatrick Mooney 6994c87aefeSPatrick Mooney sc = calloc(1, sizeof(struct passthru_softc)); 7004c87aefeSPatrick Mooney 7014c87aefeSPatrick Mooney pi->pi_arg = sc; 7024c87aefeSPatrick Mooney sc->psc_pi = pi; 703eb9a1df2SHans Rosenfeld sc->pptfd = pptfd; 704eb9a1df2SHans Rosenfeld 705eb9a1df2SHans Rosenfeld if ((error = vm_get_pptdev_limits(ctx, pptfd, &sc->msi_limit, 706eb9a1df2SHans Rosenfeld &sc->msix_limit)) != 0) 707eb9a1df2SHans Rosenfeld goto done; 7084c87aefeSPatrick Mooney 7094c87aefeSPatrick Mooney /* initialize config space */ 710eb9a1df2SHans Rosenfeld if ((error = cfginit(ctx, sc)) != 0) 7114c87aefeSPatrick Mooney goto done; 7124c87aefeSPatrick Mooney 7134c87aefeSPatrick Mooney error = 0; /* success */ 7144c87aefeSPatrick Mooney done: 7154c87aefeSPatrick Mooney if (error) { 7164c87aefeSPatrick Mooney free(sc); 717eb9a1df2SHans Rosenfeld vm_unassign_pptdev(ctx, pptfd); 7184c87aefeSPatrick Mooney } 7194c87aefeSPatrick Mooney return (error); 7204c87aefeSPatrick Mooney } 7214c87aefeSPatrick Mooney 7224c87aefeSPatrick Mooney static int 7234c87aefeSPatrick Mooney bar_access(int coff) 7244c87aefeSPatrick Mooney { 7254c87aefeSPatrick Mooney if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) 7264c87aefeSPatrick Mooney return (1); 7274c87aefeSPatrick Mooney else 7284c87aefeSPatrick Mooney return (0); 7294c87aefeSPatrick Mooney } 7304c87aefeSPatrick Mooney 7314c87aefeSPatrick Mooney static int 7324c87aefeSPatrick Mooney msicap_access(struct passthru_softc *sc, int coff) 7334c87aefeSPatrick Mooney { 7344c87aefeSPatrick Mooney int caplen; 7354c87aefeSPatrick Mooney 7364c87aefeSPatrick Mooney if (sc->psc_msi.capoff == 0) 7374c87aefeSPatrick Mooney return (0); 7384c87aefeSPatrick Mooney 7394c87aefeSPatrick Mooney caplen = msi_caplen(sc->psc_msi.msgctrl); 7404c87aefeSPatrick Mooney 7414c87aefeSPatrick Mooney if (coff >= sc->psc_msi.capoff && coff < sc->psc_msi.capoff + caplen) 7424c87aefeSPatrick Mooney return (1); 7434c87aefeSPatrick Mooney else 7444c87aefeSPatrick Mooney return (0); 7454c87aefeSPatrick Mooney } 7464c87aefeSPatrick Mooney 7474c87aefeSPatrick Mooney static int 7484c87aefeSPatrick Mooney msixcap_access(struct passthru_softc *sc, int coff) 7494c87aefeSPatrick Mooney { 7504c87aefeSPatrick Mooney if (sc->psc_msix.capoff == 0) 7514c87aefeSPatrick Mooney return (0); 7524c87aefeSPatrick Mooney 7534c87aefeSPatrick Mooney return (coff >= sc->psc_msix.capoff && 7544c87aefeSPatrick Mooney coff < sc->psc_msix.capoff + MSIX_CAPLEN); 7554c87aefeSPatrick Mooney } 7564c87aefeSPatrick Mooney 7574c87aefeSPatrick Mooney static int 7584c87aefeSPatrick Mooney passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 7594c87aefeSPatrick Mooney int coff, int bytes, uint32_t *rv) 7604c87aefeSPatrick Mooney { 7614c87aefeSPatrick Mooney struct passthru_softc *sc; 7624c87aefeSPatrick Mooney 7634c87aefeSPatrick Mooney sc = pi->pi_arg; 7644c87aefeSPatrick Mooney 7654c87aefeSPatrick Mooney /* 7664c87aefeSPatrick Mooney * PCI BARs and MSI capability is emulated. 7674c87aefeSPatrick Mooney */ 7684c87aefeSPatrick Mooney if (bar_access(coff) || msicap_access(sc, coff)) 7694c87aefeSPatrick Mooney return (-1); 7704c87aefeSPatrick Mooney 771eb9a1df2SHans Rosenfeld /* 772eb9a1df2SHans Rosenfeld * MSI-X is also emulated since a limit on interrupts may be imposed by 773eb9a1df2SHans Rosenfeld * the OS, altering the perceived register state. 774eb9a1df2SHans Rosenfeld */ 775eb9a1df2SHans Rosenfeld if (msixcap_access(sc, coff)) 776eb9a1df2SHans Rosenfeld return (-1); 777eb9a1df2SHans Rosenfeld 7784c87aefeSPatrick Mooney #ifdef LEGACY_SUPPORT 7794c87aefeSPatrick Mooney /* 7804c87aefeSPatrick Mooney * Emulate PCIR_CAP_PTR if this device does not support MSI capability 7814c87aefeSPatrick Mooney * natively. 7824c87aefeSPatrick Mooney */ 7834c87aefeSPatrick Mooney if (sc->psc_msi.emulated) { 7844c87aefeSPatrick Mooney if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4) 7854c87aefeSPatrick Mooney return (-1); 7864c87aefeSPatrick Mooney } 7874c87aefeSPatrick Mooney #endif 7884c87aefeSPatrick Mooney 789e4321372SMichael Zeller /* 790e4321372SMichael Zeller * Emulate the command register. If a single read reads both the 791e4321372SMichael Zeller * command and status registers, read the status register from the 792e4321372SMichael Zeller * device's config space. 793e4321372SMichael Zeller */ 794e4321372SMichael Zeller if (coff == PCIR_COMMAND) { 795e4321372SMichael Zeller if (bytes <= 2) 796e4321372SMichael Zeller return (-1); 797e4321372SMichael Zeller *rv = pci_get_cfgdata16(pi, PCIR_COMMAND) << 16 | 798e4321372SMichael Zeller read_config(sc, PCIR_STATUS, 2); 799e4321372SMichael Zeller return (0); 800e4321372SMichael Zeller } 801e4321372SMichael Zeller 8024c87aefeSPatrick Mooney /* Everything else just read from the device's config space */ 803eb9a1df2SHans Rosenfeld *rv = read_config(sc, coff, bytes); 8044c87aefeSPatrick Mooney 8054c87aefeSPatrick Mooney return (0); 8064c87aefeSPatrick Mooney } 8074c87aefeSPatrick Mooney 8084c87aefeSPatrick Mooney static int 8094c87aefeSPatrick Mooney passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 8104c87aefeSPatrick Mooney int coff, int bytes, uint32_t val) 8114c87aefeSPatrick Mooney { 8124c87aefeSPatrick Mooney int error, msix_table_entries, i; 8134c87aefeSPatrick Mooney struct passthru_softc *sc; 814e4321372SMichael Zeller uint16_t cmd_old; 8154c87aefeSPatrick Mooney 8164c87aefeSPatrick Mooney sc = pi->pi_arg; 8174c87aefeSPatrick Mooney 8184c87aefeSPatrick Mooney /* 8194c87aefeSPatrick Mooney * PCI BARs are emulated 8204c87aefeSPatrick Mooney */ 8214c87aefeSPatrick Mooney if (bar_access(coff)) 8224c87aefeSPatrick Mooney return (-1); 8234c87aefeSPatrick Mooney 8244c87aefeSPatrick Mooney /* 8254c87aefeSPatrick Mooney * MSI capability is emulated 8264c87aefeSPatrick Mooney */ 8274c87aefeSPatrick Mooney if (msicap_access(sc, coff)) { 828154972afSPatrick Mooney pci_emul_capwrite(pi, coff, bytes, val, sc->psc_msi.capoff, 829154972afSPatrick Mooney PCIY_MSI); 830eb9a1df2SHans Rosenfeld error = vm_setup_pptdev_msi(ctx, vcpu, sc->pptfd, 831eb9a1df2SHans Rosenfeld pi->pi_msi.addr, pi->pi_msi.msg_data, pi->pi_msi.maxmsgnum); 8324c87aefeSPatrick Mooney if (error != 0) 8334c87aefeSPatrick Mooney err(1, "vm_setup_pptdev_msi"); 8344c87aefeSPatrick Mooney return (0); 8354c87aefeSPatrick Mooney } 8364c87aefeSPatrick Mooney 8374c87aefeSPatrick Mooney if (msixcap_access(sc, coff)) { 838154972afSPatrick Mooney pci_emul_capwrite(pi, coff, bytes, val, sc->psc_msix.capoff, 839154972afSPatrick Mooney PCIY_MSIX); 8404c87aefeSPatrick Mooney if (pi->pi_msix.enabled) { 8414c87aefeSPatrick Mooney msix_table_entries = pi->pi_msix.table_count; 8424c87aefeSPatrick Mooney for (i = 0; i < msix_table_entries; i++) { 8434c87aefeSPatrick Mooney error = vm_setup_pptdev_msix(ctx, vcpu, 844eb9a1df2SHans Rosenfeld sc->pptfd, i, 8454c87aefeSPatrick Mooney pi->pi_msix.table[i].addr, 8464c87aefeSPatrick Mooney pi->pi_msix.table[i].msg_data, 8474c87aefeSPatrick Mooney pi->pi_msix.table[i].vector_control); 8484c87aefeSPatrick Mooney 8494c87aefeSPatrick Mooney if (error) 8504c87aefeSPatrick Mooney err(1, "vm_setup_pptdev_msix"); 8514c87aefeSPatrick Mooney } 852*6960cd89SAndy Fiddaman } else { 853*6960cd89SAndy Fiddaman error = vm_disable_pptdev_msix(ctx, sc->pptfd); 854*6960cd89SAndy Fiddaman if (error) 855*6960cd89SAndy Fiddaman err(1, "vm_disable_pptdev_msix"); 8564c87aefeSPatrick Mooney } 8574c87aefeSPatrick Mooney return (0); 8584c87aefeSPatrick Mooney } 8594c87aefeSPatrick Mooney 8604c87aefeSPatrick Mooney #ifdef LEGACY_SUPPORT 8614c87aefeSPatrick Mooney /* 8624c87aefeSPatrick Mooney * If this device does not support MSI natively then we cannot let 8634c87aefeSPatrick Mooney * the guest disable legacy interrupts from the device. It is the 8644c87aefeSPatrick Mooney * legacy interrupt that is triggering the virtual MSI to the guest. 8654c87aefeSPatrick Mooney */ 8664c87aefeSPatrick Mooney if (sc->psc_msi.emulated && pci_msi_enabled(pi)) { 8674c87aefeSPatrick Mooney if (coff == PCIR_COMMAND && bytes == 2) 8684c87aefeSPatrick Mooney val &= ~PCIM_CMD_INTxDIS; 8694c87aefeSPatrick Mooney } 8704c87aefeSPatrick Mooney #endif 8714c87aefeSPatrick Mooney 872eb9a1df2SHans Rosenfeld write_config(sc, coff, bytes, val); 873e4321372SMichael Zeller if (coff == PCIR_COMMAND) { 874e4321372SMichael Zeller cmd_old = pci_get_cfgdata16(pi, PCIR_COMMAND); 875e4321372SMichael Zeller if (bytes == 1) 876e4321372SMichael Zeller pci_set_cfgdata8(pi, PCIR_COMMAND, val); 877e4321372SMichael Zeller else if (bytes == 2) 878e4321372SMichael Zeller pci_set_cfgdata16(pi, PCIR_COMMAND, val); 879e4321372SMichael Zeller pci_emul_cmd_changed(pi, cmd_old); 880e4321372SMichael Zeller } 8814c87aefeSPatrick Mooney 8824c87aefeSPatrick Mooney return (0); 8834c87aefeSPatrick Mooney } 8844c87aefeSPatrick Mooney 8854c87aefeSPatrick Mooney static void 8864c87aefeSPatrick Mooney passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 8874c87aefeSPatrick Mooney uint64_t offset, int size, uint64_t value) 8884c87aefeSPatrick Mooney { 889eb9a1df2SHans Rosenfeld struct passthru_softc *sc = pi->pi_arg; 8904c87aefeSPatrick Mooney 8914c87aefeSPatrick Mooney if (baridx == pci_msix_table_bar(pi)) { 892eb9a1df2SHans Rosenfeld passthru_msix_table_write(ctx, vcpu, sc, offset, size, value); 8934c87aefeSPatrick Mooney } else { 894eb9a1df2SHans Rosenfeld struct ppt_bar_io pbi; 8954c87aefeSPatrick Mooney 896eb9a1df2SHans Rosenfeld assert(pi->pi_bar[baridx].type == PCIBAR_IO); 897eb9a1df2SHans Rosenfeld 898eb9a1df2SHans Rosenfeld pbi.pbi_bar = baridx; 899eb9a1df2SHans Rosenfeld pbi.pbi_width = size; 900eb9a1df2SHans Rosenfeld pbi.pbi_off = offset; 901eb9a1df2SHans Rosenfeld pbi.pbi_data = value; 902eb9a1df2SHans Rosenfeld (void) ioctl(sc->pptfd, PPT_BAR_WRITE, &pbi); 9034c87aefeSPatrick Mooney } 9044c87aefeSPatrick Mooney } 9054c87aefeSPatrick Mooney 9064c87aefeSPatrick Mooney static uint64_t 9074c87aefeSPatrick Mooney passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 9084c87aefeSPatrick Mooney uint64_t offset, int size) 9094c87aefeSPatrick Mooney { 910eb9a1df2SHans Rosenfeld struct passthru_softc *sc = pi->pi_arg; 9114c87aefeSPatrick Mooney uint64_t val; 9124c87aefeSPatrick Mooney 9134c87aefeSPatrick Mooney if (baridx == pci_msix_table_bar(pi)) { 914eb9a1df2SHans Rosenfeld val = passthru_msix_table_read(sc, offset, size); 9154c87aefeSPatrick Mooney } else { 916eb9a1df2SHans Rosenfeld struct ppt_bar_io pbi; 917eb9a1df2SHans Rosenfeld 9184c87aefeSPatrick Mooney assert(pi->pi_bar[baridx].type == PCIBAR_IO); 9194c87aefeSPatrick Mooney 920eb9a1df2SHans Rosenfeld pbi.pbi_bar = baridx; 921eb9a1df2SHans Rosenfeld pbi.pbi_width = size; 922eb9a1df2SHans Rosenfeld pbi.pbi_off = offset; 923eb9a1df2SHans Rosenfeld if (ioctl(sc->pptfd, PPT_BAR_READ, &pbi) == 0) { 924eb9a1df2SHans Rosenfeld val = pbi.pbi_data; 925eb9a1df2SHans Rosenfeld } else { 926eb9a1df2SHans Rosenfeld val = 0; 927eb9a1df2SHans Rosenfeld } 9284c87aefeSPatrick Mooney } 9294c87aefeSPatrick Mooney 9304c87aefeSPatrick Mooney return (val); 9314c87aefeSPatrick Mooney } 9324c87aefeSPatrick Mooney 9334c87aefeSPatrick Mooney struct pci_devemu passthru = { 9344c87aefeSPatrick Mooney .pe_emu = "passthru", 9354c87aefeSPatrick Mooney .pe_init = passthru_init, 9364c87aefeSPatrick Mooney .pe_cfgwrite = passthru_cfgwrite, 9374c87aefeSPatrick Mooney .pe_cfgread = passthru_cfgread, 9384c87aefeSPatrick Mooney .pe_barwrite = passthru_write, 9394c87aefeSPatrick Mooney .pe_barread = passthru_read, 9404c87aefeSPatrick Mooney }; 9414c87aefeSPatrick Mooney PCI_EMUL_SET(passthru); 942