1366f6083SPeter Grehan /*- 2366f6083SPeter Grehan * Copyright (c) 2011 NetApp, Inc. 3366f6083SPeter Grehan * All rights reserved. 4366f6083SPeter Grehan * 5366f6083SPeter Grehan * Redistribution and use in source and binary forms, with or without 6366f6083SPeter Grehan * modification, are permitted provided that the following conditions 7366f6083SPeter Grehan * are met: 8366f6083SPeter Grehan * 1. Redistributions of source code must retain the above copyright 9366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer. 10366f6083SPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 11366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer in the 12366f6083SPeter Grehan * documentation and/or other materials provided with the distribution. 13366f6083SPeter Grehan * 14366f6083SPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15366f6083SPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16366f6083SPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17366f6083SPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18366f6083SPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19366f6083SPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20366f6083SPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21366f6083SPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22366f6083SPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23366f6083SPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24366f6083SPeter Grehan * SUCH DAMAGE. 25366f6083SPeter Grehan * 26366f6083SPeter Grehan * $FreeBSD$ 27366f6083SPeter Grehan */ 28366f6083SPeter Grehan 29366f6083SPeter Grehan #include <sys/cdefs.h> 30366f6083SPeter Grehan __FBSDID("$FreeBSD$"); 31366f6083SPeter Grehan 32366f6083SPeter Grehan #include <sys/param.h> 33366f6083SPeter Grehan #include <sys/types.h> 34366f6083SPeter Grehan #include <sys/pciio.h> 35366f6083SPeter Grehan #include <sys/ioctl.h> 36366f6083SPeter Grehan 37366f6083SPeter Grehan #include <dev/io/iodev.h> 382e81a7e8SNeel Natu #include <dev/pci/pcireg.h> 392e81a7e8SNeel Natu 40366f6083SPeter Grehan #include <machine/iodev.h> 41366f6083SPeter Grehan 42366f6083SPeter Grehan #include <stdio.h> 43366f6083SPeter Grehan #include <stdlib.h> 44366f6083SPeter Grehan #include <string.h> 45366f6083SPeter Grehan #include <errno.h> 46366f6083SPeter Grehan #include <fcntl.h> 47366f6083SPeter Grehan #include <unistd.h> 48366f6083SPeter Grehan 49366f6083SPeter Grehan #include <machine/vmm.h> 50366f6083SPeter Grehan #include <vmmapi.h> 51366f6083SPeter Grehan #include "pci_emul.h" 524d1e669cSPeter Grehan #include "mem.h" 53366f6083SPeter Grehan 54366f6083SPeter Grehan #ifndef _PATH_DEVPCI 55366f6083SPeter Grehan #define _PATH_DEVPCI "/dev/pci" 56366f6083SPeter Grehan #endif 57366f6083SPeter Grehan 58366f6083SPeter Grehan #ifndef _PATH_DEVIO 59366f6083SPeter Grehan #define _PATH_DEVIO "/dev/io" 60366f6083SPeter Grehan #endif 61366f6083SPeter Grehan 62366f6083SPeter Grehan #define LEGACY_SUPPORT 1 63366f6083SPeter Grehan 642e81a7e8SNeel Natu #define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1) 65cd942e0fSPeter Grehan #define MSIX_CAPLEN 12 66cd942e0fSPeter Grehan 67366f6083SPeter Grehan static int pcifd = -1; 68366f6083SPeter Grehan static int iofd = -1; 69366f6083SPeter Grehan 70366f6083SPeter Grehan struct passthru_softc { 71366f6083SPeter Grehan struct pci_devinst *psc_pi; 72366f6083SPeter Grehan struct pcibar psc_bar[PCI_BARMAX + 1]; 73366f6083SPeter Grehan struct { 74366f6083SPeter Grehan int capoff; 75366f6083SPeter Grehan int msgctrl; 76366f6083SPeter Grehan int emulated; 77366f6083SPeter Grehan } psc_msi; 78cd942e0fSPeter Grehan struct { 79cd942e0fSPeter Grehan int capoff; 80cd942e0fSPeter Grehan } psc_msix; 81366f6083SPeter Grehan struct pcisel psc_sel; 82366f6083SPeter Grehan }; 83366f6083SPeter Grehan 84366f6083SPeter Grehan static int 85366f6083SPeter Grehan msi_caplen(int msgctrl) 86366f6083SPeter Grehan { 87366f6083SPeter Grehan int len; 88366f6083SPeter Grehan 89366f6083SPeter Grehan len = 10; /* minimum length of msi capability */ 90366f6083SPeter Grehan 91366f6083SPeter Grehan if (msgctrl & PCIM_MSICTRL_64BIT) 92366f6083SPeter Grehan len += 4; 93366f6083SPeter Grehan 94366f6083SPeter Grehan #if 0 95366f6083SPeter Grehan /* 96366f6083SPeter Grehan * Ignore the 'mask' and 'pending' bits in the MSI capability. 97366f6083SPeter Grehan * We'll let the guest manipulate them directly. 98366f6083SPeter Grehan */ 99366f6083SPeter Grehan if (msgctrl & PCIM_MSICTRL_VECTOR) 100366f6083SPeter Grehan len += 10; 101366f6083SPeter Grehan #endif 102366f6083SPeter Grehan 103366f6083SPeter Grehan return (len); 104366f6083SPeter Grehan } 105366f6083SPeter Grehan 106366f6083SPeter Grehan static uint32_t 107366f6083SPeter Grehan read_config(const struct pcisel *sel, long reg, int width) 108366f6083SPeter Grehan { 109366f6083SPeter Grehan struct pci_io pi; 110366f6083SPeter Grehan 111366f6083SPeter Grehan bzero(&pi, sizeof(pi)); 112366f6083SPeter Grehan pi.pi_sel = *sel; 113366f6083SPeter Grehan pi.pi_reg = reg; 114366f6083SPeter Grehan pi.pi_width = width; 115366f6083SPeter Grehan 116366f6083SPeter Grehan if (ioctl(pcifd, PCIOCREAD, &pi) < 0) 117366f6083SPeter Grehan return (0); /* XXX */ 118366f6083SPeter Grehan else 119366f6083SPeter Grehan return (pi.pi_data); 120366f6083SPeter Grehan } 121366f6083SPeter Grehan 122366f6083SPeter Grehan static void 123366f6083SPeter Grehan write_config(const struct pcisel *sel, long reg, int width, uint32_t data) 124366f6083SPeter Grehan { 125366f6083SPeter Grehan struct pci_io pi; 126366f6083SPeter Grehan 127366f6083SPeter Grehan bzero(&pi, sizeof(pi)); 128366f6083SPeter Grehan pi.pi_sel = *sel; 129366f6083SPeter Grehan pi.pi_reg = reg; 130366f6083SPeter Grehan pi.pi_width = width; 131366f6083SPeter Grehan pi.pi_data = data; 132366f6083SPeter Grehan 133366f6083SPeter Grehan (void)ioctl(pcifd, PCIOCWRITE, &pi); /* XXX */ 134366f6083SPeter Grehan } 135366f6083SPeter Grehan 136366f6083SPeter Grehan #ifdef LEGACY_SUPPORT 137366f6083SPeter Grehan static int 138366f6083SPeter Grehan passthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr) 139366f6083SPeter Grehan { 140366f6083SPeter Grehan int capoff, i; 141366f6083SPeter Grehan struct msicap msicap; 142366f6083SPeter Grehan u_char *capdata; 143366f6083SPeter Grehan 144366f6083SPeter Grehan pci_populate_msicap(&msicap, msgnum, nextptr); 145366f6083SPeter Grehan 146366f6083SPeter Grehan /* 147366f6083SPeter Grehan * XXX 148366f6083SPeter Grehan * Copy the msi capability structure in the last 16 bytes of the 149366f6083SPeter Grehan * config space. This is wrong because it could shadow something 150366f6083SPeter Grehan * useful to the device. 151366f6083SPeter Grehan */ 152366f6083SPeter Grehan capoff = 256 - roundup(sizeof(msicap), 4); 153366f6083SPeter Grehan capdata = (u_char *)&msicap; 154366f6083SPeter Grehan for (i = 0; i < sizeof(msicap); i++) 155366f6083SPeter Grehan pci_set_cfgdata8(pi, capoff + i, capdata[i]); 156366f6083SPeter Grehan 157366f6083SPeter Grehan return (capoff); 158366f6083SPeter Grehan } 159366f6083SPeter Grehan #endif /* LEGACY_SUPPORT */ 160366f6083SPeter Grehan 161366f6083SPeter Grehan static int 162366f6083SPeter Grehan cfginitmsi(struct passthru_softc *sc) 163366f6083SPeter Grehan { 1642e81a7e8SNeel Natu int i, ptr, capptr, cap, sts, caplen, table_size; 165366f6083SPeter Grehan uint32_t u32; 166366f6083SPeter Grehan struct pcisel sel; 167366f6083SPeter Grehan struct pci_devinst *pi; 168cd942e0fSPeter Grehan struct msixcap msixcap; 169cd942e0fSPeter Grehan uint32_t *msixcap_ptr; 170366f6083SPeter Grehan 171366f6083SPeter Grehan pi = sc->psc_pi; 172366f6083SPeter Grehan sel = sc->psc_sel; 173366f6083SPeter Grehan 174366f6083SPeter Grehan /* 175366f6083SPeter Grehan * Parse the capabilities and cache the location of the MSI 176cd942e0fSPeter Grehan * and MSI-X capabilities. 177366f6083SPeter Grehan */ 178366f6083SPeter Grehan sts = read_config(&sel, PCIR_STATUS, 2); 179366f6083SPeter Grehan if (sts & PCIM_STATUS_CAPPRESENT) { 180366f6083SPeter Grehan ptr = read_config(&sel, PCIR_CAP_PTR, 1); 181366f6083SPeter Grehan while (ptr != 0 && ptr != 0xff) { 182366f6083SPeter Grehan cap = read_config(&sel, ptr + PCICAP_ID, 1); 183366f6083SPeter Grehan if (cap == PCIY_MSI) { 184366f6083SPeter Grehan /* 185366f6083SPeter Grehan * Copy the MSI capability into the config 186366f6083SPeter Grehan * space of the emulated pci device 187366f6083SPeter Grehan */ 188366f6083SPeter Grehan sc->psc_msi.capoff = ptr; 189366f6083SPeter Grehan sc->psc_msi.msgctrl = read_config(&sel, 190366f6083SPeter Grehan ptr + 2, 2); 191366f6083SPeter Grehan sc->psc_msi.emulated = 0; 192366f6083SPeter Grehan caplen = msi_caplen(sc->psc_msi.msgctrl); 193cd942e0fSPeter Grehan capptr = ptr; 194366f6083SPeter Grehan while (caplen > 0) { 195cd942e0fSPeter Grehan u32 = read_config(&sel, capptr, 4); 196cd942e0fSPeter Grehan pci_set_cfgdata32(pi, capptr, u32); 197366f6083SPeter Grehan caplen -= 4; 198cd942e0fSPeter Grehan capptr += 4; 199366f6083SPeter Grehan } 200cd942e0fSPeter Grehan } else if (cap == PCIY_MSIX) { 201cd942e0fSPeter Grehan /* 202cd942e0fSPeter Grehan * Copy the MSI-X capability 203cd942e0fSPeter Grehan */ 204cd942e0fSPeter Grehan sc->psc_msix.capoff = ptr; 205cd942e0fSPeter Grehan caplen = 12; 206cd942e0fSPeter Grehan msixcap_ptr = (uint32_t*) &msixcap; 207cd942e0fSPeter Grehan capptr = ptr; 208cd942e0fSPeter Grehan while (caplen > 0) { 209cd942e0fSPeter Grehan u32 = read_config(&sel, capptr, 4); 210cd942e0fSPeter Grehan *msixcap_ptr = u32; 211cd942e0fSPeter Grehan pci_set_cfgdata32(pi, capptr, u32); 212cd942e0fSPeter Grehan caplen -= 4; 213cd942e0fSPeter Grehan capptr += 4; 214cd942e0fSPeter Grehan msixcap_ptr++; 215cd942e0fSPeter Grehan } 216366f6083SPeter Grehan } 217366f6083SPeter Grehan ptr = read_config(&sel, ptr + PCICAP_NEXTPTR, 1); 218366f6083SPeter Grehan } 219366f6083SPeter Grehan } 220366f6083SPeter Grehan 2214d1e669cSPeter Grehan if (sc->psc_msix.capoff != 0) { 2224d1e669cSPeter Grehan pi->pi_msix.pba_bar = 2232e81a7e8SNeel Natu msixcap.pba_info & PCIM_MSIX_BIR_MASK; 2244d1e669cSPeter Grehan pi->pi_msix.pba_offset = 2252e81a7e8SNeel Natu msixcap.pba_info & ~PCIM_MSIX_BIR_MASK; 2264d1e669cSPeter Grehan pi->pi_msix.table_bar = 2272e81a7e8SNeel Natu msixcap.table_info & PCIM_MSIX_BIR_MASK; 2284d1e669cSPeter Grehan pi->pi_msix.table_offset = 2292e81a7e8SNeel Natu msixcap.table_info & ~PCIM_MSIX_BIR_MASK; 230cd942e0fSPeter Grehan pi->pi_msix.table_count = MSIX_TABLE_COUNT(msixcap.msgctrl); 2312e81a7e8SNeel Natu 2322e81a7e8SNeel Natu /* Allocate the emulated MSI-X table array */ 2332e81a7e8SNeel Natu table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; 2342e81a7e8SNeel Natu pi->pi_msix.table = malloc(table_size); 2352e81a7e8SNeel Natu bzero(pi->pi_msix.table, table_size); 2362e81a7e8SNeel Natu 2372e81a7e8SNeel Natu /* Mask all table entries */ 2382e81a7e8SNeel Natu for (i = 0; i < pi->pi_msix.table_count; i++) { 2392e81a7e8SNeel Natu pi->pi_msix.table[i].vector_control |= 2402e81a7e8SNeel Natu PCIM_MSIX_VCTRL_MASK; 2412e81a7e8SNeel Natu } 2424d1e669cSPeter Grehan } 243cd942e0fSPeter Grehan 244366f6083SPeter Grehan #ifdef LEGACY_SUPPORT 245366f6083SPeter Grehan /* 246366f6083SPeter Grehan * If the passthrough device does not support MSI then craft a 247366f6083SPeter Grehan * MSI capability for it. We link the new MSI capability at the 248366f6083SPeter Grehan * head of the list of capabilities. 249366f6083SPeter Grehan */ 250366f6083SPeter Grehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0 && sc->psc_msi.capoff == 0) { 251366f6083SPeter Grehan int origptr, msiptr; 252366f6083SPeter Grehan origptr = read_config(&sel, PCIR_CAP_PTR, 1); 253366f6083SPeter Grehan msiptr = passthru_add_msicap(pi, 1, origptr); 254366f6083SPeter Grehan sc->psc_msi.capoff = msiptr; 255366f6083SPeter Grehan sc->psc_msi.msgctrl = pci_get_cfgdata16(pi, msiptr + 2); 256366f6083SPeter Grehan sc->psc_msi.emulated = 1; 257366f6083SPeter Grehan pci_set_cfgdata8(pi, PCIR_CAP_PTR, msiptr); 258366f6083SPeter Grehan } 259366f6083SPeter Grehan #endif 260366f6083SPeter Grehan 261cd942e0fSPeter Grehan /* Make sure one of the capabilities is present */ 262cd942e0fSPeter Grehan if (sc->psc_msi.capoff == 0 && sc->psc_msix.capoff == 0) 263366f6083SPeter Grehan return (-1); 264366f6083SPeter Grehan else 265366f6083SPeter Grehan return (0); 266366f6083SPeter Grehan } 267366f6083SPeter Grehan 2684d1e669cSPeter Grehan static uint64_t 2694d1e669cSPeter Grehan msix_table_read(struct passthru_softc *sc, uint64_t offset, int size) 270cd942e0fSPeter Grehan { 271cd942e0fSPeter Grehan struct pci_devinst *pi; 2724d1e669cSPeter Grehan struct msix_table_entry *entry; 273cd942e0fSPeter Grehan uint8_t *src8; 274cd942e0fSPeter Grehan uint16_t *src16; 275cd942e0fSPeter Grehan uint32_t *src32; 276cd942e0fSPeter Grehan uint64_t *src64; 2774d1e669cSPeter Grehan uint64_t data; 2784d1e669cSPeter Grehan size_t entry_offset; 2794d1e669cSPeter Grehan int index; 280cd942e0fSPeter Grehan 281cd942e0fSPeter Grehan pi = sc->psc_pi; 2822e81a7e8SNeel Natu 283cd942e0fSPeter Grehan index = offset / MSIX_TABLE_ENTRY_SIZE; 2842e81a7e8SNeel Natu if (index >= pi->pi_msix.table_count) 2852e81a7e8SNeel Natu return (-1); 2862e81a7e8SNeel Natu 287cd942e0fSPeter Grehan entry = &pi->pi_msix.table[index]; 2882e81a7e8SNeel Natu entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 289cd942e0fSPeter Grehan 290cd942e0fSPeter Grehan switch(size) { 291cd942e0fSPeter Grehan case 1: 292cd942e0fSPeter Grehan src8 = (uint8_t *)((void *)entry + entry_offset); 2934d1e669cSPeter Grehan data = *src8; 294cd942e0fSPeter Grehan break; 295cd942e0fSPeter Grehan case 2: 296cd942e0fSPeter Grehan src16 = (uint16_t *)((void *)entry + entry_offset); 2974d1e669cSPeter Grehan data = *src16; 298cd942e0fSPeter Grehan break; 299cd942e0fSPeter Grehan case 4: 300cd942e0fSPeter Grehan src32 = (uint32_t *)((void *)entry + entry_offset); 3014d1e669cSPeter Grehan data = *src32; 302cd942e0fSPeter Grehan break; 303cd942e0fSPeter Grehan case 8: 304cd942e0fSPeter Grehan src64 = (uint64_t *)((void *)entry + entry_offset); 3054d1e669cSPeter Grehan data = *src64; 306cd942e0fSPeter Grehan break; 307cd942e0fSPeter Grehan default: 308cd942e0fSPeter Grehan return (-1); 309cd942e0fSPeter Grehan } 310cd942e0fSPeter Grehan 3114d1e669cSPeter Grehan return (data); 312cd942e0fSPeter Grehan } 313cd942e0fSPeter Grehan 3144d1e669cSPeter Grehan static void 3154d1e669cSPeter Grehan msix_table_write(struct vmctx *ctx, int vcpu, struct passthru_softc *sc, 3164d1e669cSPeter Grehan uint64_t offset, int size, uint64_t data) 317cd942e0fSPeter Grehan { 318cd942e0fSPeter Grehan struct pci_devinst *pi; 319cd942e0fSPeter Grehan struct msix_table_entry *entry; 3204d1e669cSPeter Grehan uint32_t *dest; 3214d1e669cSPeter Grehan size_t entry_offset; 322cd942e0fSPeter Grehan uint32_t vector_control; 3234d1e669cSPeter Grehan int error, index; 324cd942e0fSPeter Grehan 325cd942e0fSPeter Grehan pi = sc->psc_pi; 326cd942e0fSPeter Grehan index = offset / MSIX_TABLE_ENTRY_SIZE; 3272e81a7e8SNeel Natu if (index >= pi->pi_msix.table_count) 3282e81a7e8SNeel Natu return; 3292e81a7e8SNeel Natu 330cd942e0fSPeter Grehan entry = &pi->pi_msix.table[index]; 3312e81a7e8SNeel Natu entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 332cd942e0fSPeter Grehan 333cd942e0fSPeter Grehan /* Only 4 byte naturally-aligned writes are supported */ 3344d1e669cSPeter Grehan assert(size == 4); 3354d1e669cSPeter Grehan assert(entry_offset % 4 == 0); 3364d1e669cSPeter Grehan 337cd942e0fSPeter Grehan vector_control = entry->vector_control; 338cd942e0fSPeter Grehan dest = (uint32_t *)((void *)entry + entry_offset); 339cd942e0fSPeter Grehan *dest = data; 340cd942e0fSPeter Grehan /* If MSI-X hasn't been enabled, do nothing */ 341cd942e0fSPeter Grehan if (pi->pi_msix.enabled) { 342cd942e0fSPeter Grehan /* If the entry is masked, don't set it up */ 343cd942e0fSPeter Grehan if ((entry->vector_control & PCIM_MSIX_VCTRL_MASK) == 0 || 344cd942e0fSPeter Grehan (vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 3454d1e669cSPeter Grehan error = vm_setup_msix(ctx, vcpu, sc->psc_sel.pc_bus, 346cd942e0fSPeter Grehan sc->psc_sel.pc_dev, 347cd942e0fSPeter Grehan sc->psc_sel.pc_func, 348cd942e0fSPeter Grehan index, entry->msg_data, 349cd942e0fSPeter Grehan entry->vector_control, 350cd942e0fSPeter Grehan entry->addr); 351cd942e0fSPeter Grehan } 352cd942e0fSPeter Grehan } 353cd942e0fSPeter Grehan } 354cd942e0fSPeter Grehan 355cd942e0fSPeter Grehan static int 356cd942e0fSPeter Grehan init_msix_table(struct vmctx *ctx, struct passthru_softc *sc, uint64_t base) 357cd942e0fSPeter Grehan { 358cd942e0fSPeter Grehan int idx; 359cd942e0fSPeter Grehan size_t table_size; 360cd942e0fSPeter Grehan vm_paddr_t start; 361cd942e0fSPeter Grehan size_t len; 362cd942e0fSPeter Grehan struct pci_devinst *pi = sc->psc_pi; 363cd942e0fSPeter Grehan 364*aa12663fSNeel Natu assert(pci_msix_table_bar(pi) >= 0 && pci_msix_pba_bar(pi) >= 0); 365*aa12663fSNeel Natu 366cd942e0fSPeter Grehan /* 367cd942e0fSPeter Grehan * If the MSI-X table BAR maps memory intended for 368cd942e0fSPeter Grehan * other uses, it is at least assured that the table 369cd942e0fSPeter Grehan * either resides in its own page within the region, 370cd942e0fSPeter Grehan * or it resides in a page shared with only the PBA. 371cd942e0fSPeter Grehan */ 372cd942e0fSPeter Grehan if (pi->pi_msix.pba_bar == pi->pi_msix.table_bar && 373cd942e0fSPeter Grehan ((pi->pi_msix.pba_offset - pi->pi_msix.table_offset) < 4096)) { 374cd942e0fSPeter Grehan /* Need to also emulate the PBA, not supported yet */ 375*aa12663fSNeel Natu printf("Unsupported MSI-X configuration: %d/%d/%d\n", 376*aa12663fSNeel Natu sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, 377*aa12663fSNeel Natu sc->psc_sel.pc_func); 378cd942e0fSPeter Grehan return (-1); 379cd942e0fSPeter Grehan } 3804d1e669cSPeter Grehan 381cd942e0fSPeter Grehan /* 382cd942e0fSPeter Grehan * May need to split the BAR into 3 regions: 383cd942e0fSPeter Grehan * Before the MSI-X table, the MSI-X table, and after it 384cd942e0fSPeter Grehan * XXX for now, assume that the table is not in the middle 385cd942e0fSPeter Grehan */ 386cd942e0fSPeter Grehan table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; 387cd942e0fSPeter Grehan idx = pi->pi_msix.table_bar; 388cd942e0fSPeter Grehan 389cd942e0fSPeter Grehan /* Round up to page size */ 390c3cbaac9SNeel Natu table_size = roundup2(table_size, 4096); 391cd942e0fSPeter Grehan if (pi->pi_msix.table_offset == 0) { 392cd942e0fSPeter Grehan /* Map everything after the MSI-X table */ 393cd942e0fSPeter Grehan start = pi->pi_bar[idx].addr + table_size; 394cd942e0fSPeter Grehan len = pi->pi_bar[idx].size - table_size; 395cd942e0fSPeter Grehan } else { 396cd942e0fSPeter Grehan /* Map everything before the MSI-X table */ 397cd942e0fSPeter Grehan start = pi->pi_bar[idx].addr; 398cd942e0fSPeter Grehan len = pi->pi_msix.table_offset; 399cd942e0fSPeter Grehan } 4004d1e669cSPeter Grehan return (vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, 401cd942e0fSPeter Grehan sc->psc_sel.pc_dev, sc->psc_sel.pc_func, 4024d1e669cSPeter Grehan start, len, base + table_size)); 403cd942e0fSPeter Grehan } 404cd942e0fSPeter Grehan 405cd942e0fSPeter Grehan static int 406366f6083SPeter Grehan cfginitbar(struct vmctx *ctx, struct passthru_softc *sc) 407366f6083SPeter Grehan { 408366f6083SPeter Grehan int i, error; 409366f6083SPeter Grehan struct pci_devinst *pi; 410366f6083SPeter Grehan struct pci_bar_io bar; 411366f6083SPeter Grehan enum pcibar_type bartype; 412366f6083SPeter Grehan uint64_t base; 413366f6083SPeter Grehan 414366f6083SPeter Grehan pi = sc->psc_pi; 415366f6083SPeter Grehan 416366f6083SPeter Grehan /* 417366f6083SPeter Grehan * Initialize BAR registers 418366f6083SPeter Grehan */ 419366f6083SPeter Grehan for (i = 0; i <= PCI_BARMAX; i++) { 420366f6083SPeter Grehan bzero(&bar, sizeof(bar)); 421366f6083SPeter Grehan bar.pbi_sel = sc->psc_sel; 422366f6083SPeter Grehan bar.pbi_reg = PCIR_BAR(i); 423366f6083SPeter Grehan 424366f6083SPeter Grehan if (ioctl(pcifd, PCIOCGETBAR, &bar) < 0) 425366f6083SPeter Grehan continue; 426366f6083SPeter Grehan 427366f6083SPeter Grehan if (PCI_BAR_IO(bar.pbi_base)) { 428366f6083SPeter Grehan bartype = PCIBAR_IO; 429366f6083SPeter Grehan base = bar.pbi_base & PCIM_BAR_IO_BASE; 430366f6083SPeter Grehan } else { 431366f6083SPeter Grehan switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) { 432366f6083SPeter Grehan case PCIM_BAR_MEM_64: 433366f6083SPeter Grehan bartype = PCIBAR_MEM64; 434366f6083SPeter Grehan break; 435366f6083SPeter Grehan default: 436366f6083SPeter Grehan bartype = PCIBAR_MEM32; 437366f6083SPeter Grehan break; 438366f6083SPeter Grehan } 439366f6083SPeter Grehan base = bar.pbi_base & PCIM_BAR_MEM_BASE; 440366f6083SPeter Grehan } 441366f6083SPeter Grehan 442366f6083SPeter Grehan /* Cache information about the "real" BAR */ 443366f6083SPeter Grehan sc->psc_bar[i].type = bartype; 444366f6083SPeter Grehan sc->psc_bar[i].size = bar.pbi_length; 445366f6083SPeter Grehan sc->psc_bar[i].addr = base; 446366f6083SPeter Grehan 447366f6083SPeter Grehan /* Allocate the BAR in the guest I/O or MMIO space */ 4484d1e669cSPeter Grehan error = pci_emul_alloc_pbar(pi, i, base, bartype, 449366f6083SPeter Grehan bar.pbi_length); 450366f6083SPeter Grehan if (error) 451366f6083SPeter Grehan return (-1); 452366f6083SPeter Grehan 453cd942e0fSPeter Grehan /* The MSI-X table needs special handling */ 454*aa12663fSNeel Natu if (i == pci_msix_table_bar(pi)) { 455cd942e0fSPeter Grehan error = init_msix_table(ctx, sc, base); 456cd942e0fSPeter Grehan if (error) 457cd942e0fSPeter Grehan return (-1); 458cd942e0fSPeter Grehan } else if (bartype != PCIBAR_IO) { 459cd942e0fSPeter Grehan /* Map the physical MMIO space in the guest MMIO space */ 460366f6083SPeter Grehan error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, 461366f6083SPeter Grehan sc->psc_sel.pc_dev, sc->psc_sel.pc_func, 462366f6083SPeter Grehan pi->pi_bar[i].addr, pi->pi_bar[i].size, base); 463366f6083SPeter Grehan if (error) 464366f6083SPeter Grehan return (-1); 465366f6083SPeter Grehan } 466366f6083SPeter Grehan 467366f6083SPeter Grehan /* 468366f6083SPeter Grehan * 64-bit BAR takes up two slots so skip the next one. 469366f6083SPeter Grehan */ 470366f6083SPeter Grehan if (bartype == PCIBAR_MEM64) { 471366f6083SPeter Grehan i++; 472366f6083SPeter Grehan assert(i <= PCI_BARMAX); 473366f6083SPeter Grehan sc->psc_bar[i].type = PCIBAR_MEMHI64; 474366f6083SPeter Grehan } 475366f6083SPeter Grehan } 476366f6083SPeter Grehan return (0); 477366f6083SPeter Grehan } 478366f6083SPeter Grehan 479366f6083SPeter Grehan static int 480366f6083SPeter Grehan cfginit(struct vmctx *ctx, struct pci_devinst *pi, int bus, int slot, int func) 481366f6083SPeter Grehan { 482366f6083SPeter Grehan int error; 483366f6083SPeter Grehan struct passthru_softc *sc; 484366f6083SPeter Grehan 485366f6083SPeter Grehan error = 1; 486366f6083SPeter Grehan sc = pi->pi_arg; 487366f6083SPeter Grehan 488366f6083SPeter Grehan bzero(&sc->psc_sel, sizeof(struct pcisel)); 489366f6083SPeter Grehan sc->psc_sel.pc_bus = bus; 490366f6083SPeter Grehan sc->psc_sel.pc_dev = slot; 491366f6083SPeter Grehan sc->psc_sel.pc_func = func; 492366f6083SPeter Grehan 493cd942e0fSPeter Grehan if (cfginitmsi(sc) != 0) 494cd942e0fSPeter Grehan goto done; 495cd942e0fSPeter Grehan 496366f6083SPeter Grehan if (cfginitbar(ctx, sc) != 0) 497366f6083SPeter Grehan goto done; 498366f6083SPeter Grehan 499366f6083SPeter Grehan error = 0; /* success */ 500366f6083SPeter Grehan done: 501366f6083SPeter Grehan return (error); 502366f6083SPeter Grehan } 503366f6083SPeter Grehan 504366f6083SPeter Grehan static int 505366f6083SPeter Grehan passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 506366f6083SPeter Grehan { 507366f6083SPeter Grehan int bus, slot, func, error; 508366f6083SPeter Grehan struct passthru_softc *sc; 509366f6083SPeter Grehan 510366f6083SPeter Grehan sc = NULL; 511366f6083SPeter Grehan error = 1; 512366f6083SPeter Grehan 513366f6083SPeter Grehan if (pcifd < 0) { 514366f6083SPeter Grehan pcifd = open(_PATH_DEVPCI, O_RDWR, 0); 515366f6083SPeter Grehan if (pcifd < 0) 516366f6083SPeter Grehan goto done; 517366f6083SPeter Grehan } 518366f6083SPeter Grehan 519366f6083SPeter Grehan if (iofd < 0) { 520366f6083SPeter Grehan iofd = open(_PATH_DEVIO, O_RDWR, 0); 521366f6083SPeter Grehan if (iofd < 0) 522366f6083SPeter Grehan goto done; 523366f6083SPeter Grehan } 524366f6083SPeter Grehan 5254d1e669cSPeter Grehan if (opts == NULL || 5264d1e669cSPeter Grehan sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) 527366f6083SPeter Grehan goto done; 528366f6083SPeter Grehan 529366f6083SPeter Grehan if (vm_assign_pptdev(ctx, bus, slot, func) != 0) 530366f6083SPeter Grehan goto done; 531366f6083SPeter Grehan 532366f6083SPeter Grehan sc = malloc(sizeof(struct passthru_softc)); 533366f6083SPeter Grehan memset(sc, 0, sizeof(struct passthru_softc)); 534366f6083SPeter Grehan 535366f6083SPeter Grehan pi->pi_arg = sc; 536366f6083SPeter Grehan sc->psc_pi = pi; 537366f6083SPeter Grehan 538366f6083SPeter Grehan /* initialize config space */ 5394d1e669cSPeter Grehan if ((error = cfginit(ctx, pi, bus, slot, func)) != 0) 540366f6083SPeter Grehan goto done; 541366f6083SPeter Grehan 542366f6083SPeter Grehan error = 0; /* success */ 543366f6083SPeter Grehan done: 544366f6083SPeter Grehan if (error) { 545366f6083SPeter Grehan free(sc); 546366f6083SPeter Grehan vm_unassign_pptdev(ctx, bus, slot, func); 547366f6083SPeter Grehan } 548366f6083SPeter Grehan return (error); 549366f6083SPeter Grehan } 550366f6083SPeter Grehan 551366f6083SPeter Grehan static int 552366f6083SPeter Grehan bar_access(int coff) 553366f6083SPeter Grehan { 554366f6083SPeter Grehan if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) 555366f6083SPeter Grehan return (1); 556366f6083SPeter Grehan else 557366f6083SPeter Grehan return (0); 558366f6083SPeter Grehan } 559366f6083SPeter Grehan 560366f6083SPeter Grehan static int 561366f6083SPeter Grehan msicap_access(struct passthru_softc *sc, int coff) 562366f6083SPeter Grehan { 563366f6083SPeter Grehan int caplen; 564366f6083SPeter Grehan 565366f6083SPeter Grehan if (sc->psc_msi.capoff == 0) 566366f6083SPeter Grehan return (0); 567366f6083SPeter Grehan 568366f6083SPeter Grehan caplen = msi_caplen(sc->psc_msi.msgctrl); 569366f6083SPeter Grehan 570366f6083SPeter Grehan if (coff >= sc->psc_msi.capoff && coff < sc->psc_msi.capoff + caplen) 571366f6083SPeter Grehan return (1); 572366f6083SPeter Grehan else 573366f6083SPeter Grehan return (0); 574366f6083SPeter Grehan } 575366f6083SPeter Grehan 576366f6083SPeter Grehan static int 577cd942e0fSPeter Grehan msixcap_access(struct passthru_softc *sc, int coff) 578cd942e0fSPeter Grehan { 579cd942e0fSPeter Grehan if (sc->psc_msix.capoff == 0) 580cd942e0fSPeter Grehan return (0); 581cd942e0fSPeter Grehan 582cd942e0fSPeter Grehan return (coff >= sc->psc_msix.capoff && 583cd942e0fSPeter Grehan coff < sc->psc_msix.capoff + MSIX_CAPLEN); 584cd942e0fSPeter Grehan } 585cd942e0fSPeter Grehan 586cd942e0fSPeter Grehan static int 5874d1e669cSPeter Grehan passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 5884d1e669cSPeter Grehan int coff, int bytes, uint32_t *rv) 589366f6083SPeter Grehan { 590366f6083SPeter Grehan struct passthru_softc *sc; 591366f6083SPeter Grehan 592366f6083SPeter Grehan sc = pi->pi_arg; 593366f6083SPeter Grehan 594366f6083SPeter Grehan /* 595366f6083SPeter Grehan * PCI BARs and MSI capability is emulated. 596366f6083SPeter Grehan */ 597366f6083SPeter Grehan if (bar_access(coff) || msicap_access(sc, coff)) 598366f6083SPeter Grehan return (-1); 599366f6083SPeter Grehan 600366f6083SPeter Grehan #ifdef LEGACY_SUPPORT 601366f6083SPeter Grehan /* 602366f6083SPeter Grehan * Emulate PCIR_CAP_PTR if this device does not support MSI capability 603366f6083SPeter Grehan * natively. 604366f6083SPeter Grehan */ 605366f6083SPeter Grehan if (sc->psc_msi.emulated) { 606366f6083SPeter Grehan if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4) 607366f6083SPeter Grehan return (-1); 608366f6083SPeter Grehan } 609366f6083SPeter Grehan #endif 610366f6083SPeter Grehan 611366f6083SPeter Grehan /* Everything else just read from the device's config space */ 612366f6083SPeter Grehan *rv = read_config(&sc->psc_sel, coff, bytes); 613366f6083SPeter Grehan 614366f6083SPeter Grehan return (0); 615366f6083SPeter Grehan } 616366f6083SPeter Grehan 617366f6083SPeter Grehan static int 6184d1e669cSPeter Grehan passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 6194d1e669cSPeter Grehan int coff, int bytes, uint32_t val) 620366f6083SPeter Grehan { 621cd942e0fSPeter Grehan int error, msix_table_entries, i; 622366f6083SPeter Grehan struct passthru_softc *sc; 623366f6083SPeter Grehan 624366f6083SPeter Grehan sc = pi->pi_arg; 625366f6083SPeter Grehan 626366f6083SPeter Grehan /* 627366f6083SPeter Grehan * PCI BARs are emulated 628366f6083SPeter Grehan */ 629366f6083SPeter Grehan if (bar_access(coff)) 630366f6083SPeter Grehan return (-1); 631366f6083SPeter Grehan 632366f6083SPeter Grehan /* 633366f6083SPeter Grehan * MSI capability is emulated 634366f6083SPeter Grehan */ 635366f6083SPeter Grehan if (msicap_access(sc, coff)) { 636366f6083SPeter Grehan msicap_cfgwrite(pi, sc->psc_msi.capoff, coff, bytes, val); 637366f6083SPeter Grehan 638366f6083SPeter Grehan error = vm_setup_msi(ctx, vcpu, sc->psc_sel.pc_bus, 639366f6083SPeter Grehan sc->psc_sel.pc_dev, sc->psc_sel.pc_func, pi->pi_msi.cpu, 640366f6083SPeter Grehan pi->pi_msi.vector, pi->pi_msi.msgnum); 641366f6083SPeter Grehan if (error != 0) { 642366f6083SPeter Grehan printf("vm_setup_msi returned error %d\r\n", errno); 643366f6083SPeter Grehan exit(1); 644366f6083SPeter Grehan } 645366f6083SPeter Grehan return (0); 646366f6083SPeter Grehan } 647366f6083SPeter Grehan 648cd942e0fSPeter Grehan if (msixcap_access(sc, coff)) { 649cd942e0fSPeter Grehan msixcap_cfgwrite(pi, sc->psc_msix.capoff, coff, bytes, val); 650cd942e0fSPeter Grehan if (pi->pi_msix.enabled) { 651cd942e0fSPeter Grehan msix_table_entries = pi->pi_msix.table_count; 652cd942e0fSPeter Grehan for (i = 0; i < msix_table_entries; i++) { 653cd942e0fSPeter Grehan error = vm_setup_msix(ctx, vcpu, sc->psc_sel.pc_bus, 654cd942e0fSPeter Grehan sc->psc_sel.pc_dev, 655cd942e0fSPeter Grehan sc->psc_sel.pc_func, i, 656cd942e0fSPeter Grehan pi->pi_msix.table[i].msg_data, 657cd942e0fSPeter Grehan pi->pi_msix.table[i].vector_control, 658cd942e0fSPeter Grehan pi->pi_msix.table[i].addr); 659cd942e0fSPeter Grehan 660cd942e0fSPeter Grehan if (error) { 661cd942e0fSPeter Grehan printf("vm_setup_msix returned error %d\r\n", errno); 662cd942e0fSPeter Grehan exit(1); 663cd942e0fSPeter Grehan } 664cd942e0fSPeter Grehan } 665cd942e0fSPeter Grehan } 666cd942e0fSPeter Grehan return (0); 667cd942e0fSPeter Grehan } 668cd942e0fSPeter Grehan 669366f6083SPeter Grehan #ifdef LEGACY_SUPPORT 670366f6083SPeter Grehan /* 671366f6083SPeter Grehan * If this device does not support MSI natively then we cannot let 672366f6083SPeter Grehan * the guest disable legacy interrupts from the device. It is the 673366f6083SPeter Grehan * legacy interrupt that is triggering the virtual MSI to the guest. 674366f6083SPeter Grehan */ 675366f6083SPeter Grehan if (sc->psc_msi.emulated && pci_msi_enabled(pi)) { 676366f6083SPeter Grehan if (coff == PCIR_COMMAND && bytes == 2) 677366f6083SPeter Grehan val &= ~PCIM_CMD_INTxDIS; 678366f6083SPeter Grehan } 679366f6083SPeter Grehan #endif 680366f6083SPeter Grehan 681366f6083SPeter Grehan write_config(&sc->psc_sel, coff, bytes, val); 682366f6083SPeter Grehan 683366f6083SPeter Grehan return (0); 684366f6083SPeter Grehan } 685366f6083SPeter Grehan 686366f6083SPeter Grehan static void 6874d1e669cSPeter Grehan passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 6884d1e669cSPeter Grehan uint64_t offset, int size, uint64_t value) 689366f6083SPeter Grehan { 690366f6083SPeter Grehan struct passthru_softc *sc; 691366f6083SPeter Grehan struct iodev_pio_req pio; 692366f6083SPeter Grehan 693366f6083SPeter Grehan sc = pi->pi_arg; 694366f6083SPeter Grehan 695*aa12663fSNeel Natu if (baridx == pci_msix_table_bar(pi)) { 6964d1e669cSPeter Grehan msix_table_write(ctx, vcpu, sc, offset, size, value); 6974d1e669cSPeter Grehan } else { 6984d1e669cSPeter Grehan assert(pi->pi_bar[baridx].type == PCIBAR_IO); 699366f6083SPeter Grehan bzero(&pio, sizeof(struct iodev_pio_req)); 700366f6083SPeter Grehan pio.access = IODEV_PIO_WRITE; 701366f6083SPeter Grehan pio.port = sc->psc_bar[baridx].addr + offset; 702366f6083SPeter Grehan pio.width = size; 703366f6083SPeter Grehan pio.val = value; 704366f6083SPeter Grehan 705366f6083SPeter Grehan (void)ioctl(iofd, IODEV_PIO, &pio); 706366f6083SPeter Grehan } 7074d1e669cSPeter Grehan } 708366f6083SPeter Grehan 7094d1e669cSPeter Grehan static uint64_t 7104d1e669cSPeter Grehan passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 7114d1e669cSPeter Grehan uint64_t offset, int size) 712366f6083SPeter Grehan { 713366f6083SPeter Grehan struct passthru_softc *sc; 714366f6083SPeter Grehan struct iodev_pio_req pio; 7154d1e669cSPeter Grehan uint64_t val; 716366f6083SPeter Grehan 717366f6083SPeter Grehan sc = pi->pi_arg; 718366f6083SPeter Grehan 719*aa12663fSNeel Natu if (baridx == pci_msix_table_bar(pi)) { 7204d1e669cSPeter Grehan val = msix_table_read(sc, offset, size); 7214d1e669cSPeter Grehan } else { 7224d1e669cSPeter Grehan assert(pi->pi_bar[baridx].type == PCIBAR_IO); 723366f6083SPeter Grehan bzero(&pio, sizeof(struct iodev_pio_req)); 724366f6083SPeter Grehan pio.access = IODEV_PIO_READ; 725366f6083SPeter Grehan pio.port = sc->psc_bar[baridx].addr + offset; 726366f6083SPeter Grehan pio.width = size; 727366f6083SPeter Grehan pio.val = 0; 728366f6083SPeter Grehan 729366f6083SPeter Grehan (void)ioctl(iofd, IODEV_PIO, &pio); 730366f6083SPeter Grehan 7314d1e669cSPeter Grehan val = pio.val; 7324d1e669cSPeter Grehan } 7334d1e669cSPeter Grehan 7344d1e669cSPeter Grehan return (val); 735366f6083SPeter Grehan } 736366f6083SPeter Grehan 737366f6083SPeter Grehan struct pci_devemu passthru = { 738366f6083SPeter Grehan .pe_emu = "passthru", 739366f6083SPeter Grehan .pe_init = passthru_init, 740366f6083SPeter Grehan .pe_cfgwrite = passthru_cfgwrite, 741366f6083SPeter Grehan .pe_cfgread = passthru_cfgread, 7424d1e669cSPeter Grehan .pe_barwrite = passthru_write, 7434d1e669cSPeter Grehan .pe_barread = passthru_read, 744366f6083SPeter Grehan }; 745366f6083SPeter Grehan PCI_EMUL_SET(passthru); 746