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> 652b948146SAndy Fiddaman 662b948146SAndy Fiddaman #include "config.h" 672b948146SAndy Fiddaman #include "debug.h" 684c87aefeSPatrick Mooney #include "pci_emul.h" 694c87aefeSPatrick Mooney #include "mem.h" 704c87aefeSPatrick Mooney 714c87aefeSPatrick Mooney #define LEGACY_SUPPORT 1 724c87aefeSPatrick Mooney 734c87aefeSPatrick Mooney #define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1) 744c87aefeSPatrick Mooney #define MSIX_CAPLEN 12 754c87aefeSPatrick Mooney 764c87aefeSPatrick Mooney struct passthru_softc { 774c87aefeSPatrick Mooney struct pci_devinst *psc_pi; 784c87aefeSPatrick Mooney struct pcibar psc_bar[PCI_BARMAX + 1]; 794c87aefeSPatrick Mooney struct { 804c87aefeSPatrick Mooney int capoff; 814c87aefeSPatrick Mooney int msgctrl; 824c87aefeSPatrick Mooney int emulated; 834c87aefeSPatrick Mooney } psc_msi; 844c87aefeSPatrick Mooney struct { 854c87aefeSPatrick Mooney int capoff; 864c87aefeSPatrick Mooney } psc_msix; 87eb9a1df2SHans Rosenfeld int pptfd; 88eb9a1df2SHans Rosenfeld int msi_limit; 89eb9a1df2SHans Rosenfeld int msix_limit; 904c87aefeSPatrick Mooney }; 914c87aefeSPatrick Mooney 924c87aefeSPatrick Mooney static int 934c87aefeSPatrick Mooney msi_caplen(int msgctrl) 944c87aefeSPatrick Mooney { 954c87aefeSPatrick Mooney int len; 964c87aefeSPatrick Mooney 974c87aefeSPatrick Mooney len = 10; /* minimum length of msi capability */ 984c87aefeSPatrick Mooney 994c87aefeSPatrick Mooney if (msgctrl & PCIM_MSICTRL_64BIT) 1004c87aefeSPatrick Mooney len += 4; 1014c87aefeSPatrick Mooney 1024c87aefeSPatrick Mooney #if 0 1034c87aefeSPatrick Mooney /* 1044c87aefeSPatrick Mooney * Ignore the 'mask' and 'pending' bits in the MSI capability. 1054c87aefeSPatrick Mooney * We'll let the guest manipulate them directly. 1064c87aefeSPatrick Mooney */ 1074c87aefeSPatrick Mooney if (msgctrl & PCIM_MSICTRL_VECTOR) 1084c87aefeSPatrick Mooney len += 10; 1094c87aefeSPatrick Mooney #endif 1104c87aefeSPatrick Mooney 1114c87aefeSPatrick Mooney return (len); 1124c87aefeSPatrick Mooney } 1134c87aefeSPatrick Mooney 1144c87aefeSPatrick Mooney static uint32_t 115eb9a1df2SHans Rosenfeld read_config(const struct passthru_softc *sc, long reg, int width) 1164c87aefeSPatrick Mooney { 117eb9a1df2SHans Rosenfeld struct ppt_cfg_io pi; 1184c87aefeSPatrick Mooney 119eb9a1df2SHans Rosenfeld pi.pci_off = reg; 120eb9a1df2SHans Rosenfeld pi.pci_width = width; 1214c87aefeSPatrick Mooney 122eb9a1df2SHans Rosenfeld if (ioctl(sc->pptfd, PPT_CFG_READ, &pi) != 0) { 123eb9a1df2SHans Rosenfeld return (0); 124eb9a1df2SHans Rosenfeld } 125eb9a1df2SHans Rosenfeld return (pi.pci_data); 1264c87aefeSPatrick Mooney } 1274c87aefeSPatrick Mooney 1284c87aefeSPatrick Mooney static void 129eb9a1df2SHans Rosenfeld write_config(const struct passthru_softc *sc, long reg, int width, 130eb9a1df2SHans Rosenfeld uint32_t data) 1314c87aefeSPatrick Mooney { 132eb9a1df2SHans Rosenfeld struct ppt_cfg_io pi; 1334c87aefeSPatrick Mooney 134eb9a1df2SHans Rosenfeld pi.pci_off = reg; 135eb9a1df2SHans Rosenfeld pi.pci_width = width; 136eb9a1df2SHans Rosenfeld pi.pci_data = data; 1374c87aefeSPatrick Mooney 138eb9a1df2SHans Rosenfeld (void) ioctl(sc->pptfd, PPT_CFG_WRITE, &pi); 139eb9a1df2SHans Rosenfeld } 140eb9a1df2SHans Rosenfeld 141eb9a1df2SHans Rosenfeld static int 142eb9a1df2SHans Rosenfeld passthru_get_bar(struct passthru_softc *sc, int bar, enum pcibar_type *type, 143eb9a1df2SHans Rosenfeld uint64_t *base, uint64_t *size) 144eb9a1df2SHans Rosenfeld { 145eb9a1df2SHans Rosenfeld struct ppt_bar_query pb; 146eb9a1df2SHans Rosenfeld 147eb9a1df2SHans Rosenfeld pb.pbq_baridx = bar; 148eb9a1df2SHans Rosenfeld 149eb9a1df2SHans Rosenfeld if (ioctl(sc->pptfd, PPT_BAR_QUERY, &pb) != 0) { 150eb9a1df2SHans Rosenfeld return (-1); 151eb9a1df2SHans Rosenfeld } 152eb9a1df2SHans Rosenfeld 153eb9a1df2SHans Rosenfeld switch (pb.pbq_type) { 154eb9a1df2SHans Rosenfeld case PCI_ADDR_IO: 155eb9a1df2SHans Rosenfeld *type = PCIBAR_IO; 156eb9a1df2SHans Rosenfeld break; 157eb9a1df2SHans Rosenfeld case PCI_ADDR_MEM32: 158eb9a1df2SHans Rosenfeld *type = PCIBAR_MEM32; 159eb9a1df2SHans Rosenfeld break; 160eb9a1df2SHans Rosenfeld case PCI_ADDR_MEM64: 161eb9a1df2SHans Rosenfeld *type = PCIBAR_MEM64; 162eb9a1df2SHans Rosenfeld break; 163eb9a1df2SHans Rosenfeld default: 164eb9a1df2SHans Rosenfeld err(1, "unrecognized BAR type: %u\n", pb.pbq_type); 165eb9a1df2SHans Rosenfeld break; 166eb9a1df2SHans Rosenfeld } 167eb9a1df2SHans Rosenfeld 168eb9a1df2SHans Rosenfeld *base = pb.pbq_base; 169eb9a1df2SHans Rosenfeld *size = pb.pbq_size; 170eb9a1df2SHans Rosenfeld return (0); 171eb9a1df2SHans Rosenfeld } 172eb9a1df2SHans Rosenfeld 173eb9a1df2SHans Rosenfeld static int 174eb9a1df2SHans Rosenfeld passthru_dev_open(const char *path, int *pptfdp) 175eb9a1df2SHans Rosenfeld { 176eb9a1df2SHans Rosenfeld int pptfd; 177eb9a1df2SHans Rosenfeld 178eb9a1df2SHans Rosenfeld if ((pptfd = open(path, O_RDWR)) < 0) { 179eb9a1df2SHans Rosenfeld return (errno); 180eb9a1df2SHans Rosenfeld } 181eb9a1df2SHans Rosenfeld 182eb9a1df2SHans Rosenfeld /* XXX: verify fd with ioctl? */ 183eb9a1df2SHans Rosenfeld *pptfdp = pptfd; 184eb9a1df2SHans Rosenfeld return (0); 1854c87aefeSPatrick Mooney } 1864c87aefeSPatrick Mooney 1874c87aefeSPatrick Mooney #ifdef LEGACY_SUPPORT 1884c87aefeSPatrick Mooney static int 1894c87aefeSPatrick Mooney passthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr) 1904c87aefeSPatrick Mooney { 1914c87aefeSPatrick Mooney int capoff, i; 1924c87aefeSPatrick Mooney struct msicap msicap; 1934c87aefeSPatrick Mooney u_char *capdata; 1944c87aefeSPatrick Mooney 1954c87aefeSPatrick Mooney pci_populate_msicap(&msicap, msgnum, nextptr); 1964c87aefeSPatrick Mooney 1974c87aefeSPatrick Mooney /* 1984c87aefeSPatrick Mooney * XXX 1994c87aefeSPatrick Mooney * Copy the msi capability structure in the last 16 bytes of the 2004c87aefeSPatrick Mooney * config space. This is wrong because it could shadow something 2014c87aefeSPatrick Mooney * useful to the device. 2024c87aefeSPatrick Mooney */ 2034c87aefeSPatrick Mooney capoff = 256 - roundup(sizeof(msicap), 4); 2044c87aefeSPatrick Mooney capdata = (u_char *)&msicap; 2054c87aefeSPatrick Mooney for (i = 0; i < sizeof(msicap); i++) 2064c87aefeSPatrick Mooney pci_set_cfgdata8(pi, capoff + i, capdata[i]); 2074c87aefeSPatrick Mooney 2084c87aefeSPatrick Mooney return (capoff); 2094c87aefeSPatrick Mooney } 2104c87aefeSPatrick Mooney #endif /* LEGACY_SUPPORT */ 2114c87aefeSPatrick Mooney 212eb9a1df2SHans Rosenfeld static void 213eb9a1df2SHans Rosenfeld passthru_intr_limit(struct passthru_softc *sc, struct msixcap *msixcap) 214eb9a1df2SHans Rosenfeld { 215eb9a1df2SHans Rosenfeld struct pci_devinst *pi = sc->psc_pi; 216eb9a1df2SHans Rosenfeld int off; 217eb9a1df2SHans Rosenfeld 218eb9a1df2SHans Rosenfeld /* Reduce the number of MSI vectors if higher than OS limit */ 219eb9a1df2SHans Rosenfeld if ((off = sc->psc_msi.capoff) != 0 && sc->msi_limit != -1) { 220eb9a1df2SHans Rosenfeld int msi_limit, mmc; 221eb9a1df2SHans Rosenfeld 222eb9a1df2SHans Rosenfeld msi_limit = 223eb9a1df2SHans Rosenfeld sc->msi_limit > 16 ? PCIM_MSICTRL_MMC_32 : 224eb9a1df2SHans Rosenfeld sc->msi_limit > 8 ? PCIM_MSICTRL_MMC_16 : 225eb9a1df2SHans Rosenfeld sc->msi_limit > 4 ? PCIM_MSICTRL_MMC_8 : 226eb9a1df2SHans Rosenfeld sc->msi_limit > 2 ? PCIM_MSICTRL_MMC_4 : 227eb9a1df2SHans Rosenfeld sc->msi_limit > 1 ? PCIM_MSICTRL_MMC_2 : 228eb9a1df2SHans Rosenfeld PCIM_MSICTRL_MMC_1; 229eb9a1df2SHans Rosenfeld mmc = sc->psc_msi.msgctrl & PCIM_MSICTRL_MMC_MASK; 230eb9a1df2SHans Rosenfeld 231eb9a1df2SHans Rosenfeld if (mmc > msi_limit) { 232eb9a1df2SHans Rosenfeld sc->psc_msi.msgctrl &= ~PCIM_MSICTRL_MMC_MASK; 233eb9a1df2SHans Rosenfeld sc->psc_msi.msgctrl |= msi_limit; 234eb9a1df2SHans Rosenfeld pci_set_cfgdata16(pi, off + 2, sc->psc_msi.msgctrl); 235eb9a1df2SHans Rosenfeld } 236eb9a1df2SHans Rosenfeld } 237eb9a1df2SHans Rosenfeld 238eb9a1df2SHans Rosenfeld /* Reduce the number of MSI-X vectors if higher than OS limit */ 239eb9a1df2SHans Rosenfeld if ((off = sc->psc_msix.capoff) != 0 && sc->msix_limit != -1) { 240eb9a1df2SHans Rosenfeld if (MSIX_TABLE_COUNT(msixcap->msgctrl) > sc->msix_limit) { 241eb9a1df2SHans Rosenfeld msixcap->msgctrl &= ~PCIM_MSIXCTRL_TABLE_SIZE; 242eb9a1df2SHans Rosenfeld msixcap->msgctrl |= sc->msix_limit - 1; 243eb9a1df2SHans Rosenfeld pci_set_cfgdata16(pi, off + 2, msixcap->msgctrl); 244eb9a1df2SHans Rosenfeld } 245eb9a1df2SHans Rosenfeld } 246eb9a1df2SHans Rosenfeld } 247eb9a1df2SHans Rosenfeld 2484c87aefeSPatrick Mooney static int 2494c87aefeSPatrick Mooney cfginitmsi(struct passthru_softc *sc) 2504c87aefeSPatrick Mooney { 2514c87aefeSPatrick Mooney int i, ptr, capptr, cap, sts, caplen, table_size; 2524c87aefeSPatrick Mooney uint32_t u32; 253eb9a1df2SHans Rosenfeld struct pci_devinst *pi = sc->psc_pi; 2544c87aefeSPatrick Mooney struct msixcap msixcap; 2554c87aefeSPatrick Mooney uint32_t *msixcap_ptr; 2564c87aefeSPatrick Mooney 2574c87aefeSPatrick Mooney /* 2584c87aefeSPatrick Mooney * Parse the capabilities and cache the location of the MSI 2594c87aefeSPatrick Mooney * and MSI-X capabilities. 2604c87aefeSPatrick Mooney */ 261eb9a1df2SHans Rosenfeld sts = read_config(sc, PCIR_STATUS, 2); 2624c87aefeSPatrick Mooney if (sts & PCIM_STATUS_CAPPRESENT) { 263eb9a1df2SHans Rosenfeld ptr = read_config(sc, PCIR_CAP_PTR, 1); 2644c87aefeSPatrick Mooney while (ptr != 0 && ptr != 0xff) { 265eb9a1df2SHans Rosenfeld cap = read_config(sc, ptr + PCICAP_ID, 1); 2664c87aefeSPatrick Mooney if (cap == PCIY_MSI) { 2674c87aefeSPatrick Mooney /* 2684c87aefeSPatrick Mooney * Copy the MSI capability into the config 2694c87aefeSPatrick Mooney * space of the emulated pci device 2704c87aefeSPatrick Mooney */ 2714c87aefeSPatrick Mooney sc->psc_msi.capoff = ptr; 272eb9a1df2SHans Rosenfeld sc->psc_msi.msgctrl = read_config(sc, 2734c87aefeSPatrick Mooney ptr + 2, 2); 2744c87aefeSPatrick Mooney sc->psc_msi.emulated = 0; 2754c87aefeSPatrick Mooney caplen = msi_caplen(sc->psc_msi.msgctrl); 2764c87aefeSPatrick Mooney capptr = ptr; 2774c87aefeSPatrick Mooney while (caplen > 0) { 278eb9a1df2SHans Rosenfeld u32 = read_config(sc, capptr, 4); 2794c87aefeSPatrick Mooney pci_set_cfgdata32(pi, capptr, u32); 2804c87aefeSPatrick Mooney caplen -= 4; 2814c87aefeSPatrick Mooney capptr += 4; 2824c87aefeSPatrick Mooney } 2834c87aefeSPatrick Mooney } else if (cap == PCIY_MSIX) { 2844c87aefeSPatrick Mooney /* 2854c87aefeSPatrick Mooney * Copy the MSI-X capability 2864c87aefeSPatrick Mooney */ 2874c87aefeSPatrick Mooney sc->psc_msix.capoff = ptr; 2884c87aefeSPatrick Mooney caplen = 12; 2894c87aefeSPatrick Mooney msixcap_ptr = (uint32_t*) &msixcap; 2904c87aefeSPatrick Mooney capptr = ptr; 2914c87aefeSPatrick Mooney while (caplen > 0) { 292eb9a1df2SHans Rosenfeld u32 = read_config(sc, capptr, 4); 2934c87aefeSPatrick Mooney *msixcap_ptr = u32; 2944c87aefeSPatrick Mooney pci_set_cfgdata32(pi, capptr, u32); 2954c87aefeSPatrick Mooney caplen -= 4; 2964c87aefeSPatrick Mooney capptr += 4; 2974c87aefeSPatrick Mooney msixcap_ptr++; 2984c87aefeSPatrick Mooney } 2994c87aefeSPatrick Mooney } 300eb9a1df2SHans Rosenfeld ptr = read_config(sc, ptr + PCICAP_NEXTPTR, 1); 3014c87aefeSPatrick Mooney } 3024c87aefeSPatrick Mooney } 3034c87aefeSPatrick Mooney 304eb9a1df2SHans Rosenfeld passthru_intr_limit(sc, &msixcap); 305eb9a1df2SHans Rosenfeld 3064c87aefeSPatrick Mooney if (sc->psc_msix.capoff != 0) { 3074c87aefeSPatrick Mooney pi->pi_msix.pba_bar = 3084c87aefeSPatrick Mooney msixcap.pba_info & PCIM_MSIX_BIR_MASK; 3094c87aefeSPatrick Mooney pi->pi_msix.pba_offset = 3104c87aefeSPatrick Mooney msixcap.pba_info & ~PCIM_MSIX_BIR_MASK; 3114c87aefeSPatrick Mooney pi->pi_msix.table_bar = 3124c87aefeSPatrick Mooney msixcap.table_info & PCIM_MSIX_BIR_MASK; 3134c87aefeSPatrick Mooney pi->pi_msix.table_offset = 3144c87aefeSPatrick Mooney msixcap.table_info & ~PCIM_MSIX_BIR_MASK; 3154c87aefeSPatrick Mooney pi->pi_msix.table_count = MSIX_TABLE_COUNT(msixcap.msgctrl); 3164c87aefeSPatrick Mooney pi->pi_msix.pba_size = PBA_SIZE(pi->pi_msix.table_count); 3174c87aefeSPatrick Mooney 3184c87aefeSPatrick Mooney /* Allocate the emulated MSI-X table array */ 3194c87aefeSPatrick Mooney table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; 3204c87aefeSPatrick Mooney pi->pi_msix.table = calloc(1, table_size); 3214c87aefeSPatrick Mooney 3224c87aefeSPatrick Mooney /* Mask all table entries */ 3234c87aefeSPatrick Mooney for (i = 0; i < pi->pi_msix.table_count; i++) { 3244c87aefeSPatrick Mooney pi->pi_msix.table[i].vector_control |= 3254c87aefeSPatrick Mooney PCIM_MSIX_VCTRL_MASK; 3264c87aefeSPatrick Mooney } 3274c87aefeSPatrick Mooney } 3284c87aefeSPatrick Mooney 3294c87aefeSPatrick Mooney #ifdef LEGACY_SUPPORT 3304c87aefeSPatrick Mooney /* 3314c87aefeSPatrick Mooney * If the passthrough device does not support MSI then craft a 3324c87aefeSPatrick Mooney * MSI capability for it. We link the new MSI capability at the 3334c87aefeSPatrick Mooney * head of the list of capabilities. 3344c87aefeSPatrick Mooney */ 3354c87aefeSPatrick Mooney if ((sts & PCIM_STATUS_CAPPRESENT) != 0 && sc->psc_msi.capoff == 0) { 3364c87aefeSPatrick Mooney int origptr, msiptr; 337eb9a1df2SHans Rosenfeld origptr = read_config(sc, PCIR_CAP_PTR, 1); 3384c87aefeSPatrick Mooney msiptr = passthru_add_msicap(pi, 1, origptr); 3394c87aefeSPatrick Mooney sc->psc_msi.capoff = msiptr; 3404c87aefeSPatrick Mooney sc->psc_msi.msgctrl = pci_get_cfgdata16(pi, msiptr + 2); 3414c87aefeSPatrick Mooney sc->psc_msi.emulated = 1; 3424c87aefeSPatrick Mooney pci_set_cfgdata8(pi, PCIR_CAP_PTR, msiptr); 3434c87aefeSPatrick Mooney } 3444c87aefeSPatrick Mooney #endif 3454c87aefeSPatrick Mooney 3464c87aefeSPatrick Mooney /* Make sure one of the capabilities is present */ 347*6dc98349SAndy Fiddaman if (sc->psc_msi.capoff == 0 && sc->psc_msix.capoff == 0) 3484c87aefeSPatrick Mooney return (-1); 349*6dc98349SAndy Fiddaman else 3504c87aefeSPatrick Mooney return (0); 3514c87aefeSPatrick Mooney } 3524c87aefeSPatrick Mooney 3534c87aefeSPatrick Mooney static uint64_t 354*6dc98349SAndy Fiddaman msix_table_read(struct passthru_softc *sc, uint64_t offset, int size) 3554c87aefeSPatrick Mooney { 3564c87aefeSPatrick Mooney struct pci_devinst *pi; 3574c87aefeSPatrick Mooney struct msix_table_entry *entry; 3584c87aefeSPatrick Mooney uint8_t *src8; 3594c87aefeSPatrick Mooney uint16_t *src16; 3604c87aefeSPatrick Mooney uint32_t *src32; 3614c87aefeSPatrick Mooney uint64_t *src64; 3624c87aefeSPatrick Mooney uint64_t data; 3634c87aefeSPatrick Mooney size_t entry_offset; 364*6dc98349SAndy Fiddaman uint32_t table_offset; 365*6dc98349SAndy Fiddaman int index, table_count; 3664c87aefeSPatrick Mooney 3674c87aefeSPatrick Mooney pi = sc->psc_pi; 368*6dc98349SAndy Fiddaman 369*6dc98349SAndy Fiddaman table_offset = pi->pi_msix.table_offset; 370*6dc98349SAndy Fiddaman table_count = pi->pi_msix.table_count; 371*6dc98349SAndy Fiddaman if (offset < table_offset || 372*6dc98349SAndy Fiddaman offset >= table_offset + table_count * MSIX_TABLE_ENTRY_SIZE) { 3734c87aefeSPatrick Mooney switch (size) { 3744c87aefeSPatrick Mooney case 1: 375*6dc98349SAndy Fiddaman src8 = (uint8_t *)(pi->pi_msix.mapped_addr + offset); 3764c87aefeSPatrick Mooney data = *src8; 3774c87aefeSPatrick Mooney break; 3784c87aefeSPatrick Mooney case 2: 379*6dc98349SAndy Fiddaman src16 = (uint16_t *)(pi->pi_msix.mapped_addr + offset); 3804c87aefeSPatrick Mooney data = *src16; 3814c87aefeSPatrick Mooney break; 3824c87aefeSPatrick Mooney case 4: 383*6dc98349SAndy Fiddaman src32 = (uint32_t *)(pi->pi_msix.mapped_addr + offset); 3844c87aefeSPatrick Mooney data = *src32; 3854c87aefeSPatrick Mooney break; 3864c87aefeSPatrick Mooney case 8: 387*6dc98349SAndy Fiddaman src64 = (uint64_t *)(pi->pi_msix.mapped_addr + offset); 3884c87aefeSPatrick Mooney data = *src64; 3894c87aefeSPatrick Mooney break; 3904c87aefeSPatrick Mooney default: 3914c87aefeSPatrick Mooney return (-1); 3924c87aefeSPatrick Mooney } 3934c87aefeSPatrick Mooney return (data); 3944c87aefeSPatrick Mooney } 3954c87aefeSPatrick Mooney 396*6dc98349SAndy Fiddaman offset -= table_offset; 3974c87aefeSPatrick Mooney index = offset / MSIX_TABLE_ENTRY_SIZE; 398*6dc98349SAndy Fiddaman assert(index < table_count); 3994c87aefeSPatrick Mooney 4004c87aefeSPatrick Mooney entry = &pi->pi_msix.table[index]; 4014c87aefeSPatrick Mooney entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 4024c87aefeSPatrick Mooney 4034c87aefeSPatrick Mooney switch (size) { 4044c87aefeSPatrick Mooney case 1: 405*6dc98349SAndy Fiddaman src8 = (uint8_t *)((uint8_t *)entry + entry_offset); 4064c87aefeSPatrick Mooney data = *src8; 4074c87aefeSPatrick Mooney break; 4084c87aefeSPatrick Mooney case 2: 409*6dc98349SAndy Fiddaman src16 = (uint16_t *)((uint8_t *)entry + entry_offset); 4104c87aefeSPatrick Mooney data = *src16; 4114c87aefeSPatrick Mooney break; 4124c87aefeSPatrick Mooney case 4: 413*6dc98349SAndy Fiddaman src32 = (uint32_t *)((uint8_t *)entry + entry_offset); 4144c87aefeSPatrick Mooney data = *src32; 4154c87aefeSPatrick Mooney break; 4164c87aefeSPatrick Mooney case 8: 417*6dc98349SAndy Fiddaman src64 = (uint64_t *)((uint8_t *)entry + entry_offset); 4184c87aefeSPatrick Mooney data = *src64; 4194c87aefeSPatrick Mooney break; 4204c87aefeSPatrick Mooney default: 4214c87aefeSPatrick Mooney return (-1); 4224c87aefeSPatrick Mooney } 4234c87aefeSPatrick Mooney 4244c87aefeSPatrick Mooney return (data); 4254c87aefeSPatrick Mooney } 4264c87aefeSPatrick Mooney 4274c87aefeSPatrick Mooney static void 428*6dc98349SAndy Fiddaman msix_table_write(struct vmctx *ctx, int vcpu, struct passthru_softc *sc, 429*6dc98349SAndy Fiddaman uint64_t offset, int size, uint64_t data) 4304c87aefeSPatrick Mooney { 4314c87aefeSPatrick Mooney struct pci_devinst *pi; 4324c87aefeSPatrick Mooney struct msix_table_entry *entry; 4334c87aefeSPatrick Mooney uint8_t *dest8; 4344c87aefeSPatrick Mooney uint16_t *dest16; 4354c87aefeSPatrick Mooney uint32_t *dest32; 4364c87aefeSPatrick Mooney uint64_t *dest64; 4374c87aefeSPatrick Mooney size_t entry_offset; 438*6dc98349SAndy Fiddaman uint32_t table_offset, vector_control; 439*6dc98349SAndy Fiddaman int index, table_count; 4404c87aefeSPatrick Mooney 4414c87aefeSPatrick Mooney pi = sc->psc_pi; 442*6dc98349SAndy Fiddaman 443*6dc98349SAndy Fiddaman table_offset = pi->pi_msix.table_offset; 444*6dc98349SAndy Fiddaman table_count = pi->pi_msix.table_count; 445*6dc98349SAndy Fiddaman if (offset < table_offset || 446*6dc98349SAndy Fiddaman offset >= table_offset + table_count * MSIX_TABLE_ENTRY_SIZE) { 4474c87aefeSPatrick Mooney switch (size) { 4484c87aefeSPatrick Mooney case 1: 449*6dc98349SAndy Fiddaman dest8 = (uint8_t *)(pi->pi_msix.mapped_addr + offset); 4504c87aefeSPatrick Mooney *dest8 = data; 4514c87aefeSPatrick Mooney break; 4524c87aefeSPatrick Mooney case 2: 453*6dc98349SAndy Fiddaman dest16 = (uint16_t *)(pi->pi_msix.mapped_addr + offset); 4544c87aefeSPatrick Mooney *dest16 = data; 4554c87aefeSPatrick Mooney break; 4564c87aefeSPatrick Mooney case 4: 457*6dc98349SAndy Fiddaman dest32 = (uint32_t *)(pi->pi_msix.mapped_addr + offset); 4584c87aefeSPatrick Mooney *dest32 = data; 4594c87aefeSPatrick Mooney break; 4604c87aefeSPatrick Mooney case 8: 461*6dc98349SAndy Fiddaman dest64 = (uint64_t *)(pi->pi_msix.mapped_addr + offset); 4624c87aefeSPatrick Mooney *dest64 = data; 4634c87aefeSPatrick Mooney break; 4644c87aefeSPatrick Mooney } 4654c87aefeSPatrick Mooney return; 4664c87aefeSPatrick Mooney } 4674c87aefeSPatrick Mooney 468*6dc98349SAndy Fiddaman offset -= table_offset; 4694c87aefeSPatrick Mooney index = offset / MSIX_TABLE_ENTRY_SIZE; 470*6dc98349SAndy Fiddaman assert(index < table_count); 4714c87aefeSPatrick Mooney 4724c87aefeSPatrick Mooney entry = &pi->pi_msix.table[index]; 4734c87aefeSPatrick Mooney entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 4744c87aefeSPatrick Mooney 4754c87aefeSPatrick Mooney /* Only 4 byte naturally-aligned writes are supported */ 4764c87aefeSPatrick Mooney assert(size == 4); 4774c87aefeSPatrick Mooney assert(entry_offset % 4 == 0); 4784c87aefeSPatrick Mooney 4794c87aefeSPatrick Mooney vector_control = entry->vector_control; 4804c87aefeSPatrick Mooney dest32 = (uint32_t *)((void *)entry + entry_offset); 4814c87aefeSPatrick Mooney *dest32 = data; 4824c87aefeSPatrick Mooney /* If MSI-X hasn't been enabled, do nothing */ 4834c87aefeSPatrick Mooney if (pi->pi_msix.enabled) { 4844c87aefeSPatrick Mooney /* If the entry is masked, don't set it up */ 4854c87aefeSPatrick Mooney if ((entry->vector_control & PCIM_MSIX_VCTRL_MASK) == 0 || 4864c87aefeSPatrick Mooney (vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 487eb9a1df2SHans Rosenfeld (void) vm_setup_pptdev_msix(ctx, vcpu, sc->pptfd, 488eb9a1df2SHans Rosenfeld index, entry->addr, entry->msg_data, 489eb9a1df2SHans Rosenfeld entry->vector_control); 4904c87aefeSPatrick Mooney } 4914c87aefeSPatrick Mooney } 4924c87aefeSPatrick Mooney } 4934c87aefeSPatrick Mooney 4944c87aefeSPatrick Mooney static int 495*6dc98349SAndy Fiddaman init_msix_table(struct vmctx *ctx, struct passthru_softc *sc) 4964c87aefeSPatrick Mooney { 4974c87aefeSPatrick Mooney struct pci_devinst *pi = sc->psc_pi; 498*6dc98349SAndy Fiddaman uint32_t table_size, table_offset; 499*6dc98349SAndy Fiddaman int i; 5004c87aefeSPatrick Mooney 501*6dc98349SAndy Fiddaman i = pci_msix_table_bar(pi); 502*6dc98349SAndy Fiddaman assert(i >= 0); 5034c87aefeSPatrick Mooney 5044c87aefeSPatrick Mooney /* 505*6dc98349SAndy Fiddaman * Map the region of the BAR containing the MSI-X table. This is 506*6dc98349SAndy Fiddaman * necessary for two reasons: 507*6dc98349SAndy Fiddaman * 1. The PBA may reside in the first or last page containing the MSI-X 508*6dc98349SAndy Fiddaman * table. 509*6dc98349SAndy Fiddaman * 2. While PCI devices are not supposed to use the page(s) containing 510*6dc98349SAndy Fiddaman * the MSI-X table for other purposes, some do in practice. 5114c87aefeSPatrick Mooney */ 512*6dc98349SAndy Fiddaman 513*6dc98349SAndy Fiddaman /* 514*6dc98349SAndy Fiddaman * Mapping pptfd provides access to the BAR containing the MSI-X 515*6dc98349SAndy Fiddaman * table. See ppt_devmap() in usr/src/uts/i86pc/io/vmm/io/ppt.c 516*6dc98349SAndy Fiddaman * 517*6dc98349SAndy Fiddaman * This maps the whole BAR and then mprotect(PROT_NONE) is used below 518*6dc98349SAndy Fiddaman * to prevent access to pages that don't contain the MSI-X table. 519*6dc98349SAndy Fiddaman * When porting this, it was tempting to just map the MSI-X table pages 520*6dc98349SAndy Fiddaman * but that would mean updating everywhere that assumes that 521*6dc98349SAndy Fiddaman * pi->pi_msix.mapped_addr points to the start of the BAR. For now, 522*6dc98349SAndy Fiddaman * keep closer to upstream. 523*6dc98349SAndy Fiddaman */ 524*6dc98349SAndy Fiddaman pi->pi_msix.mapped_size = sc->psc_bar[i].size; 525*6dc98349SAndy Fiddaman pi->pi_msix.mapped_addr = (uint8_t *)mmap(NULL, pi->pi_msix.mapped_size, 526*6dc98349SAndy Fiddaman PROT_READ | PROT_WRITE, MAP_SHARED, sc->pptfd, 0); 527*6dc98349SAndy Fiddaman if (pi->pi_msix.mapped_addr == MAP_FAILED) { 528*6dc98349SAndy Fiddaman warn("Failed to map MSI-X table BAR on %d", sc->pptfd); 529*6dc98349SAndy Fiddaman return (-1); 530*6dc98349SAndy Fiddaman } 531*6dc98349SAndy Fiddaman 5324c87aefeSPatrick Mooney table_offset = rounddown2(pi->pi_msix.table_offset, 4096); 5334c87aefeSPatrick Mooney 5344c87aefeSPatrick Mooney table_size = pi->pi_msix.table_offset - table_offset; 5354c87aefeSPatrick Mooney table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; 5364c87aefeSPatrick Mooney table_size = roundup2(table_size, 4096); 5374c87aefeSPatrick Mooney 5384c87aefeSPatrick Mooney /* 539*6dc98349SAndy Fiddaman * Unmap any pages not containing the table, we do not need to emulate 540*6dc98349SAndy Fiddaman * accesses to them. Avoid releasing address space to help ensure that 541*6dc98349SAndy Fiddaman * a buggy out-of-bounds access causes a crash. 5424c87aefeSPatrick Mooney */ 543*6dc98349SAndy Fiddaman if (table_offset != 0) 544*6dc98349SAndy Fiddaman if (mprotect((caddr_t)pi->pi_msix.mapped_addr, table_offset, 545*6dc98349SAndy Fiddaman PROT_NONE) != 0) 546*6dc98349SAndy Fiddaman warn("Failed to unmap MSI-X table BAR region"); 547*6dc98349SAndy Fiddaman if (table_offset + table_size != pi->pi_msix.mapped_size) 548*6dc98349SAndy Fiddaman if (mprotect((caddr_t) 549*6dc98349SAndy Fiddaman pi->pi_msix.mapped_addr + table_offset + table_size, 550*6dc98349SAndy Fiddaman pi->pi_msix.mapped_size - (table_offset + table_size), 551*6dc98349SAndy Fiddaman PROT_NONE) != 0) 552*6dc98349SAndy Fiddaman warn("Failed to unmap MSI-X table BAR region"); 5534c87aefeSPatrick Mooney 5544c87aefeSPatrick Mooney return (0); 5554c87aefeSPatrick Mooney } 5564c87aefeSPatrick Mooney 5574c87aefeSPatrick Mooney static int 5584c87aefeSPatrick Mooney cfginitbar(struct vmctx *ctx, struct passthru_softc *sc) 5594c87aefeSPatrick Mooney { 560eb9a1df2SHans Rosenfeld struct pci_devinst *pi = sc->psc_pi; 561eb9a1df2SHans Rosenfeld uint_t i; 5624c87aefeSPatrick Mooney 5634c87aefeSPatrick Mooney /* 5644c87aefeSPatrick Mooney * Initialize BAR registers 5654c87aefeSPatrick Mooney */ 5664c87aefeSPatrick Mooney for (i = 0; i <= PCI_BARMAX; i++) { 567eb9a1df2SHans Rosenfeld enum pcibar_type bartype; 568eb9a1df2SHans Rosenfeld uint64_t base, size; 569eb9a1df2SHans Rosenfeld int error; 5704c87aefeSPatrick Mooney 571eb9a1df2SHans Rosenfeld if (passthru_get_bar(sc, i, &bartype, &base, &size) != 0) { 5724c87aefeSPatrick Mooney continue; 5734c87aefeSPatrick Mooney } 5744c87aefeSPatrick Mooney 5754c87aefeSPatrick Mooney if (bartype != PCIBAR_IO) { 5764c87aefeSPatrick Mooney if (((base | size) & PAGE_MASK) != 0) { 577eb9a1df2SHans Rosenfeld warnx("passthru device %d BAR %d: " 5784c87aefeSPatrick Mooney "base %#lx or size %#lx not page aligned\n", 579eb9a1df2SHans Rosenfeld sc->pptfd, i, base, size); 5804c87aefeSPatrick Mooney return (-1); 5814c87aefeSPatrick Mooney } 5824c87aefeSPatrick Mooney } 5834c87aefeSPatrick Mooney 5844c87aefeSPatrick Mooney /* Cache information about the "real" BAR */ 5854c87aefeSPatrick Mooney sc->psc_bar[i].type = bartype; 5864c87aefeSPatrick Mooney sc->psc_bar[i].size = size; 5874c87aefeSPatrick Mooney sc->psc_bar[i].addr = base; 588*6dc98349SAndy Fiddaman sc->psc_bar[i].lobits = 0; 5894c87aefeSPatrick Mooney 5904c87aefeSPatrick Mooney /* Allocate the BAR in the guest I/O or MMIO space */ 5916960cd89SAndy Fiddaman error = pci_emul_alloc_bar(pi, i, bartype, size); 5924c87aefeSPatrick Mooney if (error) 5934c87aefeSPatrick Mooney return (-1); 5944c87aefeSPatrick Mooney 595*6dc98349SAndy Fiddaman /* Use same lobits as physical bar */ 596*6dc98349SAndy Fiddaman uint8_t lobits = read_config(sc, PCIR_BAR(i), 0x01); 597*6dc98349SAndy Fiddaman if (bartype == PCIBAR_MEM32 || bartype == PCIBAR_MEM64) { 598*6dc98349SAndy Fiddaman lobits &= ~PCIM_BAR_MEM_BASE; 599*6dc98349SAndy Fiddaman } else { 600*6dc98349SAndy Fiddaman lobits &= ~PCIM_BAR_IO_BASE; 6014c87aefeSPatrick Mooney } 602*6dc98349SAndy Fiddaman sc->psc_bar[i].lobits = lobits; 603*6dc98349SAndy Fiddaman pi->pi_bar[i].lobits = lobits; 6044c87aefeSPatrick Mooney 6054c87aefeSPatrick Mooney /* 6064c87aefeSPatrick Mooney * 64-bit BAR takes up two slots so skip the next one. 6074c87aefeSPatrick Mooney */ 6084c87aefeSPatrick Mooney if (bartype == PCIBAR_MEM64) { 6094c87aefeSPatrick Mooney i++; 6104c87aefeSPatrick Mooney assert(i <= PCI_BARMAX); 6114c87aefeSPatrick Mooney sc->psc_bar[i].type = PCIBAR_MEMHI64; 6124c87aefeSPatrick Mooney } 6134c87aefeSPatrick Mooney } 6144c87aefeSPatrick Mooney return (0); 6154c87aefeSPatrick Mooney } 6164c87aefeSPatrick Mooney 6174c87aefeSPatrick Mooney static int 618eb9a1df2SHans Rosenfeld cfginit(struct vmctx *ctx, struct passthru_softc *sc) 6194c87aefeSPatrick Mooney { 620e4321372SMichael Zeller struct pci_devinst *pi = sc->psc_pi; 621*6dc98349SAndy Fiddaman int error; 622e4321372SMichael Zeller 6234c87aefeSPatrick Mooney if (cfginitmsi(sc) != 0) { 624eb9a1df2SHans Rosenfeld warnx("failed to initialize MSI for PCI %d", sc->pptfd); 625eb9a1df2SHans Rosenfeld return (-1); 6264c87aefeSPatrick Mooney } 6274c87aefeSPatrick Mooney 6284c87aefeSPatrick Mooney if (cfginitbar(ctx, sc) != 0) { 629eb9a1df2SHans Rosenfeld warnx("failed to initialize BARs for PCI %d", sc->pptfd); 630eb9a1df2SHans Rosenfeld return (-1); 6314c87aefeSPatrick Mooney } 6324c87aefeSPatrick Mooney 633*6dc98349SAndy Fiddaman write_config(sc, PCIR_COMMAND, 2, pci_get_cfgdata16(pi, PCIR_COMMAND)); 634e4321372SMichael Zeller 635*6dc98349SAndy Fiddaman /* 636*6dc98349SAndy Fiddaman * We need to do this after PCIR_COMMAND got possibly updated, e.g., 637*6dc98349SAndy Fiddaman * a BAR was enabled. 638*6dc98349SAndy Fiddaman */ 639*6dc98349SAndy Fiddaman if (pci_msix_table_bar(pi) >= 0) { 640*6dc98349SAndy Fiddaman error = init_msix_table(ctx, sc); 641*6dc98349SAndy Fiddaman if (error != 0) { 642*6dc98349SAndy Fiddaman warnx("failed to initialize MSI-X table for PCI %d", 643*6dc98349SAndy Fiddaman sc->pptfd); 644*6dc98349SAndy Fiddaman goto done; 645*6dc98349SAndy Fiddaman } 646*6dc98349SAndy Fiddaman } 647*6dc98349SAndy Fiddaman 648*6dc98349SAndy Fiddaman error = 0; /* success */ 649*6dc98349SAndy Fiddaman done: 650*6dc98349SAndy Fiddaman return (error); 6514c87aefeSPatrick Mooney } 6524c87aefeSPatrick Mooney 6534c87aefeSPatrick Mooney static int 6542b948146SAndy Fiddaman passthru_legacy_config(nvlist_t *nvl, const char *opts) 6552b948146SAndy Fiddaman { 6562b948146SAndy Fiddaman if (opts == NULL) 6572b948146SAndy Fiddaman return (0); 6582b948146SAndy Fiddaman 6592b948146SAndy Fiddaman if (strncmp(opts, "/dev/ppt", 8) == 0) 6602b948146SAndy Fiddaman set_config_value_node(nvl, "path", opts); 6612b948146SAndy Fiddaman 6622b948146SAndy Fiddaman return (0); 6632b948146SAndy Fiddaman } 6642b948146SAndy Fiddaman 6652b948146SAndy Fiddaman static int 6662b948146SAndy Fiddaman passthru_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) 6674c87aefeSPatrick Mooney { 668eb9a1df2SHans Rosenfeld int error, memflags, pptfd; 6694c87aefeSPatrick Mooney struct passthru_softc *sc; 6702b948146SAndy Fiddaman const char *path; 6714c87aefeSPatrick Mooney 672a73f8412SToomas Soome pptfd = -1; 6734c87aefeSPatrick Mooney sc = NULL; 6744c87aefeSPatrick Mooney error = 1; 6754c87aefeSPatrick Mooney 6764c87aefeSPatrick Mooney memflags = vm_get_memflags(ctx); 6774c87aefeSPatrick Mooney if (!(memflags & VM_MEM_F_WIRED)) { 6784c87aefeSPatrick Mooney warnx("passthru requires guest memory to be wired"); 6794c87aefeSPatrick Mooney goto done; 6804c87aefeSPatrick Mooney } 6814c87aefeSPatrick Mooney 6822b948146SAndy Fiddaman path = get_config_value_node(nvl, "path"); 6832b948146SAndy Fiddaman if (path == NULL || passthru_dev_open(path, &pptfd) != 0) { 6844c87aefeSPatrick Mooney warnx("invalid passthru options"); 6854c87aefeSPatrick Mooney goto done; 6864c87aefeSPatrick Mooney } 6874c87aefeSPatrick Mooney 688eb9a1df2SHans Rosenfeld if (vm_assign_pptdev(ctx, pptfd) != 0) { 689eb9a1df2SHans Rosenfeld warnx("PCI device at %d is not using the ppt driver", pptfd); 6904c87aefeSPatrick Mooney goto done; 6914c87aefeSPatrick Mooney } 6924c87aefeSPatrick Mooney 6934c87aefeSPatrick Mooney sc = calloc(1, sizeof(struct passthru_softc)); 6944c87aefeSPatrick Mooney 6954c87aefeSPatrick Mooney pi->pi_arg = sc; 6964c87aefeSPatrick Mooney sc->psc_pi = pi; 697eb9a1df2SHans Rosenfeld sc->pptfd = pptfd; 698eb9a1df2SHans Rosenfeld 699eb9a1df2SHans Rosenfeld if ((error = vm_get_pptdev_limits(ctx, pptfd, &sc->msi_limit, 700eb9a1df2SHans Rosenfeld &sc->msix_limit)) != 0) 701eb9a1df2SHans Rosenfeld goto done; 7024c87aefeSPatrick Mooney 7034c87aefeSPatrick Mooney /* initialize config space */ 704*6dc98349SAndy Fiddaman error = cfginit(ctx, sc); 7054c87aefeSPatrick Mooney done: 7064c87aefeSPatrick Mooney if (error) { 7074c87aefeSPatrick Mooney free(sc); 708a73f8412SToomas Soome if (pptfd != -1) 709eb9a1df2SHans Rosenfeld vm_unassign_pptdev(ctx, pptfd); 7104c87aefeSPatrick Mooney } 7114c87aefeSPatrick Mooney return (error); 7124c87aefeSPatrick Mooney } 7134c87aefeSPatrick Mooney 7144c87aefeSPatrick Mooney static int 7154c87aefeSPatrick Mooney bar_access(int coff) 7164c87aefeSPatrick Mooney { 7174c87aefeSPatrick Mooney if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) 7184c87aefeSPatrick Mooney return (1); 7194c87aefeSPatrick Mooney else 7204c87aefeSPatrick Mooney return (0); 7214c87aefeSPatrick Mooney } 7224c87aefeSPatrick Mooney 7234c87aefeSPatrick Mooney static int 7244c87aefeSPatrick Mooney msicap_access(struct passthru_softc *sc, int coff) 7254c87aefeSPatrick Mooney { 7264c87aefeSPatrick Mooney int caplen; 7274c87aefeSPatrick Mooney 7284c87aefeSPatrick Mooney if (sc->psc_msi.capoff == 0) 7294c87aefeSPatrick Mooney return (0); 7304c87aefeSPatrick Mooney 7314c87aefeSPatrick Mooney caplen = msi_caplen(sc->psc_msi.msgctrl); 7324c87aefeSPatrick Mooney 7334c87aefeSPatrick Mooney if (coff >= sc->psc_msi.capoff && coff < sc->psc_msi.capoff + caplen) 7344c87aefeSPatrick Mooney return (1); 7354c87aefeSPatrick Mooney else 7364c87aefeSPatrick Mooney return (0); 7374c87aefeSPatrick Mooney } 7384c87aefeSPatrick Mooney 7394c87aefeSPatrick Mooney static int 7404c87aefeSPatrick Mooney msixcap_access(struct passthru_softc *sc, int coff) 7414c87aefeSPatrick Mooney { 7424c87aefeSPatrick Mooney if (sc->psc_msix.capoff == 0) 7434c87aefeSPatrick Mooney return (0); 7444c87aefeSPatrick Mooney 7454c87aefeSPatrick Mooney return (coff >= sc->psc_msix.capoff && 7464c87aefeSPatrick Mooney coff < sc->psc_msix.capoff + MSIX_CAPLEN); 7474c87aefeSPatrick Mooney } 7484c87aefeSPatrick Mooney 7494c87aefeSPatrick Mooney static int 7504c87aefeSPatrick Mooney passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 7514c87aefeSPatrick Mooney int coff, int bytes, uint32_t *rv) 7524c87aefeSPatrick Mooney { 7534c87aefeSPatrick Mooney struct passthru_softc *sc; 7544c87aefeSPatrick Mooney 7554c87aefeSPatrick Mooney sc = pi->pi_arg; 7564c87aefeSPatrick Mooney 7574c87aefeSPatrick Mooney /* 7584c87aefeSPatrick Mooney * PCI BARs and MSI capability is emulated. 7594c87aefeSPatrick Mooney */ 760*6dc98349SAndy Fiddaman if (bar_access(coff) || msicap_access(sc, coff) || 761*6dc98349SAndy Fiddaman msixcap_access(sc, coff)) 7624c87aefeSPatrick Mooney return (-1); 7634c87aefeSPatrick Mooney 764eb9a1df2SHans Rosenfeld /* 765eb9a1df2SHans Rosenfeld * MSI-X is also emulated since a limit on interrupts may be imposed by 766eb9a1df2SHans Rosenfeld * the OS, altering the perceived register state. 767eb9a1df2SHans Rosenfeld */ 768eb9a1df2SHans Rosenfeld if (msixcap_access(sc, coff)) 769eb9a1df2SHans Rosenfeld return (-1); 770eb9a1df2SHans Rosenfeld 7714c87aefeSPatrick Mooney #ifdef LEGACY_SUPPORT 7724c87aefeSPatrick Mooney /* 7734c87aefeSPatrick Mooney * Emulate PCIR_CAP_PTR if this device does not support MSI capability 7744c87aefeSPatrick Mooney * natively. 7754c87aefeSPatrick Mooney */ 7764c87aefeSPatrick Mooney if (sc->psc_msi.emulated) { 7774c87aefeSPatrick Mooney if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4) 7784c87aefeSPatrick Mooney return (-1); 7794c87aefeSPatrick Mooney } 7804c87aefeSPatrick Mooney #endif 7814c87aefeSPatrick Mooney 782e4321372SMichael Zeller /* 783e4321372SMichael Zeller * Emulate the command register. If a single read reads both the 784e4321372SMichael Zeller * command and status registers, read the status register from the 785e4321372SMichael Zeller * device's config space. 786e4321372SMichael Zeller */ 787e4321372SMichael Zeller if (coff == PCIR_COMMAND) { 788e4321372SMichael Zeller if (bytes <= 2) 789e4321372SMichael Zeller return (-1); 790e4321372SMichael Zeller *rv = pci_get_cfgdata16(pi, PCIR_COMMAND) << 16 | 791e4321372SMichael Zeller read_config(sc, PCIR_STATUS, 2); 792e4321372SMichael Zeller return (0); 793e4321372SMichael Zeller } 794e4321372SMichael Zeller 7954c87aefeSPatrick Mooney /* Everything else just read from the device's config space */ 796eb9a1df2SHans Rosenfeld *rv = read_config(sc, coff, bytes); 7974c87aefeSPatrick Mooney 7984c87aefeSPatrick Mooney return (0); 7994c87aefeSPatrick Mooney } 8004c87aefeSPatrick Mooney 8014c87aefeSPatrick Mooney static int 8024c87aefeSPatrick Mooney passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 8034c87aefeSPatrick Mooney int coff, int bytes, uint32_t val) 8044c87aefeSPatrick Mooney { 8054c87aefeSPatrick Mooney int error, msix_table_entries, i; 8064c87aefeSPatrick Mooney struct passthru_softc *sc; 807e4321372SMichael Zeller uint16_t cmd_old; 8084c87aefeSPatrick Mooney 8094c87aefeSPatrick Mooney sc = pi->pi_arg; 8104c87aefeSPatrick Mooney 8114c87aefeSPatrick Mooney /* 8124c87aefeSPatrick Mooney * PCI BARs are emulated 8134c87aefeSPatrick Mooney */ 8144c87aefeSPatrick Mooney if (bar_access(coff)) 8154c87aefeSPatrick Mooney return (-1); 8164c87aefeSPatrick Mooney 8174c87aefeSPatrick Mooney /* 8184c87aefeSPatrick Mooney * MSI capability is emulated 8194c87aefeSPatrick Mooney */ 8204c87aefeSPatrick Mooney if (msicap_access(sc, coff)) { 821154972afSPatrick Mooney pci_emul_capwrite(pi, coff, bytes, val, sc->psc_msi.capoff, 822154972afSPatrick Mooney PCIY_MSI); 823eb9a1df2SHans Rosenfeld error = vm_setup_pptdev_msi(ctx, vcpu, sc->pptfd, 824eb9a1df2SHans Rosenfeld pi->pi_msi.addr, pi->pi_msi.msg_data, pi->pi_msi.maxmsgnum); 8254c87aefeSPatrick Mooney if (error != 0) 8264c87aefeSPatrick Mooney err(1, "vm_setup_pptdev_msi"); 8274c87aefeSPatrick Mooney return (0); 8284c87aefeSPatrick Mooney } 8294c87aefeSPatrick Mooney 8304c87aefeSPatrick Mooney if (msixcap_access(sc, coff)) { 831154972afSPatrick Mooney pci_emul_capwrite(pi, coff, bytes, val, sc->psc_msix.capoff, 832154972afSPatrick Mooney PCIY_MSIX); 8334c87aefeSPatrick Mooney if (pi->pi_msix.enabled) { 8344c87aefeSPatrick Mooney msix_table_entries = pi->pi_msix.table_count; 8354c87aefeSPatrick Mooney for (i = 0; i < msix_table_entries; i++) { 8364c87aefeSPatrick Mooney error = vm_setup_pptdev_msix(ctx, vcpu, 837eb9a1df2SHans Rosenfeld sc->pptfd, i, 8384c87aefeSPatrick Mooney pi->pi_msix.table[i].addr, 8394c87aefeSPatrick Mooney pi->pi_msix.table[i].msg_data, 8404c87aefeSPatrick Mooney pi->pi_msix.table[i].vector_control); 8414c87aefeSPatrick Mooney 8424c87aefeSPatrick Mooney if (error) 8434c87aefeSPatrick Mooney err(1, "vm_setup_pptdev_msix"); 8444c87aefeSPatrick Mooney } 8456960cd89SAndy Fiddaman } else { 8466960cd89SAndy Fiddaman error = vm_disable_pptdev_msix(ctx, sc->pptfd); 8476960cd89SAndy Fiddaman if (error) 8486960cd89SAndy Fiddaman err(1, "vm_disable_pptdev_msix"); 8494c87aefeSPatrick Mooney } 8504c87aefeSPatrick Mooney return (0); 8514c87aefeSPatrick Mooney } 8524c87aefeSPatrick Mooney 8534c87aefeSPatrick Mooney #ifdef LEGACY_SUPPORT 8544c87aefeSPatrick Mooney /* 8554c87aefeSPatrick Mooney * If this device does not support MSI natively then we cannot let 8564c87aefeSPatrick Mooney * the guest disable legacy interrupts from the device. It is the 8574c87aefeSPatrick Mooney * legacy interrupt that is triggering the virtual MSI to the guest. 8584c87aefeSPatrick Mooney */ 8594c87aefeSPatrick Mooney if (sc->psc_msi.emulated && pci_msi_enabled(pi)) { 8604c87aefeSPatrick Mooney if (coff == PCIR_COMMAND && bytes == 2) 8614c87aefeSPatrick Mooney val &= ~PCIM_CMD_INTxDIS; 8624c87aefeSPatrick Mooney } 8634c87aefeSPatrick Mooney #endif 8644c87aefeSPatrick Mooney 865eb9a1df2SHans Rosenfeld write_config(sc, coff, bytes, val); 866e4321372SMichael Zeller if (coff == PCIR_COMMAND) { 867e4321372SMichael Zeller cmd_old = pci_get_cfgdata16(pi, PCIR_COMMAND); 868e4321372SMichael Zeller if (bytes == 1) 869e4321372SMichael Zeller pci_set_cfgdata8(pi, PCIR_COMMAND, val); 870e4321372SMichael Zeller else if (bytes == 2) 871e4321372SMichael Zeller pci_set_cfgdata16(pi, PCIR_COMMAND, val); 872e4321372SMichael Zeller pci_emul_cmd_changed(pi, cmd_old); 873e4321372SMichael Zeller } 8744c87aefeSPatrick Mooney 8754c87aefeSPatrick Mooney return (0); 8764c87aefeSPatrick Mooney } 8774c87aefeSPatrick Mooney 8784c87aefeSPatrick Mooney static void 8794c87aefeSPatrick Mooney passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 8804c87aefeSPatrick Mooney uint64_t offset, int size, uint64_t value) 8814c87aefeSPatrick Mooney { 882eb9a1df2SHans Rosenfeld struct passthru_softc *sc = pi->pi_arg; 8834c87aefeSPatrick Mooney 8844c87aefeSPatrick Mooney if (baridx == pci_msix_table_bar(pi)) { 885*6dc98349SAndy Fiddaman msix_table_write(ctx, vcpu, sc, offset, size, value); 8864c87aefeSPatrick Mooney } else { 887eb9a1df2SHans Rosenfeld struct ppt_bar_io pbi; 8884c87aefeSPatrick Mooney 889eb9a1df2SHans Rosenfeld assert(pi->pi_bar[baridx].type == PCIBAR_IO); 890eb9a1df2SHans Rosenfeld 891eb9a1df2SHans Rosenfeld pbi.pbi_bar = baridx; 892eb9a1df2SHans Rosenfeld pbi.pbi_width = size; 893eb9a1df2SHans Rosenfeld pbi.pbi_off = offset; 894eb9a1df2SHans Rosenfeld pbi.pbi_data = value; 895eb9a1df2SHans Rosenfeld (void) ioctl(sc->pptfd, PPT_BAR_WRITE, &pbi); 8964c87aefeSPatrick Mooney } 8974c87aefeSPatrick Mooney } 8984c87aefeSPatrick Mooney 8994c87aefeSPatrick Mooney static uint64_t 9004c87aefeSPatrick Mooney passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 9014c87aefeSPatrick Mooney uint64_t offset, int size) 9024c87aefeSPatrick Mooney { 903eb9a1df2SHans Rosenfeld struct passthru_softc *sc = pi->pi_arg; 9044c87aefeSPatrick Mooney uint64_t val; 9054c87aefeSPatrick Mooney 9064c87aefeSPatrick Mooney if (baridx == pci_msix_table_bar(pi)) { 907*6dc98349SAndy Fiddaman val = msix_table_read(sc, offset, size); 9084c87aefeSPatrick Mooney } else { 909eb9a1df2SHans Rosenfeld struct ppt_bar_io pbi; 910eb9a1df2SHans Rosenfeld 9114c87aefeSPatrick Mooney assert(pi->pi_bar[baridx].type == PCIBAR_IO); 9124c87aefeSPatrick Mooney 913eb9a1df2SHans Rosenfeld pbi.pbi_bar = baridx; 914eb9a1df2SHans Rosenfeld pbi.pbi_width = size; 915eb9a1df2SHans Rosenfeld pbi.pbi_off = offset; 916eb9a1df2SHans Rosenfeld if (ioctl(sc->pptfd, PPT_BAR_READ, &pbi) == 0) { 917eb9a1df2SHans Rosenfeld val = pbi.pbi_data; 918eb9a1df2SHans Rosenfeld } else { 919eb9a1df2SHans Rosenfeld val = 0; 920eb9a1df2SHans Rosenfeld } 9214c87aefeSPatrick Mooney } 9224c87aefeSPatrick Mooney 9234c87aefeSPatrick Mooney return (val); 9244c87aefeSPatrick Mooney } 9254c87aefeSPatrick Mooney 9262b948146SAndy Fiddaman static void 9272b948146SAndy Fiddaman passthru_msix_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx, 9282b948146SAndy Fiddaman int enabled, uint64_t address) 9292b948146SAndy Fiddaman { 9302b948146SAndy Fiddaman struct passthru_softc *sc; 9312b948146SAndy Fiddaman size_t remaining; 9322b948146SAndy Fiddaman uint32_t table_size, table_offset; 9332b948146SAndy Fiddaman 9342b948146SAndy Fiddaman sc = pi->pi_arg; 9352b948146SAndy Fiddaman table_offset = rounddown2(pi->pi_msix.table_offset, 4096); 9362b948146SAndy Fiddaman if (table_offset > 0) { 9372b948146SAndy Fiddaman if (!enabled) { 9382b948146SAndy Fiddaman if (vm_unmap_pptdev_mmio(ctx, sc->pptfd, address, 9392b948146SAndy Fiddaman table_offset) != 0) 9402b948146SAndy Fiddaman warnx("pci_passthru: unmap_pptdev_mmio failed"); 9412b948146SAndy Fiddaman } else { 9422b948146SAndy Fiddaman if (vm_map_pptdev_mmio(ctx, sc->pptfd, address, 9432b948146SAndy Fiddaman table_offset, sc->psc_bar[baridx].addr) != 0) 9442b948146SAndy Fiddaman warnx("pci_passthru: map_pptdev_mmio failed"); 9452b948146SAndy Fiddaman } 9462b948146SAndy Fiddaman } 9472b948146SAndy Fiddaman table_size = pi->pi_msix.table_offset - table_offset; 9482b948146SAndy Fiddaman table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; 9492b948146SAndy Fiddaman table_size = roundup2(table_size, 4096); 9502b948146SAndy Fiddaman remaining = pi->pi_bar[baridx].size - table_offset - table_size; 9512b948146SAndy Fiddaman if (remaining > 0) { 9522b948146SAndy Fiddaman address += table_offset + table_size; 9532b948146SAndy Fiddaman if (!enabled) { 9542b948146SAndy Fiddaman if (vm_unmap_pptdev_mmio(ctx, sc->pptfd, address, 9552b948146SAndy Fiddaman remaining) != 0) 9562b948146SAndy Fiddaman warnx("pci_passthru: unmap_pptdev_mmio failed"); 9572b948146SAndy Fiddaman } else { 9582b948146SAndy Fiddaman if (vm_map_pptdev_mmio(ctx, sc->pptfd, address, 9592b948146SAndy Fiddaman remaining, sc->psc_bar[baridx].addr + 9602b948146SAndy Fiddaman table_offset + table_size) != 0) 9612b948146SAndy Fiddaman warnx("pci_passthru: map_pptdev_mmio failed"); 9622b948146SAndy Fiddaman } 9632b948146SAndy Fiddaman } 9642b948146SAndy Fiddaman } 9652b948146SAndy Fiddaman 9662b948146SAndy Fiddaman static void 9672b948146SAndy Fiddaman passthru_mmio_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx, 9682b948146SAndy Fiddaman int enabled, uint64_t address) 9692b948146SAndy Fiddaman { 9702b948146SAndy Fiddaman struct passthru_softc *sc; 9712b948146SAndy Fiddaman 9722b948146SAndy Fiddaman sc = pi->pi_arg; 9732b948146SAndy Fiddaman if (!enabled) { 9742b948146SAndy Fiddaman if (vm_unmap_pptdev_mmio(ctx, sc->pptfd, address, 9752b948146SAndy Fiddaman sc->psc_bar[baridx].size) != 0) 9762b948146SAndy Fiddaman warnx("pci_passthru: unmap_pptdev_mmio failed"); 9772b948146SAndy Fiddaman } else { 9782b948146SAndy Fiddaman if (vm_map_pptdev_mmio(ctx, sc->pptfd, address, 9792b948146SAndy Fiddaman sc->psc_bar[baridx].size, sc->psc_bar[baridx].addr) != 0) 9802b948146SAndy Fiddaman warnx("pci_passthru: map_pptdev_mmio failed"); 9812b948146SAndy Fiddaman } 9822b948146SAndy Fiddaman } 9832b948146SAndy Fiddaman 9842b948146SAndy Fiddaman static void 9852b948146SAndy Fiddaman passthru_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx, 9862b948146SAndy Fiddaman int enabled, uint64_t address) 9872b948146SAndy Fiddaman { 9882b948146SAndy Fiddaman 9892b948146SAndy Fiddaman if (pi->pi_bar[baridx].type == PCIBAR_IO) 9902b948146SAndy Fiddaman return; 9912b948146SAndy Fiddaman if (baridx == pci_msix_table_bar(pi)) 9922b948146SAndy Fiddaman passthru_msix_addr(ctx, pi, baridx, enabled, address); 9932b948146SAndy Fiddaman else 9942b948146SAndy Fiddaman passthru_mmio_addr(ctx, pi, baridx, enabled, address); 9952b948146SAndy Fiddaman } 9962b948146SAndy Fiddaman 9974c87aefeSPatrick Mooney struct pci_devemu passthru = { 9984c87aefeSPatrick Mooney .pe_emu = "passthru", 9994c87aefeSPatrick Mooney .pe_init = passthru_init, 10002b948146SAndy Fiddaman .pe_legacy_config = passthru_legacy_config, 10014c87aefeSPatrick Mooney .pe_cfgwrite = passthru_cfgwrite, 10024c87aefeSPatrick Mooney .pe_cfgread = passthru_cfgread, 10034c87aefeSPatrick Mooney .pe_barwrite = passthru_write, 10044c87aefeSPatrick Mooney .pe_barread = passthru_read, 10052b948146SAndy Fiddaman .pe_baraddr = passthru_addr, 10064c87aefeSPatrick Mooney }; 10074c87aefeSPatrick Mooney PCI_EMUL_SET(passthru); 1008